about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_lint/messages.ftl2
-rw-r--r--compiler/rustc_lint/src/internal.rs80
-rw-r--r--compiler/rustc_lint/src/lib.rs1
-rw-r--r--compiler/rustc_lint/src/lints.rs4
-rw-r--r--compiler/rustc_span/src/symbol.rs1
5 files changed, 87 insertions, 1 deletions
diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl
index 3d1b8f8ed95..3c6dbb466db 100644
--- a/compiler/rustc_lint/messages.ftl
+++ b/compiler/rustc_lint/messages.ftl
@@ -99,6 +99,8 @@ lint_diag_out_of_impl =
 
 lint_untranslatable_diag = diagnostics should be created using translatable messages
 
+lint_trivial_untranslatable_diag = diagnostic with static strings only
+
 lint_bad_opt_access = {$msg}
 
 lint_cstring_ptr = getting the inner pointer of a temporary `CString`
diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs
index 4ac589c2e10..595b50c4063 100644
--- a/compiler/rustc_lint/src/internal.rs
+++ b/compiler/rustc_lint/src/internal.rs
@@ -4,6 +4,7 @@
 use crate::lints::{
     BadOptAccessDiag, DefaultHashTypesDiag, DiagOutOfImpl, LintPassByHand, NonExistentDocKeyword,
     QueryInstability, TyQualified, TykindDiag, TykindKind, UntranslatableDiag,
+    UntranslatableDiagnosticTrivial,
 };
 use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
 use rustc_ast as ast;
@@ -366,7 +367,15 @@ declare_tool_lint! {
     report_in_external_macro: true
 }
 
-declare_lint_pass!(Diagnostics => [ UNTRANSLATABLE_DIAGNOSTIC, DIAGNOSTIC_OUTSIDE_OF_IMPL ]);
+declare_tool_lint! {
+    /// The `untranslatable_diagnostic_trivial` lint detects diagnostics created using only static strings.
+    pub rustc::UNTRANSLATABLE_DIAGNOSTIC_TRIVIAL,
+    Deny,
+    "prevent creation of diagnostics which cannot be translated, which use only static strings",
+    report_in_external_macro: true
+}
+
+declare_lint_pass!(Diagnostics => [ UNTRANSLATABLE_DIAGNOSTIC, DIAGNOSTIC_OUTSIDE_OF_IMPL, UNTRANSLATABLE_DIAGNOSTIC_TRIVIAL ]);
 
 impl LateLintPass<'_> for Diagnostics {
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
@@ -423,6 +432,75 @@ impl LateLintPass<'_> for Diagnostics {
     }
 }
 
+impl EarlyLintPass for Diagnostics {
+    #[allow(unused_must_use)]
+    fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &ast::Stmt) {
+        // Looking for a straight chain of method calls from 'struct_span_err' to 'emit'.
+        let ast::StmtKind::Semi(expr) = &stmt.kind else {
+            return;
+        };
+        let ast::ExprKind::MethodCall(meth) = &expr.kind else {
+            return;
+        };
+        if meth.seg.ident.name != sym::emit || !meth.args.is_empty() {
+            return;
+        }
+        let mut segments = vec![];
+        let mut cur = &meth.receiver;
+        let fake = &[].into();
+        loop {
+            match &cur.kind {
+                ast::ExprKind::Call(func, args) => {
+                    if let ast::ExprKind::Path(_, path) = &func.kind {
+                        segments.push((path.segments.last().unwrap().ident.name, args))
+                    }
+                    break;
+                }
+                ast::ExprKind::MethodCall(method) => {
+                    segments.push((method.seg.ident.name, &method.args));
+                    cur = &method.receiver;
+                }
+                ast::ExprKind::MacCall(mac) => {
+                    segments.push((mac.path.segments.last().unwrap().ident.name, fake));
+                    break;
+                }
+                _ => {
+                    break;
+                }
+            }
+        }
+        segments.reverse();
+        if segments.is_empty() {
+            return;
+        }
+        if segments[0].0.as_str() != "struct_span_err" {
+            return;
+        }
+        if !segments.iter().all(|(name, args)| {
+            let arg = match name.as_str() {
+                "struct_span_err" | "span_note" | "span_label" | "span_help" => &args[1],
+                "note" | "help" => &args[0],
+                _ => {
+                    return false;
+                }
+            };
+            if let ast::ExprKind::Lit(lit) = arg.kind
+                && let ast::token::LitKind::Str = lit.kind {
+                    true
+            } else {
+                false
+            }
+        }) {
+            return;
+        }
+        cx.emit_spanned_lint(
+            UNTRANSLATABLE_DIAGNOSTIC_TRIVIAL,
+            stmt.span,
+            UntranslatableDiagnosticTrivial,
+        );
+    }
+}
+
 declare_tool_lint! {
     /// The `bad_opt_access` lint detects accessing options by field instead of
     /// the wrapper function.
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index 76f07257907..319eb2ea445 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -518,6 +518,7 @@ fn register_internals(store: &mut LintStore) {
     store.register_lints(&TyTyKind::get_lints());
     store.register_late_pass(|_| Box::new(TyTyKind));
     store.register_lints(&Diagnostics::get_lints());
+    store.register_early_pass(|| Box::new(Diagnostics));
     store.register_late_pass(|_| Box::new(Diagnostics));
     store.register_lints(&BadOptAccess::get_lints());
     store.register_late_pass(|_| Box::new(BadOptAccess));
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index 1d5e02369f5..848f6a9ecb5 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -821,6 +821,10 @@ pub struct DiagOutOfImpl;
 pub struct UntranslatableDiag;
 
 #[derive(LintDiagnostic)]
+#[diag(lint_trivial_untranslatable_diag)]
+pub struct UntranslatableDiagnosticTrivial;
+
+#[derive(LintDiagnostic)]
 #[diag(lint_bad_opt_access)]
 pub struct BadOptAccessDiag<'a> {
     pub msg: &'a str,
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 70b9088de50..abf19c30e3d 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -651,6 +651,7 @@ symbols! {
         edition_panic,
         eh_catch_typeinfo,
         eh_personality,
+        emit,
         emit_enum,
         emit_enum_variant,
         emit_enum_variant_arg,