Packages
styler
1.4.2
1.11.0
1.10.1
1.10.0
1.9.1
1.9.0
1.8.0
1.7.0
1.6.0
1.5.1
1.5.0
1.4.2
1.4.1
1.4.0
1.3.3
1.3.2
1.3.1
1.3.0
1.2.1
1.2.0
1.1.2
1.1.1
1.1.0
1.0.0
1.0.0-rc.2
1.0.0-rc.1
1.0.0-rc.0
1.0.0-alpha.0
0.11.9
0.11.8
0.11.7
0.11.6
0.11.5
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.5
0.10.4
retired
0.10.3
0.10.2
0.10.1
0.10.0
0.9.7
0.9.6
0.9.5
0.9.4
0.9.3
0.9.2
retired
0.9.1
retired
0.9.0
0.8.5
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.14
0.7.13
0.7.12
0.7.11
0.7.10
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.1
0.4.0
0.3.1
0.3.0
0.2.0
0.1.1
0.1.0
A code-style enforcer that will just FIFY instead of complaining
Current section
Files
Jump to
Current section
Files
lib/style/deprecations.ex
# Copyright 2024 Adobe. All rights reserved.
# This file is licensed to you under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software distributed under
# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
# OF ANY KIND, either express or implied. See the License for the specific language
# governing permissions and limitations under the License.
defmodule Styler.Style.Deprecations do
@moduledoc """
Transformations to soft or hard deprecations introduced on newer Elixir releases
"""
@behaviour Styler.Style
def run({node, meta}, ctx), do: {:cont, {style(node), meta}, ctx}
# Deprecated in 1.18
# rewrite patterns of `first..last = ...` to `first..last//_ = ...`
defp style({:=, m, [{:.., _, [_first, _last]} = range, rhs]}), do: {:=, m, [rewrite_range_match(range), rhs]}
defp style({:->, m, [[{:.., _, [_first, _last]} = range], rhs]}), do: {:->, m, [[rewrite_range_match(range)], rhs]}
defp style({:<-, m, [{:.., _, [_first, _last]} = range, rhs]}), do: {:<-, m, [rewrite_range_match(range), rhs]}
defp style({def, dm, [{x, xm, params} | rest]}) when def in ~w(def defp)a and is_list(params),
do: {def, dm, [{x, xm, Enum.map(params, &rewrite_range_match/1)} | rest]}
# Deprecated in 1.18
# List.zip => Enum.zip
defp style({{:., dm_, [{:__aliases__, am, [:List]}, :zip]}, fm, arg}),
do: {{:., dm_, [{:__aliases__, am, [:Enum]}, :zip]}, fm, arg}
# Logger.warn => Logger.warning
# Started to emit warning after Elixir 1.15.0
defp style({{:., dm, [{:__aliases__, am, [:Logger]}, :warn]}, funm, args}),
do: {{:., dm, [{:__aliases__, am, [:Logger]}, :warning]}, funm, args}
# Path.safe_relative_to/2 => Path.safe_relative/2
# TODO: Remove after Elixir v1.19
defp style({{:., dm, [{_, _, [:Path]} = mod, :safe_relative_to]}, funm, args}),
do: {{:., dm, [mod, :safe_relative]}, funm, args}
# Pipe version for:
# Path.safe_relative_to/2 => Path.safe_relative/2
defp style({:|>, m, [lhs, {{:., dm, [{:__aliases__, am, [:Path]}, :safe_relative_to]}, funm, args}]}),
do: {:|>, m, [lhs, {{:., dm, [{:__aliases__, am, [:Path]}, :safe_relative]}, funm, args}]}
if Version.match?(System.version(), ">= 1.16.0-dev") do
# File.stream!(file, options, line_or_bytes) => File.stream!(file, line_or_bytes, options)
defp style({{:., _, [{_, _, [:File]}, :stream!]} = f, fm, [path, {:__block__, _, [modes]} = opts, lob]})
when is_list(modes),
do: {f, fm, [path, lob, opts]}
# Pipe version for File.stream!
defp style({:|>, m, [lhs, {{_, _, [{_, _, [:File]}, :stream!]} = f, fm, [{:__block__, _, [modes]} = opts, lob]}]})
when is_list(modes),
do: {:|>, m, [lhs, {f, fm, [lob, opts]}]}
end
if Version.match?(System.version(), ">= 1.17.0-dev") do
for {erl, ex} <- [hours: :hour, minutes: :minute, seconds: :second] do
defp style({{:., _, [{:__block__, _, [:timer]}, unquote(erl)]}, fm, [x]}),
do: {:to_timeout, fm, [[{{:__block__, [format: :keyword, line: fm[:line]], [unquote(ex)]}, x}]]}
end
end
# Struct update syntax is deprecated in 1.19
# `%Foo{x | y} => %{x | y}`
defp style({:%, _, [_struct, {:%{}, _, [{:|, _, _}]} = update]}), do: update
# For ranges where `start > stop`, you need to explicitly include the step
# Enum.slice(enumerable, 1..-2) => Enum.slice(enumerable, 1..-2//1)
# String.slice("elixir", 2..-1) => String.slice("elixir", 2..-1//1)
defp style({{:., _, [{_, _, [module]}, :slice]} = f, funm, [enumerable, {:.., _, [_, _]} = range]})
when module in [:Enum, :String],
do: {f, funm, [enumerable, add_step_to_decreasing_range(range)]}
# Pipe version for {Enum,String}.slice
defp style({:|>, m, [lhs, {{:., _, [{_, _, [mod]}, :slice]} = f, funm, [{:.., _, [_, _]} = range]}]})
when mod in [:Enum, :String],
do: {:|>, m, [lhs, {f, funm, [add_step_to_decreasing_range(range)]}]}
# ~R is deprecated in favor of ~r
defp style({:sigil_R, m, args}), do: {:sigil_r, m, args}
# For a decreasing range, we must use Date.range/3 instead of Date.range/2
defp style({{:., _, [{:__aliases__, _, [:Date]}, :range]} = funm, dm, [first, last]} = block) do
if add_step_to_date_range?(first, last),
do: {funm, dm, [first, last, -1]},
else: block
end
# Pipe version for Date.range/2
defp style({:|>, pm, [first, {{:., _, [{:__aliases__, _, [:Date]}, :range]} = funm, dm, [last]}]} = pipe) do
if add_step_to_date_range?(first, last),
do: {:|>, pm, [first, {funm, dm, [last, -1]}]},
else: pipe
end
# use :eof instead of :all in IO.read/2 and IO.binread/2
defp style({{:., _, [{:__aliases__, _, [:IO]}, fun]} = fm, dm, [{:__block__, am, [:all]}]})
when fun in [:read, :binread],
do: {fm, dm, [{:__block__, am, [:eof]}]}
defp style({{:., _, [{:__aliases__, _, [:IO]}, fun]} = fm, dm, [device, {:__block__, am, [:all]}]})
when fun in [:read, :binread],
do: {fm, dm, [device, {:__block__, am, [:eof]}]}
defp style(node), do: node
defp rewrite_range_match({:.., dm, [first, {_, m, _} = last]}), do: {:..//, dm, [first, last, {:_, m, nil}]}
defp rewrite_range_match(x), do: x
defp add_step_to_date_range?(first, last) do
with {:ok, f} <- extract_date_value(first),
{:ok, l} <- extract_date_value(last),
# for ex1.14 compat, use compare instead of after?
:gt <- Date.compare(f, l) do
true
else
_ -> false
end
end
defp add_step_to_decreasing_range({:.., rm, [first, {_, lm, _} = last]} = range) do
with {:ok, start} <- extract_value_from_range(first),
{:ok, stop} <- extract_value_from_range(last),
true <- start > stop do
step = {:__block__, [token: "1", line: lm[:line]], [1]}
{:..//, rm, [first, last, step]}
else
_ -> range
end
end
# Extracts the positive or negative integer from the given range block
defp extract_value_from_range({:__block__, _, [value]}) when is_integer(value), do: {:ok, value}
defp extract_value_from_range({:-, _, [{:__block__, _, [value]}]}) when is_integer(value), do: {:ok, -value}
defp extract_value_from_range(_), do: :non_int
defp extract_date_value({:sigil_D, _, [{:<<>>, _, [date]}, []]}), do: Date.from_iso8601(date)
defp extract_date_value(_), do: :unknown
end