Skip to content

Commit 337a97d

Browse files
asquared31415dtolnay
authored andcommitted
add checks for the signature of the lang item
1 parent ef4046e commit 337a97d

18 files changed

+405
-3
lines changed

compiler/rustc_error_messages/locales/en-US/hir_typeck.ftl

+11
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,14 @@ hir_typeck_add_missing_parentheses_in_range = you must surround the range in par
4646
4747
hir_typeck_op_trait_generic_params =
4848
`{$method_name}` must not have any generic parameters
49+
50+
hir_typeck_lang_start_incorrect_number_params = incorrect number of parameters for the `start` lang item
51+
hir_typeck_lang_start_incorrect_number_params_note_expected_count = the `start` lang item should have four parameters, but found {$found_param_count}
52+
53+
hir_typeck_lang_start_expected_sig_note = the `start` lang item should have the signature `fn(fn() -> T, isize, *const *const u8, u8) -> isize`
54+
55+
hir_typeck_lang_start_incorrect_param = parameter {$param_num} of the `start` lang item is incorrect
56+
.suggestion = change the type from `{$found_ty}` to `{$expected_ty}`
57+
58+
hir_typeck_lang_start_incorrect_ret_ty = the return type of the `start` lang item is incorrect
59+
.suggestion = change the type from `{$found_ty}` to `{$expected_ty}`

compiler/rustc_hir_typeck/src/check.rs

+132-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
use crate::coercion::CoerceMany;
2+
use crate::errors::{
3+
LangStartIncorrectNumberArgs, LangStartIncorrectParam, LangStartIncorrectRetTy,
4+
};
25
use crate::gather_locals::GatherLocalsVisitor;
36
use crate::FnCtxt;
47
use crate::GeneratorTypes;
@@ -9,8 +12,9 @@ use rustc_hir::lang_items::LangItem;
912
use rustc_hir_analysis::check::fn_maybe_err;
1013
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
1114
use rustc_infer::infer::RegionVariableOrigin;
12-
use rustc_middle::ty::{self, Ty, TyCtxt};
15+
use rustc_middle::ty::{self, Binder, Ty, TyCtxt};
1316
use rustc_span::def_id::LocalDefId;
17+
use rustc_target::spec::abi::Abi;
1418
use rustc_trait_selection::traits;
1519
use std::cell::RefCell;
1620

@@ -168,6 +172,10 @@ pub(super) fn check_fn<'a, 'tcx>(
168172
check_panic_info_fn(tcx, panic_impl_did.expect_local(), fn_sig, decl, declared_ret_ty);
169173
}
170174

175+
if let Some(lang_start_defid) = tcx.lang_items().start_fn() && lang_start_defid == hir.local_def_id(fn_id).to_def_id() {
176+
check_lang_start_fn(tcx, fn_sig, decl, fn_def_id);
177+
}
178+
171179
gen_ty
172180
}
173181

@@ -223,3 +231,126 @@ fn check_panic_info_fn(
223231
tcx.sess.span_err(span, "should have no const parameters");
224232
}
225233
}
234+
235+
fn check_lang_start_fn<'tcx>(
236+
tcx: TyCtxt<'tcx>,
237+
fn_sig: ty::FnSig<'tcx>,
238+
decl: &'tcx hir::FnDecl<'tcx>,
239+
def_id: LocalDefId,
240+
) {
241+
let inputs = fn_sig.inputs();
242+
243+
let arg_count = inputs.len();
244+
if arg_count != 4 {
245+
tcx.sess.emit_err(LangStartIncorrectNumberArgs {
246+
params_span: tcx.def_span(def_id),
247+
found_param_count: arg_count,
248+
});
249+
}
250+
251+
// only check args if they should exist by checking the count
252+
// note: this does not handle args being shifted or their order swapped very nicely
253+
// but it's a lang item, users shouldn't frequently encounter this
254+
255+
// first arg is `main: fn() -> T`
256+
if let Some(&main_arg) = inputs.get(0) {
257+
// make a Ty for the generic on the fn for diagnostics
258+
// FIXME: make the lang item generic checks check for the right generic *kind*
259+
// for example `start`'s generic should be a type parameter
260+
let generics = tcx.generics_of(def_id);
261+
let fn_generic = generics.param_at(0, tcx);
262+
let generic_tykind =
263+
ty::Param(ty::ParamTy { index: fn_generic.index, name: fn_generic.name });
264+
let generic_ty = tcx.mk_ty(generic_tykind);
265+
let expected_fn_sig =
266+
tcx.mk_fn_sig([].iter(), &generic_ty, false, hir::Unsafety::Normal, Abi::Rust);
267+
let expected_ty = tcx.mk_fn_ptr(Binder::dummy(expected_fn_sig));
268+
269+
// we emit the same error to suggest changing the arg no matter what's wrong with the arg
270+
let emit_main_fn_arg_err = || {
271+
tcx.sess.emit_err(LangStartIncorrectParam {
272+
param_span: decl.inputs[0].span,
273+
param_num: 1,
274+
expected_ty: expected_ty,
275+
found_ty: main_arg,
276+
});
277+
};
278+
279+
if let ty::FnPtr(main_fn_sig) = main_arg.kind() {
280+
let main_fn_inputs = main_fn_sig.inputs();
281+
if main_fn_inputs.iter().count() != 0 {
282+
emit_main_fn_arg_err();
283+
}
284+
285+
let output = main_fn_sig.output();
286+
output.map_bound(|ret_ty| {
287+
// if the output ty is a generic, it's probably the right one
288+
if !matches!(ret_ty.kind(), ty::Param(_)) {
289+
emit_main_fn_arg_err();
290+
}
291+
});
292+
} else {
293+
emit_main_fn_arg_err();
294+
}
295+
}
296+
297+
// second arg is isize
298+
if let Some(&argc_arg) = inputs.get(1) {
299+
if argc_arg != tcx.types.isize {
300+
tcx.sess.emit_err(LangStartIncorrectParam {
301+
param_span: decl.inputs[1].span,
302+
param_num: 2,
303+
expected_ty: tcx.types.isize,
304+
found_ty: argc_arg,
305+
});
306+
}
307+
}
308+
309+
// third arg is `*const *const u8`
310+
if let Some(&argv_arg) = inputs.get(2) {
311+
let mut argv_is_okay = false;
312+
if let ty::RawPtr(outer_ptr) = argv_arg.kind() {
313+
if outer_ptr.mutbl.is_not() {
314+
if let ty::RawPtr(inner_ptr) = outer_ptr.ty.kind() {
315+
if inner_ptr.mutbl.is_not() && inner_ptr.ty == tcx.types.u8 {
316+
argv_is_okay = true;
317+
}
318+
}
319+
}
320+
}
321+
322+
if !argv_is_okay {
323+
let inner_ptr_ty =
324+
tcx.mk_ptr(ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: tcx.types.u8 });
325+
let expected_ty =
326+
tcx.mk_ptr(ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: inner_ptr_ty });
327+
tcx.sess.emit_err(LangStartIncorrectParam {
328+
param_span: decl.inputs[2].span,
329+
param_num: 3,
330+
expected_ty,
331+
found_ty: argv_arg,
332+
});
333+
}
334+
}
335+
336+
// fourth arg is `sigpipe: u8`
337+
if let Some(&sigpipe_arg) = inputs.get(3) {
338+
if sigpipe_arg != tcx.types.u8 {
339+
tcx.sess.emit_err(LangStartIncorrectParam {
340+
param_span: decl.inputs[3].span,
341+
param_num: 4,
342+
expected_ty: tcx.types.u8,
343+
found_ty: sigpipe_arg,
344+
});
345+
}
346+
}
347+
348+
// output type is isize
349+
if fn_sig.output() != tcx.types.isize {
350+
tcx.sess.emit_err(LangStartIncorrectRetTy {
351+
ret_span: decl.output.span(),
352+
expected_ty: tcx.types.isize,
353+
found_ty: fn_sig.output(),
354+
});
355+
}
356+
}

compiler/rustc_hir_typeck/src/errors.rs

+33
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,36 @@ impl AddToDiagnostic for TypeMismatchFruTypo {
172172
);
173173
}
174174
}
175+
176+
#[derive(Diagnostic)]
177+
#[diag(hir_typeck_lang_start_incorrect_number_params)]
178+
#[note(hir_typeck_lang_start_incorrect_number_params_note_expected_count)]
179+
#[note(hir_typeck_lang_start_expected_sig_note)]
180+
pub struct LangStartIncorrectNumberArgs {
181+
#[primary_span]
182+
pub params_span: Span,
183+
pub found_param_count: usize,
184+
}
185+
186+
#[derive(Diagnostic)]
187+
#[diag(hir_typeck_lang_start_incorrect_param)]
188+
pub struct LangStartIncorrectParam<'tcx> {
189+
#[primary_span]
190+
#[suggestion(style = "short", code = "{expected_ty}", applicability = "machine-applicable")]
191+
pub param_span: Span,
192+
193+
pub param_num: usize,
194+
pub expected_ty: Ty<'tcx>,
195+
pub found_ty: Ty<'tcx>,
196+
}
197+
198+
#[derive(Diagnostic)]
199+
#[diag(hir_typeck_lang_start_incorrect_ret_ty)]
200+
pub struct LangStartIncorrectRetTy<'tcx> {
201+
#[primary_span]
202+
#[suggestion(style = "short", code = "{expected_ty}", applicability = "machine-applicable")]
203+
pub ret_span: Span,
204+
205+
pub expected_ty: Ty<'tcx>,
206+
pub found_ty: Ty<'tcx>,
207+
}

src/tools/clippy/tests/ui/def_id_nocore.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ pub trait Copy {}
1515
pub unsafe trait Freeze {}
1616

1717
#[lang = "start"]
18-
fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize {
18+
fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize {
1919
0
2020
}
2121

tests/run-make-fulldeps/target-specs/foo.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ trait Sized {}
1111
auto trait Freeze {}
1212

1313
#[lang = "start"]
14-
fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize {
14+
fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize {
1515
0
1616
}
1717

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: parameter 2 of the `start` lang item is incorrect
2+
--> $DIR/start_lang_item_args.rs:75:38
3+
|
4+
LL | fn start<T>(_main: fn() -> T, _argc: i8, _argv: *const *const u8, _sigpipe: u8) -> isize {
5+
| ^^ help: change the type from `i8` to `isize`
6+
7+
error: aborting due to previous error
8+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: parameter 3 of the `start` lang item is incorrect
2+
--> $DIR/start_lang_item_args.rs:89:52
3+
|
4+
LL | fn start<T>(_main: fn() -> T, _argc: isize, _argv: u8, _sigpipe: u8) -> isize {
5+
| ^^ help: change the type from `u8` to `*const *const u8`
6+
7+
error: aborting due to previous error
8+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
error: parameter 3 of the `start` lang item is incorrect
2+
--> $DIR/start_lang_item_args.rs:82:52
3+
|
4+
LL | fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const usize, _sigpipe: u8) -> isize {
5+
| ^^^^^^^^^^^^^^^^^^^
6+
|
7+
help: change the type from `*const *const usize` to `*const *const u8`
8+
|
9+
LL | fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize {
10+
| ~~~~~~~~~~~~~~~~
11+
12+
error: aborting due to previous error
13+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
error: parameter 1 of the `start` lang item is incorrect
2+
--> $DIR/start_lang_item_args.rs:61:20
3+
|
4+
LL | fn start<T>(_main: fn(i32) -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize {
5+
| ^^^^^^^^^^^^
6+
|
7+
help: change the type from `fn(i32) -> T` to `fn() -> T`
8+
|
9+
LL | fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize {
10+
| ~~~~~~~~~
11+
12+
error: aborting due to previous error
13+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
error: parameter 1 of the `start` lang item is incorrect
2+
--> $DIR/start_lang_item_args.rs:68:20
3+
|
4+
LL | fn start<T>(_main: fn() -> u16, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize {
5+
| ^^^^^^^^^^^
6+
|
7+
help: change the type from `fn() -> u16` to `fn() -> T`
8+
|
9+
LL | fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize {
10+
| ~~~~~~~~~
11+
12+
error: aborting due to previous error
13+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: parameter 1 of the `start` lang item is incorrect
2+
--> $DIR/start_lang_item_args.rs:54:20
3+
|
4+
LL | fn start<T>(_main: u64, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize {
5+
| ^^^ help: change the type from `u64` to `fn() -> T`
6+
7+
error: aborting due to previous error
8+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error: incorrect number of parameters for the `start` lang item
2+
--> $DIR/start_lang_item_args.rs:15:1
3+
|
4+
LL | fn start<T>() -> isize {
5+
| ^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: the `start` lang item should have four parameters, but found 0
8+
= note: the `start` lang item should have the signature `fn(fn() -> T, isize, *const *const u8, u8) -> isize`
9+
10+
error: aborting due to previous error
11+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: the return type of the `start` lang item is incorrect
2+
--> $DIR/start_lang_item_args.rs:29:84
3+
|
4+
LL | fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) {}
5+
| ^ help: change the type from `()` to `isize`
6+
7+
error: aborting due to previous error
8+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error: incorrect number of parameters for the `start` lang item
2+
--> $DIR/start_lang_item_args.rs:22:1
3+
|
4+
LL | fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize {
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: the `start` lang item should have four parameters, but found 3
8+
= note: the `start` lang item should have the signature `fn(fn() -> T, isize, *const *const u8, u8) -> isize`
9+
10+
error: aborting due to previous error
11+

0 commit comments

Comments
 (0)