From c69894eaec2a29f9db023cbfff63e48266de4ee5 Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Wed, 16 Oct 2024 22:19:56 +0300 Subject: [PATCH] New lint: `dangling_pointers_from_temporaries` --- compiler/rustc_lint/messages.ftl | 12 +- compiler/rustc_lint/src/dangling.rs | 223 ++++++++++++++++++ compiler/rustc_lint/src/lib.rs | 7 +- compiler/rustc_lint/src/lints.rs | 17 +- compiler/rustc_lint/src/methods.rs | 69 ------ compiler/rustc_span/src/symbol.rs | 2 + library/alloc/tests/boxed.rs | 1 + library/core/src/cell.rs | 1 + library/core/src/ffi/c_str.rs | 4 +- .../clippy_lints/src/deprecated_lints.rs | 2 +- src/tools/clippy/tests/ui/rename.fixed | 4 +- src/tools/clippy/tests/ui/rename.stderr | 18 +- tests/ui/consts/zst_no_llvm_alloc.rs | 7 +- .../allow.rs | 23 ++ .../allow.stderr | 34 +++ .../calls.rs | 52 ++++ .../calls.stderr | 62 +++++ .../cstring-as-param.rs} | 4 +- .../cstring-as-param.stderr | 10 + .../cstring-as-ptr.rs} | 5 +- .../cstring-as-ptr.stderr} | 28 ++- .../emacs.rs | 19 ++ .../example-from-issue123613.rs | 13 + .../example-from-issue123613.stderr | 29 +++ .../dangling-pointers-from-temporaries/ext.rs | 32 +++ .../ext.stderr | 29 +++ .../methods.rs | 8 + .../methods.stderr | 29 +++ .../temporaries.rs | 136 +++++++++++ .../temporaries.stderr | 99 ++++++++ .../types.rs | 52 ++++ .../types.stderr | 172 ++++++++++++++ .../lint-temporary-cstring-as-param.stderr | 18 -- 33 files changed, 1093 insertions(+), 128 deletions(-) create mode 100644 compiler/rustc_lint/src/dangling.rs delete mode 100644 compiler/rustc_lint/src/methods.rs create mode 100644 tests/ui/lint/dangling-pointers-from-temporaries/allow.rs create mode 100644 tests/ui/lint/dangling-pointers-from-temporaries/allow.stderr create mode 100644 tests/ui/lint/dangling-pointers-from-temporaries/calls.rs create mode 100644 tests/ui/lint/dangling-pointers-from-temporaries/calls.stderr rename tests/ui/lint/{lint-temporary-cstring-as-param.rs => dangling-pointers-from-temporaries/cstring-as-param.rs} (62%) create mode 100644 tests/ui/lint/dangling-pointers-from-temporaries/cstring-as-param.stderr rename tests/ui/lint/{lint-temporary-cstring-as-ptr.rs => dangling-pointers-from-temporaries/cstring-as-ptr.rs} (53%) rename tests/ui/lint/{lint-temporary-cstring-as-ptr.stderr => dangling-pointers-from-temporaries/cstring-as-ptr.stderr} (57%) create mode 100644 tests/ui/lint/dangling-pointers-from-temporaries/emacs.rs create mode 100644 tests/ui/lint/dangling-pointers-from-temporaries/example-from-issue123613.rs create mode 100644 tests/ui/lint/dangling-pointers-from-temporaries/example-from-issue123613.stderr create mode 100644 tests/ui/lint/dangling-pointers-from-temporaries/ext.rs create mode 100644 tests/ui/lint/dangling-pointers-from-temporaries/ext.stderr create mode 100644 tests/ui/lint/dangling-pointers-from-temporaries/methods.rs create mode 100644 tests/ui/lint/dangling-pointers-from-temporaries/methods.stderr create mode 100644 tests/ui/lint/dangling-pointers-from-temporaries/temporaries.rs create mode 100644 tests/ui/lint/dangling-pointers-from-temporaries/temporaries.stderr create mode 100644 tests/ui/lint/dangling-pointers-from-temporaries/types.rs create mode 100644 tests/ui/lint/dangling-pointers-from-temporaries/types.stderr delete mode 100644 tests/ui/lint/lint-temporary-cstring-as-param.stderr diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index c4d709aa1f985..3fa43296dfcac 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -203,14 +203,14 @@ lint_confusable_identifier_pair = found both `{$existing_sym}` and `{$sym}` as i .current_use = this identifier can be confused with `{$existing_sym}` .other_use = other identifier used here -lint_cstring_ptr = getting the inner pointer of a temporary `CString` - .as_ptr_label = this pointer will be invalid - .unwrap_label = this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime - .note = pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - .help = for more information, see https://doc.rust-lang.org/reference/destructors.html - lint_custom_inner_attribute_unstable = custom inner attributes are unstable +lint_dangling_pointers_from_temporaries = a dangling pointer will be produced because the temporary `{$ty}` will be dropped + .label_ptr = this pointer will immediately be invalid + .label_temporary = this `{$ty}` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + .note = pointers do not have a lifetime; when calling `{$callee}` the `{$ty}` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + .help = for more information, see + lint_default_hash_types = prefer `{$preferred}` over `{$used}`, it has better performance .note = a `use rustc_data_structures::fx::{$preferred}` may be necessary diff --git a/compiler/rustc_lint/src/dangling.rs b/compiler/rustc_lint/src/dangling.rs new file mode 100644 index 0000000000000..a34c3e267782a --- /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) { + /// let x: *const u8 = bytes.collect::>().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( + &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, Box, +// 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/lib.rs b/compiler/rustc_lint/src/lib.rs index 5389860e23bb2..9014d9019215a 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -46,6 +46,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 +66,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 +91,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 +104,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; @@ -231,7 +231,7 @@ late_lint_methods!( UngatedAsyncFnTrackCaller: UngatedAsyncFnTrackCaller, ShadowedIntoIter: ShadowedIntoIter, DropTraitConstraints: DropTraitConstraints, - TemporaryCStringAsPtr: TemporaryCStringAsPtr, + DanglingPointers: DanglingPointers, NonPanicFmt: NonPanicFmt, NoopMethodCall: NoopMethodCall, EnumIntrinsicsNonEnums: EnumIntrinsicsNonEnums, @@ -356,6 +356,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 diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 16cfae17d4020..000f4b697bd68 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 df22bf0972d71..0000000000000 --- 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_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 9214cf7593356..890c4fdafef89 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -171,6 +171,7 @@ symbols! { CallOnceFuture, CallRefFuture, Capture, + Cell, Center, Cleanup, Clone, @@ -409,6 +410,7 @@ symbols! { arm, arm_target_feature, array, + as_mut_ptr, as_ptr, as_ref, as_str, diff --git a/library/alloc/tests/boxed.rs b/library/alloc/tests/boxed.rs index bfc31a626fadd..544f60da587c8 100644 --- a/library/alloc/tests/boxed.rs +++ b/library/alloc/tests/boxed.rs @@ -4,6 +4,7 @@ use core::mem::MaybeUninit; use core::ptr::NonNull; #[test] +#[cfg_attr(not(bootstrap), expect(dangling_pointers_from_temporaries))] fn uninitialized_zero_size_box() { assert_eq!( &*Box::<()>::new_uninit() as *const _, diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 0b10624479344..7e6c042274df6 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -304,6 +304,7 @@ pub use once::OnceCell; /// ``` /// /// See the [module-level documentation](self) for more. +#[cfg_attr(not(test), rustc_diagnostic_item = "Cell")] #[stable(feature = "rust1", since = "1.0.0")] #[repr(transparent)] #[rustc_pub_transparent] diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index 0f4386190ee4c..93dd351b02958 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -464,7 +464,9 @@ impl CStr { /// behavior when `ptr` is used inside the `unsafe` block: /// /// ```no_run - /// # #![allow(unused_must_use)] #![allow(temporary_cstring_as_ptr)] + /// # #![allow(unused_must_use)] + /// # #![cfg_attr(bootstrap, expect(temporary_cstring_as_ptr))] + /// # #![cfg_attr(not(bootstrap), expect(dangling_pointers_from_temporaries))] /// use std::ffi::CString; /// /// // Do not do this: diff --git a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs index 0066ed643251a..77dbe9b78a182 100644 --- a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs +++ b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs @@ -166,7 +166,7 @@ declare_with_version! { RENAMED(RENAMED_VERSION): &[(&str, &str)] = &[ #[clippy::version = ""] ("clippy::positional_named_format_parameters", "named_arguments_used_positionally"), #[clippy::version = ""] - ("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr"), + ("clippy::temporary_cstring_as_ptr", "dangling_pointers_from_temporaries"), #[clippy::version = ""] ("clippy::undropped_manually_drops", "undropped_manually_drops"), #[clippy::version = ""] diff --git a/src/tools/clippy/tests/ui/rename.fixed b/src/tools/clippy/tests/ui/rename.fixed index b810fd8224f63..0d6e07aa546cf 100644 --- a/src/tools/clippy/tests/ui/rename.fixed +++ b/src/tools/clippy/tests/ui/rename.fixed @@ -54,7 +54,7 @@ #![allow(enum_intrinsics_non_enums)] #![allow(non_fmt_panics)] #![allow(named_arguments_used_positionally)] -#![allow(temporary_cstring_as_ptr)] +#![allow(dangling_pointers_from_temporaries)] #![allow(undropped_manually_drops)] #![allow(unknown_lints)] #![allow(unused_labels)] @@ -120,7 +120,7 @@ #![warn(unexpected_cfgs)] //~ ERROR: lint `clippy::mismatched_target_os` #![warn(non_fmt_panics)] //~ ERROR: lint `clippy::panic_params` #![warn(named_arguments_used_positionally)] //~ ERROR: lint `clippy::positional_named_format_parameters` -#![warn(temporary_cstring_as_ptr)] //~ ERROR: lint `clippy::temporary_cstring_as_ptr` +#![warn(dangling_pointers_from_temporaries)] //~ ERROR: lint `clippy::temporary_cstring_as_ptr` #![warn(undropped_manually_drops)] //~ ERROR: lint `clippy::undropped_manually_drops` #![warn(unknown_lints)] //~ ERROR: lint `clippy::unknown_clippy_lints` #![warn(unused_labels)] //~ ERROR: lint `clippy::unused_label` diff --git a/src/tools/clippy/tests/ui/rename.stderr b/src/tools/clippy/tests/ui/rename.stderr index 46d9f0fac59f9..b906079d7df3e 100644 --- a/src/tools/clippy/tests/ui/rename.stderr +++ b/src/tools/clippy/tests/ui/rename.stderr @@ -1,11 +1,17 @@ +error: lint `temporary_cstring_as_ptr` has been renamed to `dangling_pointers_from_temporaries` + --> tests/ui/rename.rs:57:10 + | +LL | #![allow(temporary_cstring_as_ptr)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `dangling_pointers_from_temporaries` + | + = note: `-D renamed-and-removed-lints` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]` + error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range` --> tests/ui/rename.rs:63:9 | LL | #![warn(clippy::almost_complete_letter_range)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range` - | - = note: `-D renamed-and-removed-lints` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]` error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names` --> tests/ui/rename.rs:64:9 @@ -361,11 +367,11 @@ error: lint `clippy::positional_named_format_parameters` has been renamed to `na LL | #![warn(clippy::positional_named_format_parameters)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` -error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` +error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `dangling_pointers_from_temporaries` --> tests/ui/rename.rs:123:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `dangling_pointers_from_temporaries` error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops` --> tests/ui/rename.rs:124:9 @@ -397,5 +403,5 @@ error: lint `clippy::reverse_range_loop` has been renamed to `clippy::reversed_e LL | #![warn(clippy::reverse_range_loop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::reversed_empty_ranges` -error: aborting due to 66 previous errors +error: aborting due to 67 previous errors diff --git a/tests/ui/consts/zst_no_llvm_alloc.rs b/tests/ui/consts/zst_no_llvm_alloc.rs index 48ef11e2b58cf..1e92e3bbd4c1b 100644 --- a/tests/ui/consts/zst_no_llvm_alloc.rs +++ b/tests/ui/consts/zst_no_llvm_alloc.rs @@ -17,8 +17,11 @@ fn main() { // The exact addresses returned by these library functions are not necessarily stable guarantees // but for now we assert that we're still matching. - assert_eq!(>::new().as_ptr(), <&[i32]>::default().as_ptr()); - assert_eq!(>::default().as_ptr(), (&[]).as_ptr()); + #[allow(dangling_pointers_from_temporaries)] + { + assert_eq!(>::new().as_ptr(), <&[i32]>::default().as_ptr()); + assert_eq!(>::default().as_ptr(), (&[]).as_ptr()); + }; // statics must have a unique address (see https://github.com/rust-lang/rust/issues/18297, not // clear whether this is a stable guarantee) diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/allow.rs b/tests/ui/lint/dangling-pointers-from-temporaries/allow.rs new file mode 100644 index 0000000000000..d892ebdf6069a --- /dev/null +++ b/tests/ui/lint/dangling-pointers-from-temporaries/allow.rs @@ -0,0 +1,23 @@ +#![allow(dangling_pointers_from_temporaries)] + +fn main() { + dbg!(String::new().as_ptr()); + // ^ no error + + #[deny(dangling_pointers_from_temporaries)] + { + dbg!(String::new().as_ptr()); + //~^ ERROR a dangling pointer will be produced because the temporary `String` will be dropped + } + S.foo() +} + +struct S; + +impl S { + #[warn(dangling_pointers_from_temporaries)] + fn foo(self) { + dbg!(String::new().as_ptr()); + //~^ WARNING a dangling pointer will be produced because the temporary `String` will be dropped + } +} diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/allow.stderr b/tests/ui/lint/dangling-pointers-from-temporaries/allow.stderr new file mode 100644 index 0000000000000..fd434eacf3de3 --- /dev/null +++ b/tests/ui/lint/dangling-pointers-from-temporaries/allow.stderr @@ -0,0 +1,34 @@ +error: a dangling pointer will be produced because the temporary `String` will be dropped + --> $DIR/allow.rs:9:28 + | +LL | dbg!(String::new().as_ptr()); + | ------------- ^^^^^^ this pointer will immediately be invalid + | | + | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | + = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see +note: the lint level is defined here + --> $DIR/allow.rs:7:12 + | +LL | #[deny(dangling_pointers_from_temporaries)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: a dangling pointer will be produced because the temporary `String` will be dropped + --> $DIR/allow.rs:20:28 + | +LL | dbg!(String::new().as_ptr()); + | ------------- ^^^^^^ this pointer will immediately be invalid + | | + | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | + = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see +note: the lint level is defined here + --> $DIR/allow.rs:18:12 + | +LL | #[warn(dangling_pointers_from_temporaries)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error; 1 warning emitted + diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/calls.rs b/tests/ui/lint/dangling-pointers-from-temporaries/calls.rs new file mode 100644 index 0000000000000..b376582a88677 --- /dev/null +++ b/tests/ui/lint/dangling-pointers-from-temporaries/calls.rs @@ -0,0 +1,52 @@ +#![deny(dangling_pointers_from_temporaries)] + +use std::ffi::{c_char, CString}; + +fn cstring() -> CString { + CString::new("hello").unwrap() +} + +fn consume(ptr: *const c_char) { + let c = unsafe { ptr.read() }; + dbg!(c); +} + +// None of these should trigger the lint. +fn ok() { + consume(cstring().as_ptr()); + consume({ cstring() }.as_ptr()); + consume({ cstring().as_ptr() }); + consume(cstring().as_ptr().cast()); + consume({ cstring() }.as_ptr().cast()); + consume({ cstring().as_ptr() }.cast()); +} + +// All of these should trigger the lint. +fn not_ok() { + { + let ptr = cstring().as_ptr(); + //~^ ERROR a dangling pointer will be produced because the temporary `CString` will be dropped + consume(ptr); + } + consume({ + let ptr = cstring().as_ptr(); + //~^ ERROR a dangling pointer will be produced because the temporary `CString` will be dropped + ptr + }); + consume({ + let s = cstring(); + s.as_ptr() + //^ FIXME: should error + }); + let _ptr: *const u8 = cstring().as_ptr().cast(); + //~^ ERROR a dangling pointer will be produced because the temporary `CString` will be dropped + let _ptr: *const u8 = { cstring() }.as_ptr().cast(); + //~^ ERROR a dangling pointer will be produced because the temporary `CString` will be dropped + let _ptr: *const u8 = { cstring().as_ptr() }.cast(); + //~^ ERROR a dangling pointer will be produced because the temporary `CString` will be dropped +} + +fn main() { + ok(); + not_ok(); +} diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/calls.stderr b/tests/ui/lint/dangling-pointers-from-temporaries/calls.stderr new file mode 100644 index 0000000000000..d1615b76d82e0 --- /dev/null +++ b/tests/ui/lint/dangling-pointers-from-temporaries/calls.stderr @@ -0,0 +1,62 @@ +error: a dangling pointer will be produced because the temporary `CString` will be dropped + --> $DIR/calls.rs:27:29 + | +LL | let ptr = cstring().as_ptr(); + | --------- ^^^^^^ this pointer will immediately be invalid + | | + | this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | + = note: pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see +note: the lint level is defined here + --> $DIR/calls.rs:1:9 + | +LL | #![deny(dangling_pointers_from_temporaries)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a dangling pointer will be produced because the temporary `CString` will be dropped + --> $DIR/calls.rs:32:29 + | +LL | let ptr = cstring().as_ptr(); + | --------- ^^^^^^ this pointer will immediately be invalid + | | + | this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | + = note: pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see + +error: a dangling pointer will be produced because the temporary `CString` will be dropped + --> $DIR/calls.rs:41:37 + | +LL | let _ptr: *const u8 = cstring().as_ptr().cast(); + | --------- ^^^^^^ this pointer will immediately be invalid + | | + | this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | + = note: pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see + +error: a dangling pointer will be produced because the temporary `CString` will be dropped + --> $DIR/calls.rs:43:41 + | +LL | let _ptr: *const u8 = { cstring() }.as_ptr().cast(); + | ------------- ^^^^^^ this pointer will immediately be invalid + | | + | this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | + = note: pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see + +error: a dangling pointer will be produced because the temporary `CString` will be dropped + --> $DIR/calls.rs:45:39 + | +LL | let _ptr: *const u8 = { cstring().as_ptr() }.cast(); + | --------- ^^^^^^ this pointer will immediately be invalid + | | + | this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | + = note: pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see + +error: aborting due to 5 previous errors + diff --git a/tests/ui/lint/lint-temporary-cstring-as-param.rs b/tests/ui/lint/dangling-pointers-from-temporaries/cstring-as-param.rs similarity index 62% rename from tests/ui/lint/lint-temporary-cstring-as-param.rs rename to tests/ui/lint/dangling-pointers-from-temporaries/cstring-as-param.rs index 9f5805367e43d..fb6ed3632720e 100644 --- a/tests/ui/lint/lint-temporary-cstring-as-param.rs +++ b/tests/ui/lint/dangling-pointers-from-temporaries/cstring-as-param.rs @@ -1,4 +1,7 @@ +//@ check-pass + #![deny(temporary_cstring_as_ptr)] +//~^ WARNING lint `temporary_cstring_as_ptr` has been renamed to `dangling_pointers_from_temporaries` use std::ffi::CString; use std::os::raw::c_char; @@ -7,5 +10,4 @@ fn some_function(data: *const c_char) {} fn main() { some_function(CString::new("").unwrap().as_ptr()); - //~^ ERROR getting the inner pointer of a temporary `CString` } diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/cstring-as-param.stderr b/tests/ui/lint/dangling-pointers-from-temporaries/cstring-as-param.stderr new file mode 100644 index 0000000000000..dd54b4971dd87 --- /dev/null +++ b/tests/ui/lint/dangling-pointers-from-temporaries/cstring-as-param.stderr @@ -0,0 +1,10 @@ +warning: lint `temporary_cstring_as_ptr` has been renamed to `dangling_pointers_from_temporaries` + --> $DIR/cstring-as-param.rs:3:9 + | +LL | #![deny(temporary_cstring_as_ptr)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `dangling_pointers_from_temporaries` + | + = note: `#[warn(renamed_and_removed_lints)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/lint/lint-temporary-cstring-as-ptr.rs b/tests/ui/lint/dangling-pointers-from-temporaries/cstring-as-ptr.rs similarity index 53% rename from tests/ui/lint/lint-temporary-cstring-as-ptr.rs rename to tests/ui/lint/dangling-pointers-from-temporaries/cstring-as-ptr.rs index fab792f128411..a98378794abc4 100644 --- a/tests/ui/lint/lint-temporary-cstring-as-ptr.rs +++ b/tests/ui/lint/dangling-pointers-from-temporaries/cstring-as-ptr.rs @@ -1,17 +1,18 @@ // this program is not technically incorrect, but is an obscure enough style to be worth linting #![deny(temporary_cstring_as_ptr)] +//~^ WARNING lint `temporary_cstring_as_ptr` has been renamed to `dangling_pointers_from_temporaries` use std::ffi::CString; macro_rules! mymacro { () => { let s = CString::new("some text").unwrap().as_ptr(); - //~^ ERROR getting the inner pointer of a temporary `CString` + //~^ ERROR a dangling pointer will be produced because the temporary `CString` will be dropped } } fn main() { let s = CString::new("some text").unwrap().as_ptr(); - //~^ ERROR getting the inner pointer of a temporary `CString` + //~^ ERROR a dangling pointer will be produced because the temporary `CString` will be dropped mymacro!(); } diff --git a/tests/ui/lint/lint-temporary-cstring-as-ptr.stderr b/tests/ui/lint/dangling-pointers-from-temporaries/cstring-as-ptr.stderr similarity index 57% rename from tests/ui/lint/lint-temporary-cstring-as-ptr.stderr rename to tests/ui/lint/dangling-pointers-from-temporaries/cstring-as-ptr.stderr index 4e5c8aa069364..5289fbb872342 100644 --- a/tests/ui/lint/lint-temporary-cstring-as-ptr.stderr +++ b/tests/ui/lint/dangling-pointers-from-temporaries/cstring-as-ptr.stderr @@ -1,24 +1,32 @@ -error: getting the inner pointer of a temporary `CString` - --> $DIR/lint-temporary-cstring-as-ptr.rs:14:48 +warning: lint `temporary_cstring_as_ptr` has been renamed to `dangling_pointers_from_temporaries` + --> $DIR/cstring-as-ptr.rs:2:9 + | +LL | #![deny(temporary_cstring_as_ptr)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `dangling_pointers_from_temporaries` + | + = note: `#[warn(renamed_and_removed_lints)]` on by default + +error: a dangling pointer will be produced because the temporary `CString` will be dropped + --> $DIR/cstring-as-ptr.rs:15:48 | LL | let s = CString::new("some text").unwrap().as_ptr(); - | ---------------------------------- ^^^^^^ this pointer will be invalid + | ---------------------------------- ^^^^^^ this pointer will immediately be invalid | | | this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | = note: pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: for more information, see https://doc.rust-lang.org/reference/destructors.html + = help: for more information, see note: the lint level is defined here - --> $DIR/lint-temporary-cstring-as-ptr.rs:2:9 + --> $DIR/cstring-as-ptr.rs:2:9 | LL | #![deny(temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: getting the inner pointer of a temporary `CString` - --> $DIR/lint-temporary-cstring-as-ptr.rs:8:52 +error: a dangling pointer will be produced because the temporary `CString` will be dropped + --> $DIR/cstring-as-ptr.rs:9:52 | LL | let s = CString::new("some text").unwrap().as_ptr(); - | ---------------------------------- ^^^^^^ this pointer will be invalid + | ---------------------------------- ^^^^^^ this pointer will immediately be invalid | | | this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime ... @@ -26,8 +34,8 @@ LL | mymacro!(); | ---------- in this macro invocation | = note: pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: for more information, see https://doc.rust-lang.org/reference/destructors.html + = help: for more information, see = note: this error originates in the macro `mymacro` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 2 previous errors +error: aborting due to 2 previous errors; 1 warning emitted diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/emacs.rs b/tests/ui/lint/dangling-pointers-from-temporaries/emacs.rs new file mode 100644 index 0000000000000..b9b7bd3ade146 --- /dev/null +++ b/tests/ui/lint/dangling-pointers-from-temporaries/emacs.rs @@ -0,0 +1,19 @@ +//@ check-pass + +#![deny(dangling_pointers_from_temporaries)] + +// The original code example comes from bindgen-produced code for emacs. +// Hence the name of the test. +// https://github.com/rust-lang/rust/pull/128985#issuecomment-2338951363 + +use std::ffi::{c_char, CString}; + +fn read(ptr: *const c_char) -> c_char { + unsafe { ptr.read() } +} + +fn main() { + let fnptr: Option c_char> = Some(read); + let x = fnptr.unwrap()(CString::new("foo").unwrap().as_ptr()); + assert_eq!(x as u8, b'f'); +} diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/example-from-issue123613.rs b/tests/ui/lint/dangling-pointers-from-temporaries/example-from-issue123613.rs new file mode 100644 index 0000000000000..0fb07a3f3bc97 --- /dev/null +++ b/tests/ui/lint/dangling-pointers-from-temporaries/example-from-issue123613.rs @@ -0,0 +1,13 @@ +#![deny(dangling_pointers_from_temporaries)] + +const MAX_PATH: usize = 260; +fn main() { + let str1 = String::with_capacity(MAX_PATH).as_mut_ptr(); + //~^ ERROR a dangling pointer will be produced because the temporary `String` will be dropped + let str2 = String::from("TotototototototototototototototototoT").as_ptr(); + //~^ ERROR a dangling pointer will be produced because the temporary `String` will be dropped + unsafe { + std::ptr::copy_nonoverlapping(str2, str1, 30); + println!("{:?}", String::from_raw_parts(str1, 30, 30)); + } +} diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/example-from-issue123613.stderr b/tests/ui/lint/dangling-pointers-from-temporaries/example-from-issue123613.stderr new file mode 100644 index 0000000000000..0de794f6ae285 --- /dev/null +++ b/tests/ui/lint/dangling-pointers-from-temporaries/example-from-issue123613.stderr @@ -0,0 +1,29 @@ +error: a dangling pointer will be produced because the temporary `String` will be dropped + --> $DIR/example-from-issue123613.rs:5:48 + | +LL | let str1 = String::with_capacity(MAX_PATH).as_mut_ptr(); + | ------------------------------- ^^^^^^^^^^ this pointer will immediately be invalid + | | + | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | + = note: pointers do not have a lifetime; when calling `as_mut_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see +note: the lint level is defined here + --> $DIR/example-from-issue123613.rs:1:9 + | +LL | #![deny(dangling_pointers_from_temporaries)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a dangling pointer will be produced because the temporary `String` will be dropped + --> $DIR/example-from-issue123613.rs:7:70 + | +LL | let str2 = String::from("TotototototototototototototototototoT").as_ptr(); + | ----------------------------------------------------- ^^^^^^ this pointer will immediately be invalid + | | + | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | + = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see + +error: aborting due to 2 previous errors + diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/ext.rs b/tests/ui/lint/dangling-pointers-from-temporaries/ext.rs new file mode 100644 index 0000000000000..a5e84d36090fc --- /dev/null +++ b/tests/ui/lint/dangling-pointers-from-temporaries/ext.rs @@ -0,0 +1,32 @@ +#![deny(dangling_pointers_from_temporaries)] + +use std::fmt::Debug; + +trait Ext1 { + fn dbg(self) -> Self + where + Self: Sized + Debug, + { + dbg!(&self); + self + } +} + +impl Ext1 for *const T {} + +trait Ext2 { + fn foo(self); +} + +impl Ext2 for *const u32 { + fn foo(self) { + dbg!(unsafe { self.read() }); + } +} + +fn main() { + let _ptr1 = Vec::::new().as_ptr().dbg(); + //~^ ERROR a dangling pointer will be produced because the temporary `Vec` will be dropped + let _ptr2 = vec![0].as_ptr().foo(); + //~^ ERROR a dangling pointer will be produced because the temporary `Vec` will be dropped +} diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/ext.stderr b/tests/ui/lint/dangling-pointers-from-temporaries/ext.stderr new file mode 100644 index 0000000000000..5d401c89c0ca2 --- /dev/null +++ b/tests/ui/lint/dangling-pointers-from-temporaries/ext.stderr @@ -0,0 +1,29 @@ +error: a dangling pointer will be produced because the temporary `Vec` will be dropped + --> $DIR/ext.rs:28:35 + | +LL | let _ptr1 = Vec::::new().as_ptr().dbg(); + | ----------------- ^^^^^^ this pointer will immediately be invalid + | | + | this `Vec` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | + = note: pointers do not have a lifetime; when calling `as_ptr` the `Vec` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see +note: the lint level is defined here + --> $DIR/ext.rs:1:9 + | +LL | #![deny(dangling_pointers_from_temporaries)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a dangling pointer will be produced because the temporary `Vec` will be dropped + --> $DIR/ext.rs:30:25 + | +LL | let _ptr2 = vec![0].as_ptr().foo(); + | ------- ^^^^^^ this pointer will immediately be invalid + | | + | this `Vec` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | + = note: pointers do not have a lifetime; when calling `as_ptr` the `Vec` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see + +error: aborting due to 2 previous errors + diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/methods.rs b/tests/ui/lint/dangling-pointers-from-temporaries/methods.rs new file mode 100644 index 0000000000000..26019b376d3a4 --- /dev/null +++ b/tests/ui/lint/dangling-pointers-from-temporaries/methods.rs @@ -0,0 +1,8 @@ +#![deny(dangling_pointers_from_temporaries)] + +fn main() { + vec![0u8].as_ptr(); + //~^ ERROR a dangling pointer will be produced because the temporary `Vec` will be dropped + vec![0u8].as_mut_ptr(); + //~^ ERROR a dangling pointer will be produced because the temporary `Vec` will be dropped +} diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/methods.stderr b/tests/ui/lint/dangling-pointers-from-temporaries/methods.stderr new file mode 100644 index 0000000000000..11c052c158e55 --- /dev/null +++ b/tests/ui/lint/dangling-pointers-from-temporaries/methods.stderr @@ -0,0 +1,29 @@ +error: a dangling pointer will be produced because the temporary `Vec` will be dropped + --> $DIR/methods.rs:4:15 + | +LL | vec![0u8].as_ptr(); + | --------- ^^^^^^ this pointer will immediately be invalid + | | + | this `Vec` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | + = note: pointers do not have a lifetime; when calling `as_ptr` the `Vec` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see +note: the lint level is defined here + --> $DIR/methods.rs:1:9 + | +LL | #![deny(dangling_pointers_from_temporaries)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a dangling pointer will be produced because the temporary `Vec` will be dropped + --> $DIR/methods.rs:6:15 + | +LL | vec![0u8].as_mut_ptr(); + | --------- ^^^^^^^^^^ this pointer will immediately be invalid + | | + | this `Vec` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | + = note: pointers do not have a lifetime; when calling `as_mut_ptr` the `Vec` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see + +error: aborting due to 2 previous errors + diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/temporaries.rs b/tests/ui/lint/dangling-pointers-from-temporaries/temporaries.rs new file mode 100644 index 0000000000000..1f216586ae81d --- /dev/null +++ b/tests/ui/lint/dangling-pointers-from-temporaries/temporaries.rs @@ -0,0 +1,136 @@ +#![allow(unused)] +#![deny(dangling_pointers_from_temporaries)] + +fn string() -> String { + "hello".into() +} + +struct Wrapper(String); + +fn main() { + // ConstBlock + const { String::new() }.as_ptr(); + + // Array + { + [string()].as_ptr(); // False negative + [true].as_ptr(); + } + + // Call + string().as_ptr(); + //~^ ERROR a dangling pointer will be produced because the temporary `String` will be dropped + + // MethodCall + "hello".to_string().as_ptr(); + //~^ ERROR a dangling pointer will be produced because the temporary `String` will be dropped + + // Tup + // impossible + + // Binary + (string() + "hello").as_ptr(); + //~^ ERROR a dangling pointer will be produced because the temporary `String` will be dropped + + // Path + { + let x = string(); + x.as_ptr(); + } + + // Unary + { + let x = string(); + let x: &String = &x; + (*x).as_ptr(); + (&[0u8]).as_ptr(); + (&string()).as_ptr(); // False negative + (*&string()).as_ptr(); // False negative + } + + // Lit + "hello".as_ptr(); + + // Cast + // impossible + + // Type + // impossible + + // DropTemps + // impossible + + // Let + // impossible + + // If + { + (if true { String::new() } else { "hello".into() }).as_ptr(); + //~^ ERROR a dangling pointer will be produced because the temporary `String` will be dropped + } + + // Loop + { + (loop { + break String::new(); + }) + .as_ptr(); + //~^ ERROR a dangling pointer will be produced because the temporary `String` will be dropped + } + + // Match + { + match string() { + s => s, + } + .as_ptr(); + //~^ ERROR a dangling pointer will be produced because the temporary `String` will be dropped + } + + // Closure + // impossible + + // Block + { string() }.as_ptr(); + //~^ ERROR a dangling pointer will be produced because the temporary `String` will be dropped + + // Assign, AssignOp + // impossible + + // Field + { + Wrapper(string()).0.as_ptr(); // False negative + let x = Wrapper(string()); + x.0.as_ptr(); + } + + // Index + { + vec![string()][0].as_ptr(); // False negative + let x = vec![string()]; + x[0].as_ptr(); + } + + // AddrOf, InlineAsm, OffsetOf + // impossible + + // Break, Continue, Ret + // are ! + + // Become, Yield + // unstable, are ! + + // Repeat + [0u8; 100].as_ptr(); + [const { String::new() }; 100].as_ptr(); + + // Struct + // Cannot test this without access to private fields of the linted types. + + // Err + // impossible + + // Macro + vec![0u8].as_ptr(); + //~^ ERROR a dangling pointer will be produced because the temporary `Vec` will be dropped +} diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/temporaries.stderr b/tests/ui/lint/dangling-pointers-from-temporaries/temporaries.stderr new file mode 100644 index 0000000000000..d2e9ac8c4e957 --- /dev/null +++ b/tests/ui/lint/dangling-pointers-from-temporaries/temporaries.stderr @@ -0,0 +1,99 @@ +error: a dangling pointer will be produced because the temporary `String` will be dropped + --> $DIR/temporaries.rs:21:14 + | +LL | string().as_ptr(); + | -------- ^^^^^^ this pointer will immediately be invalid + | | + | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | + = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see +note: the lint level is defined here + --> $DIR/temporaries.rs:2:9 + | +LL | #![deny(dangling_pointers_from_temporaries)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a dangling pointer will be produced because the temporary `String` will be dropped + --> $DIR/temporaries.rs:25:25 + | +LL | "hello".to_string().as_ptr(); + | ------------------- ^^^^^^ this pointer will immediately be invalid + | | + | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | + = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see + +error: a dangling pointer will be produced because the temporary `String` will be dropped + --> $DIR/temporaries.rs:32:26 + | +LL | (string() + "hello").as_ptr(); + | -------------------- ^^^^^^ this pointer will immediately be invalid + | | + | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | + = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see + +error: a dangling pointer will be produced because the temporary `String` will be dropped + --> $DIR/temporaries.rs:68:61 + | +LL | (if true { String::new() } else { "hello".into() }).as_ptr(); + | --------------------------------------------------- ^^^^^^ this pointer will immediately be invalid + | | + | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | + = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see + +error: a dangling pointer will be produced because the temporary `String` will be dropped + --> $DIR/temporaries.rs:77:10 + | +LL | / (loop { +LL | | break String::new(); +LL | | }) + | |__________- this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime +LL | .as_ptr(); + | ^^^^^^ this pointer will immediately be invalid + | + = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see + +error: a dangling pointer will be produced because the temporary `String` will be dropped + --> $DIR/temporaries.rs:86:10 + | +LL | / match string() { +LL | | s => s, +LL | | } + | |_________- this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime +LL | .as_ptr(); + | ^^^^^^ this pointer will immediately be invalid + | + = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see + +error: a dangling pointer will be produced because the temporary `String` will be dropped + --> $DIR/temporaries.rs:94:18 + | +LL | { string() }.as_ptr(); + | ------------ ^^^^^^ this pointer will immediately be invalid + | | + | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | + = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see + +error: a dangling pointer will be produced because the temporary `Vec` will be dropped + --> $DIR/temporaries.rs:134:15 + | +LL | vec![0u8].as_ptr(); + | --------- ^^^^^^ this pointer will immediately be invalid + | | + | this `Vec` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | + = note: pointers do not have a lifetime; when calling `as_ptr` the `Vec` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see + +error: aborting due to 8 previous errors + diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/types.rs b/tests/ui/lint/dangling-pointers-from-temporaries/types.rs new file mode 100644 index 0000000000000..2b515d3e6d5a2 --- /dev/null +++ b/tests/ui/lint/dangling-pointers-from-temporaries/types.rs @@ -0,0 +1,52 @@ +#![deny(dangling_pointers_from_temporaries)] + +use std::cell::Cell; +use std::ffi::{CStr, CString}; +use std::mem::MaybeUninit; + +struct AsPtrFake; + +impl AsPtrFake { + fn as_ptr(&self) -> *const () { + std::ptr::null() + } +} + +fn declval() -> T { + loop {} +} + +fn main() { + declval::().as_ptr(); + //~^ ERROR a dangling pointer will be produced because the temporary `CString` will be dropped + declval::().as_ptr(); + //~^ ERROR a dangling pointer will be produced because the temporary `String` will be dropped + declval::>().as_ptr(); + //~^ ERROR a dangling pointer will be produced because the temporary `Vec` will be dropped + declval::>().as_ptr(); + //~^ ERROR a dangling pointer will be produced because the temporary `Box` will be dropped + declval::>().as_ptr(); + //~^ ERROR a dangling pointer will be produced because the temporary `Box<[u8]>` will be dropped + declval::>().as_ptr(); + //~^ ERROR a dangling pointer will be produced because the temporary `Box` will be dropped + declval::>().as_ptr(); + //~^ ERROR a dangling pointer will be produced because the temporary `Box` will be dropped + declval::<[u8; 10]>().as_ptr(); + //~^ ERROR a dangling pointer will be produced because the temporary `[u8; 10]` will be dropped + declval::>().as_ptr(); + //~^ ERROR a dangling pointer will be produced because the temporary `Box<[u8; 10]>` will be dropped + declval::>>().as_ptr(); + //~^ ERROR a dangling pointer will be produced because the temporary `Box>` will be dropped + declval::>().as_ptr(); + //~^ ERROR a dangling pointer will be produced because the temporary `Box` will be dropped + declval::>>>>().as_ptr(); + //~^ ERROR a dangling pointer will be produced because the temporary `Box>>>` will be dropped + declval::>().as_ptr(); + //~^ ERROR a dangling pointer will be produced because the temporary `Cell` will be dropped + declval::>().as_ptr(); + //~^ ERROR a dangling pointer will be produced because the temporary `MaybeUninit` will be dropped + declval::>().as_ptr(); + //~^ ERROR a dangling pointer will be produced because the temporary `Vec` will be dropped + declval::>().as_ptr(); + declval::().as_ptr(); +} diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/types.stderr b/tests/ui/lint/dangling-pointers-from-temporaries/types.stderr new file mode 100644 index 0000000000000..c582a4c6540cf --- /dev/null +++ b/tests/ui/lint/dangling-pointers-from-temporaries/types.stderr @@ -0,0 +1,172 @@ +error: a dangling pointer will be produced because the temporary `CString` will be dropped + --> $DIR/types.rs:20:26 + | +LL | declval::().as_ptr(); + | -------------------- ^^^^^^ this pointer will immediately be invalid + | | + | this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | + = note: pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see +note: the lint level is defined here + --> $DIR/types.rs:1:9 + | +LL | #![deny(dangling_pointers_from_temporaries)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a dangling pointer will be produced because the temporary `String` will be dropped + --> $DIR/types.rs:22:25 + | +LL | declval::().as_ptr(); + | ------------------- ^^^^^^ this pointer will immediately be invalid + | | + | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | + = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see + +error: a dangling pointer will be produced because the temporary `Vec` will be dropped + --> $DIR/types.rs:24:26 + | +LL | declval::>().as_ptr(); + | -------------------- ^^^^^^ this pointer will immediately be invalid + | | + | this `Vec` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | + = note: pointers do not have a lifetime; when calling `as_ptr` the `Vec` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see + +error: a dangling pointer will be produced because the temporary `Box` will be dropped + --> $DIR/types.rs:26:31 + | +LL | declval::>().as_ptr(); + | ------------------------- ^^^^^^ this pointer will immediately be invalid + | | + | this `Box` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | + = note: pointers do not have a lifetime; when calling `as_ptr` the `Box` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see + +error: a dangling pointer will be produced because the temporary `Box<[u8]>` will be dropped + --> $DIR/types.rs:28:28 + | +LL | declval::>().as_ptr(); + | ---------------------- ^^^^^^ this pointer will immediately be invalid + | | + | this `Box<[u8]>` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | + = note: pointers do not have a lifetime; when calling `as_ptr` the `Box<[u8]>` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see + +error: a dangling pointer will be produced because the temporary `Box` will be dropped + --> $DIR/types.rs:30:27 + | +LL | declval::>().as_ptr(); + | --------------------- ^^^^^^ this pointer will immediately be invalid + | | + | this `Box` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | + = note: pointers do not have a lifetime; when calling `as_ptr` the `Box` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see + +error: a dangling pointer will be produced because the temporary `Box` will be dropped + --> $DIR/types.rs:32:28 + | +LL | declval::>().as_ptr(); + | ---------------------- ^^^^^^ this pointer will immediately be invalid + | | + | this `Box` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | + = note: pointers do not have a lifetime; when calling `as_ptr` the `Box` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see + +error: a dangling pointer will be produced because the temporary `[u8; 10]` will be dropped + --> $DIR/types.rs:34:27 + | +LL | declval::<[u8; 10]>().as_ptr(); + | --------------------- ^^^^^^ this pointer will immediately be invalid + | | + | this `[u8; 10]` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | + = note: pointers do not have a lifetime; when calling `as_ptr` the `[u8; 10]` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see + +error: a dangling pointer will be produced because the temporary `Box<[u8; 10]>` will be dropped + --> $DIR/types.rs:36:32 + | +LL | declval::>().as_ptr(); + | -------------------------- ^^^^^^ this pointer will immediately be invalid + | | + | this `Box<[u8; 10]>` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | + = note: pointers do not have a lifetime; when calling `as_ptr` the `Box<[u8; 10]>` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see + +error: a dangling pointer will be produced because the temporary `Box>` will be dropped + --> $DIR/types.rs:38:31 + | +LL | declval::>>().as_ptr(); + | ------------------------- ^^^^^^ this pointer will immediately be invalid + | | + | this `Box>` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | + = note: pointers do not have a lifetime; when calling `as_ptr` the `Box>` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see + +error: a dangling pointer will be produced because the temporary `Box` will be dropped + --> $DIR/types.rs:40:30 + | +LL | declval::>().as_ptr(); + | ------------------------ ^^^^^^ this pointer will immediately be invalid + | | + | this `Box` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | + = note: pointers do not have a lifetime; when calling `as_ptr` the `Box` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see + +error: a dangling pointer will be produced because the temporary `Box>>>` will be dropped + --> $DIR/types.rs:42:43 + | +LL | declval::>>>>().as_ptr(); + | ------------------------------------- ^^^^^^ this pointer will immediately be invalid + | | + | this `Box>>>` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | + = note: pointers do not have a lifetime; when calling `as_ptr` the `Box>>>` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see + +error: a dangling pointer will be produced because the temporary `Cell` will be dropped + --> $DIR/types.rs:44:27 + | +LL | declval::>().as_ptr(); + | --------------------- ^^^^^^ this pointer will immediately be invalid + | | + | this `Cell` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | + = note: pointers do not have a lifetime; when calling `as_ptr` the `Cell` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see + +error: a dangling pointer will be produced because the temporary `MaybeUninit` will be dropped + --> $DIR/types.rs:46:34 + | +LL | declval::>().as_ptr(); + | ---------------------------- ^^^^^^ this pointer will immediately be invalid + | | + | this `MaybeUninit` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | + = note: pointers do not have a lifetime; when calling `as_ptr` the `MaybeUninit` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see + +error: a dangling pointer will be produced because the temporary `Vec` will be dropped + --> $DIR/types.rs:48:33 + | +LL | declval::>().as_ptr(); + | --------------------------- ^^^^^^ this pointer will immediately be invalid + | | + | this `Vec` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | + = note: pointers do not have a lifetime; when calling `as_ptr` the `Vec` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see + +error: aborting due to 15 previous errors + diff --git a/tests/ui/lint/lint-temporary-cstring-as-param.stderr b/tests/ui/lint/lint-temporary-cstring-as-param.stderr deleted file mode 100644 index 7aa21f2560c2d..0000000000000 --- a/tests/ui/lint/lint-temporary-cstring-as-param.stderr +++ /dev/null @@ -1,18 +0,0 @@ -error: getting the inner pointer of a temporary `CString` - --> $DIR/lint-temporary-cstring-as-param.rs:9:45 - | -LL | some_function(CString::new("").unwrap().as_ptr()); - | ------------------------- ^^^^^^ this pointer will be invalid - | | - | this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime - | - = note: pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: for more information, see https://doc.rust-lang.org/reference/destructors.html -note: the lint level is defined here - --> $DIR/lint-temporary-cstring-as-param.rs:1:9 - | -LL | #![deny(temporary_cstring_as_ptr)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error -