{"id":381659,"date":"2024-06-29T03:47:52","date_gmt":"2024-06-29T03:47:52","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=381659"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=381659","title":{"rendered":"<span>PVS-Studio checks the code quality in the .NET Foundation projects: LINQ to DB<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>The .NET Foundation is an independent organization, created by Microsoft, to support open-source projects around the DotNet platform. Currently, the organization gathered many libraries under its wing. We have already tested some of these libraries with the help of PVS-Studio. The next project to check with the analyzer &#8212; LINQ to DB. <\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/54b\/7d5\/a72\/54b7d5a7227696b100a070e8b3f81063.png\" width=\"580\" height=\"327\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/54b\/7d5\/a72\/54b7d5a7227696b100a070e8b3f81063.png\"\/><figcaption><\/figcaption><\/figure>\n<h3>Introduction<\/h3>\n<p><a href=\"https:\/\/dotnetfoundation.org\/projects\/linq2db\">LINQ to DB<\/a> is a database access framework based on LINQ. LINQ to DB has collected the best of its predecessors. It allows you to work with various DBMS, whereas LINQ to SQL back in the day allowed you to work only with MS SQL. It&#8217;s not as heavy and complicated as LINQ to SQL or Entity Framework. LINQ to DB provides more control and quick access to data. The framework is not that big: it&#8217;s written in C# and contains more than 40,000 lines of code.<\/p>\n<p>LINQ to DB is also one of the .NET Foundation projects. We have previously checked the projects of this organization: <a href=\"https:\/\/pvs-studio.com\/en\/blog\/posts\/csharp\/0653\/\">Windows Forms<\/a>, <a href=\"https:\/\/pvs-studio.com\/en\/blog\/posts\/csharp\/0400\/\">Xamarin.Forms<\/a>, <a href=\"https:\/\/pvs-studio.com\/en\/blog\/posts\/csharp\/0677\/\">Teleric UI for UWP<\/a>, etc.<\/p>\n<p>A little less conversation, a little more action! Let&#8217;s check the LINQ to DB code taken from the official repository on <a href=\"https:\/\/github.com\/linq2db\/linq2db\">GitHub<\/a>. With the help of our <a href=\"https:\/\/pvs-studio.com\/\">PVS-Studio<\/a> static analyzer, we will see if everything is fine with the LINQ&#8217;s successor.<\/p>\n<h3>Deja Vu<\/h3>\n<p>Let me start, probably, with the most common cases that every developer encountered at least once: duplicate code.<\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v3001\/\">V3001<\/a> There are identical sub-expressions &#8216;genericDefinition == typeof(Tuple&lt;,,,,,,,>)&#8217; to the left and to the right of the &#8216;||&#8217; operator. TypeExtensions.cs 230<\/p>\n<pre><code>public static bool IsTupleType(this Type type) {   ....   if (genericDefinition    == typeof(Tuple&lt;>)         || genericDefinition == typeof(Tuple&lt;;,>)         || genericDefinition == typeof(Tuple&lt;,,>)         || genericDefinition == typeof(Tuple&lt;,,,>)         || genericDefinition == typeof(Tuple&lt;,,,,>)         || genericDefinition == typeof(Tuple&lt;,,,,,>)         || genericDefinition == typeof(Tuple&lt;,,,,,,>)         || genericDefinition == typeof(Tuple&lt;,,,,,,,>)         || genericDefinition == typeof(Tuple&lt;,,,,,,,>))   {     return true;   }   .... } <\/code><\/pre>\n<p>The first message of the analyzer caught my eye. Those who use tuples infrequently may think that this is a common consequence of copy-paste. Without hesitation, we can assume that a developer missed a comma in the last line of the <em>Tuple&lt;,,,,,,,><\/em> condition. However, even the Visual Studio&#8217;s functionality showed me I was wrong. <\/p>\n<p>Tuples in C# are divided into 8 types according to the number of elements. 7 of them differ only in a different number of elements, from 1 to 7, respectively. In this case, they correspond to the first seven lines in the condition. And the last one, <em>Tuple&lt;,,,,,,,><\/em>, includes 8 or more elements. <\/p>\n<p>As a result, when trying to write <em>Tuple&lt;,,,,,,,,><\/em>, Visual Studio tells that there is no such tuple. Turns out that in the example above, there is an extra check for the variable correspondence with the <em>Tuple&lt;,,,,,,,><\/em> type, and not the missing comma, as it seemed initially.<\/p>\n<p>But the next analyzer warning that caught my eye, has already raised a couple of questions.<\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v3003\/\">V3003<\/a> The use of &#8216;if (A) {&#8230;} else if (A) {&#8230;}&#8217; pattern was detected. There is a probability of logical error presence. Check lines: 256, 273. SqlPredicate.cs 256<\/p>\n<pre><code>public ISqlPredicate Reduce(EvaluationContext context) {   ....   if (Operator == Operator.Equal)   {     ....   }   else   if (Operator == Operator.NotEqual)   {     search.Conditions.Add(       new SqlCondition(false, predicate, true));     search.Conditions.Add(       new SqlCondition(false, new IsNull(Expr1, false), false));     search.Conditions.Add(       new SqlCondition(false, new IsNull(Expr2, true), true));     search.Conditions.Add(       new SqlCondition(false, new IsNull(Expr1, true), false));     search.Conditions.Add(       new SqlCondition(false, new IsNull(Expr2, false), false));   }   else   if (Operator == Operator.LessOrEqual ||        Operator == Operator.GreaterOrEqual)   {     ....   }   else if (Operator == Operator.NotEqual)   {     search.Conditions.Add(       new SqlCondition(false, predicate, true));     search.Conditions.Add(       new SqlCondition(false, new IsNull(Expr1, false), false));     search.Conditions.Add(       new SqlCondition(false, new IsNull(Expr2, false), false));   }   else   {     ....   }   .... } <\/code><\/pre>\n<p>According to the analyzer, there are two branches with the same conditions in the fragment. That&#8217;s why the second condition is always false. By the way, this is also indirectly indicated by another analyzer message: <a href=\"https:\/\/pvs-studio.com\/en\/w\/v3022\/\">V3022<\/a> Expression &#8216;Operator == Operator.NotEqual&#8217; is always false. SqlPredicate.cs 273.<\/p>\n<p>In the example, we see the repetition of the <em>Operator == Operator.NotEqual<\/em> condition. These two condition branches perform slightly different operations. So, the question is &#8212; which of the branches do the developers really need? After a little analysis of the <em>Reduce<\/em> function I assume that most likely the developers need exactly the first branch. The one that has comparison with <em>Operator.NotEqual<\/em>. Its functionality is more similar to the <em>Equal<\/em> and <em>LessOrEqual<\/em>. Unlike its twin, the second branch with <em>NotEqual<\/em> has absolutely identical functionality with the <em>else<\/em> branch. Here is a <a href=\"https:\/\/github.com\/linq2db\/linq2db\/blob\/master\/Source\/LinqToDB\/SqlQuery\/SqlPredicate.cs\">link<\/a> to the original file for comparison, pay attention to 245-284 lines.<\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v3008\/\">V3008<\/a> The &#8216;newElement&#8217; variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 1320, 1315. ConvertVisitor.cs 1320<\/p>\n<pre><code>internal IQueryElement? ConvertInternal(IQueryElement? element) {   ....   switch (element.ElementType)   {     ....     case QueryElementType.WithClause:     {       var with = (SqlWithClause)element;        var clauses = ConvertSafe(with.Clauses);        if (clauses != null &amp;&amp; !ReferenceEquals(with.Clauses, clauses))       {         newElement = new SqlWithClause()         {           Clauses = clauses         };          newElement = new SqlWithClause() { Clauses = clauses };       }       break;     }     ....   }   .... } <\/code><\/pre>\n<p>In this code fragment, the author, apparently, could not decide on the style. They couldn&#8217;t choose the one and left both options. That&#8217;s exactly what the analyzer detected. I would recommend picking one and remove the unnecessary assignment. The analyzer issued the same message one more time:<\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v3008\/\">V3008<\/a> The &#8216;Stop&#8217; variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 25, 24. TransformInfo.cs 25<\/p>\n<pre><code>public TransformInfo(Expression expression, bool stop, bool @continue) {   Expression = expression;   Stop       = false;   Stop       = stop;   Continue   = @continue; } <\/code><\/pre>\n<p>Now it&#8217;s a different story. Here the *Stop *variable is first assigned with the *false *value and immediately after in the next line &#8212; with the <em>stop<\/em> value of parameter. Logically, in this case it is necessary to remove the first assignment since it is not used and is instantly overwritten by the argument value.<\/p>\n<h3>Where did the variable go?<\/h3>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v3010\/\">V3010<\/a> The return value of function &#8216;ToDictionary&#8217; is required to be utilized. ReflectionExtensions.cs 34<\/p>\n<pre><code>public static MemberInfo[] GetPublicInstanceValueMembers(this Type type) {   if (type.IsAnonymous())   {     type.GetConstructors().Single()                                    .GetParameters()                                    .Select((p, i) => new { p.Name, i })                                    .ToDictionary(_ => _.Name, _ => _.i);   }   .... } <\/code><\/pre>\n<p>What was the developer&#8217;s intent with this fragment? It seems that there&#8217;s a variable missing, to which you need to assign the result of this expression execution. Otherwise, the logic of action is unclear. During further execution of the <em>GetPublicInstanceValueMembers<\/em> function, there is no call of such expression. The developer&#8217;s intent is unknown. Maybe this code fragment is in progress, so we need to wait for its further development.<\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v3025\/\">V3025<\/a> Incorrect format. A different number of format items is expected while calling &#8216;AppendFormat&#8217; function. Arguments not used: 1st. ExpressionTestGenerator.cs 663<\/p>\n<pre><code>void BuildType(Type type, MappingSchema mappingSchema) {   ....   _typeBuilder.AppendFormat(     type.IsGenericType ? @\" {8} {6}{7}{1} {2}&lt;{3}>{5}   {{{4}{9}   }} \" : @\" {8} {6}{7}{1} {2}{5}   {{{4}{9}   }} \",     MangleName(isUserName, type.Namespace, \"T\"),     type.IsInterface ? \"interface\"                       : type.IsClass ? \"class\"                                      : \"struct\",     name,     type.IsGenericType ? GetTypeNames(type.GetGenericArguments(), \",\")                         : null,     string.Join(\"\\r\\n\", ctors),     baseClasses.Length == 0 ? \"\"                              : \" : \" + GetTypeNames(baseClasses),     type.IsPublic ? \"public \"                    : \"\",     type.IsAbstract &amp;&amp; !type.IsInterface ? \"abstract \"                                           : \"\",     attr,     members.Length > 0 ? (ctors.Count != 0 ? \"\\r\\n\" : \"\") +                           string.Join(\"\\r\\n\", members)                         : string.Empty); } <\/code><\/pre>\n<p>In this fragment we see the string formatting. The question is, where did the first argument call go? In the first formatted line, a developer used indexes from 1 to 9. But either the developer didn&#8217;t need an argument with the index 0, or they forgot about it.<\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v3137\/\">V3137<\/a> The &#8216;version&#8217; variable is assigned but is not used by the end of the function. Query.cs 408<\/p>\n<pre><code>public void TryAdd(IDataContext dataContext, Query query, QueryFlags flags) {   QueryCacheEntry[] cache;   int version;   lock (_syncCache)   {     cache   = _cache;     version = _version;   }   ....   lock(_syncCashe)   {     ....     var versionsDiff = _version - version;     ....     _cache   = newCache;     _indexes = newPriorities;     version  = _version;   }  } <\/code><\/pre>\n<p>We&#8217;re in a tricky situation here. According to the diagnostic message, a value is assigned to the local <em>version<\/em> variable without ever using this value by end of the function. Well, one thing at a time. <\/p>\n<p>At the very beginning, the value from <em>_version<\/em> is assigned to the <em>version<\/em> variable. During code execution, the <em>version<\/em> value does not change. It&#8217;s only called once to calculate the difference with <em>_version<\/em>. And at the end, <em>_version<\/em> is assigned to the <em>version<\/em> again. The presence of <em>lock<\/em> statements implies that during the execution of a code fragment, outside the block with the <em>_version<\/em> variable, changes can occur in parallel from outside the function. <\/p>\n<p>In this case, it is logical to assume that at the end it was necessary to swap <em>version<\/em> with <em>_version<\/em>. Still, it seems strange to assign a global value to a local variable at the end of a function. The analyzer issued similar message one more time: V3137 The &#8216;leftcontext&#8217; variable is assigned but is not used by the end of the function. ExpressionBuilder.SqlBuilder.cs 1989<\/p>\n<h3>One loop iteration.<\/h3>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v3020\/\">V3020<\/a> An unconditional &#8216;return&#8217; within a loop. QueryRunner.cs 751<\/p>\n<pre><code>static T ExecuteElement(   Query          query,   IDataContext   dataContext,   Mapper      mapper,   Expression     expression,   object?[]?     ps,   object?[]?     preambles) {   using (var runner = dataContext.GetQueryRunner(query, 0, expression, ps,     preambles))   {     using (var dr = runner.ExecuteReader())     {       while (dr.Read())       {         var value = mapper.Map(dataContext, runner, dr);         runner.RowsCount++;         return value;       }     }      return Array.Empty.First();   } } <\/code><\/pre>\n<p>It&#8217;s natural to use the <em>while (reader.Read())<\/em> construct if you need to select the multiple rows from database. But here in the loop, we see the <em>return<\/em> without any conditions, which means that only one row is needed. Then the question is &#8212; why use a loop? In our case, there is no need for the <em>while<\/em> loop. If you need only the first element from the database, you can use a simple <em>if<\/em>.<\/p>\n<h3>Repeat actions make perfection<\/h3>\n<p>The cases with repeated checks are still present.<\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v3022\/\">V3022<\/a> Expression &#8216;version > 15&#8217; is always true. SqlServerTools.cs 250<\/p>\n<pre><code>internal static IDataProvider? ProviderDetector(IConnectionStringSettings css,   string connectionString) {   ....   if (int.TryParse(conn.ServerVersion.Split('.')[0], out var version))   {     if (version &lt;= 8)       return GetDataProvider(SqlServerVersion.v2000, provider);      using (var cmd = conn.CreateCommand())     {       ....       switch (version)       {         case  8 : return GetDataProvider(SqlServerVersion.v2000, provider);         case  9 : return GetDataProvider(SqlServerVersion.v2005, provider);         case 10 : return GetDataProvider(SqlServerVersion.v2008, provider);         case 11 :         case 12 : return GetDataProvider(SqlServerVersion.v2012, provider);         case 13 : return GetDataProvider(SqlServerVersion.v2016, provider);         case 14 :         case 15 : return GetDataProvider(SqlServerVersion.v2017, provider);         default :           if (version > 15)             return GetDataProvider(SqlServerVersion.v2017, provider);           return GetDataProvider(SqlServerVersion.v2008, provider);       }     }   }   .... } <\/code><\/pre>\n<p>You saw a code fragment. Did you notice an error? The analyzer says that in this example, the *version > 15 *condition is always true, which is why the <em>return GetDataProvider(SqlServerVersion.v2008, provider<\/em>) string is unreachable code. But let&#8217;s take a closer look at the <em>ProviderDetector<\/em> function.<\/p>\n<p>Firstly, I suggest paying attention to the <em>version &lt;= 8<\/em> condition. It means that further code cannot be executed if the version of SQLServer is 8 or earlier. But if we look down, we see the <em>case 8<\/em> branch in the <em>switch<\/em> statement. This branch executes identical code. The fragment is an unreachable code, because the 8th version can no longer be used due to the condition above. And since it still executes the same code, then you can safely remove this branch from <em>switch<\/em>.<\/p>\n<p>Secondly, let&#8217;s talk about the analyzer&#8217;s message. As we already said, all versions earlier than or equal to 8th will not go beyond the first condition. Versions from 9th to 15th are caught in the <em>switch<\/em> branches. In this case, we get into the <em>default<\/em> branch when the condition <em>version > 15<\/em> is met. It makes the check of the same condition inside the <em>default<\/em> branch meaningless. <\/p>\n<p>But the question remains: what do we need to write in <em>GetDataProvider<\/em> &#8212; <em>v2017<\/em> or <em>v2008<\/em>? If we look at the rest of the <em>switch<\/em> branches, we can assume the following: the older the version, the SQLServer&#8217;s release year is higher as well. In this case, let&#8217;s use <em>SQLServerVersion.V2017<\/em>. The correct version of this code should look like this:<\/p>\n<pre><code>internal static IDataProvider? ProviderDetector(IConnectionStringSettings css,   string connectionString) {   ....   if (int.TryParse(conn.ServerVersion.Split('.')[0], out var version))   {     if (version &lt;= 8)       return GetDataProvider(SqlServerVersion.v2000, provider);      using (var cmd = conn.CreateCommand())     {       ....       switch (version)       {         case  9 : return GetDataProvider(SqlServerVersion.v2005, provider);         case 10 : return GetDataProvider(SqlServerVersion.v2008, provider);         case 11 :         case 12 : return GetDataProvider(SqlServerVersion.v2012, provider);         case 13 : return GetDataProvider(SqlServerVersion.v2016, provider);         case 14 :         case 15 : return GetDataProvider(SqlServerVersion.v2017, provider);         default : return GetDataProvider(SqlServerVersion.v2017, provider);       }     }   }   .... } <\/code><\/pre>\n<p>Now let&#8217;s take a look at a simpler example of the <a href=\"https:\/\/pvs-studio.com\/en\/w\/v3022\/\">V3022<\/a> diagnostic&#8217;s triggering in this project.<\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v3022\/\">V3022<\/a> Expression &#8216;table == null&#8217; is always true. LoadWithBuilder.cs 113<\/p>\n<pre><code>TableBuilder.TableContext GetTableContext(IBuildContext ctx, Expression path,    out Expression? stopExpression) {   stopExpression = null;    var table = ctx as TableBuilder.TableContext;    if (table != null)     return table;    if (ctx is LoadWithContext lwCtx)     return lwCtx.TableContext;    if (table == null)   {     ....   }   .... } <\/code><\/pre>\n<p>What do we have here? The <em>table<\/em> variable is compared to <em>null<\/em> twice. The first time, the condition checks the variable for an inequality with <em>null<\/em>. When the condition is met, the exit from a function takes place. This means that the code below the branch of the condition is executed only when <em>table<\/em> <em>=<\/em> <em>null<\/em>. No actions are performed on the variable until the next check. As a result, when the code reaches the <em>table<\/em> <em>==<\/em> <em>null<\/em> condition, this check always returns <em>true<\/em>.<\/p>\n<p>Diagnostics of <a href=\"https:\/\/pvs-studio.com\/en\/w\/v3022\/\">V3022<\/a> issued a few more useful warnings. We will not review them all in the article, but we encourage authors to check the project themselves and see all the warnings of the PVS-Studio analyzer.<\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v3063\/\">V3063<\/a> A part of conditional expression is always true if it is evaluated: field.Field.CreateFormat != null. BasicSqlBuilder.cs 1255<\/p>\n<pre><code>protected virtual void BuildCreateTableStatement(....) {   ....   if (field.Field.CreateFormat != null)   {     if (field.Field.CreateFormat != null &amp;&amp; field.Identity.Length == 0)     {       ....     }   }   .... } <\/code><\/pre>\n<p>In the code snippet above, you can see that <em>field.Field.CreateFormat<\/em> is checked twice for <em>null<\/em>. But in this case, the second check is performed directly in the branch of the first check. Since the first check is a success, so when the checked value has not changed, it is not necessary to compare the <em>field.Field.CreateFormat<\/em> value with <em>null<\/em> for the second time.<\/p>\n<h3>null as something to die for<\/h3>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v3022\/\">V3022<\/a> Expression &#8216;rows&#8217; is always not null. The operator &#8216;?.&#8217; is excessive. SQLiteSqlBuilder.cs 214<\/p>\n<pre><code>protected override void BuildSqlValuesTable(   SqlValuesTable valuesTable,   string alias,   out bool aliasBuilt) {   valuesTable = ConvertElement(valuesTable);   var rows = valuesTable.BuildRows(OptimizationContext.Context);    if (rows.Count == 0)   {     ....   }   else   {     ....      if (rows?.Count> 0)     {      ....     }      ....   }   aliasBuilt = false; } <\/code><\/pre>\n<p>According to the analyzer, in the line of this code fragment, the *if (rows?.Count > 0) *check for <em>null<\/em> is unnecessary, since <em>rows<\/em> cannot be <em>null<\/em> at that moment. Let&#8217;s figure it out why. The result of the <em>BuildRows<\/em> function is assigned to the <em>rows<\/em> variable. Here is the code fragment of the function:<\/p>\n<pre><code>internal IReadOnlyList BuildRows(EvaluationContext context) {   if (Rows != null)     return Rows;   ....   var rows = new List();   if (ValueBuilders != null)   {     foreach (var record in source)     {       ....        var row = new ISqlExpression[ValueBuilders!.Count];       var idx = 0;       rows.Add(row);        ....     }   }   return rows; } <\/code><\/pre>\n<p>Since <em>BuildRows<\/em> cannot return <em>null<\/em>, then, according to the analyzer, check for <em>null<\/em> is redundant. But if <em>BuildRows<\/em> had returned <em>null<\/em> &#8212; what is meant by <em>rows rows?.Count > 0<\/em> condition &#8212; then at the moment of the <em>rows.Count == 0<\/em> condition check, the <em>NullReferenceException<\/em> would have been thrown. In the of such a condition, you would also need to do a <em>null<\/em> check to avoid an error. Until then, the current code looks suspicious and checking for <em>null<\/em> is redundant.<\/p>\n<p>We got to the message, which made me think hard and do a couple of checks.<\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v3042\/\">V3042<\/a> Possible NullReferenceException. The &#8216;?.&#8217; and &#8216;.&#8217; operators are used for accessing members of the &#8216;_update&#8217; object SqlUpdateStatement.cs 60<\/p>\n<pre><code>public override ISqlTableSource? GetTableSource(ISqlTableSource table) {   ....   if (table == _update?.Table)     return _update.Table;   .... } <\/code><\/pre>\n<p>A small fragment, a condition and exit from the function.<\/p>\n<p>So, the analyzer has detected that <em>update<\/em> is accessed in two ways &#8212; with the null-conditional operator and without it. You might think that the condition is met only if <em>_update<\/em> doesn&#8217;t equal <em>null<\/em> and both parts of the equality are the same. But. Big fat BUT.<\/p>\n<p>In the case when <em>table<\/em> and <em>_update<\/em> equal <em>null<\/em>, then <em>_update?.Table<\/em> returns <em>null<\/em>. That meets the condition. Then when trying to call <em>_update.Table<\/em> you will get <em>NullReferenceException<\/em>. If we can return <em>null<\/em>, as <em>ISqlTableSource?<\/em> tells us in the function declaration, then we should write <em>return _update?.Table<\/em> to avoid an error.<\/p>\n<h3>Conclusion<\/h3>\n<p>The LINQ to DB project is large and complex, which makes it more exciting to check it. The project has a very large community, and we were lucky to get some interesting warnings. <\/p>\n<p>If you want to know whether your code base have similar errors, you can <a href=\"https:\/\/pvs-studio.com\/linq-to-db-project\">try PVS-Studio<\/a> on your project.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/589773\/\"> https:\/\/habr.com\/ru\/articles\/589773\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>The .NET Foundation is an independent organization, created by Microsoft, to support open-source projects around the DotNet platform. Currently, the organization gathered many libraries under its wing. We have already tested some of these libraries with the help of PVS-Studio. The next project to check with the analyzer &#8212; LINQ to DB. <\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<h3>Introduction<\/h3>\n<p><a href=\"https:\/\/dotnetfoundation.org\/projects\/linq2db\">LINQ to DB<\/a> is a database access framework based on LINQ. LINQ to DB has collected the best of its predecessors. It allows you to work with various DBMS, whereas LINQ to SQL back in the day allowed you to work only with MS SQL. It&#8217;s not as heavy and complicated as LINQ to SQL or Entity Framework. LINQ to DB provides more control and quick access to data. The framework is not that big: it&#8217;s written in C# and contains more than 40,000 lines of code.<\/p>\n<p>LINQ to DB is also one of the .NET Foundation projects. We have previously checked the projects of this organization: <a href=\"https:\/\/pvs-studio.com\/en\/blog\/posts\/csharp\/0653\/\">Windows Forms<\/a>, <a href=\"https:\/\/pvs-studio.com\/en\/blog\/posts\/csharp\/0400\/\">Xamarin.Forms<\/a>, <a href=\"https:\/\/pvs-studio.com\/en\/blog\/posts\/csharp\/0677\/\">Teleric UI for UWP<\/a>, etc.<\/p>\n<p>A little less conversation, a little more action! Let&#8217;s check the LINQ to DB code taken from the official repository on <a href=\"https:\/\/github.com\/linq2db\/linq2db\">GitHub<\/a>. With the help of our <a href=\"https:\/\/pvs-studio.com\/\">PVS-Studio<\/a> static analyzer, we will see if everything is fine with the LINQ&#8217;s successor.<\/p>\n<h3>Deja Vu<\/h3>\n<p>Let me start, probably, with the most common cases that every developer encountered at least once: duplicate code.<\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v3001\/\">V3001<\/a> There are identical sub-expressions &#8216;genericDefinition == typeof(Tuple&lt;,,,,,,,>)&#8217; to the left and to the right of the &#8216;||&#8217; operator. TypeExtensions.cs 230<\/p>\n<pre><code>public static bool IsTupleType(this Type type) {   ....   if (genericDefinition    == typeof(Tuple&lt;>)         || genericDefinition == typeof(Tuple&lt;;,>)         || genericDefinition == typeof(Tuple&lt;,,>)         || genericDefinition == typeof(Tuple&lt;,,,>)         || genericDefinition == typeof(Tuple&lt;,,,,>)         || genericDefinition == typeof(Tuple&lt;,,,,,>)         || genericDefinition == typeof(Tuple&lt;,,,,,,>)         || genericDefinition == typeof(Tuple&lt;,,,,,,,>)         || genericDefinition == typeof(Tuple&lt;,,,,,,,>))   {     return true;   }   .... } <\/code><\/pre>\n<p>The first message of the analyzer caught my eye. Those who use tuples infrequently may think that this is a common consequence of copy-paste. Without hesitation, we can assume that a developer missed a comma in the last line of the <em>Tuple&lt;,,,,,,,><\/em> condition. However, even the Visual Studio&#8217;s functionality showed me I was wrong. <\/p>\n<p>Tuples in C# are divided into 8 types according to the number of elements. 7 of them differ only in a different number of elements, from 1 to 7, respectively. In this case, they correspond to the first seven lines in the condition. And the last one, <em>Tuple&lt;,,,,,,,><\/em>, includes 8 or more elements. <\/p>\n<p>As a result, when trying to write <em>Tuple&lt;,,,,,,,,><\/em>, Visual Studio tells that there is no such tuple. Turns out that in the example above, there is an extra check for the variable correspondence with the <em>Tuple&lt;,,,,,,,><\/em> type, and not the missing comma, as it seemed initially.<\/p>\n<p>But the next analyzer warning that caught my eye, has already raised a couple of questions.<\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v3003\/\">V3003<\/a> The use of &#8216;if (A) {&#8230;} else if (A) {&#8230;}&#8217; pattern was detected. There is a probability of logical error presence. Check lines: 256, 273. SqlPredicate.cs 256<\/p>\n<pre><code>public ISqlPredicate Reduce(EvaluationContext context) {   ....   if (Operator == Operator.Equal)   {     ....   }   else   if (Operator == Operator.NotEqual)   {     search.Conditions.Add(       new SqlCondition(false, predicate, true));     search.Conditions.Add(       new SqlCondition(false, new IsNull(Expr1, false), false));     search.Conditions.Add(       new SqlCondition(false, new IsNull(Expr2, true), true));     search.Conditions.Add(       new SqlCondition(false, new IsNull(Expr1, true), false));     search.Conditions.Add(       new SqlCondition(false, new IsNull(Expr2, false), false));   }   else   if (Operator == Operator.LessOrEqual ||        Operator == Operator.GreaterOrEqual)   {     ....   }   else if (Operator == Operator.NotEqual)   {     search.Conditions.Add(       new SqlCondition(false, predicate, true));     search.Conditions.Add(       new SqlCondition(false, new IsNull(Expr1, false), false));     search.Conditions.Add(       new SqlCondition(false, new IsNull(Expr2, false), false));   }   else   {     ....   }   .... } <\/code><\/pre>\n<p>According to the analyzer, there are two branches with the same conditions in the fragment. That&#8217;s why the second condition is always false. By the way, this is also indirectly indicated by another analyzer message: <a href=\"https:\/\/pvs-studio.com\/en\/w\/v3022\/\">V3022<\/a> Expression &#8216;Operator == Operator.NotEqual&#8217; is always false. SqlPredicate.cs 273.<\/p>\n<p>In the example, we see the repetition of the <em>Operator == Operator.NotEqual<\/em> condition. These two condition branches perform slightly different operations. So, the question is &#8212; which of the branches do the developers really need? After a little analysis of the <em>Reduce<\/em> function I assume that most likely the developers need exactly the first branch. The one that has comparison with <em>Operator.NotEqual<\/em>. Its functionality is more similar to the <em>Equal<\/em> and <em>LessOrEqual<\/em>. Unlike its twin, the second branch with <em>NotEqual<\/em> has absolutely identical functionality with the <em>else<\/em> branch. Here is a <a href=\"https:\/\/github.com\/linq2db\/linq2db\/blob\/master\/Source\/LinqToDB\/SqlQuery\/SqlPredicate.cs\">link<\/a> to the original file for comparison, pay attention to 245-284 lines.<\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v3008\/\">V3008<\/a> The &#8216;newElement&#8217; variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 1320, 1315. ConvertVisitor.cs 1320<\/p>\n<pre><code>internal IQueryElement? ConvertInternal(IQueryElement? element) {   ....   switch (element.ElementType)   {     ....     case QueryElementType.WithClause:     {       var with = (SqlWithClause)element;        var clauses = ConvertSafe(with.Clauses);        if (clauses != null &amp;&amp; !ReferenceEquals(with.Clauses, clauses))       {         newElement = new SqlWithClause()         {           Clauses = clauses         };          newElement = new SqlWithClause() { Clauses = clauses };       }       break;     }     ....   }   .... } <\/code><\/pre>\n<p>In this code fragment, the author, apparently, could not decide on the style. They couldn&#8217;t choose the one and left both options. That&#8217;s exactly what the analyzer detected. I would recommend picking one and remove the unnecessary assignment. The analyzer issued the same message one more time:<\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v3008\/\">V3008<\/a> The &#8216;Stop&#8217; variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 25, 24. TransformInfo.cs 25<\/p>\n<pre><code>public TransformInfo(Expression expression, bool stop, bool @continue) {   Expression = expression;   Stop       = false;   Stop       = stop;   Continue   = @continue; } <\/code><\/pre>\n<p>Now it&#8217;s a different story. Here the *Stop *variable is first assigned with the *false *value and immediately after in the next line &#8212; with the <em>stop<\/em> value of parameter. Logically, in this case it is necessary to remove the first assignment since it is not used and is instantly overwritten by the argument value.<\/p>\n<h3>Where did the variable go?<\/h3>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v3010\/\">V3010<\/a> The return value of function &#8216;ToDictionary&#8217; is required to be utilized. ReflectionExtensions.cs 34<\/p>\n<pre><code>public static MemberInfo[] GetPublicInstanceValueMembers(this Type type) {   if (type.IsAnonymous())   {     type.GetConstructors().Single()                                    .GetParameters()                                    .Select((p, i) => new { p.Name, i })                                    .ToDictionary(_ => _.Name, _ => _.i);   }   .... } <\/code><\/pre>\n<p>What was the developer&#8217;s intent with this fragment? It seems that there&#8217;s a variable missing, to which you need to assign the result of this expression execution. Otherwise, the logic of action is unclear. During further execution of the <em>GetPublicInstanceValueMembers<\/em> function, there is no call of such expression. The developer&#8217;s intent is unknown. Maybe this code fragment is in progress, so we need to wait for its further development.<\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v3025\/\">V3025<\/a> Incorrect format. A different number of format items is expected while calling &#8216;AppendFormat&#8217; function. Arguments not used: 1st. ExpressionTestGenerator.cs 663<\/p>\n<pre><code>void BuildType(Type type, MappingSchema mappingSchema) {   ....   _typeBuilder.AppendFormat(     type.IsGenericType ? @\" {8} {6}{7}{1} {2}&lt;{3}>{5}   {{{4}{9}   }} \" : @\" {8} {6}{7}{1} {2}{5}   {{{4}{9}   }} \",     MangleName(isUserName, type.Namespace, \"T\"),     type.IsInterface ? \"interface\"                       : type.IsClass ? \"class\"                                      : \"struct\",     name,     type.IsGenericType ? GetTypeNames(type.GetGenericArguments(), \",\")                         : null,     string.Join(\"\\r\\n\", ctors),     baseClasses.Length == 0 ? \"\"                              : \" : \" + GetTypeNames(baseClasses),     type.IsPublic ? \"public \"                    : \"\",     type.IsAbstract &amp;&amp; !type.IsInterface ? \"abstract \"                                           : \"\",     attr,     members.Length > 0 ? (ctors.Count != 0 ? \"\\r\\n\" : \"\") +                           string.Join(\"\\r\\n\", members)                         : string.Empty); } <\/code><\/pre>\n<p>In this fragment we see the string formatting. The question is, where did the first argument call go? In the first formatted line, a developer used indexes from 1 to 9. But either the developer didn&#8217;t need an argument with the index 0, or they forgot about it.<\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v3137\/\">V3137<\/a> The &#8216;version&#8217; variable is assigned but is not used by the end of the function. Query.cs 408<\/p>\n<pre><code>public void TryAdd(IDataContext dataContext, Query query, QueryFlags flags) {   QueryCacheEntry[] cache;   int version;   lock (_syncCache)   {     cache   = _cache;     version = _version;   }   ....   lock(_syncCashe)   {     ....     var versionsDiff = _version - version;     ....     _cache   = newCache;     _indexes = newPriorities;     version  = _version;   }  } <\/code><\/pre>\n<p>We&#8217;re in a tricky situation here. According to the diagnostic message, a value is assigned to the local <em>version<\/em> variable without ever using this value by end of the function. Well, one thing at a time. <\/p>\n<p>At the very beginning, the value from <em>_version<\/em> is assigned to the <em>version<\/em> variable. During code execution, the <em>version<\/em> value does not change. It&#8217;s only called once to calculate the difference with <em>_version<\/em>.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-381659","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/381659","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=381659"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/381659\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=381659"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=381659"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=381659"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}