From: Alan Mackenzie <acm@muc.de>
Date: Tue, 26 Jul 2011 19:18:40 +0000 (+0000)
Subject: Fontify bitfield declarations properly.
X-Git-Tag: emacs-pretest-24.0.90~104^2~159^2~14
X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=b248a85d103b7f34b9747e8d6884179bf3d4decb;p=emacs.git

Fontify bitfield declarations properly.

cc-langs.el (c-has-bitfields): New lang variable.
(c-symbol-chars): Now exported as a lang variable.
(c-not-primitive-type-keywords): New lang variable.

cc-fonts.el (c-font-lock-declarations): Jump over the QT keyword "more"
to prevent "more slots: ...." being spuriously parsed as a bitfield
declaraion.

cc-engine.el (c-beginning-of-statement-1): Refactor and enhance to handle
bitfield declarations.
(c-punctuation-in): New function.
(c-forward-decl-or-cast-1): Enhance CASE 3 to handle bitfield
declarations properly.
---

diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index b2e2dd6d8aa..8a25116efcb 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,21 @@
+2011-07-26  Alan Mackenzie  <acm@muc.de>
+
+	Fontify bitfield declarations properly.
+
+	* progmodes/cc-langs.el (c-has-bitfields): New lang variable.
+	(c-symbol-chars): Now exported as a lang variable.
+	(c-not-primitive-type-keywords): New lang variable.
+
+	* progmodes/cc-fonts.el (c-font-lock-declarations): Jump over the
+	QT keyword "more" to prevent "more slots: ...." being spuriously
+	parsed as a bitfield declaraion.
+
+	* progmodes/cc-engine.el (c-beginning-of-statement-1): Refactor
+	and enhance to handle bitfield declarations.
+	(c-punctuation-in): New function.
+	(c-forward-decl-or-cast-1): Enhance CASE 3 to handle bitfield
+	declarations properly.
+
 2011-07-26  Ulf Jasper  <ulf.jasper@web.de>
 
 	* calendar/icalendar.el (icalendar--all-events): Take care of
diff --git a/lisp/progmodes/cc-engine.el b/lisp/progmodes/cc-engine.el
index a6fd28be21d..0236a2be296 100644
--- a/lisp/progmodes/cc-engine.el
+++ b/lisp/progmodes/cc-engine.el
@@ -709,6 +709,9 @@ comment at the start of cc-engine.el for more info."
 	;; content was found in the label.  Note that we might still
 	;; regard it a label if it starts with `c-label-kwds'.
 	label-good-pos
+	;; Putative positions of the components of a bitfield declaration,
+	;; e.g. "int foo : NUM_FOO_BITS ;"
+	bitfield-type-pos bitfield-id-pos bitfield-size-pos
 	;; Symbol just scanned back over (e.g. 'while or 'boundary).
 	;; See above.
 	sym
@@ -765,13 +768,22 @@ comment at the start of cc-engine.el for more info."
 	    ;; Record this as the first token if not starting inside it.
 	    (setq tok start))
 
-        ;; The following while loop goes back one sexp (balanced parens,
-        ;; etc. with contents, or symbol or suchlike) each iteration.  This
-        ;; movement is accomplished with a call to scan-sexps approx 130 lines
-        ;; below.
+
+	;; The following while loop goes back one sexp (balanced parens,
+	;; etc. with contents, or symbol or suchlike) each iteration.  This
+	;; movement is accomplished with a call to c-backward-sexp approx 170
+	;; lines below.
+	;;
+	;; The loop is exited only by throwing nil to the (catch 'loop ...):
+	;; 1. On reaching the start of a macro;
+	;; 2. On having passed a stmt boundary with the PDA stack empty;
+	;; 3. On reaching the start of an Objective C method def;
+	;; 4. From macro `c-bos-pop-state'; when the stack is empty;
+	;; 5. From macro `c-bos-pop-state-and-retry' when the stack is empty.
 	(while
 	    (catch 'loop ;; Throw nil to break, non-nil to continue.
 	      (cond
+	       ;; Are we in a macro, just after the opening #?
 	       ((save-excursion
 		  (and macro-start	; Always NIL for AWK.
 		       (progn (skip-chars-backward " \t")
@@ -792,7 +804,7 @@ comment at the start of cc-engine.el for more info."
 		  (setq pos saved
 			ret 'macro
 			ignore-labels t))
-		(throw 'loop nil))
+		(throw 'loop nil))	; 1. Start of macro.
 
 	       ;; Do a round through the automaton if we've just passed a
 	       ;; statement boundary or passed a "while"-like token.
@@ -801,7 +813,7 @@ comment at the start of cc-engine.el for more info."
 			 (setq sym (intern (match-string 1)))))
 
 		(when (and (< pos start) (null stack))
-		  (throw 'loop nil))
+		  (throw 'loop nil))	; 2. Statement boundary.
 
 		;; The PDA state handling.
                 ;;
@@ -918,19 +930,14 @@ comment at the start of cc-engine.el for more info."
                 ;; HERE IS THE SINGLE PLACE INSIDE THE PDA LOOP WHERE WE MOVE
 		;; BACKWARDS THROUGH THE SOURCE.
 
-		;; This is typically fast with the caching done by
-		;; c-(backward|forward)-sws.
 		(c-backward-syntactic-ws)
-
 		(let ((before-sws-pos (point))
-		      ;; Set as long as we have to continue jumping by sexps.
-		      ;; It's the position to use as end in the next round.
-		      sexp-loop-continue-pos
 		      ;; The end position of the area to search for statement
 		      ;; barriers in this round.
-		      (sexp-loop-end-pos pos))
+		      (maybe-after-boundary-pos pos))
 
-		  ;; The following while goes back one sexp per iteration.
+		  ;; Go back over exactly one logical sexp, taking proper
+		  ;; account of macros and escaped EOLs.
 		  (while
 		      (progn
 			(unless (c-safe (c-backward-sexp) t)
@@ -938,81 +945,87 @@ comment at the start of cc-engine.el for more info."
 			  ;; stack won't be empty the code below will report a
 			  ;; suitable error.
 			  (throw 'loop nil))
-
-			;; Check if the sexp movement crossed a statement or
-			;; declaration boundary.  But first modify the point
-			;; so that `c-crosses-statement-barrier-p' only looks
-			;; at the non-sexp chars following the sexp.
-			(save-excursion
-			  (when (setq
-				 boundary-pos
-				 (cond
-				  ((if macro-start
-				       nil
-				     (save-excursion
-				       (when (c-beginning-of-macro)
-					 ;; Set continuation position in case
-					 ;; `c-crosses-statement-barrier-p'
-					 ;; doesn't detect anything below.
-					 (setq sexp-loop-continue-pos (point)))))
-				   ;; If the sexp movement took us into a
-				   ;; macro then there were only some non-sexp
-				   ;; chars after it.  Skip out of the macro
-				   ;; to analyze them but not the non-sexp
-				   ;; chars that might be inside the macro.
-				   (c-end-of-macro)
-				   (c-crosses-statement-barrier-p
-				    (point) sexp-loop-end-pos))
-
-				  ((and
-				    (eq (char-after) ?{)
-				    (not (c-looking-at-inexpr-block lim nil t)))
-				   ;; Passed a block sexp.  That's a boundary
-				   ;; alright.
-				   (point))
-
-				  ((looking-at "\\s\(")
-				   ;; Passed some other paren.  Only analyze
-				   ;; the non-sexp chars after it.
-				   (goto-char (1+ (c-down-list-backward
-						   before-sws-pos)))
-				   ;; We're at a valid token start position
-				   ;; (outside the `save-excursion') if
-				   ;; `c-crosses-statement-barrier-p' failed.
-				   (c-crosses-statement-barrier-p
-				    (point) sexp-loop-end-pos))
-
-				  (t
-				   ;; Passed a symbol sexp or line
-				   ;; continuation.  It doesn't matter that
-				   ;; it's included in the analyzed region.
-				   (if (c-crosses-statement-barrier-p
-					(point) sexp-loop-end-pos)
-				       t
-				     ;; If it was a line continuation then we
-				     ;; have to continue looping.
-				     (if (looking-at "\\\\$")
-					 (setq sexp-loop-continue-pos (point)))
-				     nil))))
-
-			    (setq pptok ptok
-				  ptok tok
-				  tok boundary-pos
-				  sym 'boundary)
-			    ;; Like a C "continue".  Analyze the next sexp.
-			    (throw 'loop t)))
-
-			sexp-loop-continue-pos)	; End of "go back a sexp" loop condition.
-		    (goto-char sexp-loop-continue-pos)
-		    (setq sexp-loop-end-pos sexp-loop-continue-pos
-			  sexp-loop-continue-pos nil))))
+			(cond
+			 ;; Have we moved into a macro?
+			 ((and (not macro-start)
+			       (c-beginning-of-macro))
+			  ;; Have we crossed a statement boundary?  If not,
+			  ;; keep going back until we find one or a "real" sexp.
+			  (and
+			   (save-excursion
+			     (c-end-of-macro)
+			     (not (c-crosses-statement-barrier-p
+				   (point) maybe-after-boundary-pos)))
+			   (setq maybe-after-boundary-pos (point))))
+			 ;; Have we just gone back over an escaped NL?  This
+			 ;; doesn't count as a sexp.
+			 ((looking-at "\\\\$")))))
+
+		  ;; Have we crossed a statement boundary?
+		  (setq boundary-pos
+			(cond
+			 ;; Are we at a macro beginning?
+			 ((and (not macro-start)
+			       c-opt-cpp-prefix
+			       (looking-at c-opt-cpp-prefix))
+			  (save-excursion
+			    (c-end-of-macro)
+			    (c-crosses-statement-barrier-p
+			     (point) maybe-after-boundary-pos)))
+			 ;; Just gone back over a brace block?
+			 ((and
+			   (eq (char-after) ?{)
+			   (not (c-looking-at-inexpr-block lim nil t)))
+			  (save-excursion
+			    (c-forward-sexp) (point)))
+			 ;; Just gone back over some paren block?
+			 ((looking-at "\\s\(")
+			  (save-excursion
+			    (goto-char (1+ (c-down-list-backward
+					    before-sws-pos)))
+			    (c-crosses-statement-barrier-p
+			     (point) maybe-after-boundary-pos)))
+			 ;; Just gone back over an ordinary symbol of some sort?
+			 (t (c-crosses-statement-barrier-p
+			     (point) maybe-after-boundary-pos))))
+	
+		  (when boundary-pos
+		    (setq pptok ptok
+			  ptok tok
+			  tok boundary-pos
+			  sym 'boundary)
+		    ;; Like a C "continue".  Analyze the next sexp.
+		    (throw 'loop t))))
 
 	      ;; ObjC method def?
 	      (when (and c-opt-method-key
 			 (setq saved (c-in-method-def-p)))
 		(setq pos saved
 		      ignore-labels t)	; Avoid the label check on exit.
-		(throw 'loop nil))
+		(throw 'loop nil))	; 3. ObjC method def.
+
+	      ;; Might we have a bitfield declaration, "<type> <id> : <size>"?
+	      (if c-has-bitfields
+		  (cond
+		   ;; The : <size> and <id> fields?
+		   ((and (numberp c-maybe-labelp)
+			 (not bitfield-size-pos)
+			 (save-excursion
+			   (goto-char (or tok start))
+			   (not (looking-at c-keywords-regexp)))
+			 (not (looking-at c-keywords-regexp))
+			 (not (c-punctuation-in (point) c-maybe-labelp)))
+		    (setq bitfield-size-pos (or tok start)
+			  bitfield-id-pos (point)))
+		   ;; The <type> field?
+		   ((and bitfield-id-pos
+			 (not bitfield-type-pos))
+		    (if (and (looking-at c-symbol-key) ; Can only be an integer type.  :-)
+			     (not (looking-at c-not-primitive-type-keywords-regexp))
+			     (not (c-punctuation-in (point) tok)))
+			(setq bitfield-type-pos (point))
+		      (setq bitfield-size-pos nil
+			    bitfield-id-pos nil)))))
 
 	      ;; Handle labels.
 	      (unless (eq ignore-labels t)
@@ -1044,8 +1057,10 @@ comment at the start of cc-engine.el for more info."
 		    pptok ptok
 		    ptok tok
 		    tok (point)
-		    pos tok)))		; Not nil (for the while loop).
-
+		    pos tok) ; always non-nil
+	      )		     ; end of (catch loop ....)
+	  )		     ; end of sexp-at-a-time (while ....)
+	
 	;; If the stack isn't empty there might be errors to report.
 	(while stack
 	  (if (and (vectorp saved-pos) (eq (length saved-pos) 3))
@@ -1067,6 +1082,7 @@ comment at the start of cc-engine.el for more info."
 		   (eq c-maybe-labelp t)
 		   (not (eq ret 'beginning))
 		   after-labels-pos
+		   (not bitfield-type-pos) ; Bitfields take precedence over labels.
 		   (or (not label-good-pos)
 		       (<= label-good-pos pos)
 		       (progn
@@ -1104,6 +1120,19 @@ comment at the start of cc-engine.el for more info."
       (goto-char pos)
       ret)))
 
+(defun c-punctuation-in (from to)
+  "Return non-nil if there is a non-comment non-macro punctuation character
+between FROM and TO.  FROM must not be in a string or comment.  The returned
+value is the position of the first such character."
+  (save-excursion
+    (goto-char from)
+    (let ((pos (point)))
+      (while (progn (skip-chars-forward c-symbol-chars to)
+		    (c-forward-syntactic-ws to)
+		    (> (point) pos))
+	(setq pos (point))))
+    (and (< (point) to) (point))))
+
 (defun c-crosses-statement-barrier-p (from to)
   "Return non-nil if buffer positions FROM to TO cross one or more
 statement or declaration boundaries.  The returned value is actually
@@ -6618,19 +6647,27 @@ comment at the start of cc-engine.el for more info."
 	  (if backup-at-type
 	      (progn
 
-		;; CASE 3
-		(when (= (point) start)
-		  ;; Got a plain list of identifiers.  If a colon follows it's
-		   ;; a valid label.  Otherwise the last one probably is the
-		   ;; declared identifier and we should back up to the previous
-		   ;; type, providing it isn't a cast.
+
+		 ;; CASE 3
+		 (when (= (point) start)
+		   ;; Got a plain list of identifiers. If a colon follows it's
+		   ;; a valid label, or maybe a bitfield.  Otherwise the last
+		   ;; one probably is the declared identifier and we should
+		   ;; back up to the previous type, providing it isn't a cast.
 		   (if (and (eq (char-after) ?:)
 			    (not (c-major-mode-is 'java-mode)))
-		       ;; If we've found a specifier keyword then it's a
-		       ;; declaration regardless.
-		       (throw 'at-decl-or-cast (eq at-decl-or-cast t))
-		    (setq backup-if-not-cast t)
-		    (throw 'at-decl-or-cast t)))
+		       (cond
+			;; If we've found a specifier keyword then it's a
+			;; declaration regardless.
+			((eq at-decl-or-cast t)
+			 (throw 'at-decl-or-cast t))
+			((and c-has-bitfields
+			      (eq at-decl-or-cast 'ids)) ; bitfield.
+			 (setq backup-if-not-cast t)
+			 (throw 'at-decl-or-cast t)))
+
+		     (setq backup-if-not-cast t)
+		     (throw 'at-decl-or-cast t)))
 
 		;; CASE 4
 		(when (and got-suffix
diff --git a/lisp/progmodes/cc-fonts.el b/lisp/progmodes/cc-fonts.el
index 0500d48ddbc..2277ba760ab 100644
--- a/lisp/progmodes/cc-fonts.el
+++ b/lisp/progmodes/cc-fonts.el
@@ -1179,6 +1179,14 @@ casts and declarations are fontified.  Used on level 2 and higher."
 		  (goto-char start-pos)))
 
 	    ;; Now analyze the construct.
+	    ;; In QT, "more" is an irritating keyword that expands to nothing.
+	    ;; We skip over it to prevent recognition of "more slots: <symbol>"
+	    ;; as a bitfield declaration.
+	    (when (and (c-major-mode-is 'c++-mode)
+		       (looking-at
+			(concat "\\(more\\)\\([^" c-symbol-chars "]\\|$\\)")))
+	      (goto-char (match-end 1))
+	      (c-forward-syntactic-ws))
 	    (setq decl-or-cast (c-forward-decl-or-cast-1
 				match-pos context last-cast-end))
 
diff --git a/lisp/progmodes/cc-langs.el b/lisp/progmodes/cc-langs.el
index d7ef278174a..35097242cb7 100644
--- a/lisp/progmodes/cc-langs.el
+++ b/lisp/progmodes/cc-langs.el
@@ -511,6 +511,12 @@ parameters \(point-min), \(point-max) and <buffer size>."
 
 ;;; Lexer-level syntax (identifiers, tokens etc).
 
+(c-lang-defconst c-has-bitfields
+  "Whether the language has bitfield declarations."
+  t nil
+  (c c++ objc) t)
+(c-lang-defvar c-has-bitfields (c-lang-const c-has-bitfields))
+
 (c-lang-defconst c-symbol-start
   "Regexp that matches the start of a symbol, i.e. any identifier or
 keyword.  It's unspecified how far it matches.	Does not contain a \\|
@@ -528,6 +534,7 @@ This is of the form that fits inside [ ] in a regexp."
   ;; operator chars too, but they are handled with other means instead.
   t    (concat c-alnum "_$")
   objc (concat c-alnum "_$@"))
+(c-lang-defvar c-symbol-chars (c-lang-const c-symbol-chars))
 
 (c-lang-defconst c-symbol-key
   "Regexp matching identifiers and keywords (with submatch 0).  Assumed
@@ -1927,6 +1934,21 @@ one of `c-type-list-kwds', `c-ref-list-kwds',
 (c-lang-defvar c-not-decl-init-keywords
   (c-lang-const c-not-decl-init-keywords))
 
+(c-lang-defconst c-not-primitive-type-keywords
+  "List of all keywords apart from primitive types (like \"int\")."
+  t (set-difference (c-lang-const c-keywords)
+		    (c-lang-const c-primitive-type-kwds)
+		    :test 'string-equal)
+  ;; The "more" for C++ is the QT keyword (as in "more slots:").
+  ;; This variable is intended for use in c-beginning-of-statement-1.
+  c++ (append (c-lang-const c-not-primitive-type-keywords) '("more")))
+
+(c-lang-defconst c-not-primitive-type-keywords-regexp
+  t (c-make-keywords-re t
+      (c-lang-const c-not-primitive-type-keywords)))
+(c-lang-defvar c-not-primitive-type-keywords-regexp
+  (c-lang-const c-not-primitive-type-keywords-regexp))
+
 (c-lang-defconst c-protection-kwds
   "Access protection label keywords in classes."
   t    nil