diff options
Diffstat (limited to 'compiler/rustc_lint/src')
| -rw-r--r-- | compiler/rustc_lint/src/async_fn_in_trait.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/builtin.rs | 82 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/context.rs | 20 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/dangling.rs | 223 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/early.rs | 50 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/if_let_rescope.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/impl_trait_overcaptures.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/internal.rs | 10 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/late.rs | 55 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/levels.rs | 117 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/lib.rs | 45 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/lints.rs | 17 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/methods.rs | 69 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/nonstandard_style.rs | 6 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/passes.rs | 12 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/tail_expr_drop_order.rs | 23 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/traits.rs | 5 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/types.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/unused.rs | 4 | 
19 files changed, 501 insertions, 247 deletions
diff --git a/compiler/rustc_lint/src/async_fn_in_trait.rs b/compiler/rustc_lint/src/async_fn_in_trait.rs index 63a8a949e96..9923f05df3c 100644 --- a/compiler/rustc_lint/src/async_fn_in_trait.rs +++ b/compiler/rustc_lint/src/async_fn_in_trait.rs @@ -95,7 +95,7 @@ impl<'tcx> LateLintPass<'tcx> for AsyncFnInTrait { && let hir::IsAsync::Async(async_span) = sig.header.asyncness { // RTN can be used to bound `async fn` in traits in a better way than "always" - if cx.tcx.features().return_type_notation { + if cx.tcx.features().return_type_notation() { return; } diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index ef8100da9e2..dbb8c667532 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -4,21 +4,14 @@ //! AST visitor. Also see `rustc_session::lint::builtin`, which contains the //! definitions of lints that are emitted directly inside the main compiler. //! -//! To add a new lint to rustc, declare it here using `declare_lint!()`. +//! 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. -//! You can do that in an existing `LintPass` if it makes sense, or in a -//! new `LintPass`, or using `Session::add_lint` elsewhere in the -//! compiler. Only do the latter if the check can't be written cleanly as a -//! `LintPass` (also, note that such lints will need to be defined in -//! `rustc_session::lint::builtin`, not here). //! -//! 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 [`EarlyLintPass`], you will also need to add it to the +//! [`crate::early_lint_methods!`] invocation in `lib.rs`. //! -//! If you define a new `LateLintPass`, you will also need to add it to the -//! `late_lint_methods!` invocation in `lib.rs`. +//! If you define a new [`LateLintPass`], you will also need to add it to the +//! [`crate::late_lint_methods!`] invocation in `lib.rs`. use std::fmt::Write; @@ -73,7 +66,6 @@ use crate::{ EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext, fluent_generated as fluent, }; - declare_lint! { /// The `while_true` lint detects `while true { }`. /// @@ -241,7 +233,8 @@ declare_lint! { /// behavior. UNSAFE_CODE, Allow, - "usage of `unsafe` code and other potentially unsound constructs" + "usage of `unsafe` code and other potentially unsound constructs", + @eval_always = true } declare_lint_pass!(UnsafeCode => [UNSAFE_CODE]); @@ -389,6 +382,7 @@ declare_lint! { report_in_external_macro } +#[derive(Default)] pub struct MissingDoc; impl_lint_pass!(MissingDoc => [MISSING_DOCS]); @@ -819,8 +813,8 @@ pub struct DeprecatedAttr { impl_lint_pass!(DeprecatedAttr => []); -impl DeprecatedAttr { - pub fn new() -> DeprecatedAttr { +impl Default for DeprecatedAttr { + fn default() -> Self { DeprecatedAttr { depr_attrs: deprecated_attributes() } } } @@ -1235,7 +1229,7 @@ impl<'tcx> LateLintPass<'tcx> for UngatedAsyncFnTrackCaller { def_id: LocalDefId, ) { if fn_kind.asyncness().is_async() - && !cx.tcx.features().async_fn_track_caller + && !cx.tcx.features().async_fn_track_caller() // Now, check if the function has the `#[track_caller]` attribute && let Some(attr) = cx.tcx.get_attr(def_id, sym::track_caller) { @@ -1424,7 +1418,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { // See also `tests/ui/const-generics/generic_const_exprs/type-alias-bounds.rs`. let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); if ty.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) - && cx.tcx.features().generic_const_exprs + && cx.tcx.features().generic_const_exprs() { return; } @@ -1538,7 +1532,7 @@ impl<'tcx> LateLintPass<'tcx> for TrivialConstraints { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { use rustc_middle::ty::ClauseKind; - if cx.tcx.features().trivial_bounds { + if cx.tcx.features().trivial_bounds() { let predicates = cx.tcx.predicates_of(item.owner_id); for &(predicate, span) in predicates.predicates { let predicate_kind_name = match predicate.kind().skip_binder() { @@ -1554,7 +1548,9 @@ impl<'tcx> LateLintPass<'tcx> for TrivialConstraints { // Ignore bounds that a user can't type | ClauseKind::WellFormed(..) // FIXME(generic_const_exprs): `ConstEvaluatable` can be written - | ClauseKind::ConstEvaluatable(..) => continue, + | ClauseKind::ConstEvaluatable(..) + // Users don't write this directly, only via another trait ref. + | ty::ClauseKind::HostEffect(..) => continue, }; if predicate.is_global() { cx.emit_span_lint(TRIVIAL_BOUNDS, span, BuiltinTrivialBounds { @@ -1892,11 +1888,11 @@ impl EarlyLintPass for KeywordIdents { fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::MacCall) { self.check_tokens(cx, &mac.args.tokens); } - fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: Ident) { + fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: &Ident) { if ident.name.as_str().starts_with('\'') { self.check_ident_token(cx, UnderMacro(false), ident.without_first_quote(), "'"); } else { - self.check_ident_token(cx, UnderMacro(false), ident, ""); + self.check_ident_token(cx, UnderMacro(false), *ident, ""); } } } @@ -2287,13 +2283,15 @@ declare_lint_pass!( impl EarlyLintPass for IncompleteInternalFeatures { fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) { let features = cx.builder.features(); - features - .declared_lang_features - .iter() - .map(|(name, span, _)| (name, span)) - .chain(features.declared_lib_features.iter().map(|(name, span)| (name, span))) - .filter(|(&name, _)| features.incomplete(name) || features.internal(name)) - .for_each(|(&name, &span)| { + let lang_features = + features.enabled_lang_features().iter().map(|feat| (feat.gate_name, feat.attr_sp)); + let lib_features = + features.enabled_lib_features().iter().map(|feat| (feat.gate_name, feat.attr_sp)); + + lang_features + .chain(lib_features) + .filter(|(name, _)| features.incomplete(*name) || features.internal(*name)) + .for_each(|(name, span)| { if features.incomplete(name) { let note = rustc_feature::find_feature_issue(name, GateIssue::Language) .map(|n| BuiltinFeatureIssueNote { n }); @@ -2657,8 +2655,8 @@ declare_lint! { /// /// ### 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))`. + /// Dereferencing a null pointer causes [undefined behavior] if it is accessed + /// (loaded from or stored to). /// /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html pub DEREF_NULLPTR, @@ -2673,14 +2671,14 @@ impl<'tcx> LateLintPass<'tcx> for DerefNullPtr { /// test if expression is a null ptr fn is_null_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { match &expr.kind { - rustc_hir::ExprKind::Cast(expr, ty) => { - if let rustc_hir::TyKind::Ptr(_) = ty.kind { + hir::ExprKind::Cast(expr, ty) => { + if let 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(path, _) => { - if let rustc_hir::ExprKind::Path(ref qpath) = path.kind { + hir::ExprKind::Call(path, _) => { + if let hir::ExprKind::Path(ref qpath) = path.kind { if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() { return matches!( cx.tcx.get_diagnostic_name(def_id), @@ -2697,7 +2695,7 @@ impl<'tcx> LateLintPass<'tcx> for DerefNullPtr { /// test if expression is the literal `0` fn is_zero(expr: &hir::Expr<'_>) -> bool { match &expr.kind { - rustc_hir::ExprKind::Lit(lit) => { + hir::ExprKind::Lit(lit) => { if let LitKind::Int(a, _) = lit.node { return a == 0; } @@ -2707,8 +2705,16 @@ impl<'tcx> LateLintPass<'tcx> for DerefNullPtr { false } - if let rustc_hir::ExprKind::Unary(rustc_hir::UnOp::Deref, expr_deref) = expr.kind { - if is_null_ptr(cx, expr_deref) { + if let hir::ExprKind::Unary(hir::UnOp::Deref, expr_deref) = expr.kind + && is_null_ptr(cx, expr_deref) + { + if let hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::AddrOf(hir::BorrowKind::Raw, ..), + .. + }) = cx.tcx.parent_hir_node(expr.hir_id) + { + // `&raw *NULL` is ok. + } else { cx.emit_span_lint(DEREF_NULLPTR, expr.span, BuiltinDerefNullptr { label: expr.span, }); diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 39f90a8e9ed..4af1fd1baf9 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -1,18 +1,7 @@ -//! Implementation of lint checking. +//! Basic types for managing and implementing lints. //! -//! The lint checking is mostly consolidated into one pass which runs -//! after all other analyses. Throughout compilation, lint warnings -//! can be added via the `add_lint` method on the Session structure. This -//! requires a span and an ID of the node that the lint is being added to. The -//! lint isn't actually emitted at that time because it is unknown what the -//! actual lint level at that location is. -//! -//! To actually emit lint warnings/errors, a separate pass is used. -//! A context keeps track of the current state of all lint levels. -//! Upon entering a node of the ast which can modify the lint settings, the -//! previous lint state is pushed onto a stack and the ast is then recursed -//! upon. As the ast is traversed, this keeps track of the current lint level -//! for all lint attributes. +//! See <https://rustc-dev-guide.rust-lang.org/diagnostics.html> for an +//! overview of how lints are implemented. use std::cell::Cell; use std::{iter, slice}; @@ -52,9 +41,6 @@ type LateLintPassFactory = dyn for<'tcx> Fn(TyCtxt<'tcx>) -> LateLintPassObject<'tcx> + sync::DynSend + sync::DynSync; /// Information about the registered lints. -/// -/// This is basically the subset of `Context` that we can -/// build early in the compile pipeline. pub struct LintStore { /// Registered lints. lints: Vec<&'static Lint>, diff --git a/compiler/rustc_lint/src/dangling.rs b/compiler/rustc_lint/src/dangling.rs new file mode 100644 index 00000000000..a34c3e26778 --- /dev/null +++ b/compiler/rustc_lint/src/dangling.rs @@ -0,0 +1,223 @@ +use rustc_ast::visit::{visit_opt, walk_list}; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::intravisit::{FnKind, Visitor, walk_expr}; +use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, LangItem}; +use rustc_middle::ty::{Ty, TyCtxt}; +use rustc_session::{declare_lint, impl_lint_pass}; +use rustc_span::Span; +use rustc_span::symbol::sym; + +use crate::lints::DanglingPointersFromTemporaries; +use crate::{LateContext, LateLintPass}; + +declare_lint! { + /// The `dangling_pointers_from_temporaries` lint detects getting a pointer to data + /// of a temporary that will immediately get dropped. + /// + /// ### Example + /// + /// ```rust + /// # #![allow(unused)] + /// # unsafe fn use_data(ptr: *const u8) { } + /// fn gather_and_use(bytes: impl Iterator<Item = u8>) { + /// let x: *const u8 = bytes.collect::<Vec<u8>>().as_ptr(); + /// unsafe { use_data(x) } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Getting a pointer from a temporary value will not prolong its lifetime, + /// which means that the value can be dropped and the allocation freed + /// while the pointer still exists, making the pointer dangling. + /// This is not an error (as far as the type system is concerned) + /// but probably is not what the user intended either. + /// + /// If you need stronger guarantees, consider using references instead, + /// as they are statically verified by the borrow-checker to never dangle. + pub DANGLING_POINTERS_FROM_TEMPORARIES, + Warn, + "detects getting a pointer from a temporary" +} + +/// FIXME: false negatives (i.e. the lint is not emitted when it should be) +/// 1. Method calls that are not checked for: +/// - [`temporary_unsafe_cell.get()`][`core::cell::UnsafeCell::get()`] +/// - [`temporary_sync_unsafe_cell.get()`][`core::cell::SyncUnsafeCell::get()`] +/// 2. Ways to get a temporary that are not recognized: +/// - `owning_temporary.field` +/// - `owning_temporary[index]` +/// 3. No checks for ref-to-ptr conversions: +/// - `&raw [mut] temporary` +/// - `&temporary as *(const|mut) _` +/// - `ptr::from_ref(&temporary)` and friends +#[derive(Clone, Copy, Default)] +pub(crate) struct DanglingPointers; + +impl_lint_pass!(DanglingPointers => [DANGLING_POINTERS_FROM_TEMPORARIES]); + +// This skips over const blocks, but they cannot use or return a dangling pointer anyways. +impl<'tcx> LateLintPass<'tcx> for DanglingPointers { + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + _: FnKind<'tcx>, + _: &'tcx FnDecl<'tcx>, + body: &'tcx Body<'tcx>, + _: Span, + _: LocalDefId, + ) { + DanglingPointerSearcher { cx, inside_call_args: false }.visit_body(body) + } +} + +/// This produces a dangling pointer: +/// ```ignore (example) +/// let ptr = CString::new("hello").unwrap().as_ptr(); +/// foo(ptr) +/// ``` +/// +/// But this does not: +/// ```ignore (example) +/// foo(CString::new("hello").unwrap().as_ptr()) +/// ``` +/// +/// But this does: +/// ```ignore (example) +/// foo({ let ptr = CString::new("hello").unwrap().as_ptr(); ptr }) +/// ``` +/// +/// So we have to keep track of when we are inside of a function/method call argument. +struct DanglingPointerSearcher<'lcx, 'tcx> { + cx: &'lcx LateContext<'tcx>, + /// Keeps track of whether we are inside of function/method call arguments, + /// where this lint should not be emitted. + /// + /// See [the main doc][`Self`] for examples. + inside_call_args: bool, +} + +impl Visitor<'_> for DanglingPointerSearcher<'_, '_> { + fn visit_expr(&mut self, expr: &Expr<'_>) -> Self::Result { + if !self.inside_call_args { + lint_expr(self.cx, expr) + } + match expr.kind { + ExprKind::Call(lhs, args) | ExprKind::MethodCall(_, lhs, args, _) => { + self.visit_expr(lhs); + self.with_inside_call_args(true, |this| walk_list!(this, visit_expr, args)) + } + ExprKind::Block(&Block { stmts, expr, .. }, _) => { + self.with_inside_call_args(false, |this| walk_list!(this, visit_stmt, stmts)); + visit_opt!(self, visit_expr, expr) + } + _ => walk_expr(self, expr), + } + } +} + +impl DanglingPointerSearcher<'_, '_> { + fn with_inside_call_args<R>( + &mut self, + inside_call_args: bool, + callback: impl FnOnce(&mut Self) -> R, + ) -> R { + let old = core::mem::replace(&mut self.inside_call_args, inside_call_args); + let result = callback(self); + self.inside_call_args = old; + result + } +} + +fn lint_expr(cx: &LateContext<'_>, expr: &Expr<'_>) { + if let ExprKind::MethodCall(method, receiver, _args, _span) = expr.kind + && matches!(method.ident.name, sym::as_ptr | sym::as_mut_ptr) + && is_temporary_rvalue(receiver) + && let ty = cx.typeck_results().expr_ty(receiver) + && is_interesting(cx.tcx, ty) + { + // FIXME: use `emit_node_lint` when `#[primary_span]` is added. + cx.tcx.emit_node_span_lint( + DANGLING_POINTERS_FROM_TEMPORARIES, + expr.hir_id, + method.ident.span, + DanglingPointersFromTemporaries { + callee: method.ident.name, + ty, + ptr_span: method.ident.span, + temporary_span: receiver.span, + }, + ) + } +} + +fn is_temporary_rvalue(expr: &Expr<'_>) -> bool { + match expr.kind { + // Const is not temporary. + ExprKind::ConstBlock(..) | ExprKind::Repeat(..) | ExprKind::Lit(..) => false, + + // This is literally lvalue. + ExprKind::Path(..) => false, + + // Calls return rvalues. + ExprKind::Call(..) | ExprKind::MethodCall(..) | ExprKind::Binary(..) => true, + + // Inner blocks are rvalues. + ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..) | ExprKind::Block(..) => true, + + // FIXME: these should probably recurse and typecheck along the way. + // Some false negatives are possible for now. + ExprKind::Index(..) | ExprKind::Field(..) | ExprKind::Unary(..) => false, + + ExprKind::Struct(..) => true, + + // FIXME: this has false negatives, but I do not want to deal with 'static/const promotion just yet. + ExprKind::Array(..) => false, + + // These typecheck to `!` + ExprKind::Break(..) | ExprKind::Continue(..) | ExprKind::Ret(..) | ExprKind::Become(..) => { + false + } + + // These typecheck to `()` + ExprKind::Assign(..) | ExprKind::AssignOp(..) | ExprKind::Yield(..) => false, + + // Compiler-magic macros + ExprKind::AddrOf(..) | ExprKind::OffsetOf(..) | ExprKind::InlineAsm(..) => false, + + // We are not interested in these + ExprKind::Cast(..) + | ExprKind::Closure(..) + | ExprKind::Tup(..) + | ExprKind::DropTemps(..) + | ExprKind::Let(..) => false, + + // Not applicable + ExprKind::Type(..) | ExprKind::Err(..) => false, + } +} + +// Array, Vec, String, CString, MaybeUninit, Cell, Box<[_]>, Box<str>, Box<CStr>, +// or any of the above in arbitrary many nested Box'es. +fn is_interesting(tcx: TyCtxt<'_>, ty: Ty<'_>) -> bool { + if ty.is_array() { + true + } else if let Some(inner) = ty.boxed_ty() { + inner.is_slice() + || inner.is_str() + || inner.ty_adt_def().is_some_and(|def| tcx.is_lang_item(def.did(), LangItem::CStr)) + || is_interesting(tcx, inner) + } else if let Some(def) = ty.ty_adt_def() { + for lang_item in [LangItem::String, LangItem::MaybeUninit] { + if tcx.is_lang_item(def.did(), lang_item) { + return true; + } + } + tcx.get_diagnostic_name(def.did()) + .is_some_and(|name| matches!(name, sym::cstring_type | sym::Vec | sym::Cell)) + } else { + false + } +} diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 2285877c9ef..acccff77a10 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -1,18 +1,8 @@ -//! Implementation of lint checking. +//! Implementation of the early lint pass. //! -//! The lint checking is mostly consolidated into one pass which runs -//! after all other analyses. Throughout compilation, lint warnings -//! can be added via the `add_lint` method on the Session structure. This -//! requires a span and an ID of the node that the lint is being added to. The -//! lint isn't actually emitted at that time because it is unknown what the -//! actual lint level at that location is. -//! -//! To actually emit lint warnings/errors, a separate pass is used. -//! A context keeps track of the current state of all lint levels. -//! Upon entering a node of the ast which can modify the lint settings, the -//! previous lint state is pushed onto a stack and the ast is then recursed -//! upon. As the ast is traversed, this keeps track of the current lint level -//! for all lint attributes. +//! The early lint pass works on AST nodes after macro expansion and name +//! resolution, just before AST lowering. These lints are for purely +//! syntactical lints. use rustc_ast::ptr::P; use rustc_ast::visit::{self as ast_visit, Visitor, walk_list}; @@ -121,6 +111,18 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> self.with_lint_attrs(e.id, &e.attrs, |cx| { lint_callback!(cx, check_expr, e); ast_visit::walk_expr(cx, e); + // Explicitly check for lints associated with 'closure_id', since + // it does not have a corresponding AST node + match e.kind { + ast::ExprKind::Closure(box ast::Closure { + coroutine_kind: Some(coroutine_kind), + .. + }) => { + cx.check_id(coroutine_kind.closure_id()); + } + _ => {} + } + lint_callback!(cx, check_expr_post, e); }) } @@ -190,7 +192,7 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> ast_visit::walk_ty(self, t); } - fn visit_ident(&mut self, ident: Ident) { + fn visit_ident(&mut self, ident: &Ident) { lint_callback!(self, check_ident, ident); } @@ -214,21 +216,6 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> }) } - fn visit_expr_post(&mut self, e: &'a ast::Expr) { - // Explicitly check for lints associated with 'closure_id', since - // it does not have a corresponding AST node - match e.kind { - ast::ExprKind::Closure(box ast::Closure { - coroutine_kind: Some(coroutine_kind), - .. - }) => { - self.check_id(coroutine_kind.closure_id()); - } - _ => {} - } - lint_callback!(self, check_expr_post, e); - } - fn visit_generic_arg(&mut self, arg: &'a ast::GenericArg) { lint_callback!(self, check_generic_arg, arg); ast_visit::walk_generic_arg(self, arg); @@ -312,6 +299,9 @@ impl LintPass for RuntimeCombinedEarlyLintPass<'_> { fn name(&self) -> &'static str { panic!() } + fn get_lints(&self) -> crate::LintVec { + panic!() + } } macro_rules! impl_early_lint_pass { diff --git a/compiler/rustc_lint/src/if_let_rescope.rs b/compiler/rustc_lint/src/if_let_rescope.rs index 58fd11fcc29..bdfcc2c0a10 100644 --- a/compiler/rustc_lint/src/if_let_rescope.rs +++ b/compiler/rustc_lint/src/if_let_rescope.rs @@ -243,7 +243,7 @@ impl_lint_pass!( impl<'tcx> LateLintPass<'tcx> for IfLetRescope { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { - if expr.span.edition().at_least_rust_2024() || !cx.tcx.features().if_let_rescope { + if expr.span.edition().at_least_rust_2024() || !cx.tcx.features().if_let_rescope() { return; } if let (Level::Allow, _) = cx.tcx.lint_level_at_node(IF_LET_RESCOPE, expr.hir_id) { diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs index d029ad93407..cc40b67ab27 100644 --- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs +++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs @@ -263,8 +263,8 @@ where && parent == self.parent_def_id { let opaque_span = self.tcx.def_span(opaque_def_id); - let new_capture_rules = - opaque_span.at_least_rust_2024() || self.tcx.features().lifetime_capture_rules_2024; + let new_capture_rules = opaque_span.at_least_rust_2024() + || self.tcx.features().lifetime_capture_rules_2024(); if !new_capture_rules && !opaque.bounds.iter().any(|bound| matches!(bound, hir::GenericBound::Use(..))) { diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 94cc58e4956..2f338f42f19 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -427,9 +427,10 @@ declare_tool_lint! { /// More details on translatable diagnostics can be found /// [here](https://rustc-dev-guide.rust-lang.org/diagnostics/translation.html). pub rustc::UNTRANSLATABLE_DIAGNOSTIC, - Deny, + Allow, "prevent creation of diagnostics which cannot be translated", - report_in_external_macro: true + report_in_external_macro: true, + @eval_always = true } declare_tool_lint! { @@ -440,9 +441,10 @@ declare_tool_lint! { /// More details on diagnostics implementations can be found /// [here](https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-structs.html). pub rustc::DIAGNOSTIC_OUTSIDE_OF_IMPL, - Deny, + Allow, "prevent diagnostic creation outside of `Diagnostic`/`Subdiagnostic`/`LintDiagnostic` impls", - report_in_external_macro: true + report_in_external_macro: true, + @eval_always = true } declare_lint_pass!(Diagnostics => [UNTRANSLATABLE_DIAGNOSTIC, DIAGNOSTIC_OUTSIDE_OF_IMPL]); diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index 6d5903ac467..9d35ce19b57 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -1,18 +1,7 @@ -//! Implementation of lint checking. +//! Implementation of the late lint pass. //! -//! The lint checking is mostly consolidated into one pass which runs -//! after all other analyses. Throughout compilation, lint warnings -//! can be added via the `add_lint` method on the Session structure. This -//! requires a span and an ID of the node that the lint is being added to. The -//! lint isn't actually emitted at that time because it is unknown what the -//! actual lint level at that location is. -//! -//! To actually emit lint warnings/errors, a separate pass is used. -//! A context keeps track of the current state of all lint levels. -//! Upon entering a node of the ast which can modify the lint settings, the -//! previous lint state is pushed onto a stack and the ast is then recursed -//! upon. As the ast is traversed, this keeps track of the current lint level -//! for all lint attributes. +//! The late lint pass Works on HIR nodes, towards the end of analysis (after +//! borrow checking, etc.). These lints have full type information available. use std::any::Any; use std::cell::Cell; @@ -26,11 +15,12 @@ use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::Session; use rustc_session::lint::LintPass; +use rustc_session::lint::builtin::HardwiredLints; use rustc_span::Span; use tracing::debug; use crate::passes::LateLintPassObject; -use crate::{LateContext, LateLintPass, LintStore}; +use crate::{LateContext, LateLintPass, LintId, LintStore}; /// Extract the [`LintStore`] from [`Session`]. /// @@ -326,6 +316,9 @@ impl LintPass for RuntimeCombinedLateLintPass<'_, '_> { fn name(&self) -> &'static str { panic!() } + fn get_lints(&self) -> crate::LintVec { + panic!() + } } macro_rules! impl_late_lint_pass { @@ -361,13 +354,20 @@ pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>( // Note: `passes` is often empty. In that case, it's faster to run // `builtin_lints` directly rather than bundling it up into the // `RuntimeCombinedLateLintPass`. - let late_module_passes = &unerased_lint_store(tcx.sess).late_module_passes; - if late_module_passes.is_empty() { + let store = unerased_lint_store(tcx.sess); + + if store.late_module_passes.is_empty() { late_lint_mod_inner(tcx, module_def_id, context, builtin_lints); } else { - let mut passes: Vec<_> = late_module_passes.iter().map(|mk_pass| (mk_pass)(tcx)).collect(); - passes.push(Box::new(builtin_lints)); - let pass = RuntimeCombinedLateLintPass { passes: &mut passes[..] }; + let builtin_lints = Box::new(builtin_lints) as Box<dyn LateLintPass<'tcx>>; + let mut binding = store + .late_module_passes + .iter() + .map(|mk_pass| (mk_pass)(tcx)) + .chain(std::iter::once(builtin_lints)) + .collect::<Vec<_>>(); + + let pass = RuntimeCombinedLateLintPass { passes: binding.as_mut_slice() }; late_lint_mod_inner(tcx, module_def_id, context, pass); } } @@ -398,7 +398,7 @@ fn late_lint_mod_inner<'tcx, T: LateLintPass<'tcx>>( fn late_lint_crate<'tcx>(tcx: TyCtxt<'tcx>) { // Note: `passes` is often empty. - let mut passes: Vec<_> = + let passes: Vec<_> = unerased_lint_store(tcx.sess).late_passes.iter().map(|mk_pass| (mk_pass)(tcx)).collect(); if passes.is_empty() { @@ -416,7 +416,18 @@ fn late_lint_crate<'tcx>(tcx: TyCtxt<'tcx>) { only_module: false, }; - let pass = RuntimeCombinedLateLintPass { passes: &mut passes[..] }; + let lints_that_dont_need_to_run = tcx.lints_that_dont_need_to_run(()); + + let mut filtered_passes: Vec<Box<dyn LateLintPass<'tcx>>> = passes + .into_iter() + .filter(|pass| { + let lints = (**pass).get_lints(); + !lints.iter().all(|lint| lints_that_dont_need_to_run.contains(&LintId::of(lint))) + }) + .collect(); + + filtered_passes.push(Box::new(HardwiredLints)); + let pass = RuntimeCombinedLateLintPass { passes: &mut filtered_passes[..] }; late_lint_crate_inner(tcx, context, pass); } diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 95a8e7625ff..97a95787422 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -1,9 +1,9 @@ use rustc_ast_pretty::pprust; -use rustc_data_structures::fx::FxIndexMap; +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::{Diag, LintDiagnostic, MultiSpan}; use rustc_feature::{Features, GateIssue}; -use rustc_hir::HirId; use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::{CRATE_HIR_ID, HirId}; use rustc_index::IndexVec; use rustc_middle::bug; use rustc_middle::hir::nested_filter; @@ -115,6 +115,38 @@ impl LintLevelSets { } } +fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet<LintId> { + let store = unerased_lint_store(&tcx.sess); + + let map = tcx.shallow_lint_levels_on(rustc_hir::CRATE_OWNER_ID); + + let dont_need_to_run: FxIndexSet<LintId> = store + .get_lints() + .into_iter() + .filter_map(|lint| { + if !lint.eval_always { + let lint_level = map.lint_level_id_at_node(tcx, LintId::of(lint), CRATE_HIR_ID); + if matches!(lint_level, (Level::Allow, ..)) + || (matches!(lint_level, (.., LintLevelSource::Default))) + && lint.default_level(tcx.sess.edition()) == Level::Allow + { + Some(LintId::of(lint)) + } else { + None + } + } else { + None + } + }) + .collect(); + + let mut visitor = LintLevelMaximum { tcx, dont_need_to_run }; + visitor.process_opts(); + tcx.hir().walk_attributes(&mut visitor); + + visitor.dont_need_to_run +} + #[instrument(level = "trace", skip(tcx), ret)] fn shallow_lint_levels_on(tcx: TyCtxt<'_>, owner: hir::OwnerId) -> ShallowLintLevelMap { let store = unerased_lint_store(tcx.sess); @@ -301,6 +333,83 @@ impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> { } } +/// Visitor with the only function of visiting every item-like in a crate and +/// computing the highest level that every lint gets put to. +/// +/// E.g., if a crate has a global #![allow(lint)] attribute, but a single item +/// uses #[warn(lint)], this visitor will set that lint level as `Warn` +struct LintLevelMaximum<'tcx> { + tcx: TyCtxt<'tcx>, + /// The actual list of detected lints. + dont_need_to_run: FxIndexSet<LintId>, +} + +impl<'tcx> LintLevelMaximum<'tcx> { + fn process_opts(&mut self) { + let store = unerased_lint_store(self.tcx.sess); + for (lint_group, level) in &self.tcx.sess.opts.lint_opts { + if *level != Level::Allow { + let Ok(lints) = store.find_lints(lint_group) else { + return; + }; + for lint in lints { + self.dont_need_to_run.swap_remove(&lint); + } + } + } + } +} + +impl<'tcx> Visitor<'tcx> for LintLevelMaximum<'tcx> { + type NestedFilter = nested_filter::All; + + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() + } + + /// FIXME(blyxyas): In a future revision, we should also graph #![allow]s, + /// but that is handled with more care + fn visit_attribute(&mut self, attribute: &'tcx ast::Attribute) { + if matches!( + Level::from_attr(attribute), + Some( + Level::Warn + | Level::Deny + | Level::Forbid + | Level::Expect(..) + | Level::ForceWarn(..), + ) + ) { + let store = unerased_lint_store(self.tcx.sess); + let Some(meta) = attribute.meta() else { return }; + // Lint attributes are always a metalist inside a + // metalist (even with just one lint). + let Some(meta_item_list) = meta.meta_item_list() else { return }; + + for meta_list in meta_item_list { + // Convert Path to String + let Some(meta_item) = meta_list.meta_item() else { return }; + let ident: &str = &meta_item + .path + .segments + .iter() + .map(|segment| segment.ident.as_str()) + .collect::<Vec<&str>>() + .join("::"); + let Ok(lints) = store.find_lints( + // Lint attributes can only have literals + ident, + ) else { + return; + }; + for lint in lints { + self.dont_need_to_run.swap_remove(&lint); + } + } + } + } +} + pub struct LintLevelsBuilder<'s, P> { sess: &'s Session, features: &'s Features, @@ -858,7 +967,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { #[track_caller] fn check_gated_lint(&self, lint_id: LintId, span: Span, lint_from_cli: bool) -> bool { let feature = if let Some(feature) = lint_id.lint.feature_gate - && !self.features.active(feature) + && !self.features.enabled(feature) { // Lint is behind a feature that is not enabled; eventually return false. feature @@ -934,7 +1043,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { } pub(crate) fn provide(providers: &mut Providers) { - *providers = Providers { shallow_lint_levels_on, ..*providers }; + *providers = Providers { shallow_lint_levels_on, lints_that_dont_need_to_run, ..*providers }; } pub(crate) fn parse_lint_and_tool_name(lint_name: &str) -> (Option<Symbol>, &str) { diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index a7faab0868d..86112277504 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -6,20 +6,14 @@ //! other phases of the compiler, which are generally required to hold in order //! to compile the program at all. //! -//! Most lints can be written as [LintPass] instances. These run after +//! Most lints can be written as [`LintPass`] instances. These run after //! all other analyses. The `LintPass`es built into rustc are defined //! within [rustc_session::lint::builtin], //! which has further comments on how to add such a lint. //! rustc can also load external lint plugins, as is done for Clippy. //! -//! Some of rustc's lints are defined elsewhere in the compiler and work by -//! calling `add_lint()` on the overall `Session` object. This works when -//! it happens before the main lint pass, which emits the lints stored by -//! `add_lint()`. To emit lints after the main lint pass (from codegen, for -//! example) requires more effort. See `emit_lint` and `GatherNodeLevels` -//! in `context.rs`. -//! -//! Some code also exists in [rustc_session::lint], [rustc_middle::lint]. +//! See <https://rustc-dev-guide.rust-lang.org/diagnostics.html> for an +//! overview of how lints are implemented. //! //! ## Note //! @@ -46,6 +40,7 @@ mod async_closures; mod async_fn_in_trait; pub mod builtin; mod context; +mod dangling; mod deref_into_dyn_supertrait; mod drop_forget_useless; mod early; @@ -65,7 +60,6 @@ mod levels; mod lints; mod macro_expr_fragment_specifier_2024_migration; mod map_unit_fn; -mod methods; mod multiple_supertrait_upcastable; mod non_ascii_idents; mod non_fmt_panic; @@ -91,6 +85,7 @@ mod unused; use async_closures::AsyncClosureUsage; use async_fn_in_trait::AsyncFnInTrait; use builtin::*; +use dangling::*; use deref_into_dyn_supertrait::*; use drop_forget_useless::*; use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums; @@ -103,7 +98,6 @@ use invalid_from_utf8::*; use let_underscore::*; use macro_expr_fragment_specifier_2024_migration::*; use map_unit_fn::*; -use methods::*; use multiple_supertrait_upcastable::*; use non_ascii_idents::*; use non_fmt_panic::NonPanicFmt; @@ -170,7 +164,7 @@ early_lint_methods!( [ pub BuiltinCombinedEarlyLintPass, [ - UnusedParens: UnusedParens::new(), + UnusedParens: UnusedParens::default(), UnusedBraces: UnusedBraces, UnusedImportBraces: UnusedImportBraces, UnsafeCode: UnsafeCode, @@ -178,7 +172,7 @@ early_lint_methods!( AnonymousParameters: AnonymousParameters, EllipsisInclusiveRangePatterns: EllipsisInclusiveRangePatterns::default(), NonCamelCaseTypes: NonCamelCaseTypes, - DeprecatedAttr: DeprecatedAttr::new(), + DeprecatedAttr: DeprecatedAttr::default(), WhileTrue: WhileTrue, NonAsciiIdents: NonAsciiIdents, HiddenUnicodeCodepoints: HiddenUnicodeCodepoints, @@ -199,7 +193,6 @@ late_lint_methods!( ForLoopsOverFallibles: ForLoopsOverFallibles, DerefIntoDynSupertrait: DerefIntoDynSupertrait, DropForgetUseless: DropForgetUseless, - HardwiredLints: HardwiredLints, ImproperCTypesDeclarations: ImproperCTypesDeclarations, ImproperCTypesDefinitions: ImproperCTypesDefinitions, InvalidFromUtf8: InvalidFromUtf8, @@ -232,7 +225,7 @@ late_lint_methods!( UngatedAsyncFnTrackCaller: UngatedAsyncFnTrackCaller, ShadowedIntoIter: ShadowedIntoIter, DropTraitConstraints: DropTraitConstraints, - TemporaryCStringAsPtr: TemporaryCStringAsPtr, + DanglingPointers: DanglingPointers, NonPanicFmt: NonPanicFmt, NoopMethodCall: NoopMethodCall, EnumIntrinsicsNonEnums: EnumIntrinsicsNonEnums, @@ -280,6 +273,7 @@ fn register_builtins(store: &mut LintStore) { store.register_lints(&BuiltinCombinedEarlyLintPass::get_lints()); store.register_lints(&BuiltinCombinedModuleLateLintPass::get_lints()); store.register_lints(&foreign_modules::get_lints()); + store.register_lints(&HardwiredLints::lint_vec()); add_lint_group!( "nonstandard_style", @@ -356,6 +350,7 @@ fn register_builtins(store: &mut LintStore) { store.register_renamed("non_fmt_panic", "non_fmt_panics"); store.register_renamed("unused_tuple_struct_fields", "dead_code"); store.register_renamed("static_mut_ref", "static_mut_refs"); + store.register_renamed("temporary_cstring_as_ptr", "dangling_pointers_from_temporaries"); // These were moved to tool lints, but rustc still sees them when compiling normally, before // tool lints are registered, so `check_tool_name_for_backwards_compat` doesn't work. Use @@ -602,25 +597,25 @@ fn register_builtins(store: &mut LintStore) { } fn register_internals(store: &mut LintStore) { - store.register_lints(&LintPassImpl::get_lints()); + store.register_lints(&LintPassImpl::lint_vec()); store.register_early_pass(|| Box::new(LintPassImpl)); - store.register_lints(&DefaultHashTypes::get_lints()); + store.register_lints(&DefaultHashTypes::lint_vec()); store.register_late_mod_pass(|_| Box::new(DefaultHashTypes)); - store.register_lints(&QueryStability::get_lints()); + store.register_lints(&QueryStability::lint_vec()); store.register_late_mod_pass(|_| Box::new(QueryStability)); - store.register_lints(&ExistingDocKeyword::get_lints()); + store.register_lints(&ExistingDocKeyword::lint_vec()); store.register_late_mod_pass(|_| Box::new(ExistingDocKeyword)); - store.register_lints(&TyTyKind::get_lints()); + store.register_lints(&TyTyKind::lint_vec()); store.register_late_mod_pass(|_| Box::new(TyTyKind)); - store.register_lints(&TypeIr::get_lints()); + store.register_lints(&TypeIr::lint_vec()); store.register_late_mod_pass(|_| Box::new(TypeIr)); - store.register_lints(&Diagnostics::get_lints()); + store.register_lints(&Diagnostics::lint_vec()); store.register_late_mod_pass(|_| Box::new(Diagnostics)); - store.register_lints(&BadOptAccess::get_lints()); + store.register_lints(&BadOptAccess::lint_vec()); store.register_late_mod_pass(|_| Box::new(BadOptAccess)); - store.register_lints(&PassByValue::get_lints()); + store.register_lints(&PassByValue::lint_vec()); store.register_late_mod_pass(|_| Box::new(PassByValue)); - store.register_lints(&SpanUseEqCtxt::get_lints()); + store.register_lints(&SpanUseEqCtxt::lint_vec()); store.register_late_mod_pass(|_| Box::new(SpanUseEqCtxt)); // FIXME(davidtwco): deliberately do not include `UNTRANSLATABLE_DIAGNOSTIC` and // `DIAGNOSTIC_OUTSIDE_OF_IMPL` here because `-Wrustc::internal` is provided to every crate and diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 16cfae17d40..000f4b697bd 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -1137,16 +1137,19 @@ pub(crate) struct IgnoredUnlessCrateSpecified<'a> { pub name: Symbol, } -// methods.rs +// dangling.rs #[derive(LintDiagnostic)] -#[diag(lint_cstring_ptr)] +#[diag(lint_dangling_pointers_from_temporaries)] #[note] #[help] -pub(crate) struct CStringPtr { - #[label(lint_as_ptr_label)] - pub as_ptr: Span, - #[label(lint_unwrap_label)] - pub unwrap: Span, +// FIXME: put #[primary_span] on `ptr_span` once it does not cause conflicts +pub(crate) struct DanglingPointersFromTemporaries<'tcx> { + pub callee: Symbol, + pub ty: Ty<'tcx>, + #[label(lint_label_ptr)] + pub ptr_span: Span, + #[label(lint_label_temporary)] + pub temporary_span: Span, } // multiple_supertrait_upcastable.rs diff --git a/compiler/rustc_lint/src/methods.rs b/compiler/rustc_lint/src/methods.rs deleted file mode 100644 index df22bf0972d..00000000000 --- a/compiler/rustc_lint/src/methods.rs +++ /dev/null @@ -1,69 +0,0 @@ -use rustc_hir::{Expr, ExprKind}; -use rustc_middle::ty; -use rustc_session::{declare_lint, declare_lint_pass}; -use rustc_span::Span; -use rustc_span::symbol::sym; - -use crate::lints::CStringPtr; -use crate::{LateContext, LateLintPass, LintContext}; - -declare_lint! { - /// The `temporary_cstring_as_ptr` lint detects getting the inner pointer of - /// a temporary `CString`. - /// - /// ### Example - /// - /// ```rust - /// # #![allow(unused)] - /// # use std::ffi::CString; - /// let c_str = CString::new("foo").unwrap().as_ptr(); - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// The inner pointer of a `CString` lives only as long as the `CString` it - /// points to. Getting the inner pointer of a *temporary* `CString` allows the `CString` - /// to be dropped at the end of the statement, as it is not being referenced as far as the - /// typesystem is concerned. This means outside of the statement the pointer will point to - /// freed memory, which causes undefined behavior if the pointer is later dereferenced. - pub TEMPORARY_CSTRING_AS_PTR, - Warn, - "detects getting the inner pointer of a temporary `CString`" -} - -declare_lint_pass!(TemporaryCStringAsPtr => [TEMPORARY_CSTRING_AS_PTR]); - -impl<'tcx> LateLintPass<'tcx> for TemporaryCStringAsPtr { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::MethodCall(as_ptr_path, as_ptr_receiver, ..) = expr.kind - && as_ptr_path.ident.name == sym::as_ptr - && let ExprKind::MethodCall(unwrap_path, unwrap_receiver, ..) = as_ptr_receiver.kind - && (unwrap_path.ident.name == sym::unwrap || unwrap_path.ident.name == sym::expect) - { - lint_cstring_as_ptr(cx, as_ptr_path.ident.span, unwrap_receiver, as_ptr_receiver); - } - } -} - -fn lint_cstring_as_ptr( - cx: &LateContext<'_>, - as_ptr_span: Span, - source: &rustc_hir::Expr<'_>, - unwrap: &rustc_hir::Expr<'_>, -) { - let source_type = cx.typeck_results().expr_ty(source); - if let ty::Adt(def, args) = source_type.kind() { - if cx.tcx.is_diagnostic_item(sym::Result, def.did()) { - if let ty::Adt(adt, _) = args.type_at(0).kind() { - if cx.tcx.is_diagnostic_item(sym::cstring_type, adt.did()) { - cx.emit_span_lint(TEMPORARY_CSTRING_AS_PTR, as_ptr_span, CStringPtr { - as_ptr: as_ptr_span, - unwrap: unwrap.span, - }); - } - } - } - } -} diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index 83a8ca4307e..1c27e1daa90 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -542,11 +542,7 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals { } fn check_generic_param(&mut self, cx: &LateContext<'_>, param: &hir::GenericParam<'_>) { - if let GenericParamKind::Const { is_host_effect, .. } = param.kind { - // `host` params are explicitly allowed to be lowercase. - if is_host_effect { - return; - } + if let GenericParamKind::Const { .. } = param.kind { NonUpperCaseGlobals::check_upper_case(cx, "const parameter", ¶m.name.ident()); } } diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs index a1d436e0d3d..9d84d36e779 100644 --- a/compiler/rustc_lint/src/passes.rs +++ b/compiler/rustc_lint/src/passes.rs @@ -110,7 +110,7 @@ macro_rules! declare_combined_late_lint_pass { $v fn get_lints() -> $crate::LintVec { let mut lints = Vec::new(); - $(lints.extend_from_slice(&$pass::get_lints());)* + $(lints.extend_from_slice(&$pass::lint_vec());)* lints } } @@ -124,6 +124,9 @@ macro_rules! declare_combined_late_lint_pass { fn name(&self) -> &'static str { panic!() } + fn get_lints(&self) -> LintVec { + panic!() + } } ) } @@ -133,7 +136,7 @@ macro_rules! early_lint_methods { ($macro:path, $args:tt) => ( $macro!($args, [ fn check_param(a: &rustc_ast::Param); - fn check_ident(a: rustc_span::symbol::Ident); + fn check_ident(a: &rustc_span::symbol::Ident); fn check_crate(a: &rustc_ast::Crate); fn check_crate_post(a: &rustc_ast::Crate); fn check_item(a: &rustc_ast::Item); @@ -222,7 +225,7 @@ macro_rules! declare_combined_early_lint_pass { $v fn get_lints() -> $crate::LintVec { let mut lints = Vec::new(); - $(lints.extend_from_slice(&$pass::get_lints());)* + $(lints.extend_from_slice(&$pass::lint_vec());)* lints } } @@ -236,6 +239,9 @@ macro_rules! declare_combined_early_lint_pass { fn name(&self) -> &'static str { panic!() } + fn get_lints(&self) -> LintVec { + panic!() + } } ) } diff --git a/compiler/rustc_lint/src/tail_expr_drop_order.rs b/compiler/rustc_lint/src/tail_expr_drop_order.rs index 44a36142ed4..89763059877 100644 --- a/compiler/rustc_lint/src/tail_expr_drop_order.rs +++ b/compiler/rustc_lint/src/tail_expr_drop_order.rs @@ -14,15 +14,14 @@ use rustc_span::edition::Edition; use crate::{LateContext, LateLintPass}; declare_lint! { - /// The `tail_expr_drop_order` lint looks for those values generated at the tail expression location, that of type - /// with a significant `Drop` implementation, such as locks. - /// In case there are also local variables of type with significant `Drop` implementation as well, - /// this lint warns you of a potential transposition in the drop order. - /// Your discretion on the new drop order introduced by Edition 2024 is required. + /// The `tail_expr_drop_order` lint looks for those values generated at the tail expression location, + /// that runs a custom `Drop` destructor. + /// Some of them may be dropped earlier in Edition 2024 that they used to in Edition 2021 and prior. + /// This lint detects those cases and provides you information on those values and their custom destructor implementations. + /// Your discretion on this information is required. /// /// ### Example - /// ```rust,edition2024 - /// #![feature(shorter_tail_lifetimes)] + /// ```rust,edition2021 /// #![warn(tail_expr_drop_order)] /// struct Droppy(i32); /// impl Droppy { @@ -37,12 +36,12 @@ declare_lint! { /// println!("loud drop {}", self.0); /// } /// } - /// fn edition_2024() -> i32 { + /// fn edition_2021() -> i32 { /// let another_droppy = Droppy(0); /// Droppy(1).get() /// } /// fn main() { - /// edition_2024(); + /// edition_2021(); /// } /// ``` /// @@ -137,7 +136,7 @@ impl<'tcx> LateLintPass<'tcx> for TailExprDropOrder { _: Span, def_id: rustc_span::def_id::LocalDefId, ) { - if cx.tcx.sess.at_least_rust_2024() && cx.tcx.features().shorter_tail_lifetimes { + if !body.value.span.edition().at_least_rust_2024() { Self::check_fn_or_closure(cx, fn_kind, body, def_id); } } @@ -185,8 +184,8 @@ impl<'a, 'tcx> Visitor<'tcx> for LintVisitor<'a, 'tcx> { impl<'a, 'tcx> LintVisitor<'a, 'tcx> { fn check_block_inner(&mut self, block: &Block<'tcx>) { - if !block.span.at_least_rust_2024() { - // We only lint for Edition 2024 onwards + if block.span.at_least_rust_2024() { + // We only lint up to Edition 2021 return; } let Some(tail_expr) = block.expr else { return }; diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs index 5a3666dcbd4..b793ec6a493 100644 --- a/compiler/rustc_lint/src/traits.rs +++ b/compiler/rustc_lint/src/traits.rs @@ -114,10 +114,7 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints { let hir::TyKind::TraitObject(bounds, _lifetime, _syntax) = &ty.kind else { return }; for bound in &bounds[..] { let def_id = bound.trait_ref.trait_def_id(); - if def_id.is_some_and(|def_id| cx.tcx.is_lang_item(def_id, LangItem::Drop)) - // FIXME: ?Drop is not a thing. - && bound.modifiers != hir::TraitBoundModifier::Maybe - { + if def_id.is_some_and(|def_id| cx.tcx.is_lang_item(def_id, LangItem::Drop)) { let Some(def_id) = cx.tcx.get_diagnostic_item(sym::needs_drop) else { return }; cx.emit_span_lint(DYN_DROP, bound.span, DropGlue { tcx: cx.tcx, def_id }); } diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 60ff925b40e..0751d35cb9c 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -167,7 +167,7 @@ declare_lint! { "detects ambiguous wide pointer comparisons" } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Default)] pub(crate) struct TypeLimits { /// Id of the last visited negated expression negated_expr_id: Option<hir::HirId>, diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index ddc18c755a8..bbb290c9459 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -1025,8 +1025,8 @@ pub(crate) struct UnusedParens { parens_in_cast_in_lt: Vec<ast::NodeId>, } -impl UnusedParens { - pub(crate) fn new() -> Self { +impl Default for UnusedParens { + fn default() -> Self { Self { with_self_ty_parens: false, parens_in_cast_in_lt: Vec::new() } } }  | 
