From bc0d10d4b0fefccda6aae0338a1935d76314736b Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Tue, 28 Nov 2023 10:15:39 -0800 Subject: [PATCH 01/12] Add genness to FnHeader --- compiler/rustc_ast/src/ast.rs | 8 ++++++-- compiler/rustc_ast/src/mut_visit.rs | 17 ++++++++++++++++- compiler/rustc_parse/src/parser/item.rs | 3 ++- compiler/rustc_parse/src/parser/ty.rs | 3 ++- 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index e8731cf20f2..4ef8d64c4c0 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2837,16 +2837,19 @@ pub struct FnHeader { pub constness: Const, /// The `extern` keyword and corresponding ABI string, if any pub ext: Extern, + /// The `gen` keyword, if any + pub genness: Gen, } impl FnHeader { /// Does this function header have any qualifiers or is it empty? pub fn has_qualifiers(&self) -> bool { - let Self { unsafety, asyncness, constness, ext } = self; + let Self { unsafety, asyncness, constness, ext, genness } = self; matches!(unsafety, Unsafe::Yes(_)) || asyncness.is_async() || matches!(constness, Const::Yes(_)) || !matches!(ext, Extern::None) + || matches!(genness, Gen::Yes { .. }) } } @@ -2857,6 +2860,7 @@ impl Default for FnHeader { asyncness: Async::No, constness: Const::No, ext: Extern::None, + genness: Gen::No, } } } @@ -3177,7 +3181,7 @@ mod size_asserts { static_assert_size!(Block, 32); static_assert_size!(Expr, 72); static_assert_size!(ExprKind, 40); - static_assert_size!(Fn, 152); + static_assert_size!(Fn, 168); static_assert_size!(ForeignItem, 96); static_assert_size!(ForeignItemKind, 24); static_assert_size!(GenericArg, 24); diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 8ce86bf9ecf..7cdbb1a8c61 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -125,6 +125,10 @@ pub trait MutVisitor: Sized { noop_visit_asyncness(a, self); } + fn visit_genness(&mut self, a: &mut Gen) { + noop_visit_genness(a, self); + } + fn visit_closure_binder(&mut self, b: &mut ClosureBinder) { noop_visit_closure_binder(b, self); } @@ -881,6 +885,16 @@ pub fn noop_visit_asyncness(asyncness: &mut Async, vis: &mut T) { } } +pub fn noop_visit_genness(genness: &mut Gen, vis: &mut T) { + match genness { + Gen::Yes { span: _, closure_id, return_impl_trait_id } => { + vis.visit_id(closure_id); + vis.visit_id(return_impl_trait_id); + } + Gen::No => {} + } +} + pub fn noop_visit_fn_decl(decl: &mut P, vis: &mut T) { let FnDecl { inputs, output } = decl.deref_mut(); inputs.flat_map_in_place(|param| vis.flat_map_param(param)); @@ -1170,9 +1184,10 @@ fn visit_const_item( } pub fn noop_visit_fn_header(header: &mut FnHeader, vis: &mut T) { - let FnHeader { unsafety, asyncness, constness, ext: _ } = header; + let FnHeader { unsafety, asyncness, constness, ext: _, genness } = header; visit_constness(constness, vis); vis.visit_asyncness(asyncness); + vis.visit_genness(genness); visit_unsafety(unsafety, vis); } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index a737f37a104..55f7310681f 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -2543,6 +2543,7 @@ impl<'a> Parser<'a> { constness: recover_constness, unsafety: recover_unsafety, asyncness: recover_asyncness, + genness, // FIXME(eholk): add keyword recovery logic here too. ext, }); } @@ -2552,7 +2553,7 @@ impl<'a> Parser<'a> { } } - Ok(FnHeader { constness, unsafety, asyncness, ext }) + Ok(FnHeader { constness, unsafety, asyncness, ext, genness }) } /// Parses the parameter list and result type of a function declaration. diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index b1a57c3dfd9..cf0dd39b562 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -596,7 +596,7 @@ impl<'a> Parser<'a> { tokens: None, }; let span_start = self.token.span; - let ast::FnHeader { ext, unsafety, constness, asyncness } = + let ast::FnHeader { ext, unsafety, constness, asyncness, genness: _ } = self.parse_fn_front_matter(&inherited_vis, Case::Sensitive)?; if self.may_recover() && self.token.kind == TokenKind::Lt { self.recover_fn_ptr_with_generics(lo, &mut params, param_insertion_point)?; @@ -612,6 +612,7 @@ impl<'a> Parser<'a> { if let ast::Async::Yes { span, .. } = asyncness { self.sess.emit_err(FnPointerCannotBeAsync { span: whole_span, qualifier: span }); } + // FIXME(eholk): emit a similar error for `gen fn()` let decl_span = span_start.to(self.token.span); Ok(TyKind::BareFn(P(BareFnTy { ext, unsafety, generic_params: params, decl, decl_span }))) } From c104f3b629cfcac35802a899478756abf24ee7c1 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Wed, 29 Nov 2023 12:07:43 -0800 Subject: [PATCH 02/12] Lower return types for gen fn to impl Iterator --- compiler/rustc_ast_lowering/src/item.rs | 146 ++++++++++++++------ compiler/rustc_ast_lowering/src/lib.rs | 75 +++++++--- compiler/rustc_ast_lowering/src/path.rs | 9 +- compiler/rustc_hir/src/hir.rs | 2 + compiler/rustc_hir_typeck/src/closure.rs | 6 +- compiler/rustc_parse/src/parser/item.rs | 4 - compiler/rustc_resolve/src/def_collector.rs | 5 +- 7 files changed, 167 insertions(+), 80 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index f0f3e2c3c74..852555048fe 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1,3 +1,5 @@ +use crate::FnReturnTransformation; + use super::errors::{InvalidAbi, InvalidAbiReason, InvalidAbiSuggestion, MisplacedRelaxTraitBound}; use super::ResolverAstLoweringExt; use super::{AstOwner, ImplTraitContext, ImplTraitPosition}; @@ -207,13 +209,33 @@ impl<'hir> LoweringContext<'_, 'hir> { // only cares about the input argument patterns in the function // declaration (decl), not the return types. let asyncness = header.asyncness; - let body_id = - this.lower_maybe_async_body(span, hir_id, decl, asyncness, body.as_deref()); + let genness = header.genness; + let body_id = this.lower_maybe_coroutine_body( + span, + hir_id, + decl, + asyncness, + genness, + body.as_deref(), + ); let itctx = ImplTraitContext::Universal; let (generics, decl) = this.lower_generics(generics, header.constness, id, &itctx, |this| { - let ret_id = asyncness.opt_return_id(); + let ret_id = asyncness + .opt_return_id() + .map(|(node_id, span)| { + crate::FnReturnTransformation::Async(node_id, span) + }) + .or_else(|| match genness { + Gen::Yes { span, closure_id: _, return_impl_trait_id } => { + Some(crate::FnReturnTransformation::Iterator( + return_impl_trait_id, + span, + )) + } + _ => None, + }); this.lower_fn_decl(decl, id, *fn_sig_span, FnDeclKind::Fn, ret_id) }); let sig = hir::FnSig { @@ -732,20 +754,31 @@ impl<'hir> LoweringContext<'_, 'hir> { sig, i.id, FnDeclKind::Trait, - asyncness.opt_return_id(), + asyncness + .opt_return_id() + .map(|(node_id, span)| crate::FnReturnTransformation::Async(node_id, span)), ); (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)), false) } AssocItemKind::Fn(box Fn { sig, generics, body: Some(body), .. }) => { let asyncness = sig.header.asyncness; - let body_id = - self.lower_maybe_async_body(i.span, hir_id, &sig.decl, asyncness, Some(body)); + let genness = sig.header.genness; + let body_id = self.lower_maybe_coroutine_body( + i.span, + hir_id, + &sig.decl, + asyncness, + genness, + Some(body), + ); let (generics, sig) = self.lower_method_sig( generics, sig, i.id, FnDeclKind::Trait, - asyncness.opt_return_id(), + asyncness + .opt_return_id() + .map(|(node_id, span)| crate::FnReturnTransformation::Async(node_id, span)), ); (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)), true) } @@ -835,11 +868,13 @@ impl<'hir> LoweringContext<'_, 'hir> { ), AssocItemKind::Fn(box Fn { sig, generics, body, .. }) => { let asyncness = sig.header.asyncness; - let body_id = self.lower_maybe_async_body( + let genness = sig.header.genness; + let body_id = self.lower_maybe_coroutine_body( i.span, hir_id, &sig.decl, asyncness, + genness, body.as_deref(), ); let (generics, sig) = self.lower_method_sig( @@ -847,7 +882,9 @@ impl<'hir> LoweringContext<'_, 'hir> { sig, i.id, if self.is_in_trait_impl { FnDeclKind::Impl } else { FnDeclKind::Inherent }, - asyncness.opt_return_id(), + asyncness + .opt_return_id() + .map(|(node_id, span)| crate::FnReturnTransformation::Async(node_id, span)), ); (generics, hir::ImplItemKind::Fn(sig, body_id)) @@ -1011,16 +1048,22 @@ impl<'hir> LoweringContext<'_, 'hir> { }) } - fn lower_maybe_async_body( + /// Takes what may be the body of an `async fn` or a `gen fn` and wraps it in an `async {}` or + /// `gen {}` block as appropriate. + fn lower_maybe_coroutine_body( &mut self, span: Span, fn_id: hir::HirId, decl: &FnDecl, asyncness: Async, + genness: Gen, body: Option<&Block>, ) -> hir::BodyId { - let (closure_id, body) = match (asyncness, body) { - (Async::Yes { closure_id, .. }, Some(body)) => (closure_id, body), + let (closure_id, body) = match (asyncness, genness, body) { + // FIXME(eholk): do something reasonable for `async gen fn`. Probably that's an error + // for now since it's not supported. + (Async::Yes { closure_id, .. }, _, Some(body)) + | (_, Gen::Yes { closure_id, .. }, Some(body)) => (closure_id, body), _ => return self.lower_fn_body_block(span, decl, body), }; @@ -1163,44 +1206,55 @@ impl<'hir> LoweringContext<'_, 'hir> { parameters.push(new_parameter); } - let async_expr = this.make_async_expr( - CaptureBy::Value { move_kw: rustc_span::DUMMY_SP }, - closure_id, - None, - body.span, - hir::CoroutineSource::Fn, - |this| { - // Create a block from the user's function body: - let user_body = this.lower_block_expr(body); + let mkbody = |this: &mut LoweringContext<'_, 'hir>| { + // Create a block from the user's function body: + let user_body = this.lower_block_expr(body); - // Transform into `drop-temps { }`, an expression: - let desugared_span = - this.mark_span_with_reason(DesugaringKind::Async, user_body.span, None); - let user_body = - this.expr_drop_temps(desugared_span, this.arena.alloc(user_body)); + // Transform into `drop-temps { }`, an expression: + let desugared_span = + this.mark_span_with_reason(DesugaringKind::Async, user_body.span, None); + let user_body = this.expr_drop_temps(desugared_span, this.arena.alloc(user_body)); - // As noted above, create the final block like - // - // ``` - // { - // let $param_pattern = $raw_param; - // ... - // drop-temps { } - // } - // ``` - let body = this.block_all( - desugared_span, - this.arena.alloc_from_iter(statements), - Some(user_body), - ); + // As noted above, create the final block like + // + // ``` + // { + // let $param_pattern = $raw_param; + // ... + // drop-temps { } + // } + // ``` + let body = this.block_all( + desugared_span, + this.arena.alloc_from_iter(statements), + Some(user_body), + ); - this.expr_block(body) - }, - ); + this.expr_block(body) + }; + let coroutine_expr = match (asyncness, genness) { + (Async::Yes { .. }, _) => this.make_async_expr( + CaptureBy::Value { move_kw: rustc_span::DUMMY_SP }, + closure_id, + None, + body.span, + hir::CoroutineSource::Fn, + mkbody, + ), + (_, Gen::Yes { .. }) => this.make_gen_expr( + CaptureBy::Value { move_kw: rustc_span::DUMMY_SP }, + closure_id, + None, + body.span, + hir::CoroutineSource::Fn, + mkbody, + ), + _ => unreachable!("we must have either an async fn or a gen fn"), + }; let hir_id = this.lower_node_id(closure_id); this.maybe_forward_track_caller(body.span, fn_id, hir_id); - let expr = hir::Expr { hir_id, kind: async_expr, span: this.lower_span(body.span) }; + let expr = hir::Expr { hir_id, kind: coroutine_expr, span: this.lower_span(body.span) }; (this.arena.alloc_from_iter(parameters), expr) }) @@ -1212,13 +1266,13 @@ impl<'hir> LoweringContext<'_, 'hir> { sig: &FnSig, id: NodeId, kind: FnDeclKind, - is_async: Option<(NodeId, Span)>, + transform_return_type: Option, ) -> (&'hir hir::Generics<'hir>, hir::FnSig<'hir>) { let header = self.lower_fn_header(sig.header); let itctx = ImplTraitContext::Universal; let (generics, decl) = self.lower_generics(generics, sig.header.constness, id, &itctx, |this| { - this.lower_fn_decl(&sig.decl, id, sig.span, kind, is_async) + this.lower_fn_decl(&sig.decl, id, sig.span, kind, transform_return_type) }); (generics, hir::FnSig { header, decl, span: self.lower_span(sig.span) }) } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index aa8ad978451..96a413e9f73 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -493,6 +493,21 @@ enum ParenthesizedGenericArgs { Err, } +/// Describes a return type transformation that can be performed by `LoweringContext::lower_fn_decl` +#[derive(Debug)] +enum FnReturnTransformation { + /// Replaces a return type `T` with `impl Future`. + /// + /// The `NodeId` is the ID of the return type `impl Trait` item, and the `Span` points to the + /// `async` keyword. + Async(NodeId, Span), + /// Replaces a return type `T` with `impl Iterator`. + /// + /// The `NodeId` is the ID of the return type `impl Trait` item, and the `Span` points to the + /// `gen` keyword. + Iterator(NodeId, Span), +} + impl<'a, 'hir> LoweringContext<'a, 'hir> { fn create_def( &mut self, @@ -1778,13 +1793,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { })) } - // Lowers a function declaration. - // - // `decl`: the unlowered (AST) function declaration. - // `fn_node_id`: `impl Trait` arguments are lowered into generic parameters on the given `NodeId`. - // `make_ret_async`: if `Some`, converts `-> T` into `-> impl Future` in the - // return type. This is used for `async fn` declarations. The `NodeId` is the ID of the - // return type `impl Trait` item, and the `Span` points to the `async` keyword. + /// Lowers a function declaration. + /// + /// `decl`: the unlowered (AST) function declaration. + /// + /// `fn_node_id`: `impl Trait` arguments are lowered into generic parameters on the given + /// `NodeId`. + /// + /// `transform_return_type`: if `Some`, applies some conversion to the return type, such as is + /// needed for `async fn` and `gen fn`. See [`FnReturnTransformation`] for more details. #[instrument(level = "debug", skip(self))] fn lower_fn_decl( &mut self, @@ -1792,7 +1809,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn_node_id: NodeId, fn_span: Span, kind: FnDeclKind, - make_ret_async: Option<(NodeId, Span)>, + transform_return_type: Option, ) -> &'hir hir::FnDecl<'hir> { let c_variadic = decl.c_variadic(); @@ -1821,11 +1838,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.lower_ty_direct(¶m.ty, &itctx) })); - let output = if let Some((ret_id, _span)) = make_ret_async { - let fn_def_id = self.local_def_id(fn_node_id); - self.lower_async_fn_ret_ty(&decl.output, fn_def_id, ret_id, kind, fn_span) - } else { - match &decl.output { + let output = match transform_return_type { + Some(transform) => { + let fn_def_id = self.local_def_id(fn_node_id); + self.lower_coroutine_fn_ret_ty(&decl.output, fn_def_id, transform, kind, fn_span) + } + None => match &decl.output { FnRetTy::Ty(ty) => { let context = if kind.return_impl_trait_allowed() { let fn_def_id = self.local_def_id(fn_node_id); @@ -1849,7 +1867,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { hir::FnRetTy::Return(self.lower_ty(ty, &context)) } FnRetTy::Default(span) => hir::FnRetTy::DefaultReturn(self.lower_span(*span)), - } + }, }; self.arena.alloc(hir::FnDecl { @@ -1888,17 +1906,22 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // `fn_node_id`: `NodeId` of the parent function (used to create child impl trait definition) // `opaque_ty_node_id`: `NodeId` of the opaque `impl Trait` type that should be created #[instrument(level = "debug", skip(self))] - fn lower_async_fn_ret_ty( + fn lower_coroutine_fn_ret_ty( &mut self, output: &FnRetTy, fn_def_id: LocalDefId, - opaque_ty_node_id: NodeId, + transform: FnReturnTransformation, fn_kind: FnDeclKind, fn_span: Span, ) -> hir::FnRetTy<'hir> { let span = self.lower_span(fn_span); let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::Async, span, None); + let opaque_ty_node_id = match transform { + FnReturnTransformation::Async(opaque_ty_node_id, _) + | FnReturnTransformation::Iterator(opaque_ty_node_id, _) => opaque_ty_node_id, + }; + let captured_lifetimes: Vec<_> = self .resolver .take_extra_lifetime_params(opaque_ty_node_id) @@ -1914,8 +1937,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { span, opaque_ty_span, |this| { - let future_bound = this.lower_async_fn_output_type_to_future_bound( + let future_bound = this.lower_coroutine_fn_output_type_to_future_bound( output, + transform, span, ImplTraitContext::ReturnPositionOpaqueTy { origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id), @@ -1931,9 +1955,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } /// Transforms `-> T` into `Future`. - fn lower_async_fn_output_type_to_future_bound( + fn lower_coroutine_fn_output_type_to_future_bound( &mut self, output: &FnRetTy, + transform: FnReturnTransformation, span: Span, nested_impl_trait_context: ImplTraitContext, ) -> hir::GenericBound<'hir> { @@ -1948,17 +1973,23 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { FnRetTy::Default(ret_ty_span) => self.arena.alloc(self.ty_tup(*ret_ty_span, &[])), }; - // "" + // "" + let (symbol, lang_item) = match transform { + FnReturnTransformation::Async(..) => (hir::FN_OUTPUT_NAME, hir::LangItem::Future), + FnReturnTransformation::Iterator(..) => { + (hir::ITERATOR_ITEM_NAME, hir::LangItem::Iterator) + } + }; + let future_args = self.arena.alloc(hir::GenericArgs { args: &[], - bindings: arena_vec![self; self.output_ty_binding(span, output_ty)], + bindings: arena_vec![self; self.assoc_ty_binding(symbol, span, output_ty)], parenthesized: hir::GenericArgsParentheses::No, span_ext: DUMMY_SP, }); hir::GenericBound::LangItemTrait( - // ::std::future::Future - hir::LangItem::Future, + lang_item, self.lower_span(span), self.next_id(), future_args, diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index db8ca7c3643..accb74d7a52 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -389,7 +389,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { FnRetTy::Default(_) => self.arena.alloc(self.ty_tup(*span, &[])), }; let args = smallvec![GenericArg::Type(self.arena.alloc(self.ty_tup(*inputs_span, inputs)))]; - let binding = self.output_ty_binding(output_ty.span, output_ty); + let binding = self.assoc_ty_binding(hir::FN_OUTPUT_NAME, output_ty.span, output_ty); ( GenericArgsCtor { args, @@ -401,13 +401,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) } - /// An associated type binding `Output = $ty`. - pub(crate) fn output_ty_binding( + /// An associated type binding `$symbol = $ty`. + pub(crate) fn assoc_ty_binding( &mut self, + symbol: rustc_span::Symbol, span: Span, ty: &'hir hir::Ty<'hir>, ) -> hir::TypeBinding<'hir> { - let ident = Ident::with_dummy_span(hir::FN_OUTPUT_NAME); + let ident = Ident::with_dummy_span(symbol); let kind = hir::TypeBindingKind::Equality { term: ty.into() }; let args = arena_vec![self;]; let bindings = arena_vec![self;]; diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 81733d8f64e..0d9e174e37a 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2255,6 +2255,8 @@ pub enum ImplItemKind<'hir> { /// The name of the associated type for `Fn` return types. pub const FN_OUTPUT_NAME: Symbol = sym::Output; +/// The name of the associated type for `Iterator` item types. +pub const ITERATOR_ITEM_NAME: Symbol = sym::Item; /// Bind a type to an associated type (i.e., `A = Foo`). /// diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index cb36d510149..f0bb18df48c 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -651,9 +651,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }, ) } - Some(hir::CoroutineKind::Gen(hir::CoroutineSource::Fn)) => { - todo!("gen closures do not exist yet") - } + // For a `gen {}` block created as a `gen fn` body, we need the return type to be + // (). + Some(hir::CoroutineKind::Gen(hir::CoroutineSource::Fn)) => self.tcx.types.unit, _ => astconv.ty_infer(None, decl.output.span()), }, diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 55f7310681f..4f01ab02c04 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -2410,10 +2410,6 @@ impl<'a> Parser<'a> { } } - if let Gen::Yes { span, .. } = genness { - self.sess.emit_err(errors::GenFn { span }); - } - if !self.eat_keyword_case(kw::Fn, case) { // It is possible for `expect_one_of` to recover given the contents of // `self.expected_tokens`, therefore, do not use `self.unexpected()` which doesn't diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 647c92785e1..306492eaa96 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -156,7 +156,10 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { fn visit_fn(&mut self, fn_kind: FnKind<'a>, span: Span, _: NodeId) { if let FnKind::Fn(_, _, sig, _, generics, body) = fn_kind { - if let Async::Yes { closure_id, .. } = sig.header.asyncness { + // FIXME(eholk): handle `async gen fn` + if let (Async::Yes { closure_id, .. }, _) | (_, Gen::Yes { closure_id, .. }) = + (sig.header.asyncness, sig.header.genness) + { self.visit_generics(generics); // For async functions, we need to create their inner defs inside of a From 7c43784cb07a2e9006acc6f230df27dd28d88dab Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Wed, 29 Nov 2023 14:13:35 -0800 Subject: [PATCH 03/12] gate gen fn behind gen_blocks --- compiler/rustc_parse/src/parser/item.rs | 4 ++++ tests/ui/coroutine/gen_fn.e2024.stderr | 6 ++++-- tests/ui/coroutine/gen_fn.rs | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 4f01ab02c04..766ec50e75b 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -2410,6 +2410,10 @@ impl<'a> Parser<'a> { } } + if let Gen::Yes { span, .. } = genness { + self.sess.gated_spans.gate(sym::gen_blocks, span); + } + if !self.eat_keyword_case(kw::Fn, case) { // It is possible for `expect_one_of` to recover given the contents of // `self.expected_tokens`, therefore, do not use `self.unexpected()` which doesn't diff --git a/tests/ui/coroutine/gen_fn.e2024.stderr b/tests/ui/coroutine/gen_fn.e2024.stderr index 928c16f57a2..9ad890af3e1 100644 --- a/tests/ui/coroutine/gen_fn.e2024.stderr +++ b/tests/ui/coroutine/gen_fn.e2024.stderr @@ -1,10 +1,12 @@ -error: `gen` functions are not yet implemented +error[E0658]: gen blocks are experimental --> $DIR/gen_fn.rs:4:1 | LL | gen fn foo() {} | ^^^ | - = help: for now you can use `gen {}` blocks and return `impl Iterator` instead + = note: see issue #117078 for more information + = help: add `#![feature(gen_blocks)]` to the crate attributes to enable error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/coroutine/gen_fn.rs b/tests/ui/coroutine/gen_fn.rs index da515f263b0..e06629c5dfe 100644 --- a/tests/ui/coroutine/gen_fn.rs +++ b/tests/ui/coroutine/gen_fn.rs @@ -3,6 +3,6 @@ gen fn foo() {} //[none]~^ ERROR: expected one of `#`, `async`, `const`, `default`, `extern`, `fn`, `pub`, `unsafe`, or `use`, found `gen` -//[e2024]~^^ ERROR: `gen` functions are not yet implemented +//[e2024]~^^ ERROR: gen blocks are experimental fn main() {} From f29b36d03ecf9986ac6c47385ff9390c9a2390e6 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Wed, 29 Nov 2023 16:59:06 -0800 Subject: [PATCH 04/12] Make async gen fn an error --- compiler/rustc_parse/messages.ftl | 2 ++ compiler/rustc_parse/src/errors.rs | 7 +++++++ compiler/rustc_parse/src/parser/item.rs | 6 ++++++ tests/ui/coroutine/async_gen_fn.rs | 11 +++++++++++ tests/ui/coroutine/async_gen_fn.stderr | 8 ++++++++ 5 files changed, 34 insertions(+) create mode 100644 tests/ui/coroutine/async_gen_fn.rs create mode 100644 tests/ui/coroutine/async_gen_fn.stderr diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 1c3c433d8b7..da51d9dbe9f 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -23,6 +23,8 @@ parse_async_block_in_2015 = `async` blocks are only allowed in Rust 2018 or late parse_async_fn_in_2015 = `async fn` is not permitted in Rust 2015 .label = to use `async fn`, switch to Rust 2018 or later +parse_async_gen_fn = `async gen` functions are not supported + parse_async_move_block_in_2015 = `async move` blocks are only allowed in Rust 2018 or later parse_async_move_order_incorrect = the order of `move` and `async` is incorrect diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 03e047b297d..4bd7c99de9a 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -562,6 +562,13 @@ pub(crate) struct GenFn { pub span: Span, } +#[derive(Diagnostic)] +#[diag(parse_async_gen_fn)] +pub(crate) struct AsyncGenFn { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(parse_comma_after_base_struct)] #[note] diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 766ec50e75b..c6d1ea882e9 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -2414,6 +2414,12 @@ impl<'a> Parser<'a> { self.sess.gated_spans.gate(sym::gen_blocks, span); } + if let (Async::Yes { span: async_span, .. }, Gen::Yes { span: gen_span, .. }) = + (asyncness, genness) + { + self.sess.emit_err(errors::AsyncGenFn { span: async_span.to(gen_span) }); + } + if !self.eat_keyword_case(kw::Fn, case) { // It is possible for `expect_one_of` to recover given the contents of // `self.expected_tokens`, therefore, do not use `self.unexpected()` which doesn't diff --git a/tests/ui/coroutine/async_gen_fn.rs b/tests/ui/coroutine/async_gen_fn.rs new file mode 100644 index 00000000000..f8860e07f6c --- /dev/null +++ b/tests/ui/coroutine/async_gen_fn.rs @@ -0,0 +1,11 @@ +// edition: 2024 +// compile-flags: -Zunstable-options +#![feature(gen_blocks)] + +// async generators are not yet supported, so this test makes sure they make some kind of reasonable +// error. + +async gen fn foo() {} +//~^ `async gen` functions are not supported + +fn main() {} diff --git a/tests/ui/coroutine/async_gen_fn.stderr b/tests/ui/coroutine/async_gen_fn.stderr new file mode 100644 index 00000000000..6857ebe6c79 --- /dev/null +++ b/tests/ui/coroutine/async_gen_fn.stderr @@ -0,0 +1,8 @@ +error: `async gen` functions are not supported + --> $DIR/async_gen_fn.rs:8:1 + | +LL | async gen fn foo() {} + | ^^^^^^^^^ + +error: aborting due to 1 previous error + From 3887b1645aaa85e2755a54ab91f40c4fedc1dc0f Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Thu, 30 Nov 2023 11:20:53 -0800 Subject: [PATCH 05/12] Add a basic test for gen fn --- tests/ui/coroutine/gen_fn_iter.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 tests/ui/coroutine/gen_fn_iter.rs diff --git a/tests/ui/coroutine/gen_fn_iter.rs b/tests/ui/coroutine/gen_fn_iter.rs new file mode 100644 index 00000000000..222ab571415 --- /dev/null +++ b/tests/ui/coroutine/gen_fn_iter.rs @@ -0,0 +1,18 @@ +// edition: 2024 +// compile-flags: -Zunstable-options +// run-pass +#![feature(gen_blocks)] + +gen fn foo() -> i32 { + yield 1; + yield 2; + yield 3; +} + +fn main() { + let mut iter = foo(); + assert_eq!(iter.next(), Some(1)); + assert_eq!(iter.next(), Some(2)); + assert_eq!(iter.next(), Some(3)); + assert_eq!(iter.next(), None); +} From 48d5f1f0f26b78f76c5fcf0dda5ac93b8754aeb6 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Thu, 30 Nov 2023 14:54:39 -0800 Subject: [PATCH 06/12] Merge Async and Gen into CoroutineKind --- compiler/rustc_ast/src/ast.rs | 54 ++--- compiler/rustc_ast/src/mut_visit.rs | 36 +--- compiler/rustc_ast/src/visit.rs | 2 +- compiler/rustc_ast_lowering/src/expr.rs | 71 ++++--- compiler/rustc_ast_lowering/src/item.rs | 89 +++----- compiler/rustc_ast_lowering/src/lib.rs | 57 +++-- .../rustc_ast_passes/src/ast_validation.rs | 2 +- compiler/rustc_ast_pretty/src/pprust/state.rs | 14 +- .../rustc_ast_pretty/src/pprust/state/expr.rs | 4 +- compiler/rustc_builtin_macros/src/test.rs | 2 +- compiler/rustc_expand/src/build.rs | 2 +- compiler/rustc_lint/src/early.rs | 8 +- compiler/rustc_parse/src/parser/expr.rs | 10 +- compiler/rustc_parse/src/parser/item.rs | 34 ++- compiler/rustc_parse/src/parser/mod.rs | 24 ++- compiler/rustc_parse/src/parser/ty.rs | 4 +- compiler/rustc_resolve/src/def_collector.rs | 11 +- compiler/rustc_resolve/src/late.rs | 6 +- src/tools/clippy/clippy_lints/src/doc/mod.rs | 199 ++++++++++++++++++ .../clippy/clippy_utils/src/ast_utils.rs | 6 +- src/tools/rustfmt/src/closures.rs | 24 ++- src/tools/rustfmt/src/expr.rs | 2 +- src/tools/rustfmt/src/items.rs | 8 +- src/tools/rustfmt/src/utils.rs | 9 +- tests/ui/coroutine/gen_fn_iter.rs | 2 + 25 files changed, 442 insertions(+), 238 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 4ef8d64c4c0..bf648388f4e 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1311,7 +1311,7 @@ pub struct Closure { pub binder: ClosureBinder, pub capture_clause: CaptureBy, pub constness: Const, - pub asyncness: Async, + pub coro_kind: CoroutineKind, pub movability: Movability, pub fn_decl: P, pub body: P, @@ -2406,28 +2406,38 @@ pub enum Unsafe { No, } +/// Describes what kind of coroutine markers, if any, a function has. +/// +/// Coroutine markers are things that cause the function to generate a coroutine, such as `async`, +/// which makes the function return `impl Future`, or `gen`, which makes the function return `impl +/// Iterator`. #[derive(Copy, Clone, Encodable, Decodable, Debug)] -pub enum Async { - Yes { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId }, - No, +pub enum CoroutineKind { + /// `async`, which evaluates to `impl Future` + Async { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId }, + /// `gen`, which evaluates to `impl Iterator` + Gen { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId }, + /// Neither `async` nor `gen` + None, } -#[derive(Copy, Clone, Encodable, Decodable, Debug)] -pub enum Gen { - Yes { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId }, - No, -} - -impl Async { +impl CoroutineKind { pub fn is_async(self) -> bool { - matches!(self, Async::Yes { .. }) + matches!(self, CoroutineKind::Async { .. }) + } + + pub fn is_gen(self) -> bool { + matches!(self, CoroutineKind::Gen { .. }) } /// In this case this is an `async` return, the `NodeId` for the generated `impl Trait` item. pub fn opt_return_id(self) -> Option<(NodeId, Span)> { match self { - Async::Yes { return_impl_trait_id, span, .. } => Some((return_impl_trait_id, span)), - Async::No => None, + CoroutineKind::Async { return_impl_trait_id, span, .. } + | CoroutineKind::Gen { return_impl_trait_id, span, .. } => { + Some((return_impl_trait_id, span)) + } + CoroutineKind::None => None, } } } @@ -2831,25 +2841,22 @@ impl Extern { pub struct FnHeader { /// The `unsafe` keyword, if any pub unsafety: Unsafe, - /// The `async` keyword, if any - pub asyncness: Async, + /// Whether this is `async`, `gen`, or nothing. + pub coro_kind: CoroutineKind, /// The `const` keyword, if any pub constness: Const, /// The `extern` keyword and corresponding ABI string, if any pub ext: Extern, - /// The `gen` keyword, if any - pub genness: Gen, } impl FnHeader { /// Does this function header have any qualifiers or is it empty? pub fn has_qualifiers(&self) -> bool { - let Self { unsafety, asyncness, constness, ext, genness } = self; + let Self { unsafety, coro_kind, constness, ext } = self; matches!(unsafety, Unsafe::Yes(_)) - || asyncness.is_async() + || !matches!(coro_kind, CoroutineKind::None) || matches!(constness, Const::Yes(_)) || !matches!(ext, Extern::None) - || matches!(genness, Gen::Yes { .. }) } } @@ -2857,10 +2864,9 @@ impl Default for FnHeader { fn default() -> FnHeader { FnHeader { unsafety: Unsafe::No, - asyncness: Async::No, + coro_kind: CoroutineKind::None, constness: Const::No, ext: Extern::None, - genness: Gen::No, } } } @@ -3181,7 +3187,7 @@ mod size_asserts { static_assert_size!(Block, 32); static_assert_size!(Expr, 72); static_assert_size!(ExprKind, 40); - static_assert_size!(Fn, 168); + static_assert_size!(Fn, 160); static_assert_size!(ForeignItem, 96); static_assert_size!(ForeignItemKind, 24); static_assert_size!(GenericArg, 24); diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 7cdbb1a8c61..f9f767862f5 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -121,12 +121,8 @@ pub trait MutVisitor: Sized { noop_visit_fn_decl(d, self); } - fn visit_asyncness(&mut self, a: &mut Async) { - noop_visit_asyncness(a, self); - } - - fn visit_genness(&mut self, a: &mut Gen) { - noop_visit_genness(a, self); + fn visit_coro_kind(&mut self, a: &mut CoroutineKind) { + noop_visit_coro_kind(a, self); } fn visit_closure_binder(&mut self, b: &mut ClosureBinder) { @@ -875,23 +871,14 @@ pub fn noop_visit_closure_binder(binder: &mut ClosureBinder, vis: } } -pub fn noop_visit_asyncness(asyncness: &mut Async, vis: &mut T) { - match asyncness { - Async::Yes { span: _, closure_id, return_impl_trait_id } => { +pub fn noop_visit_coro_kind(coro_kind: &mut CoroutineKind, vis: &mut T) { + match coro_kind { + CoroutineKind::Async { span: _, closure_id, return_impl_trait_id } + | CoroutineKind::Gen { span: _, closure_id, return_impl_trait_id } => { vis.visit_id(closure_id); vis.visit_id(return_impl_trait_id); } - Async::No => {} - } -} - -pub fn noop_visit_genness(genness: &mut Gen, vis: &mut T) { - match genness { - Gen::Yes { span: _, closure_id, return_impl_trait_id } => { - vis.visit_id(closure_id); - vis.visit_id(return_impl_trait_id); - } - Gen::No => {} + CoroutineKind::None => {} } } @@ -1184,10 +1171,9 @@ fn visit_const_item( } pub fn noop_visit_fn_header(header: &mut FnHeader, vis: &mut T) { - let FnHeader { unsafety, asyncness, constness, ext: _, genness } = header; + let FnHeader { unsafety, coro_kind, constness, ext: _ } = header; visit_constness(constness, vis); - vis.visit_asyncness(asyncness); - vis.visit_genness(genness); + vis.visit_coro_kind(coro_kind); visit_unsafety(unsafety, vis); } @@ -1421,7 +1407,7 @@ pub fn noop_visit_expr( binder, capture_clause, constness, - asyncness, + coro_kind, movability: _, fn_decl, body, @@ -1430,7 +1416,7 @@ pub fn noop_visit_expr( }) => { vis.visit_closure_binder(binder); visit_constness(constness, vis); - vis.visit_asyncness(asyncness); + vis.visit_coro_kind(coro_kind); vis.visit_capture_by(capture_clause); vis.visit_fn_decl(fn_decl); vis.visit_expr(body); diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 9dbadcb49d3..a303d6584f4 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -861,7 +861,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { ExprKind::Closure(box Closure { binder, capture_clause, - asyncness: _, + coro_kind: _, constness: _, movability: _, fn_decl, diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index eb1a1d15027..5846f17c539 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -195,39 +195,37 @@ impl<'hir> LoweringContext<'_, 'hir> { binder, capture_clause, constness, - asyncness, + coro_kind, movability, fn_decl, body, fn_decl_span, fn_arg_span, - }) => { - if let Async::Yes { closure_id, .. } = asyncness { - self.lower_expr_async_closure( - binder, - *capture_clause, - e.id, - hir_id, - *closure_id, - fn_decl, - body, - *fn_decl_span, - *fn_arg_span, - ) - } else { - self.lower_expr_closure( - binder, - *capture_clause, - e.id, - *constness, - *movability, - fn_decl, - body, - *fn_decl_span, - *fn_arg_span, - ) - } - } + }) => match coro_kind { + CoroutineKind::Async { closure_id, .. } + | CoroutineKind::Gen { closure_id, .. } => self.lower_expr_async_closure( + binder, + *capture_clause, + e.id, + hir_id, + *closure_id, + fn_decl, + body, + *fn_decl_span, + *fn_arg_span, + ), + CoroutineKind::None => self.lower_expr_closure( + binder, + *capture_clause, + e.id, + *constness, + *movability, + fn_decl, + body, + *fn_decl_span, + *fn_arg_span, + ), + }, ExprKind::Block(blk, opt_label) => { let opt_label = self.lower_label(*opt_label); hir::ExprKind::Block(self.lower_block(blk, opt_label.is_some()), opt_label) @@ -935,7 +933,13 @@ impl<'hir> LoweringContext<'_, 'hir> { let bound_generic_params = self.lower_lifetime_binder(closure_id, generic_params); // Lower outside new scope to preserve `is_in_loop_condition`. - let fn_decl = self.lower_fn_decl(decl, closure_id, fn_decl_span, FnDeclKind::Closure, None); + let fn_decl = self.lower_fn_decl( + decl, + closure_id, + fn_decl_span, + FnDeclKind::Closure, + CoroutineKind::None, + ); let c = self.arena.alloc(hir::Closure { def_id: self.local_def_id(closure_id), @@ -1050,8 +1054,13 @@ impl<'hir> LoweringContext<'_, 'hir> { // We need to lower the declaration outside the new scope, because we // have to conserve the state of being inside a loop condition for the // closure argument types. - let fn_decl = - self.lower_fn_decl(&outer_decl, closure_id, fn_decl_span, FnDeclKind::Closure, None); + let fn_decl = self.lower_fn_decl( + &outer_decl, + closure_id, + fn_decl_span, + FnDeclKind::Closure, + CoroutineKind::None, + ); let c = self.arena.alloc(hir::Closure { def_id: self.local_def_id(closure_id), diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 852555048fe..5d32e1a78f1 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1,5 +1,3 @@ -use crate::FnReturnTransformation; - use super::errors::{InvalidAbi, InvalidAbiReason, InvalidAbiSuggestion, MisplacedRelaxTraitBound}; use super::ResolverAstLoweringExt; use super::{AstOwner, ImplTraitContext, ImplTraitPosition}; @@ -208,35 +206,19 @@ impl<'hir> LoweringContext<'_, 'hir> { // `impl Future` here because lower_body // only cares about the input argument patterns in the function // declaration (decl), not the return types. - let asyncness = header.asyncness; - let genness = header.genness; + let coro_kind = header.coro_kind; let body_id = this.lower_maybe_coroutine_body( span, hir_id, decl, - asyncness, - genness, + coro_kind, body.as_deref(), ); let itctx = ImplTraitContext::Universal; let (generics, decl) = this.lower_generics(generics, header.constness, id, &itctx, |this| { - let ret_id = asyncness - .opt_return_id() - .map(|(node_id, span)| { - crate::FnReturnTransformation::Async(node_id, span) - }) - .or_else(|| match genness { - Gen::Yes { span, closure_id: _, return_impl_trait_id } => { - Some(crate::FnReturnTransformation::Iterator( - return_impl_trait_id, - span, - )) - } - _ => None, - }); - this.lower_fn_decl(decl, id, *fn_sig_span, FnDeclKind::Fn, ret_id) + this.lower_fn_decl(decl, id, *fn_sig_span, FnDeclKind::Fn, coro_kind) }); let sig = hir::FnSig { decl, @@ -620,7 +602,7 @@ impl<'hir> LoweringContext<'_, 'hir> { i.id, sig.span, FnDeclKind::ExternFn, - None, + CoroutineKind::None, ), this.lower_fn_params_to_names(fdec), ) @@ -747,28 +729,22 @@ impl<'hir> LoweringContext<'_, 'hir> { (generics, kind, expr.is_some()) } AssocItemKind::Fn(box Fn { sig, generics, body: None, .. }) => { - let asyncness = sig.header.asyncness; let names = self.lower_fn_params_to_names(&sig.decl); let (generics, sig) = self.lower_method_sig( generics, sig, i.id, FnDeclKind::Trait, - asyncness - .opt_return_id() - .map(|(node_id, span)| crate::FnReturnTransformation::Async(node_id, span)), + sig.header.coro_kind, ); (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)), false) } AssocItemKind::Fn(box Fn { sig, generics, body: Some(body), .. }) => { - let asyncness = sig.header.asyncness; - let genness = sig.header.genness; let body_id = self.lower_maybe_coroutine_body( i.span, hir_id, &sig.decl, - asyncness, - genness, + sig.header.coro_kind, Some(body), ); let (generics, sig) = self.lower_method_sig( @@ -776,9 +752,7 @@ impl<'hir> LoweringContext<'_, 'hir> { sig, i.id, FnDeclKind::Trait, - asyncness - .opt_return_id() - .map(|(node_id, span)| crate::FnReturnTransformation::Async(node_id, span)), + sig.header.coro_kind, ); (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)), true) } @@ -867,14 +841,12 @@ impl<'hir> LoweringContext<'_, 'hir> { }, ), AssocItemKind::Fn(box Fn { sig, generics, body, .. }) => { - let asyncness = sig.header.asyncness; - let genness = sig.header.genness; + self.current_item = Some(i.span); let body_id = self.lower_maybe_coroutine_body( i.span, hir_id, &sig.decl, - asyncness, - genness, + sig.header.coro_kind, body.as_deref(), ); let (generics, sig) = self.lower_method_sig( @@ -882,9 +854,7 @@ impl<'hir> LoweringContext<'_, 'hir> { sig, i.id, if self.is_in_trait_impl { FnDeclKind::Impl } else { FnDeclKind::Inherent }, - asyncness - .opt_return_id() - .map(|(node_id, span)| crate::FnReturnTransformation::Async(node_id, span)), + sig.header.coro_kind, ); (generics, hir::ImplItemKind::Fn(sig, body_id)) @@ -1055,15 +1025,14 @@ impl<'hir> LoweringContext<'_, 'hir> { span: Span, fn_id: hir::HirId, decl: &FnDecl, - asyncness: Async, - genness: Gen, + coro_kind: CoroutineKind, body: Option<&Block>, ) -> hir::BodyId { - let (closure_id, body) = match (asyncness, genness, body) { - // FIXME(eholk): do something reasonable for `async gen fn`. Probably that's an error - // for now since it's not supported. - (Async::Yes { closure_id, .. }, _, Some(body)) - | (_, Gen::Yes { closure_id, .. }, Some(body)) => (closure_id, body), + let (closure_id, body) = match (coro_kind, body) { + ( + CoroutineKind::Async { closure_id, .. } | CoroutineKind::Gen { closure_id, .. }, + Some(body), + ) => (closure_id, body), _ => return self.lower_fn_body_block(span, decl, body), }; @@ -1232,8 +1201,8 @@ impl<'hir> LoweringContext<'_, 'hir> { this.expr_block(body) }; - let coroutine_expr = match (asyncness, genness) { - (Async::Yes { .. }, _) => this.make_async_expr( + let coroutine_expr = match coro_kind { + CoroutineKind::Async { .. } => this.make_async_expr( CaptureBy::Value { move_kw: rustc_span::DUMMY_SP }, closure_id, None, @@ -1241,7 +1210,7 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::CoroutineSource::Fn, mkbody, ), - (_, Gen::Yes { .. }) => this.make_gen_expr( + CoroutineKind::Gen { .. } => this.make_gen_expr( CaptureBy::Value { move_kw: rustc_span::DUMMY_SP }, closure_id, None, @@ -1249,7 +1218,7 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::CoroutineSource::Fn, mkbody, ), - _ => unreachable!("we must have either an async fn or a gen fn"), + CoroutineKind::None => unreachable!("we must have either an async fn or a gen fn"), }; let hir_id = this.lower_node_id(closure_id); @@ -1266,21 +1235,26 @@ impl<'hir> LoweringContext<'_, 'hir> { sig: &FnSig, id: NodeId, kind: FnDeclKind, - transform_return_type: Option, + coro_kind: CoroutineKind, ) -> (&'hir hir::Generics<'hir>, hir::FnSig<'hir>) { let header = self.lower_fn_header(sig.header); let itctx = ImplTraitContext::Universal; let (generics, decl) = self.lower_generics(generics, sig.header.constness, id, &itctx, |this| { - this.lower_fn_decl(&sig.decl, id, sig.span, kind, transform_return_type) + this.lower_fn_decl(&sig.decl, id, sig.span, kind, coro_kind) }); (generics, hir::FnSig { header, decl, span: self.lower_span(sig.span) }) } fn lower_fn_header(&mut self, h: FnHeader) -> hir::FnHeader { + let asyncness = if let CoroutineKind::Async { span, .. } = h.coro_kind { + hir::IsAsync::Async(span) + } else { + hir::IsAsync::NotAsync + }; hir::FnHeader { unsafety: self.lower_unsafety(h.unsafety), - asyncness: self.lower_asyncness(h.asyncness), + asyncness: asyncness, constness: self.lower_constness(h.constness), abi: self.lower_extern(h.ext), } @@ -1322,13 +1296,6 @@ impl<'hir> LoweringContext<'_, 'hir> { }); } - fn lower_asyncness(&mut self, a: Async) -> hir::IsAsync { - match a { - Async::Yes { span, .. } => hir::IsAsync::Async(span), - Async::No => hir::IsAsync::NotAsync, - } - } - pub(super) fn lower_constness(&mut self, c: Const) -> hir::Constness { match c { Const::Yes(_) => hir::Constness::Const, diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 96a413e9f73..92650f0c47e 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -493,21 +493,6 @@ enum ParenthesizedGenericArgs { Err, } -/// Describes a return type transformation that can be performed by `LoweringContext::lower_fn_decl` -#[derive(Debug)] -enum FnReturnTransformation { - /// Replaces a return type `T` with `impl Future`. - /// - /// The `NodeId` is the ID of the return type `impl Trait` item, and the `Span` points to the - /// `async` keyword. - Async(NodeId, Span), - /// Replaces a return type `T` with `impl Iterator`. - /// - /// The `NodeId` is the ID of the return type `impl Trait` item, and the `Span` points to the - /// `gen` keyword. - Iterator(NodeId, Span), -} - impl<'a, 'hir> LoweringContext<'a, 'hir> { fn create_def( &mut self, @@ -1374,7 +1359,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { generic_params, unsafety: self.lower_unsafety(f.unsafety), abi: self.lower_extern(f.ext), - decl: self.lower_fn_decl(&f.decl, t.id, t.span, FnDeclKind::Pointer, None), + decl: self.lower_fn_decl( + &f.decl, + t.id, + t.span, + FnDeclKind::Pointer, + CoroutineKind::None, + ), param_names: self.lower_fn_params_to_names(&f.decl), })) } @@ -1809,7 +1800,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn_node_id: NodeId, fn_span: Span, kind: FnDeclKind, - transform_return_type: Option, + coro: CoroutineKind, ) -> &'hir hir::FnDecl<'hir> { let c_variadic = decl.c_variadic(); @@ -1838,12 +1829,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.lower_ty_direct(¶m.ty, &itctx) })); - let output = match transform_return_type { - Some(transform) => { + let output = match coro { + CoroutineKind::Async { .. } | CoroutineKind::Gen { .. } => { let fn_def_id = self.local_def_id(fn_node_id); - self.lower_coroutine_fn_ret_ty(&decl.output, fn_def_id, transform, kind, fn_span) + self.lower_coroutine_fn_ret_ty(&decl.output, fn_def_id, coro, kind, fn_span) } - None => match &decl.output { + CoroutineKind::None => match &decl.output { FnRetTy::Ty(ty) => { let context = if kind.return_impl_trait_allowed() { let fn_def_id = self.local_def_id(fn_node_id); @@ -1910,16 +1901,19 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { &mut self, output: &FnRetTy, fn_def_id: LocalDefId, - transform: FnReturnTransformation, + coro: CoroutineKind, fn_kind: FnDeclKind, fn_span: Span, ) -> hir::FnRetTy<'hir> { let span = self.lower_span(fn_span); let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::Async, span, None); - let opaque_ty_node_id = match transform { - FnReturnTransformation::Async(opaque_ty_node_id, _) - | FnReturnTransformation::Iterator(opaque_ty_node_id, _) => opaque_ty_node_id, + let opaque_ty_node_id = match coro { + CoroutineKind::Async { return_impl_trait_id, .. } + | CoroutineKind::Gen { return_impl_trait_id, .. } => return_impl_trait_id, + CoroutineKind::None => { + unreachable!("lower_coroutine_fn_ret_ty must be called with either Async or Gen") + } }; let captured_lifetimes: Vec<_> = self @@ -1939,7 +1933,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { |this| { let future_bound = this.lower_coroutine_fn_output_type_to_future_bound( output, - transform, + coro, span, ImplTraitContext::ReturnPositionOpaqueTy { origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id), @@ -1958,7 +1952,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn lower_coroutine_fn_output_type_to_future_bound( &mut self, output: &FnRetTy, - transform: FnReturnTransformation, + coro: CoroutineKind, span: Span, nested_impl_trait_context: ImplTraitContext, ) -> hir::GenericBound<'hir> { @@ -1974,11 +1968,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { }; // "" - let (symbol, lang_item) = match transform { - FnReturnTransformation::Async(..) => (hir::FN_OUTPUT_NAME, hir::LangItem::Future), - FnReturnTransformation::Iterator(..) => { - (hir::ITERATOR_ITEM_NAME, hir::LangItem::Iterator) - } + let (symbol, lang_item) = match coro { + CoroutineKind::Async { .. } => (hir::FN_OUTPUT_NAME, hir::LangItem::Future), + CoroutineKind::Gen { .. } => (hir::ITERATOR_ITEM_NAME, hir::LangItem::Iterator), + CoroutineKind::None => panic!("attemping to lower output type of non-coroutine fn"), }; let future_args = self.arena.alloc(hir::GenericArgs { diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 45b5e63bd1b..ce680afdc3e 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1271,7 +1271,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { // Functions cannot both be `const async` if let Some(&FnHeader { constness: Const::Yes(cspan), - asyncness: Async::Yes { span: aspan, .. }, + coro_kind: CoroutineKind::Async { span: aspan, .. }, .. }) = fk.header() { diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index cbdcc683b56..1e69674d30a 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1490,9 +1490,15 @@ impl<'a> State<'a> { } } - fn print_asyncness(&mut self, asyncness: ast::Async) { - if asyncness.is_async() { - self.word_nbsp("async"); + fn print_coro_kind(&mut self, coro_kind: ast::CoroutineKind) { + match coro_kind { + ast::CoroutineKind::None => {} + ast::CoroutineKind::Gen { .. } => { + self.word_nbsp("gen"); + } + ast::CoroutineKind::Async { .. } => { + self.word_nbsp("async"); + } } } @@ -1685,7 +1691,7 @@ impl<'a> State<'a> { fn print_fn_header_info(&mut self, header: ast::FnHeader) { self.print_constness(header.constness); - self.print_asyncness(header.asyncness); + self.print_coro_kind(header.coro_kind); self.print_unsafety(header.unsafety); match header.ext { diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 45a55e20ca7..d8641e745d0 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -413,7 +413,7 @@ impl<'a> State<'a> { binder, capture_clause, constness, - asyncness, + coro_kind, movability, fn_decl, body, @@ -423,7 +423,7 @@ impl<'a> State<'a> { self.print_closure_binder(binder); self.print_constness(*constness); self.print_movability(*movability); - self.print_asyncness(*asyncness); + self.print_coro_kind(*coro_kind); self.print_capture_clause(*capture_clause); self.print_fn_params_and_ret(fn_decl, true); diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index bf1e1ebf5dd..8f4234b4138 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -541,7 +541,7 @@ fn check_test_signature( return Err(sd.emit_err(errors::TestBadFn { span: i.span, cause: span, kind: "unsafe" })); } - if let ast::Async::Yes { span, .. } = f.sig.header.asyncness { + if let ast::CoroutineKind::Async { span, .. } = f.sig.header.coro_kind { return Err(sd.emit_err(errors::TestBadFn { span: i.span, cause: span, kind: "async" })); } diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 9a8d0d691f0..49b52d265d6 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -547,7 +547,7 @@ impl<'a> ExtCtxt<'a> { binder: ast::ClosureBinder::NotPresent, capture_clause: ast::CaptureBy::Ref, constness: ast::Const::No, - asyncness: ast::Async::No, + coro_kind: ast::CoroutineKind::None, movability: ast::Movability::Movable, fn_decl, body, diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 4cccaeeca84..41fbf1f3e2c 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -162,7 +162,9 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> // Explicitly check for lints associated with 'closure_id', since // it does not have a corresponding AST node if let ast_visit::FnKind::Fn(_, _, sig, _, _, _) = fk { - if let ast::Async::Yes { closure_id, .. } = sig.header.asyncness { + if let ast::CoroutineKind::Async { closure_id, .. } + | ast::CoroutineKind::Gen { closure_id, .. } = sig.header.coro_kind + { self.check_id(closure_id); } } @@ -223,7 +225,9 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> // it does not have a corresponding AST node match e.kind { ast::ExprKind::Closure(box ast::Closure { - asyncness: ast::Async::Yes { closure_id, .. }, + coro_kind: + ast::CoroutineKind::Async { closure_id, .. } + | ast::CoroutineKind::Gen { closure_id, .. }, .. }) => self.check_id(closure_id), _ => {} diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index f8444881d1a..8a09d4e0549 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -21,7 +21,9 @@ use rustc_ast::util::parser::{prec_let_scrutinee_needs_par, AssocOp, Fixity}; use rustc_ast::visit::Visitor; use rustc_ast::{self as ast, AttrStyle, AttrVec, CaptureBy, ExprField, UnOp, DUMMY_NODE_ID}; use rustc_ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty, TyKind}; -use rustc_ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits}; +use rustc_ast::{ + Arm, BlockCheckMode, CoroutineKind, Expr, ExprKind, Label, Movability, RangeLimits, +}; use rustc_ast::{ClosureBinder, MetaItemLit, StmtKind}; use rustc_ast_pretty::pprust; use rustc_data_structures::stack::ensure_sufficient_stack; @@ -2237,7 +2239,7 @@ impl<'a> Parser<'a> { let asyncness = if self.token.uninterpolated_span().at_least_rust_2018() { self.parse_asyncness(Case::Sensitive) } else { - Async::No + CoroutineKind::None }; let capture_clause = self.parse_capture_clause()?; @@ -2261,7 +2263,7 @@ impl<'a> Parser<'a> { } }; - if let Async::Yes { span, .. } = asyncness { + if let CoroutineKind::Async { span, .. } = asyncness { // Feature-gate `async ||` closures. self.sess.gated_spans.gate(sym::async_closure, span); } @@ -2284,7 +2286,7 @@ impl<'a> Parser<'a> { binder, capture_clause, constness, - asyncness, + coro_kind: asyncness, movability, fn_decl, body, diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index c6d1ea882e9..2bee4d5d5c6 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -11,8 +11,8 @@ use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree}; use rustc_ast::util::case::Case; use rustc_ast::MacCall; use rustc_ast::{self as ast, AttrVec, Attribute, DUMMY_NODE_ID}; -use rustc_ast::{Async, Const, Defaultness, IsAuto, Mutability, Unsafe, UseTree, UseTreeKind}; use rustc_ast::{BindingAnnotation, Block, FnDecl, FnSig, Param, SelfKind}; +use rustc_ast::{Const, Defaultness, IsAuto, Mutability, Unsafe, UseTree, UseTreeKind}; use rustc_ast::{EnumDef, FieldDef, Generics, TraitRef, Ty, TyKind, Variant, VariantData}; use rustc_ast::{FnHeader, ForeignItem, Path, PathSegment, Visibility, VisibilityKind}; use rustc_ast_pretty::pprust; @@ -2401,7 +2401,7 @@ impl<'a> Parser<'a> { let ext_start_sp = self.token.span; let ext = self.parse_extern(case); - if let Async::Yes { span, .. } = asyncness { + if let CoroutineKind::Async { span, .. } = asyncness { if span.is_rust_2015() { self.sess.emit_err(errors::AsyncFnIn2015 { span, @@ -2410,12 +2410,14 @@ impl<'a> Parser<'a> { } } - if let Gen::Yes { span, .. } = genness { + if let CoroutineKind::Gen { span, .. } = genness { self.sess.gated_spans.gate(sym::gen_blocks, span); } - if let (Async::Yes { span: async_span, .. }, Gen::Yes { span: gen_span, .. }) = - (asyncness, genness) + if let ( + CoroutineKind::Async { span: async_span, .. }, + CoroutineKind::Gen { span: gen_span, .. }, + ) = (asyncness, genness) { self.sess.emit_err(errors::AsyncGenFn { span: async_span.to(gen_span) }); } @@ -2450,9 +2452,12 @@ impl<'a> Parser<'a> { } } else if self.check_keyword(kw::Async) { match asyncness { - Async::Yes { span, .. } => Some(WrongKw::Duplicated(span)), - Async::No => { - recover_asyncness = Async::Yes { + CoroutineKind::Async { span, .. } => Some(WrongKw::Duplicated(span)), + CoroutineKind::Gen { .. } => { + panic!("not sure how to recover here") + } + CoroutineKind::None => { + recover_asyncness = CoroutineKind::Async { span: self.token.span, closure_id: DUMMY_NODE_ID, return_impl_trait_id: DUMMY_NODE_ID, @@ -2537,6 +2542,8 @@ impl<'a> Parser<'a> { } } + // FIXME(eholk): add keyword recovery logic for genness + if wrong_kw.is_some() && self.may_recover() && self.look_ahead(1, |tok| tok.is_keyword_case(kw::Fn, case)) @@ -2548,8 +2555,7 @@ impl<'a> Parser<'a> { return Ok(FnHeader { constness: recover_constness, unsafety: recover_unsafety, - asyncness: recover_asyncness, - genness, // FIXME(eholk): add keyword recovery logic here too. + coro_kind: recover_asyncness, ext, }); } @@ -2559,7 +2565,13 @@ impl<'a> Parser<'a> { } } - Ok(FnHeader { constness, unsafety, asyncness, ext, genness }) + let coro_kind = match asyncness { + CoroutineKind::Async { .. } => asyncness, + CoroutineKind::Gen { .. } => unreachable!("asycness cannot be Gen"), + CoroutineKind::None => genness, + }; + + Ok(FnHeader { constness, unsafety, coro_kind, ext }) } /// Parses the parameter list and result type of a function declaration. diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index c680a950584..a9da3043117 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -11,7 +11,6 @@ mod stmt; mod ty; use crate::lexer::UnmatchedDelim; -use ast::Gen; pub use attr_wrapper::AttrWrapper; pub use diagnostics::AttemptLocalParseRecovery; pub(crate) use expr::ForbiddenLetReason; @@ -25,9 +24,10 @@ use rustc_ast::tokenstream::{AttributesData, DelimSpan, Spacing}; use rustc_ast::tokenstream::{TokenStream, TokenTree, TokenTreeCursor}; use rustc_ast::util::case::Case; use rustc_ast::AttrId; +use rustc_ast::CoroutineKind; use rustc_ast::DUMMY_NODE_ID; use rustc_ast::{self as ast, AnonConst, Const, DelimArgs, Extern}; -use rustc_ast::{Async, AttrArgs, AttrArgsEq, Expr, ExprKind, Mutability, StrLit}; +use rustc_ast::{AttrArgs, AttrArgsEq, Expr, ExprKind, Mutability, StrLit}; use rustc_ast::{HasAttrs, HasTokens, Unsafe, Visibility, VisibilityKind}; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; @@ -1125,22 +1125,30 @@ impl<'a> Parser<'a> { } /// Parses asyncness: `async` or nothing. - fn parse_asyncness(&mut self, case: Case) -> Async { + fn parse_asyncness(&mut self, case: Case) -> CoroutineKind { if self.eat_keyword_case(kw::Async, case) { let span = self.prev_token.uninterpolated_span(); - Async::Yes { span, closure_id: DUMMY_NODE_ID, return_impl_trait_id: DUMMY_NODE_ID } + CoroutineKind::Async { + span, + closure_id: DUMMY_NODE_ID, + return_impl_trait_id: DUMMY_NODE_ID, + } } else { - Async::No + CoroutineKind::None } } /// Parses genness: `gen` or nothing. - fn parse_genness(&mut self, case: Case) -> Gen { + fn parse_genness(&mut self, case: Case) -> CoroutineKind { if self.token.span.at_least_rust_2024() && self.eat_keyword_case(kw::Gen, case) { let span = self.prev_token.uninterpolated_span(); - Gen::Yes { span, closure_id: DUMMY_NODE_ID, return_impl_trait_id: DUMMY_NODE_ID } + CoroutineKind::Gen { + span, + closure_id: DUMMY_NODE_ID, + return_impl_trait_id: DUMMY_NODE_ID, + } } else { - Gen::No + CoroutineKind::None } } diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index cf0dd39b562..a8e147a05b0 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -596,7 +596,7 @@ impl<'a> Parser<'a> { tokens: None, }; let span_start = self.token.span; - let ast::FnHeader { ext, unsafety, constness, asyncness, genness: _ } = + let ast::FnHeader { ext, unsafety, constness, coro_kind } = self.parse_fn_front_matter(&inherited_vis, Case::Sensitive)?; if self.may_recover() && self.token.kind == TokenKind::Lt { self.recover_fn_ptr_with_generics(lo, &mut params, param_insertion_point)?; @@ -609,7 +609,7 @@ impl<'a> Parser<'a> { // cover it. self.sess.emit_err(FnPointerCannotBeConst { span: whole_span, qualifier: span }); } - if let ast::Async::Yes { span, .. } = asyncness { + if let ast::CoroutineKind::Async { span, .. } = coro_kind { self.sess.emit_err(FnPointerCannotBeAsync { span: whole_span, qualifier: span }); } // FIXME(eholk): emit a similar error for `gen fn()` diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 306492eaa96..c43ec99f42a 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -157,8 +157,8 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { fn visit_fn(&mut self, fn_kind: FnKind<'a>, span: Span, _: NodeId) { if let FnKind::Fn(_, _, sig, _, generics, body) = fn_kind { // FIXME(eholk): handle `async gen fn` - if let (Async::Yes { closure_id, .. }, _) | (_, Gen::Yes { closure_id, .. }) = - (sig.header.asyncness, sig.header.genness) + if let CoroutineKind::Async { closure_id, .. } | CoroutineKind::Gen { closure_id, .. } = + sig.header.coro_kind { self.visit_generics(generics); @@ -284,11 +284,12 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { // Async closures desugar to closures inside of closures, so // we must create two defs. let closure_def = self.create_def(expr.id, kw::Empty, DefKind::Closure, expr.span); - match closure.asyncness { - Async::Yes { closure_id, .. } => { + match closure.coro_kind { + CoroutineKind::Async { closure_id, .. } + | CoroutineKind::Gen { closure_id, .. } => { self.create_def(closure_id, kw::Empty, DefKind::Closure, expr.span) } - Async::No => closure_def, + CoroutineKind::None => closure_def, } } ExprKind::Gen(_, _, _) => { diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index f20d5829030..2f7d1835113 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -916,7 +916,7 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, &sig.decl.output, ); - if let Some((async_node_id, _)) = sig.header.asyncness.opt_return_id() { + if let Some((async_node_id, _)) = sig.header.coro_kind.opt_return_id() { this.record_lifetime_params_for_impl_trait(async_node_id); } }, @@ -940,7 +940,7 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, this.visit_generics(generics); let declaration = &sig.decl; - let async_node_id = sig.header.asyncness.opt_return_id(); + let async_node_id = sig.header.coro_kind.opt_return_id(); this.with_lifetime_rib( LifetimeRibKind::AnonymousCreateParameter { @@ -4289,7 +4289,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { // resolve the arguments within the proper scopes so that usages of them inside the // closure are detected as upvars rather than normal closure arg usages. ExprKind::Closure(box ast::Closure { - asyncness: Async::Yes { .. }, + coro_kind: CoroutineKind::Async { .. }, ref fn_decl, ref body, .. diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs index ba452775015..ec4abc21f48 100644 --- a/src/tools/clippy/clippy_lints/src/doc/mod.rs +++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs @@ -655,6 +655,205 @@ fn check_doc<'a, Events: Iterator, Range, trimmed_text: &str, range: Range, fragments: Fragments<'_>) { + if trimmed_text.starts_with('\'') + && trimmed_text.ends_with('\'') + && let Some(span) = fragments.span(cx, range) + { + span_lint( + cx, + DOC_LINK_WITH_QUOTES, + span, + "possible intra-doc link using quotes instead of backticks", + ); + } +} + +fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, range: Range, fragments: Fragments<'_>) { + fn has_needless_main(code: String, edition: Edition) -> bool { + rustc_driver::catch_fatal_errors(|| { + rustc_span::create_session_globals_then(edition, || { + let filename = FileName::anon_source_code(&code); + + let fallback_bundle = + rustc_errors::fallback_fluent_bundle(rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false); + let emitter = EmitterWriter::new(Box::new(io::sink()), fallback_bundle); + let handler = Handler::with_emitter(Box::new(emitter)).disable_warnings(); + #[expect(clippy::arc_with_non_send_sync)] // `Lrc` is expected by with_span_handler + let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let sess = ParseSess::with_span_handler(handler, sm); + + let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code) { + Ok(p) => p, + Err(errs) => { + drop(errs); + return false; + }, + }; + + let mut relevant_main_found = false; + loop { + match parser.parse_item(ForceCollect::No) { + Ok(Some(item)) => match &item.kind { + ItemKind::Fn(box Fn { + sig, body: Some(block), .. + }) if item.ident.name == sym::main => { + let is_async = sig.header.coro_kind.is_async(); + let returns_nothing = match &sig.decl.output { + FnRetTy::Default(..) => true, + FnRetTy::Ty(ty) if ty.kind.is_unit() => true, + FnRetTy::Ty(_) => false, + }; + + if returns_nothing && !is_async && !block.stmts.is_empty() { + // This main function should be linted, but only if there are no other functions + relevant_main_found = true; + } else { + // This main function should not be linted, we're done + return false; + } + }, + // Tests with one of these items are ignored + ItemKind::Static(..) + | ItemKind::Const(..) + | ItemKind::ExternCrate(..) + | ItemKind::ForeignMod(..) + // Another function was found; this case is ignored + | ItemKind::Fn(..) => return false, + _ => {}, + }, + Ok(None) => break, + Err(e) => { + e.cancel(); + return false; + }, + } + } + + relevant_main_found + }) + }) + .ok() + .unwrap_or_default() + } + + let trailing_whitespace = text.len() - text.trim_end().len(); + + // Because of the global session, we need to create a new session in a different thread with + // the edition we need. + let text = text.to_owned(); + if thread::spawn(move || has_needless_main(text, edition)) + .join() + .expect("thread::spawn failed") + && let Some(span) = fragments.span(cx, range.start..range.end - trailing_whitespace) + { + span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest"); + } +} + +fn check_text(cx: &LateContext<'_>, valid_idents: &FxHashSet, text: &str, span: Span) { + for word in text.split(|c: char| c.is_whitespace() || c == '\'') { + // Trim punctuation as in `some comment (see foo::bar).` + // ^^ + // Or even as in `_foo bar_` which is emphasized. Also preserve `::` as a prefix/suffix. + let mut word = word.trim_matches(|c: char| !c.is_alphanumeric() && c != ':'); + + // Remove leading or trailing single `:` which may be part of a sentence. + if word.starts_with(':') && !word.starts_with("::") { + word = word.trim_start_matches(':'); + } + if word.ends_with(':') && !word.ends_with("::") { + word = word.trim_end_matches(':'); + } + + if valid_idents.contains(word) || word.chars().all(|c| c == ':') { + continue; + } + + // Adjust for the current word + let offset = word.as_ptr() as usize - text.as_ptr() as usize; + let span = Span::new( + span.lo() + BytePos::from_usize(offset), + span.lo() + BytePos::from_usize(offset + word.len()), + span.ctxt(), + span.parent(), + ); + + check_word(cx, word, span); + } +} + +fn check_word(cx: &LateContext<'_>, word: &str, span: Span) { + /// Checks if a string is upper-camel-case, i.e., starts with an uppercase and + /// contains at least two uppercase letters (`Clippy` is ok) and one lower-case + /// letter (`NASA` is ok). + /// Plurals are also excluded (`IDs` is ok). + fn is_camel_case(s: &str) -> bool { + if s.starts_with(|c: char| c.is_ascii_digit() | c.is_ascii_lowercase()) { + return false; + } + + let s = s.strip_suffix('s').unwrap_or(s); + + s.chars().all(char::is_alphanumeric) + && s.chars().filter(|&c| c.is_uppercase()).take(2).count() > 1 + && s.chars().filter(|&c| c.is_lowercase()).take(1).count() > 0 + } + + fn has_underscore(s: &str) -> bool { + s != "_" && !s.contains("\\_") && s.contains('_') + } + + fn has_hyphen(s: &str) -> bool { + s != "-" && s.contains('-') + } + + if let Ok(url) = Url::parse(word) { + // try to get around the fact that `foo::bar` parses as a valid URL + if !url.cannot_be_a_base() { + span_lint( + cx, + DOC_MARKDOWN, + span, + "you should put bare URLs between `<`/`>` or make a proper Markdown link", + ); + + return; + } + } + + // We assume that mixed-case words are not meant to be put inside backticks. (Issue #2343) + if has_underscore(word) && has_hyphen(word) { + return; + } + + if has_underscore(word) || word.contains("::") || is_camel_case(word) { + let mut applicability = Applicability::MachineApplicable; + + span_lint_and_then( + cx, + DOC_MARKDOWN, + span, + "item in documentation is missing backticks", + |diag| { + let snippet = snippet_with_applicability(cx, span, "..", &mut applicability); + diag.span_suggestion_with_style( + span, + "try", + format!("`{snippet}`"), + applicability, + // always show the suggestion in a separate line, since the + // inline presentation adds another pair of backticks + SuggestionStyle::ShowAlways, + ); + }, + ); + } +} + +>>>>>>> d116f1718f1 (Merge Async and Gen into CoroutineKind):src/tools/clippy/clippy_lints/src/doc.rs struct FindPanicUnwrap<'a, 'tcx> { cx: &'a LateContext<'tcx>, panic_span: Option, diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs index a2c61e07b70..ac9da383b93 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs @@ -188,7 +188,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { Closure(box ast::Closure { binder: lb, capture_clause: lc, - asyncness: la, + coro_kind: la, movability: lm, fn_decl: lf, body: le, @@ -197,7 +197,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { Closure(box ast::Closure { binder: rb, capture_clause: rc, - asyncness: ra, + coro_kind: ra, movability: rm, fn_decl: rf, body: re, @@ -565,7 +565,7 @@ pub fn eq_fn_sig(l: &FnSig, r: &FnSig) -> bool { pub fn eq_fn_header(l: &FnHeader, r: &FnHeader) -> bool { matches!(l.unsafety, Unsafe::No) == matches!(r.unsafety, Unsafe::No) - && l.asyncness.is_async() == r.asyncness.is_async() + && (l.coro_kind.is_async() == r.coro_kind.is_async() || l.coro_kind.is_gen() == r.coro_kind.is_gen()) && matches!(l.constness, Const::No) == matches!(r.constness, Const::No) && eq_ext(&l.ext, &r.ext) } diff --git a/src/tools/rustfmt/src/closures.rs b/src/tools/rustfmt/src/closures.rs index 8a4089a56f0..23cd6e4c092 100644 --- a/src/tools/rustfmt/src/closures.rs +++ b/src/tools/rustfmt/src/closures.rs @@ -29,7 +29,7 @@ pub(crate) fn rewrite_closure( binder: &ast::ClosureBinder, constness: ast::Const, capture: ast::CaptureBy, - is_async: &ast::Async, + coro_kind: &ast::CoroutineKind, movability: ast::Movability, fn_decl: &ast::FnDecl, body: &ast::Expr, @@ -40,7 +40,7 @@ pub(crate) fn rewrite_closure( debug!("rewrite_closure {:?}", body); let (prefix, extra_offset) = rewrite_closure_fn_decl( - binder, constness, capture, is_async, movability, fn_decl, body, span, context, shape, + binder, constness, capture, coro_kind, movability, fn_decl, body, span, context, shape, )?; // 1 = space between `|...|` and body. let body_shape = shape.offset_left(extra_offset)?; @@ -233,7 +233,7 @@ fn rewrite_closure_fn_decl( binder: &ast::ClosureBinder, constness: ast::Const, capture: ast::CaptureBy, - asyncness: &ast::Async, + coro_kind: &ast::CoroutineKind, movability: ast::Movability, fn_decl: &ast::FnDecl, body: &ast::Expr, @@ -263,7 +263,8 @@ fn rewrite_closure_fn_decl( } else { "" }; - let is_async = if asyncness.is_async() { "async " } else { "" }; + let is_async = if coro_kind.is_async() { "async " } else { "" }; + let is_gen = if coro_kind.is_gen() { "gen " } else { "" }; let mover = if matches!(capture, ast::CaptureBy::Value { .. }) { "move " } else { @@ -272,7 +273,14 @@ fn rewrite_closure_fn_decl( // 4 = "|| {".len(), which is overconservative when the closure consists of // a single expression. let nested_shape = shape - .shrink_left(binder.len() + const_.len() + immovable.len() + is_async.len() + mover.len())? + .shrink_left( + binder.len() + + const_.len() + + immovable.len() + + is_async.len() + + is_gen.len() + + mover.len(), + )? .sub_width(4)?; // 1 = | @@ -310,7 +318,7 @@ fn rewrite_closure_fn_decl( .tactic(tactic) .preserve_newline(true); let list_str = write_list(&item_vec, &fmt)?; - let mut prefix = format!("{binder}{const_}{immovable}{is_async}{mover}|{list_str}|"); + let mut prefix = format!("{binder}{const_}{immovable}{is_async}{is_gen}{mover}|{list_str}|"); if !ret_str.is_empty() { if prefix.contains('\n') { @@ -339,7 +347,7 @@ pub(crate) fn rewrite_last_closure( ref binder, constness, capture_clause, - ref asyncness, + ref coro_kind, movability, ref fn_decl, ref body, @@ -360,7 +368,7 @@ pub(crate) fn rewrite_last_closure( binder, constness, capture_clause, - asyncness, + coro_kind, movability, fn_decl, body, diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs index 60e0e007b1d..4515c27be37 100644 --- a/src/tools/rustfmt/src/expr.rs +++ b/src/tools/rustfmt/src/expr.rs @@ -212,7 +212,7 @@ pub(crate) fn format_expr( &cl.binder, cl.constness, cl.capture_clause, - &cl.asyncness, + &cl.coro_kind, cl.movability, &cl.fn_decl, &cl.body, diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs index edb5a5b629a..0a1f823fe87 100644 --- a/src/tools/rustfmt/src/items.rs +++ b/src/tools/rustfmt/src/items.rs @@ -287,7 +287,7 @@ pub(crate) struct FnSig<'a> { decl: &'a ast::FnDecl, generics: &'a ast::Generics, ext: ast::Extern, - is_async: Cow<'a, ast::Async>, + coro_kind: Cow<'a, ast::CoroutineKind>, constness: ast::Const, defaultness: ast::Defaultness, unsafety: ast::Unsafe, @@ -302,7 +302,7 @@ impl<'a> FnSig<'a> { ) -> FnSig<'a> { FnSig { unsafety: method_sig.header.unsafety, - is_async: Cow::Borrowed(&method_sig.header.asyncness), + coro_kind: Cow::Borrowed(&method_sig.header.coro_kind), constness: method_sig.header.constness, defaultness: ast::Defaultness::Final, ext: method_sig.header.ext, @@ -328,7 +328,7 @@ impl<'a> FnSig<'a> { generics, ext: fn_sig.header.ext, constness: fn_sig.header.constness, - is_async: Cow::Borrowed(&fn_sig.header.asyncness), + coro_kind: Cow::Borrowed(&fn_sig.header.coro_kind), defaultness, unsafety: fn_sig.header.unsafety, visibility: vis, @@ -343,7 +343,7 @@ impl<'a> FnSig<'a> { result.push_str(&*format_visibility(context, self.visibility)); result.push_str(format_defaultness(self.defaultness)); result.push_str(format_constness(self.constness)); - result.push_str(format_async(&self.is_async)); + result.push_str(format_coro(&self.coro_kind)); result.push_str(format_unsafety(self.unsafety)); result.push_str(&format_extern( self.ext, diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs index fd49030bf1b..18d8f0cdbd7 100644 --- a/src/tools/rustfmt/src/utils.rs +++ b/src/tools/rustfmt/src/utils.rs @@ -75,10 +75,11 @@ pub(crate) fn format_visibility( } #[inline] -pub(crate) fn format_async(is_async: &ast::Async) -> &'static str { - match is_async { - ast::Async::Yes { .. } => "async ", - ast::Async::No => "", +pub(crate) fn format_coro(coro_kind: &ast::CoroutineKind) -> &'static str { + match coro_kind { + ast::CoroutineKind::Async { .. } => "async ", + ast::CoroutineKind::Gen { .. } => "gen ", + ast::CoroutineKind::None => "", } } diff --git a/tests/ui/coroutine/gen_fn_iter.rs b/tests/ui/coroutine/gen_fn_iter.rs index 222ab571415..da01bc96ef4 100644 --- a/tests/ui/coroutine/gen_fn_iter.rs +++ b/tests/ui/coroutine/gen_fn_iter.rs @@ -3,6 +3,8 @@ // run-pass #![feature(gen_blocks)] +// make sure that a ridiculously simple gen fn works as an iterator. + gen fn foo() -> i32 { yield 1; yield 2; From f9d1f922dcd335e534d40923ab54088c07a5403e Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Thu, 30 Nov 2023 16:39:56 -0800 Subject: [PATCH 07/12] Option --- compiler/rustc_ast/src/ast.rs | 25 ++++++------------ compiler/rustc_ast/src/mut_visit.rs | 5 ++-- compiler/rustc_ast_lowering/src/expr.rs | 25 ++++++------------ compiler/rustc_ast_lowering/src/item.rs | 23 ++++++++-------- compiler/rustc_ast_lowering/src/lib.rs | 18 +++---------- .../rustc_ast_passes/src/ast_validation.rs | 9 +++++-- compiler/rustc_ast_pretty/src/pprust/state.rs | 3 +-- .../rustc_ast_pretty/src/pprust/state/expr.rs | 2 +- compiler/rustc_builtin_macros/src/test.rs | 2 +- compiler/rustc_expand/src/build.rs | 2 +- compiler/rustc_lint/src/early.rs | 12 ++++++--- compiler/rustc_parse/src/parser/expr.rs | 10 +++---- compiler/rustc_parse/src/parser/item.rs | 26 ++++++++++--------- compiler/rustc_parse/src/parser/mod.rs | 16 ++++++------ compiler/rustc_parse/src/parser/ty.rs | 2 +- compiler/rustc_resolve/src/def_collector.rs | 16 ++++++------ compiler/rustc_resolve/src/late.rs | 9 ++++--- src/tools/clippy/clippy_lints/src/doc/mod.rs | 2 +- .../clippy/clippy_utils/src/ast_utils.rs | 13 ++++++++-- src/tools/rustfmt/src/closures.rs | 13 +++++++--- src/tools/rustfmt/src/items.rs | 5 ++-- src/tools/rustfmt/src/utils.rs | 1 - 22 files changed, 117 insertions(+), 122 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index bf648388f4e..d6c2bfacf66 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1311,7 +1311,7 @@ pub struct Closure { pub binder: ClosureBinder, pub capture_clause: CaptureBy, pub constness: Const, - pub coro_kind: CoroutineKind, + pub coro_kind: Option, pub movability: Movability, pub fn_decl: P, pub body: P, @@ -2417,8 +2417,6 @@ pub enum CoroutineKind { Async { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId }, /// `gen`, which evaluates to `impl Iterator` Gen { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId }, - /// Neither `async` nor `gen` - None, } impl CoroutineKind { @@ -2430,14 +2428,12 @@ impl CoroutineKind { matches!(self, CoroutineKind::Gen { .. }) } - /// In this case this is an `async` return, the `NodeId` for the generated `impl Trait` item. - pub fn opt_return_id(self) -> Option<(NodeId, Span)> { + /// In this case this is an `async` or `gen` return, the `NodeId` for the generated `impl Trait` + /// item. + pub fn return_id(self) -> (NodeId, Span) { match self { CoroutineKind::Async { return_impl_trait_id, span, .. } - | CoroutineKind::Gen { return_impl_trait_id, span, .. } => { - Some((return_impl_trait_id, span)) - } - CoroutineKind::None => None, + | CoroutineKind::Gen { return_impl_trait_id, span, .. } => (return_impl_trait_id, span), } } } @@ -2842,7 +2838,7 @@ pub struct FnHeader { /// The `unsafe` keyword, if any pub unsafety: Unsafe, /// Whether this is `async`, `gen`, or nothing. - pub coro_kind: CoroutineKind, + pub coro_kind: Option, /// The `const` keyword, if any pub constness: Const, /// The `extern` keyword and corresponding ABI string, if any @@ -2854,7 +2850,7 @@ impl FnHeader { pub fn has_qualifiers(&self) -> bool { let Self { unsafety, coro_kind, constness, ext } = self; matches!(unsafety, Unsafe::Yes(_)) - || !matches!(coro_kind, CoroutineKind::None) + || coro_kind.is_some() || matches!(constness, Const::Yes(_)) || !matches!(ext, Extern::None) } @@ -2862,12 +2858,7 @@ impl FnHeader { impl Default for FnHeader { fn default() -> FnHeader { - FnHeader { - unsafety: Unsafe::No, - coro_kind: CoroutineKind::None, - constness: Const::No, - ext: Extern::None, - } + FnHeader { unsafety: Unsafe::No, coro_kind: None, constness: Const::No, ext: Extern::None } } } diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index f9f767862f5..c6aa7a6ae37 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -878,7 +878,6 @@ pub fn noop_visit_coro_kind(coro_kind: &mut CoroutineKind, vis: & vis.visit_id(closure_id); vis.visit_id(return_impl_trait_id); } - CoroutineKind::None => {} } } @@ -1173,7 +1172,7 @@ fn visit_const_item( pub fn noop_visit_fn_header(header: &mut FnHeader, vis: &mut T) { let FnHeader { unsafety, coro_kind, constness, ext: _ } = header; visit_constness(constness, vis); - vis.visit_coro_kind(coro_kind); + coro_kind.as_mut().map(|coro_kind| vis.visit_coro_kind(coro_kind)); visit_unsafety(unsafety, vis); } @@ -1416,7 +1415,7 @@ pub fn noop_visit_expr( }) => { vis.visit_closure_binder(binder); visit_constness(constness, vis); - vis.visit_coro_kind(coro_kind); + coro_kind.as_mut().map(|coro_kind| vis.visit_coro_kind(coro_kind)); vis.visit_capture_by(capture_clause); vis.visit_fn_decl(fn_decl); vis.visit_expr(body); diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 5846f17c539..3556ee02bd7 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -202,8 +202,10 @@ impl<'hir> LoweringContext<'_, 'hir> { fn_decl_span, fn_arg_span, }) => match coro_kind { - CoroutineKind::Async { closure_id, .. } - | CoroutineKind::Gen { closure_id, .. } => self.lower_expr_async_closure( + Some( + CoroutineKind::Async { closure_id, .. } + | CoroutineKind::Gen { closure_id, .. }, + ) => self.lower_expr_async_closure( binder, *capture_clause, e.id, @@ -214,7 +216,7 @@ impl<'hir> LoweringContext<'_, 'hir> { *fn_decl_span, *fn_arg_span, ), - CoroutineKind::None => self.lower_expr_closure( + None => self.lower_expr_closure( binder, *capture_clause, e.id, @@ -933,13 +935,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let bound_generic_params = self.lower_lifetime_binder(closure_id, generic_params); // Lower outside new scope to preserve `is_in_loop_condition`. - let fn_decl = self.lower_fn_decl( - decl, - closure_id, - fn_decl_span, - FnDeclKind::Closure, - CoroutineKind::None, - ); + let fn_decl = self.lower_fn_decl(decl, closure_id, fn_decl_span, FnDeclKind::Closure, None); let c = self.arena.alloc(hir::Closure { def_id: self.local_def_id(closure_id), @@ -1054,13 +1050,8 @@ impl<'hir> LoweringContext<'_, 'hir> { // We need to lower the declaration outside the new scope, because we // have to conserve the state of being inside a loop condition for the // closure argument types. - let fn_decl = self.lower_fn_decl( - &outer_decl, - closure_id, - fn_decl_span, - FnDeclKind::Closure, - CoroutineKind::None, - ); + let fn_decl = + self.lower_fn_decl(&outer_decl, closure_id, fn_decl_span, FnDeclKind::Closure, None); let c = self.arena.alloc(hir::Closure { def_id: self.local_def_id(closure_id), diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 5d32e1a78f1..a23a77f45be 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -602,7 +602,7 @@ impl<'hir> LoweringContext<'_, 'hir> { i.id, sig.span, FnDeclKind::ExternFn, - CoroutineKind::None, + None, ), this.lower_fn_params_to_names(fdec), ) @@ -841,7 +841,6 @@ impl<'hir> LoweringContext<'_, 'hir> { }, ), AssocItemKind::Fn(box Fn { sig, generics, body, .. }) => { - self.current_item = Some(i.span); let body_id = self.lower_maybe_coroutine_body( i.span, hir_id, @@ -1025,15 +1024,16 @@ impl<'hir> LoweringContext<'_, 'hir> { span: Span, fn_id: hir::HirId, decl: &FnDecl, - coro_kind: CoroutineKind, + coro_kind: Option, body: Option<&Block>, ) -> hir::BodyId { - let (closure_id, body) = match (coro_kind, body) { - ( - CoroutineKind::Async { closure_id, .. } | CoroutineKind::Gen { closure_id, .. }, - Some(body), - ) => (closure_id, body), - _ => return self.lower_fn_body_block(span, decl, body), + let (Some(coro_kind), Some(body)) = (coro_kind, body) else { + return self.lower_fn_body_block(span, decl, body); + }; + let closure_id = match coro_kind { + CoroutineKind::Async { closure_id, .. } | CoroutineKind::Gen { closure_id, .. } => { + closure_id + } }; self.lower_body(|this| { @@ -1218,7 +1218,6 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::CoroutineSource::Fn, mkbody, ), - CoroutineKind::None => unreachable!("we must have either an async fn or a gen fn"), }; let hir_id = this.lower_node_id(closure_id); @@ -1235,7 +1234,7 @@ impl<'hir> LoweringContext<'_, 'hir> { sig: &FnSig, id: NodeId, kind: FnDeclKind, - coro_kind: CoroutineKind, + coro_kind: Option, ) -> (&'hir hir::Generics<'hir>, hir::FnSig<'hir>) { let header = self.lower_fn_header(sig.header); let itctx = ImplTraitContext::Universal; @@ -1247,7 +1246,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } fn lower_fn_header(&mut self, h: FnHeader) -> hir::FnHeader { - let asyncness = if let CoroutineKind::Async { span, .. } = h.coro_kind { + let asyncness = if let Some(CoroutineKind::Async { span, .. }) = h.coro_kind { hir::IsAsync::Async(span) } else { hir::IsAsync::NotAsync diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 92650f0c47e..a35b1513e14 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1359,13 +1359,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { generic_params, unsafety: self.lower_unsafety(f.unsafety), abi: self.lower_extern(f.ext), - decl: self.lower_fn_decl( - &f.decl, - t.id, - t.span, - FnDeclKind::Pointer, - CoroutineKind::None, - ), + decl: self.lower_fn_decl(&f.decl, t.id, t.span, FnDeclKind::Pointer, None), param_names: self.lower_fn_params_to_names(&f.decl), })) } @@ -1800,7 +1794,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn_node_id: NodeId, fn_span: Span, kind: FnDeclKind, - coro: CoroutineKind, + coro: Option, ) -> &'hir hir::FnDecl<'hir> { let c_variadic = decl.c_variadic(); @@ -1830,11 +1824,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { })); let output = match coro { - CoroutineKind::Async { .. } | CoroutineKind::Gen { .. } => { + Some(coro) => { let fn_def_id = self.local_def_id(fn_node_id); self.lower_coroutine_fn_ret_ty(&decl.output, fn_def_id, coro, kind, fn_span) } - CoroutineKind::None => match &decl.output { + None => match &decl.output { FnRetTy::Ty(ty) => { let context = if kind.return_impl_trait_allowed() { let fn_def_id = self.local_def_id(fn_node_id); @@ -1911,9 +1905,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let opaque_ty_node_id = match coro { CoroutineKind::Async { return_impl_trait_id, .. } | CoroutineKind::Gen { return_impl_trait_id, .. } => return_impl_trait_id, - CoroutineKind::None => { - unreachable!("lower_coroutine_fn_ret_ty must be called with either Async or Gen") - } }; let captured_lifetimes: Vec<_> = self @@ -1971,7 +1962,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let (symbol, lang_item) = match coro { CoroutineKind::Async { .. } => (hir::FN_OUTPUT_NAME, hir::LangItem::Future), CoroutineKind::Gen { .. } => (hir::ITERATOR_ITEM_NAME, hir::LangItem::Iterator), - CoroutineKind::None => panic!("attemping to lower output type of non-coroutine fn"), }; let future_args = self.arena.alloc(hir::GenericArgs { diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index ce680afdc3e..311ab96aba0 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1268,13 +1268,18 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.check_c_variadic_type(fk); - // Functions cannot both be `const async` + // Functions cannot both be `const async` or `const gen` if let Some(&FnHeader { constness: Const::Yes(cspan), - coro_kind: CoroutineKind::Async { span: aspan, .. }, + coro_kind: + Some( + CoroutineKind::Async { span: aspan, .. } + | CoroutineKind::Gen { span: aspan, .. }, + ), .. }) = fk.header() { + // FIXME(eholk): Report a different error for `const gen` self.err_handler().emit_err(errors::ConstAndAsync { spans: vec![cspan, aspan], cspan, diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 1e69674d30a..1ad28ffbf2b 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1492,7 +1492,6 @@ impl<'a> State<'a> { fn print_coro_kind(&mut self, coro_kind: ast::CoroutineKind) { match coro_kind { - ast::CoroutineKind::None => {} ast::CoroutineKind::Gen { .. } => { self.word_nbsp("gen"); } @@ -1691,7 +1690,7 @@ impl<'a> State<'a> { fn print_fn_header_info(&mut self, header: ast::FnHeader) { self.print_constness(header.constness); - self.print_coro_kind(header.coro_kind); + header.coro_kind.map(|coro_kind| self.print_coro_kind(coro_kind)); self.print_unsafety(header.unsafety); match header.ext { diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index d8641e745d0..0082d6e350e 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -423,7 +423,7 @@ impl<'a> State<'a> { self.print_closure_binder(binder); self.print_constness(*constness); self.print_movability(*movability); - self.print_coro_kind(*coro_kind); + coro_kind.map(|coro_kind| self.print_coro_kind(coro_kind)); self.print_capture_clause(*capture_clause); self.print_fn_params_and_ret(fn_decl, true); diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index 8f4234b4138..38fdddf5834 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -541,7 +541,7 @@ fn check_test_signature( return Err(sd.emit_err(errors::TestBadFn { span: i.span, cause: span, kind: "unsafe" })); } - if let ast::CoroutineKind::Async { span, .. } = f.sig.header.coro_kind { + if let Some(ast::CoroutineKind::Async { span, .. }) = f.sig.header.coro_kind { return Err(sd.emit_err(errors::TestBadFn { span: i.span, cause: span, kind: "async" })); } diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 49b52d265d6..794e11d87d1 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -547,7 +547,7 @@ impl<'a> ExtCtxt<'a> { binder: ast::ClosureBinder::NotPresent, capture_clause: ast::CaptureBy::Ref, constness: ast::Const::No, - coro_kind: ast::CoroutineKind::None, + coro_kind: None, movability: ast::Movability::Movable, fn_decl, body, diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 41fbf1f3e2c..7c4f81a4c39 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -162,8 +162,10 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> // Explicitly check for lints associated with 'closure_id', since // it does not have a corresponding AST node if let ast_visit::FnKind::Fn(_, _, sig, _, _, _) = fk { - if let ast::CoroutineKind::Async { closure_id, .. } - | ast::CoroutineKind::Gen { closure_id, .. } = sig.header.coro_kind + if let Some( + ast::CoroutineKind::Async { closure_id, .. } + | ast::CoroutineKind::Gen { closure_id, .. }, + ) = sig.header.coro_kind { self.check_id(closure_id); } @@ -226,8 +228,10 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> match e.kind { ast::ExprKind::Closure(box ast::Closure { coro_kind: - ast::CoroutineKind::Async { closure_id, .. } - | ast::CoroutineKind::Gen { closure_id, .. }, + Some( + ast::CoroutineKind::Async { closure_id, .. } + | ast::CoroutineKind::Gen { closure_id, .. }, + ), .. }) => self.check_id(closure_id), _ => {} diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 8a09d4e0549..8482824ec4b 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -10,7 +10,7 @@ use super::{ use crate::errors; use crate::maybe_recover_from_interpolated_ty_qpath; use ast::mut_visit::{noop_visit_expr, MutVisitor}; -use ast::{GenBlockKind, Pat, Path, PathSegment}; +use ast::{CoroutineKind, GenBlockKind, Pat, Path, PathSegment}; use core::mem; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; @@ -21,9 +21,7 @@ use rustc_ast::util::parser::{prec_let_scrutinee_needs_par, AssocOp, Fixity}; use rustc_ast::visit::Visitor; use rustc_ast::{self as ast, AttrStyle, AttrVec, CaptureBy, ExprField, UnOp, DUMMY_NODE_ID}; use rustc_ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty, TyKind}; -use rustc_ast::{ - Arm, BlockCheckMode, CoroutineKind, Expr, ExprKind, Label, Movability, RangeLimits, -}; +use rustc_ast::{Arm, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits}; use rustc_ast::{ClosureBinder, MetaItemLit, StmtKind}; use rustc_ast_pretty::pprust; use rustc_data_structures::stack::ensure_sufficient_stack; @@ -2239,7 +2237,7 @@ impl<'a> Parser<'a> { let asyncness = if self.token.uninterpolated_span().at_least_rust_2018() { self.parse_asyncness(Case::Sensitive) } else { - CoroutineKind::None + None }; let capture_clause = self.parse_capture_clause()?; @@ -2263,7 +2261,7 @@ impl<'a> Parser<'a> { } }; - if let CoroutineKind::Async { span, .. } = asyncness { + if let Some(CoroutineKind::Async { span, .. }) = asyncness { // Feature-gate `async ||` closures. self.sess.gated_spans.gate(sym::async_closure, span); } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 2bee4d5d5c6..589fc46b722 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -2401,7 +2401,7 @@ impl<'a> Parser<'a> { let ext_start_sp = self.token.span; let ext = self.parse_extern(case); - if let CoroutineKind::Async { span, .. } = asyncness { + if let Some(CoroutineKind::Async { span, .. }) = asyncness { if span.is_rust_2015() { self.sess.emit_err(errors::AsyncFnIn2015 { span, @@ -2410,13 +2410,13 @@ impl<'a> Parser<'a> { } } - if let CoroutineKind::Gen { span, .. } = genness { + if let Some(CoroutineKind::Gen { span, .. }) = genness { self.sess.gated_spans.gate(sym::gen_blocks, span); } if let ( - CoroutineKind::Async { span: async_span, .. }, - CoroutineKind::Gen { span: gen_span, .. }, + Some(CoroutineKind::Async { span: async_span, .. }), + Some(CoroutineKind::Gen { span: gen_span, .. }), ) = (asyncness, genness) { self.sess.emit_err(errors::AsyncGenFn { span: async_span.to(gen_span) }); @@ -2452,16 +2452,18 @@ impl<'a> Parser<'a> { } } else if self.check_keyword(kw::Async) { match asyncness { - CoroutineKind::Async { span, .. } => Some(WrongKw::Duplicated(span)), - CoroutineKind::Gen { .. } => { + Some(CoroutineKind::Async { span, .. }) => { + Some(WrongKw::Duplicated(span)) + } + Some(CoroutineKind::Gen { .. }) => { panic!("not sure how to recover here") } - CoroutineKind::None => { - recover_asyncness = CoroutineKind::Async { + None => { + recover_asyncness = Some(CoroutineKind::Async { span: self.token.span, closure_id: DUMMY_NODE_ID, return_impl_trait_id: DUMMY_NODE_ID, - }; + }); Some(WrongKw::Misplaced(unsafe_start_sp)) } } @@ -2566,9 +2568,9 @@ impl<'a> Parser<'a> { } let coro_kind = match asyncness { - CoroutineKind::Async { .. } => asyncness, - CoroutineKind::Gen { .. } => unreachable!("asycness cannot be Gen"), - CoroutineKind::None => genness, + Some(CoroutineKind::Async { .. }) => asyncness, + Some(CoroutineKind::Gen { .. }) => unreachable!("asycness cannot be Gen"), + None => genness, }; Ok(FnHeader { constness, unsafety, coro_kind, ext }) diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index a9da3043117..2816386cbad 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -1125,30 +1125,30 @@ impl<'a> Parser<'a> { } /// Parses asyncness: `async` or nothing. - fn parse_asyncness(&mut self, case: Case) -> CoroutineKind { + fn parse_asyncness(&mut self, case: Case) -> Option { if self.eat_keyword_case(kw::Async, case) { let span = self.prev_token.uninterpolated_span(); - CoroutineKind::Async { + Some(CoroutineKind::Async { span, closure_id: DUMMY_NODE_ID, return_impl_trait_id: DUMMY_NODE_ID, - } + }) } else { - CoroutineKind::None + None } } /// Parses genness: `gen` or nothing. - fn parse_genness(&mut self, case: Case) -> CoroutineKind { + fn parse_genness(&mut self, case: Case) -> Option { if self.token.span.at_least_rust_2024() && self.eat_keyword_case(kw::Gen, case) { let span = self.prev_token.uninterpolated_span(); - CoroutineKind::Gen { + Some(CoroutineKind::Gen { span, closure_id: DUMMY_NODE_ID, return_impl_trait_id: DUMMY_NODE_ID, - } + }) } else { - CoroutineKind::None + None } } diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index a8e147a05b0..73487f4af0e 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -609,7 +609,7 @@ impl<'a> Parser<'a> { // cover it. self.sess.emit_err(FnPointerCannotBeConst { span: whole_span, qualifier: span }); } - if let ast::CoroutineKind::Async { span, .. } = coro_kind { + if let Some(ast::CoroutineKind::Async { span, .. }) = coro_kind { self.sess.emit_err(FnPointerCannotBeAsync { span: whole_span, qualifier: span }); } // FIXME(eholk): emit a similar error for `gen fn()` diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index c43ec99f42a..ab5d3b368eb 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -156,9 +156,9 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { fn visit_fn(&mut self, fn_kind: FnKind<'a>, span: Span, _: NodeId) { if let FnKind::Fn(_, _, sig, _, generics, body) = fn_kind { - // FIXME(eholk): handle `async gen fn` - if let CoroutineKind::Async { closure_id, .. } | CoroutineKind::Gen { closure_id, .. } = - sig.header.coro_kind + if let Some( + CoroutineKind::Async { closure_id, .. } | CoroutineKind::Gen { closure_id, .. }, + ) = sig.header.coro_kind { self.visit_generics(generics); @@ -285,11 +285,11 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { // we must create two defs. let closure_def = self.create_def(expr.id, kw::Empty, DefKind::Closure, expr.span); match closure.coro_kind { - CoroutineKind::Async { closure_id, .. } - | CoroutineKind::Gen { closure_id, .. } => { - self.create_def(closure_id, kw::Empty, DefKind::Closure, expr.span) - } - CoroutineKind::None => closure_def, + Some( + CoroutineKind::Async { closure_id, .. } + | CoroutineKind::Gen { closure_id, .. }, + ) => self.create_def(closure_id, kw::Empty, DefKind::Closure, expr.span), + None => closure_def, } } ExprKind::Gen(_, _, _) => { diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 2f7d1835113..c5d6574af60 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -916,7 +916,9 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, &sig.decl.output, ); - if let Some((async_node_id, _)) = sig.header.coro_kind.opt_return_id() { + if let Some((async_node_id, _)) = + sig.header.coro_kind.map(|coro_kind| coro_kind.return_id()) + { this.record_lifetime_params_for_impl_trait(async_node_id); } }, @@ -940,7 +942,8 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, this.visit_generics(generics); let declaration = &sig.decl; - let async_node_id = sig.header.coro_kind.opt_return_id(); + let async_node_id = + sig.header.coro_kind.map(|coro_kind| coro_kind.return_id()); this.with_lifetime_rib( LifetimeRibKind::AnonymousCreateParameter { @@ -4289,7 +4292,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { // resolve the arguments within the proper scopes so that usages of them inside the // closure are detected as upvars rather than normal closure arg usages. ExprKind::Closure(box ast::Closure { - coro_kind: CoroutineKind::Async { .. }, + coro_kind: Some(CoroutineKind::Async { .. }), ref fn_decl, ref body, .. diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs index ec4abc21f48..0c623dba369 100644 --- a/src/tools/clippy/clippy_lints/src/doc/mod.rs +++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs @@ -700,7 +700,7 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, range: Range { - let is_async = sig.header.coro_kind.is_async(); + let is_async = sig.header.coro_kind.map_or(false, |coro| coro.is_async()); let returns_nothing = match &sig.decl.output { FnRetTy::Default(..) => true, FnRetTy::Ty(ty) if ty.kind.is_unit() => true, diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs index ac9da383b93..12403bbff3c 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs @@ -206,7 +206,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { ) => { eq_closure_binder(lb, rb) && lc == rc - && la.is_async() == ra.is_async() + && la.map_or(false, |la| la.is_async()) == ra.map_or(false, |ra| ra.is_async()) && lm == rm && eq_fn_decl(lf, rf) && eq_expr(le, re) @@ -563,9 +563,18 @@ pub fn eq_fn_sig(l: &FnSig, r: &FnSig) -> bool { eq_fn_decl(&l.decl, &r.decl) && eq_fn_header(&l.header, &r.header) } +fn eq_opt_coro_kind(l: Option, r: Option) -> bool { + match (l, r) { + (Some(CoroutineKind::Async { .. }), Some(CoroutineKind::Async { .. })) + | (Some(CoroutineKind::Gen { .. }), Some(CoroutineKind::Gen { .. })) => true, + (None, None) => true, + _ => false, + } +} + pub fn eq_fn_header(l: &FnHeader, r: &FnHeader) -> bool { matches!(l.unsafety, Unsafe::No) == matches!(r.unsafety, Unsafe::No) - && (l.coro_kind.is_async() == r.coro_kind.is_async() || l.coro_kind.is_gen() == r.coro_kind.is_gen()) + && eq_opt_coro_kind(l.coro_kind, r.coro_kind) && matches!(l.constness, Const::No) == matches!(r.constness, Const::No) && eq_ext(&l.ext, &r.ext) } diff --git a/src/tools/rustfmt/src/closures.rs b/src/tools/rustfmt/src/closures.rs index 23cd6e4c092..d79218e78ee 100644 --- a/src/tools/rustfmt/src/closures.rs +++ b/src/tools/rustfmt/src/closures.rs @@ -29,7 +29,7 @@ pub(crate) fn rewrite_closure( binder: &ast::ClosureBinder, constness: ast::Const, capture: ast::CaptureBy, - coro_kind: &ast::CoroutineKind, + coro_kind: &Option, movability: ast::Movability, fn_decl: &ast::FnDecl, body: &ast::Expr, @@ -233,7 +233,7 @@ fn rewrite_closure_fn_decl( binder: &ast::ClosureBinder, constness: ast::Const, capture: ast::CaptureBy, - coro_kind: &ast::CoroutineKind, + coro_kind: &Option, movability: ast::Movability, fn_decl: &ast::FnDecl, body: &ast::Expr, @@ -263,8 +263,13 @@ fn rewrite_closure_fn_decl( } else { "" }; - let is_async = if coro_kind.is_async() { "async " } else { "" }; - let is_gen = if coro_kind.is_gen() { "gen " } else { "" }; + let (is_async, is_gen) = if let Some(coro_kind) = coro_kind { + let is_async = if coro_kind.is_async() { "async " } else { "" }; + let is_gen = if coro_kind.is_gen() { "gen " } else { "" }; + (is_async, is_gen) + } else { + ("", "") + }; let mover = if matches!(capture, ast::CaptureBy::Value { .. }) { "move " } else { diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs index 0a1f823fe87..4dff65f8cd0 100644 --- a/src/tools/rustfmt/src/items.rs +++ b/src/tools/rustfmt/src/items.rs @@ -287,7 +287,7 @@ pub(crate) struct FnSig<'a> { decl: &'a ast::FnDecl, generics: &'a ast::Generics, ext: ast::Extern, - coro_kind: Cow<'a, ast::CoroutineKind>, + coro_kind: Cow<'a, Option>, constness: ast::Const, defaultness: ast::Defaultness, unsafety: ast::Unsafe, @@ -343,7 +343,8 @@ impl<'a> FnSig<'a> { result.push_str(&*format_visibility(context, self.visibility)); result.push_str(format_defaultness(self.defaultness)); result.push_str(format_constness(self.constness)); - result.push_str(format_coro(&self.coro_kind)); + self.coro_kind + .map(|coro_kind| result.push_str(format_coro(&coro_kind))); result.push_str(format_unsafety(self.unsafety)); result.push_str(&format_extern( self.ext, diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs index 18d8f0cdbd7..5805e417c04 100644 --- a/src/tools/rustfmt/src/utils.rs +++ b/src/tools/rustfmt/src/utils.rs @@ -79,7 +79,6 @@ pub(crate) fn format_coro(coro_kind: &ast::CoroutineKind) -> &'static str { match coro_kind { ast::CoroutineKind::Async { .. } => "async ", ast::CoroutineKind::Gen { .. } => "gen ", - ast::CoroutineKind::None => "", } } From 26f9954971a2895580e02578fe18bc6f9adea3c9 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Thu, 30 Nov 2023 17:32:29 -0800 Subject: [PATCH 08/12] Fix some broken tests --- compiler/rustc_ast_lowering/src/lib.rs | 2 +- tests/ui-fulldeps/pprust-expr-roundtrip.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index a35b1513e14..21a33d137b8 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1786,7 +1786,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { /// `NodeId`. /// /// `transform_return_type`: if `Some`, applies some conversion to the return type, such as is - /// needed for `async fn` and `gen fn`. See [`FnReturnTransformation`] for more details. + /// needed for `async fn` and `gen fn`. See [`CoroutineKind`] for more details. #[instrument(level = "debug", skip(self))] fn lower_fn_decl( &mut self, diff --git a/tests/ui-fulldeps/pprust-expr-roundtrip.rs b/tests/ui-fulldeps/pprust-expr-roundtrip.rs index 685a029dcb2..9e581620ec1 100644 --- a/tests/ui-fulldeps/pprust-expr-roundtrip.rs +++ b/tests/ui-fulldeps/pprust-expr-roundtrip.rs @@ -132,7 +132,7 @@ fn iter_exprs(depth: usize, f: &mut dyn FnMut(P)) { binder: ClosureBinder::NotPresent, capture_clause: CaptureBy::Value { move_kw: DUMMY_SP }, constness: Const::No, - asyncness: Async::No, + coro_kind: None, movability: Movability::Movable, fn_decl: decl.clone(), body: e, From 50ef8006eb68682471894c99b49eb4e39b48c745 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Mon, 4 Dec 2023 13:43:38 -0800 Subject: [PATCH 09/12] Address code review feedback --- compiler/rustc_ast/src/mut_visit.rs | 5 +++-- compiler/rustc_ast_lowering/src/lib.rs | 14 ++++++------- compiler/rustc_ast_lowering/src/path.rs | 6 +++--- .../rustc_ast_passes/src/ast_validation.rs | 2 +- compiler/rustc_builtin_macros/src/test.rs | 4 ++++ compiler/rustc_parse/src/parser/item.rs | 2 +- compiler/rustc_parse/src/parser/ty.rs | 2 +- compiler/rustc_resolve/src/late.rs | 14 +++++++------ src/tools/rustfmt/src/closures.rs | 21 ++++++------------- tests/ui/coroutine/gen_fn_lifetime_capture.rs | 19 +++++++++++++++++ 10 files changed, 53 insertions(+), 36 deletions(-) create mode 100644 tests/ui/coroutine/gen_fn_lifetime_capture.rs diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index c6aa7a6ae37..c6a31fbdbc3 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -873,8 +873,9 @@ pub fn noop_visit_closure_binder(binder: &mut ClosureBinder, vis: pub fn noop_visit_coro_kind(coro_kind: &mut CoroutineKind, vis: &mut T) { match coro_kind { - CoroutineKind::Async { span: _, closure_id, return_impl_trait_id } - | CoroutineKind::Gen { span: _, closure_id, return_impl_trait_id } => { + CoroutineKind::Async { span, closure_id, return_impl_trait_id } + | CoroutineKind::Gen { span, closure_id, return_impl_trait_id } => { + vis.visit_span(span); vis.visit_id(closure_id); vis.visit_id(return_impl_trait_id); } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 21a33d137b8..d435082e121 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1922,7 +1922,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { span, opaque_ty_span, |this| { - let future_bound = this.lower_coroutine_fn_output_type_to_future_bound( + let bound = this.lower_coroutine_fn_output_type_to_bound( output, coro, span, @@ -1931,7 +1931,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn_kind, }, ); - arena_vec![this; future_bound] + arena_vec![this; bound] }, ); @@ -1940,7 +1940,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } /// Transforms `-> T` into `Future`. - fn lower_coroutine_fn_output_type_to_future_bound( + fn lower_coroutine_fn_output_type_to_bound( &mut self, output: &FnRetTy, coro: CoroutineKind, @@ -1958,21 +1958,21 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { FnRetTy::Default(ret_ty_span) => self.arena.alloc(self.ty_tup(*ret_ty_span, &[])), }; - // "" - let (symbol, lang_item) = match coro { + // "<$assoc_ty_name = T>" + let (assoc_ty_name, trait_lang_item) = match coro { CoroutineKind::Async { .. } => (hir::FN_OUTPUT_NAME, hir::LangItem::Future), CoroutineKind::Gen { .. } => (hir::ITERATOR_ITEM_NAME, hir::LangItem::Iterator), }; let future_args = self.arena.alloc(hir::GenericArgs { args: &[], - bindings: arena_vec![self; self.assoc_ty_binding(symbol, span, output_ty)], + bindings: arena_vec![self; self.assoc_ty_binding(assoc_ty_name, span, output_ty)], parenthesized: hir::GenericArgsParentheses::No, span_ext: DUMMY_SP, }); hir::GenericBound::LangItemTrait( - lang_item, + trait_lang_item, self.lower_span(span), self.next_id(), future_args, diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index accb74d7a52..7ab0805d086 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -401,14 +401,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) } - /// An associated type binding `$symbol = $ty`. + /// An associated type binding `$assoc_ty_name = $ty`. pub(crate) fn assoc_ty_binding( &mut self, - symbol: rustc_span::Symbol, + assoc_ty_name: rustc_span::Symbol, span: Span, ty: &'hir hir::Ty<'hir>, ) -> hir::TypeBinding<'hir> { - let ident = Ident::with_dummy_span(symbol); + let ident = Ident::with_dummy_span(assoc_ty_name); let kind = hir::TypeBindingKind::Equality { term: ty.into() }; let args = arena_vec![self;]; let bindings = arena_vec![self;]; diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 311ab96aba0..554ed36b814 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1279,7 +1279,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { .. }) = fk.header() { - // FIXME(eholk): Report a different error for `const gen` + // FIXME(gen_blocks): Report a different error for `const gen` self.err_handler().emit_err(errors::ConstAndAsync { spans: vec![cspan, aspan], cspan, diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index 38fdddf5834..81433155ecf 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -545,6 +545,10 @@ fn check_test_signature( return Err(sd.emit_err(errors::TestBadFn { span: i.span, cause: span, kind: "async" })); } + if let Some(ast::CoroutineKind::Gen { span, .. }) = f.sig.header.coro_kind { + return Err(sd.emit_err(errors::TestBadFn { span: i.span, cause: span, kind: "gen" })); + } + // If the termination trait is active, the compiler will check that the output // type implements the `Termination` trait as `libtest` enforces that. let has_output = match &f.sig.decl.output { diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 589fc46b722..8a987767dc4 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -2544,7 +2544,7 @@ impl<'a> Parser<'a> { } } - // FIXME(eholk): add keyword recovery logic for genness + // FIXME(gen_blocks): add keyword recovery logic for genness if wrong_kw.is_some() && self.may_recover() diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 73487f4af0e..068a99db4ae 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -612,7 +612,7 @@ impl<'a> Parser<'a> { if let Some(ast::CoroutineKind::Async { span, .. }) = coro_kind { self.sess.emit_err(FnPointerCannotBeAsync { span: whole_span, qualifier: span }); } - // FIXME(eholk): emit a similar error for `gen fn()` + // FIXME(gen_blocks): emit a similar error for `gen fn()` let decl_span = span_start.to(self.token.span); Ok(TyKind::BareFn(P(BareFnTy { ext, unsafety, generic_params: params, decl, decl_span }))) } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index c5d6574af60..ad14f5e5225 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -916,10 +916,10 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, &sig.decl.output, ); - if let Some((async_node_id, _)) = + if let Some((coro_node_id, _)) = sig.header.coro_kind.map(|coro_kind| coro_kind.return_id()) { - this.record_lifetime_params_for_impl_trait(async_node_id); + this.record_lifetime_params_for_impl_trait(coro_node_id); } }, ); @@ -942,13 +942,13 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, this.visit_generics(generics); let declaration = &sig.decl; - let async_node_id = + let coro_node_id = sig.header.coro_kind.map(|coro_kind| coro_kind.return_id()); this.with_lifetime_rib( LifetimeRibKind::AnonymousCreateParameter { binder: fn_id, - report_in_path: async_node_id.is_some(), + report_in_path: coro_node_id.is_some(), }, |this| { this.resolve_fn_signature( @@ -961,7 +961,7 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, &declaration.output, ); - if let Some((async_node_id, _)) = async_node_id { + if let Some((async_node_id, _)) = coro_node_id { this.record_lifetime_params_for_impl_trait(async_node_id); } }, @@ -4291,8 +4291,10 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { // `async |x| ...` gets desugared to `|x| async {...}`, so we need to // resolve the arguments within the proper scopes so that usages of them inside the // closure are detected as upvars rather than normal closure arg usages. + // + // Similarly, `gen |x| ...` gets desugared to `|x| gen {...}`, so we handle that too. ExprKind::Closure(box ast::Closure { - coro_kind: Some(CoroutineKind::Async { .. }), + coro_kind: Some(_), ref fn_decl, ref body, .. diff --git a/src/tools/rustfmt/src/closures.rs b/src/tools/rustfmt/src/closures.rs index d79218e78ee..c1ce87eadcb 100644 --- a/src/tools/rustfmt/src/closures.rs +++ b/src/tools/rustfmt/src/closures.rs @@ -263,12 +263,10 @@ fn rewrite_closure_fn_decl( } else { "" }; - let (is_async, is_gen) = if let Some(coro_kind) = coro_kind { - let is_async = if coro_kind.is_async() { "async " } else { "" }; - let is_gen = if coro_kind.is_gen() { "gen " } else { "" }; - (is_async, is_gen) - } else { - ("", "") + let coro = match coro_kind { + Some(ast::CoroutineKind::Async { .. }) => "async ", + Some(ast::CoroutineKind::Gen { .. }) => "gen ", + None => "", }; let mover = if matches!(capture, ast::CaptureBy::Value { .. }) { "move " @@ -278,14 +276,7 @@ fn rewrite_closure_fn_decl( // 4 = "|| {".len(), which is overconservative when the closure consists of // a single expression. let nested_shape = shape - .shrink_left( - binder.len() - + const_.len() - + immovable.len() - + is_async.len() - + is_gen.len() - + mover.len(), - )? + .shrink_left(binder.len() + const_.len() + immovable.len() + coro.len() + mover.len())? .sub_width(4)?; // 1 = | @@ -323,7 +314,7 @@ fn rewrite_closure_fn_decl( .tactic(tactic) .preserve_newline(true); let list_str = write_list(&item_vec, &fmt)?; - let mut prefix = format!("{binder}{const_}{immovable}{is_async}{is_gen}{mover}|{list_str}|"); + let mut prefix = format!("{binder}{const_}{immovable}{coro}{mover}|{list_str}|"); if !ret_str.is_empty() { if prefix.contains('\n') { diff --git a/tests/ui/coroutine/gen_fn_lifetime_capture.rs b/tests/ui/coroutine/gen_fn_lifetime_capture.rs new file mode 100644 index 00000000000..b6a4d71e6cc --- /dev/null +++ b/tests/ui/coroutine/gen_fn_lifetime_capture.rs @@ -0,0 +1,19 @@ +// edition: 2024 +// compile-flags: -Zunstable-options +// check-pass +#![feature(gen_blocks)] + +// make sure gen fn captures lifetimes in its signature + +gen fn foo<'a, 'b>(x: &'a i32, y: &'b i32, z: &'b i32) -> &'b i32 { + yield y; + yield z; +} + +fn main() { + let z = 3; + let mut iter = foo(&1, &2, &z); + assert_eq!(iter.next(), Some(&2)); + assert_eq!(iter.next(), Some(&3)); + assert_eq!(iter.next(), None); +} From 09f0741449ffbb09b0dd758639157ab208160806 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Mon, 4 Dec 2023 14:38:10 -0800 Subject: [PATCH 10/12] Remove bad merge --- src/tools/clippy/clippy_lints/src/doc/mod.rs | 199 ------------------- 1 file changed, 199 deletions(-) diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs index 0c623dba369..ba452775015 100644 --- a/src/tools/clippy/clippy_lints/src/doc/mod.rs +++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs @@ -655,205 +655,6 @@ fn check_doc<'a, Events: Iterator, Range, trimmed_text: &str, range: Range, fragments: Fragments<'_>) { - if trimmed_text.starts_with('\'') - && trimmed_text.ends_with('\'') - && let Some(span) = fragments.span(cx, range) - { - span_lint( - cx, - DOC_LINK_WITH_QUOTES, - span, - "possible intra-doc link using quotes instead of backticks", - ); - } -} - -fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, range: Range, fragments: Fragments<'_>) { - fn has_needless_main(code: String, edition: Edition) -> bool { - rustc_driver::catch_fatal_errors(|| { - rustc_span::create_session_globals_then(edition, || { - let filename = FileName::anon_source_code(&code); - - let fallback_bundle = - rustc_errors::fallback_fluent_bundle(rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false); - let emitter = EmitterWriter::new(Box::new(io::sink()), fallback_bundle); - let handler = Handler::with_emitter(Box::new(emitter)).disable_warnings(); - #[expect(clippy::arc_with_non_send_sync)] // `Lrc` is expected by with_span_handler - let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let sess = ParseSess::with_span_handler(handler, sm); - - let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code) { - Ok(p) => p, - Err(errs) => { - drop(errs); - return false; - }, - }; - - let mut relevant_main_found = false; - loop { - match parser.parse_item(ForceCollect::No) { - Ok(Some(item)) => match &item.kind { - ItemKind::Fn(box Fn { - sig, body: Some(block), .. - }) if item.ident.name == sym::main => { - let is_async = sig.header.coro_kind.map_or(false, |coro| coro.is_async()); - let returns_nothing = match &sig.decl.output { - FnRetTy::Default(..) => true, - FnRetTy::Ty(ty) if ty.kind.is_unit() => true, - FnRetTy::Ty(_) => false, - }; - - if returns_nothing && !is_async && !block.stmts.is_empty() { - // This main function should be linted, but only if there are no other functions - relevant_main_found = true; - } else { - // This main function should not be linted, we're done - return false; - } - }, - // Tests with one of these items are ignored - ItemKind::Static(..) - | ItemKind::Const(..) - | ItemKind::ExternCrate(..) - | ItemKind::ForeignMod(..) - // Another function was found; this case is ignored - | ItemKind::Fn(..) => return false, - _ => {}, - }, - Ok(None) => break, - Err(e) => { - e.cancel(); - return false; - }, - } - } - - relevant_main_found - }) - }) - .ok() - .unwrap_or_default() - } - - let trailing_whitespace = text.len() - text.trim_end().len(); - - // Because of the global session, we need to create a new session in a different thread with - // the edition we need. - let text = text.to_owned(); - if thread::spawn(move || has_needless_main(text, edition)) - .join() - .expect("thread::spawn failed") - && let Some(span) = fragments.span(cx, range.start..range.end - trailing_whitespace) - { - span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest"); - } -} - -fn check_text(cx: &LateContext<'_>, valid_idents: &FxHashSet, text: &str, span: Span) { - for word in text.split(|c: char| c.is_whitespace() || c == '\'') { - // Trim punctuation as in `some comment (see foo::bar).` - // ^^ - // Or even as in `_foo bar_` which is emphasized. Also preserve `::` as a prefix/suffix. - let mut word = word.trim_matches(|c: char| !c.is_alphanumeric() && c != ':'); - - // Remove leading or trailing single `:` which may be part of a sentence. - if word.starts_with(':') && !word.starts_with("::") { - word = word.trim_start_matches(':'); - } - if word.ends_with(':') && !word.ends_with("::") { - word = word.trim_end_matches(':'); - } - - if valid_idents.contains(word) || word.chars().all(|c| c == ':') { - continue; - } - - // Adjust for the current word - let offset = word.as_ptr() as usize - text.as_ptr() as usize; - let span = Span::new( - span.lo() + BytePos::from_usize(offset), - span.lo() + BytePos::from_usize(offset + word.len()), - span.ctxt(), - span.parent(), - ); - - check_word(cx, word, span); - } -} - -fn check_word(cx: &LateContext<'_>, word: &str, span: Span) { - /// Checks if a string is upper-camel-case, i.e., starts with an uppercase and - /// contains at least two uppercase letters (`Clippy` is ok) and one lower-case - /// letter (`NASA` is ok). - /// Plurals are also excluded (`IDs` is ok). - fn is_camel_case(s: &str) -> bool { - if s.starts_with(|c: char| c.is_ascii_digit() | c.is_ascii_lowercase()) { - return false; - } - - let s = s.strip_suffix('s').unwrap_or(s); - - s.chars().all(char::is_alphanumeric) - && s.chars().filter(|&c| c.is_uppercase()).take(2).count() > 1 - && s.chars().filter(|&c| c.is_lowercase()).take(1).count() > 0 - } - - fn has_underscore(s: &str) -> bool { - s != "_" && !s.contains("\\_") && s.contains('_') - } - - fn has_hyphen(s: &str) -> bool { - s != "-" && s.contains('-') - } - - if let Ok(url) = Url::parse(word) { - // try to get around the fact that `foo::bar` parses as a valid URL - if !url.cannot_be_a_base() { - span_lint( - cx, - DOC_MARKDOWN, - span, - "you should put bare URLs between `<`/`>` or make a proper Markdown link", - ); - - return; - } - } - - // We assume that mixed-case words are not meant to be put inside backticks. (Issue #2343) - if has_underscore(word) && has_hyphen(word) { - return; - } - - if has_underscore(word) || word.contains("::") || is_camel_case(word) { - let mut applicability = Applicability::MachineApplicable; - - span_lint_and_then( - cx, - DOC_MARKDOWN, - span, - "item in documentation is missing backticks", - |diag| { - let snippet = snippet_with_applicability(cx, span, "..", &mut applicability); - diag.span_suggestion_with_style( - span, - "try", - format!("`{snippet}`"), - applicability, - // always show the suggestion in a separate line, since the - // inline presentation adds another pair of backticks - SuggestionStyle::ShowAlways, - ); - }, - ); - } -} - ->>>>>>> d116f1718f1 (Merge Async and Gen into CoroutineKind):src/tools/clippy/clippy_lints/src/doc.rs struct FindPanicUnwrap<'a, 'tcx> { cx: &'a LateContext<'tcx>, panic_span: Option, From ac4d0f240fb98d8b915362af554ad8c5e434d63b Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Mon, 4 Dec 2023 16:37:45 -0800 Subject: [PATCH 11/12] Update doctest --- .../clippy/clippy_lints/src/doc/needless_doctest_main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs b/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs index e50e83834c1..8079129f782 100644 --- a/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs +++ b/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs @@ -3,7 +3,7 @@ use std::{io, thread}; use crate::doc::{NEEDLESS_DOCTEST_MAIN, TEST_ATTR_IN_DOCTEST}; use clippy_utils::diagnostics::span_lint; -use rustc_ast::{Async, Fn, FnRetTy, Item, ItemKind}; +use rustc_ast::{CoroutineKind, Fn, FnRetTy, Item, ItemKind}; use rustc_data_structures::sync::Lrc; use rustc_errors::emitter::EmitterWriter; use rustc_errors::Handler; @@ -69,7 +69,7 @@ pub fn check( if !ignore { get_test_spans(&item, &mut test_attr_spans); } - let is_async = matches!(sig.header.asyncness, Async::Yes { .. }); + let is_async = matches!(sig.header.coro_kind, CoroutineKind::Async { .. }); let returns_nothing = match &sig.decl.output { FnRetTy::Default(..) => true, FnRetTy::Ty(ty) if ty.kind.is_unit() => true, From 2c8dbd959f430d09d36f733aefa33fdbf8058e80 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Mon, 4 Dec 2023 16:46:45 -0800 Subject: [PATCH 12/12] Fix build --- src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs b/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs index 8079129f782..640d4a069ec 100644 --- a/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs +++ b/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs @@ -69,7 +69,7 @@ pub fn check( if !ignore { get_test_spans(&item, &mut test_attr_spans); } - let is_async = matches!(sig.header.coro_kind, CoroutineKind::Async { .. }); + let is_async = matches!(sig.header.coro_kind, Some(CoroutineKind::Async { .. })); let returns_nothing = match &sig.decl.output { FnRetTy::Default(..) => true, FnRetTy::Ty(ty) if ty.kind.is_unit() => true,