Skip to content

Implement lint against using Interner and InferCtxtLike in random compiler crates #138964

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions compiler/rustc_lint/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,9 @@ lint_tykind_kind = usage of `ty::TyKind::<kind>`
lint_type_ir_inherent_usage = do not use `rustc_type_ir::inherent` unless you're inside of the trait solver
.note = the method or struct you're looking for is likely defined somewhere else downstream in the compiler

lint_type_ir_trait_usage = do not use `rustc_type_ir::Interner` or `rustc_type_ir::InferCtxtLike` unless you're inside of the trait solver
.note = the method or struct you're looking for is likely defined somewhere else downstream in the compiler

lint_undropped_manually_drops = calls to `std::mem::drop` with `std::mem::ManuallyDrop` instead of the inner value does nothing
.label = argument has type `{$arg_ty}`
.suggestion = use `std::mem::ManuallyDrop::into_inner` to get the inner value
Expand Down
128 changes: 86 additions & 42 deletions compiler/rustc_lint/src/internal.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,21 @@
//! Some lints that are only useful in the compiler or crates that use compiler internals, such as
//! Clippy.

use rustc_ast as ast;
use rustc_hir::HirId;
use rustc_hir::def::Res;
use rustc_hir::def_id::DefId;
use rustc_hir::{
AmbigArg, BinOp, BinOpKind, Expr, ExprKind, GenericArg, HirId, Impl, Item, ItemKind, Node, Pat,
PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Ty, TyKind,
};
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::{Span, sym};
use tracing::debug;
use {rustc_ast as ast, rustc_hir as hir};

use crate::lints::{
BadOptAccessDiag, DefaultHashTypesDiag, DiagOutOfImpl, LintPassByHand,
NonGlobImportTypeIrInherent, QueryInstability, QueryUntracked, SpanUseEqCtxtDiag,
SymbolInternStringLiteralDiag, TyQualified, TykindDiag, TykindKind, TypeIrInherentUsage,
UntranslatableDiag,
TypeIrTraitUsage, UntranslatableDiag,
};
use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};

Expand All @@ -37,9 +34,12 @@ declare_tool_lint! {
declare_lint_pass!(DefaultHashTypes => [DEFAULT_HASH_TYPES]);

impl LateLintPass<'_> for DefaultHashTypes {
fn check_path(&mut self, cx: &LateContext<'_>, path: &Path<'_>, hir_id: HirId) {
fn check_path(&mut self, cx: &LateContext<'_>, path: &hir::Path<'_>, hir_id: HirId) {
let Res::Def(rustc_hir::def::DefKind::Struct, def_id) = path.res else { return };
if matches!(cx.tcx.hir_node(hir_id), Node::Item(Item { kind: ItemKind::Use(..), .. })) {
if matches!(
cx.tcx.hir_node(hir_id),
hir::Node::Item(hir::Item { kind: hir::ItemKind::Use(..), .. })
) {
// Don't lint imports, only actual usages.
return;
}
Expand All @@ -60,10 +60,10 @@ impl LateLintPass<'_> for DefaultHashTypes {
/// get the `DefId` and `GenericArgsRef` of the function.
fn typeck_results_of_method_fn<'tcx>(
cx: &LateContext<'tcx>,
expr: &Expr<'_>,
expr: &hir::Expr<'_>,
) -> Option<(Span, DefId, ty::GenericArgsRef<'tcx>)> {
match expr.kind {
ExprKind::MethodCall(segment, ..)
hir::ExprKind::MethodCall(segment, ..)
if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) =>
{
Some((segment.ident.span, def_id, cx.typeck_results().node_args(expr.hir_id)))
Expand Down Expand Up @@ -102,7 +102,7 @@ declare_tool_lint! {
declare_lint_pass!(QueryStability => [POTENTIAL_QUERY_INSTABILITY, UNTRACKED_QUERY_INFORMATION]);

impl LateLintPass<'_> for QueryStability {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
let Some((span, def_id, args)) = typeck_results_of_method_fn(cx, expr) else { return };
if let Ok(Some(instance)) = ty::Instance::try_resolve(cx.tcx, cx.typing_env(), def_id, args)
{
Expand Down Expand Up @@ -164,21 +164,25 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind {
}
}

fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx Ty<'tcx, AmbigArg>) {
fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx, hir::AmbigArg>) {
match &ty.kind {
TyKind::Path(QPath::Resolved(_, path)) => {
hir::TyKind::Path(hir::QPath::Resolved(_, path)) => {
if lint_ty_kind_usage(cx, &path.res) {
let span = match cx.tcx.parent_hir_node(ty.hir_id) {
Node::PatExpr(PatExpr { kind: PatExprKind::Path(qpath), .. })
| Node::Pat(Pat {
kind: PatKind::TupleStruct(qpath, ..) | PatKind::Struct(qpath, ..),
hir::Node::PatExpr(hir::PatExpr {
kind: hir::PatExprKind::Path(qpath),
..
})
| hir::Node::Pat(hir::Pat {
kind:
hir::PatKind::TupleStruct(qpath, ..) | hir::PatKind::Struct(qpath, ..),
..
})
| Node::Expr(
Expr { kind: ExprKind::Path(qpath), .. }
| &Expr { kind: ExprKind::Struct(qpath, ..), .. },
| hir::Node::Expr(
hir::Expr { kind: hir::ExprKind::Path(qpath), .. }
| &hir::Expr { kind: hir::ExprKind::Struct(qpath, ..), .. },
) => {
if let QPath::TypeRelative(qpath_ty, ..) = qpath
if let hir::QPath::TypeRelative(qpath_ty, ..) = qpath
&& qpath_ty.hir_id == ty.hir_id
{
Some(path.span)
Expand Down Expand Up @@ -223,7 +227,7 @@ fn lint_ty_kind_usage(cx: &LateContext<'_>, res: &Res) -> bool {
}
}

fn is_ty_or_ty_ctxt(cx: &LateContext<'_>, path: &Path<'_>) -> Option<String> {
fn is_ty_or_ty_ctxt(cx: &LateContext<'_>, path: &hir::Path<'_>) -> Option<String> {
match &path.res {
Res::Def(_, def_id) => {
if let Some(name @ (sym::Ty | sym::TyCtxt)) = cx.tcx.get_diagnostic_name(*def_id) {
Expand All @@ -244,13 +248,17 @@ fn is_ty_or_ty_ctxt(cx: &LateContext<'_>, path: &Path<'_>) -> Option<String> {
None
}

fn gen_args(segment: &PathSegment<'_>) -> String {
fn gen_args(segment: &hir::PathSegment<'_>) -> String {
if let Some(args) = &segment.args {
let lifetimes = args
.args
.iter()
.filter_map(|arg| {
if let GenericArg::Lifetime(lt) = arg { Some(lt.ident.to_string()) } else { None }
if let hir::GenericArg::Lifetime(lt) = arg {
Some(lt.ident.to_string())
} else {
None
}
})
.collect::<Vec<_>>();

Expand All @@ -272,7 +280,7 @@ declare_tool_lint! {
}

declare_tool_lint! {
/// The `usage_of_type_ir_inherent` lint detects usage `rustc_type_ir::inherent`.
/// The `usage_of_type_ir_inherent` lint detects usage of `rustc_type_ir::inherent`.
///
/// This module should only be used within the trait solver.
pub rustc::USAGE_OF_TYPE_IR_INHERENT,
Expand All @@ -281,10 +289,43 @@ declare_tool_lint! {
report_in_external_macro: true
}

declare_lint_pass!(TypeIr => [NON_GLOB_IMPORT_OF_TYPE_IR_INHERENT, USAGE_OF_TYPE_IR_INHERENT]);
declare_tool_lint! {
/// The `usage_of_type_ir_traits` lint detects usage of `rustc_type_ir::Interner`,
/// or `rustc_infer::InferCtxtLike`.
///
/// Methods of this trait should only be used within the type system abstraction layer,
/// and in the generic next trait solver implementation. Look for an analogously named
/// method on `TyCtxt` or `InferCtxt` (respectively).
pub rustc::USAGE_OF_TYPE_IR_TRAITS,
Allow,
"usage `rustc_type_ir`-specific abstraction traits outside of trait system",
report_in_external_macro: true
}

declare_lint_pass!(TypeIr => [NON_GLOB_IMPORT_OF_TYPE_IR_INHERENT, USAGE_OF_TYPE_IR_INHERENT, USAGE_OF_TYPE_IR_TRAITS]);

impl<'tcx> LateLintPass<'tcx> for TypeIr {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
let res_def_id = match expr.kind {
hir::ExprKind::Path(hir::QPath::Resolved(_, path)) => path.res.opt_def_id(),
hir::ExprKind::Path(hir::QPath::TypeRelative(..)) | hir::ExprKind::MethodCall(..) => {
cx.typeck_results().type_dependent_def_id(expr.hir_id)
}
_ => return,
};
let Some(res_def_id) = res_def_id else {
return;
};
if let Some(assoc_item) = cx.tcx.opt_associated_item(res_def_id)
&& let Some(trait_def_id) = assoc_item.trait_container(cx.tcx)
&& (cx.tcx.is_diagnostic_item(sym::type_ir_interner, trait_def_id)
| cx.tcx.is_diagnostic_item(sym::type_ir_infer_ctxt_like, trait_def_id))
{
cx.emit_span_lint(USAGE_OF_TYPE_IR_TRAITS, expr.span, TypeIrTraitUsage);
}
}

fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
let rustc_hir::ItemKind::Use(path, kind) = item.kind else { return };

let is_mod_inherent = |def_id| cx.tcx.is_diagnostic_item(sym::type_ir_inherent, def_id);
Expand Down Expand Up @@ -394,23 +435,23 @@ declare_tool_lint! {
declare_lint_pass!(Diagnostics => [UNTRANSLATABLE_DIAGNOSTIC, DIAGNOSTIC_OUTSIDE_OF_IMPL]);

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| {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
let collect_args_tys_and_spans = |args: &[hir::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, arg_tys_and_spans) = match expr.kind {
ExprKind::Call(callee, args) => {
hir::ExprKind::Call(callee, args) => {
match cx.typeck_results().node_type(callee.hir_id).kind() {
&ty::FnDef(def_id, fn_gen_args) => {
(callee.span, def_id, fn_gen_args, collect_args_tys_and_spans(args, false))
}
_ => return, // occurs for fns passed as args
}
}
ExprKind::MethodCall(_segment, _recv, args, _span) => {
hir::ExprKind::MethodCall(_segment, _recv, args, _span) => {
let Some((span, def_id, fn_gen_args)) = typeck_results_of_method_fn(cx, expr)
else {
return;
Expand Down Expand Up @@ -514,8 +555,8 @@ impl Diagnostics {
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_
if let hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(impl_), .. }) = parent
&& let hir::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)
Expand Down Expand Up @@ -543,8 +584,8 @@ declare_tool_lint! {
declare_lint_pass!(BadOptAccess => [BAD_OPT_ACCESS]);

impl LateLintPass<'_> for BadOptAccess {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
let ExprKind::Field(base, target) = expr.kind else { return };
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
let hir::ExprKind::Field(base, target) = expr.kind else { return };
let Some(adt_def) = cx.typeck_results().expr_ty(base).ty_adt_def() else { return };
// Skip types without `#[rustc_lint_opt_ty]` - only so that the rest of the lint can be
// avoided.
Expand Down Expand Up @@ -581,9 +622,12 @@ declare_tool_lint! {
declare_lint_pass!(SpanUseEqCtxt => [SPAN_USE_EQ_CTXT]);

impl<'tcx> LateLintPass<'tcx> for SpanUseEqCtxt {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
if let ExprKind::Binary(BinOp { node: BinOpKind::Eq | BinOpKind::Ne, .. }, lhs, rhs) =
expr.kind
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) {
if let hir::ExprKind::Binary(
hir::BinOp { node: hir::BinOpKind::Eq | hir::BinOpKind::Ne, .. },
lhs,
rhs,
) = expr.kind
{
if is_span_ctxt_call(cx, lhs) && is_span_ctxt_call(cx, rhs) {
cx.emit_span_lint(SPAN_USE_EQ_CTXT, expr.span, SpanUseEqCtxtDiag);
Expand All @@ -592,9 +636,9 @@ impl<'tcx> LateLintPass<'tcx> for SpanUseEqCtxt {
}
}

fn is_span_ctxt_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
fn is_span_ctxt_call(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
match &expr.kind {
ExprKind::MethodCall(..) => cx
hir::ExprKind::MethodCall(..) => cx
.typeck_results()
.type_dependent_def_id(expr.hir_id)
.is_some_and(|call_did| cx.tcx.is_diagnostic_item(sym::SpanCtxt, call_did)),
Expand All @@ -617,11 +661,11 @@ declare_lint_pass!(SymbolInternStringLiteral => [SYMBOL_INTERN_STRING_LITERAL]);

impl<'tcx> LateLintPass<'tcx> for SymbolInternStringLiteral {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) {
if let ExprKind::Call(path, [arg]) = expr.kind
&& let ExprKind::Path(ref qpath) = path.kind
if let hir::ExprKind::Call(path, [arg]) = expr.kind
&& let hir::ExprKind::Path(ref qpath) = path.kind
&& let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
&& cx.tcx.is_diagnostic_item(sym::SymbolIntern, def_id)
&& let ExprKind::Lit(kind) = arg.kind
&& let hir::ExprKind::Lit(kind) = arg.kind
&& let rustc_ast::LitKind::Str(_, _) = kind.node
{
cx.emit_span_lint(
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_lint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,7 @@ fn register_internals(store: &mut LintStore) {
LintId::of(USAGE_OF_QUALIFIED_TY),
LintId::of(NON_GLOB_IMPORT_OF_TYPE_IR_INHERENT),
LintId::of(USAGE_OF_TYPE_IR_INHERENT),
LintId::of(USAGE_OF_TYPE_IR_TRAITS),
LintId::of(BAD_OPT_ACCESS),
LintId::of(SPAN_USE_EQ_CTXT),
],
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_lint/src/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -943,6 +943,11 @@ pub(crate) struct TyQualified {
#[note]
pub(crate) struct TypeIrInherentUsage;

#[derive(LintDiagnostic)]
#[diag(lint_type_ir_trait_usage)]
#[note]
pub(crate) struct TypeIrTraitUsage;

#[derive(LintDiagnostic)]
#[diag(lint_non_glob_import_type_ir_inherent)]
pub(crate) struct NonGlobImportTypeIrInherent {
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_monomorphize/src/collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,8 +225,8 @@ use rustc_middle::ty::adjustment::{CustomCoerceUnsized, PointerCoercion};
use rustc_middle::ty::layout::ValidityRequirement;
use rustc_middle::ty::print::{shrunk_instance_name, with_no_trimmed_paths};
use rustc_middle::ty::{
self, GenericArgs, GenericParamDefKind, Instance, InstanceKind, Interner, Ty, TyCtxt,
TypeFoldable, TypeVisitableExt, VtblEntry,
self, GenericArgs, GenericParamDefKind, Instance, InstanceKind, Ty, TyCtxt, TypeFoldable,
TypeVisitableExt, VtblEntry,
};
use rustc_middle::util::Providers;
use rustc_middle::{bug, span_bug};
Expand Down Expand Up @@ -967,7 +967,7 @@ fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) ->
{
// `#[rustc_force_inline]` items should never be codegened. This should be caught by
// the MIR validator.
tcx.delay_bug("attempt to codegen `#[rustc_force_inline]` item");
tcx.dcx().delayed_bug("attempt to codegen `#[rustc_force_inline]` item");
}

if def_id.is_local() {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_next_trait_solver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

// tidy-alphabetical-start
#![allow(rustc::usage_of_type_ir_inherent)]
#![cfg_attr(not(bootstrap), allow(rustc::usage_of_type_ir_traits))]
// tidy-alphabetical-end

pub mod canonicalizer;
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2118,7 +2118,9 @@ symbols! {
type_changing_struct_update,
type_const,
type_id,
type_ir_infer_ctxt_like,
type_ir_inherent,
type_ir_interner,
type_length_limit,
type_macros,
type_name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use rustc_infer::traits::{Obligation, PolyTraitObligation, SelectionError};
use rustc_middle::ty::fast_reject::DeepRejectCtxt;
use rustc_middle::ty::{self, Ty, TypeVisitableExt, TypingMode};
use rustc_middle::{bug, span_bug};
use rustc_type_ir::{Interner, elaborate};
use rustc_type_ir::elaborate;
use tracing::{debug, instrument, trace};

use super::SelectionCandidate::*;
Expand Down Expand Up @@ -802,7 +802,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
| ty::UnsafeBinder(_) => {
// Only consider auto impls of unsafe traits when there are
// no unsafe fields.
if self.tcx().trait_is_unsafe(def_id) && self_ty.has_unsafe_fields() {
if self.tcx().trait_def(def_id).safety.is_unsafe()
&& self_ty.has_unsafe_fields()
{
return;
}

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_type_ir/src/infer_ctxt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ impl<I: Interner> TypingMode<I> {
}
}

#[cfg_attr(feature = "nightly", rustc_diagnostic_item = "type_ir_infer_ctxt_like")]
pub trait InferCtxtLike: Sized {
type Interner: Interner;
fn cx(&self) -> Self::Interner;
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_type_ir/src/interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use crate::solve::{CanonicalInput, ExternalConstraintsData, PredefinedOpaquesDat
use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable};
use crate::{self as ty, search_graph};

#[cfg_attr(feature = "nightly", rustc_diagnostic_item = "type_ir_interner")]
pub trait Interner:
Sized
+ Copy
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_type_ir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
feature(associated_type_defaults, never_type, rustc_attrs, negative_impls)
)]
#![cfg_attr(feature = "nightly", allow(internal_features))]
#![cfg_attr(not(bootstrap), allow(rustc::usage_of_type_ir_traits))]
// tidy-alphabetical-end

extern crate self as rustc_type_ir;
Expand Down
Loading
Loading