1use lex_ast::{Arm, CExpr, RecordField};
37
38#[derive(Debug, Clone, PartialEq)]
40pub enum BodyMerge {
41 Merged(CExpr),
43 Conflict,
46}
47
48pub fn merge_bodies(base: &CExpr, ours: &CExpr, theirs: &CExpr) -> BodyMerge {
50 match merge3(base, ours, theirs) {
51 Some(e) => BodyMerge::Merged(e),
52 None => BodyMerge::Conflict,
53 }
54}
55
56fn merge3(base: &CExpr, ours: &CExpr, theirs: &CExpr) -> Option<CExpr> {
58 if ours == theirs {
60 return Some(ours.clone());
61 }
62 if base == ours {
63 return Some(theirs.clone());
64 }
65 if base == theirs {
66 return Some(ours.clone());
67 }
68
69 use CExpr::*;
72 match (base, ours, theirs) {
73 (
74 Call { callee: cb, args: ab },
75 Call { callee: co, args: ao },
76 Call { callee: ct, args: at },
77 ) => Some(Call {
78 callee: Box::new(merge3(cb, co, ct)?),
79 args: merge3_seq(ab, ao, at)?,
80 }),
81
82 (
83 Let { name: nb, ty: tb, value: vb, body: bb },
84 Let { name: no, ty: to, value: vo, body: bo },
85 Let { name: nt, ty: tt, value: vt, body: bt },
86 ) => Some(Let {
87 name: pick3(nb, no, nt)?,
88 ty: pick3(tb, to, tt)?,
89 value: Box::new(merge3(vb, vo, vt)?),
90 body: Box::new(merge3(bb, bo, bt)?),
91 }),
92
93 (
94 Match { scrutinee: sb, arms: amb },
95 Match { scrutinee: so, arms: amo },
96 Match { scrutinee: st, arms: amt },
97 ) => Some(Match {
98 scrutinee: Box::new(merge3(sb, so, st)?),
99 arms: merge3_arms(amb, amo, amt)?,
100 }),
101
102 (
103 Block { statements: sb, result: rb },
104 Block { statements: so, result: ro },
105 Block { statements: st, result: rt },
106 ) => Some(Block {
107 statements: merge3_seq(sb, so, st)?,
108 result: Box::new(merge3(rb, ro, rt)?),
109 }),
110
111 (
112 Constructor { name: nb, args: ab },
113 Constructor { name: no, args: ao },
114 Constructor { name: nt, args: at },
115 ) => Some(Constructor {
116 name: pick3(nb, no, nt)?,
117 args: merge3_seq(ab, ao, at)?,
118 }),
119
120 (
121 TupleLit { items: ib },
122 TupleLit { items: io },
123 TupleLit { items: it },
124 ) => Some(TupleLit { items: merge3_seq(ib, io, it)? }),
125
126 (
127 ListLit { items: ib },
128 ListLit { items: io },
129 ListLit { items: it },
130 ) => Some(ListLit { items: merge3_seq(ib, io, it)? }),
131
132 (
133 RecordLit { fields: fb },
134 RecordLit { fields: fo },
135 RecordLit { fields: ft },
136 ) => Some(RecordLit { fields: merge3_fields(fb, fo, ft)? }),
137
138 (
139 FieldAccess { value: vb, field: fb },
140 FieldAccess { value: vo, field: fo },
141 FieldAccess { value: vt, field: ft },
142 ) => Some(FieldAccess {
143 value: Box::new(merge3(vb, vo, vt)?),
144 field: pick3(fb, fo, ft)?,
145 }),
146
147 (
148 BinOp { op: ob, lhs: lb, rhs: rb },
149 BinOp { op: oo, lhs: lo, rhs: ro },
150 BinOp { op: ot, lhs: lt, rhs: rt },
151 ) => Some(BinOp {
152 op: pick3(ob, oo, ot)?,
153 lhs: Box::new(merge3(lb, lo, lt)?),
154 rhs: Box::new(merge3(rb, ro, rt)?),
155 }),
156
157 (
158 UnaryOp { op: ob, expr: eb },
159 UnaryOp { op: oo, expr: eo },
160 UnaryOp { op: ot, expr: et },
161 ) => Some(UnaryOp {
162 op: pick3(ob, oo, ot)?,
163 expr: Box::new(merge3(eb, eo, et)?),
164 }),
165
166 (
167 Return { value: vb },
168 Return { value: vo },
169 Return { value: vt },
170 ) => Some(Return { value: Box::new(merge3(vb, vo, vt)?) }),
171
172 (
173 Lambda { params: pb, return_type: rtb, effects: eb, effect_row_var: rvb, body: bb },
174 Lambda { params: po, return_type: rto, effects: eo, effect_row_var: rvo, body: bo },
175 Lambda { params: pt, return_type: rtt, effects: et, effect_row_var: rvt, body: bt },
176 ) => Some(Lambda {
177 params: pick3(pb, po, pt)?,
178 return_type: pick3(rtb, rto, rtt)?,
179 effects: pick3(eb, eo, et)?,
180 effect_row_var: pick3(rvb, rvo, rvt)?,
181 body: Box::new(merge3(bb, bo, bt)?),
182 }),
183
184 _ => None,
189 }
190}
191
192fn merge3_seq(base: &[CExpr], ours: &[CExpr], theirs: &[CExpr]) -> Option<Vec<CExpr>> {
195 if base.len() != ours.len() || base.len() != theirs.len() {
196 return None;
197 }
198 let mut out = Vec::with_capacity(base.len());
199 for i in 0..base.len() {
200 out.push(merge3(&base[i], &ours[i], &theirs[i])?);
201 }
202 Some(out)
203}
204
205fn merge3_arms(base: &[Arm], ours: &[Arm], theirs: &[Arm]) -> Option<Vec<Arm>> {
210 if base.len() != ours.len() || base.len() != theirs.len() {
211 return None;
212 }
213 let mut out = Vec::with_capacity(base.len());
214 for i in 0..base.len() {
215 out.push(Arm {
216 pattern: pick3(&base[i].pattern, &ours[i].pattern, &theirs[i].pattern)?,
217 body: merge3(&base[i].body, &ours[i].body, &theirs[i].body)?,
218 });
219 }
220 Some(out)
221}
222
223fn merge3_fields(
227 base: &[RecordField],
228 ours: &[RecordField],
229 theirs: &[RecordField],
230) -> Option<Vec<RecordField>> {
231 if base.len() != ours.len() || base.len() != theirs.len() {
232 return None;
233 }
234 let mut out = Vec::with_capacity(base.len());
235 for i in 0..base.len() {
236 let name = pick3(&base[i].name, &ours[i].name, &theirs[i].name)?;
237 out.push(RecordField {
238 name,
239 value: merge3(&base[i].value, &ours[i].value, &theirs[i].value)?,
240 });
241 }
242 Some(out)
243}
244
245fn pick3<T: PartialEq + Clone>(base: &T, ours: &T, theirs: &T) -> Option<T> {
248 if ours == theirs {
249 Some(ours.clone())
250 } else if base == ours {
251 Some(theirs.clone())
252 } else if base == theirs {
253 Some(ours.clone())
254 } else {
255 None
256 }
257}
258
259#[cfg(test)]
260mod tests {
261 use super::*;
262
263 fn body(src: &str) -> CExpr {
265 let prog = lex_syntax::parse_source(src).expect("parse");
266 let stages = lex_ast::canonicalize_program(&prog);
267 for st in stages {
268 if let lex_ast::Stage::FnDecl(fd) = st {
269 return fd.body;
270 }
271 }
272 panic!("no fn in source");
273 }
274
275 fn merged(base: &str, ours: &str, theirs: &str) -> CExpr {
276 match merge_bodies(&body(base), &body(ours), &body(theirs)) {
277 BodyMerge::Merged(e) => e,
278 BodyMerge::Conflict => panic!("expected a clean merge, got Conflict"),
279 }
280 }
281
282 fn is_conflict(base: &str, ours: &str, theirs: &str) -> bool {
283 matches!(merge_bodies(&body(base), &body(ours), &body(theirs)), BodyMerge::Conflict)
284 }
285
286 #[test]
287 fn one_sided_change_takes_that_side() {
288 let base = "fn f(x :: Int) -> Int { x }\n";
290 let ours = "fn f(x :: Int) -> Int { x + 1 }\n";
291 assert_eq!(merged(base, ours, base), body(ours));
292 assert_eq!(merged(base, base, ours), body(ours));
293 }
294
295 #[test]
296 fn identical_edit_both_sides_is_not_a_conflict() {
297 let base = "fn f(x :: Int) -> Int { x }\n";
298 let same = "fn f(x :: Int) -> Int { x + 1 }\n";
299 assert_eq!(merged(base, same, same), body(same));
300 }
301
302 #[test]
303 fn disjoint_match_arms_auto_merge() {
304 let base = "\
307fn classify(n :: Int) -> Int {
308 match n {
309 0 => 10,
310 _ => 20,
311 }
312}
313";
314 let ours = "\
315fn classify(n :: Int) -> Int {
316 match n {
317 0 => 11,
318 _ => 20,
319 }
320}
321";
322 let theirs = "\
323fn classify(n :: Int) -> Int {
324 match n {
325 0 => 10,
326 _ => 22,
327 }
328}
329";
330 let want = "\
331fn classify(n :: Int) -> Int {
332 match n {
333 0 => 11,
334 _ => 22,
335 }
336}
337";
338 assert_eq!(merged(base, ours, theirs), body(want));
339 }
340
341 #[test]
342 fn same_match_arm_edited_both_sides_conflicts() {
343 let base = "\
344fn classify(n :: Int) -> Int {
345 match n {
346 0 => 10,
347 _ => 20,
348 }
349}
350";
351 let ours = "\
352fn classify(n :: Int) -> Int {
353 match n {
354 0 => 11,
355 _ => 20,
356 }
357}
358";
359 let theirs = "\
360fn classify(n :: Int) -> Int {
361 match n {
362 0 => 12,
363 _ => 20,
364 }
365}
366";
367 assert!(is_conflict(base, ours, theirs));
368 }
369
370 #[test]
371 fn disjoint_let_bindings_auto_merge() {
372 let base = "fn f(x :: Int) -> Int {\n let y := x\n y\n}\n";
375 let ours = "fn f(x :: Int) -> Int {\n let y := x + 1\n y\n}\n";
376 let theirs = "fn f(x :: Int) -> Int {\n let y := x\n y + 100\n}\n";
377 let want = "fn f(x :: Int) -> Int {\n let y := x + 1\n y + 100\n}\n";
378 assert_eq!(merged(base, ours, theirs), body(want));
379 }
380
381 #[test]
382 fn disjoint_binop_operands_auto_merge() {
383 let base = "fn f(x :: Int) -> Int { x + x }\n";
385 let ours = "fn f(x :: Int) -> Int { (x + 1) + x }\n";
386 let theirs = "fn f(x :: Int) -> Int { x + (x + 2) }\n";
387 let want = "fn f(x :: Int) -> Int { (x + 1) + (x + 2) }\n";
388 assert_eq!(merged(base, ours, theirs), body(want));
389 }
390
391 #[test]
392 fn a_length_changing_edit_on_a_both_edited_node_conflicts() {
393 let base = "fn f(x :: Int) -> Int { g(x, x) }\nfn g(a :: Int, b :: Int) -> Int { a }\n";
397 let ours = "fn f(x :: Int) -> Int { g(x, x, x) }\nfn g(a :: Int, b :: Int) -> Int { a }\n";
398 let theirs = "fn f(x :: Int) -> Int { g(x, x + 9) }\nfn g(a :: Int, b :: Int) -> Int { a }\n";
399 assert!(is_conflict(base, ours, theirs));
400 }
401
402 #[test]
403 fn kind_replaced_one_side_edited_other_conflicts() {
404 let base = "fn f(x :: Int) -> Int { x + x }\n";
407 let ours = "fn f(x :: Int) -> Int { 42 }\n";
408 let theirs = "fn f(x :: Int) -> Int { x + (x + 1) }\n";
409 assert!(is_conflict(base, ours, theirs));
410 }
411}