macos c#_如何在Linux和macOS下工作的C#项目中查找错误

  • Post author:
  • Post category:linux


macos c#

Picture 8

PVS-Studio is a well-known static code analyzer that allows you to find a lot of tricky errors hidden in the source code. Beta testing of the new version has recently finished. It provides the possibility to analyze C# projects under Linux and macOS. The tool can also be integrated into the cross-platform IDE from JetBrains — Rider. This article will help you to get acquainted with these features using the example of checking the open source RavenDB project.

PVS-Studio是一个著名的静态代码分析器,它使您可以发现源代码中隐藏的许多棘手的错误。 新版本的Beta测试最近已经完成。 它提供了在Linux和macOS下分析C#项目的可能性。 该工具也可以集成到JetBrains – Rider的跨平台IDE中。 本文将通过检查开源RavenDB项目的示例来帮助您熟悉这些功能。




介绍

(

Introduction

)

Some time ago, my colleague

Sergey Vasiliev

wrote a

note

that beta testing of a new version of the PVS-Studio static analyzer that we are developing had started. By this moment, the beta-testing has ended and you can download the new version by following the

link

. In this article, we’ll consider the analysis of C# projects under Linux/macOS using the console interface and

Rider

. After that, we will traditionally review some interesting analyzer warnings.

前一段时间,我的同事

谢尔盖·瓦西里耶夫

写了一个

便条

说的PVS-Studio的静态分析仪的新版本测试阶段,我们正在开发已经开始。 至此,beta测试已经结束,您可以通过

链接

下载新版本。 在本文中,我们将考虑使用控制台界面和

Rider

在Linux / macOS下分析C#项目。 之后,我们通常会回顾一些有趣的分析仪警告。

Raven数据库

(

RavenDB

)

Picture 5

I chose the RavenDB open source project for the check. Its

repository

contains almost 5 thousand source code files. It is a fairly popular NoSQL database. Details can be found on the

website

. No prizes for guessing why this project got my attention. It is its size that implies that in such a serious project there will definitely be something interesting.

我选择了RavenDB开源项目进行检查。 它的

存储库

包含近5000个源代码文件。 这是一个相当流行的NoSQL数据库。 详细信息可以在

网站



找到

。 猜猜为什么这个项目吸引了我,没有奖品。 它的大小意味着在这样一个严肃的项目中肯定会有一些有趣的事情。

命令行界面

(

Command Line Interface

)

First, let’s look at how the analysis is performed via the console. This section, in my opinion, will be particularly interesting for those who want to integrate the analyzer into a CI system. The command running the analysis has a number of interesting options, but altogether everything is quite trivial. To analyze RavenDB, I go to the project folder and enter the following in the console:

首先,让我们看一下如何通过控制台执行分析。 我认为,这一部分对于那些想要将分析仪集成到CI系统中的人来说尤其有趣。 运行分析的命令有许多有趣的选项,但总的来说,一切都是微不足道的。 要分析RavenDB,我转到项目文件夹,然后在控制台中输入以下内容:

pvs-studio-dotnet -t ./RavenDB.sln

The -t flag (short for target) is used to specify the solution or project file to check. The above line starts the analysis and generates a file containing the errors found. It’s simple, isn’t it?

-t标志(target的缩写)用于指定要检查的解决方案或项目文件。 上一行开始分析,并生成一个包含发现的错误的文件。 很简单,不是吗?

骑士

(

Rider

)

Working with the analyzer in Rider is about the same as in Visual Studio. The plugin has a simple and intuitive interface that allows you to check the project in just a couple of clicks. This is not an exaggeration – to analyze RavenDB, all I had to do was click on the top Tools menu, point at «PVS-Studio» and click «Check Current Solution/Project».

在Rider中使用分析器与在Visual Studio中使用相同。 该插件具有简单直观的界面,使您只需单击几下即可检查项目。 这并不夸张-要分析RavenDB,我要做的就是单击顶部的“工具”菜单,指向“ PVS-Studio”,然后单击“检查当前解决方案/项目”。

Picture 2

The analysis results will be displayed in the lower part of the window on the PVS-Studio tab (well, which one else? 🙂 )

分析结果将显示在PVS-Studio选项卡窗口的下部(嗯,还有哪一个?))

Picture 3

The same as with the Visual Studio plugin, double-clicking on the warning will display the location it relates to. Everything is convenient and clear.

与Visual Studio插件相同,双击警告将显示其相关的位置。 一切都很方便和清晰。

More importantly, the PVS-Studio tool doesn’t just point out errors, but has an infrastructure that makes it easy to implement static analysis methodology even in a large old project.

更重要的是,PVS-Studio工具不仅可以指出错误,而且具有即使在一个大型旧项目中也可以轻松实施静态分析方法的基础架构。

The general idea is the following. Imagine, the user has started the analyzer and received many warnings. Since a project that has been developed for many years, is alive, still developing and bringing money, then most likely there won’t be many warnings in the report indicating critical defects. In other words, critical bugs have already been fixed due to more expensive ways or with the help of feedback from customers. Thus, everything that the analyzer now finds can be considered technical debt, which is impractical to try to eliminate immediately. It is rational to ignore these warnings for now, but write new code while performing regular analysis.

总体思路如下。 想象一下,用户已启动分析仪并收到许多警告。 由于已经开发了多年的项目仍然存在,并且仍在开发中并且仍在赚钱,因此报告中很可能不会有很多警告指出严重缺陷。 换句话说,由于更昂贵的方式或在客户反馈的帮助下,关键错误已得到修复。 因此,分析仪现在发现的所有东西都可以视为技术债务,试图立即消除是不切实际的。 现在暂时忽略这些警告是合理的,但是在执行常规分析时编写新代码。

You can tell PVS-Studio to consider all these warnings irrelevant so far (to postpone the technical debt for later), and not to show them any more. The analyzer creates a special file where it stores information about as-yet-uninteresting errors. From now on, PVS-Studio will issue warnings only for new or modified code. By the way, it’s all implemented in a very smart way. If an empty line is added at the beginning of a file, the analyzer will size up the situation as if nothing has really changed and will remain quiet. You can put the markup file in the version control system. Even though the file is large, it’s not a problem, as there’s no need to upload it very often.

您可以告诉PVS-Studio到目前为止,将所有这些警告视为无关紧要的(将技术债务推迟到以后),而不再显示它们。 分析器创建一个特殊的文件,在其中存储有关尚未引起关注的错误的信息。 从现在开始,PVS-Studio将仅针对新代码或修改后的代码发出警告。 顺便说一下,所有这些都是以非常聪明的方式实现的。 如果在文件的开头添加了空行,则分析器将调整情况的大小,好像什么都没有真正更改并且将保持安静。 您可以将标记文件放入版本控制系统中。 即使文件很大,也不是问题,因为不需要经常上传。

From this point, developers will see only warnings related to newly written or modified code. So you can start using the analyzer, as they say, from the next day. You can get back to technical debt later and gradually correct errors and tweak the analyzer.

从这一点来看,开发人员将仅看到与新编写或修改的代码有关的警告。 因此,您可以从第二天开始使用他们所说的分析仪。 您可以稍后再找技术债,逐步纠正错误并调整分析仪。

To suppress warnings for existing code in Rider, just go to the top menu in Tools — >PVS-Studio and click «Suppress All Messages».

要取消对Rider中现有代码的警告,只需转到“工具”中的顶部菜单-> PVS-Studio,然后单击“禁止所有消息”。

Picture 1

In the window that appears, which warns that all current warnings will get in the suppress list, click «Ok». A suppress file will be taken into account by the analyzer during further work. This file will be created in the project folder.

在显示的窗口中,警告所有当前警告都将显示在禁止列表中,单击“确定”。 分析仪在以后的工作中将考虑抑制文件。 该文件将在项目文件夹中创建。

It should be noted that Rider already has an analyzer that successfully highlights some errors. Thus, a number of PVS-Studio warnings indicate code that looks suspicious from the editor’s point of view. However, PVS-Studio quite often finds errors that could escape from the sharp look of the analyzer from JetBrains. This is why the most effective solution is to allow them to work as a team.

应该注意的是,Rider已经拥有一个可以成功突出显示某些错误的分析器。 因此,从编辑者的角度来看,许多PVS-Studio警告表明代码看起来可疑。 但是,PVS-Studio经常会发现可能从JetBrains的分析仪的锐利外观逃脱的错误。 这就是为什么最有效的解决方案是允许他们团队合作的原因。

Picture 10

甜点

(

For dessert

)

Now, as promised, let’s see what interesting warnings the analyzer showed based on the check results. The project contains a huge number of source code files, so it wasn’t surprising to find a lot of suspicious things in it. Nothing can be done here – everyone makes mistakes, but it is important to make every effort to detect and correct them on time. Static analysis makes this task much easier.

现在,按照承诺,让我们看看分析器根据检查结果显示了哪些有趣的警告。 该项目包含大量的源代码文件,因此在其中找到许多可疑的东西也就不足为奇了。 这里什么也做不了-每个人都会犯错,但是尽一切努力及时发现并纠正它们很重要。 静态分析使此任务更加容易。

As a result of the check, about a thousand warnings were shown:

检查的结果显示了大约一千个警告:

Picture 11

Read more about the different levels of warnings by following the

link

.

通过

链接

了解有关不同级别警告的更多信息。

Of course, not all warnings indicate super-scary errors. If this were the case, it is unlikely that anything would work in the project:). What is important to realise is that if the analyzer complains about something, then the code looks weird and is worth a thorough investigation.

当然,并非所有警告都表示超级可怕的错误。 如果是这种情况,则该项目中不太可能有任何工作:)。 重要的是要意识到,如果分析器抱怨某些事情,那么代码看起来很怪异,值得进行深入研究。

Overall, quite a lot of sapid warnings were detected in the project. However, we wouldn’t like the article to be too huge, so we will consider only some of them.

总体而言,在该项目中检测到了很多警告。 但是,我们不希望这篇文章太大,所以我们只考虑其中一些。

只是额外的检查?

(

Just an extra check?

)

public static void EnsurePathExists(string file)
{
  var dirpath = Path.GetDirectoryName(file);
  List<string> dirsToCreate = new List<string>();
  while (Directory.Exists(dirpath) == false)
  {
    dirsToCreate.Add(dirpath);
    dirpath = Directory.GetParent(dirpath).ToString();
    if (dirpath == null)                                  // <=
      break;
  }
  dirsToCreate.ForEach(x => Directory.CreateDirectory(x));
}

分析仪警告

(

Analyzer warning

)

:


V3022

Expression ‘dirpath == null’ is always false. PosixHelper.cs(124) Voron

V3022

表达式’dirpath == null’始终为false。 PosixHelper.cs(124)沃龙

This warning can be considered in different ways. On the one hand, no doubts that an extra check isn’t desirable, but it’s not an error in itself. On the other hand, it is worth considering: does this code really work the way the programmer intended?

可以以不同方式考虑此警告。 一方面,毫无疑问,不需要额外的检查,但这本身并不是错误。 另一方面,值得考虑:此代码是否真的按照程序员的预期工作?

Perhaps the developer really didn’t know that

ToString

would never return

null

. If this is not the case, then we can make an assumption about what the code author wanted to achieve.

也许开发人员真的不知道

ToString

永远不会返回

null

。 如果不是这种情况,那么我们可以对代码作者想要实现的目标进行假设。

Perhaps

break

should be called when it is not possible to get a parent for the considered directory. If this is the case, then checking for

null

makes sense. However, it’s not the result of

ToString

that we need to consider, but the value returned by the

GetParent

method:

当无法为所考虑的目录获取父目录时,也许应该调用

break

。 如果是这种情况,则检查

null

是有意义的。 但是,这不是我们需要考虑的

ToString

的结果,而是

GetParent

方法返回的值:

dirsToCreate.Add(dirpath);
var dir = Directory.GetParent(dirpath);    
if (dir == null)
  break;

dirpath = dir.ToString();

Otherwise, returning

null

by the

GetParent

method leads to the exception when calling

ToString

.

否则,在调用

ToString

时,通过

GetParent

方法返回

null

会导致异常。

典型值null

(

Typical null

)

public long ScanOldest()
{
  ....
  for (int i = 0; i < copy.Length; i++)
  {
    var item = copy[i].Value;
    if (item != null || item == InvalidLowLevelTransaction) // <=
    {
      if (val > item.Id)                                    // <=
        val = item.Id;
    }
  }
  ....
}

分析仪警告

(

Analyzer warning

)

:


V3125

The ‘item’ object was used after it was verified against null. Check lines: 249, 247. ActiveTransactions.cs(249), ActiveTransactions.cs(247) Voron

V3125

在验证了null之后使用了“ item”对象。 检查行:249、247。ActiveTransactions.cs(249),ActiveTransactions.cs(247)Voron

The code looks strange because of what happens when

item

is really

null

. Indeed, if

InvalidLowLevelTransaction

also turns out to be

null

, the condition will also be true and the attempt to get

item.Id

will result in the exception. If

InvalidLowLevelTransaction

can’t be

null

, the condition ”

item == InvalidLowLevelTransaction

” is simply redundant. This is because it is checked only when

item == null

. But if the

item

can’t be

null

, then the whole condition becomes meaningless and only adds unnecessary nesting.

由于

item

实际上为

null

时会发生什么,因此代码看起来很奇怪。 确实,如果

InvalidLowLevelTransaction

也变为

null

,则条件也将为true,并且尝试获取

item.Id

将导致异常。 如果

InvalidLowLevelTransaction

不能为

null

,则条件“

item == InvalidLowLevelTransaction

”只是多余的。 这是因为仅当

item == null

时才进行检查。 但是,如果

项目

不能为

null

,则整个条件将变得毫无意义,只会添加不必要的嵌套。

I think that the wrong logical operator may have been chosen here. If you replace “||” with “&& ” in the condition, the code immediately starts to look logical. In addition, there can be no problems in this case.

我认为此处可能选择了错误的逻辑运算符。 如果替换“ ||” 如果条件为“ &&”,则代码立即看起来很合逻辑。 另外,在这种情况下不会有问题。

Warnings of this type are typical representatives of potentially very dangerous errors detected by the analyzer. To be fair, the analyzer built into Rider also highlights this fragment as potentially dangerous.

这种警告是分析仪检测到的潜在非常危险错误的典型代表。 公平地讲,Rider内置的分析仪还强调了该片段潜在的危险。

另一张额外的支票?

(

Another extra check?

)

public void WriteObjectEnd()
{
  ....
  if (_continuationState.Count > 1)
  {
    var outerState = 
      _continuationState.Count > 0 ? _continuationState.Pop() : currentState
    ;
    if (outerState.FirstWrite == -1)
      outerState.FirstWrite = start;
    _continuationState.Push(outerState);
  }  
   ....
}

分析仪警告

(

Analyzer warning

)

:


V3022

Expression ‘_continuationState.Count > 0’ is always true. ManualBlittableJsonDocumentBuilder.cs(152) Sparrow

V3022

表达式’_continuationState.Count> 0’始终为true。 ManualBlittableJsonDocumentBuilder.cs(152)麻雀

First, the external condition checks that the number of items in the collection is greater than 1, and then on the next line, the ternary operator checks that their number is greater than 0. It seems that one of the checks should look different. Anyway, this code looks very suspicious and should be carefully studied and rewritten if necessary.

首先,外部条件检查集合中的项目数是否大于1,然后在下一行,三元运算符检查其项目数是否大于0。似乎其中一项检查看起来应该有所不同。 无论如何,此代码看起来非常可疑,应仔细研究并在必要时进行重写。

可能的NRE

(

Possible NRE

)

protected override Expression VisitIndex(IndexExpression node)
{
  if (node.Object != null)
  {
    Visit(node.Object);
  }
  else
  {
    Out(node.Indexer.DeclaringType.Name); // <=
  }
  if (node.Indexer != null)               // <=
  {
    Out(".");
    Out(node.Indexer.Name);
  }
  VisitExpressions('[', node.Arguments, ']');
  return node;
}

分析仪警告

(

Analyzer warning

)

:


V3095

The ‘node.Indexer’ object was used before it was verified against null. Check lines: 1180, 1182. ExpressionStringBuilder.cs(1180), ExpressionStringBuilder.cs(1182) Raven.Client

V3095

在对null进行验证之前,已使用’node.Indexer’对象。 检查行:1180、1182。ExpressionStringBuilder.cs(1180),ExpressionStringBuilder.cs(1182)Raven.Client

In fact, this is another place that both PVS-Studio and Rider consider suspicious. However, the wording is slightly different: the analyzer from JetBrains just highlights the

node.Indexer.DeclaringType

with the comment «Possible NullReferenceException».

实际上,这是PVS-Studio和Rider都认为可疑的另一个地方。 但是,措辞略有不同:JetBrains的分析器仅突出显示带有注释“ Possible NullReferenceException»的

node.Indexer.DeclaringType

Both checkers state that this fragment might trigger an exception. I should note that not only does the warning from PVS-Studio say that there may be an error, but it also explains the reasons for it. Small thing, but still nice.

两个检查器均声明该片段可能触发异常。 我要注意的是,PVS-Studio发出的警告不仅表明可能存在错误,而且还解释了引起错误的原因。 小东西,但仍然不错。

In fact, this doesn’t mean that there is really an error. It’s entirely possible that if

node.Object == null

, then

node.Indexer

is exactly set. However, a situation is possible when

node.Object

and

node.Indexer

both aren’t

null

. This is the only case when this warning from analyzers can be considered false. Therefore, it is worth carefully analyzing all possible options.

实际上,这并不意味着确实存在错误。 如果

node.Object == null

,则完全可能设置了

node.Indexer

。 但是,当

node.Object



node.Indexer

都不为

null

时,可能会出现这种情况。 只有在分析仪发出的该警告被认为是错误的情况下,这是唯一的情况。 因此,值得仔细分析所有可能的选择。

如果我们深入研究该怎么办?

(

What if we dig deeper?

)

private async Task LoadStartingWithInternal(....)
{
  ....
  var command = operation.CreateRequest();
  if (command != null)                       // <=
  {
    await RequestExecutor
      .ExecuteAsync(command, Context, SessionInfo, token)
      .ConfigureAwait(false)
    ;

    if (stream != null)
      Context.Write(stream, command.Result.Results.Parent);
    else
      operation.SetResult(command.Result);
  }
  ....
}

分析仪警告

(

Analyzer warning

)

:


V3022

Expression ‘command != null’ is always true. AsyncDocumentSession.Load.cs(175) Raven.Client

V3022

表达式’command!= null’始终为true。 AsyncDocumentSession.Load.cs(175)Raven.Client

The warning is issued because the

CreateRequest

method never returns

null

. In fact, just look at its code to make sure of this:

发出警告是因为

CreateRequest

方法从不返回

null

。 实际上,只需查看其代码即可确保这一点:

public GetDocumentsCommand CreateRequest()
{
  _session.IncrementRequestCount();
  if (Logger.IsInfoEnabled)
    Logger.Info(....);

  return new GetDocumentsCommand(....);
}

Generally speaking, this check isn’t such a problem. Although it may be that the method used to return null under certain conditions earlier, and now throws an exception if something happens. Who knows, it’s possible that instead of that

null

check, there should now be a try-catch.

一般来说,这种检查不是这样的问题。 尽管可能是该方法用于在某些情况下更早地返回null,但现在在发生某些情况时引发异常。 谁知道呢,现在应该有一个try-catch代替



检查。

You may have a very reasonable question: where is the exception thrown here? If they aren’t present, then we deal with an extra check, and there can be no error.

您可能有一个非常合理的问题:在这里抛出异常吗? 如果不存在,那么我们将进行额外的检查,并且不会有任何错误。

Alas, when executing the method, an exception can actually be thrown, and even twice. First in the

IncrementRequestCount

method:

executing,执行该方法时,实际上可以引发一个异常,甚至两次。 首先在

IncrementRequestCount

方法中:

public void IncrementRequestCount()
{
  if (++NumberOfRequests > MaxNumberOfRequestsPerSession)
    throw new InvalidOperationException(....);
}

After — in the

GetDocumentsCommand

constructor:

之后-在

GetDocumentsCommand

构造函数中:

public GetDocumentsCommand(string startWith, ....)
{
  _startWith = startWith ?? throw new ArgumentNullException(nameof(startWith));
  ....
}

传统复制粘贴

(

Traditional copy-paste

)

public override void WriteTo(StringBuilder writer)
{
  ....
  if (SqlConnectionStringsUpdated)
    json[nameof(SqlConnectionStringsUpdated)] = SqlConnectionStringsUpdated;

  if (ClientConfigurationUpdated)
    json[nameof(ClientConfigurationUpdated)] = ClientConfigurationUpdated;

  if (ConflictSolverConfigUpdated)
    json[nameof(ConflictSolverConfigUpdated)] = ClientConfigurationUpdated;

  if (PeriodicBackupsUpdated)
    json[nameof(PeriodicBackupsUpdated)] = PeriodicBackupsUpdated;

  if (ExternalReplicationsUpdated)
    json[nameof(ExternalReplicationsUpdated)] = ExternalReplicationsUpdated;
  ....
}

分析仪警告

(

Analyzer warning

)

:


V3127

Two similar code fragments were found. Perhaps, this is a typo. SmugglerResult.cs(256), SmugglerResult.cs(253) Raven.Client

V3127找到

两个相似的代码片段。 也许,这是一个错字。 SmugglerResult.cs(256),SmugglerResult.cs(253)Raven.Client

I highly doubt that anyone would have seen the oddity if they had looked at the code. The function consists of 14 similar conditions and all variable names end in Updated. Even when a small part of it is shown here, the error isn’t immediately visible.

我非常怀疑,如果有人看过代码,会不会有人觉得奇怪。 该函数由14个类似的条件组成,所有变量名都以Updated结尾。 即使此处仅显示一小部分,该错误也不会立即可见。

The human brain literally refuses to look for something in such code. At the same time, PVS-Studio easily detected that the assignment is most likely completely wrong:

人脑实际上拒绝在这种代码中寻找某些东西。 同时,PVS-Studio轻松检测到该分配很可能是完全错误的:

if (ClientConfigurationUpdated)
    json[nameof(ClientConfigurationUpdated)] = ClientConfigurationUpdated;

if (ConflictSolverConfigUpdated)
    json[nameof(ConflictSolverConfigUpdated)] = ClientConfigurationUpdated;

Logically, the lower line to the right of the assignment operator should have

ConflictSolverConfigUpdated

. I’m sure that without static analysis, this oddity would only be found if something serious enough broke because of it. The programmer will be able to notice that there is a problem hidden in this function, unless they know about it in advance.

逻辑上,赋值运算符右边的下一行应具有

ConflictSolverConfigUpdated

。 我敢肯定,如果没有静态分析,这种怪异只会在严重的事情因此而破裂的情况下才会发现。 除非他们事先知道,否则程序员将能够注意到该函数中隐藏了一个问题。

顽皮的“ ??”

(

Naughty “??”

)

public int Count => 
  _documentsByEntity.Count + _onBeforeStoreDocumentsByEntity?.Count ?? 0;

分析仪警告

(

Analyzer warning

)

:


V3123

Perhaps the ‘??’ operator works in a different way than it was expected. Its priority is lower than priority of other operators in its left part. InMemoryDocumentSessionOperations.cs(1952) Raven.Client

V3123

也许是“ ??” 操作员的工作方式与预期不同。 它的优先级低于左侧其他运营商的优先级。 InMemoryDocumentSessionOperations.cs(1952)Raven.Client

Of course, it’s still possible that this is not an error and this was written intentionally. Still, this fragment looks very suspicious. After all, it is logical to assume that the point of the function is not to return 0 when

_onBeforeStoreDocumentsByEntity == null.

当然,这仍然有可能不是错误,而是故意写的。 不过,这个片段看起来还是很可疑的。 毕竟,逻辑上是假设

_onBeforeStoreDocumentsByEntity == null

时函数的要点不返回0


I think there really is an error here related to operator priorities. In this case, you must add parenthesis:

我认为这里确实存在与操作员优先级有关的错误。 在这种情况下,必须添加括号:

_documentsByEntity.Count + (_onBeforeStoreDocumentsByEntity?.Count ?? 0)

On the other hand, if the above fragment was written specifically in this way, then it maybe worth pointing it out explicitly. This way the analyzer and programmers reading this code won’t have any questions:

另一方面,如果上面的片段是以这种方式专门编写的,那么可能值得明确指出。 这样,分析器和程序员阅读此代码就不会有任何问题:

(_documentsByEntity.Count + _onBeforeStoreDocumentsByEntity?.Count) ?? 0

But this is a matter of taste, of course.

但这当然是一个品味问题。

参数传递

(

Parameter passing

)

private static void UpdateEnvironmentVariableLicenseString(....)
{
  ....
  if (ValidateLicense(newLicense, rsaParameters, oldLicense) == false)
    return;
  ....
}

分析仪警告

(

Analyzer warning

)

:


V3066

Possible incorrect order of arguments passed to ‘ValidateLicense’ method: ‘newLicense’ and ‘oldLicense’. LicenseHelper.cs(177) Raven.Server

V3066

传递给“ ValidateLicense”方法的参数的可能错误顺序:“ newLicense”和“ oldLicense”。 LicenseHelper.cs(177)Raven.Server

Arguments are passed to the method in a strange order. Take a look at the declaration:

参数以奇怪的顺序传递给方法。 看一下声明:

private static bool ValidateLicense(
  License oldLicense, 
  RSAParameters rsaParameters, 
  License newLicense
)

It is very nice that PVS-Studio is able to find even such errors. This is a great example of the advantages of static analysis over dynamic analysis.

PVS-Studio甚至可以找到此类错误,这是非常高兴的。 这是静态分析优于动态分析的一个很好的例子。

Despite the above, I initially assumed that it might not matter in which order these arguments are passed. Of course, in this case, the names would not be quite correctly chosen, but what can we do? However, the internal structure of

ValidateLicense

suggests that these parameters still have different meanings. You can view the code of this function by following the

link

.

尽管如此,我最初还是假设这些参数的传递顺序无关紧要。 当然,在这种情况下,名称将不会被正确选择,但是我们该怎么办? 但是,

ValidateLicense

的内部结构表明这些参数仍然具有不同的含义。 您可以通过以下

链接

查看此功能的代码。

永不继续

(

Never continue

)

private List<CounterOperation> GetCounterOperationsFor(RavenEtlItem item)
{
  ....
  for (var i = 0; i < counters.Count; i++)
  {
    counters.GetPropertyByIndex(i, ref prop);

    if (
      GetCounterValueAndCheckIfShouldSkip(
        item.DocumentId, 
        null, 
        prop, 
        out long value, 
        out bool delete
      )
    ) continue;
    ....
  }
  ....
}

分析仪警告

(

Analyzer warning

)

:


V3022

Expression ‘GetCounterValueAndCheckIfShouldSkip(item.DocumentId, null, prop, out long value, out bool delete)’ is always false. RavenEtlDocumentTransformer.cs(362) Raven.Server

V3022

表达式’GetCounterValueAndCheckIfShouldSkip(item.DocumentId,null,prop,out long value,out bool delete)’始终为false。 RavenEtlDocumentTransformer.cs(362)Raven。服务器

You can check out the entire method by following the

link

.

您可以通过

链接

查看整个方法。

This warning indicates that the call to

continue

is not available in this loop. And if so, the fragment is really weird. But maybe it’s just a false positive? Especially since Rider doesn’t complain about this.

该警告表明

继续

调用在此循环中不可用。 如果是这样,该片段确实很奇怪。 但这也许只是假阳性? 特别是因为Rider对此并不抱怨。

Let’s look at the

GetCounterValueAndCheckIfShouldSkip method

:

让我们看一下

GetCounterValueAndCheckIfShouldSkip方法

private bool GetCounterValueAndCheckIfShouldSkip(
  LazyStringValue docId, 
  string function, 
  BlittableJsonReaderObject.PropertyDetails prop, 
  out long value, 
  out bool delete
)
{
  value = 0;

  if (prop.Value is LazyStringValue)
  {
    delete = true;
  }

  else
  {
    delete = false;
    value = CountersStorage.InternalGetCounterValue(
      prop.Value as BlittableJsonReaderObject.RawBlob, 
      docId, 
      prop.Name
    );

    if (function != null)
    {
      using (var result = BehaviorsScript.Run(
        Context, 
        Context, 
        function, 
        new object[] { docId, prop.Name }
      ))
      {
        if (result.BooleanValue != true)
          return true;
      }
    }
  }

  return false;
}

Obviously, this method can only return

true

if

function

!=

null

. In the above code it is the null pointer that is passed in place of this parameter. This means that the

continue

call is really unreachable.

显然,如果

函数

!=

null

,则此方法只能返回

true

。 在上面的代码中,是传递空指针代替此参数。 这意味着

继续

调用实际上是不可到达的。

This point can be either a harmless omission or a problem related to an error in the condition. Anyway, this fragment should be paid attention.

这一点可以是无害的遗漏,也可以是与状况错误相关的问题。 无论如何,这个片段应该引起注意。

首先尝试,然后信任

(

First try, then trust

)

public LicenseType Type
{
  get
  {
    if (ErrorMessage != null)
      return LicenseType.Invalid;

    if (Attributes == null)
      return LicenseType.None;

    if (Attributes != null &&                             // <=
        Attributes.TryGetValue("type", out object type) &&
        type is int
    )
    {
      var typeAsInt = (int)type;
      if (Enum.IsDefined(typeof(LicenseType), typeAsInt))
        return (LicenseType)typeAsInt;
    }

    return LicenseType.Community;
  }
}

分析仪警告

(

Analyzer warning

)

:


V3063

A part of conditional expression is always true if it is evaluated: Attributes != null. LicenseStatus.cs(28) Raven.Server

V3063

如果条件表达式的一部分被求值,则始终为true:属性!= null。 LicenseStatus.cs(28)Raven.Server

An extremely strange fragment. Usually, extra checks are somehow separated, whereas here the variable and null pointer matching is checked right in adjacent lines. It seems that the code probably doesn’t do what the programmer wanted.

一个极其奇怪的片段。 通常,多余的检查会以某种方式分开,而此处变量和null指针的匹配是在相邻的行中检查的。 似乎该代码可能无法满足程序员的要求。

可为空,永远不会为空

(

Nullable that is never null

)

public Task SuspendObserver()
{
  if (ServerStore.IsLeader())
  {
    var suspend = GetBoolValueQueryString("value");
    if (suspend.HasValue)                                  // <=
    {
      Server.ServerStore.Observer.Suspended = suspend.Value;
    }

    NoContentStatus();
    return Task.CompletedTask;
  }

  RedirectToLeader();

  return Task.CompletedTask;
}

分析仪警告

(

Analyzer warning

)

:


V3022

Expression ‘suspend.HasValue’ is always true. RachisAdminHandler.cs(116) Raven.Server

V3022

表达式“ suspend.HasValue”始终为true。 RachisAdminHandler.cs(116)Raven.Server

Another seemingly harmless «extra» check. Although it is not yet clear why the analyzer considers it such.

另一个看似无害的“额外”检查。 尽管目前尚不清楚分析仪为何如此认为。

Let’s turn to

GetBoolValueQueryString

:

让我们转向

GetBoolValueQueryString

protected bool? GetBoolValueQueryString(string name, bool required = true)
{
  var boolAsString = GetStringQueryString(name, required);
  if (boolAsString == null)
    return null;

  if (bool.TryParse(boolAsString, out bool result) == false)
    ThrowInvalidBoolean(name, boolAsString);

  return result;
}

Indeed, sometimes this function returns

null

. Moreover, Rider didn’t consider that check unnecessary. Did the unicorn really fail us?

确实,有时此函数返回

null

。 而且,Rider认为该检查没有必要。 独角兽真的让我们失望了吗?

Picture 15

What if we look at the

GetStringQueryString

method?

如果我们查看

GetStringQueryString

方法怎么办?

protected string GetStringQueryString(string name, bool required = true)
{
  var val = HttpContext.Request.Query[name];
  if (val.Count == 0 || string.IsNullOrWhiteSpace(val[0]))
  {
    if (required)
      ThrowRequiredMember(name);

    return null;
  }

  return val[0];
}

Hm, if

required

==

true

, the

ThrowRequiredMember

method will be called. I wonder what it’s doing? 🙂 Well, let me cite this to dispel all doubts:

嗯,如果

需要

==

true

,将调用

ThrowRequiredMember

方法。 我想知道它在做什么吗? :)好吧,让我引用一下以消除所有疑问:

private static void ThrowRequiredMember(string name)
{
  throw new ArgumentException(
    $"Query string {name} is mandatory, but wasn't specified."
  );
}

So, let’s sum up. The developer calls the

GetBoolValueQueryString

method. He probably believes that the method potentially won’t get the required value. As a result, it returns

null

. Inside,

GetStringQueryString

is called. If problems occur, it will either return null or throw an exception. The second occurs if the

required

parameter is set to

true

. However, this is its default value. At the same time, when calling

GetBoolValueQueryString

, it is not passed, if you look at the code above.

因此,让我们总结一下。 开发人员调用

GetBoolValueQueryString

方法。 他可能认为该方法可能无法获得所需的值。 结果,它返回

null

。 在内部,调用

GetStringQueryString

。 如果出现问题,它将返回null或引发异常。 如果

必需

参数设置为

true,

则会发生第二次。 但是,这是其默认值。 同时,在调用

GetBoolValueQueryString

时,如果您查看上面的代码,则不会传递它。

Let’s look again at the code of the

SuspendObserver

method, which triggered the analyzer:

让我们再次看一下

SuspendObserver

方法的代码,该方法触发了分析器:

public Task SuspendObserver()
{
  if (ServerStore.IsLeader())
  {
    var suspend = GetBoolValueQueryString("value");
    if (suspend.HasValue)
    {
      Server.ServerStore.Observer.Suspended = suspend.Value;
    }

    NoContentStatus();
    return Task.CompletedTask;
  }

  RedirectToLeader();

  return Task.CompletedTask;
}

It seems that the execution thread shouldn’t be interrupted here if

GetBoolValueQueryString

couldn’t get the value. In fact, checking for

null

is followed by various actions and the returned value. I think that these actions are performed

似乎如果

GetBoolValueQueryString

无法获取值,则不应在此处中断执行线程。 实际上,检查



值之后是各种操作和返回值。 我认为这些动作已经执行

独立地

(

independently

)

of


GetBoolValueQueryString

method progress. What will actually happen? The execution thread will be interrupted by an exception.

GetBoolValueQueryString

方法的进度。 实际会发生什么? 执行线程将被异常中断。

To correct this thing, when calling

GetBoolValueQueryString

, one has to pass the

false

value as the second parameter

required

. This way everything will really work as expected.

要更正此问题,在调用

GetBoolValueQueryString时

,必须将

false

值作为第二个

必需

参数传递。 这样,一切都会真正按预期进行。

As I said earlier, sometimes it seems that the analyzer is wrong (truth be told, it happens). Also, quite often, the warning looks insignificant. It would seem that there is an extra check, but it’s okay. You can even remove it and have no problems – the warning will disappear!

正如我之前说的,有时似乎分析仪是错误的(说实话,它确实发生了)。 同样,警告看起来并不重要。 似乎还有额外的检查,但是还可以。 您甚至可以删除它,没有任何问题-警告将消失!

Even in cases where the warning seems strange and incomprehensible, don’t hastily mark it as false. You should try to understand why the analyzer considers the place problematic and then make a decision.

即使在警告看起来奇怪且难以理解的情况下,也不要草率地将其标记为错误。 您应该尝试了解分析仪为什么认为位置有问题,然后再做出决定。

陌生的东西

(

Stranger things

)

private async Task<int> WriteDocumentsJsonAsync(...., int numberOfResults) // <=
{
  using (
    var writer = new AsyncBlittableJsonTextWriter(
      context, 
      ResponseBodyStream(), 
      Database.DatabaseShutdown
    )
  )
  {
    writer.WriteStartObject();
    writer.WritePropertyName(nameof(GetDocumentsResult.Results));
    numberOfResults = await writer.WriteDocumentsAsync(                    // <=
      context, 
      documentsToWrite, 
      metadataOnly
    );

    ....
  }
  return numberOfResults;
}

分析仪警告

(

Analyzer warning

)

:


V3061

Parameter ‘numberOfResults’ is always rewritten in method body before being used. DocumentHandler.cs(273), DocumentHandler.cs(267) Raven.Server

V3061

参数’numberOfResults’始终在使用前在方法主体中重写。 DocumentHandler.cs(273),DocumentHandler.cs(267)Raven.Server

The parameter passed to the function isn’t used, but is immediately overwritten. Why is it needed here? Did the authors want to pass via ref?

传递给函数的参数未使用,但立即被覆盖。 为什么在这里需要它? 作者是否想通过ref通过?

I was curious to see how this method is used in existing code. I hoped that since it was private, there shouldn’t be too many of them. Thanks to Rider, I easily found where the call is made. It was the only place:

我很好奇如何在现有代码中使用此方法。 我希望因为它是私人的,所以不应有太多。 多亏了Rider,我很容易找到了拨打电话的地方。 那是唯一的地方:

private async Task GetDocumentsByIdAsync(....)
{
  ....            
  int numberOfResults = 0;

  numberOfResults = await WriteDocumentsJsonAsync(
    context, 
    metadataOnly, 
    documents, 
    includes, 
    includeCounters?.Results, 
    numberOfResults
  );

  ....
}

The variable is assigned 0, then it is passed to the method, the result of which is assigned to it. And this parameter isn’t used inside the method in any way. Hm. Why is it all needed?

该变量被分配为0,然后将其传递给方法,并为其分配结果。 而且该参数在方法内部完全没有使用。 嗯 为什么都需要它?

Picture 6

逻辑运算符错误

(

Wrong logical operator

)

private OrderByField ExtractOrderByFromMethod(....)
{
  ....
  if (me.Arguments.Count < 2 && me.Arguments.Count > 3)
    throw new InvalidQueryException(....);
  ....
}

分析仪警告

(

Analyzer warning

)

:


V3022

Expression ‘me.Arguments.Count < 2 && me.Arguments.Count > 3’ is always false. Probably the ‘||’ operator should be used here. QueryMetadata.cs(861) Raven.Server

V3022

表达式“ me.Arguments.Count <2 && me.Arguments.Count> 3”始终为false。 可能是“ ||” 这里应该使用运算符。 QueryMetadata.cs(861)Raven。服务器

You can view the full method

here

.

您可以在

此处

查看完整方法。

This time we deal with an obvious error — using of incorrect logical operator. In the current form, checking the number of arguments simply doesn’t work, because there is no value that is both less than 2 and more than 3. The developer’s true intentions are easily revealed by the first argument passed to the exception constructor:

这次我们处理一个明显的错误-使用错误的逻辑运算符。 在当前形式中,检查参数的数量根本行不通,因为没有一个值既小于2又大于3。通过传递给异常构造函数的第一个参数,很容易揭示开发人员的真实意图:

"Invalid ORDER BY 'spatial.distance(from, to, roundFactor)' call, 
expected 2-3 arguments, got " + me.Arguments.Count

In order for the check to work correctly, you just need to replace “&&” with “||”.

为了使检查正常进行,您只需将“ &&”替换为“ ||”。

奇怪的尝试方法

(

Strange try method

)

private bool Operator(OperatorField fieldOption, out QueryExpression op)
{ 
  ....
  switch (match)
  {
    ....
    case "(":
      var isMethod = Method(field, out var method); // <=
      op = method;

      if (isMethod && Operator(OperatorField.Optional, out var methodOperator))
      {
        ....
      }

      return isMethod;
    ....
  }
}

分析仪警告

(

Analyzer warning

)

:


V3063

A part of conditional expression is always true if it is evaluated: isMethod. QueryParser.cs(1797) Raven.Server

V3063

如果条件表达式的一部分被求值,则始终为true:isMethod。 QueryParser.cs(1797)Raven.Server

You can view the full method

here

.

您可以在

此处

查看完整方法。

The

var isMethod = Method(field, out var method)

construction reminded me of standard methods like

Int.TryParse

. These methods attempt to get the result and write it to an out variable, and the operation success flag is the return value. Code that uses such functions usually checks the return value and then performs certain operations based on it.


var isMethod = Method(field,out var method)的

构造使我想起了

Int.TryParse

之类的标准方法。 这些方法尝试获取结果并将其写入out变量,并且操作成功标志是返回值。 使用此类函数的代码通常会检查返回值,然后根据返回值执行某些操作。

In my opinion, the

Method

function is used here in this way. The result of

Method

is also the return value of the

Operator

method calling it.

我认为,

Method

函数是通过这种方式使用的。

Method

的结果也是调用它的

Operator

方法的返回值。

According to the analyzer, the

isMethod

variable will always have the

true

value and its checking in the condition is pointless. This means that the

Method

function never returns

false

. Then what is the point of using such a construction?

根据分析器,

isMethod

变量将始终具有

真实

值,并且在条件下对其进行检查是毫无意义的。 这意味着

Method

函数从不返回

false

。 那么使用这样的构造有什么意义呢?

First, let’s make sure that the analyzer is not mistaken:

首先,让我们确保分析仪没有弄错:

private bool Method(FieldExpression field, out MethodExpression op)
{
  var args = ReadMethodArguments();

  op = new MethodExpression(field.FieldValue, args);
  return true;
}

Indeed, the return value of this method is always

true

. And if that’s what it was meant to be, this it is… strange, but on the whole not a big deal. But what if it’s not so?

实际上,此方法的返回值始终为

true

。 如果这就是它的原意,那就是……很奇怪,但总的来说没什么大不了的。 但是,如果不是这样呢?

The

ReadMethodArguments

function throws exceptions in some cases. You can view its code

here

. This happens when the method can’t perform its task correctly.

在某些情况下,

ReadMethodArguments

函数会引发异常。 您可以

在此处

查看其代码。 当方法无法正确执行其任务时,就会发生这种情况。

It seems that the code calling the

Method

function isn’t meant to throw exceptions. Most likely, it is expected that when the value of the

out

variable fails to be obtained correctly, the

Method

function will return

false

. However, the current implementation results in an exception instead.

看来,调用

Method

函数的代码并非旨在引发异常。 很有可能,当

out

变量的值无法正确获取时,

Method

函数将返回

false

。 但是,当前的实现会导致异常。

Whatever the case, authors should reconsider this fragment.

无论如何,作者都应该重新考虑这个片段。

空!=空吗?

(

null != null?

)

private Address GetNextEdge()
{
  if (m_curEdgeBlock == null || m_curEdgeBlock.Count <= m_curEdgeIdx)
  {
    m_curEdgeBlock = null;
    if (m_edgeBlocks.Count == 0)
    {
      throw new ApplicationException(
        "Error not enough edge data.  Giving up on heap dump."
      );
    }

    var nextEdgeBlock = m_edgeBlocks.Dequeue();
    if (
      m_curEdgeBlock != null &&                       // <=
      nextEdgeBlock.Index != m_curEdgeBlock.Index + 1
    )
    {
      throw new ApplicationException(
        "Error expected Node Index " + (m_curEdgeBlock.Index + 1) + 
        " Got " + nextEdgeBlock.Index + " Giving up on heap dump."
      );
    }

    m_curEdgeBlock = nextEdgeBlock;
    m_curEdgeIdx = 0;
  }
  return m_curEdgeBlock.Values(m_curEdgeIdx++).Target;
}

分析仪警告

(

Analyzer warning

)

:


V3063

A part of conditional expression is always false if it is evaluated: m_curEdgeBlock != null. DotNetHeapDumpGraphReader.cs(803) Raven.Debug

V3063

如果条件表达式的一部分被求值,则始终为false:m_curEdgeBlock!= null。 DotNetHeapDumpGraphReader.cs(803)Raven.Debug

The variable is assigned a null pointer, and then a few lines after it is checked for

null

. In doing so, the code checking

nextEdgeBlock.Index != m_curEdgeBlock.Index + 1

becomes of no use. In addition, an exception will never be thrown.

该变量被分配了一个空指针,然后在检查了几行后再返回

null

。 这样,检查

nextEdgeBlock.Index!= m_curEdgeBlock.Index +1

的代码就没有用了。 另外,永远不会抛出异常。

It stands to reason, that something is not working as it should, because the fragment looks very weird. Either the check is not needed at all, or it is implemented incorrectly.

可以合理地认为,某些内容无法正常工作,因为该片段看起来很奇怪。 根本不需要检查,或者执行不正确。

On the other hand, we can regard the warning by a reversal of logic. Let’s try to imagine the case where this warning is false. I think this is only possible if the value of the variable can be changed when calling

Deque

. However,

m_curEdgeBlock

is a private field, and

m_edgeBlocks

is a standard queue that is initialized in the same class. Thus, it is highly doubtful that calling

Dequeue

can affect the value of

m_curEdgeBlock

in any way. Therefore, the warning is most likely not false.

另一方面,我们可以通过逆转逻辑来看待警告。 让我们尝试想象这种警告为假的情况。 我认为这只有在调用

Deque

时可以更改变量的值的情况下才有可能。 但是,

m_curEdgeBlock

是私有字段,而

m_edgeBlocks

是在同一类中初始化的标准队列。 因此,非常令人怀疑的是,调用




队是否会

以任何方式影响

m_curEdgeBlock

的值。 因此,警告很可能不是错误的。

第一个或空

(

First or null

)

public HashSet<string> FindSpecialColumns(string tableSchema, string tableName)
{
  var mainSchema = GetTable(tableSchema, tableName);

  var result = new HashSet<string>();
  mainSchema.PrimaryKeyColumns.ForEach(x => result.Add(x)); // <=

  foreach (var fkCandidate in Tables)
    foreach (var tableReference in fkCandidate.References.Where(
        x => x.Table == tableName && x.Schema == tableSchema
      )
    )
    {
      tableReference.Columns.ForEach(x => result.Add(x));
    }

  return result;
}

分析仪警告

(

Analyzer warning

)

:


V3146

Possible null dereference of ‘mainSchema’. The ‘Tables.FirstOrDefault’ can return default null value. DatabaseSchema.cs(31) Raven.Server

V3146

可能对’mainSchema’的null取消引用。 “ Tables.FirstOrDefault”可以返回默认的空值。 DatabaseSchema.cs(31)Raven.Server

At first glance, the warning might seem obscure. Indeed, what does

FirstOrDefault

have to do with it? To make it clear why the analyzer triggers, we need to look at the

GetTable

function:

乍一看,警告似乎模糊不清。 确实,

FirstOrDefault

与它有什么关系? 为了弄清楚分析器触发的原因,我们需要查看

GetTable

函数:

public TableSchema GetTable(string schema, string tableName)
{
  return Tables.FirstOrDefault(
    x => x.Schema == schema && x.TableName == tableName
  );
}

Calling the

FirstOrDefault

method instead of

First

may be due to the fact that the collection may not have elements that match the specified condition. In this case,

FirstOrDefault

, and therefore

GetTable

, will return

null

, since

TableSchema

is a reference type. This is why PVS-Studio says that an attempt to dereference a null pointer may occur in this code.

调用

FirstOrDefault

方法而不是

First

可能是由于该集合可能没有与指定条件匹配的元素。 在这种情况下,由于

TableSchema

是引用类型,因此

FirstOrDefault



GetTable

将返回

null

。 这就是为什么PVS-Studio说在此代码中可能会尝试取消引用空指针的原因。

It may still be worth checking such a case so that execution isn’t interrupted with a

NullReferenceException

. If the scenario where

Tables.FirstOrDefault

returns

null

is not possible, then there is no point in using

FirstOrDefault

instead of

First

.

仍然值得检查这种情况,以免执行被

NullReferenceException

中断。 如果无法实现

Tables.FirstOrDefault

返回

null

的方案,那么使用

FirstOrDefault

代替

First

毫无意义。

永远为真

(

Always true

)

public override void VerifyCanExecuteCommand(
  ServerStore store, TransactionOperationContext context, bool isClusterAdmin
)
{
  using (context.OpenReadTransaction())
  {
    var read = store.Cluster.GetCertificateByThumbprint(context, Name);
    if (read == null)
      return;

    var definition = JsonDeserializationServer.CertificateDefinition(read);
    if (
      definition.SecurityClearance != SecurityClearance.ClusterAdmin || // <=
      definition.SecurityClearance != SecurityClearance.ClusterNode     // <=
    )
      return;
  }

  AssertClusterAdmin(isClusterAdmin);
}

分析仪警告

(

Analyzer warning

)

:


V3022

Expression is always true. Probably the ‘&&’ operator should be used here. DeleteCertificateFromClusterCommand.cs(21) Raven.Server

V3022

表达式始终为true。 可能应在此处使用“ &&”运算符。 DeleteCertificateFromClusterCommand.cs(21)Raven.Server

Another example of a situation where almost certainly the wrong logical operator was chosen. In this case, the condition is always true, because the variable isn’t exactly equal to at least one of the values that it is compared with.

几乎可以肯定选择了错误的逻辑运算符的情况的另一个示例。 在这种情况下,条件始终为true,因为该变量不完全等于与其比较的值中的至少一个。

I suppose that “||” should be replaced with “&&”. Then the above fragment will make sense. If the logical operator is chosen correctly, it is most likely that other variables should be compared in one of the conditions. Anyway, this fragment looks very fishily and it must be analyzed.

我想“ ||” 应替换为“ &&”。 然后上面的片段将是有意义的。 如果正确选择了逻辑运算符,则很可能应该在其中一种条件下比较其他变量。 无论如何,这个片段看起来非常混乱,必须对其进行分析。

结论

(

Conclusion

)

First of all, I’d like to thank everyone who got to this place. This article is quite long, but I hope you were interested in working with me on the new version of the PVS-Studio analyzer and studying the errors found.

首先,我要感谢来到这个地方的每个人。 本文很长,但是我希望您有兴趣与我一起使用新版本的PVS-Studio分析仪并研究发现的错误。

It is important to remember that the main goal of a developer shouldn’t be to reduce the number of warnings. You don’t need to use PVS-Studio to achieve an empty error log. Dealing with the warnings is all the same as struggling with symptoms of a disease, which affects the source code.

重要的是要记住,开发人员的主要目标不应是减少警告数量。 您无需使用PVS-Studio即可获取空的错误日志。 处理警告与处理疾病症状一样,这会影响源代码。

When reviewing analyzer messages, you should always try to understand why a particular warning is issued. Only when you understand the logic behind the analyzer’s warning you can draw conclusions whether it indicates an error or not. It is in this case that you will struggle not with the symptom, but with the disease. And this is how your code will become cleaner and healthier. Eventually, there will be less problems with such great source code. Although I’d rather wish you didn’t have any at all 🙂

查看分析器消息时,应始终尝试理解为什么会发出特定警告。 仅当您了解分析仪警告背后的逻辑时,您才能得出结论,无论它是否指示错误。 在这种情况下,您将不会因症状而挣扎,而会因疾病而挣扎。 这就是您的代码将变得更加干净和健康的方式。 最终,如此出色的源代码将带来更少的问题。 虽然我希望您一点都没有:)

Picture 16

翻译自:

https://habr.com/en/company/pvs-studio/blog/507102/

macos c#