西部荒野中最快的报道。 还有一些错误...

图片3

微软不仅发布了自己项目的开放源代码,其他公司也在遵循这一趋势。 对于我们PVS-Studio开发人员来说,这是再次测试分析仪,查看其有趣之处并将其通知项目作者的好方法。 今天,我们看一下“快速报告”项目。

检查了什么?


FastReport是由Fast Reports开发的报告生成器。 用C#编写,并且与.NET Standard 2.0+兼容。 该项目的源代码最近发布在GitHub上 ,在此下载了该源代码以进行进一步分析。

报告支持使用文本,图像,线条,形状,图表,表格,条形码等。它们可以是单页和多页的,除了数据还包括封面和封底。 数据源可以是XML,CSV,Json,MS SQL,MySql,Oracle,Postgres,MongoDB,Couchbase,RavenDB,SQLite。

有多种创建报告模板的方法:从代码; 作为xml文件; 使用在线设计师或FastReport Designer社区版。

如有必要,可以将库下载为NuGet软件包

您可以在项目的GitHub页面上阅读有关产品功能的更多信息。

图片8


项目的组装没有问题-我是从Visual Studio 2017组装的,后来从那里通过PVS-Studio插件对其进行了检查。

PVS-Studio是一种静态分析器,用于查找C,C ++,C#,Java代码中的错误。 分析C#代码时,可以使用带有PVS-Studio插件的Visual Studio IDE分析器,也可以从命令行检查使用PVS-Studio_Cmd.exe命令行实用程序的项目。 如果愿意,可以在构建服务器上配置分析 ,也可以将分析结果连接到SonarQube

好吧,让我们看看这次有趣的事情。

由于项目规模较小,因此您不应指望很多错别字和可疑的地方。 让我们看看发现的内容,甚至尝试在实践中重现某些内容。

眼镜等


达到以下方法:

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

PVS-Studio警告V3108不建议从“ ToSting()”方法返回“ null”。 Variant.cs 1519

是的,从重写的ToString()方法返回null本身并不是错误,但它仍然是错误的样式。 除其他外,在Microsoft文档中对此进行了说明您的ToString()重写不应返回Empty或null字符串 。 如果在以下代码的执行过程抛出ArgumentNullException ,则不希望将ToString()的返回值返回值的开发人员可能会感到意外(假设IEnumerable <T>调用了扩展方法)。

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

您可以发现该示例是综合性的事实,但这并没有改变。

此外,此代码的注释如下:

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

哎呀 代替“”返回null

让我们继续。

库中有一个FastString类。 说明: StringBuilder的快速替代方案 。 实际上,此类包含类型为StringBuilder的字段。 FastString类的构造函数调用Init方法,该方法初始化相应的字段。

构造函数之一的代码:

 public FastString() { Init(initCapacity); } 

以及Init方法的代码:

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

如果需要,可以通过StringBuilder属性访问sb字段:

 public StringBuilder StringBuilder { get { return sb; } } 

总的来说, FastString具有3个构造函数:

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

我已经向第一位设计师展示了他们认为剩下的两个猜测并不困难。 现在,注意。 猜猜下面的代码将输出什么:

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

注意,答案:

图片2


没想到 让我们看一下相应构造函数的主体:

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

我们文章的普通读者应该已经在这里发现问题了。 眼图分析器(气味,逻辑,叫它想要的)绝对麻木,他发现了一个问题: V3117构造函数参数'iniCapacity'未使用。 FastString.cs 434

类代码包含一个常量字段initCapacity ,这是一个巧合,它作为参数传递给Init方法而不是构造函数参数iniCapacity ...

 private const int initCapacity = 32; 

使用相似的名称时,您需要非常非常小心。 无论在哪里出现与使用相似名称相关的错误-C,C ++,C#,Java项目-到处都有这种错别字...

顺便说一句,关于错别字。

让我们做一个简单的例子,看看它是如何工作的:

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

您可能已经猜到,输出将与字符串“ Ok”不同:)

哪一个 因此,例如:

图片1


问题在于ParagraphFormat属性和使用类似的名称:

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

PVS-Studio警告V3110'ParagraphFormat '属性内可能存在无限递归。 文本对象.cs 281

ParagraphFormat属性是对paragraphFormat字段的包装。 而且,其get属性访问器的拼写正确,但是set属性访问器包含一个令人讨厌的错字:记录不是在字段中而是在同一属性中发生,从而导致递归。 同样,与相似名称相关的错误。

考虑以下代码片段。

 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警告V3004'then '语句等效于'else'语句。 HtmlTextRenderer.cs 2092

稍微复制粘贴,现在,无论表达式r.Width> availableWidth的值如何,都将执行相同的操作。 删除if语句或更改分支之一中的逻辑。

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

分析器警告V3020循环内无条件的“返回”。 CodeUtils.cs 262

由于无条件的return语句,上述循环最多执行一次迭代。 也许这段代码在重构后就出现了,或者也许这是一种不循环即可完成的非常规方法。

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

警告PVS-StudioV3106可能的负索引值。 “ idx”索引的值可能达到-1。 条码Codabar.cs 70

潜在的危险代码。 如果FindBarItem方法找不到作为参数传递的元素,则可以返回-1 。 在调用代码( GetPattern方法)中,此值被写入idx变量,并用作tabelle_cb数组的索引,而无需进行初步验证。 当访问索引-1时,抛出IndexOutOfRangeException类型的异常

让我们继续。

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

PVS-Studio警告V3022表达式'saveStreams'始终为false。 HTMLExport.cs 849

上面的带有fileIndex值并调用DoPageEnd方法的代码永远不会执行,因为代码中第二个saveStreams表达式的结果将始终为false

最有趣的是,这也许就是全部(您是否曾想到过Mono分析精神的文章?)。 还有其他分析器警告,但对我而言,它们似乎不够有趣,无法将其包括在文章中(部分始终留在幕后)。

知道设计对于他们的分析将很有用,因此理想情况下,作者应自己查看这些警告。 这些是警告,例如V3083 (可能会对事件处理程序造成危险的调用), V3022 (条件始终为true / false(在这种情况下,通常是由于返回单个值的方法所致), V3072V3073 (与IDisposposable配合使用)等警告

如果这与您无关,则可以:


结论


图片4


尽管这篇文章很短,但我还是很高兴用双手“触摸”分析仪的警告-看看实践中分析仪发誓的样子。

我希望项目的作者能够成功,纠正发现的问题,并要赞扬迈向开源社区的一步!

我建议其他人在代码上尝试使用分析器,看看有什么有趣的地方。

祝一切顺利!



如果您想与讲英语的读者分享这篇文章,请使用以下链接:Sergey Vasiliev。 狂野西部最快的报告-以及少数错误...

Source: https://habr.com/ru/post/zh-CN431370/


All Articles