11use rustc_ast:: entry:: EntryPointType ;
2- use rustc_errors:: struct_span_err ;
2+ use rustc_errors:: error_code ;
33use rustc_hir:: def:: DefKind ;
44use rustc_hir:: def_id:: { DefId , LocalDefId , CRATE_DEF_ID , LOCAL_CRATE } ;
55use rustc_hir:: { ItemId , Node , CRATE_HIR_ID } ;
@@ -8,7 +8,12 @@ use rustc_middle::ty::{DefIdTree, TyCtxt};
88use rustc_session:: config:: { sigpipe, CrateType , EntryFnType } ;
99use rustc_session:: parse:: feature_err;
1010use rustc_span:: symbol:: sym;
11- use rustc_span:: { Span , Symbol , DUMMY_SP } ;
11+ use rustc_span:: { Span , Symbol } ;
12+
13+ use crate :: errors:: {
14+ AttrOnlyInFunctions , AttrOnlyOnMain , AttrOnlyOnRootMain , ExternMain , MultipleRustcMain ,
15+ MultipleStartFunctions , NoMainErr , UnixSigpipeValues ,
16+ } ;
1217
1318struct EntryContext < ' tcx > {
1419 tcx : TyCtxt < ' tcx > ,
@@ -71,64 +76,57 @@ fn entry_point_type(ctxt: &EntryContext<'_>, id: ItemId, at_root: bool) -> Entry
7176 }
7277}
7378
74- fn err_if_attr_found ( ctxt : & EntryContext < ' _ > , id : ItemId , sym : Symbol , details : & str ) {
79+ fn attr_span_by_symbol ( ctxt : & EntryContext < ' _ > , id : ItemId , sym : Symbol ) -> Option < Span > {
7580 let attrs = ctxt. tcx . hir ( ) . attrs ( id. hir_id ( ) ) ;
76- if let Some ( attr) = ctxt. tcx . sess . find_by_name ( attrs, sym) {
77- ctxt. tcx
78- . sess
79- . struct_span_err ( attr. span , & format ! ( "`{}` attribute {}" , sym, details) )
80- . emit ( ) ;
81- }
81+ ctxt. tcx . sess . find_by_name ( attrs, sym) . map ( |attr| attr. span )
8282}
8383
8484fn find_item ( id : ItemId , ctxt : & mut EntryContext < ' _ > ) {
8585 let at_root = ctxt. tcx . opt_local_parent ( id. def_id . def_id ) == Some ( CRATE_DEF_ID ) ;
8686
8787 match entry_point_type ( ctxt, id, at_root) {
8888 EntryPointType :: None => {
89- err_if_attr_found ( ctxt, id, sym:: unix_sigpipe, "can only be used on `fn main()`" ) ;
89+ if let Some ( span) = attr_span_by_symbol ( ctxt, id, sym:: unix_sigpipe) {
90+ ctxt. tcx . sess . emit_err ( AttrOnlyOnMain { span, attr : sym:: unix_sigpipe } ) ;
91+ }
9092 }
9193 _ if !matches ! ( ctxt. tcx. def_kind( id. def_id) , DefKind :: Fn ) => {
92- err_if_attr_found ( ctxt, id, sym:: start, "can only be used on functions" ) ;
93- err_if_attr_found ( ctxt, id, sym:: rustc_main, "can only be used on functions" ) ;
94+ for attr in [ sym:: start, sym:: rustc_main] {
95+ if let Some ( span) = attr_span_by_symbol ( ctxt, id, attr) {
96+ ctxt. tcx . sess . emit_err ( AttrOnlyInFunctions { span, attr } ) ;
97+ }
98+ }
9499 }
95100 EntryPointType :: MainNamed => ( ) ,
96101 EntryPointType :: OtherMain => {
97- err_if_attr_found ( ctxt, id, sym:: unix_sigpipe, "can only be used on root `fn main()`" ) ;
102+ if let Some ( span) = attr_span_by_symbol ( ctxt, id, sym:: unix_sigpipe) {
103+ ctxt. tcx . sess . emit_err ( AttrOnlyOnRootMain { span, attr : sym:: unix_sigpipe } ) ;
104+ }
98105 ctxt. non_main_fns . push ( ctxt. tcx . def_span ( id. def_id ) ) ;
99106 }
100107 EntryPointType :: RustcMainAttr => {
101108 if ctxt. attr_main_fn . is_none ( ) {
102109 ctxt. attr_main_fn = Some ( ( id. def_id . def_id , ctxt. tcx . def_span ( id. def_id ) ) ) ;
103110 } else {
104- struct_span_err ! (
105- ctxt. tcx. sess,
106- ctxt. tcx. def_span( id. def_id. to_def_id( ) ) ,
107- E0137 ,
108- "multiple functions with a `#[rustc_main]` attribute"
109- )
110- . span_label (
111- ctxt. tcx . def_span ( id. def_id . to_def_id ( ) ) ,
112- "additional `#[rustc_main]` function" ,
113- )
114- . span_label ( ctxt. attr_main_fn . unwrap ( ) . 1 , "first `#[rustc_main]` function" )
115- . emit ( ) ;
111+ ctxt. tcx . sess . emit_err ( MultipleRustcMain {
112+ span : ctxt. tcx . def_span ( id. def_id . to_def_id ( ) ) ,
113+ first : ctxt. attr_main_fn . unwrap ( ) . 1 ,
114+ additional : ctxt. tcx . def_span ( id. def_id . to_def_id ( ) ) ,
115+ } ) ;
116116 }
117117 }
118118 EntryPointType :: Start => {
119- err_if_attr_found ( ctxt, id, sym:: unix_sigpipe, "can only be used on `fn main()`" ) ;
119+ if let Some ( span) = attr_span_by_symbol ( ctxt, id, sym:: unix_sigpipe) {
120+ ctxt. tcx . sess . emit_err ( AttrOnlyOnMain { span, attr : sym:: unix_sigpipe } ) ;
121+ }
120122 if ctxt. start_fn . is_none ( ) {
121123 ctxt. start_fn = Some ( ( id. def_id . def_id , ctxt. tcx . def_span ( id. def_id ) ) ) ;
122124 } else {
123- struct_span_err ! (
124- ctxt. tcx. sess,
125- ctxt. tcx. def_span( id. def_id) ,
126- E0138 ,
127- "multiple `start` functions"
128- )
129- . span_label ( ctxt. start_fn . unwrap ( ) . 1 , "previous `#[start]` function here" )
130- . span_label ( ctxt. tcx . def_span ( id. def_id . to_def_id ( ) ) , "multiple `start` functions" )
131- . emit ( ) ;
125+ ctxt. tcx . sess . emit_err ( MultipleStartFunctions {
126+ span : ctxt. tcx . def_span ( id. def_id ) ,
127+ labeled : ctxt. tcx . def_span ( id. def_id . to_def_id ( ) ) ,
128+ previous : ctxt. start_fn . unwrap ( ) . 1 ,
129+ } ) ;
132130 }
133131 }
134132 }
@@ -144,12 +142,7 @@ fn configure_main(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) -> Option<(DefId,
144142 if let Some ( main_def) = tcx. resolutions ( ( ) ) . main_def && let Some ( def_id) = main_def. opt_fn_def_id ( ) {
145143 // non-local main imports are handled below
146144 if let Some ( def_id) = def_id. as_local ( ) && matches ! ( tcx. hir( ) . find_by_def_id( def_id) , Some ( Node :: ForeignItem ( _) ) ) {
147- tcx. sess
148- . struct_span_err (
149- tcx. def_span ( def_id) ,
150- "the `main` function cannot be declared in an `extern` block" ,
151- )
152- . emit ( ) ;
145+ tcx. sess . emit_err ( ExternMain { span : tcx. def_span ( def_id) } ) ;
153146 return None ;
154147 }
155148
@@ -182,12 +175,7 @@ fn sigpipe(tcx: TyCtxt<'_>, def_id: DefId) -> u8 {
182175 sigpipe:: DEFAULT
183176 }
184177 _ => {
185- tcx. sess
186- . struct_span_err (
187- attr. span ,
188- "valid values for `#[unix_sigpipe = \" ...\" ]` are `inherit`, `sig_ign`, or `sig_dfl`" ,
189- )
190- . emit ( ) ;
178+ tcx. sess . emit_err ( UnixSigpipeValues { span : attr. span } ) ;
191179 sigpipe:: DEFAULT
192180 }
193181 }
@@ -206,52 +194,29 @@ fn no_main_err(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) {
206194 }
207195
208196 // There is no main function.
209- let mut err = struct_span_err ! (
210- tcx. sess,
211- DUMMY_SP ,
212- E0601 ,
213- "`main` function not found in crate `{}`" ,
214- tcx. crate_name( LOCAL_CRATE )
215- ) ;
216- let filename = & tcx. sess . local_crate_source_file ;
217- let note = if !visitor. non_main_fns . is_empty ( ) {
218- for & span in & visitor. non_main_fns {
219- err. span_note ( span, "here is a function named `main`" ) ;
220- }
221- err. note ( "you have one or more functions named `main` not defined at the crate level" ) ;
222- err. help ( "consider moving the `main` function definitions" ) ;
223- // There were some functions named `main` though. Try to give the user a hint.
224- format ! (
225- "the main function must be defined at the crate level{}" ,
226- filename. as_ref( ) . map( |f| format!( " (in `{}`)" , f. display( ) ) ) . unwrap_or_default( )
227- )
228- } else if let Some ( filename) = filename {
229- format ! ( "consider adding a `main` function to `{}`" , filename. display( ) )
230- } else {
231- String :: from ( "consider adding a `main` function at the crate level" )
232- } ;
197+ let mut has_filename = true ;
198+ let filename = tcx. sess . local_crate_source_file . clone ( ) . unwrap_or_else ( || {
199+ has_filename = false ;
200+ Default :: default ( )
201+ } ) ;
202+ let main_def_opt = tcx. resolutions ( ( ) ) . main_def ;
203+ let diagnostic_id = error_code ! ( E0601 ) ;
204+ let add_teach_note = tcx. sess . teach ( & diagnostic_id) ;
233205 // The file may be empty, which leads to the diagnostic machinery not emitting this
234206 // note. This is a relatively simple way to detect that case and emit a span-less
235207 // note instead.
236- if tcx. sess . source_map ( ) . lookup_line ( sp. hi ( ) ) . is_ok ( ) {
237- err. set_span ( sp. shrink_to_hi ( ) ) ;
238- err. span_label ( sp. shrink_to_hi ( ) , & note) ;
239- } else {
240- err. note ( & note) ;
241- }
242-
243- if let Some ( main_def) = tcx. resolutions ( ( ) ) . main_def && main_def. opt_fn_def_id ( ) . is_none ( ) {
244- // There is something at `crate::main`, but it is not a function definition.
245- err. span_label ( main_def. span , "non-function item at `crate::main` is found" ) ;
246- }
247-
248- if tcx. sess . teach ( & err. get_code ( ) . unwrap ( ) ) {
249- err. note (
250- "If you don't know the basics of Rust, you can go look to the Rust Book \
251- to get started: https://doc.rust-lang.org/book/",
252- ) ;
253- }
254- err. emit ( ) ;
208+ let file_empty = !tcx. sess . source_map ( ) . lookup_line ( sp. hi ( ) ) . is_ok ( ) ;
209+
210+ tcx. sess . emit_err ( NoMainErr {
211+ sp,
212+ crate_name : tcx. crate_name ( LOCAL_CRATE ) ,
213+ has_filename,
214+ filename,
215+ file_empty,
216+ non_main_fns : visitor. non_main_fns . clone ( ) ,
217+ main_def_opt,
218+ add_teach_note,
219+ } ) ;
255220}
256221
257222pub fn provide ( providers : & mut Providers ) {
0 commit comments