Not only Microsoft has recently been putting open source code for its own projects - other companies are also following this trend. For us, the PVS-Studio developers, this is a great way to test the analyzer again, see what interesting it can find, and inform the project authors about it. Today we are looking inside the Fast Reports project.
What was checked?
FastReport is a report generator developed by
Fast Reports . Written in C # and compatible with .NET Standard 2.0+. The source code of the project was recently
posted on GitHub , from where it was uploaded for further analysis.
The reports support the use of text, images, lines, shapes, diagrams, tables, bar codes, etc. They can be single-page and multi-page, including, in addition to the data, a cover and a back page. XML, CSV, Json, MS SQL, MySql, Oracle, Postgres, MongoDB, Couchbase, RavenDB, SQLite can be used as data sources.
There are various ways to create report templates: from code; as an xml file; using an online designer or FastReport Designer Community Edition.
')
If necessary, the library can be downloaded as
NuGet packages .
You can read more about the product features on
the project's GitHub page .

There were no problems with the build of the project - I collected it from Visual Studio 2017, from where I checked it later using the PVS-Studio plugin.
PVS-Studio is a static analyzer that looks for errors in C, C ++, C #, Java code. When analyzing C # code, you can use the Visual Studio IDE analyzer using the PVS-Studio plugin for this, or you can
check projects from the command line , for which the command-line utility PVS-Studio_Cmd.exe is used. If you wish, you can
set up the analysis on the assembly server or
pick up the analysis results to the SonarQube .
Well, let's see what was interesting this time.
Since the project is small, you should not count on a lot of typos and suspicious places. Let's look at what has been found, and even try to reproduce something in practice.
Ochepyatki and not only
I met the following method:
public override string ToString() { if (_value == null) return null; return this.String; }
PVS-Studio warning :
V3108 It is not recommended to return the 'null' from 'ToSting ()' method. Variant.cs 1519
Yes, returning
null from the overridden
ToString () method is not in itself an error, but it’s still a bad style. This is indicated, among other things, in
the Microsoft documentation :
Your ToString () should not return. Empty or a null string . Developers who do not expect to return
null as a return value of
ToString () can be unpleasantly surprised when an
ArgumentNullException exception is thrown during the execution of the code below (provided that the extension method for
IEnumerable <T> is called).
Variant varObj = new Variant(); varObj.ToString().Contains(character);
You can find fault with the fact that the example is synthetic, but the essence does not change.
Moreover, this code is commented as follows:
Oops. Returns "" instead of
null .
We continue.
The library has a class
FastString . Description:
Fast alternative of StringBuilder . Actually, this class contains a field of type
StringBuilder . The constructors of the
FastString class call the
Init method, which initializes the corresponding field.
Code of one of the designers:
public FastString() { Init(initCapacity); }
And the code of the
Init method:
private void Init(int iniCapacity) { sb = new StringBuilder(iniCapacity);
If desired, you can access the
sb field via the
StringBuilder property:
public StringBuilder StringBuilder { get { return sb; } }
In total,
FastString has 3 constructors:
public FastString(); public FastString(int iniCapacity); public FastString(string initValue);
I have already shown the body of the first designer, what the other two are doing to guess, I think, it’s also easy. And now, attention. Guess what output the following code:
FastString fs = new FastString(256); Console.WriteLine(fs.StringBuilder.Capacity);
Attention answer:

Suddenly? Let's look at the body of the corresponding constructor:
public FastString(int iniCapacity) { Init(initCapacity); }
Regular readers of our articles should already have an eye to find a problem here. At the eye analyzer (scent, logic, call it what you want) exactly planted, and he found the problem:
V3117 Constructor parameter 'iniCapacity' is not used. FastString.cs 434
What a coincidence that the class code contains a constant field
initCapacity , which is passed as an argument to the
Init method instead of the
iniCapacity constructor
parameter ...
private const int initCapacity = 32;
When using similar names you need to be very, very attentive. Wherever there are errors associated with the use of similar names - projects in C, C ++, C #, Java - there were typos of this kind everywhere ...
By the way, about typos.
Let's make the following simple example and see how it works:
static void Main(string[] args) { TextObject textObj = new TextObject(); textObj.ParagraphFormat = null; Console.WriteLine("Ok"); }
As you may have guessed, the output will be different than the string “Ok” :)
How? So, for example:

The problem lies in the
ParagraphFormat property and in the use of similar names:
public ParagraphFormat ParagraphFormat { get { return paragraphFormat; } set { ParagraphFormat = value; } }
PVS-Studio warning :
V3110 Possible infinite recursion inside 'ParagraphFormat' property. TextObject.cs 281
The
ParagraphFormat property is a wrapper over the
paragraphFormat field. Moreover, its get property accessor is written correctly, but the set property accessor contains an annoying typo: instead of a field, an entry occurs in the same property, which causes recursion. Again the error associated with similar names.
Consider the following code snippet.
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 The 'then' statement is equivalent to the 'else' statement. HtmlTextRenderer.cs 2092
A little copy-paste, and now the same actions will be performed regardless of the value of the expression
r.Width> availableWidth . It is necessary either to remove the
if statement , or to change the logic in one of the branches.
public static string GetExpression(FindTextArgs args, bool skipStrings) { while (args.StartIndex < args.Text.Length) { if (!FindMatchingBrackets(args, skipStrings)) break; return args.FoundText; } return ""; }
Analyzer warning :
V3020 An unconditional 'return' within a loop. CodeUtils.cs 262
Due to the unconditional
return operator for the above loop, no more than one iteration will be executed. Perhaps this code was obtained after refactoring, or maybe it is just an unusual way to do something that could be done without a 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; }
PVS-Studio warning :
V3106 Possible negative index value. The value of 'idx' index could reach -1. BarcodeCodabar.cs 70
Potentially dangerous code. The
FindBarItem method can return
-1 if it does not find the item passed as a parameter. In the calling code (
GetPattern method), this value is written to the
idx variable and used as the index of the array
tabelle_cb without prior checking. When accessing by index
-1, an exception of type
IndexOutOfRangeException will be generated.
We continue.
protected override void Finish() { .... if (saveStreams) { FinishSaveStreams(); } else { if (singlePage) { if (saveStreams) { int fileIndex = GeneratedFiles.IndexOf(singlePageFileName); DoPageEnd(generatedStreams[fileIndex]); } else { .... } .... } .... } .... }
PVS-Studio warning :
V3022 Expression 'saveStreams' is always false. HTMLExport.cs 849
The given code with getting the
fileIndex value and calling the
DoPageEnd method
will never be executed, since the result of the second
saveStreams given in the code will always be
false .
From the most interesting is, perhaps, everything (you did not wait for the article in the spirit of the
analysis of Mono ?). There were other analyzer warnings, but they did not seem interesting enough to include them in the article (some always remain behind the scenes).
Knowledge of the project would be useful for analyzing them, so ideally the authors should look at these warnings on their own. These are warnings such as
V3083 (a potentially dangerous invocation of event handlers),
V3022 (the condition is always true / false (in this case, often because of methods that return one value)),
V3072 ,
V3073 (working with
IDisposable ) and others.
If any of this is irrelevant, you can:
Conclusion

Despite the fact that the article came out small, I was pleased to “touch” the warnings of the analyzer with my hands - to see how what the analyzer curses is manifested in practice.
I wish success to the authors of the project, correcting the problems found and want to praise the step towards the open-source community!
I advise the rest to try the analyzer on your code and see what interesting things you can find.
All the best!

If you want to share this article with an English-speaking audience, then please use the link to the translation: Sergey Vasiliev.
The Fastest Reports in the Wild West - and a Handful of Bugs ...