1 // Copyright 2012-2014 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 built in to rustc.
13 use metadata::csearch;
17 use middle::trans::adt; // for `adt::is_ffi_safe`
19 use middle::typeck::astconv::{ast_ty_to_ty, AstConv};
20 use middle::typeck::infer;
22 use util::ppaux::{ty_to_str};
27 use std::collections::HashMap;
38 use syntax::attr::AttrMetaMethods;
40 use syntax::codemap::Span;
41 use syntax::parse::token;
42 use syntax::visit::Visitor;
43 use syntax::{ast, ast_util, visit};
45 pub fn check_while_true_expr(cx: &Context, e: &ast::Expr) {
47 ast::ExprWhile(cond, _) => {
49 ast::ExprLit(lit) => {
51 ast::LitBool(true) => {
52 cx.span_lint(lint::WhileTrue,
54 "denote infinite loops with loop \
67 pub fn check_unused_casts(cx: &Context, e: &ast::Expr) {
69 ast::ExprCast(expr, ty) => {
70 let t_t = ast_ty_to_ty(cx, &infer::new_infer_ctxt(cx.tcx), ty);
71 if ty::get(ty::expr_ty(cx.tcx, expr)).sty == ty::get(t_t).sty {
72 cx.span_lint(lint::UnnecessaryTypecast, ty.span,
73 "unnecessary type cast");
80 pub fn check_type_limits(cx: &Context, e: &ast::Expr) {
82 ast::ExprUnary(ast::UnNeg, ex) => {
84 ast::ExprLit(lit) => {
87 cx.span_lint(lint::UnsignedNegate, e.span,
88 "negation of unsigned int literal may be unintentional");
94 let t = ty::expr_ty(cx.tcx, ex);
95 match ty::get(t).sty {
97 cx.span_lint(lint::UnsignedNegate, e.span,
98 "negation of unsigned int variable may be unintentional");
105 ast::ExprBinary(binop, l, r) => {
106 if is_comparison(binop) && !check_limits(cx.tcx, binop, l, r) {
107 cx.span_lint(lint::TypeLimits, e.span,
108 "comparison is useless due to type limits");
111 ast::ExprLit(lit) => {
112 match ty::get(ty::expr_ty(cx.tcx, e)).sty {
114 let int_type = if t == ast::TyI {
115 cx.tcx.sess.targ_cfg.int_type
117 let (min, max) = int_ty_range(int_type);
118 let mut lit_val: i64 = match lit.node {
119 ast::LitInt(v, _) => v,
120 ast::LitUint(v, _) => v as i64,
121 ast::LitIntUnsuffixed(v) => v,
124 if cx.negated_expr_id == e.id {
127 if lit_val < min || lit_val > max {
128 cx.span_lint(lint::TypeOverflow, e.span,
129 "literal out of range for its type");
133 let uint_type = if t == ast::TyU {
134 cx.tcx.sess.targ_cfg.uint_type
136 let (min, max) = uint_ty_range(uint_type);
137 let lit_val: u64 = match lit.node {
138 ast::LitInt(v, _) => v as u64,
139 ast::LitUint(v, _) => v,
140 ast::LitIntUnsuffixed(v) => v as u64,
143 if lit_val < min || lit_val > max {
144 cx.span_lint(lint::TypeOverflow, e.span,
145 "literal out of range for its type");
155 fn is_valid<T:cmp::PartialOrd>(binop: ast::BinOp, v: T,
156 min: T, max: T) -> bool {
158 ast::BiLt => v > min && v <= max,
159 ast::BiLe => v >= min && v < max,
160 ast::BiGt => v >= min && v < max,
161 ast::BiGe => v > min && v <= max,
162 ast::BiEq | ast::BiNe => v >= min && v <= max,
167 fn rev_binop(binop: ast::BinOp) -> ast::BinOp {
169 ast::BiLt => ast::BiGt,
170 ast::BiLe => ast::BiGe,
171 ast::BiGt => ast::BiLt,
172 ast::BiGe => ast::BiLe,
177 // for int & uint, be conservative with the warnings, so that the
178 // warnings are consistent between 32- and 64-bit platforms
179 fn int_ty_range(int_ty: ast::IntTy) -> (i64, i64) {
181 ast::TyI => (i64::MIN, i64::MAX),
182 ast::TyI8 => (i8::MIN as i64, i8::MAX as i64),
183 ast::TyI16 => (i16::MIN as i64, i16::MAX as i64),
184 ast::TyI32 => (i32::MIN as i64, i32::MAX as i64),
185 ast::TyI64 => (i64::MIN, i64::MAX)
189 fn uint_ty_range(uint_ty: ast::UintTy) -> (u64, u64) {
191 ast::TyU => (u64::MIN, u64::MAX),
192 ast::TyU8 => (u8::MIN as u64, u8::MAX as u64),
193 ast::TyU16 => (u16::MIN as u64, u16::MAX as u64),
194 ast::TyU32 => (u32::MIN as u64, u32::MAX as u64),
195 ast::TyU64 => (u64::MIN, u64::MAX)
199 fn check_limits(tcx: &ty::ctxt, binop: ast::BinOp,
200 l: &ast::Expr, r: &ast::Expr) -> bool {
201 let (lit, expr, swap) = match (&l.node, &r.node) {
202 (&ast::ExprLit(_), _) => (l, r, true),
203 (_, &ast::ExprLit(_)) => (r, l, false),
206 // Normalize the binop so that the literal is always on the RHS in
208 let norm_binop = if swap { rev_binop(binop) } else { binop };
209 match ty::get(ty::expr_ty(tcx, expr)).sty {
210 ty::ty_int(int_ty) => {
211 let (min, max) = int_ty_range(int_ty);
212 let lit_val: i64 = match lit.node {
213 ast::ExprLit(li) => match li.node {
214 ast::LitInt(v, _) => v,
215 ast::LitUint(v, _) => v as i64,
216 ast::LitIntUnsuffixed(v) => v,
221 is_valid(norm_binop, lit_val, min, max)
223 ty::ty_uint(uint_ty) => {
224 let (min, max): (u64, u64) = uint_ty_range(uint_ty);
225 let lit_val: u64 = match lit.node {
226 ast::ExprLit(li) => match li.node {
227 ast::LitInt(v, _) => v as u64,
228 ast::LitUint(v, _) => v,
229 ast::LitIntUnsuffixed(v) => v as u64,
234 is_valid(norm_binop, lit_val, min, max)
240 fn is_comparison(binop: ast::BinOp) -> bool {
242 ast::BiEq | ast::BiLt | ast::BiLe |
243 ast::BiNe | ast::BiGe | ast::BiGt => true,
249 pub fn check_item_ctypes(cx: &Context, it: &ast::Item) {
250 fn check_ty(cx: &Context, ty: &ast::Ty) {
252 ast::TyPath(_, _, id) => {
253 match cx.tcx.def_map.borrow().get_copy(&id) {
254 def::DefPrimTy(ast::TyInt(ast::TyI)) => {
255 cx.span_lint(lint::CTypes, ty.span,
256 "found rust type `int` in foreign module, while \
257 libc::c_int or libc::c_long should be used");
259 def::DefPrimTy(ast::TyUint(ast::TyU)) => {
260 cx.span_lint(lint::CTypes, ty.span,
261 "found rust type `uint` in foreign module, while \
262 libc::c_uint or libc::c_ulong should be used");
264 def::DefTy(def_id) => {
265 if !adt::is_ffi_safe(cx.tcx, def_id) {
266 cx.span_lint(lint::CTypes, ty.span,
267 "found enum type without foreign-function-safe \
268 representation annotation in foreign module");
269 // hmm... this message could be more helpful
275 ast::TyPtr(ref mt) => { check_ty(cx, mt.ty) }
280 fn check_foreign_fn(cx: &Context, decl: &ast::FnDecl) {
281 for input in decl.inputs.iter() {
282 check_ty(cx, input.ty);
284 check_ty(cx, decl.output)
288 ast::ItemForeignMod(ref nmod) if nmod.abi != abi::RustIntrinsic => {
289 for ni in nmod.items.iter() {
291 ast::ForeignItemFn(decl, _) => check_foreign_fn(cx, decl),
292 ast::ForeignItemStatic(t, _) => check_ty(cx, t)
296 _ => {/* nothing to do */ }
300 pub fn check_heap_type(cx: &Context, span: Span, ty: ty::t) {
301 let xs = [lint::ManagedHeapMemory, lint::OwnedHeapMemory, lint::HeapMemory];
302 for &lint in xs.iter() {
303 if cx.get_level(lint) == lint::Allow { continue }
307 ty::fold_ty(cx.tcx, ty, |t| {
308 match ty::get(t).sty {
313 ty::ty_trait(box ty::TyTrait {
314 store: ty::UniqTraitStore, ..
316 ty::ty_closure(box ty::ClosureTy {
317 store: ty::UniqTraitStore,
328 if n_uniq > 0 && lint != lint::ManagedHeapMemory {
329 let s = ty_to_str(cx.tcx, ty);
330 let m = format!("type uses owned (Box type) pointers: {}", s);
331 cx.span_lint(lint, span, m.as_slice());
334 if n_box > 0 && lint != lint::OwnedHeapMemory {
335 let s = ty_to_str(cx.tcx, ty);
336 let m = format!("type uses managed (@ type) pointers: {}", s);
337 cx.span_lint(lint, span, m.as_slice());
342 pub fn check_heap_item(cx: &Context, it: &ast::Item) {
347 ast::ItemStruct(..) => check_heap_type(cx, it.span,
348 ty::node_id_to_type(cx.tcx,
353 // If it's a struct, we also have to check the fields' types
355 ast::ItemStruct(struct_def, _) => {
356 for struct_field in struct_def.fields.iter() {
357 check_heap_type(cx, struct_field.span,
358 ty::node_id_to_type(cx.tcx,
359 struct_field.node.id));
366 struct RawPtrDerivingVisitor<'a> {
370 impl<'a> Visitor<()> for RawPtrDerivingVisitor<'a> {
371 fn visit_ty(&mut self, ty: &ast::Ty, _: ()) {
372 static MSG: &'static str = "use of `#[deriving]` with a raw pointer";
374 ast::TyPtr(..) => self.cx.span_lint(lint::RawPointerDeriving, ty.span, MSG),
377 visit::walk_ty(self, ty, ());
379 // explicit override to a no-op to reduce code bloat
380 fn visit_expr(&mut self, _: &ast::Expr, _: ()) {}
381 fn visit_block(&mut self, _: &ast::Block, _: ()) {}
384 pub fn check_raw_ptr_deriving(cx: &mut Context, item: &ast::Item) {
385 if !attr::contains_name(item.attrs.as_slice(), "automatically_derived") {
388 let did = match item.node {
389 ast::ItemImpl(..) => {
390 match ty::get(ty::node_id_to_type(cx.tcx, item.id)).sty {
391 ty::ty_enum(did, _) => did,
392 ty::ty_struct(did, _) => did,
398 if !ast_util::is_local(did) { return }
399 let item = match cx.tcx.map.find(did.node) {
400 Some(ast_map::NodeItem(item)) => item,
403 if !cx.checked_raw_pointers.insert(item.id) { return }
405 ast::ItemStruct(..) | ast::ItemEnum(..) => {
406 let mut visitor = RawPtrDerivingVisitor { cx: cx };
407 visit::walk_item(&mut visitor, item, ());
413 pub fn check_unused_attribute(cx: &Context, attr: &ast::Attribute) {
414 static ATTRIBUTE_WHITELIST: &'static [&'static str] = &'static [
415 // FIXME: #14408 whitelist docs since rustdoc looks at them
418 // FIXME: #14406 these are processed in trans, which happens after the
420 "address_insignificant",
433 // not used anywhere (!?) but apparently we want to keep them around
438 // FIXME: #14407 these are only looked at on-demand so we can't
439 // guarantee they'll have already been checked
449 static CRATE_ATTRS: &'static [&'static str] = &'static [
463 for &name in ATTRIBUTE_WHITELIST.iter() {
464 if attr.check_name(name) {
469 if !attr::is_used(attr) {
470 cx.span_lint(lint::UnusedAttribute, attr.span, "unused attribute");
471 if CRATE_ATTRS.contains(&attr.name().get()) {
472 let msg = match attr.node.style {
473 ast::AttrOuter => "crate-level attribute should be an inner \
474 attribute: add an exclamation mark: #![foo]",
475 ast::AttrInner => "crate-level attribute should be in the \
478 cx.span_lint(lint::UnusedAttribute, attr.span, msg);
483 pub fn check_heap_expr(cx: &Context, e: &ast::Expr) {
484 let ty = ty::expr_ty(cx.tcx, e);
485 check_heap_type(cx, e.span, ty);
488 pub fn check_path_statement(cx: &Context, s: &ast::Stmt) {
490 ast::StmtSemi(expr, _) => {
492 ast::ExprPath(_) => {
493 cx.span_lint(lint::PathStatement,
495 "path statement with no effect");
504 pub fn check_unused_result(cx: &Context, s: &ast::Stmt) {
505 let expr = match s.node {
506 ast::StmtSemi(expr, _) => expr,
509 let t = ty::expr_ty(cx.tcx, expr);
510 match ty::get(t).sty {
511 ty::ty_nil | ty::ty_bot | ty::ty_bool => return,
515 ast::ExprRet(..) => return,
519 let t = ty::expr_ty(cx.tcx, expr);
520 let mut warned = false;
521 match ty::get(t).sty {
522 ty::ty_struct(did, _) |
523 ty::ty_enum(did, _) => {
524 if ast_util::is_local(did) {
525 match cx.tcx.map.get(did.node) {
526 ast_map::NodeItem(it) => {
527 if attr::contains_name(it.attrs.as_slice(),
529 cx.span_lint(lint::UnusedMustUse, s.span,
530 "unused result which must be used");
537 csearch::get_item_attrs(&cx.tcx.sess.cstore, did, |attrs| {
538 if attr::contains_name(attrs.as_slice(), "must_use") {
539 cx.span_lint(lint::UnusedMustUse, s.span,
540 "unused result which must be used");
549 cx.span_lint(lint::UnusedResult, s.span, "unused result");
553 pub fn check_deprecated_owned_vector(cx: &Context, e: &ast::Expr) {
554 let t = ty::expr_ty(cx.tcx, e);
555 match ty::get(t).sty {
556 ty::ty_uniq(t) => match ty::get(t).sty {
557 ty::ty_vec(_, None) => {
558 cx.span_lint(lint::DeprecatedOwnedVector, e.span,
559 "use of deprecated `~[]` vector; replaced by `std::vec::Vec`")
567 pub fn check_item_non_camel_case_types(cx: &Context, it: &ast::Item) {
568 fn is_camel_case(ident: ast::Ident) -> bool {
569 let ident = token::get_ident(ident);
570 assert!(!ident.get().is_empty());
571 let ident = ident.get().trim_chars('_');
573 // start with a non-lowercase letter rather than non-uppercase
574 // ones (some scripts don't have a concept of upper/lowercase)
575 !ident.char_at(0).is_lowercase() && !ident.contains_char('_')
578 fn to_camel_case(s: &str) -> String {
579 s.split('_').flat_map(|word| word.chars().enumerate().map(|(i, c)|
580 if i == 0 { c.to_uppercase() }
585 fn check_case(cx: &Context, sort: &str, ident: ast::Ident, span: Span) {
586 let s = token::get_ident(ident);
588 if !is_camel_case(ident) {
590 NonCamelCaseTypes, span,
591 format!("{} `{}` should have a camel case name such as `{}`",
592 sort, s, to_camel_case(s.get())).as_slice());
597 ast::ItemTy(..) | ast::ItemStruct(..) => {
598 check_case(cx, "type", it.ident, it.span)
600 ast::ItemTrait(..) => {
601 check_case(cx, "trait", it.ident, it.span)
603 ast::ItemEnum(ref enum_definition, _) => {
604 check_case(cx, "type", it.ident, it.span);
605 for variant in enum_definition.variants.iter() {
606 check_case(cx, "variant", variant.node.name, variant.span);
613 pub fn check_snake_case(cx: &Context, sort: &str, ident: ast::Ident, span: Span) {
614 fn is_snake_case(ident: ast::Ident) -> bool {
615 let ident = token::get_ident(ident);
616 assert!(!ident.get().is_empty());
617 let ident = ident.get().trim_chars('_');
619 let mut allow_underscore = true;
620 ident.chars().all(|c| {
621 allow_underscore = match c {
622 c if c.is_lowercase() || c.is_digit() => true,
623 '_' if allow_underscore => false,
630 fn to_snake_case(str: &str) -> String {
631 let mut words = vec![];
632 for s in str.split('_') {
633 let mut buf = String::new();
634 if s.is_empty() { continue; }
635 for ch in s.chars() {
636 if !buf.is_empty() && ch.is_uppercase() {
640 buf.push_char(ch.to_lowercase());
647 let s = token::get_ident(ident);
649 if !is_snake_case(ident) {
650 cx.span_lint(lint::NonSnakeCaseFunctions, span,
651 format!("{} `{}` should have a snake case name such as `{}`",
652 sort, s, to_snake_case(s.get())).as_slice());
656 pub fn check_item_non_uppercase_statics(cx: &Context, it: &ast::Item) {
658 // only check static constants
659 ast::ItemStatic(_, ast::MutImmutable, _) => {
660 let s = token::get_ident(it.ident);
661 // check for lowercase letters rather than non-uppercase
662 // ones (some scripts don't have a concept of
664 if s.get().chars().any(|c| c.is_lowercase()) {
665 cx.span_lint(lint::NonUppercaseStatics, it.span,
666 format!("static constant `{}` should have an uppercase name \
667 such as `{}`", s.get(),
668 s.get().chars().map(|c| c.to_uppercase())
669 .collect::<String>().as_slice()).as_slice());
676 pub fn check_pat_non_uppercase_statics(cx: &Context, p: &ast::Pat) {
677 // Lint for constants that look like binding identifiers (#7526)
678 match (&p.node, cx.tcx.def_map.borrow().find(&p.id)) {
679 (&ast::PatIdent(_, ref path, _), Some(&def::DefStatic(_, false))) => {
680 // last identifier alone is right choice for this lint.
681 let ident = path.segments.last().unwrap().identifier;
682 let s = token::get_ident(ident);
683 if s.get().chars().any(|c| c.is_lowercase()) {
684 cx.span_lint(lint::NonUppercasePatternStatics, path.span,
685 format!("static constant in pattern `{}` should have an uppercase \
686 name such as `{}`", s.get(),
687 s.get().chars().map(|c| c.to_uppercase())
688 .collect::<String>().as_slice()).as_slice());
695 pub fn check_pat_uppercase_variable(cx: &Context, p: &ast::Pat) {
697 &ast::PatIdent(_, ref path, _) => {
698 match cx.tcx.def_map.borrow().find(&p.id) {
699 Some(&def::DefLocal(_, _)) | Some(&def::DefBinding(_, _)) |
700 Some(&def::DefArg(_, _)) => {
701 // last identifier alone is right choice for this lint.
702 let ident = path.segments.last().unwrap().identifier;
703 let s = token::get_ident(ident);
704 if s.get().len() > 0 && s.get().char_at(0).is_uppercase() {
708 "variable names should start with a lowercase character");
718 pub fn check_struct_uppercase_variable(cx: &Context, s: &ast::StructDef) {
719 for sf in s.fields.iter() {
721 ast::StructField_ { kind: ast::NamedField(ident, _), .. } => {
722 let s = token::get_ident(ident);
723 if s.get().char_at(0).is_uppercase() {
727 "structure field names should start with a lowercase character");
735 pub fn check_unnecessary_parens_core(cx: &Context, value: &ast::Expr, msg: &str) {
737 ast::ExprParen(_) => {
738 cx.span_lint(lint::UnnecessaryParens, value.span,
739 format!("unnecessary parentheses around {}",
746 pub fn check_unnecessary_parens_expr(cx: &Context, e: &ast::Expr) {
747 let (value, msg) = match e.node {
748 ast::ExprIf(cond, _, _) => (cond, "`if` condition"),
749 ast::ExprWhile(cond, _) => (cond, "`while` condition"),
750 ast::ExprMatch(head, _) => (head, "`match` head expression"),
751 ast::ExprRet(Some(value)) => (value, "`return` value"),
752 ast::ExprAssign(_, value) => (value, "assigned value"),
753 ast::ExprAssignOp(_, _, value) => (value, "assigned value"),
756 check_unnecessary_parens_core(cx, value, msg);
759 pub fn check_unnecessary_parens_stmt(cx: &Context, s: &ast::Stmt) {
760 let (value, msg) = match s.node {
761 ast::StmtDecl(decl, _) => match decl.node {
762 ast::DeclLocal(local) => match local.init {
763 Some(value) => (value, "assigned value"),
770 check_unnecessary_parens_core(cx, value, msg);
773 pub fn check_unused_unsafe(cx: &Context, e: &ast::Expr) {
775 // Don't warn about generated blocks, that'll just pollute the output.
776 ast::ExprBlock(ref blk) => {
777 if blk.rules == ast::UnsafeBlock(ast::UserProvided) &&
778 !cx.tcx.used_unsafe.borrow().contains(&blk.id) {
779 cx.span_lint(lint::UnusedUnsafe, blk.span,
780 "unnecessary `unsafe` block");
787 pub fn check_unsafe_block(cx: &Context, e: &ast::Expr) {
789 // Don't warn about generated blocks, that'll just pollute the output.
790 ast::ExprBlock(ref blk) if blk.rules == ast::UnsafeBlock(ast::UserProvided) => {
791 cx.span_lint(lint::UnsafeBlock, blk.span, "usage of an `unsafe` block");
797 pub fn check_unused_mut_pat(cx: &Context, pats: &[@ast::Pat]) {
798 // collect all mutable pattern and group their NodeIDs by their Identifier to
799 // avoid false warnings in match arms with multiple patterns
800 let mut mutables = HashMap::new();
801 for &p in pats.iter() {
802 pat_util::pat_bindings(&cx.tcx.def_map, p, |mode, id, _, path| {
804 ast::BindByValue(ast::MutMutable) => {
805 if path.segments.len() != 1 {
806 cx.tcx.sess.span_bug(p.span,
807 "mutable binding that doesn't consist \
808 of exactly one segment");
810 let ident = path.segments.get(0).identifier;
811 if !token::get_ident(ident).get().starts_with("_") {
812 mutables.insert_or_update_with(ident.name as uint, vec!(id), |_, old| {
823 let used_mutables = cx.tcx.used_mut_nodes.borrow();
824 for (_, v) in mutables.iter() {
825 if !v.iter().any(|e| used_mutables.contains(e)) {
826 cx.span_lint(lint::UnusedMut, cx.tcx.map.span(*v.get(0)),
827 "variable does not need to be mutable");
837 pub fn check_unnecessary_allocation(cx: &Context, e: &ast::Expr) {
838 // Warn if string and vector literals with sigils, or boxing expressions,
839 // are immediately borrowed.
840 let allocation = match e.node {
841 ast::ExprVstore(e2, ast::ExprVstoreUniq) => {
843 ast::ExprLit(lit) if ast_util::lit_is_str(lit) => {
846 ast::ExprVec(..) => VectorAllocation,
850 ast::ExprUnary(ast::UnUniq, _) |
851 ast::ExprUnary(ast::UnBox, _) => BoxAllocation,
857 cx.span_lint(lint::UnnecessaryAllocation, e.span, msg);
860 match cx.tcx.adjustments.borrow().find(&e.id) {
861 Some(adjustment) => {
863 ty::AutoDerefRef(ty::AutoDerefRef { autoref, .. }) => {
864 match (allocation, autoref) {
865 (VectorAllocation, Some(ty::AutoBorrowVec(..))) => {
866 report("unnecessary allocation, the sigil can be \
870 Some(ty::AutoPtr(_, ast::MutImmutable))) => {
871 report("unnecessary allocation, use & instead");
874 Some(ty::AutoPtr(_, ast::MutMutable))) => {
875 report("unnecessary allocation, use &mut \
889 pub fn check_missing_doc_attrs(cx: &Context,
890 id: Option<ast::NodeId>,
891 attrs: &[ast::Attribute],
893 desc: &'static str) {
894 // If we're building a test harness, then warning about
895 // documentation is probably not really relevant right now.
896 if cx.tcx.sess.opts.test { return }
898 // `#[doc(hidden)]` disables missing_doc check.
899 if cx.is_doc_hidden { return }
901 // Only check publicly-visible items, using the result from the privacy pass. It's an option so
902 // the crate root can also use this function (it doesn't have a NodeId).
904 Some(ref id) if !cx.exported_items.contains(id) => return,
908 let has_doc = attrs.iter().any(|a| {
909 match a.node.value.node {
910 ast::MetaNameValue(ref name, _) if name.equiv(&("doc")) => true,
915 cx.span_lint(lint::MissingDoc,
917 format!("missing documentation for {}",
922 pub fn check_missing_doc_item(cx: &Context, it: &ast::Item) {
923 let desc = match it.node {
924 ast::ItemFn(..) => "a function",
925 ast::ItemMod(..) => "a module",
926 ast::ItemEnum(..) => "an enum",
927 ast::ItemStruct(..) => "a struct",
928 ast::ItemTrait(..) => "a trait",
931 check_missing_doc_attrs(cx,
938 pub fn check_missing_doc_method(cx: &Context, m: &ast::Method) {
939 // If the method is an impl for a trait, don't doc.
940 if lint::method_context(cx, m) == lint::TraitImpl { return; }
942 // Otherwise, doc according to privacy. This will also check
943 // doc for default methods defined on traits.
944 check_missing_doc_attrs(cx,
951 pub fn check_missing_doc_ty_method(cx: &Context, tm: &ast::TypeMethod) {
952 check_missing_doc_attrs(cx,
959 pub fn check_missing_doc_struct_field(cx: &Context, sf: &ast::StructField) {
961 ast::NamedField(_, vis) if vis == ast::Public =>
962 check_missing_doc_attrs(cx,
963 Some(cx.cur_struct_def_id),
964 sf.node.attrs.as_slice(),
971 pub fn check_missing_doc_variant(cx: &Context, v: &ast::Variant) {
972 check_missing_doc_attrs(cx,
974 v.node.attrs.as_slice(),
979 /// Checks for use of items with #[deprecated], #[experimental] and
980 /// #[unstable] (or none of them) attributes.
981 pub fn check_stability(cx: &Context, e: &ast::Expr) {
982 let id = match e.node {
983 ast::ExprPath(..) | ast::ExprStruct(..) => {
984 match cx.tcx.def_map.borrow().find(&e.id) {
985 Some(&def) => def.def_id(),
989 ast::ExprMethodCall(..) => {
990 let method_call = typeck::MethodCall::expr(e.id);
991 match cx.tcx.method_map.borrow().find(&method_call) {
993 match method.origin {
994 typeck::MethodStatic(def_id) => {
995 // If this implements a trait method, get def_id
996 // of the method inside trait definition.
997 // Otherwise, use the current def_id (which refers
998 // to the method inside impl).
999 ty::trait_method_of_method(
1000 cx.tcx, def_id).unwrap_or(def_id)
1002 typeck::MethodParam(typeck::MethodParam {
1007 | typeck::MethodObject(typeck::MethodObject {
1011 }) => ty::trait_method(cx.tcx, trait_id, index).def_id
1020 let stability = if ast_util::is_local(id) {
1022 let s = cx.tcx.map.with_attrs(id.node, |attrs| {
1023 attrs.map(|a| attr::find_stability(a.as_slice()))
1028 // no possibility of having attributes
1029 // (e.g. it's a local variable), so just
1037 // run through all the attributes and take the first
1039 csearch::get_item_attrs(&cx.tcx.sess.cstore, id, |attrs| {
1041 s = attr::find_stability(attrs.as_slice())
1047 let (lint, label) = match stability {
1048 // no stability attributes == Unstable
1049 None => (lint::Unstable, "unmarked"),
1050 Some(attr::Stability { level: attr::Unstable, .. }) =>
1051 (lint::Unstable, "unstable"),
1052 Some(attr::Stability { level: attr::Experimental, .. }) =>
1053 (lint::Experimental, "experimental"),
1054 Some(attr::Stability { level: attr::Deprecated, .. }) =>
1055 (lint::Deprecated, "deprecated"),
1059 let msg = match stability {
1060 Some(attr::Stability { text: Some(ref s), .. }) => {
1061 format!("use of {} item: {}", label, *s)
1063 _ => format!("use of {} item", label)
1066 cx.span_lint(lint, e.span, msg.as_slice());
1069 pub fn check_enum_variant_sizes(cx: &mut Context, it: &ast::Item) {
1071 ast::ItemEnum(..) => {
1072 match cx.cur.find(&(lint::VariantSizeDifference as uint)) {
1073 Some(&(lvl, src)) if lvl != lint::Allow => {
1074 cx.node_levels.insert((it.id, lint::VariantSizeDifference), (lvl, src));