Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Buffer freezes on (def ^{:macro}) #612

Closed
borkdude opened this issue Apr 1, 2022 · 7 comments · Fixed by #624
Closed

Buffer freezes on (def ^{:macro}) #612

borkdude opened this issue Apr 1, 2022 · 7 comments · Fixed by #624
Labels

Comments

@borkdude
Copy link

borkdude commented Apr 1, 2022

Expected behavior

When I have (def ^{:macro}) in my buffer (as the only code), I should see no freezing of emacs.

Actual behavior

Emacs with clojure-mode freezes.

Steps to reproduce the problem

Put (def ^{:macro}) as the sole code in a clojure buffer. Try to move the cursor a bit back and forth.

When Emacs freezes, send:

pkill -USR2 Emacs

and then see something like:

Debugger entered--entering a function:
* #f(compiled-function () #<bytecode 0x1fe4d41470e9>)()
  imenu--generic-function(((nil clojure-match-next-def 0)))
  imenu-default-create-index-function()
  imenu--make-index-alist(t)
  which-function()
  which-func-update-1(#<window 3 on foo.clj>)
  which-func-update()
  apply(which-func-update nil)
  timer-event-handler([t 0 0 500000 t which-func-update nil idle 0])
  recursive-edit()
  debug(lambda)
* thing-at-point--end-of-sexp()
  bounds-of-thing-at-point(sexp)
  clojure-match-next-def()
  imenu--generic-function(((nil clojure-match-next-def 0)))
  imenu-default-create-index-function()
  imenu--make-index-alist(t)
  which-function()
  which-func-update-1(#<window 3 on foo.clj>)
  which-func-update()
  apply(which-func-update nil)
  timer-event-handler([t 0 0 500000 t which-func-update nil idle 0])

@vemv hinted that this looked suspicious:

(while (not found?)

Environment & Version information

clojure-mode version

clojure-mode (version 5.13.10)

Emacs version

27.2

Operating system

macOS

@borkdude borkdude changed the title Buffer freezes on specific code example: (def ^{:macro}} Buffer freezes on specific code example: (def ^{:macro}) Apr 1, 2022
@vemv vemv changed the title Buffer freezes on specific code example: (def ^{:macro}) Buffer freezes on (def ^{:macro}) Apr 2, 2022
@vemv
Copy link
Member

vemv commented Apr 2, 2022

I can repro this by invoking (clojure-match-next-def) in clojure-mode latest.

I could also fix it by changing here:

(if (char-equal ?^ (char-after def-beg))
(progn (forward-sexp) (backward-sexp))

-(progn (forward-sexp) (backward-sexp))
+(forward-sexp)

I can't make sense of (progn (forward-sexp) (backward-sexp)), it seems a no-op that naturally would cause an infinite loop in conjunction with the (while (not found?)?

With my changed version, it completes and the function still does what I believe it does.

I have no idea of what imenu--generic-function is though, so perhaps someone else could take it from here?

@vemv
Copy link
Member

vemv commented Apr 2, 2022

...according to Git blame, all this code is remarkably old and therefore presumably stable.

Maybe this Imenu thing is not used enough to surface such bugs, does that sound likely?

@magnars
Copy link
Contributor

magnars commented Apr 2, 2022

Maybe this Imenu thing is not used enough to surface such bugs, does that sound likely?

I use imenu all the time, but then again I rarely type (def ^{:macro}) in a buffer, to be fair.

@vemv
Copy link
Member

vemv commented Apr 3, 2022

@magnars or @borkdude - PR welcome with the described change or similar if you find that it fixes the thing / doesn't visibly break sth else (more freezes / bad Imenu integration)

Since I don't use Imenu I wouldn't feel at ease proposing a fix myself.

Cheers - V

@bbatsov bbatsov added the bug label Apr 21, 2022
@OknoLombarda
Copy link
Contributor

OknoLombarda commented Jul 16, 2022

I've looked into this problem and managed to figure out the cause of infinite loop, I think. This (progn (forward-sexp) (backward-sexp)) code is used to skip a var metadata and move point to var name. For example, if we have this definition

(def ^String foo "bar")

before moving, the point will be just after the word String and consequent calls to (forward-sexp) and (backward-sexp) will move point just before the word foo, but if you're just typing the definition and there's no var name yet ((def ^String)) then this will bring the point back to ^, causing infinite loop. Solution proposed by @vemv will not work in cases where metadata symbol is the last sexp before closing parenthesis, because it will infinitely try to go to next sexp, with no success. #595 is example of such case

I'm new to Clojure and don't know a single thing about Emacs Lisp, but here's a solution I came up with

(if (char-equal ?^ (char-after def-beg))

-            (if (char-equal ?^ (char-after def-beg))
-                (progn (forward-sexp) (backward-sexp))
+            (when (char-equal ?^ (char-after def-beg))
+              (progn (forward-sexp) (backward-sexp)))
+            (when (or (not (char-equal ?^ (char-after (point))))
+                      (and (char-equal ?^ (char-after (point))) (= def-beg (point))))
               (setq found? t)
               (when (string= deftype "defmethod")
                 (setq def-end (progn (goto-char def-end)
                                      (forward-sexp)
                                      (point))))
              (set-match-data (list def-beg def-end)))

So, when metadata symbol is encountered, function will try to move to the beginning of the next sexp, and if the point is still where it was before, then it should exit the loop

OknoLombarda pushed a commit to OknoLombarda/clojure-mode that referenced this issue Jul 16, 2022
OknoLombarda pushed a commit to OknoLombarda/clojure-mode that referenced this issue Jul 16, 2022
OknoLombarda pushed a commit to OknoLombarda/clojure-mode that referenced this issue Jul 16, 2022
@vemv vemv closed this as completed in #624 Jul 19, 2022
@vemv
Copy link
Member

vemv commented Jul 19, 2022

Released as clojure-mode 5.15.0, which will be available within a couple hours

@borkdude
Copy link
Author

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
5 participants