]>
Commit | Line | Data |
---|---|---|
9cc50fc6 SL |
1 | #![allow(non_snake_case)] |
2 | ||
48663c56 | 3 | use rustc::hir::{ExprKind, Node}; |
dc9dc135 | 4 | use crate::hir::def_id::DefId; |
48663c56 | 5 | use rustc::hir::lowering::is_range_literal; |
532ac7d7 | 6 | use rustc::ty::subst::SubstsRef; |
0531ce1d | 7 | use rustc::ty::{self, AdtKind, ParamEnv, Ty, TyCtxt}; |
dc9dc135 | 8 | use rustc::ty::layout::{self, IntegerExt, LayoutOf, VariantIdx, SizeSkeleton}; |
9fa01778 | 9 | use rustc::{lint, util}; |
e74abb32 | 10 | use rustc_index::vec::Idx; |
476ff2be | 11 | use util::nodemap::FxHashSet; |
b039eaaf SL |
12 | use lint::{LateContext, LintContext, LintArray}; |
13 | use lint::{LintPass, LateLintPass}; | |
14 | ||
15 | use std::cmp; | |
16 | use std::{i8, i16, i32, i64, u8, u16, u32, u64, f32, f64}; | |
17 | ||
dc9dc135 | 18 | use syntax::{ast, attr, source_map}; |
b7449926 | 19 | use syntax::errors::Applicability; |
dc9dc135 | 20 | use syntax::symbol::sym; |
83c7162d | 21 | use rustc_target::spec::abi::Abi; |
3157f602 | 22 | use syntax_pos::Span; |
b039eaaf | 23 | |
54a0048b | 24 | use rustc::hir; |
b039eaaf | 25 | |
9fa01778 XL |
26 | use rustc::mir::interpret::{sign_extend, truncate}; |
27 | ||
28 | use log::debug; | |
29 | ||
b039eaaf SL |
30 | declare_lint! { |
31 | UNUSED_COMPARISONS, | |
32 | Warn, | |
33 | "comparisons made useless by limits of the types involved" | |
34 | } | |
35 | ||
36 | declare_lint! { | |
37 | OVERFLOWING_LITERALS, | |
9fa01778 XL |
38 | Deny, |
39 | "literal out of range for its type" | |
b039eaaf SL |
40 | } |
41 | ||
5bcae85e SL |
42 | declare_lint! { |
43 | VARIANT_SIZE_DIFFERENCES, | |
44 | Allow, | |
45 | "detects enums with widely varying variant sizes" | |
46 | } | |
47 | ||
b039eaaf SL |
48 | #[derive(Copy, Clone)] |
49 | pub struct TypeLimits { | |
50 | /// Id of the last visited negated expression | |
532ac7d7 | 51 | negated_expr_id: hir::HirId, |
b039eaaf SL |
52 | } |
53 | ||
532ac7d7 XL |
54 | impl_lint_pass!(TypeLimits => [UNUSED_COMPARISONS, OVERFLOWING_LITERALS]); |
55 | ||
b039eaaf SL |
56 | impl TypeLimits { |
57 | pub fn new() -> TypeLimits { | |
532ac7d7 | 58 | TypeLimits { negated_expr_id: hir::DUMMY_HIR_ID } |
b039eaaf SL |
59 | } |
60 | } | |
61 | ||
48663c56 XL |
62 | /// Attempts to special-case the overflowing literal lint when it occurs as a range endpoint. |
63 | /// Returns `true` iff the lint was overridden. | |
64 | fn lint_overflowing_range_endpoint<'a, 'tcx>( | |
65 | cx: &LateContext<'a, 'tcx>, | |
66 | lit: &hir::Lit, | |
67 | lit_val: u128, | |
68 | max: u128, | |
69 | expr: &'tcx hir::Expr, | |
70 | parent_expr: &'tcx hir::Expr, | |
60c5eb7d | 71 | ty: &str, |
48663c56 XL |
72 | ) -> bool { |
73 | // We only want to handle exclusive (`..`) ranges, | |
74 | // which are represented as `ExprKind::Struct`. | |
e74abb32 | 75 | if let ExprKind::Struct(_, eps, _) = &parent_expr.kind { |
416331ca XL |
76 | if eps.len() != 2 { |
77 | return false; | |
78 | } | |
48663c56 XL |
79 | // We can suggest using an inclusive range |
80 | // (`..=`) instead only if it is the `end` that is | |
81 | // overflowing and only by 1. | |
82 | if eps[1].expr.hir_id == expr.hir_id && lit_val - 1 == max { | |
83 | let mut err = cx.struct_span_lint( | |
84 | OVERFLOWING_LITERALS, | |
85 | parent_expr.span, | |
60c5eb7d | 86 | &format!("range endpoint is out of range for `{}`", ty), |
48663c56 XL |
87 | ); |
88 | if let Ok(start) = cx.sess().source_map().span_to_snippet(eps[0].span) { | |
89 | use ast::{LitKind, LitIntType}; | |
90 | // We need to preserve the literal's suffix, | |
91 | // as it may determine typing information. | |
92 | let suffix = match lit.node { | |
60c5eb7d XL |
93 | LitKind::Int(_, LitIntType::Signed(s)) => format!("{}", s.name_str()), |
94 | LitKind::Int(_, LitIntType::Unsigned(s)) => format!("{}", s.name_str()), | |
48663c56 XL |
95 | LitKind::Int(_, LitIntType::Unsuffixed) => "".to_owned(), |
96 | _ => bug!(), | |
97 | }; | |
98 | let suggestion = format!("{}..={}{}", start, lit_val - 1, suffix); | |
99 | err.span_suggestion( | |
100 | parent_expr.span, | |
101 | &"use an inclusive range instead", | |
102 | suggestion, | |
103 | Applicability::MachineApplicable, | |
104 | ); | |
105 | err.emit(); | |
106 | return true; | |
107 | } | |
108 | } | |
109 | } | |
110 | ||
111 | false | |
112 | } | |
113 | ||
114 | // For `isize` & `usize`, be conservative with the warnings, so that the | |
115 | // warnings are consistent between 32- and 64-bit platforms. | |
116 | fn int_ty_range(int_ty: ast::IntTy) -> (i128, i128) { | |
117 | match int_ty { | |
118 | ast::IntTy::Isize => (i64::min_value() as i128, i64::max_value() as i128), | |
119 | ast::IntTy::I8 => (i8::min_value() as i64 as i128, i8::max_value() as i128), | |
120 | ast::IntTy::I16 => (i16::min_value() as i64 as i128, i16::max_value() as i128), | |
121 | ast::IntTy::I32 => (i32::min_value() as i64 as i128, i32::max_value() as i128), | |
122 | ast::IntTy::I64 => (i64::min_value() as i128, i64::max_value() as i128), | |
123 | ast::IntTy::I128 =>(i128::min_value() as i128, i128::max_value()), | |
124 | } | |
125 | } | |
126 | ||
127 | fn uint_ty_range(uint_ty: ast::UintTy) -> (u128, u128) { | |
128 | match uint_ty { | |
129 | ast::UintTy::Usize => (u64::min_value() as u128, u64::max_value() as u128), | |
130 | ast::UintTy::U8 => (u8::min_value() as u128, u8::max_value() as u128), | |
131 | ast::UintTy::U16 => (u16::min_value() as u128, u16::max_value() as u128), | |
132 | ast::UintTy::U32 => (u32::min_value() as u128, u32::max_value() as u128), | |
133 | ast::UintTy::U64 => (u64::min_value() as u128, u64::max_value() as u128), | |
134 | ast::UintTy::U128 => (u128::min_value(), u128::max_value()), | |
135 | } | |
136 | } | |
137 | ||
138 | fn get_bin_hex_repr(cx: &LateContext<'_, '_>, lit: &hir::Lit) -> Option<String> { | |
139 | let src = cx.sess().source_map().span_to_snippet(lit.span).ok()?; | |
140 | let firstch = src.chars().next()?; | |
141 | ||
142 | if firstch == '0' { | |
143 | match src.chars().nth(1) { | |
144 | Some('x') | Some('b') => return Some(src), | |
145 | _ => return None, | |
146 | } | |
147 | } | |
148 | ||
149 | None | |
150 | } | |
151 | ||
152 | fn report_bin_hex_error( | |
153 | cx: &LateContext<'_, '_>, | |
154 | expr: &hir::Expr, | |
155 | ty: attr::IntType, | |
156 | repr_str: String, | |
157 | val: u128, | |
158 | negative: bool, | |
159 | ) { | |
160 | let size = layout::Integer::from_attr(&cx.tcx, ty).size(); | |
161 | let (t, actually) = match ty { | |
162 | attr::IntType::SignedInt(t) => { | |
163 | let actually = sign_extend(val, size) as i128; | |
60c5eb7d | 164 | (t.name_str(), actually.to_string()) |
48663c56 XL |
165 | } |
166 | attr::IntType::UnsignedInt(t) => { | |
167 | let actually = truncate(val, size); | |
60c5eb7d | 168 | (t.name_str(), actually.to_string()) |
48663c56 XL |
169 | } |
170 | }; | |
171 | let mut err = cx.struct_span_lint( | |
172 | OVERFLOWING_LITERALS, | |
173 | expr.span, | |
174 | &format!("literal out of range for {}", t), | |
175 | ); | |
176 | err.note(&format!( | |
177 | "the literal `{}` (decimal `{}`) does not fit into \ | |
178 | an `{}` and will become `{}{}`", | |
179 | repr_str, val, t, actually, t | |
180 | )); | |
181 | if let Some(sugg_ty) = | |
182 | get_type_suggestion(&cx.tables.node_type(expr.hir_id), val, negative) | |
183 | { | |
184 | if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') { | |
185 | let (sans_suffix, _) = repr_str.split_at(pos); | |
186 | err.span_suggestion( | |
187 | expr.span, | |
188 | &format!("consider using `{}` instead", sugg_ty), | |
189 | format!("{}{}", sans_suffix, sugg_ty), | |
190 | Applicability::MachineApplicable | |
191 | ); | |
192 | } else { | |
193 | err.help(&format!("consider using `{}` instead", sugg_ty)); | |
194 | } | |
195 | } | |
196 | ||
197 | err.emit(); | |
198 | } | |
199 | ||
200 | // This function finds the next fitting type and generates a suggestion string. | |
201 | // It searches for fitting types in the following way (`X < Y`): | |
202 | // - `iX`: if literal fits in `uX` => `uX`, else => `iY` | |
203 | // - `-iX` => `iY` | |
204 | // - `uX` => `uY` | |
205 | // | |
206 | // No suggestion for: `isize`, `usize`. | |
60c5eb7d | 207 | fn get_type_suggestion(t: Ty<'_>, val: u128, negative: bool) -> Option<&'static str> { |
48663c56 XL |
208 | use syntax::ast::IntTy::*; |
209 | use syntax::ast::UintTy::*; | |
210 | macro_rules! find_fit { | |
211 | ($ty:expr, $val:expr, $negative:expr, | |
212 | $($type:ident => [$($utypes:expr),*] => [$($itypes:expr),*]),+) => { | |
213 | { | |
214 | let _neg = if negative { 1 } else { 0 }; | |
215 | match $ty { | |
216 | $($type => { | |
217 | $(if !negative && val <= uint_ty_range($utypes).1 { | |
60c5eb7d | 218 | return Some($utypes.name_str()) |
48663c56 XL |
219 | })* |
220 | $(if val <= int_ty_range($itypes).1 as u128 + _neg { | |
60c5eb7d | 221 | return Some($itypes.name_str()) |
48663c56 XL |
222 | })* |
223 | None | |
dc9dc135 | 224 | },)+ |
48663c56 XL |
225 | _ => None |
226 | } | |
227 | } | |
228 | } | |
229 | } | |
e74abb32 | 230 | match t.kind { |
48663c56 XL |
231 | ty::Int(i) => find_fit!(i, val, negative, |
232 | I8 => [U8] => [I16, I32, I64, I128], | |
233 | I16 => [U16] => [I32, I64, I128], | |
234 | I32 => [U32] => [I64, I128], | |
235 | I64 => [U64] => [I128], | |
236 | I128 => [U128] => []), | |
237 | ty::Uint(u) => find_fit!(u, val, negative, | |
238 | U8 => [U8, U16, U32, U64, U128] => [], | |
239 | U16 => [U16, U32, U64, U128] => [], | |
240 | U32 => [U32, U64, U128] => [], | |
241 | U64 => [U64, U128] => [], | |
242 | U128 => [U128] => []), | |
243 | _ => None, | |
244 | } | |
245 | } | |
246 | ||
247 | fn lint_int_literal<'a, 'tcx>( | |
248 | cx: &LateContext<'a, 'tcx>, | |
249 | type_limits: &TypeLimits, | |
250 | e: &'tcx hir::Expr, | |
251 | lit: &hir::Lit, | |
252 | t: ast::IntTy, | |
253 | v: u128, | |
254 | ) { | |
60c5eb7d | 255 | let int_type = t.normalize(cx.sess().target.ptr_width); |
48663c56 XL |
256 | let (_, max) = int_ty_range(int_type); |
257 | let max = max as u128; | |
258 | let negative = type_limits.negated_expr_id == e.hir_id; | |
259 | ||
260 | // Detect literal value out of range [min, max] inclusive | |
261 | // avoiding use of -min to prevent overflow/panic | |
262 | if (negative && v > max + 1) || (!negative && v > max) { | |
263 | if let Some(repr_str) = get_bin_hex_repr(cx, lit) { | |
264 | report_bin_hex_error( | |
265 | cx, | |
266 | e, | |
267 | attr::IntType::SignedInt(t), | |
268 | repr_str, | |
269 | v, | |
270 | negative, | |
271 | ); | |
272 | return; | |
273 | } | |
274 | ||
dc9dc135 XL |
275 | let par_id = cx.tcx.hir().get_parent_node(e.hir_id); |
276 | if let Node::Expr(par_e) = cx.tcx.hir().get(par_id) { | |
e74abb32 | 277 | if let hir::ExprKind::Struct(..) = par_e.kind { |
48663c56 | 278 | if is_range_literal(cx.sess(), par_e) |
60c5eb7d | 279 | && lint_overflowing_range_endpoint(cx, lit, v, max, e, par_e, t.name_str()) |
48663c56 XL |
280 | { |
281 | // The overflowing literal lint was overridden. | |
282 | return; | |
283 | } | |
284 | } | |
285 | } | |
286 | ||
287 | cx.span_lint( | |
288 | OVERFLOWING_LITERALS, | |
289 | e.span, | |
60c5eb7d | 290 | &format!("literal out of range for `{}`", t.name_str()), |
48663c56 XL |
291 | ); |
292 | } | |
293 | } | |
294 | ||
295 | fn lint_uint_literal<'a, 'tcx>( | |
296 | cx: &LateContext<'a, 'tcx>, | |
297 | e: &'tcx hir::Expr, | |
298 | lit: &hir::Lit, | |
299 | t: ast::UintTy, | |
300 | ) { | |
60c5eb7d | 301 | let uint_type = t.normalize(cx.sess().target.ptr_width); |
48663c56 XL |
302 | let (min, max) = uint_ty_range(uint_type); |
303 | let lit_val: u128 = match lit.node { | |
304 | // _v is u8, within range by definition | |
305 | ast::LitKind::Byte(_v) => return, | |
306 | ast::LitKind::Int(v, _) => v, | |
307 | _ => bug!(), | |
308 | }; | |
309 | if lit_val < min || lit_val > max { | |
dc9dc135 XL |
310 | let parent_id = cx.tcx.hir().get_parent_node(e.hir_id); |
311 | if let Node::Expr(par_e) = cx.tcx.hir().get(parent_id) { | |
e74abb32 | 312 | match par_e.kind { |
48663c56 | 313 | hir::ExprKind::Cast(..) => { |
e74abb32 | 314 | if let ty::Char = cx.tables.expr_ty(par_e).kind { |
48663c56 XL |
315 | let mut err = cx.struct_span_lint( |
316 | OVERFLOWING_LITERALS, | |
317 | par_e.span, | |
318 | "only `u8` can be cast into `char`", | |
319 | ); | |
320 | err.span_suggestion( | |
321 | par_e.span, | |
322 | &"use a `char` literal instead", | |
323 | format!("'\\u{{{:X}}}'", lit_val), | |
324 | Applicability::MachineApplicable, | |
325 | ); | |
326 | err.emit(); | |
327 | return; | |
328 | } | |
329 | } | |
330 | hir::ExprKind::Struct(..) | |
331 | if is_range_literal(cx.sess(), par_e) => { | |
60c5eb7d | 332 | let t = t.name_str(); |
48663c56 XL |
333 | if lint_overflowing_range_endpoint(cx, lit, lit_val, max, e, par_e, t) { |
334 | // The overflowing literal lint was overridden. | |
335 | return; | |
336 | } | |
337 | } | |
338 | _ => {} | |
339 | } | |
340 | } | |
341 | if let Some(repr_str) = get_bin_hex_repr(cx, lit) { | |
342 | report_bin_hex_error(cx, e, attr::IntType::UnsignedInt(t), repr_str, lit_val, false); | |
343 | return; | |
344 | } | |
345 | cx.span_lint( | |
346 | OVERFLOWING_LITERALS, | |
347 | e.span, | |
60c5eb7d | 348 | &format!("literal out of range for `{}`", t.name_str()), |
48663c56 XL |
349 | ); |
350 | } | |
351 | } | |
352 | ||
353 | fn lint_literal<'a, 'tcx>( | |
354 | cx: &LateContext<'a, 'tcx>, | |
355 | type_limits: &TypeLimits, | |
356 | e: &'tcx hir::Expr, | |
357 | lit: &hir::Lit, | |
358 | ) { | |
e74abb32 | 359 | match cx.tables.node_type(e.hir_id).kind { |
48663c56 XL |
360 | ty::Int(t) => { |
361 | match lit.node { | |
362 | ast::LitKind::Int(v, ast::LitIntType::Signed(_)) | | |
363 | ast::LitKind::Int(v, ast::LitIntType::Unsuffixed) => { | |
364 | lint_int_literal(cx, type_limits, e, lit, t, v) | |
365 | } | |
366 | _ => bug!(), | |
367 | }; | |
368 | } | |
369 | ty::Uint(t) => { | |
370 | lint_uint_literal(cx, e, lit, t) | |
371 | } | |
372 | ty::Float(t) => { | |
373 | let is_infinite = match lit.node { | |
60c5eb7d | 374 | ast::LitKind::Float(v, _) => { |
48663c56 XL |
375 | match t { |
376 | ast::FloatTy::F32 => v.as_str().parse().map(f32::is_infinite), | |
377 | ast::FloatTy::F64 => v.as_str().parse().map(f64::is_infinite), | |
378 | } | |
379 | } | |
380 | _ => bug!(), | |
381 | }; | |
382 | if is_infinite == Ok(true) { | |
60c5eb7d XL |
383 | cx.span_lint( |
384 | OVERFLOWING_LITERALS, | |
385 | e.span, | |
386 | &format!("literal out of range for `{}`", t.name_str()), | |
387 | ); | |
48663c56 XL |
388 | } |
389 | } | |
390 | _ => {} | |
391 | } | |
392 | } | |
393 | ||
476ff2be | 394 | impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { |
ea8adc8c | 395 | fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx hir::Expr) { |
e74abb32 | 396 | match e.kind { |
8faf50e0 | 397 | hir::ExprKind::Unary(hir::UnNeg, ref expr) => { |
b039eaaf | 398 | // propagate negation, if the negation itself isn't negated |
532ac7d7 XL |
399 | if self.negated_expr_id != e.hir_id { |
400 | self.negated_expr_id = expr.hir_id; | |
b039eaaf | 401 | } |
c30ab7b3 | 402 | } |
8faf50e0 | 403 | hir::ExprKind::Binary(binop, ref l, ref r) => { |
32a655c1 | 404 | if is_comparison(binop) && !check_limits(cx, binop, &l, &r) { |
c30ab7b3 SL |
405 | cx.span_lint(UNUSED_COMPARISONS, |
406 | e.span, | |
b039eaaf SL |
407 | "comparison is useless due to type limits"); |
408 | } | |
c30ab7b3 | 409 | } |
48663c56 XL |
410 | hir::ExprKind::Lit(ref lit) => lint_literal(cx, self, e, lit), |
411 | _ => {} | |
b039eaaf SL |
412 | }; |
413 | ||
c30ab7b3 | 414 | fn is_valid<T: cmp::PartialOrd>(binop: hir::BinOp, v: T, min: T, max: T) -> bool { |
b039eaaf | 415 | match binop.node { |
8faf50e0 XL |
416 | hir::BinOpKind::Lt => v > min && v <= max, |
417 | hir::BinOpKind::Le => v >= min && v < max, | |
418 | hir::BinOpKind::Gt => v >= min && v < max, | |
419 | hir::BinOpKind::Ge => v > min && v <= max, | |
420 | hir::BinOpKind::Eq | hir::BinOpKind::Ne => v >= min && v <= max, | |
c30ab7b3 | 421 | _ => bug!(), |
b039eaaf SL |
422 | } |
423 | } | |
424 | ||
425 | fn rev_binop(binop: hir::BinOp) -> hir::BinOp { | |
b7449926 | 426 | source_map::respan(binop.span, |
c30ab7b3 | 427 | match binop.node { |
8faf50e0 XL |
428 | hir::BinOpKind::Lt => hir::BinOpKind::Gt, |
429 | hir::BinOpKind::Le => hir::BinOpKind::Ge, | |
430 | hir::BinOpKind::Gt => hir::BinOpKind::Lt, | |
431 | hir::BinOpKind::Ge => hir::BinOpKind::Le, | |
c30ab7b3 SL |
432 | _ => return binop, |
433 | }) | |
b039eaaf SL |
434 | } |
435 | ||
9fa01778 | 436 | fn check_limits(cx: &LateContext<'_, '_>, |
32a655c1 SL |
437 | binop: hir::BinOp, |
438 | l: &hir::Expr, | |
439 | r: &hir::Expr) | |
440 | -> bool { | |
e74abb32 | 441 | let (lit, expr, swap) = match (&l.kind, &r.kind) { |
8faf50e0 XL |
442 | (&hir::ExprKind::Lit(_), _) => (l, r, true), |
443 | (_, &hir::ExprKind::Lit(_)) => (r, l, false), | |
c30ab7b3 | 444 | _ => return true, |
b039eaaf SL |
445 | }; |
446 | // Normalize the binop so that the literal is always on the RHS in | |
447 | // the comparison | |
c30ab7b3 | 448 | let norm_binop = if swap { rev_binop(binop) } else { binop }; |
e74abb32 | 449 | match cx.tables.node_type(expr.hir_id).kind { |
b7449926 | 450 | ty::Int(int_ty) => { |
b039eaaf | 451 | let (min, max) = int_ty_range(int_ty); |
e74abb32 | 452 | let lit_val: i128 = match lit.kind { |
8faf50e0 | 453 | hir::ExprKind::Lit(ref li) => { |
c30ab7b3 SL |
454 | match li.node { |
455 | ast::LitKind::Int(v, ast::LitIntType::Signed(_)) | | |
32a655c1 SL |
456 | ast::LitKind::Int(v, ast::LitIntType::Unsuffixed) => v as i128, |
457 | _ => return true | |
c30ab7b3 | 458 | } |
32a655c1 SL |
459 | }, |
460 | _ => bug!() | |
b039eaaf SL |
461 | }; |
462 | is_valid(norm_binop, lit_val, min, max) | |
463 | } | |
b7449926 | 464 | ty::Uint(uint_ty) => { |
32a655c1 | 465 | let (min, max) :(u128, u128) = uint_ty_range(uint_ty); |
e74abb32 | 466 | let lit_val: u128 = match lit.kind { |
8faf50e0 | 467 | hir::ExprKind::Lit(ref li) => { |
c30ab7b3 SL |
468 | match li.node { |
469 | ast::LitKind::Int(v, _) => v, | |
32a655c1 | 470 | _ => return true |
c30ab7b3 | 471 | } |
32a655c1 SL |
472 | }, |
473 | _ => bug!() | |
b039eaaf SL |
474 | }; |
475 | is_valid(norm_binop, lit_val, min, max) | |
476 | } | |
c30ab7b3 | 477 | _ => true, |
b039eaaf SL |
478 | } |
479 | } | |
480 | ||
481 | fn is_comparison(binop: hir::BinOp) -> bool { | |
482 | match binop.node { | |
8faf50e0 XL |
483 | hir::BinOpKind::Eq | |
484 | hir::BinOpKind::Lt | | |
485 | hir::BinOpKind::Le | | |
486 | hir::BinOpKind::Ne | | |
487 | hir::BinOpKind::Ge | | |
488 | hir::BinOpKind::Gt => true, | |
c30ab7b3 | 489 | _ => false, |
b039eaaf SL |
490 | } |
491 | } | |
b039eaaf SL |
492 | } |
493 | } | |
494 | ||
495 | declare_lint! { | |
496 | IMPROPER_CTYPES, | |
497 | Warn, | |
498 | "proper use of libc types in foreign modules" | |
499 | } | |
500 | ||
532ac7d7 XL |
501 | declare_lint_pass!(ImproperCTypes => [IMPROPER_CTYPES]); |
502 | ||
dc9dc135 | 503 | struct ImproperCTypesVisitor<'a, 'tcx> { |
c30ab7b3 | 504 | cx: &'a LateContext<'a, 'tcx>, |
b039eaaf SL |
505 | } |
506 | ||
0531ce1d | 507 | enum FfiResult<'tcx> { |
b039eaaf | 508 | FfiSafe, |
0531ce1d XL |
509 | FfiPhantom(Ty<'tcx>), |
510 | FfiUnsafe { | |
511 | ty: Ty<'tcx>, | |
512 | reason: &'static str, | |
513 | help: Option<&'static str>, | |
514 | }, | |
b039eaaf SL |
515 | } |
516 | ||
dc9dc135 XL |
517 | fn is_zst<'tcx>(tcx: TyCtxt<'tcx>, did: DefId, ty: Ty<'tcx>) -> bool { |
518 | tcx.layout_of(tcx.param_env(did).and(ty)).map(|layout| layout.is_zst()).unwrap_or(false) | |
519 | } | |
b039eaaf | 520 | |
dc9dc135 | 521 | fn ty_is_known_nonnull<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { |
e74abb32 | 522 | match ty.kind { |
dc9dc135 XL |
523 | ty::FnPtr(_) => true, |
524 | ty::Ref(..) => true, | |
525 | ty::Adt(field_def, substs) if field_def.repr.transparent() && !field_def.is_union() => { | |
526 | for field in field_def.all_fields() { | |
527 | let field_ty = tcx.normalize_erasing_regions( | |
528 | ParamEnv::reveal_all(), | |
529 | field.ty(tcx, substs), | |
530 | ); | |
531 | if is_zst(tcx, field.did, field_ty) { | |
532 | continue; | |
c30ab7b3 | 533 | } |
dc9dc135 XL |
534 | |
535 | let attrs = tcx.get_attrs(field_def.did); | |
536 | if attrs.iter().any(|a| a.check_name(sym::rustc_nonnull_optimization_guaranteed)) || | |
537 | ty_is_known_nonnull(tcx, field_ty) { | |
c30ab7b3 SL |
538 | return true; |
539 | } | |
b039eaaf | 540 | } |
dc9dc135 XL |
541 | |
542 | false | |
b039eaaf | 543 | } |
dc9dc135 | 544 | _ => false, |
b039eaaf | 545 | } |
dc9dc135 XL |
546 | } |
547 | ||
548 | /// Check if this enum can be safely exported based on the | |
549 | /// "nullable pointer optimization". Currently restricted | |
550 | /// to function pointers, references, core::num::NonZero*, | |
551 | /// core::ptr::NonNull, and #[repr(transparent)] newtypes. | |
552 | /// FIXME: This duplicates code in codegen. | |
553 | fn is_repr_nullable_ptr<'tcx>( | |
554 | tcx: TyCtxt<'tcx>, | |
555 | ty: Ty<'tcx>, | |
556 | ty_def: &'tcx ty::AdtDef, | |
557 | substs: SubstsRef<'tcx>, | |
558 | ) -> bool { | |
559 | if ty_def.variants.len() != 2 { | |
560 | return false; | |
561 | } | |
562 | ||
563 | let get_variant_fields = |index| &ty_def.variants[VariantIdx::new(index)].fields; | |
564 | let variant_fields = [get_variant_fields(0), get_variant_fields(1)]; | |
565 | let fields = if variant_fields[0].is_empty() { | |
566 | &variant_fields[1] | |
567 | } else if variant_fields[1].is_empty() { | |
568 | &variant_fields[0] | |
569 | } else { | |
570 | return false; | |
571 | }; | |
572 | ||
573 | if fields.len() != 1 { | |
574 | return false; | |
575 | } | |
576 | ||
577 | let field_ty = fields[0].ty(tcx, substs); | |
578 | if !ty_is_known_nonnull(tcx, field_ty) { | |
579 | return false; | |
580 | } | |
581 | ||
582 | // At this point, the field's type is known to be nonnull and the parent enum is Option-like. | |
583 | // If the computed size for the field and the enum are different, the nonnull optimization isn't | |
584 | // being applied (and we've got a problem somewhere). | |
585 | let compute_size_skeleton = |t| SizeSkeleton::compute(t, tcx, ParamEnv::reveal_all()).unwrap(); | |
586 | if !compute_size_skeleton(ty).same_size(compute_size_skeleton(field_ty)) { | |
587 | bug!("improper_ctypes: Option nonnull optimization not applied?"); | |
588 | } | |
589 | ||
590 | true | |
b039eaaf SL |
591 | } |
592 | ||
b039eaaf | 593 | impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { |
60c5eb7d XL |
594 | |
595 | /// Check if the type is array and emit an unsafe type lint. | |
596 | fn check_for_array_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool { | |
597 | if let ty::Array(..) = ty.kind { | |
598 | self.emit_ffi_unsafe_type_lint( | |
599 | ty, | |
600 | sp, | |
601 | "passing raw arrays by value is not FFI-safe", | |
602 | Some("consider passing a pointer to the array"), | |
603 | ); | |
604 | true | |
605 | } else { | |
606 | false | |
607 | } | |
608 | } | |
609 | ||
610 | ||
9fa01778 | 611 | /// Checks if the given type is "ffi-safe" (has a stable, well-defined |
b039eaaf | 612 | /// representation which can be exported to C code). |
8bb4bdeb XL |
613 | fn check_type_for_ffi(&self, |
614 | cache: &mut FxHashSet<Ty<'tcx>>, | |
0531ce1d | 615 | ty: Ty<'tcx>) -> FfiResult<'tcx> { |
9fa01778 | 616 | use FfiResult::*; |
8bb4bdeb | 617 | |
a7813a04 | 618 | let cx = self.cx.tcx; |
b039eaaf SL |
619 | |
620 | // Protect against infinite recursion, for example | |
621 | // `struct S(*mut S);`. | |
622 | // FIXME: A recursion limit is necessary as well, for irregular | |
b7449926 | 623 | // recursive types. |
b039eaaf SL |
624 | if !cache.insert(ty) { |
625 | return FfiSafe; | |
626 | } | |
627 | ||
e74abb32 | 628 | match ty.kind { |
b7449926 | 629 | ty::Adt(def, substs) => { |
8bb4bdeb | 630 | if def.is_phantom_data() { |
0531ce1d | 631 | return FfiPhantom(ty); |
8bb4bdeb | 632 | } |
c30ab7b3 SL |
633 | match def.adt_kind() { |
634 | AdtKind::Struct => { | |
2c00a5a8 | 635 | if !def.repr.c() && !def.repr.transparent() { |
0531ce1d | 636 | return FfiUnsafe { |
e1599b0c | 637 | ty, |
0531ce1d | 638 | reason: "this struct has unspecified layout", |
dc9dc135 XL |
639 | help: Some("consider adding a `#[repr(C)]` or \ |
640 | `#[repr(transparent)]` attribute to this struct"), | |
0531ce1d | 641 | }; |
c30ab7b3 | 642 | } |
b039eaaf | 643 | |
e74abb32 XL |
644 | let is_non_exhaustive = |
645 | def.non_enum_variant().is_field_list_non_exhaustive(); | |
646 | if is_non_exhaustive && !def.did.is_local() { | |
647 | return FfiUnsafe { | |
648 | ty, | |
649 | reason: "this struct is non-exhaustive", | |
650 | help: None, | |
651 | }; | |
652 | } | |
653 | ||
2c00a5a8 | 654 | if def.non_enum_variant().fields.is_empty() { |
0531ce1d | 655 | return FfiUnsafe { |
e1599b0c | 656 | ty, |
0531ce1d XL |
657 | reason: "this struct has no fields", |
658 | help: Some("consider adding a member to this struct"), | |
659 | }; | |
c30ab7b3 | 660 | } |
b039eaaf | 661 | |
2c00a5a8 XL |
662 | // We can't completely trust repr(C) and repr(transparent) markings; |
663 | // make sure the fields are actually safe. | |
8bb4bdeb | 664 | let mut all_phantom = true; |
2c00a5a8 | 665 | for field in &def.non_enum_variant().fields { |
0531ce1d XL |
666 | let field_ty = cx.normalize_erasing_regions( |
667 | ParamEnv::reveal_all(), | |
668 | field.ty(cx, substs), | |
abe05a73 | 669 | ); |
2c00a5a8 XL |
670 | // repr(transparent) types are allowed to have arbitrary ZSTs, not just |
671 | // PhantomData -- skip checking all ZST fields | |
dc9dc135 XL |
672 | if def.repr.transparent() && is_zst(cx, field.did, field_ty) { |
673 | continue; | |
2c00a5a8 | 674 | } |
c30ab7b3 SL |
675 | let r = self.check_type_for_ffi(cache, field_ty); |
676 | match r { | |
8bb4bdeb XL |
677 | FfiSafe => { |
678 | all_phantom = false; | |
679 | } | |
0531ce1d XL |
680 | FfiPhantom(..) => {} |
681 | FfiUnsafe { .. } => { | |
c30ab7b3 SL |
682 | return r; |
683 | } | |
c30ab7b3 | 684 | } |
9e0c209e | 685 | } |
8bb4bdeb | 686 | |
0531ce1d | 687 | if all_phantom { FfiPhantom(ty) } else { FfiSafe } |
b039eaaf | 688 | } |
c30ab7b3 | 689 | AdtKind::Union => { |
dc9dc135 | 690 | if !def.repr.c() && !def.repr.transparent() { |
0531ce1d | 691 | return FfiUnsafe { |
e1599b0c | 692 | ty, |
0531ce1d | 693 | reason: "this union has unspecified layout", |
dc9dc135 XL |
694 | help: Some("consider adding a `#[repr(C)]` or \ |
695 | `#[repr(transparent)]` attribute to this union"), | |
0531ce1d | 696 | }; |
c30ab7b3 | 697 | } |
b039eaaf | 698 | |
2c00a5a8 | 699 | if def.non_enum_variant().fields.is_empty() { |
0531ce1d | 700 | return FfiUnsafe { |
e1599b0c | 701 | ty, |
0531ce1d XL |
702 | reason: "this union has no fields", |
703 | help: Some("consider adding a field to this union"), | |
704 | }; | |
8bb4bdeb XL |
705 | } |
706 | ||
707 | let mut all_phantom = true; | |
2c00a5a8 | 708 | for field in &def.non_enum_variant().fields { |
0531ce1d XL |
709 | let field_ty = cx.normalize_erasing_regions( |
710 | ParamEnv::reveal_all(), | |
711 | field.ty(cx, substs), | |
abe05a73 | 712 | ); |
dc9dc135 XL |
713 | // repr(transparent) types are allowed to have arbitrary ZSTs, not just |
714 | // PhantomData -- skip checking all ZST fields. | |
715 | if def.repr.transparent() && is_zst(cx, field.did, field_ty) { | |
716 | continue; | |
717 | } | |
c30ab7b3 SL |
718 | let r = self.check_type_for_ffi(cache, field_ty); |
719 | match r { | |
8bb4bdeb XL |
720 | FfiSafe => { |
721 | all_phantom = false; | |
722 | } | |
0531ce1d XL |
723 | FfiPhantom(..) => {} |
724 | FfiUnsafe { .. } => { | |
c30ab7b3 SL |
725 | return r; |
726 | } | |
c30ab7b3 | 727 | } |
b039eaaf | 728 | } |
8bb4bdeb | 729 | |
0531ce1d | 730 | if all_phantom { FfiPhantom(ty) } else { FfiSafe } |
b039eaaf | 731 | } |
c30ab7b3 SL |
732 | AdtKind::Enum => { |
733 | if def.variants.is_empty() { | |
734 | // Empty enums are okay... although sort of useless. | |
735 | return FfiSafe; | |
736 | } | |
9e0c209e | 737 | |
c30ab7b3 SL |
738 | // Check for a repr() attribute to specify the size of the |
739 | // discriminant. | |
dc9dc135 | 740 | if !def.repr.c() && !def.repr.transparent() && def.repr.int.is_none() { |
8bb4bdeb | 741 | // Special-case types like `Option<extern fn()>`. |
dc9dc135 | 742 | if !is_repr_nullable_ptr(cx, ty, def, substs) { |
0531ce1d | 743 | return FfiUnsafe { |
e1599b0c | 744 | ty, |
0531ce1d | 745 | reason: "enum has no representation hint", |
dc9dc135 XL |
746 | help: Some("consider adding a `#[repr(C)]`, \ |
747 | `#[repr(transparent)]`, or integer `#[repr(...)]` \ | |
748 | attribute to this enum"), | |
0531ce1d | 749 | }; |
9e0c209e | 750 | } |
8bb4bdeb | 751 | } |
c30ab7b3 | 752 | |
e74abb32 XL |
753 | if def.is_variant_list_non_exhaustive() && !def.did.is_local() { |
754 | return FfiUnsafe { | |
755 | ty, | |
756 | reason: "this enum is non-exhaustive", | |
757 | help: None, | |
758 | }; | |
759 | } | |
760 | ||
c30ab7b3 SL |
761 | // Check the contained variants. |
762 | for variant in &def.variants { | |
e74abb32 XL |
763 | let is_non_exhaustive = variant.is_field_list_non_exhaustive(); |
764 | if is_non_exhaustive && !variant.def_id.is_local() { | |
765 | return FfiUnsafe { | |
766 | ty, | |
767 | reason: "this enum has non-exhaustive variants", | |
768 | help: None, | |
769 | }; | |
770 | } | |
771 | ||
c30ab7b3 | 772 | for field in &variant.fields { |
dc9dc135 | 773 | let field_ty = cx.normalize_erasing_regions( |
0531ce1d XL |
774 | ParamEnv::reveal_all(), |
775 | field.ty(cx, substs), | |
abe05a73 | 776 | ); |
dc9dc135 XL |
777 | // repr(transparent) types are allowed to have arbitrary ZSTs, not |
778 | // just PhantomData -- skip checking all ZST fields. | |
779 | if def.repr.transparent() && is_zst(cx, field.did, field_ty) { | |
780 | continue; | |
781 | } | |
782 | let r = self.check_type_for_ffi(cache, field_ty); | |
c30ab7b3 SL |
783 | match r { |
784 | FfiSafe => {} | |
0531ce1d | 785 | FfiUnsafe { .. } => { |
c30ab7b3 SL |
786 | return r; |
787 | } | |
0531ce1d XL |
788 | FfiPhantom(..) => { |
789 | return FfiUnsafe { | |
e1599b0c | 790 | ty, |
0531ce1d XL |
791 | reason: "this enum contains a PhantomData field", |
792 | help: None, | |
793 | }; | |
c30ab7b3 SL |
794 | } |
795 | } | |
9e0c209e | 796 | } |
b039eaaf | 797 | } |
c30ab7b3 | 798 | FfiSafe |
b039eaaf SL |
799 | } |
800 | } | |
c30ab7b3 | 801 | } |
b039eaaf | 802 | |
b7449926 | 803 | ty::Char => FfiUnsafe { |
e1599b0c | 804 | ty, |
0531ce1d XL |
805 | reason: "the `char` type has no C equivalent", |
806 | help: Some("consider using `u32` or `libc::wchar_t` instead"), | |
807 | }, | |
b039eaaf | 808 | |
b7449926 | 809 | ty::Int(ast::IntTy::I128) | ty::Uint(ast::UintTy::U128) => FfiUnsafe { |
e1599b0c | 810 | ty, |
0531ce1d XL |
811 | reason: "128-bit integers don't currently have a known stable ABI", |
812 | help: None, | |
813 | }, | |
ea8adc8c | 814 | |
b039eaaf | 815 | // Primitive types with a stable representation. |
b7449926 | 816 | ty::Bool | ty::Int(..) | ty::Uint(..) | ty::Float(..) | ty::Never => FfiSafe, |
b039eaaf | 817 | |
b7449926 | 818 | ty::Slice(_) => FfiUnsafe { |
e1599b0c | 819 | ty, |
0531ce1d XL |
820 | reason: "slices have no C equivalent", |
821 | help: Some("consider using a raw pointer instead"), | |
822 | }, | |
823 | ||
b7449926 | 824 | ty::Dynamic(..) => FfiUnsafe { |
e1599b0c | 825 | ty, |
0531ce1d XL |
826 | reason: "trait objects have no C equivalent", |
827 | help: None, | |
828 | }, | |
829 | ||
b7449926 | 830 | ty::Str => FfiUnsafe { |
e1599b0c | 831 | ty, |
0531ce1d XL |
832 | reason: "string slices have no C equivalent", |
833 | help: Some("consider using `*const u8` and a length instead"), | |
834 | }, | |
835 | ||
b7449926 | 836 | ty::Tuple(..) => FfiUnsafe { |
e1599b0c | 837 | ty, |
0531ce1d XL |
838 | reason: "tuples have unspecified layout", |
839 | help: Some("consider using a struct instead"), | |
840 | }, | |
b039eaaf | 841 | |
b7449926 XL |
842 | ty::RawPtr(ty::TypeAndMut { ty, .. }) | |
843 | ty::Ref(_, ty, _) => self.check_type_for_ffi(cache, ty), | |
b039eaaf | 844 | |
60c5eb7d | 845 | ty::Array(inner_ty, _) => self.check_type_for_ffi(cache, inner_ty), |
b039eaaf | 846 | |
b7449926 | 847 | ty::FnPtr(sig) => { |
8bb4bdeb | 848 | match sig.abi() { |
c30ab7b3 | 849 | Abi::Rust | Abi::RustIntrinsic | Abi::PlatformIntrinsic | Abi::RustCall => { |
0531ce1d | 850 | return FfiUnsafe { |
e1599b0c | 851 | ty, |
0531ce1d | 852 | reason: "this function pointer has Rust-specific calling convention", |
8faf50e0 | 853 | help: Some("consider using an `extern fn(...) -> ...` \ |
0531ce1d XL |
854 | function pointer instead"), |
855 | } | |
b039eaaf SL |
856 | } |
857 | _ => {} | |
858 | } | |
859 | ||
8bb4bdeb | 860 | let sig = cx.erase_late_bound_regions(&sig); |
b7449926 | 861 | if !sig.output().is_unit() { |
476ff2be | 862 | let r = self.check_type_for_ffi(cache, sig.output()); |
5bcae85e SL |
863 | match r { |
864 | FfiSafe => {} | |
c30ab7b3 SL |
865 | _ => { |
866 | return r; | |
867 | } | |
b039eaaf SL |
868 | } |
869 | } | |
476ff2be | 870 | for arg in sig.inputs() { |
b039eaaf SL |
871 | let r = self.check_type_for_ffi(cache, arg); |
872 | match r { | |
873 | FfiSafe => {} | |
c30ab7b3 SL |
874 | _ => { |
875 | return r; | |
876 | } | |
b039eaaf SL |
877 | } |
878 | } | |
879 | FfiSafe | |
880 | } | |
881 | ||
b7449926 XL |
882 | ty::Foreign(..) => FfiSafe, |
883 | ||
884 | ty::Param(..) | | |
885 | ty::Infer(..) | | |
a1dfa0c6 | 886 | ty::Bound(..) | |
b7449926 XL |
887 | ty::Error | |
888 | ty::Closure(..) | | |
889 | ty::Generator(..) | | |
890 | ty::GeneratorWitness(..) | | |
a1dfa0c6 | 891 | ty::Placeholder(..) | |
0bf4aa26 | 892 | ty::UnnormalizedProjection(..) | |
b7449926 XL |
893 | ty::Projection(..) | |
894 | ty::Opaque(..) | | |
e1599b0c XL |
895 | ty::FnDef(..) => bug!("unexpected type in foreign function: {:?}", ty), |
896 | } | |
897 | } | |
898 | ||
899 | fn emit_ffi_unsafe_type_lint( | |
900 | &mut self, | |
901 | ty: Ty<'tcx>, | |
902 | sp: Span, | |
903 | note: &str, | |
904 | help: Option<&str>, | |
905 | ) { | |
906 | let mut diag = self.cx.struct_span_lint( | |
907 | IMPROPER_CTYPES, | |
908 | sp, | |
909 | &format!("`extern` block uses type `{}`, which is not FFI-safe", ty), | |
910 | ); | |
911 | diag.span_label(sp, "not FFI-safe"); | |
912 | if let Some(help) = help { | |
913 | diag.help(help); | |
914 | } | |
915 | diag.note(note); | |
e74abb32 | 916 | if let ty::Adt(def, _) = ty.kind { |
e1599b0c XL |
917 | if let Some(sp) = self.cx.tcx.hir().span_if_local(def.did) { |
918 | diag.span_note(sp, "type defined here"); | |
919 | } | |
920 | } | |
921 | diag.emit(); | |
922 | } | |
923 | ||
924 | fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool { | |
925 | use crate::rustc::ty::TypeFoldable; | |
926 | ||
927 | struct ProhibitOpaqueTypes<'tcx> { | |
928 | ty: Option<Ty<'tcx>>, | |
929 | }; | |
930 | ||
931 | impl<'tcx> ty::fold::TypeVisitor<'tcx> for ProhibitOpaqueTypes<'tcx> { | |
932 | fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { | |
e74abb32 | 933 | if let ty::Opaque(..) = ty.kind { |
e1599b0c XL |
934 | self.ty = Some(ty); |
935 | true | |
936 | } else { | |
937 | ty.super_visit_with(self) | |
938 | } | |
939 | } | |
940 | } | |
941 | ||
942 | let mut visitor = ProhibitOpaqueTypes { ty: None }; | |
943 | ty.visit_with(&mut visitor); | |
944 | if let Some(ty) = visitor.ty { | |
945 | self.emit_ffi_unsafe_type_lint( | |
946 | ty, | |
947 | sp, | |
948 | "opaque types have no C equivalent", | |
949 | None, | |
950 | ); | |
951 | true | |
952 | } else { | |
953 | false | |
b039eaaf SL |
954 | } |
955 | } | |
956 | ||
60c5eb7d | 957 | fn check_type_for_ffi_and_report_errors(&mut self, sp: Span, ty: Ty<'tcx>, is_static: bool) { |
e1599b0c XL |
958 | // We have to check for opaque types before `normalize_erasing_regions`, |
959 | // which will replace opaque types with their underlying concrete type. | |
960 | if self.check_for_opaque_ty(sp, ty) { | |
961 | // We've already emitted an error due to an opaque type. | |
962 | return; | |
963 | } | |
964 | ||
54a0048b SL |
965 | // it is only OK to use this function because extern fns cannot have |
966 | // any generic types right now: | |
0531ce1d | 967 | let ty = self.cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), ty); |
60c5eb7d XL |
968 | // C doesn't really support passing arrays by value. |
969 | // The only way to pass an array by value is through a struct. | |
970 | // So we first test that the top level isn't an array, | |
971 | // and then recursively check the types inside. | |
972 | if !is_static && self.check_for_array_ty(sp, ty) { | |
973 | return; | |
974 | } | |
b039eaaf | 975 | |
0bf4aa26 | 976 | match self.check_type_for_ffi(&mut FxHashSet::default(), ty) { |
b039eaaf | 977 | FfiResult::FfiSafe => {} |
0531ce1d | 978 | FfiResult::FfiPhantom(ty) => { |
e1599b0c | 979 | self.emit_ffi_unsafe_type_lint(ty, sp, "composed only of `PhantomData`", None); |
9e0c209e | 980 | } |
e1599b0c XL |
981 | FfiResult::FfiUnsafe { ty, reason, help } => { |
982 | self.emit_ffi_unsafe_type_lint(ty, sp, reason, help); | |
b039eaaf SL |
983 | } |
984 | } | |
985 | } | |
b039eaaf | 986 | |
532ac7d7 | 987 | fn check_foreign_fn(&mut self, id: hir::HirId, decl: &hir::FnDecl) { |
416331ca | 988 | let def_id = self.cx.tcx.hir().local_def_id(id); |
041b39d2 | 989 | let sig = self.cx.tcx.fn_sig(def_id); |
54a0048b SL |
990 | let sig = self.cx.tcx.erase_late_bound_regions(&sig); |
991 | ||
e74abb32 | 992 | for (input_ty, input_hir) in sig.inputs().iter().zip(&decl.inputs) { |
60c5eb7d | 993 | self.check_type_for_ffi_and_report_errors(input_hir.span, input_ty, false); |
54a0048b SL |
994 | } |
995 | ||
996 | if let hir::Return(ref ret_hir) = decl.output { | |
476ff2be | 997 | let ret_ty = sig.output(); |
b7449926 | 998 | if !ret_ty.is_unit() { |
60c5eb7d | 999 | self.check_type_for_ffi_and_report_errors(ret_hir.span, ret_ty, false); |
b039eaaf | 1000 | } |
b039eaaf SL |
1001 | } |
1002 | } | |
54a0048b | 1003 | |
532ac7d7 | 1004 | fn check_foreign_static(&mut self, id: hir::HirId, span: Span) { |
416331ca | 1005 | let def_id = self.cx.tcx.hir().local_def_id(id); |
7cac9316 | 1006 | let ty = self.cx.tcx.type_of(def_id); |
60c5eb7d | 1007 | self.check_type_for_ffi_and_report_errors(span, ty, true); |
54a0048b | 1008 | } |
b039eaaf SL |
1009 | } |
1010 | ||
476ff2be | 1011 | impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImproperCTypes { |
9fa01778 | 1012 | fn check_foreign_item(&mut self, cx: &LateContext<'_, '_>, it: &hir::ForeignItem) { |
a1dfa0c6 | 1013 | let mut vis = ImproperCTypesVisitor { cx }; |
dc9dc135 | 1014 | let abi = cx.tcx.hir().get_foreign_abi(it.hir_id); |
e1599b0c XL |
1015 | if let Abi::Rust | Abi::RustCall | Abi::RustIntrinsic | Abi::PlatformIntrinsic = abi { |
1016 | // Don't worry about types in internal ABIs. | |
1017 | } else { | |
e74abb32 | 1018 | match it.kind { |
b7449926 | 1019 | hir::ForeignItemKind::Fn(ref decl, _, _) => { |
532ac7d7 | 1020 | vis.check_foreign_fn(it.hir_id, decl); |
b7449926 XL |
1021 | } |
1022 | hir::ForeignItemKind::Static(ref ty, _) => { | |
532ac7d7 | 1023 | vis.check_foreign_static(it.hir_id, ty.span); |
b039eaaf | 1024 | } |
b7449926 | 1025 | hir::ForeignItemKind::Type => () |
b039eaaf | 1026 | } |
b039eaaf SL |
1027 | } |
1028 | } | |
1029 | } | |
5bcae85e | 1030 | |
532ac7d7 | 1031 | declare_lint_pass!(VariantSizeDifferences => [VARIANT_SIZE_DIFFERENCES]); |
5bcae85e | 1032 | |
476ff2be | 1033 | impl<'a, 'tcx> LateLintPass<'a, 'tcx> for VariantSizeDifferences { |
9fa01778 | 1034 | fn check_item(&mut self, cx: &LateContext<'_, '_>, it: &hir::Item) { |
e74abb32 | 1035 | if let hir::ItemKind::Enum(ref enum_definition, _) = it.kind { |
416331ca | 1036 | let item_def_id = cx.tcx.hir().local_def_id(it.hir_id); |
8faf50e0 XL |
1037 | let t = cx.tcx.type_of(item_def_id); |
1038 | let ty = cx.tcx.erase_regions(&t); | |
532ac7d7 XL |
1039 | let layout = match cx.layout_of(ty) { |
1040 | Ok(layout) => layout, | |
8faf50e0 XL |
1041 | Err(ty::layout::LayoutError::Unknown(_)) => return, |
1042 | Err(err @ ty::layout::LayoutError::SizeOverflow(_)) => { | |
1043 | bug!("failed to get layout for `{}`: {}", t, err); | |
1044 | } | |
532ac7d7 XL |
1045 | }; |
1046 | let (variants, tag) = match layout.variants { | |
1047 | layout::Variants::Multiple { | |
1048 | discr_kind: layout::DiscriminantKind::Tag, | |
1049 | ref discr, | |
1050 | ref variants, | |
48663c56 | 1051 | .. |
532ac7d7 XL |
1052 | } => (variants, discr), |
1053 | _ => return, | |
1054 | }; | |
1055 | ||
1056 | let discr_size = tag.value.size(&cx.tcx).bytes(); | |
1057 | ||
1058 | debug!("enum `{}` is {} bytes large with layout:\n{:#?}", | |
1059 | t, layout.size.bytes(), layout); | |
1060 | ||
1061 | let (largest, slargest, largest_index) = enum_definition.variants | |
1062 | .iter() | |
1063 | .zip(variants) | |
1064 | .map(|(variant, variant_layout)| { | |
1065 | // Subtract the size of the enum discriminant. | |
1066 | let bytes = variant_layout.size.bytes().saturating_sub(discr_size); | |
1067 | ||
1068 | debug!("- variant `{}` is {} bytes large", | |
e1599b0c | 1069 | variant.ident, |
532ac7d7 XL |
1070 | bytes); |
1071 | bytes | |
1072 | }) | |
1073 | .enumerate() | |
1074 | .fold((0, 0, 0), |(l, s, li), (idx, size)| if size > l { | |
1075 | (size, l, idx) | |
1076 | } else if size > s { | |
1077 | (l, size, li) | |
1078 | } else { | |
1079 | (l, s, li) | |
1080 | }); | |
1081 | ||
1082 | // We only warn if the largest variant is at least thrice as large as | |
1083 | // the second-largest. | |
1084 | if largest > slargest * 3 && slargest > 0 { | |
1085 | cx.span_lint(VARIANT_SIZE_DIFFERENCES, | |
1086 | enum_definition.variants[largest_index].span, | |
1087 | &format!("enum variant is more than three times \ | |
1088 | larger ({} bytes) than the next largest", | |
1089 | largest)); | |
5bcae85e SL |
1090 | } |
1091 | } | |
1092 | } | |
1093 | } |