A Microsoft não apenas publicou recentemente o código-fonte aberto de seus próprios projetos - outras empresas também estão seguindo essa tendência. Para nós, desenvolvedores do PVS-Studio, essa é uma ótima maneira de testar o analisador novamente, ver as coisas interessantes que ele pode encontrar e notificar os autores do projeto sobre isso. Hoje analisamos o projeto Fast Reports.
O que foi verificado?
FastReport é um gerador de relatórios desenvolvido pela
Fast Reports . Escrito em C # e compatível com o .NET Standard 2.0+. O código-fonte do projeto foi
postado recentemente
no GitHub , onde foi baixado para análise posterior.
Os relatórios suportam o uso de texto, imagens, linhas, formas, diagramas, tabelas, códigos de barras, etc. Eles podem ser de uma página e várias páginas, incluindo, além de dados, uma capa e uma contracapa. As fontes de dados podem ser XML, CSV, Json, MS SQL, MySQL, Oracle, Postgres, MongoDB, Couchbase, RavenDB, SQLite.
Existem várias maneiras de criar modelos de relatório: do código; como um arquivo xml; usando um designer on-line ou o FastReport Designer Community Edition.
Se necessário, as bibliotecas podem ser baixadas como
pacotes NuGet .
Você pode ler mais sobre os recursos do produto na
página do GitHub do projeto .

Não houve problemas com a montagem do projeto - eu o montei no Visual Studio 2017, de onde foi verificado posteriormente usando o plug-in PVS-Studio.
O PVS-Studio é um analisador estático que procura erros nos códigos Java C, C ++, C #. Ao analisar o código C #, você pode usar o analisador do Visual Studio IDE usando o plug-in PVS-Studio ou pode
verificar projetos na linha de comando , para a qual o utilitário de linha de comando PVS-Studio_Cmd.exe é usado. Se desejar, você pode
configurar a análise no servidor de compilação ou
conectar os resultados da análise ao SonarQube .
Bem, vamos ver o que foi interessante desta vez.
Como o projeto é pequeno, você não deve contar com muitos erros de digitação e locais suspeitos. Vamos dar uma olhada no que foi encontrado e até tentar reproduzir algo na prática.
Óculos e mais
Atendeu ao seguinte método:
public override string ToString() { if (_value == null) return null; return this.String; }
Aviso do PVS-Studio :
V3108 Não é recomendável retornar 'null' do método 'ToSting ()'. Variant.cs 1519
Sim, retornar
nulo de um método
ToString () substituído não é um erro em si, mas ainda é um estilo ruim. Isso é indicado, entre outras coisas, na
documentação da Microsoft :
Sua substituição ToString () não deve retornar Vazia ou uma seqüência nula . Os desenvolvedores que não esperam um retorno
nulo como o valor de retorno de
ToString () podem ficar desagradáveis quando um
ArgumentNullException é lançado durante a execução do código abaixo (desde que o método de extensão seja chamado para
IEnumerable <T> ).
Variant varObj = new Variant(); varObj.ToString().Contains(character);
Você pode encontrar falhas no fato de que o exemplo é sintético, mas a essência disso não muda.
Além disso, este código é comentado da seguinte maneira:
Opa Em vez de "" retorna
nulo .
Vamos continuar.
Há uma classe
FastString na
biblioteca . Descrição:
alternativa rápida do StringBuilder . Na verdade, essa classe contém um campo do tipo
StringBuilder . Os construtores da classe
FastString chamam o método
Init , que inicializa o campo correspondente.
O código de um dos construtores:
public FastString() { Init(initCapacity); }
E o código do método
Init :
private void Init(int iniCapacity) { sb = new StringBuilder(iniCapacity);
Se desejar, você pode acessar o campo
sb através da propriedade
StringBuilder :
public StringBuilder StringBuilder { get { return sb; } }
No total, o
FastString possui 3 construtores:
public FastString(); public FastString(int iniCapacity); public FastString(string initValue);
Eu já mostrei ao corpo do primeiro designer que eles fazem as duas suposições restantes, eu acho, também não é difícil. E agora atenção. Adivinhe qual será o código a seguir:
FastString fs = new FastString(256); Console.WriteLine(fs.StringBuilder.Capacity);
Atenção, a resposta:

Inesperadamente? Vejamos o corpo do construtor correspondente:
public FastString(int iniCapacity) { Init(initCapacity); }
Os leitores regulares de nossos artigos já devem estar atentos para encontrar um problema aqui. O analisador ocular (perfume, lógica, chame como você deseja) está definitivamente entorpecido e ele encontrou um problema: O parâmetro do construtor V3117 'iniCapacity' não é usado. FastString.cs 434
Que coincidência o código da classe conter um campo constante
initCapacity , que é passado como argumento para o método
Init em vez do parâmetro construtor
iniCapacity ...
private const int initCapacity = 32;
Ao usar nomes semelhantes, você precisa ter muito, muito cuidado. Sempre que foram encontrados erros relacionados ao uso de nomes semelhantes - projetos em C, C ++, C #, Java - havia erros desse tipo em todos os lugares ...
A propósito, sobre erros de digitação.
Vamos fazer o seguinte exemplo simples e ver como ele funciona:
static void Main(string[] args) { TextObject textObj = new TextObject(); textObj.ParagraphFormat = null; Console.WriteLine("Ok"); }
Como você deve ter adivinhado, a saída será diferente da sequência "Ok" :)
Qual? Então, por exemplo:

O problema está na propriedade
ParagraphFormat e no uso de nomes semelhantes:
public ParagraphFormat ParagraphFormat { get { return paragraphFormat; } set { ParagraphFormat = value; } }
PVS-Studio Warning :
V3110 Possível recursão infinita dentro da propriedade 'ParagraphFormat'. TextObject.cs 281
A propriedade
ParagraphFormat é um wrapper sobre o campo
paragraphFormat . Além disso, seu acessador de propriedade get está escrito corretamente, mas o acessador de propriedade set contém um erro de digitação irritante: em vez de um campo, o registro ocorre na mesma propriedade, o que causa recursão. Mais uma vez, um erro relacionado a nomes semelhantes.
Considere o seguinte snippet de código.
public override Run Split(float availableWidth, out Run secondPart) { .... if (r.Width > availableWidth) { List<CharWithIndex> list = new List<CharWithIndex>(); for (int i = point; i < size; i++) list.Add(chars[i]); secondPart = new RunText(renderer, word, style, list, left + r.Width, charIndex); list.Clear(); for (int i = 0; i < point; i++) list.Add(chars[i]); r = new RunText(renderer, word, style, list, left, charIndex); return r; } else { List<CharWithIndex> list = new List<CharWithIndex>(); for (int i = point; i < size; i++) list.Add(chars[i]); secondPart = new RunText(renderer, word, style, list, left + r.Width, charIndex); list.Clear(); for (int i = 0; i < point; i++) list.Add(chars[i]); r = new RunText(renderer, word, style, list, left, charIndex); return r; } .... }
PVS-Studio Warning :
V3004 A
instrução 'then' é equivalente à instrução 'else'. HtmlTextRenderer.cs 2092
Um pouco de copiar e colar e agora, independentemente do valor da expressão
r.Width> availableWidth , as mesmas ações serão executadas. Remova a
instrução if ou altere a lógica em uma das ramificações.
public static string GetExpression(FindTextArgs args, bool skipStrings) { while (args.StartIndex < args.Text.Length) { if (!FindMatchingBrackets(args, skipStrings)) break; return args.FoundText; } return ""; }
Aviso do analisador :
V3020 Um 'retorno' incondicional dentro de um loop. CodeUtils.cs 262
Devido à
declaração de retorno incondicional
, não será executada mais de uma iteração para o loop acima. Talvez esse código tenha sido lançado após a refatoração, ou talvez seja apenas uma maneira incomum de fazer o que poderia ser feito sem um loop.
private int FindBarItem(string c) { for (int i = 0; i < tabelle_cb.Length; i++) { if (c == tabelle_cb[i].c) return i; } return -1; } internal override string GetPattern() { string result = tabelle_cb[FindBarItem("A")].data + "0"; foreach (char c in text) { int idx = FindBarItem(c.ToString()); result += tabelle_cb[idx].data + "0"; } result += tabelle_cb[FindBarItem("B")].data; return result; }
Aviso PVS-Studio :
V3106 Possível valor negativo do índice. O valor do índice 'idx' pode chegar a -1. BarcodeCodabar.cs 70
Código potencialmente perigoso. O método
FindBarItem pode retornar
-1 se não encontrar o elemento passado como parâmetro. No código de chamada (método
GetPattern ), esse valor é gravado na variável
idx e é usado como o índice da matriz
tabelle_cb sem verificação preliminar. Ao acessar o índice
-1, uma exceção do tipo
IndexOutOfRangeException será
lançada .
Vamos continuar.
protected override void Finish() { .... if (saveStreams) { FinishSaveStreams(); } else { if (singlePage) { if (saveStreams) { int fileIndex = GeneratedFiles.IndexOf(singlePageFileName); DoPageEnd(generatedStreams[fileIndex]); } else { .... } .... } .... } .... }
PVS-Studio Warning :
A expressão V3022 'saveStreams' é sempre falsa. HTMLExport.cs 849
O código acima com a obtenção do valor
fileIndex e a chamada do método
DoPageEnd nunca será executado, pois o resultado da segunda expressão
saveStreams no código sempre será
falso .
O mais interessante, talvez seja tudo (você não esperava um artigo no espírito
da análise Mono ?). Havia outros avisos do analisador, mas eles não pareciam interessantes o suficiente para incluí-los no artigo (parte sempre permanece nos bastidores).
Conhecer o design seria útil para a análise deles; portanto, idealmente, os autores devem observar esses avisos por conta própria. Esses são avisos como
V3083 (uma chamada potencialmente perigosa para manipuladores de eventos),
V3022 (a condição é sempre verdadeira / falsa (nesse caso, geralmente devido a métodos que retornam um único valor)),
V3072 ,
V3073 (trabalhando com
IDisposable ) e outros.
Se nada disso for irrelevante, você pode:
Conclusão

Apesar do artigo ser curto, tive o prazer de 'tocar' as advertências do analisador com as mãos - para ver como o que o analisador jura que se manifesta na prática.
Desejo aos autores do projeto sucesso, correção dos problemas descobertos e desejo elogiar um passo em direção à comunidade de código aberto!
Aconselho o resto a experimentar o analisador no seu código e ver o que você pode achar interessante.
Tudo de bom!

Se você deseja compartilhar este artigo com um público que fala inglês, use o link para a tradução: Sergey Vasiliev.
Os relatórios mais rápidos do oeste selvagem - e um punhado de bugs ...