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