4.5. Metrics

4.5.1. Introduction

This page describes metrics that can be calculated by the Axivion Suite. All metrics are generally available for all supported languages, if not specified explicitly otherwise. Metrics measuring specific language features can only produce meaningful results for languages that support said features.

4.5.2. Metrics based on counting Elements of the System

This section describes metrics counting different aspects of the system like lines of code, tokens, statements or routine invocations.

4.5.2.1. Metrics based on Line and Token Counts

Metric.Lines.LOC

Counts the lines of code of the routine or file. For functions it is starting at the beginning of the signature, and ending at the line where the closing curly brace is located.

Metric.Lines.Code

Counts lines within the function or file that contain source code tokens.

Note

In addition to Metric.Lines.LOC and Metric.Lines.Code, there are alternative versions of these metrics limited to either function or file scope.

Metric.Lines.Empty

Count of lines within the function or file that are completely empty (whitespaces allowed).

Metric.Lines.Comment

Counts the lines within the function or file that contain a comment.

Metric.Lines.Only_Comment

Counts the lines within the function or file that contain only comments (but no other language tokens).

Note

In addition to Metric.Lines.Comment and Metric.Lines.Only_Comment, there are alternative versions of these metrics limited to either function or file scope.

Metric.Lines.Fixme, Metric.Lines.Hack, Metric.Lines.Todo, Metric.Lines.XXX

Counts lines that contain one or more times the token Fixme resp. Hack resp. Todo resp. XXX (inside a comment or as language token). The lookup itself is case insensitive, so that Fixme, FIXME and fixme are all accounted for by the Metric.Lines.Fixme metric. The names of the metric attributes are case sensitive.

The following example illustrates the computation of the line-based and token-based metrics:

// Copyright (C) 2025 Axivion GmbH
// Copyright (C) 2025-2026 The Qt Company GmbH, a subsidiary of The Qt Group
// https://www.qt.io
//
// Example for
// Metric.Lines.LOC
// Metric.Lines.File.LOC
// Metric.Lines.Routine.LOC
// Metric.Lines.Code
// Metric.Lines.Empty
// Metric.Lines.{FIXME, HACK, TODO, XXX}

void doit(int x)
{

    //FIXME use correct value here.
    int y = 2*x;

    while (y > 0)
    {

        // TODO: add corrective factor
        x = x+y;

    }
    // toDO: condition check
    // HACK: use recursion here
    doit(x-1);

}
// Metric values for doit:
// Metric.Lines.LOC = 18
// Metric.Lines.Routine.LOC = 18
// Metric.Lines.Code = 9
// Metric.Lines.Empty = 5
// Metric.Lines.FIXME = 1
// Metric.Lines.HACK = 1
// Metric.Lines.Todo = 2
// Metric.Lines.XXX = 0


// Metric.Lines.LOC = 43 (entire file)
// Metric.Lines.File.LOC = 43 (entire file)

Available rules:

4.5.2.2. Metrics counting Comment sections

For counting comment sections the following rules are applied:

  • Each comment enclosed by /* and */ is counted as a single comment section.

  • A sequence of //-comments counts as one comment section if they are not separated by a non-comment element or a newline. So in the following example

    // This
    // is
    // one comment section.
    int x = 0;
    

    only one comment section is counted; however in the next example

    // These
    // are
    
    // two comment sections.
    int x = 0;
    

    two comment sections are counted, as are in this example:

    // These
    int y = 1; // are
    // two comment sections.
    int x = 0;
    

Metric.Comment.Preceding

Counts the number of comments directly before the routine header that do not share a line with another non-comment code construct. Roughly speaking, comments that are located directly before a routine (i.e., no other code is between them and the beginning of the routine signature) are considered as preceding comments. Preceding preprocessor conditionals (like #if ..., #else ..., #ifdef ... etc.) between comments and routines are skipped if a comment is in the line directly above the conditional. In such a case however, only this immediately preceding comment C and following comments are considered as preceding comments for following entities; any comments before the comment C are not considered preceding comments (see the routines doit_c() and doit_d() in the following examples).

Metric.Comment.Internal

Counts the number of comments within the body of a routine.

Metric.Comment.Density

Computes the comment density (also called comment frequency) of a routine, which roughly corresponds to the ration between comments before and within the routine and the number of statements of the routine; however, all comments before the routine are treated as one comment. Its values coincide with the values of Metric.HIS.COMF. For a given routine r, let st(r) be the number of statements of the routine. Let p(r) be the number of comments before the routine, i.e., the value Metric.Comment.Preceding, and let i(r) be the number of comments within the routine, i.e., the value Metric.Comment.Internal. Then the comment density d(r) of routine r is computed as d(r) = (p(r)+i(r))/st(r) if st(r) > 0. If st(r) = 0, no metric value for the comment density is annotated for the routine in question.

Consider the following example:

// Copyright (C) 2025 Axivion GmbH
// Copyright (C) 2025 The Qt Company GmbH, a subsidiary of The Qt Group
// https://www.qt.io

//Example for
//Metric.Comment.Preceding, 
//Metric.Comment.Internal, 
//Metric.Comment.Density (= Metric.HIS.COMF)

int doit_a()
{
    return 1;
}

int x = 1; // global variable
/* Another comment */

int doit_b()
{
    // a  comment
    int local_x = 1; /* another one */
    /* a third */
    int y = 0;
    y = local_x + 1;
    // fourth comment
    // continued
    return y;
}

#define A 1

/* description of another_function() */
#if A
int doit_c()
{
    int x = 1;
    // Comment
    int y = 2;
    return x+y;
}
#endif


#if B

/* A description */
#else
/* more description */
int doit_d()
{
    int x = 1;
    // Comment
    int y = 2;
    return 1;
}
#endif

The computation of metric values for Metric.Comment.Preceding and Metric.Comment.Internal can be influenced by options of the corresponding rules. If the metrics are computed using the default settings, we obtain the following values for the metrics:

  • Routine doit_a(): it has 0 as value for Metric.Comment.Preceding. The initial comment is counted as a header comment in the default settings and thus is not counted as a preceding comment of the function. The value for Metric.Comment.Internal is 0, since no comments are within the routine body. This gives also a value of 0 for the metric Metric.Comment.Density. The value of Metric.Lines.Comment is 0, as is the value of Metric.Lines.Only_Comment.

  • Routine doit_b(): it has 1 as value for Metric.Comment.Preceding, since the comment /* Another comment */ is counted. The value of Metric.Comment.Internal is 4, and so Metric.Comment.Density is 1.25, since there are four statements in the function body. The value of Metric.Lines.Comment is 4, and the value of Metric.Lines.Only_Comment is 3.

  • Routine doit_c(): it has 1 as value for Metric.Comment.Preceding. The conditional #if A does not separate the comment from the signature of the routine. Its value for Metric.Comment.Internal is 1, and so the value of Metric.Comment.Density sums up to (1+1)/3 = 2/3, since there are three statements in the function body. The value of Metric.Lines.Comment is 1, as is the value of Metric.Lines.Only_Comment.

  • Routine doit_d(): it has 2 as value for Metric.Comment.Preceding. Only the comment directly above #else and the comment between #else and the signature of doit_d() are counted as preceding comments. Its value for Metric.Comment.Internal is 1, and so the value of Metric.Comment.Density sums up to (2+1)/3 = 1, since there are three statements in the function body. The value of Metric.Lines.Comment is 1, as is the value of Metric.Lines.Only_Comment.

Available rules:

4.5.2.3. Statement-Based Metrics

Metric.Number_Of_Statements

Count of all statements in routine.

Note

The metric values might be influenced by configuration values. Please refer to Options influencing the concrete metric values for an example.

Metric.Number_Of_Gotos

Count of goto-statements in routine. Corresponds to the metric Metric.HIS.GOTO.

Note

The metric values might be influenced by configuration values. Please refer to Options influencing the concrete metric values for an example.

Metric.Number_Of_Returns

Count of return statements in routine. Corresponds to the metric Metric.HIS.RETURN.

Note

The metric values might be influenced by configuration values. Please refer to Options influencing the concrete metric values for an example.

In the following we give an example for all three statement-based metrics:

// Copyright (C) 2025 Axivion GmbH
// Copyright (C) 2025 The Qt Company GmbH, a subsidiary of The Qt Group
// https://www.qt.io

// Example for
// Metric.Number_Of_Statements
// Metric.Number_Of_Gotos (= Metric.HIS.GOTO)
// Metric.Number_Of_Returns (= Metric.HIS.RETURN)

int doit(int x)
{
    if (x <= 1)
    // +1 Metric.Number_Of_Statements
    {
        return 1;
        // +1 Metric.Number_Of_Statements
        // +1 Metric.Number_Of_Returns
    }
L1:
    x -= 2;
    // +1 Metric.Number_Of_Statements
    if (x > 2)
        // +1 Metric.Number_Of_Statements
    {
        goto L1;
        // +1 Metric.Number_Of_Statements
        // +1 Metric.Number_Of_Gotos
    }
    return x;
    // +1 Metric.Number_Of_Statements
    // +1 Metric.Number_Of_Returns
}
// Metric.Number_Of_Statements = 6
// Metric.Number_Of_Returns = 2
// Metric.Number_Of_Gotos = 1

Available rules:

4.5.2.4. Metrics Based on Routine Invocations

Metric.Number_Of_Invocations

Counts number of different calls within a routine.

Note

The metric values might be influenced by configuration values. Please refer to Options influencing the concrete metric values for an example.

Metric.Number_Of_Calling_Routines

Counts for a given routine R the number of distinct routines that call R.

Note

The metric values might be influenced by configuration values. Please refer to Options influencing the concrete metric values for an example.

Metric.Number_Of_Called_Routines

Counts for a given routine R the number of distinct routines that R calls.

Note

The metric values might be influenced by configuration values. Please refer to Options influencing the concrete metric values for an example.

Metric.Number_Of_Parameters

Number of formal parameters for a given routine R. Note that this number includes types in parameters lists that are not actually represented in the dependency graph, such as primitive types like int.

Note

The metric values might be influenced by configuration values. Please refer to Options influencing the concrete metric values for an example.

We give an example using four different functions:

// Copyright (C) 2025 Axivion GmbH
// Copyright (C) 2025 The Qt Company GmbH, a subsidiary of The Qt Group
// https://www.qt.io

// Example for Metric.Number_Of_Invocations, 
// Metric.Number_Of_Calling_Routines
// Metric.Number_Of_Called_Routines

void a()
{}
// Metric.Number_Of_Invocations = 0
// Metric.Number_Of_Calling_Routines = 2 (c() and main())
// Metric.Number_Of_Called_Routines = 0 

void b()
{}
// Metric.Number_Of_Invocations = 0
// Metric.Number_Of_Calling_Routines = 1 (c())
// Metric.Number_Of_Called_Routines = 0

void c()
{
    a();
    b();
    a();
}
// Metric.Number_Of_Invocations = 3 (2 times a(), one time b())
// Metric.Number_Of_Calling_Routines = 1 (main())
// Metric.Number_Of_Called_Routines = 2 (a() and b())

int main(void)
{
    a();
    a();
    c();
    a();
}
// Metric.Number_Of_Invocations = 4 (3 times a(), one time c())
// Metric.Number_Of_Calling_Routines = 1 (artificial entry call)
// Metric.Number_Of_Called_Routines = 2 (a() and c())

Available rules:

4.5.3. Fan Metrics

Metric.Fan.In.Call

Number of incoming edges of type edge-call Call.

Metric.Fan.Out.Call

Number of outgoing edges of type edge-call Call.

The rules Metric-Fan.In.Call and Metric-Fan.Out.Call both support calculating the metrics for a different edge types by changing the edge_type option. Setting edge_type=T, where T is a valid edge type, will also change the name of the reported metric to Metric.Fan.In.T and Metric.Fan.Out.T respectively.

Metric.StabilityIndex

Measures the stability of components based on their fan-in and fan-out values.

Available rules:

  • FanMetrics: Metrics to count the number of incoming or outgoing edges.

4.5.4. Metrics based on Code Complexity

This section describes metric to measure source code complexity.

4.5.4.1. Metrics for Nesting

Metric.Maximum_Nesting

Counts the maximum nesting level of syntactic levels inside a routine.

Metric.Maximum_Extended_Nesting

Metric.Maximum_Extended_Nesting differs from Metric.Maximum_Nesting by its treatment of else if-statements: Metric.Maximum_Extended_Nesting increases the nesting level by one for each else if-statement.

The following example illustrates the computation of the nesting metrics:

// Copyright (C) 2025 Axivion GmbH
// Copyright (C) 2025 The Qt Company GmbH, a subsidiary of The Qt Group
// https://www.qt.io

// Example for
// Metric.Maximum_Nesting,
// Metric.Maximum_Extended_Nesting

int doit(int x)
{
  // Nesting depth 1
  if (x >= 1)
  {
     // Nesting depth 2
     while (x > 0)
     {
         // Nesting depth 3
         // (extremal value for Maximum_Nesting)
         x--;
     }
     return x;
  }
  else if (x <= -5)
  {
      // Nesting depth 3 for Maximum_Extended_Nesting
      // Nesting depth 2 for Maximum_Nesting
      return 2;
  }
  else if (x <= -2)
  {
      // Nesting depth 4 for Maximum_Extended_Nesting (extremal value)
      // Nesting depth 2 for Maximum_Nesting
      return 2;
  }
  else
  {
     // Nesting depth 2 
     return 1;
  }
}

// Metric.Maximum_Nesting = 3
// Metric.Maximum_Extended_Nesting = 4

Note

The metric values might be influenced by configuration values. Please refer to Options influencing the concrete metric values for an example.

Available rules:

4.5.4.2. Variants of Cyclomatic (McCabe) Complexities

Metric.McCabe_Complexity

Computes the number of nodes in the control flow graph of a routine that have more than one outgoing edge.

For nodes originating in switch-statements, each nonempty case section is counted. It is also known as cyclomatic complexity; its value is identical to the one given by metric Metric.HIS.vG.

Roughly speaking, the value Metric.McCabe_Complexity is computed by summing up

  • the number of if-statements,

  • the number of loop statements (e.g. for and while),

  • the number of conditional ( ?) operators,

  • the number of catch clauses, and

  • the number of nonempty case-sections

  • plus the value one.

So Metric.McCabe_Complexity is always greater zero.

Note

The metric values might be influenced by configuration values. Please refer to Options influencing the concrete metric values for an example.

Metric.Extended_McCabe_Complexity

Metric.Extended_McCabe_Complexity differs from Metric.McCabe_Complexity in its treatment of short circuit logical operators: it also interprets these operators as resulting in control flow nodes having more than one outgoing edge and therefore adds also the number of shortcut operators ( && and ||) to the value.

Note

The metric values might be influenced by configuration values. Please refer to Options influencing the concrete metric values for an example.

Metric.Switch_Complexity

A third variant, Metric.Switch_Complexity, does not count short circuit logical operators. In addition, the case-sections of a switch-section are not counted. Instead, each occurring switch-statement as a whole increases the value of Metric.Switch_Complexity by one.

Consider the following example:

void some_function(void)
{
   if (a && b) {
      switch (c) {
         case 1: /* ... */ break;
         case 2: /* ... */ break;
         case 3: /* ... */ break;
         default: /* ... */ ;
      }
   }
   while (a || b) {
      // ...
   }
}

Here the value of Metric.Switch_Complexity is 4, the value of Metric.McCabe_Complexity is 7, and the value of Metric.Extended_McCabe_Complexity is 9.

Another example, explaining the computation of Metric.McCabe_Complexity in more detail:

// Copyright (C) 2025 Axivion GmbH
// Copyright (C) 2025 The Qt Company GmbH, a subsidiary of The Qt Group
// https://www.qt.io

// Example for
// cyclomatic complexity
// (= Metric.McCabe Complexity = Metric.HIS.VG),
// Metric.Extended_McCabe_Complexity,
// Metric.Switch_Complexity

int foo(int x, int y)
{
  // start value = 1 (1 path)
  
  while (x > y) // +1: 2 successors: to loop body and exit
  {
      if (x)  // +1: 2 successors, then and (implicit) else 
      {
	  x = x+1;
	  if (y)  // +1: 2 successors, then and else
	  {
	      x++;
	  }
	  else
	  {
	      x--;
	  }
      }
  }
  
  y = (x > 0) ? 1 : 2;  // + 1: 2 successors (like if)
  switch(x) 
  {
      case 1:        // + 1: one nonempty switch case
          break;
      case 2:        // +0: this is just a fallthrough, does not count
      case 3:        // +1: this is a nonempty switch case
          break;
      case 4:        // +1: one nonempty switch case
          break;
      default:       // +1: default case
          break;
  }
  // +1 ONLY for Metric.Switch_Complexity (for entire switch statement)
  
  x = y && x; // +1 ONLY for Metric.Extended_McCabe_Complexity
  
  // Note: the shortcut operator ("&&" or "||") actually leads
  // to a branching control flow section,
  // however, Metric.McCabe_Complexity does not count
  // this branching, only Metric.Extended_McCabe_Complexity does.
    
  // Metric.McCabe_Complexity = 1+1+1+1+1+(1+1+1+1) = 9
  // Metric.Extended_McCabe_Complexity = 1+1+1+1+1+(1+1+1+1)+1 = 10
  // Metric.Switch_Complexity = 1+1+1+1+1+1 = 6
  return x;
}

Note

The metric values might be influenced by configuration values. Please refer to  Options influencing the concrete metric values for an example.

Available rules:

4.5.4.3. Other Control-Flow-Based Metrics

Metric.NPath

Computes the number of maximal acyclic execution paths within a function. It corresponds to the metric Metric.HIS.PATH. The rules for each syntactical construct can be found in “NPATH: a measure of execution path complexity and its applications” (Brian A. Nejmeh, Communications of the ACM, Volume 31 Issue 2, Feb. 1988).

Caution

The values of this metric can be very high and will be cut off at 2147483647 (the highest possible value for any integer metric in the RFG). See also Metric.LogNPath_Floor resp. Metric.LogNPath_Ceiling for logarithmic representations of NPATH.

A function consisting of only a simple if-statement, like in the following example:

void f(int input)
{
  if (input) { printf("A"); }
}

has an Metric.NPath value of 2, corresponding to two possible maximal acyclic paths in the function: one printing A and the other not printing anything at all. Consider the following function:

void g(int input1, int input2)
{
  if (input1) { printf("1"); }
  if (input2) { printf("2"); }
}

Here the value of Metric.NPath is 4: One maximal acyclic execution path prints 1, 2, another prints 1, another one prints 2, and one path does not print anything at all.

The following example shows how the metric value can be computed by examining each code construct (referred to as NP in the comments):

// Copyright (C) 2025 Axivion GmbH
// Copyright (C) 2025 The Qt Company GmbH, a subsidiary of The Qt Group
// https://www.qt.io

// Example for
// Metric.NPATH (= Metric.HIS.PATH)

void doit_1(int test1, int test2, int test3, int test4)
{
    
   if(test1)
   {}
   else
   {}
   // NP = NP(if-range) + NP(if-expr) + NP(else-range) = 1+0+1 = 2;
   
   if(test1)
   {}
   else
   {}
   // NP = OLD_NP * (NP(if-range) + NP(if-expr) + NP(else-range))
   // = 2 * 2 = 4

   if (test1)
   {}
   // NP = OLD_NP * (NP(if-range) + 1) = 4 * 2 = 8

   if (test1)
   {}       
   // NP = OLD_NP * (NP(if-range) + 1) = 8 * 2 = 16

   if (test1)
   {
       if  (test1)
       {}
      // NP for inner part: 2
    }
    // NP = OLD_NP * (NP(if-range) + 1) =  16 * (2+1) = 48

   // Metric.NPATH = 48
}

For computing Metric.NPath for a statement if expr { then-part } else { else-part }, the metric values obtained for the expr, then-part, else-part have to be summed up. For two subsequent statements statement1 statement2, the metric values for statement1 and statement2 have to be multiplied (in the example, the metric value for all statements before the current statement is referred to as OLD_NP. Therefore, many branching statements like if-statements or loops lead to a large value of Metric.NPath.

Metric.LogNPath_Floor, Metric.LogNPath_Ceiling

If n denotes the number of maximal acyclic execution paths within a function (see Metric.NPath), the metric

  • Metric.LogNPath_Floor computes log₂(n) rounded down to the nearest integer and

  • Metric.LogNPath_Ceiling computes log₂(n) rounded up to the nearest integer.

If n surpasses a value of approximately 10³⁰⁸, the value 2147483647 is stored as value for both Metric.NPath.LogNPath_Floor and Metric.LogNPath_Ceiling.

Metric.CognitiveComplexity

Measures complexity counting on the use of Control-Flow influencing statements with consideration of the depth of their nesting. Also taking recursive calls into account.

Note

The metric values might be influenced by configuration values. Please refer to Options influencing the concrete metric values for an example.

Available rules:

4.5.5. Metrics for Object-Oriented-Systems

This section describes metrics specifically designed for Object-Oriented-Systems.

Metric (Attribute)

Description

Lack of cohesion in methods (LCOM) ( Metric.OO.LCOM)

Count of function member pairs of a class that have no similarity regarding the common use of data members.

Lack of cohesion in methods (LCOM) ( Metric.OO.LCOM4)

Alternative version of LCOM based on the definition by Hitz and Montazeri.

Lack of cohesion in non-static methods (LCOMs) ( Metric.OO.LCOMs)

Count of function member pairs of a class that have no similarity regarding the common use of data members. Do not count static data members.

Coupling between objects (CBO) ( Metric.OO.CBO)

Number of classes that are coupled to a class C, i.e., the size of the union set of classes that are using fields and methods of C and the classes that are used by C’s fields and methods (so both directions count here).

Coupling between objects (excluding ancestors) (CBOa) ( Metric.OO.CBOa)

Like CBO but ancestor classes of a class are not taken into account.

Coupling between objects (excluding statics) (CBOs) ( Metric.OO.CBOs)

Like CBO but static data members are not taken into account when looking at the dependencies to and from other classes.

Data Abstraction Coupling (DAC) ( Metric.OO.DAC)

Number of attributes in a class that have another class as their type.

Response for a class (RFC) ( Metric.OO.RFC)

The response of a class is the count of methods of the class itself and all methods/functions called by the methods of the class (i.e., we take one level of the call tree into account). Inherited but not overridden methods are not taken into account; the potential fan out generated by calls to virtual functions is also not taken into account.

Depth of Inheritance (DIT) ( Metric.OO.DIT)

DIT of a class is the length of the longest path from class to the root in the inheritance hierarchy. Classes that do not inherit anything have a DIT of 0.

Weighted Methods of a Class (WMC) ( Metric.OO.WMC.One/ Metric.OO.WMC.McCabe_Complexity)

Sum over all weighted defined or overridden methods of a class where 1 / the metric Metric.OO.McCabe_Complexity is used as weight. Inherited methods are not counted.

Number of Child Classes (NOCC) ( Metric.OO.NOCC)

Number of classes that directly inherit from a given class.

Example:

class A {};
class B {};
class C : A, B {};
class D : A {};
class E : C, D {};

For this example, class A has NOCC of 2 (classes C and D), class B has NOCC of 1 (class C is its only child), classes C and D have a NOCC of 1 (class E is their only child). Class E has a NOCC of 0 (no children at all).

Number Of Ancestors (NOA) ( Metric.OO.NOA)

Number of classes that a given class directly or indirectly inherits from.

For the example code at NOCC, class A and B have no ancestors, so NOA is 0. Class C has ancestors A and B, so NOA is 2. Class D has only one ancestor A, so NOA is 1. Class E has ancestors A, B, C, and D, so NOA is 4.

Number of Parent Classes (NOPC) ( Metric.OO.NOPC)

Number of classes that a given class directly inherits from.

For the example code at NOCC, classes A and B have no parent classes, class D has one direct parent (class A), and classes C and E have two direct parents.

Number of Added Methods (NOMA) ( Metric.OO.NOMA)

Number of function members (including operators, constructors, and destructors) that a given class adds.

Number of Methods Overridden (NOMO) ( Metric.OO.NOMO)

Number of function members that a given class overrides.

Number of Invocations (NIVOC) ( Metric.OO.NIVOC)

Calculates the number of routines called inside a routine.

Available rules:

  • OOPMetrics: Metrics for object-oriented systems.

4.5.6. Metrics based on common Rulesets

This section describes metrics from common rulesets like Halstead and HIS.

4.5.6.1. Halstead Metrics

Halstead metrics have been introduced in “Elements of software science” (M. H. Halstead, Elsevier, 1977). All Halstead metrics are computed using the following four base metrics:

  • Metric.Halstead.Total_Operators:
    the total number of occurring operators within a code entity, which we abbreviate by N₁,
  • Metric.Halstead.Total_Operands:
    the total number of occurring operands within a code entity, which we abbreviate by N₂,
  • Metric.Halstead.Different_Operators:
    the number of distinct operators within a code entity:, which we abbreviate by η₁,
  • Metric.Halstead.Different_Operands:
    the total number of occurring operands within a code entity, which we abbreviate by η₂.

The terms operands and operators are not precisely defined in the literature, and so it is to the implementor to find a suitable interpretation for them. This has to be taken into account when e.g. using a threshold for one of the metrics. Most importantly, the Axivion Suite considers “ {“, “ }“, “ (“, ” )“, “ [“, “ ] as well as “ ;“ and “ ,” as (distinct) operators. So, the code piece

void foo()
{
   if (1) {} /* A comment. */
}

contains

  • the operators “ {“ (2 times), “ }“ (2 times), “ (“ (2 times), “ )“ (2 times), “ if“ (1 time), “ void“ (1 time)

  • and the operands “ 1“ (1 time) and “ foo (1 time)”.

Therefore N₁ = 10, N₂ = 2, η₁=6, η₂=2.

The Axivion Suite works on token-level to compute the number of operators and operands; each token is considered an operator, an operand, or neither. More precisely, the following entities are counted as operands:

  • literals, in particular character literals, float literals, integer literals, string literals, and

  • identifier.

Operators comprise all other tokens with the exception of preprocessor directives (like #if etc.). Comments are not taken into account at all. The operator_blacklist option of the HalsteadMetrics rulegroup can be used to to configure tokens that should not be treated as operators by the Halstead metrics.

The following metrics are computed using the base metrics above for files, types, and routines:

  1. Metric.Halstead.Difficulty, defined by D := η₁/2 · N₂/η₂,

  2. Metric.Halstead.Length (program length), defined by N := N₁ + N₂,

  3. Metric.Halstead.Vocabulary, defined by η:= η₁ + η₂,

  4. Metric.Halstead.Volume, defined by V := N · log₂(η),

  5. Metric.Halstead.Effort, defined by E := D · V,

  6. Metric.Halstead.Vocabulary_Frequency [1], defined by F := N/η.

Available rules:

4.5.6.2. HIS Metrics

The HIS Source Code metrics (version 1.3.1), a document published by the Hersteller Initiative Software, describes a collection of metrics together with recommendations for their admissible ranges. In the following we call these metrics HIS metrics. They are widely used, particularly in the automotive industry. We describe which HIS metrics the Axivion Suite supports, and how HIS metrics can be mapped to base metrics computed by the Axivion Suite. For example, the COMF metric corresponds to the base metric Metric.Comment.Density. The value of Metric.HIS.COMF at a fixed RFG node n is the same as the value of Metric.Comment.Density of n. Unfortunately, the definition of HIS metrics in the reference document is not too precise, so sometimes one has to choose between two possible base metrics.

HIS metric

Corresponding base metric

Alternative attribute name

COMF

Metric.Comment.Density

Metric.HIS.COMF

PATH

Metric.NPath

Metric.HIS.PATH

GOTO

Metric.Number_Of_Gotos

Metric.HIS.GOTO

v(G)

Metric.McCabe_Complexity (default) or Metric.Extended_McCabe

Metric.HIS.VG

CALLING

Metric.Number_Of_Calling_Routines

Metric.HIS.CALLING

CALLS

Metric.Number_Of_Called_Routines

Metric.HIS.CALLS

PARAM

Metric.Number_Of_Parameters

Metric.HIS.PARAM

STMT

Metric.Number_Of_Statements

Metric.HIS.STMT

LEVEL

Metric.Maximum_Nesting (default) or Metric.Maximum_Extended_Nesting

Metric.HIS.LEVEL

RETURN

Metric.Number_Of_Returns

Metric.HIS.RETURN

VOCF

Metric.Halstead.Vocabulary_Frequency

Metric.HIS.VOCF

HIS metric ap_cg_cycle:

This metric counts the number of call-graph recursions. Its value is given in the HIS metrics report.

HIS metrics NOMV and NOMVPR:

These metrics count the numbers of style violations reported for rules of the MISRA:C standard.

Differential HIS metrics:

These HIS metrics compute differences between two versions of the software. They are not yet supported by the Axivion Suite .

Available rules:

  • HISMetrics: Hersteller Initiative Software metrics

4.5.6.3. Code-Quality-Management Metrics

The Code-Quality-Management metrics, henceforth referred to as CQM metrics, are defined in the 2006 book of the same name by Dr. Frank Simon, Dipl.-Inform Olaf Seng and Dipl.-Inform. Thomas Mohaupt. The metrics offer a comprehensive list to measure various source code characteristics based around complexity and maintainability.

Due to the nature of the metrics, they are partially implemented as Stylecheck rules to report the indivitual violations.

Available rules:

  • CQMMetrics: Code-Quality-Management metrics. (Rules requiring an IR are C/C++ only.)

  • CQM: Code-Quality-Management Stylechecks. (Rules requiring an IR are C/C++ only.)

4.5.7. Configuration Options

The following configuration options are common to almost all metric rules with the exception of a few special rules that include results from other sources or count the number of violations.

4.5.7.1. Common Configuration Options

  • Base view (base_view_name): The view on which the metric is calculated on and values are stored as node attributes in.

  • Minimal value (min_value) and Maximum value (max_value): The range of allowed values for the metric. All values outside of this range are reported as metric findings.

  • Excluded node types (excluded_node_types): RFG node types the metric should not report findings on.

  • Report all values (report_all_values): Whether all metric values should be reported or only ones outside the range between min_value and max_value.

  • Display name (display_name): Description of the metric shown in the dashboard.

  • RFG metric (rfg_metric_name): Name of the node attribute storing the metric value in the RFG. Split at each . and common parts are merged together in the RFG.

4.5.7.2. Options influencing the concrete metric values

Several configuration values have an impact on how code constructs are counted for particular metrics. In this section we describe these effects in detail.

Value Metrics.opaque_macros:

if this configuration value is set to true, elements of opaque macro invocations are excluded from the computation for the following metrics:

  • Metric.Number_Of_Statements (also named Metric.HIS.STMT),

  • Metric.Number_Of_Gotos (also named Metric.HIS.GOTO) ,

  • Metric.Number_Of_Returns (also named Metric.HIS.RETURN),

  • Metric.Number_Of_Invocations,

  • Metric.McCabe_Complexity (also named Metric.HIS.VG) / Metric.Extended_McCabe,

  • Metric.Switch_Complexity,

  • Metric.CognitiveComplexity,

  • Metric.Maximum_Nesting or Metric.Maximum_Extended_Nesting (also named Metric.HIS.LEVEL), and

  • Metric.NPath (also named Metric.HIS.PATH).

A macro is opaque if it is included in one of the globbing patterns given by the rule option Metrics.macro_library_patterns. If that option is left empty, all macros are considered opaque. Consider the following example:

// Copyright (C) 2025 Axivion GmbH
// Copyright (C) 2025 The Qt Company GmbH, a subsidiary of The Qt Group
// https://www.qt.io

int global = 1;

#define INVOKE(x) { if (global>x) { f1(); } else { return 1; }}

void f1() {}

int main(void)
{
    INVOKE(20);
    INVOKE(30);
    return 0;
}

If INVOKE is an opaque macro and Metrics.opaque_macros is set to true, the metric values for main() are computed similar to the following program:

// Copyright (C) 2025 Axivion GmbH
// Copyright (C) 2025 The Qt Company GmbH, a subsidiary of The Qt Group
// https://www.qt.io

int global = 1;

extern void _invoke(int x);

void f1() {}

int main(void)
{
    _invoke(20);
    _invoke(30);
    return 0;
}

// Metric values:
// Metric.Number_Of_Statements / Metric.HIS.STMT = 3
// Metric.Number_Of_Gotos / Metric.HIS.GOTO  = 0
// Metric.Number_Of_Returns / Metric.HIS.RETURN = 1
// Metric.Number_Of_Invocations = 2
// Metric.McCabe_Complexity /  Metric.HIS.VG = 1
// Metric.Switch_Complexity = 1
// Metric.Maximum_Nesting = 1
// Metric.Maximum_Extended_Nesting / Metrich.HIS.LEVEL = 1
// Metric.NPATH / Metric.HIS.PATH = 

I.e., the invocation of opaque macros within a routine is interpreted as a call, and therefore the code within the macro body is not considered. If INVOKE is not an opaque macro or Metrics.opaque_metrics is set to false (the default), the metric values for main() is computed as for the following program:

// Copyright (C) 2025 Axivion GmbH
// Copyright (C) 2025 The Qt Company GmbH, a subsidiary of The Qt Group
// https://www.qt.io

int global = 1;

void f1() {}

int main(void)
{
    { if (global>20) { f1(); } else { return 1; }};
    { if (global>30) { f1(); } else { return 1; }};
    return 0;
}

// Metric values:
// Metric.Number_Of_Statements / Metric.HIS.STMT = 9
// Metric.Number_Of_Gotos / Metric.HIS.GOTO = 0
// Metric.Number_Of_Returns / Metric.HIS.RETURN = 3
// Metric.Number_Of_Invocations = 2
// Metric.McCabe_Complexity / Metric.HIS.VG = 3
// Metric.Switch_Complexity = 3
// Metric.Maximum_Nesting = 3
// Metric.Maximum_Extended_Nesting / Metrich.HIS.LEVEL = 3
// Metric.NPATH / Metric.HIS.PATH = 4

Here all macro invocations have been replaced by the macro expansion.

Value Architecture-Dependencies.macro_options.represent_macros :

if this configuration value is false (the default), neither Macro_Invocation edges nor nodes of type Macro are generated within the RFG. This impacts the metric values of

  • Metric.Number_Of_Calling_Routines (also named Metric.HIS.CALLING), and

  • Metric.Number_Of_Called_Routines (also named Metric.HIS.CALLS).

Consider the following example:

// Copyright (C) 2025 Axivion GmbH
// Copyright (C) 2025 The Qt Company GmbH, a subsidiary of The Qt Group
// https://www.qt.io

#define INVOKE f1()

void f1() {}

int main(void)
{
    INVOKE;
    return 0;
}

and the following two scenarios:

  • if Architecture-Dependencies.macro_options.represent_macros is set to false, no node of type Macro_Invocation is created for the macro invocation in line 7, instead a Static_Call edge is created between main() and f1(). Therefore in this case, Metric.Number_Of_Called_Routines is 1 for main and Metric.Number_Of_Calling_Routines is 1 for f1().

  • if Architecture-Dependencies.macro_options.represent_macros is set to true, line 7 is represented by a Macro_Invocation edge to a node of type Macro in the RFG. There is no outgoing call edge from main() to f1(), and so Metric.Number_Of_Called_Routines is 0 for main and Metric.Number_Of_Calling_Routines is 0 for f1().

4.5.8. Metric Results

Metric values are generally stored as attributes in the RFG on the Nodes a metric applies to, i.e. Metric.Lines.Routine.LOC can be found on node-routine Routine nodes, but not on Nodes of type node-file File or node-class Class. Metric rules can also report findings that can be viewed in the “Metrics” view in the dashboard denoted by the dashboard2-uiicon-metricviolation symbol and the prefix MV. By default only findings outside the range between the min_value and max_value options are reported by setting report_all_values=true for the individual metric rule or a whole metric rulegroup. And additionally Analysis.message_format.metric.show_all_values has to be enabled as well.

Note

Some metrics are only calculated for the whole system and their values are stored as attributes of the special System RFG node.

4.5.8.1. Suppressing Metric Findings

Metric dashboard2-uiicon-metricviolation MV findings can be suppressed via comments in code. By default, the comment should start with AXIVION Metric or AXIVION Metrics, but this can be configured.

For more information, see Justifications and Suppressions with Code Annotations.

4.5.9. Metric Value Propagation

Metrics propagation refers to the process of aggregating metric values upwards along the hierarchy of enclosing RFG nodes. Meaning that metric values are propagated upwards and aggregated at enclosing nodes, so that a node-module Module node may have the Metric.Lines.Routine.LOC.sum attribute for the sum of Metric.Lines.Routine.LOC from all routines inside it.

4.5.9.1. Configuration Options

The following configuration options are present on all metric rules to configure the propagation.

  • Hierarchy Edge (hierarchy_edge_name): The type of edges the aggregation follows in the hierarchy view.

  • Hierarchy View (hierarchy_view_name): The view used to propagate the metric values. Can be any view that shares its nodes with the base view a metric is calculated on.

  • Propagate (propagate): Whether propagation should be performed.

  • Report propagated values (report_propagated_values): Whether propagated metric values should be reported or only stored in the RFG.

4.5.9.2. Propagated Metric Values

The propagation will add the following attributes to the RFG for each metric present.

  • Average (*.avg): The average metric value over all covered nodes. Calculated with sum / cnt.

  • Count (*.cnt): The number of nodes the propagation covers.

  • Maximum (*.max): The maximum metric value from all covered nodes.

  • Minimum (*.min): The minimum metric value from all covered nodes.

  • Standard Deviation (*.std): The standard deviation by means of the sum of squares and count. Calculated with sqrt(max(0, 1 / (cnt - 1) * (ssum - 1 / cnt * sum * sum))).

  • Sum (*.sum): The sum of all metric values from all covered nodes.

  • Sum of Squares (*.ssum): The sum of squared metric values from all covered nodes.

This example shows how the propagation to the node-module Module level looks like for the 3 routines:

// Copyright (C) 2026 The Qt Company GmbH, a subsidiary of The Qt Group
// https://www.qt.io

// Example for Metric.Lines.Routine.LOC and metric propagation

// Metric.Lines.Routine.LOC = 3
int doit_a(int a, int b) {
    return a + b;
}

// Metric.Lines.Routine.LOC = 7
int doit_b(int a) {
   if(a == 0) {
      return 1;
   } else {
      return a * doit_b(a-1);
   }
}

// Metric.Lines.Routine.LOC = 9
int doit_c(int a) {
    if(a == 0){
      return 0;
   } else if(a == 1) {
      return 1;
   } else {
      return (doit_c(a-1) + doit_c(a-2));
    }
}

// Propagated values on the file node:
// Metric.Lines.Routine.LOC.avg = 6.333333
// Metric.Lines.Routine.LOC.cnt = 3
// Metric.Lines.Routine.LOC.max = 9
// Metric.Lines.Routine.LOC.min = 3
// Metric.Lines.Routine.LOC.ssum = 139
// Metric.Lines.Routine.LOC.std = 3.055050
// Metric.Lines.Routine.LOC.sum = 19