From e331ae57df08e6eab346a9caab3fc354ce61c5d5 Mon Sep 17 00:00:00 2001 From: finalchild Date: Fri, 19 Aug 2022 02:43:01 +0900 Subject: [PATCH] Migrate forbidden_default and *_without_body --- .../rustc_ast_passes/src/ast_validation.rs | 128 ++++++++---------- compiler/rustc_ast_passes/src/errors.rs | 100 +++++++++++++- .../locales/en-US/ast_passes.ftl | 33 +++++ 3 files changed, 185 insertions(+), 76 deletions(-) diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index c8b46dde1c7..b337e5328c5 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -13,7 +13,7 @@ use rustc_ast::walk_list; use rustc_ast::*; use rustc_ast_pretty::pprust::{self, State}; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{error_code, fluent, pluralize, struct_span_err, Applicability, Diagnostic}; +use rustc_errors::{error_code, fluent, pluralize, struct_span_err, Applicability}; use rustc_parse::validate_attr; use rustc_session::lint::builtin::{ DEPRECATED_WHERE_CLAUSE_LOCATION, MISSING_ABI, PATTERNS_IN_FNS_WITHOUT_BODY, @@ -390,47 +390,20 @@ impl<'a> AstValidator<'a> { fn check_defaultness(&self, span: Span, defaultness: Defaultness) { if let Defaultness::Default(def_span) = defaultness { let span = self.session.source_map().guess_head_span(span); - self.err_handler() - .struct_span_err(span, "`default` is only allowed on items in trait impls") - .span_label(def_span, "`default` because of this") - .emit(); + self.session.emit_err(ForbiddenDefault { span, def_span }); } } - fn error_item_without_body(&self, sp: Span, ctx: &str, msg: &str, sugg: &str) { - self.error_item_without_body_with_help(sp, ctx, msg, sugg, |_| ()); - } - - fn error_item_without_body_with_help( - &self, - sp: Span, - ctx: &str, - msg: &str, - sugg: &str, - help: impl FnOnce(&mut Diagnostic), - ) { + /// If `sp` ends with a semicolon, returns it as a `Span` + /// Otherwise, returns `sp.shrink_to_hi()` + fn ending_semi_or_hi(&self, sp: Span) -> Span { let source_map = self.session.source_map(); let end = source_map.end_point(sp); - let replace_span = if source_map.span_to_snippet(end).map(|s| s == ";").unwrap_or(false) { + + if source_map.span_to_snippet(end).map(|s| s == ";").unwrap_or(false) { end } else { sp.shrink_to_hi() - }; - let mut err = self.err_handler().struct_span_err(sp, msg); - err.span_suggestion( - replace_span, - &format!("provide a definition for the {}", ctx), - sugg, - Applicability::HasPlaceholders, - ); - help(&mut err); - err.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); } } @@ -1123,37 +1096,23 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.check_defaultness(item.span, defaultness); if body.is_none() { - let msg = "free function without a body"; - let ext = sig.header.ext; - - let f = |e: &mut Diagnostic| { - if let Extern::Implicit(start_span) | Extern::Explicit(_, start_span) = &ext - { - let start_suggestion = if let Extern::Explicit(abi, _) = ext { - format!("extern \"{}\" {{", abi.symbol_unescaped) - } else { - "extern {".to_owned() - }; - - let end_suggestion = " }".to_owned(); - let end_span = item.span.shrink_to_hi(); - - e - .multipart_suggestion( - "if you meant to declare an externally defined function, use an `extern` block", - vec![(*start_span, start_suggestion), (end_span, end_suggestion)], - Applicability::MaybeIncorrect, - ); - } - }; - - self.error_item_without_body_with_help( - item.span, - "function", - msg, - " { }", - f, - ); + self.session.emit_err(FnWithoutBody { + span: item.span, + replace_span: self.ending_semi_or_hi(item.span), + extern_block_suggestion: match sig.header.ext { + Extern::None => None, + Extern::Implicit(start_span) => Some(ExternBlockSuggestion { + start_span, + end_span: item.span.shrink_to_hi(), + abi: None, + }), + Extern::Explicit(abi, start_span) => Some(ExternBlockSuggestion { + start_span, + end_span: item.span.shrink_to_hi(), + abi: Some(abi.symbol_unescaped), + }), + }, + }); } self.visit_vis(&item.vis); @@ -1259,12 +1218,16 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } ItemKind::Const(def, .., None) => { self.check_defaultness(item.span, def); - let msg = "free constant item without body"; - self.error_item_without_body(item.span, "constant", msg, " = ;"); + self.session.emit_err(ConstWithoutBody { + span: item.span, + replace_span: self.ending_semi_or_hi(item.span), + }); } ItemKind::Static(.., None) => { - let msg = "free static item without body"; - self.error_item_without_body(item.span, "static", msg, " = ;"); + self.session.emit_err(StaticWithoutBody { + span: item.span, + replace_span: self.ending_semi_or_hi(item.span), + }); } ItemKind::TyAlias(box TyAlias { defaultness, @@ -1275,8 +1238,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> { }) => { self.check_defaultness(item.span, defaultness); if ty.is_none() { - let msg = "free type alias without body"; - self.error_item_without_body(item.span, "type", msg, " = ;"); + self.session.emit_err(TyAliasWithoutBody { + span: item.span, + replace_span: self.ending_semi_or_hi(item.span), + }); } self.check_type_no_bounds(bounds, "this context"); if where_clauses.1.0 { @@ -1580,10 +1545,20 @@ impl<'a> Visitor<'a> for AstValidator<'a> { if ctxt == AssocCtxt::Impl { match &item.kind { AssocItemKind::Const(_, _, body) => { - self.check_impl_item_provided(item.span, body, "constant", " = ;"); + if body.is_none() { + self.session.emit_err(AssocConstWithoutBody { + span: item.span, + replace_span: self.ending_semi_or_hi(item.span), + }); + } } AssocItemKind::Fn(box Fn { body, .. }) => { - self.check_impl_item_provided(item.span, body, "function", " { }"); + if body.is_none() { + self.session.emit_err(AssocFnWithoutBody { + span: item.span, + replace_span: self.ending_semi_or_hi(item.span), + }); + } } AssocItemKind::TyAlias(box TyAlias { generics, @@ -1593,7 +1568,12 @@ impl<'a> Visitor<'a> for AstValidator<'a> { ty, .. }) => { - self.check_impl_item_provided(item.span, ty, "type", " = ;"); + if ty.is_none() { + self.session.emit_err(AssocTypeWithoutBody { + span: item.span, + replace_span: self.ending_semi_or_hi(item.span), + }); + } self.check_type_no_bounds(bounds, "`impl`s"); if ty.is_some() { self.check_gat_where( diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 0287d71feb3..1ac4dac10e2 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -1,7 +1,6 @@ //! Errors emitted by ast_passes. -use rustc_errors::fluent; -use rustc_errors::{AddSubdiagnostic, Diagnostic}; +use rustc_errors::{fluent, AddSubdiagnostic, Applicability, Diagnostic}; use rustc_macros::{SessionDiagnostic, SessionSubdiagnostic}; use rustc_span::{Span, Symbol}; @@ -150,3 +149,100 @@ pub struct FnParamForbiddenSelf { #[label] pub span: Span, } + +#[derive(SessionDiagnostic)] +#[error(ast_passes::forbidden_default)] +pub struct ForbiddenDefault { + #[primary_span] + pub span: Span, + #[label] + pub def_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(ast_passes::assoc_const_without_body)] +pub struct AssocConstWithoutBody { + #[primary_span] + pub span: Span, + #[suggestion(code = " = ;", applicability = "has-placeholders")] + pub replace_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(ast_passes::assoc_fn_without_body)] +pub struct AssocFnWithoutBody { + #[primary_span] + pub span: Span, + #[suggestion(code = " {{ }}", applicability = "has-placeholders")] + pub replace_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(ast_passes::assoc_type_without_body)] +pub struct AssocTypeWithoutBody { + #[primary_span] + pub span: Span, + #[suggestion(code = " = ;", applicability = "has-placeholders")] + pub replace_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(ast_passes::const_without_body)] +pub struct ConstWithoutBody { + #[primary_span] + pub span: Span, + #[suggestion(code = " = ;", applicability = "has-placeholders")] + pub replace_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(ast_passes::static_without_body)] +pub struct StaticWithoutBody { + #[primary_span] + pub span: Span, + #[suggestion(code = " = ;", applicability = "has-placeholders")] + pub replace_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(ast_passes::ty_alias_without_body)] +pub struct TyAliasWithoutBody { + #[primary_span] + pub span: Span, + #[suggestion(code = " = ;", applicability = "has-placeholders")] + pub replace_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(ast_passes::fn_without_body)] +pub struct FnWithoutBody { + #[primary_span] + pub span: Span, + #[suggestion(code = " {{ }}", applicability = "has-placeholders")] + pub replace_span: Span, + #[subdiagnostic] + pub extern_block_suggestion: Option, +} + +pub struct ExternBlockSuggestion { + pub start_span: Span, + pub end_span: Span, + pub abi: Option, +} + +impl AddSubdiagnostic for ExternBlockSuggestion { + fn add_to_diagnostic(self, diag: &mut Diagnostic) { + let start_suggestion = if let Some(abi) = self.abi { + format!("extern \"{}\" {{", abi) + } else { + "extern {".to_owned() + }; + let end_suggestion = " }".to_owned(); + + diag.multipart_suggestion( + fluent::ast_passes::extern_block_suggestion, + vec![(self.start_span, start_suggestion), (self.end_span, end_suggestion)], + Applicability::MaybeIncorrect, + ); + } +} diff --git a/compiler/rustc_error_messages/locales/en-US/ast_passes.ftl b/compiler/rustc_error_messages/locales/en-US/ast_passes.ftl index 77826f639f4..db91a886c72 100644 --- a/compiler/rustc_error_messages/locales/en-US/ast_passes.ftl +++ b/compiler/rustc_error_messages/locales/en-US/ast_passes.ftl @@ -58,3 +58,36 @@ ast_passes_fn_param_forbidden_self = `self` parameter is only allowed in associated functions .label = not semantically valid as function parameter .note = associated functions are those in `impl` or `trait` definitions + +ast_passes_forbidden_default = + `default` is only allowed on items in trait impls + .label = `default` because of this + +ast_passes_assoc_const_without_body = + associated constant in `impl` without body + .suggestion = provide a definition for the constant + +ast_passes_assoc_fn_without_body = + associated function in `impl` without body + .suggestion = provide a definition for the function + +ast_passes_assoc_type_without_body = + associated type in `impl` without body + .suggestion = provide a definition for the type + +ast_passes_const_without_body = + free constant item without body + .suggestion = provide a definition for the constant + +ast_passes_static_without_body = + free static item without body + .suggestion = provide a definition for the static + +ast_passes_ty_alias_without_body = + free type alias without body + .suggestion = provide a definition for the type + +ast_passes_fn_without_body = + free function without a body + .suggestion = provide a definition for the function + .extern_block_suggestion = if you meant to declare an externally defined function, use an `extern` block