hrzndhrn / recode

A linter with autocorrection and a refactoring tool.
MIT License
281 stars 15 forks source link

Possible race condition #80

Closed axelson closed 6 months ago

axelson commented 6 months ago

I'm testing recode and running into a FunctionClauseError. It appears to happen more often on Elixir 1.15.7 and happens more often when not all of the tasks are included. To reproduce download https://github.com/axelson/recode_formatter_repro and ensure that you're running with the versions of elixir/erlang in the .tool-versions file.

In that project about 1/3 to 1/2 the time when I run mix recode --dry I get a FunctionClauseError:

> mix recode --dry
** (FunctionClauseError) no function clause matching in anonymous fn/2 in Rewrite.read!/3

    The following arguments were given to anonymous fn/2 in Rewrite.read!/3:

        # 1
        {:exit, {:badarg, [{:ets, :new, [:recode_formatter_plugin, [:set, :public, :named_table]], [error_info: %{cause: :already_exists, module: :erl_stdlib_errors}]}, {Recode.FormatterPlugin, :init, 1, [file: ~c"lib/recode/formatter_plugin.ex", line: 77]}, {Recode.FormatterPlugin, :features, 1, [file: ~c"lib/recode/formatter_plugin.ex", line: 50]}, {Mix.Tasks.Format, :find_sigils_from_plugins, 2, [file: ~c"lib/mix/tasks/format.ex", line: 618]}, {Mix.Tasks.Format, :"-load_plugins/1-fun-2-", 3, [file: ~c"lib/mix/tasks/format.ex", line: 275]}, {Enum, :"-reduce/3-lists^foldl/2-0-", 3, [file: ~c"lib/enum.ex", line: 2510]}, {Mix.Tasks.Format, :load_plugins, 1, [file: ~c"lib/mix/tasks/format.ex", line: 274]}, {Mix.Tasks.Format, :formatter_for_file, 2, [file: ~c"lib/mix/tasks/format.ex", line: 310]}]}}

        # 2
        %{"mix.exs" => %Rewrite.Source{from: :file, path: "mix.exs", content: "defmodule Fmt.MixProject do\n  use Mix.Project\n\n  def project do\n    [\n      app: :fmt,\n      version: \"0.1.0\",\n      elixir: \"~> 1.14\",\n      start_permanent: Mix.env() == :prod,\n      deps: deps(),\n    ]\n  end\n\n  # Run \"mix help compile.app\" to learn about applications.\n  def application do\n    [\n      extra_applications: [:logger],\n    ]\n  end\n\n  # Run \"mix help deps\" to learn about dependencies.\n  defp deps do\n    [\n      {:freedom_formatter, \">= 2.0.0\", only: :dev},\n      {:recode, \"~> 0.6\", only: :dev},\n      # {:dep_from_hexpm, \"~> 0.3.0\"},\n      # {:dep_from_git, git: \"https://github.com/elixir-lang/my_dep.git\", tag: \"0.1.0\"}\n    ]\n  end\nend\n", hash: <<110, 19, 26, 1, 129, 12, 212, 207, 127, 142, 86, 167, 75, 136, 132, 165>>, owner: Rewrite, filetype: %Rewrite.Source.Ex{quoted: {:defmodule, [trailing_comments: [], leading_comments: [], do: [line: 1, column: 26], end: [line: 30, column: 1], line: 1, column: 1], [{:__aliases__, [trailing_comments: [], leading_comments: [], last: [line: 1, column: 15], line: 1, column: 11], [:Fmt, :MixProject]}, [{{:__block__, [trailing_comments: [], leading_comments: [], line: 1, column: 26], [:do]}, {:__block__, [trailing_comments: [], leading_comments: []], [{:use, [trailing_comments: [], leading_comments: [], end_of_expression: [newlines: 2, line: 2, column: 18], line: 2, column: 3], [{:__aliases__, [trailing_comments: [], leading_comments: [], last: [line: 2, column: 11], line: 2, column: 7], [:Mix, :Project]}]}, {:def, [trailing_comments: [], leading_comments: [], end_of_expression: [newlines: 1, line: 12, column: 6], do: [line: 4, column: 15], end: [line: 12, column: 3], line: 4, column: 3], [{:project, [trailing_comments: [], leading_comments: [], line: 4, column: 7], nil}, [{{:__block__, [trailing_comments: [], leading_comments: [], line: 4, column: 15], [:do]}, {:__block__, [trailing_comments: [], leading_comments: [], newlines: 1, closing: [line: 11, column: 5], line: 5, column: 5], [[{{:__block__, [trailing_comments: [], leading_comments: [], format: :keyword, line: 6, column: 7], [:app]}, {:__block__, [trailing_comments: [], leading_comments: [], line: 6, column: 12], [:fmt]}}, {{:__block__, [trailing_comments: [], leading_comments: [], format: :keyword, line: 7, column: 7], [:version]}, {:__block__, [trailing_comments: [], leading_comments: [], delimiter: "\"", line: 7, column: 16], ["0.1.0"]}}, {{:__block__, [trailing_comments: [], leading_comments: [], format: :keyword, line: 8, column: 7], [:elixir]}, {:__block__, [trailing_comments: [], leading_comments: [], delimiter: "\"", line: 8, column: 15], ["~> 1.14"]}}, {{:__block__, [trailing_comments: [], leading_comments: [], format: :keyword, line: 9, column: 7], [:start_permanent]}, {:==, [trailing_comments: [], line: 9, column: 34], [{{:., [trailing_comments: [], line: 9, column: 27], [{:__aliases__, [...], ...}, :env]}, [trailing_comments: [], leading_comments: [], closing: [line: 9, column: 32], line: 9, column: 28], []}, {:__block__, [trailing_comments: [], leading_comments: [], line: 9, column: 37], [:prod]}]}}, {{:__block__, [trailing_comments: [], leading_comments: [], format: :keyword, line: 10, column: 7], [:deps]}, {:deps, [trailing_comments: [], leading_comments: [], closing: [line: 10, column: 18], line: 10, column: 13], []}}]]}}]]}, {:def, [trailing_comments: [], leading_comments: [%{line: 14, text: "# Run \"mix help compile.app\" to learn about applications.", column: 3, next_eol_count: 1, previous_eol_count: 2}], end_of_expression: [newlines: 1, line: 19, column: 6], do: [line: 15, column: 19], end: [line: 19, column: 3], line: 15, column: 3], [{:application, [trailing_comments: [], leading_comments: [], line: 15, column: 7], nil}, [{{:__block__, [trailing_comments: [], leading_comments: [], line: 15, column: 19], [:do]}, {:__block__, [trailing_comments: [], leading_comments: [], newlines: 1, closing: [line: 18, column: 5], line: 16, column: 5], [[{{:__block__, [trailing_comments: [], leading_comments: [], format: :keyword, line: 17, column: 7], [:extra_applications]}, {:__block__, [trailing_comments: [], leading_comments: [], closing: [line: 17, column: 35], line: 17, column: 27], [[{:__block__, [trailing_comments: [], leading_comments: [], line: 17, column: 28], [:logger]}]]}}]]}}]]}, {:defp, [trailing_comments: [], leading_comments: [%{line: 21, text: "# Run \"mix help deps\" to learn about dependencies.", column: 3, next_eol_count: 1, previous_eol_count: 2}], do: [line: 22, column: 13], end: [line: 29, column: 3], line: 22, column: 3], [{:deps, [trailing_comments: [], leading_comments: [], line: 22, column: 8], nil}, [{{:__block__, [trailing_comments: [], leading_comments: [], line: 22, column: 13], [:do]}, {:__block__, [trailing_comments: [%{line: 26, text: "# {:dep_from_hexpm, \"~> 0.3.0\"},", column: 7, next_eol_count: 1, previous_eol_count: 1}, %{line: 27, text: "# {:dep_from_git, git: \"https://github.com/elixir-lang/my_dep.git\", tag: \"0.1.0\"}", column: 7, next_eol_count: 1, previous_eol_count: 2}], leading_comments: [], newlines: 1, closing: [line: 28, column: 5], line: 23, column: 5], [[{:{}, [trailing_comments: [], leading_comments: [], closing: [line: 24, column: 50], line: 24, column: 7], [{:__block__, [trailing_comments: [], leading_comments: [], line: 24, column: 8], [:freedom_formatter]}, {:__block__, [trailing_comments: [], leading_comments: [], delimiter: "\"", line: 24, column: 28], [">= 2.0.0"]}, [{{:__block__, [trailing_comments: [], leading_comments: [], format: :keyword, line: 24, ...], [:only]}, {:__block__, [trailing_comments: [], leading_comments: [], line: 24, ...], [:dev]}}]]}, {:{}, [trailing_comments: [], leading_comments: [], closing: [line: 25, column: 37], line: 25, column: 7], [{:__block__, [trailing_comments: [], leading_comments: [], line: 25, column: 8], [:recode]}, {:__block__, [trailing_comments: [], leading_comments: [], delimiter: "\"", line: 25, column: 17], ["~> 0.6"]}, [{{:__block__, [trailing_comments: [], leading_comments: [], format: :keyword, ...], [:only]}, {:__block__, [trailing_comments: [], leading_comments: [], ...], [:dev]}}]]}]]}}]]}]}}]]}, formatter: #Function<2.24640748/2 in Rewrite.Source.Ex.formatter/2>, opts: [exclude_plugins: [Recode.FormatterPlugin]]}, history: [], issues: [], private: %{}}}

    (rewrite 0.10.0) anonymous fn/2 in Rewrite.read!/3
    (elixir 1.15.7) lib/enum.ex:4387: anonymous fn/3 in Enum.reduce/3
    (elixir 1.15.7) lib/task/supervised.ex:386: Task.Supervised.stream_deliver/7
    (elixir 1.15.7) lib/enum.ex:4387: Enum.reduce/3
    (rewrite 0.10.0) lib/rewrite.ex:96: Rewrite.read!/3
    (recode 0.6.5) lib/recode/runner/impl.ex:31: Recode.Runner.Impl.do_run/2
    (recode 0.6.5) lib/mix/tasks/recode.ex:86: Mix.Tasks.Recode.run/1
    (mix 1.15.7) lib/mix/task.ex:455: anonymous fn/3 in Mix.Task.run_task/5

In other projects I get it closer to 100% of the time.

NickNeck commented 6 months ago

Thank you for reporting this. I will take a look.

NickNeck commented 6 months ago

Hello @axelson I have released version 0.7.0, that should fix the race condition.

axelson commented 6 months ago

Thank you! That issue is fixed now! :tada: