Skip to main content

ruff_python_ast/
name.rs

1use std::borrow::{Borrow, Cow};
2use std::fmt::{Debug, Display, Formatter, Write};
3use std::hash::{Hash, Hasher};
4use std::ops::Deref;
5
6use arrayvec::ArrayVec;
7use char_str::{CharStr, CharString};
8
9use crate::Expr;
10use crate::generated::ExprName;
11
12/// An immutable name.
13///
14/// # Choosing a string representation
15///
16/// On 64-bit targets, [`CharStr`] occupies 16 bytes and stores up to 16 UTF-8 bytes inline. Longer
17/// values use an exactly-sized, reference-counted allocation, so cloning a heap-backed value
18/// reuses its allocation. [`compact_str::CompactString`] occupies 24 bytes, stores up to 24 bytes
19/// inline, and remains mutable; cloning a heap-backed value copies its contents into a new
20/// allocation.
21///
22/// Prefer `CharStr` for immutable text that is retained densely or passed between owners, when
23/// either the smaller handle or structural sharing offsets the extra heap allocations for values
24/// between 17 and 24 bytes. Prefer `CompactString` for uniquely owned text, especially when it is
25/// built incrementally, mutated, or commonly falls in that 17-to-24-byte range.
26///
27/// `Name` uses `CharStr` because names appear throughout the AST and repeated heap-backed parser
28/// names share an allocation. By contrast, [`crate::DebugText`] uses `CompactString` because it
29/// builds a uniquely owned buffer incrementally, and `ty_module_resolver::ModuleName` uses
30/// `CompactString` because module names can be extended in place.
31///
32/// Converting a borrowed `&str` into `CharStr` creates a new value and does not preserve structural
33/// sharing. When an API retains text already held in a `CharStr` (including a `Name`), pass or clone
34/// the owned value rather than converting it through `&str`. This is especially relevant at Salsa
35/// interning boundaries.
36#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
37#[cfg_attr(feature = "salsa", derive(salsa::SalsaValue))]
38#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
39#[cfg_attr(feature = "cache", derive(ruff_macros::CacheKey))]
40#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))]
41#[cfg_attr(
42    feature = "schemars",
43    derive(schemars::JsonSchema),
44    schemars(with = "String")
45)]
46pub struct Name(CharStr);
47
48impl Name {
49    #[inline]
50    pub fn empty() -> Self {
51        Self(CharStr::new())
52    }
53
54    #[inline]
55    pub fn new(name: impl AsRef<str>) -> Self {
56        Self(CharStr::from(name.as_ref()))
57    }
58
59    /// Creates an inline name, returning `None` if `name` does not fit inline.
60    #[inline]
61    pub fn new_inline(name: impl AsRef<str>) -> Option<Self> {
62        CharStr::new_inline(name.as_ref()).map(Self)
63    }
64
65    /// Creates an exactly-sized, heap-allocated name.
66    #[inline]
67    pub fn new_heap(name: impl AsRef<str>) -> Self {
68        Self(CharStr::new_heap(name.as_ref()))
69    }
70
71    #[inline]
72    pub const fn new_static(name: &'static str) -> Self {
73        Self(CharStr::from_static_str(name))
74    }
75
76    /// Creates an exactly-sized name by concatenating string slices.
77    ///
78    /// The combined length is computed up front, so heap storage is allocated at most once.
79    #[inline]
80    pub fn concat<T: AsRef<str>>(slices: &[T]) -> Self {
81        Self(CharStr::concat(slices))
82    }
83
84    /// Creates an exactly-sized name by joining string slices with a separator.
85    ///
86    /// Like [`Name::concat`], this computes the combined length up front, so heap storage is
87    /// allocated at most once. For dynamically formatted names, use [`Name::from`] with
88    /// [`format_char!`](char_str::format_char). If a [`CharStr`] is sufficient, use
89    /// [`format_char_str!`](char_str::format_char_str) instead.
90    #[inline]
91    pub fn join<T: AsRef<str>>(slices: &[T], separator: &str) -> Self {
92        Self(CharStr::join(slices, separator))
93    }
94
95    #[inline]
96    pub fn as_str(&self) -> &str {
97        self.0.as_str()
98    }
99}
100
101impl Debug for Name {
102    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
103        write!(f, "Name({:?})", self.as_str())
104    }
105}
106
107impl AsRef<str> for Name {
108    #[inline]
109    fn as_ref(&self) -> &str {
110        self.as_str()
111    }
112}
113
114impl Deref for Name {
115    type Target = str;
116
117    #[inline]
118    fn deref(&self) -> &Self::Target {
119        self.as_str()
120    }
121}
122
123impl Borrow<str> for Name {
124    #[inline]
125    fn borrow(&self) -> &str {
126        self.as_str()
127    }
128}
129
130impl<'a> From<&'a str> for Name {
131    #[inline]
132    fn from(s: &'a str) -> Self {
133        Name::new(s)
134    }
135}
136
137impl From<String> for Name {
138    #[inline]
139    fn from(s: String) -> Self {
140        Name(s.into())
141    }
142}
143
144impl<'a> From<&'a String> for Name {
145    #[inline]
146    fn from(s: &'a String) -> Self {
147        Name::new(s)
148    }
149}
150
151impl<'a> From<Cow<'a, str>> for Name {
152    #[inline]
153    fn from(cow: Cow<'a, str>) -> Self {
154        Name(cow.into())
155    }
156}
157
158impl From<Box<str>> for Name {
159    #[inline]
160    fn from(b: Box<str>) -> Self {
161        Name(b.into())
162    }
163}
164
165#[cfg(feature = "salsa")]
166impl salsa::Lookup<Name> for &str {
167    #[inline]
168    fn into_owned(self) -> Name {
169        Name::new(self)
170    }
171}
172
173#[cfg(feature = "salsa")]
174impl salsa::HashEqLike<&str> for Name {
175    #[inline]
176    fn eq(&self, data: &&str) -> bool {
177        self == *data
178    }
179}
180
181impl From<Name> for String {
182    #[inline]
183    fn from(name: Name) -> Self {
184        name.0.into()
185    }
186}
187
188impl From<Name> for CharStr {
189    #[inline]
190    fn from(name: Name) -> Self {
191        name.0
192    }
193}
194
195#[cfg(feature = "salsa")]
196impl salsa::Lookup<compact_str::CompactString> for Name {
197    #[inline]
198    fn into_owned(self) -> compact_str::CompactString {
199        compact_str::CompactString::new(self.as_str())
200    }
201}
202
203#[cfg(feature = "salsa")]
204impl salsa::Lookup<compact_str::CompactString> for &Name {
205    #[inline]
206    fn into_owned(self) -> compact_str::CompactString {
207        compact_str::CompactString::new(self.as_str())
208    }
209}
210
211#[cfg(feature = "salsa")]
212impl salsa::HashEqLike<Name> for compact_str::CompactString {
213    #[inline]
214    fn eq(&self, data: &Name) -> bool {
215        self.as_str() == data.as_str()
216    }
217}
218
219#[cfg(feature = "salsa")]
220impl salsa::HashEqLike<&Name> for compact_str::CompactString {
221    #[inline]
222    fn eq(&self, data: &&Name) -> bool {
223        self.as_str() == data.as_str()
224    }
225}
226
227impl From<CharString> for Name {
228    #[inline]
229    fn from(name: CharString) -> Self {
230        Self(name.freeze())
231    }
232}
233
234impl FromIterator<char> for Name {
235    fn from_iter<I: IntoIterator<Item = char>>(iter: I) -> Self {
236        Self(iter.into_iter().collect())
237    }
238}
239
240impl std::fmt::Display for Name {
241    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
242        f.write_str(self.as_str())
243    }
244}
245
246impl PartialEq<str> for Name {
247    #[inline]
248    fn eq(&self, other: &str) -> bool {
249        self.0 == other
250    }
251}
252
253impl PartialEq<Name> for str {
254    #[inline]
255    fn eq(&self, other: &Name) -> bool {
256        other == self
257    }
258}
259
260impl PartialEq<&str> for Name {
261    #[inline]
262    fn eq(&self, other: &&str) -> bool {
263        self.0 == *other
264    }
265}
266
267impl PartialEq<Name> for &str {
268    #[inline]
269    fn eq(&self, other: &Name) -> bool {
270        other == self
271    }
272}
273
274impl PartialEq<String> for Name {
275    fn eq(&self, other: &String) -> bool {
276        self == other.as_str()
277    }
278}
279
280impl PartialEq<Name> for String {
281    #[inline]
282    fn eq(&self, other: &Name) -> bool {
283        other == self
284    }
285}
286
287impl PartialEq<&String> for Name {
288    #[inline]
289    fn eq(&self, other: &&String) -> bool {
290        self == other.as_str()
291    }
292}
293
294impl PartialEq<Name> for &String {
295    #[inline]
296    fn eq(&self, other: &Name) -> bool {
297        other == self
298    }
299}
300
301/// A representation of a qualified name, like `typing.List`.
302#[derive(Debug, Clone, PartialEq, Eq, Hash)]
303pub struct QualifiedName<'a>(SegmentsVec<'a>);
304
305impl<'a> QualifiedName<'a> {
306    /// Create a [`QualifiedName`] from a dotted name.
307    ///
308    /// ```rust
309    /// # use ruff_python_ast::name::QualifiedName;
310    ///
311    /// assert_eq!(QualifiedName::from_dotted_name("typing.List").segments(), ["typing", "List"]);
312    /// assert_eq!(QualifiedName::from_dotted_name("list").segments(), ["", "list"]);
313    /// ```
314    #[inline]
315    pub fn from_dotted_name(name: &'a str) -> Self {
316        if let Some(dot) = name.find('.') {
317            let mut builder = QualifiedNameBuilder::default();
318            builder.push(&name[..dot]);
319            builder.extend(name[dot + 1..].split('.'));
320            builder.build()
321        } else {
322            Self::builtin(name)
323        }
324    }
325
326    /// Creates a name that's guaranteed not be a built in
327    #[inline]
328    pub fn user_defined(name: &'a str) -> Self {
329        name.split('.').collect()
330    }
331
332    /// Creates a qualified name for a built in
333    #[inline]
334    pub fn builtin(name: &'a str) -> Self {
335        debug_assert!(!name.contains('.'));
336        Self(SegmentsVec::from_slice(&["", name]))
337    }
338
339    #[inline]
340    pub fn segments(&self) -> &[&'a str] {
341        self.0.as_slice()
342    }
343
344    /// If the first segment is empty, the `CallPath` represents a "builtin binding".
345    ///
346    /// A builtin binding is the binding that a symbol has if it was part of Python's
347    /// global scope without any imports taking place. However, if builtin members are
348    /// accessed explicitly via the `builtins` module, they will not have a
349    /// "builtin binding", so this method will return `false`.
350    ///
351    /// Ex) `["", "bool"]` -> `"bool"`
352    fn is_builtin(&self) -> bool {
353        matches!(self.segments(), ["", ..])
354    }
355
356    /// If the call path is dot-prefixed, it's an unresolved relative import.
357    /// Ex) `[".foo", "bar"]` -> `".foo.bar"`
358    pub fn is_unresolved_import(&self) -> bool {
359        matches!(self.segments(), [".", ..])
360    }
361
362    pub fn starts_with(&self, other: &QualifiedName<'_>) -> bool {
363        self.segments().starts_with(other.segments())
364    }
365
366    /// Appends a member to the qualified name.
367    #[must_use]
368    pub fn append_member(self, member: &'a str) -> Self {
369        let mut inner = self.0;
370        inner.push(member);
371        Self(inner)
372    }
373
374    /// Extends the qualified name using the given members.
375    #[must_use]
376    pub fn extend_members<T: IntoIterator<Item = &'a str>>(self, members: T) -> Self {
377        let mut inner = self.0;
378        inner.extend(members);
379        Self(inner)
380    }
381}
382
383impl Display for QualifiedName<'_> {
384    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
385        let segments = self.segments();
386
387        if self.is_unresolved_import() {
388            let mut iter = segments.iter();
389            for segment in iter.by_ref() {
390                if *segment == "." {
391                    f.write_char('.')?;
392                } else {
393                    f.write_str(segment)?;
394                    break;
395                }
396            }
397            for segment in iter {
398                f.write_char('.')?;
399                f.write_str(segment)?;
400            }
401        } else {
402            let segments = if self.is_builtin() {
403                &segments[1..]
404            } else {
405                segments
406            };
407
408            let mut first = true;
409            for segment in segments {
410                if !first {
411                    f.write_char('.')?;
412                }
413
414                f.write_str(segment)?;
415                first = false;
416            }
417        }
418
419        Ok(())
420    }
421}
422
423impl<'a> FromIterator<&'a str> for QualifiedName<'a> {
424    fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
425        Self(SegmentsVec::from_iter(iter))
426    }
427}
428
429#[derive(Debug, Clone, Default)]
430pub struct QualifiedNameBuilder<'a> {
431    segments: SegmentsVec<'a>,
432}
433
434impl<'a> QualifiedNameBuilder<'a> {
435    pub fn with_capacity(capacity: usize) -> Self {
436        Self {
437            segments: SegmentsVec::with_capacity(capacity),
438        }
439    }
440
441    #[inline]
442    pub(crate) fn is_empty(&self) -> bool {
443        self.segments.is_empty()
444    }
445
446    #[inline]
447    pub fn push(&mut self, segment: &'a str) {
448        self.segments.push(segment);
449    }
450
451    #[inline]
452    pub(crate) fn pop(&mut self) {
453        self.segments.pop();
454    }
455
456    #[inline]
457    pub fn extend(&mut self, segments: impl IntoIterator<Item = &'a str>) {
458        self.segments.extend(segments);
459    }
460
461    #[inline]
462    pub(crate) fn extend_from_slice(&mut self, segments: &[&'a str]) {
463        self.segments.extend_from_slice(segments);
464    }
465
466    pub fn build(self) -> QualifiedName<'a> {
467        QualifiedName(self.segments)
468    }
469}
470
471#[derive(Debug, Clone, PartialEq, Eq, Hash)]
472pub struct UnqualifiedName<'a>(SegmentsVec<'a>);
473
474impl<'a> UnqualifiedName<'a> {
475    /// Convert an `Expr` to its [`UnqualifiedName`] (like `["typing", "List"]`).
476    pub fn from_expr(expr: &'a Expr) -> Option<Self> {
477        // Unroll the loop up to eight times, to match the maximum number of expected attributes.
478        // In practice, unrolling appears to give about a 4x speed-up on this hot path.
479        let attr1 = match expr {
480            Expr::Attribute(attr1) => attr1,
481            // Ex) `foo`
482            Expr::Name(ExprName { id, .. }) => return Some(Self::from_slice(&[id.as_str()])),
483            _ => return None,
484        };
485
486        let attr2 = match attr1.value.as_ref() {
487            Expr::Attribute(attr2) => attr2,
488            // Ex) `foo.bar`
489            Expr::Name(ExprName { id, .. }) => {
490                return Some(Self::from_slice(&[id.as_str(), attr1.attr.as_str()]));
491            }
492            _ => return None,
493        };
494
495        let attr3 = match attr2.value.as_ref() {
496            Expr::Attribute(attr3) => attr3,
497            // Ex) `foo.bar.baz`
498            Expr::Name(ExprName { id, .. }) => {
499                return Some(Self::from_slice(&[
500                    id.as_str(),
501                    attr2.attr.as_str(),
502                    attr1.attr.as_str(),
503                ]));
504            }
505            _ => return None,
506        };
507
508        let attr4 = match attr3.value.as_ref() {
509            Expr::Attribute(attr4) => attr4,
510            // Ex) `foo.bar.baz.bop`
511            Expr::Name(ExprName { id, .. }) => {
512                return Some(Self::from_slice(&[
513                    id.as_str(),
514                    attr3.attr.as_str(),
515                    attr2.attr.as_str(),
516                    attr1.attr.as_str(),
517                ]));
518            }
519            _ => return None,
520        };
521
522        let attr5 = match attr4.value.as_ref() {
523            Expr::Attribute(attr5) => attr5,
524            // Ex) `foo.bar.baz.bop.bap`
525            Expr::Name(ExprName { id, .. }) => {
526                return Some(Self::from_slice(&[
527                    id.as_str(),
528                    attr4.attr.as_str(),
529                    attr3.attr.as_str(),
530                    attr2.attr.as_str(),
531                    attr1.attr.as_str(),
532                ]));
533            }
534            _ => return None,
535        };
536
537        let attr6 = match attr5.value.as_ref() {
538            Expr::Attribute(attr6) => attr6,
539            // Ex) `foo.bar.baz.bop.bap.bab`
540            Expr::Name(ExprName { id, .. }) => {
541                return Some(Self::from_slice(&[
542                    id.as_str(),
543                    attr5.attr.as_str(),
544                    attr4.attr.as_str(),
545                    attr3.attr.as_str(),
546                    attr2.attr.as_str(),
547                    attr1.attr.as_str(),
548                ]));
549            }
550            _ => return None,
551        };
552
553        let attr7 = match attr6.value.as_ref() {
554            Expr::Attribute(attr7) => attr7,
555            // Ex) `foo.bar.baz.bop.bap.bab.bob`
556            Expr::Name(ExprName { id, .. }) => {
557                return Some(Self::from_slice(&[
558                    id.as_str(),
559                    attr6.attr.as_str(),
560                    attr5.attr.as_str(),
561                    attr4.attr.as_str(),
562                    attr3.attr.as_str(),
563                    attr2.attr.as_str(),
564                    attr1.attr.as_str(),
565                ]));
566            }
567            _ => return None,
568        };
569
570        let attr8 = match attr7.value.as_ref() {
571            Expr::Attribute(attr8) => attr8,
572            // Ex) `foo.bar.baz.bop.bap.bab.bob.bib`
573            Expr::Name(ExprName { id, .. }) => {
574                return Some(Self(SegmentsVec::from([
575                    id.as_str(),
576                    attr7.attr.as_str(),
577                    attr6.attr.as_str(),
578                    attr5.attr.as_str(),
579                    attr4.attr.as_str(),
580                    attr3.attr.as_str(),
581                    attr2.attr.as_str(),
582                    attr1.attr.as_str(),
583                ])));
584            }
585            _ => return None,
586        };
587
588        let mut segments = Vec::with_capacity(SMALL_LEN * 2);
589
590        let mut current = &*attr8.value;
591
592        loop {
593            current = match current {
594                Expr::Attribute(attr) => {
595                    segments.push(attr.attr.as_str());
596                    &*attr.value
597                }
598                Expr::Name(ExprName { id, .. }) => {
599                    segments.push(id.as_str());
600                    break;
601                }
602                _ => {
603                    return None;
604                }
605            }
606        }
607
608        segments.reverse();
609
610        // Append the attributes we visited before calling into the recursion.
611        segments.extend_from_slice(&[
612            attr8.attr.as_str(),
613            attr7.attr.as_str(),
614            attr6.attr.as_str(),
615            attr5.attr.as_str(),
616            attr4.attr.as_str(),
617            attr3.attr.as_str(),
618            attr2.attr.as_str(),
619            attr1.attr.as_str(),
620        ]);
621
622        Some(Self(SegmentsVec::from(segments)))
623    }
624
625    #[inline]
626    fn from_slice(segments: &[&'a str]) -> Self {
627        Self(SegmentsVec::from_slice(segments))
628    }
629
630    pub fn segments(&self) -> &[&'a str] {
631        self.0.as_slice()
632    }
633}
634
635impl Display for UnqualifiedName<'_> {
636    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
637        let mut first = true;
638        for segment in self.segments() {
639            if !first {
640                f.write_char('.')?;
641            }
642
643            f.write_str(segment)?;
644            first = false;
645        }
646
647        Ok(())
648    }
649}
650
651impl<'a> FromIterator<&'a str> for UnqualifiedName<'a> {
652    #[inline]
653    fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
654        Self(iter.into_iter().collect())
655    }
656}
657
658/// A smallvec like storage for qualified and unqualified name segments.
659///
660/// Stores up to 8 segments inline, and falls back to a heap-allocated vector for names with more segments.
661///
662/// ## Note
663/// The inline variant uses `ArrayVec` rather than `SmallVec` v1 because `SmallVec`'s type
664/// definition has a variance problem. The incorrect variance leads lifetime inference in the
665/// `SemanticModel` astray, causing all sorts of "strange" lifetime errors.
666#[derive(Clone)]
667enum SegmentsVec<'a> {
668    Stack(SegmentsStack<'a>),
669    Heap(Vec<&'a str>),
670}
671
672impl<'a> SegmentsVec<'a> {
673    /// Creates an empty segment vec.
674    fn new() -> Self {
675        Self::Stack(SegmentsStack::default())
676    }
677
678    /// Creates a segment vec that has reserved storage for up to `capacity` items.
679    fn with_capacity(capacity: usize) -> Self {
680        if capacity <= SMALL_LEN {
681            Self::new()
682        } else {
683            Self::Heap(Vec::with_capacity(capacity))
684        }
685    }
686
687    #[cfg(test)]
688    const fn is_spilled(&self) -> bool {
689        matches!(self, SegmentsVec::Heap(_))
690    }
691
692    /// Initializes the segments from a slice.
693    #[inline]
694    fn from_slice(slice: &[&'a str]) -> Self {
695        match SegmentsStack::try_from(slice) {
696            Ok(stack) => SegmentsVec::Stack(stack),
697            Err(_) => SegmentsVec::Heap(slice.to_vec()),
698        }
699    }
700
701    /// Returns the segments as a slice.
702    #[inline]
703    fn as_slice(&self) -> &[&'a str] {
704        match self {
705            Self::Stack(stack) => stack.as_slice(),
706            Self::Heap(heap) => heap.as_slice(),
707        }
708    }
709
710    /// Pushes `name` to the end of the segments.
711    ///
712    /// Spills to the heap if the segments are stored on the stack and the 9th segment is pushed.
713    #[inline]
714    fn push(&mut self, name: &'a str) {
715        match self {
716            SegmentsVec::Stack(stack) => {
717                if let Err(error) = stack.try_push(name) {
718                    let mut segments = Vec::with_capacity(stack.len() * 2);
719                    segments.extend(stack.iter().copied());
720                    segments.push(error.element());
721                    *self = SegmentsVec::Heap(segments);
722                }
723            }
724            SegmentsVec::Heap(heap) => {
725                heap.push(name);
726            }
727        }
728    }
729
730    /// Pops the last segment from the end and returns it.
731    ///
732    /// Returns `None` if the vector is empty.
733    #[inline]
734    fn pop(&mut self) -> Option<&'a str> {
735        match self {
736            SegmentsVec::Stack(stack) => stack.pop(),
737            SegmentsVec::Heap(heap) => heap.pop(),
738        }
739    }
740
741    #[inline]
742    fn extend_from_slice(&mut self, slice: &[&'a str]) {
743        match self {
744            SegmentsVec::Stack(stack) => {
745                if stack.try_extend_from_slice(slice).is_err() {
746                    let mut segments = Vec::with_capacity(stack.len() + slice.len());
747                    segments.extend(stack.iter().copied());
748                    segments.extend_from_slice(slice);
749                    *self = SegmentsVec::Heap(segments);
750                }
751            }
752            SegmentsVec::Heap(heap) => heap.extend_from_slice(slice),
753        }
754    }
755}
756
757impl Default for SegmentsVec<'_> {
758    fn default() -> Self {
759        Self::new()
760    }
761}
762
763impl Debug for SegmentsVec<'_> {
764    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
765        f.debug_list().entries(self.as_slice()).finish()
766    }
767}
768
769impl<'a> Deref for SegmentsVec<'a> {
770    type Target = [&'a str];
771    fn deref(&self) -> &Self::Target {
772        self.as_slice()
773    }
774}
775
776impl<'b> PartialEq<SegmentsVec<'b>> for SegmentsVec<'_> {
777    fn eq(&self, other: &SegmentsVec<'b>) -> bool {
778        self.as_slice() == other.as_slice()
779    }
780}
781
782impl Eq for SegmentsVec<'_> {}
783
784impl Hash for SegmentsVec<'_> {
785    fn hash<H: Hasher>(&self, state: &mut H) {
786        self.as_slice().hash(state);
787    }
788}
789
790impl<'a> FromIterator<&'a str> for SegmentsVec<'a> {
791    #[inline]
792    fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
793        let mut segments = SegmentsVec::default();
794        segments.extend(iter);
795        segments
796    }
797}
798
799impl<'a> From<[&'a str; 8]> for SegmentsVec<'a> {
800    #[inline]
801    fn from(segments: [&'a str; 8]) -> Self {
802        SegmentsVec::Stack(SegmentsStack::from(segments))
803    }
804}
805
806impl<'a> From<Vec<&'a str>> for SegmentsVec<'a> {
807    #[inline]
808    fn from(segments: Vec<&'a str>) -> Self {
809        SegmentsVec::Heap(segments)
810    }
811}
812
813impl<'a> Extend<&'a str> for SegmentsVec<'a> {
814    #[inline]
815    fn extend<T: IntoIterator<Item = &'a str>>(&mut self, iter: T) {
816        match self {
817            SegmentsVec::Stack(stack) => {
818                let mut iter = iter.into_iter();
819                let (lower, _) = iter.size_hint();
820
821                if lower > stack.remaining_capacity() {
822                    let mut segments = Vec::with_capacity(stack.len() + lower);
823                    segments.extend(stack.iter().copied());
824                    segments.extend(iter);
825                    *self = SegmentsVec::Heap(segments);
826                    return;
827                }
828
829                while let Some(name) = iter.next() {
830                    if let Err(error) = stack.try_push(name) {
831                        let mut segments = Vec::with_capacity(stack.len() * 2);
832                        segments.extend(stack.iter().copied());
833                        segments.push(error.element());
834                        segments.extend(iter);
835                        *self = SegmentsVec::Heap(segments);
836                        return;
837                    }
838                }
839            }
840            SegmentsVec::Heap(heap) => {
841                heap.extend(iter);
842            }
843        }
844    }
845}
846
847const SMALL_LEN: usize = 8;
848type SegmentsStack<'a> = ArrayVec<&'a str, SMALL_LEN>;
849
850#[cfg(test)]
851mod tests {
852    #[cfg(feature = "salsa")]
853    use std::hash::{DefaultHasher, Hash, Hasher};
854
855    #[cfg(feature = "salsa")]
856    use crate::name::Name;
857    use crate::name::SegmentsVec;
858
859    #[cfg(feature = "salsa")]
860    #[test]
861    fn salsa_lookup_name_from_str() {
862        let name = Name::new("member");
863        let lookup = "member";
864
865        let mut name_hasher = DefaultHasher::new();
866        name.hash(&mut name_hasher);
867        let mut lookup_hasher = DefaultHasher::new();
868        lookup.hash(&mut lookup_hasher);
869
870        assert_eq!(name_hasher.finish(), lookup_hasher.finish());
871        assert!(salsa::HashEqLike::<&str>::eq(&name, &lookup));
872        assert_eq!(salsa::Lookup::<Name>::into_owned(lookup), name);
873    }
874
875    #[test]
876    fn empty_vec() {
877        let empty = SegmentsVec::new();
878        assert_eq!(empty.as_slice(), &[] as &[&str]);
879        assert!(!empty.is_spilled());
880    }
881
882    #[test]
883    fn from_slice_stack() {
884        let stack = SegmentsVec::from_slice(&["a", "b", "c"]);
885
886        assert_eq!(stack.as_slice(), &["a", "b", "c"]);
887        assert!(!stack.is_spilled());
888    }
889
890    #[test]
891    fn from_slice_stack_capacity() {
892        let stack = SegmentsVec::from_slice(&["a", "b", "c", "d", "e", "f", "g", "h"]);
893
894        assert_eq!(stack.as_slice(), &["a", "b", "c", "d", "e", "f", "g", "h"]);
895        assert!(!stack.is_spilled());
896    }
897
898    #[test]
899    fn from_slice_heap() {
900        let heap = SegmentsVec::from_slice(&["a", "b", "c", "d", "e", "f", "g", "h", "i"]);
901
902        assert_eq!(
903            heap.as_slice(),
904            &["a", "b", "c", "d", "e", "f", "g", "h", "i"]
905        );
906        assert!(heap.is_spilled());
907    }
908
909    #[test]
910    fn push_stack() {
911        let mut stack = SegmentsVec::from_slice(&["a", "b", "c"]);
912        stack.push("d");
913        stack.push("e");
914
915        assert_eq!(stack.as_slice(), &["a", "b", "c", "d", "e"]);
916        assert!(!stack.is_spilled());
917    }
918
919    #[test]
920    fn push_stack_spill() {
921        let mut stack = SegmentsVec::from_slice(&["a", "b", "c", "d", "e", "f", "g"]);
922        stack.push("h");
923
924        assert!(!stack.is_spilled());
925
926        stack.push("i");
927
928        assert_eq!(
929            stack.as_slice(),
930            &["a", "b", "c", "d", "e", "f", "g", "h", "i"]
931        );
932        assert!(stack.is_spilled());
933    }
934
935    #[test]
936    fn pop_stack() {
937        let mut stack = SegmentsVec::from_slice(&["a", "b", "c", "d", "e"]);
938        assert_eq!(stack.pop(), Some("e"));
939        assert_eq!(stack.pop(), Some("d"));
940        assert_eq!(stack.pop(), Some("c"));
941        assert_eq!(stack.pop(), Some("b"));
942        assert_eq!(stack.pop(), Some("a"));
943        assert_eq!(stack.pop(), None);
944
945        assert!(!stack.is_spilled());
946    }
947
948    #[test]
949    fn pop_heap() {
950        let mut heap = SegmentsVec::from_slice(&["a", "b", "c", "d", "e", "f", "g", "h", "i"]);
951
952        assert_eq!(heap.pop(), Some("i"));
953        assert_eq!(heap.pop(), Some("h"));
954        assert_eq!(heap.pop(), Some("g"));
955
956        assert!(heap.is_spilled());
957    }
958
959    #[test]
960    fn extend_from_slice_stack() {
961        let mut stack = SegmentsVec::from_slice(&["a", "b", "c"]);
962        stack.extend_from_slice(&["d", "e", "f"]);
963
964        assert_eq!(stack.as_slice(), &["a", "b", "c", "d", "e", "f"]);
965        assert!(!stack.is_spilled());
966    }
967
968    #[test]
969    fn extend_from_slice_stack_spill() {
970        let mut spilled = SegmentsVec::from_slice(&["a", "b", "c", "d", "e", "f"]);
971        spilled.extend_from_slice(&["g", "h", "i", "j"]);
972
973        assert_eq!(
974            spilled.as_slice(),
975            &["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]
976        );
977        assert!(spilled.is_spilled());
978    }
979
980    #[test]
981    fn extend_from_slice_heap() {
982        let mut heap = SegmentsVec::from_slice(&["a", "b", "c", "d", "e", "f", "g", "h", "i"]);
983        assert!(heap.is_spilled());
984
985        heap.extend_from_slice(&["j", "k", "l"]);
986
987        assert_eq!(
988            heap.as_slice(),
989            &["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"]
990        );
991    }
992
993    #[test]
994    fn extend_stack() {
995        let mut stack = SegmentsVec::from_slice(&["a", "b", "c"]);
996        stack.extend(["d", "e", "f"]);
997
998        assert_eq!(stack.as_slice(), &["a", "b", "c", "d", "e", "f"]);
999        assert!(!stack.is_spilled());
1000    }
1001
1002    #[test]
1003    fn extend_stack_spilled() {
1004        let mut stack = SegmentsVec::from_slice(&["a", "b", "c", "d", "e", "f"]);
1005        stack.extend(["g", "h", "i", "j"]);
1006
1007        assert_eq!(
1008            stack.as_slice(),
1009            &["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]
1010        );
1011        assert!(stack.is_spilled());
1012    }
1013
1014    #[test]
1015    fn extend_heap() {
1016        let mut heap = SegmentsVec::from_slice(&["a", "b", "c", "d", "e", "f", "g", "h", "i"]);
1017        assert!(heap.is_spilled());
1018
1019        heap.extend(["j", "k", "l"]);
1020
1021        assert_eq!(
1022            heap.as_slice(),
1023            &["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"]
1024        );
1025    }
1026}