我們不斷地讓 PVS-Studio 的使用變得更方便。 我們的分析器現在可以在 Chocolatey(Windows 的套件管理器)中使用。 我們相信這將促進PVS-Studio的部署,特別是在雲端服務。 為了不走太遠,讓我們檢查一下同一個 Chocolatey 的源代碼。 Azure DevOps 將充當 CI 系統。
以下是我們關於與雲端系統整合主題的其他文章的清單:
PVS-Studio 走向雲端:Azure DevOps PVS-Studio 走向雲端:Travis CI PVS-Studio 走向雲端:CircleCI PVS-Studio 走向雲端:GitLab CI/CD
我建議您注意第一篇關於與 Azure DevOps 整合的文章,因為在這種情況下省略了一些要點,以免重複。
那麼,本文的主角:
關於使用 Chocolatey
您可以在此處查看如何安裝套件管理器本身
安裝最新版本分析器的命令:
choco install pvs-studio
安裝特定版本的 PVS-Studio 軟體包的命令:
choco install pvs-studio --version=7.05.35617.2075
預設情況下,僅安裝分析器的核心,即 Core 元件。 所有其他標誌(Standalone、JavaCore、IDEA、MSVS2010、MSVS2012、MSVS2013、MSVS2015、MSVS2017、MSVS2019)都可以使用 --package-parameters 傳遞。
將使用 Visual Studio 2019 外掛程式安裝分析器的命令範例:
choco install pvs-studio --package-parameters="'/MSVS2019'"
現在讓我們來看看在Azure DevOps下方便使用分析器的範例。
調整
讓我提醒您,有一個單獨的部分介紹註冊帳戶、建立建置管道以及將您的帳戶與 GitHub 儲存庫中的專案同步等問題。
首先,讓我們設定一個啟動觸發器,表示我們僅針對以下內容的變更啟動 主 分支:
trigger:
- master
接下來我們需要選擇一個虛擬機器。 目前,它將是 Microsoft 託管的 Windows Server 2019 和 Visual Studio 2019 代理程式:
pool:
vmImage: 'windows-latest'
讓我們繼續討論設定檔的主體(區塊 步驟)。 儘管你不能在虛擬機器中安裝任意軟體,但我沒有添加 Docker 容器。 我們可以新增 Chocolatey 作為 Azure DevOps 的擴充。 為此,讓我們轉到
在這裡您需要選擇我們將新增擴充功能的位置,然後按一下按鈕 安裝.
安裝成功後,點選 繼續組織:
現在您可以在視窗中看到 Chocolatey 任務的模板 任務 編輯設定檔時 天藍色管道.yml:
點擊 Chocolatey 並查看字段列表:
這裡我們需要選擇 安裝 與球隊一起在球場上。 在 Nuspec 檔案名稱 指示所需包的名稱 - pvs-studio。 如果不指定版本,則會安裝最新版本,這完全適合我們。 讓我們按下按鈕 添加 我們將在設定檔中看到生成的任務。
steps:
- task: ChocolateyCommand@0
inputs:
command: 'install'
installPackageId: 'pvs-studio'
接下來,讓我們繼續討論文件的主要部分:
- task: CmdLine@2
inputs:
script:
現在我們需要使用分析器許可證來建立一個文件。 這裡 PVS名稱 и PVSKEY – 我們在設定中指定其值的變數名稱。 他們將儲存 PVS-Studio 登入名稱和許可證密鑰。 要設定它們的值,請打開選單 變數->新變數。 讓我們創建變數 PVS名稱 用於登入和 PVSKEY 為分析儀鍵。 不要忘記選中該框 將此值保密 為 PVSKEY。 命令代碼:
сall "C:Program Files (x86)PVS-StudioPVS-Studio_Cmd.exe" credentials
–u $(PVSNAME) –n $(PVSKEY)
讓我們使用儲存庫中的 bat 檔案建置專案:
сall build.bat
讓我們建立一個資料夾,用於儲存包含分析器結果的檔案:
сall mkdir PVSTestResults
讓我們開始分析該項目:
сall "C:Program Files (x86)PVS-StudioPVS-Studio_Cmd.exe"
–t .srcchocolatey.sln –o .PVSTestResultsChoco.plog
我們使用 PlogСonverter 實用程式將報表轉換為 html 格式:
сall "C:Program Files (x86)PVS-StudioPlogConverter.exe"
–t html –o PVSTestResults .PVSTestResultsChoco.plog
現在您需要建立一個任務以便上傳報告。
- task: PublishBuildArtifacts@1
inputs:
pathToPublish: PVSTestResults
artifactName: PVSTestResults
condition: always()
完整的設定檔如下所示:
trigger:
- master
pool:
vmImage: 'windows-latest'
steps:
- task: ChocolateyCommand@0
inputs:
command: 'install'
installPackageId: 'pvs-studio'
- task: CmdLine@2
inputs:
script: |
call "C:Program Files (x86)PVS-StudioPVS-Studio_Cmd.exe"
credentials –u $(PVSNAME) –n $(PVSKEY)
call build.bat
call mkdir PVSTestResults
call "C:Program Files (x86)PVS-StudioPVS-Studio_Cmd.exe"
–t .srcchocolatey.sln –o .PVSTestResultsChoco.plog
call "C:Program Files (x86)PVS-StudioPlogConverter.exe"
–t html –o .PVSTestResults .PVSTestResultsChoco.plog
- task: PublishBuildArtifacts@1
inputs:
pathToPublish: PVSTestResults
artifactName: PVSTestResults
condition: always()
我們點擊一下 保存->保存->運行 運行任務。 讓我們轉到任務標籤來下載報告。
Chocolatey 專案僅包含 37615 行 C# 程式碼。 讓我們看看發現的一些錯誤。
檢測結果
警告 N1
分析儀警告:
public abstract class CrytpoHashProviderSpecsBase : TinySpec
{
....
protected CryptoHashProvider Provider;
....
public override void Context()
{
Provider = Provider = new CryptoHashProvider(FileSystem.Object);
}
}
分析器偵測到變數對其自身的賦值,這是沒有意義的。 最有可能的是,應該用其他變數來取代其中一個變數。 好吧,或者這是一個拼字錯誤,並且可以簡單地刪除額外的分配。
警告 N2
分析儀警告:
public static PlatformType get_platform()
{
switch (Environment.OSVersion.Platform)
{
case PlatformID.MacOSX:
{
....
}
case PlatformID.Unix:
if(file_system.directory_exists("/Applications")
& file_system.directory_exists("/System")
& file_system.directory_exists("/Users")
& file_system.directory_exists("/Volumes"))
{
return PlatformType.Mac;
}
else
return PlatformType.Linux;
default:
return PlatformType.Windows;
}
}
營運商差異 & 來自營運商 && 如果表達式的左邊是 假,那麼右側仍然會被計算,在這種情況下這意味著不必要的方法調用 系統目錄存在.
在所考慮的片段中,這是一個小缺陷。 是的,可以透過將 & 運算子替換為 && 運算子來優化此條件,但從實際角度來看,這不會產生任何影響。 但是,在其他情況下,當表達式的右側使用不正確/無效的值時,& 和 && 之間的混淆可能會導致嚴重的問題。 例如,在我們的錯誤集合中,
if ((k < nct) & (s[k] != 0.0))
即使指數 k 不正確,它將用於存取數組元素。 結果會拋出例外 索引超出範圍異常.
警告 N3、N4
分析儀警告:
分析儀警告:
public static string
prompt_for_confirmation(.... bool shortPrompt = false, ....)
{
....
if (shortPrompt)
{
var choicePrompt = choice.is_equal_to(defaultChoice) //1
?
shortPrompt //2
?
"[[{0}]{1}]".format_with(choice.Substring(0, 1).ToUpperInvariant(), //3
choice.Substring(1,choice.Length - 1))
:
"[{0}]".format_with(choice.ToUpperInvariant()) //0
:
shortPrompt //4
?
"[{0}]{1}".format_with(choice.Substring(0,1).ToUpperInvariant(), //5
choice.Substring(1,choice.Length - 1))
:
choice; //0
....
}
....
}
在這種情況下,三元運算子的運算背後有一個奇怪的邏輯。 讓我們仔細看看:如果滿足我用數字 1 標記的條件,那麼我們將繼續執行條件 2,該條件始終是 真,這意味著第 3 行將被執行。如果條件 1 結果為假,那麼我們將轉到標有數字 4 的行,其中的條件也始終為 真,這意味著將執行第5行。因此,註解0標記的條件永遠不會被滿足,這可能不完全是程式設計師期望的操作邏輯。
警告 N5
分析儀警告:
private static string GetArgumentName (...., string description)
{
string[] nameStart;
if (maxIndex == 1)
{
nameStart = new string[]{"{0:", "{"};
}
else
{
nameStart = new string[]{"{" + index + ":"};
}
for (int i = 0; i < nameStart.Length; ++i)
{
int start, j = 0;
do
{
start = description.IndexOf (nameStart [i], j);
}
while (start >= 0 && j != 0 ? description [j++ - 1] == '{' : false);
....
return maxIndex == 1 ? "VALUE" : "VALUE" + (index + 1);
}
}
診斷適用於此線路:
while (start >= 0 && j != 0 ? description [j++ - 1] == '{' : false)
由於變數 j 上面幾行初始化為零,三元運算子將傳回值 假。 由於這種情況,循環體將僅執行一次。 在我看來,這段程式碼根本沒有按照程式設計師的意圖運作。
警告 N6
分析儀警告:
private void remove_nuget_cache_for_package(....)
{
if (!config.AllVersions && installedPackageVersions.Count > 1)
{
const string allVersionsChoice = "All versions";
if (installedPackageVersions.Count != 1)
{
choices.Add(allVersionsChoice);
}
....
}
....
}
這裡有一個奇怪的嵌套條件: 安裝的PackageVersions.Count != 1這將永遠是 真。 通常,此類警告表示程式碼中存在邏輯錯誤,而在其他情況下,它僅表示冗餘檢查。
警告 N7
分析儀警告:
public static bool arguments_contain_sensitive_information(string
commandArguments)
{
return commandArguments.contains("-install-arguments-sensitive")
|| commandArguments.contains("-package-parameters-sensitive")
|| commandArguments.contains("apikey ")
|| commandArguments.contains("config ")
|| commandArguments.contains("push ")
|| commandArguments.contains("-p ")
|| commandArguments.contains("-p=")
|| commandArguments.contains("-password")
|| commandArguments.contains("-cp ")
|| commandArguments.contains("-cp=")
|| commandArguments.contains("-certpassword")
|| commandArguments.contains("-k ")
|| commandArguments.contains("-k=")
|| commandArguments.contains("-key ")
|| commandArguments.contains("-key=")
|| commandArguments.contains("-apikey")
|| commandArguments.contains("-api-key")
|| commandArguments.contains("-apikey")
|| commandArguments.contains("-api-key");
}
編寫這段程式碼的程式設計師複製並貼上了最後兩行,但忘記編輯它們。 因此,Chocolatey 使用者無法套用該參數 apikey 還有幾種方法。 與上面的參數類似,我可以提供以下選項:
commandArguments.contains("-apikey=");
commandArguments.contains("-api-key=");
在任何具有大量原始程式碼的專案中,複製貼上錯誤遲早都會出現,而解決這些錯誤的最佳工具之一就是靜態分析。
PS 與往常一樣,此錯誤往往出現在多行條件的末尾:)。 參見出版品“
警告 N8
分析儀警告:
public virtual ConcurrentDictionary<string, PackageResult> get_outdated(....)
{
....
var pinnedPackageResult = outdatedPackages.GetOrAdd(
packageName,
new PackageResult(installedPackage,
_fileSystem.combine_paths(
ApplicationParameters.PackagesLocation,
installedPackage.Id)));
....
if ( installedPackage != null
&& !string.IsNullOrWhiteSpace(installedPackage.Version.SpecialVersion)
&& !config.UpgradeCommand.ExcludePrerelease)
{
....
}
....
}
經典錯誤:物件優先 已安裝的套件 使用然後檢查 空。 此診斷告訴我們程序中的兩個問題之一: 已安裝的套件 永遠不平等 空,這是值得懷疑的,然後檢查是多餘的,或者我們可能會在程式碼中遇到嚴重錯誤 - 嘗試存取空引用。
結論
所以我們又邁出了一小步 - 現在使用 PVS-Studio 變得更加容易和方便。 我還想說,Chocolatey 是一個很好的套件管理器,程式碼中的錯誤很少,使用 PVS-Studio 時錯誤可能會更少。
我們邀請您
聚苯乙烯
在發表之前,我們將這篇文章發送給了 Chocolatey 開發人員,他們收到了很好的回饋。 我們沒有發現任何關鍵的東西,但例如,他們喜歡我們發現的與「api-key」金鑰相關的錯誤。
如果您想與英語讀者分享這篇文章,請使用翻譯連結:Vladislav Stolyarov。
來源: www.habr.com