Auto merge of #96825 - kckeiks:remove-item-like-visitor-trait, r=cjgillot

Retire `ItemLikeVisitor` trait

Issue #95004
cc `@cjgillot`
This commit is contained in:
bors 2022-05-17 06:51:45 +00:00
commit 7355d971a9
29 changed files with 640 additions and 791 deletions

View File

@ -1,7 +1,40 @@
//! HIR walker for walking the contents of nodes. //! HIR walker for walking the contents of nodes.
//! //!
//! **For an overview of the visitor strategy, see the docs on the //! Here are the three available patterns for the visitor strategy,
//! `super::itemlikevisit::ItemLikeVisitor` trait.** //! in roughly the order of desirability:
//!
//! 1. **Shallow visit**: Get a simple callback for every item (or item-like thing) in the HIR.
//! - Example: find all items with a `#[foo]` attribute on them.
//! - How: Use the `hir_crate_items` or `hir_module_items` query to traverse over item-like ids
//! (ItemId, TraitItemId, etc.) and use tcx.def_kind and `tcx.hir().item*(id)` to filter and
//! access actual item-like thing, respectively.
//! - Pro: Efficient; just walks the lists of item ids and gives users control whether to access
//! the hir_owners themselves or not.
//! - Con: Don't get information about nesting
//! - Con: Don't have methods for specific bits of HIR, like "on
//! every expr, do this".
//! 2. **Deep visit**: Want to scan for specific kinds of HIR nodes within
//! an item, but don't care about how item-like things are nested
//! within one another.
//! - Example: Examine each expression to look for its type and do some check or other.
//! - How: Implement `intravisit::Visitor` and override the `NestedFilter` type to
//! `nested_filter::OnlyBodies` (and implement `nested_visit_map`), and use
//! `tcx.hir().deep_visit_all_item_likes(&mut visitor)`. Within your
//! `intravisit::Visitor` impl, implement methods like `visit_expr()` (don't forget to invoke
//! `intravisit::walk_expr()` to keep walking the subparts).
//! - Pro: Visitor methods for any kind of HIR node, not just item-like things.
//! - Pro: Integrates well into dependency tracking.
//! - Con: Don't get information about nesting between items
//! 3. **Nested visit**: Want to visit the whole HIR and you care about the nesting between
//! item-like things.
//! - Example: Lifetime resolution, which wants to bring lifetimes declared on the
//! impl into scope while visiting the impl-items, and then back out again.
//! - How: Implement `intravisit::Visitor` and override the `NestedFilter` type to
//! `nested_filter::All` (and implement `nested_visit_map`). Walk your crate with
//! `tcx.hir().walk_toplevel_module(visitor)` invoked on `tcx.hir().krate()`.
//! - Pro: Visitor methods for any kind of HIR node, not just item-like things.
//! - Pro: Preserves nesting information
//! - Con: Does not integrate well into dependency tracking.
//! //!
//! If you have decided to use this visitor, here are some general //! If you have decided to use this visitor, here are some general
//! notes on how to do so: //! notes on how to do so:
@ -32,43 +65,12 @@
//! example generator inference, and possibly also HIR borrowck. //! example generator inference, and possibly also HIR borrowck.
use crate::hir::*; use crate::hir::*;
use crate::itemlikevisit::{ItemLikeVisitor, ParItemLikeVisitor}; use crate::itemlikevisit::ParItemLikeVisitor;
use rustc_ast::walk_list; use rustc_ast::walk_list;
use rustc_ast::{Attribute, Label}; use rustc_ast::{Attribute, Label};
use rustc_span::symbol::{Ident, Symbol}; use rustc_span::symbol::{Ident, Symbol};
use rustc_span::Span; use rustc_span::Span;
pub struct DeepVisitor<'v, V> {
visitor: &'v mut V,
}
impl<'v, V> DeepVisitor<'v, V> {
pub fn new(base: &'v mut V) -> Self {
DeepVisitor { visitor: base }
}
}
impl<'v, 'hir, V> ItemLikeVisitor<'hir> for DeepVisitor<'v, V>
where
V: Visitor<'hir>,
{
fn visit_item(&mut self, item: &'hir Item<'hir>) {
self.visitor.visit_item(item);
}
fn visit_trait_item(&mut self, trait_item: &'hir TraitItem<'hir>) {
self.visitor.visit_trait_item(trait_item);
}
fn visit_impl_item(&mut self, impl_item: &'hir ImplItem<'hir>) {
self.visitor.visit_impl_item(impl_item);
}
fn visit_foreign_item(&mut self, foreign_item: &'hir ForeignItem<'hir>) {
self.visitor.visit_foreign_item(foreign_item);
}
}
pub trait IntoVisitor<'hir> { pub trait IntoVisitor<'hir> {
type Visitor: Visitor<'hir>; type Visitor: Visitor<'hir>;
fn into_visitor(&self) -> Self::Visitor; fn into_visitor(&self) -> Self::Visitor;
@ -315,16 +317,6 @@ pub trait Visitor<'v>: Sized {
walk_body(self, b); walk_body(self, b);
} }
/// When invoking `visit_all_item_likes()`, you need to supply an
/// item-like visitor. This method converts an "intra-visit"
/// visitor into an item-like visitor that walks the entire tree.
/// If you use this, you probably don't want to process the
/// contents of nested item-like things, since the outer loop will
/// visit them as well.
fn as_deep_visitor(&mut self) -> DeepVisitor<'_, Self> {
DeepVisitor::new(self)
}
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
fn visit_id(&mut self, _hir_id: HirId) { fn visit_id(&mut self, _hir_id: HirId) {

View File

@ -1,55 +1,5 @@
use super::{ForeignItem, ImplItem, Item, TraitItem}; use super::{ForeignItem, ImplItem, Item, TraitItem};
/// The "item-like visitor" defines only the top-level methods
/// that can be invoked by `Crate::visit_all_item_likes()`. Whether
/// this trait is the right one to implement will depend on the
/// overall pattern you need. Here are the three available patterns,
/// in roughly the order of desirability:
///
/// 1. **Shallow visit**: Get a simple callback for every item (or item-like thing) in the HIR.
/// - Example: find all items with a `#[foo]` attribute on them.
/// - How: Implement `ItemLikeVisitor` and call `tcx.hir().visit_all_item_likes()`.
/// - Pro: Efficient; just walks the lists of item-like things, not the nodes themselves.
/// - Con: Don't get information about nesting
/// - Con: Don't have methods for specific bits of HIR, like "on
/// every expr, do this".
/// 2. **Deep visit**: Want to scan for specific kinds of HIR nodes within
/// an item, but don't care about how item-like things are nested
/// within one another.
/// - Example: Examine each expression to look for its type and do some check or other.
/// - How: Implement `intravisit::Visitor` and override the `NestedFilter` type to
/// `nested_filter::OnlyBodies` (and implement `nested_visit_map`), and use
/// `tcx.hir().visit_all_item_likes(&mut visitor.as_deep_visitor())`. Within your
/// `intravisit::Visitor` impl, implement methods like `visit_expr()` (don't forget to invoke
/// `intravisit::walk_expr()` to keep walking the subparts).
/// - Pro: Visitor methods for any kind of HIR node, not just item-like things.
/// - Pro: Integrates well into dependency tracking.
/// - Con: Don't get information about nesting between items
/// 3. **Nested visit**: Want to visit the whole HIR and you care about the nesting between
/// item-like things.
/// - Example: Lifetime resolution, which wants to bring lifetimes declared on the
/// impl into scope while visiting the impl-items, and then back out again.
/// - How: Implement `intravisit::Visitor` and override the `NestedFilter` type to
/// `nested_filter::All` (and implement `nested_visit_map`). Walk your crate with
/// `tcx.hir().walk_toplevel_module(visitor)` invoked on `tcx.hir().krate()`.
/// - Pro: Visitor methods for any kind of HIR node, not just item-like things.
/// - Pro: Preserves nesting information
/// - Con: Does not integrate well into dependency tracking.
///
/// Note: the methods of `ItemLikeVisitor` intentionally have no
/// defaults, so that as we expand the list of item-like things, we
/// revisit the various visitors to see if they need to change. This
/// is harder to do with `intravisit::Visitor`, so when you add a new
/// `visit_nested_foo()` method, it is recommended that you search for
/// existing `fn visit_nested` methods to see where changes are
/// needed.
pub trait ItemLikeVisitor<'hir> {
fn visit_item(&mut self, item: &'hir Item<'hir>);
fn visit_trait_item(&mut self, trait_item: &'hir TraitItem<'hir>);
fn visit_impl_item(&mut self, impl_item: &'hir ImplItem<'hir>);
fn visit_foreign_item(&mut self, foreign_item: &'hir ForeignItem<'hir>);
}
/// A parallel variant of `ItemLikeVisitor`. /// A parallel variant of `ItemLikeVisitor`.
pub trait ParItemLikeVisitor<'hir> { pub trait ParItemLikeVisitor<'hir> {
fn visit_item(&self, item: &'hir Item<'hir>); fn visit_item(&self, item: &'hir Item<'hir>);

View File

@ -75,7 +75,7 @@ pub fn assert_dep_graph(tcx: TyCtxt<'_>) {
let mut visitor = let mut visitor =
IfThisChanged { tcx, if_this_changed: vec![], then_this_would_need: vec![] }; IfThisChanged { tcx, if_this_changed: vec![], then_this_would_need: vec![] };
visitor.process_attrs(hir::CRATE_HIR_ID); visitor.process_attrs(hir::CRATE_HIR_ID);
tcx.hir().visit_all_item_likes(&mut visitor.as_deep_visitor()); tcx.hir().deep_visit_all_item_likes(&mut visitor);
(visitor.if_this_changed, visitor.then_this_would_need) (visitor.if_this_changed, visitor.then_this_would_need)
}; };

View File

@ -14,7 +14,6 @@ use rustc_hir::def_id::{
}; };
use rustc_hir::definitions::DefPathData; use rustc_hir::definitions::DefPathData;
use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_hir::lang_items; use rustc_hir::lang_items;
use rustc_hir::{AnonConst, GenericParamKind}; use rustc_hir::{AnonConst, GenericParamKind};
use rustc_index::bit_set::GrowableBitSet; use rustc_index::bit_set::GrowableBitSet;
@ -453,7 +452,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
return; return;
} }
self.tcx.hir().visit_all_item_likes(&mut self.as_deep_visitor()); self.tcx.hir().deep_visit_all_item_likes(self);
} }
fn encode_def_path_table(&mut self) { fn encode_def_path_table(&mut self) {
@ -2243,26 +2242,16 @@ pub fn provide(providers: &mut Providers) {
traits_in_crate: |tcx, cnum| { traits_in_crate: |tcx, cnum| {
assert_eq!(cnum, LOCAL_CRATE); assert_eq!(cnum, LOCAL_CRATE);
#[derive(Default)] let mut traits = Vec::new();
struct TraitsVisitor { for id in tcx.hir().items() {
traits: Vec<DefId>, if matches!(tcx.def_kind(id.def_id), DefKind::Trait | DefKind::TraitAlias) {
} traits.push(id.def_id.to_def_id())
impl ItemLikeVisitor<'_> for TraitsVisitor {
fn visit_item(&mut self, item: &hir::Item<'_>) {
if let hir::ItemKind::Trait(..) | hir::ItemKind::TraitAlias(..) = item.kind {
self.traits.push(item.def_id.to_def_id());
}
} }
fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem<'_>) {}
fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem<'_>) {}
fn visit_foreign_item(&mut self, _foreign_item: &hir::ForeignItem<'_>) {}
} }
let mut visitor = TraitsVisitor::default();
tcx.hir().visit_all_item_likes(&mut visitor);
// Bring everything into deterministic order. // Bring everything into deterministic order.
visitor.traits.sort_by_cached_key(|&def_id| tcx.def_path_hash(def_id)); traits.sort_by_cached_key(|&def_id| tcx.def_path_hash(def_id));
tcx.arena.alloc_slice(&visitor.traits) tcx.arena.alloc_slice(&traits)
}, },
..*providers ..*providers

View File

@ -9,7 +9,6 @@ use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE};
use rustc_hir::definitions::{DefKey, DefPath, DefPathHash}; use rustc_hir::definitions::{DefKey, DefPath, DefPathHash};
use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_hir::*; use rustc_hir::*;
use rustc_index::vec::Idx; use rustc_index::vec::Idx;
use rustc_middle::hir::nested_filter; use rustc_middle::hir::nested_filter;
@ -161,6 +160,10 @@ impl<'hir> Map<'hir> {
self.tcx.hir_crate_items(()).items.iter().copied() self.tcx.hir_crate_items(()).items.iter().copied()
} }
pub fn module_items(self, module: LocalDefId) -> impl Iterator<Item = ItemId> + 'hir {
self.tcx.hir_module_items(module).items()
}
pub fn par_for_each_item(self, f: impl Fn(ItemId) + Sync + Send) { pub fn par_for_each_item(self, f: impl Fn(ItemId) + Sync + Send) {
par_for_each_in(&self.tcx.hir_crate_items(()).items[..], |id| f(*id)); par_for_each_in(&self.tcx.hir_crate_items(()).items[..], |id| f(*id));
} }
@ -603,16 +606,16 @@ impl<'hir> Map<'hir> {
} }
/// Visits all items in the crate in some deterministic (but /// Visits all items in the crate in some deterministic (but
/// unspecified) order. If you just need to process every item, /// unspecified) order. If you need to process every item,
/// but don't care about nesting, this method is the best choice. /// and care about nesting -- usually because your algorithm
/// follows lexical scoping rules -- then this method is the best choice.
/// If you don't care about nesting, you should use the `tcx.hir_crate_items()` query
/// or `items()` instead.
/// ///
/// If you do care about nesting -- usually because your algorithm /// Please see the notes in `intravisit.rs` for more information.
/// follows lexical scoping rules -- then you want a different pub fn deep_visit_all_item_likes<V>(self, visitor: &mut V)
/// approach. You should override `visit_nested_item` in your
/// visitor and then call `intravisit::walk_crate` instead.
pub fn visit_all_item_likes<V>(self, visitor: &mut V)
where where
V: itemlikevisit::ItemLikeVisitor<'hir>, V: Visitor<'hir>,
{ {
let krate = self.krate(); let krate = self.krate();
for owner in krate.owners.iter().filter_map(|i| i.as_owner()) { for owner in krate.owners.iter().filter_map(|i| i.as_owner()) {
@ -643,9 +646,12 @@ impl<'hir> Map<'hir> {
}) })
} }
pub fn visit_item_likes_in_module<V>(self, module: LocalDefId, visitor: &mut V) /// If you don't care about nesting, you should use the
/// `tcx.hir_module_items()` query or `module_items()` instead.
/// Please see notes in `deep_visit_all_item_likes`.
pub fn deep_visit_item_likes_in_module<V>(self, module: LocalDefId, visitor: &mut V)
where where
V: ItemLikeVisitor<'hir>, V: Visitor<'hir>,
{ {
let module = self.tcx.hir_module_items(module); let module = self.tcx.hir_module_items(module);
@ -666,7 +672,7 @@ impl<'hir> Map<'hir> {
} }
} }
pub fn for_each_module(self, f: impl Fn(LocalDefId)) { pub fn for_each_module(self, mut f: impl FnMut(LocalDefId)) {
let crate_items = self.tcx.hir_crate_items(()); let crate_items = self.tcx.hir_crate_items(());
for module in crate_items.submodules.iter() { for module in crate_items.submodules.iter() {
f(*module) f(*module)

View File

@ -8,7 +8,7 @@ use rustc_hir::intravisit::nested_filter::NestedFilter;
/// constant arguments of types, e.g. in `let _: [(); /* HERE */];`. /// constant arguments of types, e.g. in `let _: [(); /* HERE */];`.
/// ///
/// **This is the most common choice.** A very common pattern is /// **This is the most common choice.** A very common pattern is
/// to use `visit_all_item_likes()` as an outer loop, /// to use `deep_visit_all_item_likes()` as an outer loop,
/// and to have the visitor that visits the contents of each item /// and to have the visitor that visits the contents of each item
/// using this setting. /// using this setting.
pub struct OnlyBodies(()); pub struct OnlyBodies(());

View File

@ -1995,7 +1995,7 @@ impl<'tcx> TyCtxt<'tcx> {
} }
/// Look up the name of a definition across crates. This does not look at HIR. /// Look up the name of a definition across crates. This does not look at HIR.
fn opt_item_name(self, def_id: DefId) -> Option<Symbol> { pub fn opt_item_name(self, def_id: DefId) -> Option<Symbol> {
if let Some(cnum) = def_id.as_crate_root() { if let Some(cnum) = def_id.as_crate_root() {
Some(self.crate_name(cnum)) Some(self.crate_name(cnum))
} else { } else {

View File

@ -170,7 +170,7 @@ fn mir_keys(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet<LocalDefId> {
intravisit::walk_struct_def(self, v) intravisit::walk_struct_def(self, v)
} }
} }
tcx.hir().visit_all_item_likes(&mut GatherCtors { tcx, set: &mut set }.as_deep_visitor()); tcx.hir().deep_visit_all_item_likes(&mut GatherCtors { tcx, set: &mut set });
set set
} }

View File

@ -2390,7 +2390,7 @@ fn check_non_exported_macro_for_invalid_attrs(tcx: TyCtxt<'_>, item: &Item<'_>)
fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
let check_attr_visitor = &mut CheckAttrVisitor { tcx }; let check_attr_visitor = &mut CheckAttrVisitor { tcx };
tcx.hir().visit_item_likes_in_module(module_def_id, &mut check_attr_visitor.as_deep_visitor()); tcx.hir().deep_visit_item_likes_in_module(module_def_id, check_attr_visitor);
if module_def_id.is_top_level_module() { if module_def_id.is_top_level_module() {
check_attr_visitor.check_attributes(CRATE_HIR_ID, DUMMY_SP, Target::Mod, None); check_attr_visitor.check_attributes(CRATE_HIR_ID, DUMMY_SP, Target::Mod, None);
check_invalid_crate_level_attr(tcx, tcx.hir().krate_attrs()); check_invalid_crate_level_attr(tcx, tcx.hir().krate_attrs());

View File

@ -57,89 +57,71 @@ impl NonConstExpr {
fn check_mod_const_bodies(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { fn check_mod_const_bodies(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
let mut vis = CheckConstVisitor::new(tcx); let mut vis = CheckConstVisitor::new(tcx);
tcx.hir().visit_item_likes_in_module(module_def_id, &mut vis.as_deep_visitor()); tcx.hir().deep_visit_item_likes_in_module(module_def_id, &mut vis);
tcx.hir().visit_item_likes_in_module(module_def_id, &mut CheckConstTraitVisitor::new(tcx));
} }
pub(crate) fn provide(providers: &mut Providers) { pub(crate) fn provide(providers: &mut Providers) {
*providers = Providers { check_mod_const_bodies, ..*providers }; *providers = Providers { check_mod_const_bodies, ..*providers };
} }
struct CheckConstTraitVisitor<'tcx> { fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) {
tcx: TyCtxt<'tcx>, let _: Option<_> = try {
} if let hir::ItemKind::Impl(ref imp) = item.kind && let hir::Constness::Const = imp.constness {
let trait_def_id = imp.of_trait.as_ref()?.trait_def_id()?;
let ancestors = tcx
.trait_def(trait_def_id)
.ancestors(tcx, item.def_id.to_def_id())
.ok()?;
let mut to_implement = Vec::new();
impl<'tcx> CheckConstTraitVisitor<'tcx> { for trait_item in tcx.associated_items(trait_def_id).in_definition_order()
fn new(tcx: TyCtxt<'tcx>) -> Self { {
CheckConstTraitVisitor { tcx } if let ty::AssocItem {
} kind: ty::AssocKind::Fn,
} defaultness,
def_id: trait_item_id,
impl<'tcx> hir::itemlikevisit::ItemLikeVisitor<'tcx> for CheckConstTraitVisitor<'tcx> { ..
/// check for const trait impls, and errors if the impl uses provided/default functions } = *trait_item
/// of the trait being implemented; as those provided functions can be non-const. {
fn visit_item<'hir>(&mut self, item: &'hir hir::Item<'hir>) { // we can ignore functions that do not have default bodies:
let _: Option<_> = try { // if those are unimplemented it will be caught by typeck.
if let hir::ItemKind::Impl(ref imp) = item.kind && let hir::Constness::Const = imp.constness { if !defaultness.has_value()
let trait_def_id = imp.of_trait.as_ref()?.trait_def_id()?; || tcx
let ancestors = self .has_attr(trait_item_id, sym::default_method_body_is_const)
.tcx
.trait_def(trait_def_id)
.ancestors(self.tcx, item.def_id.to_def_id())
.ok()?;
let mut to_implement = Vec::new();
for trait_item in self.tcx.associated_items(trait_def_id).in_definition_order()
{ {
if let ty::AssocItem { continue;
kind: ty::AssocKind::Fn,
defaultness,
def_id: trait_item_id,
..
} = *trait_item
{
// we can ignore functions that do not have default bodies:
// if those are unimplemented it will be caught by typeck.
if !defaultness.has_value()
|| self
.tcx
.has_attr(trait_item_id, sym::default_method_body_is_const)
{
continue;
}
let is_implemented = ancestors
.leaf_def(self.tcx, trait_item_id)
.map(|node_item| !node_item.defining_node.is_from_trait())
.unwrap_or(false);
if !is_implemented {
to_implement.push(self.tcx.item_name(trait_item_id).to_string());
}
}
} }
// all nonconst trait functions (not marked with #[default_method_body_is_const]) let is_implemented = ancestors
// must be implemented .leaf_def(tcx, trait_item_id)
if !to_implement.is_empty() { .map(|node_item| !node_item.defining_node.is_from_trait())
self.tcx .unwrap_or(false);
.sess
.struct_span_err( if !is_implemented {
item.span, to_implement.push(trait_item_id);
"const trait implementations may not use non-const default functions",
)
.note(&format!("`{}` not implemented", to_implement.join("`, `")))
.emit();
} }
}
} }
};
}
fn visit_trait_item<'hir>(&mut self, _: &'hir hir::TraitItem<'hir>) {} // all nonconst trait functions (not marked with #[default_method_body_is_const])
// must be implemented
fn visit_impl_item<'hir>(&mut self, _: &'hir hir::ImplItem<'hir>) {} if !to_implement.is_empty() {
let not_implemented = to_implement
fn visit_foreign_item<'hir>(&mut self, _: &'hir hir::ForeignItem<'hir>) {} .into_iter()
.map(|did| tcx.item_name(did).to_string())
.collect::<Vec<_>>()
.join("`, `");
tcx
.sess
.struct_span_err(
item.span,
"const trait implementations may not use non-const default functions",
)
.note(&format!("`{}` not implemented", not_implemented))
.emit();
}
}
};
} }
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
@ -270,6 +252,11 @@ impl<'tcx> Visitor<'tcx> for CheckConstVisitor<'tcx> {
self.tcx.hir() self.tcx.hir()
} }
fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
intravisit::walk_item(self, item);
check_item(self.tcx, item);
}
fn visit_anon_const(&mut self, anon: &'tcx hir::AnonConst) { fn visit_anon_const(&mut self, anon: &'tcx hir::AnonConst) {
let kind = Some(hir::ConstContext::Const); let kind = Some(hir::ConstContext::Const);
self.recurse_into(kind, None, |this| intravisit::walk_anon_const(this, anon)); self.recurse_into(kind, None, |this| intravisit::walk_anon_const(this, anon));

View File

@ -8,7 +8,6 @@ use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def::{CtorOf, DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_hir::{Node, PatKind, TyKind}; use rustc_hir::{Node, PatKind, TyKind};
use rustc_middle::hir::nested_filter; use rustc_middle::hir::nested_filter;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
@ -468,7 +467,7 @@ fn has_allow_dead_code_or_lang_attr(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
tcx.lint_level_at_node(lint::builtin::DEAD_CODE, id).0 == lint::Allow tcx.lint_level_at_node(lint::builtin::DEAD_CODE, id).0 == lint::Allow
} }
// This visitor seeds items that // These check_* functions seeds items that
// 1) We want to explicitly consider as live: // 1) We want to explicitly consider as live:
// * Item annotated with #[allow(dead_code)] // * Item annotated with #[allow(dead_code)]
// - This is done so that if we want to suppress warnings for a // - This is done so that if we want to suppress warnings for a
@ -481,82 +480,95 @@ fn has_allow_dead_code_or_lang_attr(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
// or // or
// 2) We are not sure to be live or not // 2) We are not sure to be live or not
// * Implementations of traits and trait methods // * Implementations of traits and trait methods
struct LifeSeeder<'tcx> { fn check_item<'tcx>(
worklist: Vec<LocalDefId>,
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
// see `MarkSymbolVisitor::struct_constructors` worklist: &mut Vec<LocalDefId>,
struct_constructors: FxHashMap<LocalDefId, LocalDefId>, struct_constructors: &mut FxHashMap<LocalDefId, LocalDefId>,
} id: hir::ItemId,
) {
let allow_dead_code = has_allow_dead_code_or_lang_attr(tcx, id.hir_id());
if allow_dead_code {
worklist.push(id.def_id);
}
impl<'v, 'tcx> ItemLikeVisitor<'v> for LifeSeeder<'tcx> { match tcx.def_kind(id.def_id) {
fn visit_item(&mut self, item: &hir::Item<'_>) { DefKind::Enum => {
let allow_dead_code = has_allow_dead_code_or_lang_attr(self.tcx, item.hir_id()); let item = tcx.hir().item(id);
if allow_dead_code { if let hir::ItemKind::Enum(ref enum_def, _) = item.kind {
self.worklist.push(item.def_id); let hir = tcx.hir();
}
match item.kind {
hir::ItemKind::Enum(ref enum_def, _) => {
let hir = self.tcx.hir();
if allow_dead_code { if allow_dead_code {
self.worklist.extend( worklist.extend(
enum_def.variants.iter().map(|variant| hir.local_def_id(variant.id)), enum_def.variants.iter().map(|variant| hir.local_def_id(variant.id)),
); );
} }
for variant in enum_def.variants { for variant in enum_def.variants {
if let Some(ctor_hir_id) = variant.data.ctor_hir_id() { if let Some(ctor_hir_id) = variant.data.ctor_hir_id() {
self.struct_constructors struct_constructors
.insert(hir.local_def_id(ctor_hir_id), hir.local_def_id(variant.id)); .insert(hir.local_def_id(ctor_hir_id), hir.local_def_id(variant.id));
} }
} }
} }
hir::ItemKind::Impl(hir::Impl { ref of_trait, items, .. }) => { }
if of_trait.is_some() { DefKind::Impl => {
self.worklist.push(item.def_id); let of_trait = tcx.impl_trait_ref(id.def_id);
}
for impl_item_ref in *items { if of_trait.is_some() {
let impl_item = self.tcx.hir().impl_item(impl_item_ref.id); worklist.push(id.def_id);
if of_trait.is_some() }
|| has_allow_dead_code_or_lang_attr(self.tcx, impl_item.hir_id())
{ // get DefIds from another query
self.worklist.push(impl_item_ref.id.def_id); let local_def_ids = tcx
} .associated_item_def_ids(id.def_id)
.iter()
.filter_map(|def_id| def_id.as_local());
// And we access the Map here to get HirId from LocalDefId
for id in local_def_ids {
if of_trait.is_some()
|| has_allow_dead_code_or_lang_attr(tcx, tcx.hir().local_def_id_to_hir_id(id))
{
worklist.push(id);
} }
} }
hir::ItemKind::Struct(ref variant_data, _) => { }
DefKind::Struct => {
let item = tcx.hir().item(id);
if let hir::ItemKind::Struct(ref variant_data, _) = item.kind {
if let Some(ctor_hir_id) = variant_data.ctor_hir_id() { if let Some(ctor_hir_id) = variant_data.ctor_hir_id() {
self.struct_constructors struct_constructors.insert(tcx.hir().local_def_id(ctor_hir_id), item.def_id);
.insert(self.tcx.hir().local_def_id(ctor_hir_id), item.def_id);
} }
} }
hir::ItemKind::GlobalAsm(_) => {
// global_asm! is always live.
self.worklist.push(item.def_id);
}
_ => (),
} }
DefKind::GlobalAsm => {
// global_asm! is always live.
worklist.push(id.def_id);
}
_ => {}
} }
}
fn visit_trait_item(&mut self, trait_item: &hir::TraitItem<'_>) { fn check_trait_item<'tcx>(tcx: TyCtxt<'tcx>, worklist: &mut Vec<LocalDefId>, id: hir::TraitItemId) {
use hir::TraitItemKind::{Const, Fn}; use hir::TraitItemKind::{Const, Fn};
if matches!(tcx.def_kind(id.def_id), DefKind::AssocConst | DefKind::AssocFn) {
let trait_item = tcx.hir().trait_item(id);
if matches!(trait_item.kind, Const(_, Some(_)) | Fn(_, hir::TraitFn::Provided(_))) if matches!(trait_item.kind, Const(_, Some(_)) | Fn(_, hir::TraitFn::Provided(_)))
&& has_allow_dead_code_or_lang_attr(self.tcx, trait_item.hir_id()) && has_allow_dead_code_or_lang_attr(tcx, trait_item.hir_id())
{ {
self.worklist.push(trait_item.def_id); worklist.push(trait_item.def_id);
} }
} }
}
fn visit_impl_item(&mut self, _item: &hir::ImplItem<'_>) { fn check_foreign_item<'tcx>(
// ignore: we are handling this in `visit_item` above tcx: TyCtxt<'tcx>,
} worklist: &mut Vec<LocalDefId>,
id: hir::ForeignItemId,
fn visit_foreign_item(&mut self, foreign_item: &hir::ForeignItem<'_>) { ) {
use hir::ForeignItemKind::{Fn, Static}; if matches!(tcx.def_kind(id.def_id), DefKind::Static(_) | DefKind::Fn)
if matches!(foreign_item.kind, Static(..) | Fn(..)) && has_allow_dead_code_or_lang_attr(tcx, id.hir_id())
&& has_allow_dead_code_or_lang_attr(self.tcx, foreign_item.hir_id()) {
{ worklist.push(id.def_id);
self.worklist.push(foreign_item.def_id);
}
} }
} }
@ -564,7 +576,9 @@ fn create_and_seed_worklist<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
) -> (Vec<LocalDefId>, FxHashMap<LocalDefId, LocalDefId>) { ) -> (Vec<LocalDefId>, FxHashMap<LocalDefId, LocalDefId>) {
let access_levels = &tcx.privacy_access_levels(()); let access_levels = &tcx.privacy_access_levels(());
let worklist = access_levels // see `MarkSymbolVisitor::struct_constructors`
let mut struct_constructors = Default::default();
let mut worklist = access_levels
.map .map
.iter() .iter()
.filter_map( .filter_map(
@ -576,11 +590,20 @@ fn create_and_seed_worklist<'tcx>(
.chain(tcx.entry_fn(()).and_then(|(def_id, _)| def_id.as_local())) .chain(tcx.entry_fn(()).and_then(|(def_id, _)| def_id.as_local()))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
// Seed implemented trait items let crate_items = tcx.hir_crate_items(());
let mut life_seeder = LifeSeeder { worklist, tcx, struct_constructors: Default::default() }; for id in crate_items.items() {
tcx.hir().visit_all_item_likes(&mut life_seeder); check_item(tcx, &mut worklist, &mut struct_constructors, id);
}
(life_seeder.worklist, life_seeder.struct_constructors) for id in crate_items.trait_items() {
check_trait_item(tcx, &mut worklist, id);
}
for id in crate_items.foreign_items() {
check_foreign_item(tcx, &mut worklist, id);
}
(worklist, struct_constructors)
} }
fn live_symbols_and_ignored_derived_traits<'tcx>( fn live_symbols_and_ignored_derived_traits<'tcx>(

View File

@ -5,8 +5,7 @@ use rustc_data_structures::fx::FxHashSet;
use rustc_expand::base::resolve_path; use rustc_expand::base::resolve_path;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def_id::CrateNum; use rustc_hir::def_id::CrateNum;
use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::HirId;
use rustc_hir::{HirId, Target};
use rustc_middle::ty::query::Providers; use rustc_middle::ty::query::Providers;
use rustc_middle::ty::TyCtxt; use rustc_middle::ty::TyCtxt;
use rustc_span::def_id::LOCAL_CRATE; use rustc_span::def_id::LOCAL_CRATE;
@ -14,96 +13,66 @@ use rustc_span::{sym, DebuggerVisualizerFile, DebuggerVisualizerType};
use std::sync::Arc; use std::sync::Arc;
struct DebuggerVisualizerCollector<'tcx> { fn check_for_debugger_visualizer<'tcx>(
debugger_visualizers: FxHashSet<DebuggerVisualizerFile>,
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
} hir_id: HirId,
debugger_visualizers: &mut FxHashSet<DebuggerVisualizerFile>,
) {
let attrs = tcx.hir().attrs(hir_id);
for attr in attrs {
if attr.has_name(sym::debugger_visualizer) {
let list = match attr.meta_item_list() {
Some(list) => list,
_ => continue,
};
impl<'v, 'tcx> ItemLikeVisitor<'v> for DebuggerVisualizerCollector<'tcx> { let meta_item = match list.len() {
fn visit_item(&mut self, item: &hir::Item<'_>) { 1 => match list[0].meta_item() {
let target = Target::from_item(item); Some(meta_item) => meta_item,
match target {
Target::Mod => {
self.check_for_debugger_visualizer(item.hir_id());
}
_ => {}
}
}
fn visit_trait_item(&mut self, _: &hir::TraitItem<'_>) {}
fn visit_impl_item(&mut self, _: &hir::ImplItem<'_>) {}
fn visit_foreign_item(&mut self, _: &hir::ForeignItem<'_>) {}
}
impl<'tcx> DebuggerVisualizerCollector<'tcx> {
fn new(tcx: TyCtxt<'tcx>) -> DebuggerVisualizerCollector<'tcx> {
DebuggerVisualizerCollector { tcx, debugger_visualizers: FxHashSet::default() }
}
fn check_for_debugger_visualizer(&mut self, hir_id: HirId) {
let attrs = self.tcx.hir().attrs(hir_id);
for attr in attrs {
if attr.has_name(sym::debugger_visualizer) {
let list = match attr.meta_item_list() {
Some(list) => list,
_ => continue, _ => continue,
}; },
_ => continue,
};
let meta_item = match list.len() { let file = match (meta_item.name_or_empty(), meta_item.value_str()) {
1 => match list[0].meta_item() { (sym::natvis_file, Some(value)) => {
Some(meta_item) => meta_item, match resolve_path(&tcx.sess.parse_sess, value.as_str(), attr.span) {
_ => continue, Ok(file) => file,
}, Err(mut err) => {
_ => continue, err.emit();
};
let file = match (meta_item.name_or_empty(), meta_item.value_str()) {
(sym::natvis_file, Some(value)) => {
match resolve_path(&self.tcx.sess.parse_sess, value.as_str(), attr.span) {
Ok(file) => file,
Err(mut err) => {
err.emit();
continue;
}
}
}
(_, _) => continue,
};
if file.is_file() {
let contents = match std::fs::read(&file) {
Ok(contents) => contents,
Err(err) => {
self.tcx
.sess
.struct_span_err(
attr.span,
&format!(
"Unable to read contents of file `{}`. {}",
file.display(),
err
),
)
.emit();
continue; continue;
} }
}; }
self.debugger_visualizers.insert(DebuggerVisualizerFile::new(
Arc::from(contents),
DebuggerVisualizerType::Natvis,
));
} else {
self.tcx
.sess
.struct_span_err(
attr.span,
&format!("{} is not a valid file", file.display()),
)
.emit();
} }
(_, _) => continue,
};
if file.is_file() {
let contents = match std::fs::read(&file) {
Ok(contents) => contents,
Err(err) => {
tcx.sess
.struct_span_err(
attr.span,
&format!(
"Unable to read contents of file `{}`. {}",
file.display(),
err
),
)
.emit();
continue;
}
};
debugger_visualizers.insert(DebuggerVisualizerFile::new(
Arc::from(contents),
DebuggerVisualizerType::Natvis,
));
} else {
tcx.sess
.struct_span_err(attr.span, &format!("{} is not a valid file", file.display()))
.emit();
} }
} }
} }
@ -114,17 +83,21 @@ fn debugger_visualizers<'tcx>(tcx: TyCtxt<'tcx>, cnum: CrateNum) -> Vec<Debugger
assert_eq!(cnum, LOCAL_CRATE); assert_eq!(cnum, LOCAL_CRATE);
// Initialize the collector. // Initialize the collector.
let mut collector = DebuggerVisualizerCollector::new(tcx); let mut debugger_visualizers = FxHashSet::default();
// Collect debugger visualizers in this crate. // Collect debugger visualizers in this crate.
tcx.hir().visit_all_item_likes(&mut collector); tcx.hir().for_each_module(|id| {
check_for_debugger_visualizer(
tcx,
tcx.hir().local_def_id_to_hir_id(id),
&mut debugger_visualizers,
)
});
// Collect debugger visualizers on the crate attributes. // Collect debugger visualizers on the crate attributes.
collector.check_for_debugger_visualizer(CRATE_HIR_ID); check_for_debugger_visualizer(tcx, CRATE_HIR_ID, &mut debugger_visualizers);
// Extract out the found debugger_visualizer items. // Extract out the found debugger_visualizer items.
let DebuggerVisualizerCollector { debugger_visualizers, .. } = collector;
let mut visualizers = debugger_visualizers.into_iter().collect::<Vec<_>>(); let mut visualizers = debugger_visualizers.into_iter().collect::<Vec<_>>();
// Sort the visualizers so we always get a deterministic query result. // Sort the visualizers so we always get a deterministic query result.

View File

@ -10,49 +10,22 @@
//! * Compiler internal types like `Ty` and `TyCtxt` //! * Compiler internal types like `Ty` and `TyCtxt`
use rustc_ast as ast; use rustc_ast as ast;
use rustc_hir as hir;
use rustc_hir::diagnostic_items::DiagnosticItems; use rustc_hir::diagnostic_items::DiagnosticItems;
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_middle::ty::query::Providers; use rustc_middle::ty::query::Providers;
use rustc_middle::ty::TyCtxt; use rustc_middle::ty::TyCtxt;
use rustc_span::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; use rustc_span::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE};
use rustc_span::symbol::{sym, Symbol}; use rustc_span::symbol::{sym, Symbol};
struct DiagnosticItemCollector<'tcx> { fn observe_item<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
diagnostic_items: DiagnosticItems, diagnostic_items: &mut DiagnosticItems,
} def_id: LocalDefId,
) {
impl<'v, 'tcx> ItemLikeVisitor<'v> for DiagnosticItemCollector<'tcx> { let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
fn visit_item(&mut self, item: &hir::Item<'_>) { let attrs = tcx.hir().attrs(hir_id);
self.observe_item(item.def_id); if let Some(name) = extract(attrs) {
} // insert into our table
collect_item(tcx, diagnostic_items, name, def_id.to_def_id());
fn visit_trait_item(&mut self, trait_item: &hir::TraitItem<'_>) {
self.observe_item(trait_item.def_id);
}
fn visit_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) {
self.observe_item(impl_item.def_id);
}
fn visit_foreign_item(&mut self, foreign_item: &hir::ForeignItem<'_>) {
self.observe_item(foreign_item.def_id);
}
}
impl<'tcx> DiagnosticItemCollector<'tcx> {
fn new(tcx: TyCtxt<'tcx>) -> DiagnosticItemCollector<'tcx> {
DiagnosticItemCollector { tcx, diagnostic_items: DiagnosticItems::default() }
}
fn observe_item(&mut self, def_id: LocalDefId) {
let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
let attrs = self.tcx.hir().attrs(hir_id);
if let Some(name) = extract(attrs) {
// insert into our table
collect_item(self.tcx, &mut self.diagnostic_items, name, def_id.to_def_id());
}
} }
} }
@ -95,12 +68,28 @@ fn diagnostic_items<'tcx>(tcx: TyCtxt<'tcx>, cnum: CrateNum) -> DiagnosticItems
assert_eq!(cnum, LOCAL_CRATE); assert_eq!(cnum, LOCAL_CRATE);
// Initialize the collector. // Initialize the collector.
let mut collector = DiagnosticItemCollector::new(tcx); let mut diagnostic_items = DiagnosticItems::default();
// Collect diagnostic items in this crate. // Collect diagnostic items in this crate.
tcx.hir().visit_all_item_likes(&mut collector); let crate_items = tcx.hir_crate_items(());
collector.diagnostic_items for id in crate_items.items() {
observe_item(tcx, &mut diagnostic_items, id.def_id);
}
for id in crate_items.trait_items() {
observe_item(tcx, &mut diagnostic_items, id.def_id);
}
for id in crate_items.impl_items() {
observe_item(tcx, &mut diagnostic_items, id.def_id);
}
for id in crate_items.foreign_items() {
observe_item(tcx, &mut diagnostic_items, id.def_id);
}
diagnostic_items
} }
/// Traverse and collect all the diagnostic items in all crates. /// Traverse and collect all the diagnostic items in all crates.

View File

@ -1,8 +1,8 @@
use rustc_ast::entry::EntryPointType; use rustc_ast::entry::EntryPointType;
use rustc_errors::struct_span_err; use rustc_errors::struct_span_err;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE};
use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::{ItemId, Node, CRATE_HIR_ID};
use rustc_hir::{ForeignItem, ImplItem, Item, ItemKind, Node, TraitItem, CRATE_HIR_ID};
use rustc_middle::ty::query::Providers; use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{DefIdTree, TyCtxt}; use rustc_middle::ty::{DefIdTree, TyCtxt};
use rustc_session::config::{CrateType, EntryFnType}; use rustc_session::config::{CrateType, EntryFnType};
@ -25,25 +25,6 @@ struct EntryContext<'tcx> {
non_main_fns: Vec<Span>, non_main_fns: Vec<Span>,
} }
impl<'tcx> ItemLikeVisitor<'tcx> for EntryContext<'tcx> {
fn visit_item(&mut self, item: &'tcx Item<'tcx>) {
let at_root = self.tcx.opt_local_parent(item.def_id) == Some(CRATE_DEF_ID);
find_item(item, self, at_root);
}
fn visit_trait_item(&mut self, _trait_item: &'tcx TraitItem<'tcx>) {
// Entry fn is never a trait item.
}
fn visit_impl_item(&mut self, _impl_item: &'tcx ImplItem<'tcx>) {
// Entry fn is never a trait item.
}
fn visit_foreign_item(&mut self, _: &'tcx ForeignItem<'tcx>) {
// Entry fn is never a foreign item.
}
}
fn entry_fn(tcx: TyCtxt<'_>, (): ()) -> Option<(DefId, EntryFnType)> { fn entry_fn(tcx: TyCtxt<'_>, (): ()) -> Option<(DefId, EntryFnType)> {
let any_exe = tcx.sess.crate_types().iter().any(|ty| *ty == CrateType::Executable); let any_exe = tcx.sess.crate_types().iter().any(|ty| *ty == CrateType::Executable);
if !any_exe { if !any_exe {
@ -59,28 +40,35 @@ fn entry_fn(tcx: TyCtxt<'_>, (): ()) -> Option<(DefId, EntryFnType)> {
let mut ctxt = let mut ctxt =
EntryContext { tcx, attr_main_fn: None, start_fn: None, non_main_fns: Vec::new() }; EntryContext { tcx, attr_main_fn: None, start_fn: None, non_main_fns: Vec::new() };
tcx.hir().visit_all_item_likes(&mut ctxt); for id in tcx.hir().items() {
find_item(id, &mut ctxt);
}
configure_main(tcx, &ctxt) configure_main(tcx, &ctxt)
} }
// Beware, this is duplicated in `librustc_builtin_macros/test_harness.rs` // Beware, this is duplicated in `librustc_builtin_macros/test_harness.rs`
// (with `ast::Item`), so make sure to keep them in sync. // (with `ast::Item`), so make sure to keep them in sync.
fn entry_point_type(ctxt: &EntryContext<'_>, item: &Item<'_>, at_root: bool) -> EntryPointType { // A small optimization was added so that hir::Item is fetched only when needed.
let attrs = ctxt.tcx.hir().attrs(item.hir_id()); // An equivalent optimization was not applied to the duplicated code in test_harness.rs.
fn entry_point_type(ctxt: &EntryContext<'_>, id: ItemId, at_root: bool) -> EntryPointType {
let attrs = ctxt.tcx.hir().attrs(id.hir_id());
if ctxt.tcx.sess.contains_name(attrs, sym::start) { if ctxt.tcx.sess.contains_name(attrs, sym::start) {
EntryPointType::Start EntryPointType::Start
} else if ctxt.tcx.sess.contains_name(attrs, sym::rustc_main) { } else if ctxt.tcx.sess.contains_name(attrs, sym::rustc_main) {
EntryPointType::MainAttr EntryPointType::MainAttr
} else if item.ident.name == sym::main {
if at_root {
// This is a top-level function so can be `main`.
EntryPointType::MainNamed
} else {
EntryPointType::OtherMain
}
} else { } else {
EntryPointType::None if let Some(name) = ctxt.tcx.opt_item_name(id.def_id.to_def_id())
&& name == sym::main {
if at_root {
// This is a top-level function so can be `main`.
EntryPointType::MainNamed
} else {
EntryPointType::OtherMain
}
} else {
EntryPointType::None
}
} }
} }
@ -89,11 +77,13 @@ fn throw_attr_err(sess: &Session, span: Span, attr: &str) {
.emit(); .emit();
} }
fn find_item(item: &Item<'_>, ctxt: &mut EntryContext<'_>, at_root: bool) { fn find_item(id: ItemId, ctxt: &mut EntryContext<'_>) {
match entry_point_type(ctxt, item, at_root) { let at_root = ctxt.tcx.opt_local_parent(id.def_id) == Some(CRATE_DEF_ID);
match entry_point_type(ctxt, id, at_root) {
EntryPointType::None => (), EntryPointType::None => (),
_ if !matches!(item.kind, ItemKind::Fn(..)) => { _ if !matches!(ctxt.tcx.def_kind(id.def_id), DefKind::Fn) => {
let attrs = ctxt.tcx.hir().attrs(item.hir_id()); let attrs = ctxt.tcx.hir().attrs(id.hir_id());
if let Some(attr) = ctxt.tcx.sess.find_by_name(attrs, sym::start) { if let Some(attr) = ctxt.tcx.sess.find_by_name(attrs, sym::start) {
throw_attr_err(&ctxt.tcx.sess, attr.span, "start"); throw_attr_err(&ctxt.tcx.sess, attr.span, "start");
} }
@ -103,31 +93,39 @@ fn find_item(item: &Item<'_>, ctxt: &mut EntryContext<'_>, at_root: bool) {
} }
EntryPointType::MainNamed => (), EntryPointType::MainNamed => (),
EntryPointType::OtherMain => { EntryPointType::OtherMain => {
ctxt.non_main_fns.push(item.span); ctxt.non_main_fns.push(ctxt.tcx.def_span(id.def_id));
} }
EntryPointType::MainAttr => { EntryPointType::MainAttr => {
if ctxt.attr_main_fn.is_none() { if ctxt.attr_main_fn.is_none() {
ctxt.attr_main_fn = Some((item.def_id, item.span)); ctxt.attr_main_fn = Some((id.def_id, ctxt.tcx.def_span(id.def_id.to_def_id())));
} else { } else {
struct_span_err!( struct_span_err!(
ctxt.tcx.sess, ctxt.tcx.sess,
item.span, ctxt.tcx.def_span(id.def_id.to_def_id()),
E0137, E0137,
"multiple functions with a `#[main]` attribute" "multiple functions with a `#[main]` attribute"
) )
.span_label(item.span, "additional `#[main]` function") .span_label(
ctxt.tcx.def_span(id.def_id.to_def_id()),
"additional `#[main]` function",
)
.span_label(ctxt.attr_main_fn.unwrap().1, "first `#[main]` function") .span_label(ctxt.attr_main_fn.unwrap().1, "first `#[main]` function")
.emit(); .emit();
} }
} }
EntryPointType::Start => { EntryPointType::Start => {
if ctxt.start_fn.is_none() { if ctxt.start_fn.is_none() {
ctxt.start_fn = Some((item.def_id, item.span)); ctxt.start_fn = Some((id.def_id, ctxt.tcx.def_span(id.def_id.to_def_id())));
} else { } else {
struct_span_err!(ctxt.tcx.sess, item.span, E0138, "multiple `start` functions") struct_span_err!(
.span_label(ctxt.start_fn.unwrap().1, "previous `#[start]` function here") ctxt.tcx.sess,
.span_label(item.span, "multiple `start` functions") ctxt.tcx.def_span(id.def_id.to_def_id()),
.emit(); E0138,
"multiple `start` functions"
)
.span_label(ctxt.start_fn.unwrap().1, "previous `#[start]` function here")
.span_label(ctxt.tcx.def_span(id.def_id.to_def_id()), "multiple `start` functions")
.emit();
} }
} }
} }

View File

@ -3,7 +3,6 @@ use rustc_data_structures::sync::Lock;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID}; use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
use rustc_hir::intravisit; use rustc_hir::intravisit;
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_hir::{HirId, ItemLocalId}; use rustc_hir::{HirId, ItemLocalId};
use rustc_middle::hir::map::Map; use rustc_middle::hir::map::Map;
use rustc_middle::hir::nested_filter; use rustc_middle::hir::nested_filter;
@ -20,8 +19,14 @@ pub fn check_crate(tcx: TyCtxt<'_>) {
let hir_map = tcx.hir(); let hir_map = tcx.hir();
hir_map.par_for_each_module(|module_id| { hir_map.par_for_each_module(|module_id| {
hir_map let mut v = HirIdValidator {
.visit_item_likes_in_module(module_id, &mut OuterVisitor { hir_map, errors: &errors }) hir_map,
owner: None,
hir_ids_seen: Default::default(),
errors: &errors,
};
tcx.hir().deep_visit_item_likes_in_module(module_id, &mut v);
}); });
let errors = errors.into_inner(); let errors = errors.into_inner();
@ -39,13 +44,8 @@ struct HirIdValidator<'a, 'hir> {
errors: &'a Lock<Vec<String>>, errors: &'a Lock<Vec<String>>,
} }
struct OuterVisitor<'a, 'hir> { impl<'a, 'hir> HirIdValidator<'a, 'hir> {
hir_map: Map<'hir>, fn new_visitor(&self, hir_map: Map<'hir>) -> HirIdValidator<'a, 'hir> {
errors: &'a Lock<Vec<String>>,
}
impl<'a, 'hir> OuterVisitor<'a, 'hir> {
fn new_inner_visitor(&self, hir_map: Map<'hir>) -> HirIdValidator<'a, 'hir> {
HirIdValidator { HirIdValidator {
hir_map, hir_map,
owner: None, owner: None,
@ -53,31 +53,7 @@ impl<'a, 'hir> OuterVisitor<'a, 'hir> {
errors: self.errors, errors: self.errors,
} }
} }
}
impl<'a, 'hir> ItemLikeVisitor<'hir> for OuterVisitor<'a, 'hir> {
fn visit_item(&mut self, i: &'hir hir::Item<'hir>) {
let mut inner_visitor = self.new_inner_visitor(self.hir_map);
inner_visitor.check(i.def_id, |this| intravisit::walk_item(this, i));
}
fn visit_trait_item(&mut self, i: &'hir hir::TraitItem<'hir>) {
let mut inner_visitor = self.new_inner_visitor(self.hir_map);
inner_visitor.check(i.def_id, |this| intravisit::walk_trait_item(this, i));
}
fn visit_impl_item(&mut self, i: &'hir hir::ImplItem<'hir>) {
let mut inner_visitor = self.new_inner_visitor(self.hir_map);
inner_visitor.check(i.def_id, |this| intravisit::walk_impl_item(this, i));
}
fn visit_foreign_item(&mut self, i: &'hir hir::ForeignItem<'hir>) {
let mut inner_visitor = self.new_inner_visitor(self.hir_map);
inner_visitor.check(i.def_id, |this| intravisit::walk_foreign_item(this, i));
}
}
impl<'a, 'hir> HirIdValidator<'a, 'hir> {
#[cold] #[cold]
#[inline(never)] #[inline(never)]
fn error(&self, f: impl FnOnce() -> String) { fn error(&self, f: impl FnOnce() -> String) {
@ -146,6 +122,11 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for HirIdValidator<'a, 'hir> {
self.hir_map self.hir_map
} }
fn visit_item(&mut self, i: &'hir hir::Item<'hir>) {
let mut inner_visitor = self.new_visitor(self.hir_map);
inner_visitor.check(i.def_id, |this| intravisit::walk_item(this, i));
}
fn visit_id(&mut self, hir_id: HirId) { fn visit_id(&mut self, hir_id: HirId) {
let owner = self.owner.expect("no owner"); let owner = self.owner.expect("no owner");
@ -163,17 +144,18 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for HirIdValidator<'a, 'hir> {
self.hir_ids_seen.insert(hir_id.local_id); self.hir_ids_seen.insert(hir_id.local_id);
} }
fn visit_impl_item_ref(&mut self, _: &'hir hir::ImplItemRef) { fn visit_foreign_item(&mut self, i: &'hir hir::ForeignItem<'hir>) {
// Explicitly do nothing here. ImplItemRefs contain hir::Visibility let mut inner_visitor = self.new_visitor(self.hir_map);
// values that actually belong to an ImplItem instead of the ItemKind::Impl inner_visitor.check(i.def_id, |this| intravisit::walk_foreign_item(this, i));
// we are currently in. So for those it's correct that they have a
// different owner.
} }
fn visit_foreign_item_ref(&mut self, _: &'hir hir::ForeignItemRef) { fn visit_trait_item(&mut self, i: &'hir hir::TraitItem<'hir>) {
// Explicitly do nothing here. ForeignItemRefs contain hir::Visibility let mut inner_visitor = self.new_visitor(self.hir_map);
// values that actually belong to an ForeignItem instead of the ItemKind::ForeignMod inner_visitor.check(i.def_id, |this| intravisit::walk_trait_item(this, i));
// we are currently in. So for those it's correct that they have a }
// different owner.
fn visit_impl_item(&mut self, i: &'hir hir::ImplItem<'hir>) {
let mut inner_visitor = self.new_visitor(self.hir_map);
inner_visitor.check(i.def_id, |this| intravisit::walk_impl_item(this, i));
} }
} }

View File

@ -17,7 +17,7 @@ use rustc_target::asm::{InlineAsmRegOrRegClass, InlineAsmType};
use rustc_target::spec::abi::Abi::RustIntrinsic; use rustc_target::spec::abi::Abi::RustIntrinsic;
fn check_mod_intrinsics(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { fn check_mod_intrinsics(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
tcx.hir().visit_item_likes_in_module(module_def_id, &mut ItemVisitor { tcx }.as_deep_visitor()); tcx.hir().deep_visit_item_likes_in_module(module_def_id, &mut ItemVisitor { tcx });
} }
pub fn provide(providers: &mut Providers) { pub fn provide(providers: &mut Providers) {

View File

@ -1,8 +1,6 @@
use rustc_ast::Attribute; use rustc_ast::Attribute;
use rustc_hir as hir; use rustc_hir::def::DefKind;
use rustc_hir::def_id::LocalDefId; use rustc_hir::def_id::LocalDefId;
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_hir::ItemKind;
use rustc_middle::ty::layout::{HasParamEnv, HasTyCtxt, LayoutError, LayoutOfHelpers, TyAndLayout}; use rustc_middle::ty::layout::{HasParamEnv, HasTyCtxt, LayoutError, LayoutOfHelpers, TyAndLayout};
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt}; use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
@ -12,97 +10,87 @@ use rustc_target::abi::{HasDataLayout, TargetDataLayout};
pub fn test_layout(tcx: TyCtxt<'_>) { pub fn test_layout(tcx: TyCtxt<'_>) {
if tcx.features().rustc_attrs { if tcx.features().rustc_attrs {
// if the `rustc_attrs` feature is not enabled, don't bother testing layout // if the `rustc_attrs` feature is not enabled, don't bother testing layout
tcx.hir().visit_all_item_likes(&mut LayoutTest { tcx }); for id in tcx.hir().items() {
} if matches!(
} tcx.def_kind(id.def_id),
DefKind::TyAlias | DefKind::Enum | DefKind::Struct | DefKind::Union
struct LayoutTest<'tcx> { ) {
tcx: TyCtxt<'tcx>, for attr in tcx.get_attrs(id.def_id.to_def_id(), sym::rustc_layout) {
} dump_layout_of(tcx, id.def_id, attr);
impl<'tcx> ItemLikeVisitor<'tcx> for LayoutTest<'tcx> {
fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
match item.kind {
ItemKind::TyAlias(..)
| ItemKind::Enum(..)
| ItemKind::Struct(..)
| ItemKind::Union(..) => {
for attr in self.tcx.get_attrs(item.def_id.to_def_id(), sym::rustc_layout) {
self.dump_layout_of(item.def_id, item, attr);
} }
} }
_ => {}
} }
} }
fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem<'tcx>) {}
fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem<'tcx>) {}
fn visit_foreign_item(&mut self, _: &'tcx hir::ForeignItem<'tcx>) {}
} }
impl<'tcx> LayoutTest<'tcx> { fn dump_layout_of<'tcx>(tcx: TyCtxt<'tcx>, item_def_id: LocalDefId, attr: &Attribute) {
fn dump_layout_of(&self, item_def_id: LocalDefId, item: &hir::Item<'tcx>, attr: &Attribute) { let tcx = tcx;
let tcx = self.tcx; let param_env = tcx.param_env(item_def_id);
let param_env = self.tcx.param_env(item_def_id); let ty = tcx.type_of(item_def_id);
let ty = self.tcx.type_of(item_def_id); match tcx.layout_of(param_env.and(ty)) {
match self.tcx.layout_of(param_env.and(ty)) { Ok(ty_layout) => {
Ok(ty_layout) => { // Check out the `#[rustc_layout(..)]` attribute to tell what to dump.
// Check out the `#[rustc_layout(..)]` attribute to tell what to dump. // The `..` are the names of fields to dump.
// The `..` are the names of fields to dump. let meta_items = attr.meta_item_list().unwrap_or_default();
let meta_items = attr.meta_item_list().unwrap_or_default(); for meta_item in meta_items {
for meta_item in meta_items { match meta_item.name_or_empty() {
match meta_item.name_or_empty() { sym::abi => {
sym::abi => { tcx.sess.span_err(
self.tcx.sess.span_err(item.span, &format!("abi: {:?}", ty_layout.abi)); tcx.def_span(item_def_id.to_def_id()),
} &format!("abi: {:?}", ty_layout.abi),
);
}
sym::align => { sym::align => {
self.tcx tcx.sess.span_err(
.sess tcx.def_span(item_def_id.to_def_id()),
.span_err(item.span, &format!("align: {:?}", ty_layout.align)); &format!("align: {:?}", ty_layout.align),
} );
}
sym::size => { sym::size => {
self.tcx tcx.sess.span_err(
.sess tcx.def_span(item_def_id.to_def_id()),
.span_err(item.span, &format!("size: {:?}", ty_layout.size)); &format!("size: {:?}", ty_layout.size),
} );
}
sym::homogeneous_aggregate => { sym::homogeneous_aggregate => {
self.tcx.sess.span_err( tcx.sess.span_err(
item.span, tcx.def_span(item_def_id.to_def_id()),
&format!( &format!(
"homogeneous_aggregate: {:?}", "homogeneous_aggregate: {:?}",
ty_layout ty_layout.homogeneous_aggregate(&UnwrapLayoutCx { tcx, param_env }),
.homogeneous_aggregate(&UnwrapLayoutCx { tcx, param_env }), ),
), );
); }
}
sym::debug => { sym::debug => {
let normalized_ty = self.tcx.normalize_erasing_regions( let normalized_ty = tcx.normalize_erasing_regions(
param_env.with_reveal_all_normalized(self.tcx), param_env.with_reveal_all_normalized(tcx),
ty, ty,
); );
self.tcx.sess.span_err( tcx.sess.span_err(
item.span, tcx.def_span(item_def_id.to_def_id()),
&format!("layout_of({:?}) = {:#?}", normalized_ty, *ty_layout), &format!("layout_of({:?}) = {:#?}", normalized_ty, *ty_layout),
); );
} }
name => { name => {
self.tcx.sess.span_err( tcx.sess.span_err(
meta_item.span(), meta_item.span(),
&format!("unrecognized field name `{}`", name), &format!("unrecognized field name `{}`", name),
); );
}
} }
} }
} }
}
Err(layout_error) => { Err(layout_error) => {
self.tcx.sess.span_err(item.span, &format!("layout error: {:?}", layout_error)); tcx.sess.span_err(
} tcx.def_span(item_def_id.to_def_id()),
&format!("layout error: {:?}", layout_error),
);
} }
} }
} }

View File

@ -140,7 +140,7 @@ fn live_node_kind_to_string(lnk: LiveNodeKind, tcx: TyCtxt<'_>) -> String {
} }
fn check_mod_liveness(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { fn check_mod_liveness(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
tcx.hir().visit_item_likes_in_module(module_def_id, &mut IrMaps::new(tcx).as_deep_visitor()); tcx.hir().deep_visit_item_likes_in_module(module_def_id, &mut IrMaps::new(tcx));
} }
pub fn provide(providers: &mut Providers) { pub fn provide(providers: &mut Providers) {

View File

@ -31,9 +31,9 @@ struct CheckLoopVisitor<'a, 'hir> {
} }
fn check_mod_loops(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { fn check_mod_loops(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
tcx.hir().visit_item_likes_in_module( tcx.hir().deep_visit_item_likes_in_module(
module_def_id, module_def_id,
&mut CheckLoopVisitor { sess: &tcx.sess, hir_map: tcx.hir(), cx: Normal }.as_deep_visitor(), &mut CheckLoopVisitor { sess: &tcx.sess, hir_map: tcx.hir(), cx: Normal },
); );
} }

View File

@ -14,10 +14,7 @@ use rustc_span::Span;
use rustc_target::spec::abi::Abi; use rustc_target::spec::abi::Abi;
fn check_mod_naked_functions(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { fn check_mod_naked_functions(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
tcx.hir().visit_item_likes_in_module( tcx.hir().deep_visit_item_likes_in_module(module_def_id, &mut CheckNakedFunctions { tcx });
module_def_id,
&mut CheckNakedFunctions { tcx }.as_deep_visitor(),
);
} }
crate fn provide(providers: &mut Providers) { crate fn provide(providers: &mut Providers) {

View File

@ -10,7 +10,6 @@ use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_hir::Node; use rustc_hir::Node;
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
use rustc_middle::middle::privacy; use rustc_middle::middle::privacy;
@ -314,79 +313,60 @@ impl<'tcx> ReachableContext<'tcx> {
} }
} }
// Some methods from non-exported (completely private) trait impls still have to be fn check_item<'tcx>(
// reachable if they are called from inlinable code. Generally, it's not known until
// monomorphization if a specific trait impl item can be reachable or not. So, we
// conservatively mark all of them as reachable.
// FIXME: One possible strategy for pruning the reachable set is to avoid marking impl
// items of non-exported traits (or maybe all local traits?) unless their respective
// trait items are used from inlinable code through method call syntax or UFCS, or their
// trait is a lang item.
struct CollectPrivateImplItemsVisitor<'a, 'tcx> {
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
access_levels: &'a privacy::AccessLevels, id: hir::ItemId,
worklist: &'a mut Vec<LocalDefId>, worklist: &mut Vec<LocalDefId>,
} access_levels: &privacy::AccessLevels,
) {
if has_custom_linkage(tcx, id.def_id) {
worklist.push(id.def_id);
}
impl CollectPrivateImplItemsVisitor<'_, '_> { if !matches!(tcx.def_kind(id.def_id), DefKind::Impl) {
fn push_to_worklist_if_has_custom_linkage(&mut self, def_id: LocalDefId) { return;
// Anything which has custom linkage gets thrown on the worklist no }
// matter where it is in the crate, along with "special std symbols"
// which are currently akin to allocator symbols. // We need only trait impls here, not inherent impls, and only non-exported ones
if self.tcx.def_kind(def_id).has_codegen_attrs() { let item = tcx.hir().item(id);
let codegen_attrs = self.tcx.codegen_fn_attrs(def_id); if let hir::ItemKind::Impl(hir::Impl { of_trait: Some(ref trait_ref), ref items, .. }) =
if codegen_attrs.contains_extern_indicator() item.kind
|| codegen_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) {
// FIXME(nbdd0121): `#[used]` are marked as reachable here so it's picked up by if !access_levels.is_reachable(item.def_id) {
// `linked_symbols` in cg_ssa. They won't be exported in binary or cdylib due to their // FIXME(#53488) remove `let`
// `SymbolExportLevel::Rust` export level but may end up being exported in dylibs. let tcx = tcx;
|| codegen_attrs.flags.contains(CodegenFnAttrFlags::USED) worklist.extend(items.iter().map(|ii_ref| ii_ref.id.def_id));
|| codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)
{ let Res::Def(DefKind::Trait, trait_def_id) = trait_ref.path.res else {
self.worklist.push(def_id); unreachable!();
};
if !trait_def_id.is_local() {
return;
} }
worklist.extend(
tcx.provided_trait_methods(trait_def_id).map(|assoc| assoc.def_id.expect_local()),
);
} }
} }
} }
impl<'a, 'tcx> ItemLikeVisitor<'tcx> for CollectPrivateImplItemsVisitor<'a, 'tcx> { fn has_custom_linkage<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> bool {
fn visit_item(&mut self, item: &hir::Item<'_>) { // Anything which has custom linkage gets thrown on the worklist no
self.push_to_worklist_if_has_custom_linkage(item.def_id); // matter where it is in the crate, along with "special std symbols"
// which are currently akin to allocator symbols.
// We need only trait impls here, not inherent impls, and only non-exported ones if !tcx.def_kind(def_id).has_codegen_attrs() {
if let hir::ItemKind::Impl(hir::Impl { of_trait: Some(ref trait_ref), ref items, .. }) = return false;
item.kind
{
if !self.access_levels.is_reachable(item.def_id) {
// FIXME(#53488) remove `let`
let tcx = self.tcx;
self.worklist.extend(items.iter().map(|ii_ref| ii_ref.id.def_id));
let Res::Def(DefKind::Trait, trait_def_id) = trait_ref.path.res else {
unreachable!();
};
if !trait_def_id.is_local() {
return;
}
self.worklist.extend(
tcx.provided_trait_methods(trait_def_id)
.map(|assoc| assoc.def_id.expect_local()),
);
}
}
}
fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem<'_>) {}
fn visit_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) {
self.push_to_worklist_if_has_custom_linkage(impl_item.def_id);
}
fn visit_foreign_item(&mut self, _foreign_item: &hir::ForeignItem<'_>) {
// We never export foreign functions as they have no body to export.
} }
let codegen_attrs = tcx.codegen_fn_attrs(def_id);
codegen_attrs.contains_extern_indicator()
|| codegen_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
// FIXME(nbdd0121): `#[used]` are marked as reachable here so it's picked up by
// `linked_symbols` in cg_ssa. They won't be exported in binary or cdylib due to their
// `SymbolExportLevel::Rust` export level but may end up being exported in dylibs.
|| codegen_attrs.flags.contains(CodegenFnAttrFlags::USED)
|| codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)
} }
fn reachable_set<'tcx>(tcx: TyCtxt<'tcx>, (): ()) -> FxHashSet<LocalDefId> { fn reachable_set<'tcx>(tcx: TyCtxt<'tcx>, (): ()) -> FxHashSet<LocalDefId> {
@ -418,12 +398,25 @@ fn reachable_set<'tcx>(tcx: TyCtxt<'tcx>, (): ()) -> FxHashSet<LocalDefId> {
} }
} }
{ {
let mut collect_private_impl_items = CollectPrivateImplItemsVisitor { // Some methods from non-exported (completely private) trait impls still have to be
tcx, // reachable if they are called from inlinable code. Generally, it's not known until
access_levels, // monomorphization if a specific trait impl item can be reachable or not. So, we
worklist: &mut reachable_context.worklist, // conservatively mark all of them as reachable.
}; // FIXME: One possible strategy for pruning the reachable set is to avoid marking impl
tcx.hir().visit_all_item_likes(&mut collect_private_impl_items); // items of non-exported traits (or maybe all local traits?) unless their respective
// trait items are used from inlinable code through method call syntax or UFCS, or their
// trait is a lang item.
let crate_items = tcx.hir_crate_items(());
for id in crate_items.items() {
check_item(tcx, id, &mut reachable_context.worklist, access_levels);
}
for id in crate_items.impl_items() {
if has_custom_linkage(tcx, id.def_id) {
reachable_context.worklist.push(id.def_id);
}
}
} }
// Step 2: Mark all symbols that the symbols on the worklist touch. // Step 2: Mark all symbols that the symbols on the worklist touch.

View File

@ -661,7 +661,7 @@ fn stability_index(tcx: TyCtxt<'_>, (): ()) -> Index {
/// Cross-references the feature names of unstable APIs with enabled /// Cross-references the feature names of unstable APIs with enabled
/// features and possibly prints errors. /// features and possibly prints errors.
fn check_mod_unstable_api_usage(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { fn check_mod_unstable_api_usage(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
tcx.hir().visit_item_likes_in_module(module_def_id, &mut Checker { tcx }.as_deep_visitor()); tcx.hir().deep_visit_item_likes_in_module(module_def_id, &mut Checker { tcx });
} }
pub(crate) fn provide(providers: &mut Providers) { pub(crate) fn provide(providers: &mut Providers) {
@ -837,7 +837,7 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
let mut missing = MissingStabilityAnnotations { tcx, access_levels }; let mut missing = MissingStabilityAnnotations { tcx, access_levels };
missing.check_missing_stability(CRATE_DEF_ID, tcx.hir().span(CRATE_HIR_ID)); missing.check_missing_stability(CRATE_DEF_ID, tcx.hir().span(CRATE_HIR_ID));
tcx.hir().walk_toplevel_module(&mut missing); tcx.hir().walk_toplevel_module(&mut missing);
tcx.hir().visit_all_item_likes(&mut missing.as_deep_visitor()); tcx.hir().deep_visit_all_item_likes(&mut missing);
} }
let declared_lang_features = &tcx.features().declared_lang_features; let declared_lang_features = &tcx.features().declared_lang_features;

View File

@ -14,8 +14,8 @@ use rustc_errors::struct_span_err;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdSet, CRATE_DEF_ID}; use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdSet, CRATE_DEF_ID};
use rustc_hir::intravisit::{self, DeepVisitor, Visitor}; use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{AssocItemKind, HirIdSet, Node, PatKind}; use rustc_hir::{AssocItemKind, HirIdSet, ItemId, Node, PatKind};
use rustc_middle::bug; use rustc_middle::bug;
use rustc_middle::hir::nested_filter; use rustc_middle::hir::nested_filter;
use rustc_middle::middle::privacy::{AccessLevel, AccessLevels}; use rustc_middle::middle::privacy::{AccessLevel, AccessLevels};
@ -1802,12 +1802,12 @@ impl<'tcx> DefIdVisitor<'tcx> for SearchInterfaceForPrivateItemsVisitor<'tcx> {
} }
} }
struct PrivateItemsInPublicInterfacesVisitor<'tcx> { struct PrivateItemsInPublicInterfacesChecker<'tcx> {
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
old_error_set_ancestry: LocalDefIdSet, old_error_set_ancestry: LocalDefIdSet,
} }
impl<'tcx> PrivateItemsInPublicInterfacesVisitor<'tcx> { impl<'tcx> PrivateItemsInPublicInterfacesChecker<'tcx> {
fn check( fn check(
&self, &self,
def_id: LocalDefId, def_id: LocalDefId,
@ -1841,110 +1841,110 @@ impl<'tcx> PrivateItemsInPublicInterfacesVisitor<'tcx> {
check.ty(); check.ty();
} }
} }
}
impl<'tcx> Visitor<'tcx> for PrivateItemsInPublicInterfacesVisitor<'tcx> { pub fn check_item(&mut self, id: ItemId) {
type NestedFilter = nested_filter::OnlyBodies;
fn nested_visit_map(&mut self) -> Self::Map {
self.tcx.hir()
}
fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
let tcx = self.tcx; let tcx = self.tcx;
let item_visibility = tcx.visibility(item.def_id); let item_visibility = tcx.visibility(id.def_id);
let def_kind = tcx.def_kind(id.def_id);
match item.kind { match def_kind {
// Crates are always public. DefKind::Const | DefKind::Static(_) | DefKind::Fn | DefKind::TyAlias => {
hir::ItemKind::ExternCrate(..) => {} self.check(id.def_id, item_visibility).generics().predicates().ty();
// All nested items are checked by `visit_item`.
hir::ItemKind::Mod(..) => {}
// Checked in resolve.
hir::ItemKind::Use(..) => {}
// No subitems.
hir::ItemKind::Macro(..) | hir::ItemKind::GlobalAsm(..) => {}
// Subitems of these items have inherited publicity.
hir::ItemKind::Const(..)
| hir::ItemKind::Static(..)
| hir::ItemKind::Fn(..)
| hir::ItemKind::TyAlias(..) => {
self.check(item.def_id, item_visibility).generics().predicates().ty();
} }
hir::ItemKind::OpaqueTy(..) => { DefKind::OpaqueTy => {
// `ty()` for opaque types is the underlying type, // `ty()` for opaque types is the underlying type,
// it's not a part of interface, so we skip it. // it's not a part of interface, so we skip it.
self.check(item.def_id, item_visibility).generics().bounds(); self.check(id.def_id, item_visibility).generics().bounds();
} }
hir::ItemKind::Trait(.., trait_item_refs) => { DefKind::Trait => {
self.check(item.def_id, item_visibility).generics().predicates(); let item = tcx.hir().item(id);
if let hir::ItemKind::Trait(.., trait_item_refs) = item.kind {
self.check(item.def_id, item_visibility).generics().predicates();
for trait_item_ref in trait_item_refs { for trait_item_ref in trait_item_refs {
self.check_assoc_item( self.check_assoc_item(
trait_item_ref.id.def_id, trait_item_ref.id.def_id,
trait_item_ref.kind, trait_item_ref.kind,
trait_item_ref.defaultness, trait_item_ref.defaultness,
item_visibility, item_visibility,
); );
if let AssocItemKind::Type = trait_item_ref.kind { if let AssocItemKind::Type = trait_item_ref.kind {
self.check(trait_item_ref.id.def_id, item_visibility).bounds(); self.check(trait_item_ref.id.def_id, item_visibility).bounds();
}
} }
} }
} }
hir::ItemKind::TraitAlias(..) => { DefKind::TraitAlias => {
self.check(item.def_id, item_visibility).generics().predicates(); self.check(id.def_id, item_visibility).generics().predicates();
} }
hir::ItemKind::Enum(ref def, _) => { DefKind::Enum => {
self.check(item.def_id, item_visibility).generics().predicates(); let item = tcx.hir().item(id);
if let hir::ItemKind::Enum(ref def, _) = item.kind {
self.check(item.def_id, item_visibility).generics().predicates();
for variant in def.variants { for variant in def.variants {
for field in variant.data.fields() { for field in variant.data.fields() {
self.check(self.tcx.hir().local_def_id(field.hir_id), item_visibility).ty(); self.check(self.tcx.hir().local_def_id(field.hir_id), item_visibility)
.ty();
}
} }
} }
} }
// Subitems of foreign modules have their own publicity. // Subitems of foreign modules have their own publicity.
hir::ItemKind::ForeignMod { items, .. } => { DefKind::ForeignMod => {
for foreign_item in items { let item = tcx.hir().item(id);
let vis = tcx.visibility(foreign_item.id.def_id); if let hir::ItemKind::ForeignMod { items, .. } = item.kind {
self.check(foreign_item.id.def_id, vis).generics().predicates().ty(); for foreign_item in items {
let vis = tcx.visibility(foreign_item.id.def_id);
self.check(foreign_item.id.def_id, vis).generics().predicates().ty();
}
} }
} }
// Subitems of structs and unions have their own publicity. // Subitems of structs and unions have their own publicity.
hir::ItemKind::Struct(ref struct_def, _) | hir::ItemKind::Union(ref struct_def, _) => { DefKind::Struct | DefKind::Union => {
self.check(item.def_id, item_visibility).generics().predicates(); let item = tcx.hir().item(id);
if let hir::ItemKind::Struct(ref struct_def, _)
| hir::ItemKind::Union(ref struct_def, _) = item.kind
{
self.check(item.def_id, item_visibility).generics().predicates();
for field in struct_def.fields() { for field in struct_def.fields() {
let def_id = tcx.hir().local_def_id(field.hir_id); let def_id = tcx.hir().local_def_id(field.hir_id);
let field_visibility = tcx.visibility(def_id); let field_visibility = tcx.visibility(def_id);
self.check(def_id, min(item_visibility, field_visibility, tcx)).ty(); self.check(def_id, min(item_visibility, field_visibility, tcx)).ty();
}
} }
} }
// An inherent impl is public when its type is public // An inherent impl is public when its type is public
// Subitems of inherent impls have their own publicity. // Subitems of inherent impls have their own publicity.
// A trait impl is public when both its type and its trait are public // A trait impl is public when both its type and its trait are public
// Subitems of trait impls have inherited publicity. // Subitems of trait impls have inherited publicity.
hir::ItemKind::Impl(ref impl_) => { DefKind::Impl => {
let impl_vis = ty::Visibility::of_impl(item.def_id, tcx, &Default::default()); let item = tcx.hir().item(id);
// check that private components do not appear in the generics or predicates of inherent impls if let hir::ItemKind::Impl(ref impl_) = item.kind {
// this check is intentionally NOT performed for impls of traits, per #90586 let impl_vis = ty::Visibility::of_impl(item.def_id, tcx, &Default::default());
if impl_.of_trait.is_none() { // check that private components do not appear in the generics or predicates of inherent impls
self.check(item.def_id, impl_vis).generics().predicates(); // this check is intentionally NOT performed for impls of traits, per #90586
} if impl_.of_trait.is_none() {
for impl_item_ref in impl_.items { self.check(item.def_id, impl_vis).generics().predicates();
let impl_item_vis = if impl_.of_trait.is_none() { }
min(tcx.visibility(impl_item_ref.id.def_id), impl_vis, tcx) for impl_item_ref in impl_.items {
} else { let impl_item_vis = if impl_.of_trait.is_none() {
impl_vis min(tcx.visibility(impl_item_ref.id.def_id), impl_vis, tcx)
}; } else {
self.check_assoc_item( impl_vis
impl_item_ref.id.def_id, };
impl_item_ref.kind, self.check_assoc_item(
impl_item_ref.defaultness, impl_item_ref.id.def_id,
impl_item_vis, impl_item_ref.kind,
); impl_item_ref.defaultness,
impl_item_vis,
);
}
} }
} }
_ => {}
} }
} }
} }
@ -2069,7 +2069,7 @@ fn check_private_in_public(tcx: TyCtxt<'_>, (): ()) {
} }
// Check for private types and traits in public interfaces. // Check for private types and traits in public interfaces.
let mut visitor = PrivateItemsInPublicInterfacesVisitor { let mut checker = PrivateItemsInPublicInterfacesChecker {
tcx, tcx,
// Only definition IDs are ever searched in `old_error_set_ancestry`, // Only definition IDs are ever searched in `old_error_set_ancestry`,
// so we can filter away all non-definition IDs at this point. // so we can filter away all non-definition IDs at this point.
@ -2078,5 +2078,8 @@ fn check_private_in_public(tcx: TyCtxt<'_>, (): ()) {
.filter_map(|hir_id| tcx.hir().opt_local_def_id(hir_id)) .filter_map(|hir_id| tcx.hir().opt_local_def_id(hir_id))
.collect(), .collect(),
}; };
tcx.hir().visit_all_item_likes(&mut DeepVisitor::new(&mut visitor));
for id in tcx.hir().items() {
checker.check_item(id);
}
} }

View File

@ -59,10 +59,7 @@ struct OnlySelfBounds(bool);
// Main entry point // Main entry point
fn collect_mod_item_types(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { fn collect_mod_item_types(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
tcx.hir().visit_item_likes_in_module( tcx.hir().deep_visit_item_likes_in_module(module_def_id, &mut CollectItemTypesVisitor { tcx });
module_def_id,
&mut CollectItemTypesVisitor { tcx }.as_deep_visitor(),
);
} }
pub fn provide(providers: &mut Providers) { pub fn provide(providers: &mut Providers) {

View File

@ -303,7 +303,7 @@ crate fn run(
// Run call-finder on all items // Run call-finder on all items
let mut calls = FxHashMap::default(); let mut calls = FxHashMap::default();
let mut finder = FindCalls { calls: &mut calls, tcx, map: tcx.hir(), cx, target_crates }; let mut finder = FindCalls { calls: &mut calls, tcx, map: tcx.hir(), cx, target_crates };
tcx.hir().visit_all_item_likes(&mut finder.as_deep_visitor()); tcx.hir().deep_visit_all_item_likes(&mut finder);
// Sort call locations within a given file in document order // Sort call locations within a given file in document order
for fn_calls in calls.values_mut() { for fn_calls in calls.values_mut() {

View File

@ -21,7 +21,7 @@ extern crate rustc_session;
use rustc_borrowck::consumers::BodyWithBorrowckFacts; use rustc_borrowck::consumers::BodyWithBorrowckFacts;
use rustc_driver::Compilation; use rustc_driver::Compilation;
use rustc_hir::def_id::LocalDefId; use rustc_hir::def_id::LocalDefId;
use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::def::DefKind;
use rustc_interface::interface::Compiler; use rustc_interface::interface::Compiler;
use rustc_interface::{Config, Queries}; use rustc_interface::{Config, Queries};
use rustc_middle::ty::query::query_values::mir_borrowck; use rustc_middle::ty::query::query_values::mir_borrowck;
@ -65,11 +65,34 @@ impl rustc_driver::Callbacks for CompilerCalls {
queries.global_ctxt().unwrap().peek_mut().enter(|tcx| { queries.global_ctxt().unwrap().peek_mut().enter(|tcx| {
// Collect definition ids of MIR bodies. // Collect definition ids of MIR bodies.
let hir = tcx.hir(); let hir = tcx.hir();
let mut visitor = HirVisitor { bodies: Vec::new() }; let mut bodies = Vec::new();
hir.visit_all_item_likes(&mut visitor);
let crate_items = tcx.hir_crate_items(());
for id in crate_items.items() {
if matches!(tcx.def_kind(id.def_id), DefKind::Fn) {
bodies.push(id.def_id);
}
}
for id in crate_items.trait_items() {
if matches!(tcx.def_kind(id.def_id), DefKind::AssocFn) {
let trait_item = hir.trait_item(id);
if let rustc_hir::TraitItemKind::Fn(_, trait_fn) = &trait_item.kind {
if let rustc_hir::TraitFn::Provided(_) = trait_fn {
bodies.push(trait_item.def_id);
}
}
}
}
for id in crate_items.impl_items() {
if matches!(tcx.def_kind(id.def_id), DefKind::AssocFn) {
bodies.push(id.def_id);
}
}
// Trigger borrow checking of all bodies. // Trigger borrow checking of all bodies.
for def_id in visitor.bodies { for def_id in bodies {
let _ = tcx.optimized_mir(def_id); let _ = tcx.optimized_mir(def_id);
} }
@ -121,35 +144,6 @@ fn mir_borrowck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> mir_borrowck<'tc
original_mir_borrowck(tcx, def_id) original_mir_borrowck(tcx, def_id)
} }
/// Visitor that collects all body definition ids mentioned in the program.
struct HirVisitor {
bodies: Vec<LocalDefId>,
}
impl<'tcx> ItemLikeVisitor<'tcx> for HirVisitor {
fn visit_item(&mut self, item: &rustc_hir::Item) {
if let rustc_hir::ItemKind::Fn(..) = item.kind {
self.bodies.push(item.def_id);
}
}
fn visit_trait_item(&mut self, trait_item: &rustc_hir::TraitItem) {
if let rustc_hir::TraitItemKind::Fn(_, trait_fn) = &trait_item.kind {
if let rustc_hir::TraitFn::Provided(_) = trait_fn {
self.bodies.push(trait_item.def_id);
}
}
}
fn visit_impl_item(&mut self, impl_item: &rustc_hir::ImplItem) {
if let rustc_hir::ImplItemKind::Fn(..) = impl_item.kind {
self.bodies.push(impl_item.def_id);
}
}
fn visit_foreign_item(&mut self, _foreign_item: &rustc_hir::ForeignItem) {}
}
/// Pull MIR bodies stored in the thread-local. /// Pull MIR bodies stored in the thread-local.
fn get_bodies<'tcx>(tcx: TyCtxt<'tcx>) -> Vec<(String, BodyWithBorrowckFacts<'tcx>)> { fn get_bodies<'tcx>(tcx: TyCtxt<'tcx>) -> Vec<(String, BodyWithBorrowckFacts<'tcx>)> {
MIR_BODIES.with(|state| { MIR_BODIES.with(|state| {

View File

@ -2,10 +2,10 @@ error[E0138]: multiple `start` functions
--> $DIR/E0138.rs:7:1 --> $DIR/E0138.rs:7:1
| |
LL | fn foo(argc: isize, argv: *const *const u8) -> isize { 0 } LL | fn foo(argc: isize, argv: *const *const u8) -> isize { 0 }
| ---------------------------------------------------------- previous `#[start]` function here | ---------------------------------------------------- previous `#[start]` function here
... ...
LL | fn f(argc: isize, argv: *const *const u8) -> isize { 0 } LL | fn f(argc: isize, argv: *const *const u8) -> isize { 0 }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ multiple `start` functions | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ multiple `start` functions
error: aborting due to previous error error: aborting due to previous error

View File

@ -8,7 +8,7 @@ note: here is a function named `main`
--> $DIR/main-wrong-location.rs:4:5 --> $DIR/main-wrong-location.rs:4:5
| |
LL | fn main() { } LL | fn main() { }
| ^^^^^^^^^^^^^ | ^^^^^^^^^
= note: you have one or more functions named `main` not defined at the crate level = note: you have one or more functions named `main` not defined at the crate level
= help: consider moving the `main` function definitions = help: consider moving the `main` function definitions

View File

@ -74,11 +74,10 @@ use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, CRATE_DEF_ID}; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, CRATE_DEF_ID};
use rustc_hir::hir_id::{HirIdMap, HirIdSet}; use rustc_hir::hir_id::{HirIdMap, HirIdSet};
use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk}; use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk};
use rustc_hir::{ use rustc_hir::{
def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Constness, Destination, Expr, ExprKind, FnDecl, def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Constness, Destination, Expr, ExprKind, FnDecl,
ForeignItem, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource,
Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind,
TraitRef, TyKind, UnOp, TraitRef, TyKind, UnOp,
}; };
@ -2068,35 +2067,6 @@ pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
false false
} }
struct TestItemNamesVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
names: Vec<Symbol>,
}
impl<'hir> ItemLikeVisitor<'hir> for TestItemNamesVisitor<'hir> {
fn visit_item(&mut self, item: &Item<'_>) {
if let ItemKind::Const(ty, _body) = item.kind {
if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
// We could also check for the type name `test::TestDescAndFn`
if let Res::Def(DefKind::Struct, _) = path.res {
let has_test_marker = self
.tcx
.hir()
.attrs(item.hir_id())
.iter()
.any(|a| a.has_name(sym::rustc_test_marker));
if has_test_marker {
self.names.push(item.ident.name);
}
}
}
}
}
fn visit_trait_item(&mut self, _: &TraitItem<'_>) {}
fn visit_impl_item(&mut self, _: &ImplItem<'_>) {}
fn visit_foreign_item(&mut self, _: &ForeignItem<'_>) {}
}
static TEST_ITEM_NAMES_CACHE: SyncOnceCell<Mutex<FxHashMap<LocalDefId, Vec<Symbol>>>> = SyncOnceCell::new(); static TEST_ITEM_NAMES_CACHE: SyncOnceCell<Mutex<FxHashMap<LocalDefId, Vec<Symbol>>>> = SyncOnceCell::new();
fn with_test_item_names<'tcx>(tcx: TyCtxt<'tcx>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool { fn with_test_item_names<'tcx>(tcx: TyCtxt<'tcx>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool {
@ -2105,10 +2075,28 @@ fn with_test_item_names<'tcx>(tcx: TyCtxt<'tcx>, module: LocalDefId, f: impl Fn(
match map.entry(module) { match map.entry(module) {
Entry::Occupied(entry) => f(entry.get()), Entry::Occupied(entry) => f(entry.get()),
Entry::Vacant(entry) => { Entry::Vacant(entry) => {
let mut visitor = TestItemNamesVisitor { tcx, names: Vec::new() }; let mut names = Vec::new();
tcx.hir().visit_item_likes_in_module(module, &mut visitor); for id in tcx.hir().module_items(module) {
visitor.names.sort_unstable(); if matches!(tcx.def_kind(id.def_id), DefKind::Const)
f(&*entry.insert(visitor.names)) && let item = tcx.hir().item(id)
&& let ItemKind::Const(ty, _body) = item.kind {
if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
// We could also check for the type name `test::TestDescAndFn`
if let Res::Def(DefKind::Struct, _) = path.res {
let has_test_marker = tcx
.hir()
.attrs(item.hir_id())
.iter()
.any(|a| a.has_name(sym::rustc_test_marker));
if has_test_marker {
names.push(item.ident.name);
}
}
}
}
}
names.sort_unstable();
f(&*entry.insert(names))
}, },
} }
} }