Skip to content

Conversation

@SciMind2460
Copy link
Contributor

@SciMind2460 SciMind2460 commented Nov 4, 2025

This introduces a new lint for detecting usage of [T; 0]: Default where T: !Default. It includes checks in check_item and check_expr methods to enforce the linting rules. This is in light of @WaffleLapkin's analysis in #145457 that it might be OK for this impl to break for the [T; N]: Default where T: Default impl. If the Rust community disagrees, this PR will close.

Please review as I am a new contributor and may have made many mistakes.

This introduces a new lint for detecting usage of `[T; 0]: Default` where `T: !Default`. It includes checks in `check_item` and `check_expr` methods to enforce the linting rules.
@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Nov 4, 2025
@rustbot
Copy link
Collaborator

rustbot commented Nov 4, 2025

r? @davidtwco

rustbot has assigned @davidtwco.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rust-log-analyzer

This comment has been minimized.

@SciMind2460
Copy link
Contributor Author

Please excuse the syntax errors - I am running the code on Github as I don't want to run it locally.

@rust-log-analyzer
Copy link
Collaborator

The job tidy failed! Check out the build log: (web) (plain enhanced) (plain)

Click to see the possible cause of the failure (guessed by this bot)
[TIMING:end] tool::ToolBuild { build_compiler: Compiler { stage: 0, host: x86_64-unknown-linux-gnu, forced_compiler: false }, target: x86_64-unknown-linux-gnu, tool: "tidy", path: "src/tools/tidy", mode: ToolBootstrap, source_type: InTree, extra_features: [], allow_features: "", cargo_args: [], artifact_kind: Binary } -- 11.624
[TIMING:end] tool::Tidy { compiler: Compiler { stage: 0, host: x86_64-unknown-linux-gnu, forced_compiler: false }, target: x86_64-unknown-linux-gnu } -- 0.000
fmt check
Diff in /checkout/compiler/rustc_lint/src/impl_default_on_zero_size_nondefault_arrays.rs:1:
 use rustc_hir::hir;
-use rustc_session::{declare_lint, impl_lint_pass};
+use rustc_infer::infer::TyCtxtInferExt;
 use rustc_middle::ty;
+use rustc_session::{declare_lint, impl_lint_pass};
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
-use rustc_infer::infer::TyCtxtInferExt;
 
 fn array_trait_check<'tcx>(
     cx: &rustc_lint::LateContext<'tcx>,
Diff in /checkout/compiler/rustc_lint/src/impl_default_on_zero_size_nondefault_arrays.rs:27:
 
         // Now assume T: OtherTrait
         let other_ref = ty::TraitRef::new(tcx, other_trait_id, [t_ty]);
-        let other_pred = ty::Binder::dummy(ty::PredicateKind::Trait(
-            ty::TraitPredicate { trait_ref: other_ref },
-        ));
+        let other_pred = ty::Binder::dummy(ty::PredicateKind::Trait(ty::TraitPredicate {
+            trait_ref: other_ref,
+        }));
 
         let extended_env = cx.param_env.with_additional_predicate(other_pred);
 
Diff in /checkout/compiler/rustc_lint/src/impl_default_on_zero_size_nondefault_arrays.rs:43:
 }
 
 declare_lint! {
-    /// The `impl_default_on_zero_size_nondefault_arrays` lint 
+    /// The `impl_default_on_zero_size_nondefault_arrays` lint
     /// detects usage of `[T; 0]: Default where T: !Default`
     /// ### Example
     ///
Diff in /checkout/compiler/rustc_lint/src/impl_default_on_zero_size_nondefault_arrays.rs:56:
     /// ```
     ///
     /// ### Explanation
-    /// 
-    /// There is a plan to make `impl Default for [T; N] where T: Default` 
+    ///
+    /// There is a plan to make `impl Default for [T; N] where T: Default`
     /// as per https://github.com/rust-lang/rust/issues/61415.
     /// `[T; 0]: Default where T: !Default` interferes with that plan.
     /// This will become a hard error in the future.
Diff in /checkout/compiler/rustc_lint/src/impl_default_on_zero_size_nondefault_arrays.rs:74:
 
 impl<'tcx> LateLintPass<'tcx> for ImplDefaultZeroSizeNonDefaultArrays {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx rustc_hir::Item<'tcx>) {
-      let def_id = cx.tcx.hir().local_def_id(item.hir_id());
-      let preds = cx.tcx.predicates_of(def_id);
+        let def_id = cx.tcx.hir().local_def_id(item.hir_id());
+        let preds = cx.tcx.predicates_of(def_id);
 
-      if let Some(default_id) = cx.tcx.get_diagnostic_item(rustc_span::symbol::sym::Default) {
-          for (clause, _) in preds.predicates {
+        if let Some(default_id) = cx.tcx.get_diagnostic_item(rustc_span::symbol::sym::Default) {
+            for (clause, _) in preds.predicates {
                 if let Some(binder) = clause.as_trait_clause() {
                     let trait_id = binder.skip_binder().trait_ref();
                     if trait_id == default_id {
Diff in /checkout/compiler/rustc_lint/src/impl_default_on_zero_size_nondefault_arrays.rs:85:
-                        if let ty::GenericArgKind::Type(ty) = binder.skip_binder().trait_ref().args().as_slice()[0].kind() {
+                        if let ty::GenericArgKind::Type(ty) =
+                            binder.skip_binder().trait_ref().args().as_slice()[0].kind()
+                        {
                             if let ty::tyKind::Array(t_ty, len_const) = ty.kind() {
                                 if let Some(0) = len_const.try_eval_usize(cx.tcx, cx.param_env) {
-                                    if !cx.tcx.infer_ctxt().type_implements_trait(default_id, [t_ty], cx.param_env)  {
+                                    if !cx.tcx.infer_ctxt().type_implements_trait(
+                                        default_id,
+                                        [t_ty],
+                                        cx.param_env,
+                                    ) {
                                         cx.struct_span_lint(ARRAY_TRAIT_BOUND, item.span, |lint| {
                                         lint.build(&format!(
                                             "trait bound `[T; 0]: Default where T: !Default` likely to be removed soon, see https://github.com/rust-lang/rust/issues/61415",
Diff in /checkout/compiler/rustc_lint/src/impl_default_on_zero_size_nondefault_arrays.rs:93:
                                         .emit();
                                         });
                                     }
-                               }
---
                 }
-           }
-      }
-  }
-  fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
-        if let hir::ExprKind::MethodCall(_, receiver, _, _) = expr.kind {
-            let method_def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id);
-            if let Some(default_id) = cx.tcx.get_diagnostic_item(rustc_span::symbol::sym::Default) {
-                if let Some(def_id) = method_def_id {
-                    if let TraitImpl(Ok(trait_id)) = cx.tcx.associated_item(def_id).container && trait_id == default_id {
-                        let recv_ty = cx.typeck_results().expr_ty(receiver);
-                        if let ty::Array(t_ty, len_const) = recv_ty.kind() {
-                            if let Some(0) = len_const.to_value().try_to_target_usize(cx.tcx) {
-                                if !cx.tcx.infer_ctxt().type_implements_trait(default_id, [t_ty], cx.param_env) {
-                                    let trait_name = cx.tcx.def_path_str(trait_id);
-                                    cx.struct_span_lint(ARRAY_TRAIT_IMPL_USAGE, expr.span, |lint| {
-                                        lint.build(&format!(
+            }
+        }
+        fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
+            if let hir::ExprKind::MethodCall(_, receiver, _, _) = expr.kind {
+                let method_def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id);
+                if let Some(default_id) =
+                    cx.tcx.get_diagnostic_item(rustc_span::symbol::sym::Default)
+                {
+                    if let Some(def_id) = method_def_id {
+                        if let TraitImpl(Ok(trait_id)) = cx.tcx.associated_item(def_id).container
+                            && trait_id == default_id
+                        {
+                            let recv_ty = cx.typeck_results().expr_ty(receiver);
+                            if let ty::Array(t_ty, len_const) = recv_ty.kind() {
+                                if let Some(0) = len_const.to_value().try_to_target_usize(cx.tcx) {
+                                    if !cx.tcx.infer_ctxt().type_implements_trait(
+                                        default_id,
+                                        [t_ty],
+                                        cx.param_env,
+                                    ) {
+                                        let trait_name = cx.tcx.def_path_str(trait_id);
+                                        cx.struct_span_lint(
+                                            ARRAY_TRAIT_IMPL_USAGE,
+                                            expr.span,
+                                            |lint| {
+                                                lint.build(&format!(
                                             "use of trait `{}` implemented on array type `{}`",
                                             trait_name,
                                             recv_ty
Diff in /checkout/compiler/rustc_lint/src/impl_default_on_zero_size_nondefault_arrays.rs:119:
                                         ))
                                         .emit();

@WaffleLapkin
Copy link
Member

Please add tests for this in a (new) first commit, bless them there, then bless them in the second one with the changes made here, to show that this works.
@rustbot author

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Nov 4, 2025
@rustbot
Copy link
Collaborator

rustbot commented Nov 4, 2025

Reminder, once the PR becomes ready for a review, use @rustbot ready.

@joshtriplett
Copy link
Member

joshtriplett commented Nov 4, 2025

Please see #145457 (comment) ; libs-api is not sure if we want to warn about this.

@traviscross traviscross added T-lang Relevant to the language team needs-fcp This change is insta-stable, or significant enough to need a team FCP to proceed. I-lang-radar Items that are on lang's radar and will need eventual work or consideration. labels Nov 4, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

I-lang-radar Items that are on lang's radar and will need eventual work or consideration. needs-fcp This change is insta-stable, or significant enough to need a team FCP to proceed. S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants