﻿# How the Carla car simulator helped us level up the static analysis of Unreal Engine 4 projects

One of the mechanisms of static analysis is method annotations of popular libraries\. Annotations provide more information about functions during errors detecting\. CARLA is an impressive open\-source project in C\+\+ that helped us implement this mechanism to our analyzer\. Subsequently, the simulator became a test\-target for the improved PVS\-Studio static analyzer\.

![0888_Carla/image2.jpg](https://import.viva64.com/docx/blog/0888_Carla/image2.jpg)

## Introduction

[CARLA](https://github.com/carla-simulator/carla) is an open\-source simulator for autonomous driving research\. CARLA has been developed from the ground up to support development, training, and validation of autonomous driving systems\. In addition to open\-source code and protocols, CARLA provides open digital assets \(urban layouts, buildings, vehicles\) that were created for this purpose and can be used freely\. The simulation platform supports flexible specification of sensor suites and environmental conditions\.

The project is cross\-platform and contains almost 78,000 lines of C\+\+ code\. In the project repository, we also found code written in Python, XML, YAML, DOS Batch, CMake and other languages\.

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

[Static code analysis](https://pvs-studio.com/en/blog/terms/0046/) is the process of detecting errors and defects in a software's source code\. Static analysis can be viewed as an automated code review process\. One of the [technologies](https://pvs-studio.com/en/blog/posts/cpp/0466/) used in static analysis is function annotations of popular libraries\. The developer studies the documentation of such functions and notes facts useful for analysis\. During the program check, the analyzer takes these facts from the annotations\. This approach allows the analysis to be carried out with higher accuracy\. 

The result of checking projects \- a report with warnings\. In [PVS\-Studio](https://pvs-studio.com/en/docs/manual/0007/), you can open the report in text editor or in the analyzer utility\. It's possible to open reports in software development tools, such as Visual Studio or CLion, but it requires the use of appropriate plugins\. Further the article will show you the top 10 errors found in the CARLA project\. You can also test your skills and try detecting them yourself\.

## Building and analysis

To manage the build process in Unreal Engine, use their custom build system \- Unreal Build Tool\. Therefore, the analysis of projects written on the Unreal Engine is performed in a special way\. There are two options for checking UE projects:

1. [analysis using Unreal Build Tool integration](https://pvs-studio.com/en/docs/manual/0043/);
1. [analysis using compiler monitoring](https://pvs-studio.com/en/docs/manual/0043/)\.

CARLA uses a modified Unreal Engine 4 kernel, which is also available on GitHub\. However, both the original and modified kernel have private access\. [Building on Windows](https://carla.readthedocs.io/en/0.9.12/build_windows/) consists of two stages: building the engine and building the project itself\. We will see how to analyze both\.

### Unreal Engine 4 Build

You can build Unreal Engine 4 in 8 steps\.

1. Register for an Epic Games account\.
1. Link your GitHub account to your Epic Games account\.
1. Accept an invitation to GitHub from Epic Games\. After that, you will get an access to the Unreal Engine repository\.
1. Download the modified kernel repository\.
1. Run the _Setup\.bat_ and _GenerateProjectFiles\.bat_ configuration scripts\. 
1. Open the _UE4\.sln_ solution generated in Visual Studio 2019\.
1. Select the _Development Editor_ configuration and the _Win64_ platform\.
1. Build the project\.

### Unreal Engine 4 analysis

To check the engine, integrate static analysis into the Unreal Build Tool assembly system\. To perform the analysis and get the check results, you need to perform the following steps\.

1. Install PVS\-Studio if you haven't done so\. Plugins for all versions of Visual Studio install automatically\.
1. In Visual Studio, open the Project Properties and go to the NMake tab\.
1. In the Build Command Line field, add _\-Staticanalyzer\=PVSStudio_ at the very end\. You can do the same for the Rebuild Command Line field\.
1. Build the project\.
1. In Visual Studio menu bar, select: Extensions \-\> PVS\-Studio \-\> Open/Save \-\> Open Analysis Report\.
1. In the explorer window that opens, select the _\*\\Engine\\Saved\\PVS\-Studio\\shadercompileworker\.pvslog_ file, where '\*' is the path to the Unreal Engine 4 folder\.

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

As a result, instead of the project building or rebuilding, PVS\-Studio performs the source code analysis\. Now let's build the CARLA simulator itself\.

### CARLA build and analysis

The project does not generate a solution\. This doesn't allow us to integrate into the Unreal Build Tool\. So, let's check the project through compiler monitoring\. There are two ways to do this: 

* use the command line utility \- _CLMonitoring\.exe_;
* use the _C and C\+\+ Compiler Monitoring UI_ IDE_\._

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

Both utilities are already in the _C:\\Program Files \(x86\)\\PVS\-Studio_ folder after installing [PVS\-Studio](https://pvs-studio.com/en/docs/manual/0007/)\. Let's use the second option \- C and C\+\+ Compiler Monitoring UI IDE\. To start build process, follow the steps:

1. Download the [project repository](https://github.com/carla-simulator/carla) from GitHub\.
1. Run _Update\.bat_ to download resources\. Unpack them using 7zip\.
1. Set the _UE4\_ROOT_ environment variable with the path value to the Unreal Engine kernel folder\.
1. Run _C and C\+\+ Compiler Monitoring UI_\. In the main menu, select _Tools \-\> Analyze your files \(C and C\+\+\)_\. In the window that opens, click _Start Monitoring_\. After that, another compiler monitoring window will appear\.
1. Open _x64 Native Tools Command Prompt for VS 2019_ and go to the folder where CARLA is located\.
1. Run the _make PythonAPI_ command to build the client\.
1. Run the _make launch_ command to build the server\. 
1. Click the _Stop Monitoring_ button in the compiler monitoring window\. Within seconds, the analysis based on the gathered information will start\. The report is downloaded automatically\.

To view the analyzer warnings easily, you can use Visual Studio\. Open the folder with the CARLA repository and download the report\. It may be useful to filter warnings issued on kernel files, autogenerated files and included library files\. To do this, perform a few more actions:

1. In _C and C\+\+ Compiler Monitoring UI_, in the menu bar, select _Save PVS\-Studio Log As_ and specify the path to save the report\.
1. In Visual Studio, in the menu bar, select _Extensions \-\> PVS\-Studio \-\> Open/Save \-\> Open Analysis Report_ and specify the same path as in the previous step\.
1. In Visual Studio, in the menu bar, select _Extensions \-\> PVS\-Studio \-\> Options_\.
1. In the window that opens, go to_ PVS\-Studio \-\> Don't Check Files_\.
1. Add the _\*\.gen\.\*_ mask to the _FileNameMasks_ group\.
1. Add the path to the Unreal Engine 4 folder to the _PathMasks_ group\.
1. Add the _\*\\Unreal\\CARLAUE4\\Plugins\\CARLA\\carladependencies\\include\\boost\\_ path to the _PathMasks_ group, where '\*' \- the path to the CARLA repository folder\.

Now let's study the analyzer warnings in Visual Studio\. Let's start with warnings issued on CARLA simulator code and their own libraries\.

![0888_Carla/image8.png](https://import.viva64.com/docx/blog/0888_Carla/image8.png)

We will view the errors found in the CARLA source files a little later\. The point is, we needed to check this project for another task\. Before testing the simulator, we slightly modified the PVS\-Studio kernel so that it collects statistics of Unreal Engine 4 method calls\. This data can now help us with annotating\. 

## Method annotation

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

Annotation is performed in two stages:

1.  studying library methods;
1.  recording useful facts about these methods in a special format that analyzer understands\.

At the next check of the project, information about the annotated methods that you encounter in the code will be obtained both from function signatures and annotations\. 

For example, an annotation may suggest that:

* a function parameter cannot be a null pointer \(for example, the first or second parameter of _strncat_\);
* a function parameter specifies the number of elements or the number of bytes \(for example, the third parameter of _strncat_\);
* two different parameters cannot receive the same value \(for example, the first and second parameters of _strncat_ \);
* a parameter is a pointer by which the memory allocated by the function will be returned;
* a return value of the function must be used \(for example, the _strcmp_ function\);
* a function has or does not have an internal state;
* a function can return _nullptr_ \(for example, the _malloc_ function\);
* a function returns a pointer or a reference to the data \(for example, the _std::string::c\_str_ function\);
* a function returns the iterator to a potentially invalid position \(for example, _std::find_\);
* a function frees some resource \(for example, the _std::basic\_string::clear_ function\);
* a function behaves like _memcpy_ \(for example, the _qMemCopy_ function\);
* and many more useful things\.

Which annotation would be the most useful? It's a good question\. Let's find out in the comments below\.

Annotations not only help to detect new errors, but also allow you to exclude some false positives\.

What did we need the CARLA simulator for? To take and annotate all the Unreal Engine 4 functions is a very large\-scale task\. It requires a lot of time\. Someday, maybe, we will power through it, but now we decided to start small and see the results\. In order not to take 200 random engine functions, we decided to identify the most popular ones\. We found a couple of large projects\. They are rather outdated Unreal Tournament game and the currently supported CARLA simulator\. The simulator in C\+\+ suited us for the following reasons:

* it's an open source project;
* it has an up\-to\-date kernel \(UE4 version 4\.27\);
* it's a large\-sized project \(according to the authors, it takes about 4 hours to complete the build\);
* it offers an easy build and a detailed tutorial\.

So, we selected the projects\. We successfully completed the build and checked the projects\. What's next? Now we need to collect statistics on functions calls of the game engine\. How to do that \- that is the question\. Fortunately, we have the analyzer source code at hand\. The analyzer builds a [parse tree](https://pvs-studio.com/en/blog/terms/0039/) and allows us to find function calls with all the necessary information\. So, it was enough to write something similar to a new diagnostic\. The function suited us if two conditions were met:

* a function is called from a file that belongs to the CARLA project;
* a function declaration must be in a file that belongs to Unreal Engine 4\.

If both conditions were met, information was recorded in a separate file\. All we had to do was run the analysis with a modified kernel\. After the analysis, we received a log of functions\. Then we applied some simple formulas in Excel and converted the statistics to the following form:

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

We decided that for a start it is enough to annotate all the functions that we encountered more than 10 times\. There were about 200 of them\. Since developers don't really like to document code, we had to study the implementation of each Unreal Engine 4 function in the source code to annotate it\. As an example, here is an annotation of the _ConstructUFunction_ function:

```cpp
C_"void ConstructUFunction(UFunction*& OutFunction, \
                           const FFunctionParams& Params)"
ADD(HAVE_STATE | RET_SKIP | F_ARG_ALLOC,
    "UE4CodeGen_Private",
    nullptr,
    "ConstructUFunction",
    ALLOC_ARG, SKIP);
```

The _F\_ARG\_ALLOC_ flag means that the function allocates the resource and gives it back through one of its parameters\. The _ALLOC\_ARG_ flag indicates that a pointer to the allocated resource is returned through the first parameter of the function, namely _OutFunction_\. The _SKIP_ flag says that the second argument of the function is not special and uninteresting for us\.

After we annotated all the functions, we double\-checked the CARLA simulator and the version of the engine the simulator uses\. As expected, some of the false positives disappeared and several new warnings appeared\.

**New warning N1**

[V611](https://pvs-studio.com/en/docs/warnings/v611/) The memory was allocated using 'new' operator but was released using the 'free' function\. Consider inspecting operation logics behind the 'Allocation' variable\. Check lines: 1746, 1786\. BulkData2\.cpp 1746

```cpp
void FBulkDataAllocation::SetMemoryMappedData(
  FBulkDataBase* Owner,
  IMappedFileHandle* MappedHandle,
  IMappedFileRegion* MappedRegion)
{
  ....
  FOwnedBulkDataPtr* Ptr
    = new FOwnedBulkDataPtr(MappedHandle, MappedRegion);      // <=

  Owner->SetRuntimeBulkDataFlags(BULKDATA_DataIsMemoryMapped);

  Allocation = Ptr;                                           // <=
}

void FBulkDataAllocation::Free(FBulkDataBase* Owner)
{
  if (!Owner->IsDataMemoryMapped())
  {
    FMemory::Free(Allocation);                                // <=
    Allocation = nullptr;
  }
  else { .... }
}
```

An object of the _FOwnedBulkDataPtr_type is created using the _new_ operator and released using the _Free_ function\. This last function calls _std::free_\. This can lead to undefined behavior\. The triggering appeared after we annotated the _FMemory::Free_ function\.

```cpp
C_"static void Free(void* Original)"
  ADD(HAVE_STATE_DONT_MODIFY_VARS | RET_SKIP,
      nullptr,
      "FMemory",
      "Free",
      POINTER_TO_FREE);
```

**New warning N2**

[V530](https://pvs-studio.com/en/docs/warnings/v530/) The return value of function 'CalcCacheValueSize' is required to be utilized\. MemoryDerivedDataBackend\.cpp 135

```cpp
void FMemoryDerivedDataBackend::PutCachedData(
  const TCHAR* CacheKey,
  TArrayView<const uint8> InData,
  bool bPutEvenIfExists)
{
  ....
  FString Key(CacheKey);
  ....
  FCacheValue* Val = new FCacheValue(InData);
  int32 CacheValueSize = CalcCacheValueSize(Key, *Val);

  // check if we haven't exceeded the MaxCacheSize
  if (   MaxCacheSize > 0
      && (CurrentCacheSize + CacheValueSize) > MaxCacheSize)
  {
    ....
  }
  else
  {
    COOK_STAT(Timer.AddHit(InData.Num()));
    CacheItems.Add(Key, Val);
    CalcCacheValueSize(Key, *Val);                            // <=

    CurrentCacheSize += CacheValueSize;
  }
}
```

The return value of the _CalcCacheValueSize_ method was not used\. According to the analyzer, calling this method with no return value is senseless\. Analyzer has information about the signatures of the _CalcCacheValueSize_ method and its implementation, that's why it realized that the function has no state\. Neither arguments, nor class properties, nor any other variables change\. This became clear since annotated methods were used inside the _CalcCacheValueSize_ function\. A senseless function call may indicate a possible error in the program logic\.

**New warning N3**

[V630](https://pvs-studio.com/en/docs/warnings/v630/) The 'Malloc' function is used to allocate memory for an array of objects which are classes containing constructors\. UnrealNames\.cpp 639

```cpp
class alignas(PLATFORM_CACHE_LINE_SIZE) FNamePoolShardBase : FNoncopyable
{
public:
  void Initialize(FNameEntryAllocator& InEntries)
  {
    LLM_SCOPE(ELLMTag::FName);
    Entries = &InEntries;

    Slots = (FNameSlot*)FMemory::Malloc(
      FNamePoolInitialSlotsPerShard * sizeof(FNameSlot), alignof(FNameSlot));
    memset(Slots, 0, FNamePoolInitialSlotsPerShard * sizeof(FNameSlot));
    CapacityMask = FNamePoolInitialSlotsPerShard - 1;
  }
....
}
```

The _FNameSlot_ type objects are created without existing constructor call\. The annotation of the _Malloc_ function gives a hint\. The annotation states that the _Malloc_ function only allocates memory, and the size of the allocated memory block is specified in the first argument\. This code fragment is suspicious and may lead to errors\.

Thus, the Unreal Engine method annotations allows you to detect new errors\. And now let's look at the check results of the CARLA simulator\.

## Check results

![0888_Carla/image12.png](https://import.viva64.com/docx/blog/0888_Carla/image12.png)

**Warning N1**

[V522](https://pvs-studio.com/en/docs/warnings/v522/) Dereferencing of the null pointer 'CarlaActor' might take place\. CarlaServer\.cpp 1652

```cpp
void FCarlaServer::FPimpl::BindActions()
{
  ....
  FCarlaActor* CarlaActor = Episode->FindCarlaActor(ActorId);
  if (CarlaActor)
  {
    return RespondError("get_light_boxes",
                        ECarlaServerResponse::ActorNotFound,
                        " Actor Id: " + FString::FromInt(ActorId));
  }
  if (CarlaActor->IsDormant())
  {
    return RespondError("get_light_boxes",
                        ECarlaServerResponse::FunctionNotAvailiableWhenDormant,
                        " Actor Id: " + FString::FromInt(ActorId));
  }
  else { .... }
  ....
}
```

One lost exclamation mark \- and the function completely changes its behavior\. Now, if _CarlaActor_ is valid, an error is thrown\. And if it is _nullptr_, the function leads to undefined behavior, which may be an abnormal program termination\.

**Warning N2**

The analyzer issued a similar warning in another function\.

[V522](https://pvs-studio.com/en/docs/warnings/v522/) Dereferencing of the null pointer 'HISMCompPtr' might take place\. ProceduralBuilding\.cpp 32

```cpp
UHierarchicalInstancedStaticMeshComponent* AProceduralBuilding::GetHISMComp(
    const UStaticMesh* SM)
{
  ....
  UHierarchicalInstancedStaticMeshComponent** HISMCompPtr =
    HISMComps.Find(SMName);

  if (HISMCompPtr) return *HISMCompPtr;

  UHierarchicalInstancedStaticMeshComponent* HISMComp = *HISMCompPtr;

  // If it doesn't exist, create the component
  HISMComp = NewObject<UHierarchicalInstancedStaticMeshComponent>(this,
    FName(*FString::Printf(TEXT("HISMComp_%d"), HISMComps.Num())));
  HISMComp->SetupAttachment(RootComponent);
  HISMComp->RegisterComponent();
  ....
}
```

When the search for _SMName_ in _HISMComps_ is a success, the _GetHISMComp_ method returns the found element\. Otherwise, the _HISMCompPtr_ contains null pointer and dereference occurs\. This causes undefined behavior\. Most likely, initialization in the _HISMComp_ definition was unnecessary\. Immediately after, _HISMComp_ receives new value\.

**Warning N3**

[V547](https://pvs-studio.com/en/docs/warnings/v547/) Expression 'm\_trail \=\= 0' is always false\. unpack\.hpp 699

```cpp
std::size_t m_trail; 
....
inline int context::execute(const char* data, std::size_t len,
 std::size_t& off)
{
  ....
  case MSGPACK_CS_EXT_8: {
                uint8_t tmp;
                load<uint8_t>(tmp, n);
                m_trail = tmp + 1;
                if(m_trail == 0) {
                    unpack_ext(m_user, n, m_trail, obj);
                    int ret = push_proc(obj, off);
                    if (ret != 0) return ret;
                }
                else {
                    m_cs = MSGPACK_ACS_EXT_VALUE;
                    fixed_trail_again = true;
                }
            } break;
  ....
}
```

The _tmp_ variable has the _uint8\_t_ type, which means its value ranges from _0_ to _255_\. The _m\_trail_ variable is in the range from _1_ to _256_ because of integer promotion of the _tmp_ variable\. Since the _m\_trail_ in the condition cannot equal _0_, instructions in the condition body are never executed\. Such code can be redundant or not corresponding to the author's intents\. It needs checking\.

The analyzer found several more similar code fragments:

* V547 Expression 'm\_trail \=\= 0' is always false\. unpack\.hpp 741
* V547 Expression 'm\_trail \=\= 0' is always false\. unpack\.hpp 785
* V547 Expression 'm\_trail \=\= 0' is always false\. parse\.hpp 472
* V547 Expression 'm\_trail \=\= 0' is always false\. parse\.hpp 514
* V547 Expression 'm\_trail \=\= 0' is always false\. parse\.hpp 558

**Warning N4**

A very similar situation occurred in another function\.

[V547](https://pvs-studio.com/en/docs/warnings/v547/) Expression '\(uint8\) WheelLocation \>\= 0' is always true\. Unsigned type value is always \>\= 0\. CARLAWheeledVehicle\.cpp 510

```cpp
float ACarlaWheeledVehicle::GetWheelSteerAngle(
  EVehicleWheelLocation WheelLocation) {

  check((uint8)WheelLocation >= 0)
  check((uint8)WheelLocation < 4)
  ....
}
```

Some _check_ function takes the _bool_ type value as its argument\. The function throws an exception if the false value is passed\. In the first check, the expression always has the true value, since the _uint8_ type has a range from _0_ to _255_\. Probably, there is a typo in the check contents\. The exact same check is in 524 line\.

**Warning N5**

[V547](https://pvs-studio.com/en/docs/warnings/v547/) Expression 'rounds \> 1' is always true\. CarlaExporter\.cpp 137

```cpp
void FCarlaExporterModule::PluginButtonClicked()
{
  ....
  int rounds;
  rounds = 5;
  ....
  for (int round = 0; round < rounds; ++round)
  {
    for (UObject* SelectedObject : BP_Actors)
    {
      ....
      // check to export in this round or not
      if (rounds > 1)                                          // <=
      {
        if (areaType == AreaType::BLOCK && round != 0)
          continue;
        else if (areaType == AreaType::ROAD && round != 1)
          continue;
        else if (areaType == AreaType::GRASS && round != 2)
          continue;
        else if (areaType == AreaType::SIDEWALK && round != 3)
          continue;
        else if (areaType == AreaType::CROSSWALK && round != 4)
          continue;
      }
      ....
    }
  }
}
```

It's clearly a typo\. Instead of _round_ a developer wrote _rounds_\. It's easy to make a mistake in one letter, especially at the end of the tough workday\. We are all human and we get tired\. But a static code analyzer is a program, and it always works with the same vigilance\. So, it's good to have such a tool at hand\. Let me dilute the continuous code with a picture with simulator graphics\.

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

**Warning N6**

[V612](https://pvs-studio.com/en/docs/warnings/v612/) An unconditional 'return' within a loop\. EndPoint\.h 84

```cpp
static inline auto make_address(const std::string &address) {
  ....
  boost::asio::ip::tcp::resolver::iterator iter = resolver.resolve(query);
  boost::asio::ip::tcp::resolver::iterator end;
  while (iter != end)
  {
    boost::asio::ip::tcp::endpoint endpoint = *iter++;
    return endpoint.address();
  }
  return boost::asio::ip::make_address(address);
}
```

The _while_ loop, the condition, the iterator increment \- all that shows that the instructions in the block must be executed more than once\. However, due to _return_, only one iteration is performed\. Surely there must be another logic here, otherwise the loop can be eliminated\.

**Warning N7**

[V794](https://pvs-studio.com/en/docs/warnings/v794/) The assignment operator should be protected from the case of 'this \=\= &other'\. cpp11\_zone\.hpp 92

```cpp
struct finalizer_array
{
  void call() {
    finalizer* fin = m_tail;
    for(; fin != m_array; --fin) (*(fin-1))();
  }
  ~finalizer_array() {
     call();
     ::free(m_array);
  }
  finalizer_array& operator=(finalizer_array&& other) noexcept
  {
    this->~finalizer_array();                                // <=
    new (this) finalizer_array(std::move(other));
    return *this;
  }
  finalizer_array(finalizer_array&& other) noexcept
    : m_tail(other.m_tail), m_end(other.m_end), m_array(other.m_array)
  {
    other.m_tail = MSGPACK_NULLPTR;
    other.m_end = MSGPACK_NULLPTR;
    other.m_array = MSGPACK_NULLPTR;
  }
  ....
  finalizer* m_tail;
  finalizer* m_end;
  finalizer* m_array;
}
```

The analyzer detected an overloaded assignment operator, where _this \=\= &other_ lacks a check\. Calling a destructor via _this_ pointer results in the loss of _other_ data\. Subsequently, the assignment operator returns a copy of the cleaned object\. The analyzer issued several more warnings that could be potential errors:

* V794 The assignment operator should be protected from the case of 'this \=\= &other'\. cpp11\_zone\.hpp 154
* V794 The assignment operator should be protected from the case of 'this \=\= &other'\. unpack\.hpp 1093
* V794 The assignment operator should be protected from the case of 'this \=\= &other'\. create\_object\_visitor\.hpp 44
* V794 The assignment operator should be protected from the case of 'this \=\= &other'\. parse\.hpp 821
* V794 The assignment operator should be protected from the case of 'this \=\= &other'\. sbuffer\.hpp 55

**Warning N8**

[V1030](https://pvs-studio.com/en/docs/warnings/v1030/) The 'signals' variable is used after it was moved\. MapBuilder\.cpp 926

```cpp
void MapBuilder::CreateController(....,
  const std::set<road::SignId>&& signals) 
{
   ....
    // Add the signals owned by the controller
    controller_pair.first->second->_signals = std::move(signals);

    // Add ContId to the signal owned by this Controller
    auto& signals_map = _map_data._signals;
    for(auto signal: signals) {                         // <=
      auto it = signals_map.find(signal);
      if(it != signals_map.end()) {
        it->second->_controllers.insert(signal);
      }
    }
}
```

The signals container will become empty after moving, and the range\-based for loop will not execute\. One of the right approaches would be to use _controller\_pair\.first\-\>second\-\>\_signals_: 

```cpp
for (auto signal: controller_pair.first->second->_signals)
```

However, it would be correct, except for one thing\. The _signals_ container has a _const_ specifier, which means it cannot be moved\. Instead, it is copied, and therefore the program logically works correctly\. A developer who wanted to optimize the code was able to confuse both himself and the analyzer\. Kudos to him for this code\. For the V1030 diagnostic fine\-tuning, we will take this situation into account\. Maybe we will write a new diagnostic\.

**Warning N9**

[V1061](https://pvs-studio.com/en/docs/warnings/v1061/) Extending the 'std' namespace may result in undefined behavior\. Waypoint\.cpp 11

Let's look at two code snippets from the _Waypoint\.h_ and _Waypoint\.cpp_ files:

```cpp
// Waypoint.h
namespace std {

  template <>
  struct hash<carla::road::element::Waypoint> {

    using argument_type = carla::road::element::Waypoint;

    using result_type = uint64_t;

    result_type operator()(const argument_type& waypoint) const;

  };

} // namespace std

// Waypoint.cpp
namespace std {

  using WaypointHash = hash<carla::road::element::Waypoint>;  // <=

  WaypointHash::result_type WaypointHash::operator()(
    const argument_type &waypoint) const
  {
    WaypointHash::result_type seed = 0u;
    boost::hash_combine(seed, waypoint.road_id);
    boost::hash_combine(seed, waypoint.section_id);
    boost::hash_combine(seed, waypoint.lane_id);
    boost::hash_combine(seed,
                        static_cast<float>(std::floor(waypoint.s * 200.0)));
    return seed;
  }

} // namespace std
```

In the header file, the developer extends the _std_ namespace by declaring the explicit template specialization of the _hash_ class in order to work with the _carla::road::element::Waypoint_ type\. In the file _Waypoint\.cpp_, the developer adds the _WaypointHash_ alias and the definition of the _operator\(\)_ function to the _std_ namespace\.

The C\+\+ standard forbids extending the _std_ namespace\. The contents of the _'std'_ namespace are defined solely by the C\+\+ Standards Committee and changed depending on the C\+\+ language version\. Modifying namespace's content may result in undefined behavior\. However, adding an explicit or partial template specialization, as in the _Waypoint\.h_ file, is an exception\. The V1061 diagnostic says that the definition of the _operator\(\)_ function in the _Waypoint\.cpp_ file is permitted, but the alias declaration in the _std_ namespace is prohibited\.

Actually, it is not necessary to extend the _std_ namespace this way\. It is enough to add the _std::hash_ template specialization for a user type outside of _std_ \([yes, it is possible](https://en.cppreference.com/w/cpp/language/extending_std)\):

```cpp
// Waypoint.h
// Not inside namespace "std"
template <>
struct std::hash<carla::road::element::Waypoint> {....};

// Waypoint.cpp
// Not inside namespace "std"
using WaypointHash = std::hash<CARLA::road::element::Waypoint>;

WaypointHash::result_type WaypointHash::operator()(
  const WaypointHash::argument_type& waypoint) const {....}
```

**Warning N10**

![0888_Carla/image14.png](https://import.viva64.com/docx/blog/0888_Carla/image14.png)

I left one interesting error for last\. I encourage you to find it yourself\. Unlike the others, this error is from the engine of Unreal Engine 4 game itself\.

```cpp
 virtual void visit(ir_variable *var)
  {
    ....
    const bool bBuiltinVariable = (var->name && 
                                   strncmp(var->name, "gl_", 3) == 0);
    
    if (bBuiltinVariable && ShaderTarget == vertex_shader && 
                            strncmp(var->name, "gl_InstanceID", 13) == 0)
    {
      bUsesInstanceID = true;
    }

    if (bBuiltinVariable &&
      var->centroid == 0 && (var->interpolation == 0 || 
                             strncmp(var->name, "gl_Layer", 3) == 0) &&
      var->invariant == 0 && var->origin_upper_left == 0 &&
      var->pixel_center_integer == 0)
    {
      // Don't emit builtin GL variable declarations.
      needs_semicolon = false;
    }
    else if (scope_depth == 0 && var->mode == ir_var_temporary)
    {
      global_instructions.push_tail(new(mem_ctx) global_ir(var));
      needs_semicolon = false;
    }
    else {....}
    ....
}
```

Here are two hints for you:

1. the warning is issued with help of the method annotation\.
1. the warning is issued by the [V666](https://pvs-studio.com/en/docs/warnings/v666/) diagnostic\.

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

[V666](https://pvs-studio.com/en/docs/warnings/v666/) Consider inspecting third argument of the function 'strncmp'\. It is possible that the value does not correspond with the length of a string which was passed with the second argument\. GlslBackend\.cpp 943

Error in the _strncmp_ function call:

```cpp
strncmp(var->name, "gl_Layer", 3)
```

As the third argument of the function the number of characters to compare is passed, and as the second one \- a string literal\. The analyzer database has an annotation of the standard _strncmp _function, which says that the number of characters should probably match the the string literal length\. In addition, for earlier calls of the _strncmp_ function, the number of characters did coincide with the length of the string literal\. However, in the code snippet above, the function compares only part of the string\. The check of

```cpp
strncmp(var->name, "gl_Layer", 3) == 0
```

is senseless, since _bBuiltinVariable_ already contains the result of the same check:

```cpp
strncmp(var->name, "gl_", 3) == 0
```

Most likely, the function call should've looked like this:

```cpp
strncmp(var->name, "gl_Layer", 8)
```



## Conclusion

The CARLA simulator is not only an entertaining and useful Unreal Engine 4 project, but it's also a high\-quality product\. The use of static analysis decreases the time spent on application development and debugging, and function annotations help perform more accurate analysis\. We thank authors of this wonderful project for the opportunity to study the source code\.

You can read more about static analysis in video game development and view the top 10 software bugs [here](https://pvs-studio.com/en/blog/posts/cpp/0570/)\.

![0888_Carla/image16.png](https://import.viva64.com/docx/blog/0888_Carla/image16.png)

Like other C\+\+ software tools, static code analyzers never stay still for long and are continuously evolving\. You may find interesting our latest [article](https://pvs-studio.com/en/blog/posts/cpp/0873/) on C\+\+ tools evolution\. Check it out\!