Skip to content

Commit cee1e15

Browse files
committed
Auto merge of rust-lang#12993 - lowr:patch/swap-name-and-escaped-name, r=Veykril
Make `Name` hold escaped name Resolves rust-lang#12787 Resolves rust-lang#99361 This PR effectively swaps `Name` and `EscapedName` in hir. In other words, it makes `Name` hold and print escaped raw identifiers and introduces another struct `UnescapedName` for cases where you need to print names without "r#" prefix. My rationale is that it makes it easier for us to format an escaped name into string, which is what we want when we serialize names in general. This is because we format a name into string usually when we are presenting it to the users and arguably they expect its escaped form as that's what they see and write in the source code. I split the change for `Name` into 3 commits to make it easier to follow but it also made some tests fail in the intermediate commits. I can squash them into a commit after the review if desired. I've also made similar changes for `ModPath` and `EscapedModPath` as it makes them consistent with `Name`. For reference, there was a brief discussion on this in [a zulip thread](https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frust-analyzer/topic/escaping.20.60Name.60s).
2 parents 696bee3 + ba6db3e commit cee1e15

File tree

15 files changed

+255
-63
lines changed

15 files changed

+255
-63
lines changed

crates/hir-def/src/nameres/mod_resolution.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,12 @@ impl ModDir {
7373
candidate_files.push(self.dir_path.join_attr(attr_path, self.root_non_dir_owner))
7474
}
7575
None if file_id.is_include_macro(db.upcast()) => {
76+
let name = name.unescaped();
7677
candidate_files.push(format!("{}.rs", name));
7778
candidate_files.push(format!("{}/mod.rs", name));
7879
}
7980
None => {
81+
let name = name.unescaped();
8082
candidate_files.push(format!("{}{}.rs", self.dir_path.0, name));
8183
candidate_files.push(format!("{}{}/mod.rs", self.dir_path.0, name));
8284
}

crates/hir-def/src/nameres/tests/mod_resolution.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,9 @@ pub struct Bar;
132132
expect![[r#"
133133
crate
134134
Bar: t v
135-
async: t
135+
r#async: t
136136
137-
crate::async
137+
crate::r#async
138138
Bar: t v
139139
"#]],
140140
);

crates/hir-expand/src/mod_path.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ pub struct ModPath {
2222
}
2323

2424
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
25-
pub struct EscapedModPath<'a>(&'a ModPath);
25+
pub struct UnescapedModPath<'a>(&'a ModPath);
2626

2727
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2828
pub enum PathKind {
@@ -102,8 +102,8 @@ impl ModPath {
102102
}
103103
}
104104

105-
pub fn escaped(&self) -> EscapedModPath<'_> {
106-
EscapedModPath(self)
105+
pub fn unescaped(&self) -> UnescapedModPath<'_> {
106+
UnescapedModPath(self)
107107
}
108108

109109
fn _fmt(&self, f: &mut fmt::Formatter<'_>, escaped: bool) -> fmt::Result {
@@ -134,9 +134,9 @@ impl ModPath {
134134
}
135135
first_segment = false;
136136
if escaped {
137-
segment.escaped().fmt(f)?
138-
} else {
139137
segment.fmt(f)?
138+
} else {
139+
segment.unescaped().fmt(f)?
140140
};
141141
}
142142
Ok(())
@@ -145,13 +145,13 @@ impl ModPath {
145145

146146
impl Display for ModPath {
147147
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148-
self._fmt(f, false)
148+
self._fmt(f, true)
149149
}
150150
}
151151

152-
impl<'a> Display for EscapedModPath<'a> {
152+
impl<'a> Display for UnescapedModPath<'a> {
153153
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
154-
self.0._fmt(f, true)
154+
self.0._fmt(f, false)
155155
}
156156
}
157157

crates/hir-expand/src/name.rs

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,16 @@ use syntax::{ast, SmolStr, SyntaxKind};
77
/// `Name` is a wrapper around string, which is used in hir for both references
88
/// and declarations. In theory, names should also carry hygiene info, but we are
99
/// not there yet!
10+
///
11+
/// Note that `Name` holds and prints escaped name i.e. prefixed with "r#" when it
12+
/// is a raw identifier. Use [`unescaped()`][Name::unescaped] when you need the
13+
/// name without "r#".
1014
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
1115
pub struct Name(Repr);
1216

13-
/// `EscapedName` will add a prefix "r#" to the wrapped `Name` when it is a raw identifier
17+
/// Wrapper of `Name` to print the name without "r#" even when it is a raw identifier.
1418
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
15-
pub struct EscapedName<'a>(&'a Name);
19+
pub struct UnescapedName<'a>(&'a Name);
1620

1721
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
1822
enum Repr {
@@ -34,37 +38,26 @@ fn is_raw_identifier(name: &str) -> bool {
3438
is_keyword && !matches!(name, "self" | "crate" | "super" | "Self")
3539
}
3640

37-
impl<'a> fmt::Display for EscapedName<'a> {
41+
impl<'a> fmt::Display for UnescapedName<'a> {
3842
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3943
match &self.0 .0 {
4044
Repr::Text(text) => {
41-
if is_raw_identifier(text) {
42-
write!(f, "r#{}", &text)
43-
} else {
44-
fmt::Display::fmt(&text, f)
45-
}
45+
let text = text.strip_prefix("r#").unwrap_or(text);
46+
fmt::Display::fmt(&text, f)
4647
}
4748
Repr::TupleField(idx) => fmt::Display::fmt(&idx, f),
4849
}
4950
}
5051
}
5152

52-
impl<'a> EscapedName<'a> {
53-
pub fn is_escaped(&self) -> bool {
54-
match &self.0 .0 {
55-
Repr::Text(it) => is_raw_identifier(&it),
56-
Repr::TupleField(_) => false,
57-
}
58-
}
59-
60-
/// Returns the textual representation of this name as a [`SmolStr`].
61-
/// Prefer using this over [`ToString::to_string`] if possible as this conversion is cheaper in
62-
/// the general case.
53+
impl<'a> UnescapedName<'a> {
54+
/// Returns the textual representation of this name as a [`SmolStr`]. Prefer using this over
55+
/// [`ToString::to_string`] if possible as this conversion is cheaper in the general case.
6356
pub fn to_smol_str(&self) -> SmolStr {
6457
match &self.0 .0 {
6558
Repr::Text(it) => {
66-
if is_raw_identifier(&it) {
67-
SmolStr::from_iter(["r#", &it])
59+
if let Some(stripped) = it.strip_prefix("r#") {
60+
SmolStr::new(stripped)
6861
} else {
6962
it.clone()
7063
}
@@ -97,9 +90,11 @@ impl Name {
9790

9891
/// Resolve a name from the text of token.
9992
fn resolve(raw_text: &str) -> Name {
93+
// When `raw_text` starts with "r#" but the name does not coincide with any
94+
// keyword, we never need the prefix so we strip it.
10095
match raw_text.strip_prefix("r#") {
101-
Some(text) => Name::new_text(SmolStr::new(text)),
102-
None => Name::new_text(raw_text.into()),
96+
Some(text) if !is_raw_identifier(text) => Name::new_text(SmolStr::new(text)),
97+
_ => Name::new_text(raw_text.into()),
10398
}
10499
}
105100

@@ -142,8 +137,15 @@ impl Name {
142137
}
143138
}
144139

145-
pub fn escaped(&self) -> EscapedName<'_> {
146-
EscapedName(self)
140+
pub fn unescaped(&self) -> UnescapedName<'_> {
141+
UnescapedName(self)
142+
}
143+
144+
pub fn is_escaped(&self) -> bool {
145+
match &self.0 {
146+
Repr::Text(it) => it.starts_with("r#"),
147+
Repr::TupleField(_) => false,
148+
}
147149
}
148150
}
149151

crates/ide-completion/src/completions/item_list/trait_impl.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,8 @@ fn add_type_alias_impl(
233233
type_alias: hir::TypeAlias,
234234
) {
235235
let alias_name = type_alias.name(ctx.db);
236-
let (alias_name, escaped_name) = (alias_name.to_smol_str(), alias_name.escaped().to_smol_str());
236+
let (alias_name, escaped_name) =
237+
(alias_name.unescaped().to_smol_str(), alias_name.to_smol_str());
237238

238239
let label = format!("type {} =", alias_name);
239240
let replacement = format!("type {} = ", escaped_name);

crates/ide-completion/src/render.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ pub(crate) fn render_field(
117117
) -> CompletionItem {
118118
let is_deprecated = ctx.is_deprecated(field);
119119
let name = field.name(ctx.db());
120-
let (name, escaped_name) = (name.to_smol_str(), name.escaped().to_smol_str());
120+
let (name, escaped_name) = (name.unescaped().to_smol_str(), name.to_smol_str());
121121
let mut item = CompletionItem::new(
122122
SymbolKind::Field,
123123
ctx.source_range(),
@@ -283,8 +283,8 @@ fn render_resolution_path(
283283

284284
let name = local_name.to_smol_str();
285285
let mut item = render_resolution_simple_(ctx, &local_name, import_to_add, resolution);
286-
if local_name.escaped().is_escaped() {
287-
item.insert_text(local_name.escaped().to_smol_str());
286+
if local_name.is_escaped() {
287+
item.insert_text(local_name.to_smol_str());
288288
}
289289
// Add `<>` for generic types
290290
let type_path_no_ty_args = matches!(
@@ -306,7 +306,7 @@ fn render_resolution_path(
306306
item.lookup_by(name.clone())
307307
.label(SmolStr::from_iter([&name, "<…>"]))
308308
.trigger_call_info()
309-
.insert_snippet(cap, format!("{}<$0>", local_name.escaped()));
309+
.insert_snippet(cap, format!("{}<$0>", local_name));
310310
}
311311
}
312312
}
@@ -342,7 +342,8 @@ fn render_resolution_simple_(
342342
let ctx = ctx.import_to_add(import_to_add);
343343
let kind = res_to_kind(resolution);
344344

345-
let mut item = CompletionItem::new(kind, ctx.source_range(), local_name.to_smol_str());
345+
let mut item =
346+
CompletionItem::new(kind, ctx.source_range(), local_name.unescaped().to_smol_str());
346347
item.set_relevance(ctx.completion_relevance())
347348
.set_documentation(scope_def_docs(db, resolution))
348349
.set_deprecated(scope_def_is_deprecated(&ctx, resolution));

crates/ide-completion/src/render/const_.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ pub(crate) fn render_const(ctx: RenderContext<'_>, const_: hir::Const) -> Option
1313
fn render(ctx: RenderContext<'_>, const_: hir::Const) -> Option<CompletionItem> {
1414
let db = ctx.db();
1515
let name = const_.name(db)?;
16-
let (name, escaped_name) = (name.to_smol_str(), name.escaped().to_smol_str());
16+
let (name, escaped_name) = (name.unescaped().to_smol_str(), name.to_smol_str());
1717
let detail = const_.display(db).to_string();
1818

1919
let mut item = CompletionItem::new(SymbolKind::Const, ctx.source_range(), name.clone());

crates/ide-completion/src/render/function.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,10 @@ fn render(
5252

5353
let (call, escaped_call) = match &func_kind {
5454
FuncKind::Method(_, Some(receiver)) => (
55-
format!("{}.{}", receiver, &name).into(),
56-
format!("{}.{}", receiver.escaped(), name.escaped()).into(),
55+
format!("{}.{}", receiver.unescaped(), name.unescaped()).into(),
56+
format!("{}.{}", receiver, name).into(),
5757
),
58-
_ => (name.to_smol_str(), name.escaped().to_smol_str()),
58+
_ => (name.unescaped().to_smol_str(), name.to_smol_str()),
5959
};
6060
let mut item = CompletionItem::new(
6161
if func.self_param(db).is_some() {
@@ -96,7 +96,7 @@ fn render(
9696
item.set_documentation(ctx.docs(func))
9797
.set_deprecated(ctx.is_deprecated(func) || ctx.is_deprecated_assoc_item(func))
9898
.detail(detail(db, func))
99-
.lookup_by(name.to_smol_str());
99+
.lookup_by(name.unescaped().to_smol_str());
100100

101101
match ctx.completion.config.snippet_cap {
102102
Some(cap) => {

crates/ide-completion/src/render/literal.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ fn render(
7373
None => (name.clone().into(), name.into(), false),
7474
};
7575
let (qualified_name, escaped_qualified_name) =
76-
(qualified_name.to_string(), qualified_name.escaped().to_string());
76+
(qualified_name.unescaped().to_string(), qualified_name.to_string());
7777
let snippet_cap = ctx.snippet_cap();
7878

7979
let mut rendered = match kind {

crates/ide-completion/src/render/macro_.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ fn render(
4646
ctx.source_range()
4747
};
4848

49-
let (name, escaped_name) = (name.to_smol_str(), name.escaped().to_smol_str());
49+
let (name, escaped_name) = (name.unescaped().to_smol_str(), name.to_smol_str());
5050
let docs = ctx.docs(macro_);
5151
let docs_str = docs.as_ref().map(Documentation::as_str).unwrap_or_default();
5252
let is_fn_like = macro_.is_fn_like(completion.db);

crates/ide-completion/src/render/pattern.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ pub(crate) fn render_struct_pat(
3131
}
3232

3333
let name = local_name.unwrap_or_else(|| strukt.name(ctx.db()));
34-
let (name, escaped_name) = (name.to_smol_str(), name.escaped().to_smol_str());
34+
let (name, escaped_name) = (name.unescaped().to_smol_str(), name.to_smol_str());
3535
let kind = strukt.kind(ctx.db());
3636
let label = format_literal_label(name.as_str(), kind);
3737
let pat = render_pat(&ctx, pattern_ctx, &escaped_name, kind, &visible_fields, fields_omitted)?;
@@ -53,10 +53,10 @@ pub(crate) fn render_variant_pat(
5353
let (visible_fields, fields_omitted) = visible_fields(ctx.completion, &fields, variant)?;
5454

5555
let (name, escaped_name) = match path {
56-
Some(path) => (path.to_string().into(), path.escaped().to_string().into()),
56+
Some(path) => (path.unescaped().to_string().into(), path.to_string().into()),
5757
None => {
5858
let name = local_name.unwrap_or_else(|| variant.name(ctx.db()));
59-
(name.to_smol_str(), name.escaped().to_smol_str())
59+
(name.unescaped().to_smol_str(), name.to_smol_str())
6060
}
6161
};
6262

@@ -146,7 +146,7 @@ fn render_record_as_pat(
146146
format!(
147147
"{name} {{ {}{} }}",
148148
fields.enumerate().format_with(", ", |(idx, field), f| {
149-
f(&format_args!("{}${}", field.name(db).escaped(), idx + 1))
149+
f(&format_args!("{}${}", field.name(db), idx + 1))
150150
}),
151151
if fields_omitted { ", .." } else { "" },
152152
name = name
@@ -155,7 +155,7 @@ fn render_record_as_pat(
155155
None => {
156156
format!(
157157
"{name} {{ {}{} }}",
158-
fields.map(|field| field.name(db).escaped().to_smol_str()).format(", "),
158+
fields.map(|field| field.name(db).to_smol_str()).format(", "),
159159
if fields_omitted { ", .." } else { "" },
160160
name = name
161161
)

crates/ide-completion/src/render/type_alias.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,11 @@ fn render(
3232
let name = type_alias.name(db);
3333
let (name, escaped_name) = if with_eq {
3434
(
35+
SmolStr::from_iter([&name.unescaped().to_smol_str(), " = "]),
3536
SmolStr::from_iter([&name.to_smol_str(), " = "]),
36-
SmolStr::from_iter([&name.escaped().to_smol_str(), " = "]),
3737
)
3838
} else {
39-
(name.to_smol_str(), name.escaped().to_smol_str())
39+
(name.unescaped().to_smol_str(), name.to_smol_str())
4040
};
4141
let detail = type_alias.display(db).to_string();
4242

crates/ide-completion/src/render/union_literal.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ pub(crate) fn render_union_literal(
2121
let name = local_name.unwrap_or_else(|| un.name(ctx.db()));
2222

2323
let (qualified_name, escaped_qualified_name) = match path {
24-
Some(p) => (p.to_string(), p.escaped().to_string()),
25-
None => (name.to_string(), name.escaped().to_string()),
24+
Some(p) => (p.unescaped().to_string(), p.to_string()),
25+
None => (name.unescaped().to_string(), name.to_string()),
2626
};
2727

2828
let mut item = CompletionItem::new(
@@ -42,15 +42,15 @@ pub(crate) fn render_union_literal(
4242
format!(
4343
"{} {{ ${{1|{}|}}: ${{2:()}} }}$0",
4444
escaped_qualified_name,
45-
fields.iter().map(|field| field.name(ctx.db()).escaped().to_smol_str()).format(",")
45+
fields.iter().map(|field| field.name(ctx.db()).to_smol_str()).format(",")
4646
)
4747
} else {
4848
format!(
4949
"{} {{ {} }}",
5050
escaped_qualified_name,
51-
fields.iter().format_with(", ", |field, f| {
52-
f(&format_args!("{}: ()", field.name(ctx.db()).escaped()))
53-
})
51+
fields
52+
.iter()
53+
.format_with(", ", |field, f| { f(&format_args!("{}: ()", field.name(ctx.db()))) })
5454
)
5555
};
5656

crates/ide-completion/src/render/variant.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ pub(crate) fn render_record_lit(
2424
) -> RenderedLiteral {
2525
let completions = fields.iter().enumerate().format_with(", ", |(idx, field), f| {
2626
if snippet_cap.is_some() {
27-
f(&format_args!("{}: ${{{}:()}}", field.name(db).escaped(), idx + 1))
27+
f(&format_args!("{}: ${{{}:()}}", field.name(db), idx + 1))
2828
} else {
29-
f(&format_args!("{}: ()", field.name(db).escaped()))
29+
f(&format_args!("{}: ()", field.name(db)))
3030
}
3131
});
3232

0 commit comments

Comments
 (0)