Make c-mark-defun extend region when repeated, and leave a mark.
[bpt/emacs.git] / lisp / progmodes / cwarn.el
1 ;;; cwarn.el --- highlight suspicious C and C++ constructions
2
3 ;; Copyright (C) 1999-2012 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
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 3 of the License, or
15 ;; (at your option) 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. If not, see <http://www.gnu.org/licenses/>.
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
40 ;; probably meant to use the comparison operator "==".
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++
56 ;; reference parameters) are considered errors by the language
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,
96 ;; repeat the operations that triggered the error and include
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
108 (eval-when-compile (require 'cl))
109
110 (require 'custom)
111 (require 'font-lock)
112 (require 'cc-mode)
113
114 ;;}}}
115 ;;{{{ Variables
116
117 (defgroup cwarn nil
118 "Highlight suspicious C and C++ constructions."
119 :version "21.1"
120 :group 'faces)
121
122 (defvar cwarn-mode nil
123 "*Non-nil when Cwarn mode is active.
124
125 Never set this variable directly, use the command `cwarn-mode'
126 instead.")
127
128 (defcustom cwarn-configuration
129 '((c-mode (not reference))
130 (c++-mode t))
131 "List of items each describing which features are enable for a mode.
132 Each item is on the form (mode featurelist), where featurelist can be
133 on one of three forms:
134
135 * A list of enabled features.
136 * A list starting with the atom `not' followed by the features
137 which are not enabled.
138 * The atom t, that represent that all features are enabled.
139
140 See variable `cwarn-font-lock-feature-keywords-alist' for available
141 features."
142 :type '(repeat sexp)
143 :group 'cwarn)
144
145 (defcustom cwarn-font-lock-feature-keywords-alist
146 '((assign . cwarn-font-lock-assignment-keywords)
147 (semicolon . cwarn-font-lock-semicolon-keywords)
148 (reference . cwarn-font-lock-reference-keywords))
149 "An alist mapping a CWarn feature to font-lock keywords.
150 The keywords could either a font-lock keyword list or a symbol.
151 If it is a symbol it is assumed to be a variable containing a font-lock
152 keyword list."
153 :type '(alist :key-type (choice (const assign)
154 (const semicolon)
155 (const reference))
156 :value-type (sexp :tag "Value"))
157 :group 'cwarn)
158
159 (defcustom cwarn-verbose t
160 "When nil, CWarn mode will not generate any messages.
161
162 Currently, messages are generated when the mode is activated and
163 deactivated."
164 :group 'cwarn
165 :type 'boolean)
166
167 (defcustom cwarn-mode-text " CWarn"
168 "String to display in the mode line when CWarn mode is active.
169
170 \(When the string is not empty, make sure that it has a leading space.)"
171 :tag "CWarn mode text" ; To separate it from `global-...'
172 :group 'cwarn
173 :type 'string)
174
175 (defcustom cwarn-load-hook nil
176 "Functions to run when CWarn mode is first loaded."
177 :tag "Load Hook"
178 :group 'cwarn
179 :type 'hook)
180
181 ;;}}}
182 ;;{{{ The modes
183
184 ;;;###autoload
185 (define-minor-mode cwarn-mode
186 "Minor mode that highlights suspicious C and C++ constructions.
187
188 Suspicious constructs are highlighted using `font-lock-warning-face'.
189
190 Note, in addition to enabling this minor mode, the major mode must
191 be included in the variable `cwarn-configuration'. By default C and
192 C++ modes are included.
193
194 With a prefix argument ARG, enable the mode if ARG is positive,
195 and disable it otherwise. If called from Lisp, enable the mode
196 if ARG is omitted or nil."
197 :group 'cwarn :lighter cwarn-mode-text
198 (cwarn-font-lock-keywords cwarn-mode)
199 (if font-lock-mode (font-lock-fontify-buffer)))
200
201 ;;;###autoload
202 (defun turn-on-cwarn-mode ()
203 "Turn on CWarn mode.
204
205 This function is designed to be added to hooks, for example:
206 (add-hook 'c-mode-hook 'turn-on-cwarn-mode)"
207 (cwarn-mode 1))
208 (make-obsolete 'turn-on-cwarn-mode 'cwarn-mode "24.1")
209
210 ;;}}}
211 ;;{{{ Help functions
212
213 (defun cwarn-is-enabled (mode &optional feature)
214 "Non-nil if CWarn FEATURE is enabled for MODE.
215 FEATURE is an atom representing one construction to highlight.
216
217 Check if any feature is enabled for MODE if no feature is specified.
218
219 The valid features are described by the variable
220 `cwarn-font-lock-feature-keywords-alist'."
221 (let ((mode-configuration (assq mode cwarn-configuration)))
222 (and mode-configuration
223 (or (null feature)
224 (let ((list-or-t (nth 1 mode-configuration)))
225 (or (eq list-or-t t)
226 (if (eq (car-safe list-or-t) 'not)
227 (not (memq feature (cdr list-or-t)))
228 (memq feature list-or-t))))))))
229
230 (defun cwarn-inside-macro ()
231 "True if point is inside a C macro definition."
232 (save-excursion
233 (beginning-of-line)
234 (while (eq (char-before (1- (point))) ?\\)
235 (forward-line -1))
236 (back-to-indentation)
237 (eq (char-after) ?#)))
238
239 (defun cwarn-font-lock-keywords (addp)
240 "Install/remove keywords into current buffer.
241 If ADDP is non-nil, install else remove."
242 (dolist (pair cwarn-font-lock-feature-keywords-alist)
243 (let ((feature (car pair))
244 (keywords (cdr pair)))
245 (if (not (listp keywords))
246 (setq keywords (symbol-value keywords)))
247 (if (cwarn-is-enabled major-mode feature)
248 (funcall (if addp 'font-lock-add-keywords 'font-lock-remove-keywords)
249 nil keywords)))))
250
251 ;;}}}
252 ;;{{{ Font-lock keywords and match functions
253
254 ;; This section contains font-lock keywords. A font lock keyword can
255 ;; either contain a regular expression or a match function. All
256 ;; keywords defined here use match functions since the C and C++
257 ;; constructions highlighted by CWarn are too complex to be matched by
258 ;; regular expressions.
259 ;;
260 ;; A match function should act like a normal forward search. They
261 ;; should return non-nil if they found a candidate and the match data
262 ;; should correspond to the highlight part of the font-lock keyword.
263 ;; The functions should not generate errors, in that case font-lock
264 ;; will fail to highlight the buffer. A match function takes one
265 ;; argument, LIMIT, that represent the end of area to be searched.
266 ;;
267 ;; The variable `cwarn-font-lock-feature-keywords-alist' contains a
268 ;; mapping from CWarn features to the font-lock keywords defined
269 ;; below.
270
271 (defmacro cwarn-font-lock-match (re &rest body)
272 "Match RE but only if BODY holds."
273 `(let ((res nil))
274 (while
275 (progn
276 (setq res (re-search-forward ,re limit t))
277 (and res
278 (save-excursion
279 (when (match-beginning 1) (goto-char (match-beginning 1)))
280 (condition-case nil ; In case something barfs.
281 (not (save-match-data
282 ,@body))
283 (error t))))))
284 res))
285
286 ;;{{{ Assignment in expressions
287
288 (defconst cwarn-font-lock-assignment-keywords
289 '((cwarn-font-lock-match-assignment-in-expression
290 (1 font-lock-warning-face))))
291
292 (defun cwarn-font-lock-match-assignment-in-expression (limit)
293 "Match assignments inside expressions."
294 (cwarn-font-lock-match
295 "[^!<>=]\\(\\([-+*/%&^|]\\|<<\\|>>\\)?=\\)[^=]"
296 (backward-up-list 1)
297 (and (memq (following-char) '(?\( ?\[))
298 (not (progn
299 (skip-chars-backward " ")
300 (skip-chars-backward "a-zA-Z0-9_")
301 (or
302 ;; Default parameter of function.
303 (c-at-toplevel-p)
304 (looking-at "for\\>")))))))
305
306 ;;}}}
307 ;;{{{ Semicolon
308
309 (defconst cwarn-font-lock-semicolon-keywords
310 '((cwarn-font-lock-match-dangerous-semicolon (0 font-lock-warning-face))))
311
312 (defun cwarn-font-lock-match-dangerous-semicolon (limit)
313 "Match semicolons directly after `for', `while', and `if'.
314 The semicolon after a `do { ... } while (x);' construction is not matched."
315 (cwarn-font-lock-match
316 ";"
317 (backward-sexp 2) ; Expression and keyword.
318 (or (looking-at "\\(for\\|if\\)\\>")
319 (and (looking-at "while\\>")
320 (condition-case nil
321 (progn
322 (backward-sexp 2) ; Body and "do".
323 (not (looking-at "do\\>")))
324 (error t))))))
325
326 ;;}}}
327 ;;{{{ Reference
328
329 (defconst cwarn-font-lock-reference-keywords
330 '((cwarn-font-lock-match-reference (1 font-lock-warning-face))))
331
332 (defun cwarn-font-lock-match-reference (limit)
333 "Font-lock matcher for C++ reference parameters."
334 (cwarn-font-lock-match
335 "[^&]\\(&\\)[^&=]"
336 (backward-up-list 1)
337 (and (eq (following-char) ?\()
338 (not (cwarn-inside-macro))
339 (c-at-toplevel-p))))
340
341 ;;}}}
342
343 ;;}}}
344 ;;{{{ The end
345
346 (defun turn-on-cwarn-mode-if-enabled ()
347 "Turn on CWarn mode in the current buffer if applicable.
348 The mode is turned if some feature is enabled for the current
349 `major-mode' in `cwarn-configuration'."
350 (when (cwarn-is-enabled major-mode) (cwarn-mode 1)))
351
352 ;;;###autoload
353 (define-globalized-minor-mode global-cwarn-mode
354 cwarn-mode turn-on-cwarn-mode-if-enabled)
355
356 (provide 'cwarn)
357
358 (run-hooks 'cwarn-load-hook)
359
360 ;;}}}
361
362 ;;; cwarn.el ends here