|
1 |
| -use rustc_hir::{Expr, ExprKind}; |
| 1 | +use rustc_hir::{Expr, ExprKind, LangItem}; |
2 | 2 | use rustc_middle::ty;
|
3 | 3 | use rustc_session::{declare_lint, declare_lint_pass};
|
4 |
| -use rustc_span::symbol::sym; |
5 |
| -use rustc_span::Span; |
| 4 | +use rustc_span::symbol::{sym, Ident}; |
6 | 5 |
|
7 |
| -use crate::lints::CStringPtr; |
| 6 | +use crate::lints::TemporaryAsPtr; |
8 | 7 | use crate::{LateContext, LateLintPass, LintContext};
|
9 | 8 |
|
10 | 9 | declare_lint! {
|
@@ -33,38 +32,127 @@ declare_lint! {
|
33 | 32 | "detects getting the inner pointer of a temporary `CString`"
|
34 | 33 | }
|
35 | 34 |
|
36 |
| -declare_lint_pass!(TemporaryCStringAsPtr => [TEMPORARY_CSTRING_AS_PTR]); |
| 35 | +declare_lint! { |
| 36 | + /// TODO |
| 37 | + pub TEMPORARY_AS_PTR, |
| 38 | + Warn, |
| 39 | + "TODO" |
| 40 | +} |
| 41 | + |
| 42 | +declare_lint_pass!(TemporaryCStringAsPtr => [TEMPORARY_CSTRING_AS_PTR, TEMPORARY_AS_PTR]); |
37 | 43 |
|
38 | 44 | impl<'tcx> LateLintPass<'tcx> for TemporaryCStringAsPtr {
|
39 | 45 | fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
40 |
| - if let ExprKind::MethodCall(as_ptr_path, as_ptr_receiver, ..) = expr.kind |
41 |
| - && as_ptr_path.ident.name == sym::as_ptr |
42 |
| - && let ExprKind::MethodCall(unwrap_path, unwrap_receiver, ..) = as_ptr_receiver.kind |
43 |
| - && (unwrap_path.ident.name == sym::unwrap || unwrap_path.ident.name == sym::expect) |
44 |
| - { |
45 |
| - lint_cstring_as_ptr(cx, as_ptr_path.ident.span, unwrap_receiver, as_ptr_receiver); |
| 46 | + // We have a method call. |
| 47 | + let ExprKind::MethodCall(method, receiver, _args, _span) = expr.kind else { |
| 48 | + return; |
| 49 | + }; |
| 50 | + let Ident { name: method_name, span: method_span } = method.ident; |
| 51 | + tracing::debug!(?method); |
| 52 | + |
| 53 | + // The method is `.as_ptr()` or `.as_mut_ptr`. |
| 54 | + if method_name != sym::as_ptr && method_name != sym::as_mut_ptr { |
| 55 | + return; |
| 56 | + } |
| 57 | + |
| 58 | + // It is called on a temporary rvalue. |
| 59 | + let is_temp = is_temporary_rvalue(receiver); |
| 60 | + tracing::debug!(?receiver, ?is_temp); |
| 61 | + if !is_temp { |
| 62 | + return; |
46 | 63 | }
|
| 64 | + |
| 65 | + // The temporary value's type is array, box, Vec, String, or CString |
| 66 | + let ty = cx.typeck_results().expr_ty(receiver); |
| 67 | + tracing::debug!(?ty); |
| 68 | + |
| 69 | + // None => not a container |
| 70 | + // Some(true) => CString |
| 71 | + // Some(false) => String, Vec, box, array |
| 72 | + let lint_is_cstring = match ty.kind() { |
| 73 | + ty::Array(_, _) => Some(false), |
| 74 | + ty::Adt(def, _) if def.is_box() => Some(false), |
| 75 | + ty::Adt(def, _) if cx.tcx.lang_items().get(LangItem::String) == Some(def.did()) => { |
| 76 | + Some(false) |
| 77 | + } |
| 78 | + ty::Adt(def, _) => match cx.tcx.get_diagnostic_name(def.did()) { |
| 79 | + Some(sym::Vec) => Some(false), |
| 80 | + Some(sym::cstring_type) => Some(true), |
| 81 | + _ => None, |
| 82 | + }, |
| 83 | + _ => None, |
| 84 | + }; |
| 85 | + tracing::debug!(?lint_is_cstring); |
| 86 | + let Some(is_cstring) = lint_is_cstring else { |
| 87 | + return; |
| 88 | + }; |
| 89 | + |
| 90 | + let span = method.ident.span; |
| 91 | + let decorator = TemporaryAsPtr { |
| 92 | + method: method_name, |
| 93 | + ty: ty.to_string(), |
| 94 | + as_ptr_span: method_span, |
| 95 | + temporary_span: receiver.span, |
| 96 | + }; |
| 97 | + |
| 98 | + if is_cstring { |
| 99 | + cx.emit_span_lint(TEMPORARY_CSTRING_AS_PTR, span, decorator); |
| 100 | + } else { |
| 101 | + cx.emit_span_lint(TEMPORARY_AS_PTR, span, decorator); |
| 102 | + }; |
47 | 103 | }
|
48 | 104 | }
|
49 | 105 |
|
50 |
| -fn lint_cstring_as_ptr( |
51 |
| - cx: &LateContext<'_>, |
52 |
| - as_ptr_span: Span, |
53 |
| - source: &rustc_hir::Expr<'_>, |
54 |
| - unwrap: &rustc_hir::Expr<'_>, |
55 |
| -) { |
56 |
| - let source_type = cx.typeck_results().expr_ty(source); |
57 |
| - if let ty::Adt(def, args) = source_type.kind() { |
58 |
| - if cx.tcx.is_diagnostic_item(sym::Result, def.did()) { |
59 |
| - if let ty::Adt(adt, _) = args.type_at(0).kind() { |
60 |
| - if cx.tcx.is_diagnostic_item(sym::cstring_type, adt.did()) { |
61 |
| - cx.emit_span_lint( |
62 |
| - TEMPORARY_CSTRING_AS_PTR, |
63 |
| - as_ptr_span, |
64 |
| - CStringPtr { as_ptr: as_ptr_span, unwrap: unwrap.span }, |
65 |
| - ); |
66 |
| - } |
67 |
| - } |
| 106 | +fn is_temporary_rvalue(expr: &Expr<'_>) -> bool { |
| 107 | + match expr.kind { |
| 108 | + // We are not interested in these |
| 109 | + ExprKind::Cast(_, _) | ExprKind::Closure(_) | ExprKind::Tup(_) => false, |
| 110 | + |
| 111 | + // Const is not temporary. |
| 112 | + ExprKind::ConstBlock(_) => false, |
| 113 | + |
| 114 | + // This is literally lvalue. |
| 115 | + ExprKind::Path(_) => false, |
| 116 | + |
| 117 | + // Calls return rvalues. |
| 118 | + ExprKind::Call(_, _) |
| 119 | + | ExprKind::MethodCall(_, _, _, _) |
| 120 | + | ExprKind::Index(_, _, _) |
| 121 | + | ExprKind::Binary(_, _, _) => true, |
| 122 | + |
| 123 | + // TODO: Check if x: &String, *(x).as_ptr() gets triggered |
| 124 | + ExprKind::Unary(_, _) => true, |
| 125 | + |
| 126 | + // Inner blocks are rvalues. |
| 127 | + ExprKind::If(_, _, _) |
| 128 | + | ExprKind::Loop(_, _, _, _) |
| 129 | + | ExprKind::Match(_, _, _) |
| 130 | + | ExprKind::Block(_, _) => true, |
| 131 | + |
| 132 | + ExprKind::Field(parent, _) => is_temporary_rvalue(parent), |
| 133 | + |
| 134 | + // FIXME: some of these get promoted to const/'static ? |
| 135 | + ExprKind::Struct(_, _, _) |
| 136 | + | ExprKind::Array(_) |
| 137 | + | ExprKind::Repeat(_, _) |
| 138 | + | ExprKind::Lit(_) => true, |
| 139 | + |
| 140 | + // These typecheck to `!` |
| 141 | + ExprKind::Break(_, _) | ExprKind::Continue(_) | ExprKind::Ret(_) | ExprKind::Become(_) => { |
| 142 | + false |
68 | 143 | }
|
| 144 | + |
| 145 | + // These typecheck to `()` |
| 146 | + ExprKind::Assign(_, _, _) | ExprKind::AssignOp(_, _, _) => false, |
| 147 | + |
| 148 | + // Not applicable |
| 149 | + ExprKind::Type(_, _) | ExprKind::Err(_) | ExprKind::Let(_) => false, |
| 150 | + |
| 151 | + // These are compiler-magic macros |
| 152 | + ExprKind::AddrOf(_, _, _) | ExprKind::OffsetOf(_, _) | ExprKind::InlineAsm(_) => false, |
| 153 | + |
| 154 | + // TODO: WTF are these |
| 155 | + ExprKind::DropTemps(_) => todo!(), |
| 156 | + ExprKind::Yield(_, _) => todo!(), |
69 | 157 | }
|
70 | 158 | }
|
0 commit comments