From 11fc7852fe7304b84a3bb642a0ab2d60611df1e0 Mon Sep 17 00:00:00 2001
From: David Wood <david.wood@huawei.com>
Date: Wed, 31 Aug 2022 12:06:22 +0100
Subject: [PATCH] lint: avoid linting diag functions with diag lints

Functions annotated with `#[rustc_lint_diagnostics]` are used by the
diagnostic migration lints to know when to lint, but functions that are
annotated with this attribute shouldn't themselves be linted.

Signed-off-by: David Wood <david.wood@huawei.com>
---
 compiler/rustc_lint/src/internal.rs                | 12 +++++++++---
 src/test/ui-fulldeps/internal-lints/diagnostics.rs |  8 ++++++++
 .../ui-fulldeps/internal-lints/diagnostics.stderr  | 14 +++++++-------
 3 files changed, 24 insertions(+), 10 deletions(-)

diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs
index c26d7824758ef..6d451f3090acb 100644
--- a/compiler/rustc_lint/src/internal.rs
+++ b/compiler/rustc_lint/src/internal.rs
@@ -393,8 +393,14 @@ impl LateLintPass<'_> for Diagnostics {
             return;
         }
 
+        let mut found_parent_with_attr = false;
         let mut found_impl = false;
-        for (_, parent) in cx.tcx.hir().parent_iter(expr.hir_id) {
+        for (hir_id, parent) in cx.tcx.hir().parent_iter(expr.hir_id) {
+            if let Some(owner_did) = hir_id.as_owner() {
+                found_parent_with_attr = found_parent_with_attr
+                    || cx.tcx.has_attr(owner_did.to_def_id(), sym::rustc_lint_diagnostics);
+            }
+
             debug!(?parent);
             if let Node::Item(Item { kind: ItemKind::Impl(impl_), .. }) = parent &&
                 let Impl { of_trait: Some(of_trait), .. } = impl_ &&
@@ -407,7 +413,7 @@ impl LateLintPass<'_> for Diagnostics {
             }
         }
         debug!(?found_impl);
-        if !found_impl {
+        if !found_parent_with_attr && !found_impl {
             cx.struct_span_lint(DIAGNOSTIC_OUTSIDE_OF_IMPL, span, |lint| {
                 lint.build(fluent::lint::diag_out_of_impl).emit();
             })
@@ -425,7 +431,7 @@ impl LateLintPass<'_> for Diagnostics {
             }
         }
         debug!(?found_diagnostic_message);
-        if !found_diagnostic_message {
+        if !found_parent_with_attr && !found_diagnostic_message {
             cx.struct_span_lint(UNTRANSLATABLE_DIAGNOSTIC, span, |lint| {
                 lint.build(fluent::lint::untranslatable_diag).emit();
             })
diff --git a/src/test/ui-fulldeps/internal-lints/diagnostics.rs b/src/test/ui-fulldeps/internal-lints/diagnostics.rs
index 33192433bbf38..0e449256153a2 100644
--- a/src/test/ui-fulldeps/internal-lints/diagnostics.rs
+++ b/src/test/ui-fulldeps/internal-lints/diagnostics.rs
@@ -1,6 +1,7 @@
 // compile-flags: -Z unstable-options
 
 #![crate_type = "lib"]
+#![feature(rustc_attrs)]
 #![feature(rustc_private)]
 #![deny(rustc::untranslatable_diagnostic)]
 #![deny(rustc::diagnostic_outside_of_impl)]
@@ -71,3 +72,10 @@ pub fn make_diagnostics<'a>(sess: &'a ParseSess) {
     //~^ ERROR diagnostics should only be created in `SessionDiagnostic`/`AddSubdiagnostic` impls
     //~^^ ERROR diagnostics should be created using translatable messages
 }
+
+// Check that `rustc_lint_diagnostics`-annotated functions aren't themselves linted.
+
+#[rustc_lint_diagnostics]
+pub fn skipped_because_of_annotation<'a>(sess: &'a ParseSess) {
+    let _diag = sess.struct_err("untranslatable diagnostic"); // okay!
+}
diff --git a/src/test/ui-fulldeps/internal-lints/diagnostics.stderr b/src/test/ui-fulldeps/internal-lints/diagnostics.stderr
index bae78ffdc021b..ed5105dabcd3f 100644
--- a/src/test/ui-fulldeps/internal-lints/diagnostics.stderr
+++ b/src/test/ui-fulldeps/internal-lints/diagnostics.stderr
@@ -1,41 +1,41 @@
 error: diagnostics should be created using translatable messages
-  --> $DIR/diagnostics.rs:36:14
+  --> $DIR/diagnostics.rs:37:14
    |
 LL |         sess.struct_err("untranslatable diagnostic")
    |              ^^^^^^^^^^
    |
 note: the lint level is defined here
-  --> $DIR/diagnostics.rs:5:9
+  --> $DIR/diagnostics.rs:6:9
    |
 LL | #![deny(rustc::untranslatable_diagnostic)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: diagnostics should be created using translatable messages
-  --> $DIR/diagnostics.rs:53:14
+  --> $DIR/diagnostics.rs:54:14
    |
 LL |         diag.note("untranslatable diagnostic");
    |              ^^^^
 
 error: diagnostics should only be created in `SessionDiagnostic`/`AddSubdiagnostic` impls
-  --> $DIR/diagnostics.rs:67:22
+  --> $DIR/diagnostics.rs:68:22
    |
 LL |     let _diag = sess.struct_err(fluent::parser::expect_path);
    |                      ^^^^^^^^^^
    |
 note: the lint level is defined here
-  --> $DIR/diagnostics.rs:6:9
+  --> $DIR/diagnostics.rs:7:9
    |
 LL | #![deny(rustc::diagnostic_outside_of_impl)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: diagnostics should only be created in `SessionDiagnostic`/`AddSubdiagnostic` impls
-  --> $DIR/diagnostics.rs:70:22
+  --> $DIR/diagnostics.rs:71:22
    |
 LL |     let _diag = sess.struct_err("untranslatable diagnostic");
    |                      ^^^^^^^^^^
 
 error: diagnostics should be created using translatable messages
-  --> $DIR/diagnostics.rs:70:22
+  --> $DIR/diagnostics.rs:71:22
    |
 LL |     let _diag = sess.struct_err("untranslatable diagnostic");
    |                      ^^^^^^^^^^