diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 6466422c5ee..508e8af2063 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -637,6 +637,33 @@ fn main() { }
         );
     }
 
+    #[test]
+    fn hover_shows_fn_doc_attr_raw_string() {
+        check(
+            r##"
+#[doc = r#"Raw string doc attr"#]
+pub fn foo<|>(_: &Path) {}
+
+fn main() { }
+"##,
+            expect![[r##"
+                *foo*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                pub fn foo(_: &Path)
+                ```
+
+                ---
+
+                Raw string doc attr
+            "##]],
+        );
+    }
+
     #[test]
     fn hover_shows_struct_field_info() {
         // Hovering over the field when instantiating
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index 50c1c157d87..c5cd1c504da 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -7,7 +7,7 @@ use itertools::Itertools;
 use parser::SyntaxKind;
 
 use crate::{
-    ast::{self, support, AstNode, NameOwner, SyntaxNode},
+    ast::{self, support, token_ext::HasStringValue, AstNode, AstToken, NameOwner, SyntaxNode},
     SmolStr, SyntaxElement, SyntaxToken, T,
 };
 
@@ -53,8 +53,16 @@ impl ast::Attr {
     pub fn as_simple_key_value(&self) -> Option<(SmolStr, SmolStr)> {
         let lit = self.literal()?;
         let key = self.simple_name()?;
-        // FIXME: escape? raw string?
-        let value = lit.syntax().first_token()?.text().trim_matches('"').into();
+        let value_token = lit.syntax().first_token()?;
+
+        let value: SmolStr = if let Some(s) = ast::String::cast(value_token.clone()) {
+            s.value()?.into()
+        } else if let Some(s) = ast::RawString::cast(value_token) {
+            s.value()?.into()
+        } else {
+            return None;
+        };
+
         Some((key, value))
     }