diff --git a/infer/src/base/Config.ml b/infer/src/base/Config.ml index 6ad610d3e..abd228898 100644 --- a/infer/src/base/Config.ml +++ b/infer/src/base/Config.ml @@ -1184,7 +1184,14 @@ and () = let (_ : string list ref) = CLOpt.mk_string_list ?deprecated ~long ~f:(fun issue_id -> - let issue = IssueType.register_from_string ~id:issue_id Linters in + let issue = + match IssueType.find_from_string ~id:issue_id with + | Some issue -> + issue + | None -> + (* unknown issue type: assume it will be defined in AL *) + IssueType.register_from_string ~id:issue_id Linters + in IssueType.set_enabled issue b ; issue_id ) ?default ~meta:"issue_type" diff --git a/infer/src/base/IssueType.ml b/infer/src/base/IssueType.ml index 427256cf1..6c84731d8 100644 --- a/infer/src/base/IssueType.ml +++ b/infer/src/base/IssueType.ml @@ -7,6 +7,7 @@ open! IStd module F = Format +module L = Die (* Make sure we cannot create new issue types other than by calling [register_from_string]. This is because we want to keep track of the list of all the issues ever declared. *) @@ -24,6 +25,8 @@ module Unsafe : sig val pp : F.formatter -> t -> unit + val find_from_string : id:string -> t option + val register_from_string : ?enabled:bool -> ?hum:string @@ -77,6 +80,8 @@ end = struct let set_enabled issue b = issue.enabled <- b + let find_from_string ~id:unique_id = IssueSet.find_rank !all_issues unique_id + (** Avoid creating new issue types. The idea is that there are three types of issue types: + Statically pre-defined issue types, namely the ones in this module @@ -90,10 +95,21 @@ end = struct of the issue type, eg in AL. *) let register_from_string ?(enabled = true) ?hum:hum0 ?doc_url ?linters_def_file ~id:unique_id checker = - match IssueSet.find_rank !all_issues unique_id with - | Some issue -> - (* update human-readable string in case it was supplied this time, but keep the previous - value of enabled (see doc comment) *) + match find_from_string ~id:unique_id with + | ((Some + ( { unique_id= _ (* we know it has to be the same *) + ; checker= checker_old + ; enabled= _ (* not touching this one since [Config] will have set it *) + ; hum= _ (* mutable field to update *) + ; doc_url= _ (* mutable field to update *) + ; linters_def_file= _ (* mutable field to update *) } as issue ))[@warning "+9"]) -> + (* update fields that were supplied this time around, but keep the previous values of others + and assert that the immutable fields are the same (see doc comment) *) + if not (Checker.equal checker checker_old) then + L.die InternalError + "Checker definition for issue \"%s\" doesn't match: found new checker \"%s\" but \ + checker \"%s\" was already registered for this issue type" + unique_id (Checker.get_name checker) (Checker.get_name checker_old) ; Option.iter hum0 ~f:(fun hum -> issue.hum <- hum) ; if Option.is_some doc_url then issue.doc_url <- doc_url ; if Option.is_some linters_def_file then issue.linters_def_file <- linters_def_file ; diff --git a/infer/src/base/IssueType.mli b/infer/src/base/IssueType.mli index f2445dfc3..894eebf72 100644 --- a/infer/src/base/IssueType.mli +++ b/infer/src/base/IssueType.mli @@ -24,6 +24,9 @@ val all_issues : unit -> t list val pp : Format.formatter -> t -> unit (** pretty print a localised string *) +val find_from_string : id:string -> t option +(** return the issue type if it was previously registered *) + val register_from_string : ?enabled:bool -> ?hum:string