lint: Improve checks for internal macros

* lisp/org-lint.el (org-lint-invalid-macro-argument-and-template): Add
arity checks for internal macros.
* testing/lisp/test-org-lint.el (test-org-lint/invalid-macro-argument-and-template):
Add tests.
This commit is contained in:
Nicolas Goaziou 2021-04-19 02:06:01 +02:00
parent 772f7acb65
commit 628bb4f324
2 changed files with 60 additions and 27 deletions

View File

@ -687,15 +687,42 @@ Use \"export %s\" instead"
reports))
(defun org-lint-invalid-macro-argument-and-template (ast)
(let ((extract-placeholders
(lambda (template)
(let ((start 0)
args)
(while (string-match "\\$\\([1-9][0-9]*\\)" template start)
(setf start (match-end 0))
(push (string-to-number (match-string 1 template)) args))
(sort (org-uniquify args) #'<))))
reports)
(let* ((reports nil)
(extract-placeholders
(lambda (template)
(let ((start 0)
args)
(while (string-match "\\$\\([1-9][0-9]*\\)" template start)
(setf start (match-end 0))
(push (string-to-number (match-string 1 template)) args))
(sort (org-uniquify args) #'<))))
(check-arity
(lambda (arity macro)
(let* ((name (org-element-property :key macro))
(pos (org-element-property :begin macro))
(args (org-element-property :args macro))
(l (length args)))
(cond
((< l (1- (car arity)))
(push (list pos (format "Missing arguments in macro %S" name))
reports))
((< l (car arity))
(push (list pos (format "Missing argument in macro %S" name))
reports))
((> l (1+ (cdr arity)))
(push (let ((spurious-args (nthcdr (cdr arity) args)))
(list pos
(format "Spurious arguments in macro %S: %s"
name
(mapconcat #'org-trim spurious-args ", "))))
reports))
((> l (cdr arity))
(push (list pos
(format "Spurious argument in macro %S: %s"
name
(org-last args)))
reports))
(t nil))))))
;; Check arguments for macro templates.
(org-element-map ast 'keyword
(lambda (k)
@ -730,31 +757,31 @@ Use \"export %s\" instead"
(org-element-map ast 'macro
(lambda (macro)
(let* ((name (org-element-property :key macro))
(args (org-element-property :args macro))
(template (cdr (assoc-string name templates t))))
(pcase template
(`nil
(push (list (org-element-property :begin macro)
(format "Undefined macro %S" name))
reports))
((pred functionp) nil) ;ignore it
((guard (string= name "keyword"))
(funcall check-arity '(1 . 1) macro))
((guard (string= name "modification-time"))
(funcall check-arity '(1 . 2) macro))
((guard (string= name "n"))
(funcall check-arity '(0 . 2) macro))
((guard (string= name "property"))
(funcall check-arity '(1 . 2) macro))
((guard (string= name "time"))
(funcall check-arity '(1 . 1) macro))
((pred functionp)) ;ignore (eval ...) templates
(_
(let ((arg-numbers (funcall extract-placeholders template)))
(when arg-numbers
(let ((spurious-args
(nthcdr (apply #'max arg-numbers)
(org-element-property :args macro))))
(when spurious-args
(push
(list (org-element-property :begin macro)
(pcase spurious-args
(`(,arg)
(format "Unused argument in macro %S: %s"
name arg))
(args
(format "Unused arguments in macro %S: %s"
name
(mapconcat #'org-trim args ", ")))))
reports)))))))))))
(let* ((arg-numbers (funcall extract-placeholders template))
(arity (if (null arg-numbers)
'(0 . 0)
(let ((m (apply #'max arg-numbers)))
(cons m m)))))
(funcall check-arity arity macro))))))))
reports))
(defun org-lint-undefined-footnote-reference (ast)

View File

@ -339,6 +339,12 @@ This is not a node property
(should-not
(org-test-with-temp-text
"#+macro: valid $1 $2\n{{{valid(1, 2)}}}"
(org-lint '(invalid-macro-argument-and-template))))
(should
(org-test-with-temp-text "{{{keyword}}}"
(org-lint '(invalid-macro-argument-and-template))))
(should
(org-test-with-temp-text "{{{keyword(one, too many)}}}"
(org-lint '(invalid-macro-argument-and-template)))))
(ert-deftest test-org-lint/undefined-footnote-reference ()