﻿# PVS\-Studio and Continuous Integration: TeamCity\. Analysis of the Open RollerCoaster Tycoon 2 project

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's see how it works\. 

![0749_TeamCity/image1.png](https://import.viva64.com/docx/blog/0749_TeamCity/image1.png)

## About the software used

[PVS\-Studio](https://pvs-studio.com/en/pvs-studio/) 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\.

[CLMonitor](https://pvs-studio.com/en/docs/manual/0031/) 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\.

[PlogConverter](https://pvs-studio.com/en/docs/manual/0038/) is a utility for converting the analyzer report into different formats\.

## About the checked project

Let's try this feature on a practical example by analyzing the OpenRCT2 project\. 

[OpenRCT2](https://openrct2.org/) 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\.

## Configuration 

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:

![0749_TeamCity/image3.png](https://import.viva64.com/docx/blog/0749_TeamCity/image3.png)

Click on Create Project\. Next, select _Manually_ and fill in the fields\.

![0749_TeamCity/image5.png](https://import.viva64.com/docx/blog/0749_TeamCity/image5.png)

After clicking _Create_, we see the window with settings\.



![0749_TeamCity/image7.png](https://import.viva64.com/docx/blog/0749_TeamCity/image7.png)

Click_ Create build configuration\._

![0749_TeamCity/image9.png](https://import.viva64.com/docx/blog/0749_TeamCity/image9.png)

Fill in the fields and click _Create_\. We see the window suggesting to select a version control system\. Since the sources are already located locally, click _Skip_\.

![0749_TeamCity/image11.png](https://import.viva64.com/docx/blog/0749_TeamCity/image11.png)

Finally, we go to the project settings\.

![0749_TeamCity/image13.png](https://import.viva64.com/docx/blog/0749_TeamCity/image13.png)

We'll add the build steps\. To do this, click: _Build steps \-\> Add build step_\.

![0749_TeamCity/image15.png](https://import.viva64.com/docx/blog/0749_TeamCity/image15.png)

Here we choose:

* _Runner type \-\> Command Line_
* _Run \-\> Custom Script_

Since we will perform analysis during the project compilation, the build and analysis must be one step, therefore we will fill in the _Custom Script_ field:



![0749_TeamCity/image17.png](https://import.viva64.com/docx/blog/0749_TeamCity/image17.png)

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\.

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: _Parameters \-\> Add new parameter_ and add three variables:

![0749_TeamCity/image19.png](https://import.viva64.com/docx/blog/0749_TeamCity/image19.png)

Just click on _Run_ in the upper\-right corner\. While the project is being built and analyzed, let me will tell you about the script\.

## The script itself

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 [article](https://pvs-studio.com/en/blog/posts/csharp/0712/):

```cpp
choco install pvs-studio -y
```

Next, run the CLMonitor project build monitoring utility\.

```cpp
%CLmon% monitor –-attach
```

Then we will build the project\. The _MSB_ environment variable represents the path to the MSBuild version that I need to build\.

```cpp
%MSB% %ProjPath% /t:clean
%MSB% %ProjPath% /t:rebuild /p:configuration=release
%MSB% %ProjPath% /t:g2
%MSB% %ProjPath% /t:PublishPortable
```

Enter the username and license key for PVS\-Studio:

```cpp
%PVS-Studio_cmd% credentials --username %PVS_Name% --serialNumber %PVS_Key%
```

After the build is complete, we will run CLMonitor again to generate preprocessed files and perform static analysis:

```cpp
%CLmon% analyze -l "c:\ptest.plog"
```

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\.

```cpp
%PlogConverter% "c:\ptest.plog" --renderTypes=TeamCity -o "C:\temp"
```

The last action is to output the formatted report to _stdout,_ where it will be picked up by the TeamCity parser\.

```cpp
type "C:\temp\ptest.plog_TeamCity.txt"
```

Full script code:

```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"
```

In the meantime, the build and analysis of the project has been completed successfully, so we can go to the _Projects_ tab and make sure of it\.

![0749_TeamCity/image21.png](https://import.viva64.com/docx/blog/0749_TeamCity/image21.png)

Now click on _Inspections Total_ to view the analyzer report:

![0749_TeamCity/image23.png](https://import.viva64.com/docx/blog/0749_TeamCity/image23.png)

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 _SourceTreeRoot_ marker\. Those who are interested in this mode of the analyzer operation are welcome to read the related [documentation](https://pvs-studio.com/en/docs/manual/0027/) section\.

## Viewing the analysis results

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\.

**Warning N1**

[V773](https://pvs-studio.com/en/docs/warnings/v773/) \[CWE\-401\] The exception was thrown without releasing the 'result' pointer\. A memory leak is possible\. libopenrct2 ObjectFactory\.cpp 443

```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& 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;
}
```

The analyzer noticed the error that after dynamic memory allocation in _CreateObject_, when an exception occurs, the memory is not cleared, and consequently, a memory leak occurs\.

**Warning N2**

[V501](https://pvs-studio.com/en/docs/warnings/v501/) There are identical sub\-expressions '\(1ULL << WIDX\_MONTH\_BOX\)' to the left and to the right of the '\|' operator\. libopenrct2ui Cheats\.cpp 487

```cpp
static uint64_t window_cheats_page_enabled_widgets[] = 
{
  MAIN_CHEAT_ENABLED_WIDGETS |
  (1ULL << WIDX_NO_MONEY) |
  (1ULL << WIDX_ADD_SET_MONEY_GROUP) |
  (1ULL << WIDX_MONEY_SPINNER) |
  (1ULL << WIDX_MONEY_SPINNER_INCREMENT) |
  (1ULL << WIDX_MONEY_SPINNER_DECREMENT) |
  (1ULL << WIDX_ADD_MONEY) |
  (1ULL << WIDX_SET_MONEY) |
  (1ULL << WIDX_CLEAR_LOAN) |
  (1ULL << WIDX_DATE_SET) |
  (1ULL << WIDX_MONTH_BOX) |  // <=
  (1ULL << WIDX_MONTH_UP) |
  (1ULL << WIDX_MONTH_DOWN) |
  (1ULL << WIDX_YEAR_BOX) |
  (1ULL << WIDX_YEAR_UP) |
  (1ULL << WIDX_YEAR_DOWN) |
  (1ULL << WIDX_DAY_BOX) |
  (1ULL << WIDX_DAY_UP) |
  (1ULL << WIDX_DAY_DOWN) |
  (1ULL << WIDX_MONTH_BOX) |  // <=
  (1ULL << WIDX_DATE_GROUP) |
  (1ULL << WIDX_DATE_RESET),
  ....
};
```

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\.

**Warnings N3**

[V703](https://pvs-studio.com/en/docs/warnings/v703/) It is odd that the 'flags' field in derived class 'RCT12BannerElement' overwrites field in base class 'RCT12TileElementBase'\. Check lines: RCT12\.h:570, RCT12\.h:259\. libopenrct2 RCT12\.h 570

```cpp
struct RCT12SpriteBase
{
  ....
  uint8_t flags;
  ....
};
struct rct1_peep : RCT12SpriteBase
{
  ....
  uint8_t flags;
  ....
};
```

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\.

**Warning N4**

[V793](https://pvs-studio.com/en/docs/warnings/v793/) It is odd that the result of the 'imageDirection / 8' statement is a part of the condition\. Perhaps, this statement should have been compared with something else\. libopenrct2 ObservationTower\.cpp 38

```cpp
void vehicle_visual_observation_tower(...., int32_t imageDirection, ....)
{
  if ((imageDirection / 8) && (imageDirection / 8) != 3)
  {
    ....
  }
  ....
}
```

Let's look at it in more detail\. The _imageDirection / 8_ expression will be false if _imageDirection_ is in the range from \-7 to 7\. Second part: _\(imageDirection / 8\) \!\= 3_ checks _imageDirection_ 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\.

**Warning N5**

[V587](https://pvs-studio.com/en/docs/warnings/v587/) An odd sequence of assignments of this kind: A \= B; B \= A;\. Check lines: 1115, 1118\. libopenrct2ui MouseInput\.cpp 1118

```cpp
void process_mouse_over(....)
{
  ....
  switch (window->widgets[widgetId].type)
  {
    case WWT_VIEWPORT:
      ebx = 0;
      edi = cursorId;                                 // <=
      // 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;                                 // <=
      if ((ebx & 0xFF) != 0)
      {
        set_cursor(cursorId);
        return;
      }
      break;
      ....
  }
  ....
}
```

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 _cursorId_ that also don't make much sense\.

**Warning N6**

[V1004](https://pvs-studio.com/en/docs/warnings/v1004/) \[CWE\-476\] The 'player' pointer was used unsafely after it was verified against nullptr\. Check lines: 2085, 2094\. libopenrct2 Network\.cpp 2094

```cpp
void Network::ProcessPlayerList()
{
  ....
  auto* player = GetPlayerByID(pendingPlayer.Id);
  if (player == nullptr)
  {
    // Add new player.
    player = AddPlayer("", "");
    if (player)                                          // <=
    {
      *player = pendingPlayer;
       if (player->Flags & NETWORK_PLAYER_FLAG_ISSERVER)
       {
         _serverConnection->Player = player;
       }
    }
    newPlayers.push_back(player->Id);                    // <=
  }
  ....
}
```

This code is quite simple to correct \- one either needs to check _player_ for a null pointer for the third time, or add it to the body of the conditional operator\. I would suggest the second option:

```cpp
void Network::ProcessPlayerList()
{
  ....
  auto* player = GetPlayerByID(pendingPlayer.Id);
  if (player == nullptr)
  {
    // Add new player.
    player = AddPlayer("", "");
    if (player)
    {
      *player = pendingPlayer;
      if (player->Flags & NETWORK_PLAYER_FLAG_ISSERVER)
      {
        _serverConnection->Player = player;
      }
      newPlayers.push_back(player->Id);
    }
  }
  ....
}
```

**Warning N7**

[V547](https://pvs-studio.com/en/docs/warnings/v547/) \[CWE\-570\] Expression 'name \=\= nullptr' is always false\. libopenrct2 ServerList\.cpp 102

```cpp
std::optional<ServerListEntry> ServerListEntry::FromJson(...)
{
  auto name = json_object_get(server, "name");
  .....
  if (name == nullptr || version == nullptr)
  {
    ....
  }
  else
  {
    ....
    entry.name = (name == nullptr ? "" : json_string_value(name));
    ....
  }
  ....
}
```

You can get rid of a hard\-to\-read line of code in one fell swoop and solve the problem with checking for _nullptr_\. I would change the code as follows:

```cpp
std::optional<ServerListEntry> ServerListEntry::FromJson(...)
{
  auto name = json_object_get(server, "name");
  .....
  if (name == nullptr || version == nullptr)
  {
    name = ""
    ....
  }
  else
  {
    ....
    entry.name = json_string_value(name);
    ....
  }
  ....
}
```

**Warning N8**

[V1048](https://pvs-studio.com/en/docs/warnings/v1048/) \[CWE\-1164\] The 'ColumnHeaderPressedCurrentState' variable was assigned the same value\. libopenrct2ui CustomListView\.cpp 510

```cpp
void CustomListView::MouseUp(....)
{
  ....
  if (!ColumnHeaderPressedCurrentState)
  {
    ColumnHeaderPressed = std::nullopt;
    ColumnHeaderPressedCurrentState = false;
    Invalidate();
  }
}
```

The code looks quite strange\. I think there was a typo either in the condition or when reassigning the _false_ value to the _ColumnHeaderPressedCurrentState_variable\.

## Conclusion

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\.