Validate generic parameter and argument order in ast_validation
Co-Authored-By: Gabriel Smith <yodaldevoid@users.noreply.github.com>
This commit is contained in:
parent
2fec52bf58
commit
11874a0c14
1 changed files with 112 additions and 16 deletions
|
@ -9,6 +9,7 @@
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use rustc::lint;
|
use rustc::lint;
|
||||||
use rustc::session::Session;
|
use rustc::session::Session;
|
||||||
|
use rustc_data_structures::fx::FxHashMap;
|
||||||
use syntax::ast::*;
|
use syntax::ast::*;
|
||||||
use syntax::attr;
|
use syntax::attr;
|
||||||
use syntax::source_map::Spanned;
|
use syntax::source_map::Spanned;
|
||||||
|
@ -271,7 +272,74 @@ impl<'a> AstValidator<'a> {
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum GenericPosition {
|
||||||
|
Param,
|
||||||
|
Arg,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_generics_order<'a>(
|
||||||
|
handler: &errors::Handler,
|
||||||
|
generics: impl Iterator<Item = (ParamKindOrd, Span, Option<Ident>)>,
|
||||||
|
pos: GenericPosition,
|
||||||
|
span: Span,
|
||||||
|
) {
|
||||||
|
let mut max_param: Option<ParamKindOrd> = None;
|
||||||
|
let mut out_of_order = FxHashMap::default();
|
||||||
|
let mut param_idents = vec![];
|
||||||
|
|
||||||
|
for (kind, span, ident) in generics {
|
||||||
|
if let Some(ident) = ident {
|
||||||
|
param_idents.push((kind, param_idents.len(), ident));
|
||||||
|
}
|
||||||
|
let max_param = &mut max_param;
|
||||||
|
match max_param {
|
||||||
|
Some(max_param) if *max_param > kind => {
|
||||||
|
let entry = out_of_order.entry(kind).or_insert((*max_param, vec![]));
|
||||||
|
entry.1.push(span);
|
||||||
|
}
|
||||||
|
Some(_) | None => *max_param = Some(kind),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut ordered_params = "<".to_string();
|
||||||
|
if !out_of_order.is_empty() {
|
||||||
|
param_idents.sort_by_key(|&(po, i, _)| (po, i));
|
||||||
|
let mut first = true;
|
||||||
|
for (_, _, ident) in param_idents {
|
||||||
|
if !first {
|
||||||
|
ordered_params += ", ";
|
||||||
|
}
|
||||||
|
ordered_params += &ident.as_str();
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ordered_params += ">";
|
||||||
|
|
||||||
|
let pos_str = match pos {
|
||||||
|
GenericPosition::Param => "parameter",
|
||||||
|
GenericPosition::Arg => "argument",
|
||||||
|
};
|
||||||
|
|
||||||
|
for (param_ord, (max_param, spans)) in out_of_order {
|
||||||
|
let mut err = handler.struct_span_err(spans,
|
||||||
|
&format!(
|
||||||
|
"{} {pos}s must be declared prior to {} {pos}s",
|
||||||
|
param_ord,
|
||||||
|
max_param,
|
||||||
|
pos = pos_str,
|
||||||
|
));
|
||||||
|
if let GenericPosition::Param = pos {
|
||||||
|
err.span_suggestion(
|
||||||
|
span,
|
||||||
|
&format!("reorder the {}s: lifetimes, then types, then consts", pos_str),
|
||||||
|
ordered_params.clone(),
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
err.emit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Visitor<'a> for AstValidator<'a> {
|
impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||||
|
@ -412,6 +480,26 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||||
.note("only trait implementations may be annotated with default").emit();
|
.note("only trait implementations may be annotated with default").emit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ItemKind::Fn(_, header, ref generics, _) => {
|
||||||
|
// We currently do not permit const generics in `const fn`, as
|
||||||
|
// this is tantamount to allowing compile-time dependent typing.
|
||||||
|
if header.constness.node == Constness::Const {
|
||||||
|
// Look for const generics and error if we find any.
|
||||||
|
for param in &generics.params {
|
||||||
|
match param.kind {
|
||||||
|
GenericParamKind::Const { .. } => {
|
||||||
|
self.err_handler()
|
||||||
|
.struct_span_err(
|
||||||
|
item.span,
|
||||||
|
"const parameters are not permitted in `const fn`",
|
||||||
|
)
|
||||||
|
.emit();
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
ItemKind::ForeignMod(..) => {
|
ItemKind::ForeignMod(..) => {
|
||||||
self.invalid_visibility(
|
self.invalid_visibility(
|
||||||
&item.vis,
|
&item.vis,
|
||||||
|
@ -508,6 +596,13 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||||
match *generic_args {
|
match *generic_args {
|
||||||
GenericArgs::AngleBracketed(ref data) => {
|
GenericArgs::AngleBracketed(ref data) => {
|
||||||
walk_list!(self, visit_generic_arg, &data.args);
|
walk_list!(self, visit_generic_arg, &data.args);
|
||||||
|
validate_generics_order(self.err_handler(), data.args.iter().map(|arg| {
|
||||||
|
(match arg {
|
||||||
|
GenericArg::Lifetime(..) => ParamKindOrd::Lifetime,
|
||||||
|
GenericArg::Type(..) => ParamKindOrd::Type,
|
||||||
|
GenericArg::Const(..) => ParamKindOrd::Const,
|
||||||
|
}, arg.span(), None)
|
||||||
|
}), GenericPosition::Arg, generic_args.span());
|
||||||
// Type bindings such as `Item=impl Debug` in `Iterator<Item=Debug>`
|
// Type bindings such as `Item=impl Debug` in `Iterator<Item=Debug>`
|
||||||
// are allowed to contain nested `impl Trait`.
|
// are allowed to contain nested `impl Trait`.
|
||||||
self.with_impl_trait(None, |this| {
|
self.with_impl_trait(None, |this| {
|
||||||
|
@ -526,27 +621,27 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_generics(&mut self, generics: &'a Generics) {
|
fn visit_generics(&mut self, generics: &'a Generics) {
|
||||||
let mut seen_non_lifetime_param = false;
|
let mut prev_ty_default = None;
|
||||||
let mut seen_default = None;
|
|
||||||
for param in &generics.params {
|
for param in &generics.params {
|
||||||
match (¶m.kind, seen_non_lifetime_param) {
|
if let GenericParamKind::Type { ref default, .. } = param.kind {
|
||||||
(GenericParamKind::Lifetime { .. }, true) => {
|
|
||||||
self.err_handler()
|
|
||||||
.span_err(param.ident.span, "lifetime parameters must be leading");
|
|
||||||
},
|
|
||||||
(GenericParamKind::Lifetime { .. }, false) => {}
|
|
||||||
(GenericParamKind::Type { ref default, .. }, _) => {
|
|
||||||
seen_non_lifetime_param = true;
|
|
||||||
if default.is_some() {
|
if default.is_some() {
|
||||||
seen_default = Some(param.ident.span);
|
prev_ty_default = Some(param.ident.span);
|
||||||
} else if let Some(span) = seen_default {
|
} else if let Some(span) = prev_ty_default {
|
||||||
self.err_handler()
|
self.err_handler()
|
||||||
.span_err(span, "type parameters with a default must be trailing");
|
.span_err(span, "type parameters with a default must be trailing");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
validate_generics_order(self.err_handler(), generics.params.iter().map(|param| {
|
||||||
|
(match param.kind {
|
||||||
|
GenericParamKind::Lifetime { .. } => ParamKindOrd::Lifetime,
|
||||||
|
GenericParamKind::Type { .. } => ParamKindOrd::Type,
|
||||||
|
GenericParamKind::Const { .. } => ParamKindOrd::Const,
|
||||||
|
}, param.ident.span, Some(param.ident))
|
||||||
|
}), GenericPosition::Param, generics.span);
|
||||||
|
|
||||||
for predicate in &generics.where_clause.predicates {
|
for predicate in &generics.where_clause.predicates {
|
||||||
if let WherePredicate::EqPredicate(ref predicate) = *predicate {
|
if let WherePredicate::EqPredicate(ref predicate) = *predicate {
|
||||||
self.err_handler()
|
self.err_handler()
|
||||||
|
@ -554,6 +649,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||||
supported in where clauses (see #20041)");
|
supported in where clauses (see #20041)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
visit::walk_generics(self, generics)
|
visit::walk_generics(self, generics)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue