Los informes más rápidos en el salvaje oeste. Y un puñado de errores además ...

Cuadro 3

Microsoft no solo ha publicado recientemente el código fuente abierto de sus propios proyectos, sino que otras compañías también están siguiendo esta tendencia. Para nosotros, los desarrolladores de PVS-Studio, esta es una excelente manera de probar el analizador nuevamente, ver qué cosas interesantes puede encontrar y notificar a los autores del proyecto al respecto. Hoy miramos dentro del proyecto Fast Reports.

¿Qué se comprobó?


FastReport es un generador de informes desarrollado por Fast Reports . Escrito en C # y compatible con .NET Standard 2.0+. El código fuente del proyecto se publicó recientemente en GitHub , donde se descargó para su posterior análisis.

Los informes admiten el uso de texto, imágenes, líneas, formas, diagramas, tablas, códigos de barras, etc. Pueden ser de una sola página y de varias páginas, lo que incluye, además de datos, una portada y una contraportada. Las fuentes de datos pueden ser XML, CSV, Json, MS SQL, MySql, Oracle, Postgres, MongoDB, Couchbase, RavenDB, SQLite.

Hay varias formas de crear plantillas de informes: desde el código; como un archivo xml; utilizando un diseñador en línea o FastReport Designer Community Edition.

Si es necesario, las bibliotecas se pueden descargar como paquetes NuGet .

Puede leer más sobre las características del producto en la página GitHub del proyecto .

Cuadro 8


No hubo problemas con el ensamblaje del proyecto: lo ensamblé desde Visual Studio 2017, desde donde posteriormente se verificó utilizando el complemento PVS-Studio.

PVS-Studio es un analizador estático que busca errores en C, C ++, C #, código Java. Al analizar el código C #, puede usar el analizador IDE de Visual Studio usando el complemento PVS-Studio, o puede verificar proyectos desde la línea de comando , para la cual se usa la utilidad de línea de comando PVS-Studio_Cmd.exe. Si lo desea, puede configurar el análisis en el servidor de compilación o conectar los resultados del análisis a SonarQube .

Bueno, veamos qué fue interesante esta vez.

Como el proyecto es pequeño, no debe contar con muchos errores tipográficos y lugares sospechosos. Veamos lo que se encontró e incluso intentemos reproducir algo en la práctica.

Gafas y mas


Se reunió el siguiente método:

public override string ToString() { if (_value == null) return null; return this.String; } 

Advertencia de PVS-Studio : V3108 No se recomienda devolver 'nulo' del método 'ToSting ()'. Variant.cs 1519

Sí, devolver nulo desde un método ToString () anulado no es un error en sí mismo, pero aún así es un mal estilo. Esto se indica, entre otras cosas, en la documentación de Microsoft : Su anulación ToString () no debe devolver Empty o una cadena nula . Los desarrolladores que no esperan un retorno nulo como valor de retorno de ToString () pueden sorprenderse desagradablemente cuando se lanza una excepción ArgumentNullException durante la ejecución del siguiente código (siempre que el método de extensión se llame para IEnumerable <T> ).

 Variant varObj = new Variant(); varObj.ToString().Contains(character); 

Puede encontrar fallas en el hecho de que el ejemplo es sintético, pero la esencia de esto no cambia.

Además, este código se comenta de la siguiente manera:

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

Ups En lugar de "" devuelve nulo .

Vamos a continuar

Hay una clase FastString en la biblioteca . Descripción: alternativa rápida de StringBuilder . En realidad, esta clase contiene un campo de tipo StringBuilder . Los constructores de la clase FastString llaman al método Init , que inicializa el campo correspondiente.

El código de uno de los constructores:

 public FastString() { Init(initCapacity); } 

Y el código del método Init :

 private void Init(int iniCapacity) { sb = new StringBuilder(iniCapacity); //chars = new char[iniCapacity]; //capacity = iniCapacity; } 

Si lo desea, puede acceder al campo sb a través de la propiedad StringBuilder :

 public StringBuilder StringBuilder { get { return sb; } } 

En total, FastString tiene 3 constructores:

 public FastString(); public FastString(int iniCapacity); public FastString(string initValue); 

Ya he demostrado al cuerpo del primer diseñador que hacen las dos conjeturas restantes, creo, tampoco es difícil. Y ahora, atención. Adivina qué generará el siguiente código:

 FastString fs = new FastString(256); Console.WriteLine(fs.StringBuilder.Capacity); 

Atención, la respuesta:

Imagen 2


Inesperadamente? Veamos el cuerpo del constructor correspondiente:

 public FastString(int iniCapacity) { Init(initCapacity); } 

Los lectores habituales de nuestros artículos ya deberían tener un ojo para encontrar un problema aquí. El analizador de ojos (aroma, lógica, llámalo como quieras) definitivamente está entumecido, y encontró un problema: el parámetro V3117 Constructor 'iniCapacity' no se usa. FastString.cs 434

Qué coincidencia que el código de clase contenga un campo constante initCapacity , que se pasa como argumento al método Init en lugar del parámetro constructor iniCapacity ...

 private const int initCapacity = 32; 

Cuando use nombres similares, debe ser muy, muy cuidadoso. Dondequiera que se cometieron errores relacionados con el uso de nombres similares (proyectos en C, C ++, C #, Java), hubo errores tipográficos de este tipo en todas partes ...

Por cierto, sobre errores tipográficos.

Hagamos el siguiente ejemplo simple y veamos cómo funciona:

 static void Main(string[] args) { TextObject textObj = new TextObject(); textObj.ParagraphFormat = null; Console.WriteLine("Ok"); } 

Como habrás adivinado, la salida será diferente a la cadena "Ok" :)

Cual? Entonces, por ejemplo:

Imagen 1


El problema radica en la propiedad ParagraphFormat y en el uso de nombres similares:

 public ParagraphFormat ParagraphFormat { get { return paragraphFormat; } set { ParagraphFormat = value; } } 

Advertencia de PVS-Studio : V3110 Posible recursión infinita dentro de la propiedad 'ParagraphFormat'. TextObject.cs 281

La propiedad ParagraphFormat es un contenedor sobre el campo párrafoFormato . Además, su acceso de propiedad get está escrito correctamente, pero el acceso de propiedad set contiene un error tipográfico molesto: en lugar del campo, el registro se produce en la misma propiedad, lo que provoca una recursión. De nuevo, un error relacionado con nombres similares.

Considere el siguiente fragmento 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; } .... } 

Advertencia de PVS-Studio : V3004 La declaración 'then' es equivalente a la declaración 'else'. HtmlTextRenderer.cs 2092

Un poco de copiar y pegar, y ahora, independientemente del valor de la expresión r.Width> availableWidth , se realizarán las mismas acciones. Elimine la instrucción if o cambie la lógica en una de las ramas.

 public static string GetExpression(FindTextArgs args, bool skipStrings) { while (args.StartIndex < args.Text.Length) { if (!FindMatchingBrackets(args, skipStrings)) break; return args.FoundText; } return ""; } 

Advertencia del analizador : V3020 Un 'retorno' incondicional dentro de un bucle. CodeUtils.cs 262

Debido a la declaración de retorno incondicional , no se ejecutará más de una iteración para el bucle anterior. Tal vez este código resultó después de refactorizar, o tal vez es solo una forma inusual de hacer lo que podría hacerse sin un bucle.

 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; } 

Advertencia PVS-Studio : V3106 Posible valor de índice negativo. El valor del índice 'idx' podría alcanzar -1. BarcodeCodabar.cs 70

Código potencialmente peligroso. El método FindBarItem puede devolver -1 si no encuentra el elemento pasado como parámetro. En el código de llamada (método GetPattern ), este valor se escribe en la variable idx y se usa como índice de la matriz tabelle_cb sin verificación preliminar. Al acceder al índice -1, se generará una excepción de tipo IndexOutOfRangeException .

Vamos a continuar

 protected override void Finish() { .... if (saveStreams) { FinishSaveStreams(); } else { if (singlePage) { if (saveStreams) { int fileIndex = GeneratedFiles.IndexOf(singlePageFileName); DoPageEnd(generatedStreams[fileIndex]); } else { .... } .... } .... } .... } 

Advertencia de PVS-Studio : La expresión V3022 'saveStreams' siempre es falsa. HTMLExport.cs 849

El código anterior al obtener el valor fileIndex y llamar al método DoPageEnd nunca se ejecutará, ya que el resultado de la segunda expresión saveStreams en el código siempre será falso .

De lo más interesante, esto es quizás todo (¿no esperaba un artículo en el espíritu del análisis Mono ?). Hubo otras advertencias del analizador, pero no me parecieron lo suficientemente interesantes como para incluirlas en el artículo (parte siempre permanece detrás de escena).

Conocer el diseño sería útil para su análisis, por lo que idealmente, los autores deberían mirar estas advertencias por su cuenta. Estas son advertencias como V3083 (una llamada potencialmente peligrosa a los controladores de eventos), V3022 (la condición es siempre verdadera / falsa (en este caso, a menudo debido a métodos que devuelven un valor único)), V3072 , V3073 (trabajando con IDisposable ) y otros.

Si algo de esto es irrelevante, puede:


Conclusión


Cuadro 4


A pesar de que el artículo fue breve, me complació "tocar" las advertencias del analizador con mis manos, para ver cómo se manifiesta en la práctica lo que jura el analizador.

¡Deseo a los autores del proyecto el éxito, la corrección de los problemas descubiertos, y quiero alabar un paso hacia la comunidad de código abierto!

Aconsejo al resto que pruebe el analizador en su código y vea qué puede encontrar interesante.

Todo lo mejor!



Si desea compartir este artículo con una audiencia de habla inglesa, utilice el enlace a la traducción: Sergey Vasiliev. Los informes más rápidos en el salvaje oeste ... y un puñado de errores ...

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


All Articles