Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support fetching Attribute of items. #127022

Merged
merged 2 commits into from
Jun 28, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
@@ -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",
2 changes: 2 additions & 0 deletions compiler/rustc_smir/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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" }
40 changes: 40 additions & 0 deletions compiler/rustc_smir/src/rustc_smir/context.rs
Original file line number Diff line number Diff line change
@@ -228,6 +228,46 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
}
}

fn get_attrs_by_path(
&self,
def_id: stable_mir::DefId,
attr: &[stable_mir::Symbol],
) -> Vec<stable_mir::crate_def::Attribute> {
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::crate_def::Attribute::new(attr_str, span.stable(&mut *tables))
})
.collect()
}

fn get_all_attrs(&self, def_id: stable_mir::DefId) -> Vec<stable_mir::crate_def::Attribute> {
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()
}

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])
10 changes: 10 additions & 0 deletions compiler/stable_mir/src/compiler_interface.rs
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@
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};
@@ -55,6 +56,15 @@ pub trait Context {
/// Returns the name of given `DefId`
fn def_name(&self, def_id: DefId, trimmed: bool) -> Symbol;

/// 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<Attribute>;

/// Get all attributes of a definition.
fn get_all_attrs(&self, def_id: DefId) -> Vec<Attribute>;

/// Returns printable, human readable form of `Span`
fn span_to_string(&self, span: Span) -> String;

37 changes: 37 additions & 0 deletions compiler/stable_mir/src/crate_def.rs
Original file line number Diff line number Diff line change
@@ -50,6 +50,21 @@ 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.
///
/// 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<Attribute> {
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<Attribute> {
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.
@@ -69,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 $(;)?
155 changes: 155 additions & 0 deletions tests/ui-fulldeps/stable-mir/check_attribute.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
//@ 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);
test_all_attrs(&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, "<Foo as std::fmt::Debug>::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 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> {
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() {{}}

// A function with many attributes.
#[inline]
#[allow(unused_variables)]
#[allow(dead_code)]
#[allow(unused_imports)]
fn many_attrs() {{
#![allow(clippy::filter_map)]
todo!()
}}
"#
)?;
Ok(())
}