aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_def/src/attr.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_def/src/attr.rs')
-rw-r--r--crates/hir_def/src/attr.rs105
1 files changed, 72 insertions, 33 deletions
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index ab77d924a..786fad6e1 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -9,14 +9,14 @@ use std::{
9use base_db::CrateId; 9use base_db::CrateId;
10use cfg::{CfgExpr, CfgOptions}; 10use cfg::{CfgExpr, CfgOptions};
11use either::Either; 11use either::Either;
12use hir_expand::{hygiene::Hygiene, name::AsName, AstId, InFile}; 12use hir_expand::{hygiene::Hygiene, name::AsName, AstId, AttrId, InFile};
13use itertools::Itertools; 13use itertools::Itertools;
14use la_arena::ArenaMap; 14use la_arena::ArenaMap;
15use mbe::ast_to_token_tree; 15use mbe::ast_to_token_tree;
16use smallvec::{smallvec, SmallVec}; 16use smallvec::{smallvec, SmallVec};
17use syntax::{ 17use syntax::{
18 ast::{self, AstNode, AttrsOwner}, 18 ast::{self, AstNode, AttrsOwner},
19 match_ast, AstToken, SmolStr, SyntaxNode, TextRange, TextSize, 19 match_ast, AstPtr, AstToken, SmolStr, SyntaxNode, TextRange, TextSize,
20}; 20};
21use tt::Subtree; 21use tt::Subtree;
22 22
@@ -98,13 +98,16 @@ impl RawAttrs {
98 pub(crate) fn new(owner: &dyn ast::AttrsOwner, hygiene: &Hygiene) -> Self { 98 pub(crate) fn new(owner: &dyn ast::AttrsOwner, hygiene: &Hygiene) -> Self {
99 let entries = collect_attrs(owner) 99 let entries = collect_attrs(owner)
100 .enumerate() 100 .enumerate()
101 .flat_map(|(i, attr)| match attr { 101 .flat_map(|(i, attr)| {
102 Either::Left(attr) => Attr::from_src(attr, hygiene, i as u32), 102 let index = AttrId(i as u32);
103 Either::Right(comment) => comment.doc_comment().map(|doc| Attr { 103 match attr {
104 index: i as u32, 104 Either::Left(attr) => Attr::from_src(attr, hygiene, index),
105 input: Some(AttrInput::Literal(SmolStr::new(doc))), 105 Either::Right(comment) => comment.doc_comment().map(|doc| Attr {
106 path: Interned::new(ModPath::from(hir_expand::name!(doc))), 106 id: index,
107 }), 107 input: Some(AttrInput::Literal(SmolStr::new(doc))),
108 path: Interned::new(ModPath::from(hir_expand::name!(doc))),
109 }),
110 }
108 }) 111 })
109 .collect::<Arc<_>>(); 112 .collect::<Arc<_>>();
110 113
@@ -161,7 +164,7 @@ impl RawAttrs {
161 let cfg = parts.next().unwrap(); 164 let cfg = parts.next().unwrap();
162 let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg.to_vec() }; 165 let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg.to_vec() };
163 let cfg = CfgExpr::parse(&cfg); 166 let cfg = CfgExpr::parse(&cfg);
164 let index = attr.index; 167 let index = attr.id;
165 let attrs = parts.filter(|a| !a.is_empty()).filter_map(|attr| { 168 let attrs = parts.filter(|a| !a.is_empty()).filter_map(|attr| {
166 let tree = Subtree { delimiter: None, token_trees: attr.to_vec() }; 169 let tree = Subtree { delimiter: None, token_trees: attr.to_vec() };
167 let attr = ast::Attr::parse(&format!("#[{}]", tree)).ok()?; 170 let attr = ast::Attr::parse(&format!("#[{}]", tree)).ok()?;
@@ -215,12 +218,11 @@ impl Attrs {
215 let mut res = ArenaMap::default(); 218 let mut res = ArenaMap::default();
216 219
217 for (id, fld) in src.value.iter() { 220 for (id, fld) in src.value.iter() {
218 let attrs = match fld { 221 let owner: &dyn AttrsOwner = match fld {
219 Either::Left(_tuple) => Attrs::default(), 222 Either::Left(tuple) => tuple,
220 Either::Right(record) => { 223 Either::Right(record) => record,
221 RawAttrs::from_attrs_owner(db, src.with_value(record)).filter(db, krate)
222 }
223 }; 224 };
225 let attrs = RawAttrs::from_attrs_owner(db, src.with_value(owner)).filter(db, krate);
224 226
225 res.insert(id, attrs); 227 res.insert(id, attrs);
226 } 228 }
@@ -404,10 +406,14 @@ impl AttrsWithOwner {
404 return AttrSourceMap { attrs }; 406 return AttrSourceMap { attrs };
405 } 407 }
406 AttrDefId::FieldId(id) => { 408 AttrDefId::FieldId(id) => {
407 id.parent.child_source(db).map(|source| match &source[id.local_id] { 409 let map = db.fields_attrs_source_map(id.parent);
408 Either::Left(field) => ast::AttrsOwnerNode::new(field.clone()), 410 let file_id = id.parent.file_id(db);
409 Either::Right(field) => ast::AttrsOwnerNode::new(field.clone()), 411 let root = db.parse_or_expand(file_id).unwrap();
410 }) 412 let owner = match &map[id.local_id] {
413 Either::Left(it) => ast::AttrsOwnerNode::new(it.to_node(&root)),
414 Either::Right(it) => ast::AttrsOwnerNode::new(it.to_node(&root)),
415 };
416 InFile::new(file_id, owner)
411 } 417 }
412 AttrDefId::AdtId(adt) => match adt { 418 AttrDefId::AdtId(adt) => match adt {
413 AdtId::StructId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new), 419 AdtId::StructId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new),
@@ -415,10 +421,12 @@ impl AttrsWithOwner {
415 AdtId::EnumId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new), 421 AdtId::EnumId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new),
416 }, 422 },
417 AttrDefId::FunctionId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new), 423 AttrDefId::FunctionId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new),
418 AttrDefId::EnumVariantId(id) => id 424 AttrDefId::EnumVariantId(id) => {
419 .parent 425 let map = db.variants_attrs_source_map(id.parent);
420 .child_source(db) 426 let file_id = id.parent.lookup(db).id.file_id();
421 .map(|source| ast::AttrsOwnerNode::new(source[id.local_id].clone())), 427 let root = db.parse_or_expand(file_id).unwrap();
428 InFile::new(file_id, ast::AttrsOwnerNode::new(map[id.local_id].to_node(&root)))
429 }
422 AttrDefId::StaticId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new), 430 AttrDefId::StaticId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new),
423 AttrDefId::ConstId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new), 431 AttrDefId::ConstId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new),
424 AttrDefId::TraitId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new), 432 AttrDefId::TraitId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new),
@@ -463,7 +471,7 @@ impl AttrsWithOwner {
463 ) -> Option<(Documentation, DocsRangeMap)> { 471 ) -> Option<(Documentation, DocsRangeMap)> {
464 // FIXME: code duplication in `docs` above 472 // FIXME: code duplication in `docs` above
465 let docs = self.by_key("doc").attrs().flat_map(|attr| match attr.input.as_ref()? { 473 let docs = self.by_key("doc").attrs().flat_map(|attr| match attr.input.as_ref()? {
466 AttrInput::Literal(s) => Some((s, attr.index)), 474 AttrInput::Literal(s) => Some((s, attr.id)),
467 AttrInput::TokenTree(_) => None, 475 AttrInput::TokenTree(_) => None,
468 }); 476 });
469 let indent = docs 477 let indent = docs
@@ -555,8 +563,8 @@ impl AttrSourceMap {
555 /// the attribute represented by `Attr`. 563 /// the attribute represented by `Attr`.
556 pub fn source_of(&self, attr: &Attr) -> InFile<&Either<ast::Attr, ast::Comment>> { 564 pub fn source_of(&self, attr: &Attr) -> InFile<&Either<ast::Attr, ast::Comment>> {
557 self.attrs 565 self.attrs
558 .get(attr.index as usize) 566 .get(attr.id.0 as usize)
559 .unwrap_or_else(|| panic!("cannot find `Attr` at index {}", attr.index)) 567 .unwrap_or_else(|| panic!("cannot find `Attr` at index {:?}", attr.id))
560 .as_ref() 568 .as_ref()
561 } 569 }
562} 570}
@@ -567,7 +575,7 @@ pub struct DocsRangeMap {
567 // (docstring-line-range, attr_index, attr-string-range) 575 // (docstring-line-range, attr_index, attr-string-range)
568 // a mapping from the text range of a line of the [`Documentation`] to the attribute index and 576 // a mapping from the text range of a line of the [`Documentation`] to the attribute index and
569 // the original (untrimmed) syntax doc line 577 // the original (untrimmed) syntax doc line
570 mapping: Vec<(TextRange, u32, TextRange)>, 578 mapping: Vec<(TextRange, AttrId, TextRange)>,
571} 579}
572 580
573impl DocsRangeMap { 581impl DocsRangeMap {
@@ -580,7 +588,7 @@ impl DocsRangeMap {
580 588
581 let relative_range = range - line_docs_range.start(); 589 let relative_range = range - line_docs_range.start();
582 590
583 let &InFile { file_id, value: ref source } = &self.source[idx as usize]; 591 let &InFile { file_id, value: ref source } = &self.source[idx.0 as usize];
584 match source { 592 match source {
585 Either::Left(_) => None, // FIXME, figure out a nice way to handle doc attributes here 593 Either::Left(_) => None, // FIXME, figure out a nice way to handle doc attributes here
586 // as well as for whats done in syntax highlight doc injection 594 // as well as for whats done in syntax highlight doc injection
@@ -601,7 +609,7 @@ impl DocsRangeMap {
601 609
602#[derive(Debug, Clone, PartialEq, Eq)] 610#[derive(Debug, Clone, PartialEq, Eq)]
603pub struct Attr { 611pub struct Attr {
604 index: u32, 612 pub(crate) id: AttrId,
605 pub(crate) path: Interned<ModPath>, 613 pub(crate) path: Interned<ModPath>,
606 pub(crate) input: Option<AttrInput>, 614 pub(crate) input: Option<AttrInput>,
607} 615}
@@ -615,7 +623,7 @@ pub enum AttrInput {
615} 623}
616 624
617impl Attr { 625impl Attr {
618 fn from_src(ast: ast::Attr, hygiene: &Hygiene, index: u32) -> Option<Attr> { 626 fn from_src(ast: ast::Attr, hygiene: &Hygiene, id: AttrId) -> Option<Attr> {
619 let path = Interned::new(ModPath::from_src(ast.path()?, hygiene)?); 627 let path = Interned::new(ModPath::from_src(ast.path()?, hygiene)?);
620 let input = if let Some(ast::Expr::Literal(lit)) = ast.expr() { 628 let input = if let Some(ast::Expr::Literal(lit)) = ast.expr() {
621 let value = match lit.kind() { 629 let value = match lit.kind() {
@@ -628,7 +636,7 @@ impl Attr {
628 } else { 636 } else {
629 None 637 None
630 }; 638 };
631 Some(Attr { index, path, input }) 639 Some(Attr { id, path, input })
632 } 640 }
633 641
634 /// Parses this attribute as a `#[derive]`, returns an iterator that yields all contained paths 642 /// Parses this attribute as a `#[derive]`, returns an iterator that yields all contained paths
@@ -743,7 +751,38 @@ fn collect_attrs(
743 .chain(inner_docs.into_iter().flatten()) 751 .chain(inner_docs.into_iter().flatten())
744 .map(|docs_text| (docs_text.syntax().text_range().start(), Either::Right(docs_text))); 752 .map(|docs_text| (docs_text.syntax().text_range().start(), Either::Right(docs_text)));
745 // sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved 753 // sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved
746 let attrs: Vec<_> = docs.chain(attrs).sorted_by_key(|&(offset, _)| offset).collect(); 754 docs.chain(attrs).sorted_by_key(|&(offset, _)| offset).map(|(_, attr)| attr)
755}
756
757pub(crate) fn variants_attrs_source_map(
758 db: &dyn DefDatabase,
759 def: EnumId,
760) -> Arc<ArenaMap<LocalEnumVariantId, AstPtr<ast::Variant>>> {
761 let mut res = ArenaMap::default();
762 let child_source = def.child_source(db);
763
764 for (idx, variant) in child_source.value.iter() {
765 res.insert(idx, AstPtr::new(variant));
766 }
767
768 Arc::new(res)
769}
770
771pub(crate) fn fields_attrs_source_map(
772 db: &dyn DefDatabase,
773 def: VariantId,
774) -> Arc<ArenaMap<LocalFieldId, Either<AstPtr<ast::TupleField>, AstPtr<ast::RecordField>>>> {
775 let mut res = ArenaMap::default();
776 let child_source = def.child_source(db);
777
778 for (idx, variant) in child_source.value.iter() {
779 res.insert(
780 idx,
781 variant
782 .as_ref()
783 .either(|l| Either::Left(AstPtr::new(l)), |r| Either::Right(AstPtr::new(r))),
784 );
785 }
747 786
748 attrs.into_iter().map(|(_, attr)| attr) 787 Arc::new(res)
749} 788}