Skip to content

Commit d212c38

Browse files
committed
Auto merge of rust-lang#6278 - ThibsG:DerefAddrOf, r=llogiq
Fix bad suggestions for `deref_addrof` and `try_err` lints Fix bad suggestions when in macro expansion for `deref_addrof` and `try_err` lints. Fixes: rust-lang#6234 Fixes: rust-lang#6242 Fixes: rust-lang#6237 changelog: none r? `@llogiq`
2 parents 2067a01 + 83e75f9 commit d212c38

File tree

8 files changed

+207
-15
lines changed

8 files changed

+207
-15
lines changed

clippy_lints/src/reference.rs

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
use crate::utils::{in_macro, snippet_with_applicability, span_lint_and_sugg};
1+
use crate::utils::{in_macro, snippet_opt, snippet_with_applicability, span_lint_and_sugg};
22
use if_chain::if_chain;
3-
use rustc_ast::ast::{Expr, ExprKind, UnOp};
3+
use rustc_ast::ast::{Expr, ExprKind, Mutability, UnOp};
44
use rustc_errors::Applicability;
55
use rustc_lint::{EarlyContext, EarlyLintPass};
66
use rustc_session::{declare_lint_pass, declare_tool_lint};
7+
use rustc_span::BytePos;
78

89
declare_clippy_lint! {
910
/// **What it does:** Checks for usage of `*&` and `*&mut` in expressions.
@@ -42,17 +43,45 @@ impl EarlyLintPass for DerefAddrOf {
4243
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) {
4344
if_chain! {
4445
if let ExprKind::Unary(UnOp::Deref, ref deref_target) = e.kind;
45-
if let ExprKind::AddrOf(_, _, ref addrof_target) = without_parens(deref_target).kind;
46+
if let ExprKind::AddrOf(_, ref mutability, ref addrof_target) = without_parens(deref_target).kind;
4647
if !in_macro(addrof_target.span);
4748
then {
4849
let mut applicability = Applicability::MachineApplicable;
50+
let sugg = if e.span.from_expansion() {
51+
if let Ok(macro_source) = cx.sess.source_map().span_to_snippet(e.span) {
52+
// Remove leading whitespace from the given span
53+
// e.g: ` $visitor` turns into `$visitor`
54+
let trim_leading_whitespaces = |span| {
55+
snippet_opt(cx, span).and_then(|snip| {
56+
#[allow(clippy::cast_possible_truncation)]
57+
snip.find(|c: char| !c.is_whitespace()).map(|pos| {
58+
span.lo() + BytePos(pos as u32)
59+
})
60+
}).map_or(span, |start_no_whitespace| e.span.with_lo(start_no_whitespace))
61+
};
62+
63+
let rpos = if *mutability == Mutability::Mut {
64+
macro_source.rfind("mut").expect("already checked this is a mutable reference") + "mut".len()
65+
} else {
66+
macro_source.rfind('&').expect("already checked this is a reference") + "&".len()
67+
};
68+
#[allow(clippy::cast_possible_truncation)]
69+
let span_after_ref = e.span.with_lo(BytePos(e.span.lo().0 + rpos as u32));
70+
let span = trim_leading_whitespaces(span_after_ref);
71+
snippet_with_applicability(cx, span, "_", &mut applicability)
72+
} else {
73+
snippet_with_applicability(cx, e.span, "_", &mut applicability)
74+
}
75+
} else {
76+
snippet_with_applicability(cx, addrof_target.span, "_", &mut applicability)
77+
}.to_string();
4978
span_lint_and_sugg(
5079
cx,
5180
DEREF_ADDROF,
5281
e.span,
5382
"immediately dereferencing a reference",
5483
"try this",
55-
format!("{}", snippet_with_applicability(cx, addrof_target.span, "_", &mut applicability)),
84+
sugg,
5685
applicability,
5786
);
5887
}

clippy_lints/src/try_err.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::utils::{
2-
is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet, snippet_with_macro_callsite,
3-
span_lint_and_sugg,
2+
differing_macro_contexts, in_macro, is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet,
3+
snippet_with_macro_callsite, span_lint_and_sugg,
44
};
55
use if_chain::if_chain;
66
use rustc_errors::Applicability;
@@ -92,8 +92,11 @@ impl<'tcx> LateLintPass<'tcx> for TryErr {
9292
};
9393

9494
let expr_err_ty = cx.typeck_results().expr_ty(err_arg);
95+
let differing_contexts = differing_macro_contexts(expr.span, err_arg.span);
9596

96-
let origin_snippet = if err_arg.span.from_expansion() {
97+
let origin_snippet = if in_macro(expr.span) && in_macro(err_arg.span) && differing_contexts {
98+
snippet(cx, err_arg.span.ctxt().outer_expn_data().call_site, "_")
99+
} else if err_arg.span.from_expansion() && !in_macro(expr.span) {
97100
snippet_with_macro_callsite(cx, err_arg.span, "_")
98101
} else {
99102
snippet(cx, err_arg.span, "_")

tests/ui/deref_addrof.fixed

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// run-rustfix
2+
#![warn(clippy::deref_addrof)]
23

34
fn get_number() -> usize {
45
10
@@ -10,7 +11,6 @@ fn get_reference(n: &usize) -> &usize {
1011

1112
#[allow(clippy::many_single_char_names, clippy::double_parens)]
1213
#[allow(unused_variables, unused_parens)]
13-
#[warn(clippy::deref_addrof)]
1414
fn main() {
1515
let a = 10;
1616
let aref = &a;
@@ -37,3 +37,27 @@ fn main() {
3737

3838
let b = *aref;
3939
}
40+
41+
#[rustfmt::skip]
42+
macro_rules! m {
43+
($visitor: expr) => {
44+
$visitor
45+
};
46+
}
47+
48+
#[rustfmt::skip]
49+
macro_rules! m_mut {
50+
($visitor: expr) => {
51+
$visitor
52+
};
53+
}
54+
55+
pub struct S;
56+
impl S {
57+
pub fn f(&self) -> &Self {
58+
m!(self)
59+
}
60+
pub fn f_mut(&self) -> &Self {
61+
m_mut!(self)
62+
}
63+
}

tests/ui/deref_addrof.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// run-rustfix
2+
#![warn(clippy::deref_addrof)]
23

34
fn get_number() -> usize {
45
10
@@ -10,7 +11,6 @@ fn get_reference(n: &usize) -> &usize {
1011

1112
#[allow(clippy::many_single_char_names, clippy::double_parens)]
1213
#[allow(unused_variables, unused_parens)]
13-
#[warn(clippy::deref_addrof)]
1414
fn main() {
1515
let a = 10;
1616
let aref = &a;
@@ -37,3 +37,27 @@ fn main() {
3737

3838
let b = **&aref;
3939
}
40+
41+
#[rustfmt::skip]
42+
macro_rules! m {
43+
($visitor: expr) => {
44+
*& $visitor
45+
};
46+
}
47+
48+
#[rustfmt::skip]
49+
macro_rules! m_mut {
50+
($visitor: expr) => {
51+
*& mut $visitor
52+
};
53+
}
54+
55+
pub struct S;
56+
impl S {
57+
pub fn f(&self) -> &Self {
58+
m!(self)
59+
}
60+
pub fn f_mut(&self) -> &Self {
61+
m_mut!(self)
62+
}
63+
}

tests/ui/deref_addrof.stderr

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,27 @@ error: immediately dereferencing a reference
4848
LL | let b = **&aref;
4949
| ^^^^^^ help: try this: `aref`
5050

51-
error: aborting due to 8 previous errors
51+
error: immediately dereferencing a reference
52+
--> $DIR/deref_addrof.rs:44:9
53+
|
54+
LL | *& $visitor
55+
| ^^^^^^^^^^^ help: try this: `$visitor`
56+
...
57+
LL | m!(self)
58+
| -------- in this macro invocation
59+
|
60+
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
61+
62+
error: immediately dereferencing a reference
63+
--> $DIR/deref_addrof.rs:51:9
64+
|
65+
LL | *& mut $visitor
66+
| ^^^^^^^^^^^^^^^ help: try this: `$visitor`
67+
...
68+
LL | m_mut!(self)
69+
| ------------ in this macro invocation
70+
|
71+
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
72+
73+
error: aborting due to 10 previous errors
5274

tests/ui/try_err.fixed

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,46 @@ fn nested_error() -> Result<i32, i32> {
7878
Ok(1)
7979
}
8080

81+
// Bad suggestion when in macro (see #6242)
82+
macro_rules! try_validation {
83+
($e: expr) => {{
84+
match $e {
85+
Ok(_) => 0,
86+
Err(_) => return Err(1),
87+
}
88+
}};
89+
}
90+
91+
macro_rules! ret_one {
92+
() => {
93+
1
94+
};
95+
}
96+
97+
macro_rules! try_validation_in_macro {
98+
($e: expr) => {{
99+
match $e {
100+
Ok(_) => 0,
101+
Err(_) => return Err(ret_one!()),
102+
}
103+
}};
104+
}
105+
106+
fn calling_macro() -> Result<i32, i32> {
107+
// macro
108+
try_validation!(Ok::<_, i32>(5));
109+
// `Err` arg is another macro
110+
try_validation_in_macro!(Ok::<_, i32>(5));
111+
Ok(5)
112+
}
113+
81114
fn main() {
82115
basic_test().unwrap();
83116
into_test().unwrap();
84117
negative_test().unwrap();
85118
closure_matches_test().unwrap();
86119
closure_into_test().unwrap();
120+
calling_macro().unwrap();
87121

88122
// We don't want to lint in external macros
89123
try_err!();

tests/ui/try_err.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,46 @@ fn nested_error() -> Result<i32, i32> {
7878
Ok(1)
7979
}
8080

81+
// Bad suggestion when in macro (see #6242)
82+
macro_rules! try_validation {
83+
($e: expr) => {{
84+
match $e {
85+
Ok(_) => 0,
86+
Err(_) => Err(1)?,
87+
}
88+
}};
89+
}
90+
91+
macro_rules! ret_one {
92+
() => {
93+
1
94+
};
95+
}
96+
97+
macro_rules! try_validation_in_macro {
98+
($e: expr) => {{
99+
match $e {
100+
Ok(_) => 0,
101+
Err(_) => Err(ret_one!())?,
102+
}
103+
}};
104+
}
105+
106+
fn calling_macro() -> Result<i32, i32> {
107+
// macro
108+
try_validation!(Ok::<_, i32>(5));
109+
// `Err` arg is another macro
110+
try_validation_in_macro!(Ok::<_, i32>(5));
111+
Ok(5)
112+
}
113+
81114
fn main() {
82115
basic_test().unwrap();
83116
into_test().unwrap();
84117
negative_test().unwrap();
85118
closure_matches_test().unwrap();
86119
closure_into_test().unwrap();
120+
calling_macro().unwrap();
87121

88122
// We don't want to lint in external macros
89123
try_err!();

tests/ui/try_err.stderr

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,28 +29,50 @@ LL | Err(err)?;
2929
| ^^^^^^^^^ help: try this: `return Err(err.into())`
3030

3131
error: returning an `Err(_)` with the `?` operator
32-
--> $DIR/try_err.rs:106:9
32+
--> $DIR/try_err.rs:86:23
33+
|
34+
LL | Err(_) => Err(1)?,
35+
| ^^^^^^^ help: try this: `return Err(1)`
36+
...
37+
LL | try_validation!(Ok::<_, i32>(5));
38+
| --------------------------------- in this macro invocation
39+
|
40+
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
41+
42+
error: returning an `Err(_)` with the `?` operator
43+
--> $DIR/try_err.rs:101:23
44+
|
45+
LL | Err(_) => Err(ret_one!())?,
46+
| ^^^^^^^^^^^^^^^^ help: try this: `return Err(ret_one!())`
47+
...
48+
LL | try_validation_in_macro!(Ok::<_, i32>(5));
49+
| ------------------------------------------ in this macro invocation
50+
|
51+
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
52+
53+
error: returning an `Err(_)` with the `?` operator
54+
--> $DIR/try_err.rs:140:9
3355
|
3456
LL | Err(foo!())?;
3557
| ^^^^^^^^^^^^ help: try this: `return Err(foo!())`
3658

3759
error: returning an `Err(_)` with the `?` operator
38-
--> $DIR/try_err.rs:113:9
60+
--> $DIR/try_err.rs:147:9
3961
|
4062
LL | Err(io::ErrorKind::WriteZero)?
4163
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Err(io::ErrorKind::WriteZero.into()))`
4264

4365
error: returning an `Err(_)` with the `?` operator
44-
--> $DIR/try_err.rs:115:9
66+
--> $DIR/try_err.rs:149:9
4567
|
4668
LL | Err(io::Error::new(io::ErrorKind::InvalidInput, "error"))?
4769
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, "error")))`
4870

4971
error: returning an `Err(_)` with the `?` operator
50-
--> $DIR/try_err.rs:123:9
72+
--> $DIR/try_err.rs:157:9
5173
|
5274
LL | Err(io::ErrorKind::NotFound)?
5375
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Some(Err(io::ErrorKind::NotFound.into())))`
5476

55-
error: aborting due to 8 previous errors
77+
error: aborting due to 10 previous errors
5678

0 commit comments

Comments
 (0)