diff options
Diffstat (limited to 'compiler/rustc_attr_parsing')
| -rw-r--r-- | compiler/rustc_attr_parsing/Cargo.toml | 1 | ||||
| -rw-r--r-- | compiler/rustc_attr_parsing/messages.ftl | 52 | ||||
| -rw-r--r-- | compiler/rustc_attr_parsing/src/attributes/cfg.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_attr_parsing/src/attributes/link_attrs.rs | 419 | ||||
| -rw-r--r-- | compiler/rustc_attr_parsing/src/context.rs | 3 | ||||
| -rw-r--r-- | compiler/rustc_attr_parsing/src/lib.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_attr_parsing/src/session_diagnostics.rs | 94 |
7 files changed, 568 insertions, 4 deletions
diff --git a/compiler/rustc_attr_parsing/Cargo.toml b/compiler/rustc_attr_parsing/Cargo.toml index ba95b630b8c..8bfde43fd33 100644 --- a/compiler/rustc_attr_parsing/Cargo.toml +++ b/compiler/rustc_attr_parsing/Cargo.toml @@ -17,5 +17,6 @@ rustc_macros = { path = "../rustc_macros" } rustc_parse = { path = "../rustc_parse" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } +rustc_target = { path = "../rustc_target" } thin-vec.workspace = true # tidy-alphabetical-end diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index 8f24b51f1d9..b8a748563d5 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -195,3 +195,55 @@ attr_parsing_invalid_meta_item = expected a literal (`1u8`, `1.0f32`, `"string"` attr_parsing_suffixed_literal_in_attribute = suffixed literals are not allowed in attributes .help = instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.) + +attr_parsing_as_needed_compatibility = + linking modifier `as-needed` is only compatible with `dylib` and `framework` linking kinds + +attr_parsing_bundle_needs_static = + linking modifier `bundle` is only compatible with `static` linking kind + +attr_parsing_empty_link_name = + link name must not be empty + .label = empty link name + +attr_parsing_import_name_type_raw = + import name type can only be used with link kind `raw-dylib` + +attr_parsing_import_name_type_x86 = + import name type is only supported on x86 + +attr_parsing_incompatible_wasm_link = + `wasm_import_module` is incompatible with other arguments in `#[link]` attributes + +attr_parsing_invalid_link_modifier = + invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed + +attr_parsing_link_arg_unstable = + link kind `link-arg` is unstable + +attr_parsing_link_cfg_unstable = + link cfg is unstable + +attr_parsing_link_framework_apple = + link kind `framework` is only supported on Apple targets + +attr_parsing_link_requires_name = + `#[link]` attribute requires a `name = "string"` argument + .label = missing `name` argument + +attr_parsing_multiple_modifiers = + multiple `{$modifier}` modifiers in a single `modifiers` argument + +attr_parsing_multiple_renamings = + multiple renamings were specified for library `{$lib_name}` +attr_parsing_raw_dylib_no_nul = + link name must not contain NUL characters if link kind is `raw-dylib` + +attr_parsing_raw_dylib_elf_unstable = + link kind `raw-dylib` is unstable on ELF platforms + +attr_parsing_raw_dylib_only_windows = + link kind `raw-dylib` is only supported on Windows targets + +attr_parsing_whole_archive_needs_static = + linking modifier `whole-archive` is only compatible with `static` linking kind diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index 695ee666476..70855611079 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -36,7 +36,7 @@ pub fn parse_cfg_attr<'c, S: Stage>( parse_cfg_entry(cx, single) } -fn parse_cfg_entry<S: Stage>( +pub(crate) fn parse_cfg_entry<S: Stage>( cx: &mut AcceptContext<'_, '_, S>, item: &MetaItemOrLitParser<'_>, ) -> Option<CfgEntry> { diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index 5e4551ccd79..d4942e56f42 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -1,9 +1,21 @@ +use rustc_feature::Features; use rustc_hir::attrs::AttributeKind::{LinkName, LinkOrdinal, LinkSection}; -use rustc_hir::attrs::Linkage; +use rustc_hir::attrs::*; +use rustc_session::Session; +use rustc_session::parse::feature_err; +use rustc_span::kw; +use rustc_target::spec::BinaryFormat; use super::prelude::*; use super::util::parse_single_integer; -use crate::session_diagnostics::{LinkOrdinalOutOfRange, NullOnLinkSection}; +use crate::attributes::cfg::parse_cfg_entry; +use crate::fluent_generated; +use crate::session_diagnostics::{ + AsNeededCompatibility, BundleNeedsStatic, EmptyLinkName, ImportNameTypeRaw, ImportNameTypeX86, + IncompatibleWasmLink, InvalidLinkModifier, LinkFrameworkApple, LinkOrdinalOutOfRange, + LinkRequiresName, MultipleModifiers, NullOnLinkSection, RawDylibNoNul, RawDylibOnlyWindows, + WholeArchiveNeedsStatic, +}; pub(crate) struct LinkNameParser; @@ -34,6 +46,409 @@ impl<S: Stage> SingleAttributeParser<S> for LinkNameParser { } } +pub(crate) struct LinkParser; + +impl<S: Stage> CombineAttributeParser<S> for LinkParser { + type Item = LinkEntry; + const PATH: &[Symbol] = &[sym::link]; + const CONVERT: ConvertFn<Self::Item> = AttributeKind::Link; + const TEMPLATE: AttributeTemplate = template!(List: &[ + r#"name = "...""#, + r#"name = "...", kind = "dylib|static|...""#, + r#"name = "...", wasm_import_module = "...""#, + r#"name = "...", import_name_type = "decorated|noprefix|undecorated""#, + r#"name = "...", kind = "dylib|static|...", wasm_import_module = "...", import_name_type = "decorated|noprefix|undecorated""#, + ], "https://doc.rust-lang.org/reference/items/external-blocks.html#the-link-attribute"); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); //FIXME Still checked fully in `check_attr.rs` + + fn extend<'c>( + cx: &'c mut AcceptContext<'_, '_, S>, + args: &'c ArgParser<'_>, + ) -> impl IntoIterator<Item = Self::Item> + 'c { + let mut result = None; + let Some(items) = args.list() else { + cx.expected_list(cx.attr_span); + return result; + }; + + let sess = cx.sess(); + let features = cx.features(); + + let mut name = None; + let mut kind = None; + let mut modifiers = None; + let mut cfg = None; + let mut wasm_import_module = None; + let mut import_name_type = None; + for item in items.mixed() { + let Some(item) = item.meta_item() else { + cx.unexpected_literal(item.span()); + continue; + }; + + let cont = match item.path().word().map(|ident| ident.name) { + Some(sym::name) => Self::parse_link_name(item, &mut name, cx), + Some(sym::kind) => Self::parse_link_kind(item, &mut kind, cx, sess, features), + Some(sym::modifiers) => Self::parse_link_modifiers(item, &mut modifiers, cx), + Some(sym::cfg) => Self::parse_link_cfg(item, &mut cfg, cx, sess, features), + Some(sym::wasm_import_module) => { + Self::parse_link_wasm_import_module(item, &mut wasm_import_module, cx) + } + Some(sym::import_name_type) => { + Self::parse_link_import_name_type(item, &mut import_name_type, cx) + } + _ => { + cx.expected_specific_argument_strings( + item.span(), + &[ + sym::name, + sym::kind, + sym::modifiers, + sym::cfg, + sym::wasm_import_module, + sym::import_name_type, + ], + ); + true + } + }; + if !cont { + return result; + } + } + + // Do this outside the above loop so we don't depend on modifiers coming after kinds + let mut verbatim = None; + if let Some((modifiers, span)) = modifiers { + for modifier in modifiers.as_str().split(',') { + let (modifier, value): (Symbol, bool) = match modifier.strip_prefix(&['+', '-']) { + Some(m) => (Symbol::intern(m), modifier.starts_with('+')), + None => { + cx.emit_err(InvalidLinkModifier { span }); + continue; + } + }; + + macro report_unstable_modifier($feature: ident) { + if !features.$feature() { + // FIXME: make this translatable + #[expect(rustc::untranslatable_diagnostic)] + feature_err( + sess, + sym::$feature, + span, + format!("linking modifier `{modifier}` is unstable"), + ) + .emit(); + } + } + let assign_modifier = |dst: &mut Option<bool>| { + if dst.is_some() { + cx.emit_err(MultipleModifiers { span, modifier }); + } else { + *dst = Some(value); + } + }; + match (modifier, &mut kind) { + (sym::bundle, Some(NativeLibKind::Static { bundle, .. })) => { + assign_modifier(bundle) + } + (sym::bundle, _) => { + cx.emit_err(BundleNeedsStatic { span }); + } + + (sym::verbatim, _) => assign_modifier(&mut verbatim), + + ( + sym::whole_dash_archive, + Some(NativeLibKind::Static { whole_archive, .. }), + ) => assign_modifier(whole_archive), + (sym::whole_dash_archive, _) => { + cx.emit_err(WholeArchiveNeedsStatic { span }); + } + + (sym::as_dash_needed, Some(NativeLibKind::Dylib { as_needed })) + | (sym::as_dash_needed, Some(NativeLibKind::Framework { as_needed })) => { + report_unstable_modifier!(native_link_modifiers_as_needed); + assign_modifier(as_needed) + } + (sym::as_dash_needed, _) => { + cx.emit_err(AsNeededCompatibility { span }); + } + + _ => { + cx.expected_specific_argument_strings( + span, + &[ + sym::bundle, + sym::verbatim, + sym::whole_dash_archive, + sym::as_dash_needed, + ], + ); + } + } + } + } + + if let Some((_, span)) = wasm_import_module { + if name.is_some() || kind.is_some() || modifiers.is_some() || cfg.is_some() { + cx.emit_err(IncompatibleWasmLink { span }); + } + } + + if wasm_import_module.is_some() { + (name, kind) = (wasm_import_module, Some(NativeLibKind::WasmImportModule)); + } + let Some((name, name_span)) = name else { + cx.emit_err(LinkRequiresName { span: cx.attr_span }); + return result; + }; + + // Do this outside of the loop so that `import_name_type` can be specified before `kind`. + if let Some((_, span)) = import_name_type { + if kind != Some(NativeLibKind::RawDylib) { + cx.emit_err(ImportNameTypeRaw { span }); + } + } + + if let Some(NativeLibKind::RawDylib) = kind + && name.as_str().contains('\0') + { + cx.emit_err(RawDylibNoNul { span: name_span }); + } + + result = Some(LinkEntry { + span: cx.attr_span, + kind: kind.unwrap_or(NativeLibKind::Unspecified), + name, + cfg, + verbatim, + import_name_type, + }); + result + } +} + +impl LinkParser { + fn parse_link_name<S: Stage>( + item: &MetaItemParser<'_>, + name: &mut Option<(Symbol, Span)>, + cx: &mut AcceptContext<'_, '_, S>, + ) -> bool { + if name.is_some() { + cx.duplicate_key(item.span(), sym::name); + return true; + } + let Some(nv) = item.args().name_value() else { + cx.expected_name_value(item.span(), Some(sym::name)); + return false; + }; + let Some(link_name) = nv.value_as_str() else { + cx.expected_name_value(item.span(), Some(sym::name)); + return false; + }; + + if link_name.is_empty() { + cx.emit_err(EmptyLinkName { span: nv.value_span }); + } + *name = Some((link_name, nv.value_span)); + true + } + + fn parse_link_kind<S: Stage>( + item: &MetaItemParser<'_>, + kind: &mut Option<NativeLibKind>, + cx: &mut AcceptContext<'_, '_, S>, + sess: &Session, + features: &Features, + ) -> bool { + if kind.is_some() { + cx.duplicate_key(item.span(), sym::kind); + return true; + } + let Some(nv) = item.args().name_value() else { + cx.expected_name_value(item.span(), Some(sym::kind)); + return true; + }; + let Some(link_kind) = nv.value_as_str() else { + cx.expected_name_value(item.span(), Some(sym::kind)); + return true; + }; + + let link_kind = match link_kind { + kw::Static => NativeLibKind::Static { bundle: None, whole_archive: None }, + sym::dylib => NativeLibKind::Dylib { as_needed: None }, + sym::framework => { + if !sess.target.is_like_darwin { + cx.emit_err(LinkFrameworkApple { span: nv.value_span }); + } + NativeLibKind::Framework { as_needed: None } + } + sym::raw_dash_dylib => { + if sess.target.is_like_windows { + // raw-dylib is stable and working on Windows + } else if sess.target.binary_format == BinaryFormat::Elf && features.raw_dylib_elf() + { + // raw-dylib is unstable on ELF, but the user opted in + } else if sess.target.binary_format == BinaryFormat::Elf && sess.is_nightly_build() + { + feature_err( + sess, + sym::raw_dylib_elf, + nv.value_span, + fluent_generated::attr_parsing_raw_dylib_elf_unstable, + ) + .emit(); + } else { + cx.emit_err(RawDylibOnlyWindows { span: nv.value_span }); + } + + NativeLibKind::RawDylib + } + sym::link_dash_arg => { + if !features.link_arg_attribute() { + feature_err( + sess, + sym::link_arg_attribute, + nv.value_span, + fluent_generated::attr_parsing_link_arg_unstable, + ) + .emit(); + } + NativeLibKind::LinkArg + } + _kind => { + cx.expected_specific_argument_strings( + nv.value_span, + &[ + kw::Static, + sym::dylib, + sym::framework, + sym::raw_dash_dylib, + sym::link_dash_arg, + ], + ); + return true; + } + }; + *kind = Some(link_kind); + true + } + + fn parse_link_modifiers<S: Stage>( + item: &MetaItemParser<'_>, + modifiers: &mut Option<(Symbol, Span)>, + cx: &mut AcceptContext<'_, '_, S>, + ) -> bool { + if modifiers.is_some() { + cx.duplicate_key(item.span(), sym::modifiers); + return true; + } + let Some(nv) = item.args().name_value() else { + cx.expected_name_value(item.span(), Some(sym::modifiers)); + return true; + }; + let Some(link_modifiers) = nv.value_as_str() else { + cx.expected_name_value(item.span(), Some(sym::modifiers)); + return true; + }; + *modifiers = Some((link_modifiers, nv.value_span)); + true + } + + fn parse_link_cfg<S: Stage>( + item: &MetaItemParser<'_>, + cfg: &mut Option<CfgEntry>, + cx: &mut AcceptContext<'_, '_, S>, + sess: &Session, + features: &Features, + ) -> bool { + if cfg.is_some() { + cx.duplicate_key(item.span(), sym::cfg); + return true; + } + let Some(link_cfg) = item.args().list() else { + cx.expected_list(item.span()); + return true; + }; + let Some(link_cfg) = link_cfg.single() else { + cx.expected_single_argument(item.span()); + return true; + }; + if !features.link_cfg() { + feature_err( + sess, + sym::link_cfg, + item.span(), + fluent_generated::attr_parsing_link_cfg_unstable, + ) + .emit(); + } + *cfg = parse_cfg_entry(cx, link_cfg); + true + } + + fn parse_link_wasm_import_module<S: Stage>( + item: &MetaItemParser<'_>, + wasm_import_module: &mut Option<(Symbol, Span)>, + cx: &mut AcceptContext<'_, '_, S>, + ) -> bool { + if wasm_import_module.is_some() { + cx.duplicate_key(item.span(), sym::wasm_import_module); + return true; + } + let Some(nv) = item.args().name_value() else { + cx.expected_name_value(item.span(), Some(sym::wasm_import_module)); + return true; + }; + let Some(link_wasm_import_module) = nv.value_as_str() else { + cx.expected_name_value(item.span(), Some(sym::wasm_import_module)); + return true; + }; + *wasm_import_module = Some((link_wasm_import_module, item.span())); + true + } + + fn parse_link_import_name_type<S: Stage>( + item: &MetaItemParser<'_>, + import_name_type: &mut Option<(PeImportNameType, Span)>, + cx: &mut AcceptContext<'_, '_, S>, + ) -> bool { + if import_name_type.is_some() { + cx.duplicate_key(item.span(), sym::import_name_type); + return true; + } + let Some(nv) = item.args().name_value() else { + cx.expected_name_value(item.span(), Some(sym::import_name_type)); + return true; + }; + let Some(link_import_name_type) = nv.value_as_str() else { + cx.expected_name_value(item.span(), Some(sym::import_name_type)); + return true; + }; + if cx.sess().target.arch != "x86" { + cx.emit_err(ImportNameTypeX86 { span: item.span() }); + return true; + } + + let link_import_name_type = match link_import_name_type { + sym::decorated => PeImportNameType::Decorated, + sym::noprefix => PeImportNameType::NoPrefix, + sym::undecorated => PeImportNameType::Undecorated, + _ => { + cx.expected_specific_argument_strings( + item.span(), + &[sym::decorated, sym::noprefix, sym::undecorated], + ); + return true; + } + }; + *import_name_type = Some((link_import_name_type, item.span())); + true + } +} + pub(crate) struct LinkSectionParser; impl<S: Stage> SingleAttributeParser<S> for LinkSectionParser { diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index b16ef7edd64..7f5b810f244 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -30,7 +30,7 @@ use crate::attributes::dummy::DummyParser; use crate::attributes::inline::{InlineParser, RustcForceInlineParser}; use crate::attributes::link_attrs::{ ExportStableParser, FfiConstParser, FfiPureParser, LinkNameParser, LinkOrdinalParser, - LinkSectionParser, LinkageParser, StdInternalSymbolParser, + LinkParser, LinkSectionParser, LinkageParser, StdInternalSymbolParser, }; use crate::attributes::lint_helpers::{ AsPtrParser, AutomaticallyDerivedParser, PassByValueParser, PubTransparentParser, @@ -162,6 +162,7 @@ attribute_parsers!( Combine<AllowConstFnUnstableParser>, Combine<AllowInternalUnstableParser>, Combine<ForceTargetFeatureParser>, + Combine<LinkParser>, Combine<ReprParser>, Combine<TargetFeatureParser>, Combine<UnstableFeatureBoundParser>, diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index 4dd908cdc40..f51cc8c4e8b 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -79,6 +79,7 @@ // tidy-alphabetical-start #![allow(internal_features)] #![doc(rust_logo)] +#![feature(decl_macro)] #![feature(rustdoc_internals)] #![recursion_limit = "256"] // tidy-alphabetical-end diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index a639b55e81f..a9dee23bf6a 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -836,3 +836,97 @@ pub(crate) struct InvalidAttrStyle { pub target_span: Option<Span>, pub target: Target, } + +#[derive(Diagnostic)] +#[diag(attr_parsing_empty_link_name, code = E0454)] +pub(crate) struct EmptyLinkName { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_link_framework_apple, code = E0455)] +pub(crate) struct LinkFrameworkApple { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_incompatible_wasm_link)] +pub(crate) struct IncompatibleWasmLink { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_link_requires_name, code = E0459)] +pub(crate) struct LinkRequiresName { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_raw_dylib_no_nul)] +pub(crate) struct RawDylibNoNul { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_raw_dylib_only_windows, code = E0455)] +pub(crate) struct RawDylibOnlyWindows { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_invalid_link_modifier)] +pub(crate) struct InvalidLinkModifier { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_multiple_modifiers)] +pub(crate) struct MultipleModifiers { + #[primary_span] + pub span: Span, + pub modifier: Symbol, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_import_name_type_x86)] +pub(crate) struct ImportNameTypeX86 { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_bundle_needs_static)] +pub(crate) struct BundleNeedsStatic { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_whole_archive_needs_static)] +pub(crate) struct WholeArchiveNeedsStatic { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_as_needed_compatibility)] +pub(crate) struct AsNeededCompatibility { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_import_name_type_raw)] +pub(crate) struct ImportNameTypeRaw { + #[primary_span] + pub span: Span, +} |
