diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 18:31:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 18:31:44 +0000 |
commit | c23a457e72abe608715ac76f076f47dc42af07a5 (patch) | |
tree | 2772049aaf84b5c9d0ed12ec8d86812f7a7904b6 /compiler/rustc_ast_passes | |
parent | Releasing progress-linux version 1.73.0+dfsg1-1~progress7.99u1. (diff) | |
download | rustc-c23a457e72abe608715ac76f076f47dc42af07a5.tar.xz rustc-c23a457e72abe608715ac76f076f47dc42af07a5.zip |
Merging upstream version 1.74.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_ast_passes')
-rw-r--r-- | compiler/rustc_ast_passes/messages.ftl | 22 | ||||
-rw-r--r-- | compiler/rustc_ast_passes/src/ast_validation.rs | 188 | ||||
-rw-r--r-- | compiler/rustc_ast_passes/src/errors.rs | 46 | ||||
-rw-r--r-- | compiler/rustc_ast_passes/src/feature_gate.rs | 25 |
4 files changed, 139 insertions, 142 deletions
diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index f323bb4c2..43020a930 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -1,3 +1,7 @@ +ast_passes_anon_struct_or_union_not_allowed = + anonymous {$struct_or_union}s are not allowed outside of unnamed struct or union fields + .label = anonymous {$struct_or_union} declared here + ast_passes_assoc_const_without_body = associated constant in `impl` without body .suggestion = provide a definition for the constant @@ -113,16 +117,6 @@ ast_passes_forbidden_default = `default` is only allowed on items in trait impls .label = `default` because of this -ast_passes_forbidden_let = - `let` expressions are not supported here - .note = only supported directly in conditions of `if` and `while` expressions - .not_supported_or = `||` operators are not supported in let chain expressions - .not_supported_parentheses = `let`s wrapped in parentheses are not supported in a context with let chains - -ast_passes_forbidden_let_stable = - expected expression, found statement (`let`) - .note = variable declaration using `let` is a statement - ast_passes_forbidden_lifetime_bound = lifetime bounds cannot be used in this context @@ -162,6 +156,14 @@ ast_passes_inherent_cannot_be = inherent impls cannot be {$annotation} ast_passes_invalid_label = invalid label name `{$name}` +ast_passes_invalid_unnamed_field = + unnamed fields are not allowed outside of structs or unions + .label = unnamed field declared here + +ast_passes_invalid_unnamed_field_ty = + unnamed fields can only have struct or union types + .label = not a struct or union + ast_passes_item_underscore = `{$kind}` items in this context need a name .label = `_` is not a valid name for this `{$kind}` item diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index bd3e676da..7bc685a54 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -14,14 +14,12 @@ use rustc_ast::{walk_list, StaticItem}; use rustc_ast_pretty::pprust::{self, State}; use rustc_data_structures::fx::FxIndexMap; use rustc_feature::Features; -use rustc_macros::Subdiagnostic; use rustc_parse::validate_attr; use rustc_session::lint::builtin::{ DEPRECATED_WHERE_CLAUSE_LOCATION, MISSING_ABI, PATTERNS_IN_FNS_WITHOUT_BODY, }; use rustc_session::lint::{BuiltinLintDiagnostics, LintBuffer}; use rustc_session::Session; -use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Span; use rustc_target::spec::abi; @@ -69,9 +67,6 @@ struct AstValidator<'a> { /// or `Foo::Bar<impl Trait>` is_impl_trait_banned: bool, - /// See [ForbiddenLetReason] - forbidden_let_reason: Option<ForbiddenLetReason>, - lint_buffer: &'a mut LintBuffer, } @@ -118,26 +113,6 @@ impl<'a> AstValidator<'a> { self.with_tilde_const(Some(ctx), f) } - fn with_let_management( - &mut self, - forbidden_let_reason: Option<ForbiddenLetReason>, - f: impl FnOnce(&mut Self, Option<ForbiddenLetReason>), - ) { - let old = mem::replace(&mut self.forbidden_let_reason, forbidden_let_reason); - f(self, old); - self.forbidden_let_reason = old; - } - - /// Emits an error banning the `let` expression provided in the given location. - fn ban_let_expr(&self, expr: &'a Expr, forbidden_let_reason: ForbiddenLetReason) { - let sess = &self.session; - if sess.opts.unstable_features.is_nightly_build() { - sess.emit_err(errors::ForbiddenLet { span: expr.span, reason: forbidden_let_reason }); - } else { - sess.emit_err(errors::ForbiddenLetStable { span: expr.span }); - } - } - fn check_type_alias_where_clause_location( &mut self, ty_alias: &TyAlias, @@ -223,10 +198,27 @@ impl<'a> AstValidator<'a> { } } } + TyKind::AnonStruct(ref fields, ..) | TyKind::AnonUnion(ref fields, ..) => { + walk_list!(self, visit_field_def, fields) + } _ => visit::walk_ty(self, t), } } + fn visit_struct_field_def(&mut self, field: &'a FieldDef) { + if let Some(ident) = field.ident && + ident.name == kw::Underscore { + self.check_unnamed_field_ty(&field.ty, ident.span); + self.visit_vis(&field.vis); + self.visit_ident(ident); + self.visit_ty_common(&field.ty); + self.walk_ty(&field.ty); + walk_list!(self, visit_attribute, &field.attrs); + } else { + self.visit_field_def(field); + } + } + fn err_handler(&self) -> &rustc_errors::Handler { &self.session.diagnostic() } @@ -264,6 +256,42 @@ impl<'a> AstValidator<'a> { } } + fn check_unnamed_field_ty(&self, ty: &Ty, span: Span) { + if matches!( + &ty.kind, + // We already checked for `kw::Underscore` before calling this function, + // so skip the check + TyKind::AnonStruct(..) | TyKind::AnonUnion(..) + // If the anonymous field contains a Path as type, we can't determine + // if the path is a valid struct or union, so skip the check + | TyKind::Path(..) + ) { + return; + } + self.err_handler().emit_err(errors::InvalidUnnamedFieldTy { span, ty_span: ty.span }); + } + + fn deny_anon_struct_or_union(&self, ty: &Ty) { + let struct_or_union = match &ty.kind { + TyKind::AnonStruct(..) => "struct", + TyKind::AnonUnion(..) => "union", + _ => return, + }; + self.err_handler() + .emit_err(errors::AnonStructOrUnionNotAllowed { struct_or_union, span: ty.span }); + } + + fn deny_unnamed_field(&self, field: &FieldDef) { + if let Some(ident) = field.ident && + ident.name == kw::Underscore { + self.err_handler() + .emit_err(errors::InvalidUnnamedField { + span: field.span, + ident_span: ident.span + }); + } + } + fn check_trait_fn_not_const(&self, constness: Const) { if let Const::Yes(span) = constness { self.session.emit_err(errors::TraitFnConst { span }); @@ -726,69 +754,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> { validate_attr::check_attr(&self.session.parse_sess, attr); } - fn visit_expr(&mut self, expr: &'a Expr) { - self.with_let_management(Some(ForbiddenLetReason::GenericForbidden), |this, forbidden_let_reason| { - match &expr.kind { - ExprKind::Binary(Spanned { node: BinOpKind::Or, span }, lhs, rhs) => { - let local_reason = Some(ForbiddenLetReason::NotSupportedOr(*span)); - this.with_let_management(local_reason, |this, _| this.visit_expr(lhs)); - this.with_let_management(local_reason, |this, _| this.visit_expr(rhs)); - } - ExprKind::If(cond, then, opt_else) => { - this.visit_block(then); - walk_list!(this, visit_expr, opt_else); - this.with_let_management(None, |this, _| this.visit_expr(cond)); - return; - } - ExprKind::Let(..) if let Some(elem) = forbidden_let_reason => { - this.ban_let_expr(expr, elem); - }, - ExprKind::Match(scrutinee, arms) => { - this.visit_expr(scrutinee); - for arm in arms { - this.visit_expr(&arm.body); - this.visit_pat(&arm.pat); - walk_list!(this, visit_attribute, &arm.attrs); - if let Some(guard) = &arm.guard { - this.with_let_management(None, |this, _| { - this.visit_expr(guard) - }); - } - } - } - ExprKind::Paren(local_expr) => { - fn has_let_expr(expr: &Expr) -> bool { - match &expr.kind { - ExprKind::Binary(_, lhs, rhs) => has_let_expr(lhs) || has_let_expr(rhs), - ExprKind::Let(..) => true, - _ => false, - } - } - let local_reason = if has_let_expr(local_expr) { - Some(ForbiddenLetReason::NotSupportedParentheses(local_expr.span)) - } - else { - forbidden_let_reason - }; - this.with_let_management(local_reason, |this, _| this.visit_expr(local_expr)); - } - ExprKind::Binary(Spanned { node: BinOpKind::And, .. }, ..) => { - this.with_let_management(forbidden_let_reason, |this, _| visit::walk_expr(this, expr)); - return; - } - ExprKind::While(cond, then, opt_label) => { - walk_list!(this, visit_label, opt_label); - this.visit_block(then); - this.with_let_management(None, |this, _| this.visit_expr(cond)); - return; - } - _ => visit::walk_expr(this, expr), - } - }); - } - fn visit_ty(&mut self, ty: &'a Ty) { self.visit_ty_common(ty); + self.deny_anon_struct_or_union(ty); self.walk_ty(ty) } @@ -803,6 +771,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } fn visit_field_def(&mut self, field: &'a FieldDef) { + self.deny_unnamed_field(field); visit::walk_field_def(self, field) } @@ -995,10 +964,38 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.check_mod_file_item_asciionly(item.ident); } } - ItemKind::Union(vdata, ..) => { + ItemKind::Struct(vdata, generics) => match vdata { + // Duplicating the `Visitor` logic allows catching all cases + // of `Anonymous(Struct, Union)` outside of a field struct or union. + // + // Inside `visit_ty` the validator catches every `Anonymous(Struct, Union)` it + // encounters, and only on `ItemKind::Struct` and `ItemKind::Union` + // it uses `visit_ty_common`, which doesn't contain that specific check. + VariantData::Struct(fields, ..) => { + self.visit_vis(&item.vis); + self.visit_ident(item.ident); + self.visit_generics(generics); + walk_list!(self, visit_struct_field_def, fields); + walk_list!(self, visit_attribute, &item.attrs); + return; + } + _ => {} + }, + ItemKind::Union(vdata, generics) => { if vdata.fields().is_empty() { self.err_handler().emit_err(errors::FieldlessUnion { span: item.span }); } + match vdata { + VariantData::Struct(fields, ..) => { + self.visit_vis(&item.vis); + self.visit_ident(item.ident); + self.visit_generics(generics); + walk_list!(self, visit_struct_field_def, fields); + walk_list!(self, visit_attribute, &item.attrs); + return; + } + _ => {} + } } ItemKind::Const(box ConstItem { defaultness, expr: None, .. }) => { self.check_defaultness(item.span, *defaultness); @@ -1518,26 +1515,9 @@ pub fn check_crate( outer_impl_trait: None, disallow_tilde_const: None, is_impl_trait_banned: false, - forbidden_let_reason: Some(ForbiddenLetReason::GenericForbidden), lint_buffer: lints, }; visit::walk_crate(&mut validator, krate); validator.has_proc_macro_decls } - -/// Used to forbid `let` expressions in certain syntactic locations. -#[derive(Clone, Copy, Subdiagnostic)] -pub(crate) enum ForbiddenLetReason { - /// `let` is not valid and the source environment is not important - GenericForbidden, - /// A let chain with the `||` operator - #[note(ast_passes_not_supported_or)] - NotSupportedOr(#[primary_span] Span), - /// A let chain with invalid parentheses - /// - /// For example, `let 1 = 1 && (expr && expr)` is allowed - /// but `(let 1 = 1 && (let 1 = 1 && (let 1 = 1))) && let a = 1` is not - #[note(ast_passes_not_supported_parentheses)] - NotSupportedParentheses(#[primary_span] Span), -} diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index a6f217d47..e74d94e43 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -5,28 +5,9 @@ use rustc_errors::AddToDiagnostic; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{symbol::Ident, Span, Symbol}; -use crate::ast_validation::ForbiddenLetReason; use crate::fluent_generated as fluent; #[derive(Diagnostic)] -#[diag(ast_passes_forbidden_let)] -#[note] -pub struct ForbiddenLet { - #[primary_span] - pub span: Span, - #[subdiagnostic] - pub(crate) reason: ForbiddenLetReason, -} - -#[derive(Diagnostic)] -#[diag(ast_passes_forbidden_let_stable)] -#[note] -pub struct ForbiddenLetStable { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] #[diag(ast_passes_keyword_lifetime)] pub struct KeywordLifetime { #[primary_span] @@ -727,3 +708,30 @@ pub struct ConstraintOnNegativeBound { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(ast_passes_invalid_unnamed_field_ty)] +pub struct InvalidUnnamedFieldTy { + #[primary_span] + pub span: Span, + #[label] + pub ty_span: Span, +} + +#[derive(Diagnostic)] +#[diag(ast_passes_invalid_unnamed_field)] +pub struct InvalidUnnamedField { + #[primary_span] + pub span: Span, + #[label] + pub ident_span: Span, +} + +#[derive(Diagnostic)] +#[diag(ast_passes_anon_struct_or_union_not_allowed)] +pub struct AnonStructOrUnionNotAllowed { + #[primary_span] + #[label] + pub span: Span, + pub struct_or_union: &'static str, +} diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 10c9c3ef1..62dc7ae58 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -570,6 +570,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(builtin_syntax, "`builtin #` syntax is unstable"); gate_all!(explicit_tail_calls, "`become` expression is experimental"); gate_all!(generic_const_items, "generic const items are experimental"); + gate_all!(unnamed_fields, "unnamed fields are not yet fully implemented"); if !visitor.features.negative_bounds { for &span in spans.get(&sym::negative_bounds).iter().copied().flatten() { @@ -577,11 +578,11 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { } } - // All uses of `gate_all!` below this point were added in #65742, + // All uses of `gate_all_legacy_dont_use!` below this point were added in #65742, // and subsequently disabled (with the non-early gating readded). // We emit an early future-incompatible warning for these. // New syntax gates should go above here to get a hard error gate. - macro_rules! gate_all { + macro_rules! gate_all_legacy_dont_use { ($gate:ident, $msg:literal) => { for span in spans.get(&sym::$gate).unwrap_or(&vec![]) { gate_feature_post!(future_incompatible; &visitor, $gate, *span, $msg); @@ -589,13 +590,19 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { }; } - gate_all!(trait_alias, "trait aliases are experimental"); - gate_all!(associated_type_bounds, "associated type bounds are unstable"); - gate_all!(return_type_notation, "return type notation is experimental"); - gate_all!(decl_macro, "`macro` is experimental"); - gate_all!(box_patterns, "box pattern syntax is experimental"); - gate_all!(exclusive_range_pattern, "exclusive range pattern syntax is experimental"); - gate_all!(try_blocks, "`try` blocks are unstable"); + gate_all_legacy_dont_use!(trait_alias, "trait aliases are experimental"); + gate_all_legacy_dont_use!(associated_type_bounds, "associated type bounds are unstable"); + // Despite being a new feature, `where T: Trait<Assoc(): Sized>`, which is RTN syntax now, + // used to be gated under associated_type_bounds, which are right above, so RTN needs to + // be too. + gate_all_legacy_dont_use!(return_type_notation, "return type notation is experimental"); + gate_all_legacy_dont_use!(decl_macro, "`macro` is experimental"); + gate_all_legacy_dont_use!(box_patterns, "box pattern syntax is experimental"); + gate_all_legacy_dont_use!( + exclusive_range_pattern, + "exclusive range pattern syntax is experimental" + ); + gate_all_legacy_dont_use!(try_blocks, "`try` blocks are unstable"); visit::walk_crate(&mut visitor, krate); } |