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.rs146
1 files changed, 93 insertions, 53 deletions
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index b0b4b5052..e4c84afbf 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -76,37 +76,23 @@ impl ops::Deref for Attrs {
76impl RawAttrs { 76impl RawAttrs {
77 pub(crate) const EMPTY: Self = Self { entries: None }; 77 pub(crate) const EMPTY: Self = Self { entries: None };
78 78
79 pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Self { 79 pub(crate) fn new(owner: &dyn ast::AttrsOwner, hygiene: &Hygiene) -> Self {
80 let attrs: Vec<_> = collect_attrs(owner).collect(); 80 let entries = collect_attrs(owner)
81 let entries = if attrs.is_empty() { 81 .enumerate()
82 // Avoid heap allocation 82 .flat_map(|(i, attr)| match attr {
83 None 83 Either::Left(attr) => Attr::from_src(attr, hygiene, i as u32),
84 } else { 84 Either::Right(comment) => comment.doc_comment().map(|doc| Attr {
85 Some( 85 index: i as u32,
86 attrs 86 input: Some(AttrInput::Literal(SmolStr::new(doc))),
87 .into_iter() 87 path: ModPath::from(hir_expand::name!(doc)),
88 .enumerate() 88 }),
89 .flat_map(|(i, attr)| match attr { 89 })
90 Either::Left(attr) => Attr::from_src(attr, hygiene).map(|attr| (i, attr)), 90 .collect::<Arc<_>>();
91 Either::Right(comment) => comment.doc_comment().map(|doc| { 91
92 ( 92 Self { entries: if entries.is_empty() { None } else { Some(entries) } }
93 i,
94 Attr {
95 index: 0,
96 input: Some(AttrInput::Literal(SmolStr::new(doc))),
97 path: ModPath::from(hir_expand::name!(doc)),
98 },
99 )
100 }),
101 })
102 .map(|(i, attr)| Attr { index: i as u32, ..attr })
103 .collect(),
104 )
105 };
106 Self { entries }
107 } 93 }
108 94
109 fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn AttrsOwner>) -> Self { 95 fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn ast::AttrsOwner>) -> Self {
110 let hygiene = Hygiene::new(db.upcast(), owner.file_id); 96 let hygiene = Hygiene::new(db.upcast(), owner.file_id);
111 Self::new(owner.value, &hygiene) 97 Self::new(owner.value, &hygiene)
112 } 98 }
@@ -136,16 +122,15 @@ impl RawAttrs {
136 let new_attrs = self 122 let new_attrs = self
137 .iter() 123 .iter()
138 .flat_map(|attr| -> SmallVec<[_; 1]> { 124 .flat_map(|attr| -> SmallVec<[_; 1]> {
139 let attr = attr.clone();
140 let is_cfg_attr = 125 let is_cfg_attr =
141 attr.path.as_ident().map_or(false, |name| *name == hir_expand::name![cfg_attr]); 126 attr.path.as_ident().map_or(false, |name| *name == hir_expand::name![cfg_attr]);
142 if !is_cfg_attr { 127 if !is_cfg_attr {
143 return smallvec![attr]; 128 return smallvec![attr.clone()];
144 } 129 }
145 130
146 let subtree = match &attr.input { 131 let subtree = match &attr.input {
147 Some(AttrInput::TokenTree(it)) => it, 132 Some(AttrInput::TokenTree(it)) => it,
148 _ => return smallvec![attr], 133 _ => return smallvec![attr.clone()],
149 }; 134 };
150 135
151 // Input subtree is: `(cfg, $(attr),+)` 136 // Input subtree is: `(cfg, $(attr),+)`
@@ -157,11 +142,13 @@ impl RawAttrs {
157 let cfg = parts.next().unwrap(); 142 let cfg = parts.next().unwrap();
158 let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg.to_vec() }; 143 let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg.to_vec() };
159 let cfg = CfgExpr::parse(&cfg); 144 let cfg = CfgExpr::parse(&cfg);
145 let index = attr.index;
160 let attrs = parts.filter(|a| !a.is_empty()).filter_map(|attr| { 146 let attrs = parts.filter(|a| !a.is_empty()).filter_map(|attr| {
161 let tree = Subtree { delimiter: None, token_trees: attr.to_vec() }; 147 let tree = Subtree { delimiter: None, token_trees: attr.to_vec() };
162 let attr = ast::Attr::parse(&format!("#[{}]", tree)).ok()?; 148 let attr = ast::Attr::parse(&format!("#[{}]", tree)).ok()?;
163 let hygiene = Hygiene::new_unhygienic(); // FIXME 149 // FIXME hygiene
164 Attr::from_src(attr, &hygiene) 150 let hygiene = Hygiene::new_unhygienic();
151 Attr::from_src(attr, &hygiene, index)
165 }); 152 });
166 153
167 let cfg_options = &crate_graph[krate].cfg_options; 154 let cfg_options = &crate_graph[krate].cfg_options;
@@ -191,7 +178,7 @@ impl Attrs {
191 Some(it) => { 178 Some(it) => {
192 let raw_attrs = RawAttrs::from_attrs_owner( 179 let raw_attrs = RawAttrs::from_attrs_owner(
193 db, 180 db,
194 it.as_ref().map(|it| it as &dyn AttrsOwner), 181 it.as_ref().map(|it| it as &dyn ast::AttrsOwner),
195 ); 182 );
196 match mod_data.definition_source(db) { 183 match mod_data.definition_source(db) {
197 InFile { file_id, value: ModuleSource::SourceFile(file) } => raw_attrs 184 InFile { file_id, value: ModuleSource::SourceFile(file) } => raw_attrs
@@ -202,9 +189,9 @@ impl Attrs {
202 None => RawAttrs::from_attrs_owner( 189 None => RawAttrs::from_attrs_owner(
203 db, 190 db,
204 mod_data.definition_source(db).as_ref().map(|src| match src { 191 mod_data.definition_source(db).as_ref().map(|src| match src {
205 ModuleSource::SourceFile(file) => file as &dyn AttrsOwner, 192 ModuleSource::SourceFile(file) => file as &dyn ast::AttrsOwner,
206 ModuleSource::Module(module) => module as &dyn AttrsOwner, 193 ModuleSource::Module(module) => module as &dyn ast::AttrsOwner,
207 ModuleSource::BlockExpr(block) => block as &dyn AttrsOwner, 194 ModuleSource::BlockExpr(block) => block as &dyn ast::AttrsOwner,
208 }), 195 }),
209 ), 196 ),
210 } 197 }
@@ -262,7 +249,7 @@ impl Attrs {
262 let mut res = ArenaMap::default(); 249 let mut res = ArenaMap::default();
263 250
264 for (id, var) in src.value.iter() { 251 for (id, var) in src.value.iter() {
265 let attrs = RawAttrs::from_attrs_owner(db, src.with_value(var as &dyn AttrsOwner)) 252 let attrs = RawAttrs::from_attrs_owner(db, src.with_value(var as &dyn ast::AttrsOwner))
266 .filter(db, krate); 253 .filter(db, krate);
267 254
268 res.insert(id, attrs) 255 res.insert(id, attrs)
@@ -293,6 +280,13 @@ impl Attrs {
293 Arc::new(res) 280 Arc::new(res)
294 } 281 }
295 282
283 /// Constructs a map that maps the lowered `Attr`s in this `Attrs` back to its original syntax nodes.
284 ///
285 /// `owner` must be the original owner of the attributes.
286 pub fn source_map(&self, owner: &dyn ast::AttrsOwner) -> AttrSourceMap {
287 AttrSourceMap { attrs: collect_attrs(owner).collect() }
288 }
289
296 pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> { 290 pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> {
297 AttrQuery { attrs: self, key } 291 AttrQuery { attrs: self, key }
298 } 292 }
@@ -317,15 +311,34 @@ impl Attrs {
317 AttrInput::Literal(s) => Some(s), 311 AttrInput::Literal(s) => Some(s),
318 AttrInput::TokenTree(_) => None, 312 AttrInput::TokenTree(_) => None,
319 }); 313 });
320 // FIXME: Replace `Itertools::intersperse` with `Iterator::intersperse[_with]` until the 314 let indent = docs
321 // libstd api gets stabilized (https://github.com/rust-lang/rust/issues/79524). 315 .clone()
322 let docs = Itertools::intersperse(docs, &SmolStr::new_inline("\n")) 316 .flat_map(|s| s.lines())
323 .map(|it| it.as_str()) 317 .filter(|line| !line.chars().all(|c| c.is_whitespace()))
324 .collect::<String>(); 318 .map(|line| line.chars().take_while(|c| c.is_whitespace()).count())
325 if docs.is_empty() { 319 .min()
320 .unwrap_or(0);
321 let mut buf = String::new();
322 for doc in docs {
323 // str::lines doesn't yield anything for the empty string
324 if !doc.is_empty() {
325 buf.extend(Itertools::intersperse(
326 doc.lines().map(|line| {
327 line.char_indices()
328 .nth(indent)
329 .map_or(line, |(offset, _)| &line[offset..])
330 .trim_end()
331 }),
332 "\n",
333 ));
334 }
335 buf.push('\n');
336 }
337 buf.pop();
338 if buf.is_empty() {
326 None 339 None
327 } else { 340 } else {
328 Some(Documentation(docs)) 341 Some(Documentation(buf))
329 } 342 }
330 } 343 }
331} 344}
@@ -365,6 +378,24 @@ fn inner_attributes(
365 Some((attrs, docs)) 378 Some((attrs, docs))
366} 379}
367 380
381pub struct AttrSourceMap {
382 attrs: Vec<Either<ast::Attr, ast::Comment>>,
383}
384
385impl AttrSourceMap {
386 /// Maps the lowered `Attr` back to its original syntax node.
387 ///
388 /// `attr` must come from the `owner` used for AttrSourceMap
389 ///
390 /// Note that the returned syntax node might be a `#[cfg_attr]`, or a doc comment, instead of
391 /// the attribute represented by `Attr`.
392 pub fn source_of(&self, attr: &Attr) -> &Either<ast::Attr, ast::Comment> {
393 self.attrs
394 .get(attr.index as usize)
395 .unwrap_or_else(|| panic!("cannot find `Attr` at index {}", attr.index))
396 }
397}
398
368#[derive(Debug, Clone, PartialEq, Eq)] 399#[derive(Debug, Clone, PartialEq, Eq)]
369pub struct Attr { 400pub struct Attr {
370 index: u32, 401 index: u32,
@@ -381,7 +412,7 @@ pub enum AttrInput {
381} 412}
382 413
383impl Attr { 414impl Attr {
384 fn from_src(ast: ast::Attr, hygiene: &Hygiene) -> Option<Attr> { 415 fn from_src(ast: ast::Attr, hygiene: &Hygiene, index: u32) -> Option<Attr> {
385 let path = ModPath::from_src(ast.path()?, hygiene)?; 416 let path = ModPath::from_src(ast.path()?, hygiene)?;
386 let input = if let Some(lit) = ast.literal() { 417 let input = if let Some(lit) = ast.literal() {
387 let value = match lit.kind() { 418 let value = match lit.kind() {
@@ -394,7 +425,7 @@ impl Attr {
394 } else { 425 } else {
395 None 426 None
396 }; 427 };
397 Some(Attr { index: 0, path, input }) 428 Some(Attr { index, path, input })
398 } 429 }
399 430
400 /// Maps this lowered `Attr` back to its original syntax node. 431 /// Maps this lowered `Attr` back to its original syntax node.
@@ -403,7 +434,7 @@ impl Attr {
403 /// 434 ///
404 /// Note that the returned syntax node might be a `#[cfg_attr]`, or a doc comment, instead of 435 /// Note that the returned syntax node might be a `#[cfg_attr]`, or a doc comment, instead of
405 /// the attribute represented by `Attr`. 436 /// the attribute represented by `Attr`.
406 pub fn to_src(&self, owner: &dyn AttrsOwner) -> Either<ast::Attr, ast::Comment> { 437 pub fn to_src(&self, owner: &dyn ast::AttrsOwner) -> Either<ast::Attr, ast::Comment> {
407 collect_attrs(owner).nth(self.index as usize).unwrap_or_else(|| { 438 collect_attrs(owner).nth(self.index as usize).unwrap_or_else(|| {
408 panic!("cannot find `Attr` at index {} in {}", self.index, owner.syntax()) 439 panic!("cannot find `Attr` at index {} in {}", self.index, owner.syntax())
409 }) 440 })
@@ -448,6 +479,13 @@ impl Attr {
448 _ => None, 479 _ => None,
449 } 480 }
450 } 481 }
482
483 pub fn string_value(&self) -> Option<&SmolStr> {
484 match self.input.as_ref()? {
485 AttrInput::Literal(it) => Some(it),
486 _ => None,
487 }
488 }
451} 489}
452 490
453#[derive(Debug, Clone, Copy)] 491#[derive(Debug, Clone, Copy)]
@@ -475,7 +513,7 @@ impl<'a> AttrQuery<'a> {
475 self.attrs().next().is_some() 513 self.attrs().next().is_some()
476 } 514 }
477 515
478 pub(crate) fn attrs(self) -> impl Iterator<Item = &'a Attr> { 516 pub fn attrs(self) -> impl Iterator<Item = &'a Attr> + Clone {
479 let key = self.key; 517 let key = self.key;
480 self.attrs 518 self.attrs
481 .iter() 519 .iter()
@@ -488,16 +526,18 @@ where
488 N: ast::AttrsOwner, 526 N: ast::AttrsOwner,
489{ 527{
490 let src = InFile::new(src.file_id, src.to_node(db.upcast())); 528 let src = InFile::new(src.file_id, src.to_node(db.upcast()));
491 RawAttrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner)) 529 RawAttrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn ast::AttrsOwner))
492} 530}
493 531
494fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase) -> RawAttrs { 532fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase) -> RawAttrs {
495 let tree = db.item_tree(id.file_id); 533 let tree = id.item_tree(db);
496 let mod_item = N::id_to_mod_item(id.value); 534 let mod_item = N::id_to_mod_item(id.value);
497 tree.raw_attrs(mod_item.into()).clone() 535 tree.raw_attrs(mod_item.into()).clone()
498} 536}
499 537
500fn collect_attrs(owner: &dyn AttrsOwner) -> impl Iterator<Item = Either<ast::Attr, ast::Comment>> { 538fn collect_attrs(
539 owner: &dyn ast::AttrsOwner,
540) -> impl Iterator<Item = Either<ast::Attr, ast::Comment>> {
501 let (inner_attrs, inner_docs) = inner_attributes(owner.syntax()) 541 let (inner_attrs, inner_docs) = inner_attributes(owner.syntax())
502 .map_or((None, None), |(attrs, docs)| ((Some(attrs), Some(docs)))); 542 .map_or((None, None), |(attrs, docs)| ((Some(attrs), Some(docs))));
503 543