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