2015-02-25 23:03:44 +11:00
|
|
|
//! Lints in the Rust compiler.
|
2015-02-25 22:44:44 +11:00
|
|
|
//!
|
2015-02-25 23:03:44 +11:00
|
|
|
//! 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.
|
2015-02-25 22:44:44 +11:00
|
|
|
//!
|
|
|
|
//! 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.
|
2015-02-25 23:03:44 +11:00
|
|
|
//! 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).
|
2015-02-25 22:44:44 +11:00
|
|
|
//!
|
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`.
|
2015-02-25 22:44:44 +11:00
|
|
|
|
2019-04-20 19:36:05 +03:00
|
|
|
use rustc::hir::def::{Res, DefKind};
|
2019-01-13 01:06:50 +01:00
|
|
|
use rustc::hir::def_id::{DefId, LOCAL_CRATE};
|
2019-05-25 10:28:17 +01:00
|
|
|
use rustc::ty::{self, Ty, TyCtxt};
|
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;
|
2017-07-26 21:51:09 -07:00
|
|
|
use lint::{LateContext, LintContext, LintArray};
|
2016-10-11 15:51:27 +13:00
|
|
|
use lint::{LintPass, LateLintPass, EarlyLintPass, EarlyContext};
|
2015-02-25 22:44:44 +11:00
|
|
|
|
2018-06-04 18:32:06 +02:00
|
|
|
use rustc::util::nodemap::FxHashSet;
|
2015-02-25 22:44:44 +11:00
|
|
|
|
2018-07-14 16:40:17 +02:00
|
|
|
use syntax::tokenstream::{TokenTree, TokenStream};
|
2016-10-18 18:04:28 +13:00
|
|
|
use syntax::ast;
|
2018-11-10 18:08:50 +00:00
|
|
|
use syntax::ptr::P;
|
|
|
|
use syntax::ast::Expr;
|
2019-01-24 15:49:03 -05:00
|
|
|
use syntax::attr::{self, HasAttrs};
|
2018-08-18 12:14:03 +02:00
|
|
|
use syntax::source_map::Spanned;
|
2018-08-24 13:48:20 -07:00
|
|
|
use syntax::edition::Edition;
|
2019-01-02 02:21:05 +03:00
|
|
|
use syntax::feature_gate::{AttributeGate, AttributeTemplate, AttributeType};
|
|
|
|
use syntax::feature_gate::{Stability, deprecated_attributes};
|
2017-10-11 23:08:48 -07:00
|
|
|
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};
|
2018-11-10 18:08:50 +00:00
|
|
|
use syntax::print::pprust::expr_to_string;
|
2019-01-15 06:54:28 +09:00
|
|
|
use syntax::visit::FnKind;
|
2015-02-25 22:44:44 +11:00
|
|
|
|
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;
|
2015-09-14 22:36:39 -04:00
|
|
|
|
2015-02-25 22:44:44 +11:00
|
|
|
// hardwired lints from librustc
|
|
|
|
pub use lint::builtin::*;
|
|
|
|
|
|
|
|
declare_lint! {
|
|
|
|
WHILE_TRUE,
|
|
|
|
Warn,
|
|
|
|
"suggest using `loop { }` instead of `while true { }`"
|
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
declare_lint_pass!(WhileTrue => [WHILE_TRUE]);
|
2015-02-25 22:44:44 +11:00
|
|
|
|
2016-12-07 13:14:47 +01:00
|
|
|
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 {
|
2016-02-08 17:06:20 +01:00
|
|
|
if let ast::LitKind::Bool(true) = lit.node {
|
2017-09-01 17:45:46 +02:00
|
|
|
if lit.span.ctxt() == SyntaxContext::empty() {
|
2017-09-29 23:52:51 -07:00
|
|
|
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);
|
2017-12-19 14:45:17 -08:00
|
|
|
let mut err = cx.struct_span_lint(WHILE_TRUE, condition_span, msg);
|
2019-01-25 16:03:27 -05:00
|
|
|
err.span_suggestion_short(
|
2018-08-01 20:30:04 -07:00
|
|
|
condition_span,
|
|
|
|
"use `loop`",
|
|
|
|
"loop".to_owned(),
|
|
|
|
Applicability::MachineApplicable
|
|
|
|
);
|
2017-09-29 23:52:51 -07:00
|
|
|
err.emit();
|
2017-09-01 17:45:46 +02:00
|
|
|
}
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
declare_lint! {
|
|
|
|
BOX_POINTERS,
|
|
|
|
Allow,
|
|
|
|
"use of owned (Box type) heap memory"
|
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
declare_lint_pass!(BoxPointers => [BOX_POINTERS]);
|
2015-02-25 22:44:44 +11:00
|
|
|
|
|
|
|
impl BoxPointers {
|
2019-06-11 12:47:08 +03:00
|
|
|
fn check_heap_type(&self, cx: &LateContext<'_, '_>, span: Span, ty: Ty<'_>) {
|
2015-06-25 23:42:17 +03:00
|
|
|
for leaf_ty in ty.walk() {
|
2017-01-21 17:40:31 +03:00
|
|
|
if leaf_ty.is_box() {
|
2015-06-25 23:42:17 +03:00
|
|
|
let m = format!("type uses owned (Box type) pointers: {}", ty);
|
|
|
|
cx.span_lint(BOX_POINTERS, span, &m);
|
|
|
|
}
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-07 13:14:47 +01:00
|
|
|
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) {
|
2015-02-25 22:44:44 +11:00
|
|
|
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);
|
2017-04-24 15:20:46 +03:00
|
|
|
self.check_heap_type(cx, it.span, cx.tcx.type_of(def_id))
|
2016-10-27 04:52:10 +03:00
|
|
|
}
|
2016-11-10 16:49:53 +02:00
|
|
|
_ => ()
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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);
|
2016-11-10 16:49:53 +02:00
|
|
|
self.check_heap_type(cx, struct_field.span,
|
2017-04-24 15:20:46 +03:00
|
|
|
cx.tcx.type_of(def_id));
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
}
|
2016-10-09 09:38:07 +05:30
|
|
|
_ => (),
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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);
|
2015-02-25 22:44:44 +11:00
|
|
|
self.check_heap_type(cx, e.span, ty);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
declare_lint! {
|
|
|
|
NON_SHORTHAND_FIELD_PATTERNS,
|
|
|
|
Warn,
|
2017-10-11 23:06:45 -07:00
|
|
|
"using `Struct { x: x }` instead of `Struct { x }` in a pattern"
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
declare_lint_pass!(NonShorthandFieldPatterns => [NON_SHORTHAND_FIELD_PATTERNS]);
|
2015-02-25 22:44:44 +11:00
|
|
|
|
2016-12-07 13:14:47 +01:00
|
|
|
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) {
|
2018-04-05 03:20:21 +03:00
|
|
|
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")
|
2019-04-20 19:36:05 +03:00
|
|
|
.variant_of_res(cx.tables.qpath_res(qpath, pat.hir_id));
|
2016-06-03 23:15:00 +03:00
|
|
|
for fieldpat in field_pats {
|
2015-02-28 13:31:14 +01:00
|
|
|
if fieldpat.node.is_shorthand {
|
2016-06-03 23:15:00 +03:00
|
|
|
continue;
|
2015-09-10 15:53:08 -04:00
|
|
|
}
|
2019-05-27 13:52:11 +10:00
|
|
|
if fieldpat.span.ctxt().outer_expn_info().is_some() {
|
2018-04-05 03:20:21 +03:00
|
|
|
// 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 {
|
2018-06-10 19:33:30 +03:00
|
|
|
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)) {
|
2017-10-11 23:06:45 -07:00
|
|
|
let mut err = cx.struct_span_lint(NON_SHORTHAND_FIELD_PATTERNS,
|
2016-10-09 09:38:07 +05:30
|
|
|
fieldpat.span,
|
2018-06-10 19:33:30 +03:00
|
|
|
&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,
|
|
|
|
':');
|
2019-01-25 16:03:27 -05:00
|
|
|
err.span_suggestion_short(
|
2018-08-01 20:30:04 -07:00
|
|
|
subspan,
|
|
|
|
"remove this",
|
|
|
|
ident.to_string(),
|
|
|
|
Applicability::MachineApplicable
|
|
|
|
);
|
2017-10-11 23:06:45 -07:00
|
|
|
err.emit();
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
declare_lint! {
|
|
|
|
UNSAFE_CODE,
|
|
|
|
Allow,
|
|
|
|
"usage of `unsafe` code"
|
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
declare_lint_pass!(UnsafeCode => [UNSAFE_CODE]);
|
2015-02-25 22:44:44 +11:00
|
|
|
|
2017-08-08 18:21:20 +03:00
|
|
|
impl UnsafeCode {
|
2019-02-08 20:35:41 +09:00
|
|
|
fn report_unsafe(&self, cx: &EarlyContext<'_>, span: Span, desc: &'static str) {
|
2019-02-28 22:43:53 +00:00
|
|
|
// This comes from a macro that has `#[allow_internal_unsafe]`.
|
2017-08-08 18:21:20 +03:00
|
|
|
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) {
|
2019-05-08 13:21:18 +10:00
|
|
|
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 {
|
2019-02-28 22:43:53 +00:00
|
|
|
// 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) {
|
2017-08-08 18:21:20 +03:00
|
|
|
self.report_unsafe(cx, blk.span, "usage of an `unsafe` block");
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-08 20:35:41 +09:00
|
|
|
fn check_item(&mut self, cx: &EarlyContext<'_>, it: &ast::Item) {
|
2015-02-25 22:44:44 +11:00
|
|
|
match it.node {
|
2019-01-15 06:54:28 +09:00
|
|
|
ast::ItemKind::Trait(_, ast::Unsafety::Unsafe, ..) => {
|
2017-08-08 18:21:20 +03:00
|
|
|
self.report_unsafe(cx, it.span, "declaration of an `unsafe` trait")
|
2016-10-09 09:38:07 +05:30
|
|
|
}
|
2015-02-25 22:44:44 +11:00
|
|
|
|
2019-01-15 06:54:28 +09:00
|
|
|
ast::ItemKind::Impl(ast::Unsafety::Unsafe, ..) => {
|
2017-08-08 18:21:20 +03:00
|
|
|
self.report_unsafe(cx, it.span, "implementation of an `unsafe` trait")
|
2016-10-09 09:38:07 +05:30
|
|
|
}
|
2015-02-25 22:44:44 +11:00
|
|
|
|
|
|
|
_ => 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) {
|
2015-02-25 22:44:44 +11:00
|
|
|
match fk {
|
2019-01-15 06:54:28 +09:00
|
|
|
FnKind::ItemFn(_, ast::FnHeader { unsafety: ast::Unsafety::Unsafe, .. }, ..) => {
|
2017-08-08 18:21:20 +03:00
|
|
|
self.report_unsafe(cx, span, "declaration of an `unsafe` function")
|
2016-10-09 09:38:07 +05:30
|
|
|
}
|
2015-02-25 22:44:44 +11:00
|
|
|
|
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 {
|
2017-08-08 18:21:20 +03:00
|
|
|
self.report_unsafe(cx, span, "implementation of an `unsafe` method")
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
2016-10-09 09:38:07 +05:30
|
|
|
}
|
2015-02-25 22:44:44 +11:00
|
|
|
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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 {
|
2017-08-08 18:21:20 +03:00
|
|
|
self.report_unsafe(cx, item.span, "declaration of an `unsafe` method")
|
2015-03-10 12:28:44 +02:00
|
|
|
}
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
declare_lint! {
|
2018-07-05 20:06:33 +02:00
|
|
|
pub MISSING_DOCS,
|
2015-02-25 22:44:44 +11:00
|
|
|
Allow,
|
2018-08-21 16:29:08 +05:30
|
|
|
"detects missing documentation for public members",
|
|
|
|
report_in_external_macro: true
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
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.
|
2015-02-25 22:44:44 +11:00
|
|
|
doc_hidden_stack: Vec<bool>,
|
2015-03-29 23:41:54 -04:00
|
|
|
|
|
|
|
/// 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>,
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
impl_lint_pass!(MissingDoc => [MISSING_DOCS]);
|
|
|
|
|
2018-12-01 14:46:25 +01:00
|
|
|
fn has_doc(attr: &ast::Attribute) -> bool {
|
2019-05-08 13:21:18 +10:00
|
|
|
if !attr.check_name(sym::doc) {
|
2018-12-01 14:46:25 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if attr.is_value_str() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(list) = attr.meta_item_list() {
|
|
|
|
for meta in list {
|
2019-05-08 13:21:18 +10:00
|
|
|
if meta.check_name(sym::include) || meta.check_name(sym::hidden) {
|
2018-12-01 14:46:25 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
false
|
|
|
|
}
|
|
|
|
|
2015-02-25 22:44:44 +11:00
|
|
|
impl MissingDoc {
|
|
|
|
pub fn new() -> MissingDoc {
|
|
|
|
MissingDoc {
|
2016-10-09 09:38:07 +05:30
|
|
|
doc_hidden_stack: vec![false],
|
2018-08-18 13:55:43 +03:00
|
|
|
private_traits: FxHashSet::default(),
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2015-02-25 22:44:44 +11:00
|
|
|
// 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;
|
|
|
|
}
|
2015-02-25 22:44:44 +11:00
|
|
|
|
|
|
|
// `#[doc(hidden)]` disables missing_docs check.
|
2015-02-28 13:31:14 +01:00
|
|
|
if self.doc_hidden() {
|
|
|
|
return;
|
|
|
|
}
|
2015-02-25 22:44:44 +11:00
|
|
|
|
|
|
|
// 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
|
2019-02-28 22:43:53 +00:00
|
|
|
// have a `NodeId`).
|
2015-11-19 14:16:35 +03:00
|
|
|
if let Some(id) = id {
|
2019-03-11 11:03:19 +01:00
|
|
|
if !cx.access_levels.is_exported(id) {
|
2015-02-25 22:44:44 +11:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-19 15:25:06 -06:00
|
|
|
let has_doc = attrs.iter().any(|a| has_doc(a));
|
2015-02-25 22:44:44 +11:00
|
|
|
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));
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-07 13:14:47 +01:00
|
|
|
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| {
|
2019-05-08 13:21:18 +10:00
|
|
|
attr.check_name(sym::doc) &&
|
2016-10-09 09:38:07 +05:30
|
|
|
match attr.meta_item_list() {
|
2015-02-25 22:44:44 +11:00
|
|
|
None => false,
|
2019-05-08 13:21:18 +10:00
|
|
|
Some(l) => attr::list_contains_name(&l, sym::hidden),
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
});
|
|
|
|
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]) {
|
2015-02-25 22:44:44 +11:00
|
|
|
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");
|
2018-12-01 14:46:25 +01:00
|
|
|
|
|
|
|
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");
|
|
|
|
}
|
|
|
|
}
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
|
2019-02-08 20:35:41 +09:00
|
|
|
fn check_item(&mut self, cx: &LateContext<'_, '_>, it: &hir::Item) {
|
2015-02-25 22:44:44 +11:00
|
|
|
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) => {
|
2019-02-28 22:43:53 +00:00
|
|
|
// Issue #11592: traits are always considered exported, even when private.
|
2018-03-21 01:58:25 +03:00
|
|
|
if let hir::VisibilityKind::Inherited = it.vis.node {
|
2019-02-26 11:04:58 +01:00
|
|
|
self.private_traits.insert(it.hir_id);
|
2016-12-04 04:21:06 +02:00
|
|
|
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);
|
2015-03-29 23:41:54 -04:00
|
|
|
}
|
2016-10-09 09:38:07 +05:30
|
|
|
return;
|
2015-03-29 23:41:54 -04:00
|
|
|
}
|
|
|
|
"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) => {
|
2019-02-28 22:43:53 +00:00
|
|
|
// If the trait is private, add the impl items to `private_traits` so they don't get
|
2015-03-29 23:41:54 -04:00
|
|
|
// reported for missing docs.
|
2019-04-20 19:36:05 +03:00
|
|
|
let real_trait = trait_ref.path.res.def_id();
|
2019-03-04 09:00:30 +01:00
|
|
|
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)) => {
|
2018-03-21 01:58:25 +03:00
|
|
|
if let hir::VisibilityKind::Inherited = item.vis.node {
|
2016-11-10 09:47:00 -05:00
|
|
|
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
|
|
|
}
|
2015-09-04 13:52:28 -04:00
|
|
|
}
|
2016-10-09 09:38:07 +05:30
|
|
|
}
|
|
|
|
_ => {}
|
2015-09-04 13:52:28 -04:00
|
|
|
}
|
2015-03-29 23:41:54 -04:00
|
|
|
}
|
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,
|
2015-02-25 22:44:44 +11:00
|
|
|
};
|
2015-03-29 23:41:54 -04:00
|
|
|
|
2019-02-26 11:04:58 +01:00
|
|
|
self.check_missing_docs_attrs(cx, Some(it.hir_id), &it.attrs, it.span, desc);
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
}
|
2015-03-29 23:41:54 -04:00
|
|
|
|
2015-03-10 12:28:44 +02:00
|
|
|
let desc = match trait_item.node {
|
2016-12-04 04:21:06 +02:00
|
|
|
hir::TraitItemKind::Const(..) => "an associated constant",
|
|
|
|
hir::TraitItemKind::Method(..) => "a trait method",
|
|
|
|
hir::TraitItemKind::Type(..) => "an associated type",
|
2015-03-10 12:28:44 +02:00
|
|
|
};
|
2015-03-29 23:41:54 -04:00
|
|
|
|
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),
|
2015-03-10 12:28:44 +02:00
|
|
|
&trait_item.attrs,
|
2016-10-09 09:38:07 +05:30
|
|
|
trait_item.span,
|
|
|
|
desc);
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
|
2019-02-08 20:35:41 +09:00
|
|
|
fn check_impl_item(&mut self, cx: &LateContext<'_, '_>, impl_item: &hir::ImplItem) {
|
2015-03-10 12:28:44 +02:00
|
|
|
// 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 {
|
2015-03-10 12:28:44 +02:00
|
|
|
return;
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
2015-03-10 12:28:44 +02:00
|
|
|
|
|
|
|
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",
|
2015-03-10 12:28:44 +02:00
|
|
|
};
|
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),
|
2015-03-10 12:28:44 +02:00
|
|
|
&impl_item.attrs,
|
2016-10-09 09:38:07 +05:30
|
|
|
impl_item.span,
|
|
|
|
desc);
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
|
2019-02-08 20:35:41 +09:00
|
|
|
fn check_struct_field(&mut self, cx: &LateContext<'_, '_>, sf: &hir::StructField) {
|
2016-02-27 11:34:29 +03:00
|
|
|
if !sf.is_positional() {
|
2017-05-27 10:06:15 -06:00
|
|
|
self.check_missing_docs_attrs(cx,
|
2019-02-26 11:04:58 +01:00
|
|
|
Some(sf.hir_id),
|
2017-05-27 10:06:15 -06:00
|
|
|
&sf.attrs,
|
|
|
|
sf.span,
|
|
|
|
"a struct field")
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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,
|
2019-03-21 23:38:50 +01:00
|
|
|
Some(v.node.id),
|
2016-10-09 09:38:07 +05:30
|
|
|
&v.node.attrs,
|
|
|
|
v.span,
|
|
|
|
"a variant");
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
declare_lint! {
|
|
|
|
pub MISSING_COPY_IMPLEMENTATIONS,
|
|
|
|
Allow,
|
|
|
|
"detects potentially-forgotten implementations of `Copy`"
|
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
declare_lint_pass!(MissingCopyImplementations => [MISSING_COPY_IMPLEMENTATIONS]);
|
2015-02-25 22:44:44 +11:00
|
|
|
|
2016-12-07 13:14:47 +01:00
|
|
|
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) {
|
2019-03-11 11:03:19 +01:00
|
|
|
if !cx.access_levels.is_reachable(item.hir_id) {
|
2015-02-28 13:31:14 +01:00
|
|
|
return;
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
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) => {
|
2017-10-16 21:07:26 +02:00
|
|
|
if !ast_generics.params.is_empty() {
|
2015-02-28 13:31:14 +01:00
|
|
|
return;
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
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));
|
2016-10-24 18:23:29 -06:00
|
|
|
(def, cx.tcx.mk_adt(def, cx.tcx.intern_substs(&[])))
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
2018-07-11 23:36:06 +08:00
|
|
|
hir::ItemKind::Union(_, ref ast_generics) => {
|
2017-10-16 21:07:26 +02:00
|
|
|
if !ast_generics.params.is_empty() {
|
2016-08-10 21:00:17 +03: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));
|
2016-10-24 18:23:29 -06:00
|
|
|
(def, cx.tcx.mk_adt(def, cx.tcx.intern_substs(&[])))
|
2016-08-10 21:00:17 +03:00
|
|
|
}
|
2018-07-11 23:36:06 +08:00
|
|
|
hir::ItemKind::Enum(_, ref ast_generics) => {
|
2017-10-16 21:07:26 +02:00
|
|
|
if !ast_generics.params.is_empty() {
|
2015-02-28 13:31:14 +01:00
|
|
|
return;
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
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));
|
2016-10-24 18:23:29 -06:00
|
|
|
(def, cx.tcx.mk_adt(def, cx.tcx.intern_substs(&[])))
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
_ => return,
|
|
|
|
};
|
2017-02-19 14:46:29 +02:00
|
|
|
if def.has_dtor(cx.tcx) {
|
2016-10-09 09:38:07 +05:30
|
|
|
return;
|
|
|
|
}
|
2018-02-10 13:18:02 -05:00
|
|
|
let param_env = ty::ParamEnv::empty();
|
2018-11-20 11:59:06 -05:00
|
|
|
if ty.is_copy_modulo_regions(cx.tcx, param_env, item.span) {
|
2015-02-28 13:31:14 +01:00
|
|
|
return;
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
2018-05-08 11:38:35 -03:00
|
|
|
if param_env.can_type_implement_copy(cx.tcx, ty).is_ok() {
|
2015-02-25 22:44:44 +11:00
|
|
|
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"
|
|
|
|
}
|
|
|
|
|
2019-04-24 13:47:26 +01:00
|
|
|
#[derive(Default)]
|
2015-02-25 22:44:44 +11:00
|
|
|
pub struct MissingDebugImplementations {
|
2019-02-27 17:35:24 +01:00
|
|
|
impling_types: Option<HirIdSet>,
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
impl_lint_pass!(MissingDebugImplementations => [MISSING_DEBUG_IMPLEMENTATIONS]);
|
|
|
|
|
2016-12-07 13:14:47 +01:00
|
|
|
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) {
|
2019-03-11 11:03:19 +01:00
|
|
|
if !cx.access_levels.is_reachable(item.hir_id) {
|
2015-02-25 22:44:44 +11:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
match item.node {
|
2018-07-11 23:36:06 +08:00
|
|
|
hir::ItemKind::Struct(..) |
|
|
|
|
hir::ItemKind::Union(..) |
|
|
|
|
hir::ItemKind::Enum(..) => {}
|
2015-02-25 22:44:44 +11:00
|
|
|
_ => return,
|
|
|
|
}
|
|
|
|
|
2017-08-31 08:57:41 -07:00
|
|
|
let debug = match cx.tcx.lang_items().debug_trait() {
|
2015-02-25 22:44:44 +11:00
|
|
|
Some(debug) => debug,
|
|
|
|
None => return,
|
|
|
|
};
|
|
|
|
|
|
|
|
if self.impling_types.is_none() {
|
2019-02-27 17:35:24 +01:00
|
|
|
let mut impls = HirIdSet::default();
|
2017-08-07 20:50:34 +00:00
|
|
|
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);
|
2015-04-21 19:00:12 +03:00
|
|
|
}
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
2015-04-21 19:00:12 +03:00
|
|
|
});
|
|
|
|
|
2015-02-25 22:44:44 +11:00
|
|
|
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) {
|
2015-02-25 22:44:44 +11:00
|
|
|
cx.span_lint(MISSING_DEBUG_IMPLEMENTATIONS,
|
|
|
|
item.span,
|
|
|
|
"type does not implement `fmt::Debug`; consider adding #[derive(Debug)] \
|
|
|
|
or a manual implementation")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-02 04:38:46 +02:00
|
|
|
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"
|
2017-05-02 04:38:46 +02:00
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
declare_lint_pass!(
|
|
|
|
/// Checks for use of anonymous parameters (RFC 1685).
|
|
|
|
AnonymousParameters => [ANONYMOUS_PARAMETERS]
|
|
|
|
);
|
2017-05-02 04:38:46 +02:00
|
|
|
|
|
|
|
impl EarlyLintPass for AnonymousParameters {
|
2019-02-08 20:35:41 +09:00
|
|
|
fn check_trait_item(&mut self, cx: &EarlyContext<'_>, it: &ast::TraitItem) {
|
2017-05-02 04:38:46 +02:00
|
|
|
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."
|
2019-01-25 16:03:27 -05:00
|
|
|
).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();
|
2017-05-02 04:38:46 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-08 14:53:55 +01:00
|
|
|
/// Check for use of attributes which have been deprecated.
|
2016-10-11 15:51:27 +13:00
|
|
|
#[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.
|
2019-04-10 19:47:55 +02:00
|
|
|
depr_attrs: Vec<&'static (Symbol, AttributeType, AttributeTemplate, AttributeGate)>,
|
2016-10-18 18:04:28 +13:00
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
impl_lint_pass!(DeprecatedAttr => []);
|
|
|
|
|
2016-10-18 18:04:28 +13:00
|
|
|
impl DeprecatedAttr {
|
|
|
|
pub fn new() -> DeprecatedAttr {
|
|
|
|
DeprecatedAttr {
|
|
|
|
depr_attrs: deprecated_attributes(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-10-11 15:51:27 +13:00
|
|
|
|
|
|
|
impl EarlyLintPass for DeprecatedAttr {
|
2019-02-08 20:35:41 +09:00
|
|
|
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) {
|
2019-01-02 02:21:05 +03:00
|
|
|
for &&(n, _, _, ref g) in &self.depr_attrs {
|
2019-04-10 19:47:55 +02:00
|
|
|
if attr.ident().map(|ident| ident.name) == Some(n) {
|
2018-09-17 03:40:31 +00:00
|
|
|
if let &AttributeGate::Gated(Stability::Deprecated(link, suggestion),
|
2016-10-18 18:04:28 +13:00
|
|
|
ref name,
|
|
|
|
ref reason,
|
|
|
|
_) = g {
|
2017-09-29 23:44:41 -07:00
|
|
|
let msg = format!("use of deprecated attribute `{}`: {}. See {}",
|
|
|
|
name, reason, link);
|
|
|
|
let mut err = cx.struct_span_lint(DEPRECATED, attr.span, &msg);
|
2019-01-25 16:03:27 -05:00
|
|
|
err.span_suggestion_short(
|
2018-08-01 20:30:04 -07:00
|
|
|
attr.span,
|
2018-09-17 03:40:31 +00:00
|
|
|
suggestion.unwrap_or("remove this attribute"),
|
2018-08-23 10:14:52 +02:00
|
|
|
String::new(),
|
2018-08-01 20:30:04 -07:00
|
|
|
Applicability::MachineApplicable
|
|
|
|
);
|
2017-09-29 23:44:41 -07:00
|
|
|
err.emit();
|
2016-10-11 15:51:27 +13:00
|
|
|
}
|
|
|
|
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"
|
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
declare_lint_pass!(UnusedDocComment => [UNUSED_DOC_COMMENTS]);
|
2017-07-16 00:17:35 +02:00
|
|
|
|
|
|
|
impl UnusedDocComment {
|
2019-01-24 15:49:03 -05:00
|
|
|
fn warn_if_doc(
|
|
|
|
&self,
|
|
|
|
cx: &EarlyContext<'_>,
|
|
|
|
node_span: Span,
|
|
|
|
node_kind: &str,
|
|
|
|
is_macro_expansion: bool,
|
|
|
|
attrs: &[ast::Attribute]
|
|
|
|
) {
|
2019-01-23 13:44:43 -05:00
|
|
|
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);
|
|
|
|
|
2019-05-08 13:21:18 +10:00
|
|
|
if attr.check_name(sym::doc) {
|
2019-01-24 15:49:03 -05:00
|
|
|
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();
|
2019-01-23 13:44:43 -05:00
|
|
|
}
|
2017-07-16 00:17:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl EarlyLintPass for UnusedDocComment {
|
2019-01-24 15:49:03 -05:00
|
|
|
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) {
|
2019-01-24 15:49:03 -05:00
|
|
|
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) {
|
2019-01-24 15:49:03 -05:00
|
|
|
self.warn_if_doc(cx, expr.span, "expressions", false, &expr.attrs);
|
2017-07-16 00:17:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-25 22:44:44 +11:00
|
|
|
declare_lint! {
|
|
|
|
PLUGIN_AS_LIBRARY,
|
|
|
|
Warn,
|
|
|
|
"compiler plugin used as ordinary library in non-plugin crate"
|
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
declare_lint_pass!(PluginAsLibrary => [PLUGIN_AS_LIBRARY]);
|
2015-02-25 22:44:44 +11:00
|
|
|
|
2016-12-07 13:14:47 +01:00
|
|
|
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() {
|
2015-02-25 22:44:44 +11:00
|
|
|
// 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(..) => (),
|
2015-02-25 22:44:44 +11:00
|
|
|
_ => return,
|
|
|
|
};
|
|
|
|
|
2019-02-27 17:35:24 +01:00
|
|
|
let def_id = cx.tcx.hir().local_def_id_from_hir_id(it.hir_id);
|
2017-09-08 13:51:57 -07:00
|
|
|
let prfn = match cx.tcx.extern_mod_stmt_cnum(def_id) {
|
2017-08-28 17:30:27 -07:00
|
|
|
Some(cnum) => cx.tcx.plugin_registrar_fn(cnum),
|
2015-02-25 22:44:44 +11:00
|
|
|
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");
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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"
|
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
declare_lint_pass!(InvalidNoMangleItems => [NO_MANGLE_CONST_ITEMS, NO_MANGLE_GENERIC_ITEMS]);
|
2015-02-25 22:44:44 +11:00
|
|
|
|
2016-12-07 13:14:47 +01:00
|
|
|
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) {
|
2015-02-25 22:44:44 +11:00
|
|
|
match it.node {
|
2018-07-11 23:36:06 +08:00
|
|
|
hir::ItemKind::Fn(.., ref generics, _) => {
|
2019-05-08 13:21:18 +10:00
|
|
|
if let Some(no_mangle_attr) = attr::find_by_name(&it.attrs, sym::no_mangle) {
|
2018-05-26 13:11:39 +01:00
|
|
|
for param in &generics.params {
|
|
|
|
match param.kind {
|
|
|
|
GenericParamKind::Lifetime { .. } => {}
|
2019-02-15 22:25:42 +00:00
|
|
|
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",
|
|
|
|
);
|
2019-01-25 16:03:27 -05:00
|
|
|
err.span_suggestion_short(
|
2018-08-01 20:30:04 -07:00
|
|
|
no_mangle_attr.span,
|
|
|
|
"remove this attribute",
|
2018-08-23 10:14:52 +02:00
|
|
|
String::new(),
|
2018-08-01 20:30:04 -07:00
|
|
|
// Use of `#[no_mangle]` suggests FFI intent; correct
|
|
|
|
// fix may be to monomorphize source by hand
|
|
|
|
Applicability::MaybeIncorrect
|
|
|
|
);
|
2018-05-26 13:11:39 +01:00
|
|
|
err.emit();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2015-12-09 01:48:40 +09:00
|
|
|
}
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
2016-10-09 09:38:07 +05:30
|
|
|
}
|
2018-07-11 23:36:06 +08:00
|
|
|
hir::ItemKind::Const(..) => {
|
2019-05-08 13:21:18 +10:00
|
|
|
if attr::contains_name(&it.attrs, sym::no_mangle) {
|
2015-02-25 22:44:44 +11:00
|
|
|
// Const items do not refer to a particular location in memory, and therefore
|
|
|
|
// don't have anything to attach a symbol to
|
2017-10-11 23:08:48 -07:00
|
|
|
let msg = "const items should never be #[no_mangle]";
|
|
|
|
let mut err = cx.struct_span_lint(NO_MANGLE_CONST_ITEMS, it.span, msg);
|
2018-01-07 18:57:32 -08:00
|
|
|
|
|
|
|
// 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)
|
2018-01-07 18:57:32 -08:00
|
|
|
.map(|snippet| snippet.find("const").unwrap_or(0))
|
|
|
|
.unwrap_or(0) as u32;
|
2017-10-11 23:08:48 -07:00
|
|
|
// `const` is 5 chars
|
2018-01-07 18:57:32 -08:00
|
|
|
let const_span = it.span.with_hi(BytePos(it.span.lo().0 + start + 5));
|
2019-01-25 16:03:27 -05:00
|
|
|
err.span_suggestion(
|
2018-08-01 20:30:04 -07:00
|
|
|
const_span,
|
|
|
|
"try a static value",
|
|
|
|
"pub static".to_owned(),
|
|
|
|
Applicability::MachineApplicable
|
|
|
|
);
|
2017-10-11 23:08:48 -07:00
|
|
|
err.emit();
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
}
|
2016-10-09 09:38:07 +05:30
|
|
|
_ => {}
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-13 14:49:10 -07:00
|
|
|
declare_lint! {
|
|
|
|
MUTABLE_TRANSMUTES,
|
|
|
|
Deny,
|
|
|
|
"mutating transmuted &mut T from &T may cause undefined behavior"
|
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
declare_lint_pass!(MutableTransmutes => [MUTABLE_TRANSMUTES]);
|
2015-04-13 14:49:10 -07:00
|
|
|
|
2016-12-07 13:14:47 +01:00
|
|
|
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) {
|
2018-04-25 19:30:39 +03:00
|
|
|
use rustc_target::spec::abi::Abi::RustIntrinsic;
|
2015-07-31 00:04:06 -07:00
|
|
|
|
2016-10-31 19:41:22 +01:00
|
|
|
let msg = "mutating transmuted &mut T from &T may cause undefined behavior, \
|
2015-04-13 14:49:10 -07:00
|
|
|
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)) {
|
2018-08-22 01:35:02 +01:00
|
|
|
Some((&ty::Ref(_, _, from_mt), &ty::Ref(_, _, to_mt))) => {
|
2018-05-02 15:21:05 +02:00
|
|
|
if to_mt == hir::Mutability::MutMutable &&
|
|
|
|
from_mt == hir::Mutability::MutImmutable {
|
2015-04-13 14:49:10 -07:00
|
|
|
cx.span_lint(MUTABLE_TRANSMUTES, expr.span, msg);
|
|
|
|
}
|
|
|
|
}
|
2016-10-09 09:38:07 +05:30
|
|
|
_ => (),
|
2015-04-13 14:49:10 -07:00
|
|
|
}
|
|
|
|
|
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 {
|
2019-04-20 19:36:05 +03:00
|
|
|
cx.tables.qpath_res(qpath, expr.hir_id)
|
2016-11-25 13:21:19 +02:00
|
|
|
} else {
|
|
|
|
return None;
|
|
|
|
};
|
2019-04-20 19:36:05 +03:00
|
|
|
if let Res::Def(DefKind::Fn, did) = def {
|
2015-04-13 14:49:10 -07:00
|
|
|
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);
|
2017-05-13 17:11:52 +03:00
|
|
|
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));
|
2015-04-13 14:49:10 -07:00
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
2019-02-08 20:35:41 +09:00
|
|
|
fn def_id_is_transmute(cx: &LateContext<'_, '_>, def_id: DefId) -> bool {
|
2017-05-13 17:11:52 +03:00
|
|
|
cx.tcx.fn_sig(def_id).abi() == RustIntrinsic &&
|
2019-05-15 06:32:44 +10:00
|
|
|
cx.tcx.item_name(def_id) == sym::transmute
|
2015-04-13 14:49:10 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-28 13:31:14 +01:00
|
|
|
declare_lint! {
|
|
|
|
UNSTABLE_FEATURES,
|
|
|
|
Allow,
|
2015-06-17 17:48:16 -07:00
|
|
|
"enabling unstable features (deprecated. do not use)"
|
2015-02-28 13:31:14 +01:00
|
|
|
}
|
2015-02-25 22:44:44 +11:00
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
declare_lint_pass!(
|
|
|
|
/// Forbids using the `#[feature(...)]` attribute
|
|
|
|
UnstableFeatures => [UNSTABLE_FEATURES]
|
|
|
|
);
|
2015-09-10 16:40:59 +12:00
|
|
|
|
2016-12-07 13:14:47 +01:00
|
|
|
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) {
|
2019-05-08 13:21:18 +10:00
|
|
|
if attr.check_name(sym::feature) {
|
2017-03-03 09:23:59 +00:00
|
|
|
if let Some(items) = attr.meta_item_list() {
|
2015-05-18 16:37:05 +02:00
|
|
|
for item in items {
|
2016-07-15 13:13:17 -07:00
|
|
|
ctx.span_lint(UNSTABLE_FEATURES, item.span(), "unstable feature");
|
2015-05-18 16:37:05 +02:00
|
|
|
}
|
|
|
|
}
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-08-19 19:20:30 +03:00
|
|
|
|
|
|
|
declare_lint! {
|
|
|
|
UNIONS_WITH_DROP_FIELDS,
|
|
|
|
Warn,
|
|
|
|
"use of unions that contain fields with possibly non-trivial drop code"
|
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
declare_lint_pass!(
|
|
|
|
/// Lint for unions that contain fields with possibly non-trivial destructors.
|
|
|
|
UnionsWithDropFields => [UNIONS_WITH_DROP_FIELDS]
|
|
|
|
);
|
2016-08-19 19:20:30 +03:00
|
|
|
|
2016-12-07 13:14:47 +01:00
|
|
|
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 {
|
2016-08-19 19:20:30 +03:00
|
|
|
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));
|
2017-06-07 15:21:55 +03:00
|
|
|
if field_ty.needs_drop(ctx.tcx, ctx.param_env) {
|
2016-08-19 19:20:30 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-11-02 20:50:17 -07:00
|
|
|
|
|
|
|
declare_lint! {
|
2018-03-08 13:23:52 -08:00
|
|
|
pub UNREACHABLE_PUB,
|
2017-11-02 20:50:17 -07:00
|
|
|
Allow,
|
|
|
|
"`pub` items not reachable from crate root"
|
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
declare_lint_pass!(
|
|
|
|
/// Lint for items marked `pub` that aren't reachable from other crates.
|
|
|
|
UnreachablePub => [UNREACHABLE_PUB]
|
|
|
|
);
|
2017-11-02 20:50:17 -07:00
|
|
|
|
|
|
|
impl UnreachablePub {
|
2019-02-26 15:11:59 +01:00
|
|
|
fn perform_lint(&self, cx: &LateContext<'_, '_>, what: &str, id: hir::HirId,
|
2018-06-27 21:23:18 -07:00
|
|
|
vis: &hir::Visibility, span: Span, exportable: bool) {
|
|
|
|
let mut applicability = Applicability::MachineApplicable;
|
|
|
|
match vis.node {
|
2019-03-11 11:03:19 +01:00
|
|
|
hir::VisibilityKind::Public if !cx.access_levels.is_reachable(id) => {
|
2019-05-27 13:52:11 +10:00
|
|
|
if span.ctxt().outer_expn_info().is_some() {
|
2018-06-27 21:23:18 -07:00
|
|
|
applicability = Applicability::MaybeIncorrect;
|
|
|
|
}
|
2018-08-18 12:14:09 +02:00
|
|
|
let def_span = cx.tcx.sess.source_map().def_span(span);
|
2018-06-27 21:23:18 -07:00
|
|
|
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();
|
|
|
|
|
2019-01-25 16:03:27 -05:00
|
|
|
err.span_suggestion(
|
|
|
|
vis.span,
|
|
|
|
"consider restricting its visibility",
|
|
|
|
replacement,
|
|
|
|
applicability,
|
|
|
|
);
|
2018-06-27 21:23:18 -07:00
|
|
|
if exportable {
|
|
|
|
err.help("or consider exporting it for use by other crates");
|
|
|
|
}
|
|
|
|
err.emit();
|
|
|
|
},
|
|
|
|
_ => {}
|
2017-11-02 20:50:17 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2017-11-02 20:50:17 -07:00
|
|
|
}
|
|
|
|
|
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,
|
2018-06-27 21:23:18 -07:00
|
|
|
foreign_item.span, true);
|
2017-11-02 20:50:17 -07:00
|
|
|
}
|
|
|
|
|
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);
|
2017-11-02 20:50:17 -07:00
|
|
|
}
|
|
|
|
|
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);
|
2017-11-02 20:50:17 -07:00
|
|
|
}
|
|
|
|
}
|
2018-02-18 19:40:35 +01:00
|
|
|
|
|
|
|
declare_lint! {
|
2018-03-10 11:43:51 +01:00
|
|
|
TYPE_ALIAS_BOUNDS,
|
2018-02-18 19:40:35 +01:00
|
|
|
Warn,
|
2018-03-10 11:43:51 +01:00
|
|
|
"bounds in type aliases are not enforced"
|
2018-02-18 19:40:35 +01:00
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
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]
|
|
|
|
);
|
2018-02-18 19:40:35 +01:00
|
|
|
|
2018-03-10 13:32:11 +01:00
|
|
|
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)) => {
|
2019-04-20 19:36:05 +03:00
|
|
|
match path.res {
|
|
|
|
Res::Def(DefKind::TyParam, _) => true,
|
2018-03-10 13:32:11 +01:00
|
|
|
_ => 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<'_>) {
|
2018-03-10 13:32:11 +01:00
|
|
|
// 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-10 13:32:11 +01:00
|
|
|
|
2018-03-19 18:01:14 +01:00
|
|
|
// We use a HIR visitor to walk the type.
|
2018-03-10 13:32:11 +01:00
|
|
|
use rustc::hir::intravisit::{self, Visitor};
|
2019-06-14 19:43:53 +03:00
|
|
|
struct WalkAssocTypes<'a, 'db> {
|
2018-03-10 13:32:11 +01:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2018-07-31 10:43:51 -06:00
|
|
|
fn visit_qpath(&mut self, qpath: &'v hir::QPath, id: hir::HirId, span: Span) {
|
2018-03-10 13:32:11 +01:00
|
|
|
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");
|
2018-03-10 13:32:11 +01:00
|
|
|
}
|
|
|
|
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) {
|
2018-03-10 13:32:11 +01:00
|
|
|
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),
|
2018-03-06 11:22:24 +01:00
|
|
|
_ => return,
|
|
|
|
};
|
2018-03-10 13:32:11 +01:00
|
|
|
let mut suggested_changing_assoc_types = false;
|
2018-03-06 11:22:24 +01:00
|
|
|
// 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();
|
2018-03-10 13:32:11 +01:00
|
|
|
let mut err = cx.struct_span_lint(TYPE_ALIAS_BOUNDS, spans,
|
2018-03-10 11:43:51 +01:00
|
|
|
"where clauses are not enforced in type aliases");
|
2018-03-10 13:32:11 +01:00
|
|
|
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();
|
2018-02-18 19:40:35 +01:00
|
|
|
}
|
2018-03-06 11:22:24 +01:00
|
|
|
// 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();
|
2018-03-06 11:22:24 +01:00
|
|
|
if !spans.is_empty() {
|
2018-03-10 13:32:11 +01:00
|
|
|
let mut err = cx.struct_span_lint(
|
2018-03-10 11:43:51 +01:00
|
|
|
TYPE_ALIAS_BOUNDS,
|
2018-03-06 11:22:24 +01:00
|
|
|
spans,
|
2018-03-10 11:43:51 +01:00
|
|
|
"bounds on generic parameters are not enforced in type aliases",
|
2018-03-06 11:22:24 +01:00
|
|
|
);
|
2018-03-10 13:32:11 +01:00
|
|
|
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-02-18 19:40:35 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-04-20 14:18:29 +02:00
|
|
|
|
2019-04-03 16:05:40 +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-01-18 07:40:55 +01:00
|
|
|
|
2019-02-08 20:35:41 +09:00
|
|
|
fn check_const(cx: &LateContext<'_, '_>, body_id: hir::BodyId) {
|
2018-12-04 13:45:36 +01:00
|
|
|
let def_id = cx.tcx.hir().body_owner_def_id(body_id);
|
2019-04-21 14:41:51 +03:00
|
|
|
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.
|
2018-08-01 00:25:46 +02:00
|
|
|
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
|
|
|
|
};
|
2018-08-26 15:19:34 +02:00
|
|
|
// trigger the query once for all constants since that will already report the errors
|
2019-03-26 17:04:00 +01:00
|
|
|
// FIXME: Use ensure here
|
2018-08-26 15:19:34 +02:00
|
|
|
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) => {
|
2018-08-26 15:19:34 +02:00
|
|
|
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) => {
|
2018-08-26 15:19:34 +02:00
|
|
|
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"
|
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
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);
|
2018-09-16 20:15:49 +03:00
|
|
|
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,
|
|
|
|
};
|
2018-05-15 21:48:35 +01:00
|
|
|
if predicate.is_global() {
|
2018-05-06 22:52:58 +01:00
|
|
|
cx.span_lint(
|
|
|
|
TRIVIAL_BOUNDS,
|
2018-09-16 20:15:49 +03:00
|
|
|
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
|
|
|
|
2019-04-03 16:05:40 +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
|
|
|
|
]
|
|
|
|
);
|
2018-05-28 19:32:03 -07:00
|
|
|
|
|
|
|
declare_lint! {
|
|
|
|
pub ELLIPSIS_INCLUSIVE_RANGE_PATTERNS,
|
2019-05-30 04:13:55 -04:00
|
|
|
Warn,
|
2018-05-28 19:32:03 -07:00
|
|
|
"`...` range patterns are deprecated"
|
|
|
|
}
|
|
|
|
|
2019-04-24 19:38:10 +01:00
|
|
|
#[derive(Default)]
|
2019-04-21 17:09:30 +03:00
|
|
|
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]);
|
|
|
|
|
2018-05-28 19:32:03 -07:00
|
|
|
impl EarlyLintPass for EllipsisInclusiveRangePatterns {
|
2019-04-21 17:09:30 +03:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2018-11-10 18:08:50 +00:00
|
|
|
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)),
|
|
|
|
};
|
2018-05-28 19:32:03 -07:00
|
|
|
|
2018-11-10 18:08:50 +00:00
|
|
|
if let Some((start, end, join)) = endpoints {
|
2018-05-28 19:32:03 -07:00
|
|
|
let msg = "`...` range patterns are deprecated";
|
2018-11-10 18:08:50 +00:00
|
|
|
let suggestion = "use `..=` for an inclusive range";
|
2018-11-10 21:27:40 +00:00
|
|
|
if parenthesise {
|
2019-04-21 17:09:30 +03:00
|
|
|
self.node_id = Some(pat.id);
|
2018-11-10 21:27:40 +00:00
|
|
|
let mut err = cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, pat.span, msg);
|
2019-01-25 16:03:27 -05:00
|
|
|
err.span_suggestion(
|
2018-11-10 21:27:40 +00:00
|
|
|
pat.span,
|
|
|
|
suggestion,
|
|
|
|
format!("&({}..={})", expr_to_string(&start), expr_to_string(&end)),
|
|
|
|
Applicability::MachineApplicable,
|
|
|
|
);
|
|
|
|
err.emit();
|
2018-11-10 18:08:50 +00:00
|
|
|
} else {
|
2018-11-10 21:27:40 +00:00
|
|
|
let mut err = cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, join, msg);
|
2019-01-25 16:03:27 -05:00
|
|
|
err.span_suggestion_short(
|
2018-11-10 21:27:40 +00:00
|
|
|
join,
|
|
|
|
suggestion,
|
|
|
|
"..=".to_owned(),
|
|
|
|
Applicability::MachineApplicable,
|
|
|
|
);
|
|
|
|
err.emit();
|
2018-11-10 18:08:50 +00:00
|
|
|
};
|
2018-05-28 19:32:03 -07:00
|
|
|
}
|
|
|
|
}
|
2019-04-21 17:09:30 +03:00
|
|
|
|
|
|
|
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-05-28 19:32:03 -07:00
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:40 +02: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;
|
|
|
|
}
|
|
|
|
|
2019-05-08 13:21:18 +10:00
|
|
|
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! {
|
2018-08-24 13:48:20 -07:00
|
|
|
pub KEYWORD_IDENTS,
|
2018-07-18 10:55:41 +02:00
|
|
|
Allow,
|
2018-08-24 13:48:20 -07:00
|
|
|
"detects edition keywords being used as an identifier"
|
2018-07-14 16:40:17 +02:00
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:40 +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
|
|
|
|
2019-03-27 14:43:29 +01:00
|
|
|
struct UnderMacro(bool);
|
|
|
|
|
2018-08-24 13:48:20 -07:00
|
|
|
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
|
|
|
}
|
2018-11-30 10:02:04 +11:00
|
|
|
TokenTree::Delimited(_, _, tts) => {
|
2019-01-09 16:53:14 +11:00
|
|
|
self.check_tokens(cx, tts)
|
2018-07-14 16:40:17 +02:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-27 14:43:29 +01:00
|
|
|
fn check_ident_token(&mut self,
|
|
|
|
cx: &EarlyContext<'_>,
|
|
|
|
UnderMacro(under_macro): UnderMacro,
|
|
|
|
ident: ast::Ident)
|
|
|
|
{
|
2019-04-18 12:55:23 -07:00
|
|
|
let next_edition = match cx.sess.edition() {
|
2018-08-24 13:48:20 -07:00
|
|
|
Edition::Edition2015 => {
|
2019-05-23 16:11:52 +10:00
|
|
|
match ident.name {
|
|
|
|
kw::Async | kw::Await | kw::Try => Edition::Edition2018,
|
2019-03-27 14:43:29 +01:00
|
|
|
|
|
|
|
// 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.
|
2019-05-23 16:11:52 +10:00
|
|
|
kw::Dyn if !under_macro => Edition::Edition2018,
|
2019-03-27 14:43:29 +01:00
|
|
|
|
2018-08-24 13:48:20 -07:00
|
|
|
_ => return,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-27 02:59:49 +00:00
|
|
|
// There are no new keywords yet for the 2018 edition and beyond.
|
2019-04-18 12:55:23 -07:00
|
|
|
_ => return,
|
2018-08-24 13:48:20 -07:00
|
|
|
};
|
|
|
|
|
2019-02-28 22:43:53 +00:00
|
|
|
// Don't lint `r#foo`.
|
2019-04-18 12:55:23 -07:00
|
|
|
if cx.sess.parse_sess.raw_identifier_spans.borrow().contains(&ident.span) {
|
2018-08-24 13:48:20 -07:00
|
|
|
return;
|
2018-07-14 16:40:17 +02:00
|
|
|
}
|
2018-08-24 13:48:20 -07:00
|
|
|
|
|
|
|
let mut lint = cx.struct_span_lint(
|
|
|
|
KEYWORD_IDENTS,
|
|
|
|
ident.span,
|
|
|
|
&format!("`{}` is a keyword in the {} edition",
|
|
|
|
ident.as_str(),
|
|
|
|
next_edition),
|
|
|
|
);
|
2019-01-25 16:03:27 -05:00
|
|
|
lint.span_suggestion(
|
2018-08-24 13:48:20 -07:00
|
|
|
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
|
|
|
|
2019-03-27 14:43:29 +01: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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
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 {
|
2019-05-25 10:28:17 +01:00
|
|
|
fn lifetimes_outliving_lifetime<'tcx>(
|
|
|
|
inferred_outlives: &'tcx [ty::Predicate<'tcx>],
|
|
|
|
index: u32,
|
|
|
|
) -> Vec<ty::Region<'tcx>> {
|
|
|
|
inferred_outlives.iter().filter_map(|pred| {
|
|
|
|
match pred {
|
|
|
|
ty::Predicate::RegionOutlives(outlives) => {
|
|
|
|
let outlives = outlives.skip_binder();
|
|
|
|
match outlives.0 {
|
|
|
|
ty::ReEarlyBound(ebr) if ebr.index == index => {
|
|
|
|
Some(outlives.1)
|
|
|
|
}
|
|
|
|
_ => None,
|
|
|
|
}
|
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
|
|
|
}
|
2019-05-25 10:28:17 +01:00
|
|
|
_ => None
|
|
|
|
}
|
|
|
|
}).collect()
|
|
|
|
}
|
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
|
|
|
|
2019-05-25 10:28:17 +01:00
|
|
|
fn lifetimes_outliving_type<'tcx>(
|
|
|
|
inferred_outlives: &'tcx [ty::Predicate<'tcx>],
|
|
|
|
index: u32,
|
|
|
|
) -> Vec<ty::Region<'tcx>> {
|
|
|
|
inferred_outlives.iter().filter_map(|pred| {
|
|
|
|
match pred {
|
|
|
|
ty::Predicate::TypeOutlives(outlives) => {
|
|
|
|
let outlives = outlives.skip_binder();
|
|
|
|
if outlives.0.is_param(index) {
|
|
|
|
Some(outlives.1)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
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
|
|
|
}
|
2019-05-25 10:28:17 +01:00
|
|
|
_ => None
|
|
|
|
}
|
|
|
|
}).collect()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn collect_outlived_lifetimes<'tcx>(
|
|
|
|
&self,
|
|
|
|
param: &'tcx hir::GenericParam,
|
|
|
|
tcx: TyCtxt<'tcx>,
|
|
|
|
inferred_outlives: &'tcx [ty::Predicate<'tcx>],
|
|
|
|
ty_generics: &'tcx ty::Generics,
|
|
|
|
) -> Vec<ty::Region<'tcx>> {
|
|
|
|
let index = ty_generics.param_def_id_to_index[
|
|
|
|
&tcx.hir().local_def_id_from_hir_id(param.hir_id)];
|
|
|
|
|
|
|
|
match param.kind {
|
|
|
|
hir::GenericParamKind::Lifetime { .. } => {
|
|
|
|
Self::lifetimes_outliving_lifetime(inferred_outlives, index)
|
|
|
|
}
|
|
|
|
hir::GenericParamKind::Type { .. } => {
|
|
|
|
Self::lifetimes_outliving_type(inferred_outlives, index)
|
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
|
|
|
}
|
2019-05-25 10:28:17 +01:00
|
|
|
hir::GenericParamKind::Const { .. } => Vec::new(),
|
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
|
|
|
}
|
2019-05-25 10:28:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn collect_outlives_bound_spans<'tcx>(
|
|
|
|
&self,
|
|
|
|
tcx: TyCtxt<'tcx>,
|
|
|
|
bounds: &hir::GenericBounds,
|
|
|
|
inferred_outlives: &[ty::Region<'tcx>],
|
|
|
|
infer_static: bool,
|
|
|
|
) -> Vec<(usize, Span)> {
|
|
|
|
use rustc::middle::resolve_lifetime::Region;
|
|
|
|
|
|
|
|
bounds
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.filter_map(|(i, bound)| {
|
|
|
|
if let hir::GenericBound::Outlives(lifetime) = bound {
|
|
|
|
let is_inferred = match tcx.named_region(lifetime.hir_id) {
|
|
|
|
Some(Region::Static) if infer_static => {
|
|
|
|
inferred_outlives.iter()
|
|
|
|
.any(|r| if let ty::ReStatic = r { true } else { false })
|
|
|
|
}
|
|
|
|
Some(Region::EarlyBound(index, ..)) => inferred_outlives
|
|
|
|
.iter()
|
|
|
|
.any(|r| {
|
|
|
|
if let ty::ReEarlyBound(ebr) = r {
|
|
|
|
ebr.index == index
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
_ => false,
|
|
|
|
};
|
|
|
|
if is_inferred {
|
|
|
|
Some((i, bound.span()))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect()
|
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
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2019-05-25 10:28:17 +01:00
|
|
|
// If the first bound is inferable, our span should also eat the leading `+`
|
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
|
|
|
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) {
|
2019-05-25 10:28:17 +01:00
|
|
|
use rustc::middle::resolve_lifetime::Region;
|
|
|
|
|
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 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);
|
2019-05-25 10:28:17 +01:00
|
|
|
if let hir::ItemKind::Struct(_, ref hir_generics)
|
|
|
|
| hir::ItemKind::Enum(_, ref hir_generics)
|
|
|
|
| hir::ItemKind::Union(_, ref hir_generics) = item.node
|
|
|
|
{
|
|
|
|
let inferred_outlives = cx.tcx.inferred_outlives_of(def_id);
|
|
|
|
if inferred_outlives.is_empty() {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let ty_generics = cx.tcx.generics_of(def_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
|
|
|
let mut bound_count = 0;
|
|
|
|
let mut lint_spans = Vec::new();
|
|
|
|
|
2019-05-25 10:28:17 +01:00
|
|
|
for param in &hir_generics.params {
|
|
|
|
let has_lifetime_bounds = param.bounds.iter().any(|bound| {
|
|
|
|
if let hir::GenericBound::Outlives(_) = bound {
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
false
|
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
|
|
|
}
|
2019-05-25 10:28:17 +01:00
|
|
|
});
|
|
|
|
if !has_lifetime_bounds {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
let relevant_lifetimes = self.collect_outlived_lifetimes(
|
|
|
|
param,
|
|
|
|
cx.tcx,
|
|
|
|
inferred_outlives,
|
|
|
|
ty_generics,
|
|
|
|
);
|
|
|
|
if relevant_lifetimes.is_empty() {
|
|
|
|
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(
|
2019-05-25 10:28:17 +01:00
|
|
|
cx.tcx, ¶m.bounds, &relevant_lifetimes, infer_static,
|
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
|
|
|
);
|
|
|
|
bound_count += bound_spans.len();
|
|
|
|
lint_spans.extend(
|
|
|
|
self.consolidate_outlives_bound_spans(
|
|
|
|
param.span.shrink_to_hi(), ¶m.bounds, bound_spans
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut where_lint_spans = Vec::new();
|
|
|
|
let mut dropped_predicate_count = 0;
|
2019-05-25 10:28:17 +01:00
|
|
|
let num_predicates = hir_generics.where_clause.predicates.len();
|
|
|
|
for (i, where_predicate) in hir_generics.where_clause.predicates.iter().enumerate() {
|
|
|
|
let (relevant_lifetimes, bounds, span) = match where_predicate {
|
|
|
|
hir::WherePredicate::RegionPredicate(predicate) => {
|
|
|
|
if let Some(Region::EarlyBound(index, ..))
|
|
|
|
= cx.tcx.named_region(predicate.lifetime.hir_id)
|
|
|
|
{
|
|
|
|
(
|
|
|
|
Self::lifetimes_outliving_lifetime(inferred_outlives, index),
|
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
|
|
|
&predicate.bounds,
|
2019-05-25 10:28:17 +01:00
|
|
|
predicate.span,
|
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
|
|
|
)
|
2019-05-25 10:28:17 +01:00
|
|
|
} else {
|
|
|
|
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
|
|
|
}
|
2019-05-25 10:28:17 +01:00
|
|
|
hir::WherePredicate::BoundPredicate(predicate) => {
|
|
|
|
// FIXME we can also infer bounds on associated types,
|
|
|
|
// and should check for them here.
|
|
|
|
match predicate.bounded_ty.node {
|
|
|
|
hir::TyKind::Path(hir::QPath::Resolved(
|
|
|
|
None,
|
|
|
|
ref path,
|
|
|
|
)) => if let Res::Def(DefKind::TyParam, def_id) = path.res {
|
|
|
|
let index = ty_generics.param_def_id_to_index[&def_id];
|
|
|
|
(
|
|
|
|
Self::lifetimes_outliving_type(inferred_outlives, index),
|
|
|
|
&predicate.bounds,
|
|
|
|
predicate.span,
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
continue
|
|
|
|
},
|
|
|
|
_ => { continue; }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => continue,
|
|
|
|
};
|
|
|
|
if relevant_lifetimes.is_empty() {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
let bound_spans = self.collect_outlives_bound_spans(
|
|
|
|
cx.tcx, bounds, &relevant_lifetimes, infer_static,
|
|
|
|
);
|
|
|
|
bound_count += bound_spans.len();
|
|
|
|
|
|
|
|
let drop_predicate = bound_spans.len() == 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 = hir_generics.where_clause.predicates[i+1].span();
|
|
|
|
where_lint_spans.push(
|
|
|
|
span.to(next_predicate_span.shrink_to_lo())
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
where_lint_spans.extend(
|
|
|
|
self.consolidate_outlives_bound_spans(
|
|
|
|
span.shrink_to_lo(),
|
|
|
|
bounds,
|
|
|
|
bound_spans
|
|
|
|
)
|
|
|
|
);
|
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 all predicates are inferable, drop the entire clause
|
|
|
|
// (including the `where`)
|
|
|
|
if num_predicates > 0 && dropped_predicate_count == num_predicates {
|
2019-05-25 10:28:17 +01:00
|
|
|
let where_span = hir_generics.where_clause.span()
|
|
|
|
.expect("span of (nonempty) where clause should exist");
|
|
|
|
// Extend the where clause back to the closing `>` of the
|
|
|
|
// generics, except for tuple struct, which have the `where`
|
|
|
|
// after the fields of the struct.
|
|
|
|
let full_where_span = match item.node {
|
|
|
|
hir::ItemKind::Struct(hir::VariantData::Tuple(..), _) => {
|
|
|
|
where_span
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
hir_generics.span.shrink_to_hi().to(where_span)
|
|
|
|
}
|
|
|
|
};
|
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
|
|
|
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"
|
|
|
|
);
|
2019-01-25 16:03:27 -05:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|