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_errors",
"rustc_feature",
"rustc_fluent_macro",
"rustc_hir",
"rustc_index",
"rustc_infer",

View file

@ -111,11 +111,7 @@ pub fn default_translator() -> Translator {
Translator::with_fallback_bundle(DEFAULT_LOCALE_RESOURCES.to_vec(), false)
}
pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[
// tidy-alphabetical-start
rustc_lint::DEFAULT_LOCALE_RESOURCE,
// tidy-alphabetical-end
];
pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[];
/// Exit status code used for successful compilation and help output.
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_errors = { path = "../rustc_errors" }
rustc_feature = { path = "../rustc_feature" }
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
rustc_hir = { path = "../rustc_hir" }
rustc_index = { path = "../rustc_index" }
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)]
#[diag(lint_closure_returning_async_block)]
#[diag("closure returning async block can be made into an async closure")]
struct ClosureReturningAsyncBlock {
#[label]
#[label(
"this async block can be removed, and the closure can be turned into an async closure"
)]
async_decl_span: Span,
#[subdiagnostic]
sugg: AsyncClosureSugg,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(lint_suggestion, applicability = "maybe-incorrect")]
#[multipart_suggestion("turn this into an async closure", applicability = "maybe-incorrect")]
struct AsyncClosureSugg {
#[suggestion_part(code = "")]
deletion_span: Span,

View file

@ -22,7 +22,7 @@ use rustc_ast::visit::{FnCtxt, FnKind};
use rustc_ast::{self as ast, *};
use rustc_ast_pretty::pprust::expr_to_string;
use rustc_attr_parsing::AttributeParser;
use rustc_errors::{Applicability, LintDiagnostic};
use rustc_errors::{Applicability, LintDiagnostic, inline_fluent};
use rustc_feature::GateIssue;
use rustc_hir as hir;
use rustc_hir::attrs::{AttributeKind, DocAttribute};
@ -61,10 +61,7 @@ use crate::lints::{
BuiltinUnreachablePub, BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment,
BuiltinUnusedDocCommentSub, BuiltinWhileTrue, InvalidAsmLabel,
};
use crate::{
EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext,
fluent_generated as fluent,
};
use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext};
declare_lint! {
/// 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);
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_uninit,
InitKind::Zeroed => {
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 };
cx.emit_span_lint(

View file

@ -1,17 +1,15 @@
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_session::lint::Level;
use rustc_span::{Span, Symbol};
use crate::fluent_generated as fluent;
#[derive(Diagnostic)]
#[diag(lint_overruled_attribute, code = E0453)]
#[diag("{$lint_level}({$lint_source}) incompatible with previous forbid", code = E0453)]
pub(crate) struct OverruledAttribute<'a> {
#[primary_span]
pub span: Span,
#[label]
#[label("overruled by previous forbid")]
pub overruled: Span,
pub lint_level: &'a str,
pub lint_source: Symbol,
@ -29,24 +27,24 @@ impl Subdiagnostic for OverruledAttributeSub {
fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
match self {
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);
}
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 {
diag.note(rationale.to_string());
}
}
OverruledAttributeSub::CommandLineSource => {
diag.note(fluent::lint_command_line_source);
diag.note(inline_fluent!("`forbid` lint level was set on command line"));
}
}
}
}
#[derive(Diagnostic)]
#[diag(lint_malformed_attribute, code = E0452)]
#[diag("malformed lint attribute input", code = E0452)]
pub(crate) struct MalformedAttribute {
#[primary_span]
pub span: Span,
@ -56,50 +54,55 @@ pub(crate) struct MalformedAttribute {
#[derive(Subdiagnostic)]
pub(crate) enum MalformedAttributeSub {
#[label(lint_bad_attribute_argument)]
#[label("bad attribute argument")]
BadAttributeArgument(#[primary_span] Span),
#[label(lint_reason_must_be_string_literal)]
#[label("reason must be a string literal")]
ReasonMustBeStringLiteral(#[primary_span] Span),
#[label(lint_reason_must_come_last)]
#[label("reason in lint attribute must come last")]
ReasonMustComeLast(#[primary_span] Span),
}
#[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 {
#[primary_span]
pub span: Option<Span>,
pub tool_name: Symbol,
pub lint_name: String,
#[help]
#[help("add `#![register_tool({$tool_name})]` to the crate root")]
pub is_nightly_build: bool,
}
#[derive(Diagnostic)]
#[diag(lint_builtin_ellipsis_inclusive_range_patterns, code = E0783)]
#[diag("`...` range patterns are deprecated", code = E0783)]
pub(crate) struct BuiltinEllipsisInclusiveRangePatterns {
#[primary_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 replace: String,
}
#[derive(Subdiagnostic)]
#[note(lint_requested_level)]
#[note("requested on the command line with `{$level} {$lint_name}`")]
pub(crate) struct RequestedLevel<'a> {
pub level: Level,
pub lint_name: &'a str,
}
#[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 lint_group: String,
}
#[derive(Diagnostic)]
#[diag(lint_check_name_unknown_tool, code = E0602)]
#[diag("unknown lint tool: `{$tool_name}`", code = E0602)]
pub(crate) struct CheckNameUnknownTool<'a> {
pub tool_name: Symbol,
#[subdiagnostic]

View file

@ -3,7 +3,9 @@ use std::ops::ControlFlow;
use hir::intravisit::{self, Visitor};
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_macros::{LintDiagnostic, Subdiagnostic};
use rustc_middle::ty::adjustment::Adjust;
@ -303,13 +305,15 @@ impl<'tcx> LateLintPass<'tcx> for IfLetRescope {
}
#[derive(LintDiagnostic)]
#[diag(lint_if_let_rescope)]
#[diag("`if let` assigns a shorter lifetime since Edition 2024")]
struct IfLetRescopeLint {
#[subdiagnostic]
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>,
#[help]
#[help("the value is now dropped here in Edition 2024")]
lifetime_ends: Vec<Span>,
#[subdiagnostic]
rewrite: Option<IfLetRescopeRewrite>,
@ -352,7 +356,9 @@ impl Subdiagnostic for IfLetRescopeRewrite {
.chain(repeat_n('}', closing_brackets.count))
.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(
msg,
suggestions,
@ -363,7 +369,12 @@ impl Subdiagnostic for IfLetRescopeRewrite {
}
#[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 {
#[primary_span]
span: Span,

View file

@ -3,7 +3,7 @@ use std::cell::LazyCell;
use rustc_data_structures::debug_assert_matches;
use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
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::def::DefKind;
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::traits::ObligationCtxt;
use crate::{LateContext, LateLintPass, fluent_generated as fluent};
use crate::{LateContext, LateLintPass};
declare_lint! {
/// The `impl_trait_overcaptures` lint warns against cases where lifetime
@ -435,11 +435,23 @@ struct ImplTraitOvercapturesLint<'tcx> {
impl<'a> LintDiagnostic<'a, ()> for ImplTraitOvercapturesLint<'_> {
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())
.arg("num_captured", self.num_captured)
.span_note(self.uncaptured_spans, fluent::lint_note)
.note(fluent::lint_note2);
.span_note(
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 {
suggestion.add_to_diag(diag);
}
@ -447,9 +459,9 @@ impl<'a> LintDiagnostic<'a, ()> for ImplTraitOvercapturesLint<'_> {
}
#[derive(LintDiagnostic)]
#[diag(lint_impl_trait_redundant_captures)]
#[diag("all possible in-scope parameters are already captured, so `use<...>` syntax is redundant")]
struct ImplTraitRedundantCapturesLint {
#[suggestion(lint_suggestion, code = "", applicability = "machine-applicable")]
#[suggestion("remove the `use<...>` syntax", code = "", applicability = "machine-applicable")]
capturing_span: Span,
}

View file

@ -2,7 +2,7 @@ use rustc_ast::attr::AttributeExt;
use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
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_hir::HirId;
use rustc_hir::intravisit::{self, Visitor};
@ -31,7 +31,6 @@ use crate::errors::{
CheckNameUnknownTool, MalformedAttribute, MalformedAttributeSub, OverruledAttribute,
OverruledAttributeSub, RequestedLevel, UnknownToolInScopedLint, UnsupportedGroup,
};
use crate::fluent_generated as fluent;
use crate::late::unerased_lint_store;
use crate::lints::{
DeprecatedLintName, DeprecatedLintNameFromCommandLine, IgnoredUnlessCrateSpecified,
@ -942,9 +941,9 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
let lint = builtin::UNKNOWN_LINTS;
let level = self.lint_level(builtin::UNKNOWN_LINTS);
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.note(fluent::lint_note);
lint.note(inline_fluent!("the `{$name}` lint is unstable"));
rustc_session::parse::add_feature_diagnostics_for_issue(
lint,
&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::{FutureIncompatibleInfo, Lint, LintId, LintPass, LintVec};
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
pub fn provide(providers: &mut Providers) {
levels::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_errors::Applicability;
use rustc_errors::{Applicability, inline_fluent};
use rustc_hir::{self as hir, LangItem};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::{bug, ty};
@ -10,7 +10,7 @@ use rustc_span::{InnerSpan, Span, Symbol, hygiene, sym};
use rustc_trait_selection::infer::InferCtxtExt;
use crate::lints::{NonFmtPanicBraces, NonFmtPanicUnused};
use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent};
use crate::{LateContext, LateLintPass, LintContext};
declare_lint! {
/// 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| {
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.note(fluent::lint_note);
lint.note(fluent::lint_more_info_note);
lint.note(inline_fluent!("this usage of `{$name}!()` is deprecated; it will be a hard error in Rust 2021"));
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) {
// No clue where this argument is coming from.
return;
}
if arg_macro.is_some_and(|id| cx.tcx.is_diagnostic_item(sym::format_macro, id)) {
// 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) {
lint.multipart_suggestion(
fluent::lint_supports_fmt_suggestion,
inline_fluent!("remove the `format!(..)` macro call"),
vec![
(arg_span.until(open.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 {
lint.span_suggestion_verbose(
arg_span.shrink_to_lo(),
fluent::lint_display_suggestion,
inline_fluent!(r#"add a "{"{"}{"}"}" format string to `Display` the message"#),
"\"{}\", ",
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.span_suggestion_verbose(
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,
);
@ -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) {
lint.arg("already_suggested", suggest_display || suggest_debug);
lint.multipart_suggestion(
fluent::lint_panic_suggestion,
inline_fluent!("{$already_suggested ->
[true] or use
*[false] use
} std::panic::panic_any instead"),
if del == '(' {
vec![(span.until(open), "std::panic::panic_any".into())]
} 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::def::{DefKind, Res};
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 crate::lints::{NonLocalDefinitionsCargoUpdateNote, NonLocalDefinitionsDiag};
use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent};
use crate::{LateContext, LateLintPass, LintContext};
declare_lint! {
/// The `non_local_definitions` lint checks for `impl` blocks and `#[macro_export]`
@ -210,7 +210,12 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
if !doctest {
ms.push_span_label(
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)]
#[diag(lint_opaque_hidden_inferred_bound)]
#[diag("opaque type `{$ty}` does not satisfy its associated type bounds")]
struct OpaqueHiddenInferredBoundLint<'tcx> {
ty: Ty<'tcx>,
proj_ty: Ty<'tcx>,
#[label(lint_specifically)]
#[label("this associated type bound is unsatisfied for `{$proj_ty}`")]
assoc_pred_span: Span,
#[subdiagnostic]
add_bound: Option<AddBound<'tcx>>,
@ -214,7 +214,7 @@ struct OpaqueHiddenInferredBoundLint<'tcx> {
#[derive(Subdiagnostic)]
#[suggestion(
lint_opaque_hidden_inferred_bound_sugg,
"add this bound",
style = "verbose",
applicability = "machine-applicable",
code = " + {trait_ref}"

View file

@ -369,8 +369,10 @@ fn check_unnecessary_transmute<'tcx>(
}
#[derive(LintDiagnostic)]
#[diag(lint_undefined_transmute)]
#[note]
#[note(lint_note2)]
#[help]
#[diag("pointers cannot be transmuted to integers during const eval")]
#[note("at compile-time, pointers do not have an integer value")]
#[note(
"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;

View file

@ -4,7 +4,7 @@ use std::ops::ControlFlow;
use bitflags::bitflags;
use rustc_abi::VariantIdx;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::DiagMessage;
use rustc_errors::{DiagMessage, inline_fluent};
use rustc_hir::def::CtorKind;
use rustc_hir::intravisit::VisitorExt;
use rustc_hir::{self as hir, AmbigArg};
@ -21,7 +21,7 @@ use tracing::debug;
use super::repr_nullable_ptr;
use crate::lints::{ImproperCTypes, UsesPowerAlignment};
use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent};
use crate::{LateContext, LateLintPass, LintContext};
declare_lint! {
/// 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), }`
// but exempt enums with unit ctors like C's (e.g. from rust-bindgen)
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() {
return ControlFlow::Break(fluent::lint_improper_ctypes_non_exhaustive_variant);
return ControlFlow::Break(inline_fluent!("this enum has non-exhaustive variants"));
}
ControlFlow::Continue(())
@ -424,7 +424,11 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
if all_phantom {
FfiPhantom(ty)
} 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 {
FfiSafe
}
@ -460,7 +464,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
} else {
return FfiUnsafe {
ty,
reason: fluent::lint_improper_ctypes_box,
reason: inline_fluent!("box cannot be represented as a single pointer"),
help: None,
};
}
@ -476,8 +480,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
{
return FfiUnsafe {
ty,
reason: fluent::lint_improper_ctypes_cstr_reason,
help: Some(fluent::lint_improper_ctypes_cstr_help),
reason: inline_fluent!(
"`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 {
ty,
reason: if def.is_struct() {
fluent::lint_improper_ctypes_struct_layout_reason
inline_fluent!("this struct has unspecified layout")
} else {
fluent::lint_improper_ctypes_union_layout_reason
inline_fluent!("this union has unspecified layout")
},
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 {
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 {
ty,
reason: if def.is_struct() {
fluent::lint_improper_ctypes_struct_non_exhaustive
inline_fluent!("this struct is non-exhaustive")
} else {
fluent::lint_improper_ctypes_union_non_exhaustive
inline_fluent!("this union is non-exhaustive")
},
help: None,
};
@ -513,14 +525,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
return FfiUnsafe {
ty,
reason: if def.is_struct() {
fluent::lint_improper_ctypes_struct_fieldless_reason
inline_fluent!("this struct has no fields")
} else {
fluent::lint_improper_ctypes_union_fieldless_reason
inline_fluent!("this union has no fields")
},
help: if def.is_struct() {
Some(fluent::lint_improper_ctypes_struct_fieldless_help)
Some(inline_fluent!("consider adding a member to this struct"))
} 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 {
ty,
reason: fluent::lint_improper_ctypes_enum_repr_reason,
help: Some(fluent::lint_improper_ctypes_enum_repr_help),
reason: inline_fluent!("enum has no representation hint"),
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,
reason: fluent::lint_improper_ctypes_char_reason,
help: Some(fluent::lint_improper_ctypes_char_help),
reason: inline_fluent!("the `char` type has no C equivalent"),
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,
@ -585,24 +599,26 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
ty::Slice(_) => FfiUnsafe {
ty,
reason: fluent::lint_improper_ctypes_slice_reason,
help: Some(fluent::lint_improper_ctypes_slice_help),
reason: inline_fluent!("slices have no C equivalent"),
help: Some(inline_fluent!("consider using a raw pointer instead")),
},
ty::Dynamic(..) => {
FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_dyn, help: None }
}
ty::Dynamic(..) => FfiUnsafe {
ty,
reason: inline_fluent!("trait objects have no C equivalent"),
help: None,
},
ty::Str => FfiUnsafe {
ty,
reason: fluent::lint_improper_ctypes_str_reason,
help: Some(fluent::lint_improper_ctypes_str_help),
reason: inline_fluent!("string slices have no C equivalent"),
help: Some(inline_fluent!("consider using `*const u8` and a length instead")),
},
ty::Tuple(..) => FfiUnsafe {
ty,
reason: fluent::lint_improper_ctypes_tuple_reason,
help: Some(fluent::lint_improper_ctypes_tuple_help),
reason: inline_fluent!("tuples have unspecified layout"),
help: Some(inline_fluent!("consider using a struct instead")),
},
ty::RawPtr(ty, _) | ty::Ref(_, ty, _)
@ -632,8 +648,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
if sig.abi().is_rustic_abi() {
return FfiUnsafe {
ty,
reason: fluent::lint_improper_ctypes_fnptr_reason,
help: Some(fluent::lint_improper_ctypes_fnptr_help),
reason: inline_fluent!(
"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
// normalizes to an opaque type, then it will reach this branch.
ty::Alias(ty::Opaque, ..) => {
FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_opaque, help: None }
}
ty::Alias(ty::Opaque, ..) => FfiUnsafe {
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,
// so they are currently ignored for the purposes of this lint.
@ -669,9 +691,13 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
FfiSafe
}
ty::UnsafeBinder(_) => {
FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_unsafe_binder, help: None }
}
ty::UnsafeBinder(_) => FfiUnsafe {
ty,
reason: inline_fluent!(
"unsafe binders are incompatible with foreign function interfaces"
),
help: None,
},
ty::Param(..)
| ty::Alias(ty::Projection | ty::Inherent | ty::Free, ..)
@ -715,7 +741,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
{
Some(FfiResult::FfiUnsafe {
ty,
reason: fluent::lint_improper_ctypes_opaque,
reason: inline_fluent!("opaque types have no C equivalent"),
help: None,
})
} else {
@ -728,8 +754,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
if let ty::Array(..) = ty.kind() {
Some(FfiResult::FfiUnsafe {
ty,
reason: fluent::lint_improper_ctypes_array_reason,
help: Some(fluent::lint_improper_ctypes_array_help),
reason: inline_fluent!("passing raw arrays by value is not FFI-safe"),
help: Some(inline_fluent!("consider passing a pointer to the array")),
})
} else {
None
@ -908,7 +934,7 @@ impl<'tcx> ImproperCTypesLint {
cx,
ty,
sp,
fluent::lint_improper_ctypes_only_phantomdata,
inline_fluent!("composed only of `PhantomData`"),
None,
fn_mode,
);