From bba00121a45a2075455f7e5579d57922d84023b5 Mon Sep 17 00:00:00 2001 From: Corentin De Souza <9597216+fantazio@users.noreply.github.com> Date: Mon, 9 Feb 2026 19:25:36 +0100 Subject: [PATCH 01/16] [docs][exported_values][1/n] initial documentation Provide definitions and basic usage information about exported unused values. Provide very basic examples without any external use, with and without .mli. The examples are walkthrough of the usage of the analyzer on actual code. --- docs/USER_DOC.md | 1 + docs/exported_values/EXPORTED_VALUES.md | 351 ++++++++++++++++++++ docs/exported_values/hello_world.ml | 9 + docs/exported_values/hello_world.mli | 4 + docs/exported_values/hello_world_no_intf.ml | 9 + 5 files changed, 374 insertions(+) create mode 100644 docs/exported_values/EXPORTED_VALUES.md create mode 100644 docs/exported_values/hello_world.ml create mode 100644 docs/exported_values/hello_world.mli create mode 100644 docs/exported_values/hello_world_no_intf.ml diff --git a/docs/USER_DOC.md b/docs/USER_DOC.md index 48c03ae0..7869813b 100644 --- a/docs/USER_DOC.md +++ b/docs/USER_DOC.md @@ -53,6 +53,7 @@ not related to the _use_ of elements of code. This documentation is split accross different topics: - [Usage](USAGE.md) describes the usage of the `dead_code_analyzer` and its options. +- [Exported Values](exported_values/EXPORTED_VALUES.md) ## Footnotes diff --git a/docs/exported_values/EXPORTED_VALUES.md b/docs/exported_values/EXPORTED_VALUES.md new file mode 100644 index 00000000..0c0bd879 --- /dev/null +++ b/docs/exported_values/EXPORTED_VALUES.md @@ -0,0 +1,351 @@ +# Table of contents + ++ [Exported Values](#exported-values) + + [Definitions](#definitions) + + [Usage](#usage) ++ [Examples](#examples) + + [Single compilation unit without interface](#single-compilation-unit-without-interface) + + [Single compilation unit with interface](#single-compilation-unit-with-interface) + +# Exported Values + +## Definitions + +A **value** is the result of an expression. For the analyzer, we'll restrict +this definition to a _named_ value, i.e. a name bound to an expression. +In general, this is the `lhs` of `let lhs = ...` or `val lhs : ...`. + +An **exported** value is one that is accessible outside its compilation unit. +I.e. a value that can be referenced in other `.ml` and/or `.mli` files than +the ones that declare it. + +> [!NOTE] +> The _exported_ attribute is important here. +> The compiler already reports unused _unexported_ values (warning 32 +> `unused-value-declaration`) and unused _local_ +> values (warning 26 `unused-var`) [^unused-var-strict]. Because the compiler +> already reports these 2 categories of values, the `dead_code_analyzer` +> complements its work by focusing on the 3rd category. + +[^unused-var-strict]: The compiler also has the warning 27 `unused-var-strict` +for unused values not bound by `let` or `as` (e.g. function parameters or values +in patterns). These are out of context for the analyzer. + +A **use** is either : +- An explicit reference. + E.g. + ```OCaml + let answer = 42 + let () = print_int answer + ``` + The value `answer` is explicitly referenced in `print_int answer`. +- A requirement for that value to exist. + E.g. + ```OCaml + module M = struct + let answer = 42 + end + + module M2 : sig val answer : int end = M + ``` + There are 2 values here : `M.answer`, and `M2.answer`. + Neither of them is explictly referenced. However, `M.answer` is used by + requirement in `M2 : ... = M`. I.e. it needs to be provided by `M` to fulfill + `M2`'s signature. Without it the compilation would fail with an error like : + ``` + File "requirement.ml", line 4, characters 39-40: + 4 | module M2 : sig val answer : int end = M + ^ + Error: Signature mismatch: + Modules do not match: + sig end + is not included in + sig val answer : int end + The value answer is required but not provided + File "requirement.ml", line 4, characters 16-32: Expected declaration + ``` + +## Usage + +Unused exported values are reported by default. +Their reports can be deactivated by using the `--nothing` or `-E nothing` +command line arguments. +They can be reactivated by using the `--all` or `-E all` command line arguments. +For more detail on the command line arguments see [the more general Usage +documentation](../USAGE.md). + +The report section title is `.> UNUSED EXPORTED VALUES:`. + +The expected resolution for an unused exported value is to remove it from the +`.mli` if there is one, or the `.ml` otherwise. + +> [!IMPORTANT] +> Removing unused values from the codebase (reported by either the compiler or +> the analyzer) may trigger the detection of new unused values for both the +> compiler and the analyzer. Consequently, it is expected that a user might need +> to compile and analyze their code multiple times when cleaning up their +> codebase. + +In order to provide actionable reports, the analyzer does not track the same +uses depending on whether a `.mli` exists or not : +- If a value is declared in a `.mli`, then only uses outside its compilation +unit are tracked (by default). +- If there is no `.mli`, then uses inside the compilation unit are also tracked. + +With that same goal in mind, the analyzer does not report _transitively_ unused +exported values. I.e. if a value is only used by unused values, then it will +not be reported as unused. It would be reported unused only after all the code +using it has been removed. +This is also the compiler's behavior for its warnings about unused values. + +# Examples + +## Single compilation unit without interface + +This example illustrates a simple case of a compilation unit without `.mli` and +without any external use. + +The reference file for this example is [`hello_world_no_intf.ml`](./hello_world_no_intf.ml). + +The compilation command to produce the `hello_world_no_intf.cmi` and +`hello_world_no_intf.cmt` is : +``` +ocamlopt -bin-annot hello_world_no_intf.ml +``` + +The analysis command is : +``` +dead_code_analyzer --nothing -E all hello_world_no_intf.cmi hello_world_no_intf.cmt +``` + +### First run + +Code : +```OCaml +(* hello_world_no_intf.ml *) +let hello = "Hello" +let goodbye = "Goodbye" +let world = "World" + +let () = + let hello_world = hello ^ world in + let goodbye_world = goodbye ^ world in + print_endline hello_world +``` + +Compile and analyze : +``` +$ ocamlopt -bin-annot hello_world_no_intf.ml +File "hello_world_no_intf.ml", line 8, characters 6-19: +8 | let goodbye_world = goodbye ^ world in + ^^^^^^^^^^^^^ +Warning 26 [unused-var]: unused variable goodbye_world. + +$ dead_code_analyzer --nothing -E all hello_world_no_intf.cmi hello_world_no_intf.cmt +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= + +Nothing else to report in this section +-------------------------------------------------------------------------------- +``` + +Here we can see the warning 26 is triggered during the compilation. Hence, the +_local_ value `goodbye_world` at line 8 can be removed. + +The analyzer reports that there is no unused _exported_ value. The 3 exported +values are `hello`, `goodbye` and `world`. They are all referenced internally. +Because there is no `hello_world_no_intf.mli`, the internal uses are accounted for. + +### Fixing the warning 26 + +Let's remove the unused `goodbye_world`. + +Code : +```OCaml +(* hello_world_no_intf.ml *) +let hello = "Hello" +let goodbye = "Goodbye" +let world = "World" + +let () = + let hello_world = hello ^ world in + print_endline hello_world +``` + +Compile and analyze : +``` +$ ocamlopt -bin-annot hello_world_no_intf.ml + +$ dead_code_analyzer --nothing -E all hello_world_no_intf.cmi hello_world_no_intf.cmt +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= +/tmp/docs/exported_values/hello_world_no_intf.ml:3: goodbye + +Nothing else to report in this section +-------------------------------------------------------------------------------- +``` + +The compiler does not report any unused value. + +The analyzer, however, detects that `goodbye` declared at line 3 is now unused. + +Like we did with the warning 26, `goodbye` can be removed. + +### Removing the unused `goodbye` + +Code : +```OCaml +(* hello_world_no_intf.ml *) +let hello = "Hello" +let world = "World" + +let () = + let hello_world = hello ^ world in + print_endline hello_world +``` + +Compile and analyze : +``` +$ ocamlopt -bin-annot hello_world_no_intf.ml + +$ dead_code_analyzer --nothing -E all hello_world_no_intf.cmi hello_world_no_intf.cmt +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= + +Nothing else to report in this section +-------------------------------------------------------------------------------- +``` + +Now, neither the compiler nor the analyzer report any unused value. Our work here is done. + +## Single compilation unit with interface + +This example illustrates a simple case of a compilation unit with `.mli` and +without any external use. +This is the same as the previous example with an extra interface. + +The reference files for this example are [`hello_world.mli`](./hello_world.mli) +and [`hello_world.ml`](./hello_world.ml) + +The compilation command to produce the `hello_world.cmi` and `hello_world.cmt` is : +``` +ocamlopt -bin-annot hello_world.mli hello_world.ml +``` + +The analysis command is : +``` +dead_code_analyzer --nothing -E all hello_world.cmi hello_world.cmt +``` + +### First run + +Code : +```OCaml +(* hello_world.mli *) +val hello : string +val goodbye : string +val world : string +``` +```OCaml +(* hello_world.ml *) +let hello = "Hello" +let goodbye = "Goodbye" +let world = "World" + +let () = + let hello_world = hello ^ world in + let goodbye_world = goodbye ^ world in + print_endline hello_world +``` + +Compile and analyze : +``` +$ ocamlopt -bin-annot hello_world.ml +File "hello_world.ml", line 8, characters 6-19: +8 | let goodbye_world = goodbye ^ world in + ^^^^^^^^^^^^^ +Warning 26 [unused-var]: unused variable goodbye_world. + +$ dead_code_analyzer --nothing -E all hello_world.cmi hello_world.cmt +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= +/tmp/docs/exported_values/hello_world.mli:2: hello +/tmp/docs/exported_values/hello_world.mli:3: goodbye +/tmp/docs/exported_values/hello_world.mli:4: world + +Nothing else to report in this section +-------------------------------------------------------------------------------- +``` + +Like in the previous example, the warning 26 is triggered during the +compilation. Hence, the _local_ value `goodbye_world` at line 8 can be removed. + +The analyzer reports that there are 3 unused _exported_ value in +`hello_world.mli`: `hello` at line 2, `goodbye` line 3, and `world` line 4. +These are the only exported values in the `hello_world` compilation unit because +they are the only one listed in the `.mli`. They are all used in +`hello_world.ml`, but not outside of their compilation unit. Because there is an +interface file available, only external uses are accounted for. Thus, they are +considered unused and can be dropped from the `.mli` + +### Removing the unused values + +Let's remove the unused `goodbye_world` from `hello_world.ml`, reported unused +by the compiler, and the values in `hello_world.mli` reported by the analyzer. + +Code : +```OCaml +(* hello_world.mli *) +``` +```OCaml +(* hello_world.ml *) +let hello = "Hello" +let goodbye = "Goodbye" +let world = "World" + +let () = + let hello_world = hello ^ world in + print_endline hello_world +``` +Compile and analyze : +``` +$ ocamlopt -bin-annot hello_world.ml +$ dead_code_analyzer --nothing -E all hello_world.cmi hello_world.cmt +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= + +Nothing else to report in this section +-------------------------------------------------------------------------------- +``` + +Now, neither the compiler nor the analyzer report any unused value. + +We learned from the previous example that the `goodbye` value is unused after +we remove `goodbye_world`. This is on the compiler to report it in this example +because it is an _unexported_ value here (while it was an _exported_ value in +the previous example). This warning is actually off by default and can be +activated by passing the `-w +32` argument to the compiler : +``` +$ ocamlopt -w +32 hello_world.ml +File "hello_world.ml", line 3, characters 4-11: +3 | let goodbye = "Goodbye" + ^^^^^^^ +Warning 32 [unused-value-declaration]: unused value goodbye. +``` +The `goodbye` value can be safely removed and neither the compiler nor the +analyzer will report unused values anymore. Our work here is done. diff --git a/docs/exported_values/hello_world.ml b/docs/exported_values/hello_world.ml new file mode 100644 index 00000000..ac2bc5f6 --- /dev/null +++ b/docs/exported_values/hello_world.ml @@ -0,0 +1,9 @@ +(* hello_world.ml *) +let hello = "Hello" +let goodbye = "Goodbye" +let world = "World" + +let () = + let hello_world = hello ^ world in + let goodbye_world = goodbye ^ world in + print_endline hello_world diff --git a/docs/exported_values/hello_world.mli b/docs/exported_values/hello_world.mli new file mode 100644 index 00000000..aae4f08e --- /dev/null +++ b/docs/exported_values/hello_world.mli @@ -0,0 +1,4 @@ +(* hello_world.mli *) +val hello : string +val goodbye : string +val world : string diff --git a/docs/exported_values/hello_world_no_intf.ml b/docs/exported_values/hello_world_no_intf.ml new file mode 100644 index 00000000..d498448f --- /dev/null +++ b/docs/exported_values/hello_world_no_intf.ml @@ -0,0 +1,9 @@ +(* hello_world_no_intf.ml *) +let hello = "Hello" +let goodbye = "Goodbye" +let world = "World" + +let () = + let hello_world = hello ^ world in + let goodbye_world = goodbye ^ world in + print_endline hello_world From a6ed15ef5f6980e3d4a8f3f96b214df263680ef8 Mon Sep 17 00:00:00 2001 From: Corentin De Souza <9597216+fantazio@users.noreply.github.com> Date: Tue, 10 Feb 2026 17:17:07 +0100 Subject: [PATCH 02/16] [docs][exported_values][2/n] add multiple comp units example The new example is an hello world splitted in lib and bin. --- docs/exported_values/EXPORTED_VALUES.md | 180 +++++++++++++++++++++++ docs/exported_values/hello_world_bin.ml | 6 + docs/exported_values/hello_world_lib.ml | 4 + docs/exported_values/hello_world_lib.mli | 4 + 4 files changed, 194 insertions(+) create mode 100644 docs/exported_values/hello_world_bin.ml create mode 100644 docs/exported_values/hello_world_lib.ml create mode 100644 docs/exported_values/hello_world_lib.mli diff --git a/docs/exported_values/EXPORTED_VALUES.md b/docs/exported_values/EXPORTED_VALUES.md index 0c0bd879..9d0da026 100644 --- a/docs/exported_values/EXPORTED_VALUES.md +++ b/docs/exported_values/EXPORTED_VALUES.md @@ -6,6 +6,7 @@ + [Examples](#examples) + [Single compilation unit without interface](#single-compilation-unit-without-interface) + [Single compilation unit with interface](#single-compilation-unit-with-interface) + + [Multiple compilation units](#multiple-compilation-units) # Exported Values @@ -349,3 +350,182 @@ Warning 32 [unused-value-declaration]: unused value goodbye. ``` The `goodbye` value can be safely removed and neither the compiler nor the analyzer will report unused values anymore. Our work here is done. + +## Multiple compilation units + +This example illustrates a simple case of a library used by a binary. +This is the same as the previous example with an extra indirection. + +The reference files for this example are +[`hello_world_lib.mli`](./hello_world_lib.mli), +[`hello_world_lib.ml`](./hello_world_lib.ml), and +[`hello_world_bin.ml`](./hello_world_bin.ml) + +The compilation command to produce the the necessary `.cmi` and `.cmt` files is : +``` +ocamlopt -bin-annot hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml +``` + +The analysis command is : +``` +dead_code_analyzer --nothing -E all hello_world_lib.cmi hello_world_lib.cmt hello_world_bin.cmi hello_world_bin.cmt +``` + +> [!NOTE] +> It is left as an exercise to the user to explore this example without +> `hello_world_lib.mli`. + +### First run + +Code : +```OCaml +(* hello_world_lib.mli *) +val hello : string +val goodbye : string +val world : string +``` +```OCaml +(* hello_world_lib.ml *) +let hello = "Hello" +let goodbye = "Goodbye" +let world = "World" +``` +```OCaml +(* hello_world_bin.ml *) +let () = + let open Hello_world_lib in + let hello_world = hello ^ world in + let goodbye_world = goodbye ^ world in + print_endline hello_world +``` + +Compile and analyze : +``` +$ ocamlopt -bin-annot hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml +File "hello_world_bin.ml", line 5, characters 6-19: +5 | let goodbye_world = goodbye ^ world in + ^^^^^^^^^^^^^ +Warning 26 [unused-var]: unused variable goodbye_world. + +$ dead_code_analyzer --nothing -E all hello_world_lib.cmi hello_world_lib.cmt hello_world_bin.cmi hello_world_bin.cmt +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= + +Nothing else to report in this section +-------------------------------------------------------------------------------- +``` + +Like in the previous example, the warning 26 is triggered during the +compilation. Hence, the _local_ value `goodbye_world` at line 8 can be removed. + +The analyzer reports that there is no unused _exported_ value. The 3 exported +values are `hello`, `goodbye` and `world` in `hello_world_lib.mli`. They are all +referenced externally, in `hello_world_bin.ml`. + +> [!NOTE] +> All the different flavors of explicit reference are taken into account the +> same way. Here, the values are referenced after a local `open`. They could +> have been referenced after a global `open` or using their full paths (e.g. +> `Hello_world_lib.hello`) without making any difference on the reports. + +### Fixing the warning 26 + +Let's remove the unused `goodbye_world`. + +Code : +```OCaml +(* hello_world_lib.mli *) +val hello : string +val goodbye : string +val world : string +``` +```OCaml +(* hello_world_lib.ml *) +let hello = "Hello" +let goodbye = "Goodbye" +let world = "World" +``` +```OCaml +(* hello_world_bin.ml *) +let () = + let open Hello_world_lib in + let hello_world = hello ^ world in + print_endline hello_world +``` + +Compile and analyze : +``` +$ ocamlopt -bin-annot hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml + +$ dead_code_analyzer --nothing -E all hello_world_lib.cmi hello_world_lib.cmt hello_world_bin.cmi hello_world_bin.cmt +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= +/tmp/docs/exported_values/hello_world_lib.mli:3: goodbye + +Nothing else to report in this section +-------------------------------------------------------------------------------- +``` + +The compiler does not report any unused value. + +The analyzer, however, detects that `goodbye` declared in `hello_world_lib.mli` +at line 3 is now unused. + +Like we did with the warning 26, `goodbye` can be removed. + +### Unexporting `goodbye` + +Code : +```OCaml +(* hello_world_lib.mli *) +val hello : string +val world : string +``` +```OCaml +(* hello_world_lib.ml *) +let hello = "Hello" +let goodbye = "Goodbye" +let world = "World" +``` +```OCaml +(* hello_world_bin.ml *) +let () = + let open Hello_world_lib in + let hello_world = hello ^ world in + print_endline hello_world +``` + +Compile and analyze : +``` +$ ocamlopt -bin-annot hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml + +$ dead_code_analyzer --nothing -E all hello_world_lib.cmi hello_world_lib.cmt hello_world_bin.cmi hello_world_bin.cmt +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= + +Nothing else to report in this section +-------------------------------------------------------------------------------- +``` + +Now, neither the compiler nor the analyzer report any unused value. + +Like in the previous example, we need to pass `-w +32` to the compiler to +trigger the `unused-value-declaration` warning : +``` +$ ocamlopt -bin-annot hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml +File "hello_world_lib.ml", line 3, characters 4-11: +3 | let goodbye = "Goodbye" + ^^^^^^^ +Warning 32 [unused-value-declaration]: unused value goodbye. +``` +The `goodbye` value can be safely removed and neither the compiler nor the +analyzer will report unused values anymore. Our work here is done. diff --git a/docs/exported_values/hello_world_bin.ml b/docs/exported_values/hello_world_bin.ml new file mode 100644 index 00000000..a6293a4e --- /dev/null +++ b/docs/exported_values/hello_world_bin.ml @@ -0,0 +1,6 @@ +(* hello_world_bin.ml *) +let () = + let open Hello_world_lib in + let hello_world = hello ^ world in + let goodbye_world = goodbye ^ world in + print_endline hello_world diff --git a/docs/exported_values/hello_world_lib.ml b/docs/exported_values/hello_world_lib.ml new file mode 100644 index 00000000..93fb93bd --- /dev/null +++ b/docs/exported_values/hello_world_lib.ml @@ -0,0 +1,4 @@ +(* hello_world_lib.ml *) +let hello = "Hello" +let goodbye = "Goodbye" +let world = "World" diff --git a/docs/exported_values/hello_world_lib.mli b/docs/exported_values/hello_world_lib.mli new file mode 100644 index 00000000..77fe3cf0 --- /dev/null +++ b/docs/exported_values/hello_world_lib.mli @@ -0,0 +1,4 @@ +(* hello_world_lib.mli *) +val hello : string +val goodbye : string +val world : string From d07c0d4f3b2a7fcfcdcc5ae6085f0c4f2a4a3bd8 Mon Sep 17 00:00:00 2001 From: Corentin De Souza <9597216+fantazio@users.noreply.github.com> Date: Wed, 11 Feb 2026 16:29:11 +0100 Subject: [PATCH 03/16] [docs][exported_values][3/n] add example grouping all the hello world examples The new example shows the analyzer's behavior on a large codebase. --- docs/exported_values/EXPORTED_VALUES.md | 156 +++++++++++++++++++++--- 1 file changed, 142 insertions(+), 14 deletions(-) diff --git a/docs/exported_values/EXPORTED_VALUES.md b/docs/exported_values/EXPORTED_VALUES.md index 9d0da026..29292b4e 100644 --- a/docs/exported_values/EXPORTED_VALUES.md +++ b/docs/exported_values/EXPORTED_VALUES.md @@ -4,9 +4,11 @@ + [Definitions](#definitions) + [Usage](#usage) + [Examples](#examples) - + [Single compilation unit without interface](#single-compilation-unit-without-interface) - + [Single compilation unit with interface](#single-compilation-unit-with-interface) - + [Multiple compilation units](#multiple-compilation-units) + + [Hello world](#hello-world) + + [Single compilation unit without interface](#single-compilation-unit-without-interface) + + [Single compilation unit with interface](#single-compilation-unit-with-interface) + + [Multiple compilation units](#multiple-compilation-units) + + [All Together](#all-together) # Exported Values @@ -101,7 +103,9 @@ This is also the compiler's behavior for its warnings about unused values. # Examples -## Single compilation unit without interface +## Hello world + +### Single compilation unit without interface This example illustrates a simple case of a compilation unit without `.mli` and without any external use. @@ -119,7 +123,7 @@ The analysis command is : dead_code_analyzer --nothing -E all hello_world_no_intf.cmi hello_world_no_intf.cmt ``` -### First run +#### First run Code : ```OCaml @@ -160,7 +164,7 @@ The analyzer reports that there is no unused _exported_ value. The 3 exported values are `hello`, `goodbye` and `world`. They are all referenced internally. Because there is no `hello_world_no_intf.mli`, the internal uses are accounted for. -### Fixing the warning 26 +#### Fixing the warning 26 Let's remove the unused `goodbye_world`. @@ -198,7 +202,7 @@ The analyzer, however, detects that `goodbye` declared at line 3 is now unused. Like we did with the warning 26, `goodbye` can be removed. -### Removing the unused `goodbye` +#### Removing the unused `goodbye` Code : ```OCaml @@ -228,7 +232,7 @@ Nothing else to report in this section Now, neither the compiler nor the analyzer report any unused value. Our work here is done. -## Single compilation unit with interface +### Single compilation unit with interface This example illustrates a simple case of a compilation unit with `.mli` and without any external use. @@ -247,7 +251,7 @@ The analysis command is : dead_code_analyzer --nothing -E all hello_world.cmi hello_world.cmt ``` -### First run +#### First run Code : ```OCaml @@ -301,7 +305,7 @@ they are the only one listed in the `.mli`. They are all used in interface file available, only external uses are accounted for. Thus, they are considered unused and can be dropped from the `.mli` -### Removing the unused values +#### Removing the unused values Let's remove the unused `goodbye_world` from `hello_world.ml`, reported unused by the compiler, and the values in `hello_world.mli` reported by the analyzer. @@ -351,7 +355,7 @@ Warning 32 [unused-value-declaration]: unused value goodbye. The `goodbye` value can be safely removed and neither the compiler nor the analyzer will report unused values anymore. Our work here is done. -## Multiple compilation units +### Multiple compilation units This example illustrates a simple case of a library used by a binary. This is the same as the previous example with an extra indirection. @@ -375,7 +379,7 @@ dead_code_analyzer --nothing -E all hello_world_lib.cmi hello_world_lib.cmt hell > It is left as an exercise to the user to explore this example without > `hello_world_lib.mli`. -### First run +#### First run Code : ```OCaml @@ -431,7 +435,7 @@ referenced externally, in `hello_world_bin.ml`. > have been referenced after a global `open` or using their full paths (e.g. > `Hello_world_lib.hello`) without making any difference on the reports. -### Fixing the warning 26 +#### Fixing the warning 26 Let's remove the unused `goodbye_world`. @@ -479,7 +483,7 @@ at line 3 is now unused. Like we did with the warning 26, `goodbye` can be removed. -### Unexporting `goodbye` +#### Unexporting `goodbye` Code : ```OCaml @@ -529,3 +533,127 @@ Warning 32 [unused-value-declaration]: unused value goodbye. ``` The `goodbye` value can be safely removed and neither the compiler nor the analyzer will report unused values anymore. Our work here is done. + +### All Together + +This example illustrates a simple case of a complete codebase with independent +and dependent components, and duplicate names. +This is the grouping of all the previous [*Hello World*](#hello-world) examples. +Analyzing all the files at once reduces the number of iterations to reach a +satisfying codebase. + +The reference files for this example are all those listed previously. + +The compilation command to produce the the necessary `.cmi` and `.cmt` files, +and the desired warnings is the combination of all the previous ones : +``` +ocamlopt -w +32 -bin-annot hello_world_no_intf.ml hello_world.mli hello_world.ml hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml +``` + +> [!NOTE] +> For our usage, this has the same effect has running each of the previous +> compliation commands, with the extra `-w +32` argument, one after the other. +> The benefit is that all the warnings will be printed at once. + +The analysis command is : +``` +dead_code_analyzer --nothing -E all . +``` + +> [!TIP] +> As we can see for the compilation command, there is a large number of files to +> list. Instead of listing all the `.cmi` and `.cmt` files in the command line, +> the analyzer accepts directories as arguments and will analyze all the +> relevant files it can find in them and in their subdirectories. + +The code is not re-exposed at each iteration here. It is the same as in the previous examples. + +#### First Run + +Compile : +``` +$ ocamlopt -w +32 -bin-annot hello_world_no_intf.ml hello_world.mli hello_world.ml hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml +File "hello_world_no_intf.ml", line 8, characters 6-19: +8 | let goodbye_world = goodbye ^ world in + ^^^^^^^^^^^^^ +Warning 26 [unused-var]: unused variable goodbye_world. + +File "hello_world.ml", line 8, characters 6-19: +8 | let goodbye_world = goodbye ^ world in + ^^^^^^^^^^^^^ +Warning 26 [unused-var]: unused variable goodbye_world. + +File "hello_world_bin.ml", line 5, characters 6-19: +5 | let goodbye_world = goodbye ^ world in + ^^^^^^^^^^^^^ +Warning 26 [unused-var]: unused variable goodbye_world. +``` + +Without any surprise, the compiler identifies the different warning 26 explored +in the previous examples. +Let's fix them and **recompile**, before running the analyzer. + +Analyze : +``` +$ dead_code_analyzer --nothing -E all . +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= +/tmp/docs/exported_values/hello_world.mli:2: hello +/tmp/docs/exported_values/hello_world.mli:3: goodbye +/tmp/docs/exported_values/hello_world.mli:4: world +/tmp/docs/exported_values/hello_world_lib.mli:3: goodbye +/tmp/docs/exported_values/hello_world_no_intf.ml:3: goodbye + +Nothing else to report in this section +-------------------------------------------------------------------------------- +``` + +The analyzer correclty identifies the unused exported values it identified for +the previous examples, all in one run. Note that the reports are in the +lexicographical order. +Unlike the compiler which listed its warnings in the order the files appeared in +the command line, the analyzer always sorts its reports in this order. + +#### Removing the unused exported values + +Like we did, in the previous examples, we can remove the exported values at the +locations reported by the analyzer. + +Compile : +``` +$ ocamlopt -w +32 -bin-annot hello_world_no_intf.ml hello_world.mli hello_world.ml hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml +File "hello_world.ml", line 3, characters 4-11: +3 | let goodbye = "Goodbye" + ^^^^^^^ +Warning 32 [unused-value-declaration]: unused value goodbye. + +File "hello_world_lib.ml", line 3, characters 4-11: +3 | let goodbye = "Goodbye" + ^^^^^^^ +Warning 32 [unused-value-declaration]: unused value goodbye. +``` + +Once again, the finidings are the same as for the previous examples. After +`goodbye_world` is removed and `goodbye` is unexported, the compiler warning 32 +indicates that it is unused. +Let's fix the warnings. + +Compile and analyze : +``` +$ ocamlopt -w +32 -bin-annot hello_world_no_intf.ml hello_world.mli hello_world.ml hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml + +$ dead_code_analyzer --nothing -E all . +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= + +Nothing else to report in this section +-------------------------------------------------------------------------------- +``` + +Now, neither the compiler nor the analyzer report any unused value. Our work here is done. From ff9a91b857ecee4cb37a67919d49ff0a5ccb8583 Mon Sep 17 00:00:00 2001 From: Corentin De Souza <9597216+fantazio@users.noreply.github.com> Date: Wed, 11 Feb 2026 16:38:13 +0100 Subject: [PATCH 04/16] [docs][exported_values][4/n] group the hello world examples They are located in `docs/exported_values/hello_word/`. The documentation is updated to point to the right files and the analyzer's reports as well. --- docs/exported_values/EXPORTED_VALUES.md | 41 +++++++++++-------- .../{ => hello_world}/hello_world.ml | 0 .../{ => hello_world}/hello_world.mli | 0 .../{ => hello_world}/hello_world_bin.ml | 0 .../{ => hello_world}/hello_world_lib.ml | 0 .../{ => hello_world}/hello_world_lib.mli | 0 .../{ => hello_world}/hello_world_no_intf.ml | 0 7 files changed, 25 insertions(+), 16 deletions(-) rename docs/exported_values/{ => hello_world}/hello_world.ml (100%) rename docs/exported_values/{ => hello_world}/hello_world.mli (100%) rename docs/exported_values/{ => hello_world}/hello_world_bin.ml (100%) rename docs/exported_values/{ => hello_world}/hello_world_lib.ml (100%) rename docs/exported_values/{ => hello_world}/hello_world_lib.mli (100%) rename docs/exported_values/{ => hello_world}/hello_world_no_intf.ml (100%) diff --git a/docs/exported_values/EXPORTED_VALUES.md b/docs/exported_values/EXPORTED_VALUES.md index 29292b4e..e9e2785d 100644 --- a/docs/exported_values/EXPORTED_VALUES.md +++ b/docs/exported_values/EXPORTED_VALUES.md @@ -105,12 +105,20 @@ This is also the compiler's behavior for its warnings about unused values. ## Hello world +All of the following examples can be found in the [hello\_world](./hello_world) +directory. + +The reference takes place in `/tmp/docs/exported_values/hello_world`, which +contains copies of the examples. Reported locations may differ depending on the +location of the source files. + ### Single compilation unit without interface This example illustrates a simple case of a compilation unit without `.mli` and without any external use. -The reference file for this example is [`hello_world_no_intf.ml`](./hello_world_no_intf.ml). +The reference file for this example is +[`hello_world_no_intf.ml`](./hello_world/hello_world_no_intf.ml). The compilation command to produce the `hello_world_no_intf.cmi` and `hello_world_no_intf.cmt` is : @@ -190,7 +198,7 @@ Scanning files... .> UNUSED EXPORTED VALUES: ========================= -/tmp/docs/exported_values/hello_world_no_intf.ml:3: goodbye +/tmp/docs/exported_values/hello_world/hello_world_no_intf.ml:3: goodbye Nothing else to report in this section -------------------------------------------------------------------------------- @@ -238,8 +246,9 @@ This example illustrates a simple case of a compilation unit with `.mli` and without any external use. This is the same as the previous example with an extra interface. -The reference files for this example are [`hello_world.mli`](./hello_world.mli) -and [`hello_world.ml`](./hello_world.ml) +The reference files for this example are +[`hello_world.mli`](./hello_world/hello_world.mli) and +[`hello_world.ml`](./hello_world/hello_world.ml) The compilation command to produce the `hello_world.cmi` and `hello_world.cmt` is : ``` @@ -286,9 +295,9 @@ Scanning files... .> UNUSED EXPORTED VALUES: ========================= -/tmp/docs/exported_values/hello_world.mli:2: hello -/tmp/docs/exported_values/hello_world.mli:3: goodbye -/tmp/docs/exported_values/hello_world.mli:4: world +/tmp/docs/exported_values/hello_world/hello_world.mli:2: hello +/tmp/docs/exported_values/hello_world/hello_world.mli:3: goodbye +/tmp/docs/exported_values/hello_world/hello_world.mli:4: world Nothing else to report in this section -------------------------------------------------------------------------------- @@ -361,9 +370,9 @@ This example illustrates a simple case of a library used by a binary. This is the same as the previous example with an extra indirection. The reference files for this example are -[`hello_world_lib.mli`](./hello_world_lib.mli), -[`hello_world_lib.ml`](./hello_world_lib.ml), and -[`hello_world_bin.ml`](./hello_world_bin.ml) +[`hello_world_lib.mli`](./hello_world/hello_world_lib.mli), +[`hello_world_lib.ml`](./hello_world/hello_world_lib.ml), and +[`hello_world_bin.ml`](./hello_world/hello_world_bin.ml) The compilation command to produce the the necessary `.cmi` and `.cmt` files is : ``` @@ -470,7 +479,7 @@ Scanning files... .> UNUSED EXPORTED VALUES: ========================= -/tmp/docs/exported_values/hello_world_lib.mli:3: goodbye +/tmp/docs/exported_values/hello_world/hello_world_lib.mli:3: goodbye Nothing else to report in this section -------------------------------------------------------------------------------- @@ -601,11 +610,11 @@ Scanning files... .> UNUSED EXPORTED VALUES: ========================= -/tmp/docs/exported_values/hello_world.mli:2: hello -/tmp/docs/exported_values/hello_world.mli:3: goodbye -/tmp/docs/exported_values/hello_world.mli:4: world -/tmp/docs/exported_values/hello_world_lib.mli:3: goodbye -/tmp/docs/exported_values/hello_world_no_intf.ml:3: goodbye +/tmp/docs/exported_values/hello_world/hello_world.mli:2: hello +/tmp/docs/exported_values/hello_world/hello_world.mli:3: goodbye +/tmp/docs/exported_values/hello_world/hello_world.mli:4: world +/tmp/docs/exported_values/hello_world/hello_world_lib.mli:3: goodbye +/tmp/docs/exported_values/hello_world/hello_world_no_intf.ml:3: goodbye Nothing else to report in this section -------------------------------------------------------------------------------- diff --git a/docs/exported_values/hello_world.ml b/docs/exported_values/hello_world/hello_world.ml similarity index 100% rename from docs/exported_values/hello_world.ml rename to docs/exported_values/hello_world/hello_world.ml diff --git a/docs/exported_values/hello_world.mli b/docs/exported_values/hello_world/hello_world.mli similarity index 100% rename from docs/exported_values/hello_world.mli rename to docs/exported_values/hello_world/hello_world.mli diff --git a/docs/exported_values/hello_world_bin.ml b/docs/exported_values/hello_world/hello_world_bin.ml similarity index 100% rename from docs/exported_values/hello_world_bin.ml rename to docs/exported_values/hello_world/hello_world_bin.ml diff --git a/docs/exported_values/hello_world_lib.ml b/docs/exported_values/hello_world/hello_world_lib.ml similarity index 100% rename from docs/exported_values/hello_world_lib.ml rename to docs/exported_values/hello_world/hello_world_lib.ml diff --git a/docs/exported_values/hello_world_lib.mli b/docs/exported_values/hello_world/hello_world_lib.mli similarity index 100% rename from docs/exported_values/hello_world_lib.mli rename to docs/exported_values/hello_world/hello_world_lib.mli diff --git a/docs/exported_values/hello_world_no_intf.ml b/docs/exported_values/hello_world/hello_world_no_intf.ml similarity index 100% rename from docs/exported_values/hello_world_no_intf.ml rename to docs/exported_values/hello_world/hello_world_no_intf.ml From 7ee535cff607e64640a3bad81af7c7a0cd5c1277 Mon Sep 17 00:00:00 2001 From: Corentin De Souza <9597216+fantazio@users.noreply.github.com> Date: Wed, 11 Feb 2026 17:57:57 +0100 Subject: [PATCH 05/16] [docs][exported_values][5/n] add a module example This is the first code-construct-oriented example. It simply demonstrates that values are not only tracked at the top level of a compilation unit but also within its submodules. It also shows that the reports explicitly give the full path within the compilation unit. --- docs/exported_values/EXPORTED_VALUES.md | 165 +++++++++++++++++- .../code_constructs/module/Makefile | 12 ++ .../code_constructs/module/module_bin.ml | 3 + .../code_constructs/module/module_lib.ml | 10 ++ .../code_constructs/module/module_lib.mli | 7 + 5 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 docs/exported_values/code_constructs/module/Makefile create mode 100644 docs/exported_values/code_constructs/module/module_bin.ml create mode 100644 docs/exported_values/code_constructs/module/module_lib.ml create mode 100644 docs/exported_values/code_constructs/module/module_lib.mli diff --git a/docs/exported_values/EXPORTED_VALUES.md b/docs/exported_values/EXPORTED_VALUES.md index e9e2785d..ee5deb13 100644 --- a/docs/exported_values/EXPORTED_VALUES.md +++ b/docs/exported_values/EXPORTED_VALUES.md @@ -9,6 +9,8 @@ + [Single compilation unit with interface](#single-compilation-unit-with-interface) + [Multiple compilation units](#multiple-compilation-units) + [All Together](#all-together) + + [Code constructs](#code-constructs) + + [Module](#module) # Exported Values @@ -385,7 +387,7 @@ dead_code_analyzer --nothing -E all hello_world_lib.cmi hello_world_lib.cmt hell ``` > [!NOTE] -> It is left as an exercise to the user to explore this example without +> It is left as an exercise to the reader to explore this example without > `hello_world_lib.mli`. #### First run @@ -666,3 +668,164 @@ Nothing else to report in this section ``` Now, neither the compiler nor the analyzer report any unused value. Our work here is done. + + +## Code constructs + +All of the following examples can be found in the +[code\_constructs](./code_constructs) directory. + +The reference takes place in `/tmp/docs/exported_values/code_constructs`, which +is a copy of `code_constructs`. Reported locations may differ depending on the +location of the source files. + +### Module + +This example illustrates a simple case of exported values in submodules. + +The reference files for this example are in the +[module](./code_constructs/module) directory. + +The compilation command is : +``` +make -C module build +``` + +The analysis command is : +``` +make -C module analyze +``` + +The compile + analyze command is : +``` +make -C module +``` + +#### First run + +Code: +```OCaml +(* module_lib.mli *) +module M : sig + type t + val externally_used : t + val internally_used : t + val unused : t +end +``` +```OCaml +(* module_lib.ml *) +module M = struct + type t = unit + let externally_used = () + let internally_used = () + let unused = () + let unused_unexported = () +end + +let () = M.internally_used +``` +```OCaml +(* module_bin.ml *) +let () = + ignore Module_lib.M.externally_used +``` + +Before looking at the analysis results, let's look at the code. +Here, all the values of `Module_lib.M` are exported except for +`unused_unexported`. Among the exported values, `unused` is not referenced +anywhere, `internally_used` is only referenced within its compilation unit +(`Module_lib`), and `externally_used` is only referenced outside of it. + +> [!IMPORTANT] +> Using `internally_used` inside of `M` rather than outside would provide the +> same results. The only scope of interest is the compilation unit. + +Compile and analyze : +``` +$ make -C module +make: Entering directory '/tmp/docs/exported_values/code_constructs/module' +ocamlopt -w +32 -bin-annot module_lib.mli module_lib.ml module_bin.ml +File "module_lib.ml", line 7, characters 6-23: +7 | let unused_unexported = () + ^^^^^^^^^^^^^^^^^ +Warning 32 [unused-value-declaration]: unused value unused_unexported. +dead_code_analyzer --nothing -E all . +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= +/tmp/docs/exported_values/code_constructs/module/module_lib.mli:5: M.internally_used +/tmp/docs/exported_values/code_constructs/module/module_lib.mli:6: M.unused + +Nothing else to report in this section +-------------------------------------------------------------------------------- + + +make: Leaving directory '/tmp/docs/exported_values/code_constructs/module' +``` + +The compiler detects that `unused_unexported` is unused. + +The analyzer reports `M.internally_used` and `M.unused` as unused. Notice how +it did not only report the name of the value but its full path within its +compilation unit. + +> [!NOTE] +> `M.internally_used` is only used withing its compialtion unit. Because its is +> declared in a `.mli`, only external uses are accounted for. +> It is left as an exercise to the reader to explore this example without +> `module_lib.mli` + +#### Removing the unused values + +Code: +```OCaml +(* module_lib.mli *) +module M : sig + type t + val externally_used : t +end +``` +```OCaml +(* module_lib.ml *) +module M = struct + type t = unit + let externally_used = () + let internally_used = () + let unused = () +end + +let () = M.internally_used +``` +```OCaml +(* module_bin.ml *) +let () = + ignore Module_lib.M.externally_used +``` + +Compile and analyze : +``` +$ make -C module +make: Entering directory '/tmp/docs/exported_values/code_constructs/module' +ocamlopt -w +32 -bin-annot module_lib.mli module_lib.ml module_bin.ml +File "module_lib.ml", line 6, characters 6-12: +6 | let unused = () + ^^^^^^ +Warning 32 [unused-value-declaration]: unused value unused. +dead_code_analyzer --nothing -E all . +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= + +Nothing else to report in this section +-------------------------------------------------------------------------------- + + +make: Leaving directory '/tmp/docs/exported_values/code_constructs/module' +``` + +The compiler reports `unused` as unused and the analyzer does not report anything. Removing that value fixes all the warnings. Our work here is done. diff --git a/docs/exported_values/code_constructs/module/Makefile b/docs/exported_values/code_constructs/module/Makefile new file mode 100644 index 00000000..4917174f --- /dev/null +++ b/docs/exported_values/code_constructs/module/Makefile @@ -0,0 +1,12 @@ +SRC:=module_lib.mli module_lib.ml module_bin.ml + +all: build analyze + +build: + ocamlopt -w +32 -bin-annot ${SRC} + +analyze: + dead_code_analyzer --nothing -E all . + +clean: + rm -f *.cm* *.o a.out diff --git a/docs/exported_values/code_constructs/module/module_bin.ml b/docs/exported_values/code_constructs/module/module_bin.ml new file mode 100644 index 00000000..19c25d77 --- /dev/null +++ b/docs/exported_values/code_constructs/module/module_bin.ml @@ -0,0 +1,3 @@ +(* module_bin.ml *) +let () = + ignore Module_lib.M.externally_used diff --git a/docs/exported_values/code_constructs/module/module_lib.ml b/docs/exported_values/code_constructs/module/module_lib.ml new file mode 100644 index 00000000..1d50dbc1 --- /dev/null +++ b/docs/exported_values/code_constructs/module/module_lib.ml @@ -0,0 +1,10 @@ +(* module_lib.ml *) +module M = struct + type t = unit + let externally_used = () + let internally_used = () + let unused = () + let unused_unexported = () +end + +let () = M.internally_used diff --git a/docs/exported_values/code_constructs/module/module_lib.mli b/docs/exported_values/code_constructs/module/module_lib.mli new file mode 100644 index 00000000..7eccf6f3 --- /dev/null +++ b/docs/exported_values/code_constructs/module/module_lib.mli @@ -0,0 +1,7 @@ +(* module_lib.mli *) +module M : sig + type t + val externally_used : t + val internally_used : t + val unused : t +end From 3987df51887af053dfb6f4d500b8c95c07a53f64 Mon Sep 17 00:00:00 2001 From: Corentin De Souza <9597216+fantazio@users.noreply.github.com> Date: Sat, 14 Feb 2026 18:25:45 +0100 Subject: [PATCH 06/16] [docs][exported_values][6/n] add a functor example It demontrates both the tracking of uses by requirement and of values exposed by functors. It also exposes the limitation of not tracking the values required by the parameters (this work is left to the compiler). --- docs/exported_values/EXPORTED_VALUES.md | 334 ++++++++++++++++++ .../code_constructs/functor/Makefile | 12 + .../code_constructs/functor/functor_bin.ml | 8 + .../code_constructs/functor/functor_lib.ml | 25 ++ .../code_constructs/functor/functor_lib.mli | 27 ++ 5 files changed, 406 insertions(+) create mode 100644 docs/exported_values/code_constructs/functor/Makefile create mode 100644 docs/exported_values/code_constructs/functor/functor_bin.ml create mode 100644 docs/exported_values/code_constructs/functor/functor_lib.ml create mode 100644 docs/exported_values/code_constructs/functor/functor_lib.mli diff --git a/docs/exported_values/EXPORTED_VALUES.md b/docs/exported_values/EXPORTED_VALUES.md index ee5deb13..ee87de6e 100644 --- a/docs/exported_values/EXPORTED_VALUES.md +++ b/docs/exported_values/EXPORTED_VALUES.md @@ -11,6 +11,7 @@ + [All Together](#all-together) + [Code constructs](#code-constructs) + [Module](#module) + + [Functor](#functor) # Exported Values @@ -829,3 +830,336 @@ make: Leaving directory '/tmp/docs/exported_values/code_constructs/module' ``` The compiler reports `unused` as unused and the analyzer does not report anything. Removing that value fixes all the warnings. Our work here is done. + +### Functor + +This example illustrates a simple case of exported values in functors. + +The reference files for this example are in the +[functor](./code_constructs/functor) directory. + +The compilation command is : +``` +make -C functor build +``` + +The analysis command is : +``` +make -C functor analyze +``` + +The compile + analyze command is : +``` +make -C functor +``` + +#### First run + +Code: +```OCaml +(* functor_lib.mli *) +type t + +module F (P : sig + val used_required : t + val unused_required : t +end) : sig + val externally_used : t + val internally_used : t + val unused : t +end + +module InternalParam : sig + val used_required : t + val unused_required : t +end + +module ExternalParam : sig + val used_required : t + val unused_required : t +end + +module InternalApp : sig + val externally_used : t + val internally_used : t + val unused : t +end +``` +```OCaml +(* functor_lib.ml *) +type t = unit + +module F (P : sig + val used_required : t + val unused_required : t +end) = struct + let externally_used = P.used_required + let internally_used = P.used_required + let unused = P.used_required + let unused_unexported = P.used_required + let () = internally_used +end + +module InternalParam = struct + let used_required = () + let unused_required = () +end + +module ExternalParam = struct + let used_required = () + let unused_required = () +end + +module InternalApp = F(InternalParam) +``` +```OCaml +(* functor_bin.ml *) +open Functor_lib + +module ExternalApp = F(ExternalParam) + +let () = + ignore InternalApp.externally_used; + ignore ExternalApp.externally_used +``` + +Before looking at the analysis results, let's look at the code. + +The `Functor_lib` compilation unit exports 1 functor and 3 modules : +- `F` takes a module containing the values `used_required` and `unused_required`, + and returns a module with 3 values whose names are explicit. +- `InternalParam` and `ExternalParam` fit the signature of `F`'s parameter `P`. + The first one is used for a functor application inside `Functor_lib`. + The second one is used for a functor application outside of it. +- `InternalApp` fits the signature of the result of `F`, and is impemented as + the result of applying `F` inside its compilation unit. + +The `Functor_bin` compilation unit exports 1 module : `ExternalApp`, which is +the result of applying `Functor_lib.F` outside its compilation unit. + +Among all the exported values, the only explicit references accounted for are +those of `InternalApp.externally_used` and `External_app.externally_used` in +`Functor_bin`. + +Additionally, some values are used by requirement. Because `InternalParam` and +`ExternalParam` are passed as arguments to `F`, their values `used_required` and +`unused_required` are used by requirement to fulfill the signature of `F`'s +parameter `P`. + +With those observations in mind, let's see what the compiler and the anlyzer +report. + +Compile and analyze: +``` +$ make -C functor +make: Entering directory '/tmp/docs/exported_values/code_constructs/functor' +ocamlopt -w +32 -bin-annot functor_lib.mli functor_lib.ml functor_bin.ml +File "functor_lib.ml", line 6, characters 2-25: +6 | val unused_required : t + ^^^^^^^^^^^^^^^^^^^^^^^ +Warning 32 [unused-value-declaration]: unused value unused_required. + +File "functor_lib.ml", line 11, characters 6-23: +11 | let unused_unexported = P.used_required + ^^^^^^^^^^^^^^^^^ +Warning 32 [unused-value-declaration]: unused value unused_unexported. +dead_code_analyzer --nothing -E all . +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= +/tmp/docs/exported_values/code_constructs/functor/functor_lib.mli:9: F.internally_used +/tmp/docs/exported_values/code_constructs/functor/functor_lib.mli:10: F.unused +/tmp/docs/exported_values/code_constructs/functor/functor_lib.mli:14: InternalParam.used_required +/tmp/docs/exported_values/code_constructs/functor/functor_lib.mli:15: InternalParam.unused_required +/tmp/docs/exported_values/code_constructs/functor/functor_lib.mli:25: InternalApp.internally_used +/tmp/docs/exported_values/code_constructs/functor/functor_lib.mli:26: InternalApp.unused + +Nothing else to report in this section +-------------------------------------------------------------------------------- + + +make: Leaving directory '/tmp/docs/exported_values/code_constructs/functor' +``` + +The compiler tells us that `unused_required` is unused in the implementation of +`F`. It also reports `unexported_unused` in `F` like it did in the +[module example](#module) for regular modules. These 2 values can be removed at +the indicated locations. + +The analyzer reports 6 unused exported values, all in the `functor_lib.mli` : +2 in `F`, 2 in `InternalParam`, and 2 in `InternalApp`. Let's observe them by +module (in reverse order) : + +- The reports for `InternalApp` are identical to the [module example](#module). +Although, `InternalApp` is the result of applying `F`, it has an explicit +signature. I.e. it exposes its own values and the link between them and those of +`F` is absent from the signature. Consequently, they are tracked independently. + +- As we observed before runnning the analyzer, the values in `InternalParam` are +used by requirement. However, this use is internal to `Functor_lib`, and there +is an interface available : `functor_lib.mli`. Consequently, the internal uses +are ignored, and `InternalParam`'s values become unused. + +> [!NOTE] +> The kind of use (explicit reference, or by requirement) has no influence on +> the tracking mode (external only, or internal + external). + +- `F` is a functor but is tracked like a regular module. The reported values are +those of its result module. Reporting on those may feel like duplicates, but, +as explained for the reports of `InternalApp`, the values of the result +module are declared independently of those of `InternalApp`, hence they are +tracked and reported independently. + +All the values reported by the analyzer can be safely removed. + +Before moving on, there is another observation that we can make : +the values `unused` and `internally_used` of `ExternalApp` are not reported. +Because they are reported for `InternalApp` and `F`, it would be natural to +expect them in the reports for `ExternalApp` as well. In reality, they are not +tracked individually for `ExternalApp` because it does not expose them +explicitly. Unlike `InternalApp` which has an explicit module signature, +`ExternalApp` does not. Consequently, its values are directly linked to those of +`F`. This situation will be explored in the +[module signature](#module-signature) example. + +> [!TIP] +> If we activated the compiler warning 67 `unused-functor-parameter`(by +> passing the argument `-w +67`), then the compiler would have reported : +> ``` +> File "functor_lib.mli", line 4, characters 10-11: +> 4 | module F (P : sig +> ^ +> Warning 67 [unused-functor-parameter]: unused functor parameter P. +> ``` +> This can be fixed by either replacing `P` with `_`, or by rewriting the +> declaration of `F` as : +> ```OCaml +> module F : sig +> val used_required : t +> val unused_required : t +> end +> -> sig +> val externally_used : t +> val internally_used : t +> val unused : t +> end +> ``` + +#### Removing the unused values + +In addition to removing everything that was reported by the compiler and the +analyzer, we also commented out `P.used_required` and `InternalParam` in +`functor_lib.mli`. It is up to the user to decide whether they would like to +keep them or remove them from their specifications. Neither would be reported +by the compiler or the analyzer. + +> [!NOTE] +> Functors are covariant in their parameters. I.e. it is allowed to give a +> larger module as argument than what the parameter specifies. +> Similarly it is allowed to declare the parameter larger in the interface than +> it is in the implementation. Consequently, the compiler would not complain if +> `P` would expect `unused_required` in the `.mli` but not in the `.ml`. + +Code: +```OCaml +(* functor_lib.mli *) +type t + +module F (P : sig + val used_required : t + (* val unused_required : t *) +end) : sig + val externally_used : t +end + +(* +module InternalParam : sig +end +*) + +module ExternalParam : sig + val used_required : t + val unused_required : t +end + +module InternalApp : sig + val externally_used : t +end +``` +```OCaml +(* functor_lib.ml *) +type t = unit + +module F (P : sig + val used_required : t +end) = struct + let externally_used = P.used_required + let internally_used = P.used_required + let unused = P.used_required + let () = internally_used +end + +module InternalParam = struct + let used_required = () + let unused_required = () +end + +module ExternalParam = struct + let used_required = () + let unused_required = () +end + +module InternalApp = F(InternalParam) +``` +```OCaml +(* functor_bin.ml *) +open Functor_lib + +module ExternalApp = F(ExternalParam) + +let () = + ignore InternalApp.externally_used; + ignore ExternalApp.externally_used +``` + +Compile and analyze : +``` +$ make -C functor +make: Entering directory '/tmp/docs/exported_values/code_constructs/functor' +ocamlopt -w +32 -bin-annot functor_lib.mli functor_lib.ml functor_bin.ml +File "functor_lib.ml", line 9, characters 6-12: +9 | let unused = P.used_required + ^^^^^^ +Warning 32 [unused-value-declaration]: unused value unused. + +File "functor_lib.ml", line 15, characters 6-21: +15 | let unused_required = () + ^^^^^^^^^^^^^^^ +Warning 32 [unused-value-declaration]: unused value unused_required. +dead_code_analyzer --nothing -E all . +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= +/tmp/docs/exported_values/code_constructs/functor/functor_lib.mli:18: ExternalParam.unused_required + +Nothing else to report in this section +-------------------------------------------------------------------------------- + + +make: Leaving directory '/tmp/docs/exported_values/code_constructs/functor' +``` + +Now that `F.unused` and `InternalParam.unused_required` are not exported, they +are reported as unused by the compiler, and can be removed safely. + +`ExternalParam.unused_required` was used by requirement. Now that it is not +required by `P` (because it is commented out), it is unused and the analyzer +correctly reports it. It can be removed safely. Removing it will trigger the +same compiler warning as for `InternalParam.unused_required`, so it can be +removed from both the interface and the implementation. + +The unused values can be removed as explained. Our work here is done. diff --git a/docs/exported_values/code_constructs/functor/Makefile b/docs/exported_values/code_constructs/functor/Makefile new file mode 100644 index 00000000..c91b18eb --- /dev/null +++ b/docs/exported_values/code_constructs/functor/Makefile @@ -0,0 +1,12 @@ +SRC:=functor_lib.mli functor_lib.ml functor_bin.ml + +all: build analyze + +build: + ocamlopt -w +32 -bin-annot ${SRC} + +analyze: + dead_code_analyzer --nothing -E all . + +clean: + rm -f *.cm* *.o a.out diff --git a/docs/exported_values/code_constructs/functor/functor_bin.ml b/docs/exported_values/code_constructs/functor/functor_bin.ml new file mode 100644 index 00000000..28a98f04 --- /dev/null +++ b/docs/exported_values/code_constructs/functor/functor_bin.ml @@ -0,0 +1,8 @@ +(* functor_bin.ml *) +open Functor_lib + +module ExternalApp = F(ExternalParam) + +let () = + ignore InternalApp.externally_used; + ignore ExternalApp.externally_used diff --git a/docs/exported_values/code_constructs/functor/functor_lib.ml b/docs/exported_values/code_constructs/functor/functor_lib.ml new file mode 100644 index 00000000..911b7b1a --- /dev/null +++ b/docs/exported_values/code_constructs/functor/functor_lib.ml @@ -0,0 +1,25 @@ +(* functor_lib.ml *) +type t = unit + +module F (P : sig + val used_required : t + val unused_required : t +end) = struct + let externally_used = P.used_required + let internally_used = P.used_required + let unused = P.used_required + let unused_unexported = P.used_required + let () = internally_used +end + +module InternalParam = struct + let used_required = () + let unused_required = () +end + +module ExternalParam = struct + let used_required = () + let unused_required = () +end + +module InternalApp = F(InternalParam) diff --git a/docs/exported_values/code_constructs/functor/functor_lib.mli b/docs/exported_values/code_constructs/functor/functor_lib.mli new file mode 100644 index 00000000..45af0126 --- /dev/null +++ b/docs/exported_values/code_constructs/functor/functor_lib.mli @@ -0,0 +1,27 @@ +(* functor_lib.mli *) +type t + +module F (P : sig + val used_required : t + val unused_required : t +end) : sig + val externally_used : t + val internally_used : t + val unused : t +end + +module InternalParam : sig + val used_required : t + val unused_required : t +end + +module ExternalParam : sig + val used_required : t + val unused_required : t +end + +module InternalApp : sig + val externally_used : t + val internally_used : t + val unused : t +end From e0466fb840d0cb94500fa8f49bc25946f80e1b01 Mon Sep 17 00:00:00 2001 From: Corentin De Souza <9597216+fantazio@users.noreply.github.com> Date: Wed, 25 Feb 2026 15:04:23 +0100 Subject: [PATCH 07/16] [docs][exported_values][7/n] add a function example This shows that functions are analyzed like regular values and that partial application and passing the mas argument is like any other use. --- docs/exported_values/EXPORTED_VALUES.md | 180 ++++++++++++++++++ .../code_constructs/function/Makefile | 12 ++ .../code_constructs/function/function_bin.ml | 6 + .../code_constructs/function/function_lib.ml | 17 ++ .../code_constructs/function/function_lib.mli | 9 + 5 files changed, 224 insertions(+) create mode 100644 docs/exported_values/code_constructs/function/Makefile create mode 100644 docs/exported_values/code_constructs/function/function_bin.ml create mode 100644 docs/exported_values/code_constructs/function/function_lib.ml create mode 100644 docs/exported_values/code_constructs/function/function_lib.mli diff --git a/docs/exported_values/EXPORTED_VALUES.md b/docs/exported_values/EXPORTED_VALUES.md index ee87de6e..20b01bc4 100644 --- a/docs/exported_values/EXPORTED_VALUES.md +++ b/docs/exported_values/EXPORTED_VALUES.md @@ -10,6 +10,7 @@ + [Multiple compilation units](#multiple-compilation-units) + [All Together](#all-together) + [Code constructs](#code-constructs) + + [Function](#function) + [Module](#module) + [Functor](#functor) @@ -680,6 +681,185 @@ The reference takes place in `/tmp/docs/exported_values/code_constructs`, which is a copy of `code_constructs`. Reported locations may differ depending on the location of the source files. +### Function + +This example illustrates that exported functions are exported values. + +The reference files for this example are in the +[function](./code_constructs/function) directory. + +The compilation command is : +``` +make -C function build +``` + +The analysis command is : +``` +make -C function analyze +``` + +The compile + analyze command is : +``` +make -C function +``` + +#### First run + +Code: +```OCaml +(* function_lib.mli *) + +val memoize : f:('a -> 'b) -> 'a -> 'b + +val heavy_computation : 'a -> 'a + +val unused : 'a -> 'a + +val do_nothing : 'a -> unit +``` +```OCaml +(* function_lib.ml *) + +let memoize ~f = + let mem = Hashtbl.create 8 in + function x -> + match Hashtbl.find_opt mem x with + | Some y -> y + | None -> + let y = f x in + Hashtbl.add mem x y; + y + +let heavy_computation x = x + +let unused x = x + +let do_nothing x = () +``` +```OCaml +(* function_bin.ml *) + +let () = + let my_memoized = Function_lib.(memoize ~f:heavy_computation) in + Function_lib.do_nothing (); + assert (my_memoized 42 = my_memoized 42) +``` + +Function values are analyzed like any other value. Hence, passing them as +argument to a function or applying them (even partially) count as uses just like +any other explicit reference. Therefore, `Function_lib`'s `heavy_computation`, +`memoize`, and `do_nothing` are used in `Function_bin`. This leaves +`Function_lib.unused` as the only unused exported value. + + +Compile and analyze : +``` +$ make -C function +make: Entering directory '/tmp/docs/exported_values/code_constructs/function' +ocamlopt -w +27+32 -bin-annot function_lib.mli function_lib.ml function_bin.ml +File "function_lib.ml", line 17, characters 15-16: +17 | let do_nothing x = () + ^ +Warning 27 [unused-var-strict]: unused variable x. +dead_code_analyzer --nothing -E all . +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= +/tmp/docs/exported_values/code_constructs/function/function_lib.mli:7: unused + +Nothing else to report in this section +-------------------------------------------------------------------------------- + + +make: Leaving directory '/tmp/docs/exported_values/code_constructs/function' +``` + +The compiler reports that `do_nothing`'s parameter `x` is unused via a +warning 27. This can be easily fixed by prefixing the name with an underscore +`_` or even replacing the name by an underscore. Depending on the context, other +solutions may be considered, such as removing the parameter, or fixing the +argument's type to `unit`. + +> [!NOTE] +> The warning 27 is off by default. It is enabled by passing the `-w +27` +> argument to the compiler + +As expected, the only unused exported value reported by the analyzer is +`unused` in `function_lib.mli`. + +#### Removing the unused values + +The warning 27 is fixed by updating `do_nothing`'s type to `unit -> unit` and +its parameter `x` to `()`. + +Code: +```OCaml +(* function_lib.mli *) + +val memoize : f:('a -> 'b) -> 'a -> 'b + +val heavy_computation : 'a -> 'a + +val do_nothing : unit -> unit +``` +```OCaml +(* function_lib.ml *) + +let memoize ~f = + let mem = Hashtbl.create 8 in + function x -> + match Hashtbl.find_opt mem x with + | Some y -> y + | None -> + let y = f x in + Hashtbl.add mem x y; + y + +let heavy_computation x = x + +let unused x = x + +let do_nothing () = () +``` +```OCaml +(* function_bin.ml *) + +let () = + let my_memoized = Function_lib.(memoize ~f:heavy_computation) in + Function_lib.do_nothing (); + assert (my_memoized 42 = my_memoized 42) +``` + +Compile and analyze : +``` +$ make -C function +make: Entering directory '/tmp/docs/exported_values/code_constructs/function' +ocamlopt -w +27+32 -bin-annot function_lib.mli function_lib.ml function_bin.ml +File "function_lib.ml", line 15, characters 4-10: +15 | let unused x = x + ^^^^^^ +Warning 32 [unused-value-declaration]: unused value unused. +dead_code_analyzer --nothing -E all . +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= + +Nothing else to report in this section +-------------------------------------------------------------------------------- + + +make: Leaving directory '/tmp/docs/exported_values/code_constructs/function' +``` + +Now that `unused` is unexported, the compiler can report it as unused via the +warning 32, and the analyzer does not report anaything. Removing that value +fixes all the warnings. Our work here is done. + + ### Module This example illustrates a simple case of exported values in submodules. diff --git a/docs/exported_values/code_constructs/function/Makefile b/docs/exported_values/code_constructs/function/Makefile new file mode 100644 index 00000000..1487fe4c --- /dev/null +++ b/docs/exported_values/code_constructs/function/Makefile @@ -0,0 +1,12 @@ +SRC:=function_lib.mli function_lib.ml function_bin.ml + +all: build analyze + +build: + ocamlopt -w +27+32 -bin-annot ${SRC} + +analyze: + dead_code_analyzer --nothing -E all . + +clean: + rm -f *.cm* *.o a.out diff --git a/docs/exported_values/code_constructs/function/function_bin.ml b/docs/exported_values/code_constructs/function/function_bin.ml new file mode 100644 index 00000000..7aa027dc --- /dev/null +++ b/docs/exported_values/code_constructs/function/function_bin.ml @@ -0,0 +1,6 @@ +(* function_bin.ml *) + +let () = + let my_memoized = Function_lib.(memoize ~f:heavy_computation) in + Function_lib.do_nothing (); + assert (my_memoized 42 = my_memoized 42) diff --git a/docs/exported_values/code_constructs/function/function_lib.ml b/docs/exported_values/code_constructs/function/function_lib.ml new file mode 100644 index 00000000..89be8141 --- /dev/null +++ b/docs/exported_values/code_constructs/function/function_lib.ml @@ -0,0 +1,17 @@ +(* function_lib.ml *) + +let memoize ~f = + let mem = Hashtbl.create 8 in + function x -> + match Hashtbl.find_opt mem x with + | Some y -> y + | None -> + let y = f x in + Hashtbl.add mem x y; + y + +let heavy_computation x = x + +let unused x = x + +let do_nothing x = () diff --git a/docs/exported_values/code_constructs/function/function_lib.mli b/docs/exported_values/code_constructs/function/function_lib.mli new file mode 100644 index 00000000..7792b0fe --- /dev/null +++ b/docs/exported_values/code_constructs/function/function_lib.mli @@ -0,0 +1,9 @@ +(* function_lib.mli *) + +val memoize : f:('a -> 'b) -> 'a -> 'b + +val heavy_computation : 'a -> 'a + +val unused : 'a -> 'a + +val do_nothing : 'a -> unit From 71d3f00453a05349fe99e6ee1f818fe82f6eadc6 Mon Sep 17 00:00:00 2001 From: Corentin De Souza <9597216+fantazio@users.noreply.github.com> Date: Sat, 28 Feb 2026 17:11:20 +0100 Subject: [PATCH 08/16] [docs][exported_values][8/n] add a modtyp example It demonstrates the current limitation of tracking exported values in module types. --- docs/exported_values/EXPORTED_VALUES.md | 153 ++++++++++++++++++ .../code_constructs/modtyp/Makefile | 12 ++ .../code_constructs/modtyp/modtyp_bin.ml | 6 + .../code_constructs/modtyp/modtyp_lib.ml | 25 +++ .../code_constructs/modtyp/modtyp_lib.mli | 20 +++ 5 files changed, 216 insertions(+) create mode 100644 docs/exported_values/code_constructs/modtyp/Makefile create mode 100644 docs/exported_values/code_constructs/modtyp/modtyp_bin.ml create mode 100644 docs/exported_values/code_constructs/modtyp/modtyp_lib.ml create mode 100644 docs/exported_values/code_constructs/modtyp/modtyp_lib.mli diff --git a/docs/exported_values/EXPORTED_VALUES.md b/docs/exported_values/EXPORTED_VALUES.md index 20b01bc4..88c62e42 100644 --- a/docs/exported_values/EXPORTED_VALUES.md +++ b/docs/exported_values/EXPORTED_VALUES.md @@ -13,6 +13,7 @@ + [Function](#function) + [Module](#module) + [Functor](#functor) + + [Module type](#module-type) # Exported Values @@ -1343,3 +1344,155 @@ same compiler warning as for `InternalParam.unused_required`, so it can be removed from both the interface and the implementation. The unused values can be removed as explained. Our work here is done. + +### Module Type + +This example illustrates a simple case of exported values in module types. + +The reference files for this example are in the +[modtyp](./code_constructs/modtyp) directory. + +The compilation command is : +``` +make -C modtyp build +``` + +The analysis command is : +``` +make -C modtyp analyze +``` + +The compile + analyze command is : +``` +make -C modtyp +``` + +> [!IMPORTANT] +> **LIMITATION** +> In order to reduce noise (false positives and duplication) in the results, +> the analyzer currently ignores values exported by module types +> (see [issue #50](https://github.com/LexiFi/dead_code_analyzer/issues/50)). + +#### First run + +Code: +```OCaml +(* modtyp_lib.mli *) +module type T = sig + type t + val externally_used : t + val internally_used : t + val unused : t +end + +module M_reuse : T + +module M_constr : T with type t = unit + +module M_subst : T with type t := unit + +module M_redef : sig + type t + val externally_used : t + val internally_used : t + val unused : t +end +``` +```OCaml +(* modtyp_lib.ml *) +module type T = sig + type t + val externally_used : t + val internally_used : t + val unused : t +end + +module M = struct + type t = unit + let externally_used = () + let internally_used = () + let unused = () + let unused_unexported = () +end + +let () = M.internally_used + +module M_reuse = M + +module M_constr = M + +module M_subst = M + +module M_redef = M +``` +```OCaml +(* modtyp_bin.ml *) +let () = + ignore Modtyp_lib.M_reuse.externally_used; + ignore Modtyp_lib.M_constr.externally_used; + ignore Modtyp_lib.M_subst.externally_used; + ignore Modtyp_lib.M_redef.externally_used +``` + +Once again, let's look at the code before diving into the compilation and +analysis. Here the `Modtyp_lib` exports 1 module type `T` and 4 modules : +`M_reuse`, `M_constr`, `M_subst`, and `M_redef`. Of these 4 modules, the first +3 have `T` as signature (with minor twists), while the last one has its own +explicit signature, which is a copy of `T`. In this way, `M_redef` is equivalent +to the [module](#module) example's `Module_lib.M` module : it exposes exactly +the same information. +Each of the modules exposed by `Modtyp_lib` are used exactly in the same way : +their `externally_used` values are explicitly referenced in `Modtyp_bin`. + +One could naturally expect that all the exported values are reported +except for the `externally_used`. However, reporting e.g. `M_subst.internally_used` +as unused would not be really actionable. In reality, this value is explicitly +declared by `T`. +Fixing an unused value reported in a module using a module type as signature +would require either removing the value from the module type (if possible), +or explicilty describing the signature of the module, effectively losing the +benefits of using the module type. Thus, reporting unused values for the module +itself would be counterproductive. + +An actionable report would be that of the value in the module type itself, +if it is unused by all the modules of that module type (as it is the case here +for `T.unused`). Currenyl, and as described in the introduction of this example, +the values exported by module types are ignored by the analyzeri, and, +consequently, are not reported. + +Now that we have explained what the expected behavior of the analyzer should be, +let's look at its results on the code above. + +Compile and analyze : +``` +$ make -C modtyp +make: Entering directory '/tmp/docs/exported_values/code_constructs/modtyp' +ocamlopt -w +32 -bin-annot modtyp_lib.mli modtyp_lib.ml modtyp_bin.ml +File "modtyp_lib.ml", line 14, characters 6-23: +14 | let unused_unexported = () + ^^^^^^^^^^^^^^^^^ +Warning 32 [unused-value-declaration]: unused value unused_unexported. +dead_code_analyzer --nothing -E all . +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= +/tmp/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:18: M_redef.internally_used +/tmp/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:19: M_redef.unused + +Nothing else to report in this section +-------------------------------------------------------------------------------- + + +make: Leaving directory '/tmp/docs/exported_values/code_constructs/modtyp' +``` + +As in the module example, the compiler detects that `unused_unexported` is unused. + +As in the module example, the analyzer reports `M_redef.internally_used` and +`M_redef.unused` as unused exported values. + +All the reports are similar to those of the [module example](#module). +The exploration iand resolution of that example can be applied here identically. +Our work here is done. diff --git a/docs/exported_values/code_constructs/modtyp/Makefile b/docs/exported_values/code_constructs/modtyp/Makefile new file mode 100644 index 00000000..5b9b6dd0 --- /dev/null +++ b/docs/exported_values/code_constructs/modtyp/Makefile @@ -0,0 +1,12 @@ +SRC:=modtyp_lib.mli modtyp_lib.ml modtyp_bin.ml + +all: build analyze + +build: + ocamlopt -w +32 -bin-annot ${SRC} + +analyze: + dead_code_analyzer --nothing -E all . + +clean: + rm -f *.cm* *.o a.out diff --git a/docs/exported_values/code_constructs/modtyp/modtyp_bin.ml b/docs/exported_values/code_constructs/modtyp/modtyp_bin.ml new file mode 100644 index 00000000..61a25ead --- /dev/null +++ b/docs/exported_values/code_constructs/modtyp/modtyp_bin.ml @@ -0,0 +1,6 @@ +(* modtyp_bin.ml *) +let () = + ignore Modtyp_lib.M_reuse.externally_used; + ignore Modtyp_lib.M_constr.externally_used; + ignore Modtyp_lib.M_subst.externally_used; + ignore Modtyp_lib.M_redef.externally_used diff --git a/docs/exported_values/code_constructs/modtyp/modtyp_lib.ml b/docs/exported_values/code_constructs/modtyp/modtyp_lib.ml new file mode 100644 index 00000000..c0bec72e --- /dev/null +++ b/docs/exported_values/code_constructs/modtyp/modtyp_lib.ml @@ -0,0 +1,25 @@ +(* modtyp_lib.ml *) +module type T = sig + type t + val externally_used : t + val internally_used : t + val unused : t +end + +module M = struct + type t = unit + let externally_used = () + let internally_used = () + let unused = () + let unused_unexported = () +end + +let () = M.internally_used + +module M_reuse = M + +module M_constr = M + +module M_subst = M + +module M_redef = M diff --git a/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli b/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli new file mode 100644 index 00000000..4027f7e1 --- /dev/null +++ b/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli @@ -0,0 +1,20 @@ +(* modtyp_lib.mli *) +module type T = sig + type t + val externally_used : t + val internally_used : t + val unused : t +end + +module M_reuse : T + +module M_constr : T with type t = unit + +module M_subst : T with type t := unit + +module M_redef : sig + type t + val externally_used : t + val internally_used : t + val unused : t +end From 7f31425ee55fa034af76351bf93dcfa0c449939e Mon Sep 17 00:00:00 2001 From: Corentin De Souza <9597216+fantazio@users.noreply.github.com> Date: Thu, 5 Mar 2026 15:15:45 +0100 Subject: [PATCH 09/16] [docs][exported_values][9/n] add a modsig example It demonstrates the nuances of tracking exported values in aliases with and without signatures. --- docs/exported_values/EXPORTED_VALUES.md | 166 ++++++++++++++++++ .../code_constructs/modsig/Makefile | 12 ++ .../code_constructs/modsig/modsig_bin.ml | 5 + .../code_constructs/modsig/modsig_lib.ml | 13 ++ 4 files changed, 196 insertions(+) create mode 100644 docs/exported_values/code_constructs/modsig/Makefile create mode 100644 docs/exported_values/code_constructs/modsig/modsig_bin.ml create mode 100644 docs/exported_values/code_constructs/modsig/modsig_lib.ml diff --git a/docs/exported_values/EXPORTED_VALUES.md b/docs/exported_values/EXPORTED_VALUES.md index 88c62e42..96401718 100644 --- a/docs/exported_values/EXPORTED_VALUES.md +++ b/docs/exported_values/EXPORTED_VALUES.md @@ -14,6 +14,7 @@ + [Module](#module) + [Functor](#functor) + [Module type](#module-type) + + [Module signature](#module-signature) # Exported Values @@ -1496,3 +1497,168 @@ As in the module example, the analyzer reports `M_redef.internally_used` and All the reports are similar to those of the [module example](#module). The exploration iand resolution of that example can be applied here identically. Our work here is done. + +### Module Signature + +This example illustrates a simple case of exported values in module signatures. + +The reference files for this example are in the +[modsig](./code_constructs/modsig) directory. + +The compilation command is : +``` +make -C modsig build +``` + +The analysis command is : +``` +make -C modsig analyze +``` + +The compile + analyze command is : +``` +make -C modsig +``` + +#### First run + +Code: +```OCaml +(* modsig_lib.ml *) +module Original = struct + let used_directly = () + let used_indirectly = () + let used_by_requirement = () + let unused = () +end + +module Alias_without_sig = Original + +module Alias_with_sig : sig + val used_by_requirement : unit +end = Original +``` +```OCaml +(* modsig_bin.ml *) +let () = + let open Modsig_lib in + Original.used_directly; + Alias_without_sig.used_indirectly +``` + +Before looking at the analysis results, let's look at the code. + +The `Modsig_lib` compilation unit does not have a `.mli`, so all the internal +uses are accounted for. It exposes 3 modules : `Original`, `Alias_without_sig`, +and `Alias_with_sig`. Only the first module actually defines values. The second +one is an trivial alias, and the third is an alias with an explicit signature. +Because the 2 latter modules are aliases for the `Original` module, one could +expect that the values exposed by them are unified with the ones in `Original`. +This would imply that only the values in `Original` could be reported as unused. +This reasoning is partially true. + +As explained in the [module type example](#module-type), a report on a value in +`Alias_without_sig` would not be trivially solved, but require removing the +value from `Original` itself if possible or add an explicit signature to +unexport the unused value. Thus, it is the case for `Alias_without_sig` that +its values are unified with those of `Original`, and, consequently, that they +cannot be reported as unused. Only the values in `Original` can be reported in this case. +However, `Alias_with_sig` has an explicit signature, which means 2 things: +1. it controls what it exports, thus a reporting values in that module is + trivially actionable by removing the reported values from the signature +2. it has requirements, thus all the values in `Original` that are expected in + the signature of `Alias_with_sig` are considered used. + +Now that we have explained the nuances introduced by the existence of an +explicit module signature, let's look at the results. + +Compile and analyze: +``` +$ make -C modsig +make: Entering directory '/tmp/docs/exported_values/code_constructs/modsig' +ocamlopt -bin-annot modsig_lib.ml modsig_bin.ml +dead_code_analyzer --nothing -E all . +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= +/tmp/docs/exported_values/code_constructs/modsig/modsig_lib.ml:6: Original.unused +/tmp/docs/exported_values/code_constructs/modsig/modsig_lib.ml:12: Alias_with_sig.used_by_requirement + +Nothing else to report in this section +-------------------------------------------------------------------------------- + + +make: Leaving directory '/tmp/docs/exported_values/code_constructs/modsig' +``` + +The compiler does not report any unused value. + +The analyzer detects that `Original.unused` and +`Alias_with_sig.used_by_requirement` are unused. As expected, it does not report +any unused value in `Alias_without_sig`. +Let's look more closely at the values and their uses. +- `Original.used_directly` is explicitly referenced in `Modsig_bin` +- `Original.used_indirectly` is used by an explicit reference to + `Alias_without_sig.used_indirectly` in `Modsig_bin` +- `Original.used_by_requirement` is used by requirement to fulfill + `Alias_with_sig`'s signature +- `Original.unused` is not referenced nor required anywhere +- `Alias_without_sig` does not "own" any value +- `Alias_with_sig.used_by_requirement` is not referenced nor required anywhere + +#### Removing the unused values + +The reported values can be removed : `Original.unused` is removed from the +module's strucutre because it does not have an explicit signature, and +`Alias_with_sig.used_by_requirement` is removed from the module's signature. + +Code: +```OCaml +(* modsig_lib.ml *) +module Original = struct + let used_directly = () + let used_indirectly = () + let used_by_requirement = () +end + +module Alias_without_sig = Original + +module Alias_with_sig : sig end = Original +``` +```OCaml +(* modsig_bin.ml *) +let () = + let open Modsig_lib in + Original.used_directly; + Alias_without_sig.used_indirectly +``` + +Compile and analyze: +``` +$ make -C modsig +make: Entering directory '/tmp/docs/exported_values/code_constructs/modsig' +ocamlopt -bin-annot modsig_lib.ml modsig_bin.ml +dead_code_analyzer --nothing -E all . +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= +/tmp/docs/exported_values/code_constructs/modsig/modsig_lib.ml:5: Original.used_by_requirement + +Nothing else to report in this section +-------------------------------------------------------------------------------- + + +make: Leaving directory '/tmp/docs/exported_values/code_constructs/modsig' +``` + +The compiler does not report any unused value. + +The analyzer now detects that `Original.used_by_requirement` is unused. Indeed, +by removing `used_by_requirement` from the signature of `Alias_with_sig` we +removed the requirement for `Original` to provide it. This value can be removed +from `Orignal`, and neither the compiler nor the analyzer will report unused +values anymore. Our work here is done. diff --git a/docs/exported_values/code_constructs/modsig/Makefile b/docs/exported_values/code_constructs/modsig/Makefile new file mode 100644 index 00000000..109a96da --- /dev/null +++ b/docs/exported_values/code_constructs/modsig/Makefile @@ -0,0 +1,12 @@ +SRC:=modsig_lib.ml modsig_bin.ml + +all: build analyze + +build: + ocamlopt -bin-annot ${SRC} + +analyze: + dead_code_analyzer --nothing -E all . + +clean: + rm -f *.cm* *.o a.out diff --git a/docs/exported_values/code_constructs/modsig/modsig_bin.ml b/docs/exported_values/code_constructs/modsig/modsig_bin.ml new file mode 100644 index 00000000..971d3e6c --- /dev/null +++ b/docs/exported_values/code_constructs/modsig/modsig_bin.ml @@ -0,0 +1,5 @@ +(* modsig_bin.ml *) +let () = + let open Modsig_lib in + Original.used_directly; + Alias_without_sig.used_indirectly diff --git a/docs/exported_values/code_constructs/modsig/modsig_lib.ml b/docs/exported_values/code_constructs/modsig/modsig_lib.ml new file mode 100644 index 00000000..31329a04 --- /dev/null +++ b/docs/exported_values/code_constructs/modsig/modsig_lib.ml @@ -0,0 +1,13 @@ +(* modsig_lib.ml *) +module Original = struct + let used_directly = () + let used_indirectly = () + let used_by_requirement = () + let unused = () +end + +module Alias_without_sig = Original + +module Alias_with_sig : sig + val used_by_requirement : unit +end = Original From 260c0848c927e73e4a38a23609738e9e6f6f295f Mon Sep 17 00:00:00 2001 From: Corentin De Souza <9597216+fantazio@users.noreply.github.com> Date: Thu, 5 Mar 2026 18:09:20 +0100 Subject: [PATCH 10/16] [docs][exported_values][10/n] add an include example It demonstrates the absence of tracking for values coming from `include`. --- docs/exported_values/EXPORTED_VALUES.md | 160 ++++++++++++++++++ .../code_constructs/include/Makefile | 12 ++ .../code_constructs/include/include_bin.ml | 6 + .../code_constructs/include/include_lib.ml | 16 ++ 4 files changed, 194 insertions(+) create mode 100644 docs/exported_values/code_constructs/include/Makefile create mode 100644 docs/exported_values/code_constructs/include/include_bin.ml create mode 100644 docs/exported_values/code_constructs/include/include_lib.ml diff --git a/docs/exported_values/EXPORTED_VALUES.md b/docs/exported_values/EXPORTED_VALUES.md index 96401718..72d2eafc 100644 --- a/docs/exported_values/EXPORTED_VALUES.md +++ b/docs/exported_values/EXPORTED_VALUES.md @@ -15,6 +15,7 @@ + [Functor](#functor) + [Module type](#module-type) + [Module signature](#module-signature) + + [Include](#include) # Exported Values @@ -1662,3 +1663,162 @@ by removing `used_by_requirement` from the signature of `Alias_with_sig` we removed the requirement for `Original` to provide it. This value can be removed from `Orignal`, and neither the compiler nor the analyzer will report unused values anymore. Our work here is done. + +### Include + +The reference files for this example are in the +[include](./code_constructs/include) directory. + +The compilation command is : +``` +make -C include build +``` + +The analysis command is : +``` +make -C include analyze +``` + +The compile + analyze command is : +``` +make -C include +``` + +#### First run + +Code: +```OCaml +(* include_lib.ml *) +module Original = struct + let used_directly = () + let used_indirectly = () + let unused = () +end + +module Reexport = struct + include Original +end + +module Redefine = struct + include Original + let used_directly = () + let unused = () +end +``` +```OCaml +(* include_bin.ml *) +let () = + let open Include_lib in + ignore Original.used_directly; + ignore Reexport.used_indirectly; + ignore Redefine.used_directly; +``` + +Before looking at the analysis results, let's look at the code. + +The `Include_lib` compilation unit does not have a `.mli`, so all the internal +uses are accounted for. It exposes 3 modules : `Original`, `Reexport`, and +`Redefine`. The 1st one defines all its values, the 2nd only includes the 1st +one, and the 3rd one includes the 1st and redefines 2 values : `used_directly`, +and `unused`. +By the explanation in the [module signature](#module-signature) and +[module type](#module-type) examples, although there are 9 exported values +(`used_directly`, `used_indirectly`, and `unused` for each module), only 5 are +expected to be tracked by the analyzer : those in `Original` and the 2 redefined +in `Redefine`. These are the only values a developer can trivially removeif +they are reported unused. +Thus, the only values used are `Original.used_directly`, +`Original.used_indirectly` (by an explicit reference to +`Reexport.used_indirectly`), and `Redefine.used_directly`. This means that the +unused exported values tracked by the analyzer are `Original.unused` and +`Redefine.unused`. + +Let's look at the actual results. + +Compile and analyze: +``` +$ make -C include +make: Entering directory '/tmp/docs/exported_values/code_constructs/include' +ocamlopt -bin-annot include_lib.ml include_bin.ml +dead_code_analyzer --nothing -E all . +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= +/tmp/docs/exported_values/code_constructs/include/include_lib.ml:5: Original.unused +/tmp/docs/exported_values/code_constructs/include/include_lib.ml:5: Reexport.unused +/tmp/docs/exported_values/code_constructs/include/include_lib.ml:15: Redefine.unused + +Nothing else to report in this section +-------------------------------------------------------------------------------- + + +make: Leaving directory '/tmp/docs/exported_values/code_constructs/include' +``` + +The compiler does not report any unused value. + +As expected, the analyzer reports `Original.unused` and `Redefine.unused`. +However, it also reports `Reexport.unused`, which is unexpected. + +> [!WARNING] +> The extra report on `Reexport.unused` is a known bug, tracked by +> [issue #57](https://github.com/LexiFi/dead_code_analyzer/issues/57). +> This duplicated report only exists because the modules `Original` and +> `Reexport` belong to the same compilation unit (`Include_lib`). This can +> easily be verified by moving `Reexport` in `Include_bin` instead. + +Because the report of `Reexport.unused` is actually a duplicate of the report +of `Original.unused`, we can simply ignore it. + +#### Removing the unused values + +The reported values can be removed from the implementation. + +Code: +```OCaml +(* include_lib.ml *) +module Original = struct + let used_directly = () + let used_indirectly = () +end + +module Reexport = struct + include Original +end + +module Redefine = struct + include Original + let used_directly = () +end +``` +```OCaml +(* include_bin.ml *) +let () = + let open Include_lib in + ignore Original.used_directly; + ignore Reexport.used_indirectly; + ignore Redefine.used_directly; +``` + +Compile and analyze: +``` +$ make -C include +make: Entering directory '/tmp/docs/exported_values/code_constructs/include' +ocamlopt -bin-annot include_lib.ml include_bin.ml +dead_code_analyzer --nothing -E all . +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= + +Nothing else to report in this section +-------------------------------------------------------------------------------- + + +make: Leaving directory '/tmp/docs/exported_values/code_constructs/include' +``` + +Now, neither the compiler nor the analyzer report any unused value. Our work here is done. diff --git a/docs/exported_values/code_constructs/include/Makefile b/docs/exported_values/code_constructs/include/Makefile new file mode 100644 index 00000000..44b93352 --- /dev/null +++ b/docs/exported_values/code_constructs/include/Makefile @@ -0,0 +1,12 @@ +SRC:=include_lib.ml include_bin.ml + +all: build analyze + +build: + ocamlopt -bin-annot ${SRC} + +analyze: + dead_code_analyzer --nothing -E all . + +clean: + rm -f *.cm* *.o a.out diff --git a/docs/exported_values/code_constructs/include/include_bin.ml b/docs/exported_values/code_constructs/include/include_bin.ml new file mode 100644 index 00000000..563106cc --- /dev/null +++ b/docs/exported_values/code_constructs/include/include_bin.ml @@ -0,0 +1,6 @@ +(* include_bin.ml *) +let () = + let open Include_lib in + ignore Original.used_directly; + ignore Reexport.used_indirectly; + ignore Redefine.used_directly; diff --git a/docs/exported_values/code_constructs/include/include_lib.ml b/docs/exported_values/code_constructs/include/include_lib.ml new file mode 100644 index 00000000..8e41947c --- /dev/null +++ b/docs/exported_values/code_constructs/include/include_lib.ml @@ -0,0 +1,16 @@ +(* include_lib.ml *) +module Original = struct + let used_directly = () + let used_indirectly = () + let unused = () +end + +module Reexport = struct + include Original +end + +module Redefine = struct + include Original + let used_directly = () + let unused = () +end From 4d963c9aff45d25b5920c87e3cb4576249a32b26 Mon Sep 17 00:00:00 2001 From: Corentin De Souza Date: Tue, 10 Mar 2026 09:33:39 +0100 Subject: [PATCH 11/16] [docs][exported_values][11/n] fix typos and wordings Apply suggestions from code review --- docs/USER_DOC.md | 2 +- docs/exported_values/EXPORTED_VALUES.md | 248 ++++++++++++------------ 2 files changed, 122 insertions(+), 128 deletions(-) diff --git a/docs/USER_DOC.md b/docs/USER_DOC.md index 7869813b..0fff3c01 100644 --- a/docs/USER_DOC.md +++ b/docs/USER_DOC.md @@ -53,7 +53,7 @@ not related to the _use_ of elements of code. This documentation is split accross different topics: - [Usage](USAGE.md) describes the usage of the `dead_code_analyzer` and its options. -- [Exported Values](exported_values/EXPORTED_VALUES.md) +- [Exported Values](exported_values/EXPORTED_VALUES.md) describes the semantics and usage of the "unused exported values" report section, and provides examples. ## Footnotes diff --git a/docs/exported_values/EXPORTED_VALUES.md b/docs/exported_values/EXPORTED_VALUES.md index 72d2eafc..9e5023d7 100644 --- a/docs/exported_values/EXPORTED_VALUES.md +++ b/docs/exported_values/EXPORTED_VALUES.md @@ -81,10 +81,10 @@ Unused exported values are reported by default. Their reports can be deactivated by using the `--nothing` or `-E nothing` command line arguments. They can be reactivated by using the `--all` or `-E all` command line arguments. -For more detail on the command line arguments see [the more general Usage +For more details about the command line arguments see [the more general Usage documentation](../USAGE.md). -The report section title is `.> UNUSED EXPORTED VALUES:`. +The report section looks like: The expected resolution for an unused exported value is to remove it from the `.mli` if there is one, or the `.ml` otherwise. @@ -127,7 +127,7 @@ without any external use. The reference file for this example is [`hello_world_no_intf.ml`](./hello_world/hello_world_no_intf.ml). -The compilation command to produce the `hello_world_no_intf.cmi` and +The compilation command to produce `hello_world_no_intf.cmi` and `hello_world_no_intf.cmt` is : ``` ocamlopt -bin-annot hello_world_no_intf.ml @@ -153,14 +153,22 @@ let () = print_endline hello_world ``` -Compile and analyze : +Compile : ``` $ ocamlopt -bin-annot hello_world_no_intf.ml File "hello_world_no_intf.ml", line 8, characters 6-19: 8 | let goodbye_world = goodbye ^ world in ^^^^^^^^^^^^^ Warning 26 [unused-var]: unused variable goodbye_world. +``` +The compiler reports a warning 26 on `goodbye_world`: +`Warning 26 [unused-var]: unused variable goodbye_world.` +This tells us that the _local_ value is unused, and, thus, can be removed at the +reported location: `File "hello_world_no_intf.ml", line 8` + +Analyze : +``` $ dead_code_analyzer --nothing -E all hello_world_no_intf.cmi hello_world_no_intf.cmt Scanning files... [DONE] @@ -172,12 +180,12 @@ Nothing else to report in this section -------------------------------------------------------------------------------- ``` -Here we can see the warning 26 is triggered during the compilation. Hence, the -_local_ value `goodbye_world` at line 8 can be removed. - -The analyzer reports that there is no unused _exported_ value. The 3 exported -values are `hello`, `goodbye` and `world`. They are all referenced internally. -Because there is no `hello_world_no_intf.mli`, the internal uses are accounted for. +The analyzer does not report any unused _exported_ value. There are 3 exported +in the `Hello_world_no_intf` compilation unit : `hello`, `goodbye` and `world`. +These are the top level values of `hello_world_no_intf.ml`. +They are all referenced internally. Because there is no `hello_world_no_intf.mli`, +the internal uses are accounted for. Consequently, none of the exported values +are considered unused by the analyzer. #### Fixing the warning 26 @@ -213,9 +221,10 @@ Nothing else to report in this section The compiler does not report any unused value. -The analyzer, however, detects that `goodbye` declared at line 3 is now unused. - -Like we did with the warning 26, `goodbye` can be removed. +The analyzer reports that `goodbye` declared at line 3 is unused : +`/tmp/docs/exported_values/hello_world/hello_world_no_intf.ml:3: goodbye` +Like the warning 26 above, this report tells us that `goodbye` can be removed +at the reported location. #### Removing the unused `goodbye` @@ -245,19 +254,20 @@ Nothing else to report in this section -------------------------------------------------------------------------------- ``` -Now, neither the compiler nor the analyzer report any unused value. Our work here is done. +Now, neither the compiler nor the analyzer report any unused value. +Our work here is done. ### Single compilation unit with interface -This example illustrates a simple case of a compilation unit with `.mli` and -without any external use. -This is the same as the previous example with an extra interface. +This example is the same as the previous example, with an explicit `.mli`. +Although an interface is provided, all the uses remain inside the same +compilation unit. The reference files for this example are [`hello_world.mli`](./hello_world/hello_world.mli) and [`hello_world.ml`](./hello_world/hello_world.ml) -The compilation command to produce the `hello_world.cmi` and `hello_world.cmt` is : +The compilation command to produce `hello_world.cmi` and `hello_world.cmt` is : ``` ocamlopt -bin-annot hello_world.mli hello_world.ml ``` @@ -310,13 +320,13 @@ Nothing else to report in this section -------------------------------------------------------------------------------- ``` -Like in the previous example, the warning 26 is triggered during the -compilation. Hence, the _local_ value `goodbye_world` at line 8 can be removed. +The compiler reports the same warning 26 on `goodbye_world` as in the previous +example. The analyzer reports that there are 3 unused _exported_ value in `hello_world.mli`: `hello` at line 2, `goodbye` line 3, and `world` line 4. -These are the only exported values in the `hello_world` compilation unit because -they are the only one listed in the `.mli`. They are all used in +These are the only exported values in the `Hello_world` compilation unit because +they are the only ones listed in the `.mli`. They are all used in `hello_world.ml`, but not outside of their compilation unit. Because there is an interface file available, only external uses are accounted for. Thus, they are considered unused and can be dropped from the `.mli` @@ -359,8 +369,8 @@ Now, neither the compiler nor the analyzer report any unused value. We learned from the previous example that the `goodbye` value is unused after we remove `goodbye_world`. This is on the compiler to report it in this example because it is an _unexported_ value here (while it was an _exported_ value in -the previous example). This warning is actually off by default and can be -activated by passing the `-w +32` argument to the compiler : +the previous example). The corresponding warning is actually off by default and +can be activated by passing the `-w +32` argument to the compiler : ``` $ ocamlopt -w +32 hello_world.ml File "hello_world.ml", line 3, characters 4-11: @@ -373,15 +383,15 @@ analyzer will report unused values anymore. Our work here is done. ### Multiple compilation units -This example illustrates a simple case of a library used by a binary. -This is the same as the previous example with an extra indirection. +This example is the same as the previous example, split in 2 separate +compilation units. All the exported values are now used externally. The reference files for this example are [`hello_world_lib.mli`](./hello_world/hello_world_lib.mli), [`hello_world_lib.ml`](./hello_world/hello_world_lib.ml), and [`hello_world_bin.ml`](./hello_world/hello_world_bin.ml) -The compilation command to produce the the necessary `.cmi` and `.cmt` files is : +The compilation command to produce the necessary `.cmi` and `.cmt` files is : ``` ocamlopt -bin-annot hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml ``` @@ -438,10 +448,10 @@ Nothing else to report in this section -------------------------------------------------------------------------------- ``` -Like in the previous example, the warning 26 is triggered during the -compilation. Hence, the _local_ value `goodbye_world` at line 8 can be removed. +The compiler reports the same warning 26 on `goodbye_world` as in the previous +example. -The analyzer reports that there is no unused _exported_ value. The 3 exported +The analyzer does not report any unused _exported_ value. The 3 exported values are `hello`, `goodbye` and `world` in `hello_world_lib.mli`. They are all referenced externally, in `hello_world_bin.ml`. @@ -494,10 +504,7 @@ Nothing else to report in this section The compiler does not report any unused value. -The analyzer, however, detects that `goodbye` declared in `hello_world_lib.mli` -at line 3 is now unused. - -Like we did with the warning 26, `goodbye` can be removed. +The analyzer reports `goodbye` as unused, as in the previous example. #### Unexporting `goodbye` @@ -552,15 +559,13 @@ analyzer will report unused values anymore. Our work here is done. ### All Together -This example illustrates a simple case of a complete codebase with independent -and dependent components, and duplicate names. -This is the grouping of all the previous [*Hello World*](#hello-world) examples. -Analyzing all the files at once reduces the number of iterations to reach a -satisfying codebase. +This example is the grouping of all the previous [*Hello World*](#hello-world) +examples. Analyzing all the files at once reduces the number of iterations to +reach a satisfying codebase. The reference files for this example are all those listed previously. -The compilation command to produce the the necessary `.cmi` and `.cmt` files, +The compilation command to produce the necessary `.cmi` and `.cmt` files, and the desired warnings is the combination of all the previous ones : ``` ocamlopt -w +32 -bin-annot hello_world_no_intf.ml hello_world.mli hello_world.ml hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml @@ -577,12 +582,13 @@ dead_code_analyzer --nothing -E all . ``` > [!TIP] -> As we can see for the compilation command, there is a large number of files to +> As we can see in the compilation command, there is a large number of files to > list. Instead of listing all the `.cmi` and `.cmt` files in the command line, > the analyzer accepts directories as arguments and will analyze all the -> relevant files it can find in them and in their subdirectories. +> relevant files it can find in them. -The code is not re-exposed at each iteration here. It is the same as in the previous examples. +The code is not re-exposed at each iteration here. It is the same as in the +previous examples. #### First Run @@ -605,7 +611,7 @@ File "hello_world_bin.ml", line 5, characters 6-19: Warning 26 [unused-var]: unused variable goodbye_world. ``` -Without any surprise, the compiler identifies the different warning 26 explored +Without any surprise, the compiler reports the warnings 26 explored in the previous examples. Let's fix them and **recompile**, before running the analyzer. @@ -627,10 +633,10 @@ Nothing else to report in this section -------------------------------------------------------------------------------- ``` -The analyzer correclty identifies the unused exported values it identified for +The analyzer correctly reports the unused exported values it reported in the previous examples, all in one run. Note that the reports are in the lexicographical order. -Unlike the compiler which listed its warnings in the order the files appeared in +Unlike the compiler which listed its warnings in the order of the files in the command line, the analyzer always sorts its reports in this order. #### Removing the unused exported values @@ -652,9 +658,9 @@ File "hello_world_lib.ml", line 3, characters 4-11: Warning 32 [unused-value-declaration]: unused value goodbye. ``` -Once again, the finidings are the same as for the previous examples. After +Once again, the warnings are the same as for the previous examples. After `goodbye_world` is removed and `goodbye` is unexported, the compiler warning 32 -indicates that it is unused. +indicates that `goodbye` is unused. Let's fix the warnings. Compile and analyze : @@ -672,7 +678,8 @@ Nothing else to report in this section -------------------------------------------------------------------------------- ``` -Now, neither the compiler nor the analyzer report any unused value. Our work here is done. +Now, neither the compiler nor the analyzer report any unused value. +Our work here is done. ## Code constructs @@ -686,8 +693,6 @@ location of the source files. ### Function -This example illustrates that exported functions are exported values. - The reference files for this example are in the [function](./code_constructs/function) directory. @@ -749,9 +754,9 @@ let () = ``` Function values are analyzed like any other value. Hence, passing them as -argument to a function or applying them (even partially) count as uses just like -any other explicit reference. Therefore, `Function_lib`'s `heavy_computation`, -`memoize`, and `do_nothing` are used in `Function_bin`. This leaves +arguments to a function or applying them (even partially) count as uses just +like any other explicit reference. Therefore, `Function_lib`'s `memoize`, +`heavy_computation`, and `do_nothing` are used in `Function_bin`. This leaves `Function_lib.unused` as the only unused exported value. @@ -785,12 +790,7 @@ warning 27. This can be easily fixed by prefixing the name with an underscore solutions may be considered, such as removing the parameter, or fixing the argument's type to `unit`. -> [!NOTE] -> The warning 27 is off by default. It is enabled by passing the `-w +27` -> argument to the compiler - -As expected, the only unused exported value reported by the analyzer is -`unused` in `function_lib.mli`. +As expected, the analyzer only reports `unused`, declared in `function_lib.mli`. #### Removing the unused values @@ -858,15 +858,13 @@ Nothing else to report in this section make: Leaving directory '/tmp/docs/exported_values/code_constructs/function' ``` -Now that `unused` is unexported, the compiler can report it as unused via the -warning 32, and the analyzer does not report anaything. Removing that value +Now that `unused` is unexported, the compiler reports it as unused via the +warning 32, and the analyzer does not report anything. Removing that value fixes all the warnings. Our work here is done. ### Module -This example illustrates a simple case of exported values in submodules. - The reference files for this example are in the [module](./code_constructs/module) directory. @@ -916,7 +914,8 @@ let () = ``` Before looking at the analysis results, let's look at the code. -Here, all the values of `Module_lib.M` are exported except for + +All the values of `Module_lib.M` are exported except for `unused_unexported`. Among the exported values, `unused` is not referenced anywhere, `internally_used` is only referenced within its compilation unit (`Module_lib`), and `externally_used` is only referenced outside of it. @@ -950,14 +949,14 @@ Nothing else to report in this section make: Leaving directory '/tmp/docs/exported_values/code_constructs/module' ``` -The compiler detects that `unused_unexported` is unused. +The compiler reports that `unused_unexported` is unused. The analyzer reports `M.internally_used` and `M.unused` as unused. Notice how it did not only report the name of the value but its full path within its compilation unit. > [!NOTE] -> `M.internally_used` is only used withing its compialtion unit. Because its is +> `M.internally_used` is only used within its compilation unit. Because it is > declared in a `.mli`, only external uses are accounted for. > It is left as an exercise to the reader to explore this example without > `module_lib.mli` @@ -1012,12 +1011,11 @@ Nothing else to report in this section make: Leaving directory '/tmp/docs/exported_values/code_constructs/module' ``` -The compiler reports `unused` as unused and the analyzer does not report anything. Removing that value fixes all the warnings. Our work here is done. +The compiler reports `unused` as unused and the analyzer does not report +anything. Removing that value fixes all the warnings. Our work here is done. ### Functor -This example illustrates a simple case of exported values in functors. - The reference files for this example are in the [functor](./code_constructs/functor) directory. @@ -1121,7 +1119,7 @@ The `Functor_bin` compilation unit exports 1 module : `ExternalApp`, which is the result of applying `Functor_lib.F` outside its compilation unit. Among all the exported values, the only explicit references accounted for are -those of `InternalApp.externally_used` and `External_app.externally_used` in +`InternalApp.externally_used` and `External_app.externally_used` in `Functor_bin`. Additionally, some values are used by requirement. Because `InternalParam` and @@ -1129,7 +1127,7 @@ Additionally, some values are used by requirement. Because `InternalParam` and `unused_required` are used by requirement to fulfill the signature of `F`'s parameter `P`. -With those observations in mind, let's see what the compiler and the anlyzer +With those observations in mind, let's see what the compiler and the analyzer report. Compile and analyze: @@ -1166,18 +1164,18 @@ Nothing else to report in this section make: Leaving directory '/tmp/docs/exported_values/code_constructs/functor' ``` -The compiler tells us that `unused_required` is unused in the implementation of -`F`. It also reports `unexported_unused` in `F` like it did in the -[module example](#module) for regular modules. These 2 values can be removed at -the indicated locations. +> The compiler reports 2 unused values, that can be removed at the reported locations : +> - `unused_required`, defined by `P`, the parameter of `F`; +> - `unexported_unused`, defined by `F`, like it did in the [module](#module) example. -The analyzer reports 6 unused exported values, all in the `functor_lib.mli` : +The analyzer reports 6 unused exported values, all in `functor_lib.mli` : 2 in `F`, 2 in `InternalParam`, and 2 in `InternalApp`. Let's observe them by module (in reverse order) : -- The reports for `InternalApp` are identical to the [module example](#module). -Although, `InternalApp` is the result of applying `F`, it has an explicit -signature. I.e. it exposes its own values and the link between them and those of +- The reports for `InternalApp` are identical to the [module](#module) example. +Although, `InternalApp` is implemented as the result of applying `F` in +`functor_lib.ml`, its signature is independent of `F` in `functor_lib.mli`. +I.e. it exposes its own values and the link between them and those of `F` is absent from the signature. Consequently, they are tracked independently. - As we observed before runnning the analyzer, the values in `InternalParam` are @@ -1185,10 +1183,6 @@ used by requirement. However, this use is internal to `Functor_lib`, and there is an interface available : `functor_lib.mli`. Consequently, the internal uses are ignored, and `InternalParam`'s values become unused. -> [!NOTE] -> The kind of use (explicit reference, or by requirement) has no influence on -> the tracking mode (external only, or internal + external). - - `F` is a functor but is tracked like a regular module. The reported values are those of its result module. Reporting on those may feel like duplicates, but, as explained for the reports of `InternalApp`, the values of the result @@ -1199,16 +1193,16 @@ All the values reported by the analyzer can be safely removed. Before moving on, there is another observation that we can make : the values `unused` and `internally_used` of `ExternalApp` are not reported. -Because they are reported for `InternalApp` and `F`, it would be natural to -expect them in the reports for `ExternalApp` as well. In reality, they are not +Because they are reported for `InternalApp` and `F`, one could +expect them to be reported for `ExternalApp` as well. In reality, they are not tracked individually for `ExternalApp` because it does not expose them explicitly. Unlike `InternalApp` which has an explicit module signature, `ExternalApp` does not. Consequently, its values are directly linked to those of -`F`. This situation will be explored in the +`F`. This situation is explored in the [module signature](#module-signature) example. > [!TIP] -> If we activated the compiler warning 67 `unused-functor-parameter`(by +> If we activated the compiler warning 67 `unused-functor-parameter` (by > passing the argument `-w +67`), then the compiler would have reported : > ``` > File "functor_lib.mli", line 4, characters 10-11: @@ -1239,11 +1233,11 @@ keep them or remove them from their specifications. Neither would be reported by the compiler or the analyzer. > [!NOTE] -> Functors are covariant in their parameters. I.e. it is allowed to give a -> larger module as argument than what the parameter specifies. -> Similarly it is allowed to declare the parameter larger in the interface than -> it is in the implementation. Consequently, the compiler would not complain if -> `P` would expect `unused_required` in the `.mli` but not in the `.ml`. +> It is allowed to give a larger module as argument than what the parameter +> specifies. Similarly it is allowed to declare the parameter larger in the +> interface than it is in the implementation. Consequently, the compiler would +> not complain if `P` expected `unused_required` in the `.mli` but not in the +> `.ml`. Code: ```OCaml @@ -1349,8 +1343,6 @@ The unused values can be removed as explained. Our work here is done. ### Module Type -This example illustrates a simple case of exported values in module types. - The reference files for this example are in the [modtyp](./code_constructs/modtyp) directory. @@ -1371,6 +1363,7 @@ make -C modtyp > [!IMPORTANT] > **LIMITATION** +> > In order to reduce noise (false positives and duplication) in the results, > the analyzer currently ignores values exported by module types > (see [issue #50](https://github.com/LexiFi/dead_code_analyzer/issues/50)). @@ -1436,19 +1429,20 @@ let () = ignore Modtyp_lib.M_redef.externally_used ``` -Once again, let's look at the code before diving into the compilation and -analysis. Here the `Modtyp_lib` exports 1 module type `T` and 4 modules : +Before looking at the analysis results, let's look at the code. + +The `Modtyp_lib` exports 1 module type `T` and 4 modules : `M_reuse`, `M_constr`, `M_subst`, and `M_redef`. Of these 4 modules, the first 3 have `T` as signature (with minor twists), while the last one has its own explicit signature, which is a copy of `T`. In this way, `M_redef` is equivalent -to the [module](#module) example's `Module_lib.M` module : it exposes exactly +to `Module_lib.M` in the [module](#module) example : it exposes exactly the same information. Each of the modules exposed by `Modtyp_lib` are used exactly in the same way : their `externally_used` values are explicitly referenced in `Modtyp_bin`. -One could naturally expect that all the exported values are reported -except for the `externally_used`. However, reporting e.g. `M_subst.internally_used` -as unused would not be really actionable. In reality, this value is explicitly +One could expect that all the exported values are reported except for the +`externally_used`. However, reporting e.g. `M_subst.internally_used` as unused +would not be immediately actionable. In reality, this value is explicitly declared by `T`. Fixing an unused value reported in a module using a module type as signature would require either removing the value from the module type (if possible), @@ -1456,10 +1450,10 @@ or explicilty describing the signature of the module, effectively losing the benefits of using the module type. Thus, reporting unused values for the module itself would be counterproductive. -An actionable report would be that of the value in the module type itself, +An actionable report would be of the value in the module type itself, if it is unused by all the modules of that module type (as it is the case here -for `T.unused`). Currenyl, and as described in the introduction of this example, -the values exported by module types are ignored by the analyzeri, and, +for `T.unused`). Currently, and as described in the introduction of this example, +the values exported by module types are ignored by the analyzer, and, consequently, are not reported. Now that we have explained what the expected behavior of the analyzer should be, @@ -1490,19 +1484,17 @@ Nothing else to report in this section make: Leaving directory '/tmp/docs/exported_values/code_constructs/modtyp' ``` -As in the module example, the compiler detects that `unused_unexported` is unused. +As in the module example, the compiler reports that `unused_unexported` is unused. As in the module example, the analyzer reports `M_redef.internally_used` and `M_redef.unused` as unused exported values. -All the reports are similar to those of the [module example](#module). -The exploration iand resolution of that example can be applied here identically. +All the reports are similar to those of the [module](#module) example. +Its exploration and resolution can be applied. Our work here is done. ### Module Signature -This example illustrates a simple case of exported values in module signatures. - The reference files for this example are in the [modsig](./code_constructs/modsig) directory. @@ -1553,17 +1545,17 @@ The `Modsig_lib` compilation unit does not have a `.mli`, so all the internal uses are accounted for. It exposes 3 modules : `Original`, `Alias_without_sig`, and `Alias_with_sig`. Only the first module actually defines values. The second one is an trivial alias, and the third is an alias with an explicit signature. -Because the 2 latter modules are aliases for the `Original` module, one could -expect that the values exposed by them are unified with the ones in `Original`. +Because the 2 latter modules are aliases of the `Original` module, one could +expect that the values they expose are unified with the ones of `Original`. This would imply that only the values in `Original` could be reported as unused. This reasoning is partially true. -As explained in the [module type example](#module-type), a report on a value in -`Alias_without_sig` would not be trivially solved, but require removing the -value from `Original` itself if possible or add an explicit signature to -unexport the unused value. Thus, it is the case for `Alias_without_sig` that -its values are unified with those of `Original`, and, consequently, that they -cannot be reported as unused. Only the values in `Original` can be reported in this case. +As explained in the [module type](#module-type) example, reporting a value in +`Alias_without_sig` would not be trivially solved. Thus, +its values are unified with those of `Original`, and, consequently, they +cannot be reported by the analyzer. Only the values in `Original` can be +reported in this case. + However, `Alias_with_sig` has an explicit signature, which means 2 things: 1. it controls what it exports, thus a reporting values in that module is trivially actionable by removing the reported values from the signature @@ -1596,7 +1588,7 @@ make: Leaving directory '/tmp/docs/exported_values/code_constructs/modsig' The compiler does not report any unused value. -The analyzer detects that `Original.unused` and +The analyzer reports that `Original.unused` and `Alias_with_sig.used_by_requirement` are unused. As expected, it does not report any unused value in `Alias_without_sig`. Let's look more closely at the values and their uses. @@ -1658,7 +1650,7 @@ make: Leaving directory '/tmp/docs/exported_values/code_constructs/modsig' The compiler does not report any unused value. -The analyzer now detects that `Original.used_by_requirement` is unused. Indeed, +The analyzer reports `Original.used_by_requirement` as unused. Indeed, by removing `used_by_requirement` from the signature of `Alias_with_sig` we removed the requirement for `Original` to provide it. This value can be removed from `Orignal`, and neither the compiler nor the analyzer will report unused @@ -1717,24 +1709,25 @@ let () = Before looking at the analysis results, let's look at the code. The `Include_lib` compilation unit does not have a `.mli`, so all the internal -uses are accounted for. It exposes 3 modules : `Original`, `Reexport`, and -`Redefine`. The 1st one defines all its values, the 2nd only includes the 1st -one, and the 3rd one includes the 1st and redefines 2 values : `used_directly`, -and `unused`. +uses are accounted for. It exposes 3 modules : +- `Original`, which explicitly defines all its values; +- `Reexport`, which only includes `Original`; +- `Redefine`, which includes `Original` and redefines 2 values : + `used_directly`, and `unused`. + By the explanation in the [module signature](#module-signature) and [module type](#module-type) examples, although there are 9 exported values (`used_directly`, `used_indirectly`, and `unused` for each module), only 5 are expected to be tracked by the analyzer : those in `Original` and the 2 redefined -in `Redefine`. These are the only values a developer can trivially removeif +in `Redefine`. These are the only values a developer can trivially remove if they are reported unused. + Thus, the only values used are `Original.used_directly`, `Original.used_indirectly` (by an explicit reference to `Reexport.used_indirectly`), and `Redefine.used_directly`. This means that the unused exported values tracked by the analyzer are `Original.unused` and `Redefine.unused`. -Let's look at the actual results. - Compile and analyze: ``` $ make -C include @@ -1821,4 +1814,5 @@ Nothing else to report in this section make: Leaving directory '/tmp/docs/exported_values/code_constructs/include' ``` -Now, neither the compiler nor the analyzer report any unused value. Our work here is done. +Now, neither the compiler nor the analyzer report any unused value. +Our work here is done. From 1d0a626e0eacf054b1cab12c73e69392ae751be2 Mon Sep 17 00:00:00 2001 From: Corentin De Souza <9597216+fantazio@users.noreply.github.com> Date: Tue, 10 Mar 2026 10:57:47 +0100 Subject: [PATCH 12/16] [docs][exported_values][12/n] add compiler warnings section Instead of living in a short note, a section describes the relevant compiler warnings and provides examples. Add an example to show that the compiler warnings are not transitive. --- docs/exported_values/EXPORTED_VALUES.md | 125 +++++++++++++++++++++--- 1 file changed, 113 insertions(+), 12 deletions(-) diff --git a/docs/exported_values/EXPORTED_VALUES.md b/docs/exported_values/EXPORTED_VALUES.md index 9e5023d7..a93afb25 100644 --- a/docs/exported_values/EXPORTED_VALUES.md +++ b/docs/exported_values/EXPORTED_VALUES.md @@ -2,6 +2,10 @@ + [Exported Values](#exported-values) + [Definitions](#definitions) + + [Compiler warnings](#compiler-warnings) + + [Warning 26: unused-var](#warning-26-unused-var) + + [Warning 27: unused-var-strict](#warning-27-unused-var-strict) + + [Warning 32: unused-value-declaration](#warning-32-unused-value-declaration) + [Usage](#usage) + [Examples](#examples) + [Hello world](#hello-world) @@ -29,18 +33,6 @@ An **exported** value is one that is accessible outside its compilation unit. I.e. a value that can be referenced in other `.ml` and/or `.mli` files than the ones that declare it. -> [!NOTE] -> The _exported_ attribute is important here. -> The compiler already reports unused _unexported_ values (warning 32 -> `unused-value-declaration`) and unused _local_ -> values (warning 26 `unused-var`) [^unused-var-strict]. Because the compiler -> already reports these 2 categories of values, the `dead_code_analyzer` -> complements its work by focusing on the 3rd category. - -[^unused-var-strict]: The compiler also has the warning 27 `unused-var-strict` -for unused values not bound by `let` or `as` (e.g. function parameters or values -in patterns). These are out of context for the analyzer. - A **use** is either : - An explicit reference. E.g. @@ -75,6 +67,98 @@ A **use** is either : File "requirement.ml", line 4, characters 16-32: Expected declaration ``` +## Compiler warnings + +The analyzer reports unused _exported_ values while the compiler reports other +kinds of unused values. They complement each other. + +> [!TIP] +> To obtain a list of available compiler warnings, use +> `ocamlopt -warn-help` + +The compiler warnings related to unused values are the 26, 27, and 32. +The two firsts warn about unused local values. The third warns about unused +unexported toplevel values. + +### Warning 26: unused-var + +This warning is enabled by default. +I can be disabled by passing the `-w -26` to the compiler. + +Description: +``` +26 [unused-var] Suspicious unused variable: unused variable that is bound + with "let" or "as", and doesn't start with an underscore ("_") + character. +``` + +Example: +```OCaml +(* warning26.ml *) +let () = + let x = () in + () +``` +``` +$ ocamlopt warning26.ml +File "warning26.ml", line 3, characters 6-7: +3 | let x = () in + ^ +Warning 26 [unused-var]: unused variable x. +``` + +### Warning 27: unused-var-strict + +This warning is disabled by default. +I can be enabled by passing the `-w +27` to the compiler. + +Description: +``` +27 [unused-var-strict] Innocuous unused variable: unused variable that is not bound with + "let" nor "as", and doesn't start with an underscore ("_") + character. +``` + +Example: +```OCaml +(* warning27.ml *) +let f = function + | x -> () +``` +``` +$ ocamlopt -w +27 warning27.ml +File "warning27.ml", line 2, characters 4-5: +2 | | x -> () + ^ +Warning 27 [unused-var-strict]: unused variable x. +``` + +### Warning 32: unused-value-declaration + +This warning is disabled by default. +I can be enabled by passing the `-w +32` to the compiler. + +Description: +``` +32 [unused-value-declaration] Unused value declaration. (since 4.00) +``` + +Example: +```OCaml +(* warning32.mli *) +``` +```OCaml +(* warning32.ml *) +let x = () +``` +``` +$ ocamlopt -w +32 warning32.mli warning32.ml +File "warning32.ml", line 2, characters 4-5: +2 | let x = () + ^ +Warning 32 [unused-value-declaration]: unused value x. +``` + ## Usage Unused exported values are reported by default. @@ -108,6 +192,23 @@ not be reported as unused. It would be reported unused only after all the code using it has been removed. This is also the compiler's behavior for its warnings about unused values. +E.g. +```OCaml +(* not_transitive.ml *) +let () = + let x = () in + let y = x in + () +``` +``` +$ ocamlopt not_transitive.ml +File "not_transitive.ml", line 4, characters 6-7: +4 | let y = x in + ^ +Warning 26 [unused-var]: unused variable y. +``` +`y` is reported by the warning 26 but not `x`, because `x` is used by `y`. + # Examples ## Hello world From 50ed4dad92583b13833e3b2e89cb87140ff1f127 Mon Sep 17 00:00:00 2001 From: Corentin De Souza <9597216+fantazio@users.noreply.github.com> Date: Tue, 10 Mar 2026 12:18:21 +0100 Subject: [PATCH 13/16] [docs][exported_values][13/n] extract code examples to `examples` The examples shown in the documentation are now part of the test suite. Any fix a breakage will be easy to notice and propagate into the doc. --- check/classic/classic.exp | 53 ++++++++++ check/classic/classic.ref | 60 ++++++++++- check/internal/internal.exp | 43 ++++++++ check/internal/internal.ref | 50 +++++++++- check/src/path.ml | 17 ++-- check/threshold-1/threshold-1.exp | 73 ++++++++++++++ check/threshold-1/threshold-1.ref | 85 +++++++++++++++- check/threshold-3-0.5/threshold-3-0.5.exp | 88 ++++++++++++++++- check/threshold-3-0.5/threshold-3-0.5.ref | 99 ++++++++++++++++++- docs/exported_values/EXPORTED_VALUES.md | 28 +++--- examples/Makefile | 14 ++- examples/docs/Makefile | 11 +++ examples/docs/exported_values/Makefile | 13 +++ .../exported_values/code_constructs/Makefile | 21 ++++ .../code_constructs/function/Makefile | 0 .../code_constructs/function/function_bin.ml | 0 .../code_constructs/function/function_lib.ml | 0 .../code_constructs/function/function_lib.mli | 0 .../code_constructs/functor/Makefile | 0 .../code_constructs/functor/functor_bin.ml | 0 .../code_constructs/functor/functor_lib.ml | 0 .../code_constructs/functor/functor_lib.mli | 0 .../code_constructs/include/Makefile | 0 .../code_constructs/include/include_bin.ml | 0 .../code_constructs/include/include_lib.ml | 0 .../code_constructs/modsig/Makefile | 0 .../code_constructs/modsig/modsig_bin.ml | 0 .../code_constructs/modsig/modsig_lib.ml | 0 .../code_constructs/modtyp/Makefile | 0 .../code_constructs/modtyp/modtyp_bin.ml | 0 .../code_constructs/modtyp/modtyp_lib.ml | 0 .../code_constructs/modtyp/modtyp_lib.mli | 0 .../code_constructs/module/Makefile | 0 .../code_constructs/module/module_bin.ml | 0 .../code_constructs/module/module_lib.ml | 0 .../code_constructs/module/module_lib.mli | 0 .../docs/exported_values/hello_world/Makefile | 19 ++++ .../hello_world/hello_world.ml | 0 .../hello_world/hello_world.mli | 0 .../hello_world/hello_world_bin.ml | 0 .../hello_world/hello_world_lib.ml | 0 .../hello_world/hello_world_lib.mli | 0 .../hello_world/hello_world_no_intf.ml | 0 43 files changed, 632 insertions(+), 42 deletions(-) create mode 100644 examples/docs/Makefile create mode 100644 examples/docs/exported_values/Makefile create mode 100644 examples/docs/exported_values/code_constructs/Makefile rename {docs => examples/docs}/exported_values/code_constructs/function/Makefile (100%) rename {docs => examples/docs}/exported_values/code_constructs/function/function_bin.ml (100%) rename {docs => examples/docs}/exported_values/code_constructs/function/function_lib.ml (100%) rename {docs => examples/docs}/exported_values/code_constructs/function/function_lib.mli (100%) rename {docs => examples/docs}/exported_values/code_constructs/functor/Makefile (100%) rename {docs => examples/docs}/exported_values/code_constructs/functor/functor_bin.ml (100%) rename {docs => examples/docs}/exported_values/code_constructs/functor/functor_lib.ml (100%) rename {docs => examples/docs}/exported_values/code_constructs/functor/functor_lib.mli (100%) rename {docs => examples/docs}/exported_values/code_constructs/include/Makefile (100%) rename {docs => examples/docs}/exported_values/code_constructs/include/include_bin.ml (100%) rename {docs => examples/docs}/exported_values/code_constructs/include/include_lib.ml (100%) rename {docs => examples/docs}/exported_values/code_constructs/modsig/Makefile (100%) rename {docs => examples/docs}/exported_values/code_constructs/modsig/modsig_bin.ml (100%) rename {docs => examples/docs}/exported_values/code_constructs/modsig/modsig_lib.ml (100%) rename {docs => examples/docs}/exported_values/code_constructs/modtyp/Makefile (100%) rename {docs => examples/docs}/exported_values/code_constructs/modtyp/modtyp_bin.ml (100%) rename {docs => examples/docs}/exported_values/code_constructs/modtyp/modtyp_lib.ml (100%) rename {docs => examples/docs}/exported_values/code_constructs/modtyp/modtyp_lib.mli (100%) rename {docs => examples/docs}/exported_values/code_constructs/module/Makefile (100%) rename {docs => examples/docs}/exported_values/code_constructs/module/module_bin.ml (100%) rename {docs => examples/docs}/exported_values/code_constructs/module/module_lib.ml (100%) rename {docs => examples/docs}/exported_values/code_constructs/module/module_lib.mli (100%) create mode 100644 examples/docs/exported_values/hello_world/Makefile rename {docs => examples/docs}/exported_values/hello_world/hello_world.ml (100%) rename {docs => examples/docs}/exported_values/hello_world/hello_world.mli (100%) rename {docs => examples/docs}/exported_values/hello_world/hello_world_bin.ml (100%) rename {docs => examples/docs}/exported_values/hello_world/hello_world_lib.ml (100%) rename {docs => examples/docs}/exported_values/hello_world/hello_world_lib.mli (100%) rename {docs => examples/docs}/exported_values/hello_world/hello_world_no_intf.ml (100%) diff --git a/check/classic/classic.exp b/check/classic/classic.exp index 7d7dce5b..7cc0546a 100644 --- a/check/classic/classic.exp +++ b/check/classic/classic.exp @@ -1,5 +1,30 @@ .> UNUSED EXPORTED VALUES: ========================= +./examples/docs/exported_values/code_constructs/function/function_lib.mli:7: unused + +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:9: F.internally_used +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:10: F.unused +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:14: InternalParam.used_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:15: InternalParam.unused_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:25: InternalApp.internally_used +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:26: InternalApp.unused + +./examples/docs/exported_values/code_constructs/include/include_lib.ml:5: Original.unused +./examples/docs/exported_values/code_constructs/include/include_lib.ml:15: Redefine.unused + +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:6: Original.unused +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:12: Alias_with_sig.used_by_requirement + +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:18: M_redef.internally_used +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:19: M_redef.unused + +./examples/docs/exported_values/code_constructs/module/module_lib.mli:5: M.internally_used +./examples/docs/exported_values/code_constructs/module/module_lib.mli:6: M.unused + +./examples/docs/exported_values/hello_world/hello_world.mli:2: hello +./examples/docs/exported_values/hello_world/hello_world.mli:3: goodbye +./examples/docs/exported_values/hello_world/hello_world.mli:4: world + ./examples/using_dune/preprocessed_lib/preprocessed.mli:1: unused ./examples/using_dune/preprocessed_lib/preprocessed.mli:3: internally_used ./examples/using_dune/preprocessed_lib/preprocessed.mli:38: internally_used_f @@ -518,6 +543,32 @@ Nothing else to report in this section .> CODING STYLE: =============== +./examples/docs/exported_values/code_constructs/functor/functor_lib.ml:16: unit pattern used_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.ml:17: unit pattern unused_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.ml:21: unit pattern used_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.ml:22: unit pattern unused_required + +./examples/docs/exported_values/code_constructs/include/include_lib.ml:3: unit pattern used_directly +./examples/docs/exported_values/code_constructs/include/include_lib.ml:4: unit pattern used_indirectly +./examples/docs/exported_values/code_constructs/include/include_lib.ml:5: unit pattern unused +./examples/docs/exported_values/code_constructs/include/include_lib.ml:14: unit pattern used_directly +./examples/docs/exported_values/code_constructs/include/include_lib.ml:15: unit pattern unused + +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:3: unit pattern used_directly +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:4: unit pattern used_indirectly +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:5: unit pattern used_by_requirement +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:6: unit pattern unused + +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.ml:11: unit pattern externally_used +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.ml:12: unit pattern internally_used +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.ml:13: unit pattern unused +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.ml:14: unit pattern unused_unexported + +./examples/docs/exported_values/code_constructs/module/module_lib.ml:4: unit pattern externally_used +./examples/docs/exported_values/code_constructs/module/module_lib.ml:5: unit pattern internally_used +./examples/docs/exported_values/code_constructs/module/module_lib.ml:6: unit pattern unused +./examples/docs/exported_values/code_constructs/module/module_lib.ml:7: unit pattern unused_unexported + ./examples/using_dune/preprocessed_lib/preprocessed.ml:83: let () = ... in ... (=> use sequence) ./examples/using_dune/preprocessed_lib/preprocessed.ml:83: unit pattern unit_binding ./examples/using_dune/preprocessed_lib/preprocessed.ml:84: val f: ... -> (... -> ?_:_ -> ...) -> ... @@ -563,3 +614,5 @@ Nothing else to report in this section Nothing else to report in this section -------------------------------------------------------------------------------- + + diff --git a/check/classic/classic.ref b/check/classic/classic.ref index 1a7e8a02..b7036208 100644 --- a/check/classic/classic.ref +++ b/check/classic/classic.ref @@ -1,5 +1,31 @@ .> UNUSED EXPORTED VALUES: ========================= +./examples/docs/exported_values/code_constructs/function/function_lib.mli:7: unused + +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:9: F.internally_used +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:10: F.unused +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:14: InternalParam.used_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:15: InternalParam.unused_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:25: InternalApp.internally_used +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:26: InternalApp.unused + +./examples/docs/exported_values/code_constructs/include/include_lib.ml:5: Original.unused +./examples/docs/exported_values/code_constructs/include/include_lib.ml:5: Reexport.unused: Should not be detected +./examples/docs/exported_values/code_constructs/include/include_lib.ml:15: Redefine.unused + +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:6: Original.unused +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:12: Alias_with_sig.used_by_requirement + +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:18: M_redef.internally_used +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:19: M_redef.unused + +./examples/docs/exported_values/code_constructs/module/module_lib.mli:5: M.internally_used +./examples/docs/exported_values/code_constructs/module/module_lib.mli:6: M.unused + +./examples/docs/exported_values/hello_world/hello_world.mli:2: hello +./examples/docs/exported_values/hello_world/hello_world.mli:3: goodbye +./examples/docs/exported_values/hello_world/hello_world.mli:4: world + ./examples/using_dune/preprocessed_lib/preprocessed.mli:1: unused ./examples/using_dune/preprocessed_lib/preprocessed.mli:3: internally_used ./examples/using_dune/preprocessed_lib/preprocessed.mli:38: internally_used_f @@ -518,6 +544,32 @@ Nothing else to report in this section .> CODING STYLE: =============== +./examples/docs/exported_values/code_constructs/functor/functor_lib.ml:16: unit pattern used_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.ml:17: unit pattern unused_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.ml:21: unit pattern used_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.ml:22: unit pattern unused_required + +./examples/docs/exported_values/code_constructs/include/include_lib.ml:3: unit pattern used_directly +./examples/docs/exported_values/code_constructs/include/include_lib.ml:4: unit pattern used_indirectly +./examples/docs/exported_values/code_constructs/include/include_lib.ml:5: unit pattern unused +./examples/docs/exported_values/code_constructs/include/include_lib.ml:14: unit pattern used_directly +./examples/docs/exported_values/code_constructs/include/include_lib.ml:15: unit pattern unused + +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:3: unit pattern used_directly +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:4: unit pattern used_indirectly +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:5: unit pattern used_by_requirement +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:6: unit pattern unused + +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.ml:11: unit pattern externally_used +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.ml:12: unit pattern internally_used +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.ml:13: unit pattern unused +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.ml:14: unit pattern unused_unexported + +./examples/docs/exported_values/code_constructs/module/module_lib.ml:4: unit pattern externally_used +./examples/docs/exported_values/code_constructs/module/module_lib.ml:5: unit pattern internally_used +./examples/docs/exported_values/code_constructs/module/module_lib.ml:6: unit pattern unused +./examples/docs/exported_values/code_constructs/module/module_lib.ml:7: unit pattern unused_unexported + ./examples/using_dune/preprocessed_lib/preprocessed.ml:83: let () = ... in ... (=> use sequence) ./examples/using_dune/preprocessed_lib/preprocessed.ml:83: unit pattern unit_binding ./examples/using_dune/preprocessed_lib/preprocessed.ml:84: val f: ... -> (... -> ?_:_ -> ...) -> ... @@ -565,7 +617,7 @@ Nothing else to report in this section -------------------------------------------------------------------------------- -Total: 491 -Success: 491 -Failed: 0 -Ratio: 100.% +Total: 531 +Success: 530 +Failed: 1 +Ratio: 99.8116760829% diff --git a/check/internal/internal.exp b/check/internal/internal.exp index 266dd6e9..13782328 100644 --- a/check/internal/internal.exp +++ b/check/internal/internal.exp @@ -1,5 +1,20 @@ .> UNUSED EXPORTED VALUES: ========================= +./examples/docs/exported_values/code_constructs/function/function_lib.mli:7: unused + +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:10: F.unused +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:26: InternalApp.unused + +./examples/docs/exported_values/code_constructs/include/include_lib.ml:5: Original.unused +./examples/docs/exported_values/code_constructs/include/include_lib.ml:15: Redefine.unused + +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:6: Original.unused +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:12: Alias_with_sig.used_by_requirement + +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:19: M_redef.unused + +./examples/docs/exported_values/code_constructs/module/module_lib.mli:6: M.unused + ./examples/using_dune/preprocessed_lib/preprocessed.mli:1: unused ./examples/using_dune/preprocessed_lib/preprocessed_no_intf.ml:2: unused @@ -483,6 +498,32 @@ Nothing else to report in this section .> CODING STYLE: =============== +./examples/docs/exported_values/code_constructs/functor/functor_lib.ml:16: unit pattern used_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.ml:17: unit pattern unused_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.ml:21: unit pattern used_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.ml:22: unit pattern unused_required + +./examples/docs/exported_values/code_constructs/include/include_lib.ml:3: unit pattern used_directly +./examples/docs/exported_values/code_constructs/include/include_lib.ml:4: unit pattern used_indirectly +./examples/docs/exported_values/code_constructs/include/include_lib.ml:5: unit pattern unused +./examples/docs/exported_values/code_constructs/include/include_lib.ml:14: unit pattern used_directly +./examples/docs/exported_values/code_constructs/include/include_lib.ml:15: unit pattern unused + +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:3: unit pattern used_directly +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:4: unit pattern used_indirectly +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:5: unit pattern used_by_requirement +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:6: unit pattern unused + +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.ml:11: unit pattern externally_used +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.ml:12: unit pattern internally_used +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.ml:13: unit pattern unused +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.ml:14: unit pattern unused_unexported + +./examples/docs/exported_values/code_constructs/module/module_lib.ml:4: unit pattern externally_used +./examples/docs/exported_values/code_constructs/module/module_lib.ml:5: unit pattern internally_used +./examples/docs/exported_values/code_constructs/module/module_lib.ml:6: unit pattern unused +./examples/docs/exported_values/code_constructs/module/module_lib.ml:7: unit pattern unused_unexported + ./examples/using_dune/preprocessed_lib/preprocessed.ml:83: let () = ... in ... (=> use sequence) ./examples/using_dune/preprocessed_lib/preprocessed.ml:83: unit pattern unit_binding ./examples/using_dune/preprocessed_lib/preprocessed.ml:84: val f: ... -> (... -> ?_:_ -> ...) -> ... @@ -528,3 +569,5 @@ Nothing else to report in this section Nothing else to report in this section -------------------------------------------------------------------------------- + + diff --git a/check/internal/internal.ref b/check/internal/internal.ref index 61168480..928df905 100644 --- a/check/internal/internal.ref +++ b/check/internal/internal.ref @@ -1,5 +1,21 @@ .> UNUSED EXPORTED VALUES: ========================= +./examples/docs/exported_values/code_constructs/function/function_lib.mli:7: unused + +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:10: F.unused +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:26: InternalApp.unused + +./examples/docs/exported_values/code_constructs/include/include_lib.ml:5: Original.unused +./examples/docs/exported_values/code_constructs/include/include_lib.ml:5: Reexport.unused: Should not be detected +./examples/docs/exported_values/code_constructs/include/include_lib.ml:15: Redefine.unused + +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:6: Original.unused +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:12: Alias_with_sig.used_by_requirement + +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:19: M_redef.unused + +./examples/docs/exported_values/code_constructs/module/module_lib.mli:6: M.unused + ./examples/using_dune/preprocessed_lib/preprocessed.mli:1: unused ./examples/using_dune/preprocessed_lib/preprocessed_no_intf.ml:2: unused @@ -483,6 +499,32 @@ Nothing else to report in this section .> CODING STYLE: =============== +./examples/docs/exported_values/code_constructs/functor/functor_lib.ml:16: unit pattern used_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.ml:17: unit pattern unused_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.ml:21: unit pattern used_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.ml:22: unit pattern unused_required + +./examples/docs/exported_values/code_constructs/include/include_lib.ml:3: unit pattern used_directly +./examples/docs/exported_values/code_constructs/include/include_lib.ml:4: unit pattern used_indirectly +./examples/docs/exported_values/code_constructs/include/include_lib.ml:5: unit pattern unused +./examples/docs/exported_values/code_constructs/include/include_lib.ml:14: unit pattern used_directly +./examples/docs/exported_values/code_constructs/include/include_lib.ml:15: unit pattern unused + +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:3: unit pattern used_directly +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:4: unit pattern used_indirectly +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:5: unit pattern used_by_requirement +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:6: unit pattern unused + +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.ml:11: unit pattern externally_used +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.ml:12: unit pattern internally_used +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.ml:13: unit pattern unused +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.ml:14: unit pattern unused_unexported + +./examples/docs/exported_values/code_constructs/module/module_lib.ml:4: unit pattern externally_used +./examples/docs/exported_values/code_constructs/module/module_lib.ml:5: unit pattern internally_used +./examples/docs/exported_values/code_constructs/module/module_lib.ml:6: unit pattern unused +./examples/docs/exported_values/code_constructs/module/module_lib.ml:7: unit pattern unused_unexported + ./examples/using_dune/preprocessed_lib/preprocessed.ml:83: let () = ... in ... (=> use sequence) ./examples/using_dune/preprocessed_lib/preprocessed.ml:83: unit pattern unit_binding ./examples/using_dune/preprocessed_lib/preprocessed.ml:84: val f: ... -> (... -> ?_:_ -> ...) -> ... @@ -530,7 +572,7 @@ Nothing else to report in this section -------------------------------------------------------------------------------- -Total: 458 -Success: 458 -Failed: 0 -Ratio: 100.% +Total: 489 +Success: 488 +Failed: 1 +Ratio: 99.7955010225% diff --git a/check/src/path.ml b/check/src/path.ml index 3684991a..b6a00a70 100644 --- a/check/src/path.ml +++ b/check/src/path.ml @@ -37,22 +37,23 @@ let normalize path = (* Relocate a `path` found in a `.got` file as a relative path from the project's root. - - For files in 'using_make': there is no 'using_make' file or directory in - '/examples/using_'. Therefore, taking everything in the - path from the last 'using_make' filename provides the common part for the - relocation. Then, adding an extra './' completes the path as found in the - expected reports + - For files in 'docs' and 'using_make': there is no 'using_make' file or + directory in '/examples/*'. Therefore, taking everything in the + path from the last 'docs' or 'using_make' filename provides the common part + for the relocation. Then, adding an extra './iexamples' completes the path as + found in the expected reports - For files in 'using_dune': the same startegy as for files in 'using_make' can be applied with the removal of '_build/default' on the way. Because those directory names are unique, we can actually share the same logic for both 'using_make' and 'using_dune'. *) let relocate path = (* retrieve the subpath starting from the last occurence of - "using_" if it exists, and remove "_build" and "default" + a valid basename if it exists, and remove "_build" and "default" directories from the path *) + let valid_basenames = ["docs"; "using_make"; "using_dune"] in let rec extract_subpath acc dirpath = let basename = Filename.basename dirpath in - if basename = "using_make" || basename = "using_dune" then + if List.mem basename valid_basenames then basename::acc else if basename = dirpath (* fixpoint *) then path::[] (* TODO: error handling *) @@ -64,7 +65,7 @@ let relocate path = in extract_subpath acc (Filename.dirname dirpath) in - let relative_path = (* ["using"; ...] *) + let relative_path = (* [valid_basename; ...] *) extract_subpath [] path in String.concat Filename.dir_sep ("."::"examples"::relative_path) diff --git a/check/threshold-1/threshold-1.exp b/check/threshold-1/threshold-1.exp index 3ee1152d..b59d60eb 100644 --- a/check/threshold-1/threshold-1.exp +++ b/check/threshold-1/threshold-1.exp @@ -1,5 +1,20 @@ .> UNUSED EXPORTED VALUES: ========================= +./examples/docs/exported_values/code_constructs/function/function_lib.mli:7: unused + +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:10: F.unused +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:26: InternalApp.unused + +./examples/docs/exported_values/code_constructs/include/include_lib.ml:5: Original.unused +./examples/docs/exported_values/code_constructs/include/include_lib.ml:15: Redefine.unused + +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:6: Original.unused +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:12: Alias_with_sig.used_by_requirement + +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:19: M_redef.unused + +./examples/docs/exported_values/code_constructs/module/module_lib.mli:6: M.unused + ./examples/using_dune/preprocessed_lib/preprocessed.mli:1: unused ./examples/using_dune/preprocessed_lib/preprocessed_no_intf.ml:2: unused @@ -84,6 +99,38 @@ .>-> ALMOST UNUSED EXPORTED VALUES: Called 1 time(s): ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +./examples/docs/exported_values/code_constructs/function/function_lib.mli:3: memoize +./examples/docs/exported_values/code_constructs/function/function_lib.mli:5: heavy_computation +./examples/docs/exported_values/code_constructs/function/function_lib.mli:9: do_nothing + +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:8: F.externally_used +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:9: F.internally_used +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:14: InternalParam.used_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:15: InternalParam.unused_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:24: InternalApp.externally_used +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:25: InternalApp.internally_used + +./examples/docs/exported_values/code_constructs/include/include_lib.ml:3: Original.used_directly +./examples/docs/exported_values/code_constructs/include/include_lib.ml:4: Original.used_indirectly +./examples/docs/exported_values/code_constructs/include/include_lib.ml:14: Redefine.used_directly + +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:3: Original.used_directly +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:4: Original.used_indirectly +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:5: Original.used_by_requirement + +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:17: M_redef.externally_used +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:18: M_redef.internally_used + +./examples/docs/exported_values/code_constructs/module/module_lib.mli:4: M.externally_used +./examples/docs/exported_values/code_constructs/module/module_lib.mli:5: M.internally_used + +./examples/docs/exported_values/hello_world/hello_world.mli:2: hello +./examples/docs/exported_values/hello_world/hello_world.mli:3: goodbye +./examples/docs/exported_values/hello_world/hello_world_lib.mli:2: hello +./examples/docs/exported_values/hello_world/hello_world_lib.mli:3: goodbye +./examples/docs/exported_values/hello_world/hello_world_no_intf.ml:2: hello +./examples/docs/exported_values/hello_world/hello_world_no_intf.ml:3: goodbye + ./examples/using_dune/bin/use_preprocessed_lib/use_preprocessed.mli:1: mark_used ./examples/using_dune/bin/use_preprocessed_lib/use_preprocessed_lib.mli:1: mark_used ./examples/using_dune/bin/use_preprocessed_lib/use_preprocessed_no_intf.mli:1: mark_used @@ -826,6 +873,32 @@ Nothing else to report in this section .> CODING STYLE: =============== +./examples/docs/exported_values/code_constructs/functor/functor_lib.ml:16: unit pattern used_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.ml:17: unit pattern unused_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.ml:21: unit pattern used_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.ml:22: unit pattern unused_required + +./examples/docs/exported_values/code_constructs/include/include_lib.ml:3: unit pattern used_directly +./examples/docs/exported_values/code_constructs/include/include_lib.ml:4: unit pattern used_indirectly +./examples/docs/exported_values/code_constructs/include/include_lib.ml:5: unit pattern unused +./examples/docs/exported_values/code_constructs/include/include_lib.ml:14: unit pattern used_directly +./examples/docs/exported_values/code_constructs/include/include_lib.ml:15: unit pattern unused + +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:3: unit pattern used_directly +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:4: unit pattern used_indirectly +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:5: unit pattern used_by_requirement +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:6: unit pattern unused + +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.ml:11: unit pattern externally_used +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.ml:12: unit pattern internally_used +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.ml:13: unit pattern unused +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.ml:14: unit pattern unused_unexported + +./examples/docs/exported_values/code_constructs/module/module_lib.ml:4: unit pattern externally_used +./examples/docs/exported_values/code_constructs/module/module_lib.ml:5: unit pattern internally_used +./examples/docs/exported_values/code_constructs/module/module_lib.ml:6: unit pattern unused +./examples/docs/exported_values/code_constructs/module/module_lib.ml:7: unit pattern unused_unexported + ./examples/using_dune/preprocessed_lib/preprocessed.ml:83: let () = ... in ... (=> use sequence) ./examples/using_dune/preprocessed_lib/preprocessed.ml:83: unit pattern unit_binding ./examples/using_dune/preprocessed_lib/preprocessed.ml:84: val f: ... -> (... -> ?_:_ -> ...) -> ... diff --git a/check/threshold-1/threshold-1.ref b/check/threshold-1/threshold-1.ref index b1d8443f..75229b83 100644 --- a/check/threshold-1/threshold-1.ref +++ b/check/threshold-1/threshold-1.ref @@ -1,5 +1,21 @@ .> UNUSED EXPORTED VALUES: ========================= +./examples/docs/exported_values/code_constructs/function/function_lib.mli:7: unused + +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:10: F.unused +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:26: InternalApp.unused + +./examples/docs/exported_values/code_constructs/include/include_lib.ml:5: Original.unused +./examples/docs/exported_values/code_constructs/include/include_lib.ml:5: Reexport.unused: Should not be detected +./examples/docs/exported_values/code_constructs/include/include_lib.ml:15: Redefine.unused + +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:6: Original.unused +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:12: Alias_with_sig.used_by_requirement + +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:19: M_redef.unused + +./examples/docs/exported_values/code_constructs/module/module_lib.mli:6: M.unused + ./examples/using_dune/preprocessed_lib/preprocessed.mli:1: unused ./examples/using_dune/preprocessed_lib/preprocessed_no_intf.ml:2: unused @@ -84,6 +100,41 @@ .>-> ALMOST UNUSED EXPORTED VALUES: Called 1 time(s): ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +./examples/docs/exported_values/code_constructs/function/function_lib.mli:3: memoize +./examples/docs/exported_values/code_constructs/function/function_lib.mli:5: heavy_computation +./examples/docs/exported_values/code_constructs/function/function_lib.mli:9: do_nothing + +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:8: F.externally_used +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:9: F.internally_used +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:14: InternalParam.used_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:15: InternalParam.unused_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:24: InternalApp.externally_used +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:25: InternalApp.internally_used + +./examples/docs/exported_values/code_constructs/include/include_lib.ml:3: Original.used_directly +./examples/docs/exported_values/code_constructs/include/include_lib.ml:3: Reexport.used_directly: Should not be detected +./examples/docs/exported_values/code_constructs/include/include_lib.ml:4: Original.used_indirectly +./examples/docs/exported_values/code_constructs/include/include_lib.ml:4: Redefine.used_indirectly: Should not be detected +./examples/docs/exported_values/code_constructs/include/include_lib.ml:4: Reexport.used_indirectly: Should not be detected +./examples/docs/exported_values/code_constructs/include/include_lib.ml:14: Redefine.used_directly + +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:3: Original.used_directly +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:4: Original.used_indirectly +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:5: Original.used_by_requirement + +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:17: M_redef.externally_used +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:18: M_redef.internally_used + +./examples/docs/exported_values/code_constructs/module/module_lib.mli:4: M.externally_used +./examples/docs/exported_values/code_constructs/module/module_lib.mli:5: M.internally_used + +./examples/docs/exported_values/hello_world/hello_world.mli:2: hello +./examples/docs/exported_values/hello_world/hello_world.mli:3: goodbye +./examples/docs/exported_values/hello_world/hello_world_lib.mli:2: hello +./examples/docs/exported_values/hello_world/hello_world_lib.mli:3: goodbye +./examples/docs/exported_values/hello_world/hello_world_no_intf.ml:2: hello +./examples/docs/exported_values/hello_world/hello_world_no_intf.ml:3: goodbye + ./examples/using_dune/bin/use_preprocessed_lib/use_preprocessed.mli:1: mark_used ./examples/using_dune/bin/use_preprocessed_lib/use_preprocessed_lib.mli:1: mark_used ./examples/using_dune/bin/use_preprocessed_lib/use_preprocessed_no_intf.mli:1: mark_used @@ -826,6 +877,32 @@ Nothing else to report in this section .> CODING STYLE: =============== +./examples/docs/exported_values/code_constructs/functor/functor_lib.ml:16: unit pattern used_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.ml:17: unit pattern unused_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.ml:21: unit pattern used_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.ml:22: unit pattern unused_required + +./examples/docs/exported_values/code_constructs/include/include_lib.ml:3: unit pattern used_directly +./examples/docs/exported_values/code_constructs/include/include_lib.ml:4: unit pattern used_indirectly +./examples/docs/exported_values/code_constructs/include/include_lib.ml:5: unit pattern unused +./examples/docs/exported_values/code_constructs/include/include_lib.ml:14: unit pattern used_directly +./examples/docs/exported_values/code_constructs/include/include_lib.ml:15: unit pattern unused + +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:3: unit pattern used_directly +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:4: unit pattern used_indirectly +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:5: unit pattern used_by_requirement +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:6: unit pattern unused + +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.ml:11: unit pattern externally_used +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.ml:12: unit pattern internally_used +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.ml:13: unit pattern unused +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.ml:14: unit pattern unused_unexported + +./examples/docs/exported_values/code_constructs/module/module_lib.ml:4: unit pattern externally_used +./examples/docs/exported_values/code_constructs/module/module_lib.ml:5: unit pattern internally_used +./examples/docs/exported_values/code_constructs/module/module_lib.ml:6: unit pattern unused +./examples/docs/exported_values/code_constructs/module/module_lib.ml:7: unit pattern unused_unexported + ./examples/using_dune/preprocessed_lib/preprocessed.ml:83: let () = ... in ... (=> use sequence) ./examples/using_dune/preprocessed_lib/preprocessed.ml:83: unit pattern unit_binding ./examples/using_dune/preprocessed_lib/preprocessed.ml:84: val f: ... -> (... -> ?_:_ -> ...) -> ... @@ -873,7 +950,7 @@ Nothing else to report in this section -------------------------------------------------------------------------------- -Total: 762 -Success: 762 -Failed: 0 -Ratio: 100.% +Total: 821 +Success: 817 +Failed: 4 +Ratio: 99.5127892814% 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 9a443ecb..5e4daace 100644 --- a/check/threshold-3-0.5/threshold-3-0.5.exp +++ b/check/threshold-3-0.5/threshold-3-0.5.exp @@ -1,5 +1,20 @@ .> UNUSED EXPORTED VALUES: ========================= +./examples/docs/exported_values/code_constructs/function/function_lib.mli:7: unused + +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:10: F.unused +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:26: InternalApp.unused + +./examples/docs/exported_values/code_constructs/include/include_lib.ml:5: Original.unused +./examples/docs/exported_values/code_constructs/include/include_lib.ml:15: Redefine.unused + +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:6: Original.unused +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:12: Alias_with_sig.used_by_requirement + +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:19: M_redef.unused + +./examples/docs/exported_values/code_constructs/module/module_lib.mli:6: M.unused + ./examples/using_dune/preprocessed_lib/preprocessed.mli:1: unused ./examples/using_dune/preprocessed_lib/preprocessed_no_intf.ml:2: unused @@ -84,6 +99,38 @@ .>-> ALMOST UNUSED EXPORTED VALUES: Called 1 time(s): ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +./examples/docs/exported_values/code_constructs/function/function_lib.mli:3: memoize +./examples/docs/exported_values/code_constructs/function/function_lib.mli:5: heavy_computation +./examples/docs/exported_values/code_constructs/function/function_lib.mli:9: do_nothing + +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:8: F.externally_used +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:9: F.internally_used +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:14: InternalParam.used_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:15: InternalParam.unused_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:24: InternalApp.externally_used +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:25: InternalApp.internally_used + +./examples/docs/exported_values/code_constructs/include/include_lib.ml:3: Original.used_directly +./examples/docs/exported_values/code_constructs/include/include_lib.ml:4: Original.used_indirectly +./examples/docs/exported_values/code_constructs/include/include_lib.ml:14: Redefine.used_directly + +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:3: Original.used_directly +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:4: Original.used_indirectly +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:5: Original.used_by_requirement + +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:17: M_redef.externally_used +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:18: M_redef.internally_used + +./examples/docs/exported_values/code_constructs/module/module_lib.mli:4: M.externally_used +./examples/docs/exported_values/code_constructs/module/module_lib.mli:5: M.internally_used + +./examples/docs/exported_values/hello_world/hello_world.mli:2: hello +./examples/docs/exported_values/hello_world/hello_world.mli:3: goodbye +./examples/docs/exported_values/hello_world/hello_world_lib.mli:2: hello +./examples/docs/exported_values/hello_world/hello_world_lib.mli:3: goodbye +./examples/docs/exported_values/hello_world/hello_world_no_intf.ml:2: hello +./examples/docs/exported_values/hello_world/hello_world_no_intf.ml:3: goodbye + ./examples/using_dune/bin/use_preprocessed_lib/use_preprocessed.mli:1: mark_used ./examples/using_dune/bin/use_preprocessed_lib/use_preprocessed_lib.mli:1: mark_used ./examples/using_dune/bin/use_preprocessed_lib/use_preprocessed_no_intf.mli:1: mark_used @@ -287,6 +334,17 @@ .>-> ALMOST UNUSED EXPORTED VALUES: Called 2 time(s): ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:19: ExternalParam.used_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:20: ExternalParam.unused_required + +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:13: M_subst.externally_used +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:13: M_subst.internally_used +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:13: M_subst.unused + +./examples/docs/exported_values/hello_world/hello_world.mli:4: world +./examples/docs/exported_values/hello_world/hello_world_lib.mli:4: world +./examples/docs/exported_values/hello_world/hello_world_no_intf.ml:4: world + ./examples/using_dune/preprocessed_lib/preprocessed.mli:2: used ./examples/using_dune/preprocessed_lib/preprocessed.mli:31: exported_f ./examples/using_dune/preprocessed_lib/preprocessed.mli:38: internally_used_f @@ -993,7 +1051,9 @@ Nothing else to report in this section ./examples/using_make/dir/anonFn2.mli:2: ?a (2/3 calls) ./examples/using_make/dir/anonFn2.mli:2: ?b (2/3 calls) ./examples/using_make/dir/match_opt.ml:1: ?b (2/3 calls) + ./examples/using_make/dir/matchopt.ml:1: ?x (3/4 calls) + ./examples/using_make/dir/ref_fn.ml:1: ?a (2/3 calls) ./examples/using_make/dir/ref_fn.ml:1: ?b (2/3 calls) @@ -1192,9 +1252,9 @@ Nothing else to report in this section ./examples/using_make/dir/anonFn.mli:3: ?a (1/2 calls) ./examples/using_make/dir/anonFn.mli:3: ?b (1/2 calls) ./examples/using_make/dir/anonFn2.mli:1: ?b (2/3 calls) + ./examples/using_make/dir/matchopt.ml:1: ?y (3/4 calls) ./examples/using_make/dir/matchopt.ml:1: ?z (3/4 calls) - ./examples/using_make/let_in.ml:1: ?b (2/3 calls) ./examples/using_make/matchopt.ml:1: ?y (3/4 calls) ./examples/using_make/matchopt.ml:1: ?z (3/4 calls) @@ -1209,6 +1269,32 @@ Nothing else to report in this section .> CODING STYLE: =============== +./examples/docs/exported_values/code_constructs/functor/functor_lib.ml:16: unit pattern used_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.ml:17: unit pattern unused_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.ml:21: unit pattern used_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.ml:22: unit pattern unused_required + +./examples/docs/exported_values/code_constructs/include/include_lib.ml:3: unit pattern used_directly +./examples/docs/exported_values/code_constructs/include/include_lib.ml:4: unit pattern used_indirectly +./examples/docs/exported_values/code_constructs/include/include_lib.ml:5: unit pattern unused +./examples/docs/exported_values/code_constructs/include/include_lib.ml:14: unit pattern used_directly +./examples/docs/exported_values/code_constructs/include/include_lib.ml:15: unit pattern unused + +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:3: unit pattern used_directly +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:4: unit pattern used_indirectly +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:5: unit pattern used_by_requirement +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:6: unit pattern unused + +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.ml:11: unit pattern externally_used +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.ml:12: unit pattern internally_used +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.ml:13: unit pattern unused +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.ml:14: unit pattern unused_unexported + +./examples/docs/exported_values/code_constructs/module/module_lib.ml:4: unit pattern externally_used +./examples/docs/exported_values/code_constructs/module/module_lib.ml:5: unit pattern internally_used +./examples/docs/exported_values/code_constructs/module/module_lib.ml:6: unit pattern unused +./examples/docs/exported_values/code_constructs/module/module_lib.ml:7: unit pattern unused_unexported + ./examples/using_dune/preprocessed_lib/preprocessed.ml:83: let () = ... in ... (=> use sequence) ./examples/using_dune/preprocessed_lib/preprocessed.ml:83: unit pattern unit_binding ./examples/using_dune/preprocessed_lib/preprocessed.ml:84: val f: ... -> (... -> ?_:_ -> ...) -> ... 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 1b6277b2..442c1413 100644 --- a/check/threshold-3-0.5/threshold-3-0.5.ref +++ b/check/threshold-3-0.5/threshold-3-0.5.ref @@ -1,5 +1,21 @@ .> UNUSED EXPORTED VALUES: ========================= +./examples/docs/exported_values/code_constructs/function/function_lib.mli:7: unused + +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:10: F.unused +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:26: InternalApp.unused + +./examples/docs/exported_values/code_constructs/include/include_lib.ml:5: Original.unused +./examples/docs/exported_values/code_constructs/include/include_lib.ml:5: Reexport.unused: Should not be detected +./examples/docs/exported_values/code_constructs/include/include_lib.ml:15: Redefine.unused + +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:6: Original.unused +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:12: Alias_with_sig.used_by_requirement + +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:19: M_redef.unused + +./examples/docs/exported_values/code_constructs/module/module_lib.mli:6: M.unused + ./examples/using_dune/preprocessed_lib/preprocessed.mli:1: unused ./examples/using_dune/preprocessed_lib/preprocessed_no_intf.ml:2: unused @@ -84,6 +100,41 @@ .>-> ALMOST UNUSED EXPORTED VALUES: Called 1 time(s): ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +./examples/docs/exported_values/code_constructs/function/function_lib.mli:3: memoize +./examples/docs/exported_values/code_constructs/function/function_lib.mli:5: heavy_computation +./examples/docs/exported_values/code_constructs/function/function_lib.mli:9: do_nothing + +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:8: F.externally_used +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:9: F.internally_used +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:14: InternalParam.used_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:15: InternalParam.unused_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:24: InternalApp.externally_used +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:25: InternalApp.internally_used + +./examples/docs/exported_values/code_constructs/include/include_lib.ml:3: Original.used_directly +./examples/docs/exported_values/code_constructs/include/include_lib.ml:3: Reexport.used_directly: Should not be detected +./examples/docs/exported_values/code_constructs/include/include_lib.ml:4: Original.used_indirectly +./examples/docs/exported_values/code_constructs/include/include_lib.ml:4: Redefine.used_indirectly: Should not be detected +./examples/docs/exported_values/code_constructs/include/include_lib.ml:4: Reexport.used_indirectly: Should not be detected +./examples/docs/exported_values/code_constructs/include/include_lib.ml:14: Redefine.used_directly + +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:3: Original.used_directly +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:4: Original.used_indirectly +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:5: Original.used_by_requirement + +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:17: M_redef.externally_used +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:18: M_redef.internally_used + +./examples/docs/exported_values/code_constructs/module/module_lib.mli:4: M.externally_used +./examples/docs/exported_values/code_constructs/module/module_lib.mli:5: M.internally_used + +./examples/docs/exported_values/hello_world/hello_world.mli:2: hello +./examples/docs/exported_values/hello_world/hello_world.mli:3: goodbye +./examples/docs/exported_values/hello_world/hello_world_lib.mli:2: hello +./examples/docs/exported_values/hello_world/hello_world_lib.mli:3: goodbye +./examples/docs/exported_values/hello_world/hello_world_no_intf.ml:2: hello +./examples/docs/exported_values/hello_world/hello_world_no_intf.ml:3: goodbye + ./examples/using_dune/bin/use_preprocessed_lib/use_preprocessed.mli:1: mark_used ./examples/using_dune/bin/use_preprocessed_lib/use_preprocessed_lib.mli:1: mark_used ./examples/using_dune/bin/use_preprocessed_lib/use_preprocessed_no_intf.mli:1: mark_used @@ -287,6 +338,17 @@ .>-> ALMOST UNUSED EXPORTED VALUES: Called 2 time(s): ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:19: ExternalParam.used_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:20: ExternalParam.unused_required + +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:13: M_subst.externally_used +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:13: M_subst.internally_used +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:13: M_subst.unused + +./examples/docs/exported_values/hello_world/hello_world.mli:4: world +./examples/docs/exported_values/hello_world/hello_world_lib.mli:4: world +./examples/docs/exported_values/hello_world/hello_world_no_intf.ml:4: world + ./examples/using_dune/preprocessed_lib/preprocessed.mli:2: used ./examples/using_dune/preprocessed_lib/preprocessed.mli:31: exported_f ./examples/using_dune/preprocessed_lib/preprocessed.mli:38: internally_used_f @@ -993,7 +1055,9 @@ Nothing else to report in this section ./examples/using_make/dir/anonFn2.mli:2: ?a (2/3 calls) ./examples/using_make/dir/anonFn2.mli:2: ?b (2/3 calls) ./examples/using_make/dir/match_opt.ml:1: ?b (2/3 calls) + ./examples/using_make/dir/matchopt.ml:1: ?x (3/4 calls) + ./examples/using_make/dir/ref_fn.ml:1: ?a (2/3 calls) ./examples/using_make/dir/ref_fn.ml:1: ?b (2/3 calls) @@ -1192,6 +1256,7 @@ Nothing else to report in this section ./examples/using_make/dir/anonFn.mli:3: ?a (1/2 calls) ./examples/using_make/dir/anonFn.mli:3: ?b (1/2 calls) ./examples/using_make/dir/anonFn2.mli:1: ?b (2/3 calls) + ./examples/using_make/dir/matchopt.ml:1: ?y (3/4 calls) ./examples/using_make/dir/matchopt.ml:1: ?z (3/4 calls) ./examples/using_make/let_in.ml:1: ?b (2/3 calls) @@ -1208,6 +1273,32 @@ Nothing else to report in this section .> CODING STYLE: =============== +./examples/docs/exported_values/code_constructs/functor/functor_lib.ml:16: unit pattern used_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.ml:17: unit pattern unused_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.ml:21: unit pattern used_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.ml:22: unit pattern unused_required + +./examples/docs/exported_values/code_constructs/include/include_lib.ml:3: unit pattern used_directly +./examples/docs/exported_values/code_constructs/include/include_lib.ml:4: unit pattern used_indirectly +./examples/docs/exported_values/code_constructs/include/include_lib.ml:5: unit pattern unused +./examples/docs/exported_values/code_constructs/include/include_lib.ml:14: unit pattern used_directly +./examples/docs/exported_values/code_constructs/include/include_lib.ml:15: unit pattern unused + +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:3: unit pattern used_directly +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:4: unit pattern used_indirectly +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:5: unit pattern used_by_requirement +./examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml:6: unit pattern unused + +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.ml:11: unit pattern externally_used +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.ml:12: unit pattern internally_used +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.ml:13: unit pattern unused +./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.ml:14: unit pattern unused_unexported + +./examples/docs/exported_values/code_constructs/module/module_lib.ml:4: unit pattern externally_used +./examples/docs/exported_values/code_constructs/module/module_lib.ml:5: unit pattern internally_used +./examples/docs/exported_values/code_constructs/module/module_lib.ml:6: unit pattern unused +./examples/docs/exported_values/code_constructs/module/module_lib.ml:7: unit pattern unused_unexported + ./examples/using_dune/preprocessed_lib/preprocessed.ml:83: let () = ... in ... (=> use sequence) ./examples/using_dune/preprocessed_lib/preprocessed.ml:83: unit pattern unit_binding ./examples/using_dune/preprocessed_lib/preprocessed.ml:84: val f: ... -> (... -> ?_:_ -> ...) -> ... @@ -1255,7 +1346,7 @@ Nothing else to report in this section -------------------------------------------------------------------------------- -Total: 1071 -Success: 1071 -Failed: 0 -Ratio: 100.% +Total: 1138 +Success: 1134 +Failed: 4 +Ratio: 99.6485061511% diff --git a/docs/exported_values/EXPORTED_VALUES.md b/docs/exported_values/EXPORTED_VALUES.md index a93afb25..c872261e 100644 --- a/docs/exported_values/EXPORTED_VALUES.md +++ b/docs/exported_values/EXPORTED_VALUES.md @@ -213,7 +213,7 @@ Warning 26 [unused-var]: unused variable y. ## Hello world -All of the following examples can be found in the [hello\_world](./hello_world) +All of the following examples can be found in the [hello\_world](../../examples/docs/exported_values/hello_world) directory. The reference takes place in `/tmp/docs/exported_values/hello_world`, which @@ -226,7 +226,7 @@ This example illustrates a simple case of a compilation unit without `.mli` and without any external use. The reference file for this example is -[`hello_world_no_intf.ml`](./hello_world/hello_world_no_intf.ml). +[`hello_world_no_intf.ml`](../../examples/docs/exported_values/hello_world/hello_world_no_intf.ml). The compilation command to produce `hello_world_no_intf.cmi` and `hello_world_no_intf.cmt` is : @@ -365,8 +365,8 @@ Although an interface is provided, all the uses remain inside the same compilation unit. The reference files for this example are -[`hello_world.mli`](./hello_world/hello_world.mli) and -[`hello_world.ml`](./hello_world/hello_world.ml) +[`hello_world.mli`](../../examples/docs/exported_values/hello_world/hello_world.mli) and +[`hello_world.ml`](../../examples/docs/exported_values/hello_world/hello_world.ml) The compilation command to produce `hello_world.cmi` and `hello_world.cmt` is : ``` @@ -488,9 +488,9 @@ This example is the same as the previous example, split in 2 separate compilation units. All the exported values are now used externally. The reference files for this example are -[`hello_world_lib.mli`](./hello_world/hello_world_lib.mli), -[`hello_world_lib.ml`](./hello_world/hello_world_lib.ml), and -[`hello_world_bin.ml`](./hello_world/hello_world_bin.ml) +[`hello_world_lib.mli`](../../examples/docs/exported_values/hello_world/hello_world_lib.mli), +[`hello_world_lib.ml`](../../examples/docs/exported_values/hello_world/hello_world_lib.ml), and +[`hello_world_bin.ml`](../../examples/docs/exported_values/hello_world/hello_world_bin.ml) The compilation command to produce the necessary `.cmi` and `.cmt` files is : ``` @@ -786,7 +786,7 @@ Our work here is done. ## Code constructs All of the following examples can be found in the -[code\_constructs](./code_constructs) directory. +[code\_constructs](../../examples/docs/exported_values/code_constructs) directory. The reference takes place in `/tmp/docs/exported_values/code_constructs`, which is a copy of `code_constructs`. Reported locations may differ depending on the @@ -795,7 +795,7 @@ location of the source files. ### Function The reference files for this example are in the -[function](./code_constructs/function) directory. +[function](../../examples/docs/exported_values/code_constructs/function) directory. The compilation command is : ``` @@ -967,7 +967,7 @@ fixes all the warnings. Our work here is done. ### Module The reference files for this example are in the -[module](./code_constructs/module) directory. +[module](../../examples/docs/exported_values/code_constructs/module) directory. The compilation command is : ``` @@ -1118,7 +1118,7 @@ anything. Removing that value fixes all the warnings. Our work here is done. ### Functor The reference files for this example are in the -[functor](./code_constructs/functor) directory. +[functor](../../examples/docs/exported_values/code_constructs/functor) directory. The compilation command is : ``` @@ -1445,7 +1445,7 @@ The unused values can be removed as explained. Our work here is done. ### Module Type The reference files for this example are in the -[modtyp](./code_constructs/modtyp) directory. +[modtyp](../../examples/docs/exported_values/code_constructs/modtyp) directory. The compilation command is : ``` @@ -1597,7 +1597,7 @@ Our work here is done. ### Module Signature The reference files for this example are in the -[modsig](./code_constructs/modsig) directory. +[modsig](../../examples/docs/exported_values/code_constructs/modsig) directory. The compilation command is : ``` @@ -1760,7 +1760,7 @@ values anymore. Our work here is done. ### Include The reference files for this example are in the -[include](./code_constructs/include) directory. +[include](../../examples/docs/exported_values/code_constructs/include) directory. The compilation command is : ``` diff --git a/examples/Makefile b/examples/Makefile index 5f43d246..0100a940 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -1,19 +1,26 @@ -.PHONY: clean run_dca build_dca build_dune build_make build dune_only make_only +.PHONY: clean run_dca build_dca build_dune build_make build_docs build dune_only make_only docs_only +CMTI_ROOT_DOCS=docs CMTI_ROOT_MAKE=using_make CMTI_ROOT_DUNE=using_dune/_build/default/ -CMTI_ROOT?=$(CMTI_ROOT_MAKE) $(CMTI_ROOT_DUNE) +CMTI_ROOT?=$(CMTI_ROOT_DOCS) $(CMTI_ROOT_MAKE) $(CMTI_ROOT_DUNE) REDIRECT?="" # Used by ../check/Makefile all: build build_dca run_dca +docs_only: build_make build_dca + make run_dca CMTI_ROOT=$(CMTI_ROOT_DOCS) + make_only: build_make build_dca make run_dca CMTI_ROOT=$(CMTI_ROOT_MAKE) dune_only: build_dune build_dca make run_dca CMTI_ROOT=$(CMTI_ROOT_DUNE) -build: build_make build_dune +build: build_docs build_make build_dune + +build_docs: + make -C docs build_make: make -C using_make @@ -29,5 +36,6 @@ run_dca: clean: rm -f *~ *.cm* *.o *.obj + make -C docs clean make -C using_make clean dune clean --root=using_dune diff --git a/examples/docs/Makefile b/examples/docs/Makefile new file mode 100644 index 00000000..ae38a077 --- /dev/null +++ b/examples/docs/Makefile @@ -0,0 +1,11 @@ +.PHONY: clean build + +all: build + +build: + make -C exported_values + +clean: + rm -f *~ *.cm* *.o *.obj + make -C exported_values clean + diff --git a/examples/docs/exported_values/Makefile b/examples/docs/exported_values/Makefile new file mode 100644 index 00000000..c452f3e9 --- /dev/null +++ b/examples/docs/exported_values/Makefile @@ -0,0 +1,13 @@ +.PHONY: clean build + +all: build + +build: + make -C hello_world + make -C code_constructs + +clean: + rm -f *~ *.cm* *.o *.obj + make -C hello_world clean + make -C code_constructs clean + diff --git a/examples/docs/exported_values/code_constructs/Makefile b/examples/docs/exported_values/code_constructs/Makefile new file mode 100644 index 00000000..ff3799a2 --- /dev/null +++ b/examples/docs/exported_values/code_constructs/Makefile @@ -0,0 +1,21 @@ +.PHONY: clean build + +all: build + +build: + make -C function build + make -C module build + make -C functor build + make -C modtyp build + make -C modsig build + make -C include build + +clean: + rm -f *~ *.cm* *.o *.obj + make -C function clean + make -C module clean + make -C functor clean + make -C modtyp clean + make -C modsig clean + make -C include clean + diff --git a/docs/exported_values/code_constructs/function/Makefile b/examples/docs/exported_values/code_constructs/function/Makefile similarity index 100% rename from docs/exported_values/code_constructs/function/Makefile rename to examples/docs/exported_values/code_constructs/function/Makefile diff --git a/docs/exported_values/code_constructs/function/function_bin.ml b/examples/docs/exported_values/code_constructs/function/function_bin.ml similarity index 100% rename from docs/exported_values/code_constructs/function/function_bin.ml rename to examples/docs/exported_values/code_constructs/function/function_bin.ml diff --git a/docs/exported_values/code_constructs/function/function_lib.ml b/examples/docs/exported_values/code_constructs/function/function_lib.ml similarity index 100% rename from docs/exported_values/code_constructs/function/function_lib.ml rename to examples/docs/exported_values/code_constructs/function/function_lib.ml diff --git a/docs/exported_values/code_constructs/function/function_lib.mli b/examples/docs/exported_values/code_constructs/function/function_lib.mli similarity index 100% rename from docs/exported_values/code_constructs/function/function_lib.mli rename to examples/docs/exported_values/code_constructs/function/function_lib.mli diff --git a/docs/exported_values/code_constructs/functor/Makefile b/examples/docs/exported_values/code_constructs/functor/Makefile similarity index 100% rename from docs/exported_values/code_constructs/functor/Makefile rename to examples/docs/exported_values/code_constructs/functor/Makefile diff --git a/docs/exported_values/code_constructs/functor/functor_bin.ml b/examples/docs/exported_values/code_constructs/functor/functor_bin.ml similarity index 100% rename from docs/exported_values/code_constructs/functor/functor_bin.ml rename to examples/docs/exported_values/code_constructs/functor/functor_bin.ml diff --git a/docs/exported_values/code_constructs/functor/functor_lib.ml b/examples/docs/exported_values/code_constructs/functor/functor_lib.ml similarity index 100% rename from docs/exported_values/code_constructs/functor/functor_lib.ml rename to examples/docs/exported_values/code_constructs/functor/functor_lib.ml diff --git a/docs/exported_values/code_constructs/functor/functor_lib.mli b/examples/docs/exported_values/code_constructs/functor/functor_lib.mli similarity index 100% rename from docs/exported_values/code_constructs/functor/functor_lib.mli rename to examples/docs/exported_values/code_constructs/functor/functor_lib.mli diff --git a/docs/exported_values/code_constructs/include/Makefile b/examples/docs/exported_values/code_constructs/include/Makefile similarity index 100% rename from docs/exported_values/code_constructs/include/Makefile rename to examples/docs/exported_values/code_constructs/include/Makefile diff --git a/docs/exported_values/code_constructs/include/include_bin.ml b/examples/docs/exported_values/code_constructs/include/include_bin.ml similarity index 100% rename from docs/exported_values/code_constructs/include/include_bin.ml rename to examples/docs/exported_values/code_constructs/include/include_bin.ml diff --git a/docs/exported_values/code_constructs/include/include_lib.ml b/examples/docs/exported_values/code_constructs/include/include_lib.ml similarity index 100% rename from docs/exported_values/code_constructs/include/include_lib.ml rename to examples/docs/exported_values/code_constructs/include/include_lib.ml diff --git a/docs/exported_values/code_constructs/modsig/Makefile b/examples/docs/exported_values/code_constructs/modsig/Makefile similarity index 100% rename from docs/exported_values/code_constructs/modsig/Makefile rename to examples/docs/exported_values/code_constructs/modsig/Makefile diff --git a/docs/exported_values/code_constructs/modsig/modsig_bin.ml b/examples/docs/exported_values/code_constructs/modsig/modsig_bin.ml similarity index 100% rename from docs/exported_values/code_constructs/modsig/modsig_bin.ml rename to examples/docs/exported_values/code_constructs/modsig/modsig_bin.ml diff --git a/docs/exported_values/code_constructs/modsig/modsig_lib.ml b/examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml similarity index 100% rename from docs/exported_values/code_constructs/modsig/modsig_lib.ml rename to examples/docs/exported_values/code_constructs/modsig/modsig_lib.ml diff --git a/docs/exported_values/code_constructs/modtyp/Makefile b/examples/docs/exported_values/code_constructs/modtyp/Makefile similarity index 100% rename from docs/exported_values/code_constructs/modtyp/Makefile rename to examples/docs/exported_values/code_constructs/modtyp/Makefile diff --git a/docs/exported_values/code_constructs/modtyp/modtyp_bin.ml b/examples/docs/exported_values/code_constructs/modtyp/modtyp_bin.ml similarity index 100% rename from docs/exported_values/code_constructs/modtyp/modtyp_bin.ml rename to examples/docs/exported_values/code_constructs/modtyp/modtyp_bin.ml diff --git a/docs/exported_values/code_constructs/modtyp/modtyp_lib.ml b/examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.ml similarity index 100% rename from docs/exported_values/code_constructs/modtyp/modtyp_lib.ml rename to examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.ml diff --git a/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli b/examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli similarity index 100% rename from docs/exported_values/code_constructs/modtyp/modtyp_lib.mli rename to examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli diff --git a/docs/exported_values/code_constructs/module/Makefile b/examples/docs/exported_values/code_constructs/module/Makefile similarity index 100% rename from docs/exported_values/code_constructs/module/Makefile rename to examples/docs/exported_values/code_constructs/module/Makefile diff --git a/docs/exported_values/code_constructs/module/module_bin.ml b/examples/docs/exported_values/code_constructs/module/module_bin.ml similarity index 100% rename from docs/exported_values/code_constructs/module/module_bin.ml rename to examples/docs/exported_values/code_constructs/module/module_bin.ml diff --git a/docs/exported_values/code_constructs/module/module_lib.ml b/examples/docs/exported_values/code_constructs/module/module_lib.ml similarity index 100% rename from docs/exported_values/code_constructs/module/module_lib.ml rename to examples/docs/exported_values/code_constructs/module/module_lib.ml diff --git a/docs/exported_values/code_constructs/module/module_lib.mli b/examples/docs/exported_values/code_constructs/module/module_lib.mli similarity index 100% rename from docs/exported_values/code_constructs/module/module_lib.mli rename to examples/docs/exported_values/code_constructs/module/module_lib.mli diff --git a/examples/docs/exported_values/hello_world/Makefile b/examples/docs/exported_values/hello_world/Makefile new file mode 100644 index 00000000..6285d3ee --- /dev/null +++ b/examples/docs/exported_values/hello_world/Makefile @@ -0,0 +1,19 @@ +.PHONY: clean build + +COMPFLAGS=-w +32 -bin-annot -keep-locs +OCAMLC=ocamlc $(COMPFLAGS) +SRC=hello_world_no_intf.ml\ + hello_world.mli hello_world.ml\ + hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml +CMTI=$(filter %.cmi,$(SRC:.ml=.cmi)) $(filter %.cmt,$(SRC:.ml=.cmt)) + +all: build + +build: $(CMTI) + +$(CMTI): $(SRC) + $(OCAMLC) -c $(SRC) + +clean: + rm -f *~ *.cm* *.o *.obj + diff --git a/docs/exported_values/hello_world/hello_world.ml b/examples/docs/exported_values/hello_world/hello_world.ml similarity index 100% rename from docs/exported_values/hello_world/hello_world.ml rename to examples/docs/exported_values/hello_world/hello_world.ml diff --git a/docs/exported_values/hello_world/hello_world.mli b/examples/docs/exported_values/hello_world/hello_world.mli similarity index 100% rename from docs/exported_values/hello_world/hello_world.mli rename to examples/docs/exported_values/hello_world/hello_world.mli diff --git a/docs/exported_values/hello_world/hello_world_bin.ml b/examples/docs/exported_values/hello_world/hello_world_bin.ml similarity index 100% rename from docs/exported_values/hello_world/hello_world_bin.ml rename to examples/docs/exported_values/hello_world/hello_world_bin.ml diff --git a/docs/exported_values/hello_world/hello_world_lib.ml b/examples/docs/exported_values/hello_world/hello_world_lib.ml similarity index 100% rename from docs/exported_values/hello_world/hello_world_lib.ml rename to examples/docs/exported_values/hello_world/hello_world_lib.ml diff --git a/docs/exported_values/hello_world/hello_world_lib.mli b/examples/docs/exported_values/hello_world/hello_world_lib.mli similarity index 100% rename from docs/exported_values/hello_world/hello_world_lib.mli rename to examples/docs/exported_values/hello_world/hello_world_lib.mli diff --git a/docs/exported_values/hello_world/hello_world_no_intf.ml b/examples/docs/exported_values/hello_world/hello_world_no_intf.ml similarity index 100% rename from docs/exported_values/hello_world/hello_world_no_intf.ml rename to examples/docs/exported_values/hello_world/hello_world_no_intf.ml From b6a2bfc4f437d9232218a58fc6ecf5d51d722891 Mon Sep 17 00:00:00 2001 From: Corentin De Souza <9597216+fantazio@users.noreply.github.com> Date: Tue, 10 Mar 2026 14:58:16 +0100 Subject: [PATCH 14/16] [docs][exported_values][14/n] rename hello_world examples As suggested in the code review, renaming the compilation units to `hello_world_without_inf` and `hello_world_with_intf` provides a more direct parallel between the 2. --- check/classic/classic.exp | 6 +- check/classic/classic.ref | 6 +- check/threshold-1/threshold-1.exp | 8 +- check/threshold-1/threshold-1.ref | 8 +- check/threshold-3-0.5/threshold-3-0.5.exp | 12 +- check/threshold-3-0.5/threshold-3-0.5.ref | 12 +- docs/exported_values/EXPORTED_VALUES.md | 106 +++++++++--------- .../docs/exported_values/hello_world/Makefile | 4 +- ...ello_world.ml => hello_world_with_intf.ml} | 2 +- ...lo_world.mli => hello_world_with_intf.mli} | 2 +- ...no_intf.ml => hello_world_without_intf.ml} | 2 +- 11 files changed, 84 insertions(+), 84 deletions(-) rename examples/docs/exported_values/hello_world/{hello_world.ml => hello_world_with_intf.ml} (85%) rename examples/docs/exported_values/hello_world/{hello_world.mli => hello_world_with_intf.mli} (64%) rename examples/docs/exported_values/hello_world/{hello_world_no_intf.ml => hello_world_without_intf.ml} (84%) diff --git a/check/classic/classic.exp b/check/classic/classic.exp index 7cc0546a..c7289d0d 100644 --- a/check/classic/classic.exp +++ b/check/classic/classic.exp @@ -21,9 +21,9 @@ ./examples/docs/exported_values/code_constructs/module/module_lib.mli:5: M.internally_used ./examples/docs/exported_values/code_constructs/module/module_lib.mli:6: M.unused -./examples/docs/exported_values/hello_world/hello_world.mli:2: hello -./examples/docs/exported_values/hello_world/hello_world.mli:3: goodbye -./examples/docs/exported_values/hello_world/hello_world.mli:4: world +./examples/docs/exported_values/hello_world/hello_world_with_intf.mli:2: hello +./examples/docs/exported_values/hello_world/hello_world_with_intf.mli:3: goodbye +./examples/docs/exported_values/hello_world/hello_world_with_intf.mli:4: world ./examples/using_dune/preprocessed_lib/preprocessed.mli:1: unused ./examples/using_dune/preprocessed_lib/preprocessed.mli:3: internally_used diff --git a/check/classic/classic.ref b/check/classic/classic.ref index b7036208..98369634 100644 --- a/check/classic/classic.ref +++ b/check/classic/classic.ref @@ -22,9 +22,9 @@ ./examples/docs/exported_values/code_constructs/module/module_lib.mli:5: M.internally_used ./examples/docs/exported_values/code_constructs/module/module_lib.mli:6: M.unused -./examples/docs/exported_values/hello_world/hello_world.mli:2: hello -./examples/docs/exported_values/hello_world/hello_world.mli:3: goodbye -./examples/docs/exported_values/hello_world/hello_world.mli:4: world +./examples/docs/exported_values/hello_world/hello_world_with_intf.mli:2: hello +./examples/docs/exported_values/hello_world/hello_world_with_intf.mli:3: goodbye +./examples/docs/exported_values/hello_world/hello_world_with_intf.mli:4: world ./examples/using_dune/preprocessed_lib/preprocessed.mli:1: unused ./examples/using_dune/preprocessed_lib/preprocessed.mli:3: internally_used diff --git a/check/threshold-1/threshold-1.exp b/check/threshold-1/threshold-1.exp index b59d60eb..983f61ae 100644 --- a/check/threshold-1/threshold-1.exp +++ b/check/threshold-1/threshold-1.exp @@ -124,12 +124,12 @@ ./examples/docs/exported_values/code_constructs/module/module_lib.mli:4: M.externally_used ./examples/docs/exported_values/code_constructs/module/module_lib.mli:5: M.internally_used -./examples/docs/exported_values/hello_world/hello_world.mli:2: hello -./examples/docs/exported_values/hello_world/hello_world.mli:3: goodbye ./examples/docs/exported_values/hello_world/hello_world_lib.mli:2: hello ./examples/docs/exported_values/hello_world/hello_world_lib.mli:3: goodbye -./examples/docs/exported_values/hello_world/hello_world_no_intf.ml:2: hello -./examples/docs/exported_values/hello_world/hello_world_no_intf.ml:3: goodbye +./examples/docs/exported_values/hello_world/hello_world_with_intf.mli:2: hello +./examples/docs/exported_values/hello_world/hello_world_with_intf.mli:3: goodbye +./examples/docs/exported_values/hello_world/hello_world_without_intf.ml:2: hello +./examples/docs/exported_values/hello_world/hello_world_without_intf.ml:3: goodbye ./examples/using_dune/bin/use_preprocessed_lib/use_preprocessed.mli:1: mark_used ./examples/using_dune/bin/use_preprocessed_lib/use_preprocessed_lib.mli:1: mark_used diff --git a/check/threshold-1/threshold-1.ref b/check/threshold-1/threshold-1.ref index 75229b83..30dbf0e9 100644 --- a/check/threshold-1/threshold-1.ref +++ b/check/threshold-1/threshold-1.ref @@ -128,12 +128,12 @@ ./examples/docs/exported_values/code_constructs/module/module_lib.mli:4: M.externally_used ./examples/docs/exported_values/code_constructs/module/module_lib.mli:5: M.internally_used -./examples/docs/exported_values/hello_world/hello_world.mli:2: hello -./examples/docs/exported_values/hello_world/hello_world.mli:3: goodbye ./examples/docs/exported_values/hello_world/hello_world_lib.mli:2: hello ./examples/docs/exported_values/hello_world/hello_world_lib.mli:3: goodbye -./examples/docs/exported_values/hello_world/hello_world_no_intf.ml:2: hello -./examples/docs/exported_values/hello_world/hello_world_no_intf.ml:3: goodbye +./examples/docs/exported_values/hello_world/hello_world_with_intf.mli:2: hello +./examples/docs/exported_values/hello_world/hello_world_with_intf.mli:3: goodbye +./examples/docs/exported_values/hello_world/hello_world_without_intf.ml:2: hello +./examples/docs/exported_values/hello_world/hello_world_without_intf.ml:3: goodbye ./examples/using_dune/bin/use_preprocessed_lib/use_preprocessed.mli:1: mark_used ./examples/using_dune/bin/use_preprocessed_lib/use_preprocessed_lib.mli:1: mark_used 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 5e4daace..194ab8fe 100644 --- a/check/threshold-3-0.5/threshold-3-0.5.exp +++ b/check/threshold-3-0.5/threshold-3-0.5.exp @@ -124,12 +124,12 @@ ./examples/docs/exported_values/code_constructs/module/module_lib.mli:4: M.externally_used ./examples/docs/exported_values/code_constructs/module/module_lib.mli:5: M.internally_used -./examples/docs/exported_values/hello_world/hello_world.mli:2: hello -./examples/docs/exported_values/hello_world/hello_world.mli:3: goodbye ./examples/docs/exported_values/hello_world/hello_world_lib.mli:2: hello ./examples/docs/exported_values/hello_world/hello_world_lib.mli:3: goodbye -./examples/docs/exported_values/hello_world/hello_world_no_intf.ml:2: hello -./examples/docs/exported_values/hello_world/hello_world_no_intf.ml:3: goodbye +./examples/docs/exported_values/hello_world/hello_world_with_intf.mli:2: hello +./examples/docs/exported_values/hello_world/hello_world_with_intf.mli:3: goodbye +./examples/docs/exported_values/hello_world/hello_world_without_intf.ml:2: hello +./examples/docs/exported_values/hello_world/hello_world_without_intf.ml:3: goodbye ./examples/using_dune/bin/use_preprocessed_lib/use_preprocessed.mli:1: mark_used ./examples/using_dune/bin/use_preprocessed_lib/use_preprocessed_lib.mli:1: mark_used @@ -341,9 +341,9 @@ ./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:13: M_subst.internally_used ./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:13: M_subst.unused -./examples/docs/exported_values/hello_world/hello_world.mli:4: world ./examples/docs/exported_values/hello_world/hello_world_lib.mli:4: world -./examples/docs/exported_values/hello_world/hello_world_no_intf.ml:4: world +./examples/docs/exported_values/hello_world/hello_world_with_intf.mli:4: world +./examples/docs/exported_values/hello_world/hello_world_without_intf.ml:4: world ./examples/using_dune/preprocessed_lib/preprocessed.mli:2: used ./examples/using_dune/preprocessed_lib/preprocessed.mli:31: exported_f 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 442c1413..6566bf45 100644 --- a/check/threshold-3-0.5/threshold-3-0.5.ref +++ b/check/threshold-3-0.5/threshold-3-0.5.ref @@ -128,12 +128,12 @@ ./examples/docs/exported_values/code_constructs/module/module_lib.mli:4: M.externally_used ./examples/docs/exported_values/code_constructs/module/module_lib.mli:5: M.internally_used -./examples/docs/exported_values/hello_world/hello_world.mli:2: hello -./examples/docs/exported_values/hello_world/hello_world.mli:3: goodbye ./examples/docs/exported_values/hello_world/hello_world_lib.mli:2: hello ./examples/docs/exported_values/hello_world/hello_world_lib.mli:3: goodbye -./examples/docs/exported_values/hello_world/hello_world_no_intf.ml:2: hello -./examples/docs/exported_values/hello_world/hello_world_no_intf.ml:3: goodbye +./examples/docs/exported_values/hello_world/hello_world_with_intf.mli:2: hello +./examples/docs/exported_values/hello_world/hello_world_with_intf.mli:3: goodbye +./examples/docs/exported_values/hello_world/hello_world_without_intf.ml:2: hello +./examples/docs/exported_values/hello_world/hello_world_without_intf.ml:3: goodbye ./examples/using_dune/bin/use_preprocessed_lib/use_preprocessed.mli:1: mark_used ./examples/using_dune/bin/use_preprocessed_lib/use_preprocessed_lib.mli:1: mark_used @@ -345,9 +345,9 @@ ./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:13: M_subst.internally_used ./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:13: M_subst.unused -./examples/docs/exported_values/hello_world/hello_world.mli:4: world ./examples/docs/exported_values/hello_world/hello_world_lib.mli:4: world -./examples/docs/exported_values/hello_world/hello_world_no_intf.ml:4: world +./examples/docs/exported_values/hello_world/hello_world_with_intf.mli:4: world +./examples/docs/exported_values/hello_world/hello_world_without_intf.ml:4: world ./examples/using_dune/preprocessed_lib/preprocessed.mli:2: used ./examples/using_dune/preprocessed_lib/preprocessed.mli:31: exported_f diff --git a/docs/exported_values/EXPORTED_VALUES.md b/docs/exported_values/EXPORTED_VALUES.md index c872261e..46f88822 100644 --- a/docs/exported_values/EXPORTED_VALUES.md +++ b/docs/exported_values/EXPORTED_VALUES.md @@ -226,24 +226,24 @@ This example illustrates a simple case of a compilation unit without `.mli` and without any external use. The reference file for this example is -[`hello_world_no_intf.ml`](../../examples/docs/exported_values/hello_world/hello_world_no_intf.ml). +[`hello_world_without_intf.ml`](../../examples/docs/exported_values/hello_world/hello_world_without_intf.ml). -The compilation command to produce `hello_world_no_intf.cmi` and -`hello_world_no_intf.cmt` is : +The compilation command to produce `hello_world_without_intf.cmi` and +`hello_world_without_intf.cmt` is : ``` -ocamlopt -bin-annot hello_world_no_intf.ml +ocamlopt -bin-annot hello_world_without_intf.ml ``` The analysis command is : ``` -dead_code_analyzer --nothing -E all hello_world_no_intf.cmi hello_world_no_intf.cmt +dead_code_analyzer --nothing -E all hello_world_without_intf.cmi hello_world_without_intf.cmt ``` #### First run Code : ```OCaml -(* hello_world_no_intf.ml *) +(* hello_world_without_intf.ml *) let hello = "Hello" let goodbye = "Goodbye" let world = "World" @@ -256,8 +256,8 @@ let () = Compile : ``` -$ ocamlopt -bin-annot hello_world_no_intf.ml -File "hello_world_no_intf.ml", line 8, characters 6-19: +$ ocamlopt -bin-annot hello_world_without_intf.ml +File "hello_world_without_intf.ml", line 8, characters 6-19: 8 | let goodbye_world = goodbye ^ world in ^^^^^^^^^^^^^ Warning 26 [unused-var]: unused variable goodbye_world. @@ -266,11 +266,11 @@ Warning 26 [unused-var]: unused variable goodbye_world. The compiler reports a warning 26 on `goodbye_world`: `Warning 26 [unused-var]: unused variable goodbye_world.` This tells us that the _local_ value is unused, and, thus, can be removed at the -reported location: `File "hello_world_no_intf.ml", line 8` +reported location: `File "hello_world_without_intf.ml", line 8` Analyze : ``` -$ dead_code_analyzer --nothing -E all hello_world_no_intf.cmi hello_world_no_intf.cmt +$ dead_code_analyzer --nothing -E all hello_world_without_intf.cmi hello_world_without_intf.cmt Scanning files... [DONE] @@ -282,9 +282,9 @@ Nothing else to report in this section ``` The analyzer does not report any unused _exported_ value. There are 3 exported -in the `Hello_world_no_intf` compilation unit : `hello`, `goodbye` and `world`. -These are the top level values of `hello_world_no_intf.ml`. -They are all referenced internally. Because there is no `hello_world_no_intf.mli`, +in the `Hello_world_without_intf` compilation unit : `hello`, `goodbye` and `world`. +These are the top level values of `hello_world_without_intf.ml`. +They are all referenced internally. Because there is no `hello_world_without_intf.mli`, the internal uses are accounted for. Consequently, none of the exported values are considered unused by the analyzer. @@ -294,7 +294,7 @@ Let's remove the unused `goodbye_world`. Code : ```OCaml -(* hello_world_no_intf.ml *) +(* hello_world_without_intf.ml *) let hello = "Hello" let goodbye = "Goodbye" let world = "World" @@ -306,15 +306,15 @@ let () = Compile and analyze : ``` -$ ocamlopt -bin-annot hello_world_no_intf.ml +$ ocamlopt -bin-annot hello_world_without_intf.ml -$ dead_code_analyzer --nothing -E all hello_world_no_intf.cmi hello_world_no_intf.cmt +$ dead_code_analyzer --nothing -E all hello_world_without_intf.cmi hello_world_without_intf.cmt Scanning files... [DONE] .> UNUSED EXPORTED VALUES: ========================= -/tmp/docs/exported_values/hello_world/hello_world_no_intf.ml:3: goodbye +/tmp/docs/exported_values/hello_world/hello_world_without_intf.ml:3: goodbye Nothing else to report in this section -------------------------------------------------------------------------------- @@ -323,7 +323,7 @@ Nothing else to report in this section The compiler does not report any unused value. The analyzer reports that `goodbye` declared at line 3 is unused : -`/tmp/docs/exported_values/hello_world/hello_world_no_intf.ml:3: goodbye` +`/tmp/docs/exported_values/hello_world/hello_world_without_intf.ml:3: goodbye` Like the warning 26 above, this report tells us that `goodbye` can be removed at the reported location. @@ -331,7 +331,7 @@ at the reported location. Code : ```OCaml -(* hello_world_no_intf.ml *) +(* hello_world_without_intf.ml *) let hello = "Hello" let world = "World" @@ -342,9 +342,9 @@ let () = Compile and analyze : ``` -$ ocamlopt -bin-annot hello_world_no_intf.ml +$ ocamlopt -bin-annot hello_world_without_intf.ml -$ dead_code_analyzer --nothing -E all hello_world_no_intf.cmi hello_world_no_intf.cmt +$ dead_code_analyzer --nothing -E all hello_world_without_intf.cmi hello_world_without_intf.cmt Scanning files... [DONE] @@ -365,12 +365,12 @@ Although an interface is provided, all the uses remain inside the same compilation unit. The reference files for this example are -[`hello_world.mli`](../../examples/docs/exported_values/hello_world/hello_world.mli) and -[`hello_world.ml`](../../examples/docs/exported_values/hello_world/hello_world.ml) +[`hello_world_with_intf.mli`](../../examples/docs/exported_values/hello_world/hello_world_with_intf.mli) and +[`hello_world_with_intf.ml`](../../examples/docs/exported_values/hello_world/hello_world_with_intf.ml) The compilation command to produce `hello_world.cmi` and `hello_world.cmt` is : ``` -ocamlopt -bin-annot hello_world.mli hello_world.ml +ocamlopt -bin-annot hello_world_with_intf.mli hello_world_with_intf.ml ``` The analysis command is : @@ -382,13 +382,13 @@ dead_code_analyzer --nothing -E all hello_world.cmi hello_world.cmt Code : ```OCaml -(* hello_world.mli *) +(* hello_world_with_intf.mli *) val hello : string val goodbye : string val world : string ``` ```OCaml -(* hello_world.ml *) +(* hello_world_with_intf.ml *) let hello = "Hello" let goodbye = "Goodbye" let world = "World" @@ -401,8 +401,8 @@ let () = Compile and analyze : ``` -$ ocamlopt -bin-annot hello_world.ml -File "hello_world.ml", line 8, characters 6-19: +$ ocamlopt -bin-annot hello_world_with_intf.ml +File "hello_world_with_intf.ml", line 8, characters 6-19: 8 | let goodbye_world = goodbye ^ world in ^^^^^^^^^^^^^ Warning 26 [unused-var]: unused variable goodbye_world. @@ -413,9 +413,9 @@ Scanning files... .> UNUSED EXPORTED VALUES: ========================= -/tmp/docs/exported_values/hello_world/hello_world.mli:2: hello -/tmp/docs/exported_values/hello_world/hello_world.mli:3: goodbye -/tmp/docs/exported_values/hello_world/hello_world.mli:4: world +/tmp/docs/exported_values/hello_world/hello_world_with_intf.mli:2: hello +/tmp/docs/exported_values/hello_world/hello_world_with_intf.mli:3: goodbye +/tmp/docs/exported_values/hello_world/hello_world_with_intf.mli:4: world Nothing else to report in this section -------------------------------------------------------------------------------- @@ -425,24 +425,24 @@ The compiler reports the same warning 26 on `goodbye_world` as in the previous example. The analyzer reports that there are 3 unused _exported_ value in -`hello_world.mli`: `hello` at line 2, `goodbye` line 3, and `world` line 4. -These are the only exported values in the `Hello_world` compilation unit because -they are the only ones listed in the `.mli`. They are all used in -`hello_world.ml`, but not outside of their compilation unit. Because there is an +`hello_world_with_intf.mli`: `hello` at line 2, `goodbye` line 3, and `world` line 4. +These are the only exported values in the `Hello_world_with_intf` compilation +unit because they are the only ones listed in the `.mli`. They are all used in +`hello_world_with_intf.ml`, but not outside of their compilation unit. Because there is an interface file available, only external uses are accounted for. Thus, they are considered unused and can be dropped from the `.mli` #### Removing the unused values -Let's remove the unused `goodbye_world` from `hello_world.ml`, reported unused -by the compiler, and the values in `hello_world.mli` reported by the analyzer. +Let's remove the unused `goodbye_world` from `hello_world_with_intf.ml`, reported unused +by the compiler, and the values in `hello_world_with_intf.mli` reported by the analyzer. Code : ```OCaml -(* hello_world.mli *) +(* hello_world_with_intf.mli *) ``` ```OCaml -(* hello_world.ml *) +(* hello_world_with_intf.ml *) let hello = "Hello" let goodbye = "Goodbye" let world = "World" @@ -453,7 +453,7 @@ let () = ``` Compile and analyze : ``` -$ ocamlopt -bin-annot hello_world.ml +$ ocamlopt -bin-annot hello_world_with_intf.ml $ dead_code_analyzer --nothing -E all hello_world.cmi hello_world.cmt Scanning files... [DONE] @@ -473,8 +473,8 @@ because it is an _unexported_ value here (while it was an _exported_ value in the previous example). The corresponding warning is actually off by default and can be activated by passing the `-w +32` argument to the compiler : ``` -$ ocamlopt -w +32 hello_world.ml -File "hello_world.ml", line 3, characters 4-11: +$ ocamlopt -w +32 hello_world_with_intf.ml +File "hello_world_with_intf.ml", line 3, characters 4-11: 3 | let goodbye = "Goodbye" ^^^^^^^ Warning 32 [unused-value-declaration]: unused value goodbye. @@ -669,7 +669,7 @@ The reference files for this example are all those listed previously. The compilation command to produce the necessary `.cmi` and `.cmt` files, and the desired warnings is the combination of all the previous ones : ``` -ocamlopt -w +32 -bin-annot hello_world_no_intf.ml hello_world.mli hello_world.ml hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml +ocamlopt -w +32 -bin-annot hello_world_without_intf.ml hello_world_with_intf.mli hello_world_with_intf.ml hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml ``` > [!NOTE] @@ -695,13 +695,13 @@ previous examples. Compile : ``` -$ ocamlopt -w +32 -bin-annot hello_world_no_intf.ml hello_world.mli hello_world.ml hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml -File "hello_world_no_intf.ml", line 8, characters 6-19: +$ ocamlopt -w +32 -bin-annot hello_world_without_intf.ml hello_world_with_intf.mli hello_world_with_intf.ml hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml +File "hello_world_without_intf.ml", line 8, characters 6-19: 8 | let goodbye_world = goodbye ^ world in ^^^^^^^^^^^^^ Warning 26 [unused-var]: unused variable goodbye_world. -File "hello_world.ml", line 8, characters 6-19: +File "hello_world_with_intf.ml", line 8, characters 6-19: 8 | let goodbye_world = goodbye ^ world in ^^^^^^^^^^^^^ Warning 26 [unused-var]: unused variable goodbye_world. @@ -724,11 +724,11 @@ Scanning files... .> UNUSED EXPORTED VALUES: ========================= -/tmp/docs/exported_values/hello_world/hello_world.mli:2: hello -/tmp/docs/exported_values/hello_world/hello_world.mli:3: goodbye -/tmp/docs/exported_values/hello_world/hello_world.mli:4: world +/tmp/docs/exported_values/hello_world/hello_world_with_intf.mli:2: hello +/tmp/docs/exported_values/hello_world/hello_world_with_intf.mli:3: goodbye +/tmp/docs/exported_values/hello_world/hello_world_with_intf.mli:4: world /tmp/docs/exported_values/hello_world/hello_world_lib.mli:3: goodbye -/tmp/docs/exported_values/hello_world/hello_world_no_intf.ml:3: goodbye +/tmp/docs/exported_values/hello_world/hello_world_without_intf.ml:3: goodbye Nothing else to report in this section -------------------------------------------------------------------------------- @@ -747,8 +747,8 @@ locations reported by the analyzer. Compile : ``` -$ ocamlopt -w +32 -bin-annot hello_world_no_intf.ml hello_world.mli hello_world.ml hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml -File "hello_world.ml", line 3, characters 4-11: +$ ocamlopt -w +32 -bin-annot hello_world_without_intf.ml hello_world_with_intf.mli hello_world_with_intf.ml hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml +File "hello_world_with_intf.ml", line 3, characters 4-11: 3 | let goodbye = "Goodbye" ^^^^^^^ Warning 32 [unused-value-declaration]: unused value goodbye. @@ -766,7 +766,7 @@ Let's fix the warnings. Compile and analyze : ``` -$ ocamlopt -w +32 -bin-annot hello_world_no_intf.ml hello_world.mli hello_world.ml hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml +$ ocamlopt -w +32 -bin-annot hello_world_without_intf.ml hello_world_with_intf.mli hello_world_with_intf.ml hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml $ dead_code_analyzer --nothing -E all . Scanning files... diff --git a/examples/docs/exported_values/hello_world/Makefile b/examples/docs/exported_values/hello_world/Makefile index 6285d3ee..45da992c 100644 --- a/examples/docs/exported_values/hello_world/Makefile +++ b/examples/docs/exported_values/hello_world/Makefile @@ -2,8 +2,8 @@ COMPFLAGS=-w +32 -bin-annot -keep-locs OCAMLC=ocamlc $(COMPFLAGS) -SRC=hello_world_no_intf.ml\ - hello_world.mli hello_world.ml\ +SRC=hello_world_without_intf.ml\ + hello_world_with_intf.mli hello_world_with_intf.ml\ hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml CMTI=$(filter %.cmi,$(SRC:.ml=.cmi)) $(filter %.cmt,$(SRC:.ml=.cmt)) diff --git a/examples/docs/exported_values/hello_world/hello_world.ml b/examples/docs/exported_values/hello_world/hello_world_with_intf.ml similarity index 85% rename from examples/docs/exported_values/hello_world/hello_world.ml rename to examples/docs/exported_values/hello_world/hello_world_with_intf.ml index ac2bc5f6..a31aab06 100644 --- a/examples/docs/exported_values/hello_world/hello_world.ml +++ b/examples/docs/exported_values/hello_world/hello_world_with_intf.ml @@ -1,4 +1,4 @@ -(* hello_world.ml *) +(* hello_world_with_intf.ml *) let hello = "Hello" let goodbye = "Goodbye" let world = "World" diff --git a/examples/docs/exported_values/hello_world/hello_world.mli b/examples/docs/exported_values/hello_world/hello_world_with_intf.mli similarity index 64% rename from examples/docs/exported_values/hello_world/hello_world.mli rename to examples/docs/exported_values/hello_world/hello_world_with_intf.mli index aae4f08e..1dfb73c6 100644 --- a/examples/docs/exported_values/hello_world/hello_world.mli +++ b/examples/docs/exported_values/hello_world/hello_world_with_intf.mli @@ -1,4 +1,4 @@ -(* hello_world.mli *) +(* hello_world_with_intf.mli *) val hello : string val goodbye : string val world : string diff --git a/examples/docs/exported_values/hello_world/hello_world_no_intf.ml b/examples/docs/exported_values/hello_world/hello_world_without_intf.ml similarity index 84% rename from examples/docs/exported_values/hello_world/hello_world_no_intf.ml rename to examples/docs/exported_values/hello_world/hello_world_without_intf.ml index d498448f..842cc316 100644 --- a/examples/docs/exported_values/hello_world/hello_world_no_intf.ml +++ b/examples/docs/exported_values/hello_world/hello_world_without_intf.ml @@ -1,4 +1,4 @@ -(* hello_world_no_intf.ml *) +(* hello_world_without_intf.ml *) let hello = "Hello" let goodbye = "Goodbye" let world = "World" From 6d403d6feef6e307b7d6ab4344fc6bfff88ab964 Mon Sep 17 00:00:00 2001 From: Corentin De Souza <9597216+fantazio@users.noreply.github.com> Date: Tue, 10 Mar 2026 15:28:35 +0100 Subject: [PATCH 15/16] [docs][exported_values][15/n] extract examples to their own `.md` --- docs/exported_values/EXPORTED_VALUES.md | 1731 +---------------- docs/exported_values/HELLO_WORLD.md | 575 ++++++ .../code_constructs/FUNCTION.md | 173 ++ .../code_constructs/FUNCTOR.md | 329 ++++ .../code_constructs/INCLUDE.md | 163 ++ .../exported_values/code_constructs/MODSIG.md | 165 ++ .../exported_values/code_constructs/MODTYP.md | 154 ++ .../exported_values/code_constructs/MODULE.md | 153 ++ 8 files changed, 1725 insertions(+), 1718 deletions(-) create mode 100644 docs/exported_values/HELLO_WORLD.md create mode 100644 docs/exported_values/code_constructs/FUNCTION.md create mode 100644 docs/exported_values/code_constructs/FUNCTOR.md create mode 100644 docs/exported_values/code_constructs/INCLUDE.md create mode 100644 docs/exported_values/code_constructs/MODSIG.md create mode 100644 docs/exported_values/code_constructs/MODTYP.md create mode 100644 docs/exported_values/code_constructs/MODULE.md diff --git a/docs/exported_values/EXPORTED_VALUES.md b/docs/exported_values/EXPORTED_VALUES.md index 46f88822..715bf97f 100644 --- a/docs/exported_values/EXPORTED_VALUES.md +++ b/docs/exported_values/EXPORTED_VALUES.md @@ -8,18 +8,6 @@ + [Warning 32: unused-value-declaration](#warning-32-unused-value-declaration) + [Usage](#usage) + [Examples](#examples) - + [Hello world](#hello-world) - + [Single compilation unit without interface](#single-compilation-unit-without-interface) - + [Single compilation unit with interface](#single-compilation-unit-with-interface) - + [Multiple compilation units](#multiple-compilation-units) - + [All Together](#all-together) - + [Code constructs](#code-constructs) - + [Function](#function) - + [Module](#module) - + [Functor](#functor) - + [Module type](#module-type) - + [Module signature](#module-signature) - + [Include](#include) # Exported Values @@ -211,1709 +199,16 @@ Warning 26 [unused-var]: unused variable y. # Examples -## Hello world - -All of the following examples can be found in the [hello\_world](../../examples/docs/exported_values/hello_world) -directory. - -The reference takes place in `/tmp/docs/exported_values/hello_world`, which -contains copies of the examples. Reported locations may differ depending on the -location of the source files. - -### Single compilation unit without interface - -This example illustrates a simple case of a compilation unit without `.mli` and -without any external use. - -The reference file for this example is -[`hello_world_without_intf.ml`](../../examples/docs/exported_values/hello_world/hello_world_without_intf.ml). - -The compilation command to produce `hello_world_without_intf.cmi` and -`hello_world_without_intf.cmt` is : -``` -ocamlopt -bin-annot hello_world_without_intf.ml -``` - -The analysis command is : -``` -dead_code_analyzer --nothing -E all hello_world_without_intf.cmi hello_world_without_intf.cmt -``` - -#### First run - -Code : -```OCaml -(* hello_world_without_intf.ml *) -let hello = "Hello" -let goodbye = "Goodbye" -let world = "World" - -let () = - let hello_world = hello ^ world in - let goodbye_world = goodbye ^ world in - print_endline hello_world -``` - -Compile : -``` -$ ocamlopt -bin-annot hello_world_without_intf.ml -File "hello_world_without_intf.ml", line 8, characters 6-19: -8 | let goodbye_world = goodbye ^ world in - ^^^^^^^^^^^^^ -Warning 26 [unused-var]: unused variable goodbye_world. -``` - -The compiler reports a warning 26 on `goodbye_world`: -`Warning 26 [unused-var]: unused variable goodbye_world.` -This tells us that the _local_ value is unused, and, thus, can be removed at the -reported location: `File "hello_world_without_intf.ml", line 8` - -Analyze : -``` -$ dead_code_analyzer --nothing -E all hello_world_without_intf.cmi hello_world_without_intf.cmt -Scanning files... - [DONE] - -.> UNUSED EXPORTED VALUES: -========================= - -Nothing else to report in this section --------------------------------------------------------------------------------- -``` - -The analyzer does not report any unused _exported_ value. There are 3 exported -in the `Hello_world_without_intf` compilation unit : `hello`, `goodbye` and `world`. -These are the top level values of `hello_world_without_intf.ml`. -They are all referenced internally. Because there is no `hello_world_without_intf.mli`, -the internal uses are accounted for. Consequently, none of the exported values -are considered unused by the analyzer. - -#### Fixing the warning 26 - -Let's remove the unused `goodbye_world`. - -Code : -```OCaml -(* hello_world_without_intf.ml *) -let hello = "Hello" -let goodbye = "Goodbye" -let world = "World" - -let () = - let hello_world = hello ^ world in - print_endline hello_world -``` - -Compile and analyze : -``` -$ ocamlopt -bin-annot hello_world_without_intf.ml - -$ dead_code_analyzer --nothing -E all hello_world_without_intf.cmi hello_world_without_intf.cmt -Scanning files... - [DONE] - -.> UNUSED EXPORTED VALUES: -========================= -/tmp/docs/exported_values/hello_world/hello_world_without_intf.ml:3: goodbye - -Nothing else to report in this section --------------------------------------------------------------------------------- -``` - -The compiler does not report any unused value. - -The analyzer reports that `goodbye` declared at line 3 is unused : -`/tmp/docs/exported_values/hello_world/hello_world_without_intf.ml:3: goodbye` -Like the warning 26 above, this report tells us that `goodbye` can be removed -at the reported location. - -#### Removing the unused `goodbye` - -Code : -```OCaml -(* hello_world_without_intf.ml *) -let hello = "Hello" -let world = "World" - -let () = - let hello_world = hello ^ world in - print_endline hello_world -``` - -Compile and analyze : -``` -$ ocamlopt -bin-annot hello_world_without_intf.ml - -$ dead_code_analyzer --nothing -E all hello_world_without_intf.cmi hello_world_without_intf.cmt -Scanning files... - [DONE] - -.> UNUSED EXPORTED VALUES: -========================= - -Nothing else to report in this section --------------------------------------------------------------------------------- -``` - -Now, neither the compiler nor the analyzer report any unused value. -Our work here is done. - -### Single compilation unit with interface - -This example is the same as the previous example, with an explicit `.mli`. -Although an interface is provided, all the uses remain inside the same -compilation unit. - -The reference files for this example are -[`hello_world_with_intf.mli`](../../examples/docs/exported_values/hello_world/hello_world_with_intf.mli) and -[`hello_world_with_intf.ml`](../../examples/docs/exported_values/hello_world/hello_world_with_intf.ml) - -The compilation command to produce `hello_world.cmi` and `hello_world.cmt` is : -``` -ocamlopt -bin-annot hello_world_with_intf.mli hello_world_with_intf.ml -``` - -The analysis command is : -``` -dead_code_analyzer --nothing -E all hello_world.cmi hello_world.cmt -``` - -#### First run - -Code : -```OCaml -(* hello_world_with_intf.mli *) -val hello : string -val goodbye : string -val world : string -``` -```OCaml -(* hello_world_with_intf.ml *) -let hello = "Hello" -let goodbye = "Goodbye" -let world = "World" - -let () = - let hello_world = hello ^ world in - let goodbye_world = goodbye ^ world in - print_endline hello_world -``` - -Compile and analyze : -``` -$ ocamlopt -bin-annot hello_world_with_intf.ml -File "hello_world_with_intf.ml", line 8, characters 6-19: -8 | let goodbye_world = goodbye ^ world in - ^^^^^^^^^^^^^ -Warning 26 [unused-var]: unused variable goodbye_world. - -$ dead_code_analyzer --nothing -E all hello_world.cmi hello_world.cmt -Scanning files... - [DONE] - -.> UNUSED EXPORTED VALUES: -========================= -/tmp/docs/exported_values/hello_world/hello_world_with_intf.mli:2: hello -/tmp/docs/exported_values/hello_world/hello_world_with_intf.mli:3: goodbye -/tmp/docs/exported_values/hello_world/hello_world_with_intf.mli:4: world - -Nothing else to report in this section --------------------------------------------------------------------------------- -``` - -The compiler reports the same warning 26 on `goodbye_world` as in the previous -example. - -The analyzer reports that there are 3 unused _exported_ value in -`hello_world_with_intf.mli`: `hello` at line 2, `goodbye` line 3, and `world` line 4. -These are the only exported values in the `Hello_world_with_intf` compilation -unit because they are the only ones listed in the `.mli`. They are all used in -`hello_world_with_intf.ml`, but not outside of their compilation unit. Because there is an -interface file available, only external uses are accounted for. Thus, they are -considered unused and can be dropped from the `.mli` - -#### Removing the unused values - -Let's remove the unused `goodbye_world` from `hello_world_with_intf.ml`, reported unused -by the compiler, and the values in `hello_world_with_intf.mli` reported by the analyzer. - -Code : -```OCaml -(* hello_world_with_intf.mli *) -``` -```OCaml -(* hello_world_with_intf.ml *) -let hello = "Hello" -let goodbye = "Goodbye" -let world = "World" - -let () = - let hello_world = hello ^ world in - print_endline hello_world -``` -Compile and analyze : -``` -$ ocamlopt -bin-annot hello_world_with_intf.ml -$ dead_code_analyzer --nothing -E all hello_world.cmi hello_world.cmt -Scanning files... - [DONE] - -.> UNUSED EXPORTED VALUES: -========================= - -Nothing else to report in this section --------------------------------------------------------------------------------- -``` - -Now, neither the compiler nor the analyzer report any unused value. - -We learned from the previous example that the `goodbye` value is unused after -we remove `goodbye_world`. This is on the compiler to report it in this example -because it is an _unexported_ value here (while it was an _exported_ value in -the previous example). The corresponding warning is actually off by default and -can be activated by passing the `-w +32` argument to the compiler : -``` -$ ocamlopt -w +32 hello_world_with_intf.ml -File "hello_world_with_intf.ml", line 3, characters 4-11: -3 | let goodbye = "Goodbye" - ^^^^^^^ -Warning 32 [unused-value-declaration]: unused value goodbye. -``` -The `goodbye` value can be safely removed and neither the compiler nor the -analyzer will report unused values anymore. Our work here is done. - -### Multiple compilation units - -This example is the same as the previous example, split in 2 separate -compilation units. All the exported values are now used externally. - -The reference files for this example are -[`hello_world_lib.mli`](../../examples/docs/exported_values/hello_world/hello_world_lib.mli), -[`hello_world_lib.ml`](../../examples/docs/exported_values/hello_world/hello_world_lib.ml), and -[`hello_world_bin.ml`](../../examples/docs/exported_values/hello_world/hello_world_bin.ml) - -The compilation command to produce the necessary `.cmi` and `.cmt` files is : -``` -ocamlopt -bin-annot hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml -``` - -The analysis command is : -``` -dead_code_analyzer --nothing -E all hello_world_lib.cmi hello_world_lib.cmt hello_world_bin.cmi hello_world_bin.cmt -``` - -> [!NOTE] -> It is left as an exercise to the reader to explore this example without -> `hello_world_lib.mli`. - -#### First run - -Code : -```OCaml -(* hello_world_lib.mli *) -val hello : string -val goodbye : string -val world : string -``` -```OCaml -(* hello_world_lib.ml *) -let hello = "Hello" -let goodbye = "Goodbye" -let world = "World" -``` -```OCaml -(* hello_world_bin.ml *) -let () = - let open Hello_world_lib in - let hello_world = hello ^ world in - let goodbye_world = goodbye ^ world in - print_endline hello_world -``` - -Compile and analyze : -``` -$ ocamlopt -bin-annot hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml -File "hello_world_bin.ml", line 5, characters 6-19: -5 | let goodbye_world = goodbye ^ world in - ^^^^^^^^^^^^^ -Warning 26 [unused-var]: unused variable goodbye_world. - -$ dead_code_analyzer --nothing -E all hello_world_lib.cmi hello_world_lib.cmt hello_world_bin.cmi hello_world_bin.cmt -Scanning files... - [DONE] - -.> UNUSED EXPORTED VALUES: -========================= - -Nothing else to report in this section --------------------------------------------------------------------------------- -``` - -The compiler reports the same warning 26 on `goodbye_world` as in the previous -example. - -The analyzer does not report any unused _exported_ value. The 3 exported -values are `hello`, `goodbye` and `world` in `hello_world_lib.mli`. They are all -referenced externally, in `hello_world_bin.ml`. - -> [!NOTE] -> All the different flavors of explicit reference are taken into account the -> same way. Here, the values are referenced after a local `open`. They could -> have been referenced after a global `open` or using their full paths (e.g. -> `Hello_world_lib.hello`) without making any difference on the reports. - -#### Fixing the warning 26 - -Let's remove the unused `goodbye_world`. - -Code : -```OCaml -(* hello_world_lib.mli *) -val hello : string -val goodbye : string -val world : string -``` -```OCaml -(* hello_world_lib.ml *) -let hello = "Hello" -let goodbye = "Goodbye" -let world = "World" -``` -```OCaml -(* hello_world_bin.ml *) -let () = - let open Hello_world_lib in - let hello_world = hello ^ world in - print_endline hello_world -``` - -Compile and analyze : -``` -$ ocamlopt -bin-annot hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml - -$ dead_code_analyzer --nothing -E all hello_world_lib.cmi hello_world_lib.cmt hello_world_bin.cmi hello_world_bin.cmt -Scanning files... - [DONE] - -.> UNUSED EXPORTED VALUES: -========================= -/tmp/docs/exported_values/hello_world/hello_world_lib.mli:3: goodbye - -Nothing else to report in this section --------------------------------------------------------------------------------- -``` - -The compiler does not report any unused value. - -The analyzer reports `goodbye` as unused, as in the previous example. - -#### Unexporting `goodbye` - -Code : -```OCaml -(* hello_world_lib.mli *) -val hello : string -val world : string -``` -```OCaml -(* hello_world_lib.ml *) -let hello = "Hello" -let goodbye = "Goodbye" -let world = "World" -``` -```OCaml -(* hello_world_bin.ml *) -let () = - let open Hello_world_lib in - let hello_world = hello ^ world in - print_endline hello_world -``` - -Compile and analyze : -``` -$ ocamlopt -bin-annot hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml - -$ dead_code_analyzer --nothing -E all hello_world_lib.cmi hello_world_lib.cmt hello_world_bin.cmi hello_world_bin.cmt -Scanning files... - [DONE] - -.> UNUSED EXPORTED VALUES: -========================= - -Nothing else to report in this section --------------------------------------------------------------------------------- -``` - -Now, neither the compiler nor the analyzer report any unused value. - -Like in the previous example, we need to pass `-w +32` to the compiler to -trigger the `unused-value-declaration` warning : -``` -$ ocamlopt -bin-annot hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml -File "hello_world_lib.ml", line 3, characters 4-11: -3 | let goodbye = "Goodbye" - ^^^^^^^ -Warning 32 [unused-value-declaration]: unused value goodbye. -``` -The `goodbye` value can be safely removed and neither the compiler nor the -analyzer will report unused values anymore. Our work here is done. - -### All Together - -This example is the grouping of all the previous [*Hello World*](#hello-world) -examples. Analyzing all the files at once reduces the number of iterations to -reach a satisfying codebase. - -The reference files for this example are all those listed previously. - -The compilation command to produce the necessary `.cmi` and `.cmt` files, -and the desired warnings is the combination of all the previous ones : -``` -ocamlopt -w +32 -bin-annot hello_world_without_intf.ml hello_world_with_intf.mli hello_world_with_intf.ml hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml -``` - -> [!NOTE] -> For our usage, this has the same effect has running each of the previous -> compliation commands, with the extra `-w +32` argument, one after the other. -> The benefit is that all the warnings will be printed at once. - -The analysis command is : -``` -dead_code_analyzer --nothing -E all . -``` - -> [!TIP] -> As we can see in the compilation command, there is a large number of files to -> list. Instead of listing all the `.cmi` and `.cmt` files in the command line, -> the analyzer accepts directories as arguments and will analyze all the -> relevant files it can find in them. - -The code is not re-exposed at each iteration here. It is the same as in the -previous examples. - -#### First Run - -Compile : -``` -$ ocamlopt -w +32 -bin-annot hello_world_without_intf.ml hello_world_with_intf.mli hello_world_with_intf.ml hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml -File "hello_world_without_intf.ml", line 8, characters 6-19: -8 | let goodbye_world = goodbye ^ world in - ^^^^^^^^^^^^^ -Warning 26 [unused-var]: unused variable goodbye_world. - -File "hello_world_with_intf.ml", line 8, characters 6-19: -8 | let goodbye_world = goodbye ^ world in - ^^^^^^^^^^^^^ -Warning 26 [unused-var]: unused variable goodbye_world. - -File "hello_world_bin.ml", line 5, characters 6-19: -5 | let goodbye_world = goodbye ^ world in - ^^^^^^^^^^^^^ -Warning 26 [unused-var]: unused variable goodbye_world. -``` - -Without any surprise, the compiler reports the warnings 26 explored -in the previous examples. -Let's fix them and **recompile**, before running the analyzer. - -Analyze : -``` -$ dead_code_analyzer --nothing -E all . -Scanning files... - [DONE] - -.> UNUSED EXPORTED VALUES: -========================= -/tmp/docs/exported_values/hello_world/hello_world_with_intf.mli:2: hello -/tmp/docs/exported_values/hello_world/hello_world_with_intf.mli:3: goodbye -/tmp/docs/exported_values/hello_world/hello_world_with_intf.mli:4: world -/tmp/docs/exported_values/hello_world/hello_world_lib.mli:3: goodbye -/tmp/docs/exported_values/hello_world/hello_world_without_intf.ml:3: goodbye - -Nothing else to report in this section --------------------------------------------------------------------------------- -``` - -The analyzer correctly reports the unused exported values it reported in -the previous examples, all in one run. Note that the reports are in the -lexicographical order. -Unlike the compiler which listed its warnings in the order of the files in -the command line, the analyzer always sorts its reports in this order. - -#### Removing the unused exported values - -Like we did, in the previous examples, we can remove the exported values at the -locations reported by the analyzer. - -Compile : -``` -$ ocamlopt -w +32 -bin-annot hello_world_without_intf.ml hello_world_with_intf.mli hello_world_with_intf.ml hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml -File "hello_world_with_intf.ml", line 3, characters 4-11: -3 | let goodbye = "Goodbye" - ^^^^^^^ -Warning 32 [unused-value-declaration]: unused value goodbye. - -File "hello_world_lib.ml", line 3, characters 4-11: -3 | let goodbye = "Goodbye" - ^^^^^^^ -Warning 32 [unused-value-declaration]: unused value goodbye. -``` - -Once again, the warnings are the same as for the previous examples. After -`goodbye_world` is removed and `goodbye` is unexported, the compiler warning 32 -indicates that `goodbye` is unused. -Let's fix the warnings. - -Compile and analyze : -``` -$ ocamlopt -w +32 -bin-annot hello_world_without_intf.ml hello_world_with_intf.mli hello_world_with_intf.ml hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml - -$ dead_code_analyzer --nothing -E all . -Scanning files... - [DONE] - -.> UNUSED EXPORTED VALUES: -========================= - -Nothing else to report in this section --------------------------------------------------------------------------------- -``` - -Now, neither the compiler nor the analyzer report any unused value. -Our work here is done. - - -## Code constructs - -All of the following examples can be found in the -[code\_constructs](../../examples/docs/exported_values/code_constructs) directory. - -The reference takes place in `/tmp/docs/exported_values/code_constructs`, which -is a copy of `code_constructs`. Reported locations may differ depending on the -location of the source files. - -### Function - -The reference files for this example are in the -[function](../../examples/docs/exported_values/code_constructs/function) directory. - -The compilation command is : -``` -make -C function build -``` - -The analysis command is : -``` -make -C function analyze -``` - -The compile + analyze command is : -``` -make -C function -``` - -#### First run - -Code: -```OCaml -(* function_lib.mli *) - -val memoize : f:('a -> 'b) -> 'a -> 'b - -val heavy_computation : 'a -> 'a - -val unused : 'a -> 'a - -val do_nothing : 'a -> unit -``` -```OCaml -(* function_lib.ml *) - -let memoize ~f = - let mem = Hashtbl.create 8 in - function x -> - match Hashtbl.find_opt mem x with - | Some y -> y - | None -> - let y = f x in - Hashtbl.add mem x y; - y - -let heavy_computation x = x - -let unused x = x - -let do_nothing x = () -``` -```OCaml -(* function_bin.ml *) - -let () = - let my_memoized = Function_lib.(memoize ~f:heavy_computation) in - Function_lib.do_nothing (); - assert (my_memoized 42 = my_memoized 42) -``` - -Function values are analyzed like any other value. Hence, passing them as -arguments to a function or applying them (even partially) count as uses just -like any other explicit reference. Therefore, `Function_lib`'s `memoize`, -`heavy_computation`, and `do_nothing` are used in `Function_bin`. This leaves -`Function_lib.unused` as the only unused exported value. - - -Compile and analyze : -``` -$ make -C function -make: Entering directory '/tmp/docs/exported_values/code_constructs/function' -ocamlopt -w +27+32 -bin-annot function_lib.mli function_lib.ml function_bin.ml -File "function_lib.ml", line 17, characters 15-16: -17 | let do_nothing x = () - ^ -Warning 27 [unused-var-strict]: unused variable x. -dead_code_analyzer --nothing -E all . -Scanning files... - [DONE] - -.> UNUSED EXPORTED VALUES: -========================= -/tmp/docs/exported_values/code_constructs/function/function_lib.mli:7: unused - -Nothing else to report in this section --------------------------------------------------------------------------------- - - -make: Leaving directory '/tmp/docs/exported_values/code_constructs/function' -``` - -The compiler reports that `do_nothing`'s parameter `x` is unused via a -warning 27. This can be easily fixed by prefixing the name with an underscore -`_` or even replacing the name by an underscore. Depending on the context, other -solutions may be considered, such as removing the parameter, or fixing the -argument's type to `unit`. - -As expected, the analyzer only reports `unused`, declared in `function_lib.mli`. - -#### Removing the unused values - -The warning 27 is fixed by updating `do_nothing`'s type to `unit -> unit` and -its parameter `x` to `()`. - -Code: -```OCaml -(* function_lib.mli *) - -val memoize : f:('a -> 'b) -> 'a -> 'b - -val heavy_computation : 'a -> 'a - -val do_nothing : unit -> unit -``` -```OCaml -(* function_lib.ml *) - -let memoize ~f = - let mem = Hashtbl.create 8 in - function x -> - match Hashtbl.find_opt mem x with - | Some y -> y - | None -> - let y = f x in - Hashtbl.add mem x y; - y - -let heavy_computation x = x - -let unused x = x - -let do_nothing () = () -``` -```OCaml -(* function_bin.ml *) - -let () = - let my_memoized = Function_lib.(memoize ~f:heavy_computation) in - Function_lib.do_nothing (); - assert (my_memoized 42 = my_memoized 42) -``` - -Compile and analyze : -``` -$ make -C function -make: Entering directory '/tmp/docs/exported_values/code_constructs/function' -ocamlopt -w +27+32 -bin-annot function_lib.mli function_lib.ml function_bin.ml -File "function_lib.ml", line 15, characters 4-10: -15 | let unused x = x - ^^^^^^ -Warning 32 [unused-value-declaration]: unused value unused. -dead_code_analyzer --nothing -E all . -Scanning files... - [DONE] - -.> UNUSED EXPORTED VALUES: -========================= - -Nothing else to report in this section --------------------------------------------------------------------------------- - - -make: Leaving directory '/tmp/docs/exported_values/code_constructs/function' -``` - -Now that `unused` is unexported, the compiler reports it as unused via the -warning 32, and the analyzer does not report anything. Removing that value -fixes all the warnings. Our work here is done. - - -### Module - -The reference files for this example are in the -[module](../../examples/docs/exported_values/code_constructs/module) directory. - -The compilation command is : -``` -make -C module build -``` - -The analysis command is : -``` -make -C module analyze -``` - -The compile + analyze command is : -``` -make -C module -``` - -#### First run - -Code: -```OCaml -(* module_lib.mli *) -module M : sig - type t - val externally_used : t - val internally_used : t - val unused : t -end -``` -```OCaml -(* module_lib.ml *) -module M = struct - type t = unit - let externally_used = () - let internally_used = () - let unused = () - let unused_unexported = () -end - -let () = M.internally_used -``` -```OCaml -(* module_bin.ml *) -let () = - ignore Module_lib.M.externally_used -``` - -Before looking at the analysis results, let's look at the code. - -All the values of `Module_lib.M` are exported except for -`unused_unexported`. Among the exported values, `unused` is not referenced -anywhere, `internally_used` is only referenced within its compilation unit -(`Module_lib`), and `externally_used` is only referenced outside of it. - -> [!IMPORTANT] -> Using `internally_used` inside of `M` rather than outside would provide the -> same results. The only scope of interest is the compilation unit. - -Compile and analyze : -``` -$ make -C module -make: Entering directory '/tmp/docs/exported_values/code_constructs/module' -ocamlopt -w +32 -bin-annot module_lib.mli module_lib.ml module_bin.ml -File "module_lib.ml", line 7, characters 6-23: -7 | let unused_unexported = () - ^^^^^^^^^^^^^^^^^ -Warning 32 [unused-value-declaration]: unused value unused_unexported. -dead_code_analyzer --nothing -E all . -Scanning files... - [DONE] - -.> UNUSED EXPORTED VALUES: -========================= -/tmp/docs/exported_values/code_constructs/module/module_lib.mli:5: M.internally_used -/tmp/docs/exported_values/code_constructs/module/module_lib.mli:6: M.unused - -Nothing else to report in this section --------------------------------------------------------------------------------- - - -make: Leaving directory '/tmp/docs/exported_values/code_constructs/module' -``` - -The compiler reports that `unused_unexported` is unused. - -The analyzer reports `M.internally_used` and `M.unused` as unused. Notice how -it did not only report the name of the value but its full path within its -compilation unit. - -> [!NOTE] -> `M.internally_used` is only used within its compilation unit. Because it is -> declared in a `.mli`, only external uses are accounted for. -> It is left as an exercise to the reader to explore this example without -> `module_lib.mli` - -#### Removing the unused values - -Code: -```OCaml -(* module_lib.mli *) -module M : sig - type t - val externally_used : t -end -``` -```OCaml -(* module_lib.ml *) -module M = struct - type t = unit - let externally_used = () - let internally_used = () - let unused = () -end - -let () = M.internally_used -``` -```OCaml -(* module_bin.ml *) -let () = - ignore Module_lib.M.externally_used -``` - -Compile and analyze : -``` -$ make -C module -make: Entering directory '/tmp/docs/exported_values/code_constructs/module' -ocamlopt -w +32 -bin-annot module_lib.mli module_lib.ml module_bin.ml -File "module_lib.ml", line 6, characters 6-12: -6 | let unused = () - ^^^^^^ -Warning 32 [unused-value-declaration]: unused value unused. -dead_code_analyzer --nothing -E all . -Scanning files... - [DONE] - -.> UNUSED EXPORTED VALUES: -========================= - -Nothing else to report in this section --------------------------------------------------------------------------------- - - -make: Leaving directory '/tmp/docs/exported_values/code_constructs/module' -``` - -The compiler reports `unused` as unused and the analyzer does not report -anything. Removing that value fixes all the warnings. Our work here is done. - -### Functor - -The reference files for this example are in the -[functor](../../examples/docs/exported_values/code_constructs/functor) directory. - -The compilation command is : -``` -make -C functor build -``` - -The analysis command is : -``` -make -C functor analyze -``` - -The compile + analyze command is : -``` -make -C functor -``` - -#### First run - -Code: -```OCaml -(* functor_lib.mli *) -type t - -module F (P : sig - val used_required : t - val unused_required : t -end) : sig - val externally_used : t - val internally_used : t - val unused : t -end - -module InternalParam : sig - val used_required : t - val unused_required : t -end - -module ExternalParam : sig - val used_required : t - val unused_required : t -end - -module InternalApp : sig - val externally_used : t - val internally_used : t - val unused : t -end -``` -```OCaml -(* functor_lib.ml *) -type t = unit - -module F (P : sig - val used_required : t - val unused_required : t -end) = struct - let externally_used = P.used_required - let internally_used = P.used_required - let unused = P.used_required - let unused_unexported = P.used_required - let () = internally_used -end - -module InternalParam = struct - let used_required = () - let unused_required = () -end - -module ExternalParam = struct - let used_required = () - let unused_required = () -end - -module InternalApp = F(InternalParam) -``` -```OCaml -(* functor_bin.ml *) -open Functor_lib - -module ExternalApp = F(ExternalParam) - -let () = - ignore InternalApp.externally_used; - ignore ExternalApp.externally_used -``` - -Before looking at the analysis results, let's look at the code. - -The `Functor_lib` compilation unit exports 1 functor and 3 modules : -- `F` takes a module containing the values `used_required` and `unused_required`, - and returns a module with 3 values whose names are explicit. -- `InternalParam` and `ExternalParam` fit the signature of `F`'s parameter `P`. - The first one is used for a functor application inside `Functor_lib`. - The second one is used for a functor application outside of it. -- `InternalApp` fits the signature of the result of `F`, and is impemented as - the result of applying `F` inside its compilation unit. - -The `Functor_bin` compilation unit exports 1 module : `ExternalApp`, which is -the result of applying `Functor_lib.F` outside its compilation unit. - -Among all the exported values, the only explicit references accounted for are -`InternalApp.externally_used` and `External_app.externally_used` in -`Functor_bin`. - -Additionally, some values are used by requirement. Because `InternalParam` and -`ExternalParam` are passed as arguments to `F`, their values `used_required` and -`unused_required` are used by requirement to fulfill the signature of `F`'s -parameter `P`. - -With those observations in mind, let's see what the compiler and the analyzer -report. - -Compile and analyze: -``` -$ make -C functor -make: Entering directory '/tmp/docs/exported_values/code_constructs/functor' -ocamlopt -w +32 -bin-annot functor_lib.mli functor_lib.ml functor_bin.ml -File "functor_lib.ml", line 6, characters 2-25: -6 | val unused_required : t - ^^^^^^^^^^^^^^^^^^^^^^^ -Warning 32 [unused-value-declaration]: unused value unused_required. - -File "functor_lib.ml", line 11, characters 6-23: -11 | let unused_unexported = P.used_required - ^^^^^^^^^^^^^^^^^ -Warning 32 [unused-value-declaration]: unused value unused_unexported. -dead_code_analyzer --nothing -E all . -Scanning files... - [DONE] - -.> UNUSED EXPORTED VALUES: -========================= -/tmp/docs/exported_values/code_constructs/functor/functor_lib.mli:9: F.internally_used -/tmp/docs/exported_values/code_constructs/functor/functor_lib.mli:10: F.unused -/tmp/docs/exported_values/code_constructs/functor/functor_lib.mli:14: InternalParam.used_required -/tmp/docs/exported_values/code_constructs/functor/functor_lib.mli:15: InternalParam.unused_required -/tmp/docs/exported_values/code_constructs/functor/functor_lib.mli:25: InternalApp.internally_used -/tmp/docs/exported_values/code_constructs/functor/functor_lib.mli:26: InternalApp.unused - -Nothing else to report in this section --------------------------------------------------------------------------------- - - -make: Leaving directory '/tmp/docs/exported_values/code_constructs/functor' -``` - -> The compiler reports 2 unused values, that can be removed at the reported locations : -> - `unused_required`, defined by `P`, the parameter of `F`; -> - `unexported_unused`, defined by `F`, like it did in the [module](#module) example. - -The analyzer reports 6 unused exported values, all in `functor_lib.mli` : -2 in `F`, 2 in `InternalParam`, and 2 in `InternalApp`. Let's observe them by -module (in reverse order) : - -- The reports for `InternalApp` are identical to the [module](#module) example. -Although, `InternalApp` is implemented as the result of applying `F` in -`functor_lib.ml`, its signature is independent of `F` in `functor_lib.mli`. -I.e. it exposes its own values and the link between them and those of -`F` is absent from the signature. Consequently, they are tracked independently. - -- As we observed before runnning the analyzer, the values in `InternalParam` are -used by requirement. However, this use is internal to `Functor_lib`, and there -is an interface available : `functor_lib.mli`. Consequently, the internal uses -are ignored, and `InternalParam`'s values become unused. - -- `F` is a functor but is tracked like a regular module. The reported values are -those of its result module. Reporting on those may feel like duplicates, but, -as explained for the reports of `InternalApp`, the values of the result -module are declared independently of those of `InternalApp`, hence they are -tracked and reported independently. - -All the values reported by the analyzer can be safely removed. - -Before moving on, there is another observation that we can make : -the values `unused` and `internally_used` of `ExternalApp` are not reported. -Because they are reported for `InternalApp` and `F`, one could -expect them to be reported for `ExternalApp` as well. In reality, they are not -tracked individually for `ExternalApp` because it does not expose them -explicitly. Unlike `InternalApp` which has an explicit module signature, -`ExternalApp` does not. Consequently, its values are directly linked to those of -`F`. This situation is explored in the -[module signature](#module-signature) example. - -> [!TIP] -> If we activated the compiler warning 67 `unused-functor-parameter` (by -> passing the argument `-w +67`), then the compiler would have reported : -> ``` -> File "functor_lib.mli", line 4, characters 10-11: -> 4 | module F (P : sig -> ^ -> Warning 67 [unused-functor-parameter]: unused functor parameter P. -> ``` -> This can be fixed by either replacing `P` with `_`, or by rewriting the -> declaration of `F` as : -> ```OCaml -> module F : sig -> val used_required : t -> val unused_required : t -> end -> -> sig -> val externally_used : t -> val internally_used : t -> val unused : t -> end -> ``` - -#### Removing the unused values - -In addition to removing everything that was reported by the compiler and the -analyzer, we also commented out `P.used_required` and `InternalParam` in -`functor_lib.mli`. It is up to the user to decide whether they would like to -keep them or remove them from their specifications. Neither would be reported -by the compiler or the analyzer. - -> [!NOTE] -> It is allowed to give a larger module as argument than what the parameter -> specifies. Similarly it is allowed to declare the parameter larger in the -> interface than it is in the implementation. Consequently, the compiler would -> not complain if `P` expected `unused_required` in the `.mli` but not in the -> `.ml`. - -Code: -```OCaml -(* functor_lib.mli *) -type t - -module F (P : sig - val used_required : t - (* val unused_required : t *) -end) : sig - val externally_used : t -end - -(* -module InternalParam : sig -end -*) - -module ExternalParam : sig - val used_required : t - val unused_required : t -end - -module InternalApp : sig - val externally_used : t -end -``` -```OCaml -(* functor_lib.ml *) -type t = unit - -module F (P : sig - val used_required : t -end) = struct - let externally_used = P.used_required - let internally_used = P.used_required - let unused = P.used_required - let () = internally_used -end - -module InternalParam = struct - let used_required = () - let unused_required = () -end - -module ExternalParam = struct - let used_required = () - let unused_required = () -end - -module InternalApp = F(InternalParam) -``` -```OCaml -(* functor_bin.ml *) -open Functor_lib - -module ExternalApp = F(ExternalParam) - -let () = - ignore InternalApp.externally_used; - ignore ExternalApp.externally_used -``` - -Compile and analyze : -``` -$ make -C functor -make: Entering directory '/tmp/docs/exported_values/code_constructs/functor' -ocamlopt -w +32 -bin-annot functor_lib.mli functor_lib.ml functor_bin.ml -File "functor_lib.ml", line 9, characters 6-12: -9 | let unused = P.used_required - ^^^^^^ -Warning 32 [unused-value-declaration]: unused value unused. - -File "functor_lib.ml", line 15, characters 6-21: -15 | let unused_required = () - ^^^^^^^^^^^^^^^ -Warning 32 [unused-value-declaration]: unused value unused_required. -dead_code_analyzer --nothing -E all . -Scanning files... - [DONE] - -.> UNUSED EXPORTED VALUES: -========================= -/tmp/docs/exported_values/code_constructs/functor/functor_lib.mli:18: ExternalParam.unused_required - -Nothing else to report in this section --------------------------------------------------------------------------------- - - -make: Leaving directory '/tmp/docs/exported_values/code_constructs/functor' -``` - -Now that `F.unused` and `InternalParam.unused_required` are not exported, they -are reported as unused by the compiler, and can be removed safely. - -`ExternalParam.unused_required` was used by requirement. Now that it is not -required by `P` (because it is commented out), it is unused and the analyzer -correctly reports it. It can be removed safely. Removing it will trigger the -same compiler warning as for `InternalParam.unused_required`, so it can be -removed from both the interface and the implementation. - -The unused values can be removed as explained. Our work here is done. - -### Module Type - -The reference files for this example are in the -[modtyp](../../examples/docs/exported_values/code_constructs/modtyp) directory. - -The compilation command is : -``` -make -C modtyp build -``` - -The analysis command is : -``` -make -C modtyp analyze -``` - -The compile + analyze command is : -``` -make -C modtyp -``` - -> [!IMPORTANT] -> **LIMITATION** -> -> In order to reduce noise (false positives and duplication) in the results, -> the analyzer currently ignores values exported by module types -> (see [issue #50](https://github.com/LexiFi/dead_code_analyzer/issues/50)). - -#### First run - -Code: -```OCaml -(* modtyp_lib.mli *) -module type T = sig - type t - val externally_used : t - val internally_used : t - val unused : t -end - -module M_reuse : T - -module M_constr : T with type t = unit - -module M_subst : T with type t := unit - -module M_redef : sig - type t - val externally_used : t - val internally_used : t - val unused : t -end -``` -```OCaml -(* modtyp_lib.ml *) -module type T = sig - type t - val externally_used : t - val internally_used : t - val unused : t -end - -module M = struct - type t = unit - let externally_used = () - let internally_used = () - let unused = () - let unused_unexported = () -end - -let () = M.internally_used - -module M_reuse = M - -module M_constr = M - -module M_subst = M - -module M_redef = M -``` -```OCaml -(* modtyp_bin.ml *) -let () = - ignore Modtyp_lib.M_reuse.externally_used; - ignore Modtyp_lib.M_constr.externally_used; - ignore Modtyp_lib.M_subst.externally_used; - ignore Modtyp_lib.M_redef.externally_used -``` - -Before looking at the analysis results, let's look at the code. - -The `Modtyp_lib` exports 1 module type `T` and 4 modules : -`M_reuse`, `M_constr`, `M_subst`, and `M_redef`. Of these 4 modules, the first -3 have `T` as signature (with minor twists), while the last one has its own -explicit signature, which is a copy of `T`. In this way, `M_redef` is equivalent -to `Module_lib.M` in the [module](#module) example : it exposes exactly -the same information. -Each of the modules exposed by `Modtyp_lib` are used exactly in the same way : -their `externally_used` values are explicitly referenced in `Modtyp_bin`. - -One could expect that all the exported values are reported except for the -`externally_used`. However, reporting e.g. `M_subst.internally_used` as unused -would not be immediately actionable. In reality, this value is explicitly -declared by `T`. -Fixing an unused value reported in a module using a module type as signature -would require either removing the value from the module type (if possible), -or explicilty describing the signature of the module, effectively losing the -benefits of using the module type. Thus, reporting unused values for the module -itself would be counterproductive. - -An actionable report would be of the value in the module type itself, -if it is unused by all the modules of that module type (as it is the case here -for `T.unused`). Currently, and as described in the introduction of this example, -the values exported by module types are ignored by the analyzer, and, -consequently, are not reported. - -Now that we have explained what the expected behavior of the analyzer should be, -let's look at its results on the code above. - -Compile and analyze : -``` -$ make -C modtyp -make: Entering directory '/tmp/docs/exported_values/code_constructs/modtyp' -ocamlopt -w +32 -bin-annot modtyp_lib.mli modtyp_lib.ml modtyp_bin.ml -File "modtyp_lib.ml", line 14, characters 6-23: -14 | let unused_unexported = () - ^^^^^^^^^^^^^^^^^ -Warning 32 [unused-value-declaration]: unused value unused_unexported. -dead_code_analyzer --nothing -E all . -Scanning files... - [DONE] - -.> UNUSED EXPORTED VALUES: -========================= -/tmp/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:18: M_redef.internally_used -/tmp/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:19: M_redef.unused - -Nothing else to report in this section --------------------------------------------------------------------------------- - - -make: Leaving directory '/tmp/docs/exported_values/code_constructs/modtyp' -``` - -As in the module example, the compiler reports that `unused_unexported` is unused. - -As in the module example, the analyzer reports `M_redef.internally_used` and -`M_redef.unused` as unused exported values. - -All the reports are similar to those of the [module](#module) example. -Its exploration and resolution can be applied. -Our work here is done. - -### Module Signature - -The reference files for this example are in the -[modsig](../../examples/docs/exported_values/code_constructs/modsig) directory. - -The compilation command is : -``` -make -C modsig build -``` - -The analysis command is : -``` -make -C modsig analyze -``` - -The compile + analyze command is : -``` -make -C modsig -``` - -#### First run - -Code: -```OCaml -(* modsig_lib.ml *) -module Original = struct - let used_directly = () - let used_indirectly = () - let used_by_requirement = () - let unused = () -end - -module Alias_without_sig = Original - -module Alias_with_sig : sig - val used_by_requirement : unit -end = Original -``` -```OCaml -(* modsig_bin.ml *) -let () = - let open Modsig_lib in - Original.used_directly; - Alias_without_sig.used_indirectly -``` - -Before looking at the analysis results, let's look at the code. - -The `Modsig_lib` compilation unit does not have a `.mli`, so all the internal -uses are accounted for. It exposes 3 modules : `Original`, `Alias_without_sig`, -and `Alias_with_sig`. Only the first module actually defines values. The second -one is an trivial alias, and the third is an alias with an explicit signature. -Because the 2 latter modules are aliases of the `Original` module, one could -expect that the values they expose are unified with the ones of `Original`. -This would imply that only the values in `Original` could be reported as unused. -This reasoning is partially true. - -As explained in the [module type](#module-type) example, reporting a value in -`Alias_without_sig` would not be trivially solved. Thus, -its values are unified with those of `Original`, and, consequently, they -cannot be reported by the analyzer. Only the values in `Original` can be -reported in this case. - -However, `Alias_with_sig` has an explicit signature, which means 2 things: -1. it controls what it exports, thus a reporting values in that module is - trivially actionable by removing the reported values from the signature -2. it has requirements, thus all the values in `Original` that are expected in - the signature of `Alias_with_sig` are considered used. - -Now that we have explained the nuances introduced by the existence of an -explicit module signature, let's look at the results. - -Compile and analyze: -``` -$ make -C modsig -make: Entering directory '/tmp/docs/exported_values/code_constructs/modsig' -ocamlopt -bin-annot modsig_lib.ml modsig_bin.ml -dead_code_analyzer --nothing -E all . -Scanning files... - [DONE] - -.> UNUSED EXPORTED VALUES: -========================= -/tmp/docs/exported_values/code_constructs/modsig/modsig_lib.ml:6: Original.unused -/tmp/docs/exported_values/code_constructs/modsig/modsig_lib.ml:12: Alias_with_sig.used_by_requirement - -Nothing else to report in this section --------------------------------------------------------------------------------- - - -make: Leaving directory '/tmp/docs/exported_values/code_constructs/modsig' -``` - -The compiler does not report any unused value. - -The analyzer reports that `Original.unused` and -`Alias_with_sig.used_by_requirement` are unused. As expected, it does not report -any unused value in `Alias_without_sig`. -Let's look more closely at the values and their uses. -- `Original.used_directly` is explicitly referenced in `Modsig_bin` -- `Original.used_indirectly` is used by an explicit reference to - `Alias_without_sig.used_indirectly` in `Modsig_bin` -- `Original.used_by_requirement` is used by requirement to fulfill - `Alias_with_sig`'s signature -- `Original.unused` is not referenced nor required anywhere -- `Alias_without_sig` does not "own" any value -- `Alias_with_sig.used_by_requirement` is not referenced nor required anywhere - -#### Removing the unused values - -The reported values can be removed : `Original.unused` is removed from the -module's strucutre because it does not have an explicit signature, and -`Alias_with_sig.used_by_requirement` is removed from the module's signature. - -Code: -```OCaml -(* modsig_lib.ml *) -module Original = struct - let used_directly = () - let used_indirectly = () - let used_by_requirement = () -end - -module Alias_without_sig = Original - -module Alias_with_sig : sig end = Original -``` -```OCaml -(* modsig_bin.ml *) -let () = - let open Modsig_lib in - Original.used_directly; - Alias_without_sig.used_indirectly -``` - -Compile and analyze: -``` -$ make -C modsig -make: Entering directory '/tmp/docs/exported_values/code_constructs/modsig' -ocamlopt -bin-annot modsig_lib.ml modsig_bin.ml -dead_code_analyzer --nothing -E all . -Scanning files... - [DONE] - -.> UNUSED EXPORTED VALUES: -========================= -/tmp/docs/exported_values/code_constructs/modsig/modsig_lib.ml:5: Original.used_by_requirement - -Nothing else to report in this section --------------------------------------------------------------------------------- - - -make: Leaving directory '/tmp/docs/exported_values/code_constructs/modsig' -``` - -The compiler does not report any unused value. - -The analyzer reports `Original.used_by_requirement` as unused. Indeed, -by removing `used_by_requirement` from the signature of `Alias_with_sig` we -removed the requirement for `Original` to provide it. This value can be removed -from `Orignal`, and neither the compiler nor the analyzer will report unused -values anymore. Our work here is done. - -### Include - -The reference files for this example are in the -[include](../../examples/docs/exported_values/code_constructs/include) directory. - -The compilation command is : -``` -make -C include build -``` - -The analysis command is : -``` -make -C include analyze -``` - -The compile + analyze command is : -``` -make -C include -``` - -#### First run - -Code: -```OCaml -(* include_lib.ml *) -module Original = struct - let used_directly = () - let used_indirectly = () - let unused = () -end - -module Reexport = struct - include Original -end - -module Redefine = struct - include Original - let used_directly = () - let unused = () -end -``` -```OCaml -(* include_bin.ml *) -let () = - let open Include_lib in - ignore Original.used_directly; - ignore Reexport.used_indirectly; - ignore Redefine.used_directly; -``` - -Before looking at the analysis results, let's look at the code. - -The `Include_lib` compilation unit does not have a `.mli`, so all the internal -uses are accounted for. It exposes 3 modules : -- `Original`, which explicitly defines all its values; -- `Reexport`, which only includes `Original`; -- `Redefine`, which includes `Original` and redefines 2 values : - `used_directly`, and `unused`. - -By the explanation in the [module signature](#module-signature) and -[module type](#module-type) examples, although there are 9 exported values -(`used_directly`, `used_indirectly`, and `unused` for each module), only 5 are -expected to be tracked by the analyzer : those in `Original` and the 2 redefined -in `Redefine`. These are the only values a developer can trivially remove if -they are reported unused. - -Thus, the only values used are `Original.used_directly`, -`Original.used_indirectly` (by an explicit reference to -`Reexport.used_indirectly`), and `Redefine.used_directly`. This means that the -unused exported values tracked by the analyzer are `Original.unused` and -`Redefine.unused`. - -Compile and analyze: -``` -$ make -C include -make: Entering directory '/tmp/docs/exported_values/code_constructs/include' -ocamlopt -bin-annot include_lib.ml include_bin.ml -dead_code_analyzer --nothing -E all . -Scanning files... - [DONE] - -.> UNUSED EXPORTED VALUES: -========================= -/tmp/docs/exported_values/code_constructs/include/include_lib.ml:5: Original.unused -/tmp/docs/exported_values/code_constructs/include/include_lib.ml:5: Reexport.unused -/tmp/docs/exported_values/code_constructs/include/include_lib.ml:15: Redefine.unused - -Nothing else to report in this section --------------------------------------------------------------------------------- - - -make: Leaving directory '/tmp/docs/exported_values/code_constructs/include' -``` - -The compiler does not report any unused value. - -As expected, the analyzer reports `Original.unused` and `Redefine.unused`. -However, it also reports `Reexport.unused`, which is unexpected. - -> [!WARNING] -> The extra report on `Reexport.unused` is a known bug, tracked by -> [issue #57](https://github.com/LexiFi/dead_code_analyzer/issues/57). -> This duplicated report only exists because the modules `Original` and -> `Reexport` belong to the same compilation unit (`Include_lib`). This can -> easily be verified by moving `Reexport` in `Include_bin` instead. - -Because the report of `Reexport.unused` is actually a duplicate of the report -of `Original.unused`, we can simply ignore it. - -#### Removing the unused values - -The reported values can be removed from the implementation. - -Code: -```OCaml -(* include_lib.ml *) -module Original = struct - let used_directly = () - let used_indirectly = () -end - -module Reexport = struct - include Original -end - -module Redefine = struct - include Original - let used_directly = () -end -``` -```OCaml -(* include_bin.ml *) -let () = - let open Include_lib in - ignore Original.used_directly; - ignore Reexport.used_indirectly; - ignore Redefine.used_directly; -``` - -Compile and analyze: -``` -$ make -C include -make: Entering directory '/tmp/docs/exported_values/code_constructs/include' -ocamlopt -bin-annot include_lib.ml include_bin.ml -dead_code_analyzer --nothing -E all . -Scanning files... - [DONE] - -.> UNUSED EXPORTED VALUES: -========================= - -Nothing else to report in this section --------------------------------------------------------------------------------- - - -make: Leaving directory '/tmp/docs/exported_values/code_constructs/include' -``` - -Now, neither the compiler nor the analyzer report any unused value. -Our work here is done. +- [Hello world](./HELLO_WORLD.md) is a collection of small examples relying on the + same code, but organized differently (with/without `.mli`, single/multiple + compilation units). This aims at providing a first walk-through of the + analyzer's usage. + +- The [code constructs](./code_constructs) directory contains a collection of + examples dedicated to specific code constructs : + - [Function](./code_constructs/FUNCTION.md) + - [Module](./code_constructs/MODULE.md) + - [Functor](./code_constructs/FUNCTOR.md) + - [Module type](./code_constructs/MODTYP.md) + - [Module signature](./code_constructs/MODSIG.md) + - [Include](./code_constructs/INCLUDE.md) diff --git a/docs/exported_values/HELLO_WORLD.md b/docs/exported_values/HELLO_WORLD.md new file mode 100644 index 00000000..22935a56 --- /dev/null +++ b/docs/exported_values/HELLO_WORLD.md @@ -0,0 +1,575 @@ +# Table of contents + ++ [Single compilation unit without interface](#single-compilation-unit-without-interface) ++ [Single compilation unit with interface](#single-compilation-unit-with-interface) ++ [Multiple compilation units](#multiple-compilation-units) ++ [All Together](#all-together) + +All of the following examples can be found in the [hello\_world](../../examples/docs/exported_values/hello_world) +directory. + +The reference takes place in `/tmp/docs/exported_values/hello_world`, which +contains copies of the examples. Reported locations may differ depending on the +location of the source files. + +# Single compilation unit without interface + +This example illustrates a simple case of a compilation unit without `.mli` and +without any external use. + +The reference file for this example is +[`hello_world_without_intf.ml`](../../examples/docs/exported_values/hello_world/hello_world_without_intf.ml). + +The compilation command to produce `hello_world_without_intf.cmi` and +`hello_world_without_intf.cmt` is : +``` +ocamlopt -bin-annot hello_world_without_intf.ml +``` + +The analysis command is : +``` +dead_code_analyzer --nothing -E all hello_world_without_intf.cmi hello_world_without_intf.cmt +``` + +## First run + +Code : +```OCaml +(* hello_world_without_intf.ml *) +let hello = "Hello" +let goodbye = "Goodbye" +let world = "World" + +let () = + let hello_world = hello ^ world in + let goodbye_world = goodbye ^ world in + print_endline hello_world +``` + +Compile : +``` +$ ocamlopt -bin-annot hello_world_without_intf.ml +File "hello_world_without_intf.ml", line 8, characters 6-19: +8 | let goodbye_world = goodbye ^ world in + ^^^^^^^^^^^^^ +Warning 26 [unused-var]: unused variable goodbye_world. +``` + +The compiler reports a warning 26 on `goodbye_world`: +`Warning 26 [unused-var]: unused variable goodbye_world.` +This tells us that the _local_ value is unused, and, thus, can be removed at the +reported location: `File "hello_world_without_intf.ml", line 8` + +Analyze : +``` +$ dead_code_analyzer --nothing -E all hello_world_without_intf.cmi hello_world_without_intf.cmt +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= + +Nothing else to report in this section +-------------------------------------------------------------------------------- +``` + +The analyzer does not report any unused _exported_ value. There are 3 exported +in the `Hello_world_without_intf` compilation unit : `hello`, `goodbye` and `world`. +These are the top level values of `hello_world_without_intf.ml`. +They are all referenced internally. Because there is no `hello_world_without_intf.mli`, +the internal uses are accounted for. Consequently, none of the exported values +are considered unused by the analyzer. + +## Fixing the warning 26 + +Let's remove the unused `goodbye_world`. + +Code : +```OCaml +(* hello_world_without_intf.ml *) +let hello = "Hello" +let goodbye = "Goodbye" +let world = "World" + +let () = + let hello_world = hello ^ world in + print_endline hello_world +``` + +Compile and analyze : +``` +$ ocamlopt -bin-annot hello_world_without_intf.ml + +$ dead_code_analyzer --nothing -E all hello_world_without_intf.cmi hello_world_without_intf.cmt +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= +/tmp/docs/exported_values/hello_world/hello_world_without_intf.ml:3: goodbye + +Nothing else to report in this section +-------------------------------------------------------------------------------- +``` + +The compiler does not report any unused value. + +The analyzer reports that `goodbye` declared at line 3 is unused : +`/tmp/docs/exported_values/hello_world/hello_world_without_intf.ml:3: goodbye` +Like the warning 26 above, this report tells us that `goodbye` can be removed +at the reported location. + +## Removing the unused `goodbye` + +Code : +```OCaml +(* hello_world_without_intf.ml *) +let hello = "Hello" +let world = "World" + +let () = + let hello_world = hello ^ world in + print_endline hello_world +``` + +Compile and analyze : +``` +$ ocamlopt -bin-annot hello_world_without_intf.ml + +$ dead_code_analyzer --nothing -E all hello_world_without_intf.cmi hello_world_without_intf.cmt +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= + +Nothing else to report in this section +-------------------------------------------------------------------------------- +``` + +Now, neither the compiler nor the analyzer report any unused value. +Our work here is done. + +# Single compilation unit with interface + +This example is the same as the previous example, with an explicit `.mli`. +Although an interface is provided, all the uses remain inside the same +compilation unit. + +The reference files for this example are +[`hello_world_with_intf.mli`](../../examples/docs/exported_values/hello_world/hello_world_with_intf.mli) and +[`hello_world_with_intf.ml`](../../examples/docs/exported_values/hello_world/hello_world_with_intf.ml) + +The compilation command to produce `hello_world.cmi` and `hello_world.cmt` is : +``` +ocamlopt -bin-annot hello_world_with_intf.mli hello_world_with_intf.ml +``` + +The analysis command is : +``` +dead_code_analyzer --nothing -E all hello_world.cmi hello_world.cmt +``` + +## First run + +Code : +```OCaml +(* hello_world_with_intf.mli *) +val hello : string +val goodbye : string +val world : string +``` +```OCaml +(* hello_world_with_intf.ml *) +let hello = "Hello" +let goodbye = "Goodbye" +let world = "World" + +let () = + let hello_world = hello ^ world in + let goodbye_world = goodbye ^ world in + print_endline hello_world +``` + +Compile and analyze : +``` +$ ocamlopt -bin-annot hello_world_with_intf.ml +File "hello_world_with_intf.ml", line 8, characters 6-19: +8 | let goodbye_world = goodbye ^ world in + ^^^^^^^^^^^^^ +Warning 26 [unused-var]: unused variable goodbye_world. + +$ dead_code_analyzer --nothing -E all hello_world.cmi hello_world.cmt +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= +/tmp/docs/exported_values/hello_world/hello_world_with_intf.mli:2: hello +/tmp/docs/exported_values/hello_world/hello_world_with_intf.mli:3: goodbye +/tmp/docs/exported_values/hello_world/hello_world_with_intf.mli:4: world + +Nothing else to report in this section +-------------------------------------------------------------------------------- +``` + +The compiler reports the same warning 26 on `goodbye_world` as in the previous +example. + +The analyzer reports that there are 3 unused _exported_ value in +`hello_world_with_intf.mli`: `hello` at line 2, `goodbye` line 3, and `world` line 4. +These are the only exported values in the `Hello_world_with_intf` compilation +unit because they are the only ones listed in the `.mli`. They are all used in +`hello_world_with_intf.ml`, but not outside of their compilation unit. Because there is an +interface file available, only external uses are accounted for. Thus, they are +considered unused and can be dropped from the `.mli` + +## Removing the unused values + +Let's remove the unused `goodbye_world` from `hello_world_with_intf.ml`, reported unused +by the compiler, and the values in `hello_world_with_intf.mli` reported by the analyzer. + +Code : +```OCaml +(* hello_world_with_intf.mli *) +``` +```OCaml +(* hello_world_with_intf.ml *) +let hello = "Hello" +let goodbye = "Goodbye" +let world = "World" + +let () = + let hello_world = hello ^ world in + print_endline hello_world +``` +Compile and analyze : +``` +$ ocamlopt -bin-annot hello_world_with_intf.ml +$ dead_code_analyzer --nothing -E all hello_world.cmi hello_world.cmt +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= + +Nothing else to report in this section +-------------------------------------------------------------------------------- +``` + +Now, neither the compiler nor the analyzer report any unused value. + +We learned from the previous example that the `goodbye` value is unused after +we remove `goodbye_world`. This is on the compiler to report it in this example +because it is an _unexported_ value here (while it was an _exported_ value in +the previous example). The corresponding warning is actually off by default and +can be activated by passing the `-w +32` argument to the compiler : +``` +$ ocamlopt -w +32 hello_world_with_intf.ml +File "hello_world_with_intf.ml", line 3, characters 4-11: +3 | let goodbye = "Goodbye" + ^^^^^^^ +Warning 32 [unused-value-declaration]: unused value goodbye. +``` +The `goodbye` value can be safely removed and neither the compiler nor the +analyzer will report unused values anymore. Our work here is done. + +# Multiple compilation units + +This example is the same as the previous example, split in 2 separate +compilation units. All the exported values are now used externally. + +The reference files for this example are +[`hello_world_lib.mli`](../../examples/docs/exported_values/hello_world/hello_world_lib.mli), +[`hello_world_lib.ml`](../../examples/docs/exported_values/hello_world/hello_world_lib.ml), and +[`hello_world_bin.ml`](../../examples/docs/exported_values/hello_world/hello_world_bin.ml) + +The compilation command to produce the necessary `.cmi` and `.cmt` files is : +``` +ocamlopt -bin-annot hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml +``` + +The analysis command is : +``` +dead_code_analyzer --nothing -E all hello_world_lib.cmi hello_world_lib.cmt hello_world_bin.cmi hello_world_bin.cmt +``` + +> [!NOTE] +> It is left as an exercise to the reader to explore this example without +> `hello_world_lib.mli`. + +## First run + +Code : +```OCaml +(* hello_world_lib.mli *) +val hello : string +val goodbye : string +val world : string +``` +```OCaml +(* hello_world_lib.ml *) +let hello = "Hello" +let goodbye = "Goodbye" +let world = "World" +``` +```OCaml +(* hello_world_bin.ml *) +let () = + let open Hello_world_lib in + let hello_world = hello ^ world in + let goodbye_world = goodbye ^ world in + print_endline hello_world +``` + +Compile and analyze : +``` +$ ocamlopt -bin-annot hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml +File "hello_world_bin.ml", line 5, characters 6-19: +5 | let goodbye_world = goodbye ^ world in + ^^^^^^^^^^^^^ +Warning 26 [unused-var]: unused variable goodbye_world. + +$ dead_code_analyzer --nothing -E all hello_world_lib.cmi hello_world_lib.cmt hello_world_bin.cmi hello_world_bin.cmt +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= + +Nothing else to report in this section +-------------------------------------------------------------------------------- +``` + +The compiler reports the same warning 26 on `goodbye_world` as in the previous +example. + +The analyzer does not report any unused _exported_ value. The 3 exported +values are `hello`, `goodbye` and `world` in `hello_world_lib.mli`. They are all +referenced externally, in `hello_world_bin.ml`. + +> [!NOTE] +> All the different flavors of explicit reference are taken into account the +> same way. Here, the values are referenced after a local `open`. They could +> have been referenced after a global `open` or using their full paths (e.g. +> `Hello_world_lib.hello`) without making any difference on the reports. + +## Fixing the warning 26 + +Let's remove the unused `goodbye_world`. + +Code : +```OCaml +(* hello_world_lib.mli *) +val hello : string +val goodbye : string +val world : string +``` +```OCaml +(* hello_world_lib.ml *) +let hello = "Hello" +let goodbye = "Goodbye" +let world = "World" +``` +```OCaml +(* hello_world_bin.ml *) +let () = + let open Hello_world_lib in + let hello_world = hello ^ world in + print_endline hello_world +``` + +Compile and analyze : +``` +$ ocamlopt -bin-annot hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml + +$ dead_code_analyzer --nothing -E all hello_world_lib.cmi hello_world_lib.cmt hello_world_bin.cmi hello_world_bin.cmt +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= +/tmp/docs/exported_values/hello_world/hello_world_lib.mli:3: goodbye + +Nothing else to report in this section +-------------------------------------------------------------------------------- +``` + +The compiler does not report any unused value. + +The analyzer reports `goodbye` as unused, as in the previous example. + +## Unexporting `goodbye` + +Code : +```OCaml +(* hello_world_lib.mli *) +val hello : string +val world : string +``` +```OCaml +(* hello_world_lib.ml *) +let hello = "Hello" +let goodbye = "Goodbye" +let world = "World" +``` +```OCaml +(* hello_world_bin.ml *) +let () = + let open Hello_world_lib in + let hello_world = hello ^ world in + print_endline hello_world +``` + +Compile and analyze : +``` +$ ocamlopt -bin-annot hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml + +$ dead_code_analyzer --nothing -E all hello_world_lib.cmi hello_world_lib.cmt hello_world_bin.cmi hello_world_bin.cmt +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= + +Nothing else to report in this section +-------------------------------------------------------------------------------- +``` + +Now, neither the compiler nor the analyzer report any unused value. + +Like in the previous example, we need to pass `-w +32` to the compiler to +trigger the `unused-value-declaration` warning : +``` +$ ocamlopt -bin-annot hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml +File "hello_world_lib.ml", line 3, characters 4-11: +3 | let goodbye = "Goodbye" + ^^^^^^^ +Warning 32 [unused-value-declaration]: unused value goodbye. +``` +The `goodbye` value can be safely removed and neither the compiler nor the +analyzer will report unused values anymore. Our work here is done. + +# All Together + +This example is the grouping of all the previous [*Hello World*](#hello-world) +examples. Analyzing all the files at once reduces the number of iterations to +reach a satisfying codebase. + +The reference files for this example are all those listed previously. + +The compilation command to produce the necessary `.cmi` and `.cmt` files, +and the desired warnings is the combination of all the previous ones : +``` +ocamlopt -w +32 -bin-annot hello_world_without_intf.ml hello_world_with_intf.mli hello_world_with_intf.ml hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml +``` + +> [!NOTE] +> For our usage, this has the same effect has running each of the previous +> compliation commands, with the extra `-w +32` argument, one after the other. +> The benefit is that all the warnings will be printed at once. + +The analysis command is : +``` +dead_code_analyzer --nothing -E all . +``` + +> [!TIP] +> As we can see in the compilation command, there is a large number of files to +> list. Instead of listing all the `.cmi` and `.cmt` files in the command line, +> the analyzer accepts directories as arguments and will analyze all the +> relevant files it can find in them. + +The code is not re-exposed at each iteration here. It is the same as in the +previous examples. + +## First Run + +Compile : +``` +$ ocamlopt -w +32 -bin-annot hello_world_without_intf.ml hello_world_with_intf.mli hello_world_with_intf.ml hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml +File "hello_world_without_intf.ml", line 8, characters 6-19: +8 | let goodbye_world = goodbye ^ world in + ^^^^^^^^^^^^^ +Warning 26 [unused-var]: unused variable goodbye_world. + +File "hello_world_with_intf.ml", line 8, characters 6-19: +8 | let goodbye_world = goodbye ^ world in + ^^^^^^^^^^^^^ +Warning 26 [unused-var]: unused variable goodbye_world. + +File "hello_world_bin.ml", line 5, characters 6-19: +5 | let goodbye_world = goodbye ^ world in + ^^^^^^^^^^^^^ +Warning 26 [unused-var]: unused variable goodbye_world. +``` + +Without any surprise, the compiler reports the warnings 26 explored +in the previous examples. +Let's fix them and **recompile**, before running the analyzer. + +Analyze : +``` +$ dead_code_analyzer --nothing -E all . +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= +/tmp/docs/exported_values/hello_world/hello_world_with_intf.mli:2: hello +/tmp/docs/exported_values/hello_world/hello_world_with_intf.mli:3: goodbye +/tmp/docs/exported_values/hello_world/hello_world_with_intf.mli:4: world +/tmp/docs/exported_values/hello_world/hello_world_lib.mli:3: goodbye +/tmp/docs/exported_values/hello_world/hello_world_without_intf.ml:3: goodbye + +Nothing else to report in this section +-------------------------------------------------------------------------------- +``` + +The analyzer correctly reports the unused exported values it reported in +the previous examples, all in one run. Note that the reports are in the +lexicographical order. +Unlike the compiler which listed its warnings in the order of the files in +the command line, the analyzer always sorts its reports in this order. + +## Removing the unused exported values + +Like we did, in the previous examples, we can remove the exported values at the +locations reported by the analyzer. + +Compile : +``` +$ ocamlopt -w +32 -bin-annot hello_world_without_intf.ml hello_world_with_intf.mli hello_world_with_intf.ml hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml +File "hello_world_with_intf.ml", line 3, characters 4-11: +3 | let goodbye = "Goodbye" + ^^^^^^^ +Warning 32 [unused-value-declaration]: unused value goodbye. + +File "hello_world_lib.ml", line 3, characters 4-11: +3 | let goodbye = "Goodbye" + ^^^^^^^ +Warning 32 [unused-value-declaration]: unused value goodbye. +``` + +Once again, the warnings are the same as for the previous examples. After +`goodbye_world` is removed and `goodbye` is unexported, the compiler warning 32 +indicates that `goodbye` is unused. +Let's fix the warnings. + +Compile and analyze : +``` +$ ocamlopt -w +32 -bin-annot hello_world_without_intf.ml hello_world_with_intf.mli hello_world_with_intf.ml hello_world_lib.mli hello_world_lib.ml hello_world_bin.ml + +$ dead_code_analyzer --nothing -E all . +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= + +Nothing else to report in this section +-------------------------------------------------------------------------------- +``` + +Now, neither the compiler nor the analyzer report any unused value. +Our work here is done. diff --git a/docs/exported_values/code_constructs/FUNCTION.md b/docs/exported_values/code_constructs/FUNCTION.md new file mode 100644 index 00000000..bc2ee6db --- /dev/null +++ b/docs/exported_values/code_constructs/FUNCTION.md @@ -0,0 +1,173 @@ +The reference files for this example are in the +[function](../../../examples/docs/exported_values/code_constructs/function) directory. + +The reference takes place in `/tmp/docs/exported_values/code_constructs`, which +is a copy of the [code\_constructs](../../../examples/docs/exported_values/code_constructs) +directory. Reported locations may differ depending on the location of the source +files. + +The compilation command is : +``` +make -C function build +``` + +The analysis command is : +``` +make -C function analyze +``` + +The compile + analyze command is : +``` +make -C function +``` + +## First run + +Code: +```OCaml +(* function_lib.mli *) + +val memoize : f:('a -> 'b) -> 'a -> 'b + +val heavy_computation : 'a -> 'a + +val unused : 'a -> 'a + +val do_nothing : 'a -> unit +``` +```OCaml +(* function_lib.ml *) + +let memoize ~f = + let mem = Hashtbl.create 8 in + function x -> + match Hashtbl.find_opt mem x with + | Some y -> y + | None -> + let y = f x in + Hashtbl.add mem x y; + y + +let heavy_computation x = x + +let unused x = x + +let do_nothing x = () +``` +```OCaml +(* function_bin.ml *) + +let () = + let my_memoized = Function_lib.(memoize ~f:heavy_computation) in + Function_lib.do_nothing (); + assert (my_memoized 42 = my_memoized 42) +``` + +Function values are analyzed like any other value. Hence, passing them as +arguments to a function or applying them (even partially) count as uses just +like any other explicit reference. Therefore, `Function_lib`'s `memoize`, +`heavy_computation`, and `do_nothing` are used in `Function_bin`. This leaves +`Function_lib.unused` as the only unused exported value. + + +Compile and analyze : +``` +$ make -C function +make: Entering directory '/tmp/docs/exported_values/code_constructs/function' +ocamlopt -w +27+32 -bin-annot function_lib.mli function_lib.ml function_bin.ml +File "function_lib.ml", line 17, characters 15-16: +17 | let do_nothing x = () + ^ +Warning 27 [unused-var-strict]: unused variable x. +dead_code_analyzer --nothing -E all . +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= +/tmp/docs/exported_values/code_constructs/function/function_lib.mli:7: unused + +Nothing else to report in this section +-------------------------------------------------------------------------------- + + +make: Leaving directory '/tmp/docs/exported_values/code_constructs/function' +``` + +The compiler reports that `do_nothing`'s parameter `x` is unused via a +warning 27. This can be easily fixed by prefixing the name with an underscore +`_` or even replacing the name by an underscore. Depending on the context, other +solutions may be considered, such as removing the parameter, or fixing the +argument's type to `unit`. + +As expected, the analyzer only reports `unused`, declared in `function_lib.mli`. + +## Removing the unused values + +The warning 27 is fixed by updating `do_nothing`'s type to `unit -> unit` and +its parameter `x` to `()`. + +Code: +```OCaml +(* function_lib.mli *) + +val memoize : f:('a -> 'b) -> 'a -> 'b + +val heavy_computation : 'a -> 'a + +val do_nothing : unit -> unit +``` +```OCaml +(* function_lib.ml *) + +let memoize ~f = + let mem = Hashtbl.create 8 in + function x -> + match Hashtbl.find_opt mem x with + | Some y -> y + | None -> + let y = f x in + Hashtbl.add mem x y; + y + +let heavy_computation x = x + +let unused x = x + +let do_nothing () = () +``` +```OCaml +(* function_bin.ml *) + +let () = + let my_memoized = Function_lib.(memoize ~f:heavy_computation) in + Function_lib.do_nothing (); + assert (my_memoized 42 = my_memoized 42) +``` + +Compile and analyze : +``` +$ make -C function +make: Entering directory '/tmp/docs/exported_values/code_constructs/function' +ocamlopt -w +27+32 -bin-annot function_lib.mli function_lib.ml function_bin.ml +File "function_lib.ml", line 15, characters 4-10: +15 | let unused x = x + ^^^^^^ +Warning 32 [unused-value-declaration]: unused value unused. +dead_code_analyzer --nothing -E all . +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= + +Nothing else to report in this section +-------------------------------------------------------------------------------- + + +make: Leaving directory '/tmp/docs/exported_values/code_constructs/function' +``` + +Now that `unused` is unexported, the compiler reports it as unused via the +warning 32, and the analyzer does not report anything. Removing that value +fixes all the warnings. Our work here is done. diff --git a/docs/exported_values/code_constructs/FUNCTOR.md b/docs/exported_values/code_constructs/FUNCTOR.md new file mode 100644 index 00000000..2bde4d88 --- /dev/null +++ b/docs/exported_values/code_constructs/FUNCTOR.md @@ -0,0 +1,329 @@ +The reference files for this example are in the +[functor](../../../examples/docs/exported_values/code_constructs/functor) directory. + +The reference takes place in `/tmp/docs/exported_values/code_constructs`, which +is a copy of the [code\_constructs](../../../examples/docs/exported_values/code_constructs) +directory. Reported locations may differ depending on the location of the source +files. + +The compilation command is : +``` +make -C functor build +``` + +The analysis command is : +``` +make -C functor analyze +``` + +The compile + analyze command is : +``` +make -C functor +``` + +## First run + +Code: +```OCaml +(* functor_lib.mli *) +type t + +module F (P : sig + val used_required : t + val unused_required : t +end) : sig + val externally_used : t + val internally_used : t + val unused : t +end + +module InternalParam : sig + val used_required : t + val unused_required : t +end + +module ExternalParam : sig + val used_required : t + val unused_required : t +end + +module InternalApp : sig + val externally_used : t + val internally_used : t + val unused : t +end +``` +```OCaml +(* functor_lib.ml *) +type t = unit + +module F (P : sig + val used_required : t + val unused_required : t +end) = struct + let externally_used = P.used_required + let internally_used = P.used_required + let unused = P.used_required + let unused_unexported = P.used_required + let () = internally_used +end + +module InternalParam = struct + let used_required = () + let unused_required = () +end + +module ExternalParam = struct + let used_required = () + let unused_required = () +end + +module InternalApp = F(InternalParam) +``` +```OCaml +(* functor_bin.ml *) +open Functor_lib + +module ExternalApp = F(ExternalParam) + +let () = + ignore InternalApp.externally_used; + ignore ExternalApp.externally_used +``` + +Before looking at the analysis results, let's look at the code. + +The `Functor_lib` compilation unit exports 1 functor and 3 modules : +- `F` takes a module containing the values `used_required` and `unused_required`, + and returns a module with 3 values whose names are explicit. +- `InternalParam` and `ExternalParam` fit the signature of `F`'s parameter `P`. + The first one is used for a functor application inside `Functor_lib`. + The second one is used for a functor application outside of it. +- `InternalApp` fits the signature of the result of `F`, and is impemented as + the result of applying `F` inside its compilation unit. + +The `Functor_bin` compilation unit exports 1 module : `ExternalApp`, which is +the result of applying `Functor_lib.F` outside its compilation unit. + +Among all the exported values, the only explicit references accounted for are +`InternalApp.externally_used` and `External_app.externally_used` in +`Functor_bin`. + +Additionally, some values are used by requirement. Because `InternalParam` and +`ExternalParam` are passed as arguments to `F`, their values `used_required` and +`unused_required` are used by requirement to fulfill the signature of `F`'s +parameter `P`. + +With those observations in mind, let's see what the compiler and the analyzer +report. + +Compile and analyze: +``` +$ make -C functor +make: Entering directory '/tmp/docs/exported_values/code_constructs/functor' +ocamlopt -w +32 -bin-annot functor_lib.mli functor_lib.ml functor_bin.ml +File "functor_lib.ml", line 6, characters 2-25: +6 | val unused_required : t + ^^^^^^^^^^^^^^^^^^^^^^^ +Warning 32 [unused-value-declaration]: unused value unused_required. + +File "functor_lib.ml", line 11, characters 6-23: +11 | let unused_unexported = P.used_required + ^^^^^^^^^^^^^^^^^ +Warning 32 [unused-value-declaration]: unused value unused_unexported. +dead_code_analyzer --nothing -E all . +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= +/tmp/docs/exported_values/code_constructs/functor/functor_lib.mli:9: F.internally_used +/tmp/docs/exported_values/code_constructs/functor/functor_lib.mli:10: F.unused +/tmp/docs/exported_values/code_constructs/functor/functor_lib.mli:14: InternalParam.used_required +/tmp/docs/exported_values/code_constructs/functor/functor_lib.mli:15: InternalParam.unused_required +/tmp/docs/exported_values/code_constructs/functor/functor_lib.mli:25: InternalApp.internally_used +/tmp/docs/exported_values/code_constructs/functor/functor_lib.mli:26: InternalApp.unused + +Nothing else to report in this section +-------------------------------------------------------------------------------- + + +make: Leaving directory '/tmp/docs/exported_values/code_constructs/functor' +``` + +> The compiler reports 2 unused values, that can be removed at the reported locations : +> - `unused_required`, defined by `P`, the parameter of `F`; +> - `unexported_unused`, defined by `F`, like it did in the [module](MODULE.md) example. + +The analyzer reports 6 unused exported values, all in `functor_lib.mli` : +2 in `F`, 2 in `InternalParam`, and 2 in `InternalApp`. Let's observe them by +module (in reverse order) : + +- The reports for `InternalApp` are identical to the [module](MODULE.md) example. +Although, `InternalApp` is implemented as the result of applying `F` in +`functor_lib.ml`, its signature is independent of `F` in `functor_lib.mli`. +I.e. it exposes its own values and the link between them and those of +`F` is absent from the signature. Consequently, they are tracked independently. + +- As we observed before runnning the analyzer, the values in `InternalParam` are +used by requirement. However, this use is internal to `Functor_lib`, and there +is an interface available : `functor_lib.mli`. Consequently, the internal uses +are ignored, and `InternalParam`'s values become unused. + +- `F` is a functor but is tracked like a regular module. The reported values are +those of its result module. Reporting on those may feel like duplicates, but, +as explained for the reports of `InternalApp`, the values of the result +module are declared independently of those of `InternalApp`, hence they are +tracked and reported independently. + +All the values reported by the analyzer can be safely removed. + +Before moving on, there is another observation that we can make : +the values `unused` and `internally_used` of `ExternalApp` are not reported. +Because they are reported for `InternalApp` and `F`, one could +expect them to be reported for `ExternalApp` as well. In reality, they are not +tracked individually for `ExternalApp` because it does not expose them +explicitly. Unlike `InternalApp` which has an explicit module signature, +`ExternalApp` does not. Consequently, its values are directly linked to those of +`F`. This situation is explored in the +[module signature](MODSIG.md) example. + +> [!TIP] +> If we activated the compiler warning 67 `unused-functor-parameter` (by +> passing the argument `-w +67`), then the compiler would have reported : +> ``` +> File "functor_lib.mli", line 4, characters 10-11: +> 4 | module F (P : sig +> ^ +> Warning 67 [unused-functor-parameter]: unused functor parameter P. +> ``` +> This can be fixed by either replacing `P` with `_`, or by rewriting the +> declaration of `F` as : +> ```OCaml +> module F : sig +> val used_required : t +> val unused_required : t +> end +> -> sig +> val externally_used : t +> val internally_used : t +> val unused : t +> end +> ``` + +## Removing the unused values + +In addition to removing everything that was reported by the compiler and the +analyzer, we also commented out `P.used_required` and `InternalParam` in +`functor_lib.mli`. It is up to the user to decide whether they would like to +keep them or remove them from their specifications. Neither would be reported +by the compiler or the analyzer. + +> [!NOTE] +> It is allowed to give a larger module as argument than what the parameter +> specifies. Similarly it is allowed to declare the parameter larger in the +> interface than it is in the implementation. Consequently, the compiler would +> not complain if `P` expected `unused_required` in the `.mli` but not in the +> `.ml`. + +Code: +```OCaml +(* functor_lib.mli *) +type t + +module F (P : sig + val used_required : t + (* val unused_required : t *) +end) : sig + val externally_used : t +end + +(* +module InternalParam : sig +end +*) + +module ExternalParam : sig + val used_required : t + val unused_required : t +end + +module InternalApp : sig + val externally_used : t +end +``` +```OCaml +(* functor_lib.ml *) +type t = unit + +module F (P : sig + val used_required : t +end) = struct + let externally_used = P.used_required + let internally_used = P.used_required + let unused = P.used_required + let () = internally_used +end + +module InternalParam = struct + let used_required = () + let unused_required = () +end + +module ExternalParam = struct + let used_required = () + let unused_required = () +end + +module InternalApp = F(InternalParam) +``` +```OCaml +(* functor_bin.ml *) +open Functor_lib + +module ExternalApp = F(ExternalParam) + +let () = + ignore InternalApp.externally_used; + ignore ExternalApp.externally_used +``` + +Compile and analyze : +``` +$ make -C functor +make: Entering directory '/tmp/docs/exported_values/code_constructs/functor' +ocamlopt -w +32 -bin-annot functor_lib.mli functor_lib.ml functor_bin.ml +File "functor_lib.ml", line 9, characters 6-12: +9 | let unused = P.used_required + ^^^^^^ +Warning 32 [unused-value-declaration]: unused value unused. + +File "functor_lib.ml", line 15, characters 6-21: +15 | let unused_required = () + ^^^^^^^^^^^^^^^ +Warning 32 [unused-value-declaration]: unused value unused_required. +dead_code_analyzer --nothing -E all . +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= +/tmp/docs/exported_values/code_constructs/functor/functor_lib.mli:18: ExternalParam.unused_required + +Nothing else to report in this section +-------------------------------------------------------------------------------- + + +make: Leaving directory '/tmp/docs/exported_values/code_constructs/functor' +``` + +Now that `F.unused` and `InternalParam.unused_required` are not exported, they +are reported as unused by the compiler, and can be removed safely. + +`ExternalParam.unused_required` was used by requirement. Now that it is not +required by `P` (because it is commented out), it is unused and the analyzer +correctly reports it. It can be removed safely. Removing it will trigger the +same compiler warning as for `InternalParam.unused_required`, so it can be +removed from both the interface and the implementation. + +The unused values can be removed as explained. Our work here is done. diff --git a/docs/exported_values/code_constructs/INCLUDE.md b/docs/exported_values/code_constructs/INCLUDE.md new file mode 100644 index 00000000..32afa596 --- /dev/null +++ b/docs/exported_values/code_constructs/INCLUDE.md @@ -0,0 +1,163 @@ +The reference files for this example are in the +[include](../../../examples/docs/exported_values/code_constructs/include) directory. + +The reference takes place in `/tmp/docs/exported_values/code_constructs`, which +is a copy of the [code\_constructs](../../../examples/docs/exported_values/code_constructs) +directory. Reported locations may differ depending on the location of the source +files. + +The compilation command is : +``` +make -C include build +``` + +The analysis command is : +``` +make -C include analyze +``` + +The compile + analyze command is : +``` +make -C include +``` + +## First run + +Code: +```OCaml +(* include_lib.ml *) +module Original = struct + let used_directly = () + let used_indirectly = () + let unused = () +end + +module Reexport = struct + include Original +end + +module Redefine = struct + include Original + let used_directly = () + let unused = () +end +``` +```OCaml +(* include_bin.ml *) +let () = + let open Include_lib in + ignore Original.used_directly; + ignore Reexport.used_indirectly; + ignore Redefine.used_directly; +``` + +Before looking at the analysis results, let's look at the code. + +The `Include_lib` compilation unit does not have a `.mli`, so all the internal +uses are accounted for. It exposes 3 modules : +- `Original`, which explicitly defines all its values; +- `Reexport`, which only includes `Original`; +- `Redefine`, which includes `Original` and redefines 2 values : + `used_directly`, and `unused`. + +By the explanation in the [module signature](MODSIG.md) and +[module type](MODTYP.md) examples, although there are 9 exported values +(`used_directly`, `used_indirectly`, and `unused` for each module), only 5 are +expected to be tracked by the analyzer : those in `Original` and the 2 redefined +in `Redefine`. These are the only values a developer can trivially remove if +they are reported unused. + +Thus, the only values used are `Original.used_directly`, +`Original.used_indirectly` (by an explicit reference to +`Reexport.used_indirectly`), and `Redefine.used_directly`. This means that the +unused exported values tracked by the analyzer are `Original.unused` and +`Redefine.unused`. + +Compile and analyze: +``` +$ make -C include +make: Entering directory '/tmp/docs/exported_values/code_constructs/include' +ocamlopt -bin-annot include_lib.ml include_bin.ml +dead_code_analyzer --nothing -E all . +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= +/tmp/docs/exported_values/code_constructs/include/include_lib.ml:5: Original.unused +/tmp/docs/exported_values/code_constructs/include/include_lib.ml:5: Reexport.unused +/tmp/docs/exported_values/code_constructs/include/include_lib.ml:15: Redefine.unused + +Nothing else to report in this section +-------------------------------------------------------------------------------- + + +make: Leaving directory '/tmp/docs/exported_values/code_constructs/include' +``` + +The compiler does not report any unused value. + +As expected, the analyzer reports `Original.unused` and `Redefine.unused`. +However, it also reports `Reexport.unused`, which is unexpected. + +> [!WARNING] +> The extra report on `Reexport.unused` is a known bug, tracked by +> [issue #57](https://github.com/LexiFi/dead_code_analyzer/issues/57). +> This duplicated report only exists because the modules `Original` and +> `Reexport` belong to the same compilation unit (`Include_lib`). This can +> easily be verified by moving `Reexport` in `Include_bin` instead. + +Because the report of `Reexport.unused` is actually a duplicate of the report +of `Original.unused`, we can simply ignore it. + +## Removing the unused values + +The reported values can be removed from the implementation. + +Code: +```OCaml +(* include_lib.ml *) +module Original = struct + let used_directly = () + let used_indirectly = () +end + +module Reexport = struct + include Original +end + +module Redefine = struct + include Original + let used_directly = () +end +``` +```OCaml +(* include_bin.ml *) +let () = + let open Include_lib in + ignore Original.used_directly; + ignore Reexport.used_indirectly; + ignore Redefine.used_directly; +``` + +Compile and analyze: +``` +$ make -C include +make: Entering directory '/tmp/docs/exported_values/code_constructs/include' +ocamlopt -bin-annot include_lib.ml include_bin.ml +dead_code_analyzer --nothing -E all . +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= + +Nothing else to report in this section +-------------------------------------------------------------------------------- + + +make: Leaving directory '/tmp/docs/exported_values/code_constructs/include' +``` + +Now, neither the compiler nor the analyzer report any unused value. +Our work here is done. diff --git a/docs/exported_values/code_constructs/MODSIG.md b/docs/exported_values/code_constructs/MODSIG.md new file mode 100644 index 00000000..f49af91e --- /dev/null +++ b/docs/exported_values/code_constructs/MODSIG.md @@ -0,0 +1,165 @@ +The reference files for this example are in the +[modsig](../../../examples/docs/exported_values/code_constructs/modsig) directory. + +The reference takes place in `/tmp/docs/exported_values/code_constructs`, which +is a copy of the [code\_constructs](../../../examples/docs/exported_values/code_constructs) +directory. Reported locations may differ depending on the location of the source +files. + +The compilation command is : +``` +make -C modsig build +``` + +The analysis command is : +``` +make -C modsig analyze +``` + +The compile + analyze command is : +``` +make -C modsig +``` + +## First run + +Code: +```OCaml +(* modsig_lib.ml *) +module Original = struct + let used_directly = () + let used_indirectly = () + let used_by_requirement = () + let unused = () +end + +module Alias_without_sig = Original + +module Alias_with_sig : sig + val used_by_requirement : unit +end = Original +``` +```OCaml +(* modsig_bin.ml *) +let () = + let open Modsig_lib in + Original.used_directly; + Alias_without_sig.used_indirectly +``` + +Before looking at the analysis results, let's look at the code. + +The `Modsig_lib` compilation unit does not have a `.mli`, so all the internal +uses are accounted for. It exposes 3 modules : `Original`, `Alias_without_sig`, +and `Alias_with_sig`. Only the first module actually defines values. The second +one is an trivial alias, and the third is an alias with an explicit signature. +Because the 2 latter modules are aliases of the `Original` module, one could +expect that the values they expose are unified with the ones of `Original`. +This would imply that only the values in `Original` could be reported as unused. +This reasoning is partially true. + +As explained in the [module type](MODTYP.md) example, reporting a value in +`Alias_without_sig` would not be trivially solved. Thus, +its values are unified with those of `Original`, and, consequently, they +cannot be reported by the analyzer. Only the values in `Original` can be +reported in this case. + +However, `Alias_with_sig` has an explicit signature, which means 2 things: +1. it controls what it exports, thus a reporting values in that module is + trivially actionable by removing the reported values from the signature +2. it has requirements, thus all the values in `Original` that are expected in + the signature of `Alias_with_sig` are considered used. + +Now that we have explained the nuances introduced by the existence of an +explicit module signature, let's look at the results. + +Compile and analyze: +``` +$ make -C modsig +make: Entering directory '/tmp/docs/exported_values/code_constructs/modsig' +ocamlopt -bin-annot modsig_lib.ml modsig_bin.ml +dead_code_analyzer --nothing -E all . +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= +/tmp/docs/exported_values/code_constructs/modsig/modsig_lib.ml:6: Original.unused +/tmp/docs/exported_values/code_constructs/modsig/modsig_lib.ml:12: Alias_with_sig.used_by_requirement + +Nothing else to report in this section +-------------------------------------------------------------------------------- + + +make: Leaving directory '/tmp/docs/exported_values/code_constructs/modsig' +``` + +The compiler does not report any unused value. + +The analyzer reports that `Original.unused` and +`Alias_with_sig.used_by_requirement` are unused. As expected, it does not report +any unused value in `Alias_without_sig`. +Let's look more closely at the values and their uses. +- `Original.used_directly` is explicitly referenced in `Modsig_bin` +- `Original.used_indirectly` is used by an explicit reference to + `Alias_without_sig.used_indirectly` in `Modsig_bin` +- `Original.used_by_requirement` is used by requirement to fulfill + `Alias_with_sig`'s signature +- `Original.unused` is not referenced nor required anywhere +- `Alias_without_sig` does not "own" any value +- `Alias_with_sig.used_by_requirement` is not referenced nor required anywhere + +## Removing the unused values + +The reported values can be removed : `Original.unused` is removed from the +module's strucutre because it does not have an explicit signature, and +`Alias_with_sig.used_by_requirement` is removed from the module's signature. + +Code: +```OCaml +(* modsig_lib.ml *) +module Original = struct + let used_directly = () + let used_indirectly = () + let used_by_requirement = () +end + +module Alias_without_sig = Original + +module Alias_with_sig : sig end = Original +``` +```OCaml +(* modsig_bin.ml *) +let () = + let open Modsig_lib in + Original.used_directly; + Alias_without_sig.used_indirectly +``` + +Compile and analyze: +``` +$ make -C modsig +make: Entering directory '/tmp/docs/exported_values/code_constructs/modsig' +ocamlopt -bin-annot modsig_lib.ml modsig_bin.ml +dead_code_analyzer --nothing -E all . +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= +/tmp/docs/exported_values/code_constructs/modsig/modsig_lib.ml:5: Original.used_by_requirement + +Nothing else to report in this section +-------------------------------------------------------------------------------- + + +make: Leaving directory '/tmp/docs/exported_values/code_constructs/modsig' +``` + +The compiler does not report any unused value. + +The analyzer reports `Original.used_by_requirement` as unused. Indeed, +by removing `used_by_requirement` from the signature of `Alias_with_sig` we +removed the requirement for `Original` to provide it. This value can be removed +from `Orignal`, and neither the compiler nor the analyzer will report unused +values anymore. Our work here is done. diff --git a/docs/exported_values/code_constructs/MODTYP.md b/docs/exported_values/code_constructs/MODTYP.md new file mode 100644 index 00000000..aa06c0f7 --- /dev/null +++ b/docs/exported_values/code_constructs/MODTYP.md @@ -0,0 +1,154 @@ +The reference files for this example are in the +[modtyp](../../../examples/docs/exported_values/code_constructs/modtyp) directory. + +The reference takes place in `/tmp/docs/exported_values/code_constructs`, which +is a copy of the [code\_constructs](../../../examples/docs/exported_values/code_constructs) +directory. Reported locations may differ depending on the location of the source +files. + +The compilation command is : +``` +make -C modtyp build +``` + +The analysis command is : +``` +make -C modtyp analyze +``` + +The compile + analyze command is : +``` +make -C modtyp +``` + +> [!IMPORTANT] +> **LIMITATION** +> +> In order to reduce noise (false positives and duplication) in the results, +> the analyzer currently ignores values exported by module types +> (see [issue #50](https://github.com/LexiFi/dead_code_analyzer/issues/50)). + +## First run + +Code: +```OCaml +(* modtyp_lib.mli *) +module type T = sig + type t + val externally_used : t + val internally_used : t + val unused : t +end + +module M_reuse : T + +module M_constr : T with type t = unit + +module M_subst : T with type t := unit + +module M_redef : sig + type t + val externally_used : t + val internally_used : t + val unused : t +end +``` +```OCaml +(* modtyp_lib.ml *) +module type T = sig + type t + val externally_used : t + val internally_used : t + val unused : t +end + +module M = struct + type t = unit + let externally_used = () + let internally_used = () + let unused = () + let unused_unexported = () +end + +let () = M.internally_used + +module M_reuse = M + +module M_constr = M + +module M_subst = M + +module M_redef = M +``` +```OCaml +(* modtyp_bin.ml *) +let () = + ignore Modtyp_lib.M_reuse.externally_used; + ignore Modtyp_lib.M_constr.externally_used; + ignore Modtyp_lib.M_subst.externally_used; + ignore Modtyp_lib.M_redef.externally_used +``` + +Before looking at the analysis results, let's look at the code. + +The `Modtyp_lib` exports 1 module type `T` and 4 modules : +`M_reuse`, `M_constr`, `M_subst`, and `M_redef`. Of these 4 modules, the first +3 have `T` as signature (with minor twists), while the last one has its own +explicit signature, which is a copy of `T`. In this way, `M_redef` is equivalent +to `Module_lib.M` in the [module](MODULE.md) example : it exposes exactly +the same information. +Each of the modules exposed by `Modtyp_lib` are used exactly in the same way : +their `externally_used` values are explicitly referenced in `Modtyp_bin`. + +One could expect that all the exported values are reported except for the +`externally_used`. However, reporting e.g. `M_subst.internally_used` as unused +would not be immediately actionable. In reality, this value is explicitly +declared by `T`. +Fixing an unused value reported in a module using a module type as signature +would require either removing the value from the module type (if possible), +or explicilty describing the signature of the module, effectively losing the +benefits of using the module type. Thus, reporting unused values for the module +itself would be counterproductive. + +An actionable report would be of the value in the module type itself, +if it is unused by all the modules of that module type (as it is the case here +for `T.unused`). Currently, and as described in the introduction of this example, +the values exported by module types are ignored by the analyzer, and, +consequently, are not reported. + +Now that we have explained what the expected behavior of the analyzer should be, +let's look at its results on the code above. + +Compile and analyze : +``` +$ make -C modtyp +make: Entering directory '/tmp/docs/exported_values/code_constructs/modtyp' +ocamlopt -w +32 -bin-annot modtyp_lib.mli modtyp_lib.ml modtyp_bin.ml +File "modtyp_lib.ml", line 14, characters 6-23: +14 | let unused_unexported = () + ^^^^^^^^^^^^^^^^^ +Warning 32 [unused-value-declaration]: unused value unused_unexported. +dead_code_analyzer --nothing -E all . +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= +/tmp/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:18: M_redef.internally_used +/tmp/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:19: M_redef.unused + +Nothing else to report in this section +-------------------------------------------------------------------------------- + + +make: Leaving directory '/tmp/docs/exported_values/code_constructs/modtyp' +``` + +As in the module example, the compiler reports that `unused_unexported` is unused. + +As in the module example, the analyzer reports `M_redef.internally_used` and +`M_redef.unused` as unused exported values. + +All the reports are similar to those of the [module](MODULE.md) example. +Its exploration and resolution can be applied. +Our work here is done. diff --git a/docs/exported_values/code_constructs/MODULE.md b/docs/exported_values/code_constructs/MODULE.md new file mode 100644 index 00000000..23ada048 --- /dev/null +++ b/docs/exported_values/code_constructs/MODULE.md @@ -0,0 +1,153 @@ +The reference files for this example are in the +[module](../../../examples/docs/exported_values/code_constructs/module) directory. + +The reference takes place in `/tmp/docs/exported_values/code_constructs`, which +is a copy of the [code\_constructs](../../../examples/docs/exported_values/code_constructs) +directory. Reported locations may differ depending on the location of the source +files. + +The compilation command is : +``` +make -C module build +``` + +The analysis command is : +``` +make -C module analyze +``` + +The compile + analyze command is : +``` +make -C module +``` + +## First run + +Code: +```OCaml +(* module_lib.mli *) +module M : sig + type t + val externally_used : t + val internally_used : t + val unused : t +end +``` +```OCaml +(* module_lib.ml *) +module M = struct + type t = unit + let externally_used = () + let internally_used = () + let unused = () + let unused_unexported = () +end + +let () = M.internally_used +``` +```OCaml +(* module_bin.ml *) +let () = + ignore Module_lib.M.externally_used +``` + +Before looking at the analysis results, let's look at the code. + +All the values of `Module_lib.M` are exported except for +`unused_unexported`. Among the exported values, `unused` is not referenced +anywhere, `internally_used` is only referenced within its compilation unit +(`Module_lib`), and `externally_used` is only referenced outside of it. + +> [!IMPORTANT] +> Using `internally_used` inside of `M` rather than outside would provide the +> same results. The only scope of interest is the compilation unit. + +Compile and analyze : +``` +$ make -C module +make: Entering directory '/tmp/docs/exported_values/code_constructs/module' +ocamlopt -w +32 -bin-annot module_lib.mli module_lib.ml module_bin.ml +File "module_lib.ml", line 7, characters 6-23: +7 | let unused_unexported = () + ^^^^^^^^^^^^^^^^^ +Warning 32 [unused-value-declaration]: unused value unused_unexported. +dead_code_analyzer --nothing -E all . +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= +/tmp/docs/exported_values/code_constructs/module/module_lib.mli:5: M.internally_used +/tmp/docs/exported_values/code_constructs/module/module_lib.mli:6: M.unused + +Nothing else to report in this section +-------------------------------------------------------------------------------- + + +make: Leaving directory '/tmp/docs/exported_values/code_constructs/module' +``` + +The compiler reports that `unused_unexported` is unused. + +The analyzer reports `M.internally_used` and `M.unused` as unused. Notice how +it did not only report the name of the value but its full path within its +compilation unit. + +> [!NOTE] +> `M.internally_used` is only used within its compilation unit. Because it is +> declared in a `.mli`, only external uses are accounted for. +> It is left as an exercise to the reader to explore this example without +> `module_lib.mli` + +## Removing the unused values + +Code: +```OCaml +(* module_lib.mli *) +module M : sig + type t + val externally_used : t +end +``` +```OCaml +(* module_lib.ml *) +module M = struct + type t = unit + let externally_used = () + let internally_used = () + let unused = () +end + +let () = M.internally_used +``` +```OCaml +(* module_bin.ml *) +let () = + ignore Module_lib.M.externally_used +``` + +Compile and analyze : +``` +$ make -C module +make: Entering directory '/tmp/docs/exported_values/code_constructs/module' +ocamlopt -w +32 -bin-annot module_lib.mli module_lib.ml module_bin.ml +File "module_lib.ml", line 6, characters 6-12: +6 | let unused = () + ^^^^^^ +Warning 32 [unused-value-declaration]: unused value unused. +dead_code_analyzer --nothing -E all . +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= + +Nothing else to report in this section +-------------------------------------------------------------------------------- + + +make: Leaving directory '/tmp/docs/exported_values/code_constructs/module' +``` + +The compiler reports `unused` as unused and the analyzer does not report +anything. Removing that value fixes all the warnings. Our work here is done. From 482ab96b867ea97530c511f2dc8e6fe3ff2a89da Mon Sep 17 00:00:00 2001 From: Corentin De Souza <9597216+fantazio@users.noreply.github.com> Date: Tue, 10 Mar 2026 19:26:30 +0100 Subject: [PATCH 16/16] [docs][exported_values][16/n] use snake_case in functor example --- check/classic/classic.exp | 8 +- check/classic/classic.ref | 8 +- check/internal/internal.exp | 2 +- check/internal/internal.ref | 2 +- check/threshold-1/threshold-1.exp | 10 +- check/threshold-1/threshold-1.ref | 10 +- check/threshold-3-0.5/threshold-3-0.5.exp | 14 +-- check/threshold-3-0.5/threshold-3-0.5.ref | 14 +-- .../code_constructs/FUNCTOR.md | 92 +++++++++---------- .../code_constructs/functor/functor_bin.ml | 6 +- .../code_constructs/functor/functor_lib.ml | 6 +- .../code_constructs/functor/functor_lib.mli | 6 +- 12 files changed, 89 insertions(+), 89 deletions(-) diff --git a/check/classic/classic.exp b/check/classic/classic.exp index c7289d0d..1804311c 100644 --- a/check/classic/classic.exp +++ b/check/classic/classic.exp @@ -4,10 +4,10 @@ ./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:9: F.internally_used ./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:10: F.unused -./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:14: InternalParam.used_required -./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:15: InternalParam.unused_required -./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:25: InternalApp.internally_used -./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:26: InternalApp.unused +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:14: Internal_param.used_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:15: Internal_param.unused_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:25: Internal_app.internally_used +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:26: Internal_app.unused ./examples/docs/exported_values/code_constructs/include/include_lib.ml:5: Original.unused ./examples/docs/exported_values/code_constructs/include/include_lib.ml:15: Redefine.unused diff --git a/check/classic/classic.ref b/check/classic/classic.ref index 98369634..bf4b80b1 100644 --- a/check/classic/classic.ref +++ b/check/classic/classic.ref @@ -4,10 +4,10 @@ ./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:9: F.internally_used ./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:10: F.unused -./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:14: InternalParam.used_required -./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:15: InternalParam.unused_required -./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:25: InternalApp.internally_used -./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:26: InternalApp.unused +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:14: Internal_param.used_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:15: Internal_param.unused_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:25: Internal_app.internally_used +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:26: Internal_app.unused ./examples/docs/exported_values/code_constructs/include/include_lib.ml:5: Original.unused ./examples/docs/exported_values/code_constructs/include/include_lib.ml:5: Reexport.unused: Should not be detected diff --git a/check/internal/internal.exp b/check/internal/internal.exp index 13782328..7a7042c8 100644 --- a/check/internal/internal.exp +++ b/check/internal/internal.exp @@ -3,7 +3,7 @@ ./examples/docs/exported_values/code_constructs/function/function_lib.mli:7: unused ./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:10: F.unused -./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:26: InternalApp.unused +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:26: Internal_app.unused ./examples/docs/exported_values/code_constructs/include/include_lib.ml:5: Original.unused ./examples/docs/exported_values/code_constructs/include/include_lib.ml:15: Redefine.unused diff --git a/check/internal/internal.ref b/check/internal/internal.ref index 928df905..f028200a 100644 --- a/check/internal/internal.ref +++ b/check/internal/internal.ref @@ -3,7 +3,7 @@ ./examples/docs/exported_values/code_constructs/function/function_lib.mli:7: unused ./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:10: F.unused -./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:26: InternalApp.unused +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:26: Internal_app.unused ./examples/docs/exported_values/code_constructs/include/include_lib.ml:5: Original.unused ./examples/docs/exported_values/code_constructs/include/include_lib.ml:5: Reexport.unused: Should not be detected diff --git a/check/threshold-1/threshold-1.exp b/check/threshold-1/threshold-1.exp index 983f61ae..b1c62f3c 100644 --- a/check/threshold-1/threshold-1.exp +++ b/check/threshold-1/threshold-1.exp @@ -3,7 +3,7 @@ ./examples/docs/exported_values/code_constructs/function/function_lib.mli:7: unused ./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:10: F.unused -./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:26: InternalApp.unused +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:26: Internal_app.unused ./examples/docs/exported_values/code_constructs/include/include_lib.ml:5: Original.unused ./examples/docs/exported_values/code_constructs/include/include_lib.ml:15: Redefine.unused @@ -105,10 +105,10 @@ ./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:8: F.externally_used ./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:9: F.internally_used -./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:14: InternalParam.used_required -./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:15: InternalParam.unused_required -./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:24: InternalApp.externally_used -./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:25: InternalApp.internally_used +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:14: Internal_param.used_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:15: Internal_param.unused_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:24: Internal_app.externally_used +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:25: Internal_app.internally_used ./examples/docs/exported_values/code_constructs/include/include_lib.ml:3: Original.used_directly ./examples/docs/exported_values/code_constructs/include/include_lib.ml:4: Original.used_indirectly diff --git a/check/threshold-1/threshold-1.ref b/check/threshold-1/threshold-1.ref index 30dbf0e9..13e7d268 100644 --- a/check/threshold-1/threshold-1.ref +++ b/check/threshold-1/threshold-1.ref @@ -3,7 +3,7 @@ ./examples/docs/exported_values/code_constructs/function/function_lib.mli:7: unused ./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:10: F.unused -./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:26: InternalApp.unused +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:26: Internal_app.unused ./examples/docs/exported_values/code_constructs/include/include_lib.ml:5: Original.unused ./examples/docs/exported_values/code_constructs/include/include_lib.ml:5: Reexport.unused: Should not be detected @@ -106,10 +106,10 @@ ./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:8: F.externally_used ./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:9: F.internally_used -./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:14: InternalParam.used_required -./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:15: InternalParam.unused_required -./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:24: InternalApp.externally_used -./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:25: InternalApp.internally_used +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:14: Internal_param.used_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:15: Internal_param.unused_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:24: Internal_app.externally_used +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:25: Internal_app.internally_used ./examples/docs/exported_values/code_constructs/include/include_lib.ml:3: Original.used_directly ./examples/docs/exported_values/code_constructs/include/include_lib.ml:3: Reexport.used_directly: Should not be detected 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 194ab8fe..a587ebad 100644 --- a/check/threshold-3-0.5/threshold-3-0.5.exp +++ b/check/threshold-3-0.5/threshold-3-0.5.exp @@ -3,7 +3,7 @@ ./examples/docs/exported_values/code_constructs/function/function_lib.mli:7: unused ./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:10: F.unused -./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:26: InternalApp.unused +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:26: Internal_app.unused ./examples/docs/exported_values/code_constructs/include/include_lib.ml:5: Original.unused ./examples/docs/exported_values/code_constructs/include/include_lib.ml:15: Redefine.unused @@ -105,10 +105,10 @@ ./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:8: F.externally_used ./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:9: F.internally_used -./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:14: InternalParam.used_required -./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:15: InternalParam.unused_required -./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:24: InternalApp.externally_used -./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:25: InternalApp.internally_used +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:14: Internal_param.used_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:15: Internal_param.unused_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:24: Internal_app.externally_used +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:25: Internal_app.internally_used ./examples/docs/exported_values/code_constructs/include/include_lib.ml:3: Original.used_directly ./examples/docs/exported_values/code_constructs/include/include_lib.ml:4: Original.used_indirectly @@ -334,8 +334,8 @@ .>-> ALMOST UNUSED EXPORTED VALUES: Called 2 time(s): ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:19: ExternalParam.used_required -./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:20: ExternalParam.unused_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:19: External_param.used_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:20: External_param.unused_required ./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:13: M_subst.externally_used ./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:13: M_subst.internally_used 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 6566bf45..faf280c6 100644 --- a/check/threshold-3-0.5/threshold-3-0.5.ref +++ b/check/threshold-3-0.5/threshold-3-0.5.ref @@ -3,7 +3,7 @@ ./examples/docs/exported_values/code_constructs/function/function_lib.mli:7: unused ./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:10: F.unused -./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:26: InternalApp.unused +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:26: Internal_app.unused ./examples/docs/exported_values/code_constructs/include/include_lib.ml:5: Original.unused ./examples/docs/exported_values/code_constructs/include/include_lib.ml:5: Reexport.unused: Should not be detected @@ -106,10 +106,10 @@ ./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:8: F.externally_used ./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:9: F.internally_used -./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:14: InternalParam.used_required -./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:15: InternalParam.unused_required -./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:24: InternalApp.externally_used -./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:25: InternalApp.internally_used +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:14: Internal_param.used_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:15: Internal_param.unused_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:24: Internal_app.externally_used +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:25: Internal_app.internally_used ./examples/docs/exported_values/code_constructs/include/include_lib.ml:3: Original.used_directly ./examples/docs/exported_values/code_constructs/include/include_lib.ml:3: Reexport.used_directly: Should not be detected @@ -338,8 +338,8 @@ .>-> ALMOST UNUSED EXPORTED VALUES: Called 2 time(s): ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:19: ExternalParam.used_required -./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:20: ExternalParam.unused_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:19: External_param.used_required +./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:20: External_param.unused_required ./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:13: M_subst.externally_used ./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:13: M_subst.internally_used diff --git a/docs/exported_values/code_constructs/FUNCTOR.md b/docs/exported_values/code_constructs/FUNCTOR.md index 2bde4d88..514a40d5 100644 --- a/docs/exported_values/code_constructs/FUNCTOR.md +++ b/docs/exported_values/code_constructs/FUNCTOR.md @@ -37,17 +37,17 @@ end) : sig val unused : t end -module InternalParam : sig +module Internal_param : sig val used_required : t val unused_required : t end -module ExternalParam : sig +module External_param : sig val used_required : t val unused_required : t end -module InternalApp : sig +module Internal_app : sig val externally_used : t val internally_used : t val unused : t @@ -68,27 +68,27 @@ end) = struct let () = internally_used end -module InternalParam = struct +module Internal_param = struct let used_required = () let unused_required = () end -module ExternalParam = struct +module External_param = struct let used_required = () let unused_required = () end -module InternalApp = F(InternalParam) +module Internal_app = F(Internal_param) ``` ```OCaml (* functor_bin.ml *) open Functor_lib -module ExternalApp = F(ExternalParam) +module External_app = F(External_param) let () = - ignore InternalApp.externally_used; - ignore ExternalApp.externally_used + ignore Internal_app.externally_used; + ignore External_app.externally_used ``` Before looking at the analysis results, let's look at the code. @@ -96,21 +96,21 @@ Before looking at the analysis results, let's look at the code. The `Functor_lib` compilation unit exports 1 functor and 3 modules : - `F` takes a module containing the values `used_required` and `unused_required`, and returns a module with 3 values whose names are explicit. -- `InternalParam` and `ExternalParam` fit the signature of `F`'s parameter `P`. +- `Internal_param` and `External_param` fit the signature of `F`'s parameter `P`. The first one is used for a functor application inside `Functor_lib`. The second one is used for a functor application outside of it. -- `InternalApp` fits the signature of the result of `F`, and is impemented as +- `Internal_app` fits the signature of the result of `F`, and is impemented as the result of applying `F` inside its compilation unit. -The `Functor_bin` compilation unit exports 1 module : `ExternalApp`, which is +The `Functor_bin` compilation unit exports 1 module : `External_app`, which is the result of applying `Functor_lib.F` outside its compilation unit. Among all the exported values, the only explicit references accounted for are -`InternalApp.externally_used` and `External_app.externally_used` in +`Internal_app.externally_used` and `External_app.externally_used` in `Functor_bin`. -Additionally, some values are used by requirement. Because `InternalParam` and -`ExternalParam` are passed as arguments to `F`, their values `used_required` and +Additionally, some values are used by requirement. Because `Internal_param` and +`External_param` are passed as arguments to `F`, their values `used_required` and `unused_required` are used by requirement to fulfill the signature of `F`'s parameter `P`. @@ -139,10 +139,10 @@ Scanning files... ========================= /tmp/docs/exported_values/code_constructs/functor/functor_lib.mli:9: F.internally_used /tmp/docs/exported_values/code_constructs/functor/functor_lib.mli:10: F.unused -/tmp/docs/exported_values/code_constructs/functor/functor_lib.mli:14: InternalParam.used_required -/tmp/docs/exported_values/code_constructs/functor/functor_lib.mli:15: InternalParam.unused_required -/tmp/docs/exported_values/code_constructs/functor/functor_lib.mli:25: InternalApp.internally_used -/tmp/docs/exported_values/code_constructs/functor/functor_lib.mli:26: InternalApp.unused +/tmp/docs/exported_values/code_constructs/functor/functor_lib.mli:14: Internal_param.used_required +/tmp/docs/exported_values/code_constructs/functor/functor_lib.mli:15: Internal_param.unused_required +/tmp/docs/exported_values/code_constructs/functor/functor_lib.mli:25: Internal_app.internally_used +/tmp/docs/exported_values/code_constructs/functor/functor_lib.mli:26: Internal_app.unused Nothing else to report in this section -------------------------------------------------------------------------------- @@ -156,35 +156,35 @@ make: Leaving directory '/tmp/docs/exported_values/code_constructs/functor' > - `unexported_unused`, defined by `F`, like it did in the [module](MODULE.md) example. The analyzer reports 6 unused exported values, all in `functor_lib.mli` : -2 in `F`, 2 in `InternalParam`, and 2 in `InternalApp`. Let's observe them by +2 in `F`, 2 in `Internal_param`, and 2 in `Internal_app`. Let's observe them by module (in reverse order) : -- The reports for `InternalApp` are identical to the [module](MODULE.md) example. -Although, `InternalApp` is implemented as the result of applying `F` in +- The reports for `Internal_app` are identical to the [module](MODULE.md) example. +Although, `Internal_app` is implemented as the result of applying `F` in `functor_lib.ml`, its signature is independent of `F` in `functor_lib.mli`. I.e. it exposes its own values and the link between them and those of `F` is absent from the signature. Consequently, they are tracked independently. -- As we observed before runnning the analyzer, the values in `InternalParam` are +- As we observed before runnning the analyzer, the values in `Internal_param` are used by requirement. However, this use is internal to `Functor_lib`, and there is an interface available : `functor_lib.mli`. Consequently, the internal uses -are ignored, and `InternalParam`'s values become unused. +are ignored, and `Internal_param`'s values become unused. - `F` is a functor but is tracked like a regular module. The reported values are those of its result module. Reporting on those may feel like duplicates, but, -as explained for the reports of `InternalApp`, the values of the result -module are declared independently of those of `InternalApp`, hence they are +as explained for the reports of `Internal_app`, the values of the result +module are declared independently of those of `Internal_app`, hence they are tracked and reported independently. All the values reported by the analyzer can be safely removed. Before moving on, there is another observation that we can make : -the values `unused` and `internally_used` of `ExternalApp` are not reported. -Because they are reported for `InternalApp` and `F`, one could -expect them to be reported for `ExternalApp` as well. In reality, they are not -tracked individually for `ExternalApp` because it does not expose them -explicitly. Unlike `InternalApp` which has an explicit module signature, -`ExternalApp` does not. Consequently, its values are directly linked to those of +the values `unused` and `internally_used` of `External_app` are not reported. +Because they are reported for `Internal_app` and `F`, one could +expect them to be reported for `External_app` as well. In reality, they are not +tracked individually for `External_app` because it does not expose them +explicitly. Unlike `Internal_app` which has an explicit module signature, +`External_app` does not. Consequently, its values are directly linked to those of `F`. This situation is explored in the [module signature](MODSIG.md) example. @@ -214,7 +214,7 @@ explicitly. Unlike `InternalApp` which has an explicit module signature, ## Removing the unused values In addition to removing everything that was reported by the compiler and the -analyzer, we also commented out `P.used_required` and `InternalParam` in +analyzer, we also commented out `P.used_required` and `Internal_param` in `functor_lib.mli`. It is up to the user to decide whether they would like to keep them or remove them from their specifications. Neither would be reported by the compiler or the analyzer. @@ -239,16 +239,16 @@ end) : sig end (* -module InternalParam : sig +module Internal_param : sig end *) -module ExternalParam : sig +module External_param : sig val used_required : t val unused_required : t end -module InternalApp : sig +module Internal_app : sig val externally_used : t end ``` @@ -265,27 +265,27 @@ end) = struct let () = internally_used end -module InternalParam = struct +module Internal_param = struct let used_required = () let unused_required = () end -module ExternalParam = struct +module External_param = struct let used_required = () let unused_required = () end -module InternalApp = F(InternalParam) +module Internal_app = F(Internal_param) ``` ```OCaml (* functor_bin.ml *) open Functor_lib -module ExternalApp = F(ExternalParam) +module External_app = F(External_param) let () = - ignore InternalApp.externally_used; - ignore ExternalApp.externally_used + ignore Internal_app.externally_used; + ignore External_app.externally_used ``` Compile and analyze : @@ -308,7 +308,7 @@ Scanning files... .> UNUSED EXPORTED VALUES: ========================= -/tmp/docs/exported_values/code_constructs/functor/functor_lib.mli:18: ExternalParam.unused_required +/tmp/docs/exported_values/code_constructs/functor/functor_lib.mli:18: External_param.unused_required Nothing else to report in this section -------------------------------------------------------------------------------- @@ -317,13 +317,13 @@ Nothing else to report in this section make: Leaving directory '/tmp/docs/exported_values/code_constructs/functor' ``` -Now that `F.unused` and `InternalParam.unused_required` are not exported, they +Now that `F.unused` and `Internal_param.unused_required` are not exported, they are reported as unused by the compiler, and can be removed safely. -`ExternalParam.unused_required` was used by requirement. Now that it is not +`External_param.unused_required` was used by requirement. Now that it is not required by `P` (because it is commented out), it is unused and the analyzer correctly reports it. It can be removed safely. Removing it will trigger the -same compiler warning as for `InternalParam.unused_required`, so it can be +same compiler warning as for `Internal_param.unused_required`, so it can be removed from both the interface and the implementation. The unused values can be removed as explained. Our work here is done. diff --git a/examples/docs/exported_values/code_constructs/functor/functor_bin.ml b/examples/docs/exported_values/code_constructs/functor/functor_bin.ml index 28a98f04..18ff81c2 100644 --- a/examples/docs/exported_values/code_constructs/functor/functor_bin.ml +++ b/examples/docs/exported_values/code_constructs/functor/functor_bin.ml @@ -1,8 +1,8 @@ (* functor_bin.ml *) open Functor_lib -module ExternalApp = F(ExternalParam) +module External_app = F(External_param) let () = - ignore InternalApp.externally_used; - ignore ExternalApp.externally_used + ignore Internal_app.externally_used; + ignore External_app.externally_used diff --git a/examples/docs/exported_values/code_constructs/functor/functor_lib.ml b/examples/docs/exported_values/code_constructs/functor/functor_lib.ml index 911b7b1a..102a8973 100644 --- a/examples/docs/exported_values/code_constructs/functor/functor_lib.ml +++ b/examples/docs/exported_values/code_constructs/functor/functor_lib.ml @@ -12,14 +12,14 @@ end) = struct let () = internally_used end -module InternalParam = struct +module Internal_param = struct let used_required = () let unused_required = () end -module ExternalParam = struct +module External_param = struct let used_required = () let unused_required = () end -module InternalApp = F(InternalParam) +module Internal_app = F(Internal_param) diff --git a/examples/docs/exported_values/code_constructs/functor/functor_lib.mli b/examples/docs/exported_values/code_constructs/functor/functor_lib.mli index 45af0126..f2cdfd8a 100644 --- a/examples/docs/exported_values/code_constructs/functor/functor_lib.mli +++ b/examples/docs/exported_values/code_constructs/functor/functor_lib.mli @@ -10,17 +10,17 @@ end) : sig val unused : t end -module InternalParam : sig +module Internal_param : sig val used_required : t val unused_required : t end -module ExternalParam : sig +module External_param : sig val used_required : t val unused_required : t end -module InternalApp : sig +module Internal_app : sig val externally_used : t val internally_used : t val unused : t