Skip to content

Commit f5ff54a

Browse files
authored
Unrolled build for rust-lang#127022
Rollup merge of rust-lang#127022 - adwinwhite:attrs, r=celinval Support fetching `Attribute` of items. Fixes [https://github.com/rust-lang/project-stable-mir/issues/83](https://github.com/rust-lang/project-stable-mir/issues/83) `rustc_ast::ast::Attribute` doesn't impl `Hash` and `Eq`. Thus it cannot be directly used as key of `IndexMap` in `rustc_smir::rustc_smir::Tables` and we cannot define stable `Attribute` as index to `rustc_ast::ast::Attribute` like `Span` and many other stable definitions. Since an string (or tokens) and its span contain all info about an attribute, I defined a simple `Attribute` struct on stable side. I choose to fetch attributes via `tcx::get_attrs_by_path()` due to `get_attrs()` is marked as deprecated and `get_attrs_by_name()` cannot handle name of multiple segments like `rustfmt::skip`. r? `@celinval`
2 parents 42add88 + 9387b0b commit f5ff54a

File tree

6 files changed

+246
-0
lines changed

6 files changed

+246
-0
lines changed

Cargo.lock

+2
Original file line numberDiff line numberDiff line change
@@ -4675,6 +4675,8 @@ name = "rustc_smir"
46754675
version = "0.0.0"
46764676
dependencies = [
46774677
"rustc_abi",
4678+
"rustc_ast",
4679+
"rustc_ast_pretty",
46784680
"rustc_data_structures",
46794681
"rustc_hir",
46804682
"rustc_middle",

compiler/rustc_smir/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ edition = "2021"
66
[dependencies]
77
# tidy-alphabetical-start
88
rustc_abi = { path = "../rustc_abi" }
9+
rustc_ast = { path = "../rustc_ast" }
10+
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
911
rustc_data_structures = { path = "../rustc_data_structures" }
1012
rustc_hir = { path = "../rustc_hir" }
1113
rustc_middle = { path = "../rustc_middle" }

compiler/rustc_smir/src/rustc_smir/context.rs

+40
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,46 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
228228
}
229229
}
230230

231+
fn get_attrs_by_path(
232+
&self,
233+
def_id: stable_mir::DefId,
234+
attr: &[stable_mir::Symbol],
235+
) -> Vec<stable_mir::crate_def::Attribute> {
236+
let mut tables = self.0.borrow_mut();
237+
let tcx = tables.tcx;
238+
let did = tables[def_id];
239+
let attr_name: Vec<_> =
240+
attr.iter().map(|seg| rustc_span::symbol::Symbol::intern(&seg)).collect();
241+
tcx.get_attrs_by_path(did, &attr_name)
242+
.map(|attribute| {
243+
let attr_str = rustc_ast_pretty::pprust::attribute_to_string(attribute);
244+
let span = attribute.span;
245+
stable_mir::crate_def::Attribute::new(attr_str, span.stable(&mut *tables))
246+
})
247+
.collect()
248+
}
249+
250+
fn get_all_attrs(&self, def_id: stable_mir::DefId) -> Vec<stable_mir::crate_def::Attribute> {
251+
let mut tables = self.0.borrow_mut();
252+
let tcx = tables.tcx;
253+
let did = tables[def_id];
254+
let filter_fn = move |a: &&rustc_ast::ast::Attribute| {
255+
matches!(a.kind, rustc_ast::ast::AttrKind::Normal(_))
256+
};
257+
let attrs_iter = if let Some(did) = did.as_local() {
258+
tcx.hir().attrs(tcx.local_def_id_to_hir_id(did)).iter().filter(filter_fn)
259+
} else {
260+
tcx.item_attrs(did).iter().filter(filter_fn)
261+
};
262+
attrs_iter
263+
.map(|attribute| {
264+
let attr_str = rustc_ast_pretty::pprust::attribute_to_string(attribute);
265+
let span = attribute.span;
266+
stable_mir::crate_def::Attribute::new(attr_str, span.stable(&mut *tables))
267+
})
268+
.collect()
269+
}
270+
231271
fn span_to_string(&self, span: stable_mir::ty::Span) -> String {
232272
let tables = self.0.borrow();
233273
tables.tcx.sess.source_map().span_to_diagnostic_string(tables[span])

compiler/stable_mir/src/compiler_interface.rs

+10
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use std::cell::Cell;
77

88
use crate::abi::{FnAbi, Layout, LayoutShape};
9+
use crate::crate_def::Attribute;
910
use crate::mir::alloc::{AllocId, GlobalAlloc};
1011
use crate::mir::mono::{Instance, InstanceDef, StaticDef};
1112
use crate::mir::{BinOp, Body, Place, UnOp};
@@ -55,6 +56,15 @@ pub trait Context {
5556
/// Returns the name of given `DefId`
5657
fn def_name(&self, def_id: DefId, trimmed: bool) -> Symbol;
5758

59+
/// Return attributes with the given attribute name.
60+
///
61+
/// Single segmented name like `#[inline]` is specified as `&["inline".to_string()]`.
62+
/// Multi-segmented name like `#[rustfmt::skip]` is specified as `&["rustfmt".to_string(), "skip".to_string()]`.
63+
fn get_attrs_by_path(&self, def_id: DefId, attr: &[Symbol]) -> Vec<Attribute>;
64+
65+
/// Get all attributes of a definition.
66+
fn get_all_attrs(&self, def_id: DefId) -> Vec<Attribute>;
67+
5868
/// Returns printable, human readable form of `Span`
5969
fn span_to_string(&self, span: Span) -> String;
6070

compiler/stable_mir/src/crate_def.rs

+37
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,21 @@ pub trait CrateDef {
5050
let def_id = self.def_id();
5151
with(|cx| cx.span_of_an_item(def_id))
5252
}
53+
54+
/// Return attributes with the given attribute name.
55+
///
56+
/// Single segmented name like `#[inline]` is specified as `&["inline".to_string()]`.
57+
/// Multi-segmented name like `#[rustfmt::skip]` is specified as `&["rustfmt".to_string(), "skip".to_string()]`.
58+
fn attrs_by_path(&self, attr: &[Symbol]) -> Vec<Attribute> {
59+
let def_id = self.def_id();
60+
with(|cx| cx.get_attrs_by_path(def_id, attr))
61+
}
62+
63+
/// Return all attributes of this definition.
64+
fn all_attrs(&self) -> Vec<Attribute> {
65+
let def_id = self.def_id();
66+
with(|cx| cx.get_all_attrs(def_id))
67+
}
5368
}
5469

5570
/// A trait that can be used to retrieve a definition's type.
@@ -69,6 +84,28 @@ pub trait CrateDefType: CrateDef {
6984
}
7085
}
7186

87+
#[derive(Clone, Debug, PartialEq, Eq)]
88+
pub struct Attribute {
89+
value: String,
90+
span: Span,
91+
}
92+
93+
impl Attribute {
94+
pub fn new(value: String, span: Span) -> Attribute {
95+
Attribute { value, span }
96+
}
97+
98+
/// Get the span of this attribute.
99+
pub fn span(&self) -> Span {
100+
self.span
101+
}
102+
103+
/// Get the string representation of this attribute.
104+
pub fn as_str(&self) -> &str {
105+
&self.value
106+
}
107+
}
108+
72109
macro_rules! crate_def {
73110
( $(#[$attr:meta])*
74111
$vis:vis $name:ident $(;)?
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
//@ run-pass
2+
//! Test information regarding type layout.
3+
4+
//@ ignore-stage1
5+
//@ ignore-cross-compile
6+
//@ ignore-remote
7+
//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837
8+
9+
#![feature(rustc_private)]
10+
#![feature(control_flow_enum)]
11+
12+
extern crate rustc_hir;
13+
#[macro_use]
14+
extern crate rustc_smir;
15+
extern crate rustc_driver;
16+
extern crate rustc_interface;
17+
extern crate stable_mir;
18+
19+
use rustc_smir::rustc_internal;
20+
use stable_mir::{CrateDef, CrateItems};
21+
use std::io::Write;
22+
use std::ops::ControlFlow;
23+
24+
const CRATE_NAME: &str = "input";
25+
26+
/// This function uses the Stable MIR APIs to get information about the test crate.
27+
fn test_stable_mir() -> ControlFlow<()> {
28+
// Find items in the local crate.
29+
let items = stable_mir::all_local_items();
30+
31+
test_builtins(&items);
32+
test_derive(&items);
33+
test_tool(&items);
34+
test_all_attrs(&items);
35+
36+
ControlFlow::Continue(())
37+
}
38+
39+
// Test built-in attributes.
40+
fn test_builtins(items: &CrateItems) {
41+
let target_fn = *get_item(&items, "builtins_fn").unwrap();
42+
let allow_attrs = target_fn.attrs_by_path(&["allow".to_string()]);
43+
assert_eq!(allow_attrs[0].as_str(), "#![allow(unused_variables)]");
44+
45+
let inline_attrs = target_fn.attrs_by_path(&["inline".to_string()]);
46+
assert_eq!(inline_attrs[0].as_str(), "#[inline]");
47+
48+
let deprecated_attrs = target_fn.attrs_by_path(&["deprecated".to_string()]);
49+
assert_eq!(deprecated_attrs[0].as_str(), "#[deprecated(since = \"5.2.0\")]");
50+
}
51+
52+
// Test derive attribute.
53+
fn test_derive(items: &CrateItems) {
54+
let target_struct = *get_item(&items, "Foo").unwrap();
55+
let attrs = target_struct.attrs_by_path(&["derive".to_string()]);
56+
// No `derive` attribute since it's expanded before MIR.
57+
assert_eq!(attrs.len(), 0);
58+
59+
// Check derived trait method's attributes.
60+
let derived_fmt = *get_item(&items, "<Foo as std::fmt::Debug>::fmt").unwrap();
61+
// The Rust reference lies about this attribute. It doesn't show up in `clone` or `fmt` impl.
62+
let _fmt_attrs = derived_fmt.attrs_by_path(&["automatically_derived".to_string()]);
63+
}
64+
65+
// Test tool attributes.
66+
fn test_tool(items: &CrateItems) {
67+
let rustfmt_fn = *get_item(&items, "do_not_format").unwrap();
68+
let rustfmt_attrs = rustfmt_fn.attrs_by_path(&["rustfmt".to_string(), "skip".to_string()]);
69+
assert_eq!(rustfmt_attrs[0].as_str(), "#[rustfmt::skip]");
70+
71+
let clippy_fn = *get_item(&items, "complex_fn").unwrap();
72+
let clippy_attrs = clippy_fn.attrs_by_path(&["clippy".to_string(),
73+
"cyclomatic_complexity".to_string()]);
74+
assert_eq!(clippy_attrs[0].as_str(), "#[clippy::cyclomatic_complexity = \"100\"]");
75+
}
76+
77+
fn test_all_attrs(items: &CrateItems) {
78+
let target_fn = *get_item(&items, "many_attrs").unwrap();
79+
let all_attrs = target_fn.all_attrs();
80+
assert_eq!(all_attrs[0].as_str(), "#[inline]");
81+
assert_eq!(all_attrs[1].as_str(), "#[allow(unused_variables)]");
82+
assert_eq!(all_attrs[2].as_str(), "#[allow(dead_code)]");
83+
assert_eq!(all_attrs[3].as_str(), "#[allow(unused_imports)]");
84+
assert_eq!(all_attrs[4].as_str(), "#![allow(clippy::filter_map)]");
85+
}
86+
87+
88+
fn get_item<'a>(
89+
items: &'a CrateItems,
90+
name: &str,
91+
) -> Option<&'a stable_mir::CrateItem> {
92+
items.iter().find(|crate_item| crate_item.name() == name)
93+
}
94+
95+
/// This test will generate and analyze a dummy crate using the stable mir.
96+
/// For that, it will first write the dummy crate into a file.
97+
/// Then it will create a `StableMir` using custom arguments and then
98+
/// it will run the compiler.
99+
fn main() {
100+
let path = "attribute_input.rs";
101+
generate_input(&path).unwrap();
102+
let args = vec![
103+
"rustc".to_string(),
104+
"--crate-type=lib".to_string(),
105+
"--crate-name".to_string(),
106+
CRATE_NAME.to_string(),
107+
path.to_string(),
108+
];
109+
run!(args, test_stable_mir).unwrap();
110+
}
111+
112+
fn generate_input(path: &str) -> std::io::Result<()> {
113+
let mut file = std::fs::File::create(path)?;
114+
write!(
115+
file,
116+
r#"
117+
// General metadata applied to the enclosing module or crate.
118+
#![crate_type = "lib"]
119+
120+
// Mixed inner and outer attributes.
121+
#[inline]
122+
#[deprecated(since = "5.2.0")]
123+
fn builtins_fn() {{
124+
#![allow(unused_variables)]
125+
126+
let x = ();
127+
let y = ();
128+
let z = ();
129+
}}
130+
131+
// A derive attribute to automatically implement a trait.
132+
#[derive(Debug, Clone, Copy)]
133+
struct Foo(u32);
134+
135+
// A rustfmt tool attribute.
136+
#[rustfmt::skip]
137+
fn do_not_format() {{}}
138+
139+
// A clippy tool attribute.
140+
#[clippy::cyclomatic_complexity = "100"]
141+
pub fn complex_fn() {{}}
142+
143+
// A function with many attributes.
144+
#[inline]
145+
#[allow(unused_variables)]
146+
#[allow(dead_code)]
147+
#[allow(unused_imports)]
148+
fn many_attrs() {{
149+
#![allow(clippy::filter_map)]
150+
todo!()
151+
}}
152+
"#
153+
)?;
154+
Ok(())
155+
}

0 commit comments

Comments
 (0)