hir_def/item_tree/
attrs.rs1use std::{
8 borrow::Cow,
9 convert::Infallible,
10 ops::{self, ControlFlow},
11};
12
13use base_db::SourceDatabase;
14use cfg::{CfgExpr, CfgOptions};
15use either::Either;
16use hir_expand::{
17 attrs::{Attr, AttrId, AttrInput, collect_item_tree_attrs},
18 mod_path::ModPath,
19 name::Name,
20};
21use intern::{Interned, Symbol, sym};
22use syntax::{AstNode, ast};
23use syntax_bridge::DocCommentDesugarMode;
24use tt::token_to_literal;
25
26use crate::item_tree::lower::Ctx;
27
28#[derive(Debug, PartialEq, Eq)]
29pub(crate) enum AttrsOrCfg {
30 Enabled {
31 attrs: AttrsOwned,
32 },
33 CfgDisabled(Box<(CfgExpr, AttrsOwned)>),
35}
36
37impl Default for AttrsOrCfg {
38 #[inline]
39 fn default() -> Self {
40 AttrsOrCfg::Enabled { attrs: AttrsOwned(Box::new([])) }
41 }
42}
43
44impl AttrsOrCfg {
45 pub(crate) fn lower<'a, S>(
46 db: &dyn SourceDatabase,
47 owner: &dyn ast::HasAttrs,
48 cfg_options: &dyn Fn() -> &'a CfgOptions,
49 span_map: S,
50 ) -> AttrsOrCfg
51 where
52 S: syntax_bridge::SpanMapper + Copy,
53 {
54 let mut attrs = Vec::new();
55 let result = collect_item_tree_attrs::<Infallible>(owner, cfg_options, |meta, _| {
56 let path = meta.path();
59 let path_range = path
60 .as_ref()
61 .map(|path| path.syntax().text_range())
62 .unwrap_or_else(|| meta.syntax().text_range());
63 let (span, input) = match &meta {
64 ast::Meta::KeyValueMeta(meta) => {
65 let span = span_map.span_for(path_range);
66 let input = meta.expr().and_then(|value| {
67 if let ast::Expr::Literal(value) = value {
68 Some(Box::new(AttrInput::Literal(token_to_literal(
69 value.token().text(),
70 span_map.span_for(value.syntax().text_range()),
71 ))))
72 } else {
73 None
74 }
75 });
76 (span, input)
77 }
78 ast::Meta::TokenTreeMeta(meta) => {
79 let span = span_map.span_for(path_range);
80 let tt = syntax_bridge::syntax_node_to_token_tree(
81 &meta
82 .token_tree()
83 .map(|it| it.syntax().clone())
84 .unwrap_or_else(|| meta.syntax().clone()),
85 span_map,
86 span,
87 DocCommentDesugarMode::ProcMacro,
88 );
89 let input = Some(Box::new(AttrInput::TokenTree(tt)));
90 (span, input)
91 }
92 ast::Meta::PathMeta(_) => {
93 let span = span_map.span_for(path_range);
94 (span, None)
95 }
96 ast::Meta::CfgMeta(_) | ast::Meta::CfgAttrMeta(_) | ast::Meta::UnsafeMeta(_) => {
97 unreachable!(
98 "`cfg`, `cfg_attr` and `unsafe(...)` are handled in `collect_item_tree_attrs()`"
99 )
100 }
101 };
102
103 let path = path.and_then(|path| {
104 ModPath::from_src(db, path, &mut |range| span_map.span_for(range).ctx)
105 });
106 let path = path.unwrap_or_else(|| Name::missing().into());
107
108 attrs.push(Attr { path: Interned::new(path), input, ctxt: span.ctx });
109 ControlFlow::Continue(())
110 });
111 let attrs = AttrsOwned(attrs.into_boxed_slice());
112 match result {
113 Some(Either::Right(cfg)) => AttrsOrCfg::CfgDisabled(Box::new((cfg, attrs))),
114 None => AttrsOrCfg::Enabled { attrs },
115 }
116 }
117
118 pub(crate) fn merge(self, other: AttrsOrCfg) -> AttrsOrCfg {
139 match (self, other) {
140 (AttrsOrCfg::Enabled { attrs }, AttrsOrCfg::Enabled { attrs: other_attrs }) => {
141 let mut v = attrs.0.into_vec();
142 v.extend(other_attrs.0);
143 AttrsOrCfg::Enabled { attrs: AttrsOwned(v.into_boxed_slice()) }
144 }
145 (AttrsOrCfg::Enabled { attrs }, AttrsOrCfg::CfgDisabled(mut other)) => {
146 let other_attrs = &mut other.1;
147 let mut v = attrs.0.into_vec();
148 v.extend(std::mem::take(&mut other_attrs.0));
149 other_attrs.0 = v.into_boxed_slice();
150 AttrsOrCfg::CfgDisabled(other)
151 }
152 (this @ AttrsOrCfg::CfgDisabled(_), _) => this,
153 }
154 }
155}
156
157#[derive(Debug, PartialEq, Eq)]
158pub(crate) struct AttrsOwned(Box<[Attr]>);
159
160#[derive(Debug, Clone, Copy)]
161pub(crate) struct Attrs<'a>(&'a [Attr]);
162
163impl ops::Deref for Attrs<'_> {
164 type Target = [Attr];
165
166 #[inline]
167 fn deref(&self) -> &Self::Target {
168 self.0
169 }
170}
171
172impl Ctx<'_> {
173 #[inline]
174 pub(super) fn lower_attrs(&self, owner: &dyn ast::HasAttrs) -> AttrsOrCfg {
175 AttrsOrCfg::lower(self.db, owner, &|| self.cfg_options(), self.span_map())
176 }
177}
178
179impl AttrsOwned {
180 #[inline]
181 pub(crate) fn as_ref(&self) -> Attrs<'_> {
182 Attrs(&self.0)
183 }
184}
185
186impl<'a> Attrs<'a> {
187 pub(crate) const EMPTY: Self = Attrs(&[]);
188
189 #[inline]
190 pub(crate) fn by_key(self, key: Symbol) -> AttrQuery<'a> {
191 AttrQuery { attrs: self, key }
192 }
193
194 #[inline]
195 pub(crate) fn iter(self) -> impl Iterator<Item = (AttrId, &'a Attr)> {
196 self.0.iter().enumerate().map(|(id, attr)| (AttrId::from_item_tree_index(id as u32), attr))
197 }
198
199 #[inline]
200 pub(crate) fn iter_after(
201 self,
202 after: Option<AttrId>,
203 ) -> impl Iterator<Item = (AttrId, &'a Attr)> {
204 let skip = after.map_or(0, |after| after.item_tree_index() + 1);
205 self.0[skip as usize..]
206 .iter()
207 .enumerate()
208 .map(move |(id, attr)| (AttrId::from_item_tree_index(id as u32 + skip), attr))
209 }
210
211 #[inline]
212 pub(crate) fn is_proc_macro(&self) -> bool {
213 self.by_key(sym::proc_macro).exists()
214 }
215
216 #[inline]
217 pub(crate) fn is_proc_macro_attribute(&self) -> bool {
218 self.by_key(sym::proc_macro_attribute).exists()
219 }
220}
221#[derive(Debug, Clone)]
222pub(crate) struct AttrQuery<'attr> {
223 attrs: Attrs<'attr>,
224 key: Symbol,
225}
226
227impl<'attr> AttrQuery<'attr> {
228 #[inline]
229 pub(crate) fn tt_values(self) -> impl Iterator<Item = &'attr crate::tt::TopSubtree> {
230 self.attrs().filter_map(|attr| attr.token_tree_value())
231 }
232
233 #[inline]
234 pub(crate) fn string_value_with_span(self) -> Option<(&'attr str, span::Span)> {
235 self.attrs().find_map(|attr| attr.string_value_with_span())
236 }
237
238 #[inline]
239 pub(crate) fn string_value_unescape(self) -> Option<Cow<'attr, str>> {
240 self.attrs().find_map(|attr| attr.string_value_unescape())
241 }
242
243 #[inline]
244 pub(crate) fn exists(self) -> bool {
245 self.attrs().next().is_some()
246 }
247
248 #[inline]
249 pub(crate) fn attrs(self) -> impl Iterator<Item = &'attr Attr> + Clone {
250 let key = self.key;
251 self.attrs.0.iter().filter(move |attr| attr.path.as_ident().is_some_and(|s| *s == key))
252 }
253}
254
255impl AttrsOrCfg {
256 #[inline]
257 pub(super) fn is_empty(&self) -> bool {
258 matches!(self, AttrsOrCfg::Enabled { attrs } if attrs.0.is_empty())
259 }
260}