Fortify check for number of generic parameters.

This commit is contained in:
Camille GILLOT 2022-05-12 22:29:04 +02:00
parent da175c743c
commit 7b86c6f21e
3 changed files with 130 additions and 79 deletions

View File

@ -8,10 +8,11 @@ use super::*;
use rustc_attr as attr;
use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::Visitor;
use rustc_hir::lang_items::LangItem;
use rustc_hir::{def::Res, ItemKind, Node, PathSegment};
use rustc_hir::{ItemKind, Node, PathSegment};
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt};
use rustc_infer::traits::Obligation;
@ -29,7 +30,6 @@ use rustc_trait_selection::traits;
use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
use rustc_ty_utils::representability::{self, Representability};
use rustc_hir::def::DefKind;
use std::iter;
use std::ops::ControlFlow;
@ -93,7 +93,6 @@ pub(super) fn check_fn<'a, 'tcx>(
fcx.return_type_pre_known = return_type_pre_known;
let tcx = fcx.tcx;
let sess = tcx.sess;
let hir = tcx.hir();
let declared_ret_ty = fn_sig.output();
@ -260,20 +259,47 @@ pub(super) fn check_fn<'a, 'tcx>(
if let Some(panic_impl_did) = tcx.lang_items().panic_impl()
&& panic_impl_did == hir.local_def_id(fn_id).to_def_id()
{
if let Some(panic_info_did) = tcx.lang_items().panic_info() {
if *declared_ret_ty.kind() != ty::Never {
sess.span_err(decl.output.span(), "return type should be `!`");
check_panic_info_fn(tcx, panic_impl_did.expect_local(), fn_sig, decl, declared_ret_ty);
}
// Check that a function marked as `#[alloc_error_handler]` has signature `fn(Layout) -> !`
if let Some(alloc_error_handler_did) = tcx.lang_items().oom()
&& alloc_error_handler_did == hir.local_def_id(fn_id).to_def_id()
{
check_alloc_error_fn(tcx, alloc_error_handler_did.expect_local(), fn_sig, decl, declared_ret_ty);
}
(fcx, gen_ty)
}
fn check_panic_info_fn(
tcx: TyCtxt<'_>,
fn_id: LocalDefId,
fn_sig: ty::FnSig<'_>,
decl: &hir::FnDecl<'_>,
declared_ret_ty: Ty<'_>,
) {
let Some(panic_info_did) = tcx.lang_items().panic_info() else {
tcx.sess.err("language item required, but not found: `panic_info`");
return;
};
if *declared_ret_ty.kind() != ty::Never {
tcx.sess.span_err(decl.output.span(), "return type should be `!`");
}
let span = tcx.def_span(fn_id);
let inputs = fn_sig.inputs();
let span = hir.span(fn_id);
if inputs.len() == 1 {
if inputs.len() != 1 {
let span = tcx.sess.source_map().guess_head_span(span);
tcx.sess.span_err(span, "function should have one argument");
return;
}
let arg_is_panic_info = match *inputs[0].kind() {
ty::Ref(region, ty, mutbl) => match *ty.kind() {
ty::Adt(ref adt, _) => {
adt.did() == panic_info_did
&& mutbl == hir::Mutability::Not
&& !region.is_static()
adt.did() == panic_info_did && mutbl == hir::Mutability::Not && !region.is_static()
}
_ => false,
},
@ -281,64 +307,75 @@ pub(super) fn check_fn<'a, 'tcx>(
};
if !arg_is_panic_info {
sess.span_err(decl.inputs[0].span, "argument should be `&PanicInfo`");
tcx.sess.span_err(decl.inputs[0].span, "argument should be `&PanicInfo`");
}
if let Node::Item(item) = hir.get(fn_id)
&& let ItemKind::Fn(_, ref generics, _) = item.kind
&& !generics.params.is_empty()
{
sess.span_err(span, "should have no type parameters");
}
} else {
let span = sess.source_map().guess_head_span(span);
sess.span_err(span, "function should have one argument");
}
} else {
sess.err("language item required, but not found: `panic_info`");
}
}
let DefKind::Fn = tcx.def_kind(fn_id) else {
let span = tcx.def_span(fn_id);
tcx.sess.span_err(span, "should be a function");
return;
};
let generic_counts = tcx.generics_of(fn_id).own_counts();
if generic_counts.types != 0 {
let span = tcx.def_span(fn_id);
tcx.sess.span_err(span, "should have no type parameters");
}
if generic_counts.consts != 0 {
let span = tcx.def_span(fn_id);
tcx.sess.span_err(span, "should have no const parameters");
}
}
fn check_alloc_error_fn(
tcx: TyCtxt<'_>,
fn_id: LocalDefId,
fn_sig: ty::FnSig<'_>,
decl: &hir::FnDecl<'_>,
declared_ret_ty: Ty<'_>,
) {
let Some(alloc_layout_did) = tcx.lang_items().alloc_layout() else {
tcx.sess.err("language item required, but not found: `alloc_layout`");
return;
};
// Check that a function marked as `#[alloc_error_handler]` has signature `fn(Layout) -> !`
if let Some(alloc_error_handler_did) = tcx.lang_items().oom()
&& alloc_error_handler_did == hir.local_def_id(fn_id).to_def_id()
{
if let Some(alloc_layout_did) = tcx.lang_items().alloc_layout() {
if *declared_ret_ty.kind() != ty::Never {
sess.span_err(decl.output.span(), "return type should be `!`");
tcx.sess.span_err(decl.output.span(), "return type should be `!`");
}
let inputs = fn_sig.inputs();
let span = hir.span(fn_id);
if inputs.len() == 1 {
if inputs.len() != 1 {
let span = tcx.def_span(fn_id);
let span = tcx.sess.source_map().guess_head_span(span);
tcx.sess.span_err(span, "function should have one argument");
return;
}
let arg_is_alloc_layout = match inputs[0].kind() {
ty::Adt(ref adt, _) => adt.did() == alloc_layout_did,
_ => false,
};
if !arg_is_alloc_layout {
sess.span_err(decl.inputs[0].span, "argument should be `Layout`");
tcx.sess.span_err(decl.inputs[0].span, "argument should be `Layout`");
}
if let Node::Item(item) = hir.get(fn_id)
&& let ItemKind::Fn(_, ref generics, _) = item.kind
&& !generics.params.is_empty()
{
sess.span_err(
span,
"`#[alloc_error_handler]` function should have no type parameters",
);
}
} else {
let span = sess.source_map().guess_head_span(span);
sess.span_err(span, "function should have one argument");
}
} else {
sess.err("language item required, but not found: `alloc_layout`");
}
}
let DefKind::Fn = tcx.def_kind(fn_id) else {
let span = tcx.def_span(fn_id);
tcx.sess.span_err(span, "`#[alloc_error_handler]` should be a function");
return;
};
(fcx, gen_ty)
let generic_counts = tcx.generics_of(fn_id).own_counts();
if generic_counts.types != 0 {
let span = tcx.def_span(fn_id);
tcx.sess.span_err(span, "`#[alloc_error_handler]` function should have no type parameters");
}
if generic_counts.consts != 0 {
let span = tcx.def_span(fn_id);
tcx.sess
.span_err(span, "`#[alloc_error_handler]` function should have no const parameters");
}
}
fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId, span: Span) {

View File

@ -660,8 +660,24 @@ fn compare_number_of_generics<'tcx>(
_ => None,
})
.collect();
let spans = impl_item.generics.spans();
let span = spans.primary_span();
let spans = if impl_item.generics.params.is_empty() {
vec![impl_item.generics.span]
} else {
impl_item
.generics
.params
.iter()
.filter(|p| {
matches!(
p.kind,
hir::GenericParamKind::Type { .. }
| hir::GenericParamKind::Const { .. }
)
})
.map(|p| p.span)
.collect::<Vec<Span>>()
};
let span = spans.first().copied();
let mut err = tcx.sess.struct_span_err_with_code(
spans,

View File

@ -8,7 +8,7 @@ LL | type A = u32;
| ^ lifetimes do not match type in trait
error[E0049]: type `B` has 1 type parameter but its trait declaration has 0 type parameters
--> $DIR/parameter_number_and_kind_impl.rs:17:12
--> $DIR/parameter_number_and_kind_impl.rs:17:16
|
LL | type B<'a, 'b>;
| -- --
@ -16,9 +16,7 @@ LL | type B<'a, 'b>;
| expected 0 type parameters
...
LL | type B<'a, T> = Vec<T>;
| ^^ ^
| |
| found 1 type parameter
| ^ found 1 type parameter
error[E0195]: lifetime parameters or bounds on type `C` do not match the trait declaration
--> $DIR/parameter_number_and_kind_impl.rs:19:11