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::{
|
2020-08-03 11:08:03 +01:00
|
|
|
types::{transparent_newtype_field, CItemKind},
|
|
|
|
EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext,
|
2020-07-13 23:06:35 +10:00
|
|
|
};
|
2021-02-23 10:21:20 -05:00
|
|
|
use rustc_ast::attr;
|
2020-02-29 20:37:32 +03:00
|
|
|
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;
|
2022-03-24 02:03:04 +00:00
|
|
|
use rustc_errors::{Applicability, Diagnostic, DiagnosticStyledString, MultiSpan};
|
2021-11-12 20:15:14 +08:00
|
|
|
use rustc_feature::{deprecated_attributes, AttributeGate, BuiltinAttribute, GateIssue, Stability};
|
2020-01-05 02:37:57 +01:00
|
|
|
use rustc_hir as hir;
|
|
|
|
use rustc_hir::def::{DefKind, Res};
|
2021-07-28 18:23:40 +03:00
|
|
|
use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdSet, CRATE_DEF_ID};
|
2020-06-19 18:14:54 +10:00
|
|
|
use rustc_hir::{ForeignItemKind, GenericParamKind, PatKind};
|
2021-01-30 17:47:51 +01:00
|
|
|
use rustc_hir::{HirId, 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;
|
2021-08-30 17:38:27 +03:00
|
|
|
use rustc_middle::ty::layout::{LayoutError, LayoutOf};
|
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-12-13 23:02:32 +10:00
|
|
|
use rustc_middle::ty::Instance;
|
2021-08-30 17:38:27 +03:00
|
|
|
use rustc_middle::ty::{self, Ty, TyCtxt};
|
2021-08-19 16:34:01 -04:00
|
|
|
use rustc_session::lint::{BuiltinLintDiagnostics, FutureIncompatibilityReason};
|
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};
|
2022-03-24 02:03:04 +00:00
|
|
|
use rustc_span::{BytePos, InnerSpan, Span};
|
2021-08-30 17:38:27 +03:00
|
|
|
use rustc_target::abi::VariantIdx;
|
2022-02-06 13:03:28 -08:00
|
|
|
use rustc_trait_selection::traits::{self, 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! {
|
2020-09-08 15:09:57 -07:00
|
|
|
/// The `while_true` lint detects `while true { }`.
|
|
|
|
///
|
|
|
|
/// ### Example
|
|
|
|
///
|
|
|
|
/// ```rust,no_run
|
|
|
|
/// while true {
|
|
|
|
///
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// {{produces}}
|
|
|
|
///
|
|
|
|
/// ### Explanation
|
|
|
|
///
|
|
|
|
/// `while true` should be replaced with `loop`. A `loop` expression is
|
|
|
|
/// the preferred way to write an infinite loop because it more directly
|
|
|
|
/// expresses the intent of the loop.
|
2015-02-25 22:44:44 +11:00
|
|
|
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) {
|
2021-01-19 15:51:51 -08:00
|
|
|
if let ast::ExprKind::While(cond, _, label) = &e.kind {
|
2019-09-26 14:39:48 +01:00
|
|
|
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 { ... }`";
|
2021-01-19 15:51:51 -08:00
|
|
|
let condition_span = e.span.with_hi(cond.span.hi());
|
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`",
|
2021-01-19 15:51:51 -08:00
|
|
|
format!(
|
|
|
|
"{}loop",
|
|
|
|
label.map_or_else(String::new, |label| format!(
|
|
|
|
"{}: ",
|
|
|
|
label.ident,
|
|
|
|
))
|
|
|
|
),
|
2020-02-02 09:47:58 +10:00
|
|
|
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! {
|
2020-09-08 15:09:57 -07:00
|
|
|
/// The `box_pointers` lints use of the Box type.
|
|
|
|
///
|
|
|
|
/// ### Example
|
|
|
|
///
|
|
|
|
/// ```rust,compile_fail
|
|
|
|
/// #![deny(box_pointers)]
|
|
|
|
/// struct Foo {
|
|
|
|
/// x: Box<isize>,
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// {{produces}}
|
|
|
|
///
|
|
|
|
/// ### Explanation
|
|
|
|
///
|
|
|
|
/// This lint is mostly historical, and not particularly useful. `Box<T>`
|
|
|
|
/// used to be built into the language, and the only way to do heap
|
|
|
|
/// allocation. Today's Rust can call into other allocators, etc.
|
2015-02-25 22:44:44 +11:00
|
|
|
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 {
|
2022-01-12 03:19:52 +00:00
|
|
|
fn check_heap_type(&self, cx: &LateContext<'_>, span: Span, ty: Ty<'_>) {
|
|
|
|
for leaf in ty.walk() {
|
2020-03-23 03:57:04 +02:00
|
|
|
if let GenericArgKind::Type(leaf_ty) = leaf.unpack() {
|
|
|
|
if leaf_ty.is_box() {
|
|
|
|
cx.struct_span_lint(BOX_POINTERS, span, |lint| {
|
2022-01-22 18:49:12 -06:00
|
|
|
lint.build(&format!("type uses owned (Box type) pointers: {}", ty)).emit();
|
2020-03-23 03:57:04 +02:00
|
|
|
});
|
|
|
|
}
|
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(..) => {
|
2021-01-30 17:47:51 +01:00
|
|
|
self.check_heap_type(cx, it.span, cx.tcx.type_of(it.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! {
|
2020-09-08 15:09:57 -07:00
|
|
|
/// The `non_shorthand_field_patterns` lint detects using `Struct { x: x }`
|
|
|
|
/// instead of `Struct { x }` in a pattern.
|
|
|
|
///
|
|
|
|
/// ### Example
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// struct Point {
|
|
|
|
/// x: i32,
|
|
|
|
/// y: i32,
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// fn main() {
|
|
|
|
/// let p = Point {
|
|
|
|
/// x: 5,
|
|
|
|
/// y: 5,
|
|
|
|
/// };
|
|
|
|
///
|
|
|
|
/// match p {
|
|
|
|
/// Point { x: x, y: y } => (),
|
|
|
|
/// }
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// {{produces}}
|
|
|
|
///
|
|
|
|
/// ### Explanation
|
|
|
|
///
|
|
|
|
/// The preferred style is to avoid the repetition of specifying both the
|
|
|
|
/// field name and the binding name if both identifiers are the same.
|
2015-02-25 22:44:44 +11:00
|
|
|
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! {
|
2020-09-08 15:09:57 -07:00
|
|
|
/// The `unsafe_code` lint catches usage of `unsafe` code.
|
|
|
|
///
|
|
|
|
/// ### Example
|
|
|
|
///
|
|
|
|
/// ```rust,compile_fail
|
|
|
|
/// #![deny(unsafe_code)]
|
|
|
|
/// fn main() {
|
|
|
|
/// unsafe {
|
|
|
|
///
|
|
|
|
/// }
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// {{produces}}
|
|
|
|
///
|
|
|
|
/// ### Explanation
|
|
|
|
///
|
|
|
|
/// This lint is intended to restrict the usage of `unsafe`, which can be
|
|
|
|
/// difficult to use correctly.
|
2015-02-25 22:44:44 +11:00
|
|
|
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,
|
2022-01-22 18:49:12 -06:00
|
|
|
decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a, ()>),
|
2020-02-03 19:57:45 +10:00
|
|
|
) {
|
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
|
|
|
}
|
2020-06-20 13:34:22 +02:00
|
|
|
|
2022-03-15 02:00:08 +01:00
|
|
|
fn report_overridden_symbol_name(&self, cx: &EarlyContext<'_>, span: Span, msg: &str) {
|
2020-06-20 13:34:22 +02:00
|
|
|
self.report_unsafe(cx, span, |lint| {
|
|
|
|
lint.build(msg)
|
|
|
|
.note(
|
|
|
|
"the linker's behavior with multiple libraries exporting duplicate symbol \
|
|
|
|
names is undefined and Rust cannot provide guarantees when you manually \
|
|
|
|
override them",
|
|
|
|
)
|
|
|
|
.emit();
|
|
|
|
})
|
|
|
|
}
|
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) {
|
2021-07-29 12:00:41 -05:00
|
|
|
if attr.has_name(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",
|
|
|
|
)
|
2022-01-22 18:49:12 -06:00
|
|
|
.emit();
|
2020-02-03 19:57:45 +10:00
|
|
|
});
|
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| {
|
2022-01-22 18:49:12 -06:00
|
|
|
lint.build("usage of an `unsafe` block").emit();
|
2020-02-03 19:57:45 +10:00
|
|
|
});
|
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 {
|
2021-11-07 16:43:49 +08:00
|
|
|
ast::ItemKind::Trait(box ast::Trait { unsafety: ast::Unsafe::Yes(_), .. }) => self
|
2021-01-29 08:31:08 +01:00
|
|
|
.report_unsafe(cx, it.span, |lint| {
|
2022-01-22 18:49:12 -06:00
|
|
|
lint.build("declaration of an `unsafe` trait").emit();
|
2021-01-29 08:31:08 +01:00
|
|
|
}),
|
2015-02-25 22:44:44 +11:00
|
|
|
|
2021-11-07 16:43:49 +08:00
|
|
|
ast::ItemKind::Impl(box ast::Impl { unsafety: ast::Unsafe::Yes(_), .. }) => self
|
2021-01-29 08:31:08 +01:00
|
|
|
.report_unsafe(cx, it.span, |lint| {
|
2022-01-22 18:49:12 -06:00
|
|
|
lint.build("implementation of an `unsafe` trait").emit();
|
2021-01-29 08:31:08 +01:00
|
|
|
}),
|
2015-02-25 22:44:44 +11:00
|
|
|
|
2020-05-14 17:31:06 +02:00
|
|
|
ast::ItemKind::Fn(..) => {
|
2020-08-30 21:51:30 +02:00
|
|
|
if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::no_mangle) {
|
2022-03-15 02:00:08 +01:00
|
|
|
self.report_overridden_symbol_name(
|
2020-06-20 13:34:22 +02:00
|
|
|
cx,
|
2020-08-04 13:02:17 +02:00
|
|
|
attr.span,
|
2020-06-20 13:34:22 +02:00
|
|
|
"declaration of a `no_mangle` function",
|
|
|
|
);
|
2020-05-14 17:31:06 +02:00
|
|
|
}
|
2020-08-30 21:51:30 +02:00
|
|
|
if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::export_name) {
|
2022-03-15 02:00:08 +01:00
|
|
|
self.report_overridden_symbol_name(
|
2020-06-20 13:34:22 +02:00
|
|
|
cx,
|
2020-08-04 13:02:17 +02:00
|
|
|
attr.span,
|
2020-06-20 13:34:22 +02:00
|
|
|
"declaration of a function with `export_name`",
|
|
|
|
);
|
2020-05-15 17:36:19 +02:00
|
|
|
}
|
2020-05-14 17:31:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
ast::ItemKind::Static(..) => {
|
2020-08-30 21:51:30 +02:00
|
|
|
if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::no_mangle) {
|
2022-03-15 02:00:08 +01:00
|
|
|
self.report_overridden_symbol_name(
|
2020-06-20 13:34:22 +02:00
|
|
|
cx,
|
2020-08-04 13:02:17 +02:00
|
|
|
attr.span,
|
2020-06-20 13:34:22 +02:00
|
|
|
"declaration of a `no_mangle` static",
|
|
|
|
);
|
2020-05-14 17:31:06 +02:00
|
|
|
}
|
2020-08-30 21:51:30 +02:00
|
|
|
if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::export_name) {
|
2022-03-15 02:00:08 +01:00
|
|
|
self.report_overridden_symbol_name(
|
2020-06-20 13:34:22 +02:00
|
|
|
cx,
|
2020-08-04 13:02:17 +02:00
|
|
|
attr.span,
|
2020-06-20 13:34:22 +02:00
|
|
|
"declaration of a static with `export_name`",
|
|
|
|
);
|
2020-05-15 17:36:19 +02:00
|
|
|
}
|
2020-05-14 17:31:06 +02:00
|
|
|
}
|
|
|
|
|
2020-03-20 15:03:11 +01:00
|
|
|
_ => {}
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-08 02:10:57 +08:00
|
|
|
fn check_impl_item(&mut self, cx: &EarlyContext<'_>, it: &ast::AssocItem) {
|
|
|
|
if let ast::AssocItemKind::Fn(..) = it.kind {
|
|
|
|
if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::no_mangle) {
|
2022-03-15 02:00:08 +01:00
|
|
|
self.report_overridden_symbol_name(
|
2021-08-08 02:10:57 +08:00
|
|
|
cx,
|
|
|
|
attr.span,
|
|
|
|
"declaration of a `no_mangle` method",
|
|
|
|
);
|
|
|
|
}
|
|
|
|
if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::export_name) {
|
2022-03-15 02:00:08 +01:00
|
|
|
self.report_overridden_symbol_name(
|
2021-08-08 02:10:57 +08:00
|
|
|
cx,
|
|
|
|
attr.span,
|
|
|
|
"declaration of a method with `export_name`",
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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",
|
|
|
|
};
|
2022-01-22 18:49:12 -06:00
|
|
|
self.report_unsafe(cx, span, |lint| {
|
|
|
|
lint.build(msg).emit();
|
|
|
|
});
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
declare_lint! {
|
2020-09-08 15:09:57 -07:00
|
|
|
/// The `missing_docs` lint detects missing documentation for public items.
|
|
|
|
///
|
|
|
|
/// ### Example
|
|
|
|
///
|
|
|
|
/// ```rust,compile_fail
|
|
|
|
/// #![deny(missing_docs)]
|
|
|
|
/// pub fn foo() {}
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// {{produces}}
|
|
|
|
///
|
|
|
|
/// ### Explanation
|
|
|
|
///
|
|
|
|
/// This lint is intended to ensure that a library is well-documented.
|
|
|
|
/// Items without documentation can be difficult for users to understand
|
|
|
|
/// how to use properly.
|
|
|
|
///
|
|
|
|
/// This lint is "allow" by default because it can be noisy, and not all
|
|
|
|
/// projects may want to enforce everything to be documented.
|
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]);
|
|
|
|
|
2021-07-29 12:00:41 -05:00
|
|
|
fn has_doc(attr: &ast::Attribute) -> bool {
|
2019-12-07 21:28:29 +03:00
|
|
|
if attr.is_doc_comment() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-07-29 12:00:41 -05:00
|
|
|
if !attr.has_name(sym::doc) {
|
2018-12-01 14:46:25 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-03-16 01:50:34 -04:00
|
|
|
if attr.value_str().is_some() {
|
2018-12-01 14:46:25 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(list) = attr.meta_item_list() {
|
|
|
|
for meta in list {
|
2021-05-18 21:46:41 -04:00
|
|
|
if 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<'_>,
|
2021-07-28 18:23:40 +03:00
|
|
|
def_id: LocalDefId,
|
2019-12-22 17:42:04 -05:00
|
|
|
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`).
|
2021-07-28 18:23:40 +03:00
|
|
|
if def_id != CRATE_DEF_ID {
|
|
|
|
if !cx.access_levels.is_exported(def_id) {
|
2015-02-25 22:44:44 +11:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-28 18:23:40 +03:00
|
|
|
let attrs = cx.tcx.get_attrs(def_id.to_def_id());
|
2021-07-29 12:00:41 -05:00
|
|
|
let has_doc = attrs.iter().any(has_doc);
|
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| {
|
2022-01-22 18:49:12 -06:00
|
|
|
lint.build(&format!("missing documentation for {} {}", article, desc)).emit();
|
2020-03-09 11:42:37 -07:00
|
|
|
},
|
|
|
|
);
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-25 23:41:36 +03:00
|
|
|
impl<'tcx> LateLintPass<'tcx> for MissingDoc {
|
2021-07-29 12:00:41 -05: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| {
|
2021-07-29 12:00:41 -05:00
|
|
|
attr.has_name(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");
|
|
|
|
}
|
|
|
|
|
2021-09-12 11:58:27 +02:00
|
|
|
fn check_crate(&mut self, cx: &LateContext<'_>) {
|
2021-09-12 01:11:22 +02:00
|
|
|
self.check_missing_docs_attrs(
|
|
|
|
cx,
|
|
|
|
CRATE_DEF_ID,
|
|
|
|
cx.tcx.def_span(CRATE_DEF_ID),
|
|
|
|
"the",
|
|
|
|
"crate",
|
|
|
|
);
|
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 {
|
2021-01-30 17:47:51 +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 {
|
2021-01-30 20:46:50 +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-11-22 17:46:21 -05:00
|
|
|
hir::ItemKind::Impl(hir::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();
|
2021-12-03 03:25:11 +01:00
|
|
|
let Some(def_id) = real_trait.as_local() else { return };
|
2021-10-20 20:59:15 +02:00
|
|
|
let Some(Node::Item(item)) = cx.tcx.hir().find_by_def_id(def_id) else { return };
|
2021-12-03 03:25:11 +01:00
|
|
|
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
|
|
|
}
|
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(..)
|
2021-07-30 23:50:57 -07:00
|
|
|
| hir::ItemKind::Macro(..)
|
2020-03-15 21:48:09 -05:00
|
|
|
| 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
|
|
|
|
2021-01-30 17:47:51 +01:00
|
|
|
let (article, desc) = cx.tcx.article_and_description(it.def_id.to_def_id());
|
2020-03-15 21:48:09 -05:00
|
|
|
|
2021-07-28 18:23:40 +03:00
|
|
|
self.check_missing_docs_attrs(cx, it.def_id, 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<'_>) {
|
2021-01-30 20:46:50 +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
|
|
|
|
2021-01-30 20:46:50 +01:00
|
|
|
let (article, desc) = cx.tcx.article_and_description(trait_item.def_id.to_def_id());
|
2015-03-29 23:41:54 -04:00
|
|
|
|
2021-07-28 18:23:40 +03:00
|
|
|
self.check_missing_docs_attrs(cx, trait_item.def_id, trait_item.span, article, desc);
|
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.
|
2021-01-30 23:25:03 +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
|
|
|
|
2021-10-17 14:31:52 -05:00
|
|
|
// If the method is an impl for an item with docs_hidden, don't doc.
|
|
|
|
if method_context(cx, impl_item.hir_id()) == MethodLateContext::PlainImpl {
|
2021-10-21 19:41:47 +02:00
|
|
|
let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id());
|
2021-10-17 14:31:52 -05:00
|
|
|
let impl_ty = cx.tcx.type_of(parent);
|
|
|
|
let outerdef = match impl_ty.kind() {
|
2022-03-05 07:28:41 +11:00
|
|
|
ty::Adt(def, _) => Some(def.did()),
|
2021-10-17 14:31:52 -05:00
|
|
|
ty::Foreign(def_id) => Some(*def_id),
|
|
|
|
_ => None,
|
|
|
|
};
|
|
|
|
let is_hidden = match outerdef {
|
|
|
|
Some(id) => cx.tcx.is_doc_hidden(id),
|
|
|
|
None => false,
|
|
|
|
};
|
|
|
|
if is_hidden {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-30 23:25:03 +01:00
|
|
|
let (article, desc) = cx.tcx.article_and_description(impl_item.def_id.to_def_id());
|
2021-07-28 18:23:40 +03:00
|
|
|
self.check_missing_docs_attrs(cx, impl_item.def_id, impl_item.span, article, desc);
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
|
2020-09-21 23:48:39 +02:00
|
|
|
fn check_foreign_item(&mut self, cx: &LateContext<'_>, foreign_item: &hir::ForeignItem<'_>) {
|
2021-02-01 00:33:38 +01:00
|
|
|
let (article, desc) = cx.tcx.article_and_description(foreign_item.def_id.to_def_id());
|
2021-07-28 18:23:40 +03:00
|
|
|
self.check_missing_docs_attrs(cx, foreign_item.def_id, foreign_item.span, article, desc);
|
2020-09-21 23:48:39 +02:00
|
|
|
}
|
|
|
|
|
2021-03-16 00:36:07 +03:00
|
|
|
fn check_field_def(&mut self, cx: &LateContext<'_>, sf: &hir::FieldDef<'_>) {
|
2016-02-27 11:34:29 +03:00
|
|
|
if !sf.is_positional() {
|
2021-07-28 18:23:40 +03:00
|
|
|
let def_id = cx.tcx.hir().local_def_id(sf.hir_id);
|
|
|
|
self.check_missing_docs_attrs(cx, def_id, 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<'_>) {
|
2021-07-28 18:23:40 +03:00
|
|
|
self.check_missing_docs_attrs(cx, cx.tcx.hir().local_def_id(v.id), v.span, "a", "variant");
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
declare_lint! {
|
2020-09-08 15:09:57 -07:00
|
|
|
/// The `missing_copy_implementations` lint detects potentially-forgotten
|
|
|
|
/// implementations of [`Copy`].
|
|
|
|
///
|
|
|
|
/// [`Copy`]: https://doc.rust-lang.org/std/marker/trait.Copy.html
|
|
|
|
///
|
|
|
|
/// ### Example
|
|
|
|
///
|
|
|
|
/// ```rust,compile_fail
|
|
|
|
/// #![deny(missing_copy_implementations)]
|
|
|
|
/// pub struct Foo {
|
|
|
|
/// pub field: i32
|
|
|
|
/// }
|
|
|
|
/// # fn main() {}
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// {{produces}}
|
|
|
|
///
|
|
|
|
/// ### Explanation
|
|
|
|
///
|
|
|
|
/// Historically (before 1.0), types were automatically marked as `Copy`
|
|
|
|
/// if possible. This was changed so that it required an explicit opt-in
|
|
|
|
/// by implementing the `Copy` trait. As part of this change, a lint was
|
|
|
|
/// added to alert if a copyable type was not marked `Copy`.
|
|
|
|
///
|
|
|
|
/// This lint is "allow" by default because this code isn't bad; it is
|
|
|
|
/// common to write newtypes like this specifically so that a `Copy` type
|
|
|
|
/// is no longer `Copy`. `Copy` types can result in unintended copies of
|
|
|
|
/// large data which can impact performance.
|
2015-02-25 22:44:44 +11:00
|
|
|
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<'_>) {
|
2021-07-28 18:23:40 +03:00
|
|
|
if !cx.access_levels.is_reachable(item.def_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
|
|
|
}
|
2021-01-30 17:47:51 +01:00
|
|
|
let def = cx.tcx.adt_def(item.def_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;
|
|
|
|
}
|
2021-01-30 17:47:51 +01:00
|
|
|
let def = cx.tcx.adt_def(item.def_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
|
|
|
}
|
2021-01-30 17:47:51 +01:00
|
|
|
let def = cx.tcx.adt_def(item.def_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
|
|
|
}
|
2022-02-06 13:03:28 -08:00
|
|
|
if can_type_implement_copy(
|
|
|
|
cx.tcx,
|
|
|
|
param_env,
|
|
|
|
ty,
|
|
|
|
traits::ObligationCause::misc(item.span, item.hir_id()),
|
|
|
|
)
|
|
|
|
.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`",
|
|
|
|
)
|
2022-01-22 18:49:12 -06:00
|
|
|
.emit();
|
2020-02-03 19:57:45 +10:00
|
|
|
})
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
declare_lint! {
|
2020-09-08 15:09:57 -07:00
|
|
|
/// The `missing_debug_implementations` lint detects missing
|
|
|
|
/// implementations of [`fmt::Debug`].
|
|
|
|
///
|
|
|
|
/// [`fmt::Debug`]: https://doc.rust-lang.org/std/fmt/trait.Debug.html
|
|
|
|
///
|
|
|
|
/// ### Example
|
|
|
|
///
|
|
|
|
/// ```rust,compile_fail
|
|
|
|
/// #![deny(missing_debug_implementations)]
|
|
|
|
/// pub struct Foo;
|
|
|
|
/// # fn main() {}
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// {{produces}}
|
|
|
|
///
|
|
|
|
/// ### Explanation
|
|
|
|
///
|
|
|
|
/// Having a `Debug` implementation on all types can assist with
|
|
|
|
/// debugging, as it provides a convenient way to format and display a
|
|
|
|
/// value. Using the `#[derive(Debug)]` attribute will automatically
|
|
|
|
/// generate a typical implementation, or a custom implementation can be
|
|
|
|
/// added by manually implementing the `Debug` trait.
|
|
|
|
///
|
|
|
|
/// This lint is "allow" by default because adding `Debug` to all types can
|
|
|
|
/// have a negative impact on compile time and code size. It also requires
|
|
|
|
/// boilerplate to be added to every type, which can be an impediment.
|
2015-02-25 22:44:44 +11:00
|
|
|
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 {
|
2021-01-30 17:47:51 +01:00
|
|
|
impling_types: Option<LocalDefIdSet>,
|
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<'_>) {
|
2021-07-28 18:23:40 +03:00
|
|
|
if !cx.access_levels.is_reachable(item.def_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,
|
|
|
|
}
|
|
|
|
|
2021-12-03 03:25:11 +01:00
|
|
|
let Some(debug) = cx.tcx.get_diagnostic_item(sym::Debug) else {
|
|
|
|
return
|
2015-02-25 22:44:44 +11:00
|
|
|
};
|
|
|
|
|
|
|
|
if self.impling_types.is_none() {
|
2021-01-30 17:47:51 +01:00
|
|
|
let mut impls = LocalDefIdSet::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() {
|
2022-03-05 07:28:41 +11:00
|
|
|
if let Some(def_id) = ty_def.did().as_local() {
|
2021-01-30 17:47:51 +01:00
|
|
|
impls.insert(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);
|
|
|
|
}
|
|
|
|
|
2021-01-30 17:47:51 +01:00
|
|
|
if !self.impling_types.as_ref().unwrap().contains(&item.def_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
|
|
|
))
|
2022-01-22 18:49:12 -06:00
|
|
|
.emit();
|
2020-02-03 19:57:45 +10:00
|
|
|
});
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-02 04:38:46 +02:00
|
|
|
declare_lint! {
|
2020-09-08 15:09:57 -07:00
|
|
|
/// The `anonymous_parameters` lint detects anonymous parameters in trait
|
|
|
|
/// definitions.
|
|
|
|
///
|
|
|
|
/// ### Example
|
|
|
|
///
|
|
|
|
/// ```rust,edition2015,compile_fail
|
|
|
|
/// #![deny(anonymous_parameters)]
|
|
|
|
/// // edition 2015
|
|
|
|
/// pub trait Foo {
|
|
|
|
/// fn foo(usize);
|
|
|
|
/// }
|
|
|
|
/// fn main() {}
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// {{produces}}
|
|
|
|
///
|
|
|
|
/// ### Explanation
|
|
|
|
///
|
|
|
|
/// This syntax is mostly a historical accident, and can be worked around
|
|
|
|
/// quite easily by adding an `_` pattern or a descriptive identifier:
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// trait Foo {
|
|
|
|
/// fn foo(_: usize);
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// This syntax is now a hard error in the 2018 edition. In the 2015
|
2021-03-08 15:43:18 -08:00
|
|
|
/// edition, this lint is "warn" by default. This lint
|
2020-09-08 15:09:57 -07:00
|
|
|
/// enables the [`cargo fix`] tool with the `--edition` flag to
|
|
|
|
/// automatically transition old code from the 2015 edition to 2018. The
|
2021-03-08 15:43:18 -08:00
|
|
|
/// tool will run this lint and automatically apply the
|
2020-09-08 15:09:57 -07:00
|
|
|
/// suggested fix from the compiler (which is to add `_` to each
|
|
|
|
/// parameter). This provides a completely automated way to update old
|
|
|
|
/// code for a new edition. See [issue #41686] for more details.
|
|
|
|
///
|
|
|
|
/// [issue #41686]: https://github.com/rust-lang/rust/issues/41686
|
|
|
|
/// [`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html
|
2017-05-02 04:38:46 +02:00
|
|
|
pub ANONYMOUS_PARAMETERS,
|
2021-03-08 15:43:18 -08:00
|
|
|
Warn,
|
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>",
|
2021-06-15 17:16:21 +02:00
|
|
|
reason: FutureIncompatibilityReason::EditionError(Edition::Edition2018),
|
2019-10-08 21:49:21 -04:00
|
|
|
};
|
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) {
|
2021-09-28 00:28:49 +03:00
|
|
|
if cx.sess().edition() != Edition::Edition2015 {
|
2021-03-08 15:43:18 -08:00
|
|
|
// This is a hard error in future editions; avoid linting and erroring
|
|
|
|
return;
|
|
|
|
}
|
2021-11-07 16:43:49 +08:00
|
|
|
if let ast::AssocItemKind::Fn(box Fn { ref sig, .. }) = it.kind {
|
2020-03-22 13:36:56 +01:00
|
|
|
for arg in sig.decl.inputs.iter() {
|
|
|
|
if let ast::PatKind::Ident(_, ident, None) = arg.pat.kind {
|
2020-12-29 20:28:08 -05:00
|
|
|
if ident.name == kw::Empty {
|
2020-03-22 13:36:56 +01:00
|
|
|
cx.struct_span_lint(ANONYMOUS_PARAMETERS, arg.pat.span, |lint| {
|
2021-09-28 00:28:49 +03:00
|
|
|
let ty_snip = cx.sess().source_map().span_to_snippet(arg.ty.span);
|
2020-03-22 13:36:56 +01:00
|
|
|
|
|
|
|
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 \
|
2021-10-03 15:53:02 +09: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.
|
2021-11-12 20:15:14 +08:00
|
|
|
depr_attrs: Vec<&'static BuiltinAttribute>,
|
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) {
|
2021-11-12 20:15:14 +08:00
|
|
|
for BuiltinAttribute { name, gate, .. } in &self.depr_attrs {
|
|
|
|
if attr.ident().map(|ident| ident.name) == Some(*name) {
|
2019-12-22 17:42:04 -05:00
|
|
|
if let &AttributeGate::Gated(
|
|
|
|
Stability::Deprecated(link, suggestion),
|
2021-01-02 20:09:17 +01:00
|
|
|
name,
|
|
|
|
reason,
|
2019-12-22 17:42:04 -05:00
|
|
|
_,
|
2021-11-12 20:15:14 +08:00
|
|
|
) = gate
|
2019-12-22 17:42:04 -05:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2021-07-29 12:00:41 -05:00
|
|
|
if attr.has_name(sym::no_start) || attr.has_name(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]) {
|
2021-07-03 01:00:08 +09:00
|
|
|
use rustc_ast::token::CommentKind;
|
|
|
|
|
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() {
|
2021-07-03 01:00:08 +09:00
|
|
|
let is_doc_comment = attr.is_doc_comment();
|
|
|
|
if is_doc_comment {
|
2020-02-22 08:28:56 -08:00
|
|
|
sugared_span =
|
2020-10-18 10:59:03 +02:00
|
|
|
Some(sugared_span.map_or(attr.span, |span| span.with_hi(attr.span.hi())));
|
2020-02-22 08:28:56 -08:00
|
|
|
}
|
2019-01-23 13:44:43 -05:00
|
|
|
|
2021-04-04 02:39:55 +09:00
|
|
|
if attrs.peek().map_or(false, |next_attr| next_attr.is_doc_comment()) {
|
2020-02-22 08:28:56 -08:00
|
|
|
continue;
|
|
|
|
}
|
2019-01-23 13:44:43 -05:00
|
|
|
|
2020-09-15 23:10:24 +02:00
|
|
|
let span = sugared_span.take().unwrap_or(attr.span);
|
2019-01-23 13:44:43 -05:00
|
|
|
|
2021-07-29 12:00:41 -05:00
|
|
|
if is_doc_comment || attr.has_name(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),
|
|
|
|
);
|
2021-07-03 01:00:08 +09:00
|
|
|
match attr.kind {
|
|
|
|
AttrKind::DocComment(CommentKind::Line, _) | AttrKind::Normal(..) => {
|
|
|
|
err.help("use `//` for a plain comment");
|
|
|
|
}
|
|
|
|
AttrKind::DocComment(CommentKind::Block, _) => {
|
|
|
|
err.help("use `/* */` for a plain comment");
|
|
|
|
}
|
|
|
|
}
|
2020-02-22 08:28:56 -08:00
|
|
|
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",
|
Fix inconsistencies in handling of inert attributes on statements
When the 'early' and 'late' visitors visit an attribute target, they
activate any lint attributes (e.g. `#[allow]`) that apply to it.
This can affect warnings emitted on sibiling attributes. For example,
the following code does not produce an `unused_attributes` for
`#[inline]`, since the sibiling `#[allow(unused_attributes)]` suppressed
the warning.
```rust
trait Foo {
#[allow(unused_attributes)] #[inline] fn first();
#[inline] #[allow(unused_attributes)] fn second();
}
```
However, we do not do this for statements - instead, the lint attributes
only become active when we visit the struct nested inside `StmtKind`
(e.g. `Item`).
Currently, this is difficult to observe due to another issue - the
`HasAttrs` impl for `StmtKind` ignores attributes for `StmtKind::Item`.
As a result, the `unused_doc_comments` lint will never see attributes on
item statements.
This commit makes two interrelated fixes to the handling of inert
(non-proc-macro) attributes on statements:
* The `HasAttr` impl for `StmtKind` now returns attributes for
`StmtKind::Item`, treating it just like every other `StmtKind`
variant. The only place relying on the old behavior was macro
which has been updated to explicitly ignore attributes on item
statements. This allows the `unused_doc_comments` lint to fire for
item statements.
* The `early` and `late` lint visitors now activate lint attributes when
invoking the callback for `Stmt`. This ensures that a lint
attribute (e.g. `#[allow(unused_doc_comments)]`) can be applied to
sibiling attributes on an item statement.
For now, the `unused_doc_comments` lint is explicitly disabled on item
statements, which preserves the current behavior. The exact locatiosn
where this lint should fire are being discussed in PR #78306
2020-10-23 18:17:00 -04:00
|
|
|
// Disabled pending discussion in #78306
|
|
|
|
ast::StmtKind::Item(..) => return,
|
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
|
|
|
}
|
2021-11-30 18:47:01 +00:00
|
|
|
|
|
|
|
fn check_generic_param(&mut self, cx: &EarlyContext<'_>, param: &ast::GenericParam) {
|
|
|
|
warn_if_doc(cx, param.ident.span, "generic parameters", ¶m.attrs);
|
|
|
|
}
|
2022-03-02 17:58:10 +01:00
|
|
|
|
|
|
|
fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) {
|
|
|
|
warn_if_doc(cx, block.span, "block", &block.attrs());
|
|
|
|
}
|
|
|
|
|
|
|
|
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
|
|
|
|
if let ast::ItemKind::ForeignMod(_) = item.kind {
|
|
|
|
warn_if_doc(cx, item.span, "extern block", &item.attrs);
|
|
|
|
}
|
|
|
|
}
|
2017-07-16 00:17:35 +02:00
|
|
|
}
|
|
|
|
|
2015-02-25 22:44:44 +11:00
|
|
|
declare_lint! {
|
2020-09-08 15:09:57 -07:00
|
|
|
/// The `no_mangle_const_items` lint detects any `const` items with the
|
|
|
|
/// [`no_mangle` attribute].
|
|
|
|
///
|
|
|
|
/// [`no_mangle` attribute]: https://doc.rust-lang.org/reference/abi.html#the-no_mangle-attribute
|
|
|
|
///
|
|
|
|
/// ### Example
|
|
|
|
///
|
|
|
|
/// ```rust,compile_fail
|
|
|
|
/// #[no_mangle]
|
|
|
|
/// const FOO: i32 = 5;
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// {{produces}}
|
|
|
|
///
|
|
|
|
/// ### Explanation
|
|
|
|
///
|
|
|
|
/// Constants do not have their symbols exported, and therefore, this
|
|
|
|
/// probably means you meant to use a [`static`], not a [`const`].
|
|
|
|
///
|
|
|
|
/// [`static`]: https://doc.rust-lang.org/reference/items/static-items.html
|
|
|
|
/// [`const`]: https://doc.rust-lang.org/reference/items/constant-items.html
|
2015-02-25 22:44:44 +11:00
|
|
|
NO_MANGLE_CONST_ITEMS,
|
|
|
|
Deny,
|
|
|
|
"const items will not have their symbols exported"
|
|
|
|
}
|
|
|
|
|
2015-12-09 01:48:40 +09:00
|
|
|
declare_lint! {
|
2020-09-08 15:09:57 -07:00
|
|
|
/// The `no_mangle_generic_items` lint detects generic items that must be
|
|
|
|
/// mangled.
|
|
|
|
///
|
|
|
|
/// ### Example
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// #[no_mangle]
|
|
|
|
/// fn foo<T>(t: T) {
|
|
|
|
///
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// {{produces}}
|
|
|
|
///
|
|
|
|
/// ### Explanation
|
|
|
|
///
|
2021-05-23 13:31:04 +02:00
|
|
|
/// A function with generics must have its symbol mangled to accommodate
|
2020-09-08 15:09:57 -07:00
|
|
|
/// the generic parameter. The [`no_mangle` attribute] has no effect in
|
|
|
|
/// this situation, and should be removed.
|
|
|
|
///
|
|
|
|
/// [`no_mangle` attribute]: https://doc.rust-lang.org/reference/abi.html#the-no_mangle-attribute
|
2015-12-09 01:48:40 +09:00
|
|
|
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<'_>) {
|
2021-01-24 13:17:54 +01:00
|
|
|
let attrs = cx.tcx.hir().attrs(it.hir_id());
|
2021-08-08 02:10:57 +08:00
|
|
|
let check_no_mangle_on_generic_fn = |no_mangle_attr: &ast::Attribute,
|
|
|
|
impl_generics: Option<&hir::Generics<'_>>,
|
|
|
|
generics: &hir::Generics<'_>,
|
|
|
|
span| {
|
|
|
|
for param in
|
|
|
|
generics.params.iter().chain(impl_generics.map(|g| g.params).into_iter().flatten())
|
|
|
|
{
|
|
|
|
match param.kind {
|
|
|
|
GenericParamKind::Lifetime { .. } => {}
|
|
|
|
GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {
|
|
|
|
cx.struct_span_lint(NO_MANGLE_GENERIC_ITEMS, 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();
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2019-09-26 17:51:36 +01:00
|
|
|
match it.kind {
|
2018-07-11 23:36:06 +08:00
|
|
|
hir::ItemKind::Fn(.., ref generics, _) => {
|
2021-01-24 13:17:54 +01:00
|
|
|
if let Some(no_mangle_attr) = cx.sess().find_by_name(attrs, sym::no_mangle) {
|
2021-08-08 02:10:57 +08:00
|
|
|
check_no_mangle_on_generic_fn(no_mangle_attr, None, generics, it.span);
|
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(..) => {
|
2021-01-24 13:17:54 +01:00
|
|
|
if cx.sess().contains_name(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
|
|
|
}
|
|
|
|
}
|
2021-08-08 02:10:57 +08:00
|
|
|
hir::ItemKind::Impl(hir::Impl { ref generics, items, .. }) => {
|
|
|
|
for it in items {
|
|
|
|
if let hir::AssocItemKind::Fn { .. } = it.kind {
|
|
|
|
if let Some(no_mangle_attr) = cx
|
|
|
|
.sess()
|
|
|
|
.find_by_name(cx.tcx.hir().attrs(it.id.hir_id()), sym::no_mangle)
|
|
|
|
{
|
|
|
|
check_no_mangle_on_generic_fn(
|
|
|
|
no_mangle_attr,
|
|
|
|
Some(generics),
|
2021-10-20 20:59:15 +02:00
|
|
|
cx.tcx.hir().get_generics(it.id.def_id).unwrap(),
|
2021-08-08 02:10:57 +08:00
|
|
|
it.span,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
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! {
|
2020-09-08 15:09:57 -07:00
|
|
|
/// The `mutable_transmutes` lint catches transmuting from `&T` to `&mut
|
|
|
|
/// T` because it is [undefined behavior].
|
|
|
|
///
|
|
|
|
/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
|
|
|
|
///
|
|
|
|
/// ### Example
|
|
|
|
///
|
|
|
|
/// ```rust,compile_fail
|
|
|
|
/// unsafe {
|
|
|
|
/// let y = std::mem::transmute::<&i32, &mut i32>(&5);
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// {{produces}}
|
|
|
|
///
|
|
|
|
/// ### Explanation
|
|
|
|
///
|
|
|
|
/// Certain assumptions are made about aliasing of data, and this transmute
|
|
|
|
/// violates those assumptions. Consider using [`UnsafeCell`] instead.
|
|
|
|
///
|
|
|
|
/// [`UnsafeCell`]: https://doc.rust-lang.org/std/cell/struct.UnsafeCell.html
|
2015-04-13 14:49:10 -07:00
|
|
|
MUTABLE_TRANSMUTES,
|
|
|
|
Deny,
|
2022-01-09 22:15:23 +00:00
|
|
|
"transmuting &T to &mut T is undefined behavior, even if the reference is unused"
|
2015-04-13 14:49:10 -07:00
|
|
|
}
|
|
|
|
|
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))) =
|
2020-08-03 00:49:11 +02:00
|
|
|
get_transmute_from_to(cx, expr).map(|(ty1, ty2)| (ty1.kind(), ty2.kind()))
|
2020-03-22 13:36:56 +01:00
|
|
|
{
|
|
|
|
if to_mt == hir::Mutability::Mut && from_mt == hir::Mutability::Not {
|
2022-01-09 22:15:23 +00:00
|
|
|
let msg = "transmuting &T to &mut T is undefined behavior, \
|
|
|
|
even if the reference is unused, consider instead using an UnsafeCell";
|
2022-01-22 18:49:12 -06: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! {
|
2020-09-08 15:09:57 -07:00
|
|
|
/// The `unstable_features` is deprecated and should no longer be used.
|
2015-02-28 13:31:14 +01:00
|
|
|
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) {
|
2021-07-29 12:00:41 -05:00
|
|
|
if attr.has_name(sym::feature) {
|
2017-03-03 09:23:59 +00:00
|
|
|
if let Some(items) = attr.meta_item_list() {
|
2015-05-18 16:37:05 +02:00
|
|
|
for item in items {
|
2020-07-30 11:27:50 +10:00
|
|
|
cx.struct_span_lint(UNSTABLE_FEATURES, item.span(), |lint| {
|
2022-01-22 18:49:12 -06:00
|
|
|
lint.build("unstable feature").emit();
|
2020-02-03 19:57:45 +10:00
|
|
|
});
|
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! {
|
2020-09-08 15:09:57 -07:00
|
|
|
/// The `unreachable_pub` lint triggers for `pub` items not reachable from
|
|
|
|
/// the crate root.
|
|
|
|
///
|
|
|
|
/// ### Example
|
|
|
|
///
|
|
|
|
/// ```rust,compile_fail
|
|
|
|
/// #![deny(unreachable_pub)]
|
|
|
|
/// mod foo {
|
|
|
|
/// pub mod bar {
|
|
|
|
///
|
|
|
|
/// }
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// {{produces}}
|
|
|
|
///
|
|
|
|
/// ### Explanation
|
|
|
|
///
|
|
|
|
/// A bare `pub` visibility may be misleading if the item is not actually
|
|
|
|
/// publicly exported from the crate. The `pub(crate)` visibility is
|
|
|
|
/// recommended to be used instead, which more clearly expresses the intent
|
|
|
|
/// that the item is only visible within its own crate.
|
|
|
|
///
|
|
|
|
/// This lint is "allow" by default because it will trigger for a large
|
|
|
|
/// amount existing Rust code, and has some false-positives. Eventually it
|
|
|
|
/// is desired for this to become warn-by-default.
|
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,
|
2021-07-28 18:23:40 +03:00
|
|
|
def_id: LocalDefId,
|
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 {
|
2021-07-28 18:23:40 +03:00
|
|
|
hir::VisibilityKind::Public if !cx.access_levels.is_reachable(def_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<'_>) {
|
2021-07-28 18:23:40 +03:00
|
|
|
self.perform_lint(cx, "item", item.def_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",
|
2021-07-28 18:23:40 +03:00
|
|
|
foreign_item.def_id,
|
2019-12-22 17:42:04 -05:00
|
|
|
&foreign_item.vis,
|
|
|
|
foreign_item.span,
|
|
|
|
true,
|
|
|
|
);
|
2017-11-02 20:50:17 -07:00
|
|
|
}
|
|
|
|
|
2021-03-16 00:36:07 +03:00
|
|
|
fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) {
|
2021-07-28 18:23:40 +03:00
|
|
|
let def_id = cx.tcx.hir().local_def_id(field.hir_id);
|
|
|
|
self.perform_lint(cx, "field", def_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<'_>) {
|
2021-07-28 18:23:40 +03:00
|
|
|
self.perform_lint(cx, "item", impl_item.def_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! {
|
2020-09-08 15:09:57 -07:00
|
|
|
/// The `type_alias_bounds` lint detects bounds in type aliases.
|
|
|
|
///
|
|
|
|
/// ### Example
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// type SendVec<T: Send> = Vec<T>;
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// {{produces}}
|
|
|
|
///
|
|
|
|
/// ### Explanation
|
|
|
|
///
|
|
|
|
/// The trait bounds in a type alias are currently ignored, and should not
|
|
|
|
/// be included to avoid confusion. This was previously allowed
|
|
|
|
/// unintentionally; this may become a hard error in the future.
|
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 {
|
2020-10-26 20:02:06 -04:00
|
|
|
hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) => {
|
|
|
|
matches!(path.res, Res::Def(DefKind::TyParam, _))
|
|
|
|
}
|
2019-12-22 17:42:04 -05:00
|
|
|
_ => 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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-23 20:41:46 +00:00
|
|
|
fn suggest_changing_assoc_types(ty: &hir::Ty<'_>, err: &mut Diagnostic) {
|
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};
|
2022-01-23 20:41:46 +00:00
|
|
|
struct WalkAssocTypes<'a> {
|
|
|
|
err: &'a mut Diagnostic,
|
2018-03-10 13:32:11 +01:00
|
|
|
}
|
2022-01-23 20:41:46 +00:00
|
|
|
impl Visitor<'_> for WalkAssocTypes<'_> {
|
|
|
|
fn visit_qpath(&mut self, qpath: &hir::QPath<'_>, 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<'_>) {
|
2021-12-03 03:25:11 +01:00
|
|
|
let hir::ItemKind::TyAlias(ty, type_alias_generics) = &item.kind else {
|
|
|
|
return
|
2018-03-06 11:22:24 +01:00
|
|
|
};
|
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";
|
2022-03-26 07:27:43 +00:00
|
|
|
err.multipart_suggestion(msg, suggestion, Applicability::MachineApplicable);
|
2020-02-02 09:47:58 +10:00
|
|
|
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
|
|
|
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) => {
|
2020-07-31 13:27:54 +02:00
|
|
|
let def_id = cx.tcx.hir().body_owner_def_id(body_id).to_def_id();
|
|
|
|
// trigger the query once for all constants since that will already report the errors
|
|
|
|
// FIXME: Use ensure here
|
|
|
|
let _ = cx.tcx.const_eval_poly(def_id);
|
2019-12-22 17:42:04 -05:00
|
|
|
}
|
2018-06-04 18:32:06 +02:00
|
|
|
hir::ItemKind::Static(_, _, body_id) => {
|
2020-07-31 13:27:54 +02:00
|
|
|
let def_id = cx.tcx.hir().body_owner_def_id(body_id).to_def_id();
|
2020-09-19 10:57:14 +02:00
|
|
|
// FIXME: Use ensure here
|
2020-07-31 13:27:54 +02:00
|
|
|
let _ = cx.tcx.eval_static_initializer(def_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! {
|
2020-09-08 15:09:57 -07:00
|
|
|
/// The `trivial_bounds` lint detects trait bounds that don't depend on
|
|
|
|
/// any type parameters.
|
|
|
|
///
|
|
|
|
/// ### Example
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// #![feature(trivial_bounds)]
|
|
|
|
/// pub struct A where i32: Copy;
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// {{produces}}
|
|
|
|
///
|
|
|
|
/// ### Explanation
|
|
|
|
///
|
|
|
|
/// Usually you would not write a trait bound that you know is always
|
|
|
|
/// true, or never true. However, when using macros, the macro may not
|
|
|
|
/// know whether or not the constraint would hold or not at the time when
|
|
|
|
/// generating the code. Currently, the compiler does not alert you if the
|
|
|
|
/// constraint is always true, and generates an error if it is never true.
|
|
|
|
/// The `trivial_bounds` feature changes this to be a warning in both
|
|
|
|
/// cases, giving macros more freedom and flexibility to generate code,
|
|
|
|
/// while still providing a signal when writing non-macro code that
|
|
|
|
/// something is amiss.
|
|
|
|
///
|
|
|
|
/// See [RFC 2056] for more details. This feature is currently only
|
|
|
|
/// available on the nightly channel, see [tracking issue #48214].
|
|
|
|
///
|
|
|
|
/// [RFC 2056]: https://github.com/rust-lang/rfcs/blob/master/text/2056-allow-trivial-where-clause-constraints.md
|
|
|
|
/// [tracking issue #48214]: https://github.com/rust-lang/rust/issues/48214
|
2018-05-06 22:52:58 +01:00
|
|
|
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;
|
2021-01-07 11:20:28 -05:00
|
|
|
use rustc_middle::ty::PredicateKind::*;
|
2018-05-06 22:52:58 +01:00
|
|
|
|
|
|
|
if cx.tcx.features().trivial_bounds {
|
2021-01-30 17:47:51 +01:00
|
|
|
let predicates = cx.tcx.predicates_of(item.def_id);
|
2019-10-18 03:14:57 +03:00
|
|
|
for &(predicate, span) in predicates.predicates {
|
2021-01-07 11:20:28 -05:00
|
|
|
let predicate_kind_name = match predicate.kind().skip_binder() {
|
2021-10-03 15:53:02 +09:00
|
|
|
Trait(..) => "trait",
|
2018-05-06 22:52:58 +01:00
|
|
|
TypeOutlives(..) |
|
2021-10-03 15:53:02 +09:00
|
|
|
RegionOutlives(..) => "lifetime",
|
2018-05-06 22:52:58 +01:00
|
|
|
|
|
|
|
// 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-11-21 07:06:16 -05:00
|
|
|
Coerce(..) |
|
2020-02-29 10:03:04 +13:00
|
|
|
ConstEvaluatable(..) |
|
2020-09-01 17:58:34 +02:00
|
|
|
ConstEquate(..) |
|
|
|
|
TypeWellFormedFromEnv(..) => continue,
|
2018-05-06 22:52:58 +01:00
|
|
|
};
|
2022-01-12 03:19:52 +00: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
|
|
|
))
|
2022-01-22 18:49:12 -06:00
|
|
|
.emit();
|
2020-02-03 19:57:45 +10:00
|
|
|
});
|
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! {
|
2020-09-08 15:09:57 -07:00
|
|
|
/// The `ellipsis_inclusive_range_patterns` lint detects the [`...` range
|
|
|
|
/// pattern], which is deprecated.
|
|
|
|
///
|
|
|
|
/// [`...` range pattern]: https://doc.rust-lang.org/reference/patterns.html#range-patterns
|
|
|
|
///
|
|
|
|
/// ### Example
|
|
|
|
///
|
2021-09-20 08:46:26 -04:00
|
|
|
/// ```rust,edition2018
|
2020-09-08 15:09:57 -07:00
|
|
|
/// let x = 123;
|
|
|
|
/// match x {
|
|
|
|
/// 0...100 => {}
|
|
|
|
/// _ => {}
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// {{produces}}
|
|
|
|
///
|
|
|
|
/// ### Explanation
|
|
|
|
///
|
|
|
|
/// The `...` range pattern syntax was changed to `..=` to avoid potential
|
|
|
|
/// confusion with the [`..` range expression]. Use the new form instead.
|
|
|
|
///
|
|
|
|
/// [`..` range expression]: https://doc.rust-lang.org/reference/expressions/range-expr.html
|
2018-05-28 19:32:03 -07:00
|
|
|
pub ELLIPSIS_INCLUSIVE_RANGE_PATTERNS,
|
2019-05-30 04:13:55 -04:00
|
|
|
Warn,
|
2021-04-13 16:55:54 +02:00
|
|
|
"`...` range patterns are deprecated",
|
|
|
|
@future_incompatible = FutureIncompatibleInfo {
|
2021-08-09 17:45:01 +02:00
|
|
|
reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html>",
|
2021-06-15 17:16:21 +02:00
|
|
|
reason: FutureIncompatibilityReason::EditionError(Edition::Edition2021),
|
2021-04-13 16:55:54 +02:00
|
|
|
};
|
2018-05-28 19:32:03 -07:00
|
|
|
}
|
|
|
|
|
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);
|
2021-03-16 21:47:06 +01:00
|
|
|
let end = expr_to_string(&end);
|
|
|
|
let replace = match start {
|
|
|
|
Some(start) => format!("&({}..={})", expr_to_string(&start), end),
|
|
|
|
None => format!("&(..={})", end),
|
|
|
|
};
|
2021-04-07 18:20:23 +02:00
|
|
|
if join.edition() >= Edition::Edition2021 {
|
2021-03-16 21:47:06 +01:00
|
|
|
let mut err =
|
2021-09-28 00:28:49 +03:00
|
|
|
rustc_errors::struct_span_err!(cx.sess(), pat.span, E0783, "{}", msg,);
|
2021-03-16 21:47:06 +01:00
|
|
|
err.span_suggestion(
|
|
|
|
pat.span,
|
|
|
|
suggestion,
|
|
|
|
replace,
|
|
|
|
Applicability::MachineApplicable,
|
|
|
|
)
|
|
|
|
.emit();
|
|
|
|
} else {
|
|
|
|
cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, pat.span, |lint| {
|
|
|
|
lint.build(msg)
|
|
|
|
.span_suggestion(
|
|
|
|
pat.span,
|
|
|
|
suggestion,
|
|
|
|
replace,
|
|
|
|
Applicability::MachineApplicable,
|
|
|
|
)
|
|
|
|
.emit();
|
|
|
|
});
|
|
|
|
}
|
2018-11-10 18:08:50 +00:00
|
|
|
} else {
|
2021-03-16 21:47:06 +01:00
|
|
|
let replace = "..=".to_owned();
|
2021-04-07 18:20:23 +02:00
|
|
|
if join.edition() >= Edition::Edition2021 {
|
2021-03-16 21:47:06 +01:00
|
|
|
let mut err =
|
2021-09-28 00:28:49 +03:00
|
|
|
rustc_errors::struct_span_err!(cx.sess(), pat.span, E0783, "{}", msg,);
|
2021-03-16 21:47:06 +01:00
|
|
|
err.span_suggestion_short(
|
|
|
|
join,
|
|
|
|
suggestion,
|
|
|
|
replace,
|
|
|
|
Applicability::MachineApplicable,
|
|
|
|
)
|
|
|
|
.emit();
|
|
|
|
} else {
|
|
|
|
cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, join, |lint| {
|
|
|
|
lint.build(msg)
|
|
|
|
.span_suggestion_short(
|
|
|
|
join,
|
|
|
|
suggestion,
|
|
|
|
replace,
|
|
|
|
Applicability::MachineApplicable,
|
|
|
|
)
|
|
|
|
.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! {
|
2020-09-08 15:09:57 -07:00
|
|
|
/// The `unnameable_test_items` lint detects [`#[test]`][test] functions
|
|
|
|
/// that are not able to be run by the test harness because they are in a
|
|
|
|
/// position where they are not nameable.
|
|
|
|
///
|
|
|
|
/// [test]: https://doc.rust-lang.org/reference/attributes/testing.html#the-test-attribute
|
|
|
|
///
|
|
|
|
/// ### Example
|
|
|
|
///
|
|
|
|
/// ```rust,test
|
|
|
|
/// fn main() {
|
|
|
|
/// #[test]
|
|
|
|
/// fn foo() {
|
|
|
|
/// // This test will not fail because it does not run.
|
|
|
|
/// assert_eq!(1, 2);
|
|
|
|
/// }
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// {{produces}}
|
|
|
|
///
|
|
|
|
/// ### Explanation
|
|
|
|
///
|
|
|
|
/// In order for the test harness to run a test, the test function must be
|
|
|
|
/// located in a position where it can be accessed from the crate root.
|
|
|
|
/// This generally means it must be defined in a module, and not anywhere
|
|
|
|
/// else such as inside another function. The compiler previously allowed
|
|
|
|
/// this without an error, so a lint was added as an alert that a test is
|
|
|
|
/// not being used. Whether or not this should be allowed has not yet been
|
|
|
|
/// decided, see [RFC 2471] and [issue #36629].
|
|
|
|
///
|
|
|
|
/// [RFC 2471]: https://github.com/rust-lang/rfcs/pull/2471#issuecomment-397414443
|
|
|
|
/// [issue #36629]: https://github.com/rust-lang/rust/issues/36629
|
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 {
|
2021-01-30 17:47:51 +01:00
|
|
|
boundary: Option<LocalDefId>, // Id 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;
|
2021-01-30 17:47:51 +01:00
|
|
|
self.boundary = Some(it.def_id);
|
2018-06-08 18:24:57 -07:00
|
|
|
}
|
2018-07-20 18:04:02 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-01-24 13:17:54 +01:00
|
|
|
let attrs = cx.tcx.hir().attrs(it.hir_id());
|
|
|
|
if let Some(attr) = cx.sess().find_by_name(attrs, sym::rustc_test_marker) {
|
2020-02-02 09:47:58 +10:00
|
|
|
cx.struct_span_lint(UNNAMEABLE_TEST_ITEMS, attr.span, |lint| {
|
2022-01-22 18:49:12 -06:00
|
|
|
lint.build("cannot test inner items").emit();
|
2020-02-02 09:47:58 +10:00
|
|
|
});
|
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<'_>) {
|
2021-01-30 17:47:51 +01:00
|
|
|
if !self.items_nameable && self.boundary == Some(it.def_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! {
|
2020-09-08 15:09:57 -07:00
|
|
|
/// The `keyword_idents` lint detects edition keywords being used as an
|
|
|
|
/// identifier.
|
|
|
|
///
|
|
|
|
/// ### Example
|
|
|
|
///
|
|
|
|
/// ```rust,edition2015,compile_fail
|
|
|
|
/// #![deny(keyword_idents)]
|
|
|
|
/// // edition 2015
|
|
|
|
/// fn dyn() {}
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// {{produces}}
|
|
|
|
///
|
|
|
|
/// ### Explanation
|
|
|
|
///
|
|
|
|
/// Rust [editions] allow the language to evolve without breaking
|
|
|
|
/// backwards compatibility. This lint catches code that uses new keywords
|
|
|
|
/// that are added to the language that are used as identifiers (such as a
|
|
|
|
/// variable name, function name, etc.). If you switch the compiler to a
|
|
|
|
/// new edition without updating the code, then it will fail to compile if
|
|
|
|
/// you are using a new keyword as an identifier.
|
|
|
|
///
|
|
|
|
/// You can manually change the identifiers to a non-keyword, or use a
|
|
|
|
/// [raw identifier], for example `r#dyn`, to transition to a new edition.
|
|
|
|
///
|
|
|
|
/// This lint solves the problem automatically. It is "allow" by default
|
|
|
|
/// because the code is perfectly valid in older editions. The [`cargo
|
|
|
|
/// fix`] tool with the `--edition` flag will switch this lint to "warn"
|
|
|
|
/// and automatically apply the suggested fix from the compiler (which is
|
|
|
|
/// to use a raw identifier). This provides a completely automated way to
|
|
|
|
/// update old code for a new edition.
|
|
|
|
///
|
|
|
|
/// [editions]: https://doc.rust-lang.org/edition-guide/
|
|
|
|
/// [raw identifier]: https://doc.rust-lang.org/reference/identifiers.html
|
|
|
|
/// [`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html
|
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>",
|
2021-06-15 17:16:21 +02:00
|
|
|
reason: FutureIncompatibilityReason::EditionError(Edition::Edition2018),
|
2019-10-08 21:49:21 -04:00
|
|
|
};
|
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
|
|
|
) {
|
2021-09-28 00:28:49 +03: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`.
|
2021-09-28 00:28:49 +03: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,
|
|
|
|
)
|
2022-01-22 18:49:12 -06:00
|
|
|
.emit();
|
2020-02-02 09:47:58 +10:00
|
|
|
});
|
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()
|
2021-01-07 11:20:28 -05:00
|
|
|
.filter_map(|(pred, _)| match pred.kind().skip_binder() {
|
2022-01-28 11:25:15 +11:00
|
|
|
ty::PredicateKind::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()
|
2021-01-07 11:20:28 -05:00
|
|
|
.filter_map(|(pred, _)| match pred.kind().skip_binder() {
|
|
|
|
ty::PredicateKind::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) {
|
2020-09-18 19:11:06 +02:00
|
|
|
Some(Region::Static) if infer_static => {
|
2022-01-28 11:25:15 +11:00
|
|
|
inferred_outlives.iter().any(|r| matches!(**r, ty::ReStatic))
|
2020-09-18 19:11:06 +02:00
|
|
|
}
|
2019-12-22 17:42:04 -05:00
|
|
|
Some(Region::EarlyBound(index, ..)) => inferred_outlives.iter().any(|r| {
|
2022-01-28 11:25:15 +11:00
|
|
|
if let ty::ReEarlyBound(ebr) = **r { ebr.index == index } else { false }
|
2019-12-22 17:42:04 -05:00
|
|
|
}),
|
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;
|
2021-01-30 17:47:51 +01:00
|
|
|
let def_id = item.def_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 {
|
2020-09-18 19:11:06 +02:00
|
|
|
let has_lifetime_bounds = param
|
|
|
|
.bounds
|
|
|
|
.iter()
|
|
|
|
.any(|bound| matches!(bound, hir::GenericBound::Outlives(_)));
|
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)) => {
|
2021-12-03 03:25:11 +01:00
|
|
|
let Res::Def(DefKind::TyParam, def_id) = path.res else {
|
|
|
|
continue
|
|
|
|
};
|
|
|
|
let index = ty_generics.param_def_id_to_index[&def_id];
|
|
|
|
(
|
|
|
|
Self::lifetimes_outliving_type(inferred_outlives, index),
|
|
|
|
&predicate.bounds,
|
|
|
|
predicate.span,
|
|
|
|
)
|
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! {
|
2020-09-08 15:09:57 -07:00
|
|
|
/// The `incomplete_features` lint detects unstable features enabled with
|
|
|
|
/// the [`feature` attribute] that may function improperly in some or all
|
|
|
|
/// cases.
|
|
|
|
///
|
|
|
|
/// [`feature` attribute]: https://doc.rust-lang.org/nightly/unstable-book/
|
|
|
|
///
|
|
|
|
/// ### Example
|
|
|
|
///
|
|
|
|
/// ```rust
|
2021-08-27 18:04:57 +02:00
|
|
|
/// #![feature(generic_const_exprs)]
|
2020-09-08 15:09:57 -07:00
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// {{produces}}
|
|
|
|
///
|
|
|
|
/// ### Explanation
|
|
|
|
///
|
|
|
|
/// Although it is encouraged for people to experiment with unstable
|
|
|
|
/// features, some of them are known to be incomplete or faulty. This lint
|
|
|
|
/// is a signal that the feature has not yet been finished, and you may
|
|
|
|
/// experience problems with it.
|
2019-07-29 02:00:53 +02:00
|
|
|
pub INCOMPLETE_FEATURES,
|
|
|
|
Warn,
|
|
|
|
"incomplete features that may function improperly in some or all cases"
|
|
|
|
}
|
|
|
|
|
|
|
|
declare_lint_pass!(
|
2021-04-07 14:47:01 -05:00
|
|
|
/// Check for used feature gates in `INCOMPLETE_FEATURES` in `rustc_feature/src/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) {
|
2021-09-28 00:28:49 +03:00
|
|
|
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)))
|
2021-06-28 14:39:20 -04:00
|
|
|
.filter(|(&name, _)| features.incomplete(name))
|
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,
|
|
|
|
));
|
|
|
|
}
|
2020-10-14 08:35:32 +09:00
|
|
|
if HAS_MIN_FEATURES.contains(&name) {
|
|
|
|
builder.help(&format!(
|
|
|
|
"consider using `min_{}` instead, which is more stable and complete",
|
|
|
|
name,
|
|
|
|
));
|
|
|
|
}
|
2020-05-09 14:04:32 +02:00
|
|
|
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
|
|
|
|
2020-11-17 10:55:13 +01:00
|
|
|
const HAS_MIN_FEATURES: &[Symbol] = &[sym::specialization];
|
2020-10-14 08:35:32 +09:00
|
|
|
|
2019-08-06 23:11:52 +02:00
|
|
|
declare_lint! {
|
2020-09-08 15:09:57 -07:00
|
|
|
/// The `invalid_value` lint detects creating a value that is not valid,
|
2021-05-02 15:55:22 -06:00
|
|
|
/// such as a null reference.
|
2020-09-08 15:09:57 -07:00
|
|
|
///
|
|
|
|
/// ### Example
|
|
|
|
///
|
|
|
|
/// ```rust,no_run
|
|
|
|
/// # #![allow(unused)]
|
|
|
|
/// unsafe {
|
|
|
|
/// let x: &'static i32 = std::mem::zeroed();
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// {{produces}}
|
|
|
|
///
|
|
|
|
/// ### Explanation
|
|
|
|
///
|
|
|
|
/// In some situations the compiler can detect that the code is creating
|
|
|
|
/// an invalid value, which should be avoided.
|
|
|
|
///
|
|
|
|
/// In particular, this lint will check for improper use of
|
|
|
|
/// [`mem::zeroed`], [`mem::uninitialized`], [`mem::transmute`], and
|
|
|
|
/// [`MaybeUninit::assume_init`] that can cause [undefined behavior]. The
|
|
|
|
/// lint should provide extra information to indicate what the problem is
|
|
|
|
/// and a possible solution.
|
|
|
|
///
|
|
|
|
/// [`mem::zeroed`]: https://doc.rust-lang.org/std/mem/fn.zeroed.html
|
|
|
|
/// [`mem::uninitialized`]: https://doc.rust-lang.org/std/mem/fn.uninitialized.html
|
|
|
|
/// [`mem::transmute`]: https://doc.rust-lang.org/std/mem/fn.transmute.html
|
|
|
|
/// [`MaybeUninit::assume_init`]: https://doc.rust-lang.org/std/mem/union.MaybeUninit.html#method.assume_init
|
|
|
|
/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
|
2019-08-06 23:11:52 +02:00
|
|
|
pub INVALID_VALUE,
|
|
|
|
Warn,
|
2021-05-02 15:55:22 -06:00
|
|
|
"an invalid value is being created (such as a null reference)"
|
2019-08-06 23:11:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
2020-11-25 17:00:28 -05:00
|
|
|
}
|
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-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()?;
|
2021-10-04 16:11:22 -05:00
|
|
|
match cx.tcx.get_diagnostic_name(def_id) {
|
|
|
|
Some(sym::mem_zeroed) => return Some(InitKind::Zeroed),
|
|
|
|
Some(sym::mem_uninitialized) => return Some(InitKind::Uninit),
|
|
|
|
Some(sym::transmute) if is_zero(&args[0]) => return Some(InitKind::Zeroed),
|
|
|
|
_ => {}
|
2019-08-17 08:39:20 +02:00
|
|
|
}
|
2019-11-02 16:12:33 +01:00
|
|
|
}
|
2021-12-01 11:17:50 -06: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()?;
|
2021-10-04 16:11:22 -05:00
|
|
|
match cx.tcx.get_diagnostic_name(def_id) {
|
|
|
|
Some(sym::maybe_uninit_zeroed) => return Some(InitKind::Zeroed),
|
|
|
|
Some(sym::maybe_uninit_uninit) => return Some(InitKind::Uninit),
|
|
|
|
_ => {}
|
2019-11-02 16:12:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
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".
|
2022-03-05 07:28:41 +11:00
|
|
|
fn is_multi_variant<'tcx>(adt: ty::AdtDef<'tcx>) -> bool {
|
2020-07-17 15:01:37 +02:00
|
|
|
// As an approximation, we only count dataless variants. Those are definitely inhabited.
|
2022-03-05 07:28:41 +11:00
|
|
|
let existing_variants = adt.variants().iter().filter(|v| v.fields.is_empty()).count();
|
2020-07-17 15:01:37 +02:00
|
|
|
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>(
|
2022-03-27 13:10:34 -04:00
|
|
|
cx: &LateContext<'tcx>,
|
2019-08-17 09:39:25 +02:00
|
|
|
ty: Ty<'tcx>,
|
|
|
|
init: InitKind,
|
|
|
|
) -> Option<InitError> {
|
2020-03-29 16:41:09 +02:00
|
|
|
use rustc_middle::ty::TyKind::*;
|
2020-08-03 00:49:11 +02: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)),
|
2020-08-03 00:49:11 +02:00
|
|
|
RawPtr(tm) if matches!(tm.ty.kind(), Dynamic(..)) =>
|
2019-12-22 17:42:04 -05:00
|
|
|
// 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;
|
2022-03-27 13:10:34 -04:00
|
|
|
match cx.tcx.layout_scalar_valid_range(adt_def.did()) {
|
2019-08-17 10:13:47 +02:00
|
|
|
// 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.
|
2022-03-05 07:28:41 +11: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.
|
2022-03-05 07:28:41 +11:00
|
|
|
let variant = &adt_def.variant(VariantIdx::from_u32(0));
|
2019-08-12 09:24:13 +02:00
|
|
|
variant.fields.iter().find_map(|field| {
|
2022-03-27 13:10:34 -04:00
|
|
|
ty_find_init_error(cx, field.ty(cx.tcx, substs), init).map(
|
2019-12-22 17:42:04 -05:00
|
|
|
|(mut msg, span)| {
|
|
|
|
if span.is_none() {
|
|
|
|
// Point to this field, should be helpful for figuring
|
|
|
|
// out where the source of the error is.
|
2022-03-27 13:10:34 -04:00
|
|
|
let span = cx.tcx.def_span(field.did);
|
2019-12-22 17:42:04 -05:00
|
|
|
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.
|
|
|
|
_ => {
|
2022-03-05 07:28:41 +11:00
|
|
|
if init == InitKind::Uninit && is_multi_variant(*adt_def) {
|
2022-03-27 13:10:34 -04:00
|
|
|
let span = cx.tcx.def_span(adt_def.did());
|
2020-07-17 15:01:37 +02:00
|
|
|
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.
|
2022-03-27 13:10:34 -04:00
|
|
|
ty.tuple_fields().iter().find_map(|field| ty_find_init_error(cx, field, init))
|
|
|
|
}
|
|
|
|
Array(ty, len) => {
|
|
|
|
if matches!(len.try_eval_usize(cx.tcx, cx.param_env), Some(v) if v > 0) {
|
|
|
|
// Array length known at array non-empty -- recurse.
|
|
|
|
ty_find_init_error(cx, *ty, init)
|
|
|
|
} else {
|
|
|
|
// Empty array or size unknown.
|
|
|
|
None
|
|
|
|
}
|
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)) =
|
2022-03-27 13:10:34 -04:00
|
|
|
with_no_trimmed_paths!(ty_find_init_error(cx, conjured_ty, init))
|
2020-09-03 14:33:55 +03:00
|
|
|
{
|
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-09-08 15:09:57 -07:00
|
|
|
/// The `clashing_extern_declarations` lint detects when an `extern fn`
|
|
|
|
/// has been declared with the same name but different types.
|
|
|
|
///
|
|
|
|
/// ### Example
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// mod m {
|
|
|
|
/// extern "C" {
|
|
|
|
/// fn foo();
|
|
|
|
/// }
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// extern "C" {
|
|
|
|
/// fn foo(_: u32);
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// {{produces}}
|
|
|
|
///
|
|
|
|
/// ### Explanation
|
|
|
|
///
|
|
|
|
/// Because two symbols of the same name cannot be resolved to two
|
|
|
|
/// different functions at link time, and one function cannot possibly
|
|
|
|
/// have two types, a clashing extern declaration is almost certainly a
|
|
|
|
/// mistake. Check to make sure that the `extern` definitions are correct
|
|
|
|
/// and equivalent, and possibly consider unifying them in one location.
|
|
|
|
///
|
|
|
|
/// This lint does not run between crates because a project may have
|
|
|
|
/// dependencies which both rely on the same extern function, but declare
|
|
|
|
/// it in a different (but valid) way. For example, they may both declare
|
|
|
|
/// an opaque type for one or more of the arguments (which would end up
|
|
|
|
/// distinct types), or use types that are valid conversions in the
|
|
|
|
/// language the `extern fn` is defined in. In these cases, the compiler
|
|
|
|
/// can't say that the clashing declaration is incorrect.
|
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-12-23 00:34:26 +10:00
|
|
|
/// Map of function symbol name to the first-seen hir id for that symbol name.. If seen_decls
|
|
|
|
/// contains an entry for key K, it means a symbol with name K has been seen by this lint and
|
|
|
|
/// the symbol should be reported as a clashing declaration.
|
|
|
|
// FIXME: Technically, we could just store a &'tcx str here without issue; however, the
|
|
|
|
// `impl_lint_pass` macro doesn't currently support lints parametric over a lifetime.
|
2021-01-28 08:03:36 +10:00
|
|
|
seen_decls: FxHashMap<Symbol, HirId>,
|
2020-06-19 18:14:54 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
/// 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> {
|
2021-02-01 00:33:38 +01:00
|
|
|
let did = fi.def_id.to_def_id();
|
2020-12-13 23:02:32 +10:00
|
|
|
let instance = Instance::new(did, ty::List::identity_for_item(tcx, did));
|
2021-01-28 08:03:36 +10:00
|
|
|
let name = Symbol::intern(tcx.symbol_name(instance).name);
|
|
|
|
if let Some(&hir_id) = self.seen_decls.get(&name) {
|
2020-06-19 18:14:54 +10:00
|
|
|
// 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.
|
2020-12-23 00:26:04 +10:00
|
|
|
Some(hir_id)
|
2020-06-19 18:14:54 +10:00
|
|
|
} else {
|
2021-02-01 00:33:38 +01:00
|
|
|
self.seen_decls.insert(name, fi.hir_id())
|
2020-06-19 18:14:54 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 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 {
|
|
|
|
if let Some((overridden_link_name, overridden_link_name_span)) =
|
2021-02-01 00:33:38 +01:00
|
|
|
tcx.codegen_fn_attrs(fi.def_id).link_name.map(|overridden_link_name| {
|
2020-06-19 18:14:54 +10:00
|
|
|
// 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,
|
2021-02-01 00:33:38 +01:00
|
|
|
tcx.get_attrs(fi.def_id.to_def_id())
|
2020-06-19 18:14:54 +10:00
|
|
|
.iter()
|
2021-07-29 12:00:41 -05:00
|
|
|
.find(|at| at.has_name(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 {
|
2020-08-03 00:49:11 +02:00
|
|
|
if let ty::Adt(def, substs) = *ty.kind() {
|
2022-03-05 07:28:41 +11:00
|
|
|
let is_transparent = def.subst(tcx, substs).repr().transparent();
|
|
|
|
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 {
|
2022-03-05 07:28:41 +11:00
|
|
|
debug_assert!(def.variants().len() == 1);
|
|
|
|
let v = &def.variant(VariantIdx::new(0));
|
2020-08-03 11:08:03 +01:00
|
|
|
ty = transparent_newtype_field(tcx, v)
|
2020-08-25 23:41:13 +10:00
|
|
|
.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;
|
2022-01-25 08:42:52 +01:00
|
|
|
if 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::*;
|
2020-08-03 00:49:11 +02:00
|
|
|
let a_kind = a.kind();
|
|
|
|
let b_kind = b.kind();
|
2020-08-16 10:50:20 +10:00
|
|
|
|
2020-08-26 23:32:57 +10:00
|
|
|
let compare_layouts = |a, b| -> Result<bool, LayoutError<'tcx>> {
|
|
|
|
debug!("compare_layouts({:?}, {:?})", a, b);
|
2022-03-04 13:46:56 +11:00
|
|
|
let a_layout = &cx.layout_of(a)?.layout.abi();
|
|
|
|
let b_layout = &cx.layout_of(b)?.layout.abi();
|
2020-08-26 23:32:57 +10:00
|
|
|
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.
|
2022-03-05 07:28:41 +11:00
|
|
|
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.
|
2022-02-02 14:24:45 +11:00
|
|
|
a_const.val() == b_const.val()
|
Overhaul `TyS` and `Ty`.
Specifically, change `Ty` from this:
```
pub type Ty<'tcx> = &'tcx TyS<'tcx>;
```
to this
```
pub struct Ty<'tcx>(Interned<'tcx, TyS<'tcx>>);
```
There are two benefits to this.
- It's now a first class type, so we can define methods on it. This
means we can move a lot of methods away from `TyS`, leaving `TyS` as a
barely-used type, which is appropriate given that it's not meant to
be used directly.
- The uniqueness requirement is now explicit, via the `Interned` type.
E.g. the pointer-based `Eq` and `Hash` comes from `Interned`, rather
than via `TyS`, which wasn't obvious at all.
Much of this commit is boring churn. The interesting changes are in
these files:
- compiler/rustc_middle/src/arena.rs
- compiler/rustc_middle/src/mir/visit.rs
- compiler/rustc_middle/src/ty/context.rs
- compiler/rustc_middle/src/ty/mod.rs
Specifically:
- Most mentions of `TyS` are removed. It's very much a dumb struct now;
`Ty` has all the smarts.
- `TyS` now has `crate` visibility instead of `pub`.
- `TyS::make_for_test` is removed in favour of the static `BOOL_TY`,
which just works better with the new structure.
- The `Eq`/`Ord`/`Hash` impls are removed from `TyS`. `Interned`s impls
of `Eq`/`Hash` now suffice. `Ord` is now partly on `Interned`
(pointer-based, for the `Equal` case) and partly on `TyS`
(contents-based, for the other cases).
- There are many tedious sigil adjustments, i.e. adding or removing `*`
or `&`. They seem to be unavoidable.
2022-01-25 14:13:38 +11:00
|
|
|
&& structurally_same_type_impl(seen_types, cx, *a_ty, *b_ty, ckind)
|
2020-08-18 02:00:35 +10:00
|
|
|
}
|
|
|
|
(Slice(a_ty), Slice(b_ty)) => {
|
Overhaul `TyS` and `Ty`.
Specifically, change `Ty` from this:
```
pub type Ty<'tcx> = &'tcx TyS<'tcx>;
```
to this
```
pub struct Ty<'tcx>(Interned<'tcx, TyS<'tcx>>);
```
There are two benefits to this.
- It's now a first class type, so we can define methods on it. This
means we can move a lot of methods away from `TyS`, leaving `TyS` as a
barely-used type, which is appropriate given that it's not meant to
be used directly.
- The uniqueness requirement is now explicit, via the `Interned` type.
E.g. the pointer-based `Eq` and `Hash` comes from `Interned`, rather
than via `TyS`, which wasn't obvious at all.
Much of this commit is boring churn. The interesting changes are in
these files:
- compiler/rustc_middle/src/arena.rs
- compiler/rustc_middle/src/mir/visit.rs
- compiler/rustc_middle/src/ty/context.rs
- compiler/rustc_middle/src/ty/mod.rs
Specifically:
- Most mentions of `TyS` are removed. It's very much a dumb struct now;
`Ty` has all the smarts.
- `TyS` now has `crate` visibility instead of `pub`.
- `TyS::make_for_test` is removed in favour of the static `BOOL_TY`,
which just works better with the new structure.
- The `Eq`/`Ord`/`Hash` impls are removed from `TyS`. `Interned`s impls
of `Eq`/`Hash` now suffice. `Ord` is now partly on `Interned`
(pointer-based, for the `Equal` case) and partly on `TyS`
(contents-based, for the other cases).
- There are many tedious sigil adjustments, i.e. adding or removing `*`
or `&`. They seem to be unavoidable.
2022-01-25 14:13:38 +11: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(
|
Overhaul `TyS` and `Ty`.
Specifically, change `Ty` from this:
```
pub type Ty<'tcx> = &'tcx TyS<'tcx>;
```
to this
```
pub struct Ty<'tcx>(Interned<'tcx, TyS<'tcx>>);
```
There are two benefits to this.
- It's now a first class type, so we can define methods on it. This
means we can move a lot of methods away from `TyS`, leaving `TyS` as a
barely-used type, which is appropriate given that it's not meant to
be used directly.
- The uniqueness requirement is now explicit, via the `Interned` type.
E.g. the pointer-based `Eq` and `Hash` comes from `Interned`, rather
than via `TyS`, which wasn't obvious at all.
Much of this commit is boring churn. The interesting changes are in
these files:
- compiler/rustc_middle/src/arena.rs
- compiler/rustc_middle/src/mir/visit.rs
- compiler/rustc_middle/src/ty/context.rs
- compiler/rustc_middle/src/ty/mod.rs
Specifically:
- Most mentions of `TyS` are removed. It's very much a dumb struct now;
`Ty` has all the smarts.
- `TyS` now has `crate` visibility instead of `pub`.
- `TyS::make_for_test` is removed in favour of the static `BOOL_TY`,
which just works better with the new structure.
- The `Eq`/`Ord`/`Hash` impls are removed from `TyS`. `Interned`s impls
of `Eq`/`Hash` now suffice. `Ord` is now partly on `Interned`
(pointer-based, for the `Equal` case) and partly on `TyS`
(contents-based, for the other cases).
- There are many tedious sigil adjustments, i.e. adding or removing `*`
or `&`. They seem to be unavoidable.
2022-01-25 14:13:38 +11:00
|
|
|
seen_types, cx, a_tymut.ty, b_tymut.ty, ckind,
|
2020-08-18 02:00:35 +10:00
|
|
|
)
|
|
|
|
}
|
|
|
|
(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
|
Overhaul `TyS` and `Ty`.
Specifically, change `Ty` from this:
```
pub type Ty<'tcx> = &'tcx TyS<'tcx>;
```
to this
```
pub struct Ty<'tcx>(Interned<'tcx, TyS<'tcx>>);
```
There are two benefits to this.
- It's now a first class type, so we can define methods on it. This
means we can move a lot of methods away from `TyS`, leaving `TyS` as a
barely-used type, which is appropriate given that it's not meant to
be used directly.
- The uniqueness requirement is now explicit, via the `Interned` type.
E.g. the pointer-based `Eq` and `Hash` comes from `Interned`, rather
than via `TyS`, which wasn't obvious at all.
Much of this commit is boring churn. The interesting changes are in
these files:
- compiler/rustc_middle/src/arena.rs
- compiler/rustc_middle/src/mir/visit.rs
- compiler/rustc_middle/src/ty/context.rs
- compiler/rustc_middle/src/ty/mod.rs
Specifically:
- Most mentions of `TyS` are removed. It's very much a dumb struct now;
`Ty` has all the smarts.
- `TyS` now has `crate` visibility instead of `pub`.
- `TyS::make_for_test` is removed in favour of the static `BOOL_TY`,
which just works better with the new structure.
- The `Eq`/`Ord`/`Hash` impls are removed from `TyS`. `Interned`s impls
of `Eq`/`Hash` now suffice. `Ord` is now partly on `Interned`
(pointer-based, for the `Equal` case) and partly on `TyS`
(contents-based, for the other cases).
- There are many tedious sigil adjustments, i.e. adding or removing `*`
or `&`. They seem to be unavoidable.
2022-01-25 14:13:38 +11:00
|
|
|
&& structurally_same_type_impl(seen_types, cx, *a_ty, *b_ty, ckind)
|
2020-08-18 02:00:35 +10:00
|
|
|
}
|
|
|
|
(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| {
|
Overhaul `TyS` and `Ty`.
Specifically, change `Ty` from this:
```
pub type Ty<'tcx> = &'tcx TyS<'tcx>;
```
to this
```
pub struct Ty<'tcx>(Interned<'tcx, TyS<'tcx>>);
```
There are two benefits to this.
- It's now a first class type, so we can define methods on it. This
means we can move a lot of methods away from `TyS`, leaving `TyS` as a
barely-used type, which is appropriate given that it's not meant to
be used directly.
- The uniqueness requirement is now explicit, via the `Interned` type.
E.g. the pointer-based `Eq` and `Hash` comes from `Interned`, rather
than via `TyS`, which wasn't obvious at all.
Much of this commit is boring churn. The interesting changes are in
these files:
- compiler/rustc_middle/src/arena.rs
- compiler/rustc_middle/src/mir/visit.rs
- compiler/rustc_middle/src/ty/context.rs
- compiler/rustc_middle/src/ty/mod.rs
Specifically:
- Most mentions of `TyS` are removed. It's very much a dumb struct now;
`Ty` has all the smarts.
- `TyS` now has `crate` visibility instead of `pub`.
- `TyS::make_for_test` is removed in favour of the static `BOOL_TY`,
which just works better with the new structure.
- The `Eq`/`Ord`/`Hash` impls are removed from `TyS`. `Interned`s impls
of `Eq`/`Hash` now suffice. `Ord` is now partly on `Interned`
(pointer-based, for the `Equal` case) and partly on `TyS`
(contents-based, for the other cases).
- There are many tedious sigil adjustments, i.e. adding or removing `*`
or `&`. They seem to be unavoidable.
2022-01-25 14:13:38 +11:00
|
|
|
structurally_same_type_impl(seen_types, cx, *a, *b, ckind)
|
2020-08-18 02:00:35 +10:00
|
|
|
})
|
|
|
|
&& structurally_same_type_impl(
|
|
|
|
seen_types,
|
|
|
|
cx,
|
|
|
|
a_sig.output(),
|
|
|
|
b_sig.output(),
|
|
|
|
ckind,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
(Tuple(a_substs), Tuple(b_substs)) => {
|
2022-02-07 16:06:31 +01:00
|
|
|
a_substs.iter().eq_by(b_substs.iter(), |a_ty, b_ty| {
|
2020-08-18 02:00:35 +10:00
|
|
|
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) =
|
2020-08-03 00:49:11 +02:00
|
|
|
if is_primitive_or_pointer(a.kind()) { (a, b) } else { (b, a) };
|
2020-08-18 02:00:35 +10:00
|
|
|
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 {
|
2020-10-26 20:02:06 -04:00
|
|
|
let tcx = cx.tcx;
|
2020-06-19 18:14:54 +10:00
|
|
|
if let Some(existing_hid) = self.insert(tcx, this_fi) {
|
|
|
|
let existing_decl_ty = tcx.type_of(tcx.hir().local_def_id(existing_hid));
|
2021-02-01 00:33:38 +01:00
|
|
|
let this_decl_ty = tcx.type_of(this_fi.def_id);
|
2020-06-19 18:14:54 +10:00
|
|
|
debug!(
|
2020-06-28 10:11:04 +10:00
|
|
|
"ClashingExternDeclarations: Comparing existing {:?}: {:?} to this {:?}: {:?}",
|
2021-02-01 00:33:38 +01:00
|
|
|
existing_hid, existing_decl_ty, this_fi.def_id, this_decl_ty
|
2020-06-19 18:14:54 +10:00
|
|
|
);
|
|
|
|
// 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,
|
|
|
|
) {
|
2021-10-20 22:38:10 +02:00
|
|
|
let orig_fi = tcx.hir().expect_foreign_item(existing_hid.expect_owner());
|
2020-06-19 18:14:54 +10:00
|
|
|
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,
|
2021-02-01 00:33:38 +01:00
|
|
|
this_fi.hir_id(),
|
2020-06-19 18:14:54 +10:00
|
|
|
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)
|
2022-01-22 18:49:12 -06:00
|
|
|
.emit();
|
2020-06-19 18:14:54 +10:00
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-04-06 22:01:00 +02:00
|
|
|
|
|
|
|
declare_lint! {
|
|
|
|
/// The `deref_nullptr` lint detects when an null pointer is dereferenced,
|
|
|
|
/// which causes [undefined behavior].
|
|
|
|
///
|
|
|
|
/// ### Example
|
2021-04-08 12:09:32 +02:00
|
|
|
///
|
2021-04-06 22:01:00 +02:00
|
|
|
/// ```rust,no_run
|
2021-04-08 12:09:32 +02:00
|
|
|
/// # #![allow(unused)]
|
2021-04-09 16:13:04 +02:00
|
|
|
/// use std::ptr;
|
2021-04-07 20:53:58 +02:00
|
|
|
/// unsafe {
|
2021-04-09 16:13:04 +02:00
|
|
|
/// let x = &*ptr::null::<i32>();
|
|
|
|
/// let x = ptr::addr_of!(*ptr::null::<i32>());
|
2021-04-08 12:09:32 +02:00
|
|
|
/// let x = *(0 as *const i32);
|
|
|
|
/// }
|
2021-04-06 22:01:00 +02:00
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// {{produces}}
|
|
|
|
///
|
|
|
|
/// ### Explanation
|
|
|
|
///
|
|
|
|
/// Dereferencing a null pointer causes [undefined behavior] even as a place expression,
|
|
|
|
/// like `&*(0 as *const i32)` or `addr_of!(*(0 as *const i32))`.
|
|
|
|
///
|
|
|
|
/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
|
|
|
|
pub DEREF_NULLPTR,
|
|
|
|
Warn,
|
|
|
|
"detects when an null pointer is dereferenced"
|
|
|
|
}
|
|
|
|
|
|
|
|
declare_lint_pass!(DerefNullPtr => [DEREF_NULLPTR]);
|
|
|
|
|
|
|
|
impl<'tcx> LateLintPass<'tcx> for DerefNullPtr {
|
|
|
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) {
|
|
|
|
/// test if expression is a null ptr
|
|
|
|
fn is_null_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
|
|
|
|
match &expr.kind {
|
|
|
|
rustc_hir::ExprKind::Cast(ref expr, ref ty) => {
|
|
|
|
if let rustc_hir::TyKind::Ptr(_) = ty.kind {
|
|
|
|
return is_zero(expr) || is_null_ptr(cx, expr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// check for call to `core::ptr::null` or `core::ptr::null_mut`
|
|
|
|
rustc_hir::ExprKind::Call(ref path, _) => {
|
|
|
|
if let rustc_hir::ExprKind::Path(ref qpath) = path.kind {
|
|
|
|
if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() {
|
2021-10-04 16:11:22 -05:00
|
|
|
return matches!(
|
|
|
|
cx.tcx.get_diagnostic_name(def_id),
|
|
|
|
Some(sym::ptr_null | sym::ptr_null_mut)
|
|
|
|
);
|
2021-04-06 22:01:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
false
|
|
|
|
}
|
|
|
|
|
2021-04-19 15:57:08 +03:00
|
|
|
/// test if expression is the literal `0`
|
2021-04-06 22:01:00 +02:00
|
|
|
fn is_zero(expr: &hir::Expr<'_>) -> bool {
|
|
|
|
match &expr.kind {
|
|
|
|
rustc_hir::ExprKind::Lit(ref lit) => {
|
|
|
|
if let LitKind::Int(a, _) = lit.node {
|
|
|
|
return a == 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
false
|
|
|
|
}
|
|
|
|
|
2021-11-07 10:33:27 +01:00
|
|
|
if let rustc_hir::ExprKind::Unary(rustc_hir::UnOp::Deref, expr_deref) = expr.kind {
|
|
|
|
if is_null_ptr(cx, expr_deref) {
|
|
|
|
cx.struct_span_lint(DEREF_NULLPTR, expr.span, |lint| {
|
|
|
|
let mut err = lint.build("dereferencing a null pointer");
|
|
|
|
err.span_label(expr.span, "this code causes undefined behavior when executed");
|
|
|
|
err.emit();
|
|
|
|
});
|
2021-04-06 22:01:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-08-19 16:34:01 -04:00
|
|
|
|
|
|
|
declare_lint! {
|
|
|
|
/// The `named_asm_labels` lint detects the use of named labels in the
|
|
|
|
/// inline `asm!` macro.
|
|
|
|
///
|
|
|
|
/// ### Example
|
|
|
|
///
|
|
|
|
/// ```rust,compile_fail
|
2021-12-10 00:15:33 +00:00
|
|
|
/// use std::arch::asm;
|
|
|
|
///
|
2021-08-19 16:34:01 -04:00
|
|
|
/// fn main() {
|
|
|
|
/// unsafe {
|
|
|
|
/// asm!("foo: bar");
|
|
|
|
/// }
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// {{produces}}
|
|
|
|
///
|
|
|
|
/// ### Explanation
|
|
|
|
///
|
|
|
|
/// LLVM is allowed to duplicate inline assembly blocks for any
|
|
|
|
/// reason, for example when it is in a function that gets inlined. Because
|
|
|
|
/// of this, GNU assembler [local labels] *must* be used instead of labels
|
|
|
|
/// with a name. Using named labels might cause assembler or linker errors.
|
|
|
|
///
|
2022-01-06 14:39:42 +01:00
|
|
|
/// See the explanation in [Rust By Example] for more details.
|
|
|
|
///
|
2021-08-19 16:34:01 -04:00
|
|
|
/// [local labels]: https://sourceware.org/binutils/docs/as/Symbol-Names.html#Local-Labels
|
2022-01-06 14:39:42 +01:00
|
|
|
/// [Rust By Example]: https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels
|
2021-08-19 16:34:01 -04:00
|
|
|
pub NAMED_ASM_LABELS,
|
|
|
|
Deny,
|
|
|
|
"named labels in inline assembly",
|
|
|
|
}
|
|
|
|
|
|
|
|
declare_lint_pass!(NamedAsmLabels => [NAMED_ASM_LABELS]);
|
|
|
|
|
|
|
|
impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels {
|
|
|
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
|
|
|
|
if let hir::Expr {
|
|
|
|
kind: hir::ExprKind::InlineAsm(hir::InlineAsm { template_strs, .. }),
|
|
|
|
..
|
|
|
|
} = expr
|
|
|
|
{
|
|
|
|
for (template_sym, template_snippet, template_span) in template_strs.iter() {
|
2021-12-15 14:39:23 +11:00
|
|
|
let template_str = template_sym.as_str();
|
2021-08-19 16:34:01 -04:00
|
|
|
let find_label_span = |needle: &str| -> Option<Span> {
|
|
|
|
if let Some(template_snippet) = template_snippet {
|
|
|
|
let snippet = template_snippet.as_str();
|
|
|
|
if let Some(pos) = snippet.find(needle) {
|
|
|
|
let end = pos
|
2021-11-07 10:33:27 +01:00
|
|
|
+ snippet[pos..]
|
2021-08-19 16:34:01 -04:00
|
|
|
.find(|c| c == ':')
|
|
|
|
.unwrap_or(snippet[pos..].len() - 1);
|
|
|
|
let inner = InnerSpan::new(pos, end);
|
|
|
|
return Some(template_span.from_inner(inner));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut found_labels = Vec::new();
|
|
|
|
|
|
|
|
// A semicolon might not actually be specified as a separator for all targets, but it seems like LLVM accepts it always
|
|
|
|
let statements = template_str.split(|c| matches!(c, '\n' | ';'));
|
|
|
|
for statement in statements {
|
|
|
|
// If there's a comment, trim it from the statement
|
|
|
|
let statement = statement.find("//").map_or(statement, |idx| &statement[..idx]);
|
|
|
|
let mut start_idx = 0;
|
|
|
|
for (idx, _) in statement.match_indices(':') {
|
|
|
|
let possible_label = statement[start_idx..idx].trim();
|
|
|
|
let mut chars = possible_label.chars();
|
2021-12-03 03:25:11 +01:00
|
|
|
let Some(c) = chars.next() else {
|
2021-08-19 16:34:01 -04:00
|
|
|
// Empty string means a leading ':' in this section, which is not a label
|
2021-12-03 03:25:11 +01:00
|
|
|
break
|
|
|
|
};
|
|
|
|
// A label starts with an alphabetic character or . or _ and continues with alphanumeric characters, _, or $
|
|
|
|
if (c.is_alphabetic() || matches!(c, '.' | '_'))
|
|
|
|
&& chars.all(|c| c.is_alphanumeric() || matches!(c, '_' | '$'))
|
|
|
|
{
|
|
|
|
found_labels.push(possible_label);
|
|
|
|
} else {
|
|
|
|
// If we encounter a non-label, there cannot be any further labels, so stop checking
|
2021-08-19 16:34:01 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
start_idx = idx + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
debug!("NamedAsmLabels::check_expr(): found_labels: {:#?}", &found_labels);
|
|
|
|
|
|
|
|
if found_labels.len() > 0 {
|
|
|
|
let spans = found_labels
|
|
|
|
.into_iter()
|
|
|
|
.filter_map(|label| find_label_span(label))
|
|
|
|
.collect::<Vec<Span>>();
|
|
|
|
// If there were labels but we couldn't find a span, combine the warnings and use the template span
|
|
|
|
let target_spans: MultiSpan =
|
|
|
|
if spans.len() > 0 { spans.into() } else { (*template_span).into() };
|
|
|
|
|
|
|
|
cx.lookup_with_diagnostics(
|
|
|
|
NAMED_ASM_LABELS,
|
|
|
|
Some(target_spans),
|
|
|
|
|diag| {
|
|
|
|
let mut err =
|
|
|
|
diag.build("avoid using named labels in inline assembly");
|
|
|
|
err.emit();
|
|
|
|
},
|
|
|
|
BuiltinLintDiagnostics::NamedAsmLabel(
|
|
|
|
"only local labels of the form `<number>:` should be used in inline asm"
|
|
|
|
.to_string(),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|