Skip to content

Commit 3f144fe

Browse files
committed
Remove #[main] attribute and add #[rustc_main] attribute.
1 parent 18c524f commit 3f144fe

File tree

61 files changed

+435
-457
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+435
-457
lines changed

compiler/rustc_ast/src/entry.rs

-7
This file was deleted.

compiler/rustc_ast/src/lib.rs

-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ pub mod util {
4343
pub mod ast;
4444
pub mod ast_like;
4545
pub mod attr;
46-
pub mod entry;
4746
pub mod expand;
4847
pub mod mut_visit;
4948
pub mod node_id;

compiler/rustc_ast_lowering/src/lib.rs

+76-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ use rustc_data_structures::fx::FxHashSet;
4848
use rustc_data_structures::sync::Lrc;
4949
use rustc_errors::struct_span_err;
5050
use rustc_hir as hir;
51-
use rustc_hir::def::{DefKind, Namespace, PartialRes, PerNS, Res};
51+
use rustc_hir::def::{DefKind, EntryFn, Namespace, PartialRes, PerNS, Res};
5252
use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId, CRATE_DEF_ID};
5353
use rustc_hir::definitions::{DefKey, DefPathData, Definitions};
5454
use rustc_hir::intravisit;
@@ -171,6 +171,8 @@ struct LoweringContext<'a, 'hir: 'a> {
171171

172172
allow_try_trait: Option<Lrc<[Symbol]>>,
173173
allow_gen_future: Option<Lrc<[Symbol]>>,
174+
175+
entry_fn_attr: Option<EntryFn>,
174176
}
175177

176178
pub trait ResolverAstLowering {
@@ -211,6 +213,12 @@ pub trait ResolverAstLowering {
211213
expn_id: ExpnId,
212214
span: Span,
213215
) -> LocalDefId;
216+
217+
fn resolve_ast_path_in_value_ns(
218+
&mut self,
219+
path: &Path,
220+
module_id: DefId,
221+
) -> Option<Res<NodeId>>;
214222
}
215223

216224
type NtToTokenstream = fn(&Nonterminal, &ParseSess, CanSynthesizeMissingTokens) -> TokenStream;
@@ -334,6 +342,7 @@ pub fn lower_crate<'a, 'hir>(
334342
in_scope_lifetimes: Vec::new(),
335343
allow_try_trait: Some([sym::try_trait][..].into()),
336344
allow_gen_future: Some([sym::gen_future][..].into()),
345+
entry_fn_attr: None,
337346
}
338347
.lower_crate(krate)
339348
}
@@ -547,6 +556,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
547556
})
548557
.collect();
549558

559+
self.lower_entry_fn(&c.attrs);
560+
550561
let mut def_id_to_hir_id = IndexVec::default();
551562

552563
for (node_id, hir_id) in self.node_id_to_hir_id.into_iter_enumerated() {
@@ -583,6 +594,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
583594
proc_macros,
584595
trait_map,
585596
attrs: self.attrs,
597+
entry_fn_attr: self.entry_fn_attr,
586598
}
587599
}
588600

@@ -2468,6 +2480,69 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
24682480
}
24692481
}
24702482

2483+
fn lower_entry_fn(&mut self, attrs: &[Attribute]) {
2484+
let sess = self.sess;
2485+
let resolver = &mut *self.resolver;
2486+
let entry_fn = &mut self.entry_fn_attr;
2487+
2488+
fn copy_path_to_use_node_id(path: &Path, node_id: NodeId) -> Path {
2489+
let mut path = path.clone();
2490+
path.segments.iter_mut().for_each(|seg| {
2491+
seg.id = node_id;
2492+
});
2493+
path
2494+
}
2495+
2496+
let mut ignored_entry_fns = vec![];
2497+
for rustc_main_attr in attrs.iter().filter(|attr| attr.has_name(sym::rustc_main)) {
2498+
let attr_meta_item = rustc_main_attr.meta_item_list();
2499+
let sp = rustc_main_attr.span;
2500+
let mut is_ignored = false;
2501+
let (path, is_naked) = match attr_meta_item.as_ref().map(Vec::as_slice) {
2502+
Some([NestedMetaItem::MetaItem(MetaItem { path, .. })]) => (path, false),
2503+
Some([NestedMetaItem::MetaItem(MetaItem { path, .. }), naked])
2504+
if naked.has_name(sym::naked) =>
2505+
{
2506+
(path, true)
2507+
}
2508+
Some([NestedMetaItem::MetaItem(MetaItem { path, .. }), ignore])
2509+
if ignore.has_name(sym::ignore) =>
2510+
{
2511+
is_ignored = true;
2512+
(path, false)
2513+
}
2514+
_ => {
2515+
sess.struct_span_err(sp, "incorrect #[rustc_main] attribute format").emit();
2516+
continue;
2517+
}
2518+
};
2519+
let entry_fn_node_id = resolver.next_node_id();
2520+
let entry_fn_node_path = copy_path_to_use_node_id(path, entry_fn_node_id);
2521+
2522+
let local_def_id = match resolver
2523+
.resolve_ast_path_in_value_ns(&entry_fn_node_path, CRATE_DEF_ID.to_def_id())
2524+
{
2525+
Some(Res::Def(DefKind::Fn, def_id)) => def_id.as_local(),
2526+
_ => None,
2527+
};
2528+
2529+
if is_ignored {
2530+
ignored_entry_fns.extend(local_def_id);
2531+
continue;
2532+
}
2533+
2534+
if let Some(local_def_id) = local_def_id {
2535+
*entry_fn = Some(EntryFn { local_def_id, is_naked, ignored_local_def_ids: vec![] })
2536+
} else {
2537+
sess.struct_span_err(sp, "#[rustc_main] resolution failure").emit();
2538+
continue;
2539+
}
2540+
}
2541+
if let Some(entry_fn) = entry_fn {
2542+
entry_fn.ignored_local_def_ids.extend(ignored_entry_fns.into_iter());
2543+
}
2544+
}
2545+
24712546
// Helper methods for building HIR.
24722547

24732548
fn stmt(&mut self, span: Span, kind: hir::StmtKind<'hir>) -> hir::Stmt<'hir> {

compiler/rustc_ast_passes/src/feature_gate.rs

-10
Original file line numberDiff line numberDiff line change
@@ -366,16 +366,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
366366
over time"
367367
);
368368
}
369-
if self.sess.contains_name(&i.attrs[..], sym::main) {
370-
gate_feature_post!(
371-
&self,
372-
main,
373-
i.span,
374-
"declaration of a non-standard `#[main]` \
375-
function may change over time, for now \
376-
a top-level `fn main()` is required"
377-
);
378-
}
379369
}
380370

381371
ast::ItemKind::Struct(..) => {

compiler/rustc_builtin_macros/src/test_harness.rs

+75-69
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
// Code that generates a test runner to run all the tests in a crate
22

33
use rustc_ast as ast;
4-
use rustc_ast::entry::EntryPointType;
54
use rustc_ast::mut_visit::{ExpectOne, *};
65
use rustc_ast::ptr::P;
76
use rustc_ast::{attr, ModKind};
87
use rustc_expand::base::{ExtCtxt, ResolverExpand};
98
use rustc_expand::expand::{AstFragment, ExpansionConfig};
109
use rustc_feature::Features;
10+
use rustc_session::config::CrateType;
1111
use rustc_session::Session;
1212
use rustc_span::hygiene::{AstPass, SyntaxContext, Transparency};
1313
use rustc_span::symbol::{sym, Ident, Symbol};
@@ -88,8 +88,9 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> {
8888
fn visit_crate(&mut self, c: &mut ast::Crate) {
8989
noop_visit_crate(c, self);
9090

91-
// Create a main function to run our tests
92-
c.items.push(mk_main(&mut self.cx));
91+
ensure_exist_rustc_main_attr(&mut self.cx, &mut c.attrs);
92+
// Create an entry function to run our tests
93+
mk_entry_fn(&mut self.cx, c);
9394
}
9495

9596
fn flat_map_item(&mut self, i: P<ast::Item>) -> SmallVec<[P<ast::Item>; 1]> {
@@ -124,7 +125,7 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> {
124125
Some(parent),
125126
);
126127
for test in &mut tests {
127-
// See the comment on `mk_main` for why we're using
128+
// See the comment on `mk_entry_fn` for why we're using
128129
// `apply_mark` directly.
129130
test.ident.span = test.ident.span.apply_mark(expn_id, Transparency::Opaque);
130131
}
@@ -135,28 +136,32 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> {
135136
}
136137
}
137138

138-
// Beware, this is duplicated in librustc_passes/entry.rs (with
139-
// `rustc_hir::Item`), so make sure to keep them in sync.
140-
fn entry_point_type(sess: &Session, item: &ast::Item, depth: usize) -> EntryPointType {
141-
match item.kind {
142-
ast::ItemKind::Fn(..) => {
143-
if sess.contains_name(&item.attrs, sym::start) {
144-
EntryPointType::Start
145-
} else if sess.contains_name(&item.attrs, sym::main) {
146-
EntryPointType::MainAttr
147-
} else if item.ident.name == sym::main {
148-
if depth == 1 {
149-
// This is a top-level function so can be 'main'
150-
EntryPointType::MainNamed
151-
} else {
152-
EntryPointType::OtherMain
153-
}
154-
} else {
155-
EntryPointType::None
156-
}
157-
}
158-
_ => EntryPointType::None,
139+
/// Remove any #[start] from the AST so it doesn't
140+
/// clash with the one we're going to add, but mark it as
141+
/// #[allow(dead_code)] to avoid printing warnings.
142+
fn strip_start_attr(sess: &Session, def_site: Span, item: P<ast::Item>) -> P<ast::Item> {
143+
if !matches!(item.kind, ast::ItemKind::Fn(..)) {
144+
return item;
159145
}
146+
if !sess.contains_name(&item.attrs, sym::start) {
147+
return item;
148+
}
149+
150+
item.map(|item| {
151+
let ast::Item { id, ident, attrs, kind, vis, span, tokens } = item;
152+
153+
let allow_ident = Ident::new(sym::allow, def_site);
154+
let dc_nested = attr::mk_nested_word_item(Ident::new(sym::dead_code, def_site));
155+
let allow_dead_code_item = attr::mk_list_item(allow_ident, vec![dc_nested]);
156+
let allow_dead_code = attr::mk_attr_outer(allow_dead_code_item);
157+
let attrs = attrs
158+
.into_iter()
159+
.filter(|attr| !sess.check_name(attr, sym::start))
160+
.chain(iter::once(allow_dead_code))
161+
.collect();
162+
163+
ast::Item { id, ident, attrs, kind, vis, span, tokens }
164+
})
160165
}
161166
/// A folder used to remove any entry points (like fn main) because the harness
162167
/// generator will provide its own
@@ -172,33 +177,7 @@ impl<'a> MutVisitor for EntryPointCleaner<'a> {
172177
self.depth += 1;
173178
let item = noop_flat_map_item(i, self).expect_one("noop did something");
174179
self.depth -= 1;
175-
176-
// Remove any #[main] or #[start] from the AST so it doesn't
177-
// clash with the one we're going to add, but mark it as
178-
// #[allow(dead_code)] to avoid printing warnings.
179-
let item = match entry_point_type(self.sess, &item, self.depth) {
180-
EntryPointType::MainNamed | EntryPointType::MainAttr | EntryPointType::Start => item
181-
.map(|ast::Item { id, ident, attrs, kind, vis, span, tokens }| {
182-
let allow_ident = Ident::new(sym::allow, self.def_site);
183-
let dc_nested =
184-
attr::mk_nested_word_item(Ident::new(sym::dead_code, self.def_site));
185-
let allow_dead_code_item = attr::mk_list_item(allow_ident, vec![dc_nested]);
186-
let allow_dead_code = attr::mk_attr_outer(allow_dead_code_item);
187-
let attrs = attrs
188-
.into_iter()
189-
.filter(|attr| {
190-
!self.sess.check_name(attr, sym::main)
191-
&& !self.sess.check_name(attr, sym::start)
192-
})
193-
.chain(iter::once(allow_dead_code))
194-
.collect();
195-
196-
ast::Item { id, ident, attrs, kind, vis, span, tokens }
197-
}),
198-
EntryPointType::None | EntryPointType::OtherMain => item,
199-
};
200-
201-
smallvec![item]
180+
smallvec![strip_start_attr(self.sess, self.def_site, item)]
202181
}
203182
}
204183

@@ -220,7 +199,7 @@ fn generate_test_harness(
220199
let expn_id = ext_cx.resolver.expansion_for_ast_pass(
221200
DUMMY_SP,
222201
AstPass::TestHarness,
223-
&[sym::main, sym::test, sym::rustc_attrs],
202+
&[sym::rustc_main, sym::test, sym::rustc_attrs],
224203
None,
225204
);
226205
let def_site = DUMMY_SP.with_def_site_ctxt(expn_id);
@@ -246,8 +225,8 @@ fn generate_test_harness(
246225
///
247226
/// By default this expands to
248227
///
249-
/// ```
250-
/// #[main]
228+
/// ```rust,ignore (not real code)
229+
/// #![rustc_main(crate::...::main)]
251230
/// pub fn main() {
252231
/// extern crate test;
253232
/// test::test_main_static(&[
@@ -271,9 +250,9 @@ fn generate_test_harness(
271250
/// [`TestCtxt::reexport_test_harness_main`] provides a different name for the `main`
272251
/// function and [`TestCtxt::test_runner`] provides a path that replaces
273252
/// `test::test_main_static`.
274-
fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
253+
fn mk_entry_fn(cx: &mut TestCtxt<'_>, c: &mut ast::Crate) {
275254
let sp = cx.def_site;
276-
let ecx = &cx.ext_cx;
255+
let ecx = &mut cx.ext_cx;
277256
let test_id = Ident::new(sym::test, sp);
278257

279258
let runner_name = match cx.panic_strategy {
@@ -290,17 +269,14 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
290269
test_runner.span = sp;
291270

292271
let test_main_path_expr = ecx.expr_path(test_runner);
293-
let call_test_main = ecx.expr_call(sp, test_main_path_expr, vec![mk_tests_slice(cx, sp)]);
272+
let call_test_main =
273+
ecx.expr_call(sp, test_main_path_expr, vec![mk_tests_slice(ecx, &cx.test_cases, sp)]);
294274
let call_test_main = ecx.stmt_expr(call_test_main);
295275

296276
// extern crate test
297277
let test_extern_stmt =
298278
ecx.stmt_item(sp, ecx.item(sp, test_id, vec![], ast::ItemKind::ExternCrate(None)));
299279

300-
// #[main]
301-
let main_meta = ecx.meta_word(sp, sym::main);
302-
let main_attr = ecx.attribute(main_meta);
303-
304280
// pub fn main() { ... }
305281
let main_ret_ty = ecx.ty(sp, ast::TyKind::Tup(vec![]));
306282

@@ -325,7 +301,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
325301

326302
let main = P(ast::Item {
327303
ident: main_id,
328-
attrs: vec![main_attr],
304+
attrs: vec![],
329305
id: ast::DUMMY_NODE_ID,
330306
kind: main,
331307
vis: ast::Visibility { span: sp, kind: ast::VisibilityKind::Public, tokens: None },
@@ -335,18 +311,48 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
335311

336312
// Integrate the new item into existing module structures.
337313
let main = AstFragment::Items(smallvec![main]);
338-
cx.ext_cx.monotonic_expander().fully_expand_fragment(main).make_items().pop().unwrap()
314+
let main = ecx.monotonic_expander().fully_expand_fragment(main).make_items().pop().unwrap();
315+
316+
// #[rustc_main] attr
317+
let main_id_nested_meta = ast::attr::mk_nested_word_item(main_id);
318+
let rustc_main_meta = ecx.meta_list(sp, sym::rustc_main, vec![main_id_nested_meta]);
319+
let rustc_main_attr = ecx.attribute(rustc_main_meta);
320+
c.attrs.push(rustc_main_attr);
321+
c.items.push(main);
339322
}
340323

341-
/// Creates a slice containing every test like so:
342-
/// &[&test1, &test2]
343-
fn mk_tests_slice(cx: &TestCtxt<'_>, sp: Span) -> P<ast::Expr> {
344-
debug!("building test vector from {} tests", cx.test_cases.len());
324+
fn ensure_exist_rustc_main_attr(cx: &mut TestCtxt<'_>, attrs: &mut Vec<ast::Attribute>) {
325+
let sp = cx.def_site;
345326
let ecx = &cx.ext_cx;
346327

328+
if ecx.sess.contains_name(attrs, sym::rustc_main) {
329+
return;
330+
}
331+
332+
let any_exe = ecx.sess.crate_types().iter().any(|ty| *ty == CrateType::Executable);
333+
if !any_exe {
334+
return;
335+
}
336+
337+
if ecx.sess.contains_name(attrs, sym::no_main) {
338+
return;
339+
}
340+
341+
let crate_main_nested_meta = ast::attr::mk_nested_word_item(Ident::new(sym::main, DUMMY_SP));
342+
let ignore_nested_meta = ast::attr::mk_nested_word_item(Ident::new(sym::ignore, DUMMY_SP));
343+
let rustc_main_meta =
344+
ecx.meta_list(sp, sym::rustc_main, vec![crate_main_nested_meta, ignore_nested_meta]);
345+
let rustc_main_attr = ecx.attribute(rustc_main_meta);
346+
attrs.push(rustc_main_attr);
347+
}
348+
349+
/// Creates a slice containing every test like so:
350+
/// &[&test1, &test2]
351+
fn mk_tests_slice(ecx: &ExtCtxt<'_>, test_cases: &Vec<Test>, sp: Span) -> P<ast::Expr> {
352+
debug!("building test vector from {} tests", test_cases.len());
347353
ecx.expr_vec_slice(
348354
sp,
349-
cx.test_cases
355+
test_cases
350356
.iter()
351357
.map(|test| {
352358
ecx.expr_addr_of(test.span, ecx.expr_path(ecx.path(test.span, vec![test.ident])))

0 commit comments

Comments
 (0)