Convert to inline diagnostics in rustc_lint

This commit is contained in:
Jonathan Brouwer 2026-02-06 14:58:56 +01:00
parent c7f5f3e0d5
commit c814f76c06
No known key found for this signature in database
GPG key ID: 13619B051B673C52
17 changed files with 1307 additions and 1813 deletions

View file

@ -4143,7 +4143,6 @@ dependencies = [
"rustc_data_structures", "rustc_data_structures",
"rustc_errors", "rustc_errors",
"rustc_feature", "rustc_feature",
"rustc_fluent_macro",
"rustc_hir", "rustc_hir",
"rustc_index", "rustc_index",
"rustc_infer", "rustc_infer",

View file

@ -111,11 +111,7 @@ pub fn default_translator() -> Translator {
Translator::with_fallback_bundle(DEFAULT_LOCALE_RESOURCES.to_vec(), false) Translator::with_fallback_bundle(DEFAULT_LOCALE_RESOURCES.to_vec(), false)
} }
pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[ pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[];
// tidy-alphabetical-start
rustc_lint::DEFAULT_LOCALE_RESOURCE,
// tidy-alphabetical-end
];
/// Exit status code used for successful compilation and help output. /// Exit status code used for successful compilation and help output.
pub const EXIT_SUCCESS: i32 = 0; pub const EXIT_SUCCESS: i32 = 0;

View file

@ -14,7 +14,6 @@ rustc_attr_parsing = { path = "../rustc_attr_parsing" }
rustc_data_structures = { path = "../rustc_data_structures" } rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" } rustc_errors = { path = "../rustc_errors" }
rustc_feature = { path = "../rustc_feature" } rustc_feature = { path = "../rustc_feature" }
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
rustc_hir = { path = "../rustc_hir" } rustc_hir = { path = "../rustc_hir" }
rustc_index = { path = "../rustc_index" } rustc_index = { path = "../rustc_index" }
rustc_infer = { path = "../rustc_infer" } rustc_infer = { path = "../rustc_infer" }

File diff suppressed because it is too large Load diff

View file

@ -108,16 +108,18 @@ impl<'tcx> LateLintPass<'tcx> for AsyncClosureUsage {
} }
#[derive(LintDiagnostic)] #[derive(LintDiagnostic)]
#[diag(lint_closure_returning_async_block)] #[diag("closure returning async block can be made into an async closure")]
struct ClosureReturningAsyncBlock { struct ClosureReturningAsyncBlock {
#[label] #[label(
"this async block can be removed, and the closure can be turned into an async closure"
)]
async_decl_span: Span, async_decl_span: Span,
#[subdiagnostic] #[subdiagnostic]
sugg: AsyncClosureSugg, sugg: AsyncClosureSugg,
} }
#[derive(Subdiagnostic)] #[derive(Subdiagnostic)]
#[multipart_suggestion(lint_suggestion, applicability = "maybe-incorrect")] #[multipart_suggestion("turn this into an async closure", applicability = "maybe-incorrect")]
struct AsyncClosureSugg { struct AsyncClosureSugg {
#[suggestion_part(code = "")] #[suggestion_part(code = "")]
deletion_span: Span, deletion_span: Span,

View file

@ -22,7 +22,7 @@ use rustc_ast::visit::{FnCtxt, FnKind};
use rustc_ast::{self as ast, *}; use rustc_ast::{self as ast, *};
use rustc_ast_pretty::pprust::expr_to_string; use rustc_ast_pretty::pprust::expr_to_string;
use rustc_attr_parsing::AttributeParser; use rustc_attr_parsing::AttributeParser;
use rustc_errors::{Applicability, LintDiagnostic}; use rustc_errors::{Applicability, LintDiagnostic, inline_fluent};
use rustc_feature::GateIssue; use rustc_feature::GateIssue;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::attrs::{AttributeKind, DocAttribute}; use rustc_hir::attrs::{AttributeKind, DocAttribute};
@ -61,10 +61,7 @@ use crate::lints::{
BuiltinUnreachablePub, BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnreachablePub, BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment,
BuiltinUnusedDocCommentSub, BuiltinWhileTrue, InvalidAsmLabel, BuiltinUnusedDocCommentSub, BuiltinWhileTrue, InvalidAsmLabel,
}; };
use crate::{ use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext};
EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext,
fluent_generated as fluent,
};
declare_lint! { declare_lint! {
/// The `while_true` lint detects `while true { }`. /// The `while_true` lint detects `while true { }`.
/// ///
@ -2655,8 +2652,12 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
let conjured_ty = cx.typeck_results().expr_ty(expr); let conjured_ty = cx.typeck_results().expr_ty(expr);
if let Some(err) = with_no_trimmed_paths!(ty_find_init_error(cx, conjured_ty, init)) { if let Some(err) = with_no_trimmed_paths!(ty_find_init_error(cx, conjured_ty, init)) {
let msg = match init { let msg = match init {
InitKind::Zeroed => fluent::lint_builtin_unpermitted_type_init_zeroed, InitKind::Zeroed => {
InitKind::Uninit => fluent::lint_builtin_unpermitted_type_init_uninit, inline_fluent!("the type `{$ty}` does not permit zero-initialization")
}
InitKind::Uninit => {
inline_fluent!("the type `{$ty}` does not permit being left uninitialized")
}
}; };
let sub = BuiltinUnpermittedTypeInitSub { err }; let sub = BuiltinUnpermittedTypeInitSub { err };
cx.emit_span_lint( cx.emit_span_lint(

View file

@ -1,17 +1,15 @@
use rustc_errors::codes::*; use rustc_errors::codes::*;
use rustc_errors::{Diag, EmissionGuarantee, Subdiagnostic}; use rustc_errors::{Diag, EmissionGuarantee, Subdiagnostic, inline_fluent};
use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_session::lint::Level; use rustc_session::lint::Level;
use rustc_span::{Span, Symbol}; use rustc_span::{Span, Symbol};
use crate::fluent_generated as fluent;
#[derive(Diagnostic)] #[derive(Diagnostic)]
#[diag(lint_overruled_attribute, code = E0453)] #[diag("{$lint_level}({$lint_source}) incompatible with previous forbid", code = E0453)]
pub(crate) struct OverruledAttribute<'a> { pub(crate) struct OverruledAttribute<'a> {
#[primary_span] #[primary_span]
pub span: Span, pub span: Span,
#[label] #[label("overruled by previous forbid")]
pub overruled: Span, pub overruled: Span,
pub lint_level: &'a str, pub lint_level: &'a str,
pub lint_source: Symbol, pub lint_source: Symbol,
@ -29,24 +27,24 @@ impl Subdiagnostic for OverruledAttributeSub {
fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) { fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
match self { match self {
OverruledAttributeSub::DefaultSource { id } => { OverruledAttributeSub::DefaultSource { id } => {
diag.note(fluent::lint_default_source); diag.note(inline_fluent!("`forbid` lint level is the default for {$id}"));
diag.arg("id", id); diag.arg("id", id);
} }
OverruledAttributeSub::NodeSource { span, reason } => { OverruledAttributeSub::NodeSource { span, reason } => {
diag.span_label(span, fluent::lint_node_source); diag.span_label(span, inline_fluent!("`forbid` level set here"));
if let Some(rationale) = reason { if let Some(rationale) = reason {
diag.note(rationale.to_string()); diag.note(rationale.to_string());
} }
} }
OverruledAttributeSub::CommandLineSource => { OverruledAttributeSub::CommandLineSource => {
diag.note(fluent::lint_command_line_source); diag.note(inline_fluent!("`forbid` lint level was set on command line"));
} }
} }
} }
} }
#[derive(Diagnostic)] #[derive(Diagnostic)]
#[diag(lint_malformed_attribute, code = E0452)] #[diag("malformed lint attribute input", code = E0452)]
pub(crate) struct MalformedAttribute { pub(crate) struct MalformedAttribute {
#[primary_span] #[primary_span]
pub span: Span, pub span: Span,
@ -56,50 +54,55 @@ pub(crate) struct MalformedAttribute {
#[derive(Subdiagnostic)] #[derive(Subdiagnostic)]
pub(crate) enum MalformedAttributeSub { pub(crate) enum MalformedAttributeSub {
#[label(lint_bad_attribute_argument)] #[label("bad attribute argument")]
BadAttributeArgument(#[primary_span] Span), BadAttributeArgument(#[primary_span] Span),
#[label(lint_reason_must_be_string_literal)] #[label("reason must be a string literal")]
ReasonMustBeStringLiteral(#[primary_span] Span), ReasonMustBeStringLiteral(#[primary_span] Span),
#[label(lint_reason_must_come_last)] #[label("reason in lint attribute must come last")]
ReasonMustComeLast(#[primary_span] Span), ReasonMustComeLast(#[primary_span] Span),
} }
#[derive(Diagnostic)] #[derive(Diagnostic)]
#[diag(lint_unknown_tool_in_scoped_lint, code = E0710)] #[diag("unknown tool name `{$tool_name}` found in scoped lint: `{$tool_name}::{$lint_name}`", code = E0710)]
pub(crate) struct UnknownToolInScopedLint { pub(crate) struct UnknownToolInScopedLint {
#[primary_span] #[primary_span]
pub span: Option<Span>, pub span: Option<Span>,
pub tool_name: Symbol, pub tool_name: Symbol,
pub lint_name: String, pub lint_name: String,
#[help] #[help("add `#![register_tool({$tool_name})]` to the crate root")]
pub is_nightly_build: bool, pub is_nightly_build: bool,
} }
#[derive(Diagnostic)] #[derive(Diagnostic)]
#[diag(lint_builtin_ellipsis_inclusive_range_patterns, code = E0783)] #[diag("`...` range patterns are deprecated", code = E0783)]
pub(crate) struct BuiltinEllipsisInclusiveRangePatterns { pub(crate) struct BuiltinEllipsisInclusiveRangePatterns {
#[primary_span] #[primary_span]
pub span: Span, pub span: Span,
#[suggestion(style = "short", code = "{replace}", applicability = "machine-applicable")] #[suggestion(
"use `..=` for an inclusive range",
style = "short",
code = "{replace}",
applicability = "machine-applicable"
)]
pub suggestion: Span, pub suggestion: Span,
pub replace: String, pub replace: String,
} }
#[derive(Subdiagnostic)] #[derive(Subdiagnostic)]
#[note(lint_requested_level)] #[note("requested on the command line with `{$level} {$lint_name}`")]
pub(crate) struct RequestedLevel<'a> { pub(crate) struct RequestedLevel<'a> {
pub level: Level, pub level: Level,
pub lint_name: &'a str, pub lint_name: &'a str,
} }
#[derive(Diagnostic)] #[derive(Diagnostic)]
#[diag(lint_unsupported_group, code = E0602)] #[diag("`{$lint_group}` lint group is not supported with ´--force-warn´", code = E0602)]
pub(crate) struct UnsupportedGroup { pub(crate) struct UnsupportedGroup {
pub lint_group: String, pub lint_group: String,
} }
#[derive(Diagnostic)] #[derive(Diagnostic)]
#[diag(lint_check_name_unknown_tool, code = E0602)] #[diag("unknown lint tool: `{$tool_name}`", code = E0602)]
pub(crate) struct CheckNameUnknownTool<'a> { pub(crate) struct CheckNameUnknownTool<'a> {
pub tool_name: Symbol, pub tool_name: Symbol,
#[subdiagnostic] #[subdiagnostic]

View file

@ -3,7 +3,9 @@ use std::ops::ControlFlow;
use hir::intravisit::{self, Visitor}; use hir::intravisit::{self, Visitor};
use rustc_ast::Recovered; use rustc_ast::Recovered;
use rustc_errors::{Applicability, Diag, EmissionGuarantee, Subdiagnostic, SuggestionStyle}; use rustc_errors::{
Applicability, Diag, EmissionGuarantee, Subdiagnostic, SuggestionStyle, inline_fluent,
};
use rustc_hir::{self as hir, HirIdSet}; use rustc_hir::{self as hir, HirIdSet};
use rustc_macros::{LintDiagnostic, Subdiagnostic}; use rustc_macros::{LintDiagnostic, Subdiagnostic};
use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::adjustment::Adjust;
@ -303,13 +305,15 @@ impl<'tcx> LateLintPass<'tcx> for IfLetRescope {
} }
#[derive(LintDiagnostic)] #[derive(LintDiagnostic)]
#[diag(lint_if_let_rescope)] #[diag("`if let` assigns a shorter lifetime since Edition 2024")]
struct IfLetRescopeLint { struct IfLetRescopeLint {
#[subdiagnostic] #[subdiagnostic]
destructors: Vec<DestructorLabel>, destructors: Vec<DestructorLabel>,
#[label] #[label(
"this value has a significant drop implementation which may observe a major change in drop order and requires your discretion"
)]
significant_droppers: Vec<Span>, significant_droppers: Vec<Span>,
#[help] #[help("the value is now dropped here in Edition 2024")]
lifetime_ends: Vec<Span>, lifetime_ends: Vec<Span>,
#[subdiagnostic] #[subdiagnostic]
rewrite: Option<IfLetRescopeRewrite>, rewrite: Option<IfLetRescopeRewrite>,
@ -352,7 +356,9 @@ impl Subdiagnostic for IfLetRescopeRewrite {
.chain(repeat_n('}', closing_brackets.count)) .chain(repeat_n('}', closing_brackets.count))
.collect(), .collect(),
)); ));
let msg = diag.eagerly_translate(crate::fluent_generated::lint_suggestion); let msg = diag.eagerly_translate(inline_fluent!(
"a `match` with a single arm can preserve the drop order up to Edition 2021"
));
diag.multipart_suggestion_with_style( diag.multipart_suggestion_with_style(
msg, msg,
suggestions, suggestions,
@ -363,7 +369,12 @@ impl Subdiagnostic for IfLetRescopeRewrite {
} }
#[derive(Subdiagnostic)] #[derive(Subdiagnostic)]
#[note(lint_if_let_dtor)] #[note(
"{$dtor_kind ->
[dyn] value may invoke a custom destructor because it contains a trait object
*[concrete] value invokes this custom destructor
}"
)]
struct DestructorLabel { struct DestructorLabel {
#[primary_span] #[primary_span]
span: Span, span: Span,

View file

@ -3,7 +3,7 @@ use std::cell::LazyCell;
use rustc_data_structures::debug_assert_matches; use rustc_data_structures::debug_assert_matches;
use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
use rustc_data_structures::unord::UnordSet; use rustc_data_structures::unord::UnordSet;
use rustc_errors::{LintDiagnostic, Subdiagnostic}; use rustc_errors::{LintDiagnostic, Subdiagnostic, inline_fluent};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def::DefKind; use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::def_id::{DefId, LocalDefId};
@ -28,7 +28,7 @@ use rustc_trait_selection::errors::{
use rustc_trait_selection::regions::OutlivesEnvironmentBuildExt; use rustc_trait_selection::regions::OutlivesEnvironmentBuildExt;
use rustc_trait_selection::traits::ObligationCtxt; use rustc_trait_selection::traits::ObligationCtxt;
use crate::{LateContext, LateLintPass, fluent_generated as fluent}; use crate::{LateContext, LateLintPass};
declare_lint! { declare_lint! {
/// The `impl_trait_overcaptures` lint warns against cases where lifetime /// The `impl_trait_overcaptures` lint warns against cases where lifetime
@ -435,11 +435,23 @@ struct ImplTraitOvercapturesLint<'tcx> {
impl<'a> LintDiagnostic<'a, ()> for ImplTraitOvercapturesLint<'_> { impl<'a> LintDiagnostic<'a, ()> for ImplTraitOvercapturesLint<'_> {
fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::Diag<'a, ()>) { fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::Diag<'a, ()>) {
diag.primary_message(fluent::lint_impl_trait_overcaptures); diag.primary_message(inline_fluent!(
"`{$self_ty}` will capture more lifetimes than possibly intended in edition 2024"
));
diag.arg("self_ty", self.self_ty.to_string()) diag.arg("self_ty", self.self_ty.to_string())
.arg("num_captured", self.num_captured) .arg("num_captured", self.num_captured)
.span_note(self.uncaptured_spans, fluent::lint_note) .span_note(
.note(fluent::lint_note2); self.uncaptured_spans,
inline_fluent!(
"specifically, {$num_captured ->
[one] this lifetime is
*[other] these lifetimes are
} in scope but not mentioned in the type's bounds"
),
)
.note(inline_fluent!(
"all lifetimes in scope will be captured by `impl Trait`s in edition 2024"
));
if let Some(suggestion) = self.suggestion { if let Some(suggestion) = self.suggestion {
suggestion.add_to_diag(diag); suggestion.add_to_diag(diag);
} }
@ -447,9 +459,9 @@ impl<'a> LintDiagnostic<'a, ()> for ImplTraitOvercapturesLint<'_> {
} }
#[derive(LintDiagnostic)] #[derive(LintDiagnostic)]
#[diag(lint_impl_trait_redundant_captures)] #[diag("all possible in-scope parameters are already captured, so `use<...>` syntax is redundant")]
struct ImplTraitRedundantCapturesLint { struct ImplTraitRedundantCapturesLint {
#[suggestion(lint_suggestion, code = "", applicability = "machine-applicable")] #[suggestion("remove the `use<...>` syntax", code = "", applicability = "machine-applicable")]
capturing_span: Span, capturing_span: Span,
} }

View file

@ -2,7 +2,7 @@ use rustc_ast::attr::AttributeExt;
use rustc_ast_pretty::pprust; use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
use rustc_data_structures::unord::UnordSet; use rustc_data_structures::unord::UnordSet;
use rustc_errors::{Diag, LintDiagnostic, MultiSpan}; use rustc_errors::{Diag, LintDiagnostic, MultiSpan, inline_fluent};
use rustc_feature::{Features, GateIssue}; use rustc_feature::{Features, GateIssue};
use rustc_hir::HirId; use rustc_hir::HirId;
use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::intravisit::{self, Visitor};
@ -31,7 +31,6 @@ use crate::errors::{
CheckNameUnknownTool, MalformedAttribute, MalformedAttributeSub, OverruledAttribute, CheckNameUnknownTool, MalformedAttribute, MalformedAttributeSub, OverruledAttribute,
OverruledAttributeSub, RequestedLevel, UnknownToolInScopedLint, UnsupportedGroup, OverruledAttributeSub, RequestedLevel, UnknownToolInScopedLint, UnsupportedGroup,
}; };
use crate::fluent_generated as fluent;
use crate::late::unerased_lint_store; use crate::late::unerased_lint_store;
use crate::lints::{ use crate::lints::{
DeprecatedLintName, DeprecatedLintNameFromCommandLine, IgnoredUnlessCrateSpecified, DeprecatedLintName, DeprecatedLintNameFromCommandLine, IgnoredUnlessCrateSpecified,
@ -942,9 +941,9 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
let lint = builtin::UNKNOWN_LINTS; let lint = builtin::UNKNOWN_LINTS;
let level = self.lint_level(builtin::UNKNOWN_LINTS); let level = self.lint_level(builtin::UNKNOWN_LINTS);
lint_level(self.sess, lint, level, Some(span.into()), |lint| { lint_level(self.sess, lint, level, Some(span.into()), |lint| {
lint.primary_message(fluent::lint_unknown_gated_lint); lint.primary_message(inline_fluent!("unknown lint: `{$name}`"));
lint.arg("name", lint_id.lint.name_lower()); lint.arg("name", lint_id.lint.name_lower());
lint.note(fluent::lint_note); lint.note(inline_fluent!("the `{$name}` lint is unstable"));
rustc_session::parse::add_feature_diagnostics_for_issue( rustc_session::parse::add_feature_diagnostics_for_issue(
lint, lint,
&self.sess, &self.sess,

View file

@ -139,8 +139,6 @@ pub use rustc_errors::BufferedEarlyLint;
pub use rustc_session::lint::Level::{self, *}; pub use rustc_session::lint::Level::{self, *};
pub use rustc_session::lint::{FutureIncompatibleInfo, Lint, LintId, LintPass, LintVec}; pub use rustc_session::lint::{FutureIncompatibleInfo, Lint, LintId, LintPass, LintVec};
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
pub fn provide(providers: &mut Providers) { pub fn provide(providers: &mut Providers) {
levels::provide(providers); levels::provide(providers);
expect::provide(providers); expect::provide(providers);

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
use rustc_ast as ast; use rustc_ast as ast;
use rustc_errors::Applicability; use rustc_errors::{Applicability, inline_fluent};
use rustc_hir::{self as hir, LangItem}; use rustc_hir::{self as hir, LangItem};
use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::{bug, ty}; use rustc_middle::{bug, ty};
@ -10,7 +10,7 @@ use rustc_span::{InnerSpan, Span, Symbol, hygiene, sym};
use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::infer::InferCtxtExt;
use crate::lints::{NonFmtPanicBraces, NonFmtPanicUnused}; use crate::lints::{NonFmtPanicBraces, NonFmtPanicUnused};
use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent}; use crate::{LateContext, LateLintPass, LintContext};
declare_lint! { declare_lint! {
/// The `non_fmt_panics` lint detects `panic!(..)` invocations where the first /// The `non_fmt_panics` lint detects `panic!(..)` invocations where the first
@ -121,20 +121,20 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
} }
cx.span_lint(NON_FMT_PANICS, arg_span, |lint| { cx.span_lint(NON_FMT_PANICS, arg_span, |lint| {
lint.primary_message(fluent::lint_non_fmt_panic); lint.primary_message(inline_fluent!("panic message is not a string literal"));
lint.arg("name", symbol); lint.arg("name", symbol);
lint.note(fluent::lint_note); lint.note(inline_fluent!("this usage of `{$name}!()` is deprecated; it will be a hard error in Rust 2021"));
lint.note(fluent::lint_more_info_note); lint.note(inline_fluent!("for more information, see <https://doc.rust-lang.org/edition-guide/rust-2021/panic-macro-consistency.html>"));
if !is_arg_inside_call(arg_span, span) { if !is_arg_inside_call(arg_span, span) {
// No clue where this argument is coming from. // No clue where this argument is coming from.
return; return;
} }
if arg_macro.is_some_and(|id| cx.tcx.is_diagnostic_item(sym::format_macro, id)) { if arg_macro.is_some_and(|id| cx.tcx.is_diagnostic_item(sym::format_macro, id)) {
// A case of `panic!(format!(..))`. // A case of `panic!(format!(..))`.
lint.note(fluent::lint_supports_fmt_note); lint.note(inline_fluent!("the `{$name}!()` macro supports formatting, so there's no need for the `format!()` macro here"));
if let Some((open, close, _)) = find_delimiters(cx, arg_span) { if let Some((open, close, _)) = find_delimiters(cx, arg_span) {
lint.multipart_suggestion( lint.multipart_suggestion(
fluent::lint_supports_fmt_suggestion, inline_fluent!("remove the `format!(..)` macro call"),
vec![ vec![
(arg_span.until(open.shrink_to_hi()), "".into()), (arg_span.until(open.shrink_to_hi()), "".into()),
(close.until(arg_span.shrink_to_hi()), "".into()), (close.until(arg_span.shrink_to_hi()), "".into()),
@ -178,7 +178,7 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
if suggest_display { if suggest_display {
lint.span_suggestion_verbose( lint.span_suggestion_verbose(
arg_span.shrink_to_lo(), arg_span.shrink_to_lo(),
fluent::lint_display_suggestion, inline_fluent!(r#"add a "{"{"}{"}"}" format string to `Display` the message"#),
"\"{}\", ", "\"{}\", ",
fmt_applicability, fmt_applicability,
); );
@ -186,7 +186,7 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
lint.arg("ty", ty); lint.arg("ty", ty);
lint.span_suggestion_verbose( lint.span_suggestion_verbose(
arg_span.shrink_to_lo(), arg_span.shrink_to_lo(),
fluent::lint_debug_suggestion, inline_fluent!(r#"add a "{"{"}:?{"}"}" format string to use the `Debug` implementation of `{$ty}`"#),
"\"{:?}\", ", "\"{:?}\", ",
fmt_applicability, fmt_applicability,
); );
@ -196,7 +196,10 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
if let Some((open, close, del)) = find_delimiters(cx, span) { if let Some((open, close, del)) = find_delimiters(cx, span) {
lint.arg("already_suggested", suggest_display || suggest_debug); lint.arg("already_suggested", suggest_display || suggest_debug);
lint.multipart_suggestion( lint.multipart_suggestion(
fluent::lint_panic_suggestion, inline_fluent!("{$already_suggested ->
[true] or use
*[false] use
} std::panic::panic_any instead"),
if del == '(' { if del == '(' {
vec![(span.until(open), "std::panic::panic_any".into())] vec![(span.until(open), "std::panic::panic_any".into())]
} else { } else {

View file

@ -1,4 +1,4 @@
use rustc_errors::MultiSpan; use rustc_errors::{MultiSpan, inline_fluent};
use rustc_hir::attrs::AttributeKind; use rustc_hir::attrs::AttributeKind;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit::{self, Visitor, VisitorExt}; use rustc_hir::intravisit::{self, Visitor, VisitorExt};
@ -9,7 +9,7 @@ use rustc_span::def_id::{DefId, LOCAL_CRATE};
use rustc_span::{ExpnKind, Span, kw}; use rustc_span::{ExpnKind, Span, kw};
use crate::lints::{NonLocalDefinitionsCargoUpdateNote, NonLocalDefinitionsDiag}; use crate::lints::{NonLocalDefinitionsCargoUpdateNote, NonLocalDefinitionsDiag};
use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent}; use crate::{LateContext, LateLintPass, LintContext};
declare_lint! { declare_lint! {
/// The `non_local_definitions` lint checks for `impl` blocks and `#[macro_export]` /// The `non_local_definitions` lint checks for `impl` blocks and `#[macro_export]`
@ -210,7 +210,12 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
if !doctest { if !doctest {
ms.push_span_label( ms.push_span_label(
cx.tcx.def_span(parent), cx.tcx.def_span(parent),
fluent::lint_non_local_definitions_impl_move_help, inline_fluent!(
"move the `impl` block outside of this {$body_kind_descr} {$depth ->
[one] `{$body_name}`
*[other] `{$body_name}` and up {$depth} bodies
}"
),
); );
} }

View file

@ -202,11 +202,11 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound {
} }
#[derive(LintDiagnostic)] #[derive(LintDiagnostic)]
#[diag(lint_opaque_hidden_inferred_bound)] #[diag("opaque type `{$ty}` does not satisfy its associated type bounds")]
struct OpaqueHiddenInferredBoundLint<'tcx> { struct OpaqueHiddenInferredBoundLint<'tcx> {
ty: Ty<'tcx>, ty: Ty<'tcx>,
proj_ty: Ty<'tcx>, proj_ty: Ty<'tcx>,
#[label(lint_specifically)] #[label("this associated type bound is unsatisfied for `{$proj_ty}`")]
assoc_pred_span: Span, assoc_pred_span: Span,
#[subdiagnostic] #[subdiagnostic]
add_bound: Option<AddBound<'tcx>>, add_bound: Option<AddBound<'tcx>>,
@ -214,7 +214,7 @@ struct OpaqueHiddenInferredBoundLint<'tcx> {
#[derive(Subdiagnostic)] #[derive(Subdiagnostic)]
#[suggestion( #[suggestion(
lint_opaque_hidden_inferred_bound_sugg, "add this bound",
style = "verbose", style = "verbose",
applicability = "machine-applicable", applicability = "machine-applicable",
code = " + {trait_ref}" code = " + {trait_ref}"

View file

@ -369,8 +369,10 @@ fn check_unnecessary_transmute<'tcx>(
} }
#[derive(LintDiagnostic)] #[derive(LintDiagnostic)]
#[diag(lint_undefined_transmute)] #[diag("pointers cannot be transmuted to integers during const eval")]
#[note] #[note("at compile-time, pointers do not have an integer value")]
#[note(lint_note2)] #[note(
#[help] "avoiding this restriction via `union` or raw pointers leads to compile-time undefined behavior"
)]
#[help("for more information, see https://doc.rust-lang.org/std/mem/fn.transmute.html")]
pub(crate) struct UndefinedTransmuteLint; pub(crate) struct UndefinedTransmuteLint;

View file

@ -4,7 +4,7 @@ use std::ops::ControlFlow;
use bitflags::bitflags; use bitflags::bitflags;
use rustc_abi::VariantIdx; use rustc_abi::VariantIdx;
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_errors::DiagMessage; use rustc_errors::{DiagMessage, inline_fluent};
use rustc_hir::def::CtorKind; use rustc_hir::def::CtorKind;
use rustc_hir::intravisit::VisitorExt; use rustc_hir::intravisit::VisitorExt;
use rustc_hir::{self as hir, AmbigArg}; use rustc_hir::{self as hir, AmbigArg};
@ -21,7 +21,7 @@ use tracing::debug;
use super::repr_nullable_ptr; use super::repr_nullable_ptr;
use crate::lints::{ImproperCTypes, UsesPowerAlignment}; use crate::lints::{ImproperCTypes, UsesPowerAlignment};
use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent}; use crate::{LateContext, LateLintPass, LintContext};
declare_lint! { declare_lint! {
/// The `improper_ctypes` lint detects incorrect use of types in foreign /// The `improper_ctypes` lint detects incorrect use of types in foreign
@ -158,12 +158,12 @@ pub(crate) fn check_non_exhaustive_variant(
// with an enum like `#[repr(u8)] enum Enum { A(DataA), B(DataB), }` // with an enum like `#[repr(u8)] enum Enum { A(DataA), B(DataB), }`
// but exempt enums with unit ctors like C's (e.g. from rust-bindgen) // but exempt enums with unit ctors like C's (e.g. from rust-bindgen)
if variant_has_complex_ctor(variant) { if variant_has_complex_ctor(variant) {
return ControlFlow::Break(fluent::lint_improper_ctypes_non_exhaustive); return ControlFlow::Break(inline_fluent!("this enum is non-exhaustive"));
} }
} }
if variant.field_list_has_applicable_non_exhaustive() { if variant.field_list_has_applicable_non_exhaustive() {
return ControlFlow::Break(fluent::lint_improper_ctypes_non_exhaustive_variant); return ControlFlow::Break(inline_fluent!("this enum has non-exhaustive variants"));
} }
ControlFlow::Continue(()) ControlFlow::Continue(())
@ -424,7 +424,11 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
if all_phantom { if all_phantom {
FfiPhantom(ty) FfiPhantom(ty)
} else if transparent_with_all_zst_fields { } else if transparent_with_all_zst_fields {
FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_struct_zst, help: None } FfiUnsafe {
ty,
reason: inline_fluent!("this struct contains only zero-sized fields"),
help: None,
}
} else { } else {
FfiSafe FfiSafe
} }
@ -460,7 +464,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
} else { } else {
return FfiUnsafe { return FfiUnsafe {
ty, ty,
reason: fluent::lint_improper_ctypes_box, reason: inline_fluent!("box cannot be represented as a single pointer"),
help: None, help: None,
}; };
} }
@ -476,8 +480,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
{ {
return FfiUnsafe { return FfiUnsafe {
ty, ty,
reason: fluent::lint_improper_ctypes_cstr_reason, reason: inline_fluent!(
help: Some(fluent::lint_improper_ctypes_cstr_help), "`CStr`/`CString` do not have a guaranteed layout"
),
help: Some(inline_fluent!(
"consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()`"
)),
}; };
} }
@ -485,14 +493,18 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
return FfiUnsafe { return FfiUnsafe {
ty, ty,
reason: if def.is_struct() { reason: if def.is_struct() {
fluent::lint_improper_ctypes_struct_layout_reason inline_fluent!("this struct has unspecified layout")
} else { } else {
fluent::lint_improper_ctypes_union_layout_reason inline_fluent!("this union has unspecified layout")
}, },
help: if def.is_struct() { help: if def.is_struct() {
Some(fluent::lint_improper_ctypes_struct_layout_help) Some(inline_fluent!(
"consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct"
))
} else { } else {
Some(fluent::lint_improper_ctypes_union_layout_help) Some(inline_fluent!(
"consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this union"
))
}, },
}; };
} }
@ -501,9 +513,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
return FfiUnsafe { return FfiUnsafe {
ty, ty,
reason: if def.is_struct() { reason: if def.is_struct() {
fluent::lint_improper_ctypes_struct_non_exhaustive inline_fluent!("this struct is non-exhaustive")
} else { } else {
fluent::lint_improper_ctypes_union_non_exhaustive inline_fluent!("this union is non-exhaustive")
}, },
help: None, help: None,
}; };
@ -513,14 +525,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
return FfiUnsafe { return FfiUnsafe {
ty, ty,
reason: if def.is_struct() { reason: if def.is_struct() {
fluent::lint_improper_ctypes_struct_fieldless_reason inline_fluent!("this struct has no fields")
} else { } else {
fluent::lint_improper_ctypes_union_fieldless_reason inline_fluent!("this union has no fields")
}, },
help: if def.is_struct() { help: if def.is_struct() {
Some(fluent::lint_improper_ctypes_struct_fieldless_help) Some(inline_fluent!("consider adding a member to this struct"))
} else { } else {
Some(fluent::lint_improper_ctypes_union_fieldless_help) Some(inline_fluent!("consider adding a member to this union"))
}, },
}; };
} }
@ -545,8 +557,10 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
return FfiUnsafe { return FfiUnsafe {
ty, ty,
reason: fluent::lint_improper_ctypes_enum_repr_reason, reason: inline_fluent!("enum has no representation hint"),
help: Some(fluent::lint_improper_ctypes_enum_repr_help), help: Some(inline_fluent!(
"consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum"
)),
}; };
} }
@ -572,8 +586,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
ty::Char => FfiUnsafe { ty::Char => FfiUnsafe {
ty, ty,
reason: fluent::lint_improper_ctypes_char_reason, reason: inline_fluent!("the `char` type has no C equivalent"),
help: Some(fluent::lint_improper_ctypes_char_help), help: Some(inline_fluent!("consider using `u32` or `libc::wchar_t` instead")),
}, },
// It's just extra invariants on the type that you need to uphold, // It's just extra invariants on the type that you need to uphold,
@ -585,24 +599,26 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
ty::Slice(_) => FfiUnsafe { ty::Slice(_) => FfiUnsafe {
ty, ty,
reason: fluent::lint_improper_ctypes_slice_reason, reason: inline_fluent!("slices have no C equivalent"),
help: Some(fluent::lint_improper_ctypes_slice_help), help: Some(inline_fluent!("consider using a raw pointer instead")),
}, },
ty::Dynamic(..) => { ty::Dynamic(..) => FfiUnsafe {
FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_dyn, help: None } ty,
} reason: inline_fluent!("trait objects have no C equivalent"),
help: None,
},
ty::Str => FfiUnsafe { ty::Str => FfiUnsafe {
ty, ty,
reason: fluent::lint_improper_ctypes_str_reason, reason: inline_fluent!("string slices have no C equivalent"),
help: Some(fluent::lint_improper_ctypes_str_help), help: Some(inline_fluent!("consider using `*const u8` and a length instead")),
}, },
ty::Tuple(..) => FfiUnsafe { ty::Tuple(..) => FfiUnsafe {
ty, ty,
reason: fluent::lint_improper_ctypes_tuple_reason, reason: inline_fluent!("tuples have unspecified layout"),
help: Some(fluent::lint_improper_ctypes_tuple_help), help: Some(inline_fluent!("consider using a struct instead")),
}, },
ty::RawPtr(ty, _) | ty::Ref(_, ty, _) ty::RawPtr(ty, _) | ty::Ref(_, ty, _)
@ -632,8 +648,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
if sig.abi().is_rustic_abi() { if sig.abi().is_rustic_abi() {
return FfiUnsafe { return FfiUnsafe {
ty, ty,
reason: fluent::lint_improper_ctypes_fnptr_reason, reason: inline_fluent!(
help: Some(fluent::lint_improper_ctypes_fnptr_help), "this function pointer has Rust-specific calling convention"
),
help: Some(inline_fluent!(
"consider using an `extern fn(...) -> ...` function pointer instead"
)),
}; };
} }
@ -657,9 +677,11 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
// While opaque types are checked for earlier, if a projection in a struct field // While opaque types are checked for earlier, if a projection in a struct field
// normalizes to an opaque type, then it will reach this branch. // normalizes to an opaque type, then it will reach this branch.
ty::Alias(ty::Opaque, ..) => { ty::Alias(ty::Opaque, ..) => FfiUnsafe {
FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_opaque, help: None } ty,
} reason: inline_fluent!("opaque types have no C equivalent"),
help: None,
},
// `extern "C" fn` functions can have type parameters, which may or may not be FFI-safe, // `extern "C" fn` functions can have type parameters, which may or may not be FFI-safe,
// so they are currently ignored for the purposes of this lint. // so they are currently ignored for the purposes of this lint.
@ -669,9 +691,13 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
FfiSafe FfiSafe
} }
ty::UnsafeBinder(_) => { ty::UnsafeBinder(_) => FfiUnsafe {
FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_unsafe_binder, help: None } ty,
} reason: inline_fluent!(
"unsafe binders are incompatible with foreign function interfaces"
),
help: None,
},
ty::Param(..) ty::Param(..)
| ty::Alias(ty::Projection | ty::Inherent | ty::Free, ..) | ty::Alias(ty::Projection | ty::Inherent | ty::Free, ..)
@ -715,7 +741,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
{ {
Some(FfiResult::FfiUnsafe { Some(FfiResult::FfiUnsafe {
ty, ty,
reason: fluent::lint_improper_ctypes_opaque, reason: inline_fluent!("opaque types have no C equivalent"),
help: None, help: None,
}) })
} else { } else {
@ -728,8 +754,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
if let ty::Array(..) = ty.kind() { if let ty::Array(..) = ty.kind() {
Some(FfiResult::FfiUnsafe { Some(FfiResult::FfiUnsafe {
ty, ty,
reason: fluent::lint_improper_ctypes_array_reason, reason: inline_fluent!("passing raw arrays by value is not FFI-safe"),
help: Some(fluent::lint_improper_ctypes_array_help), help: Some(inline_fluent!("consider passing a pointer to the array")),
}) })
} else { } else {
None None
@ -908,7 +934,7 @@ impl<'tcx> ImproperCTypesLint {
cx, cx,
ty, ty,
sp, sp,
fluent::lint_improper_ctypes_only_phantomdata, inline_fluent!("composed only of `PhantomData`"),
None, None,
fn_mode, fn_mode,
); );