Mercurial > emacs
annotate lisp/progmodes/cwarn.el @ 31883:c2b1c16d926c
*** empty log message ***
author | Gerd Moellmann <gerd@gnu.org> |
---|---|
date | Mon, 25 Sep 2000 15:42:25 +0000 |
parents | 182986c3d2fd |
children | b174db545cfd |
rev | line source |
---|---|
26963 | 1 ;;; cwarn.el --- Highlight suspicious C and C++ constructions. |
2 | |
30812 | 3 ;; Copyright (C) 1999, 2000 Free Software Foundation, Inc. |
26963 | 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." | |
30811
2ae4e53657a3
(cwarn) <defgroup>: Add :version, :link.
Dave Love <fx@gnu.org>
parents:
26963
diff
changeset
|
121 :version "21.1" |
2ae4e53657a3
(cwarn) <defgroup>: Add :version, :link.
Dave Love <fx@gnu.org>
parents:
26963
diff
changeset
|
122 :link '(url-link "http://www.andersl.com/emacs") |
26963 | 123 :group 'faces) |
124 | |
125 (defvar cwarn-mode nil | |
126 "*Non-nil when Cwarn mode is active. | |
127 | |
128 Never set this variable directly, use the command `cwarn-mode' | |
129 instead.") | |
130 | |
131 (defcustom global-cwarn-mode nil | |
30811
2ae4e53657a3
(cwarn) <defgroup>: Add :version, :link.
Dave Love <fx@gnu.org>
parents:
26963
diff
changeset
|
132 "When on, suspicious C and C++ constructions are highlighted. |
26963 | 133 |
134 Set this variable using \\[customize] or use the command | |
135 `global-cwarn-mode'." | |
136 :group 'cwarn | |
137 :initialize 'custom-initialize-default | |
30811
2ae4e53657a3
(cwarn) <defgroup>: Add :version, :link.
Dave Love <fx@gnu.org>
parents:
26963
diff
changeset
|
138 :set (lambda (symbol value) |
2ae4e53657a3
(cwarn) <defgroup>: Add :version, :link.
Dave Love <fx@gnu.org>
parents:
26963
diff
changeset
|
139 (global-cwarn-mode (or value 0))) |
26963 | 140 :type 'boolean |
141 :require 'cwarn) | |
142 | |
143 (defcustom cwarn-configuration | |
144 '((c-mode (not reference)) | |
145 (c++-mode t)) | |
146 "*List of items each describing which features are enable for a mode. | |
147 Each item is on the form (mode featurelist), where featurelist can be | |
148 on one of three forms: | |
149 | |
150 * A list of enabled features. | |
151 * A list starting with the atom `not' followed by the features | |
152 which are not enabled. | |
153 * The atom t, that represent that all features are enabled. | |
154 | |
155 See variable `cwarn-font-lock-feature-keywords-alist' for available | |
156 features." | |
30811
2ae4e53657a3
(cwarn) <defgroup>: Add :version, :link.
Dave Love <fx@gnu.org>
parents:
26963
diff
changeset
|
157 :type '(repeat sexp) |
26963 | 158 :group 'cwarn) |
159 | |
160 (defcustom cwarn-font-lock-feature-keywords-alist | |
161 '((assign . cwarn-font-lock-assignment-keywords) | |
162 (semicolon . cwarn-font-lock-semicolon-keywords) | |
163 (reference . cwarn-font-lock-reference-keywords)) | |
30811
2ae4e53657a3
(cwarn) <defgroup>: Add :version, :link.
Dave Love <fx@gnu.org>
parents:
26963
diff
changeset
|
164 "An alist mapping a CWarn feature to font-lock keywords. |
26963 | 165 The keywords could either a font-lock keyword list or a symbol. |
166 If it is a symbol it is assumed to be a variable containing a font-lock | |
167 keyword list." | |
30811
2ae4e53657a3
(cwarn) <defgroup>: Add :version, :link.
Dave Love <fx@gnu.org>
parents:
26963
diff
changeset
|
168 :type '(alist :key-type (choice (const assign) |
2ae4e53657a3
(cwarn) <defgroup>: Add :version, :link.
Dave Love <fx@gnu.org>
parents:
26963
diff
changeset
|
169 (const semicolon) |
2ae4e53657a3
(cwarn) <defgroup>: Add :version, :link.
Dave Love <fx@gnu.org>
parents:
26963
diff
changeset
|
170 (const reference)) |
2ae4e53657a3
(cwarn) <defgroup>: Add :version, :link.
Dave Love <fx@gnu.org>
parents:
26963
diff
changeset
|
171 :value-type face) |
26963 | 172 :group 'cwarn) |
173 | |
174 (defcustom cwarn-verbose t | |
175 "*When nil, CWarn mode will not generate any messages. | |
176 | |
177 Currently, messages are generated when the mode is activated and | |
178 deactivated." | |
179 :group 'cwarn | |
180 :type 'boolean) | |
181 | |
182 (defcustom cwarn-mode-text " CWarn" | |
183 "*String to display in the mode line when CWarn mode is active. | |
184 | |
185 \(When the string is not empty, make sure that it has a leading space.)" | |
186 :tag "CWarn mode text" ; To separate it from `global-...' | |
187 :group 'cwarn | |
188 :type 'string) | |
189 | |
190 (defcustom cwarn-mode-hook nil | |
191 "*Functions to run when CWarn mode is activated." | |
192 :tag "CWarn mode hook" ; To separate it from `global-...' | |
193 :group 'cwarn | |
194 :type 'hook) | |
195 | |
196 (defcustom global-cwarn-mode-text "" | |
197 "*String to display when Global CWarn mode is active. | |
198 | |
199 The default is nothing since when this mode is active this text doesn't | |
200 vary over time, or between buffers. Hence mode line text | |
201 would only waste precious space." | |
202 :group 'cwarn | |
203 :type 'string) | |
204 | |
205 (defcustom global-cwarn-mode-hook nil | |
206 "*Hook called when Global CWarn mode is activated." | |
207 :group 'cwarn | |
208 :type 'hook) | |
209 | |
210 (defcustom cwarn-load-hook nil | |
211 "*Functions to run when CWarn mode is first loaded." | |
212 :tag "Load Hook" | |
213 :group 'cwarn | |
214 :type 'hook) | |
215 | |
216 ;;}}} | |
217 ;;{{{ The modes | |
218 | |
219 ;;;###autoload | |
220 (defun cwarn-mode (&optional arg) | |
30811
2ae4e53657a3
(cwarn) <defgroup>: Add :version, :link.
Dave Love <fx@gnu.org>
parents:
26963
diff
changeset
|
221 "Minor mode that highlights suspicious C and C++ constructions. |
26963 | 222 |
223 Note, in addition to enabling this minor mode, the major mode must | |
224 be included in the variable `cwarn-configuration'. By default C and | |
225 C++ modes are included. | |
226 | |
227 With ARG, turn CWarn mode on if and only if arg is positive." | |
228 (interactive "P") | |
229 (make-local-variable 'cwarn-mode) | |
230 (setq cwarn-mode | |
231 (if (null arg) | |
232 (not cwarn-mode) | |
233 (> (prefix-numeric-value arg) 0))) | |
234 (if (and cwarn-verbose | |
235 (interactive-p)) | |
236 (message "Cwarn mode is now %s." | |
237 (if cwarn-mode "on" "off"))) | |
238 (if (not global-cwarn-mode) | |
239 (if cwarn-mode | |
240 (cwarn-font-lock-add-keywords) | |
241 (cwarn-font-lock-remove-keywords))) | |
242 (font-lock-fontify-buffer) | |
243 (if cwarn-mode | |
244 (run-hooks 'cwarn-mode-hook))) | |
245 | |
246 ;;;###autoload | |
247 (defun turn-on-cwarn-mode () | |
248 "Turn on CWarn mode. | |
249 | |
250 This function is designed to be added to hooks, for example: | |
251 (add-hook 'c-mode-hook 'turn-on-cwarn-mode)" | |
252 (cwarn-mode 1)) | |
253 | |
254 ;;;###autoload | |
255 (defun global-cwarn-mode (&optional arg) | |
256 "Hightlight suspicious C and C++ constructions in all buffers. | |
257 | |
258 With ARG, turn CWarn mode on globally if and only if arg is positive." | |
259 (interactive "P") | |
260 (let ((old-global-cwarn-mode global-cwarn-mode)) | |
261 (setq global-cwarn-mode | |
262 (if (null arg) | |
263 (not global-cwarn-mode) | |
264 (> (prefix-numeric-value arg) 0))) | |
265 (if (and cwarn-verbose | |
266 (interactive-p)) | |
267 (message "Global CWarn mode is now %s." | |
268 (if global-cwarn-mode "on" "off"))) | |
269 (when (not (eq global-cwarn-mode old-global-cwarn-mode)) | |
270 ;; Update for all future buffers. | |
271 (dolist (conf cwarn-configuration) | |
272 (if global-cwarn-mode | |
273 (cwarn-font-lock-add-keywords (car conf)) | |
274 (cwarn-font-lock-remove-keywords (car conf)))) | |
275 ;; Update all existing buffers. | |
276 (save-excursion | |
277 (dolist (buffer (buffer-list)) | |
278 (set-buffer buffer) | |
279 ;; Update keywords in alive buffers. | |
280 (when (and font-lock-mode | |
281 (not cwarn-mode) | |
282 (cwarn-is-enabled major-mode)) | |
283 (if global-cwarn-mode | |
284 (cwarn-font-lock-add-keywords) | |
285 (cwarn-font-lock-remove-keywords)) | |
286 (font-lock-fontify-buffer)))))) | |
287 ;; Kills all added keywords :-( | |
288 ;; (font-lock-mode 0) | |
289 ;; (makunbound 'font-lock-keywords) | |
290 ;; (font-lock-mode 1)))) | |
291 (when global-cwarn-mode | |
292 (run-hooks 'global-cwarn-mode-hook))) | |
293 | |
294 ;;}}} | |
295 ;;{{{ Help functions | |
296 | |
297 (defun cwarn-is-enabled (mode &optional feature) | |
298 "Non-nil if CWarn FEATURE is enabled for MODE. | |
299 feature is an atom representing one construction to highlight. | |
300 | |
301 Check if any feature is enabled for MODE if no feature is specified. | |
302 | |
303 The valid features are described by the variable | |
304 `cwarn-font-lock-feature-keywords-alist'." | |
305 (let ((mode-configuraion (assq mode cwarn-configuration))) | |
306 (and mode-configuraion | |
307 (or (null feature) | |
308 (let ((list-or-t (nth 1 mode-configuraion))) | |
309 (or (eq list-or-t t) | |
310 (if (eq (car-safe list-or-t) 'not) | |
311 (not (memq feature (cdr list-or-t))) | |
312 (memq feature list-or-t)))))))) | |
313 | |
314 (defun cwarn-inside-macro () | |
315 "True if point is inside a C macro definition." | |
316 (save-excursion | |
317 (beginning-of-line) | |
318 (while (eq (char-before (1- (point))) ?\\) | |
319 (forward-line -1)) | |
320 (back-to-indentation) | |
321 (eq (char-after) ?#))) | |
322 | |
323 (defun cwarn-font-lock-add-keywords (&optional mode) | |
324 "Install keywords into major MODE, or into current buffer if nil." | |
325 (dolist (pair cwarn-font-lock-feature-keywords-alist) | |
326 (let ((feature (car pair)) | |
327 (keywords (cdr pair))) | |
328 (if (not (listp keywords)) | |
329 (setq keywords (symbol-value keywords))) | |
330 (if (cwarn-is-enabled (or mode major-mode) feature) | |
331 (font-lock-add-keywords mode keywords))))) | |
332 | |
333 (defun cwarn-font-lock-remove-keywords (&optional mode) | |
334 "Remove keywords from major MODE, or from current buffer if nil." | |
335 (dolist (pair cwarn-font-lock-feature-keywords-alist) | |
336 (let ((feature (car pair)) | |
337 (keywords (cdr pair))) | |
338 (if (not (listp keywords)) | |
339 (setq keywords (symbol-value keywords))) | |
340 (if (cwarn-is-enabled (or mode major-mode) feature) | |
341 (font-lock-remove-keywords mode keywords))))) | |
342 | |
343 ;;}}} | |
344 ;;{{{ Backward compatibility | |
345 | |
346 ;; This piece of code will be part of CC mode as of Emacs 20.4. | |
347 (if (not (fboundp 'c-at-toplevel-p)) | |
348 (defun c-at-toplevel-p () | |
349 "Return a determination as to whether point is at the `top-level'. | |
350 Being at the top-level means that point is either outside any | |
351 enclosing block (such function definition), or inside a class | |
352 definition, but outside any method blocks. | |
353 | |
354 If point is not at the top-level (e.g. it is inside a method | |
355 definition), then nil is returned. Otherwise, if point is at a | |
356 top-level not enclosed within a class definition, t is returned. | |
357 Otherwise, a 2-vector is returned where the zeroth element is the | |
358 buffer position of the start of the class declaration, and the first | |
359 element is the buffer position of the enclosing class's opening | |
360 brace." | |
361 (let ((state (c-parse-state))) | |
362 (or (not (c-most-enclosing-brace state)) | |
363 (c-search-uplist-for-classkey state)))) | |
364 ) | |
365 | |
366 ;;}}} | |
367 ;;{{{ Font-lock keywords and match functions | |
368 | |
369 ;; This section contains font-lock keywords. A font lock keyword can | |
370 ;; either contain a regular expression or a match function. All | |
371 ;; keywords defined here use match functions since the C and C++ | |
372 ;; constructions highlighted by CWarn are too complex to be matched by | |
373 ;; regular expressions. | |
374 ;; | |
375 ;; A match function should act like a normal forward search. They | |
376 ;; should return non-nil if they found a candidate and the match data | |
377 ;; should correspond to the highlight part of the font-lock keyword. | |
378 ;; The functions shold not generate errors, in that case font-lock | |
379 ;; will fail to highlight the buffer. A match function takes one | |
380 ;; argument, LIMIT, that represent the end of area to be searched. | |
381 ;; | |
382 ;; The variable `cwarn-font-lock-feature-keywords-alist' contains a | |
383 ;; mapping from CWarn features to the font-lock keywords defined | |
384 ;; below. | |
385 | |
386 ;;{{{ Assignment in expressions | |
387 | |
388 (defconst cwarn-font-lock-assignment-keywords | |
389 '((cwarn-font-lock-match-assignment-in-expression | |
390 (1 font-lock-warning-face)))) | |
391 | |
392 (defun cwarn-font-lock-match-assignment-in-expression (limit) | |
393 "Match assignments inside expressions." | |
394 (let ((res nil)) | |
395 (while | |
396 (progn | |
397 (setq res (re-search-forward "[^!<>=]\\(=\\)[^=]" limit t)) | |
398 (and res | |
399 (save-excursion | |
400 (goto-char (match-beginning 1)) | |
401 (condition-case nil ; In case "backward-up-list" barfs. | |
402 (progn | |
403 (backward-up-list 1) | |
404 (or (not (memq (following-char) '(?\( ?\[))) | |
405 (save-match-data | |
406 (skip-chars-backward " ") | |
407 (skip-chars-backward "a-zA-Z0-9_") | |
408 (or | |
409 ;; Default parameter of function. | |
410 (c-at-toplevel-p) | |
411 (looking-at "for\\>"))))) | |
412 (error t)))))) | |
413 res)) | |
414 | |
415 ;;}}} | |
416 ;;{{{ Semicolon | |
417 | |
418 (defconst cwarn-font-lock-semicolon-keywords | |
419 '((cwarn-font-lock-match-dangerous-semicolon (0 font-lock-warning-face)))) | |
420 | |
421 (defun cwarn-font-lock-match-dangerous-semicolon (limit) | |
422 "Match semicolons directly after `for', `while', and `if'. | |
423 Tne semicolon after a `do { ... } while (x);' construction is not matched." | |
424 (let ((res nil)) | |
425 (while | |
426 (progn | |
427 (setq res (search-forward ";" limit t)) | |
428 (and res | |
429 (save-excursion | |
430 (condition-case nil ; In case something barfs. | |
431 (save-match-data | |
432 (backward-sexp 2) ; Expression and keyword. | |
433 (not (or (looking-at "\\(for\\|if\\)\\>") | |
434 (and (looking-at "while\\>") | |
435 (condition-case nil | |
436 (progn | |
437 (backward-sexp 2) ; Body and "do". | |
438 (not (looking-at "do\\>"))) | |
439 (error t)))))) | |
440 (error t)))))) | |
441 res)) | |
442 | |
443 ;;}}} | |
444 ;;{{{ Reference | |
445 | |
446 (defconst cwarn-font-lock-reference-keywords | |
447 '((cwarn-font-lock-match-reference (1 font-lock-warning-face)))) | |
448 | |
449 (defun cwarn-font-lock-match-reference (limit) | |
450 "Font-lock matcher for C++ reference parameters." | |
451 (let ((res nil)) | |
452 (while | |
453 (progn | |
454 (setq res (re-search-forward "[^&]\\(&\\)[^&=]" limit t)) | |
455 (and res | |
456 (save-excursion | |
457 (goto-char (match-beginning 1)) | |
458 (condition-case nil ; In case something barfs. | |
459 (save-match-data | |
460 (backward-up-list 1) | |
461 (or (not (eq (following-char) ?\()) | |
462 (cwarn-inside-macro) | |
463 (not (c-at-toplevel-p)))) | |
464 (error t)))))) | |
465 res)) | |
466 | |
467 ;;}}} | |
468 | |
469 ;;}}} | |
470 ;;{{{ The end | |
471 | |
472 (unless (assq 'cwarn-mode minor-mode-alist) | |
473 (push '(cwarn-mode cwarn-mode-text) | |
474 minor-mode-alist)) | |
475 (unless (assq 'global-cwarn-mode minor-mode-alist) | |
476 (push '(global-cwarn-mode global-cwarn-mode-text) | |
477 minor-mode-alist)) | |
478 | |
479 (provide 'cwarn) | |
480 | |
481 (run-hooks 'cwarn-load-hook) | |
482 | |
483 ;; This makes it possible to set Global CWarn mode from | |
484 ;; Customize. | |
485 (if global-cwarn-mode | |
486 (global-cwarn-mode 1)) | |
487 | |
488 ;;}}} | |
489 | |
490 ;;; cwarn.el ends here |