
向所有批评他人代码的粉丝致以问候。 :)今天,在我们的实验室中,新的研究材料是.NET的AWS开发工具包项目的源代码。 一次,我们写了一篇关于检查适用于C ++的AWS开发工具包的文章。 然后,没有什么特别有趣的。 让我们看看AWS SDK的.NET版本如何取悦我们。 这是再次展示PVS-Studio分析仪功能以及使世界变得更加完美的好机会。
亚马逊网络服务(AWS).NET SDK是一个开发人员工具包,旨在在AWS基础设施中创建基于.NET的应用程序,并大大简化了编写代码的过程。 该SDK包含适用于各种AWS服务的.NET API套件,例如Amazon S3,Amazon EC2,DynamoDB等。 SDK的源代码托管在
GitHub上 。
正如我所说,有一次我们撰写
了一篇有关检查适用于C ++的AWS开发工具包
的文章 。 文章的篇幅很小-在512万行代码中仅发现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个错误),而且表明PVS-Studio分析仪的C#工作质量与C ++相当。 好结果!
适用于.NET的AWS开发工具包的作者很棒。 从一个项目到另一个项目,它们展示了惊人的代码质量。 这可以作为其他团队的榜样。 但是,如果我没有插入5个戈比,我当然不会成为静态分析器的开发人员。 :)我们已经与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 ,而不管
if条件
(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方法中检测到错误:检查字符串是否包含字符串本身。 从方法的描述中可以得出结论,如果(在
_attributes字典中)找到了属性名称(
attributeName键),则应返回属性值,否则返回
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代替
字典 ,这样更方便。 然后,对
锁的需求就消失了。 虽然,也许我不知道该项目的任何功能。
可疑行为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语句中),以某种方式检查了
namedIndexName变量。 根据检查结果,变量
inferredIndexName会收到一个新值。 现在让我们注意第三个
if语句 。 如果
indexNames.Count> 0 ,则将执行该语句的主体(引发异常),因为条件的第一部分是
string.IsNullOrEmpty( inferredIndexName )始终为true。 也许对变量
namedIndexName和
inferredIndexName只是简单地混淆了。 或第三次检查应该没有
其他内容 ,代表独立的
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方法的使用。 他参与填充
fsm_handler_table处理程序数组以及其他类似方法(从
State1到
State28分别有28个其名称)。 在此必须注意,除了
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 。 因此,
if(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万行代码中的错误很少。尽管可能更全面地分析发出的警告,使我可以在此列表中添加更多错误。但是我也很可能白白地将所描述的某些警告归因于错误。在这种情况下,只有在检查代码上下文中的开发人员始终得出明确的结论。
如果您想与说英语的读者分享这篇文章,请使用以下链接:Sergey Khrenov。在.NET的Amazon Web Services SDK源代码中搜索错误