Bug report from Claude Opus 4.6 below:
Bug: (dune (not (has_field lint))) fails to auto-fix, while (dune (library (not (has_field lint)))) works
Description
When using (not (has_field <field>)) as a shorthand at the dune predicate level, dunolint reports an enforce failure with no automatic fix available, even though the documentation states that negation of has_field should suggest removing the field.
Steps to reproduce
Given a library stanza with a lint field:
(library
(name catch_the_bunny_stdlib)
(public_name catch-the-bunny.stdlib)
(flags :standard -w +a-4-40-41-42-44-45-48-66 -warn-error +a)
(libraries dyn ordering pp)
(instrumentation
(backend ppx_windtrap))
(lint (pps))
(preprocess no_preprocessing))
Using the rule (dune (not (has_field lint))) produces:
Error: Enforce Failure.
The following condition does not hold: (not (has_field lint))
Dunolint is able to suggest automatic modifications to satisfy linting rules
when a strategy is implemented, however in this case there is none available.
Hint: You need to attend and fix manually.
Workaround
Wrapping the condition in library (...) works correctly:
(dune (library (not (has_field lint))))
This correctly suggests removing the lint field.
Root cause and implementation notes
In library.ml (and executable.ml), the outer Linter.enforce function handles all Not cases with a blanket Not _ -> Eval fallthrough, which bypasses the inner Top.enforce that correctly handles Not (has_field _) by removing the field.
The T (positive) branch correctly delegates shorthand predicates like T (has_field _)toTop.enforce, but the corresponding Notbranch does not. This affects allhas_field variants (lint, instrumentation, preprocess, name, public_name) when used as shorthands at the dune` predicate level.
Bug report from Claude Opus 4.6 below:
Bug: (dune (not (has_field lint))) fails to auto-fix, while (dune (library (not (has_field lint)))) works
Description
When using
(not (has_field <field>))as a shorthand at the dune predicate level, dunolint reports an enforce failure with no automatic fix available, even though the documentation states that negation of has_field should suggest removing the field.Steps to reproduce
Given a library stanza with a lint field:
(library (name catch_the_bunny_stdlib) (public_name catch-the-bunny.stdlib) (flags :standard -w +a-4-40-41-42-44-45-48-66 -warn-error +a) (libraries dyn ordering pp) (instrumentation (backend ppx_windtrap)) (lint (pps)) (preprocess no_preprocessing))Using the rule
(dune (not (has_field lint)))produces:Workaround
Wrapping the condition in library (...) works correctly:
(dune (library (not (has_field lint))))This correctly suggests removing the lint field.
Root cause and implementation notes
In library.ml (and executable.ml), the outer
Linter.enforcefunction handles all Not cases with a blanket Not _ -> Eval fallthrough, which bypasses the inner Top.enforce that correctly handles Not (has_field _) by removing the field.The T (positive) branch correctly delegates shorthand predicates like T (has_field _)toTop.enforce, but the corresponding Notbranch does not. This affects allhas_field variants (lint, instrumentation, preprocess, name, public_name) when used as shorthands at the dune` predicate level.