inductive L (α : Type u) : Type u where
  | nil : L α
  | cons (x : α) (xs : L α) : L α

-- Check that L.cons.inj uses the per-ctor noConfusion principle
/--
info: theorem L.cons.inj.{u} : ∀ {α : Type u} {x : α} {xs : L α} {x_1 : α} {xs_1 : L α},
  L.cons x xs = L.cons x_1 xs_1 → x = x_1 ∧ xs = xs_1 :=
fun {α} {x} {xs} {x_1} {xs_1} x_2 => L.cons.noConfusion x_2 fun x_eq xs_eq => ⟨eq_of_heq x_eq, eq_of_heq xs_eq⟩
-/
#guard_msgs in
#print L.cons.inj

-- Check that injection uses the per-ctor noConfusion principle

theorem ex1 (h : L.cons x xs = L.cons y ys) : x = y ∧ xs = ys := by
  injection h
  repeat constructor <;> assumption

/--
info: theorem ex1.{u_1} : ∀ {α : Type u_1} {x : α} {xs : L α} {y : α} {ys : L α},
  L.cons x xs = L.cons y ys → x = y ∧ xs = ys :=
fun {α} {x} {xs} {y} {ys} h => L.cons.noConfusion h fun x_eq xs_eq => ⟨eq_of_heq x_eq, eq_of_heq xs_eq⟩
-/
#guard_msgs in #print ex1

-- Check that injection does not intro more than it should
theorem ex2 (h : L.cons x xs = L.cons y ys) : Unit → x = y ∧ xs = ys := by
  injection h
  intro u
  repeat constructor <;> assumption

-- Check that grind uses `L.cons.noConfusion`

theorem ex3 (h : L.cons x xs = L.cons y ys) : x = y ∧ xs = ys := by
  grind
/--
info: theorem ex3._proof_1_1.{u_1} : ∀ {α : Type u_1} {x : α} {xs : L α} {y : α} {ys : L α},
  L.cons x xs = L.cons y ys → x = y ∧ xs = ys :=
fun {α} {x} {xs} {y} {ys} h =>
  Classical.byContradiction
    (Lean.Grind.intro_with_eq (¬(x = y ∧ xs = ys)) (¬x = y ∨ ¬xs = ys) False (Lean.Grind.not_and (x = y) (xs = ys))
      fun h_1 =>
      id
        (Eq.mp
          (Eq.trans (Eq.symm (eq_true (id (L.cons.inj (id h)).1)))
            (Lean.Grind.eq_false_of_not_eq_true
              (Eq.trans
                (Eq.symm
                  (Lean.Grind.or_eq_of_eq_false_right
                    (Lean.Grind.not_eq_of_eq_true (eq_true (id (L.cons.inj (id h)).2)))))
                (eq_true h_1))))
          True.intro))
-/
#guard_msgs in #print ex3._proof_1_1


-- Check that for numary constructors, we get a trivial inlined “no confusion” principle

inductive AlmostEnum where | a | b | mk (b : Bool)

-- No explicit noConfusion principle for `AlmostEnum.a`:
/-- error: Unknown constant `AlmostEnum.a.noConfusion` -/
#guard_msgs in #print sig AlmostEnum.a.noConfusion


set_option linter.unusedVariables false in
theorem ex4 (h : AlmostEnum.a = AlmostEnum.a) : True := by
  injection h
  trivial

/--
info: theorem ex4 : AlmostEnum.a = AlmostEnum.a → True :=
fun h => (fun P => P) True.intro
-/
#guard_msgs in #print ex4
