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
|
|
|
|
2022-10-13 10:13:02 +01:00
|
|
|
use crate::fluent_generated as fluent;
|
2020-07-13 23:06:35 +10:00
|
|
|
use crate::{
|
2023-04-09 17:35:02 -04:00
|
|
|
errors::BuiltinEllipsisInclusiveRangePatterns,
|
2022-10-22 21:50:44 -04:00
|
|
|
lints::{
|
2022-11-13 09:46:31 -05:00
|
|
|
BuiltinAnonymousParams, BuiltinBoxPointers, BuiltinClashingExtern,
|
|
|
|
BuiltinClashingExternSub, BuiltinConstNoMangle, BuiltinDeprecatedAttrLink,
|
|
|
|
BuiltinDeprecatedAttrLinkSuggestion, BuiltinDeprecatedAttrUsed, BuiltinDerefNullptr,
|
|
|
|
BuiltinEllipsisInclusiveRangePatternsLint, BuiltinExplicitOutlives,
|
|
|
|
BuiltinExplicitOutlivesSuggestion, BuiltinIncompleteFeatures,
|
2022-11-10 19:32:30 -05:00
|
|
|
BuiltinIncompleteFeaturesHelp, BuiltinIncompleteFeaturesNote, BuiltinKeywordIdents,
|
|
|
|
BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc,
|
2022-10-22 21:50:44 -04:00
|
|
|
BuiltinMutablesTransmutes, BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns,
|
2022-11-13 09:46:31 -05:00
|
|
|
BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasGenericBounds,
|
|
|
|
BuiltinTypeAliasGenericBoundsSuggestion, BuiltinTypeAliasWhereClause,
|
|
|
|
BuiltinUnexpectedCliConfigName, BuiltinUnexpectedCliConfigValue,
|
|
|
|
BuiltinUngatedAsyncFnTrackCaller, BuiltinUnnameableTestItems, BuiltinUnpermittedTypeInit,
|
|
|
|
BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, BuiltinUnsafe,
|
|
|
|
BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub,
|
|
|
|
BuiltinWhileTrue, SuggestChangingAssocTypes,
|
2022-10-22 21:50:44 -04:00
|
|
|
},
|
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
|
|
|
};
|
2022-11-22 21:15:27 +00:00
|
|
|
use hir::IsAsync;
|
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-10-13 10:13:02 +01:00
|
|
|
use rustc_errors::{Applicability, DecorateLint, 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};
|
2022-11-22 21:15:27 +00:00
|
|
|
use rustc_hir::intravisit::FnKind as HirFnKind;
|
2022-11-05 15:33:58 +00:00
|
|
|
use rustc_hir::{Body, FnDecl, ForeignItemKind, GenericParamKind, Node, PatKind, PredicateOrigin};
|
2022-06-29 15:50:26 +01:00
|
|
|
use rustc_middle::lint::in_external_macro;
|
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;
|
2022-05-09 16:04:31 -04:00
|
|
|
use rustc_middle::ty::subst::GenericArgKind;
|
2022-09-25 22:56:01 +02:00
|
|
|
use rustc_middle::ty::{self, Instance, Ty, TyCtxt, VariantDef};
|
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-12-25 22:16:04 +01:00
|
|
|
use rustc_span::{BytePos, InnerSpan, Span};
|
2023-03-25 18:43:03 -07:00
|
|
|
use rustc_target::abi::{Abi, FIRST_VARIANT};
|
2022-09-28 15:11:27 +02:00
|
|
|
use rustc_trait_selection::infer::{InferCtxtExt, TyCtxtInferExt};
|
2022-11-30 20:41:02 +00:00
|
|
|
use rustc_trait_selection::traits::{self, misc::type_allowed_to_implement_copy};
|
2015-02-25 22:44:44 +11:00
|
|
|
|
2019-02-08 20:35:41 +09:00
|
|
|
use crate::nonstandard_style::{method_context, MethodLateContext};
|
|
|
|
|
2020-01-05 10:07:26 +01:00
|
|
|
use std::fmt::Write;
|
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 {
|
2022-12-07 14:52:28 +11:00
|
|
|
#[inline]
|
2019-06-22 01:30:24 +02:00
|
|
|
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
|
2022-09-29 09:10:57 +10:00
|
|
|
if let ast::ExprKind::While(cond, _, label) = &e.kind
|
2022-10-10 13:40:56 +11:00
|
|
|
&& let cond = pierce_parens(cond)
|
|
|
|
&& let ast::ExprKind::Lit(token_lit) = cond.kind
|
|
|
|
&& let token::Lit { kind: token::Bool, symbol: kw::True, .. } = token_lit
|
|
|
|
&& !cond.span.from_expansion()
|
2022-09-29 09:10:57 +10:00
|
|
|
{
|
|
|
|
let condition_span = e.span.with_hi(cond.span.hi());
|
2022-10-22 21:50:44 -04:00
|
|
|
let replace = format!(
|
2022-09-29 09:10:57 +10:00
|
|
|
"{}loop",
|
|
|
|
label.map_or_else(String::new, |label| format!(
|
|
|
|
"{}: ",
|
|
|
|
label.ident,
|
|
|
|
))
|
2022-10-22 21:50:44 -04:00
|
|
|
);
|
|
|
|
cx.emit_spanned_lint(WHILE_TRUE, condition_span, BuiltinWhileTrue {
|
|
|
|
suggestion: condition_span,
|
|
|
|
replace,
|
|
|
|
});
|
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() {
|
2023-04-19 14:13:22 +00:00
|
|
|
if let GenericArgKind::Type(leaf_ty) = leaf.unpack() && leaf_ty.is_box() {
|
|
|
|
cx.emit_spanned_lint(BOX_POINTERS, span, BuiltinBoxPointers { ty });
|
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 {
|
2018-07-11 23:36:06 +08:00
|
|
|
hir::ItemKind::Fn(..)
|
2019-08-02 11:02:08 +01:00
|
|
|
| hir::ItemKind::TyAlias(..)
|
2018-07-11 23:36:06 +08:00
|
|
|
| hir::ItemKind::Enum(..)
|
|
|
|
| hir::ItemKind::Struct(..)
|
|
|
|
| hir::ItemKind::Union(..) => {
|
2023-02-07 01:29:48 -07:00
|
|
|
self.check_heap_type(cx, it.span, cx.tcx.type_of(it.owner_id).subst_identity())
|
2016-10-27 04:52:10 +03:00
|
|
|
}
|
2016-11-10 16:49:53 +02:00
|
|
|
_ => (),
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
// If it's a struct, we also have to check the fields' types
|
2019-09-26 17:51:36 +01:00
|
|
|
match it.kind {
|
2018-07-11 23:36:06 +08:00
|
|
|
hir::ItemKind::Struct(ref struct_def, _) | hir::ItemKind::Union(ref struct_def, _) => {
|
2022-11-06 19:46:55 +00:00
|
|
|
for field in struct_def.fields() {
|
2023-02-06 17:48:12 -07:00
|
|
|
self.check_heap_type(
|
|
|
|
cx,
|
|
|
|
field.span,
|
2023-02-07 01:29:48 -07:00
|
|
|
cx.tcx.type_of(field.def_id).subst_identity(),
|
2023-02-06 17:48:12 -07:00
|
|
|
);
|
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 {
|
2018-04-05 03:20:21 +03:00
|
|
|
let variant = cx
|
2020-07-17 08:47:04 +00:00
|
|
|
.typeck_results()
|
2018-04-05 03:20:21 +03: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 {
|
2018-06-10 19:33:30 +03:00
|
|
|
if cx.tcx.find_field_index(ident, &variant)
|
2022-12-04 17:59:21 +00:00
|
|
|
== Some(cx.typeck_results().field_index(fieldpat.hir_id))
|
2019-08-15 02:35:36 +03:00
|
|
|
{
|
2022-10-22 21:50:44 -04:00
|
|
|
cx.emit_spanned_lint(
|
2022-09-16 11:01:02 +04:00
|
|
|
NON_SHORTHAND_FIELD_PATTERNS,
|
|
|
|
fieldpat.span,
|
2022-10-22 21:50:44 -04:00
|
|
|
BuiltinNonShorthandFieldPatterns {
|
|
|
|
ident,
|
|
|
|
suggestion: fieldpat.span,
|
|
|
|
prefix: binding_annot.prefix_str(),
|
2022-09-16 11:01:02 +04:00
|
|
|
},
|
|
|
|
);
|
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-11-12 20:18:55 -05:00
|
|
|
decorate: impl for<'a> DecorateLint<'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;
|
|
|
|
}
|
|
|
|
|
2022-11-12 20:18:55 -05:00
|
|
|
cx.emit_spanned_lint(UNSAFE_CODE, span, decorate);
|
2022-05-16 18:04:54 +01:00
|
|
|
}
|
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) {
|
2022-11-12 20:18:55 -05:00
|
|
|
self.report_unsafe(cx, attr.span, BuiltinUnsafe::AllowInternalUnsafe);
|
2019-01-09 20:53:33 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-07 14:52:28 +11:00
|
|
|
#[inline]
|
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) {
|
2022-11-12 20:18:55 -05:00
|
|
|
self.report_unsafe(cx, blk.span, BuiltinUnsafe::UnsafeBlock);
|
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 {
|
2022-09-16 11:01:02 +04:00
|
|
|
ast::ItemKind::Trait(box ast::Trait { unsafety: ast::Unsafe::Yes(_), .. }) => {
|
2022-11-12 20:18:55 -05:00
|
|
|
self.report_unsafe(cx, it.span, BuiltinUnsafe::UnsafeTrait);
|
2022-09-16 11:01:02 +04:00
|
|
|
}
|
2015-02-25 22:44:44 +11:00
|
|
|
|
2022-09-16 11:01:02 +04:00
|
|
|
ast::ItemKind::Impl(box ast::Impl { unsafety: ast::Unsafe::Yes(_), .. }) => {
|
2022-11-12 20:18:55 -05:00
|
|
|
self.report_unsafe(cx, it.span, BuiltinUnsafe::UnsafeImpl);
|
2022-09-16 11:01:02 +04:00
|
|
|
}
|
2015-02-25 22:44:44 +11:00
|
|
|
|
2020-05-14 17:31:06 +02:00
|
|
|
ast::ItemKind::Fn(..) => {
|
2023-03-19 21:32:34 +04:00
|
|
|
if let Some(attr) = attr::find_by_name(&it.attrs, sym::no_mangle) {
|
2022-11-12 20:18:55 -05:00
|
|
|
self.report_unsafe(cx, attr.span, BuiltinUnsafe::NoMangleFn);
|
2020-05-14 17:31:06 +02:00
|
|
|
}
|
2022-05-16 18:04:54 +01:00
|
|
|
|
2023-03-19 21:32:34 +04:00
|
|
|
if let Some(attr) = attr::find_by_name(&it.attrs, sym::export_name) {
|
2022-11-12 20:18:55 -05:00
|
|
|
self.report_unsafe(cx, attr.span, BuiltinUnsafe::ExportNameFn);
|
2020-05-15 17:36:19 +02:00
|
|
|
}
|
2022-05-16 18:04:54 +01:00
|
|
|
|
2023-03-19 21:32:34 +04:00
|
|
|
if let Some(attr) = attr::find_by_name(&it.attrs, sym::link_section) {
|
2022-11-12 20:18:55 -05:00
|
|
|
self.report_unsafe(cx, attr.span, BuiltinUnsafe::LinkSectionFn);
|
2022-05-16 18:04:54 +01:00
|
|
|
}
|
2020-05-14 17:31:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
ast::ItemKind::Static(..) => {
|
2023-03-19 21:32:34 +04:00
|
|
|
if let Some(attr) = attr::find_by_name(&it.attrs, sym::no_mangle) {
|
2022-11-12 20:18:55 -05:00
|
|
|
self.report_unsafe(cx, attr.span, BuiltinUnsafe::NoMangleStatic);
|
2020-05-14 17:31:06 +02:00
|
|
|
}
|
2022-05-16 18:04:54 +01:00
|
|
|
|
2023-03-19 21:32:34 +04:00
|
|
|
if let Some(attr) = attr::find_by_name(&it.attrs, sym::export_name) {
|
2022-11-12 20:18:55 -05:00
|
|
|
self.report_unsafe(cx, attr.span, BuiltinUnsafe::ExportNameStatic);
|
2020-05-15 17:36:19 +02:00
|
|
|
}
|
2022-05-16 18:04:54 +01:00
|
|
|
|
2023-03-19 21:32:34 +04:00
|
|
|
if let Some(attr) = attr::find_by_name(&it.attrs, sym::link_section) {
|
2022-11-12 20:18:55 -05:00
|
|
|
self.report_unsafe(cx, attr.span, BuiltinUnsafe::LinkSectionStatic);
|
2022-05-16 18:04:54 +01: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 {
|
2023-03-19 21:32:34 +04:00
|
|
|
if let Some(attr) = attr::find_by_name(&it.attrs, sym::no_mangle) {
|
2022-11-12 20:18:55 -05:00
|
|
|
self.report_unsafe(cx, attr.span, BuiltinUnsafe::NoMangleMethod);
|
2021-08-08 02:10:57 +08:00
|
|
|
}
|
2023-03-19 21:32:34 +04:00
|
|
|
if let Some(attr) = attr::find_by_name(&it.attrs, sym::export_name) {
|
2022-11-12 20:18:55 -05:00
|
|
|
self.report_unsafe(cx, attr.span, BuiltinUnsafe::ExportNameMethod);
|
2021-08-08 02:10:57 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-30 00:18:54 +01:00
|
|
|
fn check_fn(&mut self, cx: &EarlyContext<'_>, fk: FnKind<'_>, span: Span, _: ast::NodeId) {
|
|
|
|
if let FnKind::Fn(
|
|
|
|
ctxt,
|
|
|
|
_,
|
2020-01-30 02:42:33 +01:00
|
|
|
ast::FnSig { header: ast::FnHeader { unsafety: ast::Unsafe::Yes(_), .. }, .. },
|
2020-01-30 00:18:54 +01:00
|
|
|
_,
|
2021-11-19 22:03:43 +01:00
|
|
|
_,
|
2020-01-30 00:18:54 +01:00
|
|
|
body,
|
|
|
|
) = fk
|
|
|
|
{
|
2022-11-12 20:18:55 -05:00
|
|
|
let decorator = match ctxt {
|
2020-01-30 00:18:54 +01:00
|
|
|
FnCtxt::Foreign => return,
|
2022-11-12 20:18:55 -05:00
|
|
|
FnCtxt::Free => BuiltinUnsafe::DeclUnsafeFn,
|
|
|
|
FnCtxt::Assoc(_) if body.is_none() => BuiltinUnsafe::DeclUnsafeMethod,
|
|
|
|
FnCtxt::Assoc(_) => BuiltinUnsafe::ImplUnsafeMethod,
|
2020-01-30 00:18:54 +01:00
|
|
|
};
|
2022-11-12 20:18:55 -05:00
|
|
|
self.report_unsafe(cx, span, decorator);
|
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>,
|
|
|
|
}
|
|
|
|
|
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 {
|
2022-02-13 19:38:36 +01:00
|
|
|
MissingDoc { doc_hidden_stack: vec![false] }
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
fn doc_hidden(&self) -> bool {
|
|
|
|
*self.doc_hidden_stack.last().expect("empty doc_hidden_stack")
|
|
|
|
}
|
|
|
|
|
|
|
|
fn check_missing_docs_attrs(
|
|
|
|
&self,
|
2020-06-25 23:41:36 +03:00
|
|
|
cx: &LateContext<'_>,
|
2021-07-28 18:23:40 +03:00
|
|
|
def_id: LocalDefId,
|
2020-03-21 21:32:35 -05:00
|
|
|
article: &'static str,
|
|
|
|
desc: &'static str,
|
2016-10-09 09:38:07 +05:30
|
|
|
) {
|
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 {
|
2022-09-22 16:19:53 +03:00
|
|
|
if !cx.effective_visibilities.is_exported(def_id) {
|
2015-02-25 22:44:44 +11:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-02 09:31:56 +02:00
|
|
|
let attrs = cx.tcx.hir().attrs(cx.tcx.hir().local_def_id_to_hir_id(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 {
|
2022-10-22 21:50:44 -04:00
|
|
|
cx.emit_spanned_lint(
|
2022-09-16 11:01:02 +04:00
|
|
|
MISSING_DOCS,
|
|
|
|
cx.tcx.def_span(def_id),
|
2022-10-22 21:50:44 -04:00
|
|
|
BuiltinMissingDoc { article, desc },
|
2022-09-16 11:01:02 +04:00
|
|
|
);
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-25 23:41:36 +03:00
|
|
|
impl<'tcx> LateLintPass<'tcx> for MissingDoc {
|
2022-12-07 14:52:28 +11:00
|
|
|
#[inline]
|
2021-07-29 12:00:41 -05:00
|
|
|
fn enter_lint_attrs(&mut self, _cx: &LateContext<'_>, attrs: &[ast::Attribute]) {
|
2016-10-09 09:38:07 +05:30
|
|
|
let doc_hidden = self.doc_hidden()
|
|
|
|
|| attrs.iter().any(|attr| {
|
2021-07-29 12:00:41 -05:00
|
|
|
attr.has_name(sym::doc)
|
2016-10-09 09:38:07 +05:30
|
|
|
&& match attr.meta_item_list() {
|
2015-02-25 22:44:44 +11:00
|
|
|
None => false,
|
2019-05-08 13:21:18 +10:00
|
|
|
Some(l) => attr::list_contains_name(&l, sym::hidden),
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
});
|
|
|
|
self.doc_hidden_stack.push(doc_hidden);
|
|
|
|
}
|
|
|
|
|
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<'_>) {
|
2022-07-04 17:23:24 +09:00
|
|
|
self.check_missing_docs_attrs(cx, 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 {
|
2022-02-13 19:38:36 +01:00
|
|
|
hir::ItemKind::Trait(..) => {
|
2019-02-28 22:43:53 +00:00
|
|
|
// Issue #11592: traits are always considered exported, even when private.
|
2022-10-27 14:02:18 +11:00
|
|
|
if cx.tcx.visibility(it.owner_id)
|
2022-02-13 11:30:48 +01:00
|
|
|
== ty::Visibility::Restricted(
|
2022-10-27 14:02:18 +11:00
|
|
|
cx.tcx.parent_module_from_def_id(it.owner_id.def_id).to_def_id(),
|
2022-02-13 11:30:48 +01: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-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
|
|
|
|
2022-10-27 14:02:18 +11:00
|
|
|
let (article, desc) = cx.tcx.article_and_description(it.owner_id.to_def_id());
|
2020-03-15 21:48:09 -05:00
|
|
|
|
2022-10-27 14:02:18 +11:00
|
|
|
self.check_missing_docs_attrs(cx, it.owner_id.def_id, 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<'_>) {
|
2022-10-27 14:02:18 +11:00
|
|
|
let (article, desc) = cx.tcx.article_and_description(trait_item.owner_id.to_def_id());
|
2015-03-29 23:41:54 -04:00
|
|
|
|
2022-10-27 14:02:18 +11:00
|
|
|
self.check_missing_docs_attrs(cx, trait_item.owner_id.def_id, 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<'_>) {
|
2023-01-22 18:00:33 +00:00
|
|
|
let context = method_context(cx, impl_item.owner_id.def_id);
|
2023-01-31 07:44:34 +00:00
|
|
|
|
2023-01-30 11:03:32 +00:00
|
|
|
match context {
|
|
|
|
// If the method is an impl for a trait, don't doc.
|
|
|
|
MethodLateContext::TraitImpl => return,
|
|
|
|
MethodLateContext::TraitAutoImpl => {}
|
|
|
|
// If the method is an impl for an item with docs_hidden, don't doc.
|
|
|
|
MethodLateContext::PlainImpl => {
|
|
|
|
let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id());
|
2023-02-07 01:29:48 -07:00
|
|
|
let impl_ty = cx.tcx.type_of(parent).subst_identity();
|
2023-01-30 11:03:32 +00:00
|
|
|
let outerdef = match impl_ty.kind() {
|
|
|
|
ty::Adt(def, _) => Some(def.did()),
|
|
|
|
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-10-17 14:31:52 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-27 14:02:18 +11:00
|
|
|
let (article, desc) = cx.tcx.article_and_description(impl_item.owner_id.to_def_id());
|
|
|
|
self.check_missing_docs_attrs(cx, impl_item.owner_id.def_id, 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<'_>) {
|
2022-10-27 14:02:18 +11:00
|
|
|
let (article, desc) = cx.tcx.article_and_description(foreign_item.owner_id.to_def_id());
|
|
|
|
self.check_missing_docs_attrs(cx, foreign_item.owner_id.def_id, 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() {
|
2022-11-06 19:46:55 +00:00
|
|
|
self.check_missing_docs_attrs(cx, sf.def_id, "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<'_>) {
|
2022-11-06 19:46:55 +00:00
|
|
|
self.check_missing_docs_attrs(cx, v.def_id, "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<'_>) {
|
2022-10-27 14:02:18 +11:00
|
|
|
if !cx.effective_visibilities.is_reachable(item.owner_id.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
|
|
|
}
|
2022-10-27 14:02:18 +11:00
|
|
|
let def = cx.tcx.adt_def(item.owner_id);
|
2023-02-20 14:52:23 +11:00
|
|
|
(def, cx.tcx.mk_adt(def, ty::List::empty()))
|
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;
|
|
|
|
}
|
2022-10-27 14:02:18 +11:00
|
|
|
let def = cx.tcx.adt_def(item.owner_id);
|
2023-02-20 14:52:23 +11:00
|
|
|
(def, cx.tcx.mk_adt(def, ty::List::empty()))
|
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
|
|
|
}
|
2022-10-27 14:02:18 +11:00
|
|
|
let def = cx.tcx.adt_def(item.owner_id);
|
2023-02-20 14:52:23 +11:00
|
|
|
(def, cx.tcx.mk_adt(def, ty::List::empty()))
|
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;
|
|
|
|
}
|
2022-09-28 15:11:27 +02:00
|
|
|
|
|
|
|
// If the type contains a raw pointer, it may represent something like a handle,
|
|
|
|
// and recommending Copy might be a bad idea.
|
|
|
|
for field in def.all_fields() {
|
|
|
|
let did = field.did;
|
2023-02-07 01:29:48 -07:00
|
|
|
if cx.tcx.type_of(did).subst_identity().is_unsafe_ptr() {
|
2022-09-28 15:11:27 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2018-02-10 13:18:02 -05:00
|
|
|
let param_env = ty::ParamEnv::empty();
|
2022-10-27 14:45:02 +04:00
|
|
|
if ty.is_copy_modulo_regions(cx.tcx, param_env) {
|
2015-02-28 13:31:14 +01:00
|
|
|
return;
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
2022-09-28 15:11:27 +02:00
|
|
|
|
|
|
|
// We shouldn't recommend implementing `Copy` on stateful things,
|
|
|
|
// such as iterators.
|
2022-11-30 20:41:02 +00:00
|
|
|
if let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator)
|
|
|
|
&& cx.tcx
|
|
|
|
.infer_ctxt()
|
|
|
|
.build()
|
|
|
|
.type_implements_trait(iter_trait, [ty], param_env)
|
|
|
|
.must_apply_modulo_regions()
|
|
|
|
{
|
|
|
|
return;
|
2022-09-28 15:11:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Default value of clippy::trivially_copy_pass_by_ref
|
|
|
|
const MAX_SIZE: u64 = 256;
|
|
|
|
|
|
|
|
if let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes()) {
|
|
|
|
if size > MAX_SIZE {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-30 20:41:02 +00:00
|
|
|
if type_allowed_to_implement_copy(
|
2022-02-06 13:03:28 -08:00
|
|
|
cx.tcx,
|
|
|
|
param_env,
|
|
|
|
ty,
|
2023-01-15 12:58:46 +01:00
|
|
|
traits::ObligationCause::misc(item.span, item.owner_id.def_id),
|
2022-02-06 13:03:28 -08:00
|
|
|
)
|
|
|
|
.is_ok()
|
|
|
|
{
|
2022-10-22 21:50:44 -04:00
|
|
|
cx.emit_spanned_lint(MISSING_COPY_IMPLEMENTATIONS, item.span, BuiltinMissingCopyImpl);
|
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<'_>) {
|
2022-10-27 14:02:18 +11:00
|
|
|
if !cx.effective_visibilities.is_reachable(item.owner_id.def_id) {
|
2015-02-25 22:44:44 +11:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-09-26 17:51:36 +01:00
|
|
|
match item.kind {
|
2018-07-11 23:36:06 +08:00
|
|
|
hir::ItemKind::Struct(..) | hir::ItemKind::Union(..) | hir::ItemKind::Enum(..) => {}
|
2015-02-25 22:44:44 +11:00
|
|
|
_ => return,
|
|
|
|
}
|
|
|
|
|
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| {
|
2023-02-07 01:29:48 -07:00
|
|
|
if let Some(ty_def) = cx.tcx.type_of(d).subst_identity().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);
|
|
|
|
}
|
|
|
|
|
2022-10-27 14:02:18 +11:00
|
|
|
if !self.impling_types.as_ref().unwrap().contains(&item.owner_id.def_id) {
|
2022-10-22 21:50:44 -04:00
|
|
|
cx.emit_spanned_lint(
|
2022-09-16 11:01:02 +04:00
|
|
|
MISSING_DEBUG_IMPLEMENTATIONS,
|
|
|
|
item.span,
|
2022-10-22 21:50:44 -04:00
|
|
|
BuiltinMissingDebugImpl { tcx: cx.tcx, def_id: debug },
|
2022-09-16 11:01:02 +04: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 {
|
2022-09-16 11:01:02 +04:00
|
|
|
let ty_snip = cx.sess().source_map().span_to_snippet(arg.ty.span);
|
2018-02-17 17:33:27 -06:00
|
|
|
|
2022-09-16 11:01:02 +04:00
|
|
|
let (ty_snip, appl) = if let Ok(ref snip) = ty_snip {
|
|
|
|
(snip.as_str(), Applicability::MachineApplicable)
|
|
|
|
} else {
|
|
|
|
("<type>", Applicability::HasPlaceholders)
|
|
|
|
};
|
2022-10-22 21:50:44 -04:00
|
|
|
cx.emit_spanned_lint(
|
2022-09-16 11:01:02 +04:00
|
|
|
ANONYMOUS_PARAMETERS,
|
|
|
|
arg.pat.span,
|
2022-10-22 21:50:44 -04:00
|
|
|
BuiltinAnonymousParams { suggestion: (arg.pat.span, appl), ty_snip },
|
|
|
|
);
|
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 {
|
|
|
|
DeprecatedAttr { depr_attrs: deprecated_attributes() }
|
|
|
|
}
|
|
|
|
}
|
2016-10-11 15:51:27 +13:00
|
|
|
|
|
|
|
impl EarlyLintPass for DeprecatedAttr {
|
2019-02-08 20:35:41 +09:00
|
|
|
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) {
|
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) {
|
2018-09-17 03:40:31 +00:00
|
|
|
if let &AttributeGate::Gated(
|
|
|
|
Stability::Deprecated(link, suggestion),
|
2021-01-02 20:09:17 +01:00
|
|
|
name,
|
|
|
|
reason,
|
2016-10-18 18:04:28 +13:00
|
|
|
_,
|
2021-11-12 20:15:14 +08:00
|
|
|
) = gate
|
2016-10-18 18:04:28 +13:00
|
|
|
{
|
2022-11-13 09:46:31 -05:00
|
|
|
let suggestion = match suggestion {
|
|
|
|
Some(msg) => {
|
|
|
|
BuiltinDeprecatedAttrLinkSuggestion::Msg { suggestion: attr.span, msg }
|
|
|
|
}
|
|
|
|
None => {
|
|
|
|
BuiltinDeprecatedAttrLinkSuggestion::Default { suggestion: attr.span }
|
|
|
|
}
|
|
|
|
};
|
|
|
|
cx.emit_spanned_lint(
|
2022-09-16 11:01:02 +04:00
|
|
|
DEPRECATED,
|
|
|
|
attr.span,
|
2022-11-13 09:46:31 -05:00
|
|
|
BuiltinDeprecatedAttrLink { name, reason, link, suggestion },
|
2022-09-16 11:01:02 +04:00
|
|
|
);
|
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) {
|
2022-10-22 21:50:44 -04:00
|
|
|
cx.emit_spanned_lint(
|
2022-09-16 11:01:02 +04:00
|
|
|
DEPRECATED,
|
|
|
|
attr.span,
|
2022-10-22 21:50:44 -04:00
|
|
|
BuiltinDeprecatedAttrUsed {
|
|
|
|
name: pprust::path_to_string(&attr.get_normal_item().path),
|
|
|
|
suggestion: attr.span,
|
2022-09-16 11:01:02 +04:00
|
|
|
},
|
|
|
|
);
|
2019-09-14 17:53:14 -04:00
|
|
|
}
|
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) {
|
2022-10-22 21:50:44 -04:00
|
|
|
let sub = match attr.kind {
|
|
|
|
AttrKind::DocComment(CommentKind::Line, _) | AttrKind::Normal(..) => {
|
|
|
|
BuiltinUnusedDocCommentSub::PlainHelp
|
|
|
|
}
|
|
|
|
AttrKind::DocComment(CommentKind::Block, _) => {
|
|
|
|
BuiltinUnusedDocCommentSub::BlockHelp
|
|
|
|
}
|
|
|
|
};
|
|
|
|
cx.emit_spanned_lint(
|
2022-09-16 11:01:02 +04:00
|
|
|
UNUSED_DOC_COMMENTS,
|
|
|
|
span,
|
2022-10-22 21:50:44 -04:00
|
|
|
BuiltinUnusedDocComment { kind: node_kind, label: node_span, sub },
|
2022-09-16 11:01:02 +04:00
|
|
|
);
|
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) {
|
2022-06-30 16:17:38 -04:00
|
|
|
warn_if_doc(cx, block.span, "blocks", &block.attrs());
|
2022-03-02 17:58:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
|
|
|
|
if let ast::ItemKind::ForeignMod(_) = item.kind {
|
2022-06-30 16:17:38 -04:00
|
|
|
warn_if_doc(cx, item.span, "extern blocks", &item.attrs);
|
2022-03-02 17:58:10 +01:00
|
|
|
}
|
|
|
|
}
|
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 { .. } => {
|
2022-10-22 21:50:44 -04:00
|
|
|
cx.emit_spanned_lint(
|
2022-09-16 11:01:02 +04:00
|
|
|
NO_MANGLE_GENERIC_ITEMS,
|
|
|
|
span,
|
2022-10-22 21:50:44 -04:00
|
|
|
BuiltinNoMangleGeneric { suggestion: no_mangle_attr.span },
|
2022-09-16 11:01:02 +04:00
|
|
|
);
|
2021-08-08 02:10:57 +08:00
|
|
|
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, _) => {
|
2023-03-19 21:32:34 +04:00
|
|
|
if let Some(no_mangle_attr) = attr::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(..) => {
|
2023-03-19 21:32:34 +04:00
|
|
|
if attr::contains_name(attrs, sym::no_mangle) {
|
2022-10-22 21:50:44 -04:00
|
|
|
// 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 suggestion = it.span.with_hi(BytePos(it.span.lo().0 + start + 5));
|
|
|
|
|
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
|
2022-10-22 21:50:44 -04:00
|
|
|
cx.emit_spanned_lint(
|
2022-09-16 11:01:02 +04:00
|
|
|
NO_MANGLE_CONST_ITEMS,
|
|
|
|
it.span,
|
2022-10-22 21:50:44 -04:00
|
|
|
BuiltinConstNoMangle { suggestion },
|
2022-09-16 11:01:02 +04:00
|
|
|
);
|
2015-02-25 22:44:44 +11:00
|
|
|
}
|
|
|
|
}
|
2022-02-05 15:26:49 +01:00
|
|
|
hir::ItemKind::Impl(hir::Impl { generics, items, .. }) => {
|
|
|
|
for it in *items {
|
2021-08-08 02:10:57 +08:00
|
|
|
if let hir::AssocItemKind::Fn { .. } = it.kind {
|
2023-03-19 21:32:34 +04:00
|
|
|
if let Some(no_mangle_attr) =
|
|
|
|
attr::find_by_name(cx.tcx.hir().attrs(it.id.hir_id()), sym::no_mangle)
|
2021-08-08 02:10:57 +08:00
|
|
|
{
|
|
|
|
check_no_mangle_on_generic_fn(
|
|
|
|
no_mangle_attr,
|
|
|
|
Some(generics),
|
2022-10-27 14:02:18 +11:00
|
|
|
cx.tcx.hir().get_generics(it.id.owner_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<'_>) {
|
2022-11-23 18:22:26 +00:00
|
|
|
if let Some((&ty::Ref(_, _, from_mutbl), &ty::Ref(_, _, to_mutbl))) =
|
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
|
|
|
{
|
2022-11-23 18:22:26 +00:00
|
|
|
if from_mutbl < to_mutbl {
|
2022-10-22 21:50:44 -04:00
|
|
|
cx.emit_spanned_lint(MUTABLE_TRANSMUTES, expr.span, BuiltinMutablesTransmutes);
|
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-03-31 23:48:48 +02: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 {
|
2022-05-13 13:50:21 +00:00
|
|
|
cx.tcx.is_intrinsic(def_id) && 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 {
|
2022-10-22 21:50:44 -04:00
|
|
|
cx.emit_spanned_lint(UNSTABLE_FEATURES, item.span(), BuiltinUnstableFeatures);
|
2015-05-18 16:37:05 +02:00
|
|
|
}
|
|
|
|
}
|
2022-11-22 21:15:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
declare_lint! {
|
2022-12-07 20:26:56 +00:00
|
|
|
/// The `ungated_async_fn_track_caller` lint warns when the
|
|
|
|
/// `#[track_caller]` attribute is used on an async function, method, or
|
|
|
|
/// closure, without enabling the corresponding unstable feature flag.
|
|
|
|
///
|
|
|
|
/// ### Example
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// #[track_caller]
|
|
|
|
/// async fn foo() {}
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// {{produces}}
|
|
|
|
///
|
|
|
|
/// ### Explanation
|
|
|
|
///
|
|
|
|
/// The attribute must be used in conjunction with the
|
|
|
|
/// [`closure_track_caller` feature flag]. Otherwise, the `#[track_caller]`
|
2023-01-03 15:48:16 +08:00
|
|
|
/// annotation will function as a no-op.
|
2022-12-07 20:26:56 +00:00
|
|
|
///
|
|
|
|
/// [`closure_track_caller` feature flag]: https://doc.rust-lang.org/beta/unstable-book/language-features/closure-track-caller.html
|
2022-11-22 21:15:27 +00:00
|
|
|
UNGATED_ASYNC_FN_TRACK_CALLER,
|
|
|
|
Warn,
|
|
|
|
"enabling track_caller on an async fn is a no-op unless the closure_track_caller feature is enabled"
|
|
|
|
}
|
|
|
|
|
|
|
|
declare_lint_pass!(
|
2023-03-03 04:10:46 +01:00
|
|
|
/// Explains corresponding feature flag must be enabled for the `#[track_caller]` attribute to
|
2022-11-22 21:15:27 +00:00
|
|
|
/// do anything
|
|
|
|
UngatedAsyncFnTrackCaller => [UNGATED_ASYNC_FN_TRACK_CALLER]
|
|
|
|
);
|
|
|
|
|
|
|
|
impl<'tcx> LateLintPass<'tcx> for UngatedAsyncFnTrackCaller {
|
|
|
|
fn check_fn(
|
|
|
|
&mut self,
|
|
|
|
cx: &LateContext<'_>,
|
|
|
|
fn_kind: HirFnKind<'_>,
|
|
|
|
_: &'tcx FnDecl<'_>,
|
|
|
|
_: &'tcx Body<'_>,
|
|
|
|
span: Span,
|
2023-01-22 18:00:33 +00:00
|
|
|
def_id: LocalDefId,
|
2022-11-22 21:15:27 +00:00
|
|
|
) {
|
2022-12-21 03:13:28 +00:00
|
|
|
if fn_kind.asyncness() == IsAsync::Async
|
|
|
|
&& !cx.tcx.features().closure_track_caller
|
2022-11-22 21:15:27 +00:00
|
|
|
// Now, check if the function has the `#[track_caller]` attribute
|
2023-03-13 18:54:05 +00:00
|
|
|
&& let Some(attr) = cx.tcx.get_attr(def_id, sym::track_caller)
|
2023-01-22 18:00:33 +00:00
|
|
|
{
|
|
|
|
cx.emit_spanned_lint(UNGATED_ASYNC_FN_TRACK_CALLER, attr.span, BuiltinUngatedAsyncFnTrackCaller {
|
|
|
|
label: span,
|
|
|
|
parse_sess: &cx.tcx.sess.parse_sess,
|
|
|
|
});
|
|
|
|
}
|
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-02-26 15:11:59 +01:00
|
|
|
fn perform_lint(
|
|
|
|
&self,
|
2020-06-25 23:41:36 +03:00
|
|
|
cx: &LateContext<'_>,
|
2019-02-26 15:11:59 +01:00
|
|
|
what: &str,
|
2021-07-28 18:23:40 +03:00
|
|
|
def_id: LocalDefId,
|
2022-02-13 01:54:13 +01:00
|
|
|
vis_span: Span,
|
2018-06-27 21:23:18 -07:00
|
|
|
exportable: bool,
|
|
|
|
) {
|
|
|
|
let mut applicability = Applicability::MachineApplicable;
|
2022-09-22 16:19:53 +03:00
|
|
|
if cx.tcx.visibility(def_id).is_public() && !cx.effective_visibilities.is_reachable(def_id)
|
|
|
|
{
|
2022-02-13 01:54:13 +01:00
|
|
|
if vis_span.from_expansion() {
|
|
|
|
applicability = Applicability::MaybeIncorrect;
|
|
|
|
}
|
2022-07-23 14:42:54 +02:00
|
|
|
let def_span = cx.tcx.def_span(def_id);
|
2022-10-22 21:50:44 -04:00
|
|
|
cx.emit_spanned_lint(
|
2022-09-16 11:01:02 +04:00
|
|
|
UNREACHABLE_PUB,
|
|
|
|
def_span,
|
2022-10-22 21:50:44 -04:00
|
|
|
BuiltinUnreachablePub {
|
|
|
|
what,
|
|
|
|
suggestion: (vis_span, applicability),
|
|
|
|
help: exportable.then_some(()),
|
2022-09-16 11:01:02 +04: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<'_>) {
|
2022-02-13 19:38:36 +01:00
|
|
|
// Do not warn for fake `use` statements.
|
|
|
|
if let hir::ItemKind::Use(_, hir::UseKind::ListStem) = &item.kind {
|
|
|
|
return;
|
2022-02-13 01:54:13 +01:00
|
|
|
}
|
2022-10-27 14:02:18 +11:00
|
|
|
self.perform_lint(cx, "item", item.owner_id.def_id, item.vis_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>) {
|
2022-10-27 14:02:18 +11:00
|
|
|
self.perform_lint(cx, "item", foreign_item.owner_id.def_id, foreign_item.vis_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<'_>) {
|
2022-10-21 12:50:28 +03:00
|
|
|
let map = cx.tcx.hir();
|
2023-01-03 17:30:35 +00:00
|
|
|
if matches!(map.get_parent(field.hir_id), Node::Variant(_)) {
|
2022-10-21 12:50:28 +03:00
|
|
|
return;
|
|
|
|
}
|
2022-11-06 19:46:55 +00:00
|
|
|
self.perform_lint(cx, "field", field.def_id, field.vis_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<'_>) {
|
2022-02-13 19:38:36 +01:00
|
|
|
// Only lint inherent impl items.
|
2022-10-27 14:02:18 +11:00
|
|
|
if cx.tcx.associated_item(impl_item.owner_id).trait_item_def_id.is_none() {
|
|
|
|
self.perform_lint(cx, "item", impl_item.owner_id.def_id, impl_item.vis_span, false);
|
2022-02-13 01:54:13 +01:00
|
|
|
}
|
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 {
|
2022-11-13 09:46:31 -05:00
|
|
|
pub(crate) 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, _))
|
|
|
|
}
|
2018-03-10 13:32:11 +01:00
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
2020-08-04 14:34:24 +01:00
|
|
|
hir::QPath::Resolved(..) | hir::QPath::LangItem(..) => false,
|
2018-03-10 13:32:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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-06 11:22:24 +01:00
|
|
|
// There must not be a where clause
|
2022-02-07 22:58:30 +01:00
|
|
|
if type_alias_generics.predicates.is_empty() {
|
|
|
|
return;
|
2018-02-18 19:40:35 +01:00
|
|
|
}
|
2022-02-07 22:58:30 +01:00
|
|
|
|
|
|
|
let mut where_spans = Vec::new();
|
|
|
|
let mut inline_spans = Vec::new();
|
|
|
|
let mut inline_sugg = Vec::new();
|
|
|
|
for p in type_alias_generics.predicates {
|
|
|
|
let span = p.span();
|
|
|
|
if p.in_where_clause() {
|
|
|
|
where_spans.push(span);
|
|
|
|
} else {
|
|
|
|
for b in p.bounds() {
|
|
|
|
inline_spans.push(b.span());
|
|
|
|
}
|
|
|
|
inline_sugg.push((span, String::new()));
|
2018-02-18 19:40:35 +01:00
|
|
|
}
|
|
|
|
}
|
2022-02-07 22:58:30 +01:00
|
|
|
|
|
|
|
let mut suggested_changing_assoc_types = false;
|
|
|
|
if !where_spans.is_empty() {
|
2022-11-13 09:46:31 -05:00
|
|
|
let sub = (!suggested_changing_assoc_types).then(|| {
|
|
|
|
suggested_changing_assoc_types = true;
|
|
|
|
SuggestChangingAssocTypes { ty }
|
2022-02-07 22:58:30 +01:00
|
|
|
});
|
2022-11-13 09:46:31 -05:00
|
|
|
cx.emit_spanned_lint(
|
|
|
|
TYPE_ALIAS_BOUNDS,
|
|
|
|
where_spans,
|
|
|
|
BuiltinTypeAliasWhereClause {
|
|
|
|
suggestion: type_alias_generics.where_clause_span,
|
|
|
|
sub,
|
|
|
|
},
|
|
|
|
);
|
2022-02-07 22:58:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if !inline_spans.is_empty() {
|
2022-11-13 09:46:31 -05:00
|
|
|
let suggestion = BuiltinTypeAliasGenericBoundsSuggestion { suggestions: inline_sugg };
|
|
|
|
let sub = (!suggested_changing_assoc_types).then(|| {
|
|
|
|
suggested_changing_assoc_types = true;
|
|
|
|
SuggestChangingAssocTypes { ty }
|
2022-02-07 22:58:30 +01:00
|
|
|
});
|
2022-11-13 09:46:31 -05:00
|
|
|
cx.emit_spanned_lint(
|
|
|
|
TYPE_ALIAS_BOUNDS,
|
|
|
|
inline_spans,
|
|
|
|
BuiltinTypeAliasGenericBounds { suggestion, sub },
|
|
|
|
);
|
2022-02-07 22:58:30 +01:00
|
|
|
}
|
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
|
2022-06-19 09:44:23 +02:00
|
|
|
cx.tcx.ensure().const_eval_poly(def_id);
|
2018-04-20 14:18:29 +02: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();
|
2022-06-19 09:44:23 +02:00
|
|
|
cx.tcx.ensure().eval_static_initializer(def_id);
|
2018-06-04 18:32:06 +02:00
|
|
|
}
|
2018-04-20 14:18:29 +02:00
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-04-26 12:03:08 -07:00
|
|
|
|
2018-05-06 22:52:58 +01:00
|
|
|
declare_lint! {
|
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>) {
|
2023-02-22 02:18:40 +00:00
|
|
|
use rustc_middle::ty::visit::TypeVisitableExt;
|
2022-11-24 18:14:58 -03:00
|
|
|
use rustc_middle::ty::Clause;
|
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 {
|
2022-10-27 14:02:18 +11:00
|
|
|
let predicates = cx.tcx.predicates_of(item.owner_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() {
|
2022-11-24 18:14:58 -03:00
|
|
|
Clause(Clause::Trait(..)) => "trait",
|
|
|
|
Clause(Clause::TypeOutlives(..)) |
|
|
|
|
Clause(Clause::RegionOutlives(..)) => "lifetime",
|
2018-05-06 22:52:58 +01:00
|
|
|
|
2023-02-16 11:55:58 +00:00
|
|
|
// `ConstArgHasType` is never global as `ct` is always a param
|
|
|
|
Clause(Clause::ConstArgHasType(..)) |
|
2018-05-06 22:52:58 +01:00
|
|
|
// Ignore projections, as they can only be global
|
|
|
|
// if the trait bound is global
|
2022-11-24 18:14:58 -03:00
|
|
|
Clause(Clause::Projection(..)) |
|
2023-03-21 22:11:40 +00:00
|
|
|
AliasRelate(..) |
|
2018-05-06 22:52:58 +01:00
|
|
|
// Ignore bounds that a user can't type
|
|
|
|
WellFormed(..) |
|
|
|
|
ObjectSafe(..) |
|
|
|
|
ClosureKind(..) |
|
|
|
|
Subtype(..) |
|
2020-11-21 07:06:16 -05:00
|
|
|
Coerce(..) |
|
2023-02-10 13:43:29 +00:00
|
|
|
// FIXME(generic_const_exprs): `ConstEvaluatable` can be written
|
2020-02-29 10:03:04 +13:00
|
|
|
ConstEvaluatable(..) |
|
2020-09-01 17:58:34 +02:00
|
|
|
ConstEquate(..) |
|
2022-11-02 15:10:05 +00:00
|
|
|
Ambiguous |
|
2020-09-01 17:58:34 +02:00
|
|
|
TypeWellFormedFromEnv(..) => continue,
|
2018-05-06 22:52:58 +01:00
|
|
|
};
|
2022-01-12 03:19:52 +00:00
|
|
|
if predicate.is_global() {
|
2022-10-22 21:50:44 -04:00
|
|
|
cx.emit_spanned_lint(
|
2022-09-16 11:01:02 +04:00
|
|
|
TRIVIAL_BOUNDS,
|
|
|
|
span,
|
2022-10-22 21:50:44 -04:00
|
|
|
BuiltinTrivialBounds { predicate_kind_name, predicate },
|
2022-09-16 11:01:02 +04: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.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
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,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-09 17:35:02 -04:00
|
|
|
let (parentheses, 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 {
|
2023-04-09 17:35:02 -04:00
|
|
|
if parentheses {
|
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 {
|
2023-04-09 17:35:02 -04:00
|
|
|
cx.sess().emit_err(BuiltinEllipsisInclusiveRangePatterns {
|
2022-08-20 12:11:07 -04:00
|
|
|
span: pat.span,
|
|
|
|
suggestion: pat.span,
|
2021-03-16 21:47:06 +01:00
|
|
|
replace,
|
2022-08-20 12:11:07 -04:00
|
|
|
});
|
2021-03-16 21:47:06 +01:00
|
|
|
} else {
|
2022-10-22 21:50:44 -04:00
|
|
|
cx.emit_spanned_lint(
|
|
|
|
ELLIPSIS_INCLUSIVE_RANGE_PATTERNS,
|
|
|
|
pat.span,
|
|
|
|
BuiltinEllipsisInclusiveRangePatternsLint::Parenthesise {
|
|
|
|
suggestion: pat.span,
|
2022-09-16 11:01:02 +04:00
|
|
|
replace,
|
2022-10-22 21:50:44 -04:00
|
|
|
},
|
|
|
|
);
|
2021-03-16 21:47:06 +01:00
|
|
|
}
|
2018-11-10 18:08:50 +00:00
|
|
|
} else {
|
2022-06-13 15:48:40 +09:00
|
|
|
let replace = "..=";
|
2021-04-07 18:20:23 +02:00
|
|
|
if join.edition() >= Edition::Edition2021 {
|
2023-04-09 17:35:02 -04:00
|
|
|
cx.sess().emit_err(BuiltinEllipsisInclusiveRangePatterns {
|
2022-08-20 12:11:07 -04:00
|
|
|
span: pat.span,
|
|
|
|
suggestion: join,
|
|
|
|
replace: replace.to_string(),
|
|
|
|
});
|
2021-03-16 21:47:06 +01:00
|
|
|
} else {
|
2022-10-22 21:50:44 -04:00
|
|
|
cx.emit_spanned_lint(
|
|
|
|
ELLIPSIS_INCLUSIVE_RANGE_PATTERNS,
|
|
|
|
join,
|
|
|
|
BuiltinEllipsisInclusiveRangePatternsLint::NonParenthesise {
|
|
|
|
suggestion: join,
|
|
|
|
},
|
|
|
|
);
|
2021-03-16 21:47:06 +01:00
|
|
|
}
|
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 {
|
2022-09-20 14:11:23 +09:00
|
|
|
boundary: Option<hir::OwnerId>, // 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-09-26 17:51:36 +01:00
|
|
|
if let hir::ItemKind::Mod(..) = it.kind {
|
2018-07-20 18:04:02 -07:00
|
|
|
} else {
|
|
|
|
self.items_nameable = false;
|
2022-10-27 14:02:18 +11:00
|
|
|
self.boundary = Some(it.owner_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());
|
2023-03-19 21:32:34 +04:00
|
|
|
if let Some(attr) = attr::find_by_name(attrs, sym::rustc_test_marker) {
|
2022-10-22 21:50:44 -04:00
|
|
|
cx.emit_spanned_lint(UNNAMEABLE_TEST_ITEMS, attr.span, BuiltinUnnameableTestItems);
|
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<'_>) {
|
2022-10-27 14:02:18 +11:00
|
|
|
if !self.items_nameable && self.boundary == Some(it.owner_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.
|
Remove `TreeAndSpacing`.
A `TokenStream` contains a `Lrc<Vec<(TokenTree, Spacing)>>`. But this is
not quite right. `Spacing` makes sense for `TokenTree::Token`, but does
not make sense for `TokenTree::Delimited`, because a
`TokenTree::Delimited` cannot be joined with another `TokenTree`.
This commit fixes this problem, by adding `Spacing` to `TokenTree::Token`,
changing `TokenStream` to contain a `Lrc<Vec<TokenTree>>`, and removing the
`TreeAndSpacing` typedef.
The commit removes these two impls:
- `impl From<TokenTree> for TokenStream`
- `impl From<TokenTree> for TreeAndSpacing`
These were useful, but also resulted in code with many `.into()` calls
that was hard to read, particularly for anyone not highly familiar with
the relevant types. This commit makes some other changes to compensate:
- `TokenTree::token()` becomes `TokenTree::token_{alone,joint}()`.
- `TokenStream::token_{alone,joint}()` are added.
- `TokenStream::delimited` is added.
This results in things like this:
```rust
TokenTree::token(token::Semi, stmt.span).into()
```
changing to this:
```rust
TokenStream::token_alone(token::Semi, stmt.span)
```
This makes the type of the result, and its spacing, clearer.
These changes also simplifies `Cursor` and `CursorRef`, because they no longer
need to distinguish between `next` and `next_with_spacing`.
2022-07-28 10:31:04 +10:00
|
|
|
TokenTree::Token(token, _) => {
|
2019-06-05 14:17:56 +03:00
|
|
|
if let Some((ident, false)) = token.ident() {
|
|
|
|
self.check_ident_token(cx, UnderMacro(true), ident);
|
2019-12-22 17:42:04 -05:00
|
|
|
}
|
2018-07-14 16:40:17 +02:00
|
|
|
}
|
2018-11-30 10:02:04 +11:00
|
|
|
TokenTree::Delimited(_, _, tts) => self.check_tokens(cx, tts),
|
2018-07-14 16:40:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-27 14:43:29 +01:00
|
|
|
fn check_ident_token(
|
|
|
|
&mut self,
|
|
|
|
cx: &EarlyContext<'_>,
|
|
|
|
UnderMacro(under_macro): UnderMacro,
|
2020-04-19 13:00:18 +02:00
|
|
|
ident: Ident,
|
2019-03-27 14:43:29 +01: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`.
|
2023-03-14 12:16:19 +00:00
|
|
|
if cx.sess().parse_sess.raw_identifier_spans.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
|
|
|
|
2022-10-22 21:50:44 -04:00
|
|
|
cx.emit_spanned_lint(
|
2022-09-16 11:01:02 +04:00
|
|
|
KEYWORD_IDENTS,
|
|
|
|
ident.span,
|
2022-10-22 21:50:44 -04:00
|
|
|
BuiltinKeywordIdents { kw: ident, next: next_edition, suggestion: ident.span },
|
2022-09-16 11:01:02 +04: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 {
|
2022-09-12 13:50:18 +10:00
|
|
|
fn check_mac_def(&mut self, cx: &EarlyContext<'_>, mac_def: &ast::MacroDef) {
|
2022-11-18 11:24:21 +11:00
|
|
|
self.check_tokens(cx, mac_def.body.tokens.clone());
|
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) {
|
2022-11-18 11:24:21 +11:00
|
|
|
self.check_tokens(cx, mac.args.tokens.clone());
|
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>(
|
2022-11-25 16:35:27 -03:00
|
|
|
inferred_outlives: &'tcx [(ty::Clause<'tcx>, Span)],
|
2022-05-24 13:00:36 +02:00
|
|
|
def_id: DefId,
|
2019-05-25 10:28:17 +01:00
|
|
|
) -> Vec<ty::Region<'tcx>> {
|
2019-10-18 04:24:22 +03:00
|
|
|
inferred_outlives
|
|
|
|
.iter()
|
2022-11-25 16:35:27 -03:00
|
|
|
.filter_map(|(clause, _)| match *clause {
|
|
|
|
ty::Clause::RegionOutlives(ty::OutlivesPredicate(a, b)) => match *a {
|
2022-05-24 13:00:36 +02:00
|
|
|
ty::ReEarlyBound(ebr) if ebr.def_id == def_id => Some(b),
|
2020-06-18 20:41:43 +02:00
|
|
|
_ => None,
|
|
|
|
},
|
2019-05-25 10:28:17 +01:00
|
|
|
_ => None,
|
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
}
|
in which inferable outlives-requirements are linted
RFC 2093 (tracking issue #44493) lets us leave off
commonsensically inferable `T: 'a` outlives requirements. (A separate
feature-gate was split off for the case of 'static lifetimes, for
which questions still remain.) Detecting these was requested as an
idioms-2018 lint.
It turns out that issuing a correct, autofixable suggestion here is
somewhat subtle in the presence of other bounds and generic
parameters. Basically, we want to handle these three cases:
• One outlives-bound. We want to drop the bound altogether, including
the colon—
MyStruct<'a, T: 'a>
^^^^ help: remove this bound
• An outlives bound first, followed by a trait bound. We want to
delete the outlives bound and the following plus sign (and
hopefully get the whitespace right, too)—
MyStruct<'a, T: 'a + MyTrait>
^^^^^ help: remove this bound
• An outlives bound after a trait bound. We want to delete the
outlives lifetime and the preceding plus sign—
MyStruct<'a, T: MyTrait + 'a>
^^^^^ help: remove this bound
This gets (slightly) even more complicated in the case of where
clauses, where we want to drop the where clause altogether if there's
just the one bound. Hopefully the comments are enough to explain
what's going on!
A script (in Python, sorry) was used to generate the
hopefully-sufficiently-exhaustive UI test input. Some of these are
split off into a different file because rust-lang-nursery/rustfix#141
(and, causally upstream of that, #53934) prevents them from being
`run-rustfix`-tested.
We also make sure to include a UI test of a case (copied from RFC
2093) where the outlives-bound can't be inferred. Special thanks to
Niko Matsakis for pointing out the `inferred_outlives_of` query,
rather than blindly stripping outlives requirements as if we weren't a
production compiler and didn't care.
This concerns #52042.
2018-08-26 12:22:04 -07:00
|
|
|
|
2019-05-25 10:28:17 +01:00
|
|
|
fn lifetimes_outliving_type<'tcx>(
|
2022-11-25 16:35:27 -03:00
|
|
|
inferred_outlives: &'tcx [(ty::Clause<'tcx>, Span)],
|
2019-05-25 10:28:17 +01:00
|
|
|
index: u32,
|
|
|
|
) -> Vec<ty::Region<'tcx>> {
|
2019-10-18 04:24:22 +03:00
|
|
|
inferred_outlives
|
|
|
|
.iter()
|
2022-11-25 16:35:27 -03:00
|
|
|
.filter_map(|(clause, _)| match *clause {
|
|
|
|
ty::Clause::TypeOutlives(ty::OutlivesPredicate(a, b)) => {
|
|
|
|
a.is_param(index).then_some(b)
|
|
|
|
}
|
2019-05-25 10:28:17 +01:00
|
|
|
_ => None,
|
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
|
|
|
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>],
|
2022-12-25 22:16:04 +01:00
|
|
|
predicate_span: Span,
|
2019-05-25 10:28:17 +01:00
|
|
|
) -> Vec<(usize, Span)> {
|
2023-02-06 18:38:52 +00:00
|
|
|
use rustc_middle::middle::resolve_bound_vars::ResolvedArg;
|
2019-05-25 10:28:17 +01:00
|
|
|
|
|
|
|
bounds
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.filter_map(|(i, bound)| {
|
2022-12-22 21:37:21 +01:00
|
|
|
let hir::GenericBound::Outlives(lifetime) = bound else {
|
|
|
|
return None;
|
|
|
|
};
|
|
|
|
|
2023-02-06 18:38:52 +00:00
|
|
|
let is_inferred = match tcx.named_bound_var(lifetime.hir_id) {
|
|
|
|
Some(ResolvedArg::EarlyBound(def_id)) => inferred_outlives
|
2022-12-22 21:37:21 +01:00
|
|
|
.iter()
|
|
|
|
.any(|r| matches!(**r, ty::ReEarlyBound(ebr) if { ebr.def_id == def_id })),
|
|
|
|
_ => false,
|
|
|
|
};
|
|
|
|
|
|
|
|
if !is_inferred {
|
|
|
|
return None;
|
2019-05-25 10:28:17 +01:00
|
|
|
}
|
2022-12-22 21:37:21 +01:00
|
|
|
|
2022-12-25 22:16:04 +01:00
|
|
|
let span = bound.span().find_ancestor_inside(predicate_span)?;
|
|
|
|
if in_external_macro(tcx.sess, span) {
|
2022-12-22 21:37:21 +01:00
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
Some((i, span))
|
2019-05-25 10:28:17 +01:00
|
|
|
})
|
|
|
|
.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<'_>,
|
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_spans: Vec<(usize, Span)>,
|
|
|
|
) -> Vec<Span> {
|
|
|
|
if bounds.is_empty() {
|
|
|
|
return Vec::new();
|
|
|
|
}
|
|
|
|
if bound_spans.len() == bounds.len() {
|
|
|
|
let (_, last_bound_span) = bound_spans[bound_spans.len() - 1];
|
|
|
|
// If all bounds are inferable, we want to delete the colon, so
|
|
|
|
// start from just after the parameter (span passed as argument)
|
|
|
|
vec![lo.to(last_bound_span)]
|
|
|
|
} else {
|
|
|
|
let mut merged = Vec::new();
|
|
|
|
let mut last_merged_i = None;
|
|
|
|
|
|
|
|
let mut from_start = true;
|
|
|
|
for (i, bound_span) in bound_spans {
|
|
|
|
match last_merged_i {
|
2019-05-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);
|
|
|
|
}
|
|
|
|
// If consecutive bounds are inferable, merge their spans
|
|
|
|
Some(h) if i == h + 1 => {
|
|
|
|
if let Some(tail) = merged.last_mut() {
|
|
|
|
// Also eat the trailing `+` if the first
|
|
|
|
// more-than-one bound is inferable
|
|
|
|
let to_span = if from_start && i < bounds.len() {
|
|
|
|
bounds[i + 1].span().shrink_to_lo()
|
|
|
|
} else {
|
|
|
|
bound_span
|
|
|
|
};
|
|
|
|
*tail = tail.to(to_span);
|
|
|
|
last_merged_i = Some(i);
|
|
|
|
} else {
|
|
|
|
bug!("another bound-span visited earlier");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
// When we find a non-inferable bound, subsequent inferable bounds
|
|
|
|
// won't be consecutive from the start (and we'll eat the leading
|
|
|
|
// `+` rather than the trailing one)
|
|
|
|
from_start = false;
|
|
|
|
merged.push(bounds[i - 1].span().shrink_to_hi().to(bound_span));
|
|
|
|
last_merged_i = Some(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
merged
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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<'_>) {
|
2023-02-06 18:38:52 +00:00
|
|
|
use rustc_middle::middle::resolve_bound_vars::ResolvedArg;
|
2019-05-25 10:28:17 +01:00
|
|
|
|
2022-10-27 14:02:18 +11:00
|
|
|
let def_id = item.owner_id.def_id;
|
2022-12-25 22:16:04 +01:00
|
|
|
if let hir::ItemKind::Struct(_, hir_generics)
|
|
|
|
| hir::ItemKind::Enum(_, hir_generics)
|
|
|
|
| hir::ItemKind::Union(_, 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();
|
|
|
|
let mut where_lint_spans = Vec::new();
|
|
|
|
let mut dropped_predicate_count = 0;
|
2022-02-05 15:48:02 +01:00
|
|
|
let num_predicates = hir_generics.predicates.len();
|
|
|
|
for (i, where_predicate) in hir_generics.predicates.iter().enumerate() {
|
2022-12-25 22:16:04 +01:00
|
|
|
let (relevant_lifetimes, bounds, predicate_span, in_where_clause) =
|
|
|
|
match where_predicate {
|
|
|
|
hir::WherePredicate::RegionPredicate(predicate) => {
|
2023-02-06 18:38:52 +00:00
|
|
|
if let Some(ResolvedArg::EarlyBound(region_def_id)) =
|
|
|
|
cx.tcx.named_bound_var(predicate.lifetime.hir_id)
|
2022-12-25 22:16:04 +01:00
|
|
|
{
|
2021-12-03 03:25:11 +01:00
|
|
|
(
|
2022-12-25 22:16:04 +01:00
|
|
|
Self::lifetimes_outliving_lifetime(
|
|
|
|
inferred_outlives,
|
|
|
|
region_def_id,
|
|
|
|
),
|
2021-12-03 03:25:11 +01:00
|
|
|
&predicate.bounds,
|
|
|
|
predicate.span,
|
2022-12-25 22:16:04 +01:00
|
|
|
predicate.in_where_clause,
|
2021-12-03 03:25:11 +01:00
|
|
|
)
|
2022-12-25 22:16:04 +01:00
|
|
|
} else {
|
2019-05-25 10:28:17 +01:00
|
|
|
continue;
|
2019-12-22 17:42:04 -05:00
|
|
|
}
|
2019-05-25 10:28:17 +01:00
|
|
|
}
|
2022-12-25 22:16:04 +01:00
|
|
|
hir::WherePredicate::BoundPredicate(predicate) => {
|
|
|
|
// FIXME we can also infer bounds on associated types,
|
|
|
|
// and should check for them here.
|
|
|
|
match predicate.bounded_ty.kind {
|
|
|
|
hir::TyKind::Path(hir::QPath::Resolved(None, path)) => {
|
|
|
|
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,
|
|
|
|
predicate.origin == PredicateOrigin::WhereClause,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => continue,
|
|
|
|
};
|
2019-05-25 10:28:17 +01:00
|
|
|
if relevant_lifetimes.is_empty() {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2022-12-22 21:37:21 +01:00
|
|
|
let bound_spans = self.collect_outlives_bound_spans(
|
|
|
|
cx.tcx,
|
|
|
|
bounds,
|
|
|
|
&relevant_lifetimes,
|
2022-12-25 22:16:04 +01:00
|
|
|
predicate_span,
|
2022-12-22 21:37:21 +01:00
|
|
|
);
|
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;
|
|
|
|
}
|
|
|
|
|
2023-01-15 21:01:00 +01:00
|
|
|
if drop_predicate {
|
|
|
|
if !in_where_clause {
|
|
|
|
lint_spans.push(predicate_span);
|
|
|
|
} else if predicate_span.from_expansion() {
|
|
|
|
// Don't try to extend the span if it comes from a macro expansion.
|
|
|
|
where_lint_spans.push(predicate_span);
|
|
|
|
} else if i + 1 < num_predicates {
|
|
|
|
// If all the bounds on a predicate were inferable and there are
|
|
|
|
// further predicates, we want to eat the trailing comma.
|
|
|
|
let next_predicate_span = hir_generics.predicates[i + 1].span();
|
|
|
|
if next_predicate_span.from_expansion() {
|
|
|
|
where_lint_spans.push(predicate_span);
|
|
|
|
} else {
|
|
|
|
where_lint_spans
|
|
|
|
.push(predicate_span.to(next_predicate_span.shrink_to_lo()));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Eat the optional trailing comma after the last predicate.
|
|
|
|
let where_span = hir_generics.where_clause_span;
|
|
|
|
if where_span.from_expansion() {
|
|
|
|
where_lint_spans.push(predicate_span);
|
|
|
|
} else {
|
|
|
|
where_lint_spans.push(predicate_span.to(where_span.shrink_to_hi()));
|
|
|
|
}
|
|
|
|
}
|
2019-05-25 10:28:17 +01:00
|
|
|
} else {
|
|
|
|
where_lint_spans.extend(self.consolidate_outlives_bound_spans(
|
2022-12-25 22:16:04 +01:00
|
|
|
predicate_span.shrink_to_lo(),
|
2019-05-25 10:28:17 +01:00
|
|
|
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`)
|
2022-06-05 17:37:45 -07:00
|
|
|
if hir_generics.has_where_clause_predicates && dropped_predicate_count == num_predicates
|
|
|
|
{
|
2022-06-06 21:01:06 -07:00
|
|
|
let where_span = hir_generics.where_clause_span;
|
2019-05-25 10:28:17 +01:00
|
|
|
// 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-05-31 21:31:03 +01: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)
|
2019-05-25 10:28:17 +01:00
|
|
|
};
|
2022-12-25 22:16:04 +01:00
|
|
|
|
|
|
|
// Due to macro expansions, the `full_where_span` might not actually contain all predicates.
|
|
|
|
if where_lint_spans.iter().all(|&sp| full_where_span.contains(sp)) {
|
|
|
|
lint_spans.push(full_where_span);
|
|
|
|
} else {
|
|
|
|
lint_spans.extend(where_lint_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
|
|
|
} else {
|
|
|
|
lint_spans.extend(where_lint_spans);
|
|
|
|
}
|
|
|
|
|
|
|
|
if !lint_spans.is_empty() {
|
2022-12-25 22:16:04 +01:00
|
|
|
// Do not automatically delete outlives requirements from macros.
|
|
|
|
let applicability = if lint_spans.iter().all(|sp| sp.can_be_used_for_suggestions())
|
|
|
|
{
|
|
|
|
Applicability::MachineApplicable
|
|
|
|
} else {
|
|
|
|
Applicability::MaybeIncorrect
|
|
|
|
};
|
|
|
|
|
2023-01-15 21:01:00 +01:00
|
|
|
// Due to macros, there might be several predicates with the same span
|
|
|
|
// and we only want to suggest removing them once.
|
|
|
|
lint_spans.sort_unstable();
|
|
|
|
lint_spans.dedup();
|
|
|
|
|
2022-10-22 21:50:44 -04:00
|
|
|
cx.emit_spanned_lint(
|
2022-09-16 11:01:02 +04:00
|
|
|
EXPLICIT_OUTLIVES_REQUIREMENTS,
|
|
|
|
lint_spans.clone(),
|
2022-10-22 21:50:44 -04:00
|
|
|
BuiltinExplicitOutlives {
|
|
|
|
count: bound_count,
|
|
|
|
suggestion: BuiltinExplicitOutlivesSuggestion {
|
|
|
|
spans: lint_spans,
|
2022-12-25 22:16:04 +01:00
|
|
|
applicability,
|
2022-10-22 21:50:44 -04:00
|
|
|
},
|
2022-09-16 11:01:02 +04: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-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-07-29 02:00:53 +02:00
|
|
|
features
|
|
|
|
.declared_lang_features
|
|
|
|
.iter()
|
|
|
|
.map(|(name, span, _)| (name, span))
|
|
|
|
.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)| {
|
2022-11-10 19:32:30 -05:00
|
|
|
let note = rustc_feature::find_feature_issue(name, GateIssue::Language)
|
|
|
|
.map(|n| BuiltinIncompleteFeaturesNote { n });
|
2023-02-15 17:39:43 +00:00
|
|
|
let help =
|
|
|
|
HAS_MIN_FEATURES.contains(&name).then_some(BuiltinIncompleteFeaturesHelp);
|
2022-10-22 21:50:44 -04:00
|
|
|
cx.emit_spanned_lint(
|
2022-09-16 11:01:02 +04:00
|
|
|
INCOMPLETE_FEATURES,
|
|
|
|
span,
|
2022-11-10 19:32:30 -05:00
|
|
|
BuiltinIncompleteFeatures { name, note, help },
|
2022-10-22 21:50:44 -04: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]);
|
|
|
|
|
2022-11-13 09:46:31 -05:00
|
|
|
/// Information about why a type cannot be initialized this way.
|
|
|
|
pub struct InitError {
|
|
|
|
pub(crate) message: String,
|
|
|
|
/// Spans from struct fields and similar that can be obtained from just the type.
|
|
|
|
pub(crate) span: Option<Span>,
|
|
|
|
/// Used to report a trace through adts.
|
|
|
|
pub(crate) nested: Option<Box<InitError>>,
|
|
|
|
}
|
|
|
|
impl InitError {
|
|
|
|
fn spanned(self, span: Span) -> InitError {
|
|
|
|
Self { span: Some(span), ..self }
|
|
|
|
}
|
|
|
|
|
|
|
|
fn nested(self, nested: impl Into<Option<InitError>>) -> InitError {
|
|
|
|
assert!(self.nested.is_none());
|
|
|
|
Self { nested: nested.into().map(Box::new), ..self }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> From<&'a str> for InitError {
|
|
|
|
fn from(s: &'a str) -> Self {
|
|
|
|
s.to_owned().into()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl From<String> for InitError {
|
|
|
|
fn from(message: String) -> Self {
|
|
|
|
Self { message, span: None, nested: None }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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-08-17 08:39:20 +02: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
|
|
|
/// 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-08-17 11:48:30 +02:00
|
|
|
Lit(lit) => {
|
|
|
|
if let Int(i, _) = lit.node {
|
|
|
|
i == 0
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
2019-12-22 17:42:04 -05:00
|
|
|
}
|
2019-08-17 11:48:30 +02:00
|
|
|
Tup(tup) => tup.iter().all(is_zero),
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
2022-09-01 13:27:31 +09:00
|
|
|
} else if let hir::ExprKind::MethodCall(_, receiver, ..) = 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.
|
2022-09-01 13:27:31 +09:00
|
|
|
if let hir::ExprKind::Call(ref path_expr, _) = receiver.kind {
|
2019-11-02 16:12:33 +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::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
|
|
|
|
2022-09-25 22:56:01 +02:00
|
|
|
fn variant_find_init_error<'tcx>(
|
|
|
|
cx: &LateContext<'tcx>,
|
2022-11-03 09:20:27 +00:00
|
|
|
ty: Ty<'tcx>,
|
2022-09-25 22:56:01 +02:00
|
|
|
variant: &VariantDef,
|
|
|
|
substs: ty::SubstsRef<'tcx>,
|
|
|
|
descr: &str,
|
|
|
|
init: InitKind,
|
|
|
|
) -> Option<InitError> {
|
2022-11-07 10:48:40 +00:00
|
|
|
let mut field_err = variant.fields.iter().find_map(|field| {
|
2022-11-03 10:17:50 +00:00
|
|
|
ty_find_init_error(cx, field.ty(cx.tcx, substs), init).map(|mut err| {
|
2022-11-03 15:30:52 +00:00
|
|
|
if !field.did.is_local() {
|
|
|
|
err
|
|
|
|
} else if err.span.is_none() {
|
2022-11-03 10:17:50 +00:00
|
|
|
err.span = Some(cx.tcx.def_span(field.did));
|
|
|
|
write!(&mut err.message, " (in this {descr})").unwrap();
|
|
|
|
err
|
2022-09-25 22:56:01 +02:00
|
|
|
} else {
|
2022-11-03 10:17:50 +00:00
|
|
|
InitError::from(format!("in this {descr}"))
|
|
|
|
.spanned(cx.tcx.def_span(field.did))
|
|
|
|
.nested(err)
|
2022-09-25 22:56:01 +02:00
|
|
|
}
|
|
|
|
})
|
2022-11-03 09:20:27 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
// Check if this ADT has a constrained layout (like `NonNull` and friends).
|
2022-11-03 09:43:52 +00:00
|
|
|
if let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty)) {
|
2022-11-07 10:48:40 +00:00
|
|
|
if let Abi::Scalar(scalar) | Abi::ScalarPair(scalar, _) = &layout.abi {
|
|
|
|
let range = scalar.valid_range(cx);
|
|
|
|
let msg = if !range.contains(0) {
|
|
|
|
"must be non-null"
|
|
|
|
} else if init == InitKind::Uninit && !scalar.is_always_valid(cx) {
|
|
|
|
// Prefer reporting on the fields over the entire struct for uninit,
|
|
|
|
// as the information bubbles out and it may be unclear why the type can't
|
|
|
|
// be null from just its outside signature.
|
|
|
|
|
|
|
|
"must be initialized inside its custom valid range"
|
|
|
|
} else {
|
|
|
|
return field_err;
|
|
|
|
};
|
|
|
|
if let Some(field_err) = &mut field_err {
|
|
|
|
// Most of the time, if the field error is the same as the struct error,
|
|
|
|
// the struct error only happens because of the field error.
|
|
|
|
if field_err.message.contains(msg) {
|
|
|
|
field_err.message = format!("because {}", field_err.message);
|
2022-11-03 09:43:52 +00:00
|
|
|
}
|
2022-11-03 09:20:27 +00:00
|
|
|
}
|
2022-11-07 10:48:40 +00:00
|
|
|
return Some(InitError::from(format!("`{ty}` {msg}")).nested(field_err));
|
2022-11-03 09:20:27 +00:00
|
|
|
}
|
|
|
|
}
|
2022-11-03 09:43:52 +00:00
|
|
|
field_err
|
2020-07-17 15:01:37 +02:00
|
|
|
}
|
|
|
|
|
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> {
|
2021-01-31 10:32:34 +01:00
|
|
|
use rustc_type_ir::sty::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.
|
2022-11-03 09:10:31 +00:00
|
|
|
Ref(..) => Some("references must be non-null".into()),
|
|
|
|
Adt(..) if ty.is_box() => Some("`Box` must be non-null".into()),
|
|
|
|
FnPtr(..) => Some("function pointers must be non-null".into()),
|
|
|
|
Never => Some("the `!` type has no valid value".into()),
|
2020-08-03 00:49:11 +02:00
|
|
|
RawPtr(tm) if matches!(tm.ty.kind(), Dynamic(..)) =>
|
2019-11-02 11:56:06 +01:00
|
|
|
// raw ptr to dyn Trait
|
2019-12-22 17:42:04 -05:00
|
|
|
{
|
2022-11-03 09:10:31 +00:00
|
|
|
Some("the vtable of a wide raw pointer must be non-null".into())
|
2019-12-22 17:42:04 -05:00
|
|
|
}
|
2019-08-17 13:42:03 +02:00
|
|
|
// Primitive types with other constraints.
|
2019-08-17 09:39:25 +02:00
|
|
|
Bool if init == InitKind::Uninit => {
|
2022-11-03 09:10:31 +00:00
|
|
|
Some("booleans must be either `true` or `false`".into())
|
2019-12-22 17:42:04 -05:00
|
|
|
}
|
2019-08-17 09:39:25 +02:00
|
|
|
Char if init == InitKind::Uninit => {
|
2022-11-03 09:10:31 +00:00
|
|
|
Some("characters must be a valid Unicode codepoint".into())
|
2019-12-22 17:42:04 -05:00
|
|
|
}
|
2022-07-04 23:57:41 +01:00
|
|
|
Int(_) | Uint(_) if init == InitKind::Uninit => {
|
2022-11-07 10:52:48 +00:00
|
|
|
Some("integers must be initialized".into())
|
2022-07-04 23:57:41 +01:00
|
|
|
}
|
2022-11-07 10:52:48 +00:00
|
|
|
Float(_) if init == InitKind::Uninit => Some("floats must be initialized".into()),
|
2022-07-04 23:57:41 +01:00
|
|
|
RawPtr(_) if init == InitKind::Uninit => {
|
2022-11-07 10:52:48 +00:00
|
|
|
Some("raw pointers must be initialized".into())
|
2022-07-04 23:57:41 +01:00
|
|
|
}
|
2022-09-25 22:56:01 +02:00
|
|
|
// Recurse and checks for some compound types. (but not unions)
|
2019-08-07 08:42:50 +02:00
|
|
|
Adt(adt_def, substs) if !adt_def.is_union() => {
|
2022-09-25 22:56:01 +02:00
|
|
|
// Handle structs.
|
|
|
|
if adt_def.is_struct() {
|
|
|
|
return variant_find_init_error(
|
|
|
|
cx,
|
2022-11-03 09:20:27 +00:00
|
|
|
ty,
|
2022-09-25 22:56:01 +02:00
|
|
|
adt_def.non_enum_variant(),
|
|
|
|
substs,
|
|
|
|
"struct field",
|
|
|
|
init,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
// And now, enums.
|
|
|
|
let span = cx.tcx.def_span(adt_def.did());
|
|
|
|
let mut potential_variants = adt_def.variants().iter().filter_map(|variant| {
|
2022-10-04 09:22:39 -05:00
|
|
|
let definitely_inhabited = match variant
|
|
|
|
.inhabited_predicate(cx.tcx, *adt_def)
|
|
|
|
.subst(cx.tcx, substs)
|
|
|
|
.apply_any_module(cx.tcx, cx.param_env)
|
|
|
|
{
|
2023-04-09 17:35:02 -04:00
|
|
|
// Entirely skip uninhabited variants.
|
2022-09-25 22:56:01 +02:00
|
|
|
Some(false) => return None,
|
|
|
|
// Forward the others, but remember which ones are definitely inhabited.
|
|
|
|
Some(true) => true,
|
|
|
|
None => false,
|
|
|
|
};
|
|
|
|
Some((variant, definitely_inhabited))
|
|
|
|
});
|
|
|
|
let Some(first_variant) = potential_variants.next() else {
|
2022-11-03 09:10:31 +00:00
|
|
|
return Some(InitError::from("enums with no inhabited variants have no valid value").spanned(span));
|
2022-09-25 22:56:01 +02:00
|
|
|
};
|
|
|
|
// So we have at least one potentially inhabited variant. Might we have two?
|
|
|
|
let Some(second_variant) = potential_variants.next() else {
|
|
|
|
// There is only one potentially inhabited variant. So we can recursively check that variant!
|
|
|
|
return variant_find_init_error(
|
|
|
|
cx,
|
2022-11-03 09:20:27 +00:00
|
|
|
ty,
|
2022-09-25 22:56:01 +02:00
|
|
|
&first_variant.0,
|
|
|
|
substs,
|
|
|
|
"field of the only potentially inhabited enum variant",
|
|
|
|
init,
|
|
|
|
);
|
|
|
|
};
|
|
|
|
// So we have at least two potentially inhabited variants.
|
|
|
|
// If we can prove that we have at least two *definitely* inhabited variants,
|
|
|
|
// then we have a tag and hence leaving this uninit is definitely disallowed.
|
|
|
|
// (Leaving it zeroed could be okay, depending on which variant is encoded as zero tag.)
|
|
|
|
if init == InitKind::Uninit {
|
|
|
|
let definitely_inhabited = (first_variant.1 as usize)
|
|
|
|
+ (second_variant.1 as usize)
|
|
|
|
+ potential_variants
|
|
|
|
.filter(|(_variant, definitely_inhabited)| *definitely_inhabited)
|
|
|
|
.count();
|
|
|
|
if definitely_inhabited > 1 {
|
2022-11-03 09:10:31 +00:00
|
|
|
return Some(InitError::from(
|
|
|
|
"enums with multiple inhabited variants have to be initialized to a variant",
|
|
|
|
).spanned(span));
|
2020-07-17 15:01:37 +02:00
|
|
|
}
|
2019-08-07 08:42:50 +02:00
|
|
|
}
|
2022-09-25 22:56:01 +02:00
|
|
|
// We couldn't find anything wrong here.
|
|
|
|
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) => {
|
2023-02-14 08:51:19 +00:00
|
|
|
if matches!(len.try_eval_target_usize(cx.tcx, cx.param_env), Some(v) if v > 0) {
|
2022-03-27 13:10:34 -04:00
|
|
|
// 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);
|
2022-11-13 09:46:31 -05:00
|
|
|
if let Some(err) = with_no_trimmed_paths!(ty_find_init_error(cx, conjured_ty, init)) {
|
|
|
|
let msg = match init {
|
|
|
|
InitKind::Zeroed => fluent::lint_builtin_unpermitted_type_init_zeroed,
|
2023-04-17 09:16:07 +02:00
|
|
|
InitKind::Uninit => fluent::lint_builtin_unpermitted_type_init_uninit,
|
2022-11-13 09:46:31 -05:00
|
|
|
};
|
|
|
|
let sub = BuiltinUnpermittedTypeInitSub { err };
|
|
|
|
cx.emit_spanned_lint(
|
2022-09-16 11:01:02 +04:00
|
|
|
INVALID_VALUE,
|
|
|
|
expr.span,
|
2023-02-13 15:38:23 +01:00
|
|
|
BuiltinUnpermittedTypeInit {
|
|
|
|
msg,
|
|
|
|
ty: conjured_ty,
|
|
|
|
label: expr.span,
|
|
|
|
sub,
|
|
|
|
tcx: cx.tcx,
|
|
|
|
},
|
2022-09-16 11:01:02 +04:00
|
|
|
);
|
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.
|
2022-11-05 15:33:58 +00:00
|
|
|
seen_decls: FxHashMap<Symbol, hir::OwnerId>,
|
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 {
|
2022-05-20 19:51:09 -04:00
|
|
|
pub(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
|
|
|
}
|
2022-11-05 15:33:58 +00:00
|
|
|
|
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.
|
2022-11-05 15:33:58 +00:00
|
|
|
fn insert(&mut self, tcx: TyCtxt<'_>, fi: &hir::ForeignItem<'_>) -> Option<hir::OwnerId> {
|
2022-10-27 14:02:18 +11:00
|
|
|
let did = fi.owner_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);
|
2022-11-05 15:33:58 +00:00
|
|
|
if let Some(&existing_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.
|
2022-11-05 15:33:58 +00:00
|
|
|
Some(existing_id)
|
2020-06-19 18:14:54 +10:00
|
|
|
} else {
|
2022-11-05 15:33:58 +00:00
|
|
|
self.seen_decls.insert(name, fi.owner_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)) =
|
2022-10-27 14:02:18 +11:00
|
|
|
tcx.codegen_fn_attrs(fi.owner_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.
|
2023-03-13 18:54:05 +00:00
|
|
|
(overridden_link_name, tcx.get_attr(fi.owner_id, sym::link_name).unwrap().span)
|
2020-06-19 18:14:54 +10:00
|
|
|
})
|
|
|
|
{
|
|
|
|
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.
|
2023-03-20 00:50:03 +01:00
|
|
|
let non_transparent_ty = |mut ty: Ty<'tcx>| -> Ty<'tcx> {
|
2020-08-25 02:24:05 +10:00
|
|
|
loop {
|
2020-08-03 00:49:11 +02:00
|
|
|
if let ty::Adt(def, substs) = *ty.kind() {
|
2022-05-09 16:04:31 -04:00
|
|
|
let is_transparent = def.repr().transparent();
|
2022-03-05 07:28:41 +11:00
|
|
|
let is_non_null = crate::types::nonnull_optimization_guaranteed(tcx, def);
|
2020-08-25 02:24:05 +10:00
|
|
|
debug!(
|
2020-08-25 14:00:24 +10:00
|
|
|
"non_transparent_ty({:?}) -- type is transparent? {}, type is non-null? {}",
|
|
|
|
ty, is_transparent, is_non_null
|
2020-08-25 02:24:05 +10:00
|
|
|
);
|
2020-08-25 14:00:24 +10:00
|
|
|
if is_transparent && !is_non_null {
|
2023-03-20 00:50:03 +01:00
|
|
|
debug_assert_eq!(def.variants().len(), 1);
|
2023-03-25 18:43:03 -07:00
|
|
|
let v = &def.variant(FIRST_VARIANT);
|
2023-03-20 00:50:03 +01:00
|
|
|
// continue with `ty`'s non-ZST field,
|
|
|
|
// otherwise `ty` is a ZST and we can return
|
|
|
|
if let Some(field) = transparent_newtype_field(tcx, v) {
|
|
|
|
ty = field.ty(tcx, substs);
|
|
|
|
continue;
|
|
|
|
}
|
2020-08-25 02:24:05 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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.
|
2023-03-20 00:50:03 +01:00
|
|
|
true
|
|
|
|
} else 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.
|
2021-01-31 10:32:34 +01:00
|
|
|
use rustc_type_ir::sty::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) {
|
2022-05-09 16:04:31 -04:00
|
|
|
(Adt(a_def, _), Adt(b_def, _)) => {
|
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,
|
2023-02-07 01:29:48 -07:00
|
|
|
tcx.type_of(a_did).subst_identity(),
|
|
|
|
tcx.type_of(b_did).subst_identity(),
|
2020-07-07 21:08:14 +10:00
|
|
|
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-06-10 11:18:06 +10:00
|
|
|
a_const.kind() == b_const.kind()
|
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);
|
|
|
|
|
2022-07-18 20:41:27 +02:00
|
|
|
// We don't compare regions, but leaving bound regions around ICEs, so
|
|
|
|
// we erase them.
|
|
|
|
let a_sig = tcx.erase_late_bound_regions(a_poly_sig);
|
|
|
|
let b_sig = tcx.erase_late_bound_regions(b_poly_sig);
|
2020-08-18 02:00:35 +10:00
|
|
|
|
|
|
|
(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(..))
|
2022-11-26 21:51:55 +00:00
|
|
|
| (Alias(ty::Projection, ..), Alias(ty::Projection, ..))
|
|
|
|
| (Alias(ty::Opaque, ..), Alias(ty::Opaque, ..)) => false,
|
2020-08-18 02:00:35 +10:00
|
|
|
|
|
|
|
// 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 {
|
2022-11-05 15:33:58 +00:00
|
|
|
#[instrument(level = "trace", skip(self, cx))]
|
2020-06-25 23:41:36 +03:00
|
|
|
fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, this_fi: &hir::ForeignItem<'_>) {
|
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;
|
2022-11-05 15:33:58 +00:00
|
|
|
if let Some(existing_did) = self.insert(tcx, this_fi) {
|
2023-02-07 01:29:48 -07:00
|
|
|
let existing_decl_ty = tcx.type_of(existing_did).skip_binder();
|
|
|
|
let this_decl_ty = tcx.type_of(this_fi.owner_id).subst_identity();
|
2020-06-19 18:14:54 +10:00
|
|
|
debug!(
|
2020-06-28 10:11:04 +10:00
|
|
|
"ClashingExternDeclarations: Comparing existing {:?}: {:?} to this {:?}: {:?}",
|
2022-11-05 15:33:58 +00:00
|
|
|
existing_did, existing_decl_ty, this_fi.owner_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,
|
|
|
|
) {
|
2022-11-05 15:33:58 +00:00
|
|
|
let orig_fi = tcx.hir().expect_foreign_item(existing_did);
|
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),
|
|
|
|
};
|
2022-09-16 11:01:02 +04:00
|
|
|
|
2022-11-13 09:46:31 -05:00
|
|
|
// Finally, emit the diagnostic.
|
|
|
|
let this = this_fi.ident.name;
|
|
|
|
let orig = orig.get_name();
|
|
|
|
let previous_decl_label = get_relevant_span(orig_fi);
|
|
|
|
let mismatch_label = get_relevant_span(this_fi);
|
|
|
|
let sub = BuiltinClashingExternSub {
|
|
|
|
tcx,
|
|
|
|
expected: existing_decl_ty,
|
|
|
|
found: this_decl_ty,
|
|
|
|
};
|
|
|
|
let decorator = if orig == this {
|
|
|
|
BuiltinClashingExtern::SameName {
|
|
|
|
this,
|
|
|
|
orig,
|
|
|
|
previous_decl_label,
|
|
|
|
mismatch_label,
|
|
|
|
sub,
|
|
|
|
}
|
2022-09-16 11:01:02 +04:00
|
|
|
} else {
|
2022-11-13 09:46:31 -05:00
|
|
|
BuiltinClashingExtern::DiffName {
|
|
|
|
this,
|
|
|
|
orig,
|
|
|
|
previous_decl_label,
|
|
|
|
mismatch_label,
|
|
|
|
sub,
|
|
|
|
}
|
2022-09-16 11:01:02 +04:00
|
|
|
};
|
2022-11-13 09:46:31 -05:00
|
|
|
tcx.emit_spanned_lint(
|
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),
|
2022-11-13 09:46:31 -05:00
|
|
|
decorator,
|
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) {
|
2022-10-22 21:50:44 -04:00
|
|
|
cx.emit_spanned_lint(
|
2022-09-16 11:01:02 +04:00
|
|
|
DEREF_NULLPTR,
|
|
|
|
expr.span,
|
2022-10-22 21:50:44 -04:00
|
|
|
BuiltinDerefNullptr { label: expr.span },
|
2022-09-16 11:01:02 +04:00
|
|
|
);
|
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
|
2022-10-19 11:34:00 -07:00
|
|
|
/// # #![feature(asm_experimental_arch)]
|
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 {
|
2022-11-23 15:52:03 -05:00
|
|
|
#[allow(rustc::diagnostic_outside_of_impl)]
|
2021-08-19 16:34:01 -04:00
|
|
|
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),
|
2022-10-22 11:07:54 +02:00
|
|
|
fluent::lint_builtin_asm_labels,
|
2022-09-16 11:01:02 +04:00
|
|
|
|lint| lint,
|
2021-08-19 16:34:01 -04:00
|
|
|
BuiltinLintDiagnostics::NamedAsmLabel(
|
|
|
|
"only local labels of the form `<number>:` should be used in inline asm"
|
|
|
|
.to_string(),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-02-28 19:59:21 -05:00
|
|
|
|
|
|
|
declare_lint! {
|
|
|
|
/// The `special_module_name` lint detects module
|
|
|
|
/// declarations for files that have a special meaning.
|
|
|
|
///
|
|
|
|
/// ### Example
|
|
|
|
///
|
|
|
|
/// ```rust,compile_fail
|
|
|
|
/// mod lib;
|
|
|
|
///
|
|
|
|
/// fn main() {
|
|
|
|
/// lib::run();
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// {{produces}}
|
|
|
|
///
|
|
|
|
/// ### Explanation
|
|
|
|
///
|
|
|
|
/// Cargo recognizes `lib.rs` and `main.rs` as the root of a
|
|
|
|
/// library or binary crate, so declaring them as modules
|
|
|
|
/// will lead to miscompilation of the crate unless configured
|
|
|
|
/// explicitly.
|
|
|
|
///
|
|
|
|
/// To access a library from a binary target within the same crate,
|
2022-10-14 00:25:34 +08:00
|
|
|
/// use `your_crate_name::` as the path instead of `lib::`:
|
2022-02-28 19:59:21 -05:00
|
|
|
///
|
|
|
|
/// ```rust,compile_fail
|
|
|
|
/// // bar/src/lib.rs
|
|
|
|
/// fn run() {
|
|
|
|
/// // ...
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// // bar/src/main.rs
|
|
|
|
/// fn main() {
|
|
|
|
/// bar::run();
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// Binary targets cannot be used as libraries and so declaring
|
|
|
|
/// one as a module is not allowed.
|
|
|
|
pub SPECIAL_MODULE_NAME,
|
|
|
|
Warn,
|
|
|
|
"module declarations for files with a special meaning",
|
|
|
|
}
|
|
|
|
|
|
|
|
declare_lint_pass!(SpecialModuleName => [SPECIAL_MODULE_NAME]);
|
|
|
|
|
|
|
|
impl EarlyLintPass for SpecialModuleName {
|
|
|
|
fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &ast::Crate) {
|
|
|
|
for item in &krate.items {
|
2022-07-21 13:06:08 -04:00
|
|
|
if let ast::ItemKind::Mod(
|
|
|
|
_,
|
|
|
|
ast::ModKind::Unloaded | ast::ModKind::Loaded(_, ast::Inline::No, _),
|
|
|
|
) = item.kind
|
|
|
|
{
|
2022-02-28 19:59:21 -05:00
|
|
|
if item.attrs.iter().any(|a| a.has_name(sym::path)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
match item.ident.name.as_str() {
|
2022-10-22 21:50:44 -04:00
|
|
|
"lib" => cx.emit_spanned_lint(
|
|
|
|
SPECIAL_MODULE_NAME,
|
|
|
|
item.span,
|
|
|
|
BuiltinSpecialModuleNameUsed::Lib,
|
|
|
|
),
|
|
|
|
"main" => cx.emit_spanned_lint(
|
|
|
|
SPECIAL_MODULE_NAME,
|
|
|
|
item.span,
|
|
|
|
BuiltinSpecialModuleNameUsed::Main,
|
|
|
|
),
|
|
|
|
_ => continue,
|
2022-02-28 19:59:21 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-08-11 19:50:48 +02:00
|
|
|
|
|
|
|
pub use rustc_session::lint::builtin::UNEXPECTED_CFGS;
|
|
|
|
|
|
|
|
declare_lint_pass!(UnexpectedCfgs => [UNEXPECTED_CFGS]);
|
|
|
|
|
|
|
|
impl EarlyLintPass for UnexpectedCfgs {
|
|
|
|
fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) {
|
|
|
|
let cfg = &cx.sess().parse_sess.config;
|
|
|
|
let check_cfg = &cx.sess().parse_sess.check_config;
|
|
|
|
for &(name, value) in cfg {
|
2022-10-22 21:50:44 -04:00
|
|
|
if let Some(names_valid) = &check_cfg.names_valid && !names_valid.contains(&name){
|
|
|
|
cx.emit_lint(UNEXPECTED_CFGS, BuiltinUnexpectedCliConfigName {
|
|
|
|
name,
|
|
|
|
});
|
2022-08-11 19:50:48 +02:00
|
|
|
}
|
2022-10-22 21:50:44 -04:00
|
|
|
if let Some(value) = value && let Some(values) = check_cfg.values_valid.get(&name) && !values.contains(&value) {
|
|
|
|
cx.emit_lint(
|
|
|
|
UNEXPECTED_CFGS,
|
|
|
|
BuiltinUnexpectedCliConfigValue { name, value },
|
|
|
|
);
|
2022-08-11 19:50:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|