about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2024-08-21 19:35:11 +0200
committerGitHub <noreply@github.com>2024-08-21 19:35:11 +0200
commit47af700fe6dac24d2b3b4635290c561639b63e6c (patch)
tree661006dc39a97fb582c6a1bd3d2a66b1759a429c /compiler
parent65386c045e6c9d020ae6703b2b5151edc3f1b595 (diff)
parente94a4ee219e6c91d78bc3dd76298c5be6139a909 (diff)
downloadrust-47af700fe6dac24d2b3b4635290c561639b63e6c.tar.gz
rust-47af700fe6dac24d2b3b4635290c561639b63e6c.zip
Rollup merge of #128941 - GrigorenkoPV:internal-diagnostic-lints, r=davidtwco
 Improve diagnostic-related lints: `untranslatable_diagnostic` & `diagnostic_outside_of_impl`

Summary:
- Made `untranslatable_diagnostic` point to problematic arguments instead of the function call
  (I found this misleading while working on some `A-translation` PRs: my first impression was that
  the methods themselves were not translation-aware and needed to be changed,
  while in reality the problem was with the hardcoded strings passed as arguments).
- Made the shared pass of `untranslatable_diagnostic` & `diagnostic_outside_of_impl` more efficient.

`@rustbot` label D-imprecise-spans A-translation
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_lint/src/internal.rs152
1 files changed, 84 insertions, 68 deletions
diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs
index 044c9413f0b..65571815019 100644
--- a/compiler/rustc_lint/src/internal.rs
+++ b/compiler/rustc_lint/src/internal.rs
@@ -8,7 +8,7 @@ use rustc_hir::{
     BinOp, BinOpKind, Expr, ExprKind, GenericArg, HirId, Impl, Item, ItemKind, Node, Pat, PatKind,
     Path, PathSegment, QPath, Ty, TyKind,
 };
-use rustc_middle::ty::{self, Ty as MiddleTy};
+use rustc_middle::ty::{self, GenericArgsRef, Ty as MiddleTy};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::hygiene::{ExpnKind, MacroKind};
 use rustc_span::symbol::{kw, sym, Symbol};
@@ -415,14 +415,17 @@ declare_lint_pass!(Diagnostics => [UNTRANSLATABLE_DIAGNOSTIC, DIAGNOSTIC_OUTSIDE
 
 impl LateLintPass<'_> for Diagnostics {
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+        let collect_args_tys_and_spans = |args: &[Expr<'_>], reserve_one_extra: bool| {
+            let mut result = Vec::with_capacity(args.len() + usize::from(reserve_one_extra));
+            result.extend(args.iter().map(|arg| (cx.typeck_results().expr_ty(arg), arg.span)));
+            result
+        };
         // Only check function calls and method calls.
-        let (span, def_id, fn_gen_args, call_tys) = match expr.kind {
+        let (span, def_id, fn_gen_args, arg_tys_and_spans) = match expr.kind {
             ExprKind::Call(callee, args) => {
                 match cx.typeck_results().node_type(callee.hir_id).kind() {
                     &ty::FnDef(def_id, fn_gen_args) => {
-                        let call_tys: Vec<_> =
-                            args.iter().map(|arg| cx.typeck_results().expr_ty(arg)).collect();
-                        (callee.span, def_id, fn_gen_args, call_tys)
+                        (callee.span, def_id, fn_gen_args, collect_args_tys_and_spans(args, false))
                     }
                     _ => return, // occurs for fns passed as args
                 }
@@ -432,38 +435,40 @@ impl LateLintPass<'_> for Diagnostics {
                 else {
                     return;
                 };
-                let mut call_tys: Vec<_> =
-                    args.iter().map(|arg| cx.typeck_results().expr_ty(arg)).collect();
-                call_tys.insert(0, cx.tcx.types.self_param); // dummy inserted for `self`
-                (span, def_id, fn_gen_args, call_tys)
+                let mut args = collect_args_tys_and_spans(args, true);
+                args.insert(0, (cx.tcx.types.self_param, _recv.span)); // dummy inserted for `self`
+                (span, def_id, fn_gen_args, args)
             }
             _ => return,
         };
 
-        // Is the callee marked with `#[rustc_lint_diagnostics]`?
-        let has_attr = ty::Instance::try_resolve(cx.tcx, cx.param_env, def_id, fn_gen_args)
-            .ok()
-            .flatten()
-            .is_some_and(|inst| cx.tcx.has_attr(inst.def_id(), sym::rustc_lint_diagnostics));
-
-        // Closure: is the type `{D,Subd}iagMessage`?
-        let is_diag_message = |ty: MiddleTy<'_>| {
-            if let Some(adt_def) = ty.ty_adt_def()
-                && let Some(name) = cx.tcx.get_diagnostic_name(adt_def.did())
-                && matches!(name, sym::DiagMessage | sym::SubdiagMessage)
-            {
-                true
-            } else {
-                false
-            }
-        };
+        Self::diagnostic_outside_of_impl(cx, span, expr.hir_id, def_id, fn_gen_args);
+        Self::untranslatable_diagnostic(cx, def_id, &arg_tys_and_spans);
+    }
+}
 
-        // Does the callee have one or more `impl Into<{D,Subd}iagMessage>` parameters?
-        let mut impl_into_diagnostic_message_params = vec![];
+impl Diagnostics {
+    // Is the type `{D,Subd}iagMessage`?
+    fn is_diag_message<'cx>(cx: &LateContext<'cx>, ty: MiddleTy<'cx>) -> bool {
+        if let Some(adt_def) = ty.ty_adt_def()
+            && let Some(name) = cx.tcx.get_diagnostic_name(adt_def.did())
+            && matches!(name, sym::DiagMessage | sym::SubdiagMessage)
+        {
+            true
+        } else {
+            false
+        }
+    }
+
+    fn untranslatable_diagnostic<'cx>(
+        cx: &LateContext<'cx>,
+        def_id: DefId,
+        arg_tys_and_spans: &[(MiddleTy<'cx>, Span)],
+    ) {
         let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder();
         let predicates = cx.tcx.predicates_of(def_id).instantiate_identity(cx.tcx).predicates;
         for (i, &param_ty) in fn_sig.inputs().iter().enumerate() {
-            if let ty::Param(p) = param_ty.kind() {
+            if let ty::Param(sig_param) = param_ty.kind() {
                 // It is a type parameter. Check if it is `impl Into<{D,Subd}iagMessage>`.
                 for pred in predicates.iter() {
                     if let Some(trait_pred) = pred.as_trait_clause()
@@ -471,27 +476,53 @@ impl LateLintPass<'_> for Diagnostics {
                         && trait_ref.self_ty() == param_ty // correct predicate for the param?
                         && cx.tcx.is_diagnostic_item(sym::Into, trait_ref.def_id)
                         && let ty1 = trait_ref.args.type_at(1)
-                        && is_diag_message(ty1)
+                        && Self::is_diag_message(cx, ty1)
                     {
-                        impl_into_diagnostic_message_params.push((i, p.name));
+                        // Calls to methods with an `impl Into<{D,Subd}iagMessage>` parameter must be passed an arg
+                        // with type `{D,Subd}iagMessage` or `impl Into<{D,Subd}iagMessage>`. Otherwise, emit an
+                        // `UNTRANSLATABLE_DIAGNOSTIC` lint.
+                        let (arg_ty, arg_span) = arg_tys_and_spans[i];
+
+                        // Is the arg type `{Sub,D}iagMessage`or `impl Into<{Sub,D}iagMessage>`?
+                        let is_translatable = Self::is_diag_message(cx, arg_ty)
+                            || matches!(arg_ty.kind(), ty::Param(arg_param) if arg_param.name == sig_param.name);
+                        if !is_translatable {
+                            cx.emit_span_lint(
+                                UNTRANSLATABLE_DIAGNOSTIC,
+                                arg_span,
+                                UntranslatableDiag,
+                            );
+                        }
                     }
                 }
             }
         }
+    }
 
-        // Is the callee interesting?
-        if !has_attr && impl_into_diagnostic_message_params.is_empty() {
+    fn diagnostic_outside_of_impl<'cx>(
+        cx: &LateContext<'cx>,
+        span: Span,
+        current_id: HirId,
+        def_id: DefId,
+        fn_gen_args: GenericArgsRef<'cx>,
+    ) {
+        // Is the callee marked with `#[rustc_lint_diagnostics]`?
+        let Some(inst) =
+            ty::Instance::try_resolve(cx.tcx, cx.param_env, def_id, fn_gen_args).ok().flatten()
+        else {
             return;
-        }
+        };
+        let has_attr = cx.tcx.has_attr(inst.def_id(), sym::rustc_lint_diagnostics);
+        if !has_attr {
+            return;
+        };
 
-        // Is the parent method marked with `#[rustc_lint_diagnostics]`?
-        let mut parent_has_attr = false;
-        for (hir_id, _parent) in cx.tcx.hir().parent_iter(expr.hir_id) {
+        for (hir_id, _parent) in cx.tcx.hir().parent_iter(current_id) {
             if let Some(owner_did) = hir_id.as_owner()
                 && cx.tcx.has_attr(owner_did, sym::rustc_lint_diagnostics)
             {
-                parent_has_attr = true;
-                break;
+                // The parent method is marked with `#[rustc_lint_diagnostics]`
+                return;
             }
         }
 
@@ -500,37 +531,22 @@ impl LateLintPass<'_> for Diagnostics {
         // - inside a parent function that is itself marked with `#[rustc_lint_diagnostics]`.
         //
         // Otherwise, emit a `DIAGNOSTIC_OUTSIDE_OF_IMPL` lint.
-        if has_attr && !parent_has_attr {
-            let mut is_inside_appropriate_impl = false;
-            for (_hir_id, parent) in cx.tcx.hir().parent_iter(expr.hir_id) {
-                debug!(?parent);
-                if let Node::Item(Item { kind: ItemKind::Impl(impl_), .. }) = parent
-                    && let Impl { of_trait: Some(of_trait), .. } = impl_
-                    && let Some(def_id) = of_trait.trait_def_id()
-                    && let Some(name) = cx.tcx.get_diagnostic_name(def_id)
-                    && matches!(name, sym::Diagnostic | sym::Subdiagnostic | sym::LintDiagnostic)
-                {
-                    is_inside_appropriate_impl = true;
-                    break;
-                }
-            }
-            debug!(?is_inside_appropriate_impl);
-            if !is_inside_appropriate_impl {
-                cx.emit_span_lint(DIAGNOSTIC_OUTSIDE_OF_IMPL, span, DiagOutOfImpl);
+        let mut is_inside_appropriate_impl = false;
+        for (_hir_id, parent) in cx.tcx.hir().parent_iter(current_id) {
+            debug!(?parent);
+            if let Node::Item(Item { kind: ItemKind::Impl(impl_), .. }) = parent
+                && let Impl { of_trait: Some(of_trait), .. } = impl_
+                && let Some(def_id) = of_trait.trait_def_id()
+                && let Some(name) = cx.tcx.get_diagnostic_name(def_id)
+                && matches!(name, sym::Diagnostic | sym::Subdiagnostic | sym::LintDiagnostic)
+            {
+                is_inside_appropriate_impl = true;
+                break;
             }
         }
-
-        // Calls to methods with an `impl Into<{D,Subd}iagMessage>` parameter must be passed an arg
-        // with type `{D,Subd}iagMessage` or `impl Into<{D,Subd}iagMessage>`. Otherwise, emit an
-        // `UNTRANSLATABLE_DIAGNOSTIC` lint.
-        for (param_i, param_i_p_name) in impl_into_diagnostic_message_params {
-            // Is the arg type `{Sub,D}iagMessage`or `impl Into<{Sub,D}iagMessage>`?
-            let arg_ty = call_tys[param_i];
-            let is_translatable = is_diag_message(arg_ty)
-                || matches!(arg_ty.kind(), ty::Param(p) if p.name == param_i_p_name);
-            if !is_translatable {
-                cx.emit_span_lint(UNTRANSLATABLE_DIAGNOSTIC, span, UntranslatableDiag);
-            }
+        debug!(?is_inside_appropriate_impl);
+        if !is_inside_appropriate_impl {
+            cx.emit_span_lint(DIAGNOSTIC_OUTSIDE_OF_IMPL, span, DiagOutOfImpl);
         }
     }
 }