]> git.proxmox.com Git - rustc.git/blame - src/librustc_lint/builtin.rs
New upstream version 1.28.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};
0531ce1d 36use rustc::traits;
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;
94b46f34 46use syntax::edition::Edition;
c30ab7b3 47use syntax::feature_gate::{AttributeGate, AttributeType, Stability, deprecated_attributes};
abe05a73 48use syntax_pos::{BytePos, Span, SyntaxContext};
7cac9316 49use syntax::symbol::keywords;
83c7162d 50use syntax::errors::{Applicability, DiagnosticBuilder};
e9174d1e 51
54a0048b
SL
52use rustc::hir::{self, PatKind};
53use rustc::hir::intravisit::FnKind;
e9174d1e 54
b039eaaf 55use bad_style::{MethodLateContext, method_context};
c34b1796 56
b039eaaf
SL
57// hardwired lints from librustc
58pub use lint::builtin::*;
c34b1796 59
b039eaaf
SL
60declare_lint! {
61 WHILE_TRUE,
62 Warn,
63 "suggest using `loop { }` instead of `while true { }`"
64}
c34b1796 65
b039eaaf
SL
66#[derive(Copy, Clone)]
67pub struct WhileTrue;
68
69impl LintPass for WhileTrue {
70 fn get_lints(&self) -> LintArray {
71 lint_array!(WHILE_TRUE)
c34b1796 72 }
b039eaaf 73}
c34b1796 74
476ff2be 75impl<'a, 'tcx> LateLintPass<'a, 'tcx> for WhileTrue {
b039eaaf 76 fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
9e0c209e 77 if let hir::ExprWhile(ref cond, ..) = e.node {
b039eaaf 78 if let hir::ExprLit(ref lit) = cond.node {
7453a54e 79 if let ast::LitKind::Bool(true) = lit.node {
ea8adc8c
XL
80 if lit.span.ctxt() == SyntaxContext::empty() {
81 let msg = "denote infinite loops with `loop { ... }`";
ea8adc8c 82 let condition_span = cx.tcx.sess.codemap().def_span(e.span);
ff7c6d11
XL
83 let mut err = cx.struct_span_lint(WHILE_TRUE, condition_span, msg);
84 err.span_suggestion_short(condition_span, "use `loop`", "loop".to_owned());
ea8adc8c
XL
85 err.emit();
86 }
b039eaaf 87 }
c34b1796
AL
88 }
89 }
90 }
91}
92
93declare_lint! {
b039eaaf
SL
94 BOX_POINTERS,
95 Allow,
96 "use of owned (Box type) heap memory"
c34b1796
AL
97}
98
99#[derive(Copy, Clone)]
b039eaaf
SL
100pub struct BoxPointers;
101
102impl BoxPointers {
476ff2be 103 fn check_heap_type<'a, 'tcx>(&self, cx: &LateContext, span: Span, ty: Ty) {
b039eaaf 104 for leaf_ty in ty.walk() {
32a655c1 105 if leaf_ty.is_box() {
b039eaaf
SL
106 let m = format!("type uses owned (Box type) pointers: {}", ty);
107 cx.span_lint(BOX_POINTERS, span, &m);
c34b1796
AL
108 }
109 }
110 }
111}
112
b039eaaf 113impl LintPass for BoxPointers {
c34b1796 114 fn get_lints(&self) -> LintArray {
b039eaaf 115 lint_array!(BOX_POINTERS)
c34b1796 116 }
b039eaaf 117}
c34b1796 118
476ff2be 119impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BoxPointers {
b039eaaf 120 fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
c34b1796 121 match it.node {
b039eaaf
SL
122 hir::ItemFn(..) |
123 hir::ItemTy(..) |
124 hir::ItemEnum(..) |
9e0c209e 125 hir::ItemStruct(..) |
c30ab7b3 126 hir::ItemUnion(..) => {
32a655c1 127 let def_id = cx.tcx.hir.local_def_id(it.id);
7cac9316 128 self.check_heap_type(cx, it.span, cx.tcx.type_of(def_id))
c30ab7b3 129 }
476ff2be 130 _ => ()
d9579d0f 131 }
d9579d0f 132
b039eaaf
SL
133 // If it's a struct, we also have to check the fields' types
134 match it.node {
9e0c209e
SL
135 hir::ItemStruct(ref struct_def, _) |
136 hir::ItemUnion(ref struct_def, _) => {
b039eaaf 137 for struct_field in struct_def.fields() {
32a655c1 138 let def_id = cx.tcx.hir.local_def_id(struct_field.id);
476ff2be 139 self.check_heap_type(cx, struct_field.span,
7cac9316 140 cx.tcx.type_of(def_id));
b039eaaf 141 }
d9579d0f 142 }
c30ab7b3 143 _ => (),
d9579d0f
AL
144 }
145 }
146
b039eaaf 147 fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
3b2f2976 148 let ty = cx.tables.node_id_to_type(e.hir_id);
b039eaaf 149 self.check_heap_type(cx, e.span, ty);
c34b1796
AL
150 }
151}
152
c34b1796
AL
153declare_lint! {
154 NON_SHORTHAND_FIELD_PATTERNS,
155 Warn,
abe05a73 156 "using `Struct { x: x }` instead of `Struct { x }` in a pattern"
c34b1796
AL
157}
158
159#[derive(Copy, Clone)]
160pub struct NonShorthandFieldPatterns;
161
162impl LintPass for NonShorthandFieldPatterns {
163 fn get_lints(&self) -> LintArray {
164 lint_array!(NON_SHORTHAND_FIELD_PATTERNS)
165 }
b039eaaf 166}
c34b1796 167
476ff2be 168impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonShorthandFieldPatterns {
b039eaaf 169 fn check_pat(&mut self, cx: &LateContext, pat: &hir::Pat) {
83c7162d
XL
170 if let PatKind::Struct(ref qpath, ref field_pats, _) = pat.node {
171 let variant = cx.tables.pat_ty(pat).ty_adt_def()
172 .expect("struct pattern type is not an ADT")
173 .variant_of_def(cx.tables.qpath_def(qpath, pat.hir_id));
3157f602 174 for fieldpat in field_pats {
c34b1796 175 if fieldpat.node.is_shorthand {
3157f602 176 continue;
b039eaaf 177 }
83c7162d
XL
178 if fieldpat.span.ctxt().outer().expn_info().is_some() {
179 // Don't lint if this is a macro expansion: macro authors
180 // shouldn't have to worry about this kind of style issue
181 // (Issue #49588)
182 continue;
183 }
184 if let PatKind::Binding(_, _, name, None) = fieldpat.node.pat.node {
185 let binding_ident = ast::Ident::new(name.node, name.span);
186 if cx.tcx.find_field_index(binding_ident, &variant) ==
187 Some(cx.tcx.field_index(fieldpat.node.id, cx.tables)) {
abe05a73 188 let mut err = cx.struct_span_lint(NON_SHORTHAND_FIELD_PATTERNS,
c30ab7b3 189 fieldpat.span,
abe05a73 190 &format!("the `{}:` in this pattern is redundant",
83c7162d 191 name.node));
abe05a73
XL
192 let subspan = cx.tcx.sess.codemap().span_through_char(fieldpat.span, ':');
193 err.span_suggestion_short(subspan,
194 "remove this",
83c7162d 195 format!("{}", name.node));
abe05a73 196 err.emit();
c34b1796
AL
197 }
198 }
199 }
200 }
201 }
202}
203
c34b1796
AL
204declare_lint! {
205 UNSAFE_CODE,
206 Allow,
207 "usage of `unsafe` code"
208}
209
210#[derive(Copy, Clone)]
211pub struct UnsafeCode;
212
213impl LintPass for UnsafeCode {
214 fn get_lints(&self) -> LintArray {
215 lint_array!(UNSAFE_CODE)
216 }
b039eaaf 217}
c34b1796 218
3b2f2976
XL
219impl UnsafeCode {
220 fn report_unsafe(&self, cx: &LateContext, span: Span, desc: &'static str) {
221 // This comes from a macro that has #[allow_internal_unsafe].
222 if span.allows_unsafe() {
223 return;
224 }
225
226 cx.span_lint(UNSAFE_CODE, span, desc);
227 }
228}
229
476ff2be 230impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnsafeCode {
b039eaaf 231 fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
94b46f34 232 if let hir::ExprBlock(ref blk, _) = e.node {
c34b1796 233 // Don't warn about generated blocks, that'll just pollute the output.
e9174d1e 234 if blk.rules == hir::UnsafeBlock(hir::UserProvided) {
3b2f2976 235 self.report_unsafe(cx, blk.span, "usage of an `unsafe` block");
c34b1796
AL
236 }
237 }
238 }
239
b039eaaf 240 fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
c34b1796 241 match it.node {
abe05a73 242 hir::ItemTrait(_, hir::Unsafety::Unsafe, ..) => {
3b2f2976 243 self.report_unsafe(cx, it.span, "declaration of an `unsafe` trait")
c30ab7b3 244 }
c34b1796 245
c30ab7b3 246 hir::ItemImpl(hir::Unsafety::Unsafe, ..) => {
3b2f2976 247 self.report_unsafe(cx, it.span, "implementation of an `unsafe` trait")
c30ab7b3 248 }
c34b1796
AL
249
250 _ => return,
251 }
252 }
253
c30ab7b3
SL
254 fn check_fn(&mut self,
255 cx: &LateContext,
476ff2be 256 fk: FnKind<'tcx>,
c30ab7b3 257 _: &hir::FnDecl,
32a655c1 258 _: &hir::Body,
c30ab7b3
SL
259 span: Span,
260 _: ast::NodeId) {
c34b1796 261 match fk {
c30ab7b3 262 FnKind::ItemFn(_, _, hir::Unsafety::Unsafe, ..) => {
3b2f2976 263 self.report_unsafe(cx, span, "declaration of an `unsafe` function")
c30ab7b3 264 }
c34b1796 265
9e0c209e 266 FnKind::Method(_, sig, ..) => {
e9174d1e 267 if sig.unsafety == hir::Unsafety::Unsafe {
3b2f2976 268 self.report_unsafe(cx, span, "implementation of an `unsafe` method")
c34b1796 269 }
c30ab7b3 270 }
c34b1796
AL
271
272 _ => (),
273 }
274 }
275
32a655c1
SL
276 fn check_trait_item(&mut self, cx: &LateContext, item: &hir::TraitItem) {
277 if let hir::TraitItemKind::Method(ref sig, hir::TraitMethod::Required(_)) = item.node {
e9174d1e 278 if sig.unsafety == hir::Unsafety::Unsafe {
3b2f2976 279 self.report_unsafe(cx, item.span, "declaration of an `unsafe` method")
c34b1796
AL
280 }
281 }
282 }
283}
284
c34b1796 285declare_lint! {
94b46f34 286 pub MISSING_DOCS,
c34b1796
AL
287 Allow,
288 "detects missing documentation for public members"
289}
290
291pub struct MissingDoc {
c34b1796
AL
292 /// Stack of whether #[doc(hidden)] is set
293 /// at each level which has lint attributes.
294 doc_hidden_stack: Vec<bool>,
295
296 /// Private traits or trait items that leaked through. Don't check their methods.
297 private_traits: HashSet<ast::NodeId>,
298}
299
300impl MissingDoc {
301 pub fn new() -> MissingDoc {
302 MissingDoc {
c30ab7b3 303 doc_hidden_stack: vec![false],
c34b1796
AL
304 private_traits: HashSet::new(),
305 }
306 }
307
308 fn doc_hidden(&self) -> bool {
309 *self.doc_hidden_stack.last().expect("empty doc_hidden_stack")
310 }
311
312 fn check_missing_docs_attrs(&self,
c30ab7b3
SL
313 cx: &LateContext,
314 id: Option<ast::NodeId>,
315 attrs: &[ast::Attribute],
316 sp: Span,
317 desc: &'static str) {
c34b1796
AL
318 // If we're building a test harness, then warning about
319 // documentation is probably not really relevant right now.
320 if cx.sess().opts.test {
321 return;
322 }
323
324 // `#[doc(hidden)]` disables missing_docs check.
325 if self.doc_hidden() {
326 return;
327 }
328
329 // Only check publicly-visible items, using the result from the privacy pass.
330 // It's an option so the crate root can also use this function (it doesn't
331 // have a NodeId).
92a42be0
SL
332 if let Some(id) = id {
333 if !cx.access_levels.is_exported(id) {
c34b1796
AL
334 return;
335 }
336 }
337
ff7c6d11
XL
338 fn has_doc(attr: &ast::Attribute) -> bool {
339 if !attr.check_name("doc") {
340 return false;
341 }
342
343 if attr.is_value_str() {
344 return true;
345 }
346
347 if let Some(list) = attr.meta_item_list() {
348 for meta in list {
349 if meta.check_name("include") {
350 return true;
351 }
352 }
353 }
354
355 false
356 }
357
358 let has_doc = attrs.iter().any(|a| has_doc(a));
c34b1796 359 if !has_doc {
c30ab7b3 360 cx.span_lint(MISSING_DOCS,
ff7c6d11 361 cx.tcx.sess.codemap().def_span(sp),
c34b1796
AL
362 &format!("missing documentation for {}", desc));
363 }
364 }
365}
366
367impl LintPass for MissingDoc {
368 fn get_lints(&self) -> LintArray {
369 lint_array!(MISSING_DOCS)
370 }
b039eaaf 371}
c34b1796 372
476ff2be 373impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDoc {
b039eaaf 374 fn enter_lint_attrs(&mut self, _: &LateContext, attrs: &[ast::Attribute]) {
c30ab7b3
SL
375 let doc_hidden = self.doc_hidden() ||
376 attrs.iter().any(|attr| {
377 attr.check_name("doc") &&
378 match attr.meta_item_list() {
c34b1796 379 None => false,
cc61c64b 380 Some(l) => attr::list_contains_name(&l, "hidden"),
c34b1796
AL
381 }
382 });
383 self.doc_hidden_stack.push(doc_hidden);
384 }
385
476ff2be 386 fn exit_lint_attrs(&mut self, _: &LateContext, _attrs: &[ast::Attribute]) {
c34b1796
AL
387 self.doc_hidden_stack.pop().expect("empty doc_hidden_stack");
388 }
389
b039eaaf 390 fn check_crate(&mut self, cx: &LateContext, krate: &hir::Crate) {
c34b1796
AL
391 self.check_missing_docs_attrs(cx, None, &krate.attrs, krate.span, "crate");
392 }
393
b039eaaf 394 fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
c34b1796 395 let desc = match it.node {
e9174d1e
SL
396 hir::ItemFn(..) => "a function",
397 hir::ItemMod(..) => "a module",
398 hir::ItemEnum(..) => "an enum",
399 hir::ItemStruct(..) => "a struct",
9e0c209e 400 hir::ItemUnion(..) => "a union",
32a655c1 401 hir::ItemTrait(.., ref trait_item_refs) => {
c34b1796 402 // Issue #11592, traits are always considered exported, even when private.
e9174d1e 403 if it.vis == hir::Visibility::Inherited {
c34b1796 404 self.private_traits.insert(it.id);
32a655c1
SL
405 for trait_item_ref in trait_item_refs {
406 self.private_traits.insert(trait_item_ref.id.node_id);
c34b1796 407 }
c30ab7b3 408 return;
c34b1796
AL
409 }
410 "a trait"
c30ab7b3 411 }
e9174d1e 412 hir::ItemTy(..) => "a type alias",
476ff2be 413 hir::ItemImpl(.., Some(ref trait_ref), _, ref impl_item_refs) => {
c34b1796
AL
414 // If the trait is private, add the impl items to private_traits so they don't get
415 // reported for missing docs.
476ff2be 416 let real_trait = trait_ref.path.def.def_id();
32a655c1
SL
417 if let Some(node_id) = cx.tcx.hir.as_local_node_id(real_trait) {
418 match cx.tcx.hir.find(node_id) {
c30ab7b3
SL
419 Some(hir_map::NodeItem(item)) => {
420 if item.vis == hir::Visibility::Inherited {
476ff2be
SL
421 for impl_item_ref in impl_item_refs {
422 self.private_traits.insert(impl_item_ref.id.node_id);
c30ab7b3 423 }
b039eaaf 424 }
c30ab7b3
SL
425 }
426 _ => {}
b039eaaf 427 }
c34b1796 428 }
c30ab7b3
SL
429 return;
430 }
e9174d1e
SL
431 hir::ItemConst(..) => "a constant",
432 hir::ItemStatic(..) => "a static",
c30ab7b3 433 _ => return,
c34b1796
AL
434 };
435
436 self.check_missing_docs_attrs(cx, Some(it.id), &it.attrs, it.span, desc);
437 }
438
b039eaaf 439 fn check_trait_item(&mut self, cx: &LateContext, trait_item: &hir::TraitItem) {
c30ab7b3
SL
440 if self.private_traits.contains(&trait_item.id) {
441 return;
442 }
c34b1796
AL
443
444 let desc = match trait_item.node {
32a655c1
SL
445 hir::TraitItemKind::Const(..) => "an associated constant",
446 hir::TraitItemKind::Method(..) => "a trait method",
447 hir::TraitItemKind::Type(..) => "an associated type",
c34b1796
AL
448 };
449
c30ab7b3
SL
450 self.check_missing_docs_attrs(cx,
451 Some(trait_item.id),
c34b1796 452 &trait_item.attrs,
c30ab7b3
SL
453 trait_item.span,
454 desc);
c34b1796
AL
455 }
456
b039eaaf 457 fn check_impl_item(&mut self, cx: &LateContext, impl_item: &hir::ImplItem) {
c34b1796 458 // If the method is an impl for a trait, don't doc.
7cac9316 459 if method_context(cx, impl_item.id) == MethodLateContext::TraitImpl {
c34b1796
AL
460 return;
461 }
462
463 let desc = match impl_item.node {
92a42be0
SL
464 hir::ImplItemKind::Const(..) => "an associated constant",
465 hir::ImplItemKind::Method(..) => "a method",
466 hir::ImplItemKind::Type(_) => "an associated type",
c34b1796 467 };
c30ab7b3
SL
468 self.check_missing_docs_attrs(cx,
469 Some(impl_item.id),
c34b1796 470 &impl_item.attrs,
c30ab7b3
SL
471 impl_item.span,
472 desc);
c34b1796
AL
473 }
474
b039eaaf 475 fn check_struct_field(&mut self, cx: &LateContext, sf: &hir::StructField) {
54a0048b 476 if !sf.is_positional() {
7cac9316
XL
477 self.check_missing_docs_attrs(cx,
478 Some(sf.id),
479 &sf.attrs,
480 sf.span,
481 "a struct field")
c34b1796
AL
482 }
483 }
484
b039eaaf 485 fn check_variant(&mut self, cx: &LateContext, v: &hir::Variant, _: &hir::Generics) {
c30ab7b3
SL
486 self.check_missing_docs_attrs(cx,
487 Some(v.node.data.id()),
488 &v.node.attrs,
489 v.span,
490 "a variant");
c34b1796
AL
491 }
492}
493
494declare_lint! {
495 pub MISSING_COPY_IMPLEMENTATIONS,
496 Allow,
497 "detects potentially-forgotten implementations of `Copy`"
498}
499
500#[derive(Copy, Clone)]
501pub struct MissingCopyImplementations;
502
503impl LintPass for MissingCopyImplementations {
504 fn get_lints(&self) -> LintArray {
505 lint_array!(MISSING_COPY_IMPLEMENTATIONS)
506 }
b039eaaf 507}
c34b1796 508
476ff2be 509impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingCopyImplementations {
b039eaaf 510 fn check_item(&mut self, cx: &LateContext, item: &hir::Item) {
92a42be0 511 if !cx.access_levels.is_reachable(item.id) {
c34b1796
AL
512 return;
513 }
e9174d1e
SL
514 let (def, ty) = match item.node {
515 hir::ItemStruct(_, ref ast_generics) => {
ff7c6d11 516 if !ast_generics.params.is_empty() {
c34b1796
AL
517 return;
518 }
7cac9316 519 let def = cx.tcx.adt_def(cx.tcx.hir.local_def_id(item.id));
c30ab7b3 520 (def, cx.tcx.mk_adt(def, cx.tcx.intern_substs(&[])))
9e0c209e
SL
521 }
522 hir::ItemUnion(_, ref ast_generics) => {
ff7c6d11 523 if !ast_generics.params.is_empty() {
9e0c209e
SL
524 return;
525 }
7cac9316 526 let def = cx.tcx.adt_def(cx.tcx.hir.local_def_id(item.id));
c30ab7b3 527 (def, cx.tcx.mk_adt(def, cx.tcx.intern_substs(&[])))
c34b1796 528 }
e9174d1e 529 hir::ItemEnum(_, ref ast_generics) => {
ff7c6d11 530 if !ast_generics.params.is_empty() {
c34b1796
AL
531 return;
532 }
7cac9316 533 let def = cx.tcx.adt_def(cx.tcx.hir.local_def_id(item.id));
c30ab7b3 534 (def, cx.tcx.mk_adt(def, cx.tcx.intern_substs(&[])))
c34b1796
AL
535 }
536 _ => return,
537 };
8bb4bdeb 538 if def.has_dtor(cx.tcx) {
c30ab7b3
SL
539 return;
540 }
0531ce1d 541 let param_env = ty::ParamEnv::empty();
7cac9316 542 if !ty.moves_by_default(cx.tcx, param_env, item.span) {
c34b1796
AL
543 return;
544 }
94b46f34 545 if param_env.can_type_implement_copy(cx.tcx, ty).is_ok() {
c34b1796
AL
546 cx.span_lint(MISSING_COPY_IMPLEMENTATIONS,
547 item.span,
548 "type could implement `Copy`; consider adding `impl \
549 Copy`")
550 }
551 }
552}
553
554declare_lint! {
555 MISSING_DEBUG_IMPLEMENTATIONS,
556 Allow,
557 "detects missing implementations of fmt::Debug"
558}
559
560pub struct MissingDebugImplementations {
561 impling_types: Option<NodeSet>,
562}
563
564impl MissingDebugImplementations {
565 pub fn new() -> MissingDebugImplementations {
c30ab7b3 566 MissingDebugImplementations { impling_types: None }
c34b1796
AL
567 }
568}
569
570impl LintPass for MissingDebugImplementations {
571 fn get_lints(&self) -> LintArray {
572 lint_array!(MISSING_DEBUG_IMPLEMENTATIONS)
573 }
b039eaaf 574}
c34b1796 575
476ff2be 576impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDebugImplementations {
b039eaaf 577 fn check_item(&mut self, cx: &LateContext, item: &hir::Item) {
92a42be0 578 if !cx.access_levels.is_reachable(item.id) {
c34b1796
AL
579 return;
580 }
581
582 match item.node {
c30ab7b3
SL
583 hir::ItemStruct(..) |
584 hir::ItemUnion(..) |
585 hir::ItemEnum(..) => {}
c34b1796
AL
586 _ => return,
587 }
588
ea8adc8c 589 let debug = match cx.tcx.lang_items().debug_trait() {
c34b1796
AL
590 Some(debug) => debug,
591 None => return,
592 };
593
594 if self.impling_types.is_none() {
d9579d0f 595 let mut impls = NodeSet();
041b39d2 596 cx.tcx.for_each_impl(debug, |d| {
7cac9316 597 if let Some(ty_def) = cx.tcx.type_of(d).ty_to_def_id() {
32a655c1 598 if let Some(node_id) = cx.tcx.hir.as_local_node_id(ty_def) {
476ff2be 599 impls.insert(node_id);
d9579d0f 600 }
c34b1796 601 }
d9579d0f
AL
602 });
603
c34b1796
AL
604 self.impling_types = Some(impls);
605 debug!("{:?}", self.impling_types);
606 }
607
608 if !self.impling_types.as_ref().unwrap().contains(&item.id) {
609 cx.span_lint(MISSING_DEBUG_IMPLEMENTATIONS,
610 item.span,
611 "type does not implement `fmt::Debug`; consider adding #[derive(Debug)] \
612 or a manual implementation")
613 }
614 }
615}
616
7cac9316
XL
617declare_lint! {
618 pub ANONYMOUS_PARAMETERS,
619 Allow,
94b46f34
XL
620 "detects anonymous parameters",
621 Edition::Edition2018 => Warn,
7cac9316
XL
622}
623
624/// Checks for use of anonymous parameters (RFC 1685)
625#[derive(Clone)]
626pub struct AnonymousParameters;
627
628impl LintPass for AnonymousParameters {
629 fn get_lints(&self) -> LintArray {
630 lint_array!(ANONYMOUS_PARAMETERS)
631 }
632}
633
634impl EarlyLintPass for AnonymousParameters {
635 fn check_trait_item(&mut self, cx: &EarlyContext, it: &ast::TraitItem) {
636 match it.node {
637 ast::TraitItemKind::Method(ref sig, _) => {
638 for arg in sig.decl.inputs.iter() {
639 match arg.pat.node {
640 ast::PatKind::Ident(_, ident, None) => {
83c7162d 641 if ident.name == keywords::Invalid.name() {
94b46f34
XL
642 let ty_snip = cx
643 .sess
644 .codemap()
645 .span_to_snippet(arg.ty.span);
646
647 let (ty_snip, appl) = if let Ok(snip) = ty_snip {
648 (snip, Applicability::MachineApplicable)
649 } else {
650 ("<type>".to_owned(), Applicability::HasPlaceholders)
651 };
652
653 cx.struct_span_lint(
654 ANONYMOUS_PARAMETERS,
655 arg.pat.span,
656 "anonymous parameters are deprecated and will be \
657 removed in the next edition."
658 ).span_suggestion_with_applicability(
659 arg.pat.span,
660 "Try naming the parameter or explicitly \
661 ignoring it",
662 format!("_: {}", ty_snip),
663 appl
664 ).emit();
7cac9316
XL
665 }
666 }
667 _ => (),
668 }
669 }
670 },
671 _ => (),
672 }
673 }
674}
675
94b46f34
XL
676/// Checks for incorrect use use of `repr` attributes.
677#[derive(Clone)]
678pub struct BadRepr;
679
680impl LintPass for BadRepr {
681 fn get_lints(&self) -> LintArray {
682 lint_array!()
683 }
684}
685
686impl EarlyLintPass for BadRepr {
687 fn check_attribute(&mut self, cx: &EarlyContext, attr: &ast::Attribute) {
688 if attr.name() == "repr" {
689 let list = attr.meta_item_list();
690
691 let repr_str = |lit: &str| { format!("#[repr({})]", lit) };
692
693 // Emit warnings with `repr` either has a literal assignment (`#[repr = "C"]`) or
694 // no hints (``#[repr]`)
695 let has_hints = list.as_ref().map(|ref list| !list.is_empty()).unwrap_or(false);
696 if !has_hints {
697 let mut suggested = false;
698 let mut warn = if let Some(ref lit) = attr.value_str() {
699 // avoid warning about empty `repr` on `#[repr = "foo"]`
700 let mut warn = cx.struct_span_lint(
701 BAD_REPR,
702 attr.span,
703 "`repr` attribute isn't configurable with a literal",
704 );
705 match format!("{}", lit).as_ref() {
706 | "C" | "packed" | "rust" | "transparent"
707 | "u8" | "u16" | "u32" | "u64" | "u128" | "usize"
708 | "i8" | "i16" | "i32" | "i64" | "i128" | "isize" => {
709 // if the literal could have been a valid `repr` arg,
710 // suggest the correct syntax
711 warn.span_suggestion(
712 attr.span,
713 "give `repr` a hint",
714 repr_str(&lit.as_str()),
715 );
716 suggested = true;
717 }
718 _ => { // the literal wasn't a valid `repr` arg
719 warn.span_label(attr.span, "needs a hint");
720 }
721 };
722 warn
723 } else {
724 let mut warn = cx.struct_span_lint(
725 BAD_REPR,
726 attr.span,
727 "`repr` attribute must have a hint",
728 );
729 warn.span_label(attr.span, "needs a hint");
730 warn
731 };
732 if !suggested {
733 warn.help(&format!(
734 "valid hints include `{}`, `{}`, `{}` and `{}`",
735 repr_str("C"),
736 repr_str("packed"),
737 repr_str("rust"),
738 repr_str("transparent"),
739 ));
740 warn.note("for more information, visit \
741 <https://doc.rust-lang.org/reference/type-layout.html>");
742 }
743 warn.emit();
744 }
745 }
746 }
747}
748
c30ab7b3
SL
749/// Checks for use of attributes which have been deprecated.
750#[derive(Clone)]
751pub struct DeprecatedAttr {
752 // This is not free to compute, so we want to keep it around, rather than
753 // compute it for every attribute.
754 depr_attrs: Vec<&'static (&'static str, AttributeType, AttributeGate)>,
755}
756
757impl DeprecatedAttr {
758 pub fn new() -> DeprecatedAttr {
759 DeprecatedAttr {
760 depr_attrs: deprecated_attributes(),
761 }
762 }
763}
764
765impl LintPass for DeprecatedAttr {
766 fn get_lints(&self) -> LintArray {
abe05a73 767 lint_array!()
c30ab7b3
SL
768 }
769}
770
771impl EarlyLintPass for DeprecatedAttr {
772 fn check_attribute(&mut self, cx: &EarlyContext, attr: &ast::Attribute) {
c30ab7b3 773 for &&(n, _, ref g) in &self.depr_attrs {
83c7162d 774 if attr.name() == n {
c30ab7b3
SL
775 if let &AttributeGate::Gated(Stability::Deprecated(link),
776 ref name,
777 ref reason,
778 _) = g {
ea8adc8c
XL
779 let msg = format!("use of deprecated attribute `{}`: {}. See {}",
780 name, reason, link);
781 let mut err = cx.struct_span_lint(DEPRECATED, attr.span, &msg);
782 err.span_suggestion_short(attr.span, "remove this attribute", "".to_owned());
783 err.emit();
c30ab7b3
SL
784 }
785 return;
786 }
787 }
788 }
789}
790
3b2f2976 791declare_lint! {
83c7162d 792 pub UNUSED_DOC_COMMENTS,
3b2f2976
XL
793 Warn,
794 "detects doc comments that aren't used by rustdoc"
795}
796
797#[derive(Copy, Clone)]
798pub struct UnusedDocComment;
799
800impl LintPass for UnusedDocComment {
801 fn get_lints(&self) -> LintArray {
83c7162d 802 lint_array![UNUSED_DOC_COMMENTS]
3b2f2976
XL
803 }
804}
805
806impl UnusedDocComment {
807 fn warn_if_doc<'a, 'tcx,
808 I: Iterator<Item=&'a ast::Attribute>,
809 C: LintContext<'tcx>>(&self, mut attrs: I, cx: &C) {
810 if let Some(attr) = attrs.find(|a| a.is_value_str() && a.check_name("doc")) {
83c7162d 811 cx.struct_span_lint(UNUSED_DOC_COMMENTS, attr.span, "doc comment not used by rustdoc")
3b2f2976
XL
812 .emit();
813 }
814 }
815}
816
817impl EarlyLintPass for UnusedDocComment {
818 fn check_local(&mut self, cx: &EarlyContext, decl: &ast::Local) {
819 self.warn_if_doc(decl.attrs.iter(), cx);
820 }
821
822 fn check_arm(&mut self, cx: &EarlyContext, arm: &ast::Arm) {
823 self.warn_if_doc(arm.attrs.iter(), cx);
824 }
825
826 fn check_expr(&mut self, cx: &EarlyContext, expr: &ast::Expr) {
827 self.warn_if_doc(expr.attrs.iter(), cx);
828 }
829}
830
c34b1796
AL
831declare_lint! {
832 pub UNCONDITIONAL_RECURSION,
833 Warn,
834 "functions that cannot return without calling themselves"
835}
836
837#[derive(Copy, Clone)]
838pub struct UnconditionalRecursion;
839
840
841impl LintPass for UnconditionalRecursion {
842 fn get_lints(&self) -> LintArray {
843 lint_array![UNCONDITIONAL_RECURSION]
844 }
b039eaaf 845}
c34b1796 846
476ff2be 847impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnconditionalRecursion {
c30ab7b3
SL
848 fn check_fn(&mut self,
849 cx: &LateContext,
850 fn_kind: FnKind,
851 _: &hir::FnDecl,
32a655c1 852 body: &hir::Body,
c30ab7b3
SL
853 sp: Span,
854 id: ast::NodeId) {
c1a9b12d 855 let method = match fn_kind {
e9174d1e
SL
856 FnKind::ItemFn(..) => None,
857 FnKind::Method(..) => {
32a655c1 858 Some(cx.tcx.associated_item(cx.tcx.hir.local_def_id(id)))
c1a9b12d 859 }
c34b1796 860 // closures can't recur, so they don't matter.
c30ab7b3 861 FnKind::Closure(_) => return,
c34b1796
AL
862 };
863
c34b1796
AL
864 // Walk through this function (say `f`) looking to see if
865 // every possible path references itself, i.e. the function is
866 // called recursively unconditionally. This is done by trying
867 // to find a path from the entry node to the exit node that
868 // *doesn't* call `f` by traversing from the entry while
869 // pretending that calls of `f` are sinks (i.e. ignoring any
870 // exit edges from them).
871 //
872 // NB. this has an edge case with non-returning statements,
873 // like `loop {}` or `panic!()`: control flow never reaches
874 // the exit node through these, so one can have a function
875 // that never actually calls itselfs but is still picked up by
876 // this lint:
877 //
878 // fn f(cond: bool) {
879 // if !cond { panic!() } // could come from `assert!(cond)`
880 // f(false)
881 // }
882 //
883 // In general, functions of that form may be able to call
884 // itself a finite number of times and then diverge. The lint
885 // considers this to be an error for two reasons, (a) it is
886 // easier to implement, and (b) it seems rare to actually want
887 // to have behaviour like the above, rather than
888 // e.g. accidentally recurring after an assert.
889
8bb4bdeb 890 let cfg = cfg::CFG::new(cx.tcx, &body);
c34b1796
AL
891
892 let mut work_queue = vec![cfg.entry];
893 let mut reached_exit_without_self_call = false;
894 let mut self_call_spans = vec![];
e9174d1e 895 let mut visited = HashSet::new();
c34b1796
AL
896
897 while let Some(idx) = work_queue.pop() {
898 if idx == cfg.exit {
899 // found a path!
900 reached_exit_without_self_call = true;
901 break;
902 }
903
904 let cfg_id = idx.node_id();
905 if visited.contains(&cfg_id) {
906 // already done
907 continue;
908 }
909 visited.insert(cfg_id);
910
c34b1796 911 // is this a recursive call?
ea8adc8c
XL
912 let local_id = cfg.graph.node_data(idx).id();
913 if local_id != hir::DUMMY_ITEM_LOCAL_ID {
914 let node_id = cx.tcx.hir.hir_to_node_id(hir::HirId {
915 owner: body.value.hir_id.owner,
916 local_id
917 });
918 let self_recursive = match method {
32a655c1
SL
919 Some(ref method) => expr_refers_to_this_method(cx, method, node_id),
920 None => expr_refers_to_this_fn(cx, id, node_id),
ea8adc8c
XL
921 };
922 if self_recursive {
923 self_call_spans.push(cx.tcx.hir.span(node_id));
924 // this is a self call, so we shouldn't explore past
925 // this node in the CFG.
926 continue;
c1a9b12d 927 }
c34b1796 928 }
ea8adc8c 929
c34b1796 930 // add the successors of this node to explore the graph further.
d9579d0f 931 for (_, edge) in cfg.graph.outgoing_edges(idx) {
c34b1796
AL
932 let target_idx = edge.target();
933 let target_cfg_id = target_idx.node_id();
934 if !visited.contains(&target_cfg_id) {
935 work_queue.push(target_idx)
936 }
d9579d0f 937 }
c34b1796
AL
938 }
939
940 // Check the number of self calls because a function that
941 // doesn't return (e.g. calls a `-> !` function or `loop { /*
942 // no break */ }`) shouldn't be linted unless it actually
943 // recurs.
9346a6ac 944 if !reached_exit_without_self_call && !self_call_spans.is_empty() {
ff7c6d11 945 let sp = cx.tcx.sess.codemap().def_span(sp);
c30ab7b3
SL
946 let mut db = cx.struct_span_lint(UNCONDITIONAL_RECURSION,
947 sp,
9cc50fc6 948 "function cannot return without recurring");
ff7c6d11 949 db.span_label(sp, "cannot return without recurring");
3b2f2976
XL
950 // offer some help to the programmer.
951 for call in &self_call_spans {
ff7c6d11 952 db.span_label(*call, "recursive call site");
c34b1796 953 }
ff7c6d11 954 db.help("a `loop` may express intention better if this is on purpose");
9cc50fc6 955 db.emit();
c34b1796
AL
956 }
957
958 // all done
959 return;
960
c1a9b12d
SL
961 // Functions for identifying if the given Expr NodeId `id`
962 // represents a call to the function `fn_id`/method `method`.
963
32a655c1
SL
964 fn expr_refers_to_this_fn(cx: &LateContext, fn_id: ast::NodeId, id: ast::NodeId) -> bool {
965 match cx.tcx.hir.get(id) {
e9174d1e 966 hir_map::NodeExpr(&hir::Expr { node: hir::ExprCall(ref callee, _), .. }) => {
476ff2be 967 let def = if let hir::ExprPath(ref qpath) = callee.node {
3b2f2976 968 cx.tables.qpath_def(qpath, callee.hir_id)
476ff2be
SL
969 } else {
970 return false;
971 };
ea8adc8c
XL
972 match def {
973 Def::Local(..) | Def::Upvar(..) => false,
974 _ => def.def_id() == cx.tcx.hir.local_def_id(fn_id)
975 }
c1a9b12d 976 }
c30ab7b3 977 _ => false,
c1a9b12d
SL
978 }
979 }
c34b1796 980
c1a9b12d 981 // Check if the expression `id` performs a call to `method`.
32a655c1
SL
982 fn expr_refers_to_this_method(cx: &LateContext,
983 method: &ty::AssociatedItem,
984 id: ast::NodeId)
985 -> bool {
c30ab7b3
SL
986 use rustc::ty::adjustment::*;
987
7cac9316
XL
988 // Ignore non-expressions.
989 let expr = if let hir_map::NodeExpr(e) = cx.tcx.hir.get(id) {
990 e
991 } else {
992 return false;
993 };
c34b1796 994
c1a9b12d 995 // Check for overloaded autoderef method calls.
7cac9316
XL
996 let mut source = cx.tables.expr_ty(expr);
997 for adjustment in cx.tables.expr_adjustments(expr) {
998 if let Adjust::Deref(Some(deref)) = adjustment.kind {
999 let (def_id, substs) = deref.method_call(cx.tcx, source);
041b39d2 1000 if method_call_refers_to_method(cx, method, def_id, substs, id) {
7cac9316 1001 return true;
c34b1796
AL
1002 }
1003 }
7cac9316
XL
1004 source = adjustment.target;
1005 }
1006
1007 // Check for method calls and overloaded operators.
1008 if cx.tables.is_method_call(expr) {
3b2f2976
XL
1009 let hir_id = cx.tcx.hir.definitions().node_to_hir_id(id);
1010 let def_id = cx.tables.type_dependent_defs()[hir_id].def_id();
1011 let substs = cx.tables.node_substs(hir_id);
041b39d2 1012 if method_call_refers_to_method(cx, method, def_id, substs, id) {
7cac9316
XL
1013 return true;
1014 }
c1a9b12d 1015 }
c34b1796 1016
c1a9b12d 1017 // Check for calls to methods via explicit paths (e.g. `T::method()`).
7cac9316
XL
1018 match expr.node {
1019 hir::ExprCall(ref callee, _) => {
476ff2be 1020 let def = if let hir::ExprPath(ref qpath) = callee.node {
3b2f2976 1021 cx.tables.qpath_def(qpath, callee.hir_id)
476ff2be
SL
1022 } else {
1023 return false;
1024 };
1025 match def {
1026 Def::Method(def_id) => {
3b2f2976 1027 let substs = cx.tables.node_substs(callee.hir_id);
041b39d2 1028 method_call_refers_to_method(cx, method, def_id, substs, id)
c1a9b12d 1029 }
c30ab7b3 1030 _ => false,
c1a9b12d
SL
1031 }
1032 }
c30ab7b3 1033 _ => false,
c1a9b12d
SL
1034 }
1035 }
1036
1037 // Check if the method call to the method with the ID `callee_id`
1038 // and instantiated with `callee_substs` refers to method `method`.
041b39d2 1039 fn method_call_refers_to_method<'a, 'tcx>(cx: &LateContext<'a, 'tcx>,
476ff2be 1040 method: &ty::AssociatedItem,
a7813a04
XL
1041 callee_id: DefId,
1042 callee_substs: &Substs<'tcx>,
c30ab7b3
SL
1043 expr_id: ast::NodeId)
1044 -> bool {
041b39d2 1045 let tcx = cx.tcx;
476ff2be 1046 let callee_item = tcx.associated_item(callee_id);
c1a9b12d 1047
476ff2be 1048 match callee_item.container {
c1a9b12d
SL
1049 // This is an inherent method, so the `def_id` refers
1050 // directly to the method definition.
c30ab7b3 1051 ty::ImplContainer(_) => callee_id == method.def_id,
c1a9b12d
SL
1052
1053 // A trait method, from any number of possible sources.
1054 // Attempt to select a concrete impl before checking.
1055 ty::TraitContainer(trait_def_id) => {
9e0c209e 1056 let trait_ref = ty::TraitRef::from_method(tcx, trait_def_id, callee_substs);
83c7162d 1057 let trait_ref = ty::Binder::bind(trait_ref);
32a655c1 1058 let span = tcx.hir.span(expr_id);
c1a9b12d
SL
1059 let obligation =
1060 traits::Obligation::new(traits::ObligationCause::misc(span, expr_id),
041b39d2 1061 cx.param_env,
c1a9b12d
SL
1062 trait_ref.to_poly_trait_predicate());
1063
041b39d2 1064 tcx.infer_ctxt().enter(|infcx| {
a7813a04
XL
1065 let mut selcx = traits::SelectionContext::new(&infcx);
1066 match selcx.select(&obligation) {
1067 // The method comes from a `T: Trait` bound.
1068 // If `T` is `Self`, then this call is inside
1069 // a default method definition.
1070 Ok(Some(traits::VtableParam(_))) => {
9e0c209e 1071 let on_self = trait_ref.self_ty().is_self();
a7813a04
XL
1072 // We can only be recurring in a default
1073 // method if we're being called literally
1074 // on the `Self` type.
1075 on_self && callee_id == method.def_id
1076 }
c1a9b12d 1077
a7813a04
XL
1078 // The `impl` is known, so we check that with a
1079 // special case:
1080 Ok(Some(traits::VtableImpl(vtable_impl))) => {
1081 let container = ty::ImplContainer(vtable_impl.impl_def_id);
1082 // It matches if it comes from the same impl,
1083 // and has the same method name.
476ff2be 1084 container == method.container && callee_item.name == method.name
a7813a04 1085 }
c1a9b12d 1086
a7813a04
XL
1087 // There's no way to know if this call is
1088 // recursive, so we assume it's not.
c30ab7b3 1089 _ => false,
a7813a04
XL
1090 }
1091 })
c1a9b12d
SL
1092 }
1093 }
c34b1796
AL
1094 }
1095 }
1096}
1097
1098declare_lint! {
1099 PLUGIN_AS_LIBRARY,
1100 Warn,
1101 "compiler plugin used as ordinary library in non-plugin crate"
1102}
1103
1104#[derive(Copy, Clone)]
1105pub struct PluginAsLibrary;
1106
1107impl LintPass for PluginAsLibrary {
1108 fn get_lints(&self) -> LintArray {
1109 lint_array![PLUGIN_AS_LIBRARY]
1110 }
b039eaaf 1111}
c34b1796 1112
476ff2be 1113impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PluginAsLibrary {
b039eaaf 1114 fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
c34b1796
AL
1115 if cx.sess().plugin_registrar_fn.get().is_some() {
1116 // We're compiling a plugin; it's fine to link other plugins.
1117 return;
1118 }
1119
1120 match it.node {
e9174d1e 1121 hir::ItemExternCrate(..) => (),
c34b1796
AL
1122 _ => return,
1123 };
1124
ea8adc8c
XL
1125 let def_id = cx.tcx.hir.local_def_id(it.id);
1126 let prfn = match cx.tcx.extern_mod_stmt_cnum(def_id) {
1127 Some(cnum) => cx.tcx.plugin_registrar_fn(cnum),
c34b1796
AL
1128 None => {
1129 // Probably means we aren't linking the crate for some reason.
1130 //
1131 // Not sure if / when this could happen.
1132 return;
1133 }
1134 };
1135
92a42be0 1136 if prfn.is_some() {
c30ab7b3
SL
1137 cx.span_lint(PLUGIN_AS_LIBRARY,
1138 it.span,
c34b1796
AL
1139 "compiler plugin used as an ordinary library");
1140 }
1141 }
1142}
1143
1144declare_lint! {
1145 PRIVATE_NO_MANGLE_FNS,
1146 Warn,
1147 "functions marked #[no_mangle] should be exported"
1148}
1149
1150declare_lint! {
1151 PRIVATE_NO_MANGLE_STATICS,
1152 Warn,
1153 "statics marked #[no_mangle] should be exported"
1154}
1155
1156declare_lint! {
1157 NO_MANGLE_CONST_ITEMS,
1158 Deny,
1159 "const items will not have their symbols exported"
1160}
1161
9cc50fc6
SL
1162declare_lint! {
1163 NO_MANGLE_GENERIC_ITEMS,
1164 Warn,
1165 "generic items must be mangled"
1166}
1167
c34b1796
AL
1168#[derive(Copy, Clone)]
1169pub struct InvalidNoMangleItems;
1170
1171impl LintPass for InvalidNoMangleItems {
1172 fn get_lints(&self) -> LintArray {
1173 lint_array!(PRIVATE_NO_MANGLE_FNS,
1174 PRIVATE_NO_MANGLE_STATICS,
9cc50fc6
SL
1175 NO_MANGLE_CONST_ITEMS,
1176 NO_MANGLE_GENERIC_ITEMS)
c34b1796 1177 }
b039eaaf 1178}
c34b1796 1179
476ff2be 1180impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidNoMangleItems {
b039eaaf 1181 fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
c34b1796 1182 match it.node {
9e0c209e 1183 hir::ItemFn(.., ref generics, _) => {
abe05a73
XL
1184 if let Some(no_mangle_attr) = attr::find_by_name(&it.attrs, "no_mangle") {
1185 if attr::contains_name(&it.attrs, "linkage") {
1186 return;
1187 }
9cc50fc6 1188 if !cx.access_levels.is_reachable(it.id) {
abe05a73
XL
1189 let msg = "function is marked #[no_mangle], but not exported";
1190 let mut err = cx.struct_span_lint(PRIVATE_NO_MANGLE_FNS, it.span, msg);
0531ce1d 1191 let insertion_span = it.span.shrink_to_lo();
2c00a5a8
XL
1192 if it.vis == hir::Visibility::Inherited {
1193 err.span_suggestion(insertion_span,
1194 "try making it public",
1195 "pub ".to_owned());
1196 }
abe05a73 1197 err.emit();
9cc50fc6 1198 }
7cac9316 1199 if generics.is_type_parameterized() {
abe05a73
XL
1200 let mut err = cx.struct_span_lint(NO_MANGLE_GENERIC_ITEMS,
1201 it.span,
1202 "functions generic over \
1203 types must be mangled");
1204 err.span_suggestion_short(no_mangle_attr.span,
1205 "remove this attribute",
1206 "".to_owned());
1207 err.emit();
9cc50fc6 1208 }
c34b1796 1209 }
c30ab7b3 1210 }
e9174d1e 1211 hir::ItemStatic(..) => {
c34b1796 1212 if attr::contains_name(&it.attrs, "no_mangle") &&
c30ab7b3 1213 !cx.access_levels.is_reachable(it.id) {
abe05a73
XL
1214 let msg = "static is marked #[no_mangle], but not exported";
1215 let mut err = cx.struct_span_lint(PRIVATE_NO_MANGLE_STATICS, it.span, msg);
0531ce1d 1216 let insertion_span = it.span.shrink_to_lo();
2c00a5a8
XL
1217 if it.vis == hir::Visibility::Inherited {
1218 err.span_suggestion(insertion_span,
1219 "try making it public",
1220 "pub ".to_owned());
1221 }
abe05a73 1222 err.emit();
c34b1796 1223 }
c30ab7b3 1224 }
e9174d1e 1225 hir::ItemConst(..) => {
c34b1796
AL
1226 if attr::contains_name(&it.attrs, "no_mangle") {
1227 // Const items do not refer to a particular location in memory, and therefore
1228 // don't have anything to attach a symbol to
abe05a73
XL
1229 let msg = "const items should never be #[no_mangle]";
1230 let mut err = cx.struct_span_lint(NO_MANGLE_CONST_ITEMS, it.span, msg);
2c00a5a8
XL
1231
1232 // account for "pub const" (#45562)
1233 let start = cx.tcx.sess.codemap().span_to_snippet(it.span)
1234 .map(|snippet| snippet.find("const").unwrap_or(0))
1235 .unwrap_or(0) as u32;
abe05a73 1236 // `const` is 5 chars
2c00a5a8 1237 let const_span = it.span.with_hi(BytePos(it.span.lo().0 + start + 5));
abe05a73
XL
1238 err.span_suggestion(const_span,
1239 "try a static value",
1240 "pub static".to_owned());
1241 err.emit();
c34b1796
AL
1242 }
1243 }
c30ab7b3 1244 _ => {}
c34b1796
AL
1245 }
1246 }
1247}
1248
bd371182
AL
1249#[derive(Clone, Copy)]
1250pub struct MutableTransmutes;
1251
1252declare_lint! {
1253 MUTABLE_TRANSMUTES,
1254 Deny,
1255 "mutating transmuted &mut T from &T may cause undefined behavior"
1256}
1257
1258impl LintPass for MutableTransmutes {
1259 fn get_lints(&self) -> LintArray {
1260 lint_array!(MUTABLE_TRANSMUTES)
1261 }
b039eaaf 1262}
bd371182 1263
476ff2be 1264impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MutableTransmutes {
b039eaaf 1265 fn check_expr(&mut self, cx: &LateContext, expr: &hir::Expr) {
83c7162d 1266 use rustc_target::spec::abi::Abi::RustIntrinsic;
e9174d1e 1267
c30ab7b3 1268 let msg = "mutating transmuted &mut T from &T may cause undefined behavior, \
bd371182
AL
1269 consider instead using an UnsafeCell";
1270 match get_transmute_from_to(cx, expr) {
94b46f34
XL
1271 Some((&ty::TyRef(_, _, from_mt), &ty::TyRef(_, _, to_mt))) => {
1272 if to_mt == hir::Mutability::MutMutable &&
1273 from_mt == hir::Mutability::MutImmutable {
bd371182
AL
1274 cx.span_lint(MUTABLE_TRANSMUTES, expr.span, msg);
1275 }
1276 }
c30ab7b3 1277 _ => (),
bd371182
AL
1278 }
1279
c30ab7b3
SL
1280 fn get_transmute_from_to<'a, 'tcx>
1281 (cx: &LateContext<'a, 'tcx>,
1282 expr: &hir::Expr)
1283 -> Option<(&'tcx ty::TypeVariants<'tcx>, &'tcx ty::TypeVariants<'tcx>)> {
476ff2be 1284 let def = if let hir::ExprPath(ref qpath) = expr.node {
3b2f2976 1285 cx.tables.qpath_def(qpath, expr.hir_id)
476ff2be
SL
1286 } else {
1287 return None;
1288 };
1289 if let Def::Fn(did) = def {
bd371182
AL
1290 if !def_id_is_transmute(cx, did) {
1291 return None;
1292 }
3b2f2976 1293 let sig = cx.tables.node_id_to_type(expr.hir_id).fn_sig(cx.tcx);
041b39d2
XL
1294 let from = sig.inputs().skip_binder()[0];
1295 let to = *sig.output().skip_binder();
1296 return Some((&from.sty, &to.sty));
bd371182
AL
1297 }
1298 None
1299 }
1300
b039eaaf 1301 fn def_id_is_transmute(cx: &LateContext, def_id: DefId) -> bool {
041b39d2 1302 cx.tcx.fn_sig(def_id).abi() == RustIntrinsic &&
476ff2be 1303 cx.tcx.item_name(def_id) == "transmute"
bd371182
AL
1304 }
1305 }
1306}
1307
c34b1796
AL
1308/// Forbids using the `#[feature(...)]` attribute
1309#[derive(Copy, Clone)]
1310pub struct UnstableFeatures;
1311
1312declare_lint! {
1313 UNSTABLE_FEATURES,
1314 Allow,
62682a34 1315 "enabling unstable features (deprecated. do not use)"
c34b1796
AL
1316}
1317
1318impl LintPass for UnstableFeatures {
1319 fn get_lints(&self) -> LintArray {
1320 lint_array!(UNSTABLE_FEATURES)
1321 }
b039eaaf
SL
1322}
1323
476ff2be 1324impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnstableFeatures {
b039eaaf 1325 fn check_attribute(&mut self, ctx: &LateContext, attr: &ast::Attribute) {
cc61c64b
XL
1326 if attr.check_name("feature") {
1327 if let Some(items) = attr.meta_item_list() {
62682a34 1328 for item in items {
5bcae85e 1329 ctx.span_lint(UNSTABLE_FEATURES, item.span(), "unstable feature");
62682a34
SL
1330 }
1331 }
c34b1796
AL
1332 }
1333 }
1334}
bd371182 1335
9e0c209e
SL
1336/// Lint for unions that contain fields with possibly non-trivial destructors.
1337pub struct UnionsWithDropFields;
bd371182
AL
1338
1339declare_lint! {
9e0c209e 1340 UNIONS_WITH_DROP_FIELDS,
bd371182 1341 Warn,
9e0c209e 1342 "use of unions that contain fields with possibly non-trivial drop code"
bd371182
AL
1343}
1344
9e0c209e 1345impl LintPass for UnionsWithDropFields {
bd371182 1346 fn get_lints(&self) -> LintArray {
9e0c209e 1347 lint_array!(UNIONS_WITH_DROP_FIELDS)
bd371182 1348 }
b039eaaf 1349}
e9174d1e 1350
476ff2be 1351impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnionsWithDropFields {
9e0c209e
SL
1352 fn check_item(&mut self, ctx: &LateContext, item: &hir::Item) {
1353 if let hir::ItemUnion(ref vdata, _) = item.node {
9e0c209e 1354 for field in vdata.fields() {
7cac9316 1355 let field_ty = ctx.tcx.type_of(ctx.tcx.hir.local_def_id(field.id));
041b39d2 1356 if field_ty.needs_drop(ctx.tcx, ctx.param_env) {
9e0c209e
SL
1357 ctx.span_lint(UNIONS_WITH_DROP_FIELDS,
1358 field.span,
1359 "union contains a field with possibly non-trivial drop code, \
1360 drop code of union fields is ignored when dropping the union");
1361 return;
bd371182 1362 }
bd371182 1363 }
9e0c209e 1364 }
bd371182
AL
1365 }
1366}
abe05a73
XL
1367
1368/// Lint for items marked `pub` that aren't reachable from other crates
1369pub struct UnreachablePub;
1370
1371declare_lint! {
0531ce1d 1372 pub UNREACHABLE_PUB,
abe05a73
XL
1373 Allow,
1374 "`pub` items not reachable from crate root"
1375}
1376
1377impl LintPass for UnreachablePub {
1378 fn get_lints(&self) -> LintArray {
1379 lint_array!(UNREACHABLE_PUB)
1380 }
1381}
1382
1383impl UnreachablePub {
1384 fn perform_lint(&self, cx: &LateContext, what: &str, id: ast::NodeId,
94b46f34
XL
1385 vis: &hir::Visibility, span: Span, exportable: bool,
1386 mut applicability: Applicability) {
abe05a73 1387 if !cx.access_levels.is_reachable(id) && *vis == hir::Visibility::Public {
94b46f34
XL
1388 if span.ctxt().outer().expn_info().is_some() {
1389 applicability = Applicability::MaybeIncorrect;
1390 }
abe05a73
XL
1391 let def_span = cx.tcx.sess.codemap().def_span(span);
1392 let mut err = cx.struct_span_lint(UNREACHABLE_PUB, def_span,
1393 &format!("unreachable `pub` {}", what));
94b46f34
XL
1394 // We are presuming that visibility is token at start of
1395 // declaration (can be macro variable rather than literal `pub`)
abe05a73 1396 let pub_span = cx.tcx.sess.codemap().span_until_char(def_span, ' ');
0531ce1d 1397 let replacement = if cx.tcx.features().crate_visibility_modifier {
abe05a73
XL
1398 "crate"
1399 } else {
1400 "pub(crate)"
1401 }.to_owned();
94b46f34
XL
1402 err.span_suggestion_with_applicability(pub_span,
1403 "consider restricting its visibility",
1404 replacement,
1405 applicability);
abe05a73
XL
1406 if exportable {
1407 err.help("or consider exporting it for use by other crates");
1408 }
1409 err.emit();
1410 }
1411 }
1412}
1413
94b46f34 1414
abe05a73
XL
1415impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnreachablePub {
1416 fn check_item(&mut self, cx: &LateContext, item: &hir::Item) {
94b46f34
XL
1417 let applicability = match item.node {
1418 // suggestion span-manipulation is inadequate for `pub use
1419 // module::{item}` (Issue #50455)
1420 hir::ItemUse(..) => Applicability::MaybeIncorrect,
1421 _ => Applicability::MachineApplicable,
1422 };
1423 self.perform_lint(cx, "item", item.id, &item.vis, item.span, true, applicability);
abe05a73
XL
1424 }
1425
1426 fn check_foreign_item(&mut self, cx: &LateContext, foreign_item: &hir::ForeignItem) {
94b46f34
XL
1427 self.perform_lint(cx, "item", foreign_item.id, &foreign_item.vis,
1428 foreign_item.span, true, Applicability::MachineApplicable);
abe05a73
XL
1429 }
1430
1431 fn check_struct_field(&mut self, cx: &LateContext, field: &hir::StructField) {
94b46f34
XL
1432 self.perform_lint(cx, "field", field.id, &field.vis, field.span, false,
1433 Applicability::MachineApplicable);
abe05a73
XL
1434 }
1435
1436 fn check_impl_item(&mut self, cx: &LateContext, impl_item: &hir::ImplItem) {
94b46f34
XL
1437 self.perform_lint(cx, "item", impl_item.id, &impl_item.vis, impl_item.span, false,
1438 Applicability::MachineApplicable);
abe05a73
XL
1439 }
1440}
0531ce1d
XL
1441
1442/// Lint for trait and lifetime bounds in type aliases being mostly ignored:
1443/// They are relevant when using associated types, but otherwise neither checked
1444/// at definition site nor enforced at use site.
1445
1446pub struct TypeAliasBounds;
1447
1448declare_lint! {
1449 TYPE_ALIAS_BOUNDS,
1450 Warn,
1451 "bounds in type aliases are not enforced"
1452}
1453
1454impl LintPass for TypeAliasBounds {
1455 fn get_lints(&self) -> LintArray {
1456 lint_array!(TYPE_ALIAS_BOUNDS)
1457 }
1458}
1459
1460impl TypeAliasBounds {
1461 fn is_type_variable_assoc(qpath: &hir::QPath) -> bool {
1462 match *qpath {
1463 hir::QPath::TypeRelative(ref ty, _) => {
1464 // If this is a type variable, we found a `T::Assoc`.
1465 match ty.node {
1466 hir::TyPath(hir::QPath::Resolved(None, ref path)) => {
1467 match path.def {
1468 Def::TyParam(_) => true,
1469 _ => false
1470 }
1471 }
1472 _ => false
1473 }
1474 }
1475 hir::QPath::Resolved(..) => false,
1476 }
1477 }
1478
1479 fn suggest_changing_assoc_types(ty: &hir::Ty, err: &mut DiagnosticBuilder) {
1480 // Access to associates types should use `<T as Bound>::Assoc`, which does not need a
1481 // bound. Let's see if this type does that.
1482
1483 // We use a HIR visitor to walk the type.
1484 use rustc::hir::intravisit::{self, Visitor};
1485 use syntax::ast::NodeId;
1486 struct WalkAssocTypes<'a, 'db> where 'db: 'a {
1487 err: &'a mut DiagnosticBuilder<'db>
1488 }
1489 impl<'a, 'db, 'v> Visitor<'v> for WalkAssocTypes<'a, 'db> {
1490 fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'v>
1491 {
1492 intravisit::NestedVisitorMap::None
1493 }
1494
1495 fn visit_qpath(&mut self, qpath: &'v hir::QPath, id: NodeId, span: Span) {
1496 if TypeAliasBounds::is_type_variable_assoc(qpath) {
1497 self.err.span_help(span,
1498 "use fully disambiguated paths (i.e., `<T as Trait>::Assoc`) to refer to \
1499 associated types in type aliases");
1500 }
1501 intravisit::walk_qpath(self, qpath, id, span)
1502 }
1503 }
1504
1505 // Let's go for a walk!
1506 let mut visitor = WalkAssocTypes { err };
1507 visitor.visit_ty(ty);
1508 }
1509}
1510
1511impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeAliasBounds {
1512 fn check_item(&mut self, cx: &LateContext, item: &hir::Item) {
1513 let (ty, type_alias_generics) = match item.node {
1514 hir::ItemTy(ref ty, ref generics) => (&*ty, generics),
1515 _ => return,
1516 };
1517 let mut suggested_changing_assoc_types = false;
1518 // There must not be a where clause
1519 if !type_alias_generics.where_clause.predicates.is_empty() {
1520 let spans : Vec<_> = type_alias_generics.where_clause.predicates.iter()
1521 .map(|pred| pred.span()).collect();
1522 let mut err = cx.struct_span_lint(TYPE_ALIAS_BOUNDS, spans,
1523 "where clauses are not enforced in type aliases");
1524 err.help("the clause will not be checked when the type alias is used, \
1525 and should be removed");
1526 if !suggested_changing_assoc_types {
1527 TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err);
1528 suggested_changing_assoc_types = true;
1529 }
1530 err.emit();
1531 }
1532 // The parameters must not have bounds
1533 for param in type_alias_generics.params.iter() {
1534 let spans : Vec<_> = match param {
1535 &hir::GenericParam::Lifetime(ref l) => l.bounds.iter().map(|b| b.span).collect(),
1536 &hir::GenericParam::Type(ref ty) => ty.bounds.iter().map(|b| b.span()).collect(),
1537 };
1538 if !spans.is_empty() {
1539 let mut err = cx.struct_span_lint(
1540 TYPE_ALIAS_BOUNDS,
1541 spans,
1542 "bounds on generic parameters are not enforced in type aliases",
1543 );
1544 err.help("the bound will not be checked when the type alias is used, \
1545 and should be removed");
1546 if !suggested_changing_assoc_types {
1547 TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err);
1548 suggested_changing_assoc_types = true;
1549 }
1550 err.emit();
1551 }
1552 }
1553 }
1554}
1555
1556/// Lint constants that are erroneous.
1557/// Without this lint, we might not get any diagnostic if the constant is
1558/// unused within this crate, even though downstream crates can't use it
1559/// without producing an error.
1560pub struct UnusedBrokenConst;
1561
1562impl LintPass for UnusedBrokenConst {
1563 fn get_lints(&self) -> LintArray {
1564 lint_array!()
1565 }
1566}
1567
1568fn check_const(cx: &LateContext, body_id: hir::BodyId, what: &str) {
1569 let def_id = cx.tcx.hir.body_owner_def_id(body_id);
1570 let param_env = cx.tcx.param_env(def_id);
1571 let cid = ::rustc::mir::interpret::GlobalId {
1572 instance: ty::Instance::mono(cx.tcx, def_id),
1573 promoted: None
1574 };
1575 if let Err(err) = cx.tcx.const_eval(param_env.and(cid)) {
1576 let span = cx.tcx.def_span(def_id);
94b46f34
XL
1577 err.report_as_lint(
1578 cx.tcx.at(span),
0531ce1d 1579 &format!("this {} cannot be used", what),
94b46f34 1580 cx.current_lint_root(),
0531ce1d 1581 );
0531ce1d
XL
1582 }
1583}
1584
1585struct UnusedBrokenConstVisitor<'a, 'tcx: 'a>(&'a LateContext<'a, 'tcx>);
1586
1587impl<'a, 'tcx, 'v> hir::intravisit::Visitor<'v> for UnusedBrokenConstVisitor<'a, 'tcx> {
1588 fn visit_nested_body(&mut self, id: hir::BodyId) {
1589 check_const(self.0, id, "array length");
1590 }
1591 fn nested_visit_map<'this>(&'this mut self) -> hir::intravisit::NestedVisitorMap<'this, 'v> {
1592 hir::intravisit::NestedVisitorMap::None
1593 }
1594}
1595
1596impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedBrokenConst {
1597 fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
1598 match it.node {
1599 hir::ItemConst(_, body_id) => {
1600 check_const(cx, body_id, "constant");
1601 },
1602 hir::ItemTy(ref ty, _) => hir::intravisit::walk_ty(
1603 &mut UnusedBrokenConstVisitor(cx),
1604 ty
1605 ),
1606 _ => {},
1607 }
1608 }
1609}
94b46f34
XL
1610
1611/// Lint for trait and lifetime bounds that don't depend on type parameters
1612/// which either do nothing, or stop the item from being used.
1613pub struct TrivialConstraints;
1614
1615declare_lint! {
1616 TRIVIAL_BOUNDS,
1617 Warn,
1618 "these bounds don't depend on an type parameters"
1619}
1620
1621impl LintPass for TrivialConstraints {
1622 fn get_lints(&self) -> LintArray {
1623 lint_array!(TRIVIAL_BOUNDS)
1624 }
1625}
1626
1627impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TrivialConstraints {
1628 fn check_item(
1629 &mut self,
1630 cx: &LateContext<'a, 'tcx>,
1631 item: &'tcx hir::Item,
1632 ) {
1633 use rustc::ty::fold::TypeFoldable;
1634 use rustc::ty::Predicate::*;
1635
1636
1637 if cx.tcx.features().trivial_bounds {
1638 let def_id = cx.tcx.hir.local_def_id(item.id);
1639 let predicates = cx.tcx.predicates_of(def_id);
1640 for predicate in &predicates.predicates {
1641 let predicate_kind_name = match *predicate {
1642 Trait(..) => "Trait",
1643 TypeOutlives(..) |
1644 RegionOutlives(..) => "Lifetime",
1645
1646 // Ignore projections, as they can only be global
1647 // if the trait bound is global
1648 Projection(..) |
1649 // Ignore bounds that a user can't type
1650 WellFormed(..) |
1651 ObjectSafe(..) |
1652 ClosureKind(..) |
1653 Subtype(..) |
1654 ConstEvaluatable(..) => continue,
1655 };
1656 if predicate.is_global() {
1657 cx.span_lint(
1658 TRIVIAL_BOUNDS,
1659 item.span,
1660 &format!("{} bound {} does not depend on any type \
1661 or lifetime parameters", predicate_kind_name, predicate),
1662 );
1663 }
1664 }
1665 }
1666 }
1667}
1668
1669/// Does nothing as a lint pass, but registers some `Lint`s
1670/// which are used by other parts of the compiler.
1671#[derive(Copy, Clone)]
1672pub struct SoftLints;
1673
1674impl LintPass for SoftLints {
1675 fn get_lints(&self) -> LintArray {
1676 lint_array!(
1677 WHILE_TRUE,
1678 BOX_POINTERS,
1679 NON_SHORTHAND_FIELD_PATTERNS,
1680 UNSAFE_CODE,
1681 MISSING_DOCS,
1682 MISSING_COPY_IMPLEMENTATIONS,
1683 MISSING_DEBUG_IMPLEMENTATIONS,
1684 ANONYMOUS_PARAMETERS,
1685 UNUSED_DOC_COMMENTS,
1686 UNCONDITIONAL_RECURSION,
1687 PLUGIN_AS_LIBRARY,
1688 PRIVATE_NO_MANGLE_FNS,
1689 PRIVATE_NO_MANGLE_STATICS,
1690 NO_MANGLE_CONST_ITEMS,
1691 NO_MANGLE_GENERIC_ITEMS,
1692 MUTABLE_TRANSMUTES,
1693 UNSTABLE_FEATURES,
1694 UNIONS_WITH_DROP_FIELDS,
1695 UNREACHABLE_PUB,
1696 TYPE_ALIAS_BOUNDS,
1697 TRIVIAL_BOUNDS,
1698 )
1699 }
1700}