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