diff options
Diffstat (limited to 'crates/hir_def/src/attr.rs')
-rw-r--r-- | crates/hir_def/src/attr.rs | 146 |
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 { | |||
76 | impl RawAttrs { | 76 | impl 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 | ||
381 | pub struct AttrSourceMap { | ||
382 | attrs: Vec<Either<ast::Attr, ast::Comment>>, | ||
383 | } | ||
384 | |||
385 | impl 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)] |
369 | pub struct Attr { | 400 | pub struct Attr { |
370 | index: u32, | 401 | index: u32, |
@@ -381,7 +412,7 @@ pub enum AttrInput { | |||
381 | } | 412 | } |
382 | 413 | ||
383 | impl Attr { | 414 | impl 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 | ||
494 | fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase) -> RawAttrs { | 532 | fn 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 | ||
500 | fn collect_attrs(owner: &dyn AttrsOwner) -> impl Iterator<Item = Either<ast::Attr, ast::Comment>> { | 538 | fn 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 | ||