summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_lint
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:19:13 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:19:13 +0000
commit218caa410aa38c29984be31a5229b9fa717560ee (patch)
treec54bd55eeb6e4c508940a30e94c0032fbd45d677 /compiler/rustc_lint
parentReleasing progress-linux version 1.67.1+dfsg1-1~progress7.99u1. (diff)
downloadrustc-218caa410aa38c29984be31a5229b9fa717560ee.tar.xz
rustc-218caa410aa38c29984be31a5229b9fa717560ee.zip
Merging upstream version 1.68.2+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--compiler/rustc_lint/src/array_into_iter.rs50
-rw-r--r--compiler/rustc_lint/src/builtin.rs920
-rw-r--r--compiler/rustc_lint/src/context.rs87
-rw-r--r--compiler/rustc_lint/src/deref_into_dyn_supertrait.rs30
-rw-r--r--compiler/rustc_lint/src/early.rs166
-rw-r--r--compiler/rustc_lint/src/enum_intrinsics_non_enums.rs17
-rw-r--r--compiler/rustc_lint/src/errors.rs5
-rw-r--r--compiler/rustc_lint/src/expect.rs42
-rw-r--r--compiler/rustc_lint/src/for_loops_over_fallibles.rs73
-rw-r--r--compiler/rustc_lint/src/hidden_unicode_codepoints.rs68
-rw-r--r--compiler/rustc_lint/src/internal.rs111
-rw-r--r--compiler/rustc_lint/src/late.rs89
-rw-r--r--compiler/rustc_lint/src/let_underscore.rs47
-rw-r--r--compiler/rustc_lint/src/levels.rs195
-rw-r--r--compiler/rustc_lint/src/lib.rs14
-rw-r--r--compiler/rustc_lint/src/lints.rs1478
-rw-r--r--compiler/rustc_lint/src/methods.rs12
-rw-r--r--compiler/rustc_lint/src/non_ascii_idents.rs56
-rw-r--r--compiler/rustc_lint/src/non_fmt_panic.rs49
-rw-r--r--compiler/rustc_lint/src/nonstandard_style.rs144
-rw-r--r--compiler/rustc_lint/src/noop_method_call.rs13
-rw-r--r--compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs6
-rw-r--r--compiler/rustc_lint/src/pass_by_value.rs19
-rw-r--r--compiler/rustc_lint/src/passes.rs119
-rw-r--r--compiler/rustc_lint/src/redundant_semicolon.rs15
-rw-r--r--compiler/rustc_lint/src/traits.rs25
-rw-r--r--compiler/rustc_lint/src/types.rs355
-rw-r--r--compiler/rustc_lint/src/unused.rs299
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs82
-rw-r--r--compiler/rustc_lint_defs/src/lib.rs20
30 files changed, 2887 insertions, 1719 deletions
diff --git a/compiler/rustc_lint/src/array_into_iter.rs b/compiler/rustc_lint/src/array_into_iter.rs
index abebc533c..3593f141d 100644
--- a/compiler/rustc_lint/src/array_into_iter.rs
+++ b/compiler/rustc_lint/src/array_into_iter.rs
@@ -1,5 +1,5 @@
+use crate::lints::{ArrayIntoIterDiag, ArrayIntoIterDiagSub};
use crate::{LateContext, LateLintPass, LintContext};
-use rustc_errors::{fluent, Applicability};
use rustc_hir as hir;
use rustc_middle::ty;
use rustc_middle::ty::adjustment::{Adjust, Adjustment};
@@ -118,41 +118,23 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter {
// to an array or to a slice.
_ => bug!("array type coerced to something other than array or slice"),
};
- cx.struct_span_lint(
+ let sub = if self.for_expr_span == expr.span {
+ Some(ArrayIntoIterDiagSub::RemoveIntoIter {
+ span: receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()),
+ })
+ } else if receiver_ty.is_array() {
+ Some(ArrayIntoIterDiagSub::UseExplicitIntoIter {
+ start_span: expr.span.shrink_to_lo(),
+ end_span: receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()),
+ })
+ } else {
+ None
+ };
+ cx.emit_spanned_lint(
ARRAY_INTO_ITER,
call.ident.span,
- fluent::lint_array_into_iter,
- |diag| {
- diag.set_arg("target", target);
- diag.span_suggestion(
- call.ident.span,
- fluent::use_iter_suggestion,
- "iter",
- Applicability::MachineApplicable,
- );
- if self.for_expr_span == expr.span {
- diag.span_suggestion(
- receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()),
- fluent::remove_into_iter_suggestion,
- "",
- Applicability::MaybeIncorrect,
- );
- } else if receiver_ty.is_array() {
- diag.multipart_suggestion(
- fluent::use_explicit_into_iter_suggestion,
- vec![
- (expr.span.shrink_to_lo(), "IntoIterator::into_iter(".into()),
- (
- receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()),
- ")".into(),
- ),
- ],
- Applicability::MaybeIncorrect,
- );
- }
- diag
- },
- )
+ ArrayIntoIterDiag { target, suggestion: call.ident.span, sub },
+ );
}
}
}
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index c6c7caa7c..fe188162c 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -22,6 +22,23 @@
use crate::{
errors::BuiltinEllpisisInclusiveRangePatterns,
+ lints::{
+ BuiltinAnonymousParams, BuiltinBoxPointers, BuiltinClashingExtern,
+ BuiltinClashingExternSub, BuiltinConstNoMangle, BuiltinDeprecatedAttrLink,
+ BuiltinDeprecatedAttrLinkSuggestion, BuiltinDeprecatedAttrUsed, BuiltinDerefNullptr,
+ BuiltinEllipsisInclusiveRangePatternsLint, BuiltinExplicitOutlives,
+ BuiltinExplicitOutlivesSuggestion, BuiltinIncompleteFeatures,
+ BuiltinIncompleteFeaturesHelp, BuiltinIncompleteFeaturesNote, BuiltinKeywordIdents,
+ BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc,
+ BuiltinMutablesTransmutes, BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns,
+ BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasGenericBounds,
+ BuiltinTypeAliasGenericBoundsSuggestion, BuiltinTypeAliasWhereClause,
+ BuiltinUnexpectedCliConfigName, BuiltinUnexpectedCliConfigValue,
+ BuiltinUngatedAsyncFnTrackCaller, BuiltinUnnameableTestItems, BuiltinUnpermittedTypeInit,
+ BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, BuiltinUnsafe,
+ BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub,
+ BuiltinWhileTrue, SuggestChangingAssocTypes,
+ },
types::{transparent_newtype_field, CItemKind},
EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext,
};
@@ -33,10 +50,7 @@ use rustc_ast::{self as ast, *};
use rustc_ast_pretty::pprust::{self, expr_to_string};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::stack::ensure_sufficient_stack;
-use rustc_errors::{
- fluent, Applicability, DelayDm, Diagnostic, DiagnosticBuilder, DiagnosticMessage,
- DiagnosticStyledString, MultiSpan,
-};
+use rustc_errors::{fluent, Applicability, DecorateLint, MultiSpan};
use rustc_feature::{deprecated_attributes, AttributeGate, BuiltinAttribute, GateIssue, Stability};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
@@ -57,7 +71,8 @@ use rustc_span::source_map::Spanned;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{BytePos, InnerSpan, Span};
use rustc_target::abi::{Abi, VariantIdx};
-use rustc_trait_selection::traits::{self, misc::can_type_implement_copy};
+use rustc_trait_selection::infer::{InferCtxtExt, TyCtxtInferExt};
+use rustc_trait_selection::traits::{self, misc::type_allowed_to_implement_copy};
use crate::nonstandard_style::{method_context, MethodLateContext};
@@ -100,6 +115,7 @@ fn pierce_parens(mut expr: &ast::Expr) -> &ast::Expr {
}
impl EarlyLintPass for WhileTrue {
+ #[inline]
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
if let ast::ExprKind::While(cond, _, label) = &e.kind
&& let cond = pierce_parens(cond)
@@ -108,25 +124,17 @@ impl EarlyLintPass for WhileTrue {
&& !cond.span.from_expansion()
{
let condition_span = e.span.with_hi(cond.span.hi());
- cx.struct_span_lint(
- WHILE_TRUE,
- condition_span,
- fluent::lint_builtin_while_true,
- |lint| {
- lint.span_suggestion_short(
- condition_span,
- fluent::suggestion,
- format!(
+ let replace = format!(
"{}loop",
label.map_or_else(String::new, |label| format!(
"{}: ",
label.ident,
))
- ),
- Applicability::MachineApplicable,
- )
- },
- )
+ );
+ cx.emit_spanned_lint(WHILE_TRUE, condition_span, BuiltinWhileTrue {
+ suggestion: condition_span,
+ replace,
+ });
}
}
}
@@ -162,12 +170,7 @@ impl BoxPointers {
for leaf in ty.walk() {
if let GenericArgKind::Type(leaf_ty) = leaf.unpack() {
if leaf_ty.is_box() {
- cx.struct_span_lint(
- BOX_POINTERS,
- span,
- fluent::lint_builtin_box_pointers,
- |lint| lint.set_arg("ty", ty),
- );
+ cx.emit_spanned_lint(BOX_POINTERS, span, BuiltinBoxPointers { ty });
}
}
}
@@ -265,19 +268,13 @@ impl<'tcx> LateLintPass<'tcx> for NonShorthandFieldPatterns {
if cx.tcx.find_field_index(ident, &variant)
== Some(cx.typeck_results().field_index(fieldpat.hir_id))
{
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
NON_SHORTHAND_FIELD_PATTERNS,
fieldpat.span,
- fluent::lint_builtin_non_shorthand_field_patterns,
- |lint| {
- let suggested_ident =
- format!("{}{}", binding_annot.prefix_str(), ident);
- lint.set_arg("ident", ident.clone()).span_suggestion(
- fieldpat.span,
- fluent::suggestion,
- suggested_ident,
- Applicability::MachineApplicable,
- )
+ BuiltinNonShorthandFieldPatterns {
+ ident,
+ suggestion: fieldpat.span,
+ prefix: binding_annot.prefix_str(),
},
);
}
@@ -319,56 +316,30 @@ impl UnsafeCode {
&self,
cx: &EarlyContext<'_>,
span: Span,
- msg: impl Into<DiagnosticMessage>,
- decorate: impl for<'a, 'b> FnOnce(
- &'b mut DiagnosticBuilder<'a, ()>,
- ) -> &'b mut DiagnosticBuilder<'a, ()>,
+ decorate: impl for<'a> DecorateLint<'a, ()>,
) {
// This comes from a macro that has `#[allow_internal_unsafe]`.
if span.allows_unsafe() {
return;
}
- cx.struct_span_lint(UNSAFE_CODE, span, msg, decorate);
- }
-
- fn report_overridden_symbol_name(
- &self,
- cx: &EarlyContext<'_>,
- span: Span,
- msg: DiagnosticMessage,
- ) {
- self.report_unsafe(cx, span, msg, |lint| {
- lint.note(fluent::lint_builtin_overridden_symbol_name)
- })
- }
-
- fn report_overridden_symbol_section(
- &self,
- cx: &EarlyContext<'_>,
- span: Span,
- msg: DiagnosticMessage,
- ) {
- self.report_unsafe(cx, span, msg, |lint| {
- lint.note(fluent::lint_builtin_overridden_symbol_section)
- })
+ cx.emit_spanned_lint(UNSAFE_CODE, span, decorate);
}
}
impl EarlyLintPass for UnsafeCode {
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) {
if attr.has_name(sym::allow_internal_unsafe) {
- self.report_unsafe(cx, attr.span, fluent::lint_builtin_allow_internal_unsafe, |lint| {
- lint
- });
+ self.report_unsafe(cx, attr.span, BuiltinUnsafe::AllowInternalUnsafe);
}
}
+ #[inline]
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
if let ast::ExprKind::Block(ref blk, _) = e.kind {
// Don't warn about generated blocks; that'll just pollute the output.
if blk.rules == ast::BlockCheckMode::Unsafe(ast::UserProvided) {
- self.report_unsafe(cx, blk.span, fluent::lint_builtin_unsafe_block, |lint| lint);
+ self.report_unsafe(cx, blk.span, BuiltinUnsafe::UnsafeBlock);
}
}
}
@@ -376,62 +347,38 @@ impl EarlyLintPass for UnsafeCode {
fn check_item(&mut self, cx: &EarlyContext<'_>, it: &ast::Item) {
match it.kind {
ast::ItemKind::Trait(box ast::Trait { unsafety: ast::Unsafe::Yes(_), .. }) => {
- self.report_unsafe(cx, it.span, fluent::lint_builtin_unsafe_trait, |lint| lint)
+ self.report_unsafe(cx, it.span, BuiltinUnsafe::UnsafeTrait);
}
ast::ItemKind::Impl(box ast::Impl { unsafety: ast::Unsafe::Yes(_), .. }) => {
- self.report_unsafe(cx, it.span, fluent::lint_builtin_unsafe_impl, |lint| lint)
+ self.report_unsafe(cx, it.span, BuiltinUnsafe::UnsafeImpl);
}
ast::ItemKind::Fn(..) => {
if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::no_mangle) {
- self.report_overridden_symbol_name(
- cx,
- attr.span,
- fluent::lint_builtin_no_mangle_fn,
- );
+ self.report_unsafe(cx, attr.span, BuiltinUnsafe::NoMangleFn);
}
if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::export_name) {
- self.report_overridden_symbol_name(
- cx,
- attr.span,
- fluent::lint_builtin_export_name_fn,
- );
+ self.report_unsafe(cx, attr.span, BuiltinUnsafe::ExportNameFn);
}
if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::link_section) {
- self.report_overridden_symbol_section(
- cx,
- attr.span,
- fluent::lint_builtin_link_section_fn,
- );
+ self.report_unsafe(cx, attr.span, BuiltinUnsafe::LinkSectionFn);
}
}
ast::ItemKind::Static(..) => {
if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::no_mangle) {
- self.report_overridden_symbol_name(
- cx,
- attr.span,
- fluent::lint_builtin_no_mangle_static,
- );
+ self.report_unsafe(cx, attr.span, BuiltinUnsafe::NoMangleStatic);
}
if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::export_name) {
- self.report_overridden_symbol_name(
- cx,
- attr.span,
- fluent::lint_builtin_export_name_static,
- );
+ self.report_unsafe(cx, attr.span, BuiltinUnsafe::ExportNameStatic);
}
if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::link_section) {
- self.report_overridden_symbol_section(
- cx,
- attr.span,
- fluent::lint_builtin_link_section_static,
- );
+ self.report_unsafe(cx, attr.span, BuiltinUnsafe::LinkSectionStatic);
}
}
@@ -442,18 +389,10 @@ impl EarlyLintPass for UnsafeCode {
fn check_impl_item(&mut self, cx: &EarlyContext<'_>, it: &ast::AssocItem) {
if let ast::AssocItemKind::Fn(..) = it.kind {
if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::no_mangle) {
- self.report_overridden_symbol_name(
- cx,
- attr.span,
- fluent::lint_builtin_no_mangle_method,
- );
+ self.report_unsafe(cx, attr.span, BuiltinUnsafe::NoMangleMethod);
}
if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::export_name) {
- self.report_overridden_symbol_name(
- cx,
- attr.span,
- fluent::lint_builtin_export_name_method,
- );
+ self.report_unsafe(cx, attr.span, BuiltinUnsafe::ExportNameMethod);
}
}
}
@@ -468,13 +407,13 @@ impl EarlyLintPass for UnsafeCode {
body,
) = fk
{
- let msg = match ctxt {
+ let decorator = match ctxt {
FnCtxt::Foreign => return,
- FnCtxt::Free => fluent::lint_builtin_decl_unsafe_fn,
- FnCtxt::Assoc(_) if body.is_none() => fluent::lint_builtin_decl_unsafe_method,
- FnCtxt::Assoc(_) => fluent::lint_builtin_impl_unsafe_method,
+ FnCtxt::Free => BuiltinUnsafe::DeclUnsafeFn,
+ FnCtxt::Assoc(_) if body.is_none() => BuiltinUnsafe::DeclUnsafeMethod,
+ FnCtxt::Assoc(_) => BuiltinUnsafe::ImplUnsafeMethod,
};
- self.report_unsafe(cx, span, msg, |lint| lint);
+ self.report_unsafe(cx, span, decorator);
}
}
}
@@ -575,17 +514,17 @@ impl MissingDoc {
let attrs = cx.tcx.hir().attrs(cx.tcx.hir().local_def_id_to_hir_id(def_id));
let has_doc = attrs.iter().any(has_doc);
if !has_doc {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
MISSING_DOCS,
cx.tcx.def_span(def_id),
- fluent::lint_builtin_missing_doc,
- |lint| lint.set_arg("article", article).set_arg("desc", desc),
+ BuiltinMissingDoc { article, desc },
);
}
}
}
impl<'tcx> LateLintPass<'tcx> for MissingDoc {
+ #[inline]
fn enter_lint_attrs(&mut self, _cx: &LateContext<'_>, attrs: &[ast::Attribute]) {
let doc_hidden = self.doc_hidden()
|| attrs.iter().any(|attr| {
@@ -754,11 +693,42 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations {
if def.has_dtor(cx.tcx) {
return;
}
+
+ // If the type contains a raw pointer, it may represent something like a handle,
+ // and recommending Copy might be a bad idea.
+ for field in def.all_fields() {
+ let did = field.did;
+ if cx.tcx.type_of(did).is_unsafe_ptr() {
+ return;
+ }
+ }
let param_env = ty::ParamEnv::empty();
if ty.is_copy_modulo_regions(cx.tcx, param_env) {
return;
}
- if can_type_implement_copy(
+
+ // We shouldn't recommend implementing `Copy` on stateful things,
+ // such as iterators.
+ if let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator)
+ && cx.tcx
+ .infer_ctxt()
+ .build()
+ .type_implements_trait(iter_trait, [ty], param_env)
+ .must_apply_modulo_regions()
+ {
+ return;
+ }
+
+ // Default value of clippy::trivially_copy_pass_by_ref
+ const MAX_SIZE: u64 = 256;
+
+ if let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes()) {
+ if size > MAX_SIZE {
+ return;
+ }
+ }
+
+ if type_allowed_to_implement_copy(
cx.tcx,
param_env,
ty,
@@ -766,12 +736,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations {
)
.is_ok()
{
- cx.struct_span_lint(
- MISSING_COPY_IMPLEMENTATIONS,
- item.span,
- fluent::lint_builtin_missing_copy_impl,
- |lint| lint,
- )
+ cx.emit_spanned_lint(MISSING_COPY_IMPLEMENTATIONS, item.span, BuiltinMissingCopyImpl);
}
}
}
@@ -845,11 +810,10 @@ impl<'tcx> LateLintPass<'tcx> for MissingDebugImplementations {
}
if !self.impling_types.as_ref().unwrap().contains(&item.owner_id.def_id) {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
MISSING_DEBUG_IMPLEMENTATIONS,
item.span,
- fluent::lint_builtin_missing_debug_impl,
- |lint| lint.set_arg("debug", cx.tcx.def_path_str(debug)),
+ BuiltinMissingDebugImpl { tcx: cx.tcx, def_id: debug },
);
}
}
@@ -925,19 +889,11 @@ impl EarlyLintPass for AnonymousParameters {
} else {
("<type>", Applicability::HasPlaceholders)
};
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
ANONYMOUS_PARAMETERS,
arg.pat.span,
- fluent::lint_builtin_anonymous_params,
- |lint| {
- lint.span_suggestion(
- arg.pat.span,
- fluent::suggestion,
- format!("_: {}", ty_snip),
- appl,
- )
- },
- )
+ BuiltinAnonymousParams { suggestion: (arg.pat.span, appl), ty_snip },
+ );
}
}
}
@@ -972,42 +928,30 @@ impl EarlyLintPass for DeprecatedAttr {
_,
) = gate
{
- // FIXME(davidtwco) translatable deprecated attr
- cx.struct_span_lint(
+ let suggestion = match suggestion {
+ Some(msg) => {
+ BuiltinDeprecatedAttrLinkSuggestion::Msg { suggestion: attr.span, msg }
+ }
+ None => {
+ BuiltinDeprecatedAttrLinkSuggestion::Default { suggestion: attr.span }
+ }
+ };
+ cx.emit_spanned_lint(
DEPRECATED,
attr.span,
- fluent::lint_builtin_deprecated_attr_link,
- |lint| {
- lint.set_arg("name", name)
- .set_arg("reason", reason)
- .set_arg("link", link)
- .span_suggestion_short(
- attr.span,
- suggestion.map(|s| s.into()).unwrap_or(
- fluent::lint_builtin_deprecated_attr_default_suggestion,
- ),
- "",
- Applicability::MachineApplicable,
- )
- },
+ BuiltinDeprecatedAttrLink { name, reason, link, suggestion },
);
}
return;
}
}
if attr.has_name(sym::no_start) || attr.has_name(sym::crate_id) {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
DEPRECATED,
attr.span,
- fluent::lint_builtin_deprecated_attr_used,
- |lint| {
- lint.set_arg("name", pprust::path_to_string(&attr.get_normal_item().path))
- .span_suggestion_short(
- attr.span,
- fluent::lint_builtin_deprecated_attr_default_suggestion,
- "",
- Applicability::MachineApplicable,
- )
+ BuiltinDeprecatedAttrUsed {
+ name: pprust::path_to_string(&attr.get_normal_item().path),
+ suggestion: attr.span,
},
);
}
@@ -1036,20 +980,18 @@ fn warn_if_doc(cx: &EarlyContext<'_>, node_span: Span, node_kind: &str, attrs: &
let span = sugared_span.take().unwrap_or(attr.span);
if is_doc_comment || attr.has_name(sym::doc) {
- cx.struct_span_lint(
+ let sub = match attr.kind {
+ AttrKind::DocComment(CommentKind::Line, _) | AttrKind::Normal(..) => {
+ BuiltinUnusedDocCommentSub::PlainHelp
+ }
+ AttrKind::DocComment(CommentKind::Block, _) => {
+ BuiltinUnusedDocCommentSub::BlockHelp
+ }
+ };
+ cx.emit_spanned_lint(
UNUSED_DOC_COMMENTS,
span,
- fluent::lint_builtin_unused_doc_comment,
- |lint| {
- lint.set_arg("kind", node_kind).span_label(node_span, fluent::label).help(
- match attr.kind {
- AttrKind::DocComment(CommentKind::Line, _) | AttrKind::Normal(..) => {
- fluent::plain_help
- }
- AttrKind::DocComment(CommentKind::Block, _) => fluent::block_help,
- },
- )
- },
+ BuiltinUnusedDocComment { kind: node_kind, label: node_span, sub },
);
}
}
@@ -1164,20 +1106,10 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
match param.kind {
GenericParamKind::Lifetime { .. } => {}
GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
NO_MANGLE_GENERIC_ITEMS,
span,
- fluent::lint_builtin_no_mangle_generic,
- |lint| {
- lint.span_suggestion_short(
- no_mangle_attr.span,
- fluent::suggestion,
- "",
- // Use of `#[no_mangle]` suggests FFI intent; correct
- // fix may be to monomorphize source by hand
- Applicability::MaybeIncorrect,
- )
- },
+ BuiltinNoMangleGeneric { suggestion: no_mangle_attr.span },
);
break;
}
@@ -1192,30 +1124,23 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
}
hir::ItemKind::Const(..) => {
if cx.sess().contains_name(attrs, sym::no_mangle) {
+ // account for "pub const" (#45562)
+ let start = cx
+ .tcx
+ .sess
+ .source_map()
+ .span_to_snippet(it.span)
+ .map(|snippet| snippet.find("const").unwrap_or(0))
+ .unwrap_or(0) as u32;
+ // `const` is 5 chars
+ let suggestion = it.span.with_hi(BytePos(it.span.lo().0 + start + 5));
+
// Const items do not refer to a particular location in memory, and therefore
// don't have anything to attach a symbol to
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
NO_MANGLE_CONST_ITEMS,
it.span,
- fluent::lint_builtin_const_no_mangle,
- |lint| {
- // account for "pub const" (#45562)
- let start = cx
- .tcx
- .sess
- .source_map()
- .span_to_snippet(it.span)
- .map(|snippet| snippet.find("const").unwrap_or(0))
- .unwrap_or(0) as u32;
- // `const` is 5 chars
- let const_span = it.span.with_hi(BytePos(it.span.lo().0 + start + 5));
- lint.span_suggestion(
- const_span,
- fluent::suggestion,
- "pub static",
- Applicability::MachineApplicable,
- )
- },
+ BuiltinConstNoMangle { suggestion },
);
}
}
@@ -1276,12 +1201,7 @@ impl<'tcx> LateLintPass<'tcx> for MutableTransmutes {
get_transmute_from_to(cx, expr).map(|(ty1, ty2)| (ty1.kind(), ty2.kind()))
{
if from_mutbl < to_mutbl {
- cx.struct_span_lint(
- MUTABLE_TRANSMUTES,
- expr.span,
- fluent::lint_builtin_mutable_transmutes,
- |lint| lint,
- );
+ cx.emit_spanned_lint(MUTABLE_TRANSMUTES, expr.span, BuiltinMutablesTransmutes);
}
}
@@ -1329,12 +1249,7 @@ impl<'tcx> LateLintPass<'tcx> for UnstableFeatures {
if attr.has_name(sym::feature) {
if let Some(items) = attr.meta_item_list() {
for item in items {
- cx.struct_span_lint(
- UNSTABLE_FEATURES,
- item.span(),
- fluent::lint_builtin_unstable_features,
- |lint| lint,
- );
+ cx.emit_spanned_lint(UNSTABLE_FEATURES, item.span(), BuiltinUnstableFeatures);
}
}
}
@@ -1359,7 +1274,7 @@ declare_lint! {
///
/// The attribute must be used in conjunction with the
/// [`closure_track_caller` feature flag]. Otherwise, the `#[track_caller]`
- /// annotation will function as as no-op.
+ /// annotation will function as a no-op.
///
/// [`closure_track_caller` feature flag]: https://doc.rust-lang.org/beta/unstable-book/language-features/closure-track-caller.html
UNGATED_ASYNC_FN_TRACK_CALLER,
@@ -1389,20 +1304,10 @@ impl<'tcx> LateLintPass<'tcx> for UngatedAsyncFnTrackCaller {
// Now, check if the function has the `#[track_caller]` attribute
&& let Some(attr) = attrs.iter().find(|attr| attr.has_name(sym::track_caller))
{
- cx.struct_span_lint(
- UNGATED_ASYNC_FN_TRACK_CALLER,
- attr.span,
- fluent::lint_ungated_async_fn_track_caller,
- |lint| {
- lint.span_label(span, fluent::label);
- rustc_session::parse::add_feature_diagnostics(
- lint,
- &cx.tcx.sess.parse_sess,
- sym::closure_track_caller,
- );
- lint
- },
- );
+ cx.emit_spanned_lint(UNGATED_ASYNC_FN_TRACK_CALLER, attr.span, BuiltinUngatedAsyncFnTrackCaller {
+ label: span,
+ parse_sess: &cx.tcx.sess.parse_sess,
+ });
}
}
}
@@ -1460,18 +1365,13 @@ impl UnreachablePub {
applicability = Applicability::MaybeIncorrect;
}
let def_span = cx.tcx.def_span(def_id);
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
UNREACHABLE_PUB,
def_span,
- fluent::lint_builtin_unreachable_pub,
- |lint| {
- lint.set_arg("what", what);
-
- lint.span_suggestion(vis_span, fluent::suggestion, "pub(crate)", applicability);
- if exportable {
- lint.help(fluent::help);
- }
- lint
+ BuiltinUnreachablePub {
+ what,
+ suggestion: (vis_span, applicability),
+ help: exportable.then_some(()),
},
);
}
@@ -1493,7 +1393,7 @@ impl<'tcx> LateLintPass<'tcx> for UnreachablePub {
fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) {
let map = cx.tcx.hir();
- if matches!(map.get(map.get_parent_node(field.hir_id)), Node::Variant(_)) {
+ if matches!(map.get_parent(field.hir_id), Node::Variant(_)) {
return;
}
self.perform_lint(cx, "field", field.def_id, field.vis_span, false);
@@ -1536,7 +1436,7 @@ declare_lint_pass!(
);
impl TypeAliasBounds {
- fn is_type_variable_assoc(qpath: &hir::QPath<'_>) -> bool {
+ pub(crate) fn is_type_variable_assoc(qpath: &hir::QPath<'_>) -> bool {
match *qpath {
hir::QPath::TypeRelative(ref ty, _) => {
// If this is a type variable, we found a `T::Assoc`.
@@ -1550,29 +1450,6 @@ impl TypeAliasBounds {
hir::QPath::Resolved(..) | hir::QPath::LangItem(..) => false,
}
}
-
- fn suggest_changing_assoc_types(ty: &hir::Ty<'_>, err: &mut Diagnostic) {
- // Access to associates types should use `<T as Bound>::Assoc`, which does not need a
- // bound. Let's see if this type does that.
-
- // We use a HIR visitor to walk the type.
- use rustc_hir::intravisit::{self, Visitor};
- struct WalkAssocTypes<'a> {
- err: &'a mut Diagnostic,
- }
- impl Visitor<'_> for WalkAssocTypes<'_> {
- fn visit_qpath(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, span: Span) {
- if TypeAliasBounds::is_type_variable_assoc(qpath) {
- self.err.span_help(span, fluent::lint_builtin_type_alias_bounds_help);
- }
- intravisit::walk_qpath(self, qpath, id)
- }
- }
-
- // Let's go for a walk!
- let mut visitor = WalkAssocTypes { err };
- visitor.visit_ty(ty);
- }
}
impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
@@ -1606,35 +1483,31 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
let mut suggested_changing_assoc_types = false;
if !where_spans.is_empty() {
- cx.lint(TYPE_ALIAS_BOUNDS, fluent::lint_builtin_type_alias_where_clause, |lint| {
- lint.set_span(where_spans);
- lint.span_suggestion(
- type_alias_generics.where_clause_span,
- fluent::suggestion,
- "",
- Applicability::MachineApplicable,
- );
- if !suggested_changing_assoc_types {
- TypeAliasBounds::suggest_changing_assoc_types(ty, lint);
- suggested_changing_assoc_types = true;
- }
- lint
+ let sub = (!suggested_changing_assoc_types).then(|| {
+ suggested_changing_assoc_types = true;
+ SuggestChangingAssocTypes { ty }
});
+ cx.emit_spanned_lint(
+ TYPE_ALIAS_BOUNDS,
+ where_spans,
+ BuiltinTypeAliasWhereClause {
+ suggestion: type_alias_generics.where_clause_span,
+ sub,
+ },
+ );
}
if !inline_spans.is_empty() {
- cx.lint(TYPE_ALIAS_BOUNDS, fluent::lint_builtin_type_alias_generic_bounds, |lint| {
- lint.set_span(inline_spans);
- lint.multipart_suggestion(
- fluent::suggestion,
- inline_sugg,
- Applicability::MachineApplicable,
- );
- if !suggested_changing_assoc_types {
- TypeAliasBounds::suggest_changing_assoc_types(ty, lint);
- }
- lint
+ let suggestion = BuiltinTypeAliasGenericBoundsSuggestion { suggestions: inline_sugg };
+ let sub = (!suggested_changing_assoc_types).then(|| {
+ suggested_changing_assoc_types = true;
+ SuggestChangingAssocTypes { ty }
});
+ cx.emit_spanned_lint(
+ TYPE_ALIAS_BOUNDS,
+ inline_spans,
+ BuiltinTypeAliasGenericBounds { suggestion, sub },
+ );
}
}
}
@@ -1734,14 +1607,10 @@ impl<'tcx> LateLintPass<'tcx> for TrivialConstraints {
TypeWellFormedFromEnv(..) => continue,
};
if predicate.is_global() {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
TRIVIAL_BOUNDS,
span,
- fluent::lint_builtin_trivial_bounds,
- |lint| {
- lint.set_arg("predicate_kind_name", predicate_kind_name)
- .set_arg("predicate", predicate)
- },
+ BuiltinTrivialBounds { predicate_kind_name, predicate },
);
}
}
@@ -1842,8 +1711,6 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns {
};
if let Some((start, end, join)) = endpoints {
- let msg = fluent::lint_builtin_ellipsis_inclusive_range_patterns;
- let suggestion = fluent::suggestion;
if parenthesise {
self.node_id = Some(pat.id);
let end = expr_to_string(&end);
@@ -1858,14 +1725,14 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns {
replace,
});
} else {
- cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, pat.span, msg, |lint| {
- lint.span_suggestion(
- pat.span,
- suggestion,
+ cx.emit_spanned_lint(
+ ELLIPSIS_INCLUSIVE_RANGE_PATTERNS,
+ pat.span,
+ BuiltinEllipsisInclusiveRangePatternsLint::Parenthesise {
+ suggestion: pat.span,
replace,
- Applicability::MachineApplicable,
- )
- });
+ },
+ );
}
} else {
let replace = "..=";
@@ -1876,14 +1743,13 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns {
replace: replace.to_string(),
});
} else {
- cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, join, msg, |lint| {
- lint.span_suggestion_short(
- join,
- suggestion,
- replace,
- Applicability::MachineApplicable,
- )
- });
+ cx.emit_spanned_lint(
+ ELLIPSIS_INCLUSIVE_RANGE_PATTERNS,
+ join,
+ BuiltinEllipsisInclusiveRangePatternsLint::NonParenthesise {
+ suggestion: join,
+ },
+ );
}
};
}
@@ -1963,12 +1829,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnameableTestItems {
let attrs = cx.tcx.hir().attrs(it.hir_id());
if let Some(attr) = cx.sess().find_by_name(attrs, sym::rustc_test_marker) {
- cx.struct_span_lint(
- UNNAMEABLE_TEST_ITEMS,
- attr.span,
- fluent::lint_builtin_unnameable_test_items,
- |lint| lint,
- );
+ cx.emit_spanned_lint(UNNAMEABLE_TEST_ITEMS, attr.span, BuiltinUnnameableTestItems);
}
}
@@ -2084,18 +1945,10 @@ impl KeywordIdents {
return;
}
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
KEYWORD_IDENTS,
ident.span,
- fluent::lint_builtin_keyword_idents,
- |lint| {
- lint.set_arg("kw", ident.clone()).set_arg("next", next_edition).span_suggestion(
- ident.span,
- fluent::suggestion,
- format!("r#{}", ident),
- Applicability::MachineApplicable,
- )
- },
+ BuiltinKeywordIdents { kw: ident, next: next_edition, suggestion: ident.span },
);
}
}
@@ -2151,6 +2004,7 @@ impl ExplicitOutlivesRequirements {
tcx: TyCtxt<'tcx>,
bounds: &hir::GenericBounds<'_>,
inferred_outlives: &[ty::Region<'tcx>],
+ predicate_span: Span,
) -> Vec<(usize, Span)> {
use rustc_middle::middle::resolve_lifetime::Region;
@@ -2158,23 +2012,28 @@ impl ExplicitOutlivesRequirements {
.iter()
.enumerate()
.filter_map(|(i, bound)| {
- if let hir::GenericBound::Outlives(lifetime) = bound {
- let is_inferred = match tcx.named_region(lifetime.hir_id) {
- Some(Region::EarlyBound(def_id)) => inferred_outlives.iter().any(|r| {
- if let ty::ReEarlyBound(ebr) = **r {
- ebr.def_id == def_id
- } else {
- false
- }
- }),
- _ => false,
- };
- is_inferred.then_some((i, bound.span()))
- } else {
- None
+ let hir::GenericBound::Outlives(lifetime) = bound else {
+ return None;
+ };
+
+ let is_inferred = match tcx.named_region(lifetime.hir_id) {
+ Some(Region::EarlyBound(def_id)) => inferred_outlives
+ .iter()
+ .any(|r| matches!(**r, ty::ReEarlyBound(ebr) if { ebr.def_id == def_id })),
+ _ => false,
+ };
+
+ if !is_inferred {
+ return None;
}
+
+ let span = bound.span().find_ancestor_inside(predicate_span)?;
+ if in_external_macro(tcx.sess, span) {
+ return None;
+ }
+
+ Some((i, span))
})
- .filter(|(_, span)| !in_external_macro(tcx.sess, *span))
.collect()
}
@@ -2240,9 +2099,9 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
use rustc_middle::middle::resolve_lifetime::Region;
let def_id = item.owner_id.def_id;
- if let hir::ItemKind::Struct(_, ref hir_generics)
- | hir::ItemKind::Enum(_, ref hir_generics)
- | hir::ItemKind::Union(_, ref hir_generics) = item.kind
+ if let hir::ItemKind::Struct(_, hir_generics)
+ | hir::ItemKind::Enum(_, hir_generics)
+ | hir::ItemKind::Union(_, hir_generics) = item.kind
{
let inferred_outlives = cx.tcx.inferred_outlives_of(def_id);
if inferred_outlives.is_empty() {
@@ -2257,53 +2116,58 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
let mut dropped_predicate_count = 0;
let num_predicates = hir_generics.predicates.len();
for (i, where_predicate) in hir_generics.predicates.iter().enumerate() {
- let (relevant_lifetimes, bounds, span, in_where_clause) = match where_predicate {
- hir::WherePredicate::RegionPredicate(predicate) => {
- if let Some(Region::EarlyBound(region_def_id)) =
- cx.tcx.named_region(predicate.lifetime.hir_id)
- {
- (
- Self::lifetimes_outliving_lifetime(
- inferred_outlives,
- region_def_id,
- ),
- &predicate.bounds,
- predicate.span,
- predicate.in_where_clause,
- )
- } else {
- continue;
- }
- }
- hir::WherePredicate::BoundPredicate(predicate) => {
- // FIXME we can also infer bounds on associated types,
- // and should check for them here.
- match predicate.bounded_ty.kind {
- hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) => {
- let Res::Def(DefKind::TyParam, def_id) = path.res else {
- continue
- };
- let index = ty_generics.param_def_id_to_index[&def_id];
+ let (relevant_lifetimes, bounds, predicate_span, in_where_clause) =
+ match where_predicate {
+ hir::WherePredicate::RegionPredicate(predicate) => {
+ if let Some(Region::EarlyBound(region_def_id)) =
+ cx.tcx.named_region(predicate.lifetime.hir_id)
+ {
(
- Self::lifetimes_outliving_type(inferred_outlives, index),
+ Self::lifetimes_outliving_lifetime(
+ inferred_outlives,
+ region_def_id,
+ ),
&predicate.bounds,
predicate.span,
- predicate.origin == PredicateOrigin::WhereClause,
+ predicate.in_where_clause,
)
- }
- _ => {
+ } else {
continue;
}
}
- }
- _ => continue,
- };
+ hir::WherePredicate::BoundPredicate(predicate) => {
+ // FIXME we can also infer bounds on associated types,
+ // and should check for them here.
+ match predicate.bounded_ty.kind {
+ hir::TyKind::Path(hir::QPath::Resolved(None, path)) => {
+ let Res::Def(DefKind::TyParam, def_id) = path.res else {
+ continue;
+ };
+ let index = ty_generics.param_def_id_to_index[&def_id];
+ (
+ Self::lifetimes_outliving_type(inferred_outlives, index),
+ &predicate.bounds,
+ predicate.span,
+ predicate.origin == PredicateOrigin::WhereClause,
+ )
+ }
+ _ => {
+ continue;
+ }
+ }
+ }
+ _ => continue,
+ };
if relevant_lifetimes.is_empty() {
continue;
}
- let bound_spans =
- self.collect_outlives_bound_spans(cx.tcx, bounds, &relevant_lifetimes);
+ let bound_spans = self.collect_outlives_bound_spans(
+ cx.tcx,
+ bounds,
+ &relevant_lifetimes,
+ predicate_span,
+ );
bound_count += bound_spans.len();
let drop_predicate = bound_spans.len() == bounds.len();
@@ -2312,15 +2176,15 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
}
if drop_predicate && !in_where_clause {
- lint_spans.push(span);
+ lint_spans.push(predicate_span);
} else if drop_predicate && i + 1 < num_predicates {
// If all the bounds on a predicate were inferable and there are
// further predicates, we want to eat the trailing comma.
let next_predicate_span = hir_generics.predicates[i + 1].span();
- where_lint_spans.push(span.to(next_predicate_span.shrink_to_lo()));
+ where_lint_spans.push(predicate_span.to(next_predicate_span.shrink_to_lo()));
} else {
where_lint_spans.extend(self.consolidate_outlives_bound_spans(
- span.shrink_to_lo(),
+ predicate_span.shrink_to_lo(),
bounds,
bound_spans,
));
@@ -2341,25 +2205,35 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
} else {
hir_generics.span.shrink_to_hi().to(where_span)
};
- lint_spans.push(full_where_span);
+
+ // Due to macro expansions, the `full_where_span` might not actually contain all predicates.
+ if where_lint_spans.iter().all(|&sp| full_where_span.contains(sp)) {
+ lint_spans.push(full_where_span);
+ } else {
+ lint_spans.extend(where_lint_spans);
+ }
} else {
lint_spans.extend(where_lint_spans);
}
if !lint_spans.is_empty() {
- cx.struct_span_lint(
+ // Do not automatically delete outlives requirements from macros.
+ let applicability = if lint_spans.iter().all(|sp| sp.can_be_used_for_suggestions())
+ {
+ Applicability::MachineApplicable
+ } else {
+ Applicability::MaybeIncorrect
+ };
+
+ cx.emit_spanned_lint(
EXPLICIT_OUTLIVES_REQUIREMENTS,
lint_spans.clone(),
- fluent::lint_builtin_explicit_outlives,
- |lint| {
- lint.set_arg("count", bound_count).multipart_suggestion(
- fluent::suggestion,
- lint_spans
- .into_iter()
- .map(|span| (span, String::new()))
- .collect::<Vec<_>>(),
- Applicability::MachineApplicable,
- )
+ BuiltinExplicitOutlives {
+ count: bound_count,
+ suggestion: BuiltinExplicitOutlivesSuggestion {
+ spans: lint_spans,
+ applicability,
+ },
},
);
}
@@ -2408,24 +2282,18 @@ impl EarlyLintPass for IncompleteFeatures {
.chain(features.declared_lib_features.iter().map(|(name, span)| (name, span)))
.filter(|(&name, _)| features.incomplete(name))
.for_each(|(&name, &span)| {
- cx.struct_span_lint(
+ let note = rustc_feature::find_feature_issue(name, GateIssue::Language)
+ .map(|n| BuiltinIncompleteFeaturesNote { n });
+ let help = if HAS_MIN_FEATURES.contains(&name) {
+ Some(BuiltinIncompleteFeaturesHelp)
+ } else {
+ None
+ };
+ cx.emit_spanned_lint(
INCOMPLETE_FEATURES,
span,
- fluent::lint_builtin_incomplete_features,
- |lint| {
- lint.set_arg("name", name);
- if let Some(n) =
- rustc_feature::find_feature_issue(name, GateIssue::Language)
- {
- lint.set_arg("n", n);
- lint.note(fluent::note);
- }
- if HAS_MIN_FEATURES.contains(&name) {
- lint.help(fluent::help);
- }
- lint
- },
- )
+ BuiltinIncompleteFeatures { name, note, help },
+ );
});
}
}
@@ -2470,6 +2338,36 @@ declare_lint! {
declare_lint_pass!(InvalidValue => [INVALID_VALUE]);
+/// Information about why a type cannot be initialized this way.
+pub struct InitError {
+ pub(crate) message: String,
+ /// Spans from struct fields and similar that can be obtained from just the type.
+ pub(crate) span: Option<Span>,
+ /// Used to report a trace through adts.
+ pub(crate) nested: Option<Box<InitError>>,
+}
+impl InitError {
+ fn spanned(self, span: Span) -> InitError {
+ Self { span: Some(span), ..self }
+ }
+
+ fn nested(self, nested: impl Into<Option<InitError>>) -> InitError {
+ assert!(self.nested.is_none());
+ Self { nested: nested.into().map(Box::new), ..self }
+ }
+}
+
+impl<'a> From<&'a str> for InitError {
+ fn from(s: &'a str) -> Self {
+ s.to_owned().into()
+ }
+}
+impl From<String> for InitError {
+ fn from(message: String) -> Self {
+ Self { message, span: None, nested: None }
+ }
+}
+
impl<'tcx> LateLintPass<'tcx> for InvalidValue {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) {
#[derive(Debug, Copy, Clone, PartialEq)]
@@ -2478,36 +2376,6 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
Uninit,
}
- /// Information about why a type cannot be initialized this way.
- struct InitError {
- message: String,
- /// Spans from struct fields and similar that can be obtained from just the type.
- span: Option<Span>,
- /// Used to report a trace through adts.
- nested: Option<Box<InitError>>,
- }
- impl InitError {
- fn spanned(self, span: Span) -> InitError {
- Self { span: Some(span), ..self }
- }
-
- fn nested(self, nested: impl Into<Option<InitError>>) -> InitError {
- assert!(self.nested.is_none());
- Self { nested: nested.into().map(Box::new), ..self }
- }
- }
-
- impl<'a> From<&'a str> for InitError {
- fn from(s: &'a str) -> Self {
- s.to_owned().into()
- }
- }
- impl From<String> for InitError {
- fn from(message: String) -> Self {
- Self { message, span: None, nested: None }
- }
- }
-
/// Test if this constant is all-0.
fn is_zero(expr: &hir::Expr<'_>) -> bool {
use hir::ExprKind::*;
@@ -2731,46 +2599,16 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
// using zeroed or uninitialized memory.
// We are extremely conservative with what we warn about.
let conjured_ty = cx.typeck_results().expr_ty(expr);
- if let Some(mut err) = with_no_trimmed_paths!(ty_find_init_error(cx, conjured_ty, init))
- {
- // FIXME(davidtwco): make translatable
- cx.struct_span_lint(
+ if let Some(err) = with_no_trimmed_paths!(ty_find_init_error(cx, conjured_ty, init)) {
+ let msg = match init {
+ InitKind::Zeroed => fluent::lint_builtin_unpermitted_type_init_zeroed,
+ InitKind::Uninit => fluent::lint_builtin_unpermitted_type_init_unint,
+ };
+ let sub = BuiltinUnpermittedTypeInitSub { err };
+ cx.emit_spanned_lint(
INVALID_VALUE,
expr.span,
- DelayDm(|| {
- format!(
- "the type `{}` does not permit {}",
- conjured_ty,
- match init {
- InitKind::Zeroed => "zero-initialization",
- InitKind::Uninit => "being left uninitialized",
- },
- )
- }),
- |lint| {
- lint.span_label(
- expr.span,
- "this code causes undefined behavior when executed",
- );
- lint.span_label(
- expr.span,
- "help: use `MaybeUninit<T>` instead, \
- and only call `assume_init` after initialization is done",
- );
- loop {
- if let Some(span) = err.span {
- lint.span_note(span, &err.message);
- } else {
- lint.note(&err.message);
- }
- if let Some(e) = err.nested {
- err = *e;
- } else {
- break;
- }
- }
- lint
- },
+ BuiltinUnpermittedTypeInit { msg, ty: conjured_ty, label: expr.span, sub },
);
}
}
@@ -3053,8 +2891,8 @@ impl ClashingExternDeclarations {
| (Closure(..), Closure(..))
| (Generator(..), Generator(..))
| (GeneratorWitness(..), GeneratorWitness(..))
- | (Projection(..), Projection(..))
- | (Opaque(..), Opaque(..)) => false,
+ | (Alias(ty::Projection, ..), Alias(ty::Projection, ..))
+ | (Alias(ty::Opaque, ..), Alias(ty::Opaque, ..)) => false,
// These definitely should have been caught above.
(Bool, Bool) | (Char, Char) | (Never, Never) | (Str, Str) => unreachable!(),
@@ -3116,31 +2954,39 @@ impl<'tcx> LateLintPass<'tcx> for ClashingExternDeclarations {
SymbolName::Normal(_) => fi.span,
SymbolName::Link(_, annot_span) => fi.span.to(annot_span),
};
- // Finally, emit the diagnostic.
- let msg = if orig.get_name() == this_fi.ident.name {
- fluent::lint_builtin_clashing_extern_same_name
+ // Finally, emit the diagnostic.
+ let this = this_fi.ident.name;
+ let orig = orig.get_name();
+ let previous_decl_label = get_relevant_span(orig_fi);
+ let mismatch_label = get_relevant_span(this_fi);
+ let sub = BuiltinClashingExternSub {
+ tcx,
+ expected: existing_decl_ty,
+ found: this_decl_ty,
+ };
+ let decorator = if orig == this {
+ BuiltinClashingExtern::SameName {
+ this,
+ orig,
+ previous_decl_label,
+ mismatch_label,
+ sub,
+ }
} else {
- fluent::lint_builtin_clashing_extern_diff_name
+ BuiltinClashingExtern::DiffName {
+ this,
+ orig,
+ previous_decl_label,
+ mismatch_label,
+ sub,
+ }
};
- tcx.struct_span_lint_hir(
+ tcx.emit_spanned_lint(
CLASHING_EXTERN_DECLARATIONS,
this_fi.hir_id(),
get_relevant_span(this_fi),
- msg,
- |lint| {
- let mut expected_str = DiagnosticStyledString::new();
- expected_str.push(existing_decl_ty.fn_sig(tcx).to_string(), false);
- let mut found_str = DiagnosticStyledString::new();
- found_str.push(this_decl_ty.fn_sig(tcx).to_string(), true);
-
- lint.set_arg("this_fi", this_fi.ident.name)
- .set_arg("orig", orig.get_name())
- .span_label(get_relevant_span(orig_fi), fluent::previous_decl_label)
- .span_label(get_relevant_span(this_fi), fluent::mismatch_label)
- // FIXME(davidtwco): translatable expected/found
- .note_expected_found(&"", expected_str, &"", found_str)
- },
+ decorator,
);
}
}
@@ -3220,11 +3066,10 @@ impl<'tcx> LateLintPass<'tcx> for DerefNullPtr {
if let rustc_hir::ExprKind::Unary(rustc_hir::UnOp::Deref, expr_deref) = expr.kind {
if is_null_ptr(cx, expr_deref) {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
DEREF_NULLPTR,
expr.span,
- fluent::lint_builtin_deref_nullptr,
- |lint| lint.span_label(expr.span, fluent::label),
+ BuiltinDerefNullptr { label: expr.span },
);
}
}
@@ -3269,6 +3114,7 @@ declare_lint! {
declare_lint_pass!(NamedAsmLabels => [NAMED_ASM_LABELS]);
impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels {
+ #[allow(rustc::diagnostic_outside_of_impl)]
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
if let hir::Expr {
kind: hir::ExprKind::InlineAsm(hir::InlineAsm { template_strs, .. }),
@@ -3409,16 +3255,17 @@ impl EarlyLintPass for SpecialModuleName {
}
match item.ident.name.as_str() {
- "lib" => cx.struct_span_lint(SPECIAL_MODULE_NAME, item.span, "found module declaration for lib.rs", |lint| {
- lint
- .note("lib.rs is the root of this crate's library target")
- .help("to refer to it from other targets, use the library's name as the path")
- }),
- "main" => cx.struct_span_lint(SPECIAL_MODULE_NAME, item.span, "found module declaration for main.rs", |lint| {
- lint
- .note("a binary crate cannot be used as library")
- }),
- _ => continue
+ "lib" => cx.emit_spanned_lint(
+ SPECIAL_MODULE_NAME,
+ item.span,
+ BuiltinSpecialModuleNameUsed::Lib,
+ ),
+ "main" => cx.emit_spanned_lint(
+ SPECIAL_MODULE_NAME,
+ item.span,
+ BuiltinSpecialModuleNameUsed::Main,
+ ),
+ _ => continue,
}
}
}
@@ -3434,31 +3281,16 @@ impl EarlyLintPass for UnexpectedCfgs {
let cfg = &cx.sess().parse_sess.config;
let check_cfg = &cx.sess().parse_sess.check_config;
for &(name, value) in cfg {
- if let Some(names_valid) = &check_cfg.names_valid {
- if !names_valid.contains(&name) {
- cx.lookup(
- UNEXPECTED_CFGS,
- None::<MultiSpan>,
- fluent::lint_builtin_unexpected_cli_config_name,
- |diag| diag.help(fluent::help).set_arg("name", name),
- );
- }
+ if let Some(names_valid) = &check_cfg.names_valid && !names_valid.contains(&name){
+ cx.emit_lint(UNEXPECTED_CFGS, BuiltinUnexpectedCliConfigName {
+ name,
+ });
}
- if let Some(value) = value {
- if let Some(values) = &check_cfg.values_valid.get(&name) {
- if !values.contains(&value) {
- cx.lookup(
- UNEXPECTED_CFGS,
- None::<MultiSpan>,
- fluent::lint_builtin_unexpected_cli_config_value,
- |diag| {
- diag.help(fluent::help)
- .set_arg("name", name)
- .set_arg("value", value)
- },
- );
- }
- }
+ if let Some(value) = value && let Some(values) = check_cfg.values_valid.get(&name) && !values.contains(&value) {
+ cx.emit_lint(
+ UNEXPECTED_CFGS,
+ BuiltinUnexpectedCliConfigValue { name, value },
+ );
}
}
}
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index e6a0d7e60..8046cc21c 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -355,14 +355,12 @@ impl LintStore {
sub: RequestedLevel { level, lint_name },
});
}
- CheckLintNameResult::Tool(result) => {
- if let Err((Some(_), new_name)) = result {
- sess.emit_warning(CheckNameDeprecated {
- lint_name: lint_name.clone(),
- new_name,
- sub: RequestedLevel { level, lint_name },
- });
- }
+ CheckLintNameResult::Tool(Err((Some(_), new_name))) => {
+ sess.emit_warning(CheckNameDeprecated {
+ lint_name: lint_name.clone(),
+ new_name,
+ sub: RequestedLevel { level, lint_name },
+ });
}
CheckLintNameResult::NoTool => {
sess.emit_err(CheckNameUnknownTool {
@@ -438,18 +436,18 @@ impl LintStore {
return CheckLintNameResult::Tool(Ok(&lint_ids));
}
},
- Some(&Id(ref id)) => return CheckLintNameResult::Tool(Ok(slice::from_ref(id))),
+ Some(Id(id)) => return CheckLintNameResult::Tool(Ok(slice::from_ref(id))),
// If the lint was registered as removed or renamed by the lint tool, we don't need
// to treat tool_lints and rustc lints different and can use the code below.
_ => {}
}
}
match self.by_name.get(&complete_name) {
- Some(&Renamed(ref new_name, _)) => CheckLintNameResult::Warning(
+ Some(Renamed(new_name, _)) => CheckLintNameResult::Warning(
format!("lint `{}` has been renamed to `{}`", complete_name, new_name),
Some(new_name.to_owned()),
),
- Some(&Removed(ref reason)) => CheckLintNameResult::Warning(
+ Some(Removed(reason)) => CheckLintNameResult::Warning(
format!("lint `{}` has been removed: {}", complete_name, reason),
None,
),
@@ -470,7 +468,7 @@ impl LintStore {
CheckLintNameResult::Ok(&lint_ids)
}
},
- Some(&Id(ref id)) => CheckLintNameResult::Ok(slice::from_ref(id)),
+ Some(Id(id)) => CheckLintNameResult::Ok(slice::from_ref(id)),
Some(&Ignored) => CheckLintNameResult::Ok(&[]),
}
}
@@ -483,7 +481,16 @@ impl LintStore {
return CheckLintNameResult::NoLint(Some(Symbol::intern(&name_lower)));
}
// ...if not, search for lints with a similar name
- let groups = self.lint_groups.keys().copied().map(Symbol::intern);
+ // Note: find_best_match_for_name depends on the sort order of its input vector.
+ // To ensure deterministic output, sort elements of the lint_groups hash map.
+ // Also, never suggest deprecated lint groups.
+ let mut groups: Vec<_> = self
+ .lint_groups
+ .iter()
+ .filter_map(|(k, LintGroup { depr, .. })| if depr.is_none() { Some(k) } else { None })
+ .collect();
+ groups.sort();
+ let groups = groups.iter().map(|k| Symbol::intern(k));
let lints = self.lints.iter().map(|l| Symbol::intern(&l.name_lower()));
let names: Vec<Symbol> = groups.chain(lints).collect();
let suggestion = find_best_match_for_name(&names, Symbol::intern(&name_lower), None);
@@ -513,7 +520,7 @@ impl LintStore {
CheckLintNameResult::Tool(Err((Some(&lint_ids), complete_name)))
}
},
- Some(&Id(ref id)) => {
+ Some(Id(id)) => {
CheckLintNameResult::Tool(Err((Some(slice::from_ref(id)), complete_name)))
}
Some(other) => {
@@ -818,21 +825,24 @@ pub trait LintContext: Sized {
debug!(?param_span, ?use_span, ?deletion_span);
db.span_label(param_span, "this lifetime...");
db.span_label(use_span, "...is used only here");
- let msg = "elide the single-use lifetime";
- let (use_span, replace_lt) = if elide {
- let use_span = sess.source_map().span_extend_while(
- use_span,
- char::is_whitespace,
- ).unwrap_or(use_span);
- (use_span, String::new())
- } else {
- (use_span, "'_".to_owned())
- };
- db.multipart_suggestion(
- msg,
- vec![(deletion_span, String::new()), (use_span, replace_lt)],
- Applicability::MachineApplicable,
- );
+ if let Some(deletion_span) = deletion_span {
+ let msg = "elide the single-use lifetime";
+ let (use_span, replace_lt) = if elide {
+ let use_span = sess.source_map().span_extend_while(
+ use_span,
+ char::is_whitespace,
+ ).unwrap_or(use_span);
+ (use_span, String::new())
+ } else {
+ (use_span, "'_".to_owned())
+ };
+ debug!(?deletion_span, ?use_span);
+ db.multipart_suggestion(
+ msg,
+ vec![(deletion_span, String::new()), (use_span, replace_lt)],
+ Applicability::MachineApplicable,
+ );
+ }
},
BuiltinLintDiagnostics::SingleUseLifetime {
param_span: _,
@@ -840,12 +850,14 @@ pub trait LintContext: Sized {
deletion_span,
} => {
debug!(?deletion_span);
- db.span_suggestion(
- deletion_span,
- "elide the unused lifetime",
- "",
- Applicability::MachineApplicable,
- );
+ if let Some(deletion_span) = deletion_span {
+ db.span_suggestion(
+ deletion_span,
+ "elide the unused lifetime",
+ "",
+ Applicability::MachineApplicable,
+ );
+ }
},
BuiltinLintDiagnostics::NamedArgumentUsedPositionally{ position_sp_to_replace, position_sp_for_msg, named_arg_sp, named_arg_name, is_formatting_arg} => {
db.span_label(named_arg_sp, "this named argument is referred to by position in formatting string");
@@ -958,6 +970,7 @@ pub trait LintContext: Sized {
/// Note that this function should only be called for [`LintExpectationId`]s
/// retrieved from the current lint pass. Buffered or manually created ids can
/// cause ICEs.
+ #[rustc_lint_diagnostics]
fn fulfill_expectation(&self, expectation: LintExpectationId) {
// We need to make sure that submitted expectation ids are correctly fulfilled suppressed
// and stored between compilation sessions. To not manually do these steps, we simply create
@@ -1004,6 +1017,7 @@ impl<'tcx> LintContext for LateContext<'tcx> {
&*self.lint_store
}
+ #[rustc_lint_diagnostics]
fn lookup<S: Into<MultiSpan>>(
&self,
lint: &'static Lint,
@@ -1038,6 +1052,7 @@ impl LintContext for EarlyContext<'_> {
self.builder.lint_store()
}
+ #[rustc_lint_diagnostics]
fn lookup<S: Into<MultiSpan>>(
&self,
lint: &'static Lint,
@@ -1258,7 +1273,7 @@ impl<'tcx> LateContext<'tcx> {
tcx.associated_items(trait_id)
.find_by_name_and_kind(tcx, Ident::from_str(name), ty::AssocKind::Type, trait_id)
.and_then(|assoc| {
- let proj = tcx.mk_projection(assoc.def_id, tcx.mk_substs_trait(self_ty, []));
+ let proj = tcx.mk_projection(assoc.def_id, [self_ty]);
tcx.try_normalize_erasing_regions(self.param_env, proj).ok()
})
}
diff --git a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs
index 1d29a234a..a45d8156c 100644
--- a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs
+++ b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs
@@ -1,6 +1,8 @@
-use crate::{LateContext, LateLintPass, LintContext};
+use crate::{
+ lints::{SupertraitAsDerefTarget, SupertraitAsDerefTargetLabel},
+ LateContext, LateLintPass, LintContext,
+};
-use rustc_errors::DelayDm;
use rustc_hir as hir;
use rustc_middle::{traits::util::supertraits, ty};
use rustc_span::sym;
@@ -71,22 +73,14 @@ impl<'tcx> LateLintPass<'tcx> for DerefIntoDynSupertrait {
&& supertraits(cx.tcx, t_principal.with_self_ty(cx.tcx, cx.tcx.types.trait_object_dummy_self))
.any(|sup| sup.map_bound(|x| ty::ExistentialTraitRef::erase_self_ty(cx.tcx, x)) == target_principal)
{
- cx.struct_span_lint(
- DEREF_INTO_DYN_SUPERTRAIT,
- cx.tcx.def_span(item.owner_id.def_id),
- DelayDm(|| {
- format!(
- "`{t}` implements `Deref` with supertrait `{target_principal}` as target"
- )
- }),
- |lint| {
- if let Some(target_span) = impl_.items.iter().find_map(|i| (i.ident.name == sym::Target).then_some(i.span)) {
- lint.span_label(target_span, "target type is set here");
- }
-
- lint
- },
- )
+ let label = impl_.items.iter().find_map(|i| (i.ident.name == sym::Target).then_some(i.span)).map(|label| SupertraitAsDerefTargetLabel {
+ label,
+ });
+ cx.emit_spanned_lint(DEREF_INTO_DYN_SUPERTRAIT, cx.tcx.def_span(item.owner_id.def_id), SupertraitAsDerefTarget {
+ t,
+ target_principal,
+ label,
+ });
}
}
}
diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs
index 52363b0be..337a19dd0 100644
--- a/compiler/rustc_lint/src/early.rs
+++ b/compiler/rustc_lint/src/early.rs
@@ -19,25 +19,29 @@ use crate::passes::{EarlyLintPass, EarlyLintPassObject};
use rustc_ast::ptr::P;
use rustc_ast::visit::{self as ast_visit, Visitor};
use rustc_ast::{self as ast, walk_list, HasAttrs};
+use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_middle::ty::RegisteredTools;
-use rustc_session::lint::{BufferedEarlyLint, LintBuffer};
+use rustc_session::lint::{BufferedEarlyLint, LintBuffer, LintPass};
use rustc_session::Session;
use rustc_span::symbol::Ident;
use rustc_span::Span;
-macro_rules! run_early_passes { ($cx:expr, $f:ident, $($args:expr),*) => ({
- for pass in $cx.passes.iter_mut() {
- pass.$f(&$cx.context, $($args),*);
- }
+macro_rules! lint_callback { ($cx:expr, $f:ident, $($args:expr),*) => ({
+ $cx.pass.$f(&$cx.context, $($args),*);
}) }
-pub struct EarlyContextAndPasses<'a> {
+/// Implements the AST traversal for early lint passes. `T` provides the
+/// `check_*` methods.
+pub struct EarlyContextAndPass<'a, T: EarlyLintPass> {
context: EarlyContext<'a>,
- passes: Vec<EarlyLintPassObject>,
+ pass: T,
}
-impl<'a> EarlyContextAndPasses<'a> {
- fn check_id(&mut self, id: ast::NodeId) {
+impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> {
+ // This always-inlined function is for the hot call site.
+ #[inline(always)]
+ #[allow(rustc::diagnostic_outside_of_impl)]
+ fn inlined_check_id(&mut self, id: ast::NodeId) {
for early_lint in self.context.buffered.take(id) {
let BufferedEarlyLint { span, msg, node_id: _, lint_id, diagnostic } = early_lint;
self.context.lookup_with_diagnostics(
@@ -50,6 +54,11 @@ impl<'a> EarlyContextAndPasses<'a> {
}
}
+ // This non-inlined function is for the cold call sites.
+ fn check_id(&mut self, id: ast::NodeId) {
+ self.inlined_check_id(id)
+ }
+
/// Merge the lints specified by any lint attributes into the
/// current lint context, call the provided function, then reset the
/// lints in effect to their previous state.
@@ -61,29 +70,29 @@ impl<'a> EarlyContextAndPasses<'a> {
debug!(?id);
let push = self.context.builder.push(attrs, is_crate_node, None);
- self.check_id(id);
+ self.inlined_check_id(id);
debug!("early context: enter_attrs({:?})", attrs);
- run_early_passes!(self, enter_lint_attrs, attrs);
- f(self);
+ lint_callback!(self, enter_lint_attrs, attrs);
+ ensure_sufficient_stack(|| f(self));
debug!("early context: exit_attrs({:?})", attrs);
- run_early_passes!(self, exit_lint_attrs, attrs);
+ lint_callback!(self, exit_lint_attrs, attrs);
self.context.builder.pop(push);
}
}
-impl<'a> ast_visit::Visitor<'a> for EarlyContextAndPasses<'a> {
+impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> {
fn visit_param(&mut self, param: &'a ast::Param) {
self.with_lint_attrs(param.id, &param.attrs, |cx| {
- run_early_passes!(cx, check_param, param);
+ lint_callback!(cx, check_param, param);
ast_visit::walk_param(cx, param);
});
}
fn visit_item(&mut self, it: &'a ast::Item) {
self.with_lint_attrs(it.id, &it.attrs, |cx| {
- run_early_passes!(cx, check_item, it);
+ lint_callback!(cx, check_item, it);
ast_visit::walk_item(cx, it);
- run_early_passes!(cx, check_item_post, it);
+ lint_callback!(cx, check_item_post, it);
})
}
@@ -94,10 +103,10 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContextAndPasses<'a> {
}
fn visit_pat(&mut self, p: &'a ast::Pat) {
- run_early_passes!(self, check_pat, p);
+ lint_callback!(self, check_pat, p);
self.check_id(p.id);
ast_visit::walk_pat(self, p);
- run_early_passes!(self, check_pat_post, p);
+ lint_callback!(self, check_pat_post, p);
}
fn visit_pat_field(&mut self, field: &'a ast::PatField) {
@@ -113,7 +122,7 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContextAndPasses<'a> {
fn visit_expr(&mut self, e: &'a ast::Expr) {
self.with_lint_attrs(e.id, &e.attrs, |cx| {
- run_early_passes!(cx, check_expr, e);
+ lint_callback!(cx, check_expr, e);
ast_visit::walk_expr(cx, e);
})
}
@@ -134,7 +143,7 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContextAndPasses<'a> {
// Note that statements get their attributes from
// the AST struct that they wrap (e.g. an item)
self.with_lint_attrs(s.id, s.attrs(), |cx| {
- run_early_passes!(cx, check_stmt, s);
+ lint_callback!(cx, check_stmt, s);
cx.check_id(s.id);
});
// The visitor for the AST struct wrapped
@@ -145,7 +154,7 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContextAndPasses<'a> {
}
fn visit_fn(&mut self, fk: ast_visit::FnKind<'a>, span: Span, id: ast::NodeId) {
- run_early_passes!(self, check_fn, fk, span, id);
+ lint_callback!(self, check_fn, fk, span, id);
self.check_id(id);
ast_visit::walk_fn(self, fk);
@@ -173,37 +182,37 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContextAndPasses<'a> {
fn visit_variant(&mut self, v: &'a ast::Variant) {
self.with_lint_attrs(v.id, &v.attrs, |cx| {
- run_early_passes!(cx, check_variant, v);
+ lint_callback!(cx, check_variant, v);
ast_visit::walk_variant(cx, v);
})
}
fn visit_ty(&mut self, t: &'a ast::Ty) {
- run_early_passes!(self, check_ty, t);
+ lint_callback!(self, check_ty, t);
self.check_id(t.id);
ast_visit::walk_ty(self, t);
}
fn visit_ident(&mut self, ident: Ident) {
- run_early_passes!(self, check_ident, ident);
+ lint_callback!(self, check_ident, ident);
}
fn visit_local(&mut self, l: &'a ast::Local) {
self.with_lint_attrs(l.id, &l.attrs, |cx| {
- run_early_passes!(cx, check_local, l);
+ lint_callback!(cx, check_local, l);
ast_visit::walk_local(cx, l);
})
}
fn visit_block(&mut self, b: &'a ast::Block) {
- run_early_passes!(self, check_block, b);
+ lint_callback!(self, check_block, b);
self.check_id(b.id);
ast_visit::walk_block(self, b);
}
fn visit_arm(&mut self, a: &'a ast::Arm) {
self.with_lint_attrs(a.id, &a.attrs, |cx| {
- run_early_passes!(cx, check_arm, a);
+ lint_callback!(cx, check_arm, a);
ast_visit::walk_arm(cx, a);
})
}
@@ -222,39 +231,41 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContextAndPasses<'a> {
}
fn visit_generic_arg(&mut self, arg: &'a ast::GenericArg) {
- run_early_passes!(self, check_generic_arg, arg);
+ lint_callback!(self, check_generic_arg, arg);
ast_visit::walk_generic_arg(self, arg);
}
fn visit_generic_param(&mut self, param: &'a ast::GenericParam) {
self.with_lint_attrs(param.id, &param.attrs, |cx| {
- run_early_passes!(cx, check_generic_param, param);
+ lint_callback!(cx, check_generic_param, param);
ast_visit::walk_generic_param(cx, param);
});
}
fn visit_generics(&mut self, g: &'a ast::Generics) {
- run_early_passes!(self, check_generics, g);
+ lint_callback!(self, check_generics, g);
ast_visit::walk_generics(self, g);
}
fn visit_where_predicate(&mut self, p: &'a ast::WherePredicate) {
+ lint_callback!(self, enter_where_predicate, p);
ast_visit::walk_where_predicate(self, p);
+ lint_callback!(self, exit_where_predicate, p);
}
fn visit_poly_trait_ref(&mut self, t: &'a ast::PolyTraitRef) {
- run_early_passes!(self, check_poly_trait_ref, t);
+ lint_callback!(self, check_poly_trait_ref, t);
ast_visit::walk_poly_trait_ref(self, t);
}
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_passes!(cx, check_trait_item, item);
+ lint_callback!(cx, check_trait_item, item);
ast_visit::walk_assoc_item(cx, item, ctxt);
}
ast_visit::AssocCtxt::Impl => {
- run_early_passes!(cx, check_impl_item, item);
+ lint_callback!(cx, check_impl_item, item);
ast_visit::walk_assoc_item(cx, item, ctxt);
}
});
@@ -275,20 +286,49 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContextAndPasses<'a> {
}
fn visit_attribute(&mut self, attr: &'a ast::Attribute) {
- run_early_passes!(self, check_attribute, attr);
+ lint_callback!(self, check_attribute, attr);
}
fn visit_mac_def(&mut self, mac: &'a ast::MacroDef, id: ast::NodeId) {
- run_early_passes!(self, check_mac_def, mac);
+ lint_callback!(self, check_mac_def, mac);
self.check_id(id);
}
fn visit_mac_call(&mut self, mac: &'a ast::MacCall) {
- run_early_passes!(self, check_mac, mac);
+ lint_callback!(self, check_mac, mac);
ast_visit::walk_mac(self, mac);
}
}
+// Combines multiple lint passes into a single pass, at runtime. Each
+// `check_foo` method in `$methods` within this pass simply calls `check_foo`
+// once per `$pass`. Compare with `declare_combined_early_lint_pass`, which is
+// similar, but combines lint passes at compile time.
+struct RuntimeCombinedEarlyLintPass<'a> {
+ passes: &'a mut [EarlyLintPassObject],
+}
+
+#[allow(rustc::lint_pass_impl_without_macro)]
+impl LintPass for RuntimeCombinedEarlyLintPass<'_> {
+ fn name(&self) -> &'static str {
+ panic!()
+ }
+}
+
+macro_rules! impl_early_lint_pass {
+ ([], [$($(#[$attr:meta])* fn $f:ident($($param:ident: $arg:ty),*);)*]) => (
+ impl EarlyLintPass for RuntimeCombinedEarlyLintPass<'_> {
+ $(fn $f(&mut self, context: &EarlyContext<'_>, $($param: $arg),*) {
+ for pass in self.passes.iter_mut() {
+ pass.$f(context, $($param),*);
+ }
+ })*
+ }
+ )
+}
+
+crate::early_lint_methods!(impl_early_lint_pass, []);
+
/// Early lints work on different nodes - either on the crate root, or on freshly loaded modules.
/// This trait generalizes over those nodes.
pub trait EarlyCheckNode<'a>: Copy {
@@ -296,7 +336,7 @@ pub trait EarlyCheckNode<'a>: Copy {
fn attrs<'b>(self) -> &'b [ast::Attribute]
where
'a: 'b;
- fn check<'b>(self, cx: &mut EarlyContextAndPasses<'b>)
+ fn check<'b, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'b, T>)
where
'a: 'b;
}
@@ -311,13 +351,13 @@ impl<'a> EarlyCheckNode<'a> for &'a ast::Crate {
{
&self.attrs
}
- fn check<'b>(self, cx: &mut EarlyContextAndPasses<'b>)
+ fn check<'b, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'b, T>)
where
'a: 'b,
{
- run_early_passes!(cx, check_crate, self);
+ lint_callback!(cx, check_crate, self);
ast_visit::walk_crate(cx, self);
- run_early_passes!(cx, check_crate_post, self);
+ lint_callback!(cx, check_crate_post, self);
}
}
@@ -331,7 +371,7 @@ impl<'a> EarlyCheckNode<'a> for (ast::NodeId, &'a [ast::Attribute], &'a [P<ast::
{
self.1
}
- fn check<'b>(self, cx: &mut EarlyContextAndPasses<'b>)
+ fn check<'b, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'b, T>)
where
'a: 'b,
{
@@ -349,21 +389,37 @@ pub fn check_ast_node<'a>(
builtin_lints: impl EarlyLintPass + 'static,
check_node: impl EarlyCheckNode<'a>,
) {
+ let context = EarlyContext::new(
+ sess,
+ !pre_expansion,
+ lint_store,
+ registered_tools,
+ lint_buffer.unwrap_or_default(),
+ );
+
+ // Note: `passes` is often empty. In that case, it's faster to run
+ // `builtin_lints` directly rather than bundling it up into the
+ // `RuntimeCombinedEarlyLintPass`.
let passes =
if pre_expansion { &lint_store.pre_expansion_passes } else { &lint_store.early_passes };
- let mut passes: Vec<EarlyLintPassObject> = passes.iter().map(|p| (p)()).collect();
- passes.push(Box::new(builtin_lints));
-
- let mut cx = EarlyContextAndPasses {
- context: EarlyContext::new(
- sess,
- !pre_expansion,
- lint_store,
- registered_tools,
- lint_buffer.unwrap_or_default(),
- ),
- passes,
- };
+ if passes.is_empty() {
+ check_ast_node_inner(sess, check_node, context, builtin_lints);
+ } else {
+ let mut passes: Vec<_> = passes.iter().map(|mk_pass| (mk_pass)()).collect();
+ passes.push(Box::new(builtin_lints));
+ let pass = RuntimeCombinedEarlyLintPass { passes: &mut passes[..] };
+ check_ast_node_inner(sess, check_node, context, pass);
+ }
+}
+
+pub fn check_ast_node_inner<'a, T: EarlyLintPass>(
+ sess: &Session,
+ check_node: impl EarlyCheckNode<'a>,
+ context: EarlyContext<'_>,
+ pass: T,
+) {
+ let mut cx = EarlyContextAndPass { context, pass };
+
cx.with_lint_attrs(check_node.id(), check_node.attrs(), |cx| check_node.check(cx));
// All of the buffered lints should have been emitted at this point.
diff --git a/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs b/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs
index f9d746622..73bd41732 100644
--- a/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs
+++ b/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs
@@ -1,5 +1,8 @@
-use crate::{context::LintContext, LateContext, LateLintPass};
-use rustc_errors::fluent;
+use crate::{
+ context::LintContext,
+ lints::{EnumIntrinsicsMemDiscriminate, EnumIntrinsicsMemVariant},
+ LateContext, LateLintPass,
+};
use rustc_hir as hir;
use rustc_middle::ty::{visit::TypeVisitable, Ty};
use rustc_span::{symbol::sym, Span};
@@ -50,11 +53,10 @@ fn enforce_mem_discriminant(
) {
let ty_param = cx.typeck_results().node_substs(func_expr.hir_id).type_at(0);
if is_non_enum(ty_param) {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
ENUM_INTRINSICS_NON_ENUMS,
expr_span,
- fluent::lint_enum_intrinsics_mem_discriminant,
- |lint| lint.set_arg("ty_param", ty_param).span_note(args_span, fluent::note),
+ EnumIntrinsicsMemDiscriminate { ty_param, note: args_span },
);
}
}
@@ -62,11 +64,10 @@ fn enforce_mem_discriminant(
fn enforce_mem_variant_count(cx: &LateContext<'_>, func_expr: &hir::Expr<'_>, span: Span) {
let ty_param = cx.typeck_results().node_substs(func_expr.hir_id).type_at(0);
if is_non_enum(ty_param) {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
ENUM_INTRINSICS_NON_ENUMS,
span,
- fluent::lint_enum_intrinsics_mem_variant,
- |lint| lint.set_arg("ty_param", ty_param).note(fluent::note),
+ EnumIntrinsicsMemVariant { ty_param },
);
}
}
diff --git a/compiler/rustc_lint/src/errors.rs b/compiler/rustc_lint/src/errors.rs
index 1a769893f..f3ae26091 100644
--- a/compiler/rustc_lint/src/errors.rs
+++ b/compiler/rustc_lint/src/errors.rs
@@ -8,12 +8,12 @@ use rustc_span::{Span, Symbol};
#[derive(Diagnostic)]
#[diag(lint_overruled_attribute, code = "E0453")]
-pub struct OverruledAttribute {
+pub struct OverruledAttribute<'a> {
#[primary_span]
pub span: Span,
#[label]
pub overruled: Span,
- pub lint_level: String,
+ pub lint_level: &'a str,
pub lint_source: Symbol,
#[subdiagnostic]
pub sub: OverruledAttributeSub,
@@ -38,6 +38,7 @@ impl AddToDiagnostic for OverruledAttributeSub {
OverruledAttributeSub::NodeSource { span, reason } => {
diag.span_label(span, fluent::lint_node_source);
if let Some(rationale) = reason {
+ #[allow(rustc::untranslatable_diagnostic)]
diag.note(rationale.as_str());
}
}
diff --git a/compiler/rustc_lint/src/expect.rs b/compiler/rustc_lint/src/expect.rs
index cf8f31bcb..e9eb14ea1 100644
--- a/compiler/rustc_lint/src/expect.rs
+++ b/compiler/rustc_lint/src/expect.rs
@@ -1,8 +1,7 @@
-use crate::builtin;
-use rustc_errors::fluent;
-use rustc_hir::HirId;
+use crate::lints::{Expectation, ExpectationNote};
use rustc_middle::ty::query::Providers;
-use rustc_middle::{lint::LintExpectation, ty::TyCtxt};
+use rustc_middle::ty::TyCtxt;
+use rustc_session::lint::builtin::UNFULFILLED_LINT_EXPECTATIONS;
use rustc_session::lint::LintExpectationId;
use rustc_span::symbol::sym;
use rustc_span::Symbol;
@@ -12,7 +11,7 @@ pub(crate) fn provide(providers: &mut Providers) {
}
fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option<Symbol>) {
- if !tcx.sess.features_untracked().enabled(sym::lint_reasons) {
+ if !tcx.features().enabled(sym::lint_reasons) {
return;
}
@@ -28,34 +27,17 @@ fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option<Symbol>) {
if !fulfilled_expectations.contains(&id)
&& tool_filter.map_or(true, |filter| expectation.lint_tool == Some(filter))
{
- emit_unfulfilled_expectation_lint(tcx, *hir_id, expectation);
+ let rationale = expectation.reason.map(|rationale| ExpectationNote { rationale });
+ let note = expectation.is_unfulfilled_lint_expectations.then_some(());
+ tcx.emit_spanned_lint(
+ UNFULFILLED_LINT_EXPECTATIONS,
+ *hir_id,
+ expectation.emission_span,
+ Expectation { rationale, note },
+ );
}
} else {
unreachable!("at this stage all `LintExpectationId`s are stable");
}
}
}
-
-fn emit_unfulfilled_expectation_lint(
- tcx: TyCtxt<'_>,
- hir_id: HirId,
- expectation: &LintExpectation,
-) {
- tcx.struct_span_lint_hir(
- builtin::UNFULFILLED_LINT_EXPECTATIONS,
- hir_id,
- expectation.emission_span,
- fluent::lint_expectation,
- |lint| {
- if let Some(rationale) = expectation.reason {
- lint.note(rationale.as_str());
- }
-
- if expectation.is_unfulfilled_lint_expectations {
- lint.note(fluent::note);
- }
-
- lint
- },
- );
-}
diff --git a/compiler/rustc_lint/src/for_loops_over_fallibles.rs b/compiler/rustc_lint/src/for_loops_over_fallibles.rs
index 418785015..5219992ee 100644
--- a/compiler/rustc_lint/src/for_loops_over_fallibles.rs
+++ b/compiler/rustc_lint/src/for_loops_over_fallibles.rs
@@ -1,7 +1,12 @@
-use crate::{LateContext, LateLintPass, LintContext};
+use crate::{
+ lints::{
+ ForLoopsOverFalliblesDiag, ForLoopsOverFalliblesLoopSub, ForLoopsOverFalliblesQuestionMark,
+ ForLoopsOverFalliblesSuggestion,
+ },
+ LateContext, LateLintPass, LintContext,
+};
use hir::{Expr, Pat};
-use rustc_errors::{Applicability, DelayDm};
use rustc_hir as hir;
use rustc_infer::{infer::TyCtxtInferExt, traits::ObligationCause};
use rustc_middle::ty::{self, List};
@@ -53,53 +58,29 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopsOverFallibles {
_ => return,
};
- let msg = DelayDm(|| {
- format!(
- "for loop over {article} `{ty}`. This is more readably written as an `if let` statement",
- )
- });
-
- cx.struct_span_lint(FOR_LOOPS_OVER_FALLIBLES, arg.span, msg, |lint| {
- if let Some(recv) = extract_iterator_next_call(cx, arg)
+ let sub = if let Some(recv) = extract_iterator_next_call(cx, arg)
&& let Ok(recv_snip) = cx.sess().source_map().span_to_snippet(recv.span)
{
- lint.span_suggestion(
- recv.span.between(arg.span.shrink_to_hi()),
- format!("to iterate over `{recv_snip}` remove the call to `next`"),
- ".by_ref()",
- Applicability::MaybeIncorrect
- );
+ ForLoopsOverFalliblesLoopSub::RemoveNext { suggestion: recv.span.between(arg.span.shrink_to_hi()), recv_snip }
} else {
- lint.multipart_suggestion_verbose(
- format!("to check pattern in a loop use `while let`"),
- vec![
- // NB can't use `until` here because `expr.span` and `pat.span` have different syntax contexts
- (expr.span.with_hi(pat.span.lo()), format!("while let {var}(")),
- (pat.span.between(arg.span), format!(") = ")),
- ],
- Applicability::MaybeIncorrect
- );
- }
-
- if suggest_question_mark(cx, adt, substs, expr.span) {
- lint.span_suggestion(
- arg.span.shrink_to_hi(),
- "consider unwrapping the `Result` with `?` to iterate over its contents",
- "?",
- Applicability::MaybeIncorrect,
- );
- }
-
- lint.multipart_suggestion_verbose(
- "consider using `if let` to clear intent",
- vec![
- // NB can't use `until` here because `expr.span` and `pat.span` have different syntax contexts
- (expr.span.with_hi(pat.span.lo()), format!("if let {var}(")),
- (pat.span.between(arg.span), format!(") = ")),
- ],
- Applicability::MaybeIncorrect,
- )
- })
+ ForLoopsOverFalliblesLoopSub::UseWhileLet { start_span: expr.span.with_hi(pat.span.lo()), end_span: pat.span.between(arg.span), var }
+ } ;
+ let question_mark = if suggest_question_mark(cx, adt, substs, expr.span) {
+ Some(ForLoopsOverFalliblesQuestionMark { suggestion: arg.span.shrink_to_hi() })
+ } else {
+ None
+ };
+ let suggestion = ForLoopsOverFalliblesSuggestion {
+ var,
+ start_span: expr.span.with_hi(pat.span.lo()),
+ end_span: pat.span.between(arg.span),
+ };
+
+ cx.emit_spanned_lint(
+ FOR_LOOPS_OVER_FALLIBLES,
+ arg.span,
+ ForLoopsOverFalliblesDiag { article, ty, sub, question_mark, suggestion },
+ );
}
}
diff --git a/compiler/rustc_lint/src/hidden_unicode_codepoints.rs b/compiler/rustc_lint/src/hidden_unicode_codepoints.rs
index 7106e75db..7c1af6bee 100644
--- a/compiler/rustc_lint/src/hidden_unicode_codepoints.rs
+++ b/compiler/rustc_lint/src/hidden_unicode_codepoints.rs
@@ -1,7 +1,12 @@
-use crate::{EarlyContext, EarlyLintPass, LintContext};
+use crate::{
+ lints::{
+ HiddenUnicodeCodepointsDiag, HiddenUnicodeCodepointsDiagLabels,
+ HiddenUnicodeCodepointsDiagSub,
+ },
+ EarlyContext, EarlyLintPass, LintContext,
+};
use ast::util::unicode::{contains_text_flow_control_chars, TEXT_FLOW_CONTROL_CHARS};
use rustc_ast as ast;
-use rustc_errors::{fluent, Applicability, SuggestionStyle};
use rustc_span::{BytePos, Span, Symbol};
declare_lint! {
@@ -60,55 +65,19 @@ impl HiddenUnicodeCodepoints {
})
.collect();
- cx.struct_span_lint(
+ let count = spans.len();
+ let labels = point_at_inner_spans
+ .then_some(HiddenUnicodeCodepointsDiagLabels { spans: spans.clone() });
+ let sub = if point_at_inner_spans && !spans.is_empty() {
+ HiddenUnicodeCodepointsDiagSub::Escape { spans }
+ } else {
+ HiddenUnicodeCodepointsDiagSub::NoEscape { spans }
+ };
+
+ cx.emit_spanned_lint(
TEXT_DIRECTION_CODEPOINT_IN_LITERAL,
span,
- fluent::lint_hidden_unicode_codepoints,
- |lint| {
- lint.set_arg("label", label);
- lint.set_arg("count", spans.len());
- lint.span_label(span, fluent::label);
- lint.note(fluent::note);
- if point_at_inner_spans {
- for (c, span) in &spans {
- lint.span_label(*span, format!("{:?}", c));
- }
- }
- if point_at_inner_spans && !spans.is_empty() {
- lint.multipart_suggestion_with_style(
- fluent::suggestion_remove,
- spans.iter().map(|(_, span)| (*span, "".to_string())).collect(),
- Applicability::MachineApplicable,
- SuggestionStyle::HideCodeAlways,
- );
- lint.multipart_suggestion(
- fluent::suggestion_escape,
- spans
- .into_iter()
- .map(|(c, span)| {
- let c = format!("{:?}", c);
- (span, c[1..c.len() - 1].to_string())
- })
- .collect(),
- Applicability::MachineApplicable,
- );
- } else {
- // FIXME: in other suggestions we've reversed the inner spans of doc comments. We
- // should do the same here to provide the same good suggestions as we do for
- // literals above.
- lint.set_arg(
- "escaped",
- spans
- .into_iter()
- .map(|(c, _)| format!("{:?}", c))
- .collect::<Vec<String>>()
- .join(", "),
- );
- lint.note(fluent::suggestion_remove);
- lint.note(fluent::no_suggestion_note_escape);
- }
- lint
- },
+ HiddenUnicodeCodepointsDiag { label, count, span_label: span, labels, sub },
);
}
}
@@ -121,6 +90,7 @@ impl EarlyLintPass for HiddenUnicodeCodepoints {
}
}
+ #[inline]
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
// byte strings are already handled well enough by `EscapeError::NonAsciiCharInByteString`
match &expr.kind {
diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs
index 4f92661db..6cefaea2b 100644
--- a/compiler/rustc_lint/src/internal.rs
+++ b/compiler/rustc_lint/src/internal.rs
@@ -1,9 +1,12 @@
//! Some lints that are only useful in the compiler or crates that use compiler internals, such as
//! Clippy.
+use crate::lints::{
+ BadOptAccessDiag, DefaultHashTypesDiag, DiagOutOfImpl, LintPassByHand, NonExistantDocKeyword,
+ QueryInstability, TyQualified, TykindDiag, TykindKind, UntranslatableDiag,
+};
use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
use rustc_ast as ast;
-use rustc_errors::{fluent, Applicability};
use rustc_hir::def::Res;
use rustc_hir::{def_id::DefId, Expr, ExprKind, GenericArg, PatKind, Path, PathSegment, QPath};
use rustc_hir::{HirId, Impl, Item, ItemKind, Node, Pat, Ty, TyKind};
@@ -29,20 +32,15 @@ impl LateLintPass<'_> for DefaultHashTypes {
// don't lint imports, only actual usages
return;
}
- let replace = match cx.tcx.get_diagnostic_name(def_id) {
+ let preferred = match cx.tcx.get_diagnostic_name(def_id) {
Some(sym::HashMap) => "FxHashMap",
Some(sym::HashSet) => "FxHashSet",
_ => return,
};
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
DEFAULT_HASH_TYPES,
path.span,
- fluent::lint_default_hash_types,
- |lint| {
- lint.set_arg("preferred", replace)
- .set_arg("used", cx.tcx.item_name(def_id))
- .note(fluent::note)
- },
+ DefaultHashTypesDiag { preferred, used: cx.tcx.item_name(def_id) },
);
}
}
@@ -83,12 +81,11 @@ impl LateLintPass<'_> for QueryStability {
if let Ok(Some(instance)) = ty::Instance::resolve(cx.tcx, cx.param_env, def_id, substs) {
let def_id = instance.def_id();
if cx.tcx.has_attr(def_id, sym::rustc_lint_query_instability) {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
POTENTIAL_QUERY_INSTABILITY,
span,
- fluent::lint_query_instability,
- |lint| lint.set_arg("query", cx.tcx.item_name(def_id)).note(fluent::note),
- )
+ QueryInstability { query: cx.tcx.item_name(def_id) },
+ );
}
}
}
@@ -126,14 +123,8 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind {
let span = path.span.with_hi(
segment.args.map_or(segment.ident.span, |a| a.span_ext).hi()
);
- cx.struct_span_lint(USAGE_OF_TY_TYKIND, path.span, fluent::lint_tykind_kind, |lint| {
- lint
- .span_suggestion(
- span,
- fluent::suggestion,
- "ty",
- Applicability::MaybeIncorrect, // ty maybe needs an import
- )
+ cx.emit_spanned_lint(USAGE_OF_TY_TYKIND, path.span, TykindKind {
+ suggestion: span,
});
}
}
@@ -143,7 +134,7 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind {
TyKind::Path(QPath::Resolved(_, path)) => {
if lint_ty_kind_usage(cx, &path.res) {
let hir = cx.tcx.hir();
- let span = match hir.find(hir.get_parent_node(ty.hir_id)) {
+ let span = match hir.find_parent(ty.hir_id) {
Some(Node::Pat(Pat {
kind:
PatKind::Path(qpath)
@@ -190,39 +181,17 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind {
match span {
Some(span) => {
- cx.struct_span_lint(
- USAGE_OF_TY_TYKIND,
- path.span,
- fluent::lint_tykind_kind,
- |lint| lint.span_suggestion(
- span,
- fluent::suggestion,
- "ty",
- Applicability::MaybeIncorrect, // ty maybe needs an import
- )
- )
+ cx.emit_spanned_lint(USAGE_OF_TY_TYKIND, path.span, TykindKind {
+ suggestion: span,
+ });
},
- None => cx.struct_span_lint(
- USAGE_OF_TY_TYKIND,
- path.span,
- fluent::lint_tykind,
- |lint| lint.help(fluent::help)
- )
- }
- } else if !ty.span.from_expansion() && let Some(t) = is_ty_or_ty_ctxt(cx, &path) {
- if path.segments.len() > 1 {
- cx.struct_span_lint(USAGE_OF_QUALIFIED_TY, path.span, fluent::lint_ty_qualified, |lint| {
- lint
- .set_arg("ty", t.clone())
- .span_suggestion(
- path.span,
- fluent::suggestion,
- t,
- // The import probably needs to be changed
- Applicability::MaybeIncorrect,
- )
- })
+ None => cx.emit_spanned_lint(USAGE_OF_TY_TYKIND, path.span, TykindDiag),
}
+ } else if !ty.span.from_expansion() && path.segments.len() > 1 && let Some(ty) = is_ty_or_ty_ctxt(cx, &path) {
+ cx.emit_spanned_lint(USAGE_OF_QUALIFIED_TY, path.span, TyQualified {
+ ty,
+ suggestion: path.span,
+ });
}
}
_ => {}
@@ -303,12 +272,11 @@ impl EarlyLintPass for LintPassImpl {
&& call_site.ctxt().outer_expn_data().kind
!= ExpnKind::Macro(MacroKind::Bang, sym::declare_lint_pass)
{
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
LINT_PASS_IMPL_WITHOUT_MACRO,
lint_pass.path.span,
- fluent::lint_lintpass_by_hand,
- |lint| lint.help(fluent::help),
- )
+ LintPassByHand,
+ );
}
}
}
@@ -338,17 +306,16 @@ impl<'tcx> LateLintPass<'tcx> for ExistingDocKeyword {
if let Some(list) = attr.meta_item_list() {
for nested in list {
if nested.has_name(sym::keyword) {
- let v = nested
+ let keyword = nested
.value_str()
.expect("#[doc(keyword = \"...\")] expected a value!");
- if is_doc_keyword(v) {
+ if is_doc_keyword(keyword) {
return;
}
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
EXISTING_DOC_KEYWORD,
attr.span,
- fluent::lint_non_existant_doc_keyword,
- |lint| lint.set_arg("keyword", v).help(fluent::help),
+ NonExistantDocKeyword { keyword },
);
}
}
@@ -407,12 +374,7 @@ impl LateLintPass<'_> for Diagnostics {
}
debug!(?found_impl);
if !found_parent_with_attr && !found_impl {
- cx.struct_span_lint(
- DIAGNOSTIC_OUTSIDE_OF_IMPL,
- span,
- fluent::lint_diag_out_of_impl,
- |lint| lint,
- )
+ cx.emit_spanned_lint(DIAGNOSTIC_OUTSIDE_OF_IMPL, span, DiagOutOfImpl);
}
let mut found_diagnostic_message = false;
@@ -428,12 +390,7 @@ impl LateLintPass<'_> for Diagnostics {
}
debug!(?found_diagnostic_message);
if !found_parent_with_attr && !found_diagnostic_message {
- cx.struct_span_lint(
- UNTRANSLATABLE_DIAGNOSTIC,
- span,
- fluent::lint_untranslatable_diag,
- |lint| lint,
- )
+ cx.emit_spanned_lint(UNTRANSLATABLE_DIAGNOSTIC, span, UntranslatableDiag);
}
}
}
@@ -465,9 +422,9 @@ impl LateLintPass<'_> for BadOptAccess {
let Some(lit) = item.lit() &&
let ast::LitKind::Str(val, _) = lit.kind
{
- cx.struct_span_lint(BAD_OPT_ACCESS, expr.span, val.as_str(), |lint|
- lint
- );
+ cx.emit_spanned_lint(BAD_OPT_ACCESS, expr.span, BadOptAccessDiag {
+ msg: val.as_str(),
+ });
}
}
}
diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs
index 8a50cb1f1..b2a265674 100644
--- a/compiler/rustc_lint/src/late.rs
+++ b/compiler/rustc_lint/src/late.rs
@@ -23,6 +23,7 @@ use rustc_hir::intravisit as hir_visit;
use rustc_hir::intravisit::Visitor;
use rustc_middle::hir::nested_filter;
use rustc_middle::ty::{self, TyCtxt};
+use rustc_session::lint::LintPass;
use rustc_span::Span;
use std::any::Any;
@@ -36,17 +37,17 @@ pub fn unerased_lint_store(tcx: TyCtxt<'_>) -> &LintStore {
}
macro_rules! lint_callback { ($cx:expr, $f:ident, $($args:expr),*) => ({
- for pass in $cx.passes.iter_mut() {
- pass.$f(&$cx.context, $($args),*);
- }
+ $cx.pass.$f(&$cx.context, $($args),*);
}) }
-struct LateContextAndPasses<'tcx> {
+/// Implements the AST traversal for late lint passes. `T` provides the
+/// `check_*` methods.
+pub struct LateContextAndPass<'tcx, T: LateLintPass<'tcx>> {
context: LateContext<'tcx>,
- passes: Vec<LateLintPassObject<'tcx>>,
+ pass: T,
}
-impl<'tcx> LateContextAndPasses<'tcx> {
+impl<'tcx, T: LateLintPass<'tcx>> LateContextAndPass<'tcx, T> {
/// Merge the lints specified by any lint attributes into the
/// current lint context, call the provided function, then reset the
/// lints in effect to their previous state.
@@ -82,7 +83,7 @@ impl<'tcx> LateContextAndPasses<'tcx> {
}
}
-impl<'tcx> hir_visit::Visitor<'tcx> for LateContextAndPasses<'tcx> {
+impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPass<'tcx, T> {
type NestedFilter = nested_filter::All;
/// Because lints are scoped lexically, we want to walk nested
@@ -302,6 +303,35 @@ impl<'tcx> hir_visit::Visitor<'tcx> for LateContextAndPasses<'tcx> {
}
}
+// Combines multiple lint passes into a single pass, at runtime. Each
+// `check_foo` method in `$methods` within this pass simply calls `check_foo`
+// once per `$pass`. Compare with `declare_combined_late_lint_pass`, which is
+// similar, but combines lint passes at compile time.
+struct RuntimeCombinedLateLintPass<'a, 'tcx> {
+ passes: &'a mut [LateLintPassObject<'tcx>],
+}
+
+#[allow(rustc::lint_pass_impl_without_macro)]
+impl LintPass for RuntimeCombinedLateLintPass<'_, '_> {
+ fn name(&self) -> &'static str {
+ panic!()
+ }
+}
+
+macro_rules! impl_late_lint_pass {
+ ([], [$($(#[$attr:meta])* fn $f:ident($($param:ident: $arg:ty),*);)*]) => {
+ impl<'tcx> LateLintPass<'tcx> for RuntimeCombinedLateLintPass<'_, 'tcx> {
+ $(fn $f(&mut self, context: &LateContext<'tcx>, $($param: $arg),*) {
+ for pass in self.passes.iter_mut() {
+ pass.$f(context, $($param),*);
+ }
+ })*
+ }
+ };
+}
+
+crate::late_lint_methods!(impl_late_lint_pass, []);
+
pub(super) fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>(
tcx: TyCtxt<'tcx>,
module_def_id: LocalDefId,
@@ -319,11 +349,27 @@ pub(super) fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>(
only_module: true,
};
+ // Note: `passes` is often empty. In that case, it's faster to run
+ // `builtin_lints` directly rather than bundling it up into the
+ // `RuntimeCombinedLateLintPass`.
let mut passes: Vec<_> =
- unerased_lint_store(tcx).late_module_passes.iter().map(|pass| (pass)(tcx)).collect();
- passes.push(Box::new(builtin_lints));
+ unerased_lint_store(tcx).late_module_passes.iter().map(|mk_pass| (mk_pass)(tcx)).collect();
+ if passes.is_empty() {
+ late_lint_mod_inner(tcx, module_def_id, context, builtin_lints);
+ } else {
+ passes.push(Box::new(builtin_lints));
+ let pass = RuntimeCombinedLateLintPass { passes: &mut passes[..] };
+ late_lint_mod_inner(tcx, module_def_id, context, pass);
+ }
+}
- let mut cx = LateContextAndPasses { context, passes };
+fn late_lint_mod_inner<'tcx, T: LateLintPass<'tcx>>(
+ tcx: TyCtxt<'tcx>,
+ module_def_id: LocalDefId,
+ context: LateContext<'tcx>,
+ pass: T,
+) {
+ let mut cx = LateContextAndPass { context, pass };
let (module, _span, hir_id) = tcx.hir().get_module(module_def_id);
cx.process_mod(module, hir_id);
@@ -349,11 +395,26 @@ fn late_lint_crate<'tcx, T: LateLintPass<'tcx> + 'tcx>(tcx: TyCtxt<'tcx>, builti
only_module: false,
};
- let mut passes =
- unerased_lint_store(tcx).late_passes.iter().map(|p| (p)(tcx)).collect::<Vec<_>>();
- passes.push(Box::new(builtin_lints));
+ // Note: `passes` is often empty. In that case, it's faster to run
+ // `builtin_lints` directly rather than bundling it up into the
+ // `RuntimeCombinedLateLintPass`.
+ let mut passes: Vec<_> =
+ unerased_lint_store(tcx).late_passes.iter().map(|mk_pass| (mk_pass)(tcx)).collect();
+ if passes.is_empty() {
+ late_lint_crate_inner(tcx, context, builtin_lints);
+ } else {
+ passes.push(Box::new(builtin_lints));
+ let pass = RuntimeCombinedLateLintPass { passes: &mut passes[..] };
+ late_lint_crate_inner(tcx, context, pass);
+ }
+}
- let mut cx = LateContextAndPasses { context, passes };
+fn late_lint_crate_inner<'tcx, T: LateLintPass<'tcx>>(
+ tcx: TyCtxt<'tcx>,
+ context: LateContext<'tcx>,
+ pass: T,
+) {
+ let mut cx = LateContextAndPass { context, pass };
// Visit the whole crate.
cx.with_lint_attrs(hir::CRATE_HIR_ID, |cx| {
diff --git a/compiler/rustc_lint/src/let_underscore.rs b/compiler/rustc_lint/src/let_underscore.rs
index 04d844d21..b83a9665f 100644
--- a/compiler/rustc_lint/src/let_underscore.rs
+++ b/compiler/rustc_lint/src/let_underscore.rs
@@ -1,5 +1,8 @@
-use crate::{LateContext, LateLintPass, LintContext};
-use rustc_errors::{Applicability, DiagnosticBuilder, MultiSpan};
+use crate::{
+ lints::{NonBindingLet, NonBindingLetSub},
+ LateContext, LateLintPass, LintContext,
+};
+use rustc_errors::MultiSpan;
use rustc_hir as hir;
use rustc_middle::ty;
use rustc_span::Symbol;
@@ -119,6 +122,11 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
_ => false,
};
+ let sub = NonBindingLetSub {
+ suggestion: local.pat.span,
+ multi_suggestion_start: local.span.until(init.span),
+ multi_suggestion_end: init.span.shrink_to_hi(),
+ };
if is_sync_lock {
let mut span = MultiSpan::from_spans(vec![local.pat.span, init.span]);
span.push_span_label(
@@ -129,41 +137,14 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
init.span,
"this binding will immediately drop the value assigned to it".to_string(),
);
- cx.struct_span_lint(
- LET_UNDERSCORE_LOCK,
- span,
- "non-binding let on a synchronization lock",
- |lint| build_lint(lint, local, init.span),
- )
+ cx.emit_spanned_lint(LET_UNDERSCORE_LOCK, span, NonBindingLet::SyncLock { sub });
} else {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
LET_UNDERSCORE_DROP,
local.span,
- "non-binding let on a type that implements `Drop`",
- |lint| build_lint(lint, local, init.span),
- )
+ NonBindingLet::DropType { sub },
+ );
}
}
}
}
-
-fn build_lint<'a, 'b>(
- lint: &'a mut DiagnosticBuilder<'b, ()>,
- local: &hir::Local<'_>,
- init_span: rustc_span::Span,
-) -> &'a mut DiagnosticBuilder<'b, ()> {
- lint.span_suggestion_verbose(
- local.pat.span,
- "consider binding to an unused variable to avoid immediately dropping the value",
- "_unused",
- Applicability::MachineApplicable,
- )
- .multipart_suggestion(
- "consider immediately dropping the value",
- vec![
- (local.span.until(init_span), "drop(".to_string()),
- (init_span.shrink_to_hi(), ")".to_string()),
- ],
- Applicability::MachineApplicable,
- )
-}
diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs
index 847c356b8..cca36913d 100644
--- a/compiler/rustc_lint/src/levels.rs
+++ b/compiler/rustc_lint/src/levels.rs
@@ -1,9 +1,13 @@
use crate::context::{CheckLintNameResult, LintStore};
use crate::late::unerased_lint_store;
+use crate::lints::{
+ DeprecatedLintName, IgnoredUnlessCrateSpecified, OverruledAtributeLint, RenamedOrRemovedLint,
+ RenamedOrRemovedLintSuggestion, UnknownLint, UnknownLintSuggestion,
+};
use rustc_ast as ast;
use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, DiagnosticMessage, MultiSpan};
+use rustc_errors::{fluent, DecorateLint, DiagnosticBuilder, DiagnosticMessage, MultiSpan};
use rustc_hir as hir;
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::HirId;
@@ -15,6 +19,7 @@ use rustc_middle::lint::{
};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{RegisteredTools, TyCtxt};
+use rustc_session::lint::builtin::{RENAMED_AND_REMOVED_LINTS, UNKNOWN_LINTS, UNUSED_ATTRIBUTES};
use rustc_session::lint::{
builtin::{self, FORBIDDEN_LINT_GROUPS, SINGLE_USE_LIFETIMES, UNFULFILLED_LINT_EXPECTATIONS},
Level, Lint, LintExpectationId, LintId,
@@ -39,13 +44,13 @@ struct LintLevelSets {
}
rustc_index::newtype_index! {
+ #[custom_encodable] // we don't need encoding
struct LintStackIndex {
- ENCODABLE = custom, // we don't need encoding
- const COMMAND_LINE = 0,
+ const COMMAND_LINE = 0;
}
}
-/// Specifications found at this position in the stack. This map only represents the lints
+/// Specifications found at this position in the stack. This map only represents the lints
/// found for one set of attributes (like `shallow_lint_levels_on` does).
///
/// We store the level specifications as a linked list.
@@ -158,7 +163,7 @@ fn shallow_lint_levels_on(tcx: TyCtxt<'_>, owner: hir::OwnerId) -> ShallowLintLe
match attrs.map.range(..) {
// There is only something to do if there are attributes at all.
[] => {}
- // Most of the time, there is only one attribute. Avoid fetching HIR in that case.
+ // Most of the time, there is only one attribute. Avoid fetching HIR in that case.
[(local_id, _)] => levels.add_id(HirId { owner, local_id: *local_id }),
// Otherwise, we need to visit the attributes in source code order, so we fetch HIR and do
// a standard visit.
@@ -583,57 +588,32 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
old_src,
id_name
);
-
- let decorate_diag = |diag: &mut Diagnostic| {
- diag.span_label(src.span(), "overruled by previous forbid");
- match old_src {
- LintLevelSource::Default => {
- diag.note(&format!(
- "`forbid` lint level is the default for {}",
- id.to_string()
- ));
- }
- LintLevelSource::Node { span, reason, .. } => {
- diag.span_label(span, "`forbid` level set here");
- if let Some(rationale) = reason {
- diag.note(rationale.as_str());
- }
- }
- LintLevelSource::CommandLine(_, _) => {
- diag.note("`forbid` lint level was set on command line");
- }
+ let sub = match old_src {
+ LintLevelSource::Default => {
+ OverruledAttributeSub::DefaultSource { id: id.to_string() }
}
+ LintLevelSource::Node { span, reason, .. } => {
+ OverruledAttributeSub::NodeSource { span, reason }
+ }
+ LintLevelSource::CommandLine(_, _) => OverruledAttributeSub::CommandLineSource,
};
if !fcw_warning {
self.sess.emit_err(OverruledAttribute {
span: src.span(),
overruled: src.span(),
- lint_level: level.as_str().to_string(),
+ lint_level: level.as_str(),
lint_source: src.name(),
- sub: match old_src {
- LintLevelSource::Default => {
- OverruledAttributeSub::DefaultSource { id: id.to_string() }
- }
- LintLevelSource::Node { span, reason, .. } => {
- OverruledAttributeSub::NodeSource { span, reason }
- }
- LintLevelSource::CommandLine(_, _) => {
- OverruledAttributeSub::CommandLineSource
- }
- },
+ sub,
});
} else {
- self.struct_lint(
+ self.emit_spanned_lint(
FORBIDDEN_LINT_GROUPS,
- Some(src.span().into()),
- format!(
- "{}({}) incompatible with previous forbid",
- level.as_str(),
- src.name(),
- ),
- |lint| {
- decorate_diag(lint);
- lint
+ src.span().into(),
+ OverruledAtributeLint {
+ overruled: src.span(),
+ lint_level: level.as_str(),
+ lint_source: src.name(),
+ sub,
},
);
}
@@ -858,25 +838,13 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
}
Err((Some(ids), ref new_lint_name)) => {
let lint = builtin::RENAMED_AND_REMOVED_LINTS;
- let (lvl, src) = self.provider.get_lint_level(lint, &sess);
- struct_lint_level(
- self.sess,
+ self.emit_spanned_lint(
lint,
- lvl,
- src,
- Some(sp.into()),
- format!(
- "lint name `{}` is deprecated \
- and may not have an effect in the future.",
- name
- ),
- |lint| {
- lint.span_suggestion(
- sp,
- "change it to",
- new_lint_name,
- Applicability::MachineApplicable,
- )
+ sp.into(),
+ DeprecatedLintName {
+ name,
+ suggestion: sp,
+ replace: &new_lint_name,
},
);
@@ -917,54 +885,29 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
_ if !self.warn_about_weird_lints => {}
CheckLintNameResult::Warning(msg, renamed) => {
- let lint = builtin::RENAMED_AND_REMOVED_LINTS;
- let (renamed_lint_level, src) = self.provider.get_lint_level(lint, &sess);
- struct_lint_level(
- self.sess,
- lint,
- renamed_lint_level,
- src,
- Some(sp.into()),
- msg,
- |lint| {
- if let Some(new_name) = &renamed {
- lint.span_suggestion(
- sp,
- "use the new name",
- new_name,
- Applicability::MachineApplicable,
- );
- }
- lint
- },
+ let suggestion =
+ renamed.as_ref().map(|replace| RenamedOrRemovedLintSuggestion {
+ suggestion: sp,
+ replace: replace.as_str(),
+ });
+ self.emit_spanned_lint(
+ RENAMED_AND_REMOVED_LINTS,
+ sp.into(),
+ RenamedOrRemovedLint { msg, suggestion },
);
}
CheckLintNameResult::NoLint(suggestion) => {
- let lint = builtin::UNKNOWN_LINTS;
- let (level, src) = self.provider.get_lint_level(lint, self.sess);
let name = if let Some(tool_ident) = tool_ident {
format!("{}::{}", tool_ident.name, name)
} else {
name.to_string()
};
- struct_lint_level(
- self.sess,
- lint,
- level,
- src,
- Some(sp.into()),
- format!("unknown lint: `{}`", name),
- |lint| {
- if let Some(suggestion) = suggestion {
- lint.span_suggestion(
- sp,
- "did you mean",
- suggestion,
- Applicability::MaybeIncorrect,
- );
- }
- lint
- },
+ let suggestion = suggestion
+ .map(|replace| UnknownLintSuggestion { suggestion: sp, replace });
+ self.emit_spanned_lint(
+ UNKNOWN_LINTS,
+ sp.into(),
+ UnknownLint { name, suggestion },
);
}
}
@@ -1010,20 +953,10 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
continue
};
- let lint = builtin::UNUSED_ATTRIBUTES;
- let (lint_level, lint_src) = self.provider.get_lint_level(lint, &self.sess);
- struct_lint_level(
- self.sess,
- lint,
- lint_level,
- lint_src,
- Some(lint_attr_span.into()),
- format!(
- "{}({}) is ignored unless specified at crate level",
- level.as_str(),
- lint_attr_name
- ),
- |lint| lint,
+ self.emit_spanned_lint(
+ UNUSED_ATTRIBUTES,
+ lint_attr_span.into(),
+ IgnoredUnlessCrateSpecified { level: level.as_str(), name: lint_attr_name },
);
// don't set a separate error for every lint in the group
break;
@@ -1047,11 +980,10 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
level,
src,
Some(span.into()),
- format!("unknown lint: `{}`", lint_id.lint.name_lower()),
+ fluent::lint_unknown_gated_lint,
|lint| {
- lint.note(
- &format!("the `{}` lint is unstable", lint_id.lint.name_lower(),),
- );
+ lint.set_arg("name", lint_id.lint.name_lower());
+ lint.note(fluent::note);
add_feature_diagnostics(lint, &self.sess.parse_sess, feature);
lint
},
@@ -1086,6 +1018,25 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
let (level, src) = self.lint_level(lint);
struct_lint_level(self.sess, lint, level, src, span, msg, decorate)
}
+
+ pub fn emit_spanned_lint(
+ &self,
+ lint: &'static Lint,
+ span: MultiSpan,
+ decorate: impl for<'a> DecorateLint<'a, ()>,
+ ) {
+ let (level, src) = self.lint_level(lint);
+ struct_lint_level(self.sess, lint, level, src, Some(span), decorate.msg(), |lint| {
+ decorate.decorate_lint(lint)
+ });
+ }
+
+ pub fn emit_lint(&self, lint: &'static Lint, decorate: impl for<'a> DecorateLint<'a, ()>) {
+ let (level, src) = self.lint_level(lint);
+ struct_lint_level(self.sess, lint, level, src, None, decorate.msg(), |lint| {
+ decorate.decorate_lint(lint)
+ });
+ }
}
pub(crate) fn provide(providers: &mut Providers) {
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index 771628553..d6be4da03 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -38,6 +38,8 @@
#![feature(never_type)]
#![feature(rustc_attrs)]
#![recursion_limit = "256"]
+#![deny(rustc::untranslatable_diagnostic)]
+#![deny(rustc::diagnostic_outside_of_impl)]
#[macro_use]
extern crate rustc_middle;
@@ -60,6 +62,7 @@ mod internal;
mod late;
mod let_underscore;
mod levels;
+mod lints;
mod methods;
mod non_ascii_idents;
mod non_fmt_panic;
@@ -142,7 +145,7 @@ early_lint_methods!(
[
pub BuiltinCombinedEarlyLintPass,
[
- UnusedParens: UnusedParens,
+ UnusedParens: UnusedParens::new(),
UnusedBraces: UnusedBraces,
UnusedImportBraces: UnusedImportBraces,
UnsafeCode: UnsafeCode,
@@ -162,7 +165,8 @@ early_lint_methods!(
]
);
-// FIXME: Make a separate lint type which do not require typeck tables
+// FIXME: Make a separate lint type which does not require typeck tables.
+
late_lint_methods!(
declare_combined_late_lint_pass,
[
@@ -179,8 +183,7 @@ late_lint_methods!(
// Keeps a global list of foreign declarations.
ClashingExternDeclarations: ClashingExternDeclarations::new(),
]
- ],
- ['tcx]
+ ]
);
late_lint_methods!(
@@ -230,8 +233,7 @@ late_lint_methods!(
NamedAsmLabels: NamedAsmLabels,
OpaqueHiddenInferredBound: OpaqueHiddenInferredBound,
]
- ],
- ['tcx]
+ ]
);
pub fn new_lint_store(internal_lints: bool) -> LintStore {
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
new file mode 100644
index 000000000..329ece28e
--- /dev/null
+++ b/compiler/rustc_lint/src/lints.rs
@@ -0,0 +1,1478 @@
+#![allow(rustc::untranslatable_diagnostic)]
+#![allow(rustc::diagnostic_outside_of_impl)]
+use std::num::NonZeroU32;
+
+use rustc_errors::{
+ fluent, AddToDiagnostic, Applicability, DecorateLint, DiagnosticMessage,
+ DiagnosticStyledString, SuggestionStyle,
+};
+use rustc_hir::def_id::DefId;
+use rustc_macros::{LintDiagnostic, Subdiagnostic};
+use rustc_middle::ty::{PolyExistentialTraitRef, Predicate, Ty, TyCtxt};
+use rustc_session::parse::ParseSess;
+use rustc_span::{edition::Edition, sym, symbol::Ident, Span, Symbol};
+
+use crate::{
+ builtin::InitError, builtin::TypeAliasBounds, errors::OverruledAttributeSub, LateContext,
+};
+
+// array_into_iter.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_array_into_iter)]
+pub struct ArrayIntoIterDiag<'a> {
+ pub target: &'a str,
+ #[suggestion(use_iter_suggestion, code = "iter", applicability = "machine-applicable")]
+ pub suggestion: Span,
+ #[subdiagnostic]
+ pub sub: Option<ArrayIntoIterDiagSub>,
+}
+
+#[derive(Subdiagnostic)]
+pub enum ArrayIntoIterDiagSub {
+ #[suggestion(remove_into_iter_suggestion, code = "", applicability = "maybe-incorrect")]
+ RemoveIntoIter {
+ #[primary_span]
+ span: Span,
+ },
+ #[multipart_suggestion(use_explicit_into_iter_suggestion, applicability = "maybe-incorrect")]
+ UseExplicitIntoIter {
+ #[suggestion_part(code = "IntoIterator::into_iter(")]
+ start_span: Span,
+ #[suggestion_part(code = ")")]
+ end_span: Span,
+ },
+}
+
+// builtin.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_while_true)]
+pub struct BuiltinWhileTrue {
+ #[suggestion(style = "short", code = "{replace}", applicability = "machine-applicable")]
+ pub suggestion: Span,
+ pub replace: String,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_box_pointers)]
+pub struct BuiltinBoxPointers<'a> {
+ pub ty: Ty<'a>,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_non_shorthand_field_patterns)]
+pub struct BuiltinNonShorthandFieldPatterns {
+ pub ident: Ident,
+ #[suggestion(code = "{prefix}{ident}", applicability = "machine-applicable")]
+ pub suggestion: Span,
+ pub prefix: &'static str,
+}
+
+#[derive(LintDiagnostic)]
+pub enum BuiltinUnsafe {
+ #[diag(lint_builtin_allow_internal_unsafe)]
+ AllowInternalUnsafe,
+ #[diag(lint_builtin_unsafe_block)]
+ UnsafeBlock,
+ #[diag(lint_builtin_unsafe_trait)]
+ UnsafeTrait,
+ #[diag(lint_builtin_unsafe_impl)]
+ UnsafeImpl,
+ #[diag(lint_builtin_no_mangle_fn)]
+ #[note(lint_builtin_overridden_symbol_name)]
+ NoMangleFn,
+ #[diag(lint_builtin_export_name_fn)]
+ #[note(lint_builtin_overridden_symbol_name)]
+ ExportNameFn,
+ #[diag(lint_builtin_link_section_fn)]
+ #[note(lint_builtin_overridden_symbol_section)]
+ LinkSectionFn,
+ #[diag(lint_builtin_no_mangle_static)]
+ #[note(lint_builtin_overridden_symbol_name)]
+ NoMangleStatic,
+ #[diag(lint_builtin_export_name_static)]
+ #[note(lint_builtin_overridden_symbol_name)]
+ ExportNameStatic,
+ #[diag(lint_builtin_link_section_static)]
+ #[note(lint_builtin_overridden_symbol_section)]
+ LinkSectionStatic,
+ #[diag(lint_builtin_no_mangle_method)]
+ #[note(lint_builtin_overridden_symbol_name)]
+ NoMangleMethod,
+ #[diag(lint_builtin_export_name_method)]
+ #[note(lint_builtin_overridden_symbol_name)]
+ ExportNameMethod,
+ #[diag(lint_builtin_decl_unsafe_fn)]
+ DeclUnsafeFn,
+ #[diag(lint_builtin_decl_unsafe_method)]
+ DeclUnsafeMethod,
+ #[diag(lint_builtin_impl_unsafe_method)]
+ ImplUnsafeMethod,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_missing_doc)]
+pub struct BuiltinMissingDoc<'a> {
+ pub article: &'a str,
+ pub desc: &'a str,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_missing_copy_impl)]
+pub struct BuiltinMissingCopyImpl;
+
+pub struct BuiltinMissingDebugImpl<'a> {
+ pub tcx: TyCtxt<'a>,
+ pub def_id: DefId,
+}
+
+// Needed for def_path_str
+impl<'a> DecorateLint<'a, ()> for BuiltinMissingDebugImpl<'_> {
+ fn decorate_lint<'b>(
+ self,
+ diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>,
+ ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
+ diag.set_arg("debug", self.tcx.def_path_str(self.def_id));
+ diag
+ }
+
+ fn msg(&self) -> DiagnosticMessage {
+ fluent::lint_builtin_missing_debug_impl
+ }
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_anonymous_params)]
+pub struct BuiltinAnonymousParams<'a> {
+ #[suggestion(code = "_: {ty_snip}")]
+ pub suggestion: (Span, Applicability),
+ pub ty_snip: &'a str,
+}
+
+// FIXME(davidtwco) translatable deprecated attr
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_deprecated_attr_link)]
+pub struct BuiltinDeprecatedAttrLink<'a> {
+ pub name: Symbol,
+ pub reason: &'a str,
+ pub link: &'a str,
+ #[subdiagnostic]
+ pub suggestion: BuiltinDeprecatedAttrLinkSuggestion<'a>,
+}
+
+#[derive(Subdiagnostic)]
+pub enum BuiltinDeprecatedAttrLinkSuggestion<'a> {
+ #[suggestion(msg_suggestion, code = "", applicability = "machine-applicable")]
+ Msg {
+ #[primary_span]
+ suggestion: Span,
+ msg: &'a str,
+ },
+ #[suggestion(default_suggestion, code = "", applicability = "machine-applicable")]
+ Default {
+ #[primary_span]
+ suggestion: Span,
+ },
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_deprecated_attr_used)]
+pub struct BuiltinDeprecatedAttrUsed {
+ pub name: String,
+ #[suggestion(
+ lint_builtin_deprecated_attr_default_suggestion,
+ style = "short",
+ code = "",
+ applicability = "machine-applicable"
+ )]
+ pub suggestion: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_unused_doc_comment)]
+pub struct BuiltinUnusedDocComment<'a> {
+ pub kind: &'a str,
+ #[label]
+ pub label: Span,
+ #[subdiagnostic]
+ pub sub: BuiltinUnusedDocCommentSub,
+}
+
+#[derive(Subdiagnostic)]
+pub enum BuiltinUnusedDocCommentSub {
+ #[help(plain_help)]
+ PlainHelp,
+ #[help(block_help)]
+ BlockHelp,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_no_mangle_generic)]
+pub struct BuiltinNoMangleGeneric {
+ // Use of `#[no_mangle]` suggests FFI intent; correct
+ // fix may be to monomorphize source by hand
+ #[suggestion(style = "short", code = "", applicability = "maybe-incorrect")]
+ pub suggestion: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_const_no_mangle)]
+pub struct BuiltinConstNoMangle {
+ #[suggestion(code = "pub static", applicability = "machine-applicable")]
+ pub suggestion: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_mutable_transmutes)]
+pub struct BuiltinMutablesTransmutes;
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_unstable_features)]
+pub struct BuiltinUnstableFeatures;
+
+// lint_ungated_async_fn_track_caller
+pub struct BuiltinUngatedAsyncFnTrackCaller<'a> {
+ pub label: Span,
+ pub parse_sess: &'a ParseSess,
+}
+
+impl<'a> DecorateLint<'a, ()> for BuiltinUngatedAsyncFnTrackCaller<'_> {
+ fn decorate_lint<'b>(
+ self,
+ diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>,
+ ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
+ diag.span_label(self.label, fluent::label);
+ rustc_session::parse::add_feature_diagnostics(
+ diag,
+ &self.parse_sess,
+ sym::closure_track_caller,
+ );
+ diag
+ }
+
+ fn msg(&self) -> DiagnosticMessage {
+ fluent::lint_ungated_async_fn_track_caller
+ }
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_unreachable_pub)]
+pub struct BuiltinUnreachablePub<'a> {
+ pub what: &'a str,
+ #[suggestion(code = "pub(crate)")]
+ pub suggestion: (Span, Applicability),
+ #[help]
+ pub help: Option<()>,
+}
+
+pub struct SuggestChangingAssocTypes<'a, 'b> {
+ pub ty: &'a rustc_hir::Ty<'b>,
+}
+
+impl AddToDiagnostic for SuggestChangingAssocTypes<'_, '_> {
+ fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F)
+ where
+ F: Fn(
+ &mut rustc_errors::Diagnostic,
+ rustc_errors::SubdiagnosticMessage,
+ ) -> rustc_errors::SubdiagnosticMessage,
+ {
+ // Access to associates types should use `<T as Bound>::Assoc`, which does not need a
+ // bound. Let's see if this type does that.
+
+ // We use a HIR visitor to walk the type.
+ use rustc_hir::intravisit::{self, Visitor};
+ struct WalkAssocTypes<'a> {
+ err: &'a mut rustc_errors::Diagnostic,
+ }
+ impl Visitor<'_> for WalkAssocTypes<'_> {
+ fn visit_qpath(
+ &mut self,
+ qpath: &rustc_hir::QPath<'_>,
+ id: rustc_hir::HirId,
+ span: Span,
+ ) {
+ if TypeAliasBounds::is_type_variable_assoc(qpath) {
+ self.err.span_help(span, fluent::lint_builtin_type_alias_bounds_help);
+ }
+ intravisit::walk_qpath(self, qpath, id)
+ }
+ }
+
+ // Let's go for a walk!
+ let mut visitor = WalkAssocTypes { err: diag };
+ visitor.visit_ty(self.ty);
+ }
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_type_alias_where_clause)]
+pub struct BuiltinTypeAliasWhereClause<'a, 'b> {
+ #[suggestion(code = "", applicability = "machine-applicable")]
+ pub suggestion: Span,
+ #[subdiagnostic]
+ pub sub: Option<SuggestChangingAssocTypes<'a, 'b>>,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_type_alias_generic_bounds)]
+pub struct BuiltinTypeAliasGenericBounds<'a, 'b> {
+ #[subdiagnostic]
+ pub suggestion: BuiltinTypeAliasGenericBoundsSuggestion,
+ #[subdiagnostic]
+ pub sub: Option<SuggestChangingAssocTypes<'a, 'b>>,
+}
+
+pub struct BuiltinTypeAliasGenericBoundsSuggestion {
+ pub suggestions: Vec<(Span, String)>,
+}
+
+impl AddToDiagnostic for BuiltinTypeAliasGenericBoundsSuggestion {
+ fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F)
+ where
+ F: Fn(
+ &mut rustc_errors::Diagnostic,
+ rustc_errors::SubdiagnosticMessage,
+ ) -> rustc_errors::SubdiagnosticMessage,
+ {
+ diag.multipart_suggestion(
+ fluent::suggestion,
+ self.suggestions,
+ Applicability::MachineApplicable,
+ );
+ }
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_trivial_bounds)]
+pub struct BuiltinTrivialBounds<'a> {
+ pub predicate_kind_name: &'a str,
+ pub predicate: Predicate<'a>,
+}
+
+#[derive(LintDiagnostic)]
+pub enum BuiltinEllipsisInclusiveRangePatternsLint {
+ #[diag(lint_builtin_ellipsis_inclusive_range_patterns)]
+ Parenthesise {
+ #[suggestion(code = "{replace}", applicability = "machine-applicable")]
+ suggestion: Span,
+ replace: String,
+ },
+ #[diag(lint_builtin_ellipsis_inclusive_range_patterns)]
+ NonParenthesise {
+ #[suggestion(style = "short", code = "..=", applicability = "machine-applicable")]
+ suggestion: Span,
+ },
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_unnameable_test_items)]
+pub struct BuiltinUnnameableTestItems;
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_keyword_idents)]
+pub struct BuiltinKeywordIdents {
+ pub kw: Ident,
+ pub next: Edition,
+ #[suggestion(code = "r#{kw}", applicability = "machine-applicable")]
+ pub suggestion: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_explicit_outlives)]
+pub struct BuiltinExplicitOutlives {
+ pub count: usize,
+ #[subdiagnostic]
+ pub suggestion: BuiltinExplicitOutlivesSuggestion,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(suggestion)]
+pub struct BuiltinExplicitOutlivesSuggestion {
+ #[suggestion_part(code = "")]
+ pub spans: Vec<Span>,
+ #[applicability]
+ pub applicability: Applicability,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_incomplete_features)]
+pub struct BuiltinIncompleteFeatures {
+ pub name: Symbol,
+ #[subdiagnostic]
+ pub note: Option<BuiltinIncompleteFeaturesNote>,
+ #[subdiagnostic]
+ pub help: Option<BuiltinIncompleteFeaturesHelp>,
+}
+
+#[derive(Subdiagnostic)]
+#[help(help)]
+pub struct BuiltinIncompleteFeaturesHelp;
+
+#[derive(Subdiagnostic)]
+#[note(note)]
+pub struct BuiltinIncompleteFeaturesNote {
+ pub n: NonZeroU32,
+}
+
+pub struct BuiltinUnpermittedTypeInit<'a> {
+ pub msg: DiagnosticMessage,
+ pub ty: Ty<'a>,
+ pub label: Span,
+ pub sub: BuiltinUnpermittedTypeInitSub,
+}
+
+impl<'a> DecorateLint<'a, ()> for BuiltinUnpermittedTypeInit<'_> {
+ fn decorate_lint<'b>(
+ self,
+ diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>,
+ ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
+ diag.set_arg("ty", self.ty);
+ diag.span_label(self.label, fluent::lint_builtin_unpermitted_type_init_label);
+ diag.span_label(self.label, fluent::lint_builtin_unpermitted_type_init_label_suggestion);
+ self.sub.add_to_diagnostic(diag);
+ diag
+ }
+
+ fn msg(&self) -> rustc_errors::DiagnosticMessage {
+ self.msg.clone()
+ }
+}
+
+// FIXME(davidtwco): make translatable
+pub struct BuiltinUnpermittedTypeInitSub {
+ pub err: InitError,
+}
+
+impl AddToDiagnostic for BuiltinUnpermittedTypeInitSub {
+ fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F)
+ where
+ F: Fn(
+ &mut rustc_errors::Diagnostic,
+ rustc_errors::SubdiagnosticMessage,
+ ) -> rustc_errors::SubdiagnosticMessage,
+ {
+ let mut err = self.err;
+ loop {
+ if let Some(span) = err.span {
+ diag.span_note(span, err.message);
+ } else {
+ diag.note(err.message);
+ }
+ if let Some(e) = err.nested {
+ err = *e;
+ } else {
+ break;
+ }
+ }
+ }
+}
+
+#[derive(LintDiagnostic)]
+pub enum BuiltinClashingExtern<'a> {
+ #[diag(lint_builtin_clashing_extern_same_name)]
+ SameName {
+ this: Symbol,
+ orig: Symbol,
+ #[label(previous_decl_label)]
+ previous_decl_label: Span,
+ #[label(mismatch_label)]
+ mismatch_label: Span,
+ #[subdiagnostic]
+ sub: BuiltinClashingExternSub<'a>,
+ },
+ #[diag(lint_builtin_clashing_extern_diff_name)]
+ DiffName {
+ this: Symbol,
+ orig: Symbol,
+ #[label(previous_decl_label)]
+ previous_decl_label: Span,
+ #[label(mismatch_label)]
+ mismatch_label: Span,
+ #[subdiagnostic]
+ sub: BuiltinClashingExternSub<'a>,
+ },
+}
+
+// FIXME(davidtwco): translatable expected/found
+pub struct BuiltinClashingExternSub<'a> {
+ pub tcx: TyCtxt<'a>,
+ pub expected: Ty<'a>,
+ pub found: Ty<'a>,
+}
+
+impl AddToDiagnostic for BuiltinClashingExternSub<'_> {
+ fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F)
+ where
+ F: Fn(
+ &mut rustc_errors::Diagnostic,
+ rustc_errors::SubdiagnosticMessage,
+ ) -> rustc_errors::SubdiagnosticMessage,
+ {
+ let mut expected_str = DiagnosticStyledString::new();
+ expected_str.push(self.expected.fn_sig(self.tcx).to_string(), false);
+ let mut found_str = DiagnosticStyledString::new();
+ found_str.push(self.found.fn_sig(self.tcx).to_string(), true);
+ diag.note_expected_found(&"", expected_str, &"", found_str);
+ }
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_deref_nullptr)]
+pub struct BuiltinDerefNullptr {
+ #[label]
+ pub label: Span,
+}
+
+// FIXME: migrate fluent::lint::builtin_asm_labels
+
+#[derive(LintDiagnostic)]
+pub enum BuiltinSpecialModuleNameUsed {
+ #[diag(lint_builtin_special_module_name_used_lib)]
+ #[note]
+ #[help]
+ Lib,
+ #[diag(lint_builtin_special_module_name_used_main)]
+ #[note]
+ Main,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_unexpected_cli_config_name)]
+#[help]
+pub struct BuiltinUnexpectedCliConfigName {
+ pub name: Symbol,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_unexpected_cli_config_value)]
+#[help]
+pub struct BuiltinUnexpectedCliConfigValue {
+ pub name: Symbol,
+ pub value: Symbol,
+}
+
+// deref_into_dyn_supertrait.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_supertrait_as_deref_target)]
+pub struct SupertraitAsDerefTarget<'a> {
+ pub t: Ty<'a>,
+ pub target_principal: PolyExistentialTraitRef<'a>,
+ #[subdiagnostic]
+ pub label: Option<SupertraitAsDerefTargetLabel>,
+}
+
+#[derive(Subdiagnostic)]
+#[label(label)]
+pub struct SupertraitAsDerefTargetLabel {
+ #[primary_span]
+ pub label: Span,
+}
+
+// enum_intrinsics_non_enums.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_enum_intrinsics_mem_discriminant)]
+pub struct EnumIntrinsicsMemDiscriminate<'a> {
+ pub ty_param: Ty<'a>,
+ #[note]
+ pub note: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_enum_intrinsics_mem_variant)]
+#[note]
+pub struct EnumIntrinsicsMemVariant<'a> {
+ pub ty_param: Ty<'a>,
+}
+
+// expect.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_expectation)]
+pub struct Expectation {
+ #[subdiagnostic]
+ pub rationale: Option<ExpectationNote>,
+ #[note]
+ pub note: Option<()>,
+}
+
+#[derive(Subdiagnostic)]
+#[note(rationale)]
+pub struct ExpectationNote {
+ pub rationale: Symbol,
+}
+
+// for_loops_over_fallibles.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_for_loops_over_fallibles)]
+pub struct ForLoopsOverFalliblesDiag<'a> {
+ pub article: &'static str,
+ pub ty: &'static str,
+ #[subdiagnostic]
+ pub sub: ForLoopsOverFalliblesLoopSub<'a>,
+ #[subdiagnostic]
+ pub question_mark: Option<ForLoopsOverFalliblesQuestionMark>,
+ #[subdiagnostic]
+ pub suggestion: ForLoopsOverFalliblesSuggestion<'a>,
+}
+
+#[derive(Subdiagnostic)]
+pub enum ForLoopsOverFalliblesLoopSub<'a> {
+ #[suggestion(remove_next, code = ".by_ref()", applicability = "maybe-incorrect")]
+ RemoveNext {
+ #[primary_span]
+ suggestion: Span,
+ recv_snip: String,
+ },
+ #[multipart_suggestion(use_while_let, applicability = "maybe-incorrect")]
+ UseWhileLet {
+ #[suggestion_part(code = "while let {var}(")]
+ start_span: Span,
+ #[suggestion_part(code = ") = ")]
+ end_span: Span,
+ var: &'a str,
+ },
+}
+
+#[derive(Subdiagnostic)]
+#[suggestion(use_question_mark, code = "?", applicability = "maybe-incorrect")]
+pub struct ForLoopsOverFalliblesQuestionMark {
+ #[primary_span]
+ pub suggestion: Span,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(suggestion, applicability = "maybe-incorrect")]
+pub struct ForLoopsOverFalliblesSuggestion<'a> {
+ pub var: &'a str,
+ #[suggestion_part(code = "if let {var}(")]
+ pub start_span: Span,
+ #[suggestion_part(code = ") = ")]
+ pub end_span: Span,
+}
+
+// hidden_unicode_codepoints.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_hidden_unicode_codepoints)]
+#[note]
+pub struct HiddenUnicodeCodepointsDiag<'a> {
+ pub label: &'a str,
+ pub count: usize,
+ #[label]
+ pub span_label: Span,
+ #[subdiagnostic]
+ pub labels: Option<HiddenUnicodeCodepointsDiagLabels>,
+ #[subdiagnostic]
+ pub sub: HiddenUnicodeCodepointsDiagSub,
+}
+
+pub struct HiddenUnicodeCodepointsDiagLabels {
+ pub spans: Vec<(char, Span)>,
+}
+
+impl AddToDiagnostic for HiddenUnicodeCodepointsDiagLabels {
+ fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F)
+ where
+ F: Fn(
+ &mut rustc_errors::Diagnostic,
+ rustc_errors::SubdiagnosticMessage,
+ ) -> rustc_errors::SubdiagnosticMessage,
+ {
+ for (c, span) in self.spans {
+ diag.span_label(span, format!("{:?}", c));
+ }
+ }
+}
+
+pub enum HiddenUnicodeCodepointsDiagSub {
+ Escape { spans: Vec<(char, Span)> },
+ NoEscape { spans: Vec<(char, Span)> },
+}
+
+// Used because of multiple multipart_suggestion and note
+impl AddToDiagnostic for HiddenUnicodeCodepointsDiagSub {
+ fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F)
+ where
+ F: Fn(
+ &mut rustc_errors::Diagnostic,
+ rustc_errors::SubdiagnosticMessage,
+ ) -> rustc_errors::SubdiagnosticMessage,
+ {
+ match self {
+ HiddenUnicodeCodepointsDiagSub::Escape { spans } => {
+ diag.multipart_suggestion_with_style(
+ fluent::suggestion_remove,
+ spans.iter().map(|(_, span)| (*span, "".to_string())).collect(),
+ Applicability::MachineApplicable,
+ SuggestionStyle::HideCodeAlways,
+ );
+ diag.multipart_suggestion(
+ fluent::suggestion_escape,
+ spans
+ .into_iter()
+ .map(|(c, span)| {
+ let c = format!("{:?}", c);
+ (span, c[1..c.len() - 1].to_string())
+ })
+ .collect(),
+ Applicability::MachineApplicable,
+ );
+ }
+ HiddenUnicodeCodepointsDiagSub::NoEscape { spans } => {
+ // FIXME: in other suggestions we've reversed the inner spans of doc comments. We
+ // should do the same here to provide the same good suggestions as we do for
+ // literals above.
+ diag.set_arg(
+ "escaped",
+ spans
+ .into_iter()
+ .map(|(c, _)| format!("{:?}", c))
+ .collect::<Vec<String>>()
+ .join(", "),
+ );
+ diag.note(fluent::suggestion_remove);
+ diag.note(fluent::no_suggestion_note_escape);
+ }
+ }
+ }
+}
+
+// internal.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_default_hash_types)]
+#[note]
+pub struct DefaultHashTypesDiag<'a> {
+ pub preferred: &'a str,
+ pub used: Symbol,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_query_instability)]
+#[note]
+pub struct QueryInstability {
+ pub query: Symbol,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_tykind_kind)]
+pub struct TykindKind {
+ #[suggestion(code = "ty", applicability = "maybe-incorrect")]
+ pub suggestion: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_tykind)]
+#[help]
+pub struct TykindDiag;
+
+#[derive(LintDiagnostic)]
+#[diag(lint_ty_qualified)]
+pub struct TyQualified {
+ pub ty: String,
+ #[suggestion(code = "{ty}", applicability = "maybe-incorrect")]
+ pub suggestion: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_lintpass_by_hand)]
+#[help]
+pub struct LintPassByHand;
+
+#[derive(LintDiagnostic)]
+#[diag(lint_non_existant_doc_keyword)]
+#[help]
+pub struct NonExistantDocKeyword {
+ pub keyword: Symbol,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_diag_out_of_impl)]
+pub struct DiagOutOfImpl;
+
+#[derive(LintDiagnostic)]
+#[diag(lint_untranslatable_diag)]
+pub struct UntranslatableDiag;
+
+#[derive(LintDiagnostic)]
+#[diag(lint_bad_opt_access)]
+pub struct BadOptAccessDiag<'a> {
+ pub msg: &'a str,
+}
+
+// let_underscore.rs
+#[derive(LintDiagnostic)]
+pub enum NonBindingLet {
+ #[diag(lint_non_binding_let_on_sync_lock)]
+ SyncLock {
+ #[subdiagnostic]
+ sub: NonBindingLetSub,
+ },
+ #[diag(lint_non_binding_let_on_drop_type)]
+ DropType {
+ #[subdiagnostic]
+ sub: NonBindingLetSub,
+ },
+}
+
+pub struct NonBindingLetSub {
+ pub suggestion: Span,
+ pub multi_suggestion_start: Span,
+ pub multi_suggestion_end: Span,
+}
+
+impl AddToDiagnostic for NonBindingLetSub {
+ fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F)
+ where
+ F: Fn(
+ &mut rustc_errors::Diagnostic,
+ rustc_errors::SubdiagnosticMessage,
+ ) -> rustc_errors::SubdiagnosticMessage,
+ {
+ diag.span_suggestion_verbose(
+ self.suggestion,
+ fluent::lint_non_binding_let_suggestion,
+ "_unused",
+ Applicability::MachineApplicable,
+ );
+ diag.multipart_suggestion(
+ fluent::lint_non_binding_let_multi_suggestion,
+ vec![
+ (self.multi_suggestion_start, "drop(".to_string()),
+ (self.multi_suggestion_end, ")".to_string()),
+ ],
+ Applicability::MachineApplicable,
+ );
+ }
+}
+
+// levels.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_overruled_attribute)]
+pub struct OverruledAtributeLint<'a> {
+ #[label]
+ pub overruled: Span,
+ pub lint_level: &'a str,
+ pub lint_source: Symbol,
+ #[subdiagnostic]
+ pub sub: OverruledAttributeSub,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_deprecated_lint_name)]
+pub struct DeprecatedLintName<'a> {
+ pub name: String,
+ #[suggestion(code = "{replace}", applicability = "machine-applicable")]
+ pub suggestion: Span,
+ pub replace: &'a str,
+}
+
+// FIXME: Non-translatable msg
+#[derive(LintDiagnostic)]
+#[diag(lint_renamed_or_removed_lint)]
+pub struct RenamedOrRemovedLint<'a> {
+ pub msg: &'a str,
+ #[subdiagnostic]
+ pub suggestion: Option<RenamedOrRemovedLintSuggestion<'a>>,
+}
+
+#[derive(Subdiagnostic)]
+#[suggestion(suggestion, code = "{replace}", applicability = "machine-applicable")]
+pub struct RenamedOrRemovedLintSuggestion<'a> {
+ #[primary_span]
+ pub suggestion: Span,
+ pub replace: &'a str,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_unknown_lint)]
+pub struct UnknownLint {
+ pub name: String,
+ #[subdiagnostic]
+ pub suggestion: Option<UnknownLintSuggestion>,
+}
+
+#[derive(Subdiagnostic)]
+#[suggestion(suggestion, code = "{replace}", applicability = "maybe-incorrect")]
+pub struct UnknownLintSuggestion {
+ #[primary_span]
+ pub suggestion: Span,
+ pub replace: Symbol,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_ignored_unless_crate_specified)]
+pub struct IgnoredUnlessCrateSpecified<'a> {
+ pub level: &'a str,
+ pub name: Symbol,
+}
+
+// methods.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_cstring_ptr)]
+#[note]
+#[help]
+pub struct CStringPtr {
+ #[label(as_ptr_label)]
+ pub as_ptr: Span,
+ #[label(unwrap_label)]
+ pub unwrap: Span,
+}
+
+// non_ascii_idents.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_identifier_non_ascii_char)]
+pub struct IdentifierNonAsciiChar;
+
+#[derive(LintDiagnostic)]
+#[diag(lint_identifier_uncommon_codepoints)]
+pub struct IdentifierUncommonCodepoints;
+
+#[derive(LintDiagnostic)]
+#[diag(lint_confusable_identifier_pair)]
+pub struct ConfusableIdentifierPair {
+ pub existing_sym: Symbol,
+ pub sym: Symbol,
+ #[label]
+ pub label: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_mixed_script_confusables)]
+#[note(includes_note)]
+#[note]
+pub struct MixedScriptConfusables {
+ pub set: String,
+ pub includes: String,
+}
+
+// non_fmt_panic.rs
+pub struct NonFmtPanicUnused {
+ pub count: usize,
+ pub suggestion: Option<Span>,
+}
+
+// Used because of two suggestions based on one Option<Span>
+impl<'a> DecorateLint<'a, ()> for NonFmtPanicUnused {
+ fn decorate_lint<'b>(
+ self,
+ diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>,
+ ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
+ diag.set_arg("count", self.count);
+ diag.note(fluent::note);
+ if let Some(span) = self.suggestion {
+ diag.span_suggestion(
+ span.shrink_to_hi(),
+ fluent::add_args_suggestion,
+ ", ...",
+ Applicability::HasPlaceholders,
+ );
+ diag.span_suggestion(
+ span.shrink_to_lo(),
+ fluent::add_fmt_suggestion,
+ "\"{}\", ",
+ Applicability::MachineApplicable,
+ );
+ }
+ diag
+ }
+
+ fn msg(&self) -> rustc_errors::DiagnosticMessage {
+ fluent::lint_non_fmt_panic_unused
+ }
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_non_fmt_panic_braces)]
+#[note]
+pub struct NonFmtPanicBraces {
+ pub count: usize,
+ #[suggestion(code = "\"{{}}\", ", applicability = "machine-applicable")]
+ pub suggestion: Option<Span>,
+}
+
+// nonstandard_style.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_non_camel_case_type)]
+pub struct NonCamelCaseType<'a> {
+ pub sort: &'a str,
+ pub name: &'a str,
+ #[subdiagnostic]
+ pub sub: NonCamelCaseTypeSub,
+}
+
+#[derive(Subdiagnostic)]
+pub enum NonCamelCaseTypeSub {
+ #[label(label)]
+ Label {
+ #[primary_span]
+ span: Span,
+ },
+ #[suggestion(suggestion, code = "{replace}", applicability = "maybe-incorrect")]
+ Suggestion {
+ #[primary_span]
+ span: Span,
+ replace: String,
+ },
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_non_snake_case)]
+pub struct NonSnakeCaseDiag<'a> {
+ pub sort: &'a str,
+ pub name: &'a str,
+ pub sc: String,
+ #[subdiagnostic]
+ pub sub: NonSnakeCaseDiagSub,
+}
+
+pub enum NonSnakeCaseDiagSub {
+ Label { span: Span },
+ Help,
+ RenameOrConvertSuggestion { span: Span, suggestion: Ident },
+ ConvertSuggestion { span: Span, suggestion: String },
+ SuggestionAndNote { span: Span },
+}
+
+impl AddToDiagnostic for NonSnakeCaseDiagSub {
+ fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F)
+ where
+ F: Fn(
+ &mut rustc_errors::Diagnostic,
+ rustc_errors::SubdiagnosticMessage,
+ ) -> rustc_errors::SubdiagnosticMessage,
+ {
+ match self {
+ NonSnakeCaseDiagSub::Label { span } => {
+ diag.span_label(span, fluent::label);
+ }
+ NonSnakeCaseDiagSub::Help => {
+ diag.help(fluent::help);
+ }
+ NonSnakeCaseDiagSub::ConvertSuggestion { span, suggestion } => {
+ diag.span_suggestion(
+ span,
+ fluent::convert_suggestion,
+ suggestion,
+ Applicability::MaybeIncorrect,
+ );
+ }
+ NonSnakeCaseDiagSub::RenameOrConvertSuggestion { span, suggestion } => {
+ diag.span_suggestion(
+ span,
+ fluent::rename_or_convert_suggestion,
+ suggestion,
+ Applicability::MaybeIncorrect,
+ );
+ }
+ NonSnakeCaseDiagSub::SuggestionAndNote { span } => {
+ diag.note(fluent::cannot_convert_note);
+ diag.span_suggestion(
+ span,
+ fluent::rename_suggestion,
+ "",
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+ }
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_non_upper_case_global)]
+pub struct NonUpperCaseGlobal<'a> {
+ pub sort: &'a str,
+ pub name: &'a str,
+ #[subdiagnostic]
+ pub sub: NonUpperCaseGlobalSub,
+}
+
+#[derive(Subdiagnostic)]
+pub enum NonUpperCaseGlobalSub {
+ #[label(label)]
+ Label {
+ #[primary_span]
+ span: Span,
+ },
+ #[suggestion(suggestion, code = "{replace}", applicability = "maybe-incorrect")]
+ Suggestion {
+ #[primary_span]
+ span: Span,
+ replace: String,
+ },
+}
+
+// noop_method_call.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_noop_method_call)]
+#[note]
+pub struct NoopMethodCallDiag<'a> {
+ pub method: Symbol,
+ pub receiver_ty: Ty<'a>,
+ #[label]
+ pub label: Span,
+}
+
+// pass_by_value.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_pass_by_value)]
+pub struct PassByValueDiag {
+ pub ty: String,
+ #[suggestion(code = "{ty}", applicability = "maybe-incorrect")]
+ pub suggestion: Span,
+}
+
+// redundant_semicolon.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_redundant_semicolons)]
+pub struct RedundantSemicolonsDiag {
+ pub multiple: bool,
+ #[suggestion(code = "", applicability = "maybe-incorrect")]
+ pub suggestion: Span,
+}
+
+// traits.rs
+pub struct DropTraitConstraintsDiag<'a> {
+ pub predicate: Predicate<'a>,
+ pub tcx: TyCtxt<'a>,
+ pub def_id: DefId,
+}
+
+// Needed for def_path_str
+impl<'a> DecorateLint<'a, ()> for DropTraitConstraintsDiag<'_> {
+ fn decorate_lint<'b>(
+ self,
+ diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>,
+ ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
+ diag.set_arg("predicate", self.predicate);
+ diag.set_arg("needs_drop", self.tcx.def_path_str(self.def_id))
+ }
+
+ fn msg(&self) -> rustc_errors::DiagnosticMessage {
+ fluent::lint_drop_trait_constraints
+ }
+}
+
+pub struct DropGlue<'a> {
+ pub tcx: TyCtxt<'a>,
+ pub def_id: DefId,
+}
+
+// Needed for def_path_str
+impl<'a> DecorateLint<'a, ()> for DropGlue<'_> {
+ fn decorate_lint<'b>(
+ self,
+ diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>,
+ ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
+ diag.set_arg("needs_drop", self.tcx.def_path_str(self.def_id))
+ }
+
+ fn msg(&self) -> rustc_errors::DiagnosticMessage {
+ fluent::lint_drop_glue
+ }
+}
+
+// types.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_range_endpoint_out_of_range)]
+pub struct RangeEndpointOutOfRange<'a> {
+ pub ty: &'a str,
+ #[suggestion(code = "{start}..={literal}{suffix}", applicability = "machine-applicable")]
+ pub suggestion: Span,
+ pub start: String,
+ pub literal: u128,
+ pub suffix: &'a str,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_overflowing_bin_hex)]
+pub struct OverflowingBinHex<'a> {
+ pub ty: &'a str,
+ pub lit: String,
+ pub dec: u128,
+ pub actually: String,
+ #[subdiagnostic]
+ pub sign: OverflowingBinHexSign,
+ #[subdiagnostic]
+ pub sub: Option<OverflowingBinHexSub<'a>>,
+}
+
+pub enum OverflowingBinHexSign {
+ Positive,
+ Negative,
+}
+
+impl AddToDiagnostic for OverflowingBinHexSign {
+ fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F)
+ where
+ F: Fn(
+ &mut rustc_errors::Diagnostic,
+ rustc_errors::SubdiagnosticMessage,
+ ) -> rustc_errors::SubdiagnosticMessage,
+ {
+ match self {
+ OverflowingBinHexSign::Positive => {
+ diag.note(fluent::positive_note);
+ }
+ OverflowingBinHexSign::Negative => {
+ diag.note(fluent::negative_note);
+ diag.note(fluent::negative_becomes_note);
+ }
+ }
+ }
+}
+
+#[derive(Subdiagnostic)]
+pub enum OverflowingBinHexSub<'a> {
+ #[suggestion(
+ suggestion,
+ code = "{sans_suffix}{suggestion_ty}",
+ applicability = "machine-applicable"
+ )]
+ Suggestion {
+ #[primary_span]
+ span: Span,
+ suggestion_ty: &'a str,
+ sans_suffix: &'a str,
+ },
+ #[help(help)]
+ Help { suggestion_ty: &'a str },
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_overflowing_int)]
+#[note]
+pub struct OverflowingInt<'a> {
+ pub ty: &'a str,
+ pub lit: String,
+ pub min: i128,
+ pub max: u128,
+ #[subdiagnostic]
+ pub help: Option<OverflowingIntHelp<'a>>,
+}
+
+#[derive(Subdiagnostic)]
+#[help(help)]
+pub struct OverflowingIntHelp<'a> {
+ pub suggestion_ty: &'a str,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_only_cast_u8_to_char)]
+pub struct OnlyCastu8ToChar {
+ #[suggestion(code = "'\\u{{{literal:X}}}'", applicability = "machine-applicable")]
+ pub span: Span,
+ pub literal: u128,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_overflowing_uint)]
+#[note]
+pub struct OverflowingUInt<'a> {
+ pub ty: &'a str,
+ pub lit: String,
+ pub min: u128,
+ pub max: u128,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_overflowing_literal)]
+#[note]
+pub struct OverflowingLiteral<'a> {
+ pub ty: &'a str,
+ pub lit: String,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_unused_comparisons)]
+pub struct UnusedComparisons;
+
+pub struct ImproperCTypes<'a> {
+ pub ty: Ty<'a>,
+ pub desc: &'a str,
+ pub label: Span,
+ pub help: Option<DiagnosticMessage>,
+ pub note: DiagnosticMessage,
+ pub span_note: Option<Span>,
+}
+
+// Used because of the complexity of Option<DiagnosticMessage>, DiagnosticMessage, and Option<Span>
+impl<'a> DecorateLint<'a, ()> for ImproperCTypes<'_> {
+ fn decorate_lint<'b>(
+ self,
+ diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>,
+ ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
+ diag.set_arg("ty", self.ty);
+ diag.set_arg("desc", self.desc);
+ diag.span_label(self.label, fluent::label);
+ if let Some(help) = self.help {
+ diag.help(help);
+ }
+ diag.note(self.note);
+ if let Some(note) = self.span_note {
+ diag.span_note(note, fluent::note);
+ }
+ diag
+ }
+
+ fn msg(&self) -> rustc_errors::DiagnosticMessage {
+ fluent::lint_improper_ctypes
+ }
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_variant_size_differences)]
+pub struct VariantSizeDifferencesDiag {
+ pub largest: u64,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_atomic_ordering_load)]
+#[help]
+pub struct AtomicOrderingLoad;
+
+#[derive(LintDiagnostic)]
+#[diag(lint_atomic_ordering_store)]
+#[help]
+pub struct AtomicOrderingStore;
+
+#[derive(LintDiagnostic)]
+#[diag(lint_atomic_ordering_fence)]
+#[help]
+pub struct AtomicOrderingFence;
+
+#[derive(LintDiagnostic)]
+#[diag(lint_atomic_ordering_invalid)]
+#[help]
+pub struct InvalidAtomicOrderingDiag {
+ pub method: Symbol,
+ #[label]
+ pub fail_order_arg_span: Span,
+}
+
+// unused.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_unused_op)]
+pub struct UnusedOp<'a> {
+ pub op: &'a str,
+ #[label]
+ pub label: Span,
+ #[suggestion(style = "verbose", code = "let _ = ", applicability = "machine-applicable")]
+ pub suggestion: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_unused_result)]
+pub struct UnusedResult<'a> {
+ pub ty: Ty<'a>,
+}
+
+// FIXME(davidtwco): this isn't properly translatable becauses of the
+// pre/post strings
+#[derive(LintDiagnostic)]
+#[diag(lint_unused_closure)]
+#[note]
+pub struct UnusedClosure<'a> {
+ pub count: usize,
+ pub pre: &'a str,
+ pub post: &'a str,
+}
+
+// FIXME(davidtwco): this isn't properly translatable becauses of the
+// pre/post strings
+#[derive(LintDiagnostic)]
+#[diag(lint_unused_generator)]
+#[note]
+pub struct UnusedGenerator<'a> {
+ pub count: usize,
+ pub pre: &'a str,
+ pub post: &'a str,
+}
+
+// FIXME(davidtwco): this isn't properly translatable becauses of the pre/post
+// strings
+pub struct UnusedDef<'a, 'b> {
+ pub pre: &'a str,
+ pub post: &'a str,
+ pub cx: &'a LateContext<'b>,
+ pub def_id: DefId,
+ pub note: Option<Symbol>,
+}
+
+// Needed because of def_path_str
+impl<'a> DecorateLint<'a, ()> for UnusedDef<'_, '_> {
+ fn decorate_lint<'b>(
+ self,
+ diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>,
+ ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
+ diag.set_arg("pre", self.pre);
+ diag.set_arg("post", self.post);
+ diag.set_arg("def", self.cx.tcx.def_path_str(self.def_id));
+ // check for #[must_use = "..."]
+ if let Some(note) = self.note {
+ diag.note(note.as_str());
+ }
+ diag
+ }
+
+ fn msg(&self) -> rustc_errors::DiagnosticMessage {
+ fluent::lint_unused_def
+ }
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_path_statement_drop)]
+pub struct PathStatementDrop {
+ #[subdiagnostic]
+ pub sub: PathStatementDropSub,
+}
+
+#[derive(Subdiagnostic)]
+pub enum PathStatementDropSub {
+ #[suggestion(suggestion, code = "drop({snippet});", applicability = "machine-applicable")]
+ Suggestion {
+ #[primary_span]
+ span: Span,
+ snippet: String,
+ },
+ #[help(help)]
+ Help {
+ #[primary_span]
+ span: Span,
+ },
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_path_statement_no_effect)]
+pub struct PathStatementNoEffect;
+
+#[derive(LintDiagnostic)]
+#[diag(lint_unused_delim)]
+pub struct UnusedDelim<'a> {
+ pub delim: &'static str,
+ pub item: &'a str,
+ #[subdiagnostic]
+ pub suggestion: Option<UnusedDelimSuggestion>,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(suggestion, applicability = "machine-applicable")]
+pub struct UnusedDelimSuggestion {
+ #[suggestion_part(code = "{start_replace}")]
+ pub start_span: Span,
+ pub start_replace: &'static str,
+ #[suggestion_part(code = "{end_replace}")]
+ pub end_span: Span,
+ pub end_replace: &'static str,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_unused_import_braces)]
+pub struct UnusedImportBracesDiag {
+ pub node: Symbol,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_unused_allocation)]
+pub struct UnusedAllocationDiag;
+
+#[derive(LintDiagnostic)]
+#[diag(lint_unused_allocation_mut)]
+pub struct UnusedAllocationMutDiag;
diff --git a/compiler/rustc_lint/src/methods.rs b/compiler/rustc_lint/src/methods.rs
index e2d7d5b49..3045fc1a4 100644
--- a/compiler/rustc_lint/src/methods.rs
+++ b/compiler/rustc_lint/src/methods.rs
@@ -1,7 +1,7 @@
+use crate::lints::CStringPtr;
use crate::LateContext;
use crate::LateLintPass;
use crate::LintContext;
-use rustc_errors::fluent;
use rustc_hir::{Expr, ExprKind, PathSegment};
use rustc_middle::ty;
use rustc_span::{symbol::sym, ExpnKind, Span};
@@ -90,16 +90,10 @@ fn lint_cstring_as_ptr(
if cx.tcx.is_diagnostic_item(sym::Result, def.did()) {
if let ty::Adt(adt, _) = substs.type_at(0).kind() {
if cx.tcx.is_diagnostic_item(sym::cstring_type, adt.did()) {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
TEMPORARY_CSTRING_AS_PTR,
as_ptr_span,
- fluent::lint_cstring_ptr,
- |diag| {
- diag.span_label(as_ptr_span, fluent::as_ptr_label)
- .span_label(unwrap.span, fluent::unwrap_label)
- .note(fluent::note)
- .help(fluent::help)
- },
+ CStringPtr { as_ptr: as_ptr_span, unwrap: unwrap.span },
);
}
}
diff --git a/compiler/rustc_lint/src/non_ascii_idents.rs b/compiler/rustc_lint/src/non_ascii_idents.rs
index dea9506ac..f130a9818 100644
--- a/compiler/rustc_lint/src/non_ascii_idents.rs
+++ b/compiler/rustc_lint/src/non_ascii_idents.rs
@@ -1,7 +1,10 @@
+use crate::lints::{
+ ConfusableIdentifierPair, IdentifierNonAsciiChar, IdentifierUncommonCodepoints,
+ MixedScriptConfusables,
+};
use crate::{EarlyContext, EarlyLintPass, LintContext};
use rustc_ast as ast;
use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::fluent;
use rustc_span::symbol::Symbol;
declare_lint! {
@@ -180,21 +183,11 @@ impl EarlyLintPass for NonAsciiIdents {
continue;
}
has_non_ascii_idents = true;
- cx.struct_span_lint(
- NON_ASCII_IDENTS,
- sp,
- fluent::lint_identifier_non_ascii_char,
- |lint| lint,
- );
+ cx.emit_spanned_lint(NON_ASCII_IDENTS, sp, IdentifierNonAsciiChar);
if check_uncommon_codepoints
&& !symbol_str.chars().all(GeneralSecurityProfile::identifier_allowed)
{
- cx.struct_span_lint(
- UNCOMMON_CODEPOINTS,
- sp,
- fluent::lint_identifier_uncommon_codepoints,
- |lint| lint,
- )
+ cx.emit_spanned_lint(UNCOMMON_CODEPOINTS, sp, IdentifierUncommonCodepoints);
}
}
@@ -222,14 +215,13 @@ impl EarlyLintPass for NonAsciiIdents {
.entry(skeleton_sym)
.and_modify(|(existing_symbol, existing_span, existing_is_ascii)| {
if !*existing_is_ascii || !is_ascii {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
CONFUSABLE_IDENTS,
sp,
- fluent::lint_confusable_identifier_pair,
- |lint| {
- lint.set_arg("existing_sym", *existing_symbol)
- .set_arg("sym", symbol)
- .span_label(*existing_span, fluent::label)
+ ConfusableIdentifierPair {
+ existing_sym: *existing_symbol,
+ sym: symbol,
+ label: *existing_span,
},
);
}
@@ -331,24 +323,18 @@ impl EarlyLintPass for NonAsciiIdents {
}
for ((sp, ch_list), script_set) in lint_reports {
- cx.struct_span_lint(
+ let mut includes = String::new();
+ for (idx, ch) in ch_list.into_iter().enumerate() {
+ if idx != 0 {
+ includes += ", ";
+ }
+ let char_info = format!("'{}' (U+{:04X})", ch, ch as u32);
+ includes += &char_info;
+ }
+ cx.emit_spanned_lint(
MIXED_SCRIPT_CONFUSABLES,
sp,
- fluent::lint_mixed_script_confusables,
- |lint| {
- let mut includes = String::new();
- for (idx, ch) in ch_list.into_iter().enumerate() {
- if idx != 0 {
- includes += ", ";
- }
- let char_info = format!("'{}' (U+{:04X})", ch, ch as u32);
- includes += &char_info;
- }
- lint.set_arg("set", script_set.to_string())
- .set_arg("includes", includes)
- .note(fluent::includes_note)
- .note(fluent::note)
- },
+ MixedScriptConfusables { set: script_set.to_string(), includes },
);
}
}
diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs
index c1820ac4d..4a02c6cce 100644
--- a/compiler/rustc_lint/src/non_fmt_panic.rs
+++ b/compiler/rustc_lint/src/non_fmt_panic.rs
@@ -1,3 +1,4 @@
+use crate::lints::{NonFmtPanicBraces, NonFmtPanicUnused};
use crate::{LateContext, LateLintPass, LintContext};
use rustc_ast as ast;
use rustc_errors::{fluent, Applicability};
@@ -118,6 +119,7 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
arg_span = expn.call_site;
}
+ #[allow(rustc::diagnostic_outside_of_impl)]
cx.struct_span_lint(NON_FMT_PANICS, arg_span, fluent::lint_non_fmt_panic, |lint| {
lint.set_arg("name", symbol);
lint.note(fluent::note);
@@ -253,25 +255,14 @@ fn check_panic_str<'tcx>(
.map(|span| fmt_span.from_inner(InnerSpan::new(span.start, span.end)))
.collect(),
};
- cx.struct_span_lint(NON_FMT_PANICS, arg_spans, fluent::lint_non_fmt_panic_unused, |lint| {
- lint.set_arg("count", n_arguments);
- lint.note(fluent::note);
- if is_arg_inside_call(arg.span, span) {
- lint.span_suggestion(
- arg.span.shrink_to_hi(),
- fluent::add_args_suggestion,
- ", ...",
- Applicability::HasPlaceholders,
- );
- lint.span_suggestion(
- arg.span.shrink_to_lo(),
- fluent::add_fmt_suggestion,
- "\"{}\", ",
- Applicability::MachineApplicable,
- );
- }
- lint
- });
+ cx.emit_spanned_lint(
+ NON_FMT_PANICS,
+ arg_spans,
+ NonFmtPanicUnused {
+ count: n_arguments,
+ suggestion: is_arg_inside_call(arg.span, span).then_some(arg.span),
+ },
+ );
} else {
let brace_spans: Option<Vec<_>> =
snippet.filter(|s| s.starts_with('"') || s.starts_with("r#")).map(|s| {
@@ -281,22 +272,12 @@ fn check_panic_str<'tcx>(
.collect()
});
let count = brace_spans.as_ref().map(|v| v.len()).unwrap_or(/* any number >1 */ 2);
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
NON_FMT_PANICS,
brace_spans.unwrap_or_else(|| vec![span]),
- fluent::lint_non_fmt_panic_braces,
- |lint| {
- lint.set_arg("count", count);
- lint.note(fluent::note);
- if is_arg_inside_call(arg.span, span) {
- lint.span_suggestion(
- arg.span.shrink_to_lo(),
- fluent::suggestion,
- "\"{}\", ",
- Applicability::MachineApplicable,
- );
- }
- lint
+ NonFmtPanicBraces {
+ count,
+ suggestion: is_arg_inside_call(arg.span, span).then_some(arg.span.shrink_to_lo()),
},
);
}
@@ -304,7 +285,7 @@ fn check_panic_str<'tcx>(
/// Given the span of `some_macro!(args);`, gives the span of `(` and `)`,
/// and the type of (opening) delimiter used.
-fn find_delimiters<'tcx>(cx: &LateContext<'tcx>, span: Span) -> Option<(Span, Span, char)> {
+fn find_delimiters(cx: &LateContext<'_>, span: Span) -> Option<(Span, Span, char)> {
let snippet = cx.sess().parse_sess.source_map().span_to_snippet(span).ok()?;
let (open, open_ch) = snippet.char_indices().find(|&(_, c)| "([{".contains(c))?;
let close = snippet.rfind(|c| ")]}".contains(c))?;
diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs
index 7e50801f8..74d234fab 100644
--- a/compiler/rustc_lint/src/nonstandard_style.rs
+++ b/compiler/rustc_lint/src/nonstandard_style.rs
@@ -1,7 +1,10 @@
+use crate::lints::{
+ NonCamelCaseType, NonCamelCaseTypeSub, NonSnakeCaseDiag, NonSnakeCaseDiagSub,
+ NonUpperCaseGlobal, NonUpperCaseGlobalSub,
+};
use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
use rustc_ast as ast;
use rustc_attr as attr;
-use rustc_errors::{fluent, Applicability};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit::FnKind;
@@ -136,30 +139,17 @@ impl NonCamelCaseTypes {
let name = ident.name.as_str();
if !is_camel_case(name) {
- cx.struct_span_lint(
+ let cc = to_camel_case(name);
+ let sub = if *name != cc {
+ NonCamelCaseTypeSub::Suggestion { span: ident.span, replace: cc }
+ } else {
+ NonCamelCaseTypeSub::Label { span: ident.span }
+ };
+ cx.emit_spanned_lint(
NON_CAMEL_CASE_TYPES,
ident.span,
- fluent::lint_non_camel_case_type,
- |lint| {
- let cc = to_camel_case(name);
- // We cannot provide meaningful suggestions
- // if the characters are in the category of "Lowercase Letter".
- if *name != cc {
- lint.span_suggestion(
- ident.span,
- fluent::suggestion,
- to_camel_case(name),
- Applicability::MaybeIncorrect,
- );
- } else {
- lint.span_label(ident.span, fluent::label);
- }
-
- lint.set_arg("sort", sort);
- lint.set_arg("name", name);
- lint
- },
- )
+ NonCamelCaseType { sort, name, sub },
+ );
}
}
}
@@ -175,13 +165,23 @@ impl EarlyLintPass for NonCamelCaseTypes {
return;
}
- match it.kind {
+ match &it.kind {
ast::ItemKind::TyAlias(..)
| ast::ItemKind::Enum(..)
| ast::ItemKind::Struct(..)
| ast::ItemKind::Union(..) => self.check_case(cx, "type", &it.ident),
ast::ItemKind::Trait(..) => self.check_case(cx, "trait", &it.ident),
ast::ItemKind::TraitAlias(..) => self.check_case(cx, "trait alias", &it.ident),
+
+ // N.B. This check is only for inherent associated types, so that we don't lint against
+ // trait impls where we should have warned for the trait definition already.
+ ast::ItemKind::Impl(box ast::Impl { of_trait: None, items, .. }) => {
+ for it in items {
+ if let ast::AssocItemKind::Type(..) = it.kind {
+ self.check_case(cx, "associated type", &it.ident);
+ }
+ }
+ }
_ => (),
}
}
@@ -284,47 +284,37 @@ impl NonSnakeCase {
let name = ident.name.as_str();
if !is_snake_case(name) {
- cx.struct_span_lint(NON_SNAKE_CASE, ident.span, fluent::lint_non_snake_case, |lint| {
- let sc = NonSnakeCase::to_snake_case(name);
- // We cannot provide meaningful suggestions
- // if the characters are in the category of "Uppercase Letter".
- if name != sc {
- // We have a valid span in almost all cases, but we don't have one when linting a crate
- // name provided via the command line.
- if !ident.span.is_dummy() {
- let sc_ident = Ident::from_str_and_span(&sc, ident.span);
- let (message, suggestion) = if sc_ident.is_reserved() {
- // We shouldn't suggest a reserved identifier to fix non-snake-case identifiers.
- // Instead, recommend renaming the identifier entirely or, if permitted,
- // escaping it to create a raw identifier.
- if sc_ident.name.can_be_raw() {
- (fluent::rename_or_convert_suggestion, sc_ident.to_string())
- } else {
- lint.note(fluent::cannot_convert_note);
- (fluent::rename_suggestion, String::new())
+ let span = ident.span;
+ let sc = NonSnakeCase::to_snake_case(name);
+ // We cannot provide meaningful suggestions
+ // if the characters are in the category of "Uppercase Letter".
+ let sub = if name != sc {
+ // We have a valid span in almost all cases, but we don't have one when linting a crate
+ // name provided via the command line.
+ if !span.is_dummy() {
+ let sc_ident = Ident::from_str_and_span(&sc, span);
+ if sc_ident.is_reserved() {
+ // We shouldn't suggest a reserved identifier to fix non-snake-case identifiers.
+ // Instead, recommend renaming the identifier entirely or, if permitted,
+ // escaping it to create a raw identifier.
+ if sc_ident.name.can_be_raw() {
+ NonSnakeCaseDiagSub::RenameOrConvertSuggestion {
+ span,
+ suggestion: sc_ident,
}
} else {
- (fluent::convert_suggestion, sc.clone())
- };
-
- lint.span_suggestion(
- ident.span,
- message,
- suggestion,
- Applicability::MaybeIncorrect,
- );
+ NonSnakeCaseDiagSub::SuggestionAndNote { span }
+ }
} else {
- lint.help(fluent::help);
+ NonSnakeCaseDiagSub::ConvertSuggestion { span, suggestion: sc.clone() }
}
} else {
- lint.span_label(ident.span, fluent::label);
+ NonSnakeCaseDiagSub::Help
}
-
- lint.set_arg("sort", sort);
- lint.set_arg("name", name);
- lint.set_arg("sc", sc);
- lint
- });
+ } else {
+ NonSnakeCaseDiagSub::Label { span }
+ };
+ cx.emit_spanned_lint(NON_SNAKE_CASE, span, NonSnakeCaseDiag { sort, name, sc, sub });
}
}
}
@@ -434,8 +424,7 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase {
fn check_pat(&mut self, cx: &LateContext<'_>, p: &hir::Pat<'_>) {
if let PatKind::Binding(_, hid, ident, _) = p.kind {
- if let hir::Node::PatField(field) = cx.tcx.hir().get(cx.tcx.hir().get_parent_node(hid))
- {
+ if let hir::Node::PatField(field) = cx.tcx.hir().get_parent(hid) {
if !field.is_shorthand {
// Only check if a new name has been introduced, to avoid warning
// on both the struct definition and this pattern.
@@ -481,30 +470,19 @@ impl NonUpperCaseGlobals {
fn check_upper_case(cx: &LateContext<'_>, sort: &str, ident: &Ident) {
let name = ident.name.as_str();
if name.chars().any(|c| c.is_lowercase()) {
- cx.struct_span_lint(
+ let uc = NonSnakeCase::to_snake_case(&name).to_uppercase();
+ // We cannot provide meaningful suggestions
+ // if the characters are in the category of "Lowercase Letter".
+ let sub = if *name != uc {
+ NonUpperCaseGlobalSub::Suggestion { span: ident.span, replace: uc }
+ } else {
+ NonUpperCaseGlobalSub::Label { span: ident.span }
+ };
+ cx.emit_spanned_lint(
NON_UPPER_CASE_GLOBALS,
ident.span,
- fluent::lint_non_upper_case_global,
- |lint| {
- let uc = NonSnakeCase::to_snake_case(&name).to_uppercase();
- // We cannot provide meaningful suggestions
- // if the characters are in the category of "Lowercase Letter".
- if *name != uc {
- lint.span_suggestion(
- ident.span,
- fluent::suggestion,
- uc,
- Applicability::MaybeIncorrect,
- );
- } else {
- lint.span_label(ident.span, fluent::label);
- }
-
- lint.set_arg("sort", sort);
- lint.set_arg("name", name);
- lint
- },
- )
+ NonUpperCaseGlobal { sort, name, sub },
+ );
}
}
}
diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs
index 2ef425a10..d67a00619 100644
--- a/compiler/rustc_lint/src/noop_method_call.rs
+++ b/compiler/rustc_lint/src/noop_method_call.rs
@@ -1,7 +1,7 @@
use crate::context::LintContext;
+use crate::lints::NoopMethodCallDiag;
use crate::LateContext;
use crate::LateLintPass;
-use rustc_errors::fluent;
use rustc_hir::def::DefKind;
use rustc_hir::{Expr, ExprKind};
use rustc_middle::ty;
@@ -85,11 +85,10 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall {
}
let expr_span = expr.span;
let span = expr_span.with_lo(receiver.span.hi());
- cx.struct_span_lint(NOOP_METHOD_CALL, span, fluent::lint_noop_method_call, |lint| {
- lint.set_arg("method", call.ident.name)
- .set_arg("receiver_ty", receiver_ty)
- .span_label(span, fluent::label)
- .note(fluent::note)
- });
+ cx.emit_spanned_lint(
+ NOOP_METHOD_CALL,
+ span,
+ NoopMethodCallDiag { method: call.ident.name, receiver_ty, label: span },
+ );
}
}
diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
index 03d6f4fd9..42442cfb1 100644
--- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
+++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
@@ -82,7 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound {
let Some(proj_term) = proj.term.ty() else { continue };
let proj_ty =
- cx.tcx.mk_projection(proj.projection_ty.item_def_id, proj.projection_ty.substs);
+ cx.tcx.mk_projection(proj.projection_ty.def_id, proj.projection_ty.substs);
// For every instance of the projection type in the bounds,
// replace them with the term we're assigning to the associated
// type in our opaque type.
@@ -97,7 +97,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound {
// with `impl Send: OtherTrait`.
for (assoc_pred, assoc_pred_span) in cx
.tcx
- .bound_explicit_item_bounds(proj.projection_ty.item_def_id)
+ .bound_explicit_item_bounds(proj.projection_ty.def_id)
.subst_iter_copied(cx.tcx, &proj.projection_ty.substs)
{
let assoc_pred = assoc_pred.fold_with(proj_replacer);
@@ -117,7 +117,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound {
// then we can emit a suggestion to add the bound.
let add_bound = match (proj_term.kind(), assoc_pred.kind().skip_binder()) {
(
- ty::Opaque(def_id, _),
+ ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }),
ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)),
) => Some(AddBound {
suggest_span: cx.tcx.def_span(*def_id).shrink_to_hi(),
diff --git a/compiler/rustc_lint/src/pass_by_value.rs b/compiler/rustc_lint/src/pass_by_value.rs
index 0fa81b7e4..392e13f2f 100644
--- a/compiler/rustc_lint/src/pass_by_value.rs
+++ b/compiler/rustc_lint/src/pass_by_value.rs
@@ -1,5 +1,5 @@
+use crate::lints::PassByValueDiag;
use crate::{LateContext, LateLintPass, LintContext};
-use rustc_errors::{fluent, Applicability};
use rustc_hir as hir;
use rustc_hir::def::Res;
use rustc_hir::{GenericArg, PathSegment, QPath, TyKind};
@@ -22,27 +22,18 @@ declare_lint_pass!(PassByValue => [PASS_BY_VALUE]);
impl<'tcx> LateLintPass<'tcx> for PassByValue {
fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx>) {
match &ty.kind {
- TyKind::Rptr(_, hir::MutTy { ty: inner_ty, mutbl: hir::Mutability::Not }) => {
+ TyKind::Ref(_, hir::MutTy { ty: inner_ty, mutbl: hir::Mutability::Not }) => {
if let Some(impl_did) = cx.tcx.impl_of_method(ty.hir_id.owner.to_def_id()) {
if cx.tcx.impl_trait_ref(impl_did).is_some() {
return;
}
}
if let Some(t) = path_for_pass_by_value(cx, &inner_ty) {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
PASS_BY_VALUE,
ty.span,
- fluent::lint_pass_by_value,
- |lint| {
- lint.set_arg("ty", t.clone()).span_suggestion(
- ty.span,
- fluent::suggestion,
- t,
- // Changing type of function argument
- Applicability::MaybeIncorrect,
- )
- },
- )
+ PassByValueDiag { ty: t, suggestion: ty.span },
+ );
}
}
_ => {}
diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs
index 2f5398613..0bf01c4e5 100644
--- a/compiler/rustc_lint/src/passes.rs
+++ b/compiler/rustc_lint/src/passes.rs
@@ -9,49 +9,49 @@ use rustc_span::Span;
#[macro_export]
macro_rules! late_lint_methods {
- ($macro:path, $args:tt, [$hir:tt]) => (
- $macro!($args, [$hir], [
- fn check_body(a: &$hir hir::Body<$hir>);
- fn check_body_post(a: &$hir hir::Body<$hir>);
+ ($macro:path, $args:tt) => (
+ $macro!($args, [
+ fn check_body(a: &'tcx hir::Body<'tcx>);
+ fn check_body_post(a: &'tcx hir::Body<'tcx>);
fn check_crate();
fn check_crate_post();
- fn check_mod(a: &$hir hir::Mod<$hir>, b: hir::HirId);
- fn check_foreign_item(a: &$hir hir::ForeignItem<$hir>);
- fn check_item(a: &$hir hir::Item<$hir>);
- fn check_item_post(a: &$hir hir::Item<$hir>);
- fn check_local(a: &$hir hir::Local<$hir>);
- fn check_block(a: &$hir hir::Block<$hir>);
- fn check_block_post(a: &$hir hir::Block<$hir>);
- fn check_stmt(a: &$hir hir::Stmt<$hir>);
- fn check_arm(a: &$hir hir::Arm<$hir>);
- fn check_pat(a: &$hir hir::Pat<$hir>);
- fn check_expr(a: &$hir hir::Expr<$hir>);
- fn check_expr_post(a: &$hir hir::Expr<$hir>);
- fn check_ty(a: &$hir hir::Ty<$hir>);
- fn check_generic_param(a: &$hir hir::GenericParam<$hir>);
- fn check_generics(a: &$hir hir::Generics<$hir>);
- fn check_poly_trait_ref(a: &$hir hir::PolyTraitRef<$hir>);
+ fn check_mod(a: &'tcx hir::Mod<'tcx>, b: hir::HirId);
+ fn check_foreign_item(a: &'tcx hir::ForeignItem<'tcx>);
+ fn check_item(a: &'tcx hir::Item<'tcx>);
+ fn check_item_post(a: &'tcx hir::Item<'tcx>);
+ fn check_local(a: &'tcx hir::Local<'tcx>);
+ fn check_block(a: &'tcx hir::Block<'tcx>);
+ fn check_block_post(a: &'tcx hir::Block<'tcx>);
+ fn check_stmt(a: &'tcx hir::Stmt<'tcx>);
+ fn check_arm(a: &'tcx hir::Arm<'tcx>);
+ fn check_pat(a: &'tcx hir::Pat<'tcx>);
+ fn check_expr(a: &'tcx hir::Expr<'tcx>);
+ fn check_expr_post(a: &'tcx hir::Expr<'tcx>);
+ fn check_ty(a: &'tcx hir::Ty<'tcx>);
+ fn check_generic_param(a: &'tcx hir::GenericParam<'tcx>);
+ fn check_generics(a: &'tcx hir::Generics<'tcx>);
+ fn check_poly_trait_ref(a: &'tcx hir::PolyTraitRef<'tcx>);
fn check_fn(
- a: rustc_hir::intravisit::FnKind<$hir>,
- b: &$hir hir::FnDecl<$hir>,
- c: &$hir hir::Body<$hir>,
+ a: rustc_hir::intravisit::FnKind<'tcx>,
+ b: &'tcx hir::FnDecl<'tcx>,
+ c: &'tcx hir::Body<'tcx>,
d: Span,
e: hir::HirId);
- fn check_trait_item(a: &$hir hir::TraitItem<$hir>);
- fn check_impl_item(a: &$hir hir::ImplItem<$hir>);
- fn check_impl_item_post(a: &$hir hir::ImplItem<$hir>);
- fn check_struct_def(a: &$hir hir::VariantData<$hir>);
- fn check_field_def(a: &$hir hir::FieldDef<$hir>);
- fn check_variant(a: &$hir hir::Variant<$hir>);
- fn check_path(a: &hir::Path<$hir>, b: hir::HirId);
- fn check_attribute(a: &$hir ast::Attribute);
+ fn check_trait_item(a: &'tcx hir::TraitItem<'tcx>);
+ fn check_impl_item(a: &'tcx hir::ImplItem<'tcx>);
+ fn check_impl_item_post(a: &'tcx hir::ImplItem<'tcx>);
+ fn check_struct_def(a: &'tcx hir::VariantData<'tcx>);
+ fn check_field_def(a: &'tcx hir::FieldDef<'tcx>);
+ fn check_variant(a: &'tcx hir::Variant<'tcx>);
+ fn check_path(a: &hir::Path<'tcx>, b: hir::HirId);
+ fn check_attribute(a: &'tcx ast::Attribute);
/// Called when entering a syntax node that can have lint attributes such
/// as `#[allow(...)]`. Called with *all* the attributes of that node.
- fn enter_lint_attrs(a: &$hir [ast::Attribute]);
+ fn enter_lint_attrs(a: &'tcx [ast::Attribute]);
/// Counterpart to `enter_lint_attrs`.
- fn exit_lint_attrs(a: &$hir [ast::Attribute]);
+ fn exit_lint_attrs(a: &'tcx [ast::Attribute]);
]);
)
}
@@ -66,21 +66,23 @@ macro_rules! late_lint_methods {
// contains a few lint-specific methods with no equivalent in `Visitor`.
macro_rules! declare_late_lint_pass {
- ([], [$hir:tt], [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => (
- pub trait LateLintPass<$hir>: LintPass {
- $(#[inline(always)] fn $name(&mut self, _: &LateContext<$hir>, $(_: $arg),*) {})*
+ ([], [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => (
+ pub trait LateLintPass<'tcx>: LintPass {
+ $(#[inline(always)] fn $name(&mut self, _: &LateContext<'tcx>, $(_: $arg),*) {})*
}
)
}
-late_lint_methods!(declare_late_lint_pass, [], ['tcx]);
+// Declare the `LateLintPass` trait, which contains empty default definitions
+// for all the `check_*` methods.
+late_lint_methods!(declare_late_lint_pass, []);
impl LateLintPass<'_> for HardwiredLints {}
#[macro_export]
macro_rules! expand_combined_late_lint_pass_method {
- ([$($passes:ident),*], $self: ident, $name: ident, $params:tt) => ({
- $($self.$passes.$name $params;)*
+ ([$($pass:ident),*], $self: ident, $name: ident, $params:tt) => ({
+ $($self.$pass.$name $params;)*
})
}
@@ -93,30 +95,35 @@ macro_rules! expand_combined_late_lint_pass_methods {
)
}
+/// Combines multiple lints passes into a single lint pass, at compile time,
+/// for maximum speed. Each `check_foo` method in `$methods` within this pass
+/// simply calls `check_foo` once per `$pass`. Compare with
+/// `LateLintPassObjects`, which is similar, but combines lint passes at
+/// runtime.
#[macro_export]
macro_rules! declare_combined_late_lint_pass {
- ([$v:vis $name:ident, [$($passes:ident: $constructor:expr,)*]], [$hir:tt], $methods:tt) => (
+ ([$v:vis $name:ident, [$($pass:ident: $constructor:expr,)*]], $methods:tt) => (
#[allow(non_snake_case)]
$v struct $name {
- $($passes: $passes,)*
+ $($pass: $pass,)*
}
impl $name {
$v fn new() -> Self {
Self {
- $($passes: $constructor,)*
+ $($pass: $constructor,)*
}
}
$v fn get_lints() -> LintArray {
let mut lints = Vec::new();
- $(lints.extend_from_slice(&$passes::get_lints());)*
+ $(lints.extend_from_slice(&$pass::get_lints());)*
lints
}
}
impl<'tcx> LateLintPass<'tcx> for $name {
- expand_combined_late_lint_pass_methods!([$($passes),*], $methods);
+ expand_combined_late_lint_pass_methods!([$($pass),*], $methods);
}
#[allow(rustc::lint_pass_impl_without_macro)]
@@ -164,6 +171,9 @@ macro_rules! early_lint_methods {
/// Counterpart to `enter_lint_attrs`.
fn exit_lint_attrs(a: &[ast::Attribute]);
+
+ fn enter_where_predicate(a: &ast::WherePredicate);
+ fn exit_where_predicate(a: &ast::WherePredicate);
]);
)
}
@@ -176,12 +186,14 @@ macro_rules! declare_early_lint_pass {
)
}
+// Declare the `EarlyLintPass` trait, which contains empty default definitions
+// for all the `check_*` methods.
early_lint_methods!(declare_early_lint_pass, []);
#[macro_export]
macro_rules! expand_combined_early_lint_pass_method {
- ([$($passes:ident),*], $self: ident, $name: ident, $params:tt) => ({
- $($self.$passes.$name $params;)*
+ ([$($pass:ident),*], $self: ident, $name: ident, $params:tt) => ({
+ $($self.$pass.$name $params;)*
})
}
@@ -194,30 +206,35 @@ macro_rules! expand_combined_early_lint_pass_methods {
)
}
+/// Combines multiple lints passes into a single lint pass, at compile time,
+/// for maximum speed. Each `check_foo` method in `$methods` within this pass
+/// simply calls `check_foo` once per `$pass`. Compare with
+/// `EarlyLintPassObjects`, which is similar, but combines lint passes at
+/// runtime.
#[macro_export]
macro_rules! declare_combined_early_lint_pass {
- ([$v:vis $name:ident, [$($passes:ident: $constructor:expr,)*]], $methods:tt) => (
+ ([$v:vis $name:ident, [$($pass:ident: $constructor:expr,)*]], $methods:tt) => (
#[allow(non_snake_case)]
$v struct $name {
- $($passes: $passes,)*
+ $($pass: $pass,)*
}
impl $name {
$v fn new() -> Self {
Self {
- $($passes: $constructor,)*
+ $($pass: $constructor,)*
}
}
$v fn get_lints() -> LintArray {
let mut lints = Vec::new();
- $(lints.extend_from_slice(&$passes::get_lints());)*
+ $(lints.extend_from_slice(&$pass::get_lints());)*
lints
}
}
impl EarlyLintPass for $name {
- expand_combined_early_lint_pass_methods!([$($passes),*], $methods);
+ expand_combined_early_lint_pass_methods!([$($pass),*], $methods);
}
#[allow(rustc::lint_pass_impl_without_macro)]
diff --git a/compiler/rustc_lint/src/redundant_semicolon.rs b/compiler/rustc_lint/src/redundant_semicolon.rs
index 3521de7fc..9a8b14b49 100644
--- a/compiler/rustc_lint/src/redundant_semicolon.rs
+++ b/compiler/rustc_lint/src/redundant_semicolon.rs
@@ -1,6 +1,5 @@
-use crate::{EarlyContext, EarlyLintPass, LintContext};
+use crate::{lints::RedundantSemicolonsDiag, EarlyContext, EarlyLintPass, LintContext};
use rustc_ast::{Block, StmtKind};
-use rustc_errors::{fluent, Applicability};
use rustc_span::Span;
declare_lint! {
@@ -48,18 +47,10 @@ fn maybe_lint_redundant_semis(cx: &EarlyContext<'_>, seq: &mut Option<(Span, boo
return;
}
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
REDUNDANT_SEMICOLONS,
span,
- fluent::lint_redundant_semicolons,
- |lint| {
- lint.set_arg("multiple", multiple).span_suggestion(
- span,
- fluent::suggestion,
- "",
- Applicability::MaybeIncorrect,
- )
- },
+ RedundantSemicolonsDiag { multiple, suggestion: span },
);
}
}
diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs
index 1b21c2dac..7ea1a138b 100644
--- a/compiler/rustc_lint/src/traits.rs
+++ b/compiler/rustc_lint/src/traits.rs
@@ -1,7 +1,7 @@
+use crate::lints::{DropGlue, DropTraitConstraintsDiag};
use crate::LateContext;
use crate::LateLintPass;
use crate::LintContext;
-use rustc_errors::fluent;
use rustc_hir as hir;
use rustc_span::symbol::sym;
@@ -101,17 +101,13 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints {
if trait_predicate.trait_ref.self_ty().is_impl_trait() {
continue;
}
- let Some(needs_drop) = cx.tcx.get_diagnostic_item(sym::needs_drop) else {
- continue;
+ let Some(def_id) = cx.tcx.get_diagnostic_item(sym::needs_drop) else {
+ return
};
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
DROP_BOUNDS,
span,
- fluent::lint_drop_trait_constraints,
- |lint| {
- lint.set_arg("predicate", predicate)
- .set_arg("needs_drop", cx.tcx.def_path_str(needs_drop))
- },
+ DropTraitConstraintsDiag { predicate, tcx: cx.tcx, def_id },
);
}
}
@@ -123,12 +119,11 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints {
};
for bound in &bounds[..] {
let def_id = bound.trait_ref.trait_def_id();
- if cx.tcx.lang_items().drop_trait() == def_id
- && let Some(needs_drop) = cx.tcx.get_diagnostic_item(sym::needs_drop)
- {
- cx.struct_span_lint(DYN_DROP, bound.span, fluent::lint_drop_glue, |lint| {
- lint.set_arg("needs_drop", cx.tcx.def_path_str(needs_drop))
- });
+ if cx.tcx.lang_items().drop_trait() == def_id {
+ let Some(def_id) = cx.tcx.get_diagnostic_item(sym::needs_drop) else {
+ return
+ };
+ cx.emit_spanned_lint(DYN_DROP, bound.span, DropGlue { tcx: cx.tcx, def_id });
}
}
}
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index 297b509d4..be47a3e23 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -1,11 +1,16 @@
+use crate::lints::{
+ AtomicOrderingFence, AtomicOrderingLoad, AtomicOrderingStore, ImproperCTypes,
+ InvalidAtomicOrderingDiag, OnlyCastu8ToChar, OverflowingBinHex, OverflowingBinHexSign,
+ OverflowingBinHexSub, OverflowingInt, OverflowingIntHelp, OverflowingLiteral, OverflowingUInt,
+ RangeEndpointOutOfRange, UnusedComparisons, VariantSizeDifferencesDiag,
+};
use crate::{LateContext, LateLintPass, LintContext};
use rustc_ast as ast;
use rustc_attr as attr;
use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::{fluent, Applicability, DiagnosticMessage};
+use rustc_errors::{fluent, DiagnosticMessage};
use rustc_hir as hir;
use rustc_hir::{is_range_literal, Expr, ExprKind, Node};
-use rustc_macros::LintDiagnostic;
use rustc_middle::ty::layout::{IntegerExt, LayoutOf, SizeSkeleton};
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::{self, AdtKind, DefIdTree, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable};
@@ -16,7 +21,6 @@ use rustc_target::abi::{Abi, Size, WrappingRange};
use rustc_target::abi::{Integer, TagEncoding, Variants};
use rustc_target::spec::abi::Abi as SpecAbi;
-use std::cmp;
use std::iter;
use std::ops::ControlFlow;
@@ -128,10 +132,9 @@ fn lint_overflowing_range_endpoint<'tcx>(
) -> bool {
// We only want to handle exclusive (`..`) ranges,
// which are represented as `ExprKind::Struct`.
- let par_id = cx.tcx.hir().get_parent_node(expr.hir_id);
+ let par_id = cx.tcx.hir().parent_id(expr.hir_id);
let Node::ExprField(field) = cx.tcx.hir().get(par_id) else { return false };
- let field_par_id = cx.tcx.hir().get_parent_node(field.hir_id);
- let Node::Expr(struct_expr) = cx.tcx.hir().get(field_par_id) else { return false };
+ let Node::Expr(struct_expr) = cx.tcx.hir().get_parent(field.hir_id) else { return false };
if !is_range_literal(struct_expr) {
return false;
};
@@ -148,32 +151,22 @@ fn lint_overflowing_range_endpoint<'tcx>(
};
let Ok(start) = cx.sess().source_map().span_to_snippet(eps[0].span) else { return false };
- cx.struct_span_lint(
+ use rustc_ast::{LitIntType, LitKind};
+ let suffix = match lit.node {
+ LitKind::Int(_, LitIntType::Signed(s)) => s.name_str(),
+ LitKind::Int(_, LitIntType::Unsigned(s)) => s.name_str(),
+ LitKind::Int(_, LitIntType::Unsuffixed) => "",
+ _ => bug!(),
+ };
+ cx.emit_spanned_lint(
OVERFLOWING_LITERALS,
struct_expr.span,
- fluent::lint_range_endpoint_out_of_range,
- |lint| {
- use ast::{LitIntType, LitKind};
-
- lint.set_arg("ty", ty);
-
- // We need to preserve the literal's suffix,
- // as it may determine typing information.
- let suffix = match lit.node {
- LitKind::Int(_, LitIntType::Signed(s)) => s.name_str(),
- LitKind::Int(_, LitIntType::Unsigned(s)) => s.name_str(),
- LitKind::Int(_, LitIntType::Unsuffixed) => "",
- _ => bug!(),
- };
- let suggestion = format!("{}..={}{}", start, lit_val - 1, suffix);
- lint.span_suggestion(
- struct_expr.span,
- fluent::suggestion,
- suggestion,
- Applicability::MachineApplicable,
- );
-
- lint
+ RangeEndpointOutOfRange {
+ ty,
+ suggestion: struct_expr.span,
+ start,
+ literal: lit_val - 1,
+ suffix,
},
);
@@ -230,58 +223,37 @@ fn report_bin_hex_error(
val: u128,
negative: bool,
) {
- cx.struct_span_lint(
- OVERFLOWING_LITERALS,
- expr.span,
- fluent::lint_overflowing_bin_hex,
- |lint| {
- let (t, actually) = match ty {
- attr::IntType::SignedInt(t) => {
- let actually = if negative {
- -(size.sign_extend(val) as i128)
- } else {
- size.sign_extend(val) as i128
- };
- (t.name_str(), actually.to_string())
- }
- attr::IntType::UnsignedInt(t) => {
- let actually = size.truncate(val);
- (t.name_str(), actually.to_string())
- }
+ let (t, actually) = match ty {
+ attr::IntType::SignedInt(t) => {
+ let actually = if negative {
+ -(size.sign_extend(val) as i128)
+ } else {
+ size.sign_extend(val) as i128
};
-
- if negative {
- // If the value is negative,
- // emits a note about the value itself, apart from the literal.
- lint.note(fluent::negative_note);
- lint.note(fluent::negative_becomes_note);
+ (t.name_str(), actually.to_string())
+ }
+ attr::IntType::UnsignedInt(t) => {
+ let actually = size.truncate(val);
+ (t.name_str(), actually.to_string())
+ }
+ };
+ let sign =
+ if negative { OverflowingBinHexSign::Negative } else { OverflowingBinHexSign::Positive };
+ let sub = get_type_suggestion(cx.typeck_results().node_type(expr.hir_id), val, negative).map(
+ |suggestion_ty| {
+ if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') {
+ let (sans_suffix, _) = repr_str.split_at(pos);
+ OverflowingBinHexSub::Suggestion { span: expr.span, suggestion_ty, sans_suffix }
} else {
- lint.note(fluent::positive_note);
- }
- if let Some(sugg_ty) =
- get_type_suggestion(cx.typeck_results().node_type(expr.hir_id), val, negative)
- {
- lint.set_arg("suggestion_ty", sugg_ty);
- if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') {
- let (sans_suffix, _) = repr_str.split_at(pos);
- lint.span_suggestion(
- expr.span,
- fluent::suggestion,
- format!("{}{}", sans_suffix, sugg_ty),
- Applicability::MachineApplicable,
- );
- } else {
- lint.help(fluent::help);
- }
+ OverflowingBinHexSub::Help { suggestion_ty }
}
- lint.set_arg("ty", t)
- .set_arg("lit", repr_str)
- .set_arg("dec", val)
- .set_arg("actually", actually);
-
- lint
},
);
+ cx.emit_spanned_lint(
+ OVERFLOWING_LITERALS,
+ expr.span,
+ OverflowingBinHex { ty: t, lit: repr_str.clone(), dec: val, actually, sign, sub },
+ )
}
// This function finds the next fitting type and generates a suggestion string.
@@ -365,28 +337,19 @@ fn lint_int_literal<'tcx>(
return;
}
- cx.struct_span_lint(OVERFLOWING_LITERALS, e.span, fluent::lint_overflowing_int, |lint| {
- lint.set_arg("ty", t.name_str())
- .set_arg(
- "lit",
- cx.sess()
- .source_map()
- .span_to_snippet(lit.span)
- .expect("must get snippet from literal"),
- )
- .set_arg("min", min)
- .set_arg("max", max)
- .note(fluent::note);
-
- if let Some(sugg_ty) =
- get_type_suggestion(cx.typeck_results().node_type(e.hir_id), v, negative)
- {
- lint.set_arg("suggestion_ty", sugg_ty);
- lint.help(fluent::help);
- }
-
- lint
- });
+ let lit = cx
+ .sess()
+ .source_map()
+ .span_to_snippet(lit.span)
+ .expect("must get snippet from literal");
+ let help = get_type_suggestion(cx.typeck_results().node_type(e.hir_id), v, negative)
+ .map(|suggestion_ty| OverflowingIntHelp { suggestion_ty });
+
+ cx.emit_spanned_lint(
+ OVERFLOWING_LITERALS,
+ e.span,
+ OverflowingInt { ty: t.name_str(), lit, min, max, help },
+ );
}
}
@@ -405,23 +368,15 @@ fn lint_uint_literal<'tcx>(
_ => bug!(),
};
if lit_val < min || lit_val > max {
- let parent_id = cx.tcx.hir().get_parent_node(e.hir_id);
+ let parent_id = cx.tcx.hir().parent_id(e.hir_id);
if let Node::Expr(par_e) = cx.tcx.hir().get(parent_id) {
match par_e.kind {
hir::ExprKind::Cast(..) => {
if let ty::Char = cx.typeck_results().expr_ty(par_e).kind() {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
OVERFLOWING_LITERALS,
par_e.span,
- fluent::lint_only_cast_u8_to_char,
- |lint| {
- lint.span_suggestion(
- par_e.span,
- fluent::suggestion,
- format!("'\\u{{{:X}}}'", lit_val),
- Applicability::MachineApplicable,
- )
- },
+ OnlyCastu8ToChar { span: par_e.span, literal: lit_val },
);
return;
}
@@ -445,19 +400,20 @@ fn lint_uint_literal<'tcx>(
);
return;
}
- cx.struct_span_lint(OVERFLOWING_LITERALS, e.span, fluent::lint_overflowing_uint, |lint| {
- lint.set_arg("ty", t.name_str())
- .set_arg(
- "lit",
- cx.sess()
- .source_map()
- .span_to_snippet(lit.span)
- .expect("must get snippet from literal"),
- )
- .set_arg("min", min)
- .set_arg("max", max)
- .note(fluent::note)
- });
+ cx.emit_spanned_lint(
+ OVERFLOWING_LITERALS,
+ e.span,
+ OverflowingUInt {
+ ty: t.name_str(),
+ lit: cx
+ .sess()
+ .source_map()
+ .span_to_snippet(lit.span)
+ .expect("must get snippet from literal"),
+ min,
+ max,
+ },
+ );
}
}
@@ -486,20 +442,16 @@ fn lint_literal<'tcx>(
_ => bug!(),
};
if is_infinite == Ok(true) {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
OVERFLOWING_LITERALS,
e.span,
- fluent::lint_overflowing_literal,
- |lint| {
- lint.set_arg("ty", t.name_str())
- .set_arg(
- "lit",
- cx.sess()
- .source_map()
- .span_to_snippet(lit.span)
- .expect("must get snippet from literal"),
- )
- .note(fluent::note)
+ OverflowingLiteral {
+ ty: t.name_str(),
+ lit: cx
+ .sess()
+ .source_map()
+ .span_to_snippet(lit.span)
+ .expect("must get snippet from literal"),
},
);
}
@@ -519,19 +471,14 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits {
}
hir::ExprKind::Binary(binop, ref l, ref r) => {
if is_comparison(binop) && !check_limits(cx, binop, &l, &r) {
- cx.struct_span_lint(
- UNUSED_COMPARISONS,
- e.span,
- fluent::lint_unused_comparisons,
- |lint| lint,
- );
+ cx.emit_spanned_lint(UNUSED_COMPARISONS, e.span, UnusedComparisons);
}
}
hir::ExprKind::Lit(ref lit) => lint_literal(cx, self, e, lit),
_ => {}
};
- fn is_valid<T: cmp::PartialOrd>(binop: hir::BinOp, v: T, min: T, max: T) -> bool {
+ fn is_valid<T: PartialOrd>(binop: hir::BinOp, v: T, min: T, max: T) -> bool {
match binop.node {
hir::BinOpKind::Lt => v > min && v <= max,
hir::BinOpKind::Le => v >= min && v < max,
@@ -880,39 +827,39 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
) -> FfiResult<'tcx> {
use FfiResult::*;
- if def.repr().transparent() {
+ let transparent_safety = def.repr().transparent().then(|| {
// Can assume that at most one field is not a ZST, so only check
// that field's type for FFI-safety.
if let Some(field) = transparent_newtype_field(self.cx.tcx, variant) {
- self.check_field_type_for_ffi(cache, field, substs)
+ return self.check_field_type_for_ffi(cache, field, substs);
} else {
// All fields are ZSTs; this means that the type should behave
- // like (), which is FFI-unsafe
+ // like (), which is FFI-unsafe... except if all fields are PhantomData,
+ // which is tested for below
FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_struct_zst, help: None }
}
- } else {
- // We can't completely trust repr(C) markings; make sure the fields are
- // actually safe.
- let mut all_phantom = !variant.fields.is_empty();
- for field in &variant.fields {
- match self.check_field_type_for_ffi(cache, &field, substs) {
- FfiSafe => {
- all_phantom = false;
- }
- FfiPhantom(..) if def.is_enum() => {
- return FfiUnsafe {
- ty,
- reason: fluent::lint_improper_ctypes_enum_phantomdata,
- help: None,
- };
- }
- FfiPhantom(..) => {}
- r => return r,
+ });
+ // We can't completely trust repr(C) markings; make sure the fields are
+ // actually safe.
+ let mut all_phantom = !variant.fields.is_empty();
+ for field in &variant.fields {
+ match self.check_field_type_for_ffi(cache, &field, substs) {
+ FfiSafe => {
+ all_phantom = false;
+ }
+ FfiPhantom(..) if !def.repr().transparent() && def.is_enum() => {
+ return FfiUnsafe {
+ ty,
+ reason: fluent::lint_improper_ctypes_enum_phantomdata,
+ help: None,
+ };
}
+ FfiPhantom(..) => {}
+ r => return transparent_safety.unwrap_or(r),
}
-
- if all_phantom { FfiPhantom(ty) } else { FfiSafe }
}
+
+ if all_phantom { FfiPhantom(ty) } else { transparent_safety.unwrap_or(FfiSafe) }
}
/// Checks if the given type is "ffi-safe" (has a stable, well-defined
@@ -1140,18 +1087,20 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
// While opaque types are checked for earlier, if a projection in a struct field
// normalizes to an opaque type, then it will reach this branch.
- ty::Opaque(..) => {
+ ty::Alias(ty::Opaque, ..) => {
FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_opaque, help: None }
}
// `extern "C" fn` functions can have type parameters, which may or may not be FFI-safe,
// so they are currently ignored for the purposes of this lint.
- ty::Param(..) | ty::Projection(..) if matches!(self.mode, CItemKind::Definition) => {
+ ty::Param(..) | ty::Alias(ty::Projection, ..)
+ if matches!(self.mode, CItemKind::Definition) =>
+ {
FfiSafe
}
ty::Param(..)
- | ty::Projection(..)
+ | ty::Alias(ty::Projection, ..)
| ty::Infer(..)
| ty::Bound(..)
| ty::Error(_)
@@ -1174,26 +1123,21 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
CItemKind::Declaration => IMPROPER_CTYPES,
CItemKind::Definition => IMPROPER_CTYPES_DEFINITIONS,
};
-
- self.cx.struct_span_lint(lint, sp, fluent::lint_improper_ctypes, |lint| {
- let item_description = match self.mode {
- CItemKind::Declaration => "block",
- CItemKind::Definition => "fn",
+ let desc = match self.mode {
+ CItemKind::Declaration => "block",
+ CItemKind::Definition => "fn",
+ };
+ let span_note = if let ty::Adt(def, _) = ty.kind()
+ && let Some(sp) = self.cx.tcx.hir().span_if_local(def.did()) {
+ Some(sp)
+ } else {
+ None
};
- lint.set_arg("ty", ty);
- lint.set_arg("desc", item_description);
- lint.span_label(sp, fluent::label);
- if let Some(help) = help {
- lint.help(help);
- }
- lint.note(note);
- if let ty::Adt(def, _) = ty.kind() {
- if let Some(sp) = self.cx.tcx.hir().span_if_local(def.did()) {
- lint.span_note(sp, fluent::note);
- }
- }
- lint
- });
+ self.cx.emit_spanned_lint(
+ lint,
+ sp,
+ ImproperCTypes { ty, desc, label: sp, help, note, span_note },
+ );
}
fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool {
@@ -1203,10 +1147,10 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
if !ty.has_opaque_types() {
- return ControlFlow::CONTINUE;
+ return ControlFlow::Continue(());
}
- if let ty::Opaque(..) = ty.kind() {
+ if let ty::Alias(ty::Opaque, ..) = ty.kind() {
ControlFlow::Break(ty)
} else {
ty.super_visit_with(self)
@@ -1397,11 +1341,10 @@ impl<'tcx> LateLintPass<'tcx> for VariantSizeDifferences {
// We only warn if the largest variant is at least thrice as large as
// the second-largest.
if largest > slargest * 3 && slargest > 0 {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
VARIANT_SIZE_DIFFERENCES,
enum_definition.variants[largest_index].span,
- fluent::lint_variant_size_differences,
- |lint| lint.set_arg("largest", largest),
+ VariantSizeDifferencesDiag { largest },
);
}
}
@@ -1509,17 +1452,19 @@ impl InvalidAtomicOrdering {
fn check_atomic_load_store(cx: &LateContext<'_>, expr: &Expr<'_>) {
if let Some((method, args)) = Self::inherent_atomic_method_call(cx, expr, &[sym::load, sym::store])
- && let Some((ordering_arg, invalid_ordering, msg)) = match method {
- sym::load => Some((&args[0], sym::Release, fluent::lint_atomic_ordering_load)),
- sym::store => Some((&args[1], sym::Acquire, fluent::lint_atomic_ordering_store)),
+ && let Some((ordering_arg, invalid_ordering)) = match method {
+ sym::load => Some((&args[0], sym::Release)),
+ sym::store => Some((&args[1], sym::Acquire)),
_ => None,
}
&& let Some(ordering) = Self::match_ordering(cx, ordering_arg)
&& (ordering == invalid_ordering || ordering == sym::AcqRel)
{
- cx.struct_span_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, msg, |lint| {
- lint.help(fluent::help)
- });
+ if method == sym::load {
+ cx.emit_spanned_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, AtomicOrderingLoad);
+ } else {
+ cx.emit_spanned_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, AtomicOrderingStore);
+ };
}
}
@@ -1530,10 +1475,7 @@ impl InvalidAtomicOrdering {
&& matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::fence | sym::compiler_fence))
&& Self::match_ordering(cx, &args[0]) == Some(sym::Relaxed)
{
- cx.struct_span_lint(INVALID_ATOMIC_ORDERING, args[0].span, fluent::lint_atomic_ordering_fence, |lint| {
- lint
- .help(fluent::help)
- });
+ cx.emit_spanned_lint(INVALID_ATOMIC_ORDERING, args[0].span, AtomicOrderingFence);
}
}
@@ -1550,15 +1492,6 @@ impl InvalidAtomicOrdering {
let Some(fail_ordering) = Self::match_ordering(cx, fail_order_arg) else { return };
if matches!(fail_ordering, sym::Release | sym::AcqRel) {
- #[derive(LintDiagnostic)]
- #[diag(lint_atomic_ordering_invalid)]
- #[help]
- struct InvalidAtomicOrderingDiag {
- method: Symbol,
- #[label]
- fail_order_arg_span: Span,
- }
-
cx.emit_spanned_lint(
INVALID_ATOMIC_ORDERING,
fail_order_arg.span,
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index b5db94f8c..4c9b3df2d 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -1,9 +1,14 @@
+use crate::lints::{
+ PathStatementDrop, PathStatementDropSub, PathStatementNoEffect, UnusedAllocationDiag,
+ UnusedAllocationMutDiag, UnusedClosure, UnusedDef, UnusedDelim, UnusedDelimSuggestion,
+ UnusedGenerator, UnusedImportBracesDiag, UnusedOp, UnusedResult,
+};
use crate::Lint;
use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
use rustc_ast as ast;
use rustc_ast::util::{classify, parser};
use rustc_ast::{ExprKind, StmtKind};
-use rustc_errors::{fluent, pluralize, Applicability, MultiSpan};
+use rustc_errors::{pluralize, MultiSpan};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
@@ -96,7 +101,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
if let hir::ExprKind::Match(await_expr, _arms, hir::MatchSource::AwaitDesugar) = expr.kind
&& let ty = cx.typeck_results().expr_ty(&await_expr)
- && let ty::Opaque(future_def_id, _) = ty.kind()
+ && let ty::Alias(ty::Opaque, ty::AliasTy { def_id: future_def_id, .. }) = ty.kind()
&& cx.tcx.ty_is_opaque_future(ty)
// FIXME: This also includes non-async fns that return `impl Future`.
&& let async_fn_def_id = cx.tcx.parent(*future_def_id)
@@ -163,23 +168,20 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
let mut op_warned = false;
if let Some(must_use_op) = must_use_op {
- cx.struct_span_lint(UNUSED_MUST_USE, expr.span, fluent::lint_unused_op, |lint| {
- lint.set_arg("op", must_use_op)
- .span_label(expr.span, fluent::label)
- .span_suggestion_verbose(
- expr.span.shrink_to_lo(),
- fluent::suggestion,
- "let _ = ",
- Applicability::MachineApplicable,
- )
- });
+ cx.emit_spanned_lint(
+ UNUSED_MUST_USE,
+ expr.span,
+ UnusedOp {
+ op: must_use_op,
+ label: expr.span,
+ suggestion: expr.span.shrink_to_lo(),
+ },
+ );
op_warned = true;
}
if !(type_lint_emitted_or_suppressed || fn_warned || op_warned) {
- cx.struct_span_lint(UNUSED_RESULTS, s.span, fluent::lint_unused_result, |lint| {
- lint.set_arg("ty", ty)
- });
+ cx.emit_spanned_lint(UNUSED_RESULTS, s.span, UnusedResult { ty });
}
fn check_fn_must_use(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
@@ -251,12 +253,12 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
.map(|inner| MustUsePath::Boxed(Box::new(inner)))
}
ty::Adt(def, _) => is_def_must_use(cx, def.did(), span),
- ty::Opaque(def, _) => {
+ ty::Alias(ty::Opaque, ty::AliasTy { def_id: def, .. }) => {
elaborate_predicates_with_span(
cx.tcx,
cx.tcx.explicit_item_bounds(def).iter().cloned(),
)
- .filter_map(|obligation| {
+ .find_map(|obligation| {
// We only look at the `DefId`, so it is safe to skip the binder here.
if let ty::PredicateKind::Clause(ty::Clause::Trait(
ref poly_trait_predicate,
@@ -270,22 +272,17 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
}
})
.map(|inner| MustUsePath::Opaque(Box::new(inner)))
- .next()
}
- ty::Dynamic(binders, _, _) => binders
- .iter()
- .filter_map(|predicate| {
- if let ty::ExistentialPredicate::Trait(ref trait_ref) =
- predicate.skip_binder()
- {
- let def_id = trait_ref.def_id;
- is_def_must_use(cx, def_id, span)
- } else {
- None
- }
- .map(|inner| MustUsePath::TraitObject(Box::new(inner)))
- })
- .next(),
+ ty::Dynamic(binders, _, _) => binders.iter().find_map(|predicate| {
+ if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder()
+ {
+ let def_id = trait_ref.def_id;
+ is_def_must_use(cx, def_id, span)
+ .map(|inner| MustUsePath::TraitObject(Box::new(inner)))
+ } else {
+ None
+ }
+ }),
ty::Tuple(tys) => {
let elem_exprs = if let hir::ExprKind::Tup(elem_exprs) = expr.kind {
debug_assert_eq!(elem_exprs.len(), tys.len());
@@ -407,47 +404,31 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
);
}
MustUsePath::Closure(span) => {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
UNUSED_MUST_USE,
*span,
- fluent::lint_unused_closure,
- |lint| {
- // FIXME(davidtwco): this isn't properly translatable because of the
- // pre/post strings
- lint.set_arg("count", plural_len)
- .set_arg("pre", descr_pre)
- .set_arg("post", descr_post)
- .note(fluent::note)
- },
+ UnusedClosure { count: plural_len, pre: descr_pre, post: descr_post },
);
}
MustUsePath::Generator(span) => {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
UNUSED_MUST_USE,
*span,
- fluent::lint_unused_generator,
- |lint| {
- // FIXME(davidtwco): this isn't properly translatable because of the
- // pre/post strings
- lint.set_arg("count", plural_len)
- .set_arg("pre", descr_pre)
- .set_arg("post", descr_post)
- .note(fluent::note)
- },
+ UnusedGenerator { count: plural_len, pre: descr_pre, post: descr_post },
);
}
MustUsePath::Def(span, def_id, reason) => {
- cx.struct_span_lint(UNUSED_MUST_USE, *span, fluent::lint_unused_def, |lint| {
- // FIXME(davidtwco): this isn't properly translatable because of the pre/post
- // strings
- lint.set_arg("pre", descr_pre);
- lint.set_arg("post", descr_post);
- lint.set_arg("def", cx.tcx.def_path_str(*def_id));
- if let Some(note) = reason {
- lint.note(note.as_str());
- }
- lint
- });
+ cx.emit_spanned_lint(
+ UNUSED_MUST_USE,
+ *span,
+ UnusedDef {
+ pre: descr_pre,
+ post: descr_post,
+ cx,
+ def_id: *def_id,
+ note: *reason,
+ },
+ );
}
}
}
@@ -483,31 +464,15 @@ impl<'tcx> LateLintPass<'tcx> for PathStatements {
if let hir::ExprKind::Path(_) = expr.kind {
let ty = cx.typeck_results().expr_ty(expr);
if ty.needs_drop(cx.tcx, cx.param_env) {
- cx.struct_span_lint(
- PATH_STATEMENTS,
- s.span,
- fluent::lint_path_statement_drop,
- |lint| {
- if let Ok(snippet) = cx.sess().source_map().span_to_snippet(expr.span) {
- lint.span_suggestion(
- s.span,
- fluent::suggestion,
- format!("drop({});", snippet),
- Applicability::MachineApplicable,
- );
- } else {
- lint.span_help(s.span, fluent::suggestion);
- }
- lint
- },
- );
+ let sub = if let Ok(snippet) = cx.sess().source_map().span_to_snippet(expr.span)
+ {
+ PathStatementDropSub::Suggestion { span: s.span, snippet }
+ } else {
+ PathStatementDropSub::Help { span: s.span }
+ };
+ cx.emit_spanned_lint(PATH_STATEMENTS, s.span, PathStatementDrop { sub })
} else {
- cx.struct_span_lint(
- PATH_STATEMENTS,
- s.span,
- fluent::lint_path_statement_no_effect,
- |lint| lint,
- );
+ cx.emit_spanned_lint(PATH_STATEMENTS, s.span, PathStatementNoEffect);
}
}
}
@@ -617,7 +582,10 @@ trait UnusedDelimLint {
lhs_needs_parens
|| (followed_by_block
&& match &inner.kind {
- ExprKind::Ret(_) | ExprKind::Break(..) | ExprKind::Yield(..) => true,
+ ExprKind::Ret(_)
+ | ExprKind::Break(..)
+ | ExprKind::Yield(..)
+ | ExprKind::Yeet(..) => true,
ExprKind::Range(_lhs, Some(rhs), _limits) => {
matches!(rhs.kind, ExprKind::Block(..))
}
@@ -697,36 +665,35 @@ trait UnusedDelimLint {
} else {
MultiSpan::from(value_span)
};
- cx.struct_span_lint(self.lint(), primary_span, fluent::lint_unused_delim, |lint| {
- lint.set_arg("delim", Self::DELIM_STR);
- lint.set_arg("item", msg);
- if let Some((lo, hi)) = spans {
- let sm = cx.sess().source_map();
- let lo_replace =
+ let suggestion = spans.map(|(lo, hi)| {
+ let sm = cx.sess().source_map();
+ let lo_replace =
if keep_space.0 &&
let Ok(snip) = sm.span_to_prev_source(lo) && !snip.ends_with(' ') {
- " ".to_string()
+ " "
} else {
- "".to_string()
+ ""
};
- let hi_replace =
+ let hi_replace =
if keep_space.1 &&
let Ok(snip) = sm.span_to_next_source(hi) && !snip.starts_with(' ') {
- " ".to_string()
+ " "
} else {
- "".to_string()
+ ""
};
-
- let replacement = vec![(lo, lo_replace), (hi, hi_replace)];
- lint.multipart_suggestion(
- fluent::suggestion,
- replacement,
- Applicability::MachineApplicable,
- );
+ UnusedDelimSuggestion {
+ start_span: lo,
+ start_replace: lo_replace,
+ end_span: hi,
+ end_replace: hi_replace,
}
- lint
});
+ cx.emit_spanned_lint(
+ self.lint(),
+ primary_span,
+ UnusedDelim { delim: Self::DELIM_STR, item: msg, suggestion },
+ );
}
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
@@ -857,7 +824,17 @@ declare_lint! {
"`if`, `match`, `while` and `return` do not need parentheses"
}
-declare_lint_pass!(UnusedParens => [UNUSED_PARENS]);
+pub struct UnusedParens {
+ with_self_ty_parens: bool,
+}
+
+impl UnusedParens {
+ pub fn new() -> Self {
+ Self { with_self_ty_parens: false }
+ }
+}
+
+impl_lint_pass!(UnusedParens => [UNUSED_PARENS]);
impl UnusedDelimLint for UnusedParens {
const DELIM_STR: &'static str = "parentheses";
@@ -946,6 +923,7 @@ impl UnusedParens {
}
impl EarlyLintPass for UnusedParens {
+ #[inline]
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
match e.kind {
ExprKind::Let(ref pat, _, _) | ExprKind::ForLoop(ref pat, ..) => {
@@ -1031,36 +1009,58 @@ impl EarlyLintPass for UnusedParens {
}
fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
- if let ast::TyKind::Paren(r) = &ty.kind {
- match &r.kind {
- ast::TyKind::TraitObject(..) => {}
- ast::TyKind::BareFn(b) if b.generic_params.len() > 0 => {}
- ast::TyKind::ImplTrait(_, bounds) if bounds.len() > 1 => {}
- ast::TyKind::Array(_, len) => {
- self.check_unused_delims_expr(
- cx,
- &len.value,
- UnusedDelimsCtx::ArrayLenExpr,
- false,
- None,
- None,
- );
- }
- _ => {
- let spans = if let Some(r) = r.span.find_ancestor_inside(ty.span) {
- Some((ty.span.with_hi(r.lo()), ty.span.with_lo(r.hi())))
- } else {
- None
- };
- self.emit_unused_delims(cx, ty.span, spans, "type", (false, false));
+ match &ty.kind {
+ ast::TyKind::Array(_, len) => {
+ self.check_unused_delims_expr(
+ cx,
+ &len.value,
+ UnusedDelimsCtx::ArrayLenExpr,
+ false,
+ None,
+ None,
+ );
+ }
+ ast::TyKind::Paren(r) => {
+ match &r.kind {
+ ast::TyKind::TraitObject(..) => {}
+ ast::TyKind::BareFn(b)
+ if self.with_self_ty_parens && b.generic_params.len() > 0 => {}
+ ast::TyKind::ImplTrait(_, bounds) if bounds.len() > 1 => {}
+ _ => {
+ let spans = if let Some(r) = r.span.find_ancestor_inside(ty.span) {
+ Some((ty.span.with_hi(r.lo()), ty.span.with_lo(r.hi())))
+ } else {
+ None
+ };
+ self.emit_unused_delims(cx, ty.span, spans, "type", (false, false));
+ }
}
+ self.with_self_ty_parens = false;
}
+ _ => {}
}
}
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
<Self as UnusedDelimLint>::check_item(self, cx, item)
}
+
+ fn enter_where_predicate(&mut self, _: &EarlyContext<'_>, pred: &ast::WherePredicate) {
+ use rustc_ast::{WhereBoundPredicate, WherePredicate};
+ if let WherePredicate::BoundPredicate(WhereBoundPredicate {
+ bounded_ty,
+ bound_generic_params,
+ ..
+ }) = pred &&
+ let ast::TyKind::Paren(_) = &bounded_ty.kind &&
+ bound_generic_params.is_empty() {
+ self.with_self_ty_parens = true;
+ }
+ }
+
+ fn exit_where_predicate(&mut self, _: &EarlyContext<'_>, _: &ast::WherePredicate) {
+ assert!(!self.with_self_ty_parens);
+ }
}
declare_lint! {
@@ -1127,17 +1127,23 @@ impl UnusedDelimLint for UnusedBraces {
// ```
// - the block has no attribute and was not created inside a macro
// - if the block is an `anon_const`, the inner expr must be a literal
- // (do not lint `struct A<const N: usize>; let _: A<{ 2 + 3 }>;`)
- //
+ // not created by a macro, i.e. do not lint on:
+ // ```
+ // struct A<const N: usize>;
+ // let _: A<{ 2 + 3 }>;
+ // let _: A<{produces_literal!()}>;
+ // ```
// FIXME(const_generics): handle paths when #67075 is fixed.
if let [stmt] = inner.stmts.as_slice() {
if let ast::StmtKind::Expr(ref expr) = stmt.kind {
if !Self::is_expr_delims_necessary(expr, followed_by_block, false)
&& (ctx != UnusedDelimsCtx::AnonConst
- || matches!(expr.kind, ast::ExprKind::Lit(_)))
+ || (matches!(expr.kind, ast::ExprKind::Lit(_))
+ && !expr.span.from_expansion()))
&& !cx.sess().source_map().is_multiline(value.span)
&& value.attrs.is_empty()
&& !value.span.from_expansion()
+ && !inner.span.from_expansion()
{
self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos)
}
@@ -1164,6 +1170,7 @@ impl EarlyLintPass for UnusedBraces {
<Self as UnusedDelimLint>::check_stmt(self, cx, s)
}
+ #[inline]
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
<Self as UnusedDelimLint>::check_expr(self, cx, e);
@@ -1274,7 +1281,7 @@ impl UnusedImportBraces {
fn check_use_tree(&self, cx: &EarlyContext<'_>, use_tree: &ast::UseTree, item: &ast::Item) {
if let ast::UseTreeKind::Nested(ref items) = use_tree.kind {
// Recursively check nested UseTrees
- for &(ref tree, _) in items {
+ for (tree, _) in items {
self.check_use_tree(cx, tree, item);
}
@@ -1296,11 +1303,10 @@ impl UnusedImportBraces {
ast::UseTreeKind::Nested(_) => return,
};
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
UNUSED_IMPORT_BRACES,
item.span,
- fluent::lint_unused_import_braces,
- |lint| lint.set_arg("node", node_name),
+ UnusedImportBracesDiag { node: node_name },
);
}
}
@@ -1350,17 +1356,14 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAllocation {
for adj in cx.typeck_results().expr_adjustments(e) {
if let adjustment::Adjust::Borrow(adjustment::AutoBorrow::Ref(_, m)) = adj.kind {
- cx.struct_span_lint(
- UNUSED_ALLOCATION,
- e.span,
- match m {
- adjustment::AutoBorrowMutability::Not => fluent::lint_unused_allocation,
- adjustment::AutoBorrowMutability::Mut { .. } => {
- fluent::lint_unused_allocation_mut
- }
- },
- |lint| lint,
- );
+ match m {
+ adjustment::AutoBorrowMutability::Not => {
+ cx.emit_spanned_lint(UNUSED_ALLOCATION, e.span, UnusedAllocationDiag);
+ }
+ adjustment::AutoBorrowMutability::Mut { .. } => {
+ cx.emit_spanned_lint(UNUSED_ALLOCATION, e.span, UnusedAllocationMutDiag);
+ }
+ };
}
}
}
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index 82b837fba..b6481d70b 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -708,7 +708,7 @@ declare_lint! {
///
/// ### Example
///
- /// ```rust
+ /// ```rust,compile_fail
/// pub enum Enum {
/// Foo,
/// Bar,
@@ -743,7 +743,7 @@ declare_lint! {
/// [identifier pattern]: https://doc.rust-lang.org/reference/patterns.html#identifier-patterns
/// [path pattern]: https://doc.rust-lang.org/reference/patterns.html#path-patterns
pub BINDINGS_WITH_VARIANT_NAME,
- Warn,
+ Deny,
"detects pattern bindings with the same name as one of the matched variants"
}
@@ -911,8 +911,7 @@ declare_lint! {
declare_lint! {
/// The `trivial_casts` lint detects trivial casts which could be replaced
- /// with coercion, which may require [type ascription] or a temporary
- /// variable.
+ /// with coercion, which may require a temporary variable.
///
/// ### Example
///
@@ -934,12 +933,14 @@ declare_lint! {
/// with FFI interfaces or complex type aliases, where it triggers
/// incorrectly, or in situations where it will be more difficult to
/// clearly express the intent. It may be possible that this will become a
- /// warning in the future, possibly with [type ascription] providing a
- /// convenient way to work around the current issues. See [RFC 401] for
- /// historical context.
- ///
- /// [type ascription]: https://github.com/rust-lang/rust/issues/23416
- /// [RFC 401]: https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md
+ /// warning in the future, possibly with an explicit syntax for coercions
+ /// providing a convenient way to work around the current issues.
+ /// See [RFC 401 (coercions)][rfc-401], [RFC 803 (type ascription)][rfc-803] and
+ /// [RFC 3307 (remove type ascription)][rfc-3307] for historical context.
+ ///
+ /// [rfc-401]: https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md
+ /// [rfc-803]: https://github.com/rust-lang/rfcs/blob/master/text/0803-type-ascription.md
+ /// [rfc-3307]: https://github.com/rust-lang/rfcs/blob/master/text/3307-de-rfc-type-ascription.md
pub TRIVIAL_CASTS,
Allow,
"detects trivial casts which could be removed"
@@ -967,12 +968,14 @@ declare_lint! {
/// with FFI interfaces or complex type aliases, where it triggers
/// incorrectly, or in situations where it will be more difficult to
/// clearly express the intent. It may be possible that this will become a
- /// warning in the future, possibly with [type ascription] providing a
- /// convenient way to work around the current issues. See [RFC 401] for
- /// historical context.
- ///
- /// [type ascription]: https://github.com/rust-lang/rust/issues/23416
- /// [RFC 401]: https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md
+ /// warning in the future, possibly with an explicit syntax for coercions
+ /// providing a convenient way to work around the current issues.
+ /// See [RFC 401 (coercions)][rfc-401], [RFC 803 (type ascription)][rfc-803] and
+ /// [RFC 3307 (remove type ascription)][rfc-3307] for historical context.
+ ///
+ /// [rfc-401]: https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md
+ /// [rfc-803]: https://github.com/rust-lang/rfcs/blob/master/text/0803-type-ascription.md
+ /// [rfc-3307]: https://github.com/rust-lang/rfcs/blob/master/text/3307-de-rfc-type-ascription.md
pub TRIVIAL_NUMERIC_CASTS,
Allow,
"detects trivial casts of numeric types which could be removed"
@@ -1017,6 +1020,44 @@ declare_lint! {
}
declare_lint! {
+ /// The `invalid_alignment` lint detects dereferences of misaligned pointers during
+ /// constant evluation.
+ ///
+ /// ### Example
+ ///
+ /// ```rust,compile_fail
+ /// #![feature(const_ptr_read)]
+ /// const FOO: () = unsafe {
+ /// let x = &[0_u8; 4];
+ /// let y = x.as_ptr().cast::<u32>();
+ /// y.read(); // the address of a `u8` array is unknown and thus we don't know if
+ /// // it is aligned enough for reading a `u32`.
+ /// };
+ /// ```
+ ///
+ /// {{produces}}
+ ///
+ /// ### Explanation
+ ///
+ /// The compiler allowed dereferencing raw pointers irrespective of alignment
+ /// during const eval due to the const evaluator at the time not making it easy
+ /// or cheap to check. Now that it is both, this is not accepted anymore.
+ ///
+ /// Since it was undefined behaviour to begin with, this breakage does not violate
+ /// Rust's stability guarantees. Using undefined behaviour can cause arbitrary
+ /// behaviour, including failure to build.
+ ///
+ /// [future-incompatible]: ../index.md#future-incompatible-lints
+ pub INVALID_ALIGNMENT,
+ Deny,
+ "raw pointers must be aligned before dereferencing",
+ @future_incompatible = FutureIncompatibleInfo {
+ reference: "issue #68585 <https://github.com/rust-lang/rust/issues/104616>",
+ reason: FutureIncompatibilityReason::FutureReleaseErrorReportNow,
+ };
+}
+
+declare_lint! {
/// The `exported_private_dependencies` lint detects private dependencies
/// that are exposed in a public interface.
///
@@ -1364,7 +1405,7 @@ declare_lint! {
/// struct S;
///
/// impl S {
- /// fn late<'a, 'b>(self, _: &'a u8, _: &'b u8) {}
+ /// fn late(self, _: &u8, _: &u8) {}
/// }
///
/// fn main() {
@@ -2974,6 +3015,7 @@ declare_lint! {
"trailing semicolon in macro body used as expression",
@future_incompatible = FutureIncompatibleInfo {
reference: "issue #79813 <https://github.com/rust-lang/rust/issues/79813>",
+ reason: FutureIncompatibilityReason::FutureReleaseErrorReportNow,
};
}
@@ -3608,7 +3650,7 @@ declare_lint! {
/// fn main() {
/// let x: String = "3".try_into().unwrap();
/// // ^^^^^^^^
- /// // This call to try_into matches both Foo:try_into and TryInto::try_into as
+ /// // This call to try_into matches both Foo::try_into and TryInto::try_into as
/// // `TryInto` has been added to the Rust prelude in 2021 edition.
/// println!("{x}");
/// }
@@ -4060,10 +4102,10 @@ declare_lint! {
///
/// This can be used to implement an unsound API if used incorrectly.
pub IMPLIED_BOUNDS_ENTAILMENT,
- Warn,
+ Deny,
"impl method assumes more implied bounds than its corresponding trait method",
@future_incompatible = FutureIncompatibleInfo {
reference: "issue #105572 <https://github.com/rust-lang/rust/issues/105572>",
- reason: FutureIncompatibilityReason::FutureReleaseError,
+ reason: FutureIncompatibilityReason::FutureReleaseErrorReportNow,
};
}
diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs
index aa54b3d8a..7054d1e9f 100644
--- a/compiler/rustc_lint_defs/src/lib.rs
+++ b/compiler/rustc_lint_defs/src/lib.rs
@@ -6,8 +6,9 @@
extern crate rustc_macros;
pub use self::Level::*;
-use rustc_ast::node_id::{NodeId, NodeMap};
+use rustc_ast::node_id::NodeId;
use rustc_ast::{AttrId, Attribute};
+use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey};
use rustc_error_messages::{DiagnosticMessage, MultiSpan};
use rustc_hir::HashStableContext;
@@ -253,6 +254,19 @@ impl Level {
}
}
+ pub fn to_cmd_flag(self) -> &'static str {
+ match self {
+ Level::Warn => "-W",
+ Level::Deny => "-D",
+ Level::Forbid => "-F",
+ Level::Allow => "-A",
+ Level::ForceWarn(_) => "--force-warn",
+ Level::Expect(_) => {
+ unreachable!("the expect level does not have a commandline flag")
+ }
+ }
+ }
+
pub fn is_error(self) -> bool {
match self {
Level::Allow | Level::Expect(_) | Level::Warn | Level::ForceWarn(_) => false,
@@ -489,7 +503,7 @@ pub enum BuiltinLintDiagnostics {
param_span: Span,
/// Span of the code that should be removed when eliding this lifetime.
/// This span should include leading or trailing comma.
- deletion_span: Span,
+ deletion_span: Option<Span>,
/// Span of the single use, or None if the lifetime is never used.
/// If true, the lifetime will be fully elided.
use_span: Option<(Span, bool)>,
@@ -531,7 +545,7 @@ pub struct BufferedEarlyLint {
#[derive(Default)]
pub struct LintBuffer {
- pub map: NodeMap<Vec<BufferedEarlyLint>>,
+ pub map: FxIndexMap<NodeId, Vec<BufferedEarlyLint>>,
}
impl LintBuffer {