zf

zenflows testing
git clone https://s.sonu.ch/~srfsh/zf.git
Log | Files | Refs | Submodules | README | LICENSE

forbidden_module.ex (3023B)


      1 defmodule Credo.Check.Warning.ForbiddenModule do
      2   use Credo.Check,
      3     base_priority: :high,
      4     category: :warning,
      5     param_defaults: [modules: []],
      6     explanations: [
      7       check: """
      8       Some modules that are included by a package may be hazardous
      9       if used by your application. Use this check to allow these modules in
     10       your dependencies but forbid them to be used in your application.
     11 
     12       Examples:
     13 
     14       The `:ecto_sql` package includes the `Ecto.Adapters.SQL` module,
     15       but direct usage of the `Ecto.Adapters.SQL.query/4` function, and related functions, may
     16       cause issues when using Ecto's dynamic repositories.
     17       """,
     18       params: [
     19         modules: "List of modules or `{Module, \"Error message\"}` tuples that must not be used."
     20       ]
     21     ]
     22 
     23   alias Credo.Code.Name
     24 
     25   @impl Credo.Check
     26   def run(%SourceFile{} = source_file, params) do
     27     modules = Params.get(params, :modules, __MODULE__)
     28 
     29     modules =
     30       if Keyword.keyword?(modules) do
     31         Enum.map(modules, fn {key, value} -> {Name.full(key), value} end)
     32       else
     33         Enum.map(modules, fn key -> {Name.full(key), nil} end)
     34       end
     35 
     36     Credo.Code.prewalk(
     37       source_file,
     38       &traverse(&1, &2, modules, IssueMeta.for(source_file, params))
     39     )
     40   end
     41 
     42   defp traverse({:__aliases__, meta, modules} = ast, issues, forbidden_modules, issue_meta) do
     43     module = Name.full(modules)
     44 
     45     issues = put_issue_if_forbidden(issues, issue_meta, meta[:line], module, forbidden_modules)
     46 
     47     {ast, issues}
     48   end
     49 
     50   defp traverse(
     51          {:alias, _meta, [{{_, _, [{:__aliases__, _opts, base_alias}, :{}]}, _, aliases}]} = ast,
     52          issues,
     53          forbidden_modules,
     54          issue_meta
     55        ) do
     56     modules =
     57       Enum.map(aliases, fn {:__aliases__, meta, module} ->
     58         {Name.full([base_alias, module]), meta[:line]}
     59       end)
     60 
     61     issues =
     62       Enum.reduce(modules, issues, fn {module, line}, issues ->
     63         put_issue_if_forbidden(issues, issue_meta, line, module, forbidden_modules)
     64       end)
     65 
     66     {ast, issues}
     67   end
     68 
     69   defp traverse(ast, issues, _, _), do: {ast, issues}
     70 
     71   defp put_issue_if_forbidden(issues, issue_meta, line_no, module, forbidden_modules) do
     72     forbidden_module_names = Enum.map(forbidden_modules, &elem(&1, 0))
     73 
     74     if found_module?(forbidden_module_names, module) do
     75       [issue_for(issue_meta, line_no, module, forbidden_modules) | issues]
     76     else
     77       issues
     78     end
     79   end
     80 
     81   defp found_module?(forbidden_module_names, module) do
     82     Enum.member?(forbidden_module_names, module)
     83   end
     84 
     85   defp issue_for(issue_meta, line_no, module, forbidden_modules) do
     86     trigger = Name.full(module)
     87     message = message(forbidden_modules, module) || "The `#{trigger}` module is not allowed."
     88 
     89     format_issue(
     90       issue_meta,
     91       message: message,
     92       trigger: trigger,
     93       line_no: line_no
     94     )
     95   end
     96 
     97   defp message(forbidden_modules, module) do
     98     Enum.find_value(forbidden_modules, fn
     99       {^module, message} -> message
    100       _ -> nil
    101     end)
    102   end
    103 end