mix_env.ex (2577B)
1 defmodule Credo.Check.Warning.MixEnv do 2 use Credo.Check, 3 base_priority: :high, 4 param_defaults: [excluded_paths: []], 5 explanations: [ 6 check: """ 7 Mix is a build tool and, as such, it is not expected to be available in production. 8 Therefore, it is recommended to access Mix.env only in configuration files and inside 9 mix.exs, never in your application code (lib). 10 11 (from the Elixir docs) 12 """, 13 params: [ 14 excluded_paths: "List of paths or regex to exclude from this check" 15 ] 16 ] 17 18 alias Credo.SourceFile 19 20 @call_string "Mix.env" 21 @def_ops [:def, :defp, :defmacro] 22 23 @doc false 24 def run(%SourceFile{filename: filename} = source_file, params \\ []) do 25 excluded_paths = Params.get(params, :excluded_paths, __MODULE__) 26 27 case ignore_path?(source_file.filename, excluded_paths) do 28 true -> 29 [] 30 31 false -> 32 issue_meta = IssueMeta.for(source_file, params) 33 34 filename 35 |> Path.extname() 36 |> case do 37 ".exs" -> [] 38 _ -> Credo.Code.prewalk(source_file, &traverse(&1, &2, issue_meta)) 39 end 40 end 41 end 42 43 # Check if analyzed module path is within ignored paths 44 defp ignore_path?(filename, excluded_paths) do 45 directory = Path.dirname(filename) 46 47 Enum.any?(excluded_paths, &matches?(directory, &1)) 48 end 49 50 defp matches?(directory, %Regex{} = regex), do: Regex.match?(regex, directory) 51 defp matches?(directory, path) when is_binary(path), do: String.starts_with?(directory, path) 52 53 for op <- @def_ops do 54 # catch variables named e.g. `defp` 55 defp traverse({unquote(op), _, nil} = ast, issues, _issue_meta, _parens?) do 56 {ast, issues} 57 end 58 59 defp traverse({unquote(op), _, _body} = ast, issues, issue_meta) do 60 {ast, issues ++ Credo.Code.prewalk(ast, &traverse_defs(&1, &2, issue_meta))} 61 end 62 end 63 64 defp traverse(ast, issues, _issue_meta) do 65 {ast, issues} 66 end 67 68 defp traverse_defs( 69 {{:., _, [{:__aliases__, _, [:Mix]}, :env]}, meta, _arguments} = ast, 70 issues, 71 issue_meta 72 ) do 73 {ast, issues_for_call(meta, issues, issue_meta)} 74 end 75 76 defp traverse_defs(ast, issues, _issue_meta) do 77 {ast, issues} 78 end 79 80 defp issues_for_call(meta, issues, issue_meta) do 81 [issue_for(issue_meta, meta[:line], @call_string) | issues] 82 end 83 84 defp issue_for(issue_meta, line_no, trigger) do 85 format_issue( 86 issue_meta, 87 message: "There should be no calls to Mix.env in application code.", 88 trigger: trigger, 89 line_no: line_no 90 ) 91 end 92 end