26963
|
1 ;;; cwarn.el --- Highlight suspicious C and C++ constructions.
|
|
2
|
|
3 ;; Copyright (C) 1999 Free Software Foundation, Inc.
|
|
4
|
|
5 ;; Author: Anders Lindgren <andersl@andersl.com>
|
|
6 ;; Keywords: c, languages, faces
|
|
7 ;; X-Url: http://www.andersl.com/emacs
|
|
8 ;; Version: 1.3.1 1999-12-13
|
|
9
|
|
10 ;; This file is part of GNU Emacs.
|
|
11
|
|
12 ;; GNU Emacs is free software; you can redistribute it and/or modify
|
|
13 ;; it under the terms of the GNU General Public License as published by
|
|
14 ;; the Free Software Foundation; either version 2, or (at your option)
|
|
15 ;; any later version.
|
|
16
|
|
17 ;; GNU Emacs is distributed in the hope that it will be useful,
|
|
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
20 ;; GNU General Public License for more details.
|
|
21
|
|
22 ;; You should have received a copy of the GNU General Public License
|
|
23 ;; along with GNU Emacs; see the file COPYING. If not, write to the
|
|
24 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
25 ;; Boston, MA 02111-1307, USA.
|
|
26
|
|
27 ;;; Commentary:
|
|
28
|
|
29 ;;{{{ Documentation
|
|
30
|
|
31 ;; Description:
|
|
32 ;;
|
|
33 ;; CWarn is a package that highlights suspicious C and C++ constructions.
|
|
34 ;;
|
|
35 ;; For example, take a look at the following piece of C code:
|
|
36 ;;
|
|
37 ;; if (x = 0);
|
|
38 ;; foo();
|
|
39 ;;
|
|
40 ;; The code contains two, possibly fatal, bugs. The first is that the
|
|
41 ;; assignment operator "=" is used as part of the test; the user
|
|
42 ;; probably ment to use the comparison operator "==".
|
|
43 ;;
|
|
44 ;; The second problem is that an extra semicolon is placed after
|
|
45 ;; closing parenthesis of the test expression. This makes the body of
|
|
46 ;; the if statement to be an empty statement, not the call to the
|
|
47 ;; function "foo", as the user probably intended.
|
|
48 ;;
|
|
49 ;; This package is capable of highlighting the following C and C++
|
|
50 ;; constructions:
|
|
51 ;;
|
|
52 ;; * Assignments inside expressions, including variations like "+=".
|
|
53 ;; * Semicolon following immediately after `if', `for', and `while'
|
|
54 ;; (except, of course, after a `do .. while' statement).
|
|
55 ;; * C++ functions with reference parameters.
|
|
56 ;;
|
|
57 ;; Note that none of the constructions highlighted (especially not C++
|
|
58 ;; reference parameters) are considered errors by the langauage
|
|
59 ;; definitions.
|
|
60
|
|
61 ;; Usage:
|
|
62 ;;
|
|
63 ;; CWarn is implemented as two minor modes: `cwarn-mode' and
|
|
64 ;; `global-cwarn-mode'. The former can be applied to individual buffers
|
|
65 ;; and the latter to all buffers.
|
|
66 ;;
|
|
67 ;; Activate this package by Customize, or by placing the following line
|
|
68 ;; into the appropriate init file:
|
|
69 ;;
|
|
70 ;; (global-cwarn-mode 1)
|
|
71 ;;
|
|
72 ;; Also, `font-lock-mode' or `global-font-lock-mode' must be enabled.
|
|
73
|
|
74 ;; Afterthought:
|
|
75 ;;
|
|
76 ;; After using this package for several weeks it feels as though I
|
|
77 ;; find stupid typo-style bugs while editing rather than at compile-
|
|
78 ;; or run-time, if I ever find them.
|
|
79 ;;
|
|
80 ;; On the other hand, I find myself using assignments inside
|
|
81 ;; expressions much more often than I used to do. The reason is that
|
|
82 ;; there is no risk of interpreting an assignment operator as a
|
|
83 ;; comparison ("hey, the assignment operator is red, duh!").
|
|
84
|
|
85 ;; Reporting bugs:
|
|
86 ;;
|
|
87 ;; Out of the last ten bugs you found, how many did you report?
|
|
88 ;;
|
|
89 ;; When reporting a bug, please:
|
|
90 ;;
|
|
91 ;; * Send a mail the maintainer of the package, or to the author
|
|
92 ;; if no maintainer exists.
|
|
93 ;; * Include the name of the package in the title of the mail, to
|
|
94 ;; simplify for the recipient.
|
|
95 ;; * State exactly what you did, what happened, and what you expected
|
|
96 ;; to see when you found the bug.
|
|
97 ;; * If the bug cause an error, set the variable `debug-on-error' to t,
|
|
98 ;; repreat the operations that triggered the error and include
|
|
99 ;; the backtrace in the letter.
|
|
100 ;; * If possible, include an example that activates the bug.
|
|
101 ;; * Should you speculate about the cause of the problem, please
|
|
102 ;; state explicitly that you are guessing.
|
|
103
|
|
104 ;;}}}
|
|
105
|
|
106 ;;; Code:
|
|
107
|
|
108 ;;{{{ Dependencies
|
|
109
|
|
110 (eval-when-compile (require 'cl))
|
|
111
|
|
112 (require 'custom)
|
|
113 (require 'font-lock)
|
|
114 (require 'cc-mode)
|
|
115
|
|
116 ;;}}}
|
|
117 ;;{{{ Variables
|
|
118
|
|
119 (defgroup cwarn nil
|
|
120 "Highlight suspicious C and C++ constructions."
|
|
121 :group 'faces)
|
|
122
|
|
123 (defvar cwarn-mode nil
|
|
124 "*Non-nil when Cwarn mode is active.
|
|
125
|
|
126 Never set this variable directly, use the command `cwarn-mode'
|
|
127 instead.")
|
|
128
|
|
129 (defcustom global-cwarn-mode nil
|
|
130 "*When on, suspicious C and C++ constructions are highlighted.
|
|
131
|
|
132 Set this variable using \\[customize] or use the command
|
|
133 `global-cwarn-mode'."
|
|
134 :group 'cwarn
|
|
135 :initialize 'custom-initialize-default
|
|
136 :set '(lambda (symbol value)
|
|
137 (global-cwarn-mode (or value 0)))
|
|
138 :type 'boolean
|
|
139 :require 'cwarn)
|
|
140
|
|
141 (defcustom cwarn-configuration
|
|
142 '((c-mode (not reference))
|
|
143 (c++-mode t))
|
|
144 "*List of items each describing which features are enable for a mode.
|
|
145 Each item is on the form (mode featurelist), where featurelist can be
|
|
146 on one of three forms:
|
|
147
|
|
148 * A list of enabled features.
|
|
149 * A list starting with the atom `not' followed by the features
|
|
150 which are not enabled.
|
|
151 * The atom t, that represent that all features are enabled.
|
|
152
|
|
153 See variable `cwarn-font-lock-feature-keywords-alist' for available
|
|
154 features."
|
|
155 :group 'cwarn)
|
|
156
|
|
157 (defcustom cwarn-font-lock-feature-keywords-alist
|
|
158 '((assign . cwarn-font-lock-assignment-keywords)
|
|
159 (semicolon . cwarn-font-lock-semicolon-keywords)
|
|
160 (reference . cwarn-font-lock-reference-keywords))
|
|
161 "*Maps a CWarn feature to font-lock keywords.
|
|
162 The keywords could either a font-lock keyword list or a symbol.
|
|
163 If it is a symbol it is assumed to be a variable containing a font-lock
|
|
164 keyword list."
|
|
165 :group 'cwarn)
|
|
166
|
|
167 (defcustom cwarn-verbose t
|
|
168 "*When nil, CWarn mode will not generate any messages.
|
|
169
|
|
170 Currently, messages are generated when the mode is activated and
|
|
171 deactivated."
|
|
172 :group 'cwarn
|
|
173 :type 'boolean)
|
|
174
|
|
175 (defcustom cwarn-mode-text " CWarn"
|
|
176 "*String to display in the mode line when CWarn mode is active.
|
|
177
|
|
178 \(When the string is not empty, make sure that it has a leading space.)"
|
|
179 :tag "CWarn mode text" ; To separate it from `global-...'
|
|
180 :group 'cwarn
|
|
181 :type 'string)
|
|
182
|
|
183 (defcustom cwarn-mode-hook nil
|
|
184 "*Functions to run when CWarn mode is activated."
|
|
185 :tag "CWarn mode hook" ; To separate it from `global-...'
|
|
186 :group 'cwarn
|
|
187 :type 'hook)
|
|
188
|
|
189 (defcustom global-cwarn-mode-text ""
|
|
190 "*String to display when Global CWarn mode is active.
|
|
191
|
|
192 The default is nothing since when this mode is active this text doesn't
|
|
193 vary over time, or between buffers. Hence mode line text
|
|
194 would only waste precious space."
|
|
195 :group 'cwarn
|
|
196 :type 'string)
|
|
197
|
|
198 (defcustom global-cwarn-mode-hook nil
|
|
199 "*Hook called when Global CWarn mode is activated."
|
|
200 :group 'cwarn
|
|
201 :type 'hook)
|
|
202
|
|
203 (defcustom cwarn-load-hook nil
|
|
204 "*Functions to run when CWarn mode is first loaded."
|
|
205 :tag "Load Hook"
|
|
206 :group 'cwarn
|
|
207 :type 'hook)
|
|
208
|
|
209 ;;}}}
|
|
210 ;;{{{ The modes
|
|
211
|
|
212 ;;;###autoload
|
|
213 (defun cwarn-mode (&optional arg)
|
|
214 "Minor mode that hightlight suspicious C and C++ constructions.
|
|
215
|
|
216 Note, in addition to enabling this minor mode, the major mode must
|
|
217 be included in the variable `cwarn-configuration'. By default C and
|
|
218 C++ modes are included.
|
|
219
|
|
220 With ARG, turn CWarn mode on if and only if arg is positive."
|
|
221 (interactive "P")
|
|
222 (make-local-variable 'cwarn-mode)
|
|
223 (setq cwarn-mode
|
|
224 (if (null arg)
|
|
225 (not cwarn-mode)
|
|
226 (> (prefix-numeric-value arg) 0)))
|
|
227 (if (and cwarn-verbose
|
|
228 (interactive-p))
|
|
229 (message "Cwarn mode is now %s."
|
|
230 (if cwarn-mode "on" "off")))
|
|
231 (if (not global-cwarn-mode)
|
|
232 (if cwarn-mode
|
|
233 (cwarn-font-lock-add-keywords)
|
|
234 (cwarn-font-lock-remove-keywords)))
|
|
235 (font-lock-fontify-buffer)
|
|
236 (if cwarn-mode
|
|
237 (run-hooks 'cwarn-mode-hook)))
|
|
238
|
|
239 ;;;###autoload
|
|
240 (defun turn-on-cwarn-mode ()
|
|
241 "Turn on CWarn mode.
|
|
242
|
|
243 This function is designed to be added to hooks, for example:
|
|
244 (add-hook 'c-mode-hook 'turn-on-cwarn-mode)"
|
|
245 (cwarn-mode 1))
|
|
246
|
|
247 ;;;###autoload
|
|
248 (defun global-cwarn-mode (&optional arg)
|
|
249 "Hightlight suspicious C and C++ constructions in all buffers.
|
|
250
|
|
251 With ARG, turn CWarn mode on globally if and only if arg is positive."
|
|
252 (interactive "P")
|
|
253 (let ((old-global-cwarn-mode global-cwarn-mode))
|
|
254 (setq global-cwarn-mode
|
|
255 (if (null arg)
|
|
256 (not global-cwarn-mode)
|
|
257 (> (prefix-numeric-value arg) 0)))
|
|
258 (if (and cwarn-verbose
|
|
259 (interactive-p))
|
|
260 (message "Global CWarn mode is now %s."
|
|
261 (if global-cwarn-mode "on" "off")))
|
|
262 (when (not (eq global-cwarn-mode old-global-cwarn-mode))
|
|
263 ;; Update for all future buffers.
|
|
264 (dolist (conf cwarn-configuration)
|
|
265 (if global-cwarn-mode
|
|
266 (cwarn-font-lock-add-keywords (car conf))
|
|
267 (cwarn-font-lock-remove-keywords (car conf))))
|
|
268 ;; Update all existing buffers.
|
|
269 (save-excursion
|
|
270 (dolist (buffer (buffer-list))
|
|
271 (set-buffer buffer)
|
|
272 ;; Update keywords in alive buffers.
|
|
273 (when (and font-lock-mode
|
|
274 (not cwarn-mode)
|
|
275 (cwarn-is-enabled major-mode))
|
|
276 (if global-cwarn-mode
|
|
277 (cwarn-font-lock-add-keywords)
|
|
278 (cwarn-font-lock-remove-keywords))
|
|
279 (font-lock-fontify-buffer))))))
|
|
280 ;; Kills all added keywords :-(
|
|
281 ;; (font-lock-mode 0)
|
|
282 ;; (makunbound 'font-lock-keywords)
|
|
283 ;; (font-lock-mode 1))))
|
|
284 (when global-cwarn-mode
|
|
285 (run-hooks 'global-cwarn-mode-hook)))
|
|
286
|
|
287 ;;}}}
|
|
288 ;;{{{ Help functions
|
|
289
|
|
290 (defun cwarn-is-enabled (mode &optional feature)
|
|
291 "Non-nil if CWarn FEATURE is enabled for MODE.
|
|
292 feature is an atom representing one construction to highlight.
|
|
293
|
|
294 Check if any feature is enabled for MODE if no feature is specified.
|
|
295
|
|
296 The valid features are described by the variable
|
|
297 `cwarn-font-lock-feature-keywords-alist'."
|
|
298 (let ((mode-configuraion (assq mode cwarn-configuration)))
|
|
299 (and mode-configuraion
|
|
300 (or (null feature)
|
|
301 (let ((list-or-t (nth 1 mode-configuraion)))
|
|
302 (or (eq list-or-t t)
|
|
303 (if (eq (car-safe list-or-t) 'not)
|
|
304 (not (memq feature (cdr list-or-t)))
|
|
305 (memq feature list-or-t))))))))
|
|
306
|
|
307 (defun cwarn-inside-macro ()
|
|
308 "True if point is inside a C macro definition."
|
|
309 (save-excursion
|
|
310 (beginning-of-line)
|
|
311 (while (eq (char-before (1- (point))) ?\\)
|
|
312 (forward-line -1))
|
|
313 (back-to-indentation)
|
|
314 (eq (char-after) ?#)))
|
|
315
|
|
316 (defun cwarn-font-lock-add-keywords (&optional mode)
|
|
317 "Install keywords into major MODE, or into current buffer if nil."
|
|
318 (dolist (pair cwarn-font-lock-feature-keywords-alist)
|
|
319 (let ((feature (car pair))
|
|
320 (keywords (cdr pair)))
|
|
321 (if (not (listp keywords))
|
|
322 (setq keywords (symbol-value keywords)))
|
|
323 (if (cwarn-is-enabled (or mode major-mode) feature)
|
|
324 (font-lock-add-keywords mode keywords)))))
|
|
325
|
|
326 (defun cwarn-font-lock-remove-keywords (&optional mode)
|
|
327 "Remove keywords from major MODE, or from current buffer if nil."
|
|
328 (dolist (pair cwarn-font-lock-feature-keywords-alist)
|
|
329 (let ((feature (car pair))
|
|
330 (keywords (cdr pair)))
|
|
331 (if (not (listp keywords))
|
|
332 (setq keywords (symbol-value keywords)))
|
|
333 (if (cwarn-is-enabled (or mode major-mode) feature)
|
|
334 (font-lock-remove-keywords mode keywords)))))
|
|
335
|
|
336 ;;}}}
|
|
337 ;;{{{ Backward compatibility
|
|
338
|
|
339 ;; This piece of code will be part of CC mode as of Emacs 20.4.
|
|
340 (if (not (fboundp 'c-at-toplevel-p))
|
|
341 (defun c-at-toplevel-p ()
|
|
342 "Return a determination as to whether point is at the `top-level'.
|
|
343 Being at the top-level means that point is either outside any
|
|
344 enclosing block (such function definition), or inside a class
|
|
345 definition, but outside any method blocks.
|
|
346
|
|
347 If point is not at the top-level (e.g. it is inside a method
|
|
348 definition), then nil is returned. Otherwise, if point is at a
|
|
349 top-level not enclosed within a class definition, t is returned.
|
|
350 Otherwise, a 2-vector is returned where the zeroth element is the
|
|
351 buffer position of the start of the class declaration, and the first
|
|
352 element is the buffer position of the enclosing class's opening
|
|
353 brace."
|
|
354 (let ((state (c-parse-state)))
|
|
355 (or (not (c-most-enclosing-brace state))
|
|
356 (c-search-uplist-for-classkey state))))
|
|
357 )
|
|
358
|
|
359 ;;}}}
|
|
360 ;;{{{ Font-lock keywords and match functions
|
|
361
|
|
362 ;; This section contains font-lock keywords. A font lock keyword can
|
|
363 ;; either contain a regular expression or a match function. All
|
|
364 ;; keywords defined here use match functions since the C and C++
|
|
365 ;; constructions highlighted by CWarn are too complex to be matched by
|
|
366 ;; regular expressions.
|
|
367 ;;
|
|
368 ;; A match function should act like a normal forward search. They
|
|
369 ;; should return non-nil if they found a candidate and the match data
|
|
370 ;; should correspond to the highlight part of the font-lock keyword.
|
|
371 ;; The functions shold not generate errors, in that case font-lock
|
|
372 ;; will fail to highlight the buffer. A match function takes one
|
|
373 ;; argument, LIMIT, that represent the end of area to be searched.
|
|
374 ;;
|
|
375 ;; The variable `cwarn-font-lock-feature-keywords-alist' contains a
|
|
376 ;; mapping from CWarn features to the font-lock keywords defined
|
|
377 ;; below.
|
|
378
|
|
379 ;;{{{ Assignment in expressions
|
|
380
|
|
381 (defconst cwarn-font-lock-assignment-keywords
|
|
382 '((cwarn-font-lock-match-assignment-in-expression
|
|
383 (1 font-lock-warning-face))))
|
|
384
|
|
385 (defun cwarn-font-lock-match-assignment-in-expression (limit)
|
|
386 "Match assignments inside expressions."
|
|
387 (let ((res nil))
|
|
388 (while
|
|
389 (progn
|
|
390 (setq res (re-search-forward "[^!<>=]\\(=\\)[^=]" limit t))
|
|
391 (and res
|
|
392 (save-excursion
|
|
393 (goto-char (match-beginning 1))
|
|
394 (condition-case nil ; In case "backward-up-list" barfs.
|
|
395 (progn
|
|
396 (backward-up-list 1)
|
|
397 (or (not (memq (following-char) '(?\( ?\[)))
|
|
398 (save-match-data
|
|
399 (skip-chars-backward " ")
|
|
400 (skip-chars-backward "a-zA-Z0-9_")
|
|
401 (or
|
|
402 ;; Default parameter of function.
|
|
403 (c-at-toplevel-p)
|
|
404 (looking-at "for\\>")))))
|
|
405 (error t))))))
|
|
406 res))
|
|
407
|
|
408 ;;}}}
|
|
409 ;;{{{ Semicolon
|
|
410
|
|
411 (defconst cwarn-font-lock-semicolon-keywords
|
|
412 '((cwarn-font-lock-match-dangerous-semicolon (0 font-lock-warning-face))))
|
|
413
|
|
414 (defun cwarn-font-lock-match-dangerous-semicolon (limit)
|
|
415 "Match semicolons directly after `for', `while', and `if'.
|
|
416 Tne semicolon after a `do { ... } while (x);' construction is not matched."
|
|
417 (let ((res nil))
|
|
418 (while
|
|
419 (progn
|
|
420 (setq res (search-forward ";" limit t))
|
|
421 (and res
|
|
422 (save-excursion
|
|
423 (condition-case nil ; In case something barfs.
|
|
424 (save-match-data
|
|
425 (backward-sexp 2) ; Expression and keyword.
|
|
426 (not (or (looking-at "\\(for\\|if\\)\\>")
|
|
427 (and (looking-at "while\\>")
|
|
428 (condition-case nil
|
|
429 (progn
|
|
430 (backward-sexp 2) ; Body and "do".
|
|
431 (not (looking-at "do\\>")))
|
|
432 (error t))))))
|
|
433 (error t))))))
|
|
434 res))
|
|
435
|
|
436 ;;}}}
|
|
437 ;;{{{ Reference
|
|
438
|
|
439 (defconst cwarn-font-lock-reference-keywords
|
|
440 '((cwarn-font-lock-match-reference (1 font-lock-warning-face))))
|
|
441
|
|
442 (defun cwarn-font-lock-match-reference (limit)
|
|
443 "Font-lock matcher for C++ reference parameters."
|
|
444 (let ((res nil))
|
|
445 (while
|
|
446 (progn
|
|
447 (setq res (re-search-forward "[^&]\\(&\\)[^&=]" limit t))
|
|
448 (and res
|
|
449 (save-excursion
|
|
450 (goto-char (match-beginning 1))
|
|
451 (condition-case nil ; In case something barfs.
|
|
452 (save-match-data
|
|
453 (backward-up-list 1)
|
|
454 (or (not (eq (following-char) ?\())
|
|
455 (cwarn-inside-macro)
|
|
456 (not (c-at-toplevel-p))))
|
|
457 (error t))))))
|
|
458 res))
|
|
459
|
|
460 ;;}}}
|
|
461
|
|
462 ;;}}}
|
|
463 ;;{{{ The end
|
|
464
|
|
465 (unless (assq 'cwarn-mode minor-mode-alist)
|
|
466 (push '(cwarn-mode cwarn-mode-text)
|
|
467 minor-mode-alist))
|
|
468 (unless (assq 'global-cwarn-mode minor-mode-alist)
|
|
469 (push '(global-cwarn-mode global-cwarn-mode-text)
|
|
470 minor-mode-alist))
|
|
471
|
|
472 (provide 'cwarn)
|
|
473
|
|
474 (run-hooks 'cwarn-load-hook)
|
|
475
|
|
476 ;; This makes it possible to set Global CWarn mode from
|
|
477 ;; Customize.
|
|
478 (if global-cwarn-mode
|
|
479 (global-cwarn-mode 1))
|
|
480
|
|
481 ;;}}}
|
|
482
|
|
483 ;;; cwarn.el ends here
|