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