Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

credo@1.1.0 crashes, when sourcecode contain unicode heredoc #665

Closed
bencode opened this issue Jun 25, 2019 · 10 comments

Comments

Projects
None yet
6 participants
@bencode
Copy link

commented Jun 25, 2019

Environment

  • Credo version (mix credo -v):
    1.1.0

  • Erlang/Elixir version (elixir -v):

Erlang/OTP 22 [erts-10.4.3] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe] [dtrace]

Elixir 1.9.0 (compiled with Erlang/OTP 22)
  • Operating system:

MacOS

What were you trying to do?

mix credo

Expected outcome

Actual outcome

Screenshot 2019-06-25 at 9 16 18 PM

may be reason

in function pad_replaced_heredoc length_after_byte_index my be minus

rrrene added a commit that referenced this issue Jun 26, 2019

@rrrene

This comment has been minimized.

Copy link
Owner

commented Jun 26, 2019

@bencode Thx for reporting! 👍

Could you provide the heredoc that caused the crash or is the information sensitive? I tried to reproduce this in 2664240 to no avail.

@bencode

This comment has been minimized.

@skovmand

This comment has been minimized.

Copy link

commented Jul 2, 2019

I have the same issue in two modules, also on Elixir 1.9.0 and credo 1.1.0. It goes away when I remove all the @doc blocks from the modules or if I disable the MaxLineLength check. There is only text in the doc blocks. The stack traces (with removed code, it's a private project).

** (EXIT from #PID<0.93.0>) an exception was raised:
    ** (FunctionClauseError) no function clause matching in String.slice/3
        (elixir) lib/string.ex:1864: String.slice("<internal code removed>", 1610, -1)
        lib/credo/code/heredocs.ex:253: Credo.Code.Heredocs.pad_replaced_heredoc/2
        lib/credo/code/heredocs.ex:156: Credo.Code.Heredocs.parse_heredoc/5
        lib/credo/check/readability/max_line_length.ex:52: Credo.Check.Readability.MaxLineLength.run/2
        lib/credo/check/runner.ex:168: Credo.Check.Runner.do_run_check_on_single_source_file/3
        lib/credo/check/runner.ex:161: Credo.Check.Runner.run_check_on_single_source_file/3
        (elixir) lib/enum.ex:1336: Enum."-map/2-lists^map/1-0-"/2
        (elixir) lib/enum.ex:1336: Enum."-map/2-lists^map/1-0-"/2

and the other

** (EXIT from #PID<0.93.0>) an exception was raised:
    ** (FunctionClauseError) no function clause matching in String.slice/3
        (elixir) lib/string.ex:1864: String.slice("<internal code removed>", 701, -2)
        lib/credo/code/heredocs.ex:253: Credo.Code.Heredocs.pad_replaced_heredoc/2
        lib/credo/code/heredocs.ex:156: Credo.Code.Heredocs.parse_heredoc/5
        lib/credo/check/readability/max_line_length.ex:52: Credo.Check.Readability.MaxLineLength.run/2
        lib/credo/check/runner.ex:168: Credo.Check.Runner.do_run_check_on_single_source_file/3
        lib/credo/check/runner.ex:161: Credo.Check.Runner.run_check_on_single_source_file/3
        (elixir) lib/enum.ex:1336: Enum."-map/2-lists^map/1-0-"/2
        (elixir) lib/enum.ex:1336: Enum."-map/2-lists^map/1-0-"/2
@skovmand

This comment has been minimized.

Copy link

commented Jul 2, 2019

I have tried to isolate the error, and ended up with this minimal file to reproduce the issue.

The error goes away by doing either one of these things:

  • Deleting @some_utf_8_char
  • Removing æøå from the regular expression
  • Removing the last @doc block
defmodule CredoIssue.FailingFile do
  @some_utf8_char "💩"

  def match_whatever(whatever) do
    case Regex.run(~r/([a-zæøå]+)/, whatever) do
      # Nothing here
    end
  end

  @doc """
  Nothing to see here really
  """
  def nothing_to_nil() do
    nil
  end
end

The stacktrace is:

** (EXIT from #PID<0.93.0>) an exception was raised:
    ** (FunctionClauseError) no function clause matching in String.slice/3
        (elixir) lib/string.ex:1864: String.slice("defmodule CredoIssue.FailingFile do\n  @some_utf8_char \"💩\"\n\n  def match_whatever([whatever]) do\n    case Regex.run(~r/([a-zæøå]+)/, whatever) do\n      # Nothing here\n    end\n  end\n\n  @doc \"\"\"\n\n", 193, -1)
        lib/credo/code/heredocs.ex:253: Credo.Code.Heredocs.pad_replaced_heredoc/2
        lib/credo/code/heredocs.ex:156: Credo.Code.Heredocs.parse_heredoc/5
        lib/credo/check/readability/max_line_length.ex:52: Credo.Check.Readability.MaxLineLength.run/2
        lib/credo/check/runner.ex:168: Credo.Check.Runner.do_run_check_on_single_source_file/3
        lib/credo/check/runner.ex:161: Credo.Check.Runner.run_check_on_single_source_file/3
        (elixir) lib/enum.ex:1336: Enum."-map/2-lists^map/1-0-"/2
        (elixir) lib/enum.ex:1336: Enum."-map/2-lists^map/1-0-"/2
@skovmand

This comment has been minimized.

Copy link

commented Jul 2, 2019

And now the same minimal file reproduced from my other failing module

The weird part here is that the issue can be resolved by removing any of the characters æøå from the @danish_words list. I found that the module only crashes if there are 6 or more characters of type æøå in the @danish_words list, if one is removed, the error disappears.

Try for example to change the @danish_words list to "åååååå", and it still crashes. Change it to five å characters and it the error disappears. Changing it to "💩💩" also makes the test crash.

Removing the @doc block removes the error too.

defmodule CredoIssue.FailingFile2 do
  @danish_words [
    "Rødbede",
    "Æble",
    "Kål",
    "Kålorm",
    "Æblegrød"
  ]

  @doc """
  Just nil it!!
  """
  def nil_it() do
    nil
  end
end

or the smallest example I can get to

defmodule CredoIssue.FailingFile2 do
  @chars "💩💩"

  @doc """
  Just nil it
  """
  def nil_it() do
    nil
  end
end
@skovmand

This comment has been minimized.

Copy link

commented Jul 2, 2019

I suspect that it has something to do with the different byte_sizes of the involved international characters and emojis, since:

iex(5)> byte_size("a") 
1
iex(6)> byte_size("å") 
2
iex(7)> byte_size("💩")
4
@k-cross

This comment has been minimized.

Copy link

commented Jul 2, 2019

I also have this issue on elixir 1.7.3

@robertpeacock22

This comment has been minimized.

Copy link

commented Jul 12, 2019

Here is the smallest repro I can come up with for this issue:

defmodule UnicodeTest do
  @x "fédération fédération fédération"

  @doc """
  """
end

This results in a similar stack trace to the one seen above:

** (FunctionClauseError) no function clause matching in String.slice/3
    (elixir) lib/string.ex:1741: String.slice("defmodule UnicodeTest do\n  @x \"fédération fédération fédération\"\n\n  @doc \"\"\"\n", 79, -2)
    lib/credo/code/heredocs.ex:253: Credo.Code.Heredocs.pad_replaced_heredoc/2
    lib/credo/code/heredocs.ex:156: Credo.Code.Heredocs.parse_heredoc/5
    lib/credo/check/readability/max_line_length.ex:52: Credo.Check.Readability.MaxLineLength.run/2
    lib/credo/check/runner.ex:168: Credo.Check.Runner.do_run_check_on_single_source_file/3
    lib/credo/check/runner.ex:161: Credo.Check.Runner.run_check_on_single_source_file/3
    (elixir) lib/enum.ex:1294: Enum."-map/2-lists^map/1-0-"/2
    (elixir) lib/enum.ex:1294: Enum."-map/2-lists^map/1-0-"/2
Function: #Function<9.36871822/0 in Credo.Check.Runner.run/2>
    Args: []

rrrene added a commit that referenced this issue Jul 16, 2019

rrrene added a commit that referenced this issue Jul 16, 2019

@rrrene

This comment has been minimized.

Copy link
Owner

commented Jul 17, 2019

Thx for reporting this (and discussing it in detail)! 👏 It should be be fixed in the latest version of Credo (v1.1.1).

If it is not, please feel free to re-open this issue!

@rrrene rrrene closed this Jul 17, 2019

@jamesotron

This comment has been minimized.

Copy link

commented Jul 17, 2019

I'm really sorry to report that it's still happening:

$ mix credo --strict
Checking 19 source files ...

info: the following checks were skipped because they're not compatible with
your version of Elixir (1.9.0).

   1) Credo.Check.Refactor.MapInto
   2) Credo.Check.Warning.LazyLogging

Error while running Elixir.Credo.Check.Readability.MaxLineLength on lib/angle/dms.ex

07:56:00.170 [error] Task #PID<0.142.0> started from #PID<0.92.0> terminating
** (FunctionClauseError) no function clause matching in String.slice/3
    (elixir) lib/string.ex:1864: String.slice("defmodule Angle.DMS do\n  import Angle.Utils, only: [string_to_integer: 1, string_to_number: 1]\n  alias Angle.Degree\n\n  @moduledoc \"\"\"\n\n\"\"\"\n\n  @parser ~r/(-?[0-9]+)\n             (?:[\\x{00b0},\\ ]?\\ *)\n             ([0-9]+)\n             (?:[\\x{2032}',\\ ]?\\ *)\n             ([0-9]+(?:\\.[0-9]+)?)\n             [\\x{2033}\"]?/ux\n\n  @doc \"\"\"\n  Initialize an Angle from integer `d` degrees.\n\n  ## Examples\n\n      iex> init(13)\n      #Angle<13°>\n  \"\"\"\n\n\n\n\"\"\"\n  Initialize an Angle from integer `d` degrees, optionally followed by integer\n  `m` minutes.\n\n  ## Examples\n\n      iex> init(13, 30)\n      #Angle<13° 30′>\n  \"\"\"\n\n\n\n\"\"\"\n  Initialize an Angle from integer `d` degrees, followed by integer\n  `m` minutes and `s` seconds.\n\n  ## Examples\n\n      iex> init(13, 30, 45)\n      #Angle<13° 30′ 45″>\n  \"\"\"\n\n\n\n\n", 797, -1)
    lib/credo/code/heredocs.ex:253: Credo.Code.Heredocs.pad_replaced_heredoc/2
    lib/credo/code/heredocs.ex:156: Credo.Code.Heredocs.parse_heredoc/5
    lib/credo/check/readability/max_line_length.ex:52: Credo.Check.Readability.MaxLineLength.run/2
    lib/credo/check/runner.ex:168: Credo.Check.Runner.do_run_check_on_single_source_file/3
    lib/credo/check/runner.ex:161: Credo.Check.Runner.run_check_on_single_source_file/3
    (elixir) lib/enum.ex:1336: Enum."-map/2-lists^map/1-0-"/2
    (elixir) lib/enum.ex:1336: Enum."-map/2-lists^map/1-0-"/2
Function: #Function<9.89825571/0 in Credo.Check.Runner.run/2>
    Args: []
** (EXIT from #PID<0.92.0>) an exception was raised:
    ** (FunctionClauseError) no function clause matching in String.slice/3
        (elixir) lib/string.ex:1864: String.slice("defmodule Angle.DMS do\n  import Angle.Utils, only: [string_to_integer: 1, string_to_number: 1]\n  alias Angle.Degree\n\n  @moduledoc \"\"\"\n\n\"\"\"\n\n  @parser ~r/(-?[0-9]+)\n             (?:[\\x{00b0},\\ ]?\\ *)\n             ([0-9]+)\n             (?:[\\x{2032}',\\ ]?\\ *)\n             ([0-9]+(?:\\.[0-9]+)?)\n             [\\x{2033}\"]?/ux\n\n  @doc \"\"\"\n  Initialize an Angle from integer `d` degrees.\n\n  ## Examples\n\n      iex> init(13)\n      #Angle<13°>\n  \"\"\"\n\n\n\n\"\"\"\n  Initialize an Angle from integer `d` degrees, optionally followed by integer\n  `m` minutes.\n\n  ## Examples\n\n      iex> init(13, 30)\n      #Angle<13° 30′>\n  \"\"\"\n\n\n\n\"\"\"\n  Initialize an Angle from integer `d` degrees, followed by integer\n  `m` minutes and `s` seconds.\n\n  ## Examples\n\n      iex> init(13, 30, 45)\n      #Angle<13° 30′ 45″>\n  \"\"\"\n\n\n\n\n", 797, -1)
        lib/credo/code/heredocs.ex:253: Credo.Code.Heredocs.pad_replaced_heredoc/2
        lib/credo/code/heredocs.ex:156: Credo.Code.Heredocs.parse_heredoc/5
        lib/credo/check/readability/max_line_length.ex:52: Credo.Check.Readability.MaxLineLength.run/2
        lib/credo/check/runner.ex:168: Credo.Check.Runner.do_run_check_on_single_source_file/3
        lib/credo/check/runner.ex:161: Credo.Check.Runner.run_check_on_single_source_file/3
        (elixir) lib/enum.ex:1336: Enum."-map/2-lists^map/1-0-"/2
        (elixir) lib/enum.ex:1336: Enum."-map/2-lists^map/1-0-"/2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.