about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMazdak Farrokhzad <twingoow@gmail.com>2019-12-01 07:24:07 +0100
committerMazdak Farrokhzad <twingoow@gmail.com>2019-12-12 17:54:48 +0100
commitf6403c6c766c704569564b9021071c4917d45a25 (patch)
tree31e861f837051b1663e56cd202b823b614a5137f
parent73557faed23678fc443f3fa52727b5f200f597d2 (diff)
downloadrust-f6403c6c766c704569564b9021071c4917d45a25.tar.gz
rust-f6403c6c766c704569564b9021071c4917d45a25.zip
Use `Option` in `ImplItemKind::Method`.
-rw-r--r--src/librustc/hir/lowering.rs4
-rw-r--r--src/librustc/hir/lowering/item.rs71
-rw-r--r--src/librustc_parse/parser/item.rs12
-rw-r--r--src/librustc_passes/ast_validation.rs31
-rw-r--r--src/librustc_resolve/def_collector.rs10
-rw-r--r--src/librustc_save_analysis/dump_visitor.rs2
-rw-r--r--src/libsyntax/ast.rs2
-rw-r--r--src/libsyntax/mut_visit.rs2
-rw-r--r--src/libsyntax/print/pprust.rs26
-rw-r--r--src/libsyntax/visit.rs12
-rw-r--r--src/libsyntax_ext/deriving/generic/mod.rs2
-rw-r--r--src/test/ui/issues/issue-58856-1.rs2
-rw-r--r--src/test/ui/issues/issue-58856-1.stderr17
-rw-r--r--src/test/ui/parser/impl-item-const-semantic-fail.stderr4
-rw-r--r--src/test/ui/parser/impl-item-fn-no-body-pass.rs8
-rw-r--r--src/test/ui/parser/impl-item-fn-no-body-semantic-fail.rs7
-rw-r--r--src/test/ui/parser/impl-item-fn-no-body-semantic-fail.stderr10
17 files changed, 149 insertions, 73 deletions
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index e13f6cabb52..a82febba38a 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -1211,7 +1211,7 @@ impl<'a> LoweringContext<'a> {
                             let ct = self.with_new_scopes(|this| {
                                 hir::AnonConst {
                                     hir_id: this.lower_node_id(node_id),
-                                    body: this.lower_const_body(&path_expr),
+                                    body: this.lower_const_body(path_expr.span, Some(&path_expr)),
                                 }
                             });
                             return GenericArg::Const(ConstArg {
@@ -3003,7 +3003,7 @@ impl<'a> LoweringContext<'a> {
         self.with_new_scopes(|this| {
             hir::AnonConst {
                 hir_id: this.lower_node_id(c.id),
-                body: this.lower_const_body(&c.value),
+                body: this.lower_const_body(c.value.span, Some(&c.value)),
             }
         })
     }
diff --git a/src/librustc/hir/lowering/item.rs b/src/librustc/hir/lowering/item.rs
index 32b36d2021b..7e231cd6b59 100644
--- a/src/librustc/hir/lowering/item.rs
+++ b/src/librustc/hir/lowering/item.rs
@@ -250,7 +250,7 @@ impl LoweringContext<'_> {
             return None;
         }
 
-        let kind = self.lower_item_kind(i.id, &mut ident, &attrs, &mut vis, &i.kind);
+        let kind = self.lower_item_kind(i.span, i.id, &mut ident, &attrs, &mut vis, &i.kind);
 
         Some(hir::Item {
             hir_id: self.lower_node_id(i.id),
@@ -264,6 +264,7 @@ impl LoweringContext<'_> {
 
     fn lower_item_kind(
         &mut self,
+        span: Span,
         id: NodeId,
         ident: &mut Ident,
         attrs: &hir::HirVec<Attribute>,
@@ -292,7 +293,7 @@ impl LoweringContext<'_> {
                         }
                     ),
                     m,
-                    self.lower_const_body(e),
+                    self.lower_const_body(span, Some(e)),
                 )
             }
             ItemKind::Const(ref t, ref e) => {
@@ -305,7 +306,7 @@ impl LoweringContext<'_> {
                             ImplTraitContext::Disallowed(ImplTraitPosition::Binding)
                         }
                     ),
-                    self.lower_const_body(e)
+                    self.lower_const_body(span, Some(e))
                 )
             }
             ItemKind::Fn(FnSig { ref decl, header }, ref generics, ref body) => {
@@ -317,7 +318,12 @@ impl LoweringContext<'_> {
                     // `impl Future<Output = T>` here because lower_body
                     // only cares about the input argument patterns in the function
                     // declaration (decl), not the return types.
-                    let body_id = this.lower_maybe_async_body(&decl, header.asyncness.node, body);
+                    let body_id = this.lower_maybe_async_body(
+                        span,
+                        &decl,
+                        header.asyncness.node,
+                        Some(body),
+                    );
 
                     let (generics, decl) = this.add_in_band_defs(
                         generics,
@@ -817,7 +823,7 @@ impl LoweringContext<'_> {
                     self.lower_ty(ty, ImplTraitContext::disallowed()),
                     default
                         .as_ref()
-                        .map(|x| self.lower_const_body(x)),
+                        .map(|x| self.lower_const_body(i.span, Some(x))),
                 ),
             ),
             TraitItemKind::Method(ref sig, None) => {
@@ -832,7 +838,7 @@ impl LoweringContext<'_> {
                 (generics, hir::TraitItemKind::Method(sig, hir::TraitMethod::Required(names)))
             }
             TraitItemKind::Method(ref sig, Some(ref body)) => {
-                let body_id = self.lower_fn_body_block(&sig.decl, body);
+                let body_id = self.lower_fn_body_block(i.span, &sig.decl, Some(body));
                 let (generics, sig) = self.lower_method_sig(
                     &i.generics,
                     sig,
@@ -891,6 +897,11 @@ impl LoweringContext<'_> {
         }
     }
 
+    /// Construct `ExprKind::Err` for the given `span`.
+    fn expr_err(&mut self, span: Span) -> hir::Expr {
+        self.expr(span, hir::ExprKind::Err, ThinVec::new())
+    }
+
     fn lower_impl_item(&mut self, i: &ImplItem) -> hir::ImplItem {
         let impl_item_def_id = self.resolver.definitions().local_def_id(i.id);
 
@@ -899,19 +910,16 @@ impl LoweringContext<'_> {
                 self.lower_generics(&i.generics, ImplTraitContext::disallowed()),
                 hir::ImplItemKind::Const(
                     self.lower_ty(ty, ImplTraitContext::disallowed()),
-                    match expr {
-                        Some(expr) => self.lower_const_body(expr),
-                        None => self.lower_body(|this| (
-                            hir_vec![],
-                            this.expr(i.span, hir::ExprKind::Err, ThinVec::new()),
-                        )),
-                    }
+                    self.lower_const_body(i.span, expr.as_deref()),
                 ),
             ),
             ImplItemKind::Method(ref sig, ref body) => {
                 self.current_item = Some(i.span);
                 let body_id = self.lower_maybe_async_body(
-                    &sig.decl, sig.header.asyncness.node, body
+                    i.span,
+                    &sig.decl,
+                    sig.header.asyncness.node,
+                    body.as_deref(),
                 );
                 let impl_trait_return_allow = !self.is_in_trait_impl;
                 let (generics, sig) = self.lower_method_sig(
@@ -1069,23 +1077,39 @@ impl LoweringContext<'_> {
         ))
     }
 
-    fn lower_fn_body_block(&mut self, decl: &FnDecl, body: &Block) -> hir::BodyId {
-        self.lower_fn_body(decl, |this| this.lower_block_expr(body))
+    fn lower_fn_body_block(
+        &mut self,
+        span: Span,
+        decl: &FnDecl,
+        body: Option<&Block>,
+    ) -> hir::BodyId {
+        self.lower_fn_body(decl, |this| this.lower_block_expr_opt(span, body))
+    }
+
+    fn lower_block_expr_opt(&mut self, span: Span, block: Option<&Block>) -> hir::Expr {
+        match block {
+            Some(block) => self.lower_block_expr(block),
+            None => self.expr_err(span),
+        }
     }
 
-    pub(super) fn lower_const_body(&mut self, expr: &Expr) -> hir::BodyId {
-        self.lower_body(|this| (hir_vec![], this.lower_expr(expr)))
+    pub(super) fn lower_const_body(&mut self, span: Span, expr: Option<&Expr>) -> hir::BodyId {
+        self.lower_body(|this| (hir_vec![], match expr {
+            Some(expr) => this.lower_expr(expr),
+            None => this.expr_err(span),
+        }))
     }
 
     fn lower_maybe_async_body(
         &mut self,
+        span: Span,
         decl: &FnDecl,
         asyncness: IsAsync,
-        body: &Block,
+        body: Option<&Block>,
     ) -> hir::BodyId {
         let closure_id = match asyncness {
             IsAsync::Async { closure_id, .. } => closure_id,
-            IsAsync::NotAsync => return self.lower_fn_body_block(decl, body),
+            IsAsync::NotAsync => return self.lower_fn_body_block(span, decl, body),
         };
 
         self.lower_body(|this| {
@@ -1219,15 +1243,16 @@ impl LoweringContext<'_> {
                 parameters.push(new_parameter);
             }
 
+            let body_span = body.map_or(span, |b| b.span);
             let async_expr = this.make_async_expr(
                 CaptureBy::Value,
                 closure_id,
                 None,
-                body.span,
+                body_span,
                 hir::AsyncGeneratorKind::Fn,
                 |this| {
                     // Create a block from the user's function body:
-                    let user_body = this.lower_block_expr(body);
+                    let user_body = this.lower_block_expr_opt(body_span, body);
 
                     // Transform into `drop-temps { <user-body> }`, an expression:
                     let desugared_span = this.mark_span_with_reason(
@@ -1257,7 +1282,7 @@ impl LoweringContext<'_> {
                     );
                     this.expr_block(P(body), ThinVec::new())
                 });
-            (HirVec::from(parameters), this.expr(body.span, async_expr, ThinVec::new()))
+            (HirVec::from(parameters), this.expr(body_span, async_expr, ThinVec::new()))
         })
     }
 
diff --git a/src/librustc_parse/parser/item.rs b/src/librustc_parse/parser/item.rs
index 68fdfd24d61..053502c43dc 100644
--- a/src/librustc_parse/parser/item.rs
+++ b/src/librustc_parse/parser/item.rs
@@ -705,9 +705,7 @@ impl<'a> Parser<'a> {
             // FIXME: code copied from `parse_macro_use_or_failure` -- use abstraction!
             (Ident::invalid(), ast::ImplItemKind::Macro(mac), Generics::default())
         } else {
-            let (name, inner_attrs, generics, kind) = self.parse_impl_method(at_end)?;
-            attrs.extend(inner_attrs);
-            (name, kind, generics)
+            self.parse_impl_method(at_end, &mut attrs)?
         };
 
         Ok(ImplItem {
@@ -1842,11 +1840,11 @@ impl<'a> Parser<'a> {
     fn parse_impl_method(
         &mut self,
         at_end: &mut bool,
-    ) -> PResult<'a, (Ident, Vec<Attribute>, Generics, ImplItemKind)> {
+        attrs: &mut Vec<Attribute>,
+    ) -> PResult<'a, (Ident, ImplItemKind, Generics)> {
         let (ident, sig, generics) = self.parse_method_sig(|_| true)?;
-        *at_end = true;
-        let (inner_attrs, body) = self.parse_inner_attrs_and_block()?;
-        Ok((ident, inner_attrs, generics, ast::ImplItemKind::Method(sig, body)))
+        let body = self.parse_trait_method_body(at_end, attrs)?;
+        Ok((ident, ast::ImplItemKind::Method(sig, body), generics))
     }
 
     fn parse_trait_item_method(
diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs
index ba0e6b100ee..78866dc9cc9 100644
--- a/src/librustc_passes/ast_validation.rs
+++ b/src/librustc_passes/ast_validation.rs
@@ -279,6 +279,22 @@ impl<'a> AstValidator<'a> {
                 .emit();
         }
     }
+
+    fn check_impl_item_provided<T>(&self, sp: Span, body: &Option<T>, ctx: &str, sugg: &str) {
+        if body.is_some() {
+            return;
+        }
+
+        self.err_handler()
+            .struct_span_err(sp, &format!("associated {} in `impl` without body", ctx))
+            .span_suggestion(
+                self.session.source_map().end_point(sp),
+                &format!("provide a definition for the {}", ctx),
+                sugg.to_string(),
+                Applicability::HasPlaceholders,
+            )
+            .emit();
+    }
 }
 
 enum GenericPosition {
@@ -747,18 +763,11 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
 
     fn visit_impl_item(&mut self, ii: &'a ImplItem) {
         match &ii.kind {
-            ImplItemKind::Const(ty, None) => {
-                self.err_handler()
-                    .struct_span_err(ii.span, "associated constant in `impl` without body")
-                    .span_suggestion(
-                        ii.span,
-                        "provide a definition for the constant",
-                        format!("const {}: {} = <expr>;", ii.ident, pprust::ty_to_string(ty)),
-                        Applicability::HasPlaceholders,
-                    )
-                    .emit();
+            ImplItemKind::Const(_, body) => {
+                self.check_impl_item_provided(ii.span, body, "constant", " = <expr>;");
             }
-            ImplItemKind::Method(sig, _) => {
+            ImplItemKind::Method(sig, body) => {
+                self.check_impl_item_provided(ii.span, body, "function", " { <body> }");
                 self.check_fn_decl(&sig.decl);
             }
             _ => {}
diff --git a/src/librustc_resolve/def_collector.rs b/src/librustc_resolve/def_collector.rs
index dd6b1d2119e..76a52bb7f7f 100644
--- a/src/librustc_resolve/def_collector.rs
+++ b/src/librustc_resolve/def_collector.rs
@@ -50,7 +50,7 @@ impl<'a> DefCollector<'a> {
         header: &FnHeader,
         generics: &'a Generics,
         decl: &'a FnDecl,
-        body: &'a Block,
+        body: Option<&'a Block>,
     ) {
         let (closure_id, return_impl_trait_id) = match header.asyncness.node {
             IsAsync::Async {
@@ -74,7 +74,9 @@ impl<'a> DefCollector<'a> {
                 closure_id, DefPathData::ClosureExpr, span,
             );
             this.with_parent(closure_def, |this| {
-                visit::walk_block(this, body);
+                if let Some(body) = body {
+                    visit::walk_block(this, body);
+                }
             })
         })
     }
@@ -123,7 +125,7 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
                     &sig.header,
                     generics,
                     &sig.decl,
-                    body,
+                    Some(body),
                 )
             }
             ItemKind::Static(..) | ItemKind::Const(..) | ItemKind::Fn(..) =>
@@ -237,7 +239,7 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
                     header,
                     &ii.generics,
                     decl,
-                    body,
+                    body.as_deref(),
                 )
             }
             ImplItemKind::Method(..) |
diff --git a/src/librustc_save_analysis/dump_visitor.rs b/src/librustc_save_analysis/dump_visitor.rs
index 97cbcb6401c..99f9c3b1f2e 100644
--- a/src/librustc_save_analysis/dump_visitor.rs
+++ b/src/librustc_save_analysis/dump_visitor.rs
@@ -1119,7 +1119,7 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> {
             ast::ImplItemKind::Method(ref sig, ref body) => {
                 self.process_method(
                     sig,
-                    Some(body),
+                    body.as_deref(),
                     impl_item.id,
                     impl_item.ident,
                     &impl_item.generics,
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 17a57387da7..f6af5d8637e 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -1637,7 +1637,7 @@ pub struct ImplItem<K = ImplItemKind> {
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
 pub enum ImplItemKind  {
     Const(P<Ty>, Option<P<Expr>>),
-    Method(FnSig, P<Block>),
+    Method(FnSig, Option<P<Block>>),
     TyAlias(P<Ty>),
     Macro(Mac),
 }
diff --git a/src/libsyntax/mut_visit.rs b/src/libsyntax/mut_visit.rs
index 14701455013..0fa4dcf3ad9 100644
--- a/src/libsyntax/mut_visit.rs
+++ b/src/libsyntax/mut_visit.rs
@@ -985,7 +985,7 @@ pub fn noop_flat_map_impl_item<T: MutVisitor>(mut item: ImplItem, visitor: &mut
         }
         ImplItemKind::Method(sig, body) => {
             visit_fn_sig(sig, visitor);
-            visitor.visit_block(body);
+            visit_opt(body, |body| visitor.visit_block(body));
         }
         ImplItemKind::TyAlias(ty) => visitor.visit_ty(ty),
         ImplItemKind::Macro(mac) => visitor.visit_mac(mac),
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index c1405e15819..34097841b4a 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -1553,23 +1553,13 @@ impl<'a> State<'a> {
         self.print_defaultness(ti.defaultness);
         match ti.kind {
             ast::TraitItemKind::Const(ref ty, ref default) => {
-                self.print_associated_const(
-                    ti.ident,
-                    ty,
-                    default.as_ref().map(|expr| &**expr),
-                    &source_map::respan(ti.span.shrink_to_lo(), ast::VisibilityKind::Inherited),
-                );
+                self.print_associated_const(ti.ident, ty, default.as_deref(), &ti.vis);
             }
             ast::TraitItemKind::Method(ref sig, ref body) => {
                 if body.is_some() {
                     self.head("");
                 }
-                self.print_method_sig(
-                    ti.ident,
-                    &ti.generics,
-                    sig,
-                    &source_map::respan(ti.span.shrink_to_lo(), ast::VisibilityKind::Inherited),
-                );
+                self.print_method_sig(ti.ident, &ti.generics, sig, &ti.vis);
                 if let Some(ref body) = *body {
                     self.nbsp();
                     self.print_block_with_attrs(body, &ti.attrs);
@@ -1602,10 +1592,16 @@ impl<'a> State<'a> {
                 self.print_associated_const(ii.ident, ty, expr.as_deref(), &ii.vis);
             }
             ast::ImplItemKind::Method(ref sig, ref body) => {
-                self.head("");
+                if body.is_some() {
+                    self.head("");
+                }
                 self.print_method_sig(ii.ident, &ii.generics, sig, &ii.vis);
-                self.nbsp();
-                self.print_block_with_attrs(body, &ii.attrs);
+                if let Some(body) = body {
+                    self.nbsp();
+                    self.print_block_with_attrs(body, &ii.attrs);
+                } else {
+                    self.s.word(";");
+                }
             }
             ast::ImplItemKind::TyAlias(ref ty) => {
                 self.print_associated_type(ii.ident, None, Some(ty));
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index 0b7a7d993aa..bdf70ec46f7 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -25,7 +25,7 @@ pub enum FnKind<'a> {
     ItemFn(Ident, &'a FnHeader, &'a Visibility, &'a Block),
 
     /// E.g., `fn foo(&self)`.
-    Method(Ident, &'a FnSig, Option<&'a Visibility>, &'a Block),
+    Method(Ident, &'a FnSig, &'a Visibility, &'a Block),
 
     /// E.g., `|x, y| body`.
     Closure(&'a Expr),
@@ -596,7 +596,7 @@ pub fn walk_trait_item<'a, V: Visitor<'a>>(visitor: &mut V, trait_item: &'a Trai
             walk_fn_decl(visitor, &sig.decl);
         }
         TraitItemKind::Method(ref sig, Some(ref body)) => {
-            visitor.visit_fn(FnKind::Method(trait_item.ident, sig, None, body),
+            visitor.visit_fn(FnKind::Method(trait_item.ident, sig, &trait_item.vis, body),
                              &sig.decl, trait_item.span, trait_item.id);
         }
         TraitItemKind::Type(ref bounds, ref default) => {
@@ -619,8 +619,12 @@ pub fn walk_impl_item<'a, V: Visitor<'a>>(visitor: &mut V, impl_item: &'a ImplIt
             visitor.visit_ty(ty);
             walk_list!(visitor, visit_expr, expr);
         }
-        ImplItemKind::Method(ref sig, ref body) => {
-            visitor.visit_fn(FnKind::Method(impl_item.ident, sig, Some(&impl_item.vis), body),
+        ImplItemKind::Method(ref sig, None) => {
+            visitor.visit_fn_header(&sig.header);
+            walk_fn_decl(visitor, &sig.decl);
+        }
+        ImplItemKind::Method(ref sig, Some(ref body)) => {
+            visitor.visit_fn(FnKind::Method(impl_item.ident, sig, &impl_item.vis, body),
                              &sig.decl, impl_item.span, impl_item.id);
         }
         ImplItemKind::TyAlias(ref ty) => {
diff --git a/src/libsyntax_ext/deriving/generic/mod.rs b/src/libsyntax_ext/deriving/generic/mod.rs
index 5bd84b43a78..e8c4f993d4f 100644
--- a/src/libsyntax_ext/deriving/generic/mod.rs
+++ b/src/libsyntax_ext/deriving/generic/mod.rs
@@ -956,7 +956,7 @@ impl<'a> MethodDef<'a> {
             vis: respan(trait_lo_sp, ast::VisibilityKind::Inherited),
             defaultness: ast::Defaultness::Final,
             ident: method_ident,
-            kind: ast::ImplItemKind::Method(sig, body_block),
+            kind: ast::ImplItemKind::Method(sig, Some(body_block)),
             tokens: None,
         }
     }
diff --git a/src/test/ui/issues/issue-58856-1.rs b/src/test/ui/issues/issue-58856-1.rs
index db3984cd189..8b1a39a94e6 100644
--- a/src/test/ui/issues/issue-58856-1.rs
+++ b/src/test/ui/issues/issue-58856-1.rs
@@ -1,6 +1,8 @@
 impl A {
+    //~^ ERROR cannot find type `A` in this scope
     fn b(self>
     //~^ ERROR expected one of `)`, `,`, or `:`, found `>`
+    //~| ERROR expected `;` or `{`, found `>`
 }
 
 fn main() {}
diff --git a/src/test/ui/issues/issue-58856-1.stderr b/src/test/ui/issues/issue-58856-1.stderr
index 58ab0a142d6..0ea6b017548 100644
--- a/src/test/ui/issues/issue-58856-1.stderr
+++ b/src/test/ui/issues/issue-58856-1.stderr
@@ -1,10 +1,23 @@
 error: expected one of `)`, `,`, or `:`, found `>`
-  --> $DIR/issue-58856-1.rs:2:14
+  --> $DIR/issue-58856-1.rs:3:14
    |
 LL |     fn b(self>
    |         -    ^ help: `)` may belong here
    |         |
    |         unclosed delimiter
 
-error: aborting due to previous error
+error: expected `;` or `{`, found `>`
+  --> $DIR/issue-58856-1.rs:3:14
+   |
+LL |     fn b(self>
+   |              ^ expected `;` or `{`
+
+error[E0412]: cannot find type `A` in this scope
+  --> $DIR/issue-58856-1.rs:1:6
+   |
+LL | impl A {
+   |      ^ not found in this scope
+
+error: aborting due to 3 previous errors
 
+For more information about this error, try `rustc --explain E0412`.
diff --git a/src/test/ui/parser/impl-item-const-semantic-fail.stderr b/src/test/ui/parser/impl-item-const-semantic-fail.stderr
index 31a15f8e80e..ec3bee0ce68 100644
--- a/src/test/ui/parser/impl-item-const-semantic-fail.stderr
+++ b/src/test/ui/parser/impl-item-const-semantic-fail.stderr
@@ -2,7 +2,9 @@ error: associated constant in `impl` without body
   --> $DIR/impl-item-const-semantic-fail.rs:6:5
    |
 LL |     const Y: u8;
-   |     ^^^^^^^^^^^^ help: provide a definition for the constant: `const Y: u8 = <expr>;`
+   |     ^^^^^^^^^^^-
+   |                |
+   |                help: provide a definition for the constant: `= <expr>;`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/impl-item-fn-no-body-pass.rs b/src/test/ui/parser/impl-item-fn-no-body-pass.rs
new file mode 100644
index 00000000000..16b09d64e8c
--- /dev/null
+++ b/src/test/ui/parser/impl-item-fn-no-body-pass.rs
@@ -0,0 +1,8 @@
+// check-pass
+
+fn main() {}
+
+#[cfg(FALSE)]
+impl X {
+    fn f();
+}
diff --git a/src/test/ui/parser/impl-item-fn-no-body-semantic-fail.rs b/src/test/ui/parser/impl-item-fn-no-body-semantic-fail.rs
new file mode 100644
index 00000000000..cb183db5964
--- /dev/null
+++ b/src/test/ui/parser/impl-item-fn-no-body-semantic-fail.rs
@@ -0,0 +1,7 @@
+fn main() {}
+
+struct X;
+
+impl X {
+    fn f(); //~ ERROR associated function in `impl` without body
+}
diff --git a/src/test/ui/parser/impl-item-fn-no-body-semantic-fail.stderr b/src/test/ui/parser/impl-item-fn-no-body-semantic-fail.stderr
new file mode 100644
index 00000000000..1acb727368b
--- /dev/null
+++ b/src/test/ui/parser/impl-item-fn-no-body-semantic-fail.stderr
@@ -0,0 +1,10 @@
+error: associated function in `impl` without body
+  --> $DIR/impl-item-fn-no-body-semantic-fail.rs:6:5
+   |
+LL |     fn f();
+   |     ^^^^^^-
+   |           |
+   |           help: provide a definition for the function: `{ <body> }`
+
+error: aborting due to previous error
+