-
Notifications
You must be signed in to change notification settings - Fork 74
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
Fix: Dialyzer #74
Comments
@filipeherculano first of all, thanks a lot for this great contribution 😄 !! I've replied to your PR, looks very good overall, just let you a couple of silly comments and also run the CI tests before, just to ensure the CI runs without errors. Thanks 😉 !! |
Could you bump or commit a tag to v1.2.2 @cabol so I can incorporate the change safely on my project? Thanks again! |
Just published version bump |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The problem
When running Dialyzer with the code snippet below, it results errors of unused guards after all the quotes have been compiled.
The reason
When the function caching_action is compiled, the IR (Intermediate Representation), that is generated, has control flow code that will never run (as the error Dialyzer error says: "The guard clause: (...) can never succeed."). But when does it happen?
After careful testing on my working code, the following scenarios occurred:
When passing the
key:
value to the decorator, when it hits line 244, the generated code is acase
statement, which has both true or false flows. The false or nil case will never happen, since at compilation time its known by the Dialyzer that the code will always get theunquote(key_var)
value. We have below a IR of the line 244. This also happens if we don't use akey:
when calling the decorator.This also happens, later in the quote, when line 260-264 are compiled to (and I'm assuming that is the same on lines 290-293):
On both cases the problem is the same (as mentioned above). On this case, the default match function will always evaluate to
true
, so the Dialyzer will bitch about it. This will not be true if we create our own matching functions and pass them with variables (this will prevent that at compilation time the Dialyzer figures out useless control flow code). But since this is an optional functionality, it shouldn't work as an essential one.PS: You can look at the IR by running inside a iex shell:
f = '<PATH_TO_BEAM>' result = :beam_lib.chunks(f,[:abstract_code]) {:ok,{_,[{:abstract_code,{_,ac}}]}} = result IO.puts :erl_prettypr.format(:erl_syntax.form_list(ac))
The proposed solution
My proposed solution is to create a way that those IR will not have useless code when using the default values of
match
, or simply by using or not thekey
. To do this I replaced the usage ofKernel.||
toKeyword.get/3
, but just enhancing the already usedKeyword.get
on line 231. With this, at compile time, the generated IR code is now:Which has no unused control flow code. The second fix was to replace the
if-else
statement with acond
statement. This will be a little trickier, since credo has a bad temper with only one clause on acond
. With this replacement, now the second IR code is:Which also has comparisons, but it does compare equality with nil or false, but rather the opposite. I created a PR with this solution, since it was rather fast to code.
Waiting for reviews. Cheers!
The text was updated successfully, but these errors were encountered: