diff --git a/check/classic/classic.exp b/check/classic/classic.exp index 1804311c..efc07a67 100644 --- a/check/classic/classic.exp +++ b/check/classic/classic.exp @@ -569,6 +569,8 @@ Nothing else to report in this section ./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/docs/exported_values/limitations/sigincl/sigincl_lib.ml:9: unit pattern x + ./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/classic/classic.ref b/check/classic/classic.ref index bf4b80b1..ba7997bf 100644 --- a/check/classic/classic.ref +++ b/check/classic/classic.ref @@ -10,7 +10,6 @@ ./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 ./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 @@ -26,6 +25,11 @@ ./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/docs/exported_values/limitations/incl_same_name/oo.mli:20: copy: Should not be detected +./examples/docs/exported_values/limitations/incl_same_name/oo.mli:25: id: Should not be detected +./examples/docs/exported_values/limitations/incl_same_name/oo.mli:40: new_method: Should not be detected +./examples/docs/exported_values/limitations/incl_same_name/oo.mli:41: public_method_label: Should not be detected +./examples/docs/exported_values/limitations/sigincl/sigincl_lib.mli:10: I.x: Should not be detected ./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 @@ -570,6 +574,8 @@ Nothing else to report in this section ./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/docs/exported_values/limitations/sigincl/sigincl_lib.ml:9: unit pattern x + ./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: ... -> (... -> ?_:_ -> ...) -> ... @@ -617,7 +623,7 @@ Nothing else to report in this section -------------------------------------------------------------------------------- -Total: 531 -Success: 530 -Failed: 1 -Ratio: 99.8116760829% +Total: 536 +Success: 531 +Failed: 5 +Ratio: 99.0671641791% diff --git a/check/internal/internal.exp b/check/internal/internal.exp index 7a7042c8..0b9dfad1 100644 --- a/check/internal/internal.exp +++ b/check/internal/internal.exp @@ -524,6 +524,8 @@ Nothing else to report in this section ./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/docs/exported_values/limitations/sigincl/sigincl_lib.ml:9: unit pattern x + ./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/internal/internal.ref b/check/internal/internal.ref index f028200a..9c869823 100644 --- a/check/internal/internal.ref +++ b/check/internal/internal.ref @@ -6,7 +6,6 @@ ./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 ./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 @@ -16,6 +15,11 @@ ./examples/docs/exported_values/code_constructs/module/module_lib.mli:6: M.unused +./examples/docs/exported_values/limitations/incl_same_name/oo.mli:20: copy: Should not be detected +./examples/docs/exported_values/limitations/incl_same_name/oo.mli:25: id: Should not be detected +./examples/docs/exported_values/limitations/incl_same_name/oo.mli:40: new_method: Should not be detected +./examples/docs/exported_values/limitations/incl_same_name/oo.mli:41: public_method_label: Should not be detected +./examples/docs/exported_values/limitations/sigincl/sigincl_lib.mli:10: I.x: Should not be detected ./examples/using_dune/preprocessed_lib/preprocessed.mli:1: unused ./examples/using_dune/preprocessed_lib/preprocessed_no_intf.ml:2: unused @@ -525,6 +529,8 @@ Nothing else to report in this section ./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/docs/exported_values/limitations/sigincl/sigincl_lib.ml:9: unit pattern x + ./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: ... -> (... -> ?_:_ -> ...) -> ... @@ -572,7 +578,7 @@ Nothing else to report in this section -------------------------------------------------------------------------------- -Total: 489 -Success: 488 -Failed: 1 -Ratio: 99.7955010225% +Total: 494 +Success: 489 +Failed: 5 +Ratio: 98.987854251% diff --git a/check/threshold-1/threshold-1.exp b/check/threshold-1/threshold-1.exp index b1c62f3c..7f8b44f8 100644 --- a/check/threshold-1/threshold-1.exp +++ b/check/threshold-1/threshold-1.exp @@ -107,6 +107,8 @@ ./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: 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: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/functor/functor_lib.mli:24: Internal_app.externally_used ./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:25: Internal_app.internally_used @@ -899,6 +901,8 @@ Nothing else to report in this section ./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/docs/exported_values/limitations/sigincl/sigincl_lib.ml:9: unit pattern x + ./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 13e7d268..0f3a3f17 100644 --- a/check/threshold-1/threshold-1.ref +++ b/check/threshold-1/threshold-1.ref @@ -6,7 +6,6 @@ ./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 ./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 @@ -16,6 +15,11 @@ ./examples/docs/exported_values/code_constructs/module/module_lib.mli:6: M.unused +./examples/docs/exported_values/limitations/incl_same_name/oo.mli:20: copy: Should not be detected +./examples/docs/exported_values/limitations/incl_same_name/oo.mli:25: id: Should not be detected +./examples/docs/exported_values/limitations/incl_same_name/oo.mli:40: new_method: Should not be detected +./examples/docs/exported_values/limitations/incl_same_name/oo.mli:41: public_method_label: Should not be detected +./examples/docs/exported_values/limitations/sigincl/sigincl_lib.mli:10: I.x: Should not be detected ./examples/using_dune/preprocessed_lib/preprocessed.mli:1: unused ./examples/using_dune/preprocessed_lib/preprocessed_no_intf.ml:2: unused @@ -108,14 +112,13 @@ ./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: 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: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/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 ./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 @@ -903,6 +906,8 @@ Nothing else to report in this section ./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/docs/exported_values/limitations/sigincl/sigincl_lib.ml:9: unit pattern x + ./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: ... -> (... -> ?_:_ -> ...) -> ... @@ -950,7 +955,7 @@ Nothing else to report in this section -------------------------------------------------------------------------------- -Total: 821 -Success: 817 -Failed: 4 -Ratio: 99.5127892814% +Total: 825 +Success: 820 +Failed: 5 +Ratio: 99.3939393939% 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 a587ebad..13143ef3 100644 --- a/check/threshold-3-0.5/threshold-3-0.5.exp +++ b/check/threshold-3-0.5/threshold-3-0.5.exp @@ -107,6 +107,8 @@ ./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: 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: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/functor/functor_lib.mli:24: Internal_app.externally_used ./examples/docs/exported_values/code_constructs/functor/functor_lib.mli:25: Internal_app.internally_used @@ -334,13 +336,6 @@ .>-> ALMOST UNUSED EXPORTED VALUES: Called 2 time(s): ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -./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 -./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:13: M_subst.unused - ./examples/docs/exported_values/hello_world/hello_world_lib.mli: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 @@ -1295,6 +1290,8 @@ Nothing else to report in this section ./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/docs/exported_values/limitations/sigincl/sigincl_lib.ml:9: unit pattern x + ./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 faf280c6..518f3410 100644 --- a/check/threshold-3-0.5/threshold-3-0.5.ref +++ b/check/threshold-3-0.5/threshold-3-0.5.ref @@ -6,7 +6,6 @@ ./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 ./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 @@ -16,6 +15,11 @@ ./examples/docs/exported_values/code_constructs/module/module_lib.mli:6: M.unused +./examples/docs/exported_values/limitations/incl_same_name/oo.mli:20: copy: Should not be detected +./examples/docs/exported_values/limitations/incl_same_name/oo.mli:25: id: Should not be detected +./examples/docs/exported_values/limitations/incl_same_name/oo.mli:40: new_method: Should not be detected +./examples/docs/exported_values/limitations/incl_same_name/oo.mli:41: public_method_label: Should not be detected +./examples/docs/exported_values/limitations/sigincl/sigincl_lib.mli:10: I.x: Should not be detected ./examples/using_dune/preprocessed_lib/preprocessed.mli:1: unused ./examples/using_dune/preprocessed_lib/preprocessed_no_intf.ml:2: unused @@ -108,14 +112,13 @@ ./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: 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: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/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 ./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 @@ -338,13 +341,6 @@ .>-> ALMOST UNUSED EXPORTED VALUES: Called 2 time(s): ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -./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 -./examples/docs/exported_values/code_constructs/modtyp/modtyp_lib.mli:13: M_subst.unused - ./examples/docs/exported_values/hello_world/hello_world_lib.mli: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 @@ -1299,6 +1295,8 @@ Nothing else to report in this section ./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/docs/exported_values/limitations/sigincl/sigincl_lib.ml:9: unit pattern x + ./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: ... -> (... -> ?_:_ -> ...) -> ... @@ -1346,7 +1344,7 @@ Nothing else to report in this section -------------------------------------------------------------------------------- -Total: 1138 -Success: 1134 -Failed: 4 -Ratio: 99.6485061511% +Total: 1137 +Success: 1132 +Failed: 5 +Ratio: 99.5602462621% diff --git a/docs/exported_values/EXPORTED_VALUES.md b/docs/exported_values/EXPORTED_VALUES.md index 715bf97f..f2d43678 100644 --- a/docs/exported_values/EXPORTED_VALUES.md +++ b/docs/exported_values/EXPORTED_VALUES.md @@ -8,6 +8,10 @@ + [Warning 32: unused-value-declaration](#warning-32-unused-value-declaration) + [Usage](#usage) + [Examples](#examples) ++ [Limitations](#limitations) + + [Module type](#module-type) + + [Include module type with substitution](#include-module-type-with-substitution) + + [Including a module with same name](#including-a-module-with-the-same-name) # Exported Values @@ -157,6 +161,19 @@ For more details about the command line arguments see [the more general Usage documentation](../USAGE.md). The report section looks like: +``` +.> UNUSED EXPORTED VALUES: +========================= +filepath:line: value + +Nothing else to report in this section +-------------------------------------------------------------------------------- +``` +The report line format is `filepath:line: value` with `filepath` the absolute +path to the file (`.mli` if available, `.ml` otherwise) where `value` is +declared, `line` the line index in `filepath` at which `value` is declared, and +`value` the path of the value within its compilation unit (e.g. `M.x`). +There can be any number of such lines. The expected resolution for an unused exported value is to remove it from the `.mli` if there is one, or the `.ml` otherwise. @@ -212,3 +229,175 @@ Warning 26 [unused-var]: unused variable y. - [Module type](./code_constructs/MODTYP.md) - [Module signature](./code_constructs/MODSIG.md) - [Include](./code_constructs/INCLUDE.md) + +# Limitations + +## Module type + +Related issue : +[issue #50](https://github.com/LexiFi/dead_code_analyzer/issues/50). + +As explained in the [Module type](./code_constructs/MODTYP.md) example, the +analyzer is currently restrcited to not reporting values declared in module +types. This means that any unused value defined by a module with a module type +as signature, even with constraints or substitutions, will not be reported. + +A future improvement would be to report unused exported values declared in +module types by considering all the values defined in modules of such types as +instances of the values in the module types. + +## Include module type with substitution + +Related issue : +[issue #64](https://github.com/LexiFi/dead_code_analyzer/issues/64). + +According to the above limitation on values in module types, values included +from a module type should not be reported. Even more so according to the +semantics described in the [Include](./code_constructs/INCLUDE.md) example. +This is the case unless there is a substitution on the module type. + +### Example + +The reference files for this example are in the +[sigincl](../examples/docs/exported_values/limitations/sigincl) directory. + +The reference takes place in `/tmp/docs/exported_values/limitations`, which +is a copy of the [limitations](../examples/docs/exported_values/limitations) +directory. Reported locations may differ depending on the location of the source +files. + +The compilation command is : +``` +make -C sigincl build +``` + +The analysis command is : +``` +make -C sigincl analyze +``` + +The compile + analyze command is : +``` +make -C sigincl +``` + +Code: +```OCaml +(* sigincl_lib.mli *) +module type T = sig + type t + val x : t +end + +module M : T + +module I : sig + include T with type t := unit +end +``` +```OCaml +(* sigincl_lib.ml *) +module type T = sig + type t + val x : t +end + +module M = struct + type t = unit + let x = () +end + +module I = M +``` + +Compile and analyze: +``` +$ make -C sigincl +make: Entering directory '/tmp/docs/exported_values/limitations/sigincl' +ocamlopt -bin-annot sigincl_lib.mli sigincl_lib.ml +dead_code_analyzer --nothing -E all . +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= +/tmp/docs/exported_values/limitations/sigincl/sigincl_lib.mli:10: I.x + +Nothing else to report in this section +-------------------------------------------------------------------------------- + + +make: Leaving directory '/tmp/docs/exported_values/limitations/sigincl' +``` + +The analyzer reports `I.x` at the line where `T` is included although it is +actually declared in `T`. + +## Including a module with the same name + +Related issue : +[issue #55](https://github.com/LexiFi/dead_code_analyzer/issues/55). + +According to the semantics described in the +[Include](./code_constructs/INCLUDE.md) example, values re-exported via included +should not be reported. However, it may happen that values re-exported by +including a compilation unit out of the current codebase are reported when the +re-exporting compilation unit has the same name as the one included. + +### Example + +The reference files for this example are in the +[incl\_same\_name](../examples/docs/exported_values/limitations/incl_same_name) +directory. + +The reference takes place in `/tmp/docs/exported_values/limitations`, which +is a copy of the [limitations](../examples/docs/exported_values/limitations) +directory. Reported locations may differ depending on the location of the source +files. + +The compilation command is : +``` +make -C incl_same_name build +``` + +The analysis command is : +``` +make -C incl_same_name analyze +``` + +The compile + analyze command is : +``` +make -C incl_same_name +``` + +Code: +```OCaml +(* oo.ml *) +include Stdlib.Oo +``` + +Compile and analyze: +``` +$ make -C incl_same_name +make: Entering directory '/tmp/docs/exported_values/limitations/incl_same_name' +ocamlopt -bin-annot oo.ml +dead_code_analyzer --nothing -E all . +Scanning files... + [DONE] + +.> UNUSED EXPORTED VALUES: +========================= +/tmp/docs/exported_values/limitations/incl_same_name/oo.mli:20: copy +/tmp/docs/exported_values/limitations/incl_same_name/oo.mli:25: id +/tmp/docs/exported_values/limitations/incl_same_name/oo.mli:40: new_method +/tmp/docs/exported_values/limitations/incl_same_name/oo.mli:41: public_method_label + +Nothing else to report in this section +-------------------------------------------------------------------------------- + + +make: Leaving directory '/tmp/docs/exported_values/limitations/incl_same_name' +``` + +The analyzer reports values in `oo.mli` although they are included from +`Stdlib.Oo`. diff --git a/docs/exported_values/code_constructs/INCLUDE.md b/docs/exported_values/code_constructs/INCLUDE.md index 32afa596..6465ab5b 100644 --- a/docs/exported_values/code_constructs/INCLUDE.md +++ b/docs/exported_values/code_constructs/INCLUDE.md @@ -85,7 +85,6 @@ Scanning files... .> 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 @@ -98,17 +97,6 @@ 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 diff --git a/examples/docs/exported_values/Makefile b/examples/docs/exported_values/Makefile index c452f3e9..64f975a2 100644 --- a/examples/docs/exported_values/Makefile +++ b/examples/docs/exported_values/Makefile @@ -5,9 +5,11 @@ all: build build: make -C hello_world make -C code_constructs + make -C limitations clean: rm -f *~ *.cm* *.o *.obj make -C hello_world clean make -C code_constructs clean + make -C limitations clean diff --git a/examples/docs/exported_values/limitations/Makefile b/examples/docs/exported_values/limitations/Makefile new file mode 100644 index 00000000..e54f6339 --- /dev/null +++ b/examples/docs/exported_values/limitations/Makefile @@ -0,0 +1,12 @@ +.PHONY: clean build + +all: build + +build: + make -C sigincl build + make -C incl_same_name build + +clean: + rm -f *~ *.cm* *.o *.obj + make -C sigincl clean + make -C incl_same_name clean diff --git a/examples/docs/exported_values/limitations/incl_same_name/Makefile b/examples/docs/exported_values/limitations/incl_same_name/Makefile new file mode 100644 index 00000000..cd91c72e --- /dev/null +++ b/examples/docs/exported_values/limitations/incl_same_name/Makefile @@ -0,0 +1,12 @@ +SRC:=oo.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/examples/docs/exported_values/limitations/incl_same_name/oo.ml b/examples/docs/exported_values/limitations/incl_same_name/oo.ml new file mode 100644 index 00000000..29e63421 --- /dev/null +++ b/examples/docs/exported_values/limitations/incl_same_name/oo.ml @@ -0,0 +1,2 @@ +(* oo.ml *) +include Stdlib.Oo diff --git a/examples/docs/exported_values/limitations/sigincl/Makefile b/examples/docs/exported_values/limitations/sigincl/Makefile new file mode 100644 index 00000000..faff1949 --- /dev/null +++ b/examples/docs/exported_values/limitations/sigincl/Makefile @@ -0,0 +1,12 @@ +SRC:=sigincl_lib.mli sigincl_lib.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/examples/docs/exported_values/limitations/sigincl/sigincl_lib.ml b/examples/docs/exported_values/limitations/sigincl/sigincl_lib.ml new file mode 100644 index 00000000..f30d1341 --- /dev/null +++ b/examples/docs/exported_values/limitations/sigincl/sigincl_lib.ml @@ -0,0 +1,12 @@ +(* sigincl_lib.ml *) +module type T = sig + type t + val x : t +end + +module M = struct + type t = unit + let x = () +end + +module I = M diff --git a/examples/docs/exported_values/limitations/sigincl/sigincl_lib.mli b/examples/docs/exported_values/limitations/sigincl/sigincl_lib.mli new file mode 100644 index 00000000..a0c32a3f --- /dev/null +++ b/examples/docs/exported_values/limitations/sigincl/sigincl_lib.mli @@ -0,0 +1,11 @@ +(* sigincl_lib.mli *) +module type T = sig + type t + val x : t +end + +module M : T + +module I : sig + include T with type t := unit +end diff --git a/src/deadCode.ml b/src/deadCode.ml index 76b26c85..a05a491b 100644 --- a/src/deadCode.ml +++ b/src/deadCode.ml @@ -31,19 +31,55 @@ let main_files = Hashtbl.create 256 (* names -> paths *) (******** PROCESSING ********) -let rec collect_export ?(mod_type = false) path u stock = function +type context = + | Toplevel + | In_module of (Ident.t * Location.t) + | In_modtyp of (Ident.t * Location.t) + | Include + +let should_export_value ~context stock loc = + let state = State.get_current () in + let belongs_to_context loc = + match context with + | Toplevel | Include -> true + | In_module (_, md_loc) + | In_modtyp (_, md_loc) -> + (* When a value is part of a module sig because of: + - an include, then its location precedes that of the current module; + - a module type with substitution, then its location ends + with the current module's sig. + Checking that the value's location is striclty within the + module's rules out these 2 cases. + *) + let get_pos_info loc = + let fname, start_l, start_c = + Location.get_pos_info loc.Location.loc_start + in + let _, end_l, end_c = Location.get_pos_info loc.loc_end in + fname, (start_l, start_c), (end_l, end_c) + in + let v_fname, v_start, v_end = get_pos_info loc in + let md_fname, md_start, md_end = get_pos_info md_loc in + let ( > ) (l1, c1) (l2, c2) = + l1 > l2 || (l1 = l2 && c1 > c2) + in + String.equal v_fname md_fname + && v_start > md_start + && md_end > v_end + in + Config.must_report_section state.config.sections.exported_values + && (* do not add the loc in decs if it belongs to a module type + or if it is not actually declared in the current context *) + ( stock != decs + || (not (Hashtbl.mem in_modtype loc.Location.loc_start) + && belongs_to_context loc) + ) + +let rec collect_export ~context path u stock = function | Sig_value (id, ({Types.val_loc; val_type; _} as value), _) when not val_loc.Location.loc_ghost -> - let state = State.get_current () in - let should_export stock loc = - Config.must_report_section state.config.sections.exported_values - && (* do not add the loc in decs if it belongs to a module type *) - ( stock != decs - || not (Hashtbl.mem in_modtype loc.Location.loc_start) - ) - in - if should_export stock val_loc then export path u stock id val_loc; + if should_export_value ~context stock val_loc then export path u stock id val_loc; let path = Ident.create_persistent (Ident.name id ^ "*") :: path in DeadObj.collect_export path u stock ~obj:val_type val_loc; !DeadLexiFi.sig_value value @@ -54,15 +90,16 @@ let rec collect_export ?(mod_type = false) path u stock = function | Sig_class (id, {Types.cty_type = t; cty_loc = loc; _}, _, _) -> DeadObj.collect_export (id :: path) u stock ~cltyp:t loc - | (Sig_module (id, _, {Types.md_type = t; _}, _, _) - | Sig_modtype (id, {Types.mtd_type = Some t; _}, _)) as s -> - let stock = - match s with - | Sig_modtype _ when not mod_type -> in_modtype - | _ -> stock + | (Sig_module (id, _, {Types.md_type = t; md_loc = loc; _}, _, _) + | Sig_modtype (id, {Types.mtd_type = Some t; mtd_loc = loc; _}, _)) as s -> + let stock, context = + match s, context with + | _, Include -> stock, Include + | Sig_modtype _, _ -> in_modtype, In_modtyp (id, loc) + | _, _ -> stock, In_module (id, loc) in DeadMod.sign t - |> List.iter (collect_export ~mod_type (id :: path) u stock) + |> List.iter (collect_export ~context (id :: path) u stock) | _ -> () @@ -148,7 +185,7 @@ let structure_item super self i = |> Ident.create_persistent in List.iter - (collect_export ~mod_type:true [module_id] _include incl) + (collect_export ~context:Include [module_id] _include incl) signature; last_loc := prev_last_loc; in @@ -365,7 +402,7 @@ let read_interface fn cmi_infos state = let open Cmi_format in |> Ident.create_persistent in let f = - collect_export [module_id] u decs + collect_export ~context:Toplevel [module_id] u decs in List.iter f cmi_infos.cmi_sign; last_loc := Lexing.dummy_pos @@ -479,9 +516,19 @@ let load_file fn state = let loc_dep = if Config.must_report_section state.config.sections.exported_values then - List.rev_map + let sourceunit = + State.File_infos.get_sourceunit state.file_infos + in + let in_sourceunit (pos : Lexing.position) = + String.equal (Utils.Filepath.unit pos.pos_fname) sourceunit + in + List.filter_map (fun (vd1, vd2) -> - (vd1.Types.val_loc.Location.loc_start, vd2.Types.val_loc.Location.loc_start) + let loc1 = vd1.Types.val_loc.Location.loc_start in + let loc2 = vd2.Types.val_loc.Location.loc_start in + if in_sourceunit loc1 || in_sourceunit loc2 then + Some (loc1, loc2) + else None ) cmt_value_dependencies else []