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