Skip to content

Commit 2b30a59

Browse files
committed
Auto merge of rust-lang#11996 - J-ZhengLi:issue11992, r=xFrednet,ARandomDev99
fix suggestion for [`len_zero`] with macros fixes: rust-lang#11992 changelog: fix suggestion for [`len_zero`] with macros
2 parents 41e814a + b456ed3 commit 2b30a59

File tree

4 files changed

+114
-4
lines changed

4 files changed

+114
-4
lines changed

clippy_lints/src/len_zero.rs

+21-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
2-
use clippy_utils::source::snippet_with_context;
3-
use clippy_utils::sugg::Sugg;
2+
use clippy_utils::source::{snippet_opt, snippet_with_context};
3+
use clippy_utils::sugg::{has_enclosing_paren, Sugg};
44
use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators};
55
use rustc_ast::ast::LitKind;
66
use rustc_errors::Applicability;
@@ -192,7 +192,7 @@ impl<'tcx> LateLintPass<'tcx> for LenZero {
192192

193193
if let ExprKind::Binary(Spanned { node: cmp, .. }, left, right) = expr.kind {
194194
// expr.span might contains parenthesis, see issue #10529
195-
let actual_span = left.span.with_hi(right.span.hi());
195+
let actual_span = span_without_enclosing_paren(cx, expr.span);
196196
match cmp {
197197
BinOpKind::Eq => {
198198
check_cmp(cx, actual_span, left, right, "", 0); // len == 0
@@ -218,6 +218,20 @@ impl<'tcx> LateLintPass<'tcx> for LenZero {
218218
}
219219
}
220220

221+
fn span_without_enclosing_paren(cx: &LateContext<'_>, span: Span) -> Span {
222+
let Some(snippet) = snippet_opt(cx, span) else {
223+
return span;
224+
};
225+
if has_enclosing_paren(snippet) {
226+
let source_map = cx.tcx.sess.source_map();
227+
let left_paren = source_map.start_point(span);
228+
let right_parent = source_map.end_point(span);
229+
left_paren.between(right_parent)
230+
} else {
231+
span
232+
}
233+
}
234+
221235
fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items: &[TraitItemRef]) {
222236
fn is_named_self(cx: &LateContext<'_>, item: &TraitItemRef, name: Symbol) -> bool {
223237
item.ident.name == name
@@ -495,6 +509,10 @@ fn check_for_is_empty(
495509
}
496510

497511
fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>, op: &str, compare_to: u32) {
512+
if method.span.from_expansion() {
513+
return;
514+
}
515+
498516
if let (&ExprKind::MethodCall(method_path, receiver, args, _), ExprKind::Lit(lit)) = (&method.kind, &lit.kind) {
499517
// check if we are in an is_empty() method
500518
if let Some(name) = get_item_name(cx, method) {

tests/ui/len_zero.fixed

+37
Original file line numberDiff line numberDiff line change
@@ -189,3 +189,40 @@ fn main() {
189189
fn test_slice(b: &[u8]) {
190190
if !b.is_empty() {}
191191
}
192+
193+
// issue #11992
194+
fn binop_with_macros() {
195+
macro_rules! len {
196+
($seq:ident) => {
197+
$seq.len()
198+
};
199+
}
200+
201+
macro_rules! compare_to {
202+
($val:literal) => {
203+
$val
204+
};
205+
($val:expr) => {{ $val }};
206+
}
207+
208+
macro_rules! zero {
209+
() => {
210+
0
211+
};
212+
}
213+
214+
let has_is_empty = HasIsEmpty;
215+
// Don't lint, suggesting changes might break macro compatibility.
216+
(len!(has_is_empty) > 0).then(|| println!("This can happen."));
217+
// Don't lint, suggesting changes might break macro compatibility.
218+
if len!(has_is_empty) == 0 {}
219+
// Don't lint
220+
if has_is_empty.len() == compare_to!(if true { 0 } else { 1 }) {}
221+
// This is fine
222+
if has_is_empty.len() == compare_to!(1) {}
223+
224+
if has_is_empty.is_empty() {}
225+
if has_is_empty.is_empty() {}
226+
227+
(!has_is_empty.is_empty()).then(|| println!("This can happen."));
228+
}

tests/ui/len_zero.rs

+37
Original file line numberDiff line numberDiff line change
@@ -189,3 +189,40 @@ fn main() {
189189
fn test_slice(b: &[u8]) {
190190
if b.len() != 0 {}
191191
}
192+
193+
// issue #11992
194+
fn binop_with_macros() {
195+
macro_rules! len {
196+
($seq:ident) => {
197+
$seq.len()
198+
};
199+
}
200+
201+
macro_rules! compare_to {
202+
($val:literal) => {
203+
$val
204+
};
205+
($val:expr) => {{ $val }};
206+
}
207+
208+
macro_rules! zero {
209+
() => {
210+
0
211+
};
212+
}
213+
214+
let has_is_empty = HasIsEmpty;
215+
// Don't lint, suggesting changes might break macro compatibility.
216+
(len!(has_is_empty) > 0).then(|| println!("This can happen."));
217+
// Don't lint, suggesting changes might break macro compatibility.
218+
if len!(has_is_empty) == 0 {}
219+
// Don't lint
220+
if has_is_empty.len() == compare_to!(if true { 0 } else { 1 }) {}
221+
// This is fine
222+
if has_is_empty.len() == compare_to!(1) {}
223+
224+
if has_is_empty.len() == compare_to!(0) {}
225+
if has_is_empty.len() == zero!() {}
226+
227+
(compare_to!(0) < has_is_empty.len()).then(|| println!("This can happen."));
228+
}

tests/ui/len_zero.stderr

+19-1
Original file line numberDiff line numberDiff line change
@@ -142,5 +142,23 @@ error: length comparison to zero
142142
LL | if b.len() != 0 {}
143143
| ^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!b.is_empty()`
144144

145-
error: aborting due to 23 previous errors
145+
error: length comparison to zero
146+
--> tests/ui/len_zero.rs:224:8
147+
|
148+
LL | if has_is_empty.len() == compare_to!(0) {}
149+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()`
150+
151+
error: length comparison to zero
152+
--> tests/ui/len_zero.rs:225:8
153+
|
154+
LL | if has_is_empty.len() == zero!() {}
155+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()`
156+
157+
error: length comparison to zero
158+
--> tests/ui/len_zero.rs:227:6
159+
|
160+
LL | (compare_to!(0) < has_is_empty.len()).then(|| println!("This can happen."));
161+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()`
162+
163+
error: aborting due to 26 previous errors
146164

0 commit comments

Comments
 (0)