Skip to content

Commit 889e4c6

Browse files
committed
Remove Ident::empty.
All uses have been removed. And it's nonsensical: an identifier by definition has at least one char. The commits adds an is-non-empty assertion to `Ident::new` to enforce this, and converts some `Ident` constructions to use `Ident::new`. Adding the assertion requires making `Ident::new` and `Ident::with_dummy_span` non-const, which is no great loss. The commit amends a couple of places that do path splitting to ensure no empty identifiers are created.
1 parent ddfa5f6 commit 889e4c6

File tree

8 files changed

+55
-45
lines changed

8 files changed

+55
-45
lines changed

compiler/rustc_ast_pretty/src/pprust/state/expr.rs

+1
Original file line numberDiff line numberDiff line change
@@ -876,6 +876,7 @@ impl<'a> State<'a> {
876876
}
877877
}
878878
} else {
879+
self.end(); // Close the ibox for the pattern.
879880
self.word(",");
880881
}
881882
self.end(); // Close enclosing cbox.

compiler/rustc_hir/src/hir/tests.rs

+8-15
Original file line numberDiff line numberDiff line change
@@ -50,21 +50,14 @@ fn trait_object_roundtrips() {
5050
}
5151

5252
fn trait_object_roundtrips_impl(syntax: TraitObjectSyntax) {
53-
let unambig = TyKind::TraitObject::<'_, ()>(
54-
&[],
55-
TaggedRef::new(
56-
&const {
57-
Lifetime {
58-
hir_id: HirId::INVALID,
59-
ident: Ident::new(sym::name, DUMMY_SP),
60-
kind: LifetimeKind::Static,
61-
source: LifetimeSource::Other,
62-
syntax: LifetimeSyntax::Hidden,
63-
}
64-
},
65-
syntax,
66-
),
67-
);
53+
let lt = Lifetime {
54+
hir_id: HirId::INVALID,
55+
ident: Ident::new(sym::name, DUMMY_SP),
56+
kind: LifetimeKind::Static,
57+
source: LifetimeSource::Other,
58+
syntax: LifetimeSyntax::Hidden,
59+
};
60+
let unambig = TyKind::TraitObject::<'_, ()>(&[], TaggedRef::new(&lt, syntax));
6861
let unambig_to_ambig = unsafe { std::mem::transmute::<_, TyKind<'_, AmbigArg>>(unambig) };
6962

7063
match unambig_to_ambig {

compiler/rustc_parse/src/parser/expr.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3828,7 +3828,7 @@ impl<'a> Parser<'a> {
38283828
// Convert `label` -> `'label`,
38293829
// so that nameres doesn't complain about non-existing label
38303830
let label = format!("'{}", ident.name);
3831-
let ident = Ident { name: Symbol::intern(&label), span: ident.span };
3831+
let ident = Ident::new(Symbol::intern(&label), ident.span);
38323832

38333833
self.dcx().emit_err(errors::ExpectedLabelFoundIdent {
38343834
span: ident.span,

compiler/rustc_resolve/src/build_reduced_graph.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -549,7 +549,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
549549
source = module_path.pop().unwrap();
550550
if rename.is_none() {
551551
// Keep the span of `self`, but the name of `foo`
552-
ident = Ident { name: source.ident.name, span: self_span };
552+
ident = Ident::new(source.ident.name, self_span);
553553
}
554554
}
555555
} else {
@@ -597,7 +597,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
597597
if let Some(crate_name) = crate_name {
598598
// `crate_name` should not be interpreted as relative.
599599
module_path.push(Segment::from_ident_and_id(
600-
Ident { name: kw::PathRoot, span: source.ident.span },
600+
Ident::new(kw::PathRoot, source.ident.span),
601601
self.r.next_node_id(),
602602
));
603603
source.ident.name = crate_name;

compiler/rustc_resolve/src/lib.rs

+18-7
Original file line numberDiff line numberDiff line change
@@ -2157,13 +2157,24 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
21572157
ns: Namespace,
21582158
parent_scope: ParentScope<'ra>,
21592159
) -> Option<Res> {
2160-
let mut segments =
2161-
Vec::from_iter(path_str.split("::").map(Ident::from_str).map(Segment::from_ident));
2162-
if let Some(segment) = segments.first_mut() {
2163-
if segment.ident.name == kw::Empty {
2164-
segment.ident.name = kw::PathRoot;
2165-
}
2166-
}
2160+
let segments: Result<Vec<_>, ()> = path_str
2161+
.split("::")
2162+
.enumerate()
2163+
.map(|(i, s)| {
2164+
let sym = if s.is_empty() {
2165+
if i == 0 {
2166+
// For a path like `::a::b`, use `kw::PathRoot` as the leading segment.
2167+
kw::PathRoot
2168+
} else {
2169+
return Err(()); // occurs in cases like `String::`
2170+
}
2171+
} else {
2172+
Symbol::intern(s)
2173+
};
2174+
Ok(Segment::from_ident(Ident { name: sym, span: DUMMY_SP }))
2175+
})
2176+
.collect();
2177+
let Ok(segments) = segments else { return None };
21672178

21682179
match self.maybe_resolve_path(&segments, Some(ns), &parent_scope, None) {
21692180
PathResult::Module(ModuleOrUniformRoot::Module(module)) => Some(module.res().unwrap()),

compiler/rustc_span/src/symbol.rs

+9-13
Original file line numberDiff line numberDiff line change
@@ -2337,35 +2337,31 @@ pub const STDLIB_STABLE_CRATES: &[Symbol] = &[sym::std, sym::core, sym::alloc, s
23372337

23382338
#[derive(Copy, Clone, Eq, HashStable_Generic, Encodable, Decodable)]
23392339
pub struct Ident {
2340+
// `name` should never be the empty symbol. If you are considering that,
2341+
// you are probably conflating "empty identifer with "no identifier" and
2342+
// you should use `Option<Ident>` instead.
23402343
pub name: Symbol,
23412344
pub span: Span,
23422345
}
23432346

23442347
impl Ident {
23452348
#[inline]
23462349
/// Constructs a new identifier from a symbol and a span.
2347-
pub const fn new(name: Symbol, span: Span) -> Ident {
2350+
pub fn new(name: Symbol, span: Span) -> Ident {
2351+
assert_ne!(name, kw::Empty);
23482352
Ident { name, span }
23492353
}
23502354

23512355
/// Constructs a new identifier with a dummy span.
23522356
#[inline]
2353-
pub const fn with_dummy_span(name: Symbol) -> Ident {
2357+
pub fn with_dummy_span(name: Symbol) -> Ident {
23542358
Ident::new(name, DUMMY_SP)
23552359
}
23562360

2357-
/// This is best avoided, because it blurs the lines between "empty
2358-
/// identifier" and "no identifier". Using `Option<Ident>` is preferable,
2359-
/// where possible, because that is unambiguous.
2360-
#[inline]
2361-
pub fn empty() -> Ident {
2362-
Ident::with_dummy_span(kw::Empty)
2363-
}
2364-
23652361
// For dummy identifiers that are never used and absolutely must be
2366-
// present, it's better to use `Ident::dummy` than `Ident::Empty`, because
2367-
// it's clearer that it's intended as a dummy value, and more likely to be
2368-
// detected if it accidentally does get used.
2362+
// present. Note that this does *not* use the empty symbol; `sym::dummy`
2363+
// makes it clear that it's intended as a dummy value, and is more likely
2364+
// to be detected if it accidentally does get used.
23692365
#[inline]
23702366
pub fn dummy() -> Ident {
23712367
Ident::with_dummy_span(sym::dummy)

src/librustdoc/clean/render_macro_matchers.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ fn print_tts(printer: &mut Printer<'_>, tts: &TokenStream) {
167167
}
168168

169169
fn usually_needs_space_between_keyword_and_open_delim(symbol: Symbol, span: Span) -> bool {
170-
let ident = Ident { name: symbol, span };
170+
let ident = Ident::new(symbol, span);
171171
let is_keyword = ident.is_used_keyword() || ident.is_unused_keyword();
172172
if !is_keyword {
173173
// An identifier that is not a keyword usually does not need a space

src/librustdoc/passes/collect_intra_doc_links.rs

+15-6
Original file line numberDiff line numberDiff line change
@@ -496,12 +496,21 @@ impl<'tcx> LinkCollector<'_, 'tcx> {
496496

497497
// Try looking for methods and associated items.
498498
// NB: `path_root` could be empty when resolving in the root namespace (e.g. `::std`).
499-
let (path_root, item_str) = path_str.rsplit_once("::").ok_or_else(|| {
500-
// If there's no `::`, it's not an associated item.
501-
// So we can be sure that `rustc_resolve` was accurate when it said it wasn't resolved.
502-
debug!("found no `::`, assuming {path_str} was correctly not in scope");
503-
UnresolvedPath { item_id, module_id, partial_res: None, unresolved: path_str.into() }
504-
})?;
499+
let (path_root, item_str) = match path_str.rsplit_once("::") {
500+
Some(res @ (_path_root, item_str)) if !item_str.is_empty() => res,
501+
_ => {
502+
// If there's no `::`, or the `::` is at the end (e.g. `String::`) it's not an
503+
// associated item. So we can be sure that `rustc_resolve` was accurate when it
504+
// said it wasn't resolved.
505+
debug!("`::` missing or at end, assuming {path_str} was not in scope");
506+
return Err(UnresolvedPath {
507+
item_id,
508+
module_id,
509+
partial_res: None,
510+
unresolved: path_str.into(),
511+
});
512+
}
513+
};
505514
let item_name = Symbol::intern(item_str);
506515

507516
// FIXME(#83862): this arbitrarily gives precedence to primitives over modules to support

0 commit comments

Comments
 (0)