1;;; cwarn.el --- highlight suspicious C and C++ constructions 2 3;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 4;; Free Software Foundation, Inc. 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 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 2, or (at your option) 16;; any later version. 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 24;; along with GNU Emacs; see the file COPYING. If not, write to the 25;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 26;; Boston, MA 02110-1301, USA. 27 28;;; Commentary: 29 30;;{{{ Documentation 31 32;; Description: 33;; 34;; CWarn is a package that highlights suspicious C and C++ constructions. 35;; 36;; For example, take a look at the following piece of C code: 37;; 38;; if (x = 0); 39;; foo(); 40;; 41;; The code contains two, possibly fatal, bugs. The first is that the 42;; assignment operator "=" is used as part of the test; the user 43;; probably meant to use the comparison operator "==". 44;; 45;; The second problem is that an extra semicolon is placed after 46;; closing parenthesis of the test expression. This makes the body of 47;; the if statement to be an empty statement, not the call to the 48;; function "foo", as the user probably intended. 49;; 50;; This package is capable of highlighting the following C and C++ 51;; constructions: 52;; 53;; * Assignments inside expressions, including variations like "+=". 54;; * Semicolon following immediately after `if', `for', and `while' 55;; (except, of course, after a `do .. while' statement). 56;; * C++ functions with reference parameters. 57;; 58;; Note that none of the constructions highlighted (especially not C++ 59;; reference parameters) are considered errors by the language 60;; definitions. 61 62;; Usage: 63;; 64;; CWarn is implemented as two minor modes: `cwarn-mode' and 65;; `global-cwarn-mode'. The former can be applied to individual buffers 66;; and the latter to all buffers. 67;; 68;; Activate this package by Customize, or by placing the following line 69;; into the appropriate init file: 70;; 71;; (global-cwarn-mode 1) 72;; 73;; Also, `font-lock-mode' or `global-font-lock-mode' must be enabled. 74 75;; Afterthought: 76;; 77;; After using this package for several weeks it feels as though I 78;; find stupid typo-style bugs while editing rather than at compile- 79;; or run-time, if I ever find them. 80;; 81;; On the other hand, I find myself using assignments inside 82;; expressions much more often than I used to do. The reason is that 83;; there is no risk of interpreting an assignment operator as a 84;; comparison ("hey, the assignment operator is red, duh!"). 85 86;; Reporting bugs: 87;; 88;; Out of the last ten bugs you found, how many did you report? 89;; 90;; When reporting a bug, please: 91;; 92;; * Send a mail the maintainer of the package, or to the author 93;; if no maintainer exists. 94;; * Include the name of the package in the title of the mail, to 95;; simplify for the recipient. 96;; * State exactly what you did, what happened, and what you expected 97;; to see when you found the bug. 98;; * If the bug cause an error, set the variable `debug-on-error' to t, 99;; repeat the operations that triggered the error and include 100;; the backtrace in the letter. 101;; * If possible, include an example that activates the bug. 102;; * Should you speculate about the cause of the problem, please 103;; state explicitly that you are guessing. 104 105;;}}} 106 107;;; Code: 108 109;;{{{ Dependencies 110 111(eval-when-compile (require 'cl)) 112 113(require 'custom) 114(require 'font-lock) 115(require 'cc-mode) 116 117;;}}} 118;;{{{ Variables 119 120(defgroup cwarn nil 121 "Highlight suspicious C and C++ constructions." 122 :version "21.1" 123 :link '(url-link "http://www.andersl.com/emacs") 124 :group 'faces) 125 126(defvar cwarn-mode nil 127 "*Non-nil when Cwarn mode is active. 128 129Never set this variable directly, use the command `cwarn-mode' 130instead.") 131 132(defcustom cwarn-configuration 133 '((c-mode (not reference)) 134 (c++-mode t)) 135 "*List of items each describing which features are enable for a mode. 136Each item is on the form (mode featurelist), where featurelist can be 137on one of three forms: 138 139* A list of enabled features. 140* A list starting with the atom `not' followed by the features 141 which are not enabled. 142* The atom t, that represent that all features are enabled. 143 144See variable `cwarn-font-lock-feature-keywords-alist' for available 145features." 146 :type '(repeat sexp) 147 :group 'cwarn) 148 149(defcustom cwarn-font-lock-feature-keywords-alist 150 '((assign . cwarn-font-lock-assignment-keywords) 151 (semicolon . cwarn-font-lock-semicolon-keywords) 152 (reference . cwarn-font-lock-reference-keywords)) 153 "An alist mapping a CWarn feature to font-lock keywords. 154The keywords could either a font-lock keyword list or a symbol. 155If it is a symbol it is assumed to be a variable containing a font-lock 156keyword list." 157 :type '(alist :key-type (choice (const assign) 158 (const semicolon) 159 (const reference)) 160 :value-type (sexp :tag "Value")) 161 :group 'cwarn) 162 163(defcustom cwarn-verbose t 164 "*When nil, CWarn mode will not generate any messages. 165 166Currently, messages are generated when the mode is activated and 167deactivated." 168 :group 'cwarn 169 :type 'boolean) 170 171(defcustom cwarn-mode-text " CWarn" 172 "*String to display in the mode line when CWarn mode is active. 173 174\(When the string is not empty, make sure that it has a leading space.)" 175 :tag "CWarn mode text" ; To separate it from `global-...' 176 :group 'cwarn 177 :type 'string) 178 179(defcustom cwarn-load-hook nil 180 "*Functions to run when CWarn mode is first loaded." 181 :tag "Load Hook" 182 :group 'cwarn 183 :type 'hook) 184 185;;}}} 186;;{{{ The modes 187 188;;;###autoload 189(define-minor-mode cwarn-mode 190 "Minor mode that highlights suspicious C and C++ constructions. 191 192Note, in addition to enabling this minor mode, the major mode must 193be included in the variable `cwarn-configuration'. By default C and 194C++ modes are included. 195 196With 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))) 200 201;;;###autoload 202(defun turn-on-cwarn-mode () 203 "Turn on CWarn mode. 204 205This 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 209;;}}} 210;;{{{ Help functions 211 212(defun cwarn-is-enabled (mode &optional feature) 213 "Non-nil if CWarn FEATURE is enabled for MODE. 214feature is an atom representing one construction to highlight. 215 216Check if any feature is enabled for MODE if no feature is specified. 217 218The 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 238(defun cwarn-font-lock-keywords (addp) 239 "Install/Remove keywords into current buffer. 240If 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))))) 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'. 257Being at the top-level means that point is either outside any 258enclosing block (such function definition), or inside a class 259definition, but outside any method blocks. 260 261If point is not at the top-level (e.g. it is inside a method 262definition), then nil is returned. Otherwise, if point is at a 263top-level not enclosed within a class definition, t is returned. 264Otherwise, a 2-vector is returned where the zeroth element is the 265buffer position of the start of the class declaration, and the first 266element is the buffer position of the enclosing class' opening 267brace." 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. 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. 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 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 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." 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\\>"))))))) 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'. 336The 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)))))) 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." 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)))) 362 363;;}}} 364 365;;}}} 366;;{{{ The end 367 368(defun turn-on-cwarn-mode-if-enabled () 369 "Turn on CWarn mode in the current buffer if applicable. 370The 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 375(define-globalized-minor-mode global-cwarn-mode 376 cwarn-mode turn-on-cwarn-mode-if-enabled) 377 378(provide 'cwarn) 379 380(run-hooks 'cwarn-load-hook) 381 382;;}}} 383 384;;; arch-tag: 225fb5e2-0838-4eb1-88ce-3811c5e4d738 385;;; cwarn.el ends here 386