aboutsummaryrefslogtreecommitdiffstats
path: root/lib/tools
diff options
context:
space:
mode:
Diffstat (limited to 'lib/tools')
-rw-r--r--lib/tools/emacs/erlang-test.el57
-rw-r--r--lib/tools/emacs/erlang.el160
-rw-r--r--lib/tools/src/xref_base.erl215
-rw-r--r--lib/tools/src/xref_utils.erl8
4 files changed, 256 insertions, 184 deletions
diff --git a/lib/tools/emacs/erlang-test.el b/lib/tools/emacs/erlang-test.el
index a5aab04953..9a146632c5 100644
--- a/lib/tools/emacs/erlang-test.el
+++ b/lib/tools/emacs/erlang-test.el
@@ -33,6 +33,7 @@
(require 'ert)
(require 'cl-lib)
+(require 'erlang)
(defvar erlang-test-code
'((nil . "-module(erlang_test).")
@@ -51,27 +52,28 @@ concatenated to form an erlang file to test on.")
(ert-deftest erlang-test-tags ()
(let* ((dir (make-temp-file "erlang-test" t))
- (erlang-file (expand-file-name "erlang_test.erl" dir))
- (tags-file (expand-file-name "TAGS" dir))
- tags-file-name tags-table-list erlang-buffer)
- (unwind-protect
- (progn
- (erlang-test-create-erlang-file erlang-file)
- (erlang-test-compile-tags erlang-file tags-file)
- (setq erlang-buffer (find-file-noselect erlang-file))
- (with-current-buffer erlang-buffer
- (setq-local tags-file-name tags-file))
- ;; PENDING - setting global tags-file-name is a workaround
- ;; for GNU Emacs bug23164.
- (setq tags-file-name tags-file)
- (erlang-test-xref-find-definitions erlang-file erlang-buffer))
- (when (buffer-live-p erlang-buffer)
- (kill-buffer erlang-buffer))
- (let ((tags-buffer (find-buffer-visiting tags-file)))
- (when (buffer-live-p tags-buffer)
- (kill-buffer tags-buffer)))
- (when (file-exists-p dir)
- (delete-directory dir t)))))
+ (erlang-file (expand-file-name "erlang_test.erl" dir))
+ (tags-file (expand-file-name "TAGS" dir))
+ tags-file-name tags-table-list erlang-buffer)
+ (unwind-protect
+ (progn
+ (erlang-test-create-erlang-file erlang-file)
+ (erlang-test-compile-tags erlang-file tags-file)
+ (setq erlang-buffer (find-file-noselect erlang-file))
+ (with-current-buffer erlang-buffer
+ (setq-local tags-file-name tags-file))
+ ;; Setting global tags-file-name is a workaround for
+ ;; GNU Emacs bug#23164.
+ (setq tags-file-name tags-file)
+ (erlang-test-completion-table)
+ (erlang-test-xref-find-definitions erlang-file erlang-buffer))
+ (when (buffer-live-p erlang-buffer)
+ (kill-buffer erlang-buffer))
+ (let ((tags-buffer (find-buffer-visiting tags-file)))
+ (when (buffer-live-p tags-buffer)
+ (kill-buffer tags-buffer)))
+ (when (file-exists-p dir)
+ (delete-directory dir t)))))
(defun erlang-test-create-erlang-file (erlang-file)
(with-temp-file erlang-file
@@ -83,6 +85,19 @@ concatenated to form an erlang file to test on.")
"-o" tags-file
erlang-file))))
+(defun erlang-test-completion-table ()
+ (let ((erlang-replace-etags-tags-completion-table t))
+ (setq tags-completion-table nil)
+ (tags-completion-table))
+ (should (equal (sort tags-completion-table #'string-lessp)
+ (sort (erlang-expected-completion-table) #'string-lessp))))
+
+(defun erlang-expected-completion-table ()
+ (append (cl-loop for (symbol . _) in erlang-test-code
+ when (stringp symbol)
+ append (list symbol (concat "erlang_test:" symbol)))
+ (list "erlang_test:" "erlang_test:module_info")))
+
(defun erlang-test-xref-find-definitions (erlang-file erlang-buffer)
(cl-loop for (tagname . code) in erlang-test-code
for line = 1 then (1+ line)
diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el
index 3d20d86f43..a2062180f3 100644
--- a/lib/tools/emacs/erlang.el
+++ b/lib/tools/emacs/erlang.el
@@ -971,7 +971,7 @@ resulting regexp is surrounded by \\_< and \\_>."
(defvar erlang-defun-prompt-regexp (concat "^" erlang-atom-regexp "\\s *(")
"Regexp which should match beginning of a clause.")
-(defvar erlang-file-name-extension-regexp "\\.[eh]rl$"
+(defvar erlang-file-name-extension-regexp "\\.erl$"
"*Regexp which should match an Erlang file name.
This regexp is used when an Erlang module name is extracted from the
@@ -1291,6 +1291,11 @@ Unfortunately, XEmacs hasn't got support for a special Font
Lock syntax table. The effect is that `apply' in the atom
`foo_apply' will be highlighted as a bif.")
+(defvar erlang-replace-etags-tags-completion-table nil
+ "Internal flag used by advice `erlang-replace-tags-table'.
+This is non-nil when `etags-tags-completion-table' should be
+replaced by `erlang-etags-tags-completion-table'.")
+
;;; Avoid errors while compiling this file.
@@ -1354,6 +1359,10 @@ Lock syntax table. The effect is that `apply' in the atom
(called-interactively-p 'interactive)
(funcall (symbol-function 'interactive-p))))
+(unless (fboundp 'prog-mode)
+ (defun prog-mode ()
+ (use-local-map (make-keymap))))
+
;;;###autoload
(define-derived-mode erlang-mode prog-mode "Erlang"
"Major mode for editing Erlang source files in Emacs.
@@ -1539,7 +1548,9 @@ Other commands:
(set (make-local-variable 'outline-regexp) "[[:lower:]0-9_]+ *(.*) *-> *$")
(set (make-local-variable 'outline-level) (lambda () 1))
(set (make-local-variable 'add-log-current-defun-function)
- 'erlang-current-defun))
+ 'erlang-current-defun)
+ (set (make-local-variable 'find-tag-default-function)
+ 'erlang-find-tag-for-completion))
(defun erlang-font-lock-init ()
"Initialize Font Lock for Erlang mode."
@@ -4358,12 +4369,12 @@ works under XEmacs.)"
(require 'etags)
;; Test on a function available in the Emacs 19 version
;; of tags but not in the XEmacs version.
- (if (not (fboundp 'find-tag-noselect))
- ()
+ (when (fboundp 'find-tag-noselect)
(erlang-tags-define-keys (current-local-map))
(setq erlang-tags-installed t)))))
+
;; Set all keys bound to `find-tag' et.al. in the global map and the
;; menu to `erlang-find-tag' et.al. in `map'.
;;
@@ -4386,10 +4397,6 @@ works under XEmacs.)"
(erlang-menu-init))
-;; There exists a variable `find-tag-default-function'. It is not used
-;; since `complete-tag' uses it to get current word under point. In that
-;; situation we don't want the module to be prepended.
-
(defun erlang-find-tag-default ()
"Return the default tag.
Search `-import' list of imported functions.
@@ -4766,26 +4773,30 @@ for a tag on the form `module:tag'."
;;;
;;; The basic idea is to create a second completion table `erlang-tags-
;;; completion-table' containing all normal tags plus tags on the form
-;;; `module:tag'.
-
+;;; `module:tag' and `module:'.
-(when (and (fboundp 'etags-tags-completion-table)
+;; PENDING - Should probably make use of the
+;; `completion-at-point-functions' hook instead of this advice.
+(when (and (locate-library "etags")
+ (require 'etags)
+ (fboundp 'etags-tags-completion-table)
(fboundp 'tags-lazy-completion-table)) ; Emacs 23.1+
(if (fboundp 'advice-add)
;; Emacs 24.4+
(advice-add 'etags-tags-completion-table :around
(lambda (oldfun)
- (if (eq find-tag-default-function 'erlang-find-tag-for-completion)
+ (if erlang-replace-etags-tags-completion-table
(erlang-etags-tags-completion-table)
(funcall oldfun)))
(list :name 'erlang-replace-tags-table))
;; Emacs 23.1-24.3
- (defadvice etags-tags-completion-table (around erlang-replace-tags-table activate)
- (if (eq find-tag-default-function 'erlang-find-tag-for-completion)
+ (defadvice etags-tags-completion-table (around
+ erlang-replace-tags-table
+ activate)
+ (if erlang-replace-etags-tags-completion-table
(setq ad-return-value (erlang-etags-tags-completion-table))
ad-do-it))))
-
(defun erlang-complete-tag ()
"Perform tags completion on the text around point.
Completes to the set of names listed in the current tags table.
@@ -4799,23 +4810,19 @@ about Erlang modules."
(cond ((and erlang-tags-installed
(fboundp 'etags-tags-completion-table)
(fboundp 'tags-lazy-completion-table)) ; Emacs 23.1+
- ;; This depends on the advice called
- ;; erlang-replace-tags-table above. It is not enough to
- ;; let-bind tags-completion-table-function since that may be
- ;; overwritten in etags-recognize-tags-table.
- (let ((find-tag-default-function 'erlang-find-tag-for-completion))
+ (let ((erlang-replace-etags-tags-completion-table t))
(complete-tag)))
((and erlang-tags-installed
- (fboundp 'complete-tag)
- (fboundp 'tags-complete-tag)) ; Emacs 19
+ (fboundp 'complete-tag)
+ (fboundp 'tags-complete-tag)) ; Emacs 19-22
(let ((orig-tags-complete-tag (symbol-function 'tags-complete-tag)))
(fset 'tags-complete-tag
(symbol-function 'erlang-tags-complete-tag))
(unwind-protect
- (funcall (symbol-function 'complete-tag))
+ (complete-tag)
(fset 'tags-complete-tag orig-tags-complete-tag))))
((fboundp 'complete-tag) ; Emacs 19
- (funcall (symbol-function 'complete-tag)))
+ (complete-tag))
((fboundp 'tag-complete-symbol) ; XEmacs
(funcall (symbol-function 'tag-complete-symbol)))
(t
@@ -4830,19 +4837,22 @@ about Erlang modules."
(buffer-substring-no-properties start (point)))))
-
;; Based on `tags-complete-tag', but this one uses
;; `erlang-tags-completion-table' instead of `tags-completion-table'.
;;
;; This is the entry-point called by system function `completing-read'.
+;;
+;; Used for minibuffer completion in Emacs 19-24 and completion in
+;; erlang buffers in Emacs 19-22.
(defun erlang-tags-complete-tag (string predicate what)
- (save-excursion
- ;; If we need to ask for the tag table, allow that.
- (let ((enable-recursive-minibuffers t))
- (visit-tags-table-buffer))
+ (with-current-buffer (window-buffer (minibuffer-selected-window))
+ (save-excursion
+ ;; If we need to ask for the tag table, allow that.
+ (let ((enable-recursive-minibuffers t))
+ (visit-tags-table-buffer))
(if (eq what t)
(all-completions string (erlang-tags-completion-table) predicate)
- (try-completion string (erlang-tags-completion-table) predicate))))
+ (try-completion string (erlang-tags-completion-table) predicate)))))
;; `tags-completion-table' calls itself recursively, make it
@@ -4860,7 +4870,6 @@ about Erlang modules."
(fset 'tags-completion-table
erlang-tags-orig-completion-table)))
-
(defun erlang-tags-completion-table-1 ()
(make-local-variable 'erlang-tags-completion-table)
(or erlang-tags-completion-table
@@ -4871,60 +4880,63 @@ about Erlang modules."
(setq erlang-tags-completion-table tags-completion-table))))
+
+;; Emacs 25 expects this function to return a list (and it is ok for
+;; it to include duplicates). Older emacsen expects an obarray.
+(defun erlang-etags-tags-completion-table ()
+ (if (>= emacs-major-version 25)
+ (erlang-etags-tags-completion-table-list)
+ (let ((obarray (make-vector 511 0)))
+ (dolist (tag (erlang-etags-tags-completion-table-list))
+ (intern tag obarray))
+ obarray)))
+
;; Based on `etags-tags-completion-table'. The difference is that we
-;; add three symbols to the vector, the tag, module: and module:tag.
+;; add three strings to the list, the tag, module: and module:tag.
;; The module is extracted from the file name of a tag. (This one
;; only works if we are looking at an `etags' file. However, this is
;; the only format supported by Emacs, so far.)
-(defun erlang-etags-tags-completion-table ()
- (let ((table (make-vector 511 0))
- (file nil)
- (progress-reporter
- (when (fboundp 'make-progress-reporter)
- (make-progress-reporter
- (format "Making erlang tags completion table for %s..." buffer-file-name)
- (point-min) (point-max)))))
+(defun erlang-etags-tags-completion-table-list ()
+ (let ((progress-reporter
+ (make-progress-reporter
+ (format "Making tags completion table for %s..." buffer-file-name)
+ (point-min) (point-max)))
+ table module)
(save-excursion
(goto-char (point-min))
- ;; This monster regexp matches an etags tag line.
- ;; \1 is the string to match;
- ;; \2 is not interesting;
- ;; \3 is the guessed tag name; XXX guess should be better eg DEFUN
- ;; \4 is not interesting;
- ;; \5 is the explicitly-specified tag name.
- ;; \6 is the line to start searching at;
- ;; \7 is the char to start searching at.
(while (progn
- (while (and
- (eq (following-char) ?\f)
- (looking-at "\f\n\\([^,\n]*\\),.*\n"))
- (setq file (buffer-substring
- (match-beginning 1) (match-end 1)))
- (goto-char (match-end 0)))
+ (while (and (eq (following-char) ?\f)
+ (looking-at "\f\n\\([^,\n]*\\),.*\n"))
+ (let ((file (buffer-substring (match-beginning 1)
+ (match-end 1))))
+ (setq module (erlang-get-module-from-file-name file))
+ (when module
+ (push (concat module ":") table)
+ (push (concat module ":module_info") table))
+ (forward-line 2)))
+ ;; This regexp matches an explicit tag name or the
+ ;; place where it would start.
(re-search-forward
- "\
-^\\(\\([^\177]+[^-a-zA-Z0-9_$\177]+\\)?\\([-a-zA-Z0-9_$?:]+\\)\
-\[^-a-zA-Z0-9_$?:\177]*\\)\177\\(\\([^\n\001]+\\)\001\\)?\
-\\([0-9]+\\)?,\\([0-9]+\\)?\n"
+ "[\f\t\n\r()=,; ]?\177\\\(?:\\([^\n\001]+\\)\001\\)?"
nil t))
- (let ((tag (if (match-beginning 5)
+ (let ((tag (if (match-beginning 1)
;; There is an explicit tag name.
- (buffer-substring (match-beginning 5) (match-end 5))
- ;; No explicit tag name. Best guess.
- (buffer-substring (match-beginning 3) (match-end 3))))
- (module (and file
- (erlang-get-module-from-file-name file))))
- (intern tag table)
+ (buffer-substring (match-beginning 1) (match-end 1))
+ ;; No explicit tag name. Backtrack a little,
+ ;; and look for the implicit one.
+ (goto-char (match-beginning 0))
+ (skip-chars-backward "^\f\t\n\r()=,; ")
+ (buffer-substring (point) (match-beginning 0)))))
+ (forward-line 1)
+ (push tag table)
(when (stringp module)
- (intern (concat module ":" tag) table)
- ;; Only the first ones will be stored in the table.
- (intern (concat module ":") table)
- (intern (concat module ":module_info") table))
- (when progress-reporter
- (progress-reporter-update progress-reporter (point))))))
+ (push (concat module ":" tag) table))
+ (progress-reporter-update progress-reporter (point)))))
table))
+
+
;;; Xref backend erlang-etags
;; In GNU Emacs 25 xref was introduced. It is a framework for cross
@@ -4963,10 +4975,12 @@ about Erlang modules."
((_backend (eql erlang-etags)) identifier)
(erlang-xref-find-definitions identifier t))
- ;; PENDING - This remains to be properly implemented.
(cl-defmethod xref-backend-identifier-completion-table
((_backend (eql erlang-etags)))
- (tags-lazy-completion-table)))))
+ (let ((erlang-replace-etags-tags-completion-table t))
+ (tags-completion-table))))))
+
+
(defun erlang-xref-find-definitions (identifier &optional is-regexp)
diff --git a/lib/tools/src/xref_base.erl b/lib/tools/src/xref_base.erl
index 4322943c59..bb9815f9b0 100644
--- a/lib/tools/src/xref_base.erl
+++ b/lib/tools/src/xref_base.erl
@@ -669,16 +669,45 @@ do_add_directory(Dir, AppName, Bui, Rec, Ver, War, State) ->
warnings(War, unreadable, Unreadable),
case Errors of
[] ->
- do_add_modules(FileNames, AppName, Bui, Ver, War, State, []);
+ do_add_modules(FileNames, AppName, Bui, Ver, War, State);
[Error | _] ->
throw(Error)
end.
-do_add_modules([], _AppName, _OB, _OV, _OW, State, Modules) ->
+do_add_modules(Files, AppName, OB, OV, OW, State0) ->
+ NFiles = length(Files),
+ Reader = fun(SplitName, State) ->
+ _Pid = read_module(SplitName, AppName, OB, OV, OW, State)
+ end,
+ N = parallelism(),
+ Files1 = start_readers(Files, Reader, State0, N),
+ %% Increase the number of readers towards the end to decrease the
+ %% waiting time for the collecting process:
+ Nx = N,
+ add_mods(Files1, Reader, State0, [], NFiles, Nx).
+
+add_mods(_, _ReaderFun, State, Modules, 0, _Nx) ->
{ok, sort(Modules), State};
-do_add_modules([File | Files], AppName, OB, OV, OW, State, Modules) ->
- {ok, M, NewState} = do_add_module(File, AppName, OB, OV, OW, State),
- do_add_modules(Files, AppName, OB, OV, OW, NewState, M ++ Modules).
+add_mods(Files, ReaderFun, State, Modules, N, Nx) ->
+ {I, Nx1} = case Nx > 0 of
+ false -> {1, Nx};
+ true -> {2, Nx - 1}
+ end,
+ Files1 = start_readers(Files, ReaderFun, State, I),
+ {ok, M, NewState} = process_module(State),
+ add_mods(Files1, ReaderFun, NewState, M ++ Modules, N - 1, Nx1).
+
+start_readers([SplitName|Files], ReaderFun, State, N) when N > 0 ->
+ _Pid = ReaderFun(SplitName, State),
+ start_readers(Files, ReaderFun, State, N - 1);
+start_readers(Files, _ReaderFun, _State, _) ->
+ Files.
+
+parallelism() ->
+ case erlang:system_info(multi_scheduling) of
+ enabled -> erlang:system_info(schedulers_online);
+ _ -> 1
+ end.
%% -> {ok, Module, State} | throw(Error)
do_add_a_module(File, AppName, Builtins, Verbose, Warnings, State) ->
@@ -692,50 +721,75 @@ do_add_a_module(File, AppName, Builtins, Verbose, Warnings, State) ->
%% -> {ok, Module, State} | throw(Error)
%% Options: verbose, warnings, builtins
-do_add_module({Dir, Basename}, AppName, Builtins, Verbose, Warnings, State) ->
- File = filename:join(Dir, Basename),
- {ok, M, Bad, NewState} =
- do_add_module1(Dir, File, AppName, Builtins, Verbose, Warnings, State),
- _ = filter(fun({Tag,B}) -> warnings(Warnings, Tag, [[File,B]]) end, Bad),
- {ok, M, NewState}.
-
-do_add_module1(Dir, File, AppName, Builtins, Verbose, Warnings, State) ->
- message(Verbose, reading_beam, [File]),
- Mode = State#xref.mode,
+do_add_module(SplitName, AppName, Builtins, Verbose, Warnings, State) ->
+ _Pid = read_module(SplitName, AppName, Builtins, Verbose, Warnings, State),
+ process_module(State).
+
+read_module(SplitName, AppName, Builtins, Verbose, Warnings, State) ->
Me = self(),
- Fun = fun() -> Me ! {self(), abst(File, Builtins, Mode)} end,
- case xref_utils:subprocess(Fun, [link, {min_heap_size,100000}]) of
+ #xref{mode = Mode} = State,
+ Fun =
+ fun() ->
+ Me ! {?MODULE,
+ read_a_module(SplitName, AppName, Builtins, Verbose,
+ Warnings, Mode)}
+ end,
+ spawn_opt(Fun, [link, {min_heap_size, 1000000}, {priority, high}]).
+
+read_a_module({Dir, BaseName}, AppName, Builtins, Verbose, Warnings, Mode) ->
+ File = filename:join(Dir, BaseName),
+ case abst(File, Builtins, Mode) of
{ok, _M, no_abstract_code} when Verbose ->
- message(Verbose, skipped_beam, []),
- {ok, [], [], State};
+ message(Verbose, no_debug_info, [File]),
+ no;
{ok, _M, no_abstract_code} when not Verbose ->
message(Warnings, no_debug_info, [File]),
- {ok, [], [], State};
+ no;
{ok, M, Data, UnresCalls0} ->
- %% Remove duplicates. Identical unresolved calls on the
- %% same line are counted as _one_ unresolved call.
- UnresCalls = usort(UnresCalls0),
- message(Verbose, done, []),
- NoUnresCalls = length(UnresCalls),
- case NoUnresCalls of
- 0 -> ok;
- 1 -> warnings(Warnings, unresolved_summary1, [[M]]);
- N -> warnings(Warnings, unresolved_summary, [[M, N]])
- end,
- T = case xref_utils:file_info(File) of
- {ok, {_, _, _, Time}} -> Time;
- Error -> throw(Error)
- end,
- XMod = #xref_mod{name = M, app_name = AppName, dir = Dir,
- mtime = T, builtins = Builtins,
- no_unresolved = NoUnresCalls},
- do_add_module(State, XMod, UnresCalls, Data);
+ message(Verbose, done, [File]),
+ %% Remove duplicates. Identical unresolved calls on the
+ %% same line are counted as _one_ unresolved call.
+ UnresCalls = usort(UnresCalls0),
+ NoUnresCalls = length(UnresCalls),
+ case NoUnresCalls of
+ 0 -> ok;
+ 1 -> warnings(Warnings, unresolved_summary1, [[M]]);
+ N -> warnings(Warnings, unresolved_summary, [[M, N]])
+ end,
+ case xref_utils:file_info(File) of
+ {ok, {_, _, _, Time}} ->
+ XMod = #xref_mod{name = M, app_name = AppName,
+ dir = Dir, mtime = Time,
+ builtins = Builtins,
+ no_unresolved = NoUnresCalls},
+ {ok, PrepMod, Bad} =
+ prepare_module(Mode, XMod, UnresCalls, Data),
+ foreach(fun({Tag,B}) ->
+ warnings(Warnings, Tag,
+ [[File,B]])
+ end, Bad),
+ {ok, PrepMod};
+ Error -> Error
+ end;
Error ->
message(Verbose, error, []),
- throw(Error)
+ Error
end.
-abst(File, Builtins, Mode) when Mode =:= functions ->
+process_module(State) ->
+ receive
+ {?MODULE, Reply} ->
+ case Reply of
+ no ->
+ {ok, [], State};
+ {ok, PrepMod} ->
+ finish_module(PrepMod, State);
+ Error ->
+ throw(Error)
+ end
+ end.
+
+abst(File, Builtins, _Mode = functions) ->
case beam_lib:chunks(File, [abstract_code, exports, attributes]) of
{ok, {M,[{abstract_code,NoA},_X,_A]}} when NoA =:= no_abstract_code ->
{ok, M, NoA};
@@ -762,7 +816,7 @@ abst(File, Builtins, Mode) when Mode =:= functions ->
Error when element(1, Error) =:= error ->
Error
end;
-abst(File, Builtins, Mode) when Mode =:= modules ->
+abst(File, Builtins, _Mode = modules) ->
case beam_lib:chunks(File, [exports, imports, attributes]) of
{ok, {Mod, [{exports,X0}, {imports,I0}, {attributes,At}]}} ->
X1 = mfa_exports(X0, At, Mod),
@@ -856,19 +910,13 @@ deprecated_flag(_) -> undefined.
%% dom CallAt = LC U XC
%% Attrs is collected from the attribute 'xref' (experimental).
do_add_module(S, XMod, Unres, Data) ->
- M = XMod#xref_mod.name,
- case dict:find(M, S#xref.modules) of
- {ok, OldXMod} ->
- BF2 = module_file(XMod),
- BF1 = module_file(OldXMod),
- throw_error({module_clash, {M, BF1, BF2}});
- error ->
- do_add_module(S, M, XMod, Unres, Data)
- end.
+ #xref{mode = Mode} = S,
+ Mode = S#xref.mode,
+ {ok, PrepMod, Bad} = prepare_module(Mode, XMod, Unres, Data),
+ {ok, Ms, NS} = finish_module(PrepMod, S),
+ {ok, Ms, Bad, NS}.
-%%do_add_module(S, M, _XMod, _Unres, Data)->
-%% {ok, M, [], S};
-do_add_module(S, M, XMod, Unres0, Data) when S#xref.mode =:= functions ->
+prepare_module(_Mode = functions, XMod, Unres0, Data) ->
{DefAt0, LPreCAt0, XPreCAt0, LC0, XC0, X0, Attrs, Depr} = Data,
%% Bad is a list of bad values of 'xref' attributes.
{ALC0,AXC0,Bad0} = Attrs,
@@ -904,26 +952,27 @@ do_add_module(S, M, XMod, Unres0, Data) when S#xref.mode =:= functions ->
LC = union(LC1, ALC),
{DF1,DF_11,DF_21,DF_31,DBad} = depr_mod(Depr, X),
+ {EE, ECallAt} = inter_graph(X, L, LC, XC, CallAt),
+ {ok, {functions, XMod, [DefAt,L,X,LCallAt,XCallAt,CallAt,LC,XC,EE,ECallAt,
+ DF1,DF_11,DF_21,DF_31], NoCalls, Unres},
+ DBad++Bad};
+prepare_module(_Mode = modules, XMod, _Unres, Data) ->
+ {X0, I0, Depr} = Data,
+ X1 = xref_utils:xset(X0, [tspec(func)]),
+ I1 = xref_utils:xset(I0, [tspec(func)]),
+ {DF1,DF_11,DF_21,DF_31,DBad} = depr_mod(Depr, X1),
+ {ok, {modules, XMod, [X1,I1,DF1,DF_11,DF_21,DF_31]}, DBad}.
- %% {EE, ECallAt} = inter_graph(X, L, LC, XC, LCallAt, XCallAt),
- Self = self(),
- Fun = fun() -> inter_graph(Self, X, L, LC, XC, CallAt) end,
- {EE, ECallAt} =
- xref_utils:subprocess(Fun, [link, {min_heap_size,100000}]),
-
+finish_module({functions, XMod, List, NoCalls, Unres}, S) ->
+ ok = check_module(XMod, S),
[DefAt2,L2,X2,LCallAt2,XCallAt2,CallAt2,LC2,XC2,EE2,ECallAt2,
- DF2,DF_12,DF_22,DF_32] =
- pack([DefAt,L,X,LCallAt,XCallAt,CallAt,LC,XC,EE,ECallAt,
- DF1,DF_11,DF_21,DF_31]),
-
- %% Foo = [DefAt2,L2,X2,LCallAt2,XCallAt2,CallAt2,LC2,XC2,EE2,ECallAt2,
- %% DF2,DF_12,DF_22,DF_32],
- %% io:format("{~p, ~p, ~p},~n", [M, pack:lsize(Foo), pack:usize(Foo)]),
+ DF2,DF_12,DF_22,DF_32] = pack(List),
LU = range(LC2),
LPredefined = predefined_funs(LU),
+ M = XMod#xref_mod.name,
MS = xref_utils:xset(M, atom),
T = from_sets({MS,DefAt2,L2,X2,LCallAt2,XCallAt2,CallAt2,
LC2,XC2,LU,EE2,ECallAt2,Unres,LPredefined,
@@ -934,19 +983,28 @@ do_add_module(S, M, XMod, Unres0, Data) when S#xref.mode =:= functions ->
XMod1 = XMod#xref_mod{data = T, info = Info},
S1 = S#xref{modules = dict:store(M, XMod1, S#xref.modules)},
- {ok, [M], DBad++Bad, take_down(S1)};
-do_add_module(S, M, XMod, _Unres, Data) when S#xref.mode =:= modules ->
- {X0, I0, Depr} = Data,
- X1 = xref_utils:xset(X0, [tspec(func)]),
- I1 = xref_utils:xset(I0, [tspec(func)]),
- {DF1,DF_11,DF_21,DF_31,DBad} = depr_mod(Depr, X1),
- [X2,I2,DF2,DF_12,DF_22,DF_32] = pack([X1,I1,DF1,DF_11,DF_21,DF_31]),
+ {ok, [M], take_down(S1)};
+finish_module({modules, XMod, List}, S) ->
+ ok = check_module(XMod, S),
+ [X2,I2,DF2,DF_12,DF_22,DF_32] = pack(List),
+ M = XMod#xref_mod.name,
MS = xref_utils:xset(M, atom),
T = from_sets({MS, X2, I2, DF2, DF_12, DF_22, DF_32}),
Info = [],
XMod1 = XMod#xref_mod{data = T, info = Info},
S1 = S#xref{modules = dict:store(M, XMod1, S#xref.modules)},
- {ok, [M], DBad, take_down(S1)}.
+ {ok, [M], take_down(S1)}.
+
+check_module(XMod, State) ->
+ M = XMod#xref_mod.name,
+ case dict:find(M, State#xref.modules) of
+ {ok, OldXMod} ->
+ BF2 = module_file(XMod),
+ BF1 = module_file(OldXMod),
+ throw_error({module_clash, {M, BF1, BF2}});
+ error ->
+ ok
+ end.
depr_mod({Depr,Bad0}, X) ->
%% Bad0 are badly formed deprecated attributes.
@@ -992,9 +1050,6 @@ no_info(X, L, LC, XC, EE, Unres, NoCalls, NoUnresCalls) ->
%% Note: this is overwritten in do_set_up():
{no_inter_function_calls, no_elements(EE)}].
-inter_graph(Pid, X, L, LC, XC, CallAt) ->
- Pid ! {self(), inter_graph(X, L, LC, XC, CallAt)}.
-
%% Inter Call Graph.
%inter_graph(_X, _L, _LC, _XC, _CallAt) ->
% {empty_set(), empty_set()};
@@ -1766,10 +1821,6 @@ tpack(T, I, L) ->
message(true, What, Arg) ->
case What of
- reading_beam ->
- io:format("~ts... ", Arg);
- skipped_beam ->
- io:format("skipped (no debug information)~n", Arg);
no_debug_info ->
io:format("Skipping ~ts (no debug information)~n", Arg);
unresolved_summary1 ->
@@ -1791,7 +1842,7 @@ message(true, What, Arg) ->
set_up ->
io:format("Setting up...", Arg);
done ->
- io:format("done~n", Arg);
+ io:format("done reading ~ts~n", Arg);
error ->
io:format("error~n", Arg);
Else ->
diff --git a/lib/tools/src/xref_utils.erl b/lib/tools/src/xref_utils.erl
index f69aa70244..b0c168e018 100644
--- a/lib/tools/src/xref_utils.erl
+++ b/lib/tools/src/xref_utils.erl
@@ -47,8 +47,6 @@
-export([options/2]).
--export([subprocess/2]).
-
-export([format_error/1]).
-import(lists, [append/1, delete/2, filter/2, foldl/3, foreach/2,
@@ -512,12 +510,6 @@ find_beam(Culprit) ->
options(Options, Valid) ->
split_options(Options, [], [], [], Valid).
-subprocess(Fun, Opts) ->
- Pid = spawn_opt(Fun, Opts),
- receive
- {Pid, Reply} -> Reply
- end.
-
format_error({error, Module, Error}) ->
Module:format_error(Error);
format_error({file_error, FileName, Reason}) ->