   :classify el-patch-classify-variable
   :declare ((doc-string 3)
             (indent defun)))
+
+;;;; Validating patches
+
+(defcustom el-patch-pre-validate-hook nil
+  "Hook run before `el-patch-validate-all'.
+Also run before `el-patch-validate' if a prefix argument is
+provided. This hook should contain functions that make sure all
+of your patches are defined (for example, you might need to load
+some features if your patches are lazily defined)."
+  :type 'hook)
+
+(defcustom el-patch-post-validate-hook nil
+  "Hook run after `el-patch-validate-all'.
+Also run after `el-patch-validate' if a prefix argument is
+provided. This hook should contain functions that undo any
+patching that might have taken place in
+`el-patch-pre-validate-hook', if you do not want the patches to
+be defined permanently."
+  :type 'hook)
+
+(defun el-patch--classify-definition-type (type)
+  "Classifies a definition TYPE as a `function' or `variable'.
+TYPE is a symbol `defun', `defmacro', etc."
+  (if-let ((classify (assoc type el-patch-deftype-alist)))
+      (pcase (plist-get (cdr classify) :classify)
+        ('el-patch-classify-function 'function)
+        ('el-patch-classify-define-minor-mode 'function)
+        ('el-patch-classify-variable 'variable))
+    (error "Unexpected definition type `%S'" type)))
+
+(defun el-patch--find-symbol (name type)
+  "Return the Lisp form that defines the symbol NAME.
+Return nil if such a definition cannot be found. (That would
+happen if the definition were generated dynamically.) TYPE is a
+symbol `defun', `defmacro', etc. which is used to determine
+whether the symbol is a function or variable."
+  (let ((classification (el-patch--classify-definition-type type)))
+    (when (pcase classification
+            ('function (fboundp name))
+            ('variable (boundp name)))
+      (let* (;; Since Emacs actually opens the source file in a (hidden)
+             ;; buffer, it can try to apply local variables, which might
+             ;; result in an annoying interactive prompt. Let's disable
+             ;; that.
+             (enable-local-variables nil)
+             (enable-dir-local-variables nil)
+             ;; This is supposed to be noninteractive so we also
+             ;; suppress all the messages. This has the side effect of
+             ;; masking all debugging messages (you can use `insert'
+             ;; instead, or temporarily remove these bindings), but
+             ;; there are just so many different messages that can
+             ;; happen for various reasons and I haven't found any other
+             ;; standard way to suppress them.
+             (inhibit-message t)
+             (message-log-max nil)
+             ;; Now we actually do the find-function operation.
+             (buffer-point (save-excursion
+                             ;; This horrifying bit of hackery on
+                             ;; `get-file-buffer' prevents
+                             ;; `find-function-noselect' from
+                             ;; returning an existing buffer, so that
+                             ;; later on when we jump to the
+                             ;; definition, we don't temporarily
+                             ;; scroll the window if the definition
+                             ;; happens to be in the *current* buffer.
+                             (cl-letf (((symbol-function #'get-file-buffer)
+                                        (symbol-function #'ignore)))
+                               (pcase classification
+                                 ('function
+                                  (find-function-noselect name 'lisp-only))
+                                 ('variable
+                                  (find-variable-noselect name))))))
+             (defun-buffer (car buffer-point))
+             (defun-point (cdr buffer-point)))
+        (and defun-buffer
+             defun-point
+             (with-current-buffer defun-buffer
+               (save-excursion
+                 (goto-char defun-point)
+                 (read defun-buffer))))))))
+
+;;;###autoload
+(defun el-patch-validate (name type &optional nomsg run-hooks)
+  "Validate the patch with given NAME and TYPE.
+This means el-patch will attempt to find the original definition
+for the function, and verify that it is the same as the original
+function assumed by the patch. A warning will be signaled if the
+original definition for a patched function cannot be found, or if
+there is a difference between the actual and expected original
+definitions.
+
+Interactively, use `completing-read' to select a function to
+inspect the patch of.
+
+NAME is a symbol naming the object being patched; TYPE is a
+symbol `defun', `defmacro', etc.
+
+Returns nil if the patch is not valid, and otherwise returns t.
+If NOMSG is non-nil, does not signal a message when the patch is
+valid.
+
+If RUN-HOOKS is non-nil, runs `el-patch-pre-validate-hook' and
+`el-patch-post-validate-hook'. Interactively, this happens unless
+a prefix argument is provided.
+
+See also `el-patch-validate-all'."
+  (interactive (progn
+                 (unless current-prefix-arg
+                   (run-hooks 'el-patch-pre-validate-hook))
+                 (append (el-patch--select-patch)
+                         (list nil (unless current-prefix-arg
+                                     'post-only)))))
+  (unless (member run-hooks '(nil post-only))
+    (run-hooks 'el-patch-pre-validate-hook))
+  (unwind-protect
+      (progn
+        (let* ((patch-definition (el-patch-get name type))
+               (type (car patch-definition))
+               (expected-definition (el-patch--resolve-definition
+                                     patch-definition nil))
+               (name (cadr expected-definition))
+               (actual-definition (el-patch--find-symbol name type)))
+          (cond
+           ((not actual-definition)
+            (display-warning
+             'el-patch
+             (format "Could not find definition of %S `%S'" type name))
+            nil)
+           ((not (equal expected-definition actual-definition))
+
+            (display-warning
+             'el-patch
+             (format (concat "Definition of %S `%S' differs from what "
+                             "is assumed by its patch")
+                     type name))
+            nil)
+           (t
+            (unless nomsg
+              (message "Patch is valid"))
+            t))))
+    (when run-hooks
+      (run-hooks 'el-patch-post-validate-hook))))
+
+;;;###autoload
+(defun el-patch-validate-all ()
+  "Validate all currently defined patches.
+Runs `el-patch-pre-validate-hook' and
+`el-patch-post-validate-hook'.
+
+See `el-patch-validate'."
+  (interactive)
+  (run-hooks 'el-patch-pre-validate-hook)
+  (unwind-protect
+      (let ((patch-count 0)
+            (warning-count 0))
+        (dolist (name (hash-table-keys el-patch--patches))
+          (let ((patch-hash (gethash name el-patch--patches)))
+            (dolist (type (hash-table-keys patch-hash))
+              (setq patch-count (1+ patch-count))
+              (unless (el-patch-validate name type 'nomsg)
+                (setq warning-count (1+ warning-count))))))
+        (cond
+         ((zerop patch-count)
+          (user-error "No patches defined"))
+         ((zerop warning-count)
+          (if (= patch-count 1)
+              (message "Patch is valid (only one defined)")
+            (message "All %d patches are valid" patch-count)))
+         ((= patch-count warning-count)
+          (if (= patch-count 1)
+              (message "Patch is invalid (only one defined)")
+            (message "All %d patches are invalid" patch-count)))
+         (t
+          (message "%s valid, %s invalid"
+                   (if (= warning-count (1- patch-count))
+                       "1 patch is"
+                     (format "%d patches are" (- patch-count warning-count)))
+                   (if (= warning-count 1)
+                       "1 patch is"
+                     (format "%d patches are" warning-count))))))
+    (run-hooks 'el-patch-post-validate-hook)))
+
+;;;;; el-patch-feature
 
 ;;;###autoload
 (defmacro el-patch-feature (feature &rest args)


(provide 'el-patch)

;;; el-patch.el ends here

;; Local Variables:
;; outline-regexp: ";;;;* "
;; End:

          ,definition
          "This function was patched by `el-patch'.")))))

;;;;; Removing patches

;;;###autoload
(defun el-patch-unpatch (name type)
  "Remove the patch given by the PATCH-DEFINITION.
This restores the original functionality of the object being
patched. NAME and TYPE are as returned by `el-patch-get'."
  (interactive (el-patch--select-patch))
  (if-let ((patch-definition (el-patch-get name type)))
      (eval `(el-patch--stealthy-eval
              ,(el-patch--resolve-definition
                patch-definition nil)
              "This function was patched and then unpatched by `el-patch'."))
    (error "There is no patch for %S %S" type name)))

;;;; Defining patch types

;; Use `progn' to cause the entire macro definition to be autoloaded
;; rather than just a stub.
;;;###autoload
(progn
  (cl-defmacro el-patch-deftype
