diff --git a/check/classic/classic.exp b/check/classic/classic.exp
index c7d1a0aa..395b0276 100644
--- a/check/classic/classic.exp
+++ b/check/classic/classic.exp
@@ -261,6 +261,17 @@ Nothing else to report in this section
.> UNUSED CONSTRUCTORS/RECORD FIELDS:
====================================
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:9: sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:18: product.unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:8: sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:16: product.unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:8: sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:16: product.unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:8: sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:16: product.unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:9: Alias.sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:17: Alias.product.unused
+
./examples/docs/fields_and_constructors/code_constructs/gadt/gadt_lib.mli:4: gadt.Float
./examples/docs/fields_and_constructors/code_constructs/inline_record/inline_record_lib.mli:4: t.Left
diff --git a/check/classic/classic.ref b/check/classic/classic.ref
index cea2bcfe..7a356929 100644
--- a/check/classic/classic.ref
+++ b/check/classic/classic.ref
@@ -261,6 +261,17 @@ Nothing else to report in this section
.> UNUSED CONSTRUCTORS/RECORD FIELDS:
====================================
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:9: sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:18: product.unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:8: sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:16: product.unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:8: sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:16: product.unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:8: sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:16: product.unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:9: Alias.sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:17: Alias.product.unused
+
./examples/docs/fields_and_constructors/code_constructs/gadt/gadt_lib.mli:4: gadt.Float
./examples/docs/fields_and_constructors/code_constructs/inline_record/inline_record_lib.mli:4: t.Left
@@ -721,7 +732,7 @@ Nothing else to report in this section
--------------------------------------------------------------------------------
-Total: 593
-Success: 593
+Total: 603
+Success: 603
Failed: 0
Ratio: 100.%
diff --git a/check/internal/internal.exp b/check/internal/internal.exp
index 84a6c92a..9248e8c1 100644
--- a/check/internal/internal.exp
+++ b/check/internal/internal.exp
@@ -213,6 +213,17 @@ Nothing else to report in this section
.> UNUSED CONSTRUCTORS/RECORD FIELDS:
====================================
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:9: sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:18: product.unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:8: sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:16: product.unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:8: sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:16: product.unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:8: sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:16: product.unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:9: Alias.sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:17: Alias.product.unused
+
./examples/docs/fields_and_constructors/code_constructs/gadt/gadt_lib.mli:4: gadt.Float
./examples/docs/fields_and_constructors/code_constructs/inline_record/inline_record_lib.mli:4: t.Left
diff --git a/check/internal/internal.ref b/check/internal/internal.ref
index a004e70d..4a7eedfb 100644
--- a/check/internal/internal.ref
+++ b/check/internal/internal.ref
@@ -213,6 +213,17 @@ Nothing else to report in this section
.> UNUSED CONSTRUCTORS/RECORD FIELDS:
====================================
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:9: sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:18: product.unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:8: sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:16: product.unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:8: sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:16: product.unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:8: sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:16: product.unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:9: Alias.sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:17: Alias.product.unused
+
./examples/docs/fields_and_constructors/code_constructs/gadt/gadt_lib.mli:4: gadt.Float
./examples/docs/fields_and_constructors/code_constructs/inline_record/inline_record_lib.mli:4: t.Left
@@ -673,7 +684,7 @@ Nothing else to report in this section
--------------------------------------------------------------------------------
-Total: 549
-Success: 549
+Total: 559
+Success: 559
Failed: 0
Ratio: 100.%
diff --git a/check/threshold-1/threshold-1.exp b/check/threshold-1/threshold-1.exp
index be1548ac..c8277204 100644
--- a/check/threshold-1/threshold-1.exp
+++ b/check/threshold-1/threshold-1.exp
@@ -603,6 +603,17 @@ Nothing else to report in this section
.> UNUSED CONSTRUCTORS/RECORD FIELDS:
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:9: sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:18: product.unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:8: sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:16: product.unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:8: sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:16: product.unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:8: sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:16: product.unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:9: Alias.sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:17: Alias.product.unused
+
====================================
./examples/docs/fields_and_constructors/code_constructs/gadt/gadt_lib.mli:4: gadt.Float
@@ -684,6 +695,59 @@ Nothing else to report in this section
.>-> ALMOST UNUSED CONSTRUCTORS/RECORD FIELDS: Called 1 time(s):
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:3: sum.Used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:4: sum.Used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:5: sum.Used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:6: sum.Used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:7: sum.Used_directly
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:8: sum.Used_externally
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:12: product.used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:13: product.used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:14: product.used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:15: product.used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:16: product.used_directly
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:17: product.used_externally
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:3: sum.Used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:4: sum.Used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:5: sum.Used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:6: sum.Used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:7: sum.Used_directly
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:11: product.used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:12: product.used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:13: product.used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:14: product.used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:15: product.used_directly
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:3: sum.Used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:4: sum.Used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:5: sum.Used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:6: sum.Used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:7: sum.Used_directly
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:11: product.used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:12: product.used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:13: product.used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:14: product.used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:15: product.used_directly
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:3: sum.Used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:4: sum.Used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:5: sum.Used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:6: sum.Used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:7: sum.Used_directly
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:11: product.used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:12: product.used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:13: product.used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:14: product.used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:15: product.used_directly
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:4: Alias.sum.Used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:5: Alias.sum.Used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:6: Alias.sum.Used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:7: Alias.sum.Used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:8: Alias.sum.Used_directly
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:12: Alias.product.used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:13: Alias.product.used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:14: Alias.product.used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:15: Alias.product.used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:16: Alias.product.used_directly
+
./examples/docs/fields_and_constructors/code_constructs/gadt/gadt_lib.mli:3: gadt.Int
./examples/docs/fields_and_constructors/code_constructs/inline_record/inline_record_lib.mli:3: t.Both
diff --git a/check/threshold-1/threshold-1.ref b/check/threshold-1/threshold-1.ref
index aed84db1..c82df69c 100644
--- a/check/threshold-1/threshold-1.ref
+++ b/check/threshold-1/threshold-1.ref
@@ -603,6 +603,20 @@ Nothing else to report in this section
.> UNUSED CONSTRUCTORS/RECORD FIELDS:
+====================================: Unrecognized report line format. Expected : 'filepath:line_nb:value'
+====================================
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:9: sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:18: product.unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:8: sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:16: product.unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:8: sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:16: product.unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:8: sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:16: product.unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:9: Alias.sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:17: Alias.product.unused
+
+====================================: Unrecognized report line format. Expected : 'filepath:line_nb:value'
====================================
./examples/docs/fields_and_constructors/code_constructs/gadt/gadt_lib.mli:4: gadt.Float
@@ -684,6 +698,59 @@ Nothing else to report in this section
.>-> ALMOST UNUSED CONSTRUCTORS/RECORD FIELDS: Called 1 time(s):
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:3: sum.Used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:4: sum.Used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:5: sum.Used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:6: sum.Used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:7: sum.Used_directly
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:8: sum.Used_externally
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:12: product.used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:13: product.used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:14: product.used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:15: product.used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:16: product.used_directly
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:17: product.used_externally
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:3: sum.Used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:4: sum.Used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:5: sum.Used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:6: sum.Used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:7: sum.Used_directly
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:11: product.used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:12: product.used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:13: product.used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:14: product.used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:15: product.used_directly
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:3: sum.Used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:4: sum.Used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:5: sum.Used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:6: sum.Used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:7: sum.Used_directly
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:11: product.used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:12: product.used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:13: product.used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:14: product.used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:15: product.used_directly
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:3: sum.Used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:4: sum.Used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:5: sum.Used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:6: sum.Used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:7: sum.Used_directly
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:11: product.used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:12: product.used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:13: product.used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:14: product.used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:15: product.used_directly
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:4: Alias.sum.Used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:5: Alias.sum.Used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:6: Alias.sum.Used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:7: Alias.sum.Used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:8: Alias.sum.Used_directly
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:12: Alias.product.used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:13: Alias.product.used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:14: Alias.product.used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:15: Alias.product.used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:16: Alias.product.used_directly
+
./examples/docs/fields_and_constructors/code_constructs/gadt/gadt_lib.mli:3: gadt.Int
./examples/docs/fields_and_constructors/code_constructs/inline_record/inline_record_lib.mli:3: t.Both
@@ -1145,7 +1212,7 @@ Nothing else to report in this section
--------------------------------------------------------------------------------
-Total: 939
-Success: 939
+Total: 1001
+Success: 1001
Failed: 0
Ratio: 100.%
diff --git a/check/threshold-3-0.5/threshold-3-0.5.exp b/check/threshold-3-0.5/threshold-3-0.5.exp
index 313bc7ea..e04ce12a 100644
--- a/check/threshold-3-0.5/threshold-3-0.5.exp
+++ b/check/threshold-3-0.5/threshold-3-0.5.exp
@@ -786,6 +786,17 @@ Nothing else to report in this section
.> UNUSED CONSTRUCTORS/RECORD FIELDS:
====================================
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:9: sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:18: product.unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:8: sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:16: product.unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:8: sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:16: product.unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:8: sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:16: product.unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:9: Alias.sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:17: Alias.product.unused
+
./examples/docs/fields_and_constructors/code_constructs/gadt/gadt_lib.mli:4: gadt.Float
./examples/docs/fields_and_constructors/code_constructs/inline_record/inline_record_lib.mli:4: t.Left
@@ -866,6 +877,59 @@ Nothing else to report in this section
.>-> ALMOST UNUSED CONSTRUCTORS/RECORD FIELDS: Called 1 time(s):
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:3: sum.Used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:4: sum.Used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:5: sum.Used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:6: sum.Used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:7: sum.Used_directly
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:8: sum.Used_externally
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:12: product.used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:13: product.used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:14: product.used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:15: product.used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:16: product.used_directly
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:17: product.used_externally
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:3: sum.Used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:4: sum.Used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:5: sum.Used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:6: sum.Used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:7: sum.Used_directly
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:11: product.used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:12: product.used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:13: product.used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:14: product.used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:15: product.used_directly
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:3: sum.Used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:4: sum.Used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:5: sum.Used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:6: sum.Used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:7: sum.Used_directly
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:11: product.used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:12: product.used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:13: product.used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:14: product.used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:15: product.used_directly
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:3: sum.Used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:4: sum.Used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:5: sum.Used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:6: sum.Used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:7: sum.Used_directly
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:11: product.used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:12: product.used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:13: product.used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:14: product.used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:15: product.used_directly
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:4: Alias.sum.Used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:5: Alias.sum.Used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:6: Alias.sum.Used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:7: Alias.sum.Used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:8: Alias.sum.Used_directly
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:12: Alias.product.used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:13: Alias.product.used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:14: Alias.product.used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:15: Alias.product.used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:16: Alias.product.used_directly
+
./examples/docs/fields_and_constructors/code_constructs/gadt/gadt_lib.mli:3: gadt.Int
./examples/docs/fields_and_constructors/code_constructs/inline_record/inline_record_lib.mli:3: t.Both
diff --git a/check/threshold-3-0.5/threshold-3-0.5.ref b/check/threshold-3-0.5/threshold-3-0.5.ref
index 7fed8c11..3919783a 100644
--- a/check/threshold-3-0.5/threshold-3-0.5.ref
+++ b/check/threshold-3-0.5/threshold-3-0.5.ref
@@ -786,6 +786,17 @@ Nothing else to report in this section
.> UNUSED CONSTRUCTORS/RECORD FIELDS:
====================================
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:9: sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:18: product.unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:8: sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:16: product.unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:8: sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:16: product.unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:8: sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:16: product.unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:9: Alias.sum.Unused
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:17: Alias.product.unused
+
./examples/docs/fields_and_constructors/code_constructs/gadt/gadt_lib.mli:4: gadt.Float
./examples/docs/fields_and_constructors/code_constructs/inline_record/inline_record_lib.mli:4: t.Left
@@ -866,6 +877,59 @@ Nothing else to report in this section
.>-> ALMOST UNUSED CONSTRUCTORS/RECORD FIELDS: Called 1 time(s):
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:3: sum.Used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:4: sum.Used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:5: sum.Used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:6: sum.Used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:7: sum.Used_directly
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:8: sum.Used_externally
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:12: product.used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:13: product.used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:14: product.used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:15: product.used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:16: product.used_directly
+./examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:17: product.used_externally
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:3: sum.Used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:4: sum.Used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:5: sum.Used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:6: sum.Used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:7: sum.Used_directly
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:11: product.used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:12: product.used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:13: product.used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:14: product.used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:15: product.used_directly
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:3: sum.Used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:4: sum.Used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:5: sum.Used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:6: sum.Used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:7: sum.Used_directly
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:11: product.used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:12: product.used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:13: product.used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:14: product.used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:15: product.used_directly
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:3: sum.Used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:4: sum.Used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:5: sum.Used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:6: sum.Used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:7: sum.Used_directly
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:11: product.used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:12: product.used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:13: product.used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:14: product.used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:15: product.used_directly
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:4: Alias.sum.Used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:5: Alias.sum.Used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:6: Alias.sum.Used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:7: Alias.sum.Used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:8: Alias.sum.Used_directly
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:12: Alias.product.used_by_explicit_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:13: Alias.product.used_by_hidden_equation
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:14: Alias.product.used_by_include
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:15: Alias.product.used_by_module_alias
+./examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:16: Alias.product.used_directly
+
./examples/docs/fields_and_constructors/code_constructs/gadt/gadt_lib.mli:3: gadt.Int
./examples/docs/fields_and_constructors/code_constructs/inline_record/inline_record_lib.mli:3: t.Both
@@ -1549,7 +1613,7 @@ Nothing else to report in this section
--------------------------------------------------------------------------------
-Total: 1261
-Success: 1261
+Total: 1323
+Success: 1323
Failed: 0
Ratio: 100.%
diff --git a/docs/fields_and_constructors/FIELDS_AND_CONSTRUCTORS.md b/docs/fields_and_constructors/FIELDS_AND_CONSTRUCTORS.md
index 0670e55d..cc876d98 100644
--- a/docs/fields_and_constructors/FIELDS_AND_CONSTRUCTORS.md
+++ b/docs/fields_and_constructors/FIELDS_AND_CONSTRUCTORS.md
@@ -10,6 +10,8 @@
+ [Examples](#examples)
+ [Limitations](#limitations)
+ [Polymorphic variant](#polymorphic-variant)
+ + [Extensible variant](#extensible-variant)
+ + [Inline record](#inline-record)
# Fields and constructors
@@ -231,6 +233,7 @@ it from its type's definition.
- [GADT](./code_constructs/GADT.md)
- [Extensible variant](./code_constructs/EXTENSIBLE_VARIANT.md)
- [Inline record](./code_constructs/INLINE_RECORD.md)
+ - [Equal types](./code_constructs/EQUAL_TYPES.md)
# Limitations
diff --git a/docs/fields_and_constructors/code_constructs/EQUAL_TYPES.md b/docs/fields_and_constructors/code_constructs/EQUAL_TYPES.md
new file mode 100644
index 00000000..a9cacb86
--- /dev/null
+++ b/docs/fields_and_constructors/code_constructs/EQUAL_TYPES.md
@@ -0,0 +1,745 @@
+The reference files for this example are in the
+[equal\_types](../../../examples/docs/fields_and_constructors/code_constructs/equal_types)
+directory.
+
+The reference takes place in `/tmp/docs/fields_and_constructors/code_constructs`, which
+is a copy of the [code\_constructs](../../../examples/docs/fields_and_constructors/code_constructs)
+directory. Reported locations may differ depending on the location of the source
+files.
+
+The compilation command is :
+```
+make -C equal_types build
+```
+
+The analysis command is :
+```
+make -C equal_types analyze
+```
+
+The compile + analyze command is :
+```
+make -C equal_types
+```
+
+## First run
+
+There is a lot of code involved. Feel free to skip it and jump to its
+[description](#code_description).
+
+Code:
+```OCaml
+(* definitions.ml *)
+type sum =
+ | Used_by_explicit_equation
+ | Used_by_hidden_equation
+ | Used_by_include
+ | Used_by_module_alias
+ | Used_directly
+ | Unused
+
+type product = {
+ used_by_explicit_equation : int;
+ used_by_hidden_equation : int;
+ used_by_include : int;
+ used_by_module_alias : int;
+ used_directly : int;
+ unused : int;
+}
+```
+```OCaml
+(* via_explicit_equations.ml *)
+type sum = Definitions.sum =
+ | Used_by_explicit_equation
+ | Used_by_hidden_equation
+ | Used_by_include
+ | Used_by_module_alias
+ | Used_directly
+ | Unused
+
+type product = Definitions.product = {
+ used_by_explicit_equation : int;
+ used_by_hidden_equation : int;
+ used_by_include : int;
+ used_by_module_alias : int;
+ used_directly : int;
+ unused : int;
+}
+```
+```OCaml
+(* via_hidden_equations.mli *)
+type sum =
+ | Used_by_explicit_equation
+ | Used_by_hidden_equation
+ | Used_by_include
+ | Used_by_module_alias
+ | Used_directly
+ | Unused
+
+type product = {
+ used_by_explicit_equation : int;
+ used_by_hidden_equation : int;
+ used_by_include : int;
+ used_by_module_alias : int;
+ used_directly : int;
+ unused : int;
+}
+```
+```OCaml
+(* via_hidden_equations.ml *)
+type sum = Definitions.sum =
+ | Used_by_explicit_equation
+ | Used_by_hidden_equation
+ | Used_by_include
+ | Used_by_module_alias
+ | Used_directly
+ | Unused
+
+type product = Definitions.product = {
+ used_by_explicit_equation : int;
+ used_by_hidden_equation : int;
+ used_by_include : int;
+ used_by_module_alias : int;
+ used_directly : int;
+ unused : int;
+}
+```
+```OCaml
+(* via_include.mli *)
+type sum =
+ | Used_by_explicit_equation
+ | Used_by_hidden_equation
+ | Used_by_include
+ | Used_by_module_alias
+ | Used_directly
+ | Unused
+
+type product = {
+ used_by_explicit_equation : int;
+ used_by_hidden_equation : int;
+ used_by_include : int;
+ used_by_module_alias : int;
+ used_directly : int;
+ unused : int;
+}
+```
+```OCaml
+(* via_include.ml *)
+include Definitions
+```
+```OCaml
+(* via_module_alias.mli *)
+module Alias : sig
+ type sum =
+ | Used_by_explicit_equation
+ | Used_by_hidden_equation
+ | Used_by_include
+ | Used_by_module_alias
+ | Used_directly
+ | Unused
+
+ type product = {
+ used_by_explicit_equation : int;
+ used_by_hidden_equation : int;
+ used_by_include : int;
+ used_by_module_alias : int;
+ used_directly : int;
+ unused : int;
+ }
+end
+```
+```OCaml
+(* via_module_alias.ml *)
+module Alias = Definitions
+```
+```OCaml
+(* all_internal.mli *)
+type sum =
+ | Used_by_explicit_equation
+ | Used_by_hidden_equation
+ | Used_by_include
+ | Used_by_module_alias
+ | Used_directly
+ | Used_externally
+ | Unused
+
+type product = {
+ used_by_explicit_equation : int;
+ used_by_hidden_equation : int;
+ used_by_include : int;
+ used_by_module_alias : int;
+ used_directly : int;
+ used_externally : int;
+ unused : int;
+}
+```
+```OCaml
+(* all_internal.ml *)
+type original_sum =
+ | Used_by_explicit_equation
+ | Used_by_hidden_equation
+ | Used_by_include
+ | Used_by_module_alias
+ | Used_directly
+ | Used_externally
+ | Unused
+
+type original_product = {
+ used_by_explicit_equation : int;
+ used_by_hidden_equation : int;
+ used_by_include : int;
+ used_by_module_alias : int;
+ used_directly : int;
+ used_externally : int;
+ unused : int;
+}
+
+let _ : original_product -> original_sum =
+ fun {used_directly; _} -> Used_directly
+
+type explicit_eq_sum = original_sum =
+ | Used_by_explicit_equation
+ | Used_by_hidden_equation
+ | Used_by_include
+ | Used_by_module_alias
+ | Used_directly
+ | Used_externally
+ | Unused
+
+type explicit_eq_product = original_product = {
+ used_by_explicit_equation : int;
+ used_by_hidden_equation : int;
+ used_by_include : int;
+ used_by_module_alias : int;
+ used_directly : int;
+ used_externally : int;
+ unused : int;
+}
+
+let _ : explicit_eq_product -> explicit_eq_sum =
+ fun {used_by_explicit_equation; _} -> Used_by_explicit_equation
+
+module M : sig
+ type sum =
+ | Used_by_explicit_equation
+ | Used_by_hidden_equation
+ | Used_by_include
+ | Used_by_module_alias
+ | Used_directly
+ | Used_externally
+ | Unused
+
+ type product = {
+ used_by_explicit_equation : int;
+ used_by_hidden_equation : int;
+ used_by_include : int;
+ used_by_module_alias : int;
+ used_directly : int;
+ used_externally : int;
+ unused : int;
+ }
+end = struct
+ type sum = explicit_eq_sum =
+ | Used_by_explicit_equation
+ | Used_by_hidden_equation
+ | Used_by_include
+ | Used_by_module_alias
+ | Used_directly
+ | Used_externally
+ | Unused
+
+ type product = explicit_eq_product = {
+ used_by_explicit_equation : int;
+ used_by_hidden_equation : int;
+ used_by_include : int;
+ used_by_module_alias : int;
+ used_directly : int;
+ used_externally : int;
+ unused : int;
+ }
+end
+
+let _ : M.product -> M.sum =
+ fun {used_by_hidden_equation; _} -> Used_by_hidden_equation
+
+module Alias = M
+
+let _ : Alias.product -> Alias.sum =
+ fun {used_by_module_alias; _} -> Used_by_module_alias
+
+include Alias
+
+let _ : product -> sum =
+ fun {used_by_include; _} -> Used_by_include
+```
+```OCaml
+(* equal_types.ml *)
+let _ =
+ let open Definitions in
+ fun {used_directly; _} -> Used_directly
+
+let _ =
+ let open Via_explicit_equations in
+ fun {used_by_explicit_equation; _} -> Used_by_explicit_equation
+
+let _ =
+ let open Via_hidden_equations in
+ fun {used_by_hidden_equation; _} -> Used_by_hidden_equation
+
+let _ =
+ let open Via_include in
+ fun {used_by_include; _} -> Used_by_include
+
+let _ =
+ let open Via_module_alias.Alias in
+ fun {used_by_module_alias; _} -> Used_by_module_alias
+
+let _ =
+ let open All_internal in
+ fun {used_externally; _} -> Used_externally
+```
+
+
+Before looking at the analysis results, let's look at the code.
+
+`Definitions` exports 2 types: `sum`, a regular variant type, and
+`product`, a regular record type.
+These 2 types exported by `Definitions` are re-exposed by 4 modules :
+`Via_explicit_equations`, `Via_hidden_equations`, `Via_include`, and
+`Via_module_alias`. The types exported by each of these modules are equal
+to the ones in `Definitions` (thus, they are also all equal):
+- `Via_explicit_equations` is defined within a single .ml and it uses
+ type equations:
+ ```OCaml
+ type sum = Definitions.sum = (* constructors *)
+ type product = Definitions.product = (* fields *)
+ ```
+- `Via_hidden_equations` is implemented the same as `Via_explicit_equations`
+ in its .ml, and its .mli is the same as `Definitions`. The explicit
+ equality is hidden by the interface.
+- `Via_include` is implemented as a simple inclusion of `Definitions`
+ and its interface is the same as `Definitions`. The equality is implied
+ but hidden by the interface.
+- `Via_module_alias` contains a single module `Alias` implemented as an
+ alias of `Definitions` but, in the interface, its signature is explicit
+ and the same as `Definitions`. The explicit equality is once again
+ hidden by the interface.
+
+The constructors and fields of the exported types are used in `Equal_types`.
+Each one states through which module they are used.
+E.g. `sum.Used_by_include` is used through `Via_include`, and
+`product.used_directly` is used through `Definitions`.
+
+`All_internal` implements all the above mentioned equalitis and uses within
+a single compilation unit. Its `sum` has one additional constructor
+`Used_externally`, and `product` one additional field `used_externally`.
+They are used in `Equal_types` instead of `sum.Used_directly` and
+`product.used_directly`, which are used internally through the original
+types definitions.
+
+Overall, all the fields and constructors are used (through the different
+equivalent types) except for `sum.Unused` and `product.unused`.
+
+
+Compile and analyze :
+```
+$ make -C equal_types
+make: Entering directory '/tmp/docs/fields_and_constructors/code_constructs/equal_types'
+ocamlopt -bin-annot definitions.ml via_explicit_equations.ml via_hidden_equations.mli via_hidden_equations.ml via_include.mli via_include.ml via_module_alias.mli via_module_alias.ml all_internal.mli all_internal.ml equal_types.ml
+dead_code_analyzer --nothing -T all .
+Scanning files...
+ [DONE]
+
+.> UNUSED CONSTRUCTORS/RECORD FIELDS:
+====================================
+/tmp/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:9: sum.Unused
+/tmp/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli:18: product.unused
+/tmp/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:8: sum.Unused
+/tmp/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml:16: product.unused
+/tmp/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:8: sum.Unused
+/tmp/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli:16: product.unused
+/tmp/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:8: sum.Unused
+/tmp/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli:16: product.unused
+/tmp/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:9: Alias.sum.Unused
+/tmp/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli:17: Alias.product.unused
+
+Nothing else to report in this section
+--------------------------------------------------------------------------------
+
+
+make: Leaving directory '/tmp/docs/fields_and_constructors/code_constructs/equal_types'
+```
+
+As expected, only the constructor `sum.Unused` and field `product.unused`
+are reported. However, they are reported for each module that exports them
+and hides the equality.
+According to OCaml's [documentation](https://ocaml.org/manual/5.3/typedecl.html):
+> The optional type equation `= typexpr` makes the defined type equivalent
+> to the type expression `typexpr`: one can be substituted for the other
+> during typing. If no type equation is given, a new type is generated:
+> the defined type is incompatible with any other type.
+
+Because all the exported types are equivalent, reporting the unused fields
+and constructors for one definition would suffice. In addition, reporting
+equivalent components breaks the "independence" of reports: one report cannot
+be addressed without also addressing the others.\
+But because the equality is hidden in the signature, they must still be
+considered as different definitions.\
+In order to balance these two semantics and provide actionable reports,
+the analyzer always reports a type's component, unless it explcitly is
+exported as equivalent to another, but unifies all the equivalent
+components uses to avoid reporting one that would be unused in a
+specific type but used in another (e.g. `Definitions.sum.Used_by_include`
+vs `Via_include.sum.Used_by_include`).
+
+> [!NOTE]
+> Unifying uses avoids reporting a component for one type when it is used
+> for another equivalent type. Such reports are considered false positives
+> because one cannot remove the component from one type wihtout breaking the
+> equality (which would lead to a more complex update than a dead code
+> clean up)
+>
+> If you have a strong need/desire for separate tracking of uses and
+> having such reports, please feel free to
+> [open an issue](https://github.com/LexiFi/dead_code_analyzer/issues/new)
+
+## Removing the unused constructors and fields
+
+> [!TIP]
+> When removing one compoennt, we must remove it from all the equivalent
+> types. Otherwise, the compiler will complain with an error.
+> E.g. if we only remove `Via_include.sum.Unused`, the compiler tells us
+> the type equation `type sum = Definitions.sum` (implied by the include)
+> is invalid:
+> ```
+> File "via_include.ml", line 1:
+> Error: The implementation via_include.ml
+> does not match the interface via_include.mli:
+> Type declarations do not match:
+> type sum =
+> Definitions.sum =
+> Used_by_explicit_equation
+> | Used_by_hidden_equation
+> | Used_by_include
+> | Used_by_module_alias
+> | Used_directly
+> | Unused
+> is not included in
+> type sum =
+> Used_by_explicit_equation
+> | Used_by_hidden_equation
+> | Used_by_include
+> | Used_by_module_alias
+> | Used_directly
+> An extra constructor, Unused, is provided in the first declaration.
+> File "via_include.mli", lines 2-7, characters 0-17:
+> Expected declaration
+> File "definitions.ml", lines 2-8, characters 0-10: Actual declaration
+> ```
+
+After removing the unused constructors and fields from the .mli and .ml files,
+the compiler reports the following error:
+```
+File "via_explicit_equations.ml", lines 2-8, characters 0-10:
+2 | type sum = Definitions.sum =
+3 | | Used_by_explicit_equation
+4 | | Used_by_hidden_equation
+5 | | Used_by_include
+6 | | Used_by_module_alias
+7 | | Used_directly
+8 | | Unused
+Error: This variant or record definition does not match that of type
+ Definitions.sum
+ A constructor, Unused, is missing in the original definition.
+```
+
+As explained, the components in `Via_explicit_equations` were not reported
+because they are explicitly exported as equal to `Definitions`'s.
+Although they were not reported, the typing semantics tell us that they can
+be substituted by their equivalents, so if an equivalent component is
+unused, `Via_explicit_equations`'s component is.
+We can simply remove the missing constructor `Unused` (and field `unused`)
+from `Via_explicit_equations.sum` (and `product`)
+
+Similarly for `All_internal`, we can follow the compiler errors to fix each
+equivalent type definition.
+
+Now we have removed the unused constructors and fields, and fixed all the
+compilation errors. Let's look at the code and analyze it.
+
+Again, there is a lot of code involved. Feel free to skip it and jump the
+[analysis results](#clean_analysis).
+
+Code:
+```OCaml
+(* definitions.ml *)
+type sum =
+ | Used_by_explicit_equation
+ | Used_by_hidden_equation
+ | Used_by_include
+ | Used_by_module_alias
+ | Used_directly
+
+type product = {
+ used_by_explicit_equation : int;
+ used_by_hidden_equation : int;
+ used_by_include : int;
+ used_by_module_alias : int;
+ used_directly : int;
+}
+```
+```OCaml
+(* via_explicit_equations.ml *)
+type sum = Definitions.sum =
+ | Used_by_explicit_equation
+ | Used_by_hidden_equation
+ | Used_by_include
+ | Used_by_module_alias
+ | Used_directly
+
+type product = Definitions.product = {
+ used_by_explicit_equation : int;
+ used_by_hidden_equation : int;
+ used_by_include : int;
+ used_by_module_alias : int;
+ used_directly : int;
+}
+```
+```OCaml
+(* via_hidden_equations.mli *)
+type sum =
+ | Used_by_explicit_equation
+ | Used_by_hidden_equation
+ | Used_by_include
+ | Used_by_module_alias
+ | Used_directly
+
+type product = {
+ used_by_explicit_equation : int;
+ used_by_hidden_equation : int;
+ used_by_include : int;
+ used_by_module_alias : int;
+ used_directly : int;
+}
+```
+```OCaml
+(* via_hidden_equations.ml *)
+type sum = Definitions.sum =
+ | Used_by_explicit_equation
+ | Used_by_hidden_equation
+ | Used_by_include
+ | Used_by_module_alias
+ | Used_directly
+
+type product = Definitions.product = {
+ used_by_explicit_equation : int;
+ used_by_hidden_equation : int;
+ used_by_include : int;
+ used_by_module_alias : int;
+ used_directly : int;
+}
+```
+```OCaml
+(* via_include.mli *)
+type sum =
+ | Used_by_explicit_equation
+ | Used_by_hidden_equation
+ | Used_by_include
+ | Used_by_module_alias
+ | Used_directly
+
+type product = {
+ used_by_explicit_equation : int;
+ used_by_hidden_equation : int;
+ used_by_include : int;
+ used_by_module_alias : int;
+ used_directly : int;
+}
+```
+```OCaml
+(* via_include.ml *)
+include Definitions
+```
+```OCaml
+(* via_module_alias.mli *)
+module Alias : sig
+ type sum =
+ | Used_by_explicit_equation
+ | Used_by_hidden_equation
+ | Used_by_include
+ | Used_by_module_alias
+ | Used_directly
+
+ type product = {
+ used_by_explicit_equation : int;
+ used_by_hidden_equation : int;
+ used_by_include : int;
+ used_by_module_alias : int;
+ used_directly : int;
+ }
+end
+```
+```OCaml
+(* via_module_alias.ml *)
+module Alias = Definitions
+```
+```OCaml
+(* all_internal.mli *)
+type sum =
+ | Used_by_explicit_equation
+ | Used_by_hidden_equation
+ | Used_by_include
+ | Used_by_module_alias
+ | Used_directly
+ | Used_externally
+
+type product = {
+ used_by_explicit_equation : int;
+ used_by_hidden_equation : int;
+ used_by_include : int;
+ used_by_module_alias : int;
+ used_directly : int;
+ used_externally : int;
+}
+```
+```OCaml
+(* all_internal.ml *)
+type original_sum =
+ | Used_by_explicit_equation
+ | Used_by_hidden_equation
+ | Used_by_include
+ | Used_by_module_alias
+ | Used_directly
+ | Used_externally
+
+type original_product = {
+ used_by_explicit_equation : int;
+ used_by_hidden_equation : int;
+ used_by_include : int;
+ used_by_module_alias : int;
+ used_directly : int;
+ used_externally : int;
+}
+
+let _ : original_product -> original_sum =
+ fun {used_directly; _} -> Used_directly
+
+type explicit_eq_sum = original_sum =
+ | Used_by_explicit_equation
+ | Used_by_hidden_equation
+ | Used_by_include
+ | Used_by_module_alias
+ | Used_directly
+ | Used_externally
+
+type explicit_eq_product = original_product = {
+ used_by_explicit_equation : int;
+ used_by_hidden_equation : int;
+ used_by_include : int;
+ used_by_module_alias : int;
+ used_directly : int;
+ used_externally : int;
+}
+
+let _ : explicit_eq_product -> explicit_eq_sum =
+ fun {used_by_explicit_equation; _} -> Used_by_explicit_equation
+
+module M : sig
+ type sum =
+ | Used_by_explicit_equation
+ | Used_by_hidden_equation
+ | Used_by_include
+ | Used_by_module_alias
+ | Used_directly
+ | Used_externally
+
+ type product = {
+ used_by_explicit_equation : int;
+ used_by_hidden_equation : int;
+ used_by_include : int;
+ used_by_module_alias : int;
+ used_directly : int;
+ used_externally : int;
+ }
+end = struct
+ type sum = explicit_eq_sum =
+ | Used_by_explicit_equation
+ | Used_by_hidden_equation
+ | Used_by_include
+ | Used_by_module_alias
+ | Used_directly
+ | Used_externally
+
+ type product = explicit_eq_product = {
+ used_by_explicit_equation : int;
+ used_by_hidden_equation : int;
+ used_by_include : int;
+ used_by_module_alias : int;
+ used_directly : int;
+ used_externally : int;
+ }
+end
+
+let _ : M.product -> M.sum =
+ fun {used_by_hidden_equation; _} -> Used_by_hidden_equation
+
+module Alias = M
+
+let _ : Alias.product -> Alias.sum =
+ fun {used_by_module_alias; _} -> Used_by_module_alias
+
+include Alias
+
+let _ : product -> sum =
+ fun {used_by_include; _} -> Used_by_include
+```
+```OCaml
+(* equal_types.ml *)
+let _ =
+ let open Definitions in
+ fun {used_directly; _} -> Used_directly
+
+let _ =
+ let open Via_explicit_equations in
+ fun {used_by_explicit_equation; _} -> Used_by_explicit_equation
+
+let _ =
+ let open Via_hidden_equations in
+ fun {used_by_hidden_equation; _} -> Used_by_hidden_equation
+
+let _ =
+ let open Via_include in
+ fun {used_by_include; _} -> Used_by_include
+
+let _ =
+ let open Via_module_alias.Alias in
+ fun {used_by_module_alias; _} -> Used_by_module_alias
+
+let _ =
+ let open All_internal in
+ fun {used_externally; _} -> Used_externally
+```
+
+
+
+Compile and analyze :
+```
+$ make -C equal_types
+make: Entering directory '/tmp/docs/fields_and_constructors/code_constructs/equal_types'
+ocamlopt -bin-annot definitions.ml via_explicit_equations.ml via_hidden_equations.mli via_hidden_equations.ml via_include.mli via_include.ml via_module_alias.mli via_module_alias.ml all_internal.mli all_internal.ml equal_types.ml
+dead_code_analyzer --nothing -T all .
+Scanning files...
+ [DONE]
+
+.> UNUSED CONSTRUCTORS/RECORD FIELDS:
+====================================
+
+Nothing else to report in this section
+--------------------------------------------------------------------------------
+
+
+make: Leaving directory '/tmp/docs/fields_and_constructors/code_constructs/equal_types'
+```
+
+The analyzer does not report any unused constructor or field.
+Our work here is done.
diff --git a/examples/docs/fields_and_constructors/Makefile b/examples/docs/fields_and_constructors/Makefile
index ed0a10f4..6b0a9a62 100644
--- a/examples/docs/fields_and_constructors/Makefile
+++ b/examples/docs/fields_and_constructors/Makefile
@@ -4,7 +4,9 @@ all: build
build:
make -C code_constructs
+ make -C limitations
clean:
rm -f *~ *.cm* *.o *.obj
make -C code_constructs clean
+ make -C limitations clean
diff --git a/examples/docs/fields_and_constructors/code_constructs/Makefile b/examples/docs/fields_and_constructors/code_constructs/Makefile
index 815be187..b4ea20d5 100644
--- a/examples/docs/fields_and_constructors/code_constructs/Makefile
+++ b/examples/docs/fields_and_constructors/code_constructs/Makefile
@@ -8,6 +8,7 @@ build:
make -C gadt build
make -C extensible_variant build
make -C inline_record build
+ make -C equal_types build
clean:
rm -f *~ *.cm* *.o *.obj
@@ -16,3 +17,4 @@ clean:
make -C gadt clean
make -C extensible_variant clean
make -C inline_record clean
+ make -C equal_types clean
diff --git a/examples/docs/fields_and_constructors/code_constructs/equal_types/Makefile b/examples/docs/fields_and_constructors/code_constructs/equal_types/Makefile
new file mode 100644
index 00000000..1381f40a
--- /dev/null
+++ b/examples/docs/fields_and_constructors/code_constructs/equal_types/Makefile
@@ -0,0 +1,18 @@
+SRC:=definitions.ml \
+ via_explicit_equations.ml \
+ via_hidden_equations.mli via_hidden_equations.ml \
+ via_include.mli via_include.ml \
+ via_module_alias.mli via_module_alias.ml \
+ all_internal.mli all_internal.ml \
+ equal_types.ml
+
+all: build analyze
+
+build:
+ ocamlopt -bin-annot ${SRC}
+
+analyze:
+ ~/proj/dead_code_analyzer/_build/install/default/bin/dead_code_analyzer --nothing -T all .
+
+clean:
+ rm -f *.cm* *.o a.out
diff --git a/examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.ml b/examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.ml
new file mode 100644
index 00000000..bfea1e47
--- /dev/null
+++ b/examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.ml
@@ -0,0 +1,97 @@
+(* all_internal.ml *)
+type original_sum =
+ | Used_by_explicit_equation
+ | Used_by_hidden_equation
+ | Used_by_include
+ | Used_by_module_alias
+ | Used_directly
+ | Used_externally
+ | Unused
+
+type original_product = {
+ used_by_explicit_equation : int;
+ used_by_hidden_equation : int;
+ used_by_include : int;
+ used_by_module_alias : int;
+ used_directly : int;
+ used_externally : int;
+ unused : int;
+}
+
+let _ : original_product -> original_sum =
+ fun {used_directly; _} -> Used_directly
+
+type explicit_eq_sum = original_sum =
+ | Used_by_explicit_equation
+ | Used_by_hidden_equation
+ | Used_by_include
+ | Used_by_module_alias
+ | Used_directly
+ | Used_externally
+ | Unused
+
+type explicit_eq_product = original_product = {
+ used_by_explicit_equation : int;
+ used_by_hidden_equation : int;
+ used_by_include : int;
+ used_by_module_alias : int;
+ used_directly : int;
+ used_externally : int;
+ unused : int;
+}
+
+let _ : explicit_eq_product -> explicit_eq_sum =
+ fun {used_by_explicit_equation; _} -> Used_by_explicit_equation
+
+module M : sig
+ type sum =
+ | Used_by_explicit_equation
+ | Used_by_hidden_equation
+ | Used_by_include
+ | Used_by_module_alias
+ | Used_directly
+ | Used_externally
+ | Unused
+
+ type product = {
+ used_by_explicit_equation : int;
+ used_by_hidden_equation : int;
+ used_by_include : int;
+ used_by_module_alias : int;
+ used_directly : int;
+ used_externally : int;
+ unused : int;
+ }
+end = struct
+ type sum = explicit_eq_sum =
+ | Used_by_explicit_equation
+ | Used_by_hidden_equation
+ | Used_by_include
+ | Used_by_module_alias
+ | Used_directly
+ | Used_externally
+ | Unused
+
+ type product = explicit_eq_product = {
+ used_by_explicit_equation : int;
+ used_by_hidden_equation : int;
+ used_by_include : int;
+ used_by_module_alias : int;
+ used_directly : int;
+ used_externally : int;
+ unused : int;
+ }
+end
+
+let _ : M.product -> M.sum =
+ fun {used_by_hidden_equation; _} -> Used_by_hidden_equation
+
+module Alias = M
+
+let _ : Alias.product -> Alias.sum =
+ fun {used_by_module_alias; _} -> Used_by_module_alias
+
+include Alias
+
+let _ : product -> sum =
+ fun {used_by_include; _} -> Used_by_include
diff --git a/examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli b/examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli
new file mode 100644
index 00000000..0462e540
--- /dev/null
+++ b/examples/docs/fields_and_constructors/code_constructs/equal_types/all_internal.mli
@@ -0,0 +1,19 @@
+(* all_internal.mli *)
+type sum =
+ | Used_by_explicit_equation
+ | Used_by_hidden_equation
+ | Used_by_include
+ | Used_by_module_alias
+ | Used_directly
+ | Used_externally
+ | Unused
+
+type product = {
+ used_by_explicit_equation : int;
+ used_by_hidden_equation : int;
+ used_by_include : int;
+ used_by_module_alias : int;
+ used_directly : int;
+ used_externally : int;
+ unused : int;
+}
diff --git a/examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml b/examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml
new file mode 100644
index 00000000..1ea767d5
--- /dev/null
+++ b/examples/docs/fields_and_constructors/code_constructs/equal_types/definitions.ml
@@ -0,0 +1,17 @@
+(* definitions.ml *)
+type sum =
+ | Used_by_explicit_equation
+ | Used_by_hidden_equation
+ | Used_by_include
+ | Used_by_module_alias
+ | Used_directly
+ | Unused
+
+type product = {
+ used_by_explicit_equation : int;
+ used_by_hidden_equation : int;
+ used_by_include : int;
+ used_by_module_alias : int;
+ used_directly : int;
+ unused : int;
+}
diff --git a/examples/docs/fields_and_constructors/code_constructs/equal_types/equal_types.ml b/examples/docs/fields_and_constructors/code_constructs/equal_types/equal_types.ml
new file mode 100644
index 00000000..f122432b
--- /dev/null
+++ b/examples/docs/fields_and_constructors/code_constructs/equal_types/equal_types.ml
@@ -0,0 +1,24 @@
+(* equal_types.ml *)
+let _ =
+ let open Definitions in
+ fun {used_directly; _} -> Used_directly
+
+let _ =
+ let open Via_explicit_equations in
+ fun {used_by_explicit_equation; _} -> Used_by_explicit_equation
+
+let _ =
+ let open Via_hidden_equations in
+ fun {used_by_hidden_equation; _} -> Used_by_hidden_equation
+
+let _ =
+ let open Via_include in
+ fun {used_by_include; _} -> Used_by_include
+
+let _ =
+ let open Via_module_alias.Alias in
+ fun {used_by_module_alias; _} -> Used_by_module_alias
+
+let _ =
+ let open All_internal in
+ fun {used_externally; _} -> Used_externally
diff --git a/examples/docs/fields_and_constructors/code_constructs/equal_types/via_explicit_equations.ml b/examples/docs/fields_and_constructors/code_constructs/equal_types/via_explicit_equations.ml
new file mode 100644
index 00000000..334f7c30
--- /dev/null
+++ b/examples/docs/fields_and_constructors/code_constructs/equal_types/via_explicit_equations.ml
@@ -0,0 +1,17 @@
+(* via_explicit_equations.ml *)
+type sum = Definitions.sum =
+ | Used_by_explicit_equation
+ | Used_by_hidden_equation
+ | Used_by_include
+ | Used_by_module_alias
+ | Used_directly
+ | Unused
+
+type product = Definitions.product = {
+ used_by_explicit_equation : int;
+ used_by_hidden_equation : int;
+ used_by_include : int;
+ used_by_module_alias : int;
+ used_directly : int;
+ unused : int;
+}
diff --git a/examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.ml b/examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.ml
new file mode 100644
index 00000000..c1c73577
--- /dev/null
+++ b/examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.ml
@@ -0,0 +1,18 @@
+(* via_hidden_equations.ml *)
+type sum = Definitions.sum =
+ | Used_by_explicit_equation
+ | Used_by_hidden_equation
+ | Used_by_include
+ | Used_by_module_alias
+ | Used_directly
+ | Unused
+
+type product = Definitions.product = {
+ used_by_explicit_equation : int;
+ used_by_hidden_equation : int;
+ used_by_include : int;
+ used_by_module_alias : int;
+ used_directly : int;
+ unused : int;
+}
+
diff --git a/examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli b/examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli
new file mode 100644
index 00000000..569db26b
--- /dev/null
+++ b/examples/docs/fields_and_constructors/code_constructs/equal_types/via_hidden_equations.mli
@@ -0,0 +1,18 @@
+(* via_hidden_equations.mli *)
+type sum =
+ | Used_by_explicit_equation
+ | Used_by_hidden_equation
+ | Used_by_include
+ | Used_by_module_alias
+ | Used_directly
+ | Unused
+
+type product = {
+ used_by_explicit_equation : int;
+ used_by_hidden_equation : int;
+ used_by_include : int;
+ used_by_module_alias : int;
+ used_directly : int;
+ unused : int;
+}
+
diff --git a/examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.ml b/examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.ml
new file mode 100644
index 00000000..9d62e576
--- /dev/null
+++ b/examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.ml
@@ -0,0 +1,2 @@
+(* via_include.ml *)
+include Definitions
diff --git a/examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli b/examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli
new file mode 100644
index 00000000..18441a10
--- /dev/null
+++ b/examples/docs/fields_and_constructors/code_constructs/equal_types/via_include.mli
@@ -0,0 +1,18 @@
+(* via_include.mli *)
+type sum =
+ | Used_by_explicit_equation
+ | Used_by_hidden_equation
+ | Used_by_include
+ | Used_by_module_alias
+ | Used_directly
+ | Unused
+
+type product = {
+ used_by_explicit_equation : int;
+ used_by_hidden_equation : int;
+ used_by_include : int;
+ used_by_module_alias : int;
+ used_directly : int;
+ unused : int;
+}
+
diff --git a/examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.ml b/examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.ml
new file mode 100644
index 00000000..46e2bf96
--- /dev/null
+++ b/examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.ml
@@ -0,0 +1,2 @@
+(* via_module_alias.ml *)
+module Alias = Definitions
diff --git a/examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli b/examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli
new file mode 100644
index 00000000..74bab13b
--- /dev/null
+++ b/examples/docs/fields_and_constructors/code_constructs/equal_types/via_module_alias.mli
@@ -0,0 +1,19 @@
+(* via_module_alias.mli *)
+module Alias : sig
+ type sum =
+ | Used_by_explicit_equation
+ | Used_by_hidden_equation
+ | Used_by_include
+ | Used_by_module_alias
+ | Used_directly
+ | Unused
+
+ type product = {
+ used_by_explicit_equation : int;
+ used_by_hidden_equation : int;
+ used_by_include : int;
+ used_by_module_alias : int;
+ used_directly : int;
+ unused : int;
+ }
+end
diff --git a/examples/docs/fields_and_constructors/limitations/Makefile b/examples/docs/fields_and_constructors/limitations/Makefile
new file mode 100644
index 00000000..b3079e6d
--- /dev/null
+++ b/examples/docs/fields_and_constructors/limitations/Makefile
@@ -0,0 +1,8 @@
+.PHONY: clean build
+
+all: build
+
+build: ;
+
+clean:
+ rm -f *~ *.cm* *.o *.obj
diff --git a/src/deadCode.ml b/src/deadCode.ml
index 3b1e7123..63893c0e 100644
--- a/src/deadCode.ml
+++ b/src/deadCode.ml
@@ -105,35 +105,17 @@ let structure_item super self i =
begin match i.str_desc with
| Tstr_type (_, l) when Config.must_report_section sections.types ->
List.iter DeadType.tstr l
- | Tstr_module {mb_name = {txt = Some txt; _}; _} ->
+ | Tstr_module ({mb_name = {txt = Some txt; _}; _} as mb) ->
mods := txt :: !mods;
- DeadMod.defined := String.concat "." (List.rev !mods) :: !DeadMod.defined
+ let modname = State.File_infos.get_modname state.file_infos in
+ let path = !mods @ [modname] in
+ DeadSign.collect_eq_from_module_alias ~path mb
| Tstr_class l when Config.must_report_section sections.methods ->
List.iter DeadObj.tstr l
- | Tstr_include i ->
- let collect_include signature =
- let prev_last_loc = !last_loc in
- let module_id =
- State.File_infos.get_modname state.file_infos
- |> Ident.create_persistent
- in
- let path = [module_id] in
- List.iter
- (DeadSign.collect_export_from_include ~path)
- signature;
- last_loc := prev_last_loc;
- in
- let rec includ mod_expr =
- match mod_expr.mod_desc with
- | Tmod_ident (_, _) -> collect_include (Utils.signature_of_modtype mod_expr.mod_type)
- | Tmod_structure structure -> collect_include structure.str_type
- | Tmod_unpack (_, mod_type) -> collect_include (Utils.signature_of_modtype mod_type)
- | Tmod_functor (_, mod_expr)
- | Tmod_apply (_, mod_expr, _)
- | Tmod_apply_unit mod_expr
- | Tmod_constraint (mod_expr, _, _, _) -> includ mod_expr
- in
- includ i.incl_mod
+ | Tstr_include incl_decl ->
+ let prev_last_loc = !last_loc in
+ DeadSign.collect_from_include incl_decl;
+ last_loc := prev_last_loc
| _ -> ()
end;
let r = super.Tast_mapper.structure_item self i in
@@ -339,7 +321,6 @@ let read_interface fn export_collector state =
in
let module_id =
State.File_infos.get_modname state.file_infos
- |> Ident.create_persistent
in
let path = [module_id] in
export_collector ~path ~comp_unit;
@@ -401,6 +382,7 @@ let eof loc_dep =
end;
VdNode.eof ();
DeadObj.eof ();
+ DeadSign.eof ();
DeadType.dependencies := [];
Hashtbl.reset incl
@@ -678,6 +660,7 @@ try
in
!DeadLexiFi.prepare_report DeadType.decs;
+ DeadType.prepare_report ();
let sections = state.config.sections in
if Config.must_report_section sections.exported_values then report_unused_exported ();
DeadObj.report();
diff --git a/src/deadCommon.ml b/src/deadCommon.ml
index 7a341895..01b16d46 100644
--- a/src/deadCommon.ml
+++ b/src/deadCommon.ml
@@ -384,9 +384,9 @@ end
let export ?(sep = ".") path u stock id loc =
let value =
- String.concat "." (List.rev_map Ident.name path)
+ String.concat "." (List.rev path)
^ sep
- ^ (Ident.name id)
+ ^ id
in
(* a .cmti file can contain locations from other files.
For instance:
@@ -395,7 +395,7 @@ let export ?(sep = ".") path u stock id loc =
*)
if not loc.Location.loc_ghost
&& (u = Utils.Filepath.unit loc.Location.loc_start.Lexing.pos_fname || u == _include)
- && check_underscore (Ident.name id) then
+ && check_underscore id then
let state = State.get_current () in
let builddir = State.File_infos.get_builddir state.file_infos in
hashtbl_add_to_list stock loc.Location.loc_start (builddir, value)
diff --git a/src/deadMod.ml b/src/deadMod.ml
index d5d4fd01..79c1b73c 100644
--- a/src/deadMod.ml
+++ b/src/deadMod.ml
@@ -14,8 +14,6 @@ open DeadCommon
-let defined : string list ref = ref []
-
let item maker = function
| Sig_value (id, {val_loc = {Location.loc_start= loc; _}; _}, _) ->
(Ident.name id, loc)::[]
diff --git a/src/deadObj.ml b/src/deadObj.ml
index d4f70e84..dff43646 100644
--- a/src/deadObj.ml
+++ b/src/deadObj.ml
@@ -174,7 +174,7 @@ let collect_export path u stock ~obj ~cltyp loc =
let pos = loc.Location.loc_start in
- begin match List.rev_map (fun id -> Ident.name id) path with
+ begin match List.rev path with
| h :: t ->
let short = String.concat "." t in
let path = h ^ "." ^ short in
@@ -198,7 +198,7 @@ let collect_export path u stock ~obj ~cltyp loc =
compiled projects. Without it, looking up for an existing csml below will
always fail and can lead to false positive *)
if not (Sys.file_exists (Filename.remove_extension sourcepath ^ ".csml")) then
- export ~sep:"#" path u stock (Ident.create_persistent id) loc;
+ export ~sep:"#" path u stock id loc;
in
let typ = match cltyp with
diff --git a/src/deadObj.mli b/src/deadObj.mli
index b5ce97a3..839e52ff 100644
--- a/src/deadObj.mli
+++ b/src/deadObj.mli
@@ -16,7 +16,7 @@ val last_class : Lexing.position ref
val collect_export :
- Ident.t list
+ string list
-> string
-> (Lexing.position, string * string) Hashtbl.t
-> ?obj: Types.type_expr
diff --git a/src/deadSign.ml b/src/deadSign.ml
index 0f5a5d42..b920b19c 100644
--- a/src/deadSign.ml
+++ b/src/deadSign.ml
@@ -1,9 +1,21 @@
+let exported_modules = Hashtbl.create 32
+(* types of modules exported by the current compilation unit.
+ This is reset when calling eof.
+ This is used when encountering a module alias in the .cmt because
+ the module type information is limited to Mty_alias there. *)
+
+let eof () =
+ Hashtbl.reset exported_modules
+
(* Export helpers *)
+let export_module ~path mt =
+ Hashtbl.add exported_modules path mt
+
let export_object ~path ~comp_unit ~stock id value =
(* export a value as an object *)
- let path = Ident.create_persistent (Ident.name id ^ "*") :: path in
+ let path = (id ^ "*") :: path in
let obj = value.Types.val_type in
let loc = value.Types.val_loc in
DeadObj.collect_export path comp_unit stock ~obj loc
@@ -83,13 +95,15 @@ let collect_export_from_signature ~path ~comp_unit signature =
| Tsig_value {val_id; val_loc; val_val; _}
when not val_loc.Location.loc_ghost ->
let stock = DeadCommon.decs in
- export_value ~path ~comp_unit ~stock val_id val_val
+ let id = Ident.name val_id in
+ export_value ~path ~comp_unit ~stock id val_val
| Tsig_type (_, type_decls)->
let stock = DeadCommon.decs in
List.iter
(fun Typedtree.{typ_id; typ_type; _} ->
- export_type ~path ~comp_unit ~stock typ_id typ_type
+ let id = Ident.name typ_id in
+ export_type ~path ~comp_unit ~stock id typ_type
)
type_decls
@@ -97,12 +111,15 @@ let collect_export_from_signature ~path ~comp_unit signature =
let stock = DeadCommon.decs in
List.iter
(fun {Typedtree.ci_id_class; ci_decl; _} ->
- export_class ~path ~comp_unit ~stock ci_id_class ci_decl
+ let id = Ident.name ci_id_class in
+ export_class ~path ~comp_unit ~stock id ci_decl
)
class_descs
| Tsig_module {md_id = Some id; md_type; _} ->
+ let id = Ident.name id in
let path = id :: path in
+ export_module ~path md_type.mty_type;
Utils.typedtree_signature_of_modtype md_type
|> Option.iter (collect_signature path);
mark_modtype_elements md_type
@@ -118,9 +135,7 @@ let collect_export_from_signature ~path ~comp_unit signature =
let collect_export_from_structure ~path ~comp_unit structure =
let met = Hashtbl.create 64 in
let export export_fn ~path id param =
- let path_key =
- id :: path |> List.map Ident.name
- in
+ let path_key = id :: path in
match Hashtbl.find_opt met path_key with
| Some _ -> () (* the current path is shadowed *)
| None ->
@@ -156,9 +171,11 @@ let collect_export_from_structure ~path ~comp_unit structure =
| Tpat_variant (_, None, _) ->
()
| Tpat_var (id, {loc; _}, uid) ->
+ let id = Ident.name id in
let value = value_of pat loc uid in
export export_value ~path id value
| Tpat_alias (sub_pat, id, {loc; _}, uid) ->
+ let id = Ident.name id in
let value = value_of pat loc uid in
export export_value ~path id value;
collect_value ~path sub_pat
@@ -182,6 +199,7 @@ let collect_export_from_structure ~path ~comp_unit structure =
| Tmod_unpack _ ->
()
| Tmod_structure structure ->
+ export_module ~path m.mod_type;
collect_structure ~path structure
| Tmod_functor (_, m)
| Tmod_apply (m, _, _)
@@ -189,12 +207,13 @@ let collect_export_from_structure ~path ~comp_unit structure =
| Tmod_constraint (m, _, Tmodtype_implicit, _) ->
collect_module ~path m
| Tmod_constraint (_, _, Tmodtype_explicit mt, _) ->
+ export_module ~path mt.mty_type;
Utils.typedtree_signature_of_modtype mt
|> Option.iter (collect_export_from_signature ~path ~comp_unit)
and collect_module_binding ~path = function
| {mb_id = Some id; mb_expr; _} ->
- let path = id::path in
+ let path = Ident.name id :: path in
collect_module ~path mb_expr
| _ -> ()
@@ -204,7 +223,8 @@ let collect_export_from_structure ~path ~comp_unit structure =
| Tcl_ident _ ->
()
| Tcl_structure _ ->
- export export_class ~path c.ci_id_class c.ci_decl
+ let id = Ident.name c.ci_id_class in
+ export export_class ~path id c.ci_decl
| Tcl_fun (_, _, _, ce, _)
| Tcl_apply (ce, _)
| Tcl_let (_, _, _, ce)
@@ -219,11 +239,13 @@ let collect_export_from_structure ~path ~comp_unit structure =
| Tstr_value (_, bindings) ->
List.iter (fun {vb_pat; _} -> collect_value ~path vb_pat) bindings
| Tstr_primitive {val_id; val_val; _} ->
- export export_value ~path val_id val_val
+ let id = Ident.name val_id in
+ export export_value ~path id val_val
| Tstr_type (_, type_decls)->
List.iter
(fun {typ_id; typ_type; _} ->
- export export_type ~path typ_id typ_type
+ let id = Ident.name typ_id in
+ export export_type ~path id typ_type
)
type_decls
| Tstr_module module_binding ->
@@ -240,20 +262,151 @@ let collect_export_from_structure ~path ~comp_unit structure =
-let rec collect_export_from_include ~path sig_item =
+
+let collect_from_include incl_decl =
+ (* Get incl_decl's signature to export classes and objects in DeadCommon.incl.
+ If the incl_decl is an ident, then store the equivalence between its
+ types and those of the current compilation unit.
+ *)
+ let rec get_mod_path_and_signature mod_expr =
+ match mod_expr.Typedtree.mod_desc with
+ | Tmod_ident (mod_path, _) ->
+ let mt =
+ match mod_expr.mod_type with
+ | Mty_alias _ as mt ->
+ (* find the original signature in the env *)
+ begin try
+ let env = Utils.Envaux.load_env mod_expr.mod_env in
+ Env.scrape_alias env mt
+ with Envaux.(Error (Module_not_found _)) -> mt
+ end
+ | mt -> mt
+ in
+ let signature = Utils.signature_of_modtype mt in
+ (Some mod_path, signature)
+ | Tmod_structure structure ->
+ (None, structure.str_type)
+ | Tmod_unpack (_, mod_type) ->
+ let signature = Utils.signature_of_modtype mod_type in
+ (None, signature)
+ | Tmod_functor (_, mod_expr)
+ | Tmod_apply (mod_expr, _, _)
+ | Tmod_apply_unit mod_expr
+ | Tmod_constraint (mod_expr, _, _, _) ->
+ get_mod_path_and_signature mod_expr
+ in
+ (* incl_path is used to identify and store type equivalences *)
+ let incl_path, signature =
+ get_mod_path_and_signature incl_decl.Typedtree.incl_mod
+ in
+ (* path to the module where include happens *)
+ let current_path =
+ let state = State.get_current () in
+ let module_id = State.File_infos.get_modname state.file_infos in
+ !DeadCommon.mods @ [module_id]
+ in
+ (* comp_unit = DeadCommon._include enables exports from outside the current
+ compilation unit *)
let comp_unit = DeadCommon._include in
+ (* exports from include are stored in their dedicated stock*)
let stock = DeadCommon.incl in
- match (sig_item : Types.signature_item) with
- | Sig_value (id, ({val_loc; _} as value), _)
- when not val_loc.Location.loc_ghost ->
- export_object ~path ~comp_unit ~stock id value
- | Sig_class (id, cd, _, _) ->
- export_class ~path ~comp_unit ~stock id cd
- | Sig_module (id, _, {Types.md_type; _}, _, _) ->
- let path = id :: path in
- Utils.signature_of_modtype md_type
- |> List.iter (collect_export_from_include ~path)
- | _ -> ()
+ let rec collect_from_sig_item ~path sig_item =
+ (* [path] is the path within the included module *)
+ match (sig_item : Types.signature_item) with
+ | Sig_value (id, ({val_loc; _} as value), _)
+ when not val_loc.Location.loc_ghost ->
+ let path = path @ current_path in
+ let id = Ident.name id in
+ export_object ~path ~comp_unit ~stock id value
+ | Sig_class (id, cd, _, _) ->
+ let path = path @ current_path in
+ let id = Ident.name id in
+ export_class ~path ~comp_unit ~stock id cd
+ | Sig_module (id, _, {Types.md_type; _}, _, _) ->
+ let path = Ident.name id :: path in
+ Utils.signature_of_modtype md_type
+ |> List.iter (collect_from_sig_item ~path)
+ | Sig_type (id, t, _, _) ->
+ let id = Ident.name id in
+ Option.iter
+ (fun incl_path ->
+ let path = List.rev (id :: path) in
+ DeadType.collect_eq_from_include ~incl_path ~path t)
+ incl_path
+ | _ -> ()
+ in
+ List.iter (collect_from_sig_item ~path:[]) signature
+
+let collect_from_include inc_decl =
+ (* Only collect_from_include if methods or types section is enabled *)
+ let state = State.get_current () in
+ let sections =
+ [state.config.sections.types; state.config.sections.methods]
+ in
+ if List.exists Config.must_report_section sections then
+ collect_from_include inc_decl
+
+let collect_eq_from_module_alias ~path module_binding =
+ let state = State.get_current () in
+ match (module_binding : Typedtree.module_binding) with
+ | {mb_id = None; _} -> ()
+ | _ when not (Config.must_report_section state.config.sections.types) -> ()
+ | {mb_id = Some _; mb_expr; _} ->
+ let rev_alias_path = path in
+ let rec collect_from_sig_item ~original_path ?(sub_path=[]) sig_item =
+ match (sig_item : Types.signature_item) with
+
+ | Sig_module (id, _, {Types.md_type; _}, _, _) ->
+ let sub_path = Ident.name id :: sub_path in
+ Utils.signature_of_modtype md_type
+ |> List.iter
+ (collect_from_sig_item ~original_path ~sub_path)
+
+ | Sig_type (id, t, _, _) ->
+ let sub_path = List.rev (Ident.name id :: sub_path) in
+ DeadType.collect_eq_from_module_alias
+ ~rev_alias_path
+ ~original_path
+ ~sub_path
+ t
+
+ | _ -> ()
+ in
+ let rec collect_from_module_expr mod_expr =
+ match mod_expr.Typedtree.mod_desc with
+ | Tmod_ident (mod_path, _) ->
+ let mt =
+ let exported_mt =
+ Hashtbl.find_opt exported_modules rev_alias_path
+ in
+ match exported_mt with
+ | Some _ as mt -> mt
+ | None ->
+ try
+ let env = Utils.Envaux.load_env mod_expr.mod_env in
+ let md = Env.find_module mod_path env in
+ Some md.Types.md_type
+ with
+ | Envaux.(Error (Module_not_found _))
+ | Not_found -> None
+ in
+ let original_path = mod_path in
+ Option.iter
+ (fun mt ->
+ Utils.signature_of_modtype mt
+ |> List.iter
+ (collect_from_sig_item ~original_path)
+ )
+ mt
+ | Tmod_constraint (mod_expr, _, _, _)
+ | Tmod_functor (_, mod_expr)
+ | Tmod_apply (mod_expr, _, _)
+ | Tmod_apply_unit mod_expr ->
+ collect_from_module_expr mod_expr
+ | Tmod_structure _
+ | Tmod_unpack (_, _) -> ()
+ in
+ collect_from_module_expr mb_expr
let correct_export sig_item =
diff --git a/src/deadSign.mli b/src/deadSign.mli
index 5ba44d6f..7e88ade6 100644
--- a/src/deadSign.mli
+++ b/src/deadSign.mli
@@ -1,6 +1,6 @@
val collect_export_from_signature :
- path:Ident.t list ->
- comp_unit:string ->
+ path: string list ->
+ comp_unit: string ->
Typedtree.signature
-> unit
(** [collect_export_from_signature ~path ~comp_unit signature]
@@ -12,8 +12,8 @@ val collect_export_from_signature :
*)
val collect_export_from_structure :
- path:Ident.t list ->
- comp_unit:string ->
+ path: string list ->
+ comp_unit: string ->
Typedtree.structure
-> unit
(** [collect_export_from_structure ~path ~comp_unit structure]
@@ -22,14 +22,23 @@ val collect_export_from_structure :
See {!collect_export_from_signature} above for more information.
*)
-val collect_export_from_include :
- path:Ident.t list ->
- Types.signature_item
+val collect_from_include :
+ Typedtree.include_declaration
-> unit
-(** [collect_export_from_include ~path sig_item]
- recursively traverse the [sig_item] and store exported elements
- (values, constructors, ...) in {!DeadCommon.incl}.
- See {!collect_export_from_signature} above for more information.
+(** [collect_from_include incl_decl]
+ store exported object, class elements and type equivalences found in
+ [incl_decl].
+*)
+
+val collect_eq_from_module_alias :
+ path: string list ->
+ Typedtree.module_binding
+ -> unit
+(** [collect_eq_from_module_alias ~path module_binding]
+ links exports in the [module_binding]'s expr to the new module if it is
+ an alias (M1 = M2). Aliases on functor applications are included.
+ [path] is the current module id's path (with the new id included) in
+ reverse order (i.e. the new id is at the head).
*)
val correct_export : Types.signature_item -> unit
@@ -47,3 +56,6 @@ val modtype :
for [mt] agree. I.e. if the first one is explicit when the second one is.
If not, then the first one is implicit and [on_mismatch] is called on the
second. *)
+
+val eof : unit -> unit
+(** To use at the end of a [.cmt]'s analysis: reset internal state *)
diff --git a/src/deadType.ml b/src/deadType.ml
index 668324a0..44230b17 100644
--- a/src/deadType.ml
+++ b/src/deadType.ml
@@ -21,6 +21,8 @@ let decs = Hashtbl.create 256
let dependencies = ref [] (* like the cmt value_dependencies but for types *)
+let equivalences = ref [] (* t1 = t2 *)
+
(******** HELPERS ********)
@@ -76,9 +78,11 @@ let collect_export path u stock t =
in
let save id loc =
+ let id = Ident.name id in
if t.type_manifest = None then
+ (* do not export t1 when there is an explicit equation t1 = t2 *)
export path u stock id loc;
- let path = String.concat "." @@ List.rev_map (fun id -> Ident.name id) (id::path) in
+ let path = String.concat "." @@ List.rev (id::path) in
Hashtbl.replace fields path loc.Location.loc_start
in
@@ -130,37 +134,179 @@ let rec check_style t loc =
| _ -> ()
+let add_type_eq component_path eq_type_path component_name =
+ (* Store t1 = t2 equivalence, with t2 assumed to be defined outside the
+ current compilation unit *)
+ let eq_path = eq_type_path ^ "." ^ component_name in
+ equivalences := (component_path, eq_path) :: !equivalences
+
+
+let rec add_type_eq_internal ~internal_path ~component_path ~component_name =
+ match internal_path with
+ | [] -> assert false (* There must be at least one element *)
+ | external_type_path :: [] -> (* external alias *)
+ add_type_eq component_path external_type_path component_name
+ | external_type_path :: rev_internal_path ->
+ let eq_type_path =
+ List.rev internal_path |> String.concat "."
+ in
+ let eq_component_path = eq_type_path ^ "." ^ component_name in
+ match Hashtbl.find_opt fields eq_component_path with
+ | Some _ -> (* internal alias *)
+ add_type_eq component_path eq_type_path component_name
+ | None ->
+ let internal_path =
+ match rev_internal_path with
+ | [] | _::[] -> external_type_path :: []
+ | _::rev_internal_path -> external_type_path :: rev_internal_path
+ in
+ add_type_eq_internal ~internal_path ~component_path ~component_name
+
+
+let normalize_mod_path rev_current_path mod_path =
+ let sub_path = Path.name mod_path in
+ if Path.head mod_path |> Ident.global then
+ (* External mod_path. Keep as is *)
+ sub_path :: []
+ else
+ (* Internal mod_path *)
+ sub_path :: rev_current_path
+
+
+let collect_eq_from_module_alias
+ ~rev_alias_path ~original_path ~sub_path type_decl
+=
+ let type_path =
+ List.rev_append rev_alias_path sub_path
+ |> String.concat "."
+ in
+ let internal_path =
+ let normalized_original_path =
+ normalize_mod_path (List.tl rev_alias_path) original_path
+ in
+ match normalized_original_path with
+ | original_path :: rev_path ->
+ let original_type_path = String.concat "." (original_path :: sub_path) in
+ original_type_path :: rev_path
+ | [] -> assert false
+ in
+ let add_type_eq loc component_id =
+ let component_name = Ident.name component_id in
+ let component_path = type_path ^ "." ^ component_name in
+ if not (Hashtbl.mem fields component_path) then
+ Hashtbl.add fields component_path loc.Location.loc_start;
+ add_type_eq_internal ~internal_path ~component_path ~component_name
+ in
+ match type_decl.type_kind with
+ | Type_record (l, _) ->
+ List.iter (fun {Types.ld_id; ld_loc; _} -> add_type_eq ld_loc ld_id) l
+ | Type_variant (l, _) ->
+ List.iter (fun {Types.cd_id; cd_loc; _} -> add_type_eq cd_loc cd_id) l
+ | _ -> ()
+
+
+let collect_eq_from_include ~incl_path ~path type_decl =
+ let state = State.get_current () in
+ let module_id = State.File_infos.get_modname state.file_infos in
+ (* internal path *)
+ let rev_curr_path = !DeadCommon.mods @ [module_id] in
+ let type_path =
+ List.rev_append rev_curr_path path
+ |> String.concat "."
+ in
+ let internal_path =
+ let normalized_incl_path =
+ normalize_mod_path rev_curr_path incl_path
+ in
+ match normalized_incl_path with
+ | incl_path :: rev_path ->
+ let incl_type_path = String.concat "." (incl_path :: path) in
+ incl_type_path :: rev_path
+ | [] -> assert false
+ in
+ let add_type_eq component_id =
+ let component_name = Ident.name component_id in
+ (* internal path *)
+ let component_path = type_path ^ "." ^ component_name in
+ add_type_eq_internal ~internal_path ~component_path ~component_name
+ in
+ match type_decl.type_kind with
+ | Type_record (l, _) ->
+ List.iter (fun {Types.ld_id; _} -> add_type_eq ld_id) l
+ | Type_variant (l, _) ->
+ List.iter (fun {Types.cd_id; _} -> add_type_eq cd_id) l
+ | _ -> ()
+
let tstr typ =
let state = State.get_current() in
let modname = State.File_infos.get_modname state.file_infos in
+
+ (* A type equation [type t1 = t2 = ...] produces a
+ [typ_manifest = Some (Ttyp_constr t2)] in t1
+ In this situation, we want to remember the equality between t1 and t2's
+ components, for later resolution of equivalence classes and merging
+ all their references (see {!prepare_report} below).
+ *)
+ let eq_type_path =
+ match typ.typ_manifest with
+ | Some {ctyp_desc=Ttyp_constr (_, {txt; _}, _); _} ->
+ let path = String.concat "." (Longident.flatten txt) in
+ Some path
+ | _ -> None
+ in
+
+ let handle_external_type_eq : string -> string -> unit =
+ match eq_type_path with
+ | None -> fun _ _ -> ()
+ | Some eq_type_path ->
+ fun component_path component_name ->
+ add_type_eq component_path eq_type_path component_name
+ in
+
+ let handle_internal_type_eq : string -> string -> unit =
+ (* Store t1 = t2 equivalence as a dependency, with t2 defined within the
+ current compilation unit *)
+ match eq_type_path with
+ | None -> fun _ _ -> ()
+ | Some eq_type_path ->
+ fun component_path component_name ->
+ let eq_type_path = String.concat "." [modname; eq_type_path] in
+ let eq_component_path =
+ String.concat "." [eq_type_path; component_name]
+ in
+ match Hashtbl.find_opt fields eq_component_path with
+ | None -> () (* t2 is not defined locally *)
+ | Some _ ->
+ add_type_eq component_path eq_type_path component_name
+ in
+
+ let handle_type_dep loc path_loc component_path component_name =
+ handle_internal_type_eq component_path component_name;
+ if path_loc <> loc then
+ (* store dependency between .ml and .mli *)
+ dependencies := (path_loc, loc) :: !dependencies
+ in
+
let assoc name loc =
+ (* store the association from name to loc in fields,
+ the dependenicies and the equivalences *)
+ let component_name = name.Asttypes.txt in
let path =
let partial_path_rev =
- name.Asttypes.txt :: typ.typ_name.Asttypes.txt :: !mods
+ component_name :: typ.typ_name.Asttypes.txt :: !mods
in
modname :: List.rev partial_path_rev
|> String.concat "."
in
+ handle_external_type_eq path component_name;
match Hashtbl.find_opt fields path with
- | None -> Hashtbl.add fields path loc
+ | None ->
+ Hashtbl.add fields path loc;
+ handle_internal_type_eq path component_name
| Some path_loc ->
- (match typ.typ_manifest with
- (* TODO : describe what this pattern is for *)
- | Some {ctyp_desc=Ttyp_constr (_, {txt; _}, _); _} ->
- let constr_typ_path =
- modname :: Longident.flatten txt @ (name.Asttypes.txt :: [])
- |> String.concat "."
- in
- (match Hashtbl.find_opt fields constr_typ_path with
- | None -> ()
- | Some constr_loc ->
- dependencies := (path_loc, constr_loc) :: (constr_loc, loc) :: !dependencies
- )
- | _ -> ()
- );
- dependencies := (path_loc, loc) :: !dependencies
+ (* The path is known because the current compilation unit exports it *)
+ handle_type_dep loc path_loc path component_name
in
-
let assoc name loc ctyp =
assoc name loc;
!DeadLexiFi.tstr_type typ ctyp
@@ -180,6 +326,67 @@ let tstr typ =
| _ -> ()
+let prepare_report () =
+ (* implement a pseudo union-find via 2 tables : references and reprs *)
+ (* references hold merged references of a union class with the
+ representative as key.*)
+ let references = LocHash.create 128 in
+ (* reprs points to another member of the location's equivalence class.
+ This memeber was the representative at some point. There are no
+ circular references.
+ _The_ representative of a class points to itself.
+ Use get_repr to get _the_ representative of a location's class.
+ *)
+ let reprs = Hashtbl.create 128 in
+ let init_refs loc =
+ (* the initial value for a single-element class is the set of references
+ gathered during the analysis *)
+ LocHash.find_set DeadCommon.references loc
+ |> LocHash.replace references loc
+ in
+ let rec get_repr loc =
+ (* explore members of loc's class until finding the class representative *)
+ match Hashtbl.find_opt reprs loc with
+ | None ->
+ (* loc does not belong to a class yet. Setup its own *)
+ init_refs loc;
+ Hashtbl.add reprs loc loc;
+ loc
+ | Some repr when repr = loc -> loc (* class representative found *)
+ | Some repr -> get_repr repr (* class member but not the representative *)
+ in
+ let merge_references (path1, path2) =
+ let loc1 = Hashtbl.find_opt fields path1 in
+ let loc2 = Hashtbl.find_opt fields path2 in
+ match loc1, loc2 with
+ | None, _ | _, None -> ()
+ | Some loc1, Some loc2 ->
+ let repr1 = get_repr loc1 in
+ let repr2 = get_repr loc2 in
+ Hashtbl.replace reprs repr1 repr2;
+ (* repr1 is now represented by repr2: its references are transfered *)
+ LocHash.merge_set references repr2 references repr1;
+ LocHash.remove references repr1
+ in
+ let update_references loc =
+ Option.iter
+ (fun loc ->
+ let repr = get_repr loc in
+ let refs = LocHash.find_set references repr in
+ (* refs include the references gathered for loc and all the members
+ of its equivalence class *)
+ LocHash.replace DeadCommon.references loc refs
+ )
+ loc
+ in
+ let update_references (path1, path2) =
+ Hashtbl.find_opt fields path1 |> update_references;
+ Hashtbl.find_opt fields path2 |> update_references
+ in
+ List.iter merge_references !equivalences;
+ List.iter update_references !equivalences
+
+
let report () =
let state = State.get_current () in
report_basic
@@ -198,4 +405,17 @@ let wrap f x =
let collect_export path u stock t = wrap (collect_export path u stock) t
let tstr typ = wrap tstr typ
+let prepare_report () = wrap prepare_report ()
let report () = wrap report ()
+
+let collect_eq_from_module_alias
+ ~rev_alias_path ~original_path ~sub_path type_decl
+=
+ wrap
+ (collect_eq_from_module_alias
+ ~rev_alias_path ~original_path ~sub_path
+ )
+ type_decl
+
+let collect_eq_from_include ~incl_path ~path type_decl =
+ wrap (collect_eq_from_include ~incl_path ~path) type_decl
diff --git a/src/deadType.mli b/src/deadType.mli
index a7280fe6..fc4693f9 100644
--- a/src/deadType.mli
+++ b/src/deadType.mli
@@ -17,6 +17,45 @@ val to_string : Types.type_expr -> string
val check_style : Types.type_expr -> Lexing.position -> unit
(** Look for bad style typing. (i.e. Argument expecting an optional argument) *)
+val collect_eq_from_module_alias :
+ rev_alias_path: string list ->
+ original_path: Path.t ->
+ sub_path: string list ->
+ Types.type_declaration
+ -> unit
+(** [collect_eq_from_module_alias ~rev_alias_path ~original_path ~sub_path type_decl]
+ stores equivalences between components of the [type_decl] defined in
+ the current compilation unit at [sub_path] in the module defined at
+ [List.rev rev_alias_path], and the same components in the same type
+ at [sub_path] in the aliased module at [original_path].
+ [rev_alias_path] must not be empty and is represented backward
+ (i.e. the type name is at the head).
+ If [is_internal = true] then there is an attempt to find the aliased
+ definition in the current compilation unit (working upward from the
+ alias path). If not found, then the behavior is the same as if
+ [is_internal = false]: the aliased type is conidered external.
+
+ E.g. the .mli declares [module M : sig type t = (* type_decl *) end]
+ and the .ml [module M = N] and [N] also declares
+ [type t = (* type_decl *)]
+*)
+
+val collect_eq_from_include :
+ incl_path: Path.t ->
+ path: string list ->
+ Types.type_declaration
+ -> unit
+(** [collect_eq_from_include ~incl_path ~path type_decl]
+ stores equivalences between components of the included [type_decl]
+ defined in [incl_path] at [path] and the same components in the same
+ type at [path_at_include @ path] in the current compilation unit if it is exported.
+ [path] must not be empty and is represented forward (i.e. the type name
+ is at the end).
+
+ E.g. the .mli declares [type t = (* type_decl *)] and the .ml
+ [include M] and [M] also declares [type t = (* type_decl *)]
+*)
+
val tstr : Typedtree.type_declaration -> unit
val is_unit : Types.type_expr -> bool
@@ -26,7 +65,7 @@ val nb_args : keep:[> `All | `Lbl | `Opt | `Reg ] -> Types.type_expr -> int
val is_type : string -> bool
val collect_export :
- Ident.t list
+ string list
-> string
-> (Lexing.position, string * string) Hashtbl.t
-> Types.type_declaration
@@ -38,4 +77,9 @@ val correct_export : Types.type_declaration -> unit
val collect_references :
Lexing.position -> Lexing.position -> unit
+val prepare_report: unit -> unit
+(** To use at the end of the codebase analysis, before reporting.
+ This merges all the references of equivalent types
+*)
+
val report: unit -> unit
diff --git a/src/state/file_infos.ml b/src/state/file_infos.ml
index 76e03867..f8ea0bc7 100644
--- a/src/state/file_infos.ml
+++ b/src/state/file_infos.ml
@@ -51,6 +51,7 @@ let init_from_all_cm_infos ~cm_file cmt_infos =
Cmt {strc; sign = None; location_dependencies}
| _ -> Neither
in
+ Utils.Envaux.set_loadpaths cmt_infos.cmt_loadpath;
{builddir; cm_file; cm_infos; modname; sourcepath}
(** [init_from_cm_file cm_file] returns an [Ok t] with [t] filled with general
diff --git a/src/utils.ml b/src/utils.ml
index c52d78ec..8c9b037d 100644
--- a/src/utils.ml
+++ b/src/utils.ml
@@ -52,3 +52,26 @@ let rec typedtree_signature_of_modtype ?(select_param = false) modtype =
| _ -> None
module StringSet = Set.Make(String)
+
+module Envaux = struct
+ (* Lazy set up of loadpaths for load_env.
+ This is used to ensure the setup is applied only once and only if
+ necessary
+ *)
+ let setup = ref (Lazy.from_val ())
+ let force_setup () = Lazy.force !setup
+
+ let set_loadpaths (paths : Load_path.paths) =
+ let reset () =
+ let auto_include = Load_path.no_auto_include in
+ let visible = paths.visible in
+ let hidden = paths.hidden in
+ Load_path.init ~auto_include ~visible ~hidden;
+ Envaux.reset_cache ()
+ in
+ setup := Lazy.from_fun reset
+
+ let load_env env =
+ force_setup ();
+ Envaux.env_of_only_summary env
+end
diff --git a/src/utils.mli b/src/utils.mli
index 2900bd84..12994a43 100644
--- a/src/utils.mli
+++ b/src/utils.mli
@@ -40,3 +40,13 @@ val typedtree_signature_of_modtype :
*)
module StringSet : Set.S with type elt = String.t
+
+module Envaux : sig
+ val set_loadpaths : Load_path.paths -> unit
+ (** Reset the load_path to the [paths]. Also calls Envaux.reset_cache.
+ To call when loading a new .cmt *)
+
+ val load_env : Env.t -> Env.t
+ (** Same as Envaux.env_of_only_summary but ensures the paths submitted
+ in set_loadpaths are actually set. *)
+end