]>
Commit | Line | Data |
---|---|---|
c34b1796 AL |
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 | ||
11 | //! Lints in the Rust compiler. | |
12 | //! | |
13 | //! This contains lints which can feasibly be implemented as their own | |
14 | //! AST visitor. Also see `rustc::lint::builtin`, which contains the | |
15 | //! definitions of lints that are emitted directly inside the main | |
16 | //! compiler. | |
17 | //! | |
18 | //! To add a new lint to rustc, declare it here using `declare_lint!()`. | |
19 | //! Then add code to emit the new lint in the appropriate circumstances. | |
20 | //! You can do that in an existing `LintPass` if it makes sense, or in a | |
21 | //! new `LintPass`, or using `Session::add_lint` elsewhere in the | |
22 | //! compiler. Only do the latter if the check can't be written cleanly as a | |
23 | //! `LintPass` (also, note that such lints will need to be defined in | |
24 | //! `rustc::lint::builtin`, not here). | |
25 | //! | |
26 | //! If you define a new `LintPass`, you will also need to add it to the | |
27 | //! `add_builtin!` or `add_builtin_with_new!` invocation in `lib.rs`. | |
28 | //! Use the former for unit-like structs and the latter for structs with | |
29 | //! a `pub fn new()`. | |
30 | ||
31 | use metadata::{csearch, decoder}; | |
32 | use middle::def::*; | |
33 | use middle::subst::Substs; | |
34 | use middle::ty::{self, Ty}; | |
35 | use middle::{def, pat_util, stability}; | |
62682a34 | 36 | use middle::const_eval::{eval_const_expr_partial, ConstVal}; |
c34b1796 | 37 | use middle::cfg; |
62682a34 | 38 | use rustc::ast_map; |
c34b1796 AL |
39 | use util::nodemap::{FnvHashMap, NodeSet}; |
40 | use lint::{Level, Context, LintPass, LintArray, Lint}; | |
41 | ||
42 | use std::collections::{HashSet, BitSet}; | |
43 | use std::collections::hash_map::Entry::{Occupied, Vacant}; | |
44 | use std::{cmp, slice}; | |
45 | use std::{i8, i16, i32, i64, u8, u16, u32, u64, f32, f64}; | |
46 | ||
62682a34 | 47 | use syntax::{abi, ast}; |
c34b1796 AL |
48 | use syntax::ast_util::{self, is_shift_binop, local_def}; |
49 | use syntax::attr::{self, AttrMetaMethods}; | |
50 | use syntax::codemap::{self, Span}; | |
51 | use syntax::feature_gate::{KNOWN_ATTRIBUTES, AttributeType}; | |
52 | use syntax::parse::token; | |
53 | use syntax::ast::{TyIs, TyUs, TyI8, TyU8, TyI16, TyU16, TyI32, TyU32, TyI64, TyU64}; | |
54 | use syntax::ptr::P; | |
55 | use syntax::visit::{self, Visitor}; | |
56 | ||
57 | // hardwired lints from librustc | |
58 | pub use lint::builtin::*; | |
59 | ||
60 | declare_lint! { | |
61 | WHILE_TRUE, | |
62 | Warn, | |
63 | "suggest using `loop { }` instead of `while true { }`" | |
64 | } | |
65 | ||
66 | #[derive(Copy, Clone)] | |
67 | pub struct WhileTrue; | |
68 | ||
69 | impl LintPass for WhileTrue { | |
70 | fn get_lints(&self) -> LintArray { | |
71 | lint_array!(WHILE_TRUE) | |
72 | } | |
73 | ||
74 | fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { | |
75 | if let ast::ExprWhile(ref cond, _, _) = e.node { | |
76 | if let ast::ExprLit(ref lit) = cond.node { | |
77 | if let ast::LitBool(true) = lit.node { | |
78 | cx.span_lint(WHILE_TRUE, e.span, | |
79 | "denote infinite loops with loop { ... }"); | |
80 | } | |
81 | } | |
82 | } | |
83 | } | |
84 | } | |
85 | ||
86 | declare_lint! { | |
87 | UNSIGNED_NEGATION, | |
88 | Warn, | |
89 | "using an unary minus operator on unsigned type" | |
90 | } | |
91 | ||
92 | declare_lint! { | |
93 | UNUSED_COMPARISONS, | |
94 | Warn, | |
95 | "comparisons made useless by limits of the types involved" | |
96 | } | |
97 | ||
98 | declare_lint! { | |
99 | OVERFLOWING_LITERALS, | |
100 | Warn, | |
101 | "literal out of range for its type" | |
102 | } | |
103 | ||
104 | declare_lint! { | |
105 | EXCEEDING_BITSHIFTS, | |
106 | Deny, | |
107 | "shift exceeds the type's number of bits" | |
108 | } | |
109 | ||
110 | #[derive(Copy, Clone)] | |
111 | pub struct TypeLimits { | |
112 | /// Id of the last visited negated expression | |
113 | negated_expr_id: ast::NodeId, | |
114 | } | |
115 | ||
116 | impl TypeLimits { | |
117 | pub fn new() -> TypeLimits { | |
118 | TypeLimits { | |
119 | negated_expr_id: !0, | |
120 | } | |
121 | } | |
122 | } | |
123 | ||
124 | impl LintPass for TypeLimits { | |
125 | fn get_lints(&self) -> LintArray { | |
126 | lint_array!(UNSIGNED_NEGATION, UNUSED_COMPARISONS, OVERFLOWING_LITERALS, | |
127 | EXCEEDING_BITSHIFTS) | |
128 | } | |
129 | ||
130 | fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { | |
131 | match e.node { | |
132 | ast::ExprUnary(ast::UnNeg, ref expr) => { | |
133 | match expr.node { | |
134 | ast::ExprLit(ref lit) => { | |
135 | match lit.node { | |
136 | ast::LitInt(_, ast::UnsignedIntLit(_)) => { | |
137 | cx.span_lint(UNSIGNED_NEGATION, e.span, | |
138 | "negation of unsigned int literal may \ | |
139 | be unintentional"); | |
140 | }, | |
141 | _ => () | |
142 | } | |
143 | }, | |
144 | _ => { | |
145 | let t = ty::expr_ty(cx.tcx, &**expr); | |
146 | match t.sty { | |
62682a34 | 147 | ty::TyUint(_) => { |
c34b1796 AL |
148 | cx.span_lint(UNSIGNED_NEGATION, e.span, |
149 | "negation of unsigned int variable may \ | |
150 | be unintentional"); | |
151 | }, | |
152 | _ => () | |
153 | } | |
154 | } | |
155 | }; | |
156 | // propagate negation, if the negation itself isn't negated | |
157 | if self.negated_expr_id != e.id { | |
158 | self.negated_expr_id = expr.id; | |
159 | } | |
160 | }, | |
161 | ast::ExprParen(ref expr) if self.negated_expr_id == e.id => { | |
162 | self.negated_expr_id = expr.id; | |
163 | }, | |
164 | ast::ExprBinary(binop, ref l, ref r) => { | |
165 | if is_comparison(binop) && !check_limits(cx.tcx, binop, &**l, &**r) { | |
166 | cx.span_lint(UNUSED_COMPARISONS, e.span, | |
167 | "comparison is useless due to type limits"); | |
168 | } | |
169 | ||
170 | if is_shift_binop(binop.node) { | |
171 | let opt_ty_bits = match ty::expr_ty(cx.tcx, &**l).sty { | |
62682a34 SL |
172 | ty::TyInt(t) => Some(int_ty_bits(t, cx.sess().target.int_type)), |
173 | ty::TyUint(t) => Some(uint_ty_bits(t, cx.sess().target.uint_type)), | |
c34b1796 AL |
174 | _ => None |
175 | }; | |
176 | ||
177 | if let Some(bits) = opt_ty_bits { | |
178 | let exceeding = if let ast::ExprLit(ref lit) = r.node { | |
179 | if let ast::LitInt(shift, _) = lit.node { shift >= bits } | |
180 | else { false } | |
181 | } else { | |
182 | match eval_const_expr_partial(cx.tcx, &**r, Some(cx.tcx.types.usize)) { | |
62682a34 SL |
183 | Ok(ConstVal::Int(shift)) => { shift as u64 >= bits }, |
184 | Ok(ConstVal::Uint(shift)) => { shift >= bits }, | |
c34b1796 AL |
185 | _ => { false } |
186 | } | |
187 | }; | |
188 | if exceeding { | |
189 | cx.span_lint(EXCEEDING_BITSHIFTS, e.span, | |
190 | "bitshift exceeds the type's number of bits"); | |
191 | } | |
192 | }; | |
193 | } | |
194 | }, | |
195 | ast::ExprLit(ref lit) => { | |
196 | match ty::expr_ty(cx.tcx, e).sty { | |
62682a34 | 197 | ty::TyInt(t) => { |
c34b1796 AL |
198 | match lit.node { |
199 | ast::LitInt(v, ast::SignedIntLit(_, ast::Plus)) | | |
200 | ast::LitInt(v, ast::UnsuffixedIntLit(ast::Plus)) => { | |
201 | let int_type = if let ast::TyIs = t { | |
202 | cx.sess().target.int_type | |
203 | } else { | |
204 | t | |
205 | }; | |
62682a34 | 206 | let (_, max) = int_ty_range(int_type); |
c34b1796 AL |
207 | let negative = self.negated_expr_id == e.id; |
208 | ||
62682a34 SL |
209 | // Detect literal value out of range [min, max] inclusive |
210 | // avoiding use of -min to prevent overflow/panic | |
211 | if (negative && v > max as u64 + 1) || | |
212 | (!negative && v > max as u64) { | |
c34b1796 AL |
213 | cx.span_lint(OVERFLOWING_LITERALS, e.span, |
214 | &*format!("literal out of range for {:?}", t)); | |
215 | return; | |
216 | } | |
217 | } | |
218 | _ => panic!() | |
219 | }; | |
220 | }, | |
62682a34 | 221 | ty::TyUint(t) => { |
c34b1796 AL |
222 | let uint_type = if let ast::TyUs = t { |
223 | cx.sess().target.uint_type | |
224 | } else { | |
225 | t | |
226 | }; | |
227 | let (min, max) = uint_ty_range(uint_type); | |
228 | let lit_val: u64 = match lit.node { | |
229 | ast::LitByte(_v) => return, // _v is u8, within range by definition | |
230 | ast::LitInt(v, _) => v, | |
231 | _ => panic!() | |
232 | }; | |
233 | if lit_val < min || lit_val > max { | |
234 | cx.span_lint(OVERFLOWING_LITERALS, e.span, | |
235 | &*format!("literal out of range for {:?}", t)); | |
236 | } | |
237 | }, | |
62682a34 | 238 | ty::TyFloat(t) => { |
c34b1796 AL |
239 | let (min, max) = float_ty_range(t); |
240 | let lit_val: f64 = match lit.node { | |
241 | ast::LitFloat(ref v, _) | | |
242 | ast::LitFloatUnsuffixed(ref v) => { | |
243 | match v.parse() { | |
244 | Ok(f) => f, | |
245 | Err(_) => return | |
246 | } | |
247 | } | |
248 | _ => panic!() | |
249 | }; | |
250 | if lit_val < min || lit_val > max { | |
251 | cx.span_lint(OVERFLOWING_LITERALS, e.span, | |
252 | &*format!("literal out of range for {:?}", t)); | |
253 | } | |
254 | }, | |
255 | _ => () | |
256 | }; | |
257 | }, | |
258 | _ => () | |
259 | }; | |
260 | ||
261 | fn is_valid<T:cmp::PartialOrd>(binop: ast::BinOp, v: T, | |
262 | min: T, max: T) -> bool { | |
263 | match binop.node { | |
264 | ast::BiLt => v > min && v <= max, | |
265 | ast::BiLe => v >= min && v < max, | |
266 | ast::BiGt => v >= min && v < max, | |
267 | ast::BiGe => v > min && v <= max, | |
268 | ast::BiEq | ast::BiNe => v >= min && v <= max, | |
269 | _ => panic!() | |
270 | } | |
271 | } | |
272 | ||
273 | fn rev_binop(binop: ast::BinOp) -> ast::BinOp { | |
274 | codemap::respan(binop.span, match binop.node { | |
275 | ast::BiLt => ast::BiGt, | |
276 | ast::BiLe => ast::BiGe, | |
277 | ast::BiGt => ast::BiLt, | |
278 | ast::BiGe => ast::BiLe, | |
279 | _ => return binop | |
280 | }) | |
281 | } | |
282 | ||
283 | // for isize & usize, be conservative with the warnings, so that the | |
284 | // warnings are consistent between 32- and 64-bit platforms | |
285 | fn int_ty_range(int_ty: ast::IntTy) -> (i64, i64) { | |
286 | match int_ty { | |
287 | ast::TyIs => (i64::MIN, i64::MAX), | |
288 | ast::TyI8 => (i8::MIN as i64, i8::MAX as i64), | |
289 | ast::TyI16 => (i16::MIN as i64, i16::MAX as i64), | |
290 | ast::TyI32 => (i32::MIN as i64, i32::MAX as i64), | |
291 | ast::TyI64 => (i64::MIN, i64::MAX) | |
292 | } | |
293 | } | |
294 | ||
295 | fn uint_ty_range(uint_ty: ast::UintTy) -> (u64, u64) { | |
296 | match uint_ty { | |
297 | ast::TyUs => (u64::MIN, u64::MAX), | |
298 | ast::TyU8 => (u8::MIN as u64, u8::MAX as u64), | |
299 | ast::TyU16 => (u16::MIN as u64, u16::MAX as u64), | |
300 | ast::TyU32 => (u32::MIN as u64, u32::MAX as u64), | |
301 | ast::TyU64 => (u64::MIN, u64::MAX) | |
302 | } | |
303 | } | |
304 | ||
305 | fn float_ty_range(float_ty: ast::FloatTy) -> (f64, f64) { | |
306 | match float_ty { | |
307 | ast::TyF32 => (f32::MIN as f64, f32::MAX as f64), | |
308 | ast::TyF64 => (f64::MIN, f64::MAX) | |
309 | } | |
310 | } | |
311 | ||
312 | fn int_ty_bits(int_ty: ast::IntTy, target_int_ty: ast::IntTy) -> u64 { | |
313 | match int_ty { | |
314 | ast::TyIs => int_ty_bits(target_int_ty, target_int_ty), | |
315 | ast::TyI8 => i8::BITS as u64, | |
316 | ast::TyI16 => i16::BITS as u64, | |
317 | ast::TyI32 => i32::BITS as u64, | |
318 | ast::TyI64 => i64::BITS as u64 | |
319 | } | |
320 | } | |
321 | ||
322 | fn uint_ty_bits(uint_ty: ast::UintTy, target_uint_ty: ast::UintTy) -> u64 { | |
323 | match uint_ty { | |
324 | ast::TyUs => uint_ty_bits(target_uint_ty, target_uint_ty), | |
325 | ast::TyU8 => u8::BITS as u64, | |
326 | ast::TyU16 => u16::BITS as u64, | |
327 | ast::TyU32 => u32::BITS as u64, | |
328 | ast::TyU64 => u64::BITS as u64 | |
329 | } | |
330 | } | |
331 | ||
332 | fn check_limits(tcx: &ty::ctxt, binop: ast::BinOp, | |
333 | l: &ast::Expr, r: &ast::Expr) -> bool { | |
334 | let (lit, expr, swap) = match (&l.node, &r.node) { | |
335 | (&ast::ExprLit(_), _) => (l, r, true), | |
336 | (_, &ast::ExprLit(_)) => (r, l, false), | |
337 | _ => return true | |
338 | }; | |
339 | // Normalize the binop so that the literal is always on the RHS in | |
340 | // the comparison | |
341 | let norm_binop = if swap { | |
342 | rev_binop(binop) | |
343 | } else { | |
344 | binop | |
345 | }; | |
346 | match ty::expr_ty(tcx, expr).sty { | |
62682a34 | 347 | ty::TyInt(int_ty) => { |
c34b1796 AL |
348 | let (min, max) = int_ty_range(int_ty); |
349 | let lit_val: i64 = match lit.node { | |
350 | ast::ExprLit(ref li) => match li.node { | |
351 | ast::LitInt(v, ast::SignedIntLit(_, ast::Plus)) | | |
352 | ast::LitInt(v, ast::UnsuffixedIntLit(ast::Plus)) => v as i64, | |
353 | ast::LitInt(v, ast::SignedIntLit(_, ast::Minus)) | | |
354 | ast::LitInt(v, ast::UnsuffixedIntLit(ast::Minus)) => -(v as i64), | |
355 | _ => return true | |
356 | }, | |
357 | _ => panic!() | |
358 | }; | |
359 | is_valid(norm_binop, lit_val, min, max) | |
360 | } | |
62682a34 | 361 | ty::TyUint(uint_ty) => { |
c34b1796 AL |
362 | let (min, max): (u64, u64) = uint_ty_range(uint_ty); |
363 | let lit_val: u64 = match lit.node { | |
364 | ast::ExprLit(ref li) => match li.node { | |
365 | ast::LitInt(v, _) => v, | |
366 | _ => return true | |
367 | }, | |
368 | _ => panic!() | |
369 | }; | |
370 | is_valid(norm_binop, lit_val, min, max) | |
371 | } | |
372 | _ => true | |
373 | } | |
374 | } | |
375 | ||
376 | fn is_comparison(binop: ast::BinOp) -> bool { | |
377 | match binop.node { | |
378 | ast::BiEq | ast::BiLt | ast::BiLe | | |
379 | ast::BiNe | ast::BiGe | ast::BiGt => true, | |
380 | _ => false | |
381 | } | |
382 | } | |
383 | } | |
384 | } | |
385 | ||
386 | declare_lint! { | |
387 | IMPROPER_CTYPES, | |
388 | Warn, | |
389 | "proper use of libc types in foreign modules" | |
390 | } | |
391 | ||
392 | struct ImproperCTypesVisitor<'a, 'tcx: 'a> { | |
393 | cx: &'a Context<'a, 'tcx> | |
394 | } | |
395 | ||
396 | impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { | |
397 | fn check_def(&mut self, sp: Span, id: ast::NodeId) { | |
398 | match self.cx.tcx.def_map.borrow().get(&id).unwrap().full_def() { | |
399 | def::DefPrimTy(ast::TyInt(ast::TyIs)) => { | |
400 | self.cx.span_lint(IMPROPER_CTYPES, sp, | |
401 | "found rust type `isize` in foreign module, while \ | |
402 | libc::c_int or libc::c_long should be used"); | |
403 | } | |
404 | def::DefPrimTy(ast::TyUint(ast::TyUs)) => { | |
405 | self.cx.span_lint(IMPROPER_CTYPES, sp, | |
406 | "found rust type `usize` in foreign module, while \ | |
407 | libc::c_uint or libc::c_ulong should be used"); | |
408 | } | |
409 | def::DefTy(..) => { | |
410 | let tty = match self.cx.tcx.ast_ty_to_ty_cache.borrow().get(&id) { | |
411 | Some(&t) => t, | |
412 | None => panic!("ast_ty_to_ty_cache was incomplete after typeck!") | |
413 | }; | |
414 | ||
415 | if !ty::is_ffi_safe(self.cx.tcx, tty) { | |
416 | self.cx.span_lint(IMPROPER_CTYPES, sp, | |
417 | "found type without foreign-function-safe \ | |
418 | representation annotation in foreign module, consider \ | |
419 | adding a #[repr(...)] attribute to the type"); | |
420 | } | |
421 | } | |
422 | _ => () | |
423 | } | |
424 | } | |
425 | } | |
426 | ||
427 | impl<'a, 'tcx, 'v> Visitor<'v> for ImproperCTypesVisitor<'a, 'tcx> { | |
428 | fn visit_ty(&mut self, ty: &ast::Ty) { | |
429 | if let ast::TyPath(..) = ty.node { | |
430 | self.check_def(ty.span, ty.id); | |
431 | } | |
432 | visit::walk_ty(self, ty); | |
433 | } | |
434 | } | |
435 | ||
436 | #[derive(Copy, Clone)] | |
437 | pub struct ImproperCTypes; | |
438 | ||
439 | impl LintPass for ImproperCTypes { | |
440 | fn get_lints(&self) -> LintArray { | |
441 | lint_array!(IMPROPER_CTYPES) | |
442 | } | |
443 | ||
444 | fn check_item(&mut self, cx: &Context, it: &ast::Item) { | |
445 | fn check_ty(cx: &Context, ty: &ast::Ty) { | |
446 | let mut vis = ImproperCTypesVisitor { cx: cx }; | |
447 | vis.visit_ty(ty); | |
448 | } | |
449 | ||
450 | fn check_foreign_fn(cx: &Context, decl: &ast::FnDecl) { | |
451 | for input in &decl.inputs { | |
452 | check_ty(cx, &*input.ty); | |
453 | } | |
454 | if let ast::Return(ref ret_ty) = decl.output { | |
455 | check_ty(cx, &**ret_ty); | |
456 | } | |
457 | } | |
458 | ||
459 | match it.node { | |
460 | ast::ItemForeignMod(ref nmod) if nmod.abi != abi::RustIntrinsic => { | |
461 | for ni in &nmod.items { | |
462 | match ni.node { | |
463 | ast::ForeignItemFn(ref decl, _) => check_foreign_fn(cx, &**decl), | |
464 | ast::ForeignItemStatic(ref t, _) => check_ty(cx, &**t) | |
465 | } | |
466 | } | |
467 | } | |
468 | _ => (), | |
469 | } | |
470 | } | |
471 | } | |
472 | ||
473 | declare_lint! { | |
474 | BOX_POINTERS, | |
475 | Allow, | |
476 | "use of owned (Box type) heap memory" | |
477 | } | |
478 | ||
479 | #[derive(Copy, Clone)] | |
480 | pub struct BoxPointers; | |
481 | ||
482 | impl BoxPointers { | |
483 | fn check_heap_type<'a, 'tcx>(&self, cx: &Context<'a, 'tcx>, | |
484 | span: Span, ty: Ty<'tcx>) { | |
485 | let mut n_uniq: usize = 0; | |
486 | ty::fold_ty(cx.tcx, ty, |t| { | |
487 | match t.sty { | |
62682a34 | 488 | ty::TyBox(_) => { |
c34b1796 AL |
489 | n_uniq += 1; |
490 | } | |
491 | _ => () | |
492 | }; | |
493 | t | |
494 | }); | |
495 | ||
496 | if n_uniq > 0 { | |
62682a34 | 497 | let m = format!("type uses owned (Box type) pointers: {}", ty); |
c34b1796 AL |
498 | cx.span_lint(BOX_POINTERS, span, &m[..]); |
499 | } | |
500 | } | |
501 | } | |
502 | ||
503 | impl LintPass for BoxPointers { | |
504 | fn get_lints(&self) -> LintArray { | |
505 | lint_array!(BOX_POINTERS) | |
506 | } | |
507 | ||
508 | fn check_item(&mut self, cx: &Context, it: &ast::Item) { | |
509 | match it.node { | |
510 | ast::ItemFn(..) | | |
511 | ast::ItemTy(..) | | |
512 | ast::ItemEnum(..) | | |
513 | ast::ItemStruct(..) => | |
514 | self.check_heap_type(cx, it.span, | |
515 | ty::node_id_to_type(cx.tcx, it.id)), | |
516 | _ => () | |
517 | } | |
518 | ||
519 | // If it's a struct, we also have to check the fields' types | |
520 | match it.node { | |
521 | ast::ItemStruct(ref struct_def, _) => { | |
522 | for struct_field in &struct_def.fields { | |
523 | self.check_heap_type(cx, struct_field.span, | |
524 | ty::node_id_to_type(cx.tcx, struct_field.node.id)); | |
525 | } | |
526 | } | |
527 | _ => () | |
528 | } | |
529 | } | |
530 | ||
531 | fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { | |
532 | let ty = ty::expr_ty(cx.tcx, e); | |
533 | self.check_heap_type(cx, e.span, ty); | |
534 | } | |
535 | } | |
536 | ||
537 | declare_lint! { | |
538 | RAW_POINTER_DERIVE, | |
539 | Warn, | |
540 | "uses of #[derive] with raw pointers are rarely correct" | |
541 | } | |
542 | ||
543 | struct RawPtrDeriveVisitor<'a, 'tcx: 'a> { | |
544 | cx: &'a Context<'a, 'tcx> | |
545 | } | |
546 | ||
547 | impl<'a, 'tcx, 'v> Visitor<'v> for RawPtrDeriveVisitor<'a, 'tcx> { | |
548 | fn visit_ty(&mut self, ty: &ast::Ty) { | |
549 | const MSG: &'static str = "use of `#[derive]` with a raw pointer"; | |
550 | if let ast::TyPtr(..) = ty.node { | |
551 | self.cx.span_lint(RAW_POINTER_DERIVE, ty.span, MSG); | |
552 | } | |
553 | visit::walk_ty(self, ty); | |
554 | } | |
555 | // explicit override to a no-op to reduce code bloat | |
556 | fn visit_expr(&mut self, _: &ast::Expr) {} | |
557 | fn visit_block(&mut self, _: &ast::Block) {} | |
558 | } | |
559 | ||
560 | pub struct RawPointerDerive { | |
561 | checked_raw_pointers: NodeSet, | |
562 | } | |
563 | ||
564 | impl RawPointerDerive { | |
565 | pub fn new() -> RawPointerDerive { | |
566 | RawPointerDerive { | |
567 | checked_raw_pointers: NodeSet(), | |
568 | } | |
569 | } | |
570 | } | |
571 | ||
572 | impl LintPass for RawPointerDerive { | |
573 | fn get_lints(&self) -> LintArray { | |
574 | lint_array!(RAW_POINTER_DERIVE) | |
575 | } | |
576 | ||
577 | fn check_item(&mut self, cx: &Context, item: &ast::Item) { | |
578 | if !attr::contains_name(&item.attrs, "automatically_derived") { | |
579 | return; | |
580 | } | |
581 | let did = match item.node { | |
582 | ast::ItemImpl(_, _, _, ref t_ref_opt, _, _) => { | |
583 | // Deriving the Copy trait does not cause a warning | |
584 | if let &Some(ref trait_ref) = t_ref_opt { | |
585 | let def_id = ty::trait_ref_to_def_id(cx.tcx, trait_ref); | |
586 | if Some(def_id) == cx.tcx.lang_items.copy_trait() { | |
587 | return; | |
588 | } | |
589 | } | |
590 | ||
591 | match ty::node_id_to_type(cx.tcx, item.id).sty { | |
62682a34 SL |
592 | ty::TyEnum(did, _) => did, |
593 | ty::TyStruct(did, _) => did, | |
c34b1796 AL |
594 | _ => return, |
595 | } | |
596 | } | |
597 | _ => return, | |
598 | }; | |
599 | if !ast_util::is_local(did) { | |
600 | return; | |
601 | } | |
602 | let item = match cx.tcx.map.find(did.node) { | |
603 | Some(ast_map::NodeItem(item)) => item, | |
604 | _ => return, | |
605 | }; | |
606 | if !self.checked_raw_pointers.insert(item.id) { | |
607 | return; | |
608 | } | |
609 | match item.node { | |
610 | ast::ItemStruct(..) | ast::ItemEnum(..) => { | |
611 | let mut visitor = RawPtrDeriveVisitor { cx: cx }; | |
612 | visit::walk_item(&mut visitor, &*item); | |
613 | } | |
614 | _ => {} | |
615 | } | |
616 | } | |
617 | } | |
618 | ||
619 | declare_lint! { | |
620 | UNUSED_ATTRIBUTES, | |
621 | Warn, | |
622 | "detects attributes that were not used by the compiler" | |
623 | } | |
624 | ||
625 | #[derive(Copy, Clone)] | |
626 | pub struct UnusedAttributes; | |
627 | ||
628 | impl LintPass for UnusedAttributes { | |
629 | fn get_lints(&self) -> LintArray { | |
630 | lint_array!(UNUSED_ATTRIBUTES) | |
631 | } | |
632 | ||
633 | fn check_attribute(&mut self, cx: &Context, attr: &ast::Attribute) { | |
634 | // Note that check_name() marks the attribute as used if it matches. | |
635 | for &(ref name, ty) in KNOWN_ATTRIBUTES { | |
636 | match ty { | |
637 | AttributeType::Whitelisted | |
638 | | AttributeType::Gated(_, _) if attr.check_name(name) => { | |
639 | break; | |
640 | }, | |
641 | _ => () | |
642 | } | |
643 | } | |
644 | ||
62682a34 SL |
645 | let plugin_attributes = cx.sess().plugin_attributes.borrow_mut(); |
646 | for &(ref name, ty) in plugin_attributes.iter() { | |
647 | if ty == AttributeType::Whitelisted && attr.check_name(&*name) { | |
648 | break; | |
649 | } | |
650 | } | |
651 | ||
c34b1796 AL |
652 | if !attr::is_used(attr) { |
653 | cx.span_lint(UNUSED_ATTRIBUTES, attr.span, "unused attribute"); | |
62682a34 SL |
654 | // Is it a builtin attribute that must be used at the crate level? |
655 | let known_crate = KNOWN_ATTRIBUTES.contains(&(&attr.name(), | |
656 | AttributeType::CrateLevel)); | |
657 | // Has a plugin registered this attribute as one which must be used at | |
658 | // the crate level? | |
659 | let plugin_crate = plugin_attributes.iter() | |
660 | .find(|&&(ref x, t)| { | |
661 | &*attr.name() == &*x && | |
662 | AttributeType::CrateLevel == t | |
663 | }).is_some(); | |
664 | if known_crate || plugin_crate { | |
c34b1796 AL |
665 | let msg = match attr.node.style { |
666 | ast::AttrOuter => "crate-level attribute should be an inner \ | |
667 | attribute: add an exclamation mark: #![foo]", | |
668 | ast::AttrInner => "crate-level attribute should be in the \ | |
669 | root module", | |
670 | }; | |
671 | cx.span_lint(UNUSED_ATTRIBUTES, attr.span, msg); | |
672 | } | |
673 | } | |
674 | } | |
675 | } | |
676 | ||
677 | declare_lint! { | |
678 | pub PATH_STATEMENTS, | |
679 | Warn, | |
680 | "path statements with no effect" | |
681 | } | |
682 | ||
683 | #[derive(Copy, Clone)] | |
684 | pub struct PathStatements; | |
685 | ||
686 | impl LintPass for PathStatements { | |
687 | fn get_lints(&self) -> LintArray { | |
688 | lint_array!(PATH_STATEMENTS) | |
689 | } | |
690 | ||
691 | fn check_stmt(&mut self, cx: &Context, s: &ast::Stmt) { | |
692 | match s.node { | |
693 | ast::StmtSemi(ref expr, _) => { | |
694 | match expr.node { | |
695 | ast::ExprPath(..) => cx.span_lint(PATH_STATEMENTS, s.span, | |
696 | "path statement with no effect"), | |
697 | _ => () | |
698 | } | |
699 | } | |
700 | _ => () | |
701 | } | |
702 | } | |
703 | } | |
704 | ||
705 | declare_lint! { | |
706 | pub UNUSED_MUST_USE, | |
707 | Warn, | |
708 | "unused result of a type flagged as #[must_use]" | |
709 | } | |
710 | ||
711 | declare_lint! { | |
712 | pub UNUSED_RESULTS, | |
713 | Allow, | |
714 | "unused result of an expression in a statement" | |
715 | } | |
716 | ||
717 | #[derive(Copy, Clone)] | |
718 | pub struct UnusedResults; | |
719 | ||
720 | impl LintPass for UnusedResults { | |
721 | fn get_lints(&self) -> LintArray { | |
722 | lint_array!(UNUSED_MUST_USE, UNUSED_RESULTS) | |
723 | } | |
724 | ||
725 | fn check_stmt(&mut self, cx: &Context, s: &ast::Stmt) { | |
726 | let expr = match s.node { | |
727 | ast::StmtSemi(ref expr, _) => &**expr, | |
728 | _ => return | |
729 | }; | |
730 | ||
731 | if let ast::ExprRet(..) = expr.node { | |
732 | return; | |
733 | } | |
734 | ||
735 | let t = ty::expr_ty(cx.tcx, expr); | |
736 | let warned = match t.sty { | |
62682a34 SL |
737 | ty::TyTuple(ref tys) if tys.is_empty() => return, |
738 | ty::TyBool => return, | |
739 | ty::TyStruct(did, _) | | |
740 | ty::TyEnum(did, _) => { | |
c34b1796 AL |
741 | if ast_util::is_local(did) { |
742 | if let ast_map::NodeItem(it) = cx.tcx.map.get(did.node) { | |
743 | check_must_use(cx, &it.attrs, s.span) | |
744 | } else { | |
745 | false | |
746 | } | |
747 | } else { | |
748 | let attrs = csearch::get_item_attrs(&cx.sess().cstore, did); | |
749 | check_must_use(cx, &attrs[..], s.span) | |
750 | } | |
751 | } | |
752 | _ => false, | |
753 | }; | |
754 | if !warned { | |
755 | cx.span_lint(UNUSED_RESULTS, s.span, "unused result"); | |
756 | } | |
757 | ||
758 | fn check_must_use(cx: &Context, attrs: &[ast::Attribute], sp: Span) -> bool { | |
759 | for attr in attrs { | |
760 | if attr.check_name("must_use") { | |
761 | let mut msg = "unused result which must be used".to_string(); | |
762 | // check for #[must_use="..."] | |
763 | match attr.value_str() { | |
764 | None => {} | |
765 | Some(s) => { | |
766 | msg.push_str(": "); | |
767 | msg.push_str(&s); | |
768 | } | |
769 | } | |
770 | cx.span_lint(UNUSED_MUST_USE, sp, &msg); | |
771 | return true; | |
772 | } | |
773 | } | |
774 | false | |
775 | } | |
776 | } | |
777 | } | |
778 | ||
779 | declare_lint! { | |
780 | pub NON_CAMEL_CASE_TYPES, | |
781 | Warn, | |
782 | "types, variants, traits and type parameters should have camel case names" | |
783 | } | |
784 | ||
785 | #[derive(Copy, Clone)] | |
786 | pub struct NonCamelCaseTypes; | |
787 | ||
788 | impl NonCamelCaseTypes { | |
789 | fn check_case(&self, cx: &Context, sort: &str, ident: ast::Ident, span: Span) { | |
790 | fn is_camel_case(ident: ast::Ident) -> bool { | |
791 | let ident = token::get_ident(ident); | |
792 | if ident.is_empty() { | |
793 | return true; | |
794 | } | |
795 | let ident = ident.trim_matches('_'); | |
796 | ||
797 | // start with a non-lowercase letter rather than non-uppercase | |
798 | // ones (some scripts don't have a concept of upper/lowercase) | |
9346a6ac | 799 | !ident.is_empty() && !ident.char_at(0).is_lowercase() && !ident.contains('_') |
c34b1796 AL |
800 | } |
801 | ||
802 | fn to_camel_case(s: &str) -> String { | |
803 | s.split('_').flat_map(|word| word.chars().enumerate().map(|(i, c)| | |
804 | if i == 0 { | |
805 | c.to_uppercase().collect::<String>() | |
806 | } else { | |
807 | c.to_lowercase().collect() | |
808 | } | |
809 | )).collect::<Vec<_>>().concat() | |
810 | } | |
811 | ||
812 | let s = token::get_ident(ident); | |
813 | ||
814 | if !is_camel_case(ident) { | |
815 | let c = to_camel_case(&s); | |
816 | let m = if c.is_empty() { | |
817 | format!("{} `{}` should have a camel case name such as `CamelCase`", sort, s) | |
818 | } else { | |
819 | format!("{} `{}` should have a camel case name such as `{}`", sort, s, c) | |
820 | }; | |
821 | cx.span_lint(NON_CAMEL_CASE_TYPES, span, &m[..]); | |
822 | } | |
823 | } | |
824 | } | |
825 | ||
826 | impl LintPass for NonCamelCaseTypes { | |
827 | fn get_lints(&self) -> LintArray { | |
828 | lint_array!(NON_CAMEL_CASE_TYPES) | |
829 | } | |
830 | ||
831 | fn check_item(&mut self, cx: &Context, it: &ast::Item) { | |
832 | let has_extern_repr = it.attrs.iter().any(|attr| { | |
833 | attr::find_repr_attrs(cx.tcx.sess.diagnostic(), attr).iter() | |
834 | .any(|r| r == &attr::ReprExtern) | |
835 | }); | |
836 | if has_extern_repr { | |
837 | return; | |
838 | } | |
839 | ||
840 | match it.node { | |
841 | ast::ItemTy(..) | ast::ItemStruct(..) => { | |
842 | self.check_case(cx, "type", it.ident, it.span) | |
843 | } | |
844 | ast::ItemTrait(..) => { | |
845 | self.check_case(cx, "trait", it.ident, it.span) | |
846 | } | |
847 | ast::ItemEnum(ref enum_definition, _) => { | |
848 | if has_extern_repr { | |
849 | return; | |
850 | } | |
851 | self.check_case(cx, "type", it.ident, it.span); | |
852 | for variant in &enum_definition.variants { | |
853 | self.check_case(cx, "variant", variant.node.name, variant.span); | |
854 | } | |
855 | } | |
856 | _ => () | |
857 | } | |
858 | } | |
859 | ||
860 | fn check_generics(&mut self, cx: &Context, it: &ast::Generics) { | |
62682a34 | 861 | for gen in it.ty_params.iter() { |
c34b1796 AL |
862 | self.check_case(cx, "type parameter", gen.ident, gen.span); |
863 | } | |
864 | } | |
865 | } | |
866 | ||
867 | #[derive(PartialEq)] | |
868 | enum MethodContext { | |
869 | TraitDefaultImpl, | |
870 | TraitImpl, | |
871 | PlainImpl | |
872 | } | |
873 | ||
874 | fn method_context(cx: &Context, id: ast::NodeId, span: Span) -> MethodContext { | |
875 | match cx.tcx.impl_or_trait_items.borrow().get(&local_def(id)) { | |
876 | None => cx.sess().span_bug(span, "missing method descriptor?!"), | |
877 | Some(item) => match item.container() { | |
878 | ty::TraitContainer(..) => MethodContext::TraitDefaultImpl, | |
879 | ty::ImplContainer(cid) => { | |
880 | match ty::impl_trait_ref(cx.tcx, cid) { | |
881 | Some(_) => MethodContext::TraitImpl, | |
882 | None => MethodContext::PlainImpl | |
883 | } | |
884 | } | |
885 | } | |
886 | } | |
887 | } | |
888 | ||
889 | declare_lint! { | |
890 | pub NON_SNAKE_CASE, | |
891 | Warn, | |
892 | "methods, functions, lifetime parameters and modules should have snake case names" | |
893 | } | |
894 | ||
895 | #[derive(Copy, Clone)] | |
896 | pub struct NonSnakeCase; | |
897 | ||
898 | impl NonSnakeCase { | |
899 | fn to_snake_case(mut str: &str) -> String { | |
900 | let mut words = vec![]; | |
901 | // Preserve leading underscores | |
902 | str = str.trim_left_matches(|c: char| { | |
903 | if c == '_' { | |
904 | words.push(String::new()); | |
905 | true | |
906 | } else { | |
907 | false | |
908 | } | |
909 | }); | |
910 | for s in str.split('_') { | |
911 | let mut last_upper = false; | |
912 | let mut buf = String::new(); | |
913 | if s.is_empty() { | |
914 | continue; | |
915 | } | |
916 | for ch in s.chars() { | |
917 | if !buf.is_empty() && buf != "'" | |
918 | && ch.is_uppercase() | |
919 | && !last_upper { | |
920 | words.push(buf); | |
921 | buf = String::new(); | |
922 | } | |
923 | last_upper = ch.is_uppercase(); | |
924 | buf.extend(ch.to_lowercase()); | |
925 | } | |
926 | words.push(buf); | |
927 | } | |
928 | words.connect("_") | |
929 | } | |
930 | ||
d9579d0f AL |
931 | fn check_snake_case(&self, cx: &Context, sort: &str, name: &str, span: Option<Span>) { |
932 | fn is_snake_case(ident: &str) -> bool { | |
c34b1796 AL |
933 | if ident.is_empty() { |
934 | return true; | |
935 | } | |
936 | let ident = ident.trim_left_matches('\''); | |
937 | let ident = ident.trim_matches('_'); | |
938 | ||
939 | let mut allow_underscore = true; | |
940 | ident.chars().all(|c| { | |
941 | allow_underscore = match c { | |
942 | '_' if !allow_underscore => return false, | |
943 | '_' => false, | |
d9579d0f AL |
944 | // It would be more obvious to use `c.is_lowercase()`, |
945 | // but some characters do not have a lowercase form | |
c34b1796 AL |
946 | c if !c.is_uppercase() => true, |
947 | _ => return false, | |
948 | }; | |
949 | true | |
950 | }) | |
951 | } | |
952 | ||
d9579d0f AL |
953 | if !is_snake_case(name) { |
954 | let sc = NonSnakeCase::to_snake_case(name); | |
955 | let msg = if sc != name { | |
956 | format!("{} `{}` should have a snake case name such as `{}`", | |
957 | sort, name, sc) | |
c34b1796 | 958 | } else { |
d9579d0f AL |
959 | format!("{} `{}` should have a snake case name", |
960 | sort, name) | |
961 | }; | |
962 | match span { | |
963 | Some(span) => cx.span_lint(NON_SNAKE_CASE, span, &msg), | |
964 | None => cx.lint(NON_SNAKE_CASE, &msg), | |
c34b1796 AL |
965 | } |
966 | } | |
967 | } | |
968 | } | |
969 | ||
970 | impl LintPass for NonSnakeCase { | |
971 | fn get_lints(&self) -> LintArray { | |
972 | lint_array!(NON_SNAKE_CASE) | |
973 | } | |
974 | ||
d9579d0f AL |
975 | fn check_crate(&mut self, cx: &Context, cr: &ast::Crate) { |
976 | let attr_crate_name = cr.attrs.iter().find(|at| at.check_name("crate_name")) | |
977 | .and_then(|at| at.value_str().map(|s| (at, s))); | |
978 | if let Some(ref name) = cx.tcx.sess.opts.crate_name { | |
979 | self.check_snake_case(cx, "crate", name, None); | |
980 | } else if let Some((attr, ref name)) = attr_crate_name { | |
981 | self.check_snake_case(cx, "crate", name, Some(attr.span)); | |
982 | } | |
983 | } | |
984 | ||
c34b1796 AL |
985 | fn check_fn(&mut self, cx: &Context, |
986 | fk: visit::FnKind, _: &ast::FnDecl, | |
987 | _: &ast::Block, span: Span, id: ast::NodeId) { | |
988 | match fk { | |
9346a6ac | 989 | visit::FkMethod(ident, _, _) => match method_context(cx, id, span) { |
c34b1796 | 990 | MethodContext::PlainImpl => { |
d9579d0f | 991 | self.check_snake_case(cx, "method", &token::get_ident(ident), Some(span)) |
c34b1796 AL |
992 | }, |
993 | MethodContext::TraitDefaultImpl => { | |
d9579d0f | 994 | self.check_snake_case(cx, "trait method", &token::get_ident(ident), Some(span)) |
c34b1796 AL |
995 | }, |
996 | _ => (), | |
997 | }, | |
62682a34 | 998 | visit::FkItemFn(ident, _, _, _, _, _) => { |
d9579d0f | 999 | self.check_snake_case(cx, "function", &token::get_ident(ident), Some(span)) |
c34b1796 AL |
1000 | }, |
1001 | _ => (), | |
1002 | } | |
1003 | } | |
1004 | ||
1005 | fn check_item(&mut self, cx: &Context, it: &ast::Item) { | |
1006 | if let ast::ItemMod(_) = it.node { | |
d9579d0f | 1007 | self.check_snake_case(cx, "module", &token::get_ident(it.ident), Some(it.span)); |
c34b1796 AL |
1008 | } |
1009 | } | |
1010 | ||
1011 | fn check_trait_item(&mut self, cx: &Context, trait_item: &ast::TraitItem) { | |
1012 | if let ast::MethodTraitItem(_, None) = trait_item.node { | |
d9579d0f AL |
1013 | self.check_snake_case(cx, "trait method", &token::get_ident(trait_item.ident), |
1014 | Some(trait_item.span)); | |
c34b1796 AL |
1015 | } |
1016 | } | |
1017 | ||
1018 | fn check_lifetime_def(&mut self, cx: &Context, t: &ast::LifetimeDef) { | |
d9579d0f AL |
1019 | self.check_snake_case(cx, "lifetime", &token::get_ident(t.lifetime.name.ident()), |
1020 | Some(t.lifetime.span)); | |
c34b1796 AL |
1021 | } |
1022 | ||
1023 | fn check_pat(&mut self, cx: &Context, p: &ast::Pat) { | |
1024 | if let &ast::PatIdent(_, ref path1, _) = &p.node { | |
1025 | let def = cx.tcx.def_map.borrow().get(&p.id).map(|d| d.full_def()); | |
1026 | if let Some(def::DefLocal(_)) = def { | |
d9579d0f | 1027 | self.check_snake_case(cx, "variable", &token::get_ident(path1.node), Some(p.span)); |
c34b1796 AL |
1028 | } |
1029 | } | |
1030 | } | |
1031 | ||
1032 | fn check_struct_def(&mut self, cx: &Context, s: &ast::StructDef, | |
1033 | _: ast::Ident, _: &ast::Generics, _: ast::NodeId) { | |
1034 | for sf in &s.fields { | |
1035 | if let ast::StructField_ { kind: ast::NamedField(ident, _), .. } = sf.node { | |
d9579d0f AL |
1036 | self.check_snake_case(cx, "structure field", &token::get_ident(ident), |
1037 | Some(sf.span)); | |
c34b1796 AL |
1038 | } |
1039 | } | |
1040 | } | |
1041 | } | |
1042 | ||
1043 | declare_lint! { | |
1044 | pub NON_UPPER_CASE_GLOBALS, | |
1045 | Warn, | |
1046 | "static constants should have uppercase identifiers" | |
1047 | } | |
1048 | ||
1049 | #[derive(Copy, Clone)] | |
1050 | pub struct NonUpperCaseGlobals; | |
1051 | ||
1052 | impl NonUpperCaseGlobals { | |
1053 | fn check_upper_case(cx: &Context, sort: &str, ident: ast::Ident, span: Span) { | |
1054 | let s = token::get_ident(ident); | |
1055 | ||
1056 | if s.chars().any(|c| c.is_lowercase()) { | |
1057 | let uc = NonSnakeCase::to_snake_case(&s).to_uppercase(); | |
1058 | if uc != &s[..] { | |
1059 | cx.span_lint(NON_UPPER_CASE_GLOBALS, span, | |
1060 | &format!("{} `{}` should have an upper case name such as `{}`", | |
1061 | sort, s, uc)); | |
1062 | } else { | |
1063 | cx.span_lint(NON_UPPER_CASE_GLOBALS, span, | |
1064 | &format!("{} `{}` should have an upper case name", | |
1065 | sort, s)); | |
1066 | } | |
1067 | } | |
1068 | } | |
1069 | } | |
1070 | ||
1071 | impl LintPass for NonUpperCaseGlobals { | |
1072 | fn get_lints(&self) -> LintArray { | |
1073 | lint_array!(NON_UPPER_CASE_GLOBALS) | |
1074 | } | |
1075 | ||
1076 | fn check_item(&mut self, cx: &Context, it: &ast::Item) { | |
1077 | match it.node { | |
1078 | // only check static constants | |
1079 | ast::ItemStatic(_, ast::MutImmutable, _) => { | |
1080 | NonUpperCaseGlobals::check_upper_case(cx, "static constant", it.ident, it.span); | |
1081 | } | |
1082 | ast::ItemConst(..) => { | |
1083 | NonUpperCaseGlobals::check_upper_case(cx, "constant", it.ident, it.span); | |
1084 | } | |
1085 | _ => {} | |
1086 | } | |
1087 | } | |
1088 | ||
d9579d0f AL |
1089 | fn check_trait_item(&mut self, cx: &Context, ti: &ast::TraitItem) { |
1090 | match ti.node { | |
1091 | ast::ConstTraitItem(..) => { | |
1092 | NonUpperCaseGlobals::check_upper_case(cx, "associated constant", | |
1093 | ti.ident, ti.span); | |
1094 | } | |
1095 | _ => {} | |
1096 | } | |
1097 | } | |
1098 | ||
1099 | fn check_impl_item(&mut self, cx: &Context, ii: &ast::ImplItem) { | |
1100 | match ii.node { | |
1101 | ast::ConstImplItem(..) => { | |
1102 | NonUpperCaseGlobals::check_upper_case(cx, "associated constant", | |
1103 | ii.ident, ii.span); | |
1104 | } | |
1105 | _ => {} | |
1106 | } | |
1107 | } | |
1108 | ||
c34b1796 AL |
1109 | fn check_pat(&mut self, cx: &Context, p: &ast::Pat) { |
1110 | // Lint for constants that look like binding identifiers (#7526) | |
1111 | match (&p.node, cx.tcx.def_map.borrow().get(&p.id).map(|d| d.full_def())) { | |
1112 | (&ast::PatIdent(_, ref path1, _), Some(def::DefConst(..))) => { | |
1113 | NonUpperCaseGlobals::check_upper_case(cx, "constant in pattern", | |
1114 | path1.node, p.span); | |
1115 | } | |
1116 | _ => {} | |
1117 | } | |
1118 | } | |
1119 | } | |
1120 | ||
1121 | declare_lint! { | |
1122 | UNUSED_PARENS, | |
1123 | Warn, | |
1124 | "`if`, `match`, `while` and `return` do not need parentheses" | |
1125 | } | |
1126 | ||
1127 | #[derive(Copy, Clone)] | |
1128 | pub struct UnusedParens; | |
1129 | ||
1130 | impl UnusedParens { | |
1131 | fn check_unused_parens_core(&self, cx: &Context, value: &ast::Expr, msg: &str, | |
1132 | struct_lit_needs_parens: bool) { | |
1133 | if let ast::ExprParen(ref inner) = value.node { | |
1134 | let necessary = struct_lit_needs_parens && contains_exterior_struct_lit(&**inner); | |
1135 | if !necessary { | |
1136 | cx.span_lint(UNUSED_PARENS, value.span, | |
1137 | &format!("unnecessary parentheses around {}", msg)) | |
1138 | } | |
1139 | } | |
1140 | ||
1141 | /// Expressions that syntactically contain an "exterior" struct | |
1142 | /// literal i.e. not surrounded by any parens or other | |
1143 | /// delimiters, e.g. `X { y: 1 }`, `X { y: 1 }.method()`, `foo | |
1144 | /// == X { y: 1 }` and `X { y: 1 } == foo` all do, but `(X { | |
1145 | /// y: 1 }) == foo` does not. | |
1146 | fn contains_exterior_struct_lit(value: &ast::Expr) -> bool { | |
1147 | match value.node { | |
1148 | ast::ExprStruct(..) => true, | |
1149 | ||
1150 | ast::ExprAssign(ref lhs, ref rhs) | | |
1151 | ast::ExprAssignOp(_, ref lhs, ref rhs) | | |
1152 | ast::ExprBinary(_, ref lhs, ref rhs) => { | |
1153 | // X { y: 1 } + X { y: 2 } | |
1154 | contains_exterior_struct_lit(&**lhs) || | |
1155 | contains_exterior_struct_lit(&**rhs) | |
1156 | } | |
1157 | ast::ExprUnary(_, ref x) | | |
1158 | ast::ExprCast(ref x, _) | | |
1159 | ast::ExprField(ref x, _) | | |
1160 | ast::ExprTupField(ref x, _) | | |
1161 | ast::ExprIndex(ref x, _) => { | |
1162 | // &X { y: 1 }, X { y: 1 }.y | |
1163 | contains_exterior_struct_lit(&**x) | |
1164 | } | |
1165 | ||
1166 | ast::ExprMethodCall(_, _, ref exprs) => { | |
1167 | // X { y: 1 }.bar(...) | |
1168 | contains_exterior_struct_lit(&*exprs[0]) | |
1169 | } | |
1170 | ||
1171 | _ => false | |
1172 | } | |
1173 | } | |
1174 | } | |
1175 | } | |
1176 | ||
1177 | impl LintPass for UnusedParens { | |
1178 | fn get_lints(&self) -> LintArray { | |
1179 | lint_array!(UNUSED_PARENS) | |
1180 | } | |
1181 | ||
1182 | fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { | |
1183 | let (value, msg, struct_lit_needs_parens) = match e.node { | |
1184 | ast::ExprIf(ref cond, _, _) => (cond, "`if` condition", true), | |
1185 | ast::ExprWhile(ref cond, _, _) => (cond, "`while` condition", true), | |
1186 | ast::ExprMatch(ref head, _, source) => match source { | |
1187 | ast::MatchSource::Normal => (head, "`match` head expression", true), | |
1188 | ast::MatchSource::IfLetDesugar { .. } => (head, "`if let` head expression", true), | |
1189 | ast::MatchSource::WhileLetDesugar => (head, "`while let` head expression", true), | |
1190 | ast::MatchSource::ForLoopDesugar => (head, "`for` head expression", true), | |
1191 | }, | |
1192 | ast::ExprRet(Some(ref value)) => (value, "`return` value", false), | |
1193 | ast::ExprAssign(_, ref value) => (value, "assigned value", false), | |
1194 | ast::ExprAssignOp(_, _, ref value) => (value, "assigned value", false), | |
1195 | _ => return | |
1196 | }; | |
1197 | self.check_unused_parens_core(cx, &**value, msg, struct_lit_needs_parens); | |
1198 | } | |
1199 | ||
1200 | fn check_stmt(&mut self, cx: &Context, s: &ast::Stmt) { | |
1201 | let (value, msg) = match s.node { | |
1202 | ast::StmtDecl(ref decl, _) => match decl.node { | |
1203 | ast::DeclLocal(ref local) => match local.init { | |
1204 | Some(ref value) => (value, "assigned value"), | |
1205 | None => return | |
1206 | }, | |
1207 | _ => return | |
1208 | }, | |
1209 | _ => return | |
1210 | }; | |
1211 | self.check_unused_parens_core(cx, &**value, msg, false); | |
1212 | } | |
1213 | } | |
1214 | ||
1215 | declare_lint! { | |
1216 | UNUSED_IMPORT_BRACES, | |
1217 | Allow, | |
1218 | "unnecessary braces around an imported item" | |
1219 | } | |
1220 | ||
1221 | #[derive(Copy, Clone)] | |
1222 | pub struct UnusedImportBraces; | |
1223 | ||
1224 | impl LintPass for UnusedImportBraces { | |
1225 | fn get_lints(&self) -> LintArray { | |
1226 | lint_array!(UNUSED_IMPORT_BRACES) | |
1227 | } | |
1228 | ||
1229 | fn check_item(&mut self, cx: &Context, item: &ast::Item) { | |
1230 | if let ast::ItemUse(ref view_path) = item.node { | |
1231 | if let ast::ViewPathList(_, ref items) = view_path.node { | |
1232 | if items.len() == 1 { | |
1233 | if let ast::PathListIdent {ref name, ..} = items[0].node { | |
1234 | let m = format!("braces around {} is unnecessary", | |
1235 | &token::get_ident(*name)); | |
1236 | cx.span_lint(UNUSED_IMPORT_BRACES, item.span, | |
1237 | &m[..]); | |
1238 | } | |
1239 | } | |
1240 | } | |
1241 | } | |
1242 | } | |
1243 | } | |
1244 | ||
1245 | declare_lint! { | |
1246 | NON_SHORTHAND_FIELD_PATTERNS, | |
1247 | Warn, | |
1248 | "using `Struct { x: x }` instead of `Struct { x }`" | |
1249 | } | |
1250 | ||
1251 | #[derive(Copy, Clone)] | |
1252 | pub struct NonShorthandFieldPatterns; | |
1253 | ||
1254 | impl LintPass for NonShorthandFieldPatterns { | |
1255 | fn get_lints(&self) -> LintArray { | |
1256 | lint_array!(NON_SHORTHAND_FIELD_PATTERNS) | |
1257 | } | |
1258 | ||
1259 | fn check_pat(&mut self, cx: &Context, pat: &ast::Pat) { | |
1260 | let def_map = cx.tcx.def_map.borrow(); | |
1261 | if let ast::PatStruct(_, ref v, _) = pat.node { | |
1262 | let field_pats = v.iter().filter(|fieldpat| { | |
1263 | if fieldpat.node.is_shorthand { | |
1264 | return false; | |
1265 | } | |
1266 | let def = def_map.get(&fieldpat.node.pat.id).map(|d| d.full_def()); | |
1267 | def == Some(def::DefLocal(fieldpat.node.pat.id)) | |
1268 | }); | |
1269 | for fieldpat in field_pats { | |
1270 | if let ast::PatIdent(_, ident, None) = fieldpat.node.pat.node { | |
1271 | if ident.node.as_str() == fieldpat.node.ident.as_str() { | |
1272 | cx.span_lint(NON_SHORTHAND_FIELD_PATTERNS, fieldpat.span, | |
1273 | &format!("the `{}:` in this pattern is redundant and can \ | |
1274 | be removed", ident.node.as_str())) | |
1275 | } | |
1276 | } | |
1277 | } | |
1278 | } | |
1279 | } | |
1280 | } | |
1281 | ||
1282 | declare_lint! { | |
1283 | pub UNUSED_UNSAFE, | |
1284 | Warn, | |
1285 | "unnecessary use of an `unsafe` block" | |
1286 | } | |
1287 | ||
1288 | #[derive(Copy, Clone)] | |
1289 | pub struct UnusedUnsafe; | |
1290 | ||
1291 | impl LintPass for UnusedUnsafe { | |
1292 | fn get_lints(&self) -> LintArray { | |
1293 | lint_array!(UNUSED_UNSAFE) | |
1294 | } | |
1295 | ||
1296 | fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { | |
1297 | if let ast::ExprBlock(ref blk) = e.node { | |
1298 | // Don't warn about generated blocks, that'll just pollute the output. | |
1299 | if blk.rules == ast::UnsafeBlock(ast::UserProvided) && | |
1300 | !cx.tcx.used_unsafe.borrow().contains(&blk.id) { | |
1301 | cx.span_lint(UNUSED_UNSAFE, blk.span, "unnecessary `unsafe` block"); | |
1302 | } | |
1303 | } | |
1304 | } | |
1305 | } | |
1306 | ||
1307 | declare_lint! { | |
1308 | UNSAFE_CODE, | |
1309 | Allow, | |
1310 | "usage of `unsafe` code" | |
1311 | } | |
1312 | ||
1313 | #[derive(Copy, Clone)] | |
1314 | pub struct UnsafeCode; | |
1315 | ||
1316 | impl LintPass for UnsafeCode { | |
1317 | fn get_lints(&self) -> LintArray { | |
1318 | lint_array!(UNSAFE_CODE) | |
1319 | } | |
1320 | ||
1321 | fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { | |
1322 | if let ast::ExprBlock(ref blk) = e.node { | |
1323 | // Don't warn about generated blocks, that'll just pollute the output. | |
1324 | if blk.rules == ast::UnsafeBlock(ast::UserProvided) { | |
1325 | cx.span_lint(UNSAFE_CODE, blk.span, "usage of an `unsafe` block"); | |
1326 | } | |
1327 | } | |
1328 | } | |
1329 | ||
1330 | fn check_item(&mut self, cx: &Context, it: &ast::Item) { | |
1331 | match it.node { | |
1332 | ast::ItemTrait(ast::Unsafety::Unsafe, _, _, _) => | |
1333 | cx.span_lint(UNSAFE_CODE, it.span, "declaration of an `unsafe` trait"), | |
1334 | ||
1335 | ast::ItemImpl(ast::Unsafety::Unsafe, _, _, _, _, _) => | |
1336 | cx.span_lint(UNSAFE_CODE, it.span, "implementation of an `unsafe` trait"), | |
1337 | ||
1338 | _ => return, | |
1339 | } | |
1340 | } | |
1341 | ||
1342 | fn check_fn(&mut self, cx: &Context, fk: visit::FnKind, _: &ast::FnDecl, | |
1343 | _: &ast::Block, span: Span, _: ast::NodeId) { | |
1344 | match fk { | |
62682a34 | 1345 | visit::FkItemFn(_, _, ast::Unsafety::Unsafe, _, _, _) => |
c34b1796 AL |
1346 | cx.span_lint(UNSAFE_CODE, span, "declaration of an `unsafe` function"), |
1347 | ||
9346a6ac | 1348 | visit::FkMethod(_, sig, _) => { |
c34b1796 AL |
1349 | if sig.unsafety == ast::Unsafety::Unsafe { |
1350 | cx.span_lint(UNSAFE_CODE, span, "implementation of an `unsafe` method") | |
1351 | } | |
1352 | }, | |
1353 | ||
1354 | _ => (), | |
1355 | } | |
1356 | } | |
1357 | ||
1358 | fn check_trait_item(&mut self, cx: &Context, trait_item: &ast::TraitItem) { | |
1359 | if let ast::MethodTraitItem(ref sig, None) = trait_item.node { | |
1360 | if sig.unsafety == ast::Unsafety::Unsafe { | |
1361 | cx.span_lint(UNSAFE_CODE, trait_item.span, | |
1362 | "declaration of an `unsafe` method") | |
1363 | } | |
1364 | } | |
1365 | } | |
1366 | } | |
1367 | ||
1368 | declare_lint! { | |
1369 | pub UNUSED_MUT, | |
1370 | Warn, | |
1371 | "detect mut variables which don't need to be mutable" | |
1372 | } | |
1373 | ||
1374 | #[derive(Copy, Clone)] | |
1375 | pub struct UnusedMut; | |
1376 | ||
1377 | impl UnusedMut { | |
1378 | fn check_unused_mut_pat(&self, cx: &Context, pats: &[P<ast::Pat>]) { | |
1379 | // collect all mutable pattern and group their NodeIDs by their Identifier to | |
1380 | // avoid false warnings in match arms with multiple patterns | |
1381 | ||
1382 | let mut mutables = FnvHashMap(); | |
1383 | for p in pats { | |
1384 | pat_util::pat_bindings(&cx.tcx.def_map, &**p, |mode, id, _, path1| { | |
1385 | let ident = path1.node; | |
1386 | if let ast::BindByValue(ast::MutMutable) = mode { | |
1387 | if !token::get_ident(ident).starts_with("_") { | |
1388 | match mutables.entry(ident.name.usize()) { | |
1389 | Vacant(entry) => { entry.insert(vec![id]); }, | |
1390 | Occupied(mut entry) => { entry.get_mut().push(id); }, | |
1391 | } | |
1392 | } | |
1393 | } | |
1394 | }); | |
1395 | } | |
1396 | ||
1397 | let used_mutables = cx.tcx.used_mut_nodes.borrow(); | |
1398 | for (_, v) in &mutables { | |
1399 | if !v.iter().any(|e| used_mutables.contains(e)) { | |
1400 | cx.span_lint(UNUSED_MUT, cx.tcx.map.span(v[0]), | |
1401 | "variable does not need to be mutable"); | |
1402 | } | |
1403 | } | |
1404 | } | |
1405 | } | |
1406 | ||
1407 | impl LintPass for UnusedMut { | |
1408 | fn get_lints(&self) -> LintArray { | |
1409 | lint_array!(UNUSED_MUT) | |
1410 | } | |
1411 | ||
1412 | fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { | |
1413 | if let ast::ExprMatch(_, ref arms, _) = e.node { | |
1414 | for a in arms { | |
1415 | self.check_unused_mut_pat(cx, &a.pats) | |
1416 | } | |
1417 | } | |
1418 | } | |
1419 | ||
1420 | fn check_stmt(&mut self, cx: &Context, s: &ast::Stmt) { | |
1421 | if let ast::StmtDecl(ref d, _) = s.node { | |
1422 | if let ast::DeclLocal(ref l) = d.node { | |
1423 | self.check_unused_mut_pat(cx, slice::ref_slice(&l.pat)); | |
1424 | } | |
1425 | } | |
1426 | } | |
1427 | ||
1428 | fn check_fn(&mut self, cx: &Context, | |
1429 | _: visit::FnKind, decl: &ast::FnDecl, | |
1430 | _: &ast::Block, _: Span, _: ast::NodeId) { | |
1431 | for a in &decl.inputs { | |
1432 | self.check_unused_mut_pat(cx, slice::ref_slice(&a.pat)); | |
1433 | } | |
1434 | } | |
1435 | } | |
1436 | ||
1437 | declare_lint! { | |
1438 | UNUSED_ALLOCATION, | |
1439 | Warn, | |
1440 | "detects unnecessary allocations that can be eliminated" | |
1441 | } | |
1442 | ||
1443 | #[derive(Copy, Clone)] | |
1444 | pub struct UnusedAllocation; | |
1445 | ||
1446 | impl LintPass for UnusedAllocation { | |
1447 | fn get_lints(&self) -> LintArray { | |
1448 | lint_array!(UNUSED_ALLOCATION) | |
1449 | } | |
1450 | ||
1451 | fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { | |
1452 | match e.node { | |
1453 | ast::ExprUnary(ast::UnUniq, _) => (), | |
1454 | _ => return | |
1455 | } | |
1456 | ||
1457 | if let Some(adjustment) = cx.tcx.adjustments.borrow().get(&e.id) { | |
1458 | if let ty::AdjustDerefRef(ty::AutoDerefRef { ref autoref, .. }) = *adjustment { | |
1459 | match autoref { | |
9346a6ac | 1460 | &Some(ty::AutoPtr(_, ast::MutImmutable)) => { |
c34b1796 AL |
1461 | cx.span_lint(UNUSED_ALLOCATION, e.span, |
1462 | "unnecessary allocation, use & instead"); | |
1463 | } | |
9346a6ac | 1464 | &Some(ty::AutoPtr(_, ast::MutMutable)) => { |
c34b1796 AL |
1465 | cx.span_lint(UNUSED_ALLOCATION, e.span, |
1466 | "unnecessary allocation, use &mut instead"); | |
1467 | } | |
1468 | _ => () | |
1469 | } | |
1470 | } | |
1471 | } | |
1472 | } | |
1473 | } | |
1474 | ||
1475 | declare_lint! { | |
1476 | MISSING_DOCS, | |
1477 | Allow, | |
1478 | "detects missing documentation for public members" | |
1479 | } | |
1480 | ||
1481 | pub struct MissingDoc { | |
1482 | /// Stack of IDs of struct definitions. | |
1483 | struct_def_stack: Vec<ast::NodeId>, | |
1484 | ||
1485 | /// True if inside variant definition | |
1486 | in_variant: bool, | |
1487 | ||
1488 | /// Stack of whether #[doc(hidden)] is set | |
1489 | /// at each level which has lint attributes. | |
1490 | doc_hidden_stack: Vec<bool>, | |
1491 | ||
1492 | /// Private traits or trait items that leaked through. Don't check their methods. | |
1493 | private_traits: HashSet<ast::NodeId>, | |
1494 | } | |
1495 | ||
1496 | impl MissingDoc { | |
1497 | pub fn new() -> MissingDoc { | |
1498 | MissingDoc { | |
1499 | struct_def_stack: vec!(), | |
1500 | in_variant: false, | |
1501 | doc_hidden_stack: vec!(false), | |
1502 | private_traits: HashSet::new(), | |
1503 | } | |
1504 | } | |
1505 | ||
1506 | fn doc_hidden(&self) -> bool { | |
1507 | *self.doc_hidden_stack.last().expect("empty doc_hidden_stack") | |
1508 | } | |
1509 | ||
1510 | fn check_missing_docs_attrs(&self, | |
1511 | cx: &Context, | |
1512 | id: Option<ast::NodeId>, | |
1513 | attrs: &[ast::Attribute], | |
1514 | sp: Span, | |
1515 | desc: &'static str) { | |
1516 | // If we're building a test harness, then warning about | |
1517 | // documentation is probably not really relevant right now. | |
1518 | if cx.sess().opts.test { | |
1519 | return; | |
1520 | } | |
1521 | ||
1522 | // `#[doc(hidden)]` disables missing_docs check. | |
1523 | if self.doc_hidden() { | |
1524 | return; | |
1525 | } | |
1526 | ||
1527 | // Only check publicly-visible items, using the result from the privacy pass. | |
1528 | // It's an option so the crate root can also use this function (it doesn't | |
1529 | // have a NodeId). | |
1530 | if let Some(ref id) = id { | |
1531 | if !cx.exported_items.contains(id) { | |
1532 | return; | |
1533 | } | |
1534 | } | |
1535 | ||
1536 | let has_doc = attrs.iter().any(|a| { | |
1537 | match a.node.value.node { | |
1538 | ast::MetaNameValue(ref name, _) if *name == "doc" => true, | |
1539 | _ => false | |
1540 | } | |
1541 | }); | |
1542 | if !has_doc { | |
1543 | cx.span_lint(MISSING_DOCS, sp, | |
1544 | &format!("missing documentation for {}", desc)); | |
1545 | } | |
1546 | } | |
1547 | } | |
1548 | ||
1549 | impl LintPass for MissingDoc { | |
1550 | fn get_lints(&self) -> LintArray { | |
1551 | lint_array!(MISSING_DOCS) | |
1552 | } | |
1553 | ||
1554 | fn enter_lint_attrs(&mut self, _: &Context, attrs: &[ast::Attribute]) { | |
1555 | let doc_hidden = self.doc_hidden() || attrs.iter().any(|attr| { | |
1556 | attr.check_name("doc") && match attr.meta_item_list() { | |
1557 | None => false, | |
1558 | Some(l) => attr::contains_name(&l[..], "hidden"), | |
1559 | } | |
1560 | }); | |
1561 | self.doc_hidden_stack.push(doc_hidden); | |
1562 | } | |
1563 | ||
1564 | fn exit_lint_attrs(&mut self, _: &Context, _: &[ast::Attribute]) { | |
1565 | self.doc_hidden_stack.pop().expect("empty doc_hidden_stack"); | |
1566 | } | |
1567 | ||
1568 | fn check_struct_def(&mut self, _: &Context, _: &ast::StructDef, | |
1569 | _: ast::Ident, _: &ast::Generics, id: ast::NodeId) { | |
1570 | self.struct_def_stack.push(id); | |
1571 | } | |
1572 | ||
1573 | fn check_struct_def_post(&mut self, _: &Context, _: &ast::StructDef, | |
1574 | _: ast::Ident, _: &ast::Generics, id: ast::NodeId) { | |
1575 | let popped = self.struct_def_stack.pop().expect("empty struct_def_stack"); | |
1576 | assert!(popped == id); | |
1577 | } | |
1578 | ||
1579 | fn check_crate(&mut self, cx: &Context, krate: &ast::Crate) { | |
1580 | self.check_missing_docs_attrs(cx, None, &krate.attrs, krate.span, "crate"); | |
1581 | } | |
1582 | ||
1583 | fn check_item(&mut self, cx: &Context, it: &ast::Item) { | |
1584 | let desc = match it.node { | |
1585 | ast::ItemFn(..) => "a function", | |
1586 | ast::ItemMod(..) => "a module", | |
1587 | ast::ItemEnum(..) => "an enum", | |
1588 | ast::ItemStruct(..) => "a struct", | |
1589 | ast::ItemTrait(_, _, _, ref items) => { | |
1590 | // Issue #11592, traits are always considered exported, even when private. | |
1591 | if it.vis == ast::Visibility::Inherited { | |
1592 | self.private_traits.insert(it.id); | |
1593 | for itm in items { | |
1594 | self.private_traits.insert(itm.id); | |
1595 | } | |
1596 | return | |
1597 | } | |
1598 | "a trait" | |
1599 | }, | |
1600 | ast::ItemTy(..) => "a type alias", | |
1601 | ast::ItemImpl(_, _, _, Some(ref trait_ref), _, ref impl_items) => { | |
1602 | // If the trait is private, add the impl items to private_traits so they don't get | |
1603 | // reported for missing docs. | |
1604 | let real_trait = ty::trait_ref_to_def_id(cx.tcx, trait_ref); | |
1605 | match cx.tcx.map.find(real_trait.node) { | |
1606 | Some(ast_map::NodeItem(item)) => if item.vis == ast::Visibility::Inherited { | |
1607 | for itm in impl_items { | |
1608 | self.private_traits.insert(itm.id); | |
1609 | } | |
1610 | }, | |
1611 | _ => { } | |
1612 | } | |
1613 | return | |
1614 | }, | |
1615 | _ => return | |
1616 | }; | |
1617 | ||
1618 | self.check_missing_docs_attrs(cx, Some(it.id), &it.attrs, it.span, desc); | |
1619 | } | |
1620 | ||
1621 | fn check_trait_item(&mut self, cx: &Context, trait_item: &ast::TraitItem) { | |
1622 | if self.private_traits.contains(&trait_item.id) { return } | |
1623 | ||
1624 | let desc = match trait_item.node { | |
d9579d0f | 1625 | ast::ConstTraitItem(..) => "an associated constant", |
c34b1796 | 1626 | ast::MethodTraitItem(..) => "a trait method", |
d9579d0f | 1627 | ast::TypeTraitItem(..) => "an associated type", |
c34b1796 AL |
1628 | }; |
1629 | ||
1630 | self.check_missing_docs_attrs(cx, Some(trait_item.id), | |
1631 | &trait_item.attrs, | |
1632 | trait_item.span, desc); | |
1633 | } | |
1634 | ||
1635 | fn check_impl_item(&mut self, cx: &Context, impl_item: &ast::ImplItem) { | |
1636 | // If the method is an impl for a trait, don't doc. | |
1637 | if method_context(cx, impl_item.id, impl_item.span) == MethodContext::TraitImpl { | |
1638 | return; | |
1639 | } | |
1640 | ||
1641 | let desc = match impl_item.node { | |
d9579d0f | 1642 | ast::ConstImplItem(..) => "an associated constant", |
c34b1796 AL |
1643 | ast::MethodImplItem(..) => "a method", |
1644 | ast::TypeImplItem(_) => "an associated type", | |
d9579d0f | 1645 | ast::MacImplItem(_) => "an impl item macro", |
c34b1796 AL |
1646 | }; |
1647 | self.check_missing_docs_attrs(cx, Some(impl_item.id), | |
1648 | &impl_item.attrs, | |
1649 | impl_item.span, desc); | |
1650 | } | |
1651 | ||
1652 | fn check_struct_field(&mut self, cx: &Context, sf: &ast::StructField) { | |
1653 | if let ast::NamedField(_, vis) = sf.node.kind { | |
1654 | if vis == ast::Public || self.in_variant { | |
1655 | let cur_struct_def = *self.struct_def_stack.last() | |
1656 | .expect("empty struct_def_stack"); | |
1657 | self.check_missing_docs_attrs(cx, Some(cur_struct_def), | |
1658 | &sf.node.attrs, sf.span, | |
1659 | "a struct field") | |
1660 | } | |
1661 | } | |
1662 | } | |
1663 | ||
1664 | fn check_variant(&mut self, cx: &Context, v: &ast::Variant, _: &ast::Generics) { | |
1665 | self.check_missing_docs_attrs(cx, Some(v.node.id), &v.node.attrs, v.span, "a variant"); | |
1666 | assert!(!self.in_variant); | |
1667 | self.in_variant = true; | |
1668 | } | |
1669 | ||
1670 | fn check_variant_post(&mut self, _: &Context, _: &ast::Variant, _: &ast::Generics) { | |
1671 | assert!(self.in_variant); | |
1672 | self.in_variant = false; | |
1673 | } | |
1674 | } | |
1675 | ||
1676 | declare_lint! { | |
1677 | pub MISSING_COPY_IMPLEMENTATIONS, | |
1678 | Allow, | |
1679 | "detects potentially-forgotten implementations of `Copy`" | |
1680 | } | |
1681 | ||
1682 | #[derive(Copy, Clone)] | |
1683 | pub struct MissingCopyImplementations; | |
1684 | ||
1685 | impl LintPass for MissingCopyImplementations { | |
1686 | fn get_lints(&self) -> LintArray { | |
1687 | lint_array!(MISSING_COPY_IMPLEMENTATIONS) | |
1688 | } | |
1689 | ||
1690 | fn check_item(&mut self, cx: &Context, item: &ast::Item) { | |
1691 | if !cx.exported_items.contains(&item.id) { | |
1692 | return; | |
1693 | } | |
1694 | if cx.tcx.destructor_for_type.borrow().contains_key(&local_def(item.id)) { | |
1695 | return; | |
1696 | } | |
1697 | let ty = match item.node { | |
1698 | ast::ItemStruct(_, ref ast_generics) => { | |
1699 | if ast_generics.is_parameterized() { | |
1700 | return; | |
1701 | } | |
1702 | ty::mk_struct(cx.tcx, local_def(item.id), | |
1703 | cx.tcx.mk_substs(Substs::empty())) | |
1704 | } | |
1705 | ast::ItemEnum(_, ref ast_generics) => { | |
1706 | if ast_generics.is_parameterized() { | |
1707 | return; | |
1708 | } | |
1709 | ty::mk_enum(cx.tcx, local_def(item.id), | |
1710 | cx.tcx.mk_substs(Substs::empty())) | |
1711 | } | |
1712 | _ => return, | |
1713 | }; | |
1714 | let parameter_environment = ty::empty_parameter_environment(cx.tcx); | |
1715 | if !ty::type_moves_by_default(¶meter_environment, item.span, ty) { | |
1716 | return; | |
1717 | } | |
1718 | if ty::can_type_implement_copy(¶meter_environment, item.span, ty).is_ok() { | |
1719 | cx.span_lint(MISSING_COPY_IMPLEMENTATIONS, | |
1720 | item.span, | |
1721 | "type could implement `Copy`; consider adding `impl \ | |
1722 | Copy`") | |
1723 | } | |
1724 | } | |
1725 | } | |
1726 | ||
1727 | declare_lint! { | |
1728 | MISSING_DEBUG_IMPLEMENTATIONS, | |
1729 | Allow, | |
1730 | "detects missing implementations of fmt::Debug" | |
1731 | } | |
1732 | ||
1733 | pub struct MissingDebugImplementations { | |
1734 | impling_types: Option<NodeSet>, | |
1735 | } | |
1736 | ||
1737 | impl MissingDebugImplementations { | |
1738 | pub fn new() -> MissingDebugImplementations { | |
1739 | MissingDebugImplementations { | |
1740 | impling_types: None, | |
1741 | } | |
1742 | } | |
1743 | } | |
1744 | ||
1745 | impl LintPass for MissingDebugImplementations { | |
1746 | fn get_lints(&self) -> LintArray { | |
1747 | lint_array!(MISSING_DEBUG_IMPLEMENTATIONS) | |
1748 | } | |
1749 | ||
1750 | fn check_item(&mut self, cx: &Context, item: &ast::Item) { | |
1751 | if !cx.exported_items.contains(&item.id) { | |
1752 | return; | |
1753 | } | |
1754 | ||
1755 | match item.node { | |
1756 | ast::ItemStruct(..) | ast::ItemEnum(..) => {}, | |
1757 | _ => return, | |
1758 | } | |
1759 | ||
1760 | let debug = match cx.tcx.lang_items.debug_trait() { | |
1761 | Some(debug) => debug, | |
1762 | None => return, | |
1763 | }; | |
1764 | ||
1765 | if self.impling_types.is_none() { | |
d9579d0f AL |
1766 | let debug_def = ty::lookup_trait_def(cx.tcx, debug); |
1767 | let mut impls = NodeSet(); | |
1768 | debug_def.for_each_impl(cx.tcx, |d| { | |
1769 | if d.krate == ast::LOCAL_CRATE { | |
1770 | if let Some(ty_def) = ty::ty_to_def_id(ty::node_id_to_type(cx.tcx, d.node)) { | |
1771 | impls.insert(ty_def.node); | |
1772 | } | |
c34b1796 | 1773 | } |
d9579d0f AL |
1774 | }); |
1775 | ||
c34b1796 AL |
1776 | self.impling_types = Some(impls); |
1777 | debug!("{:?}", self.impling_types); | |
1778 | } | |
1779 | ||
1780 | if !self.impling_types.as_ref().unwrap().contains(&item.id) { | |
1781 | cx.span_lint(MISSING_DEBUG_IMPLEMENTATIONS, | |
1782 | item.span, | |
1783 | "type does not implement `fmt::Debug`; consider adding #[derive(Debug)] \ | |
1784 | or a manual implementation") | |
1785 | } | |
1786 | } | |
1787 | } | |
1788 | ||
1789 | declare_lint! { | |
1790 | DEPRECATED, | |
1791 | Warn, | |
1792 | "detects use of #[deprecated] items" | |
1793 | } | |
1794 | ||
1795 | /// Checks for use of items with `#[deprecated]` attributes | |
1796 | #[derive(Copy, Clone)] | |
1797 | pub struct Stability; | |
1798 | ||
1799 | impl Stability { | |
62682a34 SL |
1800 | fn lint(&self, cx: &Context, _id: ast::DefId, |
1801 | span: Span, stability: &Option<&attr::Stability>) { | |
c34b1796 AL |
1802 | // Deprecated attributes apply in-crate and cross-crate. |
1803 | let (lint, label) = match *stability { | |
62682a34 | 1804 | Some(&attr::Stability { deprecated_since: Some(_), .. }) => |
c34b1796 AL |
1805 | (DEPRECATED, "deprecated"), |
1806 | _ => return | |
1807 | }; | |
1808 | ||
1809 | output(cx, span, stability, lint, label); | |
1810 | ||
62682a34 | 1811 | fn output(cx: &Context, span: Span, stability: &Option<&attr::Stability>, |
c34b1796 AL |
1812 | lint: &'static Lint, label: &'static str) { |
1813 | let msg = match *stability { | |
62682a34 | 1814 | Some(&attr::Stability { reason: Some(ref s), .. }) => { |
c34b1796 AL |
1815 | format!("use of {} item: {}", label, *s) |
1816 | } | |
1817 | _ => format!("use of {} item", label) | |
1818 | }; | |
1819 | ||
1820 | cx.span_lint(lint, span, &msg[..]); | |
1821 | } | |
1822 | } | |
1823 | } | |
1824 | ||
1825 | impl LintPass for Stability { | |
1826 | fn get_lints(&self) -> LintArray { | |
1827 | lint_array!(DEPRECATED) | |
1828 | } | |
1829 | ||
1830 | fn check_item(&mut self, cx: &Context, item: &ast::Item) { | |
1831 | stability::check_item(cx.tcx, item, false, | |
1832 | &mut |id, sp, stab| self.lint(cx, id, sp, stab)); | |
1833 | } | |
1834 | ||
1835 | fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { | |
1836 | stability::check_expr(cx.tcx, e, | |
1837 | &mut |id, sp, stab| self.lint(cx, id, sp, stab)); | |
1838 | } | |
1839 | ||
1840 | fn check_path(&mut self, cx: &Context, path: &ast::Path, id: ast::NodeId) { | |
1841 | stability::check_path(cx.tcx, path, id, | |
1842 | &mut |id, sp, stab| self.lint(cx, id, sp, stab)); | |
1843 | } | |
1844 | ||
1845 | fn check_pat(&mut self, cx: &Context, pat: &ast::Pat) { | |
1846 | stability::check_pat(cx.tcx, pat, | |
1847 | &mut |id, sp, stab| self.lint(cx, id, sp, stab)) | |
1848 | } | |
1849 | } | |
1850 | ||
1851 | declare_lint! { | |
1852 | pub UNCONDITIONAL_RECURSION, | |
1853 | Warn, | |
1854 | "functions that cannot return without calling themselves" | |
1855 | } | |
1856 | ||
1857 | #[derive(Copy, Clone)] | |
1858 | pub struct UnconditionalRecursion; | |
1859 | ||
1860 | ||
1861 | impl LintPass for UnconditionalRecursion { | |
1862 | fn get_lints(&self) -> LintArray { | |
1863 | lint_array![UNCONDITIONAL_RECURSION] | |
1864 | } | |
1865 | ||
1866 | fn check_fn(&mut self, cx: &Context, fn_kind: visit::FnKind, _: &ast::FnDecl, | |
1867 | blk: &ast::Block, sp: Span, id: ast::NodeId) { | |
1868 | // FIXME(#23542) Replace with type ascription. | |
1869 | #![allow(trivial_casts)] | |
1870 | ||
1871 | type F = for<'tcx> fn(&ty::ctxt<'tcx>, | |
1872 | ast::NodeId, ast::NodeId, ast::Ident, ast::NodeId) -> bool; | |
1873 | ||
1874 | let (name, checker) = match fn_kind { | |
62682a34 | 1875 | visit::FkItemFn(name, _, _, _, _, _) => (name, id_refers_to_this_fn as F), |
9346a6ac | 1876 | visit::FkMethod(name, _, _) => (name, id_refers_to_this_method as F), |
c34b1796 AL |
1877 | // closures can't recur, so they don't matter. |
1878 | visit::FkFnBlock => return | |
1879 | }; | |
1880 | ||
1881 | let impl_def_id = ty::impl_of_method(cx.tcx, local_def(id)) | |
1882 | .unwrap_or(local_def(ast::DUMMY_NODE_ID)); | |
1883 | assert!(ast_util::is_local(impl_def_id)); | |
1884 | let impl_node_id = impl_def_id.node; | |
1885 | ||
1886 | // Walk through this function (say `f`) looking to see if | |
1887 | // every possible path references itself, i.e. the function is | |
1888 | // called recursively unconditionally. This is done by trying | |
1889 | // to find a path from the entry node to the exit node that | |
1890 | // *doesn't* call `f` by traversing from the entry while | |
1891 | // pretending that calls of `f` are sinks (i.e. ignoring any | |
1892 | // exit edges from them). | |
1893 | // | |
1894 | // NB. this has an edge case with non-returning statements, | |
1895 | // like `loop {}` or `panic!()`: control flow never reaches | |
1896 | // the exit node through these, so one can have a function | |
1897 | // that never actually calls itselfs but is still picked up by | |
1898 | // this lint: | |
1899 | // | |
1900 | // fn f(cond: bool) { | |
1901 | // if !cond { panic!() } // could come from `assert!(cond)` | |
1902 | // f(false) | |
1903 | // } | |
1904 | // | |
1905 | // In general, functions of that form may be able to call | |
1906 | // itself a finite number of times and then diverge. The lint | |
1907 | // considers this to be an error for two reasons, (a) it is | |
1908 | // easier to implement, and (b) it seems rare to actually want | |
1909 | // to have behaviour like the above, rather than | |
1910 | // e.g. accidentally recurring after an assert. | |
1911 | ||
1912 | let cfg = cfg::CFG::new(cx.tcx, blk); | |
1913 | ||
1914 | let mut work_queue = vec![cfg.entry]; | |
1915 | let mut reached_exit_without_self_call = false; | |
1916 | let mut self_call_spans = vec![]; | |
1917 | let mut visited = BitSet::new(); | |
1918 | ||
1919 | while let Some(idx) = work_queue.pop() { | |
1920 | if idx == cfg.exit { | |
1921 | // found a path! | |
1922 | reached_exit_without_self_call = true; | |
1923 | break; | |
1924 | } | |
1925 | ||
1926 | let cfg_id = idx.node_id(); | |
1927 | if visited.contains(&cfg_id) { | |
1928 | // already done | |
1929 | continue; | |
1930 | } | |
1931 | visited.insert(cfg_id); | |
1932 | ||
1933 | let node_id = cfg.graph.node_data(idx).id(); | |
1934 | ||
1935 | // is this a recursive call? | |
1936 | if node_id != ast::DUMMY_NODE_ID && checker(cx.tcx, impl_node_id, id, name, node_id) { | |
1937 | self_call_spans.push(cx.tcx.map.span(node_id)); | |
1938 | // this is a self call, so we shouldn't explore past | |
1939 | // this node in the CFG. | |
1940 | continue; | |
1941 | } | |
1942 | // add the successors of this node to explore the graph further. | |
d9579d0f | 1943 | for (_, edge) in cfg.graph.outgoing_edges(idx) { |
c34b1796 AL |
1944 | let target_idx = edge.target(); |
1945 | let target_cfg_id = target_idx.node_id(); | |
1946 | if !visited.contains(&target_cfg_id) { | |
1947 | work_queue.push(target_idx) | |
1948 | } | |
d9579d0f | 1949 | } |
c34b1796 AL |
1950 | } |
1951 | ||
1952 | // Check the number of self calls because a function that | |
1953 | // doesn't return (e.g. calls a `-> !` function or `loop { /* | |
1954 | // no break */ }`) shouldn't be linted unless it actually | |
1955 | // recurs. | |
9346a6ac | 1956 | if !reached_exit_without_self_call && !self_call_spans.is_empty() { |
c34b1796 AL |
1957 | cx.span_lint(UNCONDITIONAL_RECURSION, sp, |
1958 | "function cannot return without recurring"); | |
1959 | ||
1960 | // FIXME #19668: these could be span_lint_note's instead of this manual guard. | |
1961 | if cx.current_level(UNCONDITIONAL_RECURSION) != Level::Allow { | |
1962 | let sess = cx.sess(); | |
1963 | // offer some help to the programmer. | |
1964 | for call in &self_call_spans { | |
1965 | sess.span_note(*call, "recursive call site") | |
1966 | } | |
1967 | sess.fileline_help(sp, "a `loop` may express intention \ | |
1968 | better if this is on purpose") | |
1969 | } | |
1970 | } | |
1971 | ||
1972 | // all done | |
1973 | return; | |
1974 | ||
1975 | // Functions for identifying if the given NodeId `id` | |
1976 | // represents a call to the function `fn_id`/method | |
1977 | // `method_id`. | |
1978 | ||
1979 | fn id_refers_to_this_fn<'tcx>(tcx: &ty::ctxt<'tcx>, | |
1980 | _: ast::NodeId, | |
1981 | fn_id: ast::NodeId, | |
1982 | _: ast::Ident, | |
1983 | id: ast::NodeId) -> bool { | |
1984 | tcx.def_map.borrow().get(&id) | |
1985 | .map_or(false, |def| def.def_id() == local_def(fn_id)) | |
1986 | } | |
1987 | ||
1988 | // check if the method call `id` refers to method `method_id` | |
1989 | // (with name `method_name` contained in impl `impl_id`). | |
1990 | fn id_refers_to_this_method<'tcx>(tcx: &ty::ctxt<'tcx>, | |
1991 | impl_id: ast::NodeId, | |
1992 | method_id: ast::NodeId, | |
1993 | method_name: ast::Ident, | |
1994 | id: ast::NodeId) -> bool { | |
1995 | let did = match tcx.method_map.borrow().get(&ty::MethodCall::expr(id)) { | |
1996 | None => return false, | |
1997 | Some(m) => match m.origin { | |
1998 | // There's no way to know if a method call via a | |
1999 | // vtable is recursion, so we assume it's not. | |
2000 | ty::MethodTraitObject(_) => return false, | |
2001 | ||
2002 | // This `did` refers directly to the method definition. | |
2003 | ty::MethodStatic(did) | ty::MethodStaticClosure(did) => did, | |
2004 | ||
2005 | // MethodTypeParam are methods from traits: | |
2006 | ||
2007 | // The `impl ... for ...` of this method call | |
2008 | // isn't known, e.g. it might be a default method | |
2009 | // in a trait, so we get the def-id of the trait | |
2010 | // method instead. | |
2011 | ty::MethodTypeParam( | |
2012 | ty::MethodParam { ref trait_ref, method_num, impl_def_id: None, }) => { | |
2013 | ty::trait_item(tcx, trait_ref.def_id, method_num).def_id() | |
2014 | } | |
2015 | ||
2016 | // The `impl` is known, so we check that with a | |
2017 | // special case: | |
2018 | ty::MethodTypeParam( | |
2019 | ty::MethodParam { impl_def_id: Some(impl_def_id), .. }) => { | |
2020 | ||
2021 | let name = match tcx.map.expect_expr(id).node { | |
2022 | ast::ExprMethodCall(ref sp_ident, _, _) => sp_ident.node, | |
2023 | _ => tcx.sess.span_bug( | |
2024 | tcx.map.span(id), | |
2025 | "non-method call expr behaving like a method call?") | |
2026 | }; | |
2027 | // It matches if it comes from the same impl, | |
2028 | // and has the same method name. | |
2029 | return ast_util::is_local(impl_def_id) | |
2030 | && impl_def_id.node == impl_id | |
2031 | && method_name.name == name.name | |
2032 | } | |
2033 | } | |
2034 | }; | |
2035 | ||
2036 | ast_util::is_local(did) && did.node == method_id | |
2037 | } | |
2038 | } | |
2039 | } | |
2040 | ||
2041 | declare_lint! { | |
2042 | PLUGIN_AS_LIBRARY, | |
2043 | Warn, | |
2044 | "compiler plugin used as ordinary library in non-plugin crate" | |
2045 | } | |
2046 | ||
2047 | #[derive(Copy, Clone)] | |
2048 | pub struct PluginAsLibrary; | |
2049 | ||
2050 | impl LintPass for PluginAsLibrary { | |
2051 | fn get_lints(&self) -> LintArray { | |
2052 | lint_array![PLUGIN_AS_LIBRARY] | |
2053 | } | |
2054 | ||
2055 | fn check_item(&mut self, cx: &Context, it: &ast::Item) { | |
2056 | if cx.sess().plugin_registrar_fn.get().is_some() { | |
2057 | // We're compiling a plugin; it's fine to link other plugins. | |
2058 | return; | |
2059 | } | |
2060 | ||
2061 | match it.node { | |
2062 | ast::ItemExternCrate(..) => (), | |
2063 | _ => return, | |
2064 | }; | |
2065 | ||
2066 | let md = match cx.sess().cstore.find_extern_mod_stmt_cnum(it.id) { | |
2067 | Some(cnum) => cx.sess().cstore.get_crate_data(cnum), | |
2068 | None => { | |
2069 | // Probably means we aren't linking the crate for some reason. | |
2070 | // | |
2071 | // Not sure if / when this could happen. | |
2072 | return; | |
2073 | } | |
2074 | }; | |
2075 | ||
2076 | if decoder::get_plugin_registrar_fn(md.data()).is_some() { | |
2077 | cx.span_lint(PLUGIN_AS_LIBRARY, it.span, | |
2078 | "compiler plugin used as an ordinary library"); | |
2079 | } | |
2080 | } | |
2081 | } | |
2082 | ||
2083 | declare_lint! { | |
2084 | PRIVATE_NO_MANGLE_FNS, | |
2085 | Warn, | |
2086 | "functions marked #[no_mangle] should be exported" | |
2087 | } | |
2088 | ||
2089 | declare_lint! { | |
2090 | PRIVATE_NO_MANGLE_STATICS, | |
2091 | Warn, | |
2092 | "statics marked #[no_mangle] should be exported" | |
2093 | } | |
2094 | ||
2095 | declare_lint! { | |
2096 | NO_MANGLE_CONST_ITEMS, | |
2097 | Deny, | |
2098 | "const items will not have their symbols exported" | |
2099 | } | |
2100 | ||
2101 | #[derive(Copy, Clone)] | |
2102 | pub struct InvalidNoMangleItems; | |
2103 | ||
2104 | impl LintPass for InvalidNoMangleItems { | |
2105 | fn get_lints(&self) -> LintArray { | |
2106 | lint_array!(PRIVATE_NO_MANGLE_FNS, | |
2107 | PRIVATE_NO_MANGLE_STATICS, | |
2108 | NO_MANGLE_CONST_ITEMS) | |
2109 | } | |
2110 | ||
2111 | fn check_item(&mut self, cx: &Context, it: &ast::Item) { | |
2112 | match it.node { | |
2113 | ast::ItemFn(..) => { | |
2114 | if attr::contains_name(&it.attrs, "no_mangle") && | |
2115 | !cx.exported_items.contains(&it.id) { | |
2116 | let msg = format!("function {} is marked #[no_mangle], but not exported", | |
2117 | it.ident); | |
2118 | cx.span_lint(PRIVATE_NO_MANGLE_FNS, it.span, &msg); | |
2119 | } | |
2120 | }, | |
2121 | ast::ItemStatic(..) => { | |
2122 | if attr::contains_name(&it.attrs, "no_mangle") && | |
2123 | !cx.exported_items.contains(&it.id) { | |
2124 | let msg = format!("static {} is marked #[no_mangle], but not exported", | |
2125 | it.ident); | |
2126 | cx.span_lint(PRIVATE_NO_MANGLE_STATICS, it.span, &msg); | |
2127 | } | |
2128 | }, | |
2129 | ast::ItemConst(..) => { | |
2130 | if attr::contains_name(&it.attrs, "no_mangle") { | |
2131 | // Const items do not refer to a particular location in memory, and therefore | |
2132 | // don't have anything to attach a symbol to | |
2133 | let msg = "const items should never be #[no_mangle], consider instead using \ | |
2134 | `pub static`"; | |
2135 | cx.span_lint(NO_MANGLE_CONST_ITEMS, it.span, msg); | |
2136 | } | |
2137 | } | |
2138 | _ => {}, | |
2139 | } | |
2140 | } | |
2141 | } | |
2142 | ||
bd371182 AL |
2143 | #[derive(Clone, Copy)] |
2144 | pub struct MutableTransmutes; | |
2145 | ||
2146 | declare_lint! { | |
2147 | MUTABLE_TRANSMUTES, | |
2148 | Deny, | |
2149 | "mutating transmuted &mut T from &T may cause undefined behavior" | |
2150 | } | |
2151 | ||
2152 | impl LintPass for MutableTransmutes { | |
2153 | fn get_lints(&self) -> LintArray { | |
2154 | lint_array!(MUTABLE_TRANSMUTES) | |
2155 | } | |
2156 | ||
2157 | fn check_expr(&mut self, cx: &Context, expr: &ast::Expr) { | |
2158 | use syntax::ast::DefId; | |
2159 | use syntax::abi::RustIntrinsic; | |
2160 | let msg = "mutating transmuted &mut T from &T may cause undefined behavior,\ | |
2161 | consider instead using an UnsafeCell"; | |
2162 | match get_transmute_from_to(cx, expr) { | |
62682a34 | 2163 | Some((&ty::TyRef(_, from_mt), &ty::TyRef(_, to_mt))) => { |
bd371182 AL |
2164 | if to_mt.mutbl == ast::Mutability::MutMutable |
2165 | && from_mt.mutbl == ast::Mutability::MutImmutable { | |
2166 | cx.span_lint(MUTABLE_TRANSMUTES, expr.span, msg); | |
2167 | } | |
2168 | } | |
2169 | _ => () | |
2170 | } | |
2171 | ||
2172 | fn get_transmute_from_to<'a, 'tcx>(cx: &Context<'a, 'tcx>, expr: &ast::Expr) | |
62682a34 | 2173 | -> Option<(&'tcx ty::TypeVariants<'tcx>, &'tcx ty::TypeVariants<'tcx>)> { |
bd371182 AL |
2174 | match expr.node { |
2175 | ast::ExprPath(..) => (), | |
2176 | _ => return None | |
2177 | } | |
2178 | if let DefFn(did, _) = ty::resolve_expr(cx.tcx, expr) { | |
2179 | if !def_id_is_transmute(cx, did) { | |
2180 | return None; | |
2181 | } | |
2182 | let typ = ty::node_id_to_type(cx.tcx, expr.id); | |
2183 | match typ.sty { | |
62682a34 | 2184 | ty::TyBareFn(_, ref bare_fn) if bare_fn.abi == RustIntrinsic => { |
bd371182 AL |
2185 | if let ty::FnConverging(to) = bare_fn.sig.0.output { |
2186 | let from = bare_fn.sig.0.inputs[0]; | |
2187 | return Some((&from.sty, &to.sty)); | |
2188 | } | |
2189 | }, | |
2190 | _ => () | |
2191 | } | |
2192 | } | |
2193 | None | |
2194 | } | |
2195 | ||
2196 | fn def_id_is_transmute(cx: &Context, def_id: DefId) -> bool { | |
2197 | match ty::lookup_item_type(cx.tcx, def_id).ty.sty { | |
62682a34 | 2198 | ty::TyBareFn(_, ref bfty) if bfty.abi == RustIntrinsic => (), |
bd371182 AL |
2199 | _ => return false |
2200 | } | |
2201 | ty::with_path(cx.tcx, def_id, |path| match path.last() { | |
2202 | Some(ref last) => last.name().as_str() == "transmute", | |
2203 | _ => false | |
2204 | }) | |
2205 | } | |
2206 | } | |
2207 | } | |
2208 | ||
c34b1796 AL |
2209 | /// Forbids using the `#[feature(...)]` attribute |
2210 | #[derive(Copy, Clone)] | |
2211 | pub struct UnstableFeatures; | |
2212 | ||
2213 | declare_lint! { | |
2214 | UNSTABLE_FEATURES, | |
2215 | Allow, | |
62682a34 | 2216 | "enabling unstable features (deprecated. do not use)" |
c34b1796 AL |
2217 | } |
2218 | ||
2219 | impl LintPass for UnstableFeatures { | |
2220 | fn get_lints(&self) -> LintArray { | |
2221 | lint_array!(UNSTABLE_FEATURES) | |
2222 | } | |
2223 | fn check_attribute(&mut self, ctx: &Context, attr: &ast::Attribute) { | |
2224 | if attr::contains_name(&[attr.node.value.clone()], "feature") { | |
62682a34 SL |
2225 | if let Some(items) = attr.node.value.meta_item_list() { |
2226 | for item in items { | |
2227 | ctx.span_lint(UNSTABLE_FEATURES, item.span, "unstable feature"); | |
2228 | } | |
2229 | } | |
c34b1796 AL |
2230 | } |
2231 | } | |
2232 | } | |
bd371182 AL |
2233 | |
2234 | /// Lints for attempts to impl Drop on types that have `#[repr(C)]` | |
2235 | /// attribute (see issue #24585). | |
2236 | #[derive(Copy, Clone)] | |
2237 | pub struct DropWithReprExtern; | |
2238 | ||
2239 | declare_lint! { | |
2240 | DROP_WITH_REPR_EXTERN, | |
2241 | Warn, | |
2242 | "use of #[repr(C)] on a type that implements Drop" | |
2243 | } | |
2244 | ||
2245 | impl LintPass for DropWithReprExtern { | |
2246 | fn get_lints(&self) -> LintArray { | |
2247 | lint_array!(DROP_WITH_REPR_EXTERN) | |
2248 | } | |
2249 | fn check_crate(&mut self, ctx: &Context, _: &ast::Crate) { | |
2250 | for dtor_did in ctx.tcx.destructors.borrow().iter() { | |
2251 | let (drop_impl_did, dtor_self_type) = | |
2252 | if dtor_did.krate == ast::LOCAL_CRATE { | |
2253 | let impl_did = ctx.tcx.map.get_parent_did(dtor_did.node); | |
2254 | let ty = ty::lookup_item_type(ctx.tcx, impl_did).ty; | |
2255 | (impl_did, ty) | |
2256 | } else { | |
2257 | continue; | |
2258 | }; | |
2259 | ||
2260 | match dtor_self_type.sty { | |
62682a34 SL |
2261 | ty::TyEnum(self_type_did, _) | |
2262 | ty::TyStruct(self_type_did, _) | | |
2263 | ty::TyClosure(self_type_did, _) => { | |
bd371182 AL |
2264 | let hints = ty::lookup_repr_hints(ctx.tcx, self_type_did); |
2265 | if hints.iter().any(|attr| *attr == attr::ReprExtern) && | |
2266 | ty::ty_dtor(ctx.tcx, self_type_did).has_drop_flag() { | |
2267 | let drop_impl_span = ctx.tcx.map.def_id_span(drop_impl_did, | |
2268 | codemap::DUMMY_SP); | |
2269 | let self_defn_span = ctx.tcx.map.def_id_span(self_type_did, | |
2270 | codemap::DUMMY_SP); | |
2271 | ctx.span_lint(DROP_WITH_REPR_EXTERN, | |
2272 | drop_impl_span, | |
2273 | "implementing Drop adds hidden state to types, \ | |
2274 | possibly conflicting with `#[repr(C)]`"); | |
2275 | // FIXME #19668: could be span_lint_note instead of manual guard. | |
2276 | if ctx.current_level(DROP_WITH_REPR_EXTERN) != Level::Allow { | |
2277 | ctx.sess().span_note(self_defn_span, | |
2278 | "the `#[repr(C)]` attribute is attached here"); | |
2279 | } | |
2280 | } | |
2281 | } | |
2282 | _ => {} | |
2283 | } | |
2284 | } | |
2285 | } | |
2286 | } |