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
|
2020-03-29 16:41:09 +02:00
|
|
|
//! AST visitor. Also see `rustc_session::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
|
2020-03-29 16:41:09 +02:00
|
|
|
//! `rustc_session::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
|
|
|
|
2020-07-13 23:06:35 +10:00
|
|
|
use crate::{
|
|
|
|
types::CItemKind, EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext,
|
|
|
|
};
|
2020-02-29 20:37:32 +03:00
|
|
|
use rustc_ast::attr::{self, HasAttrs};
|
|
|
|
use rustc_ast::tokenstream::{TokenStream, TokenTree};
|
|
|
|
use rustc_ast::visit::{FnCtxt, FnKind};
|
2020-04-27 23:26:11 +05:30
|
|
|
use rustc_ast::{self as ast, *};
|
2020-01-11 17:02:46 +01:00
|
|
|
use rustc_ast_pretty::pprust::{self, expr_to_string};
|
2020-06-19 18:14:54 +10:00
|
|
|
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
2020-08-18 02:00:35 +10:00
|
|
|
use rustc_data_structures::stack::ensure_sufficient_stack;
|
2020-06-19 18:14:54 +10:00
|
|
|
use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString};
|
2019-12-22 17:42:04 -05:00
|
|
|
use rustc_feature::{deprecated_attributes, AttributeGate, AttributeTemplate, AttributeType};
|
2020-05-09 14:04:32 +02:00
|
|
|
use rustc_feature::{GateIssue, Stability};
|
2020-01-05 02:37:57 +01:00
|
|
|
use rustc_hir as hir;
|
|
|
|
use rustc_hir::def::{DefKind, Res};
|
|
|
|
use rustc_hir::def_id::DefId;
|
2020-06-19 18:14:54 +10:00
|
|
|
use rustc_hir::{ForeignItemKind, GenericParamKind, PatKind};
|
|
|
|
use rustc_hir::{HirId, HirIdSet, Node};
|
2020-08-25 14:00:24 +10:00
|
|
|
use rustc_index::vec::Idx;
|
2020-03-29 16:41:09 +02:00
|
|
|
use rustc_middle::lint::LintDiagnosticBuilder;
|
2020-09-03 14:33:55 +03:00
|
|
|
use rustc_middle::ty::print::with_no_trimmed_paths;
|
2020-07-07 21:08:14 +10:00
|
|
|
use rustc_middle::ty::subst::{GenericArgKind, Subst};
|
2020-08-26 23:32:57 +10:00
|
|
|
use rustc_middle::ty::{self, layout::LayoutError, Ty, TyCtxt};
|
2020-01-05 10:07:26 +01:00
|
|
|
use rustc_session::lint::FutureIncompatibleInfo;
|
2020-07-30 11:27:50 +10:00
|
|
|
use rustc_session::Session;
|
2020-01-01 19:40:49 +01:00
|
|
|
use rustc_span::edition::Edition;
|
2020-01-01 19:25:28 +01:00
|
|
|
use rustc_span::source_map::Spanned;
|
2020-04-19 13:00:18 +02:00
|
|
|
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
2019-12-31 20:15:40 +03:00
|
|
|
use rustc_span::{BytePos, Span};
|
2020-07-07 21:08:14 +10:00
|
|
|
use rustc_target::abi::{LayoutOf, VariantIdx};
|
2020-02-11 21:19:40 +01:00
|
|
|
use rustc_trait_selection::traits::misc::can_type_implement_copy;
|
2015-02-25 22:44:44 +11:00
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
use crate::nonstandard_style::{method_context, MethodLateContext};
|
2019-02-08 20:35:41 +09:00
|
|
|
|
2020-01-05 10:07:26 +01:00
|
|
|
use std::fmt::Write;
|
2020-08-13 23:05:01 -07:00
|
|
|
use tracing::{debug, trace};
|
2015-09-14 22:36:39 -04:00
|
|
|
|
2020-03-29 15:24:45 +02:00
|
|
|
// hardwired lints from librustc_middle
|
2020-01-09 07:52:01 +01:00
|
|
|
pub use rustc_session::lint::builtin::*;
|
2015-02-25 22:44:44 +11:00
|
|
|
|
|
|
|
declare_lint! {
|
|
|
|
WHILE_TRUE,
|
|
|
|
Warn,
|
|
|
|
"suggest using `loop { }` instead of `while true { }`"
|
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
declare_lint_pass!(WhileTrue => [WHILE_TRUE]);
|
2015-02-25 22:44:44 +11:00
|
|
|
|
2019-06-22 01:30:24 +02:00
|
|
|
/// Traverse through any amount of parenthesis and return the first non-parens expression.
|
|
|
|
fn pierce_parens(mut expr: &ast::Expr) -> &ast::Expr {
|
2019-09-26 14:39:48 +01:00
|
|
|
while let ast::ExprKind::Paren(sub) = &expr.kind {
|
2019-06-22 01:30:24 +02:00
|
|
|
expr = sub;
|
2019-06-19 17:21:28 +02:00
|
|
|
}
|
2019-06-22 01:30:24 +02:00
|
|
|
expr
|
2019-06-19 17:21:28 +02:00
|
|
|
}
|
|
|
|
|
2019-06-22 01:30:24 +02:00
|
|
|
impl EarlyLintPass for WhileTrue {
|
|
|
|
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
|
2019-09-26 14:39:48 +01:00
|
|
|
if let ast::ExprKind::While(cond, ..) = &e.kind {
|
|
|
|
if let ast::ExprKind::Lit(ref lit) = pierce_parens(cond).kind {
|
2019-09-26 16:56:53 +01:00
|
|
|
if let ast::LitKind::Bool(true) = lit.kind {
|
2019-08-11 01:08:30 +03:00
|
|
|
if !lit.span.from_expansion() {
|
2017-09-29 23:52:51 -07:00
|
|
|
let msg = "denote infinite loops with `loop { ... }`";
|
2020-03-09 11:42:37 -07:00
|
|
|
let condition_span = cx.sess.source_map().guess_head_span(e.span);
|
2020-01-31 22:24:57 +10:00
|
|
|
cx.struct_span_lint(WHILE_TRUE, condition_span, |lint| {
|
|
|
|
lint.build(msg)
|
|
|
|
.span_suggestion_short(
|
2020-02-02 09:47:58 +10:00
|
|
|
condition_span,
|
|
|
|
"use `loop`",
|
|
|
|
"loop".to_owned(),
|
|
|
|
Applicability::MachineApplicable,
|
|
|
|
)
|
|
|
|
.emit();
|
2020-01-31 22:24:57 +10:00
|
|
|
})
|
2017-09-01 17:45:46 +02:00
|
|
|
}
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
declare_lint! {
|
|
|
|
BOX_POINTERS,
|
|
|
|
Allow,
|
|
|
|
"use of owned (Box type) heap memory"
|
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
declare_lint_pass!(BoxPointers => [BOX_POINTERS]);
|
2015-02-25 22:44:44 +11:00
|
|
|
|
|
|
|
impl BoxPointers {
|
2020-06-25 23:41:36 +03:00
|
|
|
fn check_heap_type(&self, cx: &LateContext<'_>, span: Span, ty: Ty<'_>) {
|
2020-03-23 03:57:04 +02:00
|
|
|
for leaf in ty.walk() {
|
|
|
|
if let GenericArgKind::Type(leaf_ty) = leaf.unpack() {
|
|
|
|
if leaf_ty.is_box() {
|
|
|
|
cx.struct_span_lint(BOX_POINTERS, span, |lint| {
|
|
|
|
lint.build(&format!("type uses owned (Box type) pointers: {}", ty)).emit()
|
|
|
|
});
|
|
|
|
}
|
2015-06-25 23:42:17 +03:00
|
|
|
}
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-25 23:41:36 +03:00
|
|
|
impl<'tcx> LateLintPass<'tcx> for BoxPointers {
|
|
|
|
fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
|
2019-09-26 17:51:36 +01:00
|
|
|
match it.kind {
|
2019-12-22 17:42:04 -05:00
|
|
|
hir::ItemKind::Fn(..)
|
|
|
|
| hir::ItemKind::TyAlias(..)
|
|
|
|
| hir::ItemKind::Enum(..)
|
|
|
|
| hir::ItemKind::Struct(..)
|
|
|
|
| hir::ItemKind::Union(..) => {
|
2019-06-27 11:28:14 +02:00
|
|
|
let def_id = cx.tcx.hir().local_def_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
|
|
|
}
|
2019-12-22 17:42:04 -05:00
|
|
|
_ => (),
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
// If it's a struct, we also have to check the fields' types
|
2019-09-26 17:51:36 +01:00
|
|
|
match it.kind {
|
2019-12-22 17:42:04 -05: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-06-27 11:28:14 +02:00
|
|
|
let def_id = cx.tcx.hir().local_def_id(struct_field.hir_id);
|
2019-12-22 17:42:04 -05:00
|
|
|
self.check_heap_type(cx, struct_field.span, 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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-25 23:41:36 +03:00
|
|
|
fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) {
|
2020-07-17 08:47:04 +00:00
|
|
|
let ty = cx.typeck_results().node_type(e.hir_id);
|
2015-02-25 22:44:44 +11:00
|
|
|
self.check_heap_type(cx, e.span, ty);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
declare_lint! {
|
|
|
|
NON_SHORTHAND_FIELD_PATTERNS,
|
|
|
|
Warn,
|
2017-10-11 23:06:45 -07:00
|
|
|
"using `Struct { x: x }` instead of `Struct { x }` in a pattern"
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
declare_lint_pass!(NonShorthandFieldPatterns => [NON_SHORTHAND_FIELD_PATTERNS]);
|
2015-02-25 22:44:44 +11:00
|
|
|
|
2020-06-25 23:41:36 +03:00
|
|
|
impl<'tcx> LateLintPass<'tcx> for NonShorthandFieldPatterns {
|
|
|
|
fn check_pat(&mut self, cx: &LateContext<'_>, pat: &hir::Pat<'_>) {
|
2019-11-30 15:08:22 +01:00
|
|
|
if let PatKind::Struct(ref qpath, field_pats, _) = pat.kind {
|
2019-12-22 17:42:04 -05:00
|
|
|
let variant = cx
|
2020-07-17 08:47:04 +00:00
|
|
|
.typeck_results()
|
2019-12-22 17:42:04 -05:00
|
|
|
.pat_ty(pat)
|
|
|
|
.ty_adt_def()
|
|
|
|
.expect("struct pattern type is not an ADT")
|
2020-06-26 05:55:23 +03:00
|
|
|
.variant_of_res(cx.qpath_res(qpath, pat.hir_id));
|
2016-06-03 23:15:00 +03:00
|
|
|
for fieldpat in field_pats {
|
2019-08-15 02:35:36 +03:00
|
|
|
if fieldpat.is_shorthand {
|
2016-06-03 23:15:00 +03:00
|
|
|
continue;
|
2015-09-10 15:53:08 -04:00
|
|
|
}
|
2019-08-11 01:08:30 +03:00
|
|
|
if fieldpat.span.from_expansion() {
|
2018-04-05 03:20:21 +03:00
|
|
|
// Don't lint if this is a macro expansion: macro authors
|
|
|
|
// shouldn't have to worry about this kind of style issue
|
|
|
|
// (Issue #49588)
|
|
|
|
continue;
|
|
|
|
}
|
2019-12-22 07:59:38 +09:00
|
|
|
if let PatKind::Binding(binding_annot, _, ident, None) = fieldpat.pat.kind {
|
2019-12-22 17:42:04 -05:00
|
|
|
if cx.tcx.find_field_index(ident, &variant)
|
2020-07-17 08:47:04 +00:00
|
|
|
== Some(cx.tcx.field_index(fieldpat.hir_id, cx.typeck_results()))
|
2019-12-22 17:42:04 -05:00
|
|
|
{
|
2020-02-02 09:47:58 +10:00
|
|
|
cx.struct_span_lint(NON_SHORTHAND_FIELD_PATTERNS, fieldpat.span, |lint| {
|
|
|
|
let mut err = lint
|
|
|
|
.build(&format!("the `{}:` in this pattern is redundant", ident));
|
|
|
|
let binding = match binding_annot {
|
|
|
|
hir::BindingAnnotation::Unannotated => None,
|
|
|
|
hir::BindingAnnotation::Mutable => Some("mut"),
|
|
|
|
hir::BindingAnnotation::Ref => Some("ref"),
|
|
|
|
hir::BindingAnnotation::RefMut => Some("ref mut"),
|
|
|
|
};
|
|
|
|
let ident = if let Some(binding) = binding {
|
|
|
|
format!("{} {}", binding, ident)
|
|
|
|
} else {
|
|
|
|
ident.to_string()
|
|
|
|
};
|
|
|
|
err.span_suggestion(
|
|
|
|
fieldpat.span,
|
|
|
|
"use shorthand field pattern",
|
|
|
|
ident,
|
|
|
|
Applicability::MachineApplicable,
|
|
|
|
);
|
|
|
|
err.emit();
|
|
|
|
});
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
declare_lint! {
|
|
|
|
UNSAFE_CODE,
|
|
|
|
Allow,
|
|
|
|
"usage of `unsafe` code"
|
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
declare_lint_pass!(UnsafeCode => [UNSAFE_CODE]);
|
2015-02-25 22:44:44 +11:00
|
|
|
|
2017-08-08 18:21:20 +03:00
|
|
|
impl UnsafeCode {
|
2020-02-03 19:57:45 +10:00
|
|
|
fn report_unsafe(
|
|
|
|
&self,
|
|
|
|
cx: &EarlyContext<'_>,
|
|
|
|
span: Span,
|
|
|
|
decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>),
|
|
|
|
) {
|
2019-02-28 22:43:53 +00:00
|
|
|
// This comes from a macro that has `#[allow_internal_unsafe]`.
|
2017-08-08 18:21:20 +03:00
|
|
|
if span.allows_unsafe() {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-02-02 19:41:14 +10:00
|
|
|
cx.struct_span_lint(UNSAFE_CODE, span, decorate);
|
2017-08-08 18:21:20 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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) {
|
2020-07-30 11:27:50 +10:00
|
|
|
if cx.sess().check_name(attr, sym::allow_internal_unsafe) {
|
2020-02-03 19:57:45 +10:00
|
|
|
self.report_unsafe(cx, attr.span, |lint| {
|
|
|
|
lint.build(
|
|
|
|
"`allow_internal_unsafe` allows defining \
|
2019-01-15 18:01:38 +09:00
|
|
|
macros using unsafe without triggering \
|
2020-02-03 19:57:45 +10:00
|
|
|
the `unsafe_code` lint at their call site",
|
|
|
|
)
|
|
|
|
.emit()
|
|
|
|
});
|
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-09-26 14:39:48 +01:00
|
|
|
if let ast::ExprKind::Block(ref blk, _) = e.kind {
|
2019-02-28 22:43:53 +00:00
|
|
|
// Don't warn about generated blocks; that'll just pollute the output.
|
2019-01-15 06:54:28 +09:00
|
|
|
if blk.rules == ast::BlockCheckMode::Unsafe(ast::UserProvided) {
|
2020-02-03 19:57:45 +10:00
|
|
|
self.report_unsafe(cx, blk.span, |lint| {
|
|
|
|
lint.build("usage of an `unsafe` block").emit()
|
|
|
|
});
|
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) {
|
2019-09-26 17:51:36 +01:00
|
|
|
match it.kind {
|
2020-01-30 02:42:33 +01:00
|
|
|
ast::ItemKind::Trait(_, ast::Unsafe::Yes(_), ..) => {
|
2020-02-03 19:57:45 +10:00
|
|
|
self.report_unsafe(cx, it.span, |lint| {
|
|
|
|
lint.build("declaration of an `unsafe` trait").emit()
|
|
|
|
})
|
2016-10-09 09:38:07 +05:30
|
|
|
}
|
2015-02-25 22:44:44 +11:00
|
|
|
|
2020-01-30 02:42:33 +01:00
|
|
|
ast::ItemKind::Impl { unsafety: ast::Unsafe::Yes(_), .. } => {
|
2020-02-03 19:57:45 +10:00
|
|
|
self.report_unsafe(cx, it.span, |lint| {
|
|
|
|
lint.build("implementation of an `unsafe` trait").emit()
|
|
|
|
})
|
2016-10-09 09:38:07 +05:30
|
|
|
}
|
2015-02-25 22:44:44 +11:00
|
|
|
|
2020-03-20 15:03:11 +01:00
|
|
|
_ => {}
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-30 00:18:54 +01:00
|
|
|
fn check_fn(&mut self, cx: &EarlyContext<'_>, fk: FnKind<'_>, span: Span, _: ast::NodeId) {
|
|
|
|
if let FnKind::Fn(
|
|
|
|
ctxt,
|
|
|
|
_,
|
2020-01-30 02:42:33 +01:00
|
|
|
ast::FnSig { header: ast::FnHeader { unsafety: ast::Unsafe::Yes(_), .. }, .. },
|
2020-01-30 00:18:54 +01:00
|
|
|
_,
|
|
|
|
body,
|
|
|
|
) = fk
|
|
|
|
{
|
|
|
|
let msg = match ctxt {
|
|
|
|
FnCtxt::Foreign => return,
|
|
|
|
FnCtxt::Free => "declaration of an `unsafe` function",
|
|
|
|
FnCtxt::Assoc(_) if body.is_none() => "declaration of an `unsafe` method",
|
|
|
|
FnCtxt::Assoc(_) => "implementation of an `unsafe` method",
|
|
|
|
};
|
2020-02-02 19:41:14 +10:00
|
|
|
self.report_unsafe(cx, span, |lint| lint.build(msg).emit());
|
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",
|
2019-10-08 18:47:08 -04:00
|
|
|
report_in_external_macro
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct MissingDoc {
|
2019-02-08 14:53:55 +01:00
|
|
|
/// Stack of whether `#[doc(hidden)]` is set at each level which has lint attributes.
|
2015-02-25 22:44:44 +11:00
|
|
|
doc_hidden_stack: Vec<bool>,
|
2015-03-29 23:41:54 -04:00
|
|
|
|
|
|
|
/// Private traits or trait items that leaked through. Don't check their methods.
|
2019-02-26 11:04:58 +01:00
|
|
|
private_traits: FxHashSet<hir::HirId>,
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
impl_lint_pass!(MissingDoc => [MISSING_DOCS]);
|
|
|
|
|
2020-07-30 11:27:50 +10:00
|
|
|
fn has_doc(sess: &Session, attr: &ast::Attribute) -> bool {
|
2019-12-07 21:28:29 +03:00
|
|
|
if attr.is_doc_comment() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-07-30 11:27:50 +10:00
|
|
|
if !sess.check_name(attr, sym::doc) {
|
2018-12-01 14:46:25 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if attr.is_value_str() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(list) = attr.meta_item_list() {
|
|
|
|
for meta in list {
|
2020-08-02 13:17:20 +03:00
|
|
|
if meta.has_name(sym::include) || meta.has_name(sym::hidden) {
|
2018-12-01 14:46:25 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
false
|
|
|
|
}
|
|
|
|
|
2015-02-25 22:44:44 +11:00
|
|
|
impl MissingDoc {
|
|
|
|
pub fn new() -> MissingDoc {
|
2019-12-22 17:42:04 -05:00
|
|
|
MissingDoc { doc_hidden_stack: vec![false], 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")
|
|
|
|
}
|
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
fn check_missing_docs_attrs(
|
|
|
|
&self,
|
2020-06-25 23:41:36 +03:00
|
|
|
cx: &LateContext<'_>,
|
2019-12-22 17:42:04 -05:00
|
|
|
id: Option<hir::HirId>,
|
|
|
|
attrs: &[ast::Attribute],
|
|
|
|
sp: Span,
|
2020-03-21 21:32:35 -05:00
|
|
|
article: &'static str,
|
|
|
|
desc: &'static str,
|
2019-12-22 17:42:04 -05:00
|
|
|
) {
|
2015-02-25 22:44:44 +11:00
|
|
|
// If we're building a test harness, then warning about
|
|
|
|
// documentation is probably not really relevant right now.
|
2015-02-28 13:31:14 +01:00
|
|
|
if cx.sess().opts.test {
|
|
|
|
return;
|
|
|
|
}
|
2015-02-25 22:44:44 +11:00
|
|
|
|
|
|
|
// `#[doc(hidden)]` disables missing_docs check.
|
2015-02-28 13:31:14 +01:00
|
|
|
if self.doc_hidden() {
|
|
|
|
return;
|
|
|
|
}
|
2015-02-25 22:44:44 +11:00
|
|
|
|
|
|
|
// Only check publicly-visible items, using the result from the privacy pass.
|
|
|
|
// It's an option so the crate root can also use this function (it doesn't
|
2019-02-28 22:43:53 +00:00
|
|
|
// have a `NodeId`).
|
2015-11-19 14:16:35 +03:00
|
|
|
if let Some(id) = id {
|
2019-03-11 11:03:19 +01:00
|
|
|
if !cx.access_levels.is_exported(id) {
|
2015-02-25 22:44:44 +11:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-30 11:27:50 +10:00
|
|
|
let has_doc = attrs.iter().any(|a| has_doc(cx.sess(), a));
|
2015-02-25 22:44:44 +11:00
|
|
|
if !has_doc {
|
2020-03-09 11:42:37 -07:00
|
|
|
cx.struct_span_lint(
|
|
|
|
MISSING_DOCS,
|
|
|
|
cx.tcx.sess.source_map().guess_head_span(sp),
|
|
|
|
|lint| {
|
|
|
|
lint.build(&format!("missing documentation for {} {}", article, desc)).emit()
|
|
|
|
},
|
|
|
|
);
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-25 23:41:36 +03:00
|
|
|
impl<'tcx> LateLintPass<'tcx> for MissingDoc {
|
2020-07-30 11:27:50 +10:00
|
|
|
fn enter_lint_attrs(&mut self, cx: &LateContext<'_>, attrs: &[ast::Attribute]) {
|
2019-12-22 17:42:04 -05:00
|
|
|
let doc_hidden = self.doc_hidden()
|
|
|
|
|| attrs.iter().any(|attr| {
|
2020-07-30 11:27:50 +10:00
|
|
|
cx.sess().check_name(attr, sym::doc)
|
2019-12-22 17:42:04 -05:00
|
|
|
&& match attr.meta_item_list() {
|
|
|
|
None => false,
|
|
|
|
Some(l) => attr::list_contains_name(&l, sym::hidden),
|
|
|
|
}
|
|
|
|
});
|
2015-02-25 22:44:44 +11:00
|
|
|
self.doc_hidden_stack.push(doc_hidden);
|
|
|
|
}
|
|
|
|
|
2020-06-25 23:41:36 +03: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");
|
|
|
|
}
|
|
|
|
|
2020-06-25 23:41:36 +03:00
|
|
|
fn check_crate(&mut self, cx: &LateContext<'_>, krate: &hir::Crate<'_>) {
|
2020-03-22 14:37:51 -05:00
|
|
|
self.check_missing_docs_attrs(cx, None, &krate.item.attrs, krate.item.span, "the", "crate");
|
2018-12-01 14:46:25 +01:00
|
|
|
|
2019-11-28 11:49:29 +01:00
|
|
|
for macro_def in krate.exported_macros {
|
2020-07-30 11:27:50 +10:00
|
|
|
let has_doc = macro_def.attrs.iter().any(|a| has_doc(cx.sess(), a));
|
2018-12-01 14:46:25 +01:00
|
|
|
if !has_doc {
|
2020-02-02 19:41:14 +10:00
|
|
|
cx.struct_span_lint(
|
2019-12-22 17:42:04 -05:00
|
|
|
MISSING_DOCS,
|
2020-03-09 11:42:37 -07:00
|
|
|
cx.tcx.sess.source_map().guess_head_span(macro_def.span),
|
2020-02-02 19:41:14 +10:00
|
|
|
|lint| lint.build("missing documentation for macro").emit(),
|
2019-12-22 17:42:04 -05:00
|
|
|
);
|
2018-12-01 14:46:25 +01:00
|
|
|
}
|
|
|
|
}
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
|
2020-06-25 23:41:36 +03:00
|
|
|
fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
|
2020-03-15 21:48:09 -05:00
|
|
|
match it.kind {
|
2019-11-28 19:28:50 +01:00
|
|
|
hir::ItemKind::Trait(.., trait_item_refs) => {
|
2019-02-28 22:43:53 +00:00
|
|
|
// Issue #11592: traits are always considered exported, even when private.
|
2018-03-21 01:58:25 +03:00
|
|
|
if let hir::VisibilityKind::Inherited = it.vis.node {
|
2019-02-26 11:04:58 +01:00
|
|
|
self.private_traits.insert(it.hir_id);
|
2016-12-04 04:21:06 +02:00
|
|
|
for trait_item_ref in trait_item_refs {
|
2019-03-01 10:28:13 +01:00
|
|
|
self.private_traits.insert(trait_item_ref.id.hir_id);
|
2015-03-29 23:41:54 -04:00
|
|
|
}
|
2016-10-09 09:38:07 +05:30
|
|
|
return;
|
2015-03-29 23:41:54 -04:00
|
|
|
}
|
2016-10-09 09:38:07 +05:30
|
|
|
}
|
2020-01-17 16:14:29 -08:00
|
|
|
hir::ItemKind::Impl { of_trait: Some(ref trait_ref), items, .. } => {
|
2019-02-28 22:43:53 +00:00
|
|
|
// If the trait is private, add the impl items to `private_traits` so they don't get
|
2015-03-29 23:41:54 -04:00
|
|
|
// reported for missing docs.
|
2019-04-20 19:36:05 +03:00
|
|
|
let real_trait = trait_ref.path.res.def_id();
|
2020-04-23 20:48:40 +01:00
|
|
|
if let Some(def_id) = real_trait.as_local() {
|
2020-08-12 12:22:56 +02:00
|
|
|
let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id);
|
2020-03-15 21:48:09 -05:00
|
|
|
if let Some(Node::Item(item)) = cx.tcx.hir().find(hir_id) {
|
|
|
|
if let hir::VisibilityKind::Inherited = item.vis.node {
|
|
|
|
for impl_item_ref in items {
|
|
|
|
self.private_traits.insert(impl_item_ref.id.hir_id);
|
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;
|
|
|
|
}
|
2020-03-15 21:48:09 -05:00
|
|
|
|
|
|
|
hir::ItemKind::TyAlias(..)
|
|
|
|
| hir::ItemKind::Fn(..)
|
|
|
|
| hir::ItemKind::Mod(..)
|
|
|
|
| hir::ItemKind::Enum(..)
|
|
|
|
| hir::ItemKind::Struct(..)
|
|
|
|
| hir::ItemKind::Union(..)
|
|
|
|
| hir::ItemKind::Const(..)
|
|
|
|
| hir::ItemKind::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
|
|
|
|
2020-03-15 21:48:09 -05:00
|
|
|
let def_id = cx.tcx.hir().local_def_id(it.hir_id);
|
2020-04-09 09:43:00 +01:00
|
|
|
let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id());
|
2020-03-15 21:48:09 -05:00
|
|
|
|
2020-03-21 21:32:35 -05:00
|
|
|
self.check_missing_docs_attrs(cx, Some(it.hir_id), &it.attrs, it.span, article, desc);
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
|
2020-06-25 23:41:36 +03: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
|
|
|
|
2020-03-21 19:47:23 -05:00
|
|
|
let def_id = cx.tcx.hir().local_def_id(trait_item.hir_id);
|
2020-04-09 09:43:00 +01:00
|
|
|
let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id());
|
2015-03-29 23:41:54 -04:00
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
self.check_missing_docs_attrs(
|
|
|
|
cx,
|
|
|
|
Some(trait_item.hir_id),
|
|
|
|
&trait_item.attrs,
|
|
|
|
trait_item.span,
|
2020-03-21 21:32:35 -05:00
|
|
|
article,
|
|
|
|
desc,
|
2019-12-22 17:42:04 -05:00
|
|
|
);
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
|
2020-06-25 23:41:36 +03: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
|
|
|
|
2020-03-21 19:47:23 -05:00
|
|
|
let def_id = cx.tcx.hir().local_def_id(impl_item.hir_id);
|
2020-04-09 09:43:00 +01:00
|
|
|
let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id());
|
2019-12-22 17:42:04 -05:00
|
|
|
self.check_missing_docs_attrs(
|
|
|
|
cx,
|
|
|
|
Some(impl_item.hir_id),
|
|
|
|
&impl_item.attrs,
|
|
|
|
impl_item.span,
|
2020-03-21 21:32:35 -05:00
|
|
|
article,
|
|
|
|
desc,
|
2019-12-22 17:42:04 -05:00
|
|
|
);
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
|
2020-06-25 23:41:36 +03:00
|
|
|
fn check_struct_field(&mut self, cx: &LateContext<'_>, sf: &hir::StructField<'_>) {
|
2016-02-27 11:34:29 +03:00
|
|
|
if !sf.is_positional() {
|
2020-03-21 21:32:35 -05:00
|
|
|
self.check_missing_docs_attrs(
|
|
|
|
cx,
|
|
|
|
Some(sf.hir_id),
|
|
|
|
&sf.attrs,
|
|
|
|
sf.span,
|
|
|
|
"a",
|
|
|
|
"struct field",
|
|
|
|
)
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-25 23:41:36 +03:00
|
|
|
fn check_variant(&mut self, cx: &LateContext<'_>, v: &hir::Variant<'_>) {
|
2020-03-21 21:32:35 -05:00
|
|
|
self.check_missing_docs_attrs(cx, Some(v.id), &v.attrs, v.span, "a", "variant");
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
declare_lint! {
|
|
|
|
pub MISSING_COPY_IMPLEMENTATIONS,
|
|
|
|
Allow,
|
|
|
|
"detects potentially-forgotten implementations of `Copy`"
|
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
declare_lint_pass!(MissingCopyImplementations => [MISSING_COPY_IMPLEMENTATIONS]);
|
2015-02-25 22:44:44 +11:00
|
|
|
|
2020-06-25 23:41:36 +03:00
|
|
|
impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations {
|
|
|
|
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
|
|
|
}
|
2019-09-26 17:51:36 +01:00
|
|
|
let (def, ty) = match item.kind {
|
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-06-27 11:28:14 +02:00
|
|
|
let def = cx.tcx.adt_def(cx.tcx.hir().local_def_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-06-27 11:28:14 +02:00
|
|
|
let def = cx.tcx.adt_def(cx.tcx.hir().local_def_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-06-27 11:28:14 +02:00
|
|
|
let def = cx.tcx.adt_def(cx.tcx.hir().local_def_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();
|
2020-06-21 11:20:48 +02:00
|
|
|
if ty.is_copy_modulo_regions(cx.tcx.at(item.span), param_env) {
|
2015-02-28 13:31:14 +01:00
|
|
|
return;
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
2020-01-05 23:43:45 +01:00
|
|
|
if can_type_implement_copy(cx.tcx, param_env, ty).is_ok() {
|
2020-02-03 19:57:45 +10:00
|
|
|
cx.struct_span_lint(MISSING_COPY_IMPLEMENTATIONS, item.span, |lint| {
|
|
|
|
lint.build(
|
|
|
|
"type could implement `Copy`; consider adding `impl \
|
|
|
|
Copy`",
|
|
|
|
)
|
|
|
|
.emit()
|
|
|
|
})
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
declare_lint! {
|
|
|
|
MISSING_DEBUG_IMPLEMENTATIONS,
|
|
|
|
Allow,
|
2020-02-04 17:08:50 -05:00
|
|
|
"detects missing implementations of Debug"
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
|
2019-04-24 13:47:26 +01:00
|
|
|
#[derive(Default)]
|
2015-02-25 22:44:44 +11:00
|
|
|
pub struct MissingDebugImplementations {
|
2019-02-27 17:35:24 +01:00
|
|
|
impling_types: Option<HirIdSet>,
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
impl_lint_pass!(MissingDebugImplementations => [MISSING_DEBUG_IMPLEMENTATIONS]);
|
|
|
|
|
2020-06-25 23:41:36 +03:00
|
|
|
impl<'tcx> LateLintPass<'tcx> for MissingDebugImplementations {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-09-26 17:51:36 +01:00
|
|
|
match item.kind {
|
2019-12-22 17:42:04 -05:00
|
|
|
hir::ItemKind::Struct(..) | hir::ItemKind::Union(..) | hir::ItemKind::Enum(..) => {}
|
2015-02-25 22:44:44 +11:00
|
|
|
_ => return,
|
|
|
|
}
|
|
|
|
|
2019-05-19 20:16:04 +02:00
|
|
|
let debug = match cx.tcx.get_diagnostic_item(sym::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() {
|
2020-04-23 20:48:40 +01:00
|
|
|
if let Some(def_id) = ty_def.did.as_local() {
|
2020-08-12 12:22:56 +02:00
|
|
|
impls.insert(cx.tcx.hir().local_def_id_to_hir_id(def_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) {
|
2020-02-03 19:57:45 +10:00
|
|
|
cx.struct_span_lint(MISSING_DEBUG_IMPLEMENTATIONS, item.span, |lint| {
|
|
|
|
lint.build(&format!(
|
2020-02-04 17:08:50 -05:00
|
|
|
"type does not implement `{}`; consider adding `#[derive(Debug)]` \
|
|
|
|
or a manual implementation",
|
|
|
|
cx.tcx.def_path_str(debug)
|
2020-02-03 19:57:45 +10:00
|
|
|
))
|
|
|
|
.emit()
|
|
|
|
});
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-02 04:38:46 +02:00
|
|
|
declare_lint! {
|
|
|
|
pub ANONYMOUS_PARAMETERS,
|
2018-08-27 12:14:31 -05:00
|
|
|
Allow,
|
2019-10-08 21:49:21 -04:00
|
|
|
"detects anonymous parameters",
|
|
|
|
@future_incompatible = FutureIncompatibleInfo {
|
|
|
|
reference: "issue #41686 <https://github.com/rust-lang/rust/issues/41686>",
|
|
|
|
edition: Some(Edition::Edition2018),
|
|
|
|
};
|
2017-05-02 04:38:46 +02:00
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
declare_lint_pass!(
|
|
|
|
/// Checks for use of anonymous parameters (RFC 1685).
|
|
|
|
AnonymousParameters => [ANONYMOUS_PARAMETERS]
|
|
|
|
);
|
2017-05-02 04:38:46 +02:00
|
|
|
|
|
|
|
impl EarlyLintPass for AnonymousParameters {
|
2019-12-08 00:08:09 +01:00
|
|
|
fn check_trait_item(&mut self, cx: &EarlyContext<'_>, it: &ast::AssocItem) {
|
2020-03-22 13:36:56 +01:00
|
|
|
if let ast::AssocItemKind::Fn(_, ref sig, _, _) = it.kind {
|
|
|
|
for arg in sig.decl.inputs.iter() {
|
|
|
|
if let ast::PatKind::Ident(_, ident, None) = arg.pat.kind {
|
|
|
|
if ident.name == kw::Invalid {
|
|
|
|
cx.struct_span_lint(ANONYMOUS_PARAMETERS, arg.pat.span, |lint| {
|
|
|
|
let ty_snip = cx.sess.source_map().span_to_snippet(arg.ty.span);
|
|
|
|
|
|
|
|
let (ty_snip, appl) = if let Ok(ref snip) = ty_snip {
|
|
|
|
(snip.as_str(), Applicability::MachineApplicable)
|
|
|
|
} else {
|
|
|
|
("<type>", Applicability::HasPlaceholders)
|
|
|
|
};
|
2018-02-17 17:33:27 -06:00
|
|
|
|
2020-03-22 13:36:56 +01:00
|
|
|
lint.build(
|
|
|
|
"anonymous parameters are deprecated and will be \
|
2020-02-02 09:47:58 +10:00
|
|
|
removed in the next edition.",
|
2020-03-22 13:36:56 +01:00
|
|
|
)
|
|
|
|
.span_suggestion(
|
|
|
|
arg.pat.span,
|
|
|
|
"try naming the parameter or explicitly \
|
2020-01-31 22:24:57 +10:00
|
|
|
ignoring it",
|
2020-03-22 13:36:56 +01:00
|
|
|
format!("_: {}", ty_snip),
|
|
|
|
appl,
|
|
|
|
)
|
|
|
|
.emit();
|
|
|
|
})
|
2017-05-02 04:38:46 +02:00
|
|
|
}
|
|
|
|
}
|
2019-12-22 17:42:04 -05:00
|
|
|
}
|
2017-05-02 04:38:46 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-08 14:53:55 +01:00
|
|
|
/// Check for use of attributes which have been deprecated.
|
2016-10-11 15:51:27 +13:00
|
|
|
#[derive(Clone)]
|
2016-10-18 18:04:28 +13:00
|
|
|
pub struct DeprecatedAttr {
|
|
|
|
// This is not free to compute, so we want to keep it around, rather than
|
|
|
|
// compute it for every attribute.
|
2019-04-10 19:47:55 +02:00
|
|
|
depr_attrs: Vec<&'static (Symbol, AttributeType, AttributeTemplate, AttributeGate)>,
|
2016-10-18 18:04:28 +13:00
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
impl_lint_pass!(DeprecatedAttr => []);
|
|
|
|
|
2016-10-18 18:04:28 +13:00
|
|
|
impl DeprecatedAttr {
|
|
|
|
pub fn new() -> DeprecatedAttr {
|
2019-12-22 17:42:04 -05:00
|
|
|
DeprecatedAttr { depr_attrs: deprecated_attributes() }
|
2016-10-18 18:04:28 +13:00
|
|
|
}
|
|
|
|
}
|
2016-10-11 15:51:27 +13:00
|
|
|
|
2019-09-14 17:53:14 -04:00
|
|
|
fn lint_deprecated_attr(
|
|
|
|
cx: &EarlyContext<'_>,
|
|
|
|
attr: &ast::Attribute,
|
|
|
|
msg: &str,
|
|
|
|
suggestion: Option<&str>,
|
|
|
|
) {
|
2020-01-31 22:24:57 +10:00
|
|
|
cx.struct_span_lint(DEPRECATED, attr.span, |lint| {
|
|
|
|
lint.build(msg)
|
|
|
|
.span_suggestion_short(
|
|
|
|
attr.span,
|
|
|
|
suggestion.unwrap_or("remove this attribute"),
|
|
|
|
String::new(),
|
|
|
|
Applicability::MachineApplicable,
|
|
|
|
)
|
|
|
|
.emit();
|
|
|
|
})
|
2019-09-14 17:53:14 -04:00
|
|
|
}
|
|
|
|
|
2016-10-11 15:51:27 +13:00
|
|
|
impl EarlyLintPass for DeprecatedAttr {
|
2019-02-08 20:35:41 +09:00
|
|
|
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) {
|
2019-01-02 02:21:05 +03:00
|
|
|
for &&(n, _, _, ref g) in &self.depr_attrs {
|
2019-04-10 19:47:55 +02:00
|
|
|
if attr.ident().map(|ident| ident.name) == Some(n) {
|
2019-12-22 17:42:04 -05:00
|
|
|
if let &AttributeGate::Gated(
|
|
|
|
Stability::Deprecated(link, suggestion),
|
|
|
|
ref name,
|
|
|
|
ref reason,
|
|
|
|
_,
|
|
|
|
) = g
|
|
|
|
{
|
|
|
|
let msg =
|
|
|
|
format!("use of deprecated attribute `{}`: {}. See {}", name, reason, link);
|
2019-09-14 17:53:14 -04:00
|
|
|
lint_deprecated_attr(cx, attr, &msg, suggestion);
|
2016-10-11 15:51:27 +13:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2020-07-30 11:27:50 +10:00
|
|
|
if cx.sess().check_name(attr, sym::no_start) || cx.sess().check_name(attr, sym::crate_id) {
|
2019-10-24 06:33:12 +11:00
|
|
|
let path_str = pprust::path_to_string(&attr.get_normal_item().path);
|
2019-10-08 22:17:46 +02:00
|
|
|
let msg = format!("use of deprecated attribute `{}`: no longer used.", path_str);
|
2019-09-14 17:53:14 -04:00
|
|
|
lint_deprecated_attr(cx, attr, &msg, None);
|
|
|
|
}
|
2016-10-11 15:51:27 +13:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-22 08:28:56 -08:00
|
|
|
fn warn_if_doc(cx: &EarlyContext<'_>, node_span: Span, node_kind: &str, attrs: &[ast::Attribute]) {
|
2020-02-29 03:05:14 +01:00
|
|
|
let mut attrs = attrs.iter().peekable();
|
2017-07-16 00:17:35 +02:00
|
|
|
|
2020-02-22 08:28:56 -08:00
|
|
|
// Accumulate a single span for sugared doc comments.
|
|
|
|
let mut sugared_span: Option<Span> = None;
|
2017-07-16 00:17:35 +02:00
|
|
|
|
2020-02-22 08:28:56 -08:00
|
|
|
while let Some(attr) = attrs.next() {
|
|
|
|
if attr.is_doc_comment() {
|
|
|
|
sugared_span =
|
|
|
|
Some(sugared_span.map_or_else(|| attr.span, |span| span.with_hi(attr.span.hi())));
|
|
|
|
}
|
2019-01-23 13:44:43 -05:00
|
|
|
|
2020-02-22 08:28:56 -08:00
|
|
|
if attrs.peek().map(|next_attr| next_attr.is_doc_comment()).unwrap_or_default() {
|
|
|
|
continue;
|
|
|
|
}
|
2019-01-23 13:44:43 -05:00
|
|
|
|
2020-02-22 08:28:56 -08:00
|
|
|
let span = sugared_span.take().unwrap_or_else(|| attr.span);
|
2019-01-23 13:44:43 -05:00
|
|
|
|
2020-07-30 11:27:50 +10:00
|
|
|
if attr.is_doc_comment() || cx.sess().check_name(attr, sym::doc) {
|
2020-02-22 08:28:56 -08:00
|
|
|
cx.struct_span_lint(UNUSED_DOC_COMMENTS, span, |lint| {
|
|
|
|
let mut err = lint.build("unused doc comment");
|
|
|
|
err.span_label(
|
|
|
|
node_span,
|
|
|
|
format!("rustdoc does not generate documentation for {}", node_kind),
|
|
|
|
);
|
|
|
|
err.emit();
|
|
|
|
});
|
2017-07-16 00:17:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl EarlyLintPass for UnusedDocComment {
|
2019-01-24 15:49:03 -05:00
|
|
|
fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &ast::Stmt) {
|
2020-02-21 16:01:48 -08:00
|
|
|
let kind = match stmt.kind {
|
|
|
|
ast::StmtKind::Local(..) => "statements",
|
|
|
|
ast::StmtKind::Item(..) => "inner items",
|
2019-01-24 15:49:03 -05:00
|
|
|
// expressions will be reported by `check_expr`.
|
2020-02-27 04:10:42 +01:00
|
|
|
ast::StmtKind::Empty
|
|
|
|
| ast::StmtKind::Semi(_)
|
|
|
|
| ast::StmtKind::Expr(_)
|
2020-02-29 19:32:20 +03:00
|
|
|
| ast::StmtKind::MacCall(_) => return,
|
2019-01-24 15:49:03 -05:00
|
|
|
};
|
|
|
|
|
2020-02-22 08:28:56 -08:00
|
|
|
warn_if_doc(cx, stmt.span, kind, stmt.kind.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-08-28 01:57:58 +02:00
|
|
|
let arm_span = arm.pat.span.with_hi(arm.body.span.hi());
|
2020-02-22 08:28:56 -08:00
|
|
|
warn_if_doc(cx, arm_span, "match arms", &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) {
|
2020-02-22 08:28:56 -08:00
|
|
|
warn_if_doc(cx, expr.span, "expressions", &expr.attrs);
|
2017-07-16 00:17:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-25 22:44:44 +11:00
|
|
|
declare_lint! {
|
|
|
|
NO_MANGLE_CONST_ITEMS,
|
|
|
|
Deny,
|
|
|
|
"const items will not have their symbols exported"
|
|
|
|
}
|
|
|
|
|
2015-12-09 01:48:40 +09:00
|
|
|
declare_lint! {
|
|
|
|
NO_MANGLE_GENERIC_ITEMS,
|
|
|
|
Warn,
|
|
|
|
"generic items must be mangled"
|
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
declare_lint_pass!(InvalidNoMangleItems => [NO_MANGLE_CONST_ITEMS, NO_MANGLE_GENERIC_ITEMS]);
|
2015-02-25 22:44:44 +11:00
|
|
|
|
2020-06-25 23:41:36 +03:00
|
|
|
impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
|
|
|
|
fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
|
2019-09-26 17:51:36 +01:00
|
|
|
match it.kind {
|
2018-07-11 23:36:06 +08:00
|
|
|
hir::ItemKind::Fn(.., ref generics, _) => {
|
2020-07-30 11:27:50 +10:00
|
|
|
if let Some(no_mangle_attr) = cx.sess().find_by_name(&it.attrs, sym::no_mangle) {
|
2019-12-01 17:10:12 +01:00
|
|
|
for param in generics.params {
|
2018-05-26 13:11:39 +01:00
|
|
|
match param.kind {
|
|
|
|
GenericParamKind::Lifetime { .. } => {}
|
2019-12-22 17:42:04 -05:00
|
|
|
GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {
|
2020-02-02 09:47:58 +10:00
|
|
|
cx.struct_span_lint(NO_MANGLE_GENERIC_ITEMS, it.span, |lint| {
|
|
|
|
lint.build(
|
|
|
|
"functions generic over types or consts must be mangled",
|
|
|
|
)
|
|
|
|
.span_suggestion_short(
|
|
|
|
no_mangle_attr.span,
|
|
|
|
"remove this attribute",
|
|
|
|
String::new(),
|
|
|
|
// Use of `#[no_mangle]` suggests FFI intent; correct
|
|
|
|
// fix may be to monomorphize source by hand
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
)
|
|
|
|
.emit();
|
|
|
|
});
|
2018-05-26 13:11:39 +01:00
|
|
|
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(..) => {
|
2020-07-30 11:27:50 +10:00
|
|
|
if cx.sess().contains_name(&it.attrs, sym::no_mangle) {
|
2015-02-25 22:44:44 +11:00
|
|
|
// Const items do not refer to a particular location in memory, and therefore
|
|
|
|
// don't have anything to attach a symbol to
|
2020-01-31 22:24:57 +10:00
|
|
|
cx.struct_span_lint(NO_MANGLE_CONST_ITEMS, it.span, |lint| {
|
2020-02-06 01:27:46 +10:00
|
|
|
let msg = "const items should never be `#[no_mangle]`";
|
2020-01-31 22:24:57 +10:00
|
|
|
let mut err = lint.build(msg);
|
|
|
|
|
|
|
|
// account for "pub const" (#45562)
|
|
|
|
let start = cx
|
|
|
|
.tcx
|
|
|
|
.sess
|
|
|
|
.source_map()
|
|
|
|
.span_to_snippet(it.span)
|
|
|
|
.map(|snippet| snippet.find("const").unwrap_or(0))
|
|
|
|
.unwrap_or(0) as u32;
|
|
|
|
// `const` is 5 chars
|
|
|
|
let const_span = it.span.with_hi(BytePos(it.span.lo().0 + start + 5));
|
|
|
|
err.span_suggestion(
|
|
|
|
const_span,
|
|
|
|
"try a static value",
|
|
|
|
"pub static".to_owned(),
|
|
|
|
Applicability::MachineApplicable,
|
|
|
|
);
|
|
|
|
err.emit();
|
|
|
|
});
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
}
|
2016-10-09 09:38:07 +05:30
|
|
|
_ => {}
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-13 14:49:10 -07:00
|
|
|
declare_lint! {
|
|
|
|
MUTABLE_TRANSMUTES,
|
|
|
|
Deny,
|
|
|
|
"mutating transmuted &mut T from &T may cause undefined behavior"
|
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
declare_lint_pass!(MutableTransmutes => [MUTABLE_TRANSMUTES]);
|
2015-04-13 14:49:10 -07:00
|
|
|
|
2020-06-25 23:41:36 +03:00
|
|
|
impl<'tcx> LateLintPass<'tcx> for MutableTransmutes {
|
|
|
|
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;
|
2020-03-22 13:36:56 +01:00
|
|
|
if let Some((&ty::Ref(_, _, from_mt), &ty::Ref(_, _, to_mt))) =
|
|
|
|
get_transmute_from_to(cx, expr).map(|(ty1, ty2)| (&ty1.kind, &ty2.kind))
|
|
|
|
{
|
|
|
|
if to_mt == hir::Mutability::Mut && from_mt == hir::Mutability::Not {
|
|
|
|
let msg = "mutating transmuted &mut T from &T may cause undefined behavior, \
|
2020-02-06 01:27:46 +10:00
|
|
|
consider instead using an UnsafeCell";
|
2020-03-22 13:36:56 +01:00
|
|
|
cx.struct_span_lint(MUTABLE_TRANSMUTES, expr.span, |lint| lint.build(msg).emit());
|
2015-04-13 14:49:10 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-25 23:41:36 +03:00
|
|
|
fn get_transmute_from_to<'tcx>(
|
|
|
|
cx: &LateContext<'tcx>,
|
2019-11-30 15:08:22 +01:00
|
|
|
expr: &hir::Expr<'_>,
|
2019-12-22 17:42:04 -05:00
|
|
|
) -> Option<(Ty<'tcx>, Ty<'tcx>)> {
|
2019-09-26 14:39:48 +01:00
|
|
|
let def = if let hir::ExprKind::Path(ref qpath) = expr.kind {
|
2020-06-26 05:55:23 +03:00
|
|
|
cx.qpath_res(qpath, expr.hir_id)
|
2016-11-25 13:21:19 +02:00
|
|
|
} else {
|
|
|
|
return None;
|
|
|
|
};
|
2019-04-20 19:36:05 +03:00
|
|
|
if let Res::Def(DefKind::Fn, did) = def {
|
2015-04-13 14:49:10 -07:00
|
|
|
if !def_id_is_transmute(cx, did) {
|
|
|
|
return None;
|
|
|
|
}
|
2020-07-17 08:47:04 +00:00
|
|
|
let sig = cx.typeck_results().node_type(expr.hir_id).fn_sig(cx.tcx);
|
2017-05-13 17:11:52 +03:00
|
|
|
let from = sig.inputs().skip_binder()[0];
|
2020-06-24 23:40:33 +02:00
|
|
|
let to = sig.output().skip_binder();
|
2019-03-31 23:48:48 +02:00
|
|
|
return Some((from, to));
|
2015-04-13 14:49:10 -07:00
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
2020-06-25 23:41:36 +03:00
|
|
|
fn def_id_is_transmute(cx: &LateContext<'_>, def_id: DefId) -> bool {
|
2019-12-22 17:42:04 -05:00
|
|
|
cx.tcx.fn_sig(def_id).abi() == RustIntrinsic
|
|
|
|
&& cx.tcx.item_name(def_id) == sym::transmute
|
2015-04-13 14:49:10 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-28 13:31:14 +01:00
|
|
|
declare_lint! {
|
|
|
|
UNSTABLE_FEATURES,
|
|
|
|
Allow,
|
2015-06-17 17:48:16 -07:00
|
|
|
"enabling unstable features (deprecated. do not use)"
|
2015-02-28 13:31:14 +01:00
|
|
|
}
|
2015-02-25 22:44:44 +11:00
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
declare_lint_pass!(
|
|
|
|
/// Forbids using the `#[feature(...)]` attribute
|
|
|
|
UnstableFeatures => [UNSTABLE_FEATURES]
|
|
|
|
);
|
2015-09-10 16:40:59 +12:00
|
|
|
|
2020-06-25 23:41:36 +03:00
|
|
|
impl<'tcx> LateLintPass<'tcx> for UnstableFeatures {
|
2020-07-30 11:27:50 +10:00
|
|
|
fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &ast::Attribute) {
|
|
|
|
if cx.sess().check_name(attr, sym::feature) {
|
2017-03-03 09:23:59 +00:00
|
|
|
if let Some(items) = attr.meta_item_list() {
|
2015-05-18 16:37:05 +02:00
|
|
|
for item in items {
|
2020-07-30 11:27:50 +10:00
|
|
|
cx.struct_span_lint(UNSTABLE_FEATURES, item.span(), |lint| {
|
2020-02-03 19:57:45 +10:00
|
|
|
lint.build("unstable feature").emit()
|
|
|
|
});
|
2015-05-18 16:37:05 +02:00
|
|
|
}
|
|
|
|
}
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-08-19 19:20:30 +03:00
|
|
|
|
2017-11-02 20:50:17 -07:00
|
|
|
declare_lint! {
|
2018-03-08 13:23:52 -08:00
|
|
|
pub UNREACHABLE_PUB,
|
2017-11-02 20:50:17 -07:00
|
|
|
Allow,
|
|
|
|
"`pub` items not reachable from crate root"
|
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
declare_lint_pass!(
|
|
|
|
/// Lint for items marked `pub` that aren't reachable from other crates.
|
|
|
|
UnreachablePub => [UNREACHABLE_PUB]
|
|
|
|
);
|
2017-11-02 20:50:17 -07:00
|
|
|
|
|
|
|
impl UnreachablePub {
|
2019-12-22 17:42:04 -05:00
|
|
|
fn perform_lint(
|
|
|
|
&self,
|
2020-06-25 23:41:36 +03:00
|
|
|
cx: &LateContext<'_>,
|
2019-12-22 17:42:04 -05:00
|
|
|
what: &str,
|
|
|
|
id: hir::HirId,
|
2019-12-01 16:08:58 +01:00
|
|
|
vis: &hir::Visibility<'_>,
|
2019-12-22 17:42:04 -05:00
|
|
|
span: Span,
|
|
|
|
exportable: bool,
|
|
|
|
) {
|
2018-06-27 21:23:18 -07:00
|
|
|
let mut applicability = Applicability::MachineApplicable;
|
|
|
|
match vis.node {
|
2019-03-11 11:03:19 +01:00
|
|
|
hir::VisibilityKind::Public if !cx.access_levels.is_reachable(id) => {
|
2019-08-11 01:08:30 +03:00
|
|
|
if span.from_expansion() {
|
2018-06-27 21:23:18 -07:00
|
|
|
applicability = Applicability::MaybeIncorrect;
|
|
|
|
}
|
2020-03-09 11:42:37 -07:00
|
|
|
let def_span = cx.tcx.sess.source_map().guess_head_span(span);
|
2020-02-02 09:47:58 +10:00
|
|
|
cx.struct_span_lint(UNREACHABLE_PUB, def_span, |lint| {
|
|
|
|
let mut err = lint.build(&format!("unreachable `pub` {}", what));
|
|
|
|
let replacement = if cx.tcx.features().crate_visibility_modifier {
|
|
|
|
"crate"
|
|
|
|
} else {
|
|
|
|
"pub(crate)"
|
|
|
|
}
|
|
|
|
.to_owned();
|
2018-06-27 21:23:18 -07:00
|
|
|
|
2020-02-02 09:47:58 +10:00
|
|
|
err.span_suggestion(
|
|
|
|
vis.span,
|
|
|
|
"consider restricting its visibility",
|
|
|
|
replacement,
|
|
|
|
applicability,
|
|
|
|
);
|
|
|
|
if exportable {
|
|
|
|
err.help("or consider exporting it for use by other crates");
|
2020-01-31 22:24:57 +10:00
|
|
|
}
|
2020-02-02 09:47:58 +10:00
|
|
|
err.emit();
|
|
|
|
});
|
2019-12-22 17:42:04 -05:00
|
|
|
}
|
2018-06-27 21:23:18 -07:00
|
|
|
_ => {}
|
2017-11-02 20:50:17 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-25 23:41:36 +03:00
|
|
|
impl<'tcx> LateLintPass<'tcx> for UnreachablePub {
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2020-06-25 23:41:36 +03:00
|
|
|
fn check_foreign_item(&mut self, cx: &LateContext<'_>, foreign_item: &hir::ForeignItem<'tcx>) {
|
2019-12-22 17:42:04 -05:00
|
|
|
self.perform_lint(
|
|
|
|
cx,
|
|
|
|
"item",
|
|
|
|
foreign_item.hir_id,
|
|
|
|
&foreign_item.vis,
|
|
|
|
foreign_item.span,
|
|
|
|
true,
|
|
|
|
);
|
2017-11-02 20:50:17 -07:00
|
|
|
}
|
|
|
|
|
2020-06-25 23:41:36 +03: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
|
|
|
}
|
|
|
|
|
2020-06-25 23:41:36 +03:00
|
|
|
fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) {
|
2019-02-26 15:11:59 +01:00
|
|
|
self.perform_lint(cx, "item", impl_item.hir_id, &impl_item.vis, impl_item.span, false);
|
2017-11-02 20:50:17 -07:00
|
|
|
}
|
|
|
|
}
|
2018-02-18 19:40:35 +01:00
|
|
|
|
|
|
|
declare_lint! {
|
2018-03-10 11:43:51 +01:00
|
|
|
TYPE_ALIAS_BOUNDS,
|
2018-02-18 19:40:35 +01:00
|
|
|
Warn,
|
2018-03-10 11:43:51 +01:00
|
|
|
"bounds in type aliases are not enforced"
|
2018-02-18 19:40:35 +01:00
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
declare_lint_pass!(
|
|
|
|
/// Lint for trait and lifetime bounds in type aliases being mostly ignored.
|
|
|
|
/// They are relevant when using associated types, but otherwise neither checked
|
|
|
|
/// at definition site nor enforced at use site.
|
|
|
|
TypeAliasBounds => [TYPE_ALIAS_BOUNDS]
|
|
|
|
);
|
2018-02-18 19:40:35 +01:00
|
|
|
|
2018-03-10 13:32:11 +01:00
|
|
|
impl TypeAliasBounds {
|
2019-12-01 16:08:58 +01:00
|
|
|
fn is_type_variable_assoc(qpath: &hir::QPath<'_>) -> bool {
|
2018-03-10 13:32:11 +01:00
|
|
|
match *qpath {
|
|
|
|
hir::QPath::TypeRelative(ref ty, _) => {
|
|
|
|
// If this is a type variable, we found a `T::Assoc`.
|
2019-09-26 17:25:31 +01:00
|
|
|
match ty.kind {
|
2019-12-22 17:42:04 -05:00
|
|
|
hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) => match path.res {
|
|
|
|
Res::Def(DefKind::TyParam, _) => true,
|
|
|
|
_ => false,
|
|
|
|
},
|
|
|
|
_ => false,
|
2018-03-10 13:32:11 +01:00
|
|
|
}
|
|
|
|
}
|
2020-08-04 14:34:24 +01:00
|
|
|
hir::QPath::Resolved(..) | hir::QPath::LangItem(..) => false,
|
2018-03-10 13:32:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-01 16:08:58 +01: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.
|
2020-01-07 18:12:06 +01:00
|
|
|
use rustc_hir::intravisit::{self, Visitor};
|
2019-06-14 19:43:53 +03:00
|
|
|
struct WalkAssocTypes<'a, 'db> {
|
2019-12-22 17:42:04 -05:00
|
|
|
err: &'a mut DiagnosticBuilder<'db>,
|
2018-03-10 13:32:11 +01:00
|
|
|
}
|
|
|
|
impl<'a, 'db, 'v> Visitor<'v> for WalkAssocTypes<'a, 'db> {
|
2020-03-11 12:05:32 +01:00
|
|
|
type Map = intravisit::ErasedMap<'v>;
|
2020-01-07 17:25:33 +01:00
|
|
|
|
2020-02-09 15:32:00 +01:00
|
|
|
fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
|
2018-03-10 13:32:11 +01:00
|
|
|
intravisit::NestedVisitorMap::None
|
|
|
|
}
|
|
|
|
|
2019-12-01 16:08:58 +01:00
|
|
|
fn visit_qpath(&mut self, qpath: &'v hir::QPath<'v>, id: hir::HirId, span: Span) {
|
2018-03-10 13:32:11 +01:00
|
|
|
if TypeAliasBounds::is_type_variable_assoc(qpath) {
|
2019-12-22 17:42:04 -05:00
|
|
|
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 \
|
2019-12-22 17:42:04 -05:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-25 23:41:36 +03:00
|
|
|
impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
|
|
|
|
fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
|
2019-09-26 17:51:36 +01:00
|
|
|
let (ty, type_alias_generics) = match item.kind {
|
2019-08-02 11:02:08 +01:00
|
|
|
hir::ItemKind::TyAlias(ref ty, ref generics) => (&*ty, generics),
|
2018-03-06 11:22:24 +01:00
|
|
|
_ => return,
|
|
|
|
};
|
2020-06-07 18:56:17 +01:00
|
|
|
if let hir::TyKind::OpaqueDef(..) = ty.kind {
|
2020-05-10 11:57:58 +01:00
|
|
|
// Bounds are respected for `type X = impl Trait`
|
|
|
|
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() {
|
2020-02-06 03:18:40 +10:00
|
|
|
cx.lint(
|
2019-12-22 17:42:04 -05:00
|
|
|
TYPE_ALIAS_BOUNDS,
|
2020-01-31 22:24:57 +10:00
|
|
|
|lint| {
|
|
|
|
let mut err = lint.build("where clauses are not enforced in type aliases");
|
2020-02-06 03:18:40 +10:00
|
|
|
let spans: Vec<_> = type_alias_generics
|
|
|
|
.where_clause
|
|
|
|
.predicates
|
|
|
|
.iter()
|
|
|
|
.map(|pred| pred.span())
|
|
|
|
.collect();
|
|
|
|
err.set_span(spans);
|
2020-01-31 22:24:57 +10:00
|
|
|
err.span_suggestion(
|
|
|
|
type_alias_generics.where_clause.span_for_predicates_or_empty_place(),
|
|
|
|
"the clause will not be checked when the type alias is used, and should be removed",
|
|
|
|
String::new(),
|
|
|
|
Applicability::MachineApplicable,
|
|
|
|
);
|
|
|
|
if !suggested_changing_assoc_types {
|
|
|
|
TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err);
|
|
|
|
suggested_changing_assoc_types = true;
|
|
|
|
}
|
|
|
|
err.emit();
|
|
|
|
},
|
2019-10-28 16:58:27 -07:00
|
|
|
);
|
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();
|
2019-12-22 17:42:04 -05:00
|
|
|
let suggestion = spans
|
|
|
|
.iter()
|
|
|
|
.map(|sp| {
|
|
|
|
let start = param.span.between(*sp); // Include the `:` in `T: Bound`.
|
|
|
|
(start.to(*sp), String::new())
|
|
|
|
})
|
|
|
|
.collect();
|
2018-03-06 11:22:24 +01:00
|
|
|
if !spans.is_empty() {
|
2020-02-02 09:47:58 +10:00
|
|
|
cx.struct_span_lint(TYPE_ALIAS_BOUNDS, spans, |lint| {
|
|
|
|
let mut err =
|
|
|
|
lint.build("bounds on generic parameters are not enforced in type aliases");
|
|
|
|
let msg = "the bound will not be checked when the type alias is used, \
|
2020-01-31 22:24:57 +10:00
|
|
|
and should be removed";
|
2020-02-02 09:47:58 +10:00
|
|
|
err.multipart_suggestion(&msg, suggestion, Applicability::MachineApplicable);
|
|
|
|
if !suggested_changing_assoc_types {
|
|
|
|
TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err);
|
|
|
|
suggested_changing_assoc_types = true;
|
|
|
|
}
|
|
|
|
err.emit();
|
|
|
|
});
|
2018-02-18 19:40:35 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-04-20 14:18:29 +02:00
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
declare_lint_pass!(
|
|
|
|
/// Lint constants that are erroneous.
|
|
|
|
/// Without this lint, we might not get any diagnostic if the constant is
|
|
|
|
/// unused within this crate, even though downstream crates can't use it
|
|
|
|
/// without producing an error.
|
|
|
|
UnusedBrokenConst => []
|
|
|
|
);
|
2019-01-18 07:40:55 +01:00
|
|
|
|
2020-06-25 23:41:36 +03:00
|
|
|
fn check_const(cx: &LateContext<'_>, body_id: hir::BodyId) {
|
2020-04-08 14:53:06 +01:00
|
|
|
let def_id = cx.tcx.hir().body_owner_def_id(body_id).to_def_id();
|
2018-08-26 15:19:34 +02:00
|
|
|
// trigger the query once for all constants since that will already report the errors
|
2019-03-26 17:04:00 +01:00
|
|
|
// FIXME: Use ensure here
|
2019-11-30 08:42:56 +13:00
|
|
|
let _ = cx.tcx.const_eval_poly(def_id);
|
2018-04-20 14:18:29 +02:00
|
|
|
}
|
|
|
|
|
2020-06-25 23:41:36 +03:00
|
|
|
impl<'tcx> LateLintPass<'tcx> for UnusedBrokenConst {
|
|
|
|
fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
|
2019-09-26 17:51:36 +01:00
|
|
|
match it.kind {
|
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);
|
2019-12-22 17:42:04 -05: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);
|
2019-12-22 17:42:04 -05:00
|
|
|
}
|
|
|
|
_ => {}
|
2018-04-20 14:18:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-04-26 12:03:08 -07:00
|
|
|
|
2018-05-06 22:52:58 +01:00
|
|
|
declare_lint! {
|
|
|
|
TRIVIAL_BOUNDS,
|
|
|
|
Warn,
|
|
|
|
"these bounds don't depend on an type parameters"
|
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
declare_lint_pass!(
|
|
|
|
/// Lint for trait and lifetime bounds that don't depend on type parameters
|
|
|
|
/// which either do nothing, or stop the item from being used.
|
|
|
|
TrivialConstraints => [TRIVIAL_BOUNDS]
|
|
|
|
);
|
2018-05-06 22:52:58 +01:00
|
|
|
|
2020-06-25 23:41:36 +03:00
|
|
|
impl<'tcx> LateLintPass<'tcx> for TrivialConstraints {
|
|
|
|
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
|
2020-03-29 16:41:09 +02:00
|
|
|
use rustc_middle::ty::fold::TypeFoldable;
|
2020-07-09 00:35:55 +02:00
|
|
|
use rustc_middle::ty::PredicateAtom::*;
|
2018-05-06 22:52:58 +01:00
|
|
|
|
|
|
|
if cx.tcx.features().trivial_bounds {
|
2019-06-27 11:28:14 +02:00
|
|
|
let def_id = cx.tcx.hir().local_def_id(item.hir_id);
|
2018-05-06 22:52:58 +01:00
|
|
|
let predicates = cx.tcx.predicates_of(def_id);
|
2019-10-18 03:14:57 +03:00
|
|
|
for &(predicate, span) in predicates.predicates {
|
2020-07-09 00:35:55 +02:00
|
|
|
let predicate_kind_name = match predicate.skip_binders() {
|
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(..) |
|
2020-02-29 10:03:04 +13:00
|
|
|
ConstEvaluatable(..) |
|
|
|
|
ConstEquate(..) => continue,
|
2018-05-06 22:52:58 +01:00
|
|
|
};
|
2018-05-15 21:48:35 +01:00
|
|
|
if predicate.is_global() {
|
2020-02-03 19:57:45 +10:00
|
|
|
cx.struct_span_lint(TRIVIAL_BOUNDS, span, |lint| {
|
|
|
|
lint.build(&format!(
|
2019-12-22 17:42:04 -05:00
|
|
|
"{} bound {} does not depend on any type \
|
|
|
|
or lifetime parameters",
|
|
|
|
predicate_kind_name, predicate
|
2020-02-03 19:57:45 +10:00
|
|
|
))
|
|
|
|
.emit()
|
|
|
|
});
|
2018-05-06 22:52:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-06-09 17:20:58 +02:00
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
declare_lint_pass!(
|
|
|
|
/// Does nothing as a lint pass, but registers some `Lint`s
|
|
|
|
/// which are used by other parts of the compiler.
|
|
|
|
SoftLints => [
|
|
|
|
WHILE_TRUE,
|
|
|
|
BOX_POINTERS,
|
|
|
|
NON_SHORTHAND_FIELD_PATTERNS,
|
|
|
|
UNSAFE_CODE,
|
|
|
|
MISSING_DOCS,
|
|
|
|
MISSING_COPY_IMPLEMENTATIONS,
|
|
|
|
MISSING_DEBUG_IMPLEMENTATIONS,
|
|
|
|
ANONYMOUS_PARAMETERS,
|
|
|
|
UNUSED_DOC_COMMENTS,
|
|
|
|
NO_MANGLE_CONST_ITEMS,
|
|
|
|
NO_MANGLE_GENERIC_ITEMS,
|
|
|
|
MUTABLE_TRANSMUTES,
|
|
|
|
UNSTABLE_FEATURES,
|
|
|
|
UNREACHABLE_PUB,
|
|
|
|
TYPE_ALIAS_BOUNDS,
|
|
|
|
TRIVIAL_BOUNDS
|
|
|
|
]
|
|
|
|
);
|
2018-05-28 19:32:03 -07:00
|
|
|
|
|
|
|
declare_lint! {
|
|
|
|
pub ELLIPSIS_INCLUSIVE_RANGE_PATTERNS,
|
2019-05-30 04:13:55 -04:00
|
|
|
Warn,
|
2018-05-28 19:32:03 -07:00
|
|
|
"`...` range patterns are deprecated"
|
|
|
|
}
|
|
|
|
|
2019-04-24 19:38:10 +01:00
|
|
|
#[derive(Default)]
|
2019-04-21 17:09:30 +03:00
|
|
|
pub struct EllipsisInclusiveRangePatterns {
|
|
|
|
/// If `Some(_)`, suppress all subsequent pattern
|
|
|
|
/// warnings for better diagnostics.
|
|
|
|
node_id: Option<ast::NodeId>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl_lint_pass!(EllipsisInclusiveRangePatterns => [ELLIPSIS_INCLUSIVE_RANGE_PATTERNS]);
|
|
|
|
|
2018-05-28 19:32:03 -07:00
|
|
|
impl EarlyLintPass for EllipsisInclusiveRangePatterns {
|
2019-04-21 17:09:30 +03:00
|
|
|
fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &ast::Pat) {
|
|
|
|
if self.node_id.is_some() {
|
|
|
|
// Don't recursively warn about patterns inside range endpoints.
|
2019-12-22 17:42:04 -05:00
|
|
|
return;
|
2019-04-21 17:09:30 +03:00
|
|
|
}
|
|
|
|
|
2020-04-27 23:26:11 +05:30
|
|
|
use self::ast::{PatKind, RangeSyntax::DotDotDot};
|
2018-11-10 18:08:50 +00:00
|
|
|
|
|
|
|
/// If `pat` is a `...` pattern, return the start and end of the range, as well as the span
|
|
|
|
/// corresponding to the ellipsis.
|
2019-12-11 10:04:34 +01:00
|
|
|
fn matches_ellipsis_pat(pat: &ast::Pat) -> Option<(Option<&Expr>, &Expr, Span)> {
|
2019-09-26 16:18:31 +01:00
|
|
|
match &pat.kind {
|
2019-12-11 10:04:34 +01:00
|
|
|
PatKind::Range(
|
|
|
|
a,
|
|
|
|
Some(b),
|
|
|
|
Spanned { span, node: RangeEnd::Included(DotDotDot) },
|
|
|
|
) => Some((a.as_deref(), b, *span)),
|
2018-11-10 18:08:50 +00:00
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-26 16:18:31 +01:00
|
|
|
let (parenthesise, endpoints) = match &pat.kind {
|
2018-11-10 18:08:50 +00:00
|
|
|
PatKind::Ref(subpat, _) => (true, matches_ellipsis_pat(&subpat)),
|
|
|
|
_ => (false, matches_ellipsis_pat(pat)),
|
|
|
|
};
|
2018-05-28 19:32:03 -07:00
|
|
|
|
2018-11-10 18:08:50 +00:00
|
|
|
if let Some((start, end, join)) = endpoints {
|
2018-05-28 19:32:03 -07:00
|
|
|
let msg = "`...` range patterns are deprecated";
|
2018-11-10 18:08:50 +00:00
|
|
|
let suggestion = "use `..=` for an inclusive range";
|
2018-11-10 21:27:40 +00:00
|
|
|
if parenthesise {
|
2019-04-21 17:09:30 +03:00
|
|
|
self.node_id = Some(pat.id);
|
2020-01-31 22:24:57 +10:00
|
|
|
cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, pat.span, |lint| {
|
2020-02-06 01:27:46 +10:00
|
|
|
let end = expr_to_string(&end);
|
|
|
|
let replace = match start {
|
|
|
|
Some(start) => format!("&({}..={})", expr_to_string(&start), end),
|
|
|
|
None => format!("&(..={})", end),
|
|
|
|
};
|
2020-01-31 22:24:57 +10:00
|
|
|
lint.build(msg)
|
|
|
|
.span_suggestion(
|
|
|
|
pat.span,
|
|
|
|
suggestion,
|
|
|
|
replace,
|
|
|
|
Applicability::MachineApplicable,
|
|
|
|
)
|
|
|
|
.emit();
|
|
|
|
});
|
2018-11-10 18:08:50 +00:00
|
|
|
} else {
|
2020-01-31 22:24:57 +10:00
|
|
|
cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, join, |lint| {
|
|
|
|
lint.build(msg)
|
|
|
|
.span_suggestion_short(
|
|
|
|
join,
|
|
|
|
suggestion,
|
|
|
|
"..=".to_owned(),
|
2020-02-02 09:47:58 +10:00
|
|
|
Applicability::MachineApplicable,
|
2020-01-31 22:24:57 +10:00
|
|
|
)
|
|
|
|
.emit();
|
|
|
|
});
|
2018-11-10 18:08:50 +00:00
|
|
|
};
|
2018-05-28 19:32:03 -07:00
|
|
|
}
|
|
|
|
}
|
2019-04-21 17:09:30 +03:00
|
|
|
|
|
|
|
fn check_pat_post(&mut self, _cx: &EarlyContext<'_>, pat: &ast::Pat) {
|
|
|
|
if let Some(node_id) = self.node_id {
|
|
|
|
if pat.id == node_id {
|
|
|
|
self.node_id = None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-05-28 19:32:03 -07:00
|
|
|
}
|
2018-06-08 18:24:57 -07:00
|
|
|
|
|
|
|
declare_lint! {
|
2018-07-20 18:04:02 -07:00
|
|
|
UNNAMEABLE_TEST_ITEMS,
|
2018-06-08 18:24:57 -07:00
|
|
|
Warn,
|
2019-07-19 00:05:23 +02:00
|
|
|
"detects an item that cannot be named being marked as `#[test_case]`",
|
2019-10-08 18:47:08 -04:00
|
|
|
report_in_external_macro
|
2018-07-20 18:04:02 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct UnnameableTestItems {
|
2020-04-13 11:12:57 +01:00
|
|
|
boundary: Option<hir::HirId>, // HirId of the item under which things are not nameable
|
2018-07-20 18:04:02 -07:00
|
|
|
items_nameable: bool,
|
2018-06-08 18:24:57 -07:00
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
impl_lint_pass!(UnnameableTestItems => [UNNAMEABLE_TEST_ITEMS]);
|
|
|
|
|
2018-07-20 18:04:02 -07:00
|
|
|
impl UnnameableTestItems {
|
|
|
|
pub fn new() -> Self {
|
2020-04-13 11:12:57 +01:00
|
|
|
Self { boundary: None, items_nameable: true }
|
2018-07-20 18:04:02 -07:00
|
|
|
}
|
|
|
|
}
|
2018-06-08 18:24:57 -07:00
|
|
|
|
2020-06-25 23:41:36 +03:00
|
|
|
impl<'tcx> LateLintPass<'tcx> for UnnameableTestItems {
|
|
|
|
fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
|
2018-07-20 18:04:02 -07:00
|
|
|
if self.items_nameable {
|
2019-12-22 17:42:04 -05:00
|
|
|
if let hir::ItemKind::Mod(..) = it.kind {
|
|
|
|
} else {
|
2018-07-20 18:04:02 -07:00
|
|
|
self.items_nameable = false;
|
2020-04-13 11:12:57 +01:00
|
|
|
self.boundary = Some(it.hir_id);
|
2018-06-08 18:24:57 -07:00
|
|
|
}
|
2018-07-20 18:04:02 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-07-30 11:27:50 +10:00
|
|
|
if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::rustc_test_marker) {
|
2020-02-02 09:47:58 +10:00
|
|
|
cx.struct_span_lint(UNNAMEABLE_TEST_ITEMS, attr.span, |lint| {
|
|
|
|
lint.build("cannot test inner items").emit()
|
|
|
|
});
|
2018-07-20 18:04:02 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-25 23:41:36 +03:00
|
|
|
fn check_item_post(&mut self, _cx: &LateContext<'_>, it: &hir::Item<'_>) {
|
2020-04-13 11:12:57 +01:00
|
|
|
if !self.items_nameable && self.boundary == Some(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,
|
2019-10-08 21:49:21 -04:00
|
|
|
"detects edition keywords being used as an identifier",
|
|
|
|
@future_incompatible = FutureIncompatibleInfo {
|
|
|
|
reference: "issue #49716 <https://github.com/rust-lang/rust/issues/49716>",
|
|
|
|
edition: Some(Edition::Edition2018),
|
|
|
|
};
|
2018-07-14 16:40:17 +02:00
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
declare_lint_pass!(
|
|
|
|
/// Check for uses of edition keywords used as an identifier.
|
|
|
|
KeywordIdents => [KEYWORD_IDENTS]
|
|
|
|
);
|
2018-07-14 16:40:17 +02:00
|
|
|
|
2019-03-27 14:43:29 +01:00
|
|
|
struct UnderMacro(bool);
|
|
|
|
|
2018-08-24 13:48:20 -07:00
|
|
|
impl KeywordIdents {
|
2019-02-08 20:35:41 +09:00
|
|
|
fn check_tokens(&mut self, cx: &EarlyContext<'_>, tokens: TokenStream) {
|
2018-07-14 16:40:17 +02:00
|
|
|
for tt in tokens.into_trees() {
|
|
|
|
match tt {
|
2019-06-05 14:17:56 +03:00
|
|
|
// Only report non-raw idents.
|
2019-12-22 17:42:04 -05:00
|
|
|
TokenTree::Token(token) => {
|
|
|
|
if let Some((ident, false)) = token.ident() {
|
|
|
|
self.check_ident_token(cx, UnderMacro(true), ident);
|
|
|
|
}
|
2018-07-14 16:40:17 +02:00
|
|
|
}
|
2019-12-22 17:42:04 -05:00
|
|
|
TokenTree::Delimited(_, _, tts) => self.check_tokens(cx, tts),
|
2018-07-14 16:40:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
fn check_ident_token(
|
|
|
|
&mut self,
|
|
|
|
cx: &EarlyContext<'_>,
|
|
|
|
UnderMacro(under_macro): UnderMacro,
|
2020-04-19 13:00:18 +02:00
|
|
|
ident: Ident,
|
2019-12-22 17:42:04 -05:00
|
|
|
) {
|
2019-04-18 12:55:23 -07:00
|
|
|
let next_edition = match cx.sess.edition() {
|
2018-08-24 13:48:20 -07:00
|
|
|
Edition::Edition2015 => {
|
2019-05-23 16:11:52 +10:00
|
|
|
match ident.name {
|
|
|
|
kw::Async | kw::Await | kw::Try => Edition::Edition2018,
|
2019-03-27 14:43:29 +01:00
|
|
|
|
|
|
|
// rust-lang/rust#56327: Conservatively do not
|
|
|
|
// attempt to report occurrences of `dyn` within
|
|
|
|
// macro definitions or invocations, because `dyn`
|
|
|
|
// can legitimately occur as a contextual keyword
|
|
|
|
// in 2015 code denoting its 2018 meaning, and we
|
|
|
|
// do not want rustfix to inject bugs into working
|
|
|
|
// code by rewriting such occurrences.
|
|
|
|
//
|
|
|
|
// But if we see `dyn` outside of a macro, we know
|
|
|
|
// its precise role in the parsed AST and thus are
|
|
|
|
// assured this is truly an attempt to use it as
|
|
|
|
// an identifier.
|
2019-05-23 16:11:52 +10:00
|
|
|
kw::Dyn if !under_macro => Edition::Edition2018,
|
2019-03-27 14:43:29 +01:00
|
|
|
|
2018-08-24 13:48:20 -07:00
|
|
|
_ => return,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-27 02:59:49 +00:00
|
|
|
// There are no new keywords yet for the 2018 edition and beyond.
|
2019-04-18 12:55:23 -07:00
|
|
|
_ => return,
|
2018-08-24 13:48:20 -07:00
|
|
|
};
|
|
|
|
|
2019-02-28 22:43:53 +00:00
|
|
|
// Don't lint `r#foo`.
|
2019-04-18 12:55:23 -07:00
|
|
|
if cx.sess.parse_sess.raw_identifier_spans.borrow().contains(&ident.span) {
|
2018-08-24 13:48:20 -07:00
|
|
|
return;
|
2018-07-14 16:40:17 +02:00
|
|
|
}
|
2018-08-24 13:48:20 -07:00
|
|
|
|
2020-02-02 09:47:58 +10:00
|
|
|
cx.struct_span_lint(KEYWORD_IDENTS, ident.span, |lint| {
|
|
|
|
lint.build(&format!("`{}` is a keyword in the {} edition", ident, next_edition))
|
|
|
|
.span_suggestion(
|
|
|
|
ident.span,
|
|
|
|
"you can use a raw identifier to stay compatible",
|
|
|
|
format!("r#{}", ident),
|
|
|
|
Applicability::MachineApplicable,
|
|
|
|
)
|
|
|
|
.emit()
|
|
|
|
});
|
2018-07-14 16:40:17 +02:00
|
|
|
}
|
|
|
|
}
|
in which inferable outlives-requirements are linted
RFC 2093 (tracking issue #44493) lets us leave off
commonsensically inferable `T: 'a` outlives requirements. (A separate
feature-gate was split off for the case of 'static lifetimes, for
which questions still remain.) Detecting these was requested as an
idioms-2018 lint.
It turns out that issuing a correct, autofixable suggestion here is
somewhat subtle in the presence of other bounds and generic
parameters. Basically, we want to handle these three cases:
• One outlives-bound. We want to drop the bound altogether, including
the colon—
MyStruct<'a, T: 'a>
^^^^ help: remove this bound
• An outlives bound first, followed by a trait bound. We want to
delete the outlives bound and the following plus sign (and
hopefully get the whitespace right, too)—
MyStruct<'a, T: 'a + MyTrait>
^^^^^ help: remove this bound
• An outlives bound after a trait bound. We want to delete the
outlives lifetime and the preceding plus sign—
MyStruct<'a, T: MyTrait + 'a>
^^^^^ help: remove this bound
This gets (slightly) even more complicated in the case of where
clauses, where we want to drop the where clause altogether if there's
just the one bound. Hopefully the comments are enough to explain
what's going on!
A script (in Python, sorry) was used to generate the
hopefully-sufficiently-exhaustive UI test input. Some of these are
split off into a different file because rust-lang-nursery/rustfix#141
(and, causally upstream of that, #53934) prevents them from being
`run-rustfix`-tested.
We also make sure to include a UI test of a case (copied from RFC
2093) where the outlives-bound can't be inferred. Special thanks to
Niko Matsakis for pointing out the `inferred_outlives_of` query,
rather than blindly stripping outlives requirements as if we weren't a
production compiler and didn't care.
This concerns #52042.
2018-08-26 12:22:04 -07:00
|
|
|
|
2019-03-27 14:43:29 +01:00
|
|
|
impl EarlyLintPass for KeywordIdents {
|
|
|
|
fn check_mac_def(&mut self, cx: &EarlyContext<'_>, mac_def: &ast::MacroDef, _id: ast::NodeId) {
|
2019-12-01 17:53:59 +03:00
|
|
|
self.check_tokens(cx, mac_def.body.inner_tokens());
|
2019-03-27 14:43:29 +01:00
|
|
|
}
|
2020-02-29 19:32:20 +03:00
|
|
|
fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::MacCall) {
|
2019-12-01 02:25:32 +03:00
|
|
|
self.check_tokens(cx, mac.args.inner_tokens());
|
2019-03-27 14:43:29 +01:00
|
|
|
}
|
2020-04-19 13:00:18 +02:00
|
|
|
fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: Ident) {
|
2019-03-27 14:43:29 +01:00
|
|
|
self.check_ident_token(cx, UnderMacro(false), ident);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:40 +02:00
|
|
|
declare_lint_pass!(ExplicitOutlivesRequirements => [EXPLICIT_OUTLIVES_REQUIREMENTS]);
|
in which inferable outlives-requirements are linted
RFC 2093 (tracking issue #44493) lets us leave off
commonsensically inferable `T: 'a` outlives requirements. (A separate
feature-gate was split off for the case of 'static lifetimes, for
which questions still remain.) Detecting these was requested as an
idioms-2018 lint.
It turns out that issuing a correct, autofixable suggestion here is
somewhat subtle in the presence of other bounds and generic
parameters. Basically, we want to handle these three cases:
• One outlives-bound. We want to drop the bound altogether, including
the colon—
MyStruct<'a, T: 'a>
^^^^ help: remove this bound
• An outlives bound first, followed by a trait bound. We want to
delete the outlives bound and the following plus sign (and
hopefully get the whitespace right, too)—
MyStruct<'a, T: 'a + MyTrait>
^^^^^ help: remove this bound
• An outlives bound after a trait bound. We want to delete the
outlives lifetime and the preceding plus sign—
MyStruct<'a, T: MyTrait + 'a>
^^^^^ help: remove this bound
This gets (slightly) even more complicated in the case of where
clauses, where we want to drop the where clause altogether if there's
just the one bound. Hopefully the comments are enough to explain
what's going on!
A script (in Python, sorry) was used to generate the
hopefully-sufficiently-exhaustive UI test input. Some of these are
split off into a different file because rust-lang-nursery/rustfix#141
(and, causally upstream of that, #53934) prevents them from being
`run-rustfix`-tested.
We also make sure to include a UI test of a case (copied from RFC
2093) where the outlives-bound can't be inferred. Special thanks to
Niko Matsakis for pointing out the `inferred_outlives_of` query,
rather than blindly stripping outlives requirements as if we weren't a
production compiler and didn't care.
This concerns #52042.
2018-08-26 12:22:04 -07:00
|
|
|
|
|
|
|
impl ExplicitOutlivesRequirements {
|
2019-05-25 10:28:17 +01:00
|
|
|
fn lifetimes_outliving_lifetime<'tcx>(
|
2019-10-18 04:24:22 +03:00
|
|
|
inferred_outlives: &'tcx [(ty::Predicate<'tcx>, Span)],
|
2019-05-25 10:28:17 +01:00
|
|
|
index: u32,
|
|
|
|
) -> Vec<ty::Region<'tcx>> {
|
2019-12-22 17:42:04 -05:00
|
|
|
inferred_outlives
|
|
|
|
.iter()
|
2020-07-09 00:35:55 +02:00
|
|
|
.filter_map(|(pred, _)| match pred.skip_binders() {
|
|
|
|
ty::PredicateAtom::RegionOutlives(ty::OutlivesPredicate(a, b)) => match a {
|
2020-06-18 20:41:43 +02:00
|
|
|
ty::ReEarlyBound(ebr) if ebr.index == index => Some(b),
|
|
|
|
_ => None,
|
|
|
|
},
|
2019-12-22 17:42:04 -05:00
|
|
|
_ => None,
|
|
|
|
})
|
|
|
|
.collect()
|
2019-05-25 10:28:17 +01:00
|
|
|
}
|
in which inferable outlives-requirements are linted
RFC 2093 (tracking issue #44493) lets us leave off
commonsensically inferable `T: 'a` outlives requirements. (A separate
feature-gate was split off for the case of 'static lifetimes, for
which questions still remain.) Detecting these was requested as an
idioms-2018 lint.
It turns out that issuing a correct, autofixable suggestion here is
somewhat subtle in the presence of other bounds and generic
parameters. Basically, we want to handle these three cases:
• One outlives-bound. We want to drop the bound altogether, including
the colon—
MyStruct<'a, T: 'a>
^^^^ help: remove this bound
• An outlives bound first, followed by a trait bound. We want to
delete the outlives bound and the following plus sign (and
hopefully get the whitespace right, too)—
MyStruct<'a, T: 'a + MyTrait>
^^^^^ help: remove this bound
• An outlives bound after a trait bound. We want to delete the
outlives lifetime and the preceding plus sign—
MyStruct<'a, T: MyTrait + 'a>
^^^^^ help: remove this bound
This gets (slightly) even more complicated in the case of where
clauses, where we want to drop the where clause altogether if there's
just the one bound. Hopefully the comments are enough to explain
what's going on!
A script (in Python, sorry) was used to generate the
hopefully-sufficiently-exhaustive UI test input. Some of these are
split off into a different file because rust-lang-nursery/rustfix#141
(and, causally upstream of that, #53934) prevents them from being
`run-rustfix`-tested.
We also make sure to include a UI test of a case (copied from RFC
2093) where the outlives-bound can't be inferred. Special thanks to
Niko Matsakis for pointing out the `inferred_outlives_of` query,
rather than blindly stripping outlives requirements as if we weren't a
production compiler and didn't care.
This concerns #52042.
2018-08-26 12:22:04 -07:00
|
|
|
|
2019-05-25 10:28:17 +01:00
|
|
|
fn lifetimes_outliving_type<'tcx>(
|
2019-10-18 04:24:22 +03:00
|
|
|
inferred_outlives: &'tcx [(ty::Predicate<'tcx>, Span)],
|
2019-05-25 10:28:17 +01:00
|
|
|
index: u32,
|
|
|
|
) -> Vec<ty::Region<'tcx>> {
|
2019-12-22 17:42:04 -05:00
|
|
|
inferred_outlives
|
|
|
|
.iter()
|
2020-07-09 00:35:55 +02:00
|
|
|
.filter_map(|(pred, _)| match pred.skip_binders() {
|
|
|
|
ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(a, b)) => {
|
2020-06-18 20:41:43 +02:00
|
|
|
a.is_param(index).then_some(b)
|
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-12-22 17:42:04 -05:00
|
|
|
_ => None,
|
|
|
|
})
|
|
|
|
.collect()
|
2019-05-25 10:28:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn collect_outlived_lifetimes<'tcx>(
|
|
|
|
&self,
|
2019-12-01 16:08:58 +01:00
|
|
|
param: &'tcx hir::GenericParam<'tcx>,
|
2019-05-25 10:28:17 +01:00
|
|
|
tcx: TyCtxt<'tcx>,
|
2019-10-18 04:24:22 +03:00
|
|
|
inferred_outlives: &'tcx [(ty::Predicate<'tcx>, Span)],
|
2019-05-25 10:28:17 +01:00
|
|
|
ty_generics: &'tcx ty::Generics,
|
|
|
|
) -> Vec<ty::Region<'tcx>> {
|
2020-04-09 09:43:00 +01:00
|
|
|
let index =
|
|
|
|
ty_generics.param_def_id_to_index[&tcx.hir().local_def_id(param.hir_id).to_def_id()];
|
2019-05-25 10:28:17 +01:00
|
|
|
|
|
|
|
match param.kind {
|
|
|
|
hir::GenericParamKind::Lifetime { .. } => {
|
2020-06-21 12:26:17 +02:00
|
|
|
Self::lifetimes_outliving_lifetime(inferred_outlives, index)
|
2019-05-25 10:28:17 +01:00
|
|
|
}
|
|
|
|
hir::GenericParamKind::Type { .. } => {
|
2020-06-21 12:26:17 +02:00
|
|
|
Self::lifetimes_outliving_type(inferred_outlives, index)
|
in which inferable outlives-requirements are linted
RFC 2093 (tracking issue #44493) lets us leave off
commonsensically inferable `T: 'a` outlives requirements. (A separate
feature-gate was split off for the case of 'static lifetimes, for
which questions still remain.) Detecting these was requested as an
idioms-2018 lint.
It turns out that issuing a correct, autofixable suggestion here is
somewhat subtle in the presence of other bounds and generic
parameters. Basically, we want to handle these three cases:
• One outlives-bound. We want to drop the bound altogether, including
the colon—
MyStruct<'a, T: 'a>
^^^^ help: remove this bound
• An outlives bound first, followed by a trait bound. We want to
delete the outlives bound and the following plus sign (and
hopefully get the whitespace right, too)—
MyStruct<'a, T: 'a + MyTrait>
^^^^^ help: remove this bound
• An outlives bound after a trait bound. We want to delete the
outlives lifetime and the preceding plus sign—
MyStruct<'a, T: MyTrait + 'a>
^^^^^ help: remove this bound
This gets (slightly) even more complicated in the case of where
clauses, where we want to drop the where clause altogether if there's
just the one bound. Hopefully the comments are enough to explain
what's going on!
A script (in Python, sorry) was used to generate the
hopefully-sufficiently-exhaustive UI test input. Some of these are
split off into a different file because rust-lang-nursery/rustfix#141
(and, causally upstream of that, #53934) prevents them from being
`run-rustfix`-tested.
We also make sure to include a UI test of a case (copied from RFC
2093) where the outlives-bound can't be inferred. Special thanks to
Niko Matsakis for pointing out the `inferred_outlives_of` query,
rather than blindly stripping outlives requirements as if we weren't a
production compiler and didn't care.
This concerns #52042.
2018-08-26 12:22:04 -07:00
|
|
|
}
|
2019-05-25 10:28:17 +01:00
|
|
|
hir::GenericParamKind::Const { .. } => Vec::new(),
|
in which inferable outlives-requirements are linted
RFC 2093 (tracking issue #44493) lets us leave off
commonsensically inferable `T: 'a` outlives requirements. (A separate
feature-gate was split off for the case of 'static lifetimes, for
which questions still remain.) Detecting these was requested as an
idioms-2018 lint.
It turns out that issuing a correct, autofixable suggestion here is
somewhat subtle in the presence of other bounds and generic
parameters. Basically, we want to handle these three cases:
• One outlives-bound. We want to drop the bound altogether, including
the colon—
MyStruct<'a, T: 'a>
^^^^ help: remove this bound
• An outlives bound first, followed by a trait bound. We want to
delete the outlives bound and the following plus sign (and
hopefully get the whitespace right, too)—
MyStruct<'a, T: 'a + MyTrait>
^^^^^ help: remove this bound
• An outlives bound after a trait bound. We want to delete the
outlives lifetime and the preceding plus sign—
MyStruct<'a, T: MyTrait + 'a>
^^^^^ help: remove this bound
This gets (slightly) even more complicated in the case of where
clauses, where we want to drop the where clause altogether if there's
just the one bound. Hopefully the comments are enough to explain
what's going on!
A script (in Python, sorry) was used to generate the
hopefully-sufficiently-exhaustive UI test input. Some of these are
split off into a different file because rust-lang-nursery/rustfix#141
(and, causally upstream of that, #53934) prevents them from being
`run-rustfix`-tested.
We also make sure to include a UI test of a case (copied from RFC
2093) where the outlives-bound can't be inferred. Special thanks to
Niko Matsakis for pointing out the `inferred_outlives_of` query,
rather than blindly stripping outlives requirements as if we weren't a
production compiler and didn't care.
This concerns #52042.
2018-08-26 12:22:04 -07:00
|
|
|
}
|
2019-05-25 10:28:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn collect_outlives_bound_spans<'tcx>(
|
|
|
|
&self,
|
|
|
|
tcx: TyCtxt<'tcx>,
|
2019-12-01 16:08:58 +01:00
|
|
|
bounds: &hir::GenericBounds<'_>,
|
2019-05-25 10:28:17 +01:00
|
|
|
inferred_outlives: &[ty::Region<'tcx>],
|
|
|
|
infer_static: bool,
|
|
|
|
) -> Vec<(usize, Span)> {
|
2020-03-29 16:41:09 +02:00
|
|
|
use rustc_middle::middle::resolve_lifetime::Region;
|
2019-05-25 10:28:17 +01:00
|
|
|
|
|
|
|
bounds
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.filter_map(|(i, bound)| {
|
|
|
|
if let hir::GenericBound::Outlives(lifetime) = bound {
|
|
|
|
let is_inferred = match tcx.named_region(lifetime.hir_id) {
|
2019-12-22 17:42:04 -05:00
|
|
|
Some(Region::Static) if infer_static => inferred_outlives
|
2019-05-25 10:28:17 +01:00
|
|
|
.iter()
|
2019-12-22 17:42:04 -05:00
|
|
|
.any(|r| if let ty::ReStatic = r { true } else { false }),
|
|
|
|
Some(Region::EarlyBound(index, ..)) => inferred_outlives.iter().any(|r| {
|
|
|
|
if let ty::ReEarlyBound(ebr) = r { ebr.index == index } else { false }
|
|
|
|
}),
|
2019-05-25 10:28:17 +01:00
|
|
|
_ => false,
|
|
|
|
};
|
2019-12-06 12:18:32 +00:00
|
|
|
is_inferred.then_some((i, bound.span()))
|
2019-05-25 10:28:17 +01:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect()
|
in which inferable outlives-requirements are linted
RFC 2093 (tracking issue #44493) lets us leave off
commonsensically inferable `T: 'a` outlives requirements. (A separate
feature-gate was split off for the case of 'static lifetimes, for
which questions still remain.) Detecting these was requested as an
idioms-2018 lint.
It turns out that issuing a correct, autofixable suggestion here is
somewhat subtle in the presence of other bounds and generic
parameters. Basically, we want to handle these three cases:
• One outlives-bound. We want to drop the bound altogether, including
the colon—
MyStruct<'a, T: 'a>
^^^^ help: remove this bound
• An outlives bound first, followed by a trait bound. We want to
delete the outlives bound and the following plus sign (and
hopefully get the whitespace right, too)—
MyStruct<'a, T: 'a + MyTrait>
^^^^^ help: remove this bound
• An outlives bound after a trait bound. We want to delete the
outlives lifetime and the preceding plus sign—
MyStruct<'a, T: MyTrait + 'a>
^^^^^ help: remove this bound
This gets (slightly) even more complicated in the case of where
clauses, where we want to drop the where clause altogether if there's
just the one bound. Hopefully the comments are enough to explain
what's going on!
A script (in Python, sorry) was used to generate the
hopefully-sufficiently-exhaustive UI test input. Some of these are
split off into a different file because rust-lang-nursery/rustfix#141
(and, causally upstream of that, #53934) prevents them from being
`run-rustfix`-tested.
We also make sure to include a UI test of a case (copied from RFC
2093) where the outlives-bound can't be inferred. Special thanks to
Niko Matsakis for pointing out the `inferred_outlives_of` query,
rather than blindly stripping outlives requirements as if we weren't a
production compiler and didn't care.
This concerns #52042.
2018-08-26 12:22:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
fn consolidate_outlives_bound_spans(
|
|
|
|
&self,
|
|
|
|
lo: Span,
|
2019-12-01 16:08:58 +01:00
|
|
|
bounds: &hir::GenericBounds<'_>,
|
2019-12-22 17:42:04 -05:00
|
|
|
bound_spans: Vec<(usize, Span)>,
|
in which inferable outlives-requirements are linted
RFC 2093 (tracking issue #44493) lets us leave off
commonsensically inferable `T: 'a` outlives requirements. (A separate
feature-gate was split off for the case of 'static lifetimes, for
which questions still remain.) Detecting these was requested as an
idioms-2018 lint.
It turns out that issuing a correct, autofixable suggestion here is
somewhat subtle in the presence of other bounds and generic
parameters. Basically, we want to handle these three cases:
• One outlives-bound. We want to drop the bound altogether, including
the colon—
MyStruct<'a, T: 'a>
^^^^ help: remove this bound
• An outlives bound first, followed by a trait bound. We want to
delete the outlives bound and the following plus sign (and
hopefully get the whitespace right, too)—
MyStruct<'a, T: 'a + MyTrait>
^^^^^ help: remove this bound
• An outlives bound after a trait bound. We want to delete the
outlives lifetime and the preceding plus sign—
MyStruct<'a, T: MyTrait + 'a>
^^^^^ help: remove this bound
This gets (slightly) even more complicated in the case of where
clauses, where we want to drop the where clause altogether if there's
just the one bound. Hopefully the comments are enough to explain
what's going on!
A script (in Python, sorry) was used to generate the
hopefully-sufficiently-exhaustive UI test input. Some of these are
split off into a different file because rust-lang-nursery/rustfix#141
(and, causally upstream of that, #53934) prevents them from being
`run-rustfix`-tested.
We also make sure to include a UI test of a case (copied from RFC
2093) where the outlives-bound can't be inferred. Special thanks to
Niko Matsakis for pointing out the `inferred_outlives_of` query,
rather than blindly stripping outlives requirements as if we weren't a
production compiler and didn't care.
This concerns #52042.
2018-08-26 12:22:04 -07:00
|
|
|
) -> Vec<Span> {
|
|
|
|
if bounds.is_empty() {
|
|
|
|
return Vec::new();
|
|
|
|
}
|
|
|
|
if bound_spans.len() == bounds.len() {
|
2019-12-22 17:42:04 -05:00
|
|
|
let (_, last_bound_span) = bound_spans[bound_spans.len() - 1];
|
in which inferable outlives-requirements are linted
RFC 2093 (tracking issue #44493) lets us leave off
commonsensically inferable `T: 'a` outlives requirements. (A separate
feature-gate was split off for the case of 'static lifetimes, for
which questions still remain.) Detecting these was requested as an
idioms-2018 lint.
It turns out that issuing a correct, autofixable suggestion here is
somewhat subtle in the presence of other bounds and generic
parameters. Basically, we want to handle these three cases:
• One outlives-bound. We want to drop the bound altogether, including
the colon—
MyStruct<'a, T: 'a>
^^^^ help: remove this bound
• An outlives bound first, followed by a trait bound. We want to
delete the outlives bound and the following plus sign (and
hopefully get the whitespace right, too)—
MyStruct<'a, T: 'a + MyTrait>
^^^^^ help: remove this bound
• An outlives bound after a trait bound. We want to delete the
outlives lifetime and the preceding plus sign—
MyStruct<'a, T: MyTrait + 'a>
^^^^^ help: remove this bound
This gets (slightly) even more complicated in the case of where
clauses, where we want to drop the where clause altogether if there's
just the one bound. Hopefully the comments are enough to explain
what's going on!
A script (in Python, sorry) was used to generate the
hopefully-sufficiently-exhaustive UI test input. Some of these are
split off into a different file because rust-lang-nursery/rustfix#141
(and, causally upstream of that, #53934) prevents them from being
`run-rustfix`-tested.
We also make sure to include a UI test of a case (copied from RFC
2093) where the outlives-bound can't be inferred. Special thanks to
Niko Matsakis for pointing out the `inferred_outlives_of` query,
rather than blindly stripping outlives requirements as if we weren't a
production compiler and didn't care.
This concerns #52042.
2018-08-26 12:22:04 -07:00
|
|
|
// If all bounds are inferable, we want to delete the colon, so
|
|
|
|
// start from just after the parameter (span passed as argument)
|
|
|
|
vec![lo.to(last_bound_span)]
|
|
|
|
} else {
|
|
|
|
let mut merged = Vec::new();
|
|
|
|
let mut last_merged_i = None;
|
|
|
|
|
|
|
|
let mut from_start = true;
|
|
|
|
for (i, bound_span) in bound_spans {
|
|
|
|
match last_merged_i {
|
2019-05-31 21:31:03 +01:00
|
|
|
// If the first bound is inferable, our span should also eat the leading `+`.
|
in which inferable outlives-requirements are linted
RFC 2093 (tracking issue #44493) lets us leave off
commonsensically inferable `T: 'a` outlives requirements. (A separate
feature-gate was split off for the case of 'static lifetimes, for
which questions still remain.) Detecting these was requested as an
idioms-2018 lint.
It turns out that issuing a correct, autofixable suggestion here is
somewhat subtle in the presence of other bounds and generic
parameters. Basically, we want to handle these three cases:
• One outlives-bound. We want to drop the bound altogether, including
the colon—
MyStruct<'a, T: 'a>
^^^^ help: remove this bound
• An outlives bound first, followed by a trait bound. We want to
delete the outlives bound and the following plus sign (and
hopefully get the whitespace right, too)—
MyStruct<'a, T: 'a + MyTrait>
^^^^^ help: remove this bound
• An outlives bound after a trait bound. We want to delete the
outlives lifetime and the preceding plus sign—
MyStruct<'a, T: MyTrait + 'a>
^^^^^ help: remove this bound
This gets (slightly) even more complicated in the case of where
clauses, where we want to drop the where clause altogether if there's
just the one bound. Hopefully the comments are enough to explain
what's going on!
A script (in Python, sorry) was used to generate the
hopefully-sufficiently-exhaustive UI test input. Some of these are
split off into a different file because rust-lang-nursery/rustfix#141
(and, causally upstream of that, #53934) prevents them from being
`run-rustfix`-tested.
We also make sure to include a UI test of a case (copied from RFC
2093) where the outlives-bound can't be inferred. Special thanks to
Niko Matsakis for pointing out the `inferred_outlives_of` query,
rather than blindly stripping outlives requirements as if we weren't a
production compiler and didn't care.
This concerns #52042.
2018-08-26 12:22:04 -07:00
|
|
|
None if i == 0 => {
|
|
|
|
merged.push(bound_span.to(bounds[1].span().shrink_to_lo()));
|
|
|
|
last_merged_i = Some(0);
|
2019-12-22 17:42:04 -05: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
|
|
|
// If consecutive bounds are inferable, merge their spans
|
2019-12-22 17:42:04 -05:00
|
|
|
Some(h) if i == h + 1 => {
|
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 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() {
|
2019-12-22 17:42:04 -05:00
|
|
|
bounds[i + 1].span().shrink_to_lo()
|
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
|
|
|
} else {
|
|
|
|
bound_span
|
|
|
|
};
|
|
|
|
*tail = tail.to(to_span);
|
|
|
|
last_merged_i = Some(i);
|
|
|
|
} else {
|
|
|
|
bug!("another bound-span visited earlier");
|
|
|
|
}
|
2019-12-22 17:42:04 -05: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
|
|
|
_ => {
|
|
|
|
// 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;
|
2019-12-22 17:42:04 -05:00
|
|
|
merged.push(bounds[i - 1].span().shrink_to_hi().to(bound_span));
|
in which inferable outlives-requirements are linted
RFC 2093 (tracking issue #44493) lets us leave off
commonsensically inferable `T: 'a` outlives requirements. (A separate
feature-gate was split off for the case of 'static lifetimes, for
which questions still remain.) Detecting these was requested as an
idioms-2018 lint.
It turns out that issuing a correct, autofixable suggestion here is
somewhat subtle in the presence of other bounds and generic
parameters. Basically, we want to handle these three cases:
• One outlives-bound. We want to drop the bound altogether, including
the colon—
MyStruct<'a, T: 'a>
^^^^ help: remove this bound
• An outlives bound first, followed by a trait bound. We want to
delete the outlives bound and the following plus sign (and
hopefully get the whitespace right, too)—
MyStruct<'a, T: 'a + MyTrait>
^^^^^ help: remove this bound
• An outlives bound after a trait bound. We want to delete the
outlives lifetime and the preceding plus sign—
MyStruct<'a, T: MyTrait + 'a>
^^^^^ help: remove this bound
This gets (slightly) even more complicated in the case of where
clauses, where we want to drop the where clause altogether if there's
just the one bound. Hopefully the comments are enough to explain
what's going on!
A script (in Python, sorry) was used to generate the
hopefully-sufficiently-exhaustive UI test input. Some of these are
split off into a different file because rust-lang-nursery/rustfix#141
(and, causally upstream of that, #53934) prevents them from being
`run-rustfix`-tested.
We also make sure to include a UI test of a case (copied from RFC
2093) where the outlives-bound can't be inferred. Special thanks to
Niko Matsakis for pointing out the `inferred_outlives_of` query,
rather than blindly stripping outlives requirements as if we weren't a
production compiler and didn't care.
This concerns #52042.
2018-08-26 12:22:04 -07:00
|
|
|
last_merged_i = Some(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
merged
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-25 23:41:36 +03:00
|
|
|
impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
|
|
|
|
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
|
2020-03-29 16:41:09 +02:00
|
|
|
use rustc_middle::middle::resolve_lifetime::Region;
|
2019-05-25 10:28:17 +01: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
|
|
|
let infer_static = cx.tcx.features().infer_static_outlives_requirements;
|
2019-06-27 11:28:14 +02:00
|
|
|
let def_id = cx.tcx.hir().local_def_id(item.hir_id);
|
2019-05-25 10:28:17 +01:00
|
|
|
if let hir::ItemKind::Struct(_, ref hir_generics)
|
2019-12-22 17:42:04 -05:00
|
|
|
| hir::ItemKind::Enum(_, ref hir_generics)
|
|
|
|
| hir::ItemKind::Union(_, ref hir_generics) = item.kind
|
2019-05-25 10:28:17 +01:00
|
|
|
{
|
|
|
|
let inferred_outlives = cx.tcx.inferred_outlives_of(def_id);
|
|
|
|
if inferred_outlives.is_empty() {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let ty_generics = cx.tcx.generics_of(def_id);
|
|
|
|
|
in which inferable outlives-requirements are linted
RFC 2093 (tracking issue #44493) lets us leave off
commonsensically inferable `T: 'a` outlives requirements. (A separate
feature-gate was split off for the case of 'static lifetimes, for
which questions still remain.) Detecting these was requested as an
idioms-2018 lint.
It turns out that issuing a correct, autofixable suggestion here is
somewhat subtle in the presence of other bounds and generic
parameters. Basically, we want to handle these three cases:
• One outlives-bound. We want to drop the bound altogether, including
the colon—
MyStruct<'a, T: 'a>
^^^^ help: remove this bound
• An outlives bound first, followed by a trait bound. We want to
delete the outlives bound and the following plus sign (and
hopefully get the whitespace right, too)—
MyStruct<'a, T: 'a + MyTrait>
^^^^^ help: remove this bound
• An outlives bound after a trait bound. We want to delete the
outlives lifetime and the preceding plus sign—
MyStruct<'a, T: MyTrait + 'a>
^^^^^ help: remove this bound
This gets (slightly) even more complicated in the case of where
clauses, where we want to drop the where clause altogether if there's
just the one bound. Hopefully the comments are enough to explain
what's going on!
A script (in Python, sorry) was used to generate the
hopefully-sufficiently-exhaustive UI test input. Some of these are
split off into a different file because rust-lang-nursery/rustfix#141
(and, causally upstream of that, #53934) prevents them from being
`run-rustfix`-tested.
We also make sure to include a UI test of a case (copied from RFC
2093) where the outlives-bound can't be inferred. Special thanks to
Niko Matsakis for pointing out the `inferred_outlives_of` query,
rather than blindly stripping outlives requirements as if we weren't a
production compiler and didn't care.
This concerns #52042.
2018-08-26 12:22:04 -07:00
|
|
|
let mut bound_count = 0;
|
|
|
|
let mut lint_spans = Vec::new();
|
|
|
|
|
2019-12-01 17:10:12 +01:00
|
|
|
for param in hir_generics.params {
|
2019-05-25 10:28:17 +01:00
|
|
|
let has_lifetime_bounds = param.bounds.iter().any(|bound| {
|
2019-12-22 17:42:04 -05:00
|
|
|
if let hir::GenericBound::Outlives(_) = bound { true } else { false }
|
2019-05-25 10:28:17 +01:00
|
|
|
});
|
|
|
|
if !has_lifetime_bounds {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
let relevant_lifetimes =
|
|
|
|
self.collect_outlived_lifetimes(param, cx.tcx, inferred_outlives, ty_generics);
|
2019-05-25 10:28:17 +01:00
|
|
|
if relevant_lifetimes.is_empty() {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
in which inferable outlives-requirements are linted
RFC 2093 (tracking issue #44493) lets us leave off
commonsensically inferable `T: 'a` outlives requirements. (A separate
feature-gate was split off for the case of 'static lifetimes, for
which questions still remain.) Detecting these was requested as an
idioms-2018 lint.
It turns out that issuing a correct, autofixable suggestion here is
somewhat subtle in the presence of other bounds and generic
parameters. Basically, we want to handle these three cases:
• One outlives-bound. We want to drop the bound altogether, including
the colon—
MyStruct<'a, T: 'a>
^^^^ help: remove this bound
• An outlives bound first, followed by a trait bound. We want to
delete the outlives bound and the following plus sign (and
hopefully get the whitespace right, too)—
MyStruct<'a, T: 'a + MyTrait>
^^^^^ help: remove this bound
• An outlives bound after a trait bound. We want to delete the
outlives lifetime and the preceding plus sign—
MyStruct<'a, T: MyTrait + 'a>
^^^^^ help: remove this bound
This gets (slightly) even more complicated in the case of where
clauses, where we want to drop the where clause altogether if there's
just the one bound. Hopefully the comments are enough to explain
what's going on!
A script (in Python, sorry) was used to generate the
hopefully-sufficiently-exhaustive UI test input. Some of these are
split off into a different file because rust-lang-nursery/rustfix#141
(and, causally upstream of that, #53934) prevents them from being
`run-rustfix`-tested.
We also make sure to include a UI test of a case (copied from RFC
2093) where the outlives-bound can't be inferred. Special thanks to
Niko Matsakis for pointing out the `inferred_outlives_of` query,
rather than blindly stripping outlives requirements as if we weren't a
production compiler and didn't care.
This concerns #52042.
2018-08-26 12:22:04 -07:00
|
|
|
let bound_spans = self.collect_outlives_bound_spans(
|
2019-12-22 17:42:04 -05:00
|
|
|
cx.tcx,
|
|
|
|
¶m.bounds,
|
|
|
|
&relevant_lifetimes,
|
|
|
|
infer_static,
|
in which inferable outlives-requirements are linted
RFC 2093 (tracking issue #44493) lets us leave off
commonsensically inferable `T: 'a` outlives requirements. (A separate
feature-gate was split off for the case of 'static lifetimes, for
which questions still remain.) Detecting these was requested as an
idioms-2018 lint.
It turns out that issuing a correct, autofixable suggestion here is
somewhat subtle in the presence of other bounds and generic
parameters. Basically, we want to handle these three cases:
• One outlives-bound. We want to drop the bound altogether, including
the colon—
MyStruct<'a, T: 'a>
^^^^ help: remove this bound
• An outlives bound first, followed by a trait bound. We want to
delete the outlives bound and the following plus sign (and
hopefully get the whitespace right, too)—
MyStruct<'a, T: 'a + MyTrait>
^^^^^ help: remove this bound
• An outlives bound after a trait bound. We want to delete the
outlives lifetime and the preceding plus sign—
MyStruct<'a, T: MyTrait + 'a>
^^^^^ help: remove this bound
This gets (slightly) even more complicated in the case of where
clauses, where we want to drop the where clause altogether if there's
just the one bound. Hopefully the comments are enough to explain
what's going on!
A script (in Python, sorry) was used to generate the
hopefully-sufficiently-exhaustive UI test input. Some of these are
split off into a different file because rust-lang-nursery/rustfix#141
(and, causally upstream of that, #53934) prevents them from being
`run-rustfix`-tested.
We also make sure to include a UI test of a case (copied from RFC
2093) where the outlives-bound can't be inferred. Special thanks to
Niko Matsakis for pointing out the `inferred_outlives_of` query,
rather than blindly stripping outlives requirements as if we weren't a
production compiler and didn't care.
This concerns #52042.
2018-08-26 12:22:04 -07:00
|
|
|
);
|
|
|
|
bound_count += bound_spans.len();
|
2019-12-22 17:42:04 -05:00
|
|
|
lint_spans.extend(self.consolidate_outlives_bound_spans(
|
|
|
|
param.span.shrink_to_hi(),
|
|
|
|
¶m.bounds,
|
|
|
|
bound_spans,
|
|
|
|
));
|
in which inferable outlives-requirements are linted
RFC 2093 (tracking issue #44493) lets us leave off
commonsensically inferable `T: 'a` outlives requirements. (A separate
feature-gate was split off for the case of 'static lifetimes, for
which questions still remain.) Detecting these was requested as an
idioms-2018 lint.
It turns out that issuing a correct, autofixable suggestion here is
somewhat subtle in the presence of other bounds and generic
parameters. Basically, we want to handle these three cases:
• One outlives-bound. We want to drop the bound altogether, including
the colon—
MyStruct<'a, T: 'a>
^^^^ help: remove this bound
• An outlives bound first, followed by a trait bound. We want to
delete the outlives bound and the following plus sign (and
hopefully get the whitespace right, too)—
MyStruct<'a, T: 'a + MyTrait>
^^^^^ help: remove this bound
• An outlives bound after a trait bound. We want to delete the
outlives lifetime and the preceding plus sign—
MyStruct<'a, T: MyTrait + 'a>
^^^^^ help: remove this bound
This gets (slightly) even more complicated in the case of where
clauses, where we want to drop the where clause altogether if there's
just the one bound. Hopefully the comments are enough to explain
what's going on!
A script (in Python, sorry) was used to generate the
hopefully-sufficiently-exhaustive UI test input. Some of these are
split off into a different file because rust-lang-nursery/rustfix#141
(and, causally upstream of that, #53934) prevents them from being
`run-rustfix`-tested.
We also make sure to include a UI test of a case (copied from RFC
2093) where the outlives-bound can't be inferred. Special thanks to
Niko Matsakis for pointing out the `inferred_outlives_of` query,
rather than blindly stripping outlives requirements as if we weren't a
production compiler and didn't care.
This concerns #52042.
2018-08-26 12:22:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
let mut where_lint_spans = Vec::new();
|
|
|
|
let mut dropped_predicate_count = 0;
|
2019-05-25 10:28:17 +01:00
|
|
|
let num_predicates = hir_generics.where_clause.predicates.len();
|
|
|
|
for (i, where_predicate) in hir_generics.where_clause.predicates.iter().enumerate() {
|
|
|
|
let (relevant_lifetimes, bounds, span) = match where_predicate {
|
|
|
|
hir::WherePredicate::RegionPredicate(predicate) => {
|
2019-12-22 17:42:04 -05:00
|
|
|
if let Some(Region::EarlyBound(index, ..)) =
|
|
|
|
cx.tcx.named_region(predicate.lifetime.hir_id)
|
2019-05-25 10:28:17 +01:00
|
|
|
{
|
|
|
|
(
|
2020-06-21 12:26:17 +02:00
|
|
|
Self::lifetimes_outliving_lifetime(inferred_outlives, index),
|
in which inferable outlives-requirements are linted
RFC 2093 (tracking issue #44493) lets us leave off
commonsensically inferable `T: 'a` outlives requirements. (A separate
feature-gate was split off for the case of 'static lifetimes, for
which questions still remain.) Detecting these was requested as an
idioms-2018 lint.
It turns out that issuing a correct, autofixable suggestion here is
somewhat subtle in the presence of other bounds and generic
parameters. Basically, we want to handle these three cases:
• One outlives-bound. We want to drop the bound altogether, including
the colon—
MyStruct<'a, T: 'a>
^^^^ help: remove this bound
• An outlives bound first, followed by a trait bound. We want to
delete the outlives bound and the following plus sign (and
hopefully get the whitespace right, too)—
MyStruct<'a, T: 'a + MyTrait>
^^^^^ help: remove this bound
• An outlives bound after a trait bound. We want to delete the
outlives lifetime and the preceding plus sign—
MyStruct<'a, T: MyTrait + 'a>
^^^^^ help: remove this bound
This gets (slightly) even more complicated in the case of where
clauses, where we want to drop the where clause altogether if there's
just the one bound. Hopefully the comments are enough to explain
what's going on!
A script (in Python, sorry) was used to generate the
hopefully-sufficiently-exhaustive UI test input. Some of these are
split off into a different file because rust-lang-nursery/rustfix#141
(and, causally upstream of that, #53934) prevents them from being
`run-rustfix`-tested.
We also make sure to include a UI test of a case (copied from RFC
2093) where the outlives-bound can't be inferred. Special thanks to
Niko Matsakis for pointing out the `inferred_outlives_of` query,
rather than blindly stripping outlives requirements as if we weren't a
production compiler and didn't care.
This concerns #52042.
2018-08-26 12:22:04 -07:00
|
|
|
&predicate.bounds,
|
2019-05-25 10:28:17 +01:00
|
|
|
predicate.span,
|
in which inferable outlives-requirements are linted
RFC 2093 (tracking issue #44493) lets us leave off
commonsensically inferable `T: 'a` outlives requirements. (A separate
feature-gate was split off for the case of 'static lifetimes, for
which questions still remain.) Detecting these was requested as an
idioms-2018 lint.
It turns out that issuing a correct, autofixable suggestion here is
somewhat subtle in the presence of other bounds and generic
parameters. Basically, we want to handle these three cases:
• One outlives-bound. We want to drop the bound altogether, including
the colon—
MyStruct<'a, T: 'a>
^^^^ help: remove this bound
• An outlives bound first, followed by a trait bound. We want to
delete the outlives bound and the following plus sign (and
hopefully get the whitespace right, too)—
MyStruct<'a, T: 'a + MyTrait>
^^^^^ help: remove this bound
• An outlives bound after a trait bound. We want to delete the
outlives lifetime and the preceding plus sign—
MyStruct<'a, T: MyTrait + 'a>
^^^^^ help: remove this bound
This gets (slightly) even more complicated in the case of where
clauses, where we want to drop the where clause altogether if there's
just the one bound. Hopefully the comments are enough to explain
what's going on!
A script (in Python, sorry) was used to generate the
hopefully-sufficiently-exhaustive UI test input. Some of these are
split off into a different file because rust-lang-nursery/rustfix#141
(and, causally upstream of that, #53934) prevents them from being
`run-rustfix`-tested.
We also make sure to include a UI test of a case (copied from RFC
2093) where the outlives-bound can't be inferred. Special thanks to
Niko Matsakis for pointing out the `inferred_outlives_of` query,
rather than blindly stripping outlives requirements as if we weren't a
production compiler and didn't care.
This concerns #52042.
2018-08-26 12:22:04 -07:00
|
|
|
)
|
2019-05-25 10:28:17 +01:00
|
|
|
} else {
|
|
|
|
continue;
|
|
|
|
}
|
in which inferable outlives-requirements are linted
RFC 2093 (tracking issue #44493) lets us leave off
commonsensically inferable `T: 'a` outlives requirements. (A separate
feature-gate was split off for the case of 'static lifetimes, for
which questions still remain.) Detecting these was requested as an
idioms-2018 lint.
It turns out that issuing a correct, autofixable suggestion here is
somewhat subtle in the presence of other bounds and generic
parameters. Basically, we want to handle these three cases:
• One outlives-bound. We want to drop the bound altogether, including
the colon—
MyStruct<'a, T: 'a>
^^^^ help: remove this bound
• An outlives bound first, followed by a trait bound. We want to
delete the outlives bound and the following plus sign (and
hopefully get the whitespace right, too)—
MyStruct<'a, T: 'a + MyTrait>
^^^^^ help: remove this bound
• An outlives bound after a trait bound. We want to delete the
outlives lifetime and the preceding plus sign—
MyStruct<'a, T: MyTrait + 'a>
^^^^^ help: remove this bound
This gets (slightly) even more complicated in the case of where
clauses, where we want to drop the where clause altogether if there's
just the one bound. Hopefully the comments are enough to explain
what's going on!
A script (in Python, sorry) was used to generate the
hopefully-sufficiently-exhaustive UI test input. Some of these are
split off into a different file because rust-lang-nursery/rustfix#141
(and, causally upstream of that, #53934) prevents them from being
`run-rustfix`-tested.
We also make sure to include a UI test of a case (copied from RFC
2093) where the outlives-bound can't be inferred. Special thanks to
Niko Matsakis for pointing out the `inferred_outlives_of` query,
rather than blindly stripping outlives requirements as if we weren't a
production compiler and didn't care.
This concerns #52042.
2018-08-26 12:22:04 -07:00
|
|
|
}
|
2019-05-25 10:28:17 +01:00
|
|
|
hir::WherePredicate::BoundPredicate(predicate) => {
|
|
|
|
// FIXME we can also infer bounds on associated types,
|
|
|
|
// and should check for them here.
|
2019-09-26 17:25:31 +01:00
|
|
|
match predicate.bounded_ty.kind {
|
2019-12-22 17:42:04 -05:00
|
|
|
hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) => {
|
2019-05-31 21:31:03 +01:00
|
|
|
if let Res::Def(DefKind::TyParam, def_id) = path.res {
|
|
|
|
let index = ty_generics.param_def_id_to_index[&def_id];
|
|
|
|
(
|
2020-06-21 12:26:17 +02:00
|
|
|
Self::lifetimes_outliving_type(inferred_outlives, index),
|
2019-05-31 21:31:03 +01:00
|
|
|
&predicate.bounds,
|
|
|
|
predicate.span,
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
continue;
|
|
|
|
}
|
2019-12-22 17:42:04 -05:00
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
continue;
|
|
|
|
}
|
2019-05-25 10:28:17 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => continue,
|
|
|
|
};
|
|
|
|
if relevant_lifetimes.is_empty() {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
let bound_spans = self.collect_outlives_bound_spans(
|
2019-12-22 17:42:04 -05:00
|
|
|
cx.tcx,
|
|
|
|
bounds,
|
|
|
|
&relevant_lifetimes,
|
|
|
|
infer_static,
|
2019-05-25 10:28:17 +01:00
|
|
|
);
|
|
|
|
bound_count += bound_spans.len();
|
|
|
|
|
|
|
|
let drop_predicate = bound_spans.len() == bounds.len();
|
|
|
|
if drop_predicate {
|
|
|
|
dropped_predicate_count += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If all the bounds on a predicate were inferable and there are
|
2019-05-31 21:31:03 +01:00
|
|
|
// further predicates, we want to eat the trailing comma.
|
2019-05-25 10:28:17 +01:00
|
|
|
if drop_predicate && i + 1 < num_predicates {
|
2019-05-31 21:31:03 +01:00
|
|
|
let next_predicate_span = hir_generics.where_clause.predicates[i + 1].span();
|
2019-12-22 17:42:04 -05:00
|
|
|
where_lint_spans.push(span.to(next_predicate_span.shrink_to_lo()));
|
2019-05-25 10:28:17 +01:00
|
|
|
} else {
|
2019-12-22 17:42:04 -05:00
|
|
|
where_lint_spans.extend(self.consolidate_outlives_bound_spans(
|
|
|
|
span.shrink_to_lo(),
|
|
|
|
bounds,
|
|
|
|
bound_spans,
|
|
|
|
));
|
in which inferable outlives-requirements are linted
RFC 2093 (tracking issue #44493) lets us leave off
commonsensically inferable `T: 'a` outlives requirements. (A separate
feature-gate was split off for the case of 'static lifetimes, for
which questions still remain.) Detecting these was requested as an
idioms-2018 lint.
It turns out that issuing a correct, autofixable suggestion here is
somewhat subtle in the presence of other bounds and generic
parameters. Basically, we want to handle these three cases:
• One outlives-bound. We want to drop the bound altogether, including
the colon—
MyStruct<'a, T: 'a>
^^^^ help: remove this bound
• An outlives bound first, followed by a trait bound. We want to
delete the outlives bound and the following plus sign (and
hopefully get the whitespace right, too)—
MyStruct<'a, T: 'a + MyTrait>
^^^^^ help: remove this bound
• An outlives bound after a trait bound. We want to delete the
outlives lifetime and the preceding plus sign—
MyStruct<'a, T: MyTrait + 'a>
^^^^^ help: remove this bound
This gets (slightly) even more complicated in the case of where
clauses, where we want to drop the where clause altogether if there's
just the one bound. Hopefully the comments are enough to explain
what's going on!
A script (in Python, sorry) was used to generate the
hopefully-sufficiently-exhaustive UI test input. Some of these are
split off into a different file because rust-lang-nursery/rustfix#141
(and, causally upstream of that, #53934) prevents them from being
`run-rustfix`-tested.
We also make sure to include a UI test of a case (copied from RFC
2093) where the outlives-bound can't be inferred. Special thanks to
Niko Matsakis for pointing out the `inferred_outlives_of` query,
rather than blindly stripping outlives requirements as if we weren't a
production compiler and didn't care.
This concerns #52042.
2018-08-26 12:22:04 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If all predicates are inferable, drop the entire clause
|
|
|
|
// (including the `where`)
|
|
|
|
if num_predicates > 0 && dropped_predicate_count == num_predicates {
|
2019-12-22 17:42:04 -05:00
|
|
|
let where_span = hir_generics
|
|
|
|
.where_clause
|
|
|
|
.span()
|
2019-05-25 10:28:17 +01:00
|
|
|
.expect("span of (nonempty) where clause should exist");
|
|
|
|
// Extend the where clause back to the closing `>` of the
|
|
|
|
// generics, except for tuple struct, which have the `where`
|
|
|
|
// after the fields of the struct.
|
2019-12-22 17:42:04 -05:00
|
|
|
let full_where_span =
|
|
|
|
if let hir::ItemKind::Struct(hir::VariantData::Tuple(..), _) = item.kind {
|
|
|
|
where_span
|
|
|
|
} else {
|
|
|
|
hir_generics.span.shrink_to_hi().to(where_span)
|
|
|
|
};
|
|
|
|
lint_spans.push(full_where_span);
|
in which inferable outlives-requirements are linted
RFC 2093 (tracking issue #44493) lets us leave off
commonsensically inferable `T: 'a` outlives requirements. (A separate
feature-gate was split off for the case of 'static lifetimes, for
which questions still remain.) Detecting these was requested as an
idioms-2018 lint.
It turns out that issuing a correct, autofixable suggestion here is
somewhat subtle in the presence of other bounds and generic
parameters. Basically, we want to handle these three cases:
• One outlives-bound. We want to drop the bound altogether, including
the colon—
MyStruct<'a, T: 'a>
^^^^ help: remove this bound
• An outlives bound first, followed by a trait bound. We want to
delete the outlives bound and the following plus sign (and
hopefully get the whitespace right, too)—
MyStruct<'a, T: 'a + MyTrait>
^^^^^ help: remove this bound
• An outlives bound after a trait bound. We want to delete the
outlives lifetime and the preceding plus sign—
MyStruct<'a, T: MyTrait + 'a>
^^^^^ help: remove this bound
This gets (slightly) even more complicated in the case of where
clauses, where we want to drop the where clause altogether if there's
just the one bound. Hopefully the comments are enough to explain
what's going on!
A script (in Python, sorry) was used to generate the
hopefully-sufficiently-exhaustive UI test input. Some of these are
split off into a different file because rust-lang-nursery/rustfix#141
(and, causally upstream of that, #53934) prevents them from being
`run-rustfix`-tested.
We also make sure to include a UI test of a case (copied from RFC
2093) where the outlives-bound can't be inferred. Special thanks to
Niko Matsakis for pointing out the `inferred_outlives_of` query,
rather than blindly stripping outlives requirements as if we weren't a
production compiler and didn't care.
This concerns #52042.
2018-08-26 12:22:04 -07:00
|
|
|
} else {
|
|
|
|
lint_spans.extend(where_lint_spans);
|
|
|
|
}
|
|
|
|
|
|
|
|
if !lint_spans.is_empty() {
|
2020-02-02 09:47:58 +10:00
|
|
|
cx.struct_span_lint(EXPLICIT_OUTLIVES_REQUIREMENTS, lint_spans.clone(), |lint| {
|
|
|
|
lint.build("outlives requirements can be inferred")
|
|
|
|
.multipart_suggestion(
|
|
|
|
if bound_count == 1 {
|
|
|
|
"remove this bound"
|
|
|
|
} else {
|
|
|
|
"remove these bounds"
|
|
|
|
},
|
|
|
|
lint_spans
|
|
|
|
.into_iter()
|
|
|
|
.map(|span| (span, "".to_owned()))
|
|
|
|
.collect::<Vec<_>>(),
|
|
|
|
Applicability::MachineApplicable,
|
|
|
|
)
|
|
|
|
.emit();
|
|
|
|
});
|
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-07-29 02:00:53 +02:00
|
|
|
|
|
|
|
declare_lint! {
|
|
|
|
pub INCOMPLETE_FEATURES,
|
|
|
|
Warn,
|
|
|
|
"incomplete features that may function improperly in some or all cases"
|
|
|
|
}
|
|
|
|
|
|
|
|
declare_lint_pass!(
|
2020-01-13 00:51:15 +09:00
|
|
|
/// Check for used feature gates in `INCOMPLETE_FEATURES` in `librustc_feature/active.rs`.
|
2019-07-29 02:00:53 +02:00
|
|
|
IncompleteFeatures => [INCOMPLETE_FEATURES]
|
|
|
|
);
|
|
|
|
|
|
|
|
impl EarlyLintPass for IncompleteFeatures {
|
|
|
|
fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) {
|
|
|
|
let features = cx.sess.features_untracked();
|
2019-12-22 17:42:04 -05:00
|
|
|
features
|
|
|
|
.declared_lang_features
|
|
|
|
.iter()
|
|
|
|
.map(|(name, span, _)| (name, span))
|
2019-07-29 02:00:53 +02:00
|
|
|
.chain(features.declared_lib_features.iter().map(|(name, span)| (name, span)))
|
2019-11-30 00:23:38 +01:00
|
|
|
.filter(|(name, _)| rustc_feature::INCOMPLETE_FEATURES.iter().any(|f| name == &f))
|
2020-05-09 14:04:32 +02:00
|
|
|
.for_each(|(&name, &span)| {
|
2020-02-02 09:47:58 +10:00
|
|
|
cx.struct_span_lint(INCOMPLETE_FEATURES, span, |lint| {
|
2020-05-09 14:04:32 +02:00
|
|
|
let mut builder = lint.build(&format!(
|
|
|
|
"the feature `{}` is incomplete and may not be safe to use \
|
|
|
|
and/or cause compiler crashes",
|
2019-07-29 02:00:53 +02:00
|
|
|
name,
|
2020-05-09 14:04:32 +02:00
|
|
|
));
|
|
|
|
if let Some(n) = rustc_feature::find_feature_issue(name, GateIssue::Language) {
|
|
|
|
builder.note(&format!(
|
|
|
|
"see issue #{} <https://github.com/rust-lang/rust/issues/{}> \
|
|
|
|
for more information",
|
|
|
|
n, n,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
builder.emit();
|
2020-02-02 09:47:58 +10:00
|
|
|
})
|
2019-07-29 02:00:53 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2019-08-06 23:11:52 +02:00
|
|
|
|
|
|
|
declare_lint! {
|
|
|
|
pub INVALID_VALUE,
|
|
|
|
Warn,
|
|
|
|
"an invalid value is being created (such as a NULL reference)"
|
|
|
|
}
|
|
|
|
|
|
|
|
declare_lint_pass!(InvalidValue => [INVALID_VALUE]);
|
|
|
|
|
2020-06-25 23:41:36 +03:00
|
|
|
impl<'tcx> LateLintPass<'tcx> for InvalidValue {
|
|
|
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) {
|
2019-08-17 09:39:25 +02:00
|
|
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
2019-12-22 17:42:04 -05:00
|
|
|
enum InitKind {
|
|
|
|
Zeroed,
|
|
|
|
Uninit,
|
|
|
|
};
|
2019-08-17 08:39:20 +02:00
|
|
|
|
2019-08-17 11:48:30 +02:00
|
|
|
/// Information about why a type cannot be initialized this way.
|
|
|
|
/// Contains an error message and optionally a span to point at.
|
|
|
|
type InitError = (String, Option<Span>);
|
|
|
|
|
|
|
|
/// Test if this constant is all-0.
|
2019-11-30 15:08:22 +01:00
|
|
|
fn is_zero(expr: &hir::Expr<'_>) -> bool {
|
2019-08-17 11:48:30 +02:00
|
|
|
use hir::ExprKind::*;
|
2020-04-27 23:26:11 +05:30
|
|
|
use rustc_ast::LitKind::*;
|
2019-09-26 14:39:48 +01:00
|
|
|
match &expr.kind {
|
2019-12-22 17:42:04 -05:00
|
|
|
Lit(lit) => {
|
2019-08-17 11:48:30 +02:00
|
|
|
if let Int(i, _) = lit.node {
|
|
|
|
i == 0
|
|
|
|
} else {
|
|
|
|
false
|
2019-12-22 17:42:04 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Tup(tup) => tup.iter().all(is_zero),
|
|
|
|
_ => false,
|
2019-08-17 11:48:30 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-17 08:39:20 +02:00
|
|
|
/// Determine if this expression is a "dangerous initialization".
|
2020-06-25 23:41:36 +03:00
|
|
|
fn is_dangerous_init(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<InitKind> {
|
2019-08-17 11:48:30 +02:00
|
|
|
// `transmute` is inside an anonymous module (the `extern` block?);
|
|
|
|
// `Invalid` represents the empty string and matches that.
|
2019-11-09 10:34:16 +01:00
|
|
|
// FIXME(#66075): use diagnostic items. Somehow, that does not seem to work
|
|
|
|
// on intrinsics right now.
|
2019-08-17 11:48:30 +02:00
|
|
|
const TRANSMUTE_PATH: &[Symbol] =
|
|
|
|
&[sym::core, sym::intrinsics, kw::Invalid, sym::transmute];
|
2019-08-17 08:39:20 +02:00
|
|
|
|
2019-09-26 14:39:48 +01:00
|
|
|
if let hir::ExprKind::Call(ref path_expr, ref args) = expr.kind {
|
2019-11-02 16:12:33 +01:00
|
|
|
// Find calls to `mem::{uninitialized,zeroed}` methods.
|
2019-09-26 14:39:48 +01:00
|
|
|
if let hir::ExprKind::Path(ref qpath) = path_expr.kind {
|
2020-06-26 05:55:23 +03:00
|
|
|
let def_id = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()?;
|
2019-08-17 13:49:27 +02:00
|
|
|
|
2019-11-09 10:34:16 +01:00
|
|
|
if cx.tcx.is_diagnostic_item(sym::mem_zeroed, def_id) {
|
2019-08-17 13:49:27 +02:00
|
|
|
return Some(InitKind::Zeroed);
|
2019-11-09 10:34:16 +01:00
|
|
|
} else if cx.tcx.is_diagnostic_item(sym::mem_uninitialized, def_id) {
|
2019-08-17 13:49:27 +02:00
|
|
|
return Some(InitKind::Uninit);
|
2019-11-09 10:34:16 +01:00
|
|
|
} else if cx.match_def_path(def_id, TRANSMUTE_PATH) {
|
2019-08-17 13:49:27 +02:00
|
|
|
if is_zero(&args[0]) {
|
2019-08-17 08:39:20 +02:00
|
|
|
return Some(InitKind::Zeroed);
|
|
|
|
}
|
|
|
|
}
|
2019-11-02 16:12:33 +01:00
|
|
|
}
|
2020-06-09 15:34:23 -04:00
|
|
|
} else if let hir::ExprKind::MethodCall(_, _, ref args, _) = expr.kind {
|
2019-11-02 16:12:33 +01:00
|
|
|
// Find problematic calls to `MaybeUninit::assume_init`.
|
2020-07-17 08:47:04 +00:00
|
|
|
let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?;
|
2019-11-04 10:11:58 +01:00
|
|
|
if cx.tcx.is_diagnostic_item(sym::assume_init, def_id) {
|
2019-11-02 16:12:33 +01:00
|
|
|
// This is a call to *some* method named `assume_init`.
|
|
|
|
// See if the `self` parameter is one of the dangerous constructors.
|
|
|
|
if let hir::ExprKind::Call(ref path_expr, _) = args[0].kind {
|
|
|
|
if let hir::ExprKind::Path(ref qpath) = path_expr.kind {
|
2020-06-26 05:55:23 +03:00
|
|
|
let def_id = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()?;
|
2019-11-09 10:34:16 +01:00
|
|
|
|
|
|
|
if cx.tcx.is_diagnostic_item(sym::maybe_uninit_zeroed, def_id) {
|
2019-11-02 16:12:33 +01:00
|
|
|
return Some(InitKind::Zeroed);
|
2019-11-09 10:34:16 +01:00
|
|
|
} else if cx.tcx.is_diagnostic_item(sym::maybe_uninit_uninit, def_id) {
|
2019-11-02 16:12:33 +01:00
|
|
|
return Some(InitKind::Uninit);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-08-17 08:39:20 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
|
|
|
}
|
2019-08-06 23:11:52 +02:00
|
|
|
|
2020-07-17 15:01:37 +02:00
|
|
|
/// Test if this enum has several actually "existing" variants.
|
|
|
|
/// Zero-sized uninhabited variants do not always have a tag assigned and thus do not "exist".
|
|
|
|
fn is_multi_variant(adt: &ty::AdtDef) -> bool {
|
|
|
|
// As an approximation, we only count dataless variants. Those are definitely inhabited.
|
|
|
|
let existing_variants = adt.variants.iter().filter(|v| v.fields.is_empty()).count();
|
|
|
|
existing_variants > 1
|
|
|
|
}
|
|
|
|
|
2019-08-12 09:24:13 +02:00
|
|
|
/// Return `Some` only if we are sure this type does *not*
|
2019-08-06 23:11:52 +02:00
|
|
|
/// allow zero initialization.
|
2019-08-17 09:39:25 +02:00
|
|
|
fn ty_find_init_error<'tcx>(
|
|
|
|
tcx: TyCtxt<'tcx>,
|
|
|
|
ty: Ty<'tcx>,
|
|
|
|
init: InitKind,
|
|
|
|
) -> Option<InitError> {
|
2020-03-29 16:41:09 +02:00
|
|
|
use rustc_middle::ty::TyKind::*;
|
2019-09-16 19:08:35 +01:00
|
|
|
match ty.kind {
|
2019-08-06 23:11:52 +02:00
|
|
|
// Primitive types that don't like 0 as a value.
|
2020-02-29 00:35:24 +01:00
|
|
|
Ref(..) => Some(("references must be non-null".to_string(), None)),
|
|
|
|
Adt(..) if ty.is_box() => Some(("`Box` must be non-null".to_string(), None)),
|
|
|
|
FnPtr(..) => Some(("function pointers must be non-null".to_string(), None)),
|
|
|
|
Never => Some(("the `!` type has no valid value".to_string(), None)),
|
2019-12-22 17:42:04 -05:00
|
|
|
RawPtr(tm) if matches!(tm.ty.kind, Dynamic(..)) =>
|
|
|
|
// raw ptr to dyn Trait
|
|
|
|
{
|
2020-02-29 00:35:24 +01:00
|
|
|
Some(("the vtable of a wide raw pointer must be non-null".to_string(), None))
|
2019-12-22 17:42:04 -05:00
|
|
|
}
|
2019-08-17 13:42:03 +02:00
|
|
|
// Primitive types with other constraints.
|
2019-12-22 17:42:04 -05:00
|
|
|
Bool if init == InitKind::Uninit => {
|
2020-02-29 00:35:24 +01:00
|
|
|
Some(("booleans must be either `true` or `false`".to_string(), None))
|
2019-12-22 17:42:04 -05:00
|
|
|
}
|
|
|
|
Char if init == InitKind::Uninit => {
|
2020-02-29 00:35:24 +01:00
|
|
|
Some(("characters must be a valid Unicode codepoint".to_string(), None))
|
2019-12-22 17:42:04 -05:00
|
|
|
}
|
2019-08-17 10:13:47 +02:00
|
|
|
// Recurse and checks for some compound types.
|
2019-08-07 08:42:50 +02:00
|
|
|
Adt(adt_def, substs) if !adt_def.is_union() => {
|
2020-07-17 15:01:37 +02:00
|
|
|
// First check if this ADT has a layout attribute (like `NonNull` and friends).
|
2019-08-17 10:13:47 +02:00
|
|
|
use std::ops::Bound;
|
|
|
|
match tcx.layout_scalar_valid_range(adt_def.did) {
|
|
|
|
// We exploit here that `layout_scalar_valid_range` will never
|
|
|
|
// return `Bound::Excluded`. (And we have tests checking that we
|
|
|
|
// handle the attribute correctly.)
|
2019-12-22 17:42:04 -05:00
|
|
|
(Bound::Included(lo), _) if lo > 0 => {
|
2020-01-10 14:57:36 +00:00
|
|
|
return Some((format!("`{}` must be non-null", ty), None));
|
2019-12-22 17:42:04 -05:00
|
|
|
}
|
2019-08-17 10:13:47 +02:00
|
|
|
(Bound::Included(_), _) | (_, Bound::Included(_))
|
2019-12-22 17:42:04 -05:00
|
|
|
if init == InitKind::Uninit =>
|
|
|
|
{
|
2019-08-17 10:13:47 +02:00
|
|
|
return Some((
|
2020-01-10 14:57:36 +00:00
|
|
|
format!(
|
|
|
|
"`{}` must be initialized inside its custom valid range",
|
|
|
|
ty,
|
|
|
|
),
|
2019-08-17 10:13:47 +02:00
|
|
|
None,
|
2019-12-22 17:42:04 -05:00
|
|
|
));
|
|
|
|
}
|
2019-08-17 10:13:47 +02:00
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
// Now, recurse.
|
2019-08-07 08:42:50 +02:00
|
|
|
match adt_def.variants.len() {
|
2020-02-29 00:35:24 +01:00
|
|
|
0 => Some(("enums with no variants have no valid value".to_string(), None)),
|
2019-08-07 08:42:50 +02:00
|
|
|
1 => {
|
|
|
|
// Struct, or enum with exactly one variant.
|
|
|
|
// Proceed recursively, check all fields.
|
|
|
|
let variant = &adt_def.variants[VariantIdx::from_u32(0)];
|
2019-08-12 09:24:13 +02:00
|
|
|
variant.fields.iter().find_map(|field| {
|
2019-12-22 17:42:04 -05:00
|
|
|
ty_find_init_error(tcx, field.ty(tcx, substs), init).map(
|
|
|
|
|(mut msg, span)| {
|
|
|
|
if span.is_none() {
|
|
|
|
// Point to this field, should be helpful for figuring
|
|
|
|
// out where the source of the error is.
|
|
|
|
let span = tcx.def_span(field.did);
|
|
|
|
write!(
|
|
|
|
&mut msg,
|
|
|
|
" (in this {} field)",
|
|
|
|
adt_def.descr()
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
(msg, Some(span))
|
|
|
|
} else {
|
|
|
|
// Just forward.
|
|
|
|
(msg, span)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
)
|
2019-08-07 08:42:50 +02:00
|
|
|
})
|
|
|
|
}
|
2020-07-17 15:01:37 +02:00
|
|
|
// Multi-variant enum.
|
|
|
|
_ => {
|
|
|
|
if init == InitKind::Uninit && is_multi_variant(adt_def) {
|
|
|
|
let span = tcx.def_span(adt_def.did);
|
|
|
|
Some((
|
|
|
|
"enums have to be initialized to a variant".to_string(),
|
|
|
|
Some(span),
|
|
|
|
))
|
|
|
|
} else {
|
|
|
|
// In principle, for zero-initialization we could figure out which variant corresponds
|
|
|
|
// to tag 0, and check that... but for now we just accept all zero-initializations.
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
2019-08-07 08:42:50 +02:00
|
|
|
}
|
|
|
|
}
|
2019-08-07 09:01:53 +02:00
|
|
|
Tuple(..) => {
|
2019-08-07 08:42:50 +02:00
|
|
|
// Proceed recursively, check all fields.
|
2019-08-17 09:39:25 +02:00
|
|
|
ty.tuple_fields().find_map(|field| ty_find_init_error(tcx, field, init))
|
2019-08-07 08:42:50 +02:00
|
|
|
}
|
2019-08-06 23:11:52 +02:00
|
|
|
// Conservative fallback.
|
2019-08-12 09:24:13 +02:00
|
|
|
_ => None,
|
2019-08-06 23:11:52 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-17 08:39:20 +02:00
|
|
|
if let Some(init) = is_dangerous_init(cx, expr) {
|
|
|
|
// This conjures an instance of a type out of nothing,
|
|
|
|
// using zeroed or uninitialized memory.
|
|
|
|
// We are extremely conservative with what we warn about.
|
2020-07-17 08:47:04 +00:00
|
|
|
let conjured_ty = cx.typeck_results().expr_ty(expr);
|
2020-09-03 14:33:55 +03:00
|
|
|
if let Some((msg, span)) =
|
|
|
|
with_no_trimmed_paths(|| ty_find_init_error(cx.tcx, conjured_ty, init))
|
|
|
|
{
|
2020-02-02 09:47:58 +10:00
|
|
|
cx.struct_span_lint(INVALID_VALUE, expr.span, |lint| {
|
|
|
|
let mut err = lint.build(&format!(
|
|
|
|
"the type `{}` does not permit {}",
|
|
|
|
conjured_ty,
|
|
|
|
match init {
|
|
|
|
InitKind::Zeroed => "zero-initialization",
|
|
|
|
InitKind::Uninit => "being left uninitialized",
|
|
|
|
},
|
|
|
|
));
|
|
|
|
err.span_label(expr.span, "this code causes undefined behavior when executed");
|
|
|
|
err.span_label(
|
|
|
|
expr.span,
|
|
|
|
"help: use `MaybeUninit<T>` instead, \
|
2020-01-31 22:24:57 +10:00
|
|
|
and only call `assume_init` after initialization is done",
|
2020-02-02 09:47:58 +10:00
|
|
|
);
|
|
|
|
if let Some(span) = span {
|
|
|
|
err.span_note(span, &msg);
|
|
|
|
} else {
|
|
|
|
err.note(&msg);
|
|
|
|
}
|
|
|
|
err.emit();
|
|
|
|
});
|
2019-08-06 23:11:52 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-06-19 18:14:54 +10:00
|
|
|
|
|
|
|
declare_lint! {
|
2020-06-28 10:11:04 +10:00
|
|
|
pub CLASHING_EXTERN_DECLARATIONS,
|
2020-06-19 18:14:54 +10:00
|
|
|
Warn,
|
|
|
|
"detects when an extern fn has been declared with the same name but different types"
|
|
|
|
}
|
|
|
|
|
2020-06-28 10:11:04 +10:00
|
|
|
pub struct ClashingExternDeclarations {
|
2020-06-19 18:14:54 +10:00
|
|
|
seen_decls: FxHashMap<Symbol, HirId>,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Differentiate between whether the name for an extern decl came from the link_name attribute or
|
|
|
|
/// just from declaration itself. This is important because we don't want to report clashes on
|
|
|
|
/// symbol name if they don't actually clash because one or the other links against a symbol with a
|
|
|
|
/// different name.
|
|
|
|
enum SymbolName {
|
|
|
|
/// The name of the symbol + the span of the annotation which introduced the link name.
|
|
|
|
Link(Symbol, Span),
|
|
|
|
/// No link name, so just the name of the symbol.
|
|
|
|
Normal(Symbol),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl SymbolName {
|
|
|
|
fn get_name(&self) -> Symbol {
|
|
|
|
match self {
|
|
|
|
SymbolName::Link(s, _) | SymbolName::Normal(s) => *s,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-28 10:11:04 +10:00
|
|
|
impl ClashingExternDeclarations {
|
2020-06-19 18:14:54 +10:00
|
|
|
crate fn new() -> Self {
|
2020-06-28 10:11:04 +10:00
|
|
|
ClashingExternDeclarations { seen_decls: FxHashMap::default() }
|
2020-06-19 18:14:54 +10:00
|
|
|
}
|
|
|
|
/// Insert a new foreign item into the seen set. If a symbol with the same name already exists
|
|
|
|
/// for the item, return its HirId without updating the set.
|
|
|
|
fn insert(&mut self, tcx: TyCtxt<'_>, fi: &hir::ForeignItem<'_>) -> Option<HirId> {
|
|
|
|
let hid = fi.hir_id;
|
|
|
|
|
|
|
|
let name =
|
|
|
|
&tcx.codegen_fn_attrs(tcx.hir().local_def_id(hid)).link_name.unwrap_or(fi.ident.name);
|
|
|
|
|
|
|
|
if self.seen_decls.contains_key(name) {
|
|
|
|
// Avoid updating the map with the new entry when we do find a collision. We want to
|
|
|
|
// make sure we're always pointing to the first definition as the previous declaration.
|
|
|
|
// This lets us avoid emitting "knock-on" diagnostics.
|
|
|
|
Some(*self.seen_decls.get(name).unwrap())
|
|
|
|
} else {
|
|
|
|
self.seen_decls.insert(*name, hid)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get the name of the symbol that's linked against for a given extern declaration. That is,
|
|
|
|
/// the name specified in a #[link_name = ...] attribute if one was specified, else, just the
|
|
|
|
/// symbol's name.
|
|
|
|
fn name_of_extern_decl(tcx: TyCtxt<'_>, fi: &hir::ForeignItem<'_>) -> SymbolName {
|
|
|
|
let did = tcx.hir().local_def_id(fi.hir_id);
|
|
|
|
if let Some((overridden_link_name, overridden_link_name_span)) =
|
|
|
|
tcx.codegen_fn_attrs(did).link_name.map(|overridden_link_name| {
|
|
|
|
// FIXME: Instead of searching through the attributes again to get span
|
|
|
|
// information, we could have codegen_fn_attrs also give span information back for
|
|
|
|
// where the attribute was defined. However, until this is found to be a
|
|
|
|
// bottleneck, this does just fine.
|
|
|
|
(
|
|
|
|
overridden_link_name,
|
|
|
|
tcx.get_attrs(did.to_def_id())
|
|
|
|
.iter()
|
2020-07-30 11:27:50 +10:00
|
|
|
.find(|at| tcx.sess.check_name(at, sym::link_name))
|
2020-06-19 18:14:54 +10:00
|
|
|
.unwrap()
|
|
|
|
.span,
|
|
|
|
)
|
|
|
|
})
|
|
|
|
{
|
|
|
|
SymbolName::Link(overridden_link_name, overridden_link_name_span)
|
|
|
|
} else {
|
|
|
|
SymbolName::Normal(fi.ident.name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Checks whether two types are structurally the same enough that the declarations shouldn't
|
|
|
|
/// clash. We need this so we don't emit a lint when two modules both declare an extern struct,
|
|
|
|
/// with the same members (as the declarations shouldn't clash).
|
2020-07-13 23:06:35 +10:00
|
|
|
fn structurally_same_type<'tcx>(
|
|
|
|
cx: &LateContext<'tcx>,
|
|
|
|
a: Ty<'tcx>,
|
|
|
|
b: Ty<'tcx>,
|
|
|
|
ckind: CItemKind,
|
|
|
|
) -> bool {
|
2020-08-15 15:05:18 +10:00
|
|
|
fn structurally_same_type_impl<'tcx>(
|
2020-08-16 11:01:14 +10:00
|
|
|
seen_types: &mut FxHashSet<(Ty<'tcx>, Ty<'tcx>)>,
|
2020-08-15 15:05:18 +10:00
|
|
|
cx: &LateContext<'tcx>,
|
|
|
|
a: Ty<'tcx>,
|
|
|
|
b: Ty<'tcx>,
|
|
|
|
ckind: CItemKind,
|
|
|
|
) -> bool {
|
|
|
|
debug!("structurally_same_type_impl(cx, a = {:?}, b = {:?})", a, b);
|
2020-08-25 02:24:05 +10:00
|
|
|
let tcx = cx.tcx;
|
|
|
|
|
|
|
|
// Given a transparent newtype, reach through and grab the inner
|
|
|
|
// type unless the newtype makes the type non-null.
|
|
|
|
let non_transparent_ty = |ty: Ty<'tcx>| -> Ty<'tcx> {
|
|
|
|
let mut ty = ty;
|
|
|
|
loop {
|
|
|
|
if let ty::Adt(def, substs) = ty.kind {
|
|
|
|
let is_transparent = def.subst(tcx, substs).repr.transparent();
|
2020-08-25 23:52:41 +10:00
|
|
|
let is_non_null = crate::types::nonnull_optimization_guaranteed(tcx, &def);
|
2020-08-25 02:24:05 +10:00
|
|
|
debug!(
|
2020-08-25 14:00:24 +10:00
|
|
|
"non_transparent_ty({:?}) -- type is transparent? {}, type is non-null? {}",
|
|
|
|
ty, is_transparent, is_non_null
|
2020-08-25 02:24:05 +10:00
|
|
|
);
|
2020-08-25 14:00:24 +10:00
|
|
|
if is_transparent && !is_non_null {
|
|
|
|
debug_assert!(def.variants.len() == 1);
|
|
|
|
let v = &def.variants[VariantIdx::new(0)];
|
2020-08-25 23:41:13 +10:00
|
|
|
ty = v
|
|
|
|
.transparent_newtype_field(tcx)
|
|
|
|
.expect(
|
|
|
|
"single-variant transparent structure with zero-sized field",
|
|
|
|
)
|
|
|
|
.ty(tcx, substs);
|
2020-08-25 02:24:05 +10:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
debug!("non_transparent_ty -> {:?}", ty);
|
|
|
|
return ty;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let a = non_transparent_ty(a);
|
|
|
|
let b = non_transparent_ty(b);
|
|
|
|
|
2020-08-16 17:14:09 +10:00
|
|
|
if !seen_types.insert((a, b)) {
|
2020-08-16 11:01:14 +10:00
|
|
|
// We've encountered a cycle. There's no point going any further -- the types are
|
|
|
|
// structurally the same.
|
|
|
|
return true;
|
2020-08-16 10:50:20 +10:00
|
|
|
}
|
|
|
|
let tcx = cx.tcx;
|
2020-08-16 11:01:14 +10:00
|
|
|
if a == b || rustc_middle::ty::TyS::same_type(a, b) {
|
2020-08-16 10:50:20 +10:00
|
|
|
// All nominally-same types are structurally same, too.
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
// Do a full, depth-first comparison between the two.
|
|
|
|
use rustc_middle::ty::TyKind::*;
|
|
|
|
let a_kind = &a.kind;
|
|
|
|
let b_kind = &b.kind;
|
|
|
|
|
2020-08-26 23:32:57 +10:00
|
|
|
let compare_layouts = |a, b| -> Result<bool, LayoutError<'tcx>> {
|
|
|
|
debug!("compare_layouts({:?}, {:?})", a, b);
|
|
|
|
let a_layout = &cx.layout_of(a)?.layout.abi;
|
|
|
|
let b_layout = &cx.layout_of(b)?.layout.abi;
|
|
|
|
debug!(
|
|
|
|
"comparing layouts: {:?} == {:?} = {}",
|
|
|
|
a_layout,
|
|
|
|
b_layout,
|
|
|
|
a_layout == b_layout
|
|
|
|
);
|
|
|
|
Ok(a_layout == b_layout)
|
2020-08-16 10:50:20 +10:00
|
|
|
};
|
|
|
|
|
|
|
|
#[allow(rustc::usage_of_ty_tykind)]
|
2020-08-16 18:04:14 +10:00
|
|
|
let is_primitive_or_pointer = |kind: &ty::TyKind<'_>| {
|
|
|
|
kind.is_primitive() || matches!(kind, RawPtr(..) | Ref(..))
|
|
|
|
};
|
2020-07-03 20:14:05 +10:00
|
|
|
|
2020-08-18 02:00:35 +10:00
|
|
|
ensure_sufficient_stack(|| {
|
|
|
|
match (a_kind, b_kind) {
|
|
|
|
(Adt(a_def, a_substs), Adt(b_def, b_substs)) => {
|
|
|
|
let a = a.subst(cx.tcx, a_substs);
|
|
|
|
let b = b.subst(cx.tcx, b_substs);
|
|
|
|
debug!("Comparing {:?} and {:?}", a, b);
|
2020-08-16 10:50:20 +10:00
|
|
|
|
2020-08-26 23:32:57 +10:00
|
|
|
// We can immediately rule out these types as structurally same if
|
|
|
|
// their layouts differ.
|
|
|
|
match compare_layouts(a, b) {
|
|
|
|
Ok(false) => return false,
|
|
|
|
_ => (), // otherwise, continue onto the full, fields comparison
|
|
|
|
}
|
|
|
|
|
2020-08-18 02:00:35 +10:00
|
|
|
// Grab a flattened representation of all fields.
|
|
|
|
let a_fields = a_def.variants.iter().flat_map(|v| v.fields.iter());
|
|
|
|
let b_fields = b_def.variants.iter().flat_map(|v| v.fields.iter());
|
2020-08-26 23:32:57 +10:00
|
|
|
|
|
|
|
// Perform a structural comparison for each field.
|
|
|
|
a_fields.eq_by(
|
2020-07-07 21:08:14 +10:00
|
|
|
b_fields,
|
|
|
|
|&ty::FieldDef { did: a_did, .. },
|
|
|
|
&ty::FieldDef { did: b_did, .. }| {
|
2020-08-15 15:05:18 +10:00
|
|
|
structurally_same_type_impl(
|
|
|
|
seen_types,
|
2020-07-07 21:08:14 +10:00
|
|
|
cx,
|
|
|
|
tcx.type_of(a_did),
|
|
|
|
tcx.type_of(b_did),
|
|
|
|
ckind,
|
|
|
|
)
|
|
|
|
},
|
|
|
|
)
|
2020-08-18 02:00:35 +10:00
|
|
|
}
|
|
|
|
(Array(a_ty, a_const), Array(b_ty, b_const)) => {
|
|
|
|
// For arrays, we also check the constness of the type.
|
|
|
|
a_const.val == b_const.val
|
|
|
|
&& structurally_same_type_impl(seen_types, cx, a_ty, b_ty, ckind)
|
|
|
|
}
|
|
|
|
(Slice(a_ty), Slice(b_ty)) => {
|
2020-08-16 10:50:20 +10:00
|
|
|
structurally_same_type_impl(seen_types, cx, a_ty, b_ty, ckind)
|
2020-08-15 15:05:18 +10:00
|
|
|
}
|
2020-08-18 02:00:35 +10:00
|
|
|
(RawPtr(a_tymut), RawPtr(b_tymut)) => {
|
|
|
|
a_tymut.mutbl == b_tymut.mutbl
|
|
|
|
&& structurally_same_type_impl(
|
|
|
|
seen_types,
|
|
|
|
cx,
|
|
|
|
&a_tymut.ty,
|
|
|
|
&b_tymut.ty,
|
|
|
|
ckind,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
(Ref(_a_region, a_ty, a_mut), Ref(_b_region, b_ty, b_mut)) => {
|
|
|
|
// For structural sameness, we don't need the region to be same.
|
|
|
|
a_mut == b_mut
|
|
|
|
&& structurally_same_type_impl(seen_types, cx, a_ty, b_ty, ckind)
|
|
|
|
}
|
|
|
|
(FnDef(..), FnDef(..)) => {
|
|
|
|
let a_poly_sig = a.fn_sig(tcx);
|
|
|
|
let b_poly_sig = b.fn_sig(tcx);
|
|
|
|
|
|
|
|
// As we don't compare regions, skip_binder is fine.
|
|
|
|
let a_sig = a_poly_sig.skip_binder();
|
|
|
|
let b_sig = b_poly_sig.skip_binder();
|
|
|
|
|
|
|
|
(a_sig.abi, a_sig.unsafety, a_sig.c_variadic)
|
|
|
|
== (b_sig.abi, b_sig.unsafety, b_sig.c_variadic)
|
|
|
|
&& a_sig.inputs().iter().eq_by(b_sig.inputs().iter(), |a, b| {
|
|
|
|
structurally_same_type_impl(seen_types, cx, a, b, ckind)
|
|
|
|
})
|
|
|
|
&& structurally_same_type_impl(
|
|
|
|
seen_types,
|
|
|
|
cx,
|
|
|
|
a_sig.output(),
|
|
|
|
b_sig.output(),
|
|
|
|
ckind,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
(Tuple(a_substs), Tuple(b_substs)) => {
|
|
|
|
a_substs.types().eq_by(b_substs.types(), |a_ty, b_ty| {
|
|
|
|
structurally_same_type_impl(seen_types, cx, a_ty, b_ty, ckind)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
// For these, it's not quite as easy to define structural-sameness quite so easily.
|
|
|
|
// For the purposes of this lint, take the conservative approach and mark them as
|
|
|
|
// not structurally same.
|
|
|
|
(Dynamic(..), Dynamic(..))
|
|
|
|
| (Error(..), Error(..))
|
|
|
|
| (Closure(..), Closure(..))
|
|
|
|
| (Generator(..), Generator(..))
|
|
|
|
| (GeneratorWitness(..), GeneratorWitness(..))
|
|
|
|
| (Projection(..), Projection(..))
|
|
|
|
| (Opaque(..), Opaque(..)) => false,
|
|
|
|
|
|
|
|
// These definitely should have been caught above.
|
|
|
|
(Bool, Bool) | (Char, Char) | (Never, Never) | (Str, Str) => unreachable!(),
|
|
|
|
|
|
|
|
// An Adt and a primitive or pointer type. This can be FFI-safe if non-null
|
|
|
|
// enum layout optimisation is being applied.
|
|
|
|
(Adt(..), other_kind) | (other_kind, Adt(..))
|
|
|
|
if is_primitive_or_pointer(other_kind) =>
|
|
|
|
{
|
|
|
|
let (primitive, adt) =
|
|
|
|
if is_primitive_or_pointer(&a.kind) { (a, b) } else { (b, a) };
|
|
|
|
if let Some(ty) = crate::types::repr_nullable_ptr(cx, adt, ckind) {
|
|
|
|
ty == primitive
|
|
|
|
} else {
|
2020-08-26 23:32:57 +10:00
|
|
|
compare_layouts(a, b).unwrap_or(false)
|
2020-08-18 02:00:35 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// Otherwise, just compare the layouts. This may fail to lint for some
|
|
|
|
// incompatible types, but at the very least, will stop reads into
|
|
|
|
// uninitialised memory.
|
2020-08-26 23:32:57 +10:00
|
|
|
_ => compare_layouts(a, b).unwrap_or(false),
|
2020-07-03 20:14:05 +10:00
|
|
|
}
|
2020-08-18 02:00:35 +10:00
|
|
|
})
|
2020-06-19 18:14:54 +10:00
|
|
|
}
|
|
|
|
}
|
2020-08-16 11:01:14 +10:00
|
|
|
let mut seen_types = FxHashSet::default();
|
2020-08-15 15:05:18 +10:00
|
|
|
structurally_same_type_impl(&mut seen_types, cx, a, b, ckind)
|
2020-06-19 18:14:54 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-28 10:11:04 +10:00
|
|
|
impl_lint_pass!(ClashingExternDeclarations => [CLASHING_EXTERN_DECLARATIONS]);
|
2020-06-19 18:14:54 +10:00
|
|
|
|
2020-06-25 23:41:36 +03:00
|
|
|
impl<'tcx> LateLintPass<'tcx> for ClashingExternDeclarations {
|
|
|
|
fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, this_fi: &hir::ForeignItem<'_>) {
|
2020-06-28 10:11:04 +10:00
|
|
|
trace!("ClashingExternDeclarations: check_foreign_item: {:?}", this_fi);
|
2020-06-19 18:14:54 +10:00
|
|
|
if let ForeignItemKind::Fn(..) = this_fi.kind {
|
|
|
|
let tcx = *&cx.tcx;
|
|
|
|
if let Some(existing_hid) = self.insert(tcx, this_fi) {
|
|
|
|
let existing_decl_ty = tcx.type_of(tcx.hir().local_def_id(existing_hid));
|
|
|
|
let this_decl_ty = tcx.type_of(tcx.hir().local_def_id(this_fi.hir_id));
|
|
|
|
debug!(
|
2020-06-28 10:11:04 +10:00
|
|
|
"ClashingExternDeclarations: Comparing existing {:?}: {:?} to this {:?}: {:?}",
|
2020-06-19 18:14:54 +10:00
|
|
|
existing_hid, existing_decl_ty, this_fi.hir_id, this_decl_ty
|
|
|
|
);
|
|
|
|
// Check that the declarations match.
|
2020-07-13 23:06:35 +10:00
|
|
|
if !Self::structurally_same_type(
|
|
|
|
cx,
|
|
|
|
existing_decl_ty,
|
|
|
|
this_decl_ty,
|
|
|
|
CItemKind::Declaration,
|
|
|
|
) {
|
2020-06-19 18:14:54 +10:00
|
|
|
let orig_fi = tcx.hir().expect_foreign_item(existing_hid);
|
|
|
|
let orig = Self::name_of_extern_decl(tcx, orig_fi);
|
|
|
|
|
|
|
|
// We want to ensure that we use spans for both decls that include where the
|
|
|
|
// name was defined, whether that was from the link_name attribute or not.
|
|
|
|
let get_relevant_span =
|
|
|
|
|fi: &hir::ForeignItem<'_>| match Self::name_of_extern_decl(tcx, fi) {
|
|
|
|
SymbolName::Normal(_) => fi.span,
|
|
|
|
SymbolName::Link(_, annot_span) => fi.span.to(annot_span),
|
|
|
|
};
|
|
|
|
// Finally, emit the diagnostic.
|
|
|
|
tcx.struct_span_lint_hir(
|
2020-06-28 10:11:04 +10:00
|
|
|
CLASHING_EXTERN_DECLARATIONS,
|
2020-06-19 18:14:54 +10:00
|
|
|
this_fi.hir_id,
|
|
|
|
get_relevant_span(this_fi),
|
|
|
|
|lint| {
|
|
|
|
let mut expected_str = DiagnosticStyledString::new();
|
|
|
|
expected_str.push(existing_decl_ty.fn_sig(tcx).to_string(), false);
|
|
|
|
let mut found_str = DiagnosticStyledString::new();
|
|
|
|
found_str.push(this_decl_ty.fn_sig(tcx).to_string(), true);
|
|
|
|
|
|
|
|
lint.build(&format!(
|
|
|
|
"`{}` redeclare{} with a different signature",
|
|
|
|
this_fi.ident.name,
|
|
|
|
if orig.get_name() == this_fi.ident.name {
|
|
|
|
"d".to_string()
|
|
|
|
} else {
|
|
|
|
format!("s `{}`", orig.get_name())
|
|
|
|
}
|
|
|
|
))
|
|
|
|
.span_label(
|
|
|
|
get_relevant_span(orig_fi),
|
|
|
|
&format!("`{}` previously declared here", orig.get_name()),
|
|
|
|
)
|
|
|
|
.span_label(
|
|
|
|
get_relevant_span(this_fi),
|
|
|
|
"this signature doesn't match the previous declaration",
|
|
|
|
)
|
|
|
|
.note_expected_found(&"", expected_str, &"", found_str)
|
|
|
|
.emit()
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|