From 6290da183c5b01962f2f2dad453a4aef1f55b34b Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou Date: Tue, 30 Oct 2012 09:24:55 +0100 Subject: [PATCH] Signal an error when a circular macro expansion happens * lisp/org.el (org-macro-replace-all): Signal an error when a circular macro expansion happens. (org-macro-initialize-templates): Fix docstring. * testing/lisp/test-org.el: Add test. --- lisp/org.el | 46 +++++++++++++++++++++++++--------------- testing/lisp/test-org.el | 8 ++++++- 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/lisp/org.el b/lisp/org.el index 63c432380..9ba39a726 100644 --- a/lisp/org.el +++ b/lisp/org.el @@ -20927,28 +20927,40 @@ TEMPLATES is an alist of templates used for expansion. See `org-macro-templates' for a buffer-local default value." (save-excursion (goto-char (point-min)) - (while (re-search-forward "{{{[-A-Za-z0-9_]" nil t) - (let ((object (org-element-context))) - (when (eq (org-element-type object) 'macro) - (let ((value (org-macro-expand object templates))) - (when value - (delete-region - (org-element-property :begin object) - ;; Preserve white spaces after the macro. - (progn (goto-char (org-element-property :end object)) - (skip-chars-backward " \t") - (point))) - ;; Leave point before replacement in case of recursive - ;; expansions. - (save-excursion (insert value))))))))) + (let (record) + (while (re-search-forward "{{{[-A-Za-z0-9_]" nil t) + (let ((object (org-element-context))) + (when (eq (org-element-type object) 'macro) + (let* ((value (org-macro-expand object templates)) + (begin (org-element-property :begin object)) + (signature (list begin + object + (org-element-property :args object)))) + ;; Avoid circular dependencies by checking if the same + ;; macro with the same arguments is expanded at the same + ;; position twice. + (if (member signature record) + (error "Circular macro expansion: %s" + (org-element-property :key object)) + (when value + (push signature record) + (delete-region + begin + ;; Preserve white spaces after the macro. + (progn (goto-char (org-element-property :end object)) + (skip-chars-backward " \t") + (point))) + ;; Leave point before replacement in case of recursive + ;; expansions. + (save-excursion (insert value))))))))))) (defun org-macro-initialize-templates () "Collect macro templates defined in current buffer. Templates are stored in buffer-local variable `org-macro-templates'. In addition to buffer-defined macros, the -function installs the following ones: \"property\", \"date\", -\"time\". and, if appropriate, \"input-file\" and -\"modification-time\"." +function installs the following ones: \"property\", +\"time\". and, if the buffer is associated to a file, +\"input-file\" and \"modification-time\"." (let ((case-fold-search t) (set-template (lambda (cell) diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el index 58a8b30b4..b0a073a05 100644 --- a/testing/lisp/test-org.el +++ b/testing/lisp/test-org.el @@ -410,7 +410,13 @@ http://article.gmane.org/gmane.emacs.orgmode/21459/" "#+MACRO: in inner\n#+MACRO: out {{{in}}} outer\n{{{out}}}" (progn (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) - (buffer-string)))))) + (buffer-string))))) + ;; Error out when macro expansion is circular. + (should-error + (org-test-with-temp-text + "#+MACRO: mac1 {{{mac2}}}\n#+MACRO: mac2 {{{mac1}}}\n{{{mac1}}}" + (org-macro-initialize-templates) + (org-macro-replace-all org-macro-templates))))