rustc_passes/
entry.rs

1use rustc_ast::attr;
2use rustc_ast::entry::EntryPointType;
3use rustc_errors::codes::*;
4use rustc_hir::attrs::AttributeKind;
5use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LOCAL_CRATE, LocalDefId};
6use rustc_hir::{CRATE_HIR_ID, ItemId, Node, find_attr};
7use rustc_middle::query::Providers;
8use rustc_middle::ty::TyCtxt;
9use rustc_session::config::{CrateType, EntryFnType, sigpipe};
10use rustc_span::{RemapPathScopeComponents, Span, sym};
11
12use crate::errors::{ExternMain, MultipleRustcMain, NoMainErr};
13
14struct EntryContext<'tcx> {
15    tcx: TyCtxt<'tcx>,
16
17    /// The function has the `#[rustc_main]` attribute.
18    rustc_main_fn: Option<(LocalDefId, Span)>,
19
20    /// The functions that one might think are `main` but aren't, e.g.
21    /// main functions not defined at the top level. For diagnostics.
22    non_main_fns: Vec<Span>,
23}
24
25fn entry_fn(tcx: TyCtxt<'_>, (): ()) -> Option<(DefId, EntryFnType)> {
26    let any_exe = tcx.crate_types().contains(&CrateType::Executable);
27    if !any_exe {
28        // No need to find a main function.
29        return None;
30    }
31
32    // If the user wants no main function at all, then stop here.
33    if attr::contains_name(tcx.hir_attrs(CRATE_HIR_ID), sym::no_main) {
34        return None;
35    }
36
37    let mut ctxt = EntryContext { tcx, rustc_main_fn: None, non_main_fns: Vec::new() };
38
39    for id in tcx.hir_free_items() {
40        check_and_search_item(id, &mut ctxt);
41    }
42
43    configure_main(tcx, &ctxt)
44}
45
46fn check_and_search_item(id: ItemId, ctxt: &mut EntryContext<'_>) {
47    let at_root = ctxt.tcx.opt_local_parent(id.owner_id.def_id) == Some(CRATE_DEF_ID);
48
49    let attrs = ctxt.tcx.hir_attrs(id.hir_id());
50    let entry_point_type = rustc_ast::entry::entry_point_type(
51        find_attr!(attrs, AttributeKind::RustcMain),
52        at_root,
53        ctxt.tcx.opt_item_name(id.owner_id.to_def_id()),
54    );
55
56    match entry_point_type {
57        EntryPointType::None => {}
58        EntryPointType::MainNamed => {}
59        EntryPointType::OtherMain => {
60            ctxt.non_main_fns.push(ctxt.tcx.def_span(id.owner_id));
61        }
62        EntryPointType::RustcMainAttr => {
63            if ctxt.rustc_main_fn.is_none() {
64                ctxt.rustc_main_fn = Some((id.owner_id.def_id, ctxt.tcx.def_span(id.owner_id)));
65            } else {
66                ctxt.tcx.dcx().emit_err(MultipleRustcMain {
67                    span: ctxt.tcx.def_span(id.owner_id.to_def_id()),
68                    first: ctxt.rustc_main_fn.unwrap().1,
69                    additional: ctxt.tcx.def_span(id.owner_id.to_def_id()),
70                });
71            }
72        }
73    }
74}
75
76fn configure_main(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) -> Option<(DefId, EntryFnType)> {
77    if let Some((local_def_id, _)) = visitor.rustc_main_fn {
78        let def_id = local_def_id.to_def_id();
79        Some((def_id, EntryFnType::Main { sigpipe: sigpipe(tcx) }))
80    } else {
81        // The actual resolution of main happens in the resolver, this here
82        if let Some(main_def) = tcx.resolutions(()).main_def
83            && let Some(def_id) = main_def.opt_fn_def_id()
84        {
85            // non-local main imports are handled below
86            if let Some(def_id) = def_id.as_local()
87                && matches!(tcx.hir_node_by_def_id(def_id), Node::ForeignItem(_))
88            {
89                tcx.dcx().emit_err(ExternMain { span: tcx.def_span(def_id) });
90                return None;
91            }
92
93            return Some((def_id, EntryFnType::Main { sigpipe: sigpipe(tcx) }));
94        }
95        no_main_err(tcx, visitor);
96        None
97    }
98}
99
100fn sigpipe(tcx: TyCtxt<'_>) -> u8 {
101    match tcx.sess.opts.unstable_opts.on_broken_pipe {
102        rustc_target::spec::OnBrokenPipe::Default => sigpipe::DEFAULT,
103        rustc_target::spec::OnBrokenPipe::Kill => sigpipe::SIG_DFL,
104        rustc_target::spec::OnBrokenPipe::Error => sigpipe::SIG_IGN,
105        rustc_target::spec::OnBrokenPipe::Inherit => sigpipe::INHERIT,
106    }
107}
108
109fn no_main_err(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) {
110    let sp = tcx.def_span(CRATE_DEF_ID);
111
112    // There is no main function.
113    let mut has_filename = true;
114    let filename = tcx
115        .sess
116        .local_crate_source_file()
117        .map(|src| src.path(RemapPathScopeComponents::DIAGNOSTICS).to_path_buf())
118        .unwrap_or_else(|| {
119            has_filename = false;
120            Default::default()
121        });
122    let main_def_opt = tcx.resolutions(()).main_def;
123    let code = E0601;
124    let add_teach_note = tcx.sess.teach(code);
125    // The file may be empty, which leads to the diagnostic machinery not emitting this
126    // note. This is a relatively simple way to detect that case and emit a span-less
127    // note instead.
128    let file_empty = tcx.sess.source_map().lookup_line(sp.hi()).is_err();
129
130    tcx.dcx().emit_err(NoMainErr {
131        sp,
132        crate_name: tcx.crate_name(LOCAL_CRATE),
133        has_filename,
134        filename,
135        file_empty,
136        non_main_fns: visitor.non_main_fns.clone(),
137        main_def_opt,
138        add_teach_note,
139    });
140}
141
142pub fn provide(providers: &mut Providers) {
143    *providers = Providers { entry_fn, ..*providers };
144}