Skip to content

Commit de56279

Browse files
committed
Implement building the manual_memcpy sugggestion with loop counters
1 parent 720f19f commit de56279

File tree

2 files changed

+213
-50
lines changed

2 files changed

+213
-50
lines changed

clippy_lints/src/loops.rs

Lines changed: 140 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -800,27 +800,108 @@ enum OffsetSign {
800800
}
801801

802802
struct Offset {
803-
value: String,
803+
value: MinifyingSugg<'static>,
804804
sign: OffsetSign,
805805
}
806806

807807
impl Offset {
808-
fn negative(value: String) -> Self {
808+
fn negative(value: MinifyingSugg<'static>) -> Self {
809809
Self {
810810
value,
811811
sign: OffsetSign::Negative,
812812
}
813813
}
814814

815-
fn positive(value: String) -> Self {
815+
fn positive(value: MinifyingSugg<'static>) -> Self {
816816
Self {
817817
value,
818818
sign: OffsetSign::Positive,
819819
}
820820
}
821821

822822
fn empty() -> Self {
823-
Self::positive("0".into())
823+
Self::positive(MinifyingSugg::non_paren("0"))
824+
}
825+
}
826+
827+
fn apply_offset(lhs: &MinifyingSugg<'static>, rhs: &Offset) -> MinifyingSugg<'static> {
828+
match rhs.sign {
829+
OffsetSign::Positive => lhs + &rhs.value,
830+
OffsetSign::Negative => lhs - &rhs.value,
831+
}
832+
}
833+
834+
#[derive(Clone)]
835+
struct MinifyingSugg<'a>(sugg::Sugg<'a>);
836+
837+
impl std::fmt::Display for MinifyingSugg<'_> {
838+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
839+
std::fmt::Display::fmt(&self.0, f)
840+
}
841+
}
842+
843+
impl<'a> MinifyingSugg<'a> {
844+
fn as_str(&self) -> &str {
845+
let sugg::Sugg::NonParen(s) | sugg::Sugg::MaybeParen(s) | sugg::Sugg::BinOp(_, s) = &self.0;
846+
s.as_ref()
847+
}
848+
849+
fn hir(cx: &LateContext<'_, '_>, expr: &Expr<'_>, default: &'a str) -> Self {
850+
Self(sugg::Sugg::hir(cx, expr, default))
851+
}
852+
853+
fn maybe_par(self) -> Self {
854+
Self(self.0.maybe_par())
855+
}
856+
857+
fn non_paren(str: impl Into<std::borrow::Cow<'a, str>>) -> Self {
858+
Self(sugg::Sugg::NonParen(str.into()))
859+
}
860+
}
861+
862+
impl std::ops::Add for &MinifyingSugg<'static> {
863+
type Output = MinifyingSugg<'static>;
864+
fn add(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> {
865+
match (self.as_str(), rhs.as_str()) {
866+
("0", _) => rhs.clone(),
867+
(_, "0") => self.clone(),
868+
(_, _) => MinifyingSugg(&self.0 + &rhs.0),
869+
}
870+
}
871+
}
872+
873+
impl std::ops::Sub for &MinifyingSugg<'static> {
874+
type Output = MinifyingSugg<'static>;
875+
fn sub(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> {
876+
match (self.as_str(), rhs.as_str()) {
877+
(_, "0") => self.clone(),
878+
("0", _) => MinifyingSugg(sugg::make_unop("-", rhs.0.clone())),
879+
(x, y) if x == y => MinifyingSugg::non_paren("0"),
880+
(_, _) => MinifyingSugg(&self.0 - &rhs.0),
881+
}
882+
}
883+
}
884+
885+
impl std::ops::Add<&MinifyingSugg<'static>> for MinifyingSugg<'static> {
886+
type Output = MinifyingSugg<'static>;
887+
fn add(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> {
888+
match (self.as_str(), rhs.as_str()) {
889+
("0", _) => rhs.clone(),
890+
(_, "0") => self,
891+
(_, _) => MinifyingSugg(self.0 + &rhs.0),
892+
}
893+
}
894+
}
895+
896+
impl std::ops::Sub<&MinifyingSugg<'static>> for MinifyingSugg<'static> {
897+
type Output = MinifyingSugg<'static>;
898+
fn sub(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> {
899+
match (self.as_str(), rhs.as_str()) {
900+
(_, "0") => self,
901+
("0", _) => MinifyingSugg(sugg::make_unop("-", rhs.0.clone())),
902+
(x, y) if x == y => MinifyingSugg::non_paren("0"),
903+
(_, _) => MinifyingSugg(self.0 - &rhs.0),
904+
}
824905
}
825906
}
826907

@@ -878,14 +959,15 @@ fn get_offset<'tcx>(
878959
cx: &LateContext<'tcx>,
879960
e: &Expr<'_>,
880961
starts: &[Start<'tcx>],
881-
) -> Option<String> {
962+
) -> Option<MinifyingSugg<'static>> {
882963
match &e.kind {
883964
ExprKind::Lit(l) => match l.node {
884-
ast::LitKind::Int(x, _ty) => Some(x.to_string()),
965+
ast::LitKind::Int(x, _ty) => Some(MinifyingSugg::non_paren(x.to_string())),
885966
_ => None,
886967
},
887968
ExprKind::Path(..) if extract_start(cx, e, starts).is_none() => {
888-
Some(snippet_opt(cx, e.span).unwrap_or_else(|| "??".into()))
969+
// `e` is always non paren as it's a `Path`
970+
Some(MinifyingSugg::non_paren(snippet(cx, e.span, "???")))
889971
},
890972
_ => None,
891973
}
@@ -979,75 +1061,88 @@ fn build_manual_memcpy_suggestion<'tcx>(
9791061
dst: IndexExpr<'_>,
9801062
src: IndexExpr<'_>,
9811063
) -> String {
982-
fn print_sum(arg1: &str, arg2: &Offset) -> String {
983-
match (arg1, &arg2.value[..], arg2.sign) {
984-
("0", "0", _) => "0".into(),
985-
("0", x, OffsetSign::Positive) | (x, "0", _) => x.into(),
986-
("0", x, OffsetSign::Negative) => format!("-{}", x),
987-
(x, y, OffsetSign::Positive) => format!("({} + {})", x, y),
988-
(x, y, OffsetSign::Negative) => {
989-
if x == y {
990-
"0".into()
991-
} else {
992-
format!("({} - {})", x, y)
993-
}
994-
},
995-
}
996-
}
997-
998-
fn print_offset(start_str: &str, inline_offset: &Offset) -> String {
999-
let offset = print_sum(start_str, inline_offset);
1064+
fn print_offset(offset: MinifyingSugg<'static>) -> MinifyingSugg<'static> {
10001065
if offset.as_str() == "0" {
1001-
"".into()
1066+
MinifyingSugg::non_paren("")
10021067
} else {
10031068
offset
10041069
}
10051070
}
10061071

1007-
let print_limit = |end: &Expr<'_>, offset: Offset, base: &Expr<'_>| {
1072+
let print_limit = |end: &Expr<'_>, end_str: &str, base: &Expr<'_>, sugg: MinifyingSugg<'static>| -> MinifyingSugg<'static> {
10081073
if_chain! {
10091074
if let ExprKind::MethodCall(method, _, len_args, _) = end.kind;
10101075
if method.ident.name == sym!(len);
10111076
if len_args.len() == 1;
10121077
if let Some(arg) = len_args.get(0);
10131078
if var_def_id(cx, arg) == var_def_id(cx, base);
10141079
then {
1015-
match offset.sign {
1016-
OffsetSign::Negative => format!("({} - {})", snippet(cx, end.span, "<src>.len()"), offset.value),
1017-
OffsetSign::Positive => "".into(),
1080+
if sugg.as_str() == end_str {
1081+
MinifyingSugg::non_paren("")
1082+
} else {
1083+
sugg
10181084
}
10191085
} else {
1020-
let end_str = match limits {
1086+
match limits {
10211087
ast::RangeLimits::Closed => {
1022-
let end = sugg::Sugg::hir(cx, end, "<count>");
1023-
format!("{}", end + sugg::ONE)
1088+
sugg + &MinifyingSugg::non_paren("1")
10241089
},
1025-
ast::RangeLimits::HalfOpen => format!("{}", snippet(cx, end.span, "..")),
1026-
};
1027-
1028-
print_sum(&end_str, &offset)
1090+
ast::RangeLimits::HalfOpen => sugg,
1091+
}
10291092
}
10301093
}
10311094
};
10321095

1033-
let start_str = snippet(cx, start.span, "").to_string();
1034-
let dst_offset = print_offset(&start_str, &dst.idx_offset);
1035-
let dst_limit = print_limit(end, dst.idx_offset, dst.base);
1036-
let src_offset = print_offset(&start_str, &src.idx_offset);
1037-
let src_limit = print_limit(end, src.idx_offset, src.base);
1096+
let start_str = MinifyingSugg::hir(cx, start, "");
1097+
let end_str = MinifyingSugg::hir(cx, end, "");
1098+
1099+
let print_offset_and_limit = |idx_expr: &IndexExpr<'_>| match idx_expr.idx {
1100+
StartKind::Range => (
1101+
print_offset(apply_offset(&start_str, &idx_expr.idx_offset)),
1102+
print_limit(
1103+
end,
1104+
end_str.as_str(),
1105+
idx_expr.base,
1106+
apply_offset(&end_str, &idx_expr.idx_offset),
1107+
),
1108+
),
1109+
StartKind::Counter { initializer } => {
1110+
let counter_start = MinifyingSugg::hir(cx, initializer, "");
1111+
(
1112+
print_offset(apply_offset(&counter_start, &idx_expr.idx_offset)),
1113+
print_limit(
1114+
end,
1115+
end_str.as_str(),
1116+
idx_expr.base,
1117+
apply_offset(&end_str, &idx_expr.idx_offset) + &counter_start - &start_str,
1118+
),
1119+
)
1120+
},
1121+
};
1122+
1123+
let (dst_offset, dst_limit) = print_offset_and_limit(&dst);
1124+
let (src_offset, src_limit) = print_offset_and_limit(&src);
10381125

10391126
let dst_base_str = snippet_opt(cx, dst.base.span).unwrap_or_else(|| "???".into());
10401127
let src_base_str = snippet_opt(cx, src.base.span).unwrap_or_else(|| "???".into());
10411128

1042-
let dst = if dst_offset == "" && dst_limit == "" {
1129+
let dst = if dst_offset.as_str() == "" && dst_limit.as_str() == "" {
10431130
dst_base_str
10441131
} else {
1045-
format!("{}[{}..{}]", dst_base_str, dst_offset, dst_limit)
1132+
format!(
1133+
"{}[{}..{}]",
1134+
dst_base_str,
1135+
dst_offset.maybe_par(),
1136+
dst_limit.maybe_par()
1137+
)
10461138
};
10471139

10481140
format!(
10491141
"{}.clone_from_slice(&{}[{}..{}])",
1050-
dst, src_base_str, src_offset, src_limit
1142+
dst,
1143+
src_base_str,
1144+
src_offset.maybe_par(),
1145+
src_limit.maybe_par()
10511146
)
10521147
}
10531148

clippy_lints/src/utils/sugg.rs

Lines changed: 73 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,46 @@ impl Display for Sugg<'_> {
3636
}
3737
}
3838

39+
// It's impossible to derive Clone due to the lack of the impl Clone for AssocOp
40+
impl Clone for Sugg<'_> {
41+
fn clone(&self) -> Self {
42+
/// manually cloning AssocOp
43+
fn clone_assoc_op(this: &AssocOp) -> AssocOp {
44+
match this {
45+
AssocOp::Add => AssocOp::Add,
46+
AssocOp::Subtract => AssocOp::Subtract,
47+
AssocOp::Multiply => AssocOp::Multiply,
48+
AssocOp::Divide => AssocOp::Divide,
49+
AssocOp::Modulus => AssocOp::Modulus,
50+
AssocOp::LAnd => AssocOp::LAnd,
51+
AssocOp::LOr => AssocOp::LOr,
52+
AssocOp::BitXor => AssocOp::BitXor,
53+
AssocOp::BitAnd => AssocOp::BitAnd,
54+
AssocOp::BitOr => AssocOp::BitOr,
55+
AssocOp::ShiftLeft => AssocOp::ShiftLeft,
56+
AssocOp::ShiftRight => AssocOp::ShiftRight,
57+
AssocOp::Equal => AssocOp::Equal,
58+
AssocOp::Less => AssocOp::Less,
59+
AssocOp::LessEqual => AssocOp::LessEqual,
60+
AssocOp::NotEqual => AssocOp::NotEqual,
61+
AssocOp::Greater => AssocOp::Greater,
62+
AssocOp::GreaterEqual => AssocOp::GreaterEqual,
63+
AssocOp::Assign => AssocOp::Assign,
64+
AssocOp::AssignOp(t) => AssocOp::AssignOp(*t),
65+
AssocOp::As => AssocOp::As,
66+
AssocOp::DotDot => AssocOp::DotDot,
67+
AssocOp::DotDotEq => AssocOp::DotDotEq,
68+
AssocOp::Colon => AssocOp::Colon,
69+
}
70+
}
71+
match self {
72+
Sugg::NonParen(x) => Sugg::NonParen(x.clone()),
73+
Sugg::MaybeParen(x) => Sugg::MaybeParen(x.clone()),
74+
Sugg::BinOp(op, x) => Sugg::BinOp(clone_assoc_op(op), x.clone()),
75+
}
76+
}
77+
}
78+
3979
#[allow(clippy::wrong_self_convention)] // ok, because of the function `as_ty` method
4080
impl<'a> Sugg<'a> {
4181
/// Prepare a suggestion from an expression.
@@ -267,21 +307,49 @@ impl<'a> Sugg<'a> {
267307
}
268308
}
269309

270-
impl<'a, 'b> std::ops::Add<Sugg<'b>> for Sugg<'a> {
310+
impl std::ops::Add for Sugg<'_> {
271311
type Output = Sugg<'static>;
272-
fn add(self, rhs: Sugg<'b>) -> Sugg<'static> {
312+
fn add(self, rhs: Sugg<'_>) -> Sugg<'static> {
273313
make_binop(ast::BinOpKind::Add, &self, &rhs)
274314
}
275315
}
276316

277-
impl<'a, 'b> std::ops::Sub<Sugg<'b>> for Sugg<'a> {
317+
impl std::ops::Sub for Sugg<'_> {
278318
type Output = Sugg<'static>;
279-
fn sub(self, rhs: Sugg<'b>) -> Sugg<'static> {
319+
fn sub(self, rhs: Sugg<'_>) -> Sugg<'static> {
280320
make_binop(ast::BinOpKind::Sub, &self, &rhs)
281321
}
282322
}
283323

284-
impl<'a> std::ops::Not for Sugg<'a> {
324+
impl std::ops::Add<&Sugg<'_>> for Sugg<'_> {
325+
type Output = Sugg<'static>;
326+
fn add(self, rhs: &Sugg<'_>) -> Sugg<'static> {
327+
make_binop(ast::BinOpKind::Add, &self, rhs)
328+
}
329+
}
330+
331+
impl std::ops::Sub<&Sugg<'_>> for Sugg<'_> {
332+
type Output = Sugg<'static>;
333+
fn sub(self, rhs: &Sugg<'_>) -> Sugg<'static> {
334+
make_binop(ast::BinOpKind::Sub, &self, rhs)
335+
}
336+
}
337+
338+
impl std::ops::Add for &Sugg<'_> {
339+
type Output = Sugg<'static>;
340+
fn add(self, rhs: &Sugg<'_>) -> Sugg<'static> {
341+
make_binop(ast::BinOpKind::Add, self, rhs)
342+
}
343+
}
344+
345+
impl std::ops::Sub for &Sugg<'_> {
346+
type Output = Sugg<'static>;
347+
fn sub(self, rhs: &Sugg<'_>) -> Sugg<'static> {
348+
make_binop(ast::BinOpKind::Sub, self, rhs)
349+
}
350+
}
351+
352+
impl std::ops::Not for Sugg<'_> {
285353
type Output = Sugg<'static>;
286354
fn not(self) -> Sugg<'static> {
287355
make_unop("!", self)

0 commit comments

Comments
 (0)