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.
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 //! A 'lint' check is a kind of miscellaneous constraint that a user _might_
12 //! want to enforce, but might reasonably want to permit as well, on a
13 //! module-by-module basis. They contrast with static constraints enforced by
14 //! other phases of the compiler, which are generally required to hold in order
15 //! to compile the program at all.
16 //!
17 //! The lint checking is all consolidated into one pass which runs just before
18 //! translation to LLVM bytecode. Throughout compilation, lint warnings can be
19 //! added via the `add_lint` method on the Session structure. This requires a
20 //! span and an id of the node that the lint is being added to. The lint isn't
21 //! actually emitted at that time because it is unknown what the actual lint
22 //! level at that location is.
23 //!
24 //! To actually emit lint warnings/errors, a separate pass is used just before
25 //! translation. A context keeps track of the current state of all lint levels.
26 //! Upon entering a node of the ast which can modify the lint settings, the
27 //! previous lint state is pushed onto a stack and the ast is then recursed
28 //! upon. As the ast is traversed, this keeps track of the current lint level
29 //! for all lint attributes.
30 //!
31 //! To add a new lint warning, all you need to do is to either invoke `add_lint`
32 //! on the session at the appropriate time, or write a few linting functions and
33 //! modify the Context visitor appropriately. If you're adding lints from the
34 //! Context itself, span_lint should be used instead of add_lint.
35
36 #![allow(non_camel_case_types)]
37
38 use driver::session;
39 use metadata::csearch;
40 use middle::dead::DEAD_CODE_LINT_STR;
41 use middle::pat_util;
42 use middle::privacy;
43 use middle::trans::adt; // for `adt::is_ffi_safe`
44 use middle::ty;
45 use middle::typeck::astconv::{ast_ty_to_ty, AstConv};
46 use middle::typeck::infer;
47 use middle::typeck;
48 use util::ppaux::{ty_to_str};
49
50 use std::cmp;
51 use collections::HashMap;
52 use std::i16;
53 use std::i32;
54 use std::i64;
55 use std::i8;
56 use std::rc::Rc;
57 use std::to_str::ToStr;
58 use std::u16;
59 use std::u32;
60 use std::u64;
61 use std::u8;
62 use collections::SmallIntMap;
63 use syntax::abi;
64 use syntax::ast_map;
65 use syntax::ast_util::IdVisitingOperation;
66 use syntax::attr::{AttrMetaMethods, AttributeMethods};
67 use syntax::attr;
68 use syntax::codemap::Span;
69 use syntax::parse::token::InternedString;
70 use syntax::parse::token;
71 use syntax::visit::Visitor;
72 use syntax::{ast, ast_util, visit};
73
74 #[deriving(Clone, Eq, Ord, TotalEq, TotalOrd)]
75 pub enum Lint {
76 CTypes,
77 UnusedImports,
78 UnnecessaryQualification,
79 WhileTrue,
80 PathStatement,
81 UnrecognizedLint,
82 NonCamelCaseTypes,
83 NonUppercaseStatics,
84 NonUppercasePatternStatics,
85 UppercaseVariables,
86 UnnecessaryParens,
87 TypeLimits,
88 TypeOverflow,
89 UnusedUnsafe,
90 UnsafeBlock,
91 AttributeUsage,
92 UnknownFeatures,
93 UnknownCrateType,
94 UnsignedNegate,
95
96 ManagedHeapMemory,
97 OwnedHeapMemory,
98 HeapMemory,
99
100 UnusedVariable,
101 DeadAssignment,
102 UnusedMut,
103 UnnecessaryAllocation,
104 DeadCode,
105 VisiblePrivateTypes,
106 UnnecessaryTypecast,
107
108 MissingDoc,
109 UnreachableCode,
110
111 Deprecated,
112 Experimental,
113 Unstable,
114
115 UnusedMustUse,
116 UnusedResult,
117
118 DeprecatedOwnedVector,
119
120 Warnings,
121
122 RawPointerDeriving,
123 }
124
125 pub fn level_to_str(lv: level) -> &'static str {
126 match lv {
127 allow => "allow",
128 warn => "warn",
129 deny => "deny",
130 forbid => "forbid"
131 }
132 }
133
134 #[deriving(Clone, Eq, Ord, TotalEq, TotalOrd)]
135 pub enum level {
136 allow, warn, deny, forbid
137 }
138
139 #[deriving(Clone, Eq, Ord, TotalEq, TotalOrd)]
140 pub struct LintSpec {
141 pub default: level,
142 pub lint: Lint,
143 pub desc: &'static str,
144 }
145
146 pub type LintDict = HashMap<&'static str, LintSpec>;
147
148 #[deriving(Eq)]
149 enum LintSource {
150 Node(Span),
151 Default,
152 CommandLine
153 }
154
155 static lint_table: &'static [(&'static str, LintSpec)] = &[
156 ("ctypes",
157 LintSpec {
158 lint: CTypes,
159 desc: "proper use of libc types in foreign modules",
160 default: warn
161 }),
162
163 ("unused_imports",
164 LintSpec {
165 lint: UnusedImports,
166 desc: "imports that are never used",
167 default: warn
168 }),
169
170 ("unnecessary_qualification",
171 LintSpec {
172 lint: UnnecessaryQualification,
173 desc: "detects unnecessarily qualified names",
174 default: allow
175 }),
176
177 ("while_true",
178 LintSpec {
179 lint: WhileTrue,
180 desc: "suggest using `loop { }` instead of `while true { }`",
181 default: warn
182 }),
183
184 ("path_statement",
185 LintSpec {
186 lint: PathStatement,
187 desc: "path statements with no effect",
188 default: warn
189 }),
190
191 ("unrecognized_lint",
192 LintSpec {
193 lint: UnrecognizedLint,
194 desc: "unrecognized lint attribute",
195 default: warn
196 }),
197
198 ("non_camel_case_types",
199 LintSpec {
200 lint: NonCamelCaseTypes,
201 desc: "types, variants and traits should have camel case names",
202 default: warn
203 }),
204
205 ("non_uppercase_statics",
206 LintSpec {
207 lint: NonUppercaseStatics,
208 desc: "static constants should have uppercase identifiers",
209 default: allow
210 }),
211
212 ("non_uppercase_pattern_statics",
213 LintSpec {
214 lint: NonUppercasePatternStatics,
215 desc: "static constants in match patterns should be all caps",
216 default: warn
217 }),
218
219 ("uppercase_variables",
220 LintSpec {
221 lint: UppercaseVariables,
222 desc: "variable and structure field names should start with a lowercase character",
223 default: warn
224 }),
225
226 ("unnecessary_parens",
227 LintSpec {
228 lint: UnnecessaryParens,
229 desc: "`if`, `match`, `while` and `return` do not need parentheses",
230 default: warn
231 }),
232
233 ("managed_heap_memory",
234 LintSpec {
235 lint: ManagedHeapMemory,
236 desc: "use of managed (@ type) heap memory",
237 default: allow
238 }),
239
240 ("owned_heap_memory",
241 LintSpec {
242 lint: OwnedHeapMemory,
243 desc: "use of owned (Box type) heap memory",
244 default: allow
245 }),
246
247 ("heap_memory",
248 LintSpec {
249 lint: HeapMemory,
250 desc: "use of any (Box type or @ type) heap memory",
251 default: allow
252 }),
253
254 ("type_limits",
255 LintSpec {
256 lint: TypeLimits,
257 desc: "comparisons made useless by limits of the types involved",
258 default: warn
259 }),
260
261 ("type_overflow",
262 LintSpec {
263 lint: TypeOverflow,
264 desc: "literal out of range for its type",
265 default: warn
266 }),
267
268
269 ("unused_unsafe",
270 LintSpec {
271 lint: UnusedUnsafe,
272 desc: "unnecessary use of an `unsafe` block",
273 default: warn
274 }),
275
276 ("unsafe_block",
277 LintSpec {
278 lint: UnsafeBlock,
279 desc: "usage of an `unsafe` block",
280 default: allow
281 }),
282
283 ("attribute_usage",
284 LintSpec {
285 lint: AttributeUsage,
286 desc: "detects bad use of attributes",
287 default: warn
288 }),
289
290 ("unused_variable",
291 LintSpec {
292 lint: UnusedVariable,
293 desc: "detect variables which are not used in any way",
294 default: warn
295 }),
296
297 ("dead_assignment",
298 LintSpec {
299 lint: DeadAssignment,
300 desc: "detect assignments that will never be read",
301 default: warn
302 }),
303
304 ("unnecessary_typecast",
305 LintSpec {
306 lint: UnnecessaryTypecast,
307 desc: "detects unnecessary type casts, that can be removed",
308 default: allow,
309 }),
310
311 ("unused_mut",
312 LintSpec {
313 lint: UnusedMut,
314 desc: "detect mut variables which don't need to be mutable",
315 default: warn
316 }),
317
318 ("unnecessary_allocation",
319 LintSpec {
320 lint: UnnecessaryAllocation,
321 desc: "detects unnecessary allocations that can be eliminated",
322 default: warn
323 }),
324
325 (DEAD_CODE_LINT_STR,
326 LintSpec {
327 lint: DeadCode,
328 desc: "detect piece of code that will never be used",
329 default: warn
330 }),
331 ("visible_private_types",
332 LintSpec {
333 lint: VisiblePrivateTypes,
334 desc: "detect use of private types in exported type signatures",
335 default: warn
336 }),
337
338 ("missing_doc",
339 LintSpec {
340 lint: MissingDoc,
341 desc: "detects missing documentation for public members",
342 default: allow
343 }),
344
345 ("unreachable_code",
346 LintSpec {
347 lint: UnreachableCode,
348 desc: "detects unreachable code",
349 default: warn
350 }),
351
352 ("deprecated",
353 LintSpec {
354 lint: Deprecated,
355 desc: "detects use of #[deprecated] items",
356 default: warn
357 }),
358
359 ("experimental",
360 LintSpec {
361 lint: Experimental,
362 desc: "detects use of #[experimental] items",
363 default: warn
364 }),
365
366 ("unstable",
367 LintSpec {
368 lint: Unstable,
369 desc: "detects use of #[unstable] items (incl. items with no stability attribute)",
370 default: allow
371 }),
372
373 ("warnings",
374 LintSpec {
375 lint: Warnings,
376 desc: "mass-change the level for lints which produce warnings",
377 default: warn
378 }),
379
380 ("unknown_features",
381 LintSpec {
382 lint: UnknownFeatures,
383 desc: "unknown features found in crate-level #[feature] directives",
384 default: deny,
385 }),
386
387 ("unknown_crate_type",
388 LintSpec {
389 lint: UnknownCrateType,
390 desc: "unknown crate type found in #[crate_type] directive",
391 default: deny,
392 }),
393
394 ("unsigned_negate",
395 LintSpec {
396 lint: UnsignedNegate,
397 desc: "using an unary minus operator on unsigned type",
398 default: warn
399 }),
400
401 ("unused_must_use",
402 LintSpec {
403 lint: UnusedMustUse,
404 desc: "unused result of a type flagged as #[must_use]",
405 default: warn,
406 }),
407
408 ("unused_result",
409 LintSpec {
410 lint: UnusedResult,
411 desc: "unused result of an expression in a statement",
412 default: allow,
413 }),
414
415 ("deprecated_owned_vector",
416 LintSpec {
417 lint: DeprecatedOwnedVector,
418 desc: "use of a `~[T]` vector",
419 default: allow,
420 }),
421
422 ("raw_pointer_deriving",
423 LintSpec {
424 lint: RawPointerDeriving,
425 desc: "uses of #[deriving] with raw pointers are rarely correct",
426 default: warn,
427 }),
428 ];
429
430 /*
431 Pass names should not contain a '-', as the compiler normalizes
432 '-' to '_' in command-line flags
433 */
434 pub fn get_lint_dict() -> LintDict {
435 lint_table.iter().map(|&(k, v)| (k, v)).collect()
436 }
437
438 struct Context<'a> {
439 // All known lint modes (string versions)
440 dict: LintDict,
441 // Current levels of each lint warning
442 cur: SmallIntMap<(level, LintSource)>,
443 // context we're checking in (used to access fields like sess)
444 tcx: &'a ty::ctxt,
445 // Items exported by the crate; used by the missing_doc lint.
446 exported_items: &'a privacy::ExportedItems,
447 // The id of the current `ast::StructDef` being walked.
448 cur_struct_def_id: ast::NodeId,
449 // Whether some ancestor of the current node was marked
450 // #[doc(hidden)].
451 is_doc_hidden: bool,
452
453 // When recursing into an attributed node of the ast which modifies lint
454 // levels, this stack keeps track of the previous lint levels of whatever
455 // was modified.
456 lint_stack: Vec<(Lint, level, LintSource)> ,
457
458 // id of the last visited negated expression
459 negated_expr_id: ast::NodeId
460 }
461
462 impl<'a> Context<'a> {
463 fn get_level(&self, lint: Lint) -> level {
464 match self.cur.find(&(lint as uint)) {
465 Some(&(lvl, _)) => lvl,
466 None => allow
467 }
468 }
469
470 fn get_source(&self, lint: Lint) -> LintSource {
471 match self.cur.find(&(lint as uint)) {
472 Some(&(_, src)) => src,
473 None => Default
474 }
475 }
476
477 fn set_level(&mut self, lint: Lint, level: level, src: LintSource) {
478 if level == allow {
479 self.cur.remove(&(lint as uint));
480 } else {
481 self.cur.insert(lint as uint, (level, src));
482 }
483 }
484
485 fn lint_to_str(&self, lint: Lint) -> &'static str {
486 for (k, v) in self.dict.iter() {
487 if v.lint == lint {
488 return *k;
489 }
490 }
491 fail!("unregistered lint {:?}", lint);
492 }
493
494 fn span_lint(&self, lint: Lint, span: Span, msg: &str) {
495 let (level, src) = match self.cur.find(&(lint as uint)) {
496 None => { return }
497 Some(&(warn, src)) => (self.get_level(Warnings), src),
498 Some(&pair) => pair,
499 };
500 if level == allow { return }
501
502 let mut note = None;
503 let msg = match src {
504 Default => {
505 format!("{}, \\#[{}({})] on by default", msg,
506 level_to_str(level), self.lint_to_str(lint))
507 },
508 CommandLine => {
509 format!("{} [-{} {}]", msg,
510 match level {
511 warn => 'W', deny => 'D', forbid => 'F',
512 allow => fail!()
513 }, self.lint_to_str(lint).replace("_", "-"))
514 },
515 Node(src) => {
516 note = Some(src);
517 msg.to_str()
518 }
519 };
520 match level {
521 warn => { self.tcx.sess.span_warn(span, msg); }
522 deny | forbid => { self.tcx.sess.span_err(span, msg); }
523 allow => fail!(),
524 }
525
526 for &span in note.iter() {
527 self.tcx.sess.span_note(span, "lint level defined here");
528 }
529 }
530
531 /**
532 * Merge the lints specified by any lint attributes into the
533 * current lint context, call the provided function, then reset the
534 * lints in effect to their previous state.
535 */
536 fn with_lint_attrs(&mut self,
537 attrs: &[ast::Attribute],
538 f: |&mut Context|) {
539 // Parse all of the lint attributes, and then add them all to the
540 // current dictionary of lint information. Along the way, keep a history
541 // of what we changed so we can roll everything back after invoking the
542 // specified closure
543 let mut pushed = 0u;
544 each_lint(&self.tcx.sess, attrs, |meta, level, lintname| {
545 match self.dict.find_equiv(&lintname) {
546 None => {
547 self.span_lint(
548 UnrecognizedLint,
549 meta.span,
550 format!("unknown `{}` attribute: `{}`",
551 level_to_str(level), lintname));
552 }
553 Some(lint) => {
554 let lint = lint.lint;
555 let now = self.get_level(lint);
556 if now == forbid && level != forbid {
557 self.tcx.sess.span_err(meta.span,
558 format!("{}({}) overruled by outer forbid({})",
559 level_to_str(level),
560 lintname, lintname));
561 } else if now != level {
562 let src = self.get_source(lint);
563 self.lint_stack.push((lint, now, src));
564 pushed += 1;
565 self.set_level(lint, level, Node(meta.span));
566 }
567 }
568 }
569 true
570 });
571
572 let old_is_doc_hidden = self.is_doc_hidden;
573 self.is_doc_hidden =
574 self.is_doc_hidden ||
575 attrs.iter()
576 .any(|attr| {
577 attr.name().equiv(&("doc")) &&
578 match attr.meta_item_list() {
579 None => false,
580 Some(l) => {
581 attr::contains_name(l.as_slice(), "hidden")
582 }
583 }
584 });
585
586 f(self);
587
588 // rollback
589 self.is_doc_hidden = old_is_doc_hidden;
590 for _ in range(0, pushed) {
591 let (lint, lvl, src) = self.lint_stack.pop().unwrap();
592 self.set_level(lint, lvl, src);
593 }
594 }
595
596 fn visit_ids(&self, f: |&mut ast_util::IdVisitor<Context>|) {
597 let mut v = ast_util::IdVisitor {
598 operation: self,
599 pass_through_items: false,
600 visited_outermost: false,
601 };
602 f(&mut v);
603 }
604 }
605
606 // Check that every lint from the list of attributes satisfies `f`.
607 // Return true if that's the case. Otherwise return false.
608 pub fn each_lint(sess: &session::Session,
609 attrs: &[ast::Attribute],
610 f: |@ast::MetaItem, level, InternedString| -> bool)
611 -> bool {
612 let xs = [allow, warn, deny, forbid];
613 for &level in xs.iter() {
614 let level_name = level_to_str(level);
615 for attr in attrs.iter().filter(|m| m.name().equiv(&level_name)) {
616 let meta = attr.node.value;
617 let metas = match meta.node {
618 ast::MetaList(_, ref metas) => metas,
619 _ => {
620 sess.span_err(meta.span, "malformed lint attribute");
621 continue;
622 }
623 };
624 for meta in metas.iter() {
625 match meta.node {
626 ast::MetaWord(ref lintname) => {
627 if !f(*meta, level, (*lintname).clone()) {
628 return false;
629 }
630 }
631 _ => {
632 sess.span_err(meta.span, "malformed lint attribute");
633 }
634 }
635 }
636 }
637 }
638 true
639 }
640
641 // Check from a list of attributes if it contains the appropriate
642 // `#[level(lintname)]` attribute (e.g. `#[allow(dead_code)]).
643 pub fn contains_lint(attrs: &[ast::Attribute],
644 level: level,
645 lintname: &'static str)
646 -> bool {
647 let level_name = level_to_str(level);
648 for attr in attrs.iter().filter(|m| m.name().equiv(&level_name)) {
649 if attr.meta_item_list().is_none() {
650 continue
651 }
652 let list = attr.meta_item_list().unwrap();
653 for meta_item in list.iter() {
654 if meta_item.name().equiv(&lintname) {
655 return true;
656 }
657 }
658 }
659 false
660 }
661
662 fn check_while_true_expr(cx: &Context, e: &ast::Expr) {
663 match e.node {
664 ast::ExprWhile(cond, _) => {
665 match cond.node {
666 ast::ExprLit(lit) => {
667 match lit.node {
668 ast::LitBool(true) => {
669 cx.span_lint(WhileTrue,
670 e.span,
671 "denote infinite loops with loop \
672 { ... }");
673 }
674 _ => {}
675 }
676 }
677 _ => ()
678 }
679 }
680 _ => ()
681 }
682 }
683 impl<'a> AstConv for Context<'a>{
684 fn tcx<'a>(&'a self) -> &'a ty::ctxt { self.tcx }
685
686 fn get_item_ty(&self, id: ast::DefId) -> ty::ty_param_bounds_and_ty {
687 ty::lookup_item_type(self.tcx, id)
688 }
689
690 fn get_trait_def(&self, id: ast::DefId) -> Rc<ty::TraitDef> {
691 ty::lookup_trait_def(self.tcx, id)
692 }
693
694 fn ty_infer(&self, _span: Span) -> ty::t {
695 infer::new_infer_ctxt(self.tcx).next_ty_var()
696 }
697 }
698
699
700 fn check_unused_casts(cx: &Context, e: &ast::Expr) {
701 return match e.node {
702 ast::ExprCast(expr, ty) => {
703 let t_t = ast_ty_to_ty(cx, &infer::new_infer_ctxt(cx.tcx), ty);
704 if ty::get(ty::expr_ty(cx.tcx, expr)).sty == ty::get(t_t).sty {
705 cx.span_lint(UnnecessaryTypecast, ty.span,
706 "unnecessary type cast");
707 }
708 }
709 _ => ()
710 };
711 }
712
713 fn check_type_limits(cx: &Context, e: &ast::Expr) {
714 return match e.node {
715 ast::ExprUnary(ast::UnNeg, ex) => {
716 match ex.node {
717 ast::ExprLit(lit) => {
718 match lit.node {
719 ast::LitUint(..) => {
720 cx.span_lint(UnsignedNegate, e.span,
721 "negation of unsigned int literal may be unintentional");
722 },
723 _ => ()
724 }
725 },
726 _ => {
727 let t = ty::expr_ty(cx.tcx, ex);
728 match ty::get(t).sty {
729 ty::ty_uint(_) => {
730 cx.span_lint(UnsignedNegate, e.span,
731 "negation of unsigned int variable may be unintentional");
732 },
733 _ => ()
734 }
735 }
736 }
737 },
738 ast::ExprBinary(binop, l, r) => {
739 if is_comparison(binop) && !check_limits(cx.tcx, binop, l, r) {
740 cx.span_lint(TypeLimits, e.span,
741 "comparison is useless due to type limits");
742 }
743 },
744 ast::ExprLit(lit) => {
745 match ty::get(ty::expr_ty(cx.tcx, e)).sty {
746 ty::ty_int(t) => {
747 let int_type = if t == ast::TyI {
748 cx.tcx.sess.targ_cfg.int_type
749 } else { t };
750 let (min, max) = int_ty_range(int_type);
751 let mut lit_val: i64 = match lit.node {
752 ast::LitInt(v, _) => v,
753 ast::LitUint(v, _) => v as i64,
754 ast::LitIntUnsuffixed(v) => v,
755 _ => fail!()
756 };
757 if cx.negated_expr_id == e.id {
758 lit_val *= -1;
759 }
760 if lit_val < min || lit_val > max {
761 cx.span_lint(TypeOverflow, e.span,
762 "literal out of range for its type");
763 }
764 },
765 ty::ty_uint(t) => {
766 let uint_type = if t == ast::TyU {
767 cx.tcx.sess.targ_cfg.uint_type
768 } else { t };
769 let (min, max) = uint_ty_range(uint_type);
770 let lit_val: u64 = match lit.node {
771 ast::LitInt(v, _) => v as u64,
772 ast::LitUint(v, _) => v,
773 ast::LitIntUnsuffixed(v) => v as u64,
774 _ => fail!()
775 };
776 if lit_val < min || lit_val > max {
777 cx.span_lint(TypeOverflow, e.span,
778 "literal out of range for its type");
779 }
780 },
781
782 _ => ()
783 };
784 },
785 _ => ()
786 };
787
788 fn is_valid<T:cmp::Ord>(binop: ast::BinOp, v: T,
789 min: T, max: T) -> bool {
790 match binop {
791 ast::BiLt => v > min && v <= max,
792 ast::BiLe => v >= min && v < max,
793 ast::BiGt => v >= min && v < max,
794 ast::BiGe => v > min && v <= max,
795 ast::BiEq | ast::BiNe => v >= min && v <= max,
796 _ => fail!()
797 }
798 }
799
800 fn rev_binop(binop: ast::BinOp) -> ast::BinOp {
801 match binop {
802 ast::BiLt => ast::BiGt,
803 ast::BiLe => ast::BiGe,
804 ast::BiGt => ast::BiLt,
805 ast::BiGe => ast::BiLe,
806 _ => binop
807 }
808 }
809
810 // for int & uint, be conservative with the warnings, so that the
811 // warnings are consistent between 32- and 64-bit platforms
812 fn int_ty_range(int_ty: ast::IntTy) -> (i64, i64) {
813 match int_ty {
814 ast::TyI => (i64::MIN, i64::MAX),
815 ast::TyI8 => (i8::MIN as i64, i8::MAX as i64),
816 ast::TyI16 => (i16::MIN as i64, i16::MAX as i64),
817 ast::TyI32 => (i32::MIN as i64, i32::MAX as i64),
818 ast::TyI64 => (i64::MIN, i64::MAX)
819 }
820 }
821
822 fn uint_ty_range(uint_ty: ast::UintTy) -> (u64, u64) {
823 match uint_ty {
824 ast::TyU => (u64::MIN, u64::MAX),
825 ast::TyU8 => (u8::MIN as u64, u8::MAX as u64),
826 ast::TyU16 => (u16::MIN as u64, u16::MAX as u64),
827 ast::TyU32 => (u32::MIN as u64, u32::MAX as u64),
828 ast::TyU64 => (u64::MIN, u64::MAX)
829 }
830 }
831
832 fn check_limits(tcx: &ty::ctxt, binop: ast::BinOp,
833 l: &ast::Expr, r: &ast::Expr) -> bool {
834 let (lit, expr, swap) = match (&l.node, &r.node) {
835 (&ast::ExprLit(_), _) => (l, r, true),
836 (_, &ast::ExprLit(_)) => (r, l, false),
837 _ => return true
838 };
839 // Normalize the binop so that the literal is always on the RHS in
840 // the comparison
841 let norm_binop = if swap { rev_binop(binop) } else { binop };
842 match ty::get(ty::expr_ty(tcx, expr)).sty {
843 ty::ty_int(int_ty) => {
844 let (min, max) = int_ty_range(int_ty);
845 let lit_val: i64 = match lit.node {
846 ast::ExprLit(li) => match li.node {
847 ast::LitInt(v, _) => v,
848 ast::LitUint(v, _) => v as i64,
849 ast::LitIntUnsuffixed(v) => v,
850 _ => return true
851 },
852 _ => fail!()
853 };
854 is_valid(norm_binop, lit_val, min, max)
855 }
856 ty::ty_uint(uint_ty) => {
857 let (min, max): (u64, u64) = uint_ty_range(uint_ty);
858 let lit_val: u64 = match lit.node {
859 ast::ExprLit(li) => match li.node {
860 ast::LitInt(v, _) => v as u64,
861 ast::LitUint(v, _) => v,
862 ast::LitIntUnsuffixed(v) => v as u64,
863 _ => return true
864 },
865 _ => fail!()
866 };
867 is_valid(norm_binop, lit_val, min, max)
868 }
869 _ => true
870 }
871 }
872
873 fn is_comparison(binop: ast::BinOp) -> bool {
874 match binop {
875 ast::BiEq | ast::BiLt | ast::BiLe |
876 ast::BiNe | ast::BiGe | ast::BiGt => true,
877 _ => false
878 }
879 }
880 }
881
882 fn check_item_ctypes(cx: &Context, it: &ast::Item) {
883 fn check_ty(cx: &Context, ty: &ast::Ty) {
884 match ty.node {
885 ast::TyPath(_, _, id) => {
886 match cx.tcx.def_map.borrow().get_copy(&id) {
887 ast::DefPrimTy(ast::TyInt(ast::TyI)) => {
888 cx.span_lint(CTypes, ty.span,
889 "found rust type `int` in foreign module, while \
890 libc::c_int or libc::c_long should be used");
891 }
892 ast::DefPrimTy(ast::TyUint(ast::TyU)) => {
893 cx.span_lint(CTypes, ty.span,
894 "found rust type `uint` in foreign module, while \
895 libc::c_uint or libc::c_ulong should be used");
896 }
897 ast::DefTy(def_id) => {
898 if !adt::is_ffi_safe(cx.tcx, def_id) {
899 cx.span_lint(CTypes, ty.span,
900 "found enum type without foreign-function-safe \
901 representation annotation in foreign module");
902 // hmm... this message could be more helpful
903 }
904 }
905 _ => ()
906 }
907 }
908 ast::TyPtr(ref mt) => { check_ty(cx, mt.ty) }
909 _ => {}
910 }
911 }
912
913 fn check_foreign_fn(cx: &Context, decl: &ast::FnDecl) {
914 for input in decl.inputs.iter() {
915 check_ty(cx, input.ty);
916 }
917 check_ty(cx, decl.output)
918 }
919
920 match it.node {
921 ast::ItemForeignMod(ref nmod) if nmod.abi != abi::RustIntrinsic => {
922 for ni in nmod.items.iter() {
923 match ni.node {
924 ast::ForeignItemFn(decl, _) => check_foreign_fn(cx, decl),
925 ast::ForeignItemStatic(t, _) => check_ty(cx, t)
926 }
927 }
928 }
929 _ => {/* nothing to do */ }
930 }
931 }
932
933 fn check_heap_type(cx: &Context, span: Span, ty: ty::t) {
934 let xs = [ManagedHeapMemory, OwnedHeapMemory, HeapMemory];
935 for &lint in xs.iter() {
936 if cx.get_level(lint) == allow { continue }
937
938 let mut n_box = 0;
939 let mut n_uniq = 0;
940 ty::fold_ty(cx.tcx, ty, |t| {
941 match ty::get(t).sty {
942 ty::ty_box(_) => {
943 n_box += 1;
944 }
945 ty::ty_uniq(_) |
946 ty::ty_trait(box ty::TyTrait {
947 store: ty::UniqTraitStore, ..
948 }) |
949 ty::ty_closure(box ty::ClosureTy {
950 store: ty::UniqTraitStore,
951 ..
952 }) => {
953 n_uniq += 1;
954 }
955
956 _ => ()
957 };
958 t
959 });
960
961 if n_uniq > 0 && lint != ManagedHeapMemory {
962 let s = ty_to_str(cx.tcx, ty);
963 let m = format!("type uses owned (Box type) pointers: {}", s);
964 cx.span_lint(lint, span, m);
965 }
966
967 if n_box > 0 && lint != OwnedHeapMemory {
968 let s = ty_to_str(cx.tcx, ty);
969 let m = format!("type uses managed (@ type) pointers: {}", s);
970 cx.span_lint(lint, span, m);
971 }
972 }
973 }
974
975 fn check_heap_item(cx: &Context, it: &ast::Item) {
976 match it.node {
977 ast::ItemFn(..) |
978 ast::ItemTy(..) |
979 ast::ItemEnum(..) |
980 ast::ItemStruct(..) => check_heap_type(cx, it.span,
981 ty::node_id_to_type(cx.tcx,
982 it.id)),
983 _ => ()
984 }
985
986 // If it's a struct, we also have to check the fields' types
987 match it.node {
988 ast::ItemStruct(struct_def, _) => {
989 for struct_field in struct_def.fields.iter() {
990 check_heap_type(cx, struct_field.span,
991 ty::node_id_to_type(cx.tcx,
992 struct_field.node.id));
993 }
994 }
995 _ => ()
996 }
997 }
998
999 struct RawPtrDerivingVisitor<'a> {
1000 cx: &'a Context<'a>
1001 }
1002
1003 impl<'a> Visitor<()> for RawPtrDerivingVisitor<'a> {
1004 fn visit_ty(&mut self, ty: &ast::Ty, _: ()) {
1005 static MSG: &'static str = "use of `#[deriving]` with a raw pointer";
1006 match ty.node {
1007 ast::TyPtr(..) => self.cx.span_lint(RawPointerDeriving, ty.span, MSG),
1008 _ => {}
1009 }
1010 visit::walk_ty(self, ty, ());
1011 }
1012 // explicit override to a no-op to reduce code bloat
1013 fn visit_expr(&mut self, _: &ast::Expr, _: ()) {}
1014 fn visit_block(&mut self, _: &ast::Block, _: ()) {}
1015 }
1016
1017 fn check_raw_ptr_deriving(cx: &Context, item: &ast::Item) {
1018 if !attr::contains_name(item.attrs.as_slice(), "deriving") {
1019 return
1020 }
1021 match item.node {
1022 ast::ItemStruct(..) | ast::ItemEnum(..) => {
1023 let mut visitor = RawPtrDerivingVisitor { cx: cx };
1024 visit::walk_item(&mut visitor, item, ());
1025 }
1026 _ => {}
1027 }
1028 }
1029
1030 static crate_attrs: &'static [&'static str] = &[
1031 "crate_type", "feature", "no_start", "no_main", "no_std", "crate_id",
1032 "desc", "comment", "license", "copyright", // not used in rustc now
1033 ];
1034
1035
1036 static obsolete_attrs: &'static [(&'static str, &'static str)] = &[
1037 ("abi", "Use `extern \"abi\" fn` instead"),
1038 ("auto_encode", "Use `#[deriving(Encodable)]` instead"),
1039 ("auto_decode", "Use `#[deriving(Decodable)]` instead"),
1040 ("fast_ffi", "Remove it"),
1041 ("fixed_stack_segment", "Remove it"),
1042 ("rust_stack", "Remove it"),
1043 ];
1044
1045 static other_attrs: &'static [&'static str] = &[
1046 // item-level
1047 "address_insignificant", // can be crate-level too
1048 "thread_local", // for statics
1049 "allow", "deny", "forbid", "warn", // lint options
1050 "deprecated", "experimental", "unstable", "stable", "locked", "frozen", //item stability
1051 "cfg", "doc", "export_name", "link_section",
1052 "no_mangle", "static_assert", "unsafe_no_drop_flag", "packed",
1053 "simd", "repr", "deriving", "unsafe_destructor", "link", "phase",
1054 "macro_export", "must_use", "automatically_derived",
1055
1056 //mod-level
1057 "path", "link_name", "link_args", "macro_escape", "no_implicit_prelude",
1058
1059 // fn-level
1060 "test", "bench", "should_fail", "ignore", "inline", "lang", "main", "start",
1061 "no_split_stack", "cold", "macro_registrar", "linkage",
1062
1063 // internal attribute: bypass privacy inside items
1064 "!resolve_unexported",
1065 ];
1066
1067 fn check_crate_attrs_usage(cx: &Context, attrs: &[ast::Attribute]) {
1068
1069 for attr in attrs.iter() {
1070 let name = attr.node.value.name();
1071 let mut iter = crate_attrs.iter().chain(other_attrs.iter());
1072 if !iter.any(|other_attr| { name.equiv(other_attr) }) {
1073 cx.span_lint(AttributeUsage, attr.span, "unknown crate attribute");
1074 }
1075 if name.equiv(&("link")) {
1076 cx.tcx.sess.span_err(attr.span,
1077 "obsolete crate `link` attribute");
1078 cx.tcx.sess.note("the link attribute has been superceded by the crate_id \
1079 attribute, which has the format `#[crate_id = \"name#version\"]`");
1080 }
1081 }
1082 }
1083
1084 fn check_attrs_usage(cx: &Context, attrs: &[ast::Attribute]) {
1085 // check if element has crate-level, obsolete, or any unknown attributes.
1086
1087 for attr in attrs.iter() {
1088 let name = attr.node.value.name();
1089 for crate_attr in crate_attrs.iter() {
1090 if name.equiv(crate_attr) {
1091 let msg = match attr.node.style {
1092 ast::AttrOuter => "crate-level attribute should be an inner attribute: \
1093 add an exclamation mark: #![foo]",
1094 ast::AttrInner => "crate-level attribute should be in the root module",
1095 };
1096 cx.span_lint(AttributeUsage, attr.span, msg);
1097 return;
1098 }
1099 }
1100
1101 for &(obs_attr, obs_alter) in obsolete_attrs.iter() {
1102 if name.equiv(&obs_attr) {
1103 cx.span_lint(AttributeUsage, attr.span,
1104 format!("obsolete attribute: {:s}", obs_alter));
1105 return;
1106 }
1107 }
1108
1109 if !other_attrs.iter().any(|other_attr| { name.equiv(other_attr) }) {
1110 cx.span_lint(AttributeUsage, attr.span, "unknown attribute");
1111 }
1112 }
1113 }
1114
1115 fn check_heap_expr(cx: &Context, e: &ast::Expr) {
1116 let ty = ty::expr_ty(cx.tcx, e);
1117 check_heap_type(cx, e.span, ty);
1118 }
1119
1120 fn check_path_statement(cx: &Context, s: &ast::Stmt) {
1121 match s.node {
1122 ast::StmtSemi(expr, _) => {
1123 match expr.node {
1124 ast::ExprPath(_) => {
1125 cx.span_lint(PathStatement,
1126 s.span,
1127 "path statement with no effect");
1128 }
1129 _ => {}
1130 }
1131 }
1132 _ => ()
1133 }
1134 }
1135
1136 fn check_unused_result(cx: &Context, s: &ast::Stmt) {
1137 let expr = match s.node {
1138 ast::StmtSemi(expr, _) => expr,
1139 _ => return
1140 };
1141 let t = ty::expr_ty(cx.tcx, expr);
1142 match ty::get(t).sty {
1143 ty::ty_nil | ty::ty_bot | ty::ty_bool => return,
1144 _ => {}
1145 }
1146 match expr.node {
1147 ast::ExprRet(..) => return,
1148 _ => {}
1149 }
1150
1151 let t = ty::expr_ty(cx.tcx, expr);
1152 let mut warned = false;
1153 match ty::get(t).sty {
1154 ty::ty_struct(did, _) |
1155 ty::ty_enum(did, _) => {
1156 if ast_util::is_local(did) {
1157 match cx.tcx.map.get(did.node) {
1158 ast_map::NodeItem(it) => {
1159 if attr::contains_name(it.attrs.as_slice(),
1160 "must_use") {
1161 cx.span_lint(UnusedMustUse, s.span,
1162 "unused result which must be used");
1163 warned = true;
1164 }
1165 }
1166 _ => {}
1167 }
1168 } else {
1169 csearch::get_item_attrs(&cx.tcx.sess.cstore, did, |attrs| {
1170 if attr::contains_name(attrs.as_slice(), "must_use") {
1171 cx.span_lint(UnusedMustUse, s.span,
1172 "unused result which must be used");
1173 warned = true;
1174 }
1175 });
1176 }
1177 }
1178 _ => {}
1179 }
1180 if !warned {
1181 cx.span_lint(UnusedResult, s.span, "unused result");
1182 }
1183 }
1184
1185 fn check_deprecated_owned_vector(cx: &Context, e: &ast::Expr) {
1186 let t = ty::expr_ty(cx.tcx, e);
1187 match ty::get(t).sty {
1188 ty::ty_uniq(t) => match ty::get(t).sty {
1189 ty::ty_vec(_, None) => {
1190 cx.span_lint(DeprecatedOwnedVector, e.span,
1191 "use of deprecated `~[]` vector; replaced by `std::vec::Vec`")
1192 }
1193 _ => {}
1194 },
1195 _ => {}
1196 }
1197 }
1198
1199 fn check_item_non_camel_case_types(cx: &Context, it: &ast::Item) {
1200 fn is_camel_case(ident: ast::Ident) -> bool {
1201 let ident = token::get_ident(ident);
1202 assert!(!ident.get().is_empty());
1203 let ident = ident.get().trim_chars('_');
1204
1205 // start with a non-lowercase letter rather than non-uppercase
1206 // ones (some scripts don't have a concept of upper/lowercase)
1207 !ident.char_at(0).is_lowercase() && !ident.contains_char('_')
1208 }
1209
1210 fn check_case(cx: &Context, sort: &str, ident: ast::Ident, span: Span) {
1211 if !is_camel_case(ident) {
1212 cx.span_lint(
1213 NonCamelCaseTypes, span,
1214 format!("{} `{}` should have a camel case identifier",
1215 sort, token::get_ident(ident)));
1216 }
1217 }
1218
1219 match it.node {
1220 ast::ItemTy(..) | ast::ItemStruct(..) => {
1221 check_case(cx, "type", it.ident, it.span)
1222 }
1223 ast::ItemTrait(..) => {
1224 check_case(cx, "trait", it.ident, it.span)
1225 }
1226 ast::ItemEnum(ref enum_definition, _) => {
1227 check_case(cx, "type", it.ident, it.span);
1228 for variant in enum_definition.variants.iter() {
1229 check_case(cx, "variant", variant.node.name, variant.span);
1230 }
1231 }
1232 _ => ()
1233 }
1234 }
1235
1236 fn check_item_non_uppercase_statics(cx: &Context, it: &ast::Item) {
1237 match it.node {
1238 // only check static constants
1239 ast::ItemStatic(_, ast::MutImmutable, _) => {
1240 let s = token::get_ident(it.ident);
1241 // check for lowercase letters rather than non-uppercase
1242 // ones (some scripts don't have a concept of
1243 // upper/lowercase)
1244 if s.get().chars().any(|c| c.is_lowercase()) {
1245 cx.span_lint(NonUppercaseStatics, it.span,
1246 "static constant should have an uppercase identifier");
1247 }
1248 }
1249 _ => {}
1250 }
1251 }
1252
1253 fn check_pat_non_uppercase_statics(cx: &Context, p: &ast::Pat) {
1254 // Lint for constants that look like binding identifiers (#7526)
1255 match (&p.node, cx.tcx.def_map.borrow().find(&p.id)) {
1256 (&ast::PatIdent(_, ref path, _), Some(&ast::DefStatic(_, false))) => {
1257 // last identifier alone is right choice for this lint.
1258 let ident = path.segments.last().unwrap().identifier;
1259 let s = token::get_ident(ident);
1260 if s.get().chars().any(|c| c.is_lowercase()) {
1261 cx.span_lint(NonUppercasePatternStatics, path.span,
1262 "static constant in pattern should be all caps");
1263 }
1264 }
1265 _ => {}
1266 }
1267 }
1268
1269 fn check_pat_uppercase_variable(cx: &Context, p: &ast::Pat) {
1270 match &p.node {
1271 &ast::PatIdent(_, ref path, _) => {
1272 match cx.tcx.def_map.borrow().find(&p.id) {
1273 Some(&ast::DefLocal(_, _)) | Some(&ast::DefBinding(_, _)) |
1274 Some(&ast::DefArg(_, _)) => {
1275 // last identifier alone is right choice for this lint.
1276 let ident = path.segments.last().unwrap().identifier;
1277 let s = token::get_ident(ident);
1278 if s.get().len() > 0 && s.get().char_at(0).is_uppercase() {
1279 cx.span_lint(
1280 UppercaseVariables,
1281 path.span,
1282 "variable names should start with a lowercase character");
1283 }
1284 }
1285 _ => {}
1286 }
1287 }
1288 _ => {}
1289 }
1290 }
1291
1292 fn check_struct_uppercase_variable(cx: &Context, s: &ast::StructDef) {
1293 for sf in s.fields.iter() {
1294 match sf.node {
1295 ast::StructField_ { kind: ast::NamedField(ident, _), .. } => {
1296 let s = token::get_ident(ident);
1297 if s.get().char_at(0).is_uppercase() {
1298 cx.span_lint(
1299 UppercaseVariables,
1300 sf.span,
1301 "structure field names should start with a lowercase character");
1302 }
1303 }
1304 _ => {}
1305 }
1306 }
1307 }
1308
1309 fn check_unnecessary_parens_core(cx: &Context, value: &ast::Expr, msg: &str) {
1310 match value.node {
1311 ast::ExprParen(_) => {
1312 cx.span_lint(UnnecessaryParens, value.span,
1313 format!("unnecessary parentheses around {}", msg))
1314 }
1315 _ => {}
1316 }
1317 }
1318
1319 fn check_unnecessary_parens_expr(cx: &Context, e: &ast::Expr) {
1320 let (value, msg) = match e.node {
1321 ast::ExprIf(cond, _, _) => (cond, "`if` condition"),
1322 ast::ExprWhile(cond, _) => (cond, "`while` condition"),
1323 ast::ExprMatch(head, _) => (head, "`match` head expression"),
1324 ast::ExprRet(Some(value)) => (value, "`return` value"),
1325 ast::ExprAssign(_, value) => (value, "assigned value"),
1326 ast::ExprAssignOp(_, _, value) => (value, "assigned value"),
1327 _ => return
1328 };
1329 check_unnecessary_parens_core(cx, value, msg);
1330 }
1331
1332 fn check_unnecessary_parens_stmt(cx: &Context, s: &ast::Stmt) {
1333 let (value, msg) = match s.node {
1334 ast::StmtDecl(decl, _) => match decl.node {
1335 ast::DeclLocal(local) => match local.init {
1336 Some(value) => (value, "assigned value"),
1337 None => return
1338 },
1339 _ => return
1340 },
1341 _ => return
1342 };
1343 check_unnecessary_parens_core(cx, value, msg);
1344 }
1345
1346 fn check_unused_unsafe(cx: &Context, e: &ast::Expr) {
1347 match e.node {
1348 // Don't warn about generated blocks, that'll just pollute the output.
1349 ast::ExprBlock(ref blk) => {
1350 if blk.rules == ast::UnsafeBlock(ast::UserProvided) &&
1351 !cx.tcx.used_unsafe.borrow().contains(&blk.id) {
1352 cx.span_lint(UnusedUnsafe, blk.span,
1353 "unnecessary `unsafe` block");
1354 }
1355 }
1356 _ => ()
1357 }
1358 }
1359
1360 fn check_unsafe_block(cx: &Context, e: &ast::Expr) {
1361 match e.node {
1362 // Don't warn about generated blocks, that'll just pollute the output.
1363 ast::ExprBlock(ref blk) if blk.rules == ast::UnsafeBlock(ast::UserProvided) => {
1364 cx.span_lint(UnsafeBlock, blk.span, "usage of an `unsafe` block");
1365 }
1366 _ => ()
1367 }
1368 }
1369
1370 fn check_unused_mut_pat(cx: &Context, p: &ast::Pat) {
1371 match p.node {
1372 ast::PatIdent(ast::BindByValue(ast::MutMutable),
1373 ref path, _) if pat_util::pat_is_binding(&cx.tcx.def_map, p) => {
1374 // `let mut _a = 1;` doesn't need a warning.
1375 let initial_underscore = if path.segments.len() == 1 {
1376 token::get_ident(path.segments
1377 .get(0)
1378 .identifier).get().starts_with("_")
1379 } else {
1380 cx.tcx.sess.span_bug(p.span,
1381 "mutable binding that doesn't consist \
1382 of exactly one segment")
1383 };
1384
1385 if !initial_underscore &&
1386 !cx.tcx.used_mut_nodes.borrow().contains(&p.id) {
1387 cx.span_lint(UnusedMut, p.span,
1388 "variable does not need to be mutable");
1389 }
1390 }
1391 _ => ()
1392 }
1393 }
1394
1395 enum Allocation {
1396 VectorAllocation,
1397 BoxAllocation
1398 }
1399
1400 fn check_unnecessary_allocation(cx: &Context, e: &ast::Expr) {
1401 // Warn if string and vector literals with sigils, or boxing expressions,
1402 // are immediately borrowed.
1403 let allocation = match e.node {
1404 ast::ExprVstore(e2, ast::ExprVstoreUniq) => {
1405 match e2.node {
1406 ast::ExprLit(lit) if ast_util::lit_is_str(lit) => {
1407 VectorAllocation
1408 }
1409 ast::ExprVec(..) => VectorAllocation,
1410 _ => return
1411 }
1412 }
1413 ast::ExprUnary(ast::UnUniq, _) |
1414 ast::ExprUnary(ast::UnBox, _) => BoxAllocation,
1415
1416 _ => return
1417 };
1418
1419 let report = |msg| {
1420 cx.span_lint(UnnecessaryAllocation, e.span, msg);
1421 };
1422
1423 match cx.tcx.adjustments.borrow().find(&e.id) {
1424 Some(adjustment) => {
1425 match *adjustment {
1426 ty::AutoDerefRef(ty::AutoDerefRef { autoref, .. }) => {
1427 match (allocation, autoref) {
1428 (VectorAllocation, Some(ty::AutoBorrowVec(..))) => {
1429 report("unnecessary allocation, the sigil can be \
1430 removed");
1431 }
1432 (BoxAllocation,
1433 Some(ty::AutoPtr(_, ast::MutImmutable))) => {
1434 report("unnecessary allocation, use & instead");
1435 }
1436 (BoxAllocation,
1437 Some(ty::AutoPtr(_, ast::MutMutable))) => {
1438 report("unnecessary allocation, use &mut \
1439 instead");
1440 }
1441 _ => ()
1442 }
1443 }
1444 _ => {}
1445 }
1446 }
1447
1448 _ => ()
1449 }
1450 }
1451
1452 fn check_missing_doc_attrs(cx: &Context,
1453 id: Option<ast::NodeId>,
1454 attrs: &[ast::Attribute],
1455 sp: Span,
1456 desc: &'static str) {
1457 // If we're building a test harness, then warning about
1458 // documentation is probably not really relevant right now.
1459 if cx.tcx.sess.opts.test { return }
1460
1461 // `#[doc(hidden)]` disables missing_doc check.
1462 if cx.is_doc_hidden { return }
1463
1464 // Only check publicly-visible items, using the result from the privacy pass. It's an option so
1465 // the crate root can also use this function (it doesn't have a NodeId).
1466 match id {
1467 Some(ref id) if !cx.exported_items.contains(id) => return,
1468 _ => ()
1469 }
1470
1471 let has_doc = attrs.iter().any(|a| {
1472 match a.node.value.node {
1473 ast::MetaNameValue(ref name, _) if name.equiv(&("doc")) => true,
1474 _ => false
1475 }
1476 });
1477 if !has_doc {
1478 cx.span_lint(MissingDoc, sp,
1479 format!("missing documentation for {}", desc));
1480 }
1481 }
1482
1483 fn check_missing_doc_item(cx: &Context, it: &ast::Item) {
1484 let desc = match it.node {
1485 ast::ItemFn(..) => "a function",
1486 ast::ItemMod(..) => "a module",
1487 ast::ItemEnum(..) => "an enum",
1488 ast::ItemStruct(..) => "a struct",
1489 ast::ItemTrait(..) => "a trait",
1490 _ => return
1491 };
1492 check_missing_doc_attrs(cx,
1493 Some(it.id),
1494 it.attrs.as_slice(),
1495 it.span,
1496 desc);
1497 }
1498
1499 fn check_missing_doc_method(cx: &Context, m: &ast::Method) {
1500 let did = ast::DefId {
1501 krate: ast::LOCAL_CRATE,
1502 node: m.id
1503 };
1504
1505 match cx.tcx.methods.borrow().find_copy(&did) {
1506 None => cx.tcx.sess.span_bug(m.span, "missing method descriptor?!"),
1507 Some(md) => {
1508 match md.container {
1509 // Always check default methods defined on traits.
1510 ty::TraitContainer(..) => {}
1511 // For methods defined on impls, it depends on whether
1512 // it is an implementation for a trait or is a plain
1513 // impl.
1514 ty::ImplContainer(cid) => {
1515 match ty::impl_trait_ref(cx.tcx, cid) {
1516 Some(..) => return, // impl for trait: don't doc
1517 None => {} // plain impl: doc according to privacy
1518 }
1519 }
1520 }
1521 }
1522 }
1523 check_missing_doc_attrs(cx,
1524 Some(m.id),
1525 m.attrs.as_slice(),
1526 m.span,
1527 "a method");
1528 }
1529
1530 fn check_missing_doc_ty_method(cx: &Context, tm: &ast::TypeMethod) {
1531 check_missing_doc_attrs(cx,
1532 Some(tm.id),
1533 tm.attrs.as_slice(),
1534 tm.span,
1535 "a type method");
1536 }
1537
1538 fn check_missing_doc_struct_field(cx: &Context, sf: &ast::StructField) {
1539 match sf.node.kind {
1540 ast::NamedField(_, vis) if vis == ast::Public =>
1541 check_missing_doc_attrs(cx,
1542 Some(cx.cur_struct_def_id),
1543 sf.node.attrs.as_slice(),
1544 sf.span,
1545 "a struct field"),
1546 _ => {}
1547 }
1548 }
1549
1550 fn check_missing_doc_variant(cx: &Context, v: &ast::Variant) {
1551 check_missing_doc_attrs(cx,
1552 Some(v.node.id),
1553 v.node.attrs.as_slice(),
1554 v.span,
1555 "a variant");
1556 }
1557
1558 /// Checks for use of items with #[deprecated], #[experimental] and
1559 /// #[unstable] (or none of them) attributes.
1560 fn check_stability(cx: &Context, e: &ast::Expr) {
1561 let id = match e.node {
1562 ast::ExprPath(..) | ast::ExprStruct(..) => {
1563 match cx.tcx.def_map.borrow().find(&e.id) {
1564 Some(&def) => ast_util::def_id_of_def(def),
1565 None => return
1566 }
1567 }
1568 ast::ExprMethodCall(..) => {
1569 let method_call = typeck::MethodCall::expr(e.id);
1570 match cx.tcx.method_map.borrow().find(&method_call) {
1571 Some(method) => {
1572 match method.origin {
1573 typeck::MethodStatic(def_id) => {
1574 // If this implements a trait method, get def_id
1575 // of the method inside trait definition.
1576 // Otherwise, use the current def_id (which refers
1577 // to the method inside impl).
1578 ty::trait_method_of_method(
1579 cx.tcx, def_id).unwrap_or(def_id)
1580 }
1581 typeck::MethodParam(typeck::MethodParam {
1582 trait_id: trait_id,
1583 method_num: index,
1584 ..
1585 })
1586 | typeck::MethodObject(typeck::MethodObject {
1587 trait_id: trait_id,
1588 method_num: index,
1589 ..
1590 }) => ty::trait_method(cx.tcx, trait_id, index).def_id
1591 }
1592 }
1593 None => return
1594 }
1595 }
1596 _ => return
1597 };
1598
1599 let stability = if ast_util::is_local(id) {
1600 // this crate
1601 let s = cx.tcx.map.with_attrs(id.node, |attrs| {
1602 attrs.map(|a| {
1603 attr::find_stability(a.iter().map(|a| a.meta()))
1604 })
1605 });
1606 match s {
1607 Some(s) => s,
1608
1609 // no possibility of having attributes
1610 // (e.g. it's a local variable), so just
1611 // ignore it.
1612 None => return
1613 }
1614 } else {
1615 // cross-crate
1616
1617 let mut s = None;
1618 // run through all the attributes and take the first
1619 // stability one.
1620 csearch::get_item_attrs(&cx.tcx.sess.cstore, id, |meta_items| {
1621 if s.is_none() {
1622 s = attr::find_stability(meta_items.move_iter())
1623 }
1624 });
1625 s
1626 };
1627
1628 let (lint, label) = match stability {
1629 // no stability attributes == Unstable
1630 None => (Unstable, "unmarked"),
1631 Some(attr::Stability { level: attr::Unstable, .. }) =>
1632 (Unstable, "unstable"),
1633 Some(attr::Stability { level: attr::Experimental, .. }) =>
1634 (Experimental, "experimental"),
1635 Some(attr::Stability { level: attr::Deprecated, .. }) =>
1636 (Deprecated, "deprecated"),
1637 _ => return
1638 };
1639
1640 let msg = match stability {
1641 Some(attr::Stability { text: Some(ref s), .. }) => {
1642 format!("use of {} item: {}", label, *s)
1643 }
1644 _ => format!("use of {} item", label)
1645 };
1646
1647 cx.span_lint(lint, e.span, msg);
1648 }
1649
1650 impl<'a> Visitor<()> for Context<'a> {
1651 fn visit_item(&mut self, it: &ast::Item, _: ()) {
1652 self.with_lint_attrs(it.attrs.as_slice(), |cx| {
1653 check_item_ctypes(cx, it);
1654 check_item_non_camel_case_types(cx, it);
1655 check_item_non_uppercase_statics(cx, it);
1656 check_heap_item(cx, it);
1657 check_missing_doc_item(cx, it);
1658 check_attrs_usage(cx, it.attrs.as_slice());
1659 check_raw_ptr_deriving(cx, it);
1660
1661 cx.visit_ids(|v| v.visit_item(it, ()));
1662
1663 visit::walk_item(cx, it, ());
1664 })
1665 }
1666
1667 fn visit_foreign_item(&mut self, it: &ast::ForeignItem, _: ()) {
1668 self.with_lint_attrs(it.attrs.as_slice(), |cx| {
1669 check_attrs_usage(cx, it.attrs.as_slice());
1670 visit::walk_foreign_item(cx, it, ());
1671 })
1672 }
1673
1674 fn visit_view_item(&mut self, i: &ast::ViewItem, _: ()) {
1675 self.with_lint_attrs(i.attrs.as_slice(), |cx| {
1676 check_attrs_usage(cx, i.attrs.as_slice());
1677
1678 cx.visit_ids(|v| v.visit_view_item(i, ()));
1679
1680 visit::walk_view_item(cx, i, ());
1681 })
1682 }
1683
1684 fn visit_pat(&mut self, p: &ast::Pat, _: ()) {
1685 check_pat_non_uppercase_statics(self, p);
1686 check_pat_uppercase_variable(self, p);
1687 check_unused_mut_pat(self, p);
1688
1689 visit::walk_pat(self, p, ());
1690 }
1691
1692 fn visit_expr(&mut self, e: &ast::Expr, _: ()) {
1693 match e.node {
1694 ast::ExprUnary(ast::UnNeg, expr) => {
1695 // propagate negation, if the negation itself isn't negated
1696 if self.negated_expr_id != e.id {
1697 self.negated_expr_id = expr.id;
1698 }
1699 },
1700 ast::ExprParen(expr) => if self.negated_expr_id == e.id {
1701 self.negated_expr_id = expr.id
1702 },
1703 _ => ()
1704 };
1705
1706 check_while_true_expr(self, e);
1707 check_stability(self, e);
1708 check_unnecessary_parens_expr(self, e);
1709 check_unused_unsafe(self, e);
1710 check_unsafe_block(self, e);
1711 check_unnecessary_allocation(self, e);
1712 check_heap_expr(self, e);
1713
1714 check_type_limits(self, e);
1715 check_unused_casts(self, e);
1716 check_deprecated_owned_vector(self, e);
1717
1718 visit::walk_expr(self, e, ());
1719 }
1720
1721 fn visit_stmt(&mut self, s: &ast::Stmt, _: ()) {
1722 check_path_statement(self, s);
1723 check_unused_result(self, s);
1724 check_unnecessary_parens_stmt(self, s);
1725
1726 visit::walk_stmt(self, s, ());
1727 }
1728
1729 fn visit_fn(&mut self, fk: &visit::FnKind, decl: &ast::FnDecl,
1730 body: &ast::Block, span: Span, id: ast::NodeId, _: ()) {
1731 let recurse = |this: &mut Context| {
1732 visit::walk_fn(this, fk, decl, body, span, id, ());
1733 };
1734
1735 match *fk {
1736 visit::FkMethod(_, _, m) => {
1737 self.with_lint_attrs(m.attrs.as_slice(), |cx| {
1738 check_missing_doc_method(cx, m);
1739 check_attrs_usage(cx, m.attrs.as_slice());
1740
1741 cx.visit_ids(|v| {
1742 v.visit_fn(fk, decl, body, span, id, ());
1743 });
1744 recurse(cx);
1745 })
1746 }
1747 _ => recurse(self),
1748 }
1749 }
1750
1751
1752 fn visit_ty_method(&mut self, t: &ast::TypeMethod, _: ()) {
1753 self.with_lint_attrs(t.attrs.as_slice(), |cx| {
1754 check_missing_doc_ty_method(cx, t);
1755 check_attrs_usage(cx, t.attrs.as_slice());
1756
1757 visit::walk_ty_method(cx, t, ());
1758 })
1759 }
1760
1761 fn visit_struct_def(&mut self,
1762 s: &ast::StructDef,
1763 i: ast::Ident,
1764 g: &ast::Generics,
1765 id: ast::NodeId,
1766 _: ()) {
1767 check_struct_uppercase_variable(self, s);
1768
1769 let old_id = self.cur_struct_def_id;
1770 self.cur_struct_def_id = id;
1771 visit::walk_struct_def(self, s, i, g, id, ());
1772 self.cur_struct_def_id = old_id;
1773 }
1774
1775 fn visit_struct_field(&mut self, s: &ast::StructField, _: ()) {
1776 self.with_lint_attrs(s.node.attrs.as_slice(), |cx| {
1777 check_missing_doc_struct_field(cx, s);
1778 check_attrs_usage(cx, s.node.attrs.as_slice());
1779
1780 visit::walk_struct_field(cx, s, ());
1781 })
1782 }
1783
1784 fn visit_variant(&mut self, v: &ast::Variant, g: &ast::Generics, _: ()) {
1785 self.with_lint_attrs(v.node.attrs.as_slice(), |cx| {
1786 check_missing_doc_variant(cx, v);
1787 check_attrs_usage(cx, v.node.attrs.as_slice());
1788
1789 visit::walk_variant(cx, v, g, ());
1790 })
1791 }
1792
1793 // FIXME(#10894) should continue recursing
1794 fn visit_ty(&mut self, _t: &ast::Ty, _: ()) {}
1795 }
1796
1797 impl<'a> IdVisitingOperation for Context<'a> {
1798 fn visit_id(&self, id: ast::NodeId) {
1799 match self.tcx.sess.lints.borrow_mut().pop(&id) {
1800 None => {}
1801 Some(l) => {
1802 for (lint, span, msg) in l.move_iter() {
1803 self.span_lint(lint, span, msg)
1804 }
1805 }
1806 }
1807 }
1808 }
1809
1810 pub fn check_crate(tcx: &ty::ctxt,
1811 exported_items: &privacy::ExportedItems,
1812 krate: &ast::Crate) {
1813 let mut cx = Context {
1814 dict: get_lint_dict(),
1815 cur: SmallIntMap::new(),
1816 tcx: tcx,
1817 exported_items: exported_items,
1818 cur_struct_def_id: -1,
1819 is_doc_hidden: false,
1820 lint_stack: Vec::new(),
1821 negated_expr_id: -1
1822 };
1823
1824 // Install default lint levels, followed by the command line levels, and
1825 // then actually visit the whole crate.
1826 for (_, spec) in cx.dict.iter() {
1827 if spec.default != allow {
1828 cx.cur.insert(spec.lint as uint, (spec.default, Default));
1829 }
1830 }
1831 for &(lint, level) in tcx.sess.opts.lint_opts.iter() {
1832 cx.set_level(lint, level, CommandLine);
1833 }
1834 cx.with_lint_attrs(krate.attrs.as_slice(), |cx| {
1835 cx.visit_id(ast::CRATE_NODE_ID);
1836 cx.visit_ids(|v| {
1837 v.visited_outermost = true;
1838 visit::walk_crate(v, krate, ());
1839 });
1840
1841 check_crate_attrs_usage(cx, krate.attrs.as_slice());
1842 // since the root module isn't visited as an item (because it isn't an item), warn for it
1843 // here.
1844 check_missing_doc_attrs(cx,
1845 None,
1846 krate.attrs.as_slice(),
1847 krate.span,
1848 "crate");
1849
1850 visit::walk_crate(cx, krate, ());
1851 });
1852
1853 // If we missed any lints added to the session, then there's a bug somewhere
1854 // in the iteration code.
1855 for (id, v) in tcx.sess.lints.borrow().iter() {
1856 for &(lint, span, ref msg) in v.iter() {
1857 tcx.sess.span_bug(span, format!("unprocessed lint {:?} at {}: {}",
1858 lint, tcx.map.node_to_str(*id), *msg))
1859 }
1860 }
1861
1862 tcx.sess.abort_if_errors();
1863 }
librustc/middle/lint.rs:134:47-134:47 -enum- definition:
pub enum level {
allow, warn, deny, forbid
}
references:- 22462: impl<'a> Context<'a> {
463: fn get_level(&self, lint: Lint) -> level {
464: match self.cur.find(&(lint as uint)) {
--
477: fn set_level(&mut self, lint: Lint, level: level, src: LintSource) {
478: if level == allow {
--
643: pub fn contains_lint(attrs: &[ast::Attribute],
644: level: level,
645: lintname: &'static str)
librustc/driver/session.rs:
132: pub debuginfo: DebugInfoLevel,
133: pub lint_opts: Vec<(lint::Lint, lint::level)> ,
134: pub output_types: Vec<back::link::OutputType> ,
librustc/middle/lint.rs:
135: pub enum level {
librustc/middle/lint.rs:74:47-74:47 -enum- definition:
pub enum Lint {
CTypes,
UnusedImports,
references:- 2375: pub enum Lint {
--
141: pub default: level,
142: pub lint: Lint,
143: pub desc: &'static str,
--
462: impl<'a> Context<'a> {
463: fn get_level(&self, lint: Lint) -> level {
464: match self.cur.find(&(lint as uint)) {
--
494: fn span_lint(&self, lint: Lint, span: Span, msg: &str) {
495: let (level, src) = match self.cur.find(&(lint as uint)) {
librustc/driver/session.rs:
253: pub fn add_lint(&self,
254: lint: lint::Lint,
255: id: ast::NodeId,
librustc/middle/lint.rs:
477: fn set_level(&mut self, lint: Lint, level: level, src: LintSource) {
478: if level == allow {
librustc/middle/lint.rs:1083:1-1083:1 -fn- definition:
fn check_attrs_usage(cx: &Context, attrs: &[ast::Attribute]) {
// check if element has crate-level, obsolete, or any unknown attributes.
for attr in attrs.iter() {
references:- 71777: check_missing_doc_struct_field(cx, s);
1778: check_attrs_usage(cx, s.node.attrs.as_slice());
--
1786: check_missing_doc_variant(cx, v);
1787: check_attrs_usage(cx, v.node.attrs.as_slice());
librustc/middle/lint.rs:883:4-883:4 -fn- definition:
fn check_ty(cx: &Context, ty: &ast::Ty) {
match ty.node {
ast::TyPath(_, _, id) => {
references:- 4914: for input in decl.inputs.iter() {
915: check_ty(cx, input.ty);
916: }
--
924: ast::ForeignItemFn(decl, _) => check_foreign_fn(cx, decl),
925: ast::ForeignItemStatic(t, _) => check_ty(cx, t)
926: }
librustc/middle/lint.rs:145:1-145:1 -NK_AS_STR_TODO- definition:
pub type LintDict = HashMap<&'static str, LintSpec>;
enum LintSource {
Node(Span),
references:- 2433: */
434: pub fn get_lint_dict() -> LintDict {
435: lint_table.iter().map(|&(k, v)| (k, v)).collect()
--
439: // All known lint modes (string versions)
440: dict: LintDict,
441: // Current levels of each lint warning
librustc/middle/lint.rs:437:1-437:1 -struct- definition:
struct Context<'a> {
// All known lint modes (string versions)
dict: LintDict,
references:- 44librustc/middle/lint.rs:822:4-822:4 -fn- definition:
fn uint_ty_range(uint_ty: ast::UintTy) -> (u64, u64) {
match uint_ty {
ast::TyU => (u64::MIN, u64::MAX),
references:- 2856: ty::ty_uint(uint_ty) => {
857: let (min, max): (u64, u64) = uint_ty_range(uint_ty);
858: let lit_val: u64 = match lit.node {
librustc/middle/lint.rs:433:4-433:4 -fn- definition:
*/
pub fn get_lint_dict() -> LintDict {
lint_table.iter().map(|&(k, v)| (k, v)).collect()
references:- 31813: let mut cx = Context {
1814: dict: get_lint_dict(),
1815: cur: SmallIntMap::new(),
librustc/driver/driver.rs:
832: let mut lint_opts = Vec::new();
833: let lint_dict = lint::get_lint_dict();
834: for level in lint_levels.iter() {
librustc/lib.rs:
170: let lint_dict = lint::get_lint_dict();
171: let mut lint_dict = lint_dict.move_iter()
librustc/middle/lint.rs:788:4-788:4 -fn- definition:
fn is_valid<T:cmp::Ord>(binop: ast::BinOp, v: T,
min: T, max: T) -> bool {
match binop {
references:- 2853: };
854: is_valid(norm_binop, lit_val, min, max)
855: }
--
866: };
867: is_valid(norm_binop, lit_val, min, max)
868: }
librustc/middle/lint.rs:124:1-124:1 -fn- definition:
pub fn level_to_str(lv: level) -> &'static str {
match lv {
allow => "allow",
references:- 7646: -> bool {
647: let level_name = level_to_str(level);
648: for attr in attrs.iter().filter(|m| m.name().equiv(&level_name)) {
librustc/driver/driver.rs:
834: for level in lint_levels.iter() {
835: let level_name = lint::level_to_str(*level);
librustc/lib.rs:
191: padded(max_key, name),
192: lint::level_to_str(spec.default),
193: spec.desc);
librustc/middle/lint.rs:
558: format!("{}({}) overruled by outer forbid({})",
559: level_to_str(level),
560: lintname, lintname));
librustc/middle/lint.rs:1210:4-1210:4 -fn- definition:
fn check_case(cx: &Context, sort: &str, ident: ast::Ident, span: Span) {
if !is_camel_case(ident) {
cx.span_lint(
references:- 41220: ast::ItemTy(..) | ast::ItemStruct(..) => {
1221: check_case(cx, "type", it.ident, it.span)
1222: }
1223: ast::ItemTrait(..) => {
1224: check_case(cx, "trait", it.ident, it.span)
1225: }
--
1228: for variant in enum_definition.variants.iter() {
1229: check_case(cx, "variant", variant.node.name, variant.span);
1230: }
librustc/middle/lint.rs:998:1-998:1 -struct- definition:
struct RawPtrDerivingVisitor<'a> {
cx: &'a Context<'a>
}
references:- 21003: impl<'a> Visitor<()> for RawPtrDerivingVisitor<'a> {
1004: fn visit_ty(&mut self, ty: &ast::Ty, _: ()) {
--
1022: ast::ItemStruct(..) | ast::ItemEnum(..) => {
1023: let mut visitor = RawPtrDerivingVisitor { cx: cx };
1024: visit::walk_item(&mut visitor, item, ());
librustc/middle/lint.rs:932:1-932:1 -fn- definition:
fn check_heap_type(cx: &Context, span: Span, ty: ty::t) {
let xs = [ManagedHeapMemory, OwnedHeapMemory, HeapMemory];
for &lint in xs.iter() {
references:- 31116: let ty = ty::expr_ty(cx.tcx, e);
1117: check_heap_type(cx, e.span, ty);
1118: }
librustc/middle/lint.rs:1451:1-1451:1 -fn- definition:
fn check_missing_doc_attrs(cx: &Context,
id: Option<ast::NodeId>,
attrs: &[ast::Attribute],
references:- 61540: ast::NamedField(_, vis) if vis == ast::Public =>
1541: check_missing_doc_attrs(cx,
1542: Some(cx.cur_struct_def_id),
--
1550: fn check_missing_doc_variant(cx: &Context, v: &ast::Variant) {
1551: check_missing_doc_attrs(cx,
1552: Some(v.node.id),
--
1843: // here.
1844: check_missing_doc_attrs(cx,
1845: None,
librustc/middle/lint.rs:1308:1-1308:1 -fn- definition:
fn check_unnecessary_parens_core(cx: &Context, value: &ast::Expr, msg: &str) {
match value.node {
ast::ExprParen(_) => {
references:- 21342: };
1343: check_unnecessary_parens_core(cx, value, msg);
1344: }
librustc/middle/lint.rs:139:47-139:47 -struct- definition:
pub struct LintSpec {
pub default: level,
pub lint: Lint,
references:- 72librustc/lib.rs:
librustc/middle/lint.rs:
librustc/middle/lint.rs:812:4-812:4 -fn- definition:
fn int_ty_range(int_ty: ast::IntTy) -> (i64, i64) {
match int_ty {
ast::TyI => (i64::MIN, i64::MAX),
references:- 2843: ty::ty_int(int_ty) => {
844: let (min, max) = int_ty_range(int_ty);
845: let lit_val: i64 = match lit.node {
librustc/middle/lint.rs:148:16-148:16 -enum- definition:
enum LintSource {
Node(Span),
Default,
references:- 7149: enum LintSource {
--
455: // was modified.
456: lint_stack: Vec<(Lint, level, LintSource)> ,
--
470: fn get_source(&self, lint: Lint) -> LintSource {
471: match self.cur.find(&(lint as uint)) {
--
477: fn set_level(&mut self, lint: Lint, level: level, src: LintSource) {
478: if level == allow {