diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2021-03-17 16:00:43 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2021-03-17 16:00:43 +0000 |
commit | edf11480ceae1ef77d7084604011c0ef6f692c72 (patch) | |
tree | 6d1da24b8e275d8e36a12a48e1cdc9de0f8e1ec4 /crates/hir_def/src | |
parent | baa19991688c6bdd99b63b8dc6f539be44da0350 (diff) | |
parent | 5734b347ddfff0d285d3eecf5735cac30271696c (diff) |
Merge #8065
8065: Better handling of block doc comments r=Veykril a=Veykril
Moves doc string processing to `Attrs::docs`, as we need the indent info from all comments before being able to know how much to strip
Closes #7774
Co-authored-by: Lukas Wirth <[email protected]>
Diffstat (limited to 'crates/hir_def/src')
-rw-r--r-- | crates/hir_def/src/attr.rs | 105 |
1 files changed, 56 insertions, 49 deletions
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs index 7ba53ee5c..b7353d868 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 | } |
@@ -162,7 +148,7 @@ impl RawAttrs { | |||
162 | let attr = ast::Attr::parse(&format!("#[{}]", tree)).ok()?; | 148 | let attr = ast::Attr::parse(&format!("#[{}]", tree)).ok()?; |
163 | // FIXME hygiene | 149 | // FIXME hygiene |
164 | let hygiene = Hygiene::new_unhygienic(); | 150 | let hygiene = Hygiene::new_unhygienic(); |
165 | Attr::from_src(attr, &hygiene).map(|attr| Attr { index, ..attr }) | 151 | Attr::from_src(attr, &hygiene, index) |
166 | }); | 152 | }); |
167 | 153 | ||
168 | let cfg_options = &crate_graph[krate].cfg_options; | 154 | let cfg_options = &crate_graph[krate].cfg_options; |
@@ -192,7 +178,7 @@ impl Attrs { | |||
192 | Some(it) => { | 178 | Some(it) => { |
193 | let raw_attrs = RawAttrs::from_attrs_owner( | 179 | let raw_attrs = RawAttrs::from_attrs_owner( |
194 | db, | 180 | db, |
195 | it.as_ref().map(|it| it as &dyn AttrsOwner), | 181 | it.as_ref().map(|it| it as &dyn ast::AttrsOwner), |
196 | ); | 182 | ); |
197 | match mod_data.definition_source(db) { | 183 | match mod_data.definition_source(db) { |
198 | InFile { file_id, value: ModuleSource::SourceFile(file) } => raw_attrs | 184 | InFile { file_id, value: ModuleSource::SourceFile(file) } => raw_attrs |
@@ -203,9 +189,9 @@ impl Attrs { | |||
203 | None => RawAttrs::from_attrs_owner( | 189 | None => RawAttrs::from_attrs_owner( |
204 | db, | 190 | db, |
205 | mod_data.definition_source(db).as_ref().map(|src| match src { | 191 | mod_data.definition_source(db).as_ref().map(|src| match src { |
206 | ModuleSource::SourceFile(file) => file as &dyn AttrsOwner, | 192 | ModuleSource::SourceFile(file) => file as &dyn ast::AttrsOwner, |
207 | ModuleSource::Module(module) => module as &dyn AttrsOwner, | 193 | ModuleSource::Module(module) => module as &dyn ast::AttrsOwner, |
208 | ModuleSource::BlockExpr(block) => block as &dyn AttrsOwner, | 194 | ModuleSource::BlockExpr(block) => block as &dyn ast::AttrsOwner, |
209 | }), | 195 | }), |
210 | ), | 196 | ), |
211 | } | 197 | } |
@@ -263,7 +249,7 @@ impl Attrs { | |||
263 | let mut res = ArenaMap::default(); | 249 | let mut res = ArenaMap::default(); |
264 | 250 | ||
265 | for (id, var) in src.value.iter() { | 251 | for (id, var) in src.value.iter() { |
266 | 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)) |
267 | .filter(db, krate); | 253 | .filter(db, krate); |
268 | 254 | ||
269 | res.insert(id, attrs) | 255 | res.insert(id, attrs) |
@@ -297,7 +283,7 @@ impl Attrs { | |||
297 | /// Constructs a map that maps the lowered `Attr`s in this `Attrs` back to its original syntax nodes. | 283 | /// Constructs a map that maps the lowered `Attr`s in this `Attrs` back to its original syntax nodes. |
298 | /// | 284 | /// |
299 | /// `owner` must be the original owner of the attributes. | 285 | /// `owner` must be the original owner of the attributes. |
300 | pub fn source_map(&self, owner: &dyn AttrsOwner) -> AttrSourceMap { | 286 | pub fn source_map(&self, owner: &dyn ast::AttrsOwner) -> AttrSourceMap { |
301 | AttrSourceMap { attrs: collect_attrs(owner).collect() } | 287 | AttrSourceMap { attrs: collect_attrs(owner).collect() } |
302 | } | 288 | } |
303 | 289 | ||
@@ -325,15 +311,34 @@ impl Attrs { | |||
325 | AttrInput::Literal(s) => Some(s), | 311 | AttrInput::Literal(s) => Some(s), |
326 | AttrInput::TokenTree(_) => None, | 312 | AttrInput::TokenTree(_) => None, |
327 | }); | 313 | }); |
328 | // FIXME: Replace `Itertools::intersperse` with `Iterator::intersperse[_with]` until the | 314 | let indent = docs |
329 | // libstd api gets stabilized (https://github.com/rust-lang/rust/issues/79524). | 315 | .clone() |
330 | let docs = Itertools::intersperse(docs, &SmolStr::new_inline("\n")) | 316 | .flat_map(|s| s.lines()) |
331 | .map(|it| it.as_str()) | 317 | .filter(|line| !line.chars().all(|c| c.is_whitespace())) |
332 | .collect::<String>(); | 318 | .map(|line| line.chars().take_while(|c| c.is_whitespace()).count()) |
333 | 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() { | ||
334 | None | 339 | None |
335 | } else { | 340 | } else { |
336 | Some(Documentation(docs)) | 341 | Some(Documentation(buf)) |
337 | } | 342 | } |
338 | } | 343 | } |
339 | } | 344 | } |
@@ -407,7 +412,7 @@ pub enum AttrInput { | |||
407 | } | 412 | } |
408 | 413 | ||
409 | impl Attr { | 414 | impl Attr { |
410 | fn from_src(ast: ast::Attr, hygiene: &Hygiene) -> Option<Attr> { | 415 | fn from_src(ast: ast::Attr, hygiene: &Hygiene, index: u32) -> Option<Attr> { |
411 | let path = ModPath::from_src(ast.path()?, hygiene)?; | 416 | let path = ModPath::from_src(ast.path()?, hygiene)?; |
412 | let input = if let Some(lit) = ast.literal() { | 417 | let input = if let Some(lit) = ast.literal() { |
413 | let value = match lit.kind() { | 418 | let value = match lit.kind() { |
@@ -420,7 +425,7 @@ impl Attr { | |||
420 | } else { | 425 | } else { |
421 | None | 426 | None |
422 | }; | 427 | }; |
423 | Some(Attr { index: 0, path, input }) | 428 | Some(Attr { index, path, input }) |
424 | } | 429 | } |
425 | 430 | ||
426 | /// Maps this lowered `Attr` back to its original syntax node. | 431 | /// Maps this lowered `Attr` back to its original syntax node. |
@@ -429,7 +434,7 @@ impl Attr { | |||
429 | /// | 434 | /// |
430 | /// 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 |
431 | /// the attribute represented by `Attr`. | 436 | /// the attribute represented by `Attr`. |
432 | 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> { |
433 | collect_attrs(owner).nth(self.index as usize).unwrap_or_else(|| { | 438 | collect_attrs(owner).nth(self.index as usize).unwrap_or_else(|| { |
434 | panic!("cannot find `Attr` at index {} in {}", self.index, owner.syntax()) | 439 | panic!("cannot find `Attr` at index {} in {}", self.index, owner.syntax()) |
435 | }) | 440 | }) |
@@ -508,7 +513,7 @@ impl<'a> AttrQuery<'a> { | |||
508 | self.attrs().next().is_some() | 513 | self.attrs().next().is_some() |
509 | } | 514 | } |
510 | 515 | ||
511 | pub fn attrs(self) -> impl Iterator<Item = &'a Attr> { | 516 | pub fn attrs(self) -> impl Iterator<Item = &'a Attr> + Clone { |
512 | let key = self.key; | 517 | let key = self.key; |
513 | self.attrs | 518 | self.attrs |
514 | .iter() | 519 | .iter() |
@@ -521,7 +526,7 @@ where | |||
521 | N: ast::AttrsOwner, | 526 | N: ast::AttrsOwner, |
522 | { | 527 | { |
523 | 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())); |
524 | 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)) |
525 | } | 530 | } |
526 | 531 | ||
527 | 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 { |
@@ -530,7 +535,9 @@ fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase | |||
530 | tree.raw_attrs(mod_item.into()).clone() | 535 | tree.raw_attrs(mod_item.into()).clone() |
531 | } | 536 | } |
532 | 537 | ||
533 | 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>> { | ||
534 | let (inner_attrs, inner_docs) = inner_attributes(owner.syntax()) | 541 | let (inner_attrs, inner_docs) = inner_attributes(owner.syntax()) |
535 | .map_or((None, None), |(attrs, docs)| ((Some(attrs), Some(docs)))); | 542 | .map_or((None, None), |(attrs, docs)| ((Some(attrs), Some(docs)))); |
536 | 543 | ||