cwarn.el 12.2 KB
Newer Older
1
;;; cwarn.el --- highlight suspicious C and C++ constructions
Gerd Moellmann's avatar
Gerd Moellmann committed
2

Glenn Morris's avatar
Glenn Morris committed
3
;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
4
;; Free Software Foundation, Inc.
Gerd Moellmann's avatar
Gerd Moellmann committed
5 6 7 8 9 10 11 12

;; Author: Anders Lindgren <andersl@andersl.com>
;; Keywords: c, languages, faces
;; X-Url: http://www.andersl.com/emacs
;; Version: 1.3.1  1999-12-13

;; This file is part of GNU Emacs.

13
;; GNU Emacs is free software: you can redistribute it and/or modify
Gerd Moellmann's avatar
Gerd Moellmann committed
14
;; it under the terms of the GNU General Public License as published by
15 16
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
Gerd Moellmann's avatar
Gerd Moellmann committed
17 18 19 20 21 22 23

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
24
;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
Gerd Moellmann's avatar
Gerd Moellmann committed
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40

;;; Commentary:

;;{{{ Documentation

;; Description:
;;
;; CWarn is a package that highlights suspicious C and C++ constructions.
;;
;; For example, take a look at the following piece of C code:
;;
;;     if (x = 0);
;;       foo();
;;
;; The code contains two, possibly fatal, bugs.  The first is that the
;; assignment operator "=" is used as part of the test; the user
41
;; probably meant to use the comparison operator "==".
Gerd Moellmann's avatar
Gerd Moellmann committed
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
;;
;; The second problem is that an extra semicolon is placed after
;; closing parenthesis of the test expression.  This makes the body of
;; the if statement to be an empty statement, not the call to the
;; function "foo", as the user probably intended.
;;
;; This package is capable of highlighting the following C and C++
;; constructions:
;;
;; * Assignments inside expressions, including variations like "+=".
;; * Semicolon following immediately after `if', `for', and `while'
;;   (except, of course, after a `do .. while' statement).
;; * C++ functions with reference parameters.
;;
;; Note that none of the constructions highlighted (especially not C++
57
;; reference parameters) are considered errors by the language
Gerd Moellmann's avatar
Gerd Moellmann committed
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
;; definitions.

;; Usage:
;;
;; CWarn is implemented as two minor modes: `cwarn-mode' and
;; `global-cwarn-mode'.  The former can be applied to individual buffers
;; and the latter to all buffers.
;;
;; Activate this package by Customize, or by placing the following line
;; into the appropriate init file:
;;
;;    (global-cwarn-mode 1)
;;
;; Also, `font-lock-mode' or `global-font-lock-mode' must be enabled.

;; Afterthought:
;;
;; After using this package for several weeks it feels as though I
;; find stupid typo-style bugs while editing rather than at compile-
;; or run-time, if I ever find them.
;;
;; On the other hand, I find myself using assignments inside
;; expressions much more often than I used to do.  The reason is that
;; there is no risk of interpreting an assignment operator as a
;; comparison ("hey, the assignment operator is red, duh!").

;; Reporting bugs:
;;
;;     Out of the last ten bugs you found, how many did you report?
;;
;; When reporting a bug, please:
;;
;; * Send a mail the maintainer of the package, or to the author
;;   if no maintainer exists.
;; * Include the name of the package in the title of the mail, to
;;   simplify for the recipient.
;; * State exactly what you did, what happened, and what you expected
;;   to see when you found the bug.
;; * If the bug cause an error, set the variable `debug-on-error' to t,
97
;;   repeat the operations that triggered the error and include
Gerd Moellmann's avatar
Gerd Moellmann committed
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
;;   the backtrace in the letter.
;; * If possible, include an example that activates the bug.
;; * Should you speculate about the cause of the problem, please
;;   state explicitly that you are guessing.

;;}}}

;;; Code:

;;{{{ Dependencies

(eval-when-compile (require 'cl))

(require 'custom)
(require 'font-lock)
(require 'cc-mode)

;;}}}
;;{{{ Variables

(defgroup cwarn nil
  "Highlight suspicious C and C++ constructions."
120 121
  :version "21.1"
  :link '(url-link "http://www.andersl.com/emacs")
Gerd Moellmann's avatar
Gerd Moellmann committed
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
  :group 'faces)

(defvar cwarn-mode nil
  "*Non-nil when Cwarn mode is active.

Never set this variable directly, use the command `cwarn-mode'
instead.")

(defcustom cwarn-configuration
  '((c-mode (not reference))
    (c++-mode t))
  "*List of items each describing which features are enable for a mode.
Each item is on the form (mode featurelist), where featurelist can be
on one of three forms:

* A list of enabled features.
* A list starting with the atom `not' followed by the features
  which are not enabled.
* The atom t, that represent that all features are enabled.

See variable `cwarn-font-lock-feature-keywords-alist' for available
features."
144
  :type '(repeat sexp)
Gerd Moellmann's avatar
Gerd Moellmann committed
145 146 147 148 149 150
  :group 'cwarn)

(defcustom cwarn-font-lock-feature-keywords-alist
  '((assign    . cwarn-font-lock-assignment-keywords)
    (semicolon . cwarn-font-lock-semicolon-keywords)
    (reference . cwarn-font-lock-reference-keywords))
151
  "An alist mapping a CWarn feature to font-lock keywords.
Gerd Moellmann's avatar
Gerd Moellmann committed
152 153 154
The keywords could either a font-lock keyword list or a symbol.
If it is a symbol it is assumed to be a variable containing a font-lock
keyword list."
155 156 157
  :type '(alist :key-type (choice (const assign)
				  (const semicolon)
				  (const reference))
158
		:value-type (sexp :tag "Value"))
Gerd Moellmann's avatar
Gerd Moellmann committed
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
  :group 'cwarn)

(defcustom cwarn-verbose t
  "*When nil, CWarn mode will not generate any messages.

Currently, messages are generated when the mode is activated and
deactivated."
  :group 'cwarn
  :type 'boolean)

(defcustom cwarn-mode-text " CWarn"
  "*String to display in the mode line when CWarn mode is active.

\(When the string is not empty, make sure that it has a leading space.)"
  :tag "CWarn mode text"                ; To separate it from `global-...'
  :group 'cwarn
  :type 'string)

(defcustom cwarn-load-hook nil
  "*Functions to run when CWarn mode is first loaded."
  :tag "Load Hook"
  :group 'cwarn
  :type 'hook)

;;}}}
;;{{{ The modes

;;;###autoload
187
(define-minor-mode cwarn-mode
188
  "Minor mode that highlights suspicious C and C++ constructions.
Gerd Moellmann's avatar
Gerd Moellmann committed
189

Eli Zaretskii's avatar
Eli Zaretskii committed
190 191
Suspicious constructs are highlighted using `font-lock-warning-face'.

Gerd Moellmann's avatar
Gerd Moellmann committed
192 193 194 195 196
Note, in addition to enabling this minor mode, the major mode must
be included in the variable `cwarn-configuration'.  By default C and
C++ modes are included.

With ARG, turn CWarn mode on if and only if arg is positive."
Lute Kamstra's avatar
Lute Kamstra committed
197
  :group 'cwarn :lighter cwarn-mode-text
198 199
  (cwarn-font-lock-keywords cwarn-mode)
  (if font-lock-mode (font-lock-fontify-buffer)))
Gerd Moellmann's avatar
Gerd Moellmann committed
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237

;;;###autoload
(defun turn-on-cwarn-mode ()
  "Turn on CWarn mode.

This function is designed to be added to hooks, for example:
  (add-hook 'c-mode-hook 'turn-on-cwarn-mode)"
  (cwarn-mode 1))

;;}}}
;;{{{ Help functions

(defun cwarn-is-enabled (mode &optional feature)
  "Non-nil if CWarn FEATURE is enabled for MODE.
feature is an atom representing one construction to highlight.

Check if any feature is enabled for MODE if no feature is specified.

The valid features are described by the variable
`cwarn-font-lock-feature-keywords-alist'."
  (let ((mode-configuraion (assq mode cwarn-configuration)))
    (and mode-configuraion
	 (or (null feature)
	     (let ((list-or-t (nth 1 mode-configuraion)))
	       (or (eq list-or-t t)
		   (if (eq (car-safe list-or-t) 'not)
		       (not (memq feature (cdr list-or-t)))
		     (memq feature list-or-t))))))))

(defun cwarn-inside-macro ()
  "True if point is inside a C macro definition."
  (save-excursion
    (beginning-of-line)
    (while (eq (char-before (1- (point))) ?\\)
      (forward-line -1))
    (back-to-indentation)
    (eq (char-after) ?#)))

238 239 240
(defun cwarn-font-lock-keywords (addp)
  "Install/Remove keywords into current buffer.
If ADDP is non-nil, install else remove."
Gerd Moellmann's avatar
Gerd Moellmann committed
241 242 243 244 245
  (dolist (pair cwarn-font-lock-feature-keywords-alist)
    (let ((feature (car pair))
	  (keywords (cdr pair)))
      (if (not (listp keywords))
	  (setq keywords (symbol-value keywords)))
246 247 248
      (if (cwarn-is-enabled major-mode feature)
	  (funcall (if addp 'font-lock-add-keywords 'font-lock-remove-keywords)
		   nil keywords)))))
Gerd Moellmann's avatar
Gerd Moellmann committed
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265

;;}}}
;;{{{ Backward compatibility

;; This piece of code will be part of CC mode as of Emacs 20.4.
(if (not (fboundp 'c-at-toplevel-p))
(defun c-at-toplevel-p ()
  "Return a determination as to whether point is at the `top-level'.
Being at the top-level means that point is either outside any
enclosing block (such function definition), or inside a class
definition, but outside any method blocks.

If point is not at the top-level (e.g. it is inside a method
definition), then nil is returned.  Otherwise, if point is at a
top-level not enclosed within a class definition, t is returned.
Otherwise, a 2-vector is returned where the zeroth element is the
buffer position of the start of the class declaration, and the first
266
element is the buffer position of the enclosing class' opening
Gerd Moellmann's avatar
Gerd Moellmann committed
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
brace."
  (let ((state (c-parse-state)))
    (or (not (c-most-enclosing-brace state))
	(c-search-uplist-for-classkey state))))
)

;;}}}
;;{{{ Font-lock keywords and match functions

;; This section contains font-lock keywords.  A font lock keyword can
;; either contain a regular expression or a match function.  All
;; keywords defined here use match functions since the C and C++
;; constructions highlighted by CWarn are too complex to be matched by
;; regular expressions.
;;
;; A match function should act like a normal forward search.  They
;; should return non-nil if they found a candidate and the match data
;; should correspond to the highlight part of the font-lock keyword.
285
;; The functions should not generate errors, in that case font-lock
Gerd Moellmann's avatar
Gerd Moellmann committed
286 287 288 289 290 291 292
;; will fail to highlight the buffer.  A match function takes one
;; argument, LIMIT, that represent the end of area to be searched.
;;
;; The variable `cwarn-font-lock-feature-keywords-alist' contains a
;; mapping from CWarn features to the font-lock keywords defined
;; below.

293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
(defmacro cwarn-font-lock-match (re &rest body)
  "Match RE but only if BODY holds."
  `(let ((res nil))
     (while
	 (progn
	   (setq res (re-search-forward ,re limit t))
	   (and res
		(save-excursion
		  (when (match-beginning 1) (goto-char (match-beginning 1)))
		  (condition-case nil	; In case something barfs.
		      (not (save-match-data
			     ,@body))
		    (error t))))))
     res))

Gerd Moellmann's avatar
Gerd Moellmann committed
308 309 310 311 312 313 314 315
;;{{{ Assignment in expressions

(defconst cwarn-font-lock-assignment-keywords
  '((cwarn-font-lock-match-assignment-in-expression
     (1 font-lock-warning-face))))

(defun cwarn-font-lock-match-assignment-in-expression (limit)
  "Match assignments inside expressions."
316 317 318 319 320 321 322 323 324 325 326
  (cwarn-font-lock-match
   "[^!<>=]\\(\\([-+*/%&^|]\\|<<\\|>>\\)?=\\)[^=]"
   (backward-up-list 1)
   (and (memq (following-char) '(?\( ?\[))
	(not (progn
	       (skip-chars-backward " ")
	       (skip-chars-backward "a-zA-Z0-9_")
	       (or
		;; Default parameter of function.
		(c-at-toplevel-p)
		(looking-at "for\\>")))))))
Gerd Moellmann's avatar
Gerd Moellmann committed
327 328 329 330 331 332 333 334 335

;;}}}
;;{{{ Semicolon

(defconst cwarn-font-lock-semicolon-keywords
  '((cwarn-font-lock-match-dangerous-semicolon (0 font-lock-warning-face))))

(defun cwarn-font-lock-match-dangerous-semicolon (limit)
  "Match semicolons directly after `for', `while', and `if'.
336 337 338 339 340 341 342 343 344 345 346
The semicolon after a `do { ... } while (x);' construction is not matched."
  (cwarn-font-lock-match
   ";"
   (backward-sexp 2)			; Expression and keyword.
   (or (looking-at "\\(for\\|if\\)\\>")
       (and (looking-at "while\\>")
	    (condition-case nil
		(progn
		  (backward-sexp 2)	; Body and "do".
		  (not (looking-at "do\\>")))
	      (error t))))))
Gerd Moellmann's avatar
Gerd Moellmann committed
347 348 349 350 351 352 353 354 355

;;}}}
;;{{{ Reference

(defconst cwarn-font-lock-reference-keywords
  '((cwarn-font-lock-match-reference (1 font-lock-warning-face))))

(defun cwarn-font-lock-match-reference (limit)
  "Font-lock matcher for C++ reference parameters."
356 357 358 359 360 361
  (cwarn-font-lock-match
   "[^&]\\(&\\)[^&=]"
   (backward-up-list 1)
   (and (eq (following-char) ?\()
	(not (cwarn-inside-macro))
	(c-at-toplevel-p))))
Gerd Moellmann's avatar
Gerd Moellmann committed
362 363 364 365 366 367

;;}}}

;;}}}
;;{{{ The end

368 369 370 371 372 373 374
(defun turn-on-cwarn-mode-if-enabled ()
  "Turn on CWarn mode in the current buffer if applicable.
The mode is turned if some feature is enabled for the current
`major-mode' in `cwarn-configuration'."
  (if (cwarn-is-enabled major-mode) (turn-on-cwarn-mode)))

;;;###autoload
375
(define-globalized-minor-mode global-cwarn-mode
376
  cwarn-mode turn-on-cwarn-mode-if-enabled)
Gerd Moellmann's avatar
Gerd Moellmann committed
377 378 379 380 381 382 383

(provide 'cwarn)

(run-hooks 'cwarn-load-hook)

;;}}}

384
;; arch-tag: 225fb5e2-0838-4eb1-88ce-3811c5e4d738
Gerd Moellmann's avatar
Gerd Moellmann committed
385
;;; cwarn.el ends here