From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED!not-for-mail From: Philipp Stephani Newsgroups: gmane.emacs.devel Subject: Re: [PATCH] Bound index checks in cl-seq functions Date: Sat, 04 Feb 2017 20:42:10 +0000 Message-ID: References: <87efzdrjsj.fsf@calancha-pc> NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: multipart/alternative; boundary=001a113db4b25894e30547ba6fd3 X-Trace: blaine.gmane.org 1486240960 31288 195.159.176.226 (4 Feb 2017 20:42:40 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Sat, 4 Feb 2017 20:42:40 +0000 (UTC) To: Tino Calancha , Emacs developers Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Sat Feb 04 21:42:35 2017 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by blaine.gmane.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ca7AI-0007sL-HC for ged-emacs-devel@m.gmane.org; Sat, 04 Feb 2017 21:42:34 +0100 Original-Received: from localhost ([::1]:40811 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ca7AO-0005i3-0c for ged-emacs-devel@m.gmane.org; Sat, 04 Feb 2017 15:42:40 -0500 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:45730) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ca7AA-0005hv-8m for emacs-devel@gnu.org; Sat, 04 Feb 2017 15:42:29 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ca7A6-0000oS-Fo for emacs-devel@gnu.org; Sat, 04 Feb 2017 15:42:26 -0500 Original-Received: from mail-ot0-x230.google.com ([2607:f8b0:4003:c0f::230]:36287) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1ca7A6-0000oJ-5i for emacs-devel@gnu.org; Sat, 04 Feb 2017 15:42:22 -0500 Original-Received: by mail-ot0-x230.google.com with SMTP id 32so37994799oth.3 for ; Sat, 04 Feb 2017 12:42:21 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:references:in-reply-to:from:date:message-id:subject:to; bh=pX+AlwSewRWPyR+RLTDFZf14CmJlzlsXCdLZAaHIZFM=; b=ZSuVt9S4VtEV0RbnLRlL6hulSoRiv8vYy+d+zOLtQeXZgHDba4lJrF/Iqu3iry+9lW 5D9yWZicPLKpvSS1clqVvjBzGvnA66JJu6DZaw+VmTmquyAo/DtLtHu+VBHth0mLPYCT VwjxelFOI4axXbvjY3i27EAfIIiKhxCPAALsta+rZre7SJ43pvcx4381xpy90tElpIL5 WUyrweifBMedijQEGjg8k34CxdZI5xanF/EYXJa16WaIkbgA2PBEZgNrO/WxuNWUTLGO 5QaoufX/AwNwugP9bMZK+CmX5pIMhF9mo9XsAR8i0+F3sfjXqUvLtJvbZUTbAf/hqOQO Y1IQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to; bh=pX+AlwSewRWPyR+RLTDFZf14CmJlzlsXCdLZAaHIZFM=; b=nCjHMyyZcn5gT4/Vtpui15m3Z+4rCJ2Ac6mvBk7E8i1AVfcUNr0TvG74IgXOEjAygK CENFskuz9+EwPdGN+7sEMVvhziLL8sko7c2k9xfEVQlA/UYxcS+kM4GjScjFfaGsH9G4 ea8a6gflzWRbzcKL5uSKI3lBpnLAsAcb+5QLVstpP3CCHMbe6HnQ8ZtQrRbkCl9sUnSJ 4uqQIJ9fj7QuNjenv4iSWtniIkGOTshcIRXhUouG18IZKt9nEbZC2u+H49Jv8t++KgcB UDEKzIKVtb6PsejvqvV5sJJKRgGHDU64jvZVwm6eoSfNHl56Stx7x5gxcvVAGpwRzRqu YTOA== X-Gm-Message-State: AIkVDXKfhNzhpG37oLdSzjVYCEvE0+AyoT7x/2FzE2/mXwXGYj74EV8ll0xzjSWcJkKsIZ3I8qpg/KoJ4uFxXg== X-Received: by 10.157.37.209 with SMTP id q75mr1898384ota.267.1486240941116; Sat, 04 Feb 2017 12:42:21 -0800 (PST) In-Reply-To: <87efzdrjsj.fsf@calancha-pc> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 2607:f8b0:4003:c0f::230 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Original-Sender: "Emacs-devel" Xref: news.gmane.org gmane.emacs.devel:211969 Archived-At: --001a113db4b25894e30547ba6fd3 Content-Type: text/plain; charset=UTF-8 Tino Calancha schrieb am Sa., 4. Feb. 2017 um 17:41 Uhr: > > Consider following evaluations: > > (cl-position 2 (list 1 2 3) :start -999) > => -998 > (cl-position 2 (list 1 2 3) :start 999) > => nil > (cl-position 2 (list 1 2 3) :start 1 :end 0) > => nil > > *) In the first two, the indices are out of range. > *) In the 3rd, the start index is higher than the end index. > > Guy L. Steele recommends to throw an error in those cases: > http://www.lispworks.com/documentation/HyperSpec/Issues/iss332_w.htm > > We have partially done the job, at least for `cl-subseq': > (cl-subseq (list 1 2 3) 999) > ; Signal: "Bad bounding indices: 999, nil" > > (cl-subseq (list 1 2 3) -999) > ; Signal: "Bad bounding indices: -999, nil" > > (cl-subseq (list 1 2 3) -1) > => (3) ; This is OK as documented in the docstring. > > See: > https://lists.gnu.org/archive/html/emacs-devel/2015-08/msg00182.html > > It would be nice if all the sequence functions in cl-seq.el > throw errors in those situations. > > What do you think? > I agree that these should throw errors. > The following patch implements such bounding checks for the indices. > Comments are very welcome. > > > ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; > From c076a16716d6b9ad5c971d67375223974df53a40 Mon Sep 17 00:00:00 2001 > From: Tino Calancha > Date: Sat, 4 Feb 2017 21:53:48 +0900 > Subject: [PATCH 1/3] cl--parsing-keywords: Check for negative indices > > * lisp/emacs-lisp/cl-seq.el (cl--parsing-keywords): > Throw and error when the user inputs a negative index for the sequence. > --- > lisp/emacs-lisp/cl-seq.el | 8 +++++++- > 1 file changed, 7 insertions(+), 1 deletion(-) > > diff --git a/lisp/emacs-lisp/cl-seq.el b/lisp/emacs-lisp/cl-seq.el > index 67ff1a00bd..558adf22fe 100644 > --- a/lisp/emacs-lisp/cl-seq.el > +++ b/lisp/emacs-lisp/cl-seq.el > @@ -59,7 +59,13 @@ cl--parsing-keywords > (setq mem `(and ,mem (setq cl-if ,mem) t))) > (list (intern > (format "cl-%s" (substring (symbol-name var) 1))) > - (if (consp x) `(or ,mem ,(car (cdr x))) mem)))) > + `(if (and (string-match ":\\(start\\|end\\)" > (symbol-name ,var)) > I think you can replace the string-match with (memq ,var '(:start :end)) > + (integerp (or ,mem ,(car (cdr-safe x)))) > + (not (natnump (or ,mem ,(car (cdr-safe > x)))))) > + (error "Wrong negative index '%s': natnump, %s" > + (substring (symbol-name ,var) 1) > Please use (signal 'args-out-of-range ...) to be more specific and consistent with similar checks. > + (or ,mem ,(car (cdr-safe x)))) > + (or ,mem ,(car (cdr-safe x))))))) > kwords) > ,@(append > (and (not (eq other-keys t)) > -- > 2.11.0 > > From 9898718b96109c5815b81f8dc8868de4d86db453 Mon Sep 17 00:00:00 2001 > From: Tino Calancha > Date: Sun, 5 Feb 2017 00:04:12 +0900 > Subject: [PATCH 2/3] cl--check-bound-indices: Check for unbound indices > > Throw and error if the user inputs an index higher than the sequence length > or if :start value is higher than :end value. > * lisp/emacs-lisp/cl-seq.el (cl--check-bound-indices): New defun. > (cl-reduce, cl-fill, cl-replace, cl-remove, cl-delete) > (cl--delete-duplicates, cl-substitute, cl-nsubstitute, cl-position) > (cl-count, cl-mismatch, cl-search): Use it. > --- > lisp/emacs-lisp/cl-seq.el | 51 > +++++++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 51 insertions(+) > > diff --git a/lisp/emacs-lisp/cl-seq.el b/lisp/emacs-lisp/cl-seq.el > index 558adf22fe..0cb7ac7bdd 100644 > --- a/lisp/emacs-lisp/cl-seq.el > +++ b/lisp/emacs-lisp/cl-seq.el > @@ -42,6 +42,7 @@ > ;;; Code: > > (require 'cl-lib) > +(eval-when-compile (require 'subr-x)) > > ;; Keyword parsing. > ;; This is special-cased here so that we can compile > @@ -118,6 +119,44 @@ cl-test > (defvar cl-if) (defvar cl-if-not) > (defvar cl-key) > > +;; Throw an error when :start or :end are > sequence length. > +(defmacro cl--check-bound-indices (cl-seq1 cl-seq2 cl-keys) > + (declare (debug t)) > + `(let ((len1 (length ,cl-seq1)) > + (len2 (and ,cl-seq2 (length ,cl-seq2))) > + (kwds (list :start1 :start2 :start :end1 :end2 :end)) > + (keys ,cl-keys) > + alist) > + (while keys > + (when (and (memq (car keys) kwds) > + (string-match ":\\(start\\|end\\)\\([1-2]?\\)\\'" > + (symbol-name (car keys)))) > + (delq (car keys) kwds) ; Ignore succesive equal keys. > + (let* ((idx (match-string 2 (symbol-name (car keys)))) > + (len (if (equal idx "2") len2 len1))) > + (when (integerp (cadr keys)) > + (push (cons (car keys) (cadr keys)) alist)) > + (when (> (cadr keys) len) > + (error "Wrong bounding indices '%s', %s > (length %s), %s" > + (substring (symbol-name (car keys)) 1) > + (cadr keys) > + (concat "cl-seq" idx) > + len)))) > + (setq keys (cddr keys))) > + ;; Check :start value > :end value. > + (mapc (lambda (x) > + (and-let* ((start (alist-get (car x) alist)) > + (end (alist-get (cdr x) alist))) > + (when (> start end) > + (error "Bad bounding indices '%s', '%s': %d, %d" > + (substring (symbol-name (car x)) 1) > + (substring (symbol-name (cdr x)) 1) > + start > + end)))) > + (list (cons :start :end) > + (cons :start1 :end1) > + (cons :start2 :end2))))) > + > ;;;###autoload > (defun cl-reduce (cl-func cl-seq &rest cl-keys) > "Reduce two-argument FUNCTION across SEQ. > @@ -134,6 +173,7 @@ cl-reduce > > \n(fn FUNCTION SEQ [KEYWORD VALUE]...)" > (cl--parsing-keywords (:from-end (:start 0) :end :initial-value :key) () > + (cl--check-bound-indices cl-seq nil cl-keys) > (or (listp cl-seq) (setq cl-seq (append cl-seq nil))) > (setq cl-seq (cl-subseq cl-seq cl-start cl-end)) > (if cl-from-end (setq cl-seq (nreverse cl-seq))) > @@ -155,6 +195,7 @@ cl-fill > \nKeywords supported: :start :end > \n(fn SEQ ITEM [KEYWORD VALUE]...)" > (cl--parsing-keywords ((:start 0) :end) () > + (cl--check-bound-indices cl-seq nil cl-keys) > (if (listp cl-seq) > (let ((p (nthcdr cl-start cl-seq)) > (n (and cl-end (- cl-end cl-start)))) > @@ -176,6 +217,7 @@ cl-replace > \nKeywords supported: :start1 :end1 :start2 :end2 > \n(fn SEQ1 SEQ2 [KEYWORD VALUE]...)" > (cl--parsing-keywords ((:start1 0) :end1 (:start2 0) :end2) () > + (cl--check-bound-indices cl-seq1 cl-seq2 cl-keys) > (if (and (eq cl-seq1 cl-seq2) (<= cl-start2 cl-start1)) > (or (= cl-start1 cl-start2) > (let* ((cl-len (length cl-seq1)) > @@ -225,6 +267,7 @@ cl-remove > \n(fn ITEM SEQ [KEYWORD VALUE]...)" > (cl--parsing-keywords (:test :test-not :key :if :if-not :count :from-end > (:start 0) :end) () > + (cl--check-bound-indices cl-seq nil cl-keys) > (let ((len (length cl-seq))) > (if (<= (or cl-count (setq cl-count len)) 0) > cl-seq > @@ -289,6 +332,7 @@ cl-delete > \n(fn ITEM SEQ [KEYWORD VALUE]...)" > (cl--parsing-keywords (:test :test-not :key :if :if-not :count :from-end > (:start 0) :end) () > + (cl--check-bound-indices cl-seq nil cl-keys) > (let ((len (length cl-seq))) > (if (<= (or cl-count (setq cl-count len)) 0) > cl-seq > @@ -362,6 +406,7 @@ cl--delete-duplicates > ;; We need to parse :if, otherwise `cl-if' is unbound. > (:test :test-not :key (:start 0) :end :from-end :if) > () > + (cl--check-bound-indices cl-seq nil cl-keys) > (if cl-from-end > (let ((cl-p (nthcdr cl-start cl-seq)) cl-i) > (setq cl-end (- (or cl-end (length cl-seq)) cl-start)) > @@ -407,6 +452,7 @@ cl-substitute > \n(fn NEW OLD SEQ [KEYWORD VALUE]...)" > (cl--parsing-keywords (:test :test-not :key :if :if-not :count > (:start 0) :end :from-end) () > + (cl--check-bound-indices cl-seq nil cl-keys) > (if (or (eq cl-old cl-new) > (<= (or cl-count (setq cl-from-end nil > cl-count (length cl-seq))) 0)) > @@ -448,6 +494,7 @@ cl-nsubstitute > \n(fn NEW OLD SEQ [KEYWORD VALUE]...)" > (cl--parsing-keywords (:test :test-not :key :if :if-not :count > (:start 0) :end :from-end) () > + (cl--check-bound-indices cl-seq nil cl-keys) > (let ((len (length cl-seq))) > (or (eq cl-old cl-new) (<= (or cl-count (setq cl-count len)) 0) > (if (and (listp cl-seq) (or (not cl-from-end) (> cl-count (/ len > 2)))) > @@ -524,6 +571,7 @@ cl-position > \n(fn ITEM SEQ [KEYWORD VALUE]...)" > (cl--parsing-keywords (:test :test-not :key :if :if-not > (:start 0) :end :from-end) () > + (cl--check-bound-indices cl-seq nil cl-keys) > (cl--position cl-item cl-seq cl-start cl-end cl-from-end))) > > (defun cl--position (cl-item cl-seq cl-start &optional cl-end cl-from-end) > @@ -568,6 +616,7 @@ cl-count > \nKeywords supported: :test :test-not :key :start :end > \n(fn ITEM SEQ [KEYWORD VALUE]...)" > (cl--parsing-keywords (:test :test-not :key :if :if-not (:start 0) > :end) () > + (cl--check-bound-indices cl-seq nil cl-keys) > (let ((cl-count 0) cl-x) > (or cl-end (setq cl-end (length cl-seq))) > (if (consp cl-seq) (setq cl-seq (nthcdr cl-start cl-seq))) > @@ -600,6 +649,7 @@ cl-mismatch > \n(fn SEQ1 SEQ2 [KEYWORD VALUE]...)" > (cl--parsing-keywords (:test :test-not :key :from-end > (:start1 0) :end1 (:start2 0) :end2) () > + (cl--check-bound-indices cl-seq1 cl-seq2 cl-keys) > (or cl-end1 (setq cl-end1 (length cl-seq1))) > (or cl-end2 (setq cl-end2 (length cl-seq2))) > (if cl-from-end > @@ -631,6 +681,7 @@ cl-search > \n(fn SEQ1 SEQ2 [KEYWORD VALUE]...)" > (cl--parsing-keywords (:test :test-not :key :from-end > (:start1 0) :end1 (:start2 0) :end2) () > + (cl--check-bound-indices cl-seq1 cl-seq2 cl-keys) > (or cl-end1 (setq cl-end1 (length cl-seq1))) > (or cl-end2 (setq cl-end2 (length cl-seq2))) > (if (>= cl-start1 cl-end1) > -- > 2.11.0 > > From 50cce9d03fa96d622d20f2218ec5ddcfeb246425 Mon Sep 17 00:00:00 2001 > From: Tino Calancha > Date: Sun, 5 Feb 2017 01:06:01 +0900 > Subject: [PATCH 3/3] * test/lisp/emacs-lisp/cl-seq-tests.el > (cl-seq-check-bounds): New test. > > --- > test/lisp/emacs-lisp/cl-seq-tests.el | 91 > ++++++++++++++++++++++++++++++++++++ > 1 file changed, 91 insertions(+) > > diff --git a/test/lisp/emacs-lisp/cl-seq-tests.el > b/test/lisp/emacs-lisp/cl-seq-tests.el > index 61e3d72033..f5ecfe4b92 100644 > --- a/test/lisp/emacs-lisp/cl-seq-tests.el > +++ b/test/lisp/emacs-lisp/cl-seq-tests.el > @@ -294,6 +294,7 @@ cl-seq--with-side-effects > > (ert-deftest cl-seq-test-bug24264 () > "Test for http://debbugs.gnu.org/24264 ." > + :tags '(:expensive-test) > (let ((list (append (make-list 8000005 1) '(8))) > (list2 (make-list 8000005 2))) > (should (cl-position 8 list)) > @@ -302,6 +303,96 @@ cl-seq--with-side-effects > (should (equal '(2 8) (last (cl-replace list list2) 2))) > (should (equal '(1 1) (last (cl-fill list 1) 2))))) > > +(ert-deftest cl-seq-check-bounds () > + (let ((lst (list 1 2 3)) > + (lst2 (list 'a 'b 'c)) > + ;; t means pass, nil means fails. > + (tests '("((lambda (x y) (cl-reduce #'max x :start 1)) . t)" > + "((lambda (x y) (cl-reduce #'max x :start -1)))" > + "((lambda (x y) (cl-reduce #'max x :start 4)))" > + "((lambda (x y) (cl-reduce #'max x :start 2 :end 1)))" > + ;; > + "((lambda (x y) (cl-fill x 'b :start 3)) . t)" > + "((lambda (x y) (cl-fill x 'b :start 4)))" > + "((lambda (x y) (cl-fill x 'b :start -1)))" > + "((lambda (x y) (cl-fill x 'b :start 2 :end 1)))" > + ;; > + "((lambda (x y) (cl-replace x y :start1 3)) . t)" > + "((lambda (x y) (cl-replace x y :start2 3)) . t)" > + "((lambda (x y) (cl-replace x y :start1 4)))" > + "((lambda (x y) (cl-replace x y :start2 4)))" > + "((lambda (x y) (cl-replace x y :start1 -1)))" > + "((lambda (x y) (cl-replace x y :start2 -1)))" > + "((lambda (x y) (cl-replace x y :start1 2 :end1 1)))" > + "((lambda (x y) (cl-replace x y :start2 2 :end2 1)))" > + ;; > + "((lambda (x y) (cl-remove nil x :start 3)) . t)" > + "((lambda (x y) (cl-remove nil x :start 4)))" > + "((lambda (x y) (cl-remove nil x :start -1)))" > + "((lambda (x y) (cl-remove nil x :start 2 :end 1)))" > + ;; > + "((lambda (x y) (cl-delete nil x :start 3)) . t)" > + "((lambda (x y) (cl-delete nil x :start 4)))" > + "((lambda (x y) (cl-delete nil x :start -1)))" > + "((lambda (x y) (cl-delete nil x :start 2 :end 1)))" > + ;; > + "((lambda (x y) (cl-remove-duplicates x :start 3)) . t)" > + "((lambda (x y) (cl-remove-duplicates x :start 4)))" > + "((lambda (x y) (cl-remove-duplicates x :start -1)))" > + "((lambda (x y) (cl-remove-duplicates x :start 2 :end > 1)))" > + ;; > + "((lambda (x y) (cl-substitute 'foo 2 x :start 3)) . t)" > + "((lambda (x y) (cl-substitute 'foo 2 x :start 4)))" > + "((lambda (x y) (cl-substitute 'foo 2 x :start -1)))" > + "((lambda (x y) (cl-substitute 'foo 2 x :start 2 :end > 1)))" > + ;; > + "((lambda (x y) (cl-nsubstitute 'foo 2 x :start 3)) . t)" > + "((lambda (x y) (cl-nsubstitute 'foo 2 x :start 4)))" > + "((lambda (x y) (cl-nsubstitute 'foo 2 x :start -1)))" > + "((lambda (x y) (cl-nsubstitute 'foo 2 x :start 2 :end > 1)))" > + ;; > + "((lambda (x y) (cl-position 2 x :start 4)))" > + "((lambda (x y) (cl-position 2 x :start -1)))" > + "((lambda (x y) (cl-position 2 x :start 2 :end 1)))" > + ;; > + "((lambda (x y) (cl-count 2 x :start 3)) . t)" > + "((lambda (x y) (cl-count 2 x :start 4)))" > + "((lambda (x y) (cl-count 2 x :start -1)))" > + "((lambda (x y) (cl-count 2 x :start 2 :end 1)))" > + ;; > + "((lambda (x y) (cl-mismatch x x :start1 1 :start2 3)) . > t)" > + "((lambda (x y) (cl-mismatch x x :start1 1 :start2 4)))" > + "((lambda (x y) (cl-mismatch x x :start1 4 :start2 1)))" > + "((lambda (x y) (cl-mismatch x x :start1 -1 :start2 1)))" > + "((lambda (x y) (cl-mismatch x x :start1 1 :start2 -1)))" > + "((lambda (x y) (cl-mismatch x x :start1 2 :end1 1)))" > + "((lambda (x y) (cl-mismatch x x :start2 2 :end2 1)))" > + ;; > + "((lambda (x y) (cl-search x x :start1 3 :start2 3)) . > t)" > + "((lambda (x y) (cl-search x x :start1 4 :start2 4)))" > + "((lambda (x y) (cl-search x x :start1 -1 :start2 3)))" > + "((lambda (x y) (cl-search x x :start1 1 :start2 -1)))" > + "((lambda (x y) (cl-search x x :start1 2 :end1 1)))" > + "((lambda (x y) (cl-search x x :start2 2 :end2 1)))" > + ;; > + "((lambda (x y) (cl-subseq x -1)) . t)" > + "((lambda (x y) (cl-subseq x -2 -1)) . t)" > + "((lambda (x y) (cl-subseq x -4)))" > + "((lambda (x y) (cl-subseq x 2 1)))"))) > + (dolist (limit '("start" "end")) > + (dolist (x tests) > + (let ((form > + (car > + (read-from-string > + (cond ((string-match ":start\\([1-2]?\\) \\([0-9-]+\\) > :end\\([1-2]?\\)" x) > + x) > + ((string= limit "start") x) > + (t > + (replace-regexp-in-string "start" limit x))))))) > + (if (cdr form) > + (should (funcall (car form) lst lst2)) > + (should-error (funcall (car form) lst lst2)))))))) > + > > (provide 'cl-seq-tests) > ;;; cl-seq-tests.el ends here > -- > 2.11.0 > > > ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; > In GNU Emacs 26.0.50.1 (x86_64-pc-linux-gnu, GTK+ Version 3.22.7) > of 2017-02-04 > Repository revision: ff4dd0d39c3f5dfb8f4988f840c2c05621db32db > > --001a113db4b25894e30547ba6fd3 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable


Tino C= alancha <tino.calancha@gmail.= com> schrieb am Sa., 4. Feb. 2017 um 17:41=C2=A0Uhr:

Consider following evaluations:

(cl-position 2 (list 1 2 3) :start -999)
=3D> -998
(cl-position 2 (list 1 2 3) :start 999)
=3D> nil
(cl-position 2 (list 1 2 3) :start 1 :end 0)
=3D> nil

*) In the first two, the indices are out of range.
*) In the 3rd, the start index is higher than the end index.

Guy L. Steele recommends to throw an error in those cases:
http://www.l= ispworks.com/documentation/HyperSpec/Issues/iss332_w.htm

We have partially done the job, at least for `cl-subseq':
(cl-subseq (list 1 2 3) 999)
; Signal: "Bad bounding indices: 999, nil"

(cl-subseq (list 1 2 3) -999)
; Signal: "Bad bounding indices: -999, nil"

(cl-subseq (list 1 2 3) -1)
=3D> (3) ; This is OK as documented in the docstring.

See:
https://list= s.gnu.org/archive/html/emacs-devel/2015-08/msg00182.html

It would be nice if all the sequence functions in cl-seq.el
throw errors in those situations.

What do you think?

= I agree that these should throw errors.
=C2=A0
The following patch implements such bounding checks for the indices.
Comments are very welcome.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;= ;;;;
>From c076a16716d6b9ad5c971d67375223974df53a40 Mon Sep 17 00:00:00 2001
From: Tino Calancha <tino.calancha@gmail.com>
Date: Sat, 4 Feb 2017 21:53:48 +0900
Subject: [PATCH 1/3] cl--parsing-keywords: Check for negative indices

* lisp/emacs-lisp/cl-seq.el (cl--parsing-keywords):
Throw and error when the user inputs a negative index for the sequence.
---
=C2=A0lisp/emacs-lisp/cl-seq.el | 8 +++++++-
=C2=A01 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/lisp/emacs-lisp/cl-seq.el b/lisp/emacs-lisp/cl-seq.el
index 67ff1a00bd..558adf22fe 100644
--- a/lisp/emacs-lisp/cl-seq.el
+++ b/lisp/emacs-lisp/cl-seq.el
@@ -59,7 +59,13 @@ cl--parsing-keywords
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (setq= mem `(and ,mem (setq cl-if ,mem) t)))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (list (intern
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0(format "cl-%s" (substring (symbol-name var) 1)))
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0(if (consp x) `(or ,mem ,(car (cdr x))) mem))))
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0`(if (and (string-match ":\\(start\\|end\\)" (symbol-name ,var= ))

I think you can = replace the string-match with (memq ,var '(:start :end))
=C2= =A0
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(integerp (or ,mem ,(car (cdr-safe x)= )))
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(not (natnump (or ,mem ,(car (cdr-saf= e x))))))
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 (error "Wrong negative index '%s': natnump, = %s"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(substring (symbol-name ,var) = 1)

Please use (sign= al 'args-out-of-range ...) to be more specific and consistent with simi= lar checks.
=C2=A0
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(or ,mem ,(car (cdr-safe x))))=
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 (or ,mem ,(car (cdr-safe x)))))))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 kwords)
=C2=A0 =C2=A0 =C2=A0 ,@(append
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(and (not (eq other-keys t))
--
2.11.0

>From 9898718b96109c5815b81f8dc8868de4d86db453 Mon Sep 17 00:00:00 2001
From: Tino Calancha <tino.calancha@gmail.com>
Date: Sun, 5 Feb 2017 00:04:12 +0900
Subject: [PATCH 2/3] cl--check-bound-indices: Check for unbound indices

Throw and error if the user inputs an index higher than the sequence length=
or if :start value is higher than :end value.
* lisp/emacs-lisp/cl-seq.el (cl--check-bound-indices): New defun.
(cl-reduce, cl-fill, cl-replace, cl-remove, cl-delete)
(cl--delete-duplicates, cl-substitute, cl-nsubstitute, cl-position)
(cl-count, cl-mismatch, cl-search): Use it.
---
=C2=A0lisp/emacs-lisp/cl-seq.el | 51 ++++++++++++++++++++++++++++++++++++++= +++++++++
=C2=A01 file changed, 51 insertions(+)

diff --git a/lisp/emacs-lisp/cl-seq.el b/lisp/emacs-lisp/cl-seq.el
index 558adf22fe..0cb7ac7bdd 100644
--- a/lisp/emacs-lisp/cl-seq.el
+++ b/lisp/emacs-lisp/cl-seq.el
@@ -42,6 +42,7 @@
=C2=A0;;; Code:

=C2=A0(require 'cl-lib)
+(eval-when-compile (require 'subr-x))

=C2=A0;; Keyword parsing.
=C2=A0;; This is special-cased here so that we can compile
@@ -118,6 +119,44 @@ cl-test
=C2=A0(defvar cl-if) (defvar cl-if-not)
=C2=A0(defvar cl-key)

+;; Throw an error when :start or :end are > sequence length.
+(defmacro cl--check-bound-indices (cl-seq1 cl-seq2 cl-keys)
+=C2=A0 (declare (debug t))
+=C2=A0 `(let ((len1 (length ,cl-seq1))
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(len2 (and ,cl-seq2 (length ,cl-seq2)))<= br class=3D"gmail_msg"> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(kwds (list :start1 :start2 :start :end1= :end2 :end))
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(keys ,cl-keys)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0alist)
+=C2=A0 =C2=A0 =C2=A0(while keys
+=C2=A0 =C2=A0 =C2=A0 =C2=A0(when (and (memq (car keys) kwds)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (string-mat= ch ":\\(start\\|end\\)\\([1-2]?\\)\\'"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (symbol-name (car keys))))
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(delq (car keys) kwds) ; Ignore succesiv= e equal keys.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(let* ((idx (match-string 2 (symbol-name= (car keys))))
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (len (if (equal id= x "2") len2 len1)))
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(when (integerp (cadr keys))
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(push (cons (car keys) (ca= dr keys)) alist))
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(when (> (cadr keys) len)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(error "Wrong boundin= g indices '%s', %s > (length %s), %s"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (sub= string (symbol-name (car keys)) 1)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (cad= r keys)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (con= cat "cl-seq" idx)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 len)= )))
+=C2=A0 =C2=A0 =C2=A0 =C2=A0(setq keys (cddr keys)))
+=C2=A0 =C2=A0 =C2=A0;; Check :start value > :end value.
+=C2=A0 =C2=A0 =C2=A0(mapc (lambda (x)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(and-let* ((start (alist-g= et (car x) alist))
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 (end (alist-get (cdr x) alist)))
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(when (> start e= nd)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(error "= ;Bad bounding indices '%s', '%s': %d, %d"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 (substring (symbol-name (car x)) 1)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 (substring (symbol-name (cdr x)) 1)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 start
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 end))))
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(list (cons :start :end)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(cons :start= 1 :end1)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(cons :start= 2 :end2)))))
+
=C2=A0;;;###autoload
=C2=A0(defun cl-reduce (cl-func cl-seq &rest cl-keys)
=C2=A0 =C2=A0"Reduce two-argument FUNCTION across SEQ.
@@ -134,6 +173,7 @@ cl-reduce

=C2=A0\n(fn FUNCTION SEQ [KEYWORD VALUE]...)"
=C2=A0 =C2=A0(cl--parsing-keywords (:from-end (:start 0) :end :initial-valu= e :key) ()
+=C2=A0 =C2=A0 (cl--check-bound-indices cl-seq nil cl-keys)
=C2=A0 =C2=A0 =C2=A0(or (listp cl-seq) (setq cl-seq (append cl-seq nil))) =C2=A0 =C2=A0 =C2=A0(setq cl-seq (cl-subseq cl-seq cl-start cl-end))
=C2=A0 =C2=A0 =C2=A0(if cl-from-end (setq cl-seq (nreverse cl-seq)))
@@ -155,6 +195,7 @@ cl-fill
=C2=A0\nKeywords supported:=C2=A0 :start :end
=C2=A0\n(fn SEQ ITEM [KEYWORD VALUE]...)"
=C2=A0 =C2=A0(cl--parsing-keywords ((:start 0) :end) ()
+=C2=A0 =C2=A0 (cl--check-bound-indices cl-seq nil cl-keys)
=C2=A0 =C2=A0 =C2=A0(if (listp cl-seq)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 (let ((p (nthcdr cl-start cl-seq))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (n (and cl-end (- cl-end c= l-start))))
@@ -176,6 +217,7 @@ cl-replace
=C2=A0\nKeywords supported:=C2=A0 :start1 :end1 :start2 :end2
=C2=A0\n(fn SEQ1 SEQ2 [KEYWORD VALUE]...)"
=C2=A0 =C2=A0(cl--parsing-keywords ((:start1 0) :end1 (:start2 0) :end2) ()=
+=C2=A0 =C2=A0 (cl--check-bound-indices cl-seq1 cl-seq2 cl-keys)
=C2=A0 =C2=A0 =C2=A0(if (and (eq cl-seq1 cl-seq2) (<=3D cl-start2 cl-sta= rt1))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 (or (=3D cl-start1 cl-start2)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (let* ((cl-len (length cl-seq1))<= br class=3D"gmail_msg"> @@ -225,6 +267,7 @@ cl-remove
=C2=A0\n(fn ITEM SEQ [KEYWORD VALUE]...)"
=C2=A0 =C2=A0(cl--parsing-keywords (:test :test-not :key :if :if-not :count= :from-end
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 (:start 0) :end) ()
+=C2=A0 =C2=A0 (cl--check-bound-indices cl-seq nil cl-keys)
=C2=A0 =C2=A0 =C2=A0(let ((len (length cl-seq)))
=C2=A0 =C2=A0 =C2=A0 =C2=A0(if (<=3D (or cl-count (setq cl-count len)) 0= )
=C2=A0 =C2=A0 =C2=A0 =C2=A0 cl-seq
@@ -289,6 +332,7 @@ cl-delete
=C2=A0\n(fn ITEM SEQ [KEYWORD VALUE]...)"
=C2=A0 =C2=A0(cl--parsing-keywords (:test :test-not :key :if :if-not :count= :from-end
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 (:start 0) :end) ()
+=C2=A0 =C2=A0 (cl--check-bound-indices cl-seq nil cl-keys)
=C2=A0 =C2=A0 =C2=A0(let ((len (length cl-seq)))
=C2=A0 =C2=A0 =C2=A0 =C2=A0(if (<=3D (or cl-count (setq cl-count len)) 0= )
=C2=A0 =C2=A0 =C2=A0 =C2=A0 cl-seq
@@ -362,6 +406,7 @@ cl--delete-duplicates
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0;; We need to parse :if, otherwise= `cl-if' is unbound.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(:test :test-not :key (:start 0) := end :from-end :if)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ()
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 (cl--check-bound-indices cl-seq nil cl-keys) =C2=A0 =C2=A0 =C2=A0 =C2=A0 (if cl-from-end
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (let ((cl-p (nthcdr cl-start cl-s= eq)) cl-i)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (setq cl-end (- (or cl-end= (length cl-seq)) cl-start))
@@ -407,6 +452,7 @@ cl-substitute
=C2=A0\n(fn NEW OLD SEQ [KEYWORD VALUE]...)"
=C2=A0 =C2=A0(cl--parsing-keywords (:test :test-not :key :if :if-not :count=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 (:start 0) :end :from-end) ()
+=C2=A0 =C2=A0 (cl--check-bound-indices cl-seq nil cl-keys)
=C2=A0 =C2=A0 =C2=A0(if (or (eq cl-old cl-new)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (<=3D (or cl-count (setq cl-fr= om-end nil
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0cl-count (length cl-seq= ))) 0))
@@ -448,6 +494,7 @@ cl-nsubstitute
=C2=A0\n(fn NEW OLD SEQ [KEYWORD VALUE]...)"
=C2=A0 =C2=A0(cl--parsing-keywords (:test :test-not :key :if :if-not :count=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 (:start 0) :end :from-end) ()
+=C2=A0 =C2=A0 (cl--check-bound-indices cl-seq nil cl-keys)
=C2=A0 =C2=A0 =C2=A0(let ((len (length cl-seq)))
=C2=A0 =C2=A0 =C2=A0 =C2=A0(or (eq cl-old cl-new) (<=3D (or cl-count (se= tq cl-count len)) 0)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (if (and (listp cl-seq) (or (not cl-from= -end) (> cl-count (/ len 2))))
@@ -524,6 +571,7 @@ cl-position
=C2=A0\n(fn ITEM SEQ [KEYWORD VALUE]...)"
=C2=A0 =C2=A0(cl--parsing-keywords (:test :test-not :key :if :if-not
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 (:start 0) :end :from-end) ()
+=C2=A0 =C2=A0 (cl--check-bound-indices cl-seq nil cl-keys)
=C2=A0 =C2=A0 =C2=A0(cl--position cl-item cl-seq cl-start cl-end cl-from-en= d)))

=C2=A0(defun cl--position (cl-item cl-seq cl-start &optional cl-end cl-= from-end)
@@ -568,6 +616,7 @@ cl-count
=C2=A0\nKeywords supported:=C2=A0 :test :test-not :key :start :end
=C2=A0\n(fn ITEM SEQ [KEYWORD VALUE]...)"
=C2=A0 =C2=A0(cl--parsing-keywords (:test :test-not :key :if :if-not (:star= t 0) :end) ()
+=C2=A0 =C2=A0 (cl--check-bound-indices cl-seq nil cl-keys)
=C2=A0 =C2=A0 =C2=A0(let ((cl-count 0) cl-x)
=C2=A0 =C2=A0 =C2=A0 =C2=A0(or cl-end (setq cl-end (length cl-seq)))
=C2=A0 =C2=A0 =C2=A0 =C2=A0(if (consp cl-seq) (setq cl-seq (nthcdr cl-start= cl-seq)))
@@ -600,6 +649,7 @@ cl-mismatch
=C2=A0\n(fn SEQ1 SEQ2 [KEYWORD VALUE]...)"
=C2=A0 =C2=A0(cl--parsing-keywords (:test :test-not :key :from-end
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 (:start1 0) :end1 (:start2 0) :end2) ()
+=C2=A0 =C2=A0 (cl--check-bound-indices cl-seq1 cl-seq2 cl-keys)
=C2=A0 =C2=A0 =C2=A0(or cl-end1 (setq cl-end1 (length cl-seq1)))
=C2=A0 =C2=A0 =C2=A0(or cl-end2 (setq cl-end2 (length cl-seq2)))
=C2=A0 =C2=A0 =C2=A0(if cl-from-end
@@ -631,6 +681,7 @@ cl-search
=C2=A0\n(fn SEQ1 SEQ2 [KEYWORD VALUE]...)"
=C2=A0 =C2=A0(cl--parsing-keywords (:test :test-not :key :from-end
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 (:start1 0) :end1 (:start2 0) :end2) ()
+=C2=A0 =C2=A0 (cl--check-bound-indices cl-seq1 cl-seq2 cl-keys)
=C2=A0 =C2=A0 =C2=A0(or cl-end1 (setq cl-end1 (length cl-seq1)))
=C2=A0 =C2=A0 =C2=A0(or cl-end2 (setq cl-end2 (length cl-seq2)))
=C2=A0 =C2=A0 =C2=A0(if (>=3D cl-start1 cl-end1)
--
2.11.0

>From 50cce9d03fa96d622d20f2218ec5ddcfeb246425 Mon Sep 17 00:00:00 2001
From: Tino Calancha <tino.calancha@gmail.com>
Date: Sun, 5 Feb 2017 01:06:01 +0900
Subject: [PATCH 3/3] * test/lisp/emacs-lisp/cl-seq-tests.el
=C2=A0(cl-seq-check-bounds): New test.

---
=C2=A0test/lisp/emacs-lisp/cl-seq-tests.el | 91 +++++++++++++++++++++++++++= +++++++++
=C2=A01 file changed, 91 insertions(+)

diff --git a/test/lisp/emacs-lisp/cl-seq-tests.el b/test/lisp/emacs-lisp/cl= -seq-tests.el
index 61e3d72033..f5ecfe4b92 100644
--- a/test/lisp/emacs-lisp/cl-seq-tests.el
+++ b/test/lisp/emacs-lisp/cl-seq-tests.el
@@ -294,6 +294,7 @@ cl-seq--with-side-effects

=C2=A0(ert-deftest cl-seq-test-bug24264 ()
=C2=A0 =C2=A0"Test for http://debbugs.gnu.org/2= 4264 ."
+=C2=A0 :tags '(:expensive-test)
=C2=A0 =C2=A0(let ((list=C2=A0 (append (make-list 8000005 1) '(8)))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(list2 (make-list 8000005 2)))
=C2=A0 =C2=A0 =C2=A0(should (cl-position 8 list))
@@ -302,6 +303,96 @@ cl-seq--with-side-effects
=C2=A0 =C2=A0 =C2=A0(should (equal '(2 8) (last (cl-replace list list2)= 2)))
=C2=A0 =C2=A0 =C2=A0(should (equal '(1 1) (last (cl-fill list 1) 2)))))=

+(ert-deftest cl-seq-check-bounds ()
+=C2=A0 (let ((lst (list 1 2 3))
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 (lst2 (list 'a 'b 'c))
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 ;; t means pass, nil means fails.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 (tests '("((lambda (x y) (cl-reduce #= 'max x :start 1)) . t)"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-reduce #'max x :start -1)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-reduce #'max x :start 4)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-reduce #'max x :start 2 :end 1)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0;;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-fill x 'b :start 3)) . t)"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-fill x 'b :start 4)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-fill x 'b :start -1)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-fill x 'b :start 2 :end 1)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0;;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-replace x y :start1 3)) . t)"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-replace x y :start2 3)) . t)"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-replace x y :start1 4)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-replace x y :start2 4)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-replace x y :start1 -1)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-replace x y :start2 -1)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-replace x y :start1 2 :end1 1)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-replace x y :start2 2 :end2 1)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0;;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-remove nil x :start 3)) . t)"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-remove nil x :start 4)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-remove nil x :start -1)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-remove nil x :start 2 :end 1)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0;;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-delete nil x :start 3)) . t)"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-delete nil x :start 4)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-delete nil x :start -1)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-delete nil x :start 2 :end 1)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0;;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-remove-duplicates x :start 3)) . t)"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-remove-duplicates x :start 4)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-remove-duplicates x :start -1)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-remove-duplicates x :start 2 :end 1)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0;;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-substitute 'foo 2 x :start 3)) . t)"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-substitute 'foo 2 x :start 4)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-substitute 'foo 2 x :start -1)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-substitute 'foo 2 x :start 2 :end 1)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0;;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-nsubstitute 'foo 2 x :start 3)) . t)"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-nsubstitute 'foo 2 x :start 4)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-nsubstitute 'foo 2 x :start -1)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-nsubstitute 'foo 2 x :start 2 :end 1)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0;;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-position 2 x :start 4)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-position 2 x :start -1)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-position 2 x :start 2 :end 1)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0;;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-count 2 x :start 3)) . t)"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-count 2 x :start 4)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-count 2 x :start -1)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-count 2 x :start 2 :end 1)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0;;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-mismatch x x :start1 1 :start2 3)) . t)"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-mismatch x x :start1 1 :start2 4)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-mismatch x x :start1 4 :start2 1)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-mismatch x x :start1 -1 :start2 1)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-mismatch x x :start1 1 :start2 -1)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-mismatch x x :start1 2 :end1 1)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-mismatch x x :start2 2 :end2 1)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0;;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-search x x :start1 3 :start2 3)) . t)"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-search x x :start1 4 :start2 4)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-search x x :start1 -1 :start2 3)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-search x x :start1 1 :start2 -1)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-search x x :start1 2 :end1 1)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-search x x :start2 2 :end2 1)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0;;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-subseq x -1)) . t)"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-subseq x -2 -1)) . t)"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-subseq x -4)))"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"((lamb= da (x y) (cl-subseq x 2 1)))")))
+=C2=A0 =C2=A0 (dolist (limit '("start" "end"))
+=C2=A0 =C2=A0 =C2=A0 (dolist (x tests)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 (let ((form
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(car
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (read-from-string<= br class=3D"gmail_msg"> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(cond ((stri= ng-match ":start\\([1-2]?\\) \\([0-9-]+\\) :end\\([1-2]?\\)" x) +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 x)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0((string=3D limit "start") x)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0(t
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 (replace-regexp-in-string "start" limit x)))))))
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (if (cdr form)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (should (funcall (car for= m) lst lst2))
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (should-error (funcall (car form= ) lst lst2))))))))
+

=C2=A0(provide 'cl-seq-tests)
=C2=A0;;; cl-seq-tests.el ends here
--
2.11.0

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;= ;;;;
In GNU Emacs 26.0.50.1 (x86_64-pc-linux-gnu, GTK+ Version 3.22.7)
=C2=A0of 2017-02-04
Repository revision: ff4dd0d39c3f5dfb8f4988f840c2c05621db32db

--001a113db4b25894e30547ba6fd3--