Skip to content

Commit e452e29

Browse files
committed
Auto merge of #62670 - estebank:extern-fn-with-body, r=petrochenkov
Detect `fn` with a body in an `extern` block Fix #62109.
2 parents d82fd9e + f2a6a4e commit e452e29

File tree

5 files changed

+91
-15
lines changed

5 files changed

+91
-15
lines changed

Diff for: src/libsyntax/ext/expand.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -742,7 +742,7 @@ impl<'a> Parser<'a> {
742742
AstFragmentKind::ForeignItems => {
743743
let mut items = SmallVec::new();
744744
while self.token != token::Eof {
745-
items.push(self.parse_foreign_item()?);
745+
items.push(self.parse_foreign_item(DUMMY_SP)?);
746746
}
747747
AstFragment::ForeignItems(items)
748748
}

Diff for: src/libsyntax/parse/diagnostics.rs

+37
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use errors::{Applicability, DiagnosticBuilder, DiagnosticId};
1515
use rustc_data_structures::fx::FxHashSet;
1616
use syntax_pos::{Span, DUMMY_SP, MultiSpan};
1717
use log::{debug, trace};
18+
use std::mem;
1819

1920
/// Creates a placeholder argument.
2021
crate fn dummy_arg(ident: Ident) -> Arg {
@@ -783,6 +784,42 @@ impl<'a> Parser<'a> {
783784
Err(err)
784785
}
785786

787+
crate fn parse_semi_or_incorrect_foreign_fn_body(
788+
&mut self,
789+
ident: &Ident,
790+
extern_sp: Span,
791+
) -> PResult<'a, ()> {
792+
if self.token != token::Semi {
793+
// this might be an incorrect fn definition (#62109)
794+
let parser_snapshot = self.clone();
795+
match self.parse_inner_attrs_and_block() {
796+
Ok((_, body)) => {
797+
self.struct_span_err(ident.span, "incorrect `fn` inside `extern` block")
798+
.span_label(ident.span, "can't have a body")
799+
.span_label(body.span, "this body is invalid here")
800+
.span_label(
801+
extern_sp,
802+
"`extern` blocks define existing foreign functions and `fn`s \
803+
inside of them cannot have a body")
804+
.help("you might have meant to write a function accessible through ffi, \
805+
which can be done by writing `extern fn` outside of the \
806+
`extern` block")
807+
.note("for more information, visit \
808+
https://doc.rust-lang.org/std/keyword.extern.html")
809+
.emit();
810+
}
811+
Err(mut err) => {
812+
err.cancel();
813+
mem::replace(self, parser_snapshot);
814+
self.expect(&token::Semi)?;
815+
}
816+
}
817+
} else {
818+
self.bump();
819+
}
820+
Ok(())
821+
}
822+
786823
/// Consume alternative await syntaxes like `await <expr>`, `await? <expr>`, `await(<expr>)`
787824
/// and `await { <expr> }`.
788825
crate fn parse_incorrect_await_syntax(

Diff for: src/libsyntax/parse/parser.rs

+24-14
Original file line numberDiff line numberDiff line change
@@ -4611,7 +4611,7 @@ impl<'a> Parser<'a> {
46114611
}
46124612

46134613
/// Parses a block. Inner attributes are allowed.
4614-
fn parse_inner_attrs_and_block(&mut self) -> PResult<'a, (Vec<Attribute>, P<Block>)> {
4614+
crate fn parse_inner_attrs_and_block(&mut self) -> PResult<'a, (Vec<Attribute>, P<Block>)> {
46154615
maybe_whole!(self, NtBlock, |x| (Vec::new(), x));
46164616

46174617
let lo = self.token.span;
@@ -6698,15 +6698,20 @@ impl<'a> Parser<'a> {
66986698
}
66996699

67006700
/// Parses a function declaration from a foreign module.
6701-
fn parse_item_foreign_fn(&mut self, vis: ast::Visibility, lo: Span, attrs: Vec<Attribute>)
6702-
-> PResult<'a, ForeignItem> {
6701+
fn parse_item_foreign_fn(
6702+
&mut self,
6703+
vis: ast::Visibility,
6704+
lo: Span,
6705+
attrs: Vec<Attribute>,
6706+
extern_sp: Span,
6707+
) -> PResult<'a, ForeignItem> {
67036708
self.expect_keyword(kw::Fn)?;
67046709

67056710
let (ident, mut generics) = self.parse_fn_header()?;
67066711
let decl = self.parse_fn_decl(true)?;
67076712
generics.where_clause = self.parse_where_clause()?;
67086713
let hi = self.token.span;
6709-
self.expect(&token::Semi)?;
6714+
self.parse_semi_or_incorrect_foreign_fn_body(&ident, extern_sp)?;
67106715
Ok(ast::ForeignItem {
67116716
ident,
67126717
attrs,
@@ -6833,12 +6838,14 @@ impl<'a> Parser<'a> {
68336838
/// extern "C" {}
68346839
/// extern {}
68356840
/// ```
6836-
fn parse_item_foreign_mod(&mut self,
6837-
lo: Span,
6838-
opt_abi: Option<Abi>,
6839-
visibility: Visibility,
6840-
mut attrs: Vec<Attribute>)
6841-
-> PResult<'a, P<Item>> {
6841+
fn parse_item_foreign_mod(
6842+
&mut self,
6843+
lo: Span,
6844+
opt_abi: Option<Abi>,
6845+
visibility: Visibility,
6846+
mut attrs: Vec<Attribute>,
6847+
extern_sp: Span,
6848+
) -> PResult<'a, P<Item>> {
68426849
self.expect(&token::OpenDelim(token::Brace))?;
68436850

68446851
let abi = opt_abi.unwrap_or(Abi::C);
@@ -6847,7 +6854,7 @@ impl<'a> Parser<'a> {
68476854

68486855
let mut foreign_items = vec![];
68496856
while !self.eat(&token::CloseDelim(token::Brace)) {
6850-
foreign_items.push(self.parse_foreign_item()?);
6857+
foreign_items.push(self.parse_foreign_item(extern_sp)?);
68516858
}
68526859

68536860
let prev_span = self.prev_span;
@@ -7094,6 +7101,7 @@ impl<'a> Parser<'a> {
70947101
}
70957102

70967103
if self.eat_keyword(kw::Extern) {
7104+
let extern_sp = self.prev_span;
70977105
if self.eat_keyword(kw::Crate) {
70987106
return Ok(Some(self.parse_item_extern_crate(lo, visibility, attrs)?));
70997107
}
@@ -7117,7 +7125,9 @@ impl<'a> Parser<'a> {
71177125
maybe_append(attrs, extra_attrs));
71187126
return Ok(Some(item));
71197127
} else if self.check(&token::OpenDelim(token::Brace)) {
7120-
return Ok(Some(self.parse_item_foreign_mod(lo, opt_abi, visibility, attrs)?));
7128+
return Ok(Some(
7129+
self.parse_item_foreign_mod(lo, opt_abi, visibility, attrs, extern_sp)?,
7130+
));
71217131
}
71227132

71237133
self.unexpected()?;
@@ -7502,7 +7512,7 @@ impl<'a> Parser<'a> {
75027512
}
75037513

75047514
/// Parses a foreign item.
7505-
crate fn parse_foreign_item(&mut self) -> PResult<'a, ForeignItem> {
7515+
crate fn parse_foreign_item(&mut self, extern_sp: Span) -> PResult<'a, ForeignItem> {
75067516
maybe_whole!(self, NtForeignItem, |ni| ni);
75077517

75087518
let attrs = self.parse_outer_attributes()?;
@@ -7527,7 +7537,7 @@ impl<'a> Parser<'a> {
75277537
}
75287538
// FOREIGN FUNCTION ITEM
75297539
if self.check_keyword(kw::Fn) {
7530-
return Ok(self.parse_item_foreign_fn(visibility, lo, attrs)?);
7540+
return Ok(self.parse_item_foreign_fn(visibility, lo, attrs, extern_sp)?);
75317541
}
75327542
// FOREIGN TYPE ITEM
75337543
if self.check_keyword(kw::Type) {

Diff for: src/test/ui/extern/extern-ffi-fn-with-body.rs

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
extern "C" {
2+
fn foo() -> i32 { //~ ERROR incorrect `fn` inside `extern` block
3+
return 0;
4+
}
5+
}
6+
7+
extern "C" fn bar() -> i32 {
8+
return 0;
9+
}
10+
11+
fn main() {}

Diff for: src/test/ui/extern/extern-ffi-fn-with-body.stderr

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error: incorrect `fn` inside `extern` block
2+
--> $DIR/extern-ffi-fn-with-body.rs:2:8
3+
|
4+
LL | extern "C" {
5+
| ------ `extern` blocks define existing foreign functions and `fn`s inside of them cannot have a body
6+
LL | fn foo() -> i32 {
7+
| ________^^^__________-
8+
| | |
9+
| | can't have a body
10+
LL | | return 0;
11+
LL | | }
12+
| |_____- this body is invalid here
13+
|
14+
= 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
15+
= note: for more information, visit https://doc.rust-lang.org/std/keyword.extern.html
16+
17+
error: aborting due to previous error
18+

0 commit comments

Comments
 (0)