]> git.proxmox.com Git - rustc.git/blame - src/librustc_lint/builtin.rs
New upstream version 1.23.0+dfsg1
[rustc.git] / src / librustc_lint / builtin.rs
CommitLineData
c34b1796
AL
1// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
2// file at the top-level directory of this distribution and at
3// http://rust-lang.org/COPYRIGHT.
4//
5// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8// option. This file may not be copied, modified, or distributed
9// except according to those terms.
10
11//! Lints in the Rust compiler.
12//!
13//! This contains lints which can feasibly be implemented as their own
14//! AST visitor. Also see `rustc::lint::builtin`, which contains the
15//! definitions of lints that are emitted directly inside the main
16//! compiler.
17//!
18//! To add a new lint to rustc, declare it here using `declare_lint!()`.
19//! Then add code to emit the new lint in the appropriate circumstances.
20//! You can do that in an existing `LintPass` if it makes sense, or in a
21//! new `LintPass`, or using `Session::add_lint` elsewhere in the
22//! compiler. Only do the latter if the check can't be written cleanly as a
23//! `LintPass` (also, note that such lints will need to be defined in
24//! `rustc::lint::builtin`, not here).
25//!
26//! If you define a new `LintPass`, you will also need to add it to the
27//! `add_builtin!` or `add_builtin_with_new!` invocation in `lib.rs`.
28//! Use the former for unit-like structs and the latter for structs with
29//! a `pub fn new()`.
30
54a0048b 31use rustc::hir::def::Def;
54a0048b 32use rustc::hir::def_id::DefId;
a7813a04 33use rustc::cfg;
54a0048b 34use rustc::ty::subst::Substs;
041b39d2 35use rustc::ty::{self, Ty};
5bcae85e 36use rustc::traits::{self, Reveal};
54a0048b 37use rustc::hir::map as hir_map;
c30ab7b3 38use util::nodemap::NodeSet;
3b2f2976 39use lint::{LateContext, LintContext, LintArray};
c30ab7b3 40use lint::{LintPass, LateLintPass, EarlyLintPass, EarlyContext};
c34b1796 41
e9174d1e 42use std::collections::HashSet;
c34b1796 43
c30ab7b3 44use syntax::ast;
9e0c209e 45use syntax::attr;
c30ab7b3 46use syntax::feature_gate::{AttributeGate, AttributeType, Stability, deprecated_attributes};
abe05a73 47use syntax_pos::{BytePos, Span, SyntaxContext};
7cac9316 48use syntax::symbol::keywords;
e9174d1e 49
54a0048b
SL
50use rustc::hir::{self, PatKind};
51use rustc::hir::intravisit::FnKind;
e9174d1e 52
b039eaaf 53use bad_style::{MethodLateContext, method_context};
c34b1796 54
b039eaaf
SL
55// hardwired lints from librustc
56pub use lint::builtin::*;
c34b1796 57
abe05a73
XL
58declare_lint! {
59 pub AUTO_IMPL,
60 Deny,
61 "The form `impl Foo for .. {}` will be removed, please use `auto trait Foo {}`"
62}
63
64#[derive(Copy, Clone)]
65pub struct AutoImpl;
66
67impl LintPass for AutoImpl {
68 fn get_lints(&self) -> LintArray {
69 lint_array!(AUTO_IMPL)
70 }
71}
72
73impl EarlyLintPass for AutoImpl {
74 fn check_item(&mut self, cx: &EarlyContext, item: &ast::Item) {
75 let msg = "The form `impl Foo for .. {}` will be removed, please use `auto trait Foo {}`";
76 match item.node {
77 ast::ItemKind::AutoImpl(..) => cx.span_lint(AUTO_IMPL, item.span, msg),
78 _ => ()
79 }
80 }
81}
82
b039eaaf
SL
83declare_lint! {
84 WHILE_TRUE,
85 Warn,
86 "suggest using `loop { }` instead of `while true { }`"
87}
c34b1796 88
b039eaaf
SL
89#[derive(Copy, Clone)]
90pub struct WhileTrue;
91
92impl LintPass for WhileTrue {
93 fn get_lints(&self) -> LintArray {
94 lint_array!(WHILE_TRUE)
c34b1796 95 }
b039eaaf 96}
c34b1796 97
476ff2be 98impl<'a, 'tcx> LateLintPass<'a, 'tcx> for WhileTrue {
b039eaaf 99 fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
9e0c209e 100 if let hir::ExprWhile(ref cond, ..) = e.node {
b039eaaf 101 if let hir::ExprLit(ref lit) = cond.node {
7453a54e 102 if let ast::LitKind::Bool(true) = lit.node {
ea8adc8c
XL
103 if lit.span.ctxt() == SyntaxContext::empty() {
104 let msg = "denote infinite loops with `loop { ... }`";
105 let mut err = cx.struct_span_lint(WHILE_TRUE, e.span, msg);
106 let condition_span = cx.tcx.sess.codemap().def_span(e.span);
107 err.span_suggestion_short(condition_span,
108 "use `loop`",
109 "loop".to_owned());
110 err.emit();
111 }
b039eaaf 112 }
c34b1796
AL
113 }
114 }
115 }
116}
117
118declare_lint! {
b039eaaf
SL
119 BOX_POINTERS,
120 Allow,
121 "use of owned (Box type) heap memory"
c34b1796
AL
122}
123
124#[derive(Copy, Clone)]
b039eaaf
SL
125pub struct BoxPointers;
126
127impl BoxPointers {
476ff2be 128 fn check_heap_type<'a, 'tcx>(&self, cx: &LateContext, span: Span, ty: Ty) {
b039eaaf 129 for leaf_ty in ty.walk() {
32a655c1 130 if leaf_ty.is_box() {
b039eaaf
SL
131 let m = format!("type uses owned (Box type) pointers: {}", ty);
132 cx.span_lint(BOX_POINTERS, span, &m);
c34b1796
AL
133 }
134 }
135 }
136}
137
b039eaaf 138impl LintPass for BoxPointers {
c34b1796 139 fn get_lints(&self) -> LintArray {
b039eaaf 140 lint_array!(BOX_POINTERS)
c34b1796 141 }
b039eaaf 142}
c34b1796 143
476ff2be 144impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BoxPointers {
b039eaaf 145 fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
c34b1796 146 match it.node {
b039eaaf
SL
147 hir::ItemFn(..) |
148 hir::ItemTy(..) |
149 hir::ItemEnum(..) |
9e0c209e 150 hir::ItemStruct(..) |
c30ab7b3 151 hir::ItemUnion(..) => {
32a655c1 152 let def_id = cx.tcx.hir.local_def_id(it.id);
7cac9316 153 self.check_heap_type(cx, it.span, cx.tcx.type_of(def_id))
c30ab7b3 154 }
476ff2be 155 _ => ()
d9579d0f 156 }
d9579d0f 157
b039eaaf
SL
158 // If it's a struct, we also have to check the fields' types
159 match it.node {
9e0c209e
SL
160 hir::ItemStruct(ref struct_def, _) |
161 hir::ItemUnion(ref struct_def, _) => {
b039eaaf 162 for struct_field in struct_def.fields() {
32a655c1 163 let def_id = cx.tcx.hir.local_def_id(struct_field.id);
476ff2be 164 self.check_heap_type(cx, struct_field.span,
7cac9316 165 cx.tcx.type_of(def_id));
b039eaaf 166 }
d9579d0f 167 }
c30ab7b3 168 _ => (),
d9579d0f
AL
169 }
170 }
171
b039eaaf 172 fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
3b2f2976 173 let ty = cx.tables.node_id_to_type(e.hir_id);
b039eaaf 174 self.check_heap_type(cx, e.span, ty);
c34b1796
AL
175 }
176}
177
c34b1796
AL
178declare_lint! {
179 NON_SHORTHAND_FIELD_PATTERNS,
180 Warn,
abe05a73 181 "using `Struct { x: x }` instead of `Struct { x }` in a pattern"
c34b1796
AL
182}
183
184#[derive(Copy, Clone)]
185pub struct NonShorthandFieldPatterns;
186
187impl LintPass for NonShorthandFieldPatterns {
188 fn get_lints(&self) -> LintArray {
189 lint_array!(NON_SHORTHAND_FIELD_PATTERNS)
190 }
b039eaaf 191}
c34b1796 192
476ff2be 193impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonShorthandFieldPatterns {
b039eaaf 194 fn check_pat(&mut self, cx: &LateContext, pat: &hir::Pat) {
3157f602
XL
195 if let PatKind::Struct(_, ref field_pats, _) = pat.node {
196 for fieldpat in field_pats {
c34b1796 197 if fieldpat.node.is_shorthand {
3157f602 198 continue;
b039eaaf 199 }
476ff2be 200 if let PatKind::Binding(_, _, ident, None) = fieldpat.node.pat.node {
3157f602 201 if ident.node == fieldpat.node.name {
abe05a73 202 let mut err = cx.struct_span_lint(NON_SHORTHAND_FIELD_PATTERNS,
c30ab7b3 203 fieldpat.span,
abe05a73
XL
204 &format!("the `{}:` in this pattern is redundant",
205 ident.node));
206 let subspan = cx.tcx.sess.codemap().span_through_char(fieldpat.span, ':');
207 err.span_suggestion_short(subspan,
208 "remove this",
209 format!("{}", ident.node));
210 err.emit();
c34b1796
AL
211 }
212 }
213 }
214 }
215 }
216}
217
c34b1796
AL
218declare_lint! {
219 UNSAFE_CODE,
220 Allow,
221 "usage of `unsafe` code"
222}
223
224#[derive(Copy, Clone)]
225pub struct UnsafeCode;
226
227impl LintPass for UnsafeCode {
228 fn get_lints(&self) -> LintArray {
229 lint_array!(UNSAFE_CODE)
230 }
b039eaaf 231}
c34b1796 232
3b2f2976
XL
233impl UnsafeCode {
234 fn report_unsafe(&self, cx: &LateContext, span: Span, desc: &'static str) {
235 // This comes from a macro that has #[allow_internal_unsafe].
236 if span.allows_unsafe() {
237 return;
238 }
239
240 cx.span_lint(UNSAFE_CODE, span, desc);
241 }
242}
243
476ff2be 244impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnsafeCode {
b039eaaf 245 fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
e9174d1e 246 if let hir::ExprBlock(ref blk) = e.node {
c34b1796 247 // Don't warn about generated blocks, that'll just pollute the output.
e9174d1e 248 if blk.rules == hir::UnsafeBlock(hir::UserProvided) {
3b2f2976 249 self.report_unsafe(cx, blk.span, "usage of an `unsafe` block");
c34b1796
AL
250 }
251 }
252 }
253
b039eaaf 254 fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
c34b1796 255 match it.node {
abe05a73 256 hir::ItemTrait(_, hir::Unsafety::Unsafe, ..) => {
3b2f2976 257 self.report_unsafe(cx, it.span, "declaration of an `unsafe` trait")
c30ab7b3 258 }
c34b1796 259
c30ab7b3 260 hir::ItemImpl(hir::Unsafety::Unsafe, ..) => {
3b2f2976 261 self.report_unsafe(cx, it.span, "implementation of an `unsafe` trait")
c30ab7b3 262 }
c34b1796
AL
263
264 _ => return,
265 }
266 }
267
c30ab7b3
SL
268 fn check_fn(&mut self,
269 cx: &LateContext,
476ff2be 270 fk: FnKind<'tcx>,
c30ab7b3 271 _: &hir::FnDecl,
32a655c1 272 _: &hir::Body,
c30ab7b3
SL
273 span: Span,
274 _: ast::NodeId) {
c34b1796 275 match fk {
c30ab7b3 276 FnKind::ItemFn(_, _, hir::Unsafety::Unsafe, ..) => {
3b2f2976 277 self.report_unsafe(cx, span, "declaration of an `unsafe` function")
c30ab7b3 278 }
c34b1796 279
9e0c209e 280 FnKind::Method(_, sig, ..) => {
e9174d1e 281 if sig.unsafety == hir::Unsafety::Unsafe {
3b2f2976 282 self.report_unsafe(cx, span, "implementation of an `unsafe` method")
c34b1796 283 }
c30ab7b3 284 }
c34b1796
AL
285
286 _ => (),
287 }
288 }
289
32a655c1
SL
290 fn check_trait_item(&mut self, cx: &LateContext, item: &hir::TraitItem) {
291 if let hir::TraitItemKind::Method(ref sig, hir::TraitMethod::Required(_)) = item.node {
e9174d1e 292 if sig.unsafety == hir::Unsafety::Unsafe {
3b2f2976 293 self.report_unsafe(cx, item.span, "declaration of an `unsafe` method")
c34b1796
AL
294 }
295 }
296 }
297}
298
c34b1796
AL
299declare_lint! {
300 MISSING_DOCS,
301 Allow,
302 "detects missing documentation for public members"
303}
304
305pub struct MissingDoc {
c34b1796
AL
306 /// Stack of whether #[doc(hidden)] is set
307 /// at each level which has lint attributes.
308 doc_hidden_stack: Vec<bool>,
309
310 /// Private traits or trait items that leaked through. Don't check their methods.
311 private_traits: HashSet<ast::NodeId>,
312}
313
314impl MissingDoc {
315 pub fn new() -> MissingDoc {
316 MissingDoc {
c30ab7b3 317 doc_hidden_stack: vec![false],
c34b1796
AL
318 private_traits: HashSet::new(),
319 }
320 }
321
322 fn doc_hidden(&self) -> bool {
323 *self.doc_hidden_stack.last().expect("empty doc_hidden_stack")
324 }
325
326 fn check_missing_docs_attrs(&self,
c30ab7b3
SL
327 cx: &LateContext,
328 id: Option<ast::NodeId>,
329 attrs: &[ast::Attribute],
330 sp: Span,
331 desc: &'static str) {
c34b1796
AL
332 // If we're building a test harness, then warning about
333 // documentation is probably not really relevant right now.
334 if cx.sess().opts.test {
335 return;
336 }
337
338 // `#[doc(hidden)]` disables missing_docs check.
339 if self.doc_hidden() {
340 return;
341 }
342
343 // Only check publicly-visible items, using the result from the privacy pass.
344 // It's an option so the crate root can also use this function (it doesn't
345 // have a NodeId).
92a42be0
SL
346 if let Some(id) = id {
347 if !cx.access_levels.is_exported(id) {
c34b1796
AL
348 return;
349 }
350 }
351
cc61c64b 352 let has_doc = attrs.iter().any(|a| a.is_value_str() && a.check_name("doc"));
c34b1796 353 if !has_doc {
c30ab7b3
SL
354 cx.span_lint(MISSING_DOCS,
355 sp,
c34b1796
AL
356 &format!("missing documentation for {}", desc));
357 }
358 }
359}
360
361impl LintPass for MissingDoc {
362 fn get_lints(&self) -> LintArray {
363 lint_array!(MISSING_DOCS)
364 }
b039eaaf 365}
c34b1796 366
476ff2be 367impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDoc {
b039eaaf 368 fn enter_lint_attrs(&mut self, _: &LateContext, attrs: &[ast::Attribute]) {
c30ab7b3
SL
369 let doc_hidden = self.doc_hidden() ||
370 attrs.iter().any(|attr| {
371 attr.check_name("doc") &&
372 match attr.meta_item_list() {
c34b1796 373 None => false,
cc61c64b 374 Some(l) => attr::list_contains_name(&l, "hidden"),
c34b1796
AL
375 }
376 });
377 self.doc_hidden_stack.push(doc_hidden);
378 }
379
476ff2be 380 fn exit_lint_attrs(&mut self, _: &LateContext, _attrs: &[ast::Attribute]) {
c34b1796
AL
381 self.doc_hidden_stack.pop().expect("empty doc_hidden_stack");
382 }
383
b039eaaf 384 fn check_crate(&mut self, cx: &LateContext, krate: &hir::Crate) {
c34b1796
AL
385 self.check_missing_docs_attrs(cx, None, &krate.attrs, krate.span, "crate");
386 }
387
b039eaaf 388 fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
c34b1796 389 let desc = match it.node {
e9174d1e
SL
390 hir::ItemFn(..) => "a function",
391 hir::ItemMod(..) => "a module",
392 hir::ItemEnum(..) => "an enum",
393 hir::ItemStruct(..) => "a struct",
9e0c209e 394 hir::ItemUnion(..) => "a union",
32a655c1 395 hir::ItemTrait(.., ref trait_item_refs) => {
c34b1796 396 // Issue #11592, traits are always considered exported, even when private.
e9174d1e 397 if it.vis == hir::Visibility::Inherited {
c34b1796 398 self.private_traits.insert(it.id);
32a655c1
SL
399 for trait_item_ref in trait_item_refs {
400 self.private_traits.insert(trait_item_ref.id.node_id);
c34b1796 401 }
c30ab7b3 402 return;
c34b1796
AL
403 }
404 "a trait"
c30ab7b3 405 }
e9174d1e 406 hir::ItemTy(..) => "a type alias",
476ff2be 407 hir::ItemImpl(.., Some(ref trait_ref), _, ref impl_item_refs) => {
c34b1796
AL
408 // If the trait is private, add the impl items to private_traits so they don't get
409 // reported for missing docs.
476ff2be 410 let real_trait = trait_ref.path.def.def_id();
32a655c1
SL
411 if let Some(node_id) = cx.tcx.hir.as_local_node_id(real_trait) {
412 match cx.tcx.hir.find(node_id) {
c30ab7b3
SL
413 Some(hir_map::NodeItem(item)) => {
414 if item.vis == hir::Visibility::Inherited {
476ff2be
SL
415 for impl_item_ref in impl_item_refs {
416 self.private_traits.insert(impl_item_ref.id.node_id);
c30ab7b3 417 }
b039eaaf 418 }
c30ab7b3
SL
419 }
420 _ => {}
b039eaaf 421 }
c34b1796 422 }
c30ab7b3
SL
423 return;
424 }
e9174d1e
SL
425 hir::ItemConst(..) => "a constant",
426 hir::ItemStatic(..) => "a static",
c30ab7b3 427 _ => return,
c34b1796
AL
428 };
429
430 self.check_missing_docs_attrs(cx, Some(it.id), &it.attrs, it.span, desc);
431 }
432
b039eaaf 433 fn check_trait_item(&mut self, cx: &LateContext, trait_item: &hir::TraitItem) {
c30ab7b3
SL
434 if self.private_traits.contains(&trait_item.id) {
435 return;
436 }
c34b1796
AL
437
438 let desc = match trait_item.node {
32a655c1
SL
439 hir::TraitItemKind::Const(..) => "an associated constant",
440 hir::TraitItemKind::Method(..) => "a trait method",
441 hir::TraitItemKind::Type(..) => "an associated type",
c34b1796
AL
442 };
443
c30ab7b3
SL
444 self.check_missing_docs_attrs(cx,
445 Some(trait_item.id),
c34b1796 446 &trait_item.attrs,
c30ab7b3
SL
447 trait_item.span,
448 desc);
c34b1796
AL
449 }
450
b039eaaf 451 fn check_impl_item(&mut self, cx: &LateContext, impl_item: &hir::ImplItem) {
c34b1796 452 // If the method is an impl for a trait, don't doc.
7cac9316 453 if method_context(cx, impl_item.id) == MethodLateContext::TraitImpl {
c34b1796
AL
454 return;
455 }
456
457 let desc = match impl_item.node {
92a42be0
SL
458 hir::ImplItemKind::Const(..) => "an associated constant",
459 hir::ImplItemKind::Method(..) => "a method",
460 hir::ImplItemKind::Type(_) => "an associated type",
c34b1796 461 };
c30ab7b3
SL
462 self.check_missing_docs_attrs(cx,
463 Some(impl_item.id),
c34b1796 464 &impl_item.attrs,
c30ab7b3
SL
465 impl_item.span,
466 desc);
c34b1796
AL
467 }
468
b039eaaf 469 fn check_struct_field(&mut self, cx: &LateContext, sf: &hir::StructField) {
54a0048b 470 if !sf.is_positional() {
7cac9316
XL
471 self.check_missing_docs_attrs(cx,
472 Some(sf.id),
473 &sf.attrs,
474 sf.span,
475 "a struct field")
c34b1796
AL
476 }
477 }
478
b039eaaf 479 fn check_variant(&mut self, cx: &LateContext, v: &hir::Variant, _: &hir::Generics) {
c30ab7b3
SL
480 self.check_missing_docs_attrs(cx,
481 Some(v.node.data.id()),
482 &v.node.attrs,
483 v.span,
484 "a variant");
c34b1796
AL
485 }
486}
487
488declare_lint! {
489 pub MISSING_COPY_IMPLEMENTATIONS,
490 Allow,
491 "detects potentially-forgotten implementations of `Copy`"
492}
493
494#[derive(Copy, Clone)]
495pub struct MissingCopyImplementations;
496
497impl LintPass for MissingCopyImplementations {
498 fn get_lints(&self) -> LintArray {
499 lint_array!(MISSING_COPY_IMPLEMENTATIONS)
500 }
b039eaaf 501}
c34b1796 502
476ff2be 503impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingCopyImplementations {
b039eaaf 504 fn check_item(&mut self, cx: &LateContext, item: &hir::Item) {
92a42be0 505 if !cx.access_levels.is_reachable(item.id) {
c34b1796
AL
506 return;
507 }
e9174d1e
SL
508 let (def, ty) = match item.node {
509 hir::ItemStruct(_, ref ast_generics) => {
c34b1796
AL
510 if ast_generics.is_parameterized() {
511 return;
512 }
7cac9316 513 let def = cx.tcx.adt_def(cx.tcx.hir.local_def_id(item.id));
c30ab7b3 514 (def, cx.tcx.mk_adt(def, cx.tcx.intern_substs(&[])))
9e0c209e
SL
515 }
516 hir::ItemUnion(_, ref ast_generics) => {
517 if ast_generics.is_parameterized() {
518 return;
519 }
7cac9316 520 let def = cx.tcx.adt_def(cx.tcx.hir.local_def_id(item.id));
c30ab7b3 521 (def, cx.tcx.mk_adt(def, cx.tcx.intern_substs(&[])))
c34b1796 522 }
e9174d1e 523 hir::ItemEnum(_, ref ast_generics) => {
c34b1796
AL
524 if ast_generics.is_parameterized() {
525 return;
526 }
7cac9316 527 let def = cx.tcx.adt_def(cx.tcx.hir.local_def_id(item.id));
c30ab7b3 528 (def, cx.tcx.mk_adt(def, cx.tcx.intern_substs(&[])))
c34b1796
AL
529 }
530 _ => return,
531 };
8bb4bdeb 532 if def.has_dtor(cx.tcx) {
c30ab7b3
SL
533 return;
534 }
7cac9316
XL
535 let param_env = ty::ParamEnv::empty(Reveal::UserFacing);
536 if !ty.moves_by_default(cx.tcx, param_env, item.span) {
c34b1796
AL
537 return;
538 }
7cac9316 539 if param_env.can_type_implement_copy(cx.tcx, ty, item.span).is_ok() {
c34b1796
AL
540 cx.span_lint(MISSING_COPY_IMPLEMENTATIONS,
541 item.span,
542 "type could implement `Copy`; consider adding `impl \
543 Copy`")
544 }
545 }
546}
547
548declare_lint! {
549 MISSING_DEBUG_IMPLEMENTATIONS,
550 Allow,
551 "detects missing implementations of fmt::Debug"
552}
553
554pub struct MissingDebugImplementations {
555 impling_types: Option<NodeSet>,
556}
557
558impl MissingDebugImplementations {
559 pub fn new() -> MissingDebugImplementations {
c30ab7b3 560 MissingDebugImplementations { impling_types: None }
c34b1796
AL
561 }
562}
563
564impl LintPass for MissingDebugImplementations {
565 fn get_lints(&self) -> LintArray {
566 lint_array!(MISSING_DEBUG_IMPLEMENTATIONS)
567 }
b039eaaf 568}
c34b1796 569
476ff2be 570impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDebugImplementations {
b039eaaf 571 fn check_item(&mut self, cx: &LateContext, item: &hir::Item) {
92a42be0 572 if !cx.access_levels.is_reachable(item.id) {
c34b1796
AL
573 return;
574 }
575
576 match item.node {
c30ab7b3
SL
577 hir::ItemStruct(..) |
578 hir::ItemUnion(..) |
579 hir::ItemEnum(..) => {}
c34b1796
AL
580 _ => return,
581 }
582
ea8adc8c 583 let debug = match cx.tcx.lang_items().debug_trait() {
c34b1796
AL
584 Some(debug) => debug,
585 None => return,
586 };
587
588 if self.impling_types.is_none() {
d9579d0f 589 let mut impls = NodeSet();
041b39d2 590 cx.tcx.for_each_impl(debug, |d| {
7cac9316 591 if let Some(ty_def) = cx.tcx.type_of(d).ty_to_def_id() {
32a655c1 592 if let Some(node_id) = cx.tcx.hir.as_local_node_id(ty_def) {
476ff2be 593 impls.insert(node_id);
d9579d0f 594 }
c34b1796 595 }
d9579d0f
AL
596 });
597
c34b1796
AL
598 self.impling_types = Some(impls);
599 debug!("{:?}", self.impling_types);
600 }
601
602 if !self.impling_types.as_ref().unwrap().contains(&item.id) {
603 cx.span_lint(MISSING_DEBUG_IMPLEMENTATIONS,
604 item.span,
605 "type does not implement `fmt::Debug`; consider adding #[derive(Debug)] \
606 or a manual implementation")
607 }
608 }
609}
610
7cac9316
XL
611declare_lint! {
612 pub ANONYMOUS_PARAMETERS,
613 Allow,
614 "detects anonymous parameters"
615}
616
617/// Checks for use of anonymous parameters (RFC 1685)
618#[derive(Clone)]
619pub struct AnonymousParameters;
620
621impl LintPass for AnonymousParameters {
622 fn get_lints(&self) -> LintArray {
623 lint_array!(ANONYMOUS_PARAMETERS)
624 }
625}
626
627impl EarlyLintPass for AnonymousParameters {
628 fn check_trait_item(&mut self, cx: &EarlyContext, it: &ast::TraitItem) {
629 match it.node {
630 ast::TraitItemKind::Method(ref sig, _) => {
631 for arg in sig.decl.inputs.iter() {
632 match arg.pat.node {
633 ast::PatKind::Ident(_, ident, None) => {
634 if ident.node.name == keywords::Invalid.name() {
635 cx.span_lint(ANONYMOUS_PARAMETERS,
636 arg.pat.span,
637 "use of deprecated anonymous parameter");
638 }
639 }
640 _ => (),
641 }
642 }
643 },
644 _ => (),
645 }
646 }
647}
648
c30ab7b3
SL
649/// Checks for use of attributes which have been deprecated.
650#[derive(Clone)]
651pub struct DeprecatedAttr {
652 // This is not free to compute, so we want to keep it around, rather than
653 // compute it for every attribute.
654 depr_attrs: Vec<&'static (&'static str, AttributeType, AttributeGate)>,
655}
656
657impl DeprecatedAttr {
658 pub fn new() -> DeprecatedAttr {
659 DeprecatedAttr {
660 depr_attrs: deprecated_attributes(),
661 }
662 }
663}
664
665impl LintPass for DeprecatedAttr {
666 fn get_lints(&self) -> LintArray {
abe05a73 667 lint_array!()
c30ab7b3
SL
668 }
669}
670
671impl EarlyLintPass for DeprecatedAttr {
672 fn check_attribute(&mut self, cx: &EarlyContext, attr: &ast::Attribute) {
cc61c64b 673 let name = unwrap_or!(attr.name(), return);
c30ab7b3 674 for &&(n, _, ref g) in &self.depr_attrs {
476ff2be 675 if name == n {
c30ab7b3
SL
676 if let &AttributeGate::Gated(Stability::Deprecated(link),
677 ref name,
678 ref reason,
679 _) = g {
ea8adc8c
XL
680 let msg = format!("use of deprecated attribute `{}`: {}. See {}",
681 name, reason, link);
682 let mut err = cx.struct_span_lint(DEPRECATED, attr.span, &msg);
683 err.span_suggestion_short(attr.span, "remove this attribute", "".to_owned());
684 err.emit();
c30ab7b3
SL
685 }
686 return;
687 }
688 }
689 }
690}
691
7cac9316
XL
692declare_lint! {
693 pub ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
694 Warn,
695 "floating-point literals cannot be used in patterns"
696}
697
698/// Checks for floating point literals in patterns.
699#[derive(Clone)]
700pub struct IllegalFloatLiteralPattern;
701
702impl LintPass for IllegalFloatLiteralPattern {
703 fn get_lints(&self) -> LintArray {
704 lint_array!(ILLEGAL_FLOATING_POINT_LITERAL_PATTERN)
705 }
706}
707
708fn fl_lit_check_expr(cx: &EarlyContext, expr: &ast::Expr) {
709 use self::ast::{ExprKind, LitKind};
710 match expr.node {
711 ExprKind::Lit(ref l) => {
712 match l.node {
713 LitKind::FloatUnsuffixed(..) |
714 LitKind::Float(..) => {
715 cx.span_lint(ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
716 l.span,
717 "floating-point literals cannot be used in patterns");
718 },
719 _ => (),
720 }
721 }
722 // These may occur in patterns
723 // and can maybe contain float literals
724 ExprKind::Unary(_, ref f) => fl_lit_check_expr(cx, f),
041b39d2
XL
725 // Other kinds of exprs can't occur in patterns so we don't have to check them
726 // (ast_validation will emit an error if they occur)
727 _ => (),
7cac9316
XL
728 }
729}
730
731impl EarlyLintPass for IllegalFloatLiteralPattern {
732 fn check_pat(&mut self, cx: &EarlyContext, pat: &ast::Pat) {
733 use self::ast::PatKind;
734 pat.walk(&mut |p| {
735 match p.node {
736 // Wildcard patterns and paths are uninteresting for the lint
737 PatKind::Wild |
738 PatKind::Path(..) => (),
739
740 // The walk logic recurses inside these
741 PatKind::Ident(..) |
742 PatKind::Struct(..) |
743 PatKind::Tuple(..) |
744 PatKind::TupleStruct(..) |
745 PatKind::Ref(..) |
746 PatKind::Box(..) |
747 PatKind::Slice(..) => (),
748
749 // Extract the expressions and check them
750 PatKind::Lit(ref e) => fl_lit_check_expr(cx, e),
751 PatKind::Range(ref st, ref en, _) => {
752 fl_lit_check_expr(cx, st);
753 fl_lit_check_expr(cx, en);
754 },
755
756 PatKind::Mac(_) => bug!("lint must run post-expansion"),
757 }
758 true
759 });
760 }
761}
762
3b2f2976
XL
763declare_lint! {
764 pub UNUSED_DOC_COMMENT,
765 Warn,
766 "detects doc comments that aren't used by rustdoc"
767}
768
769#[derive(Copy, Clone)]
770pub struct UnusedDocComment;
771
772impl LintPass for UnusedDocComment {
773 fn get_lints(&self) -> LintArray {
774 lint_array![UNUSED_DOC_COMMENT]
775 }
776}
777
778impl UnusedDocComment {
779 fn warn_if_doc<'a, 'tcx,
780 I: Iterator<Item=&'a ast::Attribute>,
781 C: LintContext<'tcx>>(&self, mut attrs: I, cx: &C) {
782 if let Some(attr) = attrs.find(|a| a.is_value_str() && a.check_name("doc")) {
783 cx.struct_span_lint(UNUSED_DOC_COMMENT, attr.span, "doc comment not used by rustdoc")
784 .emit();
785 }
786 }
787}
788
789impl EarlyLintPass for UnusedDocComment {
790 fn check_local(&mut self, cx: &EarlyContext, decl: &ast::Local) {
791 self.warn_if_doc(decl.attrs.iter(), cx);
792 }
793
794 fn check_arm(&mut self, cx: &EarlyContext, arm: &ast::Arm) {
795 self.warn_if_doc(arm.attrs.iter(), cx);
796 }
797
798 fn check_expr(&mut self, cx: &EarlyContext, expr: &ast::Expr) {
799 self.warn_if_doc(expr.attrs.iter(), cx);
800 }
801}
802
c34b1796
AL
803declare_lint! {
804 pub UNCONDITIONAL_RECURSION,
805 Warn,
806 "functions that cannot return without calling themselves"
807}
808
809#[derive(Copy, Clone)]
810pub struct UnconditionalRecursion;
811
812
813impl LintPass for UnconditionalRecursion {
814 fn get_lints(&self) -> LintArray {
815 lint_array![UNCONDITIONAL_RECURSION]
816 }
b039eaaf 817}
c34b1796 818
476ff2be 819impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnconditionalRecursion {
c30ab7b3
SL
820 fn check_fn(&mut self,
821 cx: &LateContext,
822 fn_kind: FnKind,
823 _: &hir::FnDecl,
32a655c1 824 body: &hir::Body,
c30ab7b3
SL
825 sp: Span,
826 id: ast::NodeId) {
c1a9b12d 827 let method = match fn_kind {
e9174d1e
SL
828 FnKind::ItemFn(..) => None,
829 FnKind::Method(..) => {
32a655c1 830 Some(cx.tcx.associated_item(cx.tcx.hir.local_def_id(id)))
c1a9b12d 831 }
c34b1796 832 // closures can't recur, so they don't matter.
c30ab7b3 833 FnKind::Closure(_) => return,
c34b1796
AL
834 };
835
c34b1796
AL
836 // Walk through this function (say `f`) looking to see if
837 // every possible path references itself, i.e. the function is
838 // called recursively unconditionally. This is done by trying
839 // to find a path from the entry node to the exit node that
840 // *doesn't* call `f` by traversing from the entry while
841 // pretending that calls of `f` are sinks (i.e. ignoring any
842 // exit edges from them).
843 //
844 // NB. this has an edge case with non-returning statements,
845 // like `loop {}` or `panic!()`: control flow never reaches
846 // the exit node through these, so one can have a function
847 // that never actually calls itselfs but is still picked up by
848 // this lint:
849 //
850 // fn f(cond: bool) {
851 // if !cond { panic!() } // could come from `assert!(cond)`
852 // f(false)
853 // }
854 //
855 // In general, functions of that form may be able to call
856 // itself a finite number of times and then diverge. The lint
857 // considers this to be an error for two reasons, (a) it is
858 // easier to implement, and (b) it seems rare to actually want
859 // to have behaviour like the above, rather than
860 // e.g. accidentally recurring after an assert.
861
8bb4bdeb 862 let cfg = cfg::CFG::new(cx.tcx, &body);
c34b1796
AL
863
864 let mut work_queue = vec![cfg.entry];
865 let mut reached_exit_without_self_call = false;
866 let mut self_call_spans = vec![];
e9174d1e 867 let mut visited = HashSet::new();
c34b1796
AL
868
869 while let Some(idx) = work_queue.pop() {
870 if idx == cfg.exit {
871 // found a path!
872 reached_exit_without_self_call = true;
873 break;
874 }
875
876 let cfg_id = idx.node_id();
877 if visited.contains(&cfg_id) {
878 // already done
879 continue;
880 }
881 visited.insert(cfg_id);
882
c34b1796 883 // is this a recursive call?
ea8adc8c
XL
884 let local_id = cfg.graph.node_data(idx).id();
885 if local_id != hir::DUMMY_ITEM_LOCAL_ID {
886 let node_id = cx.tcx.hir.hir_to_node_id(hir::HirId {
887 owner: body.value.hir_id.owner,
888 local_id
889 });
890 let self_recursive = match method {
32a655c1
SL
891 Some(ref method) => expr_refers_to_this_method(cx, method, node_id),
892 None => expr_refers_to_this_fn(cx, id, node_id),
ea8adc8c
XL
893 };
894 if self_recursive {
895 self_call_spans.push(cx.tcx.hir.span(node_id));
896 // this is a self call, so we shouldn't explore past
897 // this node in the CFG.
898 continue;
c1a9b12d 899 }
c34b1796 900 }
ea8adc8c 901
c34b1796 902 // add the successors of this node to explore the graph further.
d9579d0f 903 for (_, edge) in cfg.graph.outgoing_edges(idx) {
c34b1796
AL
904 let target_idx = edge.target();
905 let target_cfg_id = target_idx.node_id();
906 if !visited.contains(&target_cfg_id) {
907 work_queue.push(target_idx)
908 }
d9579d0f 909 }
c34b1796
AL
910 }
911
912 // Check the number of self calls because a function that
913 // doesn't return (e.g. calls a `-> !` function or `loop { /*
914 // no break */ }`) shouldn't be linted unless it actually
915 // recurs.
9346a6ac 916 if !reached_exit_without_self_call && !self_call_spans.is_empty() {
c30ab7b3
SL
917 let mut db = cx.struct_span_lint(UNCONDITIONAL_RECURSION,
918 sp,
9cc50fc6 919 "function cannot return without recurring");
3b2f2976
XL
920 // offer some help to the programmer.
921 for call in &self_call_spans {
922 db.span_note(*call, "recursive call site");
c34b1796 923 }
3b2f2976
XL
924 db.help("a `loop` may express intention \
925 better if this is on purpose");
9cc50fc6 926 db.emit();
c34b1796
AL
927 }
928
929 // all done
930 return;
931
c1a9b12d
SL
932 // Functions for identifying if the given Expr NodeId `id`
933 // represents a call to the function `fn_id`/method `method`.
934
32a655c1
SL
935 fn expr_refers_to_this_fn(cx: &LateContext, fn_id: ast::NodeId, id: ast::NodeId) -> bool {
936 match cx.tcx.hir.get(id) {
e9174d1e 937 hir_map::NodeExpr(&hir::Expr { node: hir::ExprCall(ref callee, _), .. }) => {
476ff2be 938 let def = if let hir::ExprPath(ref qpath) = callee.node {
3b2f2976 939 cx.tables.qpath_def(qpath, callee.hir_id)
476ff2be
SL
940 } else {
941 return false;
942 };
ea8adc8c
XL
943 match def {
944 Def::Local(..) | Def::Upvar(..) => false,
945 _ => def.def_id() == cx.tcx.hir.local_def_id(fn_id)
946 }
c1a9b12d 947 }
c30ab7b3 948 _ => false,
c1a9b12d
SL
949 }
950 }
c34b1796 951
c1a9b12d 952 // Check if the expression `id` performs a call to `method`.
32a655c1
SL
953 fn expr_refers_to_this_method(cx: &LateContext,
954 method: &ty::AssociatedItem,
955 id: ast::NodeId)
956 -> bool {
c30ab7b3
SL
957 use rustc::ty::adjustment::*;
958
7cac9316
XL
959 // Ignore non-expressions.
960 let expr = if let hir_map::NodeExpr(e) = cx.tcx.hir.get(id) {
961 e
962 } else {
963 return false;
964 };
c34b1796 965
c1a9b12d 966 // Check for overloaded autoderef method calls.
7cac9316
XL
967 let mut source = cx.tables.expr_ty(expr);
968 for adjustment in cx.tables.expr_adjustments(expr) {
969 if let Adjust::Deref(Some(deref)) = adjustment.kind {
970 let (def_id, substs) = deref.method_call(cx.tcx, source);
041b39d2 971 if method_call_refers_to_method(cx, method, def_id, substs, id) {
7cac9316 972 return true;
c34b1796
AL
973 }
974 }
7cac9316
XL
975 source = adjustment.target;
976 }
977
978 // Check for method calls and overloaded operators.
979 if cx.tables.is_method_call(expr) {
3b2f2976
XL
980 let hir_id = cx.tcx.hir.definitions().node_to_hir_id(id);
981 let def_id = cx.tables.type_dependent_defs()[hir_id].def_id();
982 let substs = cx.tables.node_substs(hir_id);
041b39d2 983 if method_call_refers_to_method(cx, method, def_id, substs, id) {
7cac9316
XL
984 return true;
985 }
c1a9b12d 986 }
c34b1796 987
c1a9b12d 988 // Check for calls to methods via explicit paths (e.g. `T::method()`).
7cac9316
XL
989 match expr.node {
990 hir::ExprCall(ref callee, _) => {
476ff2be 991 let def = if let hir::ExprPath(ref qpath) = callee.node {
3b2f2976 992 cx.tables.qpath_def(qpath, callee.hir_id)
476ff2be
SL
993 } else {
994 return false;
995 };
996 match def {
997 Def::Method(def_id) => {
3b2f2976 998 let substs = cx.tables.node_substs(callee.hir_id);
041b39d2 999 method_call_refers_to_method(cx, method, def_id, substs, id)
c1a9b12d 1000 }
c30ab7b3 1001 _ => false,
c1a9b12d
SL
1002 }
1003 }
c30ab7b3 1004 _ => false,
c1a9b12d
SL
1005 }
1006 }
1007
1008 // Check if the method call to the method with the ID `callee_id`
1009 // and instantiated with `callee_substs` refers to method `method`.
041b39d2 1010 fn method_call_refers_to_method<'a, 'tcx>(cx: &LateContext<'a, 'tcx>,
476ff2be 1011 method: &ty::AssociatedItem,
a7813a04
XL
1012 callee_id: DefId,
1013 callee_substs: &Substs<'tcx>,
c30ab7b3
SL
1014 expr_id: ast::NodeId)
1015 -> bool {
041b39d2 1016 let tcx = cx.tcx;
476ff2be 1017 let callee_item = tcx.associated_item(callee_id);
c1a9b12d 1018
476ff2be 1019 match callee_item.container {
c1a9b12d
SL
1020 // This is an inherent method, so the `def_id` refers
1021 // directly to the method definition.
c30ab7b3 1022 ty::ImplContainer(_) => callee_id == method.def_id,
c1a9b12d
SL
1023
1024 // A trait method, from any number of possible sources.
1025 // Attempt to select a concrete impl before checking.
1026 ty::TraitContainer(trait_def_id) => {
9e0c209e 1027 let trait_ref = ty::TraitRef::from_method(tcx, trait_def_id, callee_substs);
c1a9b12d 1028 let trait_ref = ty::Binder(trait_ref);
32a655c1 1029 let span = tcx.hir.span(expr_id);
c1a9b12d
SL
1030 let obligation =
1031 traits::Obligation::new(traits::ObligationCause::misc(span, expr_id),
041b39d2 1032 cx.param_env,
c1a9b12d
SL
1033 trait_ref.to_poly_trait_predicate());
1034
041b39d2 1035 tcx.infer_ctxt().enter(|infcx| {
a7813a04
XL
1036 let mut selcx = traits::SelectionContext::new(&infcx);
1037 match selcx.select(&obligation) {
1038 // The method comes from a `T: Trait` bound.
1039 // If `T` is `Self`, then this call is inside
1040 // a default method definition.
1041 Ok(Some(traits::VtableParam(_))) => {
9e0c209e 1042 let on_self = trait_ref.self_ty().is_self();
a7813a04
XL
1043 // We can only be recurring in a default
1044 // method if we're being called literally
1045 // on the `Self` type.
1046 on_self && callee_id == method.def_id
1047 }
c1a9b12d 1048
a7813a04
XL
1049 // The `impl` is known, so we check that with a
1050 // special case:
1051 Ok(Some(traits::VtableImpl(vtable_impl))) => {
1052 let container = ty::ImplContainer(vtable_impl.impl_def_id);
1053 // It matches if it comes from the same impl,
1054 // and has the same method name.
476ff2be 1055 container == method.container && callee_item.name == method.name
a7813a04 1056 }
c1a9b12d 1057
a7813a04
XL
1058 // There's no way to know if this call is
1059 // recursive, so we assume it's not.
c30ab7b3 1060 _ => false,
a7813a04
XL
1061 }
1062 })
c1a9b12d
SL
1063 }
1064 }
c34b1796
AL
1065 }
1066 }
1067}
1068
1069declare_lint! {
1070 PLUGIN_AS_LIBRARY,
1071 Warn,
1072 "compiler plugin used as ordinary library in non-plugin crate"
1073}
1074
1075#[derive(Copy, Clone)]
1076pub struct PluginAsLibrary;
1077
1078impl LintPass for PluginAsLibrary {
1079 fn get_lints(&self) -> LintArray {
1080 lint_array![PLUGIN_AS_LIBRARY]
1081 }
b039eaaf 1082}
c34b1796 1083
476ff2be 1084impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PluginAsLibrary {
b039eaaf 1085 fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
c34b1796
AL
1086 if cx.sess().plugin_registrar_fn.get().is_some() {
1087 // We're compiling a plugin; it's fine to link other plugins.
1088 return;
1089 }
1090
1091 match it.node {
e9174d1e 1092 hir::ItemExternCrate(..) => (),
c34b1796
AL
1093 _ => return,
1094 };
1095
ea8adc8c
XL
1096 let def_id = cx.tcx.hir.local_def_id(it.id);
1097 let prfn = match cx.tcx.extern_mod_stmt_cnum(def_id) {
1098 Some(cnum) => cx.tcx.plugin_registrar_fn(cnum),
c34b1796
AL
1099 None => {
1100 // Probably means we aren't linking the crate for some reason.
1101 //
1102 // Not sure if / when this could happen.
1103 return;
1104 }
1105 };
1106
92a42be0 1107 if prfn.is_some() {
c30ab7b3
SL
1108 cx.span_lint(PLUGIN_AS_LIBRARY,
1109 it.span,
c34b1796
AL
1110 "compiler plugin used as an ordinary library");
1111 }
1112 }
1113}
1114
1115declare_lint! {
1116 PRIVATE_NO_MANGLE_FNS,
1117 Warn,
1118 "functions marked #[no_mangle] should be exported"
1119}
1120
1121declare_lint! {
1122 PRIVATE_NO_MANGLE_STATICS,
1123 Warn,
1124 "statics marked #[no_mangle] should be exported"
1125}
1126
1127declare_lint! {
1128 NO_MANGLE_CONST_ITEMS,
1129 Deny,
1130 "const items will not have their symbols exported"
1131}
1132
9cc50fc6
SL
1133declare_lint! {
1134 NO_MANGLE_GENERIC_ITEMS,
1135 Warn,
1136 "generic items must be mangled"
1137}
1138
c34b1796
AL
1139#[derive(Copy, Clone)]
1140pub struct InvalidNoMangleItems;
1141
1142impl LintPass for InvalidNoMangleItems {
1143 fn get_lints(&self) -> LintArray {
1144 lint_array!(PRIVATE_NO_MANGLE_FNS,
1145 PRIVATE_NO_MANGLE_STATICS,
9cc50fc6
SL
1146 NO_MANGLE_CONST_ITEMS,
1147 NO_MANGLE_GENERIC_ITEMS)
c34b1796 1148 }
b039eaaf 1149}
c34b1796 1150
476ff2be 1151impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidNoMangleItems {
b039eaaf 1152 fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
c34b1796 1153 match it.node {
9e0c209e 1154 hir::ItemFn(.., ref generics, _) => {
abe05a73
XL
1155 if let Some(no_mangle_attr) = attr::find_by_name(&it.attrs, "no_mangle") {
1156 if attr::contains_name(&it.attrs, "linkage") {
1157 return;
1158 }
9cc50fc6 1159 if !cx.access_levels.is_reachable(it.id) {
abe05a73
XL
1160 let msg = "function is marked #[no_mangle], but not exported";
1161 let mut err = cx.struct_span_lint(PRIVATE_NO_MANGLE_FNS, it.span, msg);
1162 let insertion_span = it.span.with_hi(it.span.lo());
1163 err.span_suggestion(insertion_span,
1164 "try making it public",
1165 "pub ".to_owned());
1166 err.emit();
9cc50fc6 1167 }
7cac9316 1168 if generics.is_type_parameterized() {
abe05a73
XL
1169 let mut err = cx.struct_span_lint(NO_MANGLE_GENERIC_ITEMS,
1170 it.span,
1171 "functions generic over \
1172 types must be mangled");
1173 err.span_suggestion_short(no_mangle_attr.span,
1174 "remove this attribute",
1175 "".to_owned());
1176 err.emit();
9cc50fc6 1177 }
c34b1796 1178 }
c30ab7b3 1179 }
e9174d1e 1180 hir::ItemStatic(..) => {
c34b1796 1181 if attr::contains_name(&it.attrs, "no_mangle") &&
c30ab7b3 1182 !cx.access_levels.is_reachable(it.id) {
abe05a73
XL
1183 let msg = "static is marked #[no_mangle], but not exported";
1184 let mut err = cx.struct_span_lint(PRIVATE_NO_MANGLE_STATICS, it.span, msg);
1185 let insertion_span = it.span.with_hi(it.span.lo());
1186 err.span_suggestion(insertion_span,
1187 "try making it public",
1188 "pub ".to_owned());
1189 err.emit();
c34b1796 1190 }
c30ab7b3 1191 }
e9174d1e 1192 hir::ItemConst(..) => {
c34b1796
AL
1193 if attr::contains_name(&it.attrs, "no_mangle") {
1194 // Const items do not refer to a particular location in memory, and therefore
1195 // don't have anything to attach a symbol to
abe05a73
XL
1196 let msg = "const items should never be #[no_mangle]";
1197 let mut err = cx.struct_span_lint(NO_MANGLE_CONST_ITEMS, it.span, msg);
1198 // `const` is 5 chars
1199 let const_span = it.span.with_hi(BytePos(it.span.lo().0 + 5));
1200 err.span_suggestion(const_span,
1201 "try a static value",
1202 "pub static".to_owned());
1203 err.emit();
c34b1796
AL
1204 }
1205 }
c30ab7b3 1206 _ => {}
c34b1796
AL
1207 }
1208 }
1209}
1210
bd371182
AL
1211#[derive(Clone, Copy)]
1212pub struct MutableTransmutes;
1213
1214declare_lint! {
1215 MUTABLE_TRANSMUTES,
1216 Deny,
1217 "mutating transmuted &mut T from &T may cause undefined behavior"
1218}
1219
1220impl LintPass for MutableTransmutes {
1221 fn get_lints(&self) -> LintArray {
1222 lint_array!(MUTABLE_TRANSMUTES)
1223 }
b039eaaf 1224}
bd371182 1225
476ff2be 1226impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MutableTransmutes {
b039eaaf 1227 fn check_expr(&mut self, cx: &LateContext, expr: &hir::Expr) {
7453a54e 1228 use syntax::abi::Abi::RustIntrinsic;
e9174d1e 1229
c30ab7b3 1230 let msg = "mutating transmuted &mut T from &T may cause undefined behavior, \
bd371182
AL
1231 consider instead using an UnsafeCell";
1232 match get_transmute_from_to(cx, expr) {
62682a34 1233 Some((&ty::TyRef(_, from_mt), &ty::TyRef(_, to_mt))) => {
c30ab7b3
SL
1234 if to_mt.mutbl == hir::Mutability::MutMutable &&
1235 from_mt.mutbl == hir::Mutability::MutImmutable {
bd371182
AL
1236 cx.span_lint(MUTABLE_TRANSMUTES, expr.span, msg);
1237 }
1238 }
c30ab7b3 1239 _ => (),
bd371182
AL
1240 }
1241
c30ab7b3
SL
1242 fn get_transmute_from_to<'a, 'tcx>
1243 (cx: &LateContext<'a, 'tcx>,
1244 expr: &hir::Expr)
1245 -> Option<(&'tcx ty::TypeVariants<'tcx>, &'tcx ty::TypeVariants<'tcx>)> {
476ff2be 1246 let def = if let hir::ExprPath(ref qpath) = expr.node {
3b2f2976 1247 cx.tables.qpath_def(qpath, expr.hir_id)
476ff2be
SL
1248 } else {
1249 return None;
1250 };
1251 if let Def::Fn(did) = def {
bd371182
AL
1252 if !def_id_is_transmute(cx, did) {
1253 return None;
1254 }
3b2f2976 1255 let sig = cx.tables.node_id_to_type(expr.hir_id).fn_sig(cx.tcx);
041b39d2
XL
1256 let from = sig.inputs().skip_binder()[0];
1257 let to = *sig.output().skip_binder();
1258 return Some((&from.sty, &to.sty));
bd371182
AL
1259 }
1260 None
1261 }
1262
b039eaaf 1263 fn def_id_is_transmute(cx: &LateContext, def_id: DefId) -> bool {
041b39d2 1264 cx.tcx.fn_sig(def_id).abi() == RustIntrinsic &&
476ff2be 1265 cx.tcx.item_name(def_id) == "transmute"
bd371182
AL
1266 }
1267 }
1268}
1269
c34b1796
AL
1270/// Forbids using the `#[feature(...)]` attribute
1271#[derive(Copy, Clone)]
1272pub struct UnstableFeatures;
1273
1274declare_lint! {
1275 UNSTABLE_FEATURES,
1276 Allow,
62682a34 1277 "enabling unstable features (deprecated. do not use)"
c34b1796
AL
1278}
1279
1280impl LintPass for UnstableFeatures {
1281 fn get_lints(&self) -> LintArray {
1282 lint_array!(UNSTABLE_FEATURES)
1283 }
b039eaaf
SL
1284}
1285
476ff2be 1286impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnstableFeatures {
b039eaaf 1287 fn check_attribute(&mut self, ctx: &LateContext, attr: &ast::Attribute) {
cc61c64b
XL
1288 if attr.check_name("feature") {
1289 if let Some(items) = attr.meta_item_list() {
62682a34 1290 for item in items {
5bcae85e 1291 ctx.span_lint(UNSTABLE_FEATURES, item.span(), "unstable feature");
62682a34
SL
1292 }
1293 }
c34b1796
AL
1294 }
1295 }
1296}
bd371182 1297
9e0c209e
SL
1298/// Lint for unions that contain fields with possibly non-trivial destructors.
1299pub struct UnionsWithDropFields;
bd371182
AL
1300
1301declare_lint! {
9e0c209e 1302 UNIONS_WITH_DROP_FIELDS,
bd371182 1303 Warn,
9e0c209e 1304 "use of unions that contain fields with possibly non-trivial drop code"
bd371182
AL
1305}
1306
9e0c209e 1307impl LintPass for UnionsWithDropFields {
bd371182 1308 fn get_lints(&self) -> LintArray {
9e0c209e 1309 lint_array!(UNIONS_WITH_DROP_FIELDS)
bd371182 1310 }
b039eaaf 1311}
e9174d1e 1312
476ff2be 1313impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnionsWithDropFields {
9e0c209e
SL
1314 fn check_item(&mut self, ctx: &LateContext, item: &hir::Item) {
1315 if let hir::ItemUnion(ref vdata, _) = item.node {
9e0c209e 1316 for field in vdata.fields() {
7cac9316 1317 let field_ty = ctx.tcx.type_of(ctx.tcx.hir.local_def_id(field.id));
041b39d2 1318 if field_ty.needs_drop(ctx.tcx, ctx.param_env) {
9e0c209e
SL
1319 ctx.span_lint(UNIONS_WITH_DROP_FIELDS,
1320 field.span,
1321 "union contains a field with possibly non-trivial drop code, \
1322 drop code of union fields is ignored when dropping the union");
1323 return;
bd371182 1324 }
bd371182 1325 }
9e0c209e 1326 }
bd371182
AL
1327 }
1328}
abe05a73
XL
1329
1330/// Lint for items marked `pub` that aren't reachable from other crates
1331pub struct UnreachablePub;
1332
1333declare_lint! {
1334 UNREACHABLE_PUB,
1335 Allow,
1336 "`pub` items not reachable from crate root"
1337}
1338
1339impl LintPass for UnreachablePub {
1340 fn get_lints(&self) -> LintArray {
1341 lint_array!(UNREACHABLE_PUB)
1342 }
1343}
1344
1345impl UnreachablePub {
1346 fn perform_lint(&self, cx: &LateContext, what: &str, id: ast::NodeId,
1347 vis: &hir::Visibility, span: Span, exportable: bool) {
1348 if !cx.access_levels.is_reachable(id) && *vis == hir::Visibility::Public {
1349 let def_span = cx.tcx.sess.codemap().def_span(span);
1350 let mut err = cx.struct_span_lint(UNREACHABLE_PUB, def_span,
1351 &format!("unreachable `pub` {}", what));
1352 // visibility is token at start of declaration (can be macro
1353 // variable rather than literal `pub`)
1354 let pub_span = cx.tcx.sess.codemap().span_until_char(def_span, ' ');
1355 let replacement = if cx.tcx.sess.features.borrow().crate_visibility_modifier {
1356 "crate"
1357 } else {
1358 "pub(crate)"
1359 }.to_owned();
1360 err.span_suggestion(pub_span, "consider restricting its visibility", replacement);
1361 if exportable {
1362 err.help("or consider exporting it for use by other crates");
1363 }
1364 err.emit();
1365 }
1366 }
1367}
1368
1369impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnreachablePub {
1370 fn check_item(&mut self, cx: &LateContext, item: &hir::Item) {
1371 self.perform_lint(cx, "item", item.id, &item.vis, item.span, true);
1372 }
1373
1374 fn check_foreign_item(&mut self, cx: &LateContext, foreign_item: &hir::ForeignItem) {
1375 self.perform_lint(cx, "item", foreign_item.id, &foreign_item.vis, foreign_item.span, true);
1376 }
1377
1378 fn check_struct_field(&mut self, cx: &LateContext, field: &hir::StructField) {
1379 self.perform_lint(cx, "field", field.id, &field.vis, field.span, false);
1380 }
1381
1382 fn check_impl_item(&mut self, cx: &LateContext, impl_item: &hir::ImplItem) {
1383 self.perform_lint(cx, "item", impl_item.id, &impl_item.vis, impl_item.span, false);
1384 }
1385}