|
9 | 9 | use std::mem;
|
10 | 10 | use syntax::print::pprust;
|
11 | 11 | use rustc::lint;
|
| 12 | +use rustc::lint::builtin::{BuiltinLintDiagnostics, NESTED_IMPL_TRAIT}; |
12 | 13 | use rustc::session::Session;
|
13 | 14 | use rustc_data_structures::fx::FxHashMap;
|
14 | 15 | use syntax::ast::*;
|
@@ -36,9 +37,32 @@ struct AstValidator<'a> {
|
36 | 37 | // Used to ban `impl Trait` in path projections like `<impl Iterator>::Item`
|
37 | 38 | // or `Foo::Bar<impl Trait>`
|
38 | 39 | is_impl_trait_banned: bool,
|
| 40 | + |
| 41 | + // rust-lang/rust#57979: the ban of nested `impl Trait` was buggy |
| 42 | + // until sometime after PR #57730 landed: it would jump directly |
| 43 | + // to walk_ty rather than visit_ty (or skip recurring entirely for |
| 44 | + // impl trait in projections), and thus miss some cases. We track |
| 45 | + // whether we should downgrade to a warning for short-term via |
| 46 | + // these booleans. |
| 47 | + warning_period_57979_nested_impl_trait: bool, |
| 48 | + warning_period_57979_impl_trait_in_proj: bool, |
39 | 49 | }
|
40 | 50 |
|
41 | 51 | impl<'a> AstValidator<'a> {
|
| 52 | + fn with_nested_impl_trait_warning<T>(&mut self, v: bool, f: impl FnOnce(&mut Self) -> T) -> T { |
| 53 | + let old = mem::replace(&mut self.warning_period_57979_nested_impl_trait, v); |
| 54 | + let ret = f(self); |
| 55 | + self.warning_period_57979_nested_impl_trait = old; |
| 56 | + ret |
| 57 | + } |
| 58 | + |
| 59 | + fn with_impl_trait_in_proj_warning<T>(&mut self, v: bool, f: impl FnOnce(&mut Self) -> T) -> T { |
| 60 | + let old = mem::replace(&mut self.warning_period_57979_impl_trait_in_proj, v); |
| 61 | + let ret = f(self); |
| 62 | + self.warning_period_57979_impl_trait_in_proj = old; |
| 63 | + ret |
| 64 | + } |
| 65 | + |
42 | 66 | fn with_banned_impl_trait(&mut self, f: impl FnOnce(&mut Self)) {
|
43 | 67 | let old = mem::replace(&mut self.is_impl_trait_banned, true);
|
44 | 68 | f(self);
|
@@ -406,22 +430,41 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
406 | 430 | }
|
407 | 431 | TyKind::ImplTrait(_, ref bounds) => {
|
408 | 432 | if self.is_impl_trait_banned {
|
409 |
| - struct_span_err!(self.session, ty.span, E0667, |
410 |
| - "`impl Trait` is not allowed in path parameters").emit(); |
| 433 | + if self.warning_period_57979_impl_trait_in_proj { |
| 434 | + self.session.buffer_lint( |
| 435 | + NESTED_IMPL_TRAIT, ty.id, ty.span, |
| 436 | + "`impl Trait` is not allowed in path parameters"); |
| 437 | + } else { |
| 438 | + struct_span_err!(self.session, ty.span, E0667, |
| 439 | + "`impl Trait` is not allowed in path parameters").emit(); |
| 440 | + } |
411 | 441 | }
|
412 | 442 |
|
413 | 443 | if let Some(outer_impl_trait) = self.outer_impl_trait {
|
414 |
| - struct_span_err!(self.session, ty.span, E0666, |
415 |
| - "nested `impl Trait` is not allowed") |
416 |
| - .span_label(outer_impl_trait, "outer `impl Trait`") |
417 |
| - .span_label(ty.span, "nested `impl Trait` here") |
418 |
| - .emit(); |
419 |
| - |
| 444 | + if self.warning_period_57979_nested_impl_trait { |
| 445 | + self.session.buffer_lint_with_diagnostic( |
| 446 | + NESTED_IMPL_TRAIT, ty.id, ty.span, |
| 447 | + "nested `impl Trait` is not allowed", |
| 448 | + BuiltinLintDiagnostics::NestedImplTrait { |
| 449 | + outer_impl_trait_span: outer_impl_trait, |
| 450 | + inner_impl_trait_span: ty.span, |
| 451 | + }); |
| 452 | + } else { |
| 453 | + struct_span_err!(self.session, ty.span, E0666, |
| 454 | + "nested `impl Trait` is not allowed") |
| 455 | + .span_label(outer_impl_trait, "outer `impl Trait`") |
| 456 | + .span_label(ty.span, "nested `impl Trait` here") |
| 457 | + .emit(); |
| 458 | + } |
420 | 459 | }
|
| 460 | + |
421 | 461 | if !bounds.iter()
|
422 | 462 | .any(|b| if let GenericBound::Trait(..) = *b { true } else { false }) {
|
423 | 463 | self.err_handler().span_err(ty.span, "at least one trait must be specified");
|
424 | 464 | }
|
| 465 | + |
| 466 | + self.with_impl_trait_in_proj_warning(true, |this| this.walk_ty(ty)); |
| 467 | + return; |
425 | 468 | }
|
426 | 469 | _ => {}
|
427 | 470 | }
|
@@ -606,18 +649,23 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
606 | 649 | GenericArg::Const(..) => ParamKindOrd::Const,
|
607 | 650 | }, arg.span(), None)
|
608 | 651 | }), GenericPosition::Arg, generic_args.span());
|
609 |
| - // Type bindings such as `Item=impl Debug` in `Iterator<Item=Debug>` |
610 |
| - // are allowed to contain nested `impl Trait`. |
611 |
| - self.with_impl_trait(None, |this| { |
612 |
| - walk_list!(this, visit_assoc_type_binding, &data.bindings); |
| 652 | + |
| 653 | + self.with_nested_impl_trait_warning(true, |this| { |
| 654 | + // Type bindings such as `Item=impl Debug` in `Iterator<Item=Debug>` |
| 655 | + // are allowed to contain nested `impl Trait`. |
| 656 | + this.with_impl_trait(None, |this| { |
| 657 | + walk_list!(this, visit_assoc_type_binding, &data.bindings); |
| 658 | + }); |
613 | 659 | });
|
614 | 660 | }
|
615 | 661 | GenericArgs::Parenthesized(ref data) => {
|
616 | 662 | walk_list!(self, visit_ty, &data.inputs);
|
617 | 663 | if let Some(ref type_) = data.output {
|
618 |
| - // `-> Foo` syntax is essentially an associated type binding, |
619 |
| - // so it is also allowed to contain nested `impl Trait`. |
620 |
| - self.with_impl_trait(None, |this| this.visit_ty(type_)); |
| 664 | + self.with_nested_impl_trait_warning(true, |this| { |
| 665 | + // `-> Foo` syntax is essentially an associated type binding, |
| 666 | + // so it is also allowed to contain nested `impl Trait`. |
| 667 | + this.with_impl_trait(None, |this| this.visit_ty(type_)); |
| 668 | + }); |
621 | 669 | }
|
622 | 670 | }
|
623 | 671 | }
|
@@ -719,6 +767,8 @@ pub fn check_crate(session: &Session, krate: &Crate) -> (bool, bool) {
|
719 | 767 | has_global_allocator: false,
|
720 | 768 | outer_impl_trait: None,
|
721 | 769 | is_impl_trait_banned: false,
|
| 770 | + warning_period_57979_nested_impl_trait: false, |
| 771 | + warning_period_57979_impl_trait_in_proj: false, |
722 | 772 | };
|
723 | 773 | visit::walk_crate(&mut validator, krate);
|
724 | 774 |
|
|
0 commit comments