Rollup merge of #125572 - mu001999-contrib:dead/enhance, r=pnkfelix

Detect pub structs never constructed and unused associated constants

<!--
If this PR is related to an unstable feature or an otherwise tracked effort,
please link to the relevant tracking issue here. If you don't know of a related
tracking issue or there are none, feel free to ignore this.

This PR will get automatically assigned to a reviewer. In case you would like
a specific user to review your work, you can assign it to them by using

    r​? <reviewer name>
-->

Lints never constructed public structs.

If we don't provide public methods to construct public structs with private fields, and don't construct them in the local crate. They would be never constructed. So that we can detect such public structs.

---
Update:

Also lints unused associated constants in traits.
This commit is contained in:
Matthias Krüger 2024-06-07 20:14:28 +02:00 committed by GitHub
commit 13314df21b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
32 changed files with 272 additions and 88 deletions

View file

@ -15,7 +15,7 @@
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::middle::privacy::Level;
use rustc_middle::query::Providers;
use rustc_middle::ty::{self, TyCtxt};
use rustc_middle::ty::{self, AssocItemContainer, TyCtxt};
use rustc_middle::{bug, span_bug};
use rustc_session::lint;
use rustc_session::lint::builtin::DEAD_CODE;
@ -44,16 +44,63 @@ fn should_explore(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
)
}
fn ty_ref_to_pub_struct(tcx: TyCtxt<'_>, ty: &hir::Ty<'_>) -> bool {
struct Publicness {
ty_is_public: bool,
ty_and_all_fields_are_public: bool,
}
impl Publicness {
fn new(ty_is_public: bool, ty_and_all_fields_are_public: bool) -> Self {
Self { ty_is_public, ty_and_all_fields_are_public }
}
}
fn struct_all_fields_are_public(tcx: TyCtxt<'_>, id: DefId) -> bool {
// treat PhantomData and positional ZST as public,
// we don't want to lint types which only have them,
// cause it's a common way to use such types to check things like well-formedness
tcx.adt_def(id).all_fields().all(|field| {
let field_type = tcx.type_of(field.did).instantiate_identity();
if field_type.is_phantom_data() {
return true;
}
let is_positional = field.name.as_str().starts_with(|c: char| c.is_ascii_digit());
if is_positional
&& tcx
.layout_of(tcx.param_env(field.did).and(field_type))
.map_or(true, |layout| layout.is_zst())
{
return true;
}
field.vis.is_public()
})
}
/// check struct and its fields are public or not,
/// for enum and union, just check they are public,
/// and doesn't solve types like &T for now, just skip them
fn ty_ref_to_pub_struct(tcx: TyCtxt<'_>, ty: &hir::Ty<'_>) -> Publicness {
if let TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind
&& let Res::Def(def_kind, def_id) = path.res
&& def_id.is_local()
&& matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union)
{
tcx.visibility(def_id).is_public()
} else {
true
return match def_kind {
DefKind::Enum | DefKind::Union => {
let ty_is_public = tcx.visibility(def_id).is_public();
Publicness::new(ty_is_public, ty_is_public)
}
DefKind::Struct => {
let ty_is_public = tcx.visibility(def_id).is_public();
Publicness::new(
ty_is_public,
ty_is_public && struct_all_fields_are_public(tcx, def_id),
)
}
_ => Publicness::new(true, true),
};
}
Publicness::new(true, true)
}
/// Determine if a work from the worklist is coming from the a `#[allow]`
@ -427,9 +474,11 @@ fn visit_node(&mut self, node: Node<'tcx>) {
{
if matches!(trait_item.kind, hir::TraitItemKind::Fn(..))
&& !ty_ref_to_pub_struct(self.tcx, impl_ref.self_ty)
.ty_and_all_fields_are_public
{
// skip methods of private ty,
// they would be solved in `solve_rest_impl_items`
// skip impl-items of non pure pub ty,
// cause we don't know the ty is constructed or not,
// check these later in `solve_rest_impl_items`
continue;
}
@ -510,22 +559,21 @@ fn impl_item_with_used_self(&mut self, impl_id: hir::ItemId, impl_item_id: Local
&& let Some(local_def_id) = def_id.as_local()
&& matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union)
{
if self.tcx.visibility(impl_item_id).is_public() {
// for the public method, we don't know the trait item is used or not,
// so we mark the method live if the self is used
return self.live_symbols.contains(&local_def_id);
}
if let Some(trait_item_id) = self.tcx.associated_item(impl_item_id).trait_item_def_id
&& let Some(local_id) = trait_item_id.as_local()
{
// for the private method, we can know the trait item is used or not,
// for the local impl item, we can know the trait item is used or not,
// so we mark the method live if the self is used and the trait item is used
return self.live_symbols.contains(&local_id)
&& self.live_symbols.contains(&local_def_id);
self.live_symbols.contains(&local_id) && self.live_symbols.contains(&local_def_id)
} else {
// for the foreign method and inherent pub method,
// we don't know the trait item or the method is used or not,
// so we mark the method live if the self is used
self.live_symbols.contains(&local_def_id)
}
} else {
false
}
false
}
}
@ -747,7 +795,9 @@ fn check_item<'tcx>(
.iter()
.filter_map(|def_id| def_id.as_local());
let ty_is_pub = ty_ref_to_pub_struct(tcx, tcx.hir().item(id).expect_impl().self_ty);
let self_ty = tcx.hir().item(id).expect_impl().self_ty;
let Publicness { ty_is_public, ty_and_all_fields_are_public } =
ty_ref_to_pub_struct(tcx, self_ty);
// And we access the Map here to get HirId from LocalDefId
for local_def_id in local_def_ids {
@ -763,18 +813,20 @@ fn check_item<'tcx>(
// for trait impl blocks,
// mark the method live if the self_ty is public,
// or the method is public and may construct self
if of_trait
&& (!matches!(tcx.def_kind(local_def_id), DefKind::AssocFn)
|| tcx.visibility(local_def_id).is_public()
&& (ty_is_pub || may_construct_self))
if of_trait && matches!(tcx.def_kind(local_def_id), DefKind::AssocTy)
|| tcx.visibility(local_def_id).is_public()
&& (ty_and_all_fields_are_public || may_construct_self)
{
// if the impl item is public,
// and the ty may be constructed or can be constructed in foreign crates,
// mark the impl item live
worklist.push((local_def_id, ComesFromAllowExpect::No));
} else if let Some(comes_from_allow) =
has_allow_dead_code_or_lang_attr(tcx, local_def_id)
{
worklist.push((local_def_id, comes_from_allow));
} else if of_trait {
// private method || public method not constructs self
} else if of_trait || tcx.visibility(local_def_id).is_public() && ty_is_public {
// private impl items of traits || public impl items not constructs self
unsolved_impl_items.push((id, local_def_id));
}
}
@ -841,6 +893,14 @@ fn create_and_seed_worklist(
effective_vis
.is_public_at_level(Level::Reachable)
.then_some(id)
.filter(|&id|
// checks impls, impl-items and pub structs with all public fields later
match tcx.def_kind(id) {
DefKind::Impl { .. } => false,
DefKind::AssocConst | DefKind::AssocFn => !matches!(tcx.associated_item(id).container, AssocItemContainer::ImplContainer),
DefKind::Struct => struct_all_fields_are_public(tcx, id.to_def_id()),
_ => true
})
.map(|id| (id, ComesFromAllowExpect::No))
})
// Seed entry point
@ -1113,10 +1173,15 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) {
|| (def_kind == DefKind::Trait && live_symbols.contains(&item.owner_id.def_id))
{
for &def_id in tcx.associated_item_def_ids(item.owner_id.def_id) {
// We have diagnosed unused methods in traits
// We have diagnosed unused assoc consts and fns in traits
if matches!(def_kind, DefKind::Impl { of_trait: true })
&& tcx.def_kind(def_id) == DefKind::AssocFn
|| def_kind == DefKind::Trait && tcx.def_kind(def_id) != DefKind::AssocFn
&& matches!(tcx.def_kind(def_id), DefKind::AssocConst | DefKind::AssocFn)
// skip unused public inherent methods,
// cause we have diagnosed unconstructed struct
|| matches!(def_kind, DefKind::Impl { of_trait: false })
&& tcx.visibility(def_id).is_public()
&& ty_ref_to_pub_struct(tcx, tcx.hir().item(item).expect_impl().self_ty).ty_is_public
|| def_kind == DefKind::Trait && tcx.def_kind(def_id) == DefKind::AssocTy
{
continue;
}

View file

@ -22,16 +22,16 @@ fn get<T2>(self, x: T2) -> (T, T2) {
}
}
pub struct LifeTimeOnly<'a> {
pub struct _LifeTimeOnly<'a> {
_a: &'a u32,
}
impl<'a> LifeTimeOnly<'a> {
//~ MONO_ITEM fn LifeTimeOnly::<'_>::foo
impl<'a> _LifeTimeOnly<'a> {
//~ MONO_ITEM fn _LifeTimeOnly::<'_>::foo
pub fn foo(&self) {}
//~ MONO_ITEM fn LifeTimeOnly::<'_>::bar
//~ MONO_ITEM fn _LifeTimeOnly::<'_>::bar
pub fn bar(&'a self) {}
//~ MONO_ITEM fn LifeTimeOnly::<'_>::baz
//~ MONO_ITEM fn _LifeTimeOnly::<'_>::baz
pub fn baz<'b>(&'b self) {}
pub fn non_instantiated<T>(&self) {}

View file

@ -5,44 +5,44 @@
use std::ops::{Add, Deref, Index, IndexMut};
pub struct Indexable {
pub struct _Indexable {
data: [u8; 3],
}
impl Index<usize> for Indexable {
impl Index<usize> for _Indexable {
type Output = u8;
//~ MONO_ITEM fn <Indexable as std::ops::Index<usize>>::index
//~ MONO_ITEM fn <_Indexable as std::ops::Index<usize>>::index
fn index(&self, index: usize) -> &Self::Output {
if index >= 3 { &self.data[0] } else { &self.data[index] }
}
}
impl IndexMut<usize> for Indexable {
//~ MONO_ITEM fn <Indexable as std::ops::IndexMut<usize>>::index_mut
impl IndexMut<usize> for _Indexable {
//~ MONO_ITEM fn <_Indexable as std::ops::IndexMut<usize>>::index_mut
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
if index >= 3 { &mut self.data[0] } else { &mut self.data[index] }
}
}
//~ MONO_ITEM fn <Equatable as std::cmp::PartialEq>::eq
//~ MONO_ITEM fn <Equatable as std::cmp::PartialEq>::ne
//~ MONO_ITEM fn <_Equatable as std::cmp::PartialEq>::eq
//~ MONO_ITEM fn <_Equatable as std::cmp::PartialEq>::ne
#[derive(PartialEq)]
pub struct Equatable(u32);
pub struct _Equatable(u32);
impl Add<u32> for Equatable {
impl Add<u32> for _Equatable {
type Output = u32;
//~ MONO_ITEM fn <Equatable as std::ops::Add<u32>>::add
//~ MONO_ITEM fn <_Equatable as std::ops::Add<u32>>::add
fn add(self, rhs: u32) -> u32 {
self.0 + rhs
}
}
impl Deref for Equatable {
impl Deref for _Equatable {
type Target = u32;
//~ MONO_ITEM fn <Equatable as std::ops::Deref>::deref
//~ MONO_ITEM fn <_Equatable as std::ops::Deref>::deref
fn deref(&self) -> &Self::Target {
&self.0
}

View file

@ -4,6 +4,7 @@
extern crate re_rebalance_coherence_lib as lib;
use lib::*;
#[allow(dead_code)]
struct Oracle;
impl Backend for Oracle {}
impl<'a, T:'a, Tab> QueryFragment<Oracle> for BatchInsert<'a, T, Tab> {}

View file

@ -2,6 +2,7 @@
//@ run-pass
#[allow(dead_code)]
#[repr(C)]
pub struct Loaf<T: Sized, const N: usize = 1> {
head: [T; N],

View file

@ -16,7 +16,8 @@ impl BlockCipher for BarCipher {
const BLOCK_SIZE: usize = 32;
}
pub struct Block<C>(#[allow(dead_code)] C);
#[allow(dead_code)]
pub struct Block<C>(C);
pub fn test<C: BlockCipher, const M: usize>()
where

View file

@ -6,6 +6,7 @@
use std::mem::MaybeUninit;
#[allow(dead_code)]
#[repr(transparent)]
pub struct MaybeUninitWrapper<const N: usize>(MaybeUninit<[u64; N]>);

View file

@ -1,9 +1,9 @@
#![forbid(dead_code)]
#[derive(Debug)]
pub struct Whatever {
pub struct Whatever { //~ ERROR struct `Whatever` is never constructed
pub field0: (),
field1: (), //~ ERROR fields `field1`, `field2`, `field3`, and `field4` are never read
field1: (),
field2: (),
field3: (),
field4: (),

View file

@ -1,19 +1,9 @@
error: fields `field1`, `field2`, `field3`, and `field4` are never read
--> $DIR/clone-debug-dead-code-in-the-same-struct.rs:6:5
error: struct `Whatever` is never constructed
--> $DIR/clone-debug-dead-code-in-the-same-struct.rs:4:12
|
LL | pub struct Whatever {
| -------- fields in this struct
LL | pub field0: (),
LL | field1: (),
| ^^^^^^
LL | field2: (),
| ^^^^^^
LL | field3: (),
| ^^^^^^
LL | field4: (),
| ^^^^^^
| ^^^^^^^^
|
= note: `Whatever` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis
note: the lint level is defined here
--> $DIR/clone-debug-dead-code-in-the-same-struct.rs:1:11
|

View file

@ -44,6 +44,7 @@ pub trait MyTrait<T> {
fn dummy(&self, t: T) -> T { panic!() }
}
#[allow(dead_code)]
pub struct MyContainer<'a, T:'a> {
foos: Vec<&'a (dyn MyTrait<T>+'a)> ,
}

View file

@ -46,11 +46,10 @@ struct UsedStruct1 {
impl SemiUsedStruct {
fn la_la_la() {}
}
struct StructUsedAsField;
struct StructUsedAsField; //~ ERROR struct `StructUsedAsField` is never constructed
pub struct StructUsedInEnum;
struct StructUsedInGeneric;
pub struct PubStruct2 {
#[allow(dead_code)]
pub struct PubStruct2 { //~ ERROR struct `PubStruct2` is never constructed
struct_used_as_field: *const StructUsedAsField
}

View file

@ -22,14 +22,26 @@ error: struct `PrivStruct` is never constructed
LL | struct PrivStruct;
| ^^^^^^^^^^
error: struct `StructUsedAsField` is never constructed
--> $DIR/lint-dead-code-1.rs:49:8
|
LL | struct StructUsedAsField;
| ^^^^^^^^^^^^^^^^^
error: struct `PubStruct2` is never constructed
--> $DIR/lint-dead-code-1.rs:52:12
|
LL | pub struct PubStruct2 {
| ^^^^^^^^^^
error: enum `priv_enum` is never used
--> $DIR/lint-dead-code-1.rs:64:6
--> $DIR/lint-dead-code-1.rs:63:6
|
LL | enum priv_enum { foo2, bar2 }
| ^^^^^^^^^
error: variant `bar3` is never constructed
--> $DIR/lint-dead-code-1.rs:67:5
--> $DIR/lint-dead-code-1.rs:66:5
|
LL | enum used_enum {
| --------- variant in this enum
@ -38,25 +50,25 @@ LL | bar3
| ^^^^
error: function `priv_fn` is never used
--> $DIR/lint-dead-code-1.rs:88:4
--> $DIR/lint-dead-code-1.rs:87:4
|
LL | fn priv_fn() {
| ^^^^^^^
error: function `foo` is never used
--> $DIR/lint-dead-code-1.rs:93:4
--> $DIR/lint-dead-code-1.rs:92:4
|
LL | fn foo() {
| ^^^
error: function `bar` is never used
--> $DIR/lint-dead-code-1.rs:98:4
--> $DIR/lint-dead-code-1.rs:97:4
|
LL | fn bar() {
| ^^^
error: function `baz` is never used
--> $DIR/lint-dead-code-1.rs:102:4
--> $DIR/lint-dead-code-1.rs:101:4
|
LL | fn baz() -> impl Copy {
| ^^^
@ -67,5 +79,5 @@ error: struct `Bar` is never constructed
LL | pub struct Bar;
| ^^^
error: aborting due to 10 previous errors
error: aborting due to 12 previous errors

View file

@ -0,0 +1,20 @@
#![deny(dead_code)]
trait Trait {
const UNUSED_CONST: i32; //~ ERROR associated constant `UNUSED_CONST` is never used
const USED_CONST: i32;
fn foo(&self) {}
}
pub struct T(());
impl Trait for T {
const UNUSED_CONST: i32 = 0;
const USED_CONST: i32 = 1;
}
fn main() {
T(()).foo();
T::USED_CONST;
}

View file

@ -0,0 +1,16 @@
error: associated constant `UNUSED_CONST` is never used
--> $DIR/unused-assoc-const.rs:4:11
|
LL | trait Trait {
| ----- associated constant in this trait
LL | const UNUSED_CONST: i32;
| ^^^^^^^^^^^^
|
note: the lint level is defined here
--> $DIR/unused-assoc-const.rs:1:9
|
LL | #![deny(dead_code)]
| ^^^^^^^^^
error: aborting due to 1 previous error

View file

@ -0,0 +1,48 @@
#![deny(dead_code)]
pub struct NotLint1(());
pub struct NotLint2(std::marker::PhantomData<i32>);
pub struct NeverConstructed(i32); //~ ERROR struct `NeverConstructed` is never constructed
impl NeverConstructed {
pub fn not_construct_self(&self) {}
}
impl Clone for NeverConstructed {
fn clone(&self) -> NeverConstructed {
NeverConstructed(0)
}
}
pub trait Trait {
fn not_construct_self(&self);
}
impl Trait for NeverConstructed {
fn not_construct_self(&self) {
self.0;
}
}
pub struct Constructed(i32);
impl Constructed {
pub fn construct_self() -> Self {
Constructed(0)
}
}
impl Clone for Constructed {
fn clone(&self) -> Constructed {
Constructed(0)
}
}
impl Trait for Constructed {
fn not_construct_self(&self) {
self.0;
}
}
fn main() {}

View file

@ -0,0 +1,14 @@
error: struct `NeverConstructed` is never constructed
--> $DIR/unused-pub-struct.rs:6:12
|
LL | pub struct NeverConstructed(i32);
| ^^^^^^^^^^^^^^^^
|
note: the lint level is defined here
--> $DIR/unused-pub-struct.rs:1:9
|
LL | #![deny(dead_code)]
| ^^^^^^^^^
error: aborting due to 1 previous error

View file

@ -1,7 +1,8 @@
// Regression test for issues #100790 and #106439.
//@ run-rustfix
pub struct Example(#[allow(dead_code)] usize)
#[allow(dead_code)]
pub struct Example(usize)
where
(): Sized;
//~^^^ ERROR where clauses are not allowed before tuple struct bodies

View file

@ -1,10 +1,11 @@
// Regression test for issues #100790 and #106439.
//@ run-rustfix
#[allow(dead_code)]
pub struct Example
where
(): Sized,
(#[allow(dead_code)] usize);
(usize);
//~^^^ ERROR where clauses are not allowed before tuple struct bodies
struct _Demo

View file

@ -1,23 +1,23 @@
error: where clauses are not allowed before tuple struct bodies
--> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:5:1
--> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:6:1
|
LL | pub struct Example
| ------- while parsing this tuple struct
LL | / where
LL | | (): Sized,
| |______________^ unexpected where clause
LL | (#[allow(dead_code)] usize);
| --------------------------- the struct body
LL | (usize);
| ------- the struct body
|
help: move the body before the where clause
|
LL ~ pub struct Example(#[allow(dead_code)] usize)
LL ~ pub struct Example(usize)
LL | where
LL ~ (): Sized;
|
error: where clauses are not allowed before tuple struct bodies
--> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:11:1
--> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:12:1
|
LL | struct _Demo
| ----- while parsing this tuple struct

View file

@ -1,6 +1,7 @@
//@ run-rustfix
pub struct T(#[allow(dead_code)] String);
#[allow(dead_code)]
pub struct T(String);
//~^ ERROR missing `struct` for struct definition
fn main() {}

View file

@ -1,6 +1,7 @@
//@ run-rustfix
pub T(#[allow(dead_code)] String);
#[allow(dead_code)]
pub T(String);
//~^ ERROR missing `struct` for struct definition
fn main() {}

View file

@ -1,12 +1,12 @@
error: missing `struct` for struct definition
--> $DIR/pub-ident-struct-4.rs:3:4
--> $DIR/pub-ident-struct-4.rs:4:4
|
LL | pub T(#[allow(dead_code)] String);
LL | pub T(String);
| ^
|
help: add `struct` here to parse `T` as a public struct
|
LL | pub struct T(#[allow(dead_code)] String);
LL | pub struct T(String);
| ++++++
error: aborting due to 1 previous error

View file

@ -5,6 +5,7 @@
//@ pretty-expanded FIXME #23616
#[allow(dead_code)]
pub struct P<'a> {
_ptr: *const &'a u8,
}

View file

@ -3,8 +3,10 @@
#![allow(unused_variables)]
//@ pretty-expanded FIXME #23616
#[allow(dead_code)]
pub struct Fd(u32);
#[allow(dead_code)]
fn foo(a: u32) {}
impl Drop for Fd {

View file

@ -1,4 +1,5 @@
//@ run-pass
pub struct Z(#[allow(dead_code)] &'static Z);
#[allow(dead_code)]
pub struct Z(&'static Z);
pub fn main() {}

View file

@ -1,6 +1,7 @@
//@ run-rustfix
// https://github.com/rust-lang/rust/issues/79076
#[allow(dead_code)]
#[derive(Clone, Eq)] //~ ERROR [E0277]
pub struct Struct<T: std::clone::Clone>(T);

View file

@ -1,6 +1,7 @@
//@ run-rustfix
// https://github.com/rust-lang/rust/issues/79076
#[allow(dead_code)]
#[derive(Clone, Eq)] //~ ERROR [E0277]
pub struct Struct<T>(T);

View file

@ -1,11 +1,11 @@
error[E0277]: the trait bound `T: Clone` is not satisfied
--> $DIR/derive-clone-for-eq.rs:4:17
--> $DIR/derive-clone-for-eq.rs:5:17
|
LL | #[derive(Clone, Eq)]
| ^^ the trait `Clone` is not implemented for `T`, which is required by `Struct<T>: PartialEq`
|
note: required for `Struct<T>` to implement `PartialEq`
--> $DIR/derive-clone-for-eq.rs:7:19
--> $DIR/derive-clone-for-eq.rs:8:19
|
LL | impl<T: Clone, U> PartialEq<U> for Struct<T>
| ----- ^^^^^^^^^^^^ ^^^^^^^^^

View file

@ -1,4 +1,5 @@
//@ run-rustfix
#[allow(dead_code)]
pub struct LipogramCorpora {
selections: Vec<(char, Option<String>)>,
}
@ -17,6 +18,7 @@ impl LipogramCorpora {
}
}
#[allow(dead_code)]
pub struct LipogramCorpora2 {
selections: Vec<(char, Result<String, String>)>,
}

View file

@ -1,4 +1,5 @@
//@ run-rustfix
#[allow(dead_code)]
pub struct LipogramCorpora {
selections: Vec<(char, Option<String>)>,
}
@ -17,6 +18,7 @@ pub fn validate_all(&mut self) -> Result<(), char> {
}
}
#[allow(dead_code)]
pub struct LipogramCorpora2 {
selections: Vec<(char, Result<String, String>)>,
}

View file

@ -1,5 +1,5 @@
error[E0507]: cannot move out of `selection.1` which is behind a shared reference
--> $DIR/option-content-move.rs:10:20
--> $DIR/option-content-move.rs:11:20
|
LL | if selection.1.unwrap().contains(selection.0) {
| ^^^^^^^^^^^ -------- `selection.1` moved due to this method call
@ -19,7 +19,7 @@ LL | if selection.1.clone().unwrap().contains(selection.0) {
| ++++++++
error[E0507]: cannot move out of `selection.1` which is behind a shared reference
--> $DIR/option-content-move.rs:28:20
--> $DIR/option-content-move.rs:30:20
|
LL | if selection.1.unwrap().contains(selection.0) {
| ^^^^^^^^^^^ -------- `selection.1` moved due to this method call

View file

@ -7,6 +7,7 @@ pub trait Trait2<A> {
fn doit(&self) -> A;
}
#[allow(dead_code)]
pub struct Impl<A1, A2, A3> {
m1: marker::PhantomData<(A1,A2,A3)>,
/*