
Por um acaso do destino, verificamos a maioria das bibliotecas da coleção "Awesome hpp". Esses são pequenos projetos C ++ que consistem apenas em arquivos de cabeçalho. Esperançosamente, os bugs encontrados ajudarão a tornar essas bibliotecas um pouco melhores. Também ficaremos felizes se seus autores começarem a usar o analisador PVS-Studio gratuitamente em uma base regular.
Chamo a sua atenção para uma visão geral dos resultados da verificação de várias bibliotecas listadas na lista awesome-hpp (uma lista com curadoria de bibliotecas C ++ incríveis apenas de cabeçalho).
Fiquei sabendo dessa lista pela primeira vez no podcast " Cross Platform Mobile Telephony ". Aproveitando esta oportunidade, recomendo a todos os programadores C ++ que se familiarizem com o CppCast . CppCast é o primeiro podcast para desenvolvedores C ++ por desenvolvedores C ++!
Apesar do grande número de projetos na lista, ocorreram poucos erros. Existem três razões para isso:
- Esses são projetos muito pequenos. Muitos consistem literalmente em um único arquivo de cabeçalho.
- Não verificamos todos os projetos. Houve problemas com a compilação de alguns deles e decidimos ignorá-los.
- Freqüentemente, para entender se há erros nas classes / funções do template ou não, eles devem ser instanciados. Dessa forma, muitos erros podem ser detectados pelo analisador apenas neste projeto, quando a biblioteca é ativamente utilizada. Apenas incluímos os arquivos de cabeçalho em um arquivo .cpp vazio e verificamos, o que torna a verificação ineficaz.
No entanto, no processo de estudo das advertências, houve número suficiente deles para escrever este artigo e alguns adicionais.
Nota para meus colegas
Uma nota para desenvolvedores de bibliotecas. Os interessados podem usar gratuitamente o analisador PVS-Studio para verificar projetos de código aberto. Para obter uma licença para seu projeto de código aberto, preencha este formulário .
Agora vamos finalmente dar uma olhada no que foi encontrado em algumas das bibliotecas.
Erros encontrados
Melhor biblioteca
Breve descrição da biblioteca iutest :
iutest é um framework para escrever testes C ++.
template<typename Event>
pool_handler<Event> & assure() {
....
return static_cast<pool_handler<Event> &>(it == pools.cend() ?
*pools.emplace_back(new pool_handler<Event>{}) : **it);
....
}
Aviso PVS-Studio: V1023 Um ponteiro sem proprietário é adicionado ao contêiner 'pools' pelo método 'emplace_back'. Ocorrerá um vazamento de memória no caso de uma exceção. entt.hpp 17114
Este código tem o potencial de vazar memória. Se o contêiner precisar de realocação e não puder alocar memória para um novo array, ele lançará uma exceção e o ponteiro será perdido.
Talvez, para testes, essa situação seja improvável e não crítica. No entanto, decidi mencionar esta lacuna para fins educacionais :).
Opção correta:
pools.emplace_back(std::make_unique<pool_handler<Event>>{})
Outro local semelhante: V1023 Um ponteiro sem proprietário é adicionado ao contêiner 'pools' pelo método 'emplace_back'. Ocorrerá um vazamento de memória no caso de uma exceção. entt.hpp 17407
Biblioteca Jsoncons
Uma breve descrição da biblioteca jsoncons :
Uma biblioteca C ++, apenas de cabeçalho, para construir formatos de dados JSON e JSON, com JSON Pointer, JSON Patch, JSONPath, JMESPath, CSV, MessagePack, CBOR, BSON, UBJSON.O primeiro erro
static constexpr uint64_t basic_type_bits = sizeof(uint64_t) * 8;
uint64_t* data()
{
return is_dynamic() ? dynamic_stor_.data_ : short_stor_.values_;
}
basic_bigint& operator<<=( uint64_t k )
{
size_type q = (size_type)(k / basic_type_bits);
....
if ( k ) // 0 < k < basic_type_bits:
{
uint64_t k1 = basic_type_bits - k;
uint64_t mask = (1 << k) - 1; // <=
....
data()[i] |= (data()[i-1] >> k1) & mask;
....
}
reduce();
return *this;
}
Aviso do PVS-Studio: V629 Considere inspecionar a expressão '1 << k'. Mudança de bit do valor de 32 bits com uma expansão subsequente para o tipo de 64 bits. bigint.hpp 744
Esse erro já foi discutido em detalhes no artigo " Por que é importante realizar uma análise estática das bibliotecas de código aberto que você adiciona ao seu projeto ". Em suma, para obter os valores de máscara corretos, você precisa escrever assim:
uint64_t mask = (static_cast<uint64_t>(1) << k) - 1;
Ou assim:
uint64_t mask = (1ull << k) - 1;
Exatamente o mesmo erro que o primeiro pode ser visto aqui: V629 Considere inspecionar a expressão '1 << k'. Mudança de bit do valor de 32 bits com uma expansão subsequente para o tipo de 64 bits. bigint.hpp 779
Segundo erro
template <class CharT = typename std::iterator_traits<Iterator>::value_type>
typename std::enable_if<sizeof(CharT) == sizeof(uint16_t)>::type
next() UNICONS_NOEXCEPT
{
begin_ += length_;
if (begin_ != last_)
{
if (begin_ != last_)
{
....
}
Aviso PVS-Studio: Verificação recorrente V571 . A condição 'if (begin_! = Last_)' já foi verificada na linha 1138. unicode_traits.hpp 1140
Strange retest . Suspeita-se de que haja algum erro de digitação aqui e a segunda condição deve ser diferente.
Biblioteca Clipp
Uma breve descrição da biblioteca clipp :
clipp - interfaces de linha de comando para C ++ moderno. Fácil de usar, poderoso e expressivo manipulação de argumentos de linha de comando para C ++ 11/14/17 contido em um único arquivo de cabeçalho.
inline bool
fwd_to_unsigned_int(const char*& s)
{
if(!s) return false;
for(; std::isspace(*s); ++s);
if(!s[0] || s[0] == '-') return false;
if(s[0] == '-') return false;
return true;
}
Aviso do PVS-Studio: V547 Expression 's [0] ==' - '' é sempre falso. clipp.h 303
Bem, na verdade, este não é um erro, mas simplesmente um código redundante. A verificação negativa é executada duas vezes.
Biblioteca SimpleIni
Uma breve descrição da biblioteca SimpleIni :
Uma biblioteca de plataforma cruzada que fornece uma API simples para ler e gravar arquivos de configuração no estilo INI. Suporta arquivos de dados em ASCII, MBCS e Unicode.
#if defined(SI_NO_MBSTOWCS_NULL) || (!defined(_MSC_VER) && !defined(_linux))
Aviso PVS-Studio: V1040 Possível erro de digitação na grafia de um nome de macro predefinido. A macro '_linux' é semelhante a '__linux'. SimpleIni.h 2923
Provavelmente, o nome da macro _linux está sem um sublinhado e o nome __linux deve ser usado . No entanto, no POSIX esta macro está obsoleta e é melhor usar __linux__ .
Biblioteca CSV Parser
Breve descrição da biblioteca CSV Parser :
Uma biblioteca C ++ moderna para ler, escrever e analisar arquivos CSV (e semelhantes).
CSV_INLINE void CSVReader::read_csv(const size_t& bytes) {
const size_t BUFFER_UPPER_LIMIT = std::min(bytes, (size_t)1000000);
std::unique_ptr<char[]> buffer(new char[BUFFER_UPPER_LIMIT]);
auto * HEDLEY_RESTRICT line_buffer = buffer.get();
line_buffer[0] = '\0';
....
this->feed_state->feed_buffer.push_back(
std::make_pair<>(std::move(buffer), line_buffer - buffer.get())); // <=
....
}
Aviso de PVS-Studio: V769 O ponteiro 'buffer.get ()' na expressão 'line_buffer - buffer.get ()' é igual a nullptr. O valor resultante não faz sentido e não deve ser usado. csv.hpp 4957
Uma situação interessante que requer consideração cuidadosa. Portanto, decidi que escreveria uma pequena nota separada sobre isso. Além disso, ao experimentar um código semelhante, encontrei uma falha no próprio PVS-Studio :). Em alguns casos, é silencioso, embora deva emitir avisos.
Em suma, se esse código funciona ou não depende da ordem em que os argumentos são avaliados. A ordem em que os argumentos são avaliados depende do compilador.
Biblioteca PPrint
Breve descrição da biblioteca PPRINT :.
Impressora bonita para C ++ moderno.
template <typename Container>
typename std::enable_if<......>::type print_internal(......) {
....
for (size_t i = 1; i < value.size() - 1; i++) {
print_internal(value[i], indent + indent_, "", level + 1);
if (is_container<T>::value == false)
print_internal_without_quotes(", ", 0, "\n");
else
print_internal_without_quotes(", ", 0, "\n");
}
....
}
Aviso PVS-Studio: V523 A instrução 'then' é equivalente à instrução 'else'. pprint.hpp 715
É muito estranho que, independentemente da condição, a mesma ação seja executada. Também não há nenhum comentário explicativo especial. Isso tudo é muito semelhante ao erro Copiar-Colar.
Avisos semelhantes:
- V523 A instrução 'then' é equivalente à instrução 'else'. pprint.hpp 780
- V523 A instrução 'then' é equivalente à instrução 'else'. pprint.hpp 851
- V523 A instrução 'then' é equivalente à instrução 'else'. pprint.hpp 927
- V523 A instrução 'then' é equivalente à instrução 'else'. pprint.hpp 1012
Biblioteca Strf
Uma breve descrição da biblioteca Strf :
Uma biblioteca de formatação C ++ rápida que oferece suporte à conversão de codificação.O primeiro erro
template <int Base>
class numpunct: private strf::digits_grouping
{
....
constexpr STRF_HD numpunct& operator=(const numpunct& other) noexcept
{
strf::digits_grouping::operator=(other);
decimal_point_ = other.decimal_point_;
thousands_sep_ = other.thousands_sep_;
}
....
};
Aviso do PVS-Studio: V591 A função não vazia deve retornar um valor. numpunct.hpp 402 Esquecemos
de escrever "return * this;" no final da função.
O segundo erro semelhante
template <int Base>
class no_grouping final
{
constexpr STRF_HD no_grouping& operator=(const no_grouping& other) noexcept
{
decimal_point_ = other.decimal_point_;
}
....
}
Aviso do PVS-Studio: V591 A função não vazia deve retornar um valor. numpunct.hpp 528.
Biblioteca de indicadores
Breve descrição da biblioteca de Indicadores :
Indicadores de atividade para C ++ moderno.
static inline void move_up(int lines) { move(0, -lines); }
static inline void move_down(int lines) { move(0, -lines); } // <=
static inline void move_right(int cols) { move(cols, 0); }
static inline void move_left(int cols) { move(-cols, 0); }
Aviso do PVS-Studio: V524 É estranho que o corpo da função 'move_down' seja totalmente equivalente ao corpo da função 'move_up'. indicadores.hpp 983
Não tenho certeza se isso é um bug. Mas o código é muito suspeito. É muito provável que a função move_up tenha sido copiada e seu nome alterado para move_down . Mas eles se esqueceram de remover o sinal de menos. Vale a pena conferir este código.
Nota. Se o código estiver correto, você precisa entender que ele engana não apenas os analisadores de código, mas também os programadores de terceiros que desejam usar ou desenvolver esse código. É útil adicionar comentários a este código.
Biblioteca Manif
Uma breve descrição da biblioteca manif :
manif é uma biblioteca de teoria de Lie em C ++ 11 apenas com cabeçalho para estimativa de estado voltada para aplicações de robótica.
template <typename _Derived>
typename LieGroupBase<_Derived>::Scalar*
LieGroupBase<_Derived>::data()
{
return derived().coeffs().data();
}
template <typename _Derived>
const typename LieGroupBase<_Derived>::Scalar*
LieGroupBase<_Derived>::data() const
{
derived().coeffs().data(); // <=
}
Aviso do PVS-Studio: V591 A função não vazia deve retornar um valor. lie_group_base.h 347 A
função não constante foi implementada corretamente, mas a função constante não. É até interessante como aconteceu ...
Biblioteca FakeIt
Uma breve descrição da biblioteca FakeIt :
FakeIt é uma estrutura de simulação simples para C ++. Suporta GCC, Clang e MS Visual C ++. FakeIt é escrito em C ++ 11 e pode ser usado para testar projetos C ++ 11 e C ++.
template<typename ... arglist>
struct ArgumentsMatcherInvocationMatcher :
public ActualInvocation<arglist...>::Matcher {
....
template<typename A>
void operator()(int index, A &actualArg) {
TypedMatcher<typename naked_type<A>::type> *matcher =
dynamic_cast<TypedMatcher<typename naked_type<A>::type> *>(
_matchers[index]);
if (_matching)
_matching = matcher->matches(actualArg);
}
....
const std::vector<Destructible *> _matchers;
};
Aviso PVS-Studio: V522 Pode haver desreferenciamento de um ponteiro nulo potencial 'matcher'. fakeit.hpp 6720
O ponteiro de correspondência é inicializado com o valor retornado pelo operador dynamic_cast . E esse operador pode retornar nullptr, e este é um cenário muito provável. Caso contrário, é mais eficiente usar static_cast em vez de dynamic_cast . Existe a suspeita de que haja um erro de digitação na condição e, de fato, deve ser escrito:
if (matcher)
_matching = matcher->matches(actualArg);
Biblioteca GuiLite
Uma breve descrição da biblioteca GuiLite :
A menor biblioteca GUI apenas de cabeçalho (4 KLOC) para todas as plataformas.
#define CORRECT(x, high_limit, low_limit) {\
x = (x > high_limit) ? high_limit : x;\
x = (x < low_limit) ? low_limit : x;\
}while(0)
void refresh_wave(unsigned char frame)
{
....
CORRECT(y_min, m_wave_bottom, m_wave_top);
....
}
Aviso PVS-Studio: V529 Odd ponto e vírgula ';' após o operador 'while'. GuiLite.h 3413
Um erro em uma macro não leva a nenhum problema. Mas ainda é um erro, então decidi descrevê-lo no artigo.
Foi planejado para usar o padrão clássico do {...} enquanto (....) na macro . Isso permite que você execute várias ações em um bloco e, ao mesmo tempo, escreva um ponto-e-vírgula ';' após a macro para fins de beleza, como se fosse uma chamada de função.
Mas no macro considerado, eles acidentalmente esqueceu de escrever o fazer palavra-chave . Como resultado, a macro foi dividida em duas partes. O primeiro é o bloco. O segundo é um loop vazio e não em execução: while (0); ...
E qual é o problema?
Por exemplo, tal macro não pode ser usada em uma construção como:
if (A)
CORRECT(y_min, m_wave_bottom, m_wave_top);
else
Foo();
Este código não será compilado, pois será expandido para:
if (A)
{ ..... }
while(0);
else
Foo();
Concordo, é melhor encontrar e corrigir esse problema no estágio de desenvolvimento da biblioteca, e não no estágio de uso. Aplique a análise de código estático :).

Biblioteca PpluX
Breve descrição da biblioteca PpluX :
Bibliotecas C ++ de cabeçalho único para agendamento de threads, renderização e assim por diante ...
struct DisplayList {
DisplayList& operator=(DisplayList &&d) {
data_ = d.data_;
d.data_ = nullptr;
}
....
}
Aviso do PVS-Studio: V591 A função não vazia deve retornar um valor. px_render.h 398
Biblioteca Universal
Uma breve descrição da biblioteca universal:
O objetivo do Universal Numbers, ou unums, é substituir o ponto flutuante IEEE por um sistema numérico que seja mais eficiente e matematicamente consistente em ambientes de execução simultânea.O primeiro erro
template<typename Scalar>
vector<Scalar> operator*(double scalar, const vector<Scalar>& v) {
vector<Scalar> scaledVector(v);
scaledVector *= scalar;
return v;
}
Aviso do PVS-Studio: V1001 A variável 'scaledVector' é atribuída, mas não é usada no final da função. vector.hpp 124 Typo
. Em vez do vetor original v, a função precisa retornar um novo vetor, scaledVector .
Um erro de digitação semelhante pode ser visto aqui: V1001 A variável 'normalizedVector' é atribuída, mas não é usada no final da função. vector.hpp 131
Segundo erro
template<typename Scalar>
class matrix {
....
matrix& diagonal() {
}
....
};
Aviso PVS-Studio: V591 função não vazia deve retornar um valor. matrix.hpp 109
Terceiro erro
template<size_t fbits, size_t abits>
void module_subtract_BROKEN(
const value<fbits>& lhs, const value<fbits>& rhs, value<abits + 1>& result)
{
if (lhs.isinf() || rhs.isinf()) {
result.setinf();
return;
}
int lhs_scale = lhs.scale(),
rhs_scale = rhs.scale(),
scale_of_result = std::max(lhs_scale, rhs_scale);
// align the fractions
bitblock<abits> r1 =
lhs.template nshift<abits>(lhs_scale - scale_of_result + 3);
bitblock<abits> r2 =
rhs.template nshift<abits>(rhs_scale - scale_of_result + 3);
bool r1_sign = lhs.sign(), r2_sign = rhs.sign();
//bool signs_are_equal = r1_sign == r2_sign;
if (r1_sign) r1 = twos_complement(r1);
if (r1_sign) r2 = twos_complement(r2); // <=
....
}
Aviso do PVS-Studio: V581 As expressões condicionais das instruções 'if' situadas lado a lado são idênticas. Verifique as linhas: 789, 790. value.hpp 790
Um erro clássico causado por Copy-Paste. Pegamos e multiplicamos a linha:
if (r1_sign) r1 = twos_complement(r1);
Alteramos r1 para r2 nele :
if (r1_sign) r2 = twos_complement(r2);
E eles se esqueceram de alterar o r1_sign . Opção correta:
if (r2_sign) r2 = twos_complement(r2);
Bibliotecas Chobo de cabeçalho único
Uma breve descrição das Bibliotecas de cabeçalho único Chobo :
Uma coleção de pequenas bibliotecas C ++ 11 de cabeçalho único da Chobolabs.O primeiro erro
template <typename T, typename U, typename Alloc = std::allocator<T>>
class vector_view
{
....
vector_view& operator=(vector_view&& other)
{
m_vector = std::move(other.m_vector);
}
....
}
Aviso do PVS-Studio: V591 A função não vazia deve retornar um valor. vector_view.hpp 163
Segundo erro
template <typename UAlloc>
vector_view& operator=(const std::vector<U, UAlloc>& other)
{
size_type n = other.size();
resize(n);
for (size_type i = 0; i < n; ++i)
{
this->at(i) = other[i];
}
}
Aviso do PVS-Studio: V591 A função não vazia deve retornar um valor. vector_view.hpp 184
Índice PGM da biblioteca
Breve descrição da biblioteca de índice PGM :
O índice de modelo geométrico por partes (índice PGM) é uma estrutura de dados que permite pesquisa rápida, predecessor, pesquisas de intervalo e atualizações em matrizes de bilhões de itens usando ordens de magnitude menos espaço do que os índices tradicionais, enquanto fornece as mesmas garantias de tempo de consulta no pior caso ...O primeiro erro
char* str_from_errno()
{
#ifdef MSVC_COMPILER
#pragma warning(disable:4996)
return strerror(errno);
#pragma warning(default:4996)
#else
return strerror(errno);
#endif
}
Aviso PVS-Studio: V665 Possivelmente, o uso de 'aviso #pragma (padrão: X)' está incorreto neste contexto. O '#pragma warning (push / pop)' deve ser usado em seu lugar. Verifique as linhas: 9170, 9172. sdsl.hpp 9172 Aviso do
compilador de desativação temporária incorreto. Essas imprecisões são de alguma forma perdoáveis para o código do usuário. Mas isso definitivamente não é permitido em bibliotecas apenas de cabeçalho.
Segundo erro
template<class t_int_vec>
t_int_vec rnd_positions(uint8_t log_s, uint64_t& mask,
uint64_t mod=0, uint64_t seed=17)
{
mask = (1<<log_s)-1; // <=
t_int_vec rands(1<<log_s ,0);
set_random_bits(rands, seed);
if (mod > 0) {
util::mod(rands, mod);
}
return rands;
}
Aviso do PVS-Studio: V629 Considere inspecionar a expressão '1 << log_s'. Mudança de bit do valor de 32 bits com uma expansão subsequente para o tipo de 64 bits. sdsl.hpp 1350
Uma das opções corretas:
mask = ((uint64_t)(1)<<log_s)-1;
Biblioteca Hnswlib
Uma breve descrição da biblioteca Hnswlib :
Implementação de C ++ HNSW somente de cabeçalho com ligações python. Código de papel para o experimento HNSW 200M SIFT.
template<typename dist_t>
class BruteforceSearch : public AlgorithmInterface<dist_t> {
public:
BruteforceSearch(SpaceInterface <dist_t> *s, size_t maxElements) {
maxelements_ = maxElements;
data_size_ = s->get_data_size();
fstdistfunc_ = s->get_dist_func();
dist_func_param_ = s->get_dist_func_param();
size_per_element_ = data_size_ + sizeof(labeltype);
data_ = (char *) malloc(maxElements * size_per_element_);
if (data_ == nullptr)
std::runtime_error(
"Not enough memory: BruteforceSearch failed to allocate data");
cur_element_count = 0;
}
....
}
Aviso PVS-Studio: V596 O objeto foi criado, mas não está sendo usado. A palavra-chave 'throw' pode estar faltando: throw runtime_error (FOO); bruteforce.h 26
Esqueceu-se de escrever uma instrução throw antes de std :: runtime_error . Outro erro: V596 O objeto foi criado, mas não está sendo usado. A palavra-chave 'throw' pode estar faltando: throw runtime_error (FOO); bruteforce.h 161
A biblioteca tiny-dnn
Uma breve descrição da biblioteca tiny-dnn :
tiny-dnn é uma implementação C ++ 14 de aprendizado profundo. É adequado para aprendizado profundo em recursos computacionais limitados, sistemas embarcados e dispositivos IoT.O primeiro erro
class nn_error : public std::exception {
public:
explicit nn_error(const std::string &msg) : msg_(msg) {}
const char *what() const throw() override { return msg_.c_str(); }
private:
std::string msg_;
};
inline Device::Device(device_t type, const int platform_id, const int device_id)
: type_(type),
has_clcuda_api_(true),
platform_id_(platform_id),
device_id_(device_id) {
....
#else
nn_error("TinyDNN has not been compiled with OpenCL or CUDA support.");
#endif
}
Aviso PVS-Studio: V596 O objeto foi criado, mas não está sendo usado. A palavra-chave 'throw' pode estar faltando: throw nn_error (FOO); device.h 68
nn_error não é uma função que lança uma exceção, mas apenas uma classe. Portanto, é correto usá-lo assim:
throw nn_error("TinyDNN has not been compiled with OpenCL or CUDA support.");
Outro uso indevido desta classe: V596 O objeto foi criado, mas não está sendo usado. A palavra-chave 'throw' pode estar faltando: throw nn_error (FOO); conv2d_op_opencl.h 136
Segundo erro
inline std::string format_str(const char *fmt, ...) {
static char buf[2048];
#ifdef _MSC_VER
#pragma warning(disable : 4996)
#endif
va_list args;
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
#ifdef _MSC_VER
#pragma warning(default : 4996)
#endif
return std::string(buf);
}
Aviso PVS-Studio: V665 Possivelmente, o uso de 'aviso #pragma (padrão: X)' está incorreto neste contexto. O '#pragma warning (push / pop)' deve ser usado em seu lugar. Verifique as linhas: 139, 146. util.h 146
Biblioteca Dlib
Uma breve descrição da biblioteca Dlib :
Dlib é um kit de ferramentas C ++ moderno contendo algoritmos de aprendizado de máquina e ferramentas para criar software complexo em C ++ para resolver problemas do mundo real.Primeiro erro
Por diversão, tente encontrar esse erro você mesmo.
class bdf_parser
{
public:
enum bdf_enums
{
NO_KEYWORD = 0,
STARTFONT = 1,
FONTBOUNDINGBOX = 2,
DWIDTH = 4,
DEFAULT_CHAR = 8,
CHARS = 16,
STARTCHAR = 32,
ENCODING = 64,
BBX = 128,
BITMAP = 256,
ENDCHAR = 512,
ENDFONT = 1024
};
....
bool parse_header( header_info& info )
{
....
while ( 1 )
{
res = find_keywords( find | stop );
if ( res & FONTBOUNDINGBOX )
{
in_ >> info.FBBx >> info.FBBy >> info.Xoff >> info.Yoff;
if ( in_.fail() )
return false; // parse_error
find &= ~FONTBOUNDINGBOX;
continue;
}
if ( res & DWIDTH )
{
in_ >> info.dwx0 >> info.dwy0;
if ( in_.fail() )
return false; // parse_error
find &= ~DWIDTH;
info.has_global_dw = true;
continue;
}
if ( res & DEFAULT_CHAR )
{
in_ >> info.default_char;
if ( in_.fail() )
return false; // parse_error
find &= ~DEFAULT_CHAR;
continue;
}
if ( res & NO_KEYWORD )
return false; // parse_error: unexpected EOF
break;
}
....
};
Encontrei?

Ela está aqui:
if ( res & NO_KEYWORD )
Aviso do PVS-Studio: V616 A constante nomeada 'NO_KEYWORD' com o valor 0 é usada na operação bit a bit. fonts.cpp 288 A
constante nomeada NO_KEYWORD tem o valor 0. Portanto, a condição não tem sentido. Seria correto escrever:
if ( res == NO_KEYWORD )
Outra verificação incorreta é encontrada aqui: V616 A constante nomeada 'NO_KEYWORD' com o valor 0 é usada na operação bit a bit. fonts.cpp 334
Segundo erro
void set(std::vector<tensor*> items)
{
....
epa.emplace_back(new enable_peer_access(*g[0], *g[i]));
....
}
Aviso PVS-Studio: V1023 Um ponteiro sem proprietário é adicionado ao container 'epa' pelo método 'emplace_back'. Ocorrerá um vazamento de memória no caso de uma exceção. tensor_tools.h 1665
Para entender onde está o problema, proponho-me familiarizar-se com a documentação para o diagnóstico V1023 .
Terceiro erro
template <
typename detection_type,
typename label_type
>
bool is_track_association_problem (
const std::vector<
std::vector<labeled_detection<detection_type,label_type> > >& samples
)
{
if (samples.size() == 0)
return false;
unsigned long num_nonzero_elements = 0;
for (unsigned long i = 0; i < samples.size(); ++i)
{
if (samples.size() > 0)
++num_nonzero_elements;
}
if (num_nonzero_elements < 2)
return false;
....
}
Aviso do PVS-Studio: A expressão V547 'samples.size ()> 0' é sempre verdadeira. svm.h 360
Este é um código muito, muito estranho! Se um loop começar, a condição (samples.size ()> 0) é sempre verdadeira. Portanto, o loop pode ser simplificado:
for (unsigned long i = 0; i < samples.size(); ++i)
{
++num_nonzero_elements;
}
Depois disso, fica claro que o loop não é necessário. Pode ser escrito de forma muito mais fácil e eficiente:
unsigned long num_nonzero_elements = samples.size();
Mas isso foi planejado para ser feito? O código claramente merece um estudo cuidadoso por um programador.
O quarto erro
class console_progress_indicator
{
....
double seen_first_val;
....
};
bool console_progress_indicator::print_status (
double cur, bool always_print)
{
....
if (!seen_first_val)
{
start_time = cur_time;
last_time = cur_time;
first_val = cur;
seen_first_val = true; // <=
return false;
}
....
}
Aviso de PVS-Studio: V601 O tipo bool é implicitamente convertido para o tipo duplo. console_progress_indicator.h 136
O valor true é gravado em um membro da classe do tipo double . Hmm ... Quinto erro
void file::init(const std::string& name)
{
....
WIN32_FIND_DATAA data;
HANDLE ffind = FindFirstFileA(state.full_name.c_str(), &data);
if (ffind == INVALID_HANDLE_VALUE ||
(data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) != 0)
{
throw file_not_found("Unable to find file " + name);
}
else
{
....
}
}
Aviso PVS-Studio: V773 A exceção foi lançada sem fechar o arquivo referenciado pelo identificador 'ffind'. Um vazamento de recursos é possível. dir_nav_kernel_1.cpp 60
Se um diretório for encontrado, uma exceção é lançada. Mas quem vai fechar a maçaneta?
O sexto erro
Outro lugar muito estranho.
inline double poly_min_extrap(double f0, double d0,
double x1, double f_x1,
double x2, double f_x2)
{
....
matrix<double,2,2> m;
matrix<double,2,1> v;
const double aa2 = x2*x2;
const double aa1 = x1*x1;
m = aa2, -aa1,
-aa2*x2, aa1*x1;
v = f_x1 - f0 - d0*x1,
f_x2 - f0 - d0*x2;
....
}
Aviso do PVS-Studio: V521 Essas expressões que usam o operador ',' são perigosas. Certifique-se de que a expressão está correta. optimization_line_search.h 211
Programado para inicializar matrizes. Mas todos esses aa2 , f_x1 , d0 e assim por diante são apenas variáveis do tipo duplo . Isso significa que as vírgulas não separam os argumentos destinados a criar matrizes, mas são operadores de vírgula comuns que retornam o valor do lado direito.
Conclusão
No início do artigo, dei um exemplo de como você pode combinar várias coisas úteis ao mesmo tempo. Usar um analisador estático também é útil ao mesmo tempo por vários motivos:
- Treinamento. Ao estudar os avisos do analisador, você pode aprender muitas coisas novas e úteis. Exemplos: memset , #pragma warning , emplace_back , estritamente alinhado .
- Detecção precoce de erros de digitação, bugs e vulnerabilidades potenciais.
- O código está gradualmente se tornando melhor, mais simples e mais compreensível.
- Você pode se orgulhar e dizer a todos que está usando tecnologias modernas no desenvolvimento de projetos :). E isso é apenas parcialmente humor. Esta é uma vantagem competitiva real.
A única questão é como começar, como implementá-lo sem dor e como usá-lo corretamente. Os seguintes artigos irão ajudá-lo com isso:
- Como visualizar rapidamente avisos interessantes gerados pelo analisador PVS-Studio para código C e C ++?
- Como implementar um analisador de código estático em um projeto legado e não desmotivar a equipe .
- Introduza a análise estática em seu processo, em vez de procurar bugs com ela .

Se você deseja compartilhar este artigo com um público que fala inglês, por favor, use o link de tradução: Andrey Karpov. Verificando uma coleção de biblioteca C ++ somente cabeçalho (awesome-hpp) .