@@ -6,14 +6,17 @@ use std::ptr;
6
6
7
7
use rustc_hir:: def:: { DefKind , Res } ;
8
8
use rustc_hir:: { Expr , ExprKind , ImplItem , ImplItemKind , Item , ItemKind , Node , TraitItem , TraitItemKind , UnOp } ;
9
+ use rustc_infer:: traits:: specialization_graph;
9
10
use rustc_lint:: { LateContext , LateLintPass , Lint } ;
10
11
use rustc_middle:: ty:: adjustment:: Adjust ;
11
- use rustc_middle:: ty:: { Ty , TypeFlags } ;
12
+ use rustc_middle:: ty:: fold:: TypeFoldable as _;
13
+ use rustc_middle:: ty:: { AssocKind , Ty , TypeFlags } ;
12
14
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
13
15
use rustc_span:: { InnerSpan , Span , DUMMY_SP } ;
14
16
use rustc_typeck:: hir_ty_to_ty;
15
17
16
- use crate :: utils:: { in_constant, is_copy, qpath_res, span_lint_and_then} ;
18
+ use crate :: utils:: { in_constant, qpath_res, span_lint_and_then} ;
19
+ use if_chain:: if_chain;
17
20
18
21
declare_clippy_lint ! {
19
22
/// **What it does:** Checks for declaration of `const` items which is interior
@@ -83,11 +86,10 @@ declare_clippy_lint! {
83
86
"referencing `const` with interior mutability"
84
87
}
85
88
86
- #[ allow( dead_code) ]
87
89
#[ derive( Copy , Clone ) ]
88
90
enum Source {
89
91
Item { item : Span } ,
90
- Assoc { item : Span , ty : Span } ,
92
+ Assoc { item : Span } ,
91
93
Expr { expr : Span } ,
92
94
}
93
95
@@ -110,10 +112,15 @@ impl Source {
110
112
}
111
113
112
114
fn verify_ty_bound < ' tcx > ( cx : & LateContext < ' tcx > , ty : Ty < ' tcx > , source : Source ) {
113
- if ty. is_freeze ( cx. tcx . at ( DUMMY_SP ) , cx. param_env ) || is_copy ( cx, ty) {
114
- // An `UnsafeCell` is `!Copy`, and an `UnsafeCell` is also the only type which
115
- // is `!Freeze`, thus if our type is `Copy` we can be sure it must be `Freeze`
116
- // as well.
115
+ // Ignore types whose layout is unknown since `is_freeze` reports every generic types as `!Freeze`,
116
+ // making it indistinguishable from `UnsafeCell`. i.e. it isn't a tool to prove a type is
117
+ // 'unfrozen'. However, this code causes a false negative in which
118
+ // a type contains a layout-unknown type, but also a unsafe cell like `const CELL: Cell<T>`.
119
+ // Yet, it's better than `ty.has_type_flags(TypeFlags::HAS_TY_PARAM | TypeFlags::HAS_PROJECTION)`
120
+ // since it works when a pointer indirection involves (`Cell<*const T>`).
121
+ // Making up a `ParamEnv` where every generic params and assoc types are `Freeze`is another option;
122
+ // but I'm not sure whether it's a decent way, if possible.
123
+ if cx. tcx . layout_of ( cx. param_env . and ( ty) ) . is_err ( ) || ty. is_freeze ( cx. tcx . at ( DUMMY_SP ) , cx. param_env ) {
117
124
return ;
118
125
}
119
126
@@ -127,11 +134,7 @@ fn verify_ty_bound<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, source: Source) {
127
134
let const_kw_span = span. from_inner ( InnerSpan :: new ( 0 , 5 ) ) ;
128
135
diag. span_label ( const_kw_span, "make this a static item (maybe with lazy_static)" ) ;
129
136
} ,
130
- Source :: Assoc { ty : ty_span, .. } => {
131
- if ty. flags ( ) . intersects ( TypeFlags :: HAS_FREE_LOCAL_NAMES ) {
132
- diag. span_label ( ty_span, & format ! ( "consider requiring `{}` to be `Copy`" , ty) ) ;
133
- }
134
- } ,
137
+ Source :: Assoc { .. } => ( ) ,
135
138
Source :: Expr { .. } => {
136
139
diag. help ( "assign this const to a local or static variable, and use the variable here" ) ;
137
140
} ,
@@ -152,32 +155,58 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
152
155
fn check_trait_item ( & mut self , cx : & LateContext < ' tcx > , trait_item : & ' tcx TraitItem < ' _ > ) {
153
156
if let TraitItemKind :: Const ( hir_ty, ..) = & trait_item. kind {
154
157
let ty = hir_ty_to_ty ( cx. tcx , hir_ty) ;
155
- verify_ty_bound (
156
- cx,
157
- ty,
158
- Source :: Assoc {
159
- ty : hir_ty. span ,
160
- item : trait_item. span ,
161
- } ,
162
- ) ;
158
+ // Normalize assoc types because ones originated from generic params
159
+ // bounded other traits could have their bound.
160
+ let normalized = cx. tcx . normalize_erasing_regions ( cx. param_env , ty) ;
161
+ verify_ty_bound ( cx, normalized, Source :: Assoc { item : trait_item. span } ) ;
163
162
}
164
163
}
165
164
166
165
fn check_impl_item ( & mut self , cx : & LateContext < ' tcx > , impl_item : & ' tcx ImplItem < ' _ > ) {
167
166
if let ImplItemKind :: Const ( hir_ty, ..) = & impl_item. kind {
168
167
let item_hir_id = cx. tcx . hir ( ) . get_parent_node ( impl_item. hir_id ) ;
169
168
let item = cx. tcx . hir ( ) . expect_item ( item_hir_id) ;
170
- // Ensure the impl is an inherent impl.
171
- if let ItemKind :: Impl { of_trait : None , .. } = item. kind {
172
- let ty = hir_ty_to_ty ( cx. tcx , hir_ty) ;
173
- verify_ty_bound (
174
- cx,
175
- ty,
176
- Source :: Assoc {
177
- ty : hir_ty. span ,
178
- item : impl_item. span ,
179
- } ,
180
- ) ;
169
+
170
+ match & item. kind {
171
+ ItemKind :: Impl {
172
+ of_trait : Some ( of_trait_ref) ,
173
+ ..
174
+ } => {
175
+ if_chain ! {
176
+ // Lint a trait impl item only when the definition is a generic type,
177
+ // assuming a assoc const is not meant to be a interior mutable type.
178
+ if let Some ( of_trait_def_id) = of_trait_ref. trait_def_id( ) ;
179
+ if let Some ( of_assoc_item) = specialization_graph:: Node :: Trait ( of_trait_def_id)
180
+ . item( cx. tcx, impl_item. ident, AssocKind :: Const , of_trait_def_id) ;
181
+ if cx. tcx
182
+ // Normalize assoc types because ones originated from generic params
183
+ // bounded other traits could have their bound at the trait defs;
184
+ // and, in that case, the definition is *not* generic.
185
+ . normalize_erasing_regions(
186
+ cx. tcx. param_env( of_trait_def_id) ,
187
+ cx. tcx. type_of( of_assoc_item. def_id) ,
188
+ )
189
+ . has_type_flags( TypeFlags :: HAS_PROJECTION | TypeFlags :: HAS_TY_PARAM ) ;
190
+ then {
191
+ let ty = hir_ty_to_ty( cx. tcx, hir_ty) ;
192
+ let normalized = cx. tcx. normalize_erasing_regions( cx. param_env, ty) ;
193
+ verify_ty_bound(
194
+ cx,
195
+ normalized,
196
+ Source :: Assoc {
197
+ item: impl_item. span,
198
+ } ,
199
+ ) ;
200
+ }
201
+ }
202
+ } ,
203
+ ItemKind :: Impl { of_trait : None , .. } => {
204
+ let ty = hir_ty_to_ty ( cx. tcx , hir_ty) ;
205
+ // Normalize assoc types originated from generic params.
206
+ let normalized = cx. tcx . normalize_erasing_regions ( cx. param_env , ty) ;
207
+ verify_ty_bound ( cx, normalized, Source :: Assoc { item : impl_item. span } ) ;
208
+ } ,
209
+ _ => ( ) ,
181
210
}
182
211
}
183
212
}
0 commit comments