Skip to main content

rustc_attr_parsing/attributes/
util.rs

1use std::num::IntErrorKind;
2
3use rustc_ast::{LitKind, ast};
4use rustc_feature::is_builtin_attr_name;
5use rustc_hir::RustcVersion;
6use rustc_hir::limit::Limit;
7use rustc_span::Symbol;
8
9use crate::context::{AcceptContext, Stage};
10use crate::parser::{ArgParser, NameValueParser};
11use crate::session_diagnostics::LimitInvalid;
12
13/// Parse a rustc version number written inside string literal in an attribute,
14/// like appears in `since = "1.0.0"`. Suffixes like "-dev" and "-nightly" are
15/// not accepted in this position, unlike when parsing CFG_RELEASE.
16pub fn parse_version(s: Symbol) -> Option<RustcVersion> {
17    let mut components = s.as_str().split('-');
18    let d = components.next()?;
19    if components.next().is_some() {
20        return None;
21    }
22    let mut digits = d.splitn(3, '.');
23    let major = digits.next()?.parse().ok()?;
24    let minor = digits.next()?.parse().ok()?;
25    let patch = digits.next().unwrap_or("0").parse().ok()?;
26    Some(RustcVersion { major, minor, patch })
27}
28
29pub fn is_builtin_attr(attr: &ast::Attribute) -> bool {
30    attr.is_doc_comment() || attr.name().is_some_and(|name| is_builtin_attr_name(name))
31}
32
33/// Parse a single integer.
34///
35/// Used by attributes that take a single integer as argument, such as
36/// `#[link_ordinal]` and `#[rustc_layout_scalar_valid_range_start]`.
37/// `cx` is the context given to the attribute.
38/// `args` is the parser for the attribute arguments.
39pub(crate) fn parse_single_integer<S: Stage>(
40    cx: &mut AcceptContext<'_, '_, S>,
41    args: &ArgParser,
42) -> Option<u128> {
43    let single = cx.expect_single_element_list(args, cx.attr_span)?;
44    let Some(lit) = single.lit() else {
45        cx.adcx().expected_integer_literal(single.span());
46        return None;
47    };
48    let LitKind::Int(num, _ty) = lit.kind else {
49        cx.adcx().expected_integer_literal(single.span());
50        return None;
51    };
52    Some(num.0)
53}
54
55impl<S: Stage> AcceptContext<'_, '_, S> {
56    pub(crate) fn parse_limit_int(&mut self, nv: &NameValueParser) -> Option<Limit> {
57        let Some(limit) = nv.value_as_str() else {
58            self.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
59            return None;
60        };
61
62        let error_str = match limit.as_str().parse() {
63            Ok(i) => return Some(Limit::new(i)),
64            Err(e) => match e.kind() {
65                IntErrorKind::PosOverflow => "`limit` is too large",
66                IntErrorKind::Empty => "`limit` must be a non-negative integer",
67                IntErrorKind::InvalidDigit => "not a valid integer",
68                IntErrorKind::NegOverflow => {
69                    {
    ::core::panicking::panic_fmt(format_args!("`limit` should never negatively overflow since we\'re parsing into a usize and we\'d get Empty instead"));
}panic!(
70                        "`limit` should never negatively overflow since we're parsing into a usize and we'd get Empty instead"
71                    )
72                }
73                IntErrorKind::Zero => {
74                    {
    ::core::panicking::panic_fmt(format_args!("zero is a valid `limit` so should have returned Ok() when parsing"));
}panic!("zero is a valid `limit` so should have returned Ok() when parsing")
75                }
76                kind => {
    ::core::panicking::panic_fmt(format_args!("unimplemented IntErrorKind variant: {0:?}",
            kind));
}panic!("unimplemented IntErrorKind variant: {:?}", kind),
77            },
78        };
79
80        self.emit_err(LimitInvalid { span: self.attr_span, value_span: nv.value_span, error_str });
81
82        None
83    }
84}