Migrate forbidden_default and *_without_body

This commit is contained in:
finalchild 2022-08-19 02:43:01 +09:00
parent 8ed8aac3ca
commit e331ae57df
3 changed files with 185 additions and 76 deletions

View File

@ -13,7 +13,7 @@ use rustc_ast::walk_list;
use rustc_ast::*; use rustc_ast::*;
use rustc_ast_pretty::pprust::{self, State}; use rustc_ast_pretty::pprust::{self, State};
use rustc_data_structures::fx::FxHashMap; 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_parse::validate_attr;
use rustc_session::lint::builtin::{ use rustc_session::lint::builtin::{
DEPRECATED_WHERE_CLAUSE_LOCATION, MISSING_ABI, PATTERNS_IN_FNS_WITHOUT_BODY, 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) { fn check_defaultness(&self, span: Span, defaultness: Defaultness) {
if let Defaultness::Default(def_span) = defaultness { if let Defaultness::Default(def_span) = defaultness {
let span = self.session.source_map().guess_head_span(span); let span = self.session.source_map().guess_head_span(span);
self.err_handler() self.session.emit_err(ForbiddenDefault { span, def_span });
.struct_span_err(span, "`default` is only allowed on items in trait impls")
.span_label(def_span, "`default` because of this")
.emit();
} }
} }
fn error_item_without_body(&self, sp: Span, ctx: &str, msg: &str, sugg: &str) { /// If `sp` ends with a semicolon, returns it as a `Span`
self.error_item_without_body_with_help(sp, ctx, msg, sugg, |_| ()); /// Otherwise, returns `sp.shrink_to_hi()`
} fn ending_semi_or_hi(&self, sp: Span) -> Span {
fn error_item_without_body_with_help(
&self,
sp: Span,
ctx: &str,
msg: &str,
sugg: &str,
help: impl FnOnce(&mut Diagnostic),
) {
let source_map = self.session.source_map(); let source_map = self.session.source_map();
let end = source_map.end_point(sp); 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 end
} else { } else {
sp.shrink_to_hi() 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<T>(&self, sp: Span, body: &Option<T>, 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); self.check_defaultness(item.span, defaultness);
if body.is_none() { if body.is_none() {
let msg = "free function without a body"; self.session.emit_err(FnWithoutBody {
let ext = sig.header.ext; span: item.span,
replace_span: self.ending_semi_or_hi(item.span),
let f = |e: &mut Diagnostic| { extern_block_suggestion: match sig.header.ext {
if let Extern::Implicit(start_span) | Extern::Explicit(_, start_span) = &ext Extern::None => None,
{ Extern::Implicit(start_span) => Some(ExternBlockSuggestion {
let start_suggestion = if let Extern::Explicit(abi, _) = ext { start_span,
format!("extern \"{}\" {{", abi.symbol_unescaped) end_span: item.span.shrink_to_hi(),
} else { abi: None,
"extern {".to_owned() }),
}; Extern::Explicit(abi, start_span) => Some(ExternBlockSuggestion {
start_span,
let end_suggestion = " }".to_owned(); end_span: item.span.shrink_to_hi(),
let end_span = item.span.shrink_to_hi(); abi: Some(abi.symbol_unescaped),
}),
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,
" { <body> }",
f,
);
} }
self.visit_vis(&item.vis); self.visit_vis(&item.vis);
@ -1259,12 +1218,16 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
} }
ItemKind::Const(def, .., None) => { ItemKind::Const(def, .., None) => {
self.check_defaultness(item.span, def); self.check_defaultness(item.span, def);
let msg = "free constant item without body"; self.session.emit_err(ConstWithoutBody {
self.error_item_without_body(item.span, "constant", msg, " = <expr>;"); span: item.span,
replace_span: self.ending_semi_or_hi(item.span),
});
} }
ItemKind::Static(.., None) => { ItemKind::Static(.., None) => {
let msg = "free static item without body"; self.session.emit_err(StaticWithoutBody {
self.error_item_without_body(item.span, "static", msg, " = <expr>;"); span: item.span,
replace_span: self.ending_semi_or_hi(item.span),
});
} }
ItemKind::TyAlias(box TyAlias { ItemKind::TyAlias(box TyAlias {
defaultness, defaultness,
@ -1275,8 +1238,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
}) => { }) => {
self.check_defaultness(item.span, defaultness); self.check_defaultness(item.span, defaultness);
if ty.is_none() { if ty.is_none() {
let msg = "free type alias without body"; self.session.emit_err(TyAliasWithoutBody {
self.error_item_without_body(item.span, "type", msg, " = <type>;"); span: item.span,
replace_span: self.ending_semi_or_hi(item.span),
});
} }
self.check_type_no_bounds(bounds, "this context"); self.check_type_no_bounds(bounds, "this context");
if where_clauses.1.0 { if where_clauses.1.0 {
@ -1580,10 +1545,20 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
if ctxt == AssocCtxt::Impl { if ctxt == AssocCtxt::Impl {
match &item.kind { match &item.kind {
AssocItemKind::Const(_, _, body) => { AssocItemKind::Const(_, _, body) => {
self.check_impl_item_provided(item.span, body, "constant", " = <expr>;"); 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, .. }) => { AssocItemKind::Fn(box Fn { body, .. }) => {
self.check_impl_item_provided(item.span, body, "function", " { <body> }"); 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 { AssocItemKind::TyAlias(box TyAlias {
generics, generics,
@ -1593,7 +1568,12 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
ty, ty,
.. ..
}) => { }) => {
self.check_impl_item_provided(item.span, ty, "type", " = <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"); self.check_type_no_bounds(bounds, "`impl`s");
if ty.is_some() { if ty.is_some() {
self.check_gat_where( self.check_gat_where(

View File

@ -1,7 +1,6 @@
//! Errors emitted by ast_passes. //! Errors emitted by ast_passes.
use rustc_errors::fluent; use rustc_errors::{fluent, AddSubdiagnostic, Applicability, Diagnostic};
use rustc_errors::{AddSubdiagnostic, Diagnostic};
use rustc_macros::{SessionDiagnostic, SessionSubdiagnostic}; use rustc_macros::{SessionDiagnostic, SessionSubdiagnostic};
use rustc_span::{Span, Symbol}; use rustc_span::{Span, Symbol};
@ -150,3 +149,100 @@ pub struct FnParamForbiddenSelf {
#[label] #[label]
pub span: Span, 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 = " = <expr>;", 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 = " {{ <body> }}", 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 = " = <type>;", 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 = " = <expr>;", 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 = " = <expr>;", 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 = " = <type>;", 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 = " {{ <body> }}", applicability = "has-placeholders")]
pub replace_span: Span,
#[subdiagnostic]
pub extern_block_suggestion: Option<ExternBlockSuggestion>,
}
pub struct ExternBlockSuggestion {
pub start_span: Span,
pub end_span: Span,
pub abi: Option<Symbol>,
}
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,
);
}
}

View File

@ -58,3 +58,36 @@ ast_passes_fn_param_forbidden_self =
`self` parameter is only allowed in associated functions `self` parameter is only allowed in associated functions
.label = not semantically valid as function parameter .label = not semantically valid as function parameter
.note = associated functions are those in `impl` or `trait` definitions .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