The recent Qt 6 release compelled us to recheck the framework with PVS-Studio. In this article, we reviewed various interesting errors we found, for example, those related to processing dates. The errors we discovered prove that developers can greatly benefit from regularly checking their projects with tools like PVS-Studio. This is a standard article that reports the results of an open-source project check. This article will add to our "evidence base" that demonstrates how useful and effective PVS-Studio is in code quality control. Though we have already checked the Qt project in the past (in 2011, 2014, and 2018), rechecking the framework was worth it. The new check's result supported a simple, but very important idea: static analysis should be used regularly! Our articles show that the PVS-Studio analyzer can find a wide variety of errors. Project authors often quickly fix the errors we describe. However, all this has nothing to do with the benefits of regular static code analysis. When static code analysis is built into the development process, developers quickly find and fix errors in new or recently edited code. Fixing code at this stage is the cheapest. Alright, enough theory! Let's take a look what the Qt 6 code has in store for us. And while you are reading this article, why don't you download PVS-Studio and request a trial key. See for yourself what the static analyzer can find in your projects :). Lately we've been noticing one more code pattern that tends to attract an increasing number of bugs. Of course, these code fragments are not as significant as comparison functions or the last line in similar code blocks. We are talking about code that works with dates. Such code can be difficult to test. So it comes as no surprise that these untested functions may process some arguments inadequately and return an incorrect result. We've already described a couple of similar cases in the following article: "Why PVS-Studio Doesn't Offer Automatic Fixes". Qt also fell prey to that trend and has occasional problems with code that processes dates. So here is where we start. Fragment #1: Error Status Misinterpreted First, let's see how the developer wrote the function that accepts a month's abbreviated name and returns its number. If successful, the function returns the month number (a value from 1 to 12). If the month's name is incorrect, the function returns a negative value (-1). Note that the function cannot return 0. However, the function above is used where the developer expects it to return null in case of an error. Here is the code fragment that uses the fromShortMonthName function incorrectly: The program never reaches the code that checks the month number for null, and continues to run with an incorrect negative month number. The PVS-Studio analyzer sees a whole bunch of inconsistencies here and reports them with four warnings at once: Fragment #2: Error in Date Processing Logic Let's take a look at the function that returns a number of seconds. The function above can return a value in the range of [0..59] or an error status of -1. Here is one location where the use of this function is very strange: PVS-Studio warns: V560 [CWE-570] A part of conditional expression is always false: time->second() == MSECS_PER_DAY — 1. qdatetime.cpp 2488 The comment in the code tells us that if something goes wrong, it's better to do nothing. However, the condition always evaluates to false and the else branch is always executed. Here is the comparison that's incorrect: "MSECS_PER_DAY — 1" equals 86399999. As we already know, the second function cannot return this value. This means the code has some logical error and requires refactoring. Static analyzers are powerful in a sense that they check all scenarios no matter how infrequent they are. Thus, static analysis is a good addition to unit tests and other code quality control tools. Fragment #3: Suddenly, Let's Talk About… HTML! PVS-Studio warns: V735 Possibly an incorrect HTML. The "</ body>" closing tag was encountered, while the "</ ul>" tag was expected. qpixeltool.cpp 707 PVS-Studio contains diagnostics that don't just check code — they also look for abnormalities in string constants. The code above triggered one of these diagnostics. Such cases are quite rare, and that's what makes this one so intriguing. Someone intended to create one list, but added two tags that open this list instead of one. This is clearly a typo. The first tag must open the list, and the second one needs to close it. Here is the correct code: Fragment #4: A Double Check Within One Condition PVS-Studio warns: V501 [CWE-570] There are identical sub-expressions to the left and to the right of the '||' operator: node->isGroup() || node->isGroup() docbookgenerator.cpp 2599 This is a common typo. The fix depends on what this code is expected to achieve. If the check is duplicated by accident, one can delete it. A different scenario is also possible: some other necessary condition has been left out. Fragment #5: One Too Many Local Variables If you want, you can test your attention to detail and look for the error yourself. I'll even move the text down for you so that you don't see the spoiler right away. Here is a beautiful unicorn from our old collection. Maybe you haven't even seen it before :).  PVS-Studio warns: V561 [CWE-563] It's probably better to assign value to 'selectedPhraseBook' variable than to declare it anew. Previous declaration: mainwindow.cpp, line 1303. mainwindow.cpp 1313 The text that originates from either of the conditional operator's branches needs to be recorded to the selectedPhraseBook variable. The developer felt the variable's name was too long to write it out again and copied it from the line that declares the variable. It looks like the developer hurried a little and copied the type of the variable as well: As a result, the else block contains an excessive local string variable that is initialized, but never used. Meanwhile, the original variable that should have been assigned a value remains empty.  Fragment #6: Operation Priority This is a classic error pattern that we encounter quite frequently. PVS-Studio warns: V593 [CWE-783] Consider reviewing the expression of the 'A = B != C' kind. The expression is calculated as following: 'A = (B != C)'. qqmlimport.cpp 754 The icID variable always has a value of 0 or 1. This is clearly not what the developer intended to do. Here's the reason: the comparison to -1 comes first, and then the icID variable is initialized. You can use modern C++ syntax to phrase the condition correctly — as shown below: By the way, I have already seen a similar error in Qt before: This demonstrates that developers will keep making the same mistakes over and over again until they integrate an analyzer like PVS-Studio into the development process. No one is perfect. Yes, this is a subtle hint that you should start using PVS-Studio :).  Fragment #7: The Evil Modulus Division Often, you may need to determine whether a number is divisible by 2 without remainder. The correct way to do this is to do a modulo division by two and check the result:  However, the developers may write something like this instead: This is wrong because the remainder of the modulo division by one is always zero. Qt also has this error: PVS-Studio warns: V1063 The modulo by 1 operation is meaningless. The result will always be zero. qm.cpp 549 Fragment #8: Overwriting a Value PVS-Studio warns: V519 [CWE-563] The 'qualifiedName' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 1227, 1228. node.cpp 1228 As far as I understand, the developer accidentally used a wrong variable name. I assume the code should read as follows: Fragment #9: Copy and Paste PVS-Studio warns: V524 It is odd that the body of '<=' function is fully equivalent to the body of '<' function. qjsonobject.h 155 No one checks boring functions like comparison operators. No one writes tests for them. Developers may take a quick look at them during code review — or skip them altogether. But that's a bad idea. And that's where static code analysis comes in handy. The analyzer never gets tired and is happy to check even boring code snippets. Here the < and <= operators are implemented in the same way. This is certainly wrong. The developer may have found the code elsewhere, copy-pasted it, and then forgot to customize it. Here is the correct code: Fragment #10: static_cast / dynamic_cast PVS-Studio warns: V547 [CWE-571] Expression 'softwareRenderer' is always true. qsgsoftwarethreadedrenderloop.cpp 510 First, let's take a look at this check: The code makes sure that the wd->renderer pointer is never null inside the conditional operator. So why add one more check? What does it do exactly? If the wd->renderer pointer is not null, the softwareRenderer pointer cannot be null. I suspect there is a typo here and the developer intended to use dynamic_cast. In this case, the code starts to make sense. If type conversion is not possible, the dynamic_cast operator returns nullptr. This returned value should be checked. However, I may have misinterpreted the situation and the code needs to be corrected in a different way. Fragment #11: Copied, but Not Altered PVS-Studio warns: V581 [CWE-670] The conditional expressions of the 'if' statements situated alongside each other are identical. Check lines: 2719, 2721. moc_qquickpath_p.cpp 2721 Take a look at these two lines: Someone copied and pasted them multiple times — and forgot to modify them. The way they are now, they do not make sense. Fragment #12: Overflow Due to the Wrong Parenthesis Placement PVS-Studio warns: V1028 [CWE-190] Possible overflow. Consider casting operands of the 'd->m_offsetFromUtc * 1000' operator to the 'qint64' type, not the result. qdatetime.cpp 3922 The developer foresees a case when the int type variable is multiplied by 1000 and causes overflow. To avoid this, the developer plans to use the qint64 64-bit type variable. And uses explicit type casting. However, the casting does not help at all, because the overflow happens before the casting. The correct code: Fragment #13: A Partly Initialized Array PVS-Studio warns:  Above is a failed attempt to initialize a 2x2 array. Two elements are initialized twice, while the other two got overlooked. The correct code: And let me say, I just love it when I see how professional developers make such silly mistakes. Don't get me wrong, but such cases demonstrate that everyone is human and can make a mistake or a typo. So, static analysis is your best friend. I think it's been about 10 years since I've started fighting sceptical — albeit professional — developers over one simple subject: such errors happen in their own code as well — students are not the only ones to breed typos in their code :). 10 years ago I wrote a note: "The second myth — expert developers do not make silly mistakes". Nothing changed since then. People keep making mistakes and pretending they don't :).  Fragment #14: Unreachable Code PVS-Studio warns: V547 [CWE-570] Expression 'm_verbose' is always false. qmlprofilerapplication.cpp 495 This code will never log anything because of the conflicting conditions. Fragment #15: Overwriting a Variable's Value PVS-Studio warns: V519 [CWE-563] The 'done' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 509, 511. qeffects.cpp 511 The entire conditional operator makes no sense, because the done variable is overwritten right after it is assigned. The code could be lacking the else keyword. Fragment #16-#20: Overwriting Variables' Values  Here is another example of a variable's value that is overwritten:  PVS-Studio warns: V519 [CWE-563] The 'lastNamespaceDeclaration' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 3030, 3036. qxmlstream.cpp 3036 The lastNamespaceDeclaration variable's first assignment may have happened by accident. It is probably okay to delete this line. However, we could be facing a serious logical error. Four more warnings indicate the same error patterns in the Qt 6 code: Fragment #21: Confusion Between Null Pointer and Empty String PVS-Studio warns: V547 [CWE-571] Expression 'lang' is always true. qfontconfigdatabase.cpp 462  The languageForWritingSystem array has no null pointers. Which is why the if(lang) check makes no sense. However, the array contains empty strings. Has the developer meant to do an empty string check? If yes, the correct code goes like this: Or you can simplify it even further: Fragment #22: A Bizarre Check PVS-Studio warns: V547 [CWE-571] Expression 'socket != — 1' is always true. qnativesocketengine_unix.cpp 315 The socket != -1 condition always evaluates to true, because the function above it always exits when the socket value is negative. Fragment #23: What Exactly Should the Function Return? PVS-Studio warns: V547 [CWE-570] Expression '!count' is always false. qsqltablemodel.cpp 1110 To make this more simple, I'll point out the most important lines: The first check indicates that if the count value equals to or is below 0, the state is incorrect and the function must return false. However, further on we see this variable compared to zero, and this case is interpreted differently: the function must return true. There's clearly something wrong here. I suspect that the developer intended to use the < operator instead of <=. Then the code starts to make sense: Fragment #24: An Unnecessary Status? The code below contains the identifierWithEscapeChars variable that looks like a redundant entity. Or is it a logical error? Or is the code unfinished? By the second check this variable is true in all scenarios PVS-Studio warns: V547 [CWE-571] Expression 'identifierWithEscapeChars' is always true. qqmljslexer.cpp 817 Fragment #25: What Do I Do With Nine Objects? PVS-Studio warns: V547 [CWE-570] Expression 'count == 9' is always false. qfont.cpp 2142 What should the function do if the count variable equals 9? On the one hand, the function should issue a warning and exit. Just as the code says: On the other hand, someone added special code to be executed for 9 objects: The function, of course, never reaches this code. The code is waiting for someone to come and fix it :). Fragments #26-#42: Using a Pointer Before Checking It PVS-Studio warns: V595 [CWE-476] The 'd_ptr' pointer was utilized before it was verified against nullptr. Check lines: 710, 713. qmetatype.cpp 710 The error is easy to overlook, but everything is straightforward here. Let's see how the code uses the d_ptr pointer: In the first if-block the pointer is dereferenced. Then the next check suggests this pointer can be null.  This is one of the most common error patterns in C and C++. Proofs. We saw quite a few errors of this kind in the Qt source code. Fragment #43: Within One Expression, The Use of a Pointer That Has Not Been Checked for Null This case is almost the same as the one above. However, this time the pointer is dereferenced and checked inside one expression. This is a classic incidental error — someone was inattentive when writing and reviewing code. PVS-Studio warns: V713 [CWE-476] The pointer 'field' was utilized in the logical expression before it was verified against nullptr in the same logical expression. qformlayout.cpp 405 Now let's take a one-minute break. I got tired from all the writing. I think the readers are tired as well. This article can wear you out even if you are just skimming through the text :). So it's about time I get my second cup of coffee. I finished my first one at around Fragment #12. Why don't you, my readers, join me for a cup of joe — or pick your favorite drink. And while we are all taking a break, I'll wander off from the topic for a bit. I am inviting the team that develops the Qt project to consider purchasing a license for the PVS-Studio code analyzer. You can request our price list here. We will provide support and help you set up the analyzer. Yes, alright, today I am more insistent. This is something new that I'm trying :).  Fragments #44-#72: No Check for the malloc Function's Product PVS-Studio warns: V575 [CWE-628] The potential null pointer is passed into 'memcpy' function. Inspect the first argument. Check lines: 277, 276. qqmlprofilerevent_p.h 277 You cannot simply take and use the pointer the malloc function returns. It is imperative you check this pointer for null, even if you are very lazy to do it. We described 4 possible reasons to do this in our article "Why it is important to check what the malloc function returned". The need to check the malloc function's output falls within that article's scope. There are more warnings, but I do not want to include them into this list, because they are too many. Just in case, I gathered 28 warnings in the following file for you: qt6-malloc.txt. I do, however, recommend developers to recheck the project and study the warnings themselves. I did not have a goal to find as many errors as possible. Interestingly enough, with all the important missed checks, I found completely unnecessary ones. I am talking about the new operator call, that, in case of an error, generates the std::bad_alloc exception. Here is one example of such redundant check: PVS-Studio warns: V668 [CWE-570] There is no sense in testing the 'isi' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error. qimagescale.cpp 245 P.S. Here the readers always ask, does the analyzer know about placement new or "new (std::nothrow) T"? Yes, it does, and no, it does not issue any false positives for them. In some scenarios, the analyzer issues warnings to code that is correct, but excessive. It may happen, for example, when the same variable is checked twice. Sometimes it's not clear whether this is a false positive or not. Technically, the analyzer is correct, but it did not find a real error. You can probably say it's a "code smell". Since the analyzer does not like this code, other developers may not like it either and may find it difficult to support. You have to spend more time to understand what is happening. Usually I don't even discuss such warnings in my articles. It's boring to do this. However, the Qt project surprised me with how many so-called "code smells" I was able to find. Definitely more than in most projects. Which is why I decided to turn your attention to "code smells" and investigate a few such cases. I think it will be useful to refactor these and many other similar patterns. To do this, you will need to use a complete report. The report's fragments I added to this article are insufficient. So let's inspect a few scenarios that illustrate the problem. Fragment #73: "Code Smell" — Reverse Check PVS-Studio warns: V547 [CWE-571] Expression 'window != m_window' is always true. qquick3dscenemanager.cpp 60 If window==m_window, the function exists. The consecutive inverse check makes no sense and just clutters the code. Fragment #74: "Code Smell" — Weird Initialization PVS-Studio warns: V547 [CWE-571] Expression 'vi < 0' is always true. qtreeview.cpp 2219  What is this? Why write something like this? The developer can simplify the code down to one line: Fragment #75: "Code Smell" — Unreachable Code PVS-Studio warns: V571 [CWE-571] Recurring check. The 'if (unmetDependencies.isEmpty())' condition was already verified in line 2203. main.cpp 2209 At first this code seems absolutely adequate. Just normal code that creates hints. But let's take a closer look. If the unmetDependencies.isEmpty() condition was met and executed once, it is not going to be executed for the second time. This is not a big deal, because the author was planning to display the same message. There is no real error, but the code is overly complicated. One can simplify it like this: Fragment #76: "Code Smell" — A Complex Ternary Operator PVS-Studio warns: V547 [CWE-571] Expression 'widgetItem' is always true. qdockarealayout.cpp 1167 We could be dealing with a real bug here. But I am more inclined to believe the developers reworked this code several times and got an unexpectedly and unnecessarily complicated code block with redundant statements. You can reduce it down to the following: Fragment #77: "Code Smell" – Excessive Protection PVS-Studio warns: V547 [CWE-570] Expression 'l != len' is always false. qv4typedarray.cpp 306 Someone worried too much that a value from a 64-bit variable might not fit into the unsigned 32-bit variable. And used two checks at once. The second check is redundant. The following code is more than enough: Then you can safely delete the snippet below. This will not endanger your code's reliability in any way. I can keep doing this, but I'll stop. I think you get the idea. One can draw a nice conclusion here: the use of PVS-Studio will benefit your code in several ways — you can remove errors and simplify your code. I stopped after I described 77 defects. This is a beautiful number, and I wrote more than enough to shape an article. However, this does not mean there are no more mistakes PVS-Studio can find. While studying the log, I was very quick. I skipped everything that required more than 2 minutes of my time to figure out whether it was a mistake :). This is why I always urge you not to rely on our articles that explore your errors, but to use PVS-Studio on your projects yourself instead. Static analysis is awesome! After you introduce the PVS-Studio into your development process, it will save your time and brain cells by finding many mistakes right after you write new code. It is much more fun to gather with your team for code review and discuss high-level errors and the efficiency of the implemented algorithms instead of typos. Moreover, as my experience shows, these nasty typos are always hiding, even if you check your code with your eyes. So let the software look for them instead. If you have any more questions or objections, I invite you to read the following article: "Why You Should Choose the PVS-Studio Static Analyzer to Integrate into Your Development Process". I give this article a 90% chance to be able to answer your questions :). If you are in the 10% — message us, let's talk :). Директор по маркетингу