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.
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.
11 //! Lints in the Rust compiler.
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
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).
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
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};
42 use std::collections::HashSet;
45 use syntax::attr::{self, AttrMetaMethods};
46 use syntax::codemap::{self, Span};
49 use rustc_front::visit::{self, FnKind, Visitor};
51 use bad_style::{MethodLateContext, method_context};
53 // hardwired lints from librustc
54 pub use lint::builtin::*;
59 "suggest using `loop { }` instead of `while true { }`"
62 #[derive(Copy, Clone)]
65 impl LintPass for WhileTrue {
66 fn get_lints(&self) -> LintArray {
67 lint_array!(WHILE_TRUE)
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 { ... }");
87 "use of owned (Box type) heap memory"
90 #[derive(Copy, Clone)]
91 pub struct 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);
105 impl LintPass for BoxPointers {
106 fn get_lints(&self) -> LintArray {
107 lint_array!(BOX_POINTERS)
111 impl LateLintPass for BoxPointers {
112 fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
117 hir::ItemStruct(..) =>
118 self.check_heap_type(cx, it.span,
119 cx.tcx.node_id_to_type(it.id)),
123 // If it's a struct, we also have to check the fields' types
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));
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);
144 "uses of #[derive] with raw pointers are rarely correct"
147 struct RawPtrDeriveVisitor<'a, 'tcx: 'a> {
148 cx: &'a LateContext<'a, 'tcx>
151 impl<'a, 'tcx, 'v> Visitor<'v> for RawPtrDeriveVisitor<'a, 'tcx> {
152 fn visit_ty(&mut self, ty: &hir::Ty) {
153 const MSG: &'static str = "use of `#[derive]` with a raw pointer";
154 if let hir::TyPtr(..) = ty.node {
155 self.cx.span_lint(RAW_POINTER_DERIVE, ty.span, MSG);
157 visit::walk_ty(self, ty);
159 // explicit override to a no-op to reduce code bloat
160 fn visit_expr(&mut self, _: &hir::Expr) {}
161 fn visit_block(&mut self, _: &hir::Block) {}
164 pub struct RawPointerDerive {
165 checked_raw_pointers: NodeSet,
168 impl RawPointerDerive {
169 pub fn new() -> RawPointerDerive {
171 checked_raw_pointers: NodeSet(),
176 impl LintPass for RawPointerDerive {
177 fn get_lints(&self) -> LintArray {
178 lint_array!(RAW_POINTER_DERIVE)
182 impl LateLintPass for RawPointerDerive {
183 fn check_item(&mut self, cx: &LateContext, item: &hir::Item) {
184 if !attr::contains_name(&item.attrs, "automatically_derived") {
187 let did = match item.node {
188 hir::ItemImpl(_, _, _, ref t_ref_opt, _, _) => {
189 // Deriving the Copy trait does not cause a warning
190 if let &Some(ref trait_ref) = t_ref_opt {
191 let def_id = cx.tcx.trait_ref_to_def_id(trait_ref);
192 if Some(def_id) == cx.tcx.lang_items.copy_trait() {
197 match cx.tcx.node_id_to_type(item.id).sty {
198 ty::TyEnum(def, _) => def.did,
199 ty::TyStruct(def, _) => def.did,
208 let item = match cx.tcx.map.find(did.node) {
209 Some(hir_map::NodeItem(item)) => item,
212 if !self.checked_raw_pointers.insert(item.id) {
216 hir::ItemStruct(..) | hir::ItemEnum(..) => {
217 let mut visitor = RawPtrDeriveVisitor { cx: cx };
218 visit::walk_item(&mut visitor, &item);
226 NON_SHORTHAND_FIELD_PATTERNS,
228 "using `Struct { x: x }` instead of `Struct { x }`"
231 #[derive(Copy, Clone)]
232 pub struct NonShorthandFieldPatterns;
234 impl LintPass for NonShorthandFieldPatterns {
235 fn get_lints(&self) -> LintArray {
236 lint_array!(NON_SHORTHAND_FIELD_PATTERNS)
240 impl LateLintPass for NonShorthandFieldPatterns {
241 fn check_pat(&mut self, cx: &LateContext, pat: &hir::Pat) {
242 let def_map = cx.tcx.def_map.borrow();
243 if let hir::PatStruct(_, ref v, _) = pat.node {
244 let field_pats = v.iter().filter(|fieldpat| {
245 if fieldpat.node.is_shorthand {
248 let def = def_map.get(&fieldpat.node.pat.id).map(|d| d.full_def());
249 def == Some(def::DefLocal(fieldpat.node.pat.id))
251 for fieldpat in field_pats {
252 if let hir::PatIdent(_, ident, None) = fieldpat.node.pat.node {
253 if ident.node.name == fieldpat.node.name {
254 // FIXME: should this comparison really be done on the name?
255 // doing it on the ident will fail during compilation of libcore
256 cx.span_lint(NON_SHORTHAND_FIELD_PATTERNS, fieldpat.span,
257 &format!("the `{}:` in this pattern is redundant and can \
258 be removed", ident.node))
269 "usage of `unsafe` code"
272 #[derive(Copy, Clone)]
273 pub struct UnsafeCode;
275 impl LintPass for UnsafeCode {
276 fn get_lints(&self) -> LintArray {
277 lint_array!(UNSAFE_CODE)
281 impl LateLintPass for UnsafeCode {
282 fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
283 if let hir::ExprBlock(ref blk) = e.node {
284 // Don't warn about generated blocks, that'll just pollute the output.
285 if blk.rules == hir::UnsafeBlock(hir::UserProvided) {
286 cx.span_lint(UNSAFE_CODE, blk.span, "usage of an `unsafe` block");
291 fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
293 hir::ItemTrait(hir::Unsafety::Unsafe, _, _, _) =>
294 cx.span_lint(UNSAFE_CODE, it.span, "declaration of an `unsafe` trait"),
296 hir::ItemImpl(hir::Unsafety::Unsafe, _, _, _, _, _) =>
297 cx.span_lint(UNSAFE_CODE, it.span, "implementation of an `unsafe` trait"),
303 fn check_fn(&mut self, cx: &LateContext, fk: FnKind, _: &hir::FnDecl,
304 _: &hir::Block, span: Span, _: ast::NodeId) {
306 FnKind::ItemFn(_, _, hir::Unsafety::Unsafe, _, _, _) =>
307 cx.span_lint(UNSAFE_CODE, span, "declaration of an `unsafe` function"),
309 FnKind::Method(_, sig, _) => {
310 if sig.unsafety == hir::Unsafety::Unsafe {
311 cx.span_lint(UNSAFE_CODE, span, "implementation of an `unsafe` method")
319 fn check_trait_item(&mut self, cx: &LateContext, trait_item: &hir::TraitItem) {
320 if let hir::MethodTraitItem(ref sig, None) = trait_item.node {
321 if sig.unsafety == hir::Unsafety::Unsafe {
322 cx.span_lint(UNSAFE_CODE, trait_item.span,
323 "declaration of an `unsafe` method")
332 "detects missing documentation for public members"
335 pub struct MissingDoc {
336 /// Stack of IDs of struct definitions.
337 struct_def_stack: Vec<ast::NodeId>,
339 /// True if inside variant definition
342 /// Stack of whether #[doc(hidden)] is set
343 /// at each level which has lint attributes.
344 doc_hidden_stack: Vec<bool>,
346 /// Private traits or trait items that leaked through. Don't check their methods.
347 private_traits: HashSet<ast::NodeId>,
351 pub fn new() -> MissingDoc {
353 struct_def_stack: vec!(),
355 doc_hidden_stack: vec!(false),
356 private_traits: HashSet::new(),
360 fn doc_hidden(&self) -> bool {
361 *self.doc_hidden_stack.last().expect("empty doc_hidden_stack")
364 fn check_missing_docs_attrs(&self,
366 id: Option<ast::NodeId>,
367 attrs: &[ast::Attribute],
369 desc: &'static str) {
370 // If we're building a test harness, then warning about
371 // documentation is probably not really relevant right now.
372 if cx.sess().opts.test {
376 // `#[doc(hidden)]` disables missing_docs check.
377 if self.doc_hidden() {
381 // Only check publicly-visible items, using the result from the privacy pass.
382 // It's an option so the crate root can also use this function (it doesn't
384 if let Some(ref id) = id {
385 if !cx.exported_items.contains(id) {
390 let has_doc = attrs.iter().any(|a| {
391 match a.node.value.node {
392 ast::MetaNameValue(ref name, _) if *name == "doc" => true,
397 cx.span_lint(MISSING_DOCS, sp,
398 &format!("missing documentation for {}", desc));
403 impl LintPass for MissingDoc {
404 fn get_lints(&self) -> LintArray {
405 lint_array!(MISSING_DOCS)
409 impl LateLintPass for MissingDoc {
410 fn enter_lint_attrs(&mut self, _: &LateContext, attrs: &[ast::Attribute]) {
411 let doc_hidden = self.doc_hidden() || attrs.iter().any(|attr| {
412 attr.check_name("doc") && match attr.meta_item_list() {
414 Some(l) => attr::contains_name(&l[..], "hidden"),
417 self.doc_hidden_stack.push(doc_hidden);
420 fn exit_lint_attrs(&mut self, _: &LateContext, _: &[ast::Attribute]) {
421 self.doc_hidden_stack.pop().expect("empty doc_hidden_stack");
424 fn check_struct_def(&mut self, _: &LateContext, _: &hir::StructDef,
425 _: ast::Name, _: &hir::Generics, id: ast::NodeId) {
426 self.struct_def_stack.push(id);
429 fn check_struct_def_post(&mut self, _: &LateContext, _: &hir::StructDef,
430 _: ast::Name, _: &hir::Generics, id: ast::NodeId) {
431 let popped = self.struct_def_stack.pop().expect("empty struct_def_stack");
432 assert!(popped == id);
435 fn check_crate(&mut self, cx: &LateContext, krate: &hir::Crate) {
436 self.check_missing_docs_attrs(cx, None, &krate.attrs, krate.span, "crate");
439 fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
440 let desc = match it.node {
441 hir::ItemFn(..) => "a function",
442 hir::ItemMod(..) => "a module",
443 hir::ItemEnum(..) => "an enum",
444 hir::ItemStruct(..) => "a struct",
445 hir::ItemTrait(_, _, _, ref items) => {
446 // Issue #11592, traits are always considered exported, even when private.
447 if it.vis == hir::Visibility::Inherited {
448 self.private_traits.insert(it.id);
450 self.private_traits.insert(itm.id);
456 hir::ItemTy(..) => "a type alias",
457 hir::ItemImpl(_, _, _, Some(ref trait_ref), _, ref impl_items) => {
458 // If the trait is private, add the impl items to private_traits so they don't get
459 // reported for missing docs.
460 let real_trait = cx.tcx.trait_ref_to_def_id(trait_ref);
461 match cx.tcx.map.find(real_trait.node) {
462 Some(hir_map::NodeItem(item)) => if item.vis == hir::Visibility::Inherited {
463 for itm in impl_items {
464 self.private_traits.insert(itm.id);
471 hir::ItemConst(..) => "a constant",
472 hir::ItemStatic(..) => "a static",
476 self.check_missing_docs_attrs(cx, Some(it.id), &it.attrs, it.span, desc);
479 fn check_trait_item(&mut self, cx: &LateContext, trait_item: &hir::TraitItem) {
480 if self.private_traits.contains(&trait_item.id) { return }
482 let desc = match trait_item.node {
483 hir::ConstTraitItem(..) => "an associated constant",
484 hir::MethodTraitItem(..) => "a trait method",
485 hir::TypeTraitItem(..) => "an associated type",
488 self.check_missing_docs_attrs(cx, Some(trait_item.id),
490 trait_item.span, desc);
493 fn check_impl_item(&mut self, cx: &LateContext, impl_item: &hir::ImplItem) {
494 // If the method is an impl for a trait, don't doc.
495 if method_context(cx, impl_item.id, impl_item.span) == MethodLateContext::TraitImpl {
499 let desc = match impl_item.node {
500 hir::ConstImplItem(..) => "an associated constant",
501 hir::MethodImplItem(..) => "a method",
502 hir::TypeImplItem(_) => "an associated type",
504 self.check_missing_docs_attrs(cx, Some(impl_item.id),
506 impl_item.span, desc);
509 fn check_struct_field(&mut self, cx: &LateContext, sf: &hir::StructField) {
510 if let hir::NamedField(_, vis) = sf.node.kind {
511 if vis == hir::Public || self.in_variant {
512 let cur_struct_def = *self.struct_def_stack.last()
513 .expect("empty struct_def_stack");
514 self.check_missing_docs_attrs(cx, Some(cur_struct_def),
515 &sf.node.attrs, sf.span,
521 fn check_variant(&mut self, cx: &LateContext, v: &hir::Variant, _: &hir::Generics) {
522 self.check_missing_docs_attrs(cx, Some(v.node.id), &v.node.attrs, v.span, "a variant");
523 assert!(!self.in_variant);
524 self.in_variant = true;
527 fn check_variant_post(&mut self, _: &LateContext, _: &hir::Variant, _: &hir::Generics) {
528 assert!(self.in_variant);
529 self.in_variant = false;
534 pub MISSING_COPY_IMPLEMENTATIONS,
536 "detects potentially-forgotten implementations of `Copy`"
539 #[derive(Copy, Clone)]
540 pub struct MissingCopyImplementations;
542 impl LintPass for MissingCopyImplementations {
543 fn get_lints(&self) -> LintArray {
544 lint_array!(MISSING_COPY_IMPLEMENTATIONS)
548 impl LateLintPass for MissingCopyImplementations {
549 fn check_item(&mut self, cx: &LateContext, item: &hir::Item) {
550 if !cx.exported_items.contains(&item.id) {
553 let (def, ty) = match item.node {
554 hir::ItemStruct(_, ref ast_generics) => {
555 if ast_generics.is_parameterized() {
558 let def = cx.tcx.lookup_adt_def(DefId::local(item.id));
559 (def, cx.tcx.mk_struct(def,
560 cx.tcx.mk_substs(Substs::empty())))
562 hir::ItemEnum(_, ref ast_generics) => {
563 if ast_generics.is_parameterized() {
566 let def = cx.tcx.lookup_adt_def(DefId::local(item.id));
567 (def, cx.tcx.mk_enum(def,
568 cx.tcx.mk_substs(Substs::empty())))
572 if def.has_dtor() { return; }
573 let parameter_environment = cx.tcx.empty_parameter_environment();
574 // FIXME (@jroesch) should probably inver this so that the parameter env still impls this
576 if !ty.moves_by_default(¶meter_environment, item.span) {
579 if parameter_environment.can_type_implement_copy(ty, item.span).is_ok() {
580 cx.span_lint(MISSING_COPY_IMPLEMENTATIONS,
582 "type could implement `Copy`; consider adding `impl \
589 MISSING_DEBUG_IMPLEMENTATIONS,
591 "detects missing implementations of fmt::Debug"
594 pub struct MissingDebugImplementations {
595 impling_types: Option<NodeSet>,
598 impl MissingDebugImplementations {
599 pub fn new() -> MissingDebugImplementations {
600 MissingDebugImplementations {
606 impl LintPass for MissingDebugImplementations {
607 fn get_lints(&self) -> LintArray {
608 lint_array!(MISSING_DEBUG_IMPLEMENTATIONS)
612 impl LateLintPass for MissingDebugImplementations {
613 fn check_item(&mut self, cx: &LateContext, item: &hir::Item) {
614 if !cx.exported_items.contains(&item.id) {
619 hir::ItemStruct(..) | hir::ItemEnum(..) => {},
623 let debug = match cx.tcx.lang_items.debug_trait() {
624 Some(debug) => debug,
628 if self.impling_types.is_none() {
629 let debug_def = cx.tcx.lookup_trait_def(debug);
630 let mut impls = NodeSet();
631 debug_def.for_each_impl(cx.tcx, |d| {
633 if let Some(ty_def) = cx.tcx.node_id_to_type(d.node).ty_to_def_id() {
634 impls.insert(ty_def.node);
639 self.impling_types = Some(impls);
640 debug!("{:?}", self.impling_types);
643 if !self.impling_types.as_ref().unwrap().contains(&item.id) {
644 cx.span_lint(MISSING_DEBUG_IMPLEMENTATIONS,
646 "type does not implement `fmt::Debug`; consider adding #[derive(Debug)] \
647 or a manual implementation")
655 "detects use of #[deprecated] items"
658 /// Checks for use of items with `#[deprecated]` attributes
659 #[derive(Copy, Clone)]
660 pub struct Stability;
663 fn lint(&self, cx: &LateContext, _id: DefId,
664 span: Span, stability: &Option<&attr::Stability>) {
665 // Deprecated attributes apply in-crate and cross-crate.
666 let (lint, label) = match *stability {
667 Some(&attr::Stability { deprecated_since: Some(_), .. }) =>
668 (DEPRECATED, "deprecated"),
672 output(cx, span, stability, lint, label);
674 fn output(cx: &LateContext, span: Span, stability: &Option<&attr::Stability>,
675 lint: &'static Lint, label: &'static str) {
676 let msg = match *stability {
677 Some(&attr::Stability { reason: Some(ref s), .. }) => {
678 format!("use of {} item: {}", label, *s)
680 _ => format!("use of {} item", label)
683 cx.span_lint(lint, span, &msg[..]);
688 fn hir_to_ast_stability(stab: &attr::Stability) -> attr::Stability {
690 level: match stab.level {
691 attr::Unstable => attr::Unstable,
692 attr::Stable => attr::Stable,
694 feature: stab.feature.clone(),
695 since: stab.since.clone(),
696 deprecated_since: stab.deprecated_since.clone(),
697 reason: stab.reason.clone(),
702 impl LintPass for Stability {
703 fn get_lints(&self) -> LintArray {
704 lint_array!(DEPRECATED)
708 impl LateLintPass for Stability {
709 fn check_item(&mut self, cx: &LateContext, item: &hir::Item) {
710 stability::check_item(cx.tcx, item, false,
712 self.lint(cx, id, sp,
713 &stab.map(|s| hir_to_ast_stability(s)).as_ref()));
716 fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
717 stability::check_expr(cx.tcx, e,
719 self.lint(cx, id, sp,
720 &stab.map(|s| hir_to_ast_stability(s)).as_ref()));
723 fn check_path(&mut self, cx: &LateContext, path: &hir::Path, id: ast::NodeId) {
724 stability::check_path(cx.tcx, path, id,
726 self.lint(cx, id, sp,
727 &stab.map(|s| hir_to_ast_stability(s)).as_ref()));
730 fn check_path_list_item(&mut self, cx: &LateContext, item: &hir::PathListItem) {
731 stability::check_path_list_item(cx.tcx, item,
733 self.lint(cx, id, sp,
734 &stab.map(|s| hir_to_ast_stability(s)).as_ref()));
737 fn check_pat(&mut self, cx: &LateContext, pat: &hir::Pat) {
738 stability::check_pat(cx.tcx, pat,
740 self.lint(cx, id, sp,
741 &stab.map(|s| hir_to_ast_stability(s)).as_ref()));
746 pub UNCONDITIONAL_RECURSION,
748 "functions that cannot return without calling themselves"
751 #[derive(Copy, Clone)]
752 pub struct UnconditionalRecursion;
755 impl LintPass for UnconditionalRecursion {
756 fn get_lints(&self) -> LintArray {
757 lint_array![UNCONDITIONAL_RECURSION]
761 impl LateLintPass for UnconditionalRecursion {
762 fn check_fn(&mut self, cx: &LateContext, fn_kind: FnKind, _: &hir::FnDecl,
763 blk: &hir::Block, sp: Span, id: ast::NodeId) {
764 let method = match fn_kind {
765 FnKind::ItemFn(..) => None,
766 FnKind::Method(..) => {
767 cx.tcx.impl_or_trait_item(DefId::local(id)).as_opt_method()
769 // closures can't recur, so they don't matter.
770 FnKind::Closure => return
773 // Walk through this function (say `f`) looking to see if
774 // every possible path references itself, i.e. the function is
775 // called recursively unconditionally. This is done by trying
776 // to find a path from the entry node to the exit node that
777 // *doesn't* call `f` by traversing from the entry while
778 // pretending that calls of `f` are sinks (i.e. ignoring any
779 // exit edges from them).
781 // NB. this has an edge case with non-returning statements,
782 // like `loop {}` or `panic!()`: control flow never reaches
783 // the exit node through these, so one can have a function
784 // that never actually calls itselfs but is still picked up by
787 // fn f(cond: bool) {
788 // if !cond { panic!() } // could come from `assert!(cond)`
792 // In general, functions of that form may be able to call
793 // itself a finite number of times and then diverge. The lint
794 // considers this to be an error for two reasons, (a) it is
795 // easier to implement, and (b) it seems rare to actually want
796 // to have behaviour like the above, rather than
797 // e.g. accidentally recurring after an assert.
799 let cfg = cfg::CFG::new(cx.tcx, blk);
801 let mut work_queue = vec![cfg.entry];
802 let mut reached_exit_without_self_call = false;
803 let mut self_call_spans = vec![];
804 let mut visited = HashSet::new();
806 while let Some(idx) = work_queue.pop() {
809 reached_exit_without_self_call = true;
813 let cfg_id = idx.node_id();
814 if visited.contains(&cfg_id) {
818 visited.insert(cfg_id);
820 let node_id = cfg.graph.node_data(idx).id();
822 // is this a recursive call?
823 let self_recursive = if node_id != ast::DUMMY_NODE_ID {
825 Some(ref method) => {
826 expr_refers_to_this_method(cx.tcx, method, node_id)
828 None => expr_refers_to_this_fn(cx.tcx, id, node_id)
834 self_call_spans.push(cx.tcx.map.span(node_id));
835 // this is a self call, so we shouldn't explore past
836 // this node in the CFG.
839 // add the successors of this node to explore the graph further.
840 for (_, edge) in cfg.graph.outgoing_edges(idx) {
841 let target_idx = edge.target();
842 let target_cfg_id = target_idx.node_id();
843 if !visited.contains(&target_cfg_id) {
844 work_queue.push(target_idx)
849 // Check the number of self calls because a function that
850 // doesn't return (e.g. calls a `-> !` function or `loop { /*
851 // no break */ }`) shouldn't be linted unless it actually
853 if !reached_exit_without_self_call && !self_call_spans.is_empty() {
854 cx.span_lint(UNCONDITIONAL_RECURSION, sp,
855 "function cannot return without recurring");
857 // FIXME #19668: these could be span_lint_note's instead of this manual guard.
858 if cx.current_level(UNCONDITIONAL_RECURSION) != Level::Allow {
859 let sess = cx.sess();
860 // offer some help to the programmer.
861 for call in &self_call_spans {
862 sess.span_note(*call, "recursive call site")
864 sess.fileline_help(sp, "a `loop` may express intention \
865 better if this is on purpose")
872 // Functions for identifying if the given Expr NodeId `id`
873 // represents a call to the function `fn_id`/method `method`.
875 fn expr_refers_to_this_fn(tcx: &ty::ctxt,
877 id: ast::NodeId) -> bool {
878 match tcx.map.get(id) {
879 hir_map::NodeExpr(&hir::Expr { node: hir::ExprCall(ref callee, _), .. }) => {
880 tcx.def_map.borrow().get(&callee.id)
881 .map_or(false, |def| def.def_id() == DefId::local(fn_id))
887 // Check if the expression `id` performs a call to `method`.
888 fn expr_refers_to_this_method(tcx: &ty::ctxt,
890 id: ast::NodeId) -> bool {
891 let tables = tcx.tables.borrow();
893 // Check for method calls and overloaded operators.
894 if let Some(m) = tables.method_map.get(&ty::MethodCall::expr(id)) {
895 if method_call_refers_to_method(tcx, method, m.def_id, m.substs, id) {
900 // Check for overloaded autoderef method calls.
901 if let Some(&adjustment::AdjustDerefRef(ref adj)) = tables.adjustments.get(&id) {
902 for i in 0..adj.autoderefs {
903 let method_call = ty::MethodCall::autoderef(id, i as u32);
904 if let Some(m) = tables.method_map.get(&method_call) {
905 if method_call_refers_to_method(tcx, method, m.def_id, m.substs, id) {
912 // Check for calls to methods via explicit paths (e.g. `T::method()`).
913 match tcx.map.get(id) {
914 hir_map::NodeExpr(&hir::Expr { node: hir::ExprCall(ref callee, _), .. }) => {
915 match tcx.def_map.borrow().get(&callee.id).map(|d| d.full_def()) {
916 Some(def::DefMethod(def_id)) => {
917 let no_substs = &ty::ItemSubsts::empty();
918 let ts = tables.item_substs.get(&callee.id).unwrap_or(no_substs);
919 method_call_refers_to_method(tcx, method, def_id, &ts.substs, id)
928 // Check if the method call to the method with the ID `callee_id`
929 // and instantiated with `callee_substs` refers to method `method`.
930 fn method_call_refers_to_method<'tcx>(tcx: &ty::ctxt<'tcx>,
933 callee_substs: &Substs<'tcx>,
934 expr_id: ast::NodeId) -> bool {
935 let callee_item = tcx.impl_or_trait_item(callee_id);
937 match callee_item.container() {
938 // This is an inherent method, so the `def_id` refers
939 // directly to the method definition.
940 ty::ImplContainer(_) => {
941 callee_id == method.def_id
944 // A trait method, from any number of possible sources.
945 // Attempt to select a concrete impl before checking.
946 ty::TraitContainer(trait_def_id) => {
947 let trait_substs = callee_substs.clone().method_to_trait();
948 let trait_substs = tcx.mk_substs(trait_substs);
949 let trait_ref = ty::TraitRef::new(trait_def_id, trait_substs);
950 let trait_ref = ty::Binder(trait_ref);
951 let span = tcx.map.span(expr_id);
953 traits::Obligation::new(traits::ObligationCause::misc(span, expr_id),
954 trait_ref.to_poly_trait_predicate());
956 let param_env = ty::ParameterEnvironment::for_item(tcx, method.def_id.node);
957 let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(param_env), false);
958 let mut selcx = traits::SelectionContext::new(&infcx);
959 match selcx.select(&obligation) {
960 // The method comes from a `T: Trait` bound.
961 // If `T` is `Self`, then this call is inside
962 // a default method definition.
963 Ok(Some(traits::VtableParam(_))) => {
964 let self_ty = callee_substs.self_ty();
965 let on_self = self_ty.map_or(false, |t| t.is_self());
966 // We can only be recurring in a default
967 // method if we're being called literally
968 // on the `Self` type.
969 on_self && callee_id == method.def_id
972 // The `impl` is known, so we check that with a
974 Ok(Some(traits::VtableImpl(vtable_impl))) => {
975 let container = ty::ImplContainer(vtable_impl.impl_def_id);
976 // It matches if it comes from the same impl,
977 // and has the same method name.
978 container == method.container
979 && callee_item.name() == method.name
982 // There's no way to know if this call is
983 // recursive, so we assume it's not.
995 "compiler plugin used as ordinary library in non-plugin crate"
998 #[derive(Copy, Clone)]
999 pub struct PluginAsLibrary;
1001 impl LintPass for PluginAsLibrary {
1002 fn get_lints(&self) -> LintArray {
1003 lint_array![PLUGIN_AS_LIBRARY]
1007 impl LateLintPass for PluginAsLibrary {
1008 fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
1009 if cx.sess().plugin_registrar_fn.get().is_some() {
1010 // We're compiling a plugin; it's fine to link other plugins.
1015 hir::ItemExternCrate(..) => (),
1019 let md = match cx.sess().cstore.find_extern_mod_stmt_cnum(it.id) {
1020 Some(cnum) => cx.sess().cstore.get_crate_data(cnum),
1022 // Probably means we aren't linking the crate for some reason.
1024 // Not sure if / when this could happen.
1029 if decoder::get_plugin_registrar_fn(md.data()).is_some() {
1030 cx.span_lint(PLUGIN_AS_LIBRARY, it.span,
1031 "compiler plugin used as an ordinary library");
1037 PRIVATE_NO_MANGLE_FNS,
1039 "functions marked #[no_mangle] should be exported"
1043 PRIVATE_NO_MANGLE_STATICS,
1045 "statics marked #[no_mangle] should be exported"
1049 NO_MANGLE_CONST_ITEMS,
1051 "const items will not have their symbols exported"
1054 #[derive(Copy, Clone)]
1055 pub struct InvalidNoMangleItems;
1057 impl LintPass for InvalidNoMangleItems {
1058 fn get_lints(&self) -> LintArray {
1059 lint_array!(PRIVATE_NO_MANGLE_FNS,
1060 PRIVATE_NO_MANGLE_STATICS,
1061 NO_MANGLE_CONST_ITEMS)
1065 impl LateLintPass for InvalidNoMangleItems {
1066 fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
1068 hir::ItemFn(..) => {
1069 if attr::contains_name(&it.attrs, "no_mangle") &&
1070 !cx.exported_items.contains(&it.id) {
1071 let msg = format!("function {} is marked #[no_mangle], but not exported",
1073 cx.span_lint(PRIVATE_NO_MANGLE_FNS, it.span, &msg);
1076 hir::ItemStatic(..) => {
1077 if attr::contains_name(&it.attrs, "no_mangle") &&
1078 !cx.exported_items.contains(&it.id) {
1079 let msg = format!("static {} is marked #[no_mangle], but not exported",
1081 cx.span_lint(PRIVATE_NO_MANGLE_STATICS, it.span, &msg);
1084 hir::ItemConst(..) => {
1085 if attr::contains_name(&it.attrs, "no_mangle") {
1086 // Const items do not refer to a particular location in memory, and therefore
1087 // don't have anything to attach a symbol to
1088 let msg = "const items should never be #[no_mangle], consider instead using \
1090 cx.span_lint(NO_MANGLE_CONST_ITEMS, it.span, msg);
1098 #[derive(Clone, Copy)]
1099 pub struct MutableTransmutes;
1104 "mutating transmuted &mut T from &T may cause undefined behavior"
1107 impl LintPass for MutableTransmutes {
1108 fn get_lints(&self) -> LintArray {
1109 lint_array!(MUTABLE_TRANSMUTES)
1113 impl LateLintPass for MutableTransmutes {
1114 fn check_expr(&mut self, cx: &LateContext, expr: &hir::Expr) {
1115 use syntax::abi::RustIntrinsic;
1117 let msg = "mutating transmuted &mut T from &T may cause undefined behavior,\
1118 consider instead using an UnsafeCell";
1119 match get_transmute_from_to(cx, expr) {
1120 Some((&ty::TyRef(_, from_mt), &ty::TyRef(_, to_mt))) => {
1121 if to_mt.mutbl == hir::Mutability::MutMutable
1122 && from_mt.mutbl == hir::Mutability::MutImmutable {
1123 cx.span_lint(MUTABLE_TRANSMUTES, expr.span, msg);
1129 fn get_transmute_from_to<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &hir::Expr)
1130 -> Option<(&'tcx ty::TypeVariants<'tcx>, &'tcx ty::TypeVariants<'tcx>)> {
1132 hir::ExprPath(..) => (),
1135 if let def::DefFn(did, _) = cx.tcx.resolve_expr(expr) {
1136 if !def_id_is_transmute(cx, did) {
1139 let typ = cx.tcx.node_id_to_type(expr.id);
1141 ty::TyBareFn(_, ref bare_fn) if bare_fn.abi == RustIntrinsic => {
1142 if let ty::FnConverging(to) = bare_fn.sig.0.output {
1143 let from = bare_fn.sig.0.inputs[0];
1144 return Some((&from.sty, &to.sty));
1153 fn def_id_is_transmute(cx: &LateContext, def_id: DefId) -> bool {
1154 match cx.tcx.lookup_item_type(def_id).ty.sty {
1155 ty::TyBareFn(_, ref bfty) if bfty.abi == RustIntrinsic => (),
1158 cx.tcx.with_path(def_id, |path| match path.last() {
1159 Some(ref last) => last.name().as_str() == "transmute",
1166 /// Forbids using the `#[feature(...)]` attribute
1167 #[derive(Copy, Clone)]
1168 pub struct UnstableFeatures;
1173 "enabling unstable features (deprecated. do not use)"
1176 impl LintPass for UnstableFeatures {
1177 fn get_lints(&self) -> LintArray {
1178 lint_array!(UNSTABLE_FEATURES)
1182 impl LateLintPass for UnstableFeatures {
1183 fn check_attribute(&mut self, ctx: &LateContext, attr: &ast::Attribute) {
1184 if attr::contains_name(&[attr.node.value.clone()], "feature") {
1185 if let Some(items) = attr.node.value.meta_item_list() {
1187 ctx.span_lint(UNSTABLE_FEATURES, item.span, "unstable feature");
1194 /// Lints for attempts to impl Drop on types that have `#[repr(C)]`
1195 /// attribute (see issue #24585).
1196 #[derive(Copy, Clone)]
1197 pub struct DropWithReprExtern;
1200 DROP_WITH_REPR_EXTERN,
1202 "use of #[repr(C)] on a type that implements Drop"
1205 impl LintPass for DropWithReprExtern {
1206 fn get_lints(&self) -> LintArray {
1207 lint_array!(DROP_WITH_REPR_EXTERN)
1211 impl LateLintPass for DropWithReprExtern {
1212 fn check_crate(&mut self, ctx: &LateContext, _: &hir::Crate) {
1213 for dtor_did in ctx.tcx.destructors.borrow().iter() {
1214 let (drop_impl_did, dtor_self_type) =
1215 if dtor_did.is_local() {
1216 let impl_did = ctx.tcx.map.get_parent_did(dtor_did.node);
1217 let ty = ctx.tcx.lookup_item_type(impl_did).ty;
1223 match dtor_self_type.sty {
1224 ty::TyEnum(self_type_def, _) |
1225 ty::TyStruct(self_type_def, _) => {
1226 let self_type_did = self_type_def.did;
1227 let hints = ctx.tcx.lookup_repr_hints(self_type_did);
1228 if hints.iter().any(|attr| *attr == attr::ReprExtern) &&
1229 self_type_def.dtor_kind().has_drop_flag() {
1230 let drop_impl_span = ctx.tcx.map.def_id_span(drop_impl_did,
1232 let self_defn_span = ctx.tcx.map.def_id_span(self_type_did,
1234 ctx.span_lint(DROP_WITH_REPR_EXTERN,
1236 "implementing Drop adds hidden state to types, \
1237 possibly conflicting with `#[repr(C)]`");
1238 // FIXME #19668: could be span_lint_note instead of manual guard.
1239 if ctx.current_level(DROP_WITH_REPR_EXTERN) != Level::Allow {
1240 ctx.sess().span_note(self_defn_span,
1241 "the `#[repr(C)]` attribute is attached here");