]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_lint/src/methods.rs
New upstream version 1.65.0+dfsg1
[rustc.git] / compiler / rustc_lint / src / methods.rs
CommitLineData
29967ef6
XL
1use crate::LateContext;
2use crate::LateLintPass;
3use crate::LintContext;
064997fb 4use rustc_errors::fluent;
29967ef6
XL
5use rustc_hir::{Expr, ExprKind, PathSegment};
6use rustc_middle::ty;
7use rustc_span::{symbol::sym, ExpnKind, Span};
8
9declare_lint! {
10 /// The `temporary_cstring_as_ptr` lint detects getting the inner pointer of
11 /// a temporary `CString`.
12 ///
13 /// ### Example
14 ///
15 /// ```rust
16 /// # #![allow(unused)]
17 /// # use std::ffi::CString;
18 /// let c_str = CString::new("foo").unwrap().as_ptr();
19 /// ```
20 ///
21 /// {{produces}}
22 ///
23 /// ### Explanation
24 ///
25 /// The inner pointer of a `CString` lives only as long as the `CString` it
26 /// points to. Getting the inner pointer of a *temporary* `CString` allows the `CString`
27 /// to be dropped at the end of the statement, as it is not being referenced as far as the typesystem
28 /// is concerned. This means outside of the statement the pointer will point to freed memory, which
29 /// causes undefined behavior if the pointer is later dereferenced.
30 pub TEMPORARY_CSTRING_AS_PTR,
31 Warn,
32 "detects getting the inner pointer of a temporary `CString`"
33}
34
35declare_lint_pass!(TemporaryCStringAsPtr => [TEMPORARY_CSTRING_AS_PTR]);
36
37fn in_macro(span: Span) -> bool {
38 if span.from_expansion() {
39 !matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Desugaring(..))
40 } else {
41 false
42 }
43}
44
45fn first_method_call<'tcx>(
46 expr: &'tcx Expr<'tcx>,
f2b60f7d
FG
47) -> Option<(&'tcx PathSegment<'tcx>, &'tcx Expr<'tcx>)> {
48 if let ExprKind::MethodCall(path, receiver, args, ..) = &expr.kind {
49 if args.iter().any(|e| e.span.from_expansion()) || receiver.span.from_expansion() {
50 None
51 } else {
52 Some((path, *receiver))
53 }
29967ef6
XL
54 } else {
55 None
56 }
57}
58
59impl<'tcx> LateLintPass<'tcx> for TemporaryCStringAsPtr {
60 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
61 if in_macro(expr.span) {
62 return;
63 }
64
65 match first_method_call(expr) {
f2b60f7d 66 Some((path, unwrap_arg)) if path.ident.name == sym::as_ptr => {
29967ef6
XL
67 let as_ptr_span = path.ident.span;
68 match first_method_call(unwrap_arg) {
f2b60f7d 69 Some((path, receiver))
29967ef6
XL
70 if path.ident.name == sym::unwrap || path.ident.name == sym::expect =>
71 {
f2b60f7d 72 lint_cstring_as_ptr(cx, as_ptr_span, receiver, unwrap_arg);
29967ef6
XL
73 }
74 _ => return,
75 }
76 }
77 _ => return,
78 }
79 }
80}
81
82fn lint_cstring_as_ptr(
83 cx: &LateContext<'_>,
84 as_ptr_span: Span,
85 source: &rustc_hir::Expr<'_>,
86 unwrap: &rustc_hir::Expr<'_>,
87) {
88 let source_type = cx.typeck_results().expr_ty(source);
89 if let ty::Adt(def, substs) = source_type.kind() {
5e7ed085 90 if cx.tcx.is_diagnostic_item(sym::Result, def.did()) {
29967ef6 91 if let ty::Adt(adt, _) = substs.type_at(0).kind() {
5e7ed085 92 if cx.tcx.is_diagnostic_item(sym::cstring_type, adt.did()) {
29967ef6 93 cx.struct_span_lint(TEMPORARY_CSTRING_AS_PTR, as_ptr_span, |diag| {
064997fb
FG
94 diag.build(fluent::lint::cstring_ptr)
95 .span_label(as_ptr_span, fluent::lint::as_ptr_label)
96 .span_label(unwrap.span, fluent::lint::unwrap_label)
97 .note(fluent::lint::note)
98 .help(fluent::lint::help)
99 .emit();
29967ef6
XL
100 });
101 }
102 }
103 }
104 }
105}