rust/src/librustc_codegen_utils/symbol_names.rs

468 lines
18 KiB
Rust
Raw Normal View History

//! The Rust Linkage Model and Symbol Names
//! =======================================
//!
//! The semantic model of Rust linkage is, broadly, that "there's no global
//! namespace" between crates. Our aim is to preserve the illusion of this
//! model despite the fact that it's not *quite* possible to implement on
//! modern linkers. We initially didn't use system linkers at all, but have
//! been convinced of their utility.
//!
//! There are a few issues to handle:
//!
//! - Linkers operate on a flat namespace, so we have to flatten names.
//! We do this using the C++ namespace-mangling technique. Foo::bar
//! symbols and such.
//!
//! - Symbols for distinct items with the same *name* need to get different
//! linkage-names. Examples of this are monomorphizations of functions or
//! items within anonymous scopes that end up having the same path.
//!
//! - Symbols in different crates but with same names "within" the crate need
//! to get different linkage-names.
//!
//! - Symbol names should be deterministic: Two consecutive runs of the
//! compiler over the same code base should produce the same symbol names for
//! the same items.
//!
//! - Symbol names should not depend on any global properties of the code base,
//! so that small modifications to the code base do not result in all symbols
//! changing. In previous versions of the compiler, symbol names incorporated
//! the SVH (Stable Version Hash) of the crate. This scheme turned out to be
//! infeasible when used in conjunction with incremental compilation because
//! small code changes would invalidate all symbols generated previously.
//!
//! - Even symbols from different versions of the same crate should be able to
//! live next to each other without conflict.
//!
//! In order to fulfill the above requirements the following scheme is used by
//! the compiler:
//!
//! The main tool for avoiding naming conflicts is the incorporation of a 64-bit
//! hash value into every exported symbol name. Anything that makes a difference
//! to the symbol being named, but does not show up in the regular path needs to
//! be fed into this hash:
//!
//! - Different monomorphizations of the same item have the same path but differ
//! in their concrete type parameters, so these parameters are part of the
//! data being digested for the symbol hash.
//!
//! - Rust allows items to be defined in anonymous scopes, such as in
//! `fn foo() { { fn bar() {} } { fn bar() {} } }`. Both `bar` functions have
//! the path `foo::bar`, since the anonymous scopes do not contribute to the
//! path of an item. The compiler already handles this case via so-called
//! disambiguating `DefPaths` which use indices to distinguish items with the
//! same name. The DefPaths of the functions above are thus `foo[0]::bar[0]`
//! and `foo[0]::bar[1]`. In order to incorporate this disambiguation
//! information into the symbol name too, these indices are fed into the
//! symbol hash, so that the above two symbols would end up with different
//! hash values.
//!
//! The two measures described above suffice to avoid intra-crate conflicts. In
//! order to also avoid inter-crate conflicts two more measures are taken:
//!
//! - The name of the crate containing the symbol is prepended to the symbol
//! name, i.e., symbols are "crate qualified". For example, a function `foo` in
//! module `bar` in crate `baz` would get a symbol name like
//! `baz::bar::foo::{hash}` instead of just `bar::foo::{hash}`. This avoids
//! simple conflicts between functions from different crates.
//!
//! - In order to be able to also use symbols from two versions of the same
//! crate (which naturally also have the same name), a stronger measure is
//! required: The compiler accepts an arbitrary "disambiguator" value via the
2018-11-12 12:05:20 -06:00
//! `-C metadata` command-line argument. This disambiguator is then fed into
//! the symbol hash of every exported item. Consequently, the symbols in two
//! identical crates but with different disambiguators are not in conflict
//! with each other. This facility is mainly intended to be used by build
//! tools like Cargo.
//!
//! A note on symbol name stability
//! -------------------------------
//! Previous versions of the compiler resorted to feeding NodeIds into the
//! symbol hash in order to disambiguate between items with the same path. The
//! current version of the name generation algorithm takes great care not to do
//! that, since NodeIds are notoriously unstable: A small change to the
//! code base will offset all NodeIds after the change and thus, much as using
//! the SVH in the hash, invalidate an unbounded number of symbol names. This
//! makes re-using previously compiled code for incremental compilation
//! virtually impossible. Thus, symbol hash generation exclusively relies on
//! DefPaths which are much more robust in the face of changes to the code base.
use rustc::hir::def_id::{DefId, LOCAL_CRATE};
2018-08-25 09:56:16 -05:00
use rustc::hir::Node;
use rustc::hir::CodegenFnAttrFlags;
2018-05-08 08:38:29 -05:00
use rustc::hir::map::definitions::DefPathData;
use rustc::ich::NodeIdHashingMode;
use rustc::ty::item_path::{self, ItemPathBuffer, RootMode};
2018-06-13 08:44:43 -05:00
use rustc::ty::query::Providers;
2019-02-09 08:11:53 -06:00
use rustc::ty::subst::SubstsRef;
2018-05-08 08:38:29 -05:00
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
use rustc::util::common::record_time;
2018-05-08 08:38:29 -05:00
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_mir::monomorphize::item::{InstantiationMode, MonoItem, MonoItemExt};
use rustc_mir::monomorphize::Instance;
use syntax_pos::symbol::Symbol;
2019-02-08 06:06:07 -06:00
use log::debug;
2019-01-27 18:16:59 -06:00
use std::fmt::Write;
use std::mem::discriminant;
2019-02-08 06:06:07 -06:00
pub fn provide(providers: &mut Providers<'_>) {
*providers = Providers {
def_symbol_name,
symbol_name,
..*providers
};
}
2018-05-08 08:38:29 -05:00
fn get_symbol_hash<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
2018-05-08 08:38:29 -05:00
// the DefId of the item this name is for
def_id: DefId,
rustc: Don't inline in CGUs at -O0 This commit tweaks the behavior of inlining functions into multiple codegen units when rustc is compiling in debug mode. Today rustc will unconditionally treat `#[inline]` functions by translating them into all codegen units that they're needed within, marking the linkage as `internal`. This commit changes the behavior so that in debug mode (compiling at `-O0`) rustc will instead only translate `#[inline]` functions into *one* codegen unit, forcing all other codegen units to reference this one copy. The goal here is to improve debug compile times by reducing the amount of translation that happens on behalf of multiple codegen units. It was discovered in #44941 that increasing the number of codegen units had the adverse side effect of increasing the overal work done by the compiler, and the suspicion here was that the compiler was inlining, translating, and codegen'ing more functions with more codegen units (for example `String` would be basically inlined into all codegen units if used). The strategy in this commit should reduce the cost of `#[inline]` functions to being equivalent to one codegen unit, which is only translating and codegen'ing inline functions once. Collected [data] shows that this does indeed improve the situation from [before] as the overall cpu-clock time increases at a much slower rate and when pinned to one core rustc does not consume significantly more wall clock time than with one codegen unit. One caveat of this commit is that the symbol names for inlined functions that are only translated once needed some slight tweaking. These inline functions could be translated into multiple crates and we need to make sure the symbols don't collideA so the crate name/disambiguator is mixed in to the symbol name hash in these situations. [data]: https://github.com/rust-lang/rust/issues/44941#issuecomment-334880911 [before]: https://github.com/rust-lang/rust/issues/44941#issuecomment-334583384
2017-10-06 16:59:33 -05:00
2018-05-08 08:38:29 -05:00
// instance this name will be for
instance: Instance<'tcx>,
2018-05-08 08:38:29 -05:00
// type of the item, without any generic
// parameters substituted; this is
// included in the hash as a kind of
// safeguard.
item_type: Ty<'tcx>,
2018-05-08 08:38:29 -05:00
// values for generic type parameters,
// if any.
2019-02-09 08:11:53 -06:00
substs: SubstsRef<'tcx>,
2018-05-08 08:38:29 -05:00
) -> u64 {
debug!(
"get_symbol_hash(def_id={:?}, parameters={:?})",
def_id, substs
);
let mut hasher = StableHasher::<u64>::new();
let mut hcx = tcx.create_stable_hashing_context();
record_time(&tcx.sess.perf_stats.symbol_hash_time, || {
// the main symbol name is not necessarily unique; hash in the
// compiler's internal def-path, guaranteeing each symbol has a
// truly unique path
tcx.def_path_hash(def_id).hash_stable(&mut hcx, &mut hasher);
// Include the main item-type. Note that, in this case, the
// assertions about `needs_subst` may not hold, but this item-type
// ought to be the same for every reference anyway.
assert!(!item_type.has_erasable_regions());
hcx.while_hashing_spans(false, |hcx| {
hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| {
item_type.hash_stable(hcx, &mut hasher);
});
});
2018-05-08 18:57:45 -05:00
// If this is a function, we hash the signature as well.
// This is not *strictly* needed, but it may help in some
// situations, see the `run-make/a-b-a-linker-guard` test.
if let ty::FnDef(..) = item_type.sty {
2018-05-08 18:57:45 -05:00
item_type.fn_sig(tcx).hash_stable(&mut hcx, &mut hasher);
}
// also include any type parameters (for generic items)
rustc: Don't inline in CGUs at -O0 This commit tweaks the behavior of inlining functions into multiple codegen units when rustc is compiling in debug mode. Today rustc will unconditionally treat `#[inline]` functions by translating them into all codegen units that they're needed within, marking the linkage as `internal`. This commit changes the behavior so that in debug mode (compiling at `-O0`) rustc will instead only translate `#[inline]` functions into *one* codegen unit, forcing all other codegen units to reference this one copy. The goal here is to improve debug compile times by reducing the amount of translation that happens on behalf of multiple codegen units. It was discovered in #44941 that increasing the number of codegen units had the adverse side effect of increasing the overal work done by the compiler, and the suspicion here was that the compiler was inlining, translating, and codegen'ing more functions with more codegen units (for example `String` would be basically inlined into all codegen units if used). The strategy in this commit should reduce the cost of `#[inline]` functions to being equivalent to one codegen unit, which is only translating and codegen'ing inline functions once. Collected [data] shows that this does indeed improve the situation from [before] as the overall cpu-clock time increases at a much slower rate and when pinned to one core rustc does not consume significantly more wall clock time than with one codegen unit. One caveat of this commit is that the symbol names for inlined functions that are only translated once needed some slight tweaking. These inline functions could be translated into multiple crates and we need to make sure the symbols don't collideA so the crate name/disambiguator is mixed in to the symbol name hash in these situations. [data]: https://github.com/rust-lang/rust/issues/44941#issuecomment-334880911 [before]: https://github.com/rust-lang/rust/issues/44941#issuecomment-334583384
2017-10-06 16:59:33 -05:00
assert!(!substs.has_erasable_regions());
assert!(!substs.needs_subst());
substs.hash_stable(&mut hcx, &mut hasher);
rustc: Don't inline in CGUs at -O0 This commit tweaks the behavior of inlining functions into multiple codegen units when rustc is compiling in debug mode. Today rustc will unconditionally treat `#[inline]` functions by translating them into all codegen units that they're needed within, marking the linkage as `internal`. This commit changes the behavior so that in debug mode (compiling at `-O0`) rustc will instead only translate `#[inline]` functions into *one* codegen unit, forcing all other codegen units to reference this one copy. The goal here is to improve debug compile times by reducing the amount of translation that happens on behalf of multiple codegen units. It was discovered in #44941 that increasing the number of codegen units had the adverse side effect of increasing the overal work done by the compiler, and the suspicion here was that the compiler was inlining, translating, and codegen'ing more functions with more codegen units (for example `String` would be basically inlined into all codegen units if used). The strategy in this commit should reduce the cost of `#[inline]` functions to being equivalent to one codegen unit, which is only translating and codegen'ing inline functions once. Collected [data] shows that this does indeed improve the situation from [before] as the overall cpu-clock time increases at a much slower rate and when pinned to one core rustc does not consume significantly more wall clock time than with one codegen unit. One caveat of this commit is that the symbol names for inlined functions that are only translated once needed some slight tweaking. These inline functions could be translated into multiple crates and we need to make sure the symbols don't collideA so the crate name/disambiguator is mixed in to the symbol name hash in these situations. [data]: https://github.com/rust-lang/rust/issues/44941#issuecomment-334880911 [before]: https://github.com/rust-lang/rust/issues/44941#issuecomment-334583384
2017-10-06 16:59:33 -05:00
let is_generic = substs.non_erasable_generics().next().is_some();
let avoid_cross_crate_conflicts =
// If this is an instance of a generic function, we also hash in
// the ID of the instantiating crate. This avoids symbol conflicts
// in case the same instances is emitted in two crates of the same
// project.
is_generic ||
// If we're dealing with an instance of a function that's inlined from
// another crate but we're marking it as globally shared to our
// compliation (aka we're not making an internal copy in each of our
// codegen units) then this symbol may become an exported (but hidden
// visibility) symbol. This means that multiple crates may do the same
// and we want to be sure to avoid any symbol conflicts here.
match MonoItem::Fn(instance).instantiation_mode(tcx) {
InstantiationMode::GloballyShared { may_conflict: true } => true,
_ => false,
};
rustc: Don't inline in CGUs at -O0 This commit tweaks the behavior of inlining functions into multiple codegen units when rustc is compiling in debug mode. Today rustc will unconditionally treat `#[inline]` functions by translating them into all codegen units that they're needed within, marking the linkage as `internal`. This commit changes the behavior so that in debug mode (compiling at `-O0`) rustc will instead only translate `#[inline]` functions into *one* codegen unit, forcing all other codegen units to reference this one copy. The goal here is to improve debug compile times by reducing the amount of translation that happens on behalf of multiple codegen units. It was discovered in #44941 that increasing the number of codegen units had the adverse side effect of increasing the overal work done by the compiler, and the suspicion here was that the compiler was inlining, translating, and codegen'ing more functions with more codegen units (for example `String` would be basically inlined into all codegen units if used). The strategy in this commit should reduce the cost of `#[inline]` functions to being equivalent to one codegen unit, which is only translating and codegen'ing inline functions once. Collected [data] shows that this does indeed improve the situation from [before] as the overall cpu-clock time increases at a much slower rate and when pinned to one core rustc does not consume significantly more wall clock time than with one codegen unit. One caveat of this commit is that the symbol names for inlined functions that are only translated once needed some slight tweaking. These inline functions could be translated into multiple crates and we need to make sure the symbols don't collideA so the crate name/disambiguator is mixed in to the symbol name hash in these situations. [data]: https://github.com/rust-lang/rust/issues/44941#issuecomment-334880911 [before]: https://github.com/rust-lang/rust/issues/44941#issuecomment-334583384
2017-10-06 16:59:33 -05:00
if avoid_cross_crate_conflicts {
let instantiating_crate = if is_generic {
if !def_id.is_local() && tcx.sess.opts.share_generics() {
// If we are re-using a monomorphization from another crate,
// we have to compute the symbol hash accordingly.
2018-05-08 08:38:29 -05:00
let upstream_monomorphizations = tcx.upstream_monomorphizations_for(def_id);
2018-05-08 08:38:29 -05:00
upstream_monomorphizations
.and_then(|monos| monos.get(&substs).cloned())
.unwrap_or(LOCAL_CRATE)
} else {
LOCAL_CRATE
}
} else {
LOCAL_CRATE
};
2018-05-08 08:38:29 -05:00
(&tcx.original_crate_name(instantiating_crate).as_str()[..])
.hash_stable(&mut hcx, &mut hasher);
(&tcx.crate_disambiguator(instantiating_crate)).hash_stable(&mut hcx, &mut hasher);
}
// We want to avoid accidental collision between different types of instances.
// Especially, VtableShim may overlap with its original instance without this.
discriminant(&instance.def).hash_stable(&mut hcx, &mut hasher);
});
// 64 bits should be enough to avoid collisions.
2017-04-24 12:32:11 -05:00
hasher.finish()
}
2018-05-08 08:38:29 -05:00
fn def_symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> ty::SymbolName {
let mut buffer = SymbolPathBuffer::new(tcx);
item_path::with_forced_absolute_paths(|| {
tcx.push_item_path(&mut buffer, def_id);
});
buffer.into_interned()
}
2018-05-08 08:38:29 -05:00
fn symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>) -> ty::SymbolName {
ty::SymbolName {
name: Symbol::intern(&compute_symbol_name(tcx, instance)).as_interned_str(),
}
}
2018-05-08 08:38:29 -05:00
fn compute_symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>) -> String {
let def_id = instance.def_id();
let substs = instance.substs;
2018-05-08 08:38:29 -05:00
debug!("symbol_name(def_id={:?}, substs={:?})", def_id, substs);
let hir_id = tcx.hir().as_local_hir_id(def_id);
2019-01-12 18:06:50 -06:00
if def_id.is_local() {
if tcx.plugin_registrar_fn(LOCAL_CRATE) == Some(def_id) {
let disambiguator = tcx.sess.local_crate_disambiguator();
return tcx.sess.generate_plugin_registrar_symbol(disambiguator);
}
2019-01-12 14:49:18 -06:00
if tcx.proc_macro_decls_static(LOCAL_CRATE) == Some(def_id) {
let disambiguator = tcx.sess.local_crate_disambiguator();
return tcx.sess.generate_proc_macro_decls_symbol(disambiguator);
}
}
// FIXME(eddyb) Precompute a custom symbol name based on attributes.
let is_foreign = if let Some(id) = hir_id {
match tcx.hir().get_by_hir_id(id) {
2018-08-25 09:56:16 -05:00
Node::ForeignItem(_) => true,
2018-05-08 08:38:29 -05:00
_ => false,
}
} else {
2017-05-05 08:15:08 -05:00
tcx.is_foreign_item(def_id)
};
let attrs = tcx.codegen_fn_attrs(def_id);
if is_foreign {
if let Some(name) = attrs.link_name {
return name.to_string();
}
// Don't mangle foreign items.
return tcx.item_name(def_id).to_string();
}
if let Some(name) = &attrs.export_name {
// Use provided name
return name.to_string();
}
if attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) {
// Don't mangle
return tcx.item_name(def_id).to_string();
}
// We want to compute the "type" of this item. Unfortunately, some
// kinds of items (e.g., closures) don't have an entry in the
// item-type array. So walk back up the find the closest parent
// that DOES have an entry.
let mut ty_def_id = def_id;
let instance_ty;
loop {
let key = tcx.def_key(ty_def_id);
match key.disambiguated_data.data {
2018-05-08 08:38:29 -05:00
DefPathData::TypeNs(_) | DefPathData::ValueNs(_) => {
instance_ty = tcx.type_of(ty_def_id);
break;
}
_ => {
// if we're making a symbol for something, there ought
// to be a value or type-def or something in there
// *somewhere*
ty_def_id.index = key.parent.unwrap_or_else(|| {
2018-05-08 08:38:29 -05:00
bug!(
"finding type for {:?}, encountered def-id {:?} with no \
parent",
def_id,
ty_def_id
);
});
}
}
}
// Erase regions because they may not be deterministic when hashed
// and should not matter anyhow.
let instance_ty = tcx.erase_regions(&instance_ty);
rustc: Don't inline in CGUs at -O0 This commit tweaks the behavior of inlining functions into multiple codegen units when rustc is compiling in debug mode. Today rustc will unconditionally treat `#[inline]` functions by translating them into all codegen units that they're needed within, marking the linkage as `internal`. This commit changes the behavior so that in debug mode (compiling at `-O0`) rustc will instead only translate `#[inline]` functions into *one* codegen unit, forcing all other codegen units to reference this one copy. The goal here is to improve debug compile times by reducing the amount of translation that happens on behalf of multiple codegen units. It was discovered in #44941 that increasing the number of codegen units had the adverse side effect of increasing the overal work done by the compiler, and the suspicion here was that the compiler was inlining, translating, and codegen'ing more functions with more codegen units (for example `String` would be basically inlined into all codegen units if used). The strategy in this commit should reduce the cost of `#[inline]` functions to being equivalent to one codegen unit, which is only translating and codegen'ing inline functions once. Collected [data] shows that this does indeed improve the situation from [before] as the overall cpu-clock time increases at a much slower rate and when pinned to one core rustc does not consume significantly more wall clock time than with one codegen unit. One caveat of this commit is that the symbol names for inlined functions that are only translated once needed some slight tweaking. These inline functions could be translated into multiple crates and we need to make sure the symbols don't collideA so the crate name/disambiguator is mixed in to the symbol name hash in these situations. [data]: https://github.com/rust-lang/rust/issues/44941#issuecomment-334880911 [before]: https://github.com/rust-lang/rust/issues/44941#issuecomment-334583384
2017-10-06 16:59:33 -05:00
let hash = get_symbol_hash(tcx, def_id, instance, instance_ty, substs);
let mut buf = SymbolPathBuffer::from_interned(tcx.def_symbol_name(def_id), tcx);
if instance.is_vtable_shim() {
buf.push("{{vtable-shim}}");
}
buf.finish(hash)
}
// Follow C++ namespace-mangling style, see
// http://en.wikipedia.org/wiki/Name_mangling for more info.
//
// It turns out that on macOS you can actually have arbitrary symbols in
// function names (at least when given to LLVM), but this is not possible
// when using unix's linker. Perhaps one day when we just use a linker from LLVM
// we won't need to do this name mangling. The problem with name mangling is
// that it seriously limits the available characters. For example we can't
// have things like &T in symbol names when one would theoretically
// want them for things like impls of traits on that type.
//
// To be able to work on all platforms and get *some* reasonable output, we
// use C++ name-mangling.
2019-01-27 18:16:59 -06:00
#[derive(Debug)]
struct SymbolPathBuffer {
result: String,
2018-05-08 08:38:29 -05:00
temp_buf: String,
2019-01-27 18:16:59 -06:00
strict_naming: bool,
}
2019-01-27 18:16:59 -06:00
impl SymbolPathBuffer {
fn new(tcx: TyCtxt<'_, '_, '_>) -> Self {
let mut result = SymbolPathBuffer {
result: String::with_capacity(64),
2018-05-08 08:38:29 -05:00
temp_buf: String::with_capacity(16),
2019-01-27 18:16:59 -06:00
strict_naming: tcx.has_strict_asm_symbol_naming(),
};
result.result.push_str("_ZN"); // _Z == Begin name-sequence, N == nested
result
}
2019-01-27 18:16:59 -06:00
fn from_interned(symbol: ty::SymbolName, tcx: TyCtxt<'_, '_, '_>) -> Self {
let mut result = SymbolPathBuffer {
result: String::with_capacity(64),
2018-05-08 08:38:29 -05:00
temp_buf: String::with_capacity(16),
2019-01-27 18:16:59 -06:00
strict_naming: tcx.has_strict_asm_symbol_naming(),
};
2018-05-26 07:12:38 -05:00
result.result.push_str(&symbol.as_str());
result
}
fn into_interned(self) -> ty::SymbolName {
2018-05-08 08:38:29 -05:00
ty::SymbolName {
name: Symbol::intern(&self.result).as_interned_str(),
}
}
2017-04-24 12:32:11 -05:00
fn finish(mut self, hash: u64) -> String {
// E = end name-sequence
let _ = write!(self.result, "17h{:016x}E", hash);
self.result
}
2019-01-27 18:16:59 -06:00
// Name sanitation. LLVM will happily accept identifiers with weird names, but
// gas doesn't!
// gas accepts the following characters in symbols: a-z, A-Z, 0-9, ., _, $
// NVPTX assembly has more strict naming rules than gas, so additionally, dots
// are replaced with '$' there.
fn sanitize_and_append(&mut self, s: &str) {
self.temp_buf.clear();
2019-01-27 18:16:59 -06:00
for c in s.chars() {
match c {
// Escape these with $ sequences
'@' => self.temp_buf.push_str("$SP$"),
'*' => self.temp_buf.push_str("$BP$"),
'&' => self.temp_buf.push_str("$RF$"),
'<' => self.temp_buf.push_str("$LT$"),
'>' => self.temp_buf.push_str("$GT$"),
'(' => self.temp_buf.push_str("$LP$"),
')' => self.temp_buf.push_str("$RP$"),
',' => self.temp_buf.push_str("$C$"),
'-' | ':' => if self.strict_naming {
// NVPTX doesn't support these characters in symbol names.
self.temp_buf.push('$')
}
else {
// '.' doesn't occur in types and functions, so reuse it
// for ':' and '-'
self.temp_buf.push('.')
},
'.' => if self.strict_naming {
self.temp_buf.push('$')
}
else {
self.temp_buf.push('.')
},
// These are legal symbols
'a'..='z' | 'A'..='Z' | '0'..='9' | '_' | '$' => self.temp_buf.push(c),
_ => {
self.temp_buf.push('$');
for c in c.escape_unicode().skip(1) {
match c {
'{' => {}
'}' => self.temp_buf.push('$'),
c => self.temp_buf.push(c),
}
}
}
}
}
let need_underscore = {
// Underscore-qualify anything that didn't start as an ident.
!self.temp_buf.is_empty()
&& self.temp_buf.as_bytes()[0] != '_' as u8
&& !(self.temp_buf.as_bytes()[0] as char).is_xid_start()
};
2018-05-08 08:38:29 -05:00
let _ = write!(
self.result,
"{}",
self.temp_buf.len() + (need_underscore as usize)
);
2019-01-27 18:16:59 -06:00
if need_underscore {
self.result.push('_');
}
2019-01-27 18:16:59 -06:00
self.result.push_str(&self.temp_buf);
}
}
2019-01-27 18:16:59 -06:00
impl ItemPathBuffer for SymbolPathBuffer {
fn root_mode(&self) -> &RootMode {
const ABSOLUTE: &RootMode = &RootMode::Absolute;
ABSOLUTE
}
2016-03-01 07:18:21 -06:00
2019-01-27 18:16:59 -06:00
fn push(&mut self, text: &str) {
self.sanitize_and_append(text);
2016-03-01 07:18:21 -06:00
}
}