Gadget

Fernando "Brujo" Benavides

Inaka

Erlang Factory

March, 2015

The Ultimate Code-Checking Machine

About Me

  • argentino: 34 years
  • programmer: 24 years
  • in the industry: 15 years
  • erlanger: 7 years
  • open-source contributor: 6 years
  • CTO @ Inaka: 1 year

About Inaka

  • Started in 2010.
  • Acquired by Erlang Solutions in 2014.
  • We build end-to-end applications with
    Erlang, Ruby, Web, iOS and Android components.
  • We develop highly concurrent application
    servers such as Whisper and TigerText.
  • We're active contributors to the
    open-source community.

How good is your code?

Does it Compile?

Without warnings?

Which Warnings?

+warn_unused_vars +warn_export_all
+warn_shadow_vars +warn_unused_import
+warn_unused_function +warn_exported_vars
+warn_bif_clash +warn_unused_record
+warn_deprecated_function +warn_export_vars
+strict_validation +warn_obsolete_guard
+warn_missing_spec +warn_untyped_record

Does it have loose ends?

Have you checked with xref?

Running Xref Manually

{ok, Xref} = xref:start(my_xref),
ok =
    xref:set_library_path(
        Xref, code:get_path()),
{ok, _Modules} =
    xref:add_directory(Xref, "ebin"),
{ok, Results} =
    xref:analyze(
        Xref, undefined_function_calls),
stopped = xref:stop(Xref),
Results.

Running Xref Manually

{ok, Xref} = xref:start(my_xref),
ok =
    xref:set_library_path(
        Xref, code:get_path()),
{ok, _Modules} =
    xref:add_directory(Xref, "ebin"),
{ok, Results} =
    xref:analyze(
        Xref, undefined_function_calls),
stopped = xref:stop(Xref),
Results.
{ok, Xref} = xref:start(my_xref),

.

.

.

.

.

.

.

.

.

Running Xref Manually

{ok, Xref} = xref:start(my_xref),
ok =
    xref:set_library_path(
        Xref, code:get_path()),
{ok, _Modules} =
    xref:add_directory(Xref, "ebin"),
{ok, Results} =
    xref:analyze(
        Xref, undefined_function_calls),
stopped = xref:stop(Xref),
Results.
ok =
    xref:set_library_path(
        Xref, code:get_path()),

.

.

.

.

.

.

.

.

.

Running Xref Manually

{ok, Xref} = xref:start(my_xref),
ok =
    xref:set_library_path(
        Xref, code:get_path()),
{ok, _Modules} =
    xref:add_directory(Xref, "ebin"),
{ok, Results} =
    xref:analyze(
        Xref, undefined_function_calls),
stopped = xref:stop(Xref),
Results.
{ok, _Modules} =
    xref:add_directory(Xref, "ebin"),

.

.

.

.

.

.

.

.

.

Running Xref Manually

{ok, Xref} = xref:start(my_xref),
ok =
    xref:set_library_path(
        Xref, code:get_path()),
{ok, _Modules} =
    xref:add_directory(Xref, "ebin"),
{ok, Results} =
    xref:analyze(
        Xref, undefined_function_calls),
stopped = xref:stop(Xref),
Results.
{ok, Results} =
    xref:analyze(
        Xref, undefined_function_calls),

.

.

.

.

.

.

.

.

.

Running Xref Manually

{ok, Xref} = xref:start(my_xref),
ok =
    xref:set_library_path(
        Xref, code:get_path()),
{ok, _Modules} =
    xref:add_directory(Xref, "ebin"),
{ok, Results} =
    xref:analyze(
        Xref, undefined_function_calls),
stopped = xref:stop(Xref),
Results.
stopped = xref:stop(Xref),

.

.

.

.

.

.

.

.

.

Running Xref Manually

[{{my_module_1,bad,0},
  {my_module_1,my_function_1,0}},
 {{my_module_1,bad,1},
  {other_module,my_function_2,1}},
 {{my_module_1,bad,1},
  {my_module_2,my_function_3,0}},
 {{my_module_2,bad,0},
  {my_module_2,my_function_4,0}},
 {{my_module_2,bad,1},
  {other_module,my_function_2,0}},
 {{my_module_2,bad,1},
  {other_module,my_function_2,1}}]

Running Xref Manually

[{{my_module_1,bad,0},
  {my_module_1,undefined_here,0}},
 {{my_module_1,bad,1},
  {other_module,undefined_somewhere_else,1}},
 {{my_module_1,bad,1},
  {my_module_2,undefined_there,0}},
 {{my_module_2,bad,0},
  {my_module_2,undefined_here,0}},
 {{my_module_2,bad,1},
  {other_module,undefined_somewhere_else,0}},
 {{my_module_2,bad,1},
  {other_module,undefined_somewhere_else,1}}]

.

.

.

.

.

.

.

.

.

.

{{my_module_2,bad,0},
 {my_module_2,my_function_4,0}},

With Rebar

$ rebar xref

Rebar xref

my_module_1.erl:5: Warning bad/0 calls undefined
 function my_module_1:my_function_1/0
my_module_1.erl:9: Warning bad/1 calls undefined
 function other_module:my_function_2/1
my_module_1.erl:9: Warning bad/1 calls undefined
 function my_module_2:my_function_3/0
my_module_2.erl:5: Warning bad/0 calls undefined
 function my_module_2:my_function_4/0
my_module_2.erl:9: Warning bad/1 calls undefined
 function other_module:my_function_2/0
my_module_2.erl:9: Warning bad/1 calls undefined
 function other_module:my_function_2/1

Rebar xref

my_module_1.erl:5: Warning bad/0 calls undefined
 function my_module_1:my_function_1/0
my_module_1.erl:9: Warning bad/1 calls undefined
 function other_module:my_function_2/1
my_module_1.erl:9: Warning bad/1 calls undefined
 function my_module_2:my_function_3/0
my_module_2.erl:5: Warning bad/0 calls undefined
 function my_module_2:my_function_4/0
my_module_2.erl:9: Warning bad/1 calls undefined
 function other_module:my_function_2/0
my_module_2.erl:9: Warning bad/1 calls undefined
 function other_module:my_function_2/1

.

.

.

.

.

.

.

.

.

.

my_module_2.erl:5: Warning bad/0 calls undefined
 function my_module_2:my_function_4/0

With erlang.mk

$ make xref

SOON

Xref Runner

1> xref_runner:check().
[#{check => undefined_function_calls,
   filename => "src/xref/my_module_1.erl",
   line => 5,
   source => {my_module_1,bad,0},
   target => {my_module_1,my_function_1,0}},
 #{check => undefined_function_calls,
   filename => "src/xref/my_module_1.erl",
   line => 9,
   source => {my_module_1,bad,1},
   target => {other_module,my_function_2,1}},
 …]

Xref Runner

1> xref_runner:check().
[#{check => undefined_function_calls,
   filename => "src/xref/my_module_1.erl",
   line => 5,
   source => {my_module_1,bad,0},
   target => {my_module_1,my_function_1,0}},
 #{check => undefined_function_calls,
   filename => "src/xref/my_module_1.erl",
   line => 9,
   source => {my_module_1,bad,1},
   target => {other_module,my_function_2,1}},
 …]

.

.

.

.

.

.

.

.

.

.

[#{check => undefined_function_calls,
   filename => "src/xref/my_module_1.erl",
   line => 5,
   source => {my_module_1,bad,0},
   target => {my_module_1,my_function_1,0}},

How do your functions behave?

Do you dialyze your code?

Dialyzer   is   SLOW

  • Build PLT < 5mins

  • Run Dialyzer < 5secs

was

Dialyzer is HARD to Understand

Function f/2 has no local return

Function i/1 will never be called

But Dialyzer is
extremely effective

  • Times don't matter if you're not the one running dialyzer

  • Warnings get clearer over time

How does your code look?

Have you ever used...

  • erl_tidy?

  • TidiEr?

Elvis rocks!

Elvis

Loading files...
Loading src/org.erl
Loading src/org_warning.erl
Applying rules...
# src/org.erl [FAIL]
  - line_length
    - Line 26 is too long: "-spec this_long_long_function_name(a_very_long_module_name:this_type_is_just_huge()) -> well_its_long()".
# src/org_warning.erl [OK]
…

$ elvis rock or $ make elvis # erlang.mk

Keeping your code in shape

Source Control

But you can't
dialyze
compile
xref

a pull request!!

Gadget!!

gadget.inakalabs.com

Commenting on your PRs

The Gadgets

Compiler

with Makefile or rebar

Xref

with xref_runner

xref.config for configuration

Dialyzer

with erlang.mk

FOR NOW

Elvis

elvis.config for configuration

Github Integration

with erlang-github

In the future…

  • erl_tidy

  • TidiEr?

  • …others?

JUST FOR

OPEN-SOURCE

References

  • gadget.inakalabs.com
  • inaka.github.io
  • inaka.net[/blog]
  • about.me/elbrujohalcon

Thank You!

elbrujohalcon@inaka.net

elbrujohalcon

@elbrujohalcon