block.ex (6027B)
1 defmodule Credo.Code.Block do 2 @moduledoc """ 3 This module provides helper functions to analyse blocks, e.g. the block taken 4 by the `if` macro. 5 """ 6 7 @doc """ 8 Returns the do: block of a given AST node. 9 """ 10 def all_blocks_for!(ast) do 11 [ 12 do_block_for!(ast), 13 else_block_for!(ast), 14 rescue_block_for!(ast), 15 after_block_for!(ast) 16 ] 17 end 18 19 @doc """ 20 Returns true if the given `ast` has a do block. 21 """ 22 def do_block?(ast) do 23 case do_block_for(ast) do 24 {:ok, _block} -> 25 true 26 27 nil -> 28 false 29 end 30 end 31 32 @doc """ 33 Returns the do: block of a given AST node. 34 """ 35 def do_block_for!(ast) do 36 case do_block_for(ast) do 37 {:ok, block} -> 38 block 39 40 nil -> 41 nil 42 end 43 end 44 45 @doc """ 46 Returns a tuple {:ok, do_block} or nil for a given AST node. 47 """ 48 def do_block_for({_atom, _meta, arguments}) when is_list(arguments) do 49 do_block_for(arguments) 50 end 51 52 def do_block_for(do: block) do 53 {:ok, block} 54 end 55 56 def do_block_for(arguments) when is_list(arguments) do 57 Enum.find_value(arguments, &find_keyword(&1, :do)) 58 end 59 60 def do_block_for(_) do 61 nil 62 end 63 64 @doc """ 65 Returns true if the given `ast` has an else block. 66 """ 67 def else_block?(ast) do 68 case else_block_for(ast) do 69 {:ok, _block} -> 70 true 71 72 nil -> 73 false 74 end 75 end 76 77 @doc """ 78 Returns the `else` block of a given AST node. 79 """ 80 def else_block_for!(ast) do 81 case else_block_for(ast) do 82 {:ok, block} -> 83 block 84 85 nil -> 86 nil 87 end 88 end 89 90 @doc """ 91 Returns a tuple {:ok, else_block} or nil for a given AST node. 92 """ 93 def else_block_for({_atom, _meta, arguments}) when is_list(arguments) do 94 else_block_for(arguments) 95 end 96 97 def else_block_for(do: _do_block, else: else_block) do 98 {:ok, else_block} 99 end 100 101 def else_block_for(arguments) when is_list(arguments) do 102 Enum.find_value(arguments, &find_keyword(&1, :else)) 103 end 104 105 def else_block_for(_) do 106 nil 107 end 108 109 @doc """ 110 Returns true if the given `ast` has an rescue block. 111 """ 112 def rescue_block?(ast) do 113 case rescue_block_for(ast) do 114 {:ok, _block} -> 115 true 116 117 nil -> 118 false 119 end 120 end 121 122 @doc """ 123 Returns the rescue: block of a given AST node. 124 """ 125 def rescue_block_for!(ast) do 126 case rescue_block_for(ast) do 127 {:ok, block} -> 128 block 129 130 nil -> 131 nil 132 end 133 end 134 135 @doc """ 136 Returns a tuple {:ok, rescue_block} or nil for a given AST node. 137 """ 138 def rescue_block_for({_atom, _meta, arguments}) when is_list(arguments) do 139 rescue_block_for(arguments) 140 end 141 142 def rescue_block_for(do: _do_block, rescue: rescue_block) do 143 {:ok, rescue_block} 144 end 145 146 def rescue_block_for(arguments) when is_list(arguments) do 147 Enum.find_value(arguments, &find_keyword(&1, :rescue)) 148 end 149 150 def rescue_block_for(_) do 151 nil 152 end 153 154 @doc """ 155 Returns true if the given `ast` has an catch block. 156 """ 157 def catch_block?(ast) do 158 case catch_block_for(ast) do 159 {:ok, _block} -> 160 true 161 162 nil -> 163 false 164 end 165 end 166 167 @doc """ 168 Returns the catch: block of a given AST node. 169 """ 170 def catch_block_for!(ast) do 171 case catch_block_for(ast) do 172 {:ok, block} -> 173 block 174 175 nil -> 176 nil 177 end 178 end 179 180 @doc """ 181 Returns a tuple {:ok, catch_block} or nil for a given AST node. 182 """ 183 def catch_block_for({_atom, _meta, arguments}) when is_list(arguments) do 184 catch_block_for(arguments) 185 end 186 187 def catch_block_for(do: _do_block, catch: catch_block) do 188 {:ok, catch_block} 189 end 190 191 def catch_block_for(arguments) when is_list(arguments) do 192 Enum.find_value(arguments, &find_keyword(&1, :catch)) 193 end 194 195 def catch_block_for(_) do 196 nil 197 end 198 199 @doc """ 200 Returns true if the given `ast` has an after block. 201 """ 202 def after_block?(ast) do 203 case after_block_for(ast) do 204 {:ok, _block} -> 205 true 206 207 nil -> 208 false 209 end 210 end 211 212 @doc """ 213 Returns the after: block of a given AST node. 214 """ 215 def after_block_for!(ast) do 216 case after_block_for(ast) do 217 {:ok, block} -> 218 block 219 220 nil -> 221 nil 222 end 223 end 224 225 @doc """ 226 Returns a tuple {:ok, after_block} or nil for a given AST node. 227 """ 228 def after_block_for({_atom, _meta, arguments}) when is_list(arguments) do 229 after_block_for(arguments) 230 end 231 232 def after_block_for(do: _do_block, after: after_block) do 233 {:ok, after_block} 234 end 235 236 def after_block_for(arguments) when is_list(arguments) do 237 Enum.find_value(arguments, &find_keyword(&1, :after)) 238 end 239 240 def after_block_for(_) do 241 nil 242 end 243 244 defp find_keyword(list, keyword) when is_list(list) do 245 if Keyword.has_key?(list, keyword) do 246 {:ok, list[keyword]} 247 else 248 nil 249 end 250 end 251 252 defp find_keyword(_, _), do: nil 253 254 @doc """ 255 Returns the children of the `do` block of the given AST node. 256 """ 257 def calls_in_do_block({_op, _meta, arguments}) do 258 arguments 259 |> do_block_for! 260 |> instructions_for 261 end 262 263 def calls_in_do_block(arg) do 264 arg 265 |> do_block_for! 266 |> instructions_for 267 end 268 269 @doc """ 270 Returns the children of the `rescue` block of the given AST node. 271 """ 272 def calls_in_rescue_block({_op, _meta, arguments}) do 273 arguments 274 |> rescue_block_for! 275 |> instructions_for 276 end 277 278 def calls_in_rescue_block(arg) do 279 arg 280 |> rescue_block_for! 281 |> instructions_for 282 end 283 284 @doc """ 285 Returns the children of the `catch` block of the given AST node. 286 """ 287 def calls_in_catch_block({_op, _meta, arguments}) do 288 arguments 289 |> catch_block_for! 290 |> instructions_for 291 end 292 293 def calls_in_catch_block(arg) do 294 arg 295 |> catch_block_for! 296 |> instructions_for 297 end 298 299 defp instructions_for({:__block__, _meta, calls}), do: calls 300 301 defp instructions_for(v) 302 when is_atom(v) or is_tuple(v) or is_binary(v) or is_float(v) or is_integer(v), 303 do: List.wrap(v) 304 305 defp instructions_for(v) when is_list(v), do: [v] 306 end