Skip to content

Commit bc6aee5

Browse files
committed
init partialord
1 parent 1cca1fa commit bc6aee5

File tree

1 file changed

+166
-0
lines changed

1 file changed

+166
-0
lines changed

crates/ide_assists/src/utils/gen_trait_fn_body.rs

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ pub(crate) fn gen_trait_fn_body(
2121
"Default" => gen_default_impl(adt, func),
2222
"Hash" => gen_hash_impl(adt, func),
2323
"PartialEq" => gen_partial_eq(adt, func),
24+
"PartialOrd" => gen_partial_ord(adt, func),
2425
_ => None,
2526
}
2627
}
@@ -572,6 +573,171 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
572573
Some(())
573574
}
574575

576+
fn gen_partial_ord(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
577+
fn gen_eq_chain(expr: Option<ast::Expr>, cmp: ast::Expr) -> Option<ast::Expr> {
578+
match expr {
579+
Some(expr) => Some(make::expr_op(ast::BinOp::BooleanAnd, expr, cmp)),
580+
None => Some(cmp),
581+
}
582+
}
583+
584+
fn gen_record_pat_field(field_name: &str, pat_name: &str) -> ast::RecordPatField {
585+
let pat = make::ext::simple_ident_pat(make::name(&pat_name));
586+
let name_ref = make::name_ref(field_name);
587+
make::record_pat_field(name_ref, pat.into())
588+
}
589+
590+
fn gen_record_pat(record_name: ast::Path, fields: Vec<ast::RecordPatField>) -> ast::RecordPat {
591+
let list = make::record_pat_field_list(fields);
592+
make::record_pat_with_fields(record_name, list)
593+
}
594+
595+
fn gen_variant_path(variant: &ast::Variant) -> Option<ast::Path> {
596+
make::ext::path_from_idents(["Self", &variant.name()?.to_string()])
597+
}
598+
599+
fn gen_tuple_field(field_name: &String) -> ast::Pat {
600+
ast::Pat::IdentPat(make::ident_pat(false, false, make::name(field_name)))
601+
}
602+
603+
// FIXME: return `None` if the trait carries a generic type; we can only
604+
// generate this code `Self` for the time being.
605+
606+
let body = match adt {
607+
// `Hash` cannot be derived for unions, so no default impl can be provided.
608+
ast::Adt::Union(_) => return None,
609+
610+
ast::Adt::Enum(enum_) => {
611+
// => std::mem::discriminant(self) == std::mem::discriminant(other)
612+
let lhs_name = make::expr_path(make::ext::ident_path("self"));
613+
let lhs = make::expr_call(make_discriminant()?, make::arg_list(Some(lhs_name.clone())));
614+
let rhs_name = make::expr_path(make::ext::ident_path("other"));
615+
let rhs = make::expr_call(make_discriminant()?, make::arg_list(Some(rhs_name.clone())));
616+
let eq_check = make::expr_op(ast::BinOp::EqualityTest, lhs, rhs);
617+
618+
let mut case_count = 0;
619+
let mut arms = vec![];
620+
for variant in enum_.variant_list()?.variants() {
621+
case_count += 1;
622+
match variant.field_list() {
623+
// => (Self::Bar { bin: l_bin }, Self::Bar { bin: r_bin }) => l_bin == r_bin,
624+
Some(ast::FieldList::RecordFieldList(list)) => {
625+
let mut expr = None;
626+
let mut l_fields = vec![];
627+
let mut r_fields = vec![];
628+
629+
for field in list.fields() {
630+
let field_name = field.name()?.to_string();
631+
632+
let l_name = &format!("l_{}", field_name);
633+
l_fields.push(gen_record_pat_field(&field_name, &l_name));
634+
635+
let r_name = &format!("r_{}", field_name);
636+
r_fields.push(gen_record_pat_field(&field_name, &r_name));
637+
638+
let lhs = make::expr_path(make::ext::ident_path(l_name));
639+
let rhs = make::expr_path(make::ext::ident_path(r_name));
640+
let cmp = make::expr_op(ast::BinOp::EqualityTest, lhs, rhs);
641+
expr = gen_eq_chain(expr, cmp);
642+
}
643+
644+
let left = gen_record_pat(gen_variant_path(&variant)?, l_fields);
645+
let right = gen_record_pat(gen_variant_path(&variant)?, r_fields);
646+
let tuple = make::tuple_pat(vec![left.into(), right.into()]);
647+
648+
if let Some(expr) = expr {
649+
arms.push(make::match_arm(Some(tuple.into()), None, expr));
650+
}
651+
}
652+
653+
Some(ast::FieldList::TupleFieldList(list)) => {
654+
let mut expr = None;
655+
let mut l_fields = vec![];
656+
let mut r_fields = vec![];
657+
658+
for (i, _) in list.fields().enumerate() {
659+
let field_name = format!("{}", i);
660+
661+
let l_name = format!("l{}", field_name);
662+
l_fields.push(gen_tuple_field(&l_name));
663+
664+
let r_name = format!("r{}", field_name);
665+
r_fields.push(gen_tuple_field(&r_name));
666+
667+
let lhs = make::expr_path(make::ext::ident_path(&l_name));
668+
let rhs = make::expr_path(make::ext::ident_path(&r_name));
669+
let cmp = make::expr_op(ast::BinOp::EqualityTest, lhs, rhs);
670+
expr = gen_eq_chain(expr, cmp);
671+
}
672+
673+
let left = make::tuple_struct_pat(gen_variant_path(&variant)?, l_fields);
674+
let right = make::tuple_struct_pat(gen_variant_path(&variant)?, r_fields);
675+
let tuple = make::tuple_pat(vec![left.into(), right.into()]);
676+
677+
if let Some(expr) = expr {
678+
arms.push(make::match_arm(Some(tuple.into()), None, expr));
679+
}
680+
}
681+
None => continue,
682+
}
683+
}
684+
685+
let expr = match arms.len() {
686+
0 => eq_check,
687+
_ => {
688+
if case_count > arms.len() {
689+
let lhs = make::wildcard_pat().into();
690+
arms.push(make::match_arm(Some(lhs), None, eq_check));
691+
}
692+
693+
let match_target = make::expr_tuple(vec![lhs_name, rhs_name]);
694+
let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1));
695+
make::expr_match(match_target, list)
696+
}
697+
};
698+
699+
make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1))
700+
}
701+
ast::Adt::Struct(strukt) => match strukt.field_list() {
702+
Some(ast::FieldList::RecordFieldList(field_list)) => {
703+
let mut expr = None;
704+
for field in field_list.fields() {
705+
let lhs = make::expr_path(make::ext::ident_path("self"));
706+
let lhs = make::expr_field(lhs, &field.name()?.to_string());
707+
let rhs = make::expr_path(make::ext::ident_path("other"));
708+
let rhs = make::expr_field(rhs, &field.name()?.to_string());
709+
let cmp = make::expr_op(ast::BinOp::EqualityTest, lhs, rhs);
710+
expr = gen_eq_chain(expr, cmp);
711+
}
712+
make::block_expr(None, expr).indent(ast::edit::IndentLevel(1))
713+
}
714+
715+
Some(ast::FieldList::TupleFieldList(field_list)) => {
716+
let mut expr = None;
717+
for (i, _) in field_list.fields().enumerate() {
718+
let idx = format!("{}", i);
719+
let lhs = make::expr_path(make::ext::ident_path("self"));
720+
let lhs = make::expr_field(lhs, &idx);
721+
let rhs = make::expr_path(make::ext::ident_path("other"));
722+
let rhs = make::expr_field(rhs, &idx);
723+
let cmp = make::expr_op(ast::BinOp::EqualityTest, lhs, rhs);
724+
expr = gen_eq_chain(expr, cmp);
725+
}
726+
make::block_expr(None, expr).indent(ast::edit::IndentLevel(1))
727+
}
728+
729+
// No fields in the body means there's nothing to hash.
730+
None => {
731+
let expr = make::expr_literal("true").into();
732+
make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1))
733+
}
734+
},
735+
};
736+
737+
ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
738+
Some(())
739+
}
740+
575741
fn make_discriminant() -> Option<ast::Expr> {
576742
Some(make::expr_path(make::ext::path_from_idents(["core", "mem", "discriminant"])?))
577743
}

0 commit comments

Comments
 (0)