Multi-version support - #100
Open
fantazio wants to merge 16 commits into
Open
Conversation
This relies on cppo. Compilation issues are naively fixed. No error is introduced in the tests. The project-configuration files (dune, opam, ci) have been updated.
This relies on cppo. Compilation issues are naively fixed. FP (and corresponding FN) are introduced in the tests. The project-configuration files (dune, opam, ci) have been updated.
…d as `Tpoly` Since OCaml 5.5 function arguments types must be `Tpoly` nodes. Monoporphic arguments are therefore represented as a `Tpoly` with an empty list of names. When looking for the type description, we always want to this pattern as the underlying monoporhic type. This fixes the FP/FN introduced in the previous commit (adaptation to OCaml 5.5).
This relies on cppo. Compilation issues are naively fixed. No error is introduced in the tests. The project-configuration files (dune, opam, ci) have been updated.
fantazio
force-pushed
the
cppo_54
branch
3 times, most recently
from
September 3, 2026 16:20
883efc7 to
ce7aac9
Compare
This relies on cppo. Compilation issues are naively fixed. No error is introduced in the tests. The project-configuration files (dune, opam, ci) have been updated.
This relies on cppo. Compilation issues are naively fixed. No error is introduced in the tests. The project-configuration files (dune, opam, ci) have been updated.
This relies on cppo. Compilation issues are naively fixed. No error is introduced in the tests. The project-configuration files (dune, opam, ci) have been updated.
The `let (x : t) = ...` case produces a different tree from `let x = ...`. The former is translated to `let ((_ : t) as x) = ...`. The particular case of `let x : t = ...` is either equivalent to the former (from OCaml 5.1 to 5.4) or the latter. The former is equivalent to the latter in OCaml 5.5.
Identify `let (_ as x) = ... in x` as a useless binding. This pattern is created by e.g. `let (x : t) = ... in x`.
Notable issues are: - Multiline lhs with multiple arguments lead to mismatch locations as demonstrated by `multiline_opt_arg_in_arg` and; `implicit_multiline_opt_arg_in_arg` - If the arg is itself an opt arg then we only detect the style issue in OCaml >= 5.2 and if a default value is provided (example `opt_arg_in_opt_arg_val`). There is also a non-stylistic opt arg issue related to implicit optional arguments (demonstrated by the `implicit_*` functions): although the optional arguments are never explicited, they are implied by the use of the function as argument. Those optional arguments exist because they are used by requirement, making the reports tautological.
This fixes the 2 style issues identified in the previous commit: - Because `Texp_function`'s representation changed in OCaml 5.2, rather than using its location, we need to use the params' locs (which were unavailable in prior versions); - The types of opt args are always wrapped in an `option` (even if a default valuei is provided), making them `Tconstr`. In order to properly check the "opt arg in arg" style on opt args, we need to extract the underlying type from the `Tconstr`.
If there is an optional argument with default value before the faulty arg, then the style issue is not detected in OCaml < 5.2.
In the case where there is an opt arg with a default value before the faulty arg, in OCaml < 5.2, an internmediate `let ... in` is intoduced to set the value of the opt arg before the rest of the `function`.
Constrained vars are misunderstood patterns in OCaml < 5.5, leading to `!!pattern!!` in the reports instead of a name, and even FP in case the name was prefixed by an underscore.
Identify `((_ : unit) as x)` as a unit binding. This pattern is created by e.g. `(x : unit)`
The same heavy pattern matching to extract the id of a `Tpat_var` (or `Tpat_alias (Tpat_any)`) was used in 3 different places. This is now done in `id_of_var` which returns an option.
fantazio
marked this pull request as ready for review
September 7, 2026 10:32
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fix #44, #45, #89
The dead_code_analyzer only supported one OCaml version at a time. Updating to a more recent version of OCaml meant losing support for the previous version. This PR adds multi-version support by relying on cppo.
The supported versions range from OCaml 4.14 to OCaml 5.5.
The project-configuration files (dune, opam, ci) have been updated. We may reduce the list of tested releases in the future, to reduce CI cost.
Tests
All the tests are passing.
New tests have been added to illustrate the fixed inconsistencies in the coding style section (observed on Frama-C and Opam).
The remaining inconsistencies are documented in #103. They mostly affect optional arguments.
A new false positive (6 occurences in the tests) is unveiled by the new tests. It is documented as the "tautological case" in #102.
Performance benchmarks on Frama-C and Opam are coherent.
On cppo
The choice of cppo was made because it is the most widely used preprocessor for specifically this category of task (version-dependent code).
The bounds in the macro conditionals are explicit : min bound and max bound are always stated, even when they are 4.14 or 5.6. The intent is for avoid having to remember which versions are supported each time one would read half bounded (min or max only) code.
Alternatively, ppx_optcomp was considered because, being a ppx, it would be more tooling-friendly. However, it comes with base as a dependency (although that should change), and I'd like to keep the project's dependencies minimal.
Spoiler: I am building an alternative for compiler-libs dependent code.
Project vaast aims at providing abstractions of compiler-libs components (such as the Typedtree), which would move the compiler version-dependency information from the preprocessor to the type checker. Thus, user code would remain tool-friendly, avoid the unsafety and code complexity that comes with cppo, and be easier to adapt to new OCaml versions.
There should be a first release to open design discussions with concerned actors soon™