From c7b31d0ac983fdf7693a0029fdb0e16247c6599d Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Fri, 15 Jul 2022 12:22:36 -0500 Subject: [PATCH 001/333] Correctly handle path stability for 'use tree' items PR #5956 started checking the stability of path segments. However, this was not applied to 'use tree' items (e.g. 'use some::path::{ItemOne, ItemTwo}') due to the way that we desugar these items in HIR lowering. This PR modifies 'use tree' lowering to preserve resolution information, which is needed by stability checking. --- compiler/rustc_ast_lowering/src/item.rs | 12 +++++++++-- compiler/rustc_ast_lowering/src/lib.rs | 10 ++++++++++ src/test/ui/lint/lint-output-format.rs | 1 + src/test/ui/lint/lint-output-format.stderr | 12 +++++++++-- .../stability-attribute/stable-in-unstable.rs | 8 ++++++++ .../stable-in-unstable.stderr | 20 ++++++++++++++++++- 6 files changed, 58 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 7da49143b461..e176364c494e 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -533,7 +533,11 @@ impl<'hir> LoweringContext<'_, 'hir> { let ident = *ident; let mut path = path.clone(); for seg in &mut path.segments { - seg.id = self.next_node_id(); + // Give the cloned segment the same resolution information + // as the old one (this is needed for stability checking). + let new_id = self.next_node_id(); + self.resolver.clone_res(seg.id, new_id); + seg.id = new_id; } let span = path.span; @@ -602,7 +606,11 @@ impl<'hir> LoweringContext<'_, 'hir> { // Give the segments new node-ids since they are being cloned. for seg in &mut prefix.segments { - seg.id = self.next_node_id(); + // Give the cloned segment the same resolution information + // as the old one (this is needed for stability checking). + let new_id = self.next_node_id(); + self.resolver.clone_res(seg.id, new_id); + seg.id = new_id; } // Each `use` import is an item and thus are owners of the diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index b79c1cafba00..ad6b857e94d4 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -155,6 +155,10 @@ trait ResolverAstLoweringExt { fn legacy_const_generic_args(&self, expr: &Expr) -> Option>; fn get_partial_res(&self, id: NodeId) -> Option; fn get_import_res(&self, id: NodeId) -> PerNS>>; + // Clones the resolution (if any) on 'source' and applies it + // to 'target'. Used when desugaring a `UseTreeKind::Nested` to + // multiple `UseTreeKind::Simple`s + fn clone_res(&mut self, source: NodeId, target: NodeId); fn get_label_res(&self, id: NodeId) -> Option; fn get_lifetime_res(&self, id: NodeId) -> Option; fn take_extra_lifetime_params(&mut self, id: NodeId) -> Vec<(Ident, NodeId, LifetimeRes)>; @@ -192,6 +196,12 @@ impl ResolverAstLoweringExt for ResolverAstLowering { None } + fn clone_res(&mut self, source: NodeId, target: NodeId) { + if let Some(res) = self.partial_res_map.get(&source) { + self.partial_res_map.insert(target, *res); + } + } + /// Obtains resolution for a `NodeId` with a single resolution. fn get_partial_res(&self, id: NodeId) -> Option { self.partial_res_map.get(&id).copied() diff --git a/src/test/ui/lint/lint-output-format.rs b/src/test/ui/lint/lint-output-format.rs index 169a98c9483c..67e8ec8f13b4 100644 --- a/src/test/ui/lint/lint-output-format.rs +++ b/src/test/ui/lint/lint-output-format.rs @@ -5,6 +5,7 @@ extern crate lint_output_format; //~ ERROR use of unstable library feature use lint_output_format::{foo, bar}; //~ ERROR use of unstable library feature +//~| ERROR use of unstable library feature fn main() { let _x = foo(); diff --git a/src/test/ui/lint/lint-output-format.stderr b/src/test/ui/lint/lint-output-format.stderr index 3bc1d6fc135e..0db79a1564fa 100644 --- a/src/test/ui/lint/lint-output-format.stderr +++ b/src/test/ui/lint/lint-output-format.stderr @@ -6,6 +6,14 @@ LL | extern crate lint_output_format; | = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable +error[E0658]: use of unstable library feature 'unstable_test_feature' + --> $DIR/lint-output-format.rs:7:26 + | +LL | use lint_output_format::{foo, bar}; + | ^^^ + | + = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable + error[E0658]: use of unstable library feature 'unstable_test_feature' --> $DIR/lint-output-format.rs:7:31 | @@ -15,13 +23,13 @@ LL | use lint_output_format::{foo, bar}; = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-output-format.rs:11:14 + --> $DIR/lint-output-format.rs:12:14 | LL | let _y = bar(); | ^^^ | = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/stability-attribute/stable-in-unstable.rs b/src/test/ui/stability-attribute/stable-in-unstable.rs index 272a1a972340..226367c39929 100644 --- a/src/test/ui/stability-attribute/stable-in-unstable.rs +++ b/src/test/ui/stability-attribute/stable-in-unstable.rs @@ -44,3 +44,11 @@ mod isolated5 { impl stable_in_unstable_std::old_stable_module::OldTrait for LocalType {} } + +mod isolated6 { + use stable_in_unstable_core::new_unstable_module::{OldTrait}; //~ ERROR use of unstable library feature 'unstable_test_feature' +} + +mod isolated7 { + use stable_in_unstable_core::new_unstable_module::*; //~ ERROR use of unstable library feature 'unstable_test_feature' +} diff --git a/src/test/ui/stability-attribute/stable-in-unstable.stderr b/src/test/ui/stability-attribute/stable-in-unstable.stderr index e123d83584c8..b5e3e5f1202c 100644 --- a/src/test/ui/stability-attribute/stable-in-unstable.stderr +++ b/src/test/ui/stability-attribute/stable-in-unstable.stderr @@ -34,6 +34,24 @@ LL | impl stable_in_unstable_core::new_unstable_module::OldTrait for LocalTy = note: see issue #1 for more information = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable -error: aborting due to 4 previous errors +error[E0658]: use of unstable library feature 'unstable_test_feature' + --> $DIR/stable-in-unstable.rs:49:56 + | +LL | use stable_in_unstable_core::new_unstable_module::{OldTrait}; + | ^^^^^^^^ + | + = note: see issue #1 for more information + = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable + +error[E0658]: use of unstable library feature 'unstable_test_feature' + --> $DIR/stable-in-unstable.rs:53:9 + | +LL | use stable_in_unstable_core::new_unstable_module::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #1 for more information + = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable + +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0658`. From 4e3b9ed337d8088e58ad0aaad503000556b5684b Mon Sep 17 00:00:00 2001 From: woppopo Date: Fri, 26 Aug 2022 18:14:12 +0900 Subject: [PATCH 002/333] constify `Location` methods --- library/core/src/panic/location.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/library/core/src/panic/location.rs b/library/core/src/panic/location.rs index 8eefd9ff20db..26a410365568 100644 --- a/library/core/src/panic/location.rs +++ b/library/core/src/panic/location.rs @@ -123,8 +123,9 @@ impl<'a> Location<'a> { /// ``` #[must_use] #[stable(feature = "panic_hooks", since = "1.10.0")] + #[rustc_const_unstable(feature = "const_caller_location", issue = "76156")] #[inline] - pub fn file(&self) -> &str { + pub const fn file(&self) -> &str { self.file } @@ -147,8 +148,9 @@ impl<'a> Location<'a> { /// ``` #[must_use] #[stable(feature = "panic_hooks", since = "1.10.0")] + #[rustc_const_unstable(feature = "const_caller_location", issue = "76156")] #[inline] - pub fn line(&self) -> u32 { + pub const fn line(&self) -> u32 { self.line } @@ -171,8 +173,9 @@ impl<'a> Location<'a> { /// ``` #[must_use] #[stable(feature = "panic_col", since = "1.25.0")] + #[rustc_const_unstable(feature = "const_caller_location", issue = "76156")] #[inline] - pub fn column(&self) -> u32 { + pub const fn column(&self) -> u32 { self.col } } From 302e33fde2785874c6b85aa9760e8e6be2e7dad4 Mon Sep 17 00:00:00 2001 From: Pointerbender Date: Mon, 12 Sep 2022 11:12:28 +0200 Subject: [PATCH 003/333] add description of the memory layout for `UnsafeCell` --- library/core/src/cell.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index fb4454c94cb3..cce4e5a59467 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -1811,6 +1811,8 @@ impl fmt::Display for RefMut<'_, T> { /// /// [`.get_mut()`]: `UnsafeCell::get_mut` /// +/// `UnsafeCell` has the same in-memory representation as its inner type `T`. +/// /// # Examples /// /// Here is an example showcasing how to soundly mutate the contents of an `UnsafeCell<_>` despite From 13bc0996ddb64919feefb256ad9468087333121d Mon Sep 17 00:00:00 2001 From: Pointerbender Date: Wed, 14 Sep 2022 10:10:18 +0200 Subject: [PATCH 004/333] expand documentation on type conversion w.r.t. `UnsafeCell` --- library/core/src/cell.rs | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index cce4e5a59467..b730e13dcb88 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -1811,7 +1811,36 @@ impl fmt::Display for RefMut<'_, T> { /// /// [`.get_mut()`]: `UnsafeCell::get_mut` /// -/// `UnsafeCell` has the same in-memory representation as its inner type `T`. +/// `UnsafeCell` has the same in-memory representation as its inner type `T`. A consequence +/// of this guarantee is that it is possible to convert between `T` and `UnsafeCell`. +/// However, it is only valid to obtain a `*mut T` pointer or `&mut T` reference to the +/// contents of an `UnsafeCell` through [`.get()`], [`.raw_get()`] or [`.get_mut()`], e.g.: +/// +/// ```rust +/// use std::cell::UnsafeCell; +/// +/// let mut x: UnsafeCell = UnsafeCell::new(5); +/// let p1: &UnsafeCell = &x; +/// // using `.get()` is okay: +/// unsafe { +/// // SAFETY: there exist no other references to the contents of `x` +/// let p2: &mut u32 = &mut *p1.get(); +/// }; +/// // using `.raw_get()` is also okay: +/// unsafe { +/// // SAFETY: there exist no other references to the contents of `x` in this scope +/// let p2: &mut u32 = &mut *UnsafeCell::raw_get(p1 as *const _); +/// }; +/// // using `.get_mut()` is always safe: +/// let p2: &mut u32 = x.get_mut(); +/// // but the following is not allowed! +/// // let p2: &mut u32 = unsafe { +/// // let t: *mut u32 = &x as *const _ as *mut u32; +/// // &mut *t +/// // }; +/// ``` +/// +/// [`.raw_get()`]: `UnsafeCell::raw_get` /// /// # Examples /// From d0f2db3bf44cbfea1a6293de2199974421e60a27 Mon Sep 17 00:00:00 2001 From: feniljain Date: Thu, 15 Sep 2022 17:22:44 +0530 Subject: [PATCH 005/333] fix(generate_method): correct method indentation inside generated impl --- .../src/handlers/generate_function.rs | 85 +++++++++---------- 1 file changed, 38 insertions(+), 47 deletions(-) diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs index e26c76da1891..f67cc924409f 100644 --- a/crates/ide-assists/src/handlers/generate_function.rs +++ b/crates/ide-assists/src/handlers/generate_function.rs @@ -174,10 +174,11 @@ fn add_func_to_accumulator( label: String, ) -> Option<()> { acc.add(AssistId("generate_function", AssistKind::Generate), label, text_range, |builder| { - let function_template = function_builder.render(); + let indent = IndentLevel::from_node(function_builder.target.syntax()); + let function_template = function_builder.render(adt_name.is_some()); let mut func = function_template.to_string(ctx.config.snippet_cap); if let Some(name) = adt_name { - func = format!("\nimpl {} {{\n{}\n}}", name, func); + func = format!("\n{}impl {} {{\n{}\n{}}}", indent, name, func, indent); } builder.edit_file(file); match ctx.config.snippet_cap { @@ -307,7 +308,7 @@ impl FunctionBuilder { }) } - fn render(self) -> FunctionTemplate { + fn render(self, is_method: bool) -> FunctionTemplate { let placeholder_expr = make::ext::expr_todo(); let fn_body = make::block_expr(vec![], Some(placeholder_expr)); let visibility = if self.needs_pub { Some(make::visibility_pub_crate()) } else { None }; @@ -325,8 +326,14 @@ impl FunctionBuilder { match self.target { GeneratedFunctionTarget::BehindItem(it) => { - let indent = IndentLevel::from_node(&it); - leading_ws = format!("\n\n{}", indent); + let mut indent = IndentLevel::from_node(&it); + if is_method { + indent = indent + 1; + leading_ws = format!("{}", indent); + } else { + leading_ws = format!("\n\n{}", indent); + } + fn_def = fn_def.indent(indent); trailing_ws = String::new(); } @@ -1470,11 +1477,9 @@ fn foo() {S.bar$0();} struct S; fn foo() {S.bar();} impl S { - - -fn bar(&self) ${0:-> _} { - todo!() -} + fn bar(&self) ${0:-> _} { + todo!() + } } ", ) @@ -1516,14 +1521,12 @@ fn foo() {s::S.bar$0();} r" mod s { pub struct S; -impl S { - - - pub(crate) fn bar(&self) ${0:-> _} { - todo!() + impl S { + pub(crate) fn bar(&self) ${0:-> _} { + todo!() + } } } -} fn foo() {s::S.bar();} ", ) @@ -1550,11 +1553,9 @@ mod s { } } impl S { - - -fn bar(&self) ${0:-> _} { - todo!() -} + fn bar(&self) ${0:-> _} { + todo!() + } } ", @@ -1573,11 +1574,9 @@ fn foo() {$0S.bar();} struct S; fn foo() {S.bar();} impl S { - - -fn bar(&self) ${0:-> _} { - todo!() -} + fn bar(&self) ${0:-> _} { + todo!() + } } ", ) @@ -1595,11 +1594,9 @@ fn foo() {S::bar$0();} struct S; fn foo() {S::bar();} impl S { - - -fn bar() ${0:-> _} { - todo!() -} + fn bar() ${0:-> _} { + todo!() + } } ", ) @@ -1641,14 +1638,12 @@ fn foo() {s::S::bar$0();} r" mod s { pub struct S; -impl S { - - - pub(crate) fn bar() ${0:-> _} { - todo!() + impl S { + pub(crate) fn bar() ${0:-> _} { + todo!() + } } } -} fn foo() {s::S::bar();} ", ) @@ -1666,11 +1661,9 @@ fn foo() {$0S::bar();} struct S; fn foo() {S::bar();} impl S { - - -fn bar() ${0:-> _} { - todo!() -} + fn bar() ${0:-> _} { + todo!() + } } ", ) @@ -1845,11 +1838,9 @@ fn main() { Foo::new(); } impl Foo { - - -fn new() ${0:-> _} { - todo!() -} + fn new() ${0:-> _} { + todo!() + } } ", ) From 37ff07e1ffa0f3e59ac6c214d40ed4b44f31db3c Mon Sep 17 00:00:00 2001 From: feniljain Date: Thu, 15 Sep 2022 19:33:19 +0530 Subject: [PATCH 006/333] fix(generate_module): generate new impl near its ADT --- .../src/handlers/generate_function.rs | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs index f67cc924409f..8b67982f9158 100644 --- a/crates/ide-assists/src/handlers/generate_function.rs +++ b/crates/ide-assists/src/handlers/generate_function.rs @@ -1,4 +1,4 @@ -use hir::{HasSource, HirDisplay, Module, Semantics, TypeInfo}; +use hir::{Adt, HasSource, HirDisplay, Module, Semantics, TypeInfo}; use ide_db::{ base_db::FileId, defs::{Definition, NameRefClass}, @@ -145,7 +145,8 @@ fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { return None; } let (impl_, file) = get_adt_source(ctx, &adt, fn_name.text().as_str())?; - let (target, insert_offset) = get_method_target(ctx, &target_module, &impl_)?; + let (target, insert_offset) = get_method_target(ctx, &impl_, &adt)?; + let function_builder = FunctionBuilder::from_method_call(ctx, &call, &fn_name, target_module, target)?; let text_range = call.syntax().text_range(); @@ -418,14 +419,13 @@ fn get_fn_target( fn get_method_target( ctx: &AssistContext<'_>, - target_module: &Module, impl_: &Option, + adt: &Adt, ) -> Option<(GeneratedFunctionTarget, TextSize)> { let target = match impl_ { Some(impl_) => next_space_for_fn_in_impl(impl_)?, None => { - next_space_for_fn_in_module(ctx.sema.db, &target_module.definition_source(ctx.sema.db))? - .1 + GeneratedFunctionTarget::BehindItem(adt.source(ctx.sema.db)?.syntax().value.clone()) } }; Some((target.clone(), get_insert_offset(&target))) @@ -444,7 +444,7 @@ fn assoc_fn_target_info( return None; } let (impl_, file) = get_adt_source(ctx, &adt, fn_name)?; - let (target, insert_offset) = get_method_target(ctx, &module, &impl_)?; + let (target, insert_offset) = get_method_target(ctx, &impl_, &adt)?; let adt_name = if impl_.is_none() { Some(adt.name(ctx.sema.db)) } else { None }; Some(TargetInfo::new(target_module, adt_name, target, file, insert_offset)) } @@ -1475,12 +1475,12 @@ fn foo() {S.bar$0();} ", r" struct S; -fn foo() {S.bar();} impl S { fn bar(&self) ${0:-> _} { todo!() } } +fn foo() {S.bar();} ", ) } @@ -1547,16 +1547,16 @@ mod s { ", r" struct S; -mod s { - fn foo() { - super::S.bar(); - } -} impl S { fn bar(&self) ${0:-> _} { todo!() } } +mod s { + fn foo() { + super::S.bar(); + } +} ", ) @@ -1572,12 +1572,12 @@ fn foo() {$0S.bar();} ", r" struct S; -fn foo() {S.bar();} impl S { fn bar(&self) ${0:-> _} { todo!() } } +fn foo() {S.bar();} ", ) } @@ -1592,12 +1592,12 @@ fn foo() {S::bar$0();} ", r" struct S; -fn foo() {S::bar();} impl S { fn bar() ${0:-> _} { todo!() } } +fn foo() {S::bar();} ", ) } @@ -1659,12 +1659,12 @@ fn foo() {$0S::bar();} ", r" struct S; -fn foo() {S::bar();} impl S { fn bar() ${0:-> _} { todo!() } } +fn foo() {S::bar();} ", ) } @@ -1834,14 +1834,14 @@ fn main() { ", r" enum Foo {} -fn main() { - Foo::new(); -} impl Foo { fn new() ${0:-> _} { todo!() } } +fn main() { + Foo::new(); +} ", ) } From 4d3a31caf08fb4591ce2eaa0c83474d2346d0a23 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Wed, 14 Sep 2022 22:41:37 -0700 Subject: [PATCH 007/333] Add `Box<[T; N]>: TryFrom>` We have `[T; N]: TryFrom>` and `Box<[T; N]>: TryFrom>`, but not the combination. `vec.into_boxed_slice().try_into()` isn't quite a replacement for this, as that'll reallocate unnecessarily in the error case. **Insta-stable, so needs an FCP** --- library/alloc/src/boxed.rs | 51 +++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 67925941c92c..c558b0ce065e 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -1622,6 +1622,22 @@ impl From<[T; N]> for Box<[T]> { } } +/// Casts a boxed slice to a boxed array. +/// +/// # Safety +/// +/// `boxed_slice.len()` must be exactly `N`. +unsafe fn boxed_slice_as_array_unchecked( + boxed_slice: Box<[T], A>, +) -> Box<[T; N], A> { + debug_assert_eq!(boxed_slice.len(), N); + + let (ptr, alloc) = Box::into_raw_with_allocator(boxed_slice); + // SAFETY: Pointer and allocator came from an existing box, + // and our safety condition requires that the length is exactly `N` + unsafe { Box::from_raw_in(ptr as *mut [T; N], alloc) } +} + #[stable(feature = "boxed_slice_try_from", since = "1.43.0")] impl TryFrom> for Box<[T; N]> { type Error = Box<[T]>; @@ -1637,13 +1653,46 @@ impl TryFrom> for Box<[T; N]> { /// `boxed_slice.len()` does not equal `N`. fn try_from(boxed_slice: Box<[T]>) -> Result { if boxed_slice.len() == N { - Ok(unsafe { Box::from_raw(Box::into_raw(boxed_slice) as *mut [T; N]) }) + Ok(unsafe { boxed_slice_as_array_unchecked(boxed_slice) }) } else { Err(boxed_slice) } } } +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "boxed_array_try_from_vec", since = "CURRENT_RUSTC_VERSION")] +impl TryFrom> for Box<[T; N]> { + type Error = Vec; + + /// Attempts to convert a `Vec` into a `Box<[T; N]>`. + /// + /// Like [`Vec::into_boxed_slice`], this is in-place if `vec.capacity() == N`, + /// but will require a reallocation otherwise. + /// + /// # Errors + /// + /// Returns the original `Vec` in the `Err` variant if + /// `boxed_slice.len()` does not equal `N`. + /// + /// # Examples + /// + /// This can be used with [`vec!`] to create an array on the heap: + /// + /// ``` + /// let state: Box<[f32; 100]> = vec![1.0; 100].try_into().unwrap(); + /// assert_eq!(state.len(), 100); + /// ``` + fn try_from(vec: Vec) -> Result { + if vec.len() == N { + let boxed_slice = vec.into_boxed_slice(); + Ok(unsafe { boxed_slice_as_array_unchecked(boxed_slice) }) + } else { + Err(vec) + } + } +} + impl Box { /// Attempt to downcast the box to a concrete type. /// From 43bcc71d074a10d9186e27f9bf21b9dafa815bf5 Mon Sep 17 00:00:00 2001 From: kxxt Date: Wed, 21 Sep 2022 20:51:03 +0800 Subject: [PATCH 008/333] refactor: use grep -E/-F instead of fgrep/egrep --- src/etc/cat-and-grep.sh | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/etc/cat-and-grep.sh b/src/etc/cat-and-grep.sh index 77dc52a93507..08de25a77783 100755 --- a/src/etc/cat-and-grep.sh +++ b/src/etc/cat-and-grep.sh @@ -26,7 +26,7 @@ Options: -i Case insensitive search. ' -GREPPER=fgrep +GREPPER=grep INVERT=0 GREPFLAGS='q' while getopts ':vieh' OPTION; do @@ -39,7 +39,7 @@ while getopts ':vieh' OPTION; do GREPFLAGS="i$GREPFLAGS" ;; e) - GREPPER=egrep + GREPFLAGS="E$GREPFLAGS" ;; h) echo "$USAGE" @@ -51,6 +51,15 @@ while getopts ':vieh' OPTION; do esac done +# an utility function to check if a string contains a substring +stringContain() { [ -z "$1" ] || { [ -z "${2##*$1*}" ] && [ -n "$2" ];};} + +if ! stringContain 'E' "$GREPFLAGS" +then + # use F flag if there is not an E flag + GREPFLAGS="F$GREPFLAGS" +fi + shift $((OPTIND - 1)) # use gnu version of tool if available (for bsd) From 6135aff27cedcf81ff863715685808829364f7cb Mon Sep 17 00:00:00 2001 From: kxxt Date: Mon, 26 Sep 2022 21:56:08 +0800 Subject: [PATCH 009/333] simplify --- src/etc/cat-and-grep.sh | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/etc/cat-and-grep.sh b/src/etc/cat-and-grep.sh index 08de25a77783..238f7f5b6602 100755 --- a/src/etc/cat-and-grep.sh +++ b/src/etc/cat-and-grep.sh @@ -51,10 +51,7 @@ while getopts ':vieh' OPTION; do esac done -# an utility function to check if a string contains a substring -stringContain() { [ -z "$1" ] || { [ -z "${2##*$1*}" ] && [ -n "$2" ];};} - -if ! stringContain 'E' "$GREPFLAGS" +if ! echo "$GREPFLAGS" | grep -q E then # use F flag if there is not an E flag GREPFLAGS="F$GREPFLAGS" From 33421da030eeaf40ed0aca6bec41125c18ca34e4 Mon Sep 17 00:00:00 2001 From: Trevor Spiteri Date: Tue, 27 Sep 2022 17:31:31 +0200 Subject: [PATCH 010/333] doc: rewrite doc for uint::{carrying_add,borrowing_sub} --- library/core/src/num/uint_macros.rs | 80 ++++++++++++++++++----------- 1 file changed, 49 insertions(+), 31 deletions(-) diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 46fd7f2d0e49..337d6bdaeab1 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -1508,37 +1508,42 @@ macro_rules! uint_impl { (a as Self, b) } - /// Calculates `self + rhs + carry` without the ability to overflow. + /// Calculates `self` + `rhs` + `carry` and returns a tuple containing + /// the sum and the output carry. /// - /// Performs "ternary addition" which takes in an extra bit to add, and may return an - /// additional bit of overflow. This allows for chaining together multiple additions - /// to create "big integers" which represent larger values. + /// Performs "ternary addition" of two integer operands and a carry-in + /// bit, and returns an output integer and a carry-out bit. This allows + /// chaining together multiple additions to create a wider addition, and + /// can be useful for bignum addition. /// #[doc = concat!("This can be thought of as a ", stringify!($BITS), "-bit \"full adder\", in the electronics sense.")] /// + /// If the input carry is false, this method is equivalent to + /// [`overflowing_add`](Self::overflowing_add), and the output carry is + /// equal to the overflow flag. Note that although carry and overflow + /// flags are similar for unsigned integers, they are different for + /// signed integers. + /// /// # Examples /// - /// Basic usage - /// /// ``` /// #![feature(bigint_helper_methods)] - #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".carrying_add(2, false), (7, false));")] - #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".carrying_add(2, true), (8, false));")] - #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.carrying_add(1, false), (0, true));")] - #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.carrying_add(0, true), (0, true));")] - #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.carrying_add(1, true), (1, true));")] - #[doc = concat!("assert_eq!(", - stringify!($SelfT), "::MAX.carrying_add(", stringify!($SelfT), "::MAX, true), ", - "(", stringify!($SelfT), "::MAX, true));" - )] - /// ``` /// - /// If `carry` is false, this method is equivalent to [`overflowing_add`](Self::overflowing_add): + #[doc = concat!("// 3 MAX (a = 3 × 2^", stringify!($BITS), " + 2^", stringify!($BITS), " - 1)")] + #[doc = concat!("// + 5 7 (b = 5 × 2^", stringify!($BITS), " + 7)")] + /// // --------- + #[doc = concat!("// 9 6 (sum = 9 × 2^", stringify!($BITS), " + 6)")] /// - /// ``` - /// #![feature(bigint_helper_methods)] - #[doc = concat!("assert_eq!(5_", stringify!($SelfT), ".carrying_add(2, false), 5_", stringify!($SelfT), ".overflowing_add(2));")] - #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.carrying_add(1, false), ", stringify!($SelfT), "::MAX.overflowing_add(1));")] + #[doc = concat!("let (a1, a0): (", stringify!($SelfT), ", ", stringify!($SelfT), ") = (3, ", stringify!($SelfT), "::MAX);")] + #[doc = concat!("let (b1, b0): (", stringify!($SelfT), ", ", stringify!($SelfT), ") = (5, 7);")] + /// let carry0 = false; + /// + /// let (sum0, carry1) = a0.carrying_add(b0, carry0); + /// assert_eq!(carry1, true); + /// let (sum1, carry2) = a1.carrying_add(b1, carry1); + /// assert_eq!(carry2, false); + /// + /// assert_eq!((sum1, sum0), (9, 6)); /// ``` #[unstable(feature = "bigint_helper_methods", issue = "85532")] #[rustc_const_unstable(feature = "const_bigint_helper_methods", issue = "85532")] @@ -1604,22 +1609,35 @@ macro_rules! uint_impl { (a as Self, b) } - /// Calculates `self - rhs - borrow` without the ability to overflow. + /// Calculates `self` − `rhs` − `borrow` and returns a tuple + /// containing the difference and the output borrow. /// - /// Performs "ternary subtraction" which takes in an extra bit to subtract, and may return - /// an additional bit of overflow. This allows for chaining together multiple subtractions - /// to create "big integers" which represent larger values. + /// Performs "ternary subtraction" by subtracting both an integer + /// operand and a borrow-in bit from `self`, and returns an output + /// integer and a borrow-out bit. This allows chaining together multiple + /// subtractions to create a wider subtraction, and can be useful for + /// bignum subtraction. /// /// # Examples /// - /// Basic usage - /// /// ``` /// #![feature(bigint_helper_methods)] - #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".borrowing_sub(2, false), (3, false));")] - #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".borrowing_sub(2, true), (2, false));")] - #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".borrowing_sub(1, false), (", stringify!($SelfT), "::MAX, true));")] - #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".borrowing_sub(1, true), (", stringify!($SelfT), "::MAX - 1, true));")] + /// + #[doc = concat!("// 9 6 (a = 9 × 2^", stringify!($BITS), " + 6)")] + #[doc = concat!("// - 5 7 (b = 5 × 2^", stringify!($BITS), " + 7)")] + /// // --------- + #[doc = concat!("// 3 MAX (diff = 3 × 2^", stringify!($BITS), " + 2^", stringify!($BITS), " - 1)")] + /// + #[doc = concat!("let (a1, a0): (", stringify!($SelfT), ", ", stringify!($SelfT), ") = (9, 6);")] + #[doc = concat!("let (b1, b0): (", stringify!($SelfT), ", ", stringify!($SelfT), ") = (5, 7);")] + /// let borrow0 = false; + /// + /// let (diff0, borrow1) = a0.borrowing_sub(b0, borrow0); + /// assert_eq!(borrow1, true); + /// let (diff1, borrow2) = a1.borrowing_sub(b1, borrow1); + /// assert_eq!(borrow2, false); + /// + #[doc = concat!("assert_eq!((diff1, diff0), (3, ", stringify!($SelfT), "::MAX));")] /// ``` #[unstable(feature = "bigint_helper_methods", issue = "85532")] #[rustc_const_unstable(feature = "const_bigint_helper_methods", issue = "85532")] From 2c666a08b03a16b4ecf28e87e6deae81b4bac590 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Tue, 27 Sep 2022 17:35:50 +0200 Subject: [PATCH 011/333] Add convert_named_struct_to_tuple_struct assist --- .../convert_named_struct_to_tuple_struct.rs | 822 ++++++++++++++++++ crates/ide-assists/src/lib.rs | 2 + 2 files changed, 824 insertions(+) create mode 100644 crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs diff --git a/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs b/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs new file mode 100644 index 000000000000..8d11e0bac941 --- /dev/null +++ b/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs @@ -0,0 +1,822 @@ +use either::Either; +use ide_db::defs::Definition; +use itertools::Itertools; +use syntax::{ + ast::{self, AstNode, HasGenericParams, HasVisibility}, + match_ast, SyntaxKind, SyntaxNode, +}; + +use crate::{assist_context::SourceChangeBuilder, AssistContext, AssistId, AssistKind, Assists}; + +// Assist: convert_named_struct_to_tuple_struct +// +// Converts struct with named fields to tuple struct, and analogously for enum variants with named +// fields. +// +// ``` +// struct Point$0 { x: f32, y: f32 } +// +// impl Point { +// pub fn new(x: f32, y: f32) -> Self { +// Point { x, y } +// } +// +// pub fn x(&self) -> f32 { +// self.x +// } +// +// pub fn y(&self) -> f32 { +// self.y +// } +// } +// ``` +// -> +// ``` +// struct Point(f32, f32); +// +// impl Point { +// pub fn new(x: f32, y: f32) -> Self { +// Point(x, y) +// } +// +// pub fn x(&self) -> f32 { +// self.0 +// } +// +// pub fn y(&self) -> f32 { +// self.1 +// } +// } +// ``` +pub(crate) fn convert_named_struct_to_tuple_struct( + acc: &mut Assists, + ctx: &AssistContext<'_>, +) -> Option<()> { + let strukt = ctx + .find_node_at_offset::() + .map(Either::Left) + .or_else(|| ctx.find_node_at_offset::().map(Either::Right))?; + let field_list = strukt.as_ref().either(|s| s.field_list(), |v| v.field_list())?; + let record_fields = match field_list { + ast::FieldList::RecordFieldList(it) => it, + ast::FieldList::TupleFieldList(_) => return None, + }; + let strukt_def = match &strukt { + Either::Left(s) => Either::Left(ctx.sema.to_def(s)?), + Either::Right(v) => Either::Right(ctx.sema.to_def(v)?), + }; + let target = strukt.as_ref().either(|s| s.syntax(), |v| v.syntax()).text_range(); + + acc.add( + AssistId("convert_named_struct_to_tuple_struct", AssistKind::RefactorRewrite), + "Convert to tuple struct", + target, + |edit| { + edit_field_references(ctx, edit, record_fields.fields()); + edit_struct_references(ctx, edit, strukt_def); + edit_struct_def(ctx, edit, &strukt, record_fields); + }, + ) +} + +fn edit_struct_def( + ctx: &AssistContext<'_>, + edit: &mut SourceChangeBuilder, + strukt: &Either, + record_fields: ast::RecordFieldList, +) { + let tuple_fields = record_fields + .fields() + .filter_map(|f| Some(ast::make::tuple_field(f.visibility(), f.ty()?))); + let tuple_fields = ast::make::tuple_field_list(tuple_fields); + let record_fields_text_range = record_fields.syntax().text_range(); + + edit.edit_file(ctx.file_id()); + edit.replace(record_fields_text_range, tuple_fields.syntax().text()); + + if let Either::Left(strukt) = strukt { + if let Some(w) = strukt.where_clause() { + let mut where_clause = w.to_string(); + if where_clause.ends_with(',') { + where_clause.pop(); + } + where_clause.push(';'); + + edit.delete(w.syntax().text_range()); + edit.insert(record_fields_text_range.end(), ast::make::tokens::single_newline().text()); + edit.insert(record_fields_text_range.end(), where_clause); + edit.insert(record_fields_text_range.end(), ast::make::tokens::single_newline().text()); + + if let Some(tok) = strukt + .generic_param_list() + .and_then(|l| l.r_angle_token()) + .and_then(|tok| tok.next_token()) + .filter(|tok| tok.kind() == SyntaxKind::WHITESPACE) + { + edit.delete(tok.text_range()); + } + } else { + edit.insert(record_fields_text_range.end(), ";"); + } + } + + if let Some(tok) = record_fields + .l_curly_token() + .and_then(|tok| tok.prev_token()) + .filter(|tok| tok.kind() == SyntaxKind::WHITESPACE) + { + edit.delete(tok.text_range()) + } +} + +fn edit_struct_references( + ctx: &AssistContext<'_>, + edit: &mut SourceChangeBuilder, + strukt: Either, +) { + let strukt_def = match strukt { + Either::Left(s) => Definition::Adt(hir::Adt::Struct(s)), + Either::Right(v) => Definition::Variant(v), + }; + let usages = strukt_def.usages(&ctx.sema).include_self_refs().all(); + + let edit_node = |edit: &mut SourceChangeBuilder, node: SyntaxNode| -> Option<()> { + match_ast! { + match node { + ast::RecordPat(record_struct_pat) => { + edit.replace( + record_struct_pat.syntax().text_range(), + ast::make::tuple_struct_pat( + record_struct_pat.path()?, + record_struct_pat + .record_pat_field_list()? + .fields() + .filter_map(|pat| pat.pat()) + ) + .to_string() + ); + }, + ast::RecordExpr(record_expr) => { + let path = record_expr.path()?; + let args = record_expr + .record_expr_field_list()? + .fields() + .filter_map(|f| f.expr()) + .join(", "); + + edit.replace(record_expr.syntax().text_range(), format!("{path}({args})")); + }, + _ => return None, + } + } + Some(()) + }; + + for (file_id, refs) in usages { + edit.edit_file(file_id); + for r in refs { + for node in r.name.syntax().ancestors() { + if edit_node(edit, node).is_some() { + break; + } + } + } + } +} + +fn edit_field_references( + ctx: &AssistContext<'_>, + edit: &mut SourceChangeBuilder, + fields: impl Iterator, +) { + for (index, field) in fields.enumerate() { + let field = match ctx.sema.to_def(&field) { + Some(it) => it, + None => continue, + }; + let def = Definition::Field(field); + let usages = def.usages(&ctx.sema).all(); + for (file_id, refs) in usages { + edit.edit_file(file_id); + for r in refs { + if let Some(name_ref) = r.name.as_name_ref() { + // Only edit the field reference if it's part of a `.field` access + if name_ref.syntax().parent().and_then(ast::FieldExpr::cast).is_some() { + edit.replace(name_ref.syntax().text_range(), index.to_string()); + } + } + } + } + } +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_assist, check_assist_not_applicable}; + + use super::*; + + #[test] + fn not_applicable_other_than_record_struct() { + check_assist_not_applicable(convert_named_struct_to_tuple_struct, r#"struct Foo$0(u32)"#); + check_assist_not_applicable(convert_named_struct_to_tuple_struct, r#"struct Foo$0;"#); + } + + #[test] + fn convert_simple_struct() { + check_assist( + convert_named_struct_to_tuple_struct, + r#" +struct Inner; +struct A$0 { inner: Inner } + +impl A { + fn new(inner: Inner) -> A { + A { inner } + } + + fn new_with_default() -> A { + A::new(Inner) + } + + fn into_inner(self) -> Inner { + self.inner + } +}"#, + r#" +struct Inner; +struct A(Inner); + +impl A { + fn new(inner: Inner) -> A { + A(inner) + } + + fn new_with_default() -> A { + A::new(Inner) + } + + fn into_inner(self) -> Inner { + self.0 + } +}"#, + ); + } + + #[test] + fn convert_struct_referenced_via_self_kw() { + check_assist( + convert_named_struct_to_tuple_struct, + r#" +struct Inner; +struct A$0 { inner: Inner } + +impl A { + fn new(inner: Inner) -> Self { + Self { inner } + } + + fn new_with_default() -> Self { + Self::new(Inner) + } + + fn into_inner(self) -> Inner { + self.inner + } +}"#, + r#" +struct Inner; +struct A(Inner); + +impl A { + fn new(inner: Inner) -> Self { + Self(inner) + } + + fn new_with_default() -> Self { + Self::new(Inner) + } + + fn into_inner(self) -> Inner { + self.0 + } +}"#, + ); + } + + #[test] + fn convert_destructured_struct() { + check_assist( + convert_named_struct_to_tuple_struct, + r#" +struct Inner; +struct A$0 { inner: Inner } + +impl A { + fn into_inner(self) -> Inner { + let A { inner: a } = self; + a + } + + fn into_inner_via_self(self) -> Inner { + let Self { inner } = self; + inner + } +}"#, + r#" +struct Inner; +struct A(Inner); + +impl A { + fn into_inner(self) -> Inner { + let A(a) = self; + a + } + + fn into_inner_via_self(self) -> Inner { + let Self(inner) = self; + inner + } +}"#, + ); + } + + #[test] + fn convert_struct_with_visibility() { + check_assist( + convert_named_struct_to_tuple_struct, + r#" +struct A$0 { + pub first: u32, + pub(crate) second: u64 +} + +impl A { + fn new() -> A { + A { first: 42, second: 42 } + } + + fn into_first(self) -> u32 { + self.first + } + + fn into_second(self) -> u64 { + self.second + } +}"#, + r#" +struct A(pub u32, pub(crate) u64); + +impl A { + fn new() -> A { + A(42, 42) + } + + fn into_first(self) -> u32 { + self.0 + } + + fn into_second(self) -> u64 { + self.1 + } +}"#, + ); + } + + #[test] + fn convert_struct_with_wrapped_references() { + check_assist( + convert_named_struct_to_tuple_struct, + r#" +struct Inner$0 { uint: u32 } +struct Outer { inner: Inner } + +impl Outer { + fn new() -> Self { + Self { inner: Inner { uint: 42 } } + } + + fn into_inner(self) -> u32 { + self.inner.uint + } + + fn into_inner_destructed(self) -> u32 { + let Outer { inner: Inner { uint: x } } = self; + x + } +}"#, + r#" +struct Inner(u32); +struct Outer { inner: Inner } + +impl Outer { + fn new() -> Self { + Self { inner: Inner(42) } + } + + fn into_inner(self) -> u32 { + self.inner.0 + } + + fn into_inner_destructed(self) -> u32 { + let Outer { inner: Inner(x) } = self; + x + } +}"#, + ); + + check_assist( + convert_named_struct_to_tuple_struct, + r#" +struct Inner { uint: u32 } +struct Outer$0 { inner: Inner } + +impl Outer { + fn new() -> Self { + Self { inner: Inner { uint: 42 } } + } + + fn into_inner(self) -> u32 { + self.inner.uint + } + + fn into_inner_destructed(self) -> u32 { + let Outer { inner: Inner { uint: x } } = self; + x + } +}"#, + r#" +struct Inner { uint: u32 } +struct Outer(Inner); + +impl Outer { + fn new() -> Self { + Self(Inner { uint: 42 }) + } + + fn into_inner(self) -> u32 { + self.0.uint + } + + fn into_inner_destructed(self) -> u32 { + let Outer(Inner { uint: x }) = self; + x + } +}"#, + ); + } + + #[test] + fn convert_struct_with_multi_file_references() { + check_assist( + convert_named_struct_to_tuple_struct, + r#" +//- /main.rs +struct Inner; +struct A$0 { inner: Inner } + +mod foo; + +//- /foo.rs +use crate::{A, Inner}; +fn f() { + let a = A { inner: Inner }; +} +"#, + r#" +//- /main.rs +struct Inner; +struct A(Inner); + +mod foo; + +//- /foo.rs +use crate::{A, Inner}; +fn f() { + let a = A(Inner); +} +"#, + ); + } + + #[test] + fn convert_struct_with_where_clause() { + check_assist( + convert_named_struct_to_tuple_struct, + r#" +struct Wrap$0 +where + T: Display, +{ field1: T } +"#, + r#" +struct Wrap(T) +where + T: Display; + +"#, + ); + } + + #[test] + fn not_applicable_other_than_record_variant() { + check_assist_not_applicable( + convert_named_struct_to_tuple_struct, + r#"enum Enum { Variant$0(usize) };"#, + ); + check_assist_not_applicable( + convert_named_struct_to_tuple_struct, + r#"enum Enum { Variant$0 }"#, + ); + } + + #[test] + fn convert_simple_variant() { + check_assist( + convert_named_struct_to_tuple_struct, + r#" +enum A { + $0Variant { field1: usize }, +} + +impl A { + fn new(value: usize) -> A { + A::Variant { field1: value } + } + + fn new_with_default() -> A { + A::new(Default::default()) + } + + fn value(self) -> usize { + match self { + A::Variant { field1: value } => value, + } + } +}"#, + r#" +enum A { + Variant(usize), +} + +impl A { + fn new(value: usize) -> A { + A::Variant(value) + } + + fn new_with_default() -> A { + A::new(Default::default()) + } + + fn value(self) -> usize { + match self { + A::Variant(value) => value, + } + } +}"#, + ); + } + + #[test] + fn convert_variant_referenced_via_self_kw() { + check_assist( + convert_named_struct_to_tuple_struct, + r#" +enum A { + $0Variant { field1: usize }, +} + +impl A { + fn new(value: usize) -> A { + Self::Variant { field1: value } + } + + fn new_with_default() -> A { + Self::new(Default::default()) + } + + fn value(self) -> usize { + match self { + Self::Variant { field1: value } => value, + } + } +}"#, + r#" +enum A { + Variant(usize), +} + +impl A { + fn new(value: usize) -> A { + Self::Variant(value) + } + + fn new_with_default() -> A { + Self::new(Default::default()) + } + + fn value(self) -> usize { + match self { + Self::Variant(value) => value, + } + } +}"#, + ); + } + + #[test] + fn convert_destructured_variant() { + check_assist( + convert_named_struct_to_tuple_struct, + r#" +enum A { + $0Variant { field1: usize }, +} + +impl A { + fn into_inner(self) -> usize { + let A::Variant { field1: first } = self; + first + } + + fn into_inner_via_self(self) -> usize { + let Self::Variant { field1: first } = self; + first + } +}"#, + r#" +enum A { + Variant(usize), +} + +impl A { + fn into_inner(self) -> usize { + let A::Variant(first) = self; + first + } + + fn into_inner_via_self(self) -> usize { + let Self::Variant(first) = self; + first + } +}"#, + ); + } + + #[test] + fn convert_variant_with_wrapped_references() { + check_assist( + convert_named_struct_to_tuple_struct, + r#" +enum Inner { + $0Variant { field1: usize }, +} +enum Outer { + Variant(Inner), +} + +impl Outer { + fn new() -> Self { + Self::Variant(Inner::Variant { field1: 42 }) + } + + fn into_inner_destructed(self) -> u32 { + let Outer::Variant(Inner::Variant { field1: x }) = self; + x + } +}"#, + r#" +enum Inner { + Variant(usize), +} +enum Outer { + Variant(Inner), +} + +impl Outer { + fn new() -> Self { + Self::Variant(Inner::Variant(42)) + } + + fn into_inner_destructed(self) -> u32 { + let Outer::Variant(Inner::Variant(x)) = self; + x + } +}"#, + ); + + check_assist( + convert_named_struct_to_tuple_struct, + r#" +enum Inner { + Variant(usize), +} +enum Outer { + $0Variant { field1: Inner }, +} + +impl Outer { + fn new() -> Self { + Self::Variant { field1: Inner::Variant(42) } + } + + fn into_inner_destructed(self) -> u32 { + let Outer::Variant { field1: Inner::Variant(x) } = self; + x + } +}"#, + r#" +enum Inner { + Variant(usize), +} +enum Outer { + Variant(Inner), +} + +impl Outer { + fn new() -> Self { + Self::Variant(Inner::Variant(42)) + } + + fn into_inner_destructed(self) -> u32 { + let Outer::Variant(Inner::Variant(x)) = self; + x + } +}"#, + ); + } + + #[test] + fn convert_variant_with_multi_file_references() { + check_assist( + convert_named_struct_to_tuple_struct, + r#" +//- /main.rs +struct Inner; +enum A { + $0Variant { field1: Inner }, +} + +mod foo; + +//- /foo.rs +use crate::{A, Inner}; +fn f() { + let a = A::Variant { field1: Inner }; +} +"#, + r#" +//- /main.rs +struct Inner; +enum A { + Variant(Inner), +} + +mod foo; + +//- /foo.rs +use crate::{A, Inner}; +fn f() { + let a = A::Variant(Inner); +} +"#, + ); + } + + #[test] + fn convert_directly_used_variant() { + check_assist( + convert_named_struct_to_tuple_struct, + r#" +//- /main.rs +struct Inner; +enum A { + $0Variant { field1: Inner }, +} + +mod foo; + +//- /foo.rs +use crate::{A::Variant, Inner}; +fn f() { + let a = Variant { field1: Inner }; +} +"#, + r#" +//- /main.rs +struct Inner; +enum A { + Variant(Inner), +} + +mod foo; + +//- /foo.rs +use crate::{A::Variant, Inner}; +fn f() { + let a = Variant(Inner); +} +"#, + ); + } +} diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs index 82bcc3dfa5d9..a07318cefad2 100644 --- a/crates/ide-assists/src/lib.rs +++ b/crates/ide-assists/src/lib.rs @@ -121,6 +121,7 @@ mod handlers { mod convert_iter_for_each_to_for; mod convert_let_else_to_match; mod convert_tuple_struct_to_named_struct; + mod convert_named_struct_to_tuple_struct; mod convert_to_guarded_return; mod convert_two_arm_bool_match_to_matches_macro; mod convert_while_to_loop; @@ -218,6 +219,7 @@ mod handlers { convert_iter_for_each_to_for::convert_iter_for_each_to_for, convert_iter_for_each_to_for::convert_for_loop_with_for_each, convert_let_else_to_match::convert_let_else_to_match, + convert_named_struct_to_tuple_struct::convert_named_struct_to_tuple_struct, convert_to_guarded_return::convert_to_guarded_return, convert_tuple_struct_to_named_struct::convert_tuple_struct_to_named_struct, convert_two_arm_bool_match_to_matches_macro::convert_two_arm_bool_match_to_matches_macro, From a3d79b517273e333127cb6edb7ea001153df800c Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Tue, 27 Sep 2022 20:40:51 +0200 Subject: [PATCH 012/333] Update outside test and generated code --- crates/ide-assists/src/tests.rs | 1 + crates/ide-assists/src/tests/generated.rs | 41 +++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/crates/ide-assists/src/tests.rs b/crates/ide-assists/src/tests.rs index a3bb66e379eb..f7f2417d0745 100644 --- a/crates/ide-assists/src/tests.rs +++ b/crates/ide-assists/src/tests.rs @@ -232,6 +232,7 @@ fn assist_order_field_struct() { assert_eq!(assists.next().expect("expected assist").label, "Generate a getter method"); assert_eq!(assists.next().expect("expected assist").label, "Generate a mut getter method"); assert_eq!(assists.next().expect("expected assist").label, "Generate a setter method"); + assert_eq!(assists.next().expect("expected assist").label, "Convert to tuple struct"); assert_eq!(assists.next().expect("expected assist").label, "Add `#[derive]`"); } diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs index d403f86c6d8c..2c4000efe0fa 100644 --- a/crates/ide-assists/src/tests/generated.rs +++ b/crates/ide-assists/src/tests/generated.rs @@ -407,6 +407,47 @@ fn main() { ) } +#[test] +fn doctest_convert_named_struct_to_tuple_struct() { + check_doc_test( + "convert_named_struct_to_tuple_struct", + r#####" +struct Point$0 { x: f32, y: f32 } + +impl Point { + pub fn new(x: f32, y: f32) -> Self { + Point { x, y } + } + + pub fn x(&self) -> f32 { + self.x + } + + pub fn y(&self) -> f32 { + self.y + } +} +"#####, + r#####" +struct Point(f32, f32); + +impl Point { + pub fn new(x: f32, y: f32) -> Self { + Point(x, y) + } + + pub fn x(&self) -> f32 { + self.0 + } + + pub fn y(&self) -> f32 { + self.1 + } +} +"#####, + ) +} + #[test] fn doctest_convert_to_guarded_return() { check_doc_test( From 834cab724441b1c538f5a84b0fef8f0052714137 Mon Sep 17 00:00:00 2001 From: woppopo Date: Tue, 27 Sep 2022 19:09:32 +0000 Subject: [PATCH 013/333] Add test cases for const `Location` --- library/core/tests/lib.rs | 2 ++ library/core/tests/panic.rs | 1 + library/core/tests/panic/location.rs | 31 ++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+) create mode 100644 library/core/tests/panic.rs create mode 100644 library/core/tests/panic/location.rs diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 59510d3cc2ae..548c1884ecd3 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -7,6 +7,7 @@ #![feature(const_assume)] #![feature(const_black_box)] #![feature(const_bool_to_option)] +#![feature(const_caller_location)] #![feature(const_cell_into_inner)] #![feature(const_convert)] #![feature(const_heap)] @@ -131,6 +132,7 @@ mod num; mod ops; mod option; mod pattern; +mod panic; mod pin; mod pin_macro; mod ptr; diff --git a/library/core/tests/panic.rs b/library/core/tests/panic.rs new file mode 100644 index 000000000000..b7056bdf36ed --- /dev/null +++ b/library/core/tests/panic.rs @@ -0,0 +1 @@ +mod location; \ No newline at end of file diff --git a/library/core/tests/panic/location.rs b/library/core/tests/panic/location.rs new file mode 100644 index 000000000000..ba066bfbc539 --- /dev/null +++ b/library/core/tests/panic/location.rs @@ -0,0 +1,31 @@ +use core::panic::Location; + +// Note: Some of the following tests depend on the source location, +// so please be careful when editing this file. + +#[test] +fn location_const_caller() { + const _CALLER_REFERENCE: &Location<'static> = Location::caller(); + const _CALLER: Location<'static> = *Location::caller(); +} + +#[test] +fn location_const_file() { + const CALLER: &Location<'static> = Location::caller(); + const FILE: &str = CALLER.file(); + assert_eq!(FILE, "library/core/tests/panic/location.rs"); +} + +#[test] +fn location_const_line() { + const CALLER: &Location<'static> = Location::caller(); + const LINE: u32 = CALLER.line(); + assert_eq!(LINE, 21); +} + +#[test] +fn location_const_column() { + const CALLER: &Location<'static> = Location::caller(); + const COLUMN: u32 = CALLER.column(); + assert_eq!(COLUMN, 39); +} \ No newline at end of file From 767a7771c72f37004be36e1656d4f7b724f495c6 Mon Sep 17 00:00:00 2001 From: woppopo Date: Tue, 27 Sep 2022 19:23:52 +0000 Subject: [PATCH 014/333] Add newlines --- library/core/tests/panic.rs | 2 +- library/core/tests/panic/location.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/tests/panic.rs b/library/core/tests/panic.rs index b7056bdf36ed..24b6c56b3569 100644 --- a/library/core/tests/panic.rs +++ b/library/core/tests/panic.rs @@ -1 +1 @@ -mod location; \ No newline at end of file +mod location; diff --git a/library/core/tests/panic/location.rs b/library/core/tests/panic/location.rs index ba066bfbc539..9d7b912458a7 100644 --- a/library/core/tests/panic/location.rs +++ b/library/core/tests/panic/location.rs @@ -28,4 +28,4 @@ fn location_const_column() { const CALLER: &Location<'static> = Location::caller(); const COLUMN: u32 = CALLER.column(); assert_eq!(COLUMN, 39); -} \ No newline at end of file +} From ca55a881612d90554eedec9a8999db890f63cfc6 Mon Sep 17 00:00:00 2001 From: woppopo Date: Tue, 27 Sep 2022 19:40:53 +0000 Subject: [PATCH 015/333] Fix indent --- library/core/tests/panic/location.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/library/core/tests/panic/location.rs b/library/core/tests/panic/location.rs index 9d7b912458a7..9df626a8326c 100644 --- a/library/core/tests/panic/location.rs +++ b/library/core/tests/panic/location.rs @@ -5,27 +5,27 @@ use core::panic::Location; #[test] fn location_const_caller() { - const _CALLER_REFERENCE: &Location<'static> = Location::caller(); - const _CALLER: Location<'static> = *Location::caller(); + const _CALLER_REFERENCE: &Location<'static> = Location::caller(); + const _CALLER: Location<'static> = *Location::caller(); } #[test] fn location_const_file() { - const CALLER: &Location<'static> = Location::caller(); - const FILE: &str = CALLER.file(); - assert_eq!(FILE, "library/core/tests/panic/location.rs"); + const CALLER: &Location<'static> = Location::caller(); + const FILE: &str = CALLER.file(); + assert_eq!(FILE, "library/core/tests/panic/location.rs"); } #[test] fn location_const_line() { - const CALLER: &Location<'static> = Location::caller(); - const LINE: u32 = CALLER.line(); - assert_eq!(LINE, 21); + const CALLER: &Location<'static> = Location::caller(); + const LINE: u32 = CALLER.line(); + assert_eq!(LINE, 21); } #[test] fn location_const_column() { - const CALLER: &Location<'static> = Location::caller(); - const COLUMN: u32 = CALLER.column(); - assert_eq!(COLUMN, 39); + const CALLER: &Location<'static> = Location::caller(); + const COLUMN: u32 = CALLER.column(); + assert_eq!(COLUMN, 40); } From 7b993885d0d998006a352dcf93cdd319ba36a6ab Mon Sep 17 00:00:00 2001 From: woppopo Date: Tue, 27 Sep 2022 19:53:58 +0000 Subject: [PATCH 016/333] Sort mod --- library/core/tests/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 548c1884ecd3..7cf2c0dcefc9 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -131,8 +131,8 @@ mod nonzero; mod num; mod ops; mod option; -mod pattern; mod panic; +mod pattern; mod pin; mod pin_macro; mod ptr; From b3bf931aa2ca95c60ec49b7f6519a1e74dae3c1d Mon Sep 17 00:00:00 2001 From: Yan Chen Date: Tue, 20 Sep 2022 14:44:55 -0700 Subject: [PATCH 017/333] Fix missing explanation of where borrowed reference is used when the borrow occurs in loop iteration --- .../src/diagnostics/explain_borrow.rs | 156 +++--------------- .../rustc_borrowck/src/region_infer/mod.rs | 23 ++- .../borrowck-mut-borrow-linear-errors.stderr | 5 +- .../ui/borrowck/two-phase-across-loop.stderr | 5 +- src/test/ui/nll/closures-in-loops.stderr | 16 +- 5 files changed, 63 insertions(+), 142 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index 1c01e78abd42..582d683dd359 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -1,8 +1,5 @@ //! Print diagnostics to explain why values are borrowed. -use std::collections::VecDeque; - -use rustc_data_structures::fx::FxHashSet; use rustc_errors::{Applicability, Diagnostic}; use rustc_index::vec::IndexVec; use rustc_infer::infer::NllRegionVariableOrigin; @@ -359,19 +356,37 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let borrow_region_vid = borrow.region; debug!(?borrow_region_vid); - let region_sub = self.regioncx.find_sub_region_live_at(borrow_region_vid, location); + let mut region_sub = self.regioncx.find_sub_region_live_at(borrow_region_vid, location); debug!(?region_sub); - match find_use::find(body, regioncx, tcx, region_sub, location) { + let mut use_location = location; + let mut use_in_later_iteration_of_loop = false; + + if region_sub == borrow_region_vid { + // When `region_sub` is the same as `borrow_region_vid` (the location where the borrow is + // issued is the same location that invalidates the reference), this is likely a loop iteration + // - in this case, try using the loop terminator location in `find_sub_region_live_at`. + if let Some(loop_terminator_location) = + regioncx.find_loop_terminator_location(borrow.region, body) + { + region_sub = self + .regioncx + .find_sub_region_live_at(borrow_region_vid, loop_terminator_location); + debug!("explain_why_borrow_contains_point: region_sub in loop={:?}", region_sub); + use_location = loop_terminator_location; + use_in_later_iteration_of_loop = true; + } + } + + match find_use::find(body, regioncx, tcx, region_sub, use_location) { Some(Cause::LiveVar(local, location)) => { let span = body.source_info(location).span; let spans = self .move_spans(Place::from(local).as_ref(), location) .or_else(|| self.borrow_spans(span, location)); - let borrow_location = location; - if self.is_use_in_later_iteration_of_loop(borrow_location, location) { - let later_use = self.later_use_kind(borrow, spans, location); + if use_in_later_iteration_of_loop { + let later_use = self.later_use_kind(borrow, spans, use_location); BorrowExplanation::UsedLaterInLoop(later_use.0, later_use.1, later_use.2) } else { // Check if the location represents a `FakeRead`, and adapt the error @@ -425,131 +440,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } - /// true if `borrow_location` can reach `use_location` by going through a loop and - /// `use_location` is also inside of that loop - fn is_use_in_later_iteration_of_loop( - &self, - borrow_location: Location, - use_location: Location, - ) -> bool { - let back_edge = self.reach_through_backedge(borrow_location, use_location); - back_edge.map_or(false, |back_edge| self.can_reach_head_of_loop(use_location, back_edge)) - } - - /// Returns the outmost back edge if `from` location can reach `to` location passing through - /// that back edge - fn reach_through_backedge(&self, from: Location, to: Location) -> Option { - let mut visited_locations = FxHashSet::default(); - let mut pending_locations = VecDeque::new(); - visited_locations.insert(from); - pending_locations.push_back(from); - debug!("reach_through_backedge: from={:?} to={:?}", from, to,); - - let mut outmost_back_edge = None; - while let Some(location) = pending_locations.pop_front() { - debug!( - "reach_through_backedge: location={:?} outmost_back_edge={:?} - pending_locations={:?} visited_locations={:?}", - location, outmost_back_edge, pending_locations, visited_locations - ); - - if location == to && outmost_back_edge.is_some() { - // We've managed to reach the use location - debug!("reach_through_backedge: found!"); - return outmost_back_edge; - } - - let block = &self.body.basic_blocks[location.block]; - - if location.statement_index < block.statements.len() { - let successor = location.successor_within_block(); - if visited_locations.insert(successor) { - pending_locations.push_back(successor); - } - } else { - pending_locations.extend( - block - .terminator() - .successors() - .map(|bb| Location { statement_index: 0, block: bb }) - .filter(|s| visited_locations.insert(*s)) - .map(|s| { - if self.is_back_edge(location, s) { - match outmost_back_edge { - None => { - outmost_back_edge = Some(location); - } - - Some(back_edge) - if location.dominates(back_edge, &self.dominators) => - { - outmost_back_edge = Some(location); - } - - Some(_) => {} - } - } - - s - }), - ); - } - } - - None - } - - /// true if `from` location can reach `loop_head` location and `loop_head` dominates all the - /// intermediate nodes - fn can_reach_head_of_loop(&self, from: Location, loop_head: Location) -> bool { - self.find_loop_head_dfs(from, loop_head, &mut FxHashSet::default()) - } - - fn find_loop_head_dfs( - &self, - from: Location, - loop_head: Location, - visited_locations: &mut FxHashSet, - ) -> bool { - visited_locations.insert(from); - - if from == loop_head { - return true; - } - - if loop_head.dominates(from, &self.dominators) { - let block = &self.body.basic_blocks[from.block]; - - if from.statement_index < block.statements.len() { - let successor = from.successor_within_block(); - - if !visited_locations.contains(&successor) - && self.find_loop_head_dfs(successor, loop_head, visited_locations) - { - return true; - } - } else { - for bb in block.terminator().successors() { - let successor = Location { statement_index: 0, block: bb }; - - if !visited_locations.contains(&successor) - && self.find_loop_head_dfs(successor, loop_head, visited_locations) - { - return true; - } - } - } - } - - false - } - - /// True if an edge `source -> target` is a backedge -- in other words, if the target - /// dominates the source. - fn is_back_edge(&self, source: Location, target: Location) -> bool { - target.dominates(source, &self.dominators) - } - /// Determine how the borrow was later used. /// First span returned points to the location of the conflicting use /// Second span if `Some` is returned in the case of closures and points diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 244e6e3422d8..347c35818337 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -15,7 +15,7 @@ use rustc_infer::infer::region_constraints::{GenericKind, VarInfos, VerifyBound, use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin}; use rustc_middle::mir::{ Body, ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements, - ConstraintCategory, Local, Location, ReturnConstraint, + ConstraintCategory, Local, Location, ReturnConstraint, TerminatorKind, }; use rustc_middle::traits::ObligationCause; use rustc_middle::traits::ObligationCauseCode; @@ -2236,6 +2236,27 @@ impl<'tcx> RegionInferenceContext<'tcx> { pub(crate) fn universe_info(&self, universe: ty::UniverseIndex) -> UniverseInfo<'tcx> { self.universe_causes[&universe].clone() } + + /// Tries to find the terminator of the loop in which the region 'r' resides. + /// Returns the location of the terminator if found. + pub(crate) fn find_loop_terminator_location( + &self, + r: RegionVid, + body: &Body<'_>, + ) -> Option { + let scc = self.constraint_sccs.scc(r.to_region_vid()); + let locations = self.scc_values.locations_outlived_by(scc); + for location in locations { + let bb = &body[location.block]; + if let Some(terminator) = &bb.terminator { + // terminator of a loop should be TerminatorKind::FalseUnwind + if let TerminatorKind::FalseUnwind { .. } = terminator.kind { + return Some(location); + } + } + } + None + } } impl<'tcx> RegionDefinition<'tcx> { diff --git a/src/test/ui/borrowck/borrowck-mut-borrow-linear-errors.stderr b/src/test/ui/borrowck/borrowck-mut-borrow-linear-errors.stderr index 15ac737606d6..d2b845619c78 100644 --- a/src/test/ui/borrowck/borrowck-mut-borrow-linear-errors.stderr +++ b/src/test/ui/borrowck/borrowck-mut-borrow-linear-errors.stderr @@ -25,7 +25,10 @@ error[E0499]: cannot borrow `x` as mutable more than once at a time --> $DIR/borrowck-mut-borrow-linear-errors.rs:12:30 | LL | _ => { addr.push(&mut x); } - | ^^^^^^ `x` was mutably borrowed here in the previous iteration of the loop + | ----------^^^^^^- + | | | + | | `x` was mutably borrowed here in the previous iteration of the loop + | first borrow used here, in later iteration of loop error: aborting due to 3 previous errors diff --git a/src/test/ui/borrowck/two-phase-across-loop.stderr b/src/test/ui/borrowck/two-phase-across-loop.stderr index 95896c6bbf98..22f9b39dfeec 100644 --- a/src/test/ui/borrowck/two-phase-across-loop.stderr +++ b/src/test/ui/borrowck/two-phase-across-loop.stderr @@ -2,7 +2,10 @@ error[E0499]: cannot borrow `foo` as mutable more than once at a time --> $DIR/two-phase-across-loop.rs:17:22 | LL | strings.push(foo.get_string()); - | ^^^^^^^^^^^^^^^^ `foo` was mutably borrowed here in the previous iteration of the loop + | -------------^^^^^^^^^^^^^^^^- + | | | + | | `foo` was mutably borrowed here in the previous iteration of the loop + | first borrow used here, in later iteration of loop error: aborting due to previous error diff --git a/src/test/ui/nll/closures-in-loops.stderr b/src/test/ui/nll/closures-in-loops.stderr index 2be0460df1fc..1c1a31d356d6 100644 --- a/src/test/ui/nll/closures-in-loops.stderr +++ b/src/test/ui/nll/closures-in-loops.stderr @@ -13,17 +13,21 @@ error[E0499]: cannot borrow `x` as mutable more than once at a time --> $DIR/closures-in-loops.rs:13:16 | LL | v.push(|| x = String::new()); - | ^^ - borrows occur due to use of `x` in closure - | | - | `x` was mutably borrowed here in the previous iteration of the loop + | -------^^------------------- + | | | | + | | | borrows occur due to use of `x` in closure + | | `x` was mutably borrowed here in the previous iteration of the loop + | first borrow used here, in later iteration of loop error[E0524]: two closures require unique access to `x` at the same time --> $DIR/closures-in-loops.rs:20:16 | LL | v.push(|| *x = String::new()); - | ^^ -- borrows occur due to use of `x` in closure - | | - | closures are constructed here in different iterations of loop + | -------^^-------------------- + | | | | + | | | borrows occur due to use of `x` in closure + | | closures are constructed here in different iterations of loop + | first borrow used here, in later iteration of loop error: aborting due to 3 previous errors From 292f0c599fee4e5feae66d57a00be0af505c74d2 Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 29 Sep 2022 12:14:39 +0200 Subject: [PATCH 018/333] only allow `ConstEquate` with `feature(gce)` --- .../src/traits/fulfill.rs | 25 ++++++++++--------- .../src/traits/select/mod.rs | 24 ++++++++++-------- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index f13736a76b21..19efc2976214 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -495,19 +495,20 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { } ty::PredicateKind::ConstEquate(c1, c2) => { + assert!( + self.selcx.tcx().features().generic_const_exprs, + "`ConstEquate` without a feature gate: {c1:?} {c2:?}", + ); debug!(?c1, ?c2, "equating consts"); - let tcx = self.selcx.tcx(); - if tcx.features().generic_const_exprs { - // FIXME: we probably should only try to unify abstract constants - // if the constants depend on generic parameters. - // - // Let's just see where this breaks :shrug: - if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) = - (c1.kind(), c2.kind()) - { - if infcx.try_unify_abstract_consts(a, b, obligation.param_env) { - return ProcessResult::Changed(vec![]); - } + // FIXME: we probably should only try to unify abstract constants + // if the constants depend on generic parameters. + // + // Let's just see where this breaks :shrug: + if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) = + (c1.kind(), c2.kind()) + { + if infcx.try_unify_abstract_consts(a, b, obligation.param_env) { + return ProcessResult::Changed(vec![]); } } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 0ed59223337a..256102eb98c0 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -677,19 +677,21 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } ty::PredicateKind::ConstEquate(c1, c2) => { + assert!( + self.tcx().features().generic_const_exprs, + "`ConstEquate` without a feature gate: {c1:?} {c2:?}", + ); debug!(?c1, ?c2, "evaluate_predicate_recursively: equating consts"); - if self.tcx().features().generic_const_exprs { - // FIXME: we probably should only try to unify abstract constants - // if the constants depend on generic parameters. - // - // Let's just see where this breaks :shrug: - if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) = - (c1.kind(), c2.kind()) - { - if self.infcx.try_unify_abstract_consts(a, b, obligation.param_env) { - return Ok(EvaluatedToOk); - } + // FIXME: we probably should only try to unify abstract constants + // if the constants depend on generic parameters. + // + // Let's just see where this breaks :shrug: + if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) = + (c1.kind(), c2.kind()) + { + if self.infcx.try_unify_abstract_consts(a, b, obligation.param_env) { + return Ok(EvaluatedToOk); } } From b9e85bf60ab4376a321f03078c06cd95ea5143ef Mon Sep 17 00:00:00 2001 From: Colin Baumgarten Date: Sat, 1 Oct 2022 00:40:59 +0200 Subject: [PATCH 019/333] Detect and reject out-of-range integers in format string literals Until now out-of-range integers in format string literals were silently ignored. They wrapped around to zero at usize::MAX, producing unexpected results. When using debug builds of rustc, such integers in format string literals even cause an 'attempt to add with overflow' panic in rustc. Fix this by producing an error diagnostic for integers in format string literals which do not fit into usize. Fixes #102528 --- compiler/rustc_parse_format/src/lib.rs | 28 ++++++++++++++++++++---- compiler/rustc_parse_format/src/tests.rs | 15 +++++++++++++ library/alloc/src/fmt.rs | 2 +- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index df22d79f82e8..1394993abade 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -740,20 +740,40 @@ impl<'a> Parser<'a> { word } - /// Optionally parses an integer at the current position. This doesn't deal - /// with overflow at all, it's just accumulating digits. fn integer(&mut self) -> Option { - let mut cur = 0; + let mut cur: usize = 0; let mut found = false; + let mut overflow = false; + let start = self.current_pos(); while let Some(&(_, c)) = self.cur.peek() { if let Some(i) = c.to_digit(10) { - cur = cur * 10 + i as usize; + let (tmp, mul_overflow) = cur.overflowing_mul(10); + let (tmp, add_overflow) = tmp.overflowing_add(i as usize); + if mul_overflow || add_overflow { + overflow = true; + } + cur = tmp; found = true; self.cur.next(); } else { break; } } + + if overflow { + let end = self.current_pos(); + let overflowed_int = &self.input[start..end]; + self.err( + format!( + "integer `{}` does not fit into the type `usize` whose range is `0..={}`", + overflowed_int, + usize::MAX + ), + "integer out of range for `usize`", + self.span(start, end), + ); + } + if found { Some(cur) } else { None } } diff --git a/compiler/rustc_parse_format/src/tests.rs b/compiler/rustc_parse_format/src/tests.rs index 2f8c229c68ff..3f9cb149b53e 100644 --- a/compiler/rustc_parse_format/src/tests.rs +++ b/compiler/rustc_parse_format/src/tests.rs @@ -57,6 +57,21 @@ fn invalid06() { musterr("{:>>>}") } +#[test] +fn invalid_position() { + musterr("{18446744073709551616}"); +} + +#[test] +fn invalid_width() { + musterr("{:18446744073709551616}"); +} + +#[test] +fn invalid_precision() { + musterr("{:.18446744073709551616}"); +} + #[test] fn format_nothing() { same( diff --git a/library/alloc/src/fmt.rs b/library/alloc/src/fmt.rs index ed398b56612c..799ce9d5daa8 100644 --- a/library/alloc/src/fmt.rs +++ b/library/alloc/src/fmt.rs @@ -327,7 +327,7 @@ //! - `text` must not contain any `'{'` or `'}'` characters, //! - `ws` is any character for which [`char::is_whitespace`] returns `true`, has no semantic //! meaning and is completely optional, -//! - `integer` is a decimal integer that may contain leading zeroes and +//! - `integer` is a decimal integer that may contain leading zeroes and must fit into an `usize` and //! - `identifier` is an `IDENTIFIER_OR_KEYWORD` (not an `IDENTIFIER`) as defined by the [Rust language reference](https://doc.rust-lang.org/reference/identifiers.html). //! //! # Formatting traits From 5dd44d4d4c4545f65f15f890e93fac68214cfe54 Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 29 Sep 2022 13:16:47 +0800 Subject: [PATCH 020/333] fix #102396, suggest parentheses for possible range methods --- .../locales/en-US/hir_analysis.ftl | 5 + .../src/check/method/suggest.rs | 75 +++++++- compiler/rustc_hir_analysis/src/errors.rs | 25 +++ src/test/ui/methods/issues/issue-90315.rs | 73 +++++++- src/test/ui/methods/issues/issue-90315.stderr | 176 +++++++++++++++++- 5 files changed, 339 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl b/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl index c6a4ff6f0e02..827c3c93a73d 100644 --- a/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl +++ b/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl @@ -133,3 +133,8 @@ hir_analysis_extern_crate_not_idiomatic = .suggestion = convert it to a `{$msg_code}` hir_analysis_expected_used_symbol = expected `used`, `used(compiler)` or `used(linker)` +hir_analysis_expected_used_symbol = expected `used`, `used(compiler)` or `used(linker)` + +hir_analysis_missing_parentheses_in_range = `{$ty_str}` is not an iterator + +hir_analysis_add_missing_parentheses_in_range = you must surround the range in parentheses to call the `{$func_name}` function diff --git a/compiler/rustc_hir_analysis/src/check/method/suggest.rs b/compiler/rustc_hir_analysis/src/check/method/suggest.rs index 0e82e4956c7c..d7189e70d064 100644 --- a/compiler/rustc_hir_analysis/src/check/method/suggest.rs +++ b/compiler/rustc_hir_analysis/src/check/method/suggest.rs @@ -2,6 +2,7 @@ //! found or is otherwise invalid. use crate::check::FnCtxt; +use crate::errors; use rustc_ast::ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{ @@ -12,7 +13,7 @@ use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; -use rustc_hir::{ExprKind, Node, QPath}; +use rustc_hir::{is_range_literal, ExprKind, Node, QPath}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_middle::traits::util::supertraits; use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; @@ -271,9 +272,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; - if self.suggest_constraining_numerical_ty( - tcx, actual, source, span, item_kind, item_name, &ty_str, - ) { + if self.suggest_range_for_iter(tcx, actual, source, span, item_name, &ty_str) + || self.suggest_constraining_numerical_ty( + tcx, actual, source, span, item_kind, item_name, &ty_str, + ) + { return None; } @@ -1201,6 +1204,69 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false } + fn suggest_range_for_iter( + &self, + tcx: TyCtxt<'tcx>, + actual: Ty<'tcx>, + source: SelfSource<'tcx>, + span: Span, + item_name: Ident, + ty_str: &str, + ) -> bool { + if let SelfSource::MethodCall(expr) = source { + let mut search_limit = 5; + for (_, parent) in tcx.hir().parent_iter(expr.hir_id) { + search_limit -= 1; + if search_limit == 0 { + break; + } + + if let Node::Expr(parent_expr) = parent && is_range_literal(parent_expr) { + let span_included = match parent_expr.kind { + hir::ExprKind::Struct(_, eps, _) => + eps.len() > 0 && eps.last().map_or(false, |ep| ep.span.contains(span)), + // `..=` desugars into `::std::ops::RangeInclusive::new(...)`. + hir::ExprKind::Call(ref func, ..) => func.span.contains(span), + _ => false, + }; + + if !span_included { + continue; + } + + let range_def_id = self.tcx.lang_items().range_struct().unwrap(); + let range_ty = self.tcx.bound_type_of(range_def_id).subst(self.tcx, &[actual.into()]); + + // avoid suggesting when the method name is not implemented for a `range` + let pick = self.lookup_probe( + span, + item_name, + range_ty, + expr, + ProbeScope::AllTraits + ); + + if pick.is_ok() { + let range_span = parent_expr.span.with_hi(expr.span.hi()); + tcx.sess.emit_err(errors::MissingParentheseInRange { + span: span, + ty_str: ty_str.to_string(), + add_missing_parentheses: Some( + errors::AddMissingParenthesesInRange { + func_name: item_name.name.as_str().to_string(), + left: range_span.shrink_to_lo(), + right: range_span.shrink_to_hi(), + } + ) + }); + return true; + } + } + } + } + false + } + fn suggest_constraining_numerical_ty( &self, tcx: TyCtxt<'tcx>, @@ -1263,7 +1329,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If this is a floating point literal that ends with '.', // get rid of it to stop this from becoming a member access. let snippet = snippet.strip_suffix('.').unwrap_or(&snippet); - err.span_suggestion( lit.span, &format!( diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index d891171b8246..6634444c636d 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -346,3 +346,28 @@ pub struct ExpectedUsedSymbol { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(hir_analysis::missing_parentheses_in_range, code = "E0599")] +pub struct MissingParentheseInRange { + #[primary_span] + #[label(hir_analysis::missing_parentheses_in_range)] + pub span: Span, + pub ty_str: String, + + #[subdiagnostic] + pub add_missing_parentheses: Option, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion_verbose( + hir_analysis::add_missing_parentheses_in_range, + applicability = "maybe-incorrect" +)] +pub struct AddMissingParenthesesInRange { + pub func_name: String, + #[suggestion_part(code = "(")] + pub left: Span, + #[suggestion_part(code = ")")] + pub right: Span, +} diff --git a/src/test/ui/methods/issues/issue-90315.rs b/src/test/ui/methods/issues/issue-90315.rs index 01bf9f484024..74cd2b358348 100644 --- a/src/test/ui/methods/issues/issue-90315.rs +++ b/src/test/ui/methods/issues/issue-90315.rs @@ -1,7 +1,70 @@ +#![allow(unused)] fn main() { - let arr = &[0,1,2,3]; - for _i in 0..arr.len().rev() { //~ERROR not an iterator - // The above error used to say “the method `rev` exists for type `usize`”. - // This regression test ensures it doesn't say that any more. - } + let arr = &[0, 1, 2, 3]; + for _i in 0..arr.len().rev() { + //~^ ERROR not an iterator + //~| surround the range in parentheses + // The above error used to say “the method `rev` exists for type `usize`”. + // This regression test ensures it doesn't say that any more. + } + + // Test for #102396 + for i in 1..11.rev() { + //~^ ERROR not an iterator + //~| HELP surround the range in parentheses + } + + let end: usize = 10; + for i in 1..end.rev() { + //~^ ERROR not an iterator + //~| HELP surround the range in parentheses + } + + for i in 1..(end + 1).rev() { + //~^ ERROR not an iterator + //~| HELP surround the range in parentheses + } + + if 1..(end + 1).is_empty() { + //~^ ERROR not an iterator + //~| ERROR mismatched types [E0308] + //~| HELP surround the range in parentheses + } + + if 1..(end + 1).is_sorted() { + //~^ ERROR mismatched types [E0308] + //~| ERROR `usize` is not an iterator [E0599] + //~| HELP surround the range in parentheses + } + + let _res: i32 = 3..6.take(2).sum(); + //~^ ERROR `{integer}` is not an iterator [E0599] + //~| ERROR mismatched types [E0308] + //~| HELP surround the range in parentheses + + let _sum: i32 = 3..6.sum(); + //~^ ERROR `{integer}` is not an iterator [E0599] + //~| ERROR mismatched types [E0308] + //~| HELP surround the range in parentheses + + let a = 1 as usize; + let b = 10 as usize; + + for _a in a..=b.rev() { + //~^ ERROR not an iterator + //~| HELP surround the range in parentheses + } + + let _res = ..10.contains(3); + //~^ ERROR not an iterator + //~| HELP surround the range in parentheses + + if 1..end.error_method() { + //~^ ERROR no method named `error_method` + //~| ERROR mismatched types [E0308] + // Won't suggest + } + + let _res = b.take(1)..a; + //~^ ERROR not an iterator } diff --git a/src/test/ui/methods/issues/issue-90315.stderr b/src/test/ui/methods/issues/issue-90315.stderr index c6a76c9e7901..f2084b593c29 100644 --- a/src/test/ui/methods/issues/issue-90315.stderr +++ b/src/test/ui/methods/issues/issue-90315.stderr @@ -1,13 +1,179 @@ error[E0599]: `usize` is not an iterator - --> $DIR/issue-90315.rs:3:26 + --> $DIR/issue-90315.rs:4:28 | -LL | for _i in 0..arr.len().rev() { - | ^^^ `usize` is not an iterator +LL | for _i in 0..arr.len().rev() { + | ^^^ `usize` is not an iterator + | +help: you must surround the range in parentheses to call the `rev` function + | +LL | for _i in (0..arr.len()).rev() { + | + + + +error[E0599]: `{integer}` is not an iterator + --> $DIR/issue-90315.rs:12:20 + | +LL | for i in 1..11.rev() { + | ^^^ `{integer}` is not an iterator + | +help: you must surround the range in parentheses to call the `rev` function + | +LL | for i in (1..11).rev() { + | + + + +error[E0599]: `usize` is not an iterator + --> $DIR/issue-90315.rs:18:21 + | +LL | for i in 1..end.rev() { + | ^^^ `usize` is not an iterator + | +help: you must surround the range in parentheses to call the `rev` function + | +LL | for i in (1..end).rev() { + | + + + +error[E0599]: `usize` is not an iterator + --> $DIR/issue-90315.rs:23:27 + | +LL | for i in 1..(end + 1).rev() { + | ^^^ `usize` is not an iterator + | +help: you must surround the range in parentheses to call the `rev` function + | +LL | for i in (1..(end + 1)).rev() { + | + + + +error[E0599]: `usize` is not an iterator + --> $DIR/issue-90315.rs:28:21 + | +LL | if 1..(end + 1).is_empty() { + | ^^^^^^^^ `usize` is not an iterator + | +help: you must surround the range in parentheses to call the `is_empty` function + | +LL | if (1..(end + 1)).is_empty() { + | + + + +error[E0308]: mismatched types + --> $DIR/issue-90315.rs:28:8 + | +LL | if 1..(end + 1).is_empty() { + | ^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range` + | + = note: expected type `bool` + found struct `std::ops::Range<{integer}>` + +error[E0599]: `usize` is not an iterator + --> $DIR/issue-90315.rs:34:21 + | +LL | if 1..(end + 1).is_sorted() { + | ^^^^^^^^^ `usize` is not an iterator + | +help: you must surround the range in parentheses to call the `is_sorted` function + | +LL | if (1..(end + 1)).is_sorted() { + | + + + +error[E0308]: mismatched types + --> $DIR/issue-90315.rs:34:8 + | +LL | if 1..(end + 1).is_sorted() { + | ^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range` + | + = note: expected type `bool` + found struct `std::ops::Range<{integer}>` + +error[E0599]: `{integer}` is not an iterator + --> $DIR/issue-90315.rs:40:26 + | +LL | let _res: i32 = 3..6.take(2).sum(); + | ^^^^ `{integer}` is not an iterator + | +help: you must surround the range in parentheses to call the `take` function + | +LL | let _res: i32 = (3..6).take(2).sum(); + | + + + +error[E0308]: mismatched types + --> $DIR/issue-90315.rs:40:21 + | +LL | let _res: i32 = 3..6.take(2).sum(); + | --- ^^^^^^^^^^^^^^^^^^ expected `i32`, found struct `std::ops::Range` + | | + | expected due to this + | + = note: expected type `i32` + found struct `std::ops::Range<{integer}>` + +error[E0599]: `{integer}` is not an iterator + --> $DIR/issue-90315.rs:45:26 + | +LL | let _sum: i32 = 3..6.sum(); + | ^^^ `{integer}` is not an iterator + | +help: you must surround the range in parentheses to call the `sum` function + | +LL | let _sum: i32 = (3..6).sum(); + | + + + +error[E0308]: mismatched types + --> $DIR/issue-90315.rs:45:21 + | +LL | let _sum: i32 = 3..6.sum(); + | --- ^^^^^^^^^^ expected `i32`, found struct `std::ops::Range` + | | + | expected due to this + | + = note: expected type `i32` + found struct `std::ops::Range<{integer}>` + +error[E0599]: `usize` is not an iterator + --> $DIR/issue-90315.rs:53:21 + | +LL | for _a in a..=b.rev() { + | ^^^ `usize` is not an iterator + | +help: you must surround the range in parentheses to call the `rev` function + | +LL | for _a in (a..=b).rev() { + | + + + +error[E0599]: `{integer}` is not an iterator + --> $DIR/issue-90315.rs:58:21 + | +LL | let _res = ..10.contains(3); + | ^^^^^^^^ `{integer}` is not an iterator + | +help: you must surround the range in parentheses to call the `contains` function + | +LL | let _res = (..10).contains(3); + | + + + +error[E0599]: no method named `error_method` found for type `usize` in the current scope + --> $DIR/issue-90315.rs:62:15 + | +LL | if 1..end.error_method() { + | ^^^^^^^^^^^^ method not found in `usize` + +error[E0308]: mismatched types + --> $DIR/issue-90315.rs:62:8 + | +LL | if 1..end.error_method() { + | ^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range` + | + = note: expected type `bool` + found struct `std::ops::Range<{integer}>` + +error[E0599]: `usize` is not an iterator + --> $DIR/issue-90315.rs:68:18 + | +LL | let _res = b.take(1)..a; + | ^^^^ `usize` is not an iterator | = note: the following trait bounds were not satisfied: `usize: Iterator` which is required by `&mut usize: Iterator` -error: aborting due to previous error +error: aborting due to 17 previous errors -For more information about this error, try `rustc --explain E0599`. +Some errors have detailed explanations: E0308, E0599. +For more information about an error, try `rustc --explain E0308`. From e747201ad83d384a418dc2b31bf3d3024e2c2a3c Mon Sep 17 00:00:00 2001 From: yukang Date: Mon, 3 Oct 2022 04:22:59 +0800 Subject: [PATCH 021/333] find the correct lang item for ranges --- .../locales/en-US/hir_analysis.ftl | 3 +- .../src/check/method/suggest.rs | 78 +++++++++++-------- compiler/rustc_hir_analysis/src/errors.rs | 3 +- src/test/ui/methods/issues/issue-90315.rs | 28 ++++--- src/test/ui/methods/issues/issue-90315.stderr | 66 ++++++++++------ 5 files changed, 109 insertions(+), 69 deletions(-) diff --git a/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl b/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl index 827c3c93a73d..5a7aee9c1c14 100644 --- a/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl +++ b/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl @@ -133,8 +133,7 @@ hir_analysis_extern_crate_not_idiomatic = .suggestion = convert it to a `{$msg_code}` hir_analysis_expected_used_symbol = expected `used`, `used(compiler)` or `used(linker)` -hir_analysis_expected_used_symbol = expected `used`, `used(compiler)` or `used(linker)` -hir_analysis_missing_parentheses_in_range = `{$ty_str}` is not an iterator +hir_analysis_missing_parentheses_in_range = can't call method `{$method_name}` on type `{$ty_str}` hir_analysis_add_missing_parentheses_in_range = you must surround the range in parentheses to call the `{$func_name}` function diff --git a/compiler/rustc_hir_analysis/src/check/method/suggest.rs b/compiler/rustc_hir_analysis/src/check/method/suggest.rs index d7189e70d064..9aa1b2e2c0a5 100644 --- a/compiler/rustc_hir_analysis/src/check/method/suggest.rs +++ b/compiler/rustc_hir_analysis/src/check/method/suggest.rs @@ -13,7 +13,7 @@ use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; -use rustc_hir::{is_range_literal, ExprKind, Node, QPath}; +use rustc_hir::{ExprKind, Node, QPath}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_middle::traits::util::supertraits; use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; @@ -1214,50 +1214,62 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty_str: &str, ) -> bool { if let SelfSource::MethodCall(expr) = source { - let mut search_limit = 5; - for (_, parent) in tcx.hir().parent_iter(expr.hir_id) { - search_limit -= 1; - if search_limit == 0 { - break; - } - - if let Node::Expr(parent_expr) = parent && is_range_literal(parent_expr) { - let span_included = match parent_expr.kind { - hir::ExprKind::Struct(_, eps, _) => - eps.len() > 0 && eps.last().map_or(false, |ep| ep.span.contains(span)), + for (_, parent) in tcx.hir().parent_iter(expr.hir_id).take(5) { + if let Node::Expr(parent_expr) = parent { + let lang_item = match parent_expr.kind { + ExprKind::Struct(ref qpath, _, _) => match **qpath { + QPath::LangItem(LangItem::Range, ..) => Some(LangItem::Range), + QPath::LangItem(LangItem::RangeTo, ..) => Some(LangItem::RangeTo), + QPath::LangItem(LangItem::RangeToInclusive, ..) => { + Some(LangItem::RangeToInclusive) + } + _ => None, + }, + ExprKind::Call(ref func, _) => match func.kind { // `..=` desugars into `::std::ops::RangeInclusive::new(...)`. - hir::ExprKind::Call(ref func, ..) => func.span.contains(span), - _ => false, + ExprKind::Path(QPath::LangItem(LangItem::RangeInclusiveNew, ..)) => { + Some(LangItem::RangeInclusiveStruct) + } + _ => None, + }, + _ => None, + }; + + if lang_item.is_none() { + continue; + } + + let span_included = match parent_expr.kind { + hir::ExprKind::Struct(_, eps, _) => { + eps.len() > 0 && eps.last().map_or(false, |ep| ep.span.contains(span)) + } + // `..=` desugars into `::std::ops::RangeInclusive::new(...)`. + hir::ExprKind::Call(ref func, ..) => func.span.contains(span), + _ => false, }; if !span_included { continue; } - let range_def_id = self.tcx.lang_items().range_struct().unwrap(); - let range_ty = self.tcx.bound_type_of(range_def_id).subst(self.tcx, &[actual.into()]); - - // avoid suggesting when the method name is not implemented for a `range` - let pick = self.lookup_probe( - span, - item_name, - range_ty, - expr, - ProbeScope::AllTraits - ); + debug!("lang_item: {:?}", lang_item); + let range_def_id = self.tcx.require_lang_item(lang_item.unwrap(), None); + let range_ty = + self.tcx.bound_type_of(range_def_id).subst(self.tcx, &[actual.into()]); + let pick = + self.lookup_probe(span, item_name, range_ty, expr, ProbeScope::AllTraits); if pick.is_ok() { let range_span = parent_expr.span.with_hi(expr.span.hi()); tcx.sess.emit_err(errors::MissingParentheseInRange { - span: span, + span, ty_str: ty_str.to_string(), - add_missing_parentheses: Some( - errors::AddMissingParenthesesInRange { - func_name: item_name.name.as_str().to_string(), - left: range_span.shrink_to_lo(), - right: range_span.shrink_to_hi(), - } - ) + method_name: item_name.as_str().to_string(), + add_missing_parentheses: Some(errors::AddMissingParenthesesInRange { + func_name: item_name.name.as_str().to_string(), + left: range_span.shrink_to_lo(), + right: range_span.shrink_to_hi(), + }), }); return true; } diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 6634444c636d..41f73323d9a9 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -348,12 +348,13 @@ pub struct ExpectedUsedSymbol { } #[derive(Diagnostic)] -#[diag(hir_analysis::missing_parentheses_in_range, code = "E0599")] +#[diag(hir_analysis::missing_parentheses_in_range, code = "E0689")] pub struct MissingParentheseInRange { #[primary_span] #[label(hir_analysis::missing_parentheses_in_range)] pub span: Span, pub ty_str: String, + pub method_name: String, #[subdiagnostic] pub add_missing_parentheses: Option, diff --git a/src/test/ui/methods/issues/issue-90315.rs b/src/test/ui/methods/issues/issue-90315.rs index 74cd2b358348..79cdc41959a7 100644 --- a/src/test/ui/methods/issues/issue-90315.rs +++ b/src/test/ui/methods/issues/issue-90315.rs @@ -2,7 +2,7 @@ fn main() { let arr = &[0, 1, 2, 3]; for _i in 0..arr.len().rev() { - //~^ ERROR not an iterator + //~^ ERROR can't call method //~| surround the range in parentheses // The above error used to say “the method `rev` exists for type `usize`”. // This regression test ensures it doesn't say that any more. @@ -10,40 +10,40 @@ fn main() { // Test for #102396 for i in 1..11.rev() { - //~^ ERROR not an iterator + //~^ ERROR can't call method //~| HELP surround the range in parentheses } let end: usize = 10; for i in 1..end.rev() { - //~^ ERROR not an iterator + //~^ ERROR can't call method //~| HELP surround the range in parentheses } for i in 1..(end + 1).rev() { - //~^ ERROR not an iterator + //~^ ERROR can't call method //~| HELP surround the range in parentheses } if 1..(end + 1).is_empty() { - //~^ ERROR not an iterator + //~^ ERROR can't call method //~| ERROR mismatched types [E0308] //~| HELP surround the range in parentheses } if 1..(end + 1).is_sorted() { //~^ ERROR mismatched types [E0308] - //~| ERROR `usize` is not an iterator [E0599] + //~| ERROR can't call method //~| HELP surround the range in parentheses } let _res: i32 = 3..6.take(2).sum(); - //~^ ERROR `{integer}` is not an iterator [E0599] + //~^ ERROR can't call method //~| ERROR mismatched types [E0308] //~| HELP surround the range in parentheses let _sum: i32 = 3..6.sum(); - //~^ ERROR `{integer}` is not an iterator [E0599] + //~^ ERROR can't call method //~| ERROR mismatched types [E0308] //~| HELP surround the range in parentheses @@ -51,12 +51,12 @@ fn main() { let b = 10 as usize; for _a in a..=b.rev() { - //~^ ERROR not an iterator + //~^ ERROR can't call method //~| HELP surround the range in parentheses } let _res = ..10.contains(3); - //~^ ERROR not an iterator + //~^ ERROR can't call method //~| HELP surround the range in parentheses if 1..end.error_method() { @@ -66,5 +66,11 @@ fn main() { } let _res = b.take(1)..a; - //~^ ERROR not an iterator + //~^ ERROR `usize` is not an iterator + + let _res: i32 = ..6.take(2).sum(); + //~^ can't call method `take` on ambiguous numeric type + //~| ERROR mismatched types [E0308] + //~| HELP you must specify a concrete type for this numeric value + // Won't suggest because `RangeTo` dest not implemented `take` } diff --git a/src/test/ui/methods/issues/issue-90315.stderr b/src/test/ui/methods/issues/issue-90315.stderr index f2084b593c29..581d6fb4fc9e 100644 --- a/src/test/ui/methods/issues/issue-90315.stderr +++ b/src/test/ui/methods/issues/issue-90315.stderr @@ -1,52 +1,52 @@ -error[E0599]: `usize` is not an iterator +error[E0689]: can't call method `rev` on type `usize` --> $DIR/issue-90315.rs:4:28 | LL | for _i in 0..arr.len().rev() { - | ^^^ `usize` is not an iterator + | ^^^ can't call method `rev` on type `usize` | help: you must surround the range in parentheses to call the `rev` function | LL | for _i in (0..arr.len()).rev() { | + + -error[E0599]: `{integer}` is not an iterator +error[E0689]: can't call method `rev` on type `{integer}` --> $DIR/issue-90315.rs:12:20 | LL | for i in 1..11.rev() { - | ^^^ `{integer}` is not an iterator + | ^^^ can't call method `rev` on type `{integer}` | help: you must surround the range in parentheses to call the `rev` function | LL | for i in (1..11).rev() { | + + -error[E0599]: `usize` is not an iterator +error[E0689]: can't call method `rev` on type `usize` --> $DIR/issue-90315.rs:18:21 | LL | for i in 1..end.rev() { - | ^^^ `usize` is not an iterator + | ^^^ can't call method `rev` on type `usize` | help: you must surround the range in parentheses to call the `rev` function | LL | for i in (1..end).rev() { | + + -error[E0599]: `usize` is not an iterator +error[E0689]: can't call method `rev` on type `usize` --> $DIR/issue-90315.rs:23:27 | LL | for i in 1..(end + 1).rev() { - | ^^^ `usize` is not an iterator + | ^^^ can't call method `rev` on type `usize` | help: you must surround the range in parentheses to call the `rev` function | LL | for i in (1..(end + 1)).rev() { | + + -error[E0599]: `usize` is not an iterator +error[E0689]: can't call method `is_empty` on type `usize` --> $DIR/issue-90315.rs:28:21 | LL | if 1..(end + 1).is_empty() { - | ^^^^^^^^ `usize` is not an iterator + | ^^^^^^^^ can't call method `is_empty` on type `usize` | help: you must surround the range in parentheses to call the `is_empty` function | @@ -62,11 +62,11 @@ LL | if 1..(end + 1).is_empty() { = note: expected type `bool` found struct `std::ops::Range<{integer}>` -error[E0599]: `usize` is not an iterator +error[E0689]: can't call method `is_sorted` on type `usize` --> $DIR/issue-90315.rs:34:21 | LL | if 1..(end + 1).is_sorted() { - | ^^^^^^^^^ `usize` is not an iterator + | ^^^^^^^^^ can't call method `is_sorted` on type `usize` | help: you must surround the range in parentheses to call the `is_sorted` function | @@ -82,11 +82,11 @@ LL | if 1..(end + 1).is_sorted() { = note: expected type `bool` found struct `std::ops::Range<{integer}>` -error[E0599]: `{integer}` is not an iterator +error[E0689]: can't call method `take` on type `{integer}` --> $DIR/issue-90315.rs:40:26 | LL | let _res: i32 = 3..6.take(2).sum(); - | ^^^^ `{integer}` is not an iterator + | ^^^^ can't call method `take` on type `{integer}` | help: you must surround the range in parentheses to call the `take` function | @@ -104,11 +104,11 @@ LL | let _res: i32 = 3..6.take(2).sum(); = note: expected type `i32` found struct `std::ops::Range<{integer}>` -error[E0599]: `{integer}` is not an iterator +error[E0689]: can't call method `sum` on type `{integer}` --> $DIR/issue-90315.rs:45:26 | LL | let _sum: i32 = 3..6.sum(); - | ^^^ `{integer}` is not an iterator + | ^^^ can't call method `sum` on type `{integer}` | help: you must surround the range in parentheses to call the `sum` function | @@ -126,22 +126,22 @@ LL | let _sum: i32 = 3..6.sum(); = note: expected type `i32` found struct `std::ops::Range<{integer}>` -error[E0599]: `usize` is not an iterator +error[E0689]: can't call method `rev` on type `usize` --> $DIR/issue-90315.rs:53:21 | LL | for _a in a..=b.rev() { - | ^^^ `usize` is not an iterator + | ^^^ can't call method `rev` on type `usize` | help: you must surround the range in parentheses to call the `rev` function | LL | for _a in (a..=b).rev() { | + + -error[E0599]: `{integer}` is not an iterator +error[E0689]: can't call method `contains` on type `{integer}` --> $DIR/issue-90315.rs:58:21 | LL | let _res = ..10.contains(3); - | ^^^^^^^^ `{integer}` is not an iterator + | ^^^^^^^^ can't call method `contains` on type `{integer}` | help: you must surround the range in parentheses to call the `contains` function | @@ -173,7 +173,29 @@ LL | let _res = b.take(1)..a; `usize: Iterator` which is required by `&mut usize: Iterator` -error: aborting due to 17 previous errors +error[E0689]: can't call method `take` on ambiguous numeric type `{integer}` + --> $DIR/issue-90315.rs:71:25 + | +LL | let _res: i32 = ..6.take(2).sum(); + | ^^^^ + | +help: you must specify a concrete type for this numeric value, like `i32` + | +LL | let _res: i32 = ..6_i32.take(2).sum(); + | ~~~~~ -Some errors have detailed explanations: E0308, E0599. +error[E0308]: mismatched types + --> $DIR/issue-90315.rs:71:21 + | +LL | let _res: i32 = ..6.take(2).sum(); + | --- ^^^^^^^^^^^^^^^^^ expected `i32`, found struct `RangeTo` + | | + | expected due to this + | + = note: expected type `i32` + found struct `RangeTo<_>` + +error: aborting due to 19 previous errors + +Some errors have detailed explanations: E0308, E0599, E0689. For more information about an error, try `rustc --explain E0308`. From 43499da7539d4d0f6bfcea14e19cbe3d7dda4777 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Mon, 3 Oct 2022 17:35:03 -0700 Subject: [PATCH 022/333] Support casting boxes to dyn* --- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 5 +++++ src/test/ui/dyn-star/box.rs | 17 +++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 src/test/ui/dyn-star/box.rs diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 56852b0fcc82..c8ff26f74e3b 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -14,6 +14,7 @@ use rustc_middle::ty::cast::{CastTy, IntTy}; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; use rustc_middle::ty::{self, adjustment::PointerCast, Instance, Ty, TyCtxt}; use rustc_span::source_map::{Span, DUMMY_SP}; +use rustc_target::abi::Size; impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { #[instrument(level = "trace", skip(self, bx))] @@ -285,6 +286,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bug!("Only valid to do a DynStar cast into a DynStar type") }; let vtable = get_vtable(bx.cx(), source.ty(self.mir, bx.tcx()), trait_ref); + let data = match operand.layout.pointee_info_at(bx.cx(), Size::ZERO) { + Some(_) => bx.ptrtoint(data, bx.cx().type_isize()), + None => data, + }; OperandValue::Pair(data, vtable) } mir::CastKind::Pointer( diff --git a/src/test/ui/dyn-star/box.rs b/src/test/ui/dyn-star/box.rs new file mode 100644 index 000000000000..d1f1819d9f35 --- /dev/null +++ b/src/test/ui/dyn-star/box.rs @@ -0,0 +1,17 @@ +// run-pass +// compile-flags: -C opt-level=0 + +#![feature(dyn_star)] +#![allow(incomplete_features)] + +use std::fmt::Display; + +fn make_dyn_star() -> dyn* Display { + Box::new(42) as dyn* Display +} + +fn main() { + let x = make_dyn_star(); + + println!("{x}"); +} From 21047f1a1c0dc1dd36c5d5a0a85d76234dd4a263 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 11 Sep 2022 07:42:04 +0000 Subject: [PATCH 023/333] Support default-body trait functions with RPITIT --- .../rustc_hir_analysis/src/collect/type_of.rs | 5 +- .../src/traits/project.rs | 46 +++++++++++++++++-- .../ui/impl-trait/in-trait/default-body.rs | 15 ++++++ 3 files changed, 60 insertions(+), 6 deletions(-) create mode 100644 src/test/ui/impl-trait/in-trait/default-body.rs diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index f8a62c849107..08a58f4fed80 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -340,10 +340,9 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { .. }) => { if in_trait { - span_bug!(item.span, "impl-trait in trait has no default") - } else { - find_opaque_ty_constraints_for_rpit(tcx, def_id, owner) + assert!(tcx.impl_defaultness(owner).has_value()); } + find_opaque_ty_constraints_for_rpit(tcx, def_id, owner) } ItemKind::Trait(..) | ItemKind::TraitAlias(..) diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 085045bcdcb5..99953be48853 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -71,7 +71,15 @@ enum ProjectionCandidate<'tcx> { /// From an "impl" (or a "pseudo-impl" returned by select) Select(Selection<'tcx>), - ImplTraitInTrait(ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>>), + ImplTraitInTrait(ImplTraitInTraitCandidate<'tcx>), +} + +#[derive(PartialEq, Eq, Debug)] +enum ImplTraitInTraitCandidate<'tcx> { + // The `impl Trait` from a trait function's default body + Trait, + // A concrete type provided from a trait's `impl Trait` from an impl + Impl(ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>>), } enum ProjectionCandidateSet<'tcx> { @@ -1317,6 +1325,17 @@ fn assemble_candidate_for_impl_trait_in_trait<'cx, 'tcx>( ) { let tcx = selcx.tcx(); if tcx.def_kind(obligation.predicate.item_def_id) == DefKind::ImplTraitPlaceholder { + // If we are trying to project an RPITIT with the _identity_ substs, + // then we must be within a default trait body. + if obligation.predicate.substs + == ty::InternalSubsts::identity_for_item(tcx, obligation.predicate.item_def_id) + { + candidate_set.push_candidate(ProjectionCandidate::ImplTraitInTrait( + ImplTraitInTraitCandidate::Trait, + )); + return; + } + let trait_fn_def_id = tcx.impl_trait_in_trait_parent(obligation.predicate.item_def_id); let trait_def_id = tcx.parent(trait_fn_def_id); let trait_substs = @@ -1329,7 +1348,9 @@ fn assemble_candidate_for_impl_trait_in_trait<'cx, 'tcx>( let _ = selcx.infcx().commit_if_ok(|_| match selcx.select(&obligation.with(trait_predicate)) { Ok(Some(super::ImplSource::UserDefined(data))) => { - candidate_set.push_candidate(ProjectionCandidate::ImplTraitInTrait(data)); + candidate_set.push_candidate(ProjectionCandidate::ImplTraitInTrait( + ImplTraitInTraitCandidate::Impl(data), + )); Ok(()) } Ok(None) => { @@ -1792,9 +1813,18 @@ fn confirm_candidate<'cx, 'tcx>( ProjectionCandidate::Select(impl_source) => { confirm_select_candidate(selcx, obligation, impl_source) } - ProjectionCandidate::ImplTraitInTrait(data) => { + ProjectionCandidate::ImplTraitInTrait(ImplTraitInTraitCandidate::Impl(data)) => { confirm_impl_trait_in_trait_candidate(selcx, obligation, data) } + // If we're projecting an RPITIT for a default trait body, that's just + // the same def-id, but as an opaque type (with regular RPIT semantics). + ProjectionCandidate::ImplTraitInTrait(ImplTraitInTraitCandidate::Trait) => Progress { + term: selcx + .tcx() + .mk_opaque(obligation.predicate.item_def_id, obligation.predicate.substs) + .into(), + obligations: vec![], + }, }; // When checking for cycle during evaluation, we compare predicates with @@ -2212,6 +2242,16 @@ fn confirm_impl_trait_in_trait_candidate<'tcx>( return Progress { term: tcx.ty_error().into(), obligations }; } + // Use the default `impl Trait` for the trait, e.g., for a default trait body + if leaf_def.item.container == ty::AssocItemContainer::TraitContainer { + return Progress { + term: tcx + .mk_opaque(obligation.predicate.item_def_id, obligation.predicate.substs) + .into(), + obligations, + }; + } + let impl_fn_def_id = leaf_def.item.def_id; let impl_fn_substs = obligation.predicate.substs.rebase_onto(tcx, trait_fn_def_id, data.substs); diff --git a/src/test/ui/impl-trait/in-trait/default-body.rs b/src/test/ui/impl-trait/in-trait/default-body.rs new file mode 100644 index 000000000000..effc4b1d951a --- /dev/null +++ b/src/test/ui/impl-trait/in-trait/default-body.rs @@ -0,0 +1,15 @@ +// check-pass +// edition:2021 + +#![feature(return_position_impl_trait_in_trait)] +#![allow(incomplete_features)] + +use std::fmt::Debug; + +trait Foo { + async fn baz() -> impl Debug { + Self::baz().await + } +} + +fn main() {} From 79450360d279f169a40bc8fd55536830f5a939fe Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 5 Oct 2022 04:16:05 +0000 Subject: [PATCH 024/333] Fix test for default body with impl --- compiler/rustc_middle/src/ty/sty.rs | 1 + compiler/rustc_privacy/src/lib.rs | 16 ++++++++++++++-- .../rustc_trait_selection/src/traits/project.rs | 5 +++-- src/test/ui/impl-trait/in-trait/default-body.rs | 14 ++++++++++---- 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 2f6ec836c3c0..f8a169fc869f 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1160,6 +1160,7 @@ impl<'tcx> ProjectionTy<'tcx> { &self, tcx: TyCtxt<'tcx>, ) -> (ty::TraitRef<'tcx>, &'tcx [ty::GenericArg<'tcx>]) { + assert_eq!(tcx.def_kind(def_id), DefKind::Trait); let def_id = tcx.parent(self.item_def_id); let trait_generics = tcx.generics_of(def_id); ( diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 841e3ebb2a13..4ec9d0b08528 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -122,8 +122,20 @@ where &mut self, projection: ty::ProjectionTy<'tcx>, ) -> ControlFlow { - let (trait_ref, assoc_substs) = - projection.trait_ref_and_own_substs(self.def_id_visitor.tcx()); + let tcx = self.def_id_visitor.tcx(); + let (trait_ref, assoc_substs) = if tcx.def_kind(projection.item_def_id) + != DefKind::ImplTraitPlaceholder + { + projection.trait_ref_and_own_substs(tcx) + } else { + // HACK(RPITIT): Remove this when RPITITs are lowered to regular assoc tys + let def_id = tcx.impl_trait_in_trait_parent(projection.item_def_id); + let trait_generics = tcx.generics_of(def_id); + ( + ty::TraitRef { def_id, substs: projection.substs.truncate_to(tcx, trait_generics) }, + &projection.substs[trait_generics.count()..], + ) + }; self.visit_trait(trait_ref)?; if self.def_id_visitor.shallow() { ControlFlow::CONTINUE diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 99953be48853..6a4ea114b401 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1325,10 +1325,11 @@ fn assemble_candidate_for_impl_trait_in_trait<'cx, 'tcx>( ) { let tcx = selcx.tcx(); if tcx.def_kind(obligation.predicate.item_def_id) == DefKind::ImplTraitPlaceholder { - // If we are trying to project an RPITIT with the _identity_ substs, + // If we are trying to project an RPITIT with trait's default `Self` parameter, // then we must be within a default trait body. - if obligation.predicate.substs + if obligation.predicate.self_ty() == ty::InternalSubsts::identity_for_item(tcx, obligation.predicate.item_def_id) + .type_at(0) { candidate_set.push_candidate(ProjectionCandidate::ImplTraitInTrait( ImplTraitInTraitCandidate::Trait, diff --git a/src/test/ui/impl-trait/in-trait/default-body.rs b/src/test/ui/impl-trait/in-trait/default-body.rs index effc4b1d951a..b0baf5bb10dd 100644 --- a/src/test/ui/impl-trait/in-trait/default-body.rs +++ b/src/test/ui/impl-trait/in-trait/default-body.rs @@ -1,15 +1,21 @@ // check-pass // edition:2021 -#![feature(return_position_impl_trait_in_trait)] +#![feature(async_fn_in_trait, return_position_impl_trait_in_trait)] #![allow(incomplete_features)] use std::fmt::Debug; trait Foo { - async fn baz() -> impl Debug { - Self::baz().await + async fn baz(&self) -> &str { + "" } } -fn main() {} +struct Bar; + +impl Foo for Bar {} + +fn main() { + let _ = Bar.baz(); +} From 86734b04a7c36da0f84226f687f0da331b7f921f Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 11 Sep 2022 08:51:25 +0000 Subject: [PATCH 025/333] Bless tests --- src/test/ui/async-await/async-trait-fn.rs | 3 - src/test/ui/async-await/async-trait-fn.stderr | 56 ++----------------- .../edition-deny-async-fns-2015.rs | 1 - .../edition-deny-async-fns-2015.stderr | 26 ++------- ...021-incompatible-closure-captures-93117.rs | 1 - ...incompatible-closure-captures-93117.stderr | 22 ++------ 6 files changed, 14 insertions(+), 95 deletions(-) diff --git a/src/test/ui/async-await/async-trait-fn.rs b/src/test/ui/async-await/async-trait-fn.rs index 0ea685986db4..e2062e82725c 100644 --- a/src/test/ui/async-await/async-trait-fn.rs +++ b/src/test/ui/async-await/async-trait-fn.rs @@ -1,11 +1,8 @@ // edition:2018 trait T { async fn foo() {} //~ ERROR functions in traits cannot be declared `async` - //~^ ERROR mismatched types async fn bar(&self) {} //~ ERROR functions in traits cannot be declared `async` - //~^ ERROR mismatched types async fn baz() { //~ ERROR functions in traits cannot be declared `async` - //~^ ERROR mismatched types // Nested item must not ICE. fn a() {} } diff --git a/src/test/ui/async-await/async-trait-fn.stderr b/src/test/ui/async-await/async-trait-fn.stderr index 4fa54c6e3696..afbe25cf7ab7 100644 --- a/src/test/ui/async-await/async-trait-fn.stderr +++ b/src/test/ui/async-await/async-trait-fn.stderr @@ -12,7 +12,7 @@ LL | async fn foo() {} = help: add `#![feature(async_fn_in_trait)]` to the crate attributes to enable error[E0706]: functions in traits cannot be declared `async` - --> $DIR/async-trait-fn.rs:5:5 + --> $DIR/async-trait-fn.rs:4:5 | LL | async fn bar(&self) {} | -----^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL | async fn bar(&self) {} = help: add `#![feature(async_fn_in_trait)]` to the crate attributes to enable error[E0706]: functions in traits cannot be declared `async` - --> $DIR/async-trait-fn.rs:7:5 + --> $DIR/async-trait-fn.rs:5:5 | LL | async fn baz() { | -----^^^^^^^^^ @@ -37,54 +37,6 @@ LL | async fn baz() { = note: see issue #91611 for more information = help: add `#![feature(async_fn_in_trait)]` to the crate attributes to enable -error[E0308]: mismatched types - --> $DIR/async-trait-fn.rs:3:20 - | -LL | async fn foo() {} - | ^^ expected associated type, found opaque type - | - ::: $SRC_DIR/core/src/future/mod.rs:LL:COL - | -LL | pub const fn from_generator(gen: T) -> impl Future - | ------------------------------- the found opaque type - | - = note: expected associated type `impl Future` (trait associated opaque type at <$DIR/async-trait-fn.rs:3:20>) - found opaque type `impl Future` (opaque type at <$SRC_DIR/core/src/future/mod.rs:LL:COL>) +error: aborting due to 3 previous errors -error[E0308]: mismatched types - --> $DIR/async-trait-fn.rs:5:25 - | -LL | async fn bar(&self) {} - | ^^ expected associated type, found opaque type - | - ::: $SRC_DIR/core/src/future/mod.rs:LL:COL - | -LL | pub const fn from_generator(gen: T) -> impl Future - | ------------------------------- the found opaque type - | - = note: expected associated type `impl Future` (trait associated opaque type at <$DIR/async-trait-fn.rs:5:25>) - found opaque type `impl Future` (opaque type at <$SRC_DIR/core/src/future/mod.rs:LL:COL>) - -error[E0308]: mismatched types - --> $DIR/async-trait-fn.rs:7:20 - | -LL | async fn baz() { - | ____________________^ -LL | | -LL | | // Nested item must not ICE. -LL | | fn a() {} -LL | | } - | |_____^ expected associated type, found opaque type - | - ::: $SRC_DIR/core/src/future/mod.rs:LL:COL - | -LL | pub const fn from_generator(gen: T) -> impl Future - | ------------------------------- the found opaque type - | - = note: expected associated type `impl Future` (trait associated opaque type at <$DIR/async-trait-fn.rs:7:20>) - found opaque type `impl Future` (opaque type at <$SRC_DIR/core/src/future/mod.rs:LL:COL>) - -error: aborting due to 6 previous errors - -Some errors have detailed explanations: E0308, E0706. -For more information about an error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0706`. diff --git a/src/test/ui/async-await/edition-deny-async-fns-2015.rs b/src/test/ui/async-await/edition-deny-async-fns-2015.rs index 22a61dcd25f9..6bd6d879a4ac 100644 --- a/src/test/ui/async-await/edition-deny-async-fns-2015.rs +++ b/src/test/ui/async-await/edition-deny-async-fns-2015.rs @@ -17,7 +17,6 @@ impl Foo { trait Bar { async fn foo() {} //~ ERROR `async fn` is not permitted in Rust 2015 //~^ ERROR functions in traits cannot be declared `async` - //~| ERROR mismatched types } fn main() { diff --git a/src/test/ui/async-await/edition-deny-async-fns-2015.stderr b/src/test/ui/async-await/edition-deny-async-fns-2015.stderr index 62a243e69e74..ba918eb28def 100644 --- a/src/test/ui/async-await/edition-deny-async-fns-2015.stderr +++ b/src/test/ui/async-await/edition-deny-async-fns-2015.stderr @@ -53,7 +53,7 @@ LL | async fn foo() {} = note: for more on editions, read https://doc.rust-lang.org/edition-guide error[E0670]: `async fn` is not permitted in Rust 2015 - --> $DIR/edition-deny-async-fns-2015.rs:37:9 + --> $DIR/edition-deny-async-fns-2015.rs:36:9 | LL | async fn bar() {} | ^^^^^ to use `async fn`, switch to Rust 2018 or later @@ -62,7 +62,7 @@ LL | async fn bar() {} = note: for more on editions, read https://doc.rust-lang.org/edition-guide error[E0670]: `async fn` is not permitted in Rust 2015 - --> $DIR/edition-deny-async-fns-2015.rs:27:9 + --> $DIR/edition-deny-async-fns-2015.rs:26:9 | LL | async fn foo() {} | ^^^^^ to use `async fn`, switch to Rust 2018 or later @@ -71,7 +71,7 @@ LL | async fn foo() {} = note: for more on editions, read https://doc.rust-lang.org/edition-guide error[E0670]: `async fn` is not permitted in Rust 2015 - --> $DIR/edition-deny-async-fns-2015.rs:32:13 + --> $DIR/edition-deny-async-fns-2015.rs:31:13 | LL | async fn bar() {} | ^^^^^ to use `async fn`, switch to Rust 2018 or later @@ -92,21 +92,7 @@ LL | async fn foo() {} = note: see issue #91611 for more information = help: add `#![feature(async_fn_in_trait)]` to the crate attributes to enable -error[E0308]: mismatched types - --> $DIR/edition-deny-async-fns-2015.rs:18:20 - | -LL | async fn foo() {} - | ^^ expected associated type, found opaque type - | - ::: $SRC_DIR/core/src/future/mod.rs:LL:COL - | -LL | pub const fn from_generator(gen: T) -> impl Future - | ------------------------------- the found opaque type - | - = note: expected associated type `impl Future` (trait associated opaque type at <$DIR/edition-deny-async-fns-2015.rs:18:20>) - found opaque type `impl Future` (opaque type at <$SRC_DIR/core/src/future/mod.rs:LL:COL>) +error: aborting due to 10 previous errors -error: aborting due to 11 previous errors - -Some errors have detailed explanations: E0308, E0670, E0706. -For more information about an error, try `rustc --explain E0308`. +Some errors have detailed explanations: E0670, E0706. +For more information about an error, try `rustc --explain E0670`. diff --git a/src/test/ui/span/drop-location-span-error-rust-2021-incompatible-closure-captures-93117.rs b/src/test/ui/span/drop-location-span-error-rust-2021-incompatible-closure-captures-93117.rs index 94f578af209a..4559da91e47a 100644 --- a/src/test/ui/span/drop-location-span-error-rust-2021-incompatible-closure-captures-93117.rs +++ b/src/test/ui/span/drop-location-span-error-rust-2021-incompatible-closure-captures-93117.rs @@ -12,7 +12,6 @@ impl A { trait C{async fn new(val: T) {} //~ ERROR `async fn` is not permitted in Rust 2015 //~^ ERROR functions in traits cannot be declared `async` -//~| ERROR mismatched types //~| ERROR cannot find type `T` in this scope //~| WARN changes to closure capture in Rust 2021 will affect drop order [rust_2021_incompatible_closure_captures] diff --git a/src/test/ui/span/drop-location-span-error-rust-2021-incompatible-closure-captures-93117.stderr b/src/test/ui/span/drop-location-span-error-rust-2021-incompatible-closure-captures-93117.stderr index ee72a0c65c8e..f7109d406d57 100644 --- a/src/test/ui/span/drop-location-span-error-rust-2021-incompatible-closure-captures-93117.stderr +++ b/src/test/ui/span/drop-location-span-error-rust-2021-incompatible-closure-captures-93117.stderr @@ -1,5 +1,5 @@ error: this file contains an unclosed delimiter - --> $DIR/drop-location-span-error-rust-2021-incompatible-closure-captures-93117.rs:19:53 + --> $DIR/drop-location-span-error-rust-2021-incompatible-closure-captures-93117.rs:18:53 | LL | trait C{async fn new(val: T) {} | - unclosed delimiter @@ -74,20 +74,6 @@ help: add a dummy let to cause `path` to be fully captured LL | async fn create(path: impl AsRef) { let _ = &path; | ++++++++++++++ -error[E0308]: mismatched types - --> $DIR/drop-location-span-error-rust-2021-incompatible-closure-captures-93117.rs:13:30 - | -LL | trait C{async fn new(val: T) {} - | ^^ expected associated type, found opaque type - | - ::: $SRC_DIR/core/src/future/mod.rs:LL:COL - | -LL | pub const fn from_generator(gen: T) -> impl Future - | ------------------------------- the found opaque type - | - = note: expected associated type `impl Future` (trait associated opaque type at <$DIR/drop-location-span-error-rust-2021-incompatible-closure-captures-93117.rs:13:30>) - found opaque type `impl Future` (opaque type at <$SRC_DIR/core/src/future/mod.rs:LL:COL>) - warning: changes to closure capture in Rust 2021 will affect drop order --> $DIR/drop-location-span-error-rust-2021-incompatible-closure-captures-93117.rs:13:30 | @@ -103,7 +89,7 @@ help: add a dummy let to cause `val` to be fully captured LL | trait C{async fn new(val: T) { let _ = &val;} | +++++++++++++ -error: aborting due to 7 previous errors; 2 warnings emitted +error: aborting due to 6 previous errors; 2 warnings emitted -Some errors have detailed explanations: E0308, E0412, E0423, E0670, E0706. -For more information about an error, try `rustc --explain E0308`. +Some errors have detailed explanations: E0412, E0423, E0670, E0706. +For more information about an error, try `rustc --explain E0412`. From 9a5936b8144c412394ecd0d9dd931faeb4d2034d Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 30 Sep 2022 19:16:13 +0000 Subject: [PATCH 026/333] Validate opaques in default trait bodies, don't normalize unless a body is provided --- .../rustc_hir_analysis/src/check/check.rs | 62 +++++++++++-------- compiler/rustc_middle/src/ty/sty.rs | 2 +- .../src/traits/project.rs | 3 +- 3 files changed, 39 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 824144aeac00..bf022286b825 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -525,23 +525,33 @@ fn check_static_inhabited<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) { /// Checks that an opaque type does not contain cycles and does not use `Self` or `T::Foo` /// projections that would result in "inheriting lifetimes". -pub(super) fn check_opaque<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: LocalDefId, - substs: SubstsRef<'tcx>, - origin: &hir::OpaqueTyOrigin, -) { - let span = tcx.def_span(def_id); - check_opaque_for_inheriting_lifetimes(tcx, def_id, span); - if tcx.type_of(def_id).references_error() { +fn check_opaque<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) { + let item = tcx.hir().item(id); + let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item.kind else { + tcx.sess.delay_span_bug(tcx.hir().span(id.hir_id()), "expected opaque item"); return; - } - if check_opaque_for_cycles(tcx, def_id, substs, span, origin).is_err() { - return; - } - check_opaque_meets_bounds(tcx, def_id, substs, span, origin); -} + }; + // HACK(jynelson): trying to infer the type of `impl trait` breaks documenting + // `async-std` (and `pub async fn` in general). + // Since rustdoc doesn't care about the concrete type behind `impl Trait`, just don't look at it! + // See https://github.com/rust-lang/rust/issues/75100 + if tcx.sess.opts.actually_rustdoc { + return; + } + + let substs = InternalSubsts::identity_for_item(tcx, item.def_id.to_def_id()); + let span = tcx.def_span(item.def_id.def_id); + + check_opaque_for_inheriting_lifetimes(tcx, item.def_id.def_id, span); + if tcx.type_of(item.def_id.def_id).references_error() { + return; + } + if check_opaque_for_cycles(tcx, item.def_id.def_id, substs, span, &origin).is_err() { + return; + } + check_opaque_meets_bounds(tcx, item.def_id.def_id, substs, span, &origin); +} /// Checks that an opaque type does not use `Self` or `T::Foo` projections that would result /// in "inheriting lifetimes". #[instrument(level = "debug", skip(tcx, span))] @@ -858,17 +868,17 @@ fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) { check_union(tcx, id.def_id.def_id); } DefKind::OpaqueTy => { - let item = tcx.hir().item(id); - let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item.kind else { - return; - }; - // HACK(jynelson): trying to infer the type of `impl trait` breaks documenting - // `async-std` (and `pub async fn` in general). - // Since rustdoc doesn't care about the concrete type behind `impl Trait`, just don't look at it! - // See https://github.com/rust-lang/rust/issues/75100 - if !tcx.sess.opts.actually_rustdoc { - let substs = InternalSubsts::identity_for_item(tcx, item.def_id.to_def_id()); - check_opaque(tcx, item.def_id.def_id, substs, &origin); + check_opaque(tcx, id); + } + DefKind::ImplTraitPlaceholder => { + let parent = tcx.impl_trait_in_trait_parent(id.def_id.to_def_id()); + // Only check the validity of this opaque type if the function has a default body + if let hir::Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(_)), + .. + }) = tcx.hir().get_by_def_id(parent.expect_local()) + { + check_opaque(tcx, id); } } DefKind::TyAlias => { diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index f8a169fc869f..646369633133 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1160,8 +1160,8 @@ impl<'tcx> ProjectionTy<'tcx> { &self, tcx: TyCtxt<'tcx>, ) -> (ty::TraitRef<'tcx>, &'tcx [ty::GenericArg<'tcx>]) { - assert_eq!(tcx.def_kind(def_id), DefKind::Trait); let def_id = tcx.parent(self.item_def_id); + assert_eq!(tcx.def_kind(def_id), DefKind::Trait); let trait_generics = tcx.generics_of(def_id); ( ty::TraitRef { def_id, substs: self.substs.truncate_to(tcx, trait_generics) }, diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 6a4ea114b401..8e2f1d5def3d 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1325,11 +1325,13 @@ fn assemble_candidate_for_impl_trait_in_trait<'cx, 'tcx>( ) { let tcx = selcx.tcx(); if tcx.def_kind(obligation.predicate.item_def_id) == DefKind::ImplTraitPlaceholder { + let trait_fn_def_id = tcx.impl_trait_in_trait_parent(obligation.predicate.item_def_id); // If we are trying to project an RPITIT with trait's default `Self` parameter, // then we must be within a default trait body. if obligation.predicate.self_ty() == ty::InternalSubsts::identity_for_item(tcx, obligation.predicate.item_def_id) .type_at(0) + && tcx.associated_item(trait_fn_def_id).defaultness(tcx).has_value() { candidate_set.push_candidate(ProjectionCandidate::ImplTraitInTrait( ImplTraitInTraitCandidate::Trait, @@ -1337,7 +1339,6 @@ fn assemble_candidate_for_impl_trait_in_trait<'cx, 'tcx>( return; } - let trait_fn_def_id = tcx.impl_trait_in_trait_parent(obligation.predicate.item_def_id); let trait_def_id = tcx.parent(trait_fn_def_id); let trait_substs = obligation.predicate.substs.truncate_to(tcx, tcx.generics_of(trait_def_id)); From 28c21bc8a8044ae232558a4d359045008df63597 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 6 Oct 2022 14:31:06 +0200 Subject: [PATCH 027/333] internal: Allow minicore flags specification to be order independent --- crates/test-utils/src/fixture.rs | 53 +++++++++++++++--------------- crates/test-utils/src/minicore.rs | 54 +++++++++++++++---------------- 2 files changed, 54 insertions(+), 53 deletions(-) diff --git a/crates/test-utils/src/fixture.rs b/crates/test-utils/src/fixture.rs index 8c806e7925b1..c824f5af7258 100644 --- a/crates/test-utils/src/fixture.rs +++ b/crates/test-utils/src/fixture.rs @@ -61,6 +61,8 @@ //! " //! ``` +use std::iter; + use rustc_hash::FxHashMap; use stdx::trim_indent; @@ -259,7 +261,7 @@ impl MiniCore { if res.has_flag(entry) { panic!("duplicate minicore flag: {:?}", entry); } - res.activated_flags.push(entry.to_string()); + res.activated_flags.push(entry.to_owned()); } res @@ -273,35 +275,34 @@ impl MiniCore { let raw_mini_core = include_str!("./minicore.rs"); let mut lines = raw_mini_core.split_inclusive('\n'); - let mut parsing_flags = false; let mut implications = Vec::new(); // Parse `//!` preamble and extract flags and dependencies. - for line in lines.by_ref() { - let line = match line.strip_prefix("//!") { - Some(it) => it, - None => { - assert!(line.trim().is_empty()); - break; - } - }; - - if parsing_flags { - let (flag, deps) = line.split_once(':').unwrap(); - let flag = flag.trim(); - self.valid_flags.push(flag.to_string()); - for dep in deps.split(", ") { - let dep = dep.trim(); - if !dep.is_empty() { - self.assert_valid_flag(dep); - implications.push((flag, dep)); - } - } + let trim_doc: fn(&str) -> Option<&str> = |line| match line.strip_prefix("//!") { + Some(it) => Some(it), + None => { + assert!(line.trim().is_empty(), "expected empty line after minicore header"); + None } + }; + for line in lines + .by_ref() + .map_while(trim_doc) + .skip_while(|line| !line.contains("Available flags:")) + .skip(1) + { + let (flag, deps) = line.split_once(':').unwrap(); + let flag = flag.trim(); - if line.contains("Available flags:") { - parsing_flags = true; - } + self.valid_flags.push(flag.to_string()); + implications.extend( + iter::repeat(flag) + .zip(deps.split(", ").map(str::trim).filter(|dep| !dep.is_empty())), + ); + } + + for (_, dep) in &implications { + self.assert_valid_flag(dep); } for flag in &self.activated_flags { @@ -332,7 +333,7 @@ impl MiniCore { } if let Some(region) = trimmed.strip_prefix("// endregion:") { let prev = active_regions.pop().unwrap(); - assert_eq!(prev, region); + assert_eq!(prev, region, "unbalanced region pairs"); continue; } diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index 10386b5b7bcd..69d2e62b2567 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -8,36 +8,36 @@ //! We then strip all the code marked with other flags. //! //! Available flags: -//! sized: -//! unsize: sized -//! coerce_unsized: unsize -//! slice: -//! range: -//! deref: sized -//! deref_mut: deref -//! index: sized -//! fn: -//! try: -//! pin: -//! future: pin -//! option: -//! result: -//! iterator: option -//! iterators: iterator, fn -//! default: sized -//! hash: -//! clone: sized -//! copy: clone -//! from: sized -//! eq: sized -//! ord: eq, option -//! derive: -//! fmt: result -//! bool_impl: option, fn //! add: //! as_ref: sized +//! bool_impl: option, fn +//! clone: sized +//! coerce_unsized: unsize +//! copy: clone +//! default: sized +//! deref_mut: deref +//! deref: sized +//! derive: //! drop: +//! eq: sized +//! fmt: result +//! fn: +//! from: sized +//! future: pin //! generator: pin +//! hash: +//! index: sized +//! iterator: option +//! iterators: iterator, fn +//! option: +//! ord: eq, option +//! pin: +//! range: +//! result: +//! sized: +//! slice: +//! try: +//! unsize: sized pub mod marker { // region:sized @@ -584,7 +584,7 @@ pub mod iter { } } } - pub use self::adapters::{Take, FilterMap}; + pub use self::adapters::{FilterMap, Take}; mod sources { mod repeat { From 75f4c54d8c1304966dff2e1d45cff88ac96a9d0f Mon Sep 17 00:00:00 2001 From: btwotwo <10519967+btwotwo@users.noreply.github.com> Date: Wed, 5 Oct 2022 00:02:03 +0200 Subject: [PATCH 028/333] Add stub for cargo environment variables auto completion --- crates/ide-completion/src/completions.rs | 1 + .../src/completions/env_vars.rs | 60 +++++++++++++++++++ crates/ide-completion/src/lib.rs | 1 + 3 files changed, 62 insertions(+) create mode 100644 crates/ide-completion/src/completions/env_vars.rs diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs index 97b90c62dd77..296dfc14250f 100644 --- a/crates/ide-completion/src/completions.rs +++ b/crates/ide-completion/src/completions.rs @@ -19,6 +19,7 @@ pub(crate) mod snippet; pub(crate) mod r#type; pub(crate) mod use_; pub(crate) mod vis; +pub(crate) mod env_vars; use std::iter; diff --git a/crates/ide-completion/src/completions/env_vars.rs b/crates/ide-completion/src/completions/env_vars.rs new file mode 100644 index 000000000000..6cfbd0f922f8 --- /dev/null +++ b/crates/ide-completion/src/completions/env_vars.rs @@ -0,0 +1,60 @@ +//! Completes environment variables defined by Cargo (https://doc.rust-lang.org/cargo/reference/environment-variables.html) + +use syntax::{ast, AstToken, AstNode, TextRange, TextSize}; + +use crate::{context::CompletionContext, CompletionItem, CompletionItemKind}; + +use super::Completions; + +pub(crate) fn complete_cargo_env_vars( + acc: &mut Completions, + ctx: &CompletionContext<'_>, + original: &ast::String +) { + if !is_env_macro(original) { + return; + } + + let start = ctx.original_token.text_range().start() + TextSize::from(1); + let cursor = ctx.position.offset; + + CompletionItem::new(CompletionItemKind::Binding, TextRange::new(start, cursor), "CARGO").add_to(acc); +} + +fn is_env_macro(string: &ast::String) -> bool { + //todo: replace copypaste from format_string with separate function + (|| { + let macro_call = string.syntax().parent_ancestors().find_map(ast::MacroCall::cast)?; + let name = macro_call.path()?.segment()?.name_ref()?; + + if !matches!(name.text().as_str(), + "env" | "option_env") { + return None; + } + + + Some(()) + })() + .is_some() +} + +#[cfg(test)] +mod tests { + use expect_test::{expect, Expect}; + use crate::tests::{check_edit}; + + #[test] + fn completes_env_variables() { + check_edit("CARGO", + r#" + fn main() { + let foo = env!("CA$0); + } + "# + ,r#" + fn main() { + let foo = env!("CARGO); + } + "#) + } +} \ No newline at end of file diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs index 8d21f4fce0a2..cd9da0d3dcf0 100644 --- a/crates/ide-completion/src/lib.rs +++ b/crates/ide-completion/src/lib.rs @@ -183,6 +183,7 @@ pub fn completions( CompletionAnalysis::String { original, expanded: Some(expanded) } => { completions::extern_abi::complete_extern_abi(acc, ctx, expanded); completions::format_string::format_string(acc, ctx, original, expanded); + completions::env_vars::complete_cargo_env_vars(acc, ctx, original) } CompletionAnalysis::UnexpandedAttrTT { colon_prefix, From 07621ce09676b5a2267c8305ba85b3d37e1403f4 Mon Sep 17 00:00:00 2001 From: btwotwo <10519967+btwotwo@users.noreply.github.com> Date: Thu, 6 Oct 2022 16:25:12 +0200 Subject: [PATCH 029/333] Use expanded version of text for env var completion --- crates/ide-completion/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs index cd9da0d3dcf0..c5718ec40f58 100644 --- a/crates/ide-completion/src/lib.rs +++ b/crates/ide-completion/src/lib.rs @@ -183,7 +183,7 @@ pub fn completions( CompletionAnalysis::String { original, expanded: Some(expanded) } => { completions::extern_abi::complete_extern_abi(acc, ctx, expanded); completions::format_string::format_string(acc, ctx, original, expanded); - completions::env_vars::complete_cargo_env_vars(acc, ctx, original) + completions::env_vars::complete_cargo_env_vars(acc, expanded); } CompletionAnalysis::UnexpandedAttrTT { colon_prefix, From 08b0c92105fc9a0139a30f8215c7ca44e55ac7b0 Mon Sep 17 00:00:00 2001 From: btwotwo <10519967+btwotwo@users.noreply.github.com> Date: Thu, 6 Oct 2022 16:26:49 +0200 Subject: [PATCH 030/333] Add helper method to get a macro name from passed string --- crates/ide-db/src/syntax_helpers/node_ext.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/crates/ide-db/src/syntax_helpers/node_ext.rs b/crates/ide-db/src/syntax_helpers/node_ext.rs index b890e2b58df8..78d6bd008afe 100644 --- a/crates/ide-db/src/syntax_helpers/node_ext.rs +++ b/crates/ide-db/src/syntax_helpers/node_ext.rs @@ -2,8 +2,8 @@ use itertools::Itertools; use parser::T; use syntax::{ - ast::{self, HasLoopBody, PathSegmentKind, VisibilityKind}, - AstNode, Preorder, RustLanguage, WalkEvent, + ast::{self, HasLoopBody, PathSegmentKind, VisibilityKind, NameRef}, + AstNode, Preorder, RustLanguage, WalkEvent, AstToken, }; pub fn expr_as_name_ref(expr: &ast::Expr) -> Option { @@ -457,3 +457,10 @@ pub fn parse_tt_as_comma_sep_paths(input: ast::TokenTree) -> Option Option { + let macro_call = string.syntax().parent_ancestors().find_map(ast::MacroCall::cast)?; + let name = macro_call.path()?.segment()?.name_ref()?; + + Some(name) +} \ No newline at end of file From ddf68ea7c330ec4fef8564062af2a2e15dd5f45e Mon Sep 17 00:00:00 2001 From: btwotwo <10519967+btwotwo@users.noreply.github.com> Date: Thu, 6 Oct 2022 16:27:27 +0200 Subject: [PATCH 031/333] Add const list of cargo-defined env variables with descriptions --- .../src/completions/env_vars.rs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/crates/ide-completion/src/completions/env_vars.rs b/crates/ide-completion/src/completions/env_vars.rs index 6cfbd0f922f8..eba39bba3d5d 100644 --- a/crates/ide-completion/src/completions/env_vars.rs +++ b/crates/ide-completion/src/completions/env_vars.rs @@ -5,6 +5,27 @@ use syntax::{ast, AstToken, AstNode, TextRange, TextSize}; use crate::{context::CompletionContext, CompletionItem, CompletionItemKind}; use super::Completions; +const CARGO_DEFINED_VARS: &[(&str, &str)] = &[ +("CARGO","Path to the cargo binary performing the build"), +("CARGO_MANIFEST_DIR","The directory containing the manifest of your package"), +("CARGO_PKG_VERSION","The full version of your package"), +("CARGO_PKG_VERSION_MAJOR","The major version of your package"), +("CARGO_PKG_VERSION_MINOR","The minor version of your package"), +("CARGO_PKG_VERSION_PATCH","The patch version of your package"), +("CARGO_PKG_VERSION_PRE","The pre-release version of your package"), +("CARGO_PKG_AUTHORS","Colon separated list of authors from the manifest of your package"), +("CARGO_PKG_NAME","The name of your package"), +("CARGO_PKG_DESCRIPTION","The description from the manifest of your package"), +("CARGO_PKG_HOMEPAGE","The home page from the manifest of your package"), +("CARGO_PKG_REPOSITORY","The repository from the manifest of your package"), +("CARGO_PKG_LICENSE","The license from the manifest of your package"), +("CARGO_PKG_LICENSE_FILE","The license file from the manifest of your package"), +("CARGO_PKG_RUST_VERSION","The Rust version from the manifest of your package. Note that this is the minimum Rust version supported by the package, not the current Rust version"), +("CARGO_CRATE_NAME","The name of the crate that is currently being compiled"), +("CARGO_BIN_NAME","The name of the binary that is currently being compiled (if it is a binary). This name does not include any file extension, such as .exe"), +("CARGO_PRIMARY_PACKAGE","This environment variable will be set if the package being built is primary. Primary packages are the ones the user selected on the command-line, either with -p flags or the defaults based on the current directory and the default workspace members. This environment variable will not be set when building dependencies. This is only set when compiling the package (not when running binaries or tests)"), +("CARGO_TARGET_TMPDIR","Only set when building integration test or benchmark code. This is a path to a directory inside the target directory where integration tests or benchmarks are free to put any data needed by the tests/benches. Cargo initially creates this directory but doesn't manage its content in any way, this is the responsibility of the test code") +]; pub(crate) fn complete_cargo_env_vars( acc: &mut Completions, From bc7080884c92148fdb47701067d1f1dbdef4ed1d Mon Sep 17 00:00:00 2001 From: btwotwo <10519967+btwotwo@users.noreply.github.com> Date: Thu, 6 Oct 2022 16:27:53 +0200 Subject: [PATCH 032/333] Add tests for env var completion --- .../src/completions/env_vars.rs | 54 +++++++++++++++---- 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/crates/ide-completion/src/completions/env_vars.rs b/crates/ide-completion/src/completions/env_vars.rs index eba39bba3d5d..43d67f093e97 100644 --- a/crates/ide-completion/src/completions/env_vars.rs +++ b/crates/ide-completion/src/completions/env_vars.rs @@ -61,21 +61,55 @@ fn is_env_macro(string: &ast::String) -> bool { #[cfg(test)] mod tests { - use expect_test::{expect, Expect}; - use crate::tests::{check_edit}; + use crate::tests::{check_edit, completion_list}; + + fn check(macro_name: &str) { + check_edit("CARGO_BIN_NAME",&format!(r#" + fn main() {{ + let foo = {}!("CAR$0"); + }} + "#, macro_name), &format!(r#" + fn main() {{ + let foo = {}!("CARGO_BIN_NAME"); + }} + "#, macro_name)); + } + #[test] + fn completes_env_variable_in_env() { + check("env") + } #[test] - fn completes_env_variables() { - check_edit("CARGO", - r#" + fn completes_env_variable_in_option_env() { + check("option_env"); + } + + #[test] + fn doesnt_complete_in_random_strings() { + let fixture = r#" fn main() { - let foo = env!("CA$0); + let foo = "CA$0"; } - "# - ,r#" + "#; + + let completions = completion_list(fixture); + assert!(completions.is_empty(), "Completions weren't empty: {}", completions); + } + + #[test] + fn doesnt_complete_in_random_macro() { + let fixture = r#" + macro_rules! bar { + ($($arg:tt)*) => { 0 } + } + fn main() { - let foo = env!("CARGO); + let foo = bar!("CA$0"); + } - "#) + "#; + + let completions = completion_list(fixture); + assert!(completions.is_empty(), "Completions weren't empty: {}", completions); } } \ No newline at end of file From 55c5014b760a5d4bf3c6dc6a19818278ec83816e Mon Sep 17 00:00:00 2001 From: btwotwo <10519967+btwotwo@users.noreply.github.com> Date: Thu, 6 Oct 2022 16:28:33 +0200 Subject: [PATCH 033/333] Replace if with option, add detail for each env variable completion --- .../src/completions/env_vars.rs | 47 ++++++++----------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/crates/ide-completion/src/completions/env_vars.rs b/crates/ide-completion/src/completions/env_vars.rs index 43d67f093e97..e2e0bc142c0a 100644 --- a/crates/ide-completion/src/completions/env_vars.rs +++ b/crates/ide-completion/src/completions/env_vars.rs @@ -1,8 +1,8 @@ //! Completes environment variables defined by Cargo (https://doc.rust-lang.org/cargo/reference/environment-variables.html) +use ide_db::syntax_helpers::node_ext::get_outer_macro_name; +use syntax::ast::{self, IsString}; -use syntax::{ast, AstToken, AstNode, TextRange, TextSize}; - -use crate::{context::CompletionContext, CompletionItem, CompletionItemKind}; +use crate::{CompletionItem, CompletionItemKind}; use super::Completions; const CARGO_DEFINED_VARS: &[(&str, &str)] = &[ @@ -29,34 +29,27 @@ const CARGO_DEFINED_VARS: &[(&str, &str)] = &[ pub(crate) fn complete_cargo_env_vars( acc: &mut Completions, - ctx: &CompletionContext<'_>, - original: &ast::String -) { - if !is_env_macro(original) { - return; - } + expanded: &ast::String, +) -> Option<()> { + guard_env_macro(expanded)?; + let range = expanded.text_range_between_quotes()?; - let start = ctx.original_token.text_range().start() + TextSize::from(1); - let cursor = ctx.position.offset; + CARGO_DEFINED_VARS.iter().for_each(|(var, detail)| { + let mut item = CompletionItem::new(CompletionItemKind::Keyword, range, *var); + item.detail(*detail); + item.add_to(acc); + }); - CompletionItem::new(CompletionItemKind::Binding, TextRange::new(start, cursor), "CARGO").add_to(acc); + Some(()) } -fn is_env_macro(string: &ast::String) -> bool { - //todo: replace copypaste from format_string with separate function - (|| { - let macro_call = string.syntax().parent_ancestors().find_map(ast::MacroCall::cast)?; - let name = macro_call.path()?.segment()?.name_ref()?; +fn guard_env_macro(string: &ast::String) -> Option<()> { + let name = get_outer_macro_name(string)?; + if !matches!(name.text().as_str(), "env" | "option_env") { + return None; + } - if !matches!(name.text().as_str(), - "env" | "option_env") { - return None; - } - - - Some(()) - })() - .is_some() + Some(()) } #[cfg(test)] @@ -112,4 +105,4 @@ mod tests { let completions = completion_list(fixture); assert!(completions.is_empty(), "Completions weren't empty: {}", completions); } -} \ No newline at end of file +} From 386f46a5a1bff654fc8b29ea84dcd18c29c75c18 Mon Sep 17 00:00:00 2001 From: btwotwo <10519967+btwotwo@users.noreply.github.com> Date: Thu, 6 Oct 2022 16:29:23 +0200 Subject: [PATCH 034/333] Use helper method in is_format_string --- crates/ide-db/src/syntax_helpers/format_string.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/ide-db/src/syntax_helpers/format_string.rs b/crates/ide-db/src/syntax_helpers/format_string.rs index f48a57008665..894a4b0fbb34 100644 --- a/crates/ide-db/src/syntax_helpers/format_string.rs +++ b/crates/ide-db/src/syntax_helpers/format_string.rs @@ -1,9 +1,10 @@ //! Tools to work with format string literals for the `format_args!` family of macros. use syntax::{ - ast::{self, IsString}, - AstNode, AstToken, TextRange, TextSize, + ast::{self, IsString}, TextRange, TextSize, }; +use super::node_ext::get_outer_macro_name; + pub fn is_format_string(string: &ast::String) -> bool { // Check if `string` is a format string argument of a macro invocation. // `string` is a string literal, mapped down into the innermost macro expansion. @@ -14,8 +15,7 @@ pub fn is_format_string(string: &ast::String) -> bool { // This setup lets us correctly highlight the components of `concat!("{}", "bla")` format // strings. It still fails for `concat!("{", "}")`, but that is rare. (|| { - let macro_call = string.syntax().parent_ancestors().find_map(ast::MacroCall::cast)?; - let name = macro_call.path()?.segment()?.name_ref()?; + let name = get_outer_macro_name(string)?; if !matches!( name.text().as_str(), From c660a5ce3444a8c84eeb3afe8ea7cdf12b025e25 Mon Sep 17 00:00:00 2001 From: btwotwo <10519967+btwotwo@users.noreply.github.com> Date: Thu, 6 Oct 2022 16:42:31 +0200 Subject: [PATCH 035/333] Remove unnecessary dereference --- crates/ide-completion/src/completions/env_vars.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ide-completion/src/completions/env_vars.rs b/crates/ide-completion/src/completions/env_vars.rs index e2e0bc142c0a..3c573a5f984d 100644 --- a/crates/ide-completion/src/completions/env_vars.rs +++ b/crates/ide-completion/src/completions/env_vars.rs @@ -35,7 +35,7 @@ pub(crate) fn complete_cargo_env_vars( let range = expanded.text_range_between_quotes()?; CARGO_DEFINED_VARS.iter().for_each(|(var, detail)| { - let mut item = CompletionItem::new(CompletionItemKind::Keyword, range, *var); + let mut item = CompletionItem::new(CompletionItemKind::Keyword, range, var); item.detail(*detail); item.add_to(acc); }); From 8a92910f9765689a6422de42a208724f8d3935e8 Mon Sep 17 00:00:00 2001 From: btwotwo <10519967+btwotwo@users.noreply.github.com> Date: Thu, 6 Oct 2022 16:53:17 +0200 Subject: [PATCH 036/333] Formatting --- .../src/completions/env_vars.rs | 21 ++++++++++++------- .../src/syntax_helpers/format_string.rs | 3 ++- crates/ide-db/src/syntax_helpers/node_ext.rs | 6 +++--- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/crates/ide-completion/src/completions/env_vars.rs b/crates/ide-completion/src/completions/env_vars.rs index 3c573a5f984d..809f6d4b7c1e 100644 --- a/crates/ide-completion/src/completions/env_vars.rs +++ b/crates/ide-completion/src/completions/env_vars.rs @@ -27,10 +27,7 @@ const CARGO_DEFINED_VARS: &[(&str, &str)] = &[ ("CARGO_TARGET_TMPDIR","Only set when building integration test or benchmark code. This is a path to a directory inside the target directory where integration tests or benchmarks are free to put any data needed by the tests/benches. Cargo initially creates this directory but doesn't manage its content in any way, this is the responsibility of the test code") ]; -pub(crate) fn complete_cargo_env_vars( - acc: &mut Completions, - expanded: &ast::String, -) -> Option<()> { +pub(crate) fn complete_cargo_env_vars(acc: &mut Completions, expanded: &ast::String) -> Option<()> { guard_env_macro(expanded)?; let range = expanded.text_range_between_quotes()?; @@ -57,15 +54,25 @@ mod tests { use crate::tests::{check_edit, completion_list}; fn check(macro_name: &str) { - check_edit("CARGO_BIN_NAME",&format!(r#" + check_edit( + "CARGO_BIN_NAME", + &format!( + r#" fn main() {{ let foo = {}!("CAR$0"); }} - "#, macro_name), &format!(r#" + "#, + macro_name + ), + &format!( + r#" fn main() {{ let foo = {}!("CARGO_BIN_NAME"); }} - "#, macro_name)); + "#, + macro_name + ), + ); } #[test] fn completes_env_variable_in_env() { diff --git a/crates/ide-db/src/syntax_helpers/format_string.rs b/crates/ide-db/src/syntax_helpers/format_string.rs index 894a4b0fbb34..8836167630a4 100644 --- a/crates/ide-db/src/syntax_helpers/format_string.rs +++ b/crates/ide-db/src/syntax_helpers/format_string.rs @@ -1,6 +1,7 @@ //! Tools to work with format string literals for the `format_args!` family of macros. use syntax::{ - ast::{self, IsString}, TextRange, TextSize, + ast::{self, IsString}, + TextRange, TextSize, }; use super::node_ext::get_outer_macro_name; diff --git a/crates/ide-db/src/syntax_helpers/node_ext.rs b/crates/ide-db/src/syntax_helpers/node_ext.rs index 78d6bd008afe..5265b3d16156 100644 --- a/crates/ide-db/src/syntax_helpers/node_ext.rs +++ b/crates/ide-db/src/syntax_helpers/node_ext.rs @@ -2,8 +2,8 @@ use itertools::Itertools; use parser::T; use syntax::{ - ast::{self, HasLoopBody, PathSegmentKind, VisibilityKind, NameRef}, - AstNode, Preorder, RustLanguage, WalkEvent, AstToken, + ast::{self, HasLoopBody, NameRef, PathSegmentKind, VisibilityKind}, + AstNode, AstToken, Preorder, RustLanguage, WalkEvent, }; pub fn expr_as_name_ref(expr: &ast::Expr) -> Option { @@ -463,4 +463,4 @@ pub fn get_outer_macro_name(string: &ast::String) -> Option { let name = macro_call.path()?.segment()?.name_ref()?; Some(name) -} \ No newline at end of file +} From f458ea15d637db94d1e95c595aab6c1d7f3937e0 Mon Sep 17 00:00:00 2001 From: btwotwo <10519967+btwotwo@users.noreply.github.com> Date: Thu, 6 Oct 2022 21:34:19 +0200 Subject: [PATCH 037/333] Make helper method less specific --- crates/ide-db/src/syntax_helpers/format_string.rs | 4 ++-- crates/ide-db/src/syntax_helpers/node_ext.rs | 8 +++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/crates/ide-db/src/syntax_helpers/format_string.rs b/crates/ide-db/src/syntax_helpers/format_string.rs index 8836167630a4..b3e227ddd27d 100644 --- a/crates/ide-db/src/syntax_helpers/format_string.rs +++ b/crates/ide-db/src/syntax_helpers/format_string.rs @@ -4,7 +4,7 @@ use syntax::{ TextRange, TextSize, }; -use super::node_ext::get_outer_macro_name; +use super::node_ext::get_outer_macro; pub fn is_format_string(string: &ast::String) -> bool { // Check if `string` is a format string argument of a macro invocation. @@ -16,7 +16,7 @@ pub fn is_format_string(string: &ast::String) -> bool { // This setup lets us correctly highlight the components of `concat!("{}", "bla")` format // strings. It still fails for `concat!("{", "}")`, but that is rare. (|| { - let name = get_outer_macro_name(string)?; + let name = get_outer_macro(string)?.path()?.segment()?.name_ref()?; if !matches!( name.text().as_str(), diff --git a/crates/ide-db/src/syntax_helpers/node_ext.rs b/crates/ide-db/src/syntax_helpers/node_ext.rs index 5265b3d16156..9cfcdfb77b97 100644 --- a/crates/ide-db/src/syntax_helpers/node_ext.rs +++ b/crates/ide-db/src/syntax_helpers/node_ext.rs @@ -2,7 +2,7 @@ use itertools::Itertools; use parser::T; use syntax::{ - ast::{self, HasLoopBody, NameRef, PathSegmentKind, VisibilityKind}, + ast::{self, HasLoopBody, MacroCall, PathSegmentKind, VisibilityKind}, AstNode, AstToken, Preorder, RustLanguage, WalkEvent, }; @@ -458,9 +458,7 @@ pub fn parse_tt_as_comma_sep_paths(input: ast::TokenTree) -> Option Option { +pub fn get_outer_macro(string: &ast::String) -> Option { let macro_call = string.syntax().parent_ancestors().find_map(ast::MacroCall::cast)?; - let name = macro_call.path()?.segment()?.name_ref()?; - - Some(name) + Some(macro_call) } From e2d3c1506f10861f9c864e3611fa41387c11546b Mon Sep 17 00:00:00 2001 From: btwotwo <10519967+btwotwo@users.noreply.github.com> Date: Thu, 6 Oct 2022 21:35:01 +0200 Subject: [PATCH 038/333] Restrict auto-completion for only built-in macros --- .../src/completions/env_vars.rs | 61 +++++++++++++++---- 1 file changed, 49 insertions(+), 12 deletions(-) diff --git a/crates/ide-completion/src/completions/env_vars.rs b/crates/ide-completion/src/completions/env_vars.rs index 809f6d4b7c1e..5483fdf31a24 100644 --- a/crates/ide-completion/src/completions/env_vars.rs +++ b/crates/ide-completion/src/completions/env_vars.rs @@ -1,8 +1,9 @@ //! Completes environment variables defined by Cargo (https://doc.rust-lang.org/cargo/reference/environment-variables.html) -use ide_db::syntax_helpers::node_ext::get_outer_macro_name; +use hir::Semantics; +use ide_db::{syntax_helpers::node_ext::get_outer_macro, RootDatabase}; use syntax::ast::{self, IsString}; -use crate::{CompletionItem, CompletionItemKind}; +use crate::{context::CompletionContext, CompletionItem, CompletionItemKind}; use super::Completions; const CARGO_DEFINED_VARS: &[(&str, &str)] = &[ @@ -27,8 +28,12 @@ const CARGO_DEFINED_VARS: &[(&str, &str)] = &[ ("CARGO_TARGET_TMPDIR","Only set when building integration test or benchmark code. This is a path to a directory inside the target directory where integration tests or benchmarks are free to put any data needed by the tests/benches. Cargo initially creates this directory but doesn't manage its content in any way, this is the responsibility of the test code") ]; -pub(crate) fn complete_cargo_env_vars(acc: &mut Completions, expanded: &ast::String) -> Option<()> { - guard_env_macro(expanded)?; +pub(crate) fn complete_cargo_env_vars( + acc: &mut Completions, + ctx: &CompletionContext<'_>, + expanded: &ast::String, +) -> Option<()> { + guard_env_macro(expanded, &ctx.sema, &ctx.db)?; let range = expanded.text_range_between_quotes()?; CARGO_DEFINED_VARS.iter().for_each(|(var, detail)| { @@ -40,13 +45,19 @@ pub(crate) fn complete_cargo_env_vars(acc: &mut Completions, expanded: &ast::Str Some(()) } -fn guard_env_macro(string: &ast::String) -> Option<()> { - let name = get_outer_macro_name(string)?; - if !matches!(name.text().as_str(), "env" | "option_env") { - return None; - } +fn guard_env_macro( + string: &ast::String, + semantics: &Semantics<'_, RootDatabase>, + db: &RootDatabase, +) -> Option<()> { + let call = get_outer_macro(string)?; + let name = call.path()?.segment()?.name_ref()?; + let makro = semantics.resolve_macro_call(&call)?; - Some(()) + match name.text().as_str() { + "env" | "option_env" if makro.kind(db) == hir::MacroKind::BuiltIn => Some(()), + _ => None, + } } #[cfg(test)] @@ -58,19 +69,29 @@ mod tests { "CARGO_BIN_NAME", &format!( r#" + #[rustc_builtin_macro] + macro_rules! {} {{ + ($var:literal) => {{ 0 }} + }} + fn main() {{ let foo = {}!("CAR$0"); }} "#, - macro_name + macro_name, macro_name ), &format!( r#" + #[rustc_builtin_macro] + macro_rules! {} {{ + ($var:literal) => {{ 0 }} + }} + fn main() {{ let foo = {}!("CARGO_BIN_NAME"); }} "#, - macro_name + macro_name, macro_name ), ); } @@ -112,4 +133,20 @@ mod tests { let completions = completion_list(fixture); assert!(completions.is_empty(), "Completions weren't empty: {}", completions); } + + #[test] + fn doesnt_complete_for_shadowed_macro() { + let fixture = r#" + macro_rules! env { + ($var:literal) => { 0 } + } + + fn main() { + let foo = env!("CA$0"); + } + "#; + + let completions = completion_list(fixture); + assert!(completions.is_empty(), "Completions weren't empty: {}", completions) + } } From 3732d159b2e7dec436f8bb91c53832e56b1f2720 Mon Sep 17 00:00:00 2001 From: btwotwo <10519967+btwotwo@users.noreply.github.com> Date: Thu, 6 Oct 2022 21:35:12 +0200 Subject: [PATCH 039/333] Pass context to env vars completion --- crates/ide-completion/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs index c5718ec40f58..9d0044e55f59 100644 --- a/crates/ide-completion/src/lib.rs +++ b/crates/ide-completion/src/lib.rs @@ -183,7 +183,7 @@ pub fn completions( CompletionAnalysis::String { original, expanded: Some(expanded) } => { completions::extern_abi::complete_extern_abi(acc, ctx, expanded); completions::format_string::format_string(acc, ctx, original, expanded); - completions::env_vars::complete_cargo_env_vars(acc, expanded); + completions::env_vars::complete_cargo_env_vars(acc, ctx, expanded); } CompletionAnalysis::UnexpandedAttrTT { colon_prefix, From 99182dd8058f0a1153b8b7fcf873028caa6fbfa7 Mon Sep 17 00:00:00 2001 From: joboet Date: Thu, 6 Oct 2022 22:46:15 +0200 Subject: [PATCH 040/333] std: use semaphore for thread parking on Apple platforms --- .../std/src/sys/unix/thread_parker/darwin.rs | 131 ++++++++++++++++++ library/std/src/sys/unix/thread_parker/mod.rs | 10 +- 2 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 library/std/src/sys/unix/thread_parker/darwin.rs diff --git a/library/std/src/sys/unix/thread_parker/darwin.rs b/library/std/src/sys/unix/thread_parker/darwin.rs new file mode 100644 index 000000000000..510839d5dafb --- /dev/null +++ b/library/std/src/sys/unix/thread_parker/darwin.rs @@ -0,0 +1,131 @@ +//! Thread parking for Darwin-based systems. +//! +//! Darwin actually has futex syscalls (`__ulock_wait`/`__ulock_wake`), but they +//! cannot be used in `std` because they are non-public (their use will lead to +//! rejection from the App Store) and because they are only available starting +//! with macOS version 10.12, even though the minimum target version is 10.7. +//! +//! Therefore, we need to look for other synchronization primitives. Luckily, Darwin +//! supports semaphores, which allow us to implement the behaviour we need with +//! only one primitive (as opposed to a mutex-condvar pair). We use the semaphore +//! provided by libdispatch, as the underlying Mach semaphore is only dubiously +//! public. + +use crate::pin::Pin; +use crate::sync::atomic::{ + AtomicI8, + Ordering::{Acquire, Release}, +}; +use crate::time::Duration; + +type dispatch_semaphore_t = *mut crate::ffi::c_void; +type dispatch_time_t = u64; + +const DISPATCH_TIME_NOW: dispatch_time_t = 0; +const DISPATCH_TIME_FOREVER: dispatch_time_t = !0; + +#[link(name = "System", kind = "dylib")] +extern "C" { + fn dispatch_time(when: dispatch_time_t, delta: i64) -> dispatch_time_t; + fn dispatch_semaphore_create(val: isize) -> dispatch_semaphore_t; + fn dispatch_semaphore_wait(dsema: dispatch_semaphore_t, timeout: dispatch_time_t) -> isize; + fn dispatch_semaphore_signal(dsema: dispatch_semaphore_t) -> isize; + fn dispatch_release(object: *mut crate::ffi::c_void); +} + +const EMPTY: i8 = 0; +const NOTIFIED: i8 = 1; +const PARKED: i8 = -1; + +pub struct Parker { + semaphore: dispatch_semaphore_t, + state: AtomicI8, +} + +unsafe impl Sync for Parker {} +unsafe impl Send for Parker {} + +impl Parker { + pub unsafe fn new(parker: *mut Parker) { + let semaphore = dispatch_semaphore_create(0); + assert!( + !semaphore.is_null(), + "failed to create dispatch semaphore for thread synchronization" + ); + parker.write(Parker { semaphore, state: AtomicI8::new(EMPTY) }) + } + + // Does not need `Pin`, but other implementation do. + pub unsafe fn park(self: Pin<&Self>) { + // The semaphore counter must be zero at this point, because unparking + // threads will not actually increase it until we signalled that we + // are waiting. + + // Change NOTIFIED to EMPTY and EMPTY to PARKED. + if self.state.fetch_sub(1, Acquire) == NOTIFIED { + return; + } + + // Another thread may increase the semaphore counter from this point on. + // If it is faster than us, we will decrement it again immediately below. + // If we are faster, we wait. + + // Ensure that the semaphore counter has actually been decremented, even + // if the call timed out for some reason. + while dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER) != 0 {} + + // At this point, the semaphore counter is zero again. + + // We were definitely woken up, so we don't need to check the state. + // Still, we need to reset the state using a swap to observe the state + // change with acquire ordering. + self.state.swap(EMPTY, Acquire); + } + + // Does not need `Pin`, but other implementation do. + pub unsafe fn park_timeout(self: Pin<&Self>, dur: Duration) { + if self.state.fetch_sub(1, Acquire) == NOTIFIED { + return; + } + + let nanos = dur.as_nanos().try_into().unwrap_or(i64::MAX); + let timeout = dispatch_time(DISPATCH_TIME_NOW, nanos); + + let timeout = dispatch_semaphore_wait(self.semaphore, timeout) != 0; + + let state = self.state.swap(EMPTY, Acquire); + if state == NOTIFIED && timeout { + // If the state was NOTIFIED but semaphore_wait returned without + // decrementing the count because of a timeout, it means another + // thread is about to call semaphore_signal. We must wait for that + // to happen to ensure the semaphore count is reset. + while dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER) != 0 {} + } else { + // Either a timeout occurred and we reset the state before any thread + // tried to wake us up, or we were woken up and reset the state, + // making sure to observe the state change with acquire ordering. + // Either way, the semaphore counter is now zero again. + } + } + + // Does not need `Pin`, but other implementation do. + pub fn unpark(self: Pin<&Self>) { + let state = self.state.swap(NOTIFIED, Release); + if state == PARKED { + unsafe { + dispatch_semaphore_signal(self.semaphore); + } + } + } +} + +impl Drop for Parker { + fn drop(&mut self) { + // SAFETY: + // We always ensure that the semaphore count is reset, so this will + // never cause an exception. + unsafe { + dispatch_release(self.semaphore); + } + } +} diff --git a/library/std/src/sys/unix/thread_parker/mod.rs b/library/std/src/sys/unix/thread_parker/mod.rs index e2453580dc72..724ec2d482ed 100644 --- a/library/std/src/sys/unix/thread_parker/mod.rs +++ b/library/std/src/sys/unix/thread_parker/mod.rs @@ -11,7 +11,15 @@ )))] cfg_if::cfg_if! { - if #[cfg(target_os = "netbsd")] { + if #[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "watchos", + target_os = "tvos", + ))] { + mod darwin; + pub use darwin::Parker; + } else if #[cfg(target_os = "netbsd")] { mod netbsd; pub use netbsd::Parker; } else { From 0ad4dd494a67a0fffc5a3e4df08f0c26cf074c59 Mon Sep 17 00:00:00 2001 From: joboet Date: Thu, 6 Oct 2022 22:46:47 +0200 Subject: [PATCH 041/333] std: add thread parking tests --- library/std/src/thread/tests.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/library/std/src/thread/tests.rs b/library/std/src/thread/tests.rs index 130e47c8d44f..dfb8765ab4ee 100644 --- a/library/std/src/thread/tests.rs +++ b/library/std/src/thread/tests.rs @@ -244,6 +244,28 @@ fn test_try_panic_any_message_unit_struct() { } } +#[test] +fn test_park_unpark_before() { + for _ in 0..10 { + thread::current().unpark(); + thread::park(); + } +} + +#[test] +fn test_park_unpark_called_other_thread() { + for _ in 0..10 { + let th = thread::current(); + + let _guard = thread::spawn(move || { + super::sleep(Duration::from_millis(50)); + th.unpark(); + }); + + thread::park(); + } +} + #[test] fn test_park_timeout_unpark_before() { for _ in 0..10 { From 57d0278d5cd397a89bb33f1857b3ed431c1f341c Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Fri, 7 Oct 2022 11:37:33 +0900 Subject: [PATCH 042/333] suggest `==` to the first expr which has `ExprKind::Assign` --- compiler/rustc_hir_analysis/src/check/expr.rs | 12 ++++++++++++ .../ui/type/type-check/assignment-in-if.rs | 6 ++++++ .../type/type-check/assignment-in-if.stderr | 19 ++++++++++++++++++- 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_hir_analysis/src/check/expr.rs b/compiler/rustc_hir_analysis/src/check/expr.rs index 5effa102e87c..489c9067e97f 100644 --- a/compiler/rustc_hir_analysis/src/check/expr.rs +++ b/compiler/rustc_hir_analysis/src/check/expr.rs @@ -1051,8 +1051,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rhs_expr, ) = lhs.kind { + // if x == 1 && y == 2 { .. } + // + let actual_lhs_ty = self.check_expr(&rhs_expr); (Applicability::MaybeIncorrect, self.can_coerce(rhs_ty, actual_lhs_ty)) + } else if let ExprKind::Binary( + Spanned { node: hir::BinOpKind::And | hir::BinOpKind::Or, .. }, + lhs_expr, + _, + ) = rhs.kind + { + // if x == 1 && y == 2 { .. } + // + + let actual_rhs_ty = self.check_expr(&lhs_expr); + (Applicability::MaybeIncorrect, self.can_coerce(actual_rhs_ty, lhs_ty)) } else { (Applicability::MaybeIncorrect, false) }; diff --git a/src/test/ui/type/type-check/assignment-in-if.rs b/src/test/ui/type/type-check/assignment-in-if.rs index 3a7845096fd2..ada250df2469 100644 --- a/src/test/ui/type/type-check/assignment-in-if.rs +++ b/src/test/ui/type/type-check/assignment-in-if.rs @@ -53,4 +53,10 @@ fn main() { //~| ERROR mismatched types println!("{}", x); } + + if x = 1 && x == 1 { + //~^ ERROR mismatched types + //~| ERROR mismatched types + println!("{}", x); + } } diff --git a/src/test/ui/type/type-check/assignment-in-if.stderr b/src/test/ui/type/type-check/assignment-in-if.stderr index 166f22937776..8ab08e25e309 100644 --- a/src/test/ui/type/type-check/assignment-in-if.stderr +++ b/src/test/ui/type/type-check/assignment-in-if.stderr @@ -104,6 +104,23 @@ help: you might have meant to compare for equality LL | if x == x && x == x && x == x { | + -error: aborting due to 11 previous errors +error[E0308]: mismatched types + --> $DIR/assignment-in-if.rs:57:12 + | +LL | if x = 1 && x == 1 { + | ^ expected `bool`, found integer + +error[E0308]: mismatched types + --> $DIR/assignment-in-if.rs:57:8 + | +LL | if x = 1 && x == 1 { + | ^^^^^^^^^^^^^^^ expected `bool`, found `()` + | +help: you might have meant to compare for equality + | +LL | if x == 1 && x == 1 { + | + + +error: aborting due to 13 previous errors For more information about this error, try `rustc --explain E0308`. From 4cf30c0022a434718799b66cdf8911ad8030d0e1 Mon Sep 17 00:00:00 2001 From: StackOverflowExcept1on <109800286+StackOverflowExcept1on@users.noreply.github.com> Date: Fri, 7 Oct 2022 15:42:05 +0300 Subject: [PATCH 043/333] Improved documentation for `std::io::Error` --- library/std/src/io/error.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index feb3fb989a7a..2bbfbe38a24a 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -481,6 +481,7 @@ impl Error { /// originate from the OS itself. The `error` argument is an arbitrary /// payload which will be contained in this [`Error`]. /// + /// Note that this function allocates memory on the heap. /// If no extra payload is required, use the `From` conversion from /// `ErrorKind`. /// @@ -495,7 +496,7 @@ impl Error { /// // errors can also be created from other errors /// let custom_error2 = Error::new(ErrorKind::Interrupted, custom_error); /// - /// // creating an error without payload + /// // creating an error without payload (also without memory allocation) /// let eof_error = Error::from(ErrorKind::UnexpectedEof); /// ``` #[stable(feature = "rust1", since = "1.0.0")] From 5d15beb5919e4ed481c44a1ac19c2ad04a36ee8a Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 20 Sep 2022 08:56:04 +0000 Subject: [PATCH 044/333] Unconditionally encode hidden types in typeck results --- .../rustc_hir_analysis/src/check/writeback.rs | 44 +++++++++---------- .../rustc_hir_analysis/src/collect/type_of.rs | 23 ++++------ compiler/rustc_middle/src/ty/context.rs | 8 ++-- .../ui/impl-trait/issues/issue-86800.stderr | 15 +++++-- 4 files changed, 43 insertions(+), 47 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/writeback.rs b/compiler/rustc_hir_analysis/src/check/writeback.rs index 3583769b7cd6..f789978ae7e4 100644 --- a/compiler/rustc_hir_analysis/src/check/writeback.rs +++ b/compiler/rustc_hir_analysis/src/check/writeback.rs @@ -536,33 +536,29 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { let opaque_types = self.fcx.infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); for (opaque_type_key, decl) in opaque_types { - let hidden_type = match decl.origin { - hir::OpaqueTyOrigin::FnReturn(_) | hir::OpaqueTyOrigin::AsyncFn(_) => { - let ty = self.resolve(decl.hidden_type.ty, &decl.hidden_type.span); - struct RecursionChecker { - def_id: LocalDefId, - } - impl<'tcx> ty::TypeVisitor<'tcx> for RecursionChecker { - type BreakTy = (); - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { - if let ty::Opaque(def_id, _) = *t.kind() { - if def_id == self.def_id.to_def_id() { - return ControlFlow::Break(()); - } - } - t.super_visit_with(self) + let hidden_type = self.resolve(decl.hidden_type.ty, &decl.hidden_type.span); + + struct RecursionChecker { + def_id: LocalDefId, + } + impl<'tcx> ty::TypeVisitor<'tcx> for RecursionChecker { + type BreakTy = (); + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { + if let ty::Opaque(def_id, _) = *t.kind() { + if def_id == self.def_id.to_def_id() { + return ControlFlow::Break(()); } } - if ty - .visit_with(&mut RecursionChecker { def_id: opaque_type_key.def_id }) - .is_break() - { - return; - } - Some(ty) + t.super_visit_with(self) } - hir::OpaqueTyOrigin::TyAlias => None, - }; + } + if hidden_type + .visit_with(&mut RecursionChecker { def_id: opaque_type_key.def_id }) + .is_break() + { + continue; + } + self.typeck_results.concrete_opaque_types.insert(opaque_type_key.def_id, hidden_type); } } diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index f8a62c849107..ad9b124c8ab6 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -788,20 +788,15 @@ fn find_opaque_ty_constraints_for_rpit( // the `concrete_opaque_types` table. tcx.ty_error() } else { - table - .concrete_opaque_types - .get(&def_id) - .copied() - .unwrap_or_else(|| { - // We failed to resolve the opaque type or it - // resolves to itself. We interpret this as the - // no values of the hidden type ever being constructed, - // so we can just make the hidden type be `!`. - // For backwards compatibility reasons, we fall back to - // `()` until we the diverging default is changed. - Some(tcx.mk_diverging_default()) - }) - .expect("RPIT always have a hidden type from typeck") + table.concrete_opaque_types.get(&def_id).copied().unwrap_or_else(|| { + // We failed to resolve the opaque type or it + // resolves to itself. We interpret this as the + // no values of the hidden type ever being constructed, + // so we can just make the hidden type be `!`. + // For backwards compatibility reasons, we fall back to + // `()` until we the diverging default is changed. + tcx.mk_diverging_default() + }) } }) } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 97646003e733..8277d1413587 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -539,12 +539,10 @@ pub struct TypeckResults<'tcx> { pub tainted_by_errors: Option, /// All the opaque types that have hidden types set - /// by this function. For return-position-impl-trait we also store the - /// type here, so that mir-borrowck can figure out hidden types, + /// by this function. We also store the + /// type here, so that mir-borrowck can use it as a hint for figuring out hidden types, /// even if they are only set in dead code (which doesn't show up in MIR). - /// For type-alias-impl-trait, this map is only used to prevent query cycles, - /// so the hidden types are all `None`. - pub concrete_opaque_types: VecMap>>, + pub concrete_opaque_types: VecMap>, /// Tracks the minimum captures required for a closure; /// see `MinCaptureInformationMap` for more details. diff --git a/src/test/ui/impl-trait/issues/issue-86800.stderr b/src/test/ui/impl-trait/issues/issue-86800.stderr index 135d06d44ada..6c4aa35679d5 100644 --- a/src/test/ui/impl-trait/issues/issue-86800.stderr +++ b/src/test/ui/impl-trait/issues/issue-86800.stderr @@ -1,3 +1,11 @@ +error: unconstrained opaque type + --> $DIR/issue-86800.rs:33:34 + | +LL | type TransactionFuture<'__, O> = impl '__ + Future>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = + stack backtrace: @@ -12,8 +20,7 @@ error: internal compiler error: unexpected panic query stack during panic: -#0 [mir_borrowck] borrow-checking `execute_transaction_fut` -#1 [type_of] computing type of `TransactionFuture::{opaque#0}` -#2 [check_mod_item_types] checking item types in top-level module -#3 [analysis] running analysis passes on this crate +#0 [type_of] computing type of `TransactionFuture::{opaque#0}` +#1 [check_mod_item_types] checking item types in top-level module +#2 [analysis] running analysis passes on this crate end of query stack From d643ae1bbf4da44c00c2aff87cb3081a93ec44bb Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 27 Sep 2022 11:14:14 +0000 Subject: [PATCH 045/333] Remove some dead code --- .../src/region_infer/opaque_types.rs | 26 +------------------ 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index a7c4671665f4..b2914d234496 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -2,7 +2,6 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::vec_map::VecMap; use rustc_hir::def_id::LocalDefId; use rustc_hir::OpaqueTyOrigin; -use rustc_infer::infer::error_reporting::unexpected_hidden_region_diagnostic; use rustc_infer::infer::TyCtxtInferExt as _; use rustc_infer::infer::{DefiningAnchor, InferCtxt}; use rustc_infer::traits::{Obligation, ObligationCause, TraitEngine}; @@ -248,9 +247,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { // after producing an error for each of them. let definition_ty = instantiated_ty.ty.fold_with(&mut ReverseMapper::new( self.tcx, - opaque_type_key, map, - instantiated_ty.ty, instantiated_ty.span, )); debug!(?definition_ty); @@ -427,14 +424,9 @@ fn check_opaque_type_parameter_valid( struct ReverseMapper<'tcx> { tcx: TyCtxt<'tcx>, - - key: ty::OpaqueTypeKey<'tcx>, map: FxHashMap, GenericArg<'tcx>>, do_not_error: bool, - /// initially `Some`, set to `None` once error has been reported - hidden_ty: Option>, - /// Span of function being checked. span: Span, } @@ -442,12 +434,10 @@ struct ReverseMapper<'tcx> { impl<'tcx> ReverseMapper<'tcx> { fn new( tcx: TyCtxt<'tcx>, - key: ty::OpaqueTypeKey<'tcx>, map: FxHashMap, GenericArg<'tcx>>, - hidden_ty: Ty<'tcx>, span: Span, ) -> Self { - Self { tcx, key, map, do_not_error: false, hidden_ty: Some(hidden_ty), span } + Self { tcx, map, do_not_error: false, span } } fn fold_kind_no_missing_regions_error(&mut self, kind: GenericArg<'tcx>) -> GenericArg<'tcx> { @@ -493,24 +483,10 @@ impl<'tcx> TypeFolder<'tcx> for ReverseMapper<'tcx> { } } - let generics = self.tcx().generics_of(self.key.def_id); match self.map.get(&r.into()).map(|k| k.unpack()) { Some(GenericArgKind::Lifetime(r1)) => r1, Some(u) => panic!("region mapped to unexpected kind: {:?}", u), None if self.do_not_error => self.tcx.lifetimes.re_static, - None if generics.parent.is_some() => { - if let Some(hidden_ty) = self.hidden_ty.take() { - unexpected_hidden_region_diagnostic( - self.tcx, - self.tcx.def_span(self.key.def_id), - hidden_ty, - r, - self.key, - ) - .emit(); - } - self.tcx.lifetimes.re_static - } None => { self.tcx .sess From 9eb69e82e0e4bcbedb4f21f392210204ad8588ab Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 27 Sep 2022 11:37:10 +0000 Subject: [PATCH 046/333] Move ReverseMapper logic onto OpaqueHiddenType --- .../src/region_infer/opaque_types.rs | 231 +----------------- .../rustc_borrowck/src/session_diagnostics.rs | 9 - compiler/rustc_middle/src/ty/diagnostics.rs | 8 + compiler/rustc_middle/src/ty/mod.rs | 27 ++ compiler/rustc_middle/src/ty/opaque_types.rs | 205 ++++++++++++++++ 5 files changed, 247 insertions(+), 233 deletions(-) create mode 100644 compiler/rustc_middle/src/ty/opaque_types.rs diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index b2914d234496..aa751927ba71 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -5,8 +5,7 @@ use rustc_hir::OpaqueTyOrigin; use rustc_infer::infer::TyCtxtInferExt as _; use rustc_infer::infer::{DefiningAnchor, InferCtxt}; use rustc_infer::traits::{Obligation, ObligationCause, TraitEngine}; -use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable}; -use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts}; +use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts}; use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{ self, OpaqueHiddenType, OpaqueTypeKey, ToPredicate, Ty, TyCtxt, TypeFoldable, @@ -15,8 +14,6 @@ use rustc_span::Span; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; use rustc_trait_selection::traits::TraitEngineExt as _; -use crate::session_diagnostics::ConstNotUsedTraitAlias; - use super::RegionInferenceContext; impl<'tcx> RegionInferenceContext<'tcx> { @@ -228,29 +225,9 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { return self.tcx.ty_error(); } - let OpaqueTypeKey { def_id, substs } = opaque_type_key; - - // Use substs to build up a reverse map from regions to their - // identity mappings. This is necessary because of `impl - // Trait` lifetimes are computed by replacing existing - // lifetimes with 'static and remapping only those used in the - // `impl Trait` return type, resulting in the parameters - // shifting. - let id_substs = InternalSubsts::identity_for_item(self.tcx, def_id.to_def_id()); - debug!(?id_substs); - let map: FxHashMap, GenericArg<'tcx>> = - substs.iter().enumerate().map(|(index, subst)| (subst, id_substs[index])).collect(); - debug!("map = {:#?}", map); - - // Convert the type from the function into a type valid outside - // the function, by replacing invalid regions with 'static, - // after producing an error for each of them. - let definition_ty = instantiated_ty.ty.fold_with(&mut ReverseMapper::new( - self.tcx, - map, - instantiated_ty.span, - )); - debug!(?definition_ty); + let definition_ty = instantiated_ty + .remap_generic_params_to_declaration_params(opaque_type_key, self.tcx) + .ty; if !check_opaque_type_parameter_valid( self.tcx, @@ -266,6 +243,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { let OpaqueTyOrigin::TyAlias = origin else { return definition_ty; }; + let def_id = opaque_type_key.def_id; // This logic duplicates most of `check_opaque_meets_bounds`. // FIXME(oli-obk): Also do region checks here and then consider removing `check_opaque_meets_bounds` entirely. let param_env = self.tcx.param_env(def_id); @@ -281,6 +259,8 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { .to_predicate(infcx.tcx); let mut fulfillment_cx = >::new(infcx.tcx); + let id_substs = InternalSubsts::identity_for_item(self.tcx, def_id.to_def_id()); + // Require that the hidden type actually fulfills all the bounds of the opaque type, even without // the bounds that the function supplies. match infcx.register_hidden_type( @@ -421,200 +401,3 @@ fn check_opaque_type_parameter_valid( } true } - -struct ReverseMapper<'tcx> { - tcx: TyCtxt<'tcx>, - map: FxHashMap, GenericArg<'tcx>>, - do_not_error: bool, - - /// Span of function being checked. - span: Span, -} - -impl<'tcx> ReverseMapper<'tcx> { - fn new( - tcx: TyCtxt<'tcx>, - map: FxHashMap, GenericArg<'tcx>>, - span: Span, - ) -> Self { - Self { tcx, map, do_not_error: false, span } - } - - fn fold_kind_no_missing_regions_error(&mut self, kind: GenericArg<'tcx>) -> GenericArg<'tcx> { - assert!(!self.do_not_error); - self.do_not_error = true; - let kind = kind.fold_with(self); - self.do_not_error = false; - kind - } - - fn fold_kind_normally(&mut self, kind: GenericArg<'tcx>) -> GenericArg<'tcx> { - assert!(!self.do_not_error); - kind.fold_with(self) - } -} - -impl<'tcx> TypeFolder<'tcx> for ReverseMapper<'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - #[instrument(skip(self), level = "debug")] - fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - match *r { - // Ignore bound regions and `'static` regions that appear in the - // type, we only need to remap regions that reference lifetimes - // from the function declaration. - // This would ignore `'r` in a type like `for<'r> fn(&'r u32)`. - ty::ReLateBound(..) | ty::ReStatic => return r, - - // If regions have been erased (by writeback), don't try to unerase - // them. - ty::ReErased => return r, - - // The regions that we expect from borrow checking. - ty::ReEarlyBound(_) | ty::ReFree(_) => {} - - ty::RePlaceholder(_) | ty::ReVar(_) => { - // All of the regions in the type should either have been - // erased by writeback, or mapped back to named regions by - // borrow checking. - bug!("unexpected region kind in opaque type: {:?}", r); - } - } - - match self.map.get(&r.into()).map(|k| k.unpack()) { - Some(GenericArgKind::Lifetime(r1)) => r1, - Some(u) => panic!("region mapped to unexpected kind: {:?}", u), - None if self.do_not_error => self.tcx.lifetimes.re_static, - None => { - self.tcx - .sess - .struct_span_err(self.span, "non-defining opaque type use in defining scope") - .span_label( - self.span, - format!( - "lifetime `{}` is part of concrete type but not used in \ - parameter list of the `impl Trait` type alias", - r - ), - ) - .emit(); - - self.tcx().lifetimes.re_static - } - } - } - - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - match *ty.kind() { - ty::Closure(def_id, substs) => { - // I am a horrible monster and I pray for death. When - // we encounter a closure here, it is always a closure - // from within the function that we are currently - // type-checking -- one that is now being encapsulated - // in an opaque type. Ideally, we would - // go through the types/lifetimes that it references - // and treat them just like we would any other type, - // which means we would error out if we find any - // reference to a type/region that is not in the - // "reverse map". - // - // **However,** in the case of closures, there is a - // somewhat subtle (read: hacky) consideration. The - // problem is that our closure types currently include - // all the lifetime parameters declared on the - // enclosing function, even if they are unused by the - // closure itself. We can't readily filter them out, - // so here we replace those values with `'empty`. This - // can't really make a difference to the rest of the - // compiler; those regions are ignored for the - // outlives relation, and hence don't affect trait - // selection or auto traits, and they are erased - // during codegen. - - let generics = self.tcx.generics_of(def_id); - let substs = self.tcx.mk_substs(substs.iter().enumerate().map(|(index, kind)| { - if index < generics.parent_count { - // Accommodate missing regions in the parent kinds... - self.fold_kind_no_missing_regions_error(kind) - } else { - // ...but not elsewhere. - self.fold_kind_normally(kind) - } - })); - - self.tcx.mk_closure(def_id, substs) - } - - ty::Generator(def_id, substs, movability) => { - let generics = self.tcx.generics_of(def_id); - let substs = self.tcx.mk_substs(substs.iter().enumerate().map(|(index, kind)| { - if index < generics.parent_count { - // Accommodate missing regions in the parent kinds... - self.fold_kind_no_missing_regions_error(kind) - } else { - // ...but not elsewhere. - self.fold_kind_normally(kind) - } - })); - - self.tcx.mk_generator(def_id, substs, movability) - } - - ty::Param(param) => { - // Look it up in the substitution list. - match self.map.get(&ty.into()).map(|k| k.unpack()) { - // Found it in the substitution list; replace with the parameter from the - // opaque type. - Some(GenericArgKind::Type(t1)) => t1, - Some(u) => panic!("type mapped to unexpected kind: {:?}", u), - None => { - debug!(?param, ?self.map); - self.tcx - .sess - .struct_span_err( - self.span, - &format!( - "type parameter `{}` is part of concrete type but not \ - used in parameter list for the `impl Trait` type alias", - ty - ), - ) - .emit(); - - self.tcx().ty_error() - } - } - } - - _ => ty.super_fold_with(self), - } - } - - fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { - trace!("checking const {:?}", ct); - // Find a const parameter - match ct.kind() { - ty::ConstKind::Param(..) => { - // Look it up in the substitution list. - match self.map.get(&ct.into()).map(|k| k.unpack()) { - // Found it in the substitution list, replace with the parameter from the - // opaque type. - Some(GenericArgKind::Const(c1)) => c1, - Some(u) => panic!("const mapped to unexpected kind: {:?}", u), - None => { - self.tcx.sess.emit_err(ConstNotUsedTraitAlias { - ct: ct.to_string(), - span: self.span, - }); - - self.tcx().const_error(ct.ty()) - } - } - } - - _ => ct, - } - } -} diff --git a/compiler/rustc_borrowck/src/session_diagnostics.rs b/compiler/rustc_borrowck/src/session_diagnostics.rs index 9f19453a1a65..ff667896eb10 100644 --- a/compiler/rustc_borrowck/src/session_diagnostics.rs +++ b/compiler/rustc_borrowck/src/session_diagnostics.rs @@ -52,15 +52,6 @@ pub(crate) struct VarNeedNotMut { #[suggestion_short(applicability = "machine-applicable", code = "")] pub span: Span, } - -#[derive(Diagnostic)] -#[diag(borrowck::const_not_used_in_type_alias)] -pub(crate) struct ConstNotUsedTraitAlias { - pub ct: String, - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag(borrowck::var_cannot_escape_closure)] #[note] diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index 855917fb8286..ffade628e53b 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -511,3 +511,11 @@ impl<'tcx> TypeVisitor<'tcx> for IsSuggestableVisitor<'tcx> { c.super_visit_with(self) } } + +#[derive(Diagnostic)] +#[diag(borrowck::const_not_used_in_type_alias)] +pub(super) struct ConstNotUsedTraitAlias { + pub ct: String, + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 753d7bffe84c..7438a58be71d 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -131,6 +131,7 @@ mod generics; mod impls_ty; mod instance; mod list; +mod opaque_types; mod parameterized; mod rvalue_scopes; mod structural_impls; @@ -1300,6 +1301,32 @@ impl<'tcx> OpaqueHiddenType<'tcx> { sub: sub_diag, }); } + + #[instrument(level = "debug", skip(tcx), ret)] + pub fn remap_generic_params_to_declaration_params( + self, + opaque_type_key: OpaqueTypeKey<'tcx>, + tcx: TyCtxt<'tcx>, + ) -> Self { + let OpaqueTypeKey { def_id, substs } = opaque_type_key; + + // Use substs to build up a reverse map from regions to their + // identity mappings. This is necessary because of `impl + // Trait` lifetimes are computed by replacing existing + // lifetimes with 'static and remapping only those used in the + // `impl Trait` return type, resulting in the parameters + // shifting. + let id_substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); + debug!(?id_substs); + let map: FxHashMap, GenericArg<'tcx>> = + substs.iter().enumerate().map(|(index, subst)| (subst, id_substs[index])).collect(); + debug!("map = {:#?}", map); + + // Convert the type from the function into a type valid outside + // the function, by replacing invalid regions with 'static, + // after producing an error for each of them. + self.fold_with(&mut opaque_types::ReverseMapper::new(tcx, map, self.span)) + } } /// The "placeholder index" fully defines a placeholder region, type, or const. Placeholders are diff --git a/compiler/rustc_middle/src/ty/opaque_types.rs b/compiler/rustc_middle/src/ty/opaque_types.rs new file mode 100644 index 000000000000..9bea4e431224 --- /dev/null +++ b/compiler/rustc_middle/src/ty/opaque_types.rs @@ -0,0 +1,205 @@ +use rustc_data_structures::fx::FxHashMap; +use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable}; +use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable}; +use rustc_span::Span; + +/// Converts generic params of a TypeFoldable from one +/// item's generics to another. Usually from a function's generics +/// list to the opaque type's own generics. +pub(super) struct ReverseMapper<'tcx> { + tcx: TyCtxt<'tcx>, + map: FxHashMap, GenericArg<'tcx>>, + do_not_error: bool, + + /// Span of function being checked. + span: Span, +} + +impl<'tcx> ReverseMapper<'tcx> { + pub(super) fn new( + tcx: TyCtxt<'tcx>, + map: FxHashMap, GenericArg<'tcx>>, + span: Span, + ) -> Self { + Self { tcx, map, do_not_error: false, span } + } + + fn fold_kind_no_missing_regions_error(&mut self, kind: GenericArg<'tcx>) -> GenericArg<'tcx> { + assert!(!self.do_not_error); + self.do_not_error = true; + let kind = kind.fold_with(self); + self.do_not_error = false; + kind + } + + fn fold_kind_normally(&mut self, kind: GenericArg<'tcx>) -> GenericArg<'tcx> { + assert!(!self.do_not_error); + kind.fold_with(self) + } +} + +impl<'tcx> TypeFolder<'tcx> for ReverseMapper<'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + #[instrument(skip(self), level = "debug")] + fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { + match *r { + // Ignore bound regions and `'static` regions that appear in the + // type, we only need to remap regions that reference lifetimes + // from the function declaration. + // This would ignore `'r` in a type like `for<'r> fn(&'r u32)`. + ty::ReLateBound(..) | ty::ReStatic => return r, + + // If regions have been erased (by writeback), don't try to unerase + // them. + ty::ReErased => return r, + + // The regions that we expect from borrow checking. + ty::ReEarlyBound(_) | ty::ReFree(_) => {} + + ty::RePlaceholder(_) | ty::ReVar(_) => { + // All of the regions in the type should either have been + // erased by writeback, or mapped back to named regions by + // borrow checking. + bug!("unexpected region kind in opaque type: {:?}", r); + } + } + + match self.map.get(&r.into()).map(|k| k.unpack()) { + Some(GenericArgKind::Lifetime(r1)) => r1, + Some(u) => panic!("region mapped to unexpected kind: {:?}", u), + None if self.do_not_error => self.tcx.lifetimes.re_static, + None => { + self.tcx + .sess + .struct_span_err(self.span, "non-defining opaque type use in defining scope") + .span_label( + self.span, + format!( + "lifetime `{}` is part of concrete type but not used in \ + parameter list of the `impl Trait` type alias", + r + ), + ) + .emit(); + + self.tcx().lifetimes.re_static + } + } + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + match *ty.kind() { + ty::Closure(def_id, substs) => { + // I am a horrible monster and I pray for death. When + // we encounter a closure here, it is always a closure + // from within the function that we are currently + // type-checking -- one that is now being encapsulated + // in an opaque type. Ideally, we would + // go through the types/lifetimes that it references + // and treat them just like we would any other type, + // which means we would error out if we find any + // reference to a type/region that is not in the + // "reverse map". + // + // **However,** in the case of closures, there is a + // somewhat subtle (read: hacky) consideration. The + // problem is that our closure types currently include + // all the lifetime parameters declared on the + // enclosing function, even if they are unused by the + // closure itself. We can't readily filter them out, + // so here we replace those values with `'empty`. This + // can't really make a difference to the rest of the + // compiler; those regions are ignored for the + // outlives relation, and hence don't affect trait + // selection or auto traits, and they are erased + // during codegen. + + let generics = self.tcx.generics_of(def_id); + let substs = self.tcx.mk_substs(substs.iter().enumerate().map(|(index, kind)| { + if index < generics.parent_count { + // Accommodate missing regions in the parent kinds... + self.fold_kind_no_missing_regions_error(kind) + } else { + // ...but not elsewhere. + self.fold_kind_normally(kind) + } + })); + + self.tcx.mk_closure(def_id, substs) + } + + ty::Generator(def_id, substs, movability) => { + let generics = self.tcx.generics_of(def_id); + let substs = self.tcx.mk_substs(substs.iter().enumerate().map(|(index, kind)| { + if index < generics.parent_count { + // Accommodate missing regions in the parent kinds... + self.fold_kind_no_missing_regions_error(kind) + } else { + // ...but not elsewhere. + self.fold_kind_normally(kind) + } + })); + + self.tcx.mk_generator(def_id, substs, movability) + } + + ty::Param(param) => { + // Look it up in the substitution list. + match self.map.get(&ty.into()).map(|k| k.unpack()) { + // Found it in the substitution list; replace with the parameter from the + // opaque type. + Some(GenericArgKind::Type(t1)) => t1, + Some(u) => panic!("type mapped to unexpected kind: {:?}", u), + None => { + debug!(?param, ?self.map); + self.tcx + .sess + .struct_span_err( + self.span, + &format!( + "type parameter `{}` is part of concrete type but not \ + used in parameter list for the `impl Trait` type alias", + ty + ), + ) + .emit(); + + self.tcx().ty_error() + } + } + } + + _ => ty.super_fold_with(self), + } + } + + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + trace!("checking const {:?}", ct); + // Find a const parameter + match ct.kind() { + ty::ConstKind::Param(..) => { + // Look it up in the substitution list. + match self.map.get(&ct.into()).map(|k| k.unpack()) { + // Found it in the substitution list, replace with the parameter from the + // opaque type. + Some(GenericArgKind::Const(c1)) => c1, + Some(u) => panic!("const mapped to unexpected kind: {:?}", u), + None => { + self.tcx.sess.emit_err(ty::ConstNotUsedTraitAlias { + ct: ct.to_string(), + span: self.span, + }); + + self.tcx().const_error(ct.ty()) + } + } + } + + _ => ct, + } + } +} From 70d39abbc294ef54a9c900a5d0075f3973c91ece Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 27 Sep 2022 13:02:44 +0000 Subject: [PATCH 047/333] Remap hidden types from typeck before storing them in the TypeckResult --- .../src/region_infer/opaque_types.rs | 2 +- .../rustc_hir_analysis/src/check/writeback.rs | 11 ++++- compiler/rustc_middle/src/ty/mod.rs | 4 +- compiler/rustc_middle/src/ty/opaque_types.rs | 43 ++++++++++++------- 4 files changed, 42 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index aa751927ba71..56987edd1b6e 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -226,7 +226,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { } let definition_ty = instantiated_ty - .remap_generic_params_to_declaration_params(opaque_type_key, self.tcx) + .remap_generic_params_to_declaration_params(opaque_type_key, self.tcx, false) .ty; if !check_opaque_type_parameter_valid( diff --git a/compiler/rustc_hir_analysis/src/check/writeback.rs b/compiler/rustc_hir_analysis/src/check/writeback.rs index f789978ae7e4..9aeee091fdde 100644 --- a/compiler/rustc_hir_analysis/src/check/writeback.rs +++ b/compiler/rustc_hir_analysis/src/check/writeback.rs @@ -536,7 +536,8 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { let opaque_types = self.fcx.infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); for (opaque_type_key, decl) in opaque_types { - let hidden_type = self.resolve(decl.hidden_type.ty, &decl.hidden_type.span); + let hidden_type = self.resolve(decl.hidden_type, &decl.hidden_type.span); + let opaque_type_key = self.resolve(opaque_type_key, &decl.hidden_type.span); struct RecursionChecker { def_id: LocalDefId, @@ -559,6 +560,14 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { continue; } + let hidden_type = hidden_type + .remap_generic_params_to_declaration_params( + opaque_type_key, + self.fcx.infcx.tcx, + true, + ) + .ty; + self.typeck_results.concrete_opaque_types.insert(opaque_type_key.def_id, hidden_type); } } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 7438a58be71d..cb7700f9bcab 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1307,6 +1307,8 @@ impl<'tcx> OpaqueHiddenType<'tcx> { self, opaque_type_key: OpaqueTypeKey<'tcx>, tcx: TyCtxt<'tcx>, + // typeck errors have subpar spans for opaque types, so delay error reporting until borrowck. + ignore_errors: bool, ) -> Self { let OpaqueTypeKey { def_id, substs } = opaque_type_key; @@ -1325,7 +1327,7 @@ impl<'tcx> OpaqueHiddenType<'tcx> { // Convert the type from the function into a type valid outside // the function, by replacing invalid regions with 'static, // after producing an error for each of them. - self.fold_with(&mut opaque_types::ReverseMapper::new(tcx, map, self.span)) + self.fold_with(&mut opaque_types::ReverseMapper::new(tcx, map, self.span, ignore_errors)) } } diff --git a/compiler/rustc_middle/src/ty/opaque_types.rs b/compiler/rustc_middle/src/ty/opaque_types.rs index 9bea4e431224..b05c6310929b 100644 --- a/compiler/rustc_middle/src/ty/opaque_types.rs +++ b/compiler/rustc_middle/src/ty/opaque_types.rs @@ -10,8 +10,16 @@ use rustc_span::Span; pub(super) struct ReverseMapper<'tcx> { tcx: TyCtxt<'tcx>, map: FxHashMap, GenericArg<'tcx>>, + /// see call sites to fold_kind_no_missing_regions_error + /// for an explanation of this field. do_not_error: bool, + /// We do not want to emit any errors in typeck because + /// the spans in typeck are subpar at the moment. + /// Borrowck will do the same work again (this time with + /// lifetime information) and thus report better errors. + ignore_errors: bool, + /// Span of function being checked. span: Span, } @@ -21,8 +29,9 @@ impl<'tcx> ReverseMapper<'tcx> { tcx: TyCtxt<'tcx>, map: FxHashMap, GenericArg<'tcx>>, span: Span, + ignore_errors: bool, ) -> Self { - Self { tcx, map, do_not_error: false, span } + Self { tcx, map, do_not_error: false, ignore_errors, span } } fn fold_kind_no_missing_regions_error(&mut self, kind: GenericArg<'tcx>) -> GenericArg<'tcx> { @@ -156,17 +165,19 @@ impl<'tcx> TypeFolder<'tcx> for ReverseMapper<'tcx> { Some(u) => panic!("type mapped to unexpected kind: {:?}", u), None => { debug!(?param, ?self.map); - self.tcx - .sess - .struct_span_err( - self.span, - &format!( - "type parameter `{}` is part of concrete type but not \ + if !self.ignore_errors { + self.tcx + .sess + .struct_span_err( + self.span, + &format!( + "type parameter `{}` is part of concrete type but not \ used in parameter list for the `impl Trait` type alias", - ty - ), - ) - .emit(); + ty + ), + ) + .emit(); + } self.tcx().ty_error() } @@ -189,10 +200,12 @@ impl<'tcx> TypeFolder<'tcx> for ReverseMapper<'tcx> { Some(GenericArgKind::Const(c1)) => c1, Some(u) => panic!("const mapped to unexpected kind: {:?}", u), None => { - self.tcx.sess.emit_err(ty::ConstNotUsedTraitAlias { - ct: ct.to_string(), - span: self.span, - }); + if !self.ignore_errors { + self.tcx.sess.emit_err(ty::ConstNotUsedTraitAlias { + ct: ct.to_string(), + span: self.span, + }); + } self.tcx().const_error(ct.ty()) } From f85d3a7e333d48098209fc05438b37e285cd1f9c Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Sat, 1 Oct 2022 11:08:58 +0000 Subject: [PATCH 048/333] Check hidden types in dead code --- .../rustc_hir_analysis/src/check/writeback.rs | 12 ++--- .../rustc_hir_analysis/src/collect/type_of.rs | 46 +++++++++++++------ compiler/rustc_middle/src/ty/context.rs | 2 +- .../different_defining_uses_never_type.rs | 4 +- .../different_defining_uses_never_type.stderr | 14 ++++++ 5 files changed, 55 insertions(+), 23 deletions(-) create mode 100644 src/test/ui/type-alias-impl-trait/different_defining_uses_never_type.stderr diff --git a/compiler/rustc_hir_analysis/src/check/writeback.rs b/compiler/rustc_hir_analysis/src/check/writeback.rs index 9aeee091fdde..e3e4a934ab56 100644 --- a/compiler/rustc_hir_analysis/src/check/writeback.rs +++ b/compiler/rustc_hir_analysis/src/check/writeback.rs @@ -560,13 +560,11 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { continue; } - let hidden_type = hidden_type - .remap_generic_params_to_declaration_params( - opaque_type_key, - self.fcx.infcx.tcx, - true, - ) - .ty; + let hidden_type = hidden_type.remap_generic_params_to_declaration_params( + opaque_type_key, + self.fcx.infcx.tcx, + true, + ); self.typeck_results.concrete_opaque_types.insert(opaque_type_key.def_id, hidden_type); } diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index ad9b124c8ab6..0a69f965e751 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -564,6 +564,11 @@ fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> T /// checked against it (we also carry the span of that first /// type). found: Option>, + + /// In the presence of dead code, typeck may figure out a hidden type + /// while borrowck will now. We collect these cases here and check at + /// the end that we actually found a type that matches (modulo regions). + typeck_types: Vec>, } impl ConstraintLocator<'_> { @@ -590,18 +595,23 @@ fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> T self.found = Some(ty::OpaqueHiddenType { span: DUMMY_SP, ty: self.tcx.ty_error() }); return; } - if !tables.concrete_opaque_types.contains_key(&self.def_id) { + let Some(&typeck_hidden_ty) = tables.concrete_opaque_types.get(&self.def_id) else { debug!("no constraints in typeck results"); return; + }; + if self.typeck_types.iter().all(|prev| prev.ty != typeck_hidden_ty.ty) { + self.typeck_types.push(typeck_hidden_ty); } + // Use borrowck to get the type with unerased regions. let concrete_opaque_types = &self.tcx.mir_borrowck(item_def_id).concrete_opaque_types; debug!(?concrete_opaque_types); if let Some(&concrete_type) = concrete_opaque_types.get(&self.def_id) { debug!(?concrete_type, "found constraint"); - if let Some(prev) = self.found { - if concrete_type.ty != prev.ty && !(concrete_type, prev).references_error() { + if let Some(prev) = &mut self.found { + if concrete_type.ty != prev.ty && !(concrete_type, prev.ty).references_error() { prev.report_mismatch(&concrete_type, self.tcx); + prev.ty = self.tcx.ty_error(); } } else { self.found = Some(concrete_type); @@ -648,7 +658,7 @@ fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> T let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); let scope = tcx.hir().get_defining_scope(hir_id); - let mut locator = ConstraintLocator { def_id: def_id, tcx, found: None }; + let mut locator = ConstraintLocator { def_id: def_id, tcx, found: None, typeck_types: vec![] }; debug!(?scope); @@ -678,16 +688,26 @@ fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> T } } - match locator.found { - Some(hidden) => hidden.ty, - None => { - tcx.sess.emit_err(UnconstrainedOpaqueType { - span: tcx.def_span(def_id), - name: tcx.item_name(tcx.local_parent(def_id).to_def_id()), - }); - tcx.ty_error() + let Some(hidden) = locator.found else { + tcx.sess.emit_err(UnconstrainedOpaqueType { + span: tcx.def_span(def_id), + name: tcx.item_name(tcx.local_parent(def_id).to_def_id()), + }); + return tcx.ty_error(); + }; + + // Only check against typeck if we didn't already error + if !hidden.ty.references_error() { + for concrete_type in locator.typeck_types { + if tcx.erase_regions(concrete_type.ty) != tcx.erase_regions(hidden.ty) + && !(concrete_type, hidden).references_error() + { + hidden.report_mismatch(&concrete_type, tcx); + } } } + + hidden.ty } fn find_opaque_ty_constraints_for_rpit( @@ -788,7 +808,7 @@ fn find_opaque_ty_constraints_for_rpit( // the `concrete_opaque_types` table. tcx.ty_error() } else { - table.concrete_opaque_types.get(&def_id).copied().unwrap_or_else(|| { + table.concrete_opaque_types.get(&def_id).map(|ty| ty.ty).unwrap_or_else(|| { // We failed to resolve the opaque type or it // resolves to itself. We interpret this as the // no values of the hidden type ever being constructed, diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 8277d1413587..8a9160d24664 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -542,7 +542,7 @@ pub struct TypeckResults<'tcx> { /// by this function. We also store the /// type here, so that mir-borrowck can use it as a hint for figuring out hidden types, /// even if they are only set in dead code (which doesn't show up in MIR). - pub concrete_opaque_types: VecMap>, + pub concrete_opaque_types: VecMap>, /// Tracks the minimum captures required for a closure; /// see `MinCaptureInformationMap` for more details. diff --git a/src/test/ui/type-alias-impl-trait/different_defining_uses_never_type.rs b/src/test/ui/type-alias-impl-trait/different_defining_uses_never_type.rs index 7740f774ebca..0b8157fe33dd 100644 --- a/src/test/ui/type-alias-impl-trait/different_defining_uses_never_type.rs +++ b/src/test/ui/type-alias-impl-trait/different_defining_uses_never_type.rs @@ -1,5 +1,5 @@ #![feature(type_alias_impl_trait)] -// check-pass + fn main() {} // two definitions with different types @@ -9,7 +9,7 @@ fn foo() -> Foo { "" } -fn bar() -> Foo { +fn bar() -> Foo { //~ ERROR: concrete type differs from previous defining opaque type use panic!() } diff --git a/src/test/ui/type-alias-impl-trait/different_defining_uses_never_type.stderr b/src/test/ui/type-alias-impl-trait/different_defining_uses_never_type.stderr new file mode 100644 index 000000000000..09dadb0afcef --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/different_defining_uses_never_type.stderr @@ -0,0 +1,14 @@ +error: concrete type differs from previous defining opaque type use + --> $DIR/different_defining_uses_never_type.rs:12:13 + | +LL | fn bar() -> Foo { + | ^^^ expected `&'static str`, got `()` + | +note: previous use here + --> $DIR/different_defining_uses_never_type.rs:9:5 + | +LL | "" + | ^^ + +error: aborting due to previous error + From e07f36c9610bac9433151cdaacb9764004d822e6 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 5 Oct 2022 09:18:00 +0000 Subject: [PATCH 049/333] Add a regression test --- .../different_defining_uses_never_type3.rs | 12 ++++++++++++ .../different_defining_uses_never_type3.stderr | 14 ++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 src/test/ui/type-alias-impl-trait/different_defining_uses_never_type3.rs create mode 100644 src/test/ui/type-alias-impl-trait/different_defining_uses_never_type3.stderr diff --git a/src/test/ui/type-alias-impl-trait/different_defining_uses_never_type3.rs b/src/test/ui/type-alias-impl-trait/different_defining_uses_never_type3.rs new file mode 100644 index 000000000000..bc827a8f2113 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/different_defining_uses_never_type3.rs @@ -0,0 +1,12 @@ +#![feature(type_alias_impl_trait)] + +type Tait = impl Sized; + +struct One; +fn one() -> Tait { One } + +struct Two(T); +fn two() -> Tait { Two::<()>(todo!()) } +//~^ ERROR concrete type differs from previous defining opaque type use + +fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/different_defining_uses_never_type3.stderr b/src/test/ui/type-alias-impl-trait/different_defining_uses_never_type3.stderr new file mode 100644 index 000000000000..146a57cbb7e0 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/different_defining_uses_never_type3.stderr @@ -0,0 +1,14 @@ +error: concrete type differs from previous defining opaque type use + --> $DIR/different_defining_uses_never_type3.rs:9:13 + | +LL | fn two() -> Tait { Two::<()>(todo!()) } + | ^^^^ expected `One`, got `Two<()>` + | +note: previous use here + --> $DIR/different_defining_uses_never_type3.rs:6:20 + | +LL | fn one() -> Tait { One } + | ^^^ + +error: aborting due to previous error + From 3c8b46cab91b312fe148ae4ce576fa5a2c13e6b3 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 5 Oct 2022 10:46:59 +0000 Subject: [PATCH 050/333] typeck result is now affected by opaque type spans --- src/test/incremental/hashes/function_interfaces.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/incremental/hashes/function_interfaces.rs b/src/test/incremental/hashes/function_interfaces.rs index 076eddaabc04..3ff949fbb3f8 100644 --- a/src/test/incremental/hashes/function_interfaces.rs +++ b/src/test/incremental/hashes/function_interfaces.rs @@ -323,7 +323,7 @@ pub fn change_return_impl_trait() -> impl Clone { #[cfg(not(any(cfail1,cfail4)))] #[rustc_clean(cfg = "cfail2")] #[rustc_clean(cfg = "cfail3")] -#[rustc_clean(cfg = "cfail5")] +#[rustc_clean(cfg = "cfail5", except = "typeck")] #[rustc_clean(cfg = "cfail6")] pub fn change_return_impl_trait() -> impl Copy { 0u32 From 0c47fdfedbb44256b3c78f315cd6150190317e38 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Fri, 7 Oct 2022 13:05:45 -0700 Subject: [PATCH 051/333] Cast vtable type too --- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index c8ff26f74e3b..5837b4ff2b9d 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -286,6 +286,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bug!("Only valid to do a DynStar cast into a DynStar type") }; let vtable = get_vtable(bx.cx(), source.ty(self.mir, bx.tcx()), trait_ref); + let vtable = bx.pointercast(vtable, bx.cx().type_ptr_to(bx.cx().type_isize())); let data = match operand.layout.pointee_info_at(bx.cx(), Size::ZERO) { Some(_) => bx.ptrtoint(data, bx.cx().type_isize()), None => data, From b4c8a7b952de72bc70e798408efbd4124fa15c59 Mon Sep 17 00:00:00 2001 From: joboet Date: Sat, 8 Oct 2022 09:07:28 +0200 Subject: [PATCH 052/333] std: remove unused linker attribute --- library/std/src/sys/unix/thread_parker/darwin.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/sys/unix/thread_parker/darwin.rs b/library/std/src/sys/unix/thread_parker/darwin.rs index 510839d5dafb..2f5356fe2276 100644 --- a/library/std/src/sys/unix/thread_parker/darwin.rs +++ b/library/std/src/sys/unix/thread_parker/darwin.rs @@ -24,7 +24,7 @@ type dispatch_time_t = u64; const DISPATCH_TIME_NOW: dispatch_time_t = 0; const DISPATCH_TIME_FOREVER: dispatch_time_t = !0; -#[link(name = "System", kind = "dylib")] +// Contained in libSystem.dylib, which is linked by default. extern "C" { fn dispatch_time(when: dispatch_time_t, delta: i64) -> dispatch_time_t; fn dispatch_semaphore_create(val: isize) -> dispatch_semaphore_t; From c320ab98ff1d4adb32cece206aa895e4effae175 Mon Sep 17 00:00:00 2001 From: joboet Date: Sat, 8 Oct 2022 09:12:06 +0200 Subject: [PATCH 053/333] std: do not use dispatch semaphore under miri (yet) --- library/std/src/sys/unix/thread_parker/mod.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/library/std/src/sys/unix/thread_parker/mod.rs b/library/std/src/sys/unix/thread_parker/mod.rs index 724ec2d482ed..35f1e68a87e5 100644 --- a/library/std/src/sys/unix/thread_parker/mod.rs +++ b/library/std/src/sys/unix/thread_parker/mod.rs @@ -11,11 +11,14 @@ )))] cfg_if::cfg_if! { - if #[cfg(any( - target_os = "macos", - target_os = "ios", - target_os = "watchos", - target_os = "tvos", + if #[cfg(all( + any( + target_os = "macos", + target_os = "ios", + target_os = "watchos", + target_os = "tvos", + ), + not(miri), ))] { mod darwin; pub use darwin::Parker; From f0b8167a4e7150aa77a3da8072b1b019f2c52d5d Mon Sep 17 00:00:00 2001 From: woppopo Date: Sat, 8 Oct 2022 11:48:53 +0000 Subject: [PATCH 054/333] Fix test (location_const_file) --- library/core/tests/panic/location.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/tests/panic/location.rs b/library/core/tests/panic/location.rs index 9df626a8326c..d20241d83800 100644 --- a/library/core/tests/panic/location.rs +++ b/library/core/tests/panic/location.rs @@ -13,7 +13,7 @@ fn location_const_caller() { fn location_const_file() { const CALLER: &Location<'static> = Location::caller(); const FILE: &str = CALLER.file(); - assert_eq!(FILE, "library/core/tests/panic/location.rs"); + assert_eq!(FILE, file!()); } #[test] From d4578013541a5c6ae34b62a83e8dcb11fb6d4b05 Mon Sep 17 00:00:00 2001 From: joboet Date: Sat, 8 Oct 2022 20:19:21 +0200 Subject: [PATCH 055/333] std: optimize TLS on Windows --- library/std/src/sys/sgx/thread_local_key.rs | 5 - library/std/src/sys/solid/thread_local_key.rs | 5 - library/std/src/sys/unix/thread_local_key.rs | 5 - .../src/sys/unsupported/thread_local_key.rs | 5 - library/std/src/sys/windows/c.rs | 17 ++ .../std/src/sys/windows/thread_local_key.rs | 204 +++++++++++------- .../src/sys/windows/thread_local_key/tests.rs | 53 +++++ library/std/src/sys_common/mod.rs | 9 +- .../std/src/sys_common/thread_local_key.rs | 24 +-- 9 files changed, 206 insertions(+), 121 deletions(-) create mode 100644 library/std/src/sys/windows/thread_local_key/tests.rs diff --git a/library/std/src/sys/sgx/thread_local_key.rs b/library/std/src/sys/sgx/thread_local_key.rs index b21784475f0d..c7a57d3a3d47 100644 --- a/library/std/src/sys/sgx/thread_local_key.rs +++ b/library/std/src/sys/sgx/thread_local_key.rs @@ -21,8 +21,3 @@ pub unsafe fn get(key: Key) -> *mut u8 { pub unsafe fn destroy(key: Key) { Tls::destroy(AbiKey::from_usize(key)) } - -#[inline] -pub fn requires_synchronized_create() -> bool { - false -} diff --git a/library/std/src/sys/solid/thread_local_key.rs b/library/std/src/sys/solid/thread_local_key.rs index b17521f701da..b37bf9996988 100644 --- a/library/std/src/sys/solid/thread_local_key.rs +++ b/library/std/src/sys/solid/thread_local_key.rs @@ -19,8 +19,3 @@ pub unsafe fn get(_key: Key) -> *mut u8 { pub unsafe fn destroy(_key: Key) { panic!("should not be used on the solid target"); } - -#[inline] -pub fn requires_synchronized_create() -> bool { - panic!("should not be used on the solid target"); -} diff --git a/library/std/src/sys/unix/thread_local_key.rs b/library/std/src/sys/unix/thread_local_key.rs index 2c5b94b1e61e..2b2d079ee4d0 100644 --- a/library/std/src/sys/unix/thread_local_key.rs +++ b/library/std/src/sys/unix/thread_local_key.rs @@ -27,8 +27,3 @@ pub unsafe fn destroy(key: Key) { let r = libc::pthread_key_delete(key); debug_assert_eq!(r, 0); } - -#[inline] -pub fn requires_synchronized_create() -> bool { - false -} diff --git a/library/std/src/sys/unsupported/thread_local_key.rs b/library/std/src/sys/unsupported/thread_local_key.rs index c31b61cbf56d..b6e5e4cd2e19 100644 --- a/library/std/src/sys/unsupported/thread_local_key.rs +++ b/library/std/src/sys/unsupported/thread_local_key.rs @@ -19,8 +19,3 @@ pub unsafe fn get(_key: Key) -> *mut u8 { pub unsafe fn destroy(_key: Key) { panic!("should not be used on this target"); } - -#[inline] -pub fn requires_synchronized_create() -> bool { - panic!("should not be used on this target"); -} diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs index 89d0ab59be89..e43229588f88 100644 --- a/library/std/src/sys/windows/c.rs +++ b/library/std/src/sys/windows/c.rs @@ -71,6 +71,7 @@ pub type BCRYPT_ALG_HANDLE = LPVOID; pub type PCONDITION_VARIABLE = *mut CONDITION_VARIABLE; pub type PLARGE_INTEGER = *mut c_longlong; pub type PSRWLOCK = *mut SRWLOCK; +pub type LPINIT_ONCE = *mut INIT_ONCE; pub type SOCKET = crate::os::windows::raw::SOCKET; pub type socklen_t = c_int; @@ -194,6 +195,9 @@ pub const DUPLICATE_SAME_ACCESS: DWORD = 0x00000002; pub const CONDITION_VARIABLE_INIT: CONDITION_VARIABLE = CONDITION_VARIABLE { ptr: ptr::null_mut() }; pub const SRWLOCK_INIT: SRWLOCK = SRWLOCK { ptr: ptr::null_mut() }; +pub const INIT_ONCE_STATIC_INIT: INIT_ONCE = INIT_ONCE { ptr: ptr::null_mut() }; + +pub const INIT_ONCE_INIT_FAILED: DWORD = 0x00000004; pub const DETACHED_PROCESS: DWORD = 0x00000008; pub const CREATE_NEW_PROCESS_GROUP: DWORD = 0x00000200; @@ -565,6 +569,10 @@ pub struct CONDITION_VARIABLE { pub struct SRWLOCK { pub ptr: LPVOID, } +#[repr(C)] +pub struct INIT_ONCE { + pub ptr: LPVOID, +} #[repr(C)] pub struct REPARSE_MOUNTPOINT_DATA_BUFFER { @@ -959,6 +967,7 @@ extern "system" { pub fn TlsAlloc() -> DWORD; pub fn TlsGetValue(dwTlsIndex: DWORD) -> LPVOID; pub fn TlsSetValue(dwTlsIndex: DWORD, lpTlsvalue: LPVOID) -> BOOL; + pub fn TlsFree(dwTlsIndex: DWORD) -> BOOL; pub fn GetLastError() -> DWORD; pub fn QueryPerformanceFrequency(lpFrequency: *mut LARGE_INTEGER) -> BOOL; pub fn QueryPerformanceCounter(lpPerformanceCount: *mut LARGE_INTEGER) -> BOOL; @@ -1118,6 +1127,14 @@ extern "system" { pub fn TryAcquireSRWLockExclusive(SRWLock: PSRWLOCK) -> BOOLEAN; pub fn TryAcquireSRWLockShared(SRWLock: PSRWLOCK) -> BOOLEAN; + pub fn InitOnceBeginInitialize( + lpInitOnce: LPINIT_ONCE, + dwFlags: DWORD, + fPending: LPBOOL, + lpContext: *mut LPVOID, + ) -> BOOL; + pub fn InitOnceComplete(lpInitOnce: LPINIT_ONCE, dwFlags: DWORD, lpContext: LPVOID) -> BOOL; + pub fn CompareStringOrdinal( lpString1: LPCWSTR, cchCount1: c_int, diff --git a/library/std/src/sys/windows/thread_local_key.rs b/library/std/src/sys/windows/thread_local_key.rs index ec670238e6f0..17628b7579b8 100644 --- a/library/std/src/sys/windows/thread_local_key.rs +++ b/library/std/src/sys/windows/thread_local_key.rs @@ -1,11 +1,16 @@ -use crate::mem::ManuallyDrop; +use crate::cell::UnsafeCell; use crate::ptr; -use crate::sync::atomic::AtomicPtr; -use crate::sync::atomic::Ordering::SeqCst; +use crate::sync::atomic::{ + AtomicPtr, AtomicU32, + Ordering::{AcqRel, Acquire, Relaxed, Release}, +}; use crate::sys::c; -pub type Key = c::DWORD; -pub type Dtor = unsafe extern "C" fn(*mut u8); +#[cfg(test)] +mod tests; + +type Key = c::DWORD; +type Dtor = unsafe extern "C" fn(*mut u8); // Turns out, like pretty much everything, Windows is pretty close the // functionality that Unix provides, but slightly different! In the case of @@ -22,59 +27,108 @@ pub type Dtor = unsafe extern "C" fn(*mut u8); // To accomplish this feat, we perform a number of threads, all contained // within this module: // -// * All TLS destructors are tracked by *us*, not the windows runtime. This +// * All TLS destructors are tracked by *us*, not the Windows runtime. This // means that we have a global list of destructors for each TLS key that // we know about. // * When a thread exits, we run over the entire list and run dtors for all // non-null keys. This attempts to match Unix semantics in this regard. // -// This ends up having the overhead of using a global list, having some -// locks here and there, and in general just adding some more code bloat. We -// attempt to optimize runtime by forgetting keys that don't have -// destructors, but this only gets us so far. -// // For more details and nitty-gritty, see the code sections below! // // [1]: https://www.codeproject.com/Articles/8113/Thread-Local-Storage-The-C-Way -// [2]: https://github.com/ChromiumWebApps/chromium/blob/master/base -// /threading/thread_local_storage_win.cc#L42 +// [2]: https://github.com/ChromiumWebApps/chromium/blob/master/base/threading/thread_local_storage_win.cc#L42 -// ------------------------------------------------------------------------- -// Native bindings -// -// This section is just raw bindings to the native functions that Windows -// provides, There's a few extra calls to deal with destructors. +pub struct StaticKey { + /// The key value shifted up by one. Since TLS_OUT_OF_INDEXES == DWORD::MAX + /// is not a valid key value, this allows us to use zero as sentinel value + /// without risking overflow. + key: AtomicU32, + dtor: Option, + next: AtomicPtr, + /// Currently, destructors cannot be unregistered, so we cannot use racy + /// initialization for keys. Instead, we need synchronize initialization. + /// Use the Windows-provided `Once` since it does not require TLS. + once: UnsafeCell, +} -#[inline] -pub unsafe fn create(dtor: Option) -> Key { - let key = c::TlsAlloc(); - assert!(key != c::TLS_OUT_OF_INDEXES); - if let Some(f) = dtor { - register_dtor(key, f); +impl StaticKey { + #[inline] + pub const fn new(dtor: Option) -> StaticKey { + StaticKey { + key: AtomicU32::new(0), + dtor, + next: AtomicPtr::new(ptr::null_mut()), + once: UnsafeCell::new(c::INIT_ONCE_STATIC_INIT), + } + } + + #[inline] + pub unsafe fn set(&'static self, val: *mut u8) { + let r = c::TlsSetValue(self.key(), val.cast()); + debug_assert_eq!(r, c::TRUE); + } + + #[inline] + pub unsafe fn get(&'static self) -> *mut u8 { + c::TlsGetValue(self.key()).cast() + } + + #[inline] + unsafe fn key(&'static self) -> Key { + match self.key.load(Acquire) { + 0 => self.init(), + key => key - 1, + } + } + + #[cold] + unsafe fn init(&'static self) -> Key { + if self.dtor.is_some() { + let mut pending = c::FALSE; + let r = c::InitOnceBeginInitialize(self.once.get(), 0, &mut pending, ptr::null_mut()); + assert_eq!(r, c::TRUE); + + if pending == c::FALSE { + // Some other thread initialized the key, load it. + self.key.load(Relaxed) - 1 + } else { + let key = c::TlsAlloc(); + if key == c::TLS_OUT_OF_INDEXES { + // Wakeup the waiting threads before panicking to avoid deadlock. + c::InitOnceComplete(self.once.get(), c::INIT_ONCE_INIT_FAILED, ptr::null_mut()); + panic!("out of TLS indexes"); + } + + self.key.store(key + 1, Release); + register_dtor(self); + + let r = c::InitOnceComplete(self.once.get(), 0, ptr::null_mut()); + debug_assert_eq!(r, c::TRUE); + + key + } + } else { + // If there is no destructor to clean up, we can use racy initialization. + + let key = c::TlsAlloc(); + assert_ne!(key, c::TLS_OUT_OF_INDEXES, "out of TLS indexes"); + + match self.key.compare_exchange(0, key + 1, AcqRel, Acquire) { + Ok(_) => key, + Err(new) => { + // Some other thread completed initialization first, so destroy + // our key and use theirs. + let r = c::TlsFree(key); + debug_assert_eq!(r, c::TRUE); + new - 1 + } + } + } } - key } -#[inline] -pub unsafe fn set(key: Key, value: *mut u8) { - let r = c::TlsSetValue(key, value as c::LPVOID); - debug_assert!(r != 0); -} - -#[inline] -pub unsafe fn get(key: Key) -> *mut u8 { - c::TlsGetValue(key) as *mut u8 -} - -#[inline] -pub unsafe fn destroy(_key: Key) { - rtabort!("can't destroy tls keys on windows") -} - -#[inline] -pub fn requires_synchronized_create() -> bool { - true -} +unsafe impl Send for StaticKey {} +unsafe impl Sync for StaticKey {} // ------------------------------------------------------------------------- // Dtor registration @@ -96,29 +150,21 @@ pub fn requires_synchronized_create() -> bool { // Typically processes have a statically known set of TLS keys which is pretty // small, and we'd want to keep this memory alive for the whole process anyway // really. -// -// Perhaps one day we can fold the `Box` here into a static allocation, -// expanding the `StaticKey` structure to contain not only a slot for the TLS -// key but also a slot for the destructor queue on windows. An optimization for -// another day! -static DTORS: AtomicPtr = AtomicPtr::new(ptr::null_mut()); +static DTORS: AtomicPtr = AtomicPtr::new(ptr::null_mut()); -struct Node { - dtor: Dtor, - key: Key, - next: *mut Node, -} - -unsafe fn register_dtor(key: Key, dtor: Dtor) { - let mut node = ManuallyDrop::new(Box::new(Node { key, dtor, next: ptr::null_mut() })); - - let mut head = DTORS.load(SeqCst); +/// Should only be called once per key, otherwise loops or breaks may occur in +/// the linked list. +unsafe fn register_dtor(key: &'static StaticKey) { + let this = <*const StaticKey>::cast_mut(key); + // Use acquire ordering to pass along the changes done by the previously + // registered keys when we store the new head with release ordering. + let mut head = DTORS.load(Acquire); loop { - node.next = head; - match DTORS.compare_exchange(head, &mut **node, SeqCst, SeqCst) { - Ok(_) => return, // nothing to drop, we successfully added the node to the list - Err(cur) => head = cur, + key.next.store(head, Relaxed); + match DTORS.compare_exchange_weak(head, this, Release, Acquire) { + Ok(_) => break, + Err(new) => head = new, } } } @@ -214,25 +260,29 @@ unsafe extern "system" fn on_tls_callback(h: c::LPVOID, dwReason: c::DWORD, pv: unsafe fn reference_tls_used() {} } -#[allow(dead_code)] // actually called above +#[allow(dead_code)] // actually called below unsafe fn run_dtors() { - let mut any_run = true; for _ in 0..5 { - if !any_run { - break; - } - any_run = false; - let mut cur = DTORS.load(SeqCst); - while !cur.is_null() { - let ptr = c::TlsGetValue((*cur).key); + let mut any_run = false; + // Use acquire ordering to observe key initialization. + let mut cur = DTORS.load(Acquire); + while !cur.is_null() { + let key = (*cur).key.load(Relaxed) - 1; + let dtor = (*cur).dtor.unwrap(); + + let ptr = c::TlsGetValue(key); if !ptr.is_null() { - c::TlsSetValue((*cur).key, ptr::null_mut()); - ((*cur).dtor)(ptr as *mut _); + c::TlsSetValue(key, ptr::null_mut()); + dtor(ptr as *mut _); any_run = true; } - cur = (*cur).next; + cur = (*cur).next.load(Relaxed); + } + + if !any_run { + break; } } } diff --git a/library/std/src/sys/windows/thread_local_key/tests.rs b/library/std/src/sys/windows/thread_local_key/tests.rs new file mode 100644 index 000000000000..c95f383fb90e --- /dev/null +++ b/library/std/src/sys/windows/thread_local_key/tests.rs @@ -0,0 +1,53 @@ +use super::StaticKey; +use crate::ptr; + +#[test] +fn smoke() { + static K1: StaticKey = StaticKey::new(None); + static K2: StaticKey = StaticKey::new(None); + + unsafe { + assert!(K1.get().is_null()); + assert!(K2.get().is_null()); + K1.set(ptr::invalid_mut(1)); + K2.set(ptr::invalid_mut(2)); + assert_eq!(K1.get() as usize, 1); + assert_eq!(K2.get() as usize, 2); + } +} + +#[test] +fn destructors() { + use crate::mem::ManuallyDrop; + use crate::sync::Arc; + use crate::thread; + + unsafe extern "C" fn destruct(ptr: *mut u8) { + drop(Arc::from_raw(ptr as *const ())); + } + + static KEY: StaticKey = StaticKey::new(Some(destruct)); + + let shared1 = Arc::new(()); + let shared2 = Arc::clone(&shared1); + + unsafe { + assert!(KEY.get().is_null()); + KEY.set(Arc::into_raw(shared1) as *mut u8); + } + + thread::spawn(move || unsafe { + assert!(KEY.get().is_null()); + KEY.set(Arc::into_raw(shared2) as *mut u8); + }) + .join() + .unwrap(); + + // Leak the Arc, let the TLS destructor clean it up. + let shared1 = unsafe { ManuallyDrop::new(Arc::from_raw(KEY.get() as *const ())) }; + assert_eq!( + Arc::strong_count(&shared1), + 1, + "destructor should have dropped the other reference on thread exit" + ); +} diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs index 80f56bf7522b..9ea3c52fa6d7 100644 --- a/library/std/src/sys_common/mod.rs +++ b/library/std/src/sys_common/mod.rs @@ -33,10 +33,17 @@ pub mod rwlock; pub mod thread; pub mod thread_info; pub mod thread_local_dtor; -pub mod thread_local_key; pub mod thread_parker; pub mod wtf8; +cfg_if::cfg_if! { + if #[cfg(target_os = "windows")] { + pub use crate::sys::thread_local_key; + } else { + pub mod thread_local_key; + } +} + cfg_if::cfg_if! { if #[cfg(any(target_os = "l4re", target_os = "hermit", diff --git a/library/std/src/sys_common/thread_local_key.rs b/library/std/src/sys_common/thread_local_key.rs index 032bf604d738..747579f17812 100644 --- a/library/std/src/sys_common/thread_local_key.rs +++ b/library/std/src/sys_common/thread_local_key.rs @@ -53,7 +53,6 @@ mod tests; use crate::sync::atomic::{self, AtomicUsize, Ordering}; use crate::sys::thread_local_key as imp; -use crate::sys_common::mutex::StaticMutex; /// A type for TLS keys that are statically allocated. /// @@ -151,25 +150,6 @@ impl StaticKey { } unsafe fn lazy_init(&self) -> usize { - // Currently the Windows implementation of TLS is pretty hairy, and - // it greatly simplifies creation if we just synchronize everything. - // - // Additionally a 0-index of a tls key hasn't been seen on windows, so - // we just simplify the whole branch. - if imp::requires_synchronized_create() { - // We never call `INIT_LOCK.init()`, so it is UB to attempt to - // acquire this mutex reentrantly! - static INIT_LOCK: StaticMutex = StaticMutex::new(); - let _guard = INIT_LOCK.lock(); - let mut key = self.key.load(Ordering::SeqCst); - if key == 0 { - key = imp::create(self.dtor) as usize; - self.key.store(key, Ordering::SeqCst); - } - rtassert!(key != 0); - return key; - } - // POSIX allows the key created here to be 0, but the compare_exchange // below relies on using 0 as a sentinel value to check who won the // race to set the shared TLS key. As far as I know, there is no @@ -232,8 +212,6 @@ impl Key { impl Drop for Key { fn drop(&mut self) { - // Right now Windows doesn't support TLS key destruction, but this also - // isn't used anywhere other than tests, so just leak the TLS key. - // unsafe { imp::destroy(self.key) } + unsafe { imp::destroy(self.key) } } } From df7d39b2ed601fe4d3284d15c21e871750b3cbc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Sun, 2 Oct 2022 20:04:47 +0300 Subject: [PATCH 056/333] Bump once_cell --- Cargo.lock | 4 ++-- crates/hir-def/Cargo.toml | 2 +- crates/hir-ty/Cargo.toml | 2 +- crates/hir/Cargo.toml | 2 +- crates/ide-completion/Cargo.toml | 2 +- crates/ide-db/Cargo.toml | 2 +- crates/profile/Cargo.toml | 2 +- crates/syntax/Cargo.toml | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e45cde683881..cf1f166703f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1017,9 +1017,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.13.1" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e" +checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" [[package]] name = "oorandom" diff --git a/crates/hir-def/Cargo.toml b/crates/hir-def/Cargo.toml index e8cff2f3e6cd..54b0832ca6ad 100644 --- a/crates/hir-def/Cargo.toml +++ b/crates/hir-def/Cargo.toml @@ -23,7 +23,7 @@ hashbrown = { version = "0.12.1", default-features = false } indexmap = "1.9.1" itertools = "0.10.3" la-arena = { version = "0.3.0", path = "../../lib/la-arena" } -once_cell = "1.12.0" +once_cell = "1.15.0" rustc-hash = "1.1.0" smallvec = "1.9.0" tracing = "0.1.35" diff --git a/crates/hir-ty/Cargo.toml b/crates/hir-ty/Cargo.toml index 7f143f396c76..d8ede9956fc7 100644 --- a/crates/hir-ty/Cargo.toml +++ b/crates/hir-ty/Cargo.toml @@ -22,7 +22,7 @@ chalk-solve = { version = "0.84.0", default-features = false } chalk-ir = "0.84.0" chalk-recursive = { version = "0.84.0", default-features = false } la-arena = { version = "0.3.0", path = "../../lib/la-arena" } -once_cell = "1.12.0" +once_cell = "1.15.0" typed-arena = "2.0.1" stdx = { path = "../stdx", version = "0.0.0" } diff --git a/crates/hir/Cargo.toml b/crates/hir/Cargo.toml index 8e6a2441b331..0c3eed57af03 100644 --- a/crates/hir/Cargo.toml +++ b/crates/hir/Cargo.toml @@ -15,7 +15,7 @@ either = "1.7.0" arrayvec = "0.7.2" itertools = "0.10.3" smallvec = "1.9.0" -once_cell = "1.12.0" +once_cell = "1.15.0" stdx = { path = "../stdx", version = "0.0.0" } syntax = { path = "../syntax", version = "0.0.0" } diff --git a/crates/ide-completion/Cargo.toml b/crates/ide-completion/Cargo.toml index 8c9d6b228648..550f65375a83 100644 --- a/crates/ide-completion/Cargo.toml +++ b/crates/ide-completion/Cargo.toml @@ -13,7 +13,7 @@ doctest = false cov-mark = "2.0.0-pre.1" itertools = "0.10.3" -once_cell = "1.12.0" +once_cell = "1.15.0" smallvec = "1.9.0" stdx = { path = "../stdx", version = "0.0.0" } diff --git a/crates/ide-db/Cargo.toml b/crates/ide-db/Cargo.toml index 30272bc16f63..3dca3e202228 100644 --- a/crates/ide-db/Cargo.toml +++ b/crates/ide-db/Cargo.toml @@ -15,7 +15,7 @@ tracing = "0.1.35" rayon = "1.5.3" fst = { version = "0.4.7", default-features = false } rustc-hash = "1.1.0" -once_cell = "1.12.0" +once_cell = "1.15.0" either = "1.7.0" itertools = "0.10.3" arrayvec = "0.7.2" diff --git a/crates/profile/Cargo.toml b/crates/profile/Cargo.toml index 0b78a45a24b3..ed3d816da523 100644 --- a/crates/profile/Cargo.toml +++ b/crates/profile/Cargo.toml @@ -10,7 +10,7 @@ rust-version = "1.57" doctest = false [dependencies] -once_cell = "1.12.0" +once_cell = "1.15.0" cfg-if = "1.0.0" libc = "0.2.126" la-arena = { version = "0.3.0", path = "../../lib/la-arena" } diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml index 0e2dec386ff7..8005b20206de 100644 --- a/crates/syntax/Cargo.toml +++ b/crates/syntax/Cargo.toml @@ -16,7 +16,7 @@ itertools = "0.10.3" rowan = "0.15.8" rustc_lexer = { version = "725.0.0", package = "rustc-ap-rustc_lexer" } rustc-hash = "1.1.0" -once_cell = "1.12.0" +once_cell = "1.15.0" indexmap = "1.9.1" smol_str = "0.1.23" From 5bbfea03ccada14bbaca6df6c0ef3760ac44a9a5 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 8 Oct 2022 23:18:11 +0100 Subject: [PATCH 057/333] fix: in VSCode, correctly resolve relative paths to errors VS Code problem matcher are restricted to be static "regexes". You can't create a problem matcher dynamically, and you can't use custom code in lieu of problem matcher. This creates a problem for rust/cargo compiler errors. They use paths relative to the root of the Cargo workspace, but VS Code doesn't necessary know where that root is. Luckily, there's a way out: our current problem matcher is defined like this: "fileLocation": [ "autoDetect", "${workspaceRoot}" ], That means that relative pahts would be resoleved relative to workspace root. VS Code allows to specify a command inside `${}`. So we can plug custom logic there to fetch Cargo's workspace root! And that's exactly what this PR is doing! --- editors/code/package.json | 9 +++++++++ editors/code/src/commands.ts | 4 ++++ editors/code/src/ctx.ts | 4 ++++ editors/code/src/main.ts | 1 + editors/code/src/run.ts | 2 ++ editors/code/src/tasks.ts | 2 +- 6 files changed, 21 insertions(+), 1 deletion(-) diff --git a/editors/code/package.json b/editors/code/package.json index f1dd3aa79ff0..ccf62d002bb5 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -1301,6 +1301,15 @@ "endsPattern": "^\\[Finished running\\b" }, "pattern": "$rustc" + }, + { + "name": "rustc-run", + "base": "$rustc", + "fileLocation": [ + "autoDetect", + "${command:rust-analyzer.cargoWorkspaceRootForCurrentRun}" + ], + "pattern": "$rustc-run" } ], "colors": [ diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index b9ad525e361f..6b10073aa869 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts @@ -842,6 +842,7 @@ export function run(ctx: Ctx): Cmd { item.detail = "rerun"; prevRunnable = item; const task = await createTask(item.runnable, ctx.config); + ctx.cargoWorkspaceRootForCurrentRun = item.cargoWorkspaceRoot; return await vscode.tasks.executeTask(task); }; } @@ -946,3 +947,6 @@ export function linkToCommand(ctx: Ctx): Cmd { } }; } +export function getCargoWorkspaceDir(ctx: Ctx): Cmd { + return async () => ctx.cargoWorkspaceRootForCurrentRun; +} diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index 26510011d439..b6c0eedfb16a 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts @@ -17,6 +17,10 @@ export type Workspace = }; export class Ctx { + // Helps VS Code to correctly link problems from runnables. This is used by + // `rust-analyzer.cargoWorkspaceRootForCurrentRun` command of $rustc-run problem matcher. + cargoWorkspaceRootForCurrentRun?: string = undefined; + private constructor( readonly config: Config, private readonly extCtx: vscode.ExtensionContext, diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index 41bde4195e07..6e6c2513dd90 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts @@ -189,6 +189,7 @@ async function initCommonContext(context: vscode.ExtensionContext, ctx: Ctx) { ctx.registerCommand("resolveCodeAction", commands.resolveCodeAction); ctx.registerCommand("applyActionGroup", commands.applyActionGroup); ctx.registerCommand("gotoLocation", commands.gotoLocation); + ctx.registerCommand("cargoWorkspaceRootForCurrentRun", commands.getCargoWorkspaceDir); ctx.registerCommand("linkToCommand", commands.linkToCommand); } diff --git a/editors/code/src/run.ts b/editors/code/src/run.ts index 22e5eda6827d..100c0fe2d8c3 100644 --- a/editors/code/src/run.ts +++ b/editors/code/src/run.ts @@ -89,12 +89,14 @@ export async function selectRunnable( export class RunnableQuickPick implements vscode.QuickPickItem { public label: string; + public cargoWorkspaceRoot?: string; public description?: string | undefined; public detail?: string | undefined; public picked?: boolean | undefined; constructor(public runnable: ra.Runnable) { this.label = runnable.label; + this.cargoWorkspaceRoot = runnable.args.workspaceRoot; } } diff --git a/editors/code/src/tasks.ts b/editors/code/src/tasks.ts index e6239deeb21a..44697f95baba 100644 --- a/editors/code/src/tasks.ts +++ b/editors/code/src/tasks.ts @@ -128,7 +128,7 @@ export async function buildCargoTask( name, TASK_SOURCE, exec, - ["$rustc"] + ["$rustc-run"] ); } From 28fdcade79a0ed86f8453e3089a114d38a73e791 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sun, 9 Oct 2022 12:46:23 +0400 Subject: [PATCH 058/333] rustc_target: Fix json target specs using LLD linker flavors in link args --- compiler/rustc_target/src/spec/mod.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 9396d769dc70..8909cf33af91 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1739,11 +1739,15 @@ impl TargetOptions { self.lld_flavor_json, self.linker_is_gnu_json, ); - match linker_flavor { - LinkerFlavor::Gnu(_, Lld::Yes) - | LinkerFlavor::Darwin(_, Lld::Yes) - | LinkerFlavor::Msvc(Lld::Yes) => {} - _ => add_link_args_iter(args, linker_flavor, args_json.iter().cloned()), + // Normalize to no lld to avoid asserts. + let linker_flavor = match linker_flavor { + LinkerFlavor::Gnu(cc, _) => LinkerFlavor::Gnu(cc, Lld::No), + LinkerFlavor::Darwin(cc, _) => LinkerFlavor::Darwin(cc, Lld::No), + LinkerFlavor::Msvc(_) => LinkerFlavor::Msvc(Lld::No), + _ => linker_flavor, + }; + if !args.contains_key(&linker_flavor) { + add_link_args_iter(args, linker_flavor, args_json.iter().cloned()); } } } From b3c21efa8a9c22e8b25136d32b35537856bd9c45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Marie?= Date: Sun, 9 Oct 2022 16:45:04 +0000 Subject: [PATCH 059/333] openbsd: don't reallocate a guard page on the stack. the kernel currently enforce that a stack is immutable. calling mmap(2) or mprotect(2) to change it will result in EPERM, which generate a panic!(). so just do like for Linux, and trust the kernel to do the right thing. --- library/std/src/sys/unix/thread.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs index 7db3065dee08..f2c604bf7eee 100644 --- a/library/std/src/sys/unix/thread.rs +++ b/library/std/src/sys/unix/thread.rs @@ -767,6 +767,16 @@ pub mod guard { const GUARD_PAGES: usize = 1; let guard = guardaddr..guardaddr + GUARD_PAGES * page_size; Some(guard) + } else if cfg!(target_os = "openbsd") { + // OpenBSD stack already includes a guard page, and stack is + // immutable. + // + // We'll just note where we expect rlimit to start + // faulting, so our handler can report "stack overflow", and + // trust that the kernel's own stack guard will work. + let stackptr = get_stack_start_aligned()?; + let stackaddr = stackptr.addr(); + Some(stackaddr - page_size..stackaddr) } else { // Reallocate the last page of the stack. // This ensures SIGBUS will be raised on From 88bb4e4bda2e5ccafbc28ded2ef289841d5fb655 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 9 Oct 2022 15:31:27 +0100 Subject: [PATCH 060/333] impl AsFd for io::{Stdin, Stdout, Stderr}, not the sys versions https://github.com/rust-lang/rust/pull/100892 implemented AsFd for the sys versions, rather than for the public types. Change the implementations to apply to the public types. --- library/std/src/sys/wasi/stdio.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/std/src/sys/wasi/stdio.rs b/library/std/src/sys/wasi/stdio.rs index bf045c7841f6..427dcf6bb069 100644 --- a/library/std/src/sys/wasi/stdio.rs +++ b/library/std/src/sys/wasi/stdio.rs @@ -23,7 +23,7 @@ impl AsRawFd for Stdin { } } -impl AsFd for Stdin { +impl AsFd for io::Stdin { #[inline] fn as_fd(&self) -> BorrowedFd<'_> { unsafe { BorrowedFd::borrow_raw(0) } @@ -67,7 +67,7 @@ impl AsRawFd for Stdout { } } -impl AsFd for Stdout { +impl AsFd for io::Stdout { #[inline] fn as_fd(&self) -> BorrowedFd<'_> { unsafe { BorrowedFd::borrow_raw(1) } @@ -114,7 +114,7 @@ impl AsRawFd for Stderr { } } -impl AsFd for Stderr { +impl AsFd for io::Stderr { #[inline] fn as_fd(&self) -> BorrowedFd<'_> { unsafe { BorrowedFd::borrow_raw(2) } From 9c37c801ad737006c494f6b3bd97ff59764573ae Mon Sep 17 00:00:00 2001 From: Pointerbender Date: Sun, 9 Oct 2022 22:32:23 +0200 Subject: [PATCH 061/333] expand documentation on type conversion w.r.t. `UnsafeCell` --- library/core/src/cell.rs | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index b730e13dcb88..be1017e58428 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -1811,35 +1811,43 @@ impl fmt::Display for RefMut<'_, T> { /// /// [`.get_mut()`]: `UnsafeCell::get_mut` /// -/// `UnsafeCell` has the same in-memory representation as its inner type `T`. A consequence -/// of this guarantee is that it is possible to convert between `T` and `UnsafeCell`. -/// However, it is only valid to obtain a `*mut T` pointer or `&mut T` reference to the -/// contents of an `UnsafeCell` through [`.get()`], [`.raw_get()`] or [`.get_mut()`], e.g.: +/// `UnsafeCell` has the same in-memory representation as its inner type `T` if and only if +/// the type `T` does not contain a [niche] (e.g. the type `Option>` is typically +/// 8 bytes large on 64-bit platforms, but the type `Option>>` takes +/// up 16 bytes of space). A consequence of this guarantee is that it is possible to convert +/// between `T` and `UnsafeCell` when `T` has no niches. However, it is only valid to obtain +/// a `*mut T` pointer to the contents of a _shared_ `UnsafeCell` through [`.get()`] or +/// [`.raw_get()`]. A `&mut T` reference can be obtained by either dereferencing this pointer +/// or by calling [`.get_mut()`] on an _exclusive_ `UnsafeCell`, e.g.: /// /// ```rust /// use std::cell::UnsafeCell; /// /// let mut x: UnsafeCell = UnsafeCell::new(5); -/// let p1: &UnsafeCell = &x; +/// let shared: &UnsafeCell = &x; /// // using `.get()` is okay: /// unsafe { /// // SAFETY: there exist no other references to the contents of `x` -/// let p2: &mut u32 = &mut *p1.get(); +/// let exclusive: &mut u32 = &mut *shared.get(); /// }; /// // using `.raw_get()` is also okay: /// unsafe { /// // SAFETY: there exist no other references to the contents of `x` in this scope -/// let p2: &mut u32 = &mut *UnsafeCell::raw_get(p1 as *const _); +/// let exclusive: &mut u32 = &mut *UnsafeCell::raw_get(shared as *const _); /// }; /// // using `.get_mut()` is always safe: -/// let p2: &mut u32 = x.get_mut(); -/// // but the following is not allowed! -/// // let p2: &mut u32 = unsafe { -/// // let t: *mut u32 = &x as *const _ as *mut u32; -/// // &mut *t -/// // }; +/// let exclusive: &mut u32 = x.get_mut(); +/// +/// // when we have exclusive access, we can convert it to a shared `&UnsafeCell`: +/// unsafe { +/// // SAFETY: `u32` has no niche, therefore it has the same layout as `UnsafeCell` +/// let shared: &UnsafeCell = &*(exclusive as *mut _ as *const UnsafeCell); +/// // SAFETY: there exist no other *active* references to the contents of `x` in this scope +/// let exclusive: &mut u32 = &mut *shared.get(); +/// } /// ``` /// +/// [niche]: https://rust-lang.github.io/unsafe-code-guidelines/glossary.html#niche /// [`.raw_get()`]: `UnsafeCell::raw_get` /// /// # Examples From 75f641799effae2c0138215eca968b33429d23fe Mon Sep 17 00:00:00 2001 From: DropDemBits Date: Sun, 9 Oct 2022 17:58:55 -0400 Subject: [PATCH 062/333] Add `GenericParamList::to_generic_args` --- .../extract_struct_from_enum_variant.rs | 34 ++----------------- crates/syntax/src/ast/edit_in_place.rs | 17 ++++++++++ 2 files changed, 19 insertions(+), 32 deletions(-) diff --git a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index 8d5cab283d06..970e948dfd93 100644 --- a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -9,7 +9,7 @@ use ide_db::{ search::FileReference, FxHashSet, RootDatabase, }; -use itertools::{Itertools, Position}; +use itertools::Itertools; use syntax::{ ast::{ self, edit::IndentLevel, edit_in_place::Indent, make, AstNode, HasAttrs, HasGenericParams, @@ -298,37 +298,7 @@ fn update_variant(variant: &ast::Variant, generics: Option 0) - .map(|generics| { - let mut generic_str = String::with_capacity(8); - - for (p, more) in generics.generic_params().with_position().map(|p| match p { - Position::First(p) | Position::Middle(p) => (p, true), - Position::Last(p) | Position::Only(p) => (p, false), - }) { - match p { - ast::GenericParam::ConstParam(konst) => { - if let Some(name) = konst.name() { - generic_str.push_str(name.text().as_str()); - } - } - ast::GenericParam::LifetimeParam(lt) => { - if let Some(lt) = lt.lifetime() { - generic_str.push_str(lt.text().as_str()); - } - } - ast::GenericParam::TypeParam(ty) => { - if let Some(name) = ty.name() { - generic_str.push_str(name.text().as_str()); - } - } - } - if more { - generic_str.push_str(", "); - } - } - - make::ty(&format!("{}<{}>", &name.text(), &generic_str)) - }) + .map(|generics| make::ty(&format!("{}{}", &name.text(), generics.to_generic_args()))) .unwrap_or_else(|| make::ty(&name.text())); // change from a record to a tuple field list diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs index eadebbe8a212..7d632352828e 100644 --- a/crates/syntax/src/ast/edit_in_place.rs +++ b/crates/syntax/src/ast/edit_in_place.rs @@ -235,6 +235,23 @@ impl ast::GenericParamList { } } } + + /// Extracts the const, type, and lifetime names into a new [`ast::GenericParamList`] + pub fn to_generic_args(&self) -> ast::GenericParamList { + let params = self.generic_params().filter_map(|param| match param { + ast::GenericParam::ConstParam(it) => { + Some(ast::GenericParam::TypeParam(make::type_param(it.name()?, None))) + } + ast::GenericParam::LifetimeParam(it) => { + Some(ast::GenericParam::LifetimeParam(make::lifetime_param(it.lifetime()?))) + } + ast::GenericParam::TypeParam(it) => { + Some(ast::GenericParam::TypeParam(make::type_param(it.name()?, None))) + } + }); + + make::generic_param_list(params) + } } impl ast::WhereClause { From bfe6ec9b778d12287fce1fdf2df7e7b0d58ee775 Mon Sep 17 00:00:00 2001 From: DropDemBits Date: Sun, 9 Oct 2022 18:12:08 -0400 Subject: [PATCH 063/333] Add `{TypeParam, ConstParam}::remove_default` Also includes a drive-by refactor of `utils::generate_impl_text_inner`, since that's what drove this change --- .../ide-assists/src/handlers/generate_impl.rs | 13 ++++ crates/ide-assists/src/utils.rs | 76 +++++++++---------- crates/syntax/src/ast/edit_in_place.rs | 36 +++++++++ 3 files changed, 87 insertions(+), 38 deletions(-) diff --git a/crates/ide-assists/src/handlers/generate_impl.rs b/crates/ide-assists/src/handlers/generate_impl.rs index 68287a20bf80..307cea3d0a4f 100644 --- a/crates/ide-assists/src/handlers/generate_impl.rs +++ b/crates/ide-assists/src/handlers/generate_impl.rs @@ -52,6 +52,7 @@ mod tests { use super::*; + // FIXME: break up into separate test fns #[test] fn test_add_impl() { check_assist( @@ -134,6 +135,18 @@ mod tests { }"#, ); + check_assist( + generate_impl, + r#" + struct Defaulted {}$0"#, + r#" + struct Defaulted {} + + impl Defaulted { + $0 + }"#, + ); + check_assist( generate_impl, r#"pub trait Trait {} diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs index 4ab6e2627fa7..38396cd7d7ba 100644 --- a/crates/ide-assists/src/utils.rs +++ b/crates/ide-assists/src/utils.rs @@ -2,8 +2,6 @@ use std::ops; -use itertools::Itertools; - pub(crate) use gen_trait_fn_body::gen_trait_fn_body; use hir::{db::HirDatabase, HirDisplay, Semantics}; use ide_db::{famous_defs::FamousDefs, path_transform::PathTransform, RootDatabase, SnippetCap}; @@ -15,7 +13,7 @@ use syntax::{ edit_in_place::{AttrsOwnerEdit, Removable}, make, HasArgList, HasAttrs, HasGenericParams, HasName, HasTypeBounds, Whitespace, }, - ted, AstNode, AstToken, Direction, SmolStr, SourceFile, + ted, AstNode, AstToken, Direction, SourceFile, SyntaxKind::*, SyntaxNode, TextRange, TextSize, T, }; @@ -424,34 +422,44 @@ pub(crate) fn generate_trait_impl_text(adt: &ast::Adt, trait_text: &str, code: & } fn generate_impl_text_inner(adt: &ast::Adt, trait_text: Option<&str>, code: &str) -> String { - let generic_params = adt.generic_param_list(); + // Ensure lifetime params are before type & const params + let generic_params = adt.generic_param_list().map(|generic_params| { + let lifetime_params = + generic_params.lifetime_params().map(ast::GenericParam::LifetimeParam); + let ty_or_const_params = generic_params.type_or_const_params().filter_map(|param| { + // remove defaults since they can't be specified in impls + match param { + ast::TypeOrConstParam::Type(param) => { + let param = param.clone_for_update(); + param.remove_default(); + Some(ast::GenericParam::TypeParam(param)) + } + ast::TypeOrConstParam::Const(param) => { + let param = param.clone_for_update(); + param.remove_default(); + Some(ast::GenericParam::ConstParam(param)) + } + } + }); + + make::generic_param_list(itertools::chain(lifetime_params, ty_or_const_params)) + }); + + // FIXME: use syntax::make & mutable AST apis instead + // `trait_text` and `code` can't be opaque blobs of text let mut buf = String::with_capacity(code.len()); + + // Copy any cfg attrs from the original adt buf.push_str("\n\n"); - adt.attrs() - .filter(|attr| attr.as_simple_call().map(|(name, _arg)| name == "cfg").unwrap_or(false)) - .for_each(|attr| buf.push_str(format!("{}\n", attr).as_str())); + let cfg_attrs = adt + .attrs() + .filter(|attr| attr.as_simple_call().map(|(name, _arg)| name == "cfg").unwrap_or(false)); + cfg_attrs.for_each(|attr| buf.push_str(&format!("{attr}\n"))); + + // `impl{generic_params} {trait_text} for {name}{generic_params.to_generic_args()}` buf.push_str("impl"); if let Some(generic_params) = &generic_params { - let lifetimes = generic_params.lifetime_params().map(|lt| format!("{}", lt.syntax())); - let toc_params = generic_params.type_or_const_params().map(|toc_param| { - let type_param = match toc_param { - ast::TypeOrConstParam::Type(x) => x, - ast::TypeOrConstParam::Const(x) => return x.syntax().to_string(), - }; - let mut buf = String::new(); - if let Some(it) = type_param.name() { - format_to!(buf, "{}", it.syntax()); - } - if let Some(it) = type_param.colon_token() { - format_to!(buf, "{} ", it); - } - if let Some(it) = type_param.type_bound_list() { - format_to!(buf, "{}", it.syntax()); - } - buf - }); - let generics = lifetimes.chain(toc_params).format(", "); - format_to!(buf, "<{}>", generics); + format_to!(buf, "{generic_params}"); } buf.push(' '); if let Some(trait_text) = trait_text { @@ -460,23 +468,15 @@ fn generate_impl_text_inner(adt: &ast::Adt, trait_text: Option<&str>, code: &str } buf.push_str(&adt.name().unwrap().text()); if let Some(generic_params) = generic_params { - let lifetime_params = generic_params - .lifetime_params() - .filter_map(|it| it.lifetime()) - .map(|it| SmolStr::from(it.text())); - let toc_params = generic_params - .type_or_const_params() - .filter_map(|it| it.name()) - .map(|it| SmolStr::from(it.text())); - format_to!(buf, "<{}>", lifetime_params.chain(toc_params).format(", ")) + format_to!(buf, "{}", generic_params.to_generic_args()); } match adt.where_clause() { Some(where_clause) => { - format_to!(buf, "\n{}\n{{\n{}\n}}", where_clause, code); + format_to!(buf, "\n{where_clause}\n{{\n{code}\n}}"); } None => { - format_to!(buf, " {{\n{}\n}}", code); + format_to!(buf, " {{\n{code}\n}}"); } } diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs index 7d632352828e..173c064ccc97 100644 --- a/crates/syntax/src/ast/edit_in_place.rs +++ b/crates/syntax/src/ast/edit_in_place.rs @@ -265,6 +265,42 @@ impl ast::WhereClause { } } +impl ast::TypeParam { + pub fn remove_default(&self) { + if let Some((eq, last)) = self + .syntax() + .children_with_tokens() + .find(|it| it.kind() == T![=]) + .zip(self.syntax().last_child_or_token()) + { + ted::remove_all(eq..=last); + + // remove any trailing ws + if let Some(last) = self.syntax().last_token().filter(|it| it.kind() == WHITESPACE) { + last.detach(); + } + } + } +} + +impl ast::ConstParam { + pub fn remove_default(&self) { + if let Some((eq, last)) = self + .syntax() + .children_with_tokens() + .find(|it| it.kind() == T![=]) + .zip(self.syntax().last_child_or_token()) + { + ted::remove_all(eq..=last); + + // remove any trailing ws + if let Some(last) = self.syntax().last_token().filter(|it| it.kind() == WHITESPACE) { + last.detach(); + } + } + } +} + pub trait Removable: AstNode { fn remove(&self); } From 1015a177d4a9f1c8ed853dee68dc2bf13e0cb02d Mon Sep 17 00:00:00 2001 From: DropDemBits Date: Sun, 9 Oct 2022 20:45:20 -0400 Subject: [PATCH 064/333] Have `to_generic_args` return `ast::GenericArgList` --- crates/syntax/src/ast/edit_in_place.rs | 21 +++++++++++---------- crates/syntax/src/ast/make.rs | 23 +++++++++++++++++++---- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs index 173c064ccc97..229e7419b736 100644 --- a/crates/syntax/src/ast/edit_in_place.rs +++ b/crates/syntax/src/ast/edit_in_place.rs @@ -236,21 +236,22 @@ impl ast::GenericParamList { } } - /// Extracts the const, type, and lifetime names into a new [`ast::GenericParamList`] - pub fn to_generic_args(&self) -> ast::GenericParamList { - let params = self.generic_params().filter_map(|param| match param { - ast::GenericParam::ConstParam(it) => { - Some(ast::GenericParam::TypeParam(make::type_param(it.name()?, None))) - } + /// Constructs a matching [`ast::GenericArgList`] + pub fn to_generic_args(&self) -> ast::GenericArgList { + let args = self.generic_params().filter_map(|param| match param { ast::GenericParam::LifetimeParam(it) => { - Some(ast::GenericParam::LifetimeParam(make::lifetime_param(it.lifetime()?))) + Some(ast::GenericArg::LifetimeArg(make::lifetime_arg(it.lifetime()?))) } ast::GenericParam::TypeParam(it) => { - Some(ast::GenericParam::TypeParam(make::type_param(it.name()?, None))) + Some(ast::GenericArg::TypeArg(make::type_arg(make::ext::ty_name(it.name()?)))) + } + ast::GenericParam::ConstParam(it) => { + // Name-only const params get parsed as `TypeArg`s + Some(ast::GenericArg::TypeArg(make::type_arg(make::ext::ty_name(it.name()?)))) } }); - make::generic_param_list(params) + make::generic_arg_list(args) } } @@ -317,7 +318,7 @@ impl Removable for ast::TypeBoundList { impl ast::PathSegment { pub fn get_or_create_generic_arg_list(&self) -> ast::GenericArgList { if self.generic_arg_list().is_none() { - let arg_list = make::generic_arg_list().clone_for_update(); + let arg_list = make::generic_arg_list(empty()).clone_for_update(); ted::append_child(self.syntax(), arg_list.syntax()); } self.generic_arg_list().unwrap() diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 83f8bbac5880..c9a21e12c085 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -88,6 +88,9 @@ pub mod ext { block_expr(None, None) } + pub fn ty_name(name: ast::Name) -> ast::Type { + ty_path(ident_path(&format!("{name}"))) + } pub fn ty_bool() -> ast::Type { ty_path(ident_path("bool")) } @@ -160,6 +163,7 @@ pub fn assoc_item_list() -> ast::AssocItemList { ast_from_text("impl C for D {}") } +// FIXME: `ty_params` should be `ast::GenericArgList` pub fn impl_( ty: ast::Path, params: Option, @@ -185,10 +189,6 @@ pub fn impl_trait( ast_from_text(&format!("impl{ty_params} {trait_} for {ty}{ty_params} {{}}")) } -pub(crate) fn generic_arg_list() -> ast::GenericArgList { - ast_from_text("const S: T<> = ();") -} - pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment { ast_from_text(&format!("type __ = {name_ref};")) } @@ -718,6 +718,21 @@ pub fn generic_param_list( ast_from_text(&format!("fn f<{args}>() {{ }}")) } +pub fn type_arg(ty: ast::Type) -> ast::TypeArg { + ast_from_text(&format!("const S: T<{ty}> = ();")) +} + +pub fn lifetime_arg(lifetime: ast::Lifetime) -> ast::LifetimeArg { + ast_from_text(&format!("const S: T<{lifetime}> = ();")) +} + +pub(crate) fn generic_arg_list( + args: impl IntoIterator, +) -> ast::GenericArgList { + let args = args.into_iter().join(", "); + ast_from_text(&format!("const S: T<{args}> = ();")) +} + pub fn visibility_pub_crate() -> ast::Visibility { ast_from_text("pub(crate) struct S") } From a69cccfc0e40095f3f9f9b73eeeb4ac913ee3796 Mon Sep 17 00:00:00 2001 From: DropDemBits Date: Sun, 9 Oct 2022 21:28:21 -0400 Subject: [PATCH 065/333] Underline only the intra-doc link instead of the whole doc comment --- crates/ide/src/goto_definition.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index f86ea61d1586..d0be1b3f4047 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -48,10 +48,14 @@ pub(crate) fn goto_definition( _ => 1, })?; if let Some(doc_comment) = token_as_doc_comment(&original_token) { - return doc_comment.get_definition_with_descend_at(sema, position.offset, |def, _, _| { - let nav = def.try_to_nav(db)?; - Some(RangeInfo::new(original_token.text_range(), vec![nav])) - }); + return doc_comment.get_definition_with_descend_at( + sema, + position.offset, + |def, _, link_range| { + let nav = def.try_to_nav(db)?; + Some(RangeInfo::new(link_range, vec![nav])) + }, + ); } let navs = sema .descend_into_macros(original_token.clone()) From 63ed71bd30fab43630547b97e25ebf73a51ace6e Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 10 Oct 2022 09:47:09 +0200 Subject: [PATCH 066/333] Honor cfg attributes on params when lowering their patterns --- crates/hir-def/src/body.rs | 18 ++++++++++++++++-- crates/hir-def/src/body/lower.rs | 16 +++++++++++----- crates/hir-ty/src/tests/patterns.rs | 10 ++++++++++ crates/ide-completion/src/context.rs | 2 +- 4 files changed, 38 insertions(+), 8 deletions(-) diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs index 2dc7714bbb54..759f3b8c04b6 100644 --- a/crates/hir-def/src/body.rs +++ b/crates/hir-def/src/body.rs @@ -311,7 +311,20 @@ impl Body { DefWithBodyId::FunctionId(f) => { let f = f.lookup(db); let src = f.source(db); - params = src.value.param_list(); + params = src.value.param_list().map(|param_list| { + let item_tree = f.id.item_tree(db); + let func = &item_tree[f.id.value]; + let krate = f.container.module(db).krate; + let crate_graph = db.crate_graph(); + ( + param_list, + func.params.clone().map(move |param| { + item_tree + .attrs(db, krate, param.into()) + .is_cfg_enabled(&crate_graph[krate].cfg_options) + }), + ) + }); (src.file_id, f.module(db), src.value.body().map(ast::Expr::from)) } DefWithBodyId::ConstId(c) => { @@ -334,6 +347,7 @@ impl Body { let expander = Expander::new(db, file_id, module); let (mut body, source_map) = Body::new(db, expander, params, body); body.shrink_to_fit(); + (Arc::new(body), Arc::new(source_map)) } @@ -370,7 +384,7 @@ impl Body { fn new( db: &dyn DefDatabase, expander: Expander, - params: Option, + params: Option<(ast::ParamList, impl Iterator)>, body: Option, ) -> (Body, BodySourceMap) { lower::lower(db, expander, params, body) diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index c4f91e49a6e1..ccc01c3efca5 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -77,7 +77,7 @@ impl<'a> LowerCtx<'a> { pub(super) fn lower( db: &dyn DefDatabase, expander: Expander, - params: Option, + params: Option<(ast::ParamList, impl Iterator)>, body: Option, ) -> (Body, BodySourceMap) { ExprCollector { @@ -119,11 +119,13 @@ struct ExprCollector<'a> { impl ExprCollector<'_> { fn collect( mut self, - param_list: Option, + param_list: Option<(ast::ParamList, impl Iterator)>, body: Option, ) -> (Body, BodySourceMap) { - if let Some(param_list) = param_list { - if let Some(self_param) = param_list.self_param() { + if let Some((param_list, mut attr_enabled)) = param_list { + if let Some(self_param) = + param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false)) + { let ptr = AstPtr::new(&self_param); let param_pat = self.alloc_pat( Pat::Bind { @@ -139,7 +141,11 @@ impl ExprCollector<'_> { self.body.params.push(param_pat); } - for pat in param_list.params().filter_map(|param| param.pat()) { + for pat in param_list + .params() + .zip(attr_enabled) + .filter_map(|(param, enabled)| param.pat().filter(|_| enabled)) + { let param_pat = self.collect_pat(pat); self.body.params.push(param_pat); } diff --git a/crates/hir-ty/src/tests/patterns.rs b/crates/hir-ty/src/tests/patterns.rs index eb04bf87783b..74de33117ee7 100644 --- a/crates/hir-ty/src/tests/patterns.rs +++ b/crates/hir-ty/src/tests/patterns.rs @@ -1070,3 +1070,13 @@ fn main() { "#, ); } + +#[test] +fn cfg_params() { + check_types( + r#" +fn my_fn(#[cfg(feature = "feature")] u8: u8, u32: u32) {} + //^^^ u32 +"#, + ); +} diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index a5e854b74df9..f0563e290db4 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -1,4 +1,4 @@ -//! See `CompletionContext` structure. +//! See [`CompletionContext`] structure. mod analysis; #[cfg(test)] From 1574fe0d5438f63ddb8c59d7837d5acf3fd77c94 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 1 Oct 2022 22:51:09 +0200 Subject: [PATCH 067/333] Use $crate instead of std for panic builtin_fn_macro This should be closer to the expected output and gets rid of a few type mismatches in rustc/library --- .../macro_expansion_tests/builtin_fn_macro.rs | 24 +++++++++---------- crates/hir-expand/src/builtin_fn_macro.rs | 14 +++++------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs index 4f626105a53d..c04cd1651926 100644 --- a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs +++ b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs @@ -93,12 +93,12 @@ macro_rules! option_env {() => {}} fn main() { option_env!("TEST_ENV_VAR"); } "#, - expect![[r##" + expect![[r#" #[rustc_builtin_macro] macro_rules! option_env {() => {}} -fn main() { std::option::Option::None:: < &str>; } -"##]], +fn main() { $crate::option::Option::None:: < &str>; } +"#]], ); } @@ -191,7 +191,7 @@ fn main() { format_args!("{} {:?}", arg1(a, b, c), arg2); } "#, - expect![[r##" + expect![[r#" #[rustc_builtin_macro] macro_rules! format_args { ($fmt:expr) => ({ /* compiler built-in */ }); @@ -199,9 +199,9 @@ macro_rules! format_args { } fn main() { - std::fmt::Arguments::new_v1(&[], &[std::fmt::ArgumentV1::new(&(arg1(a, b, c)), std::fmt::Display::fmt), std::fmt::ArgumentV1::new(&(arg2), std::fmt::Display::fmt), ]); + $crate::fmt::Arguments::new_v1(&[], &[$crate::fmt::ArgumentV1::new(&(arg1(a, b, c)), $crate::fmt::Display::fmt), $crate::fmt::ArgumentV1::new(&(arg2), $crate::fmt::Display::fmt), ]); } -"##]], +"#]], ); } @@ -219,7 +219,7 @@ fn main() { format_args!("{} {:?}", a::(), b); } "#, - expect![[r##" + expect![[r#" #[rustc_builtin_macro] macro_rules! format_args { ($fmt:expr) => ({ /* compiler built-in */ }); @@ -227,9 +227,9 @@ macro_rules! format_args { } fn main() { - std::fmt::Arguments::new_v1(&[], &[std::fmt::ArgumentV1::new(&(a::()), std::fmt::Display::fmt), std::fmt::ArgumentV1::new(&(b), std::fmt::Display::fmt), ]); + $crate::fmt::Arguments::new_v1(&[], &[$crate::fmt::ArgumentV1::new(&(a::()), $crate::fmt::Display::fmt), $crate::fmt::ArgumentV1::new(&(b), $crate::fmt::Display::fmt), ]); } -"##]], +"#]], ); } @@ -248,7 +248,7 @@ fn main() { format_args!/*+errors*/("{} {:?}", a.); } "#, - expect![[r##" + expect![[r#" #[rustc_builtin_macro] macro_rules! format_args { ($fmt:expr) => ({ /* compiler built-in */ }); @@ -258,9 +258,9 @@ macro_rules! format_args { fn main() { let _ = /* parse error: expected field name or number */ -std::fmt::Arguments::new_v1(&[], &[std::fmt::ArgumentV1::new(&(a.), std::fmt::Display::fmt), ]); +$crate::fmt::Arguments::new_v1(&[], &[$crate::fmt::ArgumentV1::new(&(a.), $crate::fmt::Display::fmt), ]); } -"##]], +"#]], ); } diff --git a/crates/hir-expand/src/builtin_fn_macro.rs b/crates/hir-expand/src/builtin_fn_macro.rs index 8befa7f7da72..7b19518e25a8 100644 --- a/crates/hir-expand/src/builtin_fn_macro.rs +++ b/crates/hir-expand/src/builtin_fn_macro.rs @@ -238,9 +238,9 @@ fn format_args_expand( ) -> ExpandResult { // We expand `format_args!("", a1, a2)` to // ``` - // std::fmt::Arguments::new_v1(&[], &[ - // std::fmt::ArgumentV1::new(&arg1,std::fmt::Display::fmt), - // std::fmt::ArgumentV1::new(&arg2,std::fmt::Display::fmt), + // $crate::fmt::Arguments::new_v1(&[], &[ + // $crate::fmt::ArgumentV1::new(&arg1,$crate::fmt::Display::fmt), + // $crate::fmt::ArgumentV1::new(&arg2,$crate::fmt::Display::fmt), // ]) // ```, // which is still not really correct, but close enough for now @@ -262,10 +262,10 @@ fn format_args_expand( } let _format_string = args.remove(0); let arg_tts = args.into_iter().flat_map(|arg| { - quote! { std::fmt::ArgumentV1::new(&(#arg), std::fmt::Display::fmt), } + quote! { #DOLLAR_CRATE::fmt::ArgumentV1::new(&(#arg), #DOLLAR_CRATE::fmt::Display::fmt), } }.token_trees); let expanded = quote! { - std::fmt::Arguments::new_v1(&[], &[##arg_tts]) + #DOLLAR_CRATE::fmt::Arguments::new_v1(&[], &[##arg_tts]) }; ExpandResult::ok(expanded) } @@ -675,8 +675,8 @@ fn option_env_expand( }; let expanded = match get_env_inner(db, arg_id, &key) { - None => quote! { std::option::Option::None::<&str> }, - Some(s) => quote! { std::option::Some(#s) }, + None => quote! { #DOLLAR_CRATE::option::Option::None::<&str> }, + Some(s) => quote! { #DOLLAR_CRATE::option::Some(#s) }, }; ExpandResult::ok(ExpandedEager::new(expanded)) From 78f33c0e960a260d02d0f005350b8378d3003c0d Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 10 Oct 2022 14:25:14 +0200 Subject: [PATCH 068/333] Expand unmatched mbe fragments to reasonable default token trees Currently we expand unmatched fragments by not replacing them at all, leaving us with `$ident`. This trips up the parser or subsequent macro calls. Instead it makes more sense to replace these with some reasonable default depending on the fragment kind which should make more recursive macro calls work better for completions. --- crates/mbe/src/benchmark.rs | 30 +++++++------- crates/mbe/src/expander.rs | 3 +- crates/mbe/src/expander/matcher.rs | 54 +++++++++++++++--------- crates/mbe/src/expander/transcriber.rs | 57 ++++++++++++++++++++++++-- crates/mbe/src/lib.rs | 6 +-- crates/mbe/src/parser.rs | 41 ++++++++++++++++-- 6 files changed, 146 insertions(+), 45 deletions(-) diff --git a/crates/mbe/src/benchmark.rs b/crates/mbe/src/benchmark.rs index ac691578d887..9c92bae6a196 100644 --- a/crates/mbe/src/benchmark.rs +++ b/crates/mbe/src/benchmark.rs @@ -8,7 +8,7 @@ use syntax::{ use test_utils::{bench, bench_fixture, skip_slow_tests}; use crate::{ - parser::{Op, RepeatKind, Separator}, + parser::{MetaVarKind, Op, RepeatKind, Separator}, syntax_node_to_token_tree, DeclarativeMacro, }; @@ -111,35 +111,35 @@ fn invocation_fixtures(rules: &FxHashMap) -> Vec<(Stri fn collect_from_op(op: &Op, parent: &mut tt::Subtree, seed: &mut usize) { return match op { - Op::Var { kind, .. } => match kind.as_ref().map(|it| it.as_str()) { - Some("ident") => parent.token_trees.push(make_ident("foo")), - Some("ty") => parent.token_trees.push(make_ident("Foo")), - Some("tt") => parent.token_trees.push(make_ident("foo")), - Some("vis") => parent.token_trees.push(make_ident("pub")), - Some("pat") => parent.token_trees.push(make_ident("foo")), - Some("path") => parent.token_trees.push(make_ident("foo")), - Some("literal") => parent.token_trees.push(make_literal("1")), - Some("expr") => parent.token_trees.push(make_ident("foo")), - Some("lifetime") => { + Op::Var { kind, .. } => match kind.as_ref() { + Some(MetaVarKind::Ident) => parent.token_trees.push(make_ident("foo")), + Some(MetaVarKind::Ty) => parent.token_trees.push(make_ident("Foo")), + Some(MetaVarKind::Tt) => parent.token_trees.push(make_ident("foo")), + Some(MetaVarKind::Vis) => parent.token_trees.push(make_ident("pub")), + Some(MetaVarKind::Pat) => parent.token_trees.push(make_ident("foo")), + Some(MetaVarKind::Path) => parent.token_trees.push(make_ident("foo")), + Some(MetaVarKind::Literal) => parent.token_trees.push(make_literal("1")), + Some(MetaVarKind::Expr) => parent.token_trees.push(make_ident("foo")), + Some(MetaVarKind::Lifetime) => { parent.token_trees.push(make_punct('\'')); parent.token_trees.push(make_ident("a")); } - Some("block") => { + Some(MetaVarKind::Block) => { parent.token_trees.push(make_subtree(tt::DelimiterKind::Brace, None)) } - Some("item") => { + Some(MetaVarKind::Item) => { parent.token_trees.push(make_ident("fn")); parent.token_trees.push(make_ident("foo")); parent.token_trees.push(make_subtree(tt::DelimiterKind::Parenthesis, None)); parent.token_trees.push(make_subtree(tt::DelimiterKind::Brace, None)); } - Some("meta") => { + Some(MetaVarKind::Meta) => { parent.token_trees.push(make_ident("foo")); parent.token_trees.push(make_subtree(tt::DelimiterKind::Parenthesis, None)); } None => (), - Some(kind) => panic!("Unhandled kind {}", kind), + Some(kind) => panic!("Unhandled kind {:?}", kind), }, Op::Leaf(leaf) => parent.token_trees.push(leaf.clone().into()), Op::Repeat { tokens, kind, separator } => { diff --git a/crates/mbe/src/expander.rs b/crates/mbe/src/expander.rs index 1e1bfa550558..100ec6bfb93a 100644 --- a/crates/mbe/src/expander.rs +++ b/crates/mbe/src/expander.rs @@ -8,7 +8,7 @@ mod transcriber; use rustc_hash::FxHashMap; use syntax::SmolStr; -use crate::{ExpandError, ExpandResult}; +use crate::{parser::MetaVarKind, ExpandError, ExpandResult}; pub(crate) fn expand_rules( rules: &[crate::Rule], @@ -104,6 +104,7 @@ enum Binding { Fragment(Fragment), Nested(Vec), Empty, + Missing(MetaVarKind), } #[derive(Debug, Clone, PartialEq, Eq)] diff --git a/crates/mbe/src/expander/matcher.rs b/crates/mbe/src/expander/matcher.rs index 139a8cb8cbe5..3f656df25f7d 100644 --- a/crates/mbe/src/expander/matcher.rs +++ b/crates/mbe/src/expander/matcher.rs @@ -66,7 +66,7 @@ use syntax::SmolStr; use crate::{ expander::{Binding, Bindings, ExpandResult, Fragment}, - parser::{Op, RepeatKind, Separator}, + parser::{MetaVarKind, Op, RepeatKind, Separator}, tt_iter::TtIter, ExpandError, MetaTemplate, }; @@ -119,6 +119,7 @@ pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree) -> Match { .map(|it| match it { Binding::Fragment(_) => 1, Binding::Empty => 1, + Binding::Missing(_) => 1, Binding::Nested(it) => count(it.iter()), }) .sum() @@ -130,6 +131,7 @@ enum BindingKind { Empty(SmolStr), Optional(SmolStr), Fragment(SmolStr, Fragment), + Missing(SmolStr, MetaVarKind), Nested(usize, usize), } @@ -190,6 +192,10 @@ impl BindingsBuilder { .push(LinkNode::Node(Rc::new(BindingKind::Fragment(var.clone(), fragment)))); } + fn push_missing(&mut self, idx: &mut BindingsIdx, var: &SmolStr, kind: MetaVarKind) { + self.nodes[idx.0].push(LinkNode::Node(Rc::new(BindingKind::Missing(var.clone(), kind)))); + } + fn push_nested(&mut self, parent: &mut BindingsIdx, child: &BindingsIdx) { let BindingsIdx(idx, nidx) = self.copy(child); self.nodes[parent.0].push(LinkNode::Node(Rc::new(BindingKind::Nested(idx, nidx)))); @@ -222,6 +228,9 @@ impl BindingsBuilder { BindingKind::Fragment(name, fragment) => { bindings.inner.insert(name.clone(), Binding::Fragment(fragment.clone())); } + BindingKind::Missing(name, kind) => { + bindings.inner.insert(name.clone(), Binding::Missing(*kind)); + } BindingKind::Nested(idx, nested_idx) => { let mut nested_nodes = Vec::new(); self.collect_nested(*idx, *nested_idx, &mut nested_nodes); @@ -458,9 +467,9 @@ fn match_loop_inner<'t>( } } OpDelimited::Op(Op::Var { kind, name, .. }) => { - if let Some(kind) = kind { + if let &Some(kind) = kind { let mut fork = src.clone(); - let match_res = match_meta_var(kind.as_str(), &mut fork); + let match_res = match_meta_var(kind, &mut fork); match match_res.err { None => { // Some meta variables are optional (e.g. vis) @@ -475,8 +484,15 @@ fn match_loop_inner<'t>( } Some(err) => { res.add_err(err); - if let Some(fragment) = match_res.value { - bindings_builder.push_fragment(&mut item.bindings, name, fragment); + match match_res.value { + Some(fragment) => bindings_builder.push_fragment( + &mut item.bindings, + name, + fragment, + ), + None => { + bindings_builder.push_missing(&mut item.bindings, name, kind) + } } item.is_error = true; error_items.push(item); @@ -668,20 +684,20 @@ fn match_leaf(lhs: &tt::Leaf, src: &mut TtIter<'_>) -> Result<(), ExpandError> { } } -fn match_meta_var(kind: &str, input: &mut TtIter<'_>) -> ExpandResult> { +fn match_meta_var(kind: MetaVarKind, input: &mut TtIter<'_>) -> ExpandResult> { let fragment = match kind { - "path" => parser::PrefixEntryPoint::Path, - "ty" => parser::PrefixEntryPoint::Ty, + MetaVarKind::Path => parser::PrefixEntryPoint::Path, + MetaVarKind::Ty => parser::PrefixEntryPoint::Ty, // FIXME: These two should actually behave differently depending on the edition. // // https://doc.rust-lang.org/edition-guide/rust-2021/or-patterns-macro-rules.html - "pat" | "pat_param" => parser::PrefixEntryPoint::Pat, - "stmt" => parser::PrefixEntryPoint::Stmt, - "block" => parser::PrefixEntryPoint::Block, - "meta" => parser::PrefixEntryPoint::MetaItem, - "item" => parser::PrefixEntryPoint::Item, - "vis" => parser::PrefixEntryPoint::Vis, - "expr" => { + MetaVarKind::Pat | MetaVarKind::PatParam => parser::PrefixEntryPoint::Pat, + MetaVarKind::Stmt => parser::PrefixEntryPoint::Stmt, + MetaVarKind::Block => parser::PrefixEntryPoint::Block, + MetaVarKind::Meta => parser::PrefixEntryPoint::MetaItem, + MetaVarKind::Item => parser::PrefixEntryPoint::Item, + MetaVarKind::Vis => parser::PrefixEntryPoint::Vis, + MetaVarKind::Expr => { // `expr` should not match underscores. // HACK: Macro expansion should not be done using "rollback and try another alternative". // rustc [explicitly checks the next token][0]. @@ -698,17 +714,17 @@ fn match_meta_var(kind: &str, input: &mut TtIter<'_>) -> ExpandResult { let tt_result = match kind { - "ident" => input + MetaVarKind::Ident => input .expect_ident() .map(|ident| tt::Leaf::from(ident.clone()).into()) .map_err(|()| ExpandError::binding_error("expected ident")), - "tt" => input + MetaVarKind::Tt => input .expect_tt() .map_err(|()| ExpandError::binding_error("expected token tree")), - "lifetime" => input + MetaVarKind::Lifetime => input .expect_lifetime() .map_err(|()| ExpandError::binding_error("expected lifetime")), - "literal" => { + MetaVarKind::Literal => { let neg = input.eat_char('-'); input .expect_literal() diff --git a/crates/mbe/src/expander/transcriber.rs b/crates/mbe/src/expander/transcriber.rs index 7bcc84740f18..cbb59ab8e67b 100644 --- a/crates/mbe/src/expander/transcriber.rs +++ b/crates/mbe/src/expander/transcriber.rs @@ -6,7 +6,7 @@ use tt::{Delimiter, Subtree}; use crate::{ expander::{Binding, Bindings, Fragment}, - parser::{Op, RepeatKind, Separator}, + parser::{MetaVarKind, Op, RepeatKind, Separator}, ExpandError, ExpandResult, MetaTemplate, }; @@ -15,7 +15,7 @@ impl Bindings { self.inner.contains_key(name) } - fn get(&self, name: &str, nesting: &mut [NestingState]) -> Result<&Fragment, ExpandError> { + fn get(&self, name: &str, nesting: &mut [NestingState]) -> Result { macro_rules! binding_err { ($($arg:tt)*) => { ExpandError::binding_error(format!($($arg)*)) }; } @@ -26,6 +26,7 @@ impl Bindings { nesting_state.hit = true; b = match b { Binding::Fragment(_) => break, + Binding::Missing(_) => break, Binding::Nested(bs) => bs.get(nesting_state.idx).ok_or_else(|| { nesting_state.at_end = true; binding_err!("could not find nested binding `{name}`") @@ -37,7 +38,55 @@ impl Bindings { }; } match b { - Binding::Fragment(it) => Ok(it), + Binding::Fragment(it) => Ok(it.clone()), + // emit some reasonable default expansion for missing bindings, + // this gives better recovery than emitting the `$fragment-name` verbatim + Binding::Missing(it) => Ok(match it { + MetaVarKind::Stmt => { + Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { + id: tt::TokenId::unspecified(), + char: ';', + spacing: tt::Spacing::Alone, + }))) + } + MetaVarKind::Block => Fragment::Tokens(tt::TokenTree::Subtree(tt::Subtree { + delimiter: Some(tt::Delimiter { + id: tt::TokenId::unspecified(), + kind: tt::DelimiterKind::Brace, + }), + token_trees: vec![], + })), + // FIXME: Meta and Item should get proper defaults + MetaVarKind::Meta | MetaVarKind::Item | MetaVarKind::Tt | MetaVarKind::Vis => { + Fragment::Tokens(tt::TokenTree::Subtree(tt::Subtree { + delimiter: None, + token_trees: vec![], + })) + } + MetaVarKind::Path + | MetaVarKind::Ty + | MetaVarKind::Pat + | MetaVarKind::PatParam + | MetaVarKind::Expr + | MetaVarKind::Ident => { + Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { + text: SmolStr::new_inline("missing"), + id: tt::TokenId::unspecified(), + }))) + } + MetaVarKind::Lifetime => { + Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { + text: SmolStr::new_inline("'missing"), + id: tt::TokenId::unspecified(), + }))) + } + MetaVarKind::Literal => { + Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { + text: SmolStr::new_inline("\"missing\""), + id: tt::TokenId::unspecified(), + }))) + } + }), Binding::Nested(_) => { Err(binding_err!("expected simple binding, found nested binding `{name}`")) } @@ -157,7 +206,7 @@ fn expand_var(ctx: &mut ExpandCtx<'_>, v: &SmolStr, id: tt::TokenId) -> ExpandRe } else { ctx.bindings.get(v, &mut ctx.nesting).map_or_else( |e| ExpandResult { value: Fragment::Tokens(tt::TokenTree::empty()), err: Some(e) }, - |b| ExpandResult::ok(b.clone()), + |it| ExpandResult::ok(it), ) } } diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs index 79da84f4a022..c4f0fa20d6de 100644 --- a/crates/mbe/src/lib.rs +++ b/crates/mbe/src/lib.rs @@ -21,7 +21,7 @@ mod token_map; use std::fmt; use crate::{ - parser::{MetaTemplate, Op}, + parser::{MetaTemplate, MetaVarKind, Op}, tt_iter::TtIter, }; @@ -291,9 +291,9 @@ fn validate(pattern: &MetaTemplate) -> Result<(), ParseError> { // Checks that no repetition which could match an empty token // https://github.com/rust-lang/rust/blob/a58b1ed44f5e06976de2bdc4d7dc81c36a96934f/src/librustc_expand/mbe/macro_rules.rs#L558 let lsh_is_empty_seq = separator.is_none() && subtree.iter().all(|child_op| { - match child_op { + match *child_op { // vis is optional - Op::Var { kind: Some(kind), .. } => kind == "vis", + Op::Var { kind: Some(kind), .. } => kind == MetaVarKind::Vis, Op::Repeat { kind: parser::RepeatKind::ZeroOrMore | parser::RepeatKind::ZeroOrOne, .. diff --git a/crates/mbe/src/parser.rs b/crates/mbe/src/parser.rs index acb4be5846de..351c359b73c8 100644 --- a/crates/mbe/src/parser.rs +++ b/crates/mbe/src/parser.rs @@ -50,7 +50,7 @@ impl MetaTemplate { #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) enum Op { - Var { name: SmolStr, kind: Option, id: tt::TokenId }, + Var { name: SmolStr, kind: Option, id: tt::TokenId }, Ignore { name: SmolStr, id: tt::TokenId }, Index { depth: u32 }, Repeat { tokens: MetaTemplate, kind: RepeatKind, separator: Option }, @@ -65,6 +65,24 @@ pub(crate) enum RepeatKind { ZeroOrOne, } +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub(crate) enum MetaVarKind { + Path, + Ty, + Pat, + PatParam, + Stmt, + Block, + Meta, + Item, + Vis, + Expr, + Ident, + Tt, + Lifetime, + Literal, +} + #[derive(Clone, Debug, Eq)] pub(crate) enum Separator { Literal(tt::Literal), @@ -179,13 +197,30 @@ fn next_op<'a>(first: &tt::TokenTree, src: &mut TtIter<'a>, mode: Mode) -> Resul Ok(res) } -fn eat_fragment_kind(src: &mut TtIter<'_>, mode: Mode) -> Result, ParseError> { +fn eat_fragment_kind(src: &mut TtIter<'_>, mode: Mode) -> Result, ParseError> { if let Mode::Pattern = mode { src.expect_char(':').map_err(|()| ParseError::unexpected("missing fragment specifier"))?; let ident = src .expect_ident() .map_err(|()| ParseError::unexpected("missing fragment specifier"))?; - return Ok(Some(ident.text.clone())); + let kind = match ident.text.as_str() { + "path" => MetaVarKind::Path, + "ty" => MetaVarKind::Ty, + "pat" => MetaVarKind::Pat, + "pat_param" => MetaVarKind::PatParam, + "stmt" => MetaVarKind::Stmt, + "block" => MetaVarKind::Block, + "meta" => MetaVarKind::Meta, + "item" => MetaVarKind::Item, + "vis" => MetaVarKind::Vis, + "expr" => MetaVarKind::Expr, + "ident" => MetaVarKind::Ident, + "tt" => MetaVarKind::Tt, + "lifetime" => MetaVarKind::Lifetime, + "literal" => MetaVarKind::Literal, + _ => return Ok(None), + }; + return Ok(Some(kind)); }; Ok(None) } From febbf71219001cd7948c3d1e5ffa68706896d955 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 3 Oct 2022 15:34:00 +0100 Subject: [PATCH 069/333] macros: tidy up lint changes Small tweaks to changes made in a previous PR, unrelated to eager translation. Signed-off-by: David Wood --- compiler/rustc_macros/src/diagnostics/diagnostic.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index 83040a652b18..406a56cd4ae3 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -105,8 +105,8 @@ impl<'a> LintDiagnosticDerive<'a> { }); let msg = builder.each_variant(&mut structure, |mut builder, variant| { - // HACK(wafflelapkin): initialize slug (???) - let _preamble = builder.preamble(&variant); + // Collect the slug by generating the preamble. + let _ = builder.preamble(&variant); match builder.slug.value_ref() { None => { @@ -125,7 +125,10 @@ impl<'a> LintDiagnosticDerive<'a> { let diag = &builder.diag; structure.gen_impl(quote! { gen impl<'__a> rustc_errors::DecorateLint<'__a, ()> for @Self { - fn decorate_lint<'__b>(self, #diag: &'__b mut rustc_errors::DiagnosticBuilder<'__a, ()>) -> &'__b mut rustc_errors::DiagnosticBuilder<'__a, ()> { + fn decorate_lint<'__b>( + self, + #diag: &'__b mut rustc_errors::DiagnosticBuilder<'__a, ()> + ) -> &'__b mut rustc_errors::DiagnosticBuilder<'__a, ()> { use rustc_errors::IntoDiagnosticArg; #implementation } From 508d7e6d26bd636081c96ee6ed61f833917343ea Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 3 Oct 2022 14:02:49 +0100 Subject: [PATCH 070/333] errors: use `HashMap` to store diagnostic args Eager translation will enable subdiagnostics to be translated multiple times with different arguments - this requires the ability to replace the value of one argument with a new value, which is better suited to a `HashMap` than the previous storage, a `Vec`. Signed-off-by: David Wood --- compiler/rustc_codegen_ssa/src/back/write.rs | 7 +++-- .../src/annotate_snippet_emitter_writer.rs | 4 +-- compiler/rustc_errors/src/diagnostic.rs | 19 ++++++++---- compiler/rustc_errors/src/emitter.rs | 4 +-- compiler/rustc_errors/src/json.rs | 4 +-- compiler/rustc_errors/src/translation.rs | 30 +++++++++++++------ .../passes/check_code_block_syntax.rs | 7 +++-- 7 files changed, 49 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 6188094bbbdd..e3534544b681 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -15,7 +15,10 @@ use rustc_data_structures::profiling::TimingGuard; use rustc_data_structures::profiling::VerboseTimingGuard; use rustc_data_structures::sync::Lrc; use rustc_errors::emitter::Emitter; -use rustc_errors::{translation::Translate, DiagnosticId, FatalError, Handler, Level}; +use rustc_errors::{ + translation::{to_fluent_args, Translate}, + DiagnosticId, FatalError, Handler, Level, +}; use rustc_fs_util::link_or_copy; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_incremental::{ @@ -1750,7 +1753,7 @@ impl Translate for SharedEmitter { impl Emitter for SharedEmitter { fn emit_diagnostic(&mut self, diag: &rustc_errors::Diagnostic) { - let fluent_args = self.to_fluent_args(diag.args()); + let fluent_args = to_fluent_args(diag.args()); drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic { msg: self.translate_messages(&diag.message, &fluent_args).to_string(), code: diag.code.clone(), diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index b32fc3c719bb..f14b8ee3254f 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -7,7 +7,7 @@ use crate::emitter::FileWithAnnotatedLines; use crate::snippet::Line; -use crate::translation::Translate; +use crate::translation::{to_fluent_args, Translate}; use crate::{ CodeSuggestion, Diagnostic, DiagnosticId, DiagnosticMessage, Emitter, FluentBundle, LazyFallbackBundle, Level, MultiSpan, Style, SubDiagnostic, @@ -46,7 +46,7 @@ impl Translate for AnnotateSnippetEmitterWriter { impl Emitter for AnnotateSnippetEmitterWriter { /// The entry point for the diagnostics generation fn emit_diagnostic(&mut self, diag: &Diagnostic) { - let fluent_args = self.to_fluent_args(diag.args()); + let fluent_args = to_fluent_args(diag.args()); let mut children = diag.children.clone(); let (mut primary_span, suggestions) = self.primary_span_formatted(&diag, &fluent_args); diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 31e410aaaf08..a267bf6c0b4d 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -27,7 +27,11 @@ pub struct SuggestionsDisabled; /// Simplified version of `FluentArg` that can implement `Encodable` and `Decodable`. Collection of /// `DiagnosticArg` are converted to `FluentArgs` (consuming the collection) at the start of /// diagnostic emission. -pub type DiagnosticArg<'source> = (Cow<'source, str>, DiagnosticArgValue<'source>); +pub type DiagnosticArg<'iter, 'source> = + (&'iter DiagnosticArgName<'source>, &'iter DiagnosticArgValue<'source>); + +/// Name of a diagnostic argument. +pub type DiagnosticArgName<'source> = Cow<'source, str>; /// Simplified version of `FluentValue` that can implement `Encodable` and `Decodable`. Converted /// to a `FluentValue` by the emitter to be used in diagnostic translation. @@ -229,7 +233,7 @@ pub struct Diagnostic { pub span: MultiSpan, pub children: Vec, pub suggestions: Result, SuggestionsDisabled>, - args: Vec>, + args: FxHashMap, DiagnosticArgValue<'static>>, /// This is not used for highlighting or rendering any error message. Rather, it can be used /// as a sort key to sort a buffer of diagnostics. By default, it is the primary span of @@ -321,7 +325,7 @@ impl Diagnostic { span: MultiSpan::new(), children: vec![], suggestions: Ok(vec![]), - args: vec![], + args: Default::default(), sort_span: DUMMY_SP, is_lint: false, } @@ -956,8 +960,11 @@ impl Diagnostic { self } - pub fn args(&self) -> &[DiagnosticArg<'static>] { - &self.args + // Exact iteration order of diagnostic arguments shouldn't make a difference to output because + // they're only used in interpolation. + #[allow(rustc::potential_query_instability)] + pub fn args<'a>(&'a self) -> impl Iterator> { + self.args.iter() } pub fn set_arg( @@ -965,7 +972,7 @@ impl Diagnostic { name: impl Into>, arg: impl IntoDiagnosticArg, ) -> &mut Self { - self.args.push((name.into(), arg.into_diagnostic_arg())); + self.args.insert(name.into(), arg.into_diagnostic_arg()); self } diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 66fbb8f1213e..cd6413bc3ec6 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -14,7 +14,7 @@ use rustc_span::{FileLines, SourceFile, Span}; use crate::snippet::{Annotation, AnnotationType, Line, MultilineAnnotation, Style, StyledString}; use crate::styled_buffer::StyledBuffer; -use crate::translation::Translate; +use crate::translation::{to_fluent_args, Translate}; use crate::{ CodeSuggestion, Diagnostic, DiagnosticId, DiagnosticMessage, FluentBundle, Handler, LazyFallbackBundle, Level, MultiSpan, SubDiagnostic, SubstitutionHighlight, SuggestionStyle, @@ -535,7 +535,7 @@ impl Emitter for EmitterWriter { } fn emit_diagnostic(&mut self, diag: &Diagnostic) { - let fluent_args = self.to_fluent_args(diag.args()); + let fluent_args = to_fluent_args(diag.args()); let mut children = diag.children.clone(); let (mut primary_span, suggestions) = self.primary_span_formatted(&diag, &fluent_args); diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index 1680c6accd78..4cc7be47fc2c 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -13,7 +13,7 @@ use rustc_span::source_map::{FilePathMapping, SourceMap}; use crate::emitter::{Emitter, HumanReadableErrorType}; use crate::registry::Registry; -use crate::translation::Translate; +use crate::translation::{to_fluent_args, Translate}; use crate::DiagnosticId; use crate::{ CodeSuggestion, FluentBundle, LazyFallbackBundle, MultiSpan, SpanLabel, SubDiagnostic, @@ -312,7 +312,7 @@ struct UnusedExterns<'a, 'b, 'c> { impl Diagnostic { fn from_errors_diagnostic(diag: &crate::Diagnostic, je: &JsonEmitter) -> Diagnostic { - let args = je.to_fluent_args(diag.args()); + let args = to_fluent_args(diag.args()); let sugg = diag.suggestions.iter().flatten().map(|sugg| { let translated_message = je.translate_message(&sugg.msg, &args); Diagnostic { diff --git a/compiler/rustc_errors/src/translation.rs b/compiler/rustc_errors/src/translation.rs index 4f407badb3f9..c2ec2526a4ae 100644 --- a/compiler/rustc_errors/src/translation.rs +++ b/compiler/rustc_errors/src/translation.rs @@ -4,6 +4,27 @@ use rustc_data_structures::sync::Lrc; use rustc_error_messages::FluentArgs; use std::borrow::Cow; +/// Convert diagnostic arguments (a rustc internal type that exists to implement +/// `Encodable`/`Decodable`) into `FluentArgs` which is necessary to perform translation. +/// +/// Typically performed once for each diagnostic at the start of `emit_diagnostic` and then +/// passed around as a reference thereafter. +pub fn to_fluent_args<'iter, 'arg: 'iter>( + iter: impl Iterator>, +) -> FluentArgs<'arg> { + let mut args = if let Some(size) = iter.size_hint().1 { + FluentArgs::with_capacity(size) + } else { + FluentArgs::new() + }; + + for (k, v) in iter { + args.set(k.clone(), v.clone()); + } + + args +} + pub trait Translate { /// Return `FluentBundle` with localized diagnostics for the locale requested by the user. If no /// language was requested by the user then this will be `None` and `fallback_fluent_bundle` @@ -15,15 +36,6 @@ pub trait Translate { /// unavailable for the requested locale. fn fallback_fluent_bundle(&self) -> &FluentBundle; - /// Convert diagnostic arguments (a rustc internal type that exists to implement - /// `Encodable`/`Decodable`) into `FluentArgs` which is necessary to perform translation. - /// - /// Typically performed once for each diagnostic at the start of `emit_diagnostic` and then - /// passed around as a reference thereafter. - fn to_fluent_args<'arg>(&self, args: &[DiagnosticArg<'arg>]) -> FluentArgs<'arg> { - FromIterator::from_iter(args.iter().cloned()) - } - /// Convert `DiagnosticMessage`s to a string, performing translation if necessary. fn translate_messages( &self, diff --git a/src/librustdoc/passes/check_code_block_syntax.rs b/src/librustdoc/passes/check_code_block_syntax.rs index 14a38a760d23..2e651b538741 100644 --- a/src/librustdoc/passes/check_code_block_syntax.rs +++ b/src/librustdoc/passes/check_code_block_syntax.rs @@ -1,8 +1,9 @@ //! Validates syntax inside Rust code blocks (\`\`\`rust). use rustc_data_structures::sync::{Lock, Lrc}; use rustc_errors::{ - emitter::Emitter, translation::Translate, Applicability, Diagnostic, Handler, - LazyFallbackBundle, + emitter::Emitter, + translation::{to_fluent_args, Translate}, + Applicability, Diagnostic, Handler, LazyFallbackBundle, }; use rustc_parse::parse_stream_from_source_str; use rustc_session::parse::ParseSess; @@ -193,7 +194,7 @@ impl Emitter for BufferEmitter { fn emit_diagnostic(&mut self, diag: &Diagnostic) { let mut buffer = self.buffer.borrow_mut(); - let fluent_args = self.to_fluent_args(diag.args()); + let fluent_args = to_fluent_args(diag.args()); let translated_main_message = self.translate_message(&diag.message[0].0, &fluent_args); buffer.messages.push(format!("error from rustc: {}", translated_main_message)); From b4ac26289f17a5779d4318fb63436d94aebbf5ea Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 3 Oct 2022 14:09:05 +0100 Subject: [PATCH 071/333] errors: `AddToDiagnostic::add_to_diagnostic_with` `AddToDiagnostic::add_to_diagnostic_with` is similar to the previous `AddToDiagnostic::add_to_diagnostic` but takes a function that can be used by the caller to modify diagnostic messages originating from the subdiagnostic (such as performing translation eagerly). `add_to_diagnostic` now just calls `add_to_diagnostic_with` with an empty closure. Signed-off-by: David Wood --- compiler/rustc_ast_lowering/src/errors.rs | 15 +++++-- compiler/rustc_ast_passes/src/errors.rs | 12 +++-- compiler/rustc_errors/src/diagnostic.rs | 19 ++++++-- compiler/rustc_infer/src/errors/mod.rs | 33 +++++++++++--- .../src/errors/note_and_explain.rs | 9 +++- compiler/rustc_lint/src/errors.rs | 15 +++++-- compiler/rustc_macros/src/diagnostics/mod.rs | 4 +- .../src/diagnostics/subdiagnostic.rs | 45 ++++++++++++------- compiler/rustc_query_system/src/error.rs | 7 ++- .../ui-fulldeps/internal-lints/diagnostics.rs | 12 +++-- .../internal-lints/diagnostics.stderr | 8 ++-- 11 files changed, 129 insertions(+), 50 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs index 63ff64b00bed..c6c85ffa84dd 100644 --- a/compiler/rustc_ast_lowering/src/errors.rs +++ b/compiler/rustc_ast_lowering/src/errors.rs @@ -1,4 +1,7 @@ -use rustc_errors::{fluent, AddToDiagnostic, Applicability, Diagnostic, DiagnosticArgFromDisplay}; +use rustc_errors::{ + fluent, AddToDiagnostic, Applicability, Diagnostic, DiagnosticArgFromDisplay, + SubdiagnosticMessage, +}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{symbol::Ident, Span, Symbol}; @@ -19,7 +22,10 @@ pub struct UseAngleBrackets { } impl AddToDiagnostic for UseAngleBrackets { - fn add_to_diagnostic(self, diag: &mut Diagnostic) { + fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { diag.multipart_suggestion( fluent::ast_lowering::use_angle_brackets, vec![(self.open_param, String::from("<")), (self.close_param, String::from(">"))], @@ -69,7 +75,10 @@ pub enum AssocTyParenthesesSub { } impl AddToDiagnostic for AssocTyParenthesesSub { - fn add_to_diagnostic(self, diag: &mut Diagnostic) { + fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { match self { Self::Empty { parentheses_span } => diag.multipart_suggestion( fluent::ast_lowering::remove_parentheses, diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 035f0ce1cbc4..ba2ed24fc08f 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -1,6 +1,6 @@ //! Errors emitted by ast_passes. -use rustc_errors::{fluent, AddToDiagnostic, Applicability, Diagnostic}; +use rustc_errors::{fluent, AddToDiagnostic, Applicability, Diagnostic, SubdiagnosticMessage}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; @@ -17,7 +17,10 @@ pub struct ForbiddenLet { } impl AddToDiagnostic for ForbiddenLetReason { - fn add_to_diagnostic(self, diag: &mut Diagnostic) { + fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { match self { Self::GenericForbidden => {} Self::NotSupportedOr(span) => { @@ -228,7 +231,10 @@ pub struct ExternBlockSuggestion { } impl AddToDiagnostic for ExternBlockSuggestion { - fn add_to_diagnostic(self, diag: &mut Diagnostic) { + fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { let start_suggestion = if let Some(abi) = self.abi { format!("extern \"{}\" {{", abi) } else { diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index a267bf6c0b4d..2a6dd6da415c 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -203,9 +203,20 @@ impl IntoDiagnosticArg for ast::token::TokenKind { /// `#[derive(Subdiagnostic)]` -- see [rustc_macros::Subdiagnostic]. #[cfg_attr(bootstrap, rustc_diagnostic_item = "AddSubdiagnostic")] #[cfg_attr(not(bootstrap), rustc_diagnostic_item = "AddToDiagnostic")] -pub trait AddToDiagnostic { +pub trait AddToDiagnostic +where + Self: Sized, +{ /// Add a subdiagnostic to an existing diagnostic. - fn add_to_diagnostic(self, diag: &mut Diagnostic); + fn add_to_diagnostic(self, diag: &mut Diagnostic) { + self.add_to_diagnostic_with(diag, |_, m| m); + } + + /// Add a subdiagnostic to an existing diagnostic where `f` is invoked on every message used + /// (to optionally perform eager translation). + fn add_to_diagnostic_with(self, diag: &mut Diagnostic, f: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage; } /// Trait implemented by lint types. This should not be implemented manually. Instead, use @@ -921,8 +932,8 @@ impl Diagnostic { self } - /// Add a subdiagnostic from a type that implements `Subdiagnostic` - see - /// [rustc_macros::Subdiagnostic]. + /// Add a subdiagnostic from a type that implements `Subdiagnostic` (see + /// [rustc_macros::Subdiagnostic]). pub fn subdiagnostic(&mut self, subdiagnostic: impl AddToDiagnostic) -> &mut Self { subdiagnostic.add_to_diagnostic(self); self diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index 85b877652c6a..500900d3d4a7 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -1,6 +1,7 @@ use hir::GenericParamKind; use rustc_errors::{ - fluent, AddToDiagnostic, Applicability, DiagnosticMessage, DiagnosticStyledString, MultiSpan, + fluent, AddToDiagnostic, Applicability, Diagnostic, DiagnosticMessage, DiagnosticStyledString, + MultiSpan, SubdiagnosticMessage, }; use rustc_hir as hir; use rustc_hir::{FnRetTy, Ty}; @@ -229,7 +230,10 @@ pub enum RegionOriginNote<'a> { } impl AddToDiagnostic for RegionOriginNote<'_> { - fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) { + fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { let mut label_or_note = |span, msg: DiagnosticMessage| { let sub_count = diag.children.iter().filter(|d| d.span.is_dummy()).count(); let expanded_sub_count = diag.children.iter().filter(|d| !d.span.is_dummy()).count(); @@ -290,7 +294,10 @@ pub enum LifetimeMismatchLabels { } impl AddToDiagnostic for LifetimeMismatchLabels { - fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) { + fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { match self { LifetimeMismatchLabels::InRet { param_span, ret_span, span, label_var1 } => { diag.span_label(param_span, fluent::infer::declared_different); @@ -340,7 +347,10 @@ pub struct AddLifetimeParamsSuggestion<'a> { } impl AddToDiagnostic for AddLifetimeParamsSuggestion<'_> { - fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) { + fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { let mut mk_suggestion = || { let ( hir::Ty { kind: hir::TyKind::Rptr(lifetime_sub, _), .. }, @@ -439,7 +449,10 @@ pub struct IntroducesStaticBecauseUnmetLifetimeReq { } impl AddToDiagnostic for IntroducesStaticBecauseUnmetLifetimeReq { - fn add_to_diagnostic(mut self, diag: &mut rustc_errors::Diagnostic) { + fn add_to_diagnostic_with(mut self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { self.unmet_requirements .push_span_label(self.binding_span, fluent::infer::msl_introduces_static); diag.span_note(self.unmet_requirements, fluent::infer::msl_unmet_req); @@ -451,7 +464,10 @@ pub struct ImplNote { } impl AddToDiagnostic for ImplNote { - fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) { + fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { match self.impl_span { Some(span) => diag.span_note(span, fluent::infer::msl_impl_note), None => diag.note(fluent::infer::msl_impl_note), @@ -466,7 +482,10 @@ pub enum TraitSubdiag { // FIXME(#100717) used in `Vec` so requires eager translation/list support impl AddToDiagnostic for TraitSubdiag { - fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) { + fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { match self { TraitSubdiag::Note { span } => { diag.span_note(span, "this has an implicit `'static` lifetime requirement"); diff --git a/compiler/rustc_infer/src/errors/note_and_explain.rs b/compiler/rustc_infer/src/errors/note_and_explain.rs index 7f54918f7361..201a3c7100cc 100644 --- a/compiler/rustc_infer/src/errors/note_and_explain.rs +++ b/compiler/rustc_infer/src/errors/note_and_explain.rs @@ -1,5 +1,7 @@ use crate::infer::error_reporting::nice_region_error::find_anon_type; -use rustc_errors::{self, fluent, AddToDiagnostic, IntoDiagnosticArg}; +use rustc_errors::{ + self, fluent, AddToDiagnostic, Diagnostic, IntoDiagnosticArg, SubdiagnosticMessage, +}; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::{symbol::kw, Span}; @@ -159,7 +161,10 @@ impl RegionExplanation<'_> { } impl AddToDiagnostic for RegionExplanation<'_> { - fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) { + fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { if let Some(span) = self.desc.span { diag.span_note(span, fluent::infer::region_explanation); } else { diff --git a/compiler/rustc_lint/src/errors.rs b/compiler/rustc_lint/src/errors.rs index 880f3fbd00e6..97d012fb611d 100644 --- a/compiler/rustc_lint/src/errors.rs +++ b/compiler/rustc_lint/src/errors.rs @@ -1,4 +1,7 @@ -use rustc_errors::{fluent, AddToDiagnostic, ErrorGuaranteed, Handler, IntoDiagnostic}; +use rustc_errors::{ + fluent, AddToDiagnostic, Diagnostic, ErrorGuaranteed, Handler, IntoDiagnostic, + SubdiagnosticMessage, +}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_session::lint::Level; use rustc_span::{Span, Symbol}; @@ -23,7 +26,10 @@ pub enum OverruledAttributeSub { } impl AddToDiagnostic for OverruledAttributeSub { - fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) { + fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { match self { OverruledAttributeSub::DefaultSource { id } => { diag.note(fluent::lint::default_source); @@ -88,7 +94,10 @@ pub struct RequestedLevel { } impl AddToDiagnostic for RequestedLevel { - fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) { + fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { diag.note(fluent::lint::requested_level); diag.set_arg( "level", diff --git a/compiler/rustc_macros/src/diagnostics/mod.rs b/compiler/rustc_macros/src/diagnostics/mod.rs index 4166816b5e3c..f98cc66e9e93 100644 --- a/compiler/rustc_macros/src/diagnostics/mod.rs +++ b/compiler/rustc_macros/src/diagnostics/mod.rs @@ -9,7 +9,7 @@ use diagnostic::{DiagnosticDerive, LintDiagnosticDerive}; pub(crate) use fluent::fluent_messages; use proc_macro2::TokenStream; use quote::format_ident; -use subdiagnostic::SubdiagnosticDerive; +use subdiagnostic::SubdiagnosticDeriveBuilder; use synstructure::Structure; /// Implements `#[derive(Diagnostic)]`, which allows for errors to be specified as a struct, @@ -155,5 +155,5 @@ pub fn lint_diagnostic_derive(s: Structure<'_>) -> TokenStream { /// diag.subdiagnostic(RawIdentifierSuggestion { span, applicability, ident }); /// ``` pub fn session_subdiagnostic_derive(s: Structure<'_>) -> TokenStream { - SubdiagnosticDerive::new(s).into_tokens() + SubdiagnosticDeriveBuilder::new().into_tokens(s) } diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index 9a2588513f06..ef17dbd0426f 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -15,19 +15,19 @@ use syn::{spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, NestedMeta use synstructure::{BindingInfo, Structure, VariantInfo}; /// The central struct for constructing the `add_to_diagnostic` method from an annotated struct. -pub(crate) struct SubdiagnosticDerive<'a> { - structure: Structure<'a>, +pub(crate) struct SubdiagnosticDeriveBuilder { diag: syn::Ident, + f: syn::Ident, } -impl<'a> SubdiagnosticDerive<'a> { - pub(crate) fn new(structure: Structure<'a>) -> Self { +impl SubdiagnosticDeriveBuilder { + pub(crate) fn new() -> Self { let diag = format_ident!("diag"); - Self { structure, diag } + let f = format_ident!("f"); + Self { diag, f } } - pub(crate) fn into_tokens(self) -> TokenStream { - let SubdiagnosticDerive { mut structure, diag } = self; + pub(crate) fn into_tokens<'a>(self, mut structure: Structure<'a>) -> TokenStream { let implementation = { let ast = structure.ast(); let span = ast.span().unwrap(); @@ -53,8 +53,8 @@ impl<'a> SubdiagnosticDerive<'a> { structure.bind_with(|_| synstructure::BindStyle::Move); let variants_ = structure.each_variant(|variant| { - let mut builder = SubdiagnosticDeriveBuilder { - diag: &diag, + let mut builder = SubdiagnosticDeriveVariantBuilder { + parent: &self, variant, span, fields: build_field_mapping(variant), @@ -72,9 +72,17 @@ impl<'a> SubdiagnosticDerive<'a> { } }; + let diag = &self.diag; + let f = &self.f; let ret = structure.gen_impl(quote! { gen impl rustc_errors::AddToDiagnostic for @Self { - fn add_to_diagnostic(self, #diag: &mut rustc_errors::Diagnostic) { + fn add_to_diagnostic_with<__F>(self, #diag: &mut rustc_errors::Diagnostic, #f: __F) + where + __F: Fn( + &mut rustc_errors::Diagnostic, + rustc_errors::SubdiagnosticMessage + ) -> rustc_errors::SubdiagnosticMessage, + { use rustc_errors::{Applicability, IntoDiagnosticArg}; #implementation } @@ -88,9 +96,9 @@ impl<'a> SubdiagnosticDerive<'a> { /// for the final generated method. This is a separate struct to `SubdiagnosticDerive` /// only to be able to destructure and split `self.builder` and the `self.structure` up to avoid a /// double mut borrow later on. -struct SubdiagnosticDeriveBuilder<'a> { +struct SubdiagnosticDeriveVariantBuilder<'parent, 'a> { /// The identifier to use for the generated `DiagnosticBuilder` instance. - diag: &'a syn::Ident, + parent: &'parent SubdiagnosticDeriveBuilder, /// Info for the current variant (or the type if not an enum). variant: &'a VariantInfo<'a>, @@ -112,7 +120,7 @@ struct SubdiagnosticDeriveBuilder<'a> { has_suggestion_parts: bool, } -impl<'a> HasFieldMap for SubdiagnosticDeriveBuilder<'a> { +impl<'parent, 'a> HasFieldMap for SubdiagnosticDeriveVariantBuilder<'parent, 'a> { fn get_field_binding(&self, field: &String) -> Option<&TokenStream> { self.fields.get(field) } @@ -156,7 +164,7 @@ impl<'a> FromIterator<&'a SubdiagnosticKind> for KindsStatistics { } } -impl<'a> SubdiagnosticDeriveBuilder<'a> { +impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { fn identify_kind(&mut self) -> Result, DiagnosticDeriveError> { let mut kind_slugs = vec![]; @@ -187,7 +195,7 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> { let ast = binding.ast(); assert_eq!(ast.attrs.len(), 0, "field with attribute used as diagnostic arg"); - let diag = &self.diag; + let diag = &self.parent.diag; let ident = ast.ident.as_ref().unwrap(); // strip `r#` prefix, if present let ident = format_ident!("{}", ident); @@ -442,11 +450,14 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> { let span_field = self.span_field.value_ref(); - let diag = &self.diag; + let diag = &self.parent.diag; + let f = &self.parent.f; let mut calls = TokenStream::new(); for (kind, slug) in kind_slugs { + let message = format_ident!("__message"); + calls.extend(quote! { let #message = #f(#diag, rustc_errors::fluent::#slug.into()); }); + let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind); - let message = quote! { rustc_errors::fluent::#slug }; let call = match kind { SubdiagnosticKind::Suggestion { suggestion_kind, applicability, code } => { let applicability = applicability diff --git a/compiler/rustc_query_system/src/error.rs b/compiler/rustc_query_system/src/error.rs index 8602a4cf5aef..debdf9dbf440 100644 --- a/compiler/rustc_query_system/src/error.rs +++ b/compiler/rustc_query_system/src/error.rs @@ -1,4 +1,4 @@ -use rustc_errors::AddToDiagnostic; +use rustc_errors::{AddToDiagnostic, Diagnostic, SubdiagnosticMessage}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_session::Limit; use rustc_span::{Span, Symbol}; @@ -9,7 +9,10 @@ pub struct CycleStack { } impl AddToDiagnostic for CycleStack { - fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) { + fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { diag.span_note(self.span, &format!("...which requires {}...", self.desc)); } } diff --git a/src/test/ui-fulldeps/internal-lints/diagnostics.rs b/src/test/ui-fulldeps/internal-lints/diagnostics.rs index 9a5100ce17f5..462f5e784984 100644 --- a/src/test/ui-fulldeps/internal-lints/diagnostics.rs +++ b/src/test/ui-fulldeps/internal-lints/diagnostics.rs @@ -13,7 +13,7 @@ extern crate rustc_span; use rustc_errors::{ AddToDiagnostic, IntoDiagnostic, Diagnostic, DiagnosticBuilder, - ErrorGuaranteed, Handler, fluent + ErrorGuaranteed, Handler, fluent, SubdiagnosticMessage, }; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::Span; @@ -52,7 +52,10 @@ impl<'a> IntoDiagnostic<'a, ErrorGuaranteed> for TranslatableInIntoDiagnostic { pub struct UntranslatableInAddToDiagnostic; impl AddToDiagnostic for UntranslatableInAddToDiagnostic { - fn add_to_diagnostic(self, diag: &mut Diagnostic) { + fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { diag.note("untranslatable diagnostic"); //~^ ERROR diagnostics should be created using translatable messages } @@ -61,7 +64,10 @@ impl AddToDiagnostic for UntranslatableInAddToDiagnostic { pub struct TranslatableInAddToDiagnostic; impl AddToDiagnostic for TranslatableInAddToDiagnostic { - fn add_to_diagnostic(self, diag: &mut Diagnostic) { + fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { diag.note(fluent::compiletest::note); } } diff --git a/src/test/ui-fulldeps/internal-lints/diagnostics.stderr b/src/test/ui-fulldeps/internal-lints/diagnostics.stderr index f5f92ac1e7fb..ac820a79db27 100644 --- a/src/test/ui-fulldeps/internal-lints/diagnostics.stderr +++ b/src/test/ui-fulldeps/internal-lints/diagnostics.stderr @@ -11,13 +11,13 @@ LL | #![deny(rustc::untranslatable_diagnostic)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: diagnostics should be created using translatable messages - --> $DIR/diagnostics.rs:56:14 + --> $DIR/diagnostics.rs:59:14 | LL | diag.note("untranslatable diagnostic"); | ^^^^ error: diagnostics should only be created in `IntoDiagnostic`/`AddToDiagnostic` impls - --> $DIR/diagnostics.rs:70:25 + --> $DIR/diagnostics.rs:76:25 | LL | let _diag = handler.struct_err(fluent::compiletest::example); | ^^^^^^^^^^ @@ -29,13 +29,13 @@ LL | #![deny(rustc::diagnostic_outside_of_impl)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: diagnostics should only be created in `IntoDiagnostic`/`AddToDiagnostic` impls - --> $DIR/diagnostics.rs:73:25 + --> $DIR/diagnostics.rs:79:25 | LL | let _diag = handler.struct_err("untranslatable diagnostic"); | ^^^^^^^^^^ error: diagnostics should be created using translatable messages - --> $DIR/diagnostics.rs:73:25 + --> $DIR/diagnostics.rs:79:25 | LL | let _diag = handler.struct_err("untranslatable diagnostic"); | ^^^^^^^^^^ From 540b203bf9fe05e572f1baa938317d4c10df3528 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 3 Oct 2022 14:14:51 +0100 Subject: [PATCH 072/333] errors: `DiagnosticMessage::Eager` Add variant of `DiagnosticMessage` for eagerly translated messages (messages in the target language which don't need translated by the emitter during emission). Also adds `eager_subdiagnostic` function which is intended to be invoked by the diagnostic derive for subdiagnostic fields which are marked as needing eager translation. Signed-off-by: David Wood --- compiler/rustc_error_messages/src/lib.rs | 29 +++++++++++++++++++++++- compiler/rustc_errors/src/diagnostic.rs | 19 +++++++++++++++- compiler/rustc_errors/src/lib.rs | 11 +++++++++ compiler/rustc_errors/src/translation.rs | 4 +++- 4 files changed, 60 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index 18be60975e4e..560d6c7c6950 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -276,6 +276,18 @@ pub enum SubdiagnosticMessage { /// Non-translatable diagnostic message. // FIXME(davidtwco): can a `Cow<'static, str>` be used here? Str(String), + /// Translatable message which has already been translated eagerly. + /// + /// Some diagnostics have repeated subdiagnostics where the same interpolated variables would + /// be instantiated multiple times with different values. As translation normally happens + /// immediately prior to emission, after the diagnostic and subdiagnostic derive logic has run, + /// the setting of diagnostic arguments in the derived code will overwrite previous variable + /// values and only the final value will be set when translation occurs - resulting in + /// incorrect diagnostics. Eager translation results in translation for a subdiagnostic + /// happening immediately after the subdiagnostic derive's logic has been run. This variant + /// stores messages which have been translated eagerly. + // FIXME(#100717): can a `Cow<'static, str>` be used here? + Eager(String), /// Identifier of a Fluent message. Instances of this variant are generated by the /// `Subdiagnostic` derive. FluentIdentifier(FluentId), @@ -303,8 +315,20 @@ impl> From for SubdiagnosticMessage { #[rustc_diagnostic_item = "DiagnosticMessage"] pub enum DiagnosticMessage { /// Non-translatable diagnostic message. - // FIXME(davidtwco): can a `Cow<'static, str>` be used here? + // FIXME(#100717): can a `Cow<'static, str>` be used here? Str(String), + /// Translatable message which has already been translated eagerly. + /// + /// Some diagnostics have repeated subdiagnostics where the same interpolated variables would + /// be instantiated multiple times with different values. As translation normally happens + /// immediately prior to emission, after the diagnostic and subdiagnostic derive logic has run, + /// the setting of diagnostic arguments in the derived code will overwrite previous variable + /// values and only the final value will be set when translation occurs - resulting in + /// incorrect diagnostics. Eager translation results in translation for a subdiagnostic + /// happening immediately after the subdiagnostic derive's logic has been run. This variant + /// stores messages which have been translated eagerly. + // FIXME(#100717): can a `Cow<'static, str>` be used here? + Eager(String), /// Identifier for a Fluent message (with optional attribute) corresponding to the diagnostic /// message. /// @@ -323,6 +347,7 @@ impl DiagnosticMessage { pub fn with_subdiagnostic_message(&self, sub: SubdiagnosticMessage) -> Self { let attr = match sub { SubdiagnosticMessage::Str(s) => return DiagnosticMessage::Str(s), + SubdiagnosticMessage::Eager(s) => return DiagnosticMessage::Eager(s), SubdiagnosticMessage::FluentIdentifier(id) => { return DiagnosticMessage::FluentIdentifier(id, None); } @@ -331,6 +356,7 @@ impl DiagnosticMessage { match self { DiagnosticMessage::Str(s) => DiagnosticMessage::Str(s.clone()), + DiagnosticMessage::Eager(s) => DiagnosticMessage::Eager(s.clone()), DiagnosticMessage::FluentIdentifier(id, _) => { DiagnosticMessage::FluentIdentifier(id.clone(), Some(attr)) } @@ -379,6 +405,7 @@ impl Into for DiagnosticMessage { fn into(self) -> SubdiagnosticMessage { match self { DiagnosticMessage::Str(s) => SubdiagnosticMessage::Str(s), + DiagnosticMessage::Eager(s) => SubdiagnosticMessage::Eager(s), DiagnosticMessage::FluentIdentifier(id, None) => { SubdiagnosticMessage::FluentIdentifier(id) } diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 2a6dd6da415c..3e0840caaa69 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -939,6 +939,23 @@ impl Diagnostic { self } + /// Add a subdiagnostic from a type that implements `Subdiagnostic` (see + /// [rustc_macros::Subdiagnostic]). Performs eager translation of any translatable messages + /// used in the subdiagnostic, so suitable for use with repeated messages (i.e. re-use of + /// interpolated variables). + pub fn eager_subdiagnostic( + &mut self, + handler: &crate::Handler, + subdiagnostic: impl AddToDiagnostic, + ) -> &mut Self { + subdiagnostic.add_to_diagnostic_with(self, |diag, msg| { + let args = diag.args(); + let msg = diag.subdiagnostic_message_to_diagnostic_message(msg); + handler.eagerly_translate(msg, args) + }); + self + } + pub fn set_span>(&mut self, sp: S) -> &mut Self { self.span = sp.into(); if let Some(span) = self.span.primary_span() { @@ -994,7 +1011,7 @@ impl Diagnostic { /// Helper function that takes a `SubdiagnosticMessage` and returns a `DiagnosticMessage` by /// combining it with the primary message of the diagnostic (if translatable, otherwise it just /// passes the user's string along). - fn subdiagnostic_message_to_diagnostic_message( + pub(crate) fn subdiagnostic_message_to_diagnostic_message( &self, attr: impl Into, ) -> DiagnosticMessage { diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 94a493992e59..61c9abbb0d69 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -598,6 +598,17 @@ impl Handler { } } + /// Translate `message` eagerly with `args`. + pub fn eagerly_translate<'a>( + &self, + message: DiagnosticMessage, + args: impl Iterator>, + ) -> SubdiagnosticMessage { + let inner = self.inner.borrow(); + let args = crate::translation::to_fluent_args(args); + SubdiagnosticMessage::Eager(inner.emitter.translate_message(&message, &args).to_string()) + } + // This is here to not allow mutation of flags; // as of this writing it's only used in tests in librustc_middle. pub fn can_emit_warnings(&self) -> bool { diff --git a/compiler/rustc_errors/src/translation.rs b/compiler/rustc_errors/src/translation.rs index c2ec2526a4ae..a7737b467b75 100644 --- a/compiler/rustc_errors/src/translation.rs +++ b/compiler/rustc_errors/src/translation.rs @@ -55,7 +55,9 @@ pub trait Translate { ) -> Cow<'_, str> { trace!(?message, ?args); let (identifier, attr) = match message { - DiagnosticMessage::Str(msg) => return Cow::Borrowed(&msg), + DiagnosticMessage::Str(msg) | DiagnosticMessage::Eager(msg) => { + return Cow::Borrowed(&msg); + } DiagnosticMessage::FluentIdentifier(identifier, attr) => (identifier, attr), }; From 291a4736d9300a9be79a5f105c54a06a2a4f1d1b Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 3 Oct 2022 14:24:17 +0100 Subject: [PATCH 073/333] macros: `#[subdiagnostic(eager)]` Add support for `eager` argument to the `subdiagnostic` attribute which generates a call to `eager_subdiagnostic`. Signed-off-by: David Wood --- .../src/diagnostics/diagnostic.rs | 13 ++-- .../src/diagnostics/diagnostic_builder.rs | 71 ++++++++++++++----- .../session-diagnostic/diagnostic-derive.rs | 47 ++++++++++++ .../diagnostic-derive.stderr | 42 ++++++++++- 4 files changed, 151 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index 406a56cd4ae3..84c57b3f64e1 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -10,27 +10,31 @@ use synstructure::Structure; /// The central struct for constructing the `into_diagnostic` method from an annotated struct. pub(crate) struct DiagnosticDerive<'a> { structure: Structure<'a>, - handler: syn::Ident, builder: DiagnosticDeriveBuilder, } impl<'a> DiagnosticDerive<'a> { pub(crate) fn new(diag: syn::Ident, handler: syn::Ident, structure: Structure<'a>) -> Self { Self { - builder: DiagnosticDeriveBuilder { diag, kind: DiagnosticDeriveKind::Diagnostic }, - handler, + builder: DiagnosticDeriveBuilder { + diag, + kind: DiagnosticDeriveKind::Diagnostic { handler }, + }, structure, } } pub(crate) fn into_tokens(self) -> TokenStream { - let DiagnosticDerive { mut structure, handler, mut builder } = self; + let DiagnosticDerive { mut structure, mut builder } = self; let implementation = builder.each_variant(&mut structure, |mut builder, variant| { let preamble = builder.preamble(&variant); let body = builder.body(&variant); let diag = &builder.parent.diag; + let DiagnosticDeriveKind::Diagnostic { handler } = &builder.parent.kind else { + unreachable!() + }; let init = match builder.slug.value_ref() { None => { span_err(builder.span, "diagnostic slug not specified") @@ -56,6 +60,7 @@ impl<'a> DiagnosticDerive<'a> { } }); + let DiagnosticDeriveKind::Diagnostic { handler } = &builder.kind else { unreachable!() }; structure.gen_impl(quote! { gen impl<'__diagnostic_handler_sess, G> rustc_errors::IntoDiagnostic<'__diagnostic_handler_sess, G> diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index 9e88dc7a913a..df4e309086fd 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -17,9 +17,9 @@ use syn::{ use synstructure::{BindingInfo, Structure, VariantInfo}; /// What kind of diagnostic is being derived - a fatal/error/warning or a lint? -#[derive(Copy, Clone, PartialEq, Eq)] +#[derive(Clone, PartialEq, Eq)] pub(crate) enum DiagnosticDeriveKind { - Diagnostic, + Diagnostic { handler: syn::Ident }, LintDiagnostic, } @@ -340,18 +340,15 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { let diag = &self.parent.diag; let meta = attr.parse_meta()?; - if let Meta::Path(_) = meta { - let ident = &attr.path.segments.last().unwrap().ident; - let name = ident.to_string(); - let name = name.as_str(); - match name { - "skip_arg" => { - // Don't need to do anything - by virtue of the attribute existing, the - // `set_arg` call will not be generated. - return Ok(quote! {}); - } - "primary_span" => match self.parent.kind { - DiagnosticDeriveKind::Diagnostic => { + let ident = &attr.path.segments.last().unwrap().ident; + let name = ident.to_string(); + match (&meta, name.as_str()) { + // Don't need to do anything - by virtue of the attribute existing, the + // `set_arg` call will not be generated. + (Meta::Path(_), "skip_arg") => return Ok(quote! {}), + (Meta::Path(_), "primary_span") => { + match self.parent.kind { + DiagnosticDeriveKind::Diagnostic { .. } => { report_error_if_not_applied_to_span(attr, &info)?; return Ok(quote! { @@ -363,10 +360,50 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { diag.help("the `primary_span` field attribute is not valid for lint diagnostics") }) } - }, - "subdiagnostic" => return Ok(quote! { #diag.subdiagnostic(#binding); }), - _ => {} + } } + (Meta::Path(_), "subdiagnostic") => { + return Ok(quote! { #diag.subdiagnostic(#binding); }); + } + (Meta::NameValue(_), "subdiagnostic") => { + throw_invalid_attr!(attr, &meta, |diag| { + diag.help("`eager` is the only supported nested attribute for `subdiagnostic`") + }) + } + (Meta::List(MetaList { ref nested, .. }), "subdiagnostic") => { + if nested.len() != 1 { + throw_invalid_attr!(attr, &meta, |diag| { + diag.help( + "`eager` is the only supported nested attribute for `subdiagnostic`", + ) + }) + } + + let handler = match &self.parent.kind { + DiagnosticDeriveKind::Diagnostic { handler } => handler, + DiagnosticDeriveKind::LintDiagnostic => { + throw_invalid_attr!(attr, &meta, |diag| { + diag.help("eager subdiagnostics are not supported on lints") + }) + } + }; + + let nested_attr = nested.first().expect("pop failed for single element list"); + match nested_attr { + NestedMeta::Meta(meta @ Meta::Path(_)) + if meta.path().segments.last().unwrap().ident.to_string().as_str() + == "eager" => + { + return Ok(quote! { #diag.eager_subdiagnostic(#handler, #binding); }); + } + _ => { + throw_invalid_nested_attr!(attr, nested_attr, |diag| { + diag.help("`eager` is the only supported nested attribute for `subdiagnostic`") + }) + } + } + } + _ => (), } let (subdiag, slug) = self.parse_subdiag_attribute(attr)?; diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs index 1dc71abc104f..460af07a5596 100644 --- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs +++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs @@ -678,3 +678,50 @@ enum ExampleEnum { struct RawIdentDiagnosticArg { pub r#type: String, } + +#[derive(Diagnostic)] +#[diag(compiletest::example)] +struct SubdiagnosticBad { + #[subdiagnostic(bad)] +//~^ ERROR `#[subdiagnostic(bad)]` is not a valid attribute + note: Note, +} + +#[derive(Diagnostic)] +#[diag(compiletest::example)] +struct SubdiagnosticBadStr { + #[subdiagnostic = "bad"] +//~^ ERROR `#[subdiagnostic = ...]` is not a valid attribute + note: Note, +} + +#[derive(Diagnostic)] +#[diag(compiletest::example)] +struct SubdiagnosticBadTwice { + #[subdiagnostic(bad, bad)] +//~^ ERROR `#[subdiagnostic(...)]` is not a valid attribute + note: Note, +} + +#[derive(Diagnostic)] +#[diag(compiletest::example)] +struct SubdiagnosticBadLitStr { + #[subdiagnostic("bad")] +//~^ ERROR `#[subdiagnostic("...")]` is not a valid attribute + note: Note, +} + +#[derive(LintDiagnostic)] +#[diag(compiletest::example)] +struct SubdiagnosticEagerLint { + #[subdiagnostic(eager)] +//~^ ERROR `#[subdiagnostic(...)]` is not a valid attribute + note: Note, +} + +#[derive(Diagnostic)] +#[diag(compiletest::example)] +struct SubdiagnosticEagerCorrect { + #[subdiagnostic(eager)] + note: Note, +} diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr index 167198b47f20..7a42d618707a 100644 --- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr +++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr @@ -533,6 +533,46 @@ LL | #[label] | = help: `#[label]` and `#[suggestion]` can only be applied to fields +error: `#[subdiagnostic(bad)]` is not a valid attribute + --> $DIR/diagnostic-derive.rs:685:21 + | +LL | #[subdiagnostic(bad)] + | ^^^ + | + = help: `eager` is the only supported nested attribute for `subdiagnostic` + +error: `#[subdiagnostic = ...]` is not a valid attribute + --> $DIR/diagnostic-derive.rs:693:5 + | +LL | #[subdiagnostic = "bad"] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `eager` is the only supported nested attribute for `subdiagnostic` + +error: `#[subdiagnostic(...)]` is not a valid attribute + --> $DIR/diagnostic-derive.rs:701:5 + | +LL | #[subdiagnostic(bad, bad)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `eager` is the only supported nested attribute for `subdiagnostic` + +error: `#[subdiagnostic("...")]` is not a valid attribute + --> $DIR/diagnostic-derive.rs:709:21 + | +LL | #[subdiagnostic("bad")] + | ^^^^^ + | + = help: `eager` is the only supported nested attribute for `subdiagnostic` + +error: `#[subdiagnostic(...)]` is not a valid attribute + --> $DIR/diagnostic-derive.rs:717:5 + | +LL | #[subdiagnostic(eager)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: eager subdiagnostics are not supported on lints + error: cannot find attribute `nonsense` in this scope --> $DIR/diagnostic-derive.rs:55:3 | @@ -607,7 +647,7 @@ LL | arg: impl IntoDiagnosticArg, | ^^^^^^^^^^^^^^^^^ required by this bound in `DiagnosticBuilder::<'a, G>::set_arg` = note: this error originates in the derive macro `Diagnostic` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 75 previous errors +error: aborting due to 80 previous errors Some errors have detailed explanations: E0277, E0425. For more information about an error, try `rustc --explain E0277`. From 113e94369cb72a98648c12c263475821bbff7d9c Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 3 Oct 2022 14:26:29 +0100 Subject: [PATCH 074/333] query_system: finish migration Using eager translation, migrate the remaining repeated cycle stack diagnostic. Signed-off-by: David Wood --- .../locales/en-US/query_system.ftl | 2 ++ compiler/rustc_query_system/src/error.rs | 15 ++++----------- compiler/rustc_query_system/src/lib.rs | 2 +- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_error_messages/locales/en-US/query_system.ftl b/compiler/rustc_error_messages/locales/en-US/query_system.ftl index b914ba52a735..870e824039cb 100644 --- a/compiler/rustc_error_messages/locales/en-US/query_system.ftl +++ b/compiler/rustc_error_messages/locales/en-US/query_system.ftl @@ -12,6 +12,8 @@ query_system_cycle_usage = cycle used when {$usage} query_system_cycle_stack_single = ...which immediately requires {$stack_bottom} again +query_system_cycle_stack_middle = ...which requires {$desc}... + query_system_cycle_stack_multiple = ...which again requires {$stack_bottom}, completing the cycle query_system_cycle_recursive_ty_alias = type aliases cannot be recursive diff --git a/compiler/rustc_query_system/src/error.rs b/compiler/rustc_query_system/src/error.rs index debdf9dbf440..1e74e0e29909 100644 --- a/compiler/rustc_query_system/src/error.rs +++ b/compiler/rustc_query_system/src/error.rs @@ -1,22 +1,15 @@ -use rustc_errors::{AddToDiagnostic, Diagnostic, SubdiagnosticMessage}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_session::Limit; use rustc_span::{Span, Symbol}; +#[derive(Subdiagnostic)] +#[note(query_system::cycle_stack_middle)] pub struct CycleStack { + #[primary_span] pub span: Span, pub desc: String, } -impl AddToDiagnostic for CycleStack { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) - where - F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, - { - diag.span_note(self.span, &format!("...which requires {}...", self.desc)); - } -} - #[derive(Copy, Clone)] pub enum HandleCycleError { Error, @@ -56,7 +49,7 @@ pub struct Cycle { #[primary_span] pub span: Span, pub stack_bottom: String, - #[subdiagnostic] + #[subdiagnostic(eager)] pub cycle_stack: Vec, #[subdiagnostic] pub stack_count: StackCount, diff --git a/compiler/rustc_query_system/src/lib.rs b/compiler/rustc_query_system/src/lib.rs index 8f6da73d1f2d..f47760e9ae6c 100644 --- a/compiler/rustc_query_system/src/lib.rs +++ b/compiler/rustc_query_system/src/lib.rs @@ -4,7 +4,7 @@ #![feature(min_specialization)] #![feature(extern_types)] #![allow(rustc::potential_query_instability)] -// #![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] #[macro_use] From 7e20929e55c5c76bc1466568da746cea4171bc71 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 3 Oct 2022 14:28:02 +0100 Subject: [PATCH 075/333] macros: separate suggestion fmt'ing and emission Diagnostic derives have previously had to take special care when ordering the generated code so that fields were not used after a move. This is unlikely for most fields because a field is either annotated with a subdiagnostic attribute and is thus likely a `Span` and copiable, or is a argument, in which case it is only used once by `set_arg` anyway. However, format strings for code in suggestions can result in fields being used after being moved if not ordered carefully. As a result, the derive currently puts `set_arg` calls last (just before emission), such as: ```rust let diag = { /* create diagnostic */ }; diag.span_suggestion_with_style( span, fluent::crate::slug, format!("{}", __binding_0), Applicability::Unknown, SuggestionStyle::ShowAlways ); /* + other subdiagnostic additions */ diag.set_arg("foo", __binding_0); /* + other `set_arg` calls */ diag.emit(); ``` For eager translation, this doesn't work, as the message being translated eagerly can assume that all arguments are available - so arguments _must_ be set first. Format strings for suggestion code are now separated into two parts - an initialization line that performs the formatting into a variable, and a usage in the subdiagnostic addition. By separating these parts, the initialization can happen before arguments are set, preserving the desired order so that code compiles, while still enabling arguments to be set before subdiagnostics are added. ```rust let diag = { /* create diagnostic */ }; let __code_0 = format!("{}", __binding_0); /* + other formatting */ diag.set_arg("foo", __binding_0); /* + other `set_arg` calls */ diag.span_suggestion_with_style( span, fluent::crate::slug, __code_0, Applicability::Unknown, SuggestionStyle::ShowAlways ); /* + other subdiagnostic additions */ diag.emit(); ``` Signed-off-by: David Wood --- .../src/diagnostics/diagnostic_builder.rs | 6 ++- .../src/diagnostics/subdiagnostic.rs | 50 ++++++++++++++----- .../rustc_macros/src/diagnostics/utils.rs | 48 ++++++++++++++---- .../session-diagnostic/diagnostic-derive.rs | 24 +++++++++ 4 files changed, 105 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index df4e309086fd..017bf2365d62 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -426,7 +426,8 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { SubdiagnosticKind::Suggestion { suggestion_kind, applicability: static_applicability, - code, + code_field, + code_init, } => { let (span_field, mut applicability) = self.span_and_applicability_of_ty(info)?; @@ -440,10 +441,11 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { let style = suggestion_kind.to_suggestion_style(); Ok(quote! { + #code_init #diag.span_suggestion_with_style( #span_field, rustc_errors::fluent::#slug, - #code, + #code_field, #applicability, #style ); diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index ef17dbd0426f..3d4c3ab9fd7c 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -5,7 +5,7 @@ use crate::diagnostics::error::{ DiagnosticDeriveError, }; use crate::diagnostics::utils::{ - build_field_mapping, report_error_if_not_applied_to_applicability, + build_field_mapping, new_code_ident, report_error_if_not_applied_to_applicability, report_error_if_not_applied_to_span, FieldInfo, FieldInnerTy, FieldMap, HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind, }; @@ -57,6 +57,7 @@ impl SubdiagnosticDeriveBuilder { parent: &self, variant, span, + formatting_init: TokenStream::new(), fields: build_field_mapping(variant), span_field: None, applicability: None, @@ -105,6 +106,9 @@ struct SubdiagnosticDeriveVariantBuilder<'parent, 'a> { /// Span for the entire type. span: proc_macro::Span, + /// Initialization of format strings for code suggestions. + formatting_init: TokenStream, + /// Store a map of field name to its corresponding field. This is built on construction of the /// derive builder. fields: FieldMap, @@ -230,7 +234,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { }; let generated = self - .generate_field_code_inner(kind_stats, attr, info) + .generate_field_code_inner(kind_stats, attr, info, inner_ty.will_iterate()) .unwrap_or_else(|v| v.to_compile_error()); inner_ty.with(binding, generated) @@ -243,13 +247,18 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { kind_stats: KindsStatistics, attr: &Attribute, info: FieldInfo<'_>, + clone_suggestion_code: bool, ) -> Result { let meta = attr.parse_meta()?; match meta { Meta::Path(path) => self.generate_field_code_inner_path(kind_stats, attr, info, path), - Meta::List(list @ MetaList { .. }) => { - self.generate_field_code_inner_list(kind_stats, attr, info, list) - } + Meta::List(list @ MetaList { .. }) => self.generate_field_code_inner_list( + kind_stats, + attr, + info, + list, + clone_suggestion_code, + ), _ => throw_invalid_attr!(attr, &meta), } } @@ -353,6 +362,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { attr: &Attribute, info: FieldInfo<'_>, list: MetaList, + clone_suggestion_code: bool, ) -> Result { let span = attr.span().unwrap(); let ident = &list.path.segments.last().unwrap().ident; @@ -390,7 +400,8 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { match nested_name { "code" => { let formatted_str = self.build_format(&value.value(), value.span()); - code.set_once(formatted_str, span); + let code_field = new_code_ident(); + code.set_once((code_field, formatted_str), span); } _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { diag.help("`code` is the only valid nested attribute") @@ -398,14 +409,20 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { } } - let Some((code, _)) = code else { + let Some((code_field, formatted_str)) = code.value() else { span_err(span, "`#[suggestion_part(...)]` attribute without `code = \"...\"`") .emit(); return Ok(quote! {}); }; let binding = info.binding; - Ok(quote! { suggestions.push((#binding, #code)); }) + self.formatting_init.extend(quote! { let #code_field = #formatted_str; }); + let code_field = if clone_suggestion_code { + quote! { #code_field.clone() } + } else { + quote! { #code_field } + }; + Ok(quote! { suggestions.push((#binding, #code_field)); }) } _ => throw_invalid_attr!(attr, &Meta::List(list), |diag| { let mut span_attrs = vec![]; @@ -459,7 +476,14 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind); let call = match kind { - SubdiagnosticKind::Suggestion { suggestion_kind, applicability, code } => { + SubdiagnosticKind::Suggestion { + suggestion_kind, + applicability, + code_init, + code_field, + } => { + self.formatting_init.extend(code_init); + let applicability = applicability .value() .map(|a| quote! { #a }) @@ -468,8 +492,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { if let Some(span) = span_field { let style = suggestion_kind.to_suggestion_style(); - - quote! { #diag.#name(#span, #message, #code, #applicability, #style); } + quote! { #diag.#name(#span, #message, #code_field, #applicability, #style); } } else { span_err(self.span, "suggestion without `#[primary_span]` field").emit(); quote! { unreachable!(); } @@ -510,6 +533,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { } } }; + calls.extend(call); } @@ -521,11 +545,13 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { .map(|binding| self.generate_field_set_arg(binding)) .collect(); + let formatting_init = &self.formatting_init; Ok(quote! { #init + #formatting_init #attr_args - #calls #plain_args + #calls }) } } diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs index 162699c28687..5e1cd842b9bb 100644 --- a/compiler/rustc_macros/src/diagnostics/utils.rs +++ b/compiler/rustc_macros/src/diagnostics/utils.rs @@ -4,6 +4,7 @@ use crate::diagnostics::error::{ use proc_macro::Span; use proc_macro2::TokenStream; use quote::{format_ident, quote, ToTokens}; +use std::cell::RefCell; use std::cmp::Ordering; use std::collections::{BTreeSet, HashMap}; use std::fmt; @@ -14,6 +15,19 @@ use synstructure::{BindStyle, BindingInfo, VariantInfo}; use super::error::invalid_nested_attr; +thread_local! { + pub static CODE_IDENT_COUNT: RefCell = RefCell::new(0); +} + +/// Returns an ident of the form `__code_N` where `N` is incremented once with every call. +pub(crate) fn new_code_ident() -> syn::Ident { + CODE_IDENT_COUNT.with(|count| { + let ident = format_ident!("__code_{}", *count.borrow()); + *count.borrow_mut() += 1; + ident + }) +} + /// Checks whether the type name of `ty` matches `name`. /// /// Given some struct at `a::b::c::Foo`, this will return true for `c::Foo`, `b::c::Foo`, or @@ -142,6 +156,15 @@ impl<'ty> FieldInnerTy<'ty> { unreachable!(); } + /// Returns `true` if `FieldInnerTy::with` will result in iteration for this inner type (i.e. + /// that cloning might be required for values moved in the loop body). + pub(crate) fn will_iterate(&self) -> bool { + match self { + FieldInnerTy::Vec(..) => true, + FieldInnerTy::Option(..) | FieldInnerTy::None => false, + } + } + /// Returns `Option` containing inner type if there is one. pub(crate) fn inner_type(&self) -> Option<&'ty Type> { match self { @@ -434,7 +457,12 @@ pub(super) enum SubdiagnosticKind { Suggestion { suggestion_kind: SuggestionKind, applicability: SpannedOption, - code: TokenStream, + /// Identifier for variable used for formatted code, e.g. `___code_0`. Enables separation + /// of formatting and diagnostic emission so that `set_arg` calls can happen in-between.. + code_field: syn::Ident, + /// Initialization logic for `code_field`'s variable, e.g. + /// `let __formatted_code = /* whatever */;` + code_init: TokenStream, }, /// `#[multipart_suggestion{,_short,_hidden,_verbose}]` MultipartSuggestion { @@ -469,7 +497,8 @@ impl SubdiagnosticKind { SubdiagnosticKind::Suggestion { suggestion_kind, applicability: None, - code: TokenStream::new(), + code_field: new_code_ident(), + code_init: TokenStream::new(), } } else if let Some(suggestion_kind) = name.strip_prefix("multipart_suggestion").and_then(|s| s.parse().ok()) @@ -548,9 +577,10 @@ impl SubdiagnosticKind { }; match (nested_name, &mut kind) { - ("code", SubdiagnosticKind::Suggestion { .. }) => { + ("code", SubdiagnosticKind::Suggestion { code_field, .. }) => { let formatted_str = fields.build_format(&value.value(), value.span()); - code.set_once(formatted_str, span); + let code_init = quote! { let #code_field = #formatted_str; }; + code.set_once(code_init, span); } ( "applicability", @@ -582,13 +612,13 @@ impl SubdiagnosticKind { } match kind { - SubdiagnosticKind::Suggestion { code: ref mut code_field, .. } => { - *code_field = if let Some((code, _)) = code { - code + SubdiagnosticKind::Suggestion { ref code_field, ref mut code_init, .. } => { + *code_init = if let Some(init) = code.value() { + init } else { span_err(span, "suggestion without `code = \"...\"`").emit(); - quote! { "" } - } + quote! { let #code_field: String = unreachable!(); } + }; } SubdiagnosticKind::Label | SubdiagnosticKind::Note diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs index 460af07a5596..e873c36e0b39 100644 --- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs +++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs @@ -725,3 +725,27 @@ struct SubdiagnosticEagerCorrect { #[subdiagnostic(eager)] note: Note, } + +// Check that formatting of `correct` in suggestion doesn't move the binding for that field, making +// the `set_arg` call a compile error; and that isn't worked around by moving the `set_arg` call +// after the `span_suggestion` call - which breaks eager translation. + +#[derive(Subdiagnostic)] +#[suggestion_short( + parser::use_instead, + applicability = "machine-applicable", + code = "{correct}" +)] +pub(crate) struct SubdiagnosticWithSuggestion { + #[primary_span] + span: Span, + invalid: String, + correct: String, +} + +#[derive(Diagnostic)] +#[diag(compiletest::example)] +struct SubdiagnosticEagerSuggestion { + #[subdiagnostic(eager)] + sub: SubdiagnosticWithSuggestion, +} From fbac1f288bb797ee239f69eafc3dca558f1ea817 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 3 Oct 2022 16:10:34 +0100 Subject: [PATCH 076/333] macros: simplify field ordering in diag derive Following the approach taken in earlier commits to separate formatting initialization from use in the subdiagnostic derive, simplify the diagnostic derive by removing the field-ordering logic that previously solved this problem. Signed-off-by: David Wood --- .../src/diagnostics/diagnostic.rs | 5 +- .../src/diagnostics/diagnostic_builder.rs | 77 ++++++++----------- .../rustc_macros/src/diagnostics/utils.rs | 60 +-------------- 3 files changed, 39 insertions(+), 103 deletions(-) diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index 84c57b3f64e1..8cf307df5a56 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -52,8 +52,10 @@ impl<'a> DiagnosticDerive<'a> { } }; + let formatting_init = &builder.formatting_init; quote! { #init + #formatting_init #preamble #body #diag @@ -101,9 +103,10 @@ impl<'a> LintDiagnosticDerive<'a> { let body = builder.body(&variant); let diag = &builder.parent.diag; - + let formatting_init = &builder.formatting_init; quote! { #preamble + #formatting_init #body #diag } diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index 017bf2365d62..dcbe89251cb3 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -5,9 +5,9 @@ use crate::diagnostics::error::{ DiagnosticDeriveError, }; use crate::diagnostics::utils::{ - bind_style_of_field, build_field_mapping, report_error_if_not_applied_to_span, - report_type_error, should_generate_set_arg, type_is_unit, type_matches_path, FieldInfo, - FieldInnerTy, FieldMap, HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind, + build_field_mapping, report_error_if_not_applied_to_span, report_type_error, + should_generate_set_arg, type_is_unit, type_matches_path, FieldInfo, FieldInnerTy, FieldMap, + HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind, }; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote}; @@ -40,6 +40,9 @@ pub(crate) struct DiagnosticDeriveVariantBuilder<'parent> { /// The parent builder for the entire type. pub parent: &'parent DiagnosticDeriveBuilder, + /// Initialization of format strings for code suggestions. + pub formatting_init: TokenStream, + /// Span of the struct or the enum variant. pub span: proc_macro::Span, @@ -88,19 +91,7 @@ impl DiagnosticDeriveBuilder { } } - for variant in structure.variants_mut() { - // First, change the binding style of each field based on the code that will be - // generated for the field - e.g. `set_arg` calls needs by-move bindings, whereas - // `set_primary_span` only needs by-ref. - variant.bind_with(|bi| bind_style_of_field(bi.ast()).0); - - // Then, perform a stable sort on bindings which generates code for by-ref bindings - // before code generated for by-move bindings. Any code generated for the by-ref - // bindings which creates a reference to the by-move fields will happen before the - // by-move bindings move those fields and make them inaccessible. - variant.bindings_mut().sort_by_cached_key(|bi| bind_style_of_field(bi.ast())); - } - + structure.bind_with(|_| synstructure::BindStyle::Move); let variants = structure.each_variant(|variant| { let span = match structure.ast().data { syn::Data::Struct(..) => span, @@ -112,6 +103,7 @@ impl DiagnosticDeriveBuilder { parent: &self, span, field_map: build_field_mapping(variant), + formatting_init: TokenStream::new(), slug: None, code: None, }; @@ -143,16 +135,14 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { /// Generates calls to `span_label` and similar functions based on the attributes on fields or /// calls to `set_arg` when no attributes are present. - /// - /// Expects use of `Self::each_variant` which will have sorted bindings so that by-ref bindings - /// (which may create references to by-move bindings) have their code generated first - - /// necessary as code for suggestions uses formatting machinery and the value of other fields - /// (any given field can be referenced multiple times, so must be accessed through a borrow); - /// and when passing fields to `add_subdiagnostic` or `set_arg` for Fluent, fields must be - /// accessed by-move. pub fn body<'s>(&mut self, variant: &VariantInfo<'s>) -> TokenStream { let mut body = quote! {}; - for binding in variant.bindings() { + // Generate `set_arg` calls first.. + for binding in variant.bindings().iter().filter(|bi| should_generate_set_arg(bi.ast())) { + body.extend(self.generate_field_code(binding)); + } + // ..and then subdiagnostic additions. + for binding in variant.bindings().iter().filter(|bi| !should_generate_set_arg(bi.ast())) { body.extend(self.generate_field_attrs_code(binding)); } body @@ -274,24 +264,27 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { } } + fn generate_field_code(&mut self, binding_info: &BindingInfo<'_>) -> TokenStream { + let diag = &self.parent.diag; + + let field = binding_info.ast(); + let field_binding = &binding_info.binding; + + let ident = field.ident.as_ref().unwrap(); + let ident = format_ident!("{}", ident); // strip `r#` prefix, if present + + quote! { + #diag.set_arg( + stringify!(#ident), + #field_binding + ); + } + } + fn generate_field_attrs_code(&mut self, binding_info: &BindingInfo<'_>) -> TokenStream { let field = binding_info.ast(); let field_binding = &binding_info.binding; - if should_generate_set_arg(&field) { - let diag = &self.parent.diag; - let ident = field.ident.as_ref().unwrap(); - // strip `r#` prefix, if present - let ident = format_ident!("{}", ident); - return quote! { - #diag.set_arg( - stringify!(#ident), - #field_binding - ); - }; - } - - let needs_move = bind_style_of_field(&field).is_move(); let inner_ty = FieldInnerTy::from_type(&field.ty); field @@ -304,10 +297,8 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { let (binding, needs_destructure) = if needs_clone { // `primary_span` can accept a `Vec` so don't destructure that. (quote! { #field_binding.clone() }, false) - } else if needs_move { - (quote! { #field_binding }, true) } else { - (quote! { *#field_binding }, true) + (quote! { #field_binding }, true) }; let generated_code = self @@ -440,8 +431,8 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { .unwrap_or_else(|| quote! { rustc_errors::Applicability::Unspecified }); let style = suggestion_kind.to_suggestion_style(); + self.formatting_init.extend(code_init); Ok(quote! { - #code_init #diag.span_suggestion_with_style( #span_field, rustc_errors::fluent::#slug, @@ -490,7 +481,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { // If `ty` is `Span` w/out applicability, then use `Applicability::Unspecified`. ty @ Type::Path(..) if type_matches_path(ty, &["rustc_span", "Span"]) => { let binding = &info.binding.binding; - Ok((quote!(*#binding), None)) + Ok((quote!(#binding), None)) } // If `ty` is `(Span, Applicability)` then return tokens accessing those. Type::Tuple(tup) => { diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs index 5e1cd842b9bb..4fd4adc51126 100644 --- a/compiler/rustc_macros/src/diagnostics/utils.rs +++ b/compiler/rustc_macros/src/diagnostics/utils.rs @@ -5,13 +5,12 @@ use proc_macro::Span; use proc_macro2::TokenStream; use quote::{format_ident, quote, ToTokens}; use std::cell::RefCell; -use std::cmp::Ordering; use std::collections::{BTreeSet, HashMap}; use std::fmt; use std::str::FromStr; use syn::{spanned::Spanned, Attribute, Field, Meta, Type, TypeTuple}; use syn::{MetaList, MetaNameValue, NestedMeta, Path}; -use synstructure::{BindStyle, BindingInfo, VariantInfo}; +use synstructure::{BindingInfo, VariantInfo}; use super::error::invalid_nested_attr; @@ -650,65 +649,8 @@ impl quote::IdentFragment for SubdiagnosticKind { } } -/// Wrapper around `synstructure::BindStyle` which implements `Ord`. -#[derive(PartialEq, Eq)] -pub(super) struct OrderedBindStyle(pub(super) BindStyle); - -impl OrderedBindStyle { - /// Is `BindStyle::Move` or `BindStyle::MoveMut`? - pub(super) fn is_move(&self) -> bool { - matches!(self.0, BindStyle::Move | BindStyle::MoveMut) - } -} - -impl Ord for OrderedBindStyle { - fn cmp(&self, other: &Self) -> Ordering { - match (self.is_move(), other.is_move()) { - // If both `self` and `other` are the same, then ordering is equal. - (true, true) | (false, false) => Ordering::Equal, - // If `self` is not a move then it should be considered less than `other` (so that - // references are sorted first). - (false, _) => Ordering::Less, - // If `self` is a move then it must be greater than `other` (again, so that references - // are sorted first). - (true, _) => Ordering::Greater, - } - } -} - -impl PartialOrd for OrderedBindStyle { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - /// Returns `true` if `field` should generate a `set_arg` call rather than any other diagnostic /// call (like `span_label`). pub(super) fn should_generate_set_arg(field: &Field) -> bool { field.attrs.is_empty() } - -/// Returns `true` if `field` needs to have code generated in the by-move branch of the -/// generated derive rather than the by-ref branch. -pub(super) fn bind_style_of_field(field: &Field) -> OrderedBindStyle { - let generates_set_arg = should_generate_set_arg(field); - let is_multispan = type_matches_path(&field.ty, &["rustc_errors", "MultiSpan"]); - // FIXME(davidtwco): better support for one field needing to be in the by-move and - // by-ref branches. - let is_subdiagnostic = field - .attrs - .iter() - .map(|attr| attr.path.segments.last().unwrap().ident.to_string()) - .any(|attr| attr == "subdiagnostic"); - - // `set_arg` calls take their argument by-move.. - let needs_move = generates_set_arg - // If this is a `MultiSpan` field then it needs to be moved to be used by any - // attribute.. - || is_multispan - // If this a `#[subdiagnostic]` then it needs to be moved as the other diagnostic is - // unlikely to be `Copy`.. - || is_subdiagnostic; - - OrderedBindStyle(if needs_move { BindStyle::Move } else { BindStyle::Ref }) -} From fd9cd8476b2c59518d27e90ac955dce536785379 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 10 Oct 2022 15:41:32 +0200 Subject: [PATCH 077/333] Don't report build-scripts and proc-macros are metadata progress --- crates/project-model/src/build_scripts.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/project-model/src/build_scripts.rs b/crates/project-model/src/build_scripts.rs index 32db42f1db75..d9f09c034956 100644 --- a/crates/project-model/src/build_scripts.rs +++ b/crates/project-model/src/build_scripts.rs @@ -154,6 +154,8 @@ impl WorkspaceBuildScripts { Some(&it) => it, None => return, }; + progress(format!("running build-script: {}", workspace[package].name)); + let cfgs = { let mut acc = Vec::new(); for cfg in message.cfgs { @@ -189,7 +191,7 @@ impl WorkspaceBuildScripts { None => return, }; - progress(format!("metadata {}", message.target.name)); + progress(format!("building proc-macros: {}", message.target.name)); if message.target.kind.iter().any(|k| k == "proc-macro") { // Skip rmeta file From f3ae5e56fbf35e7b47a071b28432f72b669f444a Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 10 Oct 2022 15:45:24 +0200 Subject: [PATCH 078/333] Refactor completions expansion --- crates/ide-completion/src/context.rs | 43 +- crates/ide-completion/src/context/analysis.rs | 1977 +++++++++-------- 2 files changed, 1030 insertions(+), 990 deletions(-) diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index a5e854b74df9..5ac460fc6eb9 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -23,7 +23,10 @@ use syntax::{ }; use text_edit::Indel; -use crate::CompletionConfig; +use crate::{ + context::analysis::{expand_and_analyze, AnalysisResult}, + CompletionConfig, +}; const COMPLETION_MARKER: &str = "intellijRulezz"; @@ -561,15 +564,27 @@ impl<'a> CompletionContext<'a> { let edit = Indel::insert(offset, COMPLETION_MARKER.to_string()); parse.reparse(&edit).tree() }; - let fake_ident_token = - file_with_fake_ident.syntax().token_at_offset(offset).right_biased()?; + // always pick the token to the immediate left of the cursor, as that is what we are actually + // completing on let original_token = original_file.syntax().token_at_offset(offset).left_biased()?; - let token = sema.descend_into_macros_single(original_token.clone()); + + let AnalysisResult { + analysis, + expected: (expected_type, expected_name), + qualifier_ctx, + token, + offset, + } = expand_and_analyze( + &sema, + original_file.syntax().clone(), + file_with_fake_ident.syntax().clone(), + offset, + &original_token, + )?; // adjust for macro input, this still fails if there is no token written yet - let scope_offset = if original_token == token { offset } else { token.text_range().end() }; - let scope = sema.scope_at_offset(&token.parent()?, scope_offset)?; + let scope = sema.scope_at_offset(&token.parent()?, offset)?; let krate = scope.krate(); let module = scope.module(); @@ -583,7 +598,7 @@ impl<'a> CompletionContext<'a> { let depth_from_crate_root = iter::successors(module.parent(db), |m| m.parent(db)).count(); - let mut ctx = CompletionContext { + let ctx = CompletionContext { sema, scope, db, @@ -593,19 +608,13 @@ impl<'a> CompletionContext<'a> { token, krate, module, - expected_name: None, - expected_type: None, - qualifier_ctx: Default::default(), + expected_name, + expected_type, + qualifier_ctx, locals, depth_from_crate_root, }; - let ident_ctx = ctx.expand_and_analyze( - original_file.syntax().clone(), - file_with_fake_ident.syntax().clone(), - offset, - fake_ident_token, - )?; - Some((ctx, ident_ctx)) + Some((ctx, analysis)) } } diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index 01dd9a234f55..04111ec7efaa 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -11,1063 +11,1094 @@ use syntax::{ }; use crate::context::{ - AttrCtx, CompletionAnalysis, CompletionContext, DotAccess, DotAccessKind, ExprCtx, - ItemListKind, LifetimeContext, LifetimeKind, NameContext, NameKind, NameRefContext, - NameRefKind, ParamContext, ParamKind, PathCompletionCtx, PathKind, PatternContext, - PatternRefutability, Qualified, QualifierCtx, TypeAscriptionTarget, TypeLocation, - COMPLETION_MARKER, + AttrCtx, CompletionAnalysis, DotAccess, DotAccessKind, ExprCtx, ItemListKind, LifetimeContext, + LifetimeKind, NameContext, NameKind, NameRefContext, NameRefKind, ParamContext, ParamKind, + PathCompletionCtx, PathKind, PatternContext, PatternRefutability, Qualified, QualifierCtx, + TypeAscriptionTarget, TypeLocation, COMPLETION_MARKER, }; -impl<'a> CompletionContext<'a> { - /// Expand attributes and macro calls at the current cursor position for both the original file - /// and fake file repeatedly. As soon as one of the two expansions fail we stop so the original - /// and speculative states stay in sync. - pub(super) fn expand_and_analyze( - &mut self, - mut original_file: SyntaxNode, - mut speculative_file: SyntaxNode, - mut offset: TextSize, - mut fake_ident_token: SyntaxToken, - ) -> Option { - let _p = profile::span("CompletionContext::expand_and_fill"); - let mut derive_ctx = None; +struct ExpansionResult { + original_file: SyntaxNode, + speculative_file: SyntaxNode, + offset: TextSize, + fake_ident_token: SyntaxToken, + derive_ctx: Option<(SyntaxNode, SyntaxNode, TextSize, ast::Attr)>, +} - 'expansion: loop { - let parent_item = - |item: &ast::Item| item.syntax().ancestors().skip(1).find_map(ast::Item::cast); - let ancestor_items = iter::successors( - Option::zip( - find_node_at_offset::(&original_file, offset), - find_node_at_offset::(&speculative_file, offset), +pub(super) struct AnalysisResult { + pub(super) analysis: CompletionAnalysis, + pub(super) expected: (Option, Option), + pub(super) qualifier_ctx: QualifierCtx, + pub(super) token: SyntaxToken, + pub(super) offset: TextSize, +} + +pub(super) fn expand_and_analyze( + sema: &Semantics<'_, RootDatabase>, + original_file: SyntaxNode, + speculative_file: SyntaxNode, + offset: TextSize, + original_token: &SyntaxToken, +) -> Option { + // as we insert after the offset, right biased will *always* pick the identifier no matter + // if there is an ident already typed or not + let fake_ident_token = speculative_file.token_at_offset(offset).right_biased()?; + // the relative offset between the cursor and the *identifier* token we are completing on + let relative_offset = offset - fake_ident_token.text_range().start(); + // make the offset point to the start of the original token, as that is what the + // intermediate offsets calculated in expansion always points to + let offset = offset - relative_offset; + let expansion = expand(sema, original_file, speculative_file, offset, fake_ident_token); + // add the relative offset back, so that left_biased finds the proper token + let offset = expansion.offset + relative_offset; + let token = expansion.original_file.token_at_offset(offset).left_biased()?; + + analyze(sema, expansion, original_token, &token).map(|(analysis, expected, qualifier_ctx)| { + AnalysisResult { analysis, expected, qualifier_ctx, token, offset } + }) +} + +/// Expand attributes and macro calls at the current cursor position for both the original file +/// and fake file repeatedly. As soon as one of the two expansions fail we stop so the original +/// and speculative states stay in sync. +fn expand( + sema: &Semantics<'_, RootDatabase>, + mut original_file: SyntaxNode, + mut speculative_file: SyntaxNode, + mut offset: TextSize, + mut fake_ident_token: SyntaxToken, +) -> ExpansionResult { + let _p = profile::span("CompletionContext::expand"); + let mut derive_ctx = None; + + 'expansion: loop { + let parent_item = + |item: &ast::Item| item.syntax().ancestors().skip(1).find_map(ast::Item::cast); + let ancestor_items = iter::successors( + Option::zip( + find_node_at_offset::(&original_file, offset), + find_node_at_offset::(&speculative_file, offset), + ), + |(a, b)| parent_item(a).zip(parent_item(b)), + ); + + // first try to expand attributes as these are always the outermost macro calls + 'ancestors: for (actual_item, item_with_fake_ident) in ancestor_items { + match ( + sema.expand_attr_macro(&actual_item), + sema.speculative_expand_attr_macro( + &actual_item, + &item_with_fake_ident, + fake_ident_token.clone(), ), - |(a, b)| parent_item(a).zip(parent_item(b)), - ); - - // first try to expand attributes as these are always the outermost macro calls - 'ancestors: for (actual_item, item_with_fake_ident) in ancestor_items { - match ( - self.sema.expand_attr_macro(&actual_item), - self.sema.speculative_expand_attr_macro( - &actual_item, - &item_with_fake_ident, - fake_ident_token.clone(), - ), - ) { - // maybe parent items have attributes, so continue walking the ancestors - (None, None) => continue 'ancestors, - // successful expansions - (Some(actual_expansion), Some((fake_expansion, fake_mapped_token))) => { - let new_offset = fake_mapped_token.text_range().start(); - if new_offset > actual_expansion.text_range().end() { - // offset outside of bounds from the original expansion, - // stop here to prevent problems from happening - break 'expansion; - } - original_file = actual_expansion; - speculative_file = fake_expansion; - fake_ident_token = fake_mapped_token; - offset = new_offset; - continue 'expansion; - } - // exactly one expansion failed, inconsistent state so stop expanding completely - _ => break 'expansion, - } - } - - // No attributes have been expanded, so look for macro_call! token trees or derive token trees - let orig_tt = match find_node_at_offset::(&original_file, offset) { - Some(it) => it, - None => break 'expansion, - }; - let spec_tt = match find_node_at_offset::(&speculative_file, offset) { - Some(it) => it, - None => break 'expansion, - }; - - // Expand pseudo-derive expansion - if let (Some(orig_attr), Some(spec_attr)) = ( - orig_tt.syntax().parent().and_then(ast::Meta::cast).and_then(|it| it.parent_attr()), - spec_tt.syntax().parent().and_then(ast::Meta::cast).and_then(|it| it.parent_attr()), ) { - if let (Some(actual_expansion), Some((fake_expansion, fake_mapped_token))) = ( - self.sema.expand_derive_as_pseudo_attr_macro(&orig_attr), - self.sema.speculative_expand_derive_as_pseudo_attr_macro( - &orig_attr, - &spec_attr, - fake_ident_token.clone(), - ), - ) { - derive_ctx = Some(( - actual_expansion, - fake_expansion, - fake_mapped_token.text_range().start(), - orig_attr, - )); - } - // at this point we won't have any more successful expansions, so stop - break 'expansion; - } - - // Expand fn-like macro calls - if let (Some(actual_macro_call), Some(macro_call_with_fake_ident)) = ( - orig_tt.syntax().ancestors().find_map(ast::MacroCall::cast), - spec_tt.syntax().ancestors().find_map(ast::MacroCall::cast), - ) { - let mac_call_path0 = actual_macro_call.path().as_ref().map(|s| s.syntax().text()); - let mac_call_path1 = - macro_call_with_fake_ident.path().as_ref().map(|s| s.syntax().text()); - - // inconsistent state, stop expanding - if mac_call_path0 != mac_call_path1 { - break 'expansion; - } - let speculative_args = match macro_call_with_fake_ident.token_tree() { - Some(tt) => tt, - None => break 'expansion, - }; - - match ( - self.sema.expand(&actual_macro_call), - self.sema.speculative_expand( - &actual_macro_call, - &speculative_args, - fake_ident_token.clone(), - ), - ) { - // successful expansions - (Some(actual_expansion), Some((fake_expansion, fake_mapped_token))) => { - let new_offset = fake_mapped_token.text_range().start(); - if new_offset > actual_expansion.text_range().end() { - // offset outside of bounds from the original expansion, - // stop here to prevent problems from happening - break 'expansion; - } - original_file = actual_expansion; - speculative_file = fake_expansion; - fake_ident_token = fake_mapped_token; - offset = new_offset; - continue 'expansion; + // maybe parent items have attributes, so continue walking the ancestors + (None, None) => continue 'ancestors, + // successful expansions + (Some(actual_expansion), Some((fake_expansion, fake_mapped_token))) => { + let new_offset = fake_mapped_token.text_range().start(); + if new_offset > actual_expansion.text_range().end() { + // offset outside of bounds from the original expansion, + // stop here to prevent problems from happening + break 'expansion; } - // at least on expansion failed, we won't have anything to expand from this point - // onwards so break out - _ => break 'expansion, + original_file = actual_expansion; + speculative_file = fake_expansion; + fake_ident_token = fake_mapped_token; + offset = new_offset; + continue 'expansion; } + // exactly one expansion failed, inconsistent state so stop expanding completely + _ => break 'expansion, } + } - // none of our states have changed so stop the loop + // No attributes have been expanded, so look for macro_call! token trees or derive token trees + let orig_tt = match find_node_at_offset::(&original_file, offset) { + Some(it) => it, + None => break 'expansion, + }; + let spec_tt = match find_node_at_offset::(&speculative_file, offset) { + Some(it) => it, + None => break 'expansion, + }; + + // Expand pseudo-derive expansion + if let (Some(orig_attr), Some(spec_attr)) = ( + orig_tt.syntax().parent().and_then(ast::Meta::cast).and_then(|it| it.parent_attr()), + spec_tt.syntax().parent().and_then(ast::Meta::cast).and_then(|it| it.parent_attr()), + ) { + if let (Some(actual_expansion), Some((fake_expansion, fake_mapped_token))) = ( + sema.expand_derive_as_pseudo_attr_macro(&orig_attr), + sema.speculative_expand_derive_as_pseudo_attr_macro( + &orig_attr, + &spec_attr, + fake_ident_token.clone(), + ), + ) { + derive_ctx = Some(( + actual_expansion, + fake_expansion, + fake_mapped_token.text_range().start(), + orig_attr, + )); + } + // at this point we won't have any more successful expansions, so stop break 'expansion; } - self.analyze(&original_file, speculative_file, offset, derive_ctx) + // Expand fn-like macro calls + if let (Some(actual_macro_call), Some(macro_call_with_fake_ident)) = ( + orig_tt.syntax().ancestors().find_map(ast::MacroCall::cast), + spec_tt.syntax().ancestors().find_map(ast::MacroCall::cast), + ) { + let mac_call_path0 = actual_macro_call.path().as_ref().map(|s| s.syntax().text()); + let mac_call_path1 = + macro_call_with_fake_ident.path().as_ref().map(|s| s.syntax().text()); + + // inconsistent state, stop expanding + if mac_call_path0 != mac_call_path1 { + break 'expansion; + } + let speculative_args = match macro_call_with_fake_ident.token_tree() { + Some(tt) => tt, + None => break 'expansion, + }; + + match ( + sema.expand(&actual_macro_call), + sema.speculative_expand( + &actual_macro_call, + &speculative_args, + fake_ident_token.clone(), + ), + ) { + // successful expansions + (Some(actual_expansion), Some((fake_expansion, fake_mapped_token))) => { + let new_offset = fake_mapped_token.text_range().start(); + if new_offset > actual_expansion.text_range().end() { + // offset outside of bounds from the original expansion, + // stop here to prevent problems from happening + break 'expansion; + } + original_file = actual_expansion; + speculative_file = fake_expansion; + fake_ident_token = fake_mapped_token; + offset = new_offset; + continue 'expansion; + } + // at least on expansion failed, we won't have anything to expand from this point + // onwards so break out + _ => break 'expansion, + } + } + + // none of our states have changed so stop the loop + break 'expansion; + } + ExpansionResult { original_file, speculative_file, offset, fake_ident_token, derive_ctx } +} + +/// Fill the completion context, this is what does semantic reasoning about the surrounding context +/// of the completion location. +fn analyze( + sema: &Semantics<'_, RootDatabase>, + expansion_result: ExpansionResult, + original_token: &SyntaxToken, + self_token: &SyntaxToken, +) -> Option<(CompletionAnalysis, (Option, Option), QualifierCtx)> { + let _p = profile::span("CompletionContext::analyze"); + let ExpansionResult { original_file, speculative_file, offset, fake_ident_token, derive_ctx } = + expansion_result; + let syntax_element = NodeOrToken::Token(fake_ident_token); + if is_in_token_of_for_loop(syntax_element.clone()) { + // for pat $0 + // there is nothing to complete here except `in` keyword + // don't bother populating the context + // FIXME: the completion calculations should end up good enough + // such that this special case becomes unnecessary + return None; } - /// Calculate the expected type and name of the cursor position. - fn expected_type_and_name( - &self, - name_like: &ast::NameLike, - ) -> (Option, Option) { - let mut node = match self.token.parent() { - Some(it) => it, - None => return (None, None), - }; - - let strip_refs = |mut ty: Type| match name_like { - ast::NameLike::NameRef(n) => { - let p = match n.syntax().parent() { - Some(it) => it, - None => return ty, + // Overwrite the path kind for derives + if let Some((original_file, file_with_fake_ident, offset, origin_attr)) = derive_ctx { + if let Some(ast::NameLike::NameRef(name_ref)) = + find_node_at_offset(&file_with_fake_ident, offset) + { + let parent = name_ref.syntax().parent()?; + let (mut nameref_ctx, _) = classify_name_ref(&sema, &original_file, name_ref, parent)?; + if let NameRefKind::Path(path_ctx) = &mut nameref_ctx.kind { + path_ctx.kind = PathKind::Derive { + existing_derives: sema + .resolve_derive_macro(&origin_attr) + .into_iter() + .flatten() + .flatten() + .collect(), }; - let top_syn = match_ast! { - match p { - ast::FieldExpr(e) => e - .syntax() - .ancestors() - .map_while(ast::FieldExpr::cast) - .last() - .map(|it| it.syntax().clone()), - ast::PathSegment(e) => e - .syntax() - .ancestors() - .skip(1) - .take_while(|it| ast::Path::can_cast(it.kind()) || ast::PathExpr::can_cast(it.kind())) - .find_map(ast::PathExpr::cast) - .map(|it| it.syntax().clone()), - _ => None - } - }; - let top_syn = match top_syn { - Some(it) => it, - None => return ty, - }; - for _ in top_syn.ancestors().skip(1).map_while(ast::RefExpr::cast) { - cov_mark::hit!(expected_type_fn_param_ref); - ty = ty.strip_reference(); - } - ty } - _ => ty, - }; + return Some(( + CompletionAnalysis::NameRef(nameref_ctx), + (None, None), + QualifierCtx::default(), + )); + } + return None; + } - loop { - break match_ast! { - match node { - ast::LetStmt(it) => { - cov_mark::hit!(expected_type_let_with_leading_char); - cov_mark::hit!(expected_type_let_without_leading_char); - let ty = it.pat() - .and_then(|pat| self.sema.type_of_pat(&pat)) - .or_else(|| it.initializer().and_then(|it| self.sema.type_of_expr(&it))) - .map(TypeInfo::original); - let name = match it.pat() { - Some(ast::Pat::IdentPat(ident)) => ident.name().map(NameOrNameRef::Name), - Some(_) | None => None, - }; - - (ty, name) - }, - ast::LetExpr(it) => { - cov_mark::hit!(expected_type_if_let_without_leading_char); - let ty = it.pat() - .and_then(|pat| self.sema.type_of_pat(&pat)) - .or_else(|| it.expr().and_then(|it| self.sema.type_of_expr(&it))) - .map(TypeInfo::original); - (ty, None) - }, - ast::ArgList(_) => { - cov_mark::hit!(expected_type_fn_param); - ActiveParameter::at_token( - &self.sema, - self.token.clone(), - ).map(|ap| { - let name = ap.ident().map(NameOrNameRef::Name); - - let ty = strip_refs(ap.ty); - (Some(ty), name) - }) - .unwrap_or((None, None)) - }, - ast::RecordExprFieldList(it) => { - // wouldn't try {} be nice... - (|| { - if self.token.kind() == T![..] - || self.token.prev_token().map(|t| t.kind()) == Some(T![..]) - { - cov_mark::hit!(expected_type_struct_func_update); - let record_expr = it.syntax().parent().and_then(ast::RecordExpr::cast)?; - let ty = self.sema.type_of_expr(&record_expr.into())?; - Some(( - Some(ty.original), - None - )) - } else { - cov_mark::hit!(expected_type_struct_field_without_leading_char); - let expr_field = self.token.prev_sibling_or_token()? - .into_node() - .and_then(ast::RecordExprField::cast)?; - let (_, _, ty) = self.sema.resolve_record_field(&expr_field)?; - Some(( - Some(ty), - expr_field.field_name().map(NameOrNameRef::NameRef), - )) - } - })().unwrap_or((None, None)) - }, - ast::RecordExprField(it) => { - if let Some(expr) = it.expr() { - cov_mark::hit!(expected_type_struct_field_with_leading_char); - ( - self.sema.type_of_expr(&expr).map(TypeInfo::original), - it.field_name().map(NameOrNameRef::NameRef), - ) - } else { - cov_mark::hit!(expected_type_struct_field_followed_by_comma); - let ty = self.sema.resolve_record_field(&it) - .map(|(_, _, ty)| ty); - ( - ty, - it.field_name().map(NameOrNameRef::NameRef), - ) - } - }, - // match foo { $0 } - // match foo { ..., pat => $0 } - ast::MatchExpr(it) => { - let on_arrow = previous_non_trivia_token(self.token.clone()).map_or(false, |it| T![=>] == it.kind()); - - let ty = if on_arrow { - // match foo { ..., pat => $0 } - cov_mark::hit!(expected_type_match_arm_body_without_leading_char); - cov_mark::hit!(expected_type_match_arm_body_with_leading_char); - self.sema.type_of_expr(&it.into()) - } else { - // match foo { $0 } - cov_mark::hit!(expected_type_match_arm_without_leading_char); - it.expr().and_then(|e| self.sema.type_of_expr(&e)) - }.map(TypeInfo::original); - (ty, None) - }, - ast::IfExpr(it) => { - let ty = it.condition() - .and_then(|e| self.sema.type_of_expr(&e)) - .map(TypeInfo::original); - (ty, None) - }, - ast::IdentPat(it) => { - cov_mark::hit!(expected_type_if_let_with_leading_char); - cov_mark::hit!(expected_type_match_arm_with_leading_char); - let ty = self.sema.type_of_pat(&ast::Pat::from(it)).map(TypeInfo::original); - (ty, None) - }, - ast::Fn(it) => { - cov_mark::hit!(expected_type_fn_ret_with_leading_char); - cov_mark::hit!(expected_type_fn_ret_without_leading_char); - let def = self.sema.to_def(&it); - (def.map(|def| def.ret_type(self.db)), None) - }, - ast::ClosureExpr(it) => { - let ty = self.sema.type_of_expr(&it.into()); - ty.and_then(|ty| ty.original.as_callable(self.db)) - .map(|c| (Some(c.return_type()), None)) - .unwrap_or((None, None)) - }, - ast::ParamList(_) => (None, None), - ast::Stmt(_) => (None, None), - ast::Item(_) => (None, None), - _ => { - match node.parent() { - Some(n) => { - node = n; - continue; - }, - None => (None, None), - } - }, + let name_like = match find_node_at_offset(&speculative_file, offset) { + Some(it) => it, + None => { + let analysis = if let Some(original) = ast::String::cast(original_token.clone()) { + CompletionAnalysis::String { + original, + expanded: ast::String::cast(self_token.clone()), + } + } else { + // Fix up trailing whitespace problem + // #[attr(foo = $0 + let token = syntax::algo::skip_trivia_token(self_token.clone(), Direction::Prev)?; + let p = token.parent()?; + if p.kind() == SyntaxKind::TOKEN_TREE + && p.ancestors().any(|it| it.kind() == SyntaxKind::META) + { + let colon_prefix = previous_non_trivia_token(self_token.clone()) + .map_or(false, |it| T![:] == it.kind()); + CompletionAnalysis::UnexpandedAttrTT { + fake_attribute_under_caret: syntax_element + .ancestors() + .find_map(ast::Attr::cast), + colon_prefix, + } + } else { + return None; } }; + return Some((analysis, (None, None), QualifierCtx::default())); } - } - - /// Fill the completion context, this is what does semantic reasoning about the surrounding context - /// of the completion location. - fn analyze( - &mut self, - original_file: &SyntaxNode, - file_with_fake_ident: SyntaxNode, - offset: TextSize, - derive_ctx: Option<(SyntaxNode, SyntaxNode, TextSize, ast::Attr)>, - ) -> Option { - let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased()?; - let syntax_element = NodeOrToken::Token(fake_ident_token); - if is_in_token_of_for_loop(syntax_element.clone()) { - // for pat $0 - // there is nothing to complete here except `in` keyword - // don't bother populating the context - // FIXME: the completion calculations should end up good enough - // such that this special case becomes unnecessary - return None; + }; + let expected = expected_type_and_name(sema, &self_token, &name_like); + let mut qual_ctx = QualifierCtx::default(); + let analysis = match name_like { + ast::NameLike::Lifetime(lifetime) => { + CompletionAnalysis::Lifetime(classify_lifetime(sema, &original_file, lifetime)?) } + ast::NameLike::NameRef(name_ref) => { + let parent = name_ref.syntax().parent()?; + let (nameref_ctx, qualifier_ctx) = + classify_name_ref(sema, &original_file, name_ref, parent.clone())?; + qual_ctx = qualifier_ctx; + CompletionAnalysis::NameRef(nameref_ctx) + } + ast::NameLike::Name(name) => { + let name_ctx = classify_name(sema, &original_file, name)?; + CompletionAnalysis::Name(name_ctx) + } + }; + Some((analysis, expected, qual_ctx)) +} - // Overwrite the path kind for derives - if let Some((original_file, file_with_fake_ident, offset, origin_attr)) = derive_ctx { - if let Some(ast::NameLike::NameRef(name_ref)) = - find_node_at_offset(&file_with_fake_ident, offset) - { - let parent = name_ref.syntax().parent()?; - let (mut nameref_ctx, _) = - Self::classify_name_ref(&self.sema, &original_file, name_ref, parent)?; - if let NameRefKind::Path(path_ctx) = &mut nameref_ctx.kind { - path_ctx.kind = PathKind::Derive { - existing_derives: self - .sema - .resolve_derive_macro(&origin_attr) - .into_iter() - .flatten() - .flatten() - .collect(), - }; +/// Calculate the expected type and name of the cursor position. +fn expected_type_and_name( + sema: &Semantics<'_, RootDatabase>, + token: &SyntaxToken, + name_like: &ast::NameLike, +) -> (Option, Option) { + let mut node = match token.parent() { + Some(it) => it, + None => return (None, None), + }; + + let strip_refs = |mut ty: Type| match name_like { + ast::NameLike::NameRef(n) => { + let p = match n.syntax().parent() { + Some(it) => it, + None => return ty, + }; + let top_syn = match_ast! { + match p { + ast::FieldExpr(e) => e + .syntax() + .ancestors() + .map_while(ast::FieldExpr::cast) + .last() + .map(|it| it.syntax().clone()), + ast::PathSegment(e) => e + .syntax() + .ancestors() + .skip(1) + .take_while(|it| ast::Path::can_cast(it.kind()) || ast::PathExpr::can_cast(it.kind())) + .find_map(ast::PathExpr::cast) + .map(|it| it.syntax().clone()), + _ => None } - return Some(CompletionAnalysis::NameRef(nameref_ctx)); + }; + let top_syn = match top_syn { + Some(it) => it, + None => return ty, + }; + for _ in top_syn.ancestors().skip(1).map_while(ast::RefExpr::cast) { + cov_mark::hit!(expected_type_fn_param_ref); + ty = ty.strip_reference(); } - return None; + ty } + _ => ty, + }; - let name_like = match find_node_at_offset(&file_with_fake_ident, offset) { - Some(it) => it, - None => { - let analysis = - if let Some(original) = ast::String::cast(self.original_token.clone()) { - CompletionAnalysis::String { - original, - expanded: ast::String::cast(self.token.clone()), - } - } else { - // Fix up trailing whitespace problem - // #[attr(foo = $0 - let token = - syntax::algo::skip_trivia_token(self.token.clone(), Direction::Prev)?; - let p = token.parent()?; - if p.kind() == SyntaxKind::TOKEN_TREE - && p.ancestors().any(|it| it.kind() == SyntaxKind::META) - { - let colon_prefix = previous_non_trivia_token(self.token.clone()) - .map_or(false, |it| T![:] == it.kind()); - CompletionAnalysis::UnexpandedAttrTT { - fake_attribute_under_caret: syntax_element - .ancestors() - .find_map(ast::Attr::cast), - colon_prefix, - } - } else { - return None; - } + loop { + break match_ast! { + match node { + ast::LetStmt(it) => { + cov_mark::hit!(expected_type_let_with_leading_char); + cov_mark::hit!(expected_type_let_without_leading_char); + let ty = it.pat() + .and_then(|pat| sema.type_of_pat(&pat)) + .or_else(|| it.initializer().and_then(|it| sema.type_of_expr(&it))) + .map(TypeInfo::original); + let name = match it.pat() { + Some(ast::Pat::IdentPat(ident)) => ident.name().map(NameOrNameRef::Name), + Some(_) | None => None, }; - return Some(analysis); - } - }; - (self.expected_type, self.expected_name) = self.expected_type_and_name(&name_like); - let analysis = match name_like { - ast::NameLike::Lifetime(lifetime) => CompletionAnalysis::Lifetime( - Self::classify_lifetime(&self.sema, original_file, lifetime)?, - ), - ast::NameLike::NameRef(name_ref) => { - let parent = name_ref.syntax().parent()?; - let (nameref_ctx, qualifier_ctx) = - Self::classify_name_ref(&self.sema, &original_file, name_ref, parent.clone())?; - self.qualifier_ctx = qualifier_ctx; - CompletionAnalysis::NameRef(nameref_ctx) - } - ast::NameLike::Name(name) => { - let name_ctx = Self::classify_name(&self.sema, original_file, name)?; - CompletionAnalysis::Name(name_ctx) - } - }; - Some(analysis) - } - - fn classify_lifetime( - _sema: &Semantics<'_, RootDatabase>, - original_file: &SyntaxNode, - lifetime: ast::Lifetime, - ) -> Option { - let parent = lifetime.syntax().parent()?; - if parent.kind() == SyntaxKind::ERROR { - return None; - } - - let kind = match_ast! { - match parent { - ast::LifetimeParam(param) => LifetimeKind::LifetimeParam { - is_decl: param.lifetime().as_ref() == Some(&lifetime), - param + (ty, name) }, - ast::BreakExpr(_) => LifetimeKind::LabelRef, - ast::ContinueExpr(_) => LifetimeKind::LabelRef, - ast::Label(_) => LifetimeKind::LabelDef, - _ => LifetimeKind::Lifetime, - } - }; - let lifetime = find_node_at_offset(&original_file, lifetime.syntax().text_range().start()); + ast::LetExpr(it) => { + cov_mark::hit!(expected_type_if_let_without_leading_char); + let ty = it.pat() + .and_then(|pat| sema.type_of_pat(&pat)) + .or_else(|| it.expr().and_then(|it| sema.type_of_expr(&it))) + .map(TypeInfo::original); + (ty, None) + }, + ast::ArgList(_) => { + cov_mark::hit!(expected_type_fn_param); + ActiveParameter::at_token( + &sema, + token.clone(), + ).map(|ap| { + let name = ap.ident().map(NameOrNameRef::Name); - Some(LifetimeContext { lifetime, kind }) - } - - fn classify_name( - sema: &Semantics<'_, RootDatabase>, - original_file: &SyntaxNode, - name: ast::Name, - ) -> Option { - let parent = name.syntax().parent()?; - let kind = match_ast! { - match parent { - ast::Const(_) => NameKind::Const, - ast::ConstParam(_) => NameKind::ConstParam, - ast::Enum(_) => NameKind::Enum, - ast::Fn(_) => NameKind::Function, - ast::IdentPat(bind_pat) => { - let mut pat_ctx = pattern_context_for(sema, original_file, bind_pat.into()); - if let Some(record_field) = ast::RecordPatField::for_field_name(&name) { - pat_ctx.record_pat = find_node_in_file_compensated(sema, original_file, &record_field.parent_record_pat()); + let ty = strip_refs(ap.ty); + (Some(ty), name) + }) + .unwrap_or((None, None)) + }, + ast::RecordExprFieldList(it) => { + // wouldn't try {} be nice... + (|| { + if token.kind() == T![..] + ||token.prev_token().map(|t| t.kind()) == Some(T![..]) + { + cov_mark::hit!(expected_type_struct_func_update); + let record_expr = it.syntax().parent().and_then(ast::RecordExpr::cast)?; + let ty = sema.type_of_expr(&record_expr.into())?; + Some(( + Some(ty.original), + None + )) + } else { + cov_mark::hit!(expected_type_struct_field_without_leading_char); + let expr_field = token.prev_sibling_or_token()? + .into_node() + .and_then(ast::RecordExprField::cast)?; + let (_, _, ty) = sema.resolve_record_field(&expr_field)?; + Some(( + Some(ty), + expr_field.field_name().map(NameOrNameRef::NameRef), + )) + } + })().unwrap_or((None, None)) + }, + ast::RecordExprField(it) => { + if let Some(expr) = it.expr() { + cov_mark::hit!(expected_type_struct_field_with_leading_char); + ( + sema.type_of_expr(&expr).map(TypeInfo::original), + it.field_name().map(NameOrNameRef::NameRef), + ) + } else { + cov_mark::hit!(expected_type_struct_field_followed_by_comma); + let ty = sema.resolve_record_field(&it) + .map(|(_, _, ty)| ty); + ( + ty, + it.field_name().map(NameOrNameRef::NameRef), + ) + } + }, + // match foo { $0 } + // match foo { ..., pat => $0 } + ast::MatchExpr(it) => { + let on_arrow = previous_non_trivia_token(token.clone()).map_or(false, |it| T![=>] == it.kind()); + + let ty = if on_arrow { + // match foo { ..., pat => $0 } + cov_mark::hit!(expected_type_match_arm_body_without_leading_char); + cov_mark::hit!(expected_type_match_arm_body_with_leading_char); + sema.type_of_expr(&it.into()) + } else { + // match foo { $0 } + cov_mark::hit!(expected_type_match_arm_without_leading_char); + it.expr().and_then(|e| sema.type_of_expr(&e)) + }.map(TypeInfo::original); + (ty, None) + }, + ast::IfExpr(it) => { + let ty = it.condition() + .and_then(|e| sema.type_of_expr(&e)) + .map(TypeInfo::original); + (ty, None) + }, + ast::IdentPat(it) => { + cov_mark::hit!(expected_type_if_let_with_leading_char); + cov_mark::hit!(expected_type_match_arm_with_leading_char); + let ty = sema.type_of_pat(&ast::Pat::from(it)).map(TypeInfo::original); + (ty, None) + }, + ast::Fn(it) => { + cov_mark::hit!(expected_type_fn_ret_with_leading_char); + cov_mark::hit!(expected_type_fn_ret_without_leading_char); + let def = sema.to_def(&it); + (def.map(|def| def.ret_type(sema.db)), None) + }, + ast::ClosureExpr(it) => { + let ty = sema.type_of_expr(&it.into()); + ty.and_then(|ty| ty.original.as_callable(sema.db)) + .map(|c| (Some(c.return_type()), None)) + .unwrap_or((None, None)) + }, + ast::ParamList(_) => (None, None), + ast::Stmt(_) => (None, None), + ast::Item(_) => (None, None), + _ => { + match node.parent() { + Some(n) => { + node = n; + continue; + }, + None => (None, None), } - - NameKind::IdentPat(pat_ctx) }, - ast::MacroDef(_) => NameKind::MacroDef, - ast::MacroRules(_) => NameKind::MacroRules, - ast::Module(module) => NameKind::Module(module), - ast::RecordField(_) => NameKind::RecordField, - ast::Rename(_) => NameKind::Rename, - ast::SelfParam(_) => NameKind::SelfParam, - ast::Static(_) => NameKind::Static, - ast::Struct(_) => NameKind::Struct, - ast::Trait(_) => NameKind::Trait, - ast::TypeAlias(_) => NameKind::TypeAlias, - ast::TypeParam(_) => NameKind::TypeParam, - ast::Union(_) => NameKind::Union, - ast::Variant(_) => NameKind::Variant, - _ => return None, } }; - let name = find_node_at_offset(&original_file, name.syntax().text_range().start()); - Some(NameContext { name, kind }) + } +} + +fn classify_lifetime( + _sema: &Semantics<'_, RootDatabase>, + original_file: &SyntaxNode, + lifetime: ast::Lifetime, +) -> Option { + let parent = lifetime.syntax().parent()?; + if parent.kind() == SyntaxKind::ERROR { + return None; } - fn classify_name_ref( - sema: &Semantics<'_, RootDatabase>, - original_file: &SyntaxNode, - name_ref: ast::NameRef, - parent: SyntaxNode, - ) -> Option<(NameRefContext, QualifierCtx)> { - let nameref = find_node_at_offset(&original_file, name_ref.syntax().text_range().start()); + let kind = match_ast! { + match parent { + ast::LifetimeParam(param) => LifetimeKind::LifetimeParam { + is_decl: param.lifetime().as_ref() == Some(&lifetime), + param + }, + ast::BreakExpr(_) => LifetimeKind::LabelRef, + ast::ContinueExpr(_) => LifetimeKind::LabelRef, + ast::Label(_) => LifetimeKind::LabelDef, + _ => LifetimeKind::Lifetime, + } + }; + let lifetime = find_node_at_offset(&original_file, lifetime.syntax().text_range().start()); - let make_res = - |kind| (NameRefContext { nameref: nameref.clone(), kind }, Default::default()); + Some(LifetimeContext { lifetime, kind }) +} - if let Some(record_field) = ast::RecordExprField::for_field_name(&name_ref) { - let dot_prefix = previous_non_trivia_token(name_ref.syntax().clone()) - .map_or(false, |it| T![.] == it.kind()); +fn classify_name( + sema: &Semantics<'_, RootDatabase>, + original_file: &SyntaxNode, + name: ast::Name, +) -> Option { + let parent = name.syntax().parent()?; + let kind = match_ast! { + match parent { + ast::Const(_) => NameKind::Const, + ast::ConstParam(_) => NameKind::ConstParam, + ast::Enum(_) => NameKind::Enum, + ast::Fn(_) => NameKind::Function, + ast::IdentPat(bind_pat) => { + let mut pat_ctx = pattern_context_for(sema, original_file, bind_pat.into()); + if let Some(record_field) = ast::RecordPatField::for_field_name(&name) { + pat_ctx.record_pat = find_node_in_file_compensated(sema, original_file, &record_field.parent_record_pat()); + } - return find_node_in_file_compensated( + NameKind::IdentPat(pat_ctx) + }, + ast::MacroDef(_) => NameKind::MacroDef, + ast::MacroRules(_) => NameKind::MacroRules, + ast::Module(module) => NameKind::Module(module), + ast::RecordField(_) => NameKind::RecordField, + ast::Rename(_) => NameKind::Rename, + ast::SelfParam(_) => NameKind::SelfParam, + ast::Static(_) => NameKind::Static, + ast::Struct(_) => NameKind::Struct, + ast::Trait(_) => NameKind::Trait, + ast::TypeAlias(_) => NameKind::TypeAlias, + ast::TypeParam(_) => NameKind::TypeParam, + ast::Union(_) => NameKind::Union, + ast::Variant(_) => NameKind::Variant, + _ => return None, + } + }; + let name = find_node_at_offset(&original_file, name.syntax().text_range().start()); + Some(NameContext { name, kind }) +} + +fn classify_name_ref( + sema: &Semantics<'_, RootDatabase>, + original_file: &SyntaxNode, + name_ref: ast::NameRef, + parent: SyntaxNode, +) -> Option<(NameRefContext, QualifierCtx)> { + let nameref = find_node_at_offset(&original_file, name_ref.syntax().text_range().start()); + + let make_res = |kind| (NameRefContext { nameref: nameref.clone(), kind }, Default::default()); + + if let Some(record_field) = ast::RecordExprField::for_field_name(&name_ref) { + let dot_prefix = previous_non_trivia_token(name_ref.syntax().clone()) + .map_or(false, |it| T![.] == it.kind()); + + return find_node_in_file_compensated( + sema, + original_file, + &record_field.parent_record_lit(), + ) + .map(|expr| NameRefKind::RecordExpr { expr, dot_prefix }) + .map(make_res); + } + if let Some(record_field) = ast::RecordPatField::for_field_name_ref(&name_ref) { + let kind = NameRefKind::Pattern(PatternContext { + param_ctx: None, + has_type_ascription: false, + ref_token: None, + mut_token: None, + record_pat: find_node_in_file_compensated( sema, original_file, - &record_field.parent_record_lit(), + &record_field.parent_record_pat(), + ), + ..pattern_context_for( + sema, + original_file, + record_field.parent_record_pat().clone().into(), ) - .map(|expr| NameRefKind::RecordExpr { expr, dot_prefix }) - .map(make_res); - } - if let Some(record_field) = ast::RecordPatField::for_field_name_ref(&name_ref) { - let kind = NameRefKind::Pattern(PatternContext { - param_ctx: None, - has_type_ascription: false, - ref_token: None, - mut_token: None, - record_pat: find_node_in_file_compensated( - sema, - original_file, - &record_field.parent_record_pat(), - ), - ..pattern_context_for( - sema, - original_file, - record_field.parent_record_pat().clone().into(), - ) - }); - return Some(make_res(kind)); - } + }); + return Some(make_res(kind)); + } - let segment = match_ast! { + let segment = match_ast! { + match parent { + ast::PathSegment(segment) => segment, + ast::FieldExpr(field) => { + let receiver = find_opt_node_in_file(original_file, field.expr()); + let receiver_is_ambiguous_float_literal = match &receiver { + Some(ast::Expr::Literal(l)) => matches! { + l.kind(), + ast::LiteralKind::FloatNumber { .. } if l.syntax().last_token().map_or(false, |it| it.text().ends_with('.')) + }, + _ => false, + }; + let kind = NameRefKind::DotAccess(DotAccess { + receiver_ty: receiver.as_ref().and_then(|it| sema.type_of_expr(it)), + kind: DotAccessKind::Field { receiver_is_ambiguous_float_literal }, + receiver + }); + return Some(make_res(kind)); + }, + ast::MethodCallExpr(method) => { + let receiver = find_opt_node_in_file(original_file, method.receiver()); + let kind = NameRefKind::DotAccess(DotAccess { + receiver_ty: receiver.as_ref().and_then(|it| sema.type_of_expr(it)), + kind: DotAccessKind::Method { has_parens: method.arg_list().map_or(false, |it| it.l_paren_token().is_some()) }, + receiver + }); + return Some(make_res(kind)); + }, + _ => return None, + } + }; + + let path = segment.parent_path(); + let original_path = find_node_in_file_compensated(sema, original_file, &path); + + let mut path_ctx = PathCompletionCtx { + has_call_parens: false, + has_macro_bang: false, + qualified: Qualified::No, + parent: None, + path: path.clone(), + original_path, + kind: PathKind::Item { kind: ItemListKind::SourceFile }, + has_type_args: false, + use_tree_parent: false, + }; + + let is_in_block = |it: &SyntaxNode| { + it.parent() + .map(|node| { + ast::ExprStmt::can_cast(node.kind()) || ast::StmtList::can_cast(node.kind()) + }) + .unwrap_or(false) + }; + let func_update_record = |syn: &SyntaxNode| { + if let Some(record_expr) = syn.ancestors().nth(2).and_then(ast::RecordExpr::cast) { + find_node_in_file_compensated(sema, original_file, &record_expr) + } else { + None + } + }; + let after_if_expr = |node: SyntaxNode| { + let prev_expr = (|| { + let prev_sibling = non_trivia_sibling(node.into(), Direction::Prev)?.into_node()?; + ast::ExprStmt::cast(prev_sibling)?.expr() + })(); + matches!(prev_expr, Some(ast::Expr::IfExpr(_))) + }; + + // We do not want to generate path completions when we are sandwiched between an item decl signature and its body. + // ex. trait Foo $0 {} + // in these cases parser recovery usually kicks in for our inserted identifier, causing it + // to either be parsed as an ExprStmt or a MacroCall, depending on whether it is in a block + // expression or an item list. + // The following code checks if the body is missing, if it is we either cut off the body + // from the item or it was missing in the first place + let inbetween_body_and_decl_check = |node: SyntaxNode| { + if let Some(NodeOrToken::Node(n)) = + syntax::algo::non_trivia_sibling(node.into(), syntax::Direction::Prev) + { + if let Some(item) = ast::Item::cast(n) { + let is_inbetween = match &item { + ast::Item::Const(it) => it.body().is_none(), + ast::Item::Enum(it) => it.variant_list().is_none(), + ast::Item::ExternBlock(it) => it.extern_item_list().is_none(), + ast::Item::Fn(it) => it.body().is_none(), + ast::Item::Impl(it) => it.assoc_item_list().is_none(), + ast::Item::Module(it) => it.item_list().is_none(), + ast::Item::Static(it) => it.body().is_none(), + ast::Item::Struct(it) => it.field_list().is_none(), + ast::Item::Trait(it) => it.assoc_item_list().is_none(), + ast::Item::TypeAlias(it) => it.ty().is_none(), + ast::Item::Union(it) => it.record_field_list().is_none(), + _ => false, + }; + if is_inbetween { + return Some(item); + } + } + } + None + }; + + let type_location = |node: &SyntaxNode| { + let parent = node.parent()?; + let res = match_ast! { match parent { - ast::PathSegment(segment) => segment, - ast::FieldExpr(field) => { - let receiver = find_opt_node_in_file(original_file, field.expr()); - let receiver_is_ambiguous_float_literal = match &receiver { - Some(ast::Expr::Literal(l)) => matches! { - l.kind(), - ast::LiteralKind::FloatNumber { .. } if l.syntax().last_token().map_or(false, |it| it.text().ends_with('.')) - }, - _ => false, + ast::Const(it) => { + let name = find_opt_node_in_file(original_file, it.name())?; + let original = ast::Const::cast(name.syntax().parent()?)?; + TypeLocation::TypeAscription(TypeAscriptionTarget::Const(original.body())) + }, + ast::RetType(it) => { + if it.thin_arrow_token().is_none() { + return None; + } + let parent = match ast::Fn::cast(parent.parent()?) { + Some(x) => x.param_list(), + None => ast::ClosureExpr::cast(parent.parent()?)?.param_list(), }; - let kind = NameRefKind::DotAccess(DotAccess { - receiver_ty: receiver.as_ref().and_then(|it| sema.type_of_expr(it)), - kind: DotAccessKind::Field { receiver_is_ambiguous_float_literal }, - receiver - }); - return Some(make_res(kind)); + + let parent = find_opt_node_in_file(original_file, parent)?.syntax().parent()?; + TypeLocation::TypeAscription(TypeAscriptionTarget::RetType(match_ast! { + match parent { + ast::ClosureExpr(it) => { + it.body() + }, + ast::Fn(it) => { + it.body().map(ast::Expr::BlockExpr) + }, + _ => return None, + } + })) }, - ast::MethodCallExpr(method) => { - let receiver = find_opt_node_in_file(original_file, method.receiver()); - let kind = NameRefKind::DotAccess(DotAccess { - receiver_ty: receiver.as_ref().and_then(|it| sema.type_of_expr(it)), - kind: DotAccessKind::Method { has_parens: method.arg_list().map_or(false, |it| it.l_paren_token().is_some()) }, - receiver - }); - return Some(make_res(kind)); + ast::Param(it) => { + if it.colon_token().is_none() { + return None; + } + TypeLocation::TypeAscription(TypeAscriptionTarget::FnParam(find_opt_node_in_file(original_file, it.pat()))) }, + ast::LetStmt(it) => { + if it.colon_token().is_none() { + return None; + } + TypeLocation::TypeAscription(TypeAscriptionTarget::Let(find_opt_node_in_file(original_file, it.pat()))) + }, + ast::Impl(it) => { + match it.trait_() { + Some(t) if t.syntax() == node => TypeLocation::ImplTrait, + _ => match it.self_ty() { + Some(t) if t.syntax() == node => TypeLocation::ImplTarget, + _ => return None, + }, + } + }, + ast::TypeBound(_) => TypeLocation::TypeBound, + // is this case needed? + ast::TypeBoundList(_) => TypeLocation::TypeBound, + ast::GenericArg(it) => TypeLocation::GenericArgList(find_opt_node_in_file_compensated(sema, original_file, it.syntax().parent().and_then(ast::GenericArgList::cast))), + // is this case needed? + ast::GenericArgList(it) => TypeLocation::GenericArgList(find_opt_node_in_file_compensated(sema, original_file, Some(it))), + ast::TupleField(_) => TypeLocation::TupleField, _ => return None, } }; + Some(res) + }; - let path = segment.parent_path(); - let original_path = find_node_in_file_compensated(sema, original_file, &path); - - let mut path_ctx = PathCompletionCtx { - has_call_parens: false, - has_macro_bang: false, - qualified: Qualified::No, - parent: None, - path: path.clone(), - original_path, - kind: PathKind::Item { kind: ItemListKind::SourceFile }, - has_type_args: false, - use_tree_parent: false, - }; - - let is_in_block = |it: &SyntaxNode| { - it.parent() - .map(|node| { - ast::ExprStmt::can_cast(node.kind()) || ast::StmtList::can_cast(node.kind()) - }) - .unwrap_or(false) - }; - let func_update_record = |syn: &SyntaxNode| { - if let Some(record_expr) = syn.ancestors().nth(2).and_then(ast::RecordExpr::cast) { - find_node_in_file_compensated(sema, original_file, &record_expr) + let is_in_condition = |it: &ast::Expr| { + (|| { + let parent = it.syntax().parent()?; + if let Some(expr) = ast::WhileExpr::cast(parent.clone()) { + Some(expr.condition()? == *it) + } else if let Some(expr) = ast::IfExpr::cast(parent) { + Some(expr.condition()? == *it) } else { None } - }; - let after_if_expr = |node: SyntaxNode| { - let prev_expr = (|| { - let prev_sibling = non_trivia_sibling(node.into(), Direction::Prev)?.into_node()?; - ast::ExprStmt::cast(prev_sibling)?.expr() - })(); - matches!(prev_expr, Some(ast::Expr::IfExpr(_))) - }; + })() + .unwrap_or(false) + }; - // We do not want to generate path completions when we are sandwiched between an item decl signature and its body. - // ex. trait Foo $0 {} - // in these cases parser recovery usually kicks in for our inserted identifier, causing it - // to either be parsed as an ExprStmt or a MacroCall, depending on whether it is in a block - // expression or an item list. - // The following code checks if the body is missing, if it is we either cut off the body - // from the item or it was missing in the first place - let inbetween_body_and_decl_check = |node: SyntaxNode| { - if let Some(NodeOrToken::Node(n)) = - syntax::algo::non_trivia_sibling(node.into(), syntax::Direction::Prev) - { - if let Some(item) = ast::Item::cast(n) { - let is_inbetween = match &item { - ast::Item::Const(it) => it.body().is_none(), - ast::Item::Enum(it) => it.variant_list().is_none(), - ast::Item::ExternBlock(it) => it.extern_item_list().is_none(), - ast::Item::Fn(it) => it.body().is_none(), - ast::Item::Impl(it) => it.assoc_item_list().is_none(), - ast::Item::Module(it) => it.item_list().is_none(), - ast::Item::Static(it) => it.body().is_none(), - ast::Item::Struct(it) => it.field_list().is_none(), - ast::Item::Trait(it) => it.assoc_item_list().is_none(), - ast::Item::TypeAlias(it) => it.ty().is_none(), - ast::Item::Union(it) => it.record_field_list().is_none(), - _ => false, - }; - if is_inbetween { - return Some(item); + let make_path_kind_expr = |expr: ast::Expr| { + let it = expr.syntax(); + let in_block_expr = is_in_block(it); + let in_loop_body = is_in_loop_body(it); + let after_if_expr = after_if_expr(it.clone()); + let ref_expr_parent = + path.as_single_name_ref().and_then(|_| it.parent()).and_then(ast::RefExpr::cast); + let (innermost_ret_ty, self_param) = { + let find_ret_ty = |it: SyntaxNode| { + if let Some(item) = ast::Item::cast(it.clone()) { + match item { + ast::Item::Fn(f) => Some(sema.to_def(&f).map(|it| it.ret_type(sema.db))), + ast::Item::MacroCall(_) => None, + _ => Some(None), } - } - } - None - }; - - let type_location = |node: &SyntaxNode| { - let parent = node.parent()?; - let res = match_ast! { - match parent { - ast::Const(it) => { - let name = find_opt_node_in_file(original_file, it.name())?; - let original = ast::Const::cast(name.syntax().parent()?)?; - TypeLocation::TypeAscription(TypeAscriptionTarget::Const(original.body())) - }, - ast::RetType(it) => { - if it.thin_arrow_token().is_none() { - return None; - } - let parent = match ast::Fn::cast(parent.parent()?) { - Some(x) => x.param_list(), - None => ast::ClosureExpr::cast(parent.parent()?)?.param_list(), - }; - - let parent = find_opt_node_in_file(original_file, parent)?.syntax().parent()?; - TypeLocation::TypeAscription(TypeAscriptionTarget::RetType(match_ast! { - match parent { - ast::ClosureExpr(it) => { - it.body() - }, - ast::Fn(it) => { - it.body().map(ast::Expr::BlockExpr) - }, - _ => return None, - } - })) - }, - ast::Param(it) => { - if it.colon_token().is_none() { - return None; - } - TypeLocation::TypeAscription(TypeAscriptionTarget::FnParam(find_opt_node_in_file(original_file, it.pat()))) - }, - ast::LetStmt(it) => { - if it.colon_token().is_none() { - return None; - } - TypeLocation::TypeAscription(TypeAscriptionTarget::Let(find_opt_node_in_file(original_file, it.pat()))) - }, - ast::Impl(it) => { - match it.trait_() { - Some(t) if t.syntax() == node => TypeLocation::ImplTrait, - _ => match it.self_ty() { - Some(t) if t.syntax() == node => TypeLocation::ImplTarget, - _ => return None, - }, - } - }, - ast::TypeBound(_) => TypeLocation::TypeBound, - // is this case needed? - ast::TypeBoundList(_) => TypeLocation::TypeBound, - ast::GenericArg(it) => TypeLocation::GenericArgList(find_opt_node_in_file_compensated(sema, original_file, it.syntax().parent().and_then(ast::GenericArgList::cast))), - // is this case needed? - ast::GenericArgList(it) => TypeLocation::GenericArgList(find_opt_node_in_file_compensated(sema, original_file, Some(it))), - ast::TupleField(_) => TypeLocation::TupleField, - _ => return None, - } - }; - Some(res) - }; - - let is_in_condition = |it: &ast::Expr| { - (|| { - let parent = it.syntax().parent()?; - if let Some(expr) = ast::WhileExpr::cast(parent.clone()) { - Some(expr.condition()? == *it) - } else if let Some(expr) = ast::IfExpr::cast(parent) { - Some(expr.condition()? == *it) } else { - None - } - })() - .unwrap_or(false) - }; - - let make_path_kind_expr = |expr: ast::Expr| { - let it = expr.syntax(); - let in_block_expr = is_in_block(it); - let in_loop_body = is_in_loop_body(it); - let after_if_expr = after_if_expr(it.clone()); - let ref_expr_parent = - path.as_single_name_ref().and_then(|_| it.parent()).and_then(ast::RefExpr::cast); - let (innermost_ret_ty, self_param) = { - let find_ret_ty = |it: SyntaxNode| { - if let Some(item) = ast::Item::cast(it.clone()) { - match item { - ast::Item::Fn(f) => { - Some(sema.to_def(&f).map(|it| it.ret_type(sema.db))) - } - ast::Item::MacroCall(_) => None, - _ => Some(None), - } - } else { - let expr = ast::Expr::cast(it)?; - let callable = match expr { - // FIXME - // ast::Expr::BlockExpr(b) if b.async_token().is_some() || b.try_token().is_some() => sema.type_of_expr(b), - ast::Expr::ClosureExpr(_) => sema.type_of_expr(&expr), - _ => return None, - }; - Some( - callable - .and_then(|c| c.adjusted().as_callable(sema.db)) - .map(|it| it.return_type()), - ) - } - }; - let find_fn_self_param = |it| match it { - ast::Item::Fn(fn_) => { - Some(sema.to_def(&fn_).and_then(|it| it.self_param(sema.db))) - } - ast::Item::MacroCall(_) => None, - _ => Some(None), - }; - - match find_node_in_file_compensated(sema, original_file, &expr) { - Some(it) => { - let innermost_ret_ty = sema - .ancestors_with_macros(it.syntax().clone()) - .find_map(find_ret_ty) - .flatten(); - - let self_param = sema - .ancestors_with_macros(it.syntax().clone()) - .filter_map(ast::Item::cast) - .find_map(find_fn_self_param) - .flatten(); - (innermost_ret_ty, self_param) - } - None => (None, None), + let expr = ast::Expr::cast(it)?; + let callable = match expr { + // FIXME + // ast::Expr::BlockExpr(b) if b.async_token().is_some() || b.try_token().is_some() => sema.type_of_expr(b), + ast::Expr::ClosureExpr(_) => sema.type_of_expr(&expr), + _ => return None, + }; + Some( + callable + .and_then(|c| c.adjusted().as_callable(sema.db)) + .map(|it| it.return_type()), + ) } }; - let is_func_update = func_update_record(it); - let in_condition = is_in_condition(&expr); - let incomplete_let = it - .parent() - .and_then(ast::LetStmt::cast) - .map_or(false, |it| it.semicolon_token().is_none()); - let impl_ = fetch_immediate_impl(sema, original_file, expr.syntax()); - - let in_match_guard = match it.parent().and_then(ast::MatchArm::cast) { - Some(arm) => arm - .fat_arrow_token() - .map_or(true, |arrow| it.text_range().start() < arrow.text_range().start()), - None => false, + let find_fn_self_param = |it| match it { + ast::Item::Fn(fn_) => Some(sema.to_def(&fn_).and_then(|it| it.self_param(sema.db))), + ast::Item::MacroCall(_) => None, + _ => Some(None), }; - PathKind::Expr { - expr_ctx: ExprCtx { - in_block_expr, - in_loop_body, - after_if_expr, - in_condition, - ref_expr_parent, - is_func_update, - innermost_ret_ty, - self_param, - incomplete_let, - impl_, - in_match_guard, - }, + match find_node_in_file_compensated(sema, original_file, &expr) { + Some(it) => { + let innermost_ret_ty = sema + .ancestors_with_macros(it.syntax().clone()) + .find_map(find_ret_ty) + .flatten(); + + let self_param = sema + .ancestors_with_macros(it.syntax().clone()) + .filter_map(ast::Item::cast) + .find_map(find_fn_self_param) + .flatten(); + (innermost_ret_ty, self_param) + } + None => (None, None), } }; - let make_path_kind_type = |ty: ast::Type| { - let location = type_location(ty.syntax()); - PathKind::Type { location: location.unwrap_or(TypeLocation::Other) } + let is_func_update = func_update_record(it); + let in_condition = is_in_condition(&expr); + let incomplete_let = it + .parent() + .and_then(ast::LetStmt::cast) + .map_or(false, |it| it.semicolon_token().is_none()); + let impl_ = fetch_immediate_impl(sema, original_file, expr.syntax()); + + let in_match_guard = match it.parent().and_then(ast::MatchArm::cast) { + Some(arm) => arm + .fat_arrow_token() + .map_or(true, |arrow| it.text_range().start() < arrow.text_range().start()), + None => false, }; - let mut kind_macro_call = |it: ast::MacroCall| { - path_ctx.has_macro_bang = it.excl_token().is_some(); - let parent = it.syntax().parent()?; - // Any path in an item list will be treated as a macro call by the parser - let kind = match_ast! { - match parent { - ast::MacroExpr(expr) => make_path_kind_expr(expr.into()), - ast::MacroPat(it) => PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())}, - ast::MacroType(ty) => make_path_kind_type(ty.into()), - ast::ItemList(_) => PathKind::Item { kind: ItemListKind::Module }, - ast::AssocItemList(_) => PathKind::Item { kind: match parent.parent() { - Some(it) => match_ast! { - match it { - ast::Trait(_) => ItemListKind::Trait, - ast::Impl(it) => if it.trait_().is_some() { - ItemListKind::TraitImpl(find_node_in_file_compensated(sema, original_file, &it)) - } else { - ItemListKind::Impl - }, - _ => return None - } - }, - None => return None, - } }, - ast::ExternItemList(_) => PathKind::Item { kind: ItemListKind::ExternBlock }, - ast::SourceFile(_) => PathKind::Item { kind: ItemListKind::SourceFile }, - _ => return None, - } - }; - Some(kind) - }; - let make_path_kind_attr = |meta: ast::Meta| { - let attr = meta.parent_attr()?; - let kind = attr.kind(); - let attached = attr.syntax().parent()?; - let is_trailing_outer_attr = kind != AttrKind::Inner - && non_trivia_sibling(attr.syntax().clone().into(), syntax::Direction::Next) - .is_none(); - let annotated_item_kind = - if is_trailing_outer_attr { None } else { Some(attached.kind()) }; - Some(PathKind::Attr { attr_ctx: AttrCtx { kind, annotated_item_kind } }) - }; + PathKind::Expr { + expr_ctx: ExprCtx { + in_block_expr, + in_loop_body, + after_if_expr, + in_condition, + ref_expr_parent, + is_func_update, + innermost_ret_ty, + self_param, + incomplete_let, + impl_, + in_match_guard, + }, + } + }; + let make_path_kind_type = |ty: ast::Type| { + let location = type_location(ty.syntax()); + PathKind::Type { location: location.unwrap_or(TypeLocation::Other) } + }; - // Infer the path kind - let parent = path.syntax().parent()?; + let mut kind_macro_call = |it: ast::MacroCall| { + path_ctx.has_macro_bang = it.excl_token().is_some(); + let parent = it.syntax().parent()?; + // Any path in an item list will be treated as a macro call by the parser let kind = match_ast! { match parent { - ast::PathType(it) => make_path_kind_type(it.into()), - ast::PathExpr(it) => { - if let Some(p) = it.syntax().parent() { - if ast::ExprStmt::can_cast(p.kind()) { - if let Some(kind) = inbetween_body_and_decl_check(p) { - return Some(make_res(NameRefKind::Keyword(kind))); - } + ast::MacroExpr(expr) => make_path_kind_expr(expr.into()), + ast::MacroPat(it) => PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())}, + ast::MacroType(ty) => make_path_kind_type(ty.into()), + ast::ItemList(_) => PathKind::Item { kind: ItemListKind::Module }, + ast::AssocItemList(_) => PathKind::Item { kind: match parent.parent() { + Some(it) => match_ast! { + match it { + ast::Trait(_) => ItemListKind::Trait, + ast::Impl(it) => if it.trait_().is_some() { + ItemListKind::TraitImpl(find_node_in_file_compensated(sema, original_file, &it)) + } else { + ItemListKind::Impl + }, + _ => return None } - } - - path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind())); - - make_path_kind_expr(it.into()) - }, - ast::TupleStructPat(it) => { - path_ctx.has_call_parens = true; - PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) } - }, - ast::RecordPat(it) => { - path_ctx.has_call_parens = true; - PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) } - }, - ast::PathPat(it) => { - PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())} - }, - ast::MacroCall(it) => { - // A macro call in this position is usually a result of parsing recovery, so check that - if let Some(kind) = inbetween_body_and_decl_check(it.syntax().clone()) { - return Some(make_res(NameRefKind::Keyword(kind))); - } - - kind_macro_call(it)? - }, - ast::Meta(meta) => make_path_kind_attr(meta)?, - ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() }, - ast::UseTree(_) => PathKind::Use, - // completing inside a qualifier - ast::Path(parent) => { - path_ctx.parent = Some(parent.clone()); - let parent = iter::successors(Some(parent), |it| it.parent_path()).last()?.syntax().parent()?; - match_ast! { - match parent { - ast::PathType(it) => make_path_kind_type(it.into()), - ast::PathExpr(it) => { - path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind())); - - make_path_kind_expr(it.into()) - }, - ast::TupleStructPat(it) => { - path_ctx.has_call_parens = true; - PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) } - }, - ast::RecordPat(it) => { - path_ctx.has_call_parens = true; - PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) } - }, - ast::PathPat(it) => { - PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())} - }, - ast::MacroCall(it) => { - kind_macro_call(it)? - }, - ast::Meta(meta) => make_path_kind_attr(meta)?, - ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() }, - ast::UseTree(_) => PathKind::Use, - ast::RecordExpr(it) => make_path_kind_expr(it.into()), - _ => return None, - } - } - }, - ast::RecordExpr(it) => make_path_kind_expr(it.into()), + }, + None => return None, + } }, + ast::ExternItemList(_) => PathKind::Item { kind: ItemListKind::ExternBlock }, + ast::SourceFile(_) => PathKind::Item { kind: ItemListKind::SourceFile }, _ => return None, } }; + Some(kind) + }; + let make_path_kind_attr = |meta: ast::Meta| { + let attr = meta.parent_attr()?; + let kind = attr.kind(); + let attached = attr.syntax().parent()?; + let is_trailing_outer_attr = kind != AttrKind::Inner + && non_trivia_sibling(attr.syntax().clone().into(), syntax::Direction::Next).is_none(); + let annotated_item_kind = if is_trailing_outer_attr { None } else { Some(attached.kind()) }; + Some(PathKind::Attr { attr_ctx: AttrCtx { kind, annotated_item_kind } }) + }; - path_ctx.kind = kind; - path_ctx.has_type_args = segment.generic_arg_list().is_some(); - - // calculate the qualifier context - if let Some((qualifier, use_tree_parent)) = path_or_use_tree_qualifier(&path) { - path_ctx.use_tree_parent = use_tree_parent; - if !use_tree_parent && segment.coloncolon_token().is_some() { - path_ctx.qualified = Qualified::Absolute; - } else { - let qualifier = qualifier - .segment() - .and_then(|it| find_node_in_file(original_file, &it)) - .map(|it| it.parent_path()); - if let Some(qualifier) = qualifier { - let type_anchor = match qualifier.segment().and_then(|it| it.kind()) { - Some(ast::PathSegmentKind::Type { - type_ref: Some(type_ref), - trait_ref, - }) if qualifier.qualifier().is_none() => Some((type_ref, trait_ref)), - _ => None, - }; - - path_ctx.qualified = if let Some((ty, trait_ref)) = type_anchor { - let ty = match ty { - ast::Type::InferType(_) => None, - ty => sema.resolve_type(&ty), - }; - let trait_ = trait_ref.and_then(|it| sema.resolve_trait(&it.path()?)); - Qualified::TypeAnchor { ty, trait_ } - } else { - let res = sema.resolve_path(&qualifier); - - // For understanding how and why super_chain_len is calculated the way it - // is check the documentation at it's definition - let mut segment_count = 0; - let super_count = - iter::successors(Some(qualifier.clone()), |p| p.qualifier()) - .take_while(|p| { - p.segment() - .and_then(|s| { - segment_count += 1; - s.super_token() - }) - .is_some() - }) - .count(); - - let super_chain_len = - if segment_count > super_count { None } else { Some(super_count) }; - - Qualified::With { path: qualifier, resolution: res, super_chain_len } - } - }; - } - } else if let Some(segment) = path.segment() { - if segment.coloncolon_token().is_some() { - path_ctx.qualified = Qualified::Absolute; - } - } - - let mut qualifier_ctx = QualifierCtx::default(); - if path_ctx.is_trivial_path() { - // fetch the full expression that may have qualifiers attached to it - let top_node = match path_ctx.kind { - PathKind::Expr { expr_ctx: ExprCtx { in_block_expr: true, .. } } => { - parent.ancestors().find(|it| ast::PathExpr::can_cast(it.kind())).and_then(|p| { - let parent = p.parent()?; - if ast::StmtList::can_cast(parent.kind()) { - Some(p) - } else if ast::ExprStmt::can_cast(parent.kind()) { - Some(parent) - } else { - None + // Infer the path kind + let parent = path.syntax().parent()?; + let kind = match_ast! { + match parent { + ast::PathType(it) => make_path_kind_type(it.into()), + ast::PathExpr(it) => { + if let Some(p) = it.syntax().parent() { + if ast::ExprStmt::can_cast(p.kind()) { + if let Some(kind) = inbetween_body_and_decl_check(p) { + return Some(make_res(NameRefKind::Keyword(kind))); } - }) - } - PathKind::Item { .. } => { - parent.ancestors().find(|it| ast::MacroCall::can_cast(it.kind())) - } - _ => None, - }; - if let Some(top) = top_node { - if let Some(NodeOrToken::Node(error_node)) = - syntax::algo::non_trivia_sibling(top.clone().into(), syntax::Direction::Prev) - { - if error_node.kind() == SyntaxKind::ERROR { - qualifier_ctx.unsafe_tok = error_node - .children_with_tokens() - .filter_map(NodeOrToken::into_token) - .find(|it| it.kind() == T![unsafe]); - qualifier_ctx.vis_node = - error_node.children().find_map(ast::Visibility::cast); } } - if let PathKind::Item { .. } = path_ctx.kind { - if qualifier_ctx.none() { - if let Some(t) = top.first_token() { - if let Some(prev) = t - .prev_token() - .and_then(|t| syntax::algo::skip_trivia_token(t, Direction::Prev)) - { - if ![T![;], T!['}'], T!['{']].contains(&prev.kind()) { - // This was inferred to be an item position path, but it seems - // to be part of some other broken node which leaked into an item - // list - return None; - } + path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind())); + + make_path_kind_expr(it.into()) + }, + ast::TupleStructPat(it) => { + path_ctx.has_call_parens = true; + PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) } + }, + ast::RecordPat(it) => { + path_ctx.has_call_parens = true; + PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) } + }, + ast::PathPat(it) => { + PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())} + }, + ast::MacroCall(it) => { + // A macro call in this position is usually a result of parsing recovery, so check that + if let Some(kind) = inbetween_body_and_decl_check(it.syntax().clone()) { + return Some(make_res(NameRefKind::Keyword(kind))); + } + + kind_macro_call(it)? + }, + ast::Meta(meta) => make_path_kind_attr(meta)?, + ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() }, + ast::UseTree(_) => PathKind::Use, + // completing inside a qualifier + ast::Path(parent) => { + path_ctx.parent = Some(parent.clone()); + let parent = iter::successors(Some(parent), |it| it.parent_path()).last()?.syntax().parent()?; + match_ast! { + match parent { + ast::PathType(it) => make_path_kind_type(it.into()), + ast::PathExpr(it) => { + path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind())); + + make_path_kind_expr(it.into()) + }, + ast::TupleStructPat(it) => { + path_ctx.has_call_parens = true; + PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) } + }, + ast::RecordPat(it) => { + path_ctx.has_call_parens = true; + PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) } + }, + ast::PathPat(it) => { + PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())} + }, + ast::MacroCall(it) => { + kind_macro_call(it)? + }, + ast::Meta(meta) => make_path_kind_attr(meta)?, + ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() }, + ast::UseTree(_) => PathKind::Use, + ast::RecordExpr(it) => make_path_kind_expr(it.into()), + _ => return None, + } + } + }, + ast::RecordExpr(it) => make_path_kind_expr(it.into()), + _ => return None, + } + }; + + path_ctx.kind = kind; + path_ctx.has_type_args = segment.generic_arg_list().is_some(); + + // calculate the qualifier context + if let Some((qualifier, use_tree_parent)) = path_or_use_tree_qualifier(&path) { + path_ctx.use_tree_parent = use_tree_parent; + if !use_tree_parent && segment.coloncolon_token().is_some() { + path_ctx.qualified = Qualified::Absolute; + } else { + let qualifier = qualifier + .segment() + .and_then(|it| find_node_in_file(original_file, &it)) + .map(|it| it.parent_path()); + if let Some(qualifier) = qualifier { + let type_anchor = match qualifier.segment().and_then(|it| it.kind()) { + Some(ast::PathSegmentKind::Type { type_ref: Some(type_ref), trait_ref }) + if qualifier.qualifier().is_none() => + { + Some((type_ref, trait_ref)) + } + _ => None, + }; + + path_ctx.qualified = if let Some((ty, trait_ref)) = type_anchor { + let ty = match ty { + ast::Type::InferType(_) => None, + ty => sema.resolve_type(&ty), + }; + let trait_ = trait_ref.and_then(|it| sema.resolve_trait(&it.path()?)); + Qualified::TypeAnchor { ty, trait_ } + } else { + let res = sema.resolve_path(&qualifier); + + // For understanding how and why super_chain_len is calculated the way it + // is check the documentation at it's definition + let mut segment_count = 0; + let super_count = iter::successors(Some(qualifier.clone()), |p| p.qualifier()) + .take_while(|p| { + p.segment() + .and_then(|s| { + segment_count += 1; + s.super_token() + }) + .is_some() + }) + .count(); + + let super_chain_len = + if segment_count > super_count { None } else { Some(super_count) }; + + Qualified::With { path: qualifier, resolution: res, super_chain_len } + } + }; + } + } else if let Some(segment) = path.segment() { + if segment.coloncolon_token().is_some() { + path_ctx.qualified = Qualified::Absolute; + } + } + + let mut qualifier_ctx = QualifierCtx::default(); + if path_ctx.is_trivial_path() { + // fetch the full expression that may have qualifiers attached to it + let top_node = match path_ctx.kind { + PathKind::Expr { expr_ctx: ExprCtx { in_block_expr: true, .. } } => { + parent.ancestors().find(|it| ast::PathExpr::can_cast(it.kind())).and_then(|p| { + let parent = p.parent()?; + if ast::StmtList::can_cast(parent.kind()) { + Some(p) + } else if ast::ExprStmt::can_cast(parent.kind()) { + Some(parent) + } else { + None + } + }) + } + PathKind::Item { .. } => { + parent.ancestors().find(|it| ast::MacroCall::can_cast(it.kind())) + } + _ => None, + }; + if let Some(top) = top_node { + if let Some(NodeOrToken::Node(error_node)) = + syntax::algo::non_trivia_sibling(top.clone().into(), syntax::Direction::Prev) + { + if error_node.kind() == SyntaxKind::ERROR { + qualifier_ctx.unsafe_tok = error_node + .children_with_tokens() + .filter_map(NodeOrToken::into_token) + .find(|it| it.kind() == T![unsafe]); + qualifier_ctx.vis_node = error_node.children().find_map(ast::Visibility::cast); + } + } + + if let PathKind::Item { .. } = path_ctx.kind { + if qualifier_ctx.none() { + if let Some(t) = top.first_token() { + if let Some(prev) = t + .prev_token() + .and_then(|t| syntax::algo::skip_trivia_token(t, Direction::Prev)) + { + if ![T![;], T!['}'], T!['{']].contains(&prev.kind()) { + // This was inferred to be an item position path, but it seems + // to be part of some other broken node which leaked into an item + // list + return None; } } } } } } - Some((NameRefContext { nameref, kind: NameRefKind::Path(path_ctx) }, qualifier_ctx)) } + Some((NameRefContext { nameref, kind: NameRefKind::Path(path_ctx) }, qualifier_ctx)) } fn pattern_context_for( From d17a69e4535a73b1c556f602e97cc576ef69fadd Mon Sep 17 00:00:00 2001 From: Urgau Date: Mon, 10 Oct 2022 15:45:05 +0200 Subject: [PATCH 079/333] Fix stabilization of `feature(half_open_range_patterns)` --- compiler/rustc_feature/src/active.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 48c40eae662e..5ea433e6b3d3 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -412,8 +412,6 @@ declare_features! ( (incomplete, generic_associated_types_extended, "1.61.0", Some(95451), None), /// Allows non-trivial generic constants which have to have wfness manually propagated to callers (incomplete, generic_const_exprs, "1.56.0", Some(76560), None), - /// Allows using `..X`, `..=X`, `...X`, and `X..` as a pattern. - (active, half_open_range_patterns, "1.41.0", Some(67264), None), /// Allows using `..=X` as a patterns in slices. (active, half_open_range_patterns_in_slices, "CURRENT_RUSTC_VERSION", Some(67264), None), /// Allows `if let` guard in match arms. From ef68327de76d5f2bb6f9b2a6fa47b92fbf3ff7cc Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Mon, 10 Oct 2022 14:47:22 +0100 Subject: [PATCH 080/333] Consolidate AsFd instances for stdio types into `library/std/src/os/fd/owned.rs` --- library/std/src/os/fd/owned.rs | 52 +++++++++++++++++++++++++++++++ library/std/src/sys/unix/stdio.rs | 50 +---------------------------- library/std/src/sys/wasi/stdio.rs | 50 +---------------------------- 3 files changed, 54 insertions(+), 98 deletions(-) diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index 9875c389d8aa..9d758320cfcc 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -6,6 +6,7 @@ use super::raw::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use crate::fmt; use crate::fs; +use crate::io; use crate::marker::PhantomData; use crate::mem::forget; #[cfg(not(any(target_arch = "wasm32", target_env = "sgx")))] @@ -385,3 +386,54 @@ impl AsFd for Box { (**self).as_fd() } } + +#[stable(feature = "io_safety", since = "1.63.0")] +impl AsFd for io::Stdin { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + unsafe { BorrowedFd::borrow_raw(0) } + } +} + +#[stable(feature = "io_safety", since = "1.63.0")] +impl<'a> AsFd for io::StdinLock<'a> { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + // SAFETY: user code should not close stdin out from under the standard library + unsafe { BorrowedFd::borrow_raw(0) } + } +} + +#[stable(feature = "io_safety", since = "1.63.0")] +impl AsFd for io::Stdout { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + unsafe { BorrowedFd::borrow_raw(1) } + } +} + +#[stable(feature = "io_safety", since = "1.63.0")] +impl<'a> AsFd for io::StdoutLock<'a> { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + // SAFETY: user code should not close stdout out from under the standard library + unsafe { BorrowedFd::borrow_raw(1) } + } +} + +#[stable(feature = "io_safety", since = "1.63.0")] +impl AsFd for io::Stderr { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + unsafe { BorrowedFd::borrow_raw(2) } + } +} + +#[stable(feature = "io_safety", since = "1.63.0")] +impl<'a> AsFd for io::StderrLock<'a> { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + // SAFETY: user code should not close stderr out from under the standard library + unsafe { BorrowedFd::borrow_raw(2) } + } +} diff --git a/library/std/src/sys/unix/stdio.rs b/library/std/src/sys/unix/stdio.rs index 329f9433dba0..b3626c564e86 100644 --- a/library/std/src/sys/unix/stdio.rs +++ b/library/std/src/sys/unix/stdio.rs @@ -1,6 +1,6 @@ use crate::io::{self, IoSlice, IoSliceMut}; use crate::mem::ManuallyDrop; -use crate::os::unix::io::{AsFd, BorrowedFd, FromRawFd}; +use crate::os::unix::io::FromRawFd; use crate::sys::fd::FileDesc; pub struct Stdin(()); @@ -91,51 +91,3 @@ pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; pub fn panic_output() -> Option { Some(Stderr::new()) } - -#[stable(feature = "io_safety", since = "1.63.0")] -impl AsFd for io::Stdin { - #[inline] - fn as_fd(&self) -> BorrowedFd<'_> { - unsafe { BorrowedFd::borrow_raw(libc::STDIN_FILENO) } - } -} - -#[stable(feature = "io_safety", since = "1.63.0")] -impl<'a> AsFd for io::StdinLock<'a> { - #[inline] - fn as_fd(&self) -> BorrowedFd<'_> { - unsafe { BorrowedFd::borrow_raw(libc::STDIN_FILENO) } - } -} - -#[stable(feature = "io_safety", since = "1.63.0")] -impl AsFd for io::Stdout { - #[inline] - fn as_fd(&self) -> BorrowedFd<'_> { - unsafe { BorrowedFd::borrow_raw(libc::STDOUT_FILENO) } - } -} - -#[stable(feature = "io_safety", since = "1.63.0")] -impl<'a> AsFd for io::StdoutLock<'a> { - #[inline] - fn as_fd(&self) -> BorrowedFd<'_> { - unsafe { BorrowedFd::borrow_raw(libc::STDOUT_FILENO) } - } -} - -#[stable(feature = "io_safety", since = "1.63.0")] -impl AsFd for io::Stderr { - #[inline] - fn as_fd(&self) -> BorrowedFd<'_> { - unsafe { BorrowedFd::borrow_raw(libc::STDERR_FILENO) } - } -} - -#[stable(feature = "io_safety", since = "1.63.0")] -impl<'a> AsFd for io::StderrLock<'a> { - #[inline] - fn as_fd(&self) -> BorrowedFd<'_> { - unsafe { BorrowedFd::borrow_raw(libc::STDERR_FILENO) } - } -} diff --git a/library/std/src/sys/wasi/stdio.rs b/library/std/src/sys/wasi/stdio.rs index 427dcf6bb069..4cc0e4ed5a45 100644 --- a/library/std/src/sys/wasi/stdio.rs +++ b/library/std/src/sys/wasi/stdio.rs @@ -4,7 +4,7 @@ use super::fd::WasiFd; use crate::io::{self, IoSlice, IoSliceMut}; use crate::mem::ManuallyDrop; use crate::os::raw; -use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd}; +use crate::os::wasi::io::{AsRawFd, FromRawFd}; pub struct Stdin; pub struct Stdout; @@ -23,22 +23,6 @@ impl AsRawFd for Stdin { } } -impl AsFd for io::Stdin { - #[inline] - fn as_fd(&self) -> BorrowedFd<'_> { - unsafe { BorrowedFd::borrow_raw(0) } - } -} - -#[stable(feature = "io_safety", since = "1.63.0")] -impl<'a> AsFd for io::StdinLock<'a> { - #[inline] - fn as_fd(&self) -> BorrowedFd<'_> { - // SAFETY: user code should not close stdin out from under the standard library - unsafe { BorrowedFd::borrow_raw(0) } - } -} - impl io::Read for Stdin { fn read(&mut self, data: &mut [u8]) -> io::Result { self.read_vectored(&mut [IoSliceMut::new(data)]) @@ -67,22 +51,6 @@ impl AsRawFd for Stdout { } } -impl AsFd for io::Stdout { - #[inline] - fn as_fd(&self) -> BorrowedFd<'_> { - unsafe { BorrowedFd::borrow_raw(1) } - } -} - -#[stable(feature = "io_safety", since = "1.63.0")] -impl<'a> AsFd for io::StdoutLock<'a> { - #[inline] - fn as_fd(&self) -> BorrowedFd<'_> { - // SAFETY: user code should not close stdout out from under the standard library - unsafe { BorrowedFd::borrow_raw(1) } - } -} - impl io::Write for Stdout { fn write(&mut self, data: &[u8]) -> io::Result { self.write_vectored(&[IoSlice::new(data)]) @@ -114,22 +82,6 @@ impl AsRawFd for Stderr { } } -impl AsFd for io::Stderr { - #[inline] - fn as_fd(&self) -> BorrowedFd<'_> { - unsafe { BorrowedFd::borrow_raw(2) } - } -} - -#[stable(feature = "io_safety", since = "1.63.0")] -impl<'a> AsFd for io::StderrLock<'a> { - #[inline] - fn as_fd(&self) -> BorrowedFd<'_> { - // SAFETY: user code should not close stderr out from under the standard library - unsafe { BorrowedFd::borrow_raw(2) } - } -} - impl io::Write for Stderr { fn write(&mut self, data: &[u8]) -> io::Result { self.write_vectored(&[IoSlice::new(data)]) From da638b3a9f32974c41439be6834670d7a438faa3 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Tue, 27 Sep 2022 11:50:47 -0700 Subject: [PATCH 081/333] Allow compiling the `wasm32-wasi` std library with atomics The issue #102157 demonstrates how currently the `-Z build-std` option will fail when re-compiling the standard library with `RUSTFLAGS` like `RUSTFLAGS="-C target-feature=+atomics,+bulk-memory -C link-args=--shared-memory"`. This change attempts to resolve those build issues by depending on the the WebAssembly `futex` module and providing an implementation for `env_lock`. Fixes #102157. --- library/std/src/sys/wasi/mod.rs | 3 +++ library/std/src/sys/wasi/os.rs | 11 ++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/library/std/src/sys/wasi/mod.rs b/library/std/src/sys/wasi/mod.rs index 683a07a34dcf..c8c47763a340 100644 --- a/library/std/src/sys/wasi/mod.rs +++ b/library/std/src/sys/wasi/mod.rs @@ -25,6 +25,9 @@ pub mod cmath; pub mod env; pub mod fd; pub mod fs; +#[allow(unused)] +#[path = "../wasm/atomics/futex.rs"] +pub mod futex; pub mod io; #[path = "../unsupported/locks/mod.rs"] pub mod locks; diff --git a/library/std/src/sys/wasi/os.rs b/library/std/src/sys/wasi/os.rs index cab2887dfcf1..b340061cb3d1 100644 --- a/library/std/src/sys/wasi/os.rs +++ b/library/std/src/sys/wasi/os.rs @@ -24,10 +24,15 @@ mod libc { } } -#[cfg(not(target_feature = "atomics"))] pub unsafe fn env_lock() -> impl Any { - // No need for a lock if we're single-threaded, but this function will need - // to get implemented for multi-threaded scenarios + cfg_if::cfg_if! { + if #[cfg(target_feature = "atomics")] { + todo!() + } else { + // No need for a lock if we're single-threaded, but this function will need + // to get implemented for multi-threaded scenarios + } + } } pub fn errno() -> i32 { From 9530ba0fe2118d9a60e16861faad29ec5803d3f9 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Thu, 29 Sep 2022 11:17:15 -0700 Subject: [PATCH 082/333] Implement `env_lock` with `RwLock` Copying the approach of the Unix target, this change uses the standard `RwLock` to protect against concurrent access of libc's environment. This locking is only enabled when WebAssembly's `atomics` feature is also enabled. --- library/std/src/sys/wasi/os.rs | 35 ++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/library/std/src/sys/wasi/os.rs b/library/std/src/sys/wasi/os.rs index b340061cb3d1..cffe1e32308b 100644 --- a/library/std/src/sys/wasi/os.rs +++ b/library/std/src/sys/wasi/os.rs @@ -1,11 +1,11 @@ #![deny(unsafe_op_in_unsafe_fn)] -use crate::any::Any; use crate::error::Error as StdError; use crate::ffi::{CStr, OsStr, OsString}; use crate::fmt; use crate::io; use crate::marker::PhantomData; +use crate::ops::Drop; use crate::os::wasi::prelude::*; use crate::path::{self, PathBuf}; use crate::str; @@ -24,13 +24,24 @@ mod libc { } } -pub unsafe fn env_lock() -> impl Any { - cfg_if::cfg_if! { - if #[cfg(target_feature = "atomics")] { - todo!() - } else { - // No need for a lock if we're single-threaded, but this function will need - // to get implemented for multi-threaded scenarios +cfg_if::cfg_if! { + if #[cfg(target_feature = "atomics")] { + // Access to the environment must be protected by a lock in multi-threaded scenarios. + use crate::sync::{PoisonError, RwLock}; + static ENV_LOCK: RwLock<()> = RwLock::new(()); + pub fn env_read_lock() -> impl Drop { + ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner) + } + pub fn env_write_lock() -> impl Drop { + ENV_LOCK.write().unwrap_or_else(PoisonError::into_inner) + } + } else { + // No need for a lock if we are single-threaded. + pub fn env_read_lock() -> impl Drop { + () + } + pub fn env_write_lock() -> impl Drop { + () } } } @@ -149,7 +160,7 @@ impl Iterator for Env { pub fn env() -> Env { unsafe { - let _guard = env_lock(); + let _guard = env_read_lock(); let mut environ = libc::environ; let mut result = Vec::new(); if !environ.is_null() { @@ -180,7 +191,7 @@ pub fn env() -> Env { pub fn getenv(k: &OsStr) -> Option { let s = run_with_cstr(k.as_bytes(), |k| unsafe { - let _guard = env_lock(); + let _guard = env_read_lock(); Ok(libc::getenv(k.as_ptr()) as *const libc::c_char) }) .ok()?; @@ -194,7 +205,7 @@ pub fn getenv(k: &OsStr) -> Option { pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { run_with_cstr(k.as_bytes(), |k| { run_with_cstr(v.as_bytes(), |v| unsafe { - let _guard = env_lock(); + let _guard = env_write_lock(); cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop) }) }) @@ -202,7 +213,7 @@ pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { pub fn unsetenv(n: &OsStr) -> io::Result<()> { run_with_cstr(n.as_bytes(), |nbuf| unsafe { - let _guard = env_lock(); + let _guard = env_write_lock(); cvt(libc::unsetenv(nbuf.as_ptr())).map(drop) }) } From 76be44eed5e0fe1a80d642a31a4f0537e151f7fe Mon Sep 17 00:00:00 2001 From: dvdsk Date: Mon, 10 Oct 2022 19:00:47 +0200 Subject: [PATCH 083/333] Update manual now stable can be installed with rustup `rustup` can now install `rust-analyzer` for the stable tool-chain. This commit removes the note that `rustup` can only install for the nightly branch and adjusts the command. I also added a note on how to find the path to the `rust-analyzer` binary when installed using `rustup`, and suggestions on how to work around it not being placed in `~/.cargo/bin`. I thought it would be ideal to point everyone to use `rustup run stable rust-analyzer` to start `rust-analyzer`. That would make it trivial to switch to nightly however I could not get this to work in `nvim` therefore I left it as a suggestion at the end. --- docs/user/manual.adoc | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc index 9bd3b6a692b1..c30838e5f5e1 100644 --- a/docs/user/manual.adoc +++ b/docs/user/manual.adoc @@ -174,14 +174,25 @@ On Unix, running the editor from a shell or changing the `.desktop` file to set ==== `rustup` -`rust-analyzer` is available in `rustup`, but only in the nightly toolchain: +`rust-analyzer` is available in `rustup`: [source,bash] ---- -$ rustup +nightly component add rust-analyzer-preview +$ rustup component add rust-analyzer ---- -However, in contrast to `component add clippy` or `component add rustfmt`, this does not actually place a `rust-analyzer` binary in `~/.cargo/bin`, see https://github.com/rust-lang/rustup/issues/2411[this issue]. +However, in contrast to `component add clippy` or `component add rustfmt`, this does not actually place a `rust-analyzer` binary in `~/.cargo/bin`, see https://github.com/rust-lang/rustup/issues/2411[this issue]. You can find the path to the binary using: +[source,bash] +---- +$ rustup which --toolchain stable rust-analyzer +---- +You can link to there from `~/.cargo/bin` or configure your editor to use the full path. + +Alternatively you might be able to configure your editor to start `rust-analyzer` using the command: +[source,bash] +---- +$ rustup run stable rust-analyzer +---- ==== Arch Linux From c069f1b7d22b6e1ce9135052b4ba50be29bcea6c Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 10 Oct 2022 22:47:52 +0200 Subject: [PATCH 084/333] Prefer similar tokens when expanding macros speculatively --- crates/hir-expand/src/db.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/crates/hir-expand/src/db.rs b/crates/hir-expand/src/db.rs index bc97ee15c7d3..87e4db03984a 100644 --- a/crates/hir-expand/src/db.rs +++ b/crates/hir-expand/src/db.rs @@ -221,8 +221,16 @@ pub fn expand_speculative( fixup::reverse_fixups(&mut speculative_expansion.value, &spec_args_tmap, &fixups.undo_info); let (node, rev_tmap) = token_tree_to_syntax_node(&speculative_expansion.value, expand_to); - let range = rev_tmap.first_range_by_token(token_id, token_to_map.kind())?; - let token = node.syntax_node().covering_element(range).into_token()?; + let syntax_node = node.syntax_node(); + let token = rev_tmap + .ranges_by_token(token_id, token_to_map.kind()) + .filter_map(|range| syntax_node.covering_element(range).into_token()) + .min_by_key(|t| { + // prefer tokens of the same kind and text + // Note the inversion of the score here, as we want to prefer the first token in case + // of all tokens having the same score + (t.kind() != token_to_map.kind()) as u8 + (t.text() != token_to_map.text()) as u8 + })?; Some((node.syntax_node(), token)) } From 11a335d5e84844e0425a43c076baa6db2bbc790b Mon Sep 17 00:00:00 2001 From: zyctree Date: Tue, 11 Oct 2022 14:05:04 +0800 Subject: [PATCH 085/333] fix link in syntax.md --- docs/dev/syntax.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/syntax.md b/docs/dev/syntax.md index 30e13701383a..64c872b25176 100644 --- a/docs/dev/syntax.md +++ b/docs/dev/syntax.md @@ -11,7 +11,7 @@ The things described are implemented in three places * [rowan](https://github.com/rust-analyzer/rowan/tree/v0.9.0) -- a generic library for rowan syntax trees. * [ra_syntax](https://github.com/rust-lang/rust-analyzer/tree/cf5bdf464cad7ceb9a67e07985a3f4d3799ec0b6/crates/ra_syntax) crate inside rust-analyzer which wraps `rowan` into rust-analyzer specific API. Nothing in rust-analyzer except this crate knows about `rowan`. -* [parser](https://github.com/rust-lang/rust-analyzer/tree/cf5bdf464cad7ceb9a67e07985a3f4d3799ec0b6/crates/parser) crate parses input tokens into an `ra_syntax` tree +* [parser](https://github.com/rust-lang/rust-analyzer/tree/cf5bdf464cad7ceb9a67e07985a3f4d3799ec0b6/crates/ra_parser) crate parses input tokens into an `ra_syntax` tree ## Design Goals From a53e3acca914a1f6897c451ba35636ee1f72c0e7 Mon Sep 17 00:00:00 2001 From: woppopo Date: Tue, 11 Oct 2022 06:40:37 +0000 Subject: [PATCH 086/333] Change tracking issue from #76156 to #102911 --- library/core/src/panic/location.rs | 6 +++--- library/core/tests/lib.rs | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/library/core/src/panic/location.rs b/library/core/src/panic/location.rs index 26a410365568..6dcf23dde87c 100644 --- a/library/core/src/panic/location.rs +++ b/library/core/src/panic/location.rs @@ -123,7 +123,7 @@ impl<'a> Location<'a> { /// ``` #[must_use] #[stable(feature = "panic_hooks", since = "1.10.0")] - #[rustc_const_unstable(feature = "const_caller_location", issue = "76156")] + #[rustc_const_unstable(feature = "const_location_fields", issue = "102911")] #[inline] pub const fn file(&self) -> &str { self.file @@ -148,7 +148,7 @@ impl<'a> Location<'a> { /// ``` #[must_use] #[stable(feature = "panic_hooks", since = "1.10.0")] - #[rustc_const_unstable(feature = "const_caller_location", issue = "76156")] + #[rustc_const_unstable(feature = "const_location_fields", issue = "102911")] #[inline] pub const fn line(&self) -> u32 { self.line @@ -173,7 +173,7 @@ impl<'a> Location<'a> { /// ``` #[must_use] #[stable(feature = "panic_col", since = "1.25.0")] - #[rustc_const_unstable(feature = "const_caller_location", issue = "76156")] + #[rustc_const_unstable(feature = "const_location_fields", issue = "102911")] #[inline] pub const fn column(&self) -> u32 { self.col diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 7cf2c0dcefc9..534f92d3d8ec 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -21,6 +21,7 @@ #![feature(const_ptr_write)] #![feature(const_trait_impl)] #![feature(const_likely)] +#![feature(const_location_fields)] #![feature(core_intrinsics)] #![feature(core_private_bignum)] #![feature(core_private_diy_float)] From 5e43ea96aad65fbaae5806ea6732ab59977ede1c Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Mon, 10 Oct 2022 19:32:24 +0900 Subject: [PATCH 087/333] fix: reorder dyn bounds on render --- crates/hir-ty/src/display.rs | 12 +++++++++- .../hir-ty/src/tests/display_source_code.rs | 22 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index 7f0baf49dadc..a6602747ef79 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -751,9 +751,19 @@ impl HirDisplay for Ty { } TyKind::BoundVar(idx) => idx.hir_fmt(f)?, TyKind::Dyn(dyn_ty) => { + // Reorder bounds to satisfy `write_bounds_like_dyn_trait()`'s expectation. + // FIXME: `Iterator::partition_in_place()` or `Vec::drain_filter()` may make it + // more efficient when either of them hits stable. + let mut bounds: SmallVec<[_; 4]> = + dyn_ty.bounds.skip_binders().iter(Interner).cloned().collect(); + let (auto_traits, others): (SmallVec<[_; 4]>, _) = + bounds.drain(1..).partition(|b| b.skip_binders().trait_id().is_some()); + bounds.extend(others); + bounds.extend(auto_traits); + write_bounds_like_dyn_trait_with_prefix( "dyn", - dyn_ty.bounds.skip_binders().interned(), + &bounds, SizedByDefault::NotSized, f, )?; diff --git a/crates/hir-ty/src/tests/display_source_code.rs b/crates/hir-ty/src/tests/display_source_code.rs index 240942e488d7..8a8ff08cfe8c 100644 --- a/crates/hir-ty/src/tests/display_source_code.rs +++ b/crates/hir-ty/src/tests/display_source_code.rs @@ -55,6 +55,28 @@ fn main() { ); } +#[test] +fn render_dyn_ty_independent_of_order() { + check_types_source_code( + r#" +auto trait Send {} +trait A { + type Assoc; +} +trait B: A {} + +fn test( + _: &(dyn A + Send), + //^ &(dyn A + Send) + _: &(dyn Send + A), + //^ &(dyn A + Send) + _: &dyn B, + //^ &(dyn B) +) {} + "#, + ); +} + #[test] fn render_dyn_for_ty() { // FIXME From 34977996c3b2d045e25e17a6ce6c8a7565a16b01 Mon Sep 17 00:00:00 2001 From: zyctree Date: Tue, 11 Oct 2022 19:37:49 +0800 Subject: [PATCH 088/333] Update docs/dev/syntax.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Laurențiu Nicola --- docs/dev/syntax.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/syntax.md b/docs/dev/syntax.md index 64c872b25176..767233351b40 100644 --- a/docs/dev/syntax.md +++ b/docs/dev/syntax.md @@ -11,7 +11,7 @@ The things described are implemented in three places * [rowan](https://github.com/rust-analyzer/rowan/tree/v0.9.0) -- a generic library for rowan syntax trees. * [ra_syntax](https://github.com/rust-lang/rust-analyzer/tree/cf5bdf464cad7ceb9a67e07985a3f4d3799ec0b6/crates/ra_syntax) crate inside rust-analyzer which wraps `rowan` into rust-analyzer specific API. Nothing in rust-analyzer except this crate knows about `rowan`. -* [parser](https://github.com/rust-lang/rust-analyzer/tree/cf5bdf464cad7ceb9a67e07985a3f4d3799ec0b6/crates/ra_parser) crate parses input tokens into an `ra_syntax` tree +* [ra_parser](https://github.com/rust-lang/rust-analyzer/tree/cf5bdf464cad7ceb9a67e07985a3f4d3799ec0b6/crates/ra_parser) crate parses input tokens into an `ra_syntax` tree ## Design Goals From 3c39668ca464919ed4180049945b3e2b9bf6191a Mon Sep 17 00:00:00 2001 From: btwotwo <10519967+btwotwo@users.noreply.github.com> Date: Tue, 11 Oct 2022 19:23:41 +0200 Subject: [PATCH 089/333] Remove extra parameter, access Db through semantics --- crates/ide-completion/src/completions/env_vars.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/ide-completion/src/completions/env_vars.rs b/crates/ide-completion/src/completions/env_vars.rs index 5483fdf31a24..a3a971303948 100644 --- a/crates/ide-completion/src/completions/env_vars.rs +++ b/crates/ide-completion/src/completions/env_vars.rs @@ -18,7 +18,8 @@ const CARGO_DEFINED_VARS: &[(&str, &str)] = &[ ("CARGO_PKG_NAME","The name of your package"), ("CARGO_PKG_DESCRIPTION","The description from the manifest of your package"), ("CARGO_PKG_HOMEPAGE","The home page from the manifest of your package"), -("CARGO_PKG_REPOSITORY","The repository from the manifest of your package"), + ("CARGO_PKG_REPOSITORY", +"The repository from the manifest of your package"), ("CARGO_PKG_LICENSE","The license from the manifest of your package"), ("CARGO_PKG_LICENSE_FILE","The license file from the manifest of your package"), ("CARGO_PKG_RUST_VERSION","The Rust version from the manifest of your package. Note that this is the minimum Rust version supported by the package, not the current Rust version"), @@ -33,7 +34,7 @@ pub(crate) fn complete_cargo_env_vars( ctx: &CompletionContext<'_>, expanded: &ast::String, ) -> Option<()> { - guard_env_macro(expanded, &ctx.sema, &ctx.db)?; + guard_env_macro(expanded, &ctx.sema)?; let range = expanded.text_range_between_quotes()?; CARGO_DEFINED_VARS.iter().for_each(|(var, detail)| { @@ -48,11 +49,11 @@ pub(crate) fn complete_cargo_env_vars( fn guard_env_macro( string: &ast::String, semantics: &Semantics<'_, RootDatabase>, - db: &RootDatabase, ) -> Option<()> { let call = get_outer_macro(string)?; let name = call.path()?.segment()?.name_ref()?; let makro = semantics.resolve_macro_call(&call)?; + let db = semantics.db; match name.text().as_str() { "env" | "option_env" if makro.kind(db) == hir::MacroKind::BuiltIn => Some(()), From a807cc3afbd5bd081f4f5ff0bad1b5c403d7a690 Mon Sep 17 00:00:00 2001 From: btwotwo <10519967+btwotwo@users.noreply.github.com> Date: Tue, 11 Oct 2022 19:28:42 +0200 Subject: [PATCH 090/333] Rename `get_outer_macro` to `macro_call_for_string_token` --- crates/ide-completion/src/completions/env_vars.rs | 7 +++---- crates/ide-db/src/syntax_helpers/format_string.rs | 5 ++--- crates/ide-db/src/syntax_helpers/node_ext.rs | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/crates/ide-completion/src/completions/env_vars.rs b/crates/ide-completion/src/completions/env_vars.rs index a3a971303948..14dc17321f9f 100644 --- a/crates/ide-completion/src/completions/env_vars.rs +++ b/crates/ide-completion/src/completions/env_vars.rs @@ -1,11 +1,10 @@ //! Completes environment variables defined by Cargo (https://doc.rust-lang.org/cargo/reference/environment-variables.html) use hir::Semantics; -use ide_db::{syntax_helpers::node_ext::get_outer_macro, RootDatabase}; +use ide_db::{syntax_helpers::node_ext::macro_call_for_string_token, RootDatabase}; use syntax::ast::{self, IsString}; -use crate::{context::CompletionContext, CompletionItem, CompletionItemKind}; +use crate::{context::CompletionContext, CompletionItem, CompletionItemKind, completions::Completions}; -use super::Completions; const CARGO_DEFINED_VARS: &[(&str, &str)] = &[ ("CARGO","Path to the cargo binary performing the build"), ("CARGO_MANIFEST_DIR","The directory containing the manifest of your package"), @@ -50,7 +49,7 @@ fn guard_env_macro( string: &ast::String, semantics: &Semantics<'_, RootDatabase>, ) -> Option<()> { - let call = get_outer_macro(string)?; + let call = macro_call_for_string_token(string)?; let name = call.path()?.segment()?.name_ref()?; let makro = semantics.resolve_macro_call(&call)?; let db = semantics.db; diff --git a/crates/ide-db/src/syntax_helpers/format_string.rs b/crates/ide-db/src/syntax_helpers/format_string.rs index b3e227ddd27d..caa579e322b3 100644 --- a/crates/ide-db/src/syntax_helpers/format_string.rs +++ b/crates/ide-db/src/syntax_helpers/format_string.rs @@ -3,8 +3,7 @@ use syntax::{ ast::{self, IsString}, TextRange, TextSize, }; - -use super::node_ext::get_outer_macro; +use crate::syntax_helpers::node_ext::macro_call_for_string_token; pub fn is_format_string(string: &ast::String) -> bool { // Check if `string` is a format string argument of a macro invocation. @@ -16,7 +15,7 @@ pub fn is_format_string(string: &ast::String) -> bool { // This setup lets us correctly highlight the components of `concat!("{}", "bla")` format // strings. It still fails for `concat!("{", "}")`, but that is rare. (|| { - let name = get_outer_macro(string)?.path()?.segment()?.name_ref()?; + let name = macro_call_for_string_token(string)?.path()?.segment()?.name_ref()?; if !matches!( name.text().as_str(), diff --git a/crates/ide-db/src/syntax_helpers/node_ext.rs b/crates/ide-db/src/syntax_helpers/node_ext.rs index 9cfcdfb77b97..39710b8f13eb 100644 --- a/crates/ide-db/src/syntax_helpers/node_ext.rs +++ b/crates/ide-db/src/syntax_helpers/node_ext.rs @@ -458,7 +458,7 @@ pub fn parse_tt_as_comma_sep_paths(input: ast::TokenTree) -> Option Option { +pub fn macro_call_for_string_token(string: &ast::String) -> Option { let macro_call = string.syntax().parent_ancestors().find_map(ast::MacroCall::cast)?; Some(macro_call) } From b5e87ac11177165da100b21fbc2f83736add6ac8 Mon Sep 17 00:00:00 2001 From: btwotwo <10519967+btwotwo@users.noreply.github.com> Date: Tue, 11 Oct 2022 19:29:09 +0200 Subject: [PATCH 091/333] Fix formatting for cargo vars list --- .../src/completions/env_vars.rs | 39 +++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/crates/ide-completion/src/completions/env_vars.rs b/crates/ide-completion/src/completions/env_vars.rs index 14dc17321f9f..d7cccf0bedcf 100644 --- a/crates/ide-completion/src/completions/env_vars.rs +++ b/crates/ide-completion/src/completions/env_vars.rs @@ -6,26 +6,25 @@ use syntax::ast::{self, IsString}; use crate::{context::CompletionContext, CompletionItem, CompletionItemKind, completions::Completions}; const CARGO_DEFINED_VARS: &[(&str, &str)] = &[ -("CARGO","Path to the cargo binary performing the build"), -("CARGO_MANIFEST_DIR","The directory containing the manifest of your package"), -("CARGO_PKG_VERSION","The full version of your package"), -("CARGO_PKG_VERSION_MAJOR","The major version of your package"), -("CARGO_PKG_VERSION_MINOR","The minor version of your package"), -("CARGO_PKG_VERSION_PATCH","The patch version of your package"), -("CARGO_PKG_VERSION_PRE","The pre-release version of your package"), -("CARGO_PKG_AUTHORS","Colon separated list of authors from the manifest of your package"), -("CARGO_PKG_NAME","The name of your package"), -("CARGO_PKG_DESCRIPTION","The description from the manifest of your package"), -("CARGO_PKG_HOMEPAGE","The home page from the manifest of your package"), - ("CARGO_PKG_REPOSITORY", -"The repository from the manifest of your package"), -("CARGO_PKG_LICENSE","The license from the manifest of your package"), -("CARGO_PKG_LICENSE_FILE","The license file from the manifest of your package"), -("CARGO_PKG_RUST_VERSION","The Rust version from the manifest of your package. Note that this is the minimum Rust version supported by the package, not the current Rust version"), -("CARGO_CRATE_NAME","The name of the crate that is currently being compiled"), -("CARGO_BIN_NAME","The name of the binary that is currently being compiled (if it is a binary). This name does not include any file extension, such as .exe"), -("CARGO_PRIMARY_PACKAGE","This environment variable will be set if the package being built is primary. Primary packages are the ones the user selected on the command-line, either with -p flags or the defaults based on the current directory and the default workspace members. This environment variable will not be set when building dependencies. This is only set when compiling the package (not when running binaries or tests)"), -("CARGO_TARGET_TMPDIR","Only set when building integration test or benchmark code. This is a path to a directory inside the target directory where integration tests or benchmarks are free to put any data needed by the tests/benches. Cargo initially creates this directory but doesn't manage its content in any way, this is the responsibility of the test code") + ("CARGO","Path to the cargo binary performing the build"), + ("CARGO_MANIFEST_DIR","The directory containing the manifest of your package"), + ("CARGO_PKG_VERSION","The full version of your package"), + ("CARGO_PKG_VERSION_MAJOR","The major version of your package"), + ("CARGO_PKG_VERSION_MINOR","The minor version of your package"), + ("CARGO_PKG_VERSION_PATCH","The patch version of your package"), + ("CARGO_PKG_VERSION_PRE","The pre-release version of your package"), + ("CARGO_PKG_AUTHORS","Colon separated list of authors from the manifest of your package"), + ("CARGO_PKG_NAME","The name of your package"), + ("CARGO_PKG_DESCRIPTION","The description from the manifest of your package"), + ("CARGO_PKG_HOMEPAGE","The home page from the manifest of your package"), + ("CARGO_PKG_REPOSITORY","The repository from the manifest of your package"), + ("CARGO_PKG_LICENSE","The license from the manifest of your package"), + ("CARGO_PKG_LICENSE_FILE","The license file from the manifest of your package"), + ("CARGO_PKG_RUST_VERSION","The Rust version from the manifest of your package. Note that this is the minimum Rust version supported by the package, not the current Rust version"), + ("CARGO_CRATE_NAME","The name of the crate that is currently being compiled"), + ("CARGO_BIN_NAME","The name of the binary that is currently being compiled (if it is a binary). This name does not include any file extension, such as .exe"), + ("CARGO_PRIMARY_PACKAGE","This environment variable will be set if the package being built is primary. Primary packages are the ones the user selected on the command-line, either with -p flags or the defaults based on the current directory and the default workspace members. This environment variable will not be set when building dependencies. This is only set when compiling the package (not when running binaries or tests)"), + ("CARGO_TARGET_TMPDIR","Only set when building integration test or benchmark code. This is a path to a directory inside the target directory where integration tests or benchmarks are free to put any data needed by the tests/benches. Cargo initially creates this directory but doesn't manage its content in any way, this is the responsibility of the test code") ]; pub(crate) fn complete_cargo_env_vars( From f9d3c8352660451cb14df831002eb1ec514e59e6 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 11 Oct 2022 17:01:22 +0000 Subject: [PATCH 092/333] Fix let removal suggestion in struct --- compiler/rustc_parse/src/parser/item.rs | 17 ++++++---- src/test/ui/parser/bad-let-as-field.rs | 6 ++++ src/test/ui/parser/bad-let-as-field.stderr | 15 +++++++++ .../ui/parser/removed-syntax-field-let-2.rs | 12 +++++++ .../parser/removed-syntax-field-let-2.stderr | 33 +++++++++++++++++++ .../ui/parser/removed-syntax-field-let.stderr | 10 +++--- 6 files changed, 81 insertions(+), 12 deletions(-) create mode 100644 src/test/ui/parser/bad-let-as-field.rs create mode 100644 src/test/ui/parser/bad-let-as-field.stderr create mode 100644 src/test/ui/parser/removed-syntax-field-let-2.rs create mode 100644 src/test/ui/parser/removed-syntax-field-let-2.stderr diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index e82044a89c47..ebcbc75ba32c 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1789,20 +1789,25 @@ impl<'a> Parser<'a> { } } else { let mut err = self.expected_ident_found(); - if let Some((ident, _)) = self.token.ident() && ident.as_str() == "let" { - self.bump(); // `let` - let span = self.prev_token.span.until(self.token.span); + if self.eat_keyword_noexpect(kw::Let) + && let removal_span = self.prev_token.span.until(self.token.span) + && let Ok(ident) = self.parse_ident_common(false) + // Cancel this error, we don't need it. + .map_err(|err| err.cancel()) + && self.token.kind == TokenKind::Colon + { err.span_suggestion( - span, - "remove the let, the `let` keyword is not allowed in struct field definitions", + removal_span, + "remove this `let` keyword", String::new(), Applicability::MachineApplicable, ); err.note("the `let` keyword is not allowed in `struct` fields"); err.note("see for more information"); err.emit(); - self.bump(); return Ok(ident); + } else { + self.restore_snapshot(snapshot); } err }; diff --git a/src/test/ui/parser/bad-let-as-field.rs b/src/test/ui/parser/bad-let-as-field.rs new file mode 100644 index 000000000000..fec2bc25617a --- /dev/null +++ b/src/test/ui/parser/bad-let-as-field.rs @@ -0,0 +1,6 @@ +struct Foo { + let: i32, + //~^ ERROR expected identifier, found keyword +} + +fn main() {} diff --git a/src/test/ui/parser/bad-let-as-field.stderr b/src/test/ui/parser/bad-let-as-field.stderr new file mode 100644 index 000000000000..57def42b1ee3 --- /dev/null +++ b/src/test/ui/parser/bad-let-as-field.stderr @@ -0,0 +1,15 @@ +error: expected identifier, found keyword `let` + --> $DIR/bad-let-as-field.rs:2:5 + | +LL | struct Foo { + | --- while parsing this struct +LL | let: i32, + | ^^^ expected identifier, found keyword + | +help: escape `let` to use it as an identifier + | +LL | r#let: i32, + | ++ + +error: aborting due to previous error + diff --git a/src/test/ui/parser/removed-syntax-field-let-2.rs b/src/test/ui/parser/removed-syntax-field-let-2.rs new file mode 100644 index 000000000000..7ff91b476aeb --- /dev/null +++ b/src/test/ui/parser/removed-syntax-field-let-2.rs @@ -0,0 +1,12 @@ +struct Foo { + let x: i32, + //~^ ERROR expected identifier, found keyword + let y: i32, + //~^ ERROR expected identifier, found keyword +} + +fn main() { + let _ = Foo { + //~^ ERROR missing fields `x` and `y` in initializer of `Foo` + }; +} diff --git a/src/test/ui/parser/removed-syntax-field-let-2.stderr b/src/test/ui/parser/removed-syntax-field-let-2.stderr new file mode 100644 index 000000000000..fda0919b9b64 --- /dev/null +++ b/src/test/ui/parser/removed-syntax-field-let-2.stderr @@ -0,0 +1,33 @@ +error: expected identifier, found keyword `let` + --> $DIR/removed-syntax-field-let-2.rs:2:5 + | +LL | let x: i32, + | ^^^- + | | + | expected identifier, found keyword + | help: remove this `let` keyword + | + = note: the `let` keyword is not allowed in `struct` fields + = note: see for more information + +error: expected identifier, found keyword `let` + --> $DIR/removed-syntax-field-let-2.rs:4:5 + | +LL | let y: i32, + | ^^^- + | | + | expected identifier, found keyword + | help: remove this `let` keyword + | + = note: the `let` keyword is not allowed in `struct` fields + = note: see for more information + +error[E0063]: missing fields `x` and `y` in initializer of `Foo` + --> $DIR/removed-syntax-field-let-2.rs:9:13 + | +LL | let _ = Foo { + | ^^^ missing `x` and `y` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0063`. diff --git a/src/test/ui/parser/removed-syntax-field-let.stderr b/src/test/ui/parser/removed-syntax-field-let.stderr index bd1b23f7e3bb..9bc18dabd6ea 100644 --- a/src/test/ui/parser/removed-syntax-field-let.stderr +++ b/src/test/ui/parser/removed-syntax-field-let.stderr @@ -2,15 +2,13 @@ error: expected identifier, found keyword `let` --> $DIR/removed-syntax-field-let.rs:2:5 | LL | let foo: (), - | ^^^ expected identifier, found keyword + | ^^^- + | | + | expected identifier, found keyword + | help: remove this `let` keyword | = note: the `let` keyword is not allowed in `struct` fields = note: see for more information -help: remove the let, the `let` keyword is not allowed in struct field definitions - | -LL - let foo: (), -LL + foo: (), - | error: aborting due to previous error From 2b2b9f8c7332314b151fd8287a8b02c34a20a5a7 Mon Sep 17 00:00:00 2001 From: btwotwo <10519967+btwotwo@users.noreply.github.com> Date: Tue, 11 Oct 2022 19:53:22 +0200 Subject: [PATCH 093/333] Formatting --- crates/ide-completion/src/completions/env_vars.rs | 9 ++++----- crates/ide-db/src/syntax_helpers/format_string.rs | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/crates/ide-completion/src/completions/env_vars.rs b/crates/ide-completion/src/completions/env_vars.rs index d7cccf0bedcf..09e95e53de63 100644 --- a/crates/ide-completion/src/completions/env_vars.rs +++ b/crates/ide-completion/src/completions/env_vars.rs @@ -3,7 +3,9 @@ use hir::Semantics; use ide_db::{syntax_helpers::node_ext::macro_call_for_string_token, RootDatabase}; use syntax::ast::{self, IsString}; -use crate::{context::CompletionContext, CompletionItem, CompletionItemKind, completions::Completions}; +use crate::{ + completions::Completions, context::CompletionContext, CompletionItem, CompletionItemKind, +}; const CARGO_DEFINED_VARS: &[(&str, &str)] = &[ ("CARGO","Path to the cargo binary performing the build"), @@ -44,10 +46,7 @@ pub(crate) fn complete_cargo_env_vars( Some(()) } -fn guard_env_macro( - string: &ast::String, - semantics: &Semantics<'_, RootDatabase>, -) -> Option<()> { +fn guard_env_macro(string: &ast::String, semantics: &Semantics<'_, RootDatabase>) -> Option<()> { let call = macro_call_for_string_token(string)?; let name = call.path()?.segment()?.name_ref()?; let makro = semantics.resolve_macro_call(&call)?; diff --git a/crates/ide-db/src/syntax_helpers/format_string.rs b/crates/ide-db/src/syntax_helpers/format_string.rs index caa579e322b3..2d6927cee995 100644 --- a/crates/ide-db/src/syntax_helpers/format_string.rs +++ b/crates/ide-db/src/syntax_helpers/format_string.rs @@ -1,9 +1,9 @@ //! Tools to work with format string literals for the `format_args!` family of macros. +use crate::syntax_helpers::node_ext::macro_call_for_string_token; use syntax::{ ast::{self, IsString}, TextRange, TextSize, }; -use crate::syntax_helpers::node_ext::macro_call_for_string_token; pub fn is_format_string(string: &ast::String) -> bool { // Check if `string` is a format string argument of a macro invocation. From 95b0b2d34956c232ace714c1f5971b20e2767c6c Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Tue, 11 Oct 2022 11:42:44 -0700 Subject: [PATCH 094/333] fix: return type of single-threaded dummy lock must be droppable --- library/std/src/sys/wasi/os.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/sys/wasi/os.rs b/library/std/src/sys/wasi/os.rs index cffe1e32308b..f5513e9996d4 100644 --- a/library/std/src/sys/wasi/os.rs +++ b/library/std/src/sys/wasi/os.rs @@ -38,10 +38,10 @@ cfg_if::cfg_if! { } else { // No need for a lock if we are single-threaded. pub fn env_read_lock() -> impl Drop { - () + Box::new(()) } pub fn env_write_lock() -> impl Drop { - () + Box::new(()) } } } From 87060c42ff2e912379af41c174885bd93f0ddfda Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Tue, 11 Oct 2022 14:46:49 -0700 Subject: [PATCH 095/333] rustdoc: remove unused CSS `nav.sum` This was added in 4fd061c426902b0904c65e64a3780b21f9ab3afb, but never actually used. --- src/librustdoc/html/static/css/rustdoc.css | 1 - 1 file changed, 1 deletion(-) diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 661aed71298d..ec7ede010538 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -718,7 +718,6 @@ nav.sub { .source nav.sub { margin-left: 32px; } -nav.sum { text-align: right; } nav.sub form { display: inline; } a { From 46169e66583f6f2abf9136fff2b60aabaf1f4014 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Tue, 11 Oct 2022 15:05:22 -0700 Subject: [PATCH 096/333] rustdoc: merge identical CSS selectors --- src/librustdoc/html/static/css/rustdoc.css | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index ec7ede010538..d23d88d69dc5 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -563,8 +563,6 @@ h2.location a { .rustdoc .example-wrap { display: flex; position: relative; -} -.rustdoc .example-wrap { margin-bottom: 10px; } /* For the last child of a div, the margin will be taken care of From f8048aa3a5cf1cfbc1d9778d531b3e25189016e5 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 11 Oct 2022 16:04:17 -0700 Subject: [PATCH 097/333] Update books --- src/doc/embedded-book | 2 +- src/doc/nomicon | 2 +- src/doc/reference | 2 +- src/doc/rust-by-example | 2 +- src/doc/rustc-dev-guide | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/doc/embedded-book b/src/doc/embedded-book index 4ce51cb7441a..c533348edd69 160000 --- a/src/doc/embedded-book +++ b/src/doc/embedded-book @@ -1 +1 @@ -Subproject commit 4ce51cb7441a6f02b5bf9b07b2eb755c21ab7954 +Subproject commit c533348edd69f11a8f4225d633a05d7093fddbf3 diff --git a/src/doc/nomicon b/src/doc/nomicon index f53bfa056929..9c7328377546 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit f53bfa056929217870a5d2df1366d2e7ba35096d +Subproject commit 9c73283775466d22208a0b28afcab44db4c0cc10 diff --git a/src/doc/reference b/src/doc/reference index a7cdac33ca73..f6ed74f582bd 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit a7cdac33ca7356ad49d5c2b5e2c5010889b33eee +Subproject commit f6ed74f582bddcec73f753eafaab3749c4f7df61 diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example index 767a6bd9727a..5e7b296d6c34 160000 --- a/src/doc/rust-by-example +++ b/src/doc/rust-by-example @@ -1 +1 @@ -Subproject commit 767a6bd9727a596d7cfdbaeee475e65b2670ea3a +Subproject commit 5e7b296d6c345addbd748f242aae28c42555c015 diff --git a/src/doc/rustc-dev-guide b/src/doc/rustc-dev-guide index 9a86c0467bbe..7518c3445dc0 160000 --- a/src/doc/rustc-dev-guide +++ b/src/doc/rustc-dev-guide @@ -1 +1 @@ -Subproject commit 9a86c0467bbe42056f73fdf5b03fff757d7c4a9b +Subproject commit 7518c3445dc02df0d196f5f84e568d633c5141fb From c646c4d403d8c88cd3450f859fbdc34ec26d5bb5 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 9 Oct 2022 06:54:00 +0000 Subject: [PATCH 098/333] Unify tcx.constness and param env constness checks --- .../src/const_eval/fn_queries.rs | 72 ++++++++++++++----- compiler/rustc_metadata/src/rmeta/encoder.rs | 56 +++++++++++---- compiler/rustc_ty_utils/src/ty.rs | 69 +----------------- 3 files changed, 98 insertions(+), 99 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs index f1674d04f8d1..cdcebb61c2e8 100644 --- a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs @@ -25,12 +25,10 @@ pub fn is_parent_const_impl_raw(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { /// report whether said intrinsic has a `rustc_const_{un,}stable` attribute. Otherwise, return /// `Constness::NotConst`. fn constness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::Constness { - let def_id = def_id.expect_local(); - let node = tcx.hir().get_by_def_id(def_id); - - match node { + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); + match tcx.hir().get(hir_id) { hir::Node::Ctor(_) => hir::Constness::Const, - hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(impl_), .. }) => impl_.constness, + hir::Node::ForeignItem(hir::ForeignItem { kind: hir::ForeignItemKind::Fn(..), .. }) => { // Intrinsics use `rustc_const_{un,}stable` attributes to indicate constness. All other // foreign items cannot be evaluated at compile-time. @@ -41,20 +39,62 @@ fn constness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::Constness { }; if is_const { hir::Constness::Const } else { hir::Constness::NotConst } } - _ => { - if let Some(fn_kind) = node.fn_kind() { - if fn_kind.constness() == hir::Constness::Const { - return hir::Constness::Const; - } - // If the function itself is not annotated with `const`, it may still be a `const fn` - // if it resides in a const trait impl. - let is_const = is_parent_const_impl_raw(tcx, def_id); - if is_const { hir::Constness::Const } else { hir::Constness::NotConst } - } else { - hir::Constness::NotConst + hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. }) + if tcx.is_const_default_method(def_id) => + { + hir::Constness::Const + } + + hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(..), .. }) + | hir::Node::Item(hir::Item { kind: hir::ItemKind::Static(..), .. }) + | hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Const(..), .. }) + | hir::Node::AnonConst(_) + | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(..), .. }) + | hir::Node::ImplItem(hir::ImplItem { + kind: + hir::ImplItemKind::Fn( + hir::FnSig { + header: hir::FnHeader { constness: hir::Constness::Const, .. }, + .. + }, + .., + ), + .. + }) => hir::Constness::Const, + + hir::Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Type(..) | hir::ImplItemKind::Fn(..), + .. + }) => { + let parent_hir_id = tcx.hir().get_parent_node(hir_id); + match tcx.hir().get(parent_hir_id) { + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Impl(hir::Impl { constness, .. }), + .. + }) => *constness, + _ => span_bug!( + tcx.def_span(parent_hir_id.owner), + "impl item's parent node is not an impl", + ), } } + + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Fn(hir::FnSig { header: hir::FnHeader { constness, .. }, .. }, ..), + .. + }) + | hir::Node::TraitItem(hir::TraitItem { + kind: + hir::TraitItemKind::Fn(hir::FnSig { header: hir::FnHeader { constness, .. }, .. }, ..), + .. + }) + | hir::Node::Item(hir::Item { + kind: hir::ItemKind::Impl(hir::Impl { constness, .. }), + .. + }) => *constness, + + _ => hir::Constness::NotConst, } } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 3fc10197b912..ab4ecec2af36 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1059,6 +1059,43 @@ fn should_encode_const(def_kind: DefKind) -> bool { } } +fn should_encode_constness(def_kind: DefKind) -> bool { + match def_kind { + DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::Trait + | DefKind::AssocTy + | DefKind::Fn + | DefKind::Const + | DefKind::Static(..) + | DefKind::Ctor(..) + | DefKind::AssocFn + | DefKind::AssocConst + | DefKind::AnonConst + | DefKind::InlineConst + | DefKind::OpaqueTy + | DefKind::ImplTraitPlaceholder + | DefKind::Impl + | DefKind::Closure + | DefKind::Generator => true, + DefKind::Variant + | DefKind::TyAlias + | DefKind::TraitAlias + | DefKind::ForeignTy + | DefKind::Field + | DefKind::TyParam + | DefKind::Mod + | DefKind::ForeignMod + | DefKind::ConstParam + | DefKind::Macro(..) + | DefKind::Use + | DefKind::LifetimeParam + | DefKind::GlobalAsm + | DefKind::ExternCrate => false, + } +} + fn should_encode_trait_impl_trait_tys<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool { if tcx.def_kind(def_id) != DefKind::AssocFn { return false; @@ -1165,6 +1202,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { { record!(self.tables.trait_impl_trait_tys[def_id] <- table); } + if should_encode_constness(def_kind) { + self.tables.constness.set(def_id.index, tcx.constness(def_id)); + } } let inherent_impls = tcx.crate_inherent_impls(()); for (def_id, implementations) in inherent_impls.inherent_impls.iter() { @@ -1192,7 +1232,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { }; record!(self.tables.variant_data[def_id] <- data); - self.tables.constness.set(def_id.index, hir::Constness::Const); record_array!(self.tables.children[def_id] <- variant.fields.iter().map(|f| { assert!(f.did.is_local()); f.did.index @@ -1220,7 +1259,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { }; record!(self.tables.variant_data[def_id] <- data); - self.tables.constness.set(def_id.index, hir::Constness::Const); if variant.ctor_kind == CtorKind::Fn { record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id)); } @@ -1284,7 +1322,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { record!(self.tables.repr_options[def_id] <- adt_def.repr()); record!(self.tables.variant_data[def_id] <- data); - self.tables.constness.set(def_id.index, hir::Constness::Const); if variant.ctor_kind == CtorKind::Fn { record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id)); } @@ -1320,7 +1357,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } }; self.tables.asyncness.set(def_id.index, m_sig.header.asyncness); - self.tables.constness.set(def_id.index, hir::Constness::NotConst); } ty::AssocKind::Type => { self.encode_explicit_item_bounds(def_id); @@ -1345,13 +1381,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let hir::ImplItemKind::Fn(ref sig, body) = ast_item.kind else { bug!() }; self.tables.asyncness.set(def_id.index, sig.header.asyncness); record_array!(self.tables.fn_arg_names[def_id] <- self.tcx.hir().body_param_names(body)); - // Can be inside `impl const Trait`, so using sig.header.constness is not reliable - let constness = if self.tcx.is_const_fn_raw(def_id) { - hir::Constness::Const - } else { - hir::Constness::NotConst - }; - self.tables.constness.set(def_id.index, constness); } ty::AssocKind::Const | ty::AssocKind::Type => {} } @@ -1474,7 +1503,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { hir::ItemKind::Fn(ref sig, .., body) => { self.tables.asyncness.set(def_id.index, sig.header.asyncness); record_array!(self.tables.fn_arg_names[def_id] <- self.tcx.hir().body_param_names(body)); - self.tables.constness.set(def_id.index, sig.header.constness); } hir::ItemKind::Macro(ref macro_def, _) => { if macro_def.macro_rules { @@ -1495,7 +1523,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { hir::ItemKind::Struct(ref struct_def, _) => { let adt_def = self.tcx.adt_def(def_id); record!(self.tables.repr_options[def_id] <- adt_def.repr()); - self.tables.constness.set(def_id.index, hir::Constness::Const); // Encode def_ids for each field and method // for methods, write all the stuff get_trait_method @@ -1524,9 +1551,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { is_non_exhaustive: variant.is_field_list_non_exhaustive(), }); } - hir::ItemKind::Impl(hir::Impl { defaultness, constness, .. }) => { + hir::ItemKind::Impl(hir::Impl { defaultness, .. }) => { self.tables.impl_defaultness.set(def_id.index, *defaultness); - self.tables.constness.set(def_id.index, *constness); let trait_ref = self.tcx.impl_trait_ref(def_id); if let Some(trait_ref) = trait_ref { diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 30efbf661759..344426a18bca 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -133,77 +133,10 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> { let local_did = def_id.as_local(); let hir_id = local_did.map(|def_id| tcx.hir().local_def_id_to_hir_id(def_id)); - let constness = match hir_id { - Some(hir_id) => match tcx.hir().get(hir_id) { - hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. }) - if tcx.is_const_default_method(def_id) => - { - hir::Constness::Const - } - - hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(..), .. }) - | hir::Node::Item(hir::Item { kind: hir::ItemKind::Static(..), .. }) - | hir::Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Const(..), .. - }) - | hir::Node::AnonConst(_) - | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(..), .. }) - | hir::Node::ImplItem(hir::ImplItem { - kind: - hir::ImplItemKind::Fn( - hir::FnSig { - header: hir::FnHeader { constness: hir::Constness::Const, .. }, - .. - }, - .., - ), - .. - }) => hir::Constness::Const, - - hir::Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Type(..) | hir::ImplItemKind::Fn(..), - .. - }) => { - let parent_hir_id = tcx.hir().get_parent_node(hir_id); - match tcx.hir().get(parent_hir_id) { - hir::Node::Item(hir::Item { - kind: hir::ItemKind::Impl(hir::Impl { constness, .. }), - .. - }) => *constness, - _ => span_bug!( - tcx.def_span(parent_hir_id.owner), - "impl item's parent node is not an impl", - ), - } - } - - hir::Node::Item(hir::Item { - kind: - hir::ItemKind::Fn(hir::FnSig { header: hir::FnHeader { constness, .. }, .. }, ..), - .. - }) - | hir::Node::TraitItem(hir::TraitItem { - kind: - hir::TraitItemKind::Fn( - hir::FnSig { header: hir::FnHeader { constness, .. }, .. }, - .., - ), - .. - }) - | hir::Node::Item(hir::Item { - kind: hir::ItemKind::Impl(hir::Impl { constness, .. }), - .. - }) => *constness, - - _ => hir::Constness::NotConst, - }, - None => hir::Constness::NotConst, - }; - let unnormalized_env = ty::ParamEnv::new( tcx.intern_predicates(&predicates), traits::Reveal::UserFacing, - constness, + tcx.constness(def_id), ); let body_id = From 3021598fdbba9ce32d313bba6b49e03c7701da1f Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 12 Oct 2022 04:03:59 +0000 Subject: [PATCH 099/333] Do not register placeholder region outlives when considering_regions is false --- .../src/traits/fulfill.rs | 2 +- .../higher-rank-trait-bounds/issue-100689.rs | 29 +++++++++++++++++ .../higher-rank-trait-bounds/issue-102899.rs | 32 +++++++++++++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/higher-rank-trait-bounds/issue-100689.rs create mode 100644 src/test/ui/higher-rank-trait-bounds/issue-102899.rs diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 6eb023956855..ee752a1c73dd 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -355,7 +355,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { } ty::PredicateKind::RegionOutlives(data) => { - if infcx.considering_regions || data.has_placeholders() { + if infcx.considering_regions { infcx.region_outlives_predicate(&obligation.cause, Binder::dummy(data)); } diff --git a/src/test/ui/higher-rank-trait-bounds/issue-100689.rs b/src/test/ui/higher-rank-trait-bounds/issue-100689.rs new file mode 100644 index 000000000000..2db7f8a354cf --- /dev/null +++ b/src/test/ui/higher-rank-trait-bounds/issue-100689.rs @@ -0,0 +1,29 @@ +// check-pass + +struct Foo<'a> { + foo: &'a mut usize, +} + +trait Bar<'a> { + type FooRef<'b> + where + 'a: 'b; + fn uwu(foo: Foo<'a>, f: impl for<'b> FnMut(Self::FooRef<'b>)); +} +impl<'a> Bar<'a> for () { + type FooRef<'b> + = + &'b Foo<'a> + where + 'a : 'b, + ; + + fn uwu( + foo: Foo<'a>, + mut f: impl for<'b> FnMut(&'b Foo<'a>), //relevant part + ) { + f(&foo); + } +} + +fn main() {} diff --git a/src/test/ui/higher-rank-trait-bounds/issue-102899.rs b/src/test/ui/higher-rank-trait-bounds/issue-102899.rs new file mode 100644 index 000000000000..952b81584f30 --- /dev/null +++ b/src/test/ui/higher-rank-trait-bounds/issue-102899.rs @@ -0,0 +1,32 @@ +// check-pass + +pub trait BufferTrait<'buffer> { + type Subset<'channel> + where + 'buffer: 'channel; + + fn for_each_subset(&self, f: F) + where + F: for<'channel> Fn(Self::Subset<'channel>); +} + +pub struct SomeBuffer<'buffer> { + samples: &'buffer [()], +} + +impl<'buffer> BufferTrait<'buffer> for SomeBuffer<'buffer> { + type Subset<'subset> = Subset<'subset> where 'buffer: 'subset; + + fn for_each_subset(&self, _f: F) + where + F: for<'subset> Fn(Subset<'subset>), + { + todo!() + } +} + +pub struct Subset<'subset> { + buffer: &'subset [()], +} + +fn main() {} From bef8681a1837790f2745c1f6a7f8214af2fd7f5d Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 12 Oct 2022 04:17:21 +0000 Subject: [PATCH 100/333] TyAlias needs encoded constness too, for layout computation in rustdoc --- compiler/rustc_metadata/src/rmeta/encoder.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index ab4ecec2af36..68119598285c 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1078,9 +1078,9 @@ fn should_encode_constness(def_kind: DefKind) -> bool { | DefKind::ImplTraitPlaceholder | DefKind::Impl | DefKind::Closure - | DefKind::Generator => true, + | DefKind::Generator + | DefKind::TyAlias => true, DefKind::Variant - | DefKind::TyAlias | DefKind::TraitAlias | DefKind::ForeignTy | DefKind::Field From 9c26fec046bd993bb59edff1611be4c603ba653e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 12 Oct 2022 08:19:50 +0200 Subject: [PATCH 101/333] should-skip-this: add missing backslash --- src/ci/scripts/should-skip-this.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ci/scripts/should-skip-this.sh b/src/ci/scripts/should-skip-this.sh index 60c2960b160e..a8a1899317f8 100755 --- a/src/ci/scripts/should-skip-this.sh +++ b/src/ci/scripts/should-skip-this.sh @@ -19,7 +19,7 @@ if [[ -n "${CI_ONLY_WHEN_SUBMODULES_CHANGED-}" ]]; then # those files are present in the diff a submodule was updated. echo "Submodules were updated" elif ! (git diff --quiet "$BASE_COMMIT" -- \ - src/tools/clippy src/tools/rustfmt src/tools/miri + src/tools/clippy src/tools/rustfmt src/tools/miri \ library/std/src/sys); then # There is not an easy blanket search for subtrees. For now, manually list # the subtrees. From b11dddd1c17c7d404d0a36a30baaae331cece982 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Wed, 12 Oct 2022 19:03:46 +0900 Subject: [PATCH 102/333] fix #102946 --- compiler/rustc_resolve/src/late.rs | 2 +- src/test/ui/resolve/issue-102946.rs | 7 +++++++ src/test/ui/resolve/issue-102946.stderr | 26 +++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/resolve/issue-102946.rs create mode 100644 src/test/ui/resolve/issue-102946.stderr diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 776c8ad528c0..cc877e2fd30c 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1969,7 +1969,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { None } }) - .map(|res| res.expect_full_res()) + .and_then(|res| res.full_res()) .filter(|res| { // Permit the types that unambiguously always // result in the same type constructor being used diff --git a/src/test/ui/resolve/issue-102946.rs b/src/test/ui/resolve/issue-102946.rs new file mode 100644 index 000000000000..c6feca6f32f6 --- /dev/null +++ b/src/test/ui/resolve/issue-102946.rs @@ -0,0 +1,7 @@ +impl Error for str::Utf8Error { + //~^ ERROR cannot find trait `Error` in this scope + //~| ERROR ambiguous associated type + fn description(&self) {} +} + +fn main() {} diff --git a/src/test/ui/resolve/issue-102946.stderr b/src/test/ui/resolve/issue-102946.stderr new file mode 100644 index 000000000000..65be0258e6db --- /dev/null +++ b/src/test/ui/resolve/issue-102946.stderr @@ -0,0 +1,26 @@ +error[E0405]: cannot find trait `Error` in this scope + --> $DIR/issue-102946.rs:1:6 + | +LL | impl Error for str::Utf8Error { + | ^^^^^ not found in this scope + | +help: consider importing this trait + | +LL | use std::error::Error; + | + +error[E0223]: ambiguous associated type + --> $DIR/issue-102946.rs:1:16 + | +LL | impl Error for str::Utf8Error { + | ^^^^^^^^^^^^^^ + | +help: you are looking for the module in `std`, not the primitive type + | +LL | impl Error for std::str::Utf8Error { + | +++++ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0223, E0405. +For more information about an error, try `rustc --explain E0223`. From 0e3867338d66a016d6e6214a18537fbb715ed752 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Tue, 4 Oct 2022 22:34:07 +0200 Subject: [PATCH 103/333] Add tidy directoy `tidy-alphabetical` It can be used to ensure that a list of things is sorted alphabetically. It goes off lines, but contains several heuristics to work with normal Rust code (looking at indentation, ignoring comments and attributes). --- src/tools/tidy/src/alphabetical.rs | 98 ++++++++++++++++++++++++++++++ src/tools/tidy/src/lib.rs | 1 + src/tools/tidy/src/main.rs | 2 + 3 files changed, 101 insertions(+) create mode 100644 src/tools/tidy/src/alphabetical.rs diff --git a/src/tools/tidy/src/alphabetical.rs b/src/tools/tidy/src/alphabetical.rs new file mode 100644 index 000000000000..e02b2d98f421 --- /dev/null +++ b/src/tools/tidy/src/alphabetical.rs @@ -0,0 +1,98 @@ +//! Checks that a list of items is in alphabetical order +//! +//! To use, use the following annotation in the code: +//! ```rust +//! // tidy-alphabetical-start +//! fn aaa() {} +//! fn eee() {} +//! fn z() {} +//! // tidy-alphabetical-end +//! ``` +//! +//! The following lines are ignored: +//! - Lines that are indented with more or less spaces than the first line +//! - Lines starting with `//`, `#[`, `)`, `]`, `}` if the comment has the same indentation as +//! the first line +//! +//! If a line ends with an opening bracket, the line is ignored and the next line will have +//! its extra indentation ignored. + +use std::{fmt::Display, path::Path}; + +use crate::walk::{filter_dirs, walk}; + +fn indentation(line: &str) -> usize { + line.find(|c| c != ' ').unwrap_or(0) +} + +fn is_close_bracket(c: char) -> bool { + matches!(c, ')' | ']' | '}') +} + +fn check_section<'a>( + file: impl Display, + lines: impl Iterator, + bad: &mut bool, +) { + let content_lines = lines.take_while(|(_, line)| !line.contains("// tidy-alphabetical-end")); + + let mut prev_line = String::new(); + let mut first_indent = None; + let mut in_split_line = None; + + for (line_idx, line) in content_lines { + let indent = first_indent.unwrap_or_else(|| { + let indent = indentation(line); + first_indent = Some(indent); + indent + }); + + let line = if let Some(prev_split_line) = in_split_line { + in_split_line = None; + format!("{prev_split_line}{}", line.trim_start()) + } else { + line.to_string() + }; + + if indentation(&line) != indent { + continue; + } + + let trimmed_line = line.trim_start_matches(' '); + + if trimmed_line.starts_with("//") + || trimmed_line.starts_with("#[") + || trimmed_line.starts_with(is_close_bracket) + { + continue; + } + + if line.trim_end().ends_with('(') { + in_split_line = Some(line); + continue; + } + + let prev_line_trimmed_lowercase = prev_line.trim_start_matches(' ').to_lowercase(); + + if trimmed_line.to_lowercase() < prev_line_trimmed_lowercase { + tidy_error!(bad, "{file}:{}: line not in alphabetical order", line_idx + 1,); + } + + prev_line = line; + } +} + +const START_COMMENT: &str = "// tidy-alphabetical-start"; + +pub fn check(path: &Path, bad: &mut bool) { + walk(path, &mut filter_dirs, &mut |entry, contents| { + let file = &entry.path().display(); + + let mut lines = contents.lines().enumerate(); + while let Some((_, line)) = lines.next() { + if line.contains(START_COMMENT) { + check_section(file, &mut lines, bad); + } + } + }); +} diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index e82cca402e2d..fc0bce585723 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -38,6 +38,7 @@ macro_rules! tidy_error { }); } +pub mod alphabetical; pub mod bins; pub mod debug_artifacts; pub mod deps; diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs index c1ce94f47055..8fe361c45a26 100644 --- a/src/tools/tidy/src/main.rs +++ b/src/tools/tidy/src/main.rs @@ -90,6 +90,8 @@ fn main() { check!(edition, &compiler_path); check!(edition, &library_path); + check!(alphabetical, &compiler_path); + let collected = { while handles.len() >= concurrency.get() { handles.pop_front().unwrap().join().unwrap(); From 7bfef19844f20514cda40437847880ec8a663926 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Wed, 5 Oct 2022 21:46:21 +0200 Subject: [PATCH 104/333] Use `tidy-alphabetical` in the compiler --- compiler/rustc_ast/src/ast.rs | 5 +- compiler/rustc_ast/src/token.rs | 3 +- compiler/rustc_ast/src/tokenstream.rs | 3 +- .../rustc_const_eval/src/interpret/operand.rs | 3 +- .../rustc_const_eval/src/interpret/place.rs | 5 +- compiler/rustc_error_messages/src/lib.rs | 2 + compiler/rustc_hir/src/hir.rs | 5 +- compiler/rustc_interface/src/tests.rs | 27 +++-- compiler/rustc_middle/src/mir/mod.rs | 3 +- compiler/rustc_middle/src/mir/syntax.rs | 3 +- compiler/rustc_middle/src/thir.rs | 3 +- compiler/rustc_middle/src/ty/mod.rs | 3 +- .../rustc_parse/src/parser/attr_wrapper.rs | 3 +- compiler/rustc_session/src/options.rs | 106 +++++++++--------- compiler/rustc_target/src/abi/call/mod.rs | 3 +- src/test/rustdoc-ui/z-help.stdout | 44 ++++---- 16 files changed, 118 insertions(+), 103 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 340302766d24..60b7f2e4c222 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3039,7 +3039,7 @@ pub type ForeignItem = Item; mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; - // These are in alphabetical order, which is easy to maintain. + // tidy-alphabetical-start static_assert_size!(AssocItem, 104); static_assert_size!(AssocItemKind, 32); static_assert_size!(Attribute, 32); @@ -3060,11 +3060,12 @@ mod size_asserts { static_assert_size!(Local, 72); static_assert_size!(Param, 40); static_assert_size!(Pat, 120); - static_assert_size!(PatKind, 96); static_assert_size!(Path, 40); static_assert_size!(PathSegment, 24); + static_assert_size!(PatKind, 96); static_assert_size!(Stmt, 32); static_assert_size!(StmtKind, 16); static_assert_size!(Ty, 96); static_assert_size!(TyKind, 72); + // tidy-alphabetical-end } diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 16224d71e456..83b10d906e29 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -889,10 +889,11 @@ where mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; - // These are in alphabetical order, which is easy to maintain. + // tidy-alphabetical-start static_assert_size!(Lit, 12); static_assert_size!(LitKind, 2); static_assert_size!(Nonterminal, 16); static_assert_size!(Token, 24); static_assert_size!(TokenKind, 16); + // tidy-alphabetical-end } diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index 1d3c4fcca0a4..015f5c1ee8ae 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -646,10 +646,11 @@ impl DelimSpan { mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; - // These are in alphabetical order, which is easy to maintain. + // tidy-alphabetical-start static_assert_size!(AttrTokenStream, 8); static_assert_size!(AttrTokenTree, 32); static_assert_size!(LazyAttrTokenStream, 8); static_assert_size!(TokenStream, 8); static_assert_size!(TokenTree, 32); + // tidy-alphabetical-end } diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 510adde62962..719588a936ce 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -788,9 +788,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; - // These are in alphabetical order, which is easy to maintain. + // tidy-alphabetical-start static_assert_size!(Immediate, 48); static_assert_size!(ImmTy<'_>, 64); static_assert_size!(Operand, 56); static_assert_size!(OpTy<'_>, 80); + // tidy-alphabetical-end } diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index eeeb7d6d3e5c..b0625b5f412e 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -892,10 +892,11 @@ where mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; - // These are in alphabetical order, which is easy to maintain. - static_assert_size!(MemPlaceMeta, 24); + // tidy-alphabetical-start static_assert_size!(MemPlace, 40); + static_assert_size!(MemPlaceMeta, 24); static_assert_size!(MPlaceTy<'_>, 64); static_assert_size!(Place, 40); static_assert_size!(PlaceTy<'_>, 64); + // tidy-alphabetical-end } diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index a2d507328b38..b95fdebcc394 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -35,6 +35,7 @@ pub use unic_langid::{langid, LanguageIdentifier}; // Generates `DEFAULT_LOCALE_RESOURCES` static and `fluent_generated` module. fluent_messages! { + // tidy-alphabetical-start ast_lowering => "../locales/en-US/ast_lowering.ftl", ast_passes => "../locales/en-US/ast_passes.ftl", attr => "../locales/en-US/attr.ftl", @@ -64,6 +65,7 @@ fluent_messages! { symbol_mangling => "../locales/en-US/symbol_mangling.ftl", trait_selection => "../locales/en-US/trait_selection.ftl", ty_utils => "../locales/en-US/ty_utils.ftl", + // tidy-alphabetical-end } pub use fluent_generated::{self as fluent, DEFAULT_LOCALE_RESOURCES}; diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 098f9d515497..bc149e48d89e 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -3514,7 +3514,7 @@ impl<'hir> Node<'hir> { #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] mod size_asserts { use super::*; - // These are in alphabetical order, which is easy to maintain. + // tidy-alphabetical-start static_assert_size!(Block<'_>, 48); static_assert_size!(Body<'_>, 32); static_assert_size!(Expr<'_>, 64); @@ -3533,9 +3533,9 @@ mod size_asserts { static_assert_size!(Local<'_>, 64); static_assert_size!(Param<'_>, 32); static_assert_size!(Pat<'_>, 72); - static_assert_size!(PatKind<'_>, 48); static_assert_size!(Path<'_>, 40); static_assert_size!(PathSegment<'_>, 48); + static_assert_size!(PatKind<'_>, 48); static_assert_size!(QPath<'_>, 24); static_assert_size!(Res, 12); static_assert_size!(Stmt<'_>, 32); @@ -3544,4 +3544,5 @@ mod size_asserts { static_assert_size!(TraitItemKind<'_>, 48); static_assert_size!(Ty<'_>, 48); static_assert_size!(TyKind<'_>, 32); + // tidy-alphabetical-end } diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 98eeaad976fe..d64cdcdbaa9d 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -540,7 +540,7 @@ fn test_codegen_options_tracking_hash() { } // Make sure that changing an [UNTRACKED] option leaves the hash unchanged. - // This list is in alphabetical order. + // tidy-alphabetical-start untracked!(ar, String::from("abc")); untracked!(codegen_units, Some(42)); untracked!(default_linker_libraries, true); @@ -556,6 +556,7 @@ fn test_codegen_options_tracking_hash() { untracked!(rpath, true); untracked!(save_temps, true); untracked!(strip, Strip::Debuginfo); + // tidy-alphabetical-end macro_rules! tracked { ($name: ident, $non_default_value: expr) => { @@ -567,7 +568,7 @@ fn test_codegen_options_tracking_hash() { } // Make sure that changing a [TRACKED] option changes the hash. - // This list is in alphabetical order. + // tidy-alphabetical-start tracked!(code_model, Some(CodeModel::Large)); tracked!(control_flow_guard, CFGuard::Checks); tracked!(debug_assertions, Some(true)); @@ -577,8 +578,8 @@ fn test_codegen_options_tracking_hash() { tracked!(force_unwind_tables, Some(true)); tracked!(inline_threshold, Some(0xf007ba11)); tracked!(instrument_coverage, Some(InstrumentCoverage::All)); - tracked!(linker_plugin_lto, LinkerPluginLto::LinkerPluginAuto); tracked!(link_dead_code, Some(true)); + tracked!(linker_plugin_lto, LinkerPluginLto::LinkerPluginAuto); tracked!(llvm_args, vec![String::from("1"), String::from("2")]); tracked!(lto, LtoCli::Fat); tracked!(metadata, vec![String::from("A"), String::from("B")]); @@ -599,6 +600,7 @@ fn test_codegen_options_tracking_hash() { tracked!(symbol_mangling_version, Some(SymbolManglingVersion::V0)); tracked!(target_cpu, Some(String::from("abc"))); tracked!(target_feature, String::from("all the features, all of them")); + // tidy-alphabetical-end } #[test] @@ -619,12 +621,13 @@ fn test_top_level_options_tracked_no_crate() { } // Make sure that changing a [TRACKED_NO_CRATE_HASH] option leaves the crate hash unchanged but changes the incremental hash. - // This list is in alphabetical order. - tracked!(remap_path_prefix, vec![("/home/bors/rust".into(), "src".into())]); + // tidy-alphabetical-start tracked!( real_rust_source_base_dir, Some("/home/bors/rust/.rustup/toolchains/nightly/lib/rustlib/src/rust".into()) ); + tracked!(remap_path_prefix, vec![("/home/bors/rust".into(), "src".into())]); + // tidy-alphabetical-end } #[test] @@ -641,7 +644,7 @@ fn test_unstable_options_tracking_hash() { } // Make sure that changing an [UNTRACKED] option leaves the hash unchanged. - // This list is in alphabetical order. + // tidy-alphabetical-start untracked!(assert_incr_state, Some(String::from("loaded"))); untracked!(deduplicate_diagnostics, false); untracked!(dep_tasks, true); @@ -678,12 +681,12 @@ fn test_unstable_options_tracking_hash() { untracked!(perf_stats, true); // `pre_link_arg` is omitted because it just forwards to `pre_link_args`. untracked!(pre_link_args, vec![String::from("abc"), String::from("def")]); - untracked!(profile_closures, true); untracked!(print_llvm_passes, true); untracked!(print_mono_items, Some(String::from("abc"))); untracked!(print_type_sizes, true); untracked!(proc_macro_backtrace, true); untracked!(proc_macro_execution_strategy, ProcMacroExecutionStrategy::CrossThread); + untracked!(profile_closures, true); untracked!(query_dep_graph, true); untracked!(save_analysis, true); untracked!(self_profile, SwitchWithOptPath::Enabled(None)); @@ -701,6 +704,7 @@ fn test_unstable_options_tracking_hash() { untracked!(unstable_options, true); untracked!(validate_mir, true); untracked!(verbose, true); + // tidy-alphabetical-end macro_rules! tracked { ($name: ident, $non_default_value: expr) => { @@ -712,7 +716,7 @@ fn test_unstable_options_tracking_hash() { } // Make sure that changing a [TRACKED] option changes the hash. - // This list is in alphabetical order. + // tidy-alphabetical-start tracked!(allow_features, Some(vec![String::from("lang_items")])); tracked!(always_encode_mir, true); tracked!(asm_comments, true); @@ -733,10 +737,10 @@ fn test_unstable_options_tracking_hash() { tracked!(debug_macros, true); tracked!(dep_info_omit_d_target, true); tracked!(drop_tracking, true); - tracked!(export_executable_symbols, true); tracked!(dual_proc_macros, true); tracked!(dwarf_version, Some(5)); tracked!(emit_thin_lto, false); + tracked!(export_executable_symbols, true); tracked!(fewer_names, Some(true)); tracked!(force_unstable_if_unmarked, true); tracked!(fuel, Some(("abc".to_string(), 99))); @@ -759,8 +763,8 @@ fn test_unstable_options_tracking_hash() { tracked!(mutable_noalias, Some(true)); tracked!(no_generate_arange_section, true); tracked!(no_link, true); - tracked!(no_unique_section_names, true); tracked!(no_profiler_runtime, true); + tracked!(no_unique_section_names, true); tracked!(oom, OomStrategy::Panic); tracked!(osx_rpath_install_name, true); tracked!(packed_bundled_libs, true); @@ -773,8 +777,8 @@ fn test_unstable_options_tracking_hash() { tracked!(print_fuel, Some("abc".to_string())); tracked!(profile, true); tracked!(profile_emit, Some(PathBuf::from("abc"))); - tracked!(profiler_runtime, "abc".to_string()); tracked!(profile_sample_use, Some(PathBuf::from("abc"))); + tracked!(profiler_runtime, "abc".to_string()); tracked!(relax_elf_relocations, Some(true)); tracked!(relro_level, Some(RelroLevel::Full)); tracked!(remap_cwd_prefix, Some(PathBuf::from("abc"))); @@ -803,6 +807,7 @@ fn test_unstable_options_tracking_hash() { tracked!(verify_llvm_ir, true); tracked!(virtual_function_elimination, true); tracked!(wasi_exec_model, Some(WasiExecModel::Reactor)); + // tidy-alphabetical-end macro_rules! tracked_no_crate_hash { ($name: ident, $non_default_value: expr) => { diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index d3a98e43c538..d4258151ff3f 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -2946,11 +2946,12 @@ impl Location { mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; - // These are in alphabetical order, which is easy to maintain. + // tidy-alphabetical-start static_assert_size!(BasicBlockData<'_>, 144); static_assert_size!(LocalDecl<'_>, 56); static_assert_size!(Statement<'_>, 32); static_assert_size!(StatementKind<'_>, 16); static_assert_size!(Terminator<'_>, 112); static_assert_size!(TerminatorKind<'_>, 96); + // tidy-alphabetical-end } diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 9a22a12b93b3..85ef51f129bb 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1245,10 +1245,11 @@ pub enum BinOp { #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] mod size_asserts { use super::*; - // These are in alphabetical order, which is easy to maintain. + // tidy-alphabetical-start static_assert_size!(AggregateKind<'_>, 40); static_assert_size!(Operand<'_>, 24); static_assert_size!(Place<'_>, 16); static_assert_size!(PlaceElem<'_>, 24); static_assert_size!(Rvalue<'_>, 40); + // tidy-alphabetical-end } diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index f46f0ea4cabe..ea7a507d7a43 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -848,7 +848,7 @@ impl<'tcx> fmt::Display for Pat<'tcx> { #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] mod size_asserts { use super::*; - // These are in alphabetical order, which is easy to maintain. + // tidy-alphabetical-start static_assert_size!(Block, 56); static_assert_size!(Expr<'_>, 64); static_assert_size!(ExprKind<'_>, 40); @@ -856,4 +856,5 @@ mod size_asserts { static_assert_size!(PatKind<'_>, 56); static_assert_size!(Stmt<'_>, 48); static_assert_size!(StmtKind<'_>, 40); + // tidy-alphabetical-end } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 753d7bffe84c..12d24d6751e2 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2668,8 +2668,9 @@ pub struct DestructuredConst<'tcx> { mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; - // These are in alphabetical order, which is easy to maintain. + // tidy-alphabetical-start static_assert_size!(PredicateS<'_>, 48); static_assert_size!(TyS<'_>, 40); static_assert_size!(WithStableHash>, 56); + // tidy-alphabetical-end } diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index 0dc05475ce94..81c051b8f35e 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -459,7 +459,8 @@ fn make_token_stream( mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; - // These are in alphabetical order, which is easy to maintain. + // tidy-alphabetical-start static_assert_size!(AttrWrapper, 16); static_assert_size!(LazyAttrTokenStreamImpl, 144); + // tidy-alphabetical-end } diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 8d527c05122d..102df3a4d7ea 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1075,12 +1075,11 @@ mod parse { options! { CodegenOptions, CG_OPTIONS, cgopts, "C", "codegen", - // This list is in alphabetical order. - // // If you add a new option, please update: // - compiler/rustc_interface/src/tests.rs // - src/doc/rustc/src/codegen-options/index.md + // tidy-alphabetical-start ar: String = (String::new(), parse_string, [UNTRACKED], "this option is deprecated and does nothing"), #[rustc_lint_opt_deny_field_access("use `Session::code_model` instead of this field")] @@ -1195,9 +1194,8 @@ options! { target_feature: String = (String::new(), parse_target_feature, [TRACKED], "target specific attributes. (`rustc --print target-features` for details). \ This feature is unsafe."), + // tidy-alphabetical-end - // This list is in alphabetical order. - // // If you add a new option, please update: // - compiler/rustc_interface/src/tests.rs // - src/doc/rustc/src/codegen-options/index.md @@ -1206,24 +1204,23 @@ options! { options! { UnstableOptions, Z_OPTIONS, dbopts, "Z", "unstable", - // This list is in alphabetical order. - // // If you add a new option, please update: // - compiler/rustc_interface/src/tests.rs // - src/doc/unstable-book/src/compiler-flags + // tidy-alphabetical-start allow_features: Option> = (None, parse_opt_comma_list, [TRACKED], "only allow the listed language features to be enabled in code (space separated)"), always_encode_mir: bool = (false, parse_bool, [TRACKED], "encode MIR of all functions into the crate metadata (default: no)"), - assume_incomplete_release: bool = (false, parse_bool, [TRACKED], - "make cfg(version) treat the current version as incomplete (default: no)"), #[rustc_lint_opt_deny_field_access("use `Session::asm_comments` instead of this field")] asm_comments: bool = (false, parse_bool, [TRACKED], "generate comments into the assembly (may change behavior) (default: no)"), assert_incr_state: Option = (None, parse_opt_string, [UNTRACKED], "assert that the incremental cache is in given state: \ either `loaded` or `not-loaded`."), + assume_incomplete_release: bool = (false, parse_bool, [TRACKED], + "make cfg(version) treat the current version as incomplete (default: no)"), #[rustc_lint_opt_deny_field_access("use `Session::binary_dep_depinfo` instead of this field")] binary_dep_depinfo: bool = (false, parse_bool, [TRACKED], "include artifacts (sysroot, crate dependencies) used during compilation in dep-info \ @@ -1256,6 +1253,8 @@ options! { dep_tasks: bool = (false, parse_bool, [UNTRACKED], "print tasks that execute and the color their dep node gets (requires debug build) \ (default: no)"), + diagnostic_width: Option = (None, parse_opt_number, [UNTRACKED], + "set the current output width for diagnostic truncation"), dlltool: Option = (None, parse_opt_pathbuf, [UNTRACKED], "import library generation tool (windows-gnu only)"), dont_buffer_diagnostics: bool = (false, parse_bool, [UNTRACKED], @@ -1337,16 +1336,16 @@ options! { "hash spans relative to their parent item for incr. comp. (default: no)"), incremental_verify_ich: bool = (false, parse_bool, [UNTRACKED], "verify incr. comp. hashes of green query instances (default: no)"), + inline_in_all_cgus: Option = (None, parse_opt_bool, [TRACKED], + "control whether `#[inline]` functions are in all CGUs"), inline_llvm: bool = (true, parse_bool, [TRACKED], "enable LLVM inlining (default: yes)"), inline_mir: Option = (None, parse_opt_bool, [TRACKED], "enable MIR inlining (default: no)"), - inline_mir_threshold: Option = (None, parse_opt_number, [TRACKED], - "a default MIR inlining threshold (default: 50)"), inline_mir_hint_threshold: Option = (None, parse_opt_number, [TRACKED], "inlining threshold for functions with inline hint (default: 100)"), - inline_in_all_cgus: Option = (None, parse_opt_bool, [TRACKED], - "control whether `#[inline]` functions are in all CGUs"), + inline_mir_threshold: Option = (None, parse_opt_number, [TRACKED], + "a default MIR inlining threshold (default: 50)"), input_stats: bool = (false, parse_bool, [UNTRACKED], "gather statistics about the input (default: no)"), #[rustc_lint_opt_deny_field_access("use `Session::instrument_coverage` instead of this field")] @@ -1363,6 +1362,8 @@ options! { "insert function instrument code for mcount-based tracing (default: no)"), keep_hygiene_data: bool = (false, parse_bool, [UNTRACKED], "keep hygiene data after analysis (default: no)"), + layout_seed: Option = (None, parse_opt_number, [TRACKED], + "seed layout randomization"), link_native_libraries: bool = (true, parse_bool, [UNTRACKED], "link native libraries in the linker invocation (default: yes)"), link_only: bool = (false, parse_bool, [TRACKED], @@ -1392,11 +1393,11 @@ options! { "use like `-Zmir-enable-passes=+DestProp,-InstCombine`. Forces the specified passes to be \ enabled, overriding all other checks. Passes that are not specified are enabled or \ disabled by other flags as usual."), - mir_pretty_relative_line_numbers: bool = (false, parse_bool, [UNTRACKED], - "use line numbers relative to the function in mir pretty printing"), #[rustc_lint_opt_deny_field_access("use `Session::mir_opt_level` instead of this field")] mir_opt_level: Option = (None, parse_opt_number, [TRACKED], "MIR optimization level (0-4; default: 1 in non optimized builds and 2 in optimized builds)"), + mir_pretty_relative_line_numbers: bool = (false, parse_bool, [UNTRACKED], + "use line numbers relative to the function in mir pretty printing"), move_size_limit: Option = (None, parse_opt_number, [TRACKED], "the size at which the `large_assignments` lint starts to be emitted"), mutable_noalias: Option = (None, parse_opt_bool, [TRACKED], @@ -1419,18 +1420,16 @@ options! { "compile without linking"), no_parallel_llvm: bool = (false, parse_no_flag, [UNTRACKED], "run LLVM in non-parallel mode (while keeping codegen-units and ThinLTO)"), - no_unique_section_names: bool = (false, parse_bool, [TRACKED], - "do not use unique names for text and data sections when -Z function-sections is used"), no_profiler_runtime: bool = (false, parse_no_flag, [TRACKED], "prevent automatic injection of the profiler_builtins crate"), + no_unique_section_names: bool = (false, parse_bool, [TRACKED], + "do not use unique names for text and data sections when -Z function-sections is used"), normalize_docs: bool = (false, parse_bool, [TRACKED], "normalize associated items in rustdoc when generating documentation"), oom: OomStrategy = (OomStrategy::Abort, parse_oom_strategy, [TRACKED], "panic strategy for out-of-memory handling"), osx_rpath_install_name: bool = (false, parse_bool, [TRACKED], "pass `-install_name @rpath/...` to the macOS linker (default: no)"), - diagnostic_width: Option = (None, parse_opt_number, [UNTRACKED], - "set the current output width for diagnostic truncation"), packed_bundled_libs: bool = (false, parse_bool, [TRACKED], "change rlib format to store native libraries as archives"), panic_abort_tests: bool = (false, parse_bool, [TRACKED], @@ -1480,25 +1479,20 @@ options! { profile_emit: Option = (None, parse_opt_pathbuf, [TRACKED], "file path to emit profiling data at runtime when using 'profile' \ (default based on relative source path)"), - profiler_runtime: String = (String::from("profiler_builtins"), parse_string, [TRACKED], - "name of the profiler runtime crate to automatically inject (default: `profiler_builtins`)"), profile_sample_use: Option = (None, parse_opt_pathbuf, [TRACKED], "use the given `.prof` file for sampled profile-guided optimization (also known as AutoFDO)"), + profiler_runtime: String = (String::from("profiler_builtins"), parse_string, [TRACKED], + "name of the profiler runtime crate to automatically inject (default: `profiler_builtins`)"), query_dep_graph: bool = (false, parse_bool, [UNTRACKED], "enable queries of the dependency graph for regression testing (default: no)"), randomize_layout: bool = (false, parse_bool, [TRACKED], "randomize the layout of types (default: no)"), - layout_seed: Option = (None, parse_opt_number, [TRACKED], - "seed layout randomization"), relax_elf_relocations: Option = (None, parse_opt_bool, [TRACKED], "whether ELF relocations can be relaxed"), relro_level: Option = (None, parse_relro_level, [TRACKED], "choose which RELRO level to use"), remap_cwd_prefix: Option = (None, parse_opt_pathbuf, [TRACKED], "remap paths under the current working directory to this path prefix"), - simulate_remapped_rust_src_base: Option = (None, parse_opt_pathbuf, [TRACKED], - "simulate the effect of remap-debuginfo = true at bootstrapping by remapping path \ - to rust's source base directory. only meant for testing purposes"), report_delayed_bugs: bool = (false, parse_bool, [TRACKED], "immediately print bugs registered with `delay_span_bug` (default: no)"), sanitizer: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED], @@ -1516,27 +1510,41 @@ options! { self_profile: SwitchWithOptPath = (SwitchWithOptPath::Disabled, parse_switch_with_opt_path, [UNTRACKED], "run the self profiler and output the raw event data"), - /// keep this in sync with the event filter names in librustc_data_structures/profiling.rs - self_profile_events: Option> = (None, parse_opt_comma_list, [UNTRACKED], - "specify the events recorded by the self profiler; - for example: `-Z self-profile-events=default,query-keys` - all options: none, all, default, generic-activity, query-provider, query-cache-hit - query-blocked, incr-cache-load, incr-result-hashing, query-keys, function-args, args, llvm, artifact-sizes"), self_profile_counter: String = ("wall-time".to_string(), parse_string, [UNTRACKED], "counter used by the self profiler (default: `wall-time`), one of: `wall-time` (monotonic clock, i.e. `std::time::Instant`) `instructions:u` (retired instructions, userspace-only) `instructions-minus-irqs:u` (subtracting hardware interrupt counts for extra accuracy)" ), + /// keep this in sync with the event filter names in librustc_data_structures/profiling.rs + self_profile_events: Option> = (None, parse_opt_comma_list, [UNTRACKED], + "specify the events recorded by the self profiler; + for example: `-Z self-profile-events=default,query-keys` + all options: none, all, default, generic-activity, query-provider, query-cache-hit + query-blocked, incr-cache-load, incr-result-hashing, query-keys, function-args, args, llvm, artifact-sizes"), share_generics: Option = (None, parse_opt_bool, [TRACKED], "make the current crate share its generic instantiations"), show_span: Option = (None, parse_opt_string, [TRACKED], "show spans for compiler debugging (expr|pat|ty)"), + simulate_remapped_rust_src_base: Option = (None, parse_opt_pathbuf, [TRACKED], + "simulate the effect of remap-debuginfo = true at bootstrapping by remapping path \ + to rust's source base directory. only meant for testing purposes"), span_debug: bool = (false, parse_bool, [UNTRACKED], "forward proc_macro::Span's `Debug` impl to `Span`"), /// o/w tests have closure@path span_free_formats: bool = (false, parse_bool, [UNTRACKED], "exclude spans when debug-printing compiler state (default: no)"), + split_dwarf_inlining: bool = (true, parse_bool, [TRACKED], + "provide minimal debug info in the object/executable to facilitate online \ + symbolication/stack traces in the absence of .dwo/.dwp files when using Split DWARF"), + split_dwarf_kind: SplitDwarfKind = (SplitDwarfKind::Split, parse_split_dwarf_kind, [TRACKED], + "split dwarf variant (only if -Csplit-debuginfo is enabled and on relevant platform) + (default: `split`) + + `split`: sections which do not require relocation are written into a DWARF object (`.dwo`) + file which is ignored by the linker + `single`: sections which do not require relocation are written into object file but ignored + by the linker"), src_hash_algorithm: Option = (None, parse_src_file_hash, [TRACKED], "hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`)"), #[rustc_lint_opt_deny_field_access("use `Session::stack_protector` instead of this field")] @@ -1546,17 +1554,6 @@ options! { "control if mem::uninitialized and mem::zeroed panic on more UB"), strip: Strip = (Strip::None, parse_strip, [UNTRACKED], "tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`)"), - split_dwarf_kind: SplitDwarfKind = (SplitDwarfKind::Split, parse_split_dwarf_kind, [TRACKED], - "split dwarf variant (only if -Csplit-debuginfo is enabled and on relevant platform) - (default: `split`) - - `split`: sections which do not require relocation are written into a DWARF object (`.dwo`) - file which is ignored by the linker - `single`: sections which do not require relocation are written into object file but ignored - by the linker"), - split_dwarf_inlining: bool = (true, parse_bool, [TRACKED], - "provide minimal debug info in the object/executable to facilitate online \ - symbolication/stack traces in the absence of .dwo/.dwp files when using Split DWARF"), symbol_mangling_version: Option = (None, parse_symbol_mangling_version, [TRACKED], "which mangling version to use for symbol names ('legacy' (default) or 'v0')"), @@ -1565,17 +1562,6 @@ options! { "show extended diagnostic help (default: no)"), temps_dir: Option = (None, parse_opt_string, [UNTRACKED], "the directory the intermediate files are written to"), - // Diagnostics are considered side-effects of a query (see `QuerySideEffects`) and are saved - // alongside query results and changes to translation options can affect diagnostics - so - // translation options should be tracked. - translate_lang: Option = (None, parse_opt_langid, [TRACKED], - "language identifier for diagnostic output"), - translate_additional_ftl: Option = (None, parse_opt_pathbuf, [TRACKED], - "additional fluent translation to preferentially use (for testing translation)"), - translate_directionality_markers: bool = (false, parse_bool, [TRACKED], - "emit directionality isolation markers in translated diagnostics"), - tune_cpu: Option = (None, parse_opt_string, [TRACKED], - "select processor to schedule for (`rustc --print target-cpus` for details)"), #[rustc_lint_opt_deny_field_access("use `Session::lto` instead of this field")] thinlto: Option = (None, parse_opt_bool, [TRACKED], "enable ThinLTO when possible"), @@ -1599,6 +1585,15 @@ options! { "choose the TLS model to use (`rustc --print tls-models` for details)"), trace_macros: bool = (false, parse_bool, [UNTRACKED], "for every macro invocation, print its name and arguments (default: no)"), + // Diagnostics are considered side-effects of a query (see `QuerySideEffects`) and are saved + // alongside query results and changes to translation options can affect diagnostics - so + // translation options should be tracked. + translate_additional_ftl: Option = (None, parse_opt_pathbuf, [TRACKED], + "additional fluent translation to preferentially use (for testing translation)"), + translate_directionality_markers: bool = (false, parse_bool, [TRACKED], + "emit directionality isolation markers in translated diagnostics"), + translate_lang: Option = (None, parse_opt_langid, [TRACKED], + "language identifier for diagnostic output"), translate_remapped_path_to_local_path: bool = (true, parse_bool, [TRACKED], "translate remapped paths into local paths when possible (default: yes)"), trap_unreachable: Option = (None, parse_opt_bool, [TRACKED], @@ -1607,6 +1602,8 @@ options! { "treat error number `val` that occurs as bug"), trim_diagnostic_paths: bool = (true, parse_bool, [UNTRACKED], "in diagnostics, use heuristics to shorten paths referring to items"), + tune_cpu: Option = (None, parse_opt_string, [TRACKED], + "select processor to schedule for (`rustc --print target-cpus` for details)"), ui_testing: bool = (false, parse_bool, [UNTRACKED], "emit compiler diagnostics in a form suitable for UI testing (default: no)"), uninit_const_chunk_threshold: usize = (16, parse_number, [TRACKED], @@ -1647,9 +1644,8 @@ options! { Requires `-Clto[=[fat,yes]]`"), wasi_exec_model: Option = (None, parse_wasi_exec_model, [TRACKED], "whether to build a wasi command or reactor"), + // tidy-alphabetical-end - // This list is in alphabetical order. - // // If you add a new option, please update: // - compiler/rustc_interface/src/tests.rs } diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs index d2fb8c32ffd2..9fe7da3f29ec 100644 --- a/compiler/rustc_target/src/abi/call/mod.rs +++ b/compiler/rustc_target/src/abi/call/mod.rs @@ -740,7 +740,8 @@ impl<'a, Ty> FnAbi<'a, Ty> { mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; - // These are in alphabetical order, which is easy to maintain. + // tidy-alphabetical-start static_assert_size!(ArgAbi<'_, usize>, 56); static_assert_size!(FnAbi<'_, usize>, 80); + // tidy-alphabetical-end } diff --git a/src/test/rustdoc-ui/z-help.stdout b/src/test/rustdoc-ui/z-help.stdout index 65536cb3aa13..dbf3a8f00ee6 100644 --- a/src/test/rustdoc-ui/z-help.stdout +++ b/src/test/rustdoc-ui/z-help.stdout @@ -1,8 +1,8 @@ -Z allow-features=val -- only allow the listed language features to be enabled in code (space separated) -Z always-encode-mir=val -- encode MIR of all functions into the crate metadata (default: no) - -Z assume-incomplete-release=val -- make cfg(version) treat the current version as incomplete (default: no) -Z asm-comments=val -- generate comments into the assembly (may change behavior) (default: no) -Z assert-incr-state=val -- assert that the incremental cache is in given state: either `loaded` or `not-loaded`. + -Z assume-incomplete-release=val -- make cfg(version) treat the current version as incomplete (default: no) -Z binary-dep-depinfo=val -- include artifacts (sysroot, crate dependencies) used during compilation in dep-info (default: no) -Z box-noalias=val -- emit noalias metadata for box (default: yes) -Z branch-protection=val -- set options for branch target identification and pointer authentication on AArch64 @@ -17,6 +17,7 @@ -Z deduplicate-diagnostics=val -- deduplicate identical diagnostics (default: yes) -Z dep-info-omit-d-target=val -- in dep-info output, omit targets for tracking dependencies of the dep-info files themselves (default: no) -Z dep-tasks=val -- print tasks that execute and the color their dep node gets (requires debug build) (default: no) + -Z diagnostic-width=val -- set the current output width for diagnostic truncation -Z dlltool=val -- import library generation tool (windows-gnu only) -Z dont-buffer-diagnostics=val -- emit diagnostics rather than buffering (breaks NLL error downgrading, sorting) (default: no) -Z drop-tracking=val -- enables drop tracking in generators (default: no) @@ -54,11 +55,11 @@ -Z incremental-info=val -- print high-level information about incremental reuse (or the lack thereof) (default: no) -Z incremental-relative-spans=val -- hash spans relative to their parent item for incr. comp. (default: no) -Z incremental-verify-ich=val -- verify incr. comp. hashes of green query instances (default: no) + -Z inline-in-all-cgus=val -- control whether `#[inline]` functions are in all CGUs -Z inline-llvm=val -- enable LLVM inlining (default: yes) -Z inline-mir=val -- enable MIR inlining (default: no) - -Z inline-mir-threshold=val -- a default MIR inlining threshold (default: 50) -Z inline-mir-hint-threshold=val -- inlining threshold for functions with inline hint (default: 100) - -Z inline-in-all-cgus=val -- control whether `#[inline]` functions are in all CGUs + -Z inline-mir-threshold=val -- a default MIR inlining threshold (default: 50) -Z input-stats=val -- gather statistics about the input (default: no) -Z instrument-coverage=val -- instrument the generated code to support LLVM source-based code coverage reports (note, the compiler build config must include `profiler = true`); implies `-C symbol-mangling-version=v0`. Optional values are: `=all` (implicit value) @@ -67,6 +68,7 @@ `=off` (default) -Z instrument-mcount=val -- insert function instrument code for mcount-based tracing (default: no) -Z keep-hygiene-data=val -- keep hygiene data after analysis (default: no) + -Z layout-seed=val -- seed layout randomization -Z link-native-libraries=val -- link native libraries in the linker invocation (default: yes) -Z link-only=val -- link the `.rlink` file generated by `-Z no-link` (default: no) -Z llvm-plugins=val -- a list LLVM plugins to enable (space separated) @@ -78,8 +80,8 @@ -Z meta-stats=val -- gather metadata statistics (default: no) -Z mir-emit-retag=val -- emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 (default: no) -Z mir-enable-passes=val -- use like `-Zmir-enable-passes=+DestProp,-InstCombine`. Forces the specified passes to be enabled, overriding all other checks. Passes that are not specified are enabled or disabled by other flags as usual. - -Z mir-pretty-relative-line-numbers=val -- use line numbers relative to the function in mir pretty printing -Z mir-opt-level=val -- MIR optimization level (0-4; default: 1 in non optimized builds and 2 in optimized builds) + -Z mir-pretty-relative-line-numbers=val -- use line numbers relative to the function in mir pretty printing -Z move-size-limit=val -- the size at which the `large_assignments` lint starts to be emitted -Z mutable-noalias=val -- emit noalias metadata for mutable references (default: yes) -Z nll-facts=val -- dump facts from NLL analysis into side files (default: no) @@ -91,12 +93,11 @@ -Z no-leak-check=val -- disable the 'leak check' for subtyping; unsound, but useful for tests -Z no-link=val -- compile without linking -Z no-parallel-llvm=val -- run LLVM in non-parallel mode (while keeping codegen-units and ThinLTO) - -Z no-unique-section-names=val -- do not use unique names for text and data sections when -Z function-sections is used -Z no-profiler-runtime=val -- prevent automatic injection of the profiler_builtins crate + -Z no-unique-section-names=val -- do not use unique names for text and data sections when -Z function-sections is used -Z normalize-docs=val -- normalize associated items in rustdoc when generating documentation -Z oom=val -- panic strategy for out-of-memory handling -Z osx-rpath-install-name=val -- pass `-install_name @rpath/...` to the macOS linker (default: no) - -Z diagnostic-width=val -- set the current output width for diagnostic truncation -Z packed-bundled-libs=val -- change rlib format to store native libraries as archives -Z panic-abort-tests=val -- support compiling tests with panic=abort (default: no) -Z panic-in-drop=val -- panic strategy for panics in drops @@ -120,15 +121,13 @@ -Z profile=val -- insert profiling code (default: no) -Z profile-closures=val -- profile size of closures -Z profile-emit=val -- file path to emit profiling data at runtime when using 'profile' (default based on relative source path) - -Z profiler-runtime=val -- name of the profiler runtime crate to automatically inject (default: `profiler_builtins`) -Z profile-sample-use=val -- use the given `.prof` file for sampled profile-guided optimization (also known as AutoFDO) + -Z profiler-runtime=val -- name of the profiler runtime crate to automatically inject (default: `profiler_builtins`) -Z query-dep-graph=val -- enable queries of the dependency graph for regression testing (default: no) -Z randomize-layout=val -- randomize the layout of types (default: no) - -Z layout-seed=val -- seed layout randomization -Z relax-elf-relocations=val -- whether ELF relocations can be relaxed -Z relro-level=val -- choose which RELRO level to use -Z remap-cwd-prefix=val -- remap paths under the current working directory to this path prefix - -Z simulate-remapped-rust-src-base=val -- simulate the effect of remap-debuginfo = true at bootstrapping by remapping path to rust's source base directory. only meant for testing purposes -Z report-delayed-bugs=val -- immediately print bugs registered with `delay_span_bug` (default: no) -Z sanitizer=val -- use a sanitizer -Z sanitizer-memory-track-origins=val -- enable origins tracking in MemorySanitizer @@ -136,22 +135,20 @@ -Z saturating-float-casts=val -- make float->int casts UB-free: numbers outside the integer type's range are clipped to the max/min integer respectively, and NaN is mapped to 0 (default: yes) -Z save-analysis=val -- write syntax and type analysis (in JSON format) information, in addition to normal output (default: no) -Z self-profile=val -- run the self profiler and output the raw event data - -Z self-profile-events=val -- specify the events recorded by the self profiler; - for example: `-Z self-profile-events=default,query-keys` - all options: none, all, default, generic-activity, query-provider, query-cache-hit - query-blocked, incr-cache-load, incr-result-hashing, query-keys, function-args, args, llvm, artifact-sizes -Z self-profile-counter=val -- counter used by the self profiler (default: `wall-time`), one of: `wall-time` (monotonic clock, i.e. `std::time::Instant`) `instructions:u` (retired instructions, userspace-only) `instructions-minus-irqs:u` (subtracting hardware interrupt counts for extra accuracy) + -Z self-profile-events=val -- specify the events recorded by the self profiler; + for example: `-Z self-profile-events=default,query-keys` + all options: none, all, default, generic-activity, query-provider, query-cache-hit + query-blocked, incr-cache-load, incr-result-hashing, query-keys, function-args, args, llvm, artifact-sizes -Z share-generics=val -- make the current crate share its generic instantiations -Z show-span=val -- show spans for compiler debugging (expr|pat|ty) + -Z simulate-remapped-rust-src-base=val -- simulate the effect of remap-debuginfo = true at bootstrapping by remapping path to rust's source base directory. only meant for testing purposes -Z span-debug=val -- forward proc_macro::Span's `Debug` impl to `Span` -Z span-free-formats=val -- exclude spans when debug-printing compiler state (default: no) - -Z src-hash-algorithm=val -- hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`) - -Z stack-protector=val -- control stack smash protection strategy (`rustc --print stack-protector-strategies` for details) - -Z strict-init-checks=val -- control if mem::uninitialized and mem::zeroed panic on more UB - -Z strip=val -- tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`) + -Z split-dwarf-inlining=val -- provide minimal debug info in the object/executable to facilitate online symbolication/stack traces in the absence of .dwo/.dwp files when using Split DWARF -Z split-dwarf-kind=val -- split dwarf variant (only if -Csplit-debuginfo is enabled and on relevant platform) (default: `split`) @@ -159,14 +156,13 @@ file which is ignored by the linker `single`: sections which do not require relocation are written into object file but ignored by the linker - -Z split-dwarf-inlining=val -- provide minimal debug info in the object/executable to facilitate online symbolication/stack traces in the absence of .dwo/.dwp files when using Split DWARF + -Z src-hash-algorithm=val -- hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`) + -Z stack-protector=val -- control stack smash protection strategy (`rustc --print stack-protector-strategies` for details) + -Z strict-init-checks=val -- control if mem::uninitialized and mem::zeroed panic on more UB + -Z strip=val -- tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`) -Z symbol-mangling-version=val -- which mangling version to use for symbol names ('legacy' (default) or 'v0') -Z teach=val -- show extended diagnostic help (default: no) -Z temps-dir=val -- the directory the intermediate files are written to - -Z translate-lang=val -- language identifier for diagnostic output - -Z translate-additional-ftl=val -- additional fluent translation to preferentially use (for testing translation) - -Z translate-directionality-markers=val -- emit directionality isolation markers in translated diagnostics - -Z tune-cpu=val -- select processor to schedule for (`rustc --print target-cpus` for details) -Z thinlto=val -- enable ThinLTO when possible -Z thir-unsafeck=val -- use the THIR unsafety checker (default: no) -Z threads=val -- use a thread pool with N threads @@ -174,10 +170,14 @@ -Z time-passes=val -- measure time of each rustc pass (default: no) -Z tls-model=val -- choose the TLS model to use (`rustc --print tls-models` for details) -Z trace-macros=val -- for every macro invocation, print its name and arguments (default: no) + -Z translate-additional-ftl=val -- additional fluent translation to preferentially use (for testing translation) + -Z translate-directionality-markers=val -- emit directionality isolation markers in translated diagnostics + -Z translate-lang=val -- language identifier for diagnostic output -Z translate-remapped-path-to-local-path=val -- translate remapped paths into local paths when possible (default: yes) -Z trap-unreachable=val -- generate trap instructions for unreachable intrinsics (default: use target setting, usually yes) -Z treat-err-as-bug=val -- treat error number `val` that occurs as bug -Z trim-diagnostic-paths=val -- in diagnostics, use heuristics to shorten paths referring to items + -Z tune-cpu=val -- select processor to schedule for (`rustc --print target-cpus` for details) -Z ui-testing=val -- emit compiler diagnostics in a form suitable for UI testing (default: no) -Z uninit-const-chunk-threshold=val -- allow generating const initializers with mixed init/uninit chunks, and set the maximum number of chunks for which this is allowed (default: 16) -Z unleash-the-miri-inside-of-you=val -- take the brakes off const evaluation. NOTE: this is unsound (default: no) From ce3560979d029e102777f5bf3aec840f4ff9c0fe Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Fri, 7 Oct 2022 19:38:38 +0200 Subject: [PATCH 105/333] Error if tidy-alphabetical-end was not found --- src/tools/tidy/src/alphabetical.rs | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/tools/tidy/src/alphabetical.rs b/src/tools/tidy/src/alphabetical.rs index e02b2d98f421..c9f1dfb707f3 100644 --- a/src/tools/tidy/src/alphabetical.rs +++ b/src/tools/tidy/src/alphabetical.rs @@ -29,18 +29,29 @@ fn is_close_bracket(c: char) -> bool { matches!(c, ')' | ']' | '}') } +const START_COMMENT: &str = "// tidy-alphabetical-start"; +const END_COMMENT: &str = "// tidy-alphabetical-end"; + fn check_section<'a>( file: impl Display, lines: impl Iterator, bad: &mut bool, ) { - let content_lines = lines.take_while(|(_, line)| !line.contains("// tidy-alphabetical-end")); + let content_lines = lines.take_while(|(_, line)| !line.contains(END_COMMENT)); let mut prev_line = String::new(); let mut first_indent = None; let mut in_split_line = None; for (line_idx, line) in content_lines { + if line.contains(START_COMMENT) { + tidy_error!( + bad, + "{file}:{} found `// tidy-alphabetical-start` expecting `// tidy-alphabetical-end`", + line_idx + ) + } + let indent = first_indent.unwrap_or_else(|| { let indent = indentation(line); first_indent = Some(indent); @@ -82,16 +93,20 @@ fn check_section<'a>( } } -const START_COMMENT: &str = "// tidy-alphabetical-start"; - pub fn check(path: &Path, bad: &mut bool) { walk(path, &mut filter_dirs, &mut |entry, contents| { let file = &entry.path().display(); - let mut lines = contents.lines().enumerate(); + let mut lines = contents.lines().enumerate().peekable(); while let Some((_, line)) = lines.next() { if line.contains(START_COMMENT) { check_section(file, &mut lines, bad); + if lines.peek().is_none() { + tidy_error!( + bad, + "{file}: reached end of file expecting `// tidy-alphabetical-end`" + ) + } } } }); From 690b9faa5644e336d57a608110578d91c7ef9209 Mon Sep 17 00:00:00 2001 From: Florian Bartels Date: Wed, 12 Oct 2022 14:30:06 +0200 Subject: [PATCH 106/333] Show command line arguments --- src/tools/remote-test-server/src/main.rs | 25 +++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/tools/remote-test-server/src/main.rs b/src/tools/remote-test-server/src/main.rs index bed9d39161df..ec992da68121 100644 --- a/src/tools/remote-test-server/src/main.rs +++ b/src/tools/remote-test-server/src/main.rs @@ -74,7 +74,11 @@ impl Config { "--bind" => next_is_bind = true, "--sequential" => config.sequential = true, "--verbose" | "-v" => config.verbose = true, - arg => panic!("unknown argument: {}", arg), + "--help" | "-h" => { + show_help(); + std::process::exit(0); + } + arg => panic!("unknown argument: {}, use `--help` for known arguments", arg), } } if next_is_bind { @@ -85,6 +89,22 @@ impl Config { } } +fn show_help() { + eprintln!( + r#"Usage: + +{} [OPTIONS] + +OPTIONS: + --bind : Specify IP address and port to listen for requests, e.g. "0.0.0.0:12345" + --sequential Run only one test at a time + -v, --verbose Show status messages + -h, --help Show this help screen +"#, + std::env::args().next().unwrap() + ); +} + fn print_verbose(s: &str, conf: Config) { if conf.verbose { println!("{}", s); @@ -92,9 +112,8 @@ fn print_verbose(s: &str, conf: Config) { } fn main() { - println!("starting test server"); - let config = Config::parse_args(); + println!("starting test server"); let listener = t!(TcpListener::bind(config.bind)); let (work, tmp): (PathBuf, PathBuf) = if cfg!(target_os = "android") { From 950ca0c30201471626e1a47a9b7dd7338d9ae4b7 Mon Sep 17 00:00:00 2001 From: Rageking8 Date: Wed, 12 Oct 2022 22:32:20 +0800 Subject: [PATCH 107/333] add test for issue 102964 --- src/test/ui/issues/issue-102964.rs | 10 ++++++++++ src/test/ui/issues/issue-102964.stderr | 19 +++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 src/test/ui/issues/issue-102964.rs create mode 100644 src/test/ui/issues/issue-102964.stderr diff --git a/src/test/ui/issues/issue-102964.rs b/src/test/ui/issues/issue-102964.rs new file mode 100644 index 000000000000..43ff23600766 --- /dev/null +++ b/src/test/ui/issues/issue-102964.rs @@ -0,0 +1,10 @@ +use std::rc::Rc; +type Foo<'a, T> = &'a dyn Fn(&T); +type RcFoo<'a, T> = Rc>; + +fn bar_function(function: Foo) -> RcFoo { + //~^ ERROR mismatched types + let rc = Rc::new(function); +} + +fn main() {} diff --git a/src/test/ui/issues/issue-102964.stderr b/src/test/ui/issues/issue-102964.stderr new file mode 100644 index 000000000000..4504039097b5 --- /dev/null +++ b/src/test/ui/issues/issue-102964.stderr @@ -0,0 +1,19 @@ +error[E0308]: mismatched types + --> $DIR/issue-102964.rs:5:41 + | +LL | fn bar_function(function: Foo) -> RcFoo { + | ------------ ^^^^^^^^ expected struct `Rc`, found `()` + | | + | implicitly returns `()` as its body has no tail or `return` expression + | + = note: expected struct `Rc<&dyn for<'a> Fn(&'a T)>` + found unit type `()` +help: consider returning the local binding `rc` + | +LL ~ let rc = Rc::new(function); +LL + rc + | + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. From 73e91dda5032932a8241dcdc938788ed391fe549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Wed, 12 Oct 2022 17:01:27 +0300 Subject: [PATCH 108/333] Set opt-level = 1 on dev profile --- .github/workflows/ci.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 1563ee0b1438..1c061bb74866 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -45,6 +45,10 @@ jobs: - name: Cache Dependencies uses: Swatinem/rust-cache@ce325b60658c1b38465c06cc965b79baf32c1e72 + - name: Bump opt-level + if: matrix.os == 'ubuntu-latest' + run: sed -i '/\[profile.dev]/a opt-level=1' Cargo.toml + - name: Compile run: cargo test --no-run --locked From 82a8774a08d946cf9d821f4963fbcef9ac194644 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Wed, 12 Oct 2022 17:01:54 +0300 Subject: [PATCH 109/333] Avoid format! in favor of to_string --- crates/syntax/src/ast/make.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index c9a21e12c085..4057a75e7c1e 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -89,7 +89,7 @@ pub mod ext { } pub fn ty_name(name: ast::Name) -> ast::Type { - ty_path(ident_path(&format!("{name}"))) + ty_path(ident_path(&name.to_string())) } pub fn ty_bool() -> ast::Type { ty_path(ident_path("bool")) From 0eeeea9414d0ee5b22f6a33601282b46eca67bd6 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 5 Oct 2022 03:52:38 +0000 Subject: [PATCH 110/333] Add broken test for AFIT with RPITIT --- .../in-trait/default-body-with-rpit.rs | 21 +++++++++++++++++++ .../in-trait/default-body-with-rpit.stderr | 12 +++++++++++ 2 files changed, 33 insertions(+) create mode 100644 src/test/ui/impl-trait/in-trait/default-body-with-rpit.rs create mode 100644 src/test/ui/impl-trait/in-trait/default-body-with-rpit.stderr diff --git a/src/test/ui/impl-trait/in-trait/default-body-with-rpit.rs b/src/test/ui/impl-trait/in-trait/default-body-with-rpit.rs new file mode 100644 index 000000000000..f0d407cd527d --- /dev/null +++ b/src/test/ui/impl-trait/in-trait/default-body-with-rpit.rs @@ -0,0 +1,21 @@ +// known-bug: #102688 +// edition:2021 + +#![feature(async_fn_in_trait, return_position_impl_trait_in_trait)] +#![allow(incomplete_features)] + +use std::fmt::Debug; + +trait Foo { + async fn baz(&self) -> impl Debug { + "" + } +} + +struct Bar; + +impl Foo for Bar {} + +fn main() { + let _ = Bar.baz(); +} diff --git a/src/test/ui/impl-trait/in-trait/default-body-with-rpit.stderr b/src/test/ui/impl-trait/in-trait/default-body-with-rpit.stderr new file mode 100644 index 000000000000..4529d301f9e9 --- /dev/null +++ b/src/test/ui/impl-trait/in-trait/default-body-with-rpit.stderr @@ -0,0 +1,12 @@ +error[E0720]: cannot resolve opaque type + --> $DIR/default-body-with-rpit.rs:10:28 + | +LL | async fn baz(&self) -> impl Debug { + | ^^^^^^^^^^ cannot resolve opaque type + | + = note: these returned values have a concrete "never" type + = help: this error will resolve once the item's body returns a concrete type + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0720`. From d1982bd0af0ac3e9bc0b1e3bf596a1a5c84e60af Mon Sep 17 00:00:00 2001 From: Rageking8 Date: Thu, 13 Oct 2022 00:12:19 +0800 Subject: [PATCH 111/333] fix small word dupe typos --- compiler/rustc_error_codes/src/error_codes/E0591.md | 4 ++-- compiler/rustc_infer/src/infer/mod.rs | 2 +- library/std/src/io/error/tests.rs | 2 +- library/std/src/sys/unix/kernel_copy.rs | 2 +- src/test/ui/async-await/issue-64130-1-sync.rs | 2 +- src/test/ui/async-await/issue-64130-2-send.rs | 2 +- src/test/ui/async-await/issue-64130-3-other.rs | 2 +- src/test/ui/const-generics/occurs-check/unused-substs-2.rs | 2 +- src/test/ui/const-generics/occurs-check/unused-substs-3.rs | 2 +- src/test/ui/deprecation/deprecation-lint.rs | 2 +- src/test/ui/explain.stdout | 4 ++-- src/test/ui/issues/issue-29746.rs | 2 +- src/test/ui/issues/issue-75907.rs | 2 +- src/test/ui/issues/issue-75907_b.rs | 2 +- src/test/ui/lint/lint-stability-deprecated.rs | 2 +- src/test/ui/proc-macro/meta-macro-hygiene.rs | 4 ++-- src/test/ui/proc-macro/meta-macro-hygiene.stdout | 4 ++-- 17 files changed, 21 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_error_codes/src/error_codes/E0591.md b/compiler/rustc_error_codes/src/error_codes/E0591.md index f49805d9b4e1..6ed8370e8c1c 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0591.md +++ b/compiler/rustc_error_codes/src/error_codes/E0591.md @@ -53,8 +53,8 @@ unsafe { ``` Here, transmute is being used to convert the types of the fn arguments. -This pattern is incorrect because, because the type of `foo` is a function -**item** (`typeof(foo)`), which is zero-sized, and the target type (`fn()`) +This pattern is incorrect because the type of `foo` is a function **item** +(`typeof(foo)`), which is zero-sized, and the target type (`fn()`) is a function pointer, which is not zero-sized. This pattern should be rewritten. There are a few possible ways to do this: diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 441dc3c7e888..5a5e9db81a24 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1283,7 +1283,7 @@ impl<'tcx> InferCtxt<'tcx> { assert!(old_value.is_none()); } - /// Process the region constraints and return any any errors that + /// Process the region constraints and return any errors that /// result. After this, no more unification operations should be /// done -- or the compiler will panic -- but it is legal to use /// `resolve_vars_if_possible` as well as `fully_resolve`. diff --git a/library/std/src/io/error/tests.rs b/library/std/src/io/error/tests.rs index c897a5e8701c..16c634e9afd5 100644 --- a/library/std/src/io/error/tests.rs +++ b/library/std/src/io/error/tests.rs @@ -86,7 +86,7 @@ fn test_errorkind_packing() { assert_eq!(Error::from(ErrorKind::NotFound).kind(), ErrorKind::NotFound); assert_eq!(Error::from(ErrorKind::PermissionDenied).kind(), ErrorKind::PermissionDenied); assert_eq!(Error::from(ErrorKind::Uncategorized).kind(), ErrorKind::Uncategorized); - // Check that the innards look like like what we want. + // Check that the innards look like what we want. assert_matches!( Error::from(ErrorKind::OutOfMemory).repr.data(), ErrorData::Simple(ErrorKind::OutOfMemory), diff --git a/library/std/src/sys/unix/kernel_copy.rs b/library/std/src/sys/unix/kernel_copy.rs index 8f7abb55e237..94546ca09d00 100644 --- a/library/std/src/sys/unix/kernel_copy.rs +++ b/library/std/src/sys/unix/kernel_copy.rs @@ -20,7 +20,7 @@ //! Since those syscalls have requirements that cannot be fully checked in advance and //! gathering additional information about file descriptors would require additional syscalls //! anyway it simply attempts to use them one after another (guided by inaccurate hints) to -//! figure out which one works and and falls back to the generic read-write copy loop if none of them +//! figure out which one works and falls back to the generic read-write copy loop if none of them //! does. //! Once a working syscall is found for a pair of file descriptors it will be called in a loop //! until the copy operation is completed. diff --git a/src/test/ui/async-await/issue-64130-1-sync.rs b/src/test/ui/async-await/issue-64130-1-sync.rs index af83f14bbda5..1714cec5221d 100644 --- a/src/test/ui/async-await/issue-64130-1-sync.rs +++ b/src/test/ui/async-await/issue-64130-1-sync.rs @@ -1,7 +1,7 @@ #![feature(negative_impls)] // edition:2018 -// This tests the the specialized async-await-specific error when futures don't implement an +// This tests the specialized async-await-specific error when futures don't implement an // auto trait (which is specifically Sync) due to some type that was captured. struct Foo; diff --git a/src/test/ui/async-await/issue-64130-2-send.rs b/src/test/ui/async-await/issue-64130-2-send.rs index 2362831d8b8f..7a6e5952cb95 100644 --- a/src/test/ui/async-await/issue-64130-2-send.rs +++ b/src/test/ui/async-await/issue-64130-2-send.rs @@ -1,7 +1,7 @@ #![feature(negative_impls)] // edition:2018 -// This tests the the specialized async-await-specific error when futures don't implement an +// This tests the specialized async-await-specific error when futures don't implement an // auto trait (which is specifically Send) due to some type that was captured. struct Foo; diff --git a/src/test/ui/async-await/issue-64130-3-other.rs b/src/test/ui/async-await/issue-64130-3-other.rs index 52801c35ba3d..630fb2c41cde 100644 --- a/src/test/ui/async-await/issue-64130-3-other.rs +++ b/src/test/ui/async-await/issue-64130-3-other.rs @@ -2,7 +2,7 @@ #![feature(negative_impls)] // edition:2018 -// This tests the the unspecialized async-await-specific error when futures don't implement an +// This tests the unspecialized async-await-specific error when futures don't implement an // auto trait (which is not Send or Sync) due to some type that was captured. auto trait Qux {} diff --git a/src/test/ui/const-generics/occurs-check/unused-substs-2.rs b/src/test/ui/const-generics/occurs-check/unused-substs-2.rs index 9a73f1a53e52..9b1212694f5b 100644 --- a/src/test/ui/const-generics/occurs-check/unused-substs-2.rs +++ b/src/test/ui/const-generics/occurs-check/unused-substs-2.rs @@ -1,7 +1,7 @@ #![feature(generic_const_exprs)] #![allow(incomplete_features)] -// The goal is is to get an unevaluated const `ct` with a `Ty::Infer(TyVar(_#1t)` subst. +// The goal is to get an unevaluated const `ct` with a `Ty::Infer(TyVar(_#1t)` subst. // // If we are then able to infer `ty::Infer(TyVar(_#1t) := Ty` we introduced an // artificial inference cycle. diff --git a/src/test/ui/const-generics/occurs-check/unused-substs-3.rs b/src/test/ui/const-generics/occurs-check/unused-substs-3.rs index 0d38bd393519..d5aeab47e62b 100644 --- a/src/test/ui/const-generics/occurs-check/unused-substs-3.rs +++ b/src/test/ui/const-generics/occurs-check/unused-substs-3.rs @@ -1,7 +1,7 @@ #![feature(generic_const_exprs)] #![allow(incomplete_features)] -// The goal is is to get an unevaluated const `ct` with a `Ty::Infer(TyVar(_#1t)` subst. +// The goal is to get an unevaluated const `ct` with a `Ty::Infer(TyVar(_#1t)` subst. // // If we are then able to infer `ty::Infer(TyVar(_#1t) := Ty` we introduced an // artificial inference cycle. diff --git a/src/test/ui/deprecation/deprecation-lint.rs b/src/test/ui/deprecation/deprecation-lint.rs index 65cc4e2ef1e4..0417e952eb71 100644 --- a/src/test/ui/deprecation/deprecation-lint.rs +++ b/src/test/ui/deprecation/deprecation-lint.rs @@ -51,7 +51,7 @@ mod cross_crate { let _ = nested::DeprecatedTupleStruct (1); //~ ERROR use of deprecated tuple struct `deprecation_lint::nested::DeprecatedTupleStruct`: text - // At the moment, the lint checker only checks stability in + // At the moment, the lint checker only checks stability // in the arguments of macros. // Eventually, we will want to lint the contents of the // macro in the module *defining* it. Also, stability levels diff --git a/src/test/ui/explain.stdout b/src/test/ui/explain.stdout index 62f1a7f98ea1..ef1d866c3ff3 100644 --- a/src/test/ui/explain.stdout +++ b/src/test/ui/explain.stdout @@ -47,8 +47,8 @@ unsafe { ``` Here, transmute is being used to convert the types of the fn arguments. -This pattern is incorrect because, because the type of `foo` is a function -**item** (`typeof(foo)`), which is zero-sized, and the target type (`fn()`) +This pattern is incorrect because the type of `foo` is a function **item** +(`typeof(foo)`), which is zero-sized, and the target type (`fn()`) is a function pointer, which is not zero-sized. This pattern should be rewritten. There are a few possible ways to do this: diff --git a/src/test/ui/issues/issue-29746.rs b/src/test/ui/issues/issue-29746.rs index 428cc637f556..3470a7e09ad8 100644 --- a/src/test/ui/issues/issue-29746.rs +++ b/src/test/ui/issues/issue-29746.rs @@ -7,7 +7,7 @@ macro_rules! zip { zip!([$($rest),*], $a.zip($b), (x,y), [x,y]) }; - // Intermediate steps to build the zipped expression, the match pattern, and + // Intermediate steps to build the zipped expression, the match pattern // and the output tuple of the closure, using macro hygiene to repeatedly // introduce new variables named 'x'. ([$a:expr, $($rest:expr),*], $zip:expr, $pat:pat, [$($flat:expr),*]) => { diff --git a/src/test/ui/issues/issue-75907.rs b/src/test/ui/issues/issue-75907.rs index 1534b6d07deb..6da99cf6435c 100644 --- a/src/test/ui/issues/issue-75907.rs +++ b/src/test/ui/issues/issue-75907.rs @@ -1,4 +1,4 @@ -// Test for for diagnostic improvement issue #75907 +// Test for diagnostic improvement issue #75907 mod foo { pub(crate) struct Foo(u8); diff --git a/src/test/ui/issues/issue-75907_b.rs b/src/test/ui/issues/issue-75907_b.rs index e30747782339..fdfc5907c167 100644 --- a/src/test/ui/issues/issue-75907_b.rs +++ b/src/test/ui/issues/issue-75907_b.rs @@ -1,4 +1,4 @@ -// Test for for diagnostic improvement issue #75907, extern crate +// Test for diagnostic improvement issue #75907, extern crate // aux-build:issue-75907.rs extern crate issue_75907 as a; diff --git a/src/test/ui/lint/lint-stability-deprecated.rs b/src/test/ui/lint/lint-stability-deprecated.rs index bdc66e83083f..74c35083e60b 100644 --- a/src/test/ui/lint/lint-stability-deprecated.rs +++ b/src/test/ui/lint/lint-stability-deprecated.rs @@ -130,7 +130,7 @@ mod cross_crate { let _ = UnstableTupleStruct (1); let _ = StableTupleStruct (1); - // At the moment, the lint checker only checks stability in + // At the moment, the lint checker only checks stability // in the arguments of macros. // Eventually, we will want to lint the contents of the // macro in the module *defining* it. Also, stability levels diff --git a/src/test/ui/proc-macro/meta-macro-hygiene.rs b/src/test/ui/proc-macro/meta-macro-hygiene.rs index 62968ea54e0a..70b8d8da19b3 100644 --- a/src/test/ui/proc-macro/meta-macro-hygiene.rs +++ b/src/test/ui/proc-macro/meta-macro-hygiene.rs @@ -19,8 +19,8 @@ macro_rules! produce_it { // `print_def_site!` will respan the `$crate` identifier // with `Span::def_site()`. This should cause it to resolve // relative to `meta_macro`, *not* `make_macro` (despite - // the fact that that `print_def_site` is produced by - // a `macro_rules!` macro in `make_macro`). + // the fact that `print_def_site` is produced by a + // `macro_rules!` macro in `make_macro`). meta_macro::print_def_site!($crate::dummy!()); } } diff --git a/src/test/ui/proc-macro/meta-macro-hygiene.stdout b/src/test/ui/proc-macro/meta-macro-hygiene.stdout index 2494af1208f1..6b7b0c819cca 100644 --- a/src/test/ui/proc-macro/meta-macro-hygiene.stdout +++ b/src/test/ui/proc-macro/meta-macro-hygiene.stdout @@ -35,8 +35,8 @@ macro_rules! produce_it // `print_def_site!` will respan the `$crate` identifier // with `Span::def_site()`. This should cause it to resolve // relative to `meta_macro`, *not* `make_macro` (despite - // the fact that that `print_def_site` is produced by - // a `macro_rules!` macro in `make_macro`). + // the fact that `print_def_site` is produced by a + // `macro_rules!` macro in `make_macro`). } } From 983ae1b1c9b54bb82c5c7e90e98725d283d0889b Mon Sep 17 00:00:00 2001 From: HKalbasi <45197576+HKalbasi@users.noreply.github.com> Date: Wed, 12 Oct 2022 20:40:49 +0330 Subject: [PATCH 112/333] Cast runnableEnv items to string --- editors/code/src/config.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index a9c0f079b3da..d66aa52fadc6 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -133,7 +133,21 @@ export class Config { } get runnableEnv() { - return this.get("runnableEnv"); + const item = this.get("runnableEnv"); + if (!item) return item; + const fixRecord = (r: Record) => { + for (const key in r) { + if (typeof r[key] !== 'string') { + r[key] = String(r[key]); + } + } + }; + if (item instanceof Array) { + item.forEach((x) => fixRecord(x.env)); + } else { + fixRecord(item); + } + return item; } get restartServerOnConfigChange() { From f8da2295ded3b402a16685848199e50b2fabacbb Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Wed, 12 Oct 2022 11:24:23 -0700 Subject: [PATCH 113/333] rustdoc: merge separate `.item-info` CSS Rough timeline: * The longer `.content .item-info` selector originated in 110e7270ab7b0700ce714b8b1c7e509195dea2c4. No reason seems to be given in the PR why it needed the `.content` part, but it was probably added because of . That selector with the margin-bottom was removed when CSS containment was added in 8846c0853d8687fda0e5f23f6687b03b243980ee. * `.stability` was renamed `.item-info` in caf6c5790a858893c1d32ed2054c9577d12e7493. * The selector without the `.content` was added in d48a39a5e24ab08f727d1c919dc2af98c333ad14. --- src/librustdoc/html/static/css/rustdoc.css | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index f5b0d15d733a..577cc3ed694d 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -694,16 +694,13 @@ pre, .rustdoc.source .example-wrap { .item-info { display: block; -} - -.content .item-info code { - font-size: 0.875rem; -} - -.content .item-info { margin-left: 24px; } +.item-info code { + font-size: 0.875rem; +} + #main-content > .item-info { margin-top: 0; margin-left: 0; @@ -1948,7 +1945,7 @@ in storage.js plus the media query with (min-width: 701px) } /* Align summary-nested and unnested item-info gizmos. */ - .content .impl-items > .item-info { + .impl-items > .item-info { margin-left: 34px; } } From 5645cd5b09fb6db849376872fd09fe31bf029b57 Mon Sep 17 00:00:00 2001 From: Jhonny Bill Mena Date: Tue, 4 Oct 2022 20:56:05 -0400 Subject: [PATCH 114/333] ADD - IntoDiagnostic conformance for TargetDataLayoutErrors in rustc_errors This way we comply with the Coherence rule given that IntoDiagnostic trait is defined in rustc_errors, and almost all other crates depend on it. --- .../locales/en-US/errors.ftl | 13 +++++ .../locales/en-US/session.ftl | 14 ----- compiler/rustc_error_messages/src/lib.rs | 1 + compiler/rustc_errors/src/diagnostic_impls.rs | 53 ++++++++++++++++++ compiler/rustc_errors/src/lib.rs | 1 + compiler/rustc_middle/src/ty/context.rs | 3 +- compiler/rustc_session/src/config.rs | 3 +- compiler/rustc_session/src/errors.rs | 54 ------------------- 8 files changed, 70 insertions(+), 72 deletions(-) create mode 100644 compiler/rustc_error_messages/locales/en-US/errors.ftl create mode 100644 compiler/rustc_errors/src/diagnostic_impls.rs diff --git a/compiler/rustc_error_messages/locales/en-US/errors.ftl b/compiler/rustc_error_messages/locales/en-US/errors.ftl new file mode 100644 index 000000000000..429bdd2777f9 --- /dev/null +++ b/compiler/rustc_error_messages/locales/en-US/errors.ftl @@ -0,0 +1,13 @@ +errors_target_invalid_address_space = invalid address space `{$addr_space}` for `{$cause}` in "data-layout": {$err} + +errors_target_invalid_bits = invalid {$kind} `{$bit}` for `{$cause}` in "data-layout": {$err} + +errors_target_missing_alignment = missing alignment for `{$cause}` in "data-layout" + +errors_target_invalid_alignment = invalid alignment for `{$cause}` in "data-layout": {$err} + +errors_target_inconsistent_architecture = inconsistent target specification: "data-layout" claims architecture is {$dl}-endian, while "target-endian" is `{$target}` + +errors_target_inconsistent_pointer_width = inconsistent target specification: "data-layout" claims pointers are {$pointer_size}-bit, while "target-pointer-width" is `{$target}` + +errors_target_invalid_bits_size = {$err} diff --git a/compiler/rustc_error_messages/locales/en-US/session.ftl b/compiler/rustc_error_messages/locales/en-US/session.ftl index 3ea9429a23ab..47127ea8e9ce 100644 --- a/compiler/rustc_error_messages/locales/en-US/session.ftl +++ b/compiler/rustc_error_messages/locales/en-US/session.ftl @@ -39,20 +39,6 @@ session_unstable_virtual_function_elimination = `-Zvirtual-function-elimination` session_unsupported_dwarf_version = requested DWARF version {$dwarf_version} is greater than 5 -session_target_invalid_address_space = invalid address space `{$addr_space}` for `{$cause}` in "data-layout": {$err} - -session_target_invalid_bits = invalid {$kind} `{$bit}` for `{$cause}` in "data-layout": {$err} - -session_target_missing_alignment = missing alignment for `{$cause}` in "data-layout" - -session_target_invalid_alignment = invalid alignment for `{$cause}` in "data-layout": {$err} - -session_target_inconsistent_architecture = inconsistent target specification: "data-layout" claims architecture is {$dl}-endian, while "target-endian" is `{$target}` - -session_target_inconsistent_pointer_width = inconsistent target specification: "data-layout" claims pointers are {$pointer_size}-bit, while "target-pointer-width" is `{$target}` - -session_target_invalid_bits_size = {$err} - session_target_stack_protector_not_supported = `-Z stack-protector={$stack_protector}` is not supported for target {$target_triple} and will be ignored session_split_debuginfo_unstable_platform = `-Csplit-debuginfo={$debuginfo}` is unstable on this platform diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index a6024044ad82..9465051dd103 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -46,6 +46,7 @@ fluent_messages! { compiletest => "../locales/en-US/compiletest.ftl", const_eval => "../locales/en-US/const_eval.ftl", driver => "../locales/en-US/driver.ftl", + errors => "../locales/en-US/errors.ftl", expand => "../locales/en-US/expand.ftl", hir_analysis => "../locales/en-US/hir_analysis.ftl", infer => "../locales/en-US/infer.ftl", diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs new file mode 100644 index 000000000000..2a1a5d5afb04 --- /dev/null +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -0,0 +1,53 @@ +use crate::{fluent, DiagnosticBuilder, Handler, IntoDiagnostic}; +use rustc_target::abi::TargetDataLayoutErrors; + +impl IntoDiagnostic<'_, !> for TargetDataLayoutErrors<'_> { + fn into_diagnostic(self, handler: &Handler) -> DiagnosticBuilder<'_, !> { + let mut diag; + match self { + TargetDataLayoutErrors::InvalidAddressSpace { addr_space, err, cause } => { + diag = handler.struct_fatal(fluent::errors::target_invalid_address_space); + diag.set_arg("addr_space", addr_space); + diag.set_arg("cause", cause); + diag.set_arg("err", err); + diag + } + TargetDataLayoutErrors::InvalidBits { kind, bit, cause, err } => { + diag = handler.struct_fatal(fluent::errors::target_invalid_bits); + diag.set_arg("kind", kind); + diag.set_arg("bit", bit); + diag.set_arg("cause", cause); + diag.set_arg("err", err); + diag + } + TargetDataLayoutErrors::MissingAlignment { cause } => { + diag = handler.struct_fatal(fluent::errors::target_missing_alignment); + diag.set_arg("cause", cause); + diag + } + TargetDataLayoutErrors::InvalidAlignment { cause, err } => { + diag = handler.struct_fatal(fluent::errors::target_invalid_alignment); + diag.set_arg("cause", cause); + diag.set_arg("err", err); + diag + } + TargetDataLayoutErrors::InconsistentTargetArchitecture { dl, target } => { + diag = handler.struct_fatal(fluent::errors::target_inconsistent_architecture); + diag.set_arg("dl", dl); + diag.set_arg("target", target); + diag + } + TargetDataLayoutErrors::InconsistentTargetPointerWidth { pointer_size, target } => { + diag = handler.struct_fatal(fluent::errors::target_inconsistent_pointer_width); + diag.set_arg("pointer_size", pointer_size); + diag.set_arg("target", target); + diag + } + TargetDataLayoutErrors::InvalidBitsSize { err } => { + diag = handler.struct_fatal(fluent::errors::target_invalid_bits_size); + diag.set_arg("err", err); + diag + } + } + } +} diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index b16c54e0aaca..955e85c3616b 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -51,6 +51,7 @@ use termcolor::{Color, ColorSpec}; pub mod annotate_snippet_emitter_writer; mod diagnostic; mod diagnostic_builder; +mod diagnostic_impls; pub mod emitter; pub mod json; mod lock; diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 97646003e733..9ea54b69f47b 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -56,7 +56,6 @@ use rustc_query_system::ich::StableHashingContext; use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; use rustc_session::config::{CrateType, OutputFilenames}; use rustc_session::cstore::CrateStoreDyn; -use rustc_session::errors::TargetDataLayoutErrorsWrapper; use rustc_session::lint::Lint; use rustc_session::Limit; use rustc_session::Session; @@ -1249,7 +1248,7 @@ impl<'tcx> TyCtxt<'tcx> { output_filenames: OutputFilenames, ) -> GlobalCtxt<'tcx> { let data_layout = TargetDataLayout::parse(&s.target).unwrap_or_else(|err| { - s.emit_fatal(TargetDataLayoutErrorsWrapper(err)); + s.emit_fatal(err); }); let interners = CtxtInterners::new(arena); let common_types = CommonTypes::new( diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 70b470f3811d..f2ee52262ade 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -3,7 +3,6 @@ pub use crate::options::*; -use crate::errors::TargetDataLayoutErrorsWrapper; use crate::search_paths::SearchPath; use crate::utils::{CanonicalizedPath, NativeLib, NativeLibKind}; use crate::{early_error, early_warn, Session}; @@ -900,7 +899,7 @@ fn default_configuration(sess: &Session) -> CrateConfig { let max_atomic_width = sess.target.max_atomic_width(); let atomic_cas = sess.target.atomic_cas; let layout = TargetDataLayout::parse(&sess.target).unwrap_or_else(|err| { - sess.emit_fatal(TargetDataLayoutErrorsWrapper(err)); + sess.emit_fatal(err); }); let mut ret = CrateConfig::default(); diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index b5962f76b7f4..d12796f289e9 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -6,7 +6,6 @@ use rustc_errors::{ }; use rustc_macros::Diagnostic; use rustc_span::{Span, Symbol}; -use rustc_target::abi::TargetDataLayoutErrors; use rustc_target::spec::{SplitDebuginfo, StackProtector, TargetTriple}; #[derive(Diagnostic)] @@ -47,59 +46,6 @@ pub struct FeatureDiagnosticHelp { pub feature: Symbol, } -pub struct TargetDataLayoutErrorsWrapper<'a>(pub TargetDataLayoutErrors<'a>); - -impl IntoDiagnostic<'_, !> for TargetDataLayoutErrorsWrapper<'_> { - fn into_diagnostic(self, handler: &Handler) -> DiagnosticBuilder<'_, !> { - let mut diag; - match self.0 { - TargetDataLayoutErrors::InvalidAddressSpace { addr_space, err, cause } => { - diag = handler.struct_fatal(fluent::session::target_invalid_address_space); - diag.set_arg("addr_space", addr_space); - diag.set_arg("cause", cause); - diag.set_arg("err", err); - diag - } - TargetDataLayoutErrors::InvalidBits { kind, bit, cause, err } => { - diag = handler.struct_fatal(fluent::session::target_invalid_bits); - diag.set_arg("kind", kind); - diag.set_arg("bit", bit); - diag.set_arg("cause", cause); - diag.set_arg("err", err); - diag - } - TargetDataLayoutErrors::MissingAlignment { cause } => { - diag = handler.struct_fatal(fluent::session::target_missing_alignment); - diag.set_arg("cause", cause); - diag - } - TargetDataLayoutErrors::InvalidAlignment { cause, err } => { - diag = handler.struct_fatal(fluent::session::target_invalid_alignment); - diag.set_arg("cause", cause); - diag.set_arg("err", err); - diag - } - TargetDataLayoutErrors::InconsistentTargetArchitecture { dl, target } => { - diag = handler.struct_fatal(fluent::session::target_inconsistent_architecture); - diag.set_arg("dl", dl); - diag.set_arg("target", target); - diag - } - TargetDataLayoutErrors::InconsistentTargetPointerWidth { pointer_size, target } => { - diag = handler.struct_fatal(fluent::session::target_inconsistent_pointer_width); - diag.set_arg("pointer_size", pointer_size); - diag.set_arg("target", target); - diag - } - TargetDataLayoutErrors::InvalidBitsSize { err } => { - diag = handler.struct_fatal(fluent::session::target_invalid_bits_size); - diag.set_arg("err", err); - diag - } - } - } -} - #[derive(Diagnostic)] #[diag(session::not_circumvent_feature)] pub struct NotCircumventFeature; From be221573c853f5cdca8becab5acc600d6c79eac5 Mon Sep 17 00:00:00 2001 From: Jhonny Bill Mena Date: Wed, 12 Oct 2022 16:55:28 -0400 Subject: [PATCH 115/333] UPDATE - Move IntoDiagnosticArg implementations to diagnostic_impls file --- compiler/rustc_errors/src/diagnostic.rs | 151 +---------------- compiler/rustc_errors/src/diagnostic_impls.rs | 156 +++++++++++++++++- compiler/rustc_errors/src/lib.rs | 5 +- 3 files changed, 160 insertions(+), 152 deletions(-) diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 3e0840caaa69..518c59dba536 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -3,21 +3,15 @@ use crate::{ CodeSuggestion, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee, Level, MultiSpan, SubdiagnosticMessage, Substitution, SubstitutionPart, SuggestionStyle, }; -use rustc_ast as ast; -use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; use rustc_error_messages::FluentValue; -use rustc_hir as hir; use rustc_lint_defs::{Applicability, LintExpectationId}; use rustc_span::edition::LATEST_STABLE_EDITION; -use rustc_span::symbol::{Ident, MacroRulesNormalizedIdent, Symbol}; -use rustc_span::{edition::Edition, Span, DUMMY_SP}; -use rustc_target::spec::{PanicStrategy, SplitDebuginfo, StackProtector, TargetTriple}; +use rustc_span::symbol::Symbol; +use rustc_span::{Span, DUMMY_SP}; use std::borrow::Cow; use std::fmt; use std::hash::{Hash, Hasher}; -use std::num::ParseIntError; -use std::path::{Path, PathBuf}; /// Error type for `Diagnostic`'s `suggestions` field, indicating that /// `.disable_suggestions()` was called on the `Diagnostic`. @@ -49,119 +43,6 @@ pub trait IntoDiagnosticArg { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static>; } -pub struct DiagnosticArgFromDisplay<'a>(pub &'a dyn fmt::Display); - -impl IntoDiagnosticArg for DiagnosticArgFromDisplay<'_> { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - self.0.to_string().into_diagnostic_arg() - } -} - -impl<'a> From<&'a dyn fmt::Display> for DiagnosticArgFromDisplay<'a> { - fn from(t: &'a dyn fmt::Display) -> Self { - DiagnosticArgFromDisplay(t) - } -} - -impl<'a, T: fmt::Display> From<&'a T> for DiagnosticArgFromDisplay<'a> { - fn from(t: &'a T) -> Self { - DiagnosticArgFromDisplay(t) - } -} - -macro_rules! into_diagnostic_arg_using_display { - ($( $ty:ty ),+ $(,)?) => { - $( - impl IntoDiagnosticArg for $ty { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - self.to_string().into_diagnostic_arg() - } - } - )+ - } -} - -into_diagnostic_arg_using_display!( - i8, - u8, - i16, - u16, - i32, - u32, - i64, - u64, - i128, - u128, - std::io::Error, - std::num::NonZeroU32, - hir::Target, - Edition, - Ident, - MacroRulesNormalizedIdent, - ParseIntError, - StackProtector, - &TargetTriple, - SplitDebuginfo -); - -impl IntoDiagnosticArg for bool { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - if self { - DiagnosticArgValue::Str(Cow::Borrowed("true")) - } else { - DiagnosticArgValue::Str(Cow::Borrowed("false")) - } - } -} - -impl IntoDiagnosticArg for char { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(format!("{:?}", self))) - } -} - -impl IntoDiagnosticArg for Symbol { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - self.to_ident_string().into_diagnostic_arg() - } -} - -impl<'a> IntoDiagnosticArg for &'a str { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - self.to_string().into_diagnostic_arg() - } -} - -impl IntoDiagnosticArg for String { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self)) - } -} - -impl<'a> IntoDiagnosticArg for &'a Path { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.display().to_string())) - } -} - -impl IntoDiagnosticArg for PathBuf { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.display().to_string())) - } -} - -impl IntoDiagnosticArg for usize { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Number(self) - } -} - -impl IntoDiagnosticArg for PanicStrategy { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.desc().to_string())) - } -} - impl<'source> Into> for DiagnosticArgValue<'source> { fn into(self) -> FluentValue<'source> { match self { @@ -171,34 +52,6 @@ impl<'source> Into> for DiagnosticArgValue<'source> { } } -impl IntoDiagnosticArg for hir::ConstContext { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Borrowed(match self { - hir::ConstContext::ConstFn => "constant function", - hir::ConstContext::Static(_) => "static", - hir::ConstContext::Const => "constant", - })) - } -} - -impl IntoDiagnosticArg for ast::Path { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(pprust::path_to_string(&self))) - } -} - -impl IntoDiagnosticArg for ast::token::Token { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(pprust::token_to_string(&self)) - } -} - -impl IntoDiagnosticArg for ast::token::TokenKind { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(pprust::token_kind_to_string(&self)) - } -} - /// Trait implemented by error types. This should not be implemented manually. Instead, use /// `#[derive(Subdiagnostic)]` -- see [rustc_macros::Subdiagnostic]. #[cfg_attr(bootstrap, rustc_diagnostic_item = "AddSubdiagnostic")] diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index 2a1a5d5afb04..860f24871bcd 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -1,5 +1,159 @@ -use crate::{fluent, DiagnosticBuilder, Handler, IntoDiagnostic}; +use crate::{ + fluent, DiagnosticArgValue, DiagnosticBuilder, Handler, IntoDiagnostic, IntoDiagnosticArg, +}; use rustc_target::abi::TargetDataLayoutErrors; +use rustc_target::spec::{PanicStrategy, SplitDebuginfo, StackProtector, TargetTriple}; + +use rustc_ast as ast; +use rustc_ast_pretty::pprust; +use rustc_hir as hir; +use rustc_span::edition::Edition; +use rustc_span::symbol::{Ident, MacroRulesNormalizedIdent, Symbol}; +use std::borrow::Cow; +use std::fmt; +use std::num::ParseIntError; +use std::path::{Path, PathBuf}; + +pub struct DiagnosticArgFromDisplay<'a>(pub &'a dyn fmt::Display); + +impl IntoDiagnosticArg for DiagnosticArgFromDisplay<'_> { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + self.0.to_string().into_diagnostic_arg() + } +} + +impl<'a> From<&'a dyn fmt::Display> for DiagnosticArgFromDisplay<'a> { + fn from(t: &'a dyn fmt::Display) -> Self { + DiagnosticArgFromDisplay(t) + } +} + +impl<'a, T: fmt::Display> From<&'a T> for DiagnosticArgFromDisplay<'a> { + fn from(t: &'a T) -> Self { + DiagnosticArgFromDisplay(t) + } +} + +macro_rules! into_diagnostic_arg_using_display { + ($( $ty:ty ),+ $(,)?) => { + $( + impl IntoDiagnosticArg for $ty { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + self.to_string().into_diagnostic_arg() + } + } + )+ + } +} + +into_diagnostic_arg_using_display!( + i8, + u8, + i16, + u16, + i32, + u32, + i64, + u64, + i128, + u128, + std::io::Error, + std::num::NonZeroU32, + hir::Target, + Edition, + Ident, + MacroRulesNormalizedIdent, + ParseIntError, + StackProtector, + &TargetTriple, + SplitDebuginfo +); + +impl IntoDiagnosticArg for bool { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + if self { + DiagnosticArgValue::Str(Cow::Borrowed("true")) + } else { + DiagnosticArgValue::Str(Cow::Borrowed("false")) + } + } +} + +impl IntoDiagnosticArg for char { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::Owned(format!("{:?}", self))) + } +} + +impl IntoDiagnosticArg for Symbol { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + self.to_ident_string().into_diagnostic_arg() + } +} + +impl<'a> IntoDiagnosticArg for &'a str { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + self.to_string().into_diagnostic_arg() + } +} + +impl IntoDiagnosticArg for String { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::Owned(self)) + } +} + +impl<'a> IntoDiagnosticArg for &'a Path { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::Owned(self.display().to_string())) + } +} + +impl IntoDiagnosticArg for PathBuf { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::Owned(self.display().to_string())) + } +} + +impl IntoDiagnosticArg for usize { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Number(self) + } +} + +impl IntoDiagnosticArg for PanicStrategy { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::Owned(self.desc().to_string())) + } +} + +impl IntoDiagnosticArg for hir::ConstContext { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::Borrowed(match self { + hir::ConstContext::ConstFn => "constant function", + hir::ConstContext::Static(_) => "static", + hir::ConstContext::Const => "constant", + })) + } +} + +impl IntoDiagnosticArg for ast::Path { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::Owned(pprust::path_to_string(&self))) + } +} + +impl IntoDiagnosticArg for ast::token::Token { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(pprust::token_to_string(&self)) + } +} + +impl IntoDiagnosticArg for ast::token::TokenKind { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(pprust::token_kind_to_string(&self)) + } +} impl IntoDiagnostic<'_, !> for TargetDataLayoutErrors<'_> { fn into_diagnostic(self, handler: &Handler) -> DiagnosticBuilder<'_, !> { diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 955e85c3616b..9fafbe4bd407 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -372,10 +372,11 @@ impl fmt::Display for ExplicitBug { impl error::Error for ExplicitBug {} pub use diagnostic::{ - AddToDiagnostic, DecorateLint, Diagnostic, DiagnosticArg, DiagnosticArgFromDisplay, - DiagnosticArgValue, DiagnosticId, DiagnosticStyledString, IntoDiagnosticArg, SubDiagnostic, + AddToDiagnostic, DecorateLint, Diagnostic, DiagnosticArg, DiagnosticArgValue, DiagnosticId, + DiagnosticStyledString, IntoDiagnosticArg, SubDiagnostic, }; pub use diagnostic_builder::{DiagnosticBuilder, EmissionGuarantee, Noted}; +pub use diagnostic_impls::DiagnosticArgFromDisplay; use std::backtrace::Backtrace; /// A handler deals with errors and other compiler output. From c278700af4bf75547e0b796fb56b489ad5b72046 Mon Sep 17 00:00:00 2001 From: est31 Date: Wed, 12 Oct 2022 17:57:05 +0200 Subject: [PATCH 116/333] tidy: error if a lang feature is already present If a lang feature gets declared twice, like for example as a result of a mistake during stabilization, emit an error in tidy. Library features already have this logic. --- src/tools/tidy/src/features.rs | 265 +++++++++++++++++---------------- 1 file changed, 138 insertions(+), 127 deletions(-) diff --git a/src/tools/tidy/src/features.rs b/src/tools/tidy/src/features.rs index d8b3903b98e7..f10ecf5f201e 100644 --- a/src/tools/tidy/src/features.rs +++ b/src/tools/tidy/src/features.rs @@ -10,7 +10,7 @@ //! * Language features in a group are sorted by feature name. use crate::walk::{filter_dirs, walk, walk_many}; -use std::collections::HashMap; +use std::collections::hash_map::{Entry, HashMap}; use std::fmt; use std::fs; use std::num::NonZeroU32; @@ -280,13 +280,14 @@ fn test_filen_gate(filen_underscore: &str, features: &mut Features) -> bool { } pub fn collect_lang_features(base_compiler_path: &Path, bad: &mut bool) -> Features { - let mut all = collect_lang_features_in(base_compiler_path, "active.rs", bad); - all.extend(collect_lang_features_in(base_compiler_path, "accepted.rs", bad)); - all.extend(collect_lang_features_in(base_compiler_path, "removed.rs", bad)); - all + let mut features = Features::new(); + collect_lang_features_in(&mut features, base_compiler_path, "active.rs", bad); + collect_lang_features_in(&mut features, base_compiler_path, "accepted.rs", bad); + collect_lang_features_in(&mut features, base_compiler_path, "removed.rs", bad); + features } -fn collect_lang_features_in(base: &Path, file: &str, bad: &mut bool) -> Features { +fn collect_lang_features_in(features: &mut Features, base: &Path, file: &str, bad: &mut bool) { let path = base.join("rustc_feature").join("src").join(file); let contents = t!(fs::read_to_string(&path)); @@ -298,135 +299,145 @@ fn collect_lang_features_in(base: &Path, file: &str, bad: &mut bool) -> Features let mut in_feature_group = false; let mut prev_names = vec![]; - contents - .lines() - .zip(1..) - .filter_map(|(line, line_number)| { - let line = line.trim(); + let lines = contents.lines().zip(1..); + for (line, line_number) in lines { + let line = line.trim(); - // Within -start and -end, the tracking issue can be omitted. - match line { - "// no-tracking-issue-start" => { - next_feature_omits_tracking_issue = true; - return None; - } - "// no-tracking-issue-end" => { - next_feature_omits_tracking_issue = false; - return None; - } - _ => {} + // Within -start and -end, the tracking issue can be omitted. + match line { + "// no-tracking-issue-start" => { + next_feature_omits_tracking_issue = true; + continue; } - - if line.starts_with(FEATURE_GROUP_START_PREFIX) { - if in_feature_group { - tidy_error!( - bad, - "{}:{}: \ - new feature group is started without ending the previous one", - path.display(), - line_number, - ); - } - - in_feature_group = true; - prev_names = vec![]; - return None; - } else if line.starts_with(FEATURE_GROUP_END_PREFIX) { - in_feature_group = false; - prev_names = vec![]; - return None; + "// no-tracking-issue-end" => { + next_feature_omits_tracking_issue = false; + continue; } + _ => {} + } - let mut parts = line.split(','); - let level = match parts.next().map(|l| l.trim().trim_start_matches('(')) { - Some("active") => Status::Unstable, - Some("incomplete") => Status::Unstable, - Some("removed") => Status::Removed, - Some("accepted") => Status::Stable, - _ => return None, - }; - let name = parts.next().unwrap().trim(); - - let since_str = parts.next().unwrap().trim().trim_matches('"'); - let since = match since_str.parse() { - Ok(since) => Some(since), - Err(err) => { - tidy_error!( - bad, - "{}:{}: failed to parse since: {} ({:?})", - path.display(), - line_number, - since_str, - err, - ); - None - } - }; + if line.starts_with(FEATURE_GROUP_START_PREFIX) { if in_feature_group { - if prev_names.last() > Some(&name) { - // This assumes the user adds the feature name at the end of the list, as we're - // not looking ahead. - let correct_index = match prev_names.binary_search(&name) { - Ok(_) => { - // This only occurs when the feature name has already been declared. - tidy_error!( - bad, - "{}:{}: duplicate feature {}", - path.display(), - line_number, - name, - ); - // skip any additional checks for this line - return None; - } - Err(index) => index, - }; - - let correct_placement = if correct_index == 0 { - "at the beginning of the feature group".to_owned() - } else if correct_index == prev_names.len() { - // I don't believe this is reachable given the above assumption, but it - // doesn't hurt to be safe. - "at the end of the feature group".to_owned() - } else { - format!( - "between {} and {}", - prev_names[correct_index - 1], - prev_names[correct_index], - ) - }; - - tidy_error!( - bad, - "{}:{}: feature {} is not sorted by feature name (should be {})", - path.display(), - line_number, - name, - correct_placement, - ); - } - prev_names.push(name); + tidy_error!( + bad, + "{}:{}: \ + new feature group is started without ending the previous one", + path.display(), + line_number, + ); } - let issue_str = parts.next().unwrap().trim(); - let tracking_issue = if issue_str.starts_with("None") { - if level == Status::Unstable && !next_feature_omits_tracking_issue { - tidy_error!( - bad, - "{}:{}: no tracking issue for feature {}", - path.display(), - line_number, - name, - ); - } + in_feature_group = true; + prev_names = vec![]; + continue; + } else if line.starts_with(FEATURE_GROUP_END_PREFIX) { + in_feature_group = false; + prev_names = vec![]; + continue; + } + + let mut parts = line.split(','); + let level = match parts.next().map(|l| l.trim().trim_start_matches('(')) { + Some("active") => Status::Unstable, + Some("incomplete") => Status::Unstable, + Some("removed") => Status::Removed, + Some("accepted") => Status::Stable, + _ => continue, + }; + let name = parts.next().unwrap().trim(); + + let since_str = parts.next().unwrap().trim().trim_matches('"'); + let since = match since_str.parse() { + Ok(since) => Some(since), + Err(err) => { + tidy_error!( + bad, + "{}:{}: failed to parse since: {} ({:?})", + path.display(), + line_number, + since_str, + err, + ); None - } else { - let s = issue_str.split('(').nth(1).unwrap().split(')').next().unwrap(); - Some(s.parse().unwrap()) - }; - Some((name.to_owned(), Feature { level, since, has_gate_test: false, tracking_issue })) - }) - .collect() + } + }; + if in_feature_group { + if prev_names.last() > Some(&name) { + // This assumes the user adds the feature name at the end of the list, as we're + // not looking ahead. + let correct_index = match prev_names.binary_search(&name) { + Ok(_) => { + // This only occurs when the feature name has already been declared. + tidy_error!( + bad, + "{}:{}: duplicate feature {}", + path.display(), + line_number, + name, + ); + // skip any additional checks for this line + continue; + } + Err(index) => index, + }; + + let correct_placement = if correct_index == 0 { + "at the beginning of the feature group".to_owned() + } else if correct_index == prev_names.len() { + // I don't believe this is reachable given the above assumption, but it + // doesn't hurt to be safe. + "at the end of the feature group".to_owned() + } else { + format!( + "between {} and {}", + prev_names[correct_index - 1], + prev_names[correct_index], + ) + }; + + tidy_error!( + bad, + "{}:{}: feature {} is not sorted by feature name (should be {})", + path.display(), + line_number, + name, + correct_placement, + ); + } + prev_names.push(name); + } + + let issue_str = parts.next().unwrap().trim(); + let tracking_issue = if issue_str.starts_with("None") { + if level == Status::Unstable && !next_feature_omits_tracking_issue { + tidy_error!( + bad, + "{}:{}: no tracking issue for feature {}", + path.display(), + line_number, + name, + ); + } + None + } else { + let s = issue_str.split('(').nth(1).unwrap().split(')').next().unwrap(); + Some(s.parse().unwrap()) + }; + match features.entry(name.to_owned()) { + Entry::Occupied(e) => { + tidy_error!( + bad, + "{}:{} feature {name} already specified with status '{}'", + path.display(), + line_number, + e.get().level, + ); + } + Entry::Vacant(e) => { + e.insert(Feature { level, since, has_gate_test: false, tracking_issue }); + } + } + } } fn get_and_check_lib_features( From 58e5452a9bd6adef56e7099b21862985c4c0151c Mon Sep 17 00:00:00 2001 From: HKalbasi <45197576+HKalbasi@users.noreply.github.com> Date: Wed, 12 Oct 2022 14:14:59 -0700 Subject: [PATCH 117/333] fix formatting --- editors/code/src/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index d66aa52fadc6..15846a5e8645 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -137,7 +137,7 @@ export class Config { if (!item) return item; const fixRecord = (r: Record) => { for (const key in r) { - if (typeof r[key] !== 'string') { + if (typeof r[key] !== "string") { r[key] = String(r[key]); } } From 8b2c3ebb86b0f9a1d218daa0bd94096170233b74 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Wed, 12 Oct 2022 14:26:22 -0700 Subject: [PATCH 118/333] Add a fixme --- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 5837b4ff2b9d..e3491d367519 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -287,6 +287,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }; let vtable = get_vtable(bx.cx(), source.ty(self.mir, bx.tcx()), trait_ref); let vtable = bx.pointercast(vtable, bx.cx().type_ptr_to(bx.cx().type_isize())); + // FIXME(dyn-star): this is probably not the best way to check if this is + // a pointer, and really we should ensure that the value is a suitable + // pointer earlier in the compilation process. let data = match operand.layout.pointee_info_at(bx.cx(), Size::ZERO) { Some(_) => bx.ptrtoint(data, bx.cx().type_isize()), None => data, From ddd119b2fed57eb6b19c44c18108de95c564a48d Mon Sep 17 00:00:00 2001 From: Pointerbender Date: Wed, 12 Oct 2022 23:34:13 +0200 Subject: [PATCH 119/333] expand documentation on type conversion w.r.t. `UnsafeCell` --- library/core/src/cell.rs | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index be1017e58428..4dee386f8879 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -1811,14 +1811,19 @@ impl fmt::Display for RefMut<'_, T> { /// /// [`.get_mut()`]: `UnsafeCell::get_mut` /// -/// `UnsafeCell` has the same in-memory representation as its inner type `T` if and only if -/// the type `T` does not contain a [niche] (e.g. the type `Option>` is typically -/// 8 bytes large on 64-bit platforms, but the type `Option>>` takes -/// up 16 bytes of space). A consequence of this guarantee is that it is possible to convert -/// between `T` and `UnsafeCell` when `T` has no niches. However, it is only valid to obtain -/// a `*mut T` pointer to the contents of a _shared_ `UnsafeCell` through [`.get()`] or -/// [`.raw_get()`]. A `&mut T` reference can be obtained by either dereferencing this pointer -/// or by calling [`.get_mut()`] on an _exclusive_ `UnsafeCell`, e.g.: +/// `UnsafeCell` has the same in-memory representation as its inner type `T`. A consequence +/// of this guarantee is that it is possible to convert between `T` and `UnsafeCell`. +/// Special care has to be taken when converting a nested `T` inside of an `Outer` type +/// to an `Outer>` type: this is not sound when the `Outer` type enables [niche] +/// optimizations. For example, the type `Option>` is typically 8 bytes large on +/// 64-bit platforms, but the type `Option>>` takes up 16 bytes of space. +/// Therefore this is not a valid conversion, despite `NonNull` and `UnsafeCell>>` +/// having the same memory layout. This is because `UnsafeCell` disables niche optimizations in +/// order to avoid its interior mutability property from spreading from `T` into the `Outer` type, +/// thus this can cause distortions in the type size in these cases. Furthermore, it is only valid +/// to obtain a `*mut T` pointer to the contents of a _shared_ `UnsafeCell` through [`.get()`] +/// or [`.raw_get()`]. A `&mut T` reference can be obtained by either dereferencing this pointer or +/// by calling [`.get_mut()`] on an _exclusive_ `UnsafeCell`, e.g.: /// /// ```rust /// use std::cell::UnsafeCell; From ad8b24272428b28770471f222c19fa3154a65819 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Wed, 12 Oct 2022 15:29:08 -0700 Subject: [PATCH 120/333] Let chains should still drop temporaries by the end of the condition's execution --- compiler/rustc_ast_lowering/src/expr.rs | 45 +++++++++++++----- src/test/ui/drop/drop_order.rs | 63 +++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index c55b49063020..104010f8d435 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -388,7 +388,7 @@ impl<'hir> LoweringContext<'_, 'hir> { else_opt: Option<&Expr>, ) -> hir::ExprKind<'hir> { let lowered_cond = self.lower_expr(cond); - let new_cond = self.manage_let_cond(lowered_cond); + let new_cond = self.wrap_cond_in_drop_scope(lowered_cond); let then_expr = self.lower_block_expr(then); if let Some(rslt) = else_opt { hir::ExprKind::If(new_cond, self.arena.alloc(then_expr), Some(self.lower_expr(rslt))) @@ -397,9 +397,9 @@ impl<'hir> LoweringContext<'_, 'hir> { } } - // If `cond` kind is `let`, returns `let`. Otherwise, wraps and returns `cond` - // in a temporary block. - fn manage_let_cond(&mut self, cond: &'hir hir::Expr<'hir>) -> &'hir hir::Expr<'hir> { + // Wraps a condition (i.e. `cond` in `if cond` or `while cond`) in a terminating scope + // so that temporaries created in the condition don't live beyond it. + fn wrap_cond_in_drop_scope(&mut self, cond: &'hir hir::Expr<'hir>) -> &'hir hir::Expr<'hir> { fn has_let_expr<'hir>(expr: &'hir hir::Expr<'hir>) -> bool { match expr.kind { hir::ExprKind::Binary(_, lhs, rhs) => has_let_expr(lhs) || has_let_expr(rhs), @@ -407,12 +407,35 @@ impl<'hir> LoweringContext<'_, 'hir> { _ => false, } } - if has_let_expr(cond) { - cond - } else { - let reason = DesugaringKind::CondTemporary; - let span_block = self.mark_span_with_reason(reason, cond.span, None); - self.expr_drop_temps(span_block, cond, AttrVec::new()) + + // We have to take special care for `let` exprs in the condition, e.g. in + // `if let pat = val` or `if foo && let pat = val`, as we _do_ want `val` to live beyond the + // condition in this case. + // + // In order to mantain the drop behavior for the non `let` parts of the condition, + // we still wrap them in terminating scopes, e.g. `if foo && let pat = val` essentially + // gets transformed into `if { let _t = foo; _t } && let pat = val` + match cond.kind { + hir::ExprKind::Binary( + op @ Spanned { node: hir::BinOpKind::And | hir::BinOpKind::Or, .. }, + lhs, + rhs, + ) if has_let_expr(cond) => { + let lhs = self.wrap_cond_in_drop_scope(lhs); + let rhs = self.wrap_cond_in_drop_scope(rhs); + + self.arena.alloc(self.expr( + cond.span, + hir::ExprKind::Binary(op, lhs, rhs), + AttrVec::new(), + )) + } + hir::ExprKind::Let(_) => cond, + _ => { + let reason = DesugaringKind::CondTemporary; + let span_block = self.mark_span_with_reason(reason, cond.span, None); + self.expr_drop_temps(span_block, cond, AttrVec::new()) + } } } @@ -440,7 +463,7 @@ impl<'hir> LoweringContext<'_, 'hir> { opt_label: Option