]> git.lizzy.rs Git - rust.git/blob - crates/hir_def/src/attr.rs
clippy::complexity simplifications related to Iterators
[rust.git] / crates / hir_def / src / attr.rs
1 //! A higher level attributes based on TokenTree, with also some shortcuts.
2
3 use std::{ops, sync::Arc};
4
5 use base_db::CrateId;
6 use cfg::{CfgExpr, CfgOptions};
7 use either::Either;
8 use hir_expand::{hygiene::Hygiene, name::AsName, AstId, InFile};
9 use itertools::Itertools;
10 use la_arena::ArenaMap;
11 use mbe::ast_to_token_tree;
12 use smallvec::{smallvec, SmallVec};
13 use syntax::{
14     ast::{self, AstNode, AttrsOwner},
15     match_ast, AstToken, SmolStr, SyntaxNode,
16 };
17 use tt::Subtree;
18
19 use crate::{
20     db::DefDatabase,
21     item_tree::{ItemTreeId, ItemTreeNode},
22     nameres::ModuleSource,
23     path::{ModPath, PathKind},
24     src::{HasChildSource, HasSource},
25     AdtId, AttrDefId, EnumId, GenericParamId, HasModule, LocalEnumVariantId, LocalFieldId, Lookup,
26     VariantId,
27 };
28
29 /// Holds documentation
30 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
31 pub struct Documentation(String);
32
33 impl Documentation {
34     pub fn as_str(&self) -> &str {
35         &self.0
36     }
37 }
38
39 impl From<Documentation> for String {
40     fn from(Documentation(string): Documentation) -> Self {
41         string
42     }
43 }
44
45 /// Syntactical attributes, without filtering of `cfg_attr`s.
46 #[derive(Default, Debug, Clone, PartialEq, Eq)]
47 pub(crate) struct RawAttrs {
48     entries: Option<Arc<[Attr]>>,
49 }
50
51 #[derive(Default, Debug, Clone, PartialEq, Eq)]
52 pub struct Attrs(RawAttrs);
53
54 #[derive(Debug, Clone, PartialEq, Eq)]
55 pub struct AttrsWithOwner {
56     attrs: Attrs,
57     owner: AttrDefId,
58 }
59
60 impl ops::Deref for RawAttrs {
61     type Target = [Attr];
62
63     fn deref(&self) -> &[Attr] {
64         match &self.entries {
65             Some(it) => &*it,
66             None => &[],
67         }
68     }
69 }
70
71 impl ops::Deref for Attrs {
72     type Target = [Attr];
73
74     fn deref(&self) -> &[Attr] {
75         match &self.0.entries {
76             Some(it) => &*it,
77             None => &[],
78         }
79     }
80 }
81
82 impl ops::Deref for AttrsWithOwner {
83     type Target = Attrs;
84
85     fn deref(&self) -> &Attrs {
86         &self.attrs
87     }
88 }
89
90 impl RawAttrs {
91     pub(crate) const EMPTY: Self = Self { entries: None };
92
93     pub(crate) fn new(owner: &dyn ast::AttrsOwner, hygiene: &Hygiene) -> Self {
94         let entries = collect_attrs(owner)
95             .enumerate()
96             .flat_map(|(i, attr)| match attr {
97                 Either::Left(attr) => Attr::from_src(attr, hygiene, i as u32),
98                 Either::Right(comment) => comment.doc_comment().map(|doc| Attr {
99                     index: i as u32,
100                     input: Some(AttrInput::Literal(SmolStr::new(doc))),
101                     path: ModPath::from(hir_expand::name!(doc)),
102                 }),
103             })
104             .collect::<Arc<_>>();
105
106         Self { entries: if entries.is_empty() { None } else { Some(entries) } }
107     }
108
109     fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn ast::AttrsOwner>) -> Self {
110         let hygiene = Hygiene::new(db.upcast(), owner.file_id);
111         Self::new(owner.value, &hygiene)
112     }
113
114     pub(crate) fn merge(&self, other: Self) -> Self {
115         match (&self.entries, &other.entries) {
116             (None, None) => Self::EMPTY,
117             (Some(entries), None) | (None, Some(entries)) => {
118                 Self { entries: Some(entries.clone()) }
119             }
120             (Some(a), Some(b)) => {
121                 Self { entries: Some(a.iter().chain(b.iter()).cloned().collect()) }
122             }
123         }
124     }
125
126     /// Processes `cfg_attr`s, returning the resulting semantic `Attrs`.
127     pub(crate) fn filter(self, db: &dyn DefDatabase, krate: CrateId) -> Attrs {
128         let has_cfg_attrs = self.iter().any(|attr| {
129             attr.path.as_ident().map_or(false, |name| *name == hir_expand::name![cfg_attr])
130         });
131         if !has_cfg_attrs {
132             return Attrs(self);
133         }
134
135         let crate_graph = db.crate_graph();
136         let new_attrs = self
137             .iter()
138             .flat_map(|attr| -> SmallVec<[_; 1]> {
139                 let is_cfg_attr =
140                     attr.path.as_ident().map_or(false, |name| *name == hir_expand::name![cfg_attr]);
141                 if !is_cfg_attr {
142                     return smallvec![attr.clone()];
143                 }
144
145                 let subtree = match &attr.input {
146                     Some(AttrInput::TokenTree(it)) => it,
147                     _ => return smallvec![attr.clone()],
148                 };
149
150                 // Input subtree is: `(cfg, $(attr),+)`
151                 // Split it up into a `cfg` subtree and the `attr` subtrees.
152                 // FIXME: There should be a common API for this.
153                 let mut parts = subtree.token_trees.split(
154                     |tt| matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ','),
155                 );
156                 let cfg = parts.next().unwrap();
157                 let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg.to_vec() };
158                 let cfg = CfgExpr::parse(&cfg);
159                 let index = attr.index;
160                 let attrs = parts.filter(|a| !a.is_empty()).filter_map(|attr| {
161                     let tree = Subtree { delimiter: None, token_trees: attr.to_vec() };
162                     let attr = ast::Attr::parse(&format!("#[{}]", tree)).ok()?;
163                     // FIXME hygiene
164                     let hygiene = Hygiene::new_unhygienic();
165                     Attr::from_src(attr, &hygiene, index)
166                 });
167
168                 let cfg_options = &crate_graph[krate].cfg_options;
169                 if cfg_options.check(&cfg) == Some(false) {
170                     smallvec![]
171                 } else {
172                     cov_mark::hit!(cfg_attr_active);
173
174                     attrs.collect()
175                 }
176             })
177             .collect();
178
179         Attrs(RawAttrs { entries: Some(new_attrs) })
180     }
181 }
182
183 impl Attrs {
184     pub const EMPTY: Self = Self(RawAttrs::EMPTY);
185
186     pub(crate) fn variants_attrs_query(
187         db: &dyn DefDatabase,
188         e: EnumId,
189     ) -> Arc<ArenaMap<LocalEnumVariantId, Attrs>> {
190         let krate = e.lookup(db).container.krate;
191         let src = e.child_source(db);
192         let mut res = ArenaMap::default();
193
194         for (id, var) in src.value.iter() {
195             let attrs = RawAttrs::from_attrs_owner(db, src.with_value(var as &dyn ast::AttrsOwner))
196                 .filter(db, krate);
197
198             res.insert(id, attrs)
199         }
200
201         Arc::new(res)
202     }
203
204     pub(crate) fn fields_attrs_query(
205         db: &dyn DefDatabase,
206         v: VariantId,
207     ) -> Arc<ArenaMap<LocalFieldId, Attrs>> {
208         let krate = v.module(db).krate;
209         let src = v.child_source(db);
210         let mut res = ArenaMap::default();
211
212         for (id, fld) in src.value.iter() {
213             let attrs = match fld {
214                 Either::Left(_tuple) => Attrs::default(),
215                 Either::Right(record) => {
216                     RawAttrs::from_attrs_owner(db, src.with_value(record)).filter(db, krate)
217                 }
218             };
219
220             res.insert(id, attrs);
221         }
222
223         Arc::new(res)
224     }
225
226     pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> {
227         AttrQuery { attrs: self, key }
228     }
229
230     pub fn cfg(&self) -> Option<CfgExpr> {
231         let mut cfgs = self.by_key("cfg").tt_values().map(CfgExpr::parse).collect::<Vec<_>>();
232         match cfgs.len() {
233             0 => None,
234             1 => Some(cfgs.pop().unwrap()),
235             _ => Some(CfgExpr::All(cfgs)),
236         }
237     }
238     pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> bool {
239         match self.cfg() {
240             None => true,
241             Some(cfg) => cfg_options.check(&cfg) != Some(false),
242         }
243     }
244
245     pub fn docs(&self) -> Option<Documentation> {
246         let docs = self.by_key("doc").attrs().flat_map(|attr| match attr.input.as_ref()? {
247             AttrInput::Literal(s) => Some(s),
248             AttrInput::TokenTree(_) => None,
249         });
250         let indent = docs
251             .clone()
252             .flat_map(|s| s.lines())
253             .filter(|line| !line.chars().all(|c| c.is_whitespace()))
254             .map(|line| line.chars().take_while(|c| c.is_whitespace()).count())
255             .min()
256             .unwrap_or(0);
257         let mut buf = String::new();
258         for doc in docs {
259             // str::lines doesn't yield anything for the empty string
260             if !doc.is_empty() {
261                 buf.extend(Itertools::intersperse(
262                     doc.lines().map(|line| {
263                         line.char_indices()
264                             .nth(indent)
265                             .map_or(line, |(offset, _)| &line[offset..])
266                             .trim_end()
267                     }),
268                     "\n",
269                 ));
270             }
271             buf.push('\n');
272         }
273         buf.pop();
274         if buf.is_empty() {
275             None
276         } else {
277             Some(Documentation(buf))
278         }
279     }
280 }
281
282 impl AttrsWithOwner {
283     pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Self {
284         // FIXME: this should use `Trace` to avoid duplication in `source_map` below
285         let raw_attrs = match def {
286             AttrDefId::ModuleId(module) => {
287                 let def_map = module.def_map(db);
288                 let mod_data = &def_map[module.local_id];
289                 match mod_data.declaration_source(db) {
290                     Some(it) => {
291                         let raw_attrs = RawAttrs::from_attrs_owner(
292                             db,
293                             it.as_ref().map(|it| it as &dyn ast::AttrsOwner),
294                         );
295                         match mod_data.definition_source(db) {
296                             InFile { file_id, value: ModuleSource::SourceFile(file) } => raw_attrs
297                                 .merge(RawAttrs::from_attrs_owner(db, InFile::new(file_id, &file))),
298                             _ => raw_attrs,
299                         }
300                     }
301                     None => RawAttrs::from_attrs_owner(
302                         db,
303                         mod_data.definition_source(db).as_ref().map(|src| match src {
304                             ModuleSource::SourceFile(file) => file as &dyn ast::AttrsOwner,
305                             ModuleSource::Module(module) => module as &dyn ast::AttrsOwner,
306                             ModuleSource::BlockExpr(block) => block as &dyn ast::AttrsOwner,
307                         }),
308                     ),
309                 }
310             }
311             AttrDefId::FieldId(it) => {
312                 return Self { attrs: db.fields_attrs(it.parent)[it.local_id].clone(), owner: def };
313             }
314             AttrDefId::EnumVariantId(it) => {
315                 return Self {
316                     attrs: db.variants_attrs(it.parent)[it.local_id].clone(),
317                     owner: def,
318                 };
319             }
320             AttrDefId::AdtId(it) => match it {
321                 AdtId::StructId(it) => attrs_from_item_tree(it.lookup(db).id, db),
322                 AdtId::EnumId(it) => attrs_from_item_tree(it.lookup(db).id, db),
323                 AdtId::UnionId(it) => attrs_from_item_tree(it.lookup(db).id, db),
324             },
325             AttrDefId::TraitId(it) => attrs_from_item_tree(it.lookup(db).id, db),
326             AttrDefId::MacroDefId(it) => it
327                 .ast_id()
328                 .left()
329                 .map_or_else(Default::default, |ast_id| attrs_from_ast(ast_id, db)),
330             AttrDefId::ImplId(it) => attrs_from_item_tree(it.lookup(db).id, db),
331             AttrDefId::ConstId(it) => attrs_from_item_tree(it.lookup(db).id, db),
332             AttrDefId::StaticId(it) => attrs_from_item_tree(it.lookup(db).id, db),
333             AttrDefId::FunctionId(it) => attrs_from_item_tree(it.lookup(db).id, db),
334             AttrDefId::TypeAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db),
335             AttrDefId::GenericParamId(it) => match it {
336                 GenericParamId::TypeParamId(it) => {
337                     let src = it.parent.child_source(db);
338                     RawAttrs::from_attrs_owner(
339                         db,
340                         src.with_value(
341                             src.value[it.local_id].as_ref().either(|it| it as _, |it| it as _),
342                         ),
343                     )
344                 }
345                 GenericParamId::LifetimeParamId(it) => {
346                     let src = it.parent.child_source(db);
347                     RawAttrs::from_attrs_owner(db, src.with_value(&src.value[it.local_id]))
348                 }
349                 GenericParamId::ConstParamId(it) => {
350                     let src = it.parent.child_source(db);
351                     RawAttrs::from_attrs_owner(db, src.with_value(&src.value[it.local_id]))
352                 }
353             },
354         };
355
356         let attrs = raw_attrs.filter(db, def.krate(db));
357         Self { attrs, owner: def }
358     }
359
360     pub fn source_map(&self, db: &dyn DefDatabase) -> AttrSourceMap {
361         let owner = match self.owner {
362             AttrDefId::ModuleId(module) => {
363                 // Modules can have 2 attribute owners (the `mod x;` item, and the module file itself).
364
365                 let def_map = module.def_map(db);
366                 let mod_data = &def_map[module.local_id];
367                 let attrs = match mod_data.declaration_source(db) {
368                     Some(it) => {
369                         let mut attrs: Vec<_> = collect_attrs(&it.value as &dyn ast::AttrsOwner)
370                             .map(|attr| InFile::new(it.file_id, attr))
371                             .collect();
372                         if let InFile { file_id, value: ModuleSource::SourceFile(file) } =
373                             mod_data.definition_source(db)
374                         {
375                             attrs.extend(
376                                 collect_attrs(&file as &dyn ast::AttrsOwner)
377                                     .map(|attr| InFile::new(file_id, attr)),
378                             )
379                         }
380                         attrs
381                     }
382                     None => {
383                         let InFile { file_id, value } = mod_data.definition_source(db);
384                         match &value {
385                             ModuleSource::SourceFile(file) => {
386                                 collect_attrs(file as &dyn ast::AttrsOwner)
387                             }
388                             ModuleSource::Module(module) => {
389                                 collect_attrs(module as &dyn ast::AttrsOwner)
390                             }
391                             ModuleSource::BlockExpr(block) => {
392                                 collect_attrs(block as &dyn ast::AttrsOwner)
393                             }
394                         }
395                         .map(|attr| InFile::new(file_id, attr))
396                         .collect()
397                     }
398                 };
399                 return AttrSourceMap { attrs };
400             }
401             AttrDefId::FieldId(id) => {
402                 id.parent.child_source(db).map(|source| match &source[id.local_id] {
403                     Either::Left(field) => ast::AttrsOwnerNode::new(field.clone()),
404                     Either::Right(field) => ast::AttrsOwnerNode::new(field.clone()),
405                 })
406             }
407             AttrDefId::AdtId(adt) => match adt {
408                 AdtId::StructId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new),
409                 AdtId::UnionId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new),
410                 AdtId::EnumId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new),
411             },
412             AttrDefId::FunctionId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new),
413             AttrDefId::EnumVariantId(id) => id
414                 .parent
415                 .child_source(db)
416                 .map(|source| ast::AttrsOwnerNode::new(source[id.local_id].clone())),
417             AttrDefId::StaticId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new),
418             AttrDefId::ConstId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new),
419             AttrDefId::TraitId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new),
420             AttrDefId::TypeAliasId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new),
421             AttrDefId::MacroDefId(id) => match id.ast_id() {
422                 Either::Left(it) => {
423                     it.with_value(ast::AttrsOwnerNode::new(it.to_node(db.upcast())))
424                 }
425                 Either::Right(it) => {
426                     it.with_value(ast::AttrsOwnerNode::new(it.to_node(db.upcast())))
427                 }
428             },
429             AttrDefId::ImplId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new),
430             AttrDefId::GenericParamId(id) => match id {
431                 GenericParamId::TypeParamId(id) => {
432                     id.parent.child_source(db).map(|source| match &source[id.local_id] {
433                         Either::Left(id) => ast::AttrsOwnerNode::new(id.clone()),
434                         Either::Right(id) => ast::AttrsOwnerNode::new(id.clone()),
435                     })
436                 }
437                 GenericParamId::LifetimeParamId(id) => id
438                     .parent
439                     .child_source(db)
440                     .map(|source| ast::AttrsOwnerNode::new(source[id.local_id].clone())),
441                 GenericParamId::ConstParamId(id) => id
442                     .parent
443                     .child_source(db)
444                     .map(|source| ast::AttrsOwnerNode::new(source[id.local_id].clone())),
445             },
446         };
447
448         AttrSourceMap {
449             attrs: collect_attrs(&owner.value)
450                 .map(|attr| InFile::new(owner.file_id, attr))
451                 .collect(),
452         }
453     }
454 }
455
456 fn inner_attributes(
457     syntax: &SyntaxNode,
458 ) -> Option<(impl Iterator<Item = ast::Attr>, impl Iterator<Item = ast::Comment>)> {
459     let (attrs, docs) = match_ast! {
460         match syntax {
461             ast::SourceFile(it) => (it.attrs(), ast::CommentIter::from_syntax_node(it.syntax())),
462             ast::ExternBlock(it) => {
463                 let extern_item_list = it.extern_item_list()?;
464                 (extern_item_list.attrs(), ast::CommentIter::from_syntax_node(extern_item_list.syntax()))
465             },
466             ast::Fn(it) => {
467                 let body = it.body()?;
468                 (body.attrs(), ast::CommentIter::from_syntax_node(body.syntax()))
469             },
470             ast::Impl(it) => {
471                 let assoc_item_list = it.assoc_item_list()?;
472                 (assoc_item_list.attrs(), ast::CommentIter::from_syntax_node(assoc_item_list.syntax()))
473             },
474             ast::Module(it) => {
475                 let item_list = it.item_list()?;
476                 (item_list.attrs(), ast::CommentIter::from_syntax_node(item_list.syntax()))
477             },
478             // FIXME: BlockExpr's only accept inner attributes in specific cases
479             // Excerpt from the reference:
480             // Block expressions accept outer and inner attributes, but only when they are the outer
481             // expression of an expression statement or the final expression of another block expression.
482             ast::BlockExpr(_it) => return None,
483             _ => return None,
484         }
485     };
486     let attrs = attrs.filter(|attr| attr.excl_token().is_some());
487     let docs = docs.filter(|doc| doc.is_inner());
488     Some((attrs, docs))
489 }
490
491 pub struct AttrSourceMap {
492     attrs: Vec<InFile<Either<ast::Attr, ast::Comment>>>,
493 }
494
495 impl AttrSourceMap {
496     /// Maps the lowered `Attr` back to its original syntax node.
497     ///
498     /// `attr` must come from the `owner` used for AttrSourceMap
499     ///
500     /// Note that the returned syntax node might be a `#[cfg_attr]`, or a doc comment, instead of
501     /// the attribute represented by `Attr`.
502     pub fn source_of(&self, attr: &Attr) -> InFile<&Either<ast::Attr, ast::Comment>> {
503         self.attrs
504             .get(attr.index as usize)
505             .unwrap_or_else(|| panic!("cannot find `Attr` at index {}", attr.index))
506             .as_ref()
507     }
508 }
509
510 #[derive(Debug, Clone, PartialEq, Eq)]
511 pub struct Attr {
512     index: u32,
513     pub(crate) path: ModPath,
514     pub(crate) input: Option<AttrInput>,
515 }
516
517 #[derive(Debug, Clone, PartialEq, Eq)]
518 pub enum AttrInput {
519     /// `#[attr = "string"]`
520     Literal(SmolStr),
521     /// `#[attr(subtree)]`
522     TokenTree(Subtree),
523 }
524
525 impl Attr {
526     fn from_src(ast: ast::Attr, hygiene: &Hygiene, index: u32) -> Option<Attr> {
527         let path = ModPath::from_src(ast.path()?, hygiene)?;
528         let input = if let Some(ast::Expr::Literal(lit)) = ast.expr() {
529             let value = match lit.kind() {
530                 ast::LiteralKind::String(string) => string.value()?.into(),
531                 _ => lit.syntax().first_token()?.text().trim_matches('"').into(),
532             };
533             Some(AttrInput::Literal(value))
534         } else if let Some(tt) = ast.token_tree() {
535             Some(AttrInput::TokenTree(ast_to_token_tree(&tt)?.0))
536         } else {
537             None
538         };
539         Some(Attr { index, path, input })
540     }
541
542     /// Parses this attribute as a `#[derive]`, returns an iterator that yields all contained paths
543     /// to derive macros.
544     ///
545     /// Returns `None` when the attribute is not a well-formed `#[derive]` attribute.
546     pub(crate) fn parse_derive(&self) -> Option<impl Iterator<Item = ModPath>> {
547         if self.path.as_ident() != Some(&hir_expand::name![derive]) {
548             return None;
549         }
550
551         match &self.input {
552             Some(AttrInput::TokenTree(args)) => {
553                 let mut counter = 0;
554                 let paths = args
555                     .token_trees
556                     .iter()
557                     .group_by(move |tt| {
558                         match tt {
559                             tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ',' => {
560                                 counter += 1;
561                             }
562                             _ => {}
563                         }
564                         counter
565                     })
566                     .into_iter()
567                     .map(|(_, tts)| {
568                         let segments = tts.filter_map(|tt| match tt {
569                             tt::TokenTree::Leaf(tt::Leaf::Ident(id)) => Some(id.as_name()),
570                             _ => None,
571                         });
572                         ModPath::from_segments(PathKind::Plain, segments)
573                     })
574                     .collect::<Vec<_>>();
575
576                 Some(paths.into_iter())
577             }
578             _ => None,
579         }
580     }
581
582     pub fn string_value(&self) -> Option<&SmolStr> {
583         match self.input.as_ref()? {
584             AttrInput::Literal(it) => Some(it),
585             _ => None,
586         }
587     }
588 }
589
590 #[derive(Debug, Clone, Copy)]
591 pub struct AttrQuery<'a> {
592     attrs: &'a Attrs,
593     key: &'static str,
594 }
595
596 impl<'a> AttrQuery<'a> {
597     pub fn tt_values(self) -> impl Iterator<Item = &'a Subtree> {
598         self.attrs().filter_map(|attr| match attr.input.as_ref()? {
599             AttrInput::TokenTree(it) => Some(it),
600             _ => None,
601         })
602     }
603
604     pub fn string_value(self) -> Option<&'a SmolStr> {
605         self.attrs().find_map(|attr| match attr.input.as_ref()? {
606             AttrInput::Literal(it) => Some(it),
607             _ => None,
608         })
609     }
610
611     pub fn exists(self) -> bool {
612         self.attrs().next().is_some()
613     }
614
615     pub fn attrs(self) -> impl Iterator<Item = &'a Attr> + Clone {
616         let key = self.key;
617         self.attrs
618             .iter()
619             .filter(move |attr| attr.path.as_ident().map_or(false, |s| s.to_string() == key))
620     }
621 }
622
623 fn attrs_from_ast<N>(src: AstId<N>, db: &dyn DefDatabase) -> RawAttrs
624 where
625     N: ast::AttrsOwner,
626 {
627     let src = InFile::new(src.file_id, src.to_node(db.upcast()));
628     RawAttrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn ast::AttrsOwner))
629 }
630
631 fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase) -> RawAttrs {
632     let tree = id.item_tree(db);
633     let mod_item = N::id_to_mod_item(id.value);
634     tree.raw_attrs(mod_item.into()).clone()
635 }
636
637 fn collect_attrs(
638     owner: &dyn ast::AttrsOwner,
639 ) -> impl Iterator<Item = Either<ast::Attr, ast::Comment>> {
640     let (inner_attrs, inner_docs) = inner_attributes(owner.syntax())
641         .map_or((None, None), |(attrs, docs)| (Some(attrs), Some(docs)));
642
643     let outer_attrs = owner.attrs().filter(|attr| attr.excl_token().is_none());
644     let attrs = outer_attrs
645         .chain(inner_attrs.into_iter().flatten())
646         .map(|attr| (attr.syntax().text_range().start(), Either::Left(attr)));
647
648     let outer_docs =
649         ast::CommentIter::from_syntax_node(owner.syntax()).filter(ast::Comment::is_outer);
650     let docs = outer_docs
651         .chain(inner_docs.into_iter().flatten())
652         .map(|docs_text| (docs_text.syntax().text_range().start(), Either::Right(docs_text)));
653     // sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved
654     let attrs: Vec<_> = docs.chain(attrs).sorted_by_key(|&(offset, _)| offset).collect();
655
656     attrs.into_iter().map(|(_, attr)| attr)
657 }