3

Erlang’s Halloween 👻

 2 years ago
source link: https://medium.com/erlang-battleground/erlangs-halloween-ceffd057dc8b
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Erlang’s Halloween 👻

Some dead code that not even Dialyzer can find

It’s relatively unusual to watch me talking about the shortcomings or limitations of such an excellent tool as Dialyzer. Still, today I found some very dead code blocks that slipped through its world-famous success type analysis.

So, since Halloween is close, let’s raise some dead code from its grave!

Zombie Programmer
Zombie Programmer
By Zombie Programmer — A great blog. Check it out!

When Dialyzer produces a Warning

So, in the past, I’ve seen code like this one…

warn(X) ->
{just, X};
warn(6) ->
{six, 6}.

Trigger warnings from both the compiler and Dialyzer like the following ones…

===> Compiling my_app
src/el.erl:8: Warning: this clause for warn/1 cannot match because a previous clause at line 6 always matches===> Dialyzer starting, this may take a while...
src/el.erl
Line 8: The pattern 6 can never match since previous clauses completely covered the type any()

Lovely! The second clause in that function is clearly dead code (i.e., it will never be evaluated) since 6 matches X and thus falls through the first clause when provided as input. This is one of my favorite Dialyzer warnings ever. It’s clear and actionable. It provides a proper context with line numbers and what-not. It’s awesome.

When Dialyzer doesn’t produce a Warning

But… Today I found a piece of dead code that was very, very similar and yet… Dialyzer and the Compiler were both utterly silent about it. Let me show you…

warn(X, X) ->
{just, X};
warn(6, 6) ->
{six, 6}.

Have you spotted the difference?
Let me show you yet another example…

warn(X, Y) ->
{just, X and Y};
warn(6, 6) ->
{six, 6}.

This one does produce the expected warnings…

===> Compiling my_app
src/el.erl:8: Warning: this clause for warn/2 cannot match because a previous clause at line 6 always matches===> Dialyzer starting, this may take a while...
src/el.erl
Line 8: The pattern <6, 6> can never match since previous clauses completely covered the type <_,_>

What’s Going on Here?

Well… It seems that not every clause that cannot match can be detected by either the compiler or Dialyzer. Sadly, those that include bounded variables (like the second X in warn/2) or guards or any other semantic constructs are outside the scope of these detection tools.

So, if you have functions like that, the compiler and Dialyzer won’t detect your dead code, and it may end up living as a ghost for a long, long time.

Well, in that case, who you’re gonna call?

No, not the Ghost Busters

Test Coverage, of course!

If you have a good test coverage policy in place (maybe one like the

had where we demanded 100% coverage everywhere, perhaps a lighter one), you should immediately see that no test can ever cover those clauses. Then, you can remove them.

As usual, if you like my blog, you can buy me a coffee and if you want to participate, we’re always looking for new writers for Erlang Battleground. Hit me up on Twitter, Facebook, Slack, etc.…

Bonus Track

While writing this article, I checked several other scenarios to see what was detectable and what was not. You can see the module I used for that below…

And these are the only warnings that I get with it…

===> Analyzing applications...

===> Compiling lapp
src/el.erl:8: Warning: this clause for warn/2 cannot match because a previous clause at line 6 always matches
src/el.erl:19: Warning: this clause cannot match because a previous clause at line 17 always matches
src/el.erl:25: Warning: this clause cannot match because a previous clause at line 23 always matches
src/el.erl:27: Warning: this clause cannot match because a previous clause at line 23 always matches===> Dialyzer starting, this may take a while...
src/el.erl
Line 8: The pattern <6, 6> can never match since previous clauses completely covered the type <_,_>
Line 19: The pattern <> can never match since previous clauses completely covered the type <>
Line 25: The pattern 6 can never match since previous clauses completely covered the type any()
Line 27: The variable _ can never match since previous clauses completely covered the type any()

If you find more scenarios worth mentioning, please write a comment about them below.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK