|
1 | 1 | use rustc_hir::{Expr, ExprKind, LangItem};
|
2 |
| -use rustc_middle::ty::Ty; |
| 2 | +use rustc_middle::ty::{self, Ty}; |
3 | 3 | use rustc_session::{declare_lint, declare_lint_pass};
|
4 |
| -use rustc_span::symbol::{sym, Ident}; |
| 4 | +use rustc_span::symbol::sym; |
5 | 5 |
|
6 | 6 | use crate::lints::InstantlyDangling;
|
7 | 7 | use crate::{LateContext, LateLintPass, LintContext};
|
@@ -45,42 +45,57 @@ declare_lint_pass!(DanglingPointers => [TEMPORARY_CSTRING_AS_PTR, INSTANTLY_DANG
|
45 | 45 |
|
46 | 46 | impl<'tcx> LateLintPass<'tcx> for DanglingPointers {
|
47 | 47 | fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
48 |
| - // We have a method call. |
49 |
| - let ExprKind::MethodCall(method, receiver, _args, _span) = expr.kind else { |
50 |
| - return; |
51 |
| - }; |
52 |
| - let Ident { name: method_name, span: method_span } = method.ident; |
53 |
| - |
54 |
| - // The method is `.as_ptr()` or `.as_mut_ptr`. |
55 |
| - if method_name != sym::as_ptr && method_name != sym::as_mut_ptr { |
56 |
| - return; |
| 48 | + if let ExprKind::MethodCall(as_ptr_path, as_ptr_receiver, ..) = expr.kind |
| 49 | + && as_ptr_path.ident.name == sym::as_ptr |
| 50 | + && let ExprKind::MethodCall(unwrap_path, unwrap_receiver, ..) = as_ptr_receiver.kind |
| 51 | + && (unwrap_path.ident.name == sym::unwrap || unwrap_path.ident.name == sym::expect) |
| 52 | + && lint_cstring_as_ptr(cx, unwrap_receiver) |
| 53 | + { |
| 54 | + cx.emit_span_lint( |
| 55 | + TEMPORARY_CSTRING_AS_PTR, |
| 56 | + as_ptr_path.ident.span, |
| 57 | + InstantlyDangling { |
| 58 | + callee: as_ptr_path.ident.name, |
| 59 | + ty: "CString".into(), |
| 60 | + ptr_span: as_ptr_path.ident.span, |
| 61 | + temporary_span: as_ptr_receiver.span, |
| 62 | + }, |
| 63 | + ); |
| 64 | + return; // One lint is enough |
57 | 65 | }
|
58 | 66 |
|
59 |
| - // It is called on a temporary rvalue. |
60 |
| - if !is_temporary_rvalue(receiver) { |
61 |
| - return; |
| 67 | + if let ExprKind::MethodCall(method, receiver, _args, _span) = expr.kind |
| 68 | + && matches!(method.ident.name, sym::as_ptr | sym::as_mut_ptr) |
| 69 | + && is_temporary_rvalue(receiver) |
| 70 | + && let ty = cx.typeck_results().expr_ty(receiver) |
| 71 | + && is_interesting(cx, ty) |
| 72 | + { |
| 73 | + cx.emit_span_lint( |
| 74 | + INSTANTLY_DANGLING_POINTER, |
| 75 | + method.ident.span, |
| 76 | + InstantlyDangling { |
| 77 | + callee: method.ident.name, |
| 78 | + ty: ty.to_string(), |
| 79 | + ptr_span: method.ident.span, |
| 80 | + temporary_span: receiver.span, |
| 81 | + }, |
| 82 | + ) |
62 | 83 | }
|
| 84 | + } |
| 85 | +} |
63 | 86 |
|
64 |
| - // The temporary value's type is array, box, Vec, String, or CString |
65 |
| - let ty = cx.typeck_results().expr_ty(receiver); |
66 |
| - let Some(is_cstring) = as_container(cx, ty) else { |
67 |
| - return; |
68 |
| - }; |
69 |
| - |
70 |
| - let span = method_span; |
71 |
| - let decorator = InstantlyDangling { |
72 |
| - callee: method_name, |
73 |
| - ty: ty.to_string(), |
74 |
| - ptr_span: method_span, |
75 |
| - temporary_span: receiver.span, |
76 |
| - }; |
77 |
| - |
78 |
| - if is_cstring { |
79 |
| - cx.emit_span_lint(TEMPORARY_CSTRING_AS_PTR, span, decorator); |
80 |
| - } else { |
81 |
| - cx.emit_span_lint(INSTANTLY_DANGLING_POINTER, span, decorator); |
82 |
| - }; |
| 87 | +fn lint_cstring_as_ptr(cx: &LateContext<'_>, source: &rustc_hir::Expr<'_>) -> bool { |
| 88 | + let source_type = cx.typeck_results().expr_ty(source); |
| 89 | + if let ty::Adt(def, args) = source_type.kind() { |
| 90 | + if cx.tcx.is_diagnostic_item(sym::Result, def.did()) { |
| 91 | + if let ty::Adt(adt, _) = args.type_at(0).kind() { |
| 92 | + if cx.tcx.is_diagnostic_item(sym::cstring_type, adt.did()) { |
| 93 | + return true; |
| 94 | + } |
| 95 | + } |
| 96 | + } |
83 | 97 | }
|
| 98 | + false |
84 | 99 | }
|
85 | 100 |
|
86 | 101 | fn is_temporary_rvalue(expr: &Expr<'_>) -> bool {
|
@@ -131,37 +146,27 @@ fn is_temporary_rvalue(expr: &Expr<'_>) -> bool {
|
131 | 146 | }
|
132 | 147 | }
|
133 | 148 |
|
134 |
| -// None => not a container |
135 |
| -// Some(true) => CString |
136 |
| -// Some(false) => String, Vec, box, array, MaybeUninit, Cell |
137 |
| -fn as_container(cx: &LateContext<'_>, ty: Ty<'_>) -> Option<bool> { |
| 149 | +// Array, Vec, String, CString, MaybeUninit, Cell, Box<[_]>, Box<str>, Box<CStr>, |
| 150 | +// or any of the above in arbitrary many nested Box'es. |
| 151 | +fn is_interesting(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { |
138 | 152 | if ty.is_array() {
|
139 |
| - Some(false) |
| 153 | + true |
140 | 154 | } else if ty.is_box() {
|
141 | 155 | let inner = ty.boxed_ty();
|
142 |
| - // We only care about Box<[..]>, Box<str>, Box<CStr>, |
143 |
| - // or Box<T> iff T is another type we care about |
144 |
| - if inner.is_slice() |
| 156 | + inner.is_slice() |
145 | 157 | || inner.is_str()
|
146 | 158 | || inner.ty_adt_def().is_some_and(|def| cx.tcx.is_lang_item(def.did(), LangItem::CStr))
|
147 |
| - || as_container(cx, inner).is_some() |
148 |
| - { |
149 |
| - Some(false) |
150 |
| - } else { |
151 |
| - None |
152 |
| - } |
| 159 | + || is_interesting(cx, inner) |
153 | 160 | } else if let Some(def) = ty.ty_adt_def() {
|
154 | 161 | for lang_item in [LangItem::String, LangItem::MaybeUninit] {
|
155 | 162 | if cx.tcx.is_lang_item(def.did(), lang_item) {
|
156 |
| - return Some(false); |
| 163 | + return true; |
157 | 164 | }
|
158 | 165 | }
|
159 |
| - match cx.tcx.get_diagnostic_name(def.did()) { |
160 |
| - Some(sym::cstring_type) => Some(true), |
161 |
| - Some(sym::Vec | sym::Cell) => Some(false), |
162 |
| - _ => None, |
163 |
| - } |
| 166 | + cx.tcx |
| 167 | + .get_diagnostic_name(def.did()) |
| 168 | + .is_some_and(|name| matches!(name, sym::cstring_type | sym::Vec | sym::Cell)) |
164 | 169 | } else {
|
165 |
| - None |
| 170 | + false |
166 | 171 | }
|
167 | 172 | }
|
0 commit comments