]>
Commit | Line | Data |
---|---|---|
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 | 31 | use rustc::hir::def::Def; |
54a0048b | 32 | use rustc::hir::def_id::DefId; |
a7813a04 | 33 | use rustc::cfg; |
54a0048b | 34 | use rustc::ty::subst::Substs; |
041b39d2 | 35 | use rustc::ty::{self, Ty}; |
5bcae85e | 36 | use rustc::traits::{self, Reveal}; |
54a0048b | 37 | use rustc::hir::map as hir_map; |
c30ab7b3 | 38 | use util::nodemap::NodeSet; |
3b2f2976 | 39 | use lint::{LateContext, LintContext, LintArray}; |
c30ab7b3 | 40 | use lint::{LintPass, LateLintPass, EarlyLintPass, EarlyContext}; |
c34b1796 | 41 | |
e9174d1e | 42 | use std::collections::HashSet; |
c34b1796 | 43 | |
c30ab7b3 | 44 | use syntax::ast; |
9e0c209e | 45 | use syntax::attr; |
c30ab7b3 | 46 | use syntax::feature_gate::{AttributeGate, AttributeType, Stability, deprecated_attributes}; |
abe05a73 | 47 | use syntax_pos::{BytePos, Span, SyntaxContext}; |
7cac9316 | 48 | use syntax::symbol::keywords; |
e9174d1e | 49 | |
54a0048b SL |
50 | use rustc::hir::{self, PatKind}; |
51 | use rustc::hir::intravisit::FnKind; | |
e9174d1e | 52 | |
b039eaaf | 53 | use bad_style::{MethodLateContext, method_context}; |
c34b1796 | 54 | |
b039eaaf SL |
55 | // hardwired lints from librustc |
56 | pub use lint::builtin::*; | |
c34b1796 | 57 | |
abe05a73 XL |
58 | declare_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)] | |
65 | pub struct AutoImpl; | |
66 | ||
67 | impl LintPass for AutoImpl { | |
68 | fn get_lints(&self) -> LintArray { | |
69 | lint_array!(AUTO_IMPL) | |
70 | } | |
71 | } | |
72 | ||
73 | impl 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 |
83 | declare_lint! { |
84 | WHILE_TRUE, | |
85 | Warn, | |
86 | "suggest using `loop { }` instead of `while true { }`" | |
87 | } | |
c34b1796 | 88 | |
b039eaaf SL |
89 | #[derive(Copy, Clone)] |
90 | pub struct WhileTrue; | |
91 | ||
92 | impl LintPass for WhileTrue { | |
93 | fn get_lints(&self) -> LintArray { | |
94 | lint_array!(WHILE_TRUE) | |
c34b1796 | 95 | } |
b039eaaf | 96 | } |
c34b1796 | 97 | |
476ff2be | 98 | impl<'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 | ||
118 | declare_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 |
125 | pub struct BoxPointers; |
126 | ||
127 | impl 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 | 138 | impl LintPass for BoxPointers { |
c34b1796 | 139 | fn get_lints(&self) -> LintArray { |
b039eaaf | 140 | lint_array!(BOX_POINTERS) |
c34b1796 | 141 | } |
b039eaaf | 142 | } |
c34b1796 | 143 | |
476ff2be | 144 | impl<'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 |
178 | declare_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)] | |
185 | pub struct NonShorthandFieldPatterns; | |
186 | ||
187 | impl LintPass for NonShorthandFieldPatterns { | |
188 | fn get_lints(&self) -> LintArray { | |
189 | lint_array!(NON_SHORTHAND_FIELD_PATTERNS) | |
190 | } | |
b039eaaf | 191 | } |
c34b1796 | 192 | |
476ff2be | 193 | impl<'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 |
218 | declare_lint! { |
219 | UNSAFE_CODE, | |
220 | Allow, | |
221 | "usage of `unsafe` code" | |
222 | } | |
223 | ||
224 | #[derive(Copy, Clone)] | |
225 | pub struct UnsafeCode; | |
226 | ||
227 | impl LintPass for UnsafeCode { | |
228 | fn get_lints(&self) -> LintArray { | |
229 | lint_array!(UNSAFE_CODE) | |
230 | } | |
b039eaaf | 231 | } |
c34b1796 | 232 | |
3b2f2976 XL |
233 | impl 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 | 244 | impl<'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 |
299 | declare_lint! { |
300 | MISSING_DOCS, | |
301 | Allow, | |
302 | "detects missing documentation for public members" | |
303 | } | |
304 | ||
305 | pub 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 | ||
314 | impl 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 | ||
361 | impl LintPass for MissingDoc { | |
362 | fn get_lints(&self) -> LintArray { | |
363 | lint_array!(MISSING_DOCS) | |
364 | } | |
b039eaaf | 365 | } |
c34b1796 | 366 | |
476ff2be | 367 | impl<'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 | ||
488 | declare_lint! { | |
489 | pub MISSING_COPY_IMPLEMENTATIONS, | |
490 | Allow, | |
491 | "detects potentially-forgotten implementations of `Copy`" | |
492 | } | |
493 | ||
494 | #[derive(Copy, Clone)] | |
495 | pub struct MissingCopyImplementations; | |
496 | ||
497 | impl LintPass for MissingCopyImplementations { | |
498 | fn get_lints(&self) -> LintArray { | |
499 | lint_array!(MISSING_COPY_IMPLEMENTATIONS) | |
500 | } | |
b039eaaf | 501 | } |
c34b1796 | 502 | |
476ff2be | 503 | impl<'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 | ||
548 | declare_lint! { | |
549 | MISSING_DEBUG_IMPLEMENTATIONS, | |
550 | Allow, | |
551 | "detects missing implementations of fmt::Debug" | |
552 | } | |
553 | ||
554 | pub struct MissingDebugImplementations { | |
555 | impling_types: Option<NodeSet>, | |
556 | } | |
557 | ||
558 | impl MissingDebugImplementations { | |
559 | pub fn new() -> MissingDebugImplementations { | |
c30ab7b3 | 560 | MissingDebugImplementations { impling_types: None } |
c34b1796 AL |
561 | } |
562 | } | |
563 | ||
564 | impl LintPass for MissingDebugImplementations { | |
565 | fn get_lints(&self) -> LintArray { | |
566 | lint_array!(MISSING_DEBUG_IMPLEMENTATIONS) | |
567 | } | |
b039eaaf | 568 | } |
c34b1796 | 569 | |
476ff2be | 570 | impl<'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 |
611 | declare_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)] | |
619 | pub struct AnonymousParameters; | |
620 | ||
621 | impl LintPass for AnonymousParameters { | |
622 | fn get_lints(&self) -> LintArray { | |
623 | lint_array!(ANONYMOUS_PARAMETERS) | |
624 | } | |
625 | } | |
626 | ||
627 | impl 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)] | |
651 | pub 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 | ||
657 | impl DeprecatedAttr { | |
658 | pub fn new() -> DeprecatedAttr { | |
659 | DeprecatedAttr { | |
660 | depr_attrs: deprecated_attributes(), | |
661 | } | |
662 | } | |
663 | } | |
664 | ||
665 | impl LintPass for DeprecatedAttr { | |
666 | fn get_lints(&self) -> LintArray { | |
abe05a73 | 667 | lint_array!() |
c30ab7b3 SL |
668 | } |
669 | } | |
670 | ||
671 | impl 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 |
692 | declare_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)] | |
700 | pub struct IllegalFloatLiteralPattern; | |
701 | ||
702 | impl LintPass for IllegalFloatLiteralPattern { | |
703 | fn get_lints(&self) -> LintArray { | |
704 | lint_array!(ILLEGAL_FLOATING_POINT_LITERAL_PATTERN) | |
705 | } | |
706 | } | |
707 | ||
708 | fn 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 | ||
731 | impl 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 |
763 | declare_lint! { |
764 | pub UNUSED_DOC_COMMENT, | |
765 | Warn, | |
766 | "detects doc comments that aren't used by rustdoc" | |
767 | } | |
768 | ||
769 | #[derive(Copy, Clone)] | |
770 | pub struct UnusedDocComment; | |
771 | ||
772 | impl LintPass for UnusedDocComment { | |
773 | fn get_lints(&self) -> LintArray { | |
774 | lint_array![UNUSED_DOC_COMMENT] | |
775 | } | |
776 | } | |
777 | ||
778 | impl 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 | ||
789 | impl 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 |
803 | declare_lint! { |
804 | pub UNCONDITIONAL_RECURSION, | |
805 | Warn, | |
806 | "functions that cannot return without calling themselves" | |
807 | } | |
808 | ||
809 | #[derive(Copy, Clone)] | |
810 | pub struct UnconditionalRecursion; | |
811 | ||
812 | ||
813 | impl LintPass for UnconditionalRecursion { | |
814 | fn get_lints(&self) -> LintArray { | |
815 | lint_array![UNCONDITIONAL_RECURSION] | |
816 | } | |
b039eaaf | 817 | } |
c34b1796 | 818 | |
476ff2be | 819 | impl<'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 | ||
1069 | declare_lint! { | |
1070 | PLUGIN_AS_LIBRARY, | |
1071 | Warn, | |
1072 | "compiler plugin used as ordinary library in non-plugin crate" | |
1073 | } | |
1074 | ||
1075 | #[derive(Copy, Clone)] | |
1076 | pub struct PluginAsLibrary; | |
1077 | ||
1078 | impl LintPass for PluginAsLibrary { | |
1079 | fn get_lints(&self) -> LintArray { | |
1080 | lint_array![PLUGIN_AS_LIBRARY] | |
1081 | } | |
b039eaaf | 1082 | } |
c34b1796 | 1083 | |
476ff2be | 1084 | impl<'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 | ||
1115 | declare_lint! { | |
1116 | PRIVATE_NO_MANGLE_FNS, | |
1117 | Warn, | |
1118 | "functions marked #[no_mangle] should be exported" | |
1119 | } | |
1120 | ||
1121 | declare_lint! { | |
1122 | PRIVATE_NO_MANGLE_STATICS, | |
1123 | Warn, | |
1124 | "statics marked #[no_mangle] should be exported" | |
1125 | } | |
1126 | ||
1127 | declare_lint! { | |
1128 | NO_MANGLE_CONST_ITEMS, | |
1129 | Deny, | |
1130 | "const items will not have their symbols exported" | |
1131 | } | |
1132 | ||
9cc50fc6 SL |
1133 | declare_lint! { |
1134 | NO_MANGLE_GENERIC_ITEMS, | |
1135 | Warn, | |
1136 | "generic items must be mangled" | |
1137 | } | |
1138 | ||
c34b1796 AL |
1139 | #[derive(Copy, Clone)] |
1140 | pub struct InvalidNoMangleItems; | |
1141 | ||
1142 | impl 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 | 1151 | impl<'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)] |
1212 | pub struct MutableTransmutes; | |
1213 | ||
1214 | declare_lint! { | |
1215 | MUTABLE_TRANSMUTES, | |
1216 | Deny, | |
1217 | "mutating transmuted &mut T from &T may cause undefined behavior" | |
1218 | } | |
1219 | ||
1220 | impl LintPass for MutableTransmutes { | |
1221 | fn get_lints(&self) -> LintArray { | |
1222 | lint_array!(MUTABLE_TRANSMUTES) | |
1223 | } | |
b039eaaf | 1224 | } |
bd371182 | 1225 | |
476ff2be | 1226 | impl<'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)] | |
1272 | pub struct UnstableFeatures; | |
1273 | ||
1274 | declare_lint! { | |
1275 | UNSTABLE_FEATURES, | |
1276 | Allow, | |
62682a34 | 1277 | "enabling unstable features (deprecated. do not use)" |
c34b1796 AL |
1278 | } |
1279 | ||
1280 | impl LintPass for UnstableFeatures { | |
1281 | fn get_lints(&self) -> LintArray { | |
1282 | lint_array!(UNSTABLE_FEATURES) | |
1283 | } | |
b039eaaf SL |
1284 | } |
1285 | ||
476ff2be | 1286 | impl<'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. |
1299 | pub struct UnionsWithDropFields; | |
bd371182 AL |
1300 | |
1301 | declare_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 | 1307 | impl 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 | 1313 | impl<'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 | |
1331 | pub struct UnreachablePub; | |
1332 | ||
1333 | declare_lint! { | |
1334 | UNREACHABLE_PUB, | |
1335 | Allow, | |
1336 | "`pub` items not reachable from crate root" | |
1337 | } | |
1338 | ||
1339 | impl LintPass for UnreachablePub { | |
1340 | fn get_lints(&self) -> LintArray { | |
1341 | lint_array!(UNREACHABLE_PUB) | |
1342 | } | |
1343 | } | |
1344 | ||
1345 | impl 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 | ||
1369 | impl<'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 | } |