VERA++(1)
π VERA++ β the manual, but make it slay
okay bestie, sit down, hydrate, because we're about to talk about vera++, the programmable verification and analysis tool for C++ β¨ it's a General Commands Manual page, it's giving main character energy, and you're gonna understand every last bit of it. no cap.
πΈ SYNOPSIS
vera++ [options] [file ...]
that's it. that's the whole vibe. options, then files, go π«Ά
β¨ Introduction β¨
so Vera++ is a programmable tool for verification, analysis and transformation of C++ source code π like, it doesn't just yell at you β it can also change things. icon.
the main usage scenarios foreseen for Vera++ are:
- make sure the source code complies with the given coding standards and conventions π
- give you source code metrics and statistics π
- do automated transformations of the source code, ranging from pretty-printing to diagnostics to fault injection and advanced testing π₯
the big design idea? Vera++ is a generic engine that parses the C++ code and hands it to your scripts in the form of collections of various objects β and those scripts define the concrete actions to be executed. you're the director, it's the camera π¬
currently the following object collections are provided:
- collection of source file names π
- collection of source lines for each file π
- collection of identified tokens in each file π€
note: it is foreseen that future versions of Vera++ will provide also the semantic view on the code. future tea, not spilled yet π΅
the most important feature of Vera++ is that all activities other than code parsing are defined by scripts. meaning it's flexible and extensible, fr fr. it's giving "build your own lint, bestie".
for example, compliance with coding standards can be expressed in terms of rules, each being defined by a separate script. those scripts can access all the collections listed above and perform actions related to the given rule. and you can ask to run any given script, or some defined set of scripts, in a single program execution π«
as a simple example: a coding convention that limits the length of the source line can be implemented as a script that traverses the collection of files and the collection of source lines and checks whether each source line fits within the given limits. then a report can be generated for each non-conforming line of code, so you get clear info about where the problem is located π
all existing rules present their reports in the format that is compatible with regular compiler's output, so integrating Vera++ with your existing build framework is easy peasy π
similarly, automated transformation procedures are implemented as separate scripts that scan the above collections and produce another source files according to their algorithms. a simple example of such a transformation might be a script that removes empty lines from source code π§Ή
the Tcl programming language is currently supported for scripts that run within Vera++. yes bestie, Tcl. she's vintage, she's powerful, we respect her π
π Running Vera++ π
Vera++ needs to know where the rules and transformation scripts are located. the following rules are applied (in order, first match wins, no take-backs):
- if the
--rootoption is used, its argument is used as the name of the directory where thescriptssubdirectory with scripts should be located, otherwise - if the
VERA_ROOTenvironment variable is defined, it is used as the name of the directory where thescriptssubdirectory with scripts should be located, otherwise - if the
HOMEenvironment variable is defined, then the~/.vera++directory is used (and it should contain thescriptssubdirectory with scripts), otherwise - the current directory should contain the
scriptssubdirectory π«
π Options π
Vera++ recognizes the following parameters:
-β (a single minus) indicates that the source code to check will be provided on thestdinπ€-p,--profileprofilename β instructs the program to execute all rules defined in the given profile. the profile name is just a name of the file that will be found under theprofilesdirectory, and the content of this file is a Tcl script that must set arulesvariable to be the list of all rules that are part of the profile. an example profile definition that groups three rules (L001,L002andL003) might look like:
set rules {
L001
L002
L003
}
there is always a default profile that lists all existing rules β it is used when no profile is named explicitly π
-R,--rulerulename β instructs the program to execute the given rule. note that the name of the rule should not contain the file extension of the script implementing the rule β this is added automatically, so that for example--rule my_rulemeans thatVera++will find themy_rule.tclscript and will run it π--transformtransformationname β instructs the program to execute a single named transformation; the naming scheme is the same as for the--ruleoption β¨-o,--std-reportfilename β writes the standard (gcc-like) report to this file. a single dash-means that the standard output or the error output will be used, depending on the usage of the--warningor--erroroption. this option may be used several times in order to produce the reports in several locations β for example on the standard output and in a file. default value is-π-v,--vc-reportfilename β writes the Visual C report to this file. a single dash-means that the standard output or the error output will be used, depending on the usage of the--warningor--erroroption. this option may be used several times in order to produce the reports in several locations β for example on the standard output and in a file. this report is not produced by default π-x,--xml-reportfilename β writes the XML report to this file. not used by default. a single dash-means that the standard output or the error output will be used, depending on the usage of the--warningor--erroroption. this option may be used several times in order to produce the reports in several locations β for example on the standard output and in a file. this report is not produced by default π¦-c,--checkstyle-reportfilename β writes the checkstyle report to this file. not used by default. a single dash-means that the standard output or the error output will be used, depending on the usage of the--warningor--erroroption. this option may be used several times in order to produce the reports in several locations β for example on the standard output and in a file. this report is not produced by default π-s,--show-ruleβ includes the name of the rule in each report line π·οΈ-d,--no-duplicateβ instructs the program to omit duplicated messages in the final report (the duplicates can be a result of violating the same rule many times in the same line of source code) π§½-w,--warningβ reports are marked as warning and generated on the error output β οΈ-e,--errorβ reports are marked as error and generated on the error output. an non zero exit code is used when one or more reports are generated π¨-q,--quietβ don't display the reports. this option is best used with--summaryand/or with--errorπ€«-S,--summaryβ displays the number of reports and the number of processed files π--parametersfilename β instructs the program to read parameter values from the given file; each parameter association should be placed in a separate line of this file. this option may be used several times π-P,--parameterparameterassociation β provides the value of the named parameter to the scripts (see the documentation of each script to see whether it recognizes any parameters); the parameter association has the formname=valueπ§--exclusionsexclusionsfilename β instructs the program to exclude some source files from rule checks, as described in the given file; the content of this file is a Tcl script that must set aruleExclusionsarray, where keys are rule names and values are lists of files to omit for the given rule. for example:
set ruleExclusions(L002) {
some_file.cpp
}
set ruleExclusions(T005) {
some_file.cpp
some_other_file.cpp
}
note that the given file names are compared for exact match with the source file names that are provided as parameters to Vera++. this means that links in paths are not resolved for comparison purposes. this option may be used several times β οΈ
-i,--inputsfilename β the inputs are read from that file. a single dash-means that the files to check will be read from the standard input. this option may be used several times π₯-r,--rootpath β uses the given path as the vera++ root directory π³--versionβ prints the program version information and exits π-h,--helpβ prints the list of recognized options and exits π--β (a double dash) do not interpret any more arguments as options β
arguments that are not starting with a dash - are treated as source files to check. files starting with a dash can be checked by prefixing them with the current directory shortcut ./ π‘
when no input file is provided either as an argument or with the --input option, the list of source file names is read from the standard input π
π Examples of executing Vera++ with rules π
to execute all default verification rules against the file file.cpp, run:
vera++ file.cpp
to execute only rule L001 (this rule ensures that there is no trailing whitespace in each source line) against the same file, run:
vera++ -R L001 file.cpp
to execute rule L004 (this rule checks for too long source lines) with the parameter value providing 78 as the maximum line length, run:
vera++ -R L004 -P max-line-length=78 file.cpp
to execute all rules from your favorite profile (assuming that the my_favorite profile definition is stored in the profiles directory) against all header files in the current filesystem subtree, run:
find . -name '*.h' | vera++ --profile my_favorite
note: Vera++ collects the reports generated by each rule and prints them out sorted and after all rules were executed. if there were no problem reports, the output of the program is empty π€
note:Vera++reports are generated on the standard output by default, making them easy to use with a pipe. the--warningand--erroroptions are changing the output to the standard error. the options--std-report,--vc-report,--xml-reportand--quietmay be used to disable the output to the standard or error output π°
okay that's part one bestie, there's more coming so don't go anywhere π
πΈ Examples of executing Vera++ with transformations β¨
Okay bestie, say you wanna run the trim_right source code transformation (that's the one that deletes the trailing whitespace rule L001 was just yelling about) on literally every .cpp file chillin' in the current directory. You type:
vera++ --transform trim_right *.cpp
And boom π₯ β each .cpp file gets backed up with the extra extension .bak, and the files get trimmed by removing trailing whitespace. The exact behavior is defined by the script named trim_right.tcl in the scripts/transformations directory. (Yes, Tcl. She's vintage. She's iconic. π
)
π§ͺ Running Vera++ as a test with CMake
CMake offers the possibility to run tests that are considered to pass when they return a 0 value and to fail otherwise. And luckily for us, vera++, when used with the --error option, has exactly this behavior. Slay. π
Creating the test is just a matter of listing the sources to check:
file(GLOB_RECURSE srcs
${CMAKE_SOURCE_DIR}/src/*.cpp
${CMAKE_SOURCE_DIR}/src/*.h)
add_test(NAME VeraStyle
COMMAND vera++
--error
${srcs})
π¨ Running Vera++ during the build with CMake
So, running vera++ in a test integrates quite badly with the IDEs or with CDash (http://cdash.org): the reports are hidden in the test log, and it is not easy to look at the problematic code. Lowkey tragic. π Moreover, a failure in the coding style is not the same as a failure in a unit or functional test, and shouldn't appear in the same way. Different vibe, different category, periodt.
Another option is to run vera++ during the build and make it generate warnings that are well interpreted by the IDEs and CDash. In QtCreator for instance, it is then possible to click on the warning to go to the problematic code. Obsessed. β¨
Running vera++ during the build can be done in a similar way to the previous section, by replacing the add_test() call with a add_custom_target() that will run the style check every time the custom target is built.
file(GLOB_RECURSE srcs
${CMAKE_SOURCE_DIR}/src/*.cpp
${CMAKE_SOURCE_DIR}/src/*.h)
add_custom_target(VeraStyle ALL
vera++
--warning
${srcs})
For large projects, running the style check every time can be quite time consuming and uncomfortable for the developer. It is then more convenient to split the style check in several parts that can be run in parallel, and to avoid rerunning the check if the files to check have not been modified. A vera++ macro is available to do that very easily:
find_package(vera++)
include(${VERA++_USE_FILE})
add_vera_targets(*.h *.cpp
RECURSE
ROOT "${CMAKE_SOURCE_DIR}")
This macro adds a new style_reports target that is run every time a source file is modified. A style target is still available to force the style check. The target names can be configured with the parameters NAME and NAME_ALL. This macro is the recommended way to use vera++ with CMake. π«Ά
π΅ Backward compatibility with vera++ 1.1
Vera++ is still mostly compatible with the vera++ 1.1 command line interface, but this feature is planned for removal and its usage is not recommended. Consider this the "we keep it around for the elders" corner of the codebase. π
Vera++ tries to detect if the old command line style is used by searching for the old options in the arguments. If no old style option is found, vera++ uses the new command line parser.
The command line style can be forced to the old style by setting the environment variable VERA_LEGACY to on, true or 1. Any other value will force vera++ to use the new command line style.
β οΈ Note: the behavior ofvera++is not backward compatible withvera++1.1 when no option is passed tovera++andVERA_LEGACYis not set:
- the reports are generated on the standard output instead of the error output;
- a single dash
-means that the source code to check is read from the standard input instead of reading the list of files to check; - the lack of input files makes
vera++read the standard input instead of generating an error.
π Rules
π F001 Source files should not use the '\r' (CR) character
As a commonly accepted practice, line breaks are denoted by a single '\n' (LF) character or by two characters "\r\n" (CRLF). A single appearance of '\r' (CR) is discouraged.
Compliance: Boost
π F002 File names should be well-formed
The source file names should be well-formed in the sense of their allowed maximum length and directory depth. Directory and file names should start with alphabetic character or underscore. In addition, directory names should not contain dots and file names can have only one dot.
Recognized parameters:
max-directory-depthβ default8. Maximum depth of the directory structure.max-dirname-lengthβ default31. Maximum length of the directory path component.max-filename-lengthβ default31. Maximum length of the leaf file name.max-path-lengthβ default100. Maximum length of the full path.
Compliance: Boost
π¬οΈ L001 No trailing whitespace
Trailing whitespace is any whitespace character (space or tab) that is placed at the end of the source line, after other characters or alone.
The presence of trailing whitespace artificially influences some source code metrics and is therefore discouraged.
As a special case, the trailing whitespace in the otherwise empty lines is allowed provided that the amount of whitespace is identical to the indent in the previous line β this exception is more friendly with less smart editors, but can be switched off by setting non-zero value for the strict-trailing-space parameter.
Recognized parameters:
strict-trailing-spaceβ default0. Strict mode for trailing whitespace.
Compliance: Inspirel
π« L002 Don't use tab characters
Horizontal tabs are not consistently handled by editors and tools. Avoiding them ensures that the intended formatting of the code is preserved. (Your tabs are giving chaos, bestie. π)
Compliance: HICPP, JSF
π«₯ L003 No leading and no trailing empty lines
Leading and trailing empty lines confuse users of various tools (like head and tail) and artificially influence some source code metrics.
Compliance: Inspirel
π L004 Line cannot be too long
The source code line should not exceed some reasonable length.
Recognized parameters:
max-line-lengthβ default100. Maximum length of source code line.
Compliance: Inspirel
π«§ L005 There should not be too many consecutive empty lines
The empty lines (if any) help to introduce more "light" in the source code, but they should not be overdosed in the sense that too many consecutive empty lines make the code harder to follow.
Lines containing only whitespace are considered to be empty in this context.
Recognized parameters:
max-consecutive-empty-linesβ default2. Maximum number of consecutive empty lines.
Compliance: Inspirel
π L006 Source file should not be too long
The source file should not exceed a reasonable length.
Long source files can indicate an opportunity for refactoring. (Girl, that file is 4000 lines and you know it. π«Ά)
Recognized parameters:
max-file-lengthβ default2000. Maximum number of lines in a file.
Compliance: Inspirel
β‘οΈ T001 One-line comments should not have forced continuation
The one-line comment is a comment that starts with //.
The usual intent is to let the comment continue till the end of the line, but the preprocessing rules of the language allow to actually continue the comment in the next line if line-splicing is forced with the backslash at the end of the line:
void foo()
{
// this comment is continued in the next line \
exit(0);
}
It is not immediately obvious what happens in this example. Moreover, the line-splicing works only if the backslash is really the last character in the line β which is error prone because any white characters that might appear after the backslash will change the meaning of the program without being visible in the code. π Not the sneaky invisible bug era.
Compliance: Inspirel
π T002 Reserved names should not be used for preprocessor macros
The C++ Standard reserves some forms of names for language implementations. One of the most frequent violations is a definition of preprocessor macro that begins with underscore followed by a capital letter or containing two consecutive underscores:
#define _MY_MACRO something
#define MY__MACRO something
Even though the majority of known compilers use more obscure names for internal purposes and the above code is not likely to cause any significant problems, all such names are formally reserved and therefore should not be used.
Apart from the use of underscore in macro names, preprocessor macros should not be used to redefine language keywords:
#define private public
#define const
Compliance: ISO
β£ T003 Some keywords should be followed by a single space
Keywords from the following list:
caseclassdeleteenumexplicitexterngotonewstructunionusing
should be followed by a single space for better readability.
Compliance: Inspirel
π T004 Some keywords should be immediately followed by a colon
Keywords from the following list:
defaultprivateprotectedpublic
should be immediately followed by a colon, unless used in the list of base classes:
class A : public B, private C
{
public:
A();
~A();
protected:
// ...
private:
// ...
};
void fun(int a)
{
switch (a)
{
// ...
default:
exit(0);
}
}
Compliance: Inspirel
π T005 Keywords break and continue should be immediately followed by a semicolon
The break and continue keywords should be immediately followed by a semicolon, with no other tokens in between:
β¦and the whole shape of it is this:
while (...)
{
if (...)
{
break;
}
if (...)
{
continue;
}
// ...
}
Compliance: Inspirel πΈ
T006 Keywords return and throw should be immediately followed by a semicolon or a single space
okay so return and throw are the β¨main charactersβ¨ of this rule and they do not get to have a whole entourage of random spaces trailing behind them. Right after return or throw, it's either a ; or literally one single space. That's it, that's the vibe. π
void fun()
{
if (...)
{
return;
}
// ...
}
int add(int a, int b)
{
return a + b;
}
and there's one little exception, for exception specifications:
void fun() throw();
Compliance: Inspirel π
T007 Semicolons should not be isolated by spaces or comments from the rest of the code
bestie the semicolon is a team player, not a solo act. It should NOT be floating off on its own, separated from the rest of the code by whitespace or by comments. Keep it glued to its people. π«Ά
int a ; // bad
int b
; // bad
int c; // OK
Exception alert!! Semicolons that are surrounded by spaces ARE allowed inside for loops:
for ( ; ; ) // OK as an exception
{
// ...
}
Compliance: Inspirel π
T008 Keywords catch, for, if, switch and while should be followed by a single space
So catch, for, if, switch and while β every single one of them β get exactly one space after them, and then that opening left parenthesis. One space. Not zero, not four. One. The consistency is the point, iykyk. β¨
catch (...)
{
for (int i = 0; i != 10; ++i)
{
if (foo(i))
{
while (getline(cin, line))
{
switch (i % 3)
{
case 0:
bar(line);
break;
// ...
}
}
}
}
}
Compliance: Inspirel π
T009 Comma should not be preceded by whitespace, but should be followed by one
The comma has a strict personal-space policy, fr fr. Nothing on its left side β no whitespace. Then on its right side, yes, one whitespace, please and thank you. This goes for commas used as an operator AND commas in all the various lists. π
void fun(int x, int y, int z);
int a[] = {5, 6, 7};
class A : public B,
public C
{
// ...
};
and there's an exception allowed for operator,:
struct A {};
void operator,(const A &left, const A &right);
Compliance: Inspirel π
T010 Identifiers should not be composed of 'l' and 'O' characters only
The characters 'l' (that's lowercase L, not a one, not a pipe, lowercase L) and 'O' (that's uppercase O, not a zero bestie π) should not be the ONLY characters in an identifier. Because if they are, your identifier starts looking exactly like a numeric literal and everybody loses. Just don't. π
Compliance: Inspirel β
T011 Curly brackets from the same pair should be either in the same line or in the same column
Matching curly brackets β the { and the } that belong to each other β need to either live on the same line or sit in the same column. Why? Because it makes scopes pop visually, like the code is showing you exactly where the boundaries are. But it also lets you keep that concise one-line-definition energy and empty blocks. Best of both worlds, honestly. π
class MyException {};
struct MyPair
{
int a;
int b;
};
enum state { close, open };
enum colors
{
black,
red,
green,
blue,
white
};
Compliance: Inspirel π
T012 Negation operator should not be used in its short form
The negation operator β the exclamation mark β is lowkey unreadable because it's so teeny tiny and terse. Like, it's giving "blink and you'll miss the logic." So the move is to prefer explicit logical comparisons, or the alternative tokens, for way better readability. π¦
if (!cond) // error-prone
if (cond == false) // better
if (not cond) // better (alternative keyword)
Compliance: Inspirel β¨
T013 Source files should contain the copyright notice
The copyright notice is required by coding standards and guidelines. And in some countries? every written artwork has some copyright, even when it's implicit and nobody said anything. So the vibe is: put an explicit notice so there's zero later confusion. Protect your work bestie. π«Ά
This rule verifies that at least one comment in the source file contains the word "copyright".
Compliance: Boost π
T014 Source files should refer the Boost Software License
The Boost Software License needs to be referenced in the source code. π
This rule verifies that at least one comment in the source file contains the phrase "Boost Software License".
Heads up!! This rule is very specific to the Boost libraries and to those projects that choose to use the Boost license. So it is therefore not part of the default profile. π
Compliance: Boost π
T015 HTML links in comments and string literals should be correct
Links that get embedded in comments and in string literals have to be in correct form, and they have to actually reference files that exist. No dead links, no broken links, no "I'll fix it later." We don't do that here. π
Compliance: Boost πΈ
T016 Calls to min/max should be protected against accidental macro substitution
Calls to the min and max functions need protection from accidental macro substitution. Because macros are sneaky and they will absolutely jump in uninvited. π
x = max(y, z); // wrong, vulnerable to accidental macro substitution
x = (max)(y, z); // OK
x = max BOOST_PREVENT_MACRO_SUBSTITUTION (y, z); // OK
Compliance: Boost π¦
T017 Unnamed namespaces are not allowed in header files
Unnamed namespaces? Not allowed in header files. Periodt. π
So the typical use of an unnamed namespace is to hide module-internal names from the outside world. Cool in theory. But header files get physically concatenated into a single translation unit, and that logically merges together all namespaces that share a name. Unnamed namespaces get merged in that same process too β which effectively undermines their entire original purpose. The whole point just evaporates. π
So: use named namespaces in header files. Unnamed namespaces are allowed in implementation files only.
Compliance: Boost π
T018 Using namespace is not allowed in header files
using namespace directives are not allowed in header files. Nope. β
Here's the tea: the using namespace directive imports names from the given namespace, and when you use it in a header file, it influences the global namespace of every single file that directly or indirectly includes that header. That's a lot of chaos you did not sign up for.
It is imaginable to use the using namespace directive in a limited scope inside a header file β like in a template or an inline function definition β but for the sake of consistency, this is also discouraged. So likeβ¦ just don't. π
Compliance: C++ Coding Standards β
T019 Control structures should have complete curly-braced block of code
Control structures managed by for, if and while constructs can be associated with a single instruction, or with a complex block of code. Standardizing on the curly-braced blocks in ALL cases lets you dodge the common pitfalls and makes the code look way more uniform. It's giving consistency and we're obsessed. π
if (x) foo(); // bad style
if (x) { foo(); } // OK
if (x)
foo(); // again bad style
if (x)
{ // OK
foo();
}
if (x)
while (y) // bad style
foo(); // bad style
if (x)
{ // OK
while (y)
{ // OK
foo();
}
}
for (int i = 0; i = 10; ++i); // oops!
cout << "Hello\n";
for (int i = 0; i = 10; ++i) // OK
{
cout << "Hello\n";
}
Compliance: Inspirel π
β¨ Transformations β¨
Vera++ isn't just about yelling at your code, bestie β it can also rewrite it for you. These are the transformations, and yes, several of them edit your files in place, so buckle up. π
π move_includes β Change prefix of #include paths
Okay so this one lets you change up the prefix of file paths inside your #include directives. That's it. That's the vibe.
Why would you even want that? Because it helps you move whole libraries from one file tree to another without losing your mind. π³β‘οΈπ²
The manual literally says: please use this transformation as a boilerplate for your own customized version. So it's giving starter-pack energy. Take it and make it yours. π«Ά
So for example, this file:
#include "boost/shared_ptr.hpp"
#include "boost/bind.hpp"
will be transformed into:
#include "boom/shared_ptr.hpp"
#include "boom/bind.hpp"
Note: the transformation is performed in place, which means the source files are modified. No take-backs, no undo button. π
π§ move_macros β Change prefix in macros
Same energy, but for macros. It changes the prefix of macros so you can move whole libraries or source sets from one naming convention to another. Glow-up for your identifiers, fr fr. β¨
Also flagged as a boilerplate for your own customized version. Copy it, remix it, make it yours. π
For example, this file:
#define BOOST_SOME_MACRO 1
// ...
#ifdef BOOST_SOME_MACRO
// ...
#endif
will be transformed into:
#define BOOM_SOME_MACRO 1
// ...
#ifdef BOOM_SOME_MACRO
// ...
#endif
Note: this transformation actually does not check whether the given identifier is indeed a macro name, and the prefix replacement is performed systematically on all identifiers that match. So it will absolutely catch strays. π
Note: the transformation is performed in place, which means the source files are modified.
π move_namespace β Change namespace name
Now we're talking. This one consistently changes the namespace name, which is iconic if you want to move whole libraries or source sets from one namespace to another β for example to let two different versions of the same library coexist without beefing. π€
Yep, you guessed it: boilerplate for your own customized version. Use it as a base. π
For example, this file:
namespace boost
{
void foo();
}
void boost::foo() {/* ... */}
will be transformed into:
namespace boom
{
void foo();
}
void boom::foo() {/* ... */}
Note: this transformation actually does not check whether the given identifier is indeed a namespace name, and the replacement is performed systematically on all identifiers that match. So do not use it on code that overloads namespace names for other purposes. That would be a whole mess. π
Note: the transformation is performed in place, which means the source files are modified.
πΈ to_lower β Change identifier naming convention from CamelCase to standard_lowercase
The lowercase agenda! This transformation switches the naming convention of all identifiers from CamelCase to standard_lowercase, the same style the standard library and Boost use. Ate. π
So this code:
namespace MyTools
{
class MyClass
{
public:
void myFunction();
};
}
will be transformed into this:
namespace my_tools
{
class my_class
{
public:
void my_function();
};
}
Note: the transformation is performed in place, which means the source files are modified.
Note: this transformation does not modify comments and string literals. They stay exactly how you left them, untouched, main-character style. π
π¦ to_xml β Transform C++ code into XML
This transformation generates an XML tree where nodes relate to C++ source code tokens. Tokenizer-core. π₯
For example, the following file (file.cpp):
#include <iostream>
int main()
{
std::cout << "Hello World\n";
}
will be transformed into a new file named file.cpp.xml:
<?xml version="1.0" encoding="ISO-8859-1"?>
<cpp-source file-name="test.cpp">
<token name="pp_hheader" line="1" column="0">#include <iostream></token>
<token name="newline" line="1" column="19">![CDATA[
]]</token>
<token name="newline" line="2" column="0">![CDATA[
]]</token>
<token name="int" line="3" column="0">int</token>
<token name="space" line="3" column="3"> </token>
<token name="identifier" line="3" column="4">main</token>
<token name="leftparen" line="3" column="8">(</token>
<token name="rightparen" line="3" column="9">)</token>
<token name="newline" line="3" column="10">![CDATA[
]]</token>
<token name="leftbrace" line="4" column="0">{</token>
<token name="newline" line="4" column="1">![CDATA[
]]</token>
<token name="space" line="5" column="0"> </token>
<token name="identifier" line="5" column="4">std</token>
<token name="colon_colon" line="5" column="7">::</token>
<token name="identifier" line="5" column="9">cout</token>
<token name="space" line="5" column="13"> </token>
<token name="shiftleft" line="5" column="14"><<</token>
<token name="space" line="5" column="16"> </token>
<token name="stringlit" line="5" column="17">"Hello World\n"</token>
<token name="semicolon" line="5" column="32">;</token>
<token name="newline" line="5" column="33">![CDATA[
]]</token>
<token name="rightbrace" line="6" column="0">}</token>
<token name="newline" line="6" column="1">![CDATA[
]]</token>
<token name="eof" line="7" column="0"></token>
</cpp-source>
Note: if the source code does not use line splicing, then concatenation of all XML node values is equivalent to the original C++ code. So the round-trip lowkey works. π«Ά
π to_xml2 β Transform C++ code into XML (another variant)
Same idea, bestie β this one also generates an XML tree where nodes relate to C++ source code tokens.
The difference between this version and the one named to_xml is that here nodes have names related to token types, which can make it easier for some further XML transformations. Slay, honestly. β¨
For example, the following file (file.cpp):
#include <iostream>
int main()
{
std::cout << "Hello World\n";
}
will be transformed into a new file named file.cpp.xml:
<?xml version="1.0" encoding="ISO-8859-1"?>
<cpp-source file-name="test.cpp">
<pp_hheader line="1" column="0">#include <iostream></pp_hheader>
<newline line="1" column="19">![CDATA[
]]</newline>
<newline line="2" column="0">![CDATA[
]]</newline>
<int line="3" column="0">int</int>
<space line="3" column="3"> </space>
<identifier line="3" column="4">main</identifier>
<leftparen line="3" column="8">(</leftparen>
<rightparen line="3" column="9">)</rightparen>
<newline line="3" column="10">![CDATA[
]]</newline>
<leftbrace line="4" column="0">{</leftbrace>
<newline line="4" column="1">![CDATA[
]]</newline>
<space line="5" column="0"> </space>
<identifier line="5" column="4">std</identifier>
<colon_colon line="5" column="7">::</colon_colon>
<identifier line="5" column="9">cout</identifier>
<space line="5" column="13"> </space>
<shiftleft line="5" column="14"><<</shiftleft>
<space line="5" column="16"> </space>
<stringlit line="5" column="17">"Hello World\n"</stringlit>
<semicolon line="5" column="32">;</semicolon>
<newline line="5" column="33">![CDATA[
]]</newline>
<rightbrace line="6" column="0">}</rightbrace>
<newline line="6" column="1">![CDATA[
]]</newline>
<eof line="7" column="0"></eof>
</cpp-source>
Note: if the source code does not use line splicing, then concatenation of all XML node values is equivalent to the original C++ code. Same deal, same guarantee. π
π trim_right β Remove trailing white space
The tidy-up transformation we all need in our lives. This one removes the trailing whitespace from each line of code. Bye, invisible characters nobody asked for. π§Ήβ¨
You can treat it as a quick remedy for problems reported by rule L001. iykyk.
Note: the transformation is performed in place, which means the source files are modified.
π Script API π
okay so listen β the scripts (both the rules and the transformations) are written in Tcl, and they get run by the embedded interpreter that has access to all the relevant state of the program. Vera++ hands you a whole toolbox of commands so you can do easy read-only operations on everything it gathered while parsing your source files. it's giving very thoughtful. π«Ά
here's the full command menu:
getSourceFileNamesβ returns the list of file names that were provided to Vera++ as program parameters.getLineCount fileNameβ returns the number of lines in the given source file.getAllLines fileNameβ returns the list of lines, in their natural order, that form a give source file.getLine fileName lineNumberβ returns the selected line; line numbers are counted from 1.getTokens fileName fromLine fromColumn toLine toColumn filterβ returns the list of tokens, in their natural order, from the given source file, that match the given selection criteria.
the meaning of the arguments for selecting tokens is:
fromLineβ the lowest line number (counted from 1), inclusivefromColumnβ the lowest column number (counted from 0), inclusivetoLineβ the highest line number, inclusive;-1means that the selected range spans to the end of the filetoColumnβ the highest column number, exclusive;-1means that the selected range spans to the end of the line defined bytoLinefilterβ the list of selected token types, the recognized token types are listed below; if this list is empty, then all token types are allowed
the getTokens command returns a list of lists β and those nested lists have the following elements:
valueβ the literal text of the tokenlineNumberβ the line number (from 1) where the token appearscolumnNumberβ the column number (from 0) where the token appearsnameβ the name or type of the token; see below for the list of recognized token types
getParameter name defaultValueβ returns the value of the given parameter, or the provided default value if no such parameter is defined.report fileName lineNumber messageβ registers a report for the given file and line; this report is printed at the end of the program execution, sorted by file and line number. use this command to generate output that is compatible with the warning/error output format of popular compilers. π―
π¦ Examples: π¦
to process all lines from all source files, use the following code pattern:
foreach fileName [getSourceFileNames] {
foreach line [getAllLines $fileName] {
# ...
}
}
to process all tokens from all source files, use:
foreach fileName [getSourceFileNames] {
foreach token [getTokens $fileName 1 0 -1 -1 {}] {
set tokenValue [lindex $token 0]
set lineNumber [lindex $token 1]
set columnNumber [lindex $token 2]
set tokenType [lindex $token 3]
# ...
}
}
to process only curly braces from the given source file, use:
foreach token [getTokens $fileName 1 0 -1 -1 {leftbrace rightbrace}] {
# ...
}
the complete rule script for verifying that the lines are no longer than some limit (the limit can be provided as a parameter, but the default value is defined in by the script itself):
# Line cannot be too long
set maxLength [getParameter "max-line-length" 100]
foreach f [getSourceFileNames] {
set lineNumber 1
foreach line [getAllLines $f] {
if {[string length $line] > $maxLength} {
report $f $lineNumber "line is longer than ${maxLength} characters"
}
incr lineNumber
}
}
the above script is actually the implementation of rule L004. β¨
πΈ Notes about line splicing πΈ
as required by the C++ ISO standard, the line splicing (with the backslash at the end of the line) is performed before tokenizing. which means the lists of tokens might not strictly fit the list of lines. π
due to the internal mechanisms of the parser, the line splicing freezes the line counter and forces the column counter to continue until the last line in the spliced block. so basically: there might be physical non-empty lines that apparently don't have any tokens, and there might also be tokens that have column numbers not matching the physical source line lengths. it's giving chaos, but it's documented chaos, so we love her. π
π Recognized token types π
the following token types are recognized by the parser and can be used for filter selection in the getTokens command (heads up β some of these token types are related to compiler extensions):
and
andand
andassign
any
arrow
arrowstar
asm
assign
auto
bool
break
case
catch
ccomment
char
charlit
class
colon
colon_colon
comma
compl
const
constcast
continue
contline
cppcomment
decimalint
default
delete
divide
divideassign
do
dot
dotstar
double
dynamiccast
ellipsis
else
enum
eof
eoi
equal
explicit
export
extern
false
float
floatlit
for
friend
goto
greater
greaterequal
hexaint
identifier
if
inline
int
intlit
leftbrace
leftbracket
leftparen
less
lessequal
long
longintlit
minus
minusassign
minusminus
msext_asm
msext_based
msext_cdecl
msext_declspec
msext_endregion
msext_except
msext_fastcall
msext_finally
msext_inline
msext_int16
msext_int32
msext_int64
msext_int8
msext_leave
msext_region
msext_stdcall
msext_try
mutable
namespace
new
newline
not
notequal
octalint
operator
or
orassign
oror
percent
percentassign
plus
plusassign
plusplus
pound
pound_pound
pp_define
pp_elif
pp_else
pp_endif
pp_error
pp_hheader
pp_if
pp_ifdef
pp_ifndef
pp_include
pp_line
pp_number
pp_pragma
pp_qheader
pp_undef
pp_warning
private
protected
public
question_mark
register
reinterpretcast
return
rightbrace
rightbracket
rightparen
semicolon
shiftleft
shiftleftassign
shiftright
shiftrightassign
short
signed
sizeof
space
space2
star
starassign
static
staticcast
stringlit
struct
switch
template
this
throw
true
try
typedef
typeid
typename
union
unsigned
using
virtual
void
volatile
wchart
while
xor
xorassign
β Note β
there's a predefined rule named DUMP that prints on the screen all tokens with their types and position. this rule is genuinely so helpful as a guideline for creating your own custom filtering criteria:
vera++ --rule DUMP myfile.cpp
it's giving debugging main character energy fr fr. π
π Changes π
π¦ Vera++ 1.2.1 π¦
Vera++ 1.2.1 differs from 1.2.0 in the following ways:
- BUGFIX: fix
--inputsin order to be able to read the inputs from a file
π¦ Vera++ 1.2.0 π¦
Vera++ 1.2.0 differs from 1.1.2 in the following ways:
- Full Tcl stack printed when a rule fail.
- New command line interface that support long and short options. The old style command line is still usable for backward compatibility.
- Produce output to standard output by default so the output can easily be piped to another program. The options
--warningand--errormake vera++ produce its output on the error output. - CMake macros to easily run vera++ in any CMake project.
- Easier integration in a test chain by return an error code when at least one report is produced and the
--erroroption is used.--quietand--summarycan also help to better integrate vera++ in the test chain. - The standard output format match gcc's output format for a better integration in a build chain.
- Can read the list of files to check from one or more files.
- Can read the source code to check from the standard input.
- Can write the several reports in differents formats and in different places.
- Added
--rootoption to point the the vera root directory from the command line and ease the usage of custom rules. - Reports can be produced in checkstyle (
http://checkstyle.sourceforge.net/) XML format. - Vera++ no more impose the extension of the source files to check.
- Several exclusion files can be used.
- Several parameter files can be used.
- Build system now uses CMake.
- Builds with TCL 8.6.
- Don't require Boost sources to build.
- New documentation generation process to unify the wiki, the html doc and the manpage.
- Binary packages for MS Windows and Mac OS X (and others).
- Nightly tests to avoid regressions.
- New website.
- BUGFIX: the rule T019 now works properly with
do ... whileblocks.
π¦ Vera++ 1.1.2 π¦
Vera++ 1.1.2 differs from 1.1.1 in the following ways:
- Added
-xmlreportoption.
π¦ Vera++ 1.1.1 π¦
Vera++ 1.1.1 differs from 1.1.0 in the following ways:
- Added
-helpoption. - Updated code for compatibility with newer versions of Boost. The reference version of the Boost library is now 1.35 or 1.36.
- BUGFIX: Corrected handling of current directory when neither
HOMEnorVERA_ROOTis specified (this affects Windows users only).
π¦ Vera++ 1.1.0 π¦
Vera++ 1.1.0 differs from 1.0.0 in the following ways:
- Updated rules:
- T002: additionally recognizes redefinition (
#define) of keywords - T009: recognizes comment adjacent to colon as an exception to the rule
- T002: additionally recognizes redefinition (
- Added rules:
- F001: Source files should not use the
\r(CR) character - F002: File names should be well-formed Note: F002 is not part of the default profile.
- T012: Negation operator should not be used in its short form
- T013: Source files should contain the copyright notice
- T014: Source files should refer the Boost Software License Note: T014 is not part of the default profile.
- T015: HTML links in comments and string literals should be correct
- T016: Calls to min/max should be protected against accidental macro substitution
- T017: Unnamed namespaces are not allowed in header files
- T018: Using namespace is not allowed in header files
- T019: Control structures should have complete curly-braced block of code
- F001: Source files should not use the
- Added predefined boost profile to emulate the original Boost inspect tool.
- Added transformations:
- move_namespace: Changes the given identifier, useful for moving the whole project from one namespace to another.
- move_macros: Changes the given prefix in all identifiers, useful for moving the whole set of macros that have common prefix.
- move_includes: Changes the given part of
#include "..."directives, useful for moving libraries and whole sets of header files.
- Added documentation for all available transformations.
- Makefiles modified to better support Windows make users.
- Extension
.ippadded to the list of recognized source file extensions. - New option
-showrulesincludes name of rules in each report line. - Changed the profile definition to be an active Tcl script instead of passive text file.
- Added the possibility to define exclusions to rule checks.
- BUGFIX: Corrected handling of newline tokens.
π AUTHORS π
Maciej Sobczak; Vincent HobeΓ―ka; GaΓ«tan Lehmann.
π and that's the whole manual, bestie! this one comes from the Vera++ User Manuals, dated April 10, 2013, and it's the VERA++(1) page. periodt. π
π¦ where this page comes from
- debian bookworm: vera++ 1.2.1-2+b7 Β· BSL-1.0
- debian trixie: vera++ 1.2.1-2+b8 Β· BSL-1.0
- ubuntu jammy: vera++ 1.2.1-2build9 Β· BSL-1.0
- ubuntu noble: vera++ 1.2.1-2build10 Β· BSL-1.0
- ubuntu resolute: vera++ 1.2.1-2build12 Β· BSL-1.0
- ubuntu stonking: vera++ 1.3.0-4 Β· BSL-1.0
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms