Dynamic rulesets

Code bases may be very large and contain a great deal of diversity even within a single project. For instance, part of your code might be legacy code that cannot yet conform to modern code guidelines because the legacy compiler does not yet support these constructs.

To that end, TICS has introduced the concept of a dynamic ruleset. When a dynamic ruleset is used, TICS selects the appropriate ruleset to use from a set of subrulesets. For instance, a common use case for dynamic rulesets is that a ruleset split is made between C++ code that is compiled with a C++11 compatible compiler, and C++ code that isn't.

Configuring dynamic rulesets: the basics

At its most basic, the configuration of a dynamic ruleset is done by creating a DYNAMICRULESET: 1 property in the base ruleset (see this page) and creating a rules.json file. This file will require the following contents (with various examples further down in the page):

The SELECTION property

There are several possibilities for a SELECTION property. Note that you can also specify multiple selection parameters. In that case, if TICS cannot resolve the decision on the first selection parameter, it will use the next parameter(s) until a conclusive decision can be made.

The SCOPE property

It is possible to set the SCOPE property. Currently, this has only one possibility: SCOPE: PROJECT. This signifies that for the entire project, a dynamic ruleset should only be selected once, on the basis of the first file passed to TICS. If the parameter is not set, then TICS will select this for each file individually.

Note that currently the SCOPE parameter needs to be set to PROJECT in the case you are using a dynamic ruleset to analyze Abstract Interpretation or Security. This is because for these metrics, analysis is typically done on project level for an entire language. Therefore, the analysis done and the violations reported should be for the same ruleset from this project.

The RULESETS property

Each rules.json should contain at least two rulesets in the RULESETS property. These are the subrulesets of the dynamic ruleset. Of these subrulesets, the following properties are always mandatory, regardless of which selection parameter is used:

Furthermore, each ruleset can also have optional properties for configuring location and rules for a coding standard viewer. Like at the ruleset level in SERVER.yaml/PROJECTS.yaml, this is done with the DOCNAME, DOCSUF, SEP and ANCHOR properties. An explanation of these properties is given earlier in this document.

Note that subrulesets will inherit the coding standard viewer settings of their parent ruleset unless an override is specified within the subruleset.

Configuring for COMPILERVERSION

In the case a dynamic ruleset is made to select on compiler versions, then TICS will go through all the rulesets from top to bottom. If a REQUIREDVERSION property is present for the compiler used and the version of the compiler is equal to or higher than the REQUIREDVERSION, it will use that ruleset. Since different compilers might be used, it is also necessary to specify the version for individual compilers. So in the end, the contents end up looking as follows:

"REQUIREDVERSION": {
  "Gcc": "4.9",
  "VC": "10"
}

Example: A Pre-C++11 and Post-C++11 ruleset

One of the most common cases is splitting up the C++ ruleset by Pre-11 and Post-11 dependent on whether the compilers used support C++11 or not. Let's give the rules.json for this setup in its entirety, and explain what all of these properties do.

{
  "SELECTION": ["COMPILERVERSION"],
  "RULESETS": [
    {
      "IMPLDIR": "cpptest_pre11",
      "NAME": "Cpp Pre-11 Coding Standard",
      "DIR": "CPP-PRE11",
      "DOCNAME": "https://csviewer.tiobe.com/api/redirect?setid=36545283-5fc1-485c-ab92-08635db7b7d9&"
    },
    {
      "IMPLDIR" : "cpptest",
      "NAME": "Cpp Post-11 Coding Standard",
      "DIR": "CPP-POST11",
      "DOCNAME": "https://csviewer.tiobe.com/api/redirect?setid=778E38D6-1B0D-4A7C-AF05-02E9456740A3&",
      "REQUIREDVERSION": {
        "Gcc": "4.9",
        "VC": "10"
      }
    }
  ]
}

So, if a C++ file is passed to TICS, and it is analyzed with Gcc 4.8, it will now pick the Pre-11 Coding Standard ruleset. If it has been analyzed with Gcc 4.9, it will pick the Post-11 Coding Standard ruleset.

Configuring for PYTHONVERSION

Each subruleset should be marked with a REQUIREDVERSION property for PYTHON. TICS will then detect which Python version is used for running the Python tools (eg. pylint), and use the correct subruleset dependent on the Python version that has been found.

There is no default choice of subruleset as Python should always be available if a Python checker is used.

Example: A Python 2 and 3 ruleset

{
  "SELECTION": ["PYTHONVERSION"],
  "SCOPE": "PROJECT",
  "RULESETS": [
    {
      "IMPLDIR": "",
      "NAME": "Python3 Coding Standard",
      "DIR": "PYTHON3",
	  "REQUIREDVERSION": {
        "PYTHON": "3",
      }
    },
    {
      "IMPLDIR" : "",
      "NAME": "Python2 Coding Standard",
      "DIR": "PYTHON2",
      "REQUIREDVERSION": {
        "PYTHON": "2",
      }
    }
  ]
}

Configuring for RULESETTARGET

In the case your code is often compiled for different targets and the compiler settings used depend heavily on these targets, another logical split to make in your ruleset is to split analysis by target. This is done by specifying a list of targets in RULESETTARGET for which a subruleset is applicable. If a target is used for a file, then TICS will pick that subruleset.

  "RULESETS": [
    ...
    {
      ...
      "RULESETTARGET": ["x86", "x86_linux"],
    }
  ]

One ruleset is allowed to not have any targets; this is then the default choice. If no targets match on the other rulesets then this will be the ruleset used.

Example: Different rulesets depending on the compiler target

{
  "SELECTION": ["RULESETTARGET"],
  "RULESETS": [
    {
      "IMPLDIR": "cpptest_pre11",
      "NAME": "Cpp Pre-11 Coding Standard",
      "DIR": "CPP-PRE11",
      "DOCNAME": "https://csviewer.tiobe.com/api/redirect?setid=36545283-5fc1-485c-ab92-08635db7b7d9&"
    },
    {
      "IMPLDIR" : "cpptest",
      "NAME": "Cpp Post-11 Coding Standard",
      "DIR": "CPP-POST11",
      "DOCNAME": "https://csviewer.tiobe.com/api/redirect?setid=778E38D6-1B0D-4A7C-AF05-02E9456740A3&",
      "RULESETTARGET: ["x86", "x86_linux"],
    }
  ]
}

Configuring for SCOPE

Sometimes, the code archive is neatly separated by compatibility, because one part of the archive has been migrated to a newer version while the other part hasn't had this migration yet. In that case a subruleset can be picked dependent on scope.

To mark a certain scope of the archive to use a subruleset, use the SCOPEPARAMS property. This contains two subproperties: SCOPE is mandatory and is a regular expression which matches the scope of the subruleset, while PROJECTS is optional and specifies for which projects this scope is valid. To give an example of a subruleset:

{
  "IMPLDIR" : "cpptest",
  "NAME": "Cpp Post-11 Coding Standard",
  "DIR": "CPP-POST11",
  "DOCNAME": "https://csviewer.tiobe.com/api/redirect?setid=778E38D6-1B0D-4A7C-AF05-02E9456740A3&",
  "SCOPEPARAMS": {
    "SCOPE": "unit_already_migrated"
  }
}

One ruleset is allowed to not have any scope; this is then the default choice. If no scopes match on the other rulesets then this will be the ruleset used.

Example: Different rulesets depending on the scope

{
  "SELECTION": ["SCOPE"],
  "RULESETS": [
    {
      "IMPLDIR": "cpptest_pre11",
      "NAME": "Cpp Pre-11 Coding Standard",
      "DIR": "CPP-PRE11",
      "DOCNAME": "https://csviewer.tiobe.com/api/redirect?setid=36545283-5fc1-485c-ab92-08635db7b7d9&"
    },
    {
      "IMPLDIR" : "cpptest",
      "NAME": "Cpp Post-11 Coding Standard",
      "DIR": "CPP-POST11",
      "DOCNAME": "https://csviewer.tiobe.com/api/redirect?setid=778E38D6-1B0D-4A7C-AF05-02E9456740A3&",
      "SCOPEPARAMS": {
        "SCOPE": "unit_already_migrated"
      }
    }
  ]
}