Skip to content

Commit 6867ba4

Browse files
committed
[Experimental] <T as Into<T>>::into lint
Running crater to see how common that pattern is. The Lint would have to be at most warn-by-default because there are a handful of cases detected that are actually perfectly reasonable (`type` aliases with per-platform `cfg`, or macros) which are now at best half-heartedly handled. I've detected a handful of cases where we're calling `.into()` unnecessarily in the `rustc` codebase as well, and changed those.
1 parent 6de928d commit 6867ba4

File tree

28 files changed

+220
-34
lines changed

28 files changed

+220
-34
lines changed

compiler/rustc_ast/src/ast.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -3464,7 +3464,7 @@ impl From<ForeignItemKind> for ItemKind {
34643464
fn from(foreign_item_kind: ForeignItemKind) -> ItemKind {
34653465
match foreign_item_kind {
34663466
ForeignItemKind::Static(box static_foreign_item) => {
3467-
ItemKind::Static(Box::new(static_foreign_item.into()))
3467+
ItemKind::Static(Box::new(static_foreign_item))
34683468
}
34693469
ForeignItemKind::Fn(fn_kind) => ItemKind::Fn(fn_kind),
34703470
ForeignItemKind::TyAlias(ty_alias_kind) => ItemKind::TyAlias(ty_alias_kind),
@@ -3478,9 +3478,7 @@ impl TryFrom<ItemKind> for ForeignItemKind {
34783478

34793479
fn try_from(item_kind: ItemKind) -> Result<ForeignItemKind, ItemKind> {
34803480
Ok(match item_kind {
3481-
ItemKind::Static(box static_item) => {
3482-
ForeignItemKind::Static(Box::new(static_item.into()))
3483-
}
3481+
ItemKind::Static(box static_item) => ForeignItemKind::Static(Box::new(static_item)),
34843482
ItemKind::Fn(fn_kind) => ForeignItemKind::Fn(fn_kind),
34853483
ItemKind::TyAlias(ty_alias_kind) => ForeignItemKind::TyAlias(ty_alias_kind),
34863484
ItemKind::MacCall(a) => ForeignItemKind::MacCall(a),

compiler/rustc_const_eval/src/const_eval/eval_queries.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>(
7878
ecx.push_stack_frame_raw(
7979
cid.instance,
8080
body,
81-
&ret.clone().into(),
81+
&ret.clone(),
8282
StackPopCleanup::Root { cleanup: false },
8383
)?;
8484
ecx.storage_live_for_always_live_locals()?;

compiler/rustc_const_eval/src/interpret/call.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -854,7 +854,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
854854
(Abi::Rust, fn_abi),
855855
&[FnArg::Copy(arg.into())],
856856
false,
857-
&ret.into(),
857+
&ret,
858858
Some(target),
859859
unwind,
860860
)

compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2533,7 +2533,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
25332533
other_generic_param.name.ident() == generic_param.name.ident()
25342534
},
25352535
) {
2536-
idxs_matched.push(other_idx.into());
2536+
idxs_matched.push(other_idx);
25372537
}
25382538

25392539
if idxs_matched.is_empty() {

compiler/rustc_lint/messages.ftl

+2
Original file line numberDiff line numberDiff line change
@@ -733,6 +733,8 @@ lint_reserved_prefix = prefix `{$prefix}` is unknown
733733
.label = unknown prefix
734734
.suggestion = insert whitespace here to avoid this being parsed as a prefix in Rust 2021
735735
736+
lint_self_type_conversion = this conversion is useless `{$source}` to `{$target}`
737+
736738
lint_shadowed_into_iter =
737739
this method call resolves to `<&{$target} as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<{$target} as IntoIterator>::into_iter` in Rust {$edition}
738740
.use_iter_suggestion = use `.iter()` instead of `.into_iter()` to avoid ambiguity

compiler/rustc_lint/src/builtin.rs

+98-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ use crate::lints::{
6666
BuiltinTrivialBounds, BuiltinTypeAliasBounds, BuiltinUngatedAsyncFnTrackCaller,
6767
BuiltinUnpermittedTypeInit, BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub,
6868
BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub,
69-
BuiltinWhileTrue, InvalidAsmLabel,
69+
BuiltinWhileTrue, InvalidAsmLabel, SelfTypeConversionDiag,
7070
};
7171
use crate::nonstandard_style::{method_context, MethodLateContext};
7272
use crate::{
@@ -1604,6 +1604,7 @@ declare_lint_pass!(
16041604
SoftLints => [
16051605
WHILE_TRUE,
16061606
NON_SHORTHAND_FIELD_PATTERNS,
1607+
SELF_TYPE_CONVERSION,
16071608
UNSAFE_CODE,
16081609
MISSING_DOCS,
16091610
MISSING_COPY_IMPLEMENTATIONS,
@@ -3064,3 +3065,99 @@ impl EarlyLintPass for SpecialModuleName {
30643065
}
30653066
}
30663067
}
3068+
3069+
declare_lint! {
3070+
/// The `self_type_conversion` lint detects when a call to `.into()` does not have any effect.
3071+
///
3072+
/// ### Example
3073+
///
3074+
/// ```rust,compile_fail
3075+
/// fn main() {
3076+
/// let () = ().into();
3077+
/// }
3078+
/// ```
3079+
///
3080+
/// {{produces}}
3081+
///
3082+
/// ### Explanation
3083+
///
3084+
pub SELF_TYPE_CONVERSION,
3085+
Deny,
3086+
"",
3087+
}
3088+
3089+
pub struct SelfTypeConversion<'tcx> {
3090+
ignored_types: Vec<Ty<'tcx>>,
3091+
}
3092+
3093+
impl_lint_pass!(SelfTypeConversion<'_> => [SELF_TYPE_CONVERSION]);
3094+
3095+
impl SelfTypeConversion<'_> {
3096+
pub fn new() -> Self {
3097+
Self { ignored_types: vec![] }
3098+
}
3099+
}
3100+
3101+
impl<'tcx> LateLintPass<'tcx> for SelfTypeConversion<'tcx> {
3102+
fn check_item_post(&mut self, cx: &LateContext<'tcx>, item: &hir::Item<'_>) {
3103+
let hir::ItemKind::Use(path, kind) = item.kind else { return };
3104+
tracing::info!("{:#?}", item);
3105+
tracing::info!(?path, ?kind);
3106+
for res in &path.res {
3107+
let Res::Def(DefKind::TyAlias, def_id) = res else { continue };
3108+
let ty = cx.tcx.type_of(def_id).instantiate_identity();
3109+
let name = cx.tcx.item_name(*def_id);
3110+
// println!("{ty:?} {name:?}");
3111+
self.ignored_types.push(ty);
3112+
for stripped in cx.tcx.stripped_cfg_items(def_id.krate) {
3113+
if stripped.name.name == name {
3114+
tracing::info!("{name:#?}");
3115+
}
3116+
}
3117+
}
3118+
}
3119+
3120+
fn check_expr_post(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) {
3121+
let hir::ExprKind::MethodCall(_segment, rcvr, args, _) = expr.kind else { return };
3122+
if !args.is_empty() {
3123+
tracing::info!("non-empty args");
3124+
return;
3125+
}
3126+
let ty = cx.typeck_results().expr_ty(expr);
3127+
let rcvr_ty = cx.typeck_results().expr_ty(rcvr);
3128+
tracing::info!(?ty, ?rcvr_ty);
3129+
3130+
if ty != rcvr_ty {
3131+
tracing::info!("different types");
3132+
return;
3133+
}
3134+
if self.ignored_types.contains(&rcvr_ty) {
3135+
return;
3136+
}
3137+
let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) else {
3138+
tracing::info!("no type dependent def id");
3139+
return;
3140+
};
3141+
tracing::info!(?def_id);
3142+
if Some(def_id) != cx.tcx.get_diagnostic_item(sym::into_fn) {
3143+
tracing::info!("not into_fn {:?}", cx.tcx.get_diagnostic_item(sym::into_fn));
3144+
return;
3145+
}
3146+
tracing::info!(?def_id);
3147+
tracing::info!(?expr);
3148+
if expr.span.macro_backtrace().next().is_some() {
3149+
return;
3150+
}
3151+
if cx.tcx.sess.source_map().span_to_embeddable_string(expr.span).contains("symbolize/gimli") {
3152+
// HACK
3153+
return;
3154+
}
3155+
// println!("{:#?}", self.ignored_types);
3156+
cx.emit_span_lint(
3157+
SELF_TYPE_CONVERSION,
3158+
expr.span,
3159+
SelfTypeConversionDiag { source: rcvr_ty, target: ty },
3160+
);
3161+
// bug!("asdf");
3162+
}
3163+
}

compiler/rustc_lint/src/lib.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ early_lint_methods!(
185185
late_lint_methods!(
186186
declare_combined_late_lint_pass,
187187
[
188-
BuiltinCombinedModuleLateLintPass,
188+
BuiltinCombinedModuleLateLintPass<'tcx>,
189189
[
190190
ForLoopsOverFallibles: ForLoopsOverFallibles,
191191
DerefIntoDynSupertrait: DerefIntoDynSupertrait,
@@ -203,6 +203,7 @@ late_lint_methods!(
203203
UnitBindings: UnitBindings,
204204
NonUpperCaseGlobals: NonUpperCaseGlobals,
205205
NonShorthandFieldPatterns: NonShorthandFieldPatterns,
206+
SelfTypeConversion<'tcx>: SelfTypeConversion::new(),
206207
UnusedAllocation: UnusedAllocation,
207208
// Depends on types used in type definitions
208209
MissingCopyImplementations: MissingCopyImplementations,
@@ -268,6 +269,7 @@ fn register_builtins(store: &mut LintStore) {
268269
store.register_lints(&BuiltinCombinedModuleLateLintPass::get_lints());
269270
store.register_lints(&foreign_modules::get_lints());
270271

272+
store.register_late_pass(move |_tcx| Box::new(SelfTypeConversion::new()));
271273
add_lint_group!(
272274
"nonstandard_style",
273275
NON_CAMEL_CASE_TYPES,
@@ -298,6 +300,7 @@ fn register_builtins(store: &mut LintStore) {
298300
UNUSED_PARENS,
299301
UNUSED_BRACES,
300302
REDUNDANT_SEMICOLONS,
303+
// SELF_TYPE_CONVERSION,
301304
MAP_UNIT_FN
302305
);
303306

compiler/rustc_lint/src/lints.rs

+7
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,13 @@ pub struct BuiltinNonShorthandFieldPatterns {
7272
pub prefix: &'static str,
7373
}
7474

75+
#[derive(LintDiagnostic)]
76+
#[diag(lint_self_type_conversion)]
77+
pub struct SelfTypeConversionDiag<'t> {
78+
pub source: Ty<'t>,
79+
pub target: Ty<'t>,
80+
}
81+
7582
#[derive(LintDiagnostic)]
7683
pub enum BuiltinUnsafe {
7784
#[diag(lint_builtin_allow_internal_unsafe)]

compiler/rustc_lint/src/passes.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,13 @@ macro_rules! expand_combined_late_lint_pass_methods {
9393
/// runtime.
9494
#[macro_export]
9595
macro_rules! declare_combined_late_lint_pass {
96-
([$v:vis $name:ident, [$($pass:ident: $constructor:expr,)*]], $methods:tt) => (
96+
([$v:vis $name:ident$(<$lt:lifetime>)?, [$($pass:ident$(<$inner_lt:lifetime>)?: $constructor:expr,)*]], $methods:tt) => (
9797
#[allow(non_snake_case)]
98-
$v struct $name {
99-
$($pass: $pass,)*
98+
$v struct $name$(<$lt>)? {
99+
$($pass: $pass$(<$inner_lt>)?,)*
100100
}
101101

102-
impl $name {
102+
impl$(<$lt>)? $name$(<$lt>)? {
103103
$v fn new() -> Self {
104104
Self {
105105
$($pass: $constructor,)*
@@ -113,12 +113,12 @@ macro_rules! declare_combined_late_lint_pass {
113113
}
114114
}
115115

116-
impl<'tcx> $crate::LateLintPass<'tcx> for $name {
116+
impl<'tcx> $crate::LateLintPass<'tcx> for $name$(<$lt>)? {
117117
$crate::expand_combined_late_lint_pass_methods!([$($pass),*], $methods);
118118
}
119119

120120
#[allow(rustc::lint_pass_impl_without_macro)]
121-
impl $crate::LintPass for $name {
121+
impl$(<$lt>)? $crate::LintPass for $name$(<$lt>)? {
122122
fn name(&self) -> &'static str {
123123
panic!()
124124
}

compiler/rustc_middle/src/ty/consts.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ impl<'tcx> Const<'tcx> {
384384
Ok((tcx.type_of(unevaluated.def).instantiate(tcx, unevaluated.args), c))
385385
}
386386
Ok(Err(bad_ty)) => Err(Either::Left(bad_ty)),
387-
Err(err) => Err(Either::Right(err.into())),
387+
Err(err) => Err(Either::Right(err)),
388388
}
389389
}
390390
ConstKind::Value(ty, val) => Ok((ty, val)),

compiler/rustc_middle/src/ty/print/pretty.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1526,7 +1526,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
15261526

15271527
let precedence = |binop: rustc_middle::mir::BinOp| {
15281528
use rustc_ast::util::parser::AssocOp;
1529-
AssocOp::from_ast_binop(binop.to_hir_binop().into()).precedence()
1529+
AssocOp::from_ast_binop(binop.to_hir_binop()).precedence()
15301530
};
15311531
let op_precedence = precedence(op);
15321532
let formatted_op = op.to_hir_binop().as_str();

compiler/rustc_parse/src/parser/item.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -1586,7 +1586,7 @@ impl<'a> Parser<'a> {
15861586
(thin_vec![], Recovered::Yes(guar))
15871587
}
15881588
};
1589-
VariantData::Struct { fields, recovered: recovered.into() }
1589+
VariantData::Struct { fields, recovered }
15901590
} else if this.check(&token::OpenDelim(Delimiter::Parenthesis)) {
15911591
let body = match this.parse_tuple_struct_body() {
15921592
Ok(body) => body,
@@ -1670,7 +1670,7 @@ impl<'a> Parser<'a> {
16701670
class_name.span,
16711671
generics.where_clause.has_where_token,
16721672
)?;
1673-
VariantData::Struct { fields, recovered: recovered.into() }
1673+
VariantData::Struct { fields, recovered }
16741674
}
16751675
// No `where` so: `struct Foo<T>;`
16761676
} else if self.eat(&token::Semi) {
@@ -1682,7 +1682,7 @@ impl<'a> Parser<'a> {
16821682
class_name.span,
16831683
generics.where_clause.has_where_token,
16841684
)?;
1685-
VariantData::Struct { fields, recovered: recovered.into() }
1685+
VariantData::Struct { fields, recovered }
16861686
// Tuple-style struct definition with optional where-clause.
16871687
} else if self.token == token::OpenDelim(Delimiter::Parenthesis) {
16881688
let body = VariantData::Tuple(self.parse_tuple_struct_body()?, DUMMY_NODE_ID);
@@ -1711,14 +1711,14 @@ impl<'a> Parser<'a> {
17111711
class_name.span,
17121712
generics.where_clause.has_where_token,
17131713
)?;
1714-
VariantData::Struct { fields, recovered: recovered.into() }
1714+
VariantData::Struct { fields, recovered }
17151715
} else if self.token == token::OpenDelim(Delimiter::Brace) {
17161716
let (fields, recovered) = self.parse_record_struct_body(
17171717
"union",
17181718
class_name.span,
17191719
generics.where_clause.has_where_token,
17201720
)?;
1721-
VariantData::Struct { fields, recovered: recovered.into() }
1721+
VariantData::Struct { fields, recovered }
17221722
} else {
17231723
let token_str = super::token_descr(&self.token);
17241724
let msg = format!("expected `where` or `{{` after union name, found {token_str}");

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1045,6 +1045,7 @@ symbols! {
10451045
integer_: "integer", // underscore to avoid clashing with the function `sym::integer` below
10461046
integral,
10471047
into_async_iter_into_iter,
1048+
into_fn,
10481049
into_future,
10491050
into_iter,
10501051
intra_doc_pointers,

compiler/rustc_symbol_mangling/src/v0.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
382382
let consts = [
383383
start.unwrap_or(self.tcx.consts.unit),
384384
end.unwrap_or(self.tcx.consts.unit),
385-
ty::Const::from_bool(self.tcx, include_end).into(),
385+
ty::Const::from_bool(self.tcx, include_end),
386386
];
387387
// HACK: Represent as tuple until we have something better.
388388
// HACK: constants are used in arrays, even if the types don't match.

compiler/rustc_trait_selection/src/traits/project.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ pub(super) fn opt_normalize_projection_term<'a, 'b, 'tcx>(
406406
debug!("opt_normalize_projection_type: found error");
407407
let result = normalize_to_error(selcx, param_env, projection_term, cause, depth);
408408
obligations.extend(result.obligations);
409-
return Ok(Some(result.value.into()));
409+
return Ok(Some(result.value));
410410
}
411411
}
412412

@@ -476,7 +476,7 @@ pub(super) fn opt_normalize_projection_term<'a, 'b, 'tcx>(
476476
}
477477
let result = normalize_to_error(selcx, param_env, projection_term, cause, depth);
478478
obligations.extend(result.obligations);
479-
Ok(Some(result.value.into()))
479+
Ok(Some(result.value))
480480
}
481481
}
482482
}

library/core/src/convert/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,7 @@ pub trait AsMut<T: ?Sized> {
445445
#[stable(feature = "rust1", since = "1.0.0")]
446446
pub trait Into<T>: Sized {
447447
/// Converts this type into the (usually inferred) input type.
448+
#[rustc_diagnostic_item = "into_fn"]
448449
#[must_use]
449450
#[stable(feature = "rust1", since = "1.0.0")]
450451
fn into(self) -> T;

library/std/src/os/fd/owned.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ impl From<crate::net::TcpStream> for OwnedFd {
328328
/// Takes ownership of a [`TcpStream`](crate::net::TcpStream)'s socket file descriptor.
329329
#[inline]
330330
fn from(tcp_stream: crate::net::TcpStream) -> OwnedFd {
331-
tcp_stream.into_inner().into_socket().into_inner().into_inner().into()
331+
tcp_stream.into_inner().into_socket().into_inner().into_inner()
332332
}
333333
}
334334

@@ -355,7 +355,7 @@ impl From<crate::net::TcpListener> for OwnedFd {
355355
/// Takes ownership of a [`TcpListener`](crate::net::TcpListener)'s socket file descriptor.
356356
#[inline]
357357
fn from(tcp_listener: crate::net::TcpListener) -> OwnedFd {
358-
tcp_listener.into_inner().into_socket().into_inner().into_inner().into()
358+
tcp_listener.into_inner().into_socket().into_inner().into_inner()
359359
}
360360
}
361361

@@ -382,7 +382,7 @@ impl From<crate::net::UdpSocket> for OwnedFd {
382382
/// Takes ownership of a [`UdpSocket`](crate::net::UdpSocket)'s file descriptor.
383383
#[inline]
384384
fn from(udp_socket: crate::net::UdpSocket) -> OwnedFd {
385-
udp_socket.into_inner().into_socket().into_inner().into_inner().into()
385+
udp_socket.into_inner().into_socket().into_inner().into_inner()
386386
}
387387
}
388388

library/std/src/sys/pal/unix/process/process_unix.rs

+1
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,7 @@ impl Command {
282282
// have the drop glue anyway because this code never returns (the
283283
// child will either exec() or invoke libc::exit)
284284
#[cfg(not(any(target_os = "tvos", target_os = "watchos")))]
285+
#[cfg_attr(not(bootstrap), allow(self_type_conversion))]
285286
unsafe fn do_exec(
286287
&mut self,
287288
stdio: ChildPipes,

0 commit comments

Comments
 (0)