about summary refs log tree commit diff
path: root/compiler/rustc_attr_parsing/src/attributes/util.rs
blob: 77e8c32e59d733b7f426a7690c4c79e1c9d77437 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
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<RustcVersion> {
    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<Item = &'tcx T>,
    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<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)
}