aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_hir/Cargo.toml2
-rw-r--r--crates/ra_hir/src/nameres/collector.rs67
-rw-r--r--crates/ra_hir/src/nameres/raw.rs17
-rw-r--r--crates/ra_hir/src/nameres/tests/mods.rs14
-rw-r--r--crates/ra_ide_api/Cargo.toml2
-rw-r--r--crates/ra_mbe/src/subtree_source.rs62
-rw-r--r--crates/ra_syntax/Cargo.toml1
-rw-r--r--crates/ra_syntax/src/ast.rs46
-rw-r--r--crates/ra_syntax/src/ast/traits.rs12
9 files changed, 159 insertions, 64 deletions
diff --git a/crates/ra_hir/Cargo.toml b/crates/ra_hir/Cargo.toml
index 285b3c63a..1a2f1b47c 100644
--- a/crates/ra_hir/Cargo.toml
+++ b/crates/ra_hir/Cargo.toml
@@ -27,4 +27,4 @@ chalk-ir = { git = "https://github.com/rust-lang/chalk.git" }
27lalrpop-intern = "0.15.1" 27lalrpop-intern = "0.15.1"
28 28
29[dev-dependencies] 29[dev-dependencies]
30insta = "0.9.0" 30insta = "0.10.0"
diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs
index 06b732215..7da2dcdff 100644
--- a/crates/ra_hir/src/nameres/collector.rs
+++ b/crates/ra_hir/src/nameres/collector.rs
@@ -483,7 +483,7 @@ struct ModCollector<'a, D> {
483 module_id: CrateModuleId, 483 module_id: CrateModuleId,
484 file_id: HirFileId, 484 file_id: HirFileId,
485 raw_items: &'a raw::RawItems, 485 raw_items: &'a raw::RawItems,
486 parent_module: Option<&'a Name>, 486 parent_module: Option<ParentModule<'a>>,
487} 487}
488 488
489impl<DB> ModCollector<'_, &'_ mut DefCollector<&'_ DB>> 489impl<DB> ModCollector<'_, &'_ mut DefCollector<&'_ DB>>
@@ -508,15 +508,16 @@ where
508 fn collect_module(&mut self, module: &raw::ModuleData) { 508 fn collect_module(&mut self, module: &raw::ModuleData) {
509 match module { 509 match module {
510 // inline module, just recurse 510 // inline module, just recurse
511 raw::ModuleData::Definition { name, items, ast_id } => { 511 raw::ModuleData::Definition { name, items, ast_id, attr_path } => {
512 let module_id = 512 let module_id =
513 self.push_child_module(name.clone(), ast_id.with_file_id(self.file_id), None); 513 self.push_child_module(name.clone(), ast_id.with_file_id(self.file_id), None);
514 let parent_module = ParentModule { name, attr_path: attr_path.as_ref() };
514 ModCollector { 515 ModCollector {
515 def_collector: &mut *self.def_collector, 516 def_collector: &mut *self.def_collector,
516 module_id, 517 module_id,
517 file_id: self.file_id, 518 file_id: self.file_id,
518 raw_items: self.raw_items, 519 raw_items: self.raw_items,
519 parent_module: Some(name), 520 parent_module: Some(parent_module),
520 } 521 }
521 .collect(&*items); 522 .collect(&*items);
522 } 523 }
@@ -530,7 +531,7 @@ where
530 name, 531 name,
531 is_root, 532 is_root,
532 attr_path.as_ref(), 533 attr_path.as_ref(),
533 self.parent_module, 534 self.parent_module.as_ref(),
534 ) { 535 ) {
535 Ok(file_id) => { 536 Ok(file_id) => {
536 let module_id = self.push_child_module(name.clone(), ast_id, Some(file_id)); 537 let module_id = self.push_child_module(name.clone(), ast_id, Some(file_id));
@@ -647,7 +648,7 @@ fn resolve_submodule(
647 name: &Name, 648 name: &Name,
648 is_root: bool, 649 is_root: bool,
649 attr_path: Option<&SmolStr>, 650 attr_path: Option<&SmolStr>,
650 parent_module: Option<&Name>, 651 parent_module: Option<&ParentModule>,
651) -> Result<FileId, RelativePathBuf> { 652) -> Result<FileId, RelativePathBuf> {
652 let file_id = file_id.original_file(db); 653 let file_id = file_id.original_file(db);
653 let source_root_id = db.file_source_root(file_id); 654 let source_root_id = db.file_source_root(file_id);
@@ -657,20 +658,49 @@ fn resolve_submodule(
657 let mod_name = path.file_stem().unwrap_or("unknown"); 658 let mod_name = path.file_stem().unwrap_or("unknown");
658 659
659 let resolve_mode = match (attr_path.filter(|p| !p.is_empty()), parent_module) { 660 let resolve_mode = match (attr_path.filter(|p| !p.is_empty()), parent_module) {
660 (Some(file_path), Some(parent_name)) => { 661 (Some(file_path), Some(parent_module)) => {
661 let file_path = normalize_attribute_path(file_path); 662 let file_path = normalize_attribute_path(file_path);
662 let path = dir_path.join(format!("{}/{}", parent_name, file_path)).normalize(); 663 match parent_module.attribute_path() {
663 ResolutionMode::InsideInlineModule(InsideInlineModuleMode::WithAttributePath(path)) 664 Some(parent_module_attr_path) => {
665 let path = dir_path
666 .join(format!(
667 "{}/{}",
668 normalize_attribute_path(parent_module_attr_path),
669 file_path
670 ))
671 .normalize();
672 ResolutionMode::InlineModuleWithAttributePath(
673 InsideInlineModuleMode::WithAttributePath(path),
674 )
675 }
676 None => {
677 let path =
678 dir_path.join(format!("{}/{}", parent_module.name, file_path)).normalize();
679 ResolutionMode::InsideInlineModule(InsideInlineModuleMode::WithAttributePath(
680 path,
681 ))
682 }
683 }
664 } 684 }
685 (None, Some(parent_module)) => match parent_module.attribute_path() {
686 Some(parent_module_attr_path) => {
687 let path = dir_path.join(format!(
688 "{}/{}.rs",
689 normalize_attribute_path(parent_module_attr_path),
690 name
691 ));
692 ResolutionMode::InlineModuleWithAttributePath(InsideInlineModuleMode::File(path))
693 }
694 None => {
695 let path = dir_path.join(format!("{}/{}.rs", parent_module.name, name));
696 ResolutionMode::InsideInlineModule(InsideInlineModuleMode::File(path))
697 }
698 },
665 (Some(file_path), None) => { 699 (Some(file_path), None) => {
666 let file_path = normalize_attribute_path(file_path); 700 let file_path = normalize_attribute_path(file_path);
667 let path = dir_path.join(file_path.as_ref()).normalize(); 701 let path = dir_path.join(file_path.as_ref()).normalize();
668 ResolutionMode::OutOfLine(OutOfLineMode::WithAttributePath(path)) 702 ResolutionMode::OutOfLine(OutOfLineMode::WithAttributePath(path))
669 } 703 }
670 (None, Some(parent_name)) => {
671 let path = dir_path.join(format!("{}/{}.rs", parent_name, name));
672 ResolutionMode::InsideInlineModule(InsideInlineModuleMode::File(path))
673 }
674 _ => { 704 _ => {
675 let is_dir_owner = is_root || mod_name == "mod"; 705 let is_dir_owner = is_root || mod_name == "mod";
676 if is_dir_owner { 706 if is_dir_owner {
@@ -743,6 +773,7 @@ impl InsideInlineModuleMode {
743enum ResolutionMode { 773enum ResolutionMode {
744 OutOfLine(OutOfLineMode), 774 OutOfLine(OutOfLineMode),
745 InsideInlineModule(InsideInlineModuleMode), 775 InsideInlineModule(InsideInlineModuleMode),
776 InlineModuleWithAttributePath(InsideInlineModuleMode),
746} 777}
747 778
748impl ResolutionMode { 779impl ResolutionMode {
@@ -752,6 +783,7 @@ impl ResolutionMode {
752 match self { 783 match self {
753 OutOfLine(mode) => mode.resolve(source_root), 784 OutOfLine(mode) => mode.resolve(source_root),
754 InsideInlineModule(mode) => mode.resolve(source_root), 785 InsideInlineModule(mode) => mode.resolve(source_root),
786 InlineModuleWithAttributePath(mode) => mode.resolve(source_root),
755 } 787 }
756 } 788 }
757} 789}
@@ -773,6 +805,17 @@ fn resolve_find_result(
773 } 805 }
774} 806}
775 807
808struct ParentModule<'a> {
809 name: &'a Name,
810 attr_path: Option<&'a SmolStr>,
811}
812
813impl<'a> ParentModule<'a> {
814 pub fn attribute_path(&self) -> Option<&SmolStr> {
815 self.attr_path.filter(|p| !p.is_empty())
816 }
817}
818
776#[cfg(test)] 819#[cfg(test)]
777mod tests { 820mod tests {
778 use ra_db::SourceDatabase; 821 use ra_db::SourceDatabase;
diff --git a/crates/ra_hir/src/nameres/raw.rs b/crates/ra_hir/src/nameres/raw.rs
index 8517f3c43..584e15e29 100644
--- a/crates/ra_hir/src/nameres/raw.rs
+++ b/crates/ra_hir/src/nameres/raw.rs
@@ -130,8 +130,17 @@ impl_arena_id!(Module);
130 130
131#[derive(Debug, PartialEq, Eq)] 131#[derive(Debug, PartialEq, Eq)]
132pub(super) enum ModuleData { 132pub(super) enum ModuleData {
133 Declaration { name: Name, ast_id: FileAstId<ast::Module>, attr_path: Option<SmolStr> }, 133 Declaration {
134 Definition { name: Name, ast_id: FileAstId<ast::Module>, items: Vec<RawItem> }, 134 name: Name,
135 ast_id: FileAstId<ast::Module>,
136 attr_path: Option<SmolStr>,
137 },
138 Definition {
139 name: Name,
140 ast_id: FileAstId<ast::Module>,
141 items: Vec<RawItem>,
142 attr_path: Option<SmolStr>,
143 },
135} 144}
136 145
137#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 146#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -256,9 +265,9 @@ impl RawItemsCollector {
256 None => return, 265 None => return,
257 }; 266 };
258 267
259 let attr_path = extract_mod_path_attribute(&module);
260 let ast_id = self.source_ast_id_map.ast_id(&module); 268 let ast_id = self.source_ast_id_map.ast_id(&module);
261 if module.has_semi() { 269 if module.has_semi() {
270 let attr_path = extract_mod_path_attribute(&module);
262 let item = 271 let item =
263 self.raw_items.modules.alloc(ModuleData::Declaration { name, ast_id, attr_path }); 272 self.raw_items.modules.alloc(ModuleData::Declaration { name, ast_id, attr_path });
264 self.push_item(current_module, RawItem::Module(item)); 273 self.push_item(current_module, RawItem::Module(item));
@@ -266,10 +275,12 @@ impl RawItemsCollector {
266 } 275 }
267 276
268 if let Some(item_list) = module.item_list() { 277 if let Some(item_list) = module.item_list() {
278 let attr_path = extract_mod_path_attribute(&module);
269 let item = self.raw_items.modules.alloc(ModuleData::Definition { 279 let item = self.raw_items.modules.alloc(ModuleData::Definition {
270 name, 280 name,
271 ast_id, 281 ast_id,
272 items: Vec::new(), 282 items: Vec::new(),
283 attr_path,
273 }); 284 });
274 self.process_module(Some(item), item_list); 285 self.process_module(Some(item), item_list);
275 self.push_item(current_module, RawItem::Module(item)); 286 self.push_item(current_module, RawItem::Module(item));
diff --git a/crates/ra_hir/src/nameres/tests/mods.rs b/crates/ra_hir/src/nameres/tests/mods.rs
index 382728149..6dd18df1a 100644
--- a/crates/ra_hir/src/nameres/tests/mods.rs
+++ b/crates/ra_hir/src/nameres/tests/mods.rs
@@ -336,9 +336,7 @@ fn module_resolution_explicit_path_mod_rs_with_win_separator() {
336 "###); 336 "###);
337} 337}
338 338
339// FIXME: issue #1529. not support out-of-line modules inside inline.
340#[test] 339#[test]
341#[ignore]
342fn module_resolution_decl_inside_inline_module_with_path_attribute() { 340fn module_resolution_decl_inside_inline_module_with_path_attribute() {
343 let map = def_map_with_crate_graph( 341 let map = def_map_with_crate_graph(
344 r###" 342 r###"
@@ -397,9 +395,7 @@ fn module_resolution_decl_inside_inline_module() {
397 "###); 395 "###);
398} 396}
399 397
400// FIXME: issue #1529. not support out-of-line modules inside inline.
401#[test] 398#[test]
402#[ignore]
403fn module_resolution_decl_inside_inline_module_2_with_path_attribute() { 399fn module_resolution_decl_inside_inline_module_2_with_path_attribute() {
404 let map = def_map_with_crate_graph( 400 let map = def_map_with_crate_graph(
405 r###" 401 r###"
@@ -429,9 +425,7 @@ fn module_resolution_decl_inside_inline_module_2_with_path_attribute() {
429 "###); 425 "###);
430} 426}
431 427
432// FIXME: issue #1529. not support out-of-line modules inside inline.
433#[test] 428#[test]
434#[ignore]
435fn module_resolution_decl_inside_inline_module_3() { 429fn module_resolution_decl_inside_inline_module_3() {
436 let map = def_map_with_crate_graph( 430 let map = def_map_with_crate_graph(
437 r###" 431 r###"
@@ -462,9 +456,7 @@ fn module_resolution_decl_inside_inline_module_3() {
462 "###); 456 "###);
463} 457}
464 458
465// FIXME: issue #1529. not support out-of-line modules inside inline.
466#[test] 459#[test]
467#[ignore]
468fn module_resolution_decl_inside_inline_module_empty_path() { 460fn module_resolution_decl_inside_inline_module_empty_path() {
469 let map = def_map_with_crate_graph( 461 let map = def_map_with_crate_graph(
470 r###" 462 r###"
@@ -475,7 +467,7 @@ fn module_resolution_decl_inside_inline_module_empty_path() {
475 mod bar; 467 mod bar;
476 } 468 }
477 469
478 //- /users.rs 470 //- /foo/users.rs
479 pub struct Baz; 471 pub struct Baz;
480 "###, 472 "###,
481 crate_graph! { 473 crate_graph! {
@@ -520,9 +512,7 @@ fn module_resolution_decl_empty_path() {
520 "###); 512 "###);
521} 513}
522 514
523// FIXME: issue #1529. not support out-of-line modules inside inline.
524#[test] 515#[test]
525#[ignore]
526fn module_resolution_decl_inside_inline_module_relative_path() { 516fn module_resolution_decl_inside_inline_module_relative_path() {
527 let map = def_map_with_crate_graph( 517 let map = def_map_with_crate_graph(
528 r###" 518 r###"
@@ -660,9 +650,7 @@ fn module_resolution_decl_inside_inline_module_in_non_crate_root() {
660 "###); 650 "###);
661} 651}
662 652
663// FIXME: issue #1529. not support out-of-line modules inside inline.
664#[test] 653#[test]
665#[ignore]
666fn module_resolution_decl_inside_inline_module_in_non_crate_root_2() { 654fn module_resolution_decl_inside_inline_module_in_non_crate_root_2() {
667 let map = def_map_with_crate_graph( 655 let map = def_map_with_crate_graph(
668 r###" 656 r###"
diff --git a/crates/ra_ide_api/Cargo.toml b/crates/ra_ide_api/Cargo.toml
index dd11ec0f6..a7dc0b63a 100644
--- a/crates/ra_ide_api/Cargo.toml
+++ b/crates/ra_ide_api/Cargo.toml
@@ -27,7 +27,7 @@ test_utils = { path = "../test_utils" }
27ra_assists = { path = "../ra_assists" } 27ra_assists = { path = "../ra_assists" }
28 28
29[dev-dependencies] 29[dev-dependencies]
30insta = "0.9.0" 30insta = "0.10.0"
31 31
32[dev-dependencies.proptest] 32[dev-dependencies.proptest]
33version = "0.9.0" 33version = "0.9.0"
diff --git a/crates/ra_mbe/src/subtree_source.rs b/crates/ra_mbe/src/subtree_source.rs
index 6603ff34d..9d6d0133f 100644
--- a/crates/ra_mbe/src/subtree_source.rs
+++ b/crates/ra_mbe/src/subtree_source.rs
@@ -1,6 +1,6 @@
1use ra_parser::{Token, TokenSource}; 1use ra_parser::{Token, TokenSource};
2use ra_syntax::{classify_literal, SmolStr, SyntaxKind, SyntaxKind::*, T}; 2use ra_syntax::{classify_literal, SmolStr, SyntaxKind, SyntaxKind::*, T};
3use std::cell::{Cell, RefCell}; 3use std::cell::{Cell, Ref, RefCell};
4use tt::buffer::{Cursor, TokenBuffer}; 4use tt::buffer::{Cursor, TokenBuffer};
5 5
6#[derive(Debug, Clone, Eq, PartialEq)] 6#[derive(Debug, Clone, Eq, PartialEq)]
@@ -20,8 +20,8 @@ impl<'a> SubtreeTokenSource<'a> {
20 // Helper function used in test 20 // Helper function used in test
21 #[cfg(test)] 21 #[cfg(test)]
22 pub fn text(&self) -> SmolStr { 22 pub fn text(&self) -> SmolStr {
23 match self.get(self.curr.1) { 23 match *self.get(self.curr.1) {
24 Some(tt) => tt.text, 24 Some(ref tt) => tt.text.clone(),
25 _ => SmolStr::new(""), 25 _ => SmolStr::new(""),
26 } 26 }
27 } 27 }
@@ -41,44 +41,46 @@ impl<'a> SubtreeTokenSource<'a> {
41 } 41 }
42 42
43 fn mk_token(&self, pos: usize) -> Token { 43 fn mk_token(&self, pos: usize) -> Token {
44 match self.get(pos) { 44 match *self.get(pos) {
45 Some(tt) => Token { kind: tt.kind, is_jointed_to_next: tt.is_joint_to_next }, 45 Some(ref tt) => Token { kind: tt.kind, is_jointed_to_next: tt.is_joint_to_next },
46 None => Token { kind: EOF, is_jointed_to_next: false }, 46 None => Token { kind: EOF, is_jointed_to_next: false },
47 } 47 }
48 } 48 }
49 49
50 fn get(&self, pos: usize) -> Option<TtToken> { 50 fn get(&self, pos: usize) -> Ref<Option<TtToken>> {
51 let mut cached = self.cached.borrow_mut(); 51 if pos < self.cached.borrow().len() {
52 if pos < cached.len() { 52 return Ref::map(self.cached.borrow(), |c| &c[pos]);
53 return cached[pos].clone();
54 } 53 }
55 54
56 while pos >= cached.len() { 55 {
57 let cursor = self.cached_cursor.get(); 56 let mut cached = self.cached.borrow_mut();
58 if cursor.eof() { 57 while pos >= cached.len() {
59 cached.push(None); 58 let cursor = self.cached_cursor.get();
60 continue; 59 if cursor.eof() {
61 } 60 cached.push(None);
62 61 continue;
63 match cursor.token_tree() {
64 Some(tt::TokenTree::Leaf(leaf)) => {
65 cached.push(Some(convert_leaf(&leaf)));
66 self.cached_cursor.set(cursor.bump());
67 }
68 Some(tt::TokenTree::Subtree(subtree)) => {
69 self.cached_cursor.set(cursor.subtree().unwrap());
70 cached.push(Some(convert_delim(subtree.delimiter, false)));
71 } 62 }
72 None => { 63
73 if let Some(subtree) = cursor.end() { 64 match cursor.token_tree() {
74 cached.push(Some(convert_delim(subtree.delimiter, true))); 65 Some(tt::TokenTree::Leaf(leaf)) => {
66 cached.push(Some(convert_leaf(&leaf)));
75 self.cached_cursor.set(cursor.bump()); 67 self.cached_cursor.set(cursor.bump());
76 } 68 }
69 Some(tt::TokenTree::Subtree(subtree)) => {
70 self.cached_cursor.set(cursor.subtree().unwrap());
71 cached.push(Some(convert_delim(subtree.delimiter, false)));
72 }
73 None => {
74 if let Some(subtree) = cursor.end() {
75 cached.push(Some(convert_delim(subtree.delimiter, true)));
76 self.cached_cursor.set(cursor.bump());
77 }
78 }
77 } 79 }
78 } 80 }
79 } 81 }
80 82
81 cached[pos].clone() 83 Ref::map(self.cached.borrow(), |c| &c[pos])
82 } 84 }
83} 85}
84 86
@@ -103,8 +105,8 @@ impl<'a> TokenSource for SubtreeTokenSource<'a> {
103 105
104 /// Is the current token a specified keyword? 106 /// Is the current token a specified keyword?
105 fn is_keyword(&self, kw: &str) -> bool { 107 fn is_keyword(&self, kw: &str) -> bool {
106 match self.get(self.curr.1) { 108 match *self.get(self.curr.1) {
107 Some(t) => t.text == *kw, 109 Some(ref t) => t.text == *kw,
108 _ => false, 110 _ => false,
109 } 111 }
110 } 112 }
diff --git a/crates/ra_syntax/Cargo.toml b/crates/ra_syntax/Cargo.toml
index bc1c88070..5f8585878 100644
--- a/crates/ra_syntax/Cargo.toml
+++ b/crates/ra_syntax/Cargo.toml
@@ -8,7 +8,6 @@ description = "Comment and whitespace preserving parser for the Rust langauge"
8repository = "https://github.com/rust-analyzer/rust-analyzer" 8repository = "https://github.com/rust-analyzer/rust-analyzer"
9 9
10[dependencies] 10[dependencies]
11unicode-xid = "0.1.0"
12itertools = "0.8.0" 11itertools = "0.8.0"
13rowan = "0.6.1" 12rowan = "0.6.1"
14ra_rustc_lexer = { version = "0.1.0-pre.2" } 13ra_rustc_lexer = { version = "0.1.0-pre.2" }
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs
index c5746d98d..6f0489617 100644
--- a/crates/ra_syntax/src/ast.rs
+++ b/crates/ra_syntax/src/ast.rs
@@ -139,6 +139,52 @@ fn test_doc_comment_preserves_newlines() {
139} 139}
140 140
141#[test] 141#[test]
142fn test_doc_comment_single_line_block_strips_suffix() {
143 let file = SourceFile::parse(
144 r#"
145 /** this is mod foo*/
146 mod foo {}
147 "#,
148 )
149 .ok()
150 .unwrap();
151 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
152 assert_eq!("this is mod foo", module.doc_comment_text().unwrap());
153}
154
155#[test]
156fn test_doc_comment_single_line_block_strips_suffix_whitespace() {
157 let file = SourceFile::parse(
158 r#"
159 /** this is mod foo */
160 mod foo {}
161 "#,
162 )
163 .ok()
164 .unwrap();
165 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
166 assert_eq!("this is mod foo", module.doc_comment_text().unwrap());
167}
168
169#[test]
170fn test_doc_comment_multi_line_block_strips_suffix() {
171 let file = SourceFile::parse(
172 r#"
173 /**
174 this
175 is
176 mod foo
177 */
178 mod foo {}
179 "#,
180 )
181 .ok()
182 .unwrap();
183 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
184 assert_eq!(" this\n is\n mod foo", module.doc_comment_text().unwrap());
185}
186
187#[test]
142fn test_where_predicates() { 188fn test_where_predicates() {
143 fn assert_bound(text: &str, bound: Option<TypeBound>) { 189 fn assert_bound(text: &str, bound: Option<TypeBound>) {
144 assert_eq!(text, bound.unwrap().syntax().text().to_string()); 190 assert_eq!(text, bound.unwrap().syntax().text().to_string());
diff --git a/crates/ra_syntax/src/ast/traits.rs b/crates/ra_syntax/src/ast/traits.rs
index 6ed1b5213..1b9a2b20c 100644
--- a/crates/ra_syntax/src/ast/traits.rs
+++ b/crates/ra_syntax/src/ast/traits.rs
@@ -115,8 +115,8 @@ pub trait DocCommentsOwner: AstNode {
115 } 115 }
116 116
117 /// Returns the textual content of a doc comment block as a single string. 117 /// Returns the textual content of a doc comment block as a single string.
118 /// That is, strips leading `///` (+ optional 1 character of whitespace) 118 /// That is, strips leading `///` (+ optional 1 character of whitespace),
119 /// and joins lines. 119 /// trailing `*/`, trailing whitespace and then joins the lines.
120 fn doc_comment_text(&self) -> Option<String> { 120 fn doc_comment_text(&self) -> Option<String> {
121 let mut has_comments = false; 121 let mut has_comments = false;
122 let docs = self 122 let docs = self
@@ -136,7 +136,13 @@ pub trait DocCommentsOwner: AstNode {
136 prefix_len 136 prefix_len
137 }; 137 };
138 138
139 line[pos..].to_owned() 139 let end = if comment.kind().shape.is_block() && line.ends_with("*/") {
140 line.len() - 2
141 } else {
142 line.len()
143 };
144
145 line[pos..end].trim_end().to_owned()
140 }) 146 })
141 .join("\n"); 147 .join("\n");
142 148