Commit | Line | Data |
---|---|---|
e8af40ee | 1 | ;;; cwarn.el --- highlight suspicious C and C++ constructions |
2936437d | 2 | |
49f70d46 | 3 | ;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 |
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 | ||
127 | Never set this variable directly, use the command `cwarn-mode' | |
128 | instead.") | |
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. | |
134 | Each item is on the form (mode featurelist), where featurelist can be | |
135 | on 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 | ||
142 | See variable `cwarn-font-lock-feature-keywords-alist' for available | |
143 | features." | |
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 |
152 | The keywords could either a font-lock keyword list or a symbol. |
153 | If it is a symbol it is assumed to be a variable containing a font-lock | |
154 | keyword 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 | ||
164 | Currently, messages are generated when the mode is activated and | |
165 | deactivated." | |
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 | 189 | |
1902a98c EZ |
190 | Suspicious constructs are highlighted using `font-lock-warning-face'. |
191 | ||
2936437d GM |
192 | Note, in addition to enabling this minor mode, the major mode must |
193 | be included in the variable `cwarn-configuration'. By default C and | |
194 | C++ modes are included. | |
195 | ||
196 | With ARG, turn CWarn mode on if and only if arg is positive." | |
1f644ce3 | 197 | :group 'cwarn :lighter cwarn-mode-text |
0b05c8ca SM |
198 | (cwarn-font-lock-keywords cwarn-mode) |
199 | (if font-lock-mode (font-lock-fontify-buffer))) | |
2936437d GM |
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 | ||
2936437d GM |
209 | ;;}}} |
210 | ;;{{{ Help functions | |
211 | ||
212 | (defun cwarn-is-enabled (mode &optional feature) | |
213 | "Non-nil if CWarn FEATURE is enabled for MODE. | |
214 | feature is an atom representing one construction to highlight. | |
215 | ||
216 | Check if any feature is enabled for MODE if no feature is specified. | |
217 | ||
218 | The valid features are described by the variable | |
219 | `cwarn-font-lock-feature-keywords-alist'." | |
220 | (let ((mode-configuraion (assq mode cwarn-configuration))) | |
221 | (and mode-configuraion | |
222 | (or (null feature) | |
223 | (let ((list-or-t (nth 1 mode-configuraion))) | |
224 | (or (eq list-or-t t) | |
225 | (if (eq (car-safe list-or-t) 'not) | |
226 | (not (memq feature (cdr list-or-t))) | |
227 | (memq feature list-or-t)))))))) | |
228 | ||
229 | (defun cwarn-inside-macro () | |
230 | "True if point is inside a C macro definition." | |
231 | (save-excursion | |
232 | (beginning-of-line) | |
233 | (while (eq (char-before (1- (point))) ?\\) | |
234 | (forward-line -1)) | |
235 | (back-to-indentation) | |
236 | (eq (char-after) ?#))) | |
237 | ||
0b05c8ca SM |
238 | (defun cwarn-font-lock-keywords (addp) |
239 | "Install/Remove keywords into current buffer. | |
240 | If ADDP is non-nil, install else remove." | |
2936437d GM |
241 | (dolist (pair cwarn-font-lock-feature-keywords-alist) |
242 | (let ((feature (car pair)) | |
243 | (keywords (cdr pair))) | |
244 | (if (not (listp keywords)) | |
245 | (setq keywords (symbol-value keywords))) | |
0b05c8ca SM |
246 | (if (cwarn-is-enabled major-mode feature) |
247 | (funcall (if addp 'font-lock-add-keywords 'font-lock-remove-keywords) | |
248 | nil keywords))))) | |
2936437d GM |
249 | |
250 | ;;}}} | |
251 | ;;{{{ Backward compatibility | |
252 | ||
253 | ;; This piece of code will be part of CC mode as of Emacs 20.4. | |
254 | (if (not (fboundp 'c-at-toplevel-p)) | |
255 | (defun c-at-toplevel-p () | |
256 | "Return a determination as to whether point is at the `top-level'. | |
257 | Being at the top-level means that point is either outside any | |
258 | enclosing block (such function definition), or inside a class | |
259 | definition, but outside any method blocks. | |
260 | ||
261 | If point is not at the top-level (e.g. it is inside a method | |
262 | definition), then nil is returned. Otherwise, if point is at a | |
263 | top-level not enclosed within a class definition, t is returned. | |
264 | Otherwise, a 2-vector is returned where the zeroth element is the | |
265 | buffer position of the start of the class declaration, and the first | |
0b05c8ca | 266 | element is the buffer position of the enclosing class' opening |
2936437d GM |
267 | brace." |
268 | (let ((state (c-parse-state))) | |
269 | (or (not (c-most-enclosing-brace state)) | |
270 | (c-search-uplist-for-classkey state)))) | |
271 | ) | |
272 | ||
273 | ;;}}} | |
274 | ;;{{{ Font-lock keywords and match functions | |
275 | ||
276 | ;; This section contains font-lock keywords. A font lock keyword can | |
277 | ;; either contain a regular expression or a match function. All | |
278 | ;; keywords defined here use match functions since the C and C++ | |
279 | ;; constructions highlighted by CWarn are too complex to be matched by | |
280 | ;; regular expressions. | |
281 | ;; | |
282 | ;; A match function should act like a normal forward search. They | |
283 | ;; should return non-nil if they found a candidate and the match data | |
284 | ;; should correspond to the highlight part of the font-lock keyword. | |
0b05c8ca | 285 | ;; The functions should not generate errors, in that case font-lock |
2936437d GM |
286 | ;; will fail to highlight the buffer. A match function takes one |
287 | ;; argument, LIMIT, that represent the end of area to be searched. | |
288 | ;; | |
289 | ;; The variable `cwarn-font-lock-feature-keywords-alist' contains a | |
290 | ;; mapping from CWarn features to the font-lock keywords defined | |
291 | ;; below. | |
292 | ||
0b05c8ca SM |
293 | (defmacro cwarn-font-lock-match (re &rest body) |
294 | "Match RE but only if BODY holds." | |
295 | `(let ((res nil)) | |
296 | (while | |
297 | (progn | |
298 | (setq res (re-search-forward ,re limit t)) | |
299 | (and res | |
300 | (save-excursion | |
301 | (when (match-beginning 1) (goto-char (match-beginning 1))) | |
302 | (condition-case nil ; In case something barfs. | |
303 | (not (save-match-data | |
304 | ,@body)) | |
305 | (error t)))))) | |
306 | res)) | |
307 | ||
2936437d GM |
308 | ;;{{{ Assignment in expressions |
309 | ||
310 | (defconst cwarn-font-lock-assignment-keywords | |
311 | '((cwarn-font-lock-match-assignment-in-expression | |
312 | (1 font-lock-warning-face)))) | |
313 | ||
314 | (defun cwarn-font-lock-match-assignment-in-expression (limit) | |
315 | "Match assignments inside expressions." | |
0b05c8ca SM |
316 | (cwarn-font-lock-match |
317 | "[^!<>=]\\(\\([-+*/%&^|]\\|<<\\|>>\\)?=\\)[^=]" | |
318 | (backward-up-list 1) | |
319 | (and (memq (following-char) '(?\( ?\[)) | |
320 | (not (progn | |
321 | (skip-chars-backward " ") | |
322 | (skip-chars-backward "a-zA-Z0-9_") | |
323 | (or | |
324 | ;; Default parameter of function. | |
325 | (c-at-toplevel-p) | |
326 | (looking-at "for\\>"))))))) | |
2936437d GM |
327 | |
328 | ;;}}} | |
329 | ;;{{{ Semicolon | |
330 | ||
331 | (defconst cwarn-font-lock-semicolon-keywords | |
332 | '((cwarn-font-lock-match-dangerous-semicolon (0 font-lock-warning-face)))) | |
333 | ||
334 | (defun cwarn-font-lock-match-dangerous-semicolon (limit) | |
335 | "Match semicolons directly after `for', `while', and `if'. | |
0b05c8ca SM |
336 | The semicolon after a `do { ... } while (x);' construction is not matched." |
337 | (cwarn-font-lock-match | |
338 | ";" | |
339 | (backward-sexp 2) ; Expression and keyword. | |
340 | (or (looking-at "\\(for\\|if\\)\\>") | |
341 | (and (looking-at "while\\>") | |
342 | (condition-case nil | |
343 | (progn | |
344 | (backward-sexp 2) ; Body and "do". | |
345 | (not (looking-at "do\\>"))) | |
346 | (error t)))))) | |
2936437d GM |
347 | |
348 | ;;}}} | |
349 | ;;{{{ Reference | |
350 | ||
351 | (defconst cwarn-font-lock-reference-keywords | |
352 | '((cwarn-font-lock-match-reference (1 font-lock-warning-face)))) | |
353 | ||
354 | (defun cwarn-font-lock-match-reference (limit) | |
355 | "Font-lock matcher for C++ reference parameters." | |
0b05c8ca SM |
356 | (cwarn-font-lock-match |
357 | "[^&]\\(&\\)[^&=]" | |
358 | (backward-up-list 1) | |
359 | (and (eq (following-char) ?\() | |
360 | (not (cwarn-inside-macro)) | |
361 | (c-at-toplevel-p)))) | |
2936437d GM |
362 | |
363 | ;;}}} | |
364 | ||
365 | ;;}}} | |
366 | ;;{{{ The end | |
367 | ||
0b05c8ca SM |
368 | (defun turn-on-cwarn-mode-if-enabled () |
369 | "Turn on CWarn mode in the current buffer if applicable. | |
370 | The mode is turned if some feature is enabled for the current | |
371 | `major-mode' in `cwarn-configuration'." | |
372 | (if (cwarn-is-enabled major-mode) (turn-on-cwarn-mode))) | |
373 | ||
374 | ;;;###autoload | |
b6a856a6 | 375 | (define-globalized-minor-mode global-cwarn-mode |
e72b3747 | 376 | cwarn-mode turn-on-cwarn-mode-if-enabled) |
2936437d GM |
377 | |
378 | (provide 'cwarn) | |
379 | ||
380 | (run-hooks 'cwarn-load-hook) | |
381 | ||
2936437d GM |
382 | ;;}}} |
383 | ||
cbee283d | 384 | ;; arch-tag: 225fb5e2-0838-4eb1-88ce-3811c5e4d738 |
2936437d | 385 | ;;; cwarn.el ends here |