rustc_attr_parsing/attributes/
util.rs1use std::num::IntErrorKind;
2
3use rustc_ast::LitKind;
4use rustc_ast::attr::AttributeExt;
5use rustc_feature::is_builtin_attr_name;
6use rustc_hir::RustcVersion;
7use rustc_hir::limit::Limit;
8use rustc_span::{Symbol, sym};
9
10use crate::context::{AcceptContext, Stage};
11use crate::parser::{ArgParser, NameValueParser};
12use crate::session_diagnostics::LimitInvalid;
13
14pub fn parse_version(s: Symbol) -> Option<RustcVersion> {
18 let mut components = s.as_str().split('-');
19 let d = components.next()?;
20 if components.next().is_some() {
21 return None;
22 }
23 let mut digits = d.splitn(3, '.');
24 let major = digits.next()?.parse().ok()?;
25 let minor = digits.next()?.parse().ok()?;
26 let patch = digits.next().unwrap_or("0").parse().ok()?;
27 Some(RustcVersion { major, minor, patch })
28}
29
30pub fn is_builtin_attr(attr: &impl AttributeExt) -> bool {
31 attr.is_doc_comment().is_some()
32 || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name))
33}
34
35pub fn is_doc_alias_attrs_contain_symbol<'tcx, T: AttributeExt + 'tcx>(
36 attrs: impl Iterator<Item = &'tcx T>,
37 symbol: Symbol,
38) -> bool {
39 let doc_attrs = attrs.filter(|attr| attr.has_name(sym::doc));
40 for attr in doc_attrs {
41 let Some(values) = attr.meta_item_list() else {
42 continue;
43 };
44 let alias_values = values.iter().filter(|v| v.has_name(sym::alias));
45 for v in alias_values {
46 if let Some(nested) = v.meta_item_list() {
47 let mut iter = nested.iter().filter_map(|item| item.lit()).map(|item| item.symbol);
49 if iter.any(|s| s == symbol) {
50 return true;
51 }
52 } else if let Some(meta) = v.meta_item()
53 && let Some(lit) = meta.name_value_literal()
54 {
55 if lit.symbol == symbol {
57 return true;
58 }
59 }
60 }
61 }
62 false
63}
64
65pub(crate) fn parse_single_integer<S: Stage>(
72 cx: &mut AcceptContext<'_, '_, S>,
73 args: &ArgParser<'_>,
74) -> Option<u128> {
75 let Some(list) = args.list() else {
76 cx.expected_list(cx.attr_span);
77 return None;
78 };
79 let Some(single) = list.single() else {
80 cx.expected_single_argument(list.span);
81 return None;
82 };
83 let Some(lit) = single.lit() else {
84 cx.expected_integer_literal(single.span());
85 return None;
86 };
87 let LitKind::Int(num, _ty) = lit.kind else {
88 cx.expected_integer_literal(single.span());
89 return None;
90 };
91 Some(num.0)
92}
93
94impl<S: Stage> AcceptContext<'_, '_, S> {
95 pub(crate) fn parse_limit_int(&self, nv: &NameValueParser) -> Option<Limit> {
96 let Some(limit) = nv.value_as_str() else {
97 self.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
98 return None;
99 };
100
101 let error_str = match limit.as_str().parse() {
102 Ok(i) => return Some(Limit::new(i)),
103 Err(e) => match e.kind() {
104 IntErrorKind::PosOverflow => "`limit` is too large",
105 IntErrorKind::Empty => "`limit` must be a non-negative integer",
106 IntErrorKind::InvalidDigit => "not a valid integer",
107 IntErrorKind::NegOverflow => {
108 panic!(
109 "`limit` should never negatively overflow since we're parsing into a usize and we'd get Empty instead"
110 )
111 }
112 IntErrorKind::Zero => {
113 panic!("zero is a valid `limit` so should have returned Ok() when parsing")
114 }
115 kind => panic!("unimplemented IntErrorKind variant: {:?}", kind),
116 },
117 };
118
119 self.emit_err(LimitInvalid { span: self.attr_span, value_span: nv.value_span, error_str });
120
121 None
122 }
123}