cryptosense / ocamllint

Detect common errors in OCaml code
BSD 2-Clause "Simplified" License
68 stars 3 forks source link

Configuration system to enable/disable rules #2

Closed emillon closed 8 years ago

emillon commented 8 years ago

A linting tool should have sensible defaults and configurable rules, since it's meant to enforce rules, not prescribe them.

There are a couple problems that need to be solved:

I think that a simple way to do that is to expose an API and add a mechanism to load a module with the deriver. This is similar to how ppx_deriving modules are written. One would create a Ocamllint_config module with the following contents and pass -package ocamllint.ppx_lint -ppxopt ocamllint.ppx_lint,ocamllint_config.cma:

let _ =
    let open Ocamllint.Config in
    ignore Comparison_to_boolean;
    error (Partial_function "List.hd")
gasche commented 8 years ago

I would encourage you to consider a declarative model where, instead of having a main program and plugins that perform imperative mutations to rule bases, you provide a functional API to build rulesets, plus a function to "become a linter" from a given ruleset. This requires good API design, but if done well this can be almost as convenient as what you propose -- in terms of amount of code for end-users to write -- with a naturally more robust/predictable semantics.

You can easily build a more imperative interface on top of a functional API: add a global reference to the "current ruleset" and define ignore/error/... that mutate it. On the other hand, carving a functional API out of an imperative plugin interface after the fact is very hard.

emillon commented 8 years ago

Do you mean something like xmonad's config system? That would look like the following:

let () =
    let config = { default_config with rate_expr = default_config.rate_expr <|> custom_rate_expr } in
    let mapper = Ppx_lint.lint_mapper config in
    register "custom_lint" mapper

It seems to be a good idea but API design will be tricky indeed.

gasche commented 8 years ago

Yes, the Xmonad's configuration system works like this.

Some domain problem types may be the following:

Then a standard ocamllint.ml file could be something like

module R = Lint_ruleset
module Std = Lint_standard_rules

let rules =
  Std.default_rules
  |> R.union Lint_cryptosense.default_rules
  |> R.add Std.(partial_function "List.hd")
  |> R.remove Std.comparison_to_boolean

let () = Ocamllint_run.lint Ocamllint_config.default rules

Then ocamlbuild -use-ocamlfind -package lint -package lint-cryptosense ocamllint.native -- . would compile the linter and run it with the current directory (.) as only command-line argument.