diff --git a/Cargo.lock b/Cargo.lock index e977964b72c..0182eca0505 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 1e0a60bc371..1230667ee91 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 dde5e30c3d0..0e50241fce7 100644 --- a/compiler/rustc_smir/src/rustc_smir/context.rs +++ b/compiler/rustc_smir/src/rustc_smir/context.rs @@ -228,6 +228,25 @@ fn def_name(&self, def_id: stable_mir::DefId, trimmed: bool) -> Symbol { } } + 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 44dbf549c1a..a8f2cd7bcc3 100644 --- a/compiler/stable_mir/src/compiler_interface.rs +++ b/compiler/stable_mir/src/compiler_interface.rs @@ -11,7 +11,7 @@ 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 67752a5e629..ee6214a47cf 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 @@ fn span(&self) -> Span { 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 01e4f1d1f33..46c163e5bf1 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 00000000000..7fec0e1a90f --- /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(()) +}