]> git.proxmox.com Git - rustc.git/blob - src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs
New upstream version 1.60.0+dfsg1
[rustc.git] / src / tools / clippy / clippy_lints / src / transmute / transmute_undefined_repr.rs
1 use super::TRANSMUTE_UNDEFINED_REPR;
2 use clippy_utils::diagnostics::span_lint_and_then;
3 use rustc_hir::Expr;
4 use rustc_lint::LateContext;
5 use rustc_middle::ty::subst::{GenericArg, Subst};
6 use rustc_middle::ty::{self, Ty, TypeAndMut};
7 use rustc_span::Span;
8
9 #[allow(clippy::too_many_lines)]
10 pub(super) fn check<'tcx>(
11 cx: &LateContext<'tcx>,
12 e: &'tcx Expr<'_>,
13 from_ty_orig: Ty<'tcx>,
14 to_ty_orig: Ty<'tcx>,
15 ) -> bool {
16 let mut from_ty = cx.tcx.erase_regions(from_ty_orig);
17 let mut to_ty = cx.tcx.erase_regions(to_ty_orig);
18
19 while from_ty != to_ty {
20 match reduce_refs(cx, e.span, from_ty, to_ty) {
21 ReducedTys::FromFatPtr { unsized_ty, .. } => {
22 span_lint_and_then(
23 cx,
24 TRANSMUTE_UNDEFINED_REPR,
25 e.span,
26 &format!("transmute from `{}` which has an undefined layout", from_ty_orig),
27 |diag| {
28 if from_ty_orig.peel_refs() != unsized_ty {
29 diag.note(&format!("the contained type `&{}` has an undefined layout", unsized_ty));
30 }
31 },
32 );
33 return true;
34 },
35 ReducedTys::ToFatPtr { unsized_ty, .. } => {
36 span_lint_and_then(
37 cx,
38 TRANSMUTE_UNDEFINED_REPR,
39 e.span,
40 &format!("transmute to `{}` which has an undefined layout", to_ty_orig),
41 |diag| {
42 if to_ty_orig.peel_refs() != unsized_ty {
43 diag.note(&format!("the contained type `&{}` has an undefined layout", unsized_ty));
44 }
45 },
46 );
47 return true;
48 },
49 ReducedTys::ToPtr {
50 from_ty: from_sub_ty,
51 to_ty: to_sub_ty,
52 } => match reduce_ty(cx, from_sub_ty) {
53 ReducedTy::UnorderedFields(from_ty) => {
54 span_lint_and_then(
55 cx,
56 TRANSMUTE_UNDEFINED_REPR,
57 e.span,
58 &format!("transmute from `{}` which has an undefined layout", from_ty_orig),
59 |diag| {
60 if from_ty_orig.peel_refs() != from_ty {
61 diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
62 }
63 },
64 );
65 return true;
66 },
67 ReducedTy::Ref(from_sub_ty) => {
68 from_ty = from_sub_ty;
69 to_ty = to_sub_ty;
70 continue;
71 },
72 _ => break,
73 },
74 ReducedTys::FromPtr {
75 from_ty: from_sub_ty,
76 to_ty: to_sub_ty,
77 } => match reduce_ty(cx, to_sub_ty) {
78 ReducedTy::UnorderedFields(to_ty) => {
79 span_lint_and_then(
80 cx,
81 TRANSMUTE_UNDEFINED_REPR,
82 e.span,
83 &format!("transmute to `{}` which has an undefined layout", to_ty_orig),
84 |diag| {
85 if to_ty_orig.peel_refs() != to_ty {
86 diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
87 }
88 },
89 );
90 return true;
91 },
92 ReducedTy::Ref(to_sub_ty) => {
93 from_ty = from_sub_ty;
94 to_ty = to_sub_ty;
95 continue;
96 },
97 _ => break,
98 },
99 ReducedTys::Other {
100 from_ty: from_sub_ty,
101 to_ty: to_sub_ty,
102 } => match (reduce_ty(cx, from_sub_ty), reduce_ty(cx, to_sub_ty)) {
103 (ReducedTy::IntArray, _) | (_, ReducedTy::IntArray) => return false,
104 (ReducedTy::UnorderedFields(from_ty), ReducedTy::UnorderedFields(to_ty)) if from_ty != to_ty => {
105 span_lint_and_then(
106 cx,
107 TRANSMUTE_UNDEFINED_REPR,
108 e.span,
109 &format!(
110 "transmute from `{}` to `{}`, both of which have an undefined layout",
111 from_ty_orig, to_ty_orig
112 ),
113 |diag| {
114 if_chain! {
115 if let (Some(from_def), Some(to_def)) = (from_ty.ty_adt_def(), to_ty.ty_adt_def());
116 if from_def == to_def;
117 then {
118 diag.note(&format!(
119 "two instances of the same generic type (`{}`) may have different layouts",
120 cx.tcx.item_name(from_def.did)
121 ));
122 } else {
123 if from_ty_orig.peel_refs() != from_ty {
124 diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
125 }
126 if to_ty_orig.peel_refs() != to_ty {
127 diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
128 }
129 }
130 }
131 },
132 );
133 return true;
134 },
135 (
136 ReducedTy::UnorderedFields(from_ty),
137 ReducedTy::Other(_) | ReducedTy::OrderedFields(_) | ReducedTy::Ref(_),
138 ) => {
139 span_lint_and_then(
140 cx,
141 TRANSMUTE_UNDEFINED_REPR,
142 e.span,
143 &format!("transmute from `{}` which has an undefined layout", from_ty_orig),
144 |diag| {
145 if from_ty_orig.peel_refs() != from_ty {
146 diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
147 }
148 },
149 );
150 return true;
151 },
152 (
153 ReducedTy::Other(_) | ReducedTy::OrderedFields(_) | ReducedTy::Ref(_),
154 ReducedTy::UnorderedFields(to_ty),
155 ) => {
156 span_lint_and_then(
157 cx,
158 TRANSMUTE_UNDEFINED_REPR,
159 e.span,
160 &format!("transmute into `{}` which has an undefined layout", to_ty_orig),
161 |diag| {
162 if to_ty_orig.peel_refs() != to_ty {
163 diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
164 }
165 },
166 );
167 return true;
168 },
169 (ReducedTy::Ref(from_sub_ty), ReducedTy::Ref(to_sub_ty)) => {
170 from_ty = from_sub_ty;
171 to_ty = to_sub_ty;
172 continue;
173 },
174 (
175 ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_),
176 ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_),
177 )
178 | (ReducedTy::UnorderedFields(_), ReducedTy::UnorderedFields(_)) => break,
179 },
180 }
181 }
182
183 false
184 }
185
186 enum ReducedTys<'tcx> {
187 FromFatPtr { unsized_ty: Ty<'tcx> },
188 ToFatPtr { unsized_ty: Ty<'tcx> },
189 ToPtr { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
190 FromPtr { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
191 Other { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
192 }
193
194 fn reduce_refs<'tcx>(
195 cx: &LateContext<'tcx>,
196 span: Span,
197 mut from_ty: Ty<'tcx>,
198 mut to_ty: Ty<'tcx>,
199 ) -> ReducedTys<'tcx> {
200 loop {
201 return match (from_ty.kind(), to_ty.kind()) {
202 (
203 &ty::Ref(_, from_sub_ty, _) | &ty::RawPtr(TypeAndMut { ty: from_sub_ty, .. }),
204 &ty::Ref(_, to_sub_ty, _) | &ty::RawPtr(TypeAndMut { ty: to_sub_ty, .. }),
205 ) => {
206 from_ty = from_sub_ty;
207 to_ty = to_sub_ty;
208 continue;
209 },
210 (&ty::Ref(_, unsized_ty, _) | &ty::RawPtr(TypeAndMut { ty: unsized_ty, .. }), _)
211 if !unsized_ty.is_sized(cx.tcx.at(span), cx.param_env) =>
212 {
213 ReducedTys::FromFatPtr { unsized_ty }
214 },
215 (_, &ty::Ref(_, unsized_ty, _) | &ty::RawPtr(TypeAndMut { ty: unsized_ty, .. }))
216 if !unsized_ty.is_sized(cx.tcx.at(span), cx.param_env) =>
217 {
218 ReducedTys::ToFatPtr { unsized_ty }
219 },
220 (&ty::Ref(_, from_ty, _) | &ty::RawPtr(TypeAndMut { ty: from_ty, .. }), _) => {
221 ReducedTys::FromPtr { from_ty, to_ty }
222 },
223 (_, &ty::Ref(_, to_ty, _) | &ty::RawPtr(TypeAndMut { ty: to_ty, .. })) => {
224 ReducedTys::ToPtr { from_ty, to_ty }
225 },
226 _ => ReducedTys::Other { from_ty, to_ty },
227 };
228 }
229 }
230
231 enum ReducedTy<'tcx> {
232 OrderedFields(Ty<'tcx>),
233 UnorderedFields(Ty<'tcx>),
234 Ref(Ty<'tcx>),
235 Other(Ty<'tcx>),
236 IntArray,
237 }
238
239 fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> {
240 loop {
241 ty = cx.tcx.try_normalize_erasing_regions(cx.param_env, ty).unwrap_or(ty);
242 return match *ty.kind() {
243 ty::Array(sub_ty, _) if matches!(sub_ty.kind(), ty::Int(_) | ty::Uint(_)) => ReducedTy::IntArray,
244 ty::Array(sub_ty, _) | ty::Slice(sub_ty) => {
245 ty = sub_ty;
246 continue;
247 },
248 ty::Tuple(args) => {
249 let mut iter = args.iter().map(GenericArg::expect_ty);
250 let Some(sized_ty) = iter.find(|ty| !is_zero_sized_ty(cx, *ty)) else {
251 return ReducedTy::OrderedFields(ty);
252 };
253 if iter.all(|ty| is_zero_sized_ty(cx, ty)) {
254 ty = sized_ty;
255 continue;
256 }
257 ReducedTy::UnorderedFields(ty)
258 },
259 ty::Adt(def, substs) if def.is_struct() => {
260 if def.repr.inhibit_struct_field_reordering_opt() {
261 return ReducedTy::OrderedFields(ty);
262 }
263 let mut iter = def
264 .non_enum_variant()
265 .fields
266 .iter()
267 .map(|f| cx.tcx.type_of(f.did).subst(cx.tcx, substs));
268 let Some(sized_ty) = iter.find(|ty| !is_zero_sized_ty(cx, *ty)) else {
269 return ReducedTy::OrderedFields(ty);
270 };
271 if iter.all(|ty| is_zero_sized_ty(cx, ty)) {
272 ty = sized_ty;
273 continue;
274 }
275 ReducedTy::UnorderedFields(ty)
276 },
277 ty::Ref(..) | ty::RawPtr(_) => ReducedTy::Ref(ty),
278 _ => ReducedTy::Other(ty),
279 };
280 }
281 }
282
283 fn is_zero_sized_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
284 if_chain! {
285 if let Ok(ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, ty);
286 if let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty));
287 then {
288 layout.layout.size.bytes() == 0
289 } else {
290 false
291 }
292 }
293 }