From c8d9cd99fa2f389a69c0c42de9945f46977b11d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 13 Jul 2019 21:15:21 -0700 Subject: [PATCH] Detect `fn` with a body in an `extern` block --- src/libsyntax/ext/expand.rs | 2 +- src/libsyntax/parse/diagnostics.rs | 37 ++++++++++++++++++ src/libsyntax/parse/parser.rs | 38 ++++++++++++------- src/test/ui/extern/extern-ffi-fn-with-body.rs | 11 ++++++ .../ui/extern/extern-ffi-fn-with-body.stderr | 18 +++++++++ 5 files changed, 91 insertions(+), 15 deletions(-) create mode 100644 src/test/ui/extern/extern-ffi-fn-with-body.rs create mode 100644 src/test/ui/extern/extern-ffi-fn-with-body.stderr diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 7fc62e357c5c4..7f4b9904cab82 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -742,7 +742,7 @@ impl<'a> Parser<'a> { AstFragmentKind::ForeignItems => { let mut items = SmallVec::new(); while self.token != token::Eof { - items.push(self.parse_foreign_item()?); + items.push(self.parse_foreign_item(DUMMY_SP)?); } AstFragment::ForeignItems(items) } diff --git a/src/libsyntax/parse/diagnostics.rs b/src/libsyntax/parse/diagnostics.rs index ae24047ac8249..de392d6bb8109 100644 --- a/src/libsyntax/parse/diagnostics.rs +++ b/src/libsyntax/parse/diagnostics.rs @@ -15,6 +15,7 @@ use errors::{Applicability, DiagnosticBuilder, DiagnosticId}; use rustc_data_structures::fx::FxHashSet; use syntax_pos::{Span, DUMMY_SP, MultiSpan}; use log::{debug, trace}; +use std::mem; /// Creates a placeholder argument. crate fn dummy_arg(ident: Ident) -> Arg { @@ -783,6 +784,42 @@ impl<'a> Parser<'a> { Err(err) } + crate fn parse_semi_or_incorrect_foreign_fn_body( + &mut self, + ident: &Ident, + extern_sp: Span, + ) -> PResult<'a, ()> { + if self.token != token::Semi { + // this might be an incorrect fn definition (#62109) + let parser_snapshot = self.clone(); + match self.parse_inner_attrs_and_block() { + Ok((_, body)) => { + self.struct_span_err(ident.span, "incorrect `fn` inside `extern` block") + .span_label(ident.span, "can't have a body") + .span_label(body.span, "this body is invalid here") + .span_label( + extern_sp, + "`extern` blocks define existing foreign functions and `fn`s \ + inside of them can't have a body") + .help("you might have meant to write a function accessible through ffi, \ + which can be done by writing `extern fn` outside of the \ + `extern` block") + .note("for more information, visit \ + https://doc.rust-lang.org/std/keyword.extern.html") + .emit(); + } + Err(mut err) => { + err.cancel(); + mem::replace(self, parser_snapshot); + self.expect(&token::Semi)?; + } + } + } else { + self.bump(); + } + Ok(()) + } + /// Consume alternative await syntaxes like `await `, `await? `, `await()` /// and `await { }`. crate fn parse_incorrect_await_syntax( diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 83030e89af310..b785498e165a2 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -4615,7 +4615,7 @@ impl<'a> Parser<'a> { } /// Parses a block. Inner attributes are allowed. - fn parse_inner_attrs_and_block(&mut self) -> PResult<'a, (Vec, P)> { + crate fn parse_inner_attrs_and_block(&mut self) -> PResult<'a, (Vec, P)> { maybe_whole!(self, NtBlock, |x| (Vec::new(), x)); let lo = self.token.span; @@ -6700,15 +6700,20 @@ impl<'a> Parser<'a> { } /// Parses a function declaration from a foreign module. - fn parse_item_foreign_fn(&mut self, vis: ast::Visibility, lo: Span, attrs: Vec) - -> PResult<'a, ForeignItem> { + fn parse_item_foreign_fn( + &mut self, + vis: ast::Visibility, + lo: Span, + attrs: Vec, + extern_sp: Span, + ) -> PResult<'a, ForeignItem> { self.expect_keyword(kw::Fn)?; let (ident, mut generics) = self.parse_fn_header()?; let decl = self.parse_fn_decl(true)?; generics.where_clause = self.parse_where_clause()?; let hi = self.token.span; - self.expect(&token::Semi)?; + self.parse_semi_or_incorrect_foreign_fn_body(&ident, extern_sp)?; Ok(ast::ForeignItem { ident, attrs, @@ -6835,12 +6840,14 @@ impl<'a> Parser<'a> { /// extern "C" {} /// extern {} /// ``` - fn parse_item_foreign_mod(&mut self, - lo: Span, - opt_abi: Option, - visibility: Visibility, - mut attrs: Vec) - -> PResult<'a, P> { + fn parse_item_foreign_mod( + &mut self, + lo: Span, + opt_abi: Option, + visibility: Visibility, + mut attrs: Vec, + extern_sp: Span, + ) -> PResult<'a, P> { self.expect(&token::OpenDelim(token::Brace))?; let abi = opt_abi.unwrap_or(Abi::C); @@ -6849,7 +6856,7 @@ impl<'a> Parser<'a> { let mut foreign_items = vec![]; while !self.eat(&token::CloseDelim(token::Brace)) { - foreign_items.push(self.parse_foreign_item()?); + foreign_items.push(self.parse_foreign_item(extern_sp)?); } let prev_span = self.prev_span; @@ -7096,6 +7103,7 @@ impl<'a> Parser<'a> { } if self.eat_keyword(kw::Extern) { + let extern_sp = self.prev_span; if self.eat_keyword(kw::Crate) { return Ok(Some(self.parse_item_extern_crate(lo, visibility, attrs)?)); } @@ -7119,7 +7127,9 @@ impl<'a> Parser<'a> { maybe_append(attrs, extra_attrs)); return Ok(Some(item)); } else if self.check(&token::OpenDelim(token::Brace)) { - return Ok(Some(self.parse_item_foreign_mod(lo, opt_abi, visibility, attrs)?)); + return Ok(Some( + self.parse_item_foreign_mod(lo, opt_abi, visibility, attrs, extern_sp)?, + )); } self.unexpected()?; @@ -7504,7 +7514,7 @@ impl<'a> Parser<'a> { } /// Parses a foreign item. - crate fn parse_foreign_item(&mut self) -> PResult<'a, ForeignItem> { + crate fn parse_foreign_item(&mut self, extern_sp: Span) -> PResult<'a, ForeignItem> { maybe_whole!(self, NtForeignItem, |ni| ni); let attrs = self.parse_outer_attributes()?; @@ -7529,7 +7539,7 @@ impl<'a> Parser<'a> { } // FOREIGN FUNCTION ITEM if self.check_keyword(kw::Fn) { - return Ok(self.parse_item_foreign_fn(visibility, lo, attrs)?); + return Ok(self.parse_item_foreign_fn(visibility, lo, attrs, extern_sp)?); } // FOREIGN TYPE ITEM if self.check_keyword(kw::Type) { diff --git a/src/test/ui/extern/extern-ffi-fn-with-body.rs b/src/test/ui/extern/extern-ffi-fn-with-body.rs new file mode 100644 index 0000000000000..4cf563514ea60 --- /dev/null +++ b/src/test/ui/extern/extern-ffi-fn-with-body.rs @@ -0,0 +1,11 @@ +extern "C" { + fn foo() -> i32 { //~ ERROR incorrect `fn` inside `extern` block + return 0; + } +} + +extern "C" fn bar() -> i32 { + return 0; +} + +fn main() {} diff --git a/src/test/ui/extern/extern-ffi-fn-with-body.stderr b/src/test/ui/extern/extern-ffi-fn-with-body.stderr new file mode 100644 index 0000000000000..02d1ee5a7534b --- /dev/null +++ b/src/test/ui/extern/extern-ffi-fn-with-body.stderr @@ -0,0 +1,18 @@ +error: incorrect `fn` inside `extern` block + --> $DIR/extern-ffi-fn-with-body.rs:2:8 + | +LL | extern "C" { + | ------ `extern` blocks define existing foreign functions and `fn`s inside of them can't have a body +LL | fn foo() -> i32 { + | ________^^^__________- + | | | + | | can't have a body +LL | | return 0; +LL | | } + | |_____- this body is invalid here + | + = help: you might have meant to write a function accessible through ffi, which can be done by writing `extern fn` outside of the `extern` block + = note: for more information, visit https://doc.rust-lang.org/std/keyword.extern.html + +error: aborting due to previous error +