|
1 | 1 | use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then};
|
2 | 2 | use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt};
|
3 |
| -use clippy_utils::ty::needs_ordered_drop; |
| 3 | +use clippy_utils::ty::{is_interior_mut_ty, needs_ordered_drop}; |
4 | 4 | use clippy_utils::visitors::for_each_expr;
|
5 | 5 | use clippy_utils::{
|
6 |
| - capture_local_usage, eq_expr_value, get_enclosing_block, hash_expr, hash_stmt, if_sequence, is_else_clause, |
7 |
| - is_lint_allowed, path_to_local, search_same, ContainsName, HirEqInterExpr, SpanlessEq, |
| 6 | + capture_local_usage, def_path_def_ids, eq_expr_value, find_binding_init, get_enclosing_block, hash_expr, hash_stmt, |
| 7 | + if_sequence, is_else_clause, is_lint_allowed, path_to_local, search_same, ContainsName, HirEqInterExpr, SpanlessEq, |
8 | 8 | };
|
9 | 9 | use core::iter;
|
10 | 10 | use core::ops::ControlFlow;
|
11 | 11 | use rustc_errors::Applicability;
|
| 12 | +use rustc_hir::def_id::DefIdSet; |
12 | 13 | use rustc_hir::intravisit;
|
13 | 14 | use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, Stmt, StmtKind};
|
14 | 15 | use rustc_lint::{LateContext, LateLintPass};
|
15 |
| -use rustc_session::{declare_lint_pass, declare_tool_lint}; |
| 16 | +use rustc_middle::query::Key; |
| 17 | +use rustc_session::{declare_tool_lint, impl_lint_pass}; |
16 | 18 | use rustc_span::hygiene::walk_chain;
|
17 | 19 | use rustc_span::source_map::SourceMap;
|
18 | 20 | use rustc_span::{BytePos, Span, Symbol};
|
@@ -159,18 +161,40 @@ declare_clippy_lint! {
|
159 | 161 | "`if` statement with shared code in all blocks"
|
160 | 162 | }
|
161 | 163 |
|
162 |
| -declare_lint_pass!(CopyAndPaste => [ |
| 164 | +pub struct CopyAndPaste { |
| 165 | + ignore_interior_mutability: Vec<String>, |
| 166 | + ignored_ty_ids: DefIdSet, |
| 167 | +} |
| 168 | + |
| 169 | +impl CopyAndPaste { |
| 170 | + pub fn new(ignore_interior_mutability: Vec<String>) -> Self { |
| 171 | + Self { |
| 172 | + ignore_interior_mutability, |
| 173 | + ignored_ty_ids: DefIdSet::new(), |
| 174 | + } |
| 175 | + } |
| 176 | +} |
| 177 | + |
| 178 | +impl_lint_pass!(CopyAndPaste => [ |
163 | 179 | IFS_SAME_COND,
|
164 | 180 | SAME_FUNCTIONS_IN_IF_CONDITION,
|
165 | 181 | IF_SAME_THEN_ELSE,
|
166 | 182 | BRANCHES_SHARING_CODE
|
167 | 183 | ]);
|
168 | 184 |
|
169 | 185 | impl<'tcx> LateLintPass<'tcx> for CopyAndPaste {
|
| 186 | + fn check_crate(&mut self, cx: &LateContext<'tcx>) { |
| 187 | + for ignored_ty in &self.ignore_interior_mutability { |
| 188 | + let path: Vec<&str> = ignored_ty.split("::").collect(); |
| 189 | + for id in def_path_def_ids(cx, path.as_slice()) { |
| 190 | + self.ignored_ty_ids.insert(id); |
| 191 | + } |
| 192 | + } |
| 193 | + } |
170 | 194 | fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
171 | 195 | if !expr.span.from_expansion() && matches!(expr.kind, ExprKind::If(..)) && !is_else_clause(cx.tcx, expr) {
|
172 | 196 | let (conds, blocks) = if_sequence(expr);
|
173 |
| - lint_same_cond(cx, &conds); |
| 197 | + lint_same_cond(cx, &conds, &self.ignored_ty_ids); |
174 | 198 | lint_same_fns_in_if_cond(cx, &conds);
|
175 | 199 | let all_same =
|
176 | 200 | !is_lint_allowed(cx, IF_SAME_THEN_ELSE, expr.hir_id) && lint_if_same_then_else(cx, &conds, &blocks);
|
@@ -547,9 +571,39 @@ fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &[(HirId, Symbo
|
547 | 571 | })
|
548 | 572 | }
|
549 | 573 |
|
| 574 | +fn method_caller_is_mutable(cx: &LateContext<'_>, caller_expr: &Expr<'_>, ignored_ty_ids: &DefIdSet) -> bool { |
| 575 | + let caller_ty = cx.typeck_results().expr_ty(caller_expr); |
| 576 | + // Check if given type has inner mutability and was not set to ignored by the configuration |
| 577 | + let is_inner_mut_ty = is_interior_mut_ty(cx, caller_ty) |
| 578 | + && !matches!(caller_ty.ty_adt_id(), Some(adt_id) if ignored_ty_ids.contains(&adt_id)); |
| 579 | + |
| 580 | + is_inner_mut_ty |
| 581 | + || caller_ty.is_mutable_ptr() |
| 582 | + // `find_binding_init` will return the binding iff its not mutable |
| 583 | + || path_to_local(caller_expr) |
| 584 | + .and_then(|hid| find_binding_init(cx, hid)) |
| 585 | + .is_none() |
| 586 | +} |
| 587 | + |
550 | 588 | /// Implementation of `IFS_SAME_COND`.
|
551 |
| -fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { |
552 |
| - for (i, j) in search_same(conds, |e| hash_expr(cx, e), |lhs, rhs| eq_expr_value(cx, lhs, rhs)) { |
| 589 | +fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>], ignored_ty_ids: &DefIdSet) { |
| 590 | + for (i, j) in search_same( |
| 591 | + conds, |
| 592 | + |e| hash_expr(cx, e), |
| 593 | + |lhs, rhs| { |
| 594 | + // Ignore eq_expr side effects iff one of the expressin kind is a method call |
| 595 | + // and the caller is not a mutable, including inner mutable type. |
| 596 | + if let ExprKind::MethodCall(_, caller, _, _) = lhs.kind { |
| 597 | + if method_caller_is_mutable(cx, caller, ignored_ty_ids) { |
| 598 | + false |
| 599 | + } else { |
| 600 | + SpanlessEq::new(cx).eq_expr(lhs, rhs) |
| 601 | + } |
| 602 | + } else { |
| 603 | + eq_expr_value(cx, lhs, rhs) |
| 604 | + } |
| 605 | + }, |
| 606 | + ) { |
553 | 607 | span_lint_and_note(
|
554 | 608 | cx,
|
555 | 609 | IFS_SAME_COND,
|
|
0 commit comments