Skip to content

Commit

Permalink
Rollup merge of rust-lang#5756 - ebroto:5754_sort_by, r=flip1995
Browse files Browse the repository at this point in the history
unnecessary_sort_by: avoid linting if key borrows

changelog: Avoid linting if key borrows in [`unnecessary_sort_by`]

Fixes rust-lang#5754
Closes rust-lang#2313
  • Loading branch information
flip1995 authored Jul 13, 2020
2 parents 019e281 + db1c946 commit 32ef448
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 45 deletions.
21 changes: 2 additions & 19 deletions clippy_lints/src/let_and_return.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
use rustc_hir::{Block, Expr, ExprKind, PatKind, StmtKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
Expand All @@ -9,7 +8,7 @@ use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::subst::GenericArgKind;
use rustc_session::{declare_lint_pass, declare_tool_lint};

use crate::utils::{in_macro, match_qpath, snippet_opt, span_lint_and_then};
use crate::utils::{fn_def_id, in_macro, match_qpath, snippet_opt, span_lint_and_then};

declare_clippy_lint! {
/// **What it does:** Checks for `let`-bindings, which are subsequently
Expand Down Expand Up @@ -97,22 +96,6 @@ struct BorrowVisitor<'a, 'tcx> {
borrows: bool,
}

impl BorrowVisitor<'_, '_> {
fn fn_def_id(&self, expr: &Expr<'_>) -> Option<DefId> {
match &expr.kind {
ExprKind::MethodCall(..) => self.cx.tables().type_dependent_def_id(expr.hir_id),
ExprKind::Call(
Expr {
kind: ExprKind::Path(qpath),
..
},
..,
) => self.cx.qpath_res(qpath, expr.hir_id).opt_def_id(),
_ => None,
}
}
}

impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> {
type Map = Map<'tcx>;

Expand All @@ -121,7 +104,7 @@ impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> {
return;
}

if let Some(def_id) = self.fn_def_id(expr) {
if let Some(def_id) = fn_def_id(self.cx, expr) {
self.borrows = self
.cx
.tcx
Expand Down
48 changes: 30 additions & 18 deletions clippy_lints/src/unnecessary_sort_by.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,23 @@ use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, subst::GenericArgKind};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::Ident;

declare_clippy_lint! {
/// **What it does:**
/// Detects when people use `Vec::sort_by` and pass in a function
/// Detects uses of `Vec::sort_by` passing in a closure
/// which compares the two arguments, either directly or indirectly.
///
/// **Why is this bad?**
/// It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if
/// possible) than to use `Vec::sort_by` and and a more complicated
/// possible) than to use `Vec::sort_by` and a more complicated
/// closure.
///
/// **Known problems:**
/// If the suggested `Vec::sort_by_key` uses Reverse and it isn't
/// imported by a use statement in the current frame, then a `use`
/// statement that imports it will need to be added (which this lint
/// can't do).
/// If the suggested `Vec::sort_by_key` uses Reverse and it isn't already
/// imported by a use statement, then it will need to be added manually.
///
/// **Example:**
///
Expand Down Expand Up @@ -201,28 +200,41 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
};
let vec_name = Sugg::hir(cx, &args[0], "..").to_string();
let unstable = name == "sort_unstable_by";

if_chain! {
if let ExprKind::Path(QPath::Resolved(_, Path {
segments: [PathSegment { ident: left_name, .. }], ..
})) = &left_expr.kind;
if left_name == left_ident;
then {
Some(LintTrigger::Sort(SortDetection { vec_name, unstable }))
}
else {
Some(LintTrigger::SortByKey(SortByKeyDetection {
vec_name,
unstable,
closure_arg,
closure_body,
reverse
}))
return Some(LintTrigger::Sort(SortDetection { vec_name, unstable }))
} else {
if !key_returns_borrow(cx, left_expr) {
return Some(LintTrigger::SortByKey(SortByKeyDetection {
vec_name,
unstable,
closure_arg,
closure_body,
reverse
}))
}
}
}
} else {
None
}
}

None
}

fn key_returns_borrow(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
if let Some(def_id) = utils::fn_def_id(cx, expr) {
let output = cx.tcx.fn_sig(def_id).output();
let ty = output.skip_binder();
return matches!(ty.kind, ty::Ref(..))
|| ty.walk().any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)));
}

false
}

impl LateLintPass<'_> for UnnecessarySortBy {
Expand Down
15 changes: 15 additions & 0 deletions clippy_lints/src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1356,6 +1356,21 @@ pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
)
}

/// Returns the `DefId` of the callee if the given expression is a function or method call.
pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
match &expr.kind {
ExprKind::MethodCall(..) => cx.tables().type_dependent_def_id(expr.hir_id),
ExprKind::Call(
Expr {
kind: ExprKind::Path(qpath),
..
},
..,
) => cx.tables().qpath_res(qpath, expr.hir_id).opt_def_id(),
_ => None,
}
}

pub fn run_lints(cx: &LateContext<'_>, lints: &[&'static Lint], id: HirId) -> bool {
lints.iter().any(|lint| {
matches!(
Expand Down
46 changes: 42 additions & 4 deletions tests/ui/unnecessary_sort_by.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

use std::cmp::Reverse;

fn id(x: isize) -> isize {
x
}
fn unnecessary_sort_by() {
fn id(x: isize) -> isize {
x
}

fn main() {
let mut vec: Vec<isize> = vec![3, 6, 1, 2, 5];
// Forward examples
vec.sort();
Expand All @@ -24,3 +24,41 @@ fn main() {
vec.sort_by(|_, b| b.cmp(c));
vec.sort_unstable_by(|a, _| a.cmp(c));
}

// Should not be linted to avoid hitting https://github.com/rust-lang/rust/issues/34162
mod issue_5754 {
struct Test(String);

#[derive(PartialOrd, Ord, PartialEq, Eq)]
struct Wrapper<'a>(&'a str);

impl Test {
fn name(&self) -> &str {
&self.0
}

fn wrapped(&self) -> Wrapper<'_> {
Wrapper(&self.0)
}
}

pub fn test() {
let mut args: Vec<Test> = vec![];

// Forward
args.sort_by(|a, b| a.name().cmp(b.name()));
args.sort_by(|a, b| a.wrapped().cmp(&b.wrapped()));
args.sort_unstable_by(|a, b| a.name().cmp(b.name()));
args.sort_unstable_by(|a, b| a.wrapped().cmp(&b.wrapped()));
// Reverse
args.sort_by(|a, b| b.name().cmp(a.name()));
args.sort_by(|a, b| b.wrapped().cmp(&a.wrapped()));
args.sort_unstable_by(|a, b| b.name().cmp(a.name()));
args.sort_unstable_by(|a, b| b.wrapped().cmp(&a.wrapped()));
}
}

fn main() {
unnecessary_sort_by();
issue_5754::test();
}
46 changes: 42 additions & 4 deletions tests/ui/unnecessary_sort_by.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

use std::cmp::Reverse;

fn id(x: isize) -> isize {
x
}
fn unnecessary_sort_by() {
fn id(x: isize) -> isize {
x
}

fn main() {
let mut vec: Vec<isize> = vec![3, 6, 1, 2, 5];
// Forward examples
vec.sort_by(|a, b| a.cmp(b));
Expand All @@ -24,3 +24,41 @@ fn main() {
vec.sort_by(|_, b| b.cmp(c));
vec.sort_unstable_by(|a, _| a.cmp(c));
}

// Should not be linted to avoid hitting https://github.com/rust-lang/rust/issues/34162
mod issue_5754 {
struct Test(String);

#[derive(PartialOrd, Ord, PartialEq, Eq)]
struct Wrapper<'a>(&'a str);

impl Test {
fn name(&self) -> &str {
&self.0
}

fn wrapped(&self) -> Wrapper<'_> {
Wrapper(&self.0)
}
}

pub fn test() {
let mut args: Vec<Test> = vec![];

// Forward
args.sort_by(|a, b| a.name().cmp(b.name()));
args.sort_by(|a, b| a.wrapped().cmp(&b.wrapped()));
args.sort_unstable_by(|a, b| a.name().cmp(b.name()));
args.sort_unstable_by(|a, b| a.wrapped().cmp(&b.wrapped()));
// Reverse
args.sort_by(|a, b| b.name().cmp(a.name()));
args.sort_by(|a, b| b.wrapped().cmp(&a.wrapped()));
args.sort_unstable_by(|a, b| b.name().cmp(a.name()));
args.sort_unstable_by(|a, b| b.wrapped().cmp(&a.wrapped()));
}
}

fn main() {
unnecessary_sort_by();
issue_5754::test();
}

0 comments on commit 32ef448

Please sign in to comment.