From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Harald =?UTF-8?Q?J=C3=B6rg?= Newsgroups: gmane.emacs.bugs Subject: bug#10483: [PATCH] cperl-mode: Avoid endless loop Date: Tue, 1 Sep 2020 18:12:31 +0200 Message-ID: <922046dc-34fa-487d-4695-6597f8db148e@posteo.de> References: <83o8mqwwx2.fsf@gnu.org> <83k0xewszf.fsf@gnu.org> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="------------76D0F00860343B40124B75C8" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="11368"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Thunderbird/68.12.0 To: 10483@debbugs.gnu.org Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Tue Sep 01 18:13:10 2020 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1kD8uE-0002qZ-3i for geb-bug-gnu-emacs@m.gmane-mx.org; Tue, 01 Sep 2020 18:13:10 +0200 Original-Received: from localhost ([::1]:51034 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kD8uD-000212-1x for geb-bug-gnu-emacs@m.gmane-mx.org; Tue, 01 Sep 2020 12:13:09 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:35390) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kD8u6-00020r-1H for bug-gnu-emacs@gnu.org; Tue, 01 Sep 2020 12:13:02 -0400 Original-Received: from debbugs.gnu.org ([209.51.188.43]:45383) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1kD8u5-0005qr-Oa for bug-gnu-emacs@gnu.org; Tue, 01 Sep 2020 12:13:01 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1kD8u5-0001lx-IT for bug-gnu-emacs@gnu.org; Tue, 01 Sep 2020 12:13:01 -0400 X-Loop: help-debbugs@gnu.org Resent-From: Harald =?UTF-8?Q?J=C3=B6rg?= Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Tue, 01 Sep 2020 16:13:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 10483 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: confirmed Original-Received: via spool by 10483-submit@debbugs.gnu.org id=B10483.15989767626787 (code B ref 10483); Tue, 01 Sep 2020 16:13:01 +0000 Original-Received: (at 10483) by debbugs.gnu.org; 1 Sep 2020 16:12:42 +0000 Original-Received: from localhost ([127.0.0.1]:56929 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1kD8tl-0001lM-N5 for submit@debbugs.gnu.org; Tue, 01 Sep 2020 12:12:42 -0400 Original-Received: from mout02.posteo.de ([185.67.36.66]:49885) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1kD8tj-0001l9-NQ for 10483@debbugs.gnu.org; Tue, 01 Sep 2020 12:12:40 -0400 Original-Received: from submission (posteo.de [89.146.220.130]) by mout02.posteo.de (Postfix) with ESMTPS id E327A2400FC for <10483@debbugs.gnu.org>; Tue, 1 Sep 2020 18:12:32 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=posteo.de; s=2017; t=1598976752; bh=6hgQHoUctkBkr6uvgg6qpPdb+yIBhyFXxlXvtOmhv1U=; h=Subject:To:From:Autocrypt:Date:From; b=eC6lSrOYkfJZll46l4NQP8iaaylidMQaC8pyJHh7d+UGTMKY8BT9JsjEGjGx8LIVP XhWyor0USJqyRxUz/qsGTwdS/9t/eQhiI+jtaaw/5HcIXanhJvquvMHJiD1Kbt1hlD TI7jVaheMsci75tRfdESRXOC9bxCX7cDoKA1wtiZWKJSV7gx21Y67BSHC532FWIOo2 4OilvYs5wD3ZAvHOvxmrjIiK2TLRNY5IS/VwljVU/X6ErGzBKMmjJlT71M5OzgAEop MoA5sCjTq9NLYiSZhWY4Rv4XOiPlgXllu7MzZ3Qtj0FlrSliUni5OPyQP2TCquactb HSAnPUYmtByug== Original-Received: from customer (localhost [127.0.0.1]) by submission (posteo.de) with ESMTPSA id 4BgsXw1bMyz9rxf for <10483@debbugs.gnu.org>; Tue, 1 Sep 2020 18:12:32 +0200 (CEST) Autocrypt: addr=haj@posteo.de; keydata= mQINBF0DVrIBEAC7TRlIilBg/5Dx7R2rV4FBDEavbyMT2cS2wWyksh7JA8e17LNmTdakf+Bm u6C2m6+frolteYRlkreoyAg0fj/5tDw/s5dJDw3tM0b+m/pj2HcwTSRpQSEy5lBiCg11E6Mt F9UngCfkGtJzu8PBWXc1RDQVBwRn5MZbpMZZatE8NOq9AA3Yg6sY11Ez3xIvYU2R7a4OSxmc RJV2+dhdQ4oOTBIGQ2urQSU81kyRtkx+/Yh8WefMrk9nF3nzYziLS53euCAXyzklwYmD6Li3 XSu7wYp5fISky9DK6DPFK7h4RXEL4GFAhy31ehrVnNTcGRohM1Dm9BV9dwXkwlaeT3q0X+nE SXVqPDHtv748bXNIg2WVCZ0WgShcA46XVlSE9Fm+w0HI/1m1L4BaMmQ7gyK5dEchbhxohkk0 AqC3hEJ4ULRidrsCoCWGMFKiOIb9rjeC00vhrUkVJDkPWEGLXjhnRQykb6cESko0rPrSDLqX 9YqaNQ6KO9Un6uogE7YnXYg35EXJ8ORdX257IbXNbFVpgM8FFsl48WV3Pl9WNdADhnnOrz3O wzyK58i/BTsnjVX8ghbWkQcPQ2Kprt+4YR2trEi7TM5Uqzzrg0vp7Oe/9KZBxdxCE67thV7h ElW3CHb4q6o31b4cFmLbtFesSy+hnUU4UymgHgi/hvhxioBxIQARAQABtBxIYXJhbGQgSsO2 cmcgPGhhakBwb3N0ZW8uZGU+iQJOBBMBCAA4FiEEIpYEZLQynxF8Q17KN4XzzzqAcpsFAl0D VrICGwMFCwkIBwIGFQgJCgsCBBYCAwECHgECF4AACgkQN In-Reply-To: <83k0xewszf.fsf@gnu.org> Content-Language: en-US X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: "bug-gnu-emacs" Xref: news.gmane.io gmane.emacs.bugs:186864 Archived-At: This is a multi-part message in MIME format. --------------76D0F00860343B40124B75C8 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit [Re-sent to list: my first reply went to Eli Zaretskii only. Sorry.] On 8/31/20 8:49 PM, Eli Zaretskii wrote: >> Cc: 10483@debbugs.gnu.org >> From: Harald Jörg >> Date: Mon, 31 Aug 2020 20:08:06 +0200 >> >> I was hesitating in this particular case because _if_ this issue ever >> happens again, then the test just hangs forever. >> >> Is there a safeguard against this? > > You could run the test in a subordinate Emacs process, and have the > parent process wait for it to complete with a timeout. So be it: My attempt to do this is attached, together with tests to verify that my loop termination isn't harmful to the function. As always, a review is welcome. I still haven't much experience in Elisp. I haven't found examples to steal from, and also missed the fork/waitpid stuff which I use for such tasks in Perl. -- Cheers, haj --------------76D0F00860343B40124B75C8 Content-Type: text/x-patch; charset=UTF-8; name="0001-2020-09-01-Harald-J-rg-haj-posteo.de.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0001-2020-09-01-Harald-J-rg-haj-posteo.de.patch" >From eb41f40648ee6696acc54964f3aac364336c06e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20J=C3=B6rg?= Date: Tue, 1 Sep 2020 15:50:54 +0200 Subject: [PATCH] =?UTF-8?q?2020-09-01=20=20Harald=20J=C3=B6rg=20=20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * test/lisp/progmodes/cperl-mode-tests.el (cperl-mode-test-bug-10483): Add this test to verify that Emacs doesn't hang over a Perl statement which isn't terminated on the same line. * test/lisp/progmodes/cperl-mode-tests.el (cperl-mode-test-indent-exp): Verify that the fix avoiding the endless loop doesn't stop indenting from working properly. * test/lisp/progmodes/cperl-mode-resources/cperl-indent-exp.pl: Code and result snippets for testing `cperl-indent-exp' --- .../cperl-mode-resources/cperl-indent-exp.pl | 52 +++++++++++ test/lisp/progmodes/cperl-mode-tests.el | 91 ++++++++++++++++++- 2 files changed, 139 insertions(+), 4 deletions(-) create mode 100644 test/lisp/progmodes/cperl-mode-resources/cperl-indent-exp.pl diff --git a/test/lisp/progmodes/cperl-mode-resources/cperl-indent-exp.pl b/test/lisp/progmodes/cperl-mode-resources/cperl-indent-exp.pl new file mode 100644 index 0000000000..4a9842ffa5 --- /dev/null +++ b/test/lisp/progmodes/cperl-mode-resources/cperl-indent-exp.pl @@ -0,0 +1,52 @@ +#!/usr/bin/env perl +use strict; +use warnings; +use 5.020; + +# This file contains test input and expected output for the tests in +# cperl-mode-tests.el, cperl-mode-test-indent-exp. The code is +# syntactically valid, but doesn't make much sense. + +# -------- for loop: input -------- +for my $foo (@ARGV) +{ +...; +} +# -------- for loop: expected output -------- +for my $foo (@ARGV) { + ...; +} +# -------- for loop: end -------- + +# -------- while loop: input -------- +{ +while (1) +{ +say "boring loop"; +} +continue +{ +last; # no endless loop, though +} +} +# -------- while loop: expected output -------- +{ + while (1) { + say "boring loop"; + } continue { + last; # no endless loop, though + } +} +# -------- while loop: end -------- + +# -------- if-then-else: input -------- +if (my $foo) { bar() } elsif (quux()) { baz() } else { quuux } +# -------- if-then-else: expected output -------- +if (my $foo) { + bar(); +} elsif (quux()) { + baz(); +} else { + quuux; +} +# -------- if-then-else: end -------- diff --git a/test/lisp/progmodes/cperl-mode-tests.el b/test/lisp/progmodes/cperl-mode-tests.el index be8b42d99a..61412025fc 100644 --- a/test/lisp/progmodes/cperl-mode-tests.el +++ b/test/lisp/progmodes/cperl-mode-tests.el @@ -9,15 +9,20 @@ ;;; Commentary: -;; This is a collection of tests for the fontification of CPerl-mode. - -;; Run these tests interactively: -;; (ert-run-tests-interactively '(tag :fontification)) +;; This is a collection of tests for CPerl-mode. ;;; Code: (defvar cperl-test-mode #'cperl-mode) +(defvar cperl-mode-tests-data-directory + (expand-file-name "lisp/progmodes/cperl-mode-resources" + (or (getenv "EMACS_TEST_DIRECTORY") + (expand-file-name "../../../" + (or load-file-name + buffer-file-name)))) + "Directory containing cperl-mode test data.") + (defun cperl-test-ppss (text regexp) "Return the `syntax-ppss' of the first character matched by REGEXP in TEXT." (interactive) @@ -48,4 +53,82 @@ cperl-mode-test-bug-42168 (let ((code "{ $a- / $b } # /")) (should (equal (nth 8 (cperl-test-ppss code "/")) 7)))) +(defun cperl-mode-test--run-bug-10483 () + "Runs a short program, intended to be under timer scrutiny. +This function is intended to be used by an Emacs subprocess in +batch mode. The message buffer is used to report the result of +running `cperl-indent-exp' for a very simple input. The result +is expected to be different from the input, to verify that +indentation actually takes place.." + (let ((code "poop ('foo', \n'bar')")) ; see the bug report + (message "Test Bug#10483 started") + (with-temp-buffer + (insert code) + (funcall cperl-test-mode) + (goto-char (point-min)) + (search-forward "poop") + (cperl-indent-exp) + (message "%s" (buffer-string))))) + +(ert-deftest cperl-mode-test-bug-10483 () + "Verifies that a piece of code which ends in a paren without a +statement terminato ron tne same line does not loop forever. The +test starts an asynchronous Emacs batch process under timeout +control." + (interactive) + (let* ((emacs (concat invocation-directory invocation-name)) + (test-function 'cperl-mode-test--run-bug-10483) + (test-function-name (symbol-name test-function)) + (test-file (symbol-file test-function 'defun)) + (ran-out-of-time nil) + (process-connection-type nil) + runner) + (with-temp-buffer + (with-timeout (1 + (delete-process runner) + (setq ran-out-of-time t)) + (setq runner (start-process "speedy" + (current-buffer) + emacs + "-batch" + "--quick" + "--load" test-file + "--funcall" test-function-name)) + (while (accept-process-output runner))) + (should (equal ran-out-of-time nil)) + (goto-char (point-min)) + ;; just a very simple test for indentation: This should + ;; be rather robust with regard to indentation defaults + (should (string-match + "poop ('foo', \n 'bar')" (buffer-string)))))) + +(ert-deftest cperl-mode-test-indent-exp () + "Run various tests for `cperl-indent-exp' edge cases. +These exercise some standard blocks and also the special +treatment for Perl expressions where a closing paren isn't the +end of the statement." + (let ((file (expand-file-name "cperl-indent-exp.pl" + cperl-mode-tests-data-directory))) + (with-temp-buffer + (insert-file-contents file) + (goto-char (point-min)) + (while (re-search-forward + (concat "^# ?-+ \\_<\\(?1:.+?\\)\\_>: input ?-+\n" + "\\(?2:\\(?:.*\n\\)+?\\)" + "# ?-+ \\1: expected output ?-+\n" + "\\(?3:\\(?:.*\n\\)+?\\)" + "# ?-+ \\1: end ?-+") + nil t) + (let ((name (match-string 1)) + (code (match-string 2)) + (expected (match-string 3)) + got) + (with-temp-buffer + (insert code) + (goto-char (point-min)) + (cperl-indent-exp) ; here we go! + (setq expected (concat "test case " name ":\n" expected)) + (setq got (concat "test case " name ":\n" (buffer-string))) + (should (equal got expected)))))))) + ;;; cperl-mode-tests.el ends here -- 2.20.1 --------------76D0F00860343B40124B75C8--