diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 197fe6552d796..f8e7d79efa0ea 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -505,6 +505,13 @@ lint_suspicious_double_ref_deref = lint_trivial_untranslatable_diag = diagnostic with static strings only + +lint_ty_compare_operator_used = using the a comparison operator on `Ty` + .note = this does probably not what you want as it does not handle inference variables and more + .help = for more information, see https://rustc-dev-guide.rust-lang.org/ty.html#comparing-types + +lint_ty_no_compare_operator_for_diagnostics = it's also not recommended to use it for diagnostics + lint_ty_qualified = usage of qualified `ty::{$ty}` .suggestion = try importing it and using it unqualified diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 4b803621f71ce..44e4ea995c4d9 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -3,11 +3,12 @@ use crate::lints::{ BadOptAccessDiag, DefaultHashTypesDiag, DiagOutOfImpl, LintPassByHand, NonExistentDocKeyword, - QueryInstability, TyQualified, TykindDiag, TykindKind, UntranslatableDiag, - UntranslatableDiagnosticTrivial, + QueryInstability, TyCompareOperatorUsed, TyQualified, TykindDiag, TykindKind, + UntranslatableDiag, UntranslatableDiagnosticTrivial, }; use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; use rustc_ast as ast; +use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::{def_id::DefId, Expr, ExprKind, GenericArg, PatKind, Path, PathSegment, QPath}; use rustc_hir::{HirId, Impl, Item, ItemKind, Node, Pat, Ty, TyKind}; @@ -127,12 +128,7 @@ declare_lint_pass!(TyTyKind => [ ]); impl<'tcx> LateLintPass<'tcx> for TyTyKind { - fn check_path( - &mut self, - cx: &LateContext<'tcx>, - path: &rustc_hir::Path<'tcx>, - _: rustc_hir::HirId, - ) { + fn check_path(&mut self, cx: &LateContext<'tcx>, path: &hir::Path<'tcx>, _: hir::HirId) { if let Some(segment) = path.segments.iter().nth_back(1) && lint_ty_kind_usage(cx, &segment.res) { @@ -269,6 +265,37 @@ fn gen_args(segment: &PathSegment<'_>) -> String { String::new() } +declare_tool_lint! { + pub rustc::TY_COMPARE_OPERATOR, + Allow, + "using the a comparison operator on `Ty`" +} + +declare_lint_pass!(TyCompareOperator => [TY_COMPARE_OPERATOR]); + +impl<'tcx> LateLintPass<'tcx> for TyCompareOperator { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { + use hir::BinOpKind::*; + + if let hir::ExprKind::Binary( + hir::BinOp { node:Eq | Ne | Gt | Lt | Le | Ge, span}, + lhs, + rhs + ) = expr.kind + && let ty::Adt(lhs_def, _) = cx.typeck_results().node_type(lhs.hir_id).peel_refs().kind() + && let ty::Adt(rhs_def, _) = cx.typeck_results().node_type(rhs.hir_id).peel_refs().kind() + && cx.tcx.is_diagnostic_item(sym::Ty, lhs_def.did()) + && cx.tcx.is_diagnostic_item(sym::Ty, rhs_def.did()) + { + cx.emit_spanned_lint( + TY_COMPARE_OPERATOR, + span, + TyCompareOperatorUsed, + ) + } + } +} + declare_tool_lint! { /// The `lint_pass_impl_without_macro` detects manual implementations of a lint /// pass, without using [`declare_lint_pass`] or [`impl_lint_pass`]. @@ -318,7 +345,7 @@ fn is_doc_keyword(s: Symbol) -> bool { } impl<'tcx> LateLintPass<'tcx> for ExistingDocKeyword { - fn check_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::Item<'_>) { + fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { for attr in cx.tcx.hir().attrs(item.hir_id()) { if !attr.has_name(sym::doc) { continue; diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index b9e455e6c2a3d..391e10abb99f6 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -524,6 +524,8 @@ fn register_internals(store: &mut LintStore) { store.register_late_mod_pass(|_| Box::new(ExistingDocKeyword)); store.register_lints(&TyTyKind::get_lints()); store.register_late_mod_pass(|_| Box::new(TyTyKind)); + store.register_lints(&TyCompareOperator::get_lints()); + store.register_late_mod_pass(|_| Box::new(TyCompareOperator)); store.register_lints(&Diagnostics::get_lints()); store.register_early_pass(|| Box::new(Diagnostics)); store.register_late_mod_pass(|_| Box::new(Diagnostics)); @@ -543,6 +545,7 @@ fn register_internals(store: &mut LintStore) { LintId::of(DEFAULT_HASH_TYPES), LintId::of(POTENTIAL_QUERY_INSTABILITY), LintId::of(USAGE_OF_TY_TYKIND), + LintId::of(TY_COMPARE_OPERATOR), LintId::of(PASS_BY_VALUE), LintId::of(LINT_PASS_IMPL_WITHOUT_MACRO), LintId::of(USAGE_OF_QUALIFIED_TY), diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 594ef97b3ffb5..63228f5ddb5de 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -920,6 +920,13 @@ pub struct TyQualified { pub suggestion: Span, } +#[derive(LintDiagnostic)] +#[diag(lint_ty_compare_operator_used)] +#[note] +#[note(lint_ty_no_compare_operator_for_diagnostics)] +#[help] +pub struct TyCompareOperatorUsed; + #[derive(LintDiagnostic)] #[diag(lint_lintpass_by_hand)] #[help] diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index c714b09ec3ce1..e773541ea9410 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -1881,6 +1881,13 @@ impl<'a> Builder<'a> { if mode == Mode::Rustc { rustflags.arg("-Zunstable-options"); rustflags.arg("-Wrustc::internal"); + + // #[cfg(not(bootstrap))] + if stage != 0 { + // FIXME(Nilstrieb): Allow this lint for now. + // After it's in beta, we can start work on fixing it. + rustflags.arg("-Arustc::ty-compare-operator"); + } } // Throughout the build Cargo can execute a number of build scripts diff --git a/tests/ui/lint/internal/ty_compare_operator.rs b/tests/ui/lint/internal/ty_compare_operator.rs new file mode 100644 index 0000000000000..48cb2e502f52f --- /dev/null +++ b/tests/ui/lint/internal/ty_compare_operator.rs @@ -0,0 +1,37 @@ +// compile-flags: -Z unstable-options + +#![feature(rustc_attrs)] +#![forbid(rustc::ty_compare_operator)] + +mod ty1 { + #[derive(PartialEq, PartialOrd)] + #[rustc_diagnostic_item = "Ty"] + pub struct Ty; +} + +mod ty2 { + #[derive(PartialEq, PartialOrd)] + pub struct Ty; +} + +fn main() { + let _ = ty1::Ty == ty1::Ty; + //~^ ERROR using the a comparison operator on `Ty` + let _ = ty1::Ty != ty1::Ty; + //~^ ERROR using the a comparison operator on `Ty` + let _ = ty1::Ty < ty1::Ty; + //~^ ERROR using the a comparison operator on `Ty` + let _ = ty1::Ty <= ty1::Ty; + //~^ ERROR using the a comparison operator on `Ty` + let _ = ty1::Ty > ty1::Ty; + //~^ ERROR using the a comparison operator on `Ty` + let _ = ty1::Ty >= ty1::Ty; + //~^ ERROR using the a comparison operator on `Ty` + + let _ = ty2::Ty == ty2::Ty; + let _ = ty2::Ty != ty2::Ty; + let _ = ty2::Ty < ty2::Ty; + let _ = ty2::Ty <= ty2::Ty; + let _ = ty2::Ty > ty2::Ty; + let _ = ty2::Ty >= ty2::Ty; +} diff --git a/tests/ui/lint/internal/ty_compare_operator.stderr b/tests/ui/lint/internal/ty_compare_operator.stderr new file mode 100644 index 0000000000000..a44f8babc1e0f --- /dev/null +++ b/tests/ui/lint/internal/ty_compare_operator.stderr @@ -0,0 +1,67 @@ +error: using the a comparison operator on `Ty` + --> $DIR/ty_compare_operator.rs:18:21 + | +LL | let _ = ty1::Ty == ty1::Ty; + | ^^ + | + = note: this does probably not what you want as it does not handle inference variables and more + = note: it's also not recommended to use it for diagnostics + = help: for more information, see https://rustc-dev-guide.rust-lang.org/ty.html#comparing-types +note: the lint level is defined here + --> $DIR/ty_compare_operator.rs:4:11 + | +LL | #![forbid(rustc::ty_compare_operator)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: using the a comparison operator on `Ty` + --> $DIR/ty_compare_operator.rs:20:21 + | +LL | let _ = ty1::Ty != ty1::Ty; + | ^^ + | + = note: this does probably not what you want as it does not handle inference variables and more + = note: it's also not recommended to use it for diagnostics + = help: for more information, see https://rustc-dev-guide.rust-lang.org/ty.html#comparing-types + +error: using the a comparison operator on `Ty` + --> $DIR/ty_compare_operator.rs:22:21 + | +LL | let _ = ty1::Ty < ty1::Ty; + | ^ + | + = note: this does probably not what you want as it does not handle inference variables and more + = note: it's also not recommended to use it for diagnostics + = help: for more information, see https://rustc-dev-guide.rust-lang.org/ty.html#comparing-types + +error: using the a comparison operator on `Ty` + --> $DIR/ty_compare_operator.rs:24:21 + | +LL | let _ = ty1::Ty <= ty1::Ty; + | ^^ + | + = note: this does probably not what you want as it does not handle inference variables and more + = note: it's also not recommended to use it for diagnostics + = help: for more information, see https://rustc-dev-guide.rust-lang.org/ty.html#comparing-types + +error: using the a comparison operator on `Ty` + --> $DIR/ty_compare_operator.rs:26:21 + | +LL | let _ = ty1::Ty > ty1::Ty; + | ^ + | + = note: this does probably not what you want as it does not handle inference variables and more + = note: it's also not recommended to use it for diagnostics + = help: for more information, see https://rustc-dev-guide.rust-lang.org/ty.html#comparing-types + +error: using the a comparison operator on `Ty` + --> $DIR/ty_compare_operator.rs:28:21 + | +LL | let _ = ty1::Ty >= ty1::Ty; + | ^^ + | + = note: this does probably not what you want as it does not handle inference variables and more + = note: it's also not recommended to use it for diagnostics + = help: for more information, see https://rustc-dev-guide.rust-lang.org/ty.html#comparing-types + +error: aborting due to 6 previous errors +