diff options
Diffstat (limited to 'compiler/rustc_passes/src/entry.rs')
| -rw-r--r-- | compiler/rustc_passes/src/entry.rs | 204 |
1 files changed, 204 insertions, 0 deletions
diff --git a/compiler/rustc_passes/src/entry.rs b/compiler/rustc_passes/src/entry.rs new file mode 100644 index 00000000000..51a64b3855f --- /dev/null +++ b/compiler/rustc_passes/src/entry.rs @@ -0,0 +1,204 @@ +use rustc_ast::attr; +use rustc_ast::entry::EntryPointType; +use rustc_errors::error_code; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; +use rustc_hir::{ItemId, Node, CRATE_HIR_ID}; +use rustc_middle::query::Providers; +use rustc_middle::ty::TyCtxt; +use rustc_session::config::{sigpipe, CrateType, EntryFnType}; +use rustc_session::parse::feature_err; +use rustc_span::symbol::sym; +use rustc_span::{Span, Symbol}; + +use crate::errors::{ + AttrOnlyInFunctions, AttrOnlyOnMain, AttrOnlyOnRootMain, ExternMain, MultipleRustcMain, + MultipleStartFunctions, NoMainErr, UnixSigpipeValues, +}; + +struct EntryContext<'tcx> { + tcx: TyCtxt<'tcx>, + + /// The function that has attribute named `main`. + attr_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, attr_main_fn: None, start_fn: None, non_main_fns: Vec::new() }; + + for id in tcx.hir().items() { + find_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 find_item(id: ItemId, ctxt: &mut EntryContext<'_>) { + 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 => { + if let Some(span) = attr_span_by_symbol(ctxt, id, sym::unix_sigpipe) { + ctxt.tcx.sess.emit_err(AttrOnlyOnMain { span, attr: sym::unix_sigpipe }); + } + } + _ 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.sess.emit_err(AttrOnlyInFunctions { span, attr }); + } + } + } + EntryPointType::MainNamed => (), + EntryPointType::OtherMain => { + if let Some(span) = attr_span_by_symbol(ctxt, id, sym::unix_sigpipe) { + ctxt.tcx.sess.emit_err(AttrOnlyOnRootMain { span, attr: sym::unix_sigpipe }); + } + ctxt.non_main_fns.push(ctxt.tcx.def_span(id.owner_id)); + } + EntryPointType::RustcMainAttr => { + if ctxt.attr_main_fn.is_none() { + ctxt.attr_main_fn = Some((id.owner_id.def_id, ctxt.tcx.def_span(id.owner_id))); + } else { + ctxt.tcx.sess.emit_err(MultipleRustcMain { + span: ctxt.tcx.def_span(id.owner_id.to_def_id()), + first: ctxt.attr_main_fn.unwrap().1, + additional: ctxt.tcx.def_span(id.owner_id.to_def_id()), + }); + } + } + EntryPointType::Start => { + if let Some(span) = attr_span_by_symbol(ctxt, id, sym::unix_sigpipe) { + ctxt.tcx.sess.emit_err(AttrOnlyOnMain { span, attr: sym::unix_sigpipe }); + } + 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.sess.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.attr_main_fn { + let def_id = local_def_id.to_def_id(); + Some((def_id, EntryFnType::Main { sigpipe: sigpipe(tcx, def_id) })) + } else { + 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().find_by_def_id(def_id), Some(Node::ForeignItem(_))) + { + tcx.sess.emit_err(ExternMain { span: tcx.def_span(def_id) }); + return None; + } + + if main_def.is_import && !tcx.features().imported_main { + let span = main_def.span; + feature_err( + &tcx.sess.parse_sess, + sym::imported_main, + span, + "using an imported function as entry point `main` is experimental", + ) + .emit(); + } + return Some((def_id, EntryFnType::Main { sigpipe: sigpipe(tcx, def_id) })); + } + no_main_err(tcx, visitor); + None + } +} + +fn sigpipe(tcx: TyCtxt<'_>, def_id: DefId) -> u8 { + if let Some(attr) = tcx.get_attr(def_id, sym::unix_sigpipe) { + match (attr.value_str(), attr.meta_item_list()) { + (Some(sym::inherit), None) => sigpipe::INHERIT, + (Some(sym::sig_ign), None) => sigpipe::SIG_IGN, + (Some(sym::sig_dfl), None) => sigpipe::SIG_DFL, + (_, Some(_)) => { + // Keep going so that `fn emit_malformed_attribute()` can print + // an excellent error message + sigpipe::DEFAULT + } + _ => { + tcx.sess.emit_err(UnixSigpipeValues { span: attr.span }); + sigpipe::DEFAULT + } + } + } else { + sigpipe::DEFAULT + } +} + +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().unwrap_or_else(|| { + has_filename = false; + Default::default() + }); + let main_def_opt = tcx.resolutions(()).main_def; + let diagnostic_id = error_code!(E0601); + let add_teach_note = tcx.sess.teach(&diagnostic_id); + // 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.sess.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 }; +} |
