Repository: jeremyjh/dialyxir (elixir)

Issue:
Add a `plt_ignore_app(s)` option
### Current behavior

Some apps ([Saxy is an example](https://github.com/qcam/saxy/issues/39)) take a very large amount of time to pass the dialyzer for certain versions of OTP. If we can pinpoint which dependency takes so long, we should be able to ignore only that dependency.

Currently if I want to ignore a single dependency, I need to make a configuration that looks like this:

```elixir
   # mix.exs, project section
  ...
  dialyzer: [
    ignore_warnings: ".dialyzer_ignore.exs",
    plt_apps: [
      :erts,
      :kernel,
      :stdlib,
      ... # all other dependencies except the one that hangs
    ]
  ]
```

### Expected behavior

I'd like to be able to just say the app I'd like to ignore:

```elixir
   # mix.exs, project section
  ...
  dialyzer: [
    ignore_warnings: ".dialyzer_ignore.exs",
    plt_ignore_apps: [:saxy]
  ]
```

I imagine that there's an easy place to put a `plt_apps -- ignored_apps` in the setup. If you like the idea, I'd be happy to send in a PR

Pull request description:
Add plt_ignore_apps option to ignore dependencies
Closes #295.

The documentation will need a small change as well. I'll leave that to you

Patch:
diff --git a/README.md b/README.md
index 7ca4dcf..2ad0fc1 100644
--- a/README.md
+++ b/README.md
@@ -140,6 +140,8 @@ The apps included by default are `[ :erts, :kernel, :stdlib, :crypto]`.
 
 If you don't want to include the default apps you can specify a `:plt_apps` key and list there only the apps you want in the PLT. Using this option will mean dependencies are not added automatically (see below). If you want to just add an application to the list of defaults and dependencies you can use the `:plt_add_apps` key.
 
+If you want to ignore a specific dependency, you can specify it in the `:plt_ignore_apps` key.
+
 #### Dependencies
 
 OTP application dependencies are (transitively) added to your PLT by default. The applications added are the same as you would see displayed with the command `mix app.tree`. There is also a `:plt_add_deps` option you can set to control the dependencies added. The following options are supported:
@@ -149,7 +151,7 @@ OTP application dependencies are (transitively) added to your PLT by default. Th
   * :app_tree - Transitive OTP application dependencies e.g. `mix app.tree` (default)
 
 
-The example below changes the default to include only direct OTP dependencies, and adds another specific dependency to the list. This can be helpful if a large dependency tree is creating memory issues and only some of the transitive dependencies are required for analysis.
+The example below changes the default to include only direct OTP dependencies, adds another specific dependency, and removes a dependency from the list. This can be helpful if a large dependency tree is creating memory issues and only some of the transitive dependencies are required for analysis.
 
 ```elixir
 def project do
@@ -157,7 +159,11 @@ def project do
     app: :my_app,
     version: "0.0.1",
     deps: deps,
-    dialyzer: [plt_add_deps: :apps_direct, plt_add_apps: [:wx]]
+    dialyzer: [
+      plt_add_deps: :apps_direct,
+      plt_add_apps: [:wx],
+      plt_ignore_apps: [:mnesia]
+    ]
   ]
 end
 ```
diff --git a/lib/dialyxir/project.ex b/lib/dialyxir/project.ex
index 330b16c..2cf219c 100644
--- a/lib/dialyxir/project.ex
+++ b/lib/dialyxir/project.ex
@@ -45,7 +45,7 @@ defmodule Dialyxir.Project do
     Mix.Tasks.Deps.Loadpaths.run([])
     # compile & load current project paths
     Mix.Project.compile([])
-    apps = plt_apps() || plt_add_apps() ++ include_deps()
+    apps = plt_apps() || plt_add_apps() ++ (include_deps() -- plt_ignore_apps())
 
     apps
     |> Enum.sort()
@@ -223,6 +223,7 @@ defmodule Dialyxir.Project do
 
   defp plt_apps, do: dialyzer_config()[:plt_apps] |> load_apps()
   defp plt_add_apps, do: dialyzer_config()[:plt_add_apps] || [] |> load_apps()
+  defp plt_ignore_apps, do: dialyzer_config()[:plt_ignore_apps] || []
 
   defp load_apps(nil), do: nil
 
diff --git a/lib/mix/tasks/dialyzer.ex b/lib/mix/tasks/dialyzer.ex
index 1b22682..c5fcc16 100644
--- a/lib/mix/tasks/dialyzer.ex
+++ b/lib/mix/tasks/dialyzer.ex
@@ -75,6 +75,9 @@ defmodule Mix.Tasks.Dialyzer do
   * `dialyzer: :plt_add_apps` - applications to include
   *in addition* to the core applications and project dependencies.
 
+  * `dialyzer: :plt_ignore_apps` - applications to ignore from the list of core
+  applications and dependencies.
+
   * `dialyzer: :plt_apps` - a list of applications to include that will replace the default,
   include all the apps you need e.g.
 


Test patch:
diff --git a/test/dialyxir/project_test.exs b/test/dialyxir/project_test.exs
index 226d751..d5f5f74 100644
--- a/test/dialyxir/project_test.exs
+++ b/test/dialyxir/project_test.exs
@@ -102,6 +102,14 @@ defmodule Dialyxir.ProjectTest do
     end)
   end
 
+  test "App list for :plt_ignore_apps does not contain the ignored dependency" do
+    in_project(:ignore_apps, fn ->
+      apps = Project.cons_apps()
+
+      refute Enum.member?(apps, :logger)
+    end)
+  end
+
   test "Core PLT files located in mix home by default" do
     in_project(:default_apps, fn ->
       assert String.contains?(Project.erlang_plt(), Mix.Utils.mix_home())
diff --git a/test/fixtures/ignore_apps/mix.exs b/test/fixtures/ignore_apps/mix.exs
new file mode 100644
index 0000000..2ecbd5d
--- /dev/null
+++ b/test/fixtures/ignore_apps/mix.exs
@@ -0,0 +1,22 @@
+defmodule IgnoreApps.Mixfile do
+  use Mix.Project
+
+  def project do
+    [
+      app: :ignore_apps,
+      version: "0.1.0",
+      deps: deps(),
+      dialyzer: [
+        plt_ignore_apps: [:logger]
+      ]
+    ]
+  end
+
+  def application do
+    [applications: [:logger, :public_key]]
+  end
+
+  defp deps do
+    []
+  end
+end