cwarn.el 11 KB

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