From b31086a9e3f8e1d6190115fe745222a780cd3daa Mon Sep 17 00:00:00 2001 From: Yiming Lei Date: Wed, 12 Oct 2022 13:31:08 -0700 Subject: [PATCH 001/358] visit enum init when the enum variable doesn't have any parameters this will fix #101208 --- .../src/check/compare_method.rs | 3 + .../src/check/fn_ctxt/checks.rs | 1 + .../src/infer/error_reporting/mod.rs | 77 +++++++++++++++++-- .../src/traits/error_reporting/mod.rs | 1 + src/test/ui/type/issue-101208.rs | 10 +++ src/test/ui/type/issue-101208.stderr | 18 +++++ 6 files changed, 105 insertions(+), 5 deletions(-) create mode 100644 src/test/ui/type/issue-101208.rs create mode 100644 src/test/ui/type/issue-101208.stderr diff --git a/compiler/rustc_hir_analysis/src/check/compare_method.rs b/compiler/rustc_hir_analysis/src/check/compare_method.rs index 5e5dbedb4bd7b..8fbe740dc3abd 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_method.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_method.rs @@ -403,6 +403,7 @@ fn compare_predicate_entailment<'tcx>( terr, false, false, + false, ); return Err(diag.emit()); @@ -516,6 +517,7 @@ pub fn collect_trait_impl_trait_tys<'tcx>( terr, false, false, + false, ); return Err(diag.emit()); } @@ -1383,6 +1385,7 @@ pub(crate) fn raw_compare_const_impl<'tcx>( terr, false, false, + false, ); return Err(diag.emit()); }; diff --git a/compiler/rustc_hir_analysis/src/check/fn_ctxt/checks.rs b/compiler/rustc_hir_analysis/src/check/fn_ctxt/checks.rs index 285db90a9df55..ee3fab995008f 100644 --- a/compiler/rustc_hir_analysis/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_analysis/src/check/fn_ctxt/checks.rs @@ -848,6 +848,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { e, false, true, + false, ); } } diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index ddeeaa9618e60..2e8fd9dcfbb81 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -1,3 +1,4 @@ +// ignore-tidy-filelength //! Error Reporting Code for the inference engine //! //! Because of the way inference, and in particular region inference, @@ -64,8 +65,12 @@ use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString, Mul use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::intravisit::walk_block; +use rustc_hir::intravisit::walk_expr; +use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; -use rustc_hir::Node; +use rustc_hir::HirId; +use rustc_hir::{Expr, Node}; use rustc_middle::dep_graph::DepContext; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::relate::{self, RelateResult, TypeRelation}; @@ -620,6 +625,47 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { _ => (), // FIXME(#22750) handle traits and stuff } } + fn note_enum_suggestion( + &self, + err: &mut Diagnostic, + span: Span, + body_id: HirId, + arg_size: usize, + ) { + let body_node = self.tcx.hir().get(body_id); + let hir::Node::Expr(&hir::Expr{kind:hir::ExprKind::Block(body_expr, ..), ..}) = body_node else {return ()}; + struct FindExprVisitor<'tcx> { + target_span: Span, + size: usize, + terr: &'tcx mut Diagnostic, + } + impl<'tcx> Visitor<'tcx> for FindExprVisitor<'tcx> { + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + if expr.span == self.target_span { + let mut suggest_vec = vec![]; + let mut i = 0; + suggest_vec.push((expr.span.shrink_to_hi(), "(".to_string())); + while i < self.size { + suggest_vec.push((expr.span.shrink_to_hi(), "_".to_string())); + if i != self.size - 1 { + suggest_vec.push((expr.span.shrink_to_hi(), ",".to_string())); + } + i = i + 1; + } + suggest_vec.push((expr.span.shrink_to_hi(), ")".to_string())); + + self.terr.multipart_suggestion( + "use parentheses to instantiate this tuple variant", + suggest_vec, + Applicability::MachineApplicable, + ); + } + walk_expr(self, expr); + } + } + let mut visitor = FindExprVisitor { target_span: span, size: arg_size, terr: err }; + walk_block(&mut visitor, body_expr); + } fn note_error_origin( &self, @@ -627,6 +673,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { cause: &ObligationCause<'tcx>, exp_found: Option>>, terr: TypeError<'tcx>, + detect_enum_noparm: bool, ) { match *cause.code() { ObligationCauseCode::Pattern { origin_expr: true, span: Some(span), root_ty } => { @@ -640,8 +687,14 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { { err.span_label(span, format!("this is an iterator with items of type `{}`", substs.type_at(0))); } else { - err.span_label(span, format!("this expression has type `{}`", ty)); - } + err.span_label(span, format!("this expression has type `{}`", ty)); + if detect_enum_noparm && + let ty::FnDef(def_id, substs) = ty.kind(){ + let sig = self.tcx.bound_fn_sig(*def_id).subst(self.tcx, substs); + let sig = self.tcx.erase_late_bound_regions(sig); + self.note_enum_suggestion(err, span, cause.body_id, sig.inputs().len()); + } + } } if let Some(ty::error::ExpectedFound { found, .. }) = exp_found && ty.is_box() && ty.boxed_ty() == found @@ -1490,6 +1543,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { terr: TypeError<'tcx>, swap_secondary_and_primary: bool, prefer_label: bool, + detect_enum_noparm: bool, ) { let span = cause.span(); @@ -1940,7 +1994,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // It reads better to have the error origin as the final // thing. - self.note_error_origin(diag, cause, exp_found, terr); + self.note_error_origin(diag, cause, exp_found, terr, detect_enum_noparm); debug!(?diag); } @@ -2260,6 +2314,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let span = trace.cause.span(); let failure_code = trace.cause.as_failure_code(terr); + let mut detect_enum_noparm = false; let mut diag = match failure_code { FailureCode::Error0038(did) => { let violations = self.tcx.object_safety_violations(did); @@ -2314,6 +2369,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } } + (ty::FnDef(_, _), ty::Adt(adt_id, _)) if adt_id.is_enum() => { + detect_enum_noparm = true; + } _ => {} } } @@ -2334,7 +2392,16 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { struct_span_err!(self.tcx.sess, span, E0644, "{}", failure_str) } }; - self.note_type_err(&mut diag, &trace.cause, None, Some(trace.values), terr, false, false); + self.note_type_err( + &mut diag, + &trace.cause, + None, + Some(trace.values), + terr, + false, + false, + detect_enum_noparm, + ); diag } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 4e8baa2dfab6c..eaf5b4cc74598 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -1682,6 +1682,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err, true, false, + false, ); self.note_obligation_cause(&mut diag, obligation); diag.emit(); diff --git a/src/test/ui/type/issue-101208.rs b/src/test/ui/type/issue-101208.rs new file mode 100644 index 0000000000000..49654a9e32734 --- /dev/null +++ b/src/test/ui/type/issue-101208.rs @@ -0,0 +1,10 @@ +enum E { + One(i32, i32) +} +fn main() { + let var = E::One; + if let E::One(var1, var2) = var { + //~^ ERROR 0308 + println!("{var1} {var2}"); + } +} diff --git a/src/test/ui/type/issue-101208.stderr b/src/test/ui/type/issue-101208.stderr new file mode 100644 index 0000000000000..ae6065df2f29e --- /dev/null +++ b/src/test/ui/type/issue-101208.stderr @@ -0,0 +1,18 @@ +error[E0308]: mismatched types + --> $DIR/issue-101208.rs:6:12 + | +LL | if let E::One(var1, var2) = var { + | ^^^^^^^^^^^^^^^^^^ --- this expression has type `fn(i32, i32) -> E {E::One}` + | | + | expected fn item, found enum `E` + | + = note: expected fn item `fn(i32, i32) -> E {E::One}` + found enum `E` +help: use parentheses to instantiate this tuple variant + | +LL | if let E::One(var1, var2) = var(_,_) { + | + + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. From 4713ed729197e7bc37d26ada8e210032f960660f Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Fri, 15 Jul 2022 12:22:36 -0500 Subject: [PATCH 002/358] 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 687d810ed4e31..2117006df10f1 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -545,7 +545,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; @@ -614,7 +618,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 ce5893efa926d..724056f726840 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -160,6 +160,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 169a98c9483c7..67e8ec8f13b4f 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 3bc1d6fc135e5..0db79a1564fa2 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 272a1a972340c..226367c399299 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 e123d83584c81..b5e3e5f1202c1 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 635e6c89e384e31dabcc2e9b4dd966f38b3de304 Mon Sep 17 00:00:00 2001 From: woppopo Date: Fri, 26 Aug 2022 18:14:12 +0900 Subject: [PATCH 003/358] 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 8eefd9ff20db2..26a410365568c 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 2c84ee9d6e77491384feeda886b53044c269cf82 Mon Sep 17 00:00:00 2001 From: Pointerbender Date: Mon, 12 Sep 2022 11:12:28 +0200 Subject: [PATCH 004/358] 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 4474b673a9577..55a6ec71d5544 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -1816,6 +1816,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 4e06c1c7dc22db59a42b425e5f85edac5cf83ce3 Mon Sep 17 00:00:00 2001 From: Pointerbender Date: Wed, 14 Sep 2022 10:10:18 +0200 Subject: [PATCH 005/358] 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 55a6ec71d5544..610b65499c286 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -1816,7 +1816,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 0e39d7137555adb3d29ad591a10fcc9e31e33322 Mon Sep 17 00:00:00 2001 From: feniljain Date: Thu, 15 Sep 2022 17:22:44 +0530 Subject: [PATCH 006/358] 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs index e26c76da18916..f67cc924409ff 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs @@ -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 581fbf4716b18efc56fbdb5612ae4d80781977dc Mon Sep 17 00:00:00 2001 From: feniljain Date: Thu, 15 Sep 2022 19:33:19 +0530 Subject: [PATCH 007/358] 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs index f67cc924409ff..8b67982f91582 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs @@ -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 5826fdccfe502a0cf8141cf4cd910ea736bff19c Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Wed, 14 Sep 2022 22:41:37 -0700 Subject: [PATCH 008/358] 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 f651cb0217698..b7e7d5a38a5b1 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -1620,6 +1620,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]>; @@ -1635,13 +1651,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 7ac348485aea0292b77f7b4f1cb3e8d2d564c9af Mon Sep 17 00:00:00 2001 From: kxxt Date: Wed, 21 Sep 2022 20:51:03 +0800 Subject: [PATCH 009/358] 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 77dc52a935070..08de25a77783a 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 75739763ff43963d9b0517e60a9f2371d70954b8 Mon Sep 17 00:00:00 2001 From: kxxt Date: Mon, 26 Sep 2022 21:56:08 +0800 Subject: [PATCH 010/358] 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 08de25a77783a..238f7f5b66027 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 5de954a418d349014c8e98197e9cc1b098e42d7f Mon Sep 17 00:00:00 2001 From: Trevor Spiteri Date: Tue, 27 Sep 2022 17:31:31 +0200 Subject: [PATCH 011/358] 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 d921ff9ba1026..872fcc4f16e3f 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -1470,37 +1470,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.")] /// - /// # Examples + /// 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. /// - /// Basic usage + /// # Examples /// /// ``` /// #![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")] @@ -1565,22 +1570,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 4c8f4d6404c1c4257786e3a5f5069cfff39b4b23 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Tue, 27 Sep 2022 17:35:50 +0200 Subject: [PATCH 012/358] 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 0000000000000..8d11e0bac9413 --- /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/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs index 82bcc3dfa5d9a..a07318cefad27 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs +++ b/src/tools/rust-analyzer/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 1f814a47623bae774e5492335b8313db70f85b9f Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Tue, 27 Sep 2022 20:40:51 +0200 Subject: [PATCH 013/358] 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/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs index a3bb66e379eb8..f7f2417d0745d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs index d403f86c6d8c9..2c4000efe0fa2 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs @@ -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 b3150c0135cdeed3a1c2aff360d9c7e812a066df Mon Sep 17 00:00:00 2001 From: woppopo Date: Tue, 27 Sep 2022 19:09:32 +0000 Subject: [PATCH 014/358] 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 ca0c7a54b3e24..d78bb638565a4 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)] @@ -132,6 +133,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 0000000000000..b7056bdf36ed7 --- /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 0000000000000..ba066bfbc5398 --- /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 b786b158ab29ffcaeda0aa7e817e5de817eab4ac Mon Sep 17 00:00:00 2001 From: woppopo Date: Tue, 27 Sep 2022 19:23:52 +0000 Subject: [PATCH 015/358] 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 b7056bdf36ed7..24b6c56b35691 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 ba066bfbc5398..9d7b912458a7b 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 eb06ef9b545d555274e46e3171b63bf0615720f0 Mon Sep 17 00:00:00 2001 From: woppopo Date: Tue, 27 Sep 2022 19:40:53 +0000 Subject: [PATCH 016/358] 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 9d7b912458a7b..9df626a8326ce 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 b9e0eeecb15ddf50acce5367016fa29368c03be8 Mon Sep 17 00:00:00 2001 From: woppopo Date: Tue, 27 Sep 2022 19:53:58 +0000 Subject: [PATCH 017/358] 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 d78bb638565a4..ac8533db04d28 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -132,8 +132,8 @@ mod nonzero; mod num; mod ops; mod option; -mod pattern; mod panic; +mod pattern; mod pin; mod pin_macro; mod ptr; From 4f3c45a1f6eb32441b0c31261273016c7b2ba557 Mon Sep 17 00:00:00 2001 From: Yan Chen Date: Tue, 20 Sep 2022 14:44:55 -0700 Subject: [PATCH 018/358] 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 1c01e78abd422..582d683dd3593 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 4cefd1ec387d6..8b63294fbab0e 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 15ac737606d66..d2b845619c784 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 95896c6bbf987..22f9b39dfeecb 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 2be0460df1fc6..1c1a31d356d6f 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 0e3adb363e2a656394aed81e494f3478320c2dc2 Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 29 Sep 2022 12:14:39 +0200 Subject: [PATCH 019/358] 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 6eb0239568556..5eb16bcd1564e 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -492,19 +492,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 ddabea700d374..9ebff48920160 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -676,19 +676,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 6a2486a4f03cc9cfa6b40671e12ea98aff90f5d4 Mon Sep 17 00:00:00 2001 From: Colin Baumgarten Date: Sat, 1 Oct 2022 00:40:59 +0200 Subject: [PATCH 020/358] 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 df22d79f82e85..1394993abade9 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 2f8c229c68ffe..3f9cb149b53eb 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 ed398b56612ce..799ce9d5daa88 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 e8a48755da499565a901f2ca1122abb60c5b23fc Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 29 Sep 2022 13:16:47 +0800 Subject: [PATCH 021/358] 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 c6a4ff6f0e02f..827c3c93a73d9 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 e276c4f7d84c4..c039394bb6fd8 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; } @@ -1202,6 +1205,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>, @@ -1264,7 +1330,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 d891171b82468..6634444c636d4 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 01bf9f4840246..74cd2b3583481 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 c6a76c9e79018..f2084b593c290 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 fad05bfd57a13a100fe4410e47069b802bbd0797 Mon Sep 17 00:00:00 2001 From: yukang Date: Mon, 3 Oct 2022 04:22:59 +0800 Subject: [PATCH 022/358] find the correct lang item for ranges --- .../locales/en-US/hir_analysis.ftl | 3 +- .../src/check/method/suggest.rs | 76 +++++++++++-------- 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, 108 insertions(+), 68 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 827c3c93a73d9..5a7aee9c1c147 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 c039394bb6fd8..c1cb4d8e3773a 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}; @@ -1215,50 +1215,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; - } + 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(...)`. + ExprKind::Path(QPath::LangItem(LangItem::RangeInclusiveNew, ..)) => { + Some(LangItem::RangeInclusiveStruct) + } + _ => None, + }, + _ => None, + }; + + if lang_item.is_none() { + continue; + } - 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, + 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 6634444c636d4..41f73323d9a9a 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 74cd2b3583481..79cdc41959a74 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 f2084b593c290..581d6fb4fc9e4 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(); + | ~~~~~ + +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. +Some errors have detailed explanations: E0308, E0599, E0689. For more information about an error, try `rustc --explain E0308`. From 146e8716f4c8835670c0b9be8181b769c84a6c49 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Mon, 3 Oct 2022 17:35:03 -0700 Subject: [PATCH 023/358] 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 abf3c9a363fca..fedb944480d9f 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 0000000000000..d1f1819d9f35f --- /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 abf6cba9361c38d71ef5afee1caeaf8f741be6d2 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 11 Sep 2022 07:42:04 +0000 Subject: [PATCH 024/358] 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 1b7ed60929d00..0aa44431c79b0 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 511646b0eaf29..f30fd1c3d7e28 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -72,7 +72,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> { @@ -1318,6 +1326,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 = @@ -1330,7 +1349,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 @@ -2211,6 +2241,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 0000000000000..effc4b1d951ad --- /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 3f3983f293b5245be90bacccb45e7bb159335198 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 5 Oct 2022 04:16:05 +0000 Subject: [PATCH 025/358] 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 2f6ec836c3c06..f8a169fc869f1 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 0983c3148f2ff..e904fab4d7653 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 f30fd1c3d7e28..422a855627439 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1326,10 +1326,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 effc4b1d951ad..b0baf5bb10dd2 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 7cfe3ed01dad1e1a56a2ba3076c5839b51e5a366 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 11 Sep 2022 08:51:25 +0000 Subject: [PATCH 026/358] 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 0ea685986db40..e2062e82725c0 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 4fa54c6e36960..afbe25cf7ab74 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[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 +error: aborting due to 3 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 22a61dcd25f98..6bd6d879a4ace 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 62a243e69e743..ba918eb28def1 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 11 previous errors +error: aborting due to 10 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 94f578af209a8..4559da91e47a8 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 386385165f645..df1cafdb7d3cc 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 88328d137e35d27f009b1b73750771d0b8778c2f Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 30 Sep 2022 19:16:13 +0000 Subject: [PATCH 027/358] Validate opaques in default trait bodies, don't normalize unless a body is provided --- .../rustc_hir_analysis/src/check/check.rs | 56 +++++++++++-------- compiler/rustc_middle/src/ty/sty.rs | 2 +- .../src/traits/project.rs | 3 +- 3 files changed, 36 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index f6f25603581b2..7cee9779c5f39 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -522,23 +522,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; + }; + + // 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; } - if check_opaque_for_cycles(tcx, def_id, substs, span, origin).is_err() { + + 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, def_id, substs, span, origin); + 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))] @@ -857,17 +867,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 f8a169fc869f1..646369633133c 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 422a855627439..cd1e9a97731ee 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1326,11 +1326,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, @@ -1338,7 +1340,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 3c3b02ca22cf3ae0470b4771029856ff57a2f851 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 6 Oct 2022 14:31:06 +0200 Subject: [PATCH 028/358] internal: Allow minicore flags specification to be order independent --- .../crates/test-utils/src/fixture.rs | 53 ++++++++++--------- .../crates/test-utils/src/minicore.rs | 48 ++++++++--------- 2 files changed, 51 insertions(+), 50 deletions(-) diff --git a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs index 8c806e7925b15..c824f5af72584 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs +++ b/src/tools/rust-analyzer/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(); + + self.valid_flags.push(flag.to_string()); + implications.extend( + iter::repeat(flag) + .zip(deps.split(", ").map(str::trim).filter(|dep| !dep.is_empty())), + ); + } - if line.contains("Available flags:") { - parsing_flags = true; - } + 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/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index 10386b5b7bcdd..69d2e62b25673 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/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 +//! add: +//! as_ref: sized +//! bool_impl: option, fn +//! clone: sized //! coerce_unsized: unsize -//! slice: -//! range: -//! deref: sized +//! copy: clone +//! default: sized //! deref_mut: deref -//! index: sized +//! deref: sized +//! derive: +//! drop: +//! eq: sized +//! fmt: result //! fn: -//! try: -//! pin: +//! from: sized //! future: pin -//! option: -//! result: +//! generator: pin +//! hash: +//! index: sized //! iterator: option //! iterators: iterator, fn -//! default: sized -//! hash: -//! clone: sized -//! copy: clone -//! from: sized -//! eq: sized +//! option: //! ord: eq, option -//! derive: -//! fmt: result -//! bool_impl: option, fn -//! add: -//! as_ref: sized -//! drop: -//! generator: pin +//! 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 9d84f8a0d41980ba57b053188e46f9e2a3901183 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 029/358] Add stub for cargo environment variables auto completion --- .../src/completions/env_vars.rs | 60 +++++++++++++++++++ .../crates/ide-completion/src/completions.rs | 1 + .../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/env_vars.rs b/crates/ide-completion/src/completions/env_vars.rs new file mode 100644 index 0000000000000..6cfbd0f922f8a --- /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/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs index 97b90c62dd777..296dfc14250f7 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs index 8d21f4fce0a2b..cd9da0d3dcf04 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs +++ b/src/tools/rust-analyzer/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 b2c90717f29d126d14c2a1f5ec522a9a25912229 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 030/358] Use expanded version of text for env var completion --- src/tools/rust-analyzer/crates/ide-completion/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs index cd9da0d3dcf04..c5718ec40f588 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs +++ b/src/tools/rust-analyzer/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 0657fd7f59fd83b44d22b1d004dfeea51409eae4 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 031/358] 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/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs index b890e2b58df8f..78d6bd008afe7 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs +++ b/src/tools/rust-analyzer/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 dbeb9e77cc10054ccfce4395de58fbd39e3b46ea 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 032/358] 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 6cfbd0f922f8a..eba39bba3d5d0 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 655595463493b6e39246177c7f4f02353574e6b1 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 033/358] 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 eba39bba3d5d0..43d67f093e977 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 df11b8c2c2d6df50e6813a6281ce835f81acd1df 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 034/358] Replace if with option, add detail for each env variable completion --- .../src/completions/env_vars.rs | 51 ++++++++----------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/crates/ide-completion/src/completions/env_vars.rs b/crates/ide-completion/src/completions/env_vars.rs index 43d67f093e977..e2e0bc142c0a8 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; - } - - 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); + expanded: &ast::String, +) -> Option<()> { + guard_env_macro(expanded)?; + let range = expanded.text_range_between_quotes()?; + + CARGO_DEFINED_VARS.iter().for_each(|(var, detail)| { + let mut item = CompletionItem::new(CompletionItemKind::Keyword, range, *var); + item.detail(*detail); + item.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()?; - - if !matches!(name.text().as_str(), - "env" | "option_env") { - return None; - } - +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; + } - 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 c51b7e30d02769e113c2cf0471533a9bd5633d28 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 035/358] 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/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs index f48a570086656..894a4b0fbb343 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs +++ b/src/tools/rust-analyzer/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 5e79826bbd3d2aadc696bbe74b54fe49d3f1a00b 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 036/358] 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 e2e0bc142c0a8..3c573a5f984d3 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 c5fee22b38ba3f284be3f9c3f6b74ab407ce4ab4 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 037/358] Formatting --- .../src/completions/env_vars.rs | 21 ++++++++++++------- .../src/syntax_helpers/format_string.rs | 3 ++- .../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 3c573a5f984d3..809f6d4b7c1e6 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/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs index 894a4b0fbb343..8836167630a4d 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs index 78d6bd008afe7..5265b3d161564 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs +++ b/src/tools/rust-analyzer/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 31c6b762cf70bc30a4e38b0156afc34e418a65c6 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 038/358] 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/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs index 8836167630a4d..b3e227ddd27da 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs index 5265b3d161564..9cfcdfb77b97d 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs +++ b/src/tools/rust-analyzer/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 75b8f9e2794c4c5372111ce6959ff03d31e36a09 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 039/358] 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 809f6d4b7c1e6..5483fdf31a248 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)?; + + match name.text().as_str() { + "env" | "option_env" if makro.kind(db) == hir::MacroKind::BuiltIn => Some(()), + _ => None, } - - Some(()) } #[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 a2d2feac42d24c27cdf3f261469b116b748ebfe6 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 040/358] Pass context to env vars completion --- src/tools/rust-analyzer/crates/ide-completion/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs index c5718ec40f588..9d0044e55f598 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs +++ b/src/tools/rust-analyzer/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 4c1a07bda41f7054ba657923c169c992e759d0ef Mon Sep 17 00:00:00 2001 From: joboet Date: Thu, 6 Oct 2022 22:46:15 +0200 Subject: [PATCH 041/358] 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 0000000000000..510839d5dafbf --- /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 e2453580dc72a..724ec2d482edb 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 40e5a23812f82a04045e4f021f60de1dbc303133 Mon Sep 17 00:00:00 2001 From: joboet Date: Thu, 6 Oct 2022 22:46:47 +0200 Subject: [PATCH 042/358] 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 130e47c8d44f0..dfb8765ab4eed 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 d93cdac989eb32bfa81d3dddbb41027ccacfbb6c Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Fri, 7 Oct 2022 11:37:33 +0900 Subject: [PATCH 043/358] 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 375c13d922bc7..34c257845971e 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 3a7845096fd24..ada250df24695 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 166f229377768..8ab08e25e3092 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 3a95a0cabc3699491b80b8cdc83283e5cdc5677c 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 044/358] 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 feb3fb989a7ac..2bbfbe38a24ae 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 ff6fcc1df08e02f504b0fd5eb0dd0d316c3554e8 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 20 Sep 2022 08:56:04 +0000 Subject: [PATCH 045/358] 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 3583769b7cd64..f789978ae7e40 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 0aa44431c79b0..9be55e5b3d5fe 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -789,20 +789,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 97646003e7339..8277d1413587b 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 135d06d44adae..6c4aa35679d5b 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 3e87486f5b94b4421b35ff6c5ee23d8bd9514392 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 27 Sep 2022 11:14:14 +0000 Subject: [PATCH 046/358] 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 a7c4671665f43..b2914d234496e 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 1306f81099c2874e6c05fe0a21a785dbdb34ea10 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 27 Sep 2022 11:37:10 +0000 Subject: [PATCH 047/358] 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 b2914d234496e..aa751927ba713 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 9f19453a1a658..ff667896eb107 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 855917fb82869..ffade628e53b5 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 753d7bffe84c2..7438a58be71dc 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 0000000000000..9bea4e431224c --- /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 533a45c32a019ee003d11ac939ddbb1172707124 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 27 Sep 2022 13:02:44 +0000 Subject: [PATCH 048/358] 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 aa751927ba713..56987edd1b6e3 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 f789978ae7e40..9aeee091fdded 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 7438a58be71dc..cb7700f9bcab4 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 9bea4e431224c..b05c6310929b5 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 a237d6df43165f43b6bab5e886d82e238617e5a3 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Sat, 1 Oct 2022 11:08:58 +0000 Subject: [PATCH 049/358] 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 9aeee091fdded..e3e4a934ab563 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 9be55e5b3d5fe..32f359a815819 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -565,6 +565,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<'_> { @@ -591,18 +596,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); @@ -649,7 +659,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); @@ -679,16 +689,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( @@ -789,7 +809,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 8277d1413587b..8a9160d246640 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 7740f774ebca4..0b8157fe33dd7 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 0000000000000..09dadb0afcef5 --- /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 9b2d893fddb2255a0d3c617b4494a3ce6261b51e Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 5 Oct 2022 09:18:00 +0000 Subject: [PATCH 050/358] 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 0000000000000..bc827a8f2113a --- /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 0000000000000..146a57cbb7e05 --- /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 b4a05d5dad53effb114f70430110fc2a36b28dcb Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 5 Oct 2022 10:46:59 +0000 Subject: [PATCH 051/358] 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 076eddaabc048..3ff949fbb3f80 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 41bb7d6fe992fea2a5a86603275db6836e9103ef Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Fri, 7 Oct 2022 13:05:45 -0700 Subject: [PATCH 052/358] 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 fedb944480d9f..86a56f0ddb78c 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 fa8722b15bf08e17093eeaf4010eaac98b6e75f9 Mon Sep 17 00:00:00 2001 From: joboet Date: Sat, 8 Oct 2022 09:07:28 +0200 Subject: [PATCH 053/358] 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 510839d5dafbf..2f5356fe2276b 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 bd1571bf313e980e3ab2723981b1cfeed86d08a4 Mon Sep 17 00:00:00 2001 From: joboet Date: Sat, 8 Oct 2022 09:12:06 +0200 Subject: [PATCH 054/358] 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 724ec2d482edb..35f1e68a87e5b 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 903e3a0a5ba286660230e61f1dea85e7e0a1b49a Mon Sep 17 00:00:00 2001 From: woppopo Date: Sat, 8 Oct 2022 11:48:53 +0000 Subject: [PATCH 055/358] 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 9df626a8326ce..d20241d838001 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 6ec9d5facab3d8a2b8228bca126e4fe45fcd866e Mon Sep 17 00:00:00 2001 From: joboet Date: Sat, 8 Oct 2022 20:19:21 +0200 Subject: [PATCH 056/358] 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 | 196 +++++++++++------- .../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, 202 insertions(+), 117 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 b21784475f0d2..c7a57d3a3d47e 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 b17521f701daf..b37bf99969887 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 2c5b94b1e61e5..2b2d079ee4d01 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 c31b61cbf56d3..b6e5e4cd2e197 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 c61a7e7d1e4ab..732e227d7e277 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 { @@ -955,6 +963,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; @@ -1114,6 +1123,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 ec670238e6f0e..17628b7579b8d 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,60 +27,109 @@ 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), + } } - 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 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(key: Key) -> *mut u8 { - c::TlsGetValue(key) as *mut u8 -} + #[inline] + pub unsafe fn get(&'static self) -> *mut u8 { + c::TlsGetValue(self.key()).cast() + } -#[inline] -pub unsafe fn destroy(_key: Key) { - rtabort!("can't destroy tls keys on windows") -} + #[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); -#[inline] -pub fn requires_synchronized_create() -> bool { - 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 + } + } + } + } } +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()); - -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() })); +static DTORS: AtomicPtr = AtomicPtr::new(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); + let mut any_run = false; + + // Use acquire ordering to observe key initialization. + let mut cur = DTORS.load(Acquire); while !cur.is_null() { - let ptr = c::TlsGetValue((*cur).key); + 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 0000000000000..c95f383fb90e3 --- /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 e4dd0253668b8..8c19f9332dc56 100644 --- a/library/std/src/sys_common/mod.rs +++ b/library/std/src/sys_common/mod.rs @@ -34,10 +34,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 032bf604d7388..747579f178127 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 a59d725ca27012d64607abbae0fe043a81b50750 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 057/358] Bump once_cell --- Cargo.lock | 7 +++++++ library/stdarch | 2 +- src/tools/cargo | 2 +- src/tools/rust-analyzer/crates/hir-def/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/hir-ty/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/hir/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/ide-completion/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/ide-db/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/profile/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/syntax/Cargo.toml | 2 +- 10 files changed, 16 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0cc7f8a1c7ce3..c724c80f98ebd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1371,10 +1371,17 @@ dependencies = [ ] [[package]] +<<<<<<< HEAD name = "fs-err" version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64db3e262960f0662f43a6366788d5f10f7f244b8f7d7d987f560baf5ded5c50" +======= +name = "once_cell" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" +>>>>>>> Bump once_cell [[package]] name = "fs_extra" diff --git a/library/stdarch b/library/stdarch index 699c093a42283..790411f93c4b5 160000 --- a/library/stdarch +++ b/library/stdarch @@ -1 +1 @@ -Subproject commit 699c093a42283c07e9763b4c19439a900ae2d321 +Subproject commit 790411f93c4b5eada3c23abb4c9a063fb0b24d99 diff --git a/src/tools/cargo b/src/tools/cargo index b8f30cb23c4e5..3ff044334f056 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit b8f30cb23c4e5f20854a4f683325782b7cff9837 +Subproject commit 3ff044334f0567ce1481c78603aeee7211b91623 diff --git a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml index e8cff2f3e6cd7..54b0832ca6ada 100644 --- a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml index 7f143f396c765..d8ede9956fc7d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir/Cargo.toml b/src/tools/rust-analyzer/crates/hir/Cargo.toml index 8e6a2441b3311..0c3eed57af038 100644 --- a/src/tools/rust-analyzer/crates/hir/Cargo.toml +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml b/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml index 8c9d6b228648e..550f65375a832 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-db/Cargo.toml b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml index 30272bc16f636..3dca3e2022284 100644 --- a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/profile/Cargo.toml b/src/tools/rust-analyzer/crates/profile/Cargo.toml index 0b78a45a24b33..ed3d816da5238 100644 --- a/src/tools/rust-analyzer/crates/profile/Cargo.toml +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/syntax/Cargo.toml b/src/tools/rust-analyzer/crates/syntax/Cargo.toml index 0e2dec386ff77..8005b20206de0 100644 --- a/src/tools/rust-analyzer/crates/syntax/Cargo.toml +++ b/src/tools/rust-analyzer/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 4cbfc2f6a31685553a6e4a88ee5737d9510098ac Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 8 Oct 2022 23:18:11 +0100 Subject: [PATCH 058/358] 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! --- src/tools/rust-analyzer/editors/code/package.json | 9 +++++++++ src/tools/rust-analyzer/editors/code/src/commands.ts | 4 ++++ src/tools/rust-analyzer/editors/code/src/ctx.ts | 4 ++++ src/tools/rust-analyzer/editors/code/src/main.ts | 1 + src/tools/rust-analyzer/editors/code/src/run.ts | 2 ++ src/tools/rust-analyzer/editors/code/src/tasks.ts | 2 +- 6 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index f1dd3aa79ff04..ccf62d002bb5f 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/editors/code/src/commands.ts b/src/tools/rust-analyzer/editors/code/src/commands.ts index b9ad525e361f0..6b10073aa8690 100644 --- a/src/tools/rust-analyzer/editors/code/src/commands.ts +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/editors/code/src/ctx.ts b/src/tools/rust-analyzer/editors/code/src/ctx.ts index 26510011d4397..b6c0eedfb16aa 100644 --- a/src/tools/rust-analyzer/editors/code/src/ctx.ts +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/editors/code/src/main.ts b/src/tools/rust-analyzer/editors/code/src/main.ts index 41bde4195e07d..6e6c2513dd907 100644 --- a/src/tools/rust-analyzer/editors/code/src/main.ts +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/editors/code/src/run.ts b/src/tools/rust-analyzer/editors/code/src/run.ts index 22e5eda6827d1..100c0fe2d8c3d 100644 --- a/src/tools/rust-analyzer/editors/code/src/run.ts +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/editors/code/src/tasks.ts b/src/tools/rust-analyzer/editors/code/src/tasks.ts index e6239deeb21af..44697f95baba1 100644 --- a/src/tools/rust-analyzer/editors/code/src/tasks.ts +++ b/src/tools/rust-analyzer/editors/code/src/tasks.ts @@ -128,7 +128,7 @@ export async function buildCargoTask( name, TASK_SOURCE, exec, - ["$rustc"] + ["$rustc-run"] ); } From d68407a1baf0bfaec79864d662e53428574baa7a Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sun, 9 Oct 2022 12:46:23 +0400 Subject: [PATCH 059/358] 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 9396d769dc702..8909cf33af911 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 de1c97daf3a7a667069b0d8c74a1087547aa4e83 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 060/358] 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 7df4add8ce112..42ac6fcd8bf36 100644 --- a/library/std/src/sys/unix/thread.rs +++ b/library/std/src/sys/unix/thread.rs @@ -766,6 +766,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 2126365af416cc02b40fba85f939984923851340 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 9 Oct 2022 15:31:27 +0100 Subject: [PATCH 061/358] 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 bf045c7841f63..427dcf6bb0699 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 0a8f48ac3cae05d4f45d8c633292794485af2ee6 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sun, 9 Oct 2022 15:18:08 -0400 Subject: [PATCH 062/358] Add a regression test for #39137 --- src/test/ui/deriving/deriving-hash.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/test/ui/deriving/deriving-hash.rs b/src/test/ui/deriving/deriving-hash.rs index 8b51370bca502..16738ec4ae4ec 100644 --- a/src/test/ui/deriving/deriving-hash.rs +++ b/src/test/ui/deriving/deriving-hash.rs @@ -44,6 +44,17 @@ fn fake_hash(v: &mut Vec, a: A) { a.hash(&mut FakeHasher(v)); } +struct OnlyOneByteHasher; +impl Hasher for OnlyOneByteHasher { + fn finish(&self) -> u64 { + unreachable!() + } + + fn write(&mut self, bytes: &[u8]) { + assert_eq!(bytes.len(), 1); + } +} + fn main() { let person1 = Person { id: 5, @@ -73,4 +84,13 @@ fn main() { let mut v = vec![]; fake_hash(&mut v, SingleVariantEnum::A(17)); assert_eq!(vec![17], v); + + // issue #39137 + #[repr(u8)] + #[derive(Hash)] + enum E { + A, + B, + } + E::A.hash(&mut OnlyOneByteHasher); } From 0a16dc97caf6906a950922841a7637f8806e44b4 Mon Sep 17 00:00:00 2001 From: Pointerbender Date: Sun, 9 Oct 2022 22:32:23 +0200 Subject: [PATCH 063/358] 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 610b65499c286..b0ef887befe73 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -1816,35 +1816,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 2337efd645827c9aeccbc4420cb7005c122e6e18 Mon Sep 17 00:00:00 2001 From: DropDemBits Date: Sun, 9 Oct 2022 17:58:55 -0400 Subject: [PATCH 064/358] 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index 8d5cab283d061..970e948dfd930 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -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/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs index eadebbe8a212b..7d632352828ef 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs +++ b/src/tools/rust-analyzer/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 17feb75ff95fbbe146666e642391619288e79dc3 Mon Sep 17 00:00:00 2001 From: DropDemBits Date: Sun, 9 Oct 2022 18:12:08 -0400 Subject: [PATCH 065/358] 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs index 68287a20bf806..307cea3d0a4f8 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs @@ -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/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index 4ab6e2627fa75..38396cd7d7baf 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs index 7d632352828ef..173c064ccc971 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs +++ b/src/tools/rust-analyzer/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 d5ae8a08e160dfa9da477b746ef87c4a0bc6cf1b Mon Sep 17 00:00:00 2001 From: DropDemBits Date: Sun, 9 Oct 2022 20:45:20 -0400 Subject: [PATCH 066/358] 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/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs index 173c064ccc971..229e7419b736f 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs index 83f8bbac5880b..c9a21e12c085b 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs +++ b/src/tools/rust-analyzer/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 121b8dbae8f31999b4b4a393fce99c61d98625b5 Mon Sep 17 00:00:00 2001 From: DropDemBits Date: Sun, 9 Oct 2022 21:28:21 -0400 Subject: [PATCH 067/358] Underline only the intra-doc link instead of the whole doc comment --- .../rust-analyzer/crates/ide/src/goto_definition.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index f86ea61d1586f..d0be1b3f40479 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/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 e37dc186fe8e655fd27f6f52c745752c68aaa279 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 10 Oct 2022 09:47:09 +0200 Subject: [PATCH 068/358] Honor cfg attributes on params when lowering their patterns --- .../rust-analyzer/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/src/tools/rust-analyzer/crates/hir-def/src/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/body.rs index 2dc7714bbb540..759f3b8c04b6c 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs index c4f91e49a6e1b..ccc01c3efca51 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs index eb04bf87783b4..74de33117ee7d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index a5e854b74df9d..f0563e290db41 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -1,4 +1,4 @@ -//! See `CompletionContext` structure. +//! See [`CompletionContext`] structure. mod analysis; #[cfg(test)] From 9ef9a0d16435d862db8136e5c614c97d2a60ec18 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 1 Oct 2022 22:51:09 +0200 Subject: [PATCH 069/358] 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/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs index 4f626105a53d6..c04cd1651926d 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs index 8befa7f7da727..7b19518e25a84 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs @@ -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 9526515f0edd6f7421449005981ca918f95acc23 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 10 Oct 2022 14:25:14 +0200 Subject: [PATCH 070/358] 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. --- .../rust-analyzer/crates/mbe/src/benchmark.rs | 30 +++++----- .../rust-analyzer/crates/mbe/src/expander.rs | 3 +- .../crates/mbe/src/expander/matcher.rs | 54 +++++++++++------- .../crates/mbe/src/expander/transcriber.rs | 57 +++++++++++++++++-- src/tools/rust-analyzer/crates/mbe/src/lib.rs | 6 +- .../rust-analyzer/crates/mbe/src/parser.rs | 41 ++++++++++++- 6 files changed, 146 insertions(+), 45 deletions(-) diff --git a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs index ac691578d8873..9c92bae6a1962 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/mbe/src/expander.rs b/src/tools/rust-analyzer/crates/mbe/src/expander.rs index 1e1bfa5505583..100ec6bfb93ac 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/expander.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs index 139a8cb8cbe58..3f656df25f7d4 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs b/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs index 7bcc84740f186..cbb59ab8e67b5 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/mbe/src/lib.rs b/src/tools/rust-analyzer/crates/mbe/src/lib.rs index 79da84f4a0226..c4f0fa20d6de0 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/lib.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/mbe/src/parser.rs b/src/tools/rust-analyzer/crates/mbe/src/parser.rs index acb4be5846de1..351c359b73c87 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/parser.rs +++ b/src/tools/rust-analyzer/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 291b557e7b0b9ef6c9a343c648dc20efb810c10a Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 3 Oct 2022 15:34:00 +0100 Subject: [PATCH 071/358] 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 83040a652b18a..406a56cd4ae3e 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 a10c8460b55c803818b3e3dbe96ad0cc47158722 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 3 Oct 2022 14:02:49 +0100 Subject: [PATCH 072/358] 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 1f577e9f3524f..a292bfce31eb8 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::{ @@ -1740,7 +1743,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 b32fc3c719bbd..f14b8ee3254f3 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 31e410aaaf082..a267bf6c0b4d1 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 66fbb8f1213e0..cd6413bc3ec62 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 1680c6accd78c..4cc7be47fc2c6 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 4f407badb3f9e..c2ec2526a4aea 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 14a38a760d238..2e651b5387419 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 a866e6cd87d8218c88e9e4f012e60c6fd9af88d5 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 3 Oct 2022 14:09:05 +0100 Subject: [PATCH 073/358] 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 63ff64b00bed6..c6c85ffa84dd7 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 035f0ce1cbc42..ba2ed24fc08fc 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 a267bf6c0b4d1..2a6dd6da415cf 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 85b877652c6aa..500900d3d4a74 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 7f54918f73614..201a3c7100cc8 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 880f3fbd00e60..97d012fb611d0 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 4166816b5e3c7..f98cc66e9e93e 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 9a2588513f06d..ef17dbd0426fe 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 8602a4cf5aef2..debdf9dbf4403 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 9a5100ce17f53..462f5e7849849 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 f5f92ac1e7fbe..ac820a79db274 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 1d6529d586d40f97e236602f7fef0cc60c91ba05 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 3 Oct 2022 14:14:51 +0100 Subject: [PATCH 074/358] 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 a2d507328b389..ab2dafd828a56 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -277,6 +277,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), @@ -304,8 +316,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. /// @@ -324,6 +348,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); } @@ -332,6 +357,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)) } @@ -367,6 +393,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 2a6dd6da415cf..3e0840caaa693 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 c8ccdc539af5a..b16c54e0aacaa 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 c2ec2526a4aea..a7737b467b75b 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 db56377acebf7423b937c0e1093c22160306f09f Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 3 Oct 2022 14:24:17 +0100 Subject: [PATCH 075/358] 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 406a56cd4ae3e..84c57b3f64e18 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 9e88dc7a913a2..df4e309086fd2 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 1dc71abc104f9..460af07a55967 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 167198b47f20f..7a42d618707ad 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 034ba1ccd10654c1f89d904c1dc78e967a1416c7 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 3 Oct 2022 14:26:29 +0100 Subject: [PATCH 076/358] 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 b914ba52a7353..870e824039cb6 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 debdf9dbf4403..1e74e0e299099 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 8f6da73d1f2d2..f47760e9ae6c8 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 e8155a8422929545f89f92a7882e5401223ae786 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 3 Oct 2022 14:28:02 +0100 Subject: [PATCH 077/358] 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 df4e309086fd2..017bf2365d62b 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 ef17dbd0426fe..3d4c3ab9fd7c9 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 162699c286872..5e1cd842b9bbb 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 460af07a55967..e873c36e0b39a 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 82da0d98b745c8034d6cc26742741ab1f5cc3704 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 3 Oct 2022 16:10:34 +0100 Subject: [PATCH 078/358] 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 | 75 ++++++++----------- .../rustc_macros/src/diagnostics/utils.rs | 60 +-------------- 3 files changed, 38 insertions(+), 102 deletions(-) diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index 84c57b3f64e18..8cf307df5a565 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 017bf2365d62b..dcbe89251cb36 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_attrs_code(&mut self, binding_info: &BindingInfo<'_>) -> TokenStream { + 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; - 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 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; - 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 5e1cd842b9bbb..4fd4adc511267 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 b51d78d1e0e6b37796646f3ff408d112564079da Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 10 Oct 2022 15:41:32 +0200 Subject: [PATCH 079/358] Don't report build-scripts and proc-macros are metadata progress --- .../rust-analyzer/crates/project-model/src/build_scripts.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs b/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs index 32db42f1db75e..d9f09c0349566 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs +++ b/src/tools/rust-analyzer/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 2d12bb5aa936921764c1cc4a3183c1e568a9b716 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 10 Oct 2022 15:45:24 +0200 Subject: [PATCH 080/358] Refactor completions expansion --- .../crates/ide-completion/src/context.rs | 43 +- .../ide-completion/src/context/analysis.rs | 1939 +++++++++-------- 2 files changed, 1011 insertions(+), 971 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index f0563e290db41..9850813a0ce1c 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 01dd9a234f553..04111ec7efaa6 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -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; - - '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), +struct ExpansionResult { + original_file: SyntaxNode, + speculative_file: SyntaxNode, + offset: TextSize, + fake_ident_token: SyntaxToken, + derive_ctx: Option<(SyntaxNode, SyntaxNode, TextSize, ast::Attr)>, +} + +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; + ) { + // 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; } - // exactly one expansion failed, inconsistent state so stop expanding completely - _ => 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, - }; + // 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()), + // 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(), + ), ) { - 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 + 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, + }; - // 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), + match ( + sema.expand(&actual_macro_call), + sema.speculative_expand( + &actual_macro_call, + &speculative_args, + fake_ident_token.clone(), + ), ) { - 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; + // 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; } + // 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; } - self.analyze(&original_file, speculative_file, offset, derive_ctx) + // none of our states have changed so stop the loop + break 'expansion; } + ExpansionResult { original_file, speculative_file, offset, fake_ident_token, derive_ctx } +} - /// 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), - }; +/// 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; + } - 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 - } + // 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 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) + 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, + }; + + (ty, name) + }, + 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); + + 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![..]) { - 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, - } + 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 { - return None; + 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), + )) } - }; - return Some(analysis); + })().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), + } + }, } }; - (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; } - 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 + }, + 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 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()); + Some(LifetimeContext { lifetime, kind }) +} - 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()); + } - 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()); - } + 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 }) +} - 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); } - - 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( + 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); + }); + return Some(make_res(kind)); + } + + let segment = match_ast! { + match parent { + ast::PathSegment(segment) => segment, + ast::FieldExpr(field) => { + let receiver = find_opt_node_in_file(original_file, field.expr()); + let receiver_is_ambiguous_float_literal = match &receiver { + Some(ast::Expr::Literal(l)) => matches! { + l.kind(), + ast::LiteralKind::FloatNumber { .. } if l.syntax().last_token().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, } - 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)); + }; + + 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 segment = match_ast! { + 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))); - } - } - } - - 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::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 }, - 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, + _ => 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(); + // 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))); + } + } + } - // 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.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))); + } - 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(); + 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, + } + }; - let super_chain_len = - if segment_count > super_count { None } else { Some(super_count) }; + path_ctx.kind = kind; + path_ctx.has_type_args = segment.generic_arg_list().is_some(); - Qualified::With { path: qualifier, resolution: res, super_chain_len } + // 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, }; - } - } 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())) + 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 } } - _ => 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); + } + } 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; - } + 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 def0e0bde419e9a4017ca7fa2df718989c4f495d Mon Sep 17 00:00:00 2001 From: Urgau Date: Mon, 10 Oct 2022 15:45:05 +0200 Subject: [PATCH 081/358] 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 48c40eae662ed..5ea433e6b3d3e 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 79e237328147beff7f7d913319a5e9a9c740cd70 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Mon, 10 Oct 2022 14:47:22 +0100 Subject: [PATCH 082/358] 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 9875c389d8aaf..9d758320cfcc6 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 329f9433dba0e..b3626c564e86a 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 427dcf6bb0699..4cc0e4ed5a45a 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 d5a044805e4f56f5beb5af4ed5f0f9d48a32182d Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Tue, 27 Sep 2022 11:50:47 -0700 Subject: [PATCH 083/358] 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 683a07a34dcf9..c8c47763a340b 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 cab2887dfcf14..b340061cb3d10 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 9c9d48980b2cfa0d8ce639829df4dc9791d1177d Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Thu, 29 Sep 2022 11:17:15 -0700 Subject: [PATCH 084/358] 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 b340061cb3d10..cffe1e32308b6 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 ed9b2dfdccd0d0b5c550bb1e3a8ac86f9dfc99b7 Mon Sep 17 00:00:00 2001 From: dvdsk Date: Mon, 10 Oct 2022 19:00:47 +0200 Subject: [PATCH 085/358] 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. --- src/tools/rust-analyzer/docs/user/manual.adoc | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/docs/user/manual.adoc b/src/tools/rust-analyzer/docs/user/manual.adoc index 9bd3b6a692b1a..c30838e5f5e1e 100644 --- a/src/tools/rust-analyzer/docs/user/manual.adoc +++ b/src/tools/rust-analyzer/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 f410668fc2b69e3a9601c164819b9de6298ac011 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 10 Oct 2022 22:47:52 +0200 Subject: [PATCH 086/358] Prefer similar tokens when expanding macros speculatively --- src/tools/rust-analyzer/crates/hir-expand/src/db.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs index bc97ee15c7d30..87e4db03984ab 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ b/src/tools/rust-analyzer/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 db11540cec4cf40aca452ceefcb8b4e95acaa893 Mon Sep 17 00:00:00 2001 From: zyctree Date: Tue, 11 Oct 2022 14:05:04 +0800 Subject: [PATCH 087/358] fix link in syntax.md --- src/tools/rust-analyzer/docs/dev/syntax.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/docs/dev/syntax.md b/src/tools/rust-analyzer/docs/dev/syntax.md index 30e13701383a9..64c872b251766 100644 --- a/src/tools/rust-analyzer/docs/dev/syntax.md +++ b/src/tools/rust-analyzer/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 545214a56b53d7d158a81c6acb6f719da87ac5bd Mon Sep 17 00:00:00 2001 From: woppopo Date: Tue, 11 Oct 2022 06:40:37 +0000 Subject: [PATCH 088/358] 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 26a410365568c..6dcf23dde87c7 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 ac8533db04d28..a698e6e99e10c 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 75f4c05f83777b9238261262785effcf2a5926dc Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 4 Oct 2022 16:56:56 +0200 Subject: [PATCH 089/358] Migrate highlight style to CSS variables --- src/librustdoc/html/static/css/rustdoc.css | 52 +++++++++++++++++-- src/librustdoc/html/static/css/themes/ayu.css | 34 +++++------- .../html/static/css/themes/dark.css | 29 +++++------ .../html/static/css/themes/light.css | 28 +++++----- 4 files changed, 89 insertions(+), 54 deletions(-) diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index f5b0d15d733aa..f8bf6850b52da 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -1095,8 +1095,55 @@ so that we can apply CSS-filters to change the arrow color in themes */ color: var(--right-side-color); } +pre.rust { + tab-size: 4; + -moz-tab-size: 4; +} + +/* Code highlighting */ +pre.rust .kw { + color: var(--code-highlight-kw-color); +} +pre.rust .kw-2 { + color: var(--code-highlight-kw-2-color); +} +pre.rust .lifetime { + color: var(--code-highlight-lifetime-color); +} +pre.rust .prelude-ty { + color: var(--code-highlight-prelude-color); +} +pre.rust .prelude-val { + color: var(--code-highlight-prelude-val-color); +} +pre.rust .string { + color: var(--code-highlight-string-color); +} +pre.rust .number { + color: var(--code-highlight-number-color); +} +pre.rust .bool-val { + color: var(--code-highlight-literal-color); +} +pre.rust .self { + color: var(--code-highlight-self-color); +} +pre.rust .attribute { + color: var(--code-highlight-attribute-color); +} +pre.rust .macro, +pre.rust .macro-nonterminal { + color: var(--code-highlight-macro-color); +} pre.rust .question-mark { font-weight: bold; + color: var(--code-highlight-question-mark-color); +} +pre.rust .comment { + color: var(--code-highlight-comment-color); +} +pre.rust .doccomment { + color: var(--code-highlight-doc-comment-color); } .example-wrap.compile_fail, @@ -1302,11 +1349,6 @@ h3.variant { font-size: 1.25rem; } -pre.rust { - tab-size: 4; - -moz-tab-size: 4; -} - .search-failed { text-align: center; margin-top: 20px; diff --git a/src/librustdoc/html/static/css/themes/ayu.css b/src/librustdoc/html/static/css/themes/ayu.css index fc7713b98857b..ee74f81926ac6 100644 --- a/src/librustdoc/html/static/css/themes/ayu.css +++ b/src/librustdoc/html/static/css/themes/ayu.css @@ -41,6 +41,20 @@ Original by Dempfi (https://github.com/dempfi/ayu) --stab-background-color: #314559; --stab-code-color: #e6e1cf; --search-color: #fff; + --code-highlight-kw-color: #ff7733; + --code-highlight-kw-2-color: #ff7733; + --code-highlight-lifetime-color: #ff7733; + --code-highlight-prelude-color: #69f2df; + --code-highlight-prelude-val-color: #ff7733; + --code-highlight-number-color: #b8cc52; + --code-highlight-string-color: #b8cc52; + --code-highlight-literal-color: #ff7733; + --code-highlight-attribute-color: #e6e1cf; + --code-highlight-self-color: #36a3d9; + --code-highlight-macro-color: #a37acc; + --code-highlight-question-mark-color: #ff9011; + --code-highlight-comment-color: #788797; + --code-highlight-doc-comment-color: #a1ac88; } .slider { @@ -124,9 +138,6 @@ pre, .rustdoc.source .example-wrap { .content .item-info::before { color: #ccc; } -pre.rust .comment { color: #788797; } -pre.rust .doccomment { color: #a1ac88; } - .sidebar h2 a, .sidebar h3 a { color: white; @@ -161,23 +172,6 @@ details.rustdoc-toggle > summary::before { .src-line-numbers :target { background-color: transparent; } -/* Code highlighting */ -pre.rust .number, pre.rust .string { color: #b8cc52; } -pre.rust .kw, pre.rust .kw-2, pre.rust .prelude-ty, -pre.rust .bool-val, pre.rust .prelude-val, -pre.rust .lifetime { color: #ff7733; } -pre.rust .macro, pre.rust .macro-nonterminal { color: #a37acc; } -pre.rust .question-mark { - color: #ff9011; -} -pre.rust .self { - color: #36a3d9; - font-style: italic; -} -pre.rust .attribute { - color: #e6e1cf; -} - pre.example-line-numbers { color: #5c67736e; border: none; diff --git a/src/librustdoc/html/static/css/themes/dark.css b/src/librustdoc/html/static/css/themes/dark.css index ee2a9ec8a0bee..06baceca01d3b 100644 --- a/src/librustdoc/html/static/css/themes/dark.css +++ b/src/librustdoc/html/static/css/themes/dark.css @@ -36,6 +36,20 @@ --stab-background-color: #314559; --stab-code-color: #e6e1cf; --search-color: #111; + --code-highlight-kw-color: #ab8ac1; + --code-highlight-kw-2-color: #769acb; + --code-highlight-lifetime-color: #d97f26; + --code-highlight-prelude-color: #769acb; + --code-highlight-prelude-val-color: #ee6868; + --code-highlight-number-color: #83a300; + --code-highlight-string-color: #83a300; + --code-highlight-literal-color: #ee6868; + --code-highlight-attribute-color: #ee6868; + --code-highlight-self-color: #ee6868; + --code-highlight-macro-color: #3e999f; + --code-highlight-question-mark-color: #ff9011; + --code-highlight-comment-color: #8d8d8b; + --code-highlight-doc-comment-color: #8ca375; } .slider { @@ -62,9 +76,6 @@ input:focus + .slider { .content .item-info::before { color: #ccc; } -pre.rust .comment { color: #8d8d8b; } -pre.rust .doccomment { color: #8ca375; } - body.source .example-wrap pre.rust a { background: #333; } @@ -86,18 +97,6 @@ details.rustdoc-toggle > summary::before { .src-line-numbers :target { background-color: transparent; } -/* Code highlighting */ -pre.rust .kw { color: #ab8ac1; } -pre.rust .kw-2, pre.rust .prelude-ty { color: #769acb; } -pre.rust .number, pre.rust .string { color: #83a300; } -pre.rust .self, pre.rust .bool-val, pre.rust .prelude-val, -pre.rust .attribute { color: #ee6868; } -pre.rust .macro, pre.rust .macro-nonterminal { color: #3E999F; } -pre.rust .lifetime { color: #d97f26; } -pre.rust .question-mark { - color: #ff9011; -} - pre.example-line-numbers { border-color: #4a4949; } diff --git a/src/librustdoc/html/static/css/themes/light.css b/src/librustdoc/html/static/css/themes/light.css index 7287d5b62123b..058974c078c8d 100644 --- a/src/librustdoc/html/static/css/themes/light.css +++ b/src/librustdoc/html/static/css/themes/light.css @@ -36,6 +36,20 @@ --stab-background-color: #fff5d6; --stab-code-color: #000; --search-color: #000; + --code-highlight-kw-color: #8959a8; + --code-highlight-kw-2-color: #4271ae; + --code-highlight-lifetime-color: #b76514; + --code-highlight-prelude-color: #4271ae; + --code-highlight-prelude-val-color: #c82829; + --code-highlight-number-color: #718c00; + --code-highlight-string-color: #718c00; + --code-highlight-literal-color: #c82829; + --code-highlight-attribute-color: #c82829; + --code-highlight-self-color: #c82829; + --code-highlight-macro-color: #3e999f; + --code-highlight-question-mark-color: #ff9011; + --code-highlight-comment-color: #8e908c; + --code-highlight-doc-comment-color: #4d4d4c; } .slider { @@ -78,20 +92,6 @@ body.source .example-wrap pre.rust a { .src-line-numbers :target { background-color: transparent; } -/* Code highlighting */ -pre.rust .kw { color: #8959A8; } -pre.rust .kw-2, pre.rust .prelude-ty { color: #4271AE; } -pre.rust .number, pre.rust .string { color: #718C00; } -pre.rust .self, pre.rust .bool-val, pre.rust .prelude-val, -pre.rust .attribute { color: #C82829; } -pre.rust .comment { color: #8E908C; } -pre.rust .doccomment { color: #4D4D4C; } -pre.rust .macro, pre.rust .macro-nonterminal { color: #3E999F; } -pre.rust .lifetime { color: #B76514; } -pre.rust .question-mark { - color: #ff9011; -} - pre.example-line-numbers { border-color: #c7c7c7; } From 46d289e6ba9bcfb99fdd163a5a89ba359b6b0467 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 4 Oct 2022 17:48:23 +0200 Subject: [PATCH 090/358] Add GUI test for source code pages highlighting --- src/test/rustdoc-gui/highlight-colors.goml | 57 ++++++++++++++++++++++ src/test/rustdoc-gui/src/test_docs/lib.rs | 17 ++++++- 2 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 src/test/rustdoc-gui/highlight-colors.goml diff --git a/src/test/rustdoc-gui/highlight-colors.goml b/src/test/rustdoc-gui/highlight-colors.goml new file mode 100644 index 0000000000000..dd01dbf6148d2 --- /dev/null +++ b/src/test/rustdoc-gui/highlight-colors.goml @@ -0,0 +1,57 @@ +// This test checks the highlight colors in the source code pages. +goto: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html" +show-text: true + +local-storage: {"rustdoc-theme": "ayu", "rustdoc-use-system-theme": "false"} +reload: + +assert-css: ("pre.rust .kw", {"color": "rgb(255, 119, 51)"}, ALL) +assert-css: ("pre.rust .kw-2", {"color": "rgb(255, 119, 51)"}, ALL) +assert-css: ("pre.rust .prelude-ty", {"color": "rgb(105, 242, 223)"}, ALL) +assert-css: ("pre.rust .prelude-val", {"color": "rgb(255, 119, 51)"}, ALL) +assert-css: ("pre.rust .lifetime", {"color": "rgb(255, 119, 51)"}, ALL) +assert-css: ("pre.rust .number", {"color": "rgb(184, 204, 82)"}, ALL) +assert-css: ("pre.rust .string", {"color": "rgb(184, 204, 82)"}, ALL) +assert-css: ("pre.rust .bool-val", {"color": "rgb(255, 119, 51)"}, ALL) +assert-css: ("pre.rust .self", {"color": "rgb(54, 163, 217)"}, ALL) +assert-css: ("pre.rust .attribute", {"color": "rgb(230, 225, 207)"}, ALL) +assert-css: ("pre.rust .macro", {"color": "rgb(163, 122, 204)"}, ALL) +assert-css: ("pre.rust .question-mark", {"color": "rgb(255, 144, 17)"}, ALL) +assert-css: ("pre.rust .comment", {"color": "rgb(120, 135, 151)"}, ALL) +assert-css: ("pre.rust .doccomment", {"color": "rgb(161, 172, 136)"}, ALL) + +local-storage: {"rustdoc-theme": "dark"} +reload: + +assert-css: ("pre.rust .kw", {"color": "rgb(171, 138, 193)"}, ALL) +assert-css: ("pre.rust .kw-2", {"color": "rgb(118, 154, 203)"}, ALL) +assert-css: ("pre.rust .prelude-ty", {"color": "rgb(118, 154, 203)"}, ALL) +assert-css: ("pre.rust .prelude-val", {"color": "rgb(238, 104, 104)"}, ALL) +assert-css: ("pre.rust .lifetime", {"color": "rgb(217, 127, 38)"}, ALL) +assert-css: ("pre.rust .number", {"color": "rgb(131, 163, 0)"}, ALL) +assert-css: ("pre.rust .string", {"color": "rgb(131, 163, 0)"}, ALL) +assert-css: ("pre.rust .bool-val", {"color": "rgb(238, 104, 104)"}, ALL) +assert-css: ("pre.rust .self", {"color": "rgb(238, 104, 104)"}, ALL) +assert-css: ("pre.rust .attribute", {"color": "rgb(238, 104, 104)"}, ALL) +assert-css: ("pre.rust .macro", {"color": "rgb(62, 153, 159)"}, ALL) +assert-css: ("pre.rust .question-mark", {"color": "rgb(255, 144, 17)"}, ALL) +assert-css: ("pre.rust .comment", {"color": "rgb(141, 141, 139)"}, ALL) +assert-css: ("pre.rust .doccomment", {"color": "rgb(140, 163, 117)"}, ALL) + +local-storage: {"rustdoc-theme": "light"} +reload: + +assert-css: ("pre.rust .kw", {"color": "rgb(137, 89, 168)"}, ALL) +assert-css: ("pre.rust .kw-2", {"color": "rgb(66, 113, 174)"}, ALL) +assert-css: ("pre.rust .prelude-ty", {"color": "rgb(66, 113, 174)"}, ALL) +assert-css: ("pre.rust .prelude-val", {"color": "rgb(200, 40, 41)"}, ALL) +assert-css: ("pre.rust .lifetime", {"color": "rgb(183, 101, 20)"}, ALL) +assert-css: ("pre.rust .number", {"color": "rgb(113, 140, 0)"}, ALL) +assert-css: ("pre.rust .string", {"color": "rgb(113, 140, 0)"}, ALL) +assert-css: ("pre.rust .bool-val", {"color": "rgb(200, 40, 41)"}, ALL) +assert-css: ("pre.rust .self", {"color": "rgb(200, 40, 41)"}, ALL) +assert-css: ("pre.rust .attribute", {"color": "rgb(200, 40, 41)"}, ALL) +assert-css: ("pre.rust .macro", {"color": "rgb(62, 153, 159)"}, ALL) +assert-css: ("pre.rust .question-mark", {"color": "rgb(255, 144, 17)"}, ALL) +assert-css: ("pre.rust .comment", {"color": "rgb(142, 144, 140)"}, ALL) +assert-css: ("pre.rust .doccomment", {"color": "rgb(77, 77, 76)"}, ALL) diff --git a/src/test/rustdoc-gui/src/test_docs/lib.rs b/src/test/rustdoc-gui/src/test_docs/lib.rs index 33c74e3a331cd..cc0efe7231a43 100644 --- a/src/test/rustdoc-gui/src/test_docs/lib.rs +++ b/src/test/rustdoc-gui/src/test_docs/lib.rs @@ -363,9 +363,24 @@ pub trait TraitWithNoDocblocks { pub struct TypeWithNoDocblocks; +impl TypeWithNoDocblocks { + fn x() -> Option { + Some(Self) + } + fn y() -> Option { + // code comment + let t = Self::x()?; + Some(0) + } +} + impl TypeWithNoDocblocks { pub fn first_fn(&self) {} - pub fn second_fn(&self) {} + pub fn second_fn<'a>(&'a self) { + let x = 12; + let y = "a"; + let z = false; + } } pub unsafe fn unsafe_fn() {} From d53d3d0065d4c4eedc3d083c55fe2a0994c05245 Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Mon, 10 Oct 2022 19:32:24 +0900 Subject: [PATCH 091/358] 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/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 7f0baf49dadce..a6602747ef799 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs index 240942e488d7d..8a8ff08cfe8cd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs +++ b/src/tools/rust-analyzer/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 48466a8f0b81817d029cedca6148d7ce9ab265b6 Mon Sep 17 00:00:00 2001 From: zyctree Date: Tue, 11 Oct 2022 19:37:49 +0800 Subject: [PATCH 092/358] 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 --- src/tools/rust-analyzer/docs/dev/syntax.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/docs/dev/syntax.md b/src/tools/rust-analyzer/docs/dev/syntax.md index 64c872b251766..767233351b40a 100644 --- a/src/tools/rust-analyzer/docs/dev/syntax.md +++ b/src/tools/rust-analyzer/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 6843b884cbaee296b52e66ecb91fdd16f4f015fc 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 093/358] 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 5483fdf31a248..a3a971303948e 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 dba587baaa54ce1ff6e521bcecafe11f428c98f4 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 094/358] 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 a3a971303948e..14dc17321f9ff 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/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs index b3e227ddd27da..caa579e322b3c 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs index 9cfcdfb77b97d..39710b8f13eb5 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs +++ b/src/tools/rust-analyzer/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 5a33680a20df041aa4ce34f4650571256be00115 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 095/358] 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 14dc17321f9ff..d7cccf0bedcf7 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 6ccb24fe3a84470bfdae0c5c23dae22d09526b04 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 11 Oct 2022 17:01:22 +0000 Subject: [PATCH 096/358] 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 e82044a89c479..ebcbc75ba32c3 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 0000000000000..fec2bc25617ac --- /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 0000000000000..57def42b1ee30 --- /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 0000000000000..7ff91b476aeb5 --- /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 0000000000000..fda0919b9b647 --- /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 bd1b23f7e3bbb..9bc18dabd6ead 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 c43c9fcba9620dba4efb6982e257fcb0b423c7f0 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 097/358] 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 d7cccf0bedcf7..09e95e53de63c 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/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs index caa579e322b3c..2d6927cee9953 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs +++ b/src/tools/rust-analyzer/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 95b3411216c013418bf03ec89c10e6a396909a0c Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Tue, 11 Oct 2022 11:42:44 -0700 Subject: [PATCH 098/358] 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 cffe1e32308b6..f5513e9996d40 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 d5e69e7f56f03167449549ab585e8351c6996d4a Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Tue, 11 Oct 2022 14:46:49 -0700 Subject: [PATCH 099/358] 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 f8bf6850b52da..2adfdcf1267c0 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 f258b7916250cae1e1064a9f0e9c4bdf4675c695 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Tue, 11 Oct 2022 15:05:22 -0700 Subject: [PATCH 100/358] 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 2adfdcf1267c0..fa865de53f817 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 a9525db8c8580606f7e73c55fa047e2eb4876e28 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 11 Oct 2022 16:04:17 -0700 Subject: [PATCH 101/358] 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 4ce51cb7441a6..c533348edd69f 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 f53bfa0569292..9c73283775466 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 a7cdac33ca735..f6ed74f582bdd 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 767a6bd9727a5..5e7b296d6c345 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 9a86c0467bbe4..7518c3445dc02 160000 --- a/src/doc/rustc-dev-guide +++ b/src/doc/rustc-dev-guide @@ -1 +1 @@ -Subproject commit 9a86c0467bbe42056f73fdf5b03fff757d7c4a9b +Subproject commit 7518c3445dc02df0d196f5f84e568d633c5141fb From f449d2e035fedf78129fcc4f775c7022e6d3b851 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 9 Oct 2022 06:54:00 +0000 Subject: [PATCH 102/358] 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 f1674d04f8d15..cdcebb61c2e8c 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 3fc10197b9129..ab4ecec2af36e 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 cd9d229640571..196d70614e7c9 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -137,77 +137,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 144bb7b6e73e2df9666c07ddec9e19b7785015e8 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 12 Oct 2022 04:03:59 +0000 Subject: [PATCH 103/358] 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 5eb16bcd1564e..d4c7342738616 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 0000000000000..2db7f8a354cf5 --- /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 0000000000000..952b81584f30d --- /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 666beff077caab00030781db44a439c44f7c4c66 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 12 Oct 2022 04:17:21 +0000 Subject: [PATCH 104/358] 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 ab4ecec2af36e..68119598285c5 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 7e7ed50d1eeba6dfd5fbcd5912da9f9c36df1645 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 12 Oct 2022 08:19:50 +0200 Subject: [PATCH 105/358] 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 60c2960b160ec..a8a1899317f87 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 05813477bb24703671e109417bc0cf9e53ba8959 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 12 Oct 2022 09:28:11 +0000 Subject: [PATCH 106/358] Improve docs for `struct_lint_level` function. --- compiler/rustc_middle/src/lint.rs | 38 +++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index d95c5cbd654cb..ec9130f5e0c78 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -274,6 +274,44 @@ pub fn explain_lint_level_source( } } +/// The innermost function for emitting lints. +/// +/// If you are loocking to implement a lint, look for higher level functions, +/// for example: +/// - [`TyCtxt::emit_spanned_lint`] +/// - [`TyCtxt::struct_span_lint_hir`] +/// - [`TyCtxt::emit_lint`] +/// - [`TyCtxt::struct_lint_node`] +/// - `LintContext::lookup` +/// +/// ## `decorate` signature +/// +/// Signature of `decorate` may be confusing at first, for instance what's the +/// point of returning `&'b mut DiagnosticBuilder<'a, ()>` if the original can +/// be used instead? +/// ```ignore pseudo-code +/// _ = decorate(&mut diag); +/// /* use `diag` here again */ +/// ``` +/// +/// There 2 reasons for such choice signature. +/// +/// First off all, it prevents accidental use `.emit()` -- it's clear that the +/// builder will be later used and shouldn't be emitted right away (this is +/// especially important because the old API expected you to call `.emit()` in +/// the closure). +/// +/// Second of all, it makes the most common case of adding just a single label +/// /suggestion much nicer, since [`DiagnosticBuilder`] methods return +/// `&mut DiagnosticBuilder`, you can just chain methods, without needed +/// awkward `{ ...; }`: +/// ```ignore pseudo-code +/// struct_lint_level( +/// ..., +/// |lint| lint.span_label(sp, "lbl") +/// // ^^^^^^^^^^^^^^^^^^^^^ returns `&mut DiagnosticBuilder` by default +/// ) +/// ``` pub fn struct_lint_level( sess: &Session, lint: &'static Lint, From dc6fcb875c6c6db7c7050bf98eb411aade0a983d Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Wed, 12 Oct 2022 19:03:46 +0900 Subject: [PATCH 107/358] 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 776c8ad528c0f..cc877e2fd30ca 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 0000000000000..c6feca6f32f60 --- /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 0000000000000..65be0258e6dba --- /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 0c0d87da31c7f4c5495fcaaa192953262d394568 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 108/358] 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 0000000000000..e02b2d98f4215 --- /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 e82cca402e2d8..fc0bce5857233 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 c1ce94f470559..8fe361c45a263 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 35a04bdb77b2e34bb9cb8dd96801dd4ba357e41e 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 109/358] 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 340302766d245..60b7f2e4c2223 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 16224d71e4569..83b10d906e297 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 1d3c4fcca0a44..015f5c1ee8ae5 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 510adde62962b..719588a936ce3 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 eeeb7d6d3e5cc..b0625b5f412e0 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 ab2dafd828a56..a6024044ad82f 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 098f9d5154976..bc149e48d89e8 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 98eeaad976fe1..d64cdcdbaa9db 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 d3a98e43c5382..d4258151ff3f3 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 9a22a12b93b33..85ef51f129bbd 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 f46f0ea4cabed..ea7a507d7a43c 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 cb7700f9bcab4..0d7d4054bb3cd 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2697,8 +2697,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 0dc05475ce944..81c051b8f35e4 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 8d527c05122d1..102df3a4d7ead 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 d2fb8c32ffd27..9fe7da3f29ec1 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 65536cb3aa135..dbf3a8f00ee6d 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 a7ccf166b87ea82d3aa12681a75c319a5c66c99d 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 110/358] 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 e02b2d98f4215..c9f1dfb707f3f 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 ba9d27aae569238c8fd625b982209d9b9389e825 Mon Sep 17 00:00:00 2001 From: Florian Bartels Date: Wed, 12 Oct 2022 14:30:06 +0200 Subject: [PATCH 111/358] 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 bed9d39161df8..ec992da681219 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 77990787ddd03f1a8d87e51998a6ebf4bd90d592 Mon Sep 17 00:00:00 2001 From: Waffle Maybe Date: Wed, 12 Oct 2022 16:39:11 +0400 Subject: [PATCH 112/358] Apply suggestions from code review Co-authored-by: Ralf Jung --- compiler/rustc_middle/src/lint.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index ec9130f5e0c78..0fa44587011ba 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -286,17 +286,12 @@ pub fn explain_lint_level_source( /// /// ## `decorate` signature /// -/// Signature of `decorate` may be confusing at first, for instance what's the -/// point of returning `&'b mut DiagnosticBuilder<'a, ()>` if the original can -/// be used instead? -/// ```ignore pseudo-code -/// _ = decorate(&mut diag); -/// /* use `diag` here again */ -/// ``` +/// The return value of `decorate` is ignored by this function. So what is the +/// point of returning `&'b mut DiagnosticBuilder<'a, ()>`? /// -/// There 2 reasons for such choice signature. +/// There are 2 reasons for this signature. /// -/// First off all, it prevents accidental use `.emit()` -- it's clear that the +/// First off all, it prevents accidental use of `.emit()` -- it's clear that the /// builder will be later used and shouldn't be emitted right away (this is /// especially important because the old API expected you to call `.emit()` in /// the closure). From 8914cf04469be0a6683cb4ffc753b52ffc8fcc33 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 12 Oct 2022 14:16:24 +0000 Subject: [PATCH 113/358] link lint function with `decorate` function param to `struct_lint_level` --- compiler/rustc_lint/src/context.rs | 19 +++++++++++++++++++ compiler/rustc_lint/src/levels.rs | 4 ++++ compiler/rustc_middle/src/ty/context.rs | 10 ++++++++++ 3 files changed, 33 insertions(+) diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 74e35afc87d7b..63a11877333ef 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -574,6 +574,11 @@ pub trait LintContext: Sized { fn sess(&self) -> &Session; fn lints(&self) -> &LintStore; + /// Emit a lint at the appropriate level, with an optional associated span and an existing diagnostic. + /// + /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation. + /// + /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature fn lookup_with_diagnostics( &self, lint: &'static Lint, @@ -872,6 +877,11 @@ pub trait LintContext: Sized { // FIXME: These methods should not take an Into -- instead, callers should need to // set the span in their `decorate` function (preferably using set_span). + /// Emit a lint at the appropriate level, with an optional associated span. + /// + /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation. + /// + /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature fn lookup>( &self, lint: &'static Lint, @@ -893,6 +903,11 @@ pub trait LintContext: Sized { self.lookup(lint, Some(span), decorator.msg(), |diag| decorator.decorate_lint(diag)); } + /// Emit a lint at the appropriate level, with an associated span. + /// + /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation. + /// + /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature fn struct_span_lint>( &self, lint: &'static Lint, @@ -914,6 +929,10 @@ pub trait LintContext: Sized { } /// Emit a lint at the appropriate level, with no associated span. + /// + /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation. + /// + /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature fn lint( &self, lint: &'static Lint, diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index be1d7d98aa69c..d3879ff487de9 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -1069,6 +1069,10 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { /// Used to emit a lint-related diagnostic based on the current state of /// this lint context. + /// + /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation. + /// + /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature pub(crate) fn struct_lint( &self, lint: &'static Lint, diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 8a9160d246640..e675d797e20b5 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -2824,6 +2824,11 @@ impl<'tcx> TyCtxt<'tcx> { }) } + /// Emit a lint at the appropriate level for a hir node, with an associated span. + /// + /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation. + /// + /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature pub fn struct_span_lint_hir( self, lint: &'static Lint, @@ -2849,6 +2854,11 @@ impl<'tcx> TyCtxt<'tcx> { self.struct_lint_node(lint, id, decorator.msg(), |diag| decorator.decorate_lint(diag)) } + /// Emit a lint at the appropriate level for a hir node. + /// + /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation. + /// + /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature pub fn struct_lint_node( self, lint: &'static Lint, From 8722e9f0c1b04135230897b3f5b34b84a8bb6a2a Mon Sep 17 00:00:00 2001 From: Rageking8 Date: Wed, 12 Oct 2022 22:32:20 +0800 Subject: [PATCH 114/358] 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 0000000000000..43ff23600766e --- /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 0000000000000..4504039097b5d --- /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 2590baf2248dfeeeb1c76a0f4175d67a83c96513 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 115/358] Set opt-level = 1 on dev profile --- src/tools/rust-analyzer/.github/workflows/ci.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tools/rust-analyzer/.github/workflows/ci.yaml b/src/tools/rust-analyzer/.github/workflows/ci.yaml index 1563ee0b14385..1c061bb748664 100644 --- a/src/tools/rust-analyzer/.github/workflows/ci.yaml +++ b/src/tools/rust-analyzer/.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 8ba47d97d299bd69726498ee13b3dfc30ed16eca 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 116/358] Avoid format! in favor of to_string --- src/tools/rust-analyzer/crates/syntax/src/ast/make.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs index c9a21e12c085b..4057a75e7c1e6 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs +++ b/src/tools/rust-analyzer/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 2a835c273acbb8b9885c0fa05e7bcdecba04e05f Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 5 Oct 2022 03:52:38 +0000 Subject: [PATCH 117/358] 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 0000000000000..f0d407cd527d6 --- /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 0000000000000..4529d301f9e91 --- /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 51ad5771927eb41db3aeb0662b3a54b7845c9576 Mon Sep 17 00:00:00 2001 From: Rageking8 Date: Thu, 13 Oct 2022 00:12:19 +0800 Subject: [PATCH 118/358] 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 f49805d9b4e15..6ed8370e8c1c7 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 441dc3c7e888c..5a5e9db81a243 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 c897a5e8701c4..16c634e9afd50 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 8f7abb55e2376..94546ca09d00d 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 af83f14bbda5d..1714cec5221de 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 2362831d8b8f6..7a6e5952cb956 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 52801c35ba3d3..630fb2c41cded 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 9a73f1a53e52f..9b1212694f5ba 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 0d38bd3935194..d5aeab47e62b0 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 65cc4e2ef1e41..0417e952eb71d 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 62f1a7f98ea16..ef1d866c3ff3b 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 428cc637f5567..3470a7e09ad8b 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 1534b6d07deb0..6da99cf6435cf 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 e30747782339c..fdfc5907c1674 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 bdc66e83083f6..74c35083e60b5 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 62968ea54e0aa..70b8d8da19b32 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 2494af1208f14..6b7b0c819cca6 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 1af8fea57b82f019e7f9d284ed7a3be6661dbc21 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 119/358] Cast runnableEnv items to string --- .../rust-analyzer/editors/code/src/config.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/editors/code/src/config.ts b/src/tools/rust-analyzer/editors/code/src/config.ts index a9c0f079b3da9..d66aa52fadc6c 100644 --- a/src/tools/rust-analyzer/editors/code/src/config.ts +++ b/src/tools/rust-analyzer/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 83c6da7064f280bbe870ef6ac44f930f32e55fe2 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Wed, 12 Oct 2022 11:24:23 -0700 Subject: [PATCH 120/358] 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 | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index fa865de53f817..93aa3240c55fe 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -692,16 +692,13 @@ pre, .rustdoc.source .example-wrap { .item-info { display: block; + margin-left: 24px; } -.content .item-info code { +.item-info code { font-size: 0.875rem; } -.content .item-info { - margin-left: 24px; -} - #main-content > .item-info { margin-top: 0; margin-left: 0; @@ -1987,7 +1984,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 f6d69297e42fecfa71ce60ee90813e23f7332611 Mon Sep 17 00:00:00 2001 From: Jhonny Bill Mena Date: Tue, 4 Oct 2022 20:56:05 -0400 Subject: [PATCH 121/358] 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 0000000000000..429bdd2777f91 --- /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 3ea9429a23ab1..47127ea8e9ce8 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 a6024044ad82f..9465051dd103f 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 0000000000000..2a1a5d5afb049 --- /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 b16c54e0aacaa..955e85c3616b7 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 e675d797e20b5..8636c4465d46f 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; @@ -1247,7 +1246,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 70b470f3811d5..f2ee52262adee 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 b5962f76b7f4a..d12796f289e96 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 ed363be495b929981629b58ccd108cf8ce204312 Mon Sep 17 00:00:00 2001 From: Jhonny Bill Mena Date: Wed, 12 Oct 2022 16:55:28 -0400 Subject: [PATCH 122/358] 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 3e0840caaa693..518c59dba5366 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 2a1a5d5afb049..860f24871bcdc 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 955e85c3616b7..9fafbe4bd407e 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 d5335bcc7fececda2f7fb001552ff5a40467b4fa Mon Sep 17 00:00:00 2001 From: est31 Date: Wed, 12 Oct 2022 17:57:05 +0200 Subject: [PATCH 123/358] 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 | 263 +++++++++++++++++---------------- 1 file changed, 137 insertions(+), 126 deletions(-) diff --git a/src/tools/tidy/src/features.rs b/src/tools/tidy/src/features.rs index d8b3903b98e7c..f10ecf5f201e3 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(); - - // 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; - } - _ => {} + 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; + continue; } + "// no-tracking-issue-end" => { + next_feature_omits_tracking_issue = false; + continue; + } + _ => {} + } - if line.starts_with(FEATURE_GROUP_START_PREFIX) { - if in_feature_group { - tidy_error!( - bad, - "{}:{}: \ + 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; + path.display(), + line_number, + ); } - 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 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, - }; + 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 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], - ) - }; + 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 + } + }; + 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, + }; - tidy_error!( - bad, - "{}:{}: feature {} is not sorted by feature name (should be {})", - path.display(), - line_number, - name, - correct_placement, - ); - } - prev_names.push(name); + 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()) - }; - Some((name.to_owned(), Feature { level, since, has_gate_test: false, tracking_issue })) - }) - .collect() + 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 045d38ff97600d29ce3302ec17018af9b5a32157 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 124/358] fix formatting --- src/tools/rust-analyzer/editors/code/src/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/editors/code/src/config.ts b/src/tools/rust-analyzer/editors/code/src/config.ts index d66aa52fadc6c..15846a5e8645e 100644 --- a/src/tools/rust-analyzer/editors/code/src/config.ts +++ b/src/tools/rust-analyzer/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 7d84ba5fa758eff68bd8c0c026f33f4a70709c7e Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Wed, 12 Oct 2022 14:26:22 -0700 Subject: [PATCH 125/358] 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 86a56f0ddb78c..4ed99df1e8169 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 6a41232a33bd1a25f920f5aa351bb1f7179b9ae2 Mon Sep 17 00:00:00 2001 From: Pointerbender Date: Wed, 12 Oct 2022 23:34:13 +0200 Subject: [PATCH 126/358] 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 b0ef887befe73..3451a25504e34 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -1816,14 +1816,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 284f60d0957ea45c38d934db5415ae26446ee81b Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Wed, 12 Oct 2022 15:29:08 -0700 Subject: [PATCH 127/358] 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 c55b490630200..104010f8d435e 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