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 ...