Skip to content

Commit a39a05d

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 13a5289 commit a39a05d

File tree

27 files changed

+215
-36
lines changed

27 files changed

+215
-36
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
@@ -129,6 +129,8 @@ lint_builtin_no_mangle_static = declaration of a `no_mangle` static
129129
lint_builtin_non_shorthand_field_patterns = the `{$ident}:` in this pattern is redundant
130130
.suggestion = use shorthand field pattern
131131
132+
lint_self_type_conversion = this conversion is useless `{$source}` to `{$target}`
133+
132134
lint_builtin_overridden_symbol_name =
133135
the linker's behavior with multiple libraries exporting duplicate symbol names is undefined and Rust cannot provide guarantees when you manually override them
134136

compiler/rustc_lint/src/builtin.rs

+94-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,95 @@ 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+
// println!("{:#?}", self.ignored_types);
3152+
cx.emit_span_lint(
3153+
SELF_TYPE_CONVERSION,
3154+
expr.span,
3155+
SelfTypeConversionDiag { source: rcvr_ty, target: ty },
3156+
);
3157+
// bug!("asdf");
3158+
}
3159+
}

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
@@ -380,7 +380,7 @@ impl<'tcx> Const<'tcx> {
380380
Ok((tcx.type_of(unevaluated.def).instantiate(tcx, unevaluated.args), c))
381381
}
382382
Ok(Err(bad_ty)) => Err(Either::Left(bad_ty)),
383-
Err(err) => Err(Either::Right(err.into())),
383+
Err(err) => Err(Either::Right(err)),
384384
}
385385
}
386386
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
@@ -1587,7 +1587,7 @@ impl<'a> Parser<'a> {
15871587
(thin_vec![], Recovered::Yes(guar))
15881588
}
15891589
};
1590-
VariantData::Struct { fields, recovered: recovered.into() }
1590+
VariantData::Struct { fields, recovered }
15911591
} else if this.check(&token::OpenDelim(Delimiter::Parenthesis)) {
15921592
let body = match this.parse_tuple_struct_body() {
15931593
Ok(body) => body,
@@ -1672,7 +1672,7 @@ impl<'a> Parser<'a> {
16721672
class_name.span,
16731673
generics.where_clause.has_where_token,
16741674
)?;
1675-
VariantData::Struct { fields, recovered: recovered.into() }
1675+
VariantData::Struct { fields, recovered }
16761676
}
16771677
// No `where` so: `struct Foo<T>;`
16781678
} else if self.eat(&token::Semi) {
@@ -1684,7 +1684,7 @@ impl<'a> Parser<'a> {
16841684
class_name.span,
16851685
generics.where_clause.has_where_token,
16861686
)?;
1687-
VariantData::Struct { fields, recovered: recovered.into() }
1687+
VariantData::Struct { fields, recovered }
16881688
// Tuple-style struct definition with optional where-clause.
16891689
} else if self.token == token::OpenDelim(Delimiter::Parenthesis) {
16901690
let body = VariantData::Tuple(self.parse_tuple_struct_body()?, DUMMY_NODE_ID);
@@ -1713,14 +1713,14 @@ impl<'a> Parser<'a> {
17131713
class_name.span,
17141714
generics.where_clause.has_where_token,
17151715
)?;
1716-
VariantData::Struct { fields, recovered: recovered.into() }
1716+
VariantData::Struct { fields, recovered }
17171717
} else if self.token == token::OpenDelim(Delimiter::Brace) {
17181718
let (fields, recovered) = self.parse_record_struct_body(
17191719
"union",
17201720
class_name.span,
17211721
generics.where_clause.has_where_token,
17221722
)?;
1723-
VariantData::Struct { fields, recovered: recovered.into() }
1723+
VariantData::Struct { fields, recovered }
17241724
} else {
17251725
let token_str = super::token_descr(&self.token);
17261726
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-1
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ impl Command {
325325
// An alternative would be to require CAP_SETGID (in
326326
// addition to CAP_SETUID) for setting the UID.
327327
if e.raw_os_error() != Some(libc::EPERM) {
328-
return Err(e.into());
328+
return Err(e);
329329
}
330330
}
331331
}

library/std/src/sys_common/process.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ impl CommandEnv {
3535
let mut result = BTreeMap::<EnvKey, OsString>::new();
3636
if !self.clear {
3737
for (k, v) in env::vars_os() {
38-
result.insert(k.into(), v);
38+
result.insert(k, v);
3939
}
4040
}
4141
for (k, maybe_v) in &self.vars {

0 commit comments

Comments
 (0)