Os relatórios mais rápidos no oeste selvagem. E um punhado de bugs além disso ...

Quadro 3

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 .

Quadro 8


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:

 /// <summary> /// Returns <see cref="String"/> property unless the value on the right /// is null. If the value on the right is null, returns "". /// </summary> /// <returns></returns> 

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); //chars = new char[iniCapacity]; //capacity = 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:

Quadro 2


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:

Quadro 1


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


Quadro 4


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

Source: https://habr.com/ru/post/pt431370/


All Articles