Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions compiler/rustc_attr_parsing/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -242,8 +242,6 @@ attr_parsing_link_requires_name =
attr_parsing_multiple_modifiers =
multiple `{$modifier}` modifiers in a single `modifiers` argument
attr_parsing_multiple_renamings =
multiple renamings were specified for library `{$lib_name}`
attr_parsing_raw_dylib_no_nul =
link name must not contain NUL characters if link kind is `raw-dylib`
Expand Down
22 changes: 12 additions & 10 deletions compiler/rustc_const_eval/src/const_eval/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,20 +44,22 @@ impl MachineStopType for ConstEvalErrKind {
fn diagnostic_message(&self) -> DiagMessage {
use ConstEvalErrKind::*;

use crate::fluent_generated::*;
use crate::fluent_generated as fluent;
match self {
ConstAccessesMutGlobal => const_eval_const_accesses_mut_global,
ModifiedGlobal => const_eval_modified_global,
Panic { .. } => const_eval_panic,
RecursiveStatic => const_eval_recursive_static,
ConstAccessesMutGlobal => fluent::const_eval_const_accesses_mut_global,
ModifiedGlobal => fluent::const_eval_modified_global,
Panic { .. } => fluent::const_eval_panic,
RecursiveStatic => fluent::const_eval_recursive_static,
AssertFailure(x) => x.diagnostic_message(),
WriteThroughImmutablePointer => const_eval_write_through_immutable_pointer,
WriteThroughImmutablePointer => fluent::const_eval_write_through_immutable_pointer,
ConstMakeGlobalPtrAlreadyMadeGlobal { .. } => {
const_eval_const_make_global_ptr_already_made_global
fluent::const_eval_const_make_global_ptr_already_made_global
}
ConstMakeGlobalPtrIsNonHeap(_) => const_eval_const_make_global_ptr_is_non_heap,
ConstMakeGlobalWithDanglingPtr(_) => const_eval_const_make_global_with_dangling_ptr,
ConstMakeGlobalWithOffset(_) => const_eval_const_make_global_with_offset,
ConstMakeGlobalPtrIsNonHeap(_) => fluent::const_eval_const_make_global_ptr_is_non_heap,
ConstMakeGlobalWithDanglingPtr(_) => {
fluent::const_eval_const_make_global_with_dangling_ptr
}
ConstMakeGlobalWithOffset(_) => fluent::const_eval_const_make_global_with_offset,
}
}
fn add_args(self: Box<Self>, adder: &mut dyn FnMut(DiagArgName, DiagArgValue)) {
Expand Down
188 changes: 92 additions & 96 deletions compiler/rustc_const_eval/src/errors.rs

Large diffs are not rendered by default.

6 changes: 0 additions & 6 deletions compiler/rustc_metadata/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -237,11 +237,5 @@ metadata_unknown_target_modifier_unsafe_allowed = unknown target modifier `{$fla
metadata_wasm_c_abi =
older versions of the `wasm-bindgen` crate are incompatible with current versions of Rust; please update to `wasm-bindgen` v0.2.88
metadata_wasm_import_form =
wasm import module must be of the form `wasm_import_module = "string"`
metadata_whole_archive_needs_static =
linking modifier `whole-archive` is only compatible with `static` linking kind
metadata_raw_dylib_malformed =
link name must be well-formed if link kind is `raw-dylib`
38 changes: 19 additions & 19 deletions compiler/rustc_middle/src/mir/terminator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -336,18 +336,18 @@ impl<O> AssertKind<O> {
pub fn diagnostic_message(&self) -> DiagMessage {
use AssertKind::*;

use crate::fluent_generated::*;
use crate::fluent_generated as fluent;

match self {
BoundsCheck { .. } => middle_bounds_check,
Overflow(BinOp::Shl, _, _) => middle_assert_shl_overflow,
Overflow(BinOp::Shr, _, _) => middle_assert_shr_overflow,
Overflow(_, _, _) => middle_assert_op_overflow,
OverflowNeg(_) => middle_assert_overflow_neg,
DivisionByZero(_) => middle_assert_divide_by_zero,
RemainderByZero(_) => middle_assert_remainder_by_zero,
BoundsCheck { .. } => fluent::middle_bounds_check,
Overflow(BinOp::Shl, _, _) => fluent::middle_assert_shl_overflow,
Overflow(BinOp::Shr, _, _) => fluent::middle_assert_shr_overflow,
Overflow(_, _, _) => fluent::middle_assert_op_overflow,
OverflowNeg(_) => fluent::middle_assert_overflow_neg,
DivisionByZero(_) => fluent::middle_assert_divide_by_zero,
RemainderByZero(_) => fluent::middle_assert_remainder_by_zero,
ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
middle_assert_async_resume_after_return
fluent::middle_assert_async_resume_after_return
}
ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
todo!()
Expand All @@ -356,36 +356,36 @@ impl<O> AssertKind<O> {
bug!("gen blocks can be resumed after they return and will keep returning `None`")
}
ResumedAfterReturn(CoroutineKind::Coroutine(_)) => {
middle_assert_coroutine_resume_after_return
fluent::middle_assert_coroutine_resume_after_return
}
ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
middle_assert_async_resume_after_panic
fluent::middle_assert_async_resume_after_panic
}
ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
todo!()
}
ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
middle_assert_gen_resume_after_panic
fluent::middle_assert_gen_resume_after_panic
}
ResumedAfterPanic(CoroutineKind::Coroutine(_)) => {
middle_assert_coroutine_resume_after_panic
fluent::middle_assert_coroutine_resume_after_panic
}
NullPointerDereference => middle_assert_null_ptr_deref,
InvalidEnumConstruction(_) => middle_assert_invalid_enum_construction,
NullPointerDereference => fluent::middle_assert_null_ptr_deref,
InvalidEnumConstruction(_) => fluent::middle_assert_invalid_enum_construction,
ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
middle_assert_async_resume_after_drop
fluent::middle_assert_async_resume_after_drop
}
ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
todo!()
}
ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
middle_assert_gen_resume_after_drop
fluent::middle_assert_gen_resume_after_drop
}
ResumedAfterDrop(CoroutineKind::Coroutine(_)) => {
middle_assert_coroutine_resume_after_drop
fluent::middle_assert_coroutine_resume_after_drop
}

MisalignedPointerDereference { .. } => middle_assert_misaligned_ptr_deref,
MisalignedPointerDereference { .. } => fluent::middle_assert_misaligned_ptr_deref,
}
}

Expand Down
1 change: 0 additions & 1 deletion compiler/rustc_parse/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -868,7 +868,6 @@ parse_too_many_hashes = too many `#` symbols: raw strings may be delimited by up
parse_too_short_hex_escape = numeric character escape is too short
parse_trailing_vert_not_allowed = a trailing `{$token}` is not allowed in an or-pattern
parse_trailing_vert_not_allowed_suggestion = remove the `{$token}`
parse_trait_alias_cannot_be_auto = trait aliases cannot be `auto`
parse_trait_alias_cannot_be_const = trait aliases cannot be `const`
Expand Down
4 changes: 0 additions & 4 deletions compiler/rustc_ty_utils/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,10 @@ ty_utils_non_primitive_simd_type = monomorphising SIMD type `{$ty}` with a non-p
ty_utils_operation_not_supported = unsupported operation in generic constants
ty_utils_oversized_simd_type = monomorphising SIMD type `{$ty}` of length greater than {$max_lanes}
ty_utils_pointer_not_supported = pointer casts are not allowed in generic constants
ty_utils_tuple_not_supported = tuple construction is not supported in generic constants
ty_utils_unexpected_fnptr_associated_item = `FnPtr` trait with unexpected associated item
ty_utils_yield_not_supported = coroutine control flow is not allowed in generic constants
ty_utils_zero_length_simd_type = monomorphising SIMD type `{$ty}` of zero length
96 changes: 96 additions & 0 deletions src/tools/tidy/src/error_messages.rs
Copy link
Member

@RalfJung RalfJung Sep 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This checks all fluent messages, right? Many of them are not errors but lints. So something like "fluent_messages" or so is probably a better name for this module.

EDIT: There's already several checks called fluent_something so fluent_messages is probably not specific enough.

Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
use std::collections::HashMap;
use std::path::Path;

use fluent_syntax::ast::{Entry, Expression, InlineExpression, PatternElement};
use fluent_syntax::parser;
use regex::Regex;

use crate::diagnostics::{CheckId, DiagCtx, RunningCheck};

pub fn check(root_path: &Path, diag_ctx: DiagCtx) {
let path = &root_path.join("compiler");
let mut check = diag_ctx.start_check(CheckId::new("error_messages").path(path));
let regex = Regex::new(r#"(?:(?:#\[(diag|label|suggestion|note|help|help_once|multipart_suggestion)\()|(?:fluent(_generated)?::))\s*(?<id>[a-z_][a-zA-Z0-9_]+)\s*[\n,\)};]"#).unwrap();

for dir in std::fs::read_dir(path).unwrap() {
let Ok(dir) = dir else { continue };
let dir = dir.path();
let messages_file = dir.join("messages.ftl");

if messages_file.is_file() {
check_if_messages_are_used(&mut check, &dir.join("src"), &messages_file, &regex);
}
}
}

fn check_fluent_ast<'a>(ids: &mut HashMap<&'a str, bool>, elem: &PatternElement<&'a str>) {
if let PatternElement::Placeable { expression } = elem {
match expression {
Expression::Inline(InlineExpression::MessageReference { id, .. }) => {
*ids.entry(&id.name).or_default() = true;
}
Expression::Select { variants, .. } => {
for variant in variants {
for elem in &variant.value.elements {
check_fluent_ast(ids, elem);
}
}
}
_ => {}
}
}
}

fn check_if_messages_are_used(
check: &mut RunningCheck,
src_path: &Path,
messages_file: &Path,
regex: &Regex,
) {
// First we retrieve all error messages ID.
let content = std::fs::read_to_string(messages_file).expect("failed to read file");
let resource =
parser::parse(content.as_str()).expect("Errors encountered while parsing a resource.");

let mut ids: HashMap<&str, bool> = HashMap::new();
for entry in &resource.body {
if let Entry::Message(msg) = entry {
let id: &str = &msg.id.name;
if !ids.contains_key(&id) {
ids.insert(id, false);
}
if let Some(value) = &msg.value {
for elem in &value.elements {
check_fluent_ast(&mut ids, elem);
}
}
for attr in &msg.attributes {
for elem in &attr.value.elements {
check_fluent_ast(&mut ids, elem);
}
}
}
}

assert!(!ids.is_empty());

let skip = |f: &Path, is_dir: bool| !is_dir && !f.extension().is_some_and(|ext| ext == "rs");
crate::walk::walk(src_path, skip, &mut |_path: &_, content: &str| {
for cap in regex.captures_iter(content) {
let id = &cap["id"];
if let Some(found) = ids.get_mut(id) {
// Error message IDs can be used more than once.
*found = true;
}
}
});
const TO_IGNORE: &[&str] = &[
// FIXME: #114050
"hir_typeck_option_result_asref",
];
for (id, found) in ids {
if !found && !TO_IGNORE.iter().any(|to_ignore| *to_ignore == id) {
check.error(format!("unused message ID `{id}` from `{}`", messages_file.display()));
}
}
}
12 changes: 12 additions & 0 deletions src/tools/tidy/src/extra_checks/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ pub fn check(
bless,
extra_checks,
pos_args,
diag_ctx,
) {
check.error(e);
}
Expand All @@ -86,6 +87,7 @@ fn check_impl(
bless: bool,
extra_checks: Option<&str>,
pos_args: &[String],
diag_ctx: DiagCtx,
) -> Result<(), Error> {
let show_diff =
std::env::var("TIDY_PRINT_DIFF").is_ok_and(|v| v.eq_ignore_ascii_case("true") || v == "1");
Expand Down Expand Up @@ -138,6 +140,7 @@ fn check_impl(
let shell_lint = extra_check!(Shell, Lint);
let cpp_fmt = extra_check!(Cpp, Fmt);
let spellcheck = extra_check!(Spellcheck, None);
let fluent_check = extra_check!(Ftl, None);
let js_lint = extra_check!(Js, Lint);
let js_typecheck = extra_check!(Js, Typecheck);

Expand Down Expand Up @@ -346,6 +349,11 @@ fn check_impl(
rustdoc_js::typecheck(outdir, librustdoc_path)?;
}

if fluent_check {
eprintln!("checking ftl files");
crate::error_messages::check(&root_path, diag_ctx);
}

Ok(())
}

Expand Down Expand Up @@ -766,6 +774,7 @@ impl ExtraCheckArg {
}
&[]
}
ExtraCheckLang::Ftl => &[".ftl"],
};
exts.iter().any(|ext| filepath.ends_with(ext))
}
Expand All @@ -782,6 +791,7 @@ impl ExtraCheckArg {
ExtraCheckLang::Shell => &[Lint],
ExtraCheckLang::Spellcheck => &[],
ExtraCheckLang::Js => &[Lint, Typecheck],
ExtraCheckLang::Ftl => &[],
};
supported_kinds.contains(&kind)
}
Expand Down Expand Up @@ -823,6 +833,7 @@ enum ExtraCheckLang {
Cpp,
Spellcheck,
Js,
Ftl,
}

impl FromStr for ExtraCheckLang {
Expand All @@ -835,6 +846,7 @@ impl FromStr for ExtraCheckLang {
"cpp" => Self::Cpp,
"spellcheck" => Self::Spellcheck,
"js" => Self::Js,
"ftl" => Self::Ftl,
_ => return Err(ExtraCheckParseError::UnknownLang(s.to_string())),
})
}
Expand Down
1 change: 1 addition & 0 deletions src/tools/tidy/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ pub mod deps;
pub mod diagnostics;
pub mod edition;
pub mod error_codes;
pub mod error_messages;
pub mod extdeps;
pub mod extra_checks;
pub mod features;
Expand Down
Loading