1
Fork 0

Auto merge of #90986 - camsteffen:nested-filter, r=cjgillot

Replace `NestedVisitorMap` with generic `NestedFilter`

This is an attempt to make the `intravisit::Visitor` API simpler and "more const" with regard to nested visiting.

With this change, `intravisit::Visitor` does not visit nested things by default, unless you specify `type NestedFilter = nested_filter::OnlyBodies` (or `All`). `nested_visit_map` returns `Self::Map` instead of `NestedVisitorMap<Self::Map>`. It panics by default (unreachable if `type NestedFilter` is omitted).

One somewhat trixty thing here is that `nested_filter::{OnlyBodies, All}` live in `rustc_middle` so that they may have `type Map = map::Map` and so that `impl Visitor`s never need to specify `type Map` - it has a default of `Self::NestedFilter::Map`.
This commit is contained in:
bors 2022-01-17 14:50:50 +00:00
commit ee5d8d37ba
110 changed files with 388 additions and 947 deletions

View file

@ -160,39 +160,27 @@ impl<'hir> Map<'hir> for ! {
}
}
/// An erased version of `Map<'hir>`, using dynamic dispatch.
/// NOTE: This type is effectively only usable with `NestedVisitorMap::None`.
pub struct ErasedMap<'hir>(&'hir dyn Map<'hir>);
pub mod nested_filter {
use super::Map;
impl<'hir> Map<'hir> for ErasedMap<'hir> {
fn find(&self, _: HirId) -> Option<Node<'hir>> {
None
}
fn body(&self, id: BodyId) -> &'hir Body<'hir> {
self.0.body(id)
}
fn item(&self, id: ItemId) -> &'hir Item<'hir> {
self.0.item(id)
}
fn trait_item(&self, id: TraitItemId) -> &'hir TraitItem<'hir> {
self.0.trait_item(id)
}
fn impl_item(&self, id: ImplItemId) -> &'hir ImplItem<'hir> {
self.0.impl_item(id)
}
fn foreign_item(&self, id: ForeignItemId) -> &'hir ForeignItem<'hir> {
self.0.foreign_item(id)
}
}
/// Specifies what nested things a visitor wants to visit. The most
/// common choice is `OnlyBodies`, which will cause the visitor to
/// visit fn bodies for fns that it encounters, but skip over nested
/// item-like things.
///
/// See the comments on `ItemLikeVisitor` for more details on the overall
/// visit strategy.
pub trait NestedFilter<'hir> {
type Map: Map<'hir>;
/// Whether the visitor visits nested "item-like" things.
/// E.g., item, impl-item.
const INTER: bool;
/// Whether the visitor visits "intra item-like" things.
/// E.g., function body, closure, `AnonConst`
const INTRA: bool;
}
/// Specifies what nested things a visitor wants to visit. The most
/// common choice is `OnlyBodies`, which will cause the visitor to
/// visit fn bodies for fns that it encounters, but skip over nested
/// item-like things.
///
/// See the comments on `ItemLikeVisitor` for more details on the overall
/// visit strategy.
pub enum NestedVisitorMap<M> {
/// Do not visit any nested things. When you add a new
/// "non-nested" thing, you will want to audit such uses to see if
/// they remain valid.
@ -200,47 +188,16 @@ pub enum NestedVisitorMap<M> {
/// Use this if you are only walking some particular kind of tree
/// (i.e., a type, or fn signature) and you don't want to thread a
/// HIR map around.
None,
/// Do not visit nested item-like things, but visit nested things
/// that are inside of an item-like.
///
/// **This is the most common choice.** A very common pattern is
/// to use `visit_all_item_likes()` as an outer loop,
/// and to have the visitor that visits the contents of each item
/// using this setting.
OnlyBodies(M),
/// Visits all nested things, including item-likes.
///
/// **This is an unusual choice.** It is used when you want to
/// process everything within their lexical context. Typically you
/// kick off the visit by doing `walk_krate()`.
All(M),
}
impl<M> NestedVisitorMap<M> {
/// Returns the map to use for an "intra item-like" thing (if any).
/// E.g., function body.
fn intra(self) -> Option<M> {
match self {
NestedVisitorMap::None => None,
NestedVisitorMap::OnlyBodies(map) => Some(map),
NestedVisitorMap::All(map) => Some(map),
}
}
/// Returns the map to use for an "item-like" thing (if any).
/// E.g., item, impl-item.
fn inter(self) -> Option<M> {
match self {
NestedVisitorMap::None => None,
NestedVisitorMap::OnlyBodies(_) => None,
NestedVisitorMap::All(map) => Some(map),
}
pub struct None(());
impl NestedFilter<'_> for None {
type Map = !;
const INTER: bool = false;
const INTRA: bool = false;
}
}
use nested_filter::NestedFilter;
/// Each method of the Visitor trait is a hook to be potentially
/// overridden. Each method's default implementation recursively visits
/// the substructure of the input via the corresponding `walk` method;
@ -258,7 +215,9 @@ impl<M> NestedVisitorMap<M> {
/// to monitor future changes to `Visitor` in case a new method with a
/// new default implementation gets introduced.)
pub trait Visitor<'v>: Sized {
type Map: Map<'v>;
// this type should not be overridden, it exists for convenient usage as `Self::Map`
type Map: Map<'v> = <Self::NestedFilter as NestedFilter<'v>>::Map;
type NestedFilter: NestedFilter<'v> = nested_filter::None;
///////////////////////////////////////////////////////////////////////////
// Nested items.
@ -279,7 +238,12 @@ pub trait Visitor<'v>: Sized {
/// `panic!()`. This way, if a new `visit_nested_XXX` variant is
/// added in the future, we will see the panic in your code and
/// fix it appropriately.
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map>;
fn nested_visit_map(&mut self) -> Self::Map {
panic!(
"nested_visit_map must be implemented or consider using \
`type NestedFilter = nested_filter::None` (the default)"
);
}
/// Invoked when a nested item is encountered. By default does
/// nothing unless you override `nested_visit_map` to return other than
@ -290,32 +254,40 @@ pub trait Visitor<'v>: Sized {
/// reason to override this method is if you want a nested pattern
/// but cannot supply a `Map`; see `nested_visit_map` for advice.
fn visit_nested_item(&mut self, id: ItemId) {
let opt_item = self.nested_visit_map().inter().map(|map| map.item(id));
walk_list!(self, visit_item, opt_item);
if Self::NestedFilter::INTER {
let item = self.nested_visit_map().item(id);
self.visit_item(item);
}
}
/// Like `visit_nested_item()`, but for trait items. See
/// `visit_nested_item()` for advice on when to override this
/// method.
fn visit_nested_trait_item(&mut self, id: TraitItemId) {
let opt_item = self.nested_visit_map().inter().map(|map| map.trait_item(id));
walk_list!(self, visit_trait_item, opt_item);
if Self::NestedFilter::INTER {
let item = self.nested_visit_map().trait_item(id);
self.visit_trait_item(item);
}
}
/// Like `visit_nested_item()`, but for impl items. See
/// `visit_nested_item()` for advice on when to override this
/// method.
fn visit_nested_impl_item(&mut self, id: ImplItemId) {
let opt_item = self.nested_visit_map().inter().map(|map| map.impl_item(id));
walk_list!(self, visit_impl_item, opt_item);
if Self::NestedFilter::INTER {
let item = self.nested_visit_map().impl_item(id);
self.visit_impl_item(item);
}
}
/// Like `visit_nested_item()`, but for foreign items. See
/// `visit_nested_item()` for advice on when to override this
/// method.
fn visit_nested_foreign_item(&mut self, id: ForeignItemId) {
let opt_item = self.nested_visit_map().inter().map(|map| map.foreign_item(id));
walk_list!(self, visit_foreign_item, opt_item);
if Self::NestedFilter::INTER {
let item = self.nested_visit_map().foreign_item(id);
self.visit_foreign_item(item);
}
}
/// Invoked to visit the body of a function, method or closure. Like
@ -323,8 +295,10 @@ pub trait Visitor<'v>: Sized {
/// `nested_visit_map` to return other than `None`, in which case it will walk
/// the body.
fn visit_nested_body(&mut self, id: BodyId) {
let opt_body = self.nested_visit_map().intra().map(|map| map.body(id));
walk_list!(self, visit_body, opt_body);
if Self::NestedFilter::INTRA {
let body = self.nested_visit_map().body(id);
self.visit_body(body);
}
}
fn visit_param(&mut self, param: &'v Param<'v>) {

View file

@ -2,6 +2,7 @@
//!
//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/hir.html
#![feature(associated_type_defaults)]
#![feature(const_btree_new)]
#![feature(crate_visibility_modifier)]
#![feature(once_cell)]