diff --git a/compiler/rustc_error_messages/locales/en-US/lint.ftl b/compiler/rustc_error_messages/locales/en-US/lint.ftl index 2eb409a5ddd5e..d63ff77d8e255 100644 --- a/compiler/rustc_error_messages/locales/en-US/lint.ftl +++ b/compiler/rustc_error_messages/locales/en-US/lint.ftl @@ -15,6 +15,43 @@ lint_enum_intrinsics_mem_variant = lint_expectation = this lint expectation is unfulfilled .note = the `unfulfilled_lint_expectations` lint can't be expected and will always produce this message + .rationale = {$rationale} + +lint_for_loops_over_fallibles = + for loop over {$article} `{$ty}`. This is more readably written as an `if let` statement + .suggestion = consider using `if let` to clear intent + .remove_next = to iterate over `{$recv_snip}` remove the call to `next` + .use_while_let = to check pattern in a loop use `while let` + .use_question_mark = consider unwrapping the `Result` with `?` to iterate over its contents + +lint_non_binding_let_on_sync_lock = + non-binding let on a synchronization lock + +lint_non_binding_let_on_drop_type = + non-binding let on a type that implements `Drop` + +lint_non_binding_let_suggestion = + consider binding to an unused variable to avoid immediately dropping the value + +lint_non_binding_let_multi_suggestion = + consider immediately dropping the value + +lint_deprecated_lint_name = + lint name `{$name}` is deprecated and may not have an effect in the future. + .suggestion = change it to + +lint_renamed_or_removed_lint = {$msg} + .suggestion = use the new name + +lint_unknown_lint = + unknown lint: `{$name}` + .suggestion = did you mean + +lint_ignored_unless_crate_specified = {$level}({$name}) is ignored unless specified at crate level + +lint_unknown_gated_lint = + unknown lint: `{$name}` + .note = the `{$name}` lint is unstable lint_hidden_unicode_codepoints = unicode codepoint changing visible direction of text present in {$label} .label = this {$label} contains {$count -> @@ -55,6 +92,8 @@ lint_diag_out_of_impl = lint_untranslatable_diag = diagnostics should be created using translatable messages +lint_bad_opt_access = {$msg} + lint_cstring_ptr = getting the inner pointer of a temporary `CString` .as_ptr_label = this pointer will be invalid .unwrap_label = this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime @@ -331,6 +370,8 @@ lint_builtin_anonymous_params = anonymous parameters are deprecated and will be .suggestion = try naming the parameter or explicitly ignoring it lint_builtin_deprecated_attr_link = use of deprecated attribute `{$name}`: {$reason}. See {$link} + .msg_suggestion = {$msg} + .default_suggestion = remove this attribute lint_builtin_deprecated_attr_used = use of deprecated attribute `{$name}`: no longer used. lint_builtin_deprecated_attr_default_suggestion = remove this attribute @@ -391,10 +432,16 @@ lint_builtin_incomplete_features = the feature `{$name}` is incomplete and may n .note = see issue #{$n} for more information .help = consider using `min_{$name}` instead, which is more stable and complete -lint_builtin_clashing_extern_same_name = `{$this_fi}` redeclared with a different signature +lint_builtin_unpermitted_type_init_zeroed = the type `{$ty}` does not permit zero-initialization +lint_builtin_unpermitted_type_init_unint = the type `{$ty}` does not permit being left uninitialized + +lint_builtin_unpermitted_type_init_label = this code causes undefined behavior when executed +lint_builtin_unpermitted_type_init_label_suggestion = help: use `MaybeUninit` instead, and only call `assume_init` after initialization is done + +lint_builtin_clashing_extern_same_name = `{$this}` redeclared with a different signature .previous_decl_label = `{$orig}` previously declared here .mismatch_label = this signature doesn't match the previous declaration -lint_builtin_clashing_extern_diff_name = `{$this_fi}` redeclares `{$orig}` with a different signature +lint_builtin_clashing_extern_diff_name = `{$this}` redeclares `{$orig}` with a different signature .previous_decl_label = `{$orig}` previously declared here .mismatch_label = this signature doesn't match the previous declaration @@ -403,6 +450,16 @@ lint_builtin_deref_nullptr = dereferencing a null pointer lint_builtin_asm_labels = avoid using named labels in inline assembly +lint_builtin_special_module_name_used_lib = found module declaration for lib.rs + .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 + +lint_builtin_special_module_name_used_main = found module declaration for main.rs + .note = a binary crate cannot be used as library + +lint_supertrait_as_deref_target = `{$t}` implements `Deref` with supertrait `{$target_principal}` as target + .label = target type is set here + lint_overruled_attribute = {$lint_level}({$lint_source}) incompatible with previous forbid .label = overruled by previous forbid diff --git a/compiler/rustc_lint/src/array_into_iter.rs b/compiler/rustc_lint/src/array_into_iter.rs index abebc533cc17b..3593f141df61b 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 d58168ff37721..6f445426df70e 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}; @@ -110,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, + }); } } } @@ -164,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 }); } } } @@ -267,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).span_suggestion( - fieldpat.span, - fluent::suggestion, - suggested_ident, - Applicability::MachineApplicable, - ) + BuiltinNonShorthandFieldPatterns { + ident, + suggestion: fieldpat.span, + prefix: binding_annot.prefix_str(), }, ); } @@ -321,48 +316,21 @@ impl UnsafeCode { &self, cx: &EarlyContext<'_>, span: Span, - msg: impl Into, - 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); } } @@ -371,7 +339,7 @@ impl EarlyLintPass for UnsafeCode { 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); } } } @@ -379,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); } } @@ -445,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); } } } @@ -471,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); } } } @@ -578,11 +514,10 @@ 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 }, ); } } @@ -799,12 +734,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); } } } @@ -878,11 +808,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 }, ); } } @@ -958,19 +887,11 @@ impl EarlyLintPass for AnonymousParameters { } else { ("", 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 }, + ); } } } @@ -1005,42 +926,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, }, ); } @@ -1069,20 +978,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 }, ); } } @@ -1197,20 +1104,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; } @@ -1225,30 +1122,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 }, ); } } @@ -1309,12 +1199,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); } } @@ -1362,12 +1247,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); } } } @@ -1422,20 +1302,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, + }); } } } @@ -1493,18 +1363,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(()), }, ); } @@ -1569,7 +1434,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`. @@ -1583,29 +1448,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 `::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 { @@ -1639,35 +1481,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 }, + ); } } } @@ -1767,14 +1605,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 }, ); } } @@ -1875,8 +1709,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); @@ -1891,14 +1723,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 = "..="; @@ -1909,14 +1741,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, + }, + ); } }; } @@ -1996,12 +1827,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); } } @@ -2117,18 +1943,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).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 }, ); } } @@ -2405,16 +2223,15 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { Applicability::MaybeIncorrect }; - cx.struct_span_lint( + 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(), + BuiltinExplicitOutlives { + count: bound_count, + suggestion: BuiltinExplicitOutlivesSuggestion { + spans: lint_spans, applicability, - ) + }, }, ); } @@ -2463,24 +2280,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 }, + ); }); } } @@ -2525,6 +2336,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, + /// Used to report a trace through adts. + pub(crate) nested: Option>, +} +impl InitError { + fn spanned(self, span: Span) -> InitError { + Self { span: Some(span), ..self } + } + + fn nested(self, nested: impl Into>) -> 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 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)] @@ -2533,36 +2374,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, - /// Used to report a trace through adts. - nested: Option>, - } - impl InitError { - fn spanned(self, span: Span) -> InitError { - Self { span: Some(span), ..self } - } - - fn nested(self, nested: impl Into>) -> 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 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::*; @@ -2786,46 +2597,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` 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 }, ); } } @@ -3171,31 +2952,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, ); } } @@ -3275,11 +3064,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 }, ); } } @@ -3324,6 +3112,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, .. }), @@ -3464,16 +3253,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, } } } @@ -3489,31 +3279,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::, - 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::, - 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 a16bb7f1a5f47..c9b9a62257148 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -965,6 +965,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 @@ -1011,6 +1012,7 @@ impl<'tcx> LintContext for LateContext<'tcx> { &*self.lint_store } + #[rustc_lint_diagnostics] fn lookup>( &self, lint: &'static Lint, @@ -1045,6 +1047,7 @@ impl LintContext for EarlyContext<'_> { self.builder.lint_store() } + #[rustc_lint_diagnostics] fn lookup>( &self, lint: &'static Lint, diff --git a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs index 1d29a234a3c88..dff5a645c175e 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: target_principal.to_string(), + label, + }); } } } diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index c18abaef8e256..769c2f88ff3da 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -39,6 +39,7 @@ pub struct EarlyContextAndPass<'a, T: EarlyLintPass> { 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; diff --git a/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs b/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs index f9d7466228ad1..73bd4173270ba 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 1a769893f5520..f3ae26091863d 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 cf8f31bcbd065..70c999811a52b 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; @@ -28,34 +27,17 @@ fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option) { 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 182734fa9fc8f..5219992ee94f0 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( - "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), ") = ".to_string()), - ], - 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), ") = ".to_string()), - ], - 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 dc2f5c0e2967e..7c1af6bee1dd6 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::>() - .join(", "), - ); - lint.note(fluent::suggestion_remove); - lint.note(fluent::no_suggestion_note_escape); - } - lint - }, + HiddenUnicodeCodepointsDiag { label, count, span_label: span, labels, sub }, ); } } diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 48902cd05695c..5eb54cc003427 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, }); } } @@ -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(t) = is_ty_or_ty_ctxt(cx, &path) { + cx.emit_spanned_lint(USAGE_OF_QUALIFIED_TY, path.span, TyQualified { + ty: t.clone(), + 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/let_underscore.rs b/compiler/rustc_lint/src/let_underscore.rs index 04d844d21dc38..b83a9665fc0c6 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 e9d3d44a3f9f9..09dfb1022d857 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, @@ -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 1275d6f223c7a..3d818154cb94f 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; diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs new file mode 100644 index 0000000000000..c3782a496891d --- /dev/null +++ b/compiler/rustc_lint/src/lints.rs @@ -0,0 +1,1479 @@ +#![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::{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, +} + +#[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(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 `::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>, +} + +#[derive(LintDiagnostic)] +#[diag(lint_builtin_type_alias_generic_bounds)] +pub struct BuiltinTypeAliasGenericBounds<'a, 'b> { + #[subdiagnostic] + pub suggestion: BuiltinTypeAliasGenericBoundsSuggestion, + #[subdiagnostic] + pub sub: Option>, +} + +pub struct BuiltinTypeAliasGenericBoundsSuggestion { + pub suggestions: Vec<(Span, String)>, +} + +impl AddToDiagnostic for BuiltinTypeAliasGenericBoundsSuggestion { + fn add_to_diagnostic_with(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, + #[applicability] + pub applicability: Applicability, +} + +#[derive(LintDiagnostic)] +#[diag(lint_builtin_incomplete_features)] +pub struct BuiltinIncompleteFeatures { + pub name: Symbol, + #[subdiagnostic] + pub note: Option, + #[subdiagnostic] + pub help: Option, +} + +#[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(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(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: String, + // pub target_principal: Binder<'a, ExistentialTraitRef<'b>>, + #[subdiagnostic] + pub label: Option, +} + +#[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, + #[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, + #[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, + #[subdiagnostic] + pub sub: HiddenUnicodeCodepointsDiagSub, +} + +pub struct HiddenUnicodeCodepointsDiagLabels { + pub spans: Vec<(char, Span)>, +} + +impl AddToDiagnostic for HiddenUnicodeCodepointsDiagLabels { + fn add_to_diagnostic_with(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(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::>() + .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(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>, +} + +#[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, +} + +#[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, +} + +// Used because of two suggestions based on one Option +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, +} + +// 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(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>, +} + +pub enum OverflowingBinHexSign { + Positive, + Negative, +} + +impl AddToDiagnostic for OverflowingBinHexSign { + fn add_to_diagnostic_with(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>, +} + +#[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, + pub note: DiagnosticMessage, + pub span_note: Option, +} + +// Used because of the complexity of Option, DiagnosticMessage, and Option +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, +} + +// 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, +} + +#[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 e2d7d5b49f693..3045fc1a4761e 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 dea9506acb200..f130a98185d61 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 8dccfe0046c45..4a02c6cce47ed 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> = 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()), }, ); } diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index f37d6e9a63d49..74d234fabea08 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 }, + ); } } } @@ -294,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 }); } } } @@ -490,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 2ef425a109312..d67a00619dd09 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/pass_by_value.rs b/compiler/rustc_lint/src/pass_by_value.rs index 22caadfab177d..57482a9edba88 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}; @@ -29,20 +29,11 @@ impl<'tcx> LateLintPass<'tcx> for PassByValue { } } 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.clone(), suggestion: ty.span }, + ); } } _ => {} diff --git a/compiler/rustc_lint/src/redundant_semicolon.rs b/compiler/rustc_lint/src/redundant_semicolon.rs index 3521de7fc0848..9a8b14b4907ea 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 1b21c2dac3788..7ea1a138b7e60 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 fa415243ba066..625258991a387 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}; @@ -146,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, }, ); @@ -228,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. @@ -363,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 }, + ); } } @@ -408,18 +373,10 @@ fn lint_uint_literal<'tcx>( 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; } @@ -443,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, + }, + ); } } @@ -484,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"), }, ); } @@ -517,12 +471,7 @@ 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), @@ -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 { @@ -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 525079681ca5c..ac2b32b44e6a1 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; @@ -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 { @@ -402,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, + }, + ); } } } @@ -478,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); } } } @@ -695,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) { @@ -1297,11 +1266,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 }, ); } } @@ -1351,17 +1319,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); + } + }; } } }