53 lines
2.0 KiB
Rust
53 lines
2.0 KiB
Rust
use clippy_utils::diagnostics::span_lint_and_then;
|
|
use clippy_utils::expr_or_init;
|
|
use clippy_utils::source::snippet_opt;
|
|
use clippy_utils::ty::is_type_diagnostic_item;
|
|
use rustc_ast::ast::LitKind;
|
|
use rustc_errors::Applicability;
|
|
use rustc_hir::{Expr, ExprKind};
|
|
use rustc_lint::LateContext;
|
|
use rustc_span::symbol::sym;
|
|
use rustc_span::Span;
|
|
|
|
use super::JOIN_ABSOLUTE_PATHS;
|
|
|
|
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, recv: &'tcx Expr<'tcx>, join_arg: &'tcx Expr<'tcx>, expr_span: Span) {
|
|
let ty = cx.typeck_results().expr_ty(recv).peel_refs();
|
|
if (is_type_diagnostic_item(cx, ty, sym::Path) || is_type_diagnostic_item(cx, ty, sym::PathBuf))
|
|
&& let ExprKind::Lit(spanned) = expr_or_init(cx, join_arg).kind
|
|
&& let LitKind::Str(symbol, _) = spanned.node
|
|
&& let sym_str = symbol.as_str()
|
|
&& sym_str.starts_with(['/', '\\'])
|
|
{
|
|
span_lint_and_then(
|
|
cx,
|
|
JOIN_ABSOLUTE_PATHS,
|
|
join_arg.span,
|
|
"argument to `Path::join` starts with a path separator",
|
|
|diag| {
|
|
let arg_str = snippet_opt(cx, spanned.span).unwrap_or_else(|| "..".to_string());
|
|
|
|
let no_separator = if sym_str.starts_with('/') {
|
|
arg_str.replacen('/', "", 1)
|
|
} else {
|
|
arg_str.replacen('\\', "", 1)
|
|
};
|
|
|
|
diag.note("joining a path starting with separator will replace the path instead")
|
|
.span_suggestion(
|
|
spanned.span,
|
|
"if this is unintentional, try removing the starting separator",
|
|
no_separator,
|
|
Applicability::Unspecified,
|
|
)
|
|
.span_suggestion(
|
|
expr_span,
|
|
"if this is intentional, consider using `Path::new`",
|
|
format!("PathBuf::from({arg_str})"),
|
|
Applicability::Unspecified,
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|