Rollup merge of #124584 - Nilstrieb:entrypointy, r=fee1-dead

Various improvements to entrypoint code

This moves some code around and adds some documentation comments to make it easier to understand what's going on with the entrypoint logic, which is a bit complicated.

The only change in behavior is consolidating the error messages for unix_sigpipe to make the code slightly simpler.
This commit is contained in:
Matthias Krüger 2024-05-04 18:36:37 +02:00 committed by GitHub
commit 9af1e8ce42
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 49 additions and 43 deletions

View File

@ -4,11 +4,35 @@
#[derive(Debug)] #[derive(Debug)]
pub enum EntryPointType { pub enum EntryPointType {
/// This function is not an entrypoint.
None, None,
/// This is a function called `main` at the root level.
/// ```
/// fn main() {}
/// ```
MainNamed, MainNamed,
/// This is a function with the `#[rustc_main]` attribute.
/// Used by the testing harness to create the test entrypoint.
/// ```ignore (clashes with test entrypoint)
/// #[rustc_main]
/// fn main() {}
/// ```
RustcMainAttr, RustcMainAttr,
/// This is a function with the `#[start]` attribute.
/// ```ignore (clashes with test entrypoint)
/// #[start]
/// fn main() {}
/// ```
Start, Start,
OtherMain, // Not an entry point, but some other function named main /// This function is **not** an entrypoint but simply named `main` (not at the root).
/// This is only used for diagnostics.
/// ```
/// #[allow(dead_code)]
/// mod meow {
/// fn main() {}
/// }
/// ```
OtherMain,
} }
pub fn entry_point_type( pub fn entry_point_type(

View File

@ -266,7 +266,7 @@ fn generate_test_harness(
/// ///
/// By default this expands to /// By default this expands to
/// ///
/// ```ignore UNSOLVED (I think I still need guidance for this one. Is it correct? Do we try to make it run? How do we nicely fill it out?) /// ```ignore (messes with test internals)
/// #[rustc_main] /// #[rustc_main]
/// pub fn main() { /// pub fn main() {
/// extern crate test; /// extern crate test;

View File

@ -49,12 +49,6 @@ passes_attr_crate_level =
passes_attr_only_in_functions = passes_attr_only_in_functions =
`{$attr}` attribute can only be used on functions `{$attr}` attribute can only be used on functions
passes_attr_only_on_main =
`{$attr}` attribute can only be used on `fn main()`
passes_attr_only_on_root_main =
`{$attr}` attribute can only be used on root `fn main()`
passes_both_ffi_const_and_pure = passes_both_ffi_const_and_pure =
`#[ffi_const]` function cannot be `#[ffi_pure]` `#[ffi_const]` function cannot be `#[ffi_pure]`

View File

@ -18,10 +18,10 @@
struct EntryContext<'tcx> { struct EntryContext<'tcx> {
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
/// The function that has attribute named `main`. /// The function has the `#[rustc_main]` attribute.
attr_main_fn: Option<(LocalDefId, Span)>, rustc_main_fn: Option<(LocalDefId, Span)>,
/// The function that has the attribute 'start' on it. /// The function that has the attribute `#[start]` on it.
start_fn: Option<(LocalDefId, Span)>, start_fn: Option<(LocalDefId, Span)>,
/// The functions that one might think are `main` but aren't, e.g. /// The functions that one might think are `main` but aren't, e.g.
@ -42,10 +42,10 @@ fn entry_fn(tcx: TyCtxt<'_>, (): ()) -> Option<(DefId, EntryFnType)> {
} }
let mut ctxt = let mut ctxt =
EntryContext { tcx, attr_main_fn: None, start_fn: None, non_main_fns: Vec::new() }; EntryContext { tcx, rustc_main_fn: None, start_fn: None, non_main_fns: Vec::new() };
for id in tcx.hir().items() { for id in tcx.hir().items() {
find_item(id, &mut ctxt); check_and_search_item(id, &mut ctxt);
} }
configure_main(tcx, &ctxt) configure_main(tcx, &ctxt)
@ -56,7 +56,16 @@ fn attr_span_by_symbol(ctxt: &EntryContext<'_>, id: ItemId, sym: Symbol) -> Opti
attr::find_by_name(attrs, sym).map(|attr| attr.span) attr::find_by_name(attrs, sym).map(|attr| attr.span)
} }
fn find_item(id: ItemId, ctxt: &mut EntryContext<'_>) { fn check_and_search_item(id: ItemId, ctxt: &mut EntryContext<'_>) {
if !matches!(ctxt.tcx.def_kind(id.owner_id), DefKind::Fn) {
for attr in [sym::start, sym::rustc_main] {
if let Some(span) = attr_span_by_symbol(ctxt, id, attr) {
ctxt.tcx.dcx().emit_err(AttrOnlyInFunctions { span, attr });
}
}
return;
}
let at_root = ctxt.tcx.opt_local_parent(id.owner_id.def_id) == Some(CRATE_DEF_ID); let at_root = ctxt.tcx.opt_local_parent(id.owner_id.def_id) == Some(CRATE_DEF_ID);
let attrs = ctxt.tcx.hir().attrs(id.hir_id()); let attrs = ctxt.tcx.hir().attrs(id.hir_id());
@ -65,26 +74,20 @@ fn find_item(id: ItemId, ctxt: &mut EntryContext<'_>) {
at_root, at_root,
ctxt.tcx.opt_item_name(id.owner_id.to_def_id()), ctxt.tcx.opt_item_name(id.owner_id.to_def_id()),
); );
match entry_point_type { match entry_point_type {
EntryPointType::None => (), EntryPointType::None => {}
_ if !matches!(ctxt.tcx.def_kind(id.owner_id), DefKind::Fn) => { EntryPointType::MainNamed => {}
for attr in [sym::start, sym::rustc_main] {
if let Some(span) = attr_span_by_symbol(ctxt, id, attr) {
ctxt.tcx.dcx().emit_err(AttrOnlyInFunctions { span, attr });
}
}
}
EntryPointType::MainNamed => (),
EntryPointType::OtherMain => { EntryPointType::OtherMain => {
ctxt.non_main_fns.push(ctxt.tcx.def_span(id.owner_id)); ctxt.non_main_fns.push(ctxt.tcx.def_span(id.owner_id));
} }
EntryPointType::RustcMainAttr => { EntryPointType::RustcMainAttr => {
if ctxt.attr_main_fn.is_none() { if ctxt.rustc_main_fn.is_none() {
ctxt.attr_main_fn = Some((id.owner_id.def_id, ctxt.tcx.def_span(id.owner_id))); ctxt.rustc_main_fn = Some((id.owner_id.def_id, ctxt.tcx.def_span(id.owner_id)));
} else { } else {
ctxt.tcx.dcx().emit_err(MultipleRustcMain { ctxt.tcx.dcx().emit_err(MultipleRustcMain {
span: ctxt.tcx.def_span(id.owner_id.to_def_id()), span: ctxt.tcx.def_span(id.owner_id.to_def_id()),
first: ctxt.attr_main_fn.unwrap().1, first: ctxt.rustc_main_fn.unwrap().1,
additional: ctxt.tcx.def_span(id.owner_id.to_def_id()), additional: ctxt.tcx.def_span(id.owner_id.to_def_id()),
}); });
} }
@ -107,10 +110,11 @@ fn find_item(id: ItemId, ctxt: &mut EntryContext<'_>) {
fn configure_main(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) -> Option<(DefId, EntryFnType)> { fn configure_main(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) -> Option<(DefId, EntryFnType)> {
if let Some((def_id, _)) = visitor.start_fn { if let Some((def_id, _)) = visitor.start_fn {
Some((def_id.to_def_id(), EntryFnType::Start)) Some((def_id.to_def_id(), EntryFnType::Start))
} else if let Some((local_def_id, _)) = visitor.attr_main_fn { } else if let Some((local_def_id, _)) = visitor.rustc_main_fn {
let def_id = local_def_id.to_def_id(); let def_id = local_def_id.to_def_id();
Some((def_id, EntryFnType::Main { sigpipe: sigpipe(tcx) })) Some((def_id, EntryFnType::Main { sigpipe: sigpipe(tcx) }))
} else { } else {
// The actual resolution of main happens in the resolver, this here
if let Some(main_def) = tcx.resolutions(()).main_def if let Some(main_def) = tcx.resolutions(()).main_def
&& let Some(def_id) = main_def.opt_fn_def_id() && let Some(def_id) = main_def.opt_fn_def_id()
{ {

View File

@ -1206,22 +1206,6 @@ pub struct NakedFunctionsMustUseNoreturn {
pub last_span: Span, pub last_span: Span,
} }
#[derive(Diagnostic)]
#[diag(passes_attr_only_on_main)]
pub struct AttrOnlyOnMain {
#[primary_span]
pub span: Span,
pub attr: Symbol,
}
#[derive(Diagnostic)]
#[diag(passes_attr_only_on_root_main)]
pub struct AttrOnlyOnRootMain {
#[primary_span]
pub span: Span,
pub attr: Symbol,
}
#[derive(Diagnostic)] #[derive(Diagnostic)]
#[diag(passes_attr_only_in_functions)] #[diag(passes_attr_only_in_functions)]
pub struct AttrOnlyInFunctions { pub struct AttrOnlyInFunctions {