]> git.proxmox.com Git - rustc.git/blame - src/librustc_lint/builtin.rs
New upstream version 1.14.0+dfsg1
[rustc.git] / src / librustc_lint / builtin.rs
CommitLineData
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
54a0048b 31use rustc::hir::def::Def;
54a0048b
SL
32use rustc::hir::def_id::DefId;
33use middle::stability;
a7813a04 34use rustc::cfg;
54a0048b
SL
35use rustc::ty::subst::Substs;
36use rustc::ty::{self, Ty, TyCtxt};
5bcae85e 37use rustc::traits::{self, Reveal};
54a0048b 38use rustc::hir::map as hir_map;
c30ab7b3 39use util::nodemap::NodeSet;
b039eaaf 40use lint::{Level, LateContext, LintContext, LintArray, Lint};
c30ab7b3 41use lint::{LintPass, LateLintPass, EarlyLintPass, EarlyContext};
c34b1796 42
e9174d1e 43use std::collections::HashSet;
c34b1796 44
c30ab7b3 45use syntax::ast;
9e0c209e 46use syntax::attr;
c30ab7b3
SL
47use syntax::feature_gate::{AttributeGate, AttributeType, Stability, deprecated_attributes};
48use syntax_pos::Span;
e9174d1e 49
54a0048b
SL
50use rustc::hir::{self, PatKind};
51use rustc::hir::intravisit::FnKind;
e9174d1e 52
b039eaaf 53use bad_style::{MethodLateContext, method_context};
c34b1796 54
b039eaaf
SL
55// hardwired lints from librustc
56pub use lint::builtin::*;
c34b1796 57
b039eaaf
SL
58declare_lint! {
59 WHILE_TRUE,
60 Warn,
61 "suggest using `loop { }` instead of `while true { }`"
62}
c34b1796 63
b039eaaf
SL
64#[derive(Copy, Clone)]
65pub struct WhileTrue;
66
67impl LintPass for WhileTrue {
68 fn get_lints(&self) -> LintArray {
69 lint_array!(WHILE_TRUE)
c34b1796 70 }
b039eaaf 71}
c34b1796 72
b039eaaf
SL
73impl LateLintPass for WhileTrue {
74 fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
9e0c209e 75 if let hir::ExprWhile(ref cond, ..) = e.node {
b039eaaf 76 if let hir::ExprLit(ref lit) = cond.node {
7453a54e 77 if let ast::LitKind::Bool(true) = lit.node {
c30ab7b3
SL
78 cx.span_lint(WHILE_TRUE,
79 e.span,
b039eaaf
SL
80 "denote infinite loops with loop { ... }");
81 }
c34b1796
AL
82 }
83 }
84 }
85}
86
87declare_lint! {
b039eaaf
SL
88 BOX_POINTERS,
89 Allow,
90 "use of owned (Box type) heap memory"
c34b1796
AL
91}
92
93#[derive(Copy, Clone)]
b039eaaf
SL
94pub struct BoxPointers;
95
96impl BoxPointers {
c30ab7b3 97 fn check_heap_type<'a, 'tcx>(&self, cx: &LateContext<'a, 'tcx>, span: Span, ty: Ty<'tcx>) {
b039eaaf
SL
98 for leaf_ty in ty.walk() {
99 if let ty::TyBox(_) = leaf_ty.sty {
100 let m = format!("type uses owned (Box type) pointers: {}", ty);
101 cx.span_lint(BOX_POINTERS, span, &m);
c34b1796
AL
102 }
103 }
104 }
105}
106
b039eaaf 107impl LintPass for BoxPointers {
c34b1796 108 fn get_lints(&self) -> LintArray {
b039eaaf 109 lint_array!(BOX_POINTERS)
c34b1796 110 }
b039eaaf 111}
c34b1796 112
b039eaaf
SL
113impl LateLintPass for BoxPointers {
114 fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
c34b1796 115 match it.node {
b039eaaf
SL
116 hir::ItemFn(..) |
117 hir::ItemTy(..) |
118 hir::ItemEnum(..) |
9e0c209e 119 hir::ItemStruct(..) |
c30ab7b3
SL
120 hir::ItemUnion(..) => {
121 self.check_heap_type(cx, it.span, cx.tcx.tables().node_id_to_type(it.id))
122 }
123 _ => (),
d9579d0f 124 }
d9579d0f 125
b039eaaf
SL
126 // If it's a struct, we also have to check the fields' types
127 match it.node {
9e0c209e
SL
128 hir::ItemStruct(ref struct_def, _) |
129 hir::ItemUnion(ref struct_def, _) => {
b039eaaf 130 for struct_field in struct_def.fields() {
c30ab7b3
SL
131 self.check_heap_type(cx,
132 struct_field.span,
133 cx.tcx.tables().node_id_to_type(struct_field.id));
b039eaaf 134 }
d9579d0f 135 }
c30ab7b3 136 _ => (),
d9579d0f
AL
137 }
138 }
139
b039eaaf 140 fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
c30ab7b3 141 let ty = cx.tcx.tables().node_id_to_type(e.id);
b039eaaf 142 self.check_heap_type(cx, e.span, ty);
c34b1796
AL
143 }
144}
145
c34b1796
AL
146declare_lint! {
147 NON_SHORTHAND_FIELD_PATTERNS,
148 Warn,
149 "using `Struct { x: x }` instead of `Struct { x }`"
150}
151
152#[derive(Copy, Clone)]
153pub struct NonShorthandFieldPatterns;
154
155impl LintPass for NonShorthandFieldPatterns {
156 fn get_lints(&self) -> LintArray {
157 lint_array!(NON_SHORTHAND_FIELD_PATTERNS)
158 }
b039eaaf 159}
c34b1796 160
b039eaaf
SL
161impl LateLintPass for NonShorthandFieldPatterns {
162 fn check_pat(&mut self, cx: &LateContext, pat: &hir::Pat) {
3157f602
XL
163 if let PatKind::Struct(_, ref field_pats, _) = pat.node {
164 for fieldpat in field_pats {
c34b1796 165 if fieldpat.node.is_shorthand {
3157f602 166 continue;
b039eaaf 167 }
3157f602
XL
168 if let PatKind::Binding(_, ident, None) = fieldpat.node.pat.node {
169 if ident.node == fieldpat.node.name {
c30ab7b3
SL
170 cx.span_lint(NON_SHORTHAND_FIELD_PATTERNS,
171 fieldpat.span,
c34b1796 172 &format!("the `{}:` in this pattern is redundant and can \
c30ab7b3
SL
173 be removed",
174 ident.node))
c34b1796
AL
175 }
176 }
177 }
178 }
179 }
180}
181
c34b1796
AL
182declare_lint! {
183 UNSAFE_CODE,
184 Allow,
185 "usage of `unsafe` code"
186}
187
188#[derive(Copy, Clone)]
189pub struct UnsafeCode;
190
191impl LintPass for UnsafeCode {
192 fn get_lints(&self) -> LintArray {
193 lint_array!(UNSAFE_CODE)
194 }
b039eaaf 195}
c34b1796 196
b039eaaf
SL
197impl LateLintPass for UnsafeCode {
198 fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
e9174d1e 199 if let hir::ExprBlock(ref blk) = e.node {
c34b1796 200 // Don't warn about generated blocks, that'll just pollute the output.
e9174d1e 201 if blk.rules == hir::UnsafeBlock(hir::UserProvided) {
c34b1796
AL
202 cx.span_lint(UNSAFE_CODE, blk.span, "usage of an `unsafe` block");
203 }
204 }
205 }
206
b039eaaf 207 fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
c34b1796 208 match it.node {
c30ab7b3
SL
209 hir::ItemTrait(hir::Unsafety::Unsafe, ..) => {
210 cx.span_lint(UNSAFE_CODE, it.span, "declaration of an `unsafe` trait")
211 }
c34b1796 212
c30ab7b3
SL
213 hir::ItemImpl(hir::Unsafety::Unsafe, ..) => {
214 cx.span_lint(UNSAFE_CODE, it.span, "implementation of an `unsafe` trait")
215 }
c34b1796
AL
216
217 _ => return,
218 }
219 }
220
c30ab7b3
SL
221 fn check_fn(&mut self,
222 cx: &LateContext,
223 fk: FnKind,
224 _: &hir::FnDecl,
225 _: &hir::Block,
226 span: Span,
227 _: ast::NodeId) {
c34b1796 228 match fk {
c30ab7b3
SL
229 FnKind::ItemFn(_, _, hir::Unsafety::Unsafe, ..) => {
230 cx.span_lint(UNSAFE_CODE, span, "declaration of an `unsafe` function")
231 }
c34b1796 232
9e0c209e 233 FnKind::Method(_, sig, ..) => {
e9174d1e 234 if sig.unsafety == hir::Unsafety::Unsafe {
c34b1796
AL
235 cx.span_lint(UNSAFE_CODE, span, "implementation of an `unsafe` method")
236 }
c30ab7b3 237 }
c34b1796
AL
238
239 _ => (),
240 }
241 }
242
b039eaaf 243 fn check_trait_item(&mut self, cx: &LateContext, trait_item: &hir::TraitItem) {
e9174d1e
SL
244 if let hir::MethodTraitItem(ref sig, None) = trait_item.node {
245 if sig.unsafety == hir::Unsafety::Unsafe {
c30ab7b3
SL
246 cx.span_lint(UNSAFE_CODE,
247 trait_item.span,
c34b1796
AL
248 "declaration of an `unsafe` method")
249 }
250 }
251 }
252}
253
c34b1796
AL
254declare_lint! {
255 MISSING_DOCS,
256 Allow,
257 "detects missing documentation for public members"
258}
259
260pub struct MissingDoc {
261 /// Stack of IDs of struct definitions.
262 struct_def_stack: Vec<ast::NodeId>,
263
264 /// True if inside variant definition
265 in_variant: bool,
266
267 /// Stack of whether #[doc(hidden)] is set
268 /// at each level which has lint attributes.
269 doc_hidden_stack: Vec<bool>,
270
271 /// Private traits or trait items that leaked through. Don't check their methods.
272 private_traits: HashSet<ast::NodeId>,
273}
274
275impl MissingDoc {
276 pub fn new() -> MissingDoc {
277 MissingDoc {
c30ab7b3 278 struct_def_stack: vec![],
c34b1796 279 in_variant: false,
c30ab7b3 280 doc_hidden_stack: vec![false],
c34b1796
AL
281 private_traits: HashSet::new(),
282 }
283 }
284
285 fn doc_hidden(&self) -> bool {
286 *self.doc_hidden_stack.last().expect("empty doc_hidden_stack")
287 }
288
289 fn check_missing_docs_attrs(&self,
c30ab7b3
SL
290 cx: &LateContext,
291 id: Option<ast::NodeId>,
292 attrs: &[ast::Attribute],
293 sp: Span,
294 desc: &'static str) {
c34b1796
AL
295 // If we're building a test harness, then warning about
296 // documentation is probably not really relevant right now.
297 if cx.sess().opts.test {
298 return;
299 }
300
301 // `#[doc(hidden)]` disables missing_docs check.
302 if self.doc_hidden() {
303 return;
304 }
305
306 // Only check publicly-visible items, using the result from the privacy pass.
307 // It's an option so the crate root can also use this function (it doesn't
308 // have a NodeId).
92a42be0
SL
309 if let Some(id) = id {
310 if !cx.access_levels.is_exported(id) {
c34b1796
AL
311 return;
312 }
313 }
314
5bcae85e 315 let has_doc = attrs.iter().any(|a| a.is_value_str() && a.name() == "doc");
c34b1796 316 if !has_doc {
c30ab7b3
SL
317 cx.span_lint(MISSING_DOCS,
318 sp,
c34b1796
AL
319 &format!("missing documentation for {}", desc));
320 }
321 }
322}
323
324impl LintPass for MissingDoc {
325 fn get_lints(&self) -> LintArray {
326 lint_array!(MISSING_DOCS)
327 }
b039eaaf 328}
c34b1796 329
b039eaaf
SL
330impl LateLintPass for MissingDoc {
331 fn enter_lint_attrs(&mut self, _: &LateContext, attrs: &[ast::Attribute]) {
c30ab7b3
SL
332 let doc_hidden = self.doc_hidden() ||
333 attrs.iter().any(|attr| {
334 attr.check_name("doc") &&
335 match attr.meta_item_list() {
c34b1796 336 None => false,
9e0c209e 337 Some(l) => attr::list_contains_name(&l[..], "hidden"),
c34b1796
AL
338 }
339 });
340 self.doc_hidden_stack.push(doc_hidden);
341 }
342
b039eaaf 343 fn exit_lint_attrs(&mut self, _: &LateContext, _: &[ast::Attribute]) {
c34b1796
AL
344 self.doc_hidden_stack.pop().expect("empty doc_hidden_stack");
345 }
346
c30ab7b3
SL
347 fn check_struct_def(&mut self,
348 _: &LateContext,
349 _: &hir::VariantData,
350 _: ast::Name,
351 _: &hir::Generics,
352 item_id: ast::NodeId) {
b039eaaf 353 self.struct_def_stack.push(item_id);
c34b1796
AL
354 }
355
c30ab7b3
SL
356 fn check_struct_def_post(&mut self,
357 _: &LateContext,
358 _: &hir::VariantData,
359 _: ast::Name,
360 _: &hir::Generics,
361 item_id: ast::NodeId) {
c34b1796 362 let popped = self.struct_def_stack.pop().expect("empty struct_def_stack");
b039eaaf 363 assert!(popped == item_id);
c34b1796
AL
364 }
365
b039eaaf 366 fn check_crate(&mut self, cx: &LateContext, krate: &hir::Crate) {
c34b1796
AL
367 self.check_missing_docs_attrs(cx, None, &krate.attrs, krate.span, "crate");
368 }
369
b039eaaf 370 fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
c34b1796 371 let desc = match it.node {
e9174d1e
SL
372 hir::ItemFn(..) => "a function",
373 hir::ItemMod(..) => "a module",
374 hir::ItemEnum(..) => "an enum",
375 hir::ItemStruct(..) => "a struct",
9e0c209e
SL
376 hir::ItemUnion(..) => "a union",
377 hir::ItemTrait(.., ref items) => {
c34b1796 378 // Issue #11592, traits are always considered exported, even when private.
e9174d1e 379 if it.vis == hir::Visibility::Inherited {
c34b1796
AL
380 self.private_traits.insert(it.id);
381 for itm in items {
382 self.private_traits.insert(itm.id);
383 }
c30ab7b3 384 return;
c34b1796
AL
385 }
386 "a trait"
c30ab7b3 387 }
e9174d1e 388 hir::ItemTy(..) => "a type alias",
9e0c209e 389 hir::ItemImpl(.., Some(ref trait_ref), _, ref impl_items) => {
c34b1796
AL
390 // If the trait is private, add the impl items to private_traits so they don't get
391 // reported for missing docs.
3157f602 392 let real_trait = cx.tcx.expect_def(trait_ref.ref_id).def_id();
b039eaaf
SL
393 if let Some(node_id) = cx.tcx.map.as_local_node_id(real_trait) {
394 match cx.tcx.map.find(node_id) {
c30ab7b3
SL
395 Some(hir_map::NodeItem(item)) => {
396 if item.vis == hir::Visibility::Inherited {
397 for itm in impl_items {
398 self.private_traits.insert(itm.id);
399 }
b039eaaf 400 }
c30ab7b3
SL
401 }
402 _ => {}
b039eaaf 403 }
c34b1796 404 }
c30ab7b3
SL
405 return;
406 }
e9174d1e
SL
407 hir::ItemConst(..) => "a constant",
408 hir::ItemStatic(..) => "a static",
c30ab7b3 409 _ => return,
c34b1796
AL
410 };
411
412 self.check_missing_docs_attrs(cx, Some(it.id), &it.attrs, it.span, desc);
413 }
414
b039eaaf 415 fn check_trait_item(&mut self, cx: &LateContext, trait_item: &hir::TraitItem) {
c30ab7b3
SL
416 if self.private_traits.contains(&trait_item.id) {
417 return;
418 }
c34b1796
AL
419
420 let desc = match trait_item.node {
e9174d1e
SL
421 hir::ConstTraitItem(..) => "an associated constant",
422 hir::MethodTraitItem(..) => "a trait method",
423 hir::TypeTraitItem(..) => "an associated type",
c34b1796
AL
424 };
425
c30ab7b3
SL
426 self.check_missing_docs_attrs(cx,
427 Some(trait_item.id),
c34b1796 428 &trait_item.attrs,
c30ab7b3
SL
429 trait_item.span,
430 desc);
c34b1796
AL
431 }
432
b039eaaf 433 fn check_impl_item(&mut self, cx: &LateContext, impl_item: &hir::ImplItem) {
c34b1796 434 // If the method is an impl for a trait, don't doc.
b039eaaf 435 if method_context(cx, impl_item.id, impl_item.span) == MethodLateContext::TraitImpl {
c34b1796
AL
436 return;
437 }
438
439 let desc = match impl_item.node {
92a42be0
SL
440 hir::ImplItemKind::Const(..) => "an associated constant",
441 hir::ImplItemKind::Method(..) => "a method",
442 hir::ImplItemKind::Type(_) => "an associated type",
c34b1796 443 };
c30ab7b3
SL
444 self.check_missing_docs_attrs(cx,
445 Some(impl_item.id),
c34b1796 446 &impl_item.attrs,
c30ab7b3
SL
447 impl_item.span,
448 desc);
c34b1796
AL
449 }
450
b039eaaf 451 fn check_struct_field(&mut self, cx: &LateContext, sf: &hir::StructField) {
54a0048b
SL
452 if !sf.is_positional() {
453 if sf.vis == hir::Public || self.in_variant {
c30ab7b3
SL
454 let cur_struct_def = *self.struct_def_stack
455 .last()
c34b1796 456 .expect("empty struct_def_stack");
c30ab7b3
SL
457 self.check_missing_docs_attrs(cx,
458 Some(cur_struct_def),
459 &sf.attrs,
460 sf.span,
c34b1796
AL
461 "a struct field")
462 }
463 }
464 }
465
b039eaaf 466 fn check_variant(&mut self, cx: &LateContext, v: &hir::Variant, _: &hir::Generics) {
c30ab7b3
SL
467 self.check_missing_docs_attrs(cx,
468 Some(v.node.data.id()),
469 &v.node.attrs,
470 v.span,
471 "a variant");
c34b1796
AL
472 assert!(!self.in_variant);
473 self.in_variant = true;
474 }
475
b039eaaf 476 fn check_variant_post(&mut self, _: &LateContext, _: &hir::Variant, _: &hir::Generics) {
c34b1796
AL
477 assert!(self.in_variant);
478 self.in_variant = false;
479 }
480}
481
482declare_lint! {
483 pub MISSING_COPY_IMPLEMENTATIONS,
484 Allow,
485 "detects potentially-forgotten implementations of `Copy`"
486}
487
488#[derive(Copy, Clone)]
489pub struct MissingCopyImplementations;
490
491impl LintPass for MissingCopyImplementations {
492 fn get_lints(&self) -> LintArray {
493 lint_array!(MISSING_COPY_IMPLEMENTATIONS)
494 }
b039eaaf 495}
c34b1796 496
b039eaaf
SL
497impl LateLintPass for MissingCopyImplementations {
498 fn check_item(&mut self, cx: &LateContext, item: &hir::Item) {
92a42be0 499 if !cx.access_levels.is_reachable(item.id) {
c34b1796
AL
500 return;
501 }
e9174d1e
SL
502 let (def, ty) = match item.node {
503 hir::ItemStruct(_, ref ast_generics) => {
c34b1796
AL
504 if ast_generics.is_parameterized() {
505 return;
506 }
b039eaaf 507 let def = cx.tcx.lookup_adt_def(cx.tcx.map.local_def_id(item.id));
c30ab7b3 508 (def, cx.tcx.mk_adt(def, cx.tcx.intern_substs(&[])))
9e0c209e
SL
509 }
510 hir::ItemUnion(_, ref ast_generics) => {
511 if ast_generics.is_parameterized() {
512 return;
513 }
514 let def = cx.tcx.lookup_adt_def(cx.tcx.map.local_def_id(item.id));
c30ab7b3 515 (def, cx.tcx.mk_adt(def, cx.tcx.intern_substs(&[])))
c34b1796 516 }
e9174d1e 517 hir::ItemEnum(_, ref ast_generics) => {
c34b1796
AL
518 if ast_generics.is_parameterized() {
519 return;
520 }
b039eaaf 521 let def = cx.tcx.lookup_adt_def(cx.tcx.map.local_def_id(item.id));
c30ab7b3 522 (def, cx.tcx.mk_adt(def, cx.tcx.intern_substs(&[])))
c34b1796
AL
523 }
524 _ => return,
525 };
c30ab7b3
SL
526 if def.has_dtor() {
527 return;
528 }
c1a9b12d
SL
529 let parameter_environment = cx.tcx.empty_parameter_environment();
530 // FIXME (@jroesch) should probably inver this so that the parameter env still impls this
531 // method
a7813a04 532 if !ty.moves_by_default(cx.tcx, &parameter_environment, item.span) {
c34b1796
AL
533 return;
534 }
a7813a04 535 if parameter_environment.can_type_implement_copy(cx.tcx, ty, item.span).is_ok() {
c34b1796
AL
536 cx.span_lint(MISSING_COPY_IMPLEMENTATIONS,
537 item.span,
538 "type could implement `Copy`; consider adding `impl \
539 Copy`")
540 }
541 }
542}
543
544declare_lint! {
545 MISSING_DEBUG_IMPLEMENTATIONS,
546 Allow,
547 "detects missing implementations of fmt::Debug"
548}
549
550pub struct MissingDebugImplementations {
551 impling_types: Option<NodeSet>,
552}
553
554impl MissingDebugImplementations {
555 pub fn new() -> MissingDebugImplementations {
c30ab7b3 556 MissingDebugImplementations { impling_types: None }
c34b1796
AL
557 }
558}
559
560impl LintPass for MissingDebugImplementations {
561 fn get_lints(&self) -> LintArray {
562 lint_array!(MISSING_DEBUG_IMPLEMENTATIONS)
563 }
b039eaaf 564}
c34b1796 565
b039eaaf
SL
566impl LateLintPass for MissingDebugImplementations {
567 fn check_item(&mut self, cx: &LateContext, item: &hir::Item) {
92a42be0 568 if !cx.access_levels.is_reachable(item.id) {
c34b1796
AL
569 return;
570 }
571
572 match item.node {
c30ab7b3
SL
573 hir::ItemStruct(..) |
574 hir::ItemUnion(..) |
575 hir::ItemEnum(..) => {}
c34b1796
AL
576 _ => return,
577 }
578
579 let debug = match cx.tcx.lang_items.debug_trait() {
580 Some(debug) => debug,
581 None => return,
582 };
583
584 if self.impling_types.is_none() {
c1a9b12d 585 let debug_def = cx.tcx.lookup_trait_def(debug);
d9579d0f
AL
586 let mut impls = NodeSet();
587 debug_def.for_each_impl(cx.tcx, |d| {
b039eaaf 588 if let Some(n) = cx.tcx.map.as_local_node_id(d) {
c30ab7b3 589 if let Some(ty_def) = cx.tcx.tables().node_id_to_type(n).ty_to_def_id() {
b039eaaf
SL
590 if let Some(node_id) = cx.tcx.map.as_local_node_id(ty_def) {
591 impls.insert(node_id);
592 }
d9579d0f 593 }
c34b1796 594 }
d9579d0f
AL
595 });
596
c34b1796
AL
597 self.impling_types = Some(impls);
598 debug!("{:?}", self.impling_types);
599 }
600
601 if !self.impling_types.as_ref().unwrap().contains(&item.id) {
602 cx.span_lint(MISSING_DEBUG_IMPLEMENTATIONS,
603 item.span,
604 "type does not implement `fmt::Debug`; consider adding #[derive(Debug)] \
605 or a manual implementation")
606 }
607 }
608}
609
610declare_lint! {
611 DEPRECATED,
612 Warn,
9cc50fc6 613 "detects use of deprecated items"
c34b1796
AL
614}
615
9cc50fc6 616/// Checks for use of items with `#[deprecated]` or `#[rustc_deprecated]` attributes
5bcae85e
SL
617#[derive(Clone)]
618pub struct Deprecated {
619 /// Tracks the `NodeId` of the current item.
620 ///
621 /// This is required since not all node ids are present in the hir map.
622 current_item: ast::NodeId,
623}
c34b1796 624
9cc50fc6 625impl Deprecated {
5bcae85e 626 pub fn new() -> Deprecated {
c30ab7b3 627 Deprecated { current_item: ast::CRATE_NODE_ID }
5bcae85e
SL
628 }
629
c30ab7b3
SL
630 fn lint(&self,
631 cx: &LateContext,
632 _id: DefId,
633 span: Span,
5bcae85e
SL
634 stability: &Option<&attr::Stability>,
635 deprecation: &Option<stability::DeprecationEntry>) {
c34b1796 636 // Deprecated attributes apply in-crate and cross-crate.
9cc50fc6
SL
637 if let Some(&attr::Stability{rustc_depr: Some(attr::RustcDeprecation{ref reason, ..}), ..})
638 = *stability {
639 output(cx, DEPRECATED, span, Some(&reason))
5bcae85e
SL
640 } else if let Some(ref depr_entry) = *deprecation {
641 if let Some(parent_depr) = cx.tcx.lookup_deprecation_entry(self.parent_def(cx)) {
642 if parent_depr.same_origin(depr_entry) {
643 return;
644 }
645 }
646
647 output(cx, DEPRECATED, span, depr_entry.attr.note.as_ref().map(|x| &**x))
9cc50fc6 648 }
c34b1796 649
9cc50fc6
SL
650 fn output(cx: &LateContext, lint: &'static Lint, span: Span, note: Option<&str>) {
651 let msg = if let Some(note) = note {
652 format!("use of deprecated item: {}", note)
653 } else {
654 format!("use of deprecated item")
c34b1796
AL
655 };
656
9cc50fc6 657 cx.span_lint(lint, span, &msg);
c34b1796
AL
658 }
659 }
5bcae85e
SL
660
661 fn push_item(&mut self, item_id: ast::NodeId) {
662 self.current_item = item_id;
663 }
664
665 fn item_post(&mut self, cx: &LateContext, item_id: ast::NodeId) {
666 assert_eq!(self.current_item, item_id);
667 self.current_item = cx.tcx.map.get_parent(item_id);
668 }
669
670 fn parent_def(&self, cx: &LateContext) -> DefId {
671 cx.tcx.map.local_def_id(self.current_item)
672 }
c34b1796
AL
673}
674
9cc50fc6 675impl LintPass for Deprecated {
c34b1796
AL
676 fn get_lints(&self) -> LintArray {
677 lint_array!(DEPRECATED)
678 }
b039eaaf 679}
c34b1796 680
9cc50fc6 681impl LateLintPass for Deprecated {
b039eaaf 682 fn check_item(&mut self, cx: &LateContext, item: &hir::Item) {
5bcae85e 683 self.push_item(item.id);
c30ab7b3
SL
684 stability::check_item(cx.tcx,
685 item,
686 false,
687 &mut |id, sp, stab, depr| self.lint(cx, id, sp, &stab, &depr));
c34b1796
AL
688 }
689
5bcae85e
SL
690 fn check_item_post(&mut self, cx: &LateContext, item: &hir::Item) {
691 self.item_post(cx, item.id);
692 }
693
b039eaaf 694 fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
c30ab7b3
SL
695 stability::check_expr(cx.tcx,
696 e,
697 &mut |id, sp, stab, depr| self.lint(cx, id, sp, &stab, &depr));
c34b1796
AL
698 }
699
b039eaaf 700 fn check_path(&mut self, cx: &LateContext, path: &hir::Path, id: ast::NodeId) {
c30ab7b3
SL
701 stability::check_path(cx.tcx,
702 path,
703 id,
704 &mut |id, sp, stab, depr| self.lint(cx, id, sp, &stab, &depr));
b039eaaf
SL
705 }
706
707 fn check_path_list_item(&mut self, cx: &LateContext, item: &hir::PathListItem) {
c30ab7b3
SL
708 stability::check_path_list_item(cx.tcx,
709 item,
710 &mut |id, sp, stab, depr| {
711 self.lint(cx, id, sp, &stab, &depr)
712 });
c34b1796
AL
713 }
714
b039eaaf 715 fn check_pat(&mut self, cx: &LateContext, pat: &hir::Pat) {
c30ab7b3
SL
716 stability::check_pat(cx.tcx,
717 pat,
718 &mut |id, sp, stab, depr| self.lint(cx, id, sp, &stab, &depr));
c34b1796 719 }
5bcae85e
SL
720
721 fn check_impl_item(&mut self, _: &LateContext, item: &hir::ImplItem) {
722 self.push_item(item.id);
723 }
724
725 fn check_impl_item_post(&mut self, cx: &LateContext, item: &hir::ImplItem) {
726 self.item_post(cx, item.id);
727 }
728
729 fn check_trait_item(&mut self, _: &LateContext, item: &hir::TraitItem) {
730 self.push_item(item.id);
731 }
732
733 fn check_trait_item_post(&mut self, cx: &LateContext, item: &hir::TraitItem) {
734 self.item_post(cx, item.id);
735 }
736
737 fn check_foreign_item(&mut self, _: &LateContext, item: &hir::ForeignItem) {
738 self.push_item(item.id);
739 }
740
741 fn check_foreign_item_post(&mut self, cx: &LateContext, item: &hir::ForeignItem) {
742 self.item_post(cx, item.id);
743 }
c34b1796
AL
744}
745
c30ab7b3
SL
746declare_lint! {
747 DEPRECATED_ATTR,
748 Warn,
749 "detects use of deprecated attributes"
750}
751
752/// Checks for use of attributes which have been deprecated.
753#[derive(Clone)]
754pub struct DeprecatedAttr {
755 // This is not free to compute, so we want to keep it around, rather than
756 // compute it for every attribute.
757 depr_attrs: Vec<&'static (&'static str, AttributeType, AttributeGate)>,
758}
759
760impl DeprecatedAttr {
761 pub fn new() -> DeprecatedAttr {
762 DeprecatedAttr {
763 depr_attrs: deprecated_attributes(),
764 }
765 }
766}
767
768impl LintPass for DeprecatedAttr {
769 fn get_lints(&self) -> LintArray {
770 lint_array!(DEPRECATED_ATTR)
771 }
772}
773
774impl EarlyLintPass for DeprecatedAttr {
775 fn check_attribute(&mut self, cx: &EarlyContext, attr: &ast::Attribute) {
776 let name = &*attr.name();
777 for &&(n, _, ref g) in &self.depr_attrs {
778 if n == name {
779 if let &AttributeGate::Gated(Stability::Deprecated(link),
780 ref name,
781 ref reason,
782 _) = g {
783 cx.span_lint(DEPRECATED,
784 attr.span,
785 &format!("use of deprecated attribute `{}`: {}. See {}",
786 name, reason, link));
787 }
788 return;
789 }
790 }
791 }
792}
793
c34b1796
AL
794declare_lint! {
795 pub UNCONDITIONAL_RECURSION,
796 Warn,
797 "functions that cannot return without calling themselves"
798}
799
800#[derive(Copy, Clone)]
801pub struct UnconditionalRecursion;
802
803
804impl LintPass for UnconditionalRecursion {
805 fn get_lints(&self) -> LintArray {
806 lint_array![UNCONDITIONAL_RECURSION]
807 }
b039eaaf 808}
c34b1796 809
b039eaaf 810impl LateLintPass for UnconditionalRecursion {
c30ab7b3
SL
811 fn check_fn(&mut self,
812 cx: &LateContext,
813 fn_kind: FnKind,
814 _: &hir::FnDecl,
815 blk: &hir::Block,
816 sp: Span,
817 id: ast::NodeId) {
c1a9b12d 818 let method = match fn_kind {
e9174d1e
SL
819 FnKind::ItemFn(..) => None,
820 FnKind::Method(..) => {
b039eaaf 821 cx.tcx.impl_or_trait_item(cx.tcx.map.local_def_id(id)).as_opt_method()
c1a9b12d 822 }
c34b1796 823 // closures can't recur, so they don't matter.
c30ab7b3 824 FnKind::Closure(_) => return,
c34b1796
AL
825 };
826
c34b1796
AL
827 // Walk through this function (say `f`) looking to see if
828 // every possible path references itself, i.e. the function is
829 // called recursively unconditionally. This is done by trying
830 // to find a path from the entry node to the exit node that
831 // *doesn't* call `f` by traversing from the entry while
832 // pretending that calls of `f` are sinks (i.e. ignoring any
833 // exit edges from them).
834 //
835 // NB. this has an edge case with non-returning statements,
836 // like `loop {}` or `panic!()`: control flow never reaches
837 // the exit node through these, so one can have a function
838 // that never actually calls itselfs but is still picked up by
839 // this lint:
840 //
841 // fn f(cond: bool) {
842 // if !cond { panic!() } // could come from `assert!(cond)`
843 // f(false)
844 // }
845 //
846 // In general, functions of that form may be able to call
847 // itself a finite number of times and then diverge. The lint
848 // considers this to be an error for two reasons, (a) it is
849 // easier to implement, and (b) it seems rare to actually want
850 // to have behaviour like the above, rather than
851 // e.g. accidentally recurring after an assert.
852
853 let cfg = cfg::CFG::new(cx.tcx, blk);
854
855 let mut work_queue = vec![cfg.entry];
856 let mut reached_exit_without_self_call = false;
857 let mut self_call_spans = vec![];
e9174d1e 858 let mut visited = HashSet::new();
c34b1796
AL
859
860 while let Some(idx) = work_queue.pop() {
861 if idx == cfg.exit {
862 // found a path!
863 reached_exit_without_self_call = true;
864 break;
865 }
866
867 let cfg_id = idx.node_id();
868 if visited.contains(&cfg_id) {
869 // already done
870 continue;
871 }
872 visited.insert(cfg_id);
873
874 let node_id = cfg.graph.node_data(idx).id();
875
876 // is this a recursive call?
c1a9b12d
SL
877 let self_recursive = if node_id != ast::DUMMY_NODE_ID {
878 match method {
c30ab7b3
SL
879 Some(ref method) => expr_refers_to_this_method(cx.tcx, method, node_id),
880 None => expr_refers_to_this_fn(cx.tcx, id, node_id),
c1a9b12d
SL
881 }
882 } else {
883 false
884 };
885 if self_recursive {
c34b1796
AL
886 self_call_spans.push(cx.tcx.map.span(node_id));
887 // this is a self call, so we shouldn't explore past
888 // this node in the CFG.
889 continue;
890 }
891 // add the successors of this node to explore the graph further.
d9579d0f 892 for (_, edge) in cfg.graph.outgoing_edges(idx) {
c34b1796
AL
893 let target_idx = edge.target();
894 let target_cfg_id = target_idx.node_id();
895 if !visited.contains(&target_cfg_id) {
896 work_queue.push(target_idx)
897 }
d9579d0f 898 }
c34b1796
AL
899 }
900
901 // Check the number of self calls because a function that
902 // doesn't return (e.g. calls a `-> !` function or `loop { /*
903 // no break */ }`) shouldn't be linted unless it actually
904 // recurs.
9346a6ac 905 if !reached_exit_without_self_call && !self_call_spans.is_empty() {
c30ab7b3
SL
906 let mut db = cx.struct_span_lint(UNCONDITIONAL_RECURSION,
907 sp,
9cc50fc6 908 "function cannot return without recurring");
c34b1796
AL
909
910 // FIXME #19668: these could be span_lint_note's instead of this manual guard.
911 if cx.current_level(UNCONDITIONAL_RECURSION) != Level::Allow {
c34b1796
AL
912 // offer some help to the programmer.
913 for call in &self_call_spans {
9cc50fc6 914 db.span_note(*call, "recursive call site");
c34b1796 915 }
a7813a04
XL
916 db.help("a `loop` may express intention \
917 better if this is on purpose");
c34b1796 918 }
9cc50fc6 919 db.emit();
c34b1796
AL
920 }
921
922 // all done
923 return;
924
c1a9b12d
SL
925 // Functions for identifying if the given Expr NodeId `id`
926 // represents a call to the function `fn_id`/method `method`.
927
c30ab7b3 928 fn expr_refers_to_this_fn(tcx: TyCtxt, fn_id: ast::NodeId, id: ast::NodeId) -> bool {
c1a9b12d 929 match tcx.map.get(id) {
e9174d1e 930 hir_map::NodeExpr(&hir::Expr { node: hir::ExprCall(ref callee, _), .. }) => {
c30ab7b3
SL
931 tcx.expect_def_or_none(callee.id)
932 .map_or(false, |def| def.def_id() == tcx.map.local_def_id(fn_id))
c1a9b12d 933 }
c30ab7b3 934 _ => false,
c1a9b12d
SL
935 }
936 }
c34b1796 937
c1a9b12d 938 // Check if the expression `id` performs a call to `method`.
a7813a04
XL
939 fn expr_refers_to_this_method<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
940 method: &ty::Method,
c30ab7b3
SL
941 id: ast::NodeId)
942 -> bool {
943 use rustc::ty::adjustment::*;
944
c1a9b12d 945 // Check for method calls and overloaded operators.
c30ab7b3 946 let opt_m = tcx.tables().method_map.get(&ty::MethodCall::expr(id)).cloned();
b039eaaf 947 if let Some(m) = opt_m {
c1a9b12d
SL
948 if method_call_refers_to_method(tcx, method, m.def_id, m.substs, id) {
949 return true;
950 }
951 }
c34b1796 952
c1a9b12d 953 // Check for overloaded autoderef method calls.
c30ab7b3
SL
954 let opt_adj = tcx.tables().adjustments.get(&id).cloned();
955 if let Some(Adjustment { kind: Adjust::DerefRef { autoderefs, .. }, .. }) = opt_adj {
956 for i in 0..autoderefs {
c1a9b12d 957 let method_call = ty::MethodCall::autoderef(id, i as u32);
c30ab7b3
SL
958 if let Some(m) = tcx.tables().method_map.get(&method_call)
959 .cloned() {
c1a9b12d
SL
960 if method_call_refers_to_method(tcx, method, m.def_id, m.substs, id) {
961 return true;
962 }
c34b1796
AL
963 }
964 }
c1a9b12d 965 }
c34b1796 966
c1a9b12d
SL
967 // Check for calls to methods via explicit paths (e.g. `T::method()`).
968 match tcx.map.get(id) {
e9174d1e 969 hir_map::NodeExpr(&hir::Expr { node: hir::ExprCall(ref callee, _), .. }) => {
3157f602
XL
970 // The callee is an arbitrary expression,
971 // it doesn't necessarily have a definition.
972 match tcx.expect_def_or_none(callee.id) {
7453a54e 973 Some(Def::Method(def_id)) => {
c30ab7b3
SL
974 let substs = tcx.tables().node_id_item_substs(callee.id)
975 .unwrap_or_else(|| tcx.intern_substs(&[]));
b039eaaf 976 method_call_refers_to_method(
c30ab7b3 977 tcx, method, def_id, substs, id)
c1a9b12d 978 }
c30ab7b3 979 _ => false,
c1a9b12d
SL
980 }
981 }
c30ab7b3 982 _ => false,
c1a9b12d
SL
983 }
984 }
985
986 // Check if the method call to the method with the ID `callee_id`
987 // and instantiated with `callee_substs` refers to method `method`.
a7813a04
XL
988 fn method_call_refers_to_method<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
989 method: &ty::Method,
990 callee_id: DefId,
991 callee_substs: &Substs<'tcx>,
c30ab7b3
SL
992 expr_id: ast::NodeId)
993 -> bool {
c1a9b12d
SL
994 let callee_item = tcx.impl_or_trait_item(callee_id);
995
996 match callee_item.container() {
997 // This is an inherent method, so the `def_id` refers
998 // directly to the method definition.
c30ab7b3 999 ty::ImplContainer(_) => callee_id == method.def_id,
c1a9b12d
SL
1000
1001 // A trait method, from any number of possible sources.
1002 // Attempt to select a concrete impl before checking.
1003 ty::TraitContainer(trait_def_id) => {
9e0c209e 1004 let trait_ref = ty::TraitRef::from_method(tcx, trait_def_id, callee_substs);
c1a9b12d
SL
1005 let trait_ref = ty::Binder(trait_ref);
1006 let span = tcx.map.span(expr_id);
1007 let obligation =
1008 traits::Obligation::new(traits::ObligationCause::misc(span, expr_id),
1009 trait_ref.to_poly_trait_predicate());
1010
b039eaaf
SL
1011 // unwrap() is ok here b/c `method` is the method
1012 // defined in this crate whose body we are
1013 // checking, so it's always local
1014 let node_id = tcx.map.as_local_node_id(method.def_id).unwrap();
1015
a7813a04 1016 let param_env = Some(ty::ParameterEnvironment::for_item(tcx, node_id));
5bcae85e 1017 tcx.infer_ctxt(None, param_env, Reveal::NotSpecializable).enter(|infcx| {
a7813a04
XL
1018 let mut selcx = traits::SelectionContext::new(&infcx);
1019 match selcx.select(&obligation) {
1020 // The method comes from a `T: Trait` bound.
1021 // If `T` is `Self`, then this call is inside
1022 // a default method definition.
1023 Ok(Some(traits::VtableParam(_))) => {
9e0c209e 1024 let on_self = trait_ref.self_ty().is_self();
a7813a04
XL
1025 // We can only be recurring in a default
1026 // method if we're being called literally
1027 // on the `Self` type.
1028 on_self && callee_id == method.def_id
1029 }
c1a9b12d 1030
a7813a04
XL
1031 // The `impl` is known, so we check that with a
1032 // special case:
1033 Ok(Some(traits::VtableImpl(vtable_impl))) => {
1034 let container = ty::ImplContainer(vtable_impl.impl_def_id);
1035 // It matches if it comes from the same impl,
1036 // and has the same method name.
c30ab7b3 1037 container == method.container && callee_item.name() == method.name
a7813a04 1038 }
c1a9b12d 1039
a7813a04
XL
1040 // There's no way to know if this call is
1041 // recursive, so we assume it's not.
c30ab7b3 1042 _ => false,
a7813a04
XL
1043 }
1044 })
c1a9b12d
SL
1045 }
1046 }
c34b1796
AL
1047 }
1048 }
1049}
1050
1051declare_lint! {
1052 PLUGIN_AS_LIBRARY,
1053 Warn,
1054 "compiler plugin used as ordinary library in non-plugin crate"
1055}
1056
1057#[derive(Copy, Clone)]
1058pub struct PluginAsLibrary;
1059
1060impl LintPass for PluginAsLibrary {
1061 fn get_lints(&self) -> LintArray {
1062 lint_array![PLUGIN_AS_LIBRARY]
1063 }
b039eaaf 1064}
c34b1796 1065
b039eaaf
SL
1066impl LateLintPass for PluginAsLibrary {
1067 fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
c34b1796
AL
1068 if cx.sess().plugin_registrar_fn.get().is_some() {
1069 // We're compiling a plugin; it's fine to link other plugins.
1070 return;
1071 }
1072
1073 match it.node {
e9174d1e 1074 hir::ItemExternCrate(..) => (),
c34b1796
AL
1075 _ => return,
1076 };
1077
92a42be0
SL
1078 let prfn = match cx.sess().cstore.extern_mod_stmt_cnum(it.id) {
1079 Some(cnum) => cx.sess().cstore.plugin_registrar_fn(cnum),
c34b1796
AL
1080 None => {
1081 // Probably means we aren't linking the crate for some reason.
1082 //
1083 // Not sure if / when this could happen.
1084 return;
1085 }
1086 };
1087
92a42be0 1088 if prfn.is_some() {
c30ab7b3
SL
1089 cx.span_lint(PLUGIN_AS_LIBRARY,
1090 it.span,
c34b1796
AL
1091 "compiler plugin used as an ordinary library");
1092 }
1093 }
1094}
1095
1096declare_lint! {
1097 PRIVATE_NO_MANGLE_FNS,
1098 Warn,
1099 "functions marked #[no_mangle] should be exported"
1100}
1101
1102declare_lint! {
1103 PRIVATE_NO_MANGLE_STATICS,
1104 Warn,
1105 "statics marked #[no_mangle] should be exported"
1106}
1107
1108declare_lint! {
1109 NO_MANGLE_CONST_ITEMS,
1110 Deny,
1111 "const items will not have their symbols exported"
1112}
1113
9cc50fc6
SL
1114declare_lint! {
1115 NO_MANGLE_GENERIC_ITEMS,
1116 Warn,
1117 "generic items must be mangled"
1118}
1119
c34b1796
AL
1120#[derive(Copy, Clone)]
1121pub struct InvalidNoMangleItems;
1122
1123impl LintPass for InvalidNoMangleItems {
1124 fn get_lints(&self) -> LintArray {
1125 lint_array!(PRIVATE_NO_MANGLE_FNS,
1126 PRIVATE_NO_MANGLE_STATICS,
9cc50fc6
SL
1127 NO_MANGLE_CONST_ITEMS,
1128 NO_MANGLE_GENERIC_ITEMS)
c34b1796 1129 }
b039eaaf 1130}
c34b1796 1131
b039eaaf
SL
1132impl LateLintPass for InvalidNoMangleItems {
1133 fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
c34b1796 1134 match it.node {
9e0c209e 1135 hir::ItemFn(.., ref generics, _) => {
9cc50fc6
SL
1136 if attr::contains_name(&it.attrs, "no_mangle") {
1137 if !cx.access_levels.is_reachable(it.id) {
1138 let msg = format!("function {} is marked #[no_mangle], but not exported",
1139 it.name);
1140 cx.span_lint(PRIVATE_NO_MANGLE_FNS, it.span, &msg);
1141 }
1142 if generics.is_parameterized() {
1143 cx.span_lint(NO_MANGLE_GENERIC_ITEMS,
1144 it.span,
1145 "generic functions must be mangled");
1146 }
c34b1796 1147 }
c30ab7b3 1148 }
e9174d1e 1149 hir::ItemStatic(..) => {
c34b1796 1150 if attr::contains_name(&it.attrs, "no_mangle") &&
c30ab7b3 1151 !cx.access_levels.is_reachable(it.id) {
c34b1796 1152 let msg = format!("static {} is marked #[no_mangle], but not exported",
b039eaaf 1153 it.name);
c34b1796
AL
1154 cx.span_lint(PRIVATE_NO_MANGLE_STATICS, it.span, &msg);
1155 }
c30ab7b3 1156 }
e9174d1e 1157 hir::ItemConst(..) => {
c34b1796
AL
1158 if attr::contains_name(&it.attrs, "no_mangle") {
1159 // Const items do not refer to a particular location in memory, and therefore
1160 // don't have anything to attach a symbol to
1161 let msg = "const items should never be #[no_mangle], consider instead using \
1162 `pub static`";
1163 cx.span_lint(NO_MANGLE_CONST_ITEMS, it.span, msg);
1164 }
1165 }
c30ab7b3 1166 _ => {}
c34b1796
AL
1167 }
1168 }
1169}
1170
bd371182
AL
1171#[derive(Clone, Copy)]
1172pub struct MutableTransmutes;
1173
1174declare_lint! {
1175 MUTABLE_TRANSMUTES,
1176 Deny,
1177 "mutating transmuted &mut T from &T may cause undefined behavior"
1178}
1179
1180impl LintPass for MutableTransmutes {
1181 fn get_lints(&self) -> LintArray {
1182 lint_array!(MUTABLE_TRANSMUTES)
1183 }
b039eaaf 1184}
bd371182 1185
b039eaaf
SL
1186impl LateLintPass for MutableTransmutes {
1187 fn check_expr(&mut self, cx: &LateContext, expr: &hir::Expr) {
7453a54e 1188 use syntax::abi::Abi::RustIntrinsic;
e9174d1e 1189
c30ab7b3 1190 let msg = "mutating transmuted &mut T from &T may cause undefined behavior, \
bd371182
AL
1191 consider instead using an UnsafeCell";
1192 match get_transmute_from_to(cx, expr) {
62682a34 1193 Some((&ty::TyRef(_, from_mt), &ty::TyRef(_, to_mt))) => {
c30ab7b3
SL
1194 if to_mt.mutbl == hir::Mutability::MutMutable &&
1195 from_mt.mutbl == hir::Mutability::MutImmutable {
bd371182
AL
1196 cx.span_lint(MUTABLE_TRANSMUTES, expr.span, msg);
1197 }
1198 }
c30ab7b3 1199 _ => (),
bd371182
AL
1200 }
1201
c30ab7b3
SL
1202 fn get_transmute_from_to<'a, 'tcx>
1203 (cx: &LateContext<'a, 'tcx>,
1204 expr: &hir::Expr)
1205 -> Option<(&'tcx ty::TypeVariants<'tcx>, &'tcx ty::TypeVariants<'tcx>)> {
bd371182 1206 match expr.node {
e9174d1e 1207 hir::ExprPath(..) => (),
c30ab7b3 1208 _ => return None,
bd371182 1209 }
3157f602 1210 if let Def::Fn(did) = cx.tcx.expect_def(expr.id) {
bd371182
AL
1211 if !def_id_is_transmute(cx, did) {
1212 return None;
1213 }
c30ab7b3 1214 let typ = cx.tcx.tables().node_id_to_type(expr.id);
bd371182 1215 match typ.sty {
9e0c209e 1216 ty::TyFnDef(.., ref bare_fn) if bare_fn.abi == RustIntrinsic => {
5bcae85e
SL
1217 let from = bare_fn.sig.0.inputs[0];
1218 let to = bare_fn.sig.0.output;
1219 return Some((&from.sty, &to.sty));
c30ab7b3
SL
1220 }
1221 _ => (),
bd371182
AL
1222 }
1223 }
1224 None
1225 }
1226
b039eaaf 1227 fn def_id_is_transmute(cx: &LateContext, def_id: DefId) -> bool {
c1a9b12d 1228 match cx.tcx.lookup_item_type(def_id).ty.sty {
9e0c209e 1229 ty::TyFnDef(.., ref bfty) if bfty.abi == RustIntrinsic => (),
c30ab7b3 1230 _ => return false,
bd371182 1231 }
54a0048b 1232 cx.tcx.item_name(def_id).as_str() == "transmute"
bd371182
AL
1233 }
1234 }
1235}
1236
c34b1796
AL
1237/// Forbids using the `#[feature(...)]` attribute
1238#[derive(Copy, Clone)]
1239pub struct UnstableFeatures;
1240
1241declare_lint! {
1242 UNSTABLE_FEATURES,
1243 Allow,
62682a34 1244 "enabling unstable features (deprecated. do not use)"
c34b1796
AL
1245}
1246
1247impl LintPass for UnstableFeatures {
1248 fn get_lints(&self) -> LintArray {
1249 lint_array!(UNSTABLE_FEATURES)
1250 }
b039eaaf
SL
1251}
1252
1253impl LateLintPass for UnstableFeatures {
1254 fn check_attribute(&mut self, ctx: &LateContext, attr: &ast::Attribute) {
9e0c209e 1255 if attr.meta().check_name("feature") {
5bcae85e 1256 if let Some(items) = attr.meta().meta_item_list() {
62682a34 1257 for item in items {
5bcae85e 1258 ctx.span_lint(UNSTABLE_FEATURES, item.span(), "unstable feature");
62682a34
SL
1259 }
1260 }
c34b1796
AL
1261 }
1262 }
1263}
bd371182 1264
9e0c209e
SL
1265/// Lint for unions that contain fields with possibly non-trivial destructors.
1266pub struct UnionsWithDropFields;
bd371182
AL
1267
1268declare_lint! {
9e0c209e 1269 UNIONS_WITH_DROP_FIELDS,
bd371182 1270 Warn,
9e0c209e 1271 "use of unions that contain fields with possibly non-trivial drop code"
bd371182
AL
1272}
1273
9e0c209e 1274impl LintPass for UnionsWithDropFields {
bd371182 1275 fn get_lints(&self) -> LintArray {
9e0c209e 1276 lint_array!(UNIONS_WITH_DROP_FIELDS)
bd371182 1277 }
b039eaaf 1278}
e9174d1e 1279
9e0c209e
SL
1280impl LateLintPass for UnionsWithDropFields {
1281 fn check_item(&mut self, ctx: &LateContext, item: &hir::Item) {
1282 if let hir::ItemUnion(ref vdata, _) = item.node {
1283 let param_env = &ty::ParameterEnvironment::for_item(ctx.tcx, item.id);
1284 for field in vdata.fields() {
c30ab7b3 1285 let field_ty = ctx.tcx.tables().node_id_to_type(field.id);
9e0c209e
SL
1286 if ctx.tcx.type_needs_drop_given_env(field_ty, param_env) {
1287 ctx.span_lint(UNIONS_WITH_DROP_FIELDS,
1288 field.span,
1289 "union contains a field with possibly non-trivial drop code, \
1290 drop code of union fields is ignored when dropping the union");
1291 return;
bd371182 1292 }
bd371182 1293 }
9e0c209e 1294 }
bd371182
AL
1295 }
1296}