Skip to content

Commit 02fb345

Browse files
committed
Suggest using block for extern "abi" fn with no body
1 parent 18ca294 commit 02fb345

File tree

3 files changed

+92
-11
lines changed

3 files changed

+92
-11
lines changed

Diff for: compiler/rustc_ast_passes/src/ast_validation.rs

+54-11
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ use rustc_ast::walk_list;
1313
use rustc_ast::*;
1414
use rustc_ast_pretty::pprust::{self, State};
1515
use rustc_data_structures::fx::FxHashMap;
16-
use rustc_errors::{error_code, pluralize, struct_span_err, Applicability};
16+
use rustc_errors::{
17+
error_code, pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed,
18+
};
1719
use rustc_parse::validate_attr;
1820
use rustc_session::lint::builtin::{
1921
DEPRECATED_WHERE_CLAUSE_LOCATION, MISSING_ABI, PATTERNS_IN_FNS_WITHOUT_BODY,
@@ -476,22 +478,33 @@ impl<'a> AstValidator<'a> {
476478
}
477479

478480
fn error_item_without_body(&self, sp: Span, ctx: &str, msg: &str, sugg: &str) {
481+
self.error_item_without_body_with_help(sp, ctx, msg, sugg, |_| ());
482+
}
483+
484+
fn error_item_without_body_with_help(
485+
&self,
486+
sp: Span,
487+
ctx: &str,
488+
msg: &str,
489+
sugg: &str,
490+
help: impl FnOnce(&mut DiagnosticBuilder<'_, ErrorGuaranteed>),
491+
) {
479492
let source_map = self.session.source_map();
480493
let end = source_map.end_point(sp);
481494
let replace_span = if source_map.span_to_snippet(end).map(|s| s == ";").unwrap_or(false) {
482495
end
483496
} else {
484497
sp.shrink_to_hi()
485498
};
486-
self.err_handler()
487-
.struct_span_err(sp, msg)
488-
.span_suggestion(
489-
replace_span,
490-
&format!("provide a definition for the {}", ctx),
491-
sugg,
492-
Applicability::HasPlaceholders,
493-
)
494-
.emit();
499+
let mut err = self.err_handler().struct_span_err(sp, msg);
500+
err.span_suggestion(
501+
replace_span,
502+
&format!("provide a definition for the {}", ctx),
503+
sugg,
504+
Applicability::HasPlaceholders,
505+
);
506+
help(&mut err);
507+
err.emit();
495508
}
496509

497510
fn check_impl_item_provided<T>(&self, sp: Span, body: &Option<T>, ctx: &str, sugg: &str) {
@@ -1191,8 +1204,38 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
11911204

11921205
if body.is_none() {
11931206
let msg = "free function without a body";
1194-
self.error_item_without_body(item.span, "function", msg, " { <body> }");
1207+
let ext = sig.header.ext;
1208+
1209+
let f = |e: &mut DiagnosticBuilder<'_, _>| {
1210+
if let Extern::Implicit(start_span) | Extern::Explicit(_, start_span) = &ext
1211+
{
1212+
let start_suggestion = if let Extern::Explicit(abi, _) = ext {
1213+
format!("extern \"{}\" {{", abi.symbol_unescaped)
1214+
} else {
1215+
"extern {".to_owned()
1216+
};
1217+
1218+
let end_suggestion = " }".to_owned();
1219+
let end_span = item.span.shrink_to_hi();
1220+
1221+
e
1222+
.multipart_suggestion(
1223+
"if you meant to declare an externally defined function, use an `extern` block",
1224+
vec![(*start_span, start_suggestion), (end_span, end_suggestion)],
1225+
Applicability::MaybeIncorrect,
1226+
);
1227+
}
1228+
};
1229+
1230+
self.error_item_without_body_with_help(
1231+
item.span,
1232+
"function",
1233+
msg,
1234+
" { <body> }",
1235+
f,
1236+
);
11951237
}
1238+
11961239
self.visit_vis(&item.vis);
11971240
self.visit_ident(item.ident);
11981241
let kind =

Diff for: src/test/ui/extern/not-in-block.rs

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#![crate_type = "lib"]
2+
3+
extern fn none_fn(x: bool) -> i32;
4+
//~^ ERROR free function without a body
5+
extern "C" fn c_fn(x: bool) -> i32;
6+
//~^ ERROR free function without a body

Diff for: src/test/ui/extern/not-in-block.stderr

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
error: free function without a body
2+
--> $DIR/not-in-block.rs:3:1
3+
|
4+
LL | extern fn none_fn(x: bool) -> i32;
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
help: provide a definition for the function
8+
|
9+
LL | extern fn none_fn(x: bool) -> i32 { <body> }
10+
| ~~~~~~~~~~
11+
help: if you meant to declare an externally defined function, use an `extern` block
12+
|
13+
LL | extern { fn none_fn(x: bool) -> i32; }
14+
| ~~~~~~~~ +
15+
16+
error: free function without a body
17+
--> $DIR/not-in-block.rs:5:1
18+
|
19+
LL | extern "C" fn c_fn(x: bool) -> i32;
20+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
21+
|
22+
help: provide a definition for the function
23+
|
24+
LL | extern "C" fn c_fn(x: bool) -> i32 { <body> }
25+
| ~~~~~~~~~~
26+
help: if you meant to declare an externally defined function, use an `extern` block
27+
|
28+
LL | extern "C" { fn c_fn(x: bool) -> i32; }
29+
| ~~~~~~~~~~~~ +
30+
31+
error: aborting due to 2 previous errors
32+

0 commit comments

Comments
 (0)