Skip to content

Commit

Permalink
Detect fn with a body in an extern block
Browse files Browse the repository at this point in the history
  • Loading branch information
estebank committed Jul 14, 2019
1 parent 69656fa commit c8d9cd9
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 15 deletions.
2 changes: 1 addition & 1 deletion src/libsyntax/ext/expand.rs
Expand Up @@ -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)
}
Expand Down
37 changes: 37 additions & 0 deletions src/libsyntax/parse/diagnostics.rs
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 <expr>`, `await? <expr>`, `await(<expr>)`
/// and `await { <expr> }`.
crate fn parse_incorrect_await_syntax(
Expand Down
38 changes: 24 additions & 14 deletions src/libsyntax/parse/parser.rs
Expand Up @@ -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<Attribute>, P<Block>)> {
crate fn parse_inner_attrs_and_block(&mut self) -> PResult<'a, (Vec<Attribute>, P<Block>)> {
maybe_whole!(self, NtBlock, |x| (Vec::new(), x));

let lo = self.token.span;
Expand Down Expand Up @@ -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<Attribute>)
-> PResult<'a, ForeignItem> {
fn parse_item_foreign_fn(
&mut self,
vis: ast::Visibility,
lo: Span,
attrs: Vec<Attribute>,
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,
Expand Down Expand Up @@ -6835,12 +6840,14 @@ impl<'a> Parser<'a> {
/// extern "C" {}
/// extern {}
/// ```
fn parse_item_foreign_mod(&mut self,
lo: Span,
opt_abi: Option<Abi>,
visibility: Visibility,
mut attrs: Vec<Attribute>)
-> PResult<'a, P<Item>> {
fn parse_item_foreign_mod(
&mut self,
lo: Span,
opt_abi: Option<Abi>,
visibility: Visibility,
mut attrs: Vec<Attribute>,
extern_sp: Span,
) -> PResult<'a, P<Item>> {
self.expect(&token::OpenDelim(token::Brace))?;

let abi = opt_abi.unwrap_or(Abi::C);
Expand All @@ -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;
Expand Down Expand Up @@ -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)?));
}
Expand All @@ -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()?;
Expand Down Expand Up @@ -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()?;
Expand All @@ -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) {
Expand Down
11 changes: 11 additions & 0 deletions 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() {}
18 changes: 18 additions & 0 deletions 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

0 comments on commit c8d9cd9

Please sign in to comment.