FlymakeElisp

You can notice unmatched-parens error by using this extension.

This extension uses (scan-sexps) facility which detects unmatched-parens and unmatched-quote only.

Create following file and locate it to $PATH.

elisplint:

#!/bin/bash
:;: '-*-emacs-lisp-*-'
:; exec emacs -Q --batch --load "$0" "$@"
(dolist (file command-line-args-left)
  (with-temp-buffer
    (insert-file-contents file)
    (condition-case data
        (scan-sexps (point-min) (point-max))
      (scan-error (goto-char (nth 2 data))
                  (princ (format "%s:%s: error: Unmatched bracket or quote\n"
                                 file
                                 (line-number-at-pos)))))))

And test it.

$ chmod +x ~/bin/elisplint
$ elisplint tmp/invalid.el
tmp/invalid.el:1: Unmatched bracket or quote

Finally add following code into your .emacs.

(defun flymake-elisp-init ()
  (let* ((temp-file   (flymake-init-create-temp-buffer-copy
                       'flymake-create-temp-inplace))
         (local-file  (file-relative-name
                       temp-file
                       (file-name-directory buffer-file-name))))
    (list "elisplint" (list local-file))))
(push '("\\.el$" flymake-elisp-init) flymake-allowed-file-name-masks)
(add-hook 'emacs-lisp-mode-hook
          ;; workaround for (eq buffer-file-name nil)
          (function (lambda () (if buffer-file-name (flymake-mode)))))

If you don’t want to create external command, use it.

(defun flymake-elisp-init ()
  (unless (string-match "^ " (buffer-name))
    (let* ((temp-file   (flymake-init-create-temp-buffer-copy
                         'flymake-create-temp-inplace))
           (local-file  (file-relative-name
                         temp-file
                         (file-name-directory buffer-file-name))))
      (list
       (expand-file-name invocation-name invocation-directory)
       (list
        "-Q" "--batch" "--eval" 
        (prin1-to-string
         (quote
          (dolist (file command-line-args-left)
            (with-temp-buffer
              (insert-file-contents file)
              (condition-case data
                  (scan-sexps (point-min) (point-max))
                (scan-error
                 (goto-char(nth 2 data))
                 (princ (format "%s:%s: error: Unmatched bracket or quote\n"
                                file (line-number-at-pos)))))))
          )
         )
        local-file)))))
(push '("\\.el$" flymake-elisp-init) flymake-allowed-file-name-masks)
(add-hook 'emacs-lisp-mode-hook
          ;; workaround for (eq buffer-file-name nil)
          (function (lambda () (if buffer-file-name (flymake-mode)))))

That’s all.