Skip to content

Commit 13dc584

Browse files
committed
Merge visitors in AST validation
1 parent daa53a5 commit 13dc584

File tree

1 file changed

+111
-144
lines changed

1 file changed

+111
-144
lines changed

src/librustc_passes/ast_validation.rs

+111-144
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,79 @@ use errors::Applicability;
2020

2121
struct AstValidator<'a> {
2222
session: &'a Session,
23+
24+
// Used to ban nested `impl Trait`, e.g., `impl Into<impl Debug>`.
25+
// Nested `impl Trait` _is_ allowed in associated type position,
26+
// e.g `impl Iterator<Item=impl Debug>`
27+
outer_impl_trait: Option<Span>,
28+
29+
// Used to ban `impl Trait` in path projections like `<impl Iterator>::Item`
30+
// or `Foo::Bar<impl Trait>`
31+
is_impl_trait_banned: bool,
2332
}
2433

2534
impl<'a> AstValidator<'a> {
35+
fn with_banned_impl_trait<F>(&mut self, f: F)
36+
where F: FnOnce(&mut Self)
37+
{
38+
let old_is_impl_trait_banned = self.is_impl_trait_banned;
39+
self.is_impl_trait_banned = true;
40+
f(self);
41+
self.is_impl_trait_banned = old_is_impl_trait_banned;
42+
}
43+
44+
fn with_impl_trait<F>(&mut self, outer_impl_trait: Option<Span>, f: F)
45+
where F: FnOnce(&mut Self)
46+
{
47+
let old_outer_impl_trait = self.outer_impl_trait;
48+
self.outer_impl_trait = outer_impl_trait;
49+
f(self);
50+
self.outer_impl_trait = old_outer_impl_trait;
51+
}
52+
53+
// Mirrors visit::walk_ty, but tracks relevant state
54+
fn walk_ty(&mut self, t: &'a Ty) {
55+
match t.node {
56+
TyKind::ImplTrait(..) => {
57+
self.with_impl_trait(Some(t.span), |this| visit::walk_ty(this, t))
58+
}
59+
TyKind::Path(ref qself, ref path) => {
60+
// We allow these:
61+
// - `Option<impl Trait>`
62+
// - `option::Option<impl Trait>`
63+
// - `option::Option<T>::Foo<impl Trait>
64+
//
65+
// But not these:
66+
// - `<impl Trait>::Foo`
67+
// - `option::Option<impl Trait>::Foo`.
68+
//
69+
// To implement this, we disallow `impl Trait` from `qself`
70+
// (for cases like `<impl Trait>::Foo>`)
71+
// but we allow `impl Trait` in `GenericArgs`
72+
// iff there are no more PathSegments.
73+
if let Some(ref qself) = *qself {
74+
// `impl Trait` in `qself` is always illegal
75+
self.with_banned_impl_trait(|this| this.visit_ty(&qself.ty));
76+
}
77+
78+
// Note that there should be a call to visit_path here,
79+
// so if any logic is added to process `Path`s a call to it should be
80+
// added both in visit_path and here. This code mirrors visit::walk_path.
81+
for (i, segment) in path.segments.iter().enumerate() {
82+
// Allow `impl Trait` iff we're on the final path segment
83+
if i == path.segments.len() - 1 {
84+
self.visit_path_segment(path.span, segment);
85+
} else {
86+
self.with_banned_impl_trait(|this| {
87+
this.visit_path_segment(path.span, segment)
88+
});
89+
}
90+
}
91+
}
92+
_ => visit::walk_ty(self, t),
93+
}
94+
}
95+
2696
fn err_handler(&self) -> &errors::Handler {
2797
&self.session.diagnostic()
2898
}
@@ -267,6 +337,19 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
267337
self.no_questions_in_bounds(bounds, "trait object types", false);
268338
}
269339
TyKind::ImplTrait(_, ref bounds) => {
340+
if self.is_impl_trait_banned {
341+
struct_span_err!(self.session, ty.span, E0667,
342+
"`impl Trait` is not allowed in path parameters").emit();
343+
}
344+
345+
if let Some(outer_impl_trait) = self.outer_impl_trait {
346+
struct_span_err!(self.session, ty.span, E0666,
347+
"nested `impl Trait` is not allowed")
348+
.span_label(outer_impl_trait, "outer `impl Trait`")
349+
.span_label(ty.span, "nested `impl Trait` here")
350+
.emit();
351+
352+
}
270353
if !bounds.iter()
271354
.any(|b| if let GenericBound::Trait(..) = *b { true } else { false }) {
272355
self.err_handler().span_err(ty.span, "at least one trait must be specified");
@@ -275,7 +358,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
275358
_ => {}
276359
}
277360

278-
visit::walk_ty(self, ty)
361+
self.walk_ty(ty)
279362
}
280363

281364
fn visit_label(&mut self, label: &'a Label) {
@@ -414,6 +497,28 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
414497
visit::walk_foreign_item(self, fi)
415498
}
416499

500+
// Mirrors visit::walk_generic_args, but tracks relevant state
501+
fn visit_generic_args(&mut self, _: Span, generic_args: &'a GenericArgs) {
502+
match *generic_args {
503+
GenericArgs::AngleBracketed(ref data) => {
504+
walk_list!(self, visit_generic_arg, &data.args);
505+
// Type bindings such as `Item=impl Debug` in `Iterator<Item=Debug>`
506+
// are allowed to contain nested `impl Trait`.
507+
self.with_impl_trait(None, |this| {
508+
walk_list!(this, visit_assoc_type_binding, &data.bindings);
509+
});
510+
}
511+
GenericArgs::Parenthesized(ref data) => {
512+
walk_list!(self, visit_ty, &data.inputs);
513+
if let Some(ref type_) = data.output {
514+
// `-> Foo` syntax is essentially an associated type binding,
515+
// so it is also allowed to contain nested `impl Trait`.
516+
self.with_impl_trait(None, |this| visit::walk_ty(this, type_));
517+
}
518+
}
519+
}
520+
}
521+
417522
fn visit_generics(&mut self, generics: &'a Generics) {
418523
let mut seen_non_lifetime_param = false;
419524
let mut seen_default = None;
@@ -490,148 +595,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
490595
}
491596
}
492597

493-
// Bans nested `impl Trait`, e.g., `impl Into<impl Debug>`.
494-
// Nested `impl Trait` _is_ allowed in associated type position,
495-
// e.g `impl Iterator<Item=impl Debug>`
496-
struct NestedImplTraitVisitor<'a> {
497-
session: &'a Session,
498-
outer_impl_trait: Option<Span>,
499-
}
500-
501-
impl<'a> NestedImplTraitVisitor<'a> {
502-
fn with_impl_trait<F>(&mut self, outer_impl_trait: Option<Span>, f: F)
503-
where F: FnOnce(&mut NestedImplTraitVisitor<'a>)
504-
{
505-
let old_outer_impl_trait = self.outer_impl_trait;
506-
self.outer_impl_trait = outer_impl_trait;
507-
f(self);
508-
self.outer_impl_trait = old_outer_impl_trait;
509-
}
510-
}
511-
512-
513-
impl<'a> Visitor<'a> for NestedImplTraitVisitor<'a> {
514-
fn visit_ty(&mut self, t: &'a Ty) {
515-
if let TyKind::ImplTrait(..) = t.node {
516-
if let Some(outer_impl_trait) = self.outer_impl_trait {
517-
struct_span_err!(self.session, t.span, E0666,
518-
"nested `impl Trait` is not allowed")
519-
.span_label(outer_impl_trait, "outer `impl Trait`")
520-
.span_label(t.span, "nested `impl Trait` here")
521-
.emit();
522-
523-
}
524-
self.with_impl_trait(Some(t.span), |this| visit::walk_ty(this, t));
525-
} else {
526-
visit::walk_ty(self, t);
527-
}
528-
}
529-
fn visit_generic_args(&mut self, _: Span, generic_args: &'a GenericArgs) {
530-
match *generic_args {
531-
GenericArgs::AngleBracketed(ref data) => {
532-
for arg in &data.args {
533-
self.visit_generic_arg(arg)
534-
}
535-
for type_binding in &data.bindings {
536-
// Type bindings such as `Item=impl Debug` in `Iterator<Item=Debug>`
537-
// are allowed to contain nested `impl Trait`.
538-
self.with_impl_trait(None, |this| visit::walk_ty(this, &type_binding.ty));
539-
}
540-
}
541-
GenericArgs::Parenthesized(ref data) => {
542-
for type_ in &data.inputs {
543-
self.visit_ty(type_);
544-
}
545-
if let Some(ref type_) = data.output {
546-
// `-> Foo` syntax is essentially an associated type binding,
547-
// so it is also allowed to contain nested `impl Trait`.
548-
self.with_impl_trait(None, |this| visit::walk_ty(this, type_));
549-
}
550-
}
551-
}
552-
}
553-
554-
fn visit_mac(&mut self, _mac: &Spanned<Mac_>) {
555-
// covered in AstValidator
556-
}
557-
}
558-
559-
// Bans `impl Trait` in path projections like `<impl Iterator>::Item` or `Foo::Bar<impl Trait>`.
560-
struct ImplTraitProjectionVisitor<'a> {
561-
session: &'a Session,
562-
is_banned: bool,
563-
}
564-
565-
impl<'a> ImplTraitProjectionVisitor<'a> {
566-
fn with_ban<F>(&mut self, f: F)
567-
where F: FnOnce(&mut ImplTraitProjectionVisitor<'a>)
568-
{
569-
let old_is_banned = self.is_banned;
570-
self.is_banned = true;
571-
f(self);
572-
self.is_banned = old_is_banned;
573-
}
574-
}
575-
576-
impl<'a> Visitor<'a> for ImplTraitProjectionVisitor<'a> {
577-
fn visit_ty(&mut self, t: &'a Ty) {
578-
match t.node {
579-
TyKind::ImplTrait(..) => {
580-
if self.is_banned {
581-
struct_span_err!(self.session, t.span, E0667,
582-
"`impl Trait` is not allowed in path parameters").emit();
583-
}
584-
}
585-
TyKind::Path(ref qself, ref path) => {
586-
// We allow these:
587-
// - `Option<impl Trait>`
588-
// - `option::Option<impl Trait>`
589-
// - `option::Option<T>::Foo<impl Trait>
590-
//
591-
// But not these:
592-
// - `<impl Trait>::Foo`
593-
// - `option::Option<impl Trait>::Foo`.
594-
//
595-
// To implement this, we disallow `impl Trait` from `qself`
596-
// (for cases like `<impl Trait>::Foo>`)
597-
// but we allow `impl Trait` in `GenericArgs`
598-
// iff there are no more PathSegments.
599-
if let Some(ref qself) = *qself {
600-
// `impl Trait` in `qself` is always illegal
601-
self.with_ban(|this| this.visit_ty(&qself.ty));
602-
}
603-
604-
for (i, segment) in path.segments.iter().enumerate() {
605-
// Allow `impl Trait` iff we're on the final path segment
606-
if i == path.segments.len() - 1 {
607-
visit::walk_path_segment(self, path.span, segment);
608-
} else {
609-
self.with_ban(|this|
610-
visit::walk_path_segment(this, path.span, segment));
611-
}
612-
}
613-
}
614-
_ => visit::walk_ty(self, t),
615-
}
616-
}
617-
618-
fn visit_mac(&mut self, _mac: &Spanned<Mac_>) {
619-
// covered in AstValidator
620-
}
621-
}
622-
623598
pub fn check_crate(session: &Session, krate: &Crate) {
624-
visit::walk_crate(
625-
&mut NestedImplTraitVisitor {
626-
session,
627-
outer_impl_trait: None,
628-
}, krate);
629-
630-
visit::walk_crate(
631-
&mut ImplTraitProjectionVisitor {
632-
session,
633-
is_banned: false,
634-
}, krate);
635-
636-
visit::walk_crate(&mut AstValidator { session }, krate)
599+
visit::walk_crate(&mut AstValidator {
600+
session,
601+
outer_impl_trait: None,
602+
is_impl_trait_banned: false,
603+
}, krate)
637604
}

0 commit comments

Comments
 (0)