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