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_attr as attr;
use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan}; use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::Visitor; use rustc_hir::intravisit::Visitor;
use rustc_hir::lang_items::LangItem; 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::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt}; use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt};
use rustc_infer::traits::Obligation; 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_trait_selection::traits::error_reporting::InferCtxtExt as _;
use rustc_ty_utils::representability::{self, Representability}; use rustc_ty_utils::representability::{self, Representability};
use rustc_hir::def::DefKind;
use std::iter; use std::iter;
use std::ops::ControlFlow; use std::ops::ControlFlow;
@ -93,7 +93,6 @@ pub(super) fn check_fn<'a, 'tcx>(
fcx.return_type_pre_known = return_type_pre_known; fcx.return_type_pre_known = return_type_pre_known;
let tcx = fcx.tcx; let tcx = fcx.tcx;
let sess = tcx.sess;
let hir = tcx.hir(); let hir = tcx.hir();
let declared_ret_ty = fn_sig.output(); 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() if let Some(panic_impl_did) = tcx.lang_items().panic_impl()
&& panic_impl_did == hir.local_def_id(fn_id).to_def_id() && panic_impl_did == hir.local_def_id(fn_id).to_def_id()
{ {
if let Some(panic_info_did) = tcx.lang_items().panic_info() { check_panic_info_fn(tcx, panic_impl_did.expect_local(), fn_sig, decl, declared_ret_ty);
if *declared_ret_ty.kind() != ty::Never {
sess.span_err(decl.output.span(), "return type should be `!`");
} }
// 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 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() { let arg_is_panic_info = match *inputs[0].kind() {
ty::Ref(region, ty, mutbl) => match *ty.kind() { ty::Ref(region, ty, mutbl) => match *ty.kind() {
ty::Adt(ref adt, _) => { ty::Adt(ref adt, _) => {
adt.did() == panic_info_did adt.did() == panic_info_did && mutbl == hir::Mutability::Not && !region.is_static()
&& mutbl == hir::Mutability::Not
&& !region.is_static()
} }
_ => false, _ => false,
}, },
@ -281,64 +307,75 @@ pub(super) fn check_fn<'a, 'tcx>(
}; };
if !arg_is_panic_info { 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 DefKind::Fn = tcx.def_kind(fn_id) else {
&& let ItemKind::Fn(_, ref generics, _) = item.kind let span = tcx.def_span(fn_id);
&& !generics.params.is_empty() tcx.sess.span_err(span, "should be a function");
{ return;
sess.span_err(span, "should have no type parameters"); };
}
} else { let generic_counts = tcx.generics_of(fn_id).own_counts();
let span = sess.source_map().guess_head_span(span); if generic_counts.types != 0 {
sess.span_err(span, "function should have one argument"); let span = tcx.def_span(fn_id);
} tcx.sess.span_err(span, "should have no type parameters");
} else { }
sess.err("language item required, but not found: `panic_info`"); 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 { 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 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() { let arg_is_alloc_layout = match inputs[0].kind() {
ty::Adt(ref adt, _) => adt.did() == alloc_layout_did, ty::Adt(ref adt, _) => adt.did() == alloc_layout_did,
_ => false, _ => false,
}; };
if !arg_is_alloc_layout { 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 DefKind::Fn = tcx.def_kind(fn_id) else {
&& let ItemKind::Fn(_, ref generics, _) = item.kind let span = tcx.def_span(fn_id);
&& !generics.params.is_empty() tcx.sess.span_err(span, "`#[alloc_error_handler]` should be a function");
{ return;
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`");
}
}
(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) { fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId, span: Span) {

View File

@ -660,8 +660,24 @@ fn compare_number_of_generics<'tcx>(
_ => None, _ => None,
}) })
.collect(); .collect();
let spans = impl_item.generics.spans(); let spans = if impl_item.generics.params.is_empty() {
let span = spans.primary_span(); 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( let mut err = tcx.sess.struct_span_err_with_code(
spans, spans,

View File

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