about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_attr_data_structures/src/attributes.rs3
-rw-r--r--compiler/rustc_attr_data_structures/src/encode_cross_crate.rs1
-rw-r--r--compiler/rustc_attr_parsing/messages.ftl3
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/link_attrs.rs39
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs34
-rw-r--r--compiler/rustc_attr_parsing/src/context.rs36
-rw-r--r--compiler/rustc_attr_parsing/src/session_diagnostics.rs9
-rw-r--r--compiler/rustc_codegen_ssa/messages.ftl6
-rw-r--r--compiler/rustc_codegen_ssa/src/codegen_attrs.rs49
-rw-r--r--compiler/rustc_codegen_ssa/src/errors.rs16
-rw-r--r--compiler/rustc_metadata/src/native_libs.rs10
-rw-r--r--compiler/rustc_parse/src/validate_attr.rs1
-rw-r--r--compiler/rustc_passes/src/check_attr.rs10
-rw-r--r--tests/ui/attributes/malformed-attrs.stderr15
-rw-r--r--tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-invalid-format.rs4
-rw-r--r--tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-invalid-format.stderr19
-rw-r--r--tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-missing-argument.rs6
-rw-r--r--tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-missing-argument.stderr21
-rw-r--r--tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-too-many-arguments.rs6
-rw-r--r--tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-too-many-arguments.stderr21
20 files changed, 159 insertions, 150 deletions
diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs
index f61efcf2388..abe7fb77e84 100644
--- a/compiler/rustc_attr_data_structures/src/attributes.rs
+++ b/compiler/rustc_attr_data_structures/src/attributes.rs
@@ -294,6 +294,9 @@ pub enum AttributeKind {
     /// Represents `#[link_name]`.
     LinkName { name: Symbol, span: Span },
 
+    /// Represents `#[link_ordinal]`.
+    LinkOrdinal { ordinal: u16, span: Span },
+
     /// Represents [`#[link_section]`](https://doc.rust-lang.org/reference/abi.html#the-link_section-attribute)
     LinkSection { name: Symbol, span: Span },
 
diff --git a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs
index ad587523e03..b7653881a78 100644
--- a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs
+++ b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs
@@ -40,6 +40,7 @@ impl AttributeKind {
             Ignore { .. } => No,
             Inline(..) => No,
             LinkName { .. } => Yes,
+            LinkOrdinal { .. } => No,
             LinkSection { .. } => No,
             LoopMatch(..) => No,
             MacroTransparency(..) => Yes,
diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl
index bec3a1e8a59..8d0ead63a8d 100644
--- a/compiler/rustc_attr_parsing/messages.ftl
+++ b/compiler/rustc_attr_parsing/messages.ftl
@@ -78,6 +78,9 @@ attr_parsing_invalid_repr_hint_no_value =
 attr_parsing_invalid_since =
     'since' must be a Rust version number, such as "1.31.0"
 
+attr_parsing_link_ordinal_out_of_range = ordinal value in `link_ordinal` is too large: `{$ordinal}`
+    .note = the value may not exceed `u16::MAX`
+
 attr_parsing_missing_feature =
     missing 'feature'
 
diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs
index 23a8e96482d..960cebd8925 100644
--- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs
@@ -1,14 +1,14 @@
 use rustc_attr_data_structures::AttributeKind;
-use rustc_attr_data_structures::AttributeKind::{LinkName, LinkSection};
+use rustc_attr_data_structures::AttributeKind::{LinkName, LinkOrdinal, LinkSection};
 use rustc_feature::{AttributeTemplate, template};
 use rustc_span::{Span, Symbol, sym};
 
 use crate::attributes::{
     AttributeOrder, NoArgsAttributeParser, OnDuplicate, SingleAttributeParser,
 };
-use crate::context::{AcceptContext, Stage};
+use crate::context::{AcceptContext, Stage, parse_single_integer};
 use crate::parser::ArgParser;
-use crate::session_diagnostics::NullOnLinkSection;
+use crate::session_diagnostics::{LinkOrdinalOutOfRange, NullOnLinkSection};
 
 pub(crate) struct LinkNameParser;
 
@@ -87,3 +87,36 @@ impl<S: Stage> NoArgsAttributeParser<S> for StdInternalSymbolParser {
     const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
     const CREATE: fn(Span) -> AttributeKind = AttributeKind::StdInternalSymbol;
 }
+
+pub(crate) struct LinkOrdinalParser;
+
+impl<S: Stage> SingleAttributeParser<S> for LinkOrdinalParser {
+    const PATH: &[Symbol] = &[sym::link_ordinal];
+    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
+    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
+    const TEMPLATE: AttributeTemplate = template!(List: "ordinal");
+
+    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
+        let ordinal = parse_single_integer(cx, args)?;
+
+        // According to the table at
+        // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-header, the
+        // ordinal must fit into 16 bits. Similarly, the Ordinal field in COFFShortExport (defined
+        // in llvm/include/llvm/Object/COFFImportFile.h), which we use to communicate import
+        // information to LLVM for `#[link(kind = "raw-dylib"_])`, is also defined to be uint16_t.
+        //
+        // FIXME: should we allow an ordinal of 0?  The MSVC toolchain has inconsistent support for
+        // this: both LINK.EXE and LIB.EXE signal errors and abort when given a .DEF file that
+        // specifies a zero ordinal. However, llvm-dlltool is perfectly happy to generate an import
+        // library for such a .DEF file, and MSVC's LINK.EXE is also perfectly happy to consume an
+        // import library produced by LLVM with an ordinal of 0, and it generates an .EXE.  (I
+        // don't know yet if the resulting EXE runs, as I haven't yet built the necessary DLL --
+        // see earlier comment about LINK.EXE failing.)
+        let Ok(ordinal) = ordinal.try_into() else {
+            cx.emit_err(LinkOrdinalOutOfRange { span: cx.attr_span, ordinal });
+            return None;
+        };
+
+        Some(LinkOrdinal { ordinal, span: cx.attr_span })
+    }
+}
diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs
index ec821cb11ce..7ca951dc0bb 100644
--- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs
@@ -1,10 +1,9 @@
-use rustc_ast::LitKind;
 use rustc_attr_data_structures::AttributeKind;
 use rustc_feature::{AttributeTemplate, template};
 use rustc_span::{Symbol, sym};
 
 use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
-use crate::context::{AcceptContext, Stage};
+use crate::context::{AcceptContext, Stage, parse_single_integer};
 use crate::parser::ArgParser;
 
 pub(crate) struct RustcLayoutScalarValidRangeStart;
@@ -16,8 +15,8 @@ impl<S: Stage> SingleAttributeParser<S> for RustcLayoutScalarValidRangeStart {
     const TEMPLATE: AttributeTemplate = template!(List: "start");
 
     fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
-        parse_rustc_layout_scalar_valid_range(cx, args)
-            .map(|n| AttributeKind::RustcLayoutScalarValidRangeStart(n, cx.attr_span))
+        parse_single_integer(cx, args)
+            .map(|n| AttributeKind::RustcLayoutScalarValidRangeStart(Box::new(n), cx.attr_span))
     }
 }
 
@@ -30,34 +29,11 @@ impl<S: Stage> SingleAttributeParser<S> for RustcLayoutScalarValidRangeEnd {
     const TEMPLATE: AttributeTemplate = template!(List: "end");
 
     fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
-        parse_rustc_layout_scalar_valid_range(cx, args)
-            .map(|n| AttributeKind::RustcLayoutScalarValidRangeEnd(n, cx.attr_span))
+        parse_single_integer(cx, args)
+            .map(|n| AttributeKind::RustcLayoutScalarValidRangeEnd(Box::new(n), cx.attr_span))
     }
 }
 
-fn parse_rustc_layout_scalar_valid_range<S: Stage>(
-    cx: &mut AcceptContext<'_, '_, S>,
-    args: &ArgParser<'_>,
-) -> Option<Box<u128>> {
-    let Some(list) = args.list() else {
-        cx.expected_list(cx.attr_span);
-        return None;
-    };
-    let Some(single) = list.single() else {
-        cx.expected_single_argument(list.span);
-        return None;
-    };
-    let Some(lit) = single.lit() else {
-        cx.expected_integer_literal(single.span());
-        return None;
-    };
-    let LitKind::Int(num, _ty) = lit.kind else {
-        cx.expected_integer_literal(single.span());
-        return None;
-    };
-    Some(Box::new(num.0))
-}
-
 pub(crate) struct RustcObjectLifetimeDefaultParser;
 
 impl<S: Stage> SingleAttributeParser<S> for RustcObjectLifetimeDefaultParser {
diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs
index cc10bb3098a..854212f6e5d 100644
--- a/compiler/rustc_attr_parsing/src/context.rs
+++ b/compiler/rustc_attr_parsing/src/context.rs
@@ -5,7 +5,7 @@ use std::ops::{Deref, DerefMut};
 use std::sync::LazyLock;
 
 use private::Sealed;
-use rustc_ast::{self as ast, MetaItemLit, NodeId};
+use rustc_ast::{self as ast, LitKind, MetaItemLit, NodeId};
 use rustc_attr_data_structures::AttributeKind;
 use rustc_attr_data_structures::lints::{AttributeLint, AttributeLintKind};
 use rustc_errors::{DiagCtxtHandle, Diagnostic};
@@ -24,8 +24,8 @@ use crate::attributes::deprecation::DeprecationParser;
 use crate::attributes::dummy::DummyParser;
 use crate::attributes::inline::{InlineParser, RustcForceInlineParser};
 use crate::attributes::link_attrs::{
-    ExportStableParser, FfiConstParser, FfiPureParser, LinkNameParser, LinkSectionParser,
-    StdInternalSymbolParser,
+    ExportStableParser, FfiConstParser, FfiPureParser, LinkNameParser, LinkOrdinalParser,
+    LinkSectionParser, StdInternalSymbolParser,
 };
 use crate::attributes::lint_helpers::{AsPtrParser, PassByValueParser, PubTransparentParser};
 use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser};
@@ -141,6 +141,7 @@ attribute_parsers!(
         Single<IgnoreParser>,
         Single<InlineParser>,
         Single<LinkNameParser>,
+        Single<LinkOrdinalParser>,
         Single<LinkSectionParser>,
         Single<MustUseParser>,
         Single<OptimizeParser>,
@@ -767,3 +768,32 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
         }
     }
 }
+
+/// Parse a single integer.
+///
+/// Used by attributes that take a single integer as argument, such as
+/// `#[link_ordinal]` and `#[rustc_layout_scalar_valid_range_start]`.
+/// `cx` is the context given to the attribute.
+/// `args` is the parser for the attribute arguments.
+pub(crate) fn parse_single_integer<S: Stage>(
+    cx: &mut AcceptContext<'_, '_, S>,
+    args: &ArgParser<'_>,
+) -> Option<u128> {
+    let Some(list) = args.list() else {
+        cx.expected_list(cx.attr_span);
+        return None;
+    };
+    let Some(single) = list.single() else {
+        cx.expected_single_argument(list.span);
+        return None;
+    };
+    let Some(lit) = single.lit() else {
+        cx.expected_integer_literal(single.span());
+        return None;
+    };
+    let LitKind::Int(num, _ty) = lit.kind else {
+        cx.expected_integer_literal(single.span());
+        return None;
+    };
+    Some(num.0)
+}
diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs
index 28f6786f37f..8a240639d75 100644
--- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs
+++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs
@@ -514,6 +514,15 @@ pub(crate) struct NakedFunctionIncompatibleAttribute {
     pub attr: String,
 }
 
+#[derive(Diagnostic)]
+#[diag(attr_parsing_link_ordinal_out_of_range)]
+#[note]
+pub(crate) struct LinkOrdinalOutOfRange {
+    #[primary_span]
+    pub span: Span,
+    pub ordinal: u128,
+}
+
 pub(crate) enum AttributeParseErrorReason {
     ExpectedNoArgs,
     ExpectedStringLiteral { byte_string: Option<Span> },
diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl
index 63e9005da45..c7bd6ffd1f2 100644
--- a/compiler/rustc_codegen_ssa/messages.ftl
+++ b/compiler/rustc_codegen_ssa/messages.ftl
@@ -80,9 +80,6 @@ codegen_ssa_ignoring_emit_path = ignoring emit path because multiple .{$extensio
 
 codegen_ssa_ignoring_output = ignoring -o because multiple .{$extension} files were produced
 
-codegen_ssa_illegal_link_ordinal_format = illegal ordinal format in `link_ordinal`
-    .note = an unsuffixed integer value, e.g., `1`, is expected
-
 codegen_ssa_incorrect_cgu_reuse_type =
     CGU-reuse for `{$cgu_user_name}` is `{$actual_reuse}` but should be {$at_least ->
     [one] {"at least "}
@@ -93,9 +90,6 @@ codegen_ssa_insufficient_vs_code_product = VS Code is a different product, and i
 
 codegen_ssa_invalid_instruction_set = invalid instruction set specified
 
-codegen_ssa_invalid_link_ordinal_nargs = incorrect number of arguments to `#[link_ordinal]`
-    .note = the attribute requires exactly one argument
-
 codegen_ssa_invalid_literal_value = invalid literal value
     .label = value must be an integer between `0` and `255`
 
diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
index 85d01d4f938..dd49db26689 100644
--- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
+++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
@@ -116,6 +116,10 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                 AttributeKind::Naked(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED,
                 AttributeKind::Align { align, .. } => codegen_fn_attrs.alignment = Some(*align),
                 AttributeKind::LinkName { name, .. } => codegen_fn_attrs.link_name = Some(*name),
+                AttributeKind::LinkOrdinal { ordinal, span } => {
+                    codegen_fn_attrs.link_ordinal = Some(*ordinal);
+                    link_ordinal_span = Some(*span);
+                }
                 AttributeKind::LinkSection { name, .. } => {
                     codegen_fn_attrs.link_section = Some(*name)
                 }
@@ -250,12 +254,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                     }
                 }
             }
-            sym::link_ordinal => {
-                link_ordinal_span = Some(attr.span());
-                if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) {
-                    codegen_fn_attrs.link_ordinal = ordinal;
-                }
-            }
             sym::no_sanitize => {
                 no_sanitize_span = Some(attr.span());
                 if let Some(list) = attr.meta_item_list() {
@@ -568,45 +566,6 @@ fn inherited_align<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Option<Align> {
     tcx.codegen_fn_attrs(opt_trait_item(tcx, def_id)?).alignment
 }
 
-fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &hir::Attribute) -> Option<u16> {
-    use rustc_ast::{LitIntType, LitKind, MetaItemLit};
-    let meta_item_list = attr.meta_item_list()?;
-    let [sole_meta_list] = &meta_item_list[..] else {
-        tcx.dcx().emit_err(errors::InvalidLinkOrdinalNargs { span: attr.span() });
-        return None;
-    };
-    if let Some(MetaItemLit { kind: LitKind::Int(ordinal, LitIntType::Unsuffixed), .. }) =
-        sole_meta_list.lit()
-    {
-        // According to the table at
-        // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-header, the
-        // ordinal must fit into 16 bits. Similarly, the Ordinal field in COFFShortExport (defined
-        // in llvm/include/llvm/Object/COFFImportFile.h), which we use to communicate import
-        // information to LLVM for `#[link(kind = "raw-dylib"_])`, is also defined to be uint16_t.
-        //
-        // FIXME: should we allow an ordinal of 0?  The MSVC toolchain has inconsistent support for
-        // this: both LINK.EXE and LIB.EXE signal errors and abort when given a .DEF file that
-        // specifies a zero ordinal. However, llvm-dlltool is perfectly happy to generate an import
-        // library for such a .DEF file, and MSVC's LINK.EXE is also perfectly happy to consume an
-        // import library produced by LLVM with an ordinal of 0, and it generates an .EXE.  (I
-        // don't know yet if the resulting EXE runs, as I haven't yet built the necessary DLL --
-        // see earlier comment about LINK.EXE failing.)
-        if *ordinal <= u16::MAX as u128 {
-            Some(ordinal.get() as u16)
-        } else {
-            let msg = format!("ordinal value in `link_ordinal` is too large: `{ordinal}`");
-            tcx.dcx()
-                .struct_span_err(attr.span(), msg)
-                .with_note("the value may not exceed `u16::MAX`")
-                .emit();
-            None
-        }
-    } else {
-        tcx.dcx().emit_err(errors::InvalidLinkOrdinalFormat { span: attr.span() });
-        None
-    }
-}
-
 fn check_link_name_xor_ordinal(
     tcx: TyCtxt<'_>,
     codegen_fn_attrs: &CodegenFnAttrs,
diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs
index e042fe1f819..9040915b6af 100644
--- a/compiler/rustc_codegen_ssa/src/errors.rs
+++ b/compiler/rustc_codegen_ssa/src/errors.rs
@@ -1109,22 +1109,6 @@ pub(crate) struct InvalidNoSanitize {
 }
 
 #[derive(Diagnostic)]
-#[diag(codegen_ssa_invalid_link_ordinal_nargs)]
-#[note]
-pub(crate) struct InvalidLinkOrdinalNargs {
-    #[primary_span]
-    pub span: Span,
-}
-
-#[derive(Diagnostic)]
-#[diag(codegen_ssa_illegal_link_ordinal_format)]
-#[note]
-pub(crate) struct InvalidLinkOrdinalFormat {
-    #[primary_span]
-    pub span: Span,
-}
-
-#[derive(Diagnostic)]
 #[diag(codegen_ssa_target_feature_safe_trait)]
 pub(crate) struct TargetFeatureSafeTrait {
     #[primary_span]
diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs
index f10d71f4c65..4d276f814ef 100644
--- a/compiler/rustc_metadata/src/native_libs.rs
+++ b/compiler/rustc_metadata/src/native_libs.rs
@@ -3,6 +3,7 @@ use std::path::{Path, PathBuf};
 
 use rustc_abi::ExternAbi;
 use rustc_ast::CRATE_NODE_ID;
+use rustc_attr_data_structures::{AttributeKind, find_attr};
 use rustc_attr_parsing as attr;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_middle::query::LocalCrate;
@@ -496,14 +497,9 @@ impl<'tcx> Collector<'tcx> {
                 }
                 _ => {
                     for &child_item in foreign_items {
-                        if self.tcx.def_kind(child_item).has_codegen_attrs()
-                            && self.tcx.codegen_fn_attrs(child_item).link_ordinal.is_some()
+                        if let Some(span) = find_attr!(self.tcx.get_all_attrs(child_item), AttributeKind::LinkOrdinal {span, ..} => *span)
                         {
-                            let link_ordinal_attr =
-                                self.tcx.get_attr(child_item, sym::link_ordinal).unwrap();
-                            sess.dcx().emit_err(errors::LinkOrdinalRawDylib {
-                                span: link_ordinal_attr.span(),
-                            });
+                            sess.dcx().emit_err(errors::LinkOrdinalRawDylib { span });
                         }
                     }
 
diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs
index bb5c1e0e653..03d4b64b823 100644
--- a/compiler/rustc_parse/src/validate_attr.rs
+++ b/compiler/rustc_parse/src/validate_attr.rs
@@ -310,6 +310,7 @@ pub fn check_builtin_meta_item(
                 | sym::must_use
                 | sym::track_caller
                 | sym::link_name
+                | sym::link_ordinal
                 | sym::export_name
                 | sym::rustc_macro_transparency
                 | sym::link_section
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 2c95fead9e8..0d8973bce4b 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -248,6 +248,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 Attribute::Parsed(AttributeKind::LinkName { span: attr_span, name }) => {
                     self.check_link_name(hir_id, *attr_span, *name, span, target)
                 }
+                Attribute::Parsed(AttributeKind::LinkOrdinal { span: attr_span, .. }) => {
+                    self.check_link_ordinal(*attr_span, span, target)
+                }
                 Attribute::Parsed(AttributeKind::MayDangle(attr_span)) => {
                     self.check_may_dangle(hir_id, *attr_span)
                 }
@@ -325,7 +328,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                         [sym::rustc_has_incoherent_inherent_impls, ..] => {
                             self.check_has_incoherent_inherent_impls(attr, span, target)
                         }
-                        [sym::link_ordinal, ..] => self.check_link_ordinal(attr, span, target),
+                        [sym::ffi_pure, ..] => self.check_ffi_pure(attr.span(), attrs, target),
+                        [sym::ffi_const, ..] => self.check_ffi_const(attr.span(), target),
                         [sym::link, ..] => self.check_link(hir_id, attr, span, target),
                         [sym::macro_use, ..] | [sym::macro_escape, ..] => {
                             self.check_macro_use(hir_id, attr, target)
@@ -2248,11 +2252,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
         }
     }
 
-    fn check_link_ordinal(&self, attr: &Attribute, _span: Span, target: Target) {
+    fn check_link_ordinal(&self, attr_span: Span, _span: Span, target: Target) {
         match target {
             Target::ForeignFn | Target::ForeignStatic => {}
             _ => {
-                self.dcx().emit_err(errors::LinkOrdinal { attr_span: attr.span() });
+                self.dcx().emit_err(errors::LinkOrdinal { attr_span });
             }
         }
     }
diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr
index be529df3a49..f2696671002 100644
--- a/tests/ui/attributes/malformed-attrs.stderr
+++ b/tests/ui/attributes/malformed-attrs.stderr
@@ -116,12 +116,6 @@ error: malformed `cfi_encoding` attribute input
 LL | #[cfi_encoding]
    | ^^^^^^^^^^^^^^^ help: must be of the form: `#[cfi_encoding = "encoding"]`
 
-error: malformed `link_ordinal` attribute input
-  --> $DIR/malformed-attrs.rs:167:5
-   |
-LL |     #[link_ordinal]
-   |     ^^^^^^^^^^^^^^^ help: must be of the form: `#[link_ordinal(ordinal)]`
-
 error: malformed `linkage` attribute input
   --> $DIR/malformed-attrs.rs:173:5
    |
@@ -537,6 +531,15 @@ LL |     #[unsafe(ffi_pure = 1)]
    |     |                 didn't expect any arguments here
    |     help: must be of the form: `#[ffi_pure]`
 
+error[E0539]: malformed `link_ordinal` attribute input
+  --> $DIR/malformed-attrs.rs:167:5
+   |
+LL |     #[link_ordinal]
+   |     ^^^^^^^^^^^^^^^
+   |     |
+   |     expected this to be a list
+   |     help: must be of the form: `#[link_ordinal(ordinal)]`
+
 error[E0565]: malformed `ffi_const` attribute input
   --> $DIR/malformed-attrs.rs:171:5
    |
diff --git a/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-invalid-format.rs b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-invalid-format.rs
index 9b7e8d70743..87b4999c5f9 100644
--- a/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-invalid-format.rs
+++ b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-invalid-format.rs
@@ -1,10 +1,10 @@
 #[link(name = "foo")]
 extern "C" {
     #[link_ordinal("JustMonika")]
-    //~^ ERROR illegal ordinal format in `link_ordinal`
+    //~^ ERROR malformed `link_ordinal` attribute input
     fn foo();
     #[link_ordinal("JustMonika")]
-    //~^ ERROR illegal ordinal format in `link_ordinal`
+    //~^ ERROR malformed `link_ordinal` attribute input
     static mut imported_variable: i32;
 }
 
diff --git a/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-invalid-format.stderr b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-invalid-format.stderr
index 6341e57a0be..ffae30aabcc 100644
--- a/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-invalid-format.stderr
+++ b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-invalid-format.stderr
@@ -1,18 +1,21 @@
-error: illegal ordinal format in `link_ordinal`
+error[E0539]: malformed `link_ordinal` attribute input
   --> $DIR/link-ordinal-invalid-format.rs:3:5
    |
 LL |     #[link_ordinal("JustMonika")]
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: an unsuffixed integer value, e.g., `1`, is expected
+   |     ^^^^^^^^^^^^^^^------------^^
+   |     |              |
+   |     |              expected an integer literal here
+   |     help: must be of the form: `#[link_ordinal(ordinal)]`
 
-error: illegal ordinal format in `link_ordinal`
+error[E0539]: malformed `link_ordinal` attribute input
   --> $DIR/link-ordinal-invalid-format.rs:6:5
    |
 LL |     #[link_ordinal("JustMonika")]
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: an unsuffixed integer value, e.g., `1`, is expected
+   |     ^^^^^^^^^^^^^^^------------^^
+   |     |              |
+   |     |              expected an integer literal here
+   |     help: must be of the form: `#[link_ordinal(ordinal)]`
 
 error: aborting due to 2 previous errors
 
+For more information about this error, try `rustc --explain E0539`.
diff --git a/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-missing-argument.rs b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-missing-argument.rs
index 6b8cd49566d..2a8b9ebacf7 100644
--- a/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-missing-argument.rs
+++ b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-missing-argument.rs
@@ -1,10 +1,12 @@
 #[link(name = "foo")]
 extern "C" {
     #[link_ordinal()]
-    //~^ ERROR incorrect number of arguments to `#[link_ordinal]`
+    //~^ ERROR malformed `link_ordinal` attribute input
+    //~| NOTE  expected a single argument
     fn foo();
     #[link_ordinal()]
-    //~^ ERROR incorrect number of arguments to `#[link_ordinal]`
+    //~^ ERROR malformed `link_ordinal` attribute input
+    //~| NOTE  expected a single argument
     static mut imported_variable: i32;
 }
 
diff --git a/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-missing-argument.stderr b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-missing-argument.stderr
index 1b04bb228e7..c6b8a18d03a 100644
--- a/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-missing-argument.stderr
+++ b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-missing-argument.stderr
@@ -1,18 +1,21 @@
-error: incorrect number of arguments to `#[link_ordinal]`
+error[E0805]: malformed `link_ordinal` attribute input
   --> $DIR/link-ordinal-missing-argument.rs:3:5
    |
 LL |     #[link_ordinal()]
-   |     ^^^^^^^^^^^^^^^^^
-   |
-   = note: the attribute requires exactly one argument
+   |     ^^^^^^^^^^^^^^--^
+   |     |             |
+   |     |             expected a single argument here
+   |     help: must be of the form: `#[link_ordinal(ordinal)]`
 
-error: incorrect number of arguments to `#[link_ordinal]`
-  --> $DIR/link-ordinal-missing-argument.rs:6:5
+error[E0805]: malformed `link_ordinal` attribute input
+  --> $DIR/link-ordinal-missing-argument.rs:7:5
    |
 LL |     #[link_ordinal()]
-   |     ^^^^^^^^^^^^^^^^^
-   |
-   = note: the attribute requires exactly one argument
+   |     ^^^^^^^^^^^^^^--^
+   |     |             |
+   |     |             expected a single argument here
+   |     help: must be of the form: `#[link_ordinal(ordinal)]`
 
 error: aborting due to 2 previous errors
 
+For more information about this error, try `rustc --explain E0805`.
diff --git a/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-too-many-arguments.rs b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-too-many-arguments.rs
index 9988115fd8b..ddf9583352f 100644
--- a/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-too-many-arguments.rs
+++ b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-too-many-arguments.rs
@@ -1,10 +1,12 @@
 #[link(name = "foo")]
 extern "C" {
     #[link_ordinal(3, 4)]
-    //~^ ERROR incorrect number of arguments to `#[link_ordinal]`
+    //~^ ERROR malformed `link_ordinal` attribute input
+    //~| NOTE  expected a single argument
     fn foo();
     #[link_ordinal(3, 4)]
-    //~^ ERROR incorrect number of arguments to `#[link_ordinal]`
+    //~^ ERROR malformed `link_ordinal` attribute input
+    //~| NOTE  expected a single argument
     static mut imported_variable: i32;
 }
 
diff --git a/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-too-many-arguments.stderr b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-too-many-arguments.stderr
index d5ce8aff34f..7d63304f598 100644
--- a/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-too-many-arguments.stderr
+++ b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-too-many-arguments.stderr
@@ -1,18 +1,21 @@
-error: incorrect number of arguments to `#[link_ordinal]`
+error[E0805]: malformed `link_ordinal` attribute input
   --> $DIR/link-ordinal-too-many-arguments.rs:3:5
    |
 LL |     #[link_ordinal(3, 4)]
-   |     ^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: the attribute requires exactly one argument
+   |     ^^^^^^^^^^^^^^------^
+   |     |             |
+   |     |             expected a single argument here
+   |     help: must be of the form: `#[link_ordinal(ordinal)]`
 
-error: incorrect number of arguments to `#[link_ordinal]`
-  --> $DIR/link-ordinal-too-many-arguments.rs:6:5
+error[E0805]: malformed `link_ordinal` attribute input
+  --> $DIR/link-ordinal-too-many-arguments.rs:7:5
    |
 LL |     #[link_ordinal(3, 4)]
-   |     ^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: the attribute requires exactly one argument
+   |     ^^^^^^^^^^^^^^------^
+   |     |             |
+   |     |             expected a single argument here
+   |     help: must be of the form: `#[link_ordinal(ordinal)]`
 
 error: aborting due to 2 previous errors
 
+For more information about this error, try `rustc --explain E0805`.