From 7a3184a04c4023d9ab93fe793c0d0ffb5e91240b Mon Sep 17 00:00:00 2001 From: "Zack M. Davis" Date: Sat, 8 Jun 2019 21:35:02 -0700 Subject: [PATCH] in which we decline to suggest the anonymous lifetime in declarations The elided-lifetimes-in-path lint (part of our suite of Rust 2018 idiom lints which we are hoping to promote to Warn status) was firing with an illegal suggestion to write an anonymous lifetime in a struct/item declaration (where we don't allow it). The linting code was already deciding whether to act on the basis of a `ParamMode` enum, indicating whether the present path-segment was part of an expression, or anywhere else. The present case seemed to be part of the "anywhere else", and yet meriting different rules as far as the lint was concerned, so it seemed expedient to introduce a new enum member. We yank out a `TyKind::Path` arm into its own method so that we can call it with our new `ParamMode` specifically when lowering struct fields. (The alternative strategy of changing the signature of `lower_ty` to take a `ParamMode` would be inelegant given that most of the `TyKind` match arm bodies therein don't concern themselves with `ParamMode`.) Resolves #61124. --- src/librustc/hir/lowering.rs | 41 +++++++++++++++---- ...124-anon-lifetime-in-struct-declaration.rs | 10 +++++ ...anon-lifetime-in-struct-declaration.stderr | 9 ++++ 3 files changed, 52 insertions(+), 8 deletions(-) create mode 100644 src/test/ui/in-band-lifetimes/issue-61124-anon-lifetime-in-struct-declaration.rs create mode 100644 src/test/ui/in-band-lifetimes/issue-61124-anon-lifetime-in-struct-declaration.stderr diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index b5e9f6bd3a610..373f255e6366d 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -274,6 +274,8 @@ pub fn lower_crate( enum ParamMode { /// Any path in a type context. Explicit, + /// Path in a type definition, where the anonymous lifetime `'_` is not allowed. + ExplicitNamed, /// The `module::Type` in `module::Type::method` in an expression. Optional, } @@ -1489,6 +1491,23 @@ impl<'a> LoweringContext<'a> { P(self.lower_ty_direct(t, itctx)) } + fn lower_path_ty( + &mut self, + t: &Ty, + qself: &Option, + path: &Path, + param_mode: ParamMode, + itctx: ImplTraitContext<'_> + ) -> hir::Ty { + let id = self.lower_node_id(t.id); + let qpath = self.lower_qpath(t.id, qself, path, param_mode, itctx); + let ty = self.ty_path(id, t.span, qpath); + if let hir::TyKind::TraitObject(..) = ty.node { + self.maybe_lint_bare_trait(t.span, t.id, qself.is_none() && path.is_global()); + } + ty + } + fn lower_ty_direct(&mut self, t: &Ty, mut itctx: ImplTraitContext<'_>) -> hir::Ty { let kind = match t.node { TyKind::Infer => hir::TyKind::Infer, @@ -1534,13 +1553,7 @@ impl<'a> LoweringContext<'a> { return self.lower_ty_direct(ty, itctx); } TyKind::Path(ref qself, ref path) => { - let id = self.lower_node_id(t.id); - let qpath = self.lower_qpath(t.id, qself, path, ParamMode::Explicit, itctx); - let ty = self.ty_path(id, t.span, qpath); - if let hir::TyKind::TraitObject(..) = ty.node { - self.maybe_lint_bare_trait(t.span, t.id, qself.is_none() && path.is_global()); - } - return ty; + return self.lower_path_ty(t, qself, path, ParamMode::Explicit, itctx); } TyKind::ImplicitSelf => { let res = self.expect_full_res(t.id); @@ -3086,6 +3099,18 @@ impl<'a> LoweringContext<'a> { } fn lower_struct_field(&mut self, (index, f): (usize, &StructField)) -> hir::StructField { + let ty = if let TyKind::Path(ref qself, ref path) = f.ty.node { + let t = self.lower_path_ty( + &f.ty, + qself, + path, + ParamMode::ExplicitNamed, // no `'_` in declarations (Issue #61124) + ImplTraitContext::disallowed() + ); + P(t) + } else { + self.lower_ty(&f.ty, ImplTraitContext::disallowed()) + }; hir::StructField { span: f.span, hir_id: self.lower_node_id(f.id), @@ -3095,7 +3120,7 @@ impl<'a> LoweringContext<'a> { None => Ident::new(sym::integer(index), f.span), }, vis: self.lower_visibility(&f.vis, None), - ty: self.lower_ty(&f.ty, ImplTraitContext::disallowed()), + ty, attrs: self.lower_attrs(&f.attrs), } } diff --git a/src/test/ui/in-band-lifetimes/issue-61124-anon-lifetime-in-struct-declaration.rs b/src/test/ui/in-band-lifetimes/issue-61124-anon-lifetime-in-struct-declaration.rs new file mode 100644 index 0000000000000..cf08cb7eeacd8 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/issue-61124-anon-lifetime-in-struct-declaration.rs @@ -0,0 +1,10 @@ +#![deny(elided_lifetimes_in_paths)] + +// Previously, the elided-lifetimes-in-path lint would fire, but we don't want +// that, because `'_` isn't legal in struct declarations. + +struct Betrayal<'a> { x: &'a u8 } + +struct Heartbreak(Betrayal); //~ ERROR missing lifetime specifier + +fn main() {} diff --git a/src/test/ui/in-band-lifetimes/issue-61124-anon-lifetime-in-struct-declaration.stderr b/src/test/ui/in-band-lifetimes/issue-61124-anon-lifetime-in-struct-declaration.stderr new file mode 100644 index 0000000000000..9579abb76b32f --- /dev/null +++ b/src/test/ui/in-band-lifetimes/issue-61124-anon-lifetime-in-struct-declaration.stderr @@ -0,0 +1,9 @@ +error[E0106]: missing lifetime specifier + --> $DIR/issue-61124-anon-lifetime-in-struct-declaration.rs:8:19 + | +LL | struct Heartbreak(Betrayal); + | ^^^^^^^^ expected lifetime parameter + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0106`.