GNU ELPA - relint


Elisp regexp mistake finder
relint-1.15.tar, 2020-Mar-05, 170 KiB
Home page
Browse ELPA's repository
CGit or Gitweb

To install this package, run in Emacs:

M-x package-install RET relint RET

Full description

                relint -- Emacs regexp mistake finder

Relint scans elisp files for mistakes in regexps, including deprecated
syntax and bad practice. It also checks the regexp-like arguments to
skip-chars-forward, skip-chars-backward, skip-syntax-forward and

* Usage

  - Check a single file:

      M-x relint-file

  - Check all .el files in a directory tree:

      M-x relint-directory

  - Check current buffer:

      M-x relint-current-buffer

  - From batch mode:

      emacs -batch -l relint.el -f relint-batch FILES-AND-DIRS...

    where directories are scanned recursively.
    (Options for finding relint and xr need to be added after
    -batch, either -f package-initialize or -L DIR.)

    In the *relint* buffer, pressing "g" will re-run the same check.

  - From elisp code, use one of the above functions or

      (relint-buffer BUFFER)

    which returns a list of diagnostics.

* Installation

  From GNU ELPA (

    M-x package-install RET relint RET

  Relint requires the package xr (;
  it will be installed automatically.

* What the diagnostics mean

  - Unescaped literal 'X'

    A special character is taken literally because it occurs in a
    position where it does not need to be backslash-escaped. It is
    good style to do so anyway (assuming that it should occur as a
    literal character).

  - Escaped non-special character 'X'
    A character is backslash-escaped even though this is not necessary
    and does not turn it into a special sequence. Maybe the backslash
    was in error, or should be doubled if a literal backslash was
  - Duplicated 'X' inside character alternative
    A character occurs twice inside [...]; this is obviously
    pointless. In particular, backslashes are not special inside
    [...]; they have no escaping power, and do not need to be escaped
    in order to include a literal backslash.
  - Repetition of repetition
    A repetition construct is applied to an expression that is already
    repeated, such as a*+ (? counts as repetition here). Such
    expressions can be written with a single repetition and often
    indicate a different mistake, such as missing backslashes.

  - Reversed range 'Y-X' matches nothing

    The last character of a range precedes the first and therefore
    includes no characters at all (not even the endpoints). Most such
    ranges are caused by a misplaced hyphen.

  - Character 'B' included in range 'A-C'

    A range includes a character that also occurs individually. This
    is often caused by a misplaced hyphen.

  - Ranges 'A-M' and 'D-Z' overlap

    Two ranges have at least one character in common. This is often
    caused by a misplaced hyphen.

  - Two-character range 'A-B'

    A range only consists of its two endpoints, since they have
    consecutive character codes. This is often caused by a misplaced

  - Duplicated character class '[:class:]'

    A character class occurs twice in a single character alternative
    or skip set.

  - Duplicated alternative branch

    The same expression occurs in two different branches, like in
    A\|A. This has the effect of only including it once.

  - Branch matches superset/subset of a previous branch

    A branch in an or-expression matches a superset or subset of what
    another branch matches, like in [ab]\|a. This means that one of
    the branches can be eliminated without changing the meaning of the

  - Repetition subsumes/subsumed by preceding repetition

    An repeating expression matches a superset or subset of what the
    previous expression matches, in such a way that one of them is
    unnecessary. For example, [ab]+a* matches the same text as [ab]+,
    so the a* could be removed without changing the meaning of the

  - Uncounted repetition

    The construct A\{,\} repeats A zero or more times which was
    probably not intended.

  - Implicit zero repetition

    The construct A\{\} only matches the empty string, which was
    probably not intended.

  - Suspect '[' in char alternative

    This warning indicates badly-placed square brackets in a character
    alternative, as in [A[B]C]. A literal ] must come first
    (possibly after a negating ^).

  - Literal '-' not first or last

    It is good style to put literal hyphens last in character
    alternatives and skip sets, to clearly indicate that it was not
    intended as part of a range.

  - Repetition of zero-width assertion

    A repetition operator was applied to a zero-width assertion, like
    ^ or \<, which is completely pointless. The error may be a missing
    escaping backslash.

  - Repetition of expression matching an empty string

    A repetition operator was applied to a sub-expression that could
    match the empty string; this is not necessarily wrong, but such
    constructs run very slowly on Emacs's regexp engine. Consider
    rewriting them into a form where the repeated expression cannot
    match the empty string.

    Example: \(?:a*b*\)* is equivalent to the much faster \(?:a\|b\)*.

    Another example: \(?:a?b*\)? is better written a?b*. 

    In general, A?, where A matches the empty string, can be
    simplified to just A.

  - Suspect range '+-X' or 'X-+'

    A character range with '+' as one of its endpoints is more often an
    incorrect attempt to include both '+' and '-' in the set.

  - Unnecessarily escaped 'X'

    A character is backslash-escaped in a skip set despite not being
    one of the three special characters - (hyphen), \ (backslash) and
    ^ (caret). It could be unnecessary, or a backslash that should
    have been escaped.

  - Single-element range 'X-X'

    A range in a skip set has identical first and last elements. It is
    rather pointless to have it as a range.

  - Stray '\\' at end of string

    A single backslash at the end of a skip set is always ignored;
    double it if you want a literal backslash to be included.

  - Suspect skip set framed in '[...]'

    A skip set appears to be enclosed in [...], as if it were a
    regexp. Skip sets are not regexps and do not use brackets. To
    include the brackets themselves, put them next to each other.

  - Suspect character class framed in '[...]'

    A skip set contains a character class enclosed in double pairs of
    square brackets, as if it were a regexp. Character classes in skip
    sets are written inside a single pair of square brackets, like

  - Empty set matches nothing

    The empty string is a skip set that does not match anything, and
    is therefore pointless.

  - Negated empty set matches anything

    The string "^" is a skip set that matches anything, and is therefore

  - 'X' cannot be used for arguments to 'F'

    An expression that looks like a regexp was given as an argument to
    a function that expects a skip-set.

  - Value from 'X' cannot be spliced into '[...]'

    An expression that looks like a regexp was used to form a string
    where it is surrounded by square brackets, as if it were part of a
    character alternative. Regexps are not valid inside character
    alternatives; they use a different syntax.

    If you are just building a string containing a regexp for display
    purposes, consider using other delimiters than square brackets;
    displaying the regexp 0-9 as [0-9] is very misleading.

  - Invalid char 'X' in syntax string

    A string argument to skip-syntax-forward or skip-syntax-backward
    contains a character that doesn't indicate a syntax class. Such a
    string is not a regexp or skip-set, but just a string of syntax
    codes, possibly with a leading ^ for negation.

  - Duplicated char 'X' in syntax string

    A string argument to skip-syntax-forward or skip-syntax-backward
    contains a duplicated class, which is pointless and may indicate a
    mistake. Note that some characters indicate the same syntax class:
    '.' and ' ' (space) both mean the 'space' class.

  - Empty syntax string

    A string argument to skip-syntax-forward or skip-syntax-backward
    is empty or "^", neither of which makes sense.

* Suppressing diagnostics

  While relint has been designed to avoid false positives, there may
  be cases where it emits unfounded complaints. Most of the time, it
  is worth the trouble to change the code to make them go away, but
  sometimes it cannot be done in a reasonable way.

  To suppress such diagnostics, add a comment on the form

    ;; relint suppression: MESSAGE

  on the line before the code where the error occurred. MESSAGE is a
  substring of the message to be suppressed. Multiple suppression
  comment lines can precede a line of code to eliminate several
  complaints on the same line.

* Bugs

  The recognition of regexps is done by ad-hoc rules; the simplistic
  method employed means that many errors will go undetected.

  Still, if you believe that a flawed regexp could have been
  discovered but wasn't, please report it as a bug. Reports of false
  positives and crashes are of course equally welcome.

Old versions

relint-1.14.tar2020-Mar-01 150 KiB
relint-1.13.tar2020-Jan-31 150 KiB
relint-1.12.tar2020-Jan-23 140 KiB
relint-1.11.tar2019-Sep-29 140 KiB
relint-1.10.el2019-Aug-1059.3 KiB
relint-1.9.el2019-Aug-0455.4 KiB
relint-1.8.el2019-May-2646.0 KiB
relint-1.7.el2019-Apr-1346.1 KiB
relint-1.6.el2019-Apr-0144.3 KiB
relint-1.5.el2019-Mar-2640.2 KiB


Version 1.15:
- Improved position accuracy in various lists of regexps
- Check for mistake in rx `any' forms
- `relint-buffer' now also returns severity (warning, error)
- Relint can now also check the *scratch* buffer
Version 1.14:
- Added `relint-buffer'
- Report error position inside string literals when possible
- Scan arguments to `search-forward-regexp' and `search-backward-regexp'
- Use text quoting for messages
Version 1.13:
- Look in function/macro doc strings to find regexp arguments and
  return values
- Detect binding and mutation of some known regexp variables
- Detect regexps as arguments to `syntax-propertize-rules'
- More font-lock-keywords variables are scanned for regexps
- `relint-batch' no longer outputs a summary if there were no errors
Version 1.12:
- Improved detection of regexps in defcustom declarations
- Better suppression of false positives
- Nonzero exit status upon error in `relint-batch'
Version 1.11:
- Improved evaluator, now handling limited local variable mutation
- Bug fixes
- Test suite
Version 1.10:
- Check arguments to `skip-syntax-forward' and `skip-syntax-backward'
- Add error suppression mechanism
Version 1.9:
- Limited tracking of local variables in regexp finding
- Recognise new variable `literal' and `regexp' rx clauses
- Detect more regexps in defcustom declarations
- Requires xr 1.13
Version 1.8:
- Updated diagnostics list
- Requires xr 1.12
Version 1.7:
- Expanded regexp-generating heuristics
- Some `defalias' are now followed
- All diagnostics are now documented (see
Version 1.6:
- Add `relint-current-buffer'
- Show relative file names in *relint*
- Extended regexp-generating heuristics, warning about suspiciously-named
  variables used as skip-sets
- "-patterns" and "-pattern-list" are no longer interesting variable
Version 1.5:
- Substantially improved evaluator, able to evaluate some functions and
  macros defined in the same file, even when passed as parameters
- Detect regexps spliced into [...]
- Check bad skip-set provenance
- The *relint* buffer now uses a new relint-mode for better usability,
  with "g" bound to `relint-again'
Version 1.4:
- First version after name change to `relint'