Skip to content

Commit c173ea6

Browse files
committed
Auto merge of #12446 - y21:check_fn_span_lint, r=xFrednet
use `span_lint_hir` instead of `span_lint` in more lints Decided to grep for `check_(fn|block)` and look where `span_lint` is used, since some lints lint will then emit a lint on a statement or expression in that function, which would use the wrong lint level attributes The `LintContext` keeps track of the last entered HIR node that had any attributes, and uses those (and its parents) for figuring out the lint level when a lint is emitted However, this only works when we actually emit a lint at the same node as the `check_*` function we are in. If we're in `check_fn` and we emit a lint on a statement within that function, then there is no way to allow the lint only for that one statement (if `span_lint` is used). It would only count allow attributes on the function changelog: [`needless_return`]: [`useless_let_if_seq`]: [`mut_mut`]: [`read_zero_byte_vec`]: [`unused_io_amount`]: [`unused_peekable`]: now respects `#[allow]` attributes on the affected statement instead of only on the enclosing block or function
2 parents b2f9c4c + ced8bc5 commit c173ea6

15 files changed

+134
-52
lines changed

clippy_lints/src/let_if_seq.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use clippy_utils::diagnostics::span_lint_and_then;
1+
use clippy_utils::diagnostics::span_lint_hir_and_then;
22
use clippy_utils::path_to_local_id;
33
use clippy_utils::source::snippet;
44
use clippy_utils::visitors::is_local_used;
@@ -122,9 +122,10 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
122122
value=snippet(cx, value.span, "<value>"),
123123
default=snippet(cx, default.span, "<default>"),
124124
);
125-
span_lint_and_then(
125+
span_lint_hir_and_then(
126126
cx,
127127
USELESS_LET_IF_SEQ,
128+
local.hir_id,
128129
span,
129130
"`if _ { .. } else { .. }` is an expression",
130131
|diag| {

clippy_lints/src/mut_mut.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use clippy_utils::diagnostics::span_lint;
1+
use clippy_utils::diagnostics::{span_lint, span_lint_hir};
22
use clippy_utils::higher;
33
use rustc_hir as hir;
44
use rustc_hir::intravisit;
@@ -87,17 +87,19 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> {
8787
intravisit::walk_expr(self, body);
8888
} else if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, e) = expr.kind {
8989
if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, _) = e.kind {
90-
span_lint(
90+
span_lint_hir(
9191
self.cx,
9292
MUT_MUT,
93+
expr.hir_id,
9394
expr.span,
9495
"generally you want to avoid `&mut &mut _` if possible",
9596
);
9697
} else if let ty::Ref(_, ty, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() {
9798
if ty.peel_refs().is_sized(self.cx.tcx, self.cx.param_env) {
98-
span_lint(
99+
span_lint_hir(
99100
self.cx,
100101
MUT_MUT,
102+
expr.hir_id,
101103
expr.span,
102104
"this expression mutably borrows a mutable reference. Consider reborrowing",
103105
);

clippy_lints/src/read_zero_byte_vec.rs

+36-20
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
1+
use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
22
use clippy_utils::get_enclosing_block;
33
use clippy_utils::higher::{get_vec_init_kind, VecInitKind};
44
use clippy_utils::source::snippet;
@@ -77,37 +77,53 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec {
7777
if let Some(expr) = visitor.read_zero_expr {
7878
let applicability = Applicability::MaybeIncorrect;
7979
match vec_init_kind {
80-
VecInitKind::WithConstCapacity(len) => {
81-
span_lint_and_sugg(
80+
VecInitKind::WithConstCapacity(len) => span_lint_hir_and_then(
81+
cx,
82+
READ_ZERO_BYTE_VEC,
83+
expr.hir_id,
84+
expr.span,
85+
"reading zero byte data to `Vec`",
86+
|diag| {
87+
diag.span_suggestion(
88+
expr.span,
89+
"try",
90+
format!("{}.resize({len}, 0); {}", ident.as_str(), snippet(cx, expr.span, "..")),
91+
applicability,
92+
);
93+
},
94+
),
95+
VecInitKind::WithExprCapacity(hir_id) => {
96+
let e = cx.tcx.hir().expect_expr(hir_id);
97+
span_lint_hir_and_then(
8298
cx,
8399
READ_ZERO_BYTE_VEC,
100+
expr.hir_id,
84101
expr.span,
85102
"reading zero byte data to `Vec`",
86-
"try",
87-
format!("{}.resize({len}, 0); {}", ident.as_str(), snippet(cx, expr.span, "..")),
88-
applicability,
103+
|diag| {
104+
diag.span_suggestion(
105+
expr.span,
106+
"try",
107+
format!(
108+
"{}.resize({}, 0); {}",
109+
ident.as_str(),
110+
snippet(cx, e.span, ".."),
111+
snippet(cx, expr.span, "..")
112+
),
113+
applicability,
114+
);
115+
},
89116
);
90117
},
91-
VecInitKind::WithExprCapacity(hir_id) => {
92-
let e = cx.tcx.hir().expect_expr(hir_id);
93-
span_lint_and_sugg(
118+
_ => {
119+
span_lint_hir(
94120
cx,
95121
READ_ZERO_BYTE_VEC,
122+
expr.hir_id,
96123
expr.span,
97124
"reading zero byte data to `Vec`",
98-
"try",
99-
format!(
100-
"{}.resize({}, 0); {}",
101-
ident.as_str(),
102-
snippet(cx, e.span, ".."),
103-
snippet(cx, expr.span, "..")
104-
),
105-
applicability,
106125
);
107126
},
108-
_ => {
109-
span_lint(cx, READ_ZERO_BYTE_VEC, expr.span, "reading zero byte data to `Vec`");
110-
},
111127
}
112128
}
113129
}

clippy_lints/src/redundant_closure_call.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::rustc_lint::LintContext;
2-
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
2+
use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir};
33
use clippy_utils::get_parent_expr;
44
use clippy_utils::sugg::Sugg;
55
use hir::Param;
@@ -273,9 +273,10 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall {
273273
&& ident == path.segments[0].ident
274274
&& count_closure_usage(cx, block, path) == 1
275275
{
276-
span_lint(
276+
span_lint_hir(
277277
cx,
278278
REDUNDANT_CLOSURE_CALL,
279+
second.hir_id,
279280
second.span,
280281
"closure called just once immediately after it was declared",
281282
);

clippy_lints/src/returns.rs

+22-9
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then};
1+
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
22
use clippy_utils::source::{snippet_opt, snippet_with_context};
33
use clippy_utils::sugg::has_enclosing_paren;
44
use clippy_utils::visitors::{for_each_expr_with_closures, Descend};
@@ -380,7 +380,7 @@ fn check_final_expr<'tcx>(
380380
return;
381381
}
382382

383-
emit_return_lint(cx, ret_span, semi_spans, &replacement);
383+
emit_return_lint(cx, ret_span, semi_spans, &replacement, expr.hir_id);
384384
},
385385
ExprKind::If(_, then, else_clause_opt) => {
386386
check_block_return(cx, &then.kind, peeled_drop_expr.span, semi_spans.clone());
@@ -415,18 +415,31 @@ fn expr_contains_conjunctive_ifs<'tcx>(expr: &'tcx Expr<'tcx>) -> bool {
415415
contains_if(expr, false)
416416
}
417417

418-
fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, semi_spans: Vec<Span>, replacement: &RetReplacement<'_>) {
418+
fn emit_return_lint(
419+
cx: &LateContext<'_>,
420+
ret_span: Span,
421+
semi_spans: Vec<Span>,
422+
replacement: &RetReplacement<'_>,
423+
at: HirId,
424+
) {
419425
if ret_span.from_expansion() {
420426
return;
421427
}
422428

423-
span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| {
424-
let suggestions = std::iter::once((ret_span, replacement.to_string()))
425-
.chain(semi_spans.into_iter().map(|span| (span, String::new())))
426-
.collect();
429+
span_lint_hir_and_then(
430+
cx,
431+
NEEDLESS_RETURN,
432+
at,
433+
ret_span,
434+
"unneeded `return` statement",
435+
|diag| {
436+
let suggestions = std::iter::once((ret_span, replacement.to_string()))
437+
.chain(semi_spans.into_iter().map(|span| (span, String::new())))
438+
.collect();
427439

428-
diag.multipart_suggestion_verbose(replacement.sugg_help(), suggestions, replacement.applicability());
429-
});
440+
diag.multipart_suggestion_verbose(replacement.sugg_help(), suggestions, replacement.applicability());
441+
},
442+
);
430443
}
431444

432445
fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {

clippy_lints/src/unused_io_amount.rs

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
use clippy_utils::diagnostics::span_lint_and_then;
1+
use clippy_utils::diagnostics::span_lint_hir_and_then;
22
use clippy_utils::macros::{is_panic, root_macro_call_first_node};
33
use clippy_utils::{is_res_lang_ctor, is_trait_method, match_trait_method, paths, peel_blocks};
4-
use hir::{ExprKind, PatKind};
4+
use hir::{ExprKind, HirId, PatKind};
55
use rustc_hir as hir;
66
use rustc_lint::{LateContext, LateLintPass};
77
use rustc_session::declare_lint_pass;
@@ -135,22 +135,22 @@ fn check_expr<'a>(cx: &LateContext<'a>, expr: &'a hir::Expr<'a>) {
135135
&& is_ok_wild_or_dotdot_pattern(cx, pat)
136136
&& let Some(op) = should_lint(cx, init) =>
137137
{
138-
emit_lint(cx, cond.span, op, &[pat.span]);
138+
emit_lint(cx, cond.span, cond.hir_id, op, &[pat.span]);
139139
},
140140
// we will capture only the case where the match is Ok( ) or Err( )
141141
// prefer to match the minimum possible, and expand later if needed
142142
// to avoid false positives on something as used as this
143143
hir::ExprKind::Match(expr, [arm1, arm2], hir::MatchSource::Normal) if let Some(op) = should_lint(cx, expr) => {
144144
if non_consuming_ok_arm(cx, arm1) && non_consuming_err_arm(cx, arm2) {
145-
emit_lint(cx, expr.span, op, &[arm1.pat.span]);
145+
emit_lint(cx, expr.span, expr.hir_id, op, &[arm1.pat.span]);
146146
}
147147
if non_consuming_ok_arm(cx, arm2) && non_consuming_err_arm(cx, arm1) {
148-
emit_lint(cx, expr.span, op, &[arm2.pat.span]);
148+
emit_lint(cx, expr.span, expr.hir_id, op, &[arm2.pat.span]);
149149
}
150150
},
151151
hir::ExprKind::Match(_, _, hir::MatchSource::Normal) => {},
152152
_ if let Some(op) = should_lint(cx, expr) => {
153-
emit_lint(cx, expr.span, op, &[]);
153+
emit_lint(cx, expr.span, expr.hir_id, op, &[]);
154154
},
155155
_ => {},
156156
};
@@ -279,7 +279,7 @@ fn check_io_mode(cx: &LateContext<'_>, call: &hir::Expr<'_>) -> Option<IoOp> {
279279
}
280280
}
281281

282-
fn emit_lint(cx: &LateContext<'_>, span: Span, op: IoOp, wild_cards: &[Span]) {
282+
fn emit_lint(cx: &LateContext<'_>, span: Span, at: HirId, op: IoOp, wild_cards: &[Span]) {
283283
let (msg, help) = match op {
284284
IoOp::AsyncRead(false) => (
285285
"read amount is not handled",
@@ -301,7 +301,7 @@ fn emit_lint(cx: &LateContext<'_>, span: Span, op: IoOp, wild_cards: &[Span]) {
301301
IoOp::SyncWrite(true) | IoOp::AsyncWrite(true) => ("written amount is not handled", None),
302302
};
303303

304-
span_lint_and_then(cx, UNUSED_IO_AMOUNT, span, msg, |diag| {
304+
span_lint_hir_and_then(cx, UNUSED_IO_AMOUNT, at, span, msg, |diag| {
305305
if let Some(help_str) = help {
306306
diag.help(help_str);
307307
}

clippy_lints/src/unused_peekable.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use clippy_utils::diagnostics::span_lint_and_help;
1+
use clippy_utils::diagnostics::span_lint_hir_and_then;
22
use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable};
33
use clippy_utils::{fn_def_id, is_trait_method, path_to_local_id, peel_ref_operators};
44
use rustc_ast::Mutability;
@@ -79,13 +79,15 @@ impl<'tcx> LateLintPass<'tcx> for UnusedPeekable {
7979
}
8080

8181
if !vis.found_peek_call {
82-
span_lint_and_help(
82+
span_lint_hir_and_then(
8383
cx,
8484
UNUSED_PEEKABLE,
85+
local.hir_id,
8586
ident.span,
8687
"`peek` never called on `Peekable` iterator",
87-
None,
88-
"consider removing the call to `peekable`",
88+
|diag| {
89+
diag.help("consider removing the call to `peekable`");
90+
},
8991
);
9092
}
9193
}

tests/ui/let_if_seq.rs

+11
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,17 @@ fn early_return() -> u8 {
5757
foo
5858
}
5959

60+
fn allow_works() -> i32 {
61+
#[allow(clippy::useless_let_if_seq)]
62+
let x;
63+
if true {
64+
x = 1;
65+
} else {
66+
x = 2;
67+
}
68+
x
69+
}
70+
6071
fn main() {
6172
early_return();
6273
issue975();

tests/ui/let_if_seq.stderr

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: `if _ { .. } else { .. }` is an expression
2-
--> tests/ui/let_if_seq.rs:66:5
2+
--> tests/ui/let_if_seq.rs:77:5
33
|
44
LL | / let mut foo = 0;
55
LL | |
@@ -14,7 +14,7 @@ LL | | }
1414
= help: to override `-D warnings` add `#[allow(clippy::useless_let_if_seq)]`
1515

1616
error: `if _ { .. } else { .. }` is an expression
17-
--> tests/ui/let_if_seq.rs:73:5
17+
--> tests/ui/let_if_seq.rs:84:5
1818
|
1919
LL | / let mut bar = 0;
2020
LL | |
@@ -28,7 +28,7 @@ LL | | }
2828
= note: you might not need `mut` at all
2929

3030
error: `if _ { .. } else { .. }` is an expression
31-
--> tests/ui/let_if_seq.rs:83:5
31+
--> tests/ui/let_if_seq.rs:94:5
3232
|
3333
LL | / let quz;
3434
LL | |
@@ -40,7 +40,7 @@ LL | | }
4040
| |_____^ help: it is more idiomatic to write: `let quz = if f() { 42 } else { 0 };`
4141

4242
error: `if _ { .. } else { .. }` is an expression
43-
--> tests/ui/let_if_seq.rs:113:5
43+
--> tests/ui/let_if_seq.rs:124:5
4444
|
4545
LL | / let mut baz = 0;
4646
LL | |

tests/ui/mut_mut.rs

+5
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,8 @@ mod issue9035 {
8181

8282
fn bar(_: &mut impl Display) {}
8383
}
84+
85+
fn allow_works() {
86+
#[allow(clippy::mut_mut)]
87+
let _ = &mut &mut 1;
88+
}

tests/ui/needless_return.fixed

+7
Original file line numberDiff line numberDiff line change
@@ -316,4 +316,11 @@ fn test_match_as_stmt() {
316316
};
317317
}
318318

319+
fn allow_works() -> i32 {
320+
#[allow(clippy::needless_return, clippy::match_single_binding)]
321+
match () {
322+
() => return 42,
323+
}
324+
}
325+
319326
fn main() {}

tests/ui/needless_return.rs

+7
Original file line numberDiff line numberDiff line change
@@ -326,4 +326,11 @@ fn test_match_as_stmt() {
326326
};
327327
}
328328

329+
fn allow_works() -> i32 {
330+
#[allow(clippy::needless_return, clippy::match_single_binding)]
331+
match () {
332+
() => return 42,
333+
}
334+
}
335+
329336
fn main() {}

tests/ui/read_zero_byte_vec.rs

+6
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,10 @@ async fn test_tokio<R: TokioAsyncRead + Unpin>(r: &mut R) {
112112
//~^ ERROR: reading zero byte data to `Vec`
113113
}
114114

115+
fn allow_works<F: std::io::Read>(mut f: F) {
116+
let mut data = Vec::with_capacity(100);
117+
#[allow(clippy::read_zero_byte_vec)]
118+
f.read(&mut data).unwrap();
119+
}
120+
115121
fn main() {}

tests/ui/unused_io_amount.rs

+5
Original file line numberDiff line numberDiff line change
@@ -271,5 +271,10 @@ pub fn wildcards(rdr: &mut dyn std::io::Read) {
271271
}
272272
}
273273
}
274+
fn allow_works<F: std::io::Read>(mut f: F) {
275+
let mut data = Vec::with_capacity(100);
276+
#[allow(clippy::unused_io_amount)]
277+
f.read(&mut data).unwrap();
278+
}
274279

275280
fn main() {}

tests/ui/unused_peekable.rs

+6
Original file line numberDiff line numberDiff line change
@@ -174,3 +174,9 @@ fn valid() {
174174
let mut peekable = std::iter::empty::<u32>().peekable();
175175
takes_dyn(&mut peekable);
176176
}
177+
178+
fn allow_works() {
179+
#[allow(clippy::unused_peekable)]
180+
let iter = [1, 2, 3].iter().peekable();
181+
iter;
182+
}

0 commit comments

Comments
 (0)