From e7e896170a4cccefc5ac4744bb837329ae5d0d36 Mon Sep 17 00:00:00 2001
From: Edwin Cheng <edwin0cheng@gmail.com>
Date: Sat, 4 May 2019 15:00:16 +0800
Subject: Convert doc comment to attr

---
 crates/ra_mbe/src/syntax_bridge.rs | 73 ++++++++++++++++++++++++++++++++++++--
 crates/ra_mbe/src/tests.rs         | 25 +++++++++++++
 2 files changed, 95 insertions(+), 3 deletions(-)

diff --git a/crates/ra_mbe/src/syntax_bridge.rs b/crates/ra_mbe/src/syntax_bridge.rs
index 3521b382a..8b4098c4a 100644
--- a/crates/ra_mbe/src/syntax_bridge.rs
+++ b/crates/ra_mbe/src/syntax_bridge.rs
@@ -118,6 +118,69 @@ impl TokenMap {
     }
 }
 
+/// Returns the textual content of a doc comment block as a quoted string
+/// That is, strips leading `///` (or `/**`, etc)
+/// and strips the ending `*/`
+/// And then quote the string, which is needed to convert to `tt::Literal`
+fn doc_comment_text(comment: &ast::Comment) -> SmolStr {
+    use ast::AstToken;
+
+    let prefix_len = comment.prefix().len();
+    let mut text = &comment.text()[prefix_len..];
+
+    // Remove ending "*/"
+    if comment.kind().shape == ast::CommentShape::Block {
+        text = &text[0..text.len() - 2];
+    }
+
+    // Quote the string
+    // Note that `tt::Literal` expect an escaped string
+    let text = format!("{:?}", text);
+    text.into()
+}
+
+fn convert_doc_comment<'a>(token: &ra_syntax::SyntaxToken<'a>) -> Option<Vec<tt::TokenTree>> {
+    use ast::AstToken;
+    let comment = ast::Comment::cast(*token)?;
+    let doc = comment.kind().doc?;
+
+    // Make `doc="\" Comments\""
+    let mut meta_tkns = Vec::new();
+    meta_tkns.push(mk_ident("doc"));
+    meta_tkns.push(mk_punct('='));
+    meta_tkns.push(mk_doc_literal(&comment));
+
+    // Make `#![]`
+    let mut token_trees = Vec::new();
+    token_trees.push(mk_punct('#'));
+    if let ast::CommentPlacement::Inner = doc {
+        token_trees.push(mk_punct('!'));
+    }
+    token_trees.push(tt::TokenTree::from(tt::Subtree::from(
+        tt::Subtree { delimiter: tt::Delimiter::Bracket, token_trees: meta_tkns }.into(),
+    )));
+
+    return Some(token_trees);
+
+    // Helper functions
+    fn mk_ident(s: &str) -> tt::TokenTree {
+        tt::TokenTree::from(tt::Leaf::from(tt::Ident {
+            text: s.into(),
+            id: tt::TokenId::unspecified(),
+        }))
+    }
+
+    fn mk_punct(c: char) -> tt::TokenTree {
+        tt::TokenTree::from(tt::Leaf::from(tt::Punct { char: c, spacing: tt::Spacing::Alone }))
+    }
+
+    fn mk_doc_literal(comment: &ast::Comment) -> tt::TokenTree {
+        let lit = tt::Literal { text: doc_comment_text(comment) };
+
+        tt::TokenTree::from(tt::Leaf::from(lit))
+    }
+}
+
 fn convert_tt(
     token_map: &mut TokenMap,
     global_offset: TextUnit,
@@ -141,13 +204,17 @@ fn convert_tt(
     let mut child_iter = tt.children_with_tokens().skip(skip_first as usize).peekable();
 
     while let Some(child) = child_iter.next() {
-        if (skip_first && (child == first_child || child == last_child)) || child.kind().is_trivia()
-        {
+        if skip_first && (child == first_child || child == last_child) {
             continue;
         }
+
         match child {
             SyntaxElement::Token(token) => {
-                if token.kind().is_punct() {
+                if let Some(doc_tokens) = convert_doc_comment(&token) {
+                    token_trees.extend(doc_tokens);
+                } else if token.kind().is_trivia() {
+                    continue;
+                } else if token.kind().is_punct() {
                     assert!(token.text().len() == 1, "Input ast::token punct must be single char.");
                     let char = token.text().chars().next().unwrap();
 
diff --git a/crates/ra_mbe/src/tests.rs b/crates/ra_mbe/src/tests.rs
index bd5a44240..897f76be1 100644
--- a/crates/ra_mbe/src/tests.rs
+++ b/crates/ra_mbe/src/tests.rs
@@ -867,6 +867,31 @@ fn test_meta() {
     );
 }
 
+#[test]
+fn test_meta_doc_comments() {
+    let rules = create_rules(
+        r#"
+        macro_rules! foo {
+            ($(#[$ i:meta])+) => (
+                $(#[$ i])+
+                fn bar() {}
+            )
+        }
+"#,
+    );
+    assert_expansion(
+        MacroKind::Items,
+        &rules,
+        r#"foo! { 
+            /// Single Line Doc 1
+            /** 
+                MultiLines Doc
+            */
+        }"#,
+        "# [doc = \" Single Line Doc 1\"] # [doc = \" \\n                MultiLines Doc\\n            \"] fn bar () {}",
+    );
+}
+
 #[test]
 fn test_tt_block() {
     let rules = create_rules(
-- 
cgit v1.2.3


From 048f12d9f0bcd7357d9a885639928a90915577ab Mon Sep 17 00:00:00 2001
From: Edwin Cheng <edwin0cheng@gmail.com>
Date: Sat, 4 May 2019 17:03:22 +0800
Subject: Use explict `escape_default` instead of dbg print

---
 crates/ra_mbe/src/syntax_bridge.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/crates/ra_mbe/src/syntax_bridge.rs b/crates/ra_mbe/src/syntax_bridge.rs
index 8b4098c4a..63ca54cc2 100644
--- a/crates/ra_mbe/src/syntax_bridge.rs
+++ b/crates/ra_mbe/src/syntax_bridge.rs
@@ -135,7 +135,7 @@ fn doc_comment_text(comment: &ast::Comment) -> SmolStr {
 
     // Quote the string
     // Note that `tt::Literal` expect an escaped string
-    let text = format!("{:?}", text);
+    let text = format!("\"{}\"", text.escape_default());
     text.into()
 }
 
-- 
cgit v1.2.3


From 036141663bd934da451461dbc47dc836c432e057 Mon Sep 17 00:00:00 2001
From: Edwin Cheng <edwin0cheng@gmail.com>
Date: Sat, 4 May 2019 19:34:02 +0800
Subject: Quote the quoted comment

---
 crates/ra_mbe/src/syntax_bridge.rs | 2 +-
 crates/ra_mbe/src/tests.rs         | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/crates/ra_mbe/src/syntax_bridge.rs b/crates/ra_mbe/src/syntax_bridge.rs
index 63ca54cc2..73a0780da 100644
--- a/crates/ra_mbe/src/syntax_bridge.rs
+++ b/crates/ra_mbe/src/syntax_bridge.rs
@@ -135,7 +135,7 @@ fn doc_comment_text(comment: &ast::Comment) -> SmolStr {
 
     // Quote the string
     // Note that `tt::Literal` expect an escaped string
-    let text = format!("\"{}\"", text.escape_default());
+    let text = format!("{:?}", text.escape_default().to_string());
     text.into()
 }
 
diff --git a/crates/ra_mbe/src/tests.rs b/crates/ra_mbe/src/tests.rs
index 897f76be1..c487bbbd4 100644
--- a/crates/ra_mbe/src/tests.rs
+++ b/crates/ra_mbe/src/tests.rs
@@ -888,7 +888,7 @@ fn test_meta_doc_comments() {
                 MultiLines Doc
             */
         }"#,
-        "# [doc = \" Single Line Doc 1\"] # [doc = \" \\n                MultiLines Doc\\n            \"] fn bar () {}",
+        "# [doc = \" Single Line Doc 1\"] # [doc = \" \\\\n                MultiLines Doc\\\\n            \"] fn bar () {}",
     );
 }
 
-- 
cgit v1.2.3