@@ -769,12 +769,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
769
769
// the MIR->HIR mapping.
770
770
( current_id, parent_id)
771
771
} 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.
775
775
(
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
+ } ,
778
782
)
779
783
} ;
780
784
@@ -784,16 +788,24 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
784
788
}
785
789
}
786
790
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;
790
799
let hir = self . tcx . hir ( ) ;
791
800
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 ;
794
804
}
795
805
796
806
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.
797
809
return id;
798
810
}
799
811
@@ -802,7 +814,22 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
802
814
bug ! ( "lint traversal reached the root of the crate" ) ;
803
815
}
804
816
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
+ }
805
826
}
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
806
833
}
807
834
808
835
/// Creates a new source scope, nested in the current one.
0 commit comments