Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/elixir/lib/agent.ex
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ defmodule Agent do
@doc false
defmacro __using__(opts) do
quote location: :keep, bind_quoted: [opts: opts] do
unless Module.has_attribute?(__MODULE__, :doc) do
if not Module.has_attribute?(__MODULE__, :doc) do
@doc """
Returns a specification to start this module under a supervisor.

Expand Down
2 changes: 1 addition & 1 deletion lib/elixir/lib/calendar/time.ex
Original file line number Diff line number Diff line change
Expand Up @@ -521,7 +521,7 @@ defmodule Time do
do: unit > 0,
else: unit in ~w(second millisecond microsecond nanosecond)a

unless valid? do
if !valid? do
raise ArgumentError,
"unsupported time unit. Expected :hour, :minute, :second, :millisecond, :microsecond, :nanosecond, or a positive integer, got #{inspect(unit)}"
end
Expand Down
62 changes: 62 additions & 0 deletions lib/elixir/lib/code/formatter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ defmodule Code.Formatter do
sigils = Keyword.get(opts, :sigils, [])
normalize_bitstring_modifiers = Keyword.get(opts, :normalize_bitstring_modifiers, true)
normalize_charlists_as_sigils = Keyword.get(opts, :normalize_charlists_as_sigils, true)
rewrite_unless = Keyword.get(opts, :rewrite_unless, false)
syntax_colors = Keyword.get(opts, :syntax_colors, [])

sigils =
Expand All @@ -217,6 +218,7 @@ defmodule Code.Formatter do
file: file,
normalize_bitstring_modifiers: normalize_bitstring_modifiers,
normalize_charlists_as_sigils: normalize_charlists_as_sigils,
rewrite_unless: rewrite_unless,
inspect_opts: %Inspect.Opts{syntax_colors: syntax_colors}
}
end
Expand Down Expand Up @@ -484,6 +486,27 @@ defmodule Code.Formatter do
binary_op_to_algebra(:in, "not in", meta, left, right, context, state)
end

# rewrite unless as if!
defp quoted_to_algebra(
{:unless, meta, [condition, block]},
context,
%{rewrite_unless: true} = state
) do
quoted_to_algebra({:if, meta, [negate_condition(condition), block]}, context, state)
end

defp quoted_to_algebra(
{:|>, meta1, [condition, {:unless, meta2, [block]}]},
context,
%{rewrite_unless: true} = state
) do
quoted_to_algebra(
{:|>, meta1, [negate_condition(condition), {:if, meta2, [block]}]},
context,
state
)
end

# ..
defp quoted_to_algebra({:.., _meta, []}, context, state) do
if context in [:no_parens_arg, :no_parens_one_arg] do
Expand Down Expand Up @@ -2449,4 +2472,43 @@ defmodule Code.Formatter do
defp has_double_quote?(chunk) do
is_binary(chunk) and chunk =~ @double_quote
end

# Migration rewrites

@bool_operators [
:>,
:>=,
:<,
:<=,
:in
Comment on lines +2479 to +2483
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and and or could be added here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wish we could, but unfortunately this wouldn't be safe and might break code that is working today, since false or 42 is valid (also arguably pretty bad) but not (false or 42) would raise.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ahhh right, i always think that those are bool, bool -> bool, forgot it's bool, any -> any
i'll just join you in wishing we could then

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we could try harder in these cases, checking if the right handside itself is a guard/comparison... but I'm not sure it's worth the extra complexity and it won't capture cases like and enabled? anyway.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alternatively, only use not with in, and for the 4 comparisons rewrite them to their opposite like with the equality operators:

{:>, meta, [left, right]} -> {:<=, meta, [left, right]}
{:>=, meta, [left, right]} -> {:<, meta, [left, right]}
...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hesitated between the two approaches, not sure which would be better. Maybe yours is more natural indeed.

Copy link
Contributor Author

@sabiwara sabiwara Sep 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually not might be better in this case, if we consider that the user explicitly wrote:

unless x > @limit do

over

if x <= @limit do

which they could have done in the first place, then

if not x > @limit do

might read better to them for some reason?

It is not critical though, people might see the diff and fix it to their preference eventually.
So I'm fine with either approach.

Copy link
Contributor

@novaugust novaugust Sep 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, i think you're right. if not is a simpler change to reason over than if $swapped_equality_operator. thanks for spelling that scenario out.

]
@guards [
:is_atom,
:is_boolean,
:is_nil,
:is_number,
:is_integer,
:is_float,
:is_binary,
:is_list,
:is_map,
:is_struct,
:is_function,
:is_reference,
:is_pid
]

defp negate_condition(condition) do
case condition do
{neg, _, [condition]} when neg in [:!, :not] -> condition
{:|>, _, _} -> {:|>, [], [condition, {{:., [], [Kernel, :!]}, [closing: []], []}]}
{op, _, [_, _]} when op in @bool_operators -> {:not, [], [condition]}
{guard, _, [_ | _]} when guard in @guards -> {:not, [], [condition]}
Comment on lines +2505 to +2506
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a thought i had while implementing this for styler -- being smart and using not here might be dangerous, because it's possible the user did something whacky like

import Kernel, except: [is_atom: 1]

def is_atom(...), do: :ok

better to just always use ! and leave it to the user to translate to not as they want?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea behind migrate option is that they rewrite the AST and cannot guarantee to work in edge cases where metaprogramming assumes a given AST or Kernel overwrites are done (after all people can have re-defined unless itself, or ==, etc...).

This particular one feels like an unlikely case, the need to overwriting is_atom feels low, the convention that only guards returning booleans should start with is_ should also discourage it... I think we're fine.

{:==, meta, [left, right]} -> {:!=, meta, [left, right]}
{:===, meta, [left, right]} -> {:!==, meta, [left, right]}
{:!=, meta, [left, right]} -> {:==, meta, [left, right]}
{:!==, meta, [left, right]} -> {:===, meta, [left, right]}
_ -> {:!, [], [condition]}
end
end
end
2 changes: 1 addition & 1 deletion lib/elixir/lib/config.ex
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ defmodule Config do
"""
@doc since: "1.9.0"
def config(root_key, opts) when is_atom(root_key) and is_list(opts) do
unless Keyword.keyword?(opts) do
if not Keyword.keyword?(opts) do
raise ArgumentError, "config/2 expected a keyword list, got: #{inspect(opts)}"
end

Expand Down
2 changes: 1 addition & 1 deletion lib/elixir/lib/dynamic_supervisor.ex
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ defmodule DynamicSupervisor do
defmacro __using__(opts) do
quote location: :keep, bind_quoted: [opts: opts] do
@behaviour DynamicSupervisor
unless Module.has_attribute?(__MODULE__, :doc) do
if not Module.has_attribute?(__MODULE__, :doc) do
@doc """
Returns a specification to start this module under a supervisor.

Expand Down
4 changes: 2 additions & 2 deletions lib/elixir/lib/gen_server.ex
Original file line number Diff line number Diff line change
Expand Up @@ -845,7 +845,7 @@ defmodule GenServer do
quote location: :keep, bind_quoted: [opts: opts] do
@behaviour GenServer

unless Module.has_attribute?(__MODULE__, :doc) do
if not Module.has_attribute?(__MODULE__, :doc) do
@doc """
Returns a specification to start this module under a supervisor.

Expand Down Expand Up @@ -945,7 +945,7 @@ defmodule GenServer do
end

defmacro __before_compile__(env) do
unless Module.defines?(env.module, {:init, 1}) do
if !Module.defines?(env.module, {:init, 1}) do
message = """
function init/1 required by behaviour GenServer is not implemented \
(in module #{inspect(env.module)}).
Expand Down
2 changes: 1 addition & 1 deletion lib/elixir/lib/io/ansi/docs.ex
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ defmodule IO.ANSI.Docs do
|> String.split(@spaces)
|> write_with_wrap(options[:width] - byte_size(indent), indent, no_wrap, prefix)

unless no_wrap, do: newline_after_block(options)
if !no_wrap, do: newline_after_block(options)
end

defp format_text(text, options) do
Expand Down
14 changes: 7 additions & 7 deletions lib/elixir/lib/kernel/typespec.ex
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ defmodule Kernel.Typespec do
case spec_to_signature(expr) do
{name, arity} ->
# store doc only once in case callback has multiple clauses
unless :ets.member(set, {kind, name, arity}) do
if not :ets.member(set, {kind, name, arity}) do
{line, doc} = get_doc_info(set, :doc, line)
store_doc(set, kind, name, arity, line, :doc, doc, %{})
end
Expand Down Expand Up @@ -320,7 +320,7 @@ defmodule Kernel.Typespec do

invalid_args = :lists.filter(&(not valid_variable_ast?(&1)), args)

unless invalid_args == [] do
if invalid_args != [] do
invalid_args = :lists.join(", ", :lists.map(&Macro.to_string/1, invalid_args))

message =
Expand Down Expand Up @@ -380,7 +380,7 @@ defmodule Kernel.Typespec do
ensure_no_defaults!(args)
state = clean_local_state(state)

unless Keyword.keyword?(guard) do
if not Keyword.keyword?(guard) do
error = "expected keywords as guard in type specification, got: #{Macro.to_string(guard)}"
compile_error(caller, error)
end
Expand Down Expand Up @@ -573,7 +573,7 @@ defmodule Kernel.Typespec do
|> Map.delete(:__struct__)
|> Map.to_list()

unless Keyword.keyword?(fields) do
if not Keyword.keyword?(fields) do
compile_error(caller, "expected key-value pairs in struct #{Macro.to_string(name)}")
end

Expand All @@ -587,7 +587,7 @@ defmodule Kernel.Typespec do
)

fun = fn {field, _} ->
unless Keyword.has_key?(struct, field) do
if not Keyword.has_key?(struct, field) do
compile_error(
caller,
"undefined field #{inspect(field)} on struct #{inspect(module)}"
Expand Down Expand Up @@ -630,7 +630,7 @@ defmodule Kernel.Typespec do
)

fun = fn {field, _} ->
unless Keyword.has_key?(fields, field) do
if not Keyword.has_key?(fields, field) do
compile_error(caller, "undefined field #{field} on record #{inspect(tag)}")
end
end
Expand Down Expand Up @@ -754,7 +754,7 @@ defmodule Kernel.Typespec do
) do
remote = Module.get_attribute(caller.module, attr)

unless is_atom(remote) and remote != nil do
if not (is_atom(remote) and remote != nil) do
message =
"invalid remote in typespec: #{Macro.to_string(orig)} (@#{attr} is #{inspect(remote)})"

Expand Down
4 changes: 2 additions & 2 deletions lib/elixir/lib/macro.ex
Original file line number Diff line number Diff line change
Expand Up @@ -895,8 +895,8 @@ defmodule Macro do
defp find_invalid(bin) when is_binary(bin), do: nil

defp find_invalid(fun) when is_function(fun) do
unless Function.info(fun, :env) == {:env, []} and
Function.info(fun, :type) == {:type, :external} do
if Function.info(fun, :env) != {:env, []} or
Function.info(fun, :type) != {:type, :external} do
{:error, fun}
end
end
Expand Down
4 changes: 2 additions & 2 deletions lib/elixir/lib/module.ex
Original file line number Diff line number Diff line change
Expand Up @@ -902,7 +902,7 @@ defmodule Module do
end

def create(module, quoted, opts) when is_atom(module) and is_list(opts) do
unless Keyword.has_key?(opts, :file) do
if not Keyword.has_key?(opts, :file) do
raise ArgumentError, "expected :file to be given as option"
end

Expand Down Expand Up @@ -2260,7 +2260,7 @@ defmodule Module do
end

defp preprocess_attribute(:nifs, value) do
unless function_arity_list?(value) do
if not function_arity_list?(value) do
raise ArgumentError,
"@nifs is a built-in module attribute for specifying a list " <>
"of functions and their arities that are NIFs, got: #{inspect(value)}"
Expand Down
14 changes: 7 additions & 7 deletions lib/elixir/lib/partition_supervisor.ex
Original file line number Diff line number Diff line change
Expand Up @@ -228,26 +228,26 @@ defmodule PartitionSupervisor do
def start_link(opts) when is_list(opts) do
name = opts[:name]

unless name do
if !name do
raise ArgumentError, "the :name option must be given to PartitionSupervisor"
end

{child_spec, opts} = Keyword.pop(opts, :child_spec)

unless child_spec do
if !child_spec do
raise ArgumentError, "the :child_spec option must be given to PartitionSupervisor"
end

{partitions, opts} = Keyword.pop(opts, :partitions, System.schedulers_online())

unless is_integer(partitions) and partitions >= 1 do
if not (is_integer(partitions) and partitions >= 1) do
raise ArgumentError,
"the :partitions option must be a positive integer, got: #{inspect(partitions)}"
end

{with_arguments, opts} = Keyword.pop(opts, :with_arguments, fn args, _partition -> args end)

unless is_function(with_arguments, 2) do
if not is_function(with_arguments, 2) do
raise ArgumentError,
"the :with_arguments option must be a function that receives two arguments, " <>
"the current call arguments and the partition, got: #{inspect(with_arguments)}"
Expand All @@ -260,7 +260,7 @@ defmodule PartitionSupervisor do
for partition <- 0..(partitions - 1) do
args = with_arguments.(args, partition)

unless is_list(args) do
if not is_list(args) do
raise "the call to the function in :with_arguments must return a list, got: #{inspect(args)}"
end

Expand All @@ -270,7 +270,7 @@ defmodule PartitionSupervisor do

auto_shutdown = Keyword.get(opts, :auto_shutdown, :never)

unless auto_shutdown == :never do
if auto_shutdown != :never do
raise ArgumentError,
"the :auto_shutdown option must be :never, got: #{inspect(auto_shutdown)}"
end
Expand Down Expand Up @@ -319,7 +319,7 @@ defmodule PartitionSupervisor do
defp init_partitions({:via, _, _}, partitions) do
child_spec = {Registry, keys: :unique, name: @registry}

unless Process.whereis(@registry) do
if !Process.whereis(@registry) do
Supervisor.start_child(:elixir_sup, child_spec)
end

Expand Down
2 changes: 1 addition & 1 deletion lib/elixir/lib/protocol.ex
Original file line number Diff line number Diff line change
Expand Up @@ -896,7 +896,7 @@ defmodule Protocol do
# Inline struct implementation for performance
@compile {:inline, struct_impl_for: 1}

unless Module.defines_type?(__MODULE__, {:t, 0}) do
if !Module.defines_type?(__MODULE__, {:t, 0}) do
@typedoc """
All the types that implement this protocol.
"""
Expand Down
12 changes: 6 additions & 6 deletions lib/elixir/lib/registry.ex
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ defmodule Registry do
def start_link(options) do
keys = Keyword.get(options, :keys)

unless keys in @keys do
if keys not in @keys do
raise ArgumentError,
"expected :keys to be given and be one of :unique or :duplicate, got: #{inspect(keys)}"
end
Expand All @@ -350,27 +350,27 @@ defmodule Registry do

meta = Keyword.get(options, :meta, [])

unless Keyword.keyword?(meta) do
if not Keyword.keyword?(meta) do
raise ArgumentError, "expected :meta to be a keyword list, got: #{inspect(meta)}"
end

partitions = Keyword.get(options, :partitions, 1)

unless is_integer(partitions) and partitions >= 1 do
if not (is_integer(partitions) and partitions >= 1) do
raise ArgumentError,
"expected :partitions to be a positive integer, got: #{inspect(partitions)}"
end

listeners = Keyword.get(options, :listeners, [])

unless is_list(listeners) and Enum.all?(listeners, &is_atom/1) do
if not (is_list(listeners) and Enum.all?(listeners, &is_atom/1)) do
raise ArgumentError,
"expected :listeners to be a list of named processes, got: #{inspect(listeners)}"
end

compressed = Keyword.get(options, :compressed, false)

unless is_boolean(compressed) do
if not is_boolean(compressed) do
raise ArgumentError,
"expected :compressed to be a boolean, got: #{inspect(compressed)}"
end
Expand Down Expand Up @@ -1433,7 +1433,7 @@ defmodule Registry do
end

defp unlink_if_unregistered(pid_server, pid_ets, self) do
unless :ets.member(pid_ets, self) do
if not :ets.member(pid_ets, self) do
Process.unlink(pid_server)
end
end
Expand Down
6 changes: 3 additions & 3 deletions lib/elixir/lib/stream/reducers.ex
Original file line number Diff line number Diff line change
Expand Up @@ -151,10 +151,10 @@ defmodule Stream.Reducers do
defmacro reject(callback, fun \\ nil) do
quote do
fn entry, acc ->
unless unquote(callback).(entry) do
next(unquote(fun), entry, acc)
else
if unquote(callback).(entry) do
skip(acc)
else
next(unquote(fun), entry, acc)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/elixir/lib/supervisor.ex
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,7 @@ defmodule Supervisor do
import Supervisor.Spec
@behaviour Supervisor

unless Module.has_attribute?(__MODULE__, :doc) do
if not Module.has_attribute?(__MODULE__, :doc) do
@doc """
Returns a specification to start this module under a supervisor.

Expand Down
2 changes: 1 addition & 1 deletion lib/elixir/lib/supervisor/spec.ex
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ defmodule Supervisor.Spec do
) :: {:ok, tuple}
@deprecated "Use the new child specifications outlined in the Supervisor module instead"
def supervise(children, options) do
unless strategy = options[:strategy] do
if !(strategy = options[:strategy]) do
raise ArgumentError, "expected :strategy option to be given"
end

Expand Down
Loading
Loading