Skip to main content

rustc_passes/
entry.rs

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