A Static Analysis Tool for C++
Greg Utas - 28/Apr/2020
Greg Utas - 28/Apr/2020
[SHOWTOGROUPS=4,20]
Automating Scott Meyers' recommendations and cleaning up #include directives.
This article is a user guide to a static analysis tool for C++ code. Among other things, the tool can clean up #include lists and highlight violations of C++ best practices. It can also implement some of its suggestions by editing the code. The article also provides a high-level overview of the tool's implementation.
Introduction
C++ is a large language—too large, some would argue. Because it's a superset of C, it's easy for developers with a C background to build a hybrid OO/non-OO system. C++ also kept the preprocessor, which is sometimes used in what can only be described as despicable ways. And rather than risk offending legacy systems, the C++ standards committee seems very reluctant to deprecate anything—but not at all reluctant to keep adding what seems like one pedantic feature after another, at least to those of us struggling to keep up.
As a result of all this, there are often many ways to do something in C++, and figuring out which way is best can be difficult. Without guidance, it can only be learned through torturous experience. It is therefore unsurprising that there are many books about C++ best practices, such as Scott Meyers' Effective C++. But it's easy to forget their recommendations when you're immersed in coding, especially when new to the language. Of course, some developers don't even bother to read such books, being of the "If it works, it's correct—so don't touch it!" school. Having a tool that could serve as an automated Scott Meyers code inspector would go a long way to addressing these issues.
Background
When I started to develop the Robust Services Core (RSC), I had a reasonable knowledge of C++ but was far from proficient. The code grew very organically and was continually refactored. As I became more familiar with C++ and needed to revisit areas of the code that had lain dormant for a while, I kept finding things that I would now do differently. But there was always more code to develop and never enough time to do a tedious code inspection to find and "fix" all the things that could be improved.
Eventually I decided that, at the very least, it would be nice to clean up all the #include directives. Surely there was a publicly available tool for this. This was circa 2013, and the only thing I found was a Google initiative called "Include What You Use", which appeared to have been mothballed.Для просмотра ссылки Войдиили Зарегистрируйся I therefore decided to write such a tool as a diversion from the main focus of RSC.
Some diversion! It soon became apparent that fixing #include lists, to add the directives that should be there and remove those that shouldn't, meant writing a parser. And not just a parser, but something closer to a compiler, because it would also have to do name resolution and other things. Another option was to take an open-source C++ compiler and either modify it or extract the necessary information from files that it might produce.
Rather than give up, I decided to try writing the tool from scratch. It would be a learning experience, even if the attempt ultimately had to be abandoned. This article describes the current state of the code that emerged.
Using the Code
Not only does the code clean up #include directives, it serves as an automated Scott Meyers code inspector that can implement some of its recommendations by suitably editing the source code. Its main drawback is that it only supports the subset of C++11 that RSC uses. Although this is a reasonable subset of the language, what's Для просмотра ссылки Войдиили Зарегистрируйся will hamper its usefulness to projects that use unsupported language features. Adding one of these missing language features can be anywhere from moderately easy to quite challenging. Nonetheless, feel free to request that a specific language feature be supported—or even volunteer to implement it! This will make the tool useful to a wider range of projects.
Unlike previous articles that I've written, this one focuses more on how to use the code, and not much on how it works. However, it will provide a high-level overview of the design as a roadmap for those who want to dig into the code.
Walkthroughs
Defining the Library
Before the tool can be used, the files that make up the code base must be defined. This can be done right after RSC starts by entering the command >read buildlib from the CLI. That ">" is RSC's CLI prompt and is not entered, but this article uses it to denote a CLI command. A dump of all CLI commands is available in Для просмотра ссылки Войдиили ЗарегистрируйсяДля просмотра ссылки Войди или Зарегистрируйся; scroll down to somewhere around line 1246, to "ct>help full", to see those in the ct directory, which is where the tool is implemented.
What >read buildlib does is execute the script Для просмотра ссылки Войдиили Зарегистрируйся, which contains a sequence of CLI commands. This results in the execution of the following commands, which are copied from the console transcript file that RSC generates, with commands not relevant to this article removed:
The tool is in the ct directory, so the command >ct is used to access the CLI commands in that directory. The script Для просмотра ссылки Войдиили Зарегистрируйся is then read. It contains a series of >import commands that add, to the code library, all of the directories that are needed to compile the project (RSC, in this case). For example, the command
imports the code in the ct directory, which can subsequently be referred to as ctool in other CLI commands. The path to this directory is relative to the SourcePath configuration parameter. When RSC starts up, it obtains its configuration parameters from the file Для просмотра ссылки Войдиили Зарегистрируйся. So to use the tools on your own code, you need to
или Зарегистрируйся instance for its directory and a Для просмотра ссылки Войди или Зарегистрируйся instance for each code fileДля просмотра ссылки Войди или Зарегистрируйся in that directory. There are currently two restrictions:
Once all of the source code directories have been imported, the entire code library can be parsed, which is a prerequisite to checking it with the static analysis tool. This is done with the command
in which
[/SHOWTOGROUPS]
Automating Scott Meyers' recommendations and cleaning up #include directives.
This article is a user guide to a static analysis tool for C++ code. Among other things, the tool can clean up #include lists and highlight violations of C++ best practices. It can also implement some of its suggestions by editing the code. The article also provides a high-level overview of the tool's implementation.
- ...github.com/GregUtas/robust-services-core/archive/master.zip
- .../KB/cpp/5165710/master.zip
- .../KB/cpp/5246833/txt_files.zip
Introduction
C++ is a large language—too large, some would argue. Because it's a superset of C, it's easy for developers with a C background to build a hybrid OO/non-OO system. C++ also kept the preprocessor, which is sometimes used in what can only be described as despicable ways. And rather than risk offending legacy systems, the C++ standards committee seems very reluctant to deprecate anything—but not at all reluctant to keep adding what seems like one pedantic feature after another, at least to those of us struggling to keep up.
As a result of all this, there are often many ways to do something in C++, and figuring out which way is best can be difficult. Without guidance, it can only be learned through torturous experience. It is therefore unsurprising that there are many books about C++ best practices, such as Scott Meyers' Effective C++. But it's easy to forget their recommendations when you're immersed in coding, especially when new to the language. Of course, some developers don't even bother to read such books, being of the "If it works, it's correct—so don't touch it!" school. Having a tool that could serve as an automated Scott Meyers code inspector would go a long way to addressing these issues.
Background
When I started to develop the Robust Services Core (RSC), I had a reasonable knowledge of C++ but was far from proficient. The code grew very organically and was continually refactored. As I became more familiar with C++ and needed to revisit areas of the code that had lain dormant for a while, I kept finding things that I would now do differently. But there was always more code to develop and never enough time to do a tedious code inspection to find and "fix" all the things that could be improved.
Eventually I decided that, at the very least, it would be nice to clean up all the #include directives. Surely there was a publicly available tool for this. This was circa 2013, and the only thing I found was a Google initiative called "Include What You Use", which appeared to have been mothballed.Для просмотра ссылки Войди
Some diversion! It soon became apparent that fixing #include lists, to add the directives that should be there and remove those that shouldn't, meant writing a parser. And not just a parser, but something closer to a compiler, because it would also have to do name resolution and other things. Another option was to take an open-source C++ compiler and either modify it or extract the necessary information from files that it might produce.
Rather than give up, I decided to try writing the tool from scratch. It would be a learning experience, even if the attempt ultimately had to be abandoned. This article describes the current state of the code that emerged.
Using the Code
Not only does the code clean up #include directives, it serves as an automated Scott Meyers code inspector that can implement some of its recommendations by suitably editing the source code. Its main drawback is that it only supports the subset of C++11 that RSC uses. Although this is a reasonable subset of the language, what's Для просмотра ссылки Войди
Unlike previous articles that I've written, this one focuses more on how to use the code, and not much on how it works. However, it will provide a high-level overview of the design as a roadmap for those who want to dig into the code.
Walkthroughs
Defining the Library
Before the tool can be used, the files that make up the code base must be defined. This can be done right after RSC starts by entering the command >read buildlib from the CLI. That ">" is RSC's CLI prompt and is not entered, but this article uses it to denote a CLI command. A dump of all CLI commands is available in Для просмотра ссылки Войди
What >read buildlib does is execute the script Для просмотра ссылки Войди
Код:
nb>read buildlib
nb>ct
ct>read lib.create
ct>import subs "subs"
ct>import nbase "nb"
ct>import ntool "nt"
ct>import ctool "ct"
ct>import nwork "nw"
ct>import sbase "sb"
ct>import stool "st"
ct>import mbase "mb"
ct>import cbase "cb"
ct>import pbase "pb"
ct>import onode "on"
ct>import cnode "cn"
ct>import rnode "rn"
ct>import snode "sn"
ct>import anode "an"
ct>import diplo "dip"
ct>import rsc "rsc"
The tool is in the ct directory, so the command >ct is used to access the CLI commands in that directory. The script Для просмотра ссылки Войди
Код:
ct>import ctool "ct"
imports the code in the ct directory, which can subsequently be referred to as ctool in other CLI commands. The path to this directory is relative to the SourcePath configuration parameter. When RSC starts up, it obtains its configuration parameters from the file Для просмотра ссылки Войди
- Modify element.config by setting its SourcePath entry to a directory that subtends all of your project's code files.
- Create a file similar to lib.create in the same directory as RSC's lib.create. Each of the >import commands in that file must specify a directory that is relative to your new setting for SourcePath.
- Copy the Для просмотра ссылки Войди
или Зарегистрируйся directory from RSC into your own project, just below your SourcePath directory, and include the command >import subs "subs", as found in RSC's lib.create, in your version of lib.create. - Modify the buildlib script to >read your version of lib.create.
- Each file name must be unique (i.e., the same name cannot be used in more than one directory).
- All of the code files in a directory get imported (i.e., there is no way to exclude a code file).
Once all of the source code directories have been imported, the entire code library can be parsed, which is a prerequisite to checking it with the static analysis tool. This is done with the command
Код:
>parse - win32 $files
in which
- - specifies that no parser options are being used (the only options are ones that enable debug tools)
- win32 specifies that the target is 32-bit Windows (currently, the only other target is win64)
- $files is a built-in library variable that contains the set of all code files
Код:
ct>parse - win32 f ctool
cstdint
cctype
cmath
csignal
cstdio
cstdlib
direct.h
exception
functional
iosfwd
utility
typeinfo
winerror.h
atomic
cstddef
ctime
ios
io.h
iterator
cstring
windows.h
ostream
iomanip
memory
new
queue
stack
unordered_map
algorithm
dbghelp.h
istream
intsafe.h
list
map
set
timeb.h
vector
winsock2.h
string
iostream
ws2tcpip.h
bitset
fstream
sstream
FunctionGuard.h
SysDecls.h
Clock.h
Q1Link.h
Q2Link.h
SysTypes.h
Algorithms.h
Debug.h
std::bitset<unsigned int>
RegCell.h
Formatters.h
Exception.h
std::unique_ptr<std::basic_ostringstream>
Memory.h
//
// [many lines deleted]
//
Cxx.cpp
CxxRoot.cpp
std::unique_ptr<CodeTools::CxxStrLiteral<char,std::basic_string<char,std::char_traits<char>,
std::allocator<char>>,CodeTools::Cxx::Encoding::ASCII>>
std::vector<std::unique_ptr<CodeTools::CxxStrLiteral<char,std::basic_string<char,
std::char_traits<char>,std::allocator<char>>,CodeTools::Cxx::Encoding::ASCII>>>
std::move<std::unique_ptr<CodeTools::CxxStrLiteral<char,std::basic_string<char,
std::char_traits<char>,std::allocator<char>>,CodeTools::Cxx::Encoding::ASCII>>>
std::vector<std::unique_ptr<CodeTools::Macro>>
std::move<std::unique_ptr<CodeTools::Macro>>
std::unique_ptr<CodeTools::Define>
std::move<std::unique_ptr<CodeTools::Define>>
CodeTools::DisplayObjects<std::unique_ptr<CodeTools::Macro>>
std::iterator_t<const std::unique_ptr<CodeTools::Macro>>
NodeBase::Singleton<CodeTools::ParserTraceTool>
Parser.cpp
CodeTools::CxxCharLiteral<char,CodeTools::Cxx::Encoding::ASCII>
CodeTools::CxxCharLiteral<char16_t,CodeTools::Cxx::Encoding::U16>
CodeTools::CxxCharLiteral<char32_t,CodeTools::Cxx::Encoding::U32>
CodeTools::CxxCharLiteral<wchar_t,CodeTools::Cxx::Encoding::WIDE>
std::iterator_t<CodeTools::Cxx::Keyword>
std::iterator_t<const CodeTools::Cxx::Keyword>
std::unique_ptr<CodeTools::StringLiteral>
std::move<std::unique_ptr<CodeTools::StringLiteral>>
std::unique_ptr<CodeTools::Elif>
std::move<std::unique_ptr<CodeTools::Elif>>
std::unique_ptr<CodeTools::Else>
std::move<std::unique_ptr<CodeTools::Else>>
std::unique_ptr<CodeTools::Endif>
std::move<std::unique_ptr<CodeTools::Endif>>
std::unique_ptr<CodeTools::Error>
std::unique_ptr<CodeTools::Iff>
std::move<std::unique_ptr<CodeTools::Iff>>
std::unique_ptr<CodeTools::Ifdef>
std::move<std::unique_ptr<CodeTools::Ifdef>>
std::unique_ptr<CodeTools::Ifndef>
std::move<std::unique_ptr<CodeTools::Ifndef>>
std::unique_ptr<CodeTools::Line>
std::unique_ptr<CodeTools::Pragma>
std::unique_ptr<CodeTools::Undef>
std::move<std::unique_ptr<CodeTools::Undef>>
Total=225, failed=0
[/SHOWTOGROUPS]
Последнее редактирование: