Skip to main content

rustc_passes/
entry.rs

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