Add arch taglines
[bpt/emacs.git] / lisp / progmodes / cwarn.el
CommitLineData
e8af40ee 1;;; cwarn.el --- highlight suspicious C and C++ constructions
2936437d 2
04808157 3;; Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
2936437d
GM
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
0b05c8ca 42;; probably meant to use the comparison operator "==".
2936437d
GM
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++
0b05c8ca 58;; reference parameters) are considered errors by the language
2936437d
GM
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,
0b05c8ca 98;; repeat the operations that triggered the error and include
2936437d
GM
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."
9a513aba
DL
121 :version "21.1"
122 :link '(url-link "http://www.andersl.com/emacs")
2936437d
GM
123 :group 'faces)
124
125(defvar cwarn-mode nil
126 "*Non-nil when Cwarn mode is active.
127
128Never set this variable directly, use the command `cwarn-mode'
129instead.")
130
2936437d
GM
131(defcustom cwarn-configuration
132 '((c-mode (not reference))
133 (c++-mode t))
134 "*List of items each describing which features are enable for a mode.
135Each item is on the form (mode featurelist), where featurelist can be
136on one of three forms:
137
138* A list of enabled features.
139* A list starting with the atom `not' followed by the features
140 which are not enabled.
141* The atom t, that represent that all features are enabled.
142
143See variable `cwarn-font-lock-feature-keywords-alist' for available
144features."
9a513aba 145 :type '(repeat sexp)
2936437d
GM
146 :group 'cwarn)
147
148(defcustom cwarn-font-lock-feature-keywords-alist
149 '((assign . cwarn-font-lock-assignment-keywords)
150 (semicolon . cwarn-font-lock-semicolon-keywords)
151 (reference . cwarn-font-lock-reference-keywords))
9a513aba 152 "An alist mapping a CWarn feature to font-lock keywords.
2936437d
GM
153The keywords could either a font-lock keyword list or a symbol.
154If it is a symbol it is assumed to be a variable containing a font-lock
155keyword list."
9a513aba
DL
156 :type '(alist :key-type (choice (const assign)
157 (const semicolon)
158 (const reference))
04808157 159 :value-type (sexp :tag "Value"))
2936437d
GM
160 :group 'cwarn)
161
162(defcustom cwarn-verbose t
163 "*When nil, CWarn mode will not generate any messages.
164
165Currently, messages are generated when the mode is activated and
166deactivated."
167 :group 'cwarn
168 :type 'boolean)
169
170(defcustom cwarn-mode-text " CWarn"
171 "*String to display in the mode line when CWarn mode is active.
172
173\(When the string is not empty, make sure that it has a leading space.)"
174 :tag "CWarn mode text" ; To separate it from `global-...'
175 :group 'cwarn
176 :type 'string)
177
2936437d
GM
178(defcustom cwarn-load-hook nil
179 "*Functions to run when CWarn mode is first loaded."
180 :tag "Load Hook"
181 :group 'cwarn
182 :type 'hook)
183
184;;}}}
185;;{{{ The modes
186
187;;;###autoload
0b05c8ca 188(define-minor-mode cwarn-mode
9a513aba 189 "Minor mode that highlights suspicious C and C++ constructions.
2936437d
GM
190
191Note, in addition to enabling this minor mode, the major mode must
192be included in the variable `cwarn-configuration'. By default C and
193C++ modes are included.
194
195With ARG, turn CWarn mode on if and only if arg is positive."
0b05c8ca
SM
196 nil cwarn-mode-text nil
197 (cwarn-font-lock-keywords cwarn-mode)
198 (if font-lock-mode (font-lock-fontify-buffer)))
2936437d
GM
199
200;;;###autoload
201(defun turn-on-cwarn-mode ()
202 "Turn on CWarn mode.
203
204This function is designed to be added to hooks, for example:
205 (add-hook 'c-mode-hook 'turn-on-cwarn-mode)"
206 (cwarn-mode 1))
207
2936437d
GM
208;;}}}
209;;{{{ Help functions
210
211(defun cwarn-is-enabled (mode &optional feature)
212 "Non-nil if CWarn FEATURE is enabled for MODE.
213feature is an atom representing one construction to highlight.
214
215Check if any feature is enabled for MODE if no feature is specified.
216
217The valid features are described by the variable
218`cwarn-font-lock-feature-keywords-alist'."
219 (let ((mode-configuraion (assq mode cwarn-configuration)))
220 (and mode-configuraion
221 (or (null feature)
222 (let ((list-or-t (nth 1 mode-configuraion)))
223 (or (eq list-or-t t)
224 (if (eq (car-safe list-or-t) 'not)
225 (not (memq feature (cdr list-or-t)))
226 (memq feature list-or-t))))))))
227
228(defun cwarn-inside-macro ()
229 "True if point is inside a C macro definition."
230 (save-excursion
231 (beginning-of-line)
232 (while (eq (char-before (1- (point))) ?\\)
233 (forward-line -1))
234 (back-to-indentation)
235 (eq (char-after) ?#)))
236
0b05c8ca
SM
237(defun cwarn-font-lock-keywords (addp)
238 "Install/Remove keywords into current buffer.
239If ADDP is non-nil, install else remove."
2936437d
GM
240 (dolist (pair cwarn-font-lock-feature-keywords-alist)
241 (let ((feature (car pair))
242 (keywords (cdr pair)))
243 (if (not (listp keywords))
244 (setq keywords (symbol-value keywords)))
0b05c8ca
SM
245 (if (cwarn-is-enabled major-mode feature)
246 (funcall (if addp 'font-lock-add-keywords 'font-lock-remove-keywords)
247 nil keywords)))))
2936437d
GM
248
249;;}}}
250;;{{{ Backward compatibility
251
252;; This piece of code will be part of CC mode as of Emacs 20.4.
253(if (not (fboundp 'c-at-toplevel-p))
254(defun c-at-toplevel-p ()
255 "Return a determination as to whether point is at the `top-level'.
256Being at the top-level means that point is either outside any
257enclosing block (such function definition), or inside a class
258definition, but outside any method blocks.
259
260If point is not at the top-level (e.g. it is inside a method
261definition), then nil is returned. Otherwise, if point is at a
262top-level not enclosed within a class definition, t is returned.
263Otherwise, a 2-vector is returned where the zeroth element is the
264buffer position of the start of the class declaration, and the first
0b05c8ca 265element is the buffer position of the enclosing class' opening
2936437d
GM
266brace."
267 (let ((state (c-parse-state)))
268 (or (not (c-most-enclosing-brace state))
269 (c-search-uplist-for-classkey state))))
270)
271
272;;}}}
273;;{{{ Font-lock keywords and match functions
274
275;; This section contains font-lock keywords. A font lock keyword can
276;; either contain a regular expression or a match function. All
277;; keywords defined here use match functions since the C and C++
278;; constructions highlighted by CWarn are too complex to be matched by
279;; regular expressions.
280;;
281;; A match function should act like a normal forward search. They
282;; should return non-nil if they found a candidate and the match data
283;; should correspond to the highlight part of the font-lock keyword.
0b05c8ca 284;; The functions should not generate errors, in that case font-lock
2936437d
GM
285;; will fail to highlight the buffer. A match function takes one
286;; argument, LIMIT, that represent the end of area to be searched.
287;;
288;; The variable `cwarn-font-lock-feature-keywords-alist' contains a
289;; mapping from CWarn features to the font-lock keywords defined
290;; below.
291
0b05c8ca
SM
292(defmacro cwarn-font-lock-match (re &rest body)
293 "Match RE but only if BODY holds."
294 `(let ((res nil))
295 (while
296 (progn
297 (setq res (re-search-forward ,re limit t))
298 (and res
299 (save-excursion
300 (when (match-beginning 1) (goto-char (match-beginning 1)))
301 (condition-case nil ; In case something barfs.
302 (not (save-match-data
303 ,@body))
304 (error t))))))
305 res))
306
2936437d
GM
307;;{{{ Assignment in expressions
308
309(defconst cwarn-font-lock-assignment-keywords
310 '((cwarn-font-lock-match-assignment-in-expression
311 (1 font-lock-warning-face))))
312
313(defun cwarn-font-lock-match-assignment-in-expression (limit)
314 "Match assignments inside expressions."
0b05c8ca
SM
315 (cwarn-font-lock-match
316 "[^!<>=]\\(\\([-+*/%&^|]\\|<<\\|>>\\)?=\\)[^=]"
317 (backward-up-list 1)
318 (and (memq (following-char) '(?\( ?\[))
319 (not (progn
320 (skip-chars-backward " ")
321 (skip-chars-backward "a-zA-Z0-9_")
322 (or
323 ;; Default parameter of function.
324 (c-at-toplevel-p)
325 (looking-at "for\\>")))))))
2936437d
GM
326
327;;}}}
328;;{{{ Semicolon
329
330(defconst cwarn-font-lock-semicolon-keywords
331 '((cwarn-font-lock-match-dangerous-semicolon (0 font-lock-warning-face))))
332
333(defun cwarn-font-lock-match-dangerous-semicolon (limit)
334 "Match semicolons directly after `for', `while', and `if'.
0b05c8ca
SM
335The semicolon after a `do { ... } while (x);' construction is not matched."
336 (cwarn-font-lock-match
337 ";"
338 (backward-sexp 2) ; Expression and keyword.
339 (or (looking-at "\\(for\\|if\\)\\>")
340 (and (looking-at "while\\>")
341 (condition-case nil
342 (progn
343 (backward-sexp 2) ; Body and "do".
344 (not (looking-at "do\\>")))
345 (error t))))))
2936437d
GM
346
347;;}}}
348;;{{{ Reference
349
350(defconst cwarn-font-lock-reference-keywords
351 '((cwarn-font-lock-match-reference (1 font-lock-warning-face))))
352
353(defun cwarn-font-lock-match-reference (limit)
354 "Font-lock matcher for C++ reference parameters."
0b05c8ca
SM
355 (cwarn-font-lock-match
356 "[^&]\\(&\\)[^&=]"
357 (backward-up-list 1)
358 (and (eq (following-char) ?\()
359 (not (cwarn-inside-macro))
360 (c-at-toplevel-p))))
2936437d
GM
361
362;;}}}
363
364;;}}}
365;;{{{ The end
366
0b05c8ca
SM
367(defun turn-on-cwarn-mode-if-enabled ()
368 "Turn on CWarn mode in the current buffer if applicable.
369The mode is turned if some feature is enabled for the current
370`major-mode' in `cwarn-configuration'."
371 (if (cwarn-is-enabled major-mode) (turn-on-cwarn-mode)))
372
373;;;###autoload
374(easy-mmode-define-global-mode global-cwarn-mode cwarn-mode
375 turn-on-cwarn-mode-if-enabled)
2936437d
GM
376
377(provide 'cwarn)
378
379(run-hooks 'cwarn-load-hook)
380
2936437d
GM
381;;}}}
382
ab5796a9 383;;; arch-tag: 225fb5e2-0838-4eb1-88ce-3811c5e4d738
2936437d 384;;; cwarn.el ends here