diff lisp/progmodes/cwarn.el @ 26963:f281dfbf8cd2

*** empty log message ***
author Gerd Moellmann <gerd@gnu.org>
date Tue, 21 Dec 1999 09:08:41 +0000
parents
children 2ae4e53657a3
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/progmodes/cwarn.el	Tue Dec 21 09:08:41 1999 +0000
@@ -0,0 +1,483 @@
+;;; cwarn.el --- Highlight suspicious C and C++ constructions.
+
+;; Copyright (C) 1999 Free Software Foundation, Inc.
+
+;; Author: Anders Lindgren <andersl@andersl.com>
+;; Keywords: c, languages, faces
+;; X-Url: http://www.andersl.com/emacs
+;; Version: 1.3.1  1999-12-13
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Commentary:
+
+;;{{{ Documentation
+
+;; Description:
+;;
+;; CWarn is a package that highlights suspicious C and C++ constructions.
+;;
+;; For example, take a look at the following piece of C code:
+;;
+;;     if (x = 0);
+;;       foo();
+;;
+;; The code contains two, possibly fatal, bugs.  The first is that the
+;; assignment operator "=" is used as part of the test; the user
+;; probably ment to use the comparison operator "==".
+;;
+;; The second problem is that an extra semicolon is placed after
+;; closing parenthesis of the test expression.  This makes the body of
+;; the if statement to be an empty statement, not the call to the
+;; function "foo", as the user probably intended.
+;;
+;; This package is capable of highlighting the following C and C++
+;; constructions:
+;;
+;; * Assignments inside expressions, including variations like "+=".
+;; * Semicolon following immediately after `if', `for', and `while'
+;;   (except, of course, after a `do .. while' statement).
+;; * C++ functions with reference parameters.
+;;
+;; Note that none of the constructions highlighted (especially not C++
+;; reference parameters) are considered errors by the langauage
+;; definitions.
+
+;; Usage:
+;;
+;; CWarn is implemented as two minor modes: `cwarn-mode' and
+;; `global-cwarn-mode'.  The former can be applied to individual buffers
+;; and the latter to all buffers.
+;;
+;; Activate this package by Customize, or by placing the following line
+;; into the appropriate init file:
+;;
+;;    (global-cwarn-mode 1)
+;;
+;; Also, `font-lock-mode' or `global-font-lock-mode' must be enabled.
+
+;; Afterthought:
+;;
+;; After using this package for several weeks it feels as though I
+;; find stupid typo-style bugs while editing rather than at compile-
+;; or run-time, if I ever find them.
+;;
+;; On the other hand, I find myself using assignments inside
+;; expressions much more often than I used to do.  The reason is that
+;; there is no risk of interpreting an assignment operator as a
+;; comparison ("hey, the assignment operator is red, duh!").
+
+;; Reporting bugs:
+;;
+;;     Out of the last ten bugs you found, how many did you report?
+;;
+;; When reporting a bug, please:
+;;
+;; * Send a mail the maintainer of the package, or to the author
+;;   if no maintainer exists.
+;; * Include the name of the package in the title of the mail, to
+;;   simplify for the recipient.
+;; * State exactly what you did, what happened, and what you expected
+;;   to see when you found the bug.
+;; * If the bug cause an error, set the variable `debug-on-error' to t,
+;;   repreat the operations that triggered the error and include
+;;   the backtrace in the letter.
+;; * If possible, include an example that activates the bug.
+;; * Should you speculate about the cause of the problem, please
+;;   state explicitly that you are guessing.
+
+;;}}}
+
+;;; Code:
+
+;;{{{ Dependencies
+
+(eval-when-compile (require 'cl))
+
+(require 'custom)
+(require 'font-lock)
+(require 'cc-mode)
+
+;;}}}
+;;{{{ Variables
+
+(defgroup cwarn nil
+  "Highlight suspicious C and C++ constructions."
+  :group 'faces)
+
+(defvar cwarn-mode nil
+  "*Non-nil when Cwarn mode is active.
+
+Never set this variable directly, use the command `cwarn-mode'
+instead.")
+
+(defcustom global-cwarn-mode nil
+  "*When on, suspicious C and C++ constructions are highlighted.
+
+Set this variable using \\[customize] or use the command
+`global-cwarn-mode'."
+  :group 'cwarn
+  :initialize 'custom-initialize-default
+  :set '(lambda (symbol value)
+	  (global-cwarn-mode (or value 0)))
+  :type 'boolean
+  :require 'cwarn)
+
+(defcustom cwarn-configuration
+  '((c-mode (not reference))
+    (c++-mode t))
+  "*List of items each describing which features are enable for a mode.
+Each item is on the form (mode featurelist), where featurelist can be
+on one of three forms:
+
+* A list of enabled features.
+* A list starting with the atom `not' followed by the features
+  which are not enabled.
+* The atom t, that represent that all features are enabled.
+
+See variable `cwarn-font-lock-feature-keywords-alist' for available
+features."
+  :group 'cwarn)
+
+(defcustom cwarn-font-lock-feature-keywords-alist
+  '((assign    . cwarn-font-lock-assignment-keywords)
+    (semicolon . cwarn-font-lock-semicolon-keywords)
+    (reference . cwarn-font-lock-reference-keywords))
+  "*Maps a CWarn feature to font-lock keywords.
+The keywords could either a font-lock keyword list or a symbol.
+If it is a symbol it is assumed to be a variable containing a font-lock
+keyword list."
+  :group 'cwarn)
+
+(defcustom cwarn-verbose t
+  "*When nil, CWarn mode will not generate any messages.
+
+Currently, messages are generated when the mode is activated and
+deactivated."
+  :group 'cwarn
+  :type 'boolean)
+
+(defcustom cwarn-mode-text " CWarn"
+  "*String to display in the mode line when CWarn mode is active.
+
+\(When the string is not empty, make sure that it has a leading space.)"
+  :tag "CWarn mode text"                ; To separate it from `global-...'
+  :group 'cwarn
+  :type 'string)
+
+(defcustom cwarn-mode-hook nil
+  "*Functions to run when CWarn mode is activated."
+  :tag "CWarn mode hook"                ; To separate it from `global-...'
+  :group 'cwarn
+  :type 'hook)
+
+(defcustom global-cwarn-mode-text ""
+  "*String to display when Global CWarn mode is active.
+
+The default is nothing since when this mode is active this text doesn't
+vary over time, or between buffers.  Hence mode line text
+would only waste precious space."
+  :group 'cwarn
+  :type 'string)
+
+(defcustom global-cwarn-mode-hook nil
+  "*Hook called when Global CWarn mode is activated."
+  :group 'cwarn
+  :type 'hook)
+
+(defcustom cwarn-load-hook nil
+  "*Functions to run when CWarn mode is first loaded."
+  :tag "Load Hook"
+  :group 'cwarn
+  :type 'hook)
+
+;;}}}
+;;{{{ The modes
+
+;;;###autoload
+(defun cwarn-mode (&optional arg)
+  "Minor mode that hightlight suspicious C and C++ constructions.
+
+Note, in addition to enabling this minor mode, the major mode must
+be included in the variable `cwarn-configuration'.  By default C and
+C++ modes are included.
+
+With ARG, turn CWarn mode on if and only if arg is positive."
+  (interactive "P")
+  (make-local-variable 'cwarn-mode)
+  (setq cwarn-mode
+	(if (null arg)
+	    (not cwarn-mode)
+	  (> (prefix-numeric-value arg) 0)))
+  (if (and cwarn-verbose
+	   (interactive-p))
+      (message "Cwarn mode is now %s."
+	       (if cwarn-mode "on" "off")))
+  (if (not global-cwarn-mode)
+      (if cwarn-mode
+	  (cwarn-font-lock-add-keywords)
+	(cwarn-font-lock-remove-keywords)))
+  (font-lock-fontify-buffer)
+  (if cwarn-mode
+      (run-hooks 'cwarn-mode-hook)))
+
+;;;###autoload
+(defun turn-on-cwarn-mode ()
+  "Turn on CWarn mode.
+
+This function is designed to be added to hooks, for example:
+  (add-hook 'c-mode-hook 'turn-on-cwarn-mode)"
+  (cwarn-mode 1))
+
+;;;###autoload
+(defun global-cwarn-mode (&optional arg)
+  "Hightlight suspicious C and C++ constructions in all buffers.
+
+With ARG, turn CWarn mode on globally if and only if arg is positive."
+  (interactive "P")
+  (let ((old-global-cwarn-mode global-cwarn-mode))
+    (setq global-cwarn-mode
+	  (if (null arg)
+	      (not global-cwarn-mode)
+	    (> (prefix-numeric-value arg) 0)))
+    (if (and cwarn-verbose
+	     (interactive-p))
+	(message "Global CWarn mode is now %s."
+		 (if global-cwarn-mode "on" "off")))
+    (when (not (eq global-cwarn-mode old-global-cwarn-mode))
+      ;; Update for all future buffers.
+      (dolist (conf cwarn-configuration)
+	(if global-cwarn-mode
+	    (cwarn-font-lock-add-keywords (car conf))
+	  (cwarn-font-lock-remove-keywords (car conf))))
+      ;; Update all existing buffers.
+      (save-excursion
+	(dolist (buffer (buffer-list))
+	  (set-buffer buffer)
+	  ;; Update keywords in alive buffers.
+	  (when (and font-lock-mode
+		     (not cwarn-mode)
+		     (cwarn-is-enabled major-mode))
+	    (if global-cwarn-mode
+		(cwarn-font-lock-add-keywords)
+	      (cwarn-font-lock-remove-keywords))
+	    (font-lock-fontify-buffer))))))
+    ;; Kills all added keywords :-(
+    ;; (font-lock-mode 0)
+    ;; (makunbound 'font-lock-keywords)
+    ;; (font-lock-mode 1))))
+  (when global-cwarn-mode
+    (run-hooks 'global-cwarn-mode-hook)))
+
+;;}}}
+;;{{{ Help functions
+
+(defun cwarn-is-enabled (mode &optional feature)
+  "Non-nil if CWarn FEATURE is enabled for MODE.
+feature is an atom representing one construction to highlight.
+
+Check if any feature is enabled for MODE if no feature is specified.
+
+The valid features are described by the variable
+`cwarn-font-lock-feature-keywords-alist'."
+  (let ((mode-configuraion (assq mode cwarn-configuration)))
+    (and mode-configuraion
+	 (or (null feature)
+	     (let ((list-or-t (nth 1 mode-configuraion)))
+	       (or (eq list-or-t t)
+		   (if (eq (car-safe list-or-t) 'not)
+		       (not (memq feature (cdr list-or-t)))
+		     (memq feature list-or-t))))))))
+
+(defun cwarn-inside-macro ()
+  "True if point is inside a C macro definition."
+  (save-excursion
+    (beginning-of-line)
+    (while (eq (char-before (1- (point))) ?\\)
+      (forward-line -1))
+    (back-to-indentation)
+    (eq (char-after) ?#)))
+
+(defun cwarn-font-lock-add-keywords (&optional mode)
+  "Install keywords into major MODE, or into current buffer if nil."
+  (dolist (pair cwarn-font-lock-feature-keywords-alist)
+    (let ((feature (car pair))
+	  (keywords (cdr pair)))
+      (if (not (listp keywords))
+	  (setq keywords (symbol-value keywords)))
+      (if (cwarn-is-enabled (or mode major-mode) feature)
+	  (font-lock-add-keywords mode keywords)))))
+
+(defun cwarn-font-lock-remove-keywords (&optional mode)
+  "Remove keywords from major MODE, or from current buffer if nil."
+  (dolist (pair cwarn-font-lock-feature-keywords-alist)
+    (let ((feature (car pair))
+	  (keywords (cdr pair)))
+      (if (not (listp keywords))
+	  (setq keywords (symbol-value keywords)))
+      (if (cwarn-is-enabled (or mode major-mode) feature)
+	  (font-lock-remove-keywords mode keywords)))))
+
+;;}}}
+;;{{{ Backward compatibility
+
+;; This piece of code will be part of CC mode as of Emacs 20.4.
+(if (not (fboundp 'c-at-toplevel-p))
+(defun c-at-toplevel-p ()
+  "Return a determination as to whether point is at the `top-level'.
+Being at the top-level means that point is either outside any
+enclosing block (such function definition), or inside a class
+definition, but outside any method blocks.
+
+If point is not at the top-level (e.g. it is inside a method
+definition), then nil is returned.  Otherwise, if point is at a
+top-level not enclosed within a class definition, t is returned.
+Otherwise, a 2-vector is returned where the zeroth element is the
+buffer position of the start of the class declaration, and the first
+element is the buffer position of the enclosing class's opening
+brace."
+  (let ((state (c-parse-state)))
+    (or (not (c-most-enclosing-brace state))
+	(c-search-uplist-for-classkey state))))
+)
+
+;;}}}
+;;{{{ Font-lock keywords and match functions
+
+;; This section contains font-lock keywords.  A font lock keyword can
+;; either contain a regular expression or a match function.  All
+;; keywords defined here use match functions since the C and C++
+;; constructions highlighted by CWarn are too complex to be matched by
+;; regular expressions.
+;;
+;; A match function should act like a normal forward search.  They
+;; should return non-nil if they found a candidate and the match data
+;; should correspond to the highlight part of the font-lock keyword.
+;; The functions shold not generate errors, in that case font-lock
+;; will fail to highlight the buffer.  A match function takes one
+;; argument, LIMIT, that represent the end of area to be searched.
+;;
+;; The variable `cwarn-font-lock-feature-keywords-alist' contains a
+;; mapping from CWarn features to the font-lock keywords defined
+;; below.
+
+;;{{{ Assignment in expressions
+
+(defconst cwarn-font-lock-assignment-keywords
+  '((cwarn-font-lock-match-assignment-in-expression
+     (1 font-lock-warning-face))))
+
+(defun cwarn-font-lock-match-assignment-in-expression (limit)
+  "Match assignments inside expressions."
+  (let ((res nil))
+    (while
+	(progn
+	  (setq res (re-search-forward "[^!<>=]\\(=\\)[^=]" limit t))
+	  (and res
+	       (save-excursion
+		 (goto-char (match-beginning 1))
+		 (condition-case nil    ; In case "backward-up-list" barfs.
+		     (progn
+		       (backward-up-list 1)
+		       (or (not (memq (following-char) '(?\( ?\[)))
+			   (save-match-data
+			     (skip-chars-backward " ")
+			     (skip-chars-backward "a-zA-Z0-9_")
+			     (or
+			      ;; Default parameter of function.
+			      (c-at-toplevel-p)
+			      (looking-at "for\\>")))))
+		   (error t))))))
+      res))
+
+;;}}}
+;;{{{ Semicolon
+
+(defconst cwarn-font-lock-semicolon-keywords
+  '((cwarn-font-lock-match-dangerous-semicolon (0 font-lock-warning-face))))
+
+(defun cwarn-font-lock-match-dangerous-semicolon (limit)
+  "Match semicolons directly after `for', `while', and `if'.
+Tne semicolon after a `do { ... } while (x);' construction is not matched."
+  (let ((res nil))
+    (while
+	(progn
+	  (setq res (search-forward ";" limit t))
+	  (and res
+	       (save-excursion
+		 (condition-case nil    ; In case something barfs.
+		     (save-match-data
+		       (backward-sexp 2) ; Expression and keyword.
+		       (not (or (looking-at "\\(for\\|if\\)\\>")
+				(and (looking-at "while\\>")
+				     (condition-case nil
+					 (progn
+					   (backward-sexp 2) ; Body and "do".
+					   (not (looking-at "do\\>")))
+				       (error t))))))
+		   (error t))))))
+      res))
+
+;;}}}
+;;{{{ Reference
+
+(defconst cwarn-font-lock-reference-keywords
+  '((cwarn-font-lock-match-reference (1 font-lock-warning-face))))
+
+(defun cwarn-font-lock-match-reference (limit)
+  "Font-lock matcher for C++ reference parameters."
+  (let ((res nil))
+    (while
+	(progn
+	  (setq res (re-search-forward "[^&]\\(&\\)[^&=]" limit t))
+	  (and res
+	       (save-excursion
+		 (goto-char (match-beginning 1))
+		 (condition-case nil    ; In case something barfs.
+		     (save-match-data
+		       (backward-up-list 1)
+		       (or (not (eq (following-char) ?\())
+			   (cwarn-inside-macro)
+			   (not (c-at-toplevel-p))))
+		   (error t))))))
+    res))
+
+;;}}}
+
+;;}}}
+;;{{{ The end
+
+(unless (assq 'cwarn-mode minor-mode-alist)
+  (push '(cwarn-mode cwarn-mode-text)
+	minor-mode-alist))
+(unless (assq 'global-cwarn-mode minor-mode-alist)
+  (push '(global-cwarn-mode global-cwarn-mode-text)
+	minor-mode-alist))
+
+(provide 'cwarn)
+
+(run-hooks 'cwarn-load-hook)
+
+;; This makes it possible to set Global CWarn mode from
+;; Customize.
+(if global-cwarn-mode
+    (global-cwarn-mode 1))
+
+;;}}}
+
+;;; cwarn.el ends here