emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Emacs-diffs] master 4fbd330: Protect against an infloop in python-mode


From: Philipp Stephani
Subject: [Emacs-diffs] master 4fbd330: Protect against an infloop in python-mode
Date: Thu, 23 Mar 2017 18:07:23 -0400 (EDT)

branch: master
commit 4fbd330fae54a9c45d4a717127aa86d75e9938d5
Author: Philipp Stephani <address@hidden>
Commit: Philipp Stephani <address@hidden>

    Protect against an infloop in python-mode
    
    There appears to be an edge case caused by using `syntax-ppss' in a
    narrowed buffer during JIT lock inside of Python triple-quote strings.
    Unfortunately it is impossible to reproduce without manually
    destroying the syntactic information in the Python buffer, but it has
    been observed in practice.  In that case it can happen that the syntax
    caches get sufficiently out of whack so that there appear to be
    overlapping strings in the buffer.  As Python has no nested strings,
    this situation is impossible and leads to an infloop in
    `python-nav-end-of-statement'.  Protect against this by checking
    whether the search for the end of the current string makes progress.
    
    * python.el (python-nav-end-of-statement): Protect against infloop.
    * progmodes/python-tests.el
    (python-tests--python-nav-end-of-statement--infloop): Add unit test.
---
 lisp/progmodes/python.el            | 16 +++++++++++++---
 test/lisp/progmodes/python-tests.el | 19 +++++++++++++++++++
 2 files changed, 32 insertions(+), 3 deletions(-)

diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index 228a448..2697f1a 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -1491,10 +1491,18 @@ Optional argument NOEND is internal and makes the logic 
to not
 jump to the end of line when moving forward searching for the end
 of the statement."
   (interactive "^")
-  (let (string-start bs-pos)
+  (let (string-start bs-pos (last-string-end 0))
     (while (and (or noend (goto-char (line-end-position)))
                 (not (eobp))
                 (cond ((setq string-start (python-syntax-context 'string))
+                       ;; The assertion can only fail if syntax table
+                       ;; text properties and the `syntax-ppss' cache
+                       ;; are somehow out of whack.  This has been
+                       ;; observed when using `syntax-ppss' during
+                       ;; narrowing.
+                       (cl-assert (> string-start last-string-end)
+                                  :show-args
+                                  "Overlapping strings detected")
                        (goto-char string-start)
                        (if (python-syntax-context 'paren)
                            ;; Ended up inside a paren, roll again.
@@ -1504,8 +1512,10 @@ of the statement."
                          (goto-char (+ (point)
                                        (python-syntax-count-quotes
                                         (char-after (point)) (point))))
-                         (or (re-search-forward (rx (syntax string-delimiter)) 
nil t)
-                             (goto-char (point-max)))))
+                         (setq last-string-end
+                               (or (re-search-forward
+                                    (rx (syntax string-delimiter)) nil t)
+                                   (goto-char (point-max))))))
                       ((python-syntax-context 'paren)
                        ;; The statement won't end before we've escaped
                        ;; at least one level of parenthesis.
diff --git a/test/lisp/progmodes/python-tests.el 
b/test/lisp/progmodes/python-tests.el
index 1e6b867..2f4c2fb 100644
--- a/test/lisp/progmodes/python-tests.el
+++ b/test/lisp/progmodes/python-tests.el
@@ -5314,6 +5314,25 @@ class SomeClass:
       (or enabled (hs-minor-mode -1)))))
 
 
+(ert-deftest python-tests--python-nav-end-of-statement--infloop ()
+  "Checks that `python-nav-end-of-statement' doesn't infloop in a
+buffer with overlapping strings."
+  (python-tests-with-temp-buffer "''' '\n''' ' '\n"
+    (syntax-propertize (point-max))
+    ;; Create a situation where strings nominally overlap.  This
+    ;; shouldn't happen in practice, but apparently it can happen when
+    ;; a package calls `syntax-ppss' in a narrowed buffer during JIT
+    ;; lock.
+    (put-text-property 4 5 'syntax-table (string-to-syntax "|"))
+    (remove-text-properties 8 9 '(syntax-table nil))
+    (goto-char 4)
+    (setq-local syntax-propertize-function nil)
+    ;; The next form should not infloop.  We have to disable
+    ;; ‘debug-on-error’ so that ‘cl-assert’ doesn’t call the debugger.
+    (should-error (let ((debug-on-error nil))
+                    (python-nav-end-of-statement)))
+    (should (eolp))))
+
 
 (provide 'python-tests)
 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]