Rollup merge of #152276 - JonathanBrouwer:fmt-check, r=jdonszelmann

Add message format checking

Checks the indentation of diagnostic messages, it checks that everything is at least as indented as the attribute that contains it

r? @jdonszelmann (Anyone else, also feel free to review, just assigning to Jana because she's been reviewing the other PRs)
This commit is contained in:
Jacob Pratt
2026-02-09 05:20:29 -05:00
committed by GitHub
9 changed files with 163 additions and 127 deletions
+23 -23
View File
@@ -106,9 +106,9 @@ pub(crate) struct UnstableInStableExposed {
pub is_function_call2: bool,
#[suggestion(
"if the {$is_function_call2 ->
[true] caller
*[false] function
} is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]`",
[true] caller
*[false] function
} is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]`",
code = "#[rustc_const_unstable(feature = \"...\", issue = \"...\")]\n",
applicability = "has-placeholders"
)]
@@ -300,11 +300,11 @@ pub(crate) struct UnallowedHeapAllocations {
#[primary_span]
#[label(
r#"allocation not allowed in {$kind ->
[const] constant
[static] static
[const_fn] constant function
*[other] {""}
}s"#
[const] constant
[static] static
[const_fn] constant function
*[other] {""}
}s"#
)]
pub span: Span,
pub kind: ConstContext,
@@ -539,20 +539,20 @@ pub enum NonConstClosureNote {
},
#[note(
r#"function pointers need an RFC before allowed to be called in {$kind ->
[const] constant
[static] static
[const_fn] constant function
*[other] {""}
}s"#
[const] constant
[static] static
[const_fn] constant function
*[other] {""}
}s"#
)]
FnPtr,
#[note(
r#"closures need an RFC before allowed to be called in {$kind ->
[const] constant
[static] static
[const_fn] constant function
*[other] {""}
}s"#
[const] constant
[static] static
[const_fn] constant function
*[other] {""}
}s"#
)]
Closure,
}
@@ -608,11 +608,11 @@ pub struct LiveDrop<'tcx> {
#[primary_span]
#[label(
r#"the destructor for this type cannot be evaluated in {$kind ->
[const] constant
[static] static
[const_fn] constant function
*[other] {""}
}s"#
[const] constant
[static] static
[const_fn] constant function
*[other] {""}
}s"#
)]
pub span: Span,
pub kind: ConstContext,
@@ -203,8 +203,11 @@ fn generate_structure_code_for_attr(
)
.emit();
}
self.message =
Some(Message { message_span: message.span(), value: message.value() });
self.message = Some(Message {
attr_span: attr.span(),
message_span: message.span(),
value: message.value(),
});
}
// Parse arguments
@@ -9,6 +9,7 @@
#[derive(Clone)]
pub(crate) struct Message {
pub attr_span: Span,
pub message_span: Span,
pub value: String,
}
@@ -19,12 +20,18 @@ impl Message {
/// For subdiagnostics, we cannot check this.
pub(crate) fn diag_message(&self, variant: Option<&VariantInfo<'_>>) -> TokenStream {
let message = &self.value;
verify_fluent_message(self.message_span, &message, variant);
self.verify(variant);
quote! { rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed(#message)) }
}
fn verify(&self, variant: Option<&VariantInfo<'_>>) {
verify_variables_used(self.message_span, &self.value, variant);
verify_message_style(self.message_span, &self.value);
verify_message_formatting(self.attr_span, self.message_span, &self.value);
}
}
fn verify_fluent_message(msg_span: Span, message_str: &str, variant: Option<&VariantInfo<'_>>) {
fn verify_variables_used(msg_span: Span, message_str: &str, variant: Option<&VariantInfo<'_>>) {
// Parse the fluent message
const GENERATED_MSG_ID: &str = "generated_msg";
let resource =
@@ -53,8 +60,6 @@ fn verify_fluent_message(msg_span: Span, message_str: &str, variant: Option<&Var
}
}
}
verify_message_style(msg_span, message_str);
}
fn variable_references<'a>(msg: &fluent_syntax::ast::Message<&'a str>) -> Vec<&'a str> {
@@ -120,3 +125,29 @@ fn verify_message_style(msg_span: Span, message: &str) {
return;
}
}
/// Verifies that the message is properly indented into the code
fn verify_message_formatting(attr_span: Span, msg_span: Span, message: &str) {
// Find the indent at the start of the message (`column()` is one-indexed)
let start = attr_span.unwrap().column() - 1;
for line in message.lines().skip(1) {
if line.is_empty() {
continue;
}
let indent = line.chars().take_while(|c| *c == ' ').count();
if indent < start {
span_err(
msg_span.unwrap(),
format!("message is not properly indented. {indent} < {start}"),
)
.emit();
return;
}
if indent % 4 != 0 {
span_err(msg_span.unwrap(), "message is not indented with a multiple of 4 spaces")
.emit();
return;
}
}
}
@@ -708,7 +708,7 @@ pub(super) fn from_attr(
}
if !input.is_empty() { input.parse::<Token![,]>()?; }
if is_first {
message = Some(Message { message_span: inline_message.span(), value: inline_message.value() });
message = Some(Message { attr_span: attr.span(), message_span: inline_message.span(), value: inline_message.value() });
is_first = false;
} else {
span_err(inline_message.span().unwrap(), "a diagnostic message must be the first argument to the attribute").emit();
+17 -17
View File
@@ -198,12 +198,12 @@ pub(crate) struct UnsafeOpInUnsafeFnCallToFunctionWithRequiresUnsafe {
pub(crate) missing_target_features: DiagArgValue,
pub(crate) missing_target_features_count: usize,
#[note("the {$build_target_features} target {$build_target_features_count ->
[1] feature
*[count] features
[1] feature
*[count] features
} being enabled in the build configuration does not remove the requirement to list {$build_target_features_count ->
[1] it
*[count] them
} in `#[target_feature]`")]
[1] it
*[count] them
} in `#[target_feature]`")]
pub(crate) note: bool,
pub(crate) build_target_features: DiagArgValue,
pub(crate) build_target_features_count: usize,
@@ -532,12 +532,12 @@ pub(crate) struct CallToFunctionWithRequiresUnsafe {
pub(crate) missing_target_features: DiagArgValue,
pub(crate) missing_target_features_count: usize,
#[note("the {$build_target_features} target {$build_target_features_count ->
[1] feature
*[count] features
} being enabled in the build configuration does not remove the requirement to list {$build_target_features_count ->
[1] it
*[count] them
} in `#[target_feature]`")]
[1] feature
*[count] features
} being enabled in the build configuration does not remove the requirement to list {$build_target_features_count ->
[1] it
*[count] them
} in `#[target_feature]`")]
pub(crate) note: bool,
pub(crate) build_target_features: DiagArgValue,
pub(crate) build_target_features_count: usize,
@@ -1264,9 +1264,9 @@ pub(crate) struct InterpretedAsConstSugg {
pub(crate) enum SuggestLet {
#[multipart_suggestion(
"you might want to use `if let` to ignore the {$count ->
[one] variant that isn't
*[other] variants that aren't
} matched",
[one] variant that isn't
*[other] variants that aren't
} matched",
applicability = "has-placeholders"
)]
If {
@@ -1278,9 +1278,9 @@ pub(crate) enum SuggestLet {
},
#[suggestion(
"you might want to use `let...else` to handle the {$count ->
[one] variant that isn't
*[other] variants that aren't
} matched",
[one] variant that isn't
*[other] variants that aren't
} matched",
code = " else {{ todo!() }}",
applicability = "has-placeholders"
)]
+3 -3
View File
@@ -162,9 +162,9 @@ pub(crate) struct AbiRequiredTargetFeature<'a> {
#[primary_span]
#[label(
"function {$is_call ->
[true] called
*[false] defined
} here"
[true] called
*[false] defined
} here"
)]
pub span: Span,
pub required_feature: &'a str,
+1 -1
View File
@@ -1151,7 +1151,7 @@ pub(crate) enum MatchArmBodyWithoutBracesSugg {
#[multipart_suggestion(
"surround the {$num_statements ->
[one] statement
*[other] statements
*[other] statements
} with a body",
applicability = "machine-applicable"
)]
+2 -2
View File
@@ -1417,8 +1417,8 @@ pub(crate) struct DuplicateEiiImpls {
pub second_crate: Symbol,
#[note("in addition to these two, { $num_additional_crates ->
[one] another implementation was found in crate {$additional_crate_names}
*[other] more implementations were also found in the following crates: {$additional_crate_names}
[one] another implementation was found in crate {$additional_crate_names}
*[other] more implementations were also found in the following crates: {$additional_crate_names}
}")]
pub additional_crates: Option<()>,
+76 -74
View File
@@ -327,18 +327,18 @@ pub struct InferenceBadError<'a> {
pub enum SourceKindSubdiag<'a> {
#[suggestion(
"{$kind ->
[with_pattern] consider giving `{$name}` an explicit type
[closure] consider giving this closure parameter an explicit type
*[other] consider giving this pattern a type
}{$x_kind ->
[has_name] , where the {$prefix_kind ->
*[type] type for {$prefix}
[const_with_param] value of const parameter
[const] value of the constant
} `{$arg_name}` is specified
[underscore] , where the placeholders `_` are specified
*[empty] {\"\"}
}",
[with_pattern] consider giving `{$name}` an explicit type
[closure] consider giving this closure parameter an explicit type
*[other] consider giving this pattern a type
}{$x_kind ->
[has_name] , where the {$prefix_kind ->
*[type] type for {$prefix}
[const_with_param] value of const parameter
[const] value of the constant
} `{$arg_name}` is specified
[underscore] , where the placeholders `_` are specified
*[empty] {\"\"}
}",
style = "verbose",
code = ": {type_name}",
applicability = "has-placeholders"
@@ -356,15 +356,15 @@ pub enum SourceKindSubdiag<'a> {
},
#[label(
"cannot infer {$is_type ->
[true] type
*[false] the value
} of the {$is_type ->
[true] type
*[false] const
} {$parent_exists ->
[true] parameter `{$param_name}` declared on the {$parent_prefix} `{$parent_name}`
*[false] parameter {$param_name}
}"
[true] type
*[false] the value
} of the {$is_type ->
[true] type
*[false] const
} {$parent_exists ->
[true] parameter `{$param_name}` declared on the {$parent_prefix} `{$parent_name}`
*[false] parameter {$param_name}
}"
)]
GenericLabel {
#[primary_span]
@@ -377,9 +377,9 @@ pub enum SourceKindSubdiag<'a> {
},
#[suggestion(
"consider specifying the generic {$arg_count ->
[one] argument
*[other] arguments
}",
[one] argument
*[other] arguments
}",
style = "verbose",
code = "::<{args}>",
applicability = "has-placeholders"
@@ -945,9 +945,9 @@ fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> rustc_errors::D
#[derive(Subdiagnostic)]
pub enum ActualImplExplNotes<'tcx> {
#[note("{$leading_ellipsis ->
[true] ...
*[false] {\"\"}
}closure with signature `{$ty_or_sig}` must implement `{$trait_path}`, for any two lifetimes `'{$lifetime_1}` and `'{$lifetime_2}`...")]
[true] ...
*[false] {\"\"}
}closure with signature `{$ty_or_sig}` must implement `{$trait_path}`, for any two lifetimes `'{$lifetime_1}` and `'{$lifetime_2}`...")]
ExpectedSignatureTwo {
leading_ellipsis: bool,
ty_or_sig: TyOrSig<'tcx>,
@@ -956,9 +956,9 @@ pub enum ActualImplExplNotes<'tcx> {
lifetime_2: usize,
},
#[note("{$leading_ellipsis ->
[true] ...
*[false] {\"\"}
}closure with signature `{$ty_or_sig}` must implement `{$trait_path}`, for any lifetime `'{$lifetime_1}`...")]
[true] ...
*[false] {\"\"}
}closure with signature `{$ty_or_sig}` must implement `{$trait_path}`, for any lifetime `'{$lifetime_1}`...")]
ExpectedSignatureAny {
leading_ellipsis: bool,
ty_or_sig: TyOrSig<'tcx>,
@@ -966,9 +966,9 @@ pub enum ActualImplExplNotes<'tcx> {
lifetime_1: usize,
},
#[note("{$leading_ellipsis ->
[true] ...
*[false] {\"\"}
}closure with signature `{$ty_or_sig}` must implement `{$trait_path}`, for some specific lifetime `'{$lifetime_1}`...")]
[true] ...
*[false] {\"\"}
}closure with signature `{$ty_or_sig}` must implement `{$trait_path}`, for some specific lifetime `'{$lifetime_1}`...")]
ExpectedSignatureSome {
leading_ellipsis: bool,
ty_or_sig: TyOrSig<'tcx>,
@@ -977,9 +977,9 @@ pub enum ActualImplExplNotes<'tcx> {
},
#[note(
"{$leading_ellipsis ->
[true] ...
*[false] {\"\"}
}closure with signature `{$ty_or_sig}` must implement `{$trait_path}`"
[true] ...
*[false] {\"\"}
}closure with signature `{$ty_or_sig}` must implement `{$trait_path}`"
)]
ExpectedSignatureNothing {
leading_ellipsis: bool,
@@ -987,9 +987,9 @@ pub enum ActualImplExplNotes<'tcx> {
trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
},
#[note("{$leading_ellipsis ->
[true] ...
*[false] {\"\"}
}`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}`, for any two lifetimes `'{$lifetime_1}` and `'{$lifetime_2}`...")]
[true] ...
*[false] {\"\"}
}`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}`, for any two lifetimes `'{$lifetime_1}` and `'{$lifetime_2}`...")]
ExpectedPassiveTwo {
leading_ellipsis: bool,
ty_or_sig: TyOrSig<'tcx>,
@@ -998,9 +998,9 @@ pub enum ActualImplExplNotes<'tcx> {
lifetime_2: usize,
},
#[note("{$leading_ellipsis ->
[true] ...
*[false] {\"\"}
}`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}`, for any lifetime `'{$lifetime_1}`...")]
[true] ...
*[false] {\"\"}
}`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}`, for any lifetime `'{$lifetime_1}`...")]
ExpectedPassiveAny {
leading_ellipsis: bool,
ty_or_sig: TyOrSig<'tcx>,
@@ -1008,9 +1008,9 @@ pub enum ActualImplExplNotes<'tcx> {
lifetime_1: usize,
},
#[note("{$leading_ellipsis ->
[true] ...
*[false] {\"\"}
}`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}`, for some specific lifetime `'{$lifetime_1}`...")]
[true] ...
*[false] {\"\"}
}`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}`, for some specific lifetime `'{$lifetime_1}`...")]
ExpectedPassiveSome {
leading_ellipsis: bool,
ty_or_sig: TyOrSig<'tcx>,
@@ -1019,9 +1019,9 @@ pub enum ActualImplExplNotes<'tcx> {
},
#[note(
"{$leading_ellipsis ->
[true] ...
*[false] {\"\"}
}`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}`"
[true] ...
*[false] {\"\"}
}`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}`"
)]
ExpectedPassiveNothing {
leading_ellipsis: bool,
@@ -1029,9 +1029,9 @@ pub enum ActualImplExplNotes<'tcx> {
trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
},
#[note("{$leading_ellipsis ->
[true] ...
*[false] {\"\"}
}`{$ty_or_sig}` must implement `{$trait_path}`, for any two lifetimes `'{$lifetime_1}` and `'{$lifetime_2}`...")]
[true] ...
*[false] {\"\"}
}`{$ty_or_sig}` must implement `{$trait_path}`, for any two lifetimes `'{$lifetime_1}` and `'{$lifetime_2}`...")]
ExpectedOtherTwo {
leading_ellipsis: bool,
ty_or_sig: TyOrSig<'tcx>,
@@ -1041,9 +1041,9 @@ pub enum ActualImplExplNotes<'tcx> {
},
#[note(
"{$leading_ellipsis ->
[true] ...
*[false] {\"\"}
}`{$ty_or_sig}` must implement `{$trait_path}`, for any lifetime `'{$lifetime_1}`..."
[true] ...
*[false] {\"\"}
}`{$ty_or_sig}` must implement `{$trait_path}`, for any lifetime `'{$lifetime_1}`..."
)]
ExpectedOtherAny {
leading_ellipsis: bool,
@@ -1053,9 +1053,9 @@ pub enum ActualImplExplNotes<'tcx> {
},
#[note(
"{$leading_ellipsis ->
[true] ...
*[false] {\"\"}
}`{$ty_or_sig}` must implement `{$trait_path}`, for some specific lifetime `'{$lifetime_1}`..."
[true] ...
*[false] {\"\"}
}`{$ty_or_sig}` must implement `{$trait_path}`, for some specific lifetime `'{$lifetime_1}`..."
)]
ExpectedOtherSome {
leading_ellipsis: bool,
@@ -1065,9 +1065,9 @@ pub enum ActualImplExplNotes<'tcx> {
},
#[note(
"{$leading_ellipsis ->
[true] ...
*[false] {\"\"}
}`{$ty_or_sig}` must implement `{$trait_path}`"
[true] ...
*[false] {\"\"}
}`{$ty_or_sig}` must implement `{$trait_path}`"
)]
ExpectedOtherNothing {
leading_ellipsis: bool,
@@ -1076,9 +1076,9 @@ pub enum ActualImplExplNotes<'tcx> {
},
#[note(
"...but it actually implements `{$trait_path}`{$has_lifetime ->
[true] , for some specific lifetime `'{$lifetime}`
*[false] {\"\"}
}"
[true] , for some specific lifetime `'{$lifetime}`
*[false] {\"\"}
}"
)]
ButActuallyImplementsTrait {
trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
@@ -1087,9 +1087,9 @@ pub enum ActualImplExplNotes<'tcx> {
},
#[note(
"...but `{$trait_path}` is actually implemented for the type `{$ty}`{$has_lifetime ->
[true] , for some specific lifetime `'{$lifetime}`
*[false] {\"\"}
}"
[true] , for some specific lifetime `'{$lifetime}`
*[false] {\"\"}
}"
)]
ButActuallyImplementedForTy {
trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
@@ -1099,9 +1099,9 @@ pub enum ActualImplExplNotes<'tcx> {
},
#[note(
"...but `{$ty}` actually implements `{$trait_path}`{$has_lifetime ->
[true] , for some specific lifetime `'{$lifetime}`
*[false] {\"\"}
}"
[true] , for some specific lifetime `'{$lifetime}`
*[false] {\"\"}
}"
)]
ButActuallyTyImplements {
trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
@@ -1246,7 +1246,7 @@ pub struct TraitImplDiff {
pub trait_sp: Span,
#[note(
"expected signature `{$expected}`
{\" \"}found signature `{$found}`"
{\" \"}found signature `{$found}`"
)]
pub note: (),
#[subdiagnostic]
@@ -1940,10 +1940,12 @@ pub enum ObligationCauseFailureCode {
#[primary_span]
span: Span,
},
#[diag("{$lang_item_name ->
[panic_impl] `#[panic_handler]`
*[lang_item_name] lang item `{$lang_item_name}`
} function has wrong type", code = E0308)]
#[diag(
"{$lang_item_name ->
[panic_impl] `#[panic_handler]`
*[lang_item_name] lang item `{$lang_item_name}`
} function has wrong type"
, code = E0308)]
FnLangCorrectType {
#[primary_span]
span: Span,