From c0b7b41cff2b40d430befefc8688fb8ad847bcd4 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Wed, 29 Jan 2020 01:57:24 +0100 Subject: [PATCH 1/4] parse_ty_common: use `enum`s instead of `bool`s. --- src/librustc_parse/parser/diagnostics.rs | 9 ++-- src/librustc_parse/parser/expr.rs | 3 +- src/librustc_parse/parser/item.rs | 7 +-- src/librustc_parse/parser/path.rs | 3 +- src/librustc_parse/parser/ty.rs | 59 ++++++++++++++++-------- 5 files changed, 52 insertions(+), 29 deletions(-) diff --git a/src/librustc_parse/parser/diagnostics.rs b/src/librustc_parse/parser/diagnostics.rs index b1cab591fd97c..5f148fa6ba2d3 100644 --- a/src/librustc_parse/parser/diagnostics.rs +++ b/src/librustc_parse/parser/diagnostics.rs @@ -1,3 +1,4 @@ +use super::ty::AllowPlus; use super::{BlockMode, Parser, PathStyle, SemiColonMode, SeqSep, TokenExpectType, TokenType}; use rustc_ast_pretty::pprust; @@ -693,11 +694,11 @@ impl<'a> Parser<'a> { pub(super) fn maybe_report_ambiguous_plus( &mut self, - allow_plus: bool, + allow_plus: AllowPlus, impl_dyn_multi: bool, ty: &Ty, ) { - if !allow_plus && impl_dyn_multi { + if matches!(allow_plus, AllowPlus::No) && impl_dyn_multi { let sum_with_parens = format!("({})", pprust::ty_to_string(&ty)); self.struct_span_err(ty.span, "ambiguous `+` in a type") .span_suggestion( @@ -712,11 +713,11 @@ impl<'a> Parser<'a> { pub(super) fn maybe_recover_from_bad_type_plus( &mut self, - allow_plus: bool, + allow_plus: AllowPlus, ty: &Ty, ) -> PResult<'a, ()> { // Do not add `+` to expected tokens. - if !allow_plus || !self.token.is_like_plus() { + if matches!(allow_plus, AllowPlus::No) || !self.token.is_like_plus() { return Ok(()); } diff --git a/src/librustc_parse/parser/expr.rs b/src/librustc_parse/parser/expr.rs index 0d12f8cf6c039..d98321416957d 100644 --- a/src/librustc_parse/parser/expr.rs +++ b/src/librustc_parse/parser/expr.rs @@ -1,4 +1,5 @@ use super::pat::{GateOr, PARAM_EXPECTED}; +use super::ty::{AllowPlus, RecoverQPath}; use super::{BlockMode, Parser, PathStyle, PrevTokenKind, Restrictions, TokenType}; use super::{SemiColonMode, SeqSep, TokenExpectType}; use crate::maybe_recover_from_interpolated_ty_qpath; @@ -1399,7 +1400,7 @@ impl<'a> Parser<'a> { self.expect_or()?; args }; - let output = self.parse_ret_ty(true, true)?; + let output = self.parse_ret_ty(AllowPlus::Yes, RecoverQPath::Yes)?; Ok(P(FnDecl { inputs, output })) } diff --git a/src/librustc_parse/parser/item.rs b/src/librustc_parse/parser/item.rs index b7f299e56ae50..87a6aa76f5777 100644 --- a/src/librustc_parse/parser/item.rs +++ b/src/librustc_parse/parser/item.rs @@ -1,4 +1,5 @@ use super::diagnostics::{dummy_arg, ConsumeClosingDelim, Error}; +use super::ty::{AllowPlus, RecoverQPath}; use super::{FollowedByType, Parser, PathStyle}; use crate::maybe_whole; @@ -1839,7 +1840,7 @@ impl<'a> Parser<'a> { fn parse_fn_sig(&mut self, cfg: &ParamCfg) -> PResult<'a, (Ident, P, Generics)> { let ident = self.parse_ident()?; let mut generics = self.parse_generics()?; - let decl = self.parse_fn_decl(cfg, true)?; + let decl = self.parse_fn_decl(cfg, AllowPlus::Yes)?; generics.where_clause = self.parse_where_clause()?; Ok((ident, decl, generics)) } @@ -1848,11 +1849,11 @@ impl<'a> Parser<'a> { pub(super) fn parse_fn_decl( &mut self, cfg: &ParamCfg, - ret_allow_plus: bool, + ret_allow_plus: AllowPlus, ) -> PResult<'a, P> { Ok(P(FnDecl { inputs: self.parse_fn_params(cfg)?, - output: self.parse_ret_ty(ret_allow_plus, true)?, + output: self.parse_ret_ty(ret_allow_plus, RecoverQPath::Yes)?, })) } diff --git a/src/librustc_parse/parser/path.rs b/src/librustc_parse/parser/path.rs index a09eb42dcfe6a..cb14ffb4bd028 100644 --- a/src/librustc_parse/parser/path.rs +++ b/src/librustc_parse/parser/path.rs @@ -1,3 +1,4 @@ +use super::ty::{AllowPlus, RecoverQPath}; use super::{Parser, TokenType}; use crate::maybe_whole; use rustc_errors::{pluralize, Applicability, PResult}; @@ -224,7 +225,7 @@ impl<'a> Parser<'a> { // `(T, U) -> R` let (inputs, _) = self.parse_paren_comma_seq(|p| p.parse_ty())?; let span = ident.span.to(self.prev_span); - let output = self.parse_ret_ty(false, false)?; + let output = self.parse_ret_ty(AllowPlus::No, RecoverQPath::No)?; ParenthesizedArgs { inputs, output, span }.into() }; diff --git a/src/librustc_parse/parser/ty.rs b/src/librustc_parse/parser/ty.rs index c9c2cbb98ca40..1ed80ed7350a9 100644 --- a/src/librustc_parse/parser/ty.rs +++ b/src/librustc_parse/parser/ty.rs @@ -36,6 +36,23 @@ impl BoundModifiers { } } +#[derive(Copy, Clone)] +pub(super) enum AllowPlus { + Yes, + No, +} + +pub(super) enum RecoverQPath { + Yes, + No, +} + +// Is `...` (`CVarArgs`) legal at this level of type parsing? +enum AllowCVariadic { + Yes, + No, +} + /// Returns `true` if `IDENT t` can start a type -- `IDENT::a::b`, `IDENT`, /// `IDENT<::AssocTy>`. /// @@ -48,14 +65,14 @@ fn can_continue_type_after_non_fn_ident(t: &Token) -> bool { impl<'a> Parser<'a> { /// Parses a type. pub fn parse_ty(&mut self) -> PResult<'a, P> { - self.parse_ty_common(true, true, false) + self.parse_ty_common(AllowPlus::Yes, RecoverQPath::Yes, AllowCVariadic::No) } /// Parse a type suitable for a function or function pointer parameter. /// The difference from `parse_ty` is that this version allows `...` /// (`CVarArgs`) at the top level of the the type. pub(super) fn parse_ty_for_param(&mut self) -> PResult<'a, P> { - self.parse_ty_common(true, true, true) + self.parse_ty_common(AllowPlus::Yes, RecoverQPath::Yes, AllowCVariadic::Yes) } /// Parses a type in restricted contexts where `+` is not permitted. @@ -65,18 +82,19 @@ impl<'a> Parser<'a> { /// Example 2: `value1 as TYPE + value2` /// `+` is prohibited to avoid interactions with expression grammar. pub(super) fn parse_ty_no_plus(&mut self) -> PResult<'a, P> { - self.parse_ty_common(false, true, false) + self.parse_ty_common(AllowPlus::No, RecoverQPath::Yes, AllowCVariadic::No) } /// Parses an optional return type `[ -> TY ]` in a function declaration. pub(super) fn parse_ret_ty( &mut self, - allow_plus: bool, - allow_qpath_recovery: bool, + allow_plus: AllowPlus, + recover_qpath: RecoverQPath, ) -> PResult<'a, FunctionRetTy> { Ok(if self.eat(&token::RArrow) { // FIXME(Centril): Can we unconditionally `allow_plus`? - FunctionRetTy::Ty(self.parse_ty_common(allow_plus, allow_qpath_recovery, false)?) + let ty = self.parse_ty_common(allow_plus, recover_qpath, AllowCVariadic::No)?; + FunctionRetTy::Ty(ty) } else { FunctionRetTy::Default(self.token.span.shrink_to_lo()) }) @@ -84,11 +102,11 @@ impl<'a> Parser<'a> { fn parse_ty_common( &mut self, - allow_plus: bool, - allow_qpath_recovery: bool, - // Is `...` (`CVarArgs`) legal in the immediate top level call? - allow_c_variadic: bool, + allow_plus: AllowPlus, + recover_qpath: RecoverQPath, + allow_c_variadic: AllowCVariadic, ) -> PResult<'a, P> { + let allow_qpath_recovery = matches!(recover_qpath, RecoverQPath::Yes); maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery); maybe_whole!(self, NtTy, |x| x); @@ -124,7 +142,7 @@ impl<'a> Parser<'a> { self.parse_ty_bare_fn(lifetime_defs)? } else { let path = self.parse_path(PathStyle::Type)?; - let parse_plus = allow_plus && self.check_plus(); + let parse_plus = matches!(allow_plus, AllowPlus::Yes) && self.check_plus(); self.parse_remaining_bounds(lifetime_defs, path, lo, parse_plus)? } } else if self.eat_keyword(kw::Impl) { @@ -144,7 +162,7 @@ impl<'a> Parser<'a> { } else if self.token.is_path_start() { self.parse_path_start_ty(lo, allow_plus)? } else if self.eat(&token::DotDotDot) { - if allow_c_variadic { + if let AllowCVariadic::Yes = allow_c_variadic { TyKind::CVarArgs } else { // FIXME(Centril): Should we just allow `...` syntactically @@ -172,7 +190,7 @@ impl<'a> Parser<'a> { /// Parses either: /// - `(TYPE)`, a parenthesized type. /// - `(TYPE,)`, a tuple with a single field of type TYPE. - fn parse_ty_tuple_or_parens(&mut self, lo: Span, allow_plus: bool) -> PResult<'a, TyKind> { + fn parse_ty_tuple_or_parens(&mut self, lo: Span, allow_plus: AllowPlus) -> PResult<'a, TyKind> { let mut trailing_plus = false; let (ts, trailing) = self.parse_paren_comma_seq(|p| { let ty = p.parse_ty()?; @@ -182,7 +200,7 @@ impl<'a> Parser<'a> { if ts.len() == 1 && !trailing { let ty = ts.into_iter().nth(0).unwrap().into_inner(); - let maybe_bounds = allow_plus && self.token.is_like_plus(); + let maybe_bounds = matches!(allow_plus, AllowPlus::Yes) && self.token.is_like_plus(); match ty.kind { // `(TY_BOUND_NOPAREN) + BOUND + ...`. TyKind::Path(None, path) if maybe_bounds => { @@ -288,7 +306,8 @@ impl<'a> Parser<'a> { let unsafety = self.parse_unsafety(); let ext = self.parse_extern()?; self.expect_keyword(kw::Fn)?; - let decl = self.parse_fn_decl(&ParamCfg { is_name_required: |_| false }, false)?; + let cfg = ParamCfg { is_name_required: |_| false }; + let decl = self.parse_fn_decl(&cfg, AllowPlus::No)?; Ok(TyKind::BareFn(P(BareFnTy { ext, unsafety, generic_params, decl }))) } @@ -326,7 +345,7 @@ impl<'a> Parser<'a> { /// 1. a type macro, `mac!(...)`, /// 2. a bare trait object, `B0 + ... + Bn`, /// 3. or a path, `path::to::MyType`. - fn parse_path_start_ty(&mut self, lo: Span, allow_plus: bool) -> PResult<'a, TyKind> { + fn parse_path_start_ty(&mut self, lo: Span, allow_plus: AllowPlus) -> PResult<'a, TyKind> { // Simple path let path = self.parse_path(PathStyle::Type)?; if self.eat(&token::Not) { @@ -336,7 +355,7 @@ impl<'a> Parser<'a> { args: self.parse_mac_args()?, prior_type_ascription: self.last_type_ascription, })) - } else if allow_plus && self.check_plus() { + } else if matches!(allow_plus, AllowPlus::Yes) && self.check_plus() { // `Trait1 + Trait2 + 'a` self.parse_remaining_bounds(Vec::new(), path, lo, true) } else { @@ -359,7 +378,7 @@ impl<'a> Parser<'a> { &mut self, colon_span: Option, ) -> PResult<'a, GenericBounds> { - self.parse_generic_bounds_common(true, colon_span) + self.parse_generic_bounds_common(AllowPlus::Yes, colon_span) } /// Parses bounds of a type parameter `BOUND + BOUND + ...`, possibly with trailing `+`. @@ -367,7 +386,7 @@ impl<'a> Parser<'a> { /// See `parse_generic_bound` for the `BOUND` grammar. fn parse_generic_bounds_common( &mut self, - allow_plus: bool, + allow_plus: AllowPlus, colon_span: Option, ) -> PResult<'a, GenericBounds> { let mut bounds = Vec::new(); @@ -377,7 +396,7 @@ impl<'a> Parser<'a> { Ok(bound) => bounds.push(bound), Err(neg_sp) => negative_bounds.push(neg_sp), } - if !allow_plus || !self.eat_plus() { + if matches!(allow_plus, AllowPlus::No) || !self.eat_plus() { break; } } From b2c6eeb713d4cf9b35b7dda6ff2b0274e7f24684 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Thu, 30 Jan 2020 00:18:54 +0100 Subject: [PATCH 2/4] parser: merge `fn` grammars wrt. bodies & headers also refactor `FnKind` and `visit_assoc_item` visitors --- src/librustc_ast_lowering/item.rs | 48 ++- src/librustc_ast_lowering/lib.rs | 27 +- src/librustc_ast_passes/ast_validation.rs | 301 +++++++++++------- src/librustc_ast_passes/feature_gate.rs | 69 ++-- src/librustc_ast_passes/node_count.rs | 8 +- src/librustc_ast_pretty/pprust.rs | 64 ++-- .../global_allocator.rs | 2 +- src/librustc_builtin_macros/test_harness.rs | 2 +- src/librustc_expand/base.rs | 6 +- src/librustc_expand/expand.rs | 38 ++- src/librustc_lint/builtin.rs | 46 +-- src/librustc_lint/early.rs | 39 +-- src/librustc_lint/passes.rs | 3 +- src/librustc_parse/parser/diagnostics.rs | 41 --- src/librustc_parse/parser/item.rs | 115 +++---- src/librustc_parse/parser/stmt.rs | 2 +- src/librustc_parse/parser/ty.rs | 16 +- src/librustc_passes/hir_stats.rs | 25 +- src/librustc_resolve/build_reduced_graph.rs | 21 +- src/librustc_resolve/def_collector.rs | 39 +-- src/librustc_resolve/late.rs | 54 ++-- src/librustc_save_analysis/dump_visitor.rs | 9 +- src/librustc_save_analysis/lib.rs | 4 +- src/librustc_save_analysis/sig.rs | 3 +- src/libsyntax/ast.rs | 15 +- src/libsyntax/mut_visit.rs | 7 +- src/libsyntax/visit.rs | 136 ++++---- src/test/ui/extern/extern-ffi-fn-with-body.rs | 2 +- .../ui/extern/extern-ffi-fn-with-body.stderr | 10 +- src/test/ui/issues/issue-39616.rs | 3 +- src/test/ui/issues/issue-39616.stderr | 4 +- src/test/ui/macros/issue-54441.stderr | 4 +- src/test/ui/no-patterns-in-args-2.rs | 4 +- src/test/ui/no-patterns-in-args-2.stderr | 6 +- src/test/ui/no-patterns-in-args-macro.rs | 6 +- src/test/ui/no-patterns-in-args-macro.stderr | 4 +- src/test/ui/parser/duplicate-visibility.rs | 2 +- .../ui/parser/duplicate-visibility.stderr | 4 +- .../parser/fn-body-optional-semantic-fail.rs | 27 ++ .../fn-body-optional-semantic-fail.stderr | 40 +++ .../parser/fn-body-optional-syntactic-pass.rs | 31 ++ src/test/ui/parser/fn-header-semantic-fail.rs | 57 ++++ .../ui/parser/fn-header-semantic-fail.stderr | 136 ++++++++ .../ui/parser/fn-header-syntactic-pass.rs | 55 ++++ src/test/ui/parser/issue-24780.rs | 2 +- src/test/ui/parser/issue-24780.stderr | 4 +- src/test/ui/parser/issue-63135.rs | 2 +- src/test/ui/parser/issue-63135.stderr | 8 +- src/test/ui/parser/missing_right_paren.rs | 2 +- src/test/ui/parser/missing_right_paren.stderr | 8 +- .../ui/parser/no-const-fn-in-extern-block.rs | 4 +- .../parser/no-const-fn-in-extern-block.stderr | 21 +- src/test/ui/parser/not-a-pred.stderr | 4 +- 53 files changed, 961 insertions(+), 629 deletions(-) create mode 100644 src/test/ui/parser/fn-body-optional-semantic-fail.rs create mode 100644 src/test/ui/parser/fn-body-optional-semantic-fail.stderr create mode 100644 src/test/ui/parser/fn-body-optional-syntactic-pass.rs create mode 100644 src/test/ui/parser/fn-header-semantic-fail.rs create mode 100644 src/test/ui/parser/fn-header-semantic-fail.stderr create mode 100644 src/test/ui/parser/fn-header-syntactic-pass.rs diff --git a/src/librustc_ast_lowering/item.rs b/src/librustc_ast_lowering/item.rs index e27f2bdb8d25f..dab950e23f618 100644 --- a/src/librustc_ast_lowering/item.rs +++ b/src/librustc_ast_lowering/item.rs @@ -14,7 +14,7 @@ use rustc_target::spec::abi; use syntax::ast::*; use syntax::attr; use syntax::node_id::NodeMap; -use syntax::visit::{self, Visitor}; +use syntax::visit::{self, AssocCtxt, Visitor}; use log::debug; use smallvec::{smallvec, SmallVec}; @@ -81,25 +81,23 @@ impl<'a> Visitor<'a> for ItemLowerer<'a, '_, '_> { } } - fn visit_trait_item(&mut self, item: &'a AssocItem) { - self.lctx.with_hir_id_owner(item.id, |lctx| { - let hir_item = lctx.lower_trait_item(item); - let id = hir::TraitItemId { hir_id: hir_item.hir_id }; - lctx.trait_items.insert(id, hir_item); - lctx.modules.get_mut(&lctx.current_module).unwrap().trait_items.insert(id); + fn visit_assoc_item(&mut self, item: &'a AssocItem, ctxt: AssocCtxt) { + self.lctx.with_hir_id_owner(item.id, |lctx| match ctxt { + AssocCtxt::Trait => { + let hir_item = lctx.lower_trait_item(item); + let id = hir::TraitItemId { hir_id: hir_item.hir_id }; + lctx.trait_items.insert(id, hir_item); + lctx.modules.get_mut(&lctx.current_module).unwrap().trait_items.insert(id); + } + AssocCtxt::Impl => { + let hir_item = lctx.lower_impl_item(item); + let id = hir::ImplItemId { hir_id: hir_item.hir_id }; + lctx.impl_items.insert(id, hir_item); + lctx.modules.get_mut(&lctx.current_module).unwrap().impl_items.insert(id); + } }); - visit::walk_trait_item(self, item); - } - - fn visit_impl_item(&mut self, item: &'a AssocItem) { - self.lctx.with_hir_id_owner(item.id, |lctx| { - let hir_item = lctx.lower_impl_item(item); - let id = hir::ImplItemId { hir_id: hir_item.hir_id }; - lctx.impl_items.insert(id, hir_item); - lctx.modules.get_mut(&lctx.current_module).unwrap().impl_items.insert(id); - }); - visit::walk_impl_item(self, item); + visit::walk_assoc_item(self, item, ctxt); } } @@ -299,20 +297,17 @@ impl<'hir> LoweringContext<'_, 'hir> { // `impl Future` here because lower_body // only cares about the input argument patterns in the function // declaration (decl), not the return types. + let asyncness = header.asyncness.node; let body_id = - this.lower_maybe_async_body(span, &decl, header.asyncness.node, Some(body)); + this.lower_maybe_async_body(span, &decl, asyncness, body.as_deref()); let (generics, decl) = this.add_in_band_defs( generics, fn_def_id, AnonymousLifetimeMode::PassThrough, |this, idty| { - this.lower_fn_decl( - &decl, - Some((fn_def_id, idty)), - true, - header.asyncness.node.opt_return_id(), - ) + let ret_id = asyncness.opt_return_id(); + this.lower_fn_decl(&decl, Some((fn_def_id, idty)), true, ret_id) }, ); let sig = hir::FnSig { decl, header: this.lower_fn_header(header) }; @@ -658,7 +653,8 @@ impl<'hir> LoweringContext<'_, 'hir> { ident: i.ident, attrs: self.lower_attrs(&i.attrs), kind: match i.kind { - ForeignItemKind::Fn(ref fdec, ref generics) => { + ForeignItemKind::Fn(ref sig, ref generics, _) => { + let fdec = &sig.decl; let (generics, (fn_dec, fn_args)) = self.add_in_band_defs( generics, def_id, diff --git a/src/librustc_ast_lowering/lib.rs b/src/librustc_ast_lowering/lib.rs index c3e96a31e4001..cd33bf6547be0 100644 --- a/src/librustc_ast_lowering/lib.rs +++ b/src/librustc_ast_lowering/lib.rs @@ -63,7 +63,7 @@ use syntax::attr; use syntax::node_id::NodeMap; use syntax::token::{self, Nonterminal, Token}; use syntax::tokenstream::{TokenStream, TokenTree}; -use syntax::visit::{self, Visitor}; +use syntax::visit::{self, AssocCtxt, Visitor}; use syntax::walk_list; use log::{debug, trace}; @@ -485,25 +485,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { }); } - fn visit_trait_item(&mut self, item: &'tcx AssocItem) { + fn visit_assoc_item(&mut self, item: &'tcx AssocItem, ctxt: AssocCtxt) { self.lctx.allocate_hir_id_counter(item.id); - - match item.kind { - AssocItemKind::Fn(_, None) => { - // Ignore patterns in trait methods without bodies - self.with_hir_id_owner(None, |this| visit::walk_trait_item(this, item)); - } - _ => self.with_hir_id_owner(Some(item.id), |this| { - visit::walk_trait_item(this, item); - }), - } - } - - fn visit_impl_item(&mut self, item: &'tcx AssocItem) { - self.lctx.allocate_hir_id_counter(item.id); - self.with_hir_id_owner(Some(item.id), |this| { - visit::walk_impl_item(this, item); - }); + let owner = match (&item.kind, ctxt) { + // Ignore patterns in trait methods without bodies. + (AssocItemKind::Fn(_, None), AssocCtxt::Trait) => None, + _ => Some(item.id), + }; + self.with_hir_id_owner(owner, |this| visit::walk_assoc_item(this, item, ctxt)); } fn visit_foreign_item(&mut self, i: &'tcx ForeignItem) { diff --git a/src/librustc_ast_passes/ast_validation.rs b/src/librustc_ast_passes/ast_validation.rs index cb64e2e95bf7d..53911d147802d 100644 --- a/src/librustc_ast_passes/ast_validation.rs +++ b/src/librustc_ast_passes/ast_validation.rs @@ -8,7 +8,7 @@ use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{struct_span_err, Applicability, FatalError}; +use rustc_errors::{error_code, struct_span_err, Applicability, FatalError}; use rustc_parse::validate_attr; use rustc_session::lint::builtin::PATTERNS_IN_FNS_WITHOUT_BODY; use rustc_session::lint::LintBuffer; @@ -20,7 +20,7 @@ use std::mem; use syntax::ast::*; use syntax::attr; use syntax::expand::is_proc_macro_attr; -use syntax::visit::{self, Visitor}; +use syntax::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor}; use syntax::walk_list; /// Is `self` allowed semantically as the first parameter in an `FnDecl`? @@ -49,6 +49,13 @@ impl BoundContext { struct AstValidator<'a> { session: &'a Session, + + /// The span of the `extern` in an `extern { ... }` block, if any. + extern_mod: Option<&'a Item>, + + /// Are we inside a trait impl? + in_trait_impl: bool, + has_proc_macro_decls: bool, /// Used to ban nested `impl Trait`, e.g., `impl Into`. @@ -389,13 +396,9 @@ impl<'a> AstValidator<'a> { } } - fn check_impl_item_provided(&self, sp: Span, body: &Option, ctx: &str, sugg: &str) { - if body.is_some() { - return; - } - + fn error_item_without_body(&self, sp: Span, ctx: &str, msg: &str, sugg: &str) { self.err_handler() - .struct_span_err(sp, &format!("associated {} in `impl` without body", ctx)) + .struct_span_err(sp, msg) .span_suggestion( self.session.source_map().end_point(sp), &format!("provide a definition for the {}", ctx), @@ -405,6 +408,13 @@ impl<'a> AstValidator<'a> { .emit(); } + fn check_impl_item_provided(&self, sp: Span, body: &Option, ctx: &str, sugg: &str) { + if body.is_none() { + let msg = format!("associated {} in `impl` without body", ctx); + self.error_item_without_body(sp, ctx, &msg, sugg); + } + } + fn check_impl_assoc_type_no_bounds(&self, bounds: &[GenericBound]) { let span = match bounds { [] => return, @@ -416,8 +426,71 @@ impl<'a> AstValidator<'a> { .emit(); } - fn check_c_varadic_type(&self, decl: &FnDecl) { - for Param { ty, span, .. } in &decl.inputs { + /// An `fn` in `extern { ... }` cannot have a body `{ ... }`. + fn check_foreign_fn_bodyless(&self, ident: Ident, body: Option<&Block>) { + let body = match body { + None => return, + Some(body) => body, + }; + self.err_handler() + .struct_span_err(ident.span, "incorrect function inside `extern` block") + .span_label(ident.span, "cannot have a body") + .span_suggestion( + body.span, + "remove the invalid body", + ";".to_string(), + Applicability::MaybeIncorrect, + ) + .help( + "you might have meant to write a function accessible through FFI, \ + which can be done by writing `extern fn` outside of the `extern` block", + ) + .span_label( + self.current_extern_span(), + "`extern` blocks define existing foreign functions and functions \ + inside of them cannot have a body", + ) + .note("for more information, visit https://doc.rust-lang.org/std/keyword.extern.html") + .emit(); + } + + fn current_extern_span(&self) -> Span { + self.session.source_map().def_span(self.extern_mod.unwrap().span) + } + + /// An `fn` in `extern { ... }` cannot have qualfiers, e.g. `async fn`. + fn check_foreign_fn_headerless(&self, ident: Ident, span: Span, header: FnHeader) { + if header.has_qualifiers() { + self.err_handler() + .struct_span_err(ident.span, "functions in `extern` blocks cannot have qualifiers") + .span_label(self.current_extern_span(), "in this `extern` block") + .span_suggestion( + span.until(ident.span.shrink_to_lo()), + "remove the qualifiers", + "fn ".to_string(), + Applicability::MaybeIncorrect, + ) + .emit(); + } + } + + /// Reject C-varadic type unless the function is foreign, + /// or free and `unsafe extern "C"` semantically. + fn check_c_varadic_type(&self, fk: FnKind<'a>) { + match (fk.ctxt(), fk.header()) { + (Some(FnCtxt::Foreign), _) => return, + (Some(FnCtxt::Free), Some(header)) => match header.ext { + Extern::Explicit(StrLit { symbol_unescaped: sym::C, .. }) | Extern::Implicit + if header.unsafety == Unsafety::Unsafe => + { + return; + } + _ => {} + }, + _ => {} + }; + + for Param { ty, span, .. } in &fk.decl().inputs { if let TyKind::CVarArgs = ty.kind { self.err_handler() .struct_span_err( @@ -428,6 +501,24 @@ impl<'a> AstValidator<'a> { } } } + + /// We currently do not permit const generics in `const fn`, + /// as this is tantamount to allowing compile-time dependent typing. + /// + /// FIXME(const_generics): Is this really true / necessary? Discuss with @varkor. + /// At any rate, the restriction feels too syntactic. Consider moving it to e.g. typeck. + fn check_const_fn_const_generic(&self, span: Span, sig: &FnSig, generics: &Generics) { + if sig.header.constness.node == Constness::Const { + // Look for const generics and error if we find any. + for param in &generics.params { + if let GenericParamKind::Const { .. } = param.kind { + self.err_handler() + .struct_span_err(span, "const parameters are not permitted in `const fn`") + .emit(); + } + } + } + } } enum GenericPosition { @@ -532,9 +623,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> { fn visit_expr(&mut self, expr: &'a Expr) { match &expr.kind { - ExprKind::Closure(_, _, _, fn_decl, _, _) => { - self.check_fn_decl(fn_decl, SelfSemantic::No); - } ExprKind::InlineAsm(..) if !self.session.target.target.options.allow_asm => { struct_span_err!( self.session, @@ -647,8 +735,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> { generics: _, of_trait: Some(_), ref self_ty, - ref items, + items: _, } => { + let old_in_trait_impl = mem::replace(&mut self.in_trait_impl, true); + self.invalid_visibility(&item.vis, None); if let TyKind::Err = self_ty.kind { self.err_handler() @@ -665,13 +755,11 @@ impl<'a> Visitor<'a> for AstValidator<'a> { ) .emit(); } - for impl_item in items { - self.invalid_visibility(&impl_item.vis, None); - if let AssocItemKind::Fn(ref sig, _) = impl_item.kind { - self.check_trait_fn_not_const(sig.header.constness); - self.check_trait_fn_not_async(impl_item.span, sig.header.asyncness.node); - } - } + + visit::walk_item(self, item); + + self.in_trait_impl = old_in_trait_impl; + return; // Avoid visiting again. } ItemKind::Impl { unsafety, @@ -712,40 +800,23 @@ impl<'a> Visitor<'a> for AstValidator<'a> { .emit(); } } - ItemKind::Fn(ref sig, ref generics, _) => { - self.visit_fn_header(&sig.header); - self.check_fn_decl(&sig.decl, SelfSemantic::No); - // We currently do not permit const generics in `const fn`, as - // this is tantamount to allowing compile-time dependent typing. - if sig.header.constness.node == Constness::Const { - // Look for const generics and error if we find any. - for param in &generics.params { - match param.kind { - GenericParamKind::Const { .. } => { - self.err_handler() - .struct_span_err( - item.span, - "const parameters are not permitted in `const fn`", - ) - .emit(); - } - _ => {} - } - } - } - // Reject C-varadic type unless the function is `unsafe extern "C"` semantically. - match sig.header.ext { - Extern::Explicit(StrLit { symbol_unescaped: sym::C, .. }) - | Extern::Implicit - if sig.header.unsafety == Unsafety::Unsafe => {} - _ => self.check_c_varadic_type(&sig.decl), + ItemKind::Fn(ref sig, ref generics, ref body) => { + self.check_const_fn_const_generic(item.span, sig, generics); + + if body.is_none() { + let msg = "free function without a body"; + self.error_item_without_body(item.span, "function", msg, " { }"); } } - ItemKind::ForeignMod(..) => { + ItemKind::ForeignMod(_) => { + let old_item = mem::replace(&mut self.extern_mod, Some(item)); self.invalid_visibility( &item.vis, Some("place qualifiers on individual foreign items instead"), ); + visit::walk_item(self, item); + self.extern_mod = old_item; + return; // Avoid visiting again. } ItemKind::Enum(ref def, _) => { for variant in &def.variants { @@ -796,7 +867,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.with_bound_context(BoundContext::TraitBounds, |this| { walk_list!(this, visit_param_bound, bounds); }); - walk_list!(self, visit_trait_item, trait_items); + walk_list!(self, visit_assoc_item, trait_items, AssocCtxt::Trait); walk_list!(self, visit_attribute, &item.attrs); return; } @@ -820,19 +891,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } fn visit_foreign_item(&mut self, fi: &'a ForeignItem) { - match fi.kind { - ForeignItemKind::Fn(ref decl, _) => { - self.check_fn_decl(decl, SelfSemantic::No); - Self::check_decl_no_pat(decl, |span, _| { - struct_span_err!( - self.session, - span, - E0130, - "patterns aren't allowed in foreign function declarations" - ) - .span_label(span, "pattern not allowed in foreign function") - .emit(); - }); + match &fi.kind { + ForeignItemKind::Fn(sig, _, body) => { + self.check_foreign_fn_bodyless(fi.ident, body.as_deref()); + self.check_foreign_fn_headerless(fi.ident, fi.span, sig.header); } ForeignItemKind::Static(..) | ForeignItemKind::Ty | ForeignItemKind::Macro(..) => {} } @@ -1011,67 +1073,84 @@ impl<'a> Visitor<'a> for AstValidator<'a> { }) } - fn visit_impl_item(&mut self, ii: &'a AssocItem) { - match &ii.kind { - AssocItemKind::Const(_, body) => { - self.check_impl_item_provided(ii.span, body, "constant", " = ;"); - } - AssocItemKind::Fn(_, body) => { - self.check_impl_item_provided(ii.span, body, "function", " { }"); - } - AssocItemKind::TyAlias(bounds, body) => { - self.check_impl_item_provided(ii.span, body, "type", " = ;"); - self.check_impl_assoc_type_no_bounds(bounds); - } - _ => {} + fn visit_fn(&mut self, fk: FnKind<'a>, span: Span, id: NodeId) { + // Only associated `fn`s can have `self` parameters. + let self_semantic = match fk.ctxt() { + Some(FnCtxt::Assoc(_)) => SelfSemantic::Yes, + _ => SelfSemantic::No, + }; + self.check_fn_decl(fk.decl(), self_semantic); + + self.check_c_varadic_type(fk); + + // Functions without bodies cannot have patterns. + if let FnKind::Fn(ctxt, _, sig, _, None) = fk { + Self::check_decl_no_pat(&sig.decl, |span, mut_ident| { + let (code, msg, label) = match ctxt { + FnCtxt::Foreign => ( + error_code!(E0130), + "patterns aren't allowed in foreign function declarations", + "pattern not allowed in foreign function", + ), + _ => ( + error_code!(E0642), + "patterns aren't allowed in functions without bodies", + "pattern not allowed in function without body", + ), + }; + if mut_ident && matches!(ctxt, FnCtxt::Assoc(_)) { + self.lint_buffer.buffer_lint(PATTERNS_IN_FNS_WITHOUT_BODY, id, span, msg); + } else { + self.err_handler() + .struct_span_err(span, msg) + .span_label(span, label) + .code(code) + .emit(); + } + }); } - visit::walk_impl_item(self, ii); + + visit::walk_fn(self, fk, span); } - fn visit_trait_item(&mut self, ti: &'a AssocItem) { - self.invalid_visibility(&ti.vis, None); - self.check_defaultness(ti.span, ti.defaultness); - - if let AssocItemKind::Fn(sig, block) = &ti.kind { - self.check_trait_fn_not_async(ti.span, sig.header.asyncness.node); - self.check_trait_fn_not_const(sig.header.constness); - if block.is_none() { - Self::check_decl_no_pat(&sig.decl, |span, mut_ident| { - if mut_ident { - self.lint_buffer.buffer_lint( - PATTERNS_IN_FNS_WITHOUT_BODY, - ti.id, - span, - "patterns aren't allowed in methods without bodies", - ); - } else { - struct_span_err!( - self.session, - span, - E0642, - "patterns aren't allowed in methods without bodies" - ) - .emit(); - } - }); - } + fn visit_assoc_item(&mut self, item: &'a AssocItem, ctxt: AssocCtxt) { + if ctxt == AssocCtxt::Trait { + self.check_defaultness(item.span, item.defaultness); } - visit::walk_trait_item(self, ti); - } + if ctxt == AssocCtxt::Impl { + match &item.kind { + AssocItemKind::Const(_, body) => { + self.check_impl_item_provided(item.span, body, "constant", " = ;"); + } + AssocItemKind::Fn(_, body) => { + self.check_impl_item_provided(item.span, body, "function", " { }"); + } + AssocItemKind::TyAlias(bounds, body) => { + self.check_impl_item_provided(item.span, body, "type", " = ;"); + self.check_impl_assoc_type_no_bounds(bounds); + } + _ => {} + } + } - fn visit_assoc_item(&mut self, item: &'a AssocItem) { - if let AssocItemKind::Fn(sig, _) = &item.kind { - self.check_fn_decl(&sig.decl, SelfSemantic::Yes); - self.check_c_varadic_type(&sig.decl); + if ctxt == AssocCtxt::Trait || self.in_trait_impl { + self.invalid_visibility(&item.vis, None); + if let AssocItemKind::Fn(sig, _) = &item.kind { + self.check_trait_fn_not_const(sig.header.constness); + self.check_trait_fn_not_async(item.span, sig.header.asyncness.node); + } } - visit::walk_assoc_item(self, item); + + visit::walk_assoc_item(self, item, ctxt); } } pub fn check_crate(session: &Session, krate: &Crate, lints: &mut LintBuffer) -> bool { let mut validator = AstValidator { session, + extern_mod: None, + in_trait_impl: false, has_proc_macro_decls: false, outer_impl_trait: None, bound_context: None, diff --git a/src/librustc_ast_passes/feature_gate.rs b/src/librustc_ast_passes/feature_gate.rs index 3b13ab354fdf9..a10ac94d8942b 100644 --- a/src/librustc_ast_passes/feature_gate.rs +++ b/src/librustc_ast_passes/feature_gate.rs @@ -8,7 +8,7 @@ use rustc_span::Span; use syntax::ast::{self, AssocTyConstraint, AssocTyConstraintKind, NodeId}; use syntax::ast::{GenericParam, GenericParamKind, PatKind, RangeEnd, VariantData}; use syntax::attr; -use syntax::visit::{self, FnKind, Visitor}; +use syntax::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor}; use log::debug; @@ -492,25 +492,17 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { visit::walk_pat(self, pattern) } - fn visit_fn( - &mut self, - fn_kind: FnKind<'a>, - fn_decl: &'a ast::FnDecl, - span: Span, - _node_id: NodeId, - ) { + fn visit_fn(&mut self, fn_kind: FnKind<'a>, span: Span, _: NodeId) { if let Some(header) = fn_kind.header() { - // Stability of const fn methods are covered in - // `visit_trait_item` and `visit_impl_item` below; this is - // because default methods don't pass through this point. + // Stability of const fn methods are covered in `visit_assoc_item` below. self.check_extern(header.ext); } - if fn_decl.c_variadic() { + if fn_kind.ctxt() != Some(FnCtxt::Foreign) && fn_kind.decl().c_variadic() { gate_feature_post!(&self, c_variadic, span, "C-variadic functions are unstable"); } - visit::walk_fn(self, fn_kind, fn_decl, span) + visit::walk_fn(self, fn_kind, span) } fn visit_generic_param(&mut self, param: &'a GenericParam) { @@ -539,56 +531,35 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { visit::walk_assoc_ty_constraint(self, constraint) } - fn visit_trait_item(&mut self, ti: &'a ast::AssocItem) { - match ti.kind { - ast::AssocItemKind::Fn(ref sig, ref block) => { - if block.is_none() { - self.check_extern(sig.header.ext); - } - if sig.header.constness.node == ast::Constness::Const { - gate_feature_post!(&self, const_fn, ti.span, "const fn is unstable"); + fn visit_assoc_item(&mut self, i: &'a ast::AssocItem, ctxt: AssocCtxt) { + if i.defaultness == ast::Defaultness::Default { + gate_feature_post!(&self, specialization, i.span, "specialization is unstable"); + } + + match i.kind { + ast::AssocItemKind::Fn(ref sig, _) => { + let constness = sig.header.constness.node; + if let (ast::Constness::Const, AssocCtxt::Trait) = (constness, ctxt) { + gate_feature_post!(&self, const_fn, i.span, "const fn is unstable"); } } - ast::AssocItemKind::TyAlias(_, ref default) => { - if let Some(_) = default { + ast::AssocItemKind::TyAlias(_, ref ty) => { + if let (Some(_), AssocCtxt::Trait) = (ty, ctxt) { gate_feature_post!( &self, associated_type_defaults, - ti.span, + i.span, "associated type defaults are unstable" ); } - } - _ => {} - } - visit::walk_trait_item(self, ti) - } - - fn visit_assoc_item(&mut self, ii: &'a ast::AssocItem) { - if ii.defaultness == ast::Defaultness::Default { - gate_feature_post!(&self, specialization, ii.span, "specialization is unstable"); - } - - match ii.kind { - ast::AssocItemKind::Fn(ref sig, _) => { - if sig.decl.c_variadic() { - gate_feature_post!( - &self, - c_variadic, - ii.span, - "C-variadic functions are unstable" - ); - } - } - ast::AssocItemKind::TyAlias(_, ref ty) => { if let Some(ty) = ty { self.check_impl_trait(ty); } - self.check_gat(&ii.generics, ii.span); + self.check_gat(&i.generics, i.span); } _ => {} } - visit::walk_assoc_item(self, ii) + visit::walk_assoc_item(self, i, ctxt) } fn visit_vis(&mut self, vis: &'a ast::Visibility) { diff --git a/src/librustc_ast_passes/node_count.rs b/src/librustc_ast_passes/node_count.rs index 9fe7238fcfc3e..ed1ccdf6a768a 100644 --- a/src/librustc_ast_passes/node_count.rs +++ b/src/librustc_ast_passes/node_count.rs @@ -67,13 +67,13 @@ impl<'ast> Visitor<'ast> for NodeCounter { self.count += 1; walk_generics(self, g) } - fn visit_fn(&mut self, fk: FnKind<'_>, fd: &FnDecl, s: Span, _: NodeId) { + fn visit_fn(&mut self, fk: FnKind<'_>, s: Span, _: NodeId) { self.count += 1; - walk_fn(self, fk, fd, s) + walk_fn(self, fk, s) } - fn visit_assoc_item(&mut self, ti: &AssocItem) { + fn visit_assoc_item(&mut self, ti: &AssocItem, ctxt: AssocCtxt) { self.count += 1; - walk_assoc_item(self, ti) + walk_assoc_item(self, ti, ctxt); } fn visit_trait_ref(&mut self, t: &TraitRef) { self.count += 1; diff --git a/src/librustc_ast_pretty/pprust.rs b/src/librustc_ast_pretty/pprust.rs index 3cc67a7c82117..d9077d1606f3a 100644 --- a/src/librustc_ast_pretty/pprust.rs +++ b/src/librustc_ast_pretty/pprust.rs @@ -1020,18 +1020,8 @@ impl<'a> State<'a> { self.maybe_print_comment(item.span.lo()); self.print_outer_attributes(&item.attrs); match item.kind { - ast::ForeignItemKind::Fn(ref decl, ref generics) => { - self.head(""); - self.print_fn( - decl, - ast::FnHeader::default(), - Some(item.ident), - generics, - &item.vis, - ); - self.end(); // end head-ibox - self.s.word(";"); - self.end(); // end the outer fn box + ast::ForeignItemKind::Fn(ref sig, ref gen, ref body) => { + self.print_fn_full(sig, item.ident, gen, &item.vis, body.as_deref(), &item.attrs); } ast::ForeignItemKind::Static(ref t, m) => { self.head(visibility_qualified(&item.vis, "static")); @@ -1154,11 +1144,8 @@ impl<'a> State<'a> { self.s.word(";"); self.end(); // end the outer cbox } - ast::ItemKind::Fn(ref sig, ref param_names, ref body) => { - self.head(""); - self.print_fn(&sig.decl, sig.header, Some(item.ident), param_names, &item.vis); - self.s.word(" "); - self.print_block_with_attrs(body, &item.attrs); + ast::ItemKind::Fn(ref sig, ref gen, ref body) => { + self.print_fn_full(sig, item.ident, gen, &item.vis, body.as_deref(), &item.attrs); } ast::ItemKind::Mod(ref _mod) => { self.head(visibility_qualified(&item.vis, "mod")); @@ -1483,16 +1470,8 @@ impl<'a> State<'a> { self.print_associated_const(item.ident, ty, expr.as_deref(), &item.vis); } ast::AssocItemKind::Fn(sig, body) => { - if body.is_some() { - self.head(""); - } - self.print_fn(&sig.decl, sig.header, Some(item.ident), &item.generics, &item.vis); - if let Some(body) = body { - self.nbsp(); - self.print_block_with_attrs(body, &item.attrs); - } else { - self.s.word(";"); - } + let body = body.as_deref(); + self.print_fn_full(sig, item.ident, &item.generics, &item.vis, body, &item.attrs); } ast::AssocItemKind::TyAlias(bounds, ty) => { self.print_associated_type(item.ident, bounds, ty.as_deref()); @@ -2412,6 +2391,27 @@ impl<'a> State<'a> { } } + fn print_fn_full( + &mut self, + sig: &ast::FnSig, + name: ast::Ident, + generics: &ast::Generics, + vis: &ast::Visibility, + body: Option<&ast::Block>, + attrs: &[ast::Attribute], + ) { + if body.is_some() { + self.head(""); + } + self.print_fn(&sig.decl, sig.header, Some(name), generics, vis); + if let Some(body) = body { + self.nbsp(); + self.print_block_with_attrs(body, attrs); + } else { + self.s.word(";"); + } + } + crate fn print_fn( &mut self, decl: &ast::FnDecl, @@ -2698,13 +2698,9 @@ impl<'a> State<'a> { where_clause: ast::WhereClause { predicates: Vec::new(), span: rustc_span::DUMMY_SP }, span: rustc_span::DUMMY_SP, }; - self.print_fn( - decl, - ast::FnHeader { unsafety, ext, ..ast::FnHeader::default() }, - name, - &generics, - &dummy_spanned(ast::VisibilityKind::Inherited), - ); + let header = ast::FnHeader { unsafety, ext, ..ast::FnHeader::default() }; + let vis = dummy_spanned(ast::VisibilityKind::Inherited); + self.print_fn(decl, header, name, &generics, &vis); self.end(); } diff --git a/src/librustc_builtin_macros/global_allocator.rs b/src/librustc_builtin_macros/global_allocator.rs index 957d34b7d8983..ec0d55b38a73d 100644 --- a/src/librustc_builtin_macros/global_allocator.rs +++ b/src/librustc_builtin_macros/global_allocator.rs @@ -66,7 +66,7 @@ impl AllocFnFactory<'_, '_> { let decl = self.cx.fn_decl(abi_args, ast::FunctionRetTy::Ty(output_ty)); let header = FnHeader { unsafety: Unsafety::Unsafe, ..FnHeader::default() }; let sig = FnSig { decl, header }; - let kind = ItemKind::Fn(sig, Generics::default(), self.cx.block_expr(output_expr)); + let kind = ItemKind::Fn(sig, Generics::default(), Some(self.cx.block_expr(output_expr))); let item = self.cx.item( self.span, self.cx.ident_of(&self.kind.fn_name(method.name), self.span), diff --git a/src/librustc_builtin_macros/test_harness.rs b/src/librustc_builtin_macros/test_harness.rs index 6a73f121c99bf..70f1c0e4e2d7c 100644 --- a/src/librustc_builtin_macros/test_harness.rs +++ b/src/librustc_builtin_macros/test_harness.rs @@ -307,7 +307,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P { let decl = ecx.fn_decl(vec![], ast::FunctionRetTy::Ty(main_ret_ty)); let sig = ast::FnSig { decl, header: ast::FnHeader::default() }; - let main = ast::ItemKind::Fn(sig, ast::Generics::default(), main_body); + let main = ast::ItemKind::Fn(sig, ast::Generics::default(), Some(main_body)); // Honor the reexport_test_harness_main attribute let main_id = match cx.reexport_test_harness_main { diff --git a/src/librustc_expand/base.rs b/src/librustc_expand/base.rs index 536259e054718..e167089b93a35 100644 --- a/src/librustc_expand/base.rs +++ b/src/librustc_expand/base.rs @@ -17,7 +17,7 @@ use syntax::mut_visit::{self, MutVisitor}; use syntax::ptr::P; use syntax::token; use syntax::tokenstream::{self, TokenStream}; -use syntax::visit::Visitor; +use syntax::visit::{AssocCtxt, Visitor}; use std::default::Default; use std::iter; @@ -103,8 +103,8 @@ impl Annotatable { pub fn visit_with<'a, V: Visitor<'a>>(&'a self, visitor: &mut V) { match self { Annotatable::Item(item) => visitor.visit_item(item), - Annotatable::TraitItem(trait_item) => visitor.visit_trait_item(trait_item), - Annotatable::ImplItem(impl_item) => visitor.visit_impl_item(impl_item), + Annotatable::TraitItem(item) => visitor.visit_assoc_item(item, AssocCtxt::Trait), + Annotatable::ImplItem(item) => visitor.visit_assoc_item(item, AssocCtxt::Impl), Annotatable::ForeignItem(foreign_item) => visitor.visit_foreign_item(foreign_item), Annotatable::Stmt(stmt) => visitor.visit_stmt(stmt), Annotatable::Expr(expr) => visitor.visit_expr(expr), diff --git a/src/librustc_expand/expand.rs b/src/librustc_expand/expand.rs index f08bed5731530..eb1f23ee5b36a 100644 --- a/src/librustc_expand/expand.rs +++ b/src/librustc_expand/expand.rs @@ -25,7 +25,7 @@ use syntax::ptr::P; use syntax::token; use syntax::tokenstream::{TokenStream, TokenTree}; use syntax::util::map_in_place::MapInPlace; -use syntax::visit::{self, Visitor}; +use syntax::visit::{self, AssocCtxt, Visitor}; use smallvec::{smallvec, SmallVec}; use std::io::ErrorKind; @@ -39,7 +39,7 @@ macro_rules! ast_fragments { $($Kind:ident($AstTy:ty) { $kind_name:expr; $(one fn $mut_visit_ast:ident; fn $visit_ast:ident;)? - $(many fn $flat_map_ast_elt:ident; fn $visit_ast_elt:ident;)? + $(many fn $flat_map_ast_elt:ident; fn $visit_ast_elt:ident($($args:tt)*);)? fn $make_ast:ident; })* ) => { @@ -127,7 +127,7 @@ macro_rules! ast_fragments { AstFragment::OptExpr(None) => {} $($(AstFragment::$Kind(ref ast) => visitor.$visit_ast(ast),)?)* $($(AstFragment::$Kind(ref ast) => for ast_elt in &ast[..] { - visitor.$visit_ast_elt(ast_elt); + visitor.$visit_ast_elt(ast_elt, $($args)*); })?)* } } @@ -147,52 +147,58 @@ ast_fragments! { Pat(P) { "pattern"; one fn visit_pat; fn visit_pat; fn make_pat; } Ty(P) { "type"; one fn visit_ty; fn visit_ty; fn make_ty; } Stmts(SmallVec<[ast::Stmt; 1]>) { - "statement"; many fn flat_map_stmt; fn visit_stmt; fn make_stmts; + "statement"; many fn flat_map_stmt; fn visit_stmt(); fn make_stmts; } Items(SmallVec<[P; 1]>) { - "item"; many fn flat_map_item; fn visit_item; fn make_items; + "item"; many fn flat_map_item; fn visit_item(); fn make_items; } TraitItems(SmallVec<[P; 1]>) { - "trait item"; many fn flat_map_trait_item; fn visit_trait_item; fn make_trait_items; + "trait item"; + many fn flat_map_trait_item; + fn visit_assoc_item(AssocCtxt::Trait); + fn make_trait_items; } ImplItems(SmallVec<[P; 1]>) { - "impl item"; many fn flat_map_impl_item; fn visit_impl_item; fn make_impl_items; + "impl item"; + many fn flat_map_impl_item; + fn visit_assoc_item(AssocCtxt::Impl); + fn make_impl_items; } ForeignItems(SmallVec<[P; 1]>) { "foreign item"; many fn flat_map_foreign_item; - fn visit_foreign_item; + fn visit_foreign_item(); fn make_foreign_items; } Arms(SmallVec<[ast::Arm; 1]>) { - "match arm"; many fn flat_map_arm; fn visit_arm; fn make_arms; + "match arm"; many fn flat_map_arm; fn visit_arm(); fn make_arms; } Fields(SmallVec<[ast::Field; 1]>) { - "field expression"; many fn flat_map_field; fn visit_field; fn make_fields; + "field expression"; many fn flat_map_field; fn visit_field(); fn make_fields; } FieldPats(SmallVec<[ast::FieldPat; 1]>) { "field pattern"; many fn flat_map_field_pattern; - fn visit_field_pattern; + fn visit_field_pattern(); fn make_field_patterns; } GenericParams(SmallVec<[ast::GenericParam; 1]>) { "generic parameter"; many fn flat_map_generic_param; - fn visit_generic_param; + fn visit_generic_param(); fn make_generic_params; } Params(SmallVec<[ast::Param; 1]>) { - "function parameter"; many fn flat_map_param; fn visit_param; fn make_params; + "function parameter"; many fn flat_map_param; fn visit_param(); fn make_params; } StructFields(SmallVec<[ast::StructField; 1]>) { "field"; many fn flat_map_struct_field; - fn visit_struct_field; + fn visit_struct_field(); fn make_struct_fields; } Variants(SmallVec<[ast::Variant; 1]>) { - "variant"; many fn flat_map_variant; fn visit_variant; fn make_variants; + "variant"; many fn flat_map_variant; fn visit_variant(); fn make_variants; } } @@ -851,7 +857,7 @@ pub fn parse_ast_fragment<'a>( AstFragmentKind::ForeignItems => { let mut items = SmallVec::new(); while this.token != token::Eof { - items.push(this.parse_foreign_item(DUMMY_SP)?); + items.push(this.parse_foreign_item()?); } AstFragment::ForeignItems(items) } diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 345665de63c3c..f024bb0e8b5c8 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -43,7 +43,7 @@ use rustc_span::{BytePos, Span}; use syntax::ast::{self, Expr}; use syntax::attr::{self, HasAttrs}; use syntax::tokenstream::{TokenStream, TokenTree}; -use syntax::visit::FnKind; +use syntax::visit::{FnCtxt, FnKind}; use crate::nonstandard_style::{method_context, MethodLateContext}; @@ -259,34 +259,22 @@ impl EarlyLintPass for UnsafeCode { } } - fn check_fn( - &mut self, - cx: &EarlyContext<'_>, - fk: FnKind<'_>, - _: &ast::FnDecl, - span: Span, - _: ast::NodeId, - ) { - match fk { - FnKind::ItemFn(_, ast::FnHeader { unsafety: ast::Unsafety::Unsafe, .. }, ..) => { - self.report_unsafe(cx, span, "declaration of an `unsafe` function") - } - - FnKind::Method(_, sig, ..) => { - if sig.header.unsafety == ast::Unsafety::Unsafe { - self.report_unsafe(cx, span, "implementation of an `unsafe` method") - } - } - - _ => (), - } - } - - fn check_trait_item(&mut self, cx: &EarlyContext<'_>, item: &ast::AssocItem) { - if let ast::AssocItemKind::Fn(ref sig, None) = item.kind { - if sig.header.unsafety == ast::Unsafety::Unsafe { - self.report_unsafe(cx, item.span, "declaration of an `unsafe` method") - } + fn check_fn(&mut self, cx: &EarlyContext<'_>, fk: FnKind<'_>, span: Span, _: ast::NodeId) { + if let FnKind::Fn( + ctxt, + _, + ast::FnSig { header: ast::FnHeader { unsafety: ast::Unsafety::Unsafe, .. }, .. }, + _, + body, + ) = fk + { + let msg = match ctxt { + FnCtxt::Foreign => return, + FnCtxt::Free => "declaration of an `unsafe` function", + FnCtxt::Assoc(_) if body.is_none() => "declaration of an `unsafe` method", + FnCtxt::Assoc(_) => "implementation of an `unsafe` method", + }; + self.report_unsafe(cx, span, msg); } } } diff --git a/src/librustc_lint/early.rs b/src/librustc_lint/early.rs index 490114b2d4d2a..542cbea0c954a 100644 --- a/src/librustc_lint/early.rs +++ b/src/librustc_lint/early.rs @@ -116,17 +116,11 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> ast_visit::walk_stmt(self, s); } - fn visit_fn( - &mut self, - fk: ast_visit::FnKind<'a>, - decl: &'a ast::FnDecl, - span: Span, - id: ast::NodeId, - ) { - run_early_pass!(self, check_fn, fk, decl, span, id); + fn visit_fn(&mut self, fk: ast_visit::FnKind<'a>, span: Span, id: ast::NodeId) { + run_early_pass!(self, check_fn, fk, span, id); self.check_id(id); - ast_visit::walk_fn(self, fk, decl, span); - run_early_pass!(self, check_fn_post, fk, decl, span, id); + ast_visit::walk_fn(self, fk, span); + run_early_pass!(self, check_fn_post, fk, span, id); } fn visit_variant_data(&mut self, s: &'a ast::VariantData) { @@ -213,19 +207,18 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> ast_visit::walk_poly_trait_ref(self, t, m); } - fn visit_trait_item(&mut self, trait_item: &'a ast::AssocItem) { - self.with_lint_attrs(trait_item.id, &trait_item.attrs, |cx| { - run_early_pass!(cx, check_trait_item, trait_item); - ast_visit::walk_trait_item(cx, trait_item); - run_early_pass!(cx, check_trait_item_post, trait_item); - }); - } - - fn visit_impl_item(&mut self, impl_item: &'a ast::AssocItem) { - self.with_lint_attrs(impl_item.id, &impl_item.attrs, |cx| { - run_early_pass!(cx, check_impl_item, impl_item); - ast_visit::walk_impl_item(cx, impl_item); - run_early_pass!(cx, check_impl_item_post, impl_item); + fn visit_assoc_item(&mut self, item: &'a ast::AssocItem, ctxt: ast_visit::AssocCtxt) { + self.with_lint_attrs(item.id, &item.attrs, |cx| match ctxt { + ast_visit::AssocCtxt::Trait => { + run_early_pass!(cx, check_trait_item, item); + ast_visit::walk_assoc_item(cx, item, ctxt); + run_early_pass!(cx, check_trait_item_post, item); + } + ast_visit::AssocCtxt::Impl => { + run_early_pass!(cx, check_impl_item, item); + ast_visit::walk_assoc_item(cx, item, ctxt); + run_early_pass!(cx, check_impl_item_post, item); + } }); } diff --git a/src/librustc_lint/passes.rs b/src/librustc_lint/passes.rs index 7e5d670767ad8..36de625cafac2 100644 --- a/src/librustc_lint/passes.rs +++ b/src/librustc_lint/passes.rs @@ -179,10 +179,9 @@ macro_rules! early_lint_methods { fn check_where_predicate(a: &ast::WherePredicate); fn check_poly_trait_ref(a: &ast::PolyTraitRef, b: &ast::TraitBoundModifier); - fn check_fn(a: syntax::visit::FnKind<'_>, b: &ast::FnDecl, c: Span, d_: ast::NodeId); + fn check_fn(a: syntax::visit::FnKind<'_>, c: Span, d_: ast::NodeId); fn check_fn_post( a: syntax::visit::FnKind<'_>, - b: &ast::FnDecl, c: Span, d: ast::NodeId ); diff --git a/src/librustc_parse/parser/diagnostics.rs b/src/librustc_parse/parser/diagnostics.rs index 5f148fa6ba2d3..7c015c7a1d75d 100644 --- a/src/librustc_parse/parser/diagnostics.rs +++ b/src/librustc_parse/parser/diagnostics.rs @@ -938,47 +938,6 @@ impl<'a> Parser<'a> { self.expect(&token::Semi).map(drop) // Error unconditionally } - pub(super) fn parse_semi_or_incorrect_foreign_fn_body( - &mut self, - ident: &Ident, - extern_sp: Span, - ) -> PResult<'a, ()> { - if self.token != token::Semi { - // This might be an incorrect fn definition (#62109). - let parser_snapshot = self.clone(); - match self.parse_inner_attrs_and_block() { - Ok((_, body)) => { - self.struct_span_err(ident.span, "incorrect `fn` inside `extern` block") - .span_label(ident.span, "can't have a body") - .span_label(body.span, "this body is invalid here") - .span_label( - extern_sp, - "`extern` blocks define existing foreign functions and `fn`s \ - inside of them cannot have a body", - ) - .help( - "you might have meant to write a function accessible through ffi, \ - which can be done by writing `extern fn` outside of the \ - `extern` block", - ) - .note( - "for more information, visit \ - https://doc.rust-lang.org/std/keyword.extern.html", - ) - .emit(); - } - Err(mut err) => { - err.cancel(); - mem::replace(self, parser_snapshot); - self.expect_semi()?; - } - } - } else { - self.bump(); - } - Ok(()) - } - /// Consumes alternative await syntaxes like `await!()`, `await `, /// `await? `, `await()`, and `await { }`. pub(super) fn recover_incorrect_await_syntax( diff --git a/src/librustc_parse/parser/item.rs b/src/librustc_parse/parser/item.rs index 87a6aa76f5777..07d8bae4725bd 100644 --- a/src/librustc_parse/parser/item.rs +++ b/src/librustc_parse/parser/item.rs @@ -97,7 +97,6 @@ impl<'a> Parser<'a> { } if self.eat_keyword(kw::Extern) { - let extern_sp = self.prev_span; if self.eat_keyword(kw::Crate) { return Ok(Some(self.parse_item_extern_crate(lo, vis, attrs)?)); } @@ -115,7 +114,7 @@ impl<'a> Parser<'a> { }; return self.parse_item_fn(lo, vis, attrs, header); } else if self.check(&token::OpenDelim(token::Brace)) { - return Ok(Some(self.parse_item_foreign_mod(lo, abi, vis, attrs, extern_sp)?)); + return Ok(Some(self.parse_item_foreign_mod(lo, abi, vis, attrs)?)); } self.unexpected()?; @@ -1046,7 +1045,6 @@ impl<'a> Parser<'a> { abi: Option, visibility: Visibility, mut attrs: Vec, - extern_sp: Span, ) -> PResult<'a, P> { self.expect(&token::OpenDelim(token::Brace))?; @@ -1054,7 +1052,7 @@ impl<'a> Parser<'a> { let mut foreign_items = vec![]; while !self.eat(&token::CloseDelim(token::Brace)) { - foreign_items.push(self.parse_foreign_item(extern_sp)?); + foreign_items.push(self.parse_foreign_item()?); } let prev_span = self.prev_span; @@ -1064,51 +1062,42 @@ impl<'a> Parser<'a> { } /// Parses a foreign item. - pub fn parse_foreign_item(&mut self, extern_sp: Span) -> PResult<'a, P> { + pub fn parse_foreign_item(&mut self) -> PResult<'a, P> { maybe_whole!(self, NtForeignItem, |ni| ni); let attrs = self.parse_outer_attributes()?; let lo = self.token.span; let visibility = self.parse_visibility(FollowedByType::No)?; + // FOREIGN TYPE ITEM + if self.check_keyword(kw::Type) { + return self.parse_item_foreign_type(visibility, lo, attrs); + } + // FOREIGN STATIC ITEM - // Treat `const` as `static` for error recovery, but don't add it to expected tokens. - if self.check_keyword(kw::Static) || self.token.is_keyword(kw::Const) { - if self.token.is_keyword(kw::Const) { - let mut err = - self.struct_span_err(self.token.span, "extern items cannot be `const`"); + if self.is_static_global() { + self.bump(); // `static` + return self.parse_item_foreign_static(visibility, lo, attrs); + } - // The user wrote 'const fn' - if self.is_keyword_ahead(1, &[kw::Fn, kw::Unsafe]) { - err.emit(); - // Consume `const` - self.bump(); - // Consume `unsafe` if present, since `extern` blocks - // don't allow it. This will leave behind a plain 'fn' - self.eat_keyword(kw::Unsafe); - // Treat 'const fn` as a plain `fn` for error recovery purposes. - // We've already emitted an error, so compilation is guaranteed - // to fail - return Ok(self.parse_item_foreign_fn(visibility, lo, attrs, extern_sp)?); - } - err.span_suggestion( - self.token.span, + // Treat `const` as `static` for error recovery, but don't add it to expected tokens. + if self.is_kw_followed_by_ident(kw::Const) { + self.bump(); // `const` + self.struct_span_err(self.prev_span, "extern items cannot be `const`") + .span_suggestion( + self.prev_span, "try using a static value", "static".to_owned(), Applicability::MachineApplicable, - ); - err.emit(); - } - self.bump(); // `static` or `const` - return Ok(self.parse_item_foreign_static(visibility, lo, attrs)?); + ) + .emit(); + return self.parse_item_foreign_static(visibility, lo, attrs); } + // FOREIGN FUNCTION ITEM - if self.check_keyword(kw::Fn) { - return Ok(self.parse_item_foreign_fn(visibility, lo, attrs, extern_sp)?); - } - // FOREIGN TYPE ITEM - if self.check_keyword(kw::Type) { - return Ok(self.parse_item_foreign_type(visibility, lo, attrs)?); + const MAY_INTRODUCE_FN: &[Symbol] = &[kw::Const, kw::Async, kw::Unsafe, kw::Extern, kw::Fn]; + if MAY_INTRODUCE_FN.iter().any(|&kw| self.check_keyword(kw)) { + return self.parse_item_foreign_fn(visibility, lo, attrs); } match self.parse_assoc_macro_invoc("extern", Some(&visibility), &mut false)? { @@ -1727,14 +1716,14 @@ impl<'a> Parser<'a> { &mut self, lo: Span, vis: Visibility, - attrs: Vec, + mut attrs: Vec, header: FnHeader, ) -> PResult<'a, Option>> { let cfg = ParamCfg { is_name_required: |_| true }; let (ident, decl, generics) = self.parse_fn_sig(&cfg)?; - let (inner_attrs, body) = self.parse_inner_attrs_and_block()?; + let body = self.parse_fn_body(&mut false, &mut attrs)?; let kind = ItemKind::Fn(FnSig { decl, header }, generics, body); - self.mk_item_with_info(attrs, lo, vis, (ident, kind, Some(inner_attrs))) + self.mk_item_with_info(attrs, lo, vis, (ident, kind, None)) } /// Parses a function declaration from a foreign module. @@ -1742,15 +1731,14 @@ impl<'a> Parser<'a> { &mut self, vis: ast::Visibility, lo: Span, - attrs: Vec, - extern_sp: Span, + mut attrs: Vec, ) -> PResult<'a, P> { let cfg = ParamCfg { is_name_required: |_| true }; - self.expect_keyword(kw::Fn)?; + let header = self.parse_fn_front_matter()?; let (ident, decl, generics) = self.parse_fn_sig(&cfg)?; - let span = lo.to(self.token.span); - self.parse_semi_or_incorrect_foreign_fn_body(&ident, extern_sp)?; - let kind = ForeignItemKind::Fn(decl, generics); + let body = self.parse_fn_body(&mut false, &mut attrs)?; + let kind = ForeignItemKind::Fn(FnSig { header, decl }, generics, body); + let span = lo.to(self.prev_span); Ok(P(ast::ForeignItem { ident, attrs, kind, id: DUMMY_NODE_ID, span, vis, tokens: None })) } @@ -1761,45 +1749,40 @@ impl<'a> Parser<'a> { is_name_required: fn(&token::Token) -> bool, ) -> PResult<'a, (Ident, AssocItemKind, Generics)> { let header = self.parse_fn_front_matter()?; - let (ident, decl, generics) = self.parse_fn_sig(&ParamCfg { is_name_required })?; - let sig = FnSig { header, decl }; - let body = self.parse_assoc_fn_body(at_end, attrs)?; - Ok((ident, AssocItemKind::Fn(sig, body), generics)) + let (ident, decl, generics) = self.parse_fn_sig(&&ParamCfg { is_name_required })?; + let body = self.parse_fn_body(at_end, attrs)?; + Ok((ident, AssocItemKind::Fn(FnSig { header, decl }, body), generics)) } - /// Parse the "body" of a method in an associated item definition. + /// Parse the "body" of a function. /// This can either be `;` when there's no body, - /// or e.g. a block when the method is a provided one. - fn parse_assoc_fn_body( + /// or e.g. a block when the function is a provided one. + fn parse_fn_body( &mut self, at_end: &mut bool, attrs: &mut Vec, ) -> PResult<'a, Option>> { - Ok(match self.token.kind { + let (inner_attrs, body) = match self.token.kind { token::Semi => { - debug!("parse_assoc_fn_body(): parsing required method"); self.bump(); - *at_end = true; - None + (Vec::new(), None) } token::OpenDelim(token::Brace) => { - debug!("parse_assoc_fn_body(): parsing provided method"); - *at_end = true; - let (inner_attrs, body) = self.parse_inner_attrs_and_block()?; - attrs.extend(inner_attrs.iter().cloned()); - Some(body) + let (attrs, body) = self.parse_inner_attrs_and_block()?; + (attrs, Some(body)) } token::Interpolated(ref nt) => match **nt { token::NtBlock(..) => { - *at_end = true; - let (inner_attrs, body) = self.parse_inner_attrs_and_block()?; - attrs.extend(inner_attrs.iter().cloned()); - Some(body) + let (attrs, body) = self.parse_inner_attrs_and_block()?; + (attrs, Some(body)) } _ => return self.expected_semi_or_open_brace(), }, _ => return self.expected_semi_or_open_brace(), - }) + }; + attrs.extend(inner_attrs); + *at_end = true; + Ok(body) } /// Parses all the "front matter" for a `fn` declaration, up to diff --git a/src/librustc_parse/parser/stmt.rs b/src/librustc_parse/parser/stmt.rs index ae8f1e4db1b38..f3a69729399c1 100644 --- a/src/librustc_parse/parser/stmt.rs +++ b/src/librustc_parse/parser/stmt.rs @@ -199,7 +199,7 @@ impl<'a> Parser<'a> { } } - fn is_kw_followed_by_ident(&self, kw: Symbol) -> bool { + pub(super) fn is_kw_followed_by_ident(&self, kw: Symbol) -> bool { self.token.is_keyword(kw) && self.look_ahead(1, |t| t.is_ident() && !t.is_reserved_ident()) } diff --git a/src/librustc_parse/parser/ty.rs b/src/librustc_parse/parser/ty.rs index 1ed80ed7350a9..990661bf6b5b9 100644 --- a/src/librustc_parse/parser/ty.rs +++ b/src/librustc_parse/parser/ty.rs @@ -36,18 +36,20 @@ impl BoundModifiers { } } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, PartialEq)] pub(super) enum AllowPlus { Yes, No, } +#[derive(PartialEq)] pub(super) enum RecoverQPath { Yes, No, } // Is `...` (`CVarArgs`) legal at this level of type parsing? +#[derive(PartialEq)] enum AllowCVariadic { Yes, No, @@ -106,7 +108,7 @@ impl<'a> Parser<'a> { recover_qpath: RecoverQPath, allow_c_variadic: AllowCVariadic, ) -> PResult<'a, P> { - let allow_qpath_recovery = matches!(recover_qpath, RecoverQPath::Yes); + let allow_qpath_recovery = recover_qpath == RecoverQPath::Yes; maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery); maybe_whole!(self, NtTy, |x| x); @@ -142,7 +144,7 @@ impl<'a> Parser<'a> { self.parse_ty_bare_fn(lifetime_defs)? } else { let path = self.parse_path(PathStyle::Type)?; - let parse_plus = matches!(allow_plus, AllowPlus::Yes) && self.check_plus(); + let parse_plus = allow_plus == AllowPlus::Yes && self.check_plus(); self.parse_remaining_bounds(lifetime_defs, path, lo, parse_plus)? } } else if self.eat_keyword(kw::Impl) { @@ -162,7 +164,7 @@ impl<'a> Parser<'a> { } else if self.token.is_path_start() { self.parse_path_start_ty(lo, allow_plus)? } else if self.eat(&token::DotDotDot) { - if let AllowCVariadic::Yes = allow_c_variadic { + if allow_c_variadic == AllowCVariadic::Yes { TyKind::CVarArgs } else { // FIXME(Centril): Should we just allow `...` syntactically @@ -200,7 +202,7 @@ impl<'a> Parser<'a> { if ts.len() == 1 && !trailing { let ty = ts.into_iter().nth(0).unwrap().into_inner(); - let maybe_bounds = matches!(allow_plus, AllowPlus::Yes) && self.token.is_like_plus(); + let maybe_bounds = allow_plus == AllowPlus::Yes && self.token.is_like_plus(); match ty.kind { // `(TY_BOUND_NOPAREN) + BOUND + ...`. TyKind::Path(None, path) if maybe_bounds => { @@ -355,7 +357,7 @@ impl<'a> Parser<'a> { args: self.parse_mac_args()?, prior_type_ascription: self.last_type_ascription, })) - } else if matches!(allow_plus, AllowPlus::Yes) && self.check_plus() { + } else if allow_plus == AllowPlus::Yes && self.check_plus() { // `Trait1 + Trait2 + 'a` self.parse_remaining_bounds(Vec::new(), path, lo, true) } else { @@ -396,7 +398,7 @@ impl<'a> Parser<'a> { Ok(bound) => bounds.push(bound), Err(neg_sp) => negative_bounds.push(neg_sp), } - if matches!(allow_plus, AllowPlus::No) || !self.eat_plus() { + if allow_plus == AllowPlus::No || !self.eat_plus() { break; } } diff --git a/src/librustc_passes/hir_stats.rs b/src/librustc_passes/hir_stats.rs index b6ca2b3a595db..c6c201fa38ec1 100644 --- a/src/librustc_passes/hir_stats.rs +++ b/src/librustc_passes/hir_stats.rs @@ -302,19 +302,18 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { ast_visit::walk_ty(self, t) } - fn visit_fn(&mut self, fk: ast_visit::FnKind<'v>, fd: &'v ast::FnDecl, s: Span, _: NodeId) { - self.record("FnDecl", Id::None, fd); - ast_visit::walk_fn(self, fk, fd, s) - } - - fn visit_trait_item(&mut self, ti: &'v ast::AssocItem) { - self.record("TraitItem", Id::None, ti); - ast_visit::walk_trait_item(self, ti) - } - - fn visit_impl_item(&mut self, ii: &'v ast::AssocItem) { - self.record("ImplItem", Id::None, ii); - ast_visit::walk_impl_item(self, ii) + fn visit_fn(&mut self, fk: ast_visit::FnKind<'v>, s: Span, _: NodeId) { + self.record("FnDecl", Id::None, fk.decl()); + ast_visit::walk_fn(self, fk, s) + } + + fn visit_assoc_item(&mut self, item: &'v ast::AssocItem, ctxt: ast_visit::AssocCtxt) { + let label = match ctxt { + ast_visit::AssocCtxt::Trait => "TraitItem", + ast_visit::AssocCtxt::Impl => "ImplItem", + }; + self.record(label, Id::None, item); + ast_visit::walk_assoc_item(self, item, ctxt); } fn visit_param_bound(&mut self, bounds: &'v ast::GenericBound) { diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index c77b588d7fbc3..d6ea737385cdd 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -36,7 +36,7 @@ use syntax::ast::{self, Block, ForeignItem, ForeignItemKind, Item, ItemKind, Nod use syntax::ast::{AssocItem, AssocItemKind, MetaItemKind, StmtKind}; use syntax::ast::{Ident, Name}; use syntax::token::{self, Token}; -use syntax::visit::{self, Visitor}; +use syntax::visit::{self, AssocCtxt, Visitor}; use log::debug; use std::cell::Cell; @@ -1234,7 +1234,7 @@ impl<'a, 'b> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b> { self.parent_scope.legacy = orig_current_legacy_scope; } - fn visit_trait_item(&mut self, item: &'b AssocItem) { + fn visit_assoc_item(&mut self, item: &'b AssocItem, ctxt: AssocCtxt) { let parent = self.parent_scope.module; if let AssocItemKind::Macro(_) = item.kind { @@ -1242,6 +1242,12 @@ impl<'a, 'b> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b> { return; } + if let AssocCtxt::Impl = ctxt { + self.resolve_visibility(&item.vis); + visit::walk_assoc_item(self, item, ctxt); + return; + } + // Add the item to the trait info. let item_def_id = self.r.definitions.local_def_id(item.id); let (res, ns) = match item.kind { @@ -1260,16 +1266,7 @@ impl<'a, 'b> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b> { let expansion = self.parent_scope.expansion; self.r.define(parent, item.ident, ns, (res, vis, item.span, expansion)); - visit::walk_trait_item(self, item); - } - - fn visit_impl_item(&mut self, item: &'b ast::AssocItem) { - if let ast::AssocItemKind::Macro(..) = item.kind { - self.visit_invoc(item.id); - } else { - self.resolve_visibility(&item.vis); - visit::walk_impl_item(self, item); - } + visit::walk_assoc_item(self, item, ctxt); } fn visit_token(&mut self, t: Token) { diff --git a/src/librustc_resolve/def_collector.rs b/src/librustc_resolve/def_collector.rs index 696ba0e994c7d..3a26197c1607a 100644 --- a/src/librustc_resolve/def_collector.rs +++ b/src/librustc_resolve/def_collector.rs @@ -125,7 +125,7 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> { &sig.header, generics, &sig.decl, - Some(body), + body.as_deref(), ); } ItemKind::Static(..) | ItemKind::Const(..) | ItemKind::Fn(..) => { @@ -213,39 +213,26 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> { visit::walk_generic_param(self, param); } - fn visit_trait_item(&mut self, ti: &'a AssocItem) { - let def_data = match ti.kind { - AssocItemKind::Fn(..) | AssocItemKind::Const(..) => DefPathData::ValueNs(ti.ident.name), - AssocItemKind::TyAlias(..) => DefPathData::TypeNs(ti.ident.name), - AssocItemKind::Macro(..) => return self.visit_macro_invoc(ti.id), - }; - - let def = self.create_def(ti.id, def_data, ti.span); - self.with_parent(def, |this| visit::walk_trait_item(this, ti)); - } - - fn visit_impl_item(&mut self, ii: &'a AssocItem) { - let def_data = match ii.kind { - AssocItemKind::Fn(FnSig { ref header, ref decl }, ref body) - if header.asyncness.node.is_async() => - { + fn visit_assoc_item(&mut self, i: &'a AssocItem, ctxt: visit::AssocCtxt) { + let def_data = match &i.kind { + AssocItemKind::Fn(FnSig { header, decl }, body) if header.asyncness.node.is_async() => { return self.visit_async_fn( - ii.id, - ii.ident.name, - ii.span, + i.id, + i.ident.name, + i.span, header, - &ii.generics, + &i.generics, decl, body.as_deref(), ); } - AssocItemKind::Fn(..) | AssocItemKind::Const(..) => DefPathData::ValueNs(ii.ident.name), - AssocItemKind::TyAlias(..) => DefPathData::TypeNs(ii.ident.name), - AssocItemKind::Macro(..) => return self.visit_macro_invoc(ii.id), + AssocItemKind::Fn(..) | AssocItemKind::Const(..) => DefPathData::ValueNs(i.ident.name), + AssocItemKind::TyAlias(..) => DefPathData::TypeNs(i.ident.name), + AssocItemKind::Macro(..) => return self.visit_macro_invoc(i.id), }; - let def = self.create_def(ii.id, def_data, ii.span); - self.with_parent(def, |this| visit::walk_impl_item(this, ii)); + let def = self.create_def(i.id, def_data, i.span); + self.with_parent(def, |this| visit::walk_assoc_item(this, i, ctxt)); } fn visit_pat(&mut self, pat: &'a Pat) { diff --git a/src/librustc_resolve/late.rs b/src/librustc_resolve/late.rs index f1622af130e77..01a0e568137b2 100644 --- a/src/librustc_resolve/late.rs +++ b/src/librustc_resolve/late.rs @@ -24,7 +24,7 @@ use smallvec::{smallvec, SmallVec}; use syntax::ast::*; use syntax::ptr::P; use syntax::util::lev_distance::find_best_match_for_name; -use syntax::visit::{self, FnKind, Visitor}; +use syntax::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor}; use syntax::{unwrap_or, walk_list}; use log::debug; @@ -437,7 +437,7 @@ impl<'a, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { } fn visit_foreign_item(&mut self, foreign_item: &'ast ForeignItem) { match foreign_item.kind { - ForeignItemKind::Fn(_, ref generics) => { + ForeignItemKind::Fn(_, ref generics, _) => { self.with_generic_param_rib(generics, ItemRibKind(HasGenericParams::Yes), |this| { visit::walk_foreign_item(this, foreign_item); }); @@ -452,13 +452,15 @@ impl<'a, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { } } } - fn visit_fn(&mut self, fn_kind: FnKind<'ast>, declaration: &'ast FnDecl, sp: Span, _: NodeId) { - let previous_value = replace(&mut self.diagnostic_metadata.current_function, Some(sp)); - debug!("(resolving function) entering function"); + fn visit_fn(&mut self, fn_kind: FnKind<'ast>, sp: Span, _: NodeId) { let rib_kind = match fn_kind { - FnKind::ItemFn(..) => FnItemRibKind, - FnKind::Method(..) | FnKind::Closure(_) => NormalRibKind, + FnKind::Fn(FnCtxt::Foreign, ..) => return visit::walk_fn(self, fn_kind, sp), + FnKind::Fn(FnCtxt::Free, ..) => FnItemRibKind, + FnKind::Fn(FnCtxt::Assoc(_), ..) | FnKind::Closure(..) => NormalRibKind, }; + let previous_value = replace(&mut self.diagnostic_metadata.current_function, Some(sp)); + debug!("(resolving function) entering function"); + let declaration = fn_kind.decl(); // Create a value rib for the function. self.with_rib(ValueNS, rib_kind, |this| { @@ -471,8 +473,8 @@ impl<'a, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { // Resolve the function body, potentially inside the body of an async closure match fn_kind { - FnKind::ItemFn(.., body) | FnKind::Method(.., body) => this.visit_block(body), - FnKind::Closure(body) => this.visit_expr(body), + FnKind::Fn(.., body) => walk_list!(this, visit_block, body), + FnKind::Closure(_, body) => this.visit_expr(body), }; debug!("(resolving function) leaving function"); @@ -843,12 +845,16 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { }); } } - AssocItemKind::Fn(_, _) => { - visit::walk_trait_item(this, trait_item) - } - AssocItemKind::TyAlias(..) => { - visit::walk_trait_item(this, trait_item) - } + AssocItemKind::Fn(_, _) => visit::walk_assoc_item( + this, + trait_item, + AssocCtxt::Trait, + ), + AssocItemKind::TyAlias(..) => visit::walk_assoc_item( + this, + trait_item, + AssocCtxt::Trait, + ), AssocItemKind::Macro(_) => { panic!("unexpanded macro in resolve!") } @@ -1128,7 +1134,11 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { ); this.with_constant_rib(|this| { - visit::walk_impl_item(this, impl_item) + visit::walk_assoc_item( + this, + impl_item, + AssocCtxt::Impl, + ) }); } AssocItemKind::Fn(..) => { @@ -1139,7 +1149,11 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { impl_item.span, |n, s| MethodNotMemberOfTrait(n, s)); - visit::walk_impl_item(this, impl_item); + visit::walk_assoc_item( + this, + impl_item, + AssocCtxt::Impl, + ) } AssocItemKind::TyAlias(_, _) => { // If this is a trait impl, ensure the type @@ -1149,7 +1163,11 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { impl_item.span, |n, s| TypeNotMemberOfTrait(n, s)); - visit::walk_impl_item(this, impl_item); + visit::walk_assoc_item( + this, + impl_item, + AssocCtxt::Impl, + ) } AssocItemKind::Macro(_) => panic!("unexpanded macro in resolve!"), diff --git a/src/librustc_save_analysis/dump_visitor.rs b/src/librustc_save_analysis/dump_visitor.rs index 3f436a1e27c2c..5ce81c104e17c 100644 --- a/src/librustc_save_analysis/dump_visitor.rs +++ b/src/librustc_save_analysis/dump_visitor.rs @@ -358,7 +358,7 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> { decl: &'l ast::FnDecl, header: &'l ast::FnHeader, ty_params: &'l ast::Generics, - body: &'l ast::Block, + body: Option<&'l ast::Block>, ) { let hir_id = self.tcx.hir().node_to_hir_id(item.id); self.nest_tables(item.id, |v| { @@ -392,7 +392,7 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> { } } - v.visit_block(&body); + walk_list!(v, visit_block, body); }); } @@ -1291,7 +1291,7 @@ impl<'l, 'tcx> Visitor<'l> for DumpVisitor<'l, 'tcx> { } } Fn(ref sig, ref ty_params, ref body) => { - self.process_fn(item, &sig.decl, &sig.header, ty_params, &body) + self.process_fn(item, &sig.decl, &sig.header, ty_params, body.as_deref()) } Static(ref typ, _, ref expr) => self.process_static_or_const_item(item, typ, expr), Const(ref typ, ref expr) => self.process_static_or_const_item(item, &typ, &expr), @@ -1515,7 +1515,8 @@ impl<'l, 'tcx> Visitor<'l> for DumpVisitor<'l, 'tcx> { let access = access_from!(self.save_ctxt, item, hir_id); match item.kind { - ast::ForeignItemKind::Fn(ref decl, ref generics) => { + ast::ForeignItemKind::Fn(ref sig, ref generics, _) => { + let decl = &sig.decl; if let Some(fn_data) = self.save_ctxt.get_extern_item_data(item) { down_cast_data!(fn_data, DefData, item.span); diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs index 89054441fa3bf..e32f47443667b 100644 --- a/src/librustc_save_analysis/lib.rs +++ b/src/librustc_save_analysis/lib.rs @@ -133,7 +133,7 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> { self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(item.id)) ); match item.kind { - ast::ForeignItemKind::Fn(ref decl, ref generics) => { + ast::ForeignItemKind::Fn(ref sig, ref generics, _) => { filter!(self.span_utils, item.ident.span); Some(Data::DefData(Def { @@ -142,7 +142,7 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> { span: self.span_from_span(item.ident.span), name: item.ident.to_string(), qualname, - value: make_signature(decl, generics), + value: make_signature(&sig.decl, generics), parent: None, children: vec![], decl_id: None, diff --git a/src/librustc_save_analysis/sig.rs b/src/librustc_save_analysis/sig.rs index dbf29b6531d2a..6401cabdcd5c1 100644 --- a/src/librustc_save_analysis/sig.rs +++ b/src/librustc_save_analysis/sig.rs @@ -723,7 +723,8 @@ impl Sig for ast::ForeignItem { fn make(&self, offset: usize, _parent_id: Option, scx: &SaveContext<'_, '_>) -> Result { let id = Some(self.id); match self.kind { - ast::ForeignItemKind::Fn(ref decl, ref generics) => { + ast::ForeignItemKind::Fn(ref sig, ref generics, _) => { + let decl = &sig.decl; let mut text = String::new(); text.push_str("fn "); diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 5a8c9f76ea943..b22406124e098 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -2533,6 +2533,17 @@ pub struct FnHeader { pub ext: Extern, } +impl FnHeader { + /// Does this function header have any qualifiers or is it empty? + pub fn has_qualifiers(&self) -> bool { + let Self { unsafety, asyncness, constness, ext } = self; + matches!(unsafety, Unsafety::Unsafe) + || asyncness.node.is_async() + || matches!(constness.node, Constness::Const) + || !matches!(ext, Extern::None) + } +} + impl Default for FnHeader { fn default() -> FnHeader { FnHeader { @@ -2565,7 +2576,7 @@ pub enum ItemKind { /// A function declaration (`fn`). /// /// E.g., `fn foo(bar: usize) -> usize { .. }`. - Fn(FnSig, Generics, P), + Fn(FnSig, Generics, Option>), /// A module declaration (`mod`). /// /// E.g., `mod foo;` or `mod foo { .. }`. @@ -2667,7 +2678,7 @@ pub type ForeignItem = Item; #[derive(Clone, RustcEncodable, RustcDecodable, Debug)] pub enum ForeignItemKind { /// A foreign function. - Fn(P, Generics), + Fn(FnSig, Generics, Option>), /// A foreign static item (`static ext: u8`). Static(P, Mutability), /// A foreign type. diff --git a/src/libsyntax/mut_visit.rs b/src/libsyntax/mut_visit.rs index 3bcdf8fe286e4..8517f223f92e3 100644 --- a/src/libsyntax/mut_visit.rs +++ b/src/libsyntax/mut_visit.rs @@ -901,7 +901,7 @@ pub fn noop_visit_item_kind(kind: &mut ItemKind, vis: &mut T) { ItemKind::Fn(sig, generics, body) => { visit_fn_sig(sig, vis); vis.visit_generics(generics); - vis.visit_block(body); + visit_opt(body, |body| vis.visit_block(body)); } ItemKind::Mod(m) => vis.visit_mod(m), ItemKind::ForeignMod(nm) => vis.visit_foreign_mod(nm), @@ -1044,9 +1044,10 @@ pub fn noop_flat_map_foreign_item( visitor.visit_ident(ident); visit_attrs(attrs, visitor); match kind { - ForeignItemKind::Fn(fdec, generics) => { - visitor.visit_fn_decl(fdec); + ForeignItemKind::Fn(sig, generics, body) => { + visit_fn_sig(sig, visitor); visitor.visit_generics(generics); + visit_opt(body, |body| visitor.visit_block(body)); } ForeignItemKind::Static(t, _m) => visitor.visit_ty(t), ForeignItemKind::Ty => {} diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 946a0d29cd399..73e731397c329 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -19,24 +19,47 @@ use crate::tokenstream::{TokenStream, TokenTree}; use rustc_span::Span; +#[derive(Copy, Clone, PartialEq)] +pub enum AssocCtxt { + Trait, + Impl, +} + +#[derive(Copy, Clone, PartialEq)] +pub enum FnCtxt { + Free, + Foreign, + Assoc(AssocCtxt), +} + #[derive(Copy, Clone)] pub enum FnKind<'a> { - /// E.g., `fn foo()` or `extern "Abi" fn foo()`. - ItemFn(Ident, &'a FnHeader, &'a Visibility, &'a Block), - - /// E.g., `fn foo(&self)`. - Method(Ident, &'a FnSig, &'a Visibility, &'a Block), + /// E.g., `fn foo()`, `fn foo(&self)`, or `extern "Abi" fn foo()`. + Fn(FnCtxt, Ident, &'a FnSig, &'a Visibility, Option<&'a Block>), /// E.g., `|x, y| body`. - Closure(&'a Expr), + Closure(&'a FnDecl, &'a Expr), } impl<'a> FnKind<'a> { pub fn header(&self) -> Option<&'a FnHeader> { match *self { - FnKind::ItemFn(_, header, _, _) => Some(header), - FnKind::Method(_, sig, _, _) => Some(&sig.header), - FnKind::Closure(_) => None, + FnKind::Fn(_, _, sig, _, _) => Some(&sig.header), + FnKind::Closure(_, _) => None, + } + } + + pub fn decl(&self) -> &'a FnDecl { + match self { + FnKind::Fn(_, _, sig, _, _) => &sig.decl, + FnKind::Closure(decl, _) => decl, + } + } + + pub fn ctxt(&self) -> Option { + match self { + FnKind::Fn(ctxt, ..) => Some(*ctxt), + FnKind::Closure(..) => None, } } } @@ -106,17 +129,11 @@ pub trait Visitor<'ast>: Sized { fn visit_where_predicate(&mut self, p: &'ast WherePredicate) { walk_where_predicate(self, p) } - fn visit_fn(&mut self, fk: FnKind<'ast>, fd: &'ast FnDecl, s: Span, _: NodeId) { - walk_fn(self, fk, fd, s) - } - fn visit_trait_item(&mut self, i: &'ast AssocItem) { - walk_trait_item(self, i) + fn visit_fn(&mut self, fk: FnKind<'ast>, s: Span, _: NodeId) { + walk_fn(self, fk, s) } - fn visit_impl_item(&mut self, i: &'ast AssocItem) { - walk_impl_item(self, i) - } - fn visit_assoc_item(&mut self, i: &'ast AssocItem) { - walk_assoc_item(self, i) + fn visit_assoc_item(&mut self, i: &'ast AssocItem, ctxt: AssocCtxt) { + walk_assoc_item(self, i, ctxt) } fn visit_trait_ref(&mut self, t: &'ast TraitRef) { walk_trait_ref(self, t) @@ -287,13 +304,8 @@ pub fn walk_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a Item) { } ItemKind::Fn(ref sig, ref generics, ref body) => { visitor.visit_generics(generics); - visitor.visit_fn_header(&sig.header); - visitor.visit_fn( - FnKind::ItemFn(item.ident, &sig.header, &item.vis, body), - &sig.decl, - item.span, - item.id, - ) + let kind = FnKind::Fn(FnCtxt::Free, item.ident, sig, &item.vis, body.as_deref()); + visitor.visit_fn(kind, item.span, item.id) } ItemKind::Mod(ref module) => visitor.visit_mod(module, item.span, &item.attrs, item.id), ItemKind::ForeignMod(ref foreign_module) => { @@ -321,17 +333,17 @@ pub fn walk_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a Item) { visitor.visit_generics(generics); walk_list!(visitor, visit_trait_ref, of_trait); visitor.visit_ty(self_ty); - walk_list!(visitor, visit_impl_item, items); + walk_list!(visitor, visit_assoc_item, items, AssocCtxt::Impl); } ItemKind::Struct(ref struct_definition, ref generics) | ItemKind::Union(ref struct_definition, ref generics) => { visitor.visit_generics(generics); visitor.visit_variant_data(struct_definition); } - ItemKind::Trait(.., ref generics, ref bounds, ref methods) => { + ItemKind::Trait(.., ref generics, ref bounds, ref items) => { visitor.visit_generics(generics); walk_list!(visitor, visit_param_bound, bounds); - walk_list!(visitor, visit_trait_item, methods); + walk_list!(visitor, visit_assoc_item, items, AssocCtxt::Trait); } ItemKind::TraitAlias(ref generics, ref bounds) => { visitor.visit_generics(generics); @@ -512,21 +524,22 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) { } } -pub fn walk_foreign_item<'a, V: Visitor<'a>>(visitor: &mut V, foreign_item: &'a ForeignItem) { - visitor.visit_vis(&foreign_item.vis); - visitor.visit_ident(foreign_item.ident); +pub fn walk_foreign_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a ForeignItem) { + visitor.visit_vis(&item.vis); + visitor.visit_ident(item.ident); - match foreign_item.kind { - ForeignItemKind::Fn(ref function_declaration, ref generics) => { - walk_fn_decl(visitor, function_declaration); - visitor.visit_generics(generics) + match item.kind { + ForeignItemKind::Fn(ref sig, ref generics, ref body) => { + visitor.visit_generics(generics); + let kind = FnKind::Fn(FnCtxt::Foreign, item.ident, sig, &item.vis, body.as_deref()); + visitor.visit_fn(kind, item.span, item.id); } ForeignItemKind::Static(ref typ, _) => visitor.visit_ty(typ), ForeignItemKind::Ty => (), ForeignItemKind::Macro(ref mac) => visitor.visit_mac(mac), } - walk_list!(visitor, visit_attribute, &foreign_item.attrs); + walk_list!(visitor, visit_attribute, &item.attrs); } pub fn walk_global_asm<'a, V: Visitor<'a>>(_: &mut V, _: &'a GlobalAsm) { @@ -594,37 +607,21 @@ pub fn walk_fn_decl<'a, V: Visitor<'a>>(visitor: &mut V, function_declaration: & visitor.visit_fn_ret_ty(&function_declaration.output); } -pub fn walk_fn<'a, V>(visitor: &mut V, kind: FnKind<'a>, declaration: &'a FnDecl, _span: Span) -where - V: Visitor<'a>, -{ +pub fn walk_fn<'a, V: Visitor<'a>>(visitor: &mut V, kind: FnKind<'a>, _span: Span) { match kind { - FnKind::ItemFn(_, header, _, body) => { - visitor.visit_fn_header(header); - walk_fn_decl(visitor, declaration); - visitor.visit_block(body); - } - FnKind::Method(_, sig, _, body) => { + FnKind::Fn(_, _, sig, _, body) => { visitor.visit_fn_header(&sig.header); - walk_fn_decl(visitor, declaration); - visitor.visit_block(body); + walk_fn_decl(visitor, &sig.decl); + walk_list!(visitor, visit_block, body); } - FnKind::Closure(body) => { - walk_fn_decl(visitor, declaration); + FnKind::Closure(decl, body) => { + walk_fn_decl(visitor, decl); visitor.visit_expr(body); } } } -pub fn walk_impl_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a AssocItem) { - visitor.visit_assoc_item(item); -} - -pub fn walk_trait_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a AssocItem) { - visitor.visit_assoc_item(item); -} - -pub fn walk_assoc_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a AssocItem) { +pub fn walk_assoc_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a AssocItem, ctxt: AssocCtxt) { visitor.visit_vis(&item.vis); visitor.visit_ident(item.ident); walk_list!(visitor, visit_attribute, &item.attrs); @@ -634,17 +631,9 @@ pub fn walk_assoc_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a AssocItem) visitor.visit_ty(ty); walk_list!(visitor, visit_expr, expr); } - AssocItemKind::Fn(ref sig, None) => { - visitor.visit_fn_header(&sig.header); - walk_fn_decl(visitor, &sig.decl); - } - AssocItemKind::Fn(ref sig, Some(ref body)) => { - visitor.visit_fn( - FnKind::Method(item.ident, sig, &item.vis, body), - &sig.decl, - item.span, - item.id, - ); + AssocItemKind::Fn(ref sig, ref body) => { + let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), item.ident, sig, &item.vis, body.as_deref()); + visitor.visit_fn(kind, item.span, item.id); } AssocItemKind::TyAlias(ref bounds, ref ty) => { walk_list!(visitor, visit_param_bound, bounds); @@ -765,8 +754,9 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { visitor.visit_expr(subexpression); walk_list!(visitor, visit_arm, arms); } - ExprKind::Closure(_, _, _, ref function_declaration, ref body, _decl_span) => visitor - .visit_fn(FnKind::Closure(body), function_declaration, expression.span, expression.id), + ExprKind::Closure(_, _, _, ref decl, ref body, _decl_span) => { + visitor.visit_fn(FnKind::Closure(decl, body), expression.span, expression.id) + } ExprKind::Block(ref block, ref opt_label) => { walk_list!(visitor, visit_label, opt_label); visitor.visit_block(block); diff --git a/src/test/ui/extern/extern-ffi-fn-with-body.rs b/src/test/ui/extern/extern-ffi-fn-with-body.rs index 4cf563514ea60..ef234e8afd8ca 100644 --- a/src/test/ui/extern/extern-ffi-fn-with-body.rs +++ b/src/test/ui/extern/extern-ffi-fn-with-body.rs @@ -1,5 +1,5 @@ extern "C" { - fn foo() -> i32 { //~ ERROR incorrect `fn` inside `extern` block + fn foo() -> i32 { //~ ERROR incorrect function inside `extern` block return 0; } } diff --git a/src/test/ui/extern/extern-ffi-fn-with-body.stderr b/src/test/ui/extern/extern-ffi-fn-with-body.stderr index 4ac3ce1f93eab..079c9cecd8ed4 100644 --- a/src/test/ui/extern/extern-ffi-fn-with-body.stderr +++ b/src/test/ui/extern/extern-ffi-fn-with-body.stderr @@ -1,17 +1,17 @@ -error: incorrect `fn` inside `extern` block +error: incorrect function inside `extern` block --> $DIR/extern-ffi-fn-with-body.rs:2:8 | LL | extern "C" { - | ------ `extern` blocks define existing foreign functions and `fn`s inside of them cannot have a body + | ---------- `extern` blocks define existing foreign functions and functions inside of them cannot have a body LL | fn foo() -> i32 { | ________^^^__________- | | | - | | can't have a body + | | cannot have a body LL | | return 0; LL | | } - | |_____- this body is invalid here + | |_____- help: remove the invalid body: `;` | - = help: you might have meant to write a function accessible through ffi, which can be done by writing `extern fn` outside of the `extern` block + = help: you might have meant to write a function accessible through FFI, which can be done by writing `extern fn` outside of the `extern` block = note: for more information, visit https://doc.rust-lang.org/std/keyword.extern.html error: aborting due to previous error diff --git a/src/test/ui/issues/issue-39616.rs b/src/test/ui/issues/issue-39616.rs index 3d8e28e5c2f52..428856a36b425 100644 --- a/src/test/ui/issues/issue-39616.rs +++ b/src/test/ui/issues/issue-39616.rs @@ -1,5 +1,4 @@ fn foo(a: [0; 1]) {} //~ ERROR expected type, found `0` -//~| ERROR expected one of `)`, `,`, `->`, `where`, or `{`, found `]` -// FIXME(jseyfried): avoid emitting the second error (preexisting) +//~| ERROR expected `;` or `{`, found `]` fn main() {} diff --git a/src/test/ui/issues/issue-39616.stderr b/src/test/ui/issues/issue-39616.stderr index 74e94eda51faa..ced582746617b 100644 --- a/src/test/ui/issues/issue-39616.stderr +++ b/src/test/ui/issues/issue-39616.stderr @@ -4,11 +4,11 @@ error: expected type, found `0` LL | fn foo(a: [0; 1]) {} | ^ expected type -error: expected one of `)`, `,`, `->`, `where`, or `{`, found `]` +error: expected `;` or `{`, found `]` --> $DIR/issue-39616.rs:1:16 | LL | fn foo(a: [0; 1]) {} - | ^ expected one of `)`, `,`, `->`, `where`, or `{` + | ^ expected `;` or `{` error: aborting due to 2 previous errors diff --git a/src/test/ui/macros/issue-54441.stderr b/src/test/ui/macros/issue-54441.stderr index 1139ef06a1263..92d1afe1b645a 100644 --- a/src/test/ui/macros/issue-54441.stderr +++ b/src/test/ui/macros/issue-54441.stderr @@ -1,8 +1,8 @@ -error: expected one of `crate`, `fn`, `pub`, `static`, or `type`, found keyword `let` +error: expected one of `async`, `const`, `crate`, `extern`, `fn`, `pub`, `static`, `type`, or `unsafe`, found keyword `let` --> $DIR/issue-54441.rs:3:9 | LL | let - | ^^^ expected one of `crate`, `fn`, `pub`, `static`, or `type` + | ^^^ expected one of 9 possible tokens ... LL | m!(); | ----- in this macro invocation diff --git a/src/test/ui/no-patterns-in-args-2.rs b/src/test/ui/no-patterns-in-args-2.rs index ccf57478b4850..85b7fc5cdba49 100644 --- a/src/test/ui/no-patterns-in-args-2.rs +++ b/src/test/ui/no-patterns-in-args-2.rs @@ -1,9 +1,9 @@ #![deny(patterns_in_fns_without_body)] trait Tr { - fn f1(mut arg: u8); //~ ERROR patterns aren't allowed in methods without bodies + fn f1(mut arg: u8); //~ ERROR patterns aren't allowed in functions without bodies //~^ WARN was previously accepted - fn f2(&arg: u8); //~ ERROR patterns aren't allowed in methods without bodies + fn f2(&arg: u8); //~ ERROR patterns aren't allowed in functions without bodies fn g1(arg: u8); // OK fn g2(_: u8); // OK #[allow(anonymous_parameters)] diff --git a/src/test/ui/no-patterns-in-args-2.stderr b/src/test/ui/no-patterns-in-args-2.stderr index 905a89af4e587..21f4439d89009 100644 --- a/src/test/ui/no-patterns-in-args-2.stderr +++ b/src/test/ui/no-patterns-in-args-2.stderr @@ -1,10 +1,10 @@ -error[E0642]: patterns aren't allowed in methods without bodies +error[E0642]: patterns aren't allowed in functions without bodies --> $DIR/no-patterns-in-args-2.rs:6:11 | LL | fn f2(&arg: u8); - | ^^^^ + | ^^^^ pattern not allowed in function without body -error: patterns aren't allowed in methods without bodies +error: patterns aren't allowed in functions without bodies --> $DIR/no-patterns-in-args-2.rs:4:11 | LL | fn f1(mut arg: u8); diff --git a/src/test/ui/no-patterns-in-args-macro.rs b/src/test/ui/no-patterns-in-args-macro.rs index 59cb99453987c..b5109f9c28696 100644 --- a/src/test/ui/no-patterns-in-args-macro.rs +++ b/src/test/ui/no-patterns-in-args-macro.rs @@ -6,10 +6,10 @@ macro_rules! m { type A = fn($pat: u8); - extern { + extern "C" { fn foreign_fn($pat: u8); } - } + }; } mod good_pat { @@ -20,7 +20,7 @@ mod bad_pat { m!((bad, pat)); //~^ ERROR patterns aren't allowed in function pointer types //~| ERROR patterns aren't allowed in foreign function declarations - //~| ERROR patterns aren't allowed in methods without bodies + //~| ERROR patterns aren't allowed in functions without bodies } fn main() {} diff --git a/src/test/ui/no-patterns-in-args-macro.stderr b/src/test/ui/no-patterns-in-args-macro.stderr index f21df68d5a2cc..0016c7953f344 100644 --- a/src/test/ui/no-patterns-in-args-macro.stderr +++ b/src/test/ui/no-patterns-in-args-macro.stderr @@ -1,8 +1,8 @@ -error[E0642]: patterns aren't allowed in methods without bodies +error[E0642]: patterns aren't allowed in functions without bodies --> $DIR/no-patterns-in-args-macro.rs:20:8 | LL | m!((bad, pat)); - | ^^^^^^^^^^ + | ^^^^^^^^^^ pattern not allowed in function without body error[E0561]: patterns aren't allowed in function pointer types --> $DIR/no-patterns-in-args-macro.rs:20:8 diff --git a/src/test/ui/parser/duplicate-visibility.rs b/src/test/ui/parser/duplicate-visibility.rs index bb17e97e950c6..a8f0b7d61b985 100644 --- a/src/test/ui/parser/duplicate-visibility.rs +++ b/src/test/ui/parser/duplicate-visibility.rs @@ -1,4 +1,4 @@ -// error-pattern:expected one of `(`, `fn`, `static`, or `type` +// error-pattern: expected one of `(`, `async`, `const`, `extern`, `fn` extern { pub pub fn foo(); } diff --git a/src/test/ui/parser/duplicate-visibility.stderr b/src/test/ui/parser/duplicate-visibility.stderr index 313e88e812bb5..cba4058e48255 100644 --- a/src/test/ui/parser/duplicate-visibility.stderr +++ b/src/test/ui/parser/duplicate-visibility.stderr @@ -1,8 +1,8 @@ -error: expected one of `(`, `fn`, `static`, or `type`, found keyword `pub` +error: expected one of `(`, `async`, `const`, `extern`, `fn`, `static`, `type`, or `unsafe`, found keyword `pub` --> $DIR/duplicate-visibility.rs:3:9 | LL | pub pub fn foo(); - | ^^^ expected one of `(`, `fn`, `static`, or `type` + | ^^^ expected one of 8 possible tokens error: aborting due to previous error diff --git a/src/test/ui/parser/fn-body-optional-semantic-fail.rs b/src/test/ui/parser/fn-body-optional-semantic-fail.rs new file mode 100644 index 0000000000000..38def05e8f2b3 --- /dev/null +++ b/src/test/ui/parser/fn-body-optional-semantic-fail.rs @@ -0,0 +1,27 @@ +// Tests the different rules for `fn` forms requiring the presence or lack of a body. + +fn main() { + fn f1(); //~ ERROR free function without a body + fn f2() {} // OK. + + trait X { + fn f1(); // OK. + fn f2() {} // OK. + } + + struct Y; + impl X for Y { + fn f1(); //~ ERROR associated function in `impl` without body + fn f2() {} // OK. + } + + impl Y { + fn f3(); //~ ERROR associated function in `impl` without body + fn f4() {} // OK. + } + + extern { + fn f5(); // OK. + fn f6() {} //~ ERROR incorrect function inside `extern` block + } +} diff --git a/src/test/ui/parser/fn-body-optional-semantic-fail.stderr b/src/test/ui/parser/fn-body-optional-semantic-fail.stderr new file mode 100644 index 0000000000000..23ce98fb5d787 --- /dev/null +++ b/src/test/ui/parser/fn-body-optional-semantic-fail.stderr @@ -0,0 +1,40 @@ +error: free function without a body + --> $DIR/fn-body-optional-semantic-fail.rs:4:5 + | +LL | fn f1(); + | ^^^^^^^- + | | + | help: provide a definition for the function: `{ }` + +error: associated function in `impl` without body + --> $DIR/fn-body-optional-semantic-fail.rs:14:9 + | +LL | fn f1(); + | ^^^^^^^- + | | + | help: provide a definition for the function: `{ }` + +error: associated function in `impl` without body + --> $DIR/fn-body-optional-semantic-fail.rs:19:9 + | +LL | fn f3(); + | ^^^^^^^- + | | + | help: provide a definition for the function: `{ }` + +error: incorrect function inside `extern` block + --> $DIR/fn-body-optional-semantic-fail.rs:25:12 + | +LL | extern { + | ------ `extern` blocks define existing foreign functions and functions inside of them cannot have a body +LL | fn f5(); // OK. +LL | fn f6() {} + | ^^ -- help: remove the invalid body: `;` + | | + | cannot have a body + | + = help: you might have meant to write a function accessible through FFI, which can be done by writing `extern fn` outside of the `extern` block + = note: for more information, visit https://doc.rust-lang.org/std/keyword.extern.html + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/parser/fn-body-optional-syntactic-pass.rs b/src/test/ui/parser/fn-body-optional-syntactic-pass.rs new file mode 100644 index 0000000000000..e7991c73b4b77 --- /dev/null +++ b/src/test/ui/parser/fn-body-optional-syntactic-pass.rs @@ -0,0 +1,31 @@ +// Ensures that all `fn` forms having or lacking a body are syntactically valid. + +// check-pass + +fn main() {} + +#[cfg(FALSE)] +fn syntax() { + fn f(); + fn f() {} + + trait X { + fn f(); + fn f() {} + } + + impl X for Y { + fn f(); + fn f() {} + } + + impl Y { + fn f(); + fn f() {} + } + + extern { + fn f(); + fn f() {} + } +} diff --git a/src/test/ui/parser/fn-header-semantic-fail.rs b/src/test/ui/parser/fn-header-semantic-fail.rs new file mode 100644 index 0000000000000..c2b7e69c80d8e --- /dev/null +++ b/src/test/ui/parser/fn-header-semantic-fail.rs @@ -0,0 +1,57 @@ +// Ensures that all `fn` forms can have all the function qualifiers syntactically. + +// edition:2018 + +#![feature(const_extern_fn)] +#![feature(const_fn)] + +fn main() { + async fn ff1() {} // OK. + unsafe fn ff2() {} // OK. + const fn ff3() {} // OK. + extern "C" fn ff4() {} // OK. + const /* async */ unsafe extern "C" fn ff5() {} // OK. + //^ FIXME(Centril): `async` should be legal syntactically, ensure it's illegal semantically. + + trait X { + async fn ft1(); //~ ERROR trait fns cannot be declared `async` + unsafe fn ft2(); // OK. + const fn ft3(); //~ ERROR trait fns cannot be declared const + extern "C" fn ft4(); // OK. + /* const */ async unsafe extern "C" fn ft5(); + //~^ ERROR trait fns cannot be declared `async` + //^ FIXME(Centril): `const` should be legal syntactically, ensure it's illegal semantically. + } + + struct Y; + impl X for Y { + async fn ft1() {} //~ ERROR trait fns cannot be declared `async` + //~^ ERROR method `ft1` has an incompatible type for trait + unsafe fn ft2() {} // OK. + const fn ft3() {} //~ ERROR trait fns cannot be declared const + extern "C" fn ft4() {} + /* const */ async unsafe extern "C" fn ft5() {} + //~^ ERROR trait fns cannot be declared `async` + //~| ERROR method `ft5` has an incompatible type for trait + //^ FIXME(Centril): `const` should be legal syntactically, ensure it's illegal semantically. + } + + impl Y { + async fn fi1() {} // OK. + unsafe fn fi2() {} // OK. + const fn fi3() {} // OK. + extern "C" fn fi4() {} // OK. + /* const */ async unsafe extern "C" fn fi5() {} // OK. + //^ FIXME(Centril): `const` should be legal syntactically, ensure it's illegal semantically. + } + + extern { + async fn fe1(); //~ ERROR functions in `extern` blocks cannot have qualifiers + unsafe fn fe2(); //~ ERROR functions in `extern` blocks cannot have qualifiers + const fn fe3(); //~ ERROR functions in `extern` blocks cannot have qualifiers + extern "C" fn fe4(); //~ ERROR functions in `extern` blocks cannot have qualifiers + /* const */ async unsafe extern "C" fn fe5(); + //~^ ERROR functions in `extern` blocks cannot have qualifiers + //^ FIXME(Centril): `const` should be legal syntactically, ensure it's illegal semantically. + } +} diff --git a/src/test/ui/parser/fn-header-semantic-fail.stderr b/src/test/ui/parser/fn-header-semantic-fail.stderr new file mode 100644 index 0000000000000..41d2d9b7faaf1 --- /dev/null +++ b/src/test/ui/parser/fn-header-semantic-fail.stderr @@ -0,0 +1,136 @@ +error[E0706]: trait fns cannot be declared `async` + --> $DIR/fn-header-semantic-fail.rs:17:9 + | +LL | async fn ft1(); + | ^^^^^^^^^^^^^^^ + | + = note: `async` trait functions are not currently supported + = note: consider using the `async-trait` crate: https://crates.io/crates/async-trait + +error[E0379]: trait fns cannot be declared const + --> $DIR/fn-header-semantic-fail.rs:19:9 + | +LL | const fn ft3(); + | ^^^^^ trait fns cannot be const + +error[E0706]: trait fns cannot be declared `async` + --> $DIR/fn-header-semantic-fail.rs:21:21 + | +LL | /* const */ async unsafe extern "C" fn ft5(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `async` trait functions are not currently supported + = note: consider using the `async-trait` crate: https://crates.io/crates/async-trait + +error[E0706]: trait fns cannot be declared `async` + --> $DIR/fn-header-semantic-fail.rs:28:9 + | +LL | async fn ft1() {} + | ^^^^^^^^^^^^^^^^^ + | + = note: `async` trait functions are not currently supported + = note: consider using the `async-trait` crate: https://crates.io/crates/async-trait + +error[E0379]: trait fns cannot be declared const + --> $DIR/fn-header-semantic-fail.rs:31:9 + | +LL | const fn ft3() {} + | ^^^^^ trait fns cannot be const + +error[E0706]: trait fns cannot be declared `async` + --> $DIR/fn-header-semantic-fail.rs:33:21 + | +LL | /* const */ async unsafe extern "C" fn ft5() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `async` trait functions are not currently supported + = note: consider using the `async-trait` crate: https://crates.io/crates/async-trait + +error: functions in `extern` blocks cannot have qualifiers + --> $DIR/fn-header-semantic-fail.rs:49:18 + | +LL | extern { + | ------ in this `extern` block +LL | async fn fe1(); + | ---------^^^ + | | + | help: remove the qualifiers: `fn` + +error: functions in `extern` blocks cannot have qualifiers + --> $DIR/fn-header-semantic-fail.rs:50:19 + | +LL | extern { + | ------ in this `extern` block +LL | async fn fe1(); +LL | unsafe fn fe2(); + | ----------^^^ + | | + | help: remove the qualifiers: `fn` + +error: functions in `extern` blocks cannot have qualifiers + --> $DIR/fn-header-semantic-fail.rs:51:18 + | +LL | extern { + | ------ in this `extern` block +... +LL | const fn fe3(); + | ---------^^^ + | | + | help: remove the qualifiers: `fn` + +error: functions in `extern` blocks cannot have qualifiers + --> $DIR/fn-header-semantic-fail.rs:52:23 + | +LL | extern { + | ------ in this `extern` block +... +LL | extern "C" fn fe4(); + | --------------^^^ + | | + | help: remove the qualifiers: `fn` + +error: functions in `extern` blocks cannot have qualifiers + --> $DIR/fn-header-semantic-fail.rs:53:48 + | +LL | extern { + | ------ in this `extern` block +... +LL | /* const */ async unsafe extern "C" fn fe5(); + | ---------------------------^^^ + | | + | help: remove the qualifiers: `fn` + +error[E0053]: method `ft1` has an incompatible type for trait + --> $DIR/fn-header-semantic-fail.rs:28:24 + | +LL | async fn ft1(); + | - type in trait +... +LL | async fn ft1() {} + | ^ + | | + | the `Output` of this `async fn`'s found opaque type + | expected `()`, found opaque type + | + = note: expected fn pointer `fn()` + found fn pointer `fn() -> impl std::future::Future` + +error[E0053]: method `ft5` has an incompatible type for trait + --> $DIR/fn-header-semantic-fail.rs:33:54 + | +LL | /* const */ async unsafe extern "C" fn ft5(); + | - type in trait +... +LL | /* const */ async unsafe extern "C" fn ft5() {} + | ^ + | | + | the `Output` of this `async fn`'s found opaque type + | expected `()`, found opaque type + | + = note: expected fn pointer `unsafe extern "C" fn()` + found fn pointer `unsafe extern "C" fn() -> impl std::future::Future` + +error: aborting due to 13 previous errors + +Some errors have detailed explanations: E0053, E0379, E0706. +For more information about an error, try `rustc --explain E0053`. diff --git a/src/test/ui/parser/fn-header-syntactic-pass.rs b/src/test/ui/parser/fn-header-syntactic-pass.rs new file mode 100644 index 0000000000000..145a208cb249d --- /dev/null +++ b/src/test/ui/parser/fn-header-syntactic-pass.rs @@ -0,0 +1,55 @@ +// Ensures that all `fn` forms can have all the function qualifiers syntactically. + +// check-pass +// edition:2018 + +#![feature(const_extern_fn)] +//^ FIXME(Centril): move check to ast_validation. + +fn main() {} + +#[cfg(FALSE)] +fn syntax() { + async fn f(); + unsafe fn f(); + const fn f(); + extern "C" fn f(); + const /* async */ unsafe extern "C" fn f(); + //^ FIXME(Centril): `async` should be legal syntactically. + + trait X { + async fn f(); + unsafe fn f(); + const fn f(); + extern "C" fn f(); + /* const */ async unsafe extern "C" fn f(); + //^ FIXME(Centril): `const` should be legal syntactically. + } + + impl X for Y { + async fn f(); + unsafe fn f(); + const fn f(); + extern "C" fn f(); + /* const */ async unsafe extern "C" fn f(); + //^ FIXME(Centril): `const` should be legal syntactically. + } + + impl Y { + async fn f(); + unsafe fn f(); + const fn f(); + extern "C" fn f(); + /* const */ async unsafe extern "C" fn f(); + //^ FIXME(Centril): `const` should be legal syntactically. + } + + extern { + async fn f(); + unsafe fn f(); + const fn f(); + extern "C" fn f(); + /* const */ async unsafe extern "C" fn f(); + //^ FIXME(Centril): `const` should be legal syntactically. + } +} diff --git a/src/test/ui/parser/issue-24780.rs b/src/test/ui/parser/issue-24780.rs index 799cdd8022257..8b46aa2bf22a1 100644 --- a/src/test/ui/parser/issue-24780.rs +++ b/src/test/ui/parser/issue-24780.rs @@ -3,6 +3,6 @@ // expected one of ..., `>`, ... found `>` fn foo() -> Vec> { - //~^ ERROR expected one of `!`, `+`, `::`, `where`, or `{`, found `>` + //~^ ERROR expected `;` or `{`, found `>` Vec::new() } diff --git a/src/test/ui/parser/issue-24780.stderr b/src/test/ui/parser/issue-24780.stderr index d9470191b25a4..d65a5f448739a 100644 --- a/src/test/ui/parser/issue-24780.stderr +++ b/src/test/ui/parser/issue-24780.stderr @@ -1,8 +1,8 @@ -error: expected one of `!`, `+`, `::`, `where`, or `{`, found `>` +error: expected `;` or `{`, found `>` --> $DIR/issue-24780.rs:5:23 | LL | fn foo() -> Vec> { - | ^ expected one of `!`, `+`, `::`, `where`, or `{` + | ^ expected `;` or `{` error: aborting due to previous error diff --git a/src/test/ui/parser/issue-63135.rs b/src/test/ui/parser/issue-63135.rs index d5f5f1469f35a..7d46b8904f033 100644 --- a/src/test/ui/parser/issue-63135.rs +++ b/src/test/ui/parser/issue-63135.rs @@ -1,3 +1,3 @@ -// error-pattern: aborting due to 6 previous errors +// error-pattern: aborting due to 7 previous errors fn i(n{...,f # diff --git a/src/test/ui/parser/issue-63135.stderr b/src/test/ui/parser/issue-63135.stderr index 462fdf11f40a9..04afae93be0e5 100644 --- a/src/test/ui/parser/issue-63135.stderr +++ b/src/test/ui/parser/issue-63135.stderr @@ -43,5 +43,11 @@ error: expected one of `:` or `|`, found `)` LL | fn i(n{...,f # | ^ expected one of `:` or `|` -error: aborting due to 6 previous errors +error: expected `;` or `{`, found `` + --> $DIR/issue-63135.rs:3:16 + | +LL | fn i(n{...,f # + | ^ expected `;` or `{` + +error: aborting due to 7 previous errors diff --git a/src/test/ui/parser/missing_right_paren.rs b/src/test/ui/parser/missing_right_paren.rs index c35236ce7934e..810dee9571d81 100644 --- a/src/test/ui/parser/missing_right_paren.rs +++ b/src/test/ui/parser/missing_right_paren.rs @@ -1,3 +1,3 @@ // ignore-tidy-trailing-newlines -// error-pattern: aborting due to 3 previous errors +// error-pattern: aborting due to 4 previous errors fn main((ؼ \ No newline at end of file diff --git a/src/test/ui/parser/missing_right_paren.stderr b/src/test/ui/parser/missing_right_paren.stderr index d67e7c88912a5..c1ceb81a07c47 100644 --- a/src/test/ui/parser/missing_right_paren.stderr +++ b/src/test/ui/parser/missing_right_paren.stderr @@ -22,5 +22,11 @@ error: expected one of `:` or `|`, found `)` LL | fn main((ؼ | ^ expected one of `:` or `|` -error: aborting due to 3 previous errors +error: expected `;` or `{`, found `` + --> $DIR/missing_right_paren.rs:3:11 + | +LL | fn main((ؼ + | ^ expected `;` or `{` + +error: aborting due to 4 previous errors diff --git a/src/test/ui/parser/no-const-fn-in-extern-block.rs b/src/test/ui/parser/no-const-fn-in-extern-block.rs index 29f26389ded18..4cae703a16395 100644 --- a/src/test/ui/parser/no-const-fn-in-extern-block.rs +++ b/src/test/ui/parser/no-const-fn-in-extern-block.rs @@ -1,8 +1,8 @@ extern { const fn foo(); - //~^ ERROR extern items cannot be `const` + //~^ ERROR functions in `extern` blocks cannot have qualifiers const unsafe fn bar(); - //~^ ERROR extern items cannot be `const` + //~^ ERROR functions in `extern` blocks cannot have qualifiers } fn main() {} diff --git a/src/test/ui/parser/no-const-fn-in-extern-block.stderr b/src/test/ui/parser/no-const-fn-in-extern-block.stderr index 5b4663a702f06..de653987e406d 100644 --- a/src/test/ui/parser/no-const-fn-in-extern-block.stderr +++ b/src/test/ui/parser/no-const-fn-in-extern-block.stderr @@ -1,14 +1,23 @@ -error: extern items cannot be `const` - --> $DIR/no-const-fn-in-extern-block.rs:2:5 +error: functions in `extern` blocks cannot have qualifiers + --> $DIR/no-const-fn-in-extern-block.rs:2:14 | +LL | extern { + | ------ in this `extern` block LL | const fn foo(); - | ^^^^^ + | ---------^^^ + | | + | help: remove the qualifiers: `fn` -error: extern items cannot be `const` - --> $DIR/no-const-fn-in-extern-block.rs:4:5 +error: functions in `extern` blocks cannot have qualifiers + --> $DIR/no-const-fn-in-extern-block.rs:4:21 | +LL | extern { + | ------ in this `extern` block +... LL | const unsafe fn bar(); - | ^^^^^ + | ----------------^^^ + | | + | help: remove the qualifiers: `fn` error: aborting due to 2 previous errors diff --git a/src/test/ui/parser/not-a-pred.stderr b/src/test/ui/parser/not-a-pred.stderr index 90246b92bf0fa..dce54655fa027 100644 --- a/src/test/ui/parser/not-a-pred.stderr +++ b/src/test/ui/parser/not-a-pred.stderr @@ -1,8 +1,8 @@ -error: expected one of `->`, `where`, or `{`, found `:` +error: expected `;` or `{`, found `:` --> $DIR/not-a-pred.rs:3:26 | LL | fn f(a: isize, b: isize) : lt(a, b) { } - | ^ expected one of `->`, `where`, or `{` + | ^ expected `;` or `{` error: aborting due to previous error From 67c29ed8fc8d2aa6e6b86cb02f4596c5d222b704 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Wed, 5 Feb 2020 02:04:14 +0100 Subject: [PATCH 3/4] lowering: add recursion_limit = 256 --- src/librustc_ast_lowering/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/librustc_ast_lowering/lib.rs b/src/librustc_ast_lowering/lib.rs index cd33bf6547be0..5816a64fca52c 100644 --- a/src/librustc_ast_lowering/lib.rs +++ b/src/librustc_ast_lowering/lib.rs @@ -32,6 +32,7 @@ #![feature(array_value_iter)] #![feature(crate_visibility_modifier)] +#![recursion_limit = "256"] use rustc::arena::Arena; use rustc::dep_graph::DepGraph; From 9a4eac3944e2e2668b4aea0b5afd3d83b88e992e Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Wed, 5 Feb 2020 12:27:45 +0100 Subject: [PATCH 4/4] ast_validation: fix visiting bug. --- src/librustc_ast_passes/ast_validation.rs | 49 +++++++++++-------- .../issue-68788-in-trait-item-propagation.rs | 21 ++++++++ 2 files changed, 49 insertions(+), 21 deletions(-) create mode 100644 src/test/ui/parser/issue-68788-in-trait-item-propagation.rs diff --git a/src/librustc_ast_passes/ast_validation.rs b/src/librustc_ast_passes/ast_validation.rs index 53911d147802d..79ed7f234f72e 100644 --- a/src/librustc_ast_passes/ast_validation.rs +++ b/src/librustc_ast_passes/ast_validation.rs @@ -81,6 +81,12 @@ struct AstValidator<'a> { } impl<'a> AstValidator<'a> { + fn with_in_trait_impl(&mut self, is_in: bool, f: impl FnOnce(&mut Self)) { + let old = mem::replace(&mut self.in_trait_impl, is_in); + f(self); + self.in_trait_impl = old; + } + fn with_banned_impl_trait(&mut self, f: impl FnOnce(&mut Self)) { let old = mem::replace(&mut self.is_impl_trait_banned, true); f(self); @@ -737,28 +743,29 @@ impl<'a> Visitor<'a> for AstValidator<'a> { ref self_ty, items: _, } => { - let old_in_trait_impl = mem::replace(&mut self.in_trait_impl, true); - - self.invalid_visibility(&item.vis, None); - if let TyKind::Err = self_ty.kind { - self.err_handler() - .struct_span_err(item.span, "`impl Trait for .. {}` is an obsolete syntax") - .help("use `auto trait Trait {}` instead") + self.with_in_trait_impl(true, |this| { + this.invalid_visibility(&item.vis, None); + if let TyKind::Err = self_ty.kind { + this.err_handler() + .struct_span_err( + item.span, + "`impl Trait for .. {}` is an obsolete syntax", + ) + .help("use `auto trait Trait {}` instead") + .emit(); + } + if unsafety == Unsafety::Unsafe && polarity == ImplPolarity::Negative { + struct_span_err!( + this.session, + item.span, + E0198, + "negative impls cannot be unsafe" + ) .emit(); - } - if unsafety == Unsafety::Unsafe && polarity == ImplPolarity::Negative { - struct_span_err!( - self.session, - item.span, - E0198, - "negative impls cannot be unsafe" - ) - .emit(); - } - - visit::walk_item(self, item); + } - self.in_trait_impl = old_in_trait_impl; + visit::walk_item(this, item); + }); return; // Avoid visiting again. } ItemKind::Impl { @@ -1142,7 +1149,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } } - visit::walk_assoc_item(self, item, ctxt); + self.with_in_trait_impl(false, |this| visit::walk_assoc_item(this, item, ctxt)); } } diff --git a/src/test/ui/parser/issue-68788-in-trait-item-propagation.rs b/src/test/ui/parser/issue-68788-in-trait-item-propagation.rs new file mode 100644 index 0000000000000..7c3dd1d5a98c7 --- /dev/null +++ b/src/test/ui/parser/issue-68788-in-trait-item-propagation.rs @@ -0,0 +1,21 @@ +// Make sure we don't propagate restrictions on trait impl items to items inside them. + +// check-pass +// edition:2018 + +fn main() {} + +trait X { + fn foo(); +} + +impl X for () { + fn foo() { + struct S; + impl S { + pub const X: u8 = 0; + pub const fn bar() {} + async fn qux() {} + } + } +}