{"id":411040,"date":"2024-06-29T21:44:16","date_gmt":"2024-06-29T21:44:16","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=411040"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=411040","title":{"rendered":"<span>PVS-Studio: analyzing pull requests in Azure DevOps using self-hosted agents<\/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-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/91\/vt\/l-\/91vtl--az32_rl5mz2ljtji4ifg.png\" data-src=\"https:\/\/habrastorage.org\/webt\/91\/vt\/l-\/91vtl--az32_rl5mz2ljtji4ifg.png\"\/><\/p>\n<p>  Static code analysis is most effective when changing a project, as errors are always more difficult to fix in the future than at an early stage. We continue expanding the options for using PVS-Studio in continuous development systems. This time, we&#8217;ll show you how to configure pull request analysis using self-hosted agents in Microsoft Azure DevOps, using the example of the Minetest game.<br \/>  <a name=\"habracut\"><\/a>  <\/p>\n<h2>Briefly about what we are dealing with<\/h2>\n<p>  <a href=\"https:\/\/www.minetest.net\/\">Minetest<\/a> is an open-source cross-platform game engine containing about 200,000 lines of code in C, C++, and Lua. It allows you to create different game modes in voxel space. Supports multiplayer, and a lot of mods from community. The project repository is located here:<a href=\"https:\/\/github.com\/minetest\/minetest\"> https:\/\/github.com\/minetest\/minetest<\/a>.<\/p>\n<p>  The following tools are used to configure regular error detection:<\/p>\n<p>  <a href=\"https:\/\/www.viva64.com\/en\/pvs-studio\/\">PVS-Studio<\/a> is a static code analyzer of the code written in C, C++, C#, and Java to search for errors and security defects.<\/p>\n<p>  <a href=\"https:\/\/azure.microsoft.com\/\">Azure DevOps<\/a> is a cloud platform that allows you to develop, run applications, and store data on remote servers.<\/p>\n<p>  You can use Windows and Linux agent VMs to perform development tasks in Azure. However, running agents on the local equipment has several important advantages:<\/p>\n<ul>\n<li>The local host may have more resources than an Azure VM;<\/li>\n<li>The agent doesn&#8217;t &#171;disappear&#187; after completing its task;<\/li>\n<li>Ability to directly configure the environment and more flexible management of build processes;<\/li>\n<li>Local storage of intermediate files has a positive effect on build speed;<\/li>\n<li>You can complete more than 30 tasks per month for free.<\/li>\n<\/ul>\n<p>  <\/p>\n<h2>Preparation to using a self-hosted agent<\/h2>\n<p>  The process of getting started with Azure is described in detail in the article &#171;<a href=\"https:\/\/www.viva64.com\/en\/b\/0670\/\">PVS-Studio in the Clouds: Azure DevOps<\/a>&#171;, so I will go straight to creating a self-hosted agent.<\/p>\n<p>  In order for agents to be able to connect to project pools, they need a special Access Token. You can get it on the &#171;Personal Access Tokens&#187; page, in the &#171;User settings&#187; menu.<\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/fc7\/218\/49c\/fc721849c234f441facd08950f35b25b.png\" alt=\"image2.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/fc7\/218\/49c\/fc721849c234f441facd08950f35b25b.png\"\/><\/div>\n<p>  After clicking on &#171;New token&#187;, you must specify a name and select Read &amp; manage Agent Pools (you may need to expand the full list via &#171;Show all scopes&#187;).<\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/2f2\/9fe\/0a0\/2f29fe0a0aa124db77ad092a141dd41d.png\" alt=\"image3.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/2f2\/9fe\/0a0\/2f29fe0a0aa124db77ad092a141dd41d.png\"\/><\/div>\n<p>  You need to copy the token, because Azure will not show it again, and you will have to make a new one.<\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/cac\/f1a\/54e\/cacf1a54e69c9c4f8a1e1a45548b6965.png\" alt=\"image4.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/cac\/f1a\/54e\/cacf1a54e69c9c4f8a1e1a45548b6965.png\"\/><\/div>\n<p>  A Docker container based on Windows Server Core will be used as the agent. The host is my desktop computer on Windows 10 x64 with Hyper-V.<\/p>\n<p>  First, you will need to expand the amount of disk space available to Docker containers.<\/p>\n<p>  To do this, in Windows, you need to modify the file &#8216;C:\\ProgramData\\Docker\\config\\daemon.json&#8217; as follows:<\/p>\n<pre><code class=\"cpp\">{   \"registry-mirrors\": [],   \"insecure-registries\": [],   \"debug\": true,   \"experimental\": false,   \"data-root\": \"d:\\\\docker\",   \"storage-opts\": [ \"size=40G\" ] }<\/code><\/pre>\n<p>  To create a Docker image for agents with the build system and everything necessary, let&#8217;s add a Docker file with the following content in the directory &#8216;D:\\docker-agent&#8217;:<\/p>\n<pre><code class=\"cpp\"># escape=`  FROM mcr.microsoft.com\/dotnet\/framework\/runtime  SHELL [\"cmd\", \"\/S\", \"\/C\"]  ADD https:\/\/aka.ms\/vs\/16\/release\/vs_buildtools.exe C:\\vs_buildtools.exe RUN C:\\vs_buildtools.exe --quiet --wait --norestart --nocache `   --installPath C:\\BuildTools `   --add Microsoft.VisualStudio.Workload.VCTools `   --includeRecommended  RUN powershell.exe -Command `   Set-ExecutionPolicy Bypass -Scope Process -Force; `   [System.Net.ServicePointManager]::SecurityProtocol =     [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; `   iex ((New-Object System.Net.WebClient)     .DownloadString('https:\/\/chocolatey.org\/install.ps1')); `   choco feature enable -n=useRememberedArgumentsForUpgrades;    RUN powershell.exe -Command `   choco install -y cmake --installargs '\"ADD_CMAKE_TO_PATH=System\"'; `   choco install -y git --params '\"\/GitOnlyOnPath \/NoShellIntegration\"'  RUN powershell.exe -Command `   git clone https:\/\/github.com\/microsoft\/vcpkg.git; `   .\\vcpkg\\bootstrap-vcpkg -disableMetrics; `   $env:Path += '\";C:\\vcpkg\"'; `   [Environment]::SetEnvironmentVariable(     '\"Path\"', $env:Path, [System.EnvironmentVariableTarget]::Machine); `   [Environment]::SetEnvironmentVariable(     '\"VCPKG_DEFAULT_TRIPLET\"', '\"x64-windows\"',   [System.EnvironmentVariableTarget]::Machine)  RUN powershell.exe -Command `   choco install -y pvs-studio; `   $env:Path += '\";C:\\Program Files (x86)\\PVS-Studio\"'; `   [Environment]::SetEnvironmentVariable(     '\"Path\"', $env:Path, [System.EnvironmentVariableTarget]::Machine)  RUN powershell.exe -Command `   $latest_agent =     Invoke-RestMethod -Uri \"https:\/\/api.github.com\/repos\/Microsoft\/                           azure-pipelines-agent\/releases\/latest\"; `   $latest_agent_version =     $latest_agent.name.Substring(1, $latest_agent.tag_name.Length-1); `   $latest_agent_url =     '\"https:\/\/vstsagentpackage.azureedge.net\/agent\/\"' + $latest_agent_version +   '\"\/vsts-agent-win-x64-\"' + $latest_agent_version + '\".zip\"'; `   Invoke-WebRequest -Uri $latest_agent_url -Method Get -OutFile .\/agent.zip; `   Expand-Archive -Path .\/agent.zip -DestinationPath .\/agent  USER ContainerAdministrator RUN reg add hklm\\system\\currentcontrolset\\services\\cexecsvc         \/v ProcessShutdownTimeoutSeconds \/t REG_DWORD \/d 60   RUN reg add hklm\\system\\currentcontrolset\\control         \/v WaitToKillServiceTimeout \/t REG_SZ \/d 60000 \/f  ADD .\\entrypoint.ps1 C:\\entrypoint.ps1 SHELL [\"powershell\", \"-Command\",        \"$ErrorActionPreference = 'Stop';      $ProgressPreference = 'SilentlyContinue';\"] ENTRYPOINT .\\entrypoint.ps1<\/code><\/pre>\n<p>  The result is a build system based on MSBuild for C++, with Chocolatey for installing PVS-Studio, CMake, and Git. Vcpkg is built for convenient management of the libraries that the project depends on. Also, we have to download the latest version of the Azure Pipelines Agent.<\/p>\n<p>  To initialize the agent from the ENTRYPOINT Docker file, the PowerShell script &#8216;entrypoint.ps1&#8217; is called, to which you need to add the URL of the project&#8217;s &#171;organization&#187;, the token of the agent pool, and the PVS-Studio license parameters:<\/p>\n<pre><code class=\"cpp\">$organization_url = \"https:\/\/dev.azure.com\/&lt;Microsoft Azure account>\" $agents_token = \"&lt;agent token>\"  $pvs_studio_user = \"&lt;PVS-Studio user name>\" $pvs_studio_key = \"&lt;PVS-Studio key>\"  try {   C:\\BuildTools\\VC\\Auxiliary\\Build\\vcvars64.bat    PVS-Studio_Cmd credentials -u $pvs_studio_user -n $pvs_studio_key      .\\agent\\config.cmd --unattended `     --url $organization_url `     --auth PAT `     --token $agents_token `     --replace;   .\\agent\\run.cmd }  finally {   # Agent graceful shutdown   # https:\/\/github.com\/moby\/moby\/issues\/25982      .\\agent\\config.cmd remove --unattended `     --auth PAT `     --token $agents_token }<\/code><\/pre>\n<p>  Commands for building an image and starting the agent:<\/p>\n<pre><code class=\"cpp\">docker build -t azure-agent -m 4GB . docker run -id --name my-agent -m 4GB --cpu-count 4 azure-agent<\/code><\/pre>\n<p>  <\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/de7\/755\/ec9\/de7755ec9a5de6cb1d73c09d3d24db3e.png\" alt=\"image5.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/de7\/755\/ec9\/de7755ec9a5de6cb1d73c09d3d24db3e.png\"\/><\/div>\n<p>  The agent is running and ready to perform tasks.<\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/874\/745\/287\/874745287a32a5ffc1aca8fdd477f544.png\" alt=\"image6.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/874\/745\/287\/874745287a32a5ffc1aca8fdd477f544.png\"\/><\/div>\n<p>  <\/p>\n<h2>Running analysis on a self-hosted agent<\/h2>\n<p>  For PR analysis, a new pipeline is created with the following script:<\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/034\/ce7\/2cc\/034ce72cc09aec54dfb5fb7b3286de83.png\" alt=\"image7.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/034\/ce7\/2cc\/034ce72cc09aec54dfb5fb7b3286de83.png\"\/><\/div>\n<p>  <\/p>\n<pre><code class=\"cpp\">trigger: none  pr:   branches:     include:     - '*'  pool: Default  steps: - script: git diff --name-only     origin\/%SYSTEM_PULLREQUEST_TARGETBRANCH% >     diff-files.txt   displayName: 'Get committed files'  - script: |     cd C:\\vcpkg     git pull --rebase origin     CMD \/C \".\\bootstrap-vcpkg -disableMetrics\"     vcpkg install ^     irrlicht zlib curl[winssl] openal-soft libvorbis ^     libogg sqlite3 freetype luajit     vcpkg upgrade --no-dry-run   displayName: 'Manage dependencies (Vcpkg)'  - task: CMake@1   inputs:     cmakeArgs: -A x64       -DCMAKE_TOOLCHAIN_FILE=C:\/vcpkg\/scripts\/buildsystems\/vcpkg.cmake       -DCMAKE_BUILD_TYPE=Release -DENABLE_GETTEXT=0 -DENABLE_CURSES=0 ..   displayName: 'Run CMake'  - task: MSBuild@1   inputs:     solution: '**\/*.sln'     msbuildArchitecture: 'x64'     platform: 'x64'     configuration: 'Release'     maximumCpuCount: true   displayName: 'Build'  - script: |     IF EXIST .\\PVSTestResults RMDIR \/Q\/S .\\PVSTestResults     md .\\PVSTestResults     PVS-Studio_Cmd ^     -t .\\build\\minetest.sln ^     -S minetest ^     -o .\\PVSTestResults\\minetest.plog ^     -c Release ^     -p x64 ^     -f diff-files.txt ^     -D C:\\caches     PlogConverter ^     -t FullHtml ^     -o .\\PVSTestResults\\ ^     -a GA:1,2,3;64:1,2,3;OP:1,2,3 ^     .\\PVSTestResults\\minetest.plog     IF NOT EXIST \"$(Build.ArtifactStagingDirectory)\" ^     MKDIR \"$(Build.ArtifactStagingDirectory)\"     powershell -Command ^     \"Compress-Archive -Force ^     '.\\PVSTestResults\\fullhtml' ^     '$(Build.ArtifactStagingDirectory)\\fullhtml.zip'\"   displayName: 'PVS-Studio analyze'   continueOnError: true  - task: PublishBuildArtifacts@1   inputs:     PathtoPublish: '$(Build.ArtifactStagingDirectory)'     ArtifactName: 'psv-studio-analisys'     publishLocation: 'Container'   displayName: 'Publish analysis report'<\/code><\/pre>\n<p>  This script will work when a PR is received and will be executed on the agents assigned to the pool by default. You only need to give it a permission to work with this pool.<\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/789\/1f7\/1c3\/7891f71c3ce396c8efd80a6ef8df776a.png\" alt=\"image8.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/789\/1f7\/1c3\/7891f71c3ce396c8efd80a6ef8df776a.png\"\/><\/div>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/9d9\/a17\/26e\/9d9a1726e49a2fe09a957d692ea517a7.png\" alt=\"image9.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/9d9\/a17\/26e\/9d9a1726e49a2fe09a957d692ea517a7.png\"\/><\/div>\n<p>  The script saves the list of modified files obtained using git diff. Then the dependencies are updated, the project solution is generated via CMake, and it is built.<\/p>\n<p>  If the build was successful, analysis of the changed files is started (the flag &#8216;-f diff-files.txt&#8217;), ignoring the auxiliary projects created by CMake (select only the necessary project with the &#8216;-S minetest &#8216; flag). To make determining relations between header and source C++ files faster, a special cache is created, which will be stored in a separate directory (the flag &#8216;-D C:\\caches&#8217;).<\/p>\n<p>  This way we can now get reports on analyzing changes in the project.<\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/089\/9b3\/166\/0899b3166dd7a62e8c5208e43dd9c271.png\" alt=\"image10.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/089\/9b3\/166\/0899b3166dd7a62e8c5208e43dd9c271.png\"\/><\/div>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/cdc\/120\/f89\/cdc120f8927fead1b8d955ef12286393.png\" alt=\"image11.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/cdc\/120\/f89\/cdc120f8927fead1b8d955ef12286393.png\"\/><\/div>\n<p>  As mentioned at the beginning of the article, a nice bonus of using self-hosted agents is a noticeable acceleration of task execution, due to local storage of intermediate files.<\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/252\/558\/8bd\/2525588bd2c39b1a72d3b892b256eb2a.png\" alt=\"image13.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/252\/558\/8bd\/2525588bd2c39b1a72d3b892b256eb2a.png\"\/><\/div>\n<p>  <\/p>\n<h2>Some errors found in Minetest<\/h2>\n<p>  <b>Overwriting the result<\/b><\/p>\n<p>  <a href=\"https:\/\/www.viva64.com\/en\/w\/v519\/\">V519<\/a> The &#8216;color_name&#8217; variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 621, 627. string.cpp 627<\/p>\n<pre><code class=\"cpp\">static bool parseNamedColorString(const std::string &amp;value,                                   video::SColor &amp;color) {   std::string color_name;   std::string alpha_string;    size_t alpha_pos = value.find('#');   if (alpha_pos != std::string::npos) {     color_name = value.substr(0, alpha_pos);     alpha_string = value.substr(alpha_pos + 1);   } else {     color_name = value;   }    color_name = lowercase(value); \/\/ &lt;=    std::map&lt;const std::string, unsigned>::const_iterator it;   it = named_colors.colors.find(color_name);   if (it == named_colors.colors.end())     return false;   .... }<\/code><\/pre>\n<p>  This function should parse the color name with the transparency parameter (for example, <i>Green#77<\/i>) and return its code. Depending on the result of checking the condition, the <i>color_name<\/i> variable is passed the result of splitting the string or a copy of the function argument. However, the original argument is then converted to lowercase instead of the resulting string itself. As a result, it can&#8217;t be found in the color dictionary if the transparency parameter is present. We can fix this line like this:<\/p>\n<pre><code class=\"cpp\">color_name = lowercase(color_name);<\/code><\/pre>\n<p>  <b>Redundant checks of conditions<\/b><\/p>\n<p>  <a href=\"https:\/\/www.viva64.com\/en\/w\/v547\/\">V547<\/a> Expression &#8216;nearest_emergefull_d == \u2014 1&#8217; is always true. clientiface.cpp 363<\/p>\n<pre><code class=\"cpp\">void RemoteClient::GetNextBlocks (....) {   ....   s32 nearest_emergefull_d = -1;   ....   s16 d;   for (d = d_start; d &lt;= d_max; d++) {     ....       if (block == NULL || surely_not_found_on_disk || block_is_invalid) {         if (emerge->enqueueBlockEmerge(peer_id, p, generate)) {           if (nearest_emerged_d == -1)             nearest_emerged_d = d;         } else {           if (nearest_emergefull_d == -1) \/\/ &lt;=             nearest_emergefull_d = d;           goto queue_full_break;         }   ....   }   .... queue_full_break:   if (nearest_emerged_d != -1) { \/\/ &lt;=     new_nearest_unsent_d = nearest_emerged_d;   } else .... }<\/code><\/pre>\n<p>  The <i>nearest_emergefull_d<\/i> variable doesn&#8217;t change during the loop operation, and its checking doesn&#8217;t affect the algorithm execution progress. Either this is the result of a sloppy copy-paste, or they forgot to perform some calculations with it.<\/p>\n<p>  <a href=\"https:\/\/www.viva64.com\/en\/w\/v560\/\">V560<\/a> A part of conditional expression is always false: y > max_spawn_y. mapgen_v7.cpp 262<\/p>\n<pre><code class=\"cpp\">int MapgenV7::getSpawnLevelAtPoint(v2s16 p) {   ....   while (iters > 0 &amp;&amp; y &lt;= max_spawn_y) {               \/\/ &lt;=     if (!getMountainTerrainAtPoint(p.X, y + 1, p.Y)) {       if (y &lt;= water_level || y > max_spawn_y)          \/\/ &lt;=         return MAX_MAP_GENERATION_LIMIT; \/\/ Unsuitable spawn point        \/\/ y + 1 due to biome 'dust'       return y + 1;     }   .... }<\/code><\/pre>\n<p>  The value of the &#8216;<i>y<\/i>&#8216; variable is checked before the next iteration of the loop. A subsequent, opposite comparison will always return <i>false<\/i> and actually doesn&#8217;t affect the result of checking the condition.<\/p>\n<p>  <b>Missed pointer check<\/b><\/p>\n<p>  <a href=\"https:\/\/www.viva64.com\/en\/w\/v595\/\">V595<\/a> The &#8216;m_client&#8217; pointer was utilized before it was verified against nullptr. Check lines: 183, 187. game.cpp 183<\/p>\n<pre><code class=\"cpp\">void gotText(const StringMap &amp;fields) {   ....   if (m_formname == \"MT_DEATH_SCREEN\") {     assert(m_client != 0);     m_client->sendRespawn();     return;   }    if (m_client &amp;&amp; m_client->modsLoaded())     m_client->getScript()->on_formspec_input(m_formname, fields); }<\/code><\/pre>\n<p>  Before accessing the <i>m_client<\/i> pointer, it is checked whether it is null using the <i>assert<\/i> macro. But this only applies to the debug build. So, this precautionary measure is replaced with a dummy when building to release, and there is a risk of dereferencing the null pointer.<\/p>\n<p>  <b>Bit or not bit?<\/b><\/p>\n<p>  <a href=\"https:\/\/www.viva64.com\/en\/w\/v616\/\">V616<\/a> The &#8216;(FT_RENDER_MODE_NORMAL)&#8217; named constant with the value of 0 is used in the bitwise operation. CGUITTFont.h 360<\/p>\n<pre><code class=\"cpp\">typedef enum  FT_Render_Mode_ {   FT_RENDER_MODE_NORMAL = 0,   FT_RENDER_MODE_LIGHT,   FT_RENDER_MODE_MONO,   FT_RENDER_MODE_LCD,   FT_RENDER_MODE_LCD_V,    FT_RENDER_MODE_MAX } FT_Render_Mode;  #define FT_LOAD_TARGET_( x )   ( (FT_Int32)( (x) &amp; 15 ) &lt;&lt; 16 ) #define FT_LOAD_TARGET_NORMAL  FT_LOAD_TARGET_( FT_RENDER_MODE_NORMAL )  void update_load_flags() {   \/\/ Set up our loading flags.   load_flags = FT_LOAD_DEFAULT | FT_LOAD_RENDER;   if (!useHinting()) load_flags |= FT_LOAD_NO_HINTING;   if (!useAutoHinting()) load_flags |= FT_LOAD_NO_AUTOHINT;   if (useMonochrome()) load_flags |=      FT_LOAD_MONOCHROME | FT_LOAD_TARGET_MONO | FT_RENDER_MODE_MONO;   else load_flags |= FT_LOAD_TARGET_NORMAL; \/\/ &lt;= }<\/code><\/pre>\n<p>  The <i>FT_LOAD_TARGET_NORMAL<\/i> macro is deployed to zero, and the bitwise &#171;OR&#187; will not set any flags in <i>load_flags<\/i>, the <i>else<\/i> branch can be removed.<\/p>\n<p>  <b>Rounding integer division<\/b><\/p>\n<p>  <a href=\"https:\/\/www.viva64.com\/en\/w\/v636\/\">V636<\/a> The &#8216;rect.getHeight() \/ 16&#8217; expression was implicitly cast from &#8216;int&#8217; type to &#8216;float&#8217; type. Consider utilizing an explicit type cast to avoid the loss of a fractional part. An example: double A = (double)(X) \/ Y;. hud.cpp 771<\/p>\n<pre><code class=\"cpp\">void drawItemStack(....) {   float barheight = rect.getHeight() \/ 16;   float barpad_x = rect.getWidth() \/ 16;   float barpad_y = rect.getHeight() \/ 16;    core::rect&lt;s32> progressrect(     rect.UpperLeftCorner.X + barpad_x,     rect.LowerRightCorner.Y - barpad_y - barheight,     rect.LowerRightCorner.X - barpad_x,     rect.LowerRightCorner.Y - barpad_y); }<\/code><\/pre>\n<p>  <i>Rect<\/i> getters return integer values. The result of dividing integer numbers is written to a floating-point variable, and the fractional part gets lost. It looks like there are mismatched data types in these calculations.<\/p>\n<p>  <b>Suspicious sequence of branching operators<\/b><\/p>\n<p>  <a href=\"https:\/\/www.viva64.com\/en\/w\/v646\/\">V646<\/a> Consider inspecting the application&#8217;s logic. It&#8217;s possible that &#8216;else&#8217; keyword is missing. treegen.cpp 413<\/p>\n<pre><code class=\"cpp\">treegen::error make_ltree(...., TreeDef tree_definition) {   ....   std::stack &lt;core::matrix4> stack_orientation;   ....     if ((stack_orientation.empty() &amp;&amp;       tree_definition.trunk_type == \"double\") ||       (!stack_orientation.empty() &amp;&amp;       tree_definition.trunk_type == \"double\" &amp;&amp;       !tree_definition.thin_branches)) {       ....     } else if ((stack_orientation.empty() &amp;&amp;       tree_definition.trunk_type == \"crossed\") ||       (!stack_orientation.empty() &amp;&amp;       tree_definition.trunk_type == \"crossed\" &amp;&amp;       !tree_definition.thin_branches)) {       ....     } if (!stack_orientation.empty()) {                  \/\/ &lt;=   ....   }   .... }<\/code><\/pre>\n<p>  There are <i>else-if<\/i> sequences in the tree generation algorithm here. In the middle the next <i>if<\/i> block is on the same line with the closing brace of the previous <i>else<\/i> statement. Perhaps, the code works correctly: before this <i>if<\/i> statement, blocks of the trunk are created, followed by leaves. On the other hand, it&#8217;s possible that <i>else<\/i> is missed. Only the author can say this for sure.<\/p>\n<p>  <b>Incorrect memory allocation check<\/b><\/p>\n<p>  <a href=\"https:\/\/www.viva64.com\/en\/w\/v668\/\">V668<\/a> There is no sense in testing the &#8216;clouds&#8217; pointer against null, as the memory was allocated using the &#8216;new&#8217; operator. The exception will be generated in the case of memory allocation error. game.cpp 1367<\/p>\n<pre><code class=\"cpp\">bool Game::createClient(....) {   if (m_cache_enable_clouds) {     clouds = new Clouds(smgr, -1, time(0));     if (!clouds) {       *error_message = \"Memory allocation error (clouds)\";       errorstream &lt;&lt; *error_message &lt;&lt; std::endl;       return false;     }   } }<\/code><\/pre>\n<p>  If <i>new<\/i> can&#8217;t create an object, an <i>std::bad_alloc<\/i> exception is thrown, and it must be handled by the <i>try-catch<\/i> block. A check like this is useless.<\/p>\n<p>  <b>Reading outside the array bound<\/b><\/p>\n<p>  <a href=\"https:\/\/www.viva64.com\/en\/w\/v781\/\">V781<\/a> The value of the &#8216;i&#8217; index is checked after it was used. Perhaps there is a mistake in program logic. irrString.h 572<\/p>\n<pre><code class=\"cpp\">bool equalsn(const string&lt;T,TAlloc>&amp; other, u32 n) const {   u32 i;   for(i=0; array[i] &amp;&amp; other[i] &amp;&amp; i &lt; n; ++i) \/\/ &lt;=     if (array[i] != other[i])       return false;    \/\/ if one (or both) of the strings was smaller then they   \/\/ are only equal if they have the same length   return (i == n) || (used == other.used); }<\/code><\/pre>\n<p>  Array elements are accessed before checking the index, which may lead to an error. Perhaps the author should rewrite the loop like this:<\/p>\n<pre><code class=\"cpp\">for (i=0; i &lt; n; ++i) \/\/ &lt;=   if (!array[i] || !other[i] || array[i] != other[i])     return false;<\/code><\/pre>\n<p>  <b>Other errors<\/b><\/p>\n<p>  This article covers the analysis of pull requests in Azure DevOps and doesn&#8217;t aim to provide a detailed overview of errors found in the Minetest project. Only some code fragments that I found interesting are written here. We suggest that the project authors don&#8217;t follow this article to correct errors, but perform a more thorough analysis of the warnings that PVS-Studio will issue.<\/p>\n<h2>Conclusion<\/h2>\n<p>  Thanks to its flexible command-line configuration, PVS-Studio analysis can be integrated into a wide variety of CI\/CD scenarios. And the correct use of available resources pays off by increasing productivity.<\/p>\n<p>  Note that the pull request checking mode is only available in the Enterprise version of the analyzer. To get a demo Enterprise license, specify this in the comments when requesting a license on the <a href=\"https:\/\/www.viva64.com\/en\/pvs-studio-download\/\">download page<\/a>. You can learn more about the difference between licenses on the <a href=\"https:\/\/www.viva64.com\/en\/order\/\">Buy PVS-Studio<\/a> page.<\/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\/512680\/\"> https:\/\/habr.com\/ru\/articles\/512680\/<\/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-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/91\/vt\/l-\/91vtl--az32_rl5mz2ljtji4ifg.png\" data-src=\"https:\/\/habrastorage.org\/webt\/91\/vt\/l-\/91vtl--az32_rl5mz2ljtji4ifg.png\"\/><\/p>\n<p>  Static code analysis is most effective when changing a project, as errors are always more difficult to fix in the future than at an early stage. We continue expanding the options for using PVS-Studio in continuous development systems. This time, we&#8217;ll show you how to configure pull request analysis using self-hosted agents in Microsoft Azure DevOps, using the example of the Minetest game.  <\/p>\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-411040","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/411040","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=411040"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/411040\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=411040"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=411040"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=411040"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}