]>
Commit | Line | Data |
---|---|---|
b039eaaf SL |
1 | // Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
9cc50fc6 SL |
11 | #![allow(non_snake_case)] |
12 | ||
54a0048b | 13 | use rustc::hir::def_id::DefId; |
54a0048b | 14 | use rustc::ty::subst::Substs; |
9e0c209e | 15 | use rustc::ty::{self, AdtKind, Ty, TyCtxt}; |
5bcae85e | 16 | use rustc::ty::layout::{Layout, Primitive}; |
54a0048b | 17 | use middle::const_val::ConstVal; |
32a655c1 | 18 | use rustc_const_eval::ConstContext; |
476ff2be | 19 | use util::nodemap::FxHashSet; |
b039eaaf SL |
20 | use lint::{LateContext, LintContext, LintArray}; |
21 | use lint::{LintPass, LateLintPass}; | |
22 | ||
23 | use std::cmp; | |
24 | use std::{i8, i16, i32, i64, u8, u16, u32, u64, f32, f64}; | |
25 | ||
7453a54e SL |
26 | use syntax::ast; |
27 | use syntax::abi::Abi; | |
a7813a04 | 28 | use syntax::attr; |
3157f602 XL |
29 | use syntax_pos::Span; |
30 | use syntax::codemap; | |
b039eaaf | 31 | |
54a0048b | 32 | use rustc::hir; |
b039eaaf SL |
33 | |
34 | declare_lint! { | |
35 | UNUSED_COMPARISONS, | |
36 | Warn, | |
37 | "comparisons made useless by limits of the types involved" | |
38 | } | |
39 | ||
40 | declare_lint! { | |
41 | OVERFLOWING_LITERALS, | |
42 | Warn, | |
43 | "literal out of range for its type" | |
44 | } | |
45 | ||
46 | declare_lint! { | |
47 | EXCEEDING_BITSHIFTS, | |
48 | Deny, | |
49 | "shift exceeds the type's number of bits" | |
50 | } | |
51 | ||
5bcae85e SL |
52 | declare_lint! { |
53 | VARIANT_SIZE_DIFFERENCES, | |
54 | Allow, | |
55 | "detects enums with widely varying variant sizes" | |
56 | } | |
57 | ||
b039eaaf SL |
58 | #[derive(Copy, Clone)] |
59 | pub struct TypeLimits { | |
60 | /// Id of the last visited negated expression | |
61 | negated_expr_id: ast::NodeId, | |
62 | } | |
63 | ||
64 | impl TypeLimits { | |
65 | pub fn new() -> TypeLimits { | |
c30ab7b3 | 66 | TypeLimits { negated_expr_id: ast::DUMMY_NODE_ID } |
b039eaaf SL |
67 | } |
68 | } | |
69 | ||
70 | impl LintPass for TypeLimits { | |
71 | fn get_lints(&self) -> LintArray { | |
c30ab7b3 SL |
72 | lint_array!(UNUSED_COMPARISONS, |
73 | OVERFLOWING_LITERALS, | |
74 | EXCEEDING_BITSHIFTS) | |
b039eaaf SL |
75 | } |
76 | } | |
77 | ||
476ff2be | 78 | impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { |
ea8adc8c | 79 | fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx hir::Expr) { |
b039eaaf SL |
80 | match e.node { |
81 | hir::ExprUnary(hir::UnNeg, ref expr) => { | |
b039eaaf SL |
82 | // propagate negation, if the negation itself isn't negated |
83 | if self.negated_expr_id != e.id { | |
84 | self.negated_expr_id = expr.id; | |
85 | } | |
c30ab7b3 | 86 | } |
b039eaaf | 87 | hir::ExprBinary(binop, ref l, ref r) => { |
32a655c1 | 88 | if is_comparison(binop) && !check_limits(cx, binop, &l, &r) { |
c30ab7b3 SL |
89 | cx.span_lint(UNUSED_COMPARISONS, |
90 | e.span, | |
b039eaaf SL |
91 | "comparison is useless due to type limits"); |
92 | } | |
93 | ||
54a0048b | 94 | if binop.node.is_shift() { |
3b2f2976 | 95 | let opt_ty_bits = match cx.tables.node_id_to_type(l.hir_id).sty { |
ea8adc8c XL |
96 | ty::TyInt(t) => Some(int_ty_bits(t, cx.sess().target.isize_ty)), |
97 | ty::TyUint(t) => Some(uint_ty_bits(t, cx.sess().target.usize_ty)), | |
c30ab7b3 | 98 | _ => None, |
b039eaaf SL |
99 | }; |
100 | ||
101 | if let Some(bits) = opt_ty_bits { | |
102 | let exceeding = if let hir::ExprLit(ref lit) = r.node { | |
c30ab7b3 | 103 | if let ast::LitKind::Int(shift, _) = lit.node { |
32a655c1 | 104 | shift as u64 >= bits |
c30ab7b3 SL |
105 | } else { |
106 | false | |
107 | } | |
b039eaaf | 108 | } else { |
3b2f2976 XL |
109 | // HACK(eddyb) This might be quite inefficient. |
110 | // This would be better left to MIR constant propagation, | |
111 | // perhaps even at trans time (like is the case already | |
112 | // when the value being shifted is *also* constant). | |
113 | let parent_item = cx.tcx.hir.get_parent(e.id); | |
114 | let parent_def_id = cx.tcx.hir.local_def_id(parent_item); | |
115 | let substs = Substs::identity_for_item(cx.tcx, parent_def_id); | |
116 | let const_cx = ConstContext::new(cx.tcx, | |
117 | cx.param_env.and(substs), | |
118 | cx.tables); | |
8bb4bdeb | 119 | match const_cx.eval(&r) { |
ea8adc8c | 120 | Ok(&ty::Const { val: ConstVal::Integral(i), .. }) => { |
c30ab7b3 SL |
121 | i.is_negative() || |
122 | i.to_u64() | |
123 | .map(|i| i >= bits) | |
124 | .unwrap_or(true) | |
125 | } | |
126 | _ => false, | |
b039eaaf SL |
127 | } |
128 | }; | |
129 | if exceeding { | |
c30ab7b3 SL |
130 | cx.span_lint(EXCEEDING_BITSHIFTS, |
131 | e.span, | |
b039eaaf SL |
132 | "bitshift exceeds the type's number of bits"); |
133 | } | |
134 | }; | |
135 | } | |
c30ab7b3 | 136 | } |
b039eaaf | 137 | hir::ExprLit(ref lit) => { |
3b2f2976 | 138 | match cx.tables.node_id_to_type(e.hir_id).sty { |
b039eaaf SL |
139 | ty::TyInt(t) => { |
140 | match lit.node { | |
7453a54e SL |
141 | ast::LitKind::Int(v, ast::LitIntType::Signed(_)) | |
142 | ast::LitKind::Int(v, ast::LitIntType::Unsuffixed) => { | |
143 | let int_type = if let ast::IntTy::Is = t { | |
ea8adc8c | 144 | cx.sess().target.isize_ty |
b039eaaf SL |
145 | } else { |
146 | t | |
147 | }; | |
148 | let (_, max) = int_ty_range(int_type); | |
32a655c1 | 149 | let max = max as u128; |
b039eaaf SL |
150 | let negative = self.negated_expr_id == e.id; |
151 | ||
152 | // Detect literal value out of range [min, max] inclusive | |
153 | // avoiding use of -min to prevent overflow/panic | |
32a655c1 SL |
154 | if (negative && v > max + 1) || |
155 | (!negative && v > max) { | |
c30ab7b3 SL |
156 | cx.span_lint(OVERFLOWING_LITERALS, |
157 | e.span, | |
7453a54e | 158 | &format!("literal out of range for {:?}", t)); |
b039eaaf SL |
159 | return; |
160 | } | |
161 | } | |
c30ab7b3 | 162 | _ => bug!(), |
b039eaaf | 163 | }; |
c30ab7b3 | 164 | } |
b039eaaf | 165 | ty::TyUint(t) => { |
7453a54e | 166 | let uint_type = if let ast::UintTy::Us = t { |
ea8adc8c | 167 | cx.sess().target.usize_ty |
b039eaaf SL |
168 | } else { |
169 | t | |
170 | }; | |
171 | let (min, max) = uint_ty_range(uint_type); | |
32a655c1 | 172 | let lit_val: u128 = match lit.node { |
7453a54e SL |
173 | // _v is u8, within range by definition |
174 | ast::LitKind::Byte(_v) => return, | |
175 | ast::LitKind::Int(v, _) => v, | |
c30ab7b3 | 176 | _ => bug!(), |
b039eaaf SL |
177 | }; |
178 | if lit_val < min || lit_val > max { | |
c30ab7b3 SL |
179 | cx.span_lint(OVERFLOWING_LITERALS, |
180 | e.span, | |
7453a54e | 181 | &format!("literal out of range for {:?}", t)); |
b039eaaf | 182 | } |
c30ab7b3 | 183 | } |
b039eaaf | 184 | ty::TyFloat(t) => { |
3b2f2976 | 185 | let is_infinite = match lit.node { |
476ff2be SL |
186 | ast::LitKind::Float(v, _) | |
187 | ast::LitKind::FloatUnsuffixed(v) => { | |
3b2f2976 XL |
188 | match t { |
189 | ast::FloatTy::F32 => v.as_str().parse().map(f32::is_infinite), | |
190 | ast::FloatTy::F64 => v.as_str().parse().map(f64::is_infinite), | |
b039eaaf SL |
191 | } |
192 | } | |
c30ab7b3 | 193 | _ => bug!(), |
b039eaaf | 194 | }; |
3b2f2976 | 195 | if is_infinite == Ok(true) { |
c30ab7b3 SL |
196 | cx.span_lint(OVERFLOWING_LITERALS, |
197 | e.span, | |
7453a54e | 198 | &format!("literal out of range for {:?}", t)); |
b039eaaf | 199 | } |
c30ab7b3 SL |
200 | } |
201 | _ => (), | |
b039eaaf | 202 | }; |
c30ab7b3 SL |
203 | } |
204 | _ => (), | |
b039eaaf SL |
205 | }; |
206 | ||
c30ab7b3 | 207 | fn is_valid<T: cmp::PartialOrd>(binop: hir::BinOp, v: T, min: T, max: T) -> bool { |
b039eaaf | 208 | match binop.node { |
c30ab7b3 SL |
209 | hir::BiLt => v > min && v <= max, |
210 | hir::BiLe => v >= min && v < max, | |
211 | hir::BiGt => v >= min && v < max, | |
212 | hir::BiGe => v > min && v <= max, | |
b039eaaf | 213 | hir::BiEq | hir::BiNe => v >= min && v <= max, |
c30ab7b3 | 214 | _ => bug!(), |
b039eaaf SL |
215 | } |
216 | } | |
217 | ||
218 | fn rev_binop(binop: hir::BinOp) -> hir::BinOp { | |
c30ab7b3 SL |
219 | codemap::respan(binop.span, |
220 | match binop.node { | |
221 | hir::BiLt => hir::BiGt, | |
222 | hir::BiLe => hir::BiGe, | |
223 | hir::BiGt => hir::BiLt, | |
224 | hir::BiGe => hir::BiLe, | |
225 | _ => return binop, | |
226 | }) | |
b039eaaf SL |
227 | } |
228 | ||
229 | // for isize & usize, be conservative with the warnings, so that the | |
230 | // warnings are consistent between 32- and 64-bit platforms | |
32a655c1 | 231 | fn int_ty_range(int_ty: ast::IntTy) -> (i128, i128) { |
b039eaaf | 232 | match int_ty { |
32a655c1 SL |
233 | ast::IntTy::Is => (i64::min_value() as i128, i64::max_value() as i128), |
234 | ast::IntTy::I8 => (i8::min_value() as i64 as i128, i8::max_value() as i128), | |
235 | ast::IntTy::I16 => (i16::min_value() as i64 as i128, i16::max_value() as i128), | |
236 | ast::IntTy::I32 => (i32::min_value() as i64 as i128, i32::max_value() as i128), | |
237 | ast::IntTy::I64 => (i64::min_value() as i128, i64::max_value() as i128), | |
238 | ast::IntTy::I128 =>(i128::min_value() as i128, i128::max_value()), | |
b039eaaf SL |
239 | } |
240 | } | |
241 | ||
32a655c1 | 242 | fn uint_ty_range(uint_ty: ast::UintTy) -> (u128, u128) { |
b039eaaf | 243 | match uint_ty { |
32a655c1 SL |
244 | ast::UintTy::Us => (u64::min_value() as u128, u64::max_value() as u128), |
245 | ast::UintTy::U8 => (u8::min_value() as u128, u8::max_value() as u128), | |
246 | ast::UintTy::U16 => (u16::min_value() as u128, u16::max_value() as u128), | |
247 | ast::UintTy::U32 => (u32::min_value() as u128, u32::max_value() as u128), | |
248 | ast::UintTy::U64 => (u64::min_value() as u128, u64::max_value() as u128), | |
249 | ast::UintTy::U128 => (u128::min_value(), u128::max_value()), | |
b039eaaf SL |
250 | } |
251 | } | |
252 | ||
ea8adc8c | 253 | fn int_ty_bits(int_ty: ast::IntTy, isize_ty: ast::IntTy) -> u64 { |
b039eaaf | 254 | match int_ty { |
ea8adc8c | 255 | ast::IntTy::Is => int_ty_bits(isize_ty, isize_ty), |
7453a54e SL |
256 | ast::IntTy::I8 => 8, |
257 | ast::IntTy::I16 => 16 as u64, | |
258 | ast::IntTy::I32 => 32, | |
259 | ast::IntTy::I64 => 64, | |
32a655c1 | 260 | ast::IntTy::I128 => 128, |
b039eaaf SL |
261 | } |
262 | } | |
263 | ||
ea8adc8c | 264 | fn uint_ty_bits(uint_ty: ast::UintTy, usize_ty: ast::UintTy) -> u64 { |
b039eaaf | 265 | match uint_ty { |
ea8adc8c | 266 | ast::UintTy::Us => uint_ty_bits(usize_ty, usize_ty), |
7453a54e SL |
267 | ast::UintTy::U8 => 8, |
268 | ast::UintTy::U16 => 16, | |
269 | ast::UintTy::U32 => 32, | |
270 | ast::UintTy::U64 => 64, | |
32a655c1 | 271 | ast::UintTy::U128 => 128, |
b039eaaf SL |
272 | } |
273 | } | |
274 | ||
32a655c1 SL |
275 | fn check_limits(cx: &LateContext, |
276 | binop: hir::BinOp, | |
277 | l: &hir::Expr, | |
278 | r: &hir::Expr) | |
279 | -> bool { | |
b039eaaf SL |
280 | let (lit, expr, swap) = match (&l.node, &r.node) { |
281 | (&hir::ExprLit(_), _) => (l, r, true), | |
282 | (_, &hir::ExprLit(_)) => (r, l, false), | |
c30ab7b3 | 283 | _ => return true, |
b039eaaf SL |
284 | }; |
285 | // Normalize the binop so that the literal is always on the RHS in | |
286 | // the comparison | |
c30ab7b3 | 287 | let norm_binop = if swap { rev_binop(binop) } else { binop }; |
3b2f2976 | 288 | match cx.tables.node_id_to_type(expr.hir_id).sty { |
b039eaaf SL |
289 | ty::TyInt(int_ty) => { |
290 | let (min, max) = int_ty_range(int_ty); | |
32a655c1 | 291 | let lit_val: i128 = match lit.node { |
c30ab7b3 SL |
292 | hir::ExprLit(ref li) => { |
293 | match li.node { | |
294 | ast::LitKind::Int(v, ast::LitIntType::Signed(_)) | | |
32a655c1 SL |
295 | ast::LitKind::Int(v, ast::LitIntType::Unsuffixed) => v as i128, |
296 | _ => return true | |
c30ab7b3 | 297 | } |
32a655c1 SL |
298 | }, |
299 | _ => bug!() | |
b039eaaf SL |
300 | }; |
301 | is_valid(norm_binop, lit_val, min, max) | |
302 | } | |
303 | ty::TyUint(uint_ty) => { | |
32a655c1 SL |
304 | let (min, max) :(u128, u128) = uint_ty_range(uint_ty); |
305 | let lit_val: u128 = match lit.node { | |
c30ab7b3 SL |
306 | hir::ExprLit(ref li) => { |
307 | match li.node { | |
308 | ast::LitKind::Int(v, _) => v, | |
32a655c1 | 309 | _ => return true |
c30ab7b3 | 310 | } |
32a655c1 SL |
311 | }, |
312 | _ => bug!() | |
b039eaaf SL |
313 | }; |
314 | is_valid(norm_binop, lit_val, min, max) | |
315 | } | |
c30ab7b3 | 316 | _ => true, |
b039eaaf SL |
317 | } |
318 | } | |
319 | ||
320 | fn is_comparison(binop: hir::BinOp) -> bool { | |
321 | match binop.node { | |
c30ab7b3 SL |
322 | hir::BiEq | hir::BiLt | hir::BiLe | hir::BiNe | hir::BiGe | hir::BiGt => true, |
323 | _ => false, | |
b039eaaf SL |
324 | } |
325 | } | |
b039eaaf SL |
326 | } |
327 | } | |
328 | ||
329 | declare_lint! { | |
330 | IMPROPER_CTYPES, | |
331 | Warn, | |
332 | "proper use of libc types in foreign modules" | |
333 | } | |
334 | ||
335 | struct ImproperCTypesVisitor<'a, 'tcx: 'a> { | |
c30ab7b3 | 336 | cx: &'a LateContext<'a, 'tcx>, |
b039eaaf SL |
337 | } |
338 | ||
339 | enum FfiResult { | |
340 | FfiSafe, | |
8bb4bdeb | 341 | FfiPhantom, |
b039eaaf SL |
342 | FfiUnsafe(&'static str), |
343 | FfiBadStruct(DefId, &'static str), | |
9e0c209e SL |
344 | FfiBadUnion(DefId, &'static str), |
345 | FfiBadEnum(DefId, &'static str), | |
b039eaaf SL |
346 | } |
347 | ||
348 | /// Check if this enum can be safely exported based on the | |
349 | /// "nullable pointer optimization". Currently restricted | |
350 | /// to function pointers and references, but could be | |
351 | /// expanded to cover NonZero raw pointers and newtypes. | |
352 | /// FIXME: This duplicates code in trans. | |
a7813a04 | 353 | fn is_repr_nullable_ptr<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, |
476ff2be | 354 | def: &'tcx ty::AdtDef, |
a7813a04 XL |
355 | substs: &Substs<'tcx>) |
356 | -> bool { | |
b039eaaf SL |
357 | if def.variants.len() == 2 { |
358 | let data_idx; | |
359 | ||
360 | if def.variants[0].fields.is_empty() { | |
361 | data_idx = 1; | |
362 | } else if def.variants[1].fields.is_empty() { | |
363 | data_idx = 0; | |
364 | } else { | |
365 | return false; | |
366 | } | |
367 | ||
368 | if def.variants[data_idx].fields.len() == 1 { | |
369 | match def.variants[data_idx].fields[0].ty(tcx, substs).sty { | |
c30ab7b3 SL |
370 | ty::TyFnPtr(_) => { |
371 | return true; | |
372 | } | |
373 | ty::TyRef(..) => { | |
374 | return true; | |
375 | } | |
376 | _ => {} | |
b039eaaf SL |
377 | } |
378 | } | |
379 | } | |
380 | false | |
381 | } | |
382 | ||
8bb4bdeb XL |
383 | fn is_ffi_safe(ty: attr::IntType) -> bool { |
384 | match ty { | |
385 | attr::SignedInt(ast::IntTy::I8) | attr::UnsignedInt(ast::UintTy::U8) | | |
386 | attr::SignedInt(ast::IntTy::I16) | attr::UnsignedInt(ast::UintTy::U16) | | |
387 | attr::SignedInt(ast::IntTy::I32) | attr::UnsignedInt(ast::UintTy::U32) | | |
388 | attr::SignedInt(ast::IntTy::I64) | attr::UnsignedInt(ast::UintTy::U64) | | |
389 | attr::SignedInt(ast::IntTy::I128) | attr::UnsignedInt(ast::UintTy::U128) => true, | |
390 | attr::SignedInt(ast::IntTy::Is) | attr::UnsignedInt(ast::UintTy::Us) => false | |
391 | } | |
392 | } | |
393 | ||
b039eaaf SL |
394 | impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { |
395 | /// Check if the given type is "ffi-safe" (has a stable, well-defined | |
396 | /// representation which can be exported to C code). | |
8bb4bdeb XL |
397 | fn check_type_for_ffi(&self, |
398 | cache: &mut FxHashSet<Ty<'tcx>>, | |
399 | ty: Ty<'tcx>) -> FfiResult { | |
b039eaaf | 400 | use self::FfiResult::*; |
8bb4bdeb | 401 | |
a7813a04 | 402 | let cx = self.cx.tcx; |
b039eaaf SL |
403 | |
404 | // Protect against infinite recursion, for example | |
405 | // `struct S(*mut S);`. | |
406 | // FIXME: A recursion limit is necessary as well, for irregular | |
407 | // recusive types. | |
408 | if !cache.insert(ty) { | |
409 | return FfiSafe; | |
410 | } | |
411 | ||
412 | match ty.sty { | |
c30ab7b3 | 413 | ty::TyAdt(def, substs) => { |
8bb4bdeb XL |
414 | if def.is_phantom_data() { |
415 | return FfiPhantom; | |
416 | } | |
c30ab7b3 SL |
417 | match def.adt_kind() { |
418 | AdtKind::Struct => { | |
cc61c64b | 419 | if !def.repr.c() { |
c30ab7b3 SL |
420 | return FfiUnsafe("found struct without foreign-function-safe \ |
421 | representation annotation in foreign module, \ | |
422 | consider adding a #[repr(C)] attribute to the type"); | |
423 | } | |
b039eaaf | 424 | |
c30ab7b3 SL |
425 | if def.struct_variant().fields.is_empty() { |
426 | return FfiUnsafe("found zero-size struct in foreign module, consider \ | |
427 | adding a member to this struct"); | |
428 | } | |
b039eaaf | 429 | |
8bb4bdeb XL |
430 | // We can't completely trust repr(C) markings; make sure the |
431 | // fields are actually safe. | |
432 | let mut all_phantom = true; | |
c30ab7b3 SL |
433 | for field in &def.struct_variant().fields { |
434 | let field_ty = cx.normalize_associated_type(&field.ty(cx, substs)); | |
435 | let r = self.check_type_for_ffi(cache, field_ty); | |
436 | match r { | |
8bb4bdeb XL |
437 | FfiSafe => { |
438 | all_phantom = false; | |
439 | } | |
440 | FfiPhantom => {} | |
c30ab7b3 SL |
441 | FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => { |
442 | return r; | |
443 | } | |
444 | FfiUnsafe(s) => { | |
445 | return FfiBadStruct(def.did, s); | |
446 | } | |
447 | } | |
9e0c209e | 448 | } |
8bb4bdeb XL |
449 | |
450 | if all_phantom { FfiPhantom } else { FfiSafe } | |
b039eaaf | 451 | } |
c30ab7b3 | 452 | AdtKind::Union => { |
cc61c64b | 453 | if !def.repr.c() { |
c30ab7b3 SL |
454 | return FfiUnsafe("found union without foreign-function-safe \ |
455 | representation annotation in foreign module, \ | |
456 | consider adding a #[repr(C)] attribute to the type"); | |
457 | } | |
b039eaaf | 458 | |
8bb4bdeb XL |
459 | if def.struct_variant().fields.is_empty() { |
460 | return FfiUnsafe("found zero-size union in foreign module, consider \ | |
461 | adding a member to this union"); | |
462 | } | |
463 | ||
464 | let mut all_phantom = true; | |
c30ab7b3 SL |
465 | for field in &def.struct_variant().fields { |
466 | let field_ty = cx.normalize_associated_type(&field.ty(cx, substs)); | |
467 | let r = self.check_type_for_ffi(cache, field_ty); | |
468 | match r { | |
8bb4bdeb XL |
469 | FfiSafe => { |
470 | all_phantom = false; | |
471 | } | |
472 | FfiPhantom => {} | |
c30ab7b3 SL |
473 | FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => { |
474 | return r; | |
475 | } | |
476 | FfiUnsafe(s) => { | |
477 | return FfiBadUnion(def.did, s); | |
478 | } | |
479 | } | |
b039eaaf | 480 | } |
8bb4bdeb XL |
481 | |
482 | if all_phantom { FfiPhantom } else { FfiSafe } | |
b039eaaf | 483 | } |
c30ab7b3 SL |
484 | AdtKind::Enum => { |
485 | if def.variants.is_empty() { | |
486 | // Empty enums are okay... although sort of useless. | |
487 | return FfiSafe; | |
488 | } | |
9e0c209e | 489 | |
c30ab7b3 SL |
490 | // Check for a repr() attribute to specify the size of the |
491 | // discriminant. | |
cc61c64b | 492 | if !def.repr.c() && def.repr.int.is_none() { |
8bb4bdeb XL |
493 | // Special-case types like `Option<extern fn()>`. |
494 | if !is_repr_nullable_ptr(cx, def, substs) { | |
495 | return FfiUnsafe("found enum without foreign-function-safe \ | |
496 | representation annotation in foreign \ | |
497 | module, consider adding a #[repr(...)] \ | |
498 | attribute to the type"); | |
9e0c209e | 499 | } |
8bb4bdeb | 500 | } |
c30ab7b3 | 501 | |
8bb4bdeb XL |
502 | if let Some(int_ty) = def.repr.int { |
503 | if !is_ffi_safe(int_ty) { | |
9e0c209e SL |
504 | // FIXME: This shouldn't be reachable: we should check |
505 | // this earlier. | |
8bb4bdeb | 506 | return FfiUnsafe("enum has unexpected #[repr(...)] attribute"); |
9e0c209e | 507 | } |
8bb4bdeb XL |
508 | |
509 | // Enum with an explicitly sized discriminant; either | |
510 | // a C-style enum or a discriminated union. | |
511 | ||
512 | // The layout of enum variants is implicitly repr(C). | |
513 | // FIXME: Is that correct? | |
9e0c209e | 514 | } |
b039eaaf | 515 | |
c30ab7b3 SL |
516 | // Check the contained variants. |
517 | for variant in &def.variants { | |
518 | for field in &variant.fields { | |
519 | let arg = cx.normalize_associated_type(&field.ty(cx, substs)); | |
520 | let r = self.check_type_for_ffi(cache, arg); | |
521 | match r { | |
522 | FfiSafe => {} | |
523 | FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => { | |
524 | return r; | |
525 | } | |
8bb4bdeb XL |
526 | FfiPhantom => { |
527 | return FfiBadEnum(def.did, | |
528 | "Found phantom data in enum variant"); | |
529 | } | |
c30ab7b3 SL |
530 | FfiUnsafe(s) => { |
531 | return FfiBadEnum(def.did, s); | |
532 | } | |
533 | } | |
9e0c209e | 534 | } |
b039eaaf | 535 | } |
c30ab7b3 | 536 | FfiSafe |
b039eaaf SL |
537 | } |
538 | } | |
c30ab7b3 | 539 | } |
b039eaaf SL |
540 | |
541 | ty::TyChar => { | |
542 | FfiUnsafe("found Rust type `char` in foreign module, while \ | |
543 | `u32` or `libc::wchar_t` should be used") | |
544 | } | |
545 | ||
ea8adc8c XL |
546 | ty::TyInt(ast::IntTy::I128) => { |
547 | FfiUnsafe("found Rust type `i128` in foreign module, but \ | |
548 | 128-bit integers don't currently have a known \ | |
549 | stable ABI") | |
550 | } | |
551 | ||
552 | ty::TyUint(ast::UintTy::U128) => { | |
553 | FfiUnsafe("found Rust type `u128` in foreign module, but \ | |
554 | 128-bit integers don't currently have a known \ | |
555 | stable ABI") | |
556 | } | |
557 | ||
b039eaaf | 558 | // Primitive types with a stable representation. |
c30ab7b3 | 559 | ty::TyBool | ty::TyInt(..) | ty::TyUint(..) | ty::TyFloat(..) | ty::TyNever => FfiSafe, |
b039eaaf | 560 | |
b039eaaf SL |
561 | ty::TySlice(_) => { |
562 | FfiUnsafe("found Rust slice type in foreign module, \ | |
563 | consider using a raw pointer instead") | |
564 | } | |
565 | ||
476ff2be | 566 | ty::TyDynamic(..) => { |
b039eaaf SL |
567 | FfiUnsafe("found Rust trait type in foreign module, \ |
568 | consider using a raw pointer instead") | |
569 | } | |
570 | ||
571 | ty::TyStr => { | |
572 | FfiUnsafe("found Rust type `str` in foreign module; \ | |
573 | consider using a `*const libc::c_char`") | |
574 | } | |
575 | ||
8bb4bdeb | 576 | ty::TyTuple(..) => { |
b039eaaf | 577 | FfiUnsafe("found Rust tuple type in foreign module; \ |
9e0c209e | 578 | consider using a struct instead") |
b039eaaf SL |
579 | } |
580 | ||
c30ab7b3 SL |
581 | ty::TyRawPtr(ref m) | |
582 | ty::TyRef(_, ref m) => self.check_type_for_ffi(cache, m.ty), | |
b039eaaf | 583 | |
c30ab7b3 | 584 | ty::TyArray(ty, _) => self.check_type_for_ffi(cache, ty), |
b039eaaf | 585 | |
8bb4bdeb XL |
586 | ty::TyFnPtr(sig) => { |
587 | match sig.abi() { | |
c30ab7b3 SL |
588 | Abi::Rust | Abi::RustIntrinsic | Abi::PlatformIntrinsic | Abi::RustCall => { |
589 | return FfiUnsafe("found function pointer with Rust calling convention in \ | |
590 | foreign module; consider using an `extern` function \ | |
591 | pointer") | |
b039eaaf SL |
592 | } |
593 | _ => {} | |
594 | } | |
595 | ||
8bb4bdeb | 596 | let sig = cx.erase_late_bound_regions(&sig); |
476ff2be SL |
597 | if !sig.output().is_nil() { |
598 | let r = self.check_type_for_ffi(cache, sig.output()); | |
5bcae85e SL |
599 | match r { |
600 | FfiSafe => {} | |
c30ab7b3 SL |
601 | _ => { |
602 | return r; | |
603 | } | |
b039eaaf SL |
604 | } |
605 | } | |
476ff2be | 606 | for arg in sig.inputs() { |
b039eaaf SL |
607 | let r = self.check_type_for_ffi(cache, arg); |
608 | match r { | |
609 | FfiSafe => {} | |
c30ab7b3 SL |
610 | _ => { |
611 | return r; | |
612 | } | |
b039eaaf SL |
613 | } |
614 | } | |
615 | FfiSafe | |
616 | } | |
617 | ||
c30ab7b3 SL |
618 | ty::TyParam(..) | |
619 | ty::TyInfer(..) | | |
620 | ty::TyError | | |
621 | ty::TyClosure(..) | | |
ea8adc8c | 622 | ty::TyGenerator(..) | |
c30ab7b3 SL |
623 | ty::TyProjection(..) | |
624 | ty::TyAnon(..) | | |
625 | ty::TyFnDef(..) => bug!("Unexpected type in foreign function"), | |
b039eaaf SL |
626 | } |
627 | } | |
628 | ||
54a0048b SL |
629 | fn check_type_for_ffi_and_report_errors(&mut self, sp: Span, ty: Ty<'tcx>) { |
630 | // it is only OK to use this function because extern fns cannot have | |
631 | // any generic types right now: | |
a7813a04 | 632 | let ty = self.cx.tcx.normalize_associated_type(&ty); |
b039eaaf | 633 | |
476ff2be | 634 | match self.check_type_for_ffi(&mut FxHashSet(), ty) { |
b039eaaf | 635 | FfiResult::FfiSafe => {} |
8bb4bdeb XL |
636 | FfiResult::FfiPhantom => { |
637 | self.cx.span_lint(IMPROPER_CTYPES, | |
638 | sp, | |
639 | &format!("found zero-sized type composed only \ | |
640 | of phantom-data in a foreign-function.")); | |
641 | } | |
b039eaaf SL |
642 | FfiResult::FfiUnsafe(s) => { |
643 | self.cx.span_lint(IMPROPER_CTYPES, sp, s); | |
644 | } | |
645 | FfiResult::FfiBadStruct(_, s) => { | |
646 | // FIXME: This diagnostic is difficult to read, and doesn't | |
647 | // point at the relevant field. | |
c30ab7b3 SL |
648 | self.cx.span_lint(IMPROPER_CTYPES, |
649 | sp, | |
650 | &format!("found non-foreign-function-safe member in struct \ | |
651 | marked #[repr(C)]: {}", | |
652 | s)); | |
b039eaaf | 653 | } |
9e0c209e SL |
654 | FfiResult::FfiBadUnion(_, s) => { |
655 | // FIXME: This diagnostic is difficult to read, and doesn't | |
656 | // point at the relevant field. | |
c30ab7b3 SL |
657 | self.cx.span_lint(IMPROPER_CTYPES, |
658 | sp, | |
659 | &format!("found non-foreign-function-safe member in union \ | |
660 | marked #[repr(C)]: {}", | |
661 | s)); | |
9e0c209e | 662 | } |
b039eaaf SL |
663 | FfiResult::FfiBadEnum(_, s) => { |
664 | // FIXME: This diagnostic is difficult to read, and doesn't | |
665 | // point at the relevant variant. | |
c30ab7b3 SL |
666 | self.cx.span_lint(IMPROPER_CTYPES, |
667 | sp, | |
668 | &format!("found non-foreign-function-safe member in enum: {}", | |
669 | s)); | |
b039eaaf SL |
670 | } |
671 | } | |
672 | } | |
b039eaaf | 673 | |
54a0048b | 674 | fn check_foreign_fn(&mut self, id: ast::NodeId, decl: &hir::FnDecl) { |
32a655c1 | 675 | let def_id = self.cx.tcx.hir.local_def_id(id); |
041b39d2 | 676 | let sig = self.cx.tcx.fn_sig(def_id); |
54a0048b SL |
677 | let sig = self.cx.tcx.erase_late_bound_regions(&sig); |
678 | ||
476ff2be | 679 | for (input_ty, input_hir) in sig.inputs().iter().zip(&decl.inputs) { |
32a655c1 | 680 | self.check_type_for_ffi_and_report_errors(input_hir.span, input_ty); |
54a0048b SL |
681 | } |
682 | ||
683 | if let hir::Return(ref ret_hir) = decl.output { | |
476ff2be | 684 | let ret_ty = sig.output(); |
54a0048b SL |
685 | if !ret_ty.is_nil() { |
686 | self.check_type_for_ffi_and_report_errors(ret_hir.span, ret_ty); | |
b039eaaf | 687 | } |
b039eaaf SL |
688 | } |
689 | } | |
54a0048b SL |
690 | |
691 | fn check_foreign_static(&mut self, id: ast::NodeId, span: Span) { | |
32a655c1 | 692 | let def_id = self.cx.tcx.hir.local_def_id(id); |
7cac9316 | 693 | let ty = self.cx.tcx.type_of(def_id); |
476ff2be | 694 | self.check_type_for_ffi_and_report_errors(span, ty); |
54a0048b | 695 | } |
b039eaaf SL |
696 | } |
697 | ||
698 | #[derive(Copy, Clone)] | |
699 | pub struct ImproperCTypes; | |
700 | ||
701 | impl LintPass for ImproperCTypes { | |
702 | fn get_lints(&self) -> LintArray { | |
703 | lint_array!(IMPROPER_CTYPES) | |
704 | } | |
705 | } | |
706 | ||
476ff2be | 707 | impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImproperCTypes { |
b039eaaf | 708 | fn check_item(&mut self, cx: &LateContext, it: &hir::Item) { |
54a0048b | 709 | let mut vis = ImproperCTypesVisitor { cx: cx }; |
92a42be0 | 710 | if let hir::ItemForeignMod(ref nmod) = it.node { |
7453a54e | 711 | if nmod.abi != Abi::RustIntrinsic && nmod.abi != Abi::PlatformIntrinsic { |
b039eaaf SL |
712 | for ni in &nmod.items { |
713 | match ni.node { | |
32a655c1 | 714 | hir::ForeignItemFn(ref decl, _, _) => { |
54a0048b SL |
715 | vis.check_foreign_fn(ni.id, decl); |
716 | } | |
717 | hir::ForeignItemStatic(ref ty, _) => { | |
718 | vis.check_foreign_static(ni.id, ty.span); | |
719 | } | |
b039eaaf SL |
720 | } |
721 | } | |
722 | } | |
b039eaaf SL |
723 | } |
724 | } | |
725 | } | |
5bcae85e SL |
726 | |
727 | pub struct VariantSizeDifferences; | |
728 | ||
729 | impl LintPass for VariantSizeDifferences { | |
730 | fn get_lints(&self) -> LintArray { | |
731 | lint_array!(VARIANT_SIZE_DIFFERENCES) | |
732 | } | |
733 | } | |
734 | ||
476ff2be | 735 | impl<'a, 'tcx> LateLintPass<'a, 'tcx> for VariantSizeDifferences { |
5bcae85e SL |
736 | fn check_item(&mut self, cx: &LateContext, it: &hir::Item) { |
737 | if let hir::ItemEnum(ref enum_definition, ref gens) = it.node { | |
c30ab7b3 SL |
738 | if gens.ty_params.is_empty() { |
739 | // sizes only make sense for non-generic types | |
7cac9316 XL |
740 | let item_def_id = cx.tcx.hir.local_def_id(it.id); |
741 | let t = cx.tcx.type_of(item_def_id); | |
041b39d2 | 742 | let param_env = cx.param_env.reveal_all(); |
7cac9316 XL |
743 | let ty = cx.tcx.erase_regions(&t); |
744 | let layout = ty.layout(cx.tcx, param_env).unwrap_or_else(|e| { | |
745 | bug!("failed to get layout for `{}`: {}", t, e) | |
5bcae85e SL |
746 | }); |
747 | ||
748 | if let Layout::General { ref variants, ref size, discr, .. } = *layout { | |
cc61c64b | 749 | let discr_size = Primitive::Int(discr).size(cx.tcx).bytes(); |
5bcae85e | 750 | |
476ff2be SL |
751 | debug!("enum `{}` is {} bytes large with layout:\n{:#?}", |
752 | t, size.bytes(), layout); | |
5bcae85e SL |
753 | |
754 | let (largest, slargest, largest_index) = enum_definition.variants | |
755 | .iter() | |
756 | .zip(variants) | |
757 | .map(|(variant, variant_layout)| { | |
758 | // Subtract the size of the enum discriminant | |
c30ab7b3 SL |
759 | let bytes = variant_layout.min_size |
760 | .bytes() | |
761 | .saturating_sub(discr_size); | |
5bcae85e SL |
762 | |
763 | debug!("- variant `{}` is {} bytes large", variant.node.name, bytes); | |
764 | bytes | |
765 | }) | |
766 | .enumerate() | |
c30ab7b3 SL |
767 | .fold((0, 0, 0), |(l, s, li), (idx, size)| if size > l { |
768 | (size, l, idx) | |
769 | } else if size > s { | |
770 | (l, size, li) | |
771 | } else { | |
772 | (l, s, li) | |
773 | }); | |
5bcae85e SL |
774 | |
775 | // we only warn if the largest variant is at least thrice as large as | |
776 | // the second-largest. | |
777 | if largest > slargest * 3 && slargest > 0 { | |
778 | cx.span_lint(VARIANT_SIZE_DIFFERENCES, | |
779 | enum_definition.variants[largest_index].span, | |
780 | &format!("enum variant is more than three times larger \ | |
c30ab7b3 SL |
781 | ({} bytes) than the next largest", |
782 | largest)); | |
5bcae85e SL |
783 | } |
784 | } | |
785 | } | |
786 | } | |
787 | } | |
788 | } |