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