diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 8dadf5ab1ab..3942062656f 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -13,7 +13,9 @@ use rustc_ast::walk_list; use rustc_ast::*; use rustc_ast_pretty::pprust::{self, State}; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{error_code, pluralize, struct_span_err, Applicability}; +use rustc_errors::{ + error_code, pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, +}; use rustc_parse::validate_attr; use rustc_session::lint::builtin::{ DEPRECATED_WHERE_CLAUSE_LOCATION, MISSING_ABI, PATTERNS_IN_FNS_WITHOUT_BODY, @@ -476,6 +478,17 @@ impl<'a> AstValidator<'a> { } fn error_item_without_body(&self, sp: Span, ctx: &str, msg: &str, sugg: &str) { + self.error_item_without_body_with_help(sp, ctx, msg, sugg, |_| ()); + } + + fn error_item_without_body_with_help( + &self, + sp: Span, + ctx: &str, + msg: &str, + sugg: &str, + help: impl FnOnce(&mut DiagnosticBuilder<'_, ErrorGuaranteed>), + ) { let source_map = self.session.source_map(); let end = source_map.end_point(sp); let replace_span = if source_map.span_to_snippet(end).map(|s| s == ";").unwrap_or(false) { @@ -483,15 +496,15 @@ impl<'a> AstValidator<'a> { } else { sp.shrink_to_hi() }; - self.err_handler() - .struct_span_err(sp, msg) - .span_suggestion( - replace_span, - &format!("provide a definition for the {}", ctx), - sugg, - Applicability::HasPlaceholders, - ) - .emit(); + let mut err = self.err_handler().struct_span_err(sp, msg); + err.span_suggestion( + replace_span, + &format!("provide a definition for the {}", ctx), + sugg, + Applicability::HasPlaceholders, + ); + help(&mut err); + err.emit(); } fn check_impl_item_provided(&self, sp: Span, body: &Option, ctx: &str, sugg: &str) { @@ -1191,8 +1204,38 @@ impl<'a> Visitor<'a> for AstValidator<'a> { if body.is_none() { let msg = "free function without a body"; - self.error_item_without_body(item.span, "function", msg, " { }"); + let ext = sig.header.ext; + + let f = |e: &mut DiagnosticBuilder<'_, _>| { + if let Extern::Implicit(start_span) | Extern::Explicit(_, start_span) = &ext + { + let start_suggestion = if let Extern::Explicit(abi, _) = ext { + format!("extern \"{}\" {{", abi.symbol_unescaped) + } else { + "extern {".to_owned() + }; + + let end_suggestion = " }".to_owned(); + let end_span = item.span.shrink_to_hi(); + + e + .multipart_suggestion( + "if you meant to declare an externally defined function, use an `extern` block", + vec![(*start_span, start_suggestion), (end_span, end_suggestion)], + Applicability::MaybeIncorrect, + ); + } + }; + + self.error_item_without_body_with_help( + item.span, + "function", + msg, + " { }", + f, + ); } + self.visit_vis(&item.vis); self.visit_ident(item.ident); let kind = diff --git a/src/test/ui/extern/not-in-block.rs b/src/test/ui/extern/not-in-block.rs new file mode 100644 index 00000000000..d3bcafdef7b --- /dev/null +++ b/src/test/ui/extern/not-in-block.rs @@ -0,0 +1,6 @@ +#![crate_type = "lib"] + +extern fn none_fn(x: bool) -> i32; +//~^ ERROR free function without a body +extern "C" fn c_fn(x: bool) -> i32; +//~^ ERROR free function without a body diff --git a/src/test/ui/extern/not-in-block.stderr b/src/test/ui/extern/not-in-block.stderr new file mode 100644 index 00000000000..2544949ab17 --- /dev/null +++ b/src/test/ui/extern/not-in-block.stderr @@ -0,0 +1,32 @@ +error: free function without a body + --> $DIR/not-in-block.rs:3:1 + | +LL | extern fn none_fn(x: bool) -> i32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: provide a definition for the function + | +LL | extern fn none_fn(x: bool) -> i32 { } + | ~~~~~~~~~~ +help: if you meant to declare an externally defined function, use an `extern` block + | +LL | extern { fn none_fn(x: bool) -> i32; } + | ~~~~~~~~ + + +error: free function without a body + --> $DIR/not-in-block.rs:5:1 + | +LL | extern "C" fn c_fn(x: bool) -> i32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: provide a definition for the function + | +LL | extern "C" fn c_fn(x: bool) -> i32 { } + | ~~~~~~~~~~ +help: if you meant to declare an externally defined function, use an `extern` block + | +LL | extern "C" { fn c_fn(x: bool) -> i32; } + | ~~~~~~~~~~~~ + + +error: aborting due to 2 previous errors +