From f1d81c87d832c805a90a8dceca247cecaad803ab Mon Sep 17 00:00:00 2001
From: Maria Christakis <christakismaria@gmail.com>
Date: Sun, 17 Oct 2010 18:20:53 +0300
Subject: dialyzer: Add support for multiple PLTs

This new feature is able to take multiple PLTs, merge them during the
start of the analysis, and work from there. This works provided that
the PLTs do not have a module with the same name appearing in more
than one PLT.

The PLTs are created in the usual way:

    dialyzer --build_plt --output_plt PLT_1  FILES_TO_INCLUDE
    ...
    dialyzer --build_plt --output_plt PLT_N  FILES_TO_INCLUDE

and then can be used in either of the following ways:

    dialyzer  FILES_TO_ANALYZE  --plts PLT_1 ... PLT_N
or:
    dialyzer --plts PLT_1 ... PLT_N -- FILES_TO_ANALYZE

(Note the -- delimiter in the second case)
---
 lib/dialyzer/doc/manual.txt            |  12 +++-
 lib/dialyzer/src/dialyzer.erl          |  20 ++++--
 lib/dialyzer/src/dialyzer.hrl          |   2 +-
 lib/dialyzer/src/dialyzer_cl.erl       | 120 ++++++++++++++++++++++-----------
 lib/dialyzer/src/dialyzer_cl_parse.erl |  44 ++++++++----
 lib/dialyzer/src/dialyzer_gui.erl      |  14 ++--
 lib/dialyzer/src/dialyzer_gui_wx.erl   |  14 ++--
 lib/dialyzer/src/dialyzer_options.erl  |  19 ++++--
 lib/dialyzer/src/dialyzer_plt.erl      |  65 ++++++++++++++++++
 9 files changed, 231 insertions(+), 79 deletions(-)

(limited to 'lib')

diff --git a/lib/dialyzer/doc/manual.txt b/lib/dialyzer/doc/manual.txt
index 470ddd6c73..d9cb52f722 100644
--- a/lib/dialyzer/doc/manual.txt
+++ b/lib/dialyzer/doc/manual.txt
@@ -123,9 +123,10 @@ The exit status of the command line version is:
 
 
 Usage: dialyzer [--help] [--version] [--shell] [--quiet] [--verbose]
-		[-pa dir]* [--plt plt] [-Ddefine]* [-I include_dir]* 
-		[--output_plt file] [-Wwarn]* [--src]  [--gui | --wx]
-		[files_or_dirs] [-r dirs] [--apps applications] [-o outfile]
+		[-pa dir]* [--plt plt] [--plts plts] [-Ddefine]*
+                [-I include_dir]* [--output_plt file] [-Wwarn]*
+                [--src]  [--gui | --wx] [files_or_dirs] [-r dirs]
+                [--apps applications] [-o outfile]
 		[--build_plt] [--add_to_plt] [--remove_from_plt]
 		[--check_plt] [--no_check_plt] [--plt_info] [--get_warnings]
                 [--no_native]
@@ -167,6 +168,10 @@ Options:
    --plt plt
        Use the specified plt as the initial plt (if the plt was built 
        during setup the files will be checked for consistency)
+   --plts plts
+       Merges the specified plts to create the initial plt -- requires
+       that the plts are disjoint (i.e., do not have any module
+       appearing in more than one plt)
    -Wwarn
        A family of options which selectively turn on/off warnings
        (for help on the names of warnings use dialyzer -Whelp)
@@ -294,6 +299,7 @@ Option   :: {files,          [Filename :: string()]}
 	  | {defines,        [{Macro :: atom(), Value :: term()}]}
 	  | {from,           src_code | byte_code} %% Defaults to byte_code
 	  | {init_plt,       FileName :: string()} %% If changed from default
+          | {plts,           [FileName :: string()]} %% If changed from default
 	  | {include_dirs,   [DirName :: string()]} 
 	  | {output_file,    FileName :: string()}
 	  | {output_plt,     FileName :: string()}
diff --git a/lib/dialyzer/src/dialyzer.erl b/lib/dialyzer/src/dialyzer.erl
index b4161ea194..471f9fccd2 100644
--- a/lib/dialyzer/src/dialyzer.erl
+++ b/lib/dialyzer/src/dialyzer.erl
@@ -106,27 +106,35 @@ cl_print_plt_info(Opts) ->
       end,
   doit(F).
 
-print_plt_info(#options{init_plt = PLT, output_file = OutputFile}) ->
+print_plt_info(#options{init_plts = PLTs, output_file = OutputFile}) ->
+  PLTInfo = get_plt_info(PLTs),
+  do_print_plt_info(PLTInfo, OutputFile).
+
+get_plt_info([PLT|PLTs]) ->
   String =
     case dialyzer_plt:included_files(PLT) of
       {ok, Files} ->
-	io_lib:format("The PLT ~s includes the following files:\n~p\n",
+	io_lib:format("The PLT ~s includes the following files:\n~p\n\n",
 		      [PLT, Files]);
       {error, read_error} ->
-	Msg = io_lib:format("Could not read the PLT file ~p\n", [PLT]),
+	Msg = io_lib:format("Could not read the PLT file ~p\n\n", [PLT]),
 	throw({dialyzer_error, Msg});
       {error, no_such_file} ->
-	Msg = io_lib:format("The PLT file ~p does not exist\n", [PLT]),
+	Msg = io_lib:format("The PLT file ~p does not exist\n\n", [PLT]),
 	throw({dialyzer_error, Msg})
     end,
+  String ++ get_plt_info(PLTs);
+get_plt_info([]) -> "".
+
+do_print_plt_info(PLTInfo, OutputFile) ->
   case OutputFile =:= none of
     true ->
-      io:format("~s", [String]),
+      io:format("~s", [PLTInfo]),
       ?RET_NOTHING_SUSPICIOUS;
     false ->
       case file:open(OutputFile, [write]) of
 	{ok, FileDesc} ->
-	  io:format(FileDesc, "~s", [String]),
+	  io:format(FileDesc, "~s", [PLTInfo]),
 	  ok = file:close(FileDesc),
 	  ?RET_NOTHING_SUSPICIOUS;
 	{error, Reason} ->
diff --git a/lib/dialyzer/src/dialyzer.hrl b/lib/dialyzer/src/dialyzer.hrl
index 2da8ed2e5d..1d98574585 100644
--- a/lib/dialyzer/src/dialyzer.hrl
+++ b/lib/dialyzer/src/dialyzer.hrl
@@ -129,7 +129,7 @@
 		  defines         = []		   :: [dial_define()],
 		  from            = byte_code	   :: start_from(),
 		  get_warnings    = maybe          :: boolean() | 'maybe',
-		  init_plt        = none	   :: 'none' | file:filename(),
+		  init_plts       = []	           :: [file:filename()],
 		  include_dirs    = []		   :: [file:filename()],
 		  output_plt      = none           :: 'none' | file:filename(),
 		  legal_warnings  = ordsets:new()  :: ordset(dial_warn_tag()),
diff --git a/lib/dialyzer/src/dialyzer_cl.erl b/lib/dialyzer/src/dialyzer_cl.erl
index 0250c47ad0..1987c1732c 100644
--- a/lib/dialyzer/src/dialyzer_cl.erl
+++ b/lib/dialyzer/src/dialyzer_cl.erl
@@ -81,11 +81,15 @@ build_plt(Opts) ->
 init_opts_for_build(Opts) ->
   case Opts#options.output_plt =:= none of
     true ->
-      case Opts#options.init_plt of
-	none -> Opts#options{init_plt = none, output_plt = get_default_plt()};
-	Plt  -> Opts#options{init_plt = none, output_plt = Plt}
+      case Opts#options.init_plts of
+	[] -> Opts#options{output_plt = get_default_output_plt()};
+	[Plt] -> Opts#options{init_plts = [], output_plt = Plt};
+        Plts ->
+          Msg = io_lib:format("Could not build multiple PLT files: ~s\n",
+                              [format_plts(Plts)]),
+          error(Msg)
       end;
-    false -> Opts#options{init_plt = none}
+    false -> Opts#options{init_plts = []}
   end.
 
 %%--------------------------------------------------------------------
@@ -98,39 +102,58 @@ add_to_plt(Opts) ->
 init_opts_for_add(Opts) ->
   case Opts#options.output_plt =:= none of
     true ->
-      case Opts#options.init_plt of
-	none -> Opts#options{output_plt = get_default_plt(),
-			     init_plt = get_default_plt()};
-	Plt  -> Opts#options{output_plt = Plt}
+      case Opts#options.init_plts of
+	[] -> Opts#options{output_plt = get_default_output_plt(),
+                           init_plts = get_default_init_plt()};
+	[Plt] -> Opts#options{output_plt = Plt};
+        Plts ->
+          Msg = io_lib:format("Could not add to multiple PLT files: ~s\n",
+                              [format_plts(Plts)]),
+          error(Msg)
       end;
     false ->
-      case Opts#options.init_plt =:= none of
-	true  -> Opts#options{init_plt = get_default_plt()};
+      case Opts#options.init_plts =:= [] of
+	true  -> Opts#options{init_plts = get_default_init_plt()};
 	false -> Opts
       end
   end.
 
 %%--------------------------------------------------------------------
 
-check_plt(Opts) ->
+check_plt(#options{init_plts = []} = Opts) ->
   Opts1 = init_opts_for_check(Opts),
-  report_check(Opts),
-  plt_common(Opts1, [], []).
+  report_check(Opts1),
+  plt_common(Opts1, [], []);
+check_plt(#options{init_plts = Plts} = Opts) ->
+  check_plt_aux(Plts, Opts).
+
+check_plt_aux([_] = Plt, Opts) ->
+  Opts1 = Opts#options{init_plts = Plt},
+  Opts2 = init_opts_for_check(Opts1),
+  report_check(Opts2),
+  plt_common(Opts2, [], []);
+check_plt_aux([Plt|Plts], Opts) ->
+  Opts1 = Opts#options{init_plts = [Plt]},
+  Opts2 = init_opts_for_check(Opts1),
+  report_check(Opts2),
+  plt_common(Opts2, [], []),
+  check_plt_aux(Plts, Opts).
 
 init_opts_for_check(Opts) ->
-  Plt =
-    case Opts#options.init_plt of
-      none -> get_default_plt();
-      Plt0 -> Plt0
+  InitPlt =
+    case Opts#options.init_plts of
+      []-> get_default_init_plt();
+      Plt -> Plt
     end,
+  [OutputPlt] = InitPlt,
   Opts#options{files         = [],
 	       files_rec     = [],
 	       analysis_type = plt_check,
 	       defines       = [],
 	       from          = byte_code,
-	       init_plt      = Plt,
+	       init_plts     = InitPlt,
 	       include_dirs  = [],
-	       output_plt    = Plt,
+	       output_plt    = OutputPlt,
 	       use_contracts = true
 	      }.
 
@@ -144,21 +167,25 @@ remove_from_plt(Opts) ->
 init_opts_for_remove(Opts) ->
   case Opts#options.output_plt =:= none of
     true ->
-      case Opts#options.init_plt of
-	none -> Opts#options{output_plt = get_default_plt(),
-			     init_plt = get_default_plt()};
-	Plt  -> Opts#options{output_plt = Plt}
+      case Opts#options.init_plts of
+	[] -> Opts#options{output_plt = get_default_output_plt(),
+                           init_plts = get_default_init_plt()};
+	[Plt] -> Opts#options{output_plt = Plt};
+        Plts ->
+          Msg = io_lib:format("Could not remove from multiple PLT files: ~s\n",
+                              [format_plts(Plts)]),
+          error(Msg)
       end;
     false ->
-      case Opts#options.init_plt =:= none of
-	true  -> Opts#options{init_plt = get_default_plt()};
+      case Opts#options.init_plts =:= [] of
+	true  -> Opts#options{init_plts = get_default_init_plt()};
 	false -> Opts
       end
   end.
 
 %%--------------------------------------------------------------------
 
-plt_common(Opts, RemoveFiles, AddFiles) ->
+plt_common(#options{init_plts = [InitPlt]} = Opts, RemoveFiles, AddFiles) ->
   case check_plt(Opts, RemoveFiles, AddFiles) of
     ok ->
       case Opts#options.report_mode of
@@ -174,7 +201,7 @@ plt_common(Opts, RemoveFiles, AddFiles) ->
       report_failed_plt_check(Opts, DiffMd5),
       {AnalFiles, RemovedMods, ModDeps1} = 
 	expand_dependent_modules(Md5, DiffMd5, ModDeps),
-      Plt = clean_plt(Opts#options.init_plt, RemovedMods),
+      Plt = clean_plt(InitPlt, RemovedMods),
       case AnalFiles =:= [] of
 	true ->
 	  %% Only removed stuff. Just write the PLT.
@@ -186,19 +213,19 @@ plt_common(Opts, RemoveFiles, AddFiles) ->
       end;
     {error, no_such_file} ->
       Msg = io_lib:format("Could not find the PLT: ~s\n~s",
-			  [Opts#options.init_plt, default_plt_error_msg()]),
+			  [InitPlt, default_plt_error_msg()]),
       error(Msg);
     {error, not_valid} ->
       Msg = io_lib:format("The file: ~s is not a valid PLT file\n~s",
-			  [Opts#options.init_plt, default_plt_error_msg()]),
+			  [InitPlt, default_plt_error_msg()]),
       error(Msg);
     {error, read_error} ->
       Msg = io_lib:format("Could not read the PLT: ~s\n~s",
-			  [Opts#options.init_plt, default_plt_error_msg()]),
+			  [InitPlt, default_plt_error_msg()]),
       error(Msg);
     {error, {no_file_to_remove, F}} ->
       Msg = io_lib:format("Could not remove the file ~s from the PLT: ~s\n",
-			  [F, Opts#options.init_plt]),
+			  [F, InitPlt]),
       error(Msg)
   end.
 
@@ -218,8 +245,7 @@ default_plt_error_msg() ->
 
 %%--------------------------------------------------------------------
 
-check_plt(Opts, RemoveFiles, AddFiles) ->
-  Plt = Opts#options.init_plt,
+check_plt(#options{init_plts = [Plt]} = Opts, RemoveFiles, AddFiles) ->
   case dialyzer_plt:check_plt(Plt, RemoveFiles, AddFiles) of
     {old_version, _MD5} = OldVersion ->
       report_old_version(Opts),
@@ -234,14 +260,14 @@ check_plt(Opts, RemoveFiles, AddFiles) ->
 
 %%--------------------------------------------------------------------
 
-report_check(#options{report_mode = ReportMode, init_plt = InitPlt}) ->
+report_check(#options{report_mode = ReportMode, init_plts = [InitPlt]}) ->
   case ReportMode of
     quiet -> ok;
     _ ->
       io:format("  Checking whether the PLT ~s is up-to-date...", [InitPlt])
   end.
 
-report_old_version(#options{report_mode = ReportMode, init_plt = InitPlt}) ->
+report_old_version(#options{report_mode = ReportMode, init_plts = [InitPlt]}) ->
   case ReportMode of
     quiet -> ok;
     _ ->
@@ -264,7 +290,7 @@ report_failed_plt_check(#options{analysis_type = AnalType,
 
 report_analysis_start(#options{analysis_type = Type,
 			       report_mode = ReportMode,
-			       init_plt = InitPlt, 
+			       init_plts = InitPlts,
 			       output_plt = OutputPlt}) ->
   case ReportMode of
     quiet -> ok;
@@ -272,6 +298,7 @@ report_analysis_start(#options{analysis_type = Type,
       io:format("  "),
       case Type of
 	plt_add ->
+          [InitPlt] = InitPlts,
 	  case InitPlt =:= OutputPlt of
 	    true -> io:format("Adding information to ~s...", [OutputPlt]);
 	    false -> io:format("Adding information from ~s to ~s...", 
@@ -282,6 +309,7 @@ report_analysis_start(#options{analysis_type = Type,
 	plt_check ->
 	  io:format("Rebuilding the information in ~s...", [OutputPlt]);
 	plt_remove ->
+          [InitPlt] = InitPlts,
 	  case InitPlt =:= OutputPlt of
 	    true -> io:format("Removing information from ~s...", [OutputPlt]);
 	    false -> io:format("Removing information from ~s to ~s...", 
@@ -320,16 +348,28 @@ report_md5_diff(List) ->
 
 %%--------------------------------------------------------------------
 
-get_default_plt() ->
+get_default_init_plt() ->
+  [dialyzer_plt:get_default_plt()].
+
+get_default_output_plt() ->
   dialyzer_plt:get_default_plt().
 
 %%--------------------------------------------------------------------
 
+format_plts([Plt]) -> Plt;
+format_plts([Plt|Plts]) ->
+  Plt ++ ", " ++ format_plts(Plts).
+
+%%--------------------------------------------------------------------
+
 do_analysis(Options) ->
   Files = get_files_from_opts(Options),
-  case Options#options.init_plt of
-    none -> do_analysis(Files, Options, dialyzer_plt:new(), none);
-    File -> do_analysis(Files, Options, dialyzer_plt:from_file(File), none)
+  case Options#options.init_plts of
+    [] -> do_analysis(Files, Options, dialyzer_plt:new(), none);
+    PltFiles ->
+      Plts = [dialyzer_plt:from_file(F) || F <- PltFiles],
+      Plt = dialyzer_plt:merge_plts_or_report_conflicts(PltFiles, Plts),
+      do_analysis(Files, Options, Plt, none)
   end.
   
 do_analysis(Files, Options, Plt, PltInfo) ->
diff --git a/lib/dialyzer/src/dialyzer_cl_parse.erl b/lib/dialyzer/src/dialyzer_cl_parse.erl
index 0160b84abc..2f9e577544 100644
--- a/lib/dialyzer/src/dialyzer_cl_parse.erl
+++ b/lib/dialyzer/src/dialyzer_cl_parse.erl
@@ -138,11 +138,17 @@ cl(["-pa", Path|T]) ->
     true -> cl(T);
     {error, _} -> error("Bad directory for -pa: "++Path)
   end;
-cl(["--plt", PLT|T]) ->
-  put(dialyzer_init_plt, PLT),
-  cl(T);
 cl(["--plt"]) ->
   error("No plt specified for --plt");
+cl(["--plt", PLT|T]) ->
+  put(dialyzer_init_plts, [PLT]),
+  cl(T);
+cl(["--plts"]) ->
+  error("No plts specified for --plts");
+cl(["--plts"|T]) ->
+  {PLTs, NewT} = get_plts(T, []),
+  put(dialyzer_init_plts, PLTs),
+  cl(NewT);
 cl(["-q"|T]) ->
   put(dialyzer_options_report_mode, quiet),
   cl(T);
@@ -284,7 +290,7 @@ common_options() ->
   [{defines, get(dialyzer_options_defines)},
    {from, get(dialyzer_options_from)},
    {include_dirs, get(dialyzer_include)},
-   {init_plt, get(dialyzer_init_plt)},
+   {plts, get(dialyzer_init_plts)},
    {output_plt, get(dialyzer_output_plt)},
    {report_mode, get(dialyzer_options_report_mode)},
    {use_spec, get(dialyzer_options_use_contracts)},
@@ -309,6 +315,13 @@ get_lib_dir([], Acc) ->
 
 %%-----------------------------------------------------------------------
 
+get_plts(["--"|T], Acc) -> {lists:reverse(Acc), T};
+get_plts(["-"++_Opt = H|T], Acc) -> {lists:reverse(Acc), [H|T]};
+get_plts([H|T], Acc) -> get_plts(T, [H|Acc]);
+get_plts([], Acc) -> {lists:reverse(Acc), []}.
+
+%%-----------------------------------------------------------------------
+
 help_warnings() ->
   S = warning_options_msg(),
   io:put_chars(S),
@@ -316,9 +329,10 @@ help_warnings() ->
 
 help_message() ->
   S = "Usage: dialyzer [--help] [--version] [--shell] [--quiet] [--verbose]
-		[-pa dir]* [--plt plt] [-Ddefine]* [-I include_dir]* 
-		[--output_plt file] [-Wwarn]* [--src] [--gui | --wx]
-		[files_or_dirs] [-r dirs] [--apps applications] [-o outfile]
+		[-pa dir]* [--plt plt] [--plts plts] [-Ddefine]*
+                [-I include_dir]* [--output_plt file] [-Wwarn]*
+                [--src] [--gui | --wx] [files_or_dirs] [-r dirs]
+                [--apps applications] [-o outfile]
 		[--build_plt] [--add_to_plt] [--remove_from_plt]
 		[--check_plt] [--no_check_plt] [--plt_info] [--get_warnings]
                 [--no_native]
@@ -362,6 +376,10 @@ Options:
   --plt plt
       Use the specified plt as the initial plt (if the plt was built 
       during setup the files will be checked for consistency)
+  --plts plts
+      Merges the specified plts to create the initial plt -- requires
+      that the plts are disjoint (i.e., do not have any module
+      appearing in more than one plt)
   -Wwarn
       A family of options which selectively turn on/off warnings
       (for help on the names of warnings use dialyzer -Whelp)
@@ -378,12 +396,12 @@ Options:
   --build_plt
       The analysis starts from an empty plt and creates a new one from the
       files specified with -c and -r. Only works for beam files.
-      Use --plt or --output_plt to override the default plt location.
+      Use --plt(s) or --output_plt to override the default plt location.
   --add_to_plt
       The plt is extended to also include the files specified with -c and -r.
-      Use --plt to specify wich plt to start from, and --output_plt to 
-      specify where to put the plt. Note that the analysis might include 
-      files from the plt if they depend on the new files. 
+      Use --plt(s) to specify wich plt to start from, and --output_plt to
+      specify where to put the plt. Note that the analysis might include
+      files from the plt if they depend on the new files.
       This option only works with beam files.
   --remove_from_plt
       The information from the files specified with -c and -r is removed
@@ -396,8 +414,8 @@ Options:
       Skip the plt check when running Dialyzer. Useful when working with
       installed plts that never change.
   --plt_info
-      Makes Dialyzer print information about the plt and then quit. The plt 
-      can be specified with --plt.
+      Makes Dialyzer print information about the plt and then quit. The plt
+      can be specified with --plt(s).
   --get_warnings
       Makes Dialyzer emit warnings even when manipulating the plt. Only 
       emits warnings for files that are actually analyzed.
diff --git a/lib/dialyzer/src/dialyzer_gui.erl b/lib/dialyzer/src/dialyzer_gui.erl
index f353638cdf..4436330f7f 100644
--- a/lib/dialyzer/src/dialyzer_gui.erl
+++ b/lib/dialyzer/src/dialyzer_gui.erl
@@ -88,8 +88,8 @@
 
 -spec start(#options{}) -> ?RET_NOTHING_SUSPICIOUS.
 
-start(DialyzerOptions = #options{from = From, init_plt = InitPltFile,
-				 legal_warnings = LegalWarnings}) ->
+start(#options{from = From, init_plts = InitPltFiles,
+               legal_warnings = LegalWarnings} = DialyzerOptions) ->
   process_flag(trap_exit, true),
 
   GS = gs:start(),
@@ -336,9 +336,13 @@ start(DialyzerOptions = #options{from = From, init_plt = InitPltFile,
   gs:config(Packer, WH),
   {ok, CWD} = file:get_cwd(),
   
-  InitPlt = try dialyzer_plt:from_file(InitPltFile)
-	    catch throw:{dialyzer_error, _} -> dialyzer_plt:new()
-	    end,
+  InitPlt =
+    case InitPltFiles of
+      [] -> dialyzer_plt:new();
+      _ ->
+        Plts = [dialyzer_plt:from_file(F) || F <- InitPltFiles],
+        dialyzer_plt:merge_plts_or_report_conflicts(InitPltFiles, Plts)
+    end,
 
   State = #gui_state{add_all = AddAll,
 		     add_file = AddFile,
diff --git a/lib/dialyzer/src/dialyzer_gui_wx.erl b/lib/dialyzer/src/dialyzer_gui_wx.erl
index 2e309d7ec1..e711c15ea7 100644
--- a/lib/dialyzer/src/dialyzer_gui_wx.erl
+++ b/lib/dialyzer/src/dialyzer_gui_wx.erl
@@ -88,7 +88,7 @@ start(DialyzerOptions) ->
   State = wx:batch(fun() -> create_window(Wx, DialyzerOptions) end),
   gui_loop(State).
 
-create_window(Wx, DialyzerOptions) ->
+create_window(Wx, #options{init_plts = InitPltFiles} = DialyzerOptions) ->
   {ok, Host} = inet:gethostname(),
 
   %%---------- initializing frame ---------
@@ -258,11 +258,15 @@ create_window(Wx, DialyzerOptions) ->
 	       plt = PltMenu,
 	       options =OptionsMenu,
 	       help = HelpMenu},
- 
-  InitPlt = try dialyzer_plt:from_file(DialyzerOptions#options.init_plt)
-	    catch throw:{dialyzer_error, _} -> dialyzer_plt:new()
-	    end,
 
+  InitPlt =
+    case InitPltFiles of
+      [] -> dialyzer_plt:new();
+      _ ->
+        Plts = [dialyzer_plt:from_file(F) || F <- InitPltFiles],
+        dialyzer_plt:merge_plts_or_report_conflicts(InitPltFiles, Plts)
+    end,
+  
   #gui_state{add = AddButton,
 	     add_dir = AddDirButton,
 	     add_rec = AddRecButton,
diff --git a/lib/dialyzer/src/dialyzer_options.erl b/lib/dialyzer/src/dialyzer_options.erl
index 010625b7bd..2c0afa6e2b 100644
--- a/lib/dialyzer/src/dialyzer_options.erl
+++ b/lib/dialyzer/src/dialyzer_options.erl
@@ -53,14 +53,21 @@ build(Opts) ->
   InitPlt = dialyzer_plt:get_default_plt(),
   DefaultOpts = #options{},
   DefaultOpts1 = DefaultOpts#options{legal_warnings = DefaultWarns1,
-				      init_plt = InitPlt},
-  try 
-    NewOpts = build_options(Opts, DefaultOpts1),
+                                     init_plts = [InitPlt]},
+  try
+    Opts1 = preprocess_opts(Opts),
+    NewOpts = build_options(Opts1, DefaultOpts1),
     postprocess_opts(NewOpts)
   catch
     throw:{dialyzer_options_error, Msg} -> {error, Msg}
   end.
 
+preprocess_opts([]) -> [];
+preprocess_opts([{init_plt, File}|Opts]) ->
+  [{plts, [File]}|preprocess_opts(Opts)];
+preprocess_opts([Opt|Opts]) ->
+  [Opt|preprocess_opts(Opts)].
+
 postprocess_opts(Opts = #options{}) ->
   Opts1 = check_output_plt(Opts),
   adapt_get_warnings(Opts1).
@@ -144,9 +151,9 @@ build_options([{OptionName, Value} = Term|Rest], Options) ->
       build_options(Rest, Options#options{from = Value});
     get_warnings ->
       build_options(Rest, Options#options{get_warnings = Value});
-    init_plt ->
-      assert_filenames([Term], [Value]),
-      build_options(Rest, Options#options{init_plt = Value});
+    plts ->
+      assert_filenames(Term, Value),
+      build_options(Rest, Options#options{init_plts = Value});
     include_dirs ->
       assert_filenames(Term, Value),
       OldVal = Options#options.include_dirs,
diff --git a/lib/dialyzer/src/dialyzer_plt.erl b/lib/dialyzer/src/dialyzer_plt.erl
index 08d0b318b5..a7ba270c41 100644
--- a/lib/dialyzer/src/dialyzer_plt.erl
+++ b/lib/dialyzer/src/dialyzer_plt.erl
@@ -51,6 +51,7 @@
 	 lookup_contract/2,
 	 lookup_module/2,
 	 merge_plts/1,
+         merge_plts_or_report_conflicts/2,
 	 new/0,
 	 plt_and_info_from_file/1,
 	 get_specs/1,
@@ -292,6 +293,38 @@ merge_plts(List) ->
        exported_types = sets_merge(ExpTypesList),
        contracts = table_merge(ContractsList)}.
 
+-spec merge_disj_plts([plt()]) -> plt().
+
+merge_disj_plts(List) ->
+  InfoList = [Info || #plt{info = Info} <- List],
+  TypesList = [Types || #plt{types = Types} <- List],
+  ExpTypesList = [ExpTypes || #plt{exported_types = ExpTypes} <- List],
+  ContractsList = [Contracts || #plt{contracts = Contracts} <- List],
+  #plt{info = table_disj_merge(InfoList),
+       types = table_disj_merge(TypesList),
+       exported_types = sets_disj_merge(ExpTypesList),
+       contracts = table_disj_merge(ContractsList)}.
+
+-spec merge_plts_or_report_conflicts([file:filename()], [plt()]) -> plt().
+
+merge_plts_or_report_conflicts(PltFiles, Plts) ->
+  try
+    merge_disj_plts(Plts)
+  catch throw:{dialyzer_error, not_disjoint_plts} ->
+      IncFiles = lists:append([begin {ok, Fs} = included_files(F), Fs end
+                               || F <- PltFiles]),
+      ConfFiles = find_duplicates(IncFiles),
+      Msg = io_lib:format("Could not merge PLTs since they are not disjoint\n"
+                          "The following files are included in more than one "
+                          "PLTs:\n~p\n", [ConfFiles]),
+      error(Msg)
+  end.
+
+find_duplicates(List) ->
+  ModList = [filename:basename(E) || E <- List],
+  SortedList = lists:usort(ModList),
+  lists:usort(ModList -- SortedList).
+  
 -spec to_file(file:filename(), plt(), mod_deps(), {[file_md5()], mod_deps()}) -> 'ok'.
 
 to_file(FileName,
@@ -556,6 +589,25 @@ table_merge([Plt|Plts], Acc) ->
   NewAcc = dict:merge(fun(_Key, Val, Val) -> Val end, Plt, Acc),
   table_merge(Plts, NewAcc).
 
+table_disj_merge([H|T]) ->
+  table_disj_merge(T, H).
+
+table_disj_merge([], Acc) ->
+  Acc;
+table_disj_merge([Plt|Plts], Acc) ->
+  case table_is_disjoint(Plt, Acc) of
+    true ->
+      NewAcc = dict:merge(fun(_Key, _Val1, _Val2) -> gazonk end,
+                          Plt, Acc),
+      table_disj_merge(Plts, NewAcc);
+    false -> throw({dialyzer_error, not_disjoint_plts})
+  end.
+
+table_is_disjoint(T1, T2) ->
+  K1 = dict:fetch_keys(T1),
+  K2 = dict:fetch_keys(T2),
+  lists:all(fun(E) -> not lists:member(E, K2) end, K1).
+
 sets_merge([H|T]) ->
   sets_merge(T, H).
 
@@ -565,6 +617,19 @@ sets_merge([Plt|Plts], Acc) ->
   NewAcc = sets:union(Plt, Acc),
   sets_merge(Plts, NewAcc).
 
+sets_disj_merge([H|T]) ->
+  sets_disj_merge(T, H).
+
+sets_disj_merge([], Acc) ->
+  Acc;
+sets_disj_merge([Plt|Plts], Acc) ->
+  case sets:is_disjoint(Plt, Acc) of
+    true ->
+      NewAcc = sets:union(Plt, Acc),
+      sets_disj_merge(Plts, NewAcc);
+    false -> throw({dialyzer_error, not_disjoint_plts})
+  end.
+
 %%---------------------------------------------------------------------------
 %% Debug utilities.
 
-- 
cgit v1.2.3