aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonas Schievink <[email protected]>2021-05-10 20:50:42 +0100
committerJonas Schievink <[email protected]>2021-05-10 20:50:42 +0100
commitda08198bc94b9c51338cd94aeadc73324b373011 (patch)
tree6ff2b27cabbcd20448b986c60d7418da64832f7d
parenta87bec51485feb491f7b1b80c06a29d86ebaf681 (diff)
Rewrite `attr.rs` to allow using syntax-based indices
-rw-r--r--crates/hir_def/src/attr.rs141
-rw-r--r--crates/hir_def/src/lib.rs2
2 files changed, 84 insertions, 59 deletions
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index 469811ce8..aadd4e44a 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -101,17 +101,13 @@ impl RawAttrs {
101 hygiene: &Hygiene, 101 hygiene: &Hygiene,
102 ) -> Self { 102 ) -> Self {
103 let entries = collect_attrs(owner) 103 let entries = collect_attrs(owner)
104 .enumerate() 104 .flat_map(|(id, attr)| match attr {
105 .flat_map(|(i, attr)| { 105 Either::Left(attr) => Attr::from_src(db, attr, hygiene, id),
106 let index = AttrId(i as u32); 106 Either::Right(comment) => comment.doc_comment().map(|doc| Attr {
107 match attr { 107 id,
108 Either::Left(attr) => Attr::from_src(db, attr, hygiene, index), 108 input: Some(AttrInput::Literal(SmolStr::new(doc))),
109 Either::Right(comment) => comment.doc_comment().map(|doc| Attr { 109 path: Interned::new(ModPath::from(hir_expand::name!(doc))),
110 id: index, 110 }),
111 input: Some(AttrInput::Literal(SmolStr::new(doc))),
112 path: Interned::new(ModPath::from(hir_expand::name!(doc))),
113 }),
114 }
115 }) 111 })
116 .collect::<Arc<_>>(); 112 .collect::<Arc<_>>();
117 113
@@ -124,6 +120,7 @@ impl RawAttrs {
124 } 120 }
125 121
126 pub(crate) fn merge(&self, other: Self) -> Self { 122 pub(crate) fn merge(&self, other: Self) -> Self {
123 // FIXME: This needs to fixup `AttrId`s
127 match (&self.entries, &other.entries) { 124 match (&self.entries, &other.entries) {
128 (None, None) => Self::EMPTY, 125 (None, None) => Self::EMPTY,
129 (Some(entries), None) | (None, Some(entries)) => { 126 (Some(entries), None) | (None, Some(entries)) => {
@@ -375,39 +372,26 @@ impl AttrsWithOwner {
375 372
376 let def_map = module.def_map(db); 373 let def_map = module.def_map(db);
377 let mod_data = &def_map[module.local_id]; 374 let mod_data = &def_map[module.local_id];
378 let attrs = match mod_data.declaration_source(db) { 375 match mod_data.declaration_source(db) {
379 Some(it) => { 376 Some(it) => {
380 let mut attrs: Vec<_> = collect_attrs(&it.value as &dyn ast::AttrsOwner) 377 let mut map = AttrSourceMap::new(InFile::new(it.file_id, &it.value));
381 .map(|attr| InFile::new(it.file_id, attr))
382 .collect();
383 if let InFile { file_id, value: ModuleSource::SourceFile(file) } = 378 if let InFile { file_id, value: ModuleSource::SourceFile(file) } =
384 mod_data.definition_source(db) 379 mod_data.definition_source(db)
385 { 380 {
386 attrs.extend( 381 map.merge(AttrSourceMap::new(InFile::new(file_id, &file)));
387 collect_attrs(&file as &dyn ast::AttrsOwner)
388 .map(|attr| InFile::new(file_id, attr)),
389 )
390 } 382 }
391 attrs 383 return map;
392 } 384 }
393 None => { 385 None => {
394 let InFile { file_id, value } = mod_data.definition_source(db); 386 let InFile { file_id, value } = mod_data.definition_source(db);
395 match &value { 387 let attrs_owner = match &value {
396 ModuleSource::SourceFile(file) => { 388 ModuleSource::SourceFile(file) => file as &dyn ast::AttrsOwner,
397 collect_attrs(file as &dyn ast::AttrsOwner) 389 ModuleSource::Module(module) => module as &dyn ast::AttrsOwner,
398 } 390 ModuleSource::BlockExpr(block) => block as &dyn ast::AttrsOwner,
399 ModuleSource::Module(module) => { 391 };
400 collect_attrs(module as &dyn ast::AttrsOwner) 392 return AttrSourceMap::new(InFile::new(file_id, attrs_owner));
401 }
402 ModuleSource::BlockExpr(block) => {
403 collect_attrs(block as &dyn ast::AttrsOwner)
404 }
405 }
406 .map(|attr| InFile::new(file_id, attr))
407 .collect()
408 } 393 }
409 }; 394 }
410 return AttrSourceMap { attrs };
411 } 395 }
412 AttrDefId::FieldId(id) => { 396 AttrDefId::FieldId(id) => {
413 let map = db.fields_attrs_source_map(id.parent); 397 let map = db.fields_attrs_source_map(id.parent);
@@ -462,11 +446,7 @@ impl AttrsWithOwner {
462 }, 446 },
463 }; 447 };
464 448
465 AttrSourceMap { 449 AttrSourceMap::new(owner.as_ref().map(|node| node as &dyn AttrsOwner))
466 attrs: collect_attrs(&owner.value)
467 .map(|attr| InFile::new(owner.file_id, attr))
468 .collect(),
469 }
470 } 450 }
471 451
472 pub fn docs_with_rangemap( 452 pub fn docs_with_rangemap(
@@ -518,7 +498,7 @@ impl AttrsWithOwner {
518 if buf.is_empty() { 498 if buf.is_empty() {
519 None 499 None
520 } else { 500 } else {
521 Some((Documentation(buf), DocsRangeMap { mapping, source: self.source_map(db).attrs })) 501 Some((Documentation(buf), DocsRangeMap { mapping, source_map: self.source_map(db) }))
522 } 502 }
523 } 503 }
524} 504}
@@ -559,27 +539,59 @@ fn inner_attributes(
559} 539}
560 540
561pub struct AttrSourceMap { 541pub struct AttrSourceMap {
562 attrs: Vec<InFile<Either<ast::Attr, ast::Comment>>>, 542 attrs: Vec<InFile<ast::Attr>>,
543 doc_comments: Vec<InFile<ast::Comment>>,
563} 544}
564 545
565impl AttrSourceMap { 546impl AttrSourceMap {
547 fn new(owner: InFile<&dyn ast::AttrsOwner>) -> Self {
548 let mut attrs = Vec::new();
549 let mut doc_comments = Vec::new();
550 for (_, attr) in collect_attrs(owner.value) {
551 match attr {
552 Either::Left(attr) => attrs.push(owner.with_value(attr)),
553 Either::Right(comment) => doc_comments.push(owner.with_value(comment)),
554 }
555 }
556
557 Self { attrs, doc_comments }
558 }
559
560 fn merge(&mut self, other: Self) {
561 self.attrs.extend(other.attrs);
562 self.doc_comments.extend(other.doc_comments);
563 }
564
566 /// Maps the lowered `Attr` back to its original syntax node. 565 /// Maps the lowered `Attr` back to its original syntax node.
567 /// 566 ///
568 /// `attr` must come from the `owner` used for AttrSourceMap 567 /// `attr` must come from the `owner` used for AttrSourceMap
569 /// 568 ///
570 /// Note that the returned syntax node might be a `#[cfg_attr]`, or a doc comment, instead of 569 /// Note that the returned syntax node might be a `#[cfg_attr]`, or a doc comment, instead of
571 /// the attribute represented by `Attr`. 570 /// the attribute represented by `Attr`.
572 pub fn source_of(&self, attr: &Attr) -> InFile<&Either<ast::Attr, ast::Comment>> { 571 pub fn source_of(&self, attr: &Attr) -> InFile<Either<ast::Attr, ast::Comment>> {
573 self.attrs 572 self.source_of_id(attr.id)
574 .get(attr.id.0 as usize) 573 }
575 .unwrap_or_else(|| panic!("cannot find `Attr` at index {:?}", attr.id)) 574
576 .as_ref() 575 fn source_of_id(&self, id: AttrId) -> InFile<Either<ast::Attr, ast::Comment>> {
576 if id.is_doc_comment {
577 self.doc_comments
578 .get(id.ast_index as usize)
579 .unwrap_or_else(|| panic!("cannot find doc comment at index {:?}", id))
580 .clone()
581 .map(|attr| Either::Right(attr))
582 } else {
583 self.attrs
584 .get(id.ast_index as usize)
585 .unwrap_or_else(|| panic!("cannot find `Attr` at index {:?}", id))
586 .clone()
587 .map(|attr| Either::Left(attr))
588 }
577 } 589 }
578} 590}
579 591
580/// A struct to map text ranges from [`Documentation`] back to TextRanges in the syntax tree. 592/// A struct to map text ranges from [`Documentation`] back to TextRanges in the syntax tree.
581pub struct DocsRangeMap { 593pub struct DocsRangeMap {
582 source: Vec<InFile<Either<ast::Attr, ast::Comment>>>, 594 source_map: AttrSourceMap,
583 // (docstring-line-range, attr_index, attr-string-range) 595 // (docstring-line-range, attr_index, attr-string-range)
584 // a mapping from the text range of a line of the [`Documentation`] to the attribute index and 596 // a mapping from the text range of a line of the [`Documentation`] to the attribute index and
585 // the original (untrimmed) syntax doc line 597 // the original (untrimmed) syntax doc line
@@ -596,7 +608,7 @@ impl DocsRangeMap {
596 608
597 let relative_range = range - line_docs_range.start(); 609 let relative_range = range - line_docs_range.start();
598 610
599 let &InFile { file_id, value: ref source } = &self.source[idx.0 as usize]; 611 let &InFile { file_id, value: ref source } = &self.source_map.source_of_id(idx);
600 match source { 612 match source {
601 Either::Left(_) => None, // FIXME, figure out a nice way to handle doc attributes here 613 Either::Left(_) => None, // FIXME, figure out a nice way to handle doc attributes here
602 // as well as for whats done in syntax highlight doc injection 614 // as well as for whats done in syntax highlight doc injection
@@ -616,7 +628,10 @@ impl DocsRangeMap {
616} 628}
617 629
618#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 630#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
619pub struct AttrId(pub u32); 631pub(crate) struct AttrId {
632 is_doc_comment: bool,
633 pub(crate) ast_index: u32,
634}
620 635
621#[derive(Debug, Clone, PartialEq, Eq)] 636#[derive(Debug, Clone, PartialEq, Eq)]
622pub struct Attr { 637pub struct Attr {
@@ -752,22 +767,32 @@ fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase
752 767
753fn collect_attrs( 768fn collect_attrs(
754 owner: &dyn ast::AttrsOwner, 769 owner: &dyn ast::AttrsOwner,
755) -> impl Iterator<Item = Either<ast::Attr, ast::Comment>> { 770) -> impl Iterator<Item = (AttrId, Either<ast::Attr, ast::Comment>)> {
756 let (inner_attrs, inner_docs) = inner_attributes(owner.syntax()) 771 let (inner_attrs, inner_docs) = inner_attributes(owner.syntax())
757 .map_or((None, None), |(attrs, docs)| (Some(attrs), Some(docs))); 772 .map_or((None, None), |(attrs, docs)| (Some(attrs), Some(docs)));
758 773
759 let outer_attrs = owner.attrs().filter(|attr| attr.kind().is_outer()); 774 let outer_attrs = owner.attrs().filter(|attr| attr.kind().is_outer());
760 let attrs = outer_attrs 775 let attrs =
761 .chain(inner_attrs.into_iter().flatten()) 776 outer_attrs.chain(inner_attrs.into_iter().flatten()).enumerate().map(|(idx, attr)| {
762 .map(|attr| (attr.syntax().text_range().start(), Either::Left(attr))); 777 (
778 AttrId { ast_index: idx as u32, is_doc_comment: false },
779 attr.syntax().text_range().start(),
780 Either::Left(attr),
781 )
782 });
763 783
764 let outer_docs = 784 let outer_docs =
765 ast::CommentIter::from_syntax_node(owner.syntax()).filter(ast::Comment::is_outer); 785 ast::CommentIter::from_syntax_node(owner.syntax()).filter(ast::Comment::is_outer);
766 let docs = outer_docs 786 let docs =
767 .chain(inner_docs.into_iter().flatten()) 787 outer_docs.chain(inner_docs.into_iter().flatten()).enumerate().map(|(idx, docs_text)| {
768 .map(|docs_text| (docs_text.syntax().text_range().start(), Either::Right(docs_text))); 788 (
789 AttrId { ast_index: idx as u32, is_doc_comment: true },
790 docs_text.syntax().text_range().start(),
791 Either::Right(docs_text),
792 )
793 });
769 // sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved 794 // sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved
770 docs.chain(attrs).sorted_by_key(|&(offset, _)| offset).map(|(_, attr)| attr) 795 docs.chain(attrs).sorted_by_key(|&(_, offset, _)| offset).map(|(id, _, attr)| (id, attr))
771} 796}
772 797
773pub(crate) fn variants_attrs_source_map( 798pub(crate) fn variants_attrs_source_map(
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs
index 869e0280e..a82ea5957 100644
--- a/crates/hir_def/src/lib.rs
+++ b/crates/hir_def/src/lib.rs
@@ -753,7 +753,7 @@ fn derive_macro_as_call_id(
753 MacroCallKind::Derive { 753 MacroCallKind::Derive {
754 ast_id: item_attr.ast_id, 754 ast_id: item_attr.ast_id,
755 derive_name: last_segment.to_string(), 755 derive_name: last_segment.to_string(),
756 derive_attr_index: derive_attr.0, 756 derive_attr_index: derive_attr.ast_index,
757 }, 757 },
758 ) 758 )
759 .into(); 759 .into();