rust/src/librustc_lint/builtin.rs

1716 lines
62 KiB
Rust
Raw Normal View History

//! Lints in the Rust compiler.
//!
//! This contains lints which can feasibly be implemented as their own
//! AST visitor. Also see `rustc::lint::builtin`, which contains the
//! definitions of lints that are emitted directly inside the main
//! compiler.
//!
//! To add a new lint to rustc, declare it here using `declare_lint!()`.
//! Then add code to emit the new lint in the appropriate circumstances.
//! You can do that in an existing `LintPass` if it makes sense, or in a
//! new `LintPass`, or using `Session::add_lint` elsewhere in the
//! compiler. Only do the latter if the check can't be written cleanly as a
//! `LintPass` (also, note that such lints will need to be defined in
//! `rustc::lint::builtin`, not here).
//!
2019-02-08 14:53:55 +01:00
//! If you define a new `EarlyLintPass`, you will also need to add it to the
//! `add_early_builtin!` or `add_early_builtin_with_new!` invocation in
//! `lib.rs`. Use the former for unit-like structs and the latter for structs
//! with a `pub fn new()`.
//!
//! If you define a new `LateLintPass`, you will also need to add it to the
//! `late_lint_methods!` invocation in `lib.rs`.
use rustc::hir::def::{Res, DefKind};
2019-01-13 01:06:50 +01:00
use rustc::hir::def_id::{DefId, LOCAL_CRATE};
use rustc::ty::{self, Ty};
2019-02-08 20:35:41 +09:00
use rustc::{lint, util};
2018-08-25 15:56:16 +01:00
use hir::Node;
2019-02-27 17:35:24 +01:00
use util::nodemap::HirIdSet;
rustc: Rearchitect lints to be emitted more eagerly In preparation for incremental compilation this commit refactors the lint handling infrastructure in the compiler to be more "eager" and overall more incremental-friendly. Many passes of the compiler can emit lints at various points but before this commit all lints were buffered in a table to be emitted at the very end of compilation. This commit changes these lints to be emitted immediately during compilation using pre-calculated lint level-related data structures. Linting today is split into two phases, one set of "early" lints run on the `syntax::ast` and a "late" set of lints run on the HIR. This commit moves the "early" lints to running as late as possible in compilation, just before HIR lowering. This notably means that we're catching resolve-related lints just before HIR lowering. The early linting remains a pass very similar to how it was before, maintaining context of the current lint level as it walks the tree. Post-HIR, however, linting is structured as a method on the `TyCtxt` which transitively executes a query to calculate lint levels. Each request to lint on a `TyCtxt` will query the entire crate's 'lint level data structure' and then go from there about whether the lint should be emitted or not. The query depends on the entire HIR crate but should be very quick to calculate (just a quick walk of the HIR) and the red-green system should notice that the lint level data structure rarely changes, and should hopefully preserve incrementality. Overall this resulted in a pretty big change to the test suite now that lints are emitted much earlier in compilation (on-demand vs only at the end). This in turn necessitated the addition of many `#![allow(warnings)]` directives throughout the compile-fail test suite and a number of updates to the UI test suite.
2017-07-26 21:51:09 -07:00
use lint::{LateContext, LintContext, LintArray};
use lint::{LintPass, LateLintPass, EarlyLintPass, EarlyContext};
2018-06-04 18:32:06 +02:00
use rustc::util::nodemap::FxHashSet;
2018-07-14 16:40:17 +02:00
use syntax::tokenstream::{TokenTree, TokenStream};
2016-10-18 18:04:28 +13:00
use syntax::ast;
use syntax::ptr::P;
use syntax::ast::Expr;
use syntax::attr::{self, HasAttrs};
2018-08-18 12:14:03 +02:00
use syntax::source_map::Spanned;
use syntax::edition::Edition;
use syntax::feature_gate::{AttributeGate, AttributeTemplate, AttributeType};
use syntax::feature_gate::{Stability, deprecated_attributes};
use syntax_pos::{BytePos, Span, SyntaxContext};
2019-05-11 17:41:37 +03:00
use syntax::symbol::{Symbol, kw, sym};
2018-05-04 14:12:33 -07:00
use syntax::errors::{Applicability, DiagnosticBuilder};
use syntax::print::pprust::expr_to_string;
2019-01-15 06:54:28 +09:00
use syntax::visit::FnKind;
2018-05-26 00:27:54 +01:00
use rustc::hir::{self, GenericParamKind, PatKind};
2015-07-31 00:04:06 -07:00
2019-02-08 20:35:41 +09:00
use crate::nonstandard_style::{MethodLateContext, method_context};
use log::debug;
// hardwired lints from librustc
pub use lint::builtin::*;
declare_lint! {
WHILE_TRUE,
Warn,
"suggest using `loop { }` instead of `while true { }`"
}
declare_lint_pass!(WhileTrue => [WHILE_TRUE]);
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for WhileTrue {
2019-02-08 20:35:41 +09:00
fn check_expr(&mut self, cx: &LateContext<'_, '_>, e: &hir::Expr) {
2018-07-11 20:05:29 +08:00
if let hir::ExprKind::While(ref cond, ..) = e.node {
if let hir::ExprKind::Lit(ref lit) = cond.node {
if let ast::LitKind::Bool(true) = lit.node {
if lit.span.ctxt() == SyntaxContext::empty() {
let msg = "denote infinite loops with `loop { ... }`";
2018-08-18 12:14:09 +02:00
let condition_span = cx.tcx.sess.source_map().def_span(e.span);
let mut err = cx.struct_span_lint(WHILE_TRUE, condition_span, msg);
err.span_suggestion_short(
condition_span,
"use `loop`",
"loop".to_owned(),
Applicability::MachineApplicable
);
err.emit();
}
}
}
}
}
}
declare_lint! {
BOX_POINTERS,
Allow,
"use of owned (Box type) heap memory"
}
declare_lint_pass!(BoxPointers => [BOX_POINTERS]);
impl BoxPointers {
2019-06-11 12:47:08 +03:00
fn check_heap_type(&self, cx: &LateContext<'_, '_>, span: Span, ty: Ty<'_>) {
for leaf_ty in ty.walk() {
2017-01-21 17:40:31 +03:00
if leaf_ty.is_box() {
let m = format!("type uses owned (Box type) pointers: {}", ty);
cx.span_lint(BOX_POINTERS, span, &m);
}
}
}
}
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BoxPointers {
2019-02-08 20:35:41 +09:00
fn check_item(&mut self, cx: &LateContext<'_, '_>, it: &hir::Item) {
match it.node {
2018-07-11 23:36:06 +08:00
hir::ItemKind::Fn(..) |
hir::ItemKind::Ty(..) |
hir::ItemKind::Enum(..) |
hir::ItemKind::Struct(..) |
hir::ItemKind::Union(..) => {
2019-02-27 17:35:24 +01:00
let def_id = cx.tcx.hir().local_def_id_from_hir_id(it.hir_id);
self.check_heap_type(cx, it.span, cx.tcx.type_of(def_id))
}
_ => ()
}
// If it's a struct, we also have to check the fields' types
match it.node {
2018-07-11 23:36:06 +08:00
hir::ItemKind::Struct(ref struct_def, _) |
hir::ItemKind::Union(ref struct_def, _) => {
2015-10-08 23:45:46 +03:00
for struct_field in struct_def.fields() {
2019-02-27 15:56:59 +01:00
let def_id = cx.tcx.hir().local_def_id_from_hir_id(struct_field.hir_id);
self.check_heap_type(cx, struct_field.span,
cx.tcx.type_of(def_id));
}
}
2016-10-09 09:38:07 +05:30
_ => (),
}
}
2019-02-08 20:35:41 +09:00
fn check_expr(&mut self, cx: &LateContext<'_, '_>, e: &hir::Expr) {
2019-02-04 09:38:11 +01:00
let ty = cx.tables.node_type(e.hir_id);
self.check_heap_type(cx, e.span, ty);
}
}
declare_lint! {
NON_SHORTHAND_FIELD_PATTERNS,
Warn,
"using `Struct { x: x }` instead of `Struct { x }` in a pattern"
}
declare_lint_pass!(NonShorthandFieldPatterns => [NON_SHORTHAND_FIELD_PATTERNS]);
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonShorthandFieldPatterns {
2019-02-08 20:35:41 +09:00
fn check_pat(&mut self, cx: &LateContext<'_, '_>, pat: &hir::Pat) {
if let PatKind::Struct(ref qpath, ref field_pats, _) = pat.node {
let variant = cx.tables.pat_ty(pat).ty_adt_def()
.expect("struct pattern type is not an ADT")
.variant_of_res(cx.tables.qpath_res(qpath, pat.hir_id));
for fieldpat in field_pats {
2015-02-28 13:31:14 +01:00
if fieldpat.node.is_shorthand {
continue;
}
if fieldpat.span.ctxt().outer_expn_info().is_some() {
// Don't lint if this is a macro expansion: macro authors
// shouldn't have to worry about this kind of style issue
// (Issue #49588)
continue;
}
2019-03-07 12:18:59 +01:00
if let PatKind::Binding(_, _, ident, None) = fieldpat.node.pat.node {
if cx.tcx.find_field_index(ident, &variant) ==
2019-02-24 09:33:17 +01:00
Some(cx.tcx.field_index(fieldpat.node.hir_id, cx.tables)) {
let mut err = cx.struct_span_lint(NON_SHORTHAND_FIELD_PATTERNS,
2016-10-09 09:38:07 +05:30
fieldpat.span,
&format!("the `{}:` in this pattern is redundant", ident));
2018-08-18 12:14:31 +02:00
let subspan = cx.tcx.sess.source_map().span_through_char(fieldpat.span,
':');
err.span_suggestion_short(
subspan,
"remove this",
ident.to_string(),
Applicability::MachineApplicable
);
err.emit();
}
}
}
}
}
}
declare_lint! {
UNSAFE_CODE,
Allow,
"usage of `unsafe` code"
}
declare_lint_pass!(UnsafeCode => [UNSAFE_CODE]);
impl UnsafeCode {
2019-02-08 20:35:41 +09:00
fn report_unsafe(&self, cx: &EarlyContext<'_>, span: Span, desc: &'static str) {
// This comes from a macro that has `#[allow_internal_unsafe]`.
if span.allows_unsafe() {
return;
}
cx.span_lint(UNSAFE_CODE, span, desc);
}
}
2019-01-15 06:54:28 +09:00
impl EarlyLintPass for UnsafeCode {
2019-02-08 20:35:41 +09:00
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) {
if attr.check_name(sym::allow_internal_unsafe) {
2019-01-15 18:01:38 +09:00
self.report_unsafe(cx, attr.span, "`allow_internal_unsafe` allows defining \
macros using unsafe without triggering \
the `unsafe_code` lint at their call site");
2019-01-09 20:53:33 +09:00
}
}
2019-02-08 20:35:41 +09:00
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
2019-01-15 06:54:28 +09:00
if let ast::ExprKind::Block(ref blk, _) = e.node {
// Don't warn about generated blocks; that'll just pollute the output.
2019-01-15 06:54:28 +09:00
if blk.rules == ast::BlockCheckMode::Unsafe(ast::UserProvided) {
self.report_unsafe(cx, blk.span, "usage of an `unsafe` block");
}
}
}
2019-02-08 20:35:41 +09:00
fn check_item(&mut self, cx: &EarlyContext<'_>, it: &ast::Item) {
match it.node {
2019-01-15 06:54:28 +09:00
ast::ItemKind::Trait(_, ast::Unsafety::Unsafe, ..) => {
self.report_unsafe(cx, it.span, "declaration of an `unsafe` trait")
2016-10-09 09:38:07 +05:30
}
2019-01-15 06:54:28 +09:00
ast::ItemKind::Impl(ast::Unsafety::Unsafe, ..) => {
self.report_unsafe(cx, it.span, "implementation of an `unsafe` trait")
2016-10-09 09:38:07 +05:30
}
_ => return,
}
}
2016-10-09 09:38:07 +05:30
fn check_fn(&mut self,
2019-02-08 20:35:41 +09:00
cx: &EarlyContext<'_>,
fk: FnKind<'_>,
2019-01-15 06:54:28 +09:00
_: &ast::FnDecl,
2016-10-09 09:38:07 +05:30
span: Span,
_: ast::NodeId) {
match fk {
2019-01-15 06:54:28 +09:00
FnKind::ItemFn(_, ast::FnHeader { unsafety: ast::Unsafety::Unsafe, .. }, ..) => {
self.report_unsafe(cx, span, "declaration of an `unsafe` function")
2016-10-09 09:38:07 +05:30
}
2016-08-26 19:23:42 +03:00
FnKind::Method(_, sig, ..) => {
2019-01-15 06:54:28 +09:00
if sig.header.unsafety == ast::Unsafety::Unsafe {
self.report_unsafe(cx, span, "implementation of an `unsafe` method")
}
2016-10-09 09:38:07 +05:30
}
_ => (),
}
}
2019-02-08 20:35:41 +09:00
fn check_trait_item(&mut self, cx: &EarlyContext<'_>, item: &ast::TraitItem) {
2019-01-15 18:02:22 +09:00
if let ast::TraitItemKind::Method(ref sig, None) = item.node {
2019-01-15 06:54:28 +09:00
if sig.header.unsafety == ast::Unsafety::Unsafe {
self.report_unsafe(cx, item.span, "declaration of an `unsafe` method")
}
}
}
}
declare_lint! {
2018-07-05 20:06:33 +02:00
pub MISSING_DOCS,
Allow,
2018-08-21 16:29:08 +05:30
"detects missing documentation for public members",
report_in_external_macro: true
}
pub struct MissingDoc {
2019-02-08 14:53:55 +01:00
/// Stack of whether `#[doc(hidden)]` is set at each level which has lint attributes.
doc_hidden_stack: Vec<bool>,
/// Private traits or trait items that leaked through. Don't check their methods.
2019-02-26 11:04:58 +01:00
private_traits: FxHashSet<hir::HirId>,
}
impl_lint_pass!(MissingDoc => [MISSING_DOCS]);
fn has_doc(attr: &ast::Attribute) -> bool {
if !attr.check_name(sym::doc) {
return false;
}
if attr.is_value_str() {
return true;
}
if let Some(list) = attr.meta_item_list() {
for meta in list {
if meta.check_name(sym::include) || meta.check_name(sym::hidden) {
return true;
}
}
}
false
}
impl MissingDoc {
pub fn new() -> MissingDoc {
MissingDoc {
2016-10-09 09:38:07 +05:30
doc_hidden_stack: vec![false],
private_traits: FxHashSet::default(),
}
}
fn doc_hidden(&self) -> bool {
*self.doc_hidden_stack.last().expect("empty doc_hidden_stack")
}
fn check_missing_docs_attrs(&self,
2019-02-08 20:35:41 +09:00
cx: &LateContext<'_, '_>,
2019-02-26 11:04:58 +01:00
id: Option<hir::HirId>,
2016-10-09 09:38:07 +05:30
attrs: &[ast::Attribute],
sp: Span,
desc: &'static str) {
// If we're building a test harness, then warning about
// documentation is probably not really relevant right now.
2015-02-28 13:31:14 +01:00
if cx.sess().opts.test {
return;
}
// `#[doc(hidden)]` disables missing_docs check.
2015-02-28 13:31:14 +01:00
if self.doc_hidden() {
return;
}
// Only check publicly-visible items, using the result from the privacy pass.
// It's an option so the crate root can also use this function (it doesn't
// have a `NodeId`).
if let Some(id) = id {
if !cx.access_levels.is_exported(id) {
return;
}
}
let has_doc = attrs.iter().any(|a| has_doc(a));
if !has_doc {
2016-10-09 09:38:07 +05:30
cx.span_lint(MISSING_DOCS,
2018-08-18 12:14:09 +02:00
cx.tcx.sess.source_map().def_span(sp),
2015-02-28 13:31:14 +01:00
&format!("missing documentation for {}", desc));
}
}
}
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDoc {
2019-02-08 20:35:41 +09:00
fn enter_lint_attrs(&mut self, _: &LateContext<'_, '_>, attrs: &[ast::Attribute]) {
2016-10-09 09:38:07 +05:30
let doc_hidden = self.doc_hidden() ||
attrs.iter().any(|attr| {
attr.check_name(sym::doc) &&
2016-10-09 09:38:07 +05:30
match attr.meta_item_list() {
None => false,
Some(l) => attr::list_contains_name(&l, sym::hidden),
}
});
self.doc_hidden_stack.push(doc_hidden);
}
2019-02-08 20:35:41 +09:00
fn exit_lint_attrs(&mut self, _: &LateContext<'_, '_>, _attrs: &[ast::Attribute]) {
self.doc_hidden_stack.pop().expect("empty doc_hidden_stack");
}
2019-02-08 20:35:41 +09:00
fn check_crate(&mut self, cx: &LateContext<'_, '_>, krate: &hir::Crate) {
2015-02-28 13:31:14 +01:00
self.check_missing_docs_attrs(cx, None, &krate.attrs, krate.span, "crate");
for macro_def in &krate.exported_macros {
let has_doc = macro_def.attrs.iter().any(|a| has_doc(a));
if !has_doc {
cx.span_lint(MISSING_DOCS,
cx.tcx.sess.source_map().def_span(macro_def.span),
"missing documentation for macro");
}
}
}
2019-02-08 20:35:41 +09:00
fn check_item(&mut self, cx: &LateContext<'_, '_>, it: &hir::Item) {
let desc = match it.node {
2018-07-11 23:36:06 +08:00
hir::ItemKind::Fn(..) => "a function",
hir::ItemKind::Mod(..) => "a module",
hir::ItemKind::Enum(..) => "an enum",
hir::ItemKind::Struct(..) => "a struct",
hir::ItemKind::Union(..) => "a union",
hir::ItemKind::Trait(.., ref trait_item_refs) => {
// Issue #11592: traits are always considered exported, even when private.
if let hir::VisibilityKind::Inherited = it.vis.node {
2019-02-26 11:04:58 +01:00
self.private_traits.insert(it.hir_id);
for trait_item_ref in trait_item_refs {
2019-03-01 10:28:13 +01:00
self.private_traits.insert(trait_item_ref.id.hir_id);
}
2016-10-09 09:38:07 +05:30
return;
}
"a trait"
2016-10-09 09:38:07 +05:30
}
2018-07-11 23:36:06 +08:00
hir::ItemKind::Ty(..) => "a type alias",
hir::ItemKind::Impl(.., Some(ref trait_ref), _, ref impl_item_refs) => {
// If the trait is private, add the impl items to `private_traits` so they don't get
// reported for missing docs.
let real_trait = trait_ref.path.res.def_id();
if let Some(hir_id) = cx.tcx.hir().as_local_hir_id(real_trait) {
match cx.tcx.hir().find_by_hir_id(hir_id) {
2018-08-25 15:56:16 +01:00
Some(Node::Item(item)) => {
if let hir::VisibilityKind::Inherited = item.vis.node {
for impl_item_ref in impl_item_refs {
2019-03-01 10:28:13 +01:00
self.private_traits.insert(impl_item_ref.id.hir_id);
2016-10-09 09:38:07 +05:30
}
}
2016-10-09 09:38:07 +05:30
}
_ => {}
}
}
2016-10-09 09:38:07 +05:30
return;
}
2018-07-11 23:36:06 +08:00
hir::ItemKind::Const(..) => "a constant",
hir::ItemKind::Static(..) => "a static",
2016-10-09 09:38:07 +05:30
_ => return,
};
2019-02-26 11:04:58 +01:00
self.check_missing_docs_attrs(cx, Some(it.hir_id), &it.attrs, it.span, desc);
}
2019-02-08 20:35:41 +09:00
fn check_trait_item(&mut self, cx: &LateContext<'_, '_>, trait_item: &hir::TraitItem) {
2019-02-26 11:04:58 +01:00
if self.private_traits.contains(&trait_item.hir_id) {
2016-10-09 09:38:07 +05:30
return;
}
let desc = match trait_item.node {
hir::TraitItemKind::Const(..) => "an associated constant",
hir::TraitItemKind::Method(..) => "a trait method",
hir::TraitItemKind::Type(..) => "an associated type",
};
2016-10-09 09:38:07 +05:30
self.check_missing_docs_attrs(cx,
2019-02-26 11:04:58 +01:00
Some(trait_item.hir_id),
&trait_item.attrs,
2016-10-09 09:38:07 +05:30
trait_item.span,
desc);
}
2019-02-08 20:35:41 +09:00
fn check_impl_item(&mut self, cx: &LateContext<'_, '_>, impl_item: &hir::ImplItem) {
// If the method is an impl for a trait, don't doc.
2019-02-06 14:16:11 +01:00
if method_context(cx, impl_item.hir_id) == MethodLateContext::TraitImpl {
return;
}
let desc = match impl_item.node {
2015-11-12 15:57:51 +01:00
hir::ImplItemKind::Const(..) => "an associated constant",
hir::ImplItemKind::Method(..) => "a method",
hir::ImplItemKind::Type(_) => "an associated type",
2018-07-03 19:38:14 +02:00
hir::ImplItemKind::Existential(_) => "an associated existential type",
};
2016-10-09 09:38:07 +05:30
self.check_missing_docs_attrs(cx,
2019-02-26 11:04:58 +01:00
Some(impl_item.hir_id),
&impl_item.attrs,
2016-10-09 09:38:07 +05:30
impl_item.span,
desc);
}
2019-02-08 20:35:41 +09:00
fn check_struct_field(&mut self, cx: &LateContext<'_, '_>, sf: &hir::StructField) {
if !sf.is_positional() {
self.check_missing_docs_attrs(cx,
2019-02-26 11:04:58 +01:00
Some(sf.hir_id),
&sf.attrs,
sf.span,
"a struct field")
}
}
2019-02-08 20:35:41 +09:00
fn check_variant(&mut self, cx: &LateContext<'_, '_>, v: &hir::Variant, _: &hir::Generics) {
2016-10-09 09:38:07 +05:30
self.check_missing_docs_attrs(cx,
Some(v.node.id),
2016-10-09 09:38:07 +05:30
&v.node.attrs,
v.span,
"a variant");
}
}
declare_lint! {
pub MISSING_COPY_IMPLEMENTATIONS,
Allow,
"detects potentially-forgotten implementations of `Copy`"
}
declare_lint_pass!(MissingCopyImplementations => [MISSING_COPY_IMPLEMENTATIONS]);
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingCopyImplementations {
2019-02-08 20:35:41 +09:00
fn check_item(&mut self, cx: &LateContext<'_, '_>, item: &hir::Item) {
if !cx.access_levels.is_reachable(item.hir_id) {
2015-02-28 13:31:14 +01:00
return;
}
2015-08-25 21:52:15 +03:00
let (def, ty) = match item.node {
2018-07-11 23:36:06 +08:00
hir::ItemKind::Struct(_, ref ast_generics) => {
if !ast_generics.params.is_empty() {
2015-02-28 13:31:14 +01:00
return;
}
2019-02-27 17:35:24 +01:00
let def = cx.tcx.adt_def(cx.tcx.hir().local_def_id_from_hir_id(item.hir_id));
(def, cx.tcx.mk_adt(def, cx.tcx.intern_substs(&[])))
}
2018-07-11 23:36:06 +08:00
hir::ItemKind::Union(_, ref ast_generics) => {
if !ast_generics.params.is_empty() {
return;
}
2019-02-27 17:35:24 +01:00
let def = cx.tcx.adt_def(cx.tcx.hir().local_def_id_from_hir_id(item.hir_id));
(def, cx.tcx.mk_adt(def, cx.tcx.intern_substs(&[])))
}
2018-07-11 23:36:06 +08:00
hir::ItemKind::Enum(_, ref ast_generics) => {
if !ast_generics.params.is_empty() {
2015-02-28 13:31:14 +01:00
return;
}
2019-02-27 17:35:24 +01:00
let def = cx.tcx.adt_def(cx.tcx.hir().local_def_id_from_hir_id(item.hir_id));
(def, cx.tcx.mk_adt(def, cx.tcx.intern_substs(&[])))
}
_ => return,
};
if def.has_dtor(cx.tcx) {
2016-10-09 09:38:07 +05:30
return;
}
let param_env = ty::ParamEnv::empty();
if ty.is_copy_modulo_regions(cx.tcx, param_env, item.span) {
2015-02-28 13:31:14 +01:00
return;
}
if param_env.can_type_implement_copy(cx.tcx, ty).is_ok() {
cx.span_lint(MISSING_COPY_IMPLEMENTATIONS,
item.span,
"type could implement `Copy`; consider adding `impl \
Copy`")
}
}
}
declare_lint! {
MISSING_DEBUG_IMPLEMENTATIONS,
Allow,
"detects missing implementations of fmt::Debug"
}
#[derive(Default)]
pub struct MissingDebugImplementations {
2019-02-27 17:35:24 +01:00
impling_types: Option<HirIdSet>,
}
impl_lint_pass!(MissingDebugImplementations => [MISSING_DEBUG_IMPLEMENTATIONS]);
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDebugImplementations {
2019-02-08 20:35:41 +09:00
fn check_item(&mut self, cx: &LateContext<'_, '_>, item: &hir::Item) {
if !cx.access_levels.is_reachable(item.hir_id) {
return;
}
match item.node {
2018-07-11 23:36:06 +08:00
hir::ItemKind::Struct(..) |
hir::ItemKind::Union(..) |
hir::ItemKind::Enum(..) => {}
_ => return,
}
let debug = match cx.tcx.lang_items().debug_trait() {
Some(debug) => debug,
None => return,
};
if self.impling_types.is_none() {
2019-02-27 17:35:24 +01:00
let mut impls = HirIdSet::default();
cx.tcx.for_each_impl(debug, |d| {
2018-07-14 17:22:53 +02:00
if let Some(ty_def) = cx.tcx.type_of(d).ty_adt_def() {
2019-02-27 17:35:24 +01:00
if let Some(hir_id) = cx.tcx.hir().as_local_hir_id(ty_def.did) {
impls.insert(hir_id);
}
}
});
self.impling_types = Some(impls);
debug!("{:?}", self.impling_types);
}
2019-02-27 17:35:24 +01:00
if !self.impling_types.as_ref().unwrap().contains(&item.hir_id) {
cx.span_lint(MISSING_DEBUG_IMPLEMENTATIONS,
item.span,
"type does not implement `fmt::Debug`; consider adding #[derive(Debug)] \
or a manual implementation")
}
}
}
declare_lint! {
pub ANONYMOUS_PARAMETERS,
2018-08-27 12:14:31 -05:00
Allow,
2018-08-11 11:39:10 -05:00
"detects anonymous parameters"
}
declare_lint_pass!(
/// Checks for use of anonymous parameters (RFC 1685).
AnonymousParameters => [ANONYMOUS_PARAMETERS]
);
impl EarlyLintPass for AnonymousParameters {
2019-02-08 20:35:41 +09:00
fn check_trait_item(&mut self, cx: &EarlyContext<'_>, it: &ast::TraitItem) {
match it.node {
ast::TraitItemKind::Method(ref sig, _) => {
for arg in sig.decl.inputs.iter() {
match arg.pat.node {
ast::PatKind::Ident(_, ident, None) => {
2019-05-11 17:41:37 +03:00
if ident.name == kw::Invalid {
2018-02-17 17:33:27 -06:00
let ty_snip = cx
.sess
2018-08-18 12:14:09 +02:00
.source_map()
2018-02-17 17:33:27 -06:00
.span_to_snippet(arg.ty.span);
let (ty_snip, appl) = if let Ok(snip) = ty_snip {
(snip, Applicability::MachineApplicable)
} else {
("<type>".to_owned(), Applicability::HasPlaceholders)
};
cx.struct_span_lint(
ANONYMOUS_PARAMETERS,
arg.pat.span,
"anonymous parameters are deprecated and will be \
removed in the next edition."
).span_suggestion(
2018-02-17 17:33:27 -06:00
arg.pat.span,
"Try naming the parameter or explicitly \
ignoring it",
format!("_: {}", ty_snip),
appl
).emit();
}
}
_ => (),
}
}
},
_ => (),
}
}
}
2019-02-08 14:53:55 +01:00
/// Check for use of attributes which have been deprecated.
#[derive(Clone)]
2016-10-18 18:04:28 +13:00
pub struct DeprecatedAttr {
// This is not free to compute, so we want to keep it around, rather than
// compute it for every attribute.
depr_attrs: Vec<&'static (Symbol, AttributeType, AttributeTemplate, AttributeGate)>,
2016-10-18 18:04:28 +13:00
}
impl_lint_pass!(DeprecatedAttr => []);
2016-10-18 18:04:28 +13:00
impl DeprecatedAttr {
pub fn new() -> DeprecatedAttr {
DeprecatedAttr {
depr_attrs: deprecated_attributes(),
}
}
}
impl EarlyLintPass for DeprecatedAttr {
2019-02-08 20:35:41 +09:00
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) {
for &&(n, _, _, ref g) in &self.depr_attrs {
if attr.ident().map(|ident| ident.name) == Some(n) {
if let &AttributeGate::Gated(Stability::Deprecated(link, suggestion),
2016-10-18 18:04:28 +13:00
ref name,
ref reason,
_) = g {
let msg = format!("use of deprecated attribute `{}`: {}. See {}",
name, reason, link);
let mut err = cx.struct_span_lint(DEPRECATED, attr.span, &msg);
err.span_suggestion_short(
attr.span,
suggestion.unwrap_or("remove this attribute"),
String::new(),
Applicability::MachineApplicable
);
err.emit();
}
return;
}
}
}
}
2017-07-16 00:17:35 +02:00
declare_lint! {
2018-05-19 01:13:53 +03:00
pub UNUSED_DOC_COMMENTS,
2017-07-16 00:17:35 +02:00
Warn,
"detects doc comments that aren't used by rustdoc"
}
declare_lint_pass!(UnusedDocComment => [UNUSED_DOC_COMMENTS]);
2017-07-16 00:17:35 +02:00
impl UnusedDocComment {
fn warn_if_doc(
&self,
cx: &EarlyContext<'_>,
node_span: Span,
node_kind: &str,
is_macro_expansion: bool,
attrs: &[ast::Attribute]
) {
let mut attrs = attrs.into_iter().peekable();
// Accumulate a single span for sugared doc comments.
let mut sugared_span: Option<Span> = None;
while let Some(attr) = attrs.next() {
if attr.is_sugared_doc {
sugared_span = Some(
sugared_span.map_or_else(
|| attr.span,
|span| span.with_hi(attr.span.hi()),
),
);
}
if attrs.peek().map(|next_attr| next_attr.is_sugared_doc).unwrap_or_default() {
continue;
}
let span = sugared_span.take().unwrap_or_else(|| attr.span);
if attr.check_name(sym::doc) {
let mut err = cx.struct_span_lint(UNUSED_DOC_COMMENTS, span, "unused doc comment");
err.span_label(
node_span,
format!("rustdoc does not generate documentation for {}", node_kind)
);
if is_macro_expansion {
err.help("to document an item produced by a macro, \
the macro must produce the documentation as part of its expansion");
}
err.emit();
}
2017-07-16 00:17:35 +02:00
}
}
}
impl EarlyLintPass for UnusedDocComment {
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
if let ast::ItemKind::Mac(..) = item.node {
self.warn_if_doc(cx, item.span, "macro expansions", true, &item.attrs);
}
}
fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &ast::Stmt) {
let (kind, is_macro_expansion) = match stmt.node {
ast::StmtKind::Local(..) => ("statements", false),
ast::StmtKind::Item(..) => ("inner items", false),
ast::StmtKind::Mac(..) => ("macro expansions", true),
// expressions will be reported by `check_expr`.
ast::StmtKind::Semi(..) |
ast::StmtKind::Expr(..) => return,
};
self.warn_if_doc(cx, stmt.span, kind, is_macro_expansion, stmt.node.attrs());
2017-07-16 00:17:35 +02:00
}
2019-02-08 20:35:41 +09:00
fn check_arm(&mut self, cx: &EarlyContext<'_>, arm: &ast::Arm) {
let arm_span = arm.pats[0].span.with_hi(arm.body.span.hi());
self.warn_if_doc(cx, arm_span, "match arms", false, &arm.attrs);
2017-07-16 00:17:35 +02:00
}
2019-02-08 20:35:41 +09:00
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
self.warn_if_doc(cx, expr.span, "expressions", false, &expr.attrs);
2017-07-16 00:17:35 +02:00
}
}
declare_lint! {
PLUGIN_AS_LIBRARY,
Warn,
"compiler plugin used as ordinary library in non-plugin crate"
}
declare_lint_pass!(PluginAsLibrary => [PLUGIN_AS_LIBRARY]);
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PluginAsLibrary {
2019-02-08 20:35:41 +09:00
fn check_item(&mut self, cx: &LateContext<'_, '_>, it: &hir::Item) {
2019-01-13 01:06:50 +01:00
if cx.tcx.plugin_registrar_fn(LOCAL_CRATE).is_some() {
// We're compiling a plugin; it's fine to link other plugins.
return;
}
match it.node {
2018-07-11 23:36:06 +08:00
hir::ItemKind::ExternCrate(..) => (),
_ => return,
};
2019-02-27 17:35:24 +01:00
let def_id = cx.tcx.hir().local_def_id_from_hir_id(it.hir_id);
let prfn = match cx.tcx.extern_mod_stmt_cnum(def_id) {
Some(cnum) => cx.tcx.plugin_registrar_fn(cnum),
None => {
// Probably means we aren't linking the crate for some reason.
//
// Not sure if / when this could happen.
return;
}
};
2015-11-20 17:46:39 +02:00
if prfn.is_some() {
2016-10-09 09:38:07 +05:30
cx.span_lint(PLUGIN_AS_LIBRARY,
it.span,
2015-02-28 13:31:14 +01:00
"compiler plugin used as an ordinary library");
}
}
}
declare_lint! {
NO_MANGLE_CONST_ITEMS,
Deny,
"const items will not have their symbols exported"
}
2015-12-09 01:48:40 +09:00
declare_lint! {
NO_MANGLE_GENERIC_ITEMS,
Warn,
"generic items must be mangled"
}
declare_lint_pass!(InvalidNoMangleItems => [NO_MANGLE_CONST_ITEMS, NO_MANGLE_GENERIC_ITEMS]);
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidNoMangleItems {
2019-02-08 20:35:41 +09:00
fn check_item(&mut self, cx: &LateContext<'_, '_>, it: &hir::Item) {
match it.node {
2018-07-11 23:36:06 +08:00
hir::ItemKind::Fn(.., ref generics, _) => {
if let Some(no_mangle_attr) = attr::find_by_name(&it.attrs, sym::no_mangle) {
for param in &generics.params {
match param.kind {
GenericParamKind::Lifetime { .. } => {}
GenericParamKind::Type { .. } |
GenericParamKind::Const { .. } => {
let mut err = cx.struct_span_lint(
NO_MANGLE_GENERIC_ITEMS,
it.span,
"functions generic over types or consts must be mangled",
);
err.span_suggestion_short(
no_mangle_attr.span,
"remove this attribute",
String::new(),
// Use of `#[no_mangle]` suggests FFI intent; correct
// fix may be to monomorphize source by hand
Applicability::MaybeIncorrect
);
err.emit();
break;
}
}
2015-12-09 01:48:40 +09:00
}
}
2016-10-09 09:38:07 +05:30
}
2018-07-11 23:36:06 +08:00
hir::ItemKind::Const(..) => {
if attr::contains_name(&it.attrs, sym::no_mangle) {
// Const items do not refer to a particular location in memory, and therefore
// don't have anything to attach a symbol to
let msg = "const items should never be #[no_mangle]";
let mut err = cx.struct_span_lint(NO_MANGLE_CONST_ITEMS, it.span, msg);
// account for "pub const" (#45562)
2018-08-18 12:14:09 +02:00
let start = cx.tcx.sess.source_map().span_to_snippet(it.span)
.map(|snippet| snippet.find("const").unwrap_or(0))
.unwrap_or(0) as u32;
// `const` is 5 chars
let const_span = it.span.with_hi(BytePos(it.span.lo().0 + start + 5));
err.span_suggestion(
const_span,
"try a static value",
"pub static".to_owned(),
Applicability::MachineApplicable
);
err.emit();
}
}
2016-10-09 09:38:07 +05:30
_ => {}
}
}
}
declare_lint! {
MUTABLE_TRANSMUTES,
Deny,
"mutating transmuted &mut T from &T may cause undefined behavior"
}
declare_lint_pass!(MutableTransmutes => [MUTABLE_TRANSMUTES]);
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MutableTransmutes {
2019-02-08 20:35:41 +09:00
fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &hir::Expr) {
use rustc_target::spec::abi::Abi::RustIntrinsic;
2015-07-31 00:04:06 -07:00
let msg = "mutating transmuted &mut T from &T may cause undefined behavior, \
consider instead using an UnsafeCell";
2019-03-31 23:48:48 +02:00
match get_transmute_from_to(cx, expr).map(|(ty1, ty2)| (&ty1.sty, &ty2.sty)) {
Some((&ty::Ref(_, _, from_mt), &ty::Ref(_, _, to_mt))) => {
if to_mt == hir::Mutability::MutMutable &&
from_mt == hir::Mutability::MutImmutable {
cx.span_lint(MUTABLE_TRANSMUTES, expr.span, msg);
}
}
2016-10-09 09:38:07 +05:30
_ => (),
}
2016-10-09 09:38:07 +05:30
fn get_transmute_from_to<'a, 'tcx>
(cx: &LateContext<'a, 'tcx>,
expr: &hir::Expr)
2019-03-31 23:48:48 +02:00
-> Option<(Ty<'tcx>, Ty<'tcx>)> {
2018-07-11 20:05:29 +08:00
let def = if let hir::ExprKind::Path(ref qpath) = expr.node {
cx.tables.qpath_res(qpath, expr.hir_id)
} else {
return None;
};
if let Res::Def(DefKind::Fn, did) = def {
if !def_id_is_transmute(cx, did) {
return None;
}
2019-02-04 09:38:11 +01:00
let sig = cx.tables.node_type(expr.hir_id).fn_sig(cx.tcx);
let from = sig.inputs().skip_binder()[0];
let to = *sig.output().skip_binder();
2019-03-31 23:48:48 +02:00
return Some((from, to));
}
None
}
2019-02-08 20:35:41 +09:00
fn def_id_is_transmute(cx: &LateContext<'_, '_>, def_id: DefId) -> bool {
cx.tcx.fn_sig(def_id).abi() == RustIntrinsic &&
cx.tcx.item_name(def_id) == sym::transmute
}
}
}
2015-02-28 13:31:14 +01:00
declare_lint! {
UNSTABLE_FEATURES,
Allow,
"enabling unstable features (deprecated. do not use)"
2015-02-28 13:31:14 +01:00
}
declare_lint_pass!(
/// Forbids using the `#[feature(...)]` attribute
UnstableFeatures => [UNSTABLE_FEATURES]
);
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnstableFeatures {
2019-02-08 20:35:41 +09:00
fn check_attribute(&mut self, ctx: &LateContext<'_, '_>, attr: &ast::Attribute) {
if attr.check_name(sym::feature) {
if let Some(items) = attr.meta_item_list() {
for item in items {
ctx.span_lint(UNSTABLE_FEATURES, item.span(), "unstable feature");
}
}
}
}
}
declare_lint! {
UNIONS_WITH_DROP_FIELDS,
Warn,
"use of unions that contain fields with possibly non-trivial drop code"
}
declare_lint_pass!(
/// Lint for unions that contain fields with possibly non-trivial destructors.
UnionsWithDropFields => [UNIONS_WITH_DROP_FIELDS]
);
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnionsWithDropFields {
2019-02-08 20:35:41 +09:00
fn check_item(&mut self, ctx: &LateContext<'_, '_>, item: &hir::Item) {
2018-07-11 23:36:06 +08:00
if let hir::ItemKind::Union(ref vdata, _) = item.node {
for field in vdata.fields() {
2019-02-27 15:56:59 +01:00
let field_ty = ctx.tcx.type_of(
ctx.tcx.hir().local_def_id_from_hir_id(field.hir_id));
if field_ty.needs_drop(ctx.tcx, ctx.param_env) {
ctx.span_lint(UNIONS_WITH_DROP_FIELDS,
field.span,
"union contains a field with possibly non-trivial drop code, \
drop code of union fields is ignored when dropping the union");
return;
}
}
}
}
}
declare_lint! {
2018-03-08 13:23:52 -08:00
pub UNREACHABLE_PUB,
Allow,
"`pub` items not reachable from crate root"
}
declare_lint_pass!(
/// Lint for items marked `pub` that aren't reachable from other crates.
UnreachablePub => [UNREACHABLE_PUB]
);
impl UnreachablePub {
2019-02-26 15:11:59 +01:00
fn perform_lint(&self, cx: &LateContext<'_, '_>, what: &str, id: hir::HirId,
vis: &hir::Visibility, span: Span, exportable: bool) {
let mut applicability = Applicability::MachineApplicable;
match vis.node {
hir::VisibilityKind::Public if !cx.access_levels.is_reachable(id) => {
if span.ctxt().outer_expn_info().is_some() {
applicability = Applicability::MaybeIncorrect;
}
2018-08-18 12:14:09 +02:00
let def_span = cx.tcx.sess.source_map().def_span(span);
let mut err = cx.struct_span_lint(UNREACHABLE_PUB, def_span,
&format!("unreachable `pub` {}", what));
let replacement = if cx.tcx.features().crate_visibility_modifier {
"crate"
} else {
"pub(crate)"
}.to_owned();
err.span_suggestion(
vis.span,
"consider restricting its visibility",
replacement,
applicability,
);
if exportable {
err.help("or consider exporting it for use by other crates");
}
err.emit();
},
_ => {}
}
}
}
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnreachablePub {
2019-02-08 20:35:41 +09:00
fn check_item(&mut self, cx: &LateContext<'_, '_>, item: &hir::Item) {
2019-02-26 15:11:59 +01:00
self.perform_lint(cx, "item", item.hir_id, &item.vis, item.span, true);
}
2019-02-08 20:35:41 +09:00
fn check_foreign_item(&mut self, cx: &LateContext<'_, '_>, foreign_item: &hir::ForeignItem) {
2019-02-26 15:11:59 +01:00
self.perform_lint(cx, "item", foreign_item.hir_id, &foreign_item.vis,
foreign_item.span, true);
}
2019-02-08 20:35:41 +09:00
fn check_struct_field(&mut self, cx: &LateContext<'_, '_>, field: &hir::StructField) {
2019-02-26 15:11:59 +01:00
self.perform_lint(cx, "field", field.hir_id, &field.vis, field.span, false);
}
2019-02-08 20:35:41 +09:00
fn check_impl_item(&mut self, cx: &LateContext<'_, '_>, impl_item: &hir::ImplItem) {
2019-02-26 15:11:59 +01:00
self.perform_lint(cx, "item", impl_item.hir_id, &impl_item.vis, impl_item.span, false);
}
}
declare_lint! {
TYPE_ALIAS_BOUNDS,
Warn,
"bounds in type aliases are not enforced"
}
declare_lint_pass!(
/// Lint for trait and lifetime bounds in type aliases being mostly ignored.
/// They are relevant when using associated types, but otherwise neither checked
/// at definition site nor enforced at use site.
TypeAliasBounds => [TYPE_ALIAS_BOUNDS]
);
impl TypeAliasBounds {
fn is_type_variable_assoc(qpath: &hir::QPath) -> bool {
match *qpath {
hir::QPath::TypeRelative(ref ty, _) => {
// If this is a type variable, we found a `T::Assoc`.
match ty.node {
2018-07-11 22:41:03 +08:00
hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) => {
match path.res {
Res::Def(DefKind::TyParam, _) => true,
_ => false
}
}
_ => false
}
}
hir::QPath::Resolved(..) => false,
}
}
2019-02-08 20:35:41 +09:00
fn suggest_changing_assoc_types(ty: &hir::Ty, err: &mut DiagnosticBuilder<'_>) {
// Access to associates types should use `<T as Bound>::Assoc`, which does not need a
2018-03-19 18:01:14 +01:00
// bound. Let's see if this type does that.
2018-03-19 18:01:14 +01:00
// We use a HIR visitor to walk the type.
use rustc::hir::intravisit::{self, Visitor};
struct WalkAssocTypes<'a, 'db> where 'db {
err: &'a mut DiagnosticBuilder<'db>
}
impl<'a, 'db, 'v> Visitor<'v> for WalkAssocTypes<'a, 'db> {
fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'v>
{
intravisit::NestedVisitorMap::None
}
fn visit_qpath(&mut self, qpath: &'v hir::QPath, id: hir::HirId, span: Span) {
if TypeAliasBounds::is_type_variable_assoc(qpath) {
self.err.span_help(span,
2018-03-19 18:01:14 +01:00
"use fully disambiguated paths (i.e., `<T as Trait>::Assoc`) to refer to \
associated types in type aliases");
}
intravisit::walk_qpath(self, qpath, id, span)
}
}
// Let's go for a walk!
let mut visitor = WalkAssocTypes { err };
visitor.visit_ty(ty);
}
}
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeAliasBounds {
2019-02-08 20:35:41 +09:00
fn check_item(&mut self, cx: &LateContext<'_, '_>, item: &hir::Item) {
let (ty, type_alias_generics) = match item.node {
2018-07-11 23:36:06 +08:00
hir::ItemKind::Ty(ref ty, ref generics) => (&*ty, generics),
_ => return,
};
let mut suggested_changing_assoc_types = false;
// There must not be a where clause
if !type_alias_generics.where_clause.predicates.is_empty() {
let spans : Vec<_> = type_alias_generics.where_clause.predicates.iter()
.map(|pred| pred.span()).collect();
let mut err = cx.struct_span_lint(TYPE_ALIAS_BOUNDS, spans,
"where clauses are not enforced in type aliases");
err.help("the clause will not be checked when the type alias is used, \
and should be removed");
if !suggested_changing_assoc_types {
TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err);
suggested_changing_assoc_types = true;
}
err.emit();
}
// The parameters must not have bounds
for param in type_alias_generics.params.iter() {
2018-05-28 13:33:28 +01:00
let spans: Vec<_> = param.bounds.iter().map(|b| b.span()).collect();
if !spans.is_empty() {
let mut err = cx.struct_span_lint(
TYPE_ALIAS_BOUNDS,
spans,
"bounds on generic parameters are not enforced in type aliases",
);
err.help("the bound will not be checked when the type alias is used, \
and should be removed");
if !suggested_changing_assoc_types {
TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err);
suggested_changing_assoc_types = true;
}
err.emit();
}
}
}
}
2018-04-20 14:18:29 +02:00
declare_lint_pass!(
/// Lint constants that are erroneous.
/// Without this lint, we might not get any diagnostic if the constant is
/// unused within this crate, even though downstream crates can't use it
/// without producing an error.
UnusedBrokenConst => []
);
2019-02-08 20:35:41 +09:00
fn check_const(cx: &LateContext<'_, '_>, body_id: hir::BodyId) {
let def_id = cx.tcx.hir().body_owner_def_id(body_id);
let param_env = if cx.tcx.is_static(def_id) {
2018-08-01 08:39:30 +02:00
// Use the same param_env as `codegen_static_initializer`, to reuse the cache.
ty::ParamEnv::reveal_all()
} else {
cx.tcx.param_env(def_id)
};
2018-04-20 14:18:29 +02:00
let cid = ::rustc::mir::interpret::GlobalId {
instance: ty::Instance::mono(cx.tcx, def_id),
promoted: None
};
// trigger the query once for all constants since that will already report the errors
// FIXME: Use ensure here
let _ = cx.tcx.const_eval(param_env.and(cid));
2018-04-20 14:18:29 +02:00
}
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedBrokenConst {
2019-02-08 20:35:41 +09:00
fn check_item(&mut self, cx: &LateContext<'_, '_>, it: &hir::Item) {
2018-04-20 14:18:29 +02:00
match it.node {
2018-07-11 23:36:06 +08:00
hir::ItemKind::Const(_, body_id) => {
check_const(cx, body_id);
2018-04-20 14:18:29 +02:00
},
2018-06-04 18:32:06 +02:00
hir::ItemKind::Static(_, _, body_id) => {
check_const(cx, body_id);
2018-06-04 18:32:06 +02:00
},
2018-04-20 14:18:29 +02:00
_ => {},
}
}
}
2018-04-26 12:03:08 -07:00
2018-05-06 22:52:58 +01:00
declare_lint! {
TRIVIAL_BOUNDS,
Warn,
"these bounds don't depend on an type parameters"
}
declare_lint_pass!(
/// Lint for trait and lifetime bounds that don't depend on type parameters
/// which either do nothing, or stop the item from being used.
TrivialConstraints => [TRIVIAL_BOUNDS]
);
2018-05-06 22:52:58 +01:00
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TrivialConstraints {
fn check_item(
&mut self,
cx: &LateContext<'a, 'tcx>,
item: &'tcx hir::Item,
) {
use rustc::ty::fold::TypeFoldable;
use rustc::ty::Predicate::*;
if cx.tcx.features().trivial_bounds {
2019-02-27 17:35:24 +01:00
let def_id = cx.tcx.hir().local_def_id_from_hir_id(item.hir_id);
2018-05-06 22:52:58 +01:00
let predicates = cx.tcx.predicates_of(def_id);
for &(predicate, span) in &predicates.predicates {
let predicate_kind_name = match predicate {
2018-05-06 22:52:58 +01:00
Trait(..) => "Trait",
TypeOutlives(..) |
RegionOutlives(..) => "Lifetime",
// Ignore projections, as they can only be global
// if the trait bound is global
Projection(..) |
// Ignore bounds that a user can't type
WellFormed(..) |
ObjectSafe(..) |
ClosureKind(..) |
Subtype(..) |
ConstEvaluatable(..) => continue,
};
if predicate.is_global() {
2018-05-06 22:52:58 +01:00
cx.span_lint(
TRIVIAL_BOUNDS,
span,
2018-05-06 22:52:58 +01:00
&format!("{} bound {} does not depend on any type \
or lifetime parameters", predicate_kind_name, predicate),
);
}
}
}
}
}
2018-06-09 17:20:58 +02:00
declare_lint_pass!(
/// Does nothing as a lint pass, but registers some `Lint`s
/// which are used by other parts of the compiler.
SoftLints => [
WHILE_TRUE,
BOX_POINTERS,
NON_SHORTHAND_FIELD_PATTERNS,
UNSAFE_CODE,
MISSING_DOCS,
MISSING_COPY_IMPLEMENTATIONS,
MISSING_DEBUG_IMPLEMENTATIONS,
ANONYMOUS_PARAMETERS,
UNUSED_DOC_COMMENTS,
PLUGIN_AS_LIBRARY,
NO_MANGLE_CONST_ITEMS,
NO_MANGLE_GENERIC_ITEMS,
MUTABLE_TRANSMUTES,
UNSTABLE_FEATURES,
UNIONS_WITH_DROP_FIELDS,
UNREACHABLE_PUB,
TYPE_ALIAS_BOUNDS,
TRIVIAL_BOUNDS
]
);
declare_lint! {
pub ELLIPSIS_INCLUSIVE_RANGE_PATTERNS,
Warn,
"`...` range patterns are deprecated"
}
#[derive(Default)]
pub struct EllipsisInclusiveRangePatterns {
/// If `Some(_)`, suppress all subsequent pattern
/// warnings for better diagnostics.
node_id: Option<ast::NodeId>,
}
impl_lint_pass!(EllipsisInclusiveRangePatterns => [ELLIPSIS_INCLUSIVE_RANGE_PATTERNS]);
impl EarlyLintPass for EllipsisInclusiveRangePatterns {
fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &ast::Pat) {
if self.node_id.is_some() {
// Don't recursively warn about patterns inside range endpoints.
return
}
use self::ast::{PatKind, RangeEnd, RangeSyntax::DotDotDot};
/// If `pat` is a `...` pattern, return the start and end of the range, as well as the span
/// corresponding to the ellipsis.
fn matches_ellipsis_pat(pat: &ast::Pat) -> Option<(&P<Expr>, &P<Expr>, Span)> {
match &pat.node {
PatKind::Range(a, b, Spanned { span, node: RangeEnd::Included(DotDotDot), .. }) => {
Some((a, b, *span))
}
_ => None,
}
}
let (parenthesise, endpoints) = match &pat.node {
PatKind::Ref(subpat, _) => (true, matches_ellipsis_pat(&subpat)),
_ => (false, matches_ellipsis_pat(pat)),
};
if let Some((start, end, join)) = endpoints {
let msg = "`...` range patterns are deprecated";
let suggestion = "use `..=` for an inclusive range";
if parenthesise {
self.node_id = Some(pat.id);
let mut err = cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, pat.span, msg);
err.span_suggestion(
pat.span,
suggestion,
format!("&({}..={})", expr_to_string(&start), expr_to_string(&end)),
Applicability::MachineApplicable,
);
err.emit();
} else {
let mut err = cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, join, msg);
err.span_suggestion_short(
join,
suggestion,
"..=".to_owned(),
Applicability::MachineApplicable,
);
err.emit();
};
}
}
fn check_pat_post(&mut self, _cx: &EarlyContext<'_>, pat: &ast::Pat) {
if let Some(node_id) = self.node_id {
if pat.id == node_id {
self.node_id = None
}
}
}
}
2018-06-08 18:24:57 -07:00
declare_lint! {
2018-07-20 18:04:02 -07:00
UNNAMEABLE_TEST_ITEMS,
2018-06-08 18:24:57 -07:00
Warn,
2018-07-20 18:04:02 -07:00
"detects an item that cannot be named being marked as #[test_case]",
report_in_external_macro: true
}
pub struct UnnameableTestItems {
2019-02-27 17:35:24 +01:00
boundary: hir::HirId, // HirId of the item under which things are not nameable
2018-07-20 18:04:02 -07:00
items_nameable: bool,
2018-06-08 18:24:57 -07:00
}
impl_lint_pass!(UnnameableTestItems => [UNNAMEABLE_TEST_ITEMS]);
2018-07-20 18:04:02 -07:00
impl UnnameableTestItems {
pub fn new() -> Self {
Self {
2019-02-27 17:35:24 +01:00
boundary: hir::DUMMY_HIR_ID,
2018-07-20 18:04:02 -07:00
items_nameable: true
}
}
}
2018-06-08 18:24:57 -07:00
2018-07-20 18:04:02 -07:00
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnnameableTestItems {
2019-02-08 20:35:41 +09:00
fn check_item(&mut self, cx: &LateContext<'_, '_>, it: &hir::Item) {
2018-07-20 18:04:02 -07:00
if self.items_nameable {
if let hir::ItemKind::Mod(..) = it.node {}
else {
self.items_nameable = false;
2019-02-27 17:35:24 +01:00
self.boundary = it.hir_id;
2018-06-08 18:24:57 -07:00
}
2018-07-20 18:04:02 -07:00
return;
}
if let Some(attr) = attr::find_by_name(&it.attrs, sym::rustc_test_marker) {
2018-07-20 18:04:02 -07:00
cx.struct_span_lint(
UNNAMEABLE_TEST_ITEMS,
attr.span,
"cannot test inner items",
).emit();
}
}
2019-02-08 20:35:41 +09:00
fn check_item_post(&mut self, _cx: &LateContext<'_, '_>, it: &hir::Item) {
2019-02-27 17:35:24 +01:00
if !self.items_nameable && self.boundary == it.hir_id {
2018-07-20 18:04:02 -07:00
self.items_nameable = true;
}
2018-06-08 18:24:57 -07:00
}
}
2018-07-14 16:40:17 +02:00
declare_lint! {
pub KEYWORD_IDENTS,
2018-07-18 10:55:41 +02:00
Allow,
"detects edition keywords being used as an identifier"
2018-07-14 16:40:17 +02:00
}
declare_lint_pass!(
/// Check for uses of edition keywords used as an identifier.
KeywordIdents => [KEYWORD_IDENTS]
);
2018-07-14 16:40:17 +02:00
struct UnderMacro(bool);
impl KeywordIdents {
2019-02-08 20:35:41 +09:00
fn check_tokens(&mut self, cx: &EarlyContext<'_>, tokens: TokenStream) {
2018-07-14 16:40:17 +02:00
for tt in tokens.into_trees() {
match tt {
2019-06-05 14:17:56 +03:00
// Only report non-raw idents.
TokenTree::Token(token) => if let Some((ident, false)) = token.ident() {
self.check_ident_token(cx, UnderMacro(true), ident);
2018-07-14 16:40:17 +02:00
}
TokenTree::Delimited(_, _, tts) => {
self.check_tokens(cx, tts)
2018-07-14 16:40:17 +02:00
},
}
}
}
fn check_ident_token(&mut self,
cx: &EarlyContext<'_>,
UnderMacro(under_macro): UnderMacro,
ident: ast::Ident)
{
let next_edition = match cx.sess.edition() {
Edition::Edition2015 => {
match ident.name {
kw::Async | kw::Await | kw::Try => Edition::Edition2018,
// rust-lang/rust#56327: Conservatively do not
// attempt to report occurrences of `dyn` within
// macro definitions or invocations, because `dyn`
// can legitimately occur as a contextual keyword
// in 2015 code denoting its 2018 meaning, and we
// do not want rustfix to inject bugs into working
// code by rewriting such occurrences.
//
// But if we see `dyn` outside of a macro, we know
// its precise role in the parsed AST and thus are
// assured this is truly an attempt to use it as
// an identifier.
kw::Dyn if !under_macro => Edition::Edition2018,
_ => return,
}
}
// There are no new keywords yet for the 2018 edition and beyond.
_ => return,
};
// Don't lint `r#foo`.
if cx.sess.parse_sess.raw_identifier_spans.borrow().contains(&ident.span) {
return;
2018-07-14 16:40:17 +02:00
}
let mut lint = cx.struct_span_lint(
KEYWORD_IDENTS,
ident.span,
&format!("`{}` is a keyword in the {} edition",
ident.as_str(),
next_edition),
);
lint.span_suggestion(
ident.span,
"you can use a raw identifier to stay compatible",
format!("r#{}", ident.as_str()),
Applicability::MachineApplicable,
);
lint.emit()
2018-07-14 16:40:17 +02:00
}
}
in which inferable outlives-requirements are linted RFC 2093 (tracking issue #44493) lets us leave off commonsensically inferable `T: 'a` outlives requirements. (A separate feature-gate was split off for the case of 'static lifetimes, for which questions still remain.) Detecting these was requested as an idioms-2018 lint. It turns out that issuing a correct, autofixable suggestion here is somewhat subtle in the presence of other bounds and generic parameters. Basically, we want to handle these three cases: • One outlives-bound. We want to drop the bound altogether, including the colon— MyStruct<'a, T: 'a> ^^^^ help: remove this bound • An outlives bound first, followed by a trait bound. We want to delete the outlives bound and the following plus sign (and hopefully get the whitespace right, too)— MyStruct<'a, T: 'a + MyTrait> ^^^^^ help: remove this bound • An outlives bound after a trait bound. We want to delete the outlives lifetime and the preceding plus sign— MyStruct<'a, T: MyTrait + 'a> ^^^^^ help: remove this bound This gets (slightly) even more complicated in the case of where clauses, where we want to drop the where clause altogether if there's just the one bound. Hopefully the comments are enough to explain what's going on! A script (in Python, sorry) was used to generate the hopefully-sufficiently-exhaustive UI test input. Some of these are split off into a different file because rust-lang-nursery/rustfix#141 (and, causally upstream of that, #53934) prevents them from being `run-rustfix`-tested. We also make sure to include a UI test of a case (copied from RFC 2093) where the outlives-bound can't be inferred. Special thanks to Niko Matsakis for pointing out the `inferred_outlives_of` query, rather than blindly stripping outlives requirements as if we weren't a production compiler and didn't care. This concerns #52042.
2018-08-26 12:22:04 -07:00
impl EarlyLintPass for KeywordIdents {
fn check_mac_def(&mut self, cx: &EarlyContext<'_>, mac_def: &ast::MacroDef, _id: ast::NodeId) {
self.check_tokens(cx, mac_def.stream());
}
fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::Mac) {
self.check_tokens(cx, mac.node.tts.clone().into());
}
fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: ast::Ident) {
self.check_ident_token(cx, UnderMacro(false), ident);
}
}
declare_lint_pass!(ExplicitOutlivesRequirements => [EXPLICIT_OUTLIVES_REQUIREMENTS]);
in which inferable outlives-requirements are linted RFC 2093 (tracking issue #44493) lets us leave off commonsensically inferable `T: 'a` outlives requirements. (A separate feature-gate was split off for the case of 'static lifetimes, for which questions still remain.) Detecting these was requested as an idioms-2018 lint. It turns out that issuing a correct, autofixable suggestion here is somewhat subtle in the presence of other bounds and generic parameters. Basically, we want to handle these three cases: • One outlives-bound. We want to drop the bound altogether, including the colon— MyStruct<'a, T: 'a> ^^^^ help: remove this bound • An outlives bound first, followed by a trait bound. We want to delete the outlives bound and the following plus sign (and hopefully get the whitespace right, too)— MyStruct<'a, T: 'a + MyTrait> ^^^^^ help: remove this bound • An outlives bound after a trait bound. We want to delete the outlives lifetime and the preceding plus sign— MyStruct<'a, T: MyTrait + 'a> ^^^^^ help: remove this bound This gets (slightly) even more complicated in the case of where clauses, where we want to drop the where clause altogether if there's just the one bound. Hopefully the comments are enough to explain what's going on! A script (in Python, sorry) was used to generate the hopefully-sufficiently-exhaustive UI test input. Some of these are split off into a different file because rust-lang-nursery/rustfix#141 (and, causally upstream of that, #53934) prevents them from being `run-rustfix`-tested. We also make sure to include a UI test of a case (copied from RFC 2093) where the outlives-bound can't be inferred. Special thanks to Niko Matsakis for pointing out the `inferred_outlives_of` query, rather than blindly stripping outlives requirements as if we weren't a production compiler and didn't care. This concerns #52042.
2018-08-26 12:22:04 -07:00
impl ExplicitOutlivesRequirements {
fn collect_outlives_bound_spans(
&self,
2019-02-08 20:35:41 +09:00
cx: &LateContext<'_, '_>,
in which inferable outlives-requirements are linted RFC 2093 (tracking issue #44493) lets us leave off commonsensically inferable `T: 'a` outlives requirements. (A separate feature-gate was split off for the case of 'static lifetimes, for which questions still remain.) Detecting these was requested as an idioms-2018 lint. It turns out that issuing a correct, autofixable suggestion here is somewhat subtle in the presence of other bounds and generic parameters. Basically, we want to handle these three cases: • One outlives-bound. We want to drop the bound altogether, including the colon— MyStruct<'a, T: 'a> ^^^^ help: remove this bound • An outlives bound first, followed by a trait bound. We want to delete the outlives bound and the following plus sign (and hopefully get the whitespace right, too)— MyStruct<'a, T: 'a + MyTrait> ^^^^^ help: remove this bound • An outlives bound after a trait bound. We want to delete the outlives lifetime and the preceding plus sign— MyStruct<'a, T: MyTrait + 'a> ^^^^^ help: remove this bound This gets (slightly) even more complicated in the case of where clauses, where we want to drop the where clause altogether if there's just the one bound. Hopefully the comments are enough to explain what's going on! A script (in Python, sorry) was used to generate the hopefully-sufficiently-exhaustive UI test input. Some of these are split off into a different file because rust-lang-nursery/rustfix#141 (and, causally upstream of that, #53934) prevents them from being `run-rustfix`-tested. We also make sure to include a UI test of a case (copied from RFC 2093) where the outlives-bound can't be inferred. Special thanks to Niko Matsakis for pointing out the `inferred_outlives_of` query, rather than blindly stripping outlives requirements as if we weren't a production compiler and didn't care. This concerns #52042.
2018-08-26 12:22:04 -07:00
item_def_id: DefId,
param_name: &str,
bounds: &hir::GenericBounds,
infer_static: bool
) -> Vec<(usize, Span)> {
// For lack of a more elegant strategy for comparing the `ty::Predicate`s
// returned by this query with the params/bounds grabbed from the HIR—and
// with some regrets—we're going to covert the param/lifetime names to
// strings
let inferred_outlives = cx.tcx.inferred_outlives_of(item_def_id);
let ty_lt_names = inferred_outlives.iter().filter_map(|pred| {
let binder = match pred {
ty::Predicate::TypeOutlives(binder) => binder,
_ => { return None; }
};
let ty_outlives_pred = binder.skip_binder();
let ty_name = match ty_outlives_pred.0.sty {
ty::Param(param) => param.name.to_string(),
_ => { return None; }
};
let lt_name = match ty_outlives_pred.1 {
ty::RegionKind::ReEarlyBound(region) => {
region.name.to_string()
},
_ => { return None; }
};
Some((ty_name, lt_name))
}).collect::<Vec<_>>();
let mut bound_spans = Vec::new();
for (i, bound) in bounds.iter().enumerate() {
if let hir::GenericBound::Outlives(lifetime) = bound {
let is_static = match lifetime.name {
hir::LifetimeName::Static => true,
_ => false
};
if is_static && !infer_static {
// infer-outlives for 'static is still feature-gated (tracking issue #44493)
continue;
}
let lt_name = &lifetime.name.ident().to_string();
if ty_lt_names.contains(&(param_name.to_owned(), lt_name.to_owned())) {
bound_spans.push((i, bound.span()));
}
}
}
bound_spans
}
fn consolidate_outlives_bound_spans(
&self,
lo: Span,
bounds: &hir::GenericBounds,
bound_spans: Vec<(usize, Span)>
) -> Vec<Span> {
if bounds.is_empty() {
return Vec::new();
}
if bound_spans.len() == bounds.len() {
let (_, last_bound_span) = bound_spans[bound_spans.len()-1];
// If all bounds are inferable, we want to delete the colon, so
// start from just after the parameter (span passed as argument)
vec![lo.to(last_bound_span)]
} else {
let mut merged = Vec::new();
let mut last_merged_i = None;
let mut from_start = true;
for (i, bound_span) in bound_spans {
match last_merged_i {
// If the first bound is inferable, our span should also eat the trailing `+`
None if i == 0 => {
merged.push(bound_span.to(bounds[1].span().shrink_to_lo()));
last_merged_i = Some(0);
},
// If consecutive bounds are inferable, merge their spans
Some(h) if i == h+1 => {
if let Some(tail) = merged.last_mut() {
// Also eat the trailing `+` if the first
// more-than-one bound is inferable
let to_span = if from_start && i < bounds.len() {
bounds[i+1].span().shrink_to_lo()
} else {
bound_span
};
*tail = tail.to(to_span);
last_merged_i = Some(i);
} else {
bug!("another bound-span visited earlier");
}
},
_ => {
// When we find a non-inferable bound, subsequent inferable bounds
// won't be consecutive from the start (and we'll eat the leading
// `+` rather than the trailing one)
from_start = false;
merged.push(bounds[i-1].span().shrink_to_hi().to(bound_span));
last_merged_i = Some(i);
}
}
}
merged
}
}
}
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ExplicitOutlivesRequirements {
fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item) {
let infer_static = cx.tcx.features().infer_static_outlives_requirements;
2019-02-27 17:35:24 +01:00
let def_id = cx.tcx.hir().local_def_id_from_hir_id(item.hir_id);
in which inferable outlives-requirements are linted RFC 2093 (tracking issue #44493) lets us leave off commonsensically inferable `T: 'a` outlives requirements. (A separate feature-gate was split off for the case of 'static lifetimes, for which questions still remain.) Detecting these was requested as an idioms-2018 lint. It turns out that issuing a correct, autofixable suggestion here is somewhat subtle in the presence of other bounds and generic parameters. Basically, we want to handle these three cases: • One outlives-bound. We want to drop the bound altogether, including the colon— MyStruct<'a, T: 'a> ^^^^ help: remove this bound • An outlives bound first, followed by a trait bound. We want to delete the outlives bound and the following plus sign (and hopefully get the whitespace right, too)— MyStruct<'a, T: 'a + MyTrait> ^^^^^ help: remove this bound • An outlives bound after a trait bound. We want to delete the outlives lifetime and the preceding plus sign— MyStruct<'a, T: MyTrait + 'a> ^^^^^ help: remove this bound This gets (slightly) even more complicated in the case of where clauses, where we want to drop the where clause altogether if there's just the one bound. Hopefully the comments are enough to explain what's going on! A script (in Python, sorry) was used to generate the hopefully-sufficiently-exhaustive UI test input. Some of these are split off into a different file because rust-lang-nursery/rustfix#141 (and, causally upstream of that, #53934) prevents them from being `run-rustfix`-tested. We also make sure to include a UI test of a case (copied from RFC 2093) where the outlives-bound can't be inferred. Special thanks to Niko Matsakis for pointing out the `inferred_outlives_of` query, rather than blindly stripping outlives requirements as if we weren't a production compiler and didn't care. This concerns #52042.
2018-08-26 12:22:04 -07:00
if let hir::ItemKind::Struct(_, ref generics) = item.node {
let mut bound_count = 0;
let mut lint_spans = Vec::new();
for param in &generics.params {
let param_name = match param.kind {
hir::GenericParamKind::Lifetime { .. } => continue,
in which inferable outlives-requirements are linted RFC 2093 (tracking issue #44493) lets us leave off commonsensically inferable `T: 'a` outlives requirements. (A separate feature-gate was split off for the case of 'static lifetimes, for which questions still remain.) Detecting these was requested as an idioms-2018 lint. It turns out that issuing a correct, autofixable suggestion here is somewhat subtle in the presence of other bounds and generic parameters. Basically, we want to handle these three cases: • One outlives-bound. We want to drop the bound altogether, including the colon— MyStruct<'a, T: 'a> ^^^^ help: remove this bound • An outlives bound first, followed by a trait bound. We want to delete the outlives bound and the following plus sign (and hopefully get the whitespace right, too)— MyStruct<'a, T: 'a + MyTrait> ^^^^^ help: remove this bound • An outlives bound after a trait bound. We want to delete the outlives lifetime and the preceding plus sign— MyStruct<'a, T: MyTrait + 'a> ^^^^^ help: remove this bound This gets (slightly) even more complicated in the case of where clauses, where we want to drop the where clause altogether if there's just the one bound. Hopefully the comments are enough to explain what's going on! A script (in Python, sorry) was used to generate the hopefully-sufficiently-exhaustive UI test input. Some of these are split off into a different file because rust-lang-nursery/rustfix#141 (and, causally upstream of that, #53934) prevents them from being `run-rustfix`-tested. We also make sure to include a UI test of a case (copied from RFC 2093) where the outlives-bound can't be inferred. Special thanks to Niko Matsakis for pointing out the `inferred_outlives_of` query, rather than blindly stripping outlives requirements as if we weren't a production compiler and didn't care. This concerns #52042.
2018-08-26 12:22:04 -07:00
hir::GenericParamKind::Type { .. } => {
match param.name {
hir::ParamName::Fresh(_) => continue,
hir::ParamName::Error => continue,
hir::ParamName::Plain(name) => name.to_string(),
in which inferable outlives-requirements are linted RFC 2093 (tracking issue #44493) lets us leave off commonsensically inferable `T: 'a` outlives requirements. (A separate feature-gate was split off for the case of 'static lifetimes, for which questions still remain.) Detecting these was requested as an idioms-2018 lint. It turns out that issuing a correct, autofixable suggestion here is somewhat subtle in the presence of other bounds and generic parameters. Basically, we want to handle these three cases: • One outlives-bound. We want to drop the bound altogether, including the colon— MyStruct<'a, T: 'a> ^^^^ help: remove this bound • An outlives bound first, followed by a trait bound. We want to delete the outlives bound and the following plus sign (and hopefully get the whitespace right, too)— MyStruct<'a, T: 'a + MyTrait> ^^^^^ help: remove this bound • An outlives bound after a trait bound. We want to delete the outlives lifetime and the preceding plus sign— MyStruct<'a, T: MyTrait + 'a> ^^^^^ help: remove this bound This gets (slightly) even more complicated in the case of where clauses, where we want to drop the where clause altogether if there's just the one bound. Hopefully the comments are enough to explain what's going on! A script (in Python, sorry) was used to generate the hopefully-sufficiently-exhaustive UI test input. Some of these are split off into a different file because rust-lang-nursery/rustfix#141 (and, causally upstream of that, #53934) prevents them from being `run-rustfix`-tested. We also make sure to include a UI test of a case (copied from RFC 2093) where the outlives-bound can't be inferred. Special thanks to Niko Matsakis for pointing out the `inferred_outlives_of` query, rather than blindly stripping outlives requirements as if we weren't a production compiler and didn't care. This concerns #52042.
2018-08-26 12:22:04 -07:00
}
}
hir::GenericParamKind::Const { .. } => continue,
in which inferable outlives-requirements are linted RFC 2093 (tracking issue #44493) lets us leave off commonsensically inferable `T: 'a` outlives requirements. (A separate feature-gate was split off for the case of 'static lifetimes, for which questions still remain.) Detecting these was requested as an idioms-2018 lint. It turns out that issuing a correct, autofixable suggestion here is somewhat subtle in the presence of other bounds and generic parameters. Basically, we want to handle these three cases: • One outlives-bound. We want to drop the bound altogether, including the colon— MyStruct<'a, T: 'a> ^^^^ help: remove this bound • An outlives bound first, followed by a trait bound. We want to delete the outlives bound and the following plus sign (and hopefully get the whitespace right, too)— MyStruct<'a, T: 'a + MyTrait> ^^^^^ help: remove this bound • An outlives bound after a trait bound. We want to delete the outlives lifetime and the preceding plus sign— MyStruct<'a, T: MyTrait + 'a> ^^^^^ help: remove this bound This gets (slightly) even more complicated in the case of where clauses, where we want to drop the where clause altogether if there's just the one bound. Hopefully the comments are enough to explain what's going on! A script (in Python, sorry) was used to generate the hopefully-sufficiently-exhaustive UI test input. Some of these are split off into a different file because rust-lang-nursery/rustfix#141 (and, causally upstream of that, #53934) prevents them from being `run-rustfix`-tested. We also make sure to include a UI test of a case (copied from RFC 2093) where the outlives-bound can't be inferred. Special thanks to Niko Matsakis for pointing out the `inferred_outlives_of` query, rather than blindly stripping outlives requirements as if we weren't a production compiler and didn't care. This concerns #52042.
2018-08-26 12:22:04 -07:00
};
let bound_spans = self.collect_outlives_bound_spans(
cx, def_id, &param_name, &param.bounds, infer_static
);
bound_count += bound_spans.len();
lint_spans.extend(
self.consolidate_outlives_bound_spans(
param.span.shrink_to_hi(), &param.bounds, bound_spans
)
);
}
let mut where_lint_spans = Vec::new();
let mut dropped_predicate_count = 0;
let num_predicates = generics.where_clause.predicates.len();
for (i, where_predicate) in generics.where_clause.predicates.iter().enumerate() {
if let hir::WherePredicate::BoundPredicate(predicate) = where_predicate {
let param_name = match predicate.bounded_ty.node {
hir::TyKind::Path(ref qpath) => {
if let hir::QPath::Resolved(None, ty_param_path) = qpath {
ty_param_path.segments[0].ident.to_string()
} else {
continue;
}
},
_ => { continue; }
};
let bound_spans = self.collect_outlives_bound_spans(
cx, def_id, &param_name, &predicate.bounds, infer_static
);
bound_count += bound_spans.len();
let drop_predicate = bound_spans.len() == predicate.bounds.len();
if drop_predicate {
dropped_predicate_count += 1;
}
// If all the bounds on a predicate were inferable and there are
// further predicates, we want to eat the trailing comma
if drop_predicate && i + 1 < num_predicates {
let next_predicate_span = generics.where_clause.predicates[i+1].span();
where_lint_spans.push(
predicate.span.to(next_predicate_span.shrink_to_lo())
);
} else {
where_lint_spans.extend(
self.consolidate_outlives_bound_spans(
predicate.span.shrink_to_lo(),
&predicate.bounds,
bound_spans
)
);
}
}
}
// If all predicates are inferable, drop the entire clause
// (including the `where`)
if num_predicates > 0 && dropped_predicate_count == num_predicates {
let full_where_span = generics.span.shrink_to_hi()
.to(generics.where_clause.span()
.expect("span of (nonempty) where clause should exist"));
lint_spans.push(
full_where_span
);
} else {
lint_spans.extend(where_lint_spans);
}
if !lint_spans.is_empty() {
let mut err = cx.struct_span_lint(
EXPLICIT_OUTLIVES_REQUIREMENTS,
lint_spans.clone(),
"outlives requirements can be inferred"
);
err.multipart_suggestion(
in which inferable outlives-requirements are linted RFC 2093 (tracking issue #44493) lets us leave off commonsensically inferable `T: 'a` outlives requirements. (A separate feature-gate was split off for the case of 'static lifetimes, for which questions still remain.) Detecting these was requested as an idioms-2018 lint. It turns out that issuing a correct, autofixable suggestion here is somewhat subtle in the presence of other bounds and generic parameters. Basically, we want to handle these three cases: • One outlives-bound. We want to drop the bound altogether, including the colon— MyStruct<'a, T: 'a> ^^^^ help: remove this bound • An outlives bound first, followed by a trait bound. We want to delete the outlives bound and the following plus sign (and hopefully get the whitespace right, too)— MyStruct<'a, T: 'a + MyTrait> ^^^^^ help: remove this bound • An outlives bound after a trait bound. We want to delete the outlives lifetime and the preceding plus sign— MyStruct<'a, T: MyTrait + 'a> ^^^^^ help: remove this bound This gets (slightly) even more complicated in the case of where clauses, where we want to drop the where clause altogether if there's just the one bound. Hopefully the comments are enough to explain what's going on! A script (in Python, sorry) was used to generate the hopefully-sufficiently-exhaustive UI test input. Some of these are split off into a different file because rust-lang-nursery/rustfix#141 (and, causally upstream of that, #53934) prevents them from being `run-rustfix`-tested. We also make sure to include a UI test of a case (copied from RFC 2093) where the outlives-bound can't be inferred. Special thanks to Niko Matsakis for pointing out the `inferred_outlives_of` query, rather than blindly stripping outlives requirements as if we weren't a production compiler and didn't care. This concerns #52042.
2018-08-26 12:22:04 -07:00
if bound_count == 1 {
"remove this bound"
} else {
"remove these bounds"
},
lint_spans.into_iter().map(|span| (span, "".to_owned())).collect::<Vec<_>>(),
Applicability::MachineApplicable
);
err.emit();
}
}
}
}