Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/hir-def/src/attr.rs6
-rw-r--r--crates/hir-def/src/nameres/collector.rs13
-rw-r--r--crates/hir-expand/src/attrs.rs49
-rw-r--r--crates/ide-db/src/documentation.rs13
4 files changed, 69 insertions, 12 deletions
diff --git a/crates/hir-def/src/attr.rs b/crates/hir-def/src/attr.rs
index f4f78ce6a4..d9eeffd798 100644
--- a/crates/hir-def/src/attr.rs
+++ b/crates/hir-def/src/attr.rs
@@ -5,7 +5,7 @@ pub mod builtin;
#[cfg(test)]
mod tests;
-use std::{hash::Hash, ops, slice::Iter as SliceIter};
+use std::{borrow::Cow, hash::Hash, ops, slice::Iter as SliceIter};
use base_db::CrateId;
use cfg::{CfgExpr, CfgOptions};
@@ -573,6 +573,10 @@ impl<'attr> AttrQuery<'attr> {
self.attrs().find_map(|attr| attr.string_value())
}
+ pub fn string_value_unescape(self) -> Option<Cow<'attr, str>> {
+ self.attrs().find_map(|attr| attr.string_value_unescape())
+ }
+
pub fn exists(self) -> bool {
self.attrs().next().is_some()
}
diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs
index 0a74123abb..0a6cd0fe9e 100644
--- a/crates/hir-def/src/nameres/collector.rs
+++ b/crates/hir-def/src/nameres/collector.rs
@@ -1917,7 +1917,7 @@ impl ModCollector<'_, '_> {
}
fn collect_module(&mut self, module_id: FileItemTreeId<Mod>, attrs: &Attrs) {
- let path_attr = attrs.by_key("path").string_value();
+ let path_attr = attrs.by_key("path").string_value_unescape();
let is_macro_use = attrs.by_key("macro_use").exists();
let module = &self.item_tree[module_id];
match &module.kind {
@@ -1931,7 +1931,8 @@ impl ModCollector<'_, '_> {
module_id,
);
- let Some(mod_dir) = self.mod_dir.descend_into_definition(&module.name, path_attr)
+ let Some(mod_dir) =
+ self.mod_dir.descend_into_definition(&module.name, path_attr.as_deref())
else {
return;
};
@@ -1952,8 +1953,12 @@ impl ModCollector<'_, '_> {
ModKind::Outline => {
let ast_id = AstId::new(self.file_id(), module.ast_id);
let db = self.def_collector.db;
- match self.mod_dir.resolve_declaration(db, self.file_id(), &module.name, path_attr)
- {
+ match self.mod_dir.resolve_declaration(
+ db,
+ self.file_id(),
+ &module.name,
+ path_attr.as_deref(),
+ ) {
Ok((file_id, is_mod_rs, mod_dir)) => {
let item_tree = db.file_item_tree(file_id.into());
let krate = self.def_collector.def_map.krate;
diff --git a/crates/hir-expand/src/attrs.rs b/crates/hir-expand/src/attrs.rs
index f1540498f2..7782b0fc81 100644
--- a/crates/hir-expand/src/attrs.rs
+++ b/crates/hir-expand/src/attrs.rs
@@ -1,5 +1,5 @@
//! A higher level attributes based on TokenTree, with also some shortcuts.
-use std::{fmt, ops};
+use std::{borrow::Cow, fmt, ops};
use base_db::CrateId;
use cfg::CfgExpr;
@@ -297,6 +297,20 @@ impl Attr {
}
}
+ pub fn string_value_unescape(&self) -> Option<Cow<'_, str>> {
+ match self.input.as_deref()? {
+ AttrInput::Literal(it) => match it.text.strip_prefix('r') {
+ Some(it) => {
+ it.trim_matches('#').strip_prefix('"')?.strip_suffix('"').map(Cow::Borrowed)
+ }
+ None => {
+ it.text.strip_prefix('"')?.strip_suffix('"').and_then(unescape).map(Cow::Owned)
+ }
+ },
+ _ => None,
+ }
+ }
+
/// #[path(ident)]
pub fn single_ident_value(&self) -> Option<&tt::Ident> {
match self.input.as_deref()? {
@@ -346,6 +360,39 @@ impl Attr {
}
}
+fn unescape(s: &str) -> Option<String> {
+ let mut res = String::with_capacity(s.len());
+ let mut chars = s.chars();
+
+ while let Some(c) = chars.next() {
+ if c == '\\' {
+ match chars.next()? {
+ 'n' => res.push('\n'),
+ 'r' => res.push('\r'),
+ 't' => res.push('\t'),
+ '\\' => res.push('\\'),
+ '\'' => res.push('\''),
+ '"' => res.push('"'),
+ '0' => res.push('\0'),
+ 'x' => {
+ let hex = chars.by_ref().take(2).collect::<String>();
+ let c = u8::from_str_radix(&hex, 16).ok()?;
+ res.push(c as char);
+ }
+ 'u' => {
+ let hex = chars.by_ref().take(4).collect::<String>();
+ let c = u32::from_str_radix(&hex, 16).ok()?;
+ res.push(char::from_u32(c)?);
+ }
+ _ => return None,
+ }
+ } else {
+ res.push(c);
+ }
+ }
+ Some(res)
+}
+
pub fn collect_attrs(
owner: &dyn ast::HasAttrs,
) -> impl Iterator<Item = (AttrId, Either<ast::Attr, ast::Comment>)> {
diff --git a/crates/ide-db/src/documentation.rs b/crates/ide-db/src/documentation.rs
index 72ca354365..58e77b95c3 100644
--- a/crates/ide-db/src/documentation.rs
+++ b/crates/ide-db/src/documentation.rs
@@ -91,8 +91,10 @@ pub fn docs_with_rangemap(
db: &dyn DefDatabase,
attrs: &AttrsWithOwner,
) -> Option<(Documentation, DocsRangeMap)> {
- let docs =
- attrs.by_key("doc").attrs().filter_map(|attr| attr.string_value().map(|s| (s, attr.id)));
+ let docs = attrs
+ .by_key("doc")
+ .attrs()
+ .filter_map(|attr| attr.string_value_unescape().map(|s| (s, attr.id)));
let indent = doc_indent(attrs);
let mut buf = String::new();
let mut mapping = Vec::new();
@@ -132,7 +134,7 @@ pub fn docs_with_rangemap(
}
pub fn docs_from_attrs(attrs: &hir::Attrs) -> Option<String> {
- let docs = attrs.by_key("doc").attrs().filter_map(|attr| attr.string_value());
+ let docs = attrs.by_key("doc").attrs().filter_map(|attr| attr.string_value_unescape());
let indent = doc_indent(attrs);
let mut buf = String::new();
for doc in docs {
@@ -270,10 +272,9 @@ fn doc_indent(attrs: &hir::Attrs) -> usize {
attrs
.by_key("doc")
.attrs()
- .filter_map(|attr| attr.string_value())
+ .filter_map(|attr| attr.string_value()) // no need to use unescape version here
.flat_map(|s| s.lines())
- .filter(|line| !line.chars().all(|c| c.is_whitespace()))
- .map(|line| line.chars().take_while(|c| c.is_whitespace()).count())
+ .filter_map(|line| line.chars().position(|c| !c.is_whitespace()))
.min()
.unwrap_or(0)
}