
欢迎所有破坏别人代码的粉丝。 :)今天,在我们的实验室中,我们有了一种新的研究材料-适用于.NET项目的AWS开发工具包的源代码。 当时,我们写了一篇有关检查适用于C ++的AWS开发工具包的文章。 然后没有什么特别有趣的。 让我们看看AWS SDK版本的.NET值得什么。 再次,这是一个绝佳的机会来展示PVS-Studio分析仪的功能,并使世界变得更好。
适用于.NET的Amazon Web Services(AWS)SDK是一组开发人员工具,用于在AWS基础设施中基于.NET创建应用程序。 该集合可以显着简化代码编写过程。 该SDK包括针对各种AWS服务(例如Amazon S3,Amazon EC2,DynamoDB等)的API .NET集。 SDK源代码可在
GitHub上获得 。
正如我提到的,当时我们已经撰写了有关检查适用于C ++的AWS开发工具包的
文章 。 这篇文章很小,每512万行代码中只发现了几个错误。 这次,我们正在处理更大的代码,其中包括约3.4万个cs文件,并且代码行的总数(不包括空白行)令人印象深刻的500万。 一小部分代码(664-cs文件中的20万行)属于测试,我没有考虑它们。
如果SDK版本的.NET代码的质量与C ++的质量大致相同(每512 KLOC出现两个错误),那么我们应该得到的错误数量大约是原来的10倍。 当然,这是一种非常不准确的计算方法,它没有考虑到语言的特殊性和许多其他因素,但是我认为读者现在不想陷入无聊的推理之中。 相反,我建议继续研究结果。
使用PVS-Studio 6.27进行检查。 真是不可思议,但事实仍然是,在适用于.NET的AWS开发工具包中,分析器设法检测到40个错误,这值得一谈。 它不仅演示了高质量的SDK代码(每512 KLOC大约有4个错误),而且还演示了与C ++相比,C#PVS-Studio分析仪的质量。 很好的结果!
适用于.NET的AWS开发工具包的作者,您真是冠军! 在每个项目中,您都将展示出色的代码质量。 对于其他团队来说,这可能是一个很好的例子。 但是,如果我不给我2美分,我当然不会成为静态分析仪的开发商。 :)我们已经与Amazon的Lumberyard团队合作使用PVS-Studio。 由于它是一家非常庞大的公司,在全球拥有许多分支机构,因此.NET的AWS开发工具包团队很可能从未听说过PVS-Studio。 无论如何,我没有发现在SDK代码中使用分析器的任何迹象,尽管它没有说明任何问题。 但是,至少,团队
使用 Visual Studio中内置的分析器。 很棒,但是代码评论总是可以
增强的 :)。
结果,我确实设法在SDK代码中发现了一些错误,最后是时候分享它们了。
逻辑错误PVS-Studio警告: V3008 [CWE-563]连续两次为变量“ this.linker.s3.region”分配值。 也许这是一个错误。 检查行:116,114。AWSSDK.DynamoDBv2.Net45 S3Link.cs 116
public string Region { get { .... } set { if (String.IsNullOrEmpty(value)) { this.linker.s3.region = "us-east-1"; } this.linker.s3.region = value; } }
分析仪会警告您将重复的值分配给同一变量。 从代码中可以清楚地看出,这是由于违反程序工作逻辑的错误引起的:无论条件如何,变量
this.linker.s3.region的值将始终等于变量
value的值 。
如果(String.IsNullOrEmpty(value)) 。
if块的正文中缺少
return语句。 该代码需要固定如下:
public string Region { get { .... } set { if (String.IsNullOrEmpty(value)) { this.linker.s3.region = "us-east-1"; return; } this.linker.s3.region = value; } }
无限递归PVS-Studio警告: V3110 [CWE-674]'OnFailure'属性内可能无限递归。 AWSSDK.ElasticMapReduce.Net45 ResizeJobFlowStep.cs 171
OnFailure? onFailure = null; public OnFailure? OnFailure { get { return this.OnFailure; }
错字的经典示例,它导致
OnFailure属性的
get访问器中的无限递归。 代替返回私有字段
onFailure的值,而是访问属性
OnFailure 。 正确的变体:
public OnFailure? OnFailure { get { return this.onFailure; } set { this.onFailure = value; } }
您可能会问:“它是如何工作的?” 到目前为止-没有如何。 该属性未在其他任何地方使用,但这是临时的。 某个时候,有人会开始使用它,并且肯定会收到意想不到的结果。 为了防止出现此类错别字,建议不要使用仅在首字母不同的情况下使用的标识符。
此构造的另一个注释是使用标识符,该标识符与
OnFailure类型的名称完全匹配。 从编译器的角度来看,这是完全可以接受的,但是这会使人对代码的理解变得复杂。
另一个类似的错误:
PVS-Studio警告: V3110 [CWE-674]'SSES3'属性内可能存在无限递归。 AWSSDK.S3.Net45 InventoryEncryption.cs 37
private SSES3 sSES3; public SSES3 SSES3 { get { return this.SSES3; } set { this.SSES3 = value; } }
情况与上述相同。 但是,在同时访问属性
SSES3进行读取和分配时,将发生无限递归。 正确的变体:
public SSES3 SSES3 { get { return this.sSES3; } set { this.sSES3 = value; } }
考虑考虑现在,我想引用一个使用复制粘贴方法执行的开发人员的任务。 查看代码在Visual Studio编辑器中的外观,然后尝试查找错误。

PVS-Studio警告: V3029彼此并排的'if'语句的条件表达式相同。 检查行:91,95。AWSSDK.AppSync.Net45 CreateApiKeyResponseUnmarshaller.cs 91
我减少了方法
UnmarshallException的主体,删除了不需要的所有内容。 现在您可以看到相同的检查相互遵循:
public override AmazonServiceException UnmarshallException(....) { .... if (errorResponse.Code != null && errorResponse.Code.Equals("LimitExceededException")) { return new LimitExceededException(errorResponse.Message, innerException, errorResponse.Type, errorResponse.Code, errorResponse.RequestId, statusCode); } if (errorResponse.Code != null && errorResponse.Code.Equals("LimitExceededException")) { return new LimitExceededException(errorResponse.Message, innerException, errorResponse.Type, errorResponse.Code, errorResponse.RequestId, statusCode); } .... }
该错误似乎并不严重-只是一个额外的检查。 但是,当不执行所需的检查时,这种模式通常可能表示代码中存在更严重的问题。
在代码中,有几个类似的错误。
PVS-Studio警告:- V3029彼此并排放置的'if'语句的条件表达式相同。 检查行:75、79。AWSSDK.CloudDirectory.Net45 CreateSchemaResponseUnmarshaller.cs 75
- V3029彼此并排放置的'if'语句的条件表达式相同。 检查行:105、109。AWSSDK.CloudDirectory.Net45 GetSchemaAsJsonResponseUnmarshaller.cs 105
- V3029彼此并排放置的'if'语句的条件表达式相同。 检查行:201、205。AWSSDK.CodeCommit.Net45 PostCommentForPullRequestResponseUnmarshaller.cs 201
- V3029彼此并排放置的'if'语句的条件表达式相同。 检查行:101,105。AWSSDK.CognitoIdentityProvider.Net45 VerifySoftwareTokenResponseUnmarshaller.cs 101
- V3029彼此并排放置的'if'语句的条件表达式相同。 检查行:72,76。AWSSDK.Glue.Net45 UpdateConnectionResponseUnmarshaller.cs 72
- V3029彼此并排放置的'if'语句的条件表达式相同。 检查行:123、127。AWSSDK.Neptune.Net45 RestoreDBClusterFromSnapshotResponseUnmarshaller.cs 123
- V3029彼此并排放置的'if'语句的条件表达式相同。 检查行:167、171。AWSSDK.Neptune.Net45 RestoreDBClusterFromSnapshotResponseUnmarshaller.cs 167
- V3029彼此并排放置的'if'语句的条件表达式相同。 检查行:127,131。AWSSDK.RDS.Net45 RestoreDBClusterFromSnapshotResponseUnmarshaller.cs 127
- V3029彼此并排放置的'if'语句的条件表达式相同。 检查行:171、175。AWSSDK.RDS.Net45 RestoreDBClusterFromSnapshotResponseUnmarshaller.cs 171
- V3029彼此并排放置的'if'语句的条件表达式相同。 检查行:99、103。AWSSDK.Rekognition.Net45 RecognizeCelebritiesResponseUnmarshaller.cs 99
怎么了PVS-Studio警告: V3062对象“ attributeName”用作其自身方法的参数。 考虑检查“包含”方法的第一个实际参数。 AWSSDK.MobileAnalytics.Net45 CustomEvent.cs 261
分析器在
GetAttribute方法中检测到错误:检查字符串是否包含自身。 从方法的描述可以得出以下结论:如果找到了属性名称(
attributeName键)(在字典
_attributes中 ),则应返回属性值,否则返回
-null 。 实际上,由于条件
attributeName.Contains(attributeName)始终为true,因此尝试通过可能在字典中找不到的键返回值。 然后,将引发
KeyNotFoundException异常,而不是返回
null 。
让我们尝试修复此代码。 为了更好地了解如何执行此操作,应查看另一种方法:
此方法检查字典
_attributes中是否存在属性名称(
attributeName键)。 让我们再次回到
GetAttribute方法并修复错误:
public string GetAttribute(string attributeName) { if(string.IsNullOrEmpty(attributeName)) { throw new ArgumentNullException("attributeName"); } string ret = null; lock(_lock) { if(_attributes.ContainsKey(attributeName)) ret = _attributes[attributeName]; } return ret; }
现在,该方法完全可以执行描述中所述的操作。
此代码段还有一个小注释。 我注意到作者在使用
_attributes字典时使用
锁 。 显然,在具有多线程访问时,这是必需的,但
锁的构造相当缓慢且麻烦。 在这种情况下,使用字典的线程安全版本
-ConcurrentDictionary可能会比使用
Dictionary更方便。 这样,就无需
锁定。 虽然,也许我不了解该项目的细节。
可疑行为PVS-Studio警告: V3063 [CWE-571]如果对条件表达式进行评估,则该条件表达式的一部分始终为true:string.IsNullOrEmpty(inferredIndexName)。 AWSSDK.DynamoDBv2.PCL ContextInternal.cs 802
private static string GetQueryIndexName(....) { .... string inferredIndexName = null; if (string.IsNullOrEmpty(specifiedIndexName) && indexNames.Count == 1) { inferredIndexName = indexNames[0]; } else if (indexNames.Contains(specifiedIndexName, StringComparer.Ordinal)) { inferredIndexName = specifiedIndexName; } else if (string.IsNullOrEmpty(inferredIndexName) &&
分析器担心检查
字符串.IsNullOrEmpty(inferredIndexName) 。 确实,为字符串
inferredIndexName分配了
null ,然后此变量的值未在任何地方更改,然后由于某种原因检查了
null还是空字符串。 看起来可疑。 让我们仔细看一下上面的代码片段。 我故意没有减少它以更好地了解情况。 因此,在第一个
if语句中(以及在下一个
if语句中)将以某种方式检查
指定的变量indexIndexName。 根据检查结果,变量
inferredIndexName将获得新值。 现在让我们看一下第三条
if语句。 如果
indexNames.Count> 0(作为整个条件的第一部分
)为
string.IsNullOrEmpty(inferredIndexName)始终为true,则将执行此语句的主体(
引发异常)。 也许,
指定的变量
IndexName和
inferredIndexName混合在一起,或者第三次检查必须没有
else ,代表一个独立的
if语句:
if (string.IsNullOrEmpty(specifiedIndexName) && indexNames.Count == 1) { inferredIndexName = indexNames[0]; } else if (indexNames.Contains(specifiedIndexName, StringComparer.Ordinal)) { inferredIndexName = specifiedIndexName; } if (string.IsNullOrEmpty(inferredIndexName) && indexNames.Count > 0) throw new InvalidOperationException(....);
在这种情况下,很难给出修复此代码的选项的明确答案。 无论如何,作者需要检查一下。
NullReferenceExceptionPVS-Studio警告: V3095 [CWE-476]在对空值进行验证之前使用了“ conditionValues”对象。 检查行:228、238。AWSSDK.Core.Net45 JsonPolicyWriter.cs 228
private static void writeConditions(....) { .... foreach (....) { IList<string> conditionValues = keyEntry.Value; if (conditionValues.Count == 0)
这是经典。 使用变量
conditionValues时无需预先检查
null 。 稍后在代码中执行此检查。 该代码需要按以下方式更正:
private static void writeConditions(....) { .... foreach (....) { IList<string> conditionValues = keyEntry.Value; if (conditionValues != null && conditionValues.Count == 0) continue; .... if (conditionValues != null && conditionValues.Count != 0) { .... } .... } }
我在代码中发现了几个类似的错误。
PVS-Studio警告:- V3095 [CWE-476]在验证是否为null之前使用了“ ts.Listeners”对象。 检查行:140,143。AWSSDK.Core.Net45 Logger.Diagnostic.cs 140
- V3095 [CWE-476]在验证是否为null之前,已使用'obj'对象。 检查行:743、745。AWSSDK.Core.Net45 JsonMapper.cs 743
- V3095 [CWE-476]在对null进行验证之前,已使用'multipartUploadMultipartUploadpartsList'对象。 检查行:65,67。AWSSDK.S3.Net45 CompleteMultipartUploadRequestMarshaller.cs 65
以下警告在含义上非常相似,但情况与上述警告相反。
PVS-Studio警告: V3125 [CWE-476]在验证为空后使用了“状态”对象。 检查行:139、127。AWSSDK.Core.Net45 RefreshingAWSCredentials.cs 139
private void UpdateToGeneratedCredentials( CredentialsRefreshState state) { string errorMessage; if (ShouldUpdate) { .... if (state == null) errorMessage = "Unable to generate temporary credentials"; else .... throw new AmazonClientException(errorMessage); } state.Expiration -= PreemptExpiryTime;
代码片段之一包括检查
状态变量的值是否为
null 。 在下面的代码中,该变量用于退订
PreemptExpiryTime事件,但是,不再执行对
null的检查,并且可能
引发异常
NullReferenceException 。 该代码的更安全版本:
private void UpdateToGeneratedCredentials( CredentialsRefreshState state) { string errorMessage; if (ShouldUpdate) { .... if (state == null) errorMessage = "Unable to generate temporary credentials"; else .... throw new AmazonClientException(errorMessage); } if (state != null) state.Expiration -= PreemptExpiryTime; .... }
在代码中,还有其他类似的错误:
PVS-Studio警告:- V3125 [CWE-476]在针对null验证了'wrappedRequest.Content'对象之后,使用了该对象。 检查行:395、383。AWSSDK.Core.Net45 HttpHandler.cs 395
- V3125 [CWE-476]在对null进行验证之后,使用了“ datasetUpdates”对象。 检查行:477、437。AWSSDK.CognitoSync.Net45 Dataset.cs 477
- V3125 [CWE-476]在对null进行验证之后,使用了'cORSConfigurationCORSConfigurationcORSRulesListValue'对象。 检查行:125、111。AWSSDK.S3.Net45 PutCORSConfigurationRequestMarshaller.cs 125
- V3125 [CWE-476]在对null进行验证之后,使用了'lifecycleConfigurationLifecycleConfigurationrulesListValue'对象。 检查行:157,68。AWSSDK.S3.Net45 PutLifecycleConfigurationRequestMarshaller.cs 157
- V3125 [CWE-476]在验证了null之后使用了“ this.Key”对象。 检查行:199、183。AWSSDK.S3.Net45 S3PostUploadRequest.cs 199
非替代现实PVS-Studio警告: V3009 [CWE-393]奇怪的是,此方法始终返回一个相同的'true'值。 AWSSDK.Core.Net45 Lexer.cs 651
private static bool State19 (....) { while (....) { switch (....) { case '"': .... return true; case '\\': .... return true; default: .... continue; } } return true; }
该方法始终返回
true 。 让我们看看它对于调用代码有多重要。 我检查了使用
State19方法的情况。 它涉及用其他类似的方法(分别有28个名称分别从
State1到
State28的名称)填充处理程序
fsm_handler_table的数组。 在此重要的是要注意,除了
State19之外 ,对于某些其他处理程序,也发出了警告
V3009 [CWE-393]。 这些是处理程序:
State23,State26,State27,State28 。 分析仪为其发出的警告:
- V3009 [CWE-393]奇怪的是,此方法始终返回一个相同的“ true”值。 AWSSDK.Core.Net45 Lexer.cs 752
- V3009 [CWE-393]奇怪的是,此方法始终返回一个相同的“ true”值。 AWSSDK.Core.Net45 Lexer.cs 810
- V3009 [CWE-393]奇怪的是,此方法始终返回一个相同的“ true”值。 AWSSDK.Core.Net45 Lexer.cs 822
- V3009 [CWE-393]奇怪的是,此方法始终返回一个相同的“ true”值。 AWSSDK.Core.Net45 Lexer.cs 834
这是处理程序的声明和数组初始化的样子:
private static StateHandler[] fsm_handler_table; .... private static void PopulateFsmTables () { fsm_handler_table = new StateHandler[28] { State1, State2, .... State19, .... State23, .... State26, State27, State28 };
为了使图片更完整,让我们看分析器没有声明任何声明的处理程序之一的代码,例如
State2 :
private static bool State2 (....) { .... if (....) { return true; } switch (....) { .... default: return false; } }
这是处理程序调用发生的方式:
public bool NextToken () { .... while (true) { handler = fsm_handler_table[state - 1]; if (! handler (fsm_context))
如我们所见,如果返回
false ,将引发异常。 在我们的案例中,对于处理程序
State19,State23,State26,State27和
State28,这将永远不会发生。 看起来可疑。 另一方面,五个处理程序具有相似的行为(将始终返回
true ),因此它可能是人为设计的,而不是拼写错误的结果。
为什么我要深入了解所有这一切? 从静态分析仪通常只能指示可疑结构的意义上讲,这种情况非常重要。 即使是一个对项目没有足够了解的人(不是机器),即使花了一些时间学习代码,也仍然无法给出关于错误存在的完整答案。 开发人员应查看此代码。
无意义的检查PVS-Studio警告: V3022 [CWE-571]表达式'doLog'始终为true。 AWSSDK.Core.Net45 StoredProfileAWSCredentials.cs 235
private static bool ValidCredentialsExistInSharedFile(....) { .... var doLog = false; try { if (....) { return true; } else { doLog = true; } } catch (InvalidDataException) { doLog = true; } if (doLog)
注意
doLog变量。 在使用
false值初始化之后,此变量将在代码的所有情况下均获得
true值。 因此,检查
(doLog)是否始终为true。 也许在方法的早期有一个分支,没有为
doLog变量分配任何值。 在检查时,它可能包含初始化时收到的
错误值。 但是现在没有这样的分支。
另一个类似的错误:
PVS-Studio警告: V3022表达式“!结果”始终为false。 353 AWSSDK.CognitoSync.PCL SQLiteLocalStorage.cs
public void PutValue(....) { .... bool result = PutValueHelper(....); if (!result) <= { _logger.DebugFormat("{0}", @"Cognito Sync - SQLiteStorage - Put Value Failed"); } else { UpdateLastModifiedTimestamp(....); } .... }
分析器声称
结果变量的值始终为true。 只有在方法
PutValueHelper始终返回
true的情况下
才有可能。 看一下这个方法:
private bool PutValueHelper(....) { .... if (....)) { return true; } if (record == null) { .... return true; } else { .... return true; } }
实际上,该方法在所有情况下都将返回
true 。 此外,分析仪已对此方法发出警告。
PVS-Studio警告: V3009 [CWE-393]奇怪的是,此方法始终返回一个相同的'true'值。 SQLiteLocalStorage.cs 1016
我在查询其他错误
V3009并将其保存为这种情况时,故意不引用此警告。 因此,该工具正确指出了调用代码中的错误
V3022 。
复制粘贴。 再来一次PVS-Studio警告: V3001在'||'的左侧和右侧有相同的子表达式'this.token == JsonToken.String' 操作员。 AWSSDK.Core.Net45 JsonReader.cs 343
public bool Read() { .... if ( (this.token == JsonToken.ObjectEnd || this.token == JsonToken.ArrayEnd || this.token == JsonToken.String ||
将字段
this.token与枚举
JsonToken的值
JsonToken.String进行两次比较。 可能其中一个比较应包含另一个枚举值。 如果是这样,那么这里就犯了一个严重的错误。
重构+注意力不集中?PVS-Studio警告: V3025 [CWE-685]格式错误。 调用“格式”功能时,期望格式项目的数量不同。 未使用的参数:AWSConfigs.AWSRegionKey。 AWSSDK.Core.Net45 AWSRegion.cs 116
public InstanceProfileAWSRegion() { .... if (region == null) { throw new InvalidOperationException( string.Format(CultureInfo.InvariantCulture, "EC2 instance metadata was not available or did not contain region information.", AWSConfigs.AWSRegionKey)); } .... }
也许
string.Format方法的格式字符串先前包含格式项
{0},为此设置了参数
AWSConfigs.AWSRegionKey 。 然后更改了字符串,格式项消失了,但是开发人员忘了删除参数。 给定的代码示例可以正常工作(错误是在相反的情况下抛出的-没有参数的格式项),但是看起来并不好。 该代码应更正如下:
if (region == null) { throw new InvalidOperationException( "EC2 instance metadata was not available or did not contain region information."); }
不安全PVS-Studio警告: V3083 [CWE-367]事件'mOnSyncSuccess'的不安全调用,可能会发生NullReferenceException。 请考虑在调用事件之前将事件分配给局部变量。 AWSSDK.CognitoSync.PCL Dataset.cs 827
protected void FireSyncSuccessEvent(List<Record> records) { if (mOnSyncSuccess != null) { mOnSyncSuccess(this, new SyncSuccessEventArgs(records)); } }
事件处理程序不安全调用的常见情况。 用户可以在检查
mOnSyncSuccess变量
是否为
空和调用处理程序之间退订,因此其值将变为
null 。 发生这种情况的可能性很小,但是使代码更安全仍然更好:
protected void FireSyncSuccessEvent(List<Record> records) { mOnSyncSuccess?.Invoke(this, new SyncSuccessEventArgs(records)); }
在代码中,还有其他类似的错误:
PVS-Studio警告:- V3083 [CWE-367]事件'mOnSyncFailure'的不安全调用,可能会导致NullReferenceException。 请考虑在调用事件之前将事件分配给局部变量。 AWSSDK.CognitoSync.PCL Dataset.cs 839
- V3083 [CWE-367]事件的不安全调用,可能是NullReferenceException。 请考虑在调用事件之前将事件分配给局部变量。 AWSSDK.Core.PCL AmazonServiceClient.cs 332
- V3083 [CWE-367]事件的不安全调用,可能是NullReferenceException。 请考虑在调用事件之前将事件分配给局部变量。 AWSSDK.Core.PCL AmazonServiceClient.cs 344
- V3083 [CWE-367]事件的不安全调用,可能是NullReferenceException。 请考虑在调用事件之前将事件分配给局部变量。 AWSSDK.Core.PCL AmazonServiceClient.cs 357
- V3083 [CWE-367]事件'mExceptionEvent'的不安全调用,可能为NullReferenceException。 请考虑在调用事件之前将事件分配给局部变量。 366AWSSDK.Core.PCL AmazonServiceClient.cs
- V3083 [CWE-367]事件的不安全调用,可能是NullReferenceException。 请考虑在调用事件之前将事件分配给局部变量。 AWSSDK.Core.PCL AmazonWebServiceRequest.cs 78
- V3083 [CWE-367]事件'OnRead'的不安全调用,可能会发生NullReferenceException。 请考虑在调用事件之前将事件分配给局部变量。 AWSSDK.Core.PCL EventStream.cs 97
- V3083 [CWE-367]事件的不安全调用,可能是NullReferenceException。 请考虑在调用事件之前将事件分配给局部变量。 AWSSDK.Core.Android NetworkReachability.cs 57
- V3083 [CWE-367]事件的不安全调用,可能是NullReferenceException。 请考虑在调用事件之前将事件分配给局部变量。 AWSSDK.Core.Android NetworkReachability.cs 94
- V3083 [CWE-367]事件的不安全调用,可能是NullReferenceException。 请考虑在调用事件之前将事件分配给局部变量。 AWSSDK.Core.iOS NetworkReachability.cs 54
粗级PVS-Studio警告:实现IEquatable <T>接口的
V3126类型“ JsonData”不会覆盖“ GetHashCode”方法。 AWSSDK.Core.Net45 JsonData.cs 26
public class JsonData : IJsonWrapper, IEquatable<JsonData> { .... }
JsonData类包含很多代码,因此我没有给出完整的内容,只是引用了它的声明。 此类确实不包含被覆盖的方法
GetHashCode,这是不安全的,因为在使用
JsonData类型(例如,与集合一起使用)时,它可能导致错误的行为。 目前可能没有问题,但是将来这种策略可能会改变。 该错误在
文档中有更详细的描述。
结论这些都是有趣的错误,我可以使用PVS-Studio静态分析器在适用于.NET的AWS开发工具包的代码中检测到。 我想再次强调项目的质量。 我发现500万行代码中的错误很少。 尽管对发出的警告进行更彻底的分析可能会使我在此列表中添加更多错误。 不过,我很有可能在错误中添加了一些警告。 在这种情况下,只有在检查代码上下文中的开发人员才能得出明确的结论。