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