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