Skip to content

Commit 11874a0

Browse files
varkoryodaldevoid
andcommitted
Validate generic parameter and argument order in ast_validation
Co-Authored-By: Gabriel Smith <[email protected]>
1 parent 2fec52b commit 11874a0

File tree

1 file changed

+112
-16
lines changed

1 file changed

+112
-16
lines changed

src/librustc_passes/ast_validation.rs

+112-16
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use std::mem;
1010
use rustc::lint;
1111
use rustc::session::Session;
12+
use rustc_data_structures::fx::FxHashMap;
1213
use syntax::ast::*;
1314
use syntax::attr;
1415
use syntax::source_map::Spanned;
@@ -271,7 +272,74 @@ impl<'a> AstValidator<'a> {
271272
_ => None,
272273
}
273274
}
275+
}
274276

277+
enum GenericPosition {
278+
Param,
279+
Arg,
280+
}
281+
282+
fn validate_generics_order<'a>(
283+
handler: &errors::Handler,
284+
generics: impl Iterator<Item = (ParamKindOrd, Span, Option<Ident>)>,
285+
pos: GenericPosition,
286+
span: Span,
287+
) {
288+
let mut max_param: Option<ParamKindOrd> = None;
289+
let mut out_of_order = FxHashMap::default();
290+
let mut param_idents = vec![];
291+
292+
for (kind, span, ident) in generics {
293+
if let Some(ident) = ident {
294+
param_idents.push((kind, param_idents.len(), ident));
295+
}
296+
let max_param = &mut max_param;
297+
match max_param {
298+
Some(max_param) if *max_param > kind => {
299+
let entry = out_of_order.entry(kind).or_insert((*max_param, vec![]));
300+
entry.1.push(span);
301+
}
302+
Some(_) | None => *max_param = Some(kind),
303+
};
304+
}
305+
306+
let mut ordered_params = "<".to_string();
307+
if !out_of_order.is_empty() {
308+
param_idents.sort_by_key(|&(po, i, _)| (po, i));
309+
let mut first = true;
310+
for (_, _, ident) in param_idents {
311+
if !first {
312+
ordered_params += ", ";
313+
}
314+
ordered_params += &ident.as_str();
315+
first = false;
316+
}
317+
}
318+
ordered_params += ">";
319+
320+
let pos_str = match pos {
321+
GenericPosition::Param => "parameter",
322+
GenericPosition::Arg => "argument",
323+
};
324+
325+
for (param_ord, (max_param, spans)) in out_of_order {
326+
let mut err = handler.struct_span_err(spans,
327+
&format!(
328+
"{} {pos}s must be declared prior to {} {pos}s",
329+
param_ord,
330+
max_param,
331+
pos = pos_str,
332+
));
333+
if let GenericPosition::Param = pos {
334+
err.span_suggestion(
335+
span,
336+
&format!("reorder the {}s: lifetimes, then types, then consts", pos_str),
337+
ordered_params.clone(),
338+
Applicability::MachineApplicable,
339+
);
340+
}
341+
err.emit();
342+
}
275343
}
276344

277345
impl<'a> Visitor<'a> for AstValidator<'a> {
@@ -412,6 +480,26 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
412480
.note("only trait implementations may be annotated with default").emit();
413481
}
414482
}
483+
ItemKind::Fn(_, header, ref generics, _) => {
484+
// We currently do not permit const generics in `const fn`, as
485+
// this is tantamount to allowing compile-time dependent typing.
486+
if header.constness.node == Constness::Const {
487+
// Look for const generics and error if we find any.
488+
for param in &generics.params {
489+
match param.kind {
490+
GenericParamKind::Const { .. } => {
491+
self.err_handler()
492+
.struct_span_err(
493+
item.span,
494+
"const parameters are not permitted in `const fn`",
495+
)
496+
.emit();
497+
}
498+
_ => {}
499+
}
500+
}
501+
}
502+
}
415503
ItemKind::ForeignMod(..) => {
416504
self.invalid_visibility(
417505
&item.vis,
@@ -508,6 +596,13 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
508596
match *generic_args {
509597
GenericArgs::AngleBracketed(ref data) => {
510598
walk_list!(self, visit_generic_arg, &data.args);
599+
validate_generics_order(self.err_handler(), data.args.iter().map(|arg| {
600+
(match arg {
601+
GenericArg::Lifetime(..) => ParamKindOrd::Lifetime,
602+
GenericArg::Type(..) => ParamKindOrd::Type,
603+
GenericArg::Const(..) => ParamKindOrd::Const,
604+
}, arg.span(), None)
605+
}), GenericPosition::Arg, generic_args.span());
511606
// Type bindings such as `Item=impl Debug` in `Iterator<Item=Debug>`
512607
// are allowed to contain nested `impl Trait`.
513608
self.with_impl_trait(None, |this| {
@@ -526,34 +621,35 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
526621
}
527622

528623
fn visit_generics(&mut self, generics: &'a Generics) {
529-
let mut seen_non_lifetime_param = false;
530-
let mut seen_default = None;
624+
let mut prev_ty_default = None;
531625
for param in &generics.params {
532-
match (&param.kind, seen_non_lifetime_param) {
533-
(GenericParamKind::Lifetime { .. }, true) => {
626+
if let GenericParamKind::Type { ref default, .. } = param.kind {
627+
if default.is_some() {
628+
prev_ty_default = Some(param.ident.span);
629+
} else if let Some(span) = prev_ty_default {
534630
self.err_handler()
535-
.span_err(param.ident.span, "lifetime parameters must be leading");
536-
},
537-
(GenericParamKind::Lifetime { .. }, false) => {}
538-
(GenericParamKind::Type { ref default, .. }, _) => {
539-
seen_non_lifetime_param = true;
540-
if default.is_some() {
541-
seen_default = Some(param.ident.span);
542-
} else if let Some(span) = seen_default {
543-
self.err_handler()
544-
.span_err(span, "type parameters with a default must be trailing");
545-
break;
546-
}
631+
.span_err(span, "type parameters with a default must be trailing");
632+
break;
547633
}
548634
}
549635
}
636+
637+
validate_generics_order(self.err_handler(), generics.params.iter().map(|param| {
638+
(match param.kind {
639+
GenericParamKind::Lifetime { .. } => ParamKindOrd::Lifetime,
640+
GenericParamKind::Type { .. } => ParamKindOrd::Type,
641+
GenericParamKind::Const { .. } => ParamKindOrd::Const,
642+
}, param.ident.span, Some(param.ident))
643+
}), GenericPosition::Param, generics.span);
644+
550645
for predicate in &generics.where_clause.predicates {
551646
if let WherePredicate::EqPredicate(ref predicate) = *predicate {
552647
self.err_handler()
553648
.span_err(predicate.span, "equality constraints are not yet \
554649
supported in where clauses (see #20041)");
555650
}
556651
}
652+
557653
visit::walk_generics(self, generics)
558654
}
559655

0 commit comments

Comments
 (0)