Repository: rrrene/credo
Pull request #1165 (2024-11-28 21:16:20)

Credo.Check format_issue opts allow a custom category
### What am I doing?

Introducing support for a custom `category` option in the `format_issue` function, so we can overwrite the category given by Credo.Check `use` spec.

### Why?

This allows defining specific categories as part of the same Credo.Check, which it can be useful for uses cases where the "searching" algorithm is used for related checks, but with different categories and/or notification levels. For instance, a Credo.Check (*1) I have been implementing recently: it searches for soft-deprecated and forbidden functions (while having `--warning-as-error` enabled in our CI pipelines, so `@deprecated` cannot be used for soft ones):

- Informational Check: Used for recommendations "avoid if possible, port it when you have time", categorized separately (e.g., `:soft_deprecated`, `recommendation`, `advice`, etc), and does not block CI pipelines (exit status 0). It can suggest alternative functions (when provided), and the owners of that region of code can update whenever they have time. Soft-deprecating functions take time to update all usages, and in most cases it requires cross-squad efforts, so, better to warn without failing and leave enough time for the developers to update their code (and even indirect usages, like external services that depend on the original functions, like grafana, datadog, new relic, etc).
- Warning Check: Issues a warning and blocks the CI pipeline (non-zero exit status). The main function and category of the checker, "avoid always and right now".

By combining these checks under one implementation, we optimize performance and reduce code duplication, as 90% of the logic is shared. However, the checks remain distinct in their categories and purposes, balancing performance and maintainability against separation of concerns and clarity.

Original proposal and context in [this PR that fixes the exit_status](https://github.com/rrrene/credo/pull/1162).

Let me know if further refinements are needed!

*1 Note: Let me know if you would be interested in that Credo.Check and having it as one of the default ones in credo. I will need more time to have it polished and as fast as possible, but I'll happy to do so this winter - early 2025 ;) 


Patch:
diff --git a/lib/credo/check.ex b/lib/credo/check.ex
index 846a84f9..d5331294 100644
--- a/lib/credo/check.ex
+++ b/lib/credo/check.ex
@@ -691,14 +691,13 @@ defmodule Credo.Check do
   - `:column`       Sets the issue's column.
   - `:exit_status`  Sets the issue's exit_status.
   - `:severity`     Sets the issue's severity.
+  - `:category`     Sets the issue's category.
   """
   def format_issue(issue_meta, opts, check) do
     source_file = IssueMeta.source_file(issue_meta)
     params = IssueMeta.params(issue_meta)
-    issue_category = Params.category(params, check)
-    issue_base_priority = Params.priority(params, check)
-
-    priority = Priority.to_integer(issue_base_priority)
+    issue_category = opts[:category] || Params.category(params, check)
+    priority = params |> Params.priority(check) |> Priority.to_integer()
 
     exit_status_or_category =
       opts[:exit_status] || Params.exit_status(params, check) || issue_category


Test patch:
diff --git a/test/credo/check_test.exs b/test/credo/check_test.exs
index 6d82ca68..e711ccc0 100644
--- a/test/credo/check_test.exs
+++ b/test/credo/check_test.exs
@@ -66,7 +66,8 @@ defmodule Credo.CheckTest do
           line_no: 3,
           column: 15,
           exit_status: 23,
-          severity: 11
+          severity: 11,
+          category: :custom_category
         )
       ]
     end
@@ -89,6 +90,7 @@ defmodule Credo.CheckTest do
       assert issue.column == 15
       assert issue.exit_status == 23
       assert issue.severity == 11
+      assert issue.category == :custom_category
     end)
   end
 end