From 84071e2662daa89f00f1b8d374b95325b334c180 Mon Sep 17 00:00:00 2001 From: Adwin White Date: Wed, 26 Jun 2024 12:09:55 +0800 Subject: [PATCH 1/2] Support fetching `Attribute` of items. --- Cargo.lock | 2 + compiler/rustc_smir/Cargo.toml | 2 + compiler/rustc_smir/src/rustc_smir/context.rs | 19 +++ compiler/stable_mir/src/compiler_interface.rs | 5 +- compiler/stable_mir/src/crate_def.rs | 8 +- compiler/stable_mir/src/ty.rs | 22 +++ .../ui-fulldeps/stable-mir/check_attribute.rs | 137 ++++++++++++++++++ 7 files changed, 193 insertions(+), 2 deletions(-) create mode 100644 tests/ui-fulldeps/stable-mir/check_attribute.rs diff --git a/Cargo.lock b/Cargo.lock index e977964b72c01..0182eca05058d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4675,6 +4675,8 @@ name = "rustc_smir" version = "0.0.0" dependencies = [ "rustc_abi", + "rustc_ast", + "rustc_ast_pretty", "rustc_data_structures", "rustc_hir", "rustc_middle", diff --git a/compiler/rustc_smir/Cargo.toml b/compiler/rustc_smir/Cargo.toml index 1e0a60bc371ac..1230667ee910a 100644 --- a/compiler/rustc_smir/Cargo.toml +++ b/compiler/rustc_smir/Cargo.toml @@ -6,6 +6,8 @@ edition = "2021" [dependencies] # tidy-alphabetical-start rustc_abi = { path = "../rustc_abi" } +rustc_ast = { path = "../rustc_ast" } +rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_hir = { path = "../rustc_hir" } rustc_middle = { path = "../rustc_middle" } diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs index dde5e30c3d071..0e50241fce79a 100644 --- a/compiler/rustc_smir/src/rustc_smir/context.rs +++ b/compiler/rustc_smir/src/rustc_smir/context.rs @@ -228,6 +228,25 @@ impl<'tcx> Context for TablesWrapper<'tcx> { } } + fn get_attrs_by_path( + &self, + def_id: stable_mir::DefId, + attr: &[stable_mir::Symbol], + ) -> Vec { + let mut tables = self.0.borrow_mut(); + let tcx = tables.tcx; + let did = tables[def_id]; + let attr_name: Vec<_> = + attr.iter().map(|seg| rustc_span::symbol::Symbol::intern(&seg)).collect(); + tcx.get_attrs_by_path(did, &attr_name) + .map(|attribute| { + let attr_str = rustc_ast_pretty::pprust::attribute_to_string(attribute); + let span = attribute.span; + stable_mir::ty::Attribute::new(attr_str, span.stable(&mut *tables)) + }) + .collect() + } + fn span_to_string(&self, span: stable_mir::ty::Span) -> String { let tables = self.0.borrow(); tables.tcx.sess.source_map().span_to_diagnostic_string(tables[span]) diff --git a/compiler/stable_mir/src/compiler_interface.rs b/compiler/stable_mir/src/compiler_interface.rs index 44dbf549c1a73..a8f2cd7bcc326 100644 --- a/compiler/stable_mir/src/compiler_interface.rs +++ b/compiler/stable_mir/src/compiler_interface.rs @@ -11,7 +11,7 @@ use crate::mir::mono::{Instance, InstanceDef, StaticDef}; use crate::mir::{BinOp, Body, Place, UnOp}; use crate::target::MachineInfo; use crate::ty::{ - AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, FieldDef, FnDef, ForeignDef, + AdtDef, AdtKind, Allocation, Attribute, ClosureDef, ClosureKind, FieldDef, FnDef, ForeignDef, ForeignItemKind, ForeignModule, ForeignModuleDef, GenericArgs, GenericPredicates, Generics, ImplDef, ImplTrait, IntrinsicDef, LineInfo, MirConst, PolyFnSig, RigidTy, Span, TraitDecl, TraitDef, Ty, TyConst, TyConstId, TyKind, UintTy, VariantDef, @@ -55,6 +55,9 @@ pub trait Context { /// Returns the name of given `DefId` fn def_name(&self, def_id: DefId, trimmed: bool) -> Symbol; + /// Get all attributes with the given attribute name. + fn get_attrs_by_path(&self, def_id: DefId, attr: &[Symbol]) -> Vec; + /// Returns printable, human readable form of `Span` fn span_to_string(&self, span: Span) -> String; diff --git a/compiler/stable_mir/src/crate_def.rs b/compiler/stable_mir/src/crate_def.rs index 67752a5e629f3..ee6214a47cf4f 100644 --- a/compiler/stable_mir/src/crate_def.rs +++ b/compiler/stable_mir/src/crate_def.rs @@ -1,7 +1,7 @@ //! Module that define a common trait for things that represent a crate definition, //! such as, a function, a trait, an enum, and any other definitions. -use crate::ty::{GenericArgs, Span, Ty}; +use crate::ty::{Attribute, GenericArgs, Span, Ty}; use crate::{with, Crate, Symbol}; /// A unique identification number for each item accessible for the current compilation unit. @@ -50,6 +50,12 @@ pub trait CrateDef { let def_id = self.def_id(); with(|cx| cx.span_of_an_item(def_id)) } + + /// Return attributes with the given attribute name. + fn attrs_by_path(&self, attr: &[Symbol]) -> Vec { + let def_id = self.def_id(); + with(|cx| cx.get_attrs_by_path(def_id, attr)) + } } /// A trait that can be used to retrieve a definition's type. diff --git a/compiler/stable_mir/src/ty.rs b/compiler/stable_mir/src/ty.rs index 01e4f1d1f33bc..46c163e5bf15c 100644 --- a/compiler/stable_mir/src/ty.rs +++ b/compiler/stable_mir/src/ty.rs @@ -248,6 +248,28 @@ pub struct Placeholder { pub bound: T, } +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Attribute { + value: String, + span: Span, +} + +impl Attribute { + pub fn new(value: String, span: Span) -> Attribute { + Attribute { value, span } + } + + /// Get the span of this attribute. + pub fn span(&self) -> Span { + self.span + } + + /// Get the string representation of this attribute. + pub fn as_str(&self) -> &str { + &self.value + } +} + #[derive(Clone, Copy, PartialEq, Eq)] pub struct Span(usize); diff --git a/tests/ui-fulldeps/stable-mir/check_attribute.rs b/tests/ui-fulldeps/stable-mir/check_attribute.rs new file mode 100644 index 0000000000000..7fec0e1a90f54 --- /dev/null +++ b/tests/ui-fulldeps/stable-mir/check_attribute.rs @@ -0,0 +1,137 @@ +//@ run-pass +//! Test information regarding type layout. + +//@ ignore-stage1 +//@ ignore-cross-compile +//@ ignore-remote +//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 + +#![feature(rustc_private)] +#![feature(control_flow_enum)] + +extern crate rustc_hir; +#[macro_use] +extern crate rustc_smir; +extern crate rustc_driver; +extern crate rustc_interface; +extern crate stable_mir; + +use rustc_smir::rustc_internal; +use stable_mir::{CrateDef, CrateItems}; +use std::io::Write; +use std::ops::ControlFlow; + +const CRATE_NAME: &str = "input"; + +/// This function uses the Stable MIR APIs to get information about the test crate. +fn test_stable_mir() -> ControlFlow<()> { + // Find items in the local crate. + let items = stable_mir::all_local_items(); + + test_builtins(&items); + test_derive(&items); + test_tool(&items); + + ControlFlow::Continue(()) +} + +// Test built-in attributes. +fn test_builtins(items: &CrateItems) { + let target_fn = *get_item(&items, "builtins_fn").unwrap(); + let allow_attrs = target_fn.attrs_by_path(&["allow".to_string()]); + assert_eq!(allow_attrs[0].as_str(), "#![allow(unused_variables)]"); + + let inline_attrs = target_fn.attrs_by_path(&["inline".to_string()]); + assert_eq!(inline_attrs[0].as_str(), "#[inline]"); + + let deprecated_attrs = target_fn.attrs_by_path(&["deprecated".to_string()]); + assert_eq!(deprecated_attrs[0].as_str(), "#[deprecated(since = \"5.2.0\")]"); +} + +// Test derive attribute. +fn test_derive(items: &CrateItems) { + let target_struct = *get_item(&items, "Foo").unwrap(); + let attrs = target_struct.attrs_by_path(&["derive".to_string()]); + // No `derive` attribute since it's expanded before MIR. + assert_eq!(attrs.len(), 0); + + // Check derived trait method's attributes. + let derived_fmt = *get_item(&items, "::fmt").unwrap(); + // The Rust reference lies about this attribute. It doesn't show up in `clone` or `fmt` impl. + let _fmt_attrs = derived_fmt.attrs_by_path(&["automatically_derived".to_string()]); +} + +// Test tool attributes. +fn test_tool(items: &CrateItems) { + let rustfmt_fn = *get_item(&items, "do_not_format").unwrap(); + let rustfmt_attrs = rustfmt_fn.attrs_by_path(&["rustfmt".to_string(), "skip".to_string()]); + assert_eq!(rustfmt_attrs[0].as_str(), "#[rustfmt::skip]"); + + let clippy_fn = *get_item(&items, "complex_fn").unwrap(); + let clippy_attrs = clippy_fn.attrs_by_path(&["clippy".to_string(), + "cyclomatic_complexity".to_string()]); + assert_eq!(clippy_attrs[0].as_str(), "#[clippy::cyclomatic_complexity = \"100\"]"); +} + + +fn get_item<'a>( + items: &'a CrateItems, + name: &str, +) -> Option<&'a stable_mir::CrateItem> { + for item in items { + println!("{:?}", item); + } + items.iter().find(|crate_item| crate_item.name() == name) +} + +/// 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 `StableMir` using custom arguments and then +/// it will run the compiler. +fn main() { + let path = "attribute_input.rs"; + generate_input(&path).unwrap(); + let args = vec![ + "rustc".to_string(), + "--crate-type=lib".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#" + // General metadata applied to the enclosing module or crate. + #![crate_type = "lib"] + + // Mixed inner and outer attributes. + #[inline] + #[deprecated(since = "5.2.0")] + fn builtins_fn() {{ + #![allow(unused_variables)] + + let x = (); + let y = (); + let z = (); + }} + + // A derive attribute to automatically implement a trait. + #[derive(Debug, Clone, Copy)] + struct Foo(u32); + + // A rustfmt tool attribute. + #[rustfmt::skip] + fn do_not_format() {{}} + + // A clippy tool attribute. + #[clippy::cyclomatic_complexity = "100"] + pub fn complex_fn() {{}} + "# + )?; + Ok(()) +} From 9387b0bad9b094268675b6fab19afdbe32c7fe51 Mon Sep 17 00:00:00 2001 From: Adwin White Date: Fri, 28 Jun 2024 13:22:15 +0800 Subject: [PATCH 2/2] Add method to get all attributes on a definition --- compiler/rustc_smir/src/rustc_smir/context.rs | 25 ++++++++++++-- compiler/stable_mir/src/compiler_interface.rs | 11 +++++-- compiler/stable_mir/src/crate_def.rs | 33 ++++++++++++++++++- compiler/stable_mir/src/ty.rs | 22 ------------- .../ui-fulldeps/stable-mir/check_attribute.rs | 24 ++++++++++++-- 5 files changed, 85 insertions(+), 30 deletions(-) diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs index 0e50241fce79a..e23f4289e981a 100644 --- a/compiler/rustc_smir/src/rustc_smir/context.rs +++ b/compiler/rustc_smir/src/rustc_smir/context.rs @@ -232,7 +232,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> { &self, def_id: stable_mir::DefId, attr: &[stable_mir::Symbol], - ) -> Vec { + ) -> Vec { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let did = tables[def_id]; @@ -242,7 +242,28 @@ impl<'tcx> Context for TablesWrapper<'tcx> { .map(|attribute| { let attr_str = rustc_ast_pretty::pprust::attribute_to_string(attribute); let span = attribute.span; - stable_mir::ty::Attribute::new(attr_str, span.stable(&mut *tables)) + stable_mir::crate_def::Attribute::new(attr_str, span.stable(&mut *tables)) + }) + .collect() + } + + fn get_all_attrs(&self, def_id: stable_mir::DefId) -> Vec { + let mut tables = self.0.borrow_mut(); + let tcx = tables.tcx; + let did = tables[def_id]; + let filter_fn = move |a: &&rustc_ast::ast::Attribute| { + matches!(a.kind, rustc_ast::ast::AttrKind::Normal(_)) + }; + let attrs_iter = if let Some(did) = did.as_local() { + tcx.hir().attrs(tcx.local_def_id_to_hir_id(did)).iter().filter(filter_fn) + } else { + tcx.item_attrs(did).iter().filter(filter_fn) + }; + attrs_iter + .map(|attribute| { + let attr_str = rustc_ast_pretty::pprust::attribute_to_string(attribute); + let span = attribute.span; + stable_mir::crate_def::Attribute::new(attr_str, span.stable(&mut *tables)) }) .collect() } diff --git a/compiler/stable_mir/src/compiler_interface.rs b/compiler/stable_mir/src/compiler_interface.rs index a8f2cd7bcc326..5f2d9b96c73ca 100644 --- a/compiler/stable_mir/src/compiler_interface.rs +++ b/compiler/stable_mir/src/compiler_interface.rs @@ -6,12 +6,13 @@ use std::cell::Cell; use crate::abi::{FnAbi, Layout, LayoutShape}; +use crate::crate_def::Attribute; use crate::mir::alloc::{AllocId, GlobalAlloc}; use crate::mir::mono::{Instance, InstanceDef, StaticDef}; use crate::mir::{BinOp, Body, Place, UnOp}; use crate::target::MachineInfo; use crate::ty::{ - AdtDef, AdtKind, Allocation, Attribute, ClosureDef, ClosureKind, FieldDef, FnDef, ForeignDef, + AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, FieldDef, FnDef, ForeignDef, ForeignItemKind, ForeignModule, ForeignModuleDef, GenericArgs, GenericPredicates, Generics, ImplDef, ImplTrait, IntrinsicDef, LineInfo, MirConst, PolyFnSig, RigidTy, Span, TraitDecl, TraitDef, Ty, TyConst, TyConstId, TyKind, UintTy, VariantDef, @@ -55,9 +56,15 @@ pub trait Context { /// Returns the name of given `DefId` fn def_name(&self, def_id: DefId, trimmed: bool) -> Symbol; - /// Get all attributes with the given attribute name. + /// Return attributes with the given attribute name. + /// + /// Single segmented name like `#[inline]` is specified as `&["inline".to_string()]`. + /// Multi-segmented name like `#[rustfmt::skip]` is specified as `&["rustfmt".to_string(), "skip".to_string()]`. fn get_attrs_by_path(&self, def_id: DefId, attr: &[Symbol]) -> Vec; + /// Get all attributes of a definition. + fn get_all_attrs(&self, def_id: DefId) -> Vec; + /// Returns printable, human readable form of `Span` fn span_to_string(&self, span: Span) -> String; diff --git a/compiler/stable_mir/src/crate_def.rs b/compiler/stable_mir/src/crate_def.rs index ee6214a47cf4f..d9b987c28a26a 100644 --- a/compiler/stable_mir/src/crate_def.rs +++ b/compiler/stable_mir/src/crate_def.rs @@ -1,7 +1,7 @@ //! Module that define a common trait for things that represent a crate definition, //! such as, a function, a trait, an enum, and any other definitions. -use crate::ty::{Attribute, GenericArgs, Span, Ty}; +use crate::ty::{GenericArgs, Span, Ty}; use crate::{with, Crate, Symbol}; /// A unique identification number for each item accessible for the current compilation unit. @@ -52,10 +52,19 @@ pub trait CrateDef { } /// Return attributes with the given attribute name. + /// + /// Single segmented name like `#[inline]` is specified as `&["inline".to_string()]`. + /// Multi-segmented name like `#[rustfmt::skip]` is specified as `&["rustfmt".to_string(), "skip".to_string()]`. fn attrs_by_path(&self, attr: &[Symbol]) -> Vec { let def_id = self.def_id(); with(|cx| cx.get_attrs_by_path(def_id, attr)) } + + /// Return all attributes of this definition. + fn all_attrs(&self) -> Vec { + let def_id = self.def_id(); + with(|cx| cx.get_all_attrs(def_id)) + } } /// A trait that can be used to retrieve a definition's type. @@ -75,6 +84,28 @@ pub trait CrateDefType: CrateDef { } } +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Attribute { + value: String, + span: Span, +} + +impl Attribute { + pub fn new(value: String, span: Span) -> Attribute { + Attribute { value, span } + } + + /// Get the span of this attribute. + pub fn span(&self) -> Span { + self.span + } + + /// Get the string representation of this attribute. + pub fn as_str(&self) -> &str { + &self.value + } +} + macro_rules! crate_def { ( $(#[$attr:meta])* $vis:vis $name:ident $(;)? diff --git a/compiler/stable_mir/src/ty.rs b/compiler/stable_mir/src/ty.rs index 46c163e5bf15c..01e4f1d1f33bc 100644 --- a/compiler/stable_mir/src/ty.rs +++ b/compiler/stable_mir/src/ty.rs @@ -248,28 +248,6 @@ pub struct Placeholder { pub bound: T, } -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Attribute { - value: String, - span: Span, -} - -impl Attribute { - pub fn new(value: String, span: Span) -> Attribute { - Attribute { value, span } - } - - /// Get the span of this attribute. - pub fn span(&self) -> Span { - self.span - } - - /// Get the string representation of this attribute. - pub fn as_str(&self) -> &str { - &self.value - } -} - #[derive(Clone, Copy, PartialEq, Eq)] pub struct Span(usize); diff --git a/tests/ui-fulldeps/stable-mir/check_attribute.rs b/tests/ui-fulldeps/stable-mir/check_attribute.rs index 7fec0e1a90f54..be52853a47950 100644 --- a/tests/ui-fulldeps/stable-mir/check_attribute.rs +++ b/tests/ui-fulldeps/stable-mir/check_attribute.rs @@ -31,6 +31,7 @@ fn test_stable_mir() -> ControlFlow<()> { test_builtins(&items); test_derive(&items); test_tool(&items); + test_all_attrs(&items); ControlFlow::Continue(()) } @@ -73,14 +74,21 @@ fn test_tool(items: &CrateItems) { assert_eq!(clippy_attrs[0].as_str(), "#[clippy::cyclomatic_complexity = \"100\"]"); } +fn test_all_attrs(items: &CrateItems) { + let target_fn = *get_item(&items, "many_attrs").unwrap(); + let all_attrs = target_fn.all_attrs(); + assert_eq!(all_attrs[0].as_str(), "#[inline]"); + assert_eq!(all_attrs[1].as_str(), "#[allow(unused_variables)]"); + assert_eq!(all_attrs[2].as_str(), "#[allow(dead_code)]"); + assert_eq!(all_attrs[3].as_str(), "#[allow(unused_imports)]"); + assert_eq!(all_attrs[4].as_str(), "#![allow(clippy::filter_map)]"); +} + fn get_item<'a>( items: &'a CrateItems, name: &str, ) -> Option<&'a stable_mir::CrateItem> { - for item in items { - println!("{:?}", item); - } items.iter().find(|crate_item| crate_item.name() == name) } @@ -131,6 +139,16 @@ fn generate_input(path: &str) -> std::io::Result<()> { // A clippy tool attribute. #[clippy::cyclomatic_complexity = "100"] pub fn complex_fn() {{}} + + // A function with many attributes. + #[inline] + #[allow(unused_variables)] + #[allow(dead_code)] + #[allow(unused_imports)] + fn many_attrs() {{ + #![allow(clippy::filter_map)] + todo!() + }} "# )?; Ok(())