1919//! # The Call-site Hierarchy
2020//!
2121//! `ExpnData::call_site` in rustc, `MacroCallLoc::call_site` in rust-analyzer.
22+ #[ cfg( feature = "salsa" ) ]
2223use crate :: Edition ;
24+
2325use std:: fmt;
2426
2527/// A syntax context describes a hierarchy tracking order of macro definitions.
@@ -282,7 +284,7 @@ const _: () = {
282284 let fields = SyntaxContext :: ingredient ( zalsa) . data ( zalsa, id) ;
283285 fields. edition
284286 }
285- None => Edition :: from_u32 ( SyntaxContext :: MAX_ID - self . into_u32 ( ) ) ,
287+ None => Edition :: from_u32 ( SyntaxContext :: MAX_ROOT_ID - self . into_u32 ( ) ) ,
286288 }
287289 }
288290
@@ -332,32 +334,9 @@ const _: () = {
332334 }
333335} ;
334336
335- impl SyntaxContext {
336- #[ inline]
337- pub fn is_root ( self ) -> bool {
338- ( SyntaxContext :: MAX_ID - Edition :: LATEST as u32 ) <= self . into_u32 ( )
339- && self . into_u32 ( ) <= ( SyntaxContext :: MAX_ID - Edition :: Edition2015 as u32 )
340- }
341-
342- #[ inline]
343- pub fn remove_root_edition ( & mut self ) {
344- if self . is_root ( ) {
345- * self = Self :: root ( Edition :: Edition2015 ) ;
346- }
347- }
348-
349- /// The root context, which is the parent of all other contexts. All `FileId`s have this context.
350- #[ inline]
351- pub const fn root ( edition : Edition ) -> Self {
352- let edition = edition as u32 ;
353- // SAFETY: Roots are valid `SyntaxContext`s
354- unsafe { SyntaxContext :: from_u32 ( SyntaxContext :: MAX_ID - edition) }
355- }
356- }
357-
358337#[ cfg( feature = "salsa" ) ]
359338impl < ' db > SyntaxContext {
360- const MAX_ID : u32 = salsa:: Id :: MAX_U32 - 1 ;
339+ const MAX_ROOT_ID : u32 = salsa:: Id :: MAX_U32 + Edition :: LATEST as u32 ;
361340
362341 #[ inline]
363342 pub const fn into_u32 ( self ) -> u32 {
@@ -378,7 +357,8 @@ impl<'db> SyntaxContext {
378357 if self . is_root ( ) {
379358 None
380359 } else {
381- // SAFETY: By our invariant, this is either a root (which we verified it's not) or a valid `salsa::Id`.
360+ // SAFETY: By our invariant, this is either a root (which we verified it's not) or a
361+ // valid `salsa::Id` index.
382362 unsafe { Some ( salsa:: Id :: from_index ( self . 0 ) ) }
383363 }
384364 }
@@ -389,6 +369,27 @@ impl<'db> SyntaxContext {
389369 unsafe { Self :: from_u32 ( id. index ( ) ) }
390370 }
391371
372+ #[ inline]
373+ pub fn is_root ( self ) -> bool {
374+ ( SyntaxContext :: MAX_ROOT_ID - Edition :: LATEST as u32 ) <= self . into_u32 ( )
375+ && self . into_u32 ( ) <= ( SyntaxContext :: MAX_ROOT_ID - Edition :: Edition2015 as u32 )
376+ }
377+
378+ #[ inline]
379+ pub fn remove_root_edition ( & mut self ) {
380+ if self . is_root ( ) {
381+ * self = Self :: root ( Edition :: Edition2015 ) ;
382+ }
383+ }
384+
385+ /// The root context, which is the parent of all other contexts. All `FileId`s have this context.
386+ #[ inline]
387+ pub const fn root ( edition : Edition ) -> Self {
388+ let edition = edition as u32 ;
389+ // SAFETY: Roots are valid `SyntaxContext`s
390+ unsafe { SyntaxContext :: from_u32 ( SyntaxContext :: MAX_ROOT_ID - edition) }
391+ }
392+
392393 #[ inline]
393394 pub fn outer_mark (
394395 self ,
@@ -447,15 +448,8 @@ impl<'db> SyntaxContext {
447448#[ derive( Copy , Clone , PartialEq , PartialOrd , Eq , Ord , Hash ) ]
448449pub struct SyntaxContext ( u32 ) ;
449450
450- #[ allow( dead_code) ]
451- const SALSA_MAX_ID_MIRROR : u32 = u32:: MAX - 0xFF ;
452- #[ cfg( feature = "salsa" ) ]
453- const _: ( ) = assert ! ( salsa:: Id :: MAX_U32 == SALSA_MAX_ID_MIRROR ) ;
454-
455451#[ cfg( not( feature = "salsa" ) ) ]
456452impl SyntaxContext {
457- const MAX_ID : u32 = SALSA_MAX_ID_MIRROR - 1 ;
458-
459453 pub const fn into_u32 ( self ) -> u32 {
460454 self . 0
461455 }
@@ -496,16 +490,28 @@ impl Transparency {
496490 }
497491}
498492
493+ #[ cfg( feature = "salsa" ) ]
499494impl fmt:: Display for SyntaxContext {
500495 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
501496 if self . is_root ( ) {
502- write ! ( f, "ROOT{}" , Edition :: from_u32( SyntaxContext :: MAX_ID - self . into_u32( ) ) . number( ) )
497+ write ! (
498+ f,
499+ "ROOT{}" ,
500+ Edition :: from_u32( SyntaxContext :: MAX_ROOT_ID - self . into_u32( ) ) . number( )
501+ )
503502 } else {
504503 write ! ( f, "{}" , self . into_u32( ) )
505504 }
506505 }
507506}
508507
508+ #[ cfg( not( feature = "salsa" ) ) ]
509+ impl fmt:: Display for SyntaxContext {
510+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
511+ write ! ( f, "{}" , self . into_u32( ) )
512+ }
513+ }
514+
509515impl std:: fmt:: Debug for SyntaxContext {
510516 fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
511517 if f. alternate ( ) {
@@ -515,3 +521,69 @@ impl std::fmt::Debug for SyntaxContext {
515521 }
516522 }
517523}
524+
525+ #[ cfg( test) ]
526+ mod tests {
527+ use super :: * ;
528+
529+ #[ test]
530+ fn test_root_edition_is_root ( ) {
531+ for edition in Edition :: iter ( ) {
532+ let ctx = SyntaxContext :: root ( edition) ;
533+ assert ! ( ctx. is_root( ) , "{edition} root should be identified as root" ) ;
534+ }
535+ }
536+
537+ #[ test]
538+ fn test_root_edition_editions ( ) {
539+ let db = salsa:: DatabaseImpl :: new ( ) ;
540+ for edition in Edition :: iter ( ) {
541+ let ctx = SyntaxContext :: root ( edition) ;
542+ assert_eq ! ( edition, ctx. edition( & db) , "{edition} root should have edition {edition}" ) ;
543+ }
544+ }
545+
546+ #[ test]
547+ fn test_roots_do_not_overlap_with_salsa_ids ( ) {
548+ for edition in Edition :: iter ( ) {
549+ let root = SyntaxContext :: root ( edition) ;
550+ let root_u32 = root. into_u32 ( ) ;
551+ assert ! (
552+ root_u32 >= salsa:: Id :: MAX_U32 ,
553+ "Root context for {:?} (value {}) must be >= salsa::Id::MAX_U32 ({}) to avoid collision" ,
554+ edition,
555+ root_u32,
556+ salsa:: Id :: MAX_U32
557+ ) ;
558+ }
559+ }
560+
561+ #[ test]
562+ fn test_non_root_value_is_not_root ( ) {
563+ for edition in Edition :: iter ( ) {
564+ // SAFETY: This is just for testing purposes
565+ let ctx = unsafe { SyntaxContext :: from_u32 ( edition as u32 + 1 ) } ;
566+ assert ! ( !ctx. is_root( ) , "{edition} root should be identified as root" ) ;
567+ }
568+ }
569+
570+ #[ test]
571+ fn test_interned_context_round_trips_through_u32 ( ) {
572+ let db = salsa:: DatabaseImpl :: new ( ) ;
573+ let root = SyntaxContext :: root ( Edition :: Edition2015 ) ;
574+ let ctx = SyntaxContext :: new (
575+ & db,
576+ None ,
577+ Transparency :: Opaque ,
578+ Edition :: Edition2021 ,
579+ root,
580+ |_| root,
581+ |_| root,
582+ ) ;
583+
584+ // SAFETY: The value was produced by `SyntaxContext::into_u32` above.
585+ let round_tripped = unsafe { SyntaxContext :: from_u32 ( ctx. into_u32 ( ) ) } ;
586+ assert_eq ! ( round_tripped. edition( & db) , Edition :: Edition2021 ) ;
587+ assert_eq ! ( round_tripped. parent( & db) , root) ;
588+ }
589+ }
0 commit comments