@@ -769,12 +769,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
769769 // the MIR->HIR mapping.
770770 ( current_id, parent_id)
771771 } else {
772- // Use `maybe_lint_level_root_bounded` with `self.hir_id` as a bound
773- // to avoid adding Hir dependencies on our parents.
774- // We estimate the true lint roots here to avoid creating a lot of source scopes.
772+ // Use `maybe_lint_level_root_bounded` to avoid adding Hir dependencies on our
773+ // parents. We estimate the true lint roots here to avoid creating a lot of source
774+ // scopes.
775775 (
776- self . maybe_lint_level_root_bounded ( current_id, self . hir_id ) ,
777- self . maybe_lint_level_root_bounded ( parent_id, self . hir_id ) ,
776+ self . maybe_lint_level_root_bounded ( current_id) ,
777+ if parent_id == self . hir_id {
778+ parent_id // this is very common
779+ } else {
780+ self . maybe_lint_level_root_bounded ( parent_id)
781+ } ,
778782 )
779783 } ;
780784
@@ -784,16 +788,24 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
784788 }
785789 }
786790
787- /// Walks upwards from `id` to find a node which might change lint levels with attributes.
788- /// It stops at `bound` and just returns it if reached.
789- fn maybe_lint_level_root_bounded ( & self , mut id : HirId , bound : HirId ) -> HirId {
791+ /// Walks upwards from `orig_id` to find a node which might change lint levels with attributes.
792+ /// It stops at `self.hir_id` and just returns it if reached.
793+ fn maybe_lint_level_root_bounded ( & mut self , orig_id : HirId ) -> HirId {
794+ // This assertion lets us just store `ItemLocalId` in the cache, rather
795+ // than the full `HirId`.
796+ assert_eq ! ( orig_id. owner, self . hir_id. owner) ;
797+
798+ let mut id = orig_id;
790799 let hir = self . tcx . hir ( ) ;
791800 loop {
792- if id == bound {
793- return bound;
801+ if id == self . hir_id {
802+ // This is a moderately common case, mostly hit for previously unseen nodes.
803+ break ;
794804 }
795805
796806 if hir. attrs ( id) . iter ( ) . any ( |attr| Level :: from_attr ( attr) . is_some ( ) ) {
807+ // This is a rare case. It's for a node path that doesn't reach the root due to an
808+ // intervening lint level attribute. This result doesn't get cached.
797809 return id;
798810 }
799811
@@ -802,7 +814,22 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
802814 bug ! ( "lint traversal reached the root of the crate" ) ;
803815 }
804816 id = next;
817+
818+ // This lookup is just an optimization; it can be removed without affecting
819+ // functionality. It might seem strange to see this at the end of this loop, but the
820+ // `orig_id` passed in to this function is almost always previously unseen, for which a
821+ // lookup will be a miss. So we only do lookups for nodes up the parent chain, where
822+ // cache lookups have a very high hit rate.
823+ if self . lint_level_roots_cache . contains ( id. local_id ) {
824+ break ;
825+ }
805826 }
827+
828+ // `orig_id` traced to `self_id`; record this fact. If `orig_id` is a leaf node it will
829+ // rarely (never?) subsequently be searched for, but it's hard to know if that is the case.
830+ // The performance wins from the cache all come from caching non-leaf nodes.
831+ self . lint_level_roots_cache . insert ( orig_id. local_id ) ;
832+ self . hir_id
806833 }
807834
808835 /// Creates a new source scope, nested in the current one.
0 commit comments