about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--compiler/rustc_ast_lowering/src/errors.rs18
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs18
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs220
-rw-r--r--compiler/rustc_driver/src/lib.rs5
-rw-r--r--compiler/rustc_error_messages/locales/en-US/ast_lowering.ftl4
-rw-r--r--compiler/rustc_session/src/config.rs6
-rw-r--r--compiler/rustc_target/Cargo.toml3
-rw-r--r--compiler/rustc_target/src/spec/abi.rs188
-rw-r--r--src/test/run-make-fulldeps/print-calling-conventions/Makefile4
-rw-r--r--src/test/ui/abi/abi-typo-unstable.rs6
-rw-r--r--src/test/ui/abi/abi-typo-unstable.stderr11
-rw-r--r--src/test/ui/codemap_tests/unicode.normal.stderr2
-rw-r--r--src/test/ui/parser/issues/issue-8537.stderr2
-rw-r--r--src/test/ui/suggestions/abi-typo.fixed6
-rw-r--r--src/test/ui/suggestions/abi-typo.rs6
-rw-r--r--src/test/ui/suggestions/abi-typo.stderr14
17 files changed, 300 insertions, 214 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 12fa14ee817..cec227d2ed6 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4110,6 +4110,7 @@ version = "0.0.0"
 dependencies = [
  "bitflags",
  "rustc_data_structures",
+ "rustc_feature",
  "rustc_index",
  "rustc_macros",
  "rustc_serialize",
diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs
index 1ad5fa21d85..63ff64b00be 100644
--- a/compiler/rustc_ast_lowering/src/errors.rs
+++ b/compiler/rustc_ast_lowering/src/errors.rs
@@ -29,14 +29,28 @@ impl AddToDiagnostic for UseAngleBrackets {
 }
 
 #[derive(Diagnostic)]
-#[help]
 #[diag(ast_lowering::invalid_abi, code = "E0703")]
+#[note]
 pub struct InvalidAbi {
     #[primary_span]
     #[label]
     pub span: Span,
     pub abi: Symbol,
-    pub valid_abis: String,
+    pub command: String,
+    #[subdiagnostic]
+    pub suggestion: Option<InvalidAbiSuggestion>,
+}
+
+#[derive(Subdiagnostic)]
+#[suggestion(
+    ast_lowering::invalid_abi_suggestion,
+    code = "{suggestion}",
+    applicability = "maybe-incorrect"
+)]
+pub struct InvalidAbiSuggestion {
+    #[primary_span]
+    pub span: Span,
+    pub suggestion: String,
 }
 
 #[derive(Diagnostic, Clone, Copy)]
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index 550833275e4..dfd04fe2974 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -1,4 +1,4 @@
-use super::errors::{InvalidAbi, MisplacedRelaxTraitBound};
+use super::errors::{InvalidAbi, InvalidAbiSuggestion, MisplacedRelaxTraitBound};
 use super::ResolverAstLoweringExt;
 use super::{Arena, AstOwner, ImplTraitContext, ImplTraitPosition};
 use super::{FnDeclKind, LoweringContext, ParamMode};
@@ -14,9 +14,10 @@ use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
 use rustc_hir::PredicateOrigin;
 use rustc_index::vec::{Idx, IndexVec};
 use rustc_middle::ty::{DefIdTree, ResolverAstLowering, TyCtxt};
+use rustc_span::lev_distance::find_best_match_for_name;
 use rustc_span::source_map::DesugaringKind;
 use rustc_span::symbol::{kw, sym, Ident};
-use rustc_span::Span;
+use rustc_span::{Span, Symbol};
 use rustc_target::spec::abi;
 use smallvec::{smallvec, SmallVec};
 
@@ -1280,10 +1281,19 @@ impl<'hir> LoweringContext<'_, 'hir> {
     }
 
     fn error_on_invalid_abi(&self, abi: StrLit) {
+        let abi_names = abi::enabled_names(self.tcx.features(), abi.span)
+            .iter()
+            .map(|s| Symbol::intern(s))
+            .collect::<Vec<_>>();
+        let suggested_name = find_best_match_for_name(&abi_names, abi.symbol_unescaped, None);
         self.tcx.sess.emit_err(InvalidAbi {
+            abi: abi.symbol_unescaped,
             span: abi.span,
-            abi: abi.symbol,
-            valid_abis: abi::all_names().join(", "),
+            suggestion: suggested_name.map(|suggested_name| InvalidAbiSuggestion {
+                span: abi.span,
+                suggestion: format!("\"{suggested_name}\""),
+            }),
+            command: "rustc --print=calling-conventions".to_string(),
         });
     }
 
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index aeff73c5bbb..0017a28cf1b 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -3,13 +3,13 @@ use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor};
 use rustc_ast::{AssocConstraint, AssocConstraintKind, NodeId};
 use rustc_ast::{PatKind, RangeEnd, VariantData};
 use rustc_errors::{struct_span_err, Applicability, StashKey};
-use rustc_feature::Features;
-use rustc_feature::{AttributeGate, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
-use rustc_session::parse::{feature_err, feature_warn};
+use rustc_feature::{AttributeGate, BuiltinAttribute, Features, GateIssue, BUILTIN_ATTRIBUTE_MAP};
+use rustc_session::parse::{feature_err, feature_err_issue, feature_warn};
 use rustc_session::Session;
 use rustc_span::source_map::Spanned;
 use rustc_span::symbol::sym;
 use rustc_span::Span;
+use rustc_target::spec::abi;
 
 macro_rules! gate_feature_fn {
     ($visitor: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr, $help: expr) => {{
@@ -84,210 +84,26 @@ impl<'a> PostExpansionVisitor<'a> {
             }
         }
 
-        match symbol_unescaped.as_str() {
-            // Stable
-            "Rust" | "C" | "cdecl" | "stdcall" | "fastcall" | "aapcs" | "win64" | "sysv64"
-            | "system" => {}
-            "rust-intrinsic" => {
-                gate_feature_post!(&self, intrinsics, span, "intrinsics are subject to change");
-            }
-            "platform-intrinsic" => {
-                gate_feature_post!(
-                    &self,
-                    platform_intrinsics,
-                    span,
-                    "platform intrinsics are experimental and possibly buggy"
-                );
-            }
-            "vectorcall" => {
-                gate_feature_post!(
-                    &self,
-                    abi_vectorcall,
-                    span,
-                    "vectorcall is experimental and subject to change"
-                );
-            }
-            "thiscall" => {
-                gate_feature_post!(
-                    &self,
-                    abi_thiscall,
-                    span,
-                    "thiscall is experimental and subject to change"
-                );
-            }
-            "rust-call" => {
-                gate_feature_post!(
-                    &self,
-                    unboxed_closures,
-                    span,
-                    "rust-call ABI is subject to change"
-                );
-            }
-            "rust-cold" => {
-                gate_feature_post!(
-                    &self,
-                    rust_cold_cc,
-                    span,
-                    "rust-cold is experimental and subject to change"
-                );
-            }
-            "ptx-kernel" => {
-                gate_feature_post!(
-                    &self,
-                    abi_ptx,
-                    span,
-                    "PTX ABIs are experimental and subject to change"
-                );
-            }
-            "unadjusted" => {
-                gate_feature_post!(
-                    &self,
-                    abi_unadjusted,
-                    span,
-                    "unadjusted ABI is an implementation detail and perma-unstable"
-                );
-            }
-            "msp430-interrupt" => {
-                gate_feature_post!(
-                    &self,
-                    abi_msp430_interrupt,
-                    span,
-                    "msp430-interrupt ABI is experimental and subject to change"
-                );
-            }
-            "x86-interrupt" => {
-                gate_feature_post!(
-                    &self,
-                    abi_x86_interrupt,
-                    span,
-                    "x86-interrupt ABI is experimental and subject to change"
-                );
-            }
-            "amdgpu-kernel" => {
-                gate_feature_post!(
-                    &self,
-                    abi_amdgpu_kernel,
-                    span,
-                    "amdgpu-kernel ABI is experimental and subject to change"
-                );
-            }
-            "avr-interrupt" | "avr-non-blocking-interrupt" => {
-                gate_feature_post!(
-                    &self,
-                    abi_avr_interrupt,
-                    span,
-                    "avr-interrupt and avr-non-blocking-interrupt ABIs are experimental and subject to change"
-                );
-            }
-            "efiapi" => {
-                gate_feature_post!(
-                    &self,
-                    abi_efiapi,
-                    span,
-                    "efiapi ABI is experimental and subject to change"
-                );
-            }
-            "C-cmse-nonsecure-call" => {
-                gate_feature_post!(
-                    &self,
-                    abi_c_cmse_nonsecure_call,
-                    span,
-                    "C-cmse-nonsecure-call ABI is experimental and subject to change"
-                );
-            }
-            "C-unwind" => {
-                gate_feature_post!(
-                    &self,
-                    c_unwind,
-                    span,
-                    "C-unwind ABI is experimental and subject to change"
-                );
-            }
-            "stdcall-unwind" => {
-                gate_feature_post!(
-                    &self,
-                    c_unwind,
+        match abi::is_enabled(&self.features, span, symbol_unescaped.as_str()) {
+            Ok(()) => (),
+            Err(abi::AbiDisabled::Unstable { feature, explain }) => {
+                feature_err_issue(
+                    &self.sess.parse_sess,
+                    feature,
                     span,
-                    "stdcall-unwind ABI is experimental and subject to change"
-                );
-            }
-            "system-unwind" => {
-                gate_feature_post!(
-                    &self,
-                    c_unwind,
-                    span,
-                    "system-unwind ABI is experimental and subject to change"
-                );
-            }
-            "thiscall-unwind" => {
-                gate_feature_post!(
-                    &self,
-                    c_unwind,
-                    span,
-                    "thiscall-unwind ABI is experimental and subject to change"
-                );
-            }
-            "cdecl-unwind" => {
-                gate_feature_post!(
-                    &self,
-                    c_unwind,
-                    span,
-                    "cdecl-unwind ABI is experimental and subject to change"
-                );
-            }
-            "fastcall-unwind" => {
-                gate_feature_post!(
-                    &self,
-                    c_unwind,
-                    span,
-                    "fastcall-unwind ABI is experimental and subject to change"
-                );
-            }
-            "vectorcall-unwind" => {
-                gate_feature_post!(
-                    &self,
-                    c_unwind,
-                    span,
-                    "vectorcall-unwind ABI is experimental and subject to change"
-                );
-            }
-            "aapcs-unwind" => {
-                gate_feature_post!(
-                    &self,
-                    c_unwind,
-                    span,
-                    "aapcs-unwind ABI is experimental and subject to change"
-                );
-            }
-            "win64-unwind" => {
-                gate_feature_post!(
-                    &self,
-                    c_unwind,
-                    span,
-                    "win64-unwind ABI is experimental and subject to change"
-                );
-            }
-            "sysv64-unwind" => {
-                gate_feature_post!(
-                    &self,
-                    c_unwind,
-                    span,
-                    "sysv64-unwind ABI is experimental and subject to change"
-                );
-            }
-            "wasm" => {
-                gate_feature_post!(
-                    &self,
-                    wasm_abi,
-                    span,
-                    "wasm ABI is experimental and subject to change"
-                );
+                    GateIssue::Language,
+                    explain,
+                )
+                .emit();
             }
-            abi => {
+            Err(abi::AbiDisabled::Unrecognized) => {
                 if self.sess.opts.pretty.map_or(true, |ppm| ppm.needs_hir()) {
                     self.sess.parse_sess.span_diagnostic.delay_span_bug(
                         span,
-                        &format!("unrecognized ABI not caught in lowering: {}", abi),
+                        &format!(
+                            "unrecognized ABI not caught in lowering: {}",
+                            symbol_unescaped.as_str()
+                        ),
                     );
                 }
             }
diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs
index 8fb9508194b..c768935eb62 100644
--- a/compiler/rustc_driver/src/lib.rs
+++ b/compiler/rustc_driver/src/lib.rs
@@ -742,6 +742,11 @@ fn print_crate_info(
                     println!("{}", cfg);
                 }
             }
+            CallingConventions => {
+                let mut calling_conventions = rustc_target::spec::abi::all_names();
+                calling_conventions.sort_unstable();
+                println!("{}", calling_conventions.join("\n"));
+            }
             RelocationModels
             | CodeModels
             | TlsModels
diff --git a/compiler/rustc_error_messages/locales/en-US/ast_lowering.ftl b/compiler/rustc_error_messages/locales/en-US/ast_lowering.ftl
index c45e045b4db..03c88c6c0eb 100644
--- a/compiler/rustc_error_messages/locales/en-US/ast_lowering.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/ast_lowering.ftl
@@ -7,7 +7,9 @@ ast_lowering_use_angle_brackets = use angle brackets instead
 ast_lowering_invalid_abi =
     invalid ABI: found `{$abi}`
     .label = invalid ABI
-    .help = valid ABIs: {$valid_abis}
+    .note = invoke `{$command}` for a full list of supported calling conventions.
+
+ast_lowering_invalid_abi_suggestion = did you mean
 
 ast_lowering_assoc_ty_parentheses =
     parenthesized generic arguments cannot be used in associated type constraints
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 57c9a3f4822..70b470f3811 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -538,6 +538,7 @@ pub enum PrintRequest {
     TargetLibdir,
     CrateName,
     Cfg,
+    CallingConventions,
     TargetList,
     TargetCPUs,
     TargetFeatures,
@@ -1354,8 +1355,8 @@ pub fn rustc_short_optgroups() -> Vec<RustcOptGroup> {
             "",
             "print",
             "Compiler information to print on stdout",
-            "[crate-name|file-names|sysroot|target-libdir|cfg|target-list|\
-             target-cpus|target-features|relocation-models|code-models|\
+            "[crate-name|file-names|sysroot|target-libdir|cfg|calling-conventions|\
+             target-list|target-cpus|target-features|relocation-models|code-models|\
              tls-models|target-spec-json|native-static-libs|stack-protector-strategies|\
              link-args]",
         ),
@@ -1794,6 +1795,7 @@ fn collect_print_requests(
         "sysroot" => PrintRequest::Sysroot,
         "target-libdir" => PrintRequest::TargetLibdir,
         "cfg" => PrintRequest::Cfg,
+        "calling-conventions" => PrintRequest::CallingConventions,
         "target-list" => PrintRequest::TargetList,
         "target-cpus" => PrintRequest::TargetCPUs,
         "target-features" => PrintRequest::TargetFeatures,
diff --git a/compiler/rustc_target/Cargo.toml b/compiler/rustc_target/Cargo.toml
index 162376af45f..fc37fdb1c43 100644
--- a/compiler/rustc_target/Cargo.toml
+++ b/compiler/rustc_target/Cargo.toml
@@ -8,7 +8,8 @@ bitflags = "1.2.1"
 tracing = "0.1"
 serde_json = "1.0.59"
 rustc_data_structures = { path = "../rustc_data_structures" }
+rustc_feature = { path = "../rustc_feature" }
+rustc_index = { path = "../rustc_index" }
 rustc_macros = { path = "../rustc_macros" }
 rustc_serialize = { path = "../rustc_serialize" }
 rustc_span = { path = "../rustc_span" }
-rustc_index = { path = "../rustc_index" }
diff --git a/compiler/rustc_target/src/spec/abi.rs b/compiler/rustc_target/src/spec/abi.rs
index 337554dc96e..c915124434b 100644
--- a/compiler/rustc_target/src/spec/abi.rs
+++ b/compiler/rustc_target/src/spec/abi.rs
@@ -1,6 +1,8 @@
 use std::fmt;
 
 use rustc_macros::HashStable_Generic;
+use rustc_span::symbol::sym;
+use rustc_span::{Span, Symbol};
 
 #[cfg(test)]
 mod tests;
@@ -94,6 +96,192 @@ pub fn all_names() -> Vec<&'static str> {
     AbiDatas.iter().map(|d| d.name).collect()
 }
 
+pub fn enabled_names(features: &rustc_feature::Features, span: Span) -> Vec<&'static str> {
+    AbiDatas
+        .iter()
+        .map(|d| d.name)
+        .filter(|name| is_enabled(features, span, name).is_ok())
+        .collect()
+}
+
+pub enum AbiDisabled {
+    Unstable { feature: Symbol, explain: &'static str },
+    Unrecognized,
+}
+
+fn gate_feature_post(
+    features: &rustc_feature::Features,
+    feature: Symbol,
+    span: Span,
+    explain: &'static str,
+) -> Result<(), AbiDisabled> {
+    if !features.enabled(feature) && !span.allows_unstable(feature) {
+        Err(AbiDisabled::Unstable { feature, explain })
+    } else {
+        Ok(())
+    }
+}
+
+pub fn is_enabled(
+    features: &rustc_feature::Features,
+    span: Span,
+    name: &str,
+) -> Result<(), AbiDisabled> {
+    match name {
+        // Stable
+        "Rust" | "C" | "cdecl" | "stdcall" | "fastcall" | "aapcs" | "win64" | "sysv64"
+        | "system" => Ok(()),
+        "rust-intrinsic" => {
+            gate_feature_post(features, sym::intrinsics, span, "intrinsics are subject to change")
+        }
+        "platform-intrinsic" => gate_feature_post(
+            features,
+            sym::platform_intrinsics,
+            span,
+            "platform intrinsics are experimental and possibly buggy",
+        ),
+        "vectorcall" => gate_feature_post(
+            features,
+            sym::abi_vectorcall,
+            span,
+            "vectorcall is experimental and subject to change",
+        ),
+        "thiscall" => gate_feature_post(
+            features,
+            sym::abi_thiscall,
+            span,
+            "thiscall is experimental and subject to change",
+        ),
+        "rust-call" => gate_feature_post(
+            features,
+            sym::unboxed_closures,
+            span,
+            "rust-call ABI is subject to change",
+        ),
+        "rust-cold" => gate_feature_post(
+            features,
+            sym::rust_cold_cc,
+            span,
+            "rust-cold is experimental and subject to change",
+        ),
+        "ptx-kernel" => gate_feature_post(
+            features,
+            sym::abi_ptx,
+            span,
+            "PTX ABIs are experimental and subject to change",
+        ),
+        "unadjusted" => gate_feature_post(
+            features,
+            sym::abi_unadjusted,
+            span,
+            "unadjusted ABI is an implementation detail and perma-unstable",
+        ),
+        "msp430-interrupt" => gate_feature_post(
+            features,
+            sym::abi_msp430_interrupt,
+            span,
+            "msp430-interrupt ABI is experimental and subject to change",
+        ),
+        "x86-interrupt" => gate_feature_post(
+            features,
+            sym::abi_x86_interrupt,
+            span,
+            "x86-interrupt ABI is experimental and subject to change",
+        ),
+        "amdgpu-kernel" => gate_feature_post(
+            features,
+            sym::abi_amdgpu_kernel,
+            span,
+            "amdgpu-kernel ABI is experimental and subject to change",
+        ),
+        "avr-interrupt" | "avr-non-blocking-interrupt" => gate_feature_post(
+            features,
+            sym::abi_avr_interrupt,
+            span,
+            "avr-interrupt and avr-non-blocking-interrupt ABIs are experimental and subject to change",
+        ),
+        "efiapi" => gate_feature_post(
+            features,
+            sym::abi_efiapi,
+            span,
+            "efiapi ABI is experimental and subject to change",
+        ),
+        "C-cmse-nonsecure-call" => gate_feature_post(
+            features,
+            sym::abi_c_cmse_nonsecure_call,
+            span,
+            "C-cmse-nonsecure-call ABI is experimental and subject to change",
+        ),
+        "C-unwind" => gate_feature_post(
+            features,
+            sym::c_unwind,
+            span,
+            "C-unwind ABI is experimental and subject to change",
+        ),
+        "stdcall-unwind" => gate_feature_post(
+            features,
+            sym::c_unwind,
+            span,
+            "stdcall-unwind ABI is experimental and subject to change",
+        ),
+        "system-unwind" => gate_feature_post(
+            features,
+            sym::c_unwind,
+            span,
+            "system-unwind ABI is experimental and subject to change",
+        ),
+        "thiscall-unwind" => gate_feature_post(
+            features,
+            sym::c_unwind,
+            span,
+            "thiscall-unwind ABI is experimental and subject to change",
+        ),
+        "cdecl-unwind" => gate_feature_post(
+            features,
+            sym::c_unwind,
+            span,
+            "cdecl-unwind ABI is experimental and subject to change",
+        ),
+        "fastcall-unwind" => gate_feature_post(
+            features,
+            sym::c_unwind,
+            span,
+            "fastcall-unwind ABI is experimental and subject to change",
+        ),
+        "vectorcall-unwind" => gate_feature_post(
+            features,
+            sym::c_unwind,
+            span,
+            "vectorcall-unwind ABI is experimental and subject to change",
+        ),
+        "aapcs-unwind" => gate_feature_post(
+            features,
+            sym::c_unwind,
+            span,
+            "aapcs-unwind ABI is experimental and subject to change",
+        ),
+        "win64-unwind" => gate_feature_post(
+            features,
+            sym::c_unwind,
+            span,
+            "win64-unwind ABI is experimental and subject to change",
+        ),
+        "sysv64-unwind" => gate_feature_post(
+            features,
+            sym::c_unwind,
+            span,
+            "sysv64-unwind ABI is experimental and subject to change",
+        ),
+        "wasm" => gate_feature_post(
+            features,
+            sym::wasm_abi,
+            span,
+            "wasm ABI is experimental and subject to change",
+        ),
+        _ => Err(AbiDisabled::Unrecognized),
+    }
+}
+
 impl Abi {
     /// Default ABI chosen for `extern fn` declarations without an explicit ABI.
     pub const FALLBACK: Abi = Abi::C { unwind: false };
diff --git a/src/test/run-make-fulldeps/print-calling-conventions/Makefile b/src/test/run-make-fulldeps/print-calling-conventions/Makefile
new file mode 100644
index 00000000000..d3fd06392b0
--- /dev/null
+++ b/src/test/run-make-fulldeps/print-calling-conventions/Makefile
@@ -0,0 +1,4 @@
+-include ../tools.mk
+
+all:
+	$(RUSTC) --print calling-conventions
diff --git a/src/test/ui/abi/abi-typo-unstable.rs b/src/test/ui/abi/abi-typo-unstable.rs
new file mode 100644
index 00000000000..94991a5eb17
--- /dev/null
+++ b/src/test/ui/abi/abi-typo-unstable.rs
@@ -0,0 +1,6 @@
+// rust-intrinsic is unstable and not enabled, so it should not be suggested as a fix
+extern "rust-intrinsec" fn rust_intrinsic() {} //~ ERROR invalid ABI
+
+fn main() {
+    rust_intrinsic();
+}
diff --git a/src/test/ui/abi/abi-typo-unstable.stderr b/src/test/ui/abi/abi-typo-unstable.stderr
new file mode 100644
index 00000000000..3b346e00227
--- /dev/null
+++ b/src/test/ui/abi/abi-typo-unstable.stderr
@@ -0,0 +1,11 @@
+error[E0703]: invalid ABI: found `rust-intrinsec`
+  --> $DIR/abi-typo-unstable.rs:2:8
+   |
+LL | extern "rust-intrinsec" fn rust_intrinsic() {}
+   |        ^^^^^^^^^^^^^^^^ invalid ABI
+   |
+   = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions.
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0703`.
diff --git a/src/test/ui/codemap_tests/unicode.normal.stderr b/src/test/ui/codemap_tests/unicode.normal.stderr
index 60f8cff84b3..05ceb6910da 100644
--- a/src/test/ui/codemap_tests/unicode.normal.stderr
+++ b/src/test/ui/codemap_tests/unicode.normal.stderr
@@ -4,7 +4,7 @@ error[E0703]: invalid ABI: found `路濫狼á́́`
 LL | extern "路濫狼á́́" fn foo() {}
    |        ^^^^^^^^^ invalid ABI
    |
-   = help: valid ABIs: Rust, C, C-unwind, cdecl, cdecl-unwind, stdcall, stdcall-unwind, fastcall, fastcall-unwind, vectorcall, vectorcall-unwind, thiscall, thiscall-unwind, aapcs, aapcs-unwind, win64, win64-unwind, sysv64, sysv64-unwind, ptx-kernel, msp430-interrupt, x86-interrupt, amdgpu-kernel, efiapi, avr-interrupt, avr-non-blocking-interrupt, C-cmse-nonsecure-call, wasm, system, system-unwind, rust-intrinsic, rust-call, platform-intrinsic, unadjusted, rust-cold
+   = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions.
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/issues/issue-8537.stderr b/src/test/ui/parser/issues/issue-8537.stderr
index 505d830ef3e..523cc9dc588 100644
--- a/src/test/ui/parser/issues/issue-8537.stderr
+++ b/src/test/ui/parser/issues/issue-8537.stderr
@@ -4,7 +4,7 @@ error[E0703]: invalid ABI: found `invalid-ab_isize`
 LL |   "invalid-ab_isize"
    |   ^^^^^^^^^^^^^^^^^^ invalid ABI
    |
-   = help: valid ABIs: Rust, C, C-unwind, cdecl, cdecl-unwind, stdcall, stdcall-unwind, fastcall, fastcall-unwind, vectorcall, vectorcall-unwind, thiscall, thiscall-unwind, aapcs, aapcs-unwind, win64, win64-unwind, sysv64, sysv64-unwind, ptx-kernel, msp430-interrupt, x86-interrupt, amdgpu-kernel, efiapi, avr-interrupt, avr-non-blocking-interrupt, C-cmse-nonsecure-call, wasm, system, system-unwind, rust-intrinsic, rust-call, platform-intrinsic, unadjusted, rust-cold
+   = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions.
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/suggestions/abi-typo.fixed b/src/test/ui/suggestions/abi-typo.fixed
new file mode 100644
index 00000000000..04d265865f0
--- /dev/null
+++ b/src/test/ui/suggestions/abi-typo.fixed
@@ -0,0 +1,6 @@
+// run-rustfix
+extern "cdecl" fn cdedl() {} //~ ERROR invalid ABI
+
+fn main() {
+    cdedl();
+}
diff --git a/src/test/ui/suggestions/abi-typo.rs b/src/test/ui/suggestions/abi-typo.rs
new file mode 100644
index 00000000000..6d80db522eb
--- /dev/null
+++ b/src/test/ui/suggestions/abi-typo.rs
@@ -0,0 +1,6 @@
+// run-rustfix
+extern "cdedl" fn cdedl() {} //~ ERROR invalid ABI
+
+fn main() {
+    cdedl();
+}
diff --git a/src/test/ui/suggestions/abi-typo.stderr b/src/test/ui/suggestions/abi-typo.stderr
new file mode 100644
index 00000000000..67a84f119f6
--- /dev/null
+++ b/src/test/ui/suggestions/abi-typo.stderr
@@ -0,0 +1,14 @@
+error[E0703]: invalid ABI: found `cdedl`
+  --> $DIR/abi-typo.rs:2:8
+   |
+LL | extern "cdedl" fn cdedl() {}
+   |        ^^^^^^^
+   |        |
+   |        invalid ABI
+   |        help: did you mean: `"cdecl"`
+   |
+   = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions.
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0703`.