The long-awaited check of Unreal Engine 4

    Unreal Engine 4 and PVS-Studio

    March 19, 2014 Unreal Engine 4 became available to everyone. Subscription price is only $ 19 per month. Source codes are also posted on the github repository. From this moment we received a lot of messages in the mail, on Twitter and so on, with a request to check this game engine. We satisfy the request of our readers. Let's see what interesting can be found in the source code using the PVS-Studio static code analyzer.



    Unreal engine


    Unreal Engine is a game engine developed and maintained by Epic Games. It is written in C ++. Allows you to create games for most operating systems and platforms: Microsoft Windows, Linux, Mac OS and Mac OS X, Xbox, Xbox 360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Dreamcast and Nintendo GameCube consoles.

    Official website: https://www.unrealengine.com/

    Description on the Wikipedia website: Unreal Engine .

    Method for checking nmake-based project


    Unreal Engine project verification is not so simple. To test this project, we had to use a new opportunity that we recently implemented in PVS-Studio Standalone. Because of this, we had to delay the publication of the article a bit. We decided to publish it after the release of PVS-Studio, where this new feature will already be. Many may want to try it. It greatly facilitates the verification of projects where complex or non-standard assembly systems are used.

    The classic principle of PVS-Studio is as follows:
    • You open the project in Visual Studio.
    • Click the "check" button.
    • The plugin integrated into Visual Studio collects all the necessary information: which files should be checked, which macros to use, where the header files are and so on.
    • The plugin launches the analyzer itself and displays the results.
    The nuance is that Unreal Engine 4 is a nmake-based project, so you cannot verify it using the PVS-Studio plugin.

    I will explain. There is a project for the Visual Studio environment. But the build is done using nmake. This means that the plugin does not know which files and with which keys are compiled. Accordingly, analysis is not possible. Rather, analysis is possible, but the process is time consuming (see the documentation section: " Direct integration of the analyzer into assembly automation systems ").

    And here PVS-Studio Standalone comes to the rescue! He can work in two ways:
    1. You somehow generate the preprocessed files, and it will check them already.
    2. It tracks compiler launches and “spies” all the necessary information.
    Now we are interested in the second use case. Here's how the Unreal Engine test went:
    1. Launched PVS-Studio Standalone.
    2. Clicked the “Compiler Monitoring” button.
    3. Clicked on the “Start Monitoring” button. They saw that the mode of monitoring compiler calls was turned on.
    4. Opened the Unreal Engine project in Visual Studio. We started the assembly of the project. At the same time, in the monitoring window, you can see that the compiler calls are intercepted.
    5. When the build in Visual Studio was over, we clicked the Stop Monitoring button. After that, the PVS-Studio analyzer started working.
    As a result, diagnostic messages appear in the PVS-Studio Standalone utility window.

    Hint. For convenience, it is better to use Visual Studio rather than the editor built into PVS-Studio Standalone. It is enough to save the analysis results to a file, and then open it from the Visual Studio environment (Menu-> PVS-Studio-> Open / Save-> Open Analysis Report).

    All this and much more is described in the article “ PVS-Studio now supports any build system on Windows and any compiler. Easy and out of the box . Please be sure to read this article before starting experiments with PVS-Studio Standalone!

    Validation Results


    The Unreal Engine project code seemed very high quality to me. For example, developers use static code analysis in their development. This is indicated by such fragments in the code as:
    // Suppress static code analysis warning about a
    // potential comparison of two constants
    CA_SUPPRESS(6326);
    ....
    // Suppress static code analysis warnings about a
    // potentially ill-defined loop. BlendCount > 0 is valid.
    CA_SUPPRESS(6294)
    ....
    #if USING_CODE_ANALYSIS

    Judging by these code fragments, a static code analyzer is used, which is built into Visual Studio. About this code analyzer: Visual Studio 2013 Static Code Analysis in depth: What? When and how?

    Other analyzers may be used, but I don’t know anything about it.

    So, the code is written very high quality. When working, static code analysis tools are used. Therefore, having tested the project using PVS-Studio, we found few suspicious code fragments. However, as with any large project, there are errors in it. And some of them are able to find PVS-Studio. Let's see what managed to notice the suspicious.

    Typos


    static bool PositionIsInside(....)
    {
      return
        Position.X >= Control.Center.X - BoxSize.X * 0.5f &&
        Position.X <= Control.Center.X + BoxSize.X * 0.5f &&
        Position.Y >= Control.Center.Y - BoxSize.Y * 0.5f &&
        Position.Y >= Control.Center.Y - BoxSize.Y * 0.5f;
    }

    PVS-Studio warning: V501 There are identical sub-expressions 'Position.Y> = Control.Center.Y - BoxSize.Y * 0.5f' to the left and to the right of the '&&' operator. svirtualjoystick.cpp 97

    Note that the variable “Position.Y” is compared twice with the expression “Control.Center.Y - BoxSize.Y * 0.5f”. This is clearly a typo. In the last line, replace the '-' operator with the '+' operator.

    Another similar typo in the condition:
    void FOculusRiftHMD::PreRenderView_RenderThread(
      FSceneView& View)
    {
      ....
      if (View.StereoPass == eSSP_LEFT_EYE ||
          View.StereoPass == eSSP_LEFT_EYE)
      ....
    }

    PVS-Studio Warning: V501 There are identical sub-expressions 'View.StereoPass == eSSP_LEFT_EYE' to the left and to the right of the '||' operator. oculusrifthmd.cpp 1453

    Apparently, working with Oculus Rift has not yet been very tested.

    Let's continue.
    struct FMemoryAllocationStats_DEPRECATED
    {
      ....
      SIZE_T  NotUsed5;
      SIZE_T  NotUsed6;
      SIZE_T  NotUsed7;
      SIZE_T  NotUsed8;
      ....
    };
    FMemoryAllocationStats_DEPRECATED()
    {
      ....
      NotUsed5 = 0;
      NotUsed6 = 0;
      NotUsed6 = 0;  
      NotUsed8 = 0;  
      ....
    }

    PVS-Studio Warning: V519 The 'NotUsed6' variable is assigned values ​​twice successively. Perhaps this is a mistake. Check lines: 86, 88. memorybase.h 88

    Structure members are initialized. Due to a typo, the 'NotUsed6' member is initialized twice. And the member 'NotUsed7' remained uninitialized. However, the suffix _DEPRECATED () in the function name says that this is already uninteresting code.

    A couple more places where one variable is assigned a value twice:
    • V519 The 'HighlightText' variable is assigned values ​​twice successively. Perhaps this is a mistake. Check lines: 204, 206. srichtextblock.cpp 206
    • V519 The 'TrackError.MaxErrorInScaleDueToScale' variable is assigned values ​​twice successively. Perhaps this is a mistake. Check lines: 1715, 1716. animationutils.cpp 1716

    Null pointers


    Very often I come across dereferencing a null pointer in error handlers. No wonder. Such fragments are difficult and not interesting to test. You can meet the dereferencing of the null pointer in the error handler in the Unreal Engine project:
    bool UEngine::CommitMapChange( FWorldContext &Context )
    {
      ....
      LevelStreamingObject = Context.World()->StreamingLevels[j];
      if (LevelStreamingObject != NULL)
      {
        ....
      }
      else
      {
        check(LevelStreamingObject);
        UE_LOG(LogStreaming, Log,
               TEXT("Unable to handle streaming object %s"),
               *LevelStreamingObject->GetName());
      }
      ....
    }

    PVS-Studio Warning: V522 Dereferencing of the null pointer 'LevelStreamingObject' might take place. unrealengine.cpp 10768

    If an error occurs, then I want to print the name of the object. Just this object does not exist.

    Consider another fragment where the null pointer will be dereferenced. Everything is more interesting here. Perhaps the error is due to the wrong merge. In any case, the comment shows that the code is not completed:
    void FStreamingPause::Init()
    {
      ....
      if( GStreamingPauseBackground == NULL && GUseStreamingPause )
      {
        // @todo UE4 merge andrew
        // GStreamingPauseBackground = new FFrontBufferTexture(....);
        GStreamingPauseBackground->InitRHI();
      }
    }

    PVS-Studio Warning: V522 Dereferencing of the null pointer 'GStreamingPauseBackground' might take place. streamingpauserendering.cpp 197

    More on null pointers


    In almost any program, I come across a ton of warnings with the V595 code ( examples ). These warnings indicate the following situation:

    First, the pointer is dereferenced. Below this pointer is checked for equality to zero. This is far from always a mistake. But this is a very suspicious code, and it must be checked!

    Diagnostics V595 allows you to identify these blunders:
    /**
     * Global engine pointer.
     * Can be 0 so don't use without checking.
     */
    ENGINE_API UEngine* GEngine = NULL;
    bool UEngine::LoadMap( FWorldContext& WorldContext,
      FURL URL, class UPendingNetGame* Pending, FString& Error )
    {
      ....
      if (GEngine->GameViewport != NULL)
      {
        ClearDebugDisplayProperties();
      }
      if( GEngine )
      {
        GEngine->WorldDestroyed( WorldContext.World() );
      }
      ....
    }

    PVS-Studio Warning: V595 The 'GEngine' pointer was utilized before it was verified against nullptr. Check lines: 9714, 9719. unrealengine.cpp 9714

    Pay attention to the comment. The global variable GEngine can be zero. Before using it, it must be checked.

    The LoadMap () function does have a check like this:
    if( GEngine )

    Only here is bad luck. The check is located after the pointer has been used:
    if (GEngine->GameViewport != NULL)

    V595 warnings quite a lot (82 pieces). I think many of them will turn out to be false. Therefore, I will not litter the article with messages and list them separately: ue-v595.txt .

    Extra variable declaration


    Consider a beautiful mistake. It is due to the fact that a new variable is accidentally created, and not the old one is used.
    void FStreamableManager::AsyncLoadCallback(....)
    {
      ....
      FStreamable* Existing = StreamableItems.FindRef(TargetName);
      ....
      if (!Existing)
      {
        // hmm, maybe it was redirected by a consolidate
        TargetName = ResolveRedirects(TargetName);
        FStreamable* Existing = StreamableItems.FindRef(TargetName);
      }
      if (Existing && Existing->bAsyncLoadRequestOutstanding)
      ....
    }

    PVS-Studio Warning: V561 It's probably better to assign value to 'Existing' variable than to declare it anew. Previous declaration: streamablemanager.cpp, line 325. streamablemanager.cpp 332

    As I understand it, in fact, it should be written like this:
    // hmm, maybe it was redirected by a consolidate
    TargetName = ResolveRedirects(TargetName);
    Existing = StreamableItems.FindRef(TargetName);

    Errors when calling functions


    bool FRecastQueryFilter::IsEqual(
      const INavigationQueryFilterInterface* Other) const
    {
      // @NOTE: not type safe, should be changed when
      // another filter type is introduced
      return FMemory::Memcmp(this, Other, sizeof(this)) == 0;
    }

    PVS-Studio Warning: V579 The Memcmp function receives the pointer and its size as arguments. It is possibly a mistake. Inspect the third argument. pimplrecastnavmesh.cpp 172

    Comment warns that using Memcmp () is dangerous. But, in fact, it’s even worse than the programmer thinks. The fact is that a function compares only a part of an object.

    The sizeof (this) operator returns the size of the pointer. That is, in a 32-bit program, the function compares the first 4 bytes. In a 64-bit program, 8 bytes will be compared.

    The correct code is:
    return FMemory::Memcmp(this, Other, sizeof(*this)) == 0;

    The misadventures with the Memcmp () function do not end there. Consider the following code snippet:
    D3D11_STATE_CACHE_INLINE void GetBlendState(
      ID3D11BlendState** BlendState, float BlendFactor[4],
      uint32* SampleMask)
    {
      ....
      FMemory::Memcmp(BlendFactor, CurrentBlendFactor,
                      sizeof(CurrentBlendFactor));
      ....
    }

    PVS-Studio Warning: V530 The return value of function 'Memcmp' is required to be utilized. d3d11statecacheprivate.h 547

    The analyzer was surprised to see that the result of the Memcmp () function was not used. And indeed, this is a mistake. As I understand it, they wanted to copy here, not compare data. Then you need to use the Memcpy () function:
    FMemory::Memcpy(BlendFactor, CurrentBlendFactor,
                    sizeof(CurrentBlendFactor));

    The variable is assigned to itself.


    enum ECubeFace;
    ECubeFace CubeFace;
    friend FArchive& operator<<(
      FArchive& Ar,FResolveParams& ResolveParams)
    {
      ....
      if(Ar.IsLoading())
      {
        ResolveParams.CubeFace = (ECubeFace)ResolveParams.CubeFace;
      }
      ....
    }

    PVS-Studio Warning: V570 The 'ResolveParams.CubeFace' variable is assigned to itself. rhi.h 1279 The

    variable 'ResolveParams.CubeFace' is of type ECubeFace. The explicit cast of the variable to the ECubeFace type is used. That is, nothing happens. Then the variable is assigned to itself. There is something wrong with this code.

    The most beautiful of the found errors


    Like

    Most of all, I liked this error:
    bool VertInfluencedByActiveBone(
      FParticleEmitterInstance* Owner,
      USkeletalMeshComponent* InSkelMeshComponent,
      int32 InVertexIndex,
      int32* OutBoneIndex = NULL);
    void UParticleModuleLocationSkelVertSurface::Spawn(....)
    {
      ....
      int32 BoneIndex1, BoneIndex2, BoneIndex3;
      BoneIndex1 = BoneIndex2 = BoneIndex3 = INDEX_NONE;
      if(!VertInfluencedByActiveBone(
            Owner, SourceComponent, VertIndex[0], &BoneIndex1) &&
         !VertInfluencedByActiveBone(
            Owner, SourceComponent, VertIndex[1], &BoneIndex2) && 
         !VertInfluencedByActiveBone(
            Owner, SourceComponent, VertIndex[2]) &BoneIndex3)
      {
      ....
    }

    PVS-Studio Warning: V564 The '&' operator is applied to bool type value. You've probably forgotten to include parentheses or intended to use the '&&' operator. particlemodules_location.cpp 2120

    Noticing an error is not easy. I am sure that you skimmed through the code and did not see anything suspicious in it. The analyzer warning, unfortunately, is also strange and seems to be a false positive. Meanwhile, we are dealing with a very interesting mistake.

    Let's figure it out. Note that the final argument to the VertInfluencedByActiveBone () function is optional.

    In the code, the VertInfluencedByActiveBone () function is called 3 times. Two times 4 arguments are passed to her. In the latter case, there are only 3 arguments. This is the mistake.

    Thanks to luck, the code compiles and the error is invisible. Here is the result:
    1. A function with 3 arguments is called: "VertInfluencedByActiveBone (Owner, SourceComponent, VertIndex [2])";
    2. The operator '!' Is applied to the result of the function;
    3. The result of the expression "! VertInfluencedByActiveBone (...)" is of type bool;
    4. The '&' operator (bitwise AND) is applied to the result;
    5. Everything compiles successfully, since an expression of type bool is to the left of the '&' operator. To the right of '&' is the integer variable BoneIndex3.
    The analyzer suspected something was wrong when it saw that one of the arguments to the & operator was a type of 'bool'. He reported this. And not in vain.

    To fix the error, add a comma and place the closing bracket in another place:
    if(!VertInfluencedByActiveBone(
          Owner, SourceComponent, VertIndex[0], &BoneIndex1) &&
       !VertInfluencedByActiveBone(
          Owner, SourceComponent, VertIndex[1], &BoneIndex2) && 
       !VertInfluencedByActiveBone(
          Owner, SourceComponent, VertIndex[2], &BoneIndex3))

    The break statement is forgotten


    static void VerifyUniformLayout(....)
    {
      ....
      switch(Member.GetBaseType())
      {
        case UBMT_STRUCT:  BaseTypeName = TEXT("struct"); 
        case UBMT_BOOL:    BaseTypeName = TEXT("bool"); break;
        case UBMT_INT32:   BaseTypeName = TEXT("int"); break;
        case UBMT_UINT32:  BaseTypeName = TEXT("uint"); break;
        case UBMT_FLOAT32: BaseTypeName = TEXT("float"); break;
        default:           
          UE_LOG(LogShaders, Fatal,
            TEXT("Unrecognized uniform ......"));
      };
      ....
    }

    PVS-Studio Warning: V519 The 'BaseTypeName' variable is assigned values ​​twice successively. Perhaps this is a mistake. Check lines: 862, 863. openglshaders.cpp 863

    At the very beginning, the “break;” operator is forgotten. I think comments and explanations are unnecessary.

    Microoptimization


    PVS-Studio analyzer has a small set of diagnostic rules that help to make small optimizations in the code. However, sometimes they are very useful. Consider one of the assignment operators as an example:
    FVariant& operator=( const TArray InArray )
    {
      Type = EVariantTypes::ByteArray;
      Value = InArray;
      return *this;
    }

    Warning PVS-Studio: V801 Decreased performance. It is better to redefine the first function argument as a reference. Consider replacing 'const ... InArray' with 'const ... & InArray'. variant.h 198

    It is not a good idea to pass an array by value. The array 'InArray' can and should be passed using a constant reference.

    The analyzer generates quite a few warnings related to microoptimizations. Not all of them will be useful, but just in case, I’ll list them: ue-v801-V803.txt .

    Suspicious amount


    uint32 GetAllocatedSize() const
    {
      return UniformVectorExpressions.GetAllocatedSize()
        + UniformScalarExpressions.GetAllocatedSize()
        + Uniform2DTextureExpressions.GetAllocatedSize()
        + UniformCubeTextureExpressions.GetAllocatedSize()
        + ParameterCollections.GetAllocatedSize()
        + UniformBufferStruct
            ?
            (sizeof(FUniformBufferStruct) +
             UniformBufferStruct->GetMembers().GetAllocatedSize())
            :
            0;
    }

    Warning PVS-Studio: V502 Perhaps the '?:' Operator works in a different way than it was expected. The '?:' Operator has a lower priority than the '+' operator. materialshared.h 224

    The code is very complex. To clarify what is wrong, I will make a simplified artificial example:
    return A() + B() + C() + uniform ? UniformSize() : 0;

    A certain size is calculated. Depending on the value of the variable 'uniform', you should add 'UniformSize ()' or 0. Actually, this code does not work like that. The priority of the addition operators '+' is higher than the priority of the operator '?:'.

    Here is the result:
    return (A() + B() + C() + uniform) ? UniformSize() : 0;

    We see a similar situation in the Unreal Engine code. It seems to me that not what the programmer wanted is calculated.

    Confusion with enum


    At first, I did not want to describe this case, since I need to bring a sufficiently large piece of code. Then I still overcame laziness. Please be patient and readers.
    namespace EOnlineSharingReadCategory
    {
      enum Type
      {
        None          = 0x00,
        Posts         = 0x01,
        Friends       = 0x02,
        Mailbox       = 0x04,
        OnlineStatus  = 0x08,
        ProfileInfo   = 0x10,  
        LocationInfo  = 0x20,
        Default       = ProfileInfo|LocationInfo,
      };
    }
    namespace EOnlineSharingPublishingCategory
    {
      enum Type {
        None          = 0x00,
        Posts         = 0x01,
        Friends       = 0x02,
        AccountAdmin  = 0x04,
        Events        = 0x08,
        Default       = None,
      };
      inline const TCHAR* ToString
        (EOnlineSharingReadCategory::Type CategoryType)
      {
        switch (CategoryType)
        {
        case None:
        {
          return TEXT("Category undefined");
        }
        case Posts:
        {
          return TEXT("Posts");
        }
        case Friends:
        {
          return TEXT("Friends");
        }
        case AccountAdmin:
        {
          return TEXT("Account Admin");
        }
        ....
      }
    }

    The analyzer generates several V556 warnings here at once . The fact is that the argument to the 'switch ()' operator is a variable of type EOnlineSharingReadCategory :: Type. In this case, the 'case' statements use values ​​from another type EOnlineSharingPublishingCategory :: Type.

    Logical error


    const TCHAR* UStructProperty::ImportText_Internal(....) const
    {
      ....
      if (*Buffer == TCHAR('\"'))
      {
        while (*Buffer && *Buffer != TCHAR('\"') &&
               *Buffer != TCHAR('\n') && *Buffer != TCHAR('\r'))
        {
          Buffer++;
        }
        if (*Buffer != TCHAR('\"'))
      ....
    }

    Warning PVS-Studio: V637 Two opposite conditions were encountered. The second condition is always false. Check lines: 310, 312. propertystruct.cpp 310

    Here they wanted to skip everything that is contained in double quotes. The algorithm should have been like this:
    • If we find a double quote, then start the loop.
    • In the loop we skip the characters until we meet the next double quote.
    The error is that after finding the first double quote, we do not move the pointer to the next character. As a result, the second quote is immediately found. The cycle does not start.

    I will explain this in simplified code:
    if (*p == '\"')
    {
      while (*p && *p != '\"')
          p++;
    }

    To fix the error, you need to make the following changes:
    if (*p == '\"')
    {
      p++;
      while (*p && *p != '\"')
          p++;
    }

    Suspicious shift


    class FMallocBinned : public FMalloc
    {
      ....
      /* Used to mask off the bits that have been used to
         lookup the indirect table */
      uint64 PoolMask;
      ....
      FMallocBinned(uint32 InPageSize, uint64 AddressLimit)
      {
        ....
        PoolMask = ( ( 1 << ( HashKeyShift - PoolBitShift ) ) - 1 );
        ....
      }
    }

    PVS-Studio Warning: V629 Consider inspecting the '1 << (HashKeyShift - PoolBitShift)' expression. Bit shifting of the 32-bit value with a subsequent expansion to the 64-bit type. mallocbinned.h 800 Whether

    there is an error here or not depends on whether it is necessary to shift 1 by more than 31 bits. Since the result is placed in the 64-bit PoolMask variable, apparently there is such a need.

    If I guessed right, then the library has an error in the memory allocation subsystem.

    The number 1 is of type int. This means that it is impossible to shift 1, say by 35 digits. Formally, there will be undefined behavior ( details ). In practice, an overflow will occur and the wrong value will be calculated.

    The correct code is:
    PoolMask = ( ( 1ull << ( HashKeyShift - PoolBitShift ) ) - 1 );

    Deprecated Checks


    void FOculusRiftHMD::Startup()
    {
      ....
      pSensorFusion = new SensorFusion();
      if (!pSensorFusion)
      {
        UE_LOG(LogHMD, Warning,
          TEXT("Error creating Oculus sensor fusion."));
        return;
      }
      ....
    }  

    PVS-Studio Warning: V668 There is no sense in testing the 'pSensorFusion' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error. oculusrifthmd.cpp 1594

    For a long time, in the case of a memory allocation error, the 'new' operator throws an exception. The check "if (! PSensorFusion)" is not needed.

    In large projects, I usually find a lot of such places. Surprisingly, there are few such places in Unreal Engine. List: ue-V668.txt .

    Copy paste


    The following code fragments are most likely due to the use of Copy-Paste. Regardless of the condition, the same action is performed:
    FString FPaths::CreateTempFilename(....)
    {
      ....  
      const int32 PathLen = FCString::Strlen( Path );
      if( PathLen > 0 && Path[ PathLen - 1 ] != TEXT('/') )
      {
        UniqueFilename =
          FString::Printf( TEXT("%s/%s%s%s"), Path, Prefix,
                           *FGuid::NewGuid().ToString(), Extension );
      }
      else
      {
        UniqueFilename =
          FString::Printf( TEXT("%s/%s%s%s"), Path, Prefix,
                           *FGuid::NewGuid().ToString(), Extension );
      }
      ....
    }

    PVS-Studio Warning: V523 The 'then' statement is equivalent to the 'else' statement. paths.cpp 703

    Another such piece of code:
    template< typename DefinitionType >            
    FORCENOINLINE void Set(....)
    {
      ....
      if ( DefinitionPtr == NULL )
      {
        WidgetStyleValues.Add( PropertyName,
          MakeShareable( new DefinitionType( InStyleDefintion ) ) );
      }
      else
      {
        WidgetStyleValues.Add( PropertyName,
          MakeShareable( new DefinitionType( InStyleDefintion ) ) );
      }
    }

    PVS-Studio Warning: V523 The 'then' statement is equivalent to the 'else' statement. slatestyle.h 289

    Miscellaneous


    There was a different trifle. Describing it is not interesting. I’ll just give you code snippets and diagnostic messages.
    void FNativeClassHeaderGenerator::ExportProperties(....)
    {
      ....
      int32 NumByteProperties = 0;
      ....
      if (bIsByteProperty)
      {
        NumByteProperties;
      }
      ....
    }

    PVS-Studio Warning: V607 Ownerless expression 'NumByteProperties'. codegenerator.cpp 633
    static void GetModuleVersion( .... )
    {
      ....
      char* VersionInfo = new char[InfoSize];
      ....
      delete VersionInfo;
      ....
    }

    PVS-Studio warning: V611 The memory was allocated using 'new T []' operator but was released using the 'delete' operator. Consider inspecting this code. It's probably better to use 'delete [] VersionInfo;'. windowsplatformexceptionhandling.cpp 107
    const FSlateBrush* FSlateGameResources::GetBrush(
      const FName PropertyName, ....)
    {
      ....
      ensureMsgf(BrushAsset, TEXT("Could not find resource '%s'"),
                 PropertyName);
      ....
    }

    PVS-Studio Warning: V510 The 'EnsureNotFalseFormatted' function is not expected to receive class-type variable as sixth actual argument. slategameresources.cpp 49

    conclusions


    Using a static analyzer built into Visual Studio is useful, but not enough. It is reasonable to use specialized tools, such as our PVS-Studio analyzer. For example, if you compare PVS-Studio and the static analyzer from VS2013, then PVS-Studio finds 6 times more errors. In order not to be unfounded - “Comparison Methodology” . I invite all lovers of quality code to try our code analyzer.

    This article is in English.


    If you want to share this article with an English-speaking audience, then please use the link to the translation: Andrey Karpov. A Long-Awaited Check of Unreal Engine 4 .

    Have you read the article and have a question?
    Often our articles are asked the same questions. We collected answers to them here: Answers to questions from readers of articles about PVS-Studio and CppCat, version 2014 . Please see the list.

    Also popular now: