diff options
Diffstat (limited to 'lib/tools')
-rw-r--r-- | lib/tools/emacs/erlang-test.el | 57 | ||||
-rw-r--r-- | lib/tools/emacs/erlang.el | 160 | ||||
-rw-r--r-- | lib/tools/src/xref_base.erl | 215 | ||||
-rw-r--r-- | lib/tools/src/xref_utils.erl | 8 |
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}) -> |