Repository: rust-analyzer/rust-analyzer (rust)

Issue:
Doc comment spuriously includes non-doc comments
E.g. in
```rust
/// A documentation comment.
// Not a documentation comment
struct Something;
```
the docs for `Something` are currently given as `A documentation comment.\nNot a documentation comment` (This is rendered by `vscode` without a newline due to markdown rules).
We should really only give them as a `A documentation comment`.

## Mentoring instructions

The current syntax based approximation is defined in:
https://github.com/rust-analyzer/rust-analyzer/blob/1d6dcef5c584d0dffdf5386eec993e41daad0210/crates/ra_syntax/src/ast.rs#L73-L94.

This works by finding all comment nodes owned by the `DocCommentsOwner`, stripping their prefix (no matter what it is) and combining them with `/n`s. A first step is to improve `doc_comment_text` to only allow direct documentation comments (by checking `comment.flavor()` and checking that it is `CommentFlavor::Doc`).

However, we also need to check internal comments prefixed with `//!` (especially in `mod`ules). This requires working out where the `mod`s body is. This should be handled in `ra_analysis`. Ideally, there should be a desugaring of doc comments into `#[doc]` attributes in `ra_hir` (or `ra_analysis`), which should handle cases where `#[doc]` is used manually correctly.

Pull request description:
remove non-doc comments from doc comments
Closes #357

Patch:
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs
index ab935954c7..1e26a28893 100644
--- a/crates/ra_analysis/src/lib.rs
+++ b/crates/ra_analysis/src/lib.rs
@@ -394,7 +394,7 @@ impl Analysis {
     pub fn doc_text_for(&self, nav: NavigationTarget) -> Cancelable<Option<String>> {
         self.db.doc_text_for(nav)
     }
-    /// Returns a `mod name;` declaration whihc created the current module.
+    /// Returns a `mod name;` declaration which created the current module.
     pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<NavigationTarget>> {
         self.db.parent_module(position)
     }
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs
index e968c97281..3aaa5edda6 100644
--- a/crates/ra_syntax/src/ast.rs
+++ b/crates/ra_syntax/src/ast.rs
@@ -115,6 +115,7 @@ pub trait DocCommentsOwner<'a>: AstNode<'a> {
     /// That is, strips leading `///` and joins lines
     fn doc_comment_text(self) -> RustString {
         self.doc_comments()
+            .filter(|comment| comment.is_doc_comment())
             .map(|comment| {
                 let prefix = comment.prefix();
                 let trimmed = comment
@@ -206,6 +207,10 @@ impl<'a> Comment<'a> {
         }
     }
 
+    pub fn is_doc_comment(&self) -> bool {
+        self.flavor().is_doc_comment()
+    }
+
     pub fn prefix(&self) -> &'static str {
         self.flavor().prefix()
     }
@@ -237,6 +242,13 @@ impl CommentFlavor {
             Multiline => "/*",
         }
     }
+
+    pub fn is_doc_comment(&self) -> bool {
+        match self {
+            CommentFlavor::Doc | CommentFlavor::ModuleDoc => true,
+            _ => false,
+        }
+    }
 }
 
 impl<'a> Whitespace<'a> {
@@ -469,3 +481,16 @@ impl<'a> PrefixExpr<'a> {
         }
     }
 }
+
+#[test]
+fn test_doc_comment_of_items() {
+    let file = SourceFileNode::parse(
+        r#"
+        //! doc
+        // non-doc
+        mod foo {}
+        "#,
+    );
+    let module = file.syntax().descendants().find_map(Module::cast).unwrap();
+    assert_eq!("doc", module.doc_comment_text());
+}


Test patch:
diff --git a/crates/ra_analysis/tests/test/main.rs b/crates/ra_analysis/tests/test/main.rs
index bfdf8aef2f..859778024a 100644
--- a/crates/ra_analysis/tests/test/main.rs
+++ b/crates/ra_analysis/tests/test/main.rs
@@ -249,7 +249,8 @@ fn bar() {
 fn test_fn_signature_with_docs_simple() {
     let (desc, param) = get_signature(
         r#"
-// test
+/// test
+// non-doc-comment
 fn foo(j: u32) -> u32 {
     j
 }
diff --git a/crates/ra_syntax/tests/data/parser/ok/0037_mod.rs b/crates/ra_syntax/tests/data/parser/ok/0037_mod.rs
new file mode 100644
index 0000000000..7e5a1b8358
--- /dev/null
+++ b/crates/ra_syntax/tests/data/parser/ok/0037_mod.rs
@@ -0,0 +1,5 @@
+// https://github.com/rust-analyzer/rust-analyzer/issues/357
+
+//! docs
+// non-docs
+mod foo {}
\ No newline at end of file
diff --git a/crates/ra_syntax/tests/data/parser/ok/0037_mod.txt b/crates/ra_syntax/tests/data/parser/ok/0037_mod.txt
new file mode 100644
index 0000000000..e11c4a06df
--- /dev/null
+++ b/crates/ra_syntax/tests/data/parser/ok/0037_mod.txt
@@ -0,0 +1,17 @@
+SOURCE_FILE@[0; 93)
+  COMMENT@[0; 60)
+  WHITESPACE@[60; 62)
+  MODULE@[62; 93)
+    COMMENT@[62; 70)
+    WHITESPACE@[70; 71)
+    COMMENT@[71; 82)
+    WHITESPACE@[82; 83)
+    MOD_KW@[83; 86)
+    WHITESPACE@[86; 87)
+    NAME@[87; 90)
+      IDENT@[87; 90) "foo"
+    WHITESPACE@[90; 91)
+    ITEM_LIST@[91; 93)
+      L_CURLY@[91; 92)
+      R_CURLY@[92; 93)
+