diff options
| -rw-r--r-- | crates/hir-def/src/nameres/mod_resolution.rs | 2 | ||||
| -rw-r--r-- | crates/hir-def/src/nameres/tests/mod_resolution.rs | 4 | ||||
| -rw-r--r-- | crates/hir-expand/src/mod_path.rs | 16 | ||||
| -rw-r--r-- | crates/hir-expand/src/name.rs | 52 | ||||
| -rw-r--r-- | crates/ide-completion/src/completions/item_list/trait_impl.rs | 3 | ||||
| -rw-r--r-- | crates/ide-completion/src/render.rs | 11 | ||||
| -rw-r--r-- | crates/ide-completion/src/render/const_.rs | 2 | ||||
| -rw-r--r-- | crates/ide-completion/src/render/function.rs | 8 | ||||
| -rw-r--r-- | crates/ide-completion/src/render/literal.rs | 2 | ||||
| -rw-r--r-- | crates/ide-completion/src/render/macro_.rs | 2 | ||||
| -rw-r--r-- | crates/ide-completion/src/render/pattern.rs | 10 | ||||
| -rw-r--r-- | crates/ide-completion/src/render/type_alias.rs | 4 | ||||
| -rw-r--r-- | crates/ide-completion/src/render/union_literal.rs | 12 | ||||
| -rw-r--r-- | crates/ide-completion/src/render/variant.rs | 4 | ||||
| -rw-r--r-- | crates/ide/src/runnables.rs | 186 |
15 files changed, 255 insertions, 63 deletions
diff --git a/crates/hir-def/src/nameres/mod_resolution.rs b/crates/hir-def/src/nameres/mod_resolution.rs index 52a620fe22f..99f7f1b549e 100644 --- a/crates/hir-def/src/nameres/mod_resolution.rs +++ b/crates/hir-def/src/nameres/mod_resolution.rs @@ -73,10 +73,12 @@ impl ModDir { candidate_files.push(self.dir_path.join_attr(attr_path, self.root_non_dir_owner)) } None if file_id.is_include_macro(db.upcast()) => { + let name = name.unescaped(); candidate_files.push(format!("{}.rs", name)); candidate_files.push(format!("{}/mod.rs", name)); } None => { + let name = name.unescaped(); candidate_files.push(format!("{}{}.rs", self.dir_path.0, name)); candidate_files.push(format!("{}{}/mod.rs", self.dir_path.0, name)); } diff --git a/crates/hir-def/src/nameres/tests/mod_resolution.rs b/crates/hir-def/src/nameres/tests/mod_resolution.rs index 79a74873b4a..3fa585574de 100644 --- a/crates/hir-def/src/nameres/tests/mod_resolution.rs +++ b/crates/hir-def/src/nameres/tests/mod_resolution.rs @@ -132,9 +132,9 @@ pub struct Bar; expect![[r#" crate Bar: t v - async: t + r#async: t - crate::async + crate::r#async Bar: t v "#]], ); diff --git a/crates/hir-expand/src/mod_path.rs b/crates/hir-expand/src/mod_path.rs index fea09521e87..d0f73ec8208 100644 --- a/crates/hir-expand/src/mod_path.rs +++ b/crates/hir-expand/src/mod_path.rs @@ -22,7 +22,7 @@ pub struct ModPath { } #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct EscapedModPath<'a>(&'a ModPath); +pub struct UnescapedModPath<'a>(&'a ModPath); #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum PathKind { @@ -102,8 +102,8 @@ impl ModPath { } } - pub fn escaped(&self) -> EscapedModPath<'_> { - EscapedModPath(self) + pub fn unescaped(&self) -> UnescapedModPath<'_> { + UnescapedModPath(self) } fn _fmt(&self, f: &mut fmt::Formatter<'_>, escaped: bool) -> fmt::Result { @@ -134,9 +134,9 @@ impl ModPath { } first_segment = false; if escaped { - segment.escaped().fmt(f)? - } else { segment.fmt(f)? + } else { + segment.unescaped().fmt(f)? }; } Ok(()) @@ -145,13 +145,13 @@ impl ModPath { impl Display for ModPath { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self._fmt(f, false) + self._fmt(f, true) } } -impl<'a> Display for EscapedModPath<'a> { +impl<'a> Display for UnescapedModPath<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0._fmt(f, true) + self.0._fmt(f, false) } } diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs index 47d191822d8..87c663eec8e 100644 --- a/crates/hir-expand/src/name.rs +++ b/crates/hir-expand/src/name.rs @@ -7,12 +7,16 @@ use syntax::{ast, SmolStr, SyntaxKind}; /// `Name` is a wrapper around string, which is used in hir for both references /// and declarations. In theory, names should also carry hygiene info, but we are /// not there yet! +/// +/// Note that `Name` holds and prints escaped name i.e. prefixed with "r#" when it +/// is a raw identifier. Use [`unescaped()`][Name::unescaped] when you need the +/// name without "r#". #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct Name(Repr); -/// `EscapedName` will add a prefix "r#" to the wrapped `Name` when it is a raw identifier +/// Wrapper of `Name` to print the name without "r#" even when it is a raw identifier. #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct EscapedName<'a>(&'a Name); +pub struct UnescapedName<'a>(&'a Name); #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] enum Repr { @@ -34,37 +38,26 @@ fn is_raw_identifier(name: &str) -> bool { is_keyword && !matches!(name, "self" | "crate" | "super" | "Self") } -impl<'a> fmt::Display for EscapedName<'a> { +impl<'a> fmt::Display for UnescapedName<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &self.0 .0 { Repr::Text(text) => { - if is_raw_identifier(text) { - write!(f, "r#{}", &text) - } else { - fmt::Display::fmt(&text, f) - } + let text = text.strip_prefix("r#").unwrap_or(text); + fmt::Display::fmt(&text, f) } Repr::TupleField(idx) => fmt::Display::fmt(&idx, f), } } } -impl<'a> EscapedName<'a> { - pub fn is_escaped(&self) -> bool { - match &self.0 .0 { - Repr::Text(it) => is_raw_identifier(&it), - Repr::TupleField(_) => false, - } - } - - /// Returns the textual representation of this name as a [`SmolStr`]. - /// Prefer using this over [`ToString::to_string`] if possible as this conversion is cheaper in - /// the general case. +impl<'a> UnescapedName<'a> { + /// Returns the textual representation of this name as a [`SmolStr`]. Prefer using this over + /// [`ToString::to_string`] if possible as this conversion is cheaper in the general case. pub fn to_smol_str(&self) -> SmolStr { match &self.0 .0 { Repr::Text(it) => { - if is_raw_identifier(&it) { - SmolStr::from_iter(["r#", &it]) + if let Some(stripped) = it.strip_prefix("r#") { + SmolStr::new(stripped) } else { it.clone() } @@ -97,9 +90,11 @@ impl Name { /// Resolve a name from the text of token. fn resolve(raw_text: &str) -> Name { + // When `raw_text` starts with "r#" but the name does not coincide with any + // keyword, we never need the prefix so we strip it. match raw_text.strip_prefix("r#") { - Some(text) => Name::new_text(SmolStr::new(text)), - None => Name::new_text(raw_text.into()), + Some(text) if !is_raw_identifier(text) => Name::new_text(SmolStr::new(text)), + _ => Name::new_text(raw_text.into()), } } @@ -142,8 +137,15 @@ impl Name { } } - pub fn escaped(&self) -> EscapedName<'_> { - EscapedName(self) + pub fn unescaped(&self) -> UnescapedName<'_> { + UnescapedName(self) + } + + pub fn is_escaped(&self) -> bool { + match &self.0 { + Repr::Text(it) => it.starts_with("r#"), + Repr::TupleField(_) => false, + } } } diff --git a/crates/ide-completion/src/completions/item_list/trait_impl.rs b/crates/ide-completion/src/completions/item_list/trait_impl.rs index e9256803cc4..785db6fde1d 100644 --- a/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -233,7 +233,8 @@ fn add_type_alias_impl( type_alias: hir::TypeAlias, ) { let alias_name = type_alias.name(ctx.db); - let (alias_name, escaped_name) = (alias_name.to_smol_str(), alias_name.escaped().to_smol_str()); + let (alias_name, escaped_name) = + (alias_name.unescaped().to_smol_str(), alias_name.to_smol_str()); let label = format!("type {} =", alias_name); let replacement = format!("type {} = ", escaped_name); diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index 946134b0ff9..693007ca307 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs @@ -117,7 +117,7 @@ pub(crate) fn render_field( ) -> CompletionItem { let is_deprecated = ctx.is_deprecated(field); let name = field.name(ctx.db()); - let (name, escaped_name) = (name.to_smol_str(), name.escaped().to_smol_str()); + let (name, escaped_name) = (name.unescaped().to_smol_str(), name.to_smol_str()); let mut item = CompletionItem::new( SymbolKind::Field, ctx.source_range(), @@ -283,8 +283,8 @@ fn render_resolution_path( let name = local_name.to_smol_str(); let mut item = render_resolution_simple_(ctx, &local_name, import_to_add, resolution); - if local_name.escaped().is_escaped() { - item.insert_text(local_name.escaped().to_smol_str()); + if local_name.is_escaped() { + item.insert_text(local_name.to_smol_str()); } // Add `<>` for generic types let type_path_no_ty_args = matches!( @@ -306,7 +306,7 @@ fn render_resolution_path( item.lookup_by(name.clone()) .label(SmolStr::from_iter([&name, "<…>"])) .trigger_call_info() - .insert_snippet(cap, format!("{}<$0>", local_name.escaped())); + .insert_snippet(cap, format!("{}<$0>", local_name)); } } } @@ -342,7 +342,8 @@ fn render_resolution_simple_( let ctx = ctx.import_to_add(import_to_add); let kind = res_to_kind(resolution); - let mut item = CompletionItem::new(kind, ctx.source_range(), local_name.to_smol_str()); + let mut item = + CompletionItem::new(kind, ctx.source_range(), local_name.unescaped().to_smol_str()); item.set_relevance(ctx.completion_relevance()) .set_documentation(scope_def_docs(db, resolution)) .set_deprecated(scope_def_is_deprecated(&ctx, resolution)); diff --git a/crates/ide-completion/src/render/const_.rs b/crates/ide-completion/src/render/const_.rs index a810eef18dd..93ea825e004 100644 --- a/crates/ide-completion/src/render/const_.rs +++ b/crates/ide-completion/src/render/const_.rs @@ -13,7 +13,7 @@ pub(crate) fn render_const(ctx: RenderContext<'_>, const_: hir::Const) -> Option fn render(ctx: RenderContext<'_>, const_: hir::Const) -> Option<CompletionItem> { let db = ctx.db(); let name = const_.name(db)?; - let (name, escaped_name) = (name.to_smol_str(), name.escaped().to_smol_str()); + let (name, escaped_name) = (name.unescaped().to_smol_str(), name.to_smol_str()); let detail = const_.display(db).to_string(); let mut item = CompletionItem::new(SymbolKind::Const, ctx.source_range(), name.clone()); diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs index 4b5535718c5..9cf64691298 100644 --- a/crates/ide-completion/src/render/function.rs +++ b/crates/ide-completion/src/render/function.rs @@ -52,10 +52,10 @@ fn render( let (call, escaped_call) = match &func_kind { FuncKind::Method(_, Some(receiver)) => ( - format!("{}.{}", receiver, &name).into(), - format!("{}.{}", receiver.escaped(), name.escaped()).into(), + format!("{}.{}", receiver.unescaped(), name.unescaped()).into(), + format!("{}.{}", receiver, name).into(), ), - _ => (name.to_smol_str(), name.escaped().to_smol_str()), + _ => (name.unescaped().to_smol_str(), name.to_smol_str()), }; let mut item = CompletionItem::new( if func.self_param(db).is_some() { @@ -96,7 +96,7 @@ fn render( item.set_documentation(ctx.docs(func)) .set_deprecated(ctx.is_deprecated(func) || ctx.is_deprecated_assoc_item(func)) .detail(detail(db, func)) - .lookup_by(name.to_smol_str()); + .lookup_by(name.unescaped().to_smol_str()); match ctx.completion.config.snippet_cap { Some(cap) => { diff --git a/crates/ide-completion/src/render/literal.rs b/crates/ide-completion/src/render/literal.rs index 91a253f8fc8..707dea206be 100644 --- a/crates/ide-completion/src/render/literal.rs +++ b/crates/ide-completion/src/render/literal.rs @@ -73,7 +73,7 @@ fn render( None => (name.clone().into(), name.into(), false), }; let (qualified_name, escaped_qualified_name) = - (qualified_name.to_string(), qualified_name.escaped().to_string()); + (qualified_name.unescaped().to_string(), qualified_name.to_string()); let snippet_cap = ctx.snippet_cap(); let mut rendered = match kind { diff --git a/crates/ide-completion/src/render/macro_.rs b/crates/ide-completion/src/render/macro_.rs index ca2269f1398..eabd0bd17d6 100644 --- a/crates/ide-completion/src/render/macro_.rs +++ b/crates/ide-completion/src/render/macro_.rs @@ -46,7 +46,7 @@ fn render( ctx.source_range() }; - let (name, escaped_name) = (name.to_smol_str(), name.escaped().to_smol_str()); + let (name, escaped_name) = (name.unescaped().to_smol_str(), name.to_smol_str()); let docs = ctx.docs(macro_); let docs_str = docs.as_ref().map(Documentation::as_str).unwrap_or_default(); let is_fn_like = macro_.is_fn_like(completion.db); diff --git a/crates/ide-completion/src/render/pattern.rs b/crates/ide-completion/src/render/pattern.rs index 34a384f2f7a..1c1299e33b6 100644 --- a/crates/ide-completion/src/render/pattern.rs +++ b/crates/ide-completion/src/render/pattern.rs @@ -31,7 +31,7 @@ pub(crate) fn render_struct_pat( } let name = local_name.unwrap_or_else(|| strukt.name(ctx.db())); - let (name, escaped_name) = (name.to_smol_str(), name.escaped().to_smol_str()); + let (name, escaped_name) = (name.unescaped().to_smol_str(), name.to_smol_str()); let kind = strukt.kind(ctx.db()); let label = format_literal_label(name.as_str(), kind); let pat = render_pat(&ctx, pattern_ctx, &escaped_name, kind, &visible_fields, fields_omitted)?; @@ -53,10 +53,10 @@ pub(crate) fn render_variant_pat( let (visible_fields, fields_omitted) = visible_fields(ctx.completion, &fields, variant)?; let (name, escaped_name) = match path { - Some(path) => (path.to_string().into(), path.escaped().to_string().into()), + Some(path) => (path.unescaped().to_string().into(), path.to_string().into()), None => { let name = local_name.unwrap_or_else(|| variant.name(ctx.db())); - (name.to_smol_str(), name.escaped().to_smol_str()) + (name.unescaped().to_smol_str(), name.to_smol_str()) } }; @@ -146,7 +146,7 @@ fn render_record_as_pat( format!( "{name} {{ {}{} }}", fields.enumerate().format_with(", ", |(idx, field), f| { - f(&format_args!("{}${}", field.name(db).escaped(), idx + 1)) + f(&format_args!("{}${}", field.name(db), idx + 1)) }), if fields_omitted { ", .." } else { "" }, name = name @@ -155,7 +155,7 @@ fn render_record_as_pat( None => { format!( "{name} {{ {}{} }}", - fields.map(|field| field.name(db).escaped().to_smol_str()).format(", "), + fields.map(|field| field.name(db).to_smol_str()).format(", "), if fields_omitted { ", .." } else { "" }, name = name ) diff --git a/crates/ide-completion/src/render/type_alias.rs b/crates/ide-completion/src/render/type_alias.rs index f1b23c76e7b..de919429f2f 100644 --- a/crates/ide-completion/src/render/type_alias.rs +++ b/crates/ide-completion/src/render/type_alias.rs @@ -32,11 +32,11 @@ fn render( let name = type_alias.name(db); let (name, escaped_name) = if with_eq { ( + SmolStr::from_iter([&name.unescaped().to_smol_str(), " = "]), SmolStr::from_iter([&name.to_smol_str(), " = "]), - SmolStr::from_iter([&name.escaped().to_smol_str(), " = "]), ) } else { - (name.to_smol_str(), name.escaped().to_smol_str()) + (name.unescaped().to_smol_str(), name.to_smol_str()) }; let detail = type_alias.display(db).to_string(); diff --git a/crates/ide-completion/src/render/union_literal.rs b/crates/ide-completion/src/render/union_literal.rs index 9c9540a9bd9..bb32330f276 100644 --- a/crates/ide-completion/src/render/union_literal.rs +++ b/crates/ide-completion/src/render/union_literal.rs @@ -21,8 +21,8 @@ pub(crate) fn render_union_literal( let name = local_name.unwrap_or_else(|| un.name(ctx.db())); let (qualified_name, escaped_qualified_name) = match path { - Some(p) => (p.to_string(), p.escaped().to_string()), - None => (name.to_string(), name.escaped().to_string()), + Some(p) => (p.unescaped().to_string(), p.to_string()), + None => (name.unescaped().to_string(), name.to_string()), }; let mut item = CompletionItem::new( @@ -42,15 +42,15 @@ pub(crate) fn render_union_literal( format!( "{} {{ ${{1|{}|}}: ${{2:()}} }}$0", escaped_qualified_name, - fields.iter().map(|field| field.name(ctx.db()).escaped().to_smol_str()).format(",") + fields.iter().map(|field| field.name(ctx.db()).to_smol_str()).format(",") ) } else { format!( "{} {{ {} }}", escaped_qualified_name, - fields.iter().format_with(", ", |field, f| { - f(&format_args!("{}: ()", field.name(ctx.db()).escaped())) - }) + fields + .iter() + .format_with(", ", |field, f| { f(&format_args!("{}: ()", field.name(ctx.db()))) }) ) }; diff --git a/crates/ide-completion/src/render/variant.rs b/crates/ide-completion/src/render/variant.rs index 003a0c11ed2..664845330eb 100644 --- a/crates/ide-completion/src/render/variant.rs +++ b/crates/ide-completion/src/render/variant.rs @@ -24,9 +24,9 @@ pub(crate) fn render_record_lit( ) -> RenderedLiteral { let completions = fields.iter().enumerate().format_with(", ", |(idx, field), f| { if snippet_cap.is_some() { - f(&format_args!("{}: ${{{}:()}}", field.name(db).escaped(), idx + 1)) + f(&format_args!("{}: ${{{}:()}}", field.name(db), idx + 1)) } else { - f(&format_args!("{}: ()", field.name(db).escaped())) + f(&format_args!("{}: ()", field.name(db))) } }); diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index b0853b10fde..0181c6b8e45 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs @@ -2225,4 +2225,190 @@ macro_rules! foo { "#]], ); } + + #[test] + fn test_paths_with_raw_ident() { + check( + r#" +//- /lib.rs +$0 +mod r#mod { + #[test] + fn r#fn() {} + + /// ``` + /// ``` + fn r#for() {} + + /// ``` + /// ``` + struct r#struct<r#type>(r#type); + + /// ``` + /// ``` + impl<r#type> r#struct<r#type> { + /// ``` + /// ``` + fn r#fn() {} + } + + enum r#enum {} + impl r#struct<r#enum> { + /// ``` + /// ``` + fn r#fn() {} + } + + trait r#trait {} + + /// ``` + /// ``` + impl<T> r#trait for r#struct<T> {} +} +"#, + &[TestMod, Test, DocTest, DocTest, DocTest, DocTest, DocTest, DocTest], + expect![[r#" + [ + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 1..461, + focus_range: 5..10, + name: "r#mod", + kind: Module, + description: "mod r#mod", + }, + kind: TestMod { + path: "r#mod", + }, + cfg: None, + }, + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 17..41, + focus_range: 32..36, + name: "r#fn", + kind: Function, + }, + kind: Test { + test_id: Path( + "r#mod::r#fn", + ), + attr: TestAttr { + ignore: false, + }, + }, + cfg: None, + }, + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 47..84, + name: "r#for", + }, + kind: DocTest { + test_id: Path( + "r#mod::r#for", + ), + }, + cfg: None, + }, + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 90..146, + name: "r#struct", + }, + kind: DocTest { + test_id: Path( + "r#mod::r#struct", + ), + }, + cfg: None, + }, + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 152..266, + focus_range: 189..205, + name: "impl", + kind: Impl, + }, + kind: DocTest { + test_id: Path( + "r#struct<r#type>", + ), + }, + cfg: None, + }, + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 216..260, + name: "r#fn", + }, + kind: DocTest { + test_id: Path( + "r#mod::r#struct<r#type>::r#fn", + ), + }, + cfg: None, + }, + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 323..367, + name: "r#fn", + }, + kind: DocTest { + test_id: Path( + "r#mod::r#struct<r#enum>::r#fn", + ), + }, + cfg: None, + }, + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 401..459, + focus_range: 445..456, + name: "impl", + kind: Impl, + }, + kind: DocTest { + test_id: Path( + "r#struct<T>", + ), + }, + cfg: None, + }, + ] + "#]], + ) + } } |
