Skip to content

Commit d7a580e

Browse files
committed
Remove FIXME and make it retain old raw string hashes
1 parent 2bdb645 commit d7a580e

File tree

3 files changed

+78
-53
lines changed

3 files changed

+78
-53
lines changed

clippy_lints/src/bare_dos_device_names.rs

Lines changed: 42 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1-
use clippy_utils::{
2-
diagnostics::span_lint_and_then, get_parent_expr, is_from_proc_macro, match_def_path, path_res, paths::PATH_NEW,
3-
ty::is_type_diagnostic_item,
4-
};
5-
use rustc_ast::LitKind;
1+
use clippy_utils::diagnostics::span_lint_and_then;
2+
use clippy_utils::paths::PATH_NEW;
3+
use clippy_utils::ty::is_type_diagnostic_item;
4+
use clippy_utils::{get_parent_expr, is_from_proc_macro, match_def_path, path_res};
5+
use rustc_ast::{LitKind, StrStyle};
66
use rustc_errors::Applicability;
77
use rustc_hir::def_id::DefId;
88
use rustc_hir::{Expr, ExprKind, QPath};
99
use rustc_lint::{LateContext, LateLintPass, LintContext};
10-
use rustc_middle::{lint::in_external_macro, ty};
10+
use rustc_middle::lint::in_external_macro;
11+
use rustc_middle::ty;
1112
use rustc_session::{declare_lint_pass, declare_tool_lint};
1213
use rustc_span::{sym, Symbol};
14+
use std::borrow::Cow;
1315

1416
declare_clippy_lint! {
1517
/// ### What it does
@@ -41,7 +43,7 @@ impl<'tcx> LateLintPass<'tcx> for BareDosDeviceNames {
4143
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
4244
if !in_external_macro(cx.sess(), expr.span)
4345
&& let ExprKind::Lit(arg) = expr.kind
44-
&& let LitKind::Str(str_sym, _) = arg.node
46+
&& let LitKind::Str(str_sym, str_style) = arg.node
4547
&& matches!(
4648
&*str_sym.as_str().to_ascii_lowercase(),
4749
"aux"
@@ -55,6 +57,9 @@ impl<'tcx> LateLintPass<'tcx> for BareDosDeviceNames {
5557
// Using `CONIN$` and `CONOUT$` is common enough in other languages that it may
5658
// trip up a couple newbies coming to rust. Besides, it's unlikely someone will
5759
// ever use `CONIN$` as a filename.
60+
//
61+
// TODO: Perhaps `com10` etc. are also DOS device names? `com42` is used in
62+
// `starship-rs` so perhaps they are. But this needs confirmation.
5863
| "com1"
5964
| "com2"
6065
| "com3"
@@ -77,7 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for BareDosDeviceNames {
7782
| "prn"
7883
)
7984
&& let Some(parent) = get_parent_expr(cx, expr)
80-
&& (is_path_constructor(cx, parent) || is_path_ty(cx, expr, parent))
85+
&& (is_path_like_constructor(cx, parent) || is_path_like_ty(cx, expr, parent))
8186
&& !is_from_proc_macro(cx, expr)
8287
{
8388
span_lint_and_then(
@@ -86,20 +91,26 @@ impl<'tcx> LateLintPass<'tcx> for BareDosDeviceNames {
8691
expr.span,
8792
"this path refers to a DOS device",
8893
|diag| {
94+
// Keep `r###` and `###`
95+
let (prefix, hashes) = if let StrStyle::Raw(num) = str_style {
96+
(Cow::Borrowed("r"), "#".repeat(num as usize).into())
97+
} else {
98+
(Cow::Borrowed(""), Cow::Borrowed(""))
99+
};
100+
89101
// Suggest making current behavior explicit
90102
diag.span_suggestion_verbose(
91103
expr.span,
92-
"if this is intended, try",
93-
// FIXME: I have zero clue why it normalizes this. `\` -> `/`
94-
format!(r#"r"\\.\{str_sym}"\"#),
104+
"if this is intended, use",
105+
format!(r#"r{hashes}"\\.\{str_sym}"{hashes}"#),
95106
Applicability::MaybeIncorrect,
96107
);
97108

98109
// Suggest making the code refer to a file or folder in the current directory
99110
diag.span_suggestion_verbose(
100111
expr.span,
101-
"if this was intended to point to a file or folder, try",
102-
format!("\"./{str_sym}\""),
112+
"if this was intended to point to a file or folder, use",
113+
format!(r#"{prefix}{hashes}"./{str_sym}"{hashes}"#),
103114
Applicability::MaybeIncorrect,
104115
);
105116
}
@@ -112,9 +123,7 @@ impl<'tcx> LateLintPass<'tcx> for BareDosDeviceNames {
112123
/// parent `Expr`, for performance's sake.
113124
///
114125
/// We can't use `is_path_ty` as these take `AsRef<OsStr>` or similar.
115-
///
116-
/// TODO: Should we lint `OsStr` too, in `is_path_ty`? I personally don't think so.
117-
fn is_path_constructor(cx: &LateContext<'_>, parent: &Expr<'_>) -> bool {
126+
fn is_path_like_constructor(cx: &LateContext<'_>, parent: &Expr<'_>) -> bool {
118127
enum DefPathOrTyAndName {
119128
/// Something from `clippy_utils::paths`.
120129
DefPath(&'static [&'static str]),
@@ -136,21 +145,25 @@ fn is_path_constructor(cx: &LateContext<'_>, parent: &Expr<'_>) -> bool {
136145
&& let QPath::TypeRelative(ty, last_segment) = qpath
137146
&& let Some(call_def_id) = path_res(cx, path).opt_def_id()
138147
&& let Some(ty_def_id) = path_res(cx, ty).opt_def_id()
148+
&& LINTED_METHODS.iter().any(|method| match method {
149+
DefPath(path) => match_def_path(cx, call_def_id, path),
150+
TyAndName((ty_name, method_name)) => {
151+
cx.tcx.is_diagnostic_item(*ty_name, ty_def_id) && last_segment.ident.name == *method_name
152+
},
153+
})
139154
{
140-
return LINTED_METHODS.iter().any(|method| {
141-
match method {
142-
DefPath(path) => match_def_path(cx, call_def_id, path),
143-
TyAndName((ty_name, method_name)) => {
144-
cx.tcx.is_diagnostic_item(*ty_name, ty_def_id) && last_segment.ident.name == *method_name
145-
},
146-
}
147-
});
155+
return true;
148156
}
149157

150158
false
151159
}
152160

153161
/// Gets the `DefId` and arguments of `expr`, if it's a `Call` or `MethodCall`
162+
///
163+
/// TODO: Move this to `clippy_utils` and extend it to give more info (not just `DefId` and
164+
/// arguments). There are many lints that often need this sorta functionality. Most recently
165+
/// `incorrect_partial_ord_impl_on_ord_type`, but basically all `methods` lints can use this to lint
166+
/// `Self::method(self)` as well.
154167
fn get_def_id_and_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<(DefId, &'tcx [Expr<'tcx>])> {
155168
match expr.kind {
156169
ExprKind::Call(path, args) => Some((path_res(cx, path).opt_def_id()?, args)),
@@ -161,16 +174,14 @@ fn get_def_id_and_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) ->
161174

162175
/// Given a `Ty`, returns whether it is likely a path type, like `Path` or `PathBuf`. Also returns
163176
/// true if it's `impl AsRef<Path>`, `T: AsRef<Path>`, etc. You get the idea.
164-
fn is_path_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, parent: &'tcx Expr<'tcx>) -> bool {
177+
fn is_path_like_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, parent: &'tcx Expr<'tcx>) -> bool {
165178
const LINTED_TRAITS: &[(Symbol, Symbol)] = &[
166179
(sym::AsRef, sym::Path),
167180
(sym::AsMut, sym::Path),
168-
// Basically useless, but let's lint these anyway
169181
(sym::AsRef, sym::PathBuf),
170182
(sym::AsMut, sym::PathBuf),
171183
(sym::Into, sym::Path),
172184
(sym::Into, sym::PathBuf),
173-
// Never seen `From` used in a generic context before, but let's lint these anyway
174185
(sym::From, sym::Path),
175186
(sym::From, sym::PathBuf),
176187
// TODO: Let's add more traits here.
@@ -204,14 +215,11 @@ fn is_path_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, parent: &'tc
204215
// I believe `0` is always `Self`, i.e., `T` or `impl <trait>` so get `1` instead
205216
&& let [_, subst] = trit.trait_ref.substs.as_slice()
206217
&& let Some(as_ref_ty) = subst.as_type()
207-
{
208-
for (trait_sym, ty_sym) in LINTED_TRAITS {
209-
if cx.tcx.is_diagnostic_item(*trait_sym, trit.trait_ref.def_id)
218+
&& LINTED_TRAITS.iter().any(|(trait_sym, ty_sym)| {
219+
cx.tcx.is_diagnostic_item(*trait_sym, trit.trait_ref.def_id)
210220
&& is_type_diagnostic_item(cx, as_ref_ty, *ty_sym)
211-
{
212-
return true;
213-
}
214-
}
221+
}) {
222+
return true;
215223
}
216224
}
217225

tests/ui/bare_dos_device_names.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
//@aux-build:proc_macros.rs:proc-macro
2-
#![allow(clippy::no_effect, unused)]
2+
#![allow(clippy::needless_raw_string_hashes, clippy::no_effect, unused)]
33
#![warn(clippy::bare_dos_device_names)]
44

55
#[macro_use]
66
extern crate proc_macros;
77

88
use std::fs::File;
9-
use std::path::Path;
10-
use std::path::PathBuf;
9+
use std::path::{Path, PathBuf};
1110

1211
fn a<T: AsRef<Path>>(t: T) {}
1312

@@ -20,6 +19,9 @@ fn main() {
2019
b("conin$");
2120
File::open("conin$");
2221
std::path::PathBuf::from("a");
22+
// Keep raw string
23+
Path::new(r##"aux"##);
24+
// Don't lint
2325
PathBuf::from("a");
2426
Path::new("a");
2527
external! {

tests/ui/bare_dos_device_names.stderr

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,63 @@
11
error: this path refers to a DOS device
2-
--> $DIR/bare_dos_device_names.rs:19:7
2+
--> $DIR/bare_dos_device_names.rs:18:7
33
|
44
LL | a("con");
55
| ^^^^^
66
|
77
= note: `-D clippy::bare-dos-device-names` implied by `-D warnings`
8-
help: if this is intended, try
8+
help: if this is intended, use
99
|
10-
LL | a(r"//./con"/);
11-
| ~~~~~~~~~~~
12-
help: if this was intended to point to a file or folder, try
10+
LL | a(r"//./con");
11+
| ~~~~~~~~~~
12+
help: if this was intended to point to a file or folder, use
1313
|
1414
LL | a("./con");
1515
| ~~~~~~~
1616

1717
error: this path refers to a DOS device
18-
--> $DIR/bare_dos_device_names.rs:20:7
18+
--> $DIR/bare_dos_device_names.rs:19:7
1919
|
2020
LL | b("conin$");
2121
| ^^^^^^^^
2222
|
23-
help: if this is intended, try
23+
help: if this is intended, use
2424
|
25-
LL | b(r"//./conin$"/);
26-
| ~~~~~~~~~~~~~~
27-
help: if this was intended to point to a file or folder, try
25+
LL | b(r"//./conin$");
26+
| ~~~~~~~~~~~~~
27+
help: if this was intended to point to a file or folder, use
2828
|
2929
LL | b("./conin$");
3030
| ~~~~~~~~~~
3131

3232
error: this path refers to a DOS device
33-
--> $DIR/bare_dos_device_names.rs:21:16
33+
--> $DIR/bare_dos_device_names.rs:20:16
3434
|
3535
LL | File::open("conin$");
3636
| ^^^^^^^^
3737
|
38-
help: if this is intended, try
38+
help: if this is intended, use
3939
|
40-
LL | File::open(r"//./conin$"/);
41-
| ~~~~~~~~~~~~~~
42-
help: if this was intended to point to a file or folder, try
40+
LL | File::open(r"//./conin$");
41+
| ~~~~~~~~~~~~~
42+
help: if this was intended to point to a file or folder, use
4343
|
4444
LL | File::open("./conin$");
4545
| ~~~~~~~~~~
4646

47-
error: aborting due to 3 previous errors
47+
error: this path refers to a DOS device
48+
--> $DIR/bare_dos_device_names.rs:23:15
49+
|
50+
LL | Path::new(r##"aux"##);
51+
| ^^^^^^^^^^
52+
|
53+
help: if this is intended, use
54+
|
55+
LL | Path::new(r##"//./aux"##);
56+
| ~~~~~~~~~~~~~~
57+
help: if this was intended to point to a file or folder, use
58+
|
59+
LL | Path::new(r##"./aux"##);
60+
| ~~~~~~~~~~~~
61+
62+
error: aborting due to 4 previous errors
4863

0 commit comments

Comments
 (0)