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 .

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:
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);
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:

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:

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

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