Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit b1d26e5

Browse files
committed
Improved shared_code_in_if_blocks message and added test stderrs
1 parent 8efc6ac commit b1d26e5

File tree

10 files changed

+692
-64
lines changed

10 files changed

+692
-64
lines changed

clippy_lints/src/copies.rs

Lines changed: 135 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
use crate::utils::{both, count_eq, eq_expr_value, in_macro, search_same, SpanlessEq, SpanlessHash};
22
use crate::utils::{
3-
first_line_of_span, get_parent_expr, if_sequence, indent_of, parent_node_is_if_expr, reindent_multiline, snippet,
4-
snippet_opt, span_lint_and_note, span_lint_and_sugg, span_lint_and_then,
3+
first_line_of_span, get_enclosing_block, get_parent_expr, if_sequence, indent_of, parent_node_is_if_expr,
4+
reindent_multiline, snippet, snippet_opt, span_lint_and_note, span_lint_and_then, ContainsName,
55
};
66
use rustc_data_structures::fx::FxHashSet;
7-
use rustc_errors::Applicability;
7+
use rustc_errors::{Applicability, DiagnosticBuilder};
88
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
99
use rustc_hir::{Block, Expr, ExprKind, HirId};
1010
use rustc_lint::{LateContext, LateLintPass};
1111
use rustc_middle::hir::map::Map;
1212
use rustc_session::{declare_lint_pass, declare_tool_lint};
13-
use rustc_span::{source_map::Span, BytePos};
13+
use rustc_span::{source_map::Span, symbol::Symbol, BytePos};
1414
use std::borrow::Cow;
1515

1616
declare_clippy_lint! {
@@ -184,7 +184,6 @@ fn lint_same_then_else<'tcx>(
184184
expr: &'tcx Expr<'_>,
185185
) {
186186
// We only lint ifs with multiple blocks
187-
// TODO xFrednet 2021-01-01: Check if it's an else if block
188187
if blocks.len() < 2 || parent_node_is_if_expr(expr, cx) {
189188
return;
190189
}
@@ -242,52 +241,127 @@ fn lint_same_then_else<'tcx>(
242241

243242
// Only the start is the same
244243
if start_eq != 0 && end_eq == 0 && (!has_expr || !expr_eq) {
245-
emit_shared_code_in_if_blocks_lint(cx, start_eq, 0, false, blocks, expr);
246-
} else if end_eq != 0 && (!has_expr || !expr_eq) {
244+
let block = blocks[0];
245+
let start_stmts = block.stmts.split_at(start_eq).0;
246+
247+
let mut start_walker = UsedValueFinderVisitor::new(cx);
248+
for stmt in start_stmts {
249+
intravisit::walk_stmt(&mut start_walker, stmt);
250+
}
251+
252+
emit_shared_code_in_if_blocks_lint(
253+
cx,
254+
start_eq,
255+
0,
256+
false,
257+
check_for_warn_of_moved_symbol(cx, &start_walker.def_symbols, expr),
258+
blocks,
259+
expr,
260+
);
261+
} else if end_eq != 0 || (has_expr && expr_eq) {
247262
let block = blocks[blocks.len() - 1];
248-
let stmts = block.stmts.split_at(start_eq).1;
249-
let (block_stmts, moved_stmts) = stmts.split_at(stmts.len() - end_eq);
263+
let (start_stmts, block_stmts) = block.stmts.split_at(start_eq);
264+
let (block_stmts, end_stmts) = block_stmts.split_at(block_stmts.len() - end_eq);
265+
266+
// Scan start
267+
let mut start_walker = UsedValueFinderVisitor::new(cx);
268+
for stmt in start_stmts {
269+
intravisit::walk_stmt(&mut start_walker, stmt);
270+
}
271+
let mut moved_syms = start_walker.def_symbols;
250272

251273
// Scan block
252-
let mut walker = SymbolFinderVisitor::new(cx);
274+
let mut block_walker = UsedValueFinderVisitor::new(cx);
253275
for stmt in block_stmts {
254-
intravisit::walk_stmt(&mut walker, stmt);
276+
intravisit::walk_stmt(&mut block_walker, stmt);
255277
}
256-
let mut block_defs = walker.defs;
278+
let mut block_defs = block_walker.defs;
257279

258280
// Scan moved stmts
259281
let mut moved_start: Option<usize> = None;
260-
let mut walker = SymbolFinderVisitor::new(cx);
261-
for (index, stmt) in moved_stmts.iter().enumerate() {
262-
intravisit::walk_stmt(&mut walker, stmt);
282+
let mut end_walker = UsedValueFinderVisitor::new(cx);
283+
for (index, stmt) in end_stmts.iter().enumerate() {
284+
intravisit::walk_stmt(&mut end_walker, stmt);
263285

264-
for value in &walker.uses {
286+
for value in &end_walker.uses {
265287
// Well we can't move this and all prev statements. So reset
266288
if block_defs.contains(&value) {
267289
moved_start = Some(index + 1);
268-
walker.defs.drain().for_each(|x| {
290+
end_walker.defs.drain().for_each(|x| {
269291
block_defs.insert(x);
270292
});
293+
294+
end_walker.def_symbols.clear();
271295
}
272296
}
273297

274-
walker.uses.clear();
298+
end_walker.uses.clear();
275299
}
276300

277301
if let Some(moved_start) = moved_start {
278302
end_eq -= moved_start;
279303
}
280304

281305
let end_linable = if let Some(expr) = block.expr {
282-
intravisit::walk_expr(&mut walker, expr);
283-
walker.uses.iter().any(|x| !block_defs.contains(x))
306+
intravisit::walk_expr(&mut end_walker, expr);
307+
end_walker.uses.iter().any(|x| !block_defs.contains(x))
284308
} else if end_eq == 0 {
285309
false
286310
} else {
287311
true
288312
};
289313

290-
emit_shared_code_in_if_blocks_lint(cx, start_eq, end_eq, end_linable, blocks, expr);
314+
if end_linable {
315+
end_walker.def_symbols.drain().for_each(|x| {
316+
moved_syms.insert(x);
317+
});
318+
}
319+
320+
emit_shared_code_in_if_blocks_lint(
321+
cx,
322+
start_eq,
323+
end_eq,
324+
end_linable,
325+
check_for_warn_of_moved_symbol(cx, &moved_syms, expr),
326+
blocks,
327+
expr,
328+
);
329+
}
330+
}
331+
332+
fn check_for_warn_of_moved_symbol(
333+
cx: &LateContext<'tcx>,
334+
symbols: &FxHashSet<Symbol>,
335+
if_expr: &'tcx Expr<'_>,
336+
) -> bool {
337+
// Obs true as we include the current if block
338+
if let Some(block) = get_enclosing_block(cx, if_expr.hir_id) {
339+
let ignore_span = block.span.shrink_to_lo().to(if_expr.span);
340+
341+
symbols
342+
.iter()
343+
.filter(|sym| !sym.as_str().starts_with('_'))
344+
.any(move |sym| {
345+
let mut walker = ContainsName {
346+
name: *sym,
347+
result: false,
348+
};
349+
350+
// Scan block
351+
block
352+
.stmts
353+
.iter()
354+
.filter(|stmt| !ignore_span.overlaps(stmt.span))
355+
.for_each(|stmt| intravisit::walk_stmt(&mut walker, stmt));
356+
357+
if let Some(expr) = block.expr {
358+
intravisit::walk_expr(&mut walker, expr);
359+
}
360+
361+
walker.result
362+
})
363+
} else {
364+
false
291365
}
292366
}
293367

@@ -296,6 +370,7 @@ fn emit_shared_code_in_if_blocks_lint(
296370
start_stmts: usize,
297371
end_stmts: usize,
298372
lint_end: bool,
373+
warn_about_moved_symbol: bool,
299374
blocks: &[&Block<'tcx>],
300375
if_expr: &'tcx Expr<'_>,
301376
) {
@@ -305,7 +380,9 @@ fn emit_shared_code_in_if_blocks_lint(
305380

306381
// (help, span, suggestion)
307382
let mut suggestions: Vec<(&str, Span, String)> = vec![];
383+
let mut add_expr_note = false;
308384

385+
// Construct suggestions
309386
if start_stmts > 0 {
310387
let block = blocks[0];
311388
let span_start = first_line_of_span(cx, if_expr.span).shrink_to_lo();
@@ -357,21 +434,29 @@ fn emit_shared_code_in_if_blocks_lint(
357434
}
358435

359436
suggestions.push(("end", span, suggestion.to_string()));
437+
add_expr_note = !cx.typeck_results().expr_ty(if_expr).is_unit()
360438
}
361439

440+
let add_optional_msgs = |diag: &mut DiagnosticBuilder<'_>| {
441+
if add_expr_note {
442+
diag.note("The end suggestion probably needs some adjustments to use the expression result correctly.");
443+
}
444+
445+
if warn_about_moved_symbol {
446+
diag.warn("Some moved values might need to be renamed to avoid wrong references.");
447+
}
448+
};
449+
450+
// Emit lint
362451
if suggestions.len() == 1 {
363452
let (place_str, span, sugg) = suggestions.pop().unwrap();
364453
let msg = format!("All if blocks contain the same code at the {}", place_str);
365454
let help = format!("Consider moving the {} statements out like this", place_str);
366-
span_lint_and_sugg(
367-
cx,
368-
SHARED_CODE_IN_IF_BLOCKS,
369-
span,
370-
msg.as_str(),
371-
help.as_str(),
372-
sugg,
373-
Applicability::Unspecified,
374-
);
455+
span_lint_and_then(cx, SHARED_CODE_IN_IF_BLOCKS, span, msg.as_str(), |diag| {
456+
diag.span_suggestion(span, help.as_str(), sugg, Applicability::Unspecified);
457+
458+
add_optional_msgs(diag);
459+
});
375460
} else if suggestions.len() == 2 {
376461
let (_, end_span, end_sugg) = suggestions.pop().unwrap();
377462
let (_, start_span, start_sugg) = suggestions.pop().unwrap();
@@ -396,28 +481,39 @@ fn emit_shared_code_in_if_blocks_lint(
396481
end_sugg,
397482
Applicability::Unspecified,
398483
);
484+
485+
add_optional_msgs(diag);
399486
},
400487
);
401488
}
402489
}
403490

404-
pub struct SymbolFinderVisitor<'a, 'tcx> {
491+
/// This visitor collects HirIds and Symbols of defined symbols and HirIds of used values.
492+
struct UsedValueFinderVisitor<'a, 'tcx> {
405493
cx: &'a LateContext<'tcx>,
494+
495+
/// The `HirId`s of defined values in the scanned statements
406496
defs: FxHashSet<HirId>,
497+
498+
/// The Symbols of the defined symbols in the scanned statements
499+
def_symbols: FxHashSet<Symbol>,
500+
501+
/// The `HirId`s of the used values
407502
uses: FxHashSet<HirId>,
408503
}
409504

410-
impl<'a, 'tcx> SymbolFinderVisitor<'a, 'tcx> {
505+
impl<'a, 'tcx> UsedValueFinderVisitor<'a, 'tcx> {
411506
fn new(cx: &'a LateContext<'tcx>) -> Self {
412-
SymbolFinderVisitor {
507+
UsedValueFinderVisitor {
413508
cx,
414509
defs: FxHashSet::default(),
510+
def_symbols: FxHashSet::default(),
415511
uses: FxHashSet::default(),
416512
}
417513
}
418514
}
419515

420-
impl<'a, 'tcx> Visitor<'tcx> for SymbolFinderVisitor<'a, 'tcx> {
516+
impl<'a, 'tcx> Visitor<'tcx> for UsedValueFinderVisitor<'a, 'tcx> {
421517
type Map = Map<'tcx>;
422518

423519
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
@@ -427,6 +523,11 @@ impl<'a, 'tcx> Visitor<'tcx> for SymbolFinderVisitor<'a, 'tcx> {
427523
fn visit_local(&mut self, l: &'tcx rustc_hir::Local<'tcx>) {
428524
let local_id = l.pat.hir_id;
429525
self.defs.insert(local_id);
526+
527+
if let Some(sym) = l.pat.simple_ident() {
528+
self.def_symbols.insert(sym.name);
529+
}
530+
430531
if let Some(expr) = l.init {
431532
intravisit::walk_expr(self, expr);
432533
}

clippy_lints/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1366,7 +1366,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
13661366
LintId::of(&casts::PTR_AS_PTR),
13671367
LintId::of(&checked_conversions::CHECKED_CONVERSIONS),
13681368
LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION),
1369-
LintId::of(&copies::SHARED_CODE_IN_IF_BLOCKS),
13701369
LintId::of(&copy_iterator::COPY_ITERATOR),
13711370
LintId::of(&default::DEFAULT_TRAIT_ACCESS),
13721371
LintId::of(&dereference::EXPLICIT_DEREF_METHODS),
@@ -2064,6 +2063,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
20642063
store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
20652064
LintId::of(&attrs::EMPTY_LINE_AFTER_OUTER_ATTR),
20662065
LintId::of(&cognitive_complexity::COGNITIVE_COMPLEXITY),
2066+
LintId::of(&copies::SHARED_CODE_IN_IF_BLOCKS),
20672067
LintId::of(&disallowed_method::DISALLOWED_METHOD),
20682068
LintId::of(&fallible_impl_from::FALLIBLE_IMPL_FROM),
20692069
LintId::of(&floating_point_arithmetic::IMPRECISE_FLOPS),

clippy_lints/src/literal_representation.rs

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -464,28 +464,32 @@ impl DecimalLiteralRepresentation {
464464
{
465465
return Err(WarningType::DecimalRepresentation);
466466
}
467+
} else if digits.len() < 4 {
468+
// Lint for Literals with a hex-representation of 2 or 3 digits
469+
let f = &digits[0..1]; // first digit
470+
let s = &digits[1..]; // suffix
471+
472+
// Powers of 2
473+
if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && s.chars().all(|c| c == '0'))
474+
// Powers of 2 minus 1
475+
|| ((f.eq("1") || f.eq("3") || f.eq("7") || f.eq("F")) && s.chars().all(|c| c == 'F'))
476+
{
477+
return Err(WarningType::DecimalRepresentation);
478+
}
467479
} else {
480+
// Lint for Literals with a hex-representation of 4 digits or more
468481
let f = &digits[0..1]; // first digit
469482
let m = &digits[1..digits.len() - 1]; // middle digits, except last
470483
let s = &digits[1..]; // suffix
471-
if digits.len() < 4 {
472-
// Powers of 2
473-
if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && s.chars().all(|c| c == '0'))
474-
// Powers of 2 minus 1
475-
|| ((f.eq("1") || f.eq("3") || f.eq("7") || f.eq("F")) && s.chars().all(|c| c == 'F'))
476-
{
477-
return Err(WarningType::DecimalRepresentation);
478-
}
479-
} else {
480-
// Powers of 2 with a margin of +15/-16
481-
if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && m.chars().all(|c| c == '0'))
482-
|| ((f.eq("1") || f.eq("3") || f.eq("7") || f.eq("F")) && m.chars().all(|c| c == 'F'))
483-
// Lint for representations with only 0s and Fs, while allowing 7 as the first
484-
// digit
485-
|| ((f.eq("7") || f.eq("F")) && s.chars().all(|c| c == '0' || c == 'F'))
486-
{
487-
return Err(WarningType::DecimalRepresentation);
488-
}
484+
485+
// Powers of 2 with a margin of +15/-16
486+
if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && m.chars().all(|c| c == '0'))
487+
|| ((f.eq("1") || f.eq("3") || f.eq("7") || f.eq("F")) && m.chars().all(|c| c == 'F'))
488+
// Lint for representations with only 0s and Fs, while allowing 7 as the first
489+
// digit
490+
|| ((f.eq("7") || f.eq("F")) && s.chars().all(|c| c == '0' || c == 'F'))
491+
{
492+
return Err(WarningType::DecimalRepresentation);
489493
}
490494
}
491495

0 commit comments

Comments
 (0)