Skip to content

Commit e78e747

Browse files
committed
Auto merge of rust-lang#98827 - aDotInTheVoid:suggest-extern-block, r=nagisa
Suggest using block for `extern "abi" fn` with no body `@rustbot` modify labels: +A-diagnostics
2 parents fac8fa5 + 02fb345 commit e78e747

File tree

12 files changed

+124
-30
lines changed

12 files changed

+124
-30
lines changed

Diff for: compiler/rustc_ast/src/ast.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -2667,13 +2667,16 @@ impl Item {
26672667
#[derive(Clone, Copy, Encodable, Decodable, Debug)]
26682668
pub enum Extern {
26692669
None,
2670-
Implicit,
2671-
Explicit(StrLit),
2670+
Implicit(Span),
2671+
Explicit(StrLit, Span),
26722672
}
26732673

26742674
impl Extern {
2675-
pub fn from_abi(abi: Option<StrLit>) -> Extern {
2676-
abi.map_or(Extern::Implicit, Extern::Explicit)
2675+
pub fn from_abi(abi: Option<StrLit>, span: Span) -> Extern {
2676+
match abi {
2677+
Some(name) => Extern::Explicit(name, span),
2678+
None => Extern::Implicit(span),
2679+
}
26772680
}
26782681
}
26792682

Diff for: compiler/rustc_ast_lowering/src/item.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1272,8 +1272,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
12721272
pub(super) fn lower_extern(&mut self, ext: Extern) -> abi::Abi {
12731273
match ext {
12741274
Extern::None => abi::Abi::Rust,
1275-
Extern::Implicit => abi::Abi::FALLBACK,
1276-
Extern::Explicit(abi) => self.lower_abi(abi),
1275+
Extern::Implicit(_) => abi::Abi::FALLBACK,
1276+
Extern::Explicit(abi, _) => self.lower_abi(abi),
12771277
}
12781278
}
12791279

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

+58-14
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) {
@@ -630,7 +643,8 @@ impl<'a> AstValidator<'a> {
630643
match (fk.ctxt(), fk.header()) {
631644
(Some(FnCtxt::Foreign), _) => return,
632645
(Some(FnCtxt::Free), Some(header)) => match header.ext {
633-
Extern::Explicit(StrLit { symbol_unescaped: sym::C, .. }) | Extern::Implicit
646+
Extern::Explicit(StrLit { symbol_unescaped: sym::C, .. }, _)
647+
| Extern::Implicit(_)
634648
if matches!(header.unsafety, Unsafe::Yes(_)) =>
635649
{
636650
return;
@@ -842,7 +856,7 @@ impl<'a> AstValidator<'a> {
842856
.emit();
843857
});
844858
self.check_late_bound_lifetime_defs(&bfty.generic_params);
845-
if let Extern::Implicit = bfty.ext {
859+
if let Extern::Implicit(_) = bfty.ext {
846860
let sig_span = self.session.source_map().next_point(ty.span.shrink_to_lo());
847861
self.maybe_lint_missing_abi(sig_span, ty.id);
848862
}
@@ -1190,8 +1204,38 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
11901204

11911205
if body.is_none() {
11921206
let msg = "free function without a body";
1193-
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+
);
11941237
}
1238+
11951239
self.visit_vis(&item.vis);
11961240
self.visit_ident(item.ident);
11971241
let kind =
@@ -1556,7 +1600,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
15561600
if let FnKind::Fn(
15571601
_,
15581602
_,
1559-
FnSig { span: sig_span, header: FnHeader { ext: Extern::Implicit, .. }, .. },
1603+
FnSig { span: sig_span, header: FnHeader { ext: Extern::Implicit(_), .. }, .. },
15601604
_,
15611605
_,
15621606
_,

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ impl<'a> PostExpansionVisitor<'a> {
283283
}
284284

285285
fn check_extern(&self, ext: ast::Extern, constness: ast::Const) {
286-
if let ast::Extern::Explicit(abi) = ext {
286+
if let ast::Extern::Explicit(abi, _) = ext {
287287
self.check_abi(abi, constness);
288288
}
289289
}

Diff for: compiler/rustc_ast_pretty/src/pprust/state.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1734,10 +1734,10 @@ impl<'a> State<'a> {
17341734

17351735
match header.ext {
17361736
ast::Extern::None => {}
1737-
ast::Extern::Implicit => {
1737+
ast::Extern::Implicit(_) => {
17381738
self.word_nbsp("extern");
17391739
}
1740-
ast::Extern::Explicit(abi) => {
1740+
ast::Extern::Explicit(abi, _) => {
17411741
self.word_nbsp("extern");
17421742
self.print_literal(&abi.as_lit());
17431743
self.nbsp();

Diff for: compiler/rustc_parse/src/parser/mod.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -1353,7 +1353,16 @@ impl<'a> Parser<'a> {
13531353

13541354
/// Parses `extern string_literal?`.
13551355
fn parse_extern(&mut self) -> Extern {
1356-
if self.eat_keyword(kw::Extern) { Extern::from_abi(self.parse_abi()) } else { Extern::None }
1356+
if self.eat_keyword(kw::Extern) {
1357+
let mut extern_span = self.prev_token.span;
1358+
let abi = self.parse_abi();
1359+
if let Some(abi) = abi {
1360+
extern_span = extern_span.to(abi.span);
1361+
}
1362+
Extern::from_abi(abi, extern_span)
1363+
} else {
1364+
Extern::None
1365+
}
13571366
}
13581367

13591368
/// Parses a string literal as an ABI spec.

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+

Diff for: src/tools/clippy/clippy_lints/src/excessive_bools.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ impl ExcessiveBools {
9494

9595
fn check_fn_sig(&self, cx: &EarlyContext<'_>, fn_sig: &FnSig, span: Span) {
9696
match fn_sig.header.ext {
97-
Extern::Implicit | Extern::Explicit(_) => return,
97+
Extern::Implicit(_) | Extern::Explicit(_, _) => return,
9898
Extern::None => (),
9999
}
100100

Diff for: src/tools/clippy/clippy_utils/src/ast_utils.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -600,8 +600,8 @@ pub fn eq_ty(l: &Ty, r: &Ty) -> bool {
600600
pub fn eq_ext(l: &Extern, r: &Extern) -> bool {
601601
use Extern::*;
602602
match (l, r) {
603-
(None, None) | (Implicit, Implicit) => true,
604-
(Explicit(l), Explicit(r)) => eq_str_lit(l, r),
603+
(None, None) | (Implicit(_), Implicit(_)) => true,
604+
(Explicit(l,_), Explicit(r,_)) => eq_str_lit(l, r),
605605
_ => false,
606606
}
607607
}

Diff for: src/tools/rustfmt/src/items.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ impl<'a> Item<'a> {
148148
Item {
149149
unsafety: fm.unsafety,
150150
abi: format_extern(
151-
ast::Extern::from_abi(fm.abi),
151+
ast::Extern::from_abi(fm.abi, DUMMY_SP),
152152
config.force_explicit_abi(),
153153
true,
154154
),

Diff for: src/tools/rustfmt/src/utils.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,8 @@ pub(crate) fn format_extern(
138138
) -> Cow<'static, str> {
139139
let abi = match ext {
140140
ast::Extern::None => "Rust".to_owned(),
141-
ast::Extern::Implicit => "C".to_owned(),
142-
ast::Extern::Explicit(abi) => abi.symbol_unescaped.to_string(),
141+
ast::Extern::Implicit(_) => "C".to_owned(),
142+
ast::Extern::Explicit(abi, _) => abi.symbol_unescaped.to_string(),
143143
};
144144

145145
if abi == "Rust" && !is_mod {

0 commit comments

Comments
 (0)