Rollup merge of #144000 - jacob-greenfield:stable-defid-parent, r=celinval

Add `DefId::parent()` accessor for `rustc_public`

Adds a `parent()` method to `DefId` (the `rustc_pub` version) which exposes the parent path, ie. `foo::bar::baz` -> `foo::bar`.

This is useful for organizing/grouping definitions into a tree, and is probably simpler and less brittle than attempting to parse the fully-qualified name into path components (e.g. especially when handling path components with qualified generic parameters).
This commit is contained in:
Stuart Cook 2025-11-29 21:12:25 +11:00 committed by GitHub
commit 3a0187ec5d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 209 additions and 0 deletions

View file

@ -249,6 +249,14 @@ impl<'tcx> CompilerInterface<'tcx> {
cx.def_name(did, trimmed)
}
/// Returns the parent of the given `DefId`.
pub(crate) fn def_parent(&self, def_id: DefId) -> Option<DefId> {
let mut tables = self.tables.borrow_mut();
let cx = &*self.cx.borrow();
let did = tables[def_id];
cx.def_parent(did).map(|did| tables.create_def_id(did))
}
/// Return registered tool attributes with the given attribute name.
///
/// FIXME(jdonszelmann): may panic on non-tool attributes. After more attribute work, non-tool

View file

@ -28,6 +28,12 @@ impl DefId {
pub fn trimmed_name(&self) -> Symbol {
with(|cx| cx.def_name(*self, true))
}
/// Return the parent of this definition, or `None` if this is the root of a
/// crate.
pub fn parent(&self) -> Option<DefId> {
with(|cx| cx.def_parent(*self))
}
}
/// A trait for retrieving information about a particular definition.

View file

@ -268,6 +268,11 @@ impl<'tcx, B: Bridge> CompilerCtxt<'tcx, B> {
}
}
/// Returns the parent of the given `DefId`.
pub fn def_parent(&self, def_id: DefId) -> Option<DefId> {
self.tcx.opt_parent(def_id)
}
/// Return registered tool attributes with the given attribute name.
///
/// FIXME(jdonszelmann): may panic on non-tool attributes. After more attribute work, non-tool

View file

@ -0,0 +1,190 @@
//@ run-pass
//! Test that users are able to use public MIR APIs to retrieve information about parent
//! definitions.
//@ ignore-stage1
//@ ignore-cross-compile
//@ ignore-remote
//@ edition: 2024
// ignore-tidy-linelength
#![feature(rustc_private)]
extern crate rustc_middle;
extern crate rustc_driver;
extern crate rustc_interface;
extern crate rustc_public;
use rustc_public::ty::{RigidTy, TyKind};
use rustc_public::*;
use std::fmt::Debug;
use std::io::Write;
use std::ops::ControlFlow;
const CRATE_NAME: &str = "input";
/// Verify that each def has the correct parent
fn test_stable_mir() -> ControlFlow<()> {
fn set_once<T: Debug + PartialEq>(slot: &mut Option<T>, val: T) {
assert_eq!(slot.replace(val), None);
}
let mut const_item = None;
let mut static_item = None;
let mut trait_method = None;
let mut trait_method_helper = None;
let mut inherent_method = None;
let mut inherent_method_helper = None;
let mut main = None;
let mut mystruct_ctor = None;
let mut trait_decl = None;
let mut trait_impl = None;
let mut mystruct_ctor_ty = None;
// Extract def-ids of various items
let krate = rustc_public::local_crate();
for it in rustc_public::all_local_items() {
match &*it.0.name() {
"wrapper_mod::CONST_ITEM" => {
set_once(&mut const_item, it.0);
}
"wrapper_mod::STATIC_ITEM" => {
set_once(&mut static_item, it.0);
}
"<wrapper_mod::MyStruct as wrapper_mod::MyTrait>::trait_method" => {
set_once(&mut trait_method, it.0);
}
"<wrapper_mod::MyStruct as wrapper_mod::MyTrait>::trait_method::trait_method_helper" => {
set_once(&mut trait_method_helper, it.0);
}
"wrapper_mod::MyStruct::inherent_method" => {
set_once(&mut inherent_method, it.0);
}
"wrapper_mod::MyStruct::inherent_method::inherent_method_helper" => {
set_once(&mut inherent_method_helper, it.0);
}
"main" => {
set_once(&mut main, it.0);
}
"wrapper_mod::MyStruct" => {
set_once(&mut mystruct_ctor, it.0);
mystruct_ctor_ty = Some(it.ty());
}
_ => (),
}
}
for it in krate.trait_decls() {
match &*it.0.name() {
"wrapper_mod::MyTrait" => set_once(&mut trait_decl, it.0),
_ => (),
}
}
for it in krate.trait_impls() {
match &*it.0.name() {
"<wrapper_mod::MyStruct as wrapper_mod::MyTrait>" => set_once(&mut trait_impl, it.0),
_ => (),
}
}
let const_item = const_item.unwrap();
let static_item = static_item.unwrap();
let trait_method = trait_method.unwrap();
let trait_method_helper = trait_method_helper.unwrap();
let inherent_method = inherent_method.unwrap();
let inherent_method_helper = inherent_method_helper.unwrap();
let main = main.unwrap();
let mystruct_ctor = mystruct_ctor.unwrap();
let trait_decl = trait_decl.unwrap();
let trait_impl = trait_impl.unwrap();
let mystruct_ctor_ty = mystruct_ctor_ty.unwrap();
let mystruct_ty = mystruct_ctor_ty.kind().fn_def().unwrap().0.fn_sig().skip_binder().output();
let TyKind::RigidTy(RigidTy::Adt(mystruct_adt_def, _)) = mystruct_ty.kind() else { panic!() };
let inherent_impl = inherent_method.parent().unwrap();
let wrapper_mod = const_item.parent().unwrap();
let crate_root = wrapper_mod.parent().unwrap();
assert_eq!(&*wrapper_mod.name(), "wrapper_mod");
// Check that each def-id has the correct parent
assert_eq!(crate_root.parent(), None);
assert_eq!(inherent_impl.parent(), Some(wrapper_mod));
assert_eq!(const_item.parent(), Some(wrapper_mod));
assert_eq!(static_item.parent(), Some(wrapper_mod));
assert_eq!(trait_method.parent(), Some(trait_impl));
assert_eq!(trait_method_helper.parent(), Some(trait_method));
assert_eq!(inherent_method_helper.parent(), Some(inherent_method));
assert_eq!(main.parent(), Some(crate_root));
assert_eq!(trait_decl.parent(), Some(wrapper_mod));
assert_eq!(trait_impl.parent(), Some(wrapper_mod));
assert_eq!(mystruct_ctor.parent(), Some(mystruct_adt_def.0));
assert_eq!(mystruct_adt_def.0.parent(), Some(wrapper_mod));
ControlFlow::Continue(())
}
/// This test will generate and analyze a dummy crate using the stable mir.
/// For that, it will first write the dummy crate into a file.
/// Then it will create a `RustcPublic` using custom arguments and then
/// it will run the compiler.
fn main() {
let path = "def_parent_input.rs";
generate_input(&path).unwrap();
let args = &[
"rustc".to_string(),
"-Cpanic=abort".to_string(),
"--crate-name".to_string(),
CRATE_NAME.to_string(),
path.to_string(),
];
run!(args, test_stable_mir).unwrap();
}
fn generate_input(path: &str) -> std::io::Result<()> {
let mut file = std::fs::File::create(path)?;
write!(
file,
r#"
mod wrapper_mod {{
pub const CONST_ITEM: u32 = 100;
pub static STATIC_ITEM: u32 = 150;
pub struct MyStruct(pub u32);
pub trait MyTrait {{
fn trait_method(&self);
}}
impl MyTrait for MyStruct {{
fn trait_method(&self) {{
fn trait_method_helper() {{}}
trait_method_helper()
}}
}}
impl MyStruct {{
pub fn inherent_method(&self) {{
println!("{{}}", self.0);
fn inherent_method_helper() {{}}
inherent_method_helper()
}}
}}
}}
use wrapper_mod::{{MyStruct, MyTrait, CONST_ITEM, STATIC_ITEM}};
fn main() {{
let mystruct = MyStruct(200);
mystruct.trait_method();
mystruct.inherent_method();
let _const = CONST_ITEM;
let _static = STATIC_ITEM;
}}
"#
)?;
Ok(())
}