blob: 042dec35f7c904c2a2cd3e093336e5c5c04ec06e [file] [log] [blame] [edit]
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::res::MaybeDef;
use clippy_utils::{eq_expr_value, sym};
use rustc_hir::{Expr, ExprKind, LangItem, QPath};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
use rustc_span::symbol::sym as rustc_sym;
declare_clippy_lint! {
/// ### What it does
///
/// Checks for usages of `Vec::from_raw_parts` and `String::from_raw_parts`
/// where the same expression is used for the length and the capacity.
///
/// ### Why is this bad?
///
/// If the same expression is being passed for the length and
/// capacity, it is most likely a semantic error. In the case of a
/// Vec, for example, the only way to end up with one that has
/// the same length and capacity is by going through a boxed slice,
/// e.g. `Box::from(some_vec)`, which shrinks the capacity to match
/// the length.
///
/// ### Example
///
/// ```no_run
/// #![feature(vec_into_raw_parts)]
/// let mut original: Vec::<i32> = Vec::with_capacity(20);
/// original.extend([1, 2, 3, 4, 5]);
///
/// let (ptr, mut len, cap) = original.into_raw_parts();
///
/// // I will add three more integers:
/// unsafe {
/// let ptr = ptr as *mut i32;
///
/// for i in 6..9 {
/// *ptr.add(i - 1) = i as i32;
/// len += 1;
/// }
/// }
///
/// // But I forgot the capacity was separate from the length:
/// let reconstructed = unsafe { Vec::from_raw_parts(ptr, len, len) };
/// ```
///
/// Use instead:
/// ```no_run
/// #![feature(vec_into_raw_parts)]
/// let mut original: Vec::<i32> = Vec::with_capacity(20);
/// original.extend([1, 2, 3, 4, 5]);
///
/// let (ptr, mut len, cap) = original.into_raw_parts();
///
/// // I will add three more integers:
/// unsafe {
/// let ptr = ptr as *mut i32;
///
/// for i in 6..9 {
/// *ptr.add(i - 1) = i as i32;
/// len += 1;
/// }
/// }
///
/// // This time, leverage the previously saved capacity:
/// let reconstructed = unsafe { Vec::from_raw_parts(ptr, len, cap) };
/// ```
#[clippy::version = "1.93.0"]
pub SAME_LENGTH_AND_CAPACITY,
pedantic,
"`from_raw_parts` with same length and capacity"
}
declare_lint_pass!(SameLengthAndCapacity => [SAME_LENGTH_AND_CAPACITY]);
impl<'tcx> LateLintPass<'tcx> for SameLengthAndCapacity {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let ExprKind::Call(path_expr, args) = expr.kind
&& let ExprKind::Path(QPath::TypeRelative(ty, fn_path)) = path_expr.kind
&& fn_path.ident.name == sym::from_raw_parts
&& args.len() >= 3
&& eq_expr_value(cx, &args[1], &args[2])
{
let middle_ty = cx.typeck_results().node_type(ty.hir_id);
if middle_ty.is_diag_item(cx, rustc_sym::Vec) {
span_lint_and_help(
cx,
SAME_LENGTH_AND_CAPACITY,
expr.span,
"usage of `Vec::from_raw_parts` with the same expression for length and capacity",
None,
"try `Box::from(slice::from_raw_parts(...)).into::<Vec<_>>()`",
);
} else if middle_ty.is_lang_item(cx, LangItem::String) {
span_lint_and_help(
cx,
SAME_LENGTH_AND_CAPACITY,
expr.span,
"usage of `String::from_raw_parts` with the same expression for length and capacity",
None,
"try `String::from(str::from_utf8_unchecked(slice::from_raw_parts(...)))`",
);
}
}
}
}