Add "Package:" file headers to denote built-in packages.
[emacs.git] / lisp / progmodes / cwarn.el
blob00c11086ce1cdeb55486ba7a9d89216fd55529a0
1 ;;; cwarn.el --- highlight suspicious C and C++ constructions
3 ;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
4 ;; Free Software Foundation, Inc.
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
11 ;; This file is part of GNU Emacs.
13 ;; GNU Emacs is free software: you can redistribute it and/or modify
14 ;; it under the terms of the GNU General Public License as published by
15 ;; the Free Software Foundation, either version 3 of the License, or
16 ;; (at your option) any later version.
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.
23 ;; You should have received a copy of the GNU General Public License
24 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
26 ;;; Commentary:
28 ;;{{{ Documentation
30 ;; Description:
32 ;; CWarn is a package that highlights suspicious C and C++ constructions.
34 ;; For example, take a look at the following piece of C code:
36 ;; if (x = 0);
37 ;; foo();
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
41 ;; probably meant to use the comparison operator "==".
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.
48 ;; This package is capable of highlighting the following C and C++
49 ;; constructions:
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.
56 ;; Note that none of the constructions highlighted (especially not C++
57 ;; reference parameters) are considered errors by the language
58 ;; definitions.
60 ;; Usage:
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.
66 ;; Activate this package by Customize, or by placing the following line
67 ;; into the appropriate init file:
69 ;; (global-cwarn-mode 1)
71 ;; Also, `font-lock-mode' or `global-font-lock-mode' must be enabled.
73 ;; Afterthought:
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.
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!").
84 ;; Reporting bugs:
86 ;; Out of the last ten bugs you found, how many did you report?
88 ;; When reporting a bug, please:
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,
97 ;; repeat the operations that triggered the error and include
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.
103 ;;}}}
105 ;;; Code:
107 ;;{{{ Dependencies
109 (eval-when-compile (require 'cl))
111 (require 'custom)
112 (require 'font-lock)
113 (require 'cc-mode)
115 ;;}}}
116 ;;{{{ Variables
118 (defgroup cwarn nil
119 "Highlight suspicious C and C++ constructions."
120 :version "21.1"
121 :link '(url-link "http://www.andersl.com/emacs")
122 :group 'faces)
124 (defvar cwarn-mode nil
125 "*Non-nil when Cwarn mode is active.
127 Never set this variable directly, use the command `cwarn-mode'
128 instead.")
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:
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.
142 See variable `cwarn-font-lock-feature-keywords-alist' for available
143 features."
144 :type '(repeat sexp)
145 :group 'cwarn)
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))
151 "An alist mapping a CWarn feature to font-lock keywords.
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."
155 :type '(alist :key-type (choice (const assign)
156 (const semicolon)
157 (const reference))
158 :value-type (sexp :tag "Value"))
159 :group 'cwarn)
161 (defcustom cwarn-verbose t
162 "*When nil, CWarn mode will not generate any messages.
164 Currently, messages are generated when the mode is activated and
165 deactivated."
166 :group 'cwarn
167 :type 'boolean)
169 (defcustom cwarn-mode-text " CWarn"
170 "*String to display in the mode line when CWarn mode is active.
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)
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)
183 ;;}}}
184 ;;{{{ The modes
186 ;;;###autoload
187 (define-minor-mode cwarn-mode
188 "Minor mode that highlights suspicious C and C++ constructions.
190 Suspicious constructs are highlighted using `font-lock-warning-face'.
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.
196 With ARG, turn CWarn mode on if and only if arg is positive."
197 :group 'cwarn :lighter cwarn-mode-text
198 (cwarn-font-lock-keywords cwarn-mode)
199 (if font-lock-mode (font-lock-fontify-buffer)))
201 ;;;###autoload
202 (defun turn-on-cwarn-mode ()
203 "Turn on CWarn mode.
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))
209 ;;}}}
210 ;;{{{ Help functions
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.
216 Check if any feature is enabled for MODE if no feature is specified.
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))))))))
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) ?#)))
238 (defun cwarn-font-lock-keywords (addp)
239 "Install/Remove keywords into current buffer.
240 If ADDP is non-nil, install else remove."
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)))
246 (if (cwarn-is-enabled major-mode feature)
247 (funcall (if addp 'font-lock-add-keywords 'font-lock-remove-keywords)
248 nil keywords)))))
250 ;;}}}
251 ;;{{{ Backward compatibility
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.
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
266 element is the buffer position of the enclosing class' opening
267 brace."
268 (let ((state (c-parse-state)))
269 (or (not (c-most-enclosing-brace state))
270 (c-search-uplist-for-classkey state))))
273 ;;}}}
274 ;;{{{ Font-lock keywords and match functions
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.
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.
285 ;; The functions should not generate errors, in that case font-lock
286 ;; will fail to highlight the buffer. A match function takes one
287 ;; argument, LIMIT, that represent the end of area to be searched.
289 ;; The variable `cwarn-font-lock-feature-keywords-alist' contains a
290 ;; mapping from CWarn features to the font-lock keywords defined
291 ;; below.
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))
308 ;;{{{ Assignment in expressions
310 (defconst cwarn-font-lock-assignment-keywords
311 '((cwarn-font-lock-match-assignment-in-expression
312 (1 font-lock-warning-face))))
314 (defun cwarn-font-lock-match-assignment-in-expression (limit)
315 "Match assignments inside expressions."
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_")
324 ;; Default parameter of function.
325 (c-at-toplevel-p)
326 (looking-at "for\\>")))))))
328 ;;}}}
329 ;;{{{ Semicolon
331 (defconst cwarn-font-lock-semicolon-keywords
332 '((cwarn-font-lock-match-dangerous-semicolon (0 font-lock-warning-face))))
334 (defun cwarn-font-lock-match-dangerous-semicolon (limit)
335 "Match semicolons directly after `for', `while', and `if'.
336 The semicolon after a `do { ... } while (x);' construction is not matched."
337 (cwarn-font-lock-match
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))))))
348 ;;}}}
349 ;;{{{ Reference
351 (defconst cwarn-font-lock-reference-keywords
352 '((cwarn-font-lock-match-reference (1 font-lock-warning-face))))
354 (defun cwarn-font-lock-match-reference (limit)
355 "Font-lock matcher for C++ reference parameters."
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))))
363 ;;}}}
365 ;;}}}
366 ;;{{{ The end
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)))
374 ;;;###autoload
375 (define-globalized-minor-mode global-cwarn-mode
376 cwarn-mode turn-on-cwarn-mode-if-enabled)
378 (provide 'cwarn)
380 (run-hooks 'cwarn-load-hook)
382 ;;}}}
384 ;; arch-tag: 225fb5e2-0838-4eb1-88ce-3811c5e4d738
385 ;;; cwarn.el ends here