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
|
|
|
|
2016-03-29 12:54:26 +03:00
|
|
|
use rustc::hir::def::Def;
|
2019-01-13 01:06:50 +01:00
|
|
|
use rustc::hir::def_id::{DefId, LOCAL_CRATE};
|
2017-06-07 15:21:55 +03:00
|
|
|
use rustc::ty::{self, Ty};
|
2019-02-08 20:35:41 +09:00
|
|
|
use rustc::{lint, util};
|
2018-08-25 15:56:16 +01:00
|
|
|
use hir::Node;
|
2019-02-27 17:35:24 +01:00
|
|
|
use util::nodemap::HirIdSet;
|
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};
|
2017-05-02 04:38:46 +02:00
|
|
|
use syntax::symbol::keywords;
|
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;
|
2019-02-08 20:35:41 +09:00
|
|
|
use syntax::struct_span_err;
|
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 { }`"
|
|
|
|
}
|
|
|
|
|
2015-03-30 09:38:44 -04:00
|
|
|
#[derive(Copy, Clone)]
|
2015-02-25 22:44:44 +11:00
|
|
|
pub struct WhileTrue;
|
|
|
|
|
|
|
|
impl LintPass for WhileTrue {
|
2019-01-18 07:40:55 +01:00
|
|
|
fn name(&self) -> &'static str {
|
|
|
|
"WhileTrue"
|
|
|
|
}
|
|
|
|
|
2015-02-25 22:44:44 +11:00
|
|
|
fn get_lints(&self) -> LintArray {
|
|
|
|
lint_array!(WHILE_TRUE)
|
|
|
|
}
|
2015-09-15 11:35:25 +12:00
|
|
|
}
|
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"
|
|
|
|
}
|
|
|
|
|
2015-03-30 09:38:44 -04:00
|
|
|
#[derive(Copy, Clone)]
|
2015-02-25 22:44:44 +11:00
|
|
|
pub struct BoxPointers;
|
|
|
|
|
|
|
|
impl BoxPointers {
|
2019-02-08 20:35:41 +09:00
|
|
|
fn check_heap_type<'a, 'tcx>(&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
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl LintPass for BoxPointers {
|
2019-01-18 07:40:55 +01:00
|
|
|
fn name(&self) -> &'static str {
|
|
|
|
"BoxPointers"
|
|
|
|
}
|
|
|
|
|
2015-02-25 22:44:44 +11:00
|
|
|
fn get_lints(&self) -> LintArray {
|
|
|
|
lint_array!(BOX_POINTERS)
|
|
|
|
}
|
2015-09-15 11:35:25 +12:00
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
2015-03-30 09:38:44 -04:00
|
|
|
#[derive(Copy, Clone)]
|
2015-02-25 22:44:44 +11:00
|
|
|
pub struct NonShorthandFieldPatterns;
|
|
|
|
|
|
|
|
impl LintPass for NonShorthandFieldPatterns {
|
2019-01-18 07:40:55 +01:00
|
|
|
fn name(&self) -> &'static str {
|
|
|
|
"NonShorthandFieldPatterns"
|
|
|
|
}
|
|
|
|
|
2015-02-25 22:44:44 +11:00
|
|
|
fn get_lints(&self) -> LintArray {
|
|
|
|
lint_array!(NON_SHORTHAND_FIELD_PATTERNS)
|
|
|
|
}
|
2015-09-15 11:35:25 +12:00
|
|
|
}
|
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")
|
|
|
|
.variant_of_def(cx.tables.qpath_def(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
|
|
|
}
|
2018-04-05 03:20:21 +03:00
|
|
|
if fieldpat.span.ctxt().outer().expn_info().is_some() {
|
|
|
|
// Don't lint if this is a macro expansion: macro authors
|
|
|
|
// shouldn't have to worry about this kind of style issue
|
|
|
|
// (Issue #49588)
|
|
|
|
continue;
|
|
|
|
}
|
2019-03-07 12:18:59 +01:00
|
|
|
if let PatKind::Binding(_, _, ident, None) = fieldpat.node.pat.node {
|
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"
|
|
|
|
}
|
|
|
|
|
2015-03-30 09:38:44 -04:00
|
|
|
#[derive(Copy, Clone)]
|
2015-02-25 22:44:44 +11:00
|
|
|
pub struct UnsafeCode;
|
|
|
|
|
|
|
|
impl LintPass for UnsafeCode {
|
2019-01-18 07:40:55 +01:00
|
|
|
fn name(&self) -> &'static str {
|
|
|
|
"UnsafeCode"
|
|
|
|
}
|
|
|
|
|
2015-02-25 22:44:44 +11:00
|
|
|
fn get_lints(&self) -> LintArray {
|
|
|
|
lint_array!(UNSAFE_CODE)
|
|
|
|
}
|
2015-09-15 11:35:25 +12:00
|
|
|
}
|
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) {
|
2017-08-08 18:21:20 +03:00
|
|
|
// This comes from a macro that has #[allow_internal_unsafe].
|
|
|
|
if span.allows_unsafe() {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
cx.span_lint(UNSAFE_CODE, span, desc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-15 06:54:28 +09:00
|
|
|
impl EarlyLintPass for UnsafeCode {
|
2019-02-08 20:35:41 +09:00
|
|
|
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) {
|
2019-01-09 20:53:33 +09:00
|
|
|
if attr.check_name("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 {
|
2015-02-25 22:44:44 +11: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
|
|
|
}
|
|
|
|
|
2018-12-01 14:46:25 +01:00
|
|
|
fn has_doc(attr: &ast::Attribute) -> bool {
|
|
|
|
if !attr.check_name("doc") {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if attr.is_value_str() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(list) = attr.meta_item_list() {
|
|
|
|
for meta in list {
|
|
|
|
if meta.check_name("include") || meta.check_name("hidden") {
|
|
|
|
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
|
|
|
|
// 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
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl LintPass for MissingDoc {
|
2019-01-18 07:40:55 +01:00
|
|
|
fn name(&self) -> &'static str {
|
|
|
|
"MissingDoc"
|
|
|
|
}
|
|
|
|
|
2015-02-25 22:44:44 +11:00
|
|
|
fn get_lints(&self) -> LintArray {
|
|
|
|
lint_array!(MISSING_DOCS)
|
|
|
|
}
|
2015-09-15 11:35:25 +12:00
|
|
|
}
|
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| {
|
|
|
|
attr.check_name("doc") &&
|
|
|
|
match attr.meta_item_list() {
|
2015-02-25 22:44:44 +11:00
|
|
|
None => false,
|
2017-03-24 09:31:26 +01:00
|
|
|
Some(l) => attr::list_contains_name(&l, "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) => {
|
2015-03-29 23:41:54 -04: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) => {
|
2015-03-29 23:41:54 -04:00
|
|
|
// If the trait is private, add the impl items to private_traits so they don't get
|
|
|
|
// reported for missing docs.
|
2016-11-25 13:21:19 +02:00
|
|
|
let real_trait = trait_ref.path.def.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-02-26 11:04:58 +01:00
|
|
|
Some(v.node.data.hir_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`"
|
|
|
|
}
|
|
|
|
|
2015-03-30 09:38:44 -04:00
|
|
|
#[derive(Copy, Clone)]
|
2015-02-25 22:44:44 +11:00
|
|
|
pub struct MissingCopyImplementations;
|
|
|
|
|
|
|
|
impl LintPass for MissingCopyImplementations {
|
2019-01-18 07:40:55 +01:00
|
|
|
fn name(&self) -> &'static str {
|
|
|
|
"MissingCopyImplementations"
|
|
|
|
}
|
|
|
|
|
2015-02-25 22:44:44 +11:00
|
|
|
fn get_lints(&self) -> LintArray {
|
|
|
|
lint_array!(MISSING_COPY_IMPLEMENTATIONS)
|
|
|
|
}
|
2015-09-15 11:35:25 +12:00
|
|
|
}
|
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"
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct MissingDebugImplementations {
|
2019-02-27 17:35:24 +01:00
|
|
|
impling_types: Option<HirIdSet>,
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
impl MissingDebugImplementations {
|
|
|
|
pub fn new() -> MissingDebugImplementations {
|
2016-10-09 09:38:07 +05:30
|
|
|
MissingDebugImplementations { impling_types: None }
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl LintPass for MissingDebugImplementations {
|
2019-01-18 07:40:55 +01:00
|
|
|
fn name(&self) -> &'static str {
|
|
|
|
"MissingDebugImplementations"
|
|
|
|
}
|
|
|
|
|
2015-02-25 22:44:44 +11:00
|
|
|
fn get_lints(&self) -> LintArray {
|
|
|
|
lint_array!(MISSING_DEBUG_IMPLEMENTATIONS)
|
|
|
|
}
|
2015-09-15 11:35:25 +12:00
|
|
|
}
|
2015-02-25 22:44:44 +11:00
|
|
|
|
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-02-08 14:53:55 +01:00
|
|
|
/// Checks for use of anonymous parameters (RFC 1685).
|
|
|
|
#[derive(Copy, Clone)]
|
2017-05-02 04:38:46 +02:00
|
|
|
pub struct AnonymousParameters;
|
|
|
|
|
|
|
|
impl LintPass for AnonymousParameters {
|
2019-01-18 07:40:55 +01:00
|
|
|
fn name(&self) -> &'static str {
|
|
|
|
"AnonymousParameters"
|
|
|
|
}
|
|
|
|
|
2017-05-02 04:38:46 +02:00
|
|
|
fn get_lints(&self) -> LintArray {
|
|
|
|
lint_array!(ANONYMOUS_PARAMETERS)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl EarlyLintPass for AnonymousParameters {
|
2019-02-08 20:35:41 +09:00
|
|
|
fn check_trait_item(&mut self, cx: &EarlyContext<'_>, it: &ast::TraitItem) {
|
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) => {
|
2018-03-18 16:47:09 +03:00
|
|
|
if ident.name == keywords::Invalid.name() {
|
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-01-02 02:21:05 +03:00
|
|
|
depr_attrs: Vec<&'static (&'static str, AttributeType, AttributeTemplate, AttributeGate)>,
|
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 LintPass for DeprecatedAttr {
|
2019-01-18 07:40:55 +01:00
|
|
|
fn name(&self) -> &'static str {
|
|
|
|
"DeprecatedAttr"
|
|
|
|
}
|
|
|
|
|
2016-10-11 15:51:27 +13:00
|
|
|
fn get_lints(&self) -> LintArray {
|
2017-10-21 00:00:57 +03:00
|
|
|
lint_array!()
|
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-02-28 09:17:24 +03:00
|
|
|
if attr.ident_str() == 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"
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
pub struct UnusedDocComment;
|
|
|
|
|
|
|
|
impl LintPass for UnusedDocComment {
|
2019-01-18 07:40:55 +01:00
|
|
|
fn name(&self) -> &'static str {
|
|
|
|
"UnusedDocComment"
|
|
|
|
}
|
|
|
|
|
2017-07-16 00:17:35 +02:00
|
|
|
fn get_lints(&self) -> LintArray {
|
2018-05-19 01:13:53 +03:00
|
|
|
lint_array![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-03-11 00:24:15 +03:00
|
|
|
if attr.check_name("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"
|
|
|
|
}
|
|
|
|
|
2015-03-30 09:38:44 -04:00
|
|
|
#[derive(Copy, Clone)]
|
2015-02-25 22:44:44 +11:00
|
|
|
pub struct PluginAsLibrary;
|
|
|
|
|
|
|
|
impl LintPass for PluginAsLibrary {
|
2019-01-18 07:40:55 +01:00
|
|
|
fn name(&self) -> &'static str {
|
|
|
|
"PluginAsLibrary"
|
|
|
|
}
|
|
|
|
|
2015-02-25 22:44:44 +11:00
|
|
|
fn get_lints(&self) -> LintArray {
|
|
|
|
lint_array![PLUGIN_AS_LIBRARY]
|
|
|
|
}
|
2015-09-15 11:35:25 +12:00
|
|
|
}
|
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"
|
|
|
|
}
|
|
|
|
|
2015-03-30 09:38:44 -04:00
|
|
|
#[derive(Copy, Clone)]
|
2015-02-25 22:44:44 +11:00
|
|
|
pub struct InvalidNoMangleItems;
|
|
|
|
|
|
|
|
impl LintPass for InvalidNoMangleItems {
|
2019-01-18 07:40:55 +01:00
|
|
|
fn name(&self) -> &'static str {
|
|
|
|
"InvalidNoMangleItems"
|
|
|
|
}
|
|
|
|
|
2015-02-25 22:44:44 +11:00
|
|
|
fn get_lints(&self) -> LintArray {
|
2018-09-21 15:58:11 -07:00
|
|
|
lint_array!(NO_MANGLE_CONST_ITEMS,
|
2015-12-09 01:48:40 +09:00
|
|
|
NO_MANGLE_GENERIC_ITEMS)
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
2015-09-15 11:35:25 +12:00
|
|
|
}
|
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, _) => {
|
2017-10-11 23:08:48 -07:00
|
|
|
if let Some(no_mangle_attr) = attr::find_by_name(&it.attrs, "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(..) => {
|
2015-03-18 09:14:54 -07:00
|
|
|
if attr::contains_name(&it.attrs, "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
|
|
|
#[derive(Clone, Copy)]
|
|
|
|
pub struct MutableTransmutes;
|
|
|
|
|
|
|
|
declare_lint! {
|
|
|
|
MUTABLE_TRANSMUTES,
|
|
|
|
Deny,
|
|
|
|
"mutating transmuted &mut T from &T may cause undefined behavior"
|
|
|
|
}
|
|
|
|
|
|
|
|
impl LintPass for MutableTransmutes {
|
2019-01-18 07:40:55 +01:00
|
|
|
fn name(&self) -> &'static str {
|
|
|
|
"MutableTransmutes"
|
|
|
|
}
|
|
|
|
|
2015-04-13 14:49:10 -07:00
|
|
|
fn get_lints(&self) -> LintArray {
|
|
|
|
lint_array!(MUTABLE_TRANSMUTES)
|
|
|
|
}
|
2015-09-15 11:35:25 +12:00
|
|
|
}
|
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";
|
|
|
|
match get_transmute_from_to(cx, expr) {
|
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)
|
2018-08-22 01:34:12 +01:00
|
|
|
-> Option<(&'tcx ty::TyKind<'tcx>, &'tcx ty::TyKind<'tcx>)> {
|
2018-07-11 20:05:29 +08:00
|
|
|
let def = if let hir::ExprKind::Path(ref qpath) = expr.node {
|
2017-08-04 09:49:40 +02:00
|
|
|
cx.tables.qpath_def(qpath, expr.hir_id)
|
2016-11-25 13:21:19 +02:00
|
|
|
} else {
|
|
|
|
return None;
|
|
|
|
};
|
|
|
|
if let Def::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();
|
|
|
|
return Some((&from.sty, &to.sty));
|
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 &&
|
2016-11-17 14:04:20 +00:00
|
|
|
cx.tcx.item_name(def_id) == "transmute"
|
2015-04-13 14:49:10 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-25 22:44:44 +11:00
|
|
|
/// Forbids using the `#[feature(...)]` attribute
|
2015-03-30 09:38:44 -04:00
|
|
|
#[derive(Copy, Clone)]
|
2015-02-25 22:44:44 +11:00
|
|
|
pub struct UnstableFeatures;
|
|
|
|
|
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
|
|
|
|
|
|
|
impl LintPass for UnstableFeatures {
|
2019-01-18 07:40:55 +01:00
|
|
|
fn name(&self) -> &'static str {
|
|
|
|
"UnstableFeatures"
|
|
|
|
}
|
|
|
|
|
2015-02-25 22:44:44 +11:00
|
|
|
fn get_lints(&self) -> LintArray {
|
|
|
|
lint_array!(UNSTABLE_FEATURES)
|
|
|
|
}
|
2015-09-15 11:35:25 +12:00
|
|
|
}
|
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) {
|
2017-03-03 09:23:59 +00:00
|
|
|
if attr.check_name("feature") {
|
|
|
|
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
|
|
|
|
|
|
|
/// Lint for unions that contain fields with possibly non-trivial destructors.
|
|
|
|
pub struct UnionsWithDropFields;
|
|
|
|
|
|
|
|
declare_lint! {
|
|
|
|
UNIONS_WITH_DROP_FIELDS,
|
|
|
|
Warn,
|
|
|
|
"use of unions that contain fields with possibly non-trivial drop code"
|
|
|
|
}
|
|
|
|
|
|
|
|
impl LintPass for UnionsWithDropFields {
|
2019-01-18 07:40:55 +01:00
|
|
|
fn name(&self) -> &'static str {
|
|
|
|
"UnionsWithDropFields"
|
|
|
|
}
|
|
|
|
|
2016-08-19 19:20:30 +03:00
|
|
|
fn get_lints(&self) -> LintArray {
|
|
|
|
lint_array!(UNIONS_WITH_DROP_FIELDS)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2019-02-08 14:53:55 +01:00
|
|
|
/// Lint for items marked `pub` that aren't reachable from other crates.
|
|
|
|
#[derive(Copy, Clone)]
|
2017-11-02 20:50:17 -07:00
|
|
|
pub struct UnreachablePub;
|
|
|
|
|
|
|
|
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"
|
|
|
|
}
|
|
|
|
|
|
|
|
impl LintPass for UnreachablePub {
|
2019-01-18 07:40:55 +01:00
|
|
|
fn name(&self) -> &'static str {
|
|
|
|
"UnreachablePub"
|
|
|
|
}
|
|
|
|
|
2017-11-02 20:50:17 -07:00
|
|
|
fn get_lints(&self) -> LintArray {
|
|
|
|
lint_array!(UNREACHABLE_PUB)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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) => {
|
2018-06-27 21:23:18 -07:00
|
|
|
if span.ctxt().outer().expn_info().is_some() {
|
|
|
|
applicability = Applicability::MaybeIncorrect;
|
|
|
|
}
|
2018-08-18 12:14:09 +02:00
|
|
|
let def_span = cx.tcx.sess.source_map().def_span(span);
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-05 22:14:33 -07:00
|
|
|
|
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
|
|
|
|
2019-02-08 14:53:55 +01:00
|
|
|
/// Lint for trait and lifetime bounds in type aliases being mostly ignored.
|
2018-03-10 11:43:51 +01:00
|
|
|
/// They are relevant when using associated types, but otherwise neither checked
|
|
|
|
/// at definition site nor enforced at use site.
|
2018-02-18 19:40:35 +01:00
|
|
|
|
2018-03-10 11:43:51 +01:00
|
|
|
pub struct TypeAliasBounds;
|
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
|
|
|
}
|
|
|
|
|
2018-03-10 11:43:51 +01:00
|
|
|
impl LintPass for TypeAliasBounds {
|
2019-01-18 07:40:55 +01:00
|
|
|
fn name(&self) -> &'static str {
|
|
|
|
"TypeAliasBounds"
|
|
|
|
}
|
|
|
|
|
2018-02-18 19:40:35 +01:00
|
|
|
fn get_lints(&self) -> LintArray {
|
2018-03-10 11:43:51 +01:00
|
|
|
lint_array!(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)) => {
|
2018-03-10 13:32:11 +01:00
|
|
|
match path.def {
|
2018-08-22 02:13:31 +01:00
|
|
|
Def::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};
|
|
|
|
struct WalkAssocTypes<'a, 'db> where 'db: 'a {
|
|
|
|
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
|
|
|
|
|
|
|
/// 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.
|
|
|
|
pub struct UnusedBrokenConst;
|
|
|
|
|
|
|
|
impl LintPass for UnusedBrokenConst {
|
2019-01-18 07:40:55 +01:00
|
|
|
fn name(&self) -> &'static str {
|
|
|
|
"UnusedBrokenConst"
|
|
|
|
}
|
|
|
|
|
2018-04-20 14:18:29 +02:00
|
|
|
fn get_lints(&self) -> LintArray {
|
|
|
|
lint_array!()
|
|
|
|
}
|
|
|
|
}
|
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);
|
2018-08-01 00:25:46 +02:00
|
|
|
let is_static = cx.tcx.is_static(def_id).is_some();
|
|
|
|
let param_env = if is_static {
|
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
|
|
|
|
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
|
|
|
/// Lint for trait and lifetime bounds that don't depend on type parameters
|
|
|
|
/// which either do nothing, or stop the item from being used.
|
|
|
|
pub struct TrivialConstraints;
|
|
|
|
|
|
|
|
declare_lint! {
|
|
|
|
TRIVIAL_BOUNDS,
|
|
|
|
Warn,
|
|
|
|
"these bounds don't depend on an type parameters"
|
|
|
|
}
|
|
|
|
|
|
|
|
impl LintPass for TrivialConstraints {
|
2019-01-18 07:40:55 +01:00
|
|
|
fn name(&self) -> &'static str {
|
|
|
|
"TrivialConstraints"
|
|
|
|
}
|
|
|
|
|
2018-05-06 22:52:58 +01:00
|
|
|
fn get_lints(&self) -> LintArray {
|
|
|
|
lint_array!(TRIVIAL_BOUNDS)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
2018-05-28 19:32:03 -07:00
|
|
|
|
2018-06-09 17:20:58 +02:00
|
|
|
/// Does nothing as a lint pass, but registers some `Lint`s
|
|
|
|
/// which are used by other parts of the compiler.
|
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
pub struct SoftLints;
|
|
|
|
|
|
|
|
impl LintPass for SoftLints {
|
2019-01-18 07:40:55 +01:00
|
|
|
fn name(&self) -> &'static str {
|
|
|
|
"SoftLints"
|
|
|
|
}
|
|
|
|
|
2018-06-09 17:20:58 +02:00
|
|
|
fn get_lints(&self) -> LintArray {
|
|
|
|
lint_array!(
|
|
|
|
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,
|
2018-06-15 21:49:00 -05:00
|
|
|
TRIVIAL_BOUNDS
|
2018-06-09 17:20:58 +02:00
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
2018-05-28 19:32:03 -07:00
|
|
|
|
|
|
|
declare_lint! {
|
|
|
|
pub ELLIPSIS_INCLUSIVE_RANGE_PATTERNS,
|
|
|
|
Allow,
|
|
|
|
"`...` range patterns are deprecated"
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pub struct EllipsisInclusiveRangePatterns;
|
|
|
|
|
|
|
|
impl LintPass for EllipsisInclusiveRangePatterns {
|
2019-01-18 07:40:55 +01:00
|
|
|
fn name(&self) -> &'static str {
|
|
|
|
"EllipsisInclusiveRangePatterns"
|
|
|
|
}
|
|
|
|
|
2018-05-28 19:32:03 -07:00
|
|
|
fn get_lints(&self) -> LintArray {
|
|
|
|
lint_array!(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl EarlyLintPass for EllipsisInclusiveRangePatterns {
|
2019-02-08 20:35:41 +09:00
|
|
|
fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &ast::Pat, visit_subpats: &mut bool) {
|
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 {
|
2018-11-10 18:08:50 +00:00
|
|
|
*visit_subpats = false;
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
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 LintPass for UnnameableTestItems {
|
2019-01-18 07:40:55 +01:00
|
|
|
fn name(&self) -> &'static str {
|
|
|
|
"UnnameableTestItems"
|
|
|
|
}
|
|
|
|
|
2018-06-08 18:24:57 -07:00
|
|
|
fn get_lints(&self) -> LintArray {
|
2018-07-20 18:04:02 -07:00
|
|
|
lint_array!(UNNAMEABLE_TEST_ITEMS)
|
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;
|
|
|
|
}
|
|
|
|
|
2018-09-02 09:03:24 -07:00
|
|
|
if let Some(attr) = attr::find_by_name(&it.attrs, "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-02-08 14:53:55 +01:00
|
|
|
/// Check for uses of edition keywords used as an identifier.
|
|
|
|
#[derive(Copy, Clone)]
|
2018-08-24 13:48:20 -07:00
|
|
|
pub struct KeywordIdents;
|
2018-07-14 16:40:17 +02:00
|
|
|
|
2018-08-24 13:48:20 -07:00
|
|
|
impl LintPass for KeywordIdents {
|
2019-01-18 07:40:55 +01:00
|
|
|
fn name(&self) -> &'static str {
|
|
|
|
"KeywordIdents"
|
|
|
|
}
|
|
|
|
|
2018-07-14 16:40:17 +02:00
|
|
|
fn get_lints(&self) -> LintArray {
|
2018-08-24 13:48:20 -07:00
|
|
|
lint_array!(KEYWORD_IDENTS)
|
2018-07-14 16:40:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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 {
|
|
|
|
TokenTree::Token(span, tok) => match tok.ident() {
|
|
|
|
// only report non-raw idents
|
2018-08-24 13:48:20 -07:00
|
|
|
Some((ident, false)) => {
|
|
|
|
self.check_ident(cx, ast::Ident {
|
|
|
|
span: span.substitute_dummy(ident.span),
|
|
|
|
..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
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-24 13:48:20 -07:00
|
|
|
impl EarlyLintPass for KeywordIdents {
|
2019-02-08 20:35:41 +09:00
|
|
|
fn check_mac_def(&mut self, cx: &EarlyContext<'_>, mac_def: &ast::MacroDef, _id: ast::NodeId) {
|
2018-07-14 16:40:17 +02:00
|
|
|
self.check_tokens(cx, mac_def.stream());
|
|
|
|
}
|
2019-02-08 20:35:41 +09:00
|
|
|
fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::Mac) {
|
2018-07-14 16:40:17 +02:00
|
|
|
self.check_tokens(cx, mac.node.tts.clone().into());
|
|
|
|
}
|
2019-02-08 20:35:41 +09:00
|
|
|
fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: ast::Ident) {
|
2018-09-20 17:36:31 -07:00
|
|
|
let ident_str = &ident.as_str()[..];
|
|
|
|
let cur_edition = cx.sess.edition();
|
|
|
|
let is_raw_ident = |ident: ast::Ident| {
|
|
|
|
cx.sess.parse_sess.raw_identifier_spans.borrow().contains(&ident.span)
|
|
|
|
};
|
|
|
|
let next_edition = match cur_edition {
|
2018-08-24 13:48:20 -07:00
|
|
|
Edition::Edition2015 => {
|
2018-09-20 17:36:31 -07:00
|
|
|
match ident_str {
|
2018-09-15 18:18:49 +01:00
|
|
|
"async" | "try" | "dyn" => Edition::Edition2018,
|
2018-09-20 17:36:31 -07:00
|
|
|
// Only issue warnings for `await` if the `async_await`
|
|
|
|
// feature isn't being used. Otherwise, users need
|
|
|
|
// to keep using `await` for the macro exposed by std.
|
|
|
|
"await" if !cx.sess.features_untracked().async_await => Edition::Edition2018,
|
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.
|
2018-09-20 17:36:31 -07:00
|
|
|
// However, `await` is a "false" keyword in the 2018 edition,
|
|
|
|
// and can only be used if the `async_await` feature is enabled.
|
|
|
|
// Otherwise, we emit an error.
|
|
|
|
_ => {
|
|
|
|
if "await" == ident_str
|
|
|
|
&& !cx.sess.features_untracked().async_await
|
|
|
|
&& !is_raw_ident(ident)
|
|
|
|
{
|
|
|
|
let mut err = struct_span_err!(
|
|
|
|
cx.sess,
|
|
|
|
ident.span,
|
|
|
|
E0721,
|
|
|
|
"`await` is a keyword in the {} edition", cur_edition,
|
|
|
|
);
|
2019-01-25 16:03:27 -05:00
|
|
|
err.span_suggestion(
|
2018-09-20 17:36:31 -07:00
|
|
|
ident.span,
|
|
|
|
"you can use a raw identifier to stay compatible",
|
|
|
|
"r#await".to_string(),
|
|
|
|
Applicability::MachineApplicable,
|
|
|
|
);
|
|
|
|
err.emit();
|
|
|
|
}
|
|
|
|
return
|
|
|
|
},
|
2018-08-24 13:48:20 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
// don't lint `r#foo`
|
2018-09-20 17:36:31 -07:00
|
|
|
if is_raw_ident(ident) {
|
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
|
|
|
|
|
|
|
|
|
|
|
pub struct ExplicitOutlivesRequirements;
|
|
|
|
|
|
|
|
impl LintPass for ExplicitOutlivesRequirements {
|
2019-01-18 07:40:55 +01:00
|
|
|
fn name(&self) -> &'static str {
|
|
|
|
"ExplicitOutlivesRequirements"
|
|
|
|
}
|
|
|
|
|
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 get_lints(&self) -> LintArray {
|
|
|
|
lint_array![EXPLICIT_OUTLIVES_REQUIREMENTS]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ExplicitOutlivesRequirements {
|
|
|
|
fn collect_outlives_bound_spans(
|
|
|
|
&self,
|
2019-02-08 20:35:41 +09:00
|
|
|
cx: &LateContext<'_, '_>,
|
in which inferable outlives-requirements are linted
RFC 2093 (tracking issue #44493) lets us leave off
commonsensically inferable `T: 'a` outlives requirements. (A separate
feature-gate was split off for the case of 'static lifetimes, for
which questions still remain.) Detecting these was requested as an
idioms-2018 lint.
It turns out that issuing a correct, autofixable suggestion here is
somewhat subtle in the presence of other bounds and generic
parameters. Basically, we want to handle these three cases:
• One outlives-bound. We want to drop the bound altogether, including
the colon—
MyStruct<'a, T: 'a>
^^^^ help: remove this bound
• An outlives bound first, followed by a trait bound. We want to
delete the outlives bound and the following plus sign (and
hopefully get the whitespace right, too)—
MyStruct<'a, T: 'a + MyTrait>
^^^^^ help: remove this bound
• An outlives bound after a trait bound. We want to delete the
outlives lifetime and the preceding plus sign—
MyStruct<'a, T: MyTrait + 'a>
^^^^^ help: remove this bound
This gets (slightly) even more complicated in the case of where
clauses, where we want to drop the where clause altogether if there's
just the one bound. Hopefully the comments are enough to explain
what's going on!
A script (in Python, sorry) was used to generate the
hopefully-sufficiently-exhaustive UI test input. Some of these are
split off into a different file because rust-lang-nursery/rustfix#141
(and, causally upstream of that, #53934) prevents them from being
`run-rustfix`-tested.
We also make sure to include a UI test of a case (copied from RFC
2093) where the outlives-bound can't be inferred. Special thanks to
Niko Matsakis for pointing out the `inferred_outlives_of` query,
rather than blindly stripping outlives requirements as if we weren't a
production compiler and didn't care.
This concerns #52042.
2018-08-26 12:22:04 -07:00
|
|
|
item_def_id: DefId,
|
|
|
|
param_name: &str,
|
|
|
|
bounds: &hir::GenericBounds,
|
|
|
|
infer_static: bool
|
|
|
|
) -> Vec<(usize, Span)> {
|
|
|
|
// For lack of a more elegant strategy for comparing the `ty::Predicate`s
|
|
|
|
// returned by this query with the params/bounds grabbed from the HIR—and
|
|
|
|
// with some regrets—we're going to covert the param/lifetime names to
|
|
|
|
// strings
|
|
|
|
let inferred_outlives = cx.tcx.inferred_outlives_of(item_def_id);
|
|
|
|
|
|
|
|
let ty_lt_names = inferred_outlives.iter().filter_map(|pred| {
|
|
|
|
let binder = match pred {
|
|
|
|
ty::Predicate::TypeOutlives(binder) => binder,
|
|
|
|
_ => { return None; }
|
|
|
|
};
|
|
|
|
let ty_outlives_pred = binder.skip_binder();
|
|
|
|
let ty_name = match ty_outlives_pred.0.sty {
|
|
|
|
ty::Param(param) => param.name.to_string(),
|
|
|
|
_ => { return None; }
|
|
|
|
};
|
|
|
|
let lt_name = match ty_outlives_pred.1 {
|
|
|
|
ty::RegionKind::ReEarlyBound(region) => {
|
|
|
|
region.name.to_string()
|
|
|
|
},
|
|
|
|
_ => { return None; }
|
|
|
|
};
|
|
|
|
Some((ty_name, lt_name))
|
|
|
|
}).collect::<Vec<_>>();
|
|
|
|
|
|
|
|
let mut bound_spans = Vec::new();
|
|
|
|
for (i, bound) in bounds.iter().enumerate() {
|
|
|
|
if let hir::GenericBound::Outlives(lifetime) = bound {
|
|
|
|
let is_static = match lifetime.name {
|
|
|
|
hir::LifetimeName::Static => true,
|
|
|
|
_ => false
|
|
|
|
};
|
|
|
|
if is_static && !infer_static {
|
|
|
|
// infer-outlives for 'static is still feature-gated (tracking issue #44493)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
let lt_name = &lifetime.name.ident().to_string();
|
|
|
|
if ty_lt_names.contains(&(param_name.to_owned(), lt_name.to_owned())) {
|
|
|
|
bound_spans.push((i, bound.span()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bound_spans
|
|
|
|
}
|
|
|
|
|
|
|
|
fn consolidate_outlives_bound_spans(
|
|
|
|
&self,
|
|
|
|
lo: Span,
|
|
|
|
bounds: &hir::GenericBounds,
|
|
|
|
bound_spans: Vec<(usize, Span)>
|
|
|
|
) -> Vec<Span> {
|
|
|
|
if bounds.is_empty() {
|
|
|
|
return Vec::new();
|
|
|
|
}
|
|
|
|
if bound_spans.len() == bounds.len() {
|
|
|
|
let (_, last_bound_span) = bound_spans[bound_spans.len()-1];
|
|
|
|
// If all bounds are inferable, we want to delete the colon, so
|
|
|
|
// start from just after the parameter (span passed as argument)
|
|
|
|
vec![lo.to(last_bound_span)]
|
|
|
|
} else {
|
|
|
|
let mut merged = Vec::new();
|
|
|
|
let mut last_merged_i = None;
|
|
|
|
|
|
|
|
let mut from_start = true;
|
|
|
|
for (i, bound_span) in bound_spans {
|
|
|
|
match last_merged_i {
|
|
|
|
// If the first bound is inferable, our span should also eat the trailing `+`
|
|
|
|
None if i == 0 => {
|
|
|
|
merged.push(bound_span.to(bounds[1].span().shrink_to_lo()));
|
|
|
|
last_merged_i = Some(0);
|
|
|
|
},
|
|
|
|
// If consecutive bounds are inferable, merge their spans
|
|
|
|
Some(h) if i == h+1 => {
|
|
|
|
if let Some(tail) = merged.last_mut() {
|
|
|
|
// Also eat the trailing `+` if the first
|
|
|
|
// more-than-one bound is inferable
|
|
|
|
let to_span = if from_start && i < bounds.len() {
|
|
|
|
bounds[i+1].span().shrink_to_lo()
|
|
|
|
} else {
|
|
|
|
bound_span
|
|
|
|
};
|
|
|
|
*tail = tail.to(to_span);
|
|
|
|
last_merged_i = Some(i);
|
|
|
|
} else {
|
|
|
|
bug!("another bound-span visited earlier");
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => {
|
|
|
|
// When we find a non-inferable bound, subsequent inferable bounds
|
|
|
|
// won't be consecutive from the start (and we'll eat the leading
|
|
|
|
// `+` rather than the trailing one)
|
|
|
|
from_start = false;
|
|
|
|
merged.push(bounds[i-1].span().shrink_to_hi().to(bound_span));
|
|
|
|
last_merged_i = Some(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
merged
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ExplicitOutlivesRequirements {
|
|
|
|
fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item) {
|
|
|
|
let infer_static = cx.tcx.features().infer_static_outlives_requirements;
|
2019-02-27 17:35:24 +01:00
|
|
|
let def_id = cx.tcx.hir().local_def_id_from_hir_id(item.hir_id);
|
in which inferable outlives-requirements are linted
RFC 2093 (tracking issue #44493) lets us leave off
commonsensically inferable `T: 'a` outlives requirements. (A separate
feature-gate was split off for the case of 'static lifetimes, for
which questions still remain.) Detecting these was requested as an
idioms-2018 lint.
It turns out that issuing a correct, autofixable suggestion here is
somewhat subtle in the presence of other bounds and generic
parameters. Basically, we want to handle these three cases:
• One outlives-bound. We want to drop the bound altogether, including
the colon—
MyStruct<'a, T: 'a>
^^^^ help: remove this bound
• An outlives bound first, followed by a trait bound. We want to
delete the outlives bound and the following plus sign (and
hopefully get the whitespace right, too)—
MyStruct<'a, T: 'a + MyTrait>
^^^^^ help: remove this bound
• An outlives bound after a trait bound. We want to delete the
outlives lifetime and the preceding plus sign—
MyStruct<'a, T: MyTrait + 'a>
^^^^^ help: remove this bound
This gets (slightly) even more complicated in the case of where
clauses, where we want to drop the where clause altogether if there's
just the one bound. Hopefully the comments are enough to explain
what's going on!
A script (in Python, sorry) was used to generate the
hopefully-sufficiently-exhaustive UI test input. Some of these are
split off into a different file because rust-lang-nursery/rustfix#141
(and, causally upstream of that, #53934) prevents them from being
`run-rustfix`-tested.
We also make sure to include a UI test of a case (copied from RFC
2093) where the outlives-bound can't be inferred. Special thanks to
Niko Matsakis for pointing out the `inferred_outlives_of` query,
rather than blindly stripping outlives requirements as if we weren't a
production compiler and didn't care.
This concerns #52042.
2018-08-26 12:22:04 -07:00
|
|
|
if let hir::ItemKind::Struct(_, ref generics) = item.node {
|
|
|
|
let mut bound_count = 0;
|
|
|
|
let mut lint_spans = Vec::new();
|
|
|
|
|
|
|
|
for param in &generics.params {
|
|
|
|
let param_name = match param.kind {
|
2019-02-15 22:25:42 +00:00
|
|
|
hir::GenericParamKind::Lifetime { .. } => continue,
|
in which inferable outlives-requirements are linted
RFC 2093 (tracking issue #44493) lets us leave off
commonsensically inferable `T: 'a` outlives requirements. (A separate
feature-gate was split off for the case of 'static lifetimes, for
which questions still remain.) Detecting these was requested as an
idioms-2018 lint.
It turns out that issuing a correct, autofixable suggestion here is
somewhat subtle in the presence of other bounds and generic
parameters. Basically, we want to handle these three cases:
• One outlives-bound. We want to drop the bound altogether, including
the colon—
MyStruct<'a, T: 'a>
^^^^ help: remove this bound
• An outlives bound first, followed by a trait bound. We want to
delete the outlives bound and the following plus sign (and
hopefully get the whitespace right, too)—
MyStruct<'a, T: 'a + MyTrait>
^^^^^ help: remove this bound
• An outlives bound after a trait bound. We want to delete the
outlives lifetime and the preceding plus sign—
MyStruct<'a, T: MyTrait + 'a>
^^^^^ help: remove this bound
This gets (slightly) even more complicated in the case of where
clauses, where we want to drop the where clause altogether if there's
just the one bound. Hopefully the comments are enough to explain
what's going on!
A script (in Python, sorry) was used to generate the
hopefully-sufficiently-exhaustive UI test input. Some of these are
split off into a different file because rust-lang-nursery/rustfix#141
(and, causally upstream of that, #53934) prevents them from being
`run-rustfix`-tested.
We also make sure to include a UI test of a case (copied from RFC
2093) where the outlives-bound can't be inferred. Special thanks to
Niko Matsakis for pointing out the `inferred_outlives_of` query,
rather than blindly stripping outlives requirements as if we weren't a
production compiler and didn't care.
This concerns #52042.
2018-08-26 12:22:04 -07:00
|
|
|
hir::GenericParamKind::Type { .. } => {
|
|
|
|
match param.name {
|
2019-02-15 22:25:42 +00:00
|
|
|
hir::ParamName::Fresh(_) => continue,
|
|
|
|
hir::ParamName::Error => continue,
|
|
|
|
hir::ParamName::Plain(name) => name.to_string(),
|
in which inferable outlives-requirements are linted
RFC 2093 (tracking issue #44493) lets us leave off
commonsensically inferable `T: 'a` outlives requirements. (A separate
feature-gate was split off for the case of 'static lifetimes, for
which questions still remain.) Detecting these was requested as an
idioms-2018 lint.
It turns out that issuing a correct, autofixable suggestion here is
somewhat subtle in the presence of other bounds and generic
parameters. Basically, we want to handle these three cases:
• One outlives-bound. We want to drop the bound altogether, including
the colon—
MyStruct<'a, T: 'a>
^^^^ help: remove this bound
• An outlives bound first, followed by a trait bound. We want to
delete the outlives bound and the following plus sign (and
hopefully get the whitespace right, too)—
MyStruct<'a, T: 'a + MyTrait>
^^^^^ help: remove this bound
• An outlives bound after a trait bound. We want to delete the
outlives lifetime and the preceding plus sign—
MyStruct<'a, T: MyTrait + 'a>
^^^^^ help: remove this bound
This gets (slightly) even more complicated in the case of where
clauses, where we want to drop the where clause altogether if there's
just the one bound. Hopefully the comments are enough to explain
what's going on!
A script (in Python, sorry) was used to generate the
hopefully-sufficiently-exhaustive UI test input. Some of these are
split off into a different file because rust-lang-nursery/rustfix#141
(and, causally upstream of that, #53934) prevents them from being
`run-rustfix`-tested.
We also make sure to include a UI test of a case (copied from RFC
2093) where the outlives-bound can't be inferred. Special thanks to
Niko Matsakis for pointing out the `inferred_outlives_of` query,
rather than blindly stripping outlives requirements as if we weren't a
production compiler and didn't care.
This concerns #52042.
2018-08-26 12:22:04 -07:00
|
|
|
}
|
|
|
|
}
|
2019-02-15 22:25:42 +00:00
|
|
|
hir::GenericParamKind::Const { .. } => continue,
|
in which inferable outlives-requirements are linted
RFC 2093 (tracking issue #44493) lets us leave off
commonsensically inferable `T: 'a` outlives requirements. (A separate
feature-gate was split off for the case of 'static lifetimes, for
which questions still remain.) Detecting these was requested as an
idioms-2018 lint.
It turns out that issuing a correct, autofixable suggestion here is
somewhat subtle in the presence of other bounds and generic
parameters. Basically, we want to handle these three cases:
• One outlives-bound. We want to drop the bound altogether, including
the colon—
MyStruct<'a, T: 'a>
^^^^ help: remove this bound
• An outlives bound first, followed by a trait bound. We want to
delete the outlives bound and the following plus sign (and
hopefully get the whitespace right, too)—
MyStruct<'a, T: 'a + MyTrait>
^^^^^ help: remove this bound
• An outlives bound after a trait bound. We want to delete the
outlives lifetime and the preceding plus sign—
MyStruct<'a, T: MyTrait + 'a>
^^^^^ help: remove this bound
This gets (slightly) even more complicated in the case of where
clauses, where we want to drop the where clause altogether if there's
just the one bound. Hopefully the comments are enough to explain
what's going on!
A script (in Python, sorry) was used to generate the
hopefully-sufficiently-exhaustive UI test input. Some of these are
split off into a different file because rust-lang-nursery/rustfix#141
(and, causally upstream of that, #53934) prevents them from being
`run-rustfix`-tested.
We also make sure to include a UI test of a case (copied from RFC
2093) where the outlives-bound can't be inferred. Special thanks to
Niko Matsakis for pointing out the `inferred_outlives_of` query,
rather than blindly stripping outlives requirements as if we weren't a
production compiler and didn't care.
This concerns #52042.
2018-08-26 12:22:04 -07:00
|
|
|
};
|
|
|
|
let bound_spans = self.collect_outlives_bound_spans(
|
|
|
|
cx, def_id, ¶m_name, ¶m.bounds, infer_static
|
|
|
|
);
|
|
|
|
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;
|
|
|
|
let num_predicates = generics.where_clause.predicates.len();
|
|
|
|
for (i, where_predicate) in generics.where_clause.predicates.iter().enumerate() {
|
|
|
|
if let hir::WherePredicate::BoundPredicate(predicate) = where_predicate {
|
|
|
|
let param_name = match predicate.bounded_ty.node {
|
|
|
|
hir::TyKind::Path(ref qpath) => {
|
|
|
|
if let hir::QPath::Resolved(None, ty_param_path) = qpath {
|
|
|
|
ty_param_path.segments[0].ident.to_string()
|
|
|
|
} else {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => { continue; }
|
|
|
|
};
|
|
|
|
let bound_spans = self.collect_outlives_bound_spans(
|
|
|
|
cx, def_id, ¶m_name, &predicate.bounds, infer_static
|
|
|
|
);
|
|
|
|
bound_count += bound_spans.len();
|
|
|
|
|
|
|
|
let drop_predicate = bound_spans.len() == predicate.bounds.len();
|
|
|
|
if drop_predicate {
|
|
|
|
dropped_predicate_count += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If all the bounds on a predicate were inferable and there are
|
|
|
|
// further predicates, we want to eat the trailing comma
|
|
|
|
if drop_predicate && i + 1 < num_predicates {
|
|
|
|
let next_predicate_span = generics.where_clause.predicates[i+1].span();
|
|
|
|
where_lint_spans.push(
|
|
|
|
predicate.span.to(next_predicate_span.shrink_to_lo())
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
where_lint_spans.extend(
|
|
|
|
self.consolidate_outlives_bound_spans(
|
|
|
|
predicate.span.shrink_to_lo(),
|
|
|
|
&predicate.bounds,
|
|
|
|
bound_spans
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If all predicates are inferable, drop the entire clause
|
|
|
|
// (including the `where`)
|
|
|
|
if num_predicates > 0 && dropped_predicate_count == num_predicates {
|
|
|
|
let full_where_span = generics.span.shrink_to_hi()
|
|
|
|
.to(generics.where_clause.span()
|
|
|
|
.expect("span of (nonempty) where clause should exist"));
|
|
|
|
lint_spans.push(
|
|
|
|
full_where_span
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
lint_spans.extend(where_lint_spans);
|
|
|
|
}
|
|
|
|
|
|
|
|
if !lint_spans.is_empty() {
|
|
|
|
let mut err = cx.struct_span_lint(
|
|
|
|
EXPLICIT_OUTLIVES_REQUIREMENTS,
|
|
|
|
lint_spans.clone(),
|
|
|
|
"outlives requirements can be inferred"
|
|
|
|
);
|
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();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|