1
1
use rustc_ast:: entry:: EntryPointType ;
2
- use rustc_errors:: struct_span_err ;
2
+ use rustc_errors:: error_code ;
3
3
use rustc_hir:: def:: DefKind ;
4
4
use rustc_hir:: def_id:: { DefId , LocalDefId , CRATE_DEF_ID , LOCAL_CRATE } ;
5
5
use rustc_hir:: { ItemId , Node , CRATE_HIR_ID } ;
@@ -8,7 +8,12 @@ use rustc_middle::ty::{DefIdTree, TyCtxt};
8
8
use rustc_session:: config:: { sigpipe, CrateType , EntryFnType } ;
9
9
use rustc_session:: parse:: feature_err;
10
10
use 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
+ } ;
12
17
13
18
struct EntryContext < ' tcx > {
14
19
tcx : TyCtxt < ' tcx > ,
@@ -71,64 +76,57 @@ fn entry_point_type(ctxt: &EntryContext<'_>, id: ItemId, at_root: bool) -> Entry
71
76
}
72
77
}
73
78
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 > {
75
80
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 )
82
82
}
83
83
84
84
fn find_item ( id : ItemId , ctxt : & mut EntryContext < ' _ > ) {
85
85
let at_root = ctxt. tcx . opt_local_parent ( id. def_id . def_id ) == Some ( CRATE_DEF_ID ) ;
86
86
87
87
match entry_point_type ( ctxt, id, at_root) {
88
88
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
+ }
90
92
}
91
93
_ 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
+ }
94
99
}
95
100
EntryPointType :: MainNamed => ( ) ,
96
101
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
+ }
98
105
ctxt. non_main_fns . push ( ctxt. tcx . def_span ( id. def_id ) ) ;
99
106
}
100
107
EntryPointType :: RustcMainAttr => {
101
108
if ctxt. attr_main_fn . is_none ( ) {
102
109
ctxt. attr_main_fn = Some ( ( id. def_id . def_id , ctxt. tcx . def_span ( id. def_id ) ) ) ;
103
110
} 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
+ } ) ;
116
116
}
117
117
}
118
118
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
+ }
120
122
if ctxt. start_fn . is_none ( ) {
121
123
ctxt. start_fn = Some ( ( id. def_id . def_id , ctxt. tcx . def_span ( id. def_id ) ) ) ;
122
124
} 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
+ } ) ;
132
130
}
133
131
}
134
132
}
@@ -144,12 +142,7 @@ fn configure_main(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) -> Option<(DefId,
144
142
if let Some ( main_def) = tcx. resolutions ( ( ) ) . main_def && let Some ( def_id) = main_def. opt_fn_def_id ( ) {
145
143
// non-local main imports are handled below
146
144
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) } ) ;
153
146
return None ;
154
147
}
155
148
@@ -182,12 +175,7 @@ fn sigpipe(tcx: TyCtxt<'_>, def_id: DefId) -> u8 {
182
175
sigpipe:: DEFAULT
183
176
}
184
177
_ => {
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 } ) ;
191
179
sigpipe:: DEFAULT
192
180
}
193
181
}
@@ -206,52 +194,29 @@ fn no_main_err(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) {
206
194
}
207
195
208
196
// 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) ;
233
205
// The file may be empty, which leads to the diagnostic machinery not emitting this
234
206
// note. This is a relatively simple way to detect that case and emit a span-less
235
207
// 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
+ } ) ;
255
220
}
256
221
257
222
pub fn provide ( providers : & mut Providers ) {
0 commit comments