aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNoam Postavsky2016-12-18 00:00:30 -0500
committerNoam Postavsky2017-01-23 19:28:30 -0500
commit201dfe311868932d10da146808fcdd681948ba53 (patch)
tree17b0d5bd792909b88fa30de7282c5dd947d04e68
parent0c31ff43b6880c84498fbe1f06e1e5809b55e838 (diff)
downloademacs-201dfe311868932d10da146808fcdd681948ba53.tar.gz
emacs-201dfe311868932d10da146808fcdd681948ba53.zip
Fix comment detection on open parens
Characters having both open paren syntax and comment start syntax were being detected as open parens even when they should have been part a comment starter (Bug#24870). * src/syntax.c (in_2char_comment_start): New function, extracted from `scan_sexps_forward'. (scan_sexps_forward): Add check for a 2-char comment starter before the loop. Inside the loop, do that check after incrementing the 'from' character index. Move the single char comment syntax cases into the switch instead of special casing them before. * test/src/syntax-tests.el (parse-partial-sexp-paren-comments): (parse-partial-sexp-continue-over-comment-marker): New tests.
-rw-r--r--src/syntax.c117
-rw-r--r--test/src/syntax-tests.el85
2 files changed, 147 insertions, 55 deletions
diff --git a/src/syntax.c b/src/syntax.c
index 84147a2dc15..0ee1c746ec3 100644
--- a/src/syntax.c
+++ b/src/syntax.c
@@ -3092,6 +3092,36 @@ the prefix syntax flag (p). */)
3092 return Qnil; 3092 return Qnil;
3093} 3093}
3094 3094
3095
3096/* If the character at FROM_BYTE is the second part of a 2-character
3097 comment opener based on PREV_FROM_SYNTAX, update STATE and return
3098 true. */
3099static bool
3100in_2char_comment_start (struct lisp_parse_state *state,
3101 int prev_from_syntax,
3102 ptrdiff_t prev_from,
3103 ptrdiff_t from_byte)
3104{
3105 int c1, syntax;
3106 if (SYNTAX_FLAGS_COMSTART_FIRST (prev_from_syntax)
3107 && (c1 = FETCH_CHAR_AS_MULTIBYTE (from_byte),
3108 syntax = SYNTAX_WITH_FLAGS (c1),
3109 SYNTAX_FLAGS_COMSTART_SECOND (syntax)))
3110 {
3111 /* Record the comment style we have entered so that only
3112 the comment-end sequence of the same style actually
3113 terminates the comment section. */
3114 state->comstyle
3115 = SYNTAX_FLAGS_COMMENT_STYLE (syntax, prev_from_syntax);
3116 bool comnested = (SYNTAX_FLAGS_COMMENT_NESTED (prev_from_syntax)
3117 | SYNTAX_FLAGS_COMMENT_NESTED (syntax));
3118 state->incomment = comnested ? 1 : -1;
3119 state->comstr_start = prev_from;
3120 return true;
3121 }
3122 return false;
3123}
3124
3095/* Parse forward from FROM / FROM_BYTE to END, 3125/* Parse forward from FROM / FROM_BYTE to END,
3096 assuming that FROM has state STATE, 3126 assuming that FROM has state STATE,
3097 and return a description of the state of the parse at END. 3127 and return a description of the state of the parse at END.
@@ -3107,8 +3137,6 @@ scan_sexps_forward (struct lisp_parse_state *state,
3107 int commentstop) 3137 int commentstop)
3108{ 3138{
3109 enum syntaxcode code; 3139 enum syntaxcode code;
3110 int c1;
3111 bool comnested;
3112 struct level { ptrdiff_t last, prev; }; 3140 struct level { ptrdiff_t last, prev; };
3113 struct level levelstart[100]; 3141 struct level levelstart[100];
3114 struct level *curlevel = levelstart; 3142 struct level *curlevel = levelstart;
@@ -3122,7 +3150,6 @@ scan_sexps_forward (struct lisp_parse_state *state,
3122 ptrdiff_t prev_from; /* Keep one character before FROM. */ 3150 ptrdiff_t prev_from; /* Keep one character before FROM. */
3123 ptrdiff_t prev_from_byte; 3151 ptrdiff_t prev_from_byte;
3124 int prev_from_syntax, prev_prev_from_syntax; 3152 int prev_from_syntax, prev_prev_from_syntax;
3125 int syntax;
3126 bool boundary_stop = commentstop == -1; 3153 bool boundary_stop = commentstop == -1;
3127 bool nofence; 3154 bool nofence;
3128 bool found; 3155 bool found;
@@ -3187,53 +3214,31 @@ do { prev_from = from; \
3187 } 3214 }
3188 else if (start_quoted) 3215 else if (start_quoted)
3189 goto startquoted; 3216 goto startquoted;
3217 else if ((from < end)
3218 && (in_2char_comment_start (state, prev_from_syntax,
3219 prev_from, from_byte)))
3220 {
3221 INC_FROM;
3222 prev_from_syntax = Smax; /* the syntax has already been "used up". */
3223 goto atcomment;
3224 }
3190 3225
3191 while (from < end) 3226 while (from < end)
3192 { 3227 {
3193 if (SYNTAX_FLAGS_COMSTART_FIRST (prev_from_syntax) 3228 INC_FROM;
3194 && (c1 = FETCH_CHAR (from_byte), 3229
3195 syntax = SYNTAX_WITH_FLAGS (c1), 3230 if ((from < end)
3196 SYNTAX_FLAGS_COMSTART_SECOND (syntax))) 3231 && (in_2char_comment_start (state, prev_from_syntax,
3197 { 3232 prev_from, from_byte)))
3198 /* Record the comment style we have entered so that only
3199 the comment-end sequence of the same style actually
3200 terminates the comment section. */
3201 state->comstyle
3202 = SYNTAX_FLAGS_COMMENT_STYLE (syntax, prev_from_syntax);
3203 comnested = (SYNTAX_FLAGS_COMMENT_NESTED (prev_from_syntax)
3204 | SYNTAX_FLAGS_COMMENT_NESTED (syntax));
3205 state->incomment = comnested ? 1 : -1;
3206 state->comstr_start = prev_from;
3207 INC_FROM;
3208 prev_from_syntax = Smax; /* the syntax has already been
3209 "used up". */
3210 code = Scomment;
3211 }
3212 else
3213 { 3233 {
3214 INC_FROM; 3234 INC_FROM;
3215 code = prev_from_syntax & 0xff; 3235 prev_from_syntax = Smax; /* the syntax has already been "used up". */
3216 if (code == Scomment_fence) 3236 goto atcomment;
3217 {
3218 /* Record the comment style we have entered so that only
3219 the comment-end sequence of the same style actually
3220 terminates the comment section. */
3221 state->comstyle = ST_COMMENT_STYLE;
3222 state->incomment = -1;
3223 state->comstr_start = prev_from;
3224 code = Scomment;
3225 }
3226 else if (code == Scomment)
3227 {
3228 state->comstyle = SYNTAX_FLAGS_COMMENT_STYLE (prev_from_syntax, 0);
3229 state->incomment = (SYNTAX_FLAGS_COMMENT_NESTED (prev_from_syntax) ?
3230 1 : -1);
3231 state->comstr_start = prev_from;
3232 }
3233 } 3237 }
3234 3238
3235 if (SYNTAX_FLAGS_PREFIX (prev_from_syntax)) 3239 if (SYNTAX_FLAGS_PREFIX (prev_from_syntax))
3236 continue; 3240 continue;
3241 code = prev_from_syntax & 0xff;
3237 switch (code) 3242 switch (code)
3238 { 3243 {
3239 case Sescape: 3244 case Sescape:
@@ -3252,24 +3257,15 @@ do { prev_from = from; \
3252 symstarted: 3257 symstarted:
3253 while (from < end) 3258 while (from < end)
3254 { 3259 {
3255 int symchar = FETCH_CHAR_AS_MULTIBYTE (from_byte); 3260 if (in_2char_comment_start (state, prev_from_syntax,
3256 3261 prev_from, from_byte))
3257 if (SYNTAX_FLAGS_COMSTART_FIRST (prev_from_syntax)
3258 && (syntax = SYNTAX_WITH_FLAGS (symchar),
3259 SYNTAX_FLAGS_COMSTART_SECOND (syntax)))
3260 { 3262 {
3261 state->comstyle
3262 = SYNTAX_FLAGS_COMMENT_STYLE (syntax, prev_from_syntax);
3263 comnested = (SYNTAX_FLAGS_COMMENT_NESTED (prev_from_syntax)
3264 | SYNTAX_FLAGS_COMMENT_NESTED (syntax));
3265 state->incomment = comnested ? 1 : -1;
3266 state->comstr_start = prev_from;
3267 INC_FROM; 3263 INC_FROM;
3268 prev_from_syntax = Smax; 3264 prev_from_syntax = Smax; /* the syntax has already been "used up". */
3269 code = Scomment;
3270 goto atcomment; 3265 goto atcomment;
3271 } 3266 }
3272 3267
3268 int symchar = FETCH_CHAR_AS_MULTIBYTE (from_byte);
3273 switch (SYNTAX (symchar)) 3269 switch (SYNTAX (symchar))
3274 { 3270 {
3275 case Scharquote: 3271 case Scharquote:
@@ -3290,8 +3286,19 @@ do { prev_from = from; \
3290 curlevel->prev = curlevel->last; 3286 curlevel->prev = curlevel->last;
3291 break; 3287 break;
3292 3288
3293 case Scomment_fence: /* Can't happen because it's handled above. */ 3289 case Scomment_fence:
3290 /* Record the comment style we have entered so that only
3291 the comment-end sequence of the same style actually
3292 terminates the comment section. */
3293 state->comstyle = ST_COMMENT_STYLE;
3294 state->incomment = -1;
3295 state->comstr_start = prev_from;
3296 goto atcomment;
3294 case Scomment: 3297 case Scomment:
3298 state->comstyle = SYNTAX_FLAGS_COMMENT_STYLE (prev_from_syntax, 0);
3299 state->incomment = (SYNTAX_FLAGS_COMMENT_NESTED (prev_from_syntax) ?
3300 1 : -1);
3301 state->comstr_start = prev_from;
3295 atcomment: 3302 atcomment:
3296 if (commentstop || boundary_stop) goto done; 3303 if (commentstop || boundary_stop) goto done;
3297 startincomment: 3304 startincomment:
diff --git a/test/src/syntax-tests.el b/test/src/syntax-tests.el
new file mode 100644
index 00000000000..6edde0b137b
--- /dev/null
+++ b/test/src/syntax-tests.el
@@ -0,0 +1,85 @@
1;;; syntax-tests.el --- tests for syntax.c functions -*- lexical-binding: t -*-
2
3;; Copyright (C) 2017 Free Software Foundation, Inc.
4
5;; This file is part of GNU Emacs.
6
7;; GNU Emacs is free software: you can redistribute it and/or modify
8;; it under the terms of the GNU General Public License as published by
9;; the Free Software Foundation, either version 3 of the License, or
10;; (at your option) any later version.
11
12;; GNU Emacs is distributed in the hope that it will be useful,
13;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15;; GNU General Public License for more details.
16
17;; You should have received a copy of the GNU General Public License
18;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
19
20;;; Code:
21
22(require 'ert)
23
24(ert-deftest parse-partial-sexp-continue-over-comment-marker ()
25 "Continue a parse that stopped in the middle of a comment marker."
26 (with-temp-buffer
27 (let ((table (make-syntax-table)))
28 (modify-syntax-entry ?/ ". 124")
29 (modify-syntax-entry ?* ". 23b")
30 (set-syntax-table table))
31 (insert "/*C*/\nX")
32 (goto-char (point-min))
33 (let* ((pointC (progn (search-forward "C") (1- (point))))
34 (preC (1- pointC))
35 (pointX (progn (search-forward "X") (1- (point))))
36 (aftC (+ 2 pointC))
37 (ppsC (parse-partial-sexp (point-min) pointC))
38 (pps-preC (parse-partial-sexp (point-min) preC))
39 (pps-aftC (parse-partial-sexp (point-min) aftC))
40 (ppsX (parse-partial-sexp (point-min) pointX)))
41 ;; C should be inside comment.
42 (should (= (nth 0 ppsC) 0))
43 (should (eq (nth 4 ppsC) t))
44 (should (= (nth 8 ppsC) (- pointC 2)))
45 ;; X should not be in comment or list.
46 (should (= (nth 0 ppsX) 0))
47 (should-not (nth 4 ppsX))
48 ;; Try using OLDSTATE.
49 (should (equal (parse-partial-sexp preC pointC nil nil pps-preC)
50 ppsC))
51 (should (equal (parse-partial-sexp pointC aftC nil nil ppsC)
52 pps-aftC))
53 (should (equal (parse-partial-sexp preC aftC nil nil pps-preC)
54 pps-aftC))
55 (should (equal (parse-partial-sexp aftC pointX nil nil pps-aftC)
56 ppsX)))))
57
58(ert-deftest parse-partial-sexp-paren-comments ()
59 "Test syntax parsing with paren comment markers.
60Specifically, where the first character of the comment marker is
61also has open paren syntax (see Bug#24870)."
62 (with-temp-buffer
63 (let ((table (make-syntax-table)))
64 (modify-syntax-entry ?\{ "(}1nb" table)
65 (modify-syntax-entry ?\} "){4nb" table)
66 (modify-syntax-entry ?- ". 123" table)
67 (set-syntax-table table))
68 (insert "{-C-}\nX")
69 (goto-char (point-min))
70 (let* ((pointC (progn (search-forward "C") (1- (point))))
71 (pointX (progn (search-forward "X") (1- (point))))
72 (ppsC (parse-partial-sexp (point-min) pointC))
73 (ppsX (parse-partial-sexp (point-min) pointX)))
74 ;; C should be inside nestable comment, not list.
75 (should (= (nth 0 ppsC) 0))
76 (should (= (nth 4 ppsC) 1))
77 (should (= (nth 8 ppsC) (- pointC 2)))
78 ;; X should not be in comment or list.
79 (should (= (nth 0 ppsX) 0))
80 (should-not (nth 4 ppsX))
81 ;; Try using OLDSTATE.
82 (should (equal (parse-partial-sexp pointC pointX nil nil ppsC)
83 ppsX)))))
84
85;;; syntax-tests.el ends here