{"id":394775,"date":"2024-06-29T11:49:21","date_gmt":"2024-06-29T11:49:21","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=394775"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=394775","title":{"rendered":"<span>PVS-Studio and Continuous Integration: TeamCity. Analysis of the Open RollerCoaster Tycoon 2 project<\/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\">\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/vl\/bt\/_8\/vlbt_8tisj_zdatbgv2dou07h-o.png\" data-src=\"https:\/\/habrastorage.org\/webt\/vl\/bt\/_8\/vlbt_8tisj_zdatbgv2dou07h-o.png\"\/><\/div>\n<p>  One of the most relevant scenarios for using the PVS-Studio analyzer is its integration into CI systems. Even though a project analysis by PVS-Studio can already be embedded with just a few commands into almost any continuous integration system, we continue to make this process even more convenient. PVS-Studio now supports converting the analyzer output to the TeamCity format-TeamCity Inspections Type. Let&#8217;s see how it works. <br \/>  <a name=\"habracut\"><\/a>  <\/p>\n<h2>About the software used<\/h2>\n<p>  <a href=\"https:\/\/www.viva64.com\/en\/pvs-studio\/\">PVS-Studio<\/a> is a static analyzer of C, C++, C#, and Java code designed to facilitate the task of finding and correcting various types of errors. The analyzer can be used on Windows, Linux, and macOS. In this article, we will actively use not only the analyzer itself, but also some utilities from its distribution.<\/p>\n<p>  <a href=\"https:\/\/www.viva64.com\/en\/m\/0031\/\">CLMonitor<\/a> is a monitoring server that performs monitoring of compiler runs. It has to be run immediately before building a project. In monitoring mode, the server will intercept runs of all supported compilers. It is worth noting that this utility can only be used for analyzing C\/C++ projects.<\/p>\n<p>  <a href=\"https:\/\/www.viva64.com\/en\/m\/0038\/\">PlogConverter<\/a> is a utility for converting the analyzer report into different formats.<\/p>\n<h2>About the checked project<\/h2>\n<p>  Let&#8217;s try this feature on a practical example by analyzing the OpenRCT2 project. <\/p>\n<p>  <a href=\"https:\/\/openrct2.org\/\">OpenRCT2<\/a> is an open implementation of the RollerCoaster Tycoon 2 (RCT2) game, expanding it with new features and fixed bugs. The gameplay revolves around the construction and maintenance of an amusement park that houses rides, stores, and facilities. The player has to try to make profit and maintain the good reputation of the park, while keeping the guests happy. OpenRCT2 allows you to play both by following the script and in the sandbox. Scenarios require a player to complete a specific task in a set time, while the sandbox allows a player to build a more flexible park without any restrictions or finances.<\/p>\n<h2>Configuration <\/h2>\n<p>  In order to save time, I will probably skip the installation process and start from the point when the TeamCity server is running on my computer. We need to go to: localhost:{the port specified during installation}(in my case, localhost:9090) and enter the authorization data. After entering we will get:<\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/19b\/bda\/aa4\/19bbdaaa415eff06a518d819325ab654.png\" alt=\"image3.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/19b\/bda\/aa4\/19bbdaaa415eff06a518d819325ab654.png\"\/><\/div>\n<p>  Click on Create Project. Next, select <i>Manually<\/i> and fill in the fields.<\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/5c0\/fe6\/854\/5c0fe685439623515112b0a6cb1e706c.png\" alt=\"image5.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/5c0\/fe6\/854\/5c0fe685439623515112b0a6cb1e706c.png\"\/><\/div>\n<p>  After clicking <i>Create<\/i>, we see the window with settings.<\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/3f8\/9af\/925\/3f89af9255e7a4347f4f7865719960b6.png\" alt=\"image7.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/3f8\/9af\/925\/3f89af9255e7a4347f4f7865719960b6.png\"\/><\/div>\n<p>  Click<i> Create build configuration.<\/i><\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/b66\/fb2\/d9b\/b66fb2d9bb610cf8f4cddc04bb3b12f6.png\" alt=\"image9.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/b66\/fb2\/d9b\/b66fb2d9bb610cf8f4cddc04bb3b12f6.png\"\/><\/div>\n<p>  Fill in the fields and click <i>Create<\/i>. We see the window suggesting to select a version control system. Since the sources are already located locally, click <i>Skip<\/i>.<\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/4d5\/4b7\/f4b\/4d54b7f4b58db1b8cbd1532cd989971e.png\" alt=\"image11.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/4d5\/4b7\/f4b\/4d54b7f4b58db1b8cbd1532cd989971e.png\"\/><\/div>\n<p>  Finally, we go to the project settings.<\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/d98\/961\/36d\/d9896136d51a403258170cb6b4a6c3a8.png\" alt=\"image13.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/d98\/961\/36d\/d9896136d51a403258170cb6b4a6c3a8.png\"\/><\/div>\n<p>  We&#8217;ll add the build steps. To do this, click: <i>Build steps -> Add build step<\/i>.<\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/cb9\/057\/300\/cb905730065433a0d0ad200ab9170a98.png\" alt=\"image15.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/cb9\/057\/300\/cb905730065433a0d0ad200ab9170a98.png\"\/><\/div>\n<p>  Here we choose:<\/p>\n<ul>\n<li><i>Runner type -> Command Line<\/i><\/li>\n<li><i>Run -> Custom Script<\/i><\/li>\n<\/ul>\n<p>  Since we will perform analysis during the project compilation, the build and analysis must be one step, therefore we will fill in the <i>Custom Script<\/i> field:<\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/acc\/9c5\/0ed\/acc9c50ed1f839b2947a33e97a7fe49b.png\" alt=\"image17.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/acc\/9c5\/0ed\/acc9c50ed1f839b2947a33e97a7fe49b.png\"\/><\/div>\n<p>  We will focus on individual steps later. It is important that loading the analyzer, building the project, analyzing it, the report output, and formatting it should take only eleven lines of code.<\/p>\n<p>  The last thing we need to do is to set environment variables, which, in my case, outline some ways to improve their readability. To do this, go to: <i>Parameters -> Add new parameter<\/i> and add three variables:<\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/f8b\/9b4\/19b\/f8b9b419b0181a3c80892f295626dbc5.png\" alt=\"image19.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/f8b\/9b4\/19b\/f8b9b419b0181a3c80892f295626dbc5.png\"\/><\/div>\n<p>  Just click on <i>Run<\/i> in the upper-right corner. While the project is being built and analyzed, let me will tell you about the script.<\/p>\n<h2>The script itself<\/h2>\n<p>  First, we need to download the latest PVS-Studio distribution. To do this, we use the Chocolatey package manager. For those who want to learn more about this, there is a special <a href=\"https:\/\/www.viva64.com\/en\/b\/0712\/\">article<\/a>:<\/p>\n<pre><code class=\"cpp\">choco install pvs-studio -y<\/code><\/pre>\n<p>  Next, run the CLMonitor project build monitoring utility.<\/p>\n<pre><code class=\"cpp\">%CLmon% monitor \u2013-attach<\/code><\/pre>\n<p>  Then we will build the project. The <i>MSB<\/i> environment variable represents the path to the MSBuild version that I need to build.<\/p>\n<pre><code class=\"cpp\">%MSB% %ProjPath% \/t:clean %MSB% %ProjPath% \/t:rebuild \/p:configuration=release %MSB% %ProjPath% \/t:g2 %MSB% %ProjPath% \/t:PublishPortable<\/code><\/pre>\n<p>  Enter the username and license key for PVS-Studio:<\/p>\n<pre><code class=\"cpp\">%PVS-Studio_cmd% credentials --username %PVS_Name% --serialNumber %PVS_Key%<\/code><\/pre>\n<p>  After the build is complete, we will run CLMonitor again to generate preprocessed files and perform static analysis:<\/p>\n<pre><code class=\"cpp\">%CLmon% analyze -l \"c:\\ptest.plog\"<\/code><\/pre>\n<p>  After that, we will use another utility from our distribution. PlogConverter converts a report from standard to a TeamCity-specific format. This allows us to view it directly in the build window.<\/p>\n<pre><code class=\"cpp\">%PlogConverter% \"c:\\ptest.plog\" --renderTypes=TeamCity -o \"C:\\temp\"<\/code><\/pre>\n<p>  The last action is to output the formatted report to <i>stdout,<\/i> where it will be picked up by the TeamCity parser.<\/p>\n<pre><code class=\"cpp\">type \"C:\\temp\\ptest.plog_TeamCity.txt\"<\/code><\/pre>\n<p>  Full script code:<\/p>\n<pre><code class=\"cpp\">choco install pvs-studio -y %CLmon% monitor --attach set platform=x64 %MSB% %ProjPath% \/t:clean %MSB% %ProjPath% \/t:rebuild \/p:configuration=release %MSB% %ProjPath% \/t:g2 %MSB% %ProjPath% \/t:PublishPortable %PVS-Studio_cmd% credentials --username %PVS_Name% --serialNumber %PVS_Key% %CLmon% analyze -l \"c:\\ptest.plog\" %PlogConverter% \"c:\\ptest.plog\" --renderTypes=TeamCity -o \"C:\\temp\" type \"C:\\temp\\ptest.plog_TeamCity.txt\"<\/code><\/pre>\n<p>  In the meantime, the build and analysis of the project has been completed successfully, so we can go to the <i>Projects<\/i> tab and make sure of it.<\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/4a8\/d2b\/30c\/4a8d2b30c8dd9017c6bfebd43e9d48e4.png\" alt=\"image21.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/4a8\/d2b\/30c\/4a8d2b30c8dd9017c6bfebd43e9d48e4.png\"\/><\/div>\n<p>  Now click on <i>Inspections Total <\/i>to view the analyzer report:<\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/b0a\/76b\/9b5\/b0a76b9b58d28002359eb2b3bb3ab3f1.png\" alt=\"image23.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/b0a\/76b\/9b5\/b0a76b9b58d28002359eb2b3bb3ab3f1.png\"\/><\/div>\n<p>  Warnings are grouped by diagnostic rule numbers. To navigate along the code, click on the line number with the warning. Clicking on the question mark in the upper-right corner will open a new tab with documentation. You can also navigate along the code by clicking on the line number with the analyzer warning. Navigation from a remote computer is possible when using the <i>SourceTreeRoot<\/i> marker. Those who are interested in this mode of the analyzer operation are welcome to read the related <a href=\"https:\/\/www.viva64.com\/en\/m\/0027\/\">documentation<\/a> section.<\/p>\n<h2>Viewing the analysis results<\/h2>\n<p>  After we are done with the deployment and configuration of the build, I suggest taking a look at some interesting warnings found in the reviewed project.<\/p>\n<p>  <b>Warning N1<\/b><\/p>\n<p>  <a href=\"https:\/\/www.viva64.com\/en\/w\/v773\/\">V773<\/a> [CWE-401] The exception was thrown without releasing the &#8216;result&#8217; pointer. A memory leak is possible. libopenrct2 ObjectFactory.cpp 443<\/p>\n<pre><code class=\"cpp\">Object* CreateObjectFromJson(....) {   Object* result = nullptr;   ....   result = CreateObject(entry);   ....   if (readContext.WasError())   {     throw std::runtime_error(\"Object has errors\");   }   .... }  Object* CreateObject(const rct_object_entry&amp; entry) {   Object* result;   switch (entry.GetType())   {     case OBJECT_TYPE_RIDE:       result = new RideObject(entry);       break;     case OBJECT_TYPE_SMALL_SCENERY:       result = new SmallSceneryObject(entry);       break;     case OBJECT_TYPE_LARGE_SCENERY:       result = new LargeSceneryObject(entry);       break;     ....     default:       throw std::runtime_error(\"Invalid object type\");   }   return result; }<\/code><\/pre>\n<p>  The analyzer noticed the error that after dynamic memory allocation in <i>CreateObject<\/i>, when an exception occurs, the memory is not cleared, and consequently, a memory leak occurs.<\/p>\n<p>  <b>Warning N2<\/b><\/p>\n<p>  <a href=\"https:\/\/www.viva64.com\/en\/w\/v501\/\">V501<\/a> There are identical sub-expressions &#8216;(1ULL &lt;&lt; WIDX_MONTH_BOX)&#8217; to the left and to the right of the &#8216;|&#8217; operator. libopenrct2ui Cheats.cpp 487<\/p>\n<pre><code class=\"cpp\">static uint64_t window_cheats_page_enabled_widgets[] =  {   MAIN_CHEAT_ENABLED_WIDGETS |   (1ULL &lt;&lt; WIDX_NO_MONEY) |   (1ULL &lt;&lt; WIDX_ADD_SET_MONEY_GROUP) |   (1ULL &lt;&lt; WIDX_MONEY_SPINNER) |   (1ULL &lt;&lt; WIDX_MONEY_SPINNER_INCREMENT) |   (1ULL &lt;&lt; WIDX_MONEY_SPINNER_DECREMENT) |   (1ULL &lt;&lt; WIDX_ADD_MONEY) |   (1ULL &lt;&lt; WIDX_SET_MONEY) |   (1ULL &lt;&lt; WIDX_CLEAR_LOAN) |   (1ULL &lt;&lt; WIDX_DATE_SET) |   (1ULL &lt;&lt; WIDX_MONTH_BOX) |  \/\/ &lt;=   (1ULL &lt;&lt; WIDX_MONTH_UP) |   (1ULL &lt;&lt; WIDX_MONTH_DOWN) |   (1ULL &lt;&lt; WIDX_YEAR_BOX) |   (1ULL &lt;&lt; WIDX_YEAR_UP) |   (1ULL &lt;&lt; WIDX_YEAR_DOWN) |   (1ULL &lt;&lt; WIDX_DAY_BOX) |   (1ULL &lt;&lt; WIDX_DAY_UP) |   (1ULL &lt;&lt; WIDX_DAY_DOWN) |   (1ULL &lt;&lt; WIDX_MONTH_BOX) |  \/\/ &lt;=   (1ULL &lt;&lt; WIDX_DATE_GROUP) |   (1ULL &lt;&lt; WIDX_DATE_RESET),   .... };<\/code><\/pre>\n<p>  Few, but a static code analyzer would be able to pass this test for attention. It is diligence that this example of copy paste checks.<\/p>\n<p>  <b>Warnings N3<\/b><\/p>\n<p>  <a href=\"https:\/\/www.viva64.com\/en\/w\/v703\/\">V703<\/a> It is odd that the &#8216;flags&#8217; field in derived class &#8216;RCT12BannerElement&#8217; overwrites field in base class &#8216;RCT12TileElementBase&#8217;. Check lines: RCT12.h:570, RCT12.h:259. libopenrct2 RCT12.h 570<\/p>\n<pre><code class=\"cpp\">struct RCT12SpriteBase {   ....   uint8_t flags;   .... }; struct rct1_peep : RCT12SpriteBase {   ....   uint8_t flags;   .... };<\/code><\/pre>\n<p>  Of course, using the same name variable both in the base and derived classes is not always an error. However, inheritance technology itself assumes that all fields of the parent class are present in the child class. By declaring a field with the same name in the derived class, we create confusion.<\/p>\n<p>  <b>Warning N4<\/b><\/p>\n<p>  <a href=\"https:\/\/www.viva64.com\/en\/w\/v793\/\">V793<\/a> It is odd that the result of the &#8216;imageDirection \/ 8&#8217; statement is a part of the condition. Perhaps, this statement should have been compared with something else. libopenrct2 ObservationTower.cpp 38<\/p>\n<pre><code class=\"cpp\">void vehicle_visual_observation_tower(...., int32_t imageDirection, ....) {   if ((imageDirection \/ 8) &amp;&amp; (imageDirection \/ 8) != 3)   {     ....   }   .... }<\/code><\/pre>\n<p>  Let&#8217;s look at it in more detail. The <i>imageDirection \/ 8<\/i> expression will be false if <i>imageDirection<\/i> is in the range from -7 to 7. Second part: <i>(imageDirection \/ 8) != 3<\/i> checks <i>imageDirection<\/i> for being outside the range: from -31 to -24 and from 24 to 31, respectively. It seems rather strange to check numbers for falling into a certain range in this way, and even if there is no error in this code fragment, I would recommend rewriting these conditions to more explicit ones. This would significantly simplify the lives of people who will read and maintain this code afterwards.<\/p>\n<p>  <b>Warning N5<\/b><\/p>\n<p>  <a href=\"https:\/\/www.viva64.com\/en\/w\/v587\/\">V587<\/a> An odd sequence of assignments of this kind: A = B; B = A;. Check lines: 1115, 1118. libopenrct2ui MouseInput.cpp 1118<\/p>\n<pre><code class=\"cpp\">void process_mouse_over(....) {   ....   switch (window->widgets[widgetId].type)   {     case WWT_VIEWPORT:       ebx = 0;       edi = cursorId;                                 \/\/ &lt;=       \/\/ Window event WE_UNKNOWN_0E was called here,       \/\/ but no windows actually implemented a handler and       \/\/ it's not known what it was for       cursorId = edi;                                 \/\/ &lt;=       if ((ebx &amp; 0xFF) != 0)       {         set_cursor(cursorId);         return;       }       break;       ....   }   .... }<\/code><\/pre>\n<p>  This code fragment was most likely obtained by decompilation. Then, judging by the comment left, a part of the non-working code was deleted. However, there is still a couple of operations on <i>cursorId<\/i> that also don&#8217;t make much sense.<\/p>\n<p>  <b>Warning N6<\/b><\/p>\n<p>  <a href=\"https:\/\/www.viva64.com\/en\/w\/v1004\/\">V1004<\/a> [CWE-476] The &#8216;player&#8217; pointer was used unsafely after it was verified against nullptr. Check lines: 2085, 2094. libopenrct2 Network.cpp 2094<\/p>\n<pre><code class=\"cpp\">void Network::ProcessPlayerList() {   ....   auto* player = GetPlayerByID(pendingPlayer.Id);   if (player == nullptr)   {     \/\/ Add new player.     player = AddPlayer(\"\", \"\");     if (player)                                          \/\/ &lt;=     {       *player = pendingPlayer;        if (player->Flags &amp; NETWORK_PLAYER_FLAG_ISSERVER)        {          _serverConnection->Player = player;        }     }     newPlayers.push_back(player->Id);                    \/\/ &lt;=   }   .... }<\/code><\/pre>\n<p>  This code is quite simple to correct \u2014 one either needs to check <i>player<\/i> for a null pointer for the third time, or add it to the body of the conditional operator. I would suggest the second option:<\/p>\n<pre><code class=\"cpp\">void Network::ProcessPlayerList() {   ....   auto* player = GetPlayerByID(pendingPlayer.Id);   if (player == nullptr)   {     \/\/ Add new player.     player = AddPlayer(\"\", \"\");     if (player)     {       *player = pendingPlayer;       if (player->Flags &amp; NETWORK_PLAYER_FLAG_ISSERVER)       {         _serverConnection->Player = player;       }       newPlayers.push_back(player->Id);     }   }   .... }<\/code><\/pre>\n<p>  <b>Warning N7<\/b><\/p>\n<p>  <a href=\"https:\/\/www.viva64.com\/en\/w\/v547\/\">V547<\/a> [CWE-570] Expression &#8216;name == nullptr&#8217; is always false. libopenrct2 ServerList.cpp 102<\/p>\n<pre><code class=\"cpp\">std::optional&lt;ServerListEntry> ServerListEntry::FromJson(...) {   auto name = json_object_get(server, \"name\");   .....   if (name == nullptr || version == nullptr)   {     ....   }   else   {     ....     entry.name = (name == nullptr ? \"\" : json_string_value(name));     ....   }   .... }<\/code><\/pre>\n<p>  You can get rid of a hard-to-read line of code in one fell swoop and solve the problem with checking for <i>nullptr<\/i>. I would change the code as follows:<\/p>\n<pre><code class=\"cpp\">std::optional&lt;ServerListEntry> ServerListEntry::FromJson(...) {   auto name = json_object_get(server, \"name\");   .....   if (name == nullptr || version == nullptr)   {     name = \"\"     ....   }   else   {     ....     entry.name = json_string_value(name);     ....   }   .... }<\/code><\/pre>\n<p>  <b>Warning N8<\/b><\/p>\n<p>  <a href=\"https:\/\/www.viva64.com\/en\/w\/v1048\/\">V1048<\/a> [CWE-1164] The &#8216;ColumnHeaderPressedCurrentState&#8217; variable was assigned the same value. libopenrct2ui CustomListView.cpp 510<\/p>\n<pre><code class=\"cpp\">void CustomListView::MouseUp(....) {   ....   if (!ColumnHeaderPressedCurrentState)   {     ColumnHeaderPressed = std::nullopt;     ColumnHeaderPressedCurrentState = false;     Invalidate();   } }<\/code><\/pre>\n<p>  The code looks quite strange. I think there was a typo either in the condition or when reassigning the <i>false<\/i> value to the <i>ColumnHeaderPressedCurrentState<\/i>variable.<\/p>\n<h2>Conclusion<\/h2>\n<p>  As we can see, it is quite easy to integrate the PVS-Studio static analyzer into your TeamCity project. To do this, you just need to write one small configuration file. For its part, checking the code will allow you to detect problems immediately after the build, which will help you fix them when the complexity and cost of edits are still small.<\/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\/511770\/\"> https:\/\/habr.com\/ru\/articles\/511770\/<\/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\">\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/vl\/bt\/_8\/vlbt_8tisj_zdatbgv2dou07h-o.png\" data-src=\"https:\/\/habrastorage.org\/webt\/vl\/bt\/_8\/vlbt_8tisj_zdatbgv2dou07h-o.png\"\/><\/div>\n<p>  One of the most relevant scenarios for using the PVS-Studio analyzer is its integration into CI systems. Even though a project analysis by PVS-Studio can already be embedded with just a few commands into almost any continuous integration system, we continue to make this process even more convenient. PVS-Studio now supports converting the analyzer output to the TeamCity format-TeamCity Inspections Type. Let&#8217;s see how it works.   <\/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-394775","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/394775","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=394775"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/394775\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=394775"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=394775"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=394775"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}