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