-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Closed
Description
Elixir and Erlang/OTP versions
Erlang/OTP 28 [erts-16.1] [source] [64-bit] [smp:28:28] [ds:28:28:10] [async-threads:1] [jit:ns]
Elixir 1.19.0 (compiled with Erlang/OTP 28)
Operating system
Debian 13
Current behavior
I was trying to track down why mjml_eex (https://github.com/akoutmos/mjml_eex) started throwing up Dialyzer errors on 1.19, and got it down to a minimal repro using only stdlib.
The following file (incorrectly) reports a dialyzer error on 1.19 but not on 1.18:
defmodule EExDialyzerBug do
@moduledoc """
Minimal reproduction for Elixir 1.19 dialyzer issue with EEx.compile_string/2.
Running `elixir --version` shows: Elixir 1.19.0 (compiled with Erlang/OTP 28)
## Issue
EEx.compile_string/2 accepts [compile_opt] which includes {:engine, module()},
but internally passes all options to tokenize/2 which only accepts [tokenize_opt].
1.19's enhanced type checking detects this spec inconsistency.
## To reproduce
1. Create a new Mix project: `mix new test_eex_bug`
2. Add dialyxir to mix.exs deps:
{:dialyxir, "~> 1.4", only: [:dev, :test], runtime: false}
3. Add to mix.exs project/0 function:
dialyzer: [plt_add_apps: [:eex]]
4. Place this file in lib/
5. Run: `mix deps.get && mix compile && mix dialyzer`
## Expected result
No dialyzer warnings (the code works correctly at runtime).
## Actual result
Dialyzer reports:
"Function MACRO-__using__/2 has no local return."
"The call 'Elixir.EEx':compile_string(...,[{'engine', ...}]) will never return
since it differs in the 2nd argument from the success typing arguments"
"""
defmacro __using__(_opts) do
# This call inside the macro triggers the dialyzer error
template_ast = EEx.compile_string("<p>Hello <%= @name %>!</p>", engine: EEx.SmartEngine)
quote do
def render(assigns) do
var!(assigns) = assigns
unquote(template_ast)
end
end
end
end
defmodule EExDialyzerBug.User do
use EExDialyzerBug
end
On 1.19, shows:
lib/dialyzer_eex_bug.ex:35:24: The call 'Elixir.EEx':compile_string(<<60,112,62,72,101,108,108,111,32,60,37,61,32,64,110,97,109,101,32,37,62,33,60,47,112,62>>,[{'engine', 'Elixir.EEx.SmartEngine'}]) will never return since it differs in the 2nd argument from the success typing arguments: (binary(),[{'column',non_neg_integer()} | {'file',binary()} | {'indentation',non_neg_integer()} | {'line',non_neg_integer()} | {'trim',boolean()}])
This is because EEx.compile_string/2 accepts [compile_opt] which includes {:engine, module()}, but internally passes all options to tokenize/2 which only accepts [tokenize_opt].
Expected behavior
The same code on 1.18.4, reports:
Total errors: 0, Skipped: 0, Unnecessary Skips: 0
done in 0m1.5s
done (passed successfully)
Metadata
Metadata
Assignees
Labels
No labels