about summary refs log tree commit diff
path: root/clippy_lints/src/methods/join_absolute_paths.rs
blob: 2ad070793cbb207146579af3a625ffd24d48fc0d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::expr_or_init;
use clippy_utils::source::snippet;
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::Span;
use rustc_span::symbol::sym;

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(cx, spanned.span, "..");

                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,
                    );
            },
        );
    }
}