     (when (fboundp sym)
       (push 'function result))
     result))
+
+;; TODO: consider the following example:
+
+;; (cl-labels ((foo (x y) (+ x y)))
+;;   (foo 1 2))
+;;
+;; If you expand this, you can see `cl-labels' replaces foo with
+;; --cl-foo-- so we can't identify where foo is defined or whether
+;; it's a function call or a variable.
+
+;; TODO: Handle point being on the # in #'foo.
+(defun elisp-def--sharp-quoted-p ()
+  "Is the symbol at point sharp quoted?"
+  (save-excursion
+    (-let [(sym-start . _sym-end) (bounds-of-thing-at-point 'symbol)]
+      (when sym-start
+        (goto-char sym-start)
+        (backward-char 2)
+        (looking-at (rx "#'"))))))
+
+(defun elisp-def--namespace-at-point ()
+  "Is the symbol at point a function/macro, a global variable, a
+quoted variable, or a let-bound variable?"
+  ;; TODO: let bound variables.
+
+  ;; If it's a sharp quoted symbol, we know it's a global function
+  ;; reference.
+  (if (elisp-def--sharp-quoted-p)
+      'function
+    ;; Otherwise, macro expand the source at point and look at how the
+    ;; symbol is used.
+    (let* ((src (elisp-def--source-with-placeholder))
+           (form (read src))
+           (expanded-form (macroexpand-all form))
+           (use (elisp-def--find-use expanded-form 'elisp-def--placeholder)))
+      (setq wh/f expanded-form)
+      use)))
+
+(defun elisp-def--find-use (form sym &optional quoted)
+  "Is SYM being used as a function, a variable, or a quoted
+symbol in FORM?
+
+Assumes FORM has been macro-expanded."
+  (cond
+   ((symbolp form)
+    (if (eq form sym)
+        ;; Normal reference to the variable we're looking for.
+        (if quoted 'quoted 'variable)
+      ;; Unrelated variable.
+      nil))
+   ((consp form)
+    (cond
+     ((eq (car form) sym)
+      ;; Function call for the symbol we're looking for.
+      (if quoted 'quoted 'function))
+     ;; See if this is a quoted form that contains SYM.
+     ((eq (car form) 'quote)
+      (--any (elisp-def--find-use it sym t) (cdr form)))
+     ;; Recurse on the form to see if any arguments contain SYM.
+     (t
+      (--any (elisp-def--find-use it sym quoted) form))))
+   ((vectorp form)
+    ;; All elements in a vector are quoted.
+    (--any (elisp-def--find-use it sym t)
+           (mapcar #'identity form)))))
+
+(ert-deftest elisp-def--find-use ()
+  (should
+   (eq (elisp-def--find-use '(foo bar) 'foo)
+       'function))
+  (should
+   (eq (elisp-def--find-use '(foo bar) 'bar)
+       'variable))
+  (should
+   (eq (elisp-def--find-use [foo] 'foo)
+       'quoted))
+  (should
+   (eq (elisp-def--find-use '(foo '(bar)) 'bar)
+       'quoted))
+  (should
+   (eq (elisp-def--find-use '(let ((foo (bar)))) 'bar)
+       'function)))
+
+(defun elisp-def--source-with-placeholder ()
+  "Return the source enclosing the symbol at point,
+but with the symbol itself replaced by a placeholder."
+  (let* ((placeholder-sym "elisp-def--placeholder")
+         (start-pos (point))
+         form-start form-end)
+    ;; Find the limits of the top-level expression around point.
+    (save-excursion
+      (beginning-of-defun)
+      (setq form-start (point))
+      (end-of-defun)
+      (setq form-end (point)))
+    ;; Copy that top-level expression into a separate buffer, so we
+    ;; can modify the source.
+    (let ((src (buffer-substring-no-properties form-start form-end)))
+      (with-temp-buffer
+        (insert src)
+        ;; Replace the original symbol at point with a placeholder, so
+        ;; we can distinguish it from other occurrences of this symbol within
+        ;; the sexp.
+        (goto-char (- start-pos form-start))
+        (-let [(sym-start . sym-end) (bounds-of-thing-at-point 'symbol)]
+          (delete-region sym-start sym-end))
+        (insert placeholder-sym)
+        (buffer-string)))))
 
 (defun elisp-def--join-and (items)
   "Join a list of strings with commas and \"and\"."

(defun elisp-def ()
  "Go to the definition of the symbol at point."
  (interactive)
  (-let* ((sym (symbol-at-point))
          (sym-name (symbol-name sym))
          (namespaces (elisp-def--namespaces sym))
          (namespace nil))
    (if (> (length namespaces) 1)
        ;; User chooses.
        (let* ((formatted-namespaces
                (elisp-def--join-and
                 (--map (format "a %s" it) namespaces)))
               (prompt (format "%s is %s, choose: "
                               sym-name
                               formatted-namespaces)))
          (setq namespace
                (intern
                 (completing-read prompt namespaces nil t))))

      ;; This symbol is only bound in one namespace.
      (setq namespace (car namespaces)))

    (-let [(buf pos) (elisp-def--find-global sym (eq namespace 'function))]
      (unless (and buf pos)

        (error nil))))
    (list buf pos)))

(defun elisp-def--defined-in (sym)
  "All the namespaces that SYM is globally defined in.
Returns a list '(function variable).

