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