config_comment.ex (3731B)
1 defmodule Credo.Check.ConfigComment do 2 @moduledoc """ 3 `ConfigComment` structs represent comments which follow control Credo's behaviour. 4 5 The following comments are supported: 6 7 # credo:disable-for-this-file 8 # credo:disable-for-next-line 9 # credo:disable-for-previous-line 10 # credo:disable-for-lines:<number> 11 12 """ 13 14 @instruction_disable_file "disable-for-this-file" 15 @instruction_disable_next_line "disable-for-next-line" 16 @instruction_disable_previous_line "disable-for-previous-line" 17 @instruction_disable_lines "disable-for-lines" 18 19 alias Credo.Issue 20 21 defstruct line_no: nil, 22 line_no_end: nil, 23 instruction: nil, 24 params: nil 25 26 @doc "Returns a `ConfigComment` struct based on the given parameters." 27 def new(instruction, param_string, line_no) 28 29 def new("#{@instruction_disable_lines}:" <> line_count, param_string, line_no) do 30 line_count = String.to_integer(line_count) 31 32 params = 33 param_string 34 |> value_for() 35 |> List.wrap() 36 37 if line_count >= 0 do 38 %__MODULE__{ 39 line_no: line_no, 40 line_no_end: line_no + line_count, 41 instruction: @instruction_disable_lines, 42 params: params 43 } 44 else 45 %__MODULE__{ 46 line_no: line_no + line_count, 47 line_no_end: line_no, 48 instruction: @instruction_disable_lines, 49 params: params 50 } 51 end 52 end 53 54 def new(instruction, param_string, line_no) do 55 %__MODULE__{ 56 line_no: line_no, 57 instruction: instruction, 58 params: param_string |> value_for() |> List.wrap() 59 } 60 end 61 62 @doc "Returns `true` if the given `issue` should be ignored based on the given `config_comment`" 63 def ignores_issue?(config_comment, issue) 64 65 def ignores_issue?( 66 %__MODULE__{instruction: @instruction_disable_file, params: params}, 67 %Issue{} = issue 68 ) do 69 params_ignore_issue?(params, issue) 70 end 71 72 def ignores_issue?( 73 %__MODULE__{ 74 instruction: @instruction_disable_next_line, 75 line_no: line_no, 76 params: params 77 }, 78 %Issue{line_no: line_no_issue} = issue 79 ) 80 when line_no_issue == line_no + 1 do 81 params_ignore_issue?(params, issue) 82 end 83 84 def ignores_issue?( 85 %__MODULE__{ 86 instruction: @instruction_disable_previous_line, 87 line_no: line_no, 88 params: params 89 }, 90 %Issue{line_no: line_no_issue} = issue 91 ) 92 when line_no_issue == line_no - 1 do 93 params_ignore_issue?(params, issue) 94 end 95 96 def ignores_issue?( 97 %__MODULE__{ 98 instruction: @instruction_disable_lines, 99 line_no: line_no_start, 100 line_no_end: line_no_end, 101 params: params 102 }, 103 %Issue{line_no: line_no_issue} = issue 104 ) 105 when line_no_issue >= line_no_start and line_no_issue <= line_no_end do 106 params_ignore_issue?(params, issue) 107 end 108 109 def ignores_issue?(_, _) do 110 false 111 end 112 113 # 114 115 defp params_ignore_issue?([], _issue) do 116 true 117 end 118 119 defp params_ignore_issue?(params, issue) when is_list(params) do 120 Enum.any?(params, &check_tuple_ignores_issue?(&1, issue)) 121 end 122 123 defp check_tuple_ignores_issue?(check_or_regex, issue) do 124 if Regex.regex?(check_or_regex) do 125 issue.check 126 |> to_string 127 |> String.match?(check_or_regex) 128 else 129 issue.check == check_or_regex 130 end 131 end 132 133 defp value_for(""), do: nil 134 135 defp value_for(param_string) do 136 if regex_value?(param_string) do 137 param_string 138 |> String.slice(1..-2) 139 |> Regex.compile!("i") 140 else 141 String.to_atom("Elixir.#{param_string}") 142 end 143 end 144 145 defp regex_value?(param_string), do: param_string =~ ~r'^/.+/$' 146 end