Skip to content

Commit ca78fb4

Browse files
authored
unwrap_used, expect_used: accept macro result as receiver (#14575)
changelog: [`unwrap_used`, `expect_used`]: lint even when the receiver is a macro expansion result This also paves the way for expanding more method call lints to expanded receivers or arguments. Fixes #13455
2 parents fc811f7 + 999db5c commit ca78fb4

File tree

3 files changed

+105
-44
lines changed

3 files changed

+105
-44
lines changed

clippy_lints/src/methods/mod.rs

Lines changed: 55 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -4709,6 +4709,8 @@ impl_lint_pass!(Methods => [
47094709
]);
47104710

47114711
/// Extracts a method call name, args, and `Span` of the method name.
4712+
/// This ensures that neither the receiver nor any of the arguments
4713+
/// come from expansion.
47124714
pub fn method_call<'tcx>(
47134715
recv: &'tcx Expr<'tcx>,
47144716
) -> Option<(&'tcx str, &'tcx Expr<'tcx>, &'tcx [Expr<'tcx>], Span, Span)> {
@@ -4907,6 +4909,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
49074909
impl Methods {
49084910
#[allow(clippy::too_many_lines)]
49094911
fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
4912+
// Handle method calls whose receiver and arguments may not come from expansion
49104913
if let Some((name, recv, args, span, call_span)) = method_call(expr) {
49114914
match (name, args) {
49124915
("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => {
@@ -5049,29 +5052,12 @@ impl Methods {
50495052
Some(("err", recv, [], err_span, _)) => {
50505053
err_expect::check(cx, expr, recv, span, err_span, self.msrv);
50515054
},
5052-
_ => unwrap_expect_used::check(
5053-
cx,
5054-
expr,
5055-
recv,
5056-
false,
5057-
self.allow_expect_in_consts,
5058-
self.allow_expect_in_tests,
5059-
unwrap_expect_used::Variant::Expect,
5060-
),
5055+
_ => {},
50615056
}
50625057
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
50635058
},
5064-
("expect_err", [_]) => {
5059+
("expect_err", [_]) | ("unwrap_err" | "unwrap_unchecked" | "unwrap_err_unchecked", []) => {
50655060
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
5066-
unwrap_expect_used::check(
5067-
cx,
5068-
expr,
5069-
recv,
5070-
true,
5071-
self.allow_expect_in_consts,
5072-
self.allow_expect_in_tests,
5073-
unwrap_expect_used::Variant::Expect,
5074-
);
50755061
},
50765062
("extend", [arg]) => {
50775063
string_extend_chars::check(cx, expr, recv, arg);
@@ -5437,27 +5423,6 @@ impl Methods {
54375423
_ => {},
54385424
}
54395425
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
5440-
unwrap_expect_used::check(
5441-
cx,
5442-
expr,
5443-
recv,
5444-
false,
5445-
self.allow_unwrap_in_consts,
5446-
self.allow_unwrap_in_tests,
5447-
unwrap_expect_used::Variant::Unwrap,
5448-
);
5449-
},
5450-
("unwrap_err", []) => {
5451-
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
5452-
unwrap_expect_used::check(
5453-
cx,
5454-
expr,
5455-
recv,
5456-
true,
5457-
self.allow_unwrap_in_consts,
5458-
self.allow_unwrap_in_tests,
5459-
unwrap_expect_used::Variant::Unwrap,
5460-
);
54615426
},
54625427
("unwrap_or", [u_arg]) => {
54635428
match method_call(recv) {
@@ -5486,9 +5451,6 @@ impl Methods {
54865451
}
54875452
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
54885453
},
5489-
("unwrap_unchecked" | "unwrap_err_unchecked", []) => {
5490-
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
5491-
},
54925454
("unwrap_or_else", [u_arg]) => {
54935455
match method_call(recv) {
54945456
Some(("map", recv, [map_arg], _, _))
@@ -5526,6 +5488,56 @@ impl Methods {
55265488
_ => {},
55275489
}
55285490
}
5491+
// Handle method calls whose receiver and arguments may come from expansion
5492+
if let ExprKind::MethodCall(path, recv, args, _call_span) = expr.kind {
5493+
match (path.ident.name.as_str(), args) {
5494+
("expect", [_]) if !matches!(method_call(recv), Some(("ok" | "err", _, [], _, _))) => {
5495+
unwrap_expect_used::check(
5496+
cx,
5497+
expr,
5498+
recv,
5499+
false,
5500+
self.allow_expect_in_consts,
5501+
self.allow_expect_in_tests,
5502+
unwrap_expect_used::Variant::Expect,
5503+
);
5504+
},
5505+
("expect_err", [_]) => {
5506+
unwrap_expect_used::check(
5507+
cx,
5508+
expr,
5509+
recv,
5510+
true,
5511+
self.allow_expect_in_consts,
5512+
self.allow_expect_in_tests,
5513+
unwrap_expect_used::Variant::Expect,
5514+
);
5515+
},
5516+
("unwrap", []) => {
5517+
unwrap_expect_used::check(
5518+
cx,
5519+
expr,
5520+
recv,
5521+
false,
5522+
self.allow_unwrap_in_consts,
5523+
self.allow_unwrap_in_tests,
5524+
unwrap_expect_used::Variant::Unwrap,
5525+
);
5526+
},
5527+
("unwrap_err", []) => {
5528+
unwrap_expect_used::check(
5529+
cx,
5530+
expr,
5531+
recv,
5532+
true,
5533+
self.allow_unwrap_in_consts,
5534+
self.allow_unwrap_in_tests,
5535+
unwrap_expect_used::Variant::Unwrap,
5536+
);
5537+
},
5538+
_ => {},
5539+
}
5540+
}
55295541
}
55305542
}
55315543

tests/ui/unwrap_expect_used.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,20 @@ fn main() {
6666
SOME.expect("Still not three?");
6767
}
6868
}
69+
70+
mod with_expansion {
71+
macro_rules! open {
72+
($file:expr) => {
73+
std::fs::File::open($file)
74+
};
75+
}
76+
77+
fn test(file: &str) {
78+
use std::io::Read;
79+
let mut s = String::new();
80+
let _ = open!(file).unwrap(); //~ unwrap_used
81+
let _ = open!(file).expect("can open"); //~ expect_used
82+
let _ = open!(file).unwrap_err(); //~ unwrap_used
83+
let _ = open!(file).expect_err("can open"); //~ expect_used
84+
}
85+
}

tests/ui/unwrap_expect_used.stderr

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,5 +50,37 @@ LL | a.expect_err("Hello error!");
5050
|
5151
= note: if this value is an `Ok`, it will panic
5252

53-
error: aborting due to 6 previous errors
53+
error: used `unwrap()` on a `Result` value
54+
--> tests/ui/unwrap_expect_used.rs:80:17
55+
|
56+
LL | let _ = open!(file).unwrap();
57+
| ^^^^^^^^^^^^^^^^^^^^
58+
|
59+
= note: if this value is an `Err`, it will panic
60+
61+
error: used `expect()` on a `Result` value
62+
--> tests/ui/unwrap_expect_used.rs:81:17
63+
|
64+
LL | let _ = open!(file).expect("can open");
65+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
66+
|
67+
= note: if this value is an `Err`, it will panic
68+
69+
error: used `unwrap_err()` on a `Result` value
70+
--> tests/ui/unwrap_expect_used.rs:82:17
71+
|
72+
LL | let _ = open!(file).unwrap_err();
73+
| ^^^^^^^^^^^^^^^^^^^^^^^^
74+
|
75+
= note: if this value is an `Ok`, it will panic
76+
77+
error: used `expect_err()` on a `Result` value
78+
--> tests/ui/unwrap_expect_used.rs:83:17
79+
|
80+
LL | let _ = open!(file).expect_err("can open");
81+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
82+
|
83+
= note: if this value is an `Ok`, it will panic
84+
85+
error: aborting due to 10 previous errors
5486

0 commit comments

Comments
 (0)