rustc_attr_parsing/attributes/
util.rs

1use rustc_ast::LitKind;
2use rustc_ast::attr::AttributeExt;
3use rustc_feature::is_builtin_attr_name;
4use rustc_hir::RustcVersion;
5use rustc_span::{Symbol, sym};
6
7use crate::context::{AcceptContext, Stage};
8use crate::parser::ArgParser;
9
10/// Parse a rustc version number written inside string literal in an attribute,
11/// like appears in `since = "1.0.0"`. Suffixes like "-dev" and "-nightly" are
12/// not accepted in this position, unlike when parsing CFG_RELEASE.
13pub fn parse_version(s: Symbol) -> Option<RustcVersion> {
14    let mut components = s.as_str().split('-');
15    let d = components.next()?;
16    if components.next().is_some() {
17        return None;
18    }
19    let mut digits = d.splitn(3, '.');
20    let major = digits.next()?.parse().ok()?;
21    let minor = digits.next()?.parse().ok()?;
22    let patch = digits.next().unwrap_or("0").parse().ok()?;
23    Some(RustcVersion { major, minor, patch })
24}
25
26pub fn is_builtin_attr(attr: &impl AttributeExt) -> bool {
27    attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name))
28}
29
30pub fn is_doc_alias_attrs_contain_symbol<'tcx, T: AttributeExt + 'tcx>(
31    attrs: impl Iterator<Item = &'tcx T>,
32    symbol: Symbol,
33) -> bool {
34    let doc_attrs = attrs.filter(|attr| attr.has_name(sym::doc));
35    for attr in doc_attrs {
36        let Some(values) = attr.meta_item_list() else {
37            continue;
38        };
39        let alias_values = values.iter().filter(|v| v.has_name(sym::alias));
40        for v in alias_values {
41            if let Some(nested) = v.meta_item_list() {
42                // #[doc(alias("foo", "bar"))]
43                let mut iter = nested.iter().filter_map(|item| item.lit()).map(|item| item.symbol);
44                if iter.any(|s| s == symbol) {
45                    return true;
46                }
47            } else if let Some(meta) = v.meta_item()
48                && let Some(lit) = meta.name_value_literal()
49            {
50                // #[doc(alias = "foo")]
51                if lit.symbol == symbol {
52                    return true;
53                }
54            }
55        }
56    }
57    false
58}
59
60/// Parse a single integer.
61///
62/// Used by attributes that take a single integer as argument, such as
63/// `#[link_ordinal]` and `#[rustc_layout_scalar_valid_range_start]`.
64/// `cx` is the context given to the attribute.
65/// `args` is the parser for the attribute arguments.
66pub(crate) fn parse_single_integer<S: Stage>(
67    cx: &mut AcceptContext<'_, '_, S>,
68    args: &ArgParser<'_>,
69) -> Option<u128> {
70    let Some(list) = args.list() else {
71        cx.expected_list(cx.attr_span);
72        return None;
73    };
74    let Some(single) = list.single() else {
75        cx.expected_single_argument(list.span);
76        return None;
77    };
78    let Some(lit) = single.lit() else {
79        cx.expected_integer_literal(single.span());
80        return None;
81    };
82    let LitKind::Int(num, _ty) = lit.kind else {
83        cx.expected_integer_literal(single.span());
84        return None;
85    };
86    Some(num.0)
87}