Skip to content
Matthew Woehlke edited this page Jan 17, 2020 · 2 revisions

Overview

We have had a long-standing desire to have some mechanism to allow us to do something about options that, in retrospect, we wish had been different. #2372 started introducing the notion of option deprecation. By introducing a new config directive that specifies a version-level compatibility mode, Uncrustify gained the ability to look for "deprecated" option names and apply special handling when these are found. All of the relevant code can be found in option.cpp.

Basic Operation

Uncrustify parses its configuration as a series of directives, by comparing the first word of each line to known directives. Non-option directives are always considered first. In compatibility mode, before finally comparing the first word to the option dictionary, a special "compatibility mode" parsing function is invoked. There may be multiple incarnations of these functions, each handling a different historic version, with the oldest executing first. These functions compares the first word to the set of options which have been deprecated or otherwise modified since the version for which they provide compatibility. When a match is found, code is then executed to apply the historic behavior of the deprecated option.

Renamed Options

The simplest form of deprecation is a rename. As of writing, these must be handled "longhand", but in the future we will likely provide utility functions and/or macros to simplify implementing renames.

The "longhand" code for handling a rename should resemble the following:

   if (cmd == "old_option")
   {
      OptionWarning w{ filename, OptionWarning::MINOR };
      w("option '%s' is deprecated; use '%s' instead",
        cmd.c_str(), options::new_option.name());

      UNUSED(options::new_option.read(args[1].c_str()));
      return(true);
   }

There are four parts to this code:

  • The first part is, of course, matching the first word of the current configuration line to the old option name.
  • The second part is showing a deprecation notice to the user. We use the OptionWarning utility class to emit this warning, but, since use of deprecated options is not an error, the warning is MINOR. We also reuse the command string and the name member of the new option object, rather than using string literals for these names.
  • Third, since this is a straight rename, we pass the next argument of the configuration line to the new option object to be extracted. Note that this logic is exactly the same as how we usually handle options, except in this case we are directly naming the option object, rather than obtaining the object from the option dictionary.
  • Finally, we return true to indicate that we handled the configuration line. By default, the newest compatibility-mode parsing function returns false to indicate that it did not handle the configuration line and that process_option_line should continue trying to parse it. All others should call the oldest compatibility-mode parsing function newer than that itself, such that calling any one compatibility-mode parsing function will cascade to all newer compatibility-mode parsing functions.

Altered Options

In the case of an option whose behavior has changed, things get a little more complicated.

In some cases, a Boolean option can be directly upgraded to an I/A/R/F option with no need for compatibility logic. This is possible because we provide mappings from all valid spellings of "true" and "false" that also map to an I/A/R/F value. However, it may be that an option has changed meaning, or been replaced by several options. In these cases, the preferred procedure is to declare a temporary option object which is used to extract the old value, and take appropriate action based on the extracted value. This might consist of setting values of one or more other options.

(More details will be added when we have a concrete example. As of writing, there are no such options.)