The printf-like and scanf-like functions are fertile ground for programming errors as they are not type safe due to
their variadic nature, and incorrect use often involves undefined behavior that is not diagnosed by
compilers. PC-lint Plus performs comprehensive analysis of the use of these functions diagnosing format
incompatibilities, inconsistent and redundant specifier combinations, missing and unused arguments, mis-use
of positional specifiers, use of non-standard conversion specifiers, unbounded conversions, and other
anomalies.
There are several categories of checking performed and over two dozen messages dedicated to analysis of format string functions.
The messages in this section focus on particularly egregious errors that always have the potential to result in undefined behavior. A relatively common error is to provide fewer data arguments than required by the format string. This often happens for particularly large format strings or when the format string is changed. Another possibility is a missing comma between string literal arguments such as in:
printf("\%10s \%s", "Name" "Value");
which will be diagnosed with:
warning 558: too few data arguments for format string (1 missing) printf("%10s %s", "Name" "Value"); ~^
showing the location of the first conversion specifier without a value and the total number of missing data
arguments. Such a call results in undefined behavior as printf processes data on the stack looking for the next
argument.
The scanf-like functions have additional potential concerns. Using the %s or %[ conversion specifier without a maximum field width will result in undefined behavior if the stored string exceeds the provided buffer, e.g.:
char buf[10]; scanf("%s", buf);
which will result in:
warning 498: unbounded scanf conversion specifier 's' may result in buffer overflow scanf("%s", buf); ~^
This message will not be issued if the non-standard ’m’ prefix is used (e.g. %ms), which specifies that scanf should
dynamically allocate a buffer large enough to hold the result.
A missing closing bracket for the %[ conversion specifier is yet another instance of undefined behavior and is diagnosed with message 2406 . For example:
char buf[100]; scanf("%99[^]", buf);
Here the programmer intended to store a series of consecutive ^ characters into buf but a special exception for the scanf function causes the closing bracket to be considered part of the pattern in this case, not the closing bracket to the %[ conversion specifier. PC-lint Plus will issue:
warning 2406: no closing ']' for '%[' in scanf format string scanf("%99[^]", buf); ~~^
Another source of undefined behavior stemming from scanf is when a field width of zero is specified:
scanf("\%0s", buffer);
which will elicit:
warning 2407: zero field width in scanf format string is unused scanf("%0s", buffer); ^
Less common is a format string that is not null terminated, this can happen when a sized array of char is initialized in a way that prevents the terminating NUL character from being appended, e.g.:
const char fmt[2] = "\%s";
which when used as a format string will result in warning 496 (the declaration alone is enough to prompt info 784 ).
This group of messages will diagnose arguments to format functions that do not match the corresponding conversion specifier in the format string. Warning 559 is issued for significant discrepancies:
printf("\%s", 12);
will elicit:
warning 559: format '%s' specifies type 'char *' which is inconsistent with argument no. 2 of type 'int' printf("%s", 123); ~~ ^~~
Messages 705 and 706 are used to diagnose "nominal" inconsistencies between the expected and actual type or the type pointed to. A "nominal" difference means that the size and basic type of the argument was correct but the type was not exactly the type prescribed by the Standard, e.g. a difference in sign. printf-like functions can accept a star (*) in place of the field width and/or precision in which case the value is taken from the next int argument. If this argument is missing warning 2402 is issued. Warning 2403 is issued if the type of this argument is incorrect.
The POSIX positional argument syntax allows arguments to be referred to by number using the syntax n$ where n refers to a data argument. For example the following two calls to printf are equivalent:
printf("%d %d", 1, 2); // ISO syntax printf("%1$d %2$d", 1, 2); // POSIX positional notation
Positional arguments allow format strings to reference arguments in an order that is different from how they are supplied to the format function as well as using the same argument multiple times in the format string. There are several caveats when using positional arguments. For starters, the position starts at 1, not 0; an attempt to use 0 as a position will elicit message 493 . Positional arguments cannot be mixed with non-positional arguments in the same format string, violations of this rule are diagnosed by message 2401 . Referencing a non-existent positional argument will be diagnosed by messages 494 (data argument positions) and 2404 (field width and field precision positions).
Features that are not specified by the ISO C Standard may not be portable to other platforms and their use can be diagnosed by PC-lint Plus. These features include positional arguments described above (message 855 ), non-ISO format specifiers such as %m for printf-like functions and %C, and %D for printf-like and scanf-like functions (message 816 ), and non-standard length modifiers / conversion specifier combinations (message 499 ). Since the behavior of these features are not specified by the Standard, their use on platforms that do not support them may result in unintended or undefined behavior.
There are several reasons that a conversion specifier may be invalid and PC-lint Plus will diagnose these.
Each of these messages represent a programming error or the use of extensions that PC-lint Plus is not aware of. For example:
printf("\%.10c", 'a');
will result in:
warning 2405: precision used with 'c' conversion specifier is undefined printf("%.10c", 'a'); ~^~~~
A precision is not allowed with the %c conversion specifier and providing one results in undefined behavior.
There are several suspicious constructs that by themselves do not represent errors but are sufficiently unusual to warrant review. This includes
Message 983 will point out uses of dash(-) within a scanf scan-list (e.g. %[A-Z]). As the behavior of the dash in
this position is implementation defined, some implementations interpret this as a range, others do
not.
printf and scanf conversion specifiers can also be deprecated using the -deprecate option, which
will cause message 586 to be emitted when they are seen. E.g. -deprecate(printf_code, n) will
cause a warning to be issued whenever the %n conversion specifier is used in a printf-like function.
Similarly, use scanf_code to deprecate scanf conversion specifiers. See -deprecate for additional
information.
The -printf and -scanf options allow a user to specify functions that resemble a member of the printf or scanf family. PC-lint Plus has built-in support for the following formatting functions, including those from Annex K in the C11 Standard:
| printf-like functions | scanf-like functions | Annex K printf-like functions | Annex K scanf-like functions |
| fprintf | fscanf | fprintf_s | fscanf_s |
| printf | scanf | printf_s | scanf_s |
| snprintf | sscanf | snprintf_s | sscanf_s |
| sprintf | vfscanf | sprintf_s | vfscanf_s |
| vfprintf | vscanf | vfprintf_s | vscanf_s |
| vprintf | vsscanf | vprintf_s | vsscanf_s |
| vsnprintf | vsnprintf_s | ||
| vsprintf | vsprintf_s | ||
Several messages (including 650 , 587 , and 734 ) deal with the notion of “precision” or otherwise involve static determination of whether or not a value is representable in a particular context. In PC-lint Plus precision has been expanded from covering only the conceptual width of a value (e.g. a bitfield or right-shifted variable) to encompass the potential bit patterns that can result from the use of bitwise operators, addition, subtraction, or values of enum type. Many such messages utilize supplemental messages to convey bit pattern information when relevant. For example, for some unsigned int u:
if ( (u & 0x10) == 0x11 ) { }
will result in:
warning 587: predicate '==' can be pre-determined and always evaluates to false if( (u & 0x10) == 0x11 ) { } ~~~~~~~~~~ ^ ~~~~ supplemental 891: incompatible bit patterns: U32_00000000000000000000000000010001 vs U32_000000000000000000000000000?0000 if ( (u & 0x10) == 0x11 ) { } ^
where U32_00000000000000000000000000010001 is the binary representation of the constant 0x11 (17) as an
unsigned 32-bit integer and U32_000000000000000000000000000?0000 shows the potential values of the left
operand which may or may not have the bit in the sixteens place set but cannot have the bit in the ones place set.
In a more complex example, the effects of implicit conversions may be visible, for example:
1void f(char c1, char c2) { 2 if ( (c1 & 13) + (c2 & 8) == 6 ) { } 3}
will result in:
warning 587: predicate '==' can be pre-determined and always evaluates to false if ( (c1 & 13) + (c2 & 8) == 6 ) { } ~~~~~~~~~~~~~~~~~~~~ ^ ~ supplemental 891: incompatible bit patterns: S32_00000000000000000000000000000110 vs S32_............................??0? if ( (c1 & 13) + (c2 & 8) == 6 ) { } ^
The first bit pattern represents the constant 6. The second bit pattern is masked with periods beyond its
meaningful precision because it is signed in order to reduce confusion regarding the potential sign
extension of an inexact value. This message is indicating that the resultant sum cannot ever have
the bit in the twos place set and therefore cannot represent the value to which it is being compared.
Supplemental messages displaying bit patterns will only appear when they will provide useful information beyond that conveyed by the precision specified in the original message. For example:
1void f(unsigned char uc) { 2 if (uc == -1) { } 3}
will result in:
warning 650: constant '-1' out of range for operator '==' if (uc == -1) { } ^
with no accompanying supplemental message.
The following message numbers currently utilize the unified precision and viable bit pattern architecture:
| # | Context | Category |
| 572 | >> or >>= by a constant | loss of precision |
| 587 | ==, !=, <, <=, >, or >= with one constant operand | pre-determined predicate |
| 650 | ==, !=, <, <=, >, or >= with one constant operand, switch case | pre-determined predicate |
| 685 | <, <=, >, or >= with one constant operand | pre-determined predicate |
| 734 | assignment | loss of precision |
| 2415 | ==, !=, <, <=, >, or >= in a loop condition with no constant operands | pre-determined predicate |
Traditional lint Compilers do not flag uninitialized static (or global) variables because the C/C++ language defines them to be 0 if no explicit initialization is given. But uninitialized statics, because they can cover such a large scope, can be easily overlooked and can be a serious source of error. Additionally, some embedded compilers do not perform this standard mandated implicit initialization. PC-lint Plus will flag static variables (see messages 727 , 728 and 729 ) that have no initializer and that are assigned no value. For example, consider:
int n; int m = 0;
There is no real difference between the declarations as far as C/C++ is concerned but PC-lint Plus regards m as being explicitly initialized and n not explicitly initialized. If n is accessed by nowhere assigned a value, a complaint will be emitted.
Indentation checking can be used to locate the origins of missing left and right braces. It can also locate potential problems in a syntactically correct program. For example, consider the code fragment:
if( ... ) if( ... ) statement else statement
Apparently the programmer thought that the else associates with the first if whereas a compiler will, without
complaint, associate the else with the second if. PC-lint Plus will signal that the else is negatively indented with
respect to the second if.
There are three forms of messages; Informational 725 is issued in the case where there is no indentation (no positive
indentation) when indentation is expected, Warning 525 is issued when a construct is indented less than (negatively
indented from) a controlling clause, and 539 is issued when a statement that is not controlled by a controlling
clause is nonetheless indented from it.
Of importance in indentation checking is the weight given to leading tabs in the input file. Leading
tabs are by default regarded as 8 blanks but this can be overridden by the -t# option. For example
-t4 signifies that a tab is worth 4 blanks (see the -t# option in Section 4.3.3 Message Presentation
).
Recognizing indentation aberrations comes dangerously close to advocating a particular indentation scheme; this we wish to avoid. For example, there are at least three main strategies for indentation illustrated by the following templates:
if( e ) { statements } if( e ) { statements } if( e ) { statements }
Whereas the indentation methods appear to differ radically, the only real difference is in the way braces are handled.
Statements are always indented positively from the controlling clause. For this reason PC-lint Plus makes what is
called a strong check on statements requiring that they be indented (or else a 725 is issued) and
only a weak check on braces requiring merely that they not be negatively indented (or else a 525 is
issued).
case, and default undergo a weak check. This means, for example, that
switch() { case 'a' : break; default: break; }
raises only the informational message (725 ) on the second break but no message appears with the case and
default labels.
The while clause of a do ... while(e); compound undergoes a weak check with respect to the do, and an else
clause undergoes a weak check with respect to its corresponding if.
An else if() construct on the same line establishes an indentation level equal to the location of the else not the if. This permits use of the form:
if() statement} else if() statement else if() statement ... else statement
Only statement beginnings are checked. Thus a comment can appear anywhere on a line and it will not be
flagged. Also a long string (if it does not actually begin a statement) may appear anywhere on the
line.
A label may appear anywhere unless the +fil flag is given (Section 4.11 Flag Options ) in which case it undergoes
a weak check.
Message 539 is issued if a statement that is not controlled by a loop is indented from it. Thus:
while ( n > 0 ); n = f(n);
draws this complaint, as well it should. It appears to the casual reader that, because of the indentation,
the assignment is under the control of the while clause whereas a closer inspection reveals that it is
not.
Since the user of PC-lint Plus has the ability to set the sizes of various data objects (See the -s .. options in
Section 4.5.1 Scalar Data Size ), the reader may wonder what the effect would be of using various
sizes.
Several of the loss of precision messages (712 , 734 , 735 and 736 ) depend on a knowledge of scalar sizes. The
legitimacy of bit field sizes depends on the size of an int. Warnings of format irregularities are based in part on the
sizes of the items passed as arguments.
One of the more important effects of type sizes is the determination of the type of an expression. The types of integral constants depend upon the size of int and long in ways that may not be obvious. For example, even where int are represented in 16 bits the quantity:
35000
is long and hence occupies 4 (8-bit) bytes whereas if int is 32 bits the quantity is a four byte int. If you want it to
be unsigned use the u suffix as in 35000u or use a cast.
Here are the rules: the type of a decimal constant is the first type in the list (int, long, long long) that can
represent the value. The maximum values for these types are taken to be 2sizeof(type)*bits-per-byte-1 - 1. The
quantities sizeof(int), sizeof(long), and sizeof(long long) are based on the -si#, -sl#, and -sll# options
respectively. The type of a hex or octal constant, however, is the first type on the list (int, unsigned int, long,
unsigned long, long long, unsigned long long).
For any constant (decimal, hex or octal) with a u or U suffix, one selects from the list (unsigned int, unsigned
long, unsigned long long). If an l or L suffix, the list is (long, long long) for decimal constants and (long,
unsigned long, long long, unsigned long long) for hex and octal constants. If both suffixes are used (e.g. UL),
the list is (unsigned long, unsigned long long) for any constant. If the suffix is ll or LL, the type is unsigned
long long for decimal constants and either long long or unsigned long long for hex and octal constants.
Finally, constants containing both the u/U and ll/LL suffixes are always of type unsigned long long, regardless of
base.
The size of scalars enters into the typing of intermediate expressions in a computation. Following ANSI/ISO standards, PC-lint Plus uses the so-called value-preserving rule for promoting types. Types are promoted when a binary operator is presented with two unlike types and when unprototyped function definitions specify subinteger parameters. For example, if an int is added to an unsigned short, then the latter is converted to int provided that an int can hold all values of an unsigned short; otherwise, they are both converted to unsigned int. Thus the signedness of an expression can depend on the size of the basic data objects.
+stack(sub-option,...)
-stack(sub-option,...)
The +stack version of this option can be used to trigger a stack usage report. The -stack version is used only to
establish a set of options to be employed should a +stack option be given. To prevent surprises if a -stack option is
given without arguments it is taken as equivalent to a +stack option.
The sub-options are:
| &file=filename | This option designates the file to which the report will be written. This option must be present to obtain a report. |
| &overhead(n) | Establishes a call overhead of n bytes. The call overhead is the amount of stack consumed by a parameterless function that allocates no auto storage. |
| Thus if function A(), whose auto requirements are 10, calls function B(), whose auto requirements are also 10, and which calls no function, then the stack requirements of function A() are 20+n where n is the call overhead. By default, the overhead is 8. |
|
| &external(n) | Establishes an assumption that each external function (that is not given an explicit stack requirement, see below) requires n bytes of stack. By default this value is 32. |
| &summary | This option indicates that the programmer is interested in at least a summary of stack usage (stack used by the worst case function). The summary comes in the form of Elective Note 974 and is equivalent to issuing the option +e974. This option is not particularly useful since a summary report will automatically be given if a +stack option is given. It is provided for completeness. |
| name(n) | Where name is the name of a function, explicitly designates the named function as requiring n bytes of total stack. This is typically used to provide stack usage values for functions whose stack usage could not be computed either because the function is involved in recursion or in calls through a function pointer. name may be a qualified name. |
| &prealloc_vars | Allocations for all variables within a function will occur at the start of that function. This will typically increase stack usage since all variables are considered simultaneously reachable and are not removed from the stack when their scope ends. This is consistent with how some compilers allocate variables within a function. |
+stack( &file=s.txt, alpha(12), A::get(30) )
requests a stack report to be written to file s.txt and further, that function alpha() requires 12 bytes of stack and
function A::get() requires 30.
At global wrap-up, a record is written to the file for each defined function. The records appear alphabetized by
function name.
Each record will contain the name of a function followed by the amount of auto storage required by its
local auto variables. Note that auto variables that appear in different and non-telescoping blocks may
share storage so the amount reported is not simply the sum of the storage requirements of all auto
variables.
Each function is placed into one of seven categories as follows:
It is assumed that any recursive function requires an unbounded amount of stack. If that assumption is incorrect and you can deduce an upper bound of stack usage, then you can employ the +stack option to indicate this upper bound. In a series of such moves you can convert a set of functions containing recursion to a set of functions with a known bound on the stack requirements of each function.
The distinction between the recursive and recursive loop categories is illustrated by the example:
void x(), y(), z(); void x() { y(); z(); } void y() { x(); } void z() { x(); }
The stack report will show that x calls y, and that y calls x. As this demonstrates the recursive nature of both functions, they will be placed in the recursive loop category. The report will also show z calling x, but the displayed callee for the x entry is y. As the report does not demonstrate the call chain leading to a recursive call to z, it will be classified as recursive.
If you can determine an upper bound for the stack requirements of a non-deterministic function then, like a recursive function, you may employ the +stack option to specify this bound and in a sequence of such options determine an upper bound on the amount of stack required by the application.
(a) is labeled as ’finite’ but calls no other function or
(b) is labeled as ’external’ or
(c) is labeled as ’explicit’ (see next category).
You should be able to confirm the stack requirements by adding up the contribution from each function in the
chain plus a fixed call overhead for each call. The amount of call overhead can be controlled by the stack
option.
For ’external’ functions there is an assumed default stack requirement. You may employ the +stack option to specify the stack requirement for a specific function or to alter the default requirement for external functions.
Stack Report Formatting Options
The information provided by this option can be formatted by the user using the -format_stack option. This allows
the information to be formatted to a form that would allow it to be used as input to a database or to a spreadsheet.
This format can contain the escape codes:
’%f’ for the function name
’%a’ for the local auto storage
’%t’ for type (i.e. one of the seven categories above)
’%n’ for the total stack requirement (or -1 if unbounded by recursion)
’%c’ for the callee and
’%e’ for an ’external’ tag on the callee
See -format_stack for more details. See also Message 974 .
Applications written for the traditional 32-bit model where int, long and pointers are each represented in 32 bits,
may have difficulty when ported to one of the 64-bit models. Problems that you may encounter and that
PC-lint Plus will catch are described and implemented as options in file au-64.lnt. This file and other
.lnt files mentioned below are distributed with the product and/or are downloadable from our web
site.
The file au-64.lnt is not intended to be used directly by the programmer. There are a number of wrappers reflecting the different flavors of 64-bit computing. These wrappers specify sizes and other options unique to specific models and then invoke au-64.lnt. The models and the au file that you should be using are described below.
| Data Type | LP64 Model | LLP64 Model | ILP64 Model |
| long long | 64 bits | 64 bits | 64 bits |
| pointers | 64 bits | 64 bits | 64 bits |
| long | 64 bits | 32 bits | 64 bits |
| int | 32 bits | 32 bits | 64 bits |
The above table shows the differences between each of the 3 64-bit models. Each model has a corresponding au file:
au-lp64.lnt for LP64, au-llp64.lnt for LLP64, and au-ilp64.lnt for ILP64.
You may indicate that a particular name is not to be employed in your programs by using this option:
-deprecate(category, name [,commentary])
category is one of: function, keyword, macro, option, variable, type, basetype, ppw, printf_code or
scanf_code.
The commentary in the third argument will be appended to the message. For example,
-deprecate( variable, errno, Violates Policy XX-123 )
When the use of errno as a variable is detected (but not its definition or declaration) the following Warning is issued.
Warning 586: variable 'errno' is deprecated. Violates Policy XX-123
When the category of deprecation is variable only the use of external variables are flagged. Local variables may be
employed without disparaging comment.
If errno were a macro you would need to deprecate errno as a macro:
-deprecate( macro, errno, Violates Policy XX-123 )
If errno could be either (the standard allows both forms) then both options should be used.
You may also deprecate functions and keywords. For example:
-deprecate( keyword, goto, goto is considered harmful ) -deprecate( function, strcpy, has been known to cause overuns )
could be used to flag the use of suspect features.
Quotes (both single and double) and parentheses within the commentary need to be balanced.
When deprecating functions or variables inside of a C++ anonymous namespace, prefixing their name with "(anonymous namespace)::" will allow them to be deprecated.
Options can also be deprecated. Deprecating an option causes future uses of that option to be met with message 586 although the option is still processed as usual. When deprecating an option, you must specify the name of the option, including a leading ’-’ or ’+’ but must not provide any arguments for the option. For example:
-deprecate( option, -setenv, environment variables should not be set during the linting process)
will deprecate the use of -setenv . It is not possible to deprecate the use of individual flag options; using the -deprecate option with -f, +f, –f, or ++f will deprecate all flag options. Note that some options have forms that begin with ’-’ and another form that begins with ’+’; deprecating one form does not automatically cause the other form to be deprecated even if both forms have identical meanings.
The -deprecate option can be used to deprecate types. This can be accomplished using the deprecation categories
’type’ and ’basetype’.
When the category ’type’ is specified, message 586 is issued for any use of the type (outside of typedef declarations
and explicit template instantiations) but type alias names (introduced via typedef or using) are not looked
through and use of the underlying type is allowed without complaint if it occurs through such an
alias.
The category of ’basetype’ is similar except type aliases are looked through and if at any level the deprecated type
is present, 586 is issued. If the deprecated type is a typedef type, no diagnostic is issued for the declaration of the
type (although use of the type is diagnosed).
Using a deprecated type as a target of a typedef is not diagnosed with 586 . The logic is that for ’basetype’, the use of the typedef that targets the deprecated type will be diagnosed anyway and that for ’type’ the user is not interested in use of the type through typedefs. We do provide a new elective message, 986 , that will be issued when the target of a type alias is deprecated with the ’type’ category.
//lint -deprecate(type, int) warn about uses of int but not through an alias //lint -deprecate(basetype, DOUBLE) typedef int INT; // 986 issued for 'int' appearing in typedef typedef double DOUBLE; // okay, declaration of deprecated type typedef DOUBLE DOUBLE2; // okay, use of deprecated basetype in alias int i;// 586 - 'int' is deprecated INT i2;// okay, use of deprecated type via alias union u { const int * pci; // 586 - deprecated type 'int' used in declaration double * pd; // okay, 'double' is not deprecated DOUBLE d1; // 586 - deprecated basetype 'DOUBLE' used directly DOUBLE2 d2; // 586 - deprecated basetype 'DOUBLE' used indirectly };
While analyzing C modules, the keyword "struct", "enum", or "union" should be included in the type name of the -deprecate option unless declared using an anonymous typedef declaration. For example:
//lint -deprecate(type, MyData) typedef struct { int i; } MyData; //lint -deprecate(type, struct OtherData) struct OtherData { int j; };
While analyzing C++ modules, the inclusion of keywords such as "struct", "class", "union", or "enum" is optional when using the -deprecate option. They will be ignored.
Individual preprocessor directives can be deprecated with the ppw category. For example:
-deprecate(ppw, pragma)
will deprecate the use of the #pragma preprocessor directive, note that the # is not included in the -deprecate
option. Message 586 will be issued when the deprecated directive is encountered unless it is encountered in a
conditionally excluded region (e.g. between #if 0 ... #endif) in which case message 886 will be issued
instead.
An unknown preprocessor directive can be deprecated, PC-lint Plus will issue the deprecation message and then
complain about the unknown directive as usual. The #error directive can be deprecated in which case the
deprecation message will be emitted before the directive is handled. The null directive can be deprecated using an
empty second argument to -deprecate.
Note that the -ppw_asgn option does not establish any deprecation relationship between the named directives. For example, if the option -ppw_asgn(foo, line) is used to cause #foo to be handled as #line, deprecating line will not also deprecate foo or vice versa.
The -deprecate option can be used to deprecate conversion specifiers for printf-like and scanf-like functions using the printf_code and scanf_code categories. For example,
-deprecate(printf_code, n)
will deprecate the use of %n in printf-like functions, note that the % is not included in the -deprecate option. Deprecating a conversion specifier will result in message 586 being issued if the conversion specifier is used, regardless of any length modifiers present in the actual use, but will not deprecate other conversion specifiers with the same meaning. For example -deprecate(printf_code, i) will warn for %i and %hi but will not warn for %d (which has the same meaning as %i in printf-like functions).
The -deprecate option can be used to deprecate C++ template functions, variables, and types. Deprecations can either be applied to abstract template declarations, or to specific template specializations. For example,
-deprecate(function, templateFn) template <typename T> void templateFn(void);
will deprecate the use of templateFn with any template arguments passed to it. While,
-deprecate(function, templateFn<int>)
would only deprecate uses of the templateFn where the template argument is resolved to be int.
A specific format must be observed when specifying specific template specializations. All template arguments must be present, including default arguments that were omitted from the as-written type. All types used as a template argument must not use any typedef names or type aliases. Nested template arguments must place the closing caret immediately next to each other without spaces regardless of whether the language version being parsed supports that convention. All non-type arguments that are integer constant expressions must be evaluated to their result. A single space is expected after each comma between template arguments. Due to option parsing, it may be necessary to escape each comma used in the template argument list. An example deprecation comment and its deprecated usage is included below.
typedef int MYINT; template <typename T> class TemplateClass {}; template <int I, typename T = TemplateClass<MYINT> > void templateFn(void); //lint -deprecate(function, "templateFn<2, TemplateClass<int>>") void exampleUsage(void) { templateFn<1+1>(); // warn 586 }
PC-lint Plus supports the long-requested feature of utilizing multiple cores to achieve faster processing times. This
feature is enabled by placing the new -max_threads=n option before the first module to process. If this option does
not appear before the first module is seen, the behavior is as if -max_threads=1 was used. Threads are used both
during the main processing phase and the global wrap-up phase. In the main phase, a separate thread is dispatched
to handle each module, up to a maximum of n concurrent threads. When all of the modules have been
processed, threads are employed to handle wrap-up processing, again up to a maximum of n concurrent
threads.
When using C++20 Modules with ,
if one or more threads are waiting to process a module import pending the completion of the
build of a module interface unit in another thread, then a single extra thread may also be
launched to process another module in the meantime. This will never result in more than
threads concurrently performing work, but both waiting threads and the single extra thread
contribute to the overall process memory utilization with the potential peak memory usage
being similar to what would otherwise be expected from a fractional value somewhere between
and
threads.
While results will vary depending on a variety of factors, the best overall times are typically achieved when using a
value for n that equals the number of available cores, or about twice the number of cores for processors that support
hyper-threading. Some experimentation may be necessary to find the best value for n on a particular system. In
order to assist in that regard, the option -max_threads=0 will result in PC-lint Plus picking a value for n
that it thinks is optimal based on querying of the available hardware for systems that support it.
When using -max_threads=0, elective note 999 will report on the number of threads that has been
selected.
There are a few caveats to keep in mind when employing multiple threads:
Aside from the above caveats, there is no difference in behavior or functionality when using multiple threads.
The C and C++ Standards define minimum translation limits that must be supported by a conforming compiler.
The limits specify quantities such as the minimum number of significant characters in internal and
external identifiers, the minimum number of function parameters that an implementation must support,
the minimum number of supported concurrently defined macros, and the minimum number of data
members supported in structures. The C99 limits are specified in section 5.4.2.1 of the C99 Standard
(ISO/IEC 9899:1999) and the C++ limits are specified in Annex B of the C++ Standard (ISO/IEC
14882:2011).
-lang_limit(C\C++, limit-name, limit-value) specify minimum language translation limits
The -lang_limit option takes three arguments. The first argument is either C or C++ indicating
which language the overridden limit applies to. The second argument is the type of limit and must
be one of the names in the first column of the table below. The last argument is a value that must
be between 0 and 4294967294 (0 indicates the lack of a limit) or the special value of default, which
reverts back to the corresponding value of the table below essentially removing a previously overridden
value.
The table below lists the language limit checks supported by PC-lint Plus. The Limit Name is the name recognized
in the second parameter of the -lang_limit option. The Limit Description is the text that is used in
the 793 message when the limit is exceeded and which can be used with the -estring option for
suppression purposes. The C89 Limit shows the minimum limits specified by the ANSI C89 standard.
The C99 Limit column shows the minimum limits mandated by C99, the C11 Limits are identical.
The C++ Limit shows the limits required by the C++ Standard (all versions of C++ share the same
limits).
| Limit Name | Limit Description | C89 | C99 | C++ |
|
| Limit | Limit | Limit | |
| external_identifiers | external identifiers | 511 | 4095 | 65526 |
| internal_identifier_chars | significant characters in an internal identifier | 31 | 63 | 1024 |
| macro_identifier_chars | significant characters in a macro name | 31 | 63 | 1024 |
| external_identifier_chars | significant characters in an external identifier | 6 | 31 | 1024 |
| function_parameters | function parameters | 31 | 127 | 256 |
| function_arguments | function arguments | 31 | 127 | 256 |
| macro_parameters | macro parameters | 31 | 127 | 256 |
| string_literal_length | characters in a string literal | 509 | 4095 | 65536 |
| case_labels | case labels in a switch | 257 | 1023 | 16384 |
| structure_members | structure members | 127 | 1023 | 16384 |
| enumeration_constants | enumeration constants | 127 | 1023 | 4096 |
| base_classes | base classes | n/a | n/a | 16384 |
| direct_base_classes | direct base classes | n/a | n/a | 1024 |
| class_members | members in a class | n/a | n/a | 4096 |
| static_members | static members in a class | n/a | n/a | 1024 |
| final_functions | final overriding virtual functions in a class | n/a | n/a | 16384 |
| virtual_base_classes | virtual base classes | n/a | n/a | 1024 |
| friend_decls | friend declarations in a class | n/a | n/a | 4096 |
| access_decls | access control declarations in a class | n/a | n/a | 4096 |
| ctor_initializers | member initializers in a constructor | n/a | n/a | 6144 |
| scope_qualifiers | scope qualifiers in an identifier | n/a | n/a | 256 |
| template_arguments | template arguments in a template | n/a | n/a | 1024 |
| try_handlers | handlers in a try block | n/a | n/a | 256 |
| throw_specs | throw specifications in a function | n/a | n/a | 256 |
By default, the limits shown above are used to determine when a minimum limit has been exceeded and, for C, is
dependent on the version of the language used. If your compiler supports different limits, or if you just want to be
alerted when a different threshold is reached for a particular limit, you can use the -lang_limit to override the
defaults shown above.
The table below provides additional details about some of the limits checked by PC-lint Plus.
| Limit Name | Notes | |
| external_identifiers | External identifiers are functions and variables with external linkage. |
|
| internal_identifiers | Internal identifiers are any non-preprocessor (e.g. macro) symbols that are not external identifiers. These include type names, class names, local variables, enumeration constants, etc. |
|
| case_labels | This does not include the default label or case labels of nested switches. |
|
| structure_members | This includes only non-static data members but includes members inherited from base classes. |
|
| base_classes | The number of bases for a given class including indirect and virtual bases. |
|
| direct_base_classes | Virtual and non-virtual direct base classes for a class. |
|
| class_members | Includes all static and non-static data and function members declared directly in the class (e.g. not inherited members). Note that this also includes implicitly generated functions such as constructors, assignment operators, etc. |
|
| static_members | All static data and function members, including inherited members for a class. |
|
| virtual_base_classes | Direct and indirect virtual base classes for a class. |
|
| access_decls | The number of access control (or using) declarations present in a class. Does not include base classes. This is not a count of the access-specifiers present in a class. |
|
| ctor_initializers | The number of items initialized in a constructor member initializer list. Includes base class initializers. |
|
| scope_qualifiers | This is the number of nested name specifiers present in a qualified-id, e.g. A::B::C contains two scope qualifiers. |
|
PC-lint Plus can produce accounting data for all diagnostic encounters. A diagnostic encounter is any primary (non-supplemental) diagnostic (message) that was either emitted, or would have been emitted if the message was enabled and not otherwise suppressed via a suppression option, and any supplemental message (emitted or suppressed) associated with an emitted primary message. This accounting data includes information about each diagnostic encounter, including details of the diagnostics, the suppression options involved in the encounter, and the impact of those suppression options on the decision to emit or inhibit the message. This information may be used to identify suppressed messages and to examine the role each suppression option played in determining whether or not to emit a message. Diagnostic accounting data can additionally be used to:
Accommodate auditing demands that require an account of suppressed messages.
Provide insight into what messages are being suppressed and why.
Facilitate custom post-processing tasks to generate reports, coalesce duplicate messages, or convert analysis results to other formats.
Detect unused suppression options.
Diagnostic Accounting is enabled with the -diag_accounting option. This option accepts several arguments specifying the output files that PC-lint Plus should write accounting data to. Each output file corresponds to a table of information that is collected when diagnostic accounting is enabled and written to the specified file in JSON format. The details of each table are provided below.
The Diagnostics table contains one entry per diagnostic encounter. Each entry contains the standard information
about the diagnostic that would appear when it is emitted (its message number, message text, user-defined append
text, location) along with whether or not it was actually emitted, and whether the message comes from a library
location.
Note that while suppression options can influence the disposition of diagnostic encounters, and all diagnostic encountered are recorded regardless of disposition, there are several factors that can influence whether a diagnostic accounter occurs to begin with including:
The value of certain flag options can directly or indirectly affect diagnostic encounters. For example, the +fia flag option will disable the supplemental messages feature which will prevent encounters with these diagnostics and message 9903 is not encountered unless the f12 flag option is enabled. Other flag options may alter the behavior of PC-lint Plus in ways that indirectly affect diagnostic encounters.
The -misra_interpret option may be used to refine the situations in which certain messages are emitted.
Certain code constructs may be used to prevent encounters with specific diagnostics. For example, message 534 (ignoring return value of function) is not emitted when the return value is cast to void.
When such factors prevent a diagnostic encounter from taking place, the unmanifested encounter is not represented
in the Diagnostics table.
Note that supplemental messages are only represented in the Diagnostics table if their corresponding primary
message was emitted (supplemental messages of suppressed primary messages are not considered to be diagnostic
encounters).
| Field name | Field type | Description |
| id | Integer | A zero-indexed value that uniquely represents a diagnostic encounter within an invocation of PC-lint Plus. |
| module_id | Integer | A zero-indexed value that represents the module from which the diagnostic was encountered. For messages encountered within a module, this corresponds to the id provided in the Modules table. For messages encountered outside of a module, this value is 0. |
| pass_num | Integer | A zero-indexed value representing the analysis pass. The first pass is represented with a value of 0, the second pass a value of 1, etc. |
| is_supplemental | Boolean | Indicates whether this message is a supplemental message (e.g. 831, 891, 892, 893, 894, 896, or 897). The value of this field is independent of that value of the fin flag option which controls whether supplemental messages are rendered with a category of "info". |
| parent_id | Integer | For supplemental messages, this value is the id of the corresponding primary message. For non-supplemental messages, the value of this field is 0. |
| supplemental_index | Integer | For supplemental messages, this value represents the relative order among all supplemental messages associated with the corresponding primary message. A value of 1 indicates that this supplemental message should be presented first, etc. A value of 0 is used for non-supplemental messages. |
| msg_num | Integer | The PC-lint Plus message number, e.g. 9260. |
| filename | String | The name of the file pointed to by the message. For locations that do not correspond to a file, the value may be empty or (if the fla option is enabled) may have a representative name in angle brackets such as <command line>. |
| full_filename | String | The absolute filename of the file pointed to by the message. Can be an empty string if the filename is not on the filesystem. |
| line_num | Integer | The line number (starting at 1) associated with the diagnostic. This field will have a zero value for locationless diagnostics. |
| col_num | Integer | The column number (starting at 1) associated with the diagnostic. This field will have a zero value for locationless diagnostics. |
| highlights | Array | An array of objects containing the below members that describe the start and end locations of a highlighted region associated with the diagnostic. This field will not be present if there are no highlights. |
| filename_start | String | The file in which the highlight starts. |
| line_num_start | Integer | The line number of the start of the highlight. |
| col_num_start | Integer | The column number (starting at 1) of the start of the highlight. |
| filename_end | String | The file in which the highlight ends. |
| line_num_end | Integer | The line number of the end of the highlight. |
| col_num_end | Integer | The column number of the end of the highlight. |
| is_fatal | Boolean | Indicates whether the message is a fatal message, i.e. whether PC-lint Plus would terminate as a result of issuing the message. |
| is_suppressible | Boolean | Indicates whether this message can be suppressed (while most messages are suppressible, several messages are not). |
| is_library | Boolean | Indicates whether the diagnostic is considered to emanate from a library location. |
| was_rejected | Boolean | Indicates whether this message was suppressed due to a -reject option encountered during evaluation of a Hook in a message context. |
| was_emitted | Boolean | Indicates whether the message was emitted, i.e. not suppressed. |
| msg_text | String | The diagnostic text of the message, not including text appened by -append options. |
| append_text | String | The text to append to the message as per relevant -append options. |
The options table contains one entry for every suppression option encountered. The first two options in this table
correspond to the implicit -w3 and -wlib=1 options that reflect the default warning level and library warning level
at startup. These implicit options have a location filename of builtin-default.
| Field name | Field type | Description |
| id | Integer | A zero-indexed value that uniquely represents a suppression option within an invocation of PC-lint Plus. |
| module_id | Integer | A zero-indexed value that represents the module being analyzed when the suppression option was encountered. For options encountered within a module, this corresponds to the id provided in the Modules table. For messages encountered outside of a module, this value is 0. |
| pass_num | Integer | A zero-indexed value representing the analysis pass. The first pass is represented with a value of 0, the second pass a value of 1, etc. |
| option_text | String | The text of the suppression option. |
| filename | String | The name of the file containing the suppression option. For locations that do not correspond to a file, the value may be empty or (if the fla option is enabled) a representative name in angle brackets such as <command line>. |
| full_filename | String | The absolute filename of the file containing the suppression option. |
| line_num | Integer | The line number (starting at 1) of the suppression option. |
| col_num | Integer | The column number (starting at 1) of the suppression option. For options encountered in comments, this value is the start of the comment. |
The Suppressions table contains an entry for every suppression object. Each suppression option represented in the
Options table will correspond to one or more suppression objects. Parameterized suppression options that specify
multiple parameter patterns are represented by one entry in the Options table and one entry in the Suppressions
table for every parameter pattern provided.
| Field name | Field type | Description |
| id | Integer | A zero-indexed value that uniquely represents a suppression object within an invocation of PC-lint Plus. |
| option_id | Integer | The id of the corresponding option entry from the Options table. |
| effective_msg_set | String | A canonicalized representation of the messages affected by this suppression object. Messages that were frozen (via +efreeze /++efreeze ) at the time the corresponding suppression option was encountered are excluded from this list. The canonicalized representation consists of a space-separated list of message numbers and/or hyphenated message number ranges, e.g. 12 120-129 230-239 1200-1299. |
| param_pattern | String | The provided parameter pattern, if any. |
| is_enable | Boolean | true if the suppression object votes in favor of message issuance, false if the suppression object votes against message issuance. |
| is_library | Boolean | true if this suppression only affects diagnostics within library regions. |
| is_dss | Boolean | true if this suppression affects the default suppression set. If is_library and is_dss are both true, this suppression affects the default library suppression set. |
PC-lint Plus maintains a default suppression set and a default library suppression set which represent which
messages are globally enabled for non-library and library regions, respectively. In the absence of any other
suppression options, a message will be emitted within a non-library region if it is enabled in the default suppression
set. Within a library region, in the absence of any other suppression options, a message will be emitted if it is
enabled in both the default suppression set and the default library suppression set. The +e, -e, and -w options
operate on the default suppression set while the +elib, -elib, and -wlib options operate on the default library
suppression set.
The is_dss field indicates whether this suppression corresponds to a +e/-e/-w/+elib/-elib/-wlib option. This information can be used to determine whether an orbit with a vote count of zero represents no interaction with the diagnostic encounter (this occurs, for example, when a parameterized suppression has a message pattern that matches the message number of the diagnostic but a message parameter pattern that does not match a message parameter) or is responsible for setting the default message set state for the encounter. Being able to distinguish between these cases is useful for identifying unused suppression options.
An orbit represents the effect of a candidate suppression object within a particular diagnostic encounter. For each
diagnostic encounter, there is one orbit that corresponds to the default suppression set (manipulated via
the +e/-e/-w options) and, for library diagnostics, one orbit that corresponds to the default library
suppression set (manipulated via the +elib/-elib/-wlib options). In addition, any suppression object
currently in effect that contains the message number corresponding to the diagnostic encounter in its
effective message set participates in message issuance determination and is represented by an orbit
entry.
| Field name | Field type | Description |
| id | Integer | A zero-indexed value that uniquely represents an orbit within an invocation of PC-lint Plus. |
| diag_id | Integer | The id of the corresponding diagnostic entry from the Diagnostics table. |
| supp_id | Integer | The id of the corresponding suppression entry from the Suppressions table. |
| votes | Integer | The total number of votes that were issued in favor of emitting the diagnostic by the suppression object. A negative value represents votes cast against issuance (votes to suppress). |
| overrider | Boolean | true only if the vote cast by the suppression object cannot be overruled (see below). |
The overrider field is always true for interactions with suppression objects resulting from single-line suppression options (!e#), scoped suppression options (-e(#), –e(#), -e{#}, or –e{#}), and for options that affect the default library suppression set (-elib and -wlib) when such options vote against issuance. In all other situations, the value of the overrider field is false. As these options are only used to inhibit messages (they never cast a vote in favor of message issuance), the value of the votes field will always be negative when overrider is true. For library messages, if the message is enabled in the default library suppression set, the corresponding orbit will have a value of 0 for votes and overrider will be false (messages enabled in the library default suppression set do not add a vote for issuance). For library messages where the message is disabled in the default library suppression set, the orbit will have a value of -1 for votes and overrider will be true. Multiple suppression objects may act as overriders within a particular encounter. If any orbits act as an overrider within a particular encounter, the message will be suppressed (unless it is an unsupressible message).
The Modules table contains one entry for each module analyzed by PC-lint Plus. The id field corresponds to the
module_id field found in the Diagnostics and Options tables.
| Field name | Field type | Description |
| id | Integer | A one-indexed value that uniquely represents an analyzed module within an invocation of PC-lint Plus. The first module provided to PC-lint Plus always has the value of 1, the second module 2, etc. |
| filename | String | The name of the module as provided. |
| full_filename | String | The absolute filename of the module. |
| is_cpp | Boolean | true for C++ modules, false for C modules. |
The Files table contains one entry for each source file (module and header) processed by PC-lint Plus.
| Field name | Field type | Description |
| id | Integer | A zero-indexed value that uniquely represents a processed source file within an invocation of PC-lint Plus. |
| module_id | Integer | The id of the corresponding entry in the Modules table representing which module was being analyzed when this file was processed. |
| filename | String | The name of the source file as encountered. |
| full_filename | String | The absolute filename of the source file, if available. |
| sha1_hash | String | The SHA-1 hash value of the contents of the source file rendered as a string of 40 hexadecimal digits. |
Headers that are included in multiple modules will be represented by a separate entry for each module. Headers that are included multiple times within a single module may result in multiple entries with the same module_id.
The Metadata table contains run-level information about Diagnostic Accounting results. In consists of a single
object with the fields described in the table below.
| Field name | Field type | Description |
| messages_output_file | String | The name of the file that diagnostic encounteres were written to. |
| options_output_file | String | The name of the file that suppression options were written to. |
| suppressions_output_file | String | The name of the file that suppressions were written to. |
| orbits_output_file | String | The name of the file that orbits were written to. |
| modules_output_file | String | The name of the file that modules were written to. |
| metadata_output_file | String | The name of the file that metadata was written to. |
| diag_queue_size | Integer | The diagnostic queue size. |
| filter_null_orbits | Boolean | Whether orbits that did not cast a vote (the votes field was zero) were excluded to the orbits file. |
| exclude_library | Boolean | Whether library diagnostics (and corresponding orbits) were excluded from output. |
| exclude_suppressed_library | Boolean | Whether suppressed library diagnostics (and corresponding orbits) were excluded from output. |
| suppressions_processed | Integer | The number of suppression objects that were processed. |
| options_processed | Integer | The number of suppression options that were processed. |
| orbits_processed | Integer | The number of orbits that were processed. |
| diagnostics_processed | Integer | The number of diagnostic encounters that were processed. |
| modules_processed | Integer | The number of modules that were processed. |
| suppressions_written | Integer | The number of suppression objects that were written. |
| options_written | Integer | The number of suppression options that were written. |
| orbits_written | Integer | The number of orbits that were written. This may be less than the number of orbits processed if filter_null_orbits, exclude_library, or exclude_suppressed_library is true. |
| diagnostics_written | Integer | The number of diagnostic encounters that were written. This may be less than the number of diagnostic encounters processed if exclude_library or exclude_suppressed_library is true. |
| modules_written | Integer | The number of modules that were written. |
The was_emitted field in the Diagnostics table indicates whether the diagnostic was emitted or suppressed. The rules that dictate how suppression options interact with each other to determine the final disposition of a diagnostic encounter are described in Section 4.9 How Suppression Options are Applied . The way that these interactions are reflected in the Diagnostics and Orbits tables are described below which may be used to understand why a message was suppressed or emitted and to identify unused suppression options.
If the value of the is_suppressible field in the Diagnostics table entry is false, the message is unsuppressible and is emitted.
Otherwise, if the overrider field is true for any orbit corresponding to the diagnostic encounter (entries in the Orbits table where the diag_id field matches the id field of the entry in the Diagnostics table), the message is suppressed. This occurs for orbits created as a result of interaction with the !e#, -e{#}, –e{#}, -e(#), and –e(#) options as well as orbits for diagnostic encounters within a library region when the message is not enabled for libraries (in which case the corresponding -elib or -wlib option will be referenced by the orbit object).
Otherwise, if the diagnostic’s was_rejected field is true, the message is suppressed (this corresponds to the -reject option being encountered within a message Hook).
Otherwise, if the total number of votes cast during the voting process is less than or equal to 0, the message is suppressed. The total number of votes is calculated by adding the value of the votes field of every orbit corresponding to the diagnostic encounter.
Otherwise, the message is emitted.
While using PC-lint Plus for a long running project, suppression options may accumulate over time. It is natural to
wonder if all of those options are still relevant. The code, parameterized type, module, etc. which
prompted the suppression may no longer be present, or may now comply with the suppressed message.
Identifying and removing unused or redundant suppressions may reduce the complexity of reading the
static analysis configuration and can help with the understanding of what each suppression applies
to.
Using the output of the Diagnostic Accounting feature, the usefulness of suppression options can be determined. PC-lint Plus provides a script named unused.py in the extras/ directory of the PC-lint Plus distribution which detects unused and redundant suppression options.
Python 3.7 or higher installed
Diagnostic Accounting data from PC-lint Plus
Refer to the 17.11 Diagnostic Accounting section to gather the diagnostic accounting output.
These are python packages that may be installed to add functionality to the script.
| Package | Effect |
| tqdm | Displays a progress bar while the script is running. |
| ijson | Greatly reduces the peak memory (RAM) required to run the script, at the cost of slightly slower processing times. |
| pyzstd | Allows processing compressed *.zst files without having to decompress them first. |
The script must be provided with at least one set of Diagnostic Accounting data. The –diagnostic-data argument
tells the script where to locate the data, and how to interpret the paths specified within. It receives seven
arguments, which are the following Diagnostic Accounting JSON files in order, Options, Diagnostics,
Suppressions, Orbits, Metadata; then followed by the project directory, and a 0 or 1 depending on if the
Diagnostic accounting run was on a Windows machine. The project directory is a directory where
most modules are relative to, typically this is the current working directory of PC-lint Plus when
run.
Multiple sets of Diagnostic Accounting data for the same project can be provided to the script at once by adding another –diagnostic-data option and its arguments. This can create quite a long command line, therefore arguments to the script can be passed as response files by using a @ prefix, then the file name. Comments starting with # are ignored until the end of line, an example of such a file would be as follows.
# Diagnostic Data Input --diagnostic-data "json/options.json" "json/messages.json.zst" "json/suppressions.json" "json/orbits.json.zst" "json/metadata.json" "/home/user/my_project/" "0"
With the command invocation consisting of
python unused.py @config.txt
Where config.txt is a text file in the same directory as unused.py with the contents of the Listing 1
.
After processing time, the unused, overlapping, and redundant suppression options will be output in as <file>:<line>:<column>: <option> [Patterns = <patterns>]. Where <file> is the location of the option, which may be the command line as signified by <command line option>, <line> is the line number of the option, <column> is the column of the start of the option block, <option> is the full option text, and <patterns> is an optional comma separated list of patterns that are unused in the case of a parameterized suppressions with multiple patterns that all aren’t unused. This output format is equivalent to the vim errorfmt %f:%l:%c: %m.
There are three output categories. Unused or Impotent options are options which either never apply to or vote in any potential message emission. Overlapping overriders are options, such as single line suppressions, that override all other suppressions, but are duplicated for a given encounter. Those should be rare. Redundant options are a set of options that can be removed without affecting any message emission outcome. Consider that there are many possible sets of redundant options for a given run of PC-lint Plus, and this script tries to find the largest set and report those.
Sometimes certain suppression options are added to a project that only apply to a certain build target or build
configuration. This may look like a false positive for an unused suppression if the Diagnostic Accounting data was
run over the project with a different build configuration than the one the suppression was intended for. In order to
prevent this, the script can receive multiple diagnostic accounting –diagnostic-data options, each with its own
arguments containing diagnostic accounting files corresponding to different runs with different build configurations.
When this occurs the results from each set of data is combined to remove suppressions that are used in at least one
of the runs.
Another default that helps reduce the amount of unwanted reports is that any suppression option
that is simply enabling a message without parameterization, such as +e , is considered to be used.
Otherwise a preventative message such as 584 (trigraph sequence detected) would appear unused in a
project that does not currently have any trigraphs. Presumably the point of a +e584 option would
be to prevent any trigraphs from entering the project code, and it being unused is wanted. If this
assumption does not hold for your project, the –report-unused-default-enables allows reversal of this
assumption.
There is one other way to silence reported suppression options. Occasionally there will be some suppression options
that are unwanted to be reported, either because they are false positives, used in a different build configuration,
or from a third party library. The –freeze option can be passed to the script with five additional
arguments to specify which suppression option the freeze applies to. The effect of freezeing suppression
option is
to consider these suppressions used and necessary. The implication of considering these suppressions necessary is
that when processing overlapping or redundant options, any frozen options will not be considered
for elimination, however their effects will be considered. This means that you can make the script
‘reconsider’ its choices of redundant options to present, by freezing some of them. If the proposed options to
eliminate are desired to stay, freezing some of them may produce a different set of redundant options
instead.
The five arguments to –freeze are in order, a file path glob, an option text regex, a suppression pattern regex, a line number, and column number. The suppression pattern is matched against the patterns present in a parameterized suppression options such as -esym . In order for a –freeze option to affect a given suppression option, all provided arguments must match. An empty string passed as an argument will be considered to apply to all suppression options, and can be used to indicate a field doesn’t matter. An example in the format of a response file is listed below,
# Frozen suppressions specification file # Path # Option Text # Pattern # Line # Col --freeze "*co-gcc-c++17.lnt" "" "" "" "" --freeze "**/third_party/**" "" "" "" "" --freeze "OneFile.cpp" "\-fallthrough" "" "421" "17" --freeze "" "\-esym.*" "MyClass" "" ""
This script processes a lot of data, as can be seen from the disk space used by the input files. All of this data needs
to reside in system memory for processing, so the risk of running out of RAM or swap is present. There are some
measures that can be taken to reduce the peak memory usage. The easiest measure is to install the ijson python
package for the script to use, which allows the processing of the json data to occur without loading the entire file’s
data into memory at once.
If multiple –diagnostic-data arguments are passed to the script at once, the default behavior is to spawn multiple
processes to read the files concurrently, up to the number of cores on the system. The –concurrent-processes
argument can be provided to limit the number processes spawned for reading the files, this can lower peak memory
usage, which usually occurs during the file read processing. Note that if the progress bar library tqdm is installed, a
process will be spawned to update the terminal progress bars which does not count towards the amount specific by
the argument to the script.
A very drastic measure to reduce the peak memory usage is the pass the –only-unused-and-impotent argument which greatly reduces peak memory usage, typically down to megabytes when used in conjunction with ijson. However the effect of doing so means that redundant and overlapping suppression options cannot be calculated, and won’t be reported. Note that this also reduces processing time since the script can skip loading the large Diagnostics file.