use rustc_ast::LitKind; use rustc_ast::attr::AttributeExt; use rustc_feature::is_builtin_attr_name; use rustc_hir::RustcVersion; use rustc_span::{Symbol, sym}; use crate::context::{AcceptContext, Stage}; use crate::parser::ArgParser; /// Parse a rustc version number written inside string literal in an attribute, /// like appears in `since = "1.0.0"`. Suffixes like "-dev" and "-nightly" are /// not accepted in this position, unlike when parsing CFG_RELEASE. pub fn parse_version(s: Symbol) -> Option { let mut components = s.as_str().split('-'); let d = components.next()?; if components.next().is_some() { return None; } let mut digits = d.splitn(3, '.'); let major = digits.next()?.parse().ok()?; let minor = digits.next()?.parse().ok()?; let patch = digits.next().unwrap_or("0").parse().ok()?; Some(RustcVersion { major, minor, patch }) } pub fn is_builtin_attr(attr: &impl AttributeExt) -> bool { attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name)) } pub fn is_doc_alias_attrs_contain_symbol<'tcx, T: AttributeExt + 'tcx>( attrs: impl Iterator, symbol: Symbol, ) -> bool { let doc_attrs = attrs.filter(|attr| attr.has_name(sym::doc)); for attr in doc_attrs { let Some(values) = attr.meta_item_list() else { continue; }; let alias_values = values.iter().filter(|v| v.has_name(sym::alias)); for v in alias_values { if let Some(nested) = v.meta_item_list() { // #[doc(alias("foo", "bar"))] let mut iter = nested.iter().filter_map(|item| item.lit()).map(|item| item.symbol); if iter.any(|s| s == symbol) { return true; } } else if let Some(meta) = v.meta_item() && let Some(lit) = meta.name_value_literal() { // #[doc(alias = "foo")] if lit.symbol == symbol { return true; } } } } false } /// 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( cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>, ) -> Option { 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) }