Add elixir 1.19.x compatibility#107
Conversation
|
Thank you! |
lib/mimic/module.ex
Outdated
| defp generate_mimic_struct(module) do | ||
| if function_exported?(module, :__info__, 1) && module.__info__(:struct) != nil do | ||
| struct_info = module.__info__(:struct) | ||
| struct_info = module.__info__(:struct) || [] |
There was a problem hiding this comment.
Given that the if checks for module.__info__(:struct) != nil how could this || [] be reached? 🤔
There was a problem hiding this comment.
Hey @edgurgel !
That's a great question.
So, if we change nothing, the compiler gives us:
warning: incompatible value given to for-comprehension:
%{field: field} <- struct_info
it has type:
nil or list(%{default: if_set(term()), field: atom()})
but expected a type that implements the Enumerable protocol.
You either passed the wrong value or you must:
1. convert the given value to an Enumerable explicitly
2. implement the Enumerable protocol
where "struct_info" was given the type:
# type: nil or list(%{default: if_set(term()), field: atom()})
# from: lib/mimic/module.ex:132:21
struct_info = module.__info__(:struct)
hint: the Enumerable protocol is implemented for the following types:
dynamic(
%Date.Range{} or %File.Stream{} or %GenEvent.Stream{} or %HashDict{} or %HashSet{} or
%IO.Stream{} or %MapSet{} or %Range{} or %Stream{}
) or empty_list() or fun() or non_empty_list(term(), term()) or non_struct_map()
typing violation found at:
│
137 │ for %{field: field} <- struct_info,
│ ~
│
└─ lib/mimic/module.ex:137:31: Mimic.Module.generate_mimic_struct/1
I assumed that the compiler can't know if module.__info__(:struct) is a pure function. Perhaps calling it the second time, could theoretically bear a different result of a different type.
After you commented here I thought "I'll just refactor it so that we only call module.__info__(:struct) once".
However this had some very weird results of its own, I ended up in a bit of a rabbit hole. I came up with a couple more implementations, that I would have expected to work, but they still raised warnings. As well as one that I wouldn't have expected to work, that didn't raise warnings. If you're interested, here's a list of commits with different versions and their outcome (in the commit message).
TL;DR: there is a mega-issue in the Elixir repo, relating to false-positive warnings. The work pending includes a couple of unsuccessful rewrites that I've tried (with case and guards).
👉 I have added a new commit with a version that calls module.__info__(:struct).
It's not the nicest way, but it perhaps a bit less confusing than others (that don't raise the warning). Let me know what you think!
There was a problem hiding this comment.
Actually, I found an even simpler way:
Replaced the list comprehension with an Enum.map. No warnings, no hacky stuff.
There was a problem hiding this comment.
Oh I see! Thanks for taking the time to explain 🙇
|
2.2.0 is out 🎉 |
This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [mimic](https://hex.pm/packages/mimic) ([source](https://github.com/edgurgel/mimic)) | dev | major | `~> 1.12` → `~> 2.0` | --- ### Release Notes <details> <summary>edgurgel/mimic (mimic)</summary> ### [`v2.3.0`](https://github.com/edgurgel/mimic/blob/HEAD/CHANGELOG.md#230-2026-01-17) [Compare Source](edgurgel/mimic@v2.2.0...v2.3.0) - fix: namespace auto setup verify\_on\_exit! by [@​PragTob](https://github.com/PragTob) in [#​111](edgurgel/mimic#111) - feat: prevent global mode in async tests by [@​rbino](https://github.com/rbino) in [#​110](edgurgel/mimic#110) ### [`v2.2.0`](https://github.com/edgurgel/mimic/blob/HEAD/CHANGELOG.md#220-2025-11-18) [Compare Source](edgurgel/mimic@v2.1.1...v2.2.0) - Add elixir 1.19.x compatibility by [@​Geekfish](https://github.com/Geekfish) in [#​107](edgurgel/mimic#107) ### [`v2.1.1`](https://github.com/edgurgel/mimic/blob/HEAD/CHANGELOG.md#211-2025-09-20) [Compare Source](edgurgel/mimic@v2.1.0...v2.1.1) - Don't remove behaviour\_info/1 from behaviour modules by [@​escobera](https://github.com/escobera) in [#​105](edgurgel/mimic#105) ### [`v2.1.0`](https://github.com/edgurgel/mimic/blob/HEAD/CHANGELOG.md#210-2025-08-31) [Compare Source](edgurgel/mimic@v2.0.2...v2.1.0) - feat: Usage rules by [@​pcharbon70](https://github.com/pcharbon70) in [#​102](edgurgel/mimic#102) - fix: define replaced Elixir module macros using defmacro by [@​yastanotheruser](https://github.com/yastanotheruser) in [#​104](edgurgel/mimic#104) ### [`v2.0.2`](https://github.com/edgurgel/mimic/blob/HEAD/CHANGELOG.md#202-2025-08-12) [Compare Source](edgurgel/mimic@v2.0.1...v2.0.2) - fix: Mimic.Module compilation when no options are stored. [#​101](edgurgel/mimic#101) ### [`v2.0.1`](https://github.com/edgurgel/mimic/blob/HEAD/CHANGELOG.md#201-2025-08-08) [Compare Source](edgurgel/mimic@v2.0.0...v2.0.1) - Bump `ham` requirement ### [`v2.0.0`](https://github.com/edgurgel/mimic/blob/HEAD/CHANGELOG.md#200-2025-07-13) [Compare Source](edgurgel/mimic@v1.12.0...v2.0.0) #### Breaking changes The code below would call the original `Calculator.add/2` when all expectations were fulfilled. ```elixir Calculator |> expect(:add, fn _, _ -> :expected1 end) |> expect(:add, fn _, _ -> :expected2 end) assert Calculator.add(1, 1) == :expected1 assert Calculator.add(1, 1) == :expected2 assert Calculator.add(1, 1) == 2 ``` Now with Mimic 2 this will raise: ```elixir Calculator |> expect(:add, fn _, _ -> :expected1 end) |> expect(:add, fn _, _ -> :expected2 end) assert Calculator.add(1, 1) == :expected1 assert Calculator.add(1, 1) == :expected2 Calculator.add(1, 1) # Will raise error because more than 2 calls to Calculator.add were made and there is no stub # ** (Mimic.UnexpectedCallError) Calculator.add/2 called in process #PID<.*> but expectations are already fulfilled ``` If there is a stub the stub will be called instead. This behaviour is the same as before. ```elixir Calculator |> expect(:add, fn _, _ -> :expected1 end) |> expect(:add, fn _, _ -> :expected2 end) |> stub(:add, fn _, _ -> :stub end) assert Calculator.add(1, 1) == :expected1 assert Calculator.add(1, 1) == :expected2 assert Calculator.add(1, 1) == :stub ``` Which means that if someone wants to keep the original behaviour on Mimic 1.\* just do the following: ```elixir Calculator |> expect(:add, fn _, _ -> :expected1 end) |> expect(:add, fn _, _ -> :expected2 end) |> stub(:add, fn x, y -> call_original(Calculator, :add, [x, y]) end) assert Calculator.add(1, 1) == :expected1 assert Calculator.add(1, 1) == :expected2 assert Calculator.add(1, 1) == 2 ``` This way once all expectations are fulfilled the original function is called again. ### [`v1.12.0`](https://github.com/edgurgel/mimic/releases/tag/v1.12.0): Mimic 1.12.0 [Compare Source](edgurgel/mimic@v1.11.2...v1.12.0) #### What's Changed - Mimic.calls/3 to list args from each call by [@​brentjanderson](https://github.com/brentjanderson) in [#​94](edgurgel/mimic#94) #### New Contributors - [@​brentjanderson](https://github.com/brentjanderson) made their first contribution in [#​94](edgurgel/mimic#94) **Full Changelog**: <edgurgel/mimic@v1.11.2...v1.12.0> </details> --- ### Configuration 📅 **Schedule**: Branch creation - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) in timezone Pacific/Auckland, Automerge - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) in timezone Pacific/Auckland. 🚦 **Automerge**: Disabled because a matching PR was automerged previously. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [x] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNTYuMiIsInVwZGF0ZWRJblZlciI6IjQyLjg3LjAiLCJ0YXJnZXRCcmFuY2giOiJtYWluIiwibGFiZWxzIjpbInJlbm92YXRlIl19--> Reviewed-on: https://harton.dev/james/pca9641/pulls/116 Co-authored-by: Renovate Bot <bot@harton.nz> Co-committed-by: Renovate Bot <bot@harton.nz>
1.19in CIdefaultandrequiredare now returned)