Skip to content

Detect fn with a body in an extern block #62670

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/libsyntax/ext/expand.rs
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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 cannot 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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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 cannot 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