Check for ?const
in invalid contexts during AST validation
This commit is contained in:
parent
31edbe9aca
commit
d843e002bb
@ -24,6 +24,23 @@ use syntax::walk_list;
|
||||
|
||||
use rustc_error_codes::*;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum BoundContext {
|
||||
ImplTrait,
|
||||
TraitBounds,
|
||||
TraitObject,
|
||||
}
|
||||
|
||||
impl BoundContext {
|
||||
fn description(&self) -> &'static str {
|
||||
match self {
|
||||
Self::ImplTrait => "`impl Trait`",
|
||||
Self::TraitBounds => "supertraits",
|
||||
Self::TraitObject => "trait objects",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct AstValidator<'a> {
|
||||
session: &'a Session,
|
||||
has_proc_macro_decls: bool,
|
||||
@ -33,6 +50,11 @@ struct AstValidator<'a> {
|
||||
/// e.g., `impl Iterator<Item = impl Debug>`.
|
||||
outer_impl_trait: Option<Span>,
|
||||
|
||||
/// Tracks the context in which a bound can appear.
|
||||
///
|
||||
/// This is used to forbid `?const Trait` bounds in certain contexts.
|
||||
bound_context_stack: Vec<Option<BoundContext>>,
|
||||
|
||||
/// Used to ban `impl Trait` in path projections like `<impl Iterator>::Item`
|
||||
/// or `Foo::Bar<impl Trait>`
|
||||
is_impl_trait_banned: bool,
|
||||
@ -58,9 +80,21 @@ impl<'a> AstValidator<'a> {
|
||||
}
|
||||
|
||||
fn with_impl_trait(&mut self, outer: Option<Span>, f: impl FnOnce(&mut Self)) {
|
||||
self.bound_context_stack.push(outer.map(|_| BoundContext::ImplTrait));
|
||||
let old = mem::replace(&mut self.outer_impl_trait, outer);
|
||||
f(self);
|
||||
self.outer_impl_trait = old;
|
||||
self.bound_context_stack.pop();
|
||||
}
|
||||
|
||||
fn with_bound_context(&mut self, ctx: Option<BoundContext>, f: impl FnOnce(&mut Self)) {
|
||||
self.bound_context_stack.push(ctx);
|
||||
f(self);
|
||||
self.bound_context_stack.pop();
|
||||
}
|
||||
|
||||
fn innermost_bound_context(&mut self) -> Option<BoundContext> {
|
||||
self.bound_context_stack.iter().rev().find(|x| x.is_some()).copied().flatten()
|
||||
}
|
||||
|
||||
fn visit_assoc_ty_constraint_from_generic_args(&mut self, constraint: &'a AssocTyConstraint) {
|
||||
@ -84,6 +118,11 @@ impl<'a> AstValidator<'a> {
|
||||
TyKind::ImplTrait(..) => {
|
||||
self.with_impl_trait(Some(t.span), |this| visit::walk_ty(this, t))
|
||||
}
|
||||
TyKind::TraitObject(..) => {
|
||||
self.with_bound_context(Some(BoundContext::TraitObject), |this| {
|
||||
visit::walk_ty(this, t)
|
||||
});
|
||||
}
|
||||
TyKind::Path(ref qself, ref path) => {
|
||||
// We allow these:
|
||||
// - `Option<impl Trait>`
|
||||
@ -192,6 +231,8 @@ impl<'a> AstValidator<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(ecstaticmorse): Instead, use the `bound_context_stack` to check this in
|
||||
// `visit_param_bound`.
|
||||
fn no_questions_in_bounds(&self, bounds: &GenericBounds, where_: &str, is_trait: bool) {
|
||||
for bound in bounds {
|
||||
if let GenericBound::Trait(ref poly, TraitBoundModifier::Maybe) = *bound {
|
||||
@ -697,6 +738,15 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
}
|
||||
}
|
||||
self.no_questions_in_bounds(bounds, "supertraits", true);
|
||||
|
||||
// Equivalent of `visit::walk_item` for `ItemKind::Trait` that inserts a bound
|
||||
// context for the supertraits.
|
||||
self.visit_generics(generics);
|
||||
self.with_bound_context(Some(BoundContext::TraitBounds), |this| {
|
||||
walk_list!(this, visit_param_bound, bounds);
|
||||
});
|
||||
walk_list!(self, visit_trait_item, trait_items);
|
||||
return;
|
||||
}
|
||||
ItemKind::Mod(_) => {
|
||||
// Ensure that `path` attributes on modules are recorded as used (cf. issue #35584).
|
||||
@ -841,6 +891,29 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
visit::walk_generic_param(self, param);
|
||||
}
|
||||
|
||||
fn visit_param_bound(&mut self, bound: &'a GenericBound) {
|
||||
if let GenericBound::Trait(poly, maybe_bound) = bound {
|
||||
match poly.trait_ref.constness {
|
||||
Some(Constness::NotConst) => {
|
||||
if *maybe_bound == TraitBoundModifier::Maybe {
|
||||
self.err_handler()
|
||||
.span_err(bound.span(), "`?const` and `?` are mutually exclusive");
|
||||
}
|
||||
|
||||
if let Some(ctx) = self.innermost_bound_context() {
|
||||
let msg = format!("`?const` is not permitted in {}", ctx.description());
|
||||
self.err_handler().span_err(bound.span(), &msg);
|
||||
}
|
||||
}
|
||||
|
||||
Some(Constness::Const) => bug!("Parser should reject bare `const` on bounds"),
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
|
||||
visit::walk_param_bound(self, bound)
|
||||
}
|
||||
|
||||
fn visit_pat(&mut self, pat: &'a Pat) {
|
||||
match pat.kind {
|
||||
PatKind::Lit(ref expr) => {
|
||||
@ -949,6 +1022,7 @@ pub fn check_crate(session: &Session, krate: &Crate, lints: &mut lint::LintBuffe
|
||||
session,
|
||||
has_proc_macro_decls: false,
|
||||
outer_impl_trait: None,
|
||||
bound_context_stack: Vec::new(),
|
||||
is_impl_trait_banned: false,
|
||||
is_assoc_ty_bound_banned: false,
|
||||
lint_buffer: lints,
|
||||
|
Loading…
x
Reference in New Issue
Block a user