From 462c1c846b5488024c2e5fec6e1ed4700642b49e Mon Sep 17 00:00:00 2001 From: Christian Poveda Date: Tue, 17 May 2022 13:10:15 -0500 Subject: [PATCH 01/11] generate code for `subdiagnostic` fields in the second `match` --- .../src/diagnostics/diagnostic.rs | 74 ++++++++++++++----- .../rustc_parse/src/parser/diagnostics.rs | 16 ++-- 2 files changed, 63 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index dac3e986e7ad4..adb25e1fdef54 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -71,31 +71,46 @@ impl<'a> SessionDiagnosticDerive<'a> { } }; + // Keep track of which fields are subdiagnostics + let mut subdiagnostics = std::collections::HashSet::new(); + // Generates calls to `span_label` and similar functions based on the attributes // on fields. Code for suggestions uses formatting machinery and the value of // other fields - because any given field can be referenced multiple times, it // should be accessed through a borrow. When passing fields to `set_arg` (which // happens below) for Fluent, we want to move the data, so that has to happen // in a separate pass over the fields. - let attrs = structure.each(|field_binding| { - let field = field_binding.ast(); - let result = field.attrs.iter().map(|attr| { - builder - .generate_field_attr_code( - attr, - FieldInfo { - vis: &field.vis, - binding: field_binding, - ty: &field.ty, - span: &field.span(), - }, - ) - .unwrap_or_else(|v| v.to_compile_error()) + let attrs = structure + .clone() + // Remove the fields that have a `subdiagnostic` attribute. + .filter(|field_binding| { + field_binding.ast().attrs.iter().all(|attr| { + "subdiagnostic" != attr.path.segments.last().unwrap().ident.to_string() + || { + subdiagnostics.insert(field_binding.binding.clone()); + false + } + }) + }) + .each(|field_binding| { + let field = field_binding.ast(); + let result = field.attrs.iter().map(|attr| { + builder + .generate_field_attr_code( + attr, + FieldInfo { + vis: &field.vis, + binding: field_binding, + ty: &field.ty, + span: &field.span(), + }, + ) + .unwrap_or_else(|v| v.to_compile_error()) + }); + + quote! { #(#result);* } }); - quote! { #(#result);* } - }); - // When generating `set_arg` calls, move data rather than borrow it to avoid // requiring clones - this must therefore be the last use of each field (for // example, any formatting machinery that might refer to a field should be @@ -107,7 +122,7 @@ impl<'a> SessionDiagnosticDerive<'a> { // need to be passed as an argument to the diagnostic. But when a field has no // attributes then it must be passed as an argument to the diagnostic so that // it can be referred to by Fluent messages. - if field.attrs.is_empty() { + let tokens = if field.attrs.is_empty() { let diag = &builder.diag; let ident = field_binding.ast().ident.as_ref().unwrap(); quote! { @@ -118,6 +133,27 @@ impl<'a> SessionDiagnosticDerive<'a> { } } else { quote! {} + }; + // If this field had a subdiagnostic attribute, we generate the code here to + // avoid binding it twice. + if subdiagnostics.contains(&field_binding.binding) { + let result = field.attrs.iter().map(|attr| { + builder + .generate_field_attr_code( + attr, + FieldInfo { + vis: &field.vis, + binding: field_binding, + ty: &field.ty, + span: &field.span(), + }, + ) + .unwrap_or_else(|v| v.to_compile_error()) + }); + + quote! { #(#result);* #tokens } + } else { + tokens } }); @@ -359,6 +395,8 @@ impl SessionDiagnosticDeriveBuilder { let (binding, needs_destructure) = match (name.as_str(), &inner_ty) { // `primary_span` can accept a `Vec` so don't destructure that. ("primary_span", FieldInnerTy::Vec(_)) => (quote! { #field_binding.clone() }, false), + // `subdiagnostics` are not derefed because they are bound by value. + ("subdiagnostic", _) => (quote! { #field_binding }, true), _ => (quote! { *#field_binding }, true), }; diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 3b2ce0de50915..0c20bf49c4fb6 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -254,23 +254,23 @@ struct AmbiguousPlus { #[derive(SessionDiagnostic)] #[error(code = "E0178", slug = "parser-maybe-recover-from-bad-type-plus")] -struct BadTypePlus<'a> { +struct BadTypePlus { pub ty: String, #[primary_span] pub span: Span, #[subdiagnostic] - pub sub: BadTypePlusSub<'a>, + pub sub: BadTypePlusSub, } -#[derive(SessionSubdiagnostic, Clone, Copy)] -pub enum BadTypePlusSub<'a> { +#[derive(SessionSubdiagnostic)] +pub enum BadTypePlusSub { #[suggestion( slug = "parser-add-paren", code = "{sum_with_parens}", applicability = "machine-applicable" )] AddParen { - sum_with_parens: &'a str, + sum_with_parens: String, #[primary_span] span: Span, }, @@ -1289,11 +1289,9 @@ impl<'a> Parser<'a> { let bounds = self.parse_generic_bounds(None)?; let sum_span = ty.span.to(self.prev_token.span); - let sum_with_parens: String; - let sub = match ty.kind { TyKind::Rptr(ref lifetime, ref mut_ty) => { - sum_with_parens = pprust::to_string(|s| { + let sum_with_parens = pprust::to_string(|s| { s.s.word("&"); s.print_opt_lifetime(lifetime); s.print_mutability(mut_ty.mutbl, false); @@ -1303,7 +1301,7 @@ impl<'a> Parser<'a> { s.pclose() }); - BadTypePlusSub::AddParen { sum_with_parens: &sum_with_parens, span: sum_span } + BadTypePlusSub::AddParen { sum_with_parens, span: sum_span } } TyKind::Ptr(..) | TyKind::BareFn(..) => BadTypePlusSub::ForgotParen { span: sum_span }, _ => BadTypePlusSub::ExpectPath { span: sum_span }, From 19e1f73085c1e74f06512074805c5960db51891f Mon Sep 17 00:00:00 2001 From: Christian Poveda Date: Wed, 18 May 2022 10:50:59 -0500 Subject: [PATCH 02/11] generate `set_arg` code inside `generate_field_attrs_code` --- .../src/diagnostics/diagnostic.rs | 200 +++++++++--------- 1 file changed, 101 insertions(+), 99 deletions(-) diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index adb25e1fdef54..25cba2df7f19b 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -71,91 +71,72 @@ impl<'a> SessionDiagnosticDerive<'a> { } }; - // Keep track of which fields are subdiagnostics - let mut subdiagnostics = std::collections::HashSet::new(); + // Keep track of which fields are subdiagnostics or have no attributes. + let mut subdiagnostics_or_empty = std::collections::HashSet::new(); // Generates calls to `span_label` and similar functions based on the attributes // on fields. Code for suggestions uses formatting machinery and the value of // other fields - because any given field can be referenced multiple times, it - // should be accessed through a borrow. When passing fields to `set_arg` (which - // happens below) for Fluent, we want to move the data, so that has to happen - // in a separate pass over the fields. + // should be accessed through a borrow. When passing fields to `add_subdiagnostic` + // or `set_arg` (which happens below) for Fluent, we want to move the data, so that + // has to happen in a separate pass over the fields. let attrs = structure .clone() - // Remove the fields that have a `subdiagnostic` attribute. .filter(|field_binding| { - field_binding.ast().attrs.iter().all(|attr| { - "subdiagnostic" != attr.path.segments.last().unwrap().ident.to_string() - || { - subdiagnostics.insert(field_binding.binding.clone()); - false - } - }) + let attrs = &field_binding.ast().attrs; + + (!attrs.is_empty() + && attrs.iter().all(|attr| { + "subdiagnostic" + != attr.path.segments.last().unwrap().ident.to_string() + })) + || { + subdiagnostics_or_empty.insert(field_binding.binding.clone()); + false + } }) .each(|field_binding| { let field = field_binding.ast(); - let result = field.attrs.iter().map(|attr| { - builder - .generate_field_attr_code( - attr, - FieldInfo { - vis: &field.vis, - binding: field_binding, - ty: &field.ty, - span: &field.span(), - }, - ) - .unwrap_or_else(|v| v.to_compile_error()) - }); - - quote! { #(#result);* } + let field_span = field.span(); + + builder.generate_field_attrs_code( + &field.attrs, + FieldInfo { + vis: &field.vis, + binding: field_binding, + ty: &field.ty, + span: &field_span, + }, + ) }); - // When generating `set_arg` calls, move data rather than borrow it to avoid - // requiring clones - this must therefore be the last use of each field (for - // example, any formatting machinery that might refer to a field should be - // generated already). + // When generating `set_arg` or `add_subdiagnostic` calls, move data rather than + // borrow it to avoid requiring clones - this must therefore be the last use of + // each field (for example, any formatting machinery that might refer to a field + // should be generated already). structure.bind_with(|_| synstructure::BindStyle::Move); - let args = structure.each(|field_binding| { - let field = field_binding.ast(); - // When a field has attributes like `#[label]` or `#[note]` then it doesn't - // need to be passed as an argument to the diagnostic. But when a field has no - // attributes then it must be passed as an argument to the diagnostic so that - // it can be referred to by Fluent messages. - let tokens = if field.attrs.is_empty() { - let diag = &builder.diag; - let ident = field_binding.ast().ident.as_ref().unwrap(); - quote! { - #diag.set_arg( - stringify!(#ident), - #field_binding - ); - } - } else { - quote! {} - }; - // If this field had a subdiagnostic attribute, we generate the code here to - // avoid binding it twice. - if subdiagnostics.contains(&field_binding.binding) { - let result = field.attrs.iter().map(|attr| { - builder - .generate_field_attr_code( - attr, - FieldInfo { - vis: &field.vis, - binding: field_binding, - ty: &field.ty, - span: &field.span(), - }, - ) - .unwrap_or_else(|v| v.to_compile_error()) - }); - - quote! { #(#result);* #tokens } - } else { - tokens - } - }); + // When a field has attributes like `#[label]` or `#[note]` then it doesn't + // need to be passed as an argument to the diagnostic. But when a field has no + // attributes or a `#[subdiagnostic]` attribute then it must be passed as an + // argument to the diagnostic so that it can be referred to by Fluent messages. + let args = structure + .filter(|field_binding| { + subdiagnostics_or_empty.contains(&field_binding.binding) + }) + .each(|field_binding| { + let field = field_binding.ast(); + let field_span = field.span(); + + builder.generate_field_attrs_code( + &field.attrs, + FieldInfo { + vis: &field.vis, + binding: field_binding, + ty: &field.ty, + span: &field_span, + }, + ) + }); let span = ast.span().unwrap(); let (diag, sess) = (&builder.diag, &builder.sess); @@ -383,38 +364,59 @@ impl SessionDiagnosticDeriveBuilder { Ok(tokens.drain(..).collect()) } - fn generate_field_attr_code( - &mut self, - attr: &syn::Attribute, - info: FieldInfo<'_>, - ) -> Result { + fn generate_field_attrs_code<'a>( + &'a mut self, + attrs: &'a [syn::Attribute], + info: FieldInfo<'a>, + ) -> TokenStream { let field_binding = &info.binding.binding; let inner_ty = FieldInnerTy::from_type(&info.ty); - let name = attr.path.segments.last().unwrap().ident.to_string(); - let (binding, needs_destructure) = match (name.as_str(), &inner_ty) { - // `primary_span` can accept a `Vec` so don't destructure that. - ("primary_span", FieldInnerTy::Vec(_)) => (quote! { #field_binding.clone() }, false), - // `subdiagnostics` are not derefed because they are bound by value. - ("subdiagnostic", _) => (quote! { #field_binding }, true), - _ => (quote! { *#field_binding }, true), - }; - - let generated_code = self.generate_inner_field_code( - attr, - FieldInfo { - vis: info.vis, - binding: info.binding, - ty: inner_ty.inner_type().unwrap_or(&info.ty), - span: info.span, - }, - binding, - )?; - if needs_destructure { - Ok(inner_ty.with(field_binding, generated_code)) + if attrs.is_empty() { + let diag = &self.diag; + let ident = info.binding.ast().ident.as_ref().unwrap(); + quote! { + #diag.set_arg( + stringify!(#ident), + #field_binding + ); + } } else { - Ok(generated_code) + attrs + .iter() + .map(move |attr| { + let name = attr.path.segments.last().unwrap().ident.to_string(); + let (binding, needs_destructure) = match (name.as_str(), &inner_ty) { + // `primary_span` can accept a `Vec` so don't destructure that. + ("primary_span", FieldInnerTy::Vec(_)) => { + (quote! { #field_binding.clone() }, false) + } + // `subdiagnostics` are not derefed because they are bound by value. + ("subdiagnostic", _) => (quote! { #field_binding }, true), + _ => (quote! { *#field_binding }, true), + }; + + let generated_code = self + .generate_inner_field_code( + attr, + FieldInfo { + vis: info.vis, + binding: info.binding, + ty: inner_ty.inner_type().unwrap_or(&info.ty), + span: info.span, + }, + binding, + ) + .unwrap_or_else(|v| v.to_compile_error()); + + if needs_destructure { + inner_ty.with(field_binding, generated_code) + } else { + generated_code + } + }) + .collect() } } From 952121933e817cd10409a52620dab11c013b3e84 Mon Sep 17 00:00:00 2001 From: Christian Poveda Date: Wed, 18 May 2022 10:53:48 -0500 Subject: [PATCH 03/11] move misplaced comment --- compiler/rustc_macros/src/diagnostics/diagnostic.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index 25cba2df7f19b..8ba2fcf6ec2ec 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -110,10 +110,6 @@ impl<'a> SessionDiagnosticDerive<'a> { ) }); - // When generating `set_arg` or `add_subdiagnostic` calls, move data rather than - // borrow it to avoid requiring clones - this must therefore be the last use of - // each field (for example, any formatting machinery that might refer to a field - // should be generated already). structure.bind_with(|_| synstructure::BindStyle::Move); // When a field has attributes like `#[label]` or `#[note]` then it doesn't // need to be passed as an argument to the diagnostic. But when a field has no @@ -373,6 +369,10 @@ impl SessionDiagnosticDeriveBuilder { let inner_ty = FieldInnerTy::from_type(&info.ty); + // When generating `set_arg` or `add_subdiagnostic` calls, move data rather than + // borrow it to avoid requiring clones - this must therefore be the last use of + // each field (for example, any formatting machinery that might refer to a field + // should be generated already). if attrs.is_empty() { let diag = &self.diag; let ident = info.binding.ast().ident.as_ref().unwrap(); From 7e9be9240cf1f3c2fc48fffbb202b249821c61b2 Mon Sep 17 00:00:00 2001 From: Christian Poveda Date: Wed, 18 May 2022 10:55:35 -0500 Subject: [PATCH 04/11] remove unnecessary generics --- compiler/rustc_macros/src/diagnostics/diagnostic.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index 8ba2fcf6ec2ec..c24b45862eb03 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -360,10 +360,10 @@ impl SessionDiagnosticDeriveBuilder { Ok(tokens.drain(..).collect()) } - fn generate_field_attrs_code<'a>( - &'a mut self, - attrs: &'a [syn::Attribute], - info: FieldInfo<'a>, + fn generate_field_attrs_code( + &mut self, + attrs: &[syn::Attribute], + info: FieldInfo<'_>, ) -> TokenStream { let field_binding = &info.binding.binding; From d839d2ecc274f4633bbb621d169d84347ffc2ba9 Mon Sep 17 00:00:00 2001 From: Christian Poveda Date: Thu, 19 May 2022 00:47:45 -0500 Subject: [PATCH 05/11] let `generate_field_attrs_code` create `FieldInfo` this simplifies the code inside the `structure.each` closure argument and allows to remove the `vis` field from `FieldInfo`. --- .../src/diagnostics/diagnostic.rs | 56 +++++-------------- .../src/diagnostics/subdiagnostic.rs | 1 - .../rustc_macros/src/diagnostics/utils.rs | 3 +- 3 files changed, 14 insertions(+), 46 deletions(-) diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index c24b45862eb03..8c5b64fab5fc1 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -13,7 +13,7 @@ use quote::{format_ident, quote}; use std::collections::HashMap; use std::str::FromStr; use syn::{spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, Type}; -use synstructure::Structure; +use synstructure::{BindingInfo, Structure}; /// The central struct for constructing the `into_diagnostic` method from an annotated struct. pub(crate) struct SessionDiagnosticDerive<'a> { @@ -95,20 +95,7 @@ impl<'a> SessionDiagnosticDerive<'a> { false } }) - .each(|field_binding| { - let field = field_binding.ast(); - let field_span = field.span(); - - builder.generate_field_attrs_code( - &field.attrs, - FieldInfo { - vis: &field.vis, - binding: field_binding, - ty: &field.ty, - span: &field_span, - }, - ) - }); + .each(|field_binding| builder.generate_field_attrs_code(field_binding)); structure.bind_with(|_| synstructure::BindStyle::Move); // When a field has attributes like `#[label]` or `#[note]` then it doesn't @@ -119,20 +106,7 @@ impl<'a> SessionDiagnosticDerive<'a> { .filter(|field_binding| { subdiagnostics_or_empty.contains(&field_binding.binding) }) - .each(|field_binding| { - let field = field_binding.ast(); - let field_span = field.span(); - - builder.generate_field_attrs_code( - &field.attrs, - FieldInfo { - vis: &field.vis, - binding: field_binding, - ty: &field.ty, - span: &field_span, - }, - ) - }); + .each(|field_binding| builder.generate_field_attrs_code(field_binding)); let span = ast.span().unwrap(); let (diag, sess) = (&builder.diag, &builder.sess); @@ -360,22 +334,19 @@ impl SessionDiagnosticDeriveBuilder { Ok(tokens.drain(..).collect()) } - fn generate_field_attrs_code( - &mut self, - attrs: &[syn::Attribute], - info: FieldInfo<'_>, - ) -> TokenStream { - let field_binding = &info.binding.binding; + fn generate_field_attrs_code(&mut self, binding_info: &BindingInfo<'_>) -> TokenStream { + let field = binding_info.ast(); + let field_binding = &binding_info.binding; - let inner_ty = FieldInnerTy::from_type(&info.ty); + let inner_ty = FieldInnerTy::from_type(&field.ty); // When generating `set_arg` or `add_subdiagnostic` calls, move data rather than // borrow it to avoid requiring clones - this must therefore be the last use of // each field (for example, any formatting machinery that might refer to a field // should be generated already). - if attrs.is_empty() { + if field.attrs.is_empty() { let diag = &self.diag; - let ident = info.binding.ast().ident.as_ref().unwrap(); + let ident = field.ident.as_ref().unwrap(); quote! { #diag.set_arg( stringify!(#ident), @@ -383,7 +354,7 @@ impl SessionDiagnosticDeriveBuilder { ); } } else { - attrs + field.attrs .iter() .map(move |attr| { let name = attr.path.segments.last().unwrap().ident.to_string(); @@ -401,10 +372,9 @@ impl SessionDiagnosticDeriveBuilder { .generate_inner_field_code( attr, FieldInfo { - vis: info.vis, - binding: info.binding, - ty: inner_ty.inner_type().unwrap_or(&info.ty), - span: info.span, + binding: binding_info, + ty: inner_ty.inner_type().unwrap_or(&field.ty), + span: &field.span(), }, binding, ) diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index ae5b9dbd9ba1c..df01419c82a8e 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -303,7 +303,6 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> { let inner_ty = FieldInnerTy::from_type(&ast.ty); let info = FieldInfo { - vis: &ast.vis, binding: binding, ty: inner_ty.inner_type().unwrap_or(&ast.ty), span: &ast.span(), diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs index af5a30880e05f..636bcf1f7b1d9 100644 --- a/compiler/rustc_macros/src/diagnostics/utils.rs +++ b/compiler/rustc_macros/src/diagnostics/utils.rs @@ -4,7 +4,7 @@ use proc_macro2::TokenStream; use quote::{format_ident, quote, ToTokens}; use std::collections::BTreeSet; use std::str::FromStr; -use syn::{spanned::Spanned, Attribute, Meta, Type, TypeTuple, Visibility}; +use syn::{spanned::Spanned, Attribute, Meta, Type, TypeTuple}; use synstructure::BindingInfo; /// Checks whether the type name of `ty` matches `name`. @@ -158,7 +158,6 @@ impl<'ty> FieldInnerTy<'ty> { /// Field information passed to the builder. Deliberately omits attrs to discourage the /// `generate_*` methods from walking the attributes themselves. pub(crate) struct FieldInfo<'a> { - pub(crate) vis: &'a Visibility, pub(crate) binding: &'a BindingInfo<'a>, pub(crate) ty: &'a Type, pub(crate) span: &'a proc_macro2::Span, From 8227e69018279353bd17ee3ca227e22ce6c03783 Mon Sep 17 00:00:00 2001 From: Christian Poveda Date: Thu, 19 May 2022 09:02:50 -0500 Subject: [PATCH 06/11] formatting --- compiler/rustc_macros/src/diagnostics/diagnostic.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index 8c5b64fab5fc1..d7daee64d791e 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -354,7 +354,8 @@ impl SessionDiagnosticDeriveBuilder { ); } } else { - field.attrs + field + .attrs .iter() .map(move |attr| { let name = attr.path.segments.last().unwrap().ident.to_string(); From b2a95cb582cec1088b060e79e1ef6ce5a9393ed0 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 22 May 2022 12:40:26 -0700 Subject: [PATCH 07/11] Lifetime variance fixes for rustdoc --- compiler/rustc_hir/src/hir.rs | 8 +- src/librustdoc/clean/inline.rs | 2 +- src/librustdoc/clean/mod.rs | 288 +++++++++++++++++---------------- src/librustdoc/clean/utils.rs | 22 +-- 4 files changed, 166 insertions(+), 154 deletions(-) diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index cebf68769361b..bda7affe52983 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -343,12 +343,12 @@ pub struct GenericArgs<'hir> { pub span_ext: Span, } -impl GenericArgs<'_> { +impl<'hir> GenericArgs<'hir> { pub const fn none() -> Self { Self { args: &[], bindings: &[], parenthesized: false, span_ext: DUMMY_SP } } - pub fn inputs(&self) -> &[Ty<'_>] { + pub fn inputs(&self) -> &[Ty<'hir>] { if self.parenthesized { for arg in self.args { match arg { @@ -549,7 +549,7 @@ impl<'hir> Generics<'hir> { &NOPE } - pub fn get_named(&self, name: Symbol) -> Option<&GenericParam<'_>> { + pub fn get_named(&self, name: Symbol) -> Option<&GenericParam<'hir>> { for param in self.params { if name == param.name.ident().name { return Some(param); @@ -608,7 +608,7 @@ impl<'hir> Generics<'hir> { pub fn bounds_for_param( &self, param_def_id: LocalDefId, - ) -> impl Iterator> { + ) -> impl Iterator> { self.predicates.iter().filter_map(move |pred| match pred { WherePredicate::BoundPredicate(bp) if bp.is_param_bound(param_def_id.to_def_id()) => { Some(bp) diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index e7b966758d6e0..46c94344a8711 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -218,7 +218,7 @@ pub(crate) fn build_external_trait(cx: &mut DocContext<'_>, did: DefId) -> clean } } -fn build_external_function(cx: &mut DocContext<'_>, did: DefId) -> clean::Function { +fn build_external_function<'tcx>(cx: &mut DocContext<'tcx>, did: DefId) -> clean::Function { let sig = cx.tcx.fn_sig(did); let predicates = cx.tcx.predicates_of(did); diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 04d97e0dabfa0..461037dd3736a 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -44,12 +44,12 @@ use utils::*; pub(crate) use self::types::*; pub(crate) use self::utils::{get_auto_trait_and_blanket_impls, krate, register_res}; -pub(crate) trait Clean { - fn clean(&self, cx: &mut DocContext<'_>) -> T; +pub(crate) trait Clean<'tcx, T> { + fn clean(&self, cx: &mut DocContext<'tcx>) -> T; } -impl Clean for DocModule<'_> { - fn clean(&self, cx: &mut DocContext<'_>) -> Item { +impl<'tcx> Clean<'tcx, Item> for DocModule<'tcx> { + fn clean(&self, cx: &mut DocContext<'tcx>) -> Item { let mut items: Vec = vec![]; items.extend( self.foreigns @@ -89,14 +89,14 @@ impl Clean for DocModule<'_> { } } -impl Clean for [ast::Attribute] { +impl<'tcx> Clean<'tcx, Attributes> for [ast::Attribute] { fn clean(&self, _cx: &mut DocContext<'_>) -> Attributes { Attributes::from_ast(self, None) } } -impl Clean> for hir::GenericBound<'_> { - fn clean(&self, cx: &mut DocContext<'_>) -> Option { +impl<'tcx> Clean<'tcx, Option> for hir::GenericBound<'tcx> { + fn clean(&self, cx: &mut DocContext<'tcx>) -> Option { Some(match *self { hir::GenericBound::Outlives(lt) => GenericBound::Outlives(lt.clean(cx)), hir::GenericBound::LangItemTrait(lang_item, span, _, generic_args) => { @@ -131,9 +131,9 @@ impl Clean> for hir::GenericBound<'_> { } } -fn clean_trait_ref_with_bindings( - cx: &mut DocContext<'_>, - trait_ref: ty::TraitRef<'_>, +fn clean_trait_ref_with_bindings<'tcx>( + cx: &mut DocContext<'tcx>, + trait_ref: ty::TraitRef<'tcx>, bindings: &[TypeBinding], ) -> Path { let kind = cx.tcx.def_kind(trait_ref.def_id).into(); @@ -148,15 +148,15 @@ fn clean_trait_ref_with_bindings( path } -impl Clean for ty::TraitRef<'_> { - fn clean(&self, cx: &mut DocContext<'_>) -> Path { +impl<'tcx> Clean<'tcx, Path> for ty::TraitRef<'tcx> { + fn clean(&self, cx: &mut DocContext<'tcx>) -> Path { clean_trait_ref_with_bindings(cx, *self, &[]) } } -fn clean_poly_trait_ref_with_bindings( - cx: &mut DocContext<'_>, - poly_trait_ref: ty::PolyTraitRef<'_>, +fn clean_poly_trait_ref_with_bindings<'tcx>( + cx: &mut DocContext<'tcx>, + poly_trait_ref: ty::PolyTraitRef<'tcx>, bindings: &[TypeBinding], ) -> GenericBound { let poly_trait_ref = poly_trait_ref.lift_to_tcx(cx.tcx).unwrap(); @@ -182,14 +182,14 @@ fn clean_poly_trait_ref_with_bindings( ) } -impl<'tcx> Clean for ty::PolyTraitRef<'tcx> { - fn clean(&self, cx: &mut DocContext<'_>) -> GenericBound { +impl<'tcx> Clean<'tcx, GenericBound> for ty::PolyTraitRef<'tcx> { + fn clean(&self, cx: &mut DocContext<'tcx>) -> GenericBound { clean_poly_trait_ref_with_bindings(cx, *self, &[]) } } -impl Clean for hir::Lifetime { - fn clean(&self, cx: &mut DocContext<'_>) -> Lifetime { +impl<'tcx> Clean<'tcx, Lifetime> for hir::Lifetime { + fn clean(&self, cx: &mut DocContext<'tcx>) -> Lifetime { let def = cx.tcx.named_region(self.hir_id); if let Some( rl::Region::EarlyBound(_, node_id) @@ -205,8 +205,8 @@ impl Clean for hir::Lifetime { } } -impl Clean for hir::ConstArg { - fn clean(&self, cx: &mut DocContext<'_>) -> Constant { +impl<'tcx> Clean<'tcx, Constant> for hir::ConstArg { + fn clean(&self, cx: &mut DocContext<'tcx>) -> Constant { Constant { type_: cx .tcx @@ -217,7 +217,7 @@ impl Clean for hir::ConstArg { } } -impl Clean> for ty::Region<'_> { +impl<'tcx> Clean<'tcx, Option> for ty::Region<'tcx> { fn clean(&self, _cx: &mut DocContext<'_>) -> Option { match **self { ty::ReStatic => Some(Lifetime::statik()), @@ -239,8 +239,8 @@ impl Clean> for ty::Region<'_> { } } -impl Clean> for hir::WherePredicate<'_> { - fn clean(&self, cx: &mut DocContext<'_>) -> Option { +impl<'tcx> Clean<'tcx, Option> for hir::WherePredicate<'tcx> { + fn clean(&self, cx: &mut DocContext<'tcx>) -> Option { if !self.in_where_clause() { return None; } @@ -279,8 +279,8 @@ impl Clean> for hir::WherePredicate<'_> { } } -impl<'a> Clean> for ty::Predicate<'a> { - fn clean(&self, cx: &mut DocContext<'_>) -> Option { +impl<'tcx> Clean<'tcx, Option> for ty::Predicate<'tcx> { + fn clean(&self, cx: &mut DocContext<'tcx>) -> Option { let bound_predicate = self.kind(); match bound_predicate.skip_binder() { ty::PredicateKind::Trait(pred) => bound_predicate.rebind(pred).clean(cx), @@ -300,8 +300,8 @@ impl<'a> Clean> for ty::Predicate<'a> { } } -impl<'a> Clean> for ty::PolyTraitPredicate<'a> { - fn clean(&self, cx: &mut DocContext<'_>) -> Option { +impl<'tcx> Clean<'tcx, Option> for ty::PolyTraitPredicate<'tcx> { + fn clean(&self, cx: &mut DocContext<'tcx>) -> Option { // `T: ~const Destruct` is hidden because `T: Destruct` is a no-op. if self.skip_binder().constness == ty::BoundConstness::ConstIfConst && Some(self.skip_binder().def_id()) == cx.tcx.lang_items().destruct_trait() @@ -318,10 +318,10 @@ impl<'a> Clean> for ty::PolyTraitPredicate<'a> { } } -impl<'tcx> Clean> +impl<'tcx> Clean<'tcx, Option> for ty::OutlivesPredicate, ty::Region<'tcx>> { - fn clean(&self, cx: &mut DocContext<'_>) -> Option { + fn clean(&self, cx: &mut DocContext<'tcx>) -> Option { let ty::OutlivesPredicate(a, b) = self; if a.is_empty() && b.is_empty() { @@ -335,8 +335,10 @@ impl<'tcx> Clean> } } -impl<'tcx> Clean> for ty::OutlivesPredicate, ty::Region<'tcx>> { - fn clean(&self, cx: &mut DocContext<'_>) -> Option { +impl<'tcx> Clean<'tcx, Option> + for ty::OutlivesPredicate, ty::Region<'tcx>> +{ + fn clean(&self, cx: &mut DocContext<'tcx>) -> Option { let ty::OutlivesPredicate(ty, lt) = self; if lt.is_empty() { @@ -351,8 +353,8 @@ impl<'tcx> Clean> for ty::OutlivesPredicate, ty: } } -impl<'tcx> Clean for ty::Term<'tcx> { - fn clean(&self, cx: &mut DocContext<'_>) -> Term { +impl<'tcx> Clean<'tcx, Term> for ty::Term<'tcx> { + fn clean(&self, cx: &mut DocContext<'tcx>) -> Term { match self { ty::Term::Ty(ty) => Term::Type(ty.clean(cx)), ty::Term::Const(c) => Term::Constant(c.clean(cx)), @@ -360,8 +362,8 @@ impl<'tcx> Clean for ty::Term<'tcx> { } } -impl<'tcx> Clean for hir::Term<'tcx> { - fn clean(&self, cx: &mut DocContext<'_>) -> Term { +impl<'tcx> Clean<'tcx, Term> for hir::Term<'tcx> { + fn clean(&self, cx: &mut DocContext<'tcx>) -> Term { match self { hir::Term::Ty(ty) => Term::Type(ty.clean(cx)), hir::Term::Const(c) => { @@ -372,8 +374,8 @@ impl<'tcx> Clean for hir::Term<'tcx> { } } -impl<'tcx> Clean for ty::ProjectionPredicate<'tcx> { - fn clean(&self, cx: &mut DocContext<'_>) -> WherePredicate { +impl<'tcx> Clean<'tcx, WherePredicate> for ty::ProjectionPredicate<'tcx> { + fn clean(&self, cx: &mut DocContext<'tcx>) -> WherePredicate { let ty::ProjectionPredicate { projection_ty, term } = self; WherePredicate::EqPredicate { lhs: projection_ty.clean(cx), rhs: term.clean(cx) } } @@ -381,7 +383,7 @@ impl<'tcx> Clean for ty::ProjectionPredicate<'tcx> { fn clean_projection<'tcx>( ty: ty::ProjectionTy<'tcx>, - cx: &mut DocContext<'_>, + cx: &mut DocContext<'tcx>, def_id: Option, ) -> Type { let lifted = ty.lift_to_tcx(cx.tcx).unwrap(); @@ -401,8 +403,8 @@ fn clean_projection<'tcx>( } } -impl<'tcx> Clean for ty::ProjectionTy<'tcx> { - fn clean(&self, cx: &mut DocContext<'_>) -> Type { +impl<'tcx> Clean<'tcx, Type> for ty::ProjectionTy<'tcx> { + fn clean(&self, cx: &mut DocContext<'tcx>) -> Type { clean_projection(*self, cx, None) } } @@ -414,7 +416,10 @@ fn compute_should_show_cast(self_def_id: Option, trait_: &Path, self_type .map_or(!self_type.is_self_type(), |(id, trait_)| id != trait_) } -fn projection_to_path_segment(ty: ty::ProjectionTy<'_>, cx: &mut DocContext<'_>) -> PathSegment { +fn projection_to_path_segment<'tcx>( + ty: ty::ProjectionTy<'tcx>, + cx: &mut DocContext<'tcx>, +) -> PathSegment { let item = cx.tcx.associated_item(ty.item_def_id); let generics = cx.tcx.generics_of(ty.item_def_id); PathSegment { @@ -426,8 +431,8 @@ fn projection_to_path_segment(ty: ty::ProjectionTy<'_>, cx: &mut DocContext<'_>) } } -impl Clean for ty::GenericParamDef { - fn clean(&self, cx: &mut DocContext<'_>) -> GenericParamDef { +impl<'tcx> Clean<'tcx, GenericParamDef> for ty::GenericParamDef { + fn clean(&self, cx: &mut DocContext<'tcx>) -> GenericParamDef { let (name, kind) = match self.kind { ty::GenericParamDefKind::Lifetime => { (self.name, GenericParamDefKind::Lifetime { outlives: vec![] }) @@ -465,10 +470,10 @@ impl Clean for ty::GenericParamDef { } } -fn clean_generic_param( - cx: &mut DocContext<'_>, - generics: Option<&hir::Generics<'_>>, - param: &hir::GenericParam<'_>, +fn clean_generic_param<'tcx>( + cx: &mut DocContext<'tcx>, + generics: Option<&hir::Generics<'tcx>>, + param: &hir::GenericParam<'tcx>, ) -> GenericParamDef { let (name, kind) = match param.kind { hir::GenericParamKind::Lifetime { .. } => { @@ -536,8 +541,8 @@ fn clean_generic_param( GenericParamDef { name, kind } } -impl Clean for hir::Generics<'_> { - fn clean(&self, cx: &mut DocContext<'_>) -> Generics { +impl<'tcx> Clean<'tcx, Generics> for hir::Generics<'tcx> { + fn clean(&self, cx: &mut DocContext<'tcx>) -> Generics { // Synthetic type-parameters are inserted after normal ones. // In order for normal parameters to be able to refer to synthetic ones, // scans them first. @@ -618,10 +623,10 @@ impl Clean for hir::Generics<'_> { } } -fn clean_ty_generics( - cx: &mut DocContext<'_>, +fn clean_ty_generics<'tcx>( + cx: &mut DocContext<'tcx>, gens: &ty::Generics, - preds: ty::GenericPredicates<'_>, + preds: ty::GenericPredicates<'tcx>, ) -> Generics { // Don't populate `cx.impl_trait_bounds` before `clean`ning `where` clauses, // since `Clean for ty::Predicate` would consume them. @@ -784,13 +789,13 @@ fn clean_ty_generics( } } -fn clean_fn_or_proc_macro( - item: &hir::Item<'_>, - sig: &hir::FnSig<'_>, - generics: &hir::Generics<'_>, +fn clean_fn_or_proc_macro<'tcx>( + item: &hir::Item<'tcx>, + sig: &hir::FnSig<'tcx>, + generics: &hir::Generics<'tcx>, body_id: hir::BodyId, name: &mut Symbol, - cx: &mut DocContext<'_>, + cx: &mut DocContext<'tcx>, ) -> ItemKind { let attrs = cx.tcx.hir().attrs(item.hir_id()); let macro_kind = attrs.iter().find_map(|a| { @@ -868,10 +873,10 @@ fn clean_fn_decl_legacy_const_generics(func: &mut Function, attrs: &[ast::Attrib } } -fn clean_function( - cx: &mut DocContext<'_>, - sig: &hir::FnSig<'_>, - generics: &hir::Generics<'_>, +fn clean_function<'tcx>( + cx: &mut DocContext<'tcx>, + sig: &hir::FnSig<'tcx>, + generics: &hir::Generics<'tcx>, body_id: hir::BodyId, ) -> Function { let (generics, decl) = enter_impl_trait(cx, |cx| { @@ -884,9 +889,9 @@ fn clean_function( Function { decl, generics } } -fn clean_args_from_types_and_names( - cx: &mut DocContext<'_>, - types: &[hir::Ty<'_>], +fn clean_args_from_types_and_names<'tcx>( + cx: &mut DocContext<'tcx>, + types: &[hir::Ty<'tcx>], names: &[Ident], ) -> Arguments { Arguments { @@ -904,9 +909,9 @@ fn clean_args_from_types_and_names( } } -fn clean_args_from_types_and_body_id( - cx: &mut DocContext<'_>, - types: &[hir::Ty<'_>], +fn clean_args_from_types_and_body_id<'tcx>( + cx: &mut DocContext<'tcx>, + types: &[hir::Ty<'tcx>], body_id: hir::BodyId, ) -> Arguments { let body = cx.tcx.hir().body(body_id); @@ -924,18 +929,18 @@ fn clean_args_from_types_and_body_id( } } -fn clean_fn_decl_with_args( - cx: &mut DocContext<'_>, - decl: &hir::FnDecl<'_>, +fn clean_fn_decl_with_args<'tcx>( + cx: &mut DocContext<'tcx>, + decl: &hir::FnDecl<'tcx>, args: Arguments, ) -> FnDecl { FnDecl { inputs: args, output: decl.output.clean(cx), c_variadic: decl.c_variadic } } -fn clean_fn_decl_from_did_and_sig( - cx: &mut DocContext<'_>, +fn clean_fn_decl_from_did_and_sig<'tcx>( + cx: &mut DocContext<'tcx>, did: Option, - sig: ty::PolyFnSig<'_>, + sig: ty::PolyFnSig<'tcx>, ) -> FnDecl { let mut names = did.map_or(&[] as &[_], |did| cx.tcx.fn_arg_names(did)).iter(); @@ -964,8 +969,8 @@ fn clean_fn_decl_from_did_and_sig( } } -impl Clean for hir::FnRetTy<'_> { - fn clean(&self, cx: &mut DocContext<'_>) -> FnRetTy { +impl<'tcx> Clean<'tcx, FnRetTy> for hir::FnRetTy<'tcx> { + fn clean(&self, cx: &mut DocContext<'tcx>) -> FnRetTy { match *self { Self::Return(ref typ) => Return(typ.clean(cx)), Self::DefaultReturn(..) => DefaultReturn, @@ -973,8 +978,8 @@ impl Clean for hir::FnRetTy<'_> { } } -impl Clean for hir::IsAuto { - fn clean(&self, _: &mut DocContext<'_>) -> bool { +impl<'tcx> Clean<'tcx, bool> for hir::IsAuto { + fn clean(&self, _: &mut DocContext<'tcx>) -> bool { match *self { hir::IsAuto::Yes => true, hir::IsAuto::No => false, @@ -982,16 +987,16 @@ impl Clean for hir::IsAuto { } } -impl Clean for hir::TraitRef<'_> { - fn clean(&self, cx: &mut DocContext<'_>) -> Path { +impl<'tcx> Clean<'tcx, Path> for hir::TraitRef<'tcx> { + fn clean(&self, cx: &mut DocContext<'tcx>) -> Path { let path = self.path.clean(cx); register_res(cx, path.res); path } } -impl Clean for hir::PolyTraitRef<'_> { - fn clean(&self, cx: &mut DocContext<'_>) -> PolyTrait { +impl<'tcx> Clean<'tcx, PolyTrait> for hir::PolyTraitRef<'tcx> { + fn clean(&self, cx: &mut DocContext<'tcx>) -> PolyTrait { PolyTrait { trait_: self.trait_ref.clean(cx), generic_params: self @@ -1003,8 +1008,8 @@ impl Clean for hir::PolyTraitRef<'_> { } } -impl Clean for hir::TraitItem<'_> { - fn clean(&self, cx: &mut DocContext<'_>) -> Item { +impl<'tcx> Clean<'tcx, Item> for hir::TraitItem<'tcx> { + fn clean(&self, cx: &mut DocContext<'tcx>) -> Item { let local_did = self.def_id.to_def_id(); cx.with_param_env(local_did, |cx| { let inner = match self.kind { @@ -1050,8 +1055,8 @@ impl Clean for hir::TraitItem<'_> { } } -impl Clean for hir::ImplItem<'_> { - fn clean(&self, cx: &mut DocContext<'_>) -> Item { +impl<'tcx> Clean<'tcx, Item> for hir::ImplItem<'tcx> { + fn clean(&self, cx: &mut DocContext<'tcx>) -> Item { let local_did = self.def_id.to_def_id(); cx.with_param_env(local_did, |cx| { let inner = match self.kind { @@ -1091,8 +1096,8 @@ impl Clean for hir::ImplItem<'_> { } } -impl Clean for ty::AssocItem { - fn clean(&self, cx: &mut DocContext<'_>) -> Item { +impl<'tcx> Clean<'tcx, Item> for ty::AssocItem { + fn clean(&self, cx: &mut DocContext<'tcx>) -> Item { let tcx = cx.tcx; let kind = match self.kind { ty::AssocKind::Const => { @@ -1282,7 +1287,7 @@ impl Clean for ty::AssocItem { } } -fn clean_qpath(hir_ty: &hir::Ty<'_>, cx: &mut DocContext<'_>) -> Type { +fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type { let hir::Ty { hir_id: _, span, ref kind } = *hir_ty; let hir::TyKind::Path(qpath) = kind else { unreachable!() }; @@ -1352,7 +1357,10 @@ fn clean_qpath(hir_ty: &hir::Ty<'_>, cx: &mut DocContext<'_>) -> Type { } } -fn maybe_expand_private_type_alias(cx: &mut DocContext<'_>, path: &hir::Path<'_>) -> Option { +fn maybe_expand_private_type_alias<'tcx>( + cx: &mut DocContext<'tcx>, + path: &hir::Path<'tcx>, +) -> Option { let Res::Def(DefKind::TyAlias, def_id) = path.res else { return None }; // Substitute private type aliases let def_id = def_id.as_local()?; @@ -1435,8 +1443,8 @@ fn maybe_expand_private_type_alias(cx: &mut DocContext<'_>, path: &hir::Path<'_> Some(cx.enter_alias(substs, |cx| ty.clean(cx))) } -impl Clean for hir::Ty<'_> { - fn clean(&self, cx: &mut DocContext<'_>) -> Type { +impl<'tcx> Clean<'tcx, Type> for hir::Ty<'tcx> { + fn clean(&self, cx: &mut DocContext<'tcx>) -> Type { use rustc_hir::*; match self.kind { @@ -1530,7 +1538,7 @@ fn normalize<'tcx>(cx: &mut DocContext<'tcx>, ty: Ty<'_>) -> Option> { } } -fn clean_ty<'tcx>(this: Ty<'tcx>, cx: &mut DocContext<'_>, def_id: Option) -> Type { +fn clean_ty<'tcx>(this: Ty<'tcx>, cx: &mut DocContext<'tcx>, def_id: Option) -> Type { trace!("cleaning type: {:?}", this); let ty = normalize(cx, this).unwrap_or(this); match *ty.kind() { @@ -1715,14 +1723,14 @@ fn clean_ty<'tcx>(this: Ty<'tcx>, cx: &mut DocContext<'_>, def_id: Option } } -impl<'tcx> Clean for Ty<'tcx> { - fn clean(&self, cx: &mut DocContext<'_>) -> Type { +impl<'tcx> Clean<'tcx, Type> for Ty<'tcx> { + fn clean(&self, cx: &mut DocContext<'tcx>) -> Type { clean_ty(*self, cx, None) } } -impl<'tcx> Clean for ty::Const<'tcx> { - fn clean(&self, cx: &mut DocContext<'_>) -> Constant { +impl<'tcx> Clean<'tcx, Constant> for ty::Const<'tcx> { + fn clean(&self, cx: &mut DocContext<'tcx>) -> Constant { // FIXME: instead of storing the stringified expression, store `self` directly instead. Constant { type_: self.ty().clean(cx), @@ -1731,15 +1739,15 @@ impl<'tcx> Clean for ty::Const<'tcx> { } } -impl Clean for hir::FieldDef<'_> { - fn clean(&self, cx: &mut DocContext<'_>) -> Item { +impl<'tcx> Clean<'tcx, Item> for hir::FieldDef<'tcx> { + fn clean(&self, cx: &mut DocContext<'tcx>) -> Item { let def_id = cx.tcx.hir().local_def_id(self.hir_id).to_def_id(); clean_field(def_id, self.ident.name, self.ty.clean(cx), cx) } } -impl Clean for ty::FieldDef { - fn clean(&self, cx: &mut DocContext<'_>) -> Item { +impl<'tcx> Clean<'tcx, Item> for ty::FieldDef { + fn clean(&self, cx: &mut DocContext<'tcx>) -> Item { clean_field(self.did, self.name, cx.tcx.type_of(self.did).clean(cx), cx) } } @@ -1765,7 +1773,7 @@ fn is_field_vis_inherited(tcx: TyCtxt<'_>, def_id: DefId) -> bool { } } -impl Clean for ty::Visibility { +impl<'tcx> Clean<'tcx, Visibility> for ty::Visibility { fn clean(&self, _cx: &mut DocContext<'_>) -> Visibility { match *self { ty::Visibility::Public => Visibility::Public, @@ -1779,8 +1787,8 @@ impl Clean for ty::Visibility { } } -impl Clean for rustc_hir::VariantData<'_> { - fn clean(&self, cx: &mut DocContext<'_>) -> VariantStruct { +impl<'tcx> Clean<'tcx, VariantStruct> for rustc_hir::VariantData<'tcx> { + fn clean(&self, cx: &mut DocContext<'tcx>) -> VariantStruct { VariantStruct { struct_type: CtorKind::from_hir(self), fields: self.fields().iter().map(|x| x.clean(cx)).collect(), @@ -1789,14 +1797,14 @@ impl Clean for rustc_hir::VariantData<'_> { } } -impl Clean> for hir::VariantData<'_> { - fn clean(&self, cx: &mut DocContext<'_>) -> Vec { +impl<'tcx> Clean<'tcx, Vec> for hir::VariantData<'tcx> { + fn clean(&self, cx: &mut DocContext<'tcx>) -> Vec { self.fields().iter().map(|x| x.clean(cx)).collect() } } -impl Clean for ty::VariantDef { - fn clean(&self, cx: &mut DocContext<'_>) -> Item { +impl<'tcx> Clean<'tcx, Item> for ty::VariantDef { + fn clean(&self, cx: &mut DocContext<'tcx>) -> Item { let kind = match self.ctor_kind { CtorKind::Const => Variant::CLike, CtorKind::Fn => { @@ -1815,8 +1823,8 @@ impl Clean for ty::VariantDef { } } -impl Clean for hir::VariantData<'_> { - fn clean(&self, cx: &mut DocContext<'_>) -> Variant { +impl<'tcx> Clean<'tcx, Variant> for hir::VariantData<'tcx> { + fn clean(&self, cx: &mut DocContext<'tcx>) -> Variant { match self { hir::VariantData::Struct(..) => Variant::Struct(self.clean(cx)), hir::VariantData::Tuple(..) => Variant::Tuple(self.clean(cx)), @@ -1825,14 +1833,14 @@ impl Clean for hir::VariantData<'_> { } } -impl Clean for hir::Path<'_> { - fn clean(&self, cx: &mut DocContext<'_>) -> Path { +impl<'tcx> Clean<'tcx, Path> for hir::Path<'tcx> { + fn clean(&self, cx: &mut DocContext<'tcx>) -> Path { Path { res: self.res, segments: self.segments.iter().map(|x| x.clean(cx)).collect() } } } -impl Clean for hir::GenericArgs<'_> { - fn clean(&self, cx: &mut DocContext<'_>) -> GenericArgs { +impl<'tcx> Clean<'tcx, GenericArgs> for hir::GenericArgs<'tcx> { + fn clean(&self, cx: &mut DocContext<'tcx>) -> GenericArgs { if self.parenthesized { let output = self.bindings[0].ty().clean(cx); let output = @@ -1859,14 +1867,14 @@ impl Clean for hir::GenericArgs<'_> { } } -impl Clean for hir::PathSegment<'_> { - fn clean(&self, cx: &mut DocContext<'_>) -> PathSegment { +impl<'tcx> Clean<'tcx, PathSegment> for hir::PathSegment<'tcx> { + fn clean(&self, cx: &mut DocContext<'tcx>) -> PathSegment { PathSegment { name: self.ident.name, args: self.args().clean(cx) } } } -impl Clean for hir::BareFnTy<'_> { - fn clean(&self, cx: &mut DocContext<'_>) -> BareFunctionDecl { +impl<'tcx> Clean<'tcx, BareFunctionDecl> for hir::BareFnTy<'tcx> { + fn clean(&self, cx: &mut DocContext<'tcx>) -> BareFunctionDecl { let (generic_params, decl) = enter_impl_trait(cx, |cx| { // NOTE: generics must be cleaned before args let generic_params = @@ -1879,9 +1887,9 @@ impl Clean for hir::BareFnTy<'_> { } } -fn clean_maybe_renamed_item( - cx: &mut DocContext<'_>, - item: &hir::Item<'_>, +fn clean_maybe_renamed_item<'tcx>( + cx: &mut DocContext<'tcx>, + item: &hir::Item<'tcx>, renamed: Option, ) -> Vec { use hir::ItemKind; @@ -1965,8 +1973,8 @@ fn clean_maybe_renamed_item( }) } -impl Clean for hir::Variant<'_> { - fn clean(&self, cx: &mut DocContext<'_>) -> Item { +impl<'tcx> Clean<'tcx, Item> for hir::Variant<'tcx> { + fn clean(&self, cx: &mut DocContext<'tcx>) -> Item { let kind = VariantItem(self.data.clean(cx)); let what_rustc_thinks = Item::from_hir_id_and_parts(self.id, Some(self.ident.name), kind, cx); @@ -1975,7 +1983,11 @@ impl Clean for hir::Variant<'_> { } } -fn clean_impl(impl_: &hir::Impl<'_>, hir_id: hir::HirId, cx: &mut DocContext<'_>) -> Vec { +fn clean_impl<'tcx>( + impl_: &hir::Impl<'tcx>, + hir_id: hir::HirId, + cx: &mut DocContext<'tcx>, +) -> Vec { let tcx = cx.tcx; let mut ret = Vec::new(); let trait_ = impl_.of_trait.as_ref().map(|t| t.clean(cx)); @@ -2013,11 +2025,11 @@ fn clean_impl(impl_: &hir::Impl<'_>, hir_id: hir::HirId, cx: &mut DocContext<'_> ret } -fn clean_extern_crate( - krate: &hir::Item<'_>, +fn clean_extern_crate<'tcx>( + krate: &hir::Item<'tcx>, name: Symbol, orig_name: Option, - cx: &mut DocContext<'_>, + cx: &mut DocContext<'tcx>, ) -> Vec { // this is the ID of the `extern crate` statement let cnum = cx.tcx.extern_mod_stmt_cnum(krate.def_id).unwrap_or(LOCAL_CRATE); @@ -2063,12 +2075,12 @@ fn clean_extern_crate( }] } -fn clean_use_statement( - import: &hir::Item<'_>, +fn clean_use_statement<'tcx>( + import: &hir::Item<'tcx>, name: Symbol, - path: &hir::Path<'_>, + path: &hir::Path<'tcx>, kind: hir::UseKind, - cx: &mut DocContext<'_>, + cx: &mut DocContext<'tcx>, ) -> Vec { // We need this comparison because some imports (for std types for example) // are "inserted" as well but directly by the compiler and they should not be @@ -2176,9 +2188,9 @@ fn clean_use_statement( vec![Item::from_def_id_and_parts(import.def_id.to_def_id(), None, ImportItem(inner), cx)] } -fn clean_maybe_renamed_foreign_item( - cx: &mut DocContext<'_>, - item: &hir::ForeignItem<'_>, +fn clean_maybe_renamed_foreign_item<'tcx>( + cx: &mut DocContext<'tcx>, + item: &hir::ForeignItem<'tcx>, renamed: Option, ) -> Item { let def_id = item.def_id.to_def_id(); @@ -2209,8 +2221,8 @@ fn clean_maybe_renamed_foreign_item( }) } -impl Clean for hir::TypeBinding<'_> { - fn clean(&self, cx: &mut DocContext<'_>) -> TypeBinding { +impl<'tcx> Clean<'tcx, TypeBinding> for hir::TypeBinding<'tcx> { + fn clean(&self, cx: &mut DocContext<'tcx>) -> TypeBinding { TypeBinding { assoc: PathSegment { name: self.ident.name, args: self.gen_args.clean(cx) }, kind: self.kind.clean(cx), @@ -2218,8 +2230,8 @@ impl Clean for hir::TypeBinding<'_> { } } -impl Clean for hir::TypeBindingKind<'_> { - fn clean(&self, cx: &mut DocContext<'_>) -> TypeBindingKind { +impl<'tcx> Clean<'tcx, TypeBindingKind> for hir::TypeBindingKind<'tcx> { + fn clean(&self, cx: &mut DocContext<'tcx>) -> TypeBindingKind { match *self { hir::TypeBindingKind::Equality { ref term } => { TypeBindingKind::Equality { term: term.clean(cx) } diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 7a12ea0d5c215..285520071c963 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -75,9 +75,9 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate { Crate { module, primitives, external_traits: cx.external_traits.clone() } } -pub(crate) fn substs_to_args( - cx: &mut DocContext<'_>, - substs: &[ty::subst::GenericArg<'_>], +pub(crate) fn substs_to_args<'tcx>( + cx: &mut DocContext<'tcx>, + substs: &[ty::subst::GenericArg<'tcx>], mut skip_first: bool, ) -> Vec { substs @@ -99,12 +99,12 @@ pub(crate) fn substs_to_args( .collect() } -fn external_generic_args( - cx: &mut DocContext<'_>, +fn external_generic_args<'tcx>( + cx: &mut DocContext<'tcx>, did: DefId, has_self: bool, bindings: Vec, - substs: SubstsRef<'_>, + substs: SubstsRef<'tcx>, ) -> GenericArgs { let args = substs_to_args(cx, &substs, has_self); @@ -127,12 +127,12 @@ fn external_generic_args( } } -pub(super) fn external_path( - cx: &mut DocContext<'_>, +pub(super) fn external_path<'tcx>( + cx: &mut DocContext<'tcx>, did: DefId, has_self: bool, bindings: Vec, - substs: SubstsRef<'_>, + substs: SubstsRef<'tcx>, ) -> Path { let def_kind = cx.tcx.def_kind(did); let name = cx.tcx.item_name(did); @@ -439,9 +439,9 @@ pub(crate) fn resolve_use_source(cx: &mut DocContext<'_>, path: Path) -> ImportS } } -pub(crate) fn enter_impl_trait(cx: &mut DocContext<'_>, f: F) -> R +pub(crate) fn enter_impl_trait<'tcx, F, R>(cx: &mut DocContext<'tcx>, f: F) -> R where - F: FnOnce(&mut DocContext<'_>) -> R, + F: FnOnce(&mut DocContext<'tcx>) -> R, { let old_bounds = mem::take(&mut cx.impl_trait_bounds); let r = f(cx); From cec6dfcd67b2841e44248a070fd33895330bdfcf Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 23 May 2022 18:47:05 +0200 Subject: [PATCH 08/11] explain how to turn integers into fn ptrs (with an intermediate raw ptr, not a direct transmute) --- library/core/src/intrinsics.rs | 3 +++ library/core/src/primitive_docs.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 88e4262922dc5..6ba359f6edcd8 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -930,6 +930,9 @@ extern "rust-intrinsic" { /// fn foo() -> i32 { /// 0 /// } + /// // Crucially, we `as`-cast to a raw pointer before `transmute`ing to a function pointer. + /// // This avoids an integer-to-pointer `transmute`, which can be problematic. + /// // Transmuting between raw pointers and function pointers (i.e., two pointer types) is fine. /// let pointer = foo as *const (); /// let function = unsafe { /// std::mem::transmute::<*const (), fn() -> i32>(pointer) diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index ac4e668112b94..147312b9720d4 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -1338,6 +1338,32 @@ mod prim_ref {} /// is a reference to the function-specific ZST. `&bar` is basically never what you /// want when `bar` is a function. /// +/// ### Casting to and from integers +/// +/// You cast function pointers directly to integers: +/// +/// ```rust +/// let fnptr: fn(i32) -> i32 = |x| x+2; +/// let fnptr_addr = fnptr as usize; +/// ``` +/// +/// However, a direct cast back is not possible. You need to use `transmute`: +/// +/// ```rust +/// # let fnptr: fn(i32) -> i32 = |x| x+2; +/// # let fnptr_addr = fnptr as usize; +/// let fnptr = fnptr_addr as *const (); +/// let fnptr: fn(i32) -> i32 = unsafe { std::mem::transmute(fnptr) }; +/// assert_eq!(fnptr(40), 42); +/// ``` +/// +/// Crucially, we `as`-cast to a raw pointer before `transmute`ing to a function pointer. +/// This avoids an integer-to-pointer `transmute`, which can be problematic. +/// Transmuting between raw pointers and function pointers (i.e., two pointer types) is fine. +/// +/// Note that all of this is not portable to platforms where function pointers and data pointers +/// have different sizes. +/// /// ### Traits /// /// Function pointers implement the following traits: From 5137d15f91d5778a0d037e5bc4f1f70d9b013aa7 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 23 May 2022 19:09:23 +0200 Subject: [PATCH 09/11] sync primitive_docs --- library/std/src/primitive_docs.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/library/std/src/primitive_docs.rs b/library/std/src/primitive_docs.rs index ac4e668112b94..147312b9720d4 100644 --- a/library/std/src/primitive_docs.rs +++ b/library/std/src/primitive_docs.rs @@ -1338,6 +1338,32 @@ mod prim_ref {} /// is a reference to the function-specific ZST. `&bar` is basically never what you /// want when `bar` is a function. /// +/// ### Casting to and from integers +/// +/// You cast function pointers directly to integers: +/// +/// ```rust +/// let fnptr: fn(i32) -> i32 = |x| x+2; +/// let fnptr_addr = fnptr as usize; +/// ``` +/// +/// However, a direct cast back is not possible. You need to use `transmute`: +/// +/// ```rust +/// # let fnptr: fn(i32) -> i32 = |x| x+2; +/// # let fnptr_addr = fnptr as usize; +/// let fnptr = fnptr_addr as *const (); +/// let fnptr: fn(i32) -> i32 = unsafe { std::mem::transmute(fnptr) }; +/// assert_eq!(fnptr(40), 42); +/// ``` +/// +/// Crucially, we `as`-cast to a raw pointer before `transmute`ing to a function pointer. +/// This avoids an integer-to-pointer `transmute`, which can be problematic. +/// Transmuting between raw pointers and function pointers (i.e., two pointer types) is fine. +/// +/// Note that all of this is not portable to platforms where function pointers and data pointers +/// have different sizes. +/// /// ### Traits /// /// Function pointers implement the following traits: From 44410efe43ac2ee2165cb1cf30105b5b9166897b Mon Sep 17 00:00:00 2001 From: Jakob Degen Date: Wed, 13 Apr 2022 07:08:58 -0400 Subject: [PATCH 10/11] Modify MIR building to drop `foo` in `[foo; 0]` --- .../src/build/expr/as_rvalue.rs | 50 +++++++- compiler/rustc_mir_build/src/build/scope.rs | 1 + src/test/ui/drop/repeat-drop-2.rs | 15 +++ src/test/ui/drop/repeat-drop-2.stderr | 29 +++++ src/test/ui/drop/repeat-drop.rs | 117 ++++++++++++++++++ 5 files changed, 207 insertions(+), 5 deletions(-) create mode 100644 src/test/ui/drop/repeat-drop-2.rs create mode 100644 src/test/ui/drop/repeat-drop-2.stderr create mode 100644 src/test/ui/drop/repeat-drop.rs diff --git a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs index 0fd67f15b7501..2f9834df9d335 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs @@ -52,11 +52,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }) } ExprKind::Repeat { value, count } => { - let value_operand = unpack!( - block = - this.as_operand(block, scope, &this.thir[value], None, NeedsTemporary::No) - ); - block.and(Rvalue::Repeat(value_operand, count)) + if Some(0) == count.try_eval_usize(this.tcx, this.param_env) { + this.build_zero_repeat(block, value, scope, source_info) + } else { + let value_operand = unpack!( + block = this.as_operand( + block, + scope, + &this.thir[value], + None, + NeedsTemporary::No + ) + ); + block.and(Rvalue::Repeat(value_operand, count)) + } } ExprKind::Binary { op, lhs, rhs } => { let lhs = unpack!( @@ -515,6 +524,37 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } + fn build_zero_repeat( + &mut self, + mut block: BasicBlock, + value: ExprId, + scope: Option, + outer_source_info: SourceInfo, + ) -> BlockAnd> { + let this = self; + let value = &this.thir[value]; + let elem_ty = value.ty; + if let Some(Category::Constant) = Category::of(&value.kind) { + // Repeating a const does nothing + } else { + // For a non-const, we may need to generate an appropriate `Drop` + let value_operand = + unpack!(block = this.as_operand(block, scope, value, None, NeedsTemporary::No)); + if let Operand::Move(to_drop) = value_operand { + let success = this.cfg.start_new_block(); + this.cfg.terminate( + block, + outer_source_info, + TerminatorKind::Drop { place: to_drop, target: success, unwind: None }, + ); + this.diverge_from(block); + block = success; + } + this.record_operands_moved(&[value_operand]); + } + block.and(Rvalue::Aggregate(Box::new(AggregateKind::Array(elem_ty)), Vec::new())) + } + fn limit_capture_mutability( &mut self, upvar_span: Span, diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs index 53f9706f021ee..a5f8a5847c270 100644 --- a/compiler/rustc_mir_build/src/build/scope.rs +++ b/compiler/rustc_mir_build/src/build/scope.rs @@ -1033,6 +1033,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.block_data(start).terminator().kind, TerminatorKind::Assert { .. } | TerminatorKind::Call { .. } + | TerminatorKind::Drop { .. } | TerminatorKind::DropAndReplace { .. } | TerminatorKind::FalseUnwind { .. } | TerminatorKind::InlineAsm { .. } diff --git a/src/test/ui/drop/repeat-drop-2.rs b/src/test/ui/drop/repeat-drop-2.rs new file mode 100644 index 0000000000000..2e7855328ecbf --- /dev/null +++ b/src/test/ui/drop/repeat-drop-2.rs @@ -0,0 +1,15 @@ +fn borrowck_catch() { + let foo = String::new(); + let _bar = foo; + let _baz = [foo; 0]; //~ ERROR use of moved value: `foo` [E0382] +} + +const _: [String; 0] = [String::new(); 0]; +//~^ ERROR destructors cannot be evaluated at compile-time [E0493] + +fn must_be_init() { + let x: u8; + let _ = [x; 0]; //~ ERROR: use of possibly-uninitialized variable: `x` +} + +fn main() {} diff --git a/src/test/ui/drop/repeat-drop-2.stderr b/src/test/ui/drop/repeat-drop-2.stderr new file mode 100644 index 0000000000000..cdc58180c37b4 --- /dev/null +++ b/src/test/ui/drop/repeat-drop-2.stderr @@ -0,0 +1,29 @@ +error[E0382]: use of moved value: `foo` + --> $DIR/repeat-drop-2.rs:4:17 + | +LL | let foo = String::new(); + | --- move occurs because `foo` has type `String`, which does not implement the `Copy` trait +LL | let _bar = foo; + | --- value moved here +LL | let _baz = [foo; 0]; + | ^^^ value used here after move + +error[E0493]: destructors cannot be evaluated at compile-time + --> $DIR/repeat-drop-2.rs:7:25 + | +LL | const _: [String; 0] = [String::new(); 0]; + | -^^^^^^^^^^^^^---- + | || + | |constants cannot evaluate destructors + | value is dropped here + +error[E0381]: use of possibly-uninitialized variable: `x` + --> $DIR/repeat-drop-2.rs:12:14 + | +LL | let _ = [x; 0]; + | ^ use of possibly-uninitialized `x` + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0381, E0382, E0493. +For more information about an error, try `rustc --explain E0381`. diff --git a/src/test/ui/drop/repeat-drop.rs b/src/test/ui/drop/repeat-drop.rs new file mode 100644 index 0000000000000..f6a4a06e2507e --- /dev/null +++ b/src/test/ui/drop/repeat-drop.rs @@ -0,0 +1,117 @@ +// run-pass + +static mut CHECK: usize = 0; + +struct DropChecker(usize); + +impl Drop for DropChecker { + fn drop(&mut self) { + unsafe { + if CHECK != self.0 - 1 { + panic!("Found {}, should have found {}", CHECK, self.0 - 1); + } + CHECK = self.0; + } + } +} + +macro_rules! check_drops { + ($l:literal) => { + unsafe { assert_eq!(CHECK, $l) } + }; +} + +struct DropPanic; + +impl Drop for DropPanic { + fn drop(&mut self) { + panic!() + } +} + +fn value_zero() { + unsafe { CHECK = 0 }; + let foo = DropChecker(1); + let v: [DropChecker; 0] = [foo; 0]; + check_drops!(1); + std::mem::drop(v); + check_drops!(1); +} + +fn value_one() { + unsafe { CHECK = 0 }; + let foo = DropChecker(1); + let v: [DropChecker; 1] = [foo; 1]; + check_drops!(0); + std::mem::drop(v); + check_drops!(1); +} + +const DROP_CHECKER: DropChecker = DropChecker(1); + +fn const_zero() { + unsafe { CHECK = 0 }; + let v: [DropChecker; 0] = [DROP_CHECKER; 0]; + check_drops!(0); + std::mem::drop(v); + check_drops!(0); +} + +fn const_one() { + unsafe { CHECK = 0 }; + let v: [DropChecker; 1] = [DROP_CHECKER; 1]; + check_drops!(0); + std::mem::drop(v); + check_drops!(1); +} + +fn const_generic_zero() { + unsafe { CHECK = 0 }; + let v: [DropChecker; N] = [DROP_CHECKER; N]; + check_drops!(0); + std::mem::drop(v); + check_drops!(0); +} + +fn const_generic_one() { + unsafe { CHECK = 0 }; + let v: [DropChecker; N] = [DROP_CHECKER; N]; + check_drops!(0); + std::mem::drop(v); + check_drops!(1); +} + +// Make sure that things are allowed to promote as expected + +fn allow_promote() { + unsafe { CHECK = 0 }; + let foo = DropChecker(1); + let v: &'static [DropChecker; 0] = &[foo; 0]; + check_drops!(1); + std::mem::drop(v); + check_drops!(1); +} + +// Verify that unwinding in the drop causes the right things to drop in the right order +fn on_unwind() { + unsafe { CHECK = 0 }; + std::panic::catch_unwind(|| { + let panic = DropPanic; + let _local = DropChecker(2); + let _v = (DropChecker(1), [panic; 0]); + std::process::abort(); + }) + .unwrap_err(); + check_drops!(2); +} + +fn main() { + value_zero(); + value_one(); + const_zero(); + const_one(); + const_generic_zero::<0>(); + const_generic_one::<1>(); + allow_promote(); + on_unwind(); +} From 9be37b2d3fc0b3852a7f9b134197dec895030201 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 22 May 2022 16:06:36 -0700 Subject: [PATCH 11/11] Parse expression after `else` as a condition if followed by `{` --- compiler/rustc_parse/src/parser/expr.rs | 62 +++++++++++++++++++++++-- src/test/ui/parser/else-no-if.rs | 32 +++++++++++++ src/test/ui/parser/else-no-if.stderr | 58 +++++++++++++++++++++++ 3 files changed, 147 insertions(+), 5 deletions(-) create mode 100644 src/test/ui/parser/else-no-if.rs create mode 100644 src/test/ui/parser/else-no-if.stderr diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index b5467c659a205..37e3465694131 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2010,6 +2010,12 @@ impl<'a> Parser<'a> { Ok(self.mk_expr(blk.span, ExprKind::Block(blk, opt_label), attrs)) } + /// Parse a block which takes no attributes and has no label + fn parse_simple_block(&mut self) -> PResult<'a, P> { + let blk = self.parse_block()?; + Ok(self.mk_expr(blk.span, ExprKind::Block(blk, None), AttrVec::new())) + } + /// Recover on an explicitly quantified closure expression, e.g., `for<'a> |x: &'a u8| *x + 1`. fn recover_quantified_closure_expr(&mut self, attrs: AttrVec) -> PResult<'a, P> { let lo = self.token.span; @@ -2157,6 +2163,15 @@ impl<'a> Parser<'a> { let lo = self.prev_token.span; let cond = self.parse_cond_expr()?; + self.parse_if_after_cond(attrs, lo, cond) + } + + fn parse_if_after_cond( + &mut self, + attrs: AttrVec, + lo: Span, + cond: P, + ) -> PResult<'a, P> { let missing_then_block_binop_span = || { match cond.kind { ExprKind::Binary(Spanned { span: binop_span, .. }, _, ref right) @@ -2164,7 +2179,6 @@ impl<'a> Parser<'a> { _ => None } }; - // Verify that the parsed `if` condition makes sense as a condition. If it is a block, then // verify that the last statement is either an implicit return (no `;`) or an explicit // return. This won't catch blocks with an explicit `return`, but that would be caught by @@ -2256,15 +2270,53 @@ impl<'a> Parser<'a> { /// Parses an `else { ... }` expression (`else` token already eaten). fn parse_else_expr(&mut self) -> PResult<'a, P> { - let ctx_span = self.prev_token.span; // `else` + let else_span = self.prev_token.span; // `else` let attrs = self.parse_outer_attributes()?.take_for_recovery(); // For recovery. let expr = if self.eat_keyword(kw::If) { self.parse_if_expr(AttrVec::new())? + } else if self.check(&TokenKind::OpenDelim(Delimiter::Brace)) { + self.parse_simple_block()? } else { - let blk = self.parse_block()?; - self.mk_expr(blk.span, ExprKind::Block(blk, None), AttrVec::new()) + let snapshot = self.create_snapshot_for_diagnostic(); + let first_tok = super::token_descr(&self.token); + let first_tok_span = self.token.span; + match self.parse_expr() { + Ok(cond) + // If it's not a free-standing expression, and is followed by a block, + // then it's very likely the condition to an `else if`. + if self.check(&TokenKind::OpenDelim(Delimiter::Brace)) + && classify::expr_requires_semi_to_be_stmt(&cond) => + { + self.struct_span_err(first_tok_span, format!("expected `{{`, found {first_tok}")) + .span_label(else_span, "expected an `if` or a block after this `else`") + .span_suggestion( + cond.span.shrink_to_lo(), + "add an `if` if this is the condition to an chained `if` statement after the `else`", + "if ".to_string(), + Applicability::MaybeIncorrect, + ).multipart_suggestion( + "... otherwise, place this expression inside of a block if it is not an `if` condition", + vec![ + (cond.span.shrink_to_lo(), "{ ".to_string()), + (cond.span.shrink_to_hi(), " }".to_string()), + ], + Applicability::MaybeIncorrect, + ) + .emit(); + self.parse_if_after_cond(AttrVec::new(), cond.span.shrink_to_lo(), cond)? + } + Err(e) => { + e.cancel(); + self.restore_snapshot(snapshot); + self.parse_simple_block()? + }, + Ok(_) => { + self.restore_snapshot(snapshot); + self.parse_simple_block()? + }, + } }; - self.error_on_if_block_attrs(ctx_span, true, expr.span, &attrs); + self.error_on_if_block_attrs(else_span, true, expr.span, &attrs); Ok(expr) } diff --git a/src/test/ui/parser/else-no-if.rs b/src/test/ui/parser/else-no-if.rs new file mode 100644 index 0000000000000..f0b40ecde6660 --- /dev/null +++ b/src/test/ui/parser/else-no-if.rs @@ -0,0 +1,32 @@ +fn foo() { + if true { + } else false { + //~^ ERROR expected `{`, found keyword `false` + } +} + +fn foo2() { + if true { + } else falsy() { + //~^ ERROR expected `{`, found `falsy` + } +} + +fn foo3() { + if true { + } else falsy(); + //~^ ERROR expected `{`, found `falsy` +} + +fn foo4() { + if true { + } else loop{} + //~^ ERROR expected `{`, found keyword `loop` + {} +} + +fn falsy() -> bool { + false +} + +fn main() {} diff --git a/src/test/ui/parser/else-no-if.stderr b/src/test/ui/parser/else-no-if.stderr new file mode 100644 index 0000000000000..27abbadd7ad24 --- /dev/null +++ b/src/test/ui/parser/else-no-if.stderr @@ -0,0 +1,58 @@ +error: expected `{`, found keyword `false` + --> $DIR/else-no-if.rs:3:12 + | +LL | } else false { + | ---- ^^^^^ + | | + | expected an `if` or a block after this `else` + | +help: add an `if` if this is the condition to an chained `if` statement after the `else` + | +LL | } else if false { + | ++ +help: ... otherwise, place this expression inside of a block if it is not an `if` condition + | +LL | } else { false } { + | + + + +error: expected `{`, found `falsy` + --> $DIR/else-no-if.rs:10:12 + | +LL | } else falsy() { + | ---- ^^^^^ + | | + | expected an `if` or a block after this `else` + | +help: add an `if` if this is the condition to an chained `if` statement after the `else` + | +LL | } else if falsy() { + | ++ +help: ... otherwise, place this expression inside of a block if it is not an `if` condition + | +LL | } else { falsy() } { + | + + + +error: expected `{`, found `falsy` + --> $DIR/else-no-if.rs:17:12 + | +LL | } else falsy(); + | ^^^^^ expected `{` + | +help: try placing this code inside a block + | +LL | } else { falsy() }; + | + + + +error: expected `{`, found keyword `loop` + --> $DIR/else-no-if.rs:23:12 + | +LL | } else loop{} + | ^^^^ expected `{` + | +help: try placing this code inside a block + | +LL | } else { loop{} } + | + + + +error: aborting due to 4 previous errors +