sq-software.comsq-software.com/.../uploads/2017/05/pvs-handbook.docx  · web viewthe compiler...

1260
PVS-Studio Documentation (single file) Contents Introduction Using the program PVS-Studio Options Analyzer Diagnostics Additional information You can open full PVS-Studio documentation as single file. In addition, you can print it as .pdf with help virtual printer. Introduction 1. Two words about PVS-Studio. 2. Getting acquainted with PVS-Studio code analyzer. 3. System Requirements. 4. PVS-Studio's Trial Mode. 5. Release History. 6. Old PVS-Studio Release History (before 5.00). 7. Limitation. Using the program 1. Common information on working with the PVS-Studio analyzer. 2. Analyzer Modes. 3. Suppression of false alarms.

Upload: tranphuc

Post on 12-Mar-2018

235 views

Category:

Documents


7 download

TRANSCRIPT

Page 1: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

PVS-Studio Documentation (single file)

Contents

Introduction

Using the program

PVS-Studio Options

Analyzer Diagnostics

Additional information

You can open full PVS-Studio documentation as single file. In addition, you can

print it as .pdf with help virtual printer.

Introduction1. Two words about PVS-Studio.

2. Getting acquainted with PVS-Studio code analyzer.

3. System Requirements.

4. PVS-Studio's Trial Mode.

5. Release History.

6. Old PVS-Studio Release History (before 5.00).

7. Limitation.

Using the program1. Common information on working with the PVS-Studio analyzer.

2. Analyzer Modes.

3. Suppression of false alarms.

4. Handling the diagnostic messages list.

5. Analyzing Visual C++ (.vcxproj) and Visual C# (.csproj) projects from the command line.

Page 2: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

6. Direct integration of the analyzer into build automation systems (C/C++).

7. Using PVS-Studio with continuous integration systems.

8. Predefined PVS_STUDIO macro.

9. PVS-Studio: Troubleshooting.

10. Tips on speeding up PVS-Studio.

11. PVS-Studio's incremental analysis mode.

12. Deployment of PVS-Studio in large teams.

13. Using external tools with PVS-Studio. Integration with bug tracking systems.

14. Relative paths in PVS-Studio log files.

15. Compiler Monitoring in PVS-Studio.

16. Mass Suppression of Analyzer Messages.

17. Standalone.

18. Analyzer Work Statistics (Diagrams).

19. Installing and updating PVS-Studio on Linux

20. How to run PVS-Studio on Linux.

21. Integration of PVS-Studio analysis results into SonarQube.

22. Managing the Analysis Results (.plog file).

PVS-Studio Options1. Settings: General.

2. Settings: Common Analyzer Settings.

3. Settings: Detectable Errors.

4. Settings: Don't Check Files.

5. Settings: Keyword Message Filtering.

6. Settings: Registration.

7. Settings: Specific Analyzer Settings.

Analyzer Diagnostics1. PVS-Studio Messages.

1. General Analysis (C++)

2. General Analysis (C#)

Page 3: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

3. Diagnosis of micro-optimizations (C++)

4. Diagnosis of 64-bit errors (Viva64, C++)

5. Customer's Specific Requests (C++)

6. Problems related to code analyzer (C++, C#)

Additional information1. Credits and acknowledgements.

PVS-Studio Messages

What bugs can PVS-Studio detect?

General Analysis (C++)

General Analysis (C#)

Diagnosis of micro-optimizations (C++)

Diagnosis of 64-bit errors (Viva64, C++)

Customers Specific Requests (C++)

Problems related to code analyzer (C++, C#)

What bugs can PVS-Studio detect?We grouped the diagnostic, so that you can get the general idea of what PVS-Studio

is capable of.

As it is hard to do strict grouping, some diagnostics belong to several groups. For

example, the incorrect condition "if (abc == abc)" can be interpreted both as a

simple typo, but also as a security issue, because it leads to the program

vulnerability if the input data are incorrect.

Some of the errors, on the contrary, couldn't fit any of the groups, because they

were too specific. Nevertheless this table gives the insight about the functionality of

the static code analyzer.

Main PVS-Studio diagnostic abilities

C, C++ diagnostics C# diagnostics

Page 4: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

64-bit issues V101-V128, V201-V207, V220, V221, V301-V303

-

Check that addresses to stack memory does not leave the function

V506, V507, V558, V758 -

Arithmetic over/underflow V636, V658 V3040, V3041

Array index out of bounds V557, V582, V643 V3106

Check for double-free V586, V749 -

Dead code V606, V607 -

Microoptimization V801-V817 -

Unreachable code V551, V695, V734, V776, V779 -

Uninitialized variables V573, V614, V679, V730, V737 V3070, V3128

Unused variables V603, V751, V763 V3061, V3065, V3077, V3117

Illegal bitwise/shift operations

V610, V629, V673, V684, V770 -

Undefined/unspecified behavior

V567, V610, V611, V681, V704, V708, V726, V736, V772

-

Incorrect handling of the types (HRESULT, BSTR, BOOL, VARIANT_BOOL)

V543, V544, V545, V716, V721, V724, V745, V750, V676, V767, V768, V775

V3111, V3121

Improper understanding of function/class operation logic

V518, V530, V540, V541, V554, V575, V597, V598, V618, V630, V632, V663, V668, V698, V701, V702, V717, V718, V720, V723, V725, V727, V738, V742, V743, V748, V762, V764

V3010, V3057, V3068, V3072, V3073, V3074, V3082, V3084, V3094, V3096, V3097, V3102, V3103, V3104, V3108, V3114, V3115, V3118, V3123, V3126

Misprints V501, V503, V504, V508, V511, V516, V519, V520, V521, V525, V527, V528, V529, V532, V533, V534, V535, V536, V537, V539, V546, V549, V552, V556, V559, V560, V561, V564, V568, V570, V571, V575, V577, V578, V584, V587, V588, V589, V590, V592, V600, V602, V604, V606, V607, V616, V617, V620, V621, V622, V625, V626, V627, V633, V637, V638, V639, V644, V646, V650, V651, V653, V654, V655, V660, V661,

V3001, V3003, V3005, V3007, V3008, V3009, V3011, V3012, V3014, V3015, V3016, V3020, V3028, V3029, V3034, V3035, V3036, V3037, V3038, V3050, V3055, V3056, V3057, V3062, V3063, V3066, V3081, V3086, V3091, V3092, V3107, V3109, V3110, V3112, V3113, V3116, V3122, V3124

Page 5: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V662, V666, V669, V671, V672, V678, V682, V683, V693, V715, V722, V735, V747, V754, V756, V765, V767

Missing Virtual destructor V599, V689 -

Coding style not matching the operation logic of the source code

V563, V612, V628, V640, V646, V705 V3018, V3033, V3043, V3067, V3069

Copy-Paste V501, V517, V519, V523, V524, V571, V581, V649, V656, V691, V760, V766, V778

V3001, V3003, V3004, V3008, V3012, V3013, V3021, V3030, V3058, V3127

Incorrect usage of exceptions

V509, V565, V596, V667, V740, V741, V746, V759

V3006, V3052, V3100

Buffer overrun V512, V514, V594, V635, V641, V645, V752, V755

-

Security issues V505, V510, V511, V512, V518, V531, V541, V547, V559, V560, V569, V570, V575, V576, V579, V583, V597, V598, V618, V623, V642, V645, V675, V676, V724, V727, V729, V733, V743, V745, V750, V771, V774

V3022, V3023, V3025, V3027, V3053, V3063

Operation priority V502, V562, V593, V634, V648 V3130

Null pointer pointer/null reference dereference

V522, V595, V664, V757, V769 V3019, V3042, V3080, V3095, V3105, V3125

Unchecked parameter dereference

V595, V664 V3095

Synchronization errors V712 V3032, V3054, V3079, V3083, V3089, V3090

WPF usage errors - V3044 - V3049

Resource leaks V701, V773 -

Check for integer division by zero

V609 V3064

Customized user rules V2001-V2013 -

Table – PVS-Studio functionality.

Page 6: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

As you see, the analyzer is especially useful is such spheres as looking for bugs

caused by Copy-Paste and detecting security flaws.

To these diagnostics in action, have a look at the error base. We collect all the errors

that we have found, checking various open source projects with PVS-Studio.

General Analysis (C++)1. V501 . There are identical sub-expressions to the left and to the right of the

'foo' operator.

2. V502 . Perhaps the '?:' operator works in a different way than it was expected. The '?:' operator has a lower priority than the 'foo' operator.

3. V503 . This is a nonsensical comparison: pointer < 0.

4. V504 . It is highly probable that the semicolon ';' is missing after 'return' keyword.

5. V505 . The 'alloca' function is used inside the loop. This can quickly overflow stack.

6. V506 . Pointer to local variable 'X' is stored outside the scope of this variable. Such a pointer will become invalid.

7. V507 . Pointer to local array 'X' is stored outside the scope of this array. Such a pointer will become invalid.

8. V508 . The use of 'new type(n)' pattern was detected. Probably meant: 'new type[n]'.

9. V509 . The 'throw' operator inside the destructor should be placed within the try..catch block. Raising exception inside the destructor is illegal.

10. V510 . The 'Foo' function is not expected to receive class-type variable as 'N' actual argument.

11. V511 . The sizeof() operator returns size of the pointer, and not of the array, in given expression.

12. V512 . A call of the 'Foo' function will lead to a buffer overflow or underflow.

13. V513 . Use _beginthreadex/_endthreadex functions instead of CreateThread/ExitThread functions.

14. V514 . Dividing sizeof a pointer by another value. There is a probability of logical error presence.

15. V515 . The 'delete' operator is applied to non-pointer.

16. V516 . Consider inspecting an odd expression. Non-null function pointer is compared to null.

Page 7: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

17. V517 . The use of 'if (A) {...} else if (A) {...}' pattern was detected. There is a probability of logical error presence.

18. V518 . The 'malloc' function allocates strange amount of memory calculated by 'strlen(expr)'. Perhaps the correct variant is strlen(expr) + 1.

19. V519 . The 'x' variable is assigned values twice successively. Perhaps this is a mistake.

20. V520 . The comma operator ',' in array index expression.

21. V521 . Such expressions using the ',' operator are dangerous. Make sure the expression is correct.

22. V522 . Dereferencing of the null pointer might take place.

23. V523 . The 'then' statement is equivalent to the 'else' statement.

24. V524 . It is odd that the body of 'Foo_1' function is fully equivalent to the body of 'Foo_2' function.

25. V525 . The code containing the collection of similar blocks. Check items X, Y, Z, ... in lines N1, N2, N3, ...

26. V526 . The 'strcmp' function returns 0 if corresponding strings are equal. Consider examining the condition for mistakes.

27. V527 . It is odd that the 'zero' value is assigned to pointer. Probably meant: *ptr = zero.

28. V528 . It is odd that pointer is compared with the 'zero' value. Probably meant: *ptr != zero.

29. V529 . Odd semicolon ';' after 'if/for/while' operator.

30. V530 . The return value of function 'Foo' is required to be utilized.

31. V531 . It is odd that a sizeof() operator is multiplied by sizeof().

32. V532 . Consider inspecting the statement of '*pointer++' pattern. Probably meant: '(*pointer)++'.

33. V533 . It is likely that a wrong variable is being incremented inside the 'for' operator. Consider reviewing 'X'.

34. V534 . It is likely that a wrong variable is being compared inside the 'for' operator. Consider reviewing 'X'.

35. V535 . The variable 'X' is being used for this loop and for the outer loop.

36. V536 . Be advised that the utilized constant value is represented by an octal form.

37. V537 . Consider reviewing the correctness of 'X' item's usage.

38. V538 . The line contains control character 0x0B (vertical tabulation).

Page 8: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

39. V539 . Consider inspecting iterators which are being passed as arguments to function 'Foo'.

40. V540 . Member 'x' should point to string terminated by two 0 characters.

41. V541 . It is dangerous to print the string into itself.

42. V542 . Consider inspecting an odd type cast: 'Type1' to ' Type2'.

43. V543 . It is odd that value 'X' is assigned to the variable 'Y' of HRESULT type.

44. V544 . It is odd that the value 'X' of HRESULT type is compared with 'Y'.

45. V545 . Such conditional expression of 'if' statement is incorrect for the HRESULT type value 'Foo'. The SUCCEEDED or FAILED macro should be used instead.

46. V546 . Member of a class is initialized by itself: 'Foo(Foo)'.

47. V547 . Expression is always true/false.

48. V548 . Consider reviewing type casting. TYPE X[][] in not equivalent to TYPE **X.

49. V549 . The 'first' argument of 'Foo' function is equal to the 'second' argument.

50. V550 . An odd precise comparison. It's probably better to use a comparison with defined precision: fabs(A - B) < Epsilon or fabs(A - B) > Epsilon.

51. V551 . The code under this 'case' label is unreachable.

52. V552 . A bool type variable is being incremented. Perhaps another variable should be incremented instead.

53. V553 . The length of function's body or class's declaration is more than 2000 lines long. You should consider refactoring the code.

54. V554 . Incorrect use of smart pointer.

55. V555 . The expression of the 'A - B > 0' kind will work as 'A != B'.

56. V556 . The values of different enum types are compared.

57. V557 . Array overrun is possible.

58. V558 . Function returns the pointer/reference to temporary local object.

59. V559 . Suspicious assignment inside the conditional expression of 'if/while/for' statement.

60. V560 . A part of conditional expression is always true/false.

61. V561 . It's probably better to assign value to 'foo' variable than to declare it anew.

62. V562 . It's odd to compare a bool type value with a value of N.

Page 9: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

63. V563 . It is possible that this 'else' branch must apply to the previous 'if' statement.

64. V564 . The '&' or '|' operator is applied to bool type value. You've probably forgotten to include parentheses or intended to use the '&&' or '||' operator.

65. V565 . An empty exception handler. Silent suppression of exceptions can hide the presence of bugs in source code during testing.

66. V566 . The integer constant is converted to pointer. Possibly an error or a bad coding style.

67. V567 . Undefined behavior. The variable is modified while being used twice between sequence points.

68. V568 . It's odd that the argument of sizeof() operator is the expression.

69. V569 . Truncation of constant value.

70. V570 . The variable is assigned to itself.

71. V571 . Recurring check. This condition was already verified in previous line.

72. V572 . It is odd that the object which was created using 'new' operator is immediately cast to another type.

73. V573 . Uninitialized variable 'Foo' was used. The variable was used to initialize itself.

74. V574 . The pointer is used simultaneously as an array and as a pointer to single object.

75. V575 . Function receives an odd argument.

76. V576 . Incorrect format. Consider checking the N actual argument of the 'Foo' function.

77. V577 . Label is present inside a switch(). It is possible that these are misprints and 'default:' operator should be used instead.

78. V578 . An odd bitwise operation detected. Consider verifying it.

79. V579 . The 'Foo' function receives the pointer and its size as arguments. It is possibly a mistake. Inspect the N argument.

80. V580 . An odd explicit type casting. Consider verifying it.

81. V581 . The conditional expressions of the 'if' statements situated alongside each other are identical.

82. V582 . Consider reviewing the source code which operates the container.

83. V583 . The '?:' operator, regardless of its conditional expression, always returns one and the same value.

84. V584 . The same value is present on both sides of the operator. The expression is incorrect or it can be simplified.

Page 10: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

85. V585 . An attempt to release the memory in which the 'Foo' local variable is stored.

86. V586 . The 'Foo' function is called twice for deallocation of the same resource.

87. V587 . An odd sequence of assignments of this kind: A = B; B = A;.

88. V588 . The expression of the 'A =+ B' kind is utilized. Consider reviewing it, as it is possible that 'A += B' was meant.

89. V589 . The expression of the 'A =- B' kind is utilized. Consider reviewing it, as it is possible that 'A -= B' was meant.

90. V590 . Consider inspecting this expression. The expression is excessive or contains a misprint.

91. V591 . Non-void function should return a value.

92. V592 . The expression was enclosed by parentheses twice: ((expression)). One pair of parentheses is unnecessary or misprint is present.

93. V593 . Consider reviewing the expression of the 'A = B == C' kind. The expression is calculated as following: 'A = (B == C)'.

94. V594 . The pointer steps out of array's bounds.

95. V595 . The pointer was utilized before it was verified against nullptr. Check lines: N1, N2.

96. V596 . The object was created but it is not being used. The 'throw' keyword could be missing.

97. V597 . The compiler could delete the 'memset' function call, which is used to flush 'Foo' buffer. The RtlSecureZeroMemory() function should be used to erase the private data.

98. V598 . The 'memset/memcpy' function is used to nullify/copy the fields of 'Foo' class. Virtual table pointer will be damaged by this.

99. V599 . The virtual destructor is not present, although the 'Foo' class contains virtual functions.

100.V600 . Consider inspecting the condition. The 'Foo' pointer is always not equal to NULL.

101.V601 . An odd implicit type casting.

102.V602 . Consider inspecting this expression. '<' possibly should be replaced with '<<'.

103.V603 . The object was created but it is not being used. If you wish to call constructor, 'this->Foo::Foo(....)' should be used.

104.V604 . It is odd that the number of iterations in the loop equals to the size of the pointer.

Page 11: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

105.V605 . Consider verifying the expression. An unsigned value is compared to the number - NN.

106.V606 . Ownerless token 'Foo'.

107.V607 . Ownerless expression 'Foo'.

108.V608 . Recurring sequence of explicit type casts.

109.V609 . Divide or mod by zero.

110.V610 . Undefined behavior. Check the shift operator.

111.V611 . The memory allocation and deallocation methods are incompatible.

112.V612 . An unconditional 'break/continue/return/goto' within a loop.

113.V613 . Strange pointer arithmetic with 'malloc/new'.

114.V614 . Uninitialized variable 'Foo' used.

115.V615 . An odd explicit conversion from 'float *' type to 'double *' type.

116.V616 . The 'Foo' named constant with the value of 0 is used in the bitwise operation.

117.V617 . Consider inspecting the condition. An argument of the '|' bitwise operation always contains a non-zero value.

118.V618 . It's dangerous to call the 'Foo' function in such a manner, as the line being passed could contain format specification. The example of the safe code: printf("%s", str);

119.V619 . An array is being utilized as a pointer to single object.

120.V620 . It's unusual that the expression of sizeof(T)*N kind is being summed with the pointer to T type.

121.V621 . Consider inspecting the 'for' operator. It's possible that the loop will be executed incorrectly or won't be executed at all.

122.V622 . Consider inspecting the 'switch' statement. It's possible that the first 'case' operator is missing.

123.V623 . Consider inspecting the '?:' operator. A temporary object is being created and subsequently destroyed.

124.V624 . The constant NN is being utilized. The resulting value could be inaccurate. Consider using the M_NN constant from <math.h>.

125.V625 . Consider inspecting the 'for' operator. Initial and final values of the iterator are the same.

126.V626 . Consider checking for misprints. It's possible that ',' should be replaced by ';'.

127.V627 . Consider inspecting the expression. The argument of sizeof() is the macro which expands to a number.

Page 12: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

128.V628 . It's possible that the line was commented out improperly, thus altering the program's operation logics.

129.V629 . Consider inspecting the expression. Bit shifting of the 32-bit value with a subsequent expansion to the 64-bit type.

130.V630 . The 'malloc' function is used to allocate memory for an array of objects which are classes containing constructors/destructors.

131.V631 . Consider inspecting the 'Foo' function call. Defining an absolute path to the file or directory is considered a poor style.

132.V632 . Consider inspecting the NN argument of the 'Foo' function. It is odd that the argument is of the 'T' type.

133.V633 . Consider inspecting the expression. Probably the '!=' should be used here.

134.V634 . The priority of the '+' operation is higher than that of the '<<' operation. It's possible that parentheses should be used in the expression.

135.V635 . Consider inspecting the expression. The length should probably be multiplied by the sizeof(wchar_t).

136.V636 . The expression was implicitly cast from integer type to real type. Consider utilizing an explicit type cast to avoid overflow or loss of a fractional part.

137.V637 . Two opposite conditions were encountered. The second condition is always false.

138.V638 . A terminal null is present inside a string. The '\0xNN' characters were encountered. Probably meant: '\xNN'.

139.V639 . Consider inspecting the expression for function call. It is possible that one of the closing ')' brackets was positioned incorrectly.

140.V640 . The code's operational logic does not correspond with its formatting.

141.V641 . The size of the allocated memory buffer is not a multiple of the element size.

142.V642 . Saving the function result inside the 'byte' type variable is inappropriate. The significant bits could be lost breaking the program's logic.

143.V643 . Unusual pointer arithmetic. The value of the 'char' type is being added to the string pointer.

144.V644 . A suspicious function declaration. It is possible that the T type object was meant to be created.

145.V645 . The function call could lead to the buffer overflow. The bounds should not contain the size of the buffer, but a number of characters it can hold.

Page 13: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

146.V646 . Consider inspecting the application's logic. It's possible that 'else' keyword is missing.

147.V647 . The value of 'A' type is assigned to the pointer of 'B' type.

148.V648 . Priority of the '&&' operation is higher than that of the '||' operation.

149.V649 . There are two 'if' statements with identical conditional expressions. The first 'if' statement contains function return. This means that the second 'if' statement is senseless.

150.V650 . Type casting operation is utilized 2 times in succession. Next, the '+' operation is executed. Probably meant: (T1)((T2)a + b).

151.V651 . An odd operation of the 'sizeof(X)/sizeof(T)' kind is performed, where 'X' is of the 'class' type.

152.V652 . The operation is executed 3 or more times in succession.

153.V653 . A suspicious string consisting of two parts is used for the initialization. It is possible that a comma is missing.

154.V654 . The condition of loop is always true/false.

155.V655 . The strings were concatenated but are not utilized. Consider inspecting the expression.

156.V656 . Variables are initialized through the call to the same function. It's probably an error or un-optimized code.

157.V657 . It's odd that this function always returns one and the same value of NN.

158.V658 . A value is being subtracted from the unsigned variable. This can result in an overflow. In such a case, the comparison operation can potentially behave unexpectedly.

159.V659 . Declarations of functions with 'Foo' name differ in the 'const' keyword only, but the bodies of these functions have different composition. This is suspicious and can possibly be an error.

160.V660 . The program contains an unused label and a function call: 'CC:AA()'. It's possible that the following was intended: 'CC::AA()'.

161.V661 . A suspicious expression 'A[B < C]'. Probably meant 'A[B] < C'.

162.V662 . Consider inspecting the loop expression. Different containers are utilized for setting up initial and final values of the iterator.

163.V663 . Infinite loop is possible. The 'cin.eof()' condition is insufficient to break from the loop. Consider adding the 'cin.fail()' function call to the conditional expression.

164.V664 . The pointer is being dereferenced on the initialization list before it is verified against null inside the body of the constructor function.

Page 14: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

165.V665 . Possibly, the usage of '#pragma warning(default: X)' is incorrect in this context. The '#pragma warning(push/pop)' should be used instead.

166.V666 . Consider inspecting NN argument of the function 'Foo'. It is possible that the value does not correspond with the length of a string which was passed with the YY argument.

167.V667 . The 'throw' operator does not possess any arguments and is not situated within the 'catch' block.

168.V668 . There is no sense in testing the pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error.

169.V669 . The argument is a non-constant reference. The analyzer is unable to determine the position at which this argument is being modified. It is possible that the function contains an error.

170.V670 . An uninitialized class member is used to initialize another member. Remember that members are initialized in the order of their declarations inside a class.

171.V671 . It is possible that the 'swap' function interchanges a variable with itself.

172.V672 . There is probably no need in creating a new variable here. One of the function's arguments possesses the same name and this argument is a reference.

173.V673 . More than N bits are required to store the value, but the expression evaluates to the T type which can only hold K bits.

174.V674 . The expression contains a suspicious mix of integer and real types.

175.V675 . Writing into the read-only memory.

176.V676 . It is incorrect to compare the variable of BOOL type with TRUE.

177.V677 . Custom declaration of a standard type. The declaration from system header files should be used instead.

178.V678 . An object is used as an argument to its own method. Consider checking the first actual argument of the 'Foo' function.

179.V679 . The 'X' variable was not initialized. This variable is passed by a reference to the 'Foo' function in which its value will be utilized.

180.V680 . The 'delete A, B' expression only destroys the 'A' object. Then the ',' operator returns a resulting value from the right side of the expression.

181.V681 . The language standard does not define an order in which the 'Foo' functions will be called during evaluation of arguments.

182.V682 . Suspicious literal is present: '/r'. It is possible that a backslash should be used here instead: '\r'.

Page 15: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

183.V683 . Consider inspecting the loop expression. It is possible that the 'i' variable should be incremented instead of the 'n' variable.

184.V684 . A value of variable is not modified. Consider inspecting the expression. It is possible that '1' should be present instead of '0'.

185.V685 . Consider inspecting the return statement. The expression contains a comma.

186.V686 . A pattern was detected: A || (A && ...). The expression is excessive or contains a logical error.

187.V687 . Size of an array calculated by the sizeof() operator was added to a pointer. It is possible that the number of elements should be calculated by sizeof(A)/sizeof(A[0]).

188.V688 . The 'foo' local variable possesses the same name as one of the class members, which can result in a confusion.

189.V689 . The destructor of the 'Foo' class is not declared as a virtual. It is possible that a smart pointer will not destroy an object correctly.

190.V690 . The class implements a copy constructor/operator=, but lacks the operator=/copy constructor.

191.V691 . Empirical analysis. It is possible that a typo is present inside the string literal. The 'foo' word is suspicious.

192.V692 . An inappropriate attempt to append a null character to a string. To determine the length of a string by 'strlen' function correctly, a string ending with a null terminator should be used in the first place.

193.V693 . Consider inspecting conditional expression of the loop. It is possible that 'i < X.size()' should be used instead of 'X.size()'.

194.V694 . The condition (ptr - const_value) is only false if the value of a pointer equals a magic constant.

195.V695 . Range intersections are possible within conditional expressions.

196.V696 . The 'continue' operator will terminate 'do { ... } while (FALSE)' loop because the condition is always false.

197.V697 . A number of elements in the allocated array is equal to size of a pointer in bytes.

198.V698 . strcmp()-like functions can return not only the values -1, 0 and 1, but any values.

199.V699 . Consider inspecting the 'foo = bar = baz ? .... : ....' expression. It is possible that 'foo = bar == baz ? .... : ....' should be used here instead.

200.V700 . Consider inspecting the 'T foo = foo = x;' expression. It is odd that variable is initialized through itself.

Page 16: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

201.V701 . realloc() possible leak: when realloc() fails in allocating memory, original pointer is lost. Consider assigning realloc() to a temporary pointer.

202.V702 . Classes should always be derived from std::exception (and alike) as 'public'.

203.V703 . It is odd that the 'foo' field in derived class overwrites field in base class.

204.V704 . 'this == 0' comparison should be avoided - this comparison is always false on newer compilers.

205.V705 . It is possible that 'else' block was forgotten or commented out, thus altering the program's operation logics.

206.V706 . Suspicious division: sizeof(X) / Value. Size of every element in X array does not equal to divisor.

207.V707 . Giving short names to global variables is considered to be bad practice.

208.V708 . Dangerous construction is used: 'm[x] = m.size()', where 'm' is of 'T' class. This may lead to undefined behavior.

209.V709 . Suspicious comparison found: 'a == b == c'. Remember that 'a == b == c' is not equal to 'a == b && b == c'.

210.V710 . Suspicious declaration found. There is no point to declare constant reference to a number.

211.V711 . It is dangerous to create a local variable within a loop with a same name as a variable controlling this loop.

212.V712 . Be advised that compiler may delete this cycle or make it infinity. Use volatile variable(s) or synchronization primitives to avoid this.

213.V713 . The pointer was utilized in the logical expression before it was verified against nullptr in the same logical expression.

214.V714 . Variable is not passed into foreach loop by a reference, but its value is changed inside of the loop.

215.V715 . The 'while' operator has empty body. Suspicious pattern detected.

216.V716 . Suspicious type conversion: HRESULT -> BOOL (BOOL -> HRESULT).

217.V717 . It is suspicious to cast object of base class V to derived class U.

218.V718 . The 'Foo' function should not be called from 'DllMain' function.

219.V719 . The switch statement does not cover all values of the enum.

220.V720 . It is advised to utilize the 'SuspendThread' function only when developing a debugger (see documentation for details).

Page 17: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

221.V721 . The VARIANT_BOOL type is utilized incorrectly. The true value (VARIANT_TRUE) is defined as -1.

222.V722 . An abnormality within similar comparisons. It is possible that a typo is present inside the expression.

223.V723 . Function returns a pointer to the internal string buffer of a local object, which will be destroyed.

224.V724 . Converting integers or pointers to BOOL can lead to a loss of high-order bits. Non-zero value can become 'FALSE'.

225.V725 . A dangerous cast of 'this' to 'void*' type in the 'Base' class, as it is followed by a subsequent cast to 'Class' type.

226.V726 . An attempt to free memory containing the 'int A[10]' array by using the 'free(A)' function.

227.V727 . Return value of 'wcslen' function is not multiplied by 'sizeof(wchar_t)'

228.V728 . An excessive check can be simplified. The '||' operator is surrounded by opposite expressions 'x' and '!x'.

229.V729 . Function body contains the 'X' label that is not used by any 'goto' statements.

230.V730 . Not all members of a class are initialized inside the constructor.

231.V731 . The variable of char type is compared with pointer to string.

232.V732 . Unary minus operator does not modify a bool type value.

233.V733 . It is possible that macro expansion resulted in incorrect evaluation order.

234.V734 . An excessive expression. Examine the substrings "abc" and "abcd".

235.V735 . Possibly an incorrect HTML. The "</XX" closing tag was encountered, while the "</YY" tag was expected.

236.V736 . The behavior is undefined for arithmetic or comparisons with pointers that do not point to members of the same array.

237.V737 . It is possible that ',' comma is missing at the end of the string.

238.V738 . Temporary anonymous object is used.

239.V739 . EOF should not be compared with a value of the 'char' type. Consider using the 'int' type.

240.V740 . Because NULL is defined as 0, the exception is of the 'int' type. Keyword 'nullptr' could be used for 'pointer' type exception.

241.V741 . The following pattern is used: throw (a, b);. It is possible that type name was omitted: throw MyException(a, b);.

242.V742 . Function receives an address of a 'char' type variable instead of pointer to a buffer.

Page 18: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

243.V743 . The memory areas must not overlap. Use 'memmove' function.

244.V744 . Temporary object is immediately destroyed after being created. Consider naming the object.

245.V745 . A 'wchar_t *' type string is incorrectly converted to 'BSTR' type string.

246.V746 . Type slicing. An exception should be caught by reference rather than by value.

247.V747 . An odd expression inside parenthesis. It is possible that a function name is missing.

248.V748 . Memory for 'getline' function should be allocated only by 'malloc' or 'realloc' functions. Consider inspecting the first parameter of 'getline' function.

249.V749 . Destructor of the object will be invoked a second time after leaving the object's scope.

250.V750 . BSTR string becomes invalid. Notice that BSTR strings store their length before start of the text.

251.V751 . Parameter is not used inside method's body.

252.V752 . Creating an object with placement new requires a buffer of large size.

253.V753 . The '&=' operation always sets a value of 'Foo' variable to zero.

254.V754 . The expression of 'foo(foo(x))' pattern is excessive or contains an error.

255.V755 . Copying from unsafe data source. Buffer overflow is possible.

256.V756 . The 'X' counter is not used inside a nested loop. Consider inspecting usage of 'Y' counter.

257.V757 . It is possible that an incorrect variable is compared with null after type conversion using 'dynamic_cast'.

258.V758 . Reference invalidated, because of the destruction of the temporary object 'unique_ptr', returned by function.

259.V759 . Violated order of exception handlers. Exception caught by handler for base class.

260.V760 . Two identical text blocks detected. The second block starts with NN string.

261.V761 . NN identical blocks were found.

262.V762 . Consider inspecting virtual function arguments. See NN argument of function 'Foo' in derived class and base class.

263.V763 . Parameter is always rewritten in function body before being used.

264.V764 . Possible incorrect order of arguments passed to function.

Page 19: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

265.V765 . A compound assignment expression 'X += X + N' is suspicious. Consider inspecting it for a possible error.

266.V766 . An item with the same key has already been added.

267.V767 . Suspicious access to element by a constant index inside a loop.

268.V768 . The variable is of enum type. It is odd that it is used as a variable of a Boolean-type.

269.V769 . The pointer in the expression equals nullptr. The resulting value is meaningless and should not be used.

270.V770 . Possible use of a left shift operator instead of a comparison operator.

271.V771 . The '?:' operator uses constants from different enums.

272.V772 . Calling the 'delete' operator for a void pointer will cause undefined behavior.

273.V773 . The function was exited without releasing the pointer/handle. A memory/resource leak is possible.

274.V774 . The pointer was used after the memory was released.

275.V775 . It is odd that the BSTR data type is compared using a relational operator.

276.V776 . Potentially infinite loop. The variable in the loop exit condition does not change its value between iterations.

277.V777 . Dangerous widening type conversion from an array of derived-class objects to a base-class pointer.

278.V778 . Two similar code fragments were found. Perhaps, this is a typo and 'X' variable should be used instead of 'Y'.

279.V779 . Unreachable code detected. It is possible that an error is present.

280.V780 . The object of non-passive (non-PDS) type cannot be used with the function.

281.V781 . The value of the variable is checked after it was used. Perhaps there is a mistake in program logic. Check lines: N1, N2.

282.V782 . It is pointless to compute the distance between the elements of different arrays.

283.V783 . Dereferencing of invalid iterator 'X' might take place.

284.V784 . The size of the bit mask is less than the size of the first operand. This will cause the loss of the higher bits.

285.V785 . Constant expression in switch statement.

286.V786 . Assigning the value C to the X variable looks suspicious. The value range of the variable: [A, B].

Page 20: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

287.V787 . A wrong variable is probably used as an index in the for statement.

General Analysis (C#)1. V3001 . There are identical sub-expressions to the left and to the right of the

'foo' operator.

2. V3002 . The switch statement does not cover all values of the enum.

3. V3003 . The use of 'if (A) {...} else if (A) {...}' pattern was detected. There is a probability of logical error presence.

4. V3004 . The 'then' statement is equivalent to the 'else' statement.

5. V3005 . The 'x' variable is assigned to itself.

6. V3006 . The object was created but it is not being used. The 'throw' keyword could be missing.

7. V3007 . Odd semicolon ';' after 'if/for/while' operator.

8. V3008 . The 'x' variable is assigned values twice successively. Perhaps this is a mistake.

9. V3009 . It's odd that this method always returns one and the same value of NN.

10. V3010 . The return value of function 'Foo' is required to be utilized.

11. V3011 . Two opposite conditions were encountered. The second condition is always false.

12. V3012 . The '?:' operator, regardless of its conditional expression, always returns one and the same value.

13. V3013 . It is odd that the body of 'Foo_1' function is fully equivalent to the body of 'Foo_2' function.

14. V3014 . It is likely that a wrong variable is being incremented inside the 'for' operator. Consider reviewing 'X'.

15. V3015 . It is likely that a wrong variable is being compared inside the 'for' operator. Consider reviewing 'X'.

16. V3016 . The variable 'X' is being used for this loop and for the outer loop.

17. V3017 . A pattern was detected: A || (A && ...). The expression is excessive or contains a logical error.

18. V3018 . Consider inspecting the application's logic. It's possible that 'else' keyword is missing.

19. V3019 . It is possible that an incorrect variable is compared with null after type conversion using 'as' keyword.

20. V3020 . An unconditional 'break/continue/return/goto' within a loop.

Page 21: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

21. V3021 . There are two 'if' statements with identical conditional expressions. The first 'if' statement contains method return. This means that the second 'if' statement is senseless.

22. V3022 . Expression is always true/false.

23. V3023 . Consider inspecting this expression. The expression is excessive or contains a misprint.

24. V3024 . An odd precise comparison. Consider using a comparison with defined precision: Math.Abs(A - B) < Epsilon or Math.Abs(A - B) > Epsilon.

25. V3025 . Incorrect format. Consider checking the N format items of the 'Foo' function.

26. V3026 . The constant NN is being utilized. The resulting value could be inaccurate. Consider using the KK constant.

27. V3027 . The variable was utilized in the logical expression before it was verified against null in the same logical expression.

28. V3028 . Consider inspecting the 'for' operator. Initial and final values of the iterator are the same.

29. V3029 . The conditional expressions of the 'if' statements situated alongside each other are identical.

30. V3030 . Recurring check. This condition was already verified in previous line.

31. V3031 . An excessive check can be simplified. The operator '||' operator is surrounded by opposite expressions 'x' and '!x'.

32. V3032 . Waiting on this expression is unreliable, as compiler may optimize some of the variables. Use volatile variable(s) or synchronization primitives to avoid this.

33. V3033 . It is possible that this 'else' branch must apply to the previous 'if' statement.

34. V3034 . Consider inspecting the expression. Probably the '!=' should be used here.

35. V3035 . Consider inspecting the expression. Probably the '+=' should be used here.

36. V3036 . Consider inspecting the expression. Probably the '-=' should be used here.

37. V3037 . An odd sequence of assignments of this kind: A = B; B = A;

38. V3038 . The argument was passed to method several times. It is possible that another argument should be passed instead.

39. V3039 . Consider inspecting the 'Foo' function call. Defining an absolute path to the file or directory is considered a poor style.

Page 22: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

40. V3040 . The expression contains a suspicious mix of integer and real types

41. V3041 . The expression was implicitly cast from integer type to real type. Consider utilizing an explicit type cast to avoid the loss of a fractional part.

42. V3042 . Possible NullReferenceException. The '?.' and '.' operators are used for accessing members of the same object.

43. V3043 . The code's operational logic does not correspond with its formatting.

44. V3044 . WPF: writing and reading are performed on a different Dependency Properties.

45. V3045 . WPF: the names of the property registered for DependencyProperty, and of the property used to access it, do not correspond with each other.

46. V3046 . WPF: the type registered for DependencyProperty does not correspond with the type of the property used to access it.

47. V3047 . WPF: A class containing registered property does not correspond with a type that is passed as the ownerType.type.

48. V3048 . WPF: several Dependency Properties are registered with a same name within the owner type.

49. V3049 . WPF: readonly field of 'DependencyProperty' type is not initialized.

50. V3050 . Possibly an incorrect HTML. The </XX> closing tag was encountered, while the </YY> tag was expected.

51. V3051 . An excessive type cast or check. The object is already of the same type.

52. V3052 . The original exception object was swallowed. Stack of original exception could be lost.

53. V3053 . An excessive expression. Examine the substrings "abc" and "abcd".

54. V3054 . Potentially unsafe double-checked locking. Use volatile variable(s) or synchronization primitives to avoid this.

55. V3055 . Suspicious assignment inside the condition expression of 'if/while/for' operator.

56. V3056 . Consider reviewing the correctness of 'X' item's usage.

57. V3057 . Function receives an odd argument.

58. V3058 . An item with the same key has already been added.

59. V3059 . Consider adding '[Flags]' attribute to the enum.

60. V3060 . A value of variable is not modified. Consider inspecting the expression. It is possible that other value should be present instead of '0'.

61. V3061 . Parameter 'A' is always rewritten in method body before being used.

Page 23: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

62. V3062 . An object is used as an argument to its own method. Consider checking the first actual argument of the 'Foo' method

63. V3063 . A part of conditional expression is always true/false if it is evaluated.

64. V3064 . Division or mod division by zero.

65. V3065 . Parameter is not utilized inside method's body.

66. V3066 . Possible incorrect order of arguments passed to method.

67. V3067 . It is possible that 'else' block was forgotten or commented out, thus altering the program's operation logics.

68. V3068 . Calling overrideable class member from constructor is dangerous.

69. V3069 . It's possible that the line was commented out improperly, thus altering the program's operation logics.

70. V3070 . Uninitialized variables are used when initializing the 'A' variable.

71. V3071 . The object is returned from inside 'using' block. 'Dispose' will be invoked before exiting method.

72. V3072 . The 'A' class containing IDisposable members does not itself implement IDisposable.

73. V3073 . Not all IDisposable members are properly disposed. Call 'Dispose' when disposing 'A' class.

74. V3074 . The 'A' class contains 'Dispose' method. Consider making it implement 'IDisposable' interface.

75. V3075 . The operation is executed 2 or more times in succession.

76. V3076 . Comparison with 'double.NaN' is meaningless. Use 'double.IsNaN()' method instead.

77. V3077 . Property setter / event accessor does not utilize its 'value' parameter.

78. V3078 . Original sorting order will be lost after repetitive call to 'OrderBy' method. Use 'ThenBy' method to preserve the original sorting.

79. V3079 . 'ThreadStatic' attribute is applied to a non-static 'A' field and will be ignored.

80. V3080 . Possible null dereference.

81. V3081 . The 'X' counter is not used inside a nested loop. Consider inspecting usage of 'Y' counter.

82. V3082 . The 'Thread' object is created but is not started. It is possible that a call to 'Start' method is missing.

83. V3083 . Unsafe invocation of event, NullReferenceException is possible. Consider assigning event to a local variable before invoking it.

Page 24: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

84. V3084 . Anonymous function is used to unsubscribe from event. No handlers will be unsubscribed, as a separate delegate instance is created for each anonymous function declaration.

85. V3085 . The name of 'X' field/property in a nested type is ambiguous. The outer type contains static field/property with identical name.

86. V3086 . Variables are initialized through the call to the same function. It's probably an error or un-optimized code.

87. V3087 . Type of variable enumerated in 'foreach' is not guaranteed to be castable to the type of collection's elements.

88. V3088 . The expression was enclosed by parentheses twice: ((expression)). One pair of parentheses is unnecessary or misprint is present.

89. V3089 . Initializer of a field marked by [ThreadStatic] attribute will be called once on the first accessing thread. The field will have default value on different threads.

90. V3090 . Unsafe locking on an object.

91. V3091 . Empirical analysis. It is possible that a typo is present inside the string literal. The 'foo' word is suspicious.

92. V3092 . Range intersections are possible within conditional expressions.

93. V3093 . The operator evaluates both operands. Perhaps a short-circuit operator should be used instead.

94. V3094 . Possible exception when deserializing type. The Ctor(SerializationInfo, StreamingContext) constructor is missing.

95. V3095 . The object was used before it was verified against null. Check lines: N1, N2.

96. V3096 . Possible exception when serializing type. [Serializable] attribute is missing.

97. V3097 . Possible exception: type marked by [Serializable] contains non-serializable members not marked by [NonSerialized].

98. V3098 . The 'continue' operator will terminate 'do { ... } while (false)' loop because the condition is always false.

99. V3099 . Not all the members of type are serialized inside 'GetObjectData' method.

100.V3100 . NullReferenceException is possible. Unhandled exceptions in destructor lead to termination of runtime.

101.V3101 . Potential resurrection of 'this' object instance from destructor. Without re-registering for finalization, destructor will not be called a second time on resurrected object.

102.V3102 . Suspicious access to element by a constant index inside a loop.

Page 25: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

103.V3103 . A private Ctor(SerializationInfo, StreamingContext) constructor in unsealed type will not be accessible when deserializing derived types.

104.V3104 . 'GetObjectData' implementation in unsealed type is not virtual, incorrect serialization of derived type is possible.

105.V3105 . The 'a' variable was used after it was assigned through null-conditional operator. NullReferenceException is possible.

106.V3106 . Possibly index is out of bound.

107.V3107 . Identical expressions to the left and to the right of compound assignment.

108.V3108 . It is not recommended to return null or throw exceptions from 'ToString()' method.

109.V3109 . The same sub-expression is present on both sides of the operator. The expression is incorrect or it can be simplified.

110.V3110 . Possible infinite recursion.

111.V3111 . Checking value for null will always return false when generic type is instantiated with a value type.

112.V3112 . An abnormality within similar comparisons. It is possible that a typo is present inside the expression.

113.V3113 . Consider inspecting the loop expression. It is possible that different variables are used inside initializer and iterator.

114.V3114 . IDisposable object is not disposed before method returns.

115.V3115 . It is not recommended to throw exceptions from 'Equals(object obj)' method.

116.V3116 . Consider inspecting the 'for' operator. It's possible that the loop will be executed incorrectly or won't be executed at all.

117.V3117 . Constructor parameter is not used.

118.V3118 . A component of TimeSpan is used, which does not represent full time interval. Possibly 'Total*' value was intended instead.

119.V3119 . Calling a virtual (overridden) event may lead to unpredictable behavior. Consider implementing event accessors explicitly or use 'sealed' keyword.

120.V3120 . Potentially infinite loop. The variable in the loop exit condition does not change its value between iterations.

121.V3121 . An enumeration was declared with 'Flags' attribute, but no initializers were set to override default values.

122.V3122 . Uppercase (lowercase) string is compared with a different lowercase (uppercase) string.

Page 26: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

123.V3123 . Perhaps the '??' operator works differently from what was expected. Its priority is lower than that of other operators in its left part.

124.V3124 . Appending an element and checking for key uniqueness is performed on two different variables.

125.V3125 . The object was used after it was verified against null. Check lines: N1, N2.

126.V3126 . Type implementing IEquatable<T> interface does not override 'GetHashCode' method.

127.V3127 . Two similar code fragments were found. Perhaps this is a typo and 'X' variable should be used instead of 'Y'.

128.V3128 . The field (property) is used before it is initialized in constructor.

129.V3129 . The value of the captured variable will be overwritten on the next iteration of the loop in each instance of anonymous function that captures it.

130.V3130 . Priority of the '&&' operator is higher than that of the '||' operator. Possible missing parentheses.

131.V3131 . The expression is checked for compatibility with type 'A' but is cast to type 'B'.

132.V3132 . A terminal null is present inside a string. '\0xNN' character sequence was encountered. Probably meant: '\xNN'.

133.V3133 . Postfix increment/decrement is meaningless because this variable is overwritten.

134.V3134 . Shift by N bits is greater than the size of type.

Diagnosis of micro-optimizations (C++)1. V801 . Decreased performance. It is better to redefine the N function

argument as a reference. Consider replacing 'const T' with 'const .. &T' / 'const .. *T'.

2. V802 . On 32-bit/64-bit platform, structure size can be reduced from N to K bytes by rearranging the fields according to their sizes in decreasing order.

3. V803 . Decreased performance. It is more effective to use the prefix form of ++it. Replace iterator++ with ++iterator.

4. V804 . Decreased performance. The 'Foo' function is called twice in the specified expression to calculate length of the same string.

5. V805 . Decreased performance. It is inefficient to identify an empty string by using 'strlen(str) > 0' construct. A more efficient way is to check: str[0] != '\0'

6. V806 . Decreased performance. The expression of strlen(MyStr.c_str()) kind can be rewritten as MyStr.length().

Page 27: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

7. V807 . Decreased performance. Consider creating a pointer/reference to avoid using the same expression repeatedly.

8. V808 . An array/object was declared but was not utilized.

9. V809 . Verifying that a pointer value is not NULL is not required. The 'if (ptr != NULL)' check can be removed.

10. V810 . Decreased performance. The 'A' function was called several times with identical arguments. The result should possibly be saved to a temporary variable, which then could be used while calling the 'B' function.

11. V811 . Decreased performance. Excessive type casting: string -> char * -> string.

12. V812 . Decreased performance. Ineffective use of the 'count' function. It can possibly be replaced by the call to the 'find' function.

13. V813 . Decreased performance. The argument should probably be rendered as a constant pointer/reference.

14. V814 . Decreased performance. The 'strlen' function was called multiple times inside the body of a loop.

15. V815 . Decreased performance. Consider replacing the expression 'AA' with 'BB'.

16. V816 . It is more efficient to catch exception by reference rather than by value.

17. V817 . It is more efficient to search for 'X' character rather than a string.

Diagnosis of 64-bit errors (Viva64, C++)1. V101 . Implicit assignment type conversion to memsize type.

2. V102 . Usage of non memsize type for pointer arithmetic.

3. V103 . Implicit type conversion from memsize type to 32-bit type.

4. V104 . Implicit type conversion to memsize type in an arithmetic expression.

5. V105 . N operand of '?:' operation: implicit type conversion to memsize type.

6. V106 . Implicit type conversion N argument of function 'foo' to memsize type.

7. V107 . Implicit type conversion N argument of function 'foo' to 32-bit type.

8. V108 . Incorrect index type: 'foo[not a memsize-type]'. Use memsize type instead.

9. V109 . Implicit type conversion of return value to memsize type.

10. V110 . Implicit type conversion of return value from memsize type to 32-bit type.

Page 28: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

11. V111 . Call of function 'foo' with variable number of arguments. N argument has memsize type.

12. V112 . Dangerous magic number N used.

13. V113 . Implicit type conversion from memsize to double type or vice versa.

14. V114 . Dangerous explicit type pointer conversion.

15. V115 . Memsize type is used for throw.

16. V116 . Memsize type is used for catch.

17. V117 . Memsize type is used in the union.

18. V118 . malloc() function accepts a dangerous expression in the capacity of an argument.

19. V119 . More than one sizeof() operator is used in one expression.

20. V120 . Member operator[] of object 'foo' declared with 32-bit type argument, but called with memsize type argument.

21. V121 . Implicit conversion of the type of 'new' operator's argument to size_t type.

22. V122 . Memsize type is used in the struct/class.

23. V123 . Allocation of memory by the pattern "(X*)malloc(sizeof(Y))" where the sizes of X and Y types are not equal.

24. V124 . Function 'Foo' writes/reads 'N' bytes. The alignment rules and type sizes have been changed. Consider reviewing this value.

25. V125 . It is not advised to declare type 'T' as 32-bit type.

26. V126 . Be advised that the size of the type 'long' varies between LLP64/LP64 data models.

27. V127 . An overflow of the 32-bit variable is possible inside a long cycle which utilizes a memsize-type loop counter.

28. V128 . A variable of the memsize type is read from a stream. Consider verifying the compatibility of 32 and 64 bit versions of the application in the context of a stored data.

29. V201 . Explicit conversion from 32-bit integer type to memsize type.

30. V202 . Explicit conversion from memsize type to 32-bit integer type.

31. V203 . Explicit type conversion from memsize to double type or vice versa.

32. V204 . Explicit conversion from 32-bit integer type to pointer type.

33. V205 . Explicit conversion of pointer type to 32-bit integer type.

34. V206 . Explicit conversion from 'void *' to 'int *'.

Page 29: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

35. V207 . A 32-bit variable is utilized as a reference to a pointer. A write outside the bounds of this variable may occur.

36. V220 . Suspicious sequence of types castings: memsize -> 32-bit integer -> memsize.

37. V221 . Suspicious sequence of types castings: pointer -> memsize -> 32-bit integer.

38. V301 . Unexpected function overloading behavior. See N argument of function 'foo' in derived class 'derived' and base class 'base'.

39. V302 . Member operator[] of 'foo' class has a 32-bit type argument. Use memsize-type here.

40. V303 . The function is deprecated in the Win64 system. It is safer to use the 'foo' function.

Customers Specific Requests (C++)1. V2001 . Consider using the extended version of the 'foo'function here.

2. V2002 . Consider using the 'Ptr' version of the 'foo' function here.

3. V2003 . Explicit conversion from 'float/double' type to signed integer type.

4. V2004 . Explicit conversion from 'float/double' type to unsigned integer type.

5. V2005 . C-style explicit type casting is utilized. Consider using: static_cast/const_cast/reinterpret_cast.

6. V2006 . Implicit type conversion from enum type to integer type.

7. V2007 . This expression can be simplified. One of the operands in the operation equals NN. Probably it is a mistake.

8. V2008 . Cyclomatic complexity: NN. Consider refactoring the 'Foo' function.

9. V2009 . Consider passing the 'Foo' argument as a constant pointer/reference.

10. V2010 . Handling of two different exception types is identical.

11. V2011 . Consider inspecting signed and unsigned function arguments. See NN argument of function 'Foo' in derived class and base class.

12. V2012 . Possibility of decreased performance. It is advised to pass arguments to std::unary_function/std::binary_function template as references.

13. V2013 . Consider inspecting the correctness of handling the N argument in the 'Foo' function.

Problems related to code analyzer (C++, C#)

Page 30: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

1. V001 . A code fragment from 'file' cannot be analyzed.

2. V002 . Some diagnostic messages may contain incorrect line number.

3. V003 . Unrecognized error found...

4. V004 . Diagnostics from the 64-bit rule set are not entirely accurate without the appropriate 64-bit compiler. Consider utilizing 64-bit compiler if possible.

5. V005 . Cannot determine active configuration for project. Please check projects and solution configurations.

6. V006 . File cannot be processed. Analysis aborted by timeout.

7. V007 . Deprecated CLR switch was detected. Incorrect diagnostics are possible.

8. V008 . Unable to start the analysis on this file.

9. V009 . To use free version of PVS-Studio, source code files are required to start with a special comment.

10. V051 . Some of the references in project are missing or incorrect. The analysis results could be incomplete. Consider making the project fully compilable and building it before analysis.

11. V052 . A critical error had occurred.

Two words about PVS-StudioWe develop the PVS-Studio static code analyzer for C/C++/C#.

PVS-Studio is a static code analyzer for C/C++ (Visual Studio 2017, 2015, 2013,

2012, 2010; makefile-based projects using Visual C++ or MinGW/GCC) with a

simple licensing and pricing policies which is easy to install and use without need to

deploy a complex maintenance environment.

To promote it, we check code of well-known projects such as Chromium,

WinMerge, TortoiseSVN, Apache HTTP Server, Qt, Clang, etc. and publish results

in the form of articles.

Getting Acquainted with the PVS-Studio Static Code Analyzer

Page 31: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Pros of using a static analyzer

PVS-Studio's capabilities

o Warning levels and diagnostic rule sets

o Message filtering

o PVS-Studio and Microsoft Visual Studio

o Standalone

o Help system and technical support

PVS-Studio is a static analyzer for C/C++/C# code designed to assist programmers

in searching for and fixing a number of software errors of different patterns. The

analyzer integrates into Visual Studio as a plugin, providing a convenient user

interface for easy code navigation and error search. There is also a Standalone

version available which is used independently of Visual Studio and allows

analyzing files compiled with, besides Visual C++, such compilers as Embarcadero

C++Builder, GCC (MinGW), and Clang.

Pros of using a static analyzerA static analyzer does not substitute other bug searching tools - it just complements

them. Integrating a static analysis tool with the development process helps to

eliminate plenty of errors at the moment when they are only "born", thus saving

your time and resources on their subsequent elimination. As everyone knows, the

earlier a bug is found, the easier it is to fix it. What follows from this is the idea that

a static analyzer should be used regularly, for it is the only best way to get most of

it.

PVS-Studio's capabilitiesWarning levels and diagnostic rule sets

PVS-Studio provides 3 warning levels, the 1-st level dealing with the most critical

issues and the 3-rd with insignificant faults in the code or warnings which are very

likely to be false positives. Warning level switch buttons allow sorting the messages

according to the user's current needs.

Page 32: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

There are 3 types of diagnostic rules: general analysis diagnostics (GA), diagnostics

for microoptimizations (OP), and 64-bit diagnostics (64). Switching a certain

diagnostic rule set shows or hides the corresponding messages.

Figure 1 - Message output window (click on the image to enlarge it)

Message filtering

The tool provides an option to filter messages by a number of different criteria.

Also, you can completely turn off the displaying of certain messages, which may be

useful at times.

PVS-Studio and Microsoft Visual Studio

When installing PVS-Studio, you can choose which versions of the Microsoft

Visual Studio IDE the analyzer should integrate with.

After deciding on all the necessary options and completing the setup, PVS-Studio

will integrate into the IDE's menu. In Figure 2, you can see that the corresponding

command has appeared in Visual Studio's menu, as well as the message output

window.

Page 33: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Figure 2 - Microsoft Visual Studio's appearance after PVS-Studio's integration (click on the image to

enlarge it)

In the settings menu, you can customize PVS-Studio as you need to make it most

convenient to work with. For example, it provides the following options:

Preprocessor selection;

Exclusion of files and folders from analysis;

Selection of the diagnostic message types to be displayed during the analysis;

Plenty of other settings.

Most likely, you won't need any of those at your first encounter with PVS-Studio,

but later, they will help you optimize your work with the tool.

To learn more about specifics of PVS-Studio's work when integrated into Microsoft

Visual Studio, see the article PVS-Studio for Visual C++.

Standalone

PVS-Studio can be used independently of the Microsoft Visual Studio IDE. The

Standalone version allows analyzing projects while building them. It also supports

code navigation through clicking on the diagnostic messages, and search for code

fragments and definitions of macros and data types. To learn more about how to

work with the Standalone version, see the article Standalone.

Page 34: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Figure 3 - Standalone's start page (click on the image to enlarge it)

Help system and technical support

PVS-Studio provides an extensive help system on its diagnostic messages. This

message database is accessible both from PVS-Studio's interface and at the official

site. The message descriptions are accompanied by code samples with error

examples, the error description, and available fixing solutions.

To open a diagnostic description, just click with the left mouse button on the

diagnostic number in the message output window. These numbers are implemented

as hyperlinks.

Technical support for PVS-Studio is carried out via e-mail. Since our technical

support is delivered by the tool developers themselves, our users can promptly get

responses to a wide variety of questions.

System requirements for PVS-Studio analyzerThe PVS-Studio analyzer is intended to work on the Windows platform. It

integrates into Microsoft Visual Studio 2017, 2015, 2013, 2012, 2010 development

Page 35: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

environments. System requirements for the analyzer coincide with requirements for

Microsoft Visual Studio:

Development environment: Microsoft Visual Studio 2017, 2015, 2013, 2012, 2010. It is advisable to install the "X64 Compilers and Tools" Visual Studio component for the analysis of 64-bit applications. It is included into all the mentioned versions of Visual Studio and can be installed through Visual Studio Setup. Note that PVS-Studio cannot work with Visual C++ Express Edition since this system does not support extension packages.

Operating system: Windows Vista/2008/7/8/2012/10 x64.

Hardware: PVS-Studio works on systems with main memory of 1 GB at least (the recommended size is 2 GBs or more) for each processor core; the analyzer supports multi-core operation (the more cores you have, the faster code analysis is).

PVS-Studio's Trial Mode

Limitations and recommendations

o A lot does not mean useful

o We are here to help

I am experienced enough

Usually limitations have two purposes. The first - to show a potential user that a

static analyzer is able to find bugs in the code. The second - to prompt the user to

communicate with us via e-mail so that we could help use the tool correctly. I am

convinced that this interrelation is not clear yet, that's why I've decided to write this

little note.

Limitations and recommendationsLet's talk about our recommendations and existing limitations of PVS-Studio Trial

mode.

A lot does not mean useful

Page 36: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The most common wrong behavior pattern: a programmer turns on all the warning

settings to the maximum. This is our biggest pain. They enable all types of warnings

(general-purpose, 64-bit ones, optimizations), all levels of warnings; some people

even manage to find our custom-built warnings and turn them on.

Programmers explain it by saying that they want to see everything that the analyzer

is capable of. And this is totally wrong. A right aim would be to see how the

analyzer could be beneficial for the project. That is, first of all, you should see that

the analyzer can find real errors in the code. By turning all the warnings to the

maximum, you have a chance to drown in the large amount of warnings. Having

looked through 20-30 uninteresting warnings people lose interest. Most likely, the

stage of familiarizing with the tool will end at this point. However, if the number of

warnings that the person can see is decreased (by example, by filtering them out),

the probability to discover serious errors will increase. Then the programmer will

treat the tool differently. He will try to filter uninteresting warnings, customize the

tool and learn about the ways to suppress false positives in macros and so on...

There is another point concerning a big amount of warnings. The programmer can

be aware that he is looking at both high and low - severity warnings and he is ready

to look through a big number of messages. The trouble is that he quickly takes one's

eye off the ball. Roughly speaking, having looked at 10 warnings, he will most

likely miss the eleventh one that will point to a real issue.

Therefore, we recommend checking only High and Medium severity warnings when

using the analyzer for the first time.

We are here to help

Now, some brief facts about the existing limitations of trial mode. There is a limited

number of jump-clicks to the code that the user can perform.

Let's go through these restrictions and have a look at the reasons behind them. All

the stories are based on true facts. These restrictions aren't made up by a market

manager, they resulted from long communication with potential clients and

observations of the way people get acquainted with PVS-Studio.

Page 37: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

When a user is run out of the "jump-clicks", the program will offer to fill in a small

form with contact details that we use to find out if we can help with anything else.

After that, the user gets another portion of "clicks".

What's the point in contacting us? First of all, we can give a temporary key for a

closer look at PVS-Studio. By this moment the programmer got used to the tool,

found bugs in his code and now he is ready to see the warnings of other levels.

Secondly, what is important is that we want to help a person get familiar with PVS-

Studio. You cannot even imagine, how large the amount of ways to use this tool

incorrectly is. I'll p some examples here.

Someone may have a "nasty macro" and the analyzer issues a lot of meaningless

warnings. That's how the person loses his "clicks" going to the code fragments.

After that, asking a question "Is everything fine?", we get something like:

It's awful. How in the world people use this analyzer. I am sick of looking at the

warnings of the Vxxx number.

This is when we help telling the person about various ways to suppress the warnings

in macros or that the person can just turn off this diagnostic, for a start.

Another person complains that the warnings issued for the third-party libraries

really bother him.

Then we give a hint that such warnings can be disabled in two clicks. Really, it's

just 2 clicks.

In both cases we helped to make the life easier. If there was no communication,

those people would continue thinking how terrible the analyzer is. And most likely

won't even consider getting the license for the tool.

I am experienced enoughHere is what we can say to those who aren't new to the static analysis tools. It's all

very simple. Contact us and we'll give you a temporary key to investigate the

analyzer.

Page 38: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

PVS-Studio Release History

PVS-Studio 6.15 (April 27, 2017)

PVS-Studio 6.14 (March 17, 2017)

PVS-Studio 6.13 (January 27, 2017)

PVS-Studio 6.12 (December 22, 2016)

PVS-Studio 6.11 (November 29, 2016)

PVS-Studio 6.10 (October 25, 2016)

PVS-Studio 6.09 (October 6, 2016)

PVS-Studio 6.08 (August 22, 2016)

PVS-Studio 6.07 (August 8, 2016)

PVS-Studio 6.06 (July 7, 2016)

PVS-Studio 6.05 (June 9, 2016)

PVS-Studio 6.04 (May 16, 2016)

PVS-Studio 6.03 (April 5, 2016)

PVS-Studio 6.02 (March 9, 2016)

PVS-Studio 6.01 (February 3, 2016)

PVS-Studio 6.00 (December 22, 2015)

Release history for old versions

PVS-Studio 6.15 (April 27, 2017) Visual Studio 2017 support improved.

Fixed issue related to specific .pch files.

V782 . It is pointless to compute the distance between the elements of different arrays.

V783 . Dereferencing of invalid iterator 'X' might take place.

V784 . The size of the bit mask is less than the size of the first operand. This will cause the loss of the higher bits.

V785 . Constant expression in switch statement.

V786 . Assigning the value C to the X variable looks suspicious. The value range of the variable: [A, B].

V787 . A wrong variable is probably used as an index in the for statement.

Page 39: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

PVS-Studio 6.14 (March 17, 2017) Visual Studio 2017 support added.

Support of Roslyn 2.0 / C# 7.0 in C# PVS-Studio Analyzer.

Line highlighting added when viewing the analyzer messages in Visual Studio plugins and Standalone version.

The issue of checking C++ projects fixed. It could appear during the start of the analysis on the system without an installed Visual Studio 2015 /MSBuild 14.

V780 . The object of non-passive (non-PDS) type cannot be used with the function.

V781 . The value of the variable is checked after it was used. Perhaps there is a mistake in program logic. Check lines: N1, N2.

V3131 . The expression is checked for compatibility with type 'A' but is cast to type 'B'.

V3132 . A terminal null is present inside a string. '\0xNN' character sequence was encountered. Probably meant: '\xNN'.

V3133 . Postfix increment/decrement is meaningless because this variable is overwritten.

V3134 . Shift by N bits is greater than the size of type.

PVS-Studio 6.13 (January 27, 2017) Incremental analysis mode is added to the cmd version of the analyzer (PVS-

Studio_Cmd.exe). More details can be found in the documentation.

V779 . Unreachable code detected. It is possible that an error is present.

V3128 . The field (property) is used before it is initialized in constructor.

V3129 . The value of the captured variable will be overwritten on the next iteration of the loop in each instance of anonymous function that captures it.

V3130 . Priority of the '&&' operator is higher than that of the '||' operator. Possible missing parentheses.

PVS-Studio 6.12 (December 22, 2016) V773 . The function was exited without releasing the pointer. A memory leak

is possible.

V774 . The pointer was used after the memory was released.

Page 40: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V775 . It is odd that the BSTR data type is compared using a relational operator.

V776 . Potentially infinite loop. The variable in the loop exit condition does not change its value between iterations.

V777 . Dangerous widening type conversion from an array of derived-class objects to a base-class pointer.

V778 . Two similar code fragments were found. Perhaps, this is a typo and 'X' variable should be used instead of 'Y'.

V3123 . Perhaps the '??' operator works differently from what was expected. Its priority is lower than that of other operators in its left part.

V3124 . Appending an element and checking for key uniqueness is performed on two different variables.

V3125 . The object was used after it was verified against null. Check lines: N1, N2.

V3126 . Type implementing IEquatable<T> interface does not override 'GetHashCode' method.

PVS-Studio 6.11 (November 29, 2016) V771 . The '?:' operator uses constants from different enums.

V772 . Calling the 'delete' operator for a void pointer will cause undefined behavior.

V817 . It is more efficient to search for 'X' character rather than a string.

V3119 . Calling a virtual (overridden) event may lead to unpredictable behavior. Consider implementing event accessors explicitly or use 'sealed' keyword.

V3120 . Potentially infinite loop. The variable in the loop exit condition does not change its value between iterations.

V3121 . An enumeration was declared with 'Flags' attribute, but no initializers were set to override default values.

V3122 . Uppercase (lowercase) string is compared with a different lowercase (uppercase) string.

Support for analyzing Visual C++ projects (.vcxproj) with Intel C++ toolsets was implemented in Visual Studio plug-in.

PVS-Studio 6.10 (October 25, 2016) We are releasing PVS-Studio for Linux! Now it is possible to check C and

C+ source code with PVS-Studio not only under Windows, but under Linux

Page 41: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

as well. The analyzer is available as packages for the mainstream package management systems, and is easily integratable with most common build systems. The detailed documentation on using PVS-Studio Linux version is available here.

PVS-Studio for Windows is updated with a new user interface! The update affects Vidual Studio plug-in and Standalone PVS-Studio tool.

PVS-Studio now includes the new BlameNotifier tool. It allows to easily organize e-mail notifications with PVS-Studio analyzer messages of developers responsible for the source code that triggers these messages. Supported VCSs are Git, Svn and Mercurial. A detailed guide on managing the analysis results is available here.

The support for analyzing MSBuild projects, which are using the Intel C++ compiler, was implemented in the PVS-Studio command line version. The support for Visual Studio is coming in the near future.

V769 . The pointer in the expression equals nullptr. The resulting value is meaningless and should not be used.

V770 . Possible usage of a left shift operator instead of a comparison operator.

PVS-Studio 6.09 (October 6, 2016) If all the diagnostic groups of the analyzer (C++ or C#) are disabled, the

analysis of projects of the corresponding language won't start.

We have added proxy support with the authorization during the update check and the trial extension.

The ability to completely disable C/C++ or C# analyzer in .pvsconfig files (//-V::C++ and //-V::C#) is now supported.

In the SonarQube plugin implemented functionality for calculating the LOC metric and determining the reliability remediation effort.

V768 . The '!' operator is applied to an enumerator.

V3113 . Consider inspecting the loop expression. It is possible that different variables are used inside initializer and iterator.

V3114 . IDisposable object is not disposed before method returns.

V3115 . It is not recommended to throw exceptions from 'Equals(object obj)' method.

V3116 . Consider inspecting the 'for' operator. It's possible that the loop will be executed incorrectly or won't be executed at all.

V3117 . Constructor parameter is not used.

Page 42: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V3118 . A component of TimeSpan is used, which does not represent full time interval. Possibly 'Total*' value was intended instead.

PVS-Studio 6.08 (August 22, 2016) Visual Studio plug-in no longer supports analysis from command line with

'/command' switch. Please use PVS-Studio_Cmd.exe command line tool instead. The detailed description of the tool is available here.

V3108 . It is not recommended to return null or throw exceptions from 'ToSting()' method.

V3109 . The same sub-expression is present on both sides of the operator. The expression is incorrect or it can be simplified.

V3110 . Possible infinite recursion.

V3111 . Checking value for null will always return false when generic type is instantiated with a value type.

V3112 . An abnormality within similar comparisons. It is possible that a typo is present inside the expression.

PVS-Studio 6.07 (August 8, 2016) We are heading towards Linux support! Please read How to run PVS-Studio

on Linux.

PVS-Studio no longer supports 32-bit operating systems. PVS-Studio analyzer (both C++ and C# modules) requires quite a large amount of RAM for its operation, especially when using multiple processor cores during the analysis. The maximum amount of RAM available on a 32-bit system allows correctly running the analyzer on a single core only (i.e. one process at a time). Moreover, in case of a very large project being analyzed, even this amount of RAM could be insufficient. Because of this, and also because a very small fraction of our users still utilizes 32-bit OS, we've decided to cease support for the 32-bit version of the analyzer. This will allows us to concentrate all of our resources on further development of 64-bit version of the analyzer.

Support for SonarQube continuous quality control system was implemented in the analyzer's command line version. In addition, our installer now contains a dedicated SonarQube plugin, which can be used for integration of analysis results with SonarQube server. The detailed description of this plugin and new analyzer modes is available here.

V763 . Parameter is always rewritten in function body before being used.

V764 . Possible incorrect order of arguments passed to function.

Page 43: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V765 . A compound assignment expression 'X += X + N' is suspicious. Consider inspecting it for a possible error.

V766 . An item with the same key has already been added.

V767 . Suspicious access to element by a constant index inside a loop.

V3106 . Possibly index is out of bound.

V3107 . Identical expressions to the left and to the right of compound assignment.

PVS-Studio 6.06 (July 7, 2016) V758 . Reference invalidated, because of the destruction of the temporary

object 'unique_ptr', returned by function.

V759 . Violated order of exception handlers. Exception caught by handler for base class.

V760 . Two identical text blocks detected. The second block starts with NN string.

V761 . NN identical blocks were found.

V762 . Consider inspecting virtual function arguments. See NN argument of function 'Foo' in derived class and base class.

V3105 . The 'a' variable was used after it was assigned through null-conditional operator. NullReferenceException is possible.

PVS-Studio 6.05 (June 9, 2016) New PVS-Studio command line tool was added; it supports the check of

vcxproj and csproj projects (C++ and C#). Now there is no need to use devenv.exe for nightly checks. More details about this tool can be found here.

The support of MSBuild plugin was stopped. Instead of it we suggest using a new PVS-Studio command line tool.

V755 . Copying from unsafe data source. Buffer overflow is possible.

V756 . The 'X' counter is not used inside a nested loop. Consider inspecting usage of 'Y' counter.

V757 . It is possible that an incorrect variable is compared with null after type conversion using 'dynamic_cast'.

V3094 . Possible exception when deserializing type. The Ctor(SerializationInfo, StreamingContext) constructor is missing.

V3095 . The object was used before it was verified against null. Check lines: N1, N2.

Page 44: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V3096 . Possible exception when serializing type. [Serializable] attribute is missing.

V3097 . Possible exception: type marked by [Serializable] contains non-serializable members not marked by [NonSerialized].

V3098 . The 'continue' operator will terminate 'do { ... } while (false)' loop because the condition is always false.

V3099 . Not all the members of type are serialized inside 'GetObjectData' method.

V3100 . Unhandled NullReferenceException is possible. Unhandled exceptions in destructor lead to termination of runtime.

V3101 . Potential resurrection of 'this' object instance from destructor. Without re-registering for finalization, destructor will not be called a second time on resurrected object.

V3102 . Suspicious access to element by a constant index inside a loop.

V3103 . A private Ctor(SerializationInfo, StreamingContext) constructor in unsealed type will not be accessible when deserializing derived types.

V3104 . 'GetObjectData' implementation in unsealed type is not virtual, incorrect serialization of derived type is possible.

PVS-Studio 6.04 (May 16, 2016) V753 . The '&=' operation always sets a value of 'Foo' variable to zero.

V754 . The expression of 'foo(foo(x))' pattern is excessive or contains an error.

V3082 . The 'Thread' object is created but is not started. It is possible that a call to 'Start' method is missing.

V3083 . Unsafe invocation of event, NullReferenceException is possible. Consider assigning event to a local variable before invoking it.

V3084 . Anonymous function is used to unsubscribe from event. No handlers will be unsubscribed, as a separate delegate instance is created for each anonymous function declaration.

V3085 . The name of 'X' field/property in a nested type is ambiguous. The outer type contains static field/property with identical name.

V3086 . Variables are initialized through the call to the same function. It's probably an error or un-optimized code.

V3087 . Type of variable enumerated in 'foreach' is not guaranteed to be castable to the type of collection's elements.

V3088 . The expression was enclosed by parentheses twice: ((expression)). One pair of parentheses is unnecessary or misprint is present.

Page 45: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V3089 . Initializer of a field marked by [ThreadStatic] attribute will be called once on the first accessing thread. The field will have default value on different threads.

V3090 . Unsafe locking on an object.

V3091 . Empirical analysis. It is possible that a typo is present inside the string literal. The 'foo' word is suspicious.

V3092 . Range intersections are possible within conditional expressions.

V3093 . The operator evaluates both operands. Perhaps a short-circuit operator should be used instead.

PVS-Studio 6.03 (April 5, 2016) V751 . Parameter is not used inside method's body.

V752 . Creating an object with placement new requires a buffer of large size.

V3072 . The 'A' class containing IDisposable members does not itself implement IDisposable.

V3073 . Not all IDisposable members are properly disposed. Call 'Dispose' when disposing 'A' class.

V3074 . The 'A' class contains 'Dispose' method. Consider making it implement 'IDisposable' interface.

V3075 . The operation is executed 2 or more times in succession.

V3076 . Comparison with 'double.NaN' is meaningless. Use 'double.IsNaN()' method instead.

V3077 . Property setter / event accessor does not utilize its 'value' parameter.

V3078 . Original sorting order will be lost after repetitive call to 'OrderBy' method. Use 'ThenBy' method to preserve the original sorting.

V3079 . 'ThreadStatic' attribute is applied to a non-static 'A' field and will be ignored.

V3080 . Possible null dereference.

V3081 . The 'X' counter is not used inside a nested loop. Consider inspecting usage of 'Y' counter.

V051 . Some of the references in project are missing or incorrect. The analysis results could be incomplete. Consider making the project fully compilable and building it before analysis.

PVS-Studio 6.02 (March 9, 2016) V3057 . Function receives an odd argument.

Page 46: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V3058 . An item with the same key has already been added.

V3059 . Consider adding '[Flags]' attribute to the enum.

V3060 . A value of variable is not modified. Consider inspecting the expression. It is possible that other value should be present instead of '0'.

V3061 . Parameter 'A' is always rewritten in method body before being used.

V3062 . An object is used as an argument to its own method. Consider checking the first actual argument of the 'Foo' method.

V3063 . A part of conditional expression is always true/false.

V3064 . Division or mod division by zero.

V3065 . Parameter is not utilized inside method's body.

V3066 . Possible incorrect order of arguments passed to 'Foo' method.

V3067 . It is possible that 'else' block was forgotten or commented out, thus altering the program's operation logics.

V3068 . Calling overrideable class member from constructor is dangerous.

V3069 . It's possible that the line was commented out improperly, thus altering the program's operation logics.

V3070 . Uninitialized variables are used when initializing the 'A' variable.

V3071 . The object is returned from inside 'using' block. 'Dispose' will be invoked before exiting method.

PVS-Studio 6.01 (February 3, 2016) V736 . The behavior is undefined for arithmetic or comparisons with pointers

that do not point to members of the same array.

V737 . It is possible that ',' comma is missing at the end of the string.

V738 . Temporary anonymous object is used.

V739 . EOF should not be compared with a value of the 'char' type. Consider using the 'int' type.

V740 . Because NULL is defined as 0, the exception is of the 'int' type. Keyword 'nullptr' could be used for 'pointer' type exception.

V741 . The following pattern is used: throw (a, b);. It is possible that type name was omitted: throw MyException(a, b);..

V742 . Function receives an address of a 'char' type variable instead of pointer to a buffer.

V743 . The memory areas must not overlap. Use 'memmove' function.

Page 47: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V744 . Temporary object is immediately destroyed after being created. Consider naming the object.

V745 . A 'wchar_t *' type string is incorrectly converted to 'BSTR' type string.

V746 . Type slicing. An exception should be caught by reference rather than by value.

V747 . An odd expression inside parenthesis. It is possible that a function name is missing.

V748 . Memory for 'getline' function should be allocated only by 'malloc' or 'realloc' functions. Consider inspecting the first parameter of 'getline' function.

V749 . Destructor of the object will be invoked a second time after leaving the object's scope.

V750 . BSTR string becomes invalid. Notice that BSTR strings store their length before start of the text.

V816 . It is more efficient to catch exception by reference rather than by value.

V3042 . Possible NullReferenceException. The '?.' and '.' operators are used for accessing members of the same object.

V3043 . The code's operational logic does not correspond with its formatting.

V3044 . WPF: writing and reading are performed on a different Dependency Properties.

V3045 . WPF: the names of the property registered for DependencyProperty, and of the property used to access it, do not correspond with each other.

V3046 . WPF: the type registered for DependencyProperty does not correspond with the type of the property used to access it.

V3047 . WPF: A class containing registered property does not correspond with a type that is passed as the ownerType.type.

V3048 . WPF: several Dependency Properties are registered with a same name within the owner type.

V3049 . WPF: readonly field of 'DependencyProperty' type is not initialized.

V3050 . Possibly an incorrect HTML. The </XX> closing tag was encountered, while the </YY> tag was expected.

V3051 . An excessive type cast or check. The object is already of the same type.

V3052 . The original exception object was swallowed. Stack of original exception could be lost.

V3053 . An excessive expression. Examine the substrings "abc" and "abcd".

Page 48: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V3054 . Potentially unsafe double-checked locking. Use volatile variable(s) or synchronization primitives to avoid this.

V3055 . Suspicious assignment inside the condition expression of 'if/while/for' operator.

V3056 . Consider reviewing the correctness of 'X' item's usage.

PVS-Studio 6.00 (December 22, 2015) Static code analysis for C# added! More than 40 diagnostics in first release.

We are cancelling support for Visual Studio 2005 and Visual Studio 2008.

V734 . Searching for the longer substring is meaningless after searching for the shorter substring.

V735 . Possibly an incorrect HTML. The "</XX" closing tag was encountered, while the "</YY" tag was expected.

V3001 . There are identical sub-expressions to the left and to the right of the 'foo' operator.

V3002 . The switch statement does not cover all values of the enum.

V3003 . The use of 'if (A) {...} else if (A) {...}' pattern was detected. There is a probability of logical error presence.

V3004 . The 'then' statement is equivalent to the 'else' statement.

V3005 . The 'x' variable is assigned to itself.

V3006 . The object was created but it is not being used. The 'throw' keyword could be missing.

V3007 . Odd semicolon ';' after 'if/for/while' operator.

V3008 . The 'x' variable is assigned values twice successively. Perhaps this is a mistake.

V3009 . It's odd that this method always returns one and the same value of NN.

V3010 . The return value of function 'Foo' is required to be utilized.

V3011 . Two opposite conditions were encountered. The second condition is always false.

V3012 . The '?:' operator, regardless of its conditional expression, always returns one and the same value.

V3013 . It is odd that the body of 'Foo_1' function is fully equivalent to the body of 'Foo_2' function.

V3014 . It is likely that a wrong variable is being incremented inside the 'for' operator. Consider reviewing 'X'.

Page 49: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V3015 . It is likely that a wrong variable is being compared inside the 'for' operator. Consider reviewing 'X'.

V3016 . The variable 'X' is being used for this loop and for the outer loop.

V3017 . A pattern was detected: A || (A && ...). The expression is excessive or contains a logical error.

V3018 . Consider inspecting the application's logic. It's possible that 'else' keyword is missing.

V3019 . It is possible that an incorrect variable is compared with null after type conversion using 'as' keyword.

V3020 . An unconditional 'break/continue/return/goto' within a loop.

V3021 . There are two 'if' statements with identical conditional expressions. The first 'if' statement contains method return. This means that the second 'if' statement is senseless.

V3022 . Expression is always true/false.

V3023 . Consider inspecting this expression. The expression is excessive or contains a misprint.

V3024 . An odd precise comparison. Consider using a comparison with defined precision: Math.Abs(A - B) < Epsilon or Math.Abs(A - B) > Epsilon.

V3025 . Incorrect format. Consider checking the N format items of the 'Foo' function.

V3026 . The constant NN is being utilized. The resulting value could be inaccurate. Consider using the KK constant.

V3027 . The variable was utilized in the logical expression before it was verified against null in the same logical expression.

V3028 . Consider inspecting the 'for' operator. Initial and final values of the iterator are the same.

V3029 . The conditional expressions of the 'if' operators situated alongside each other are identical.

V3030 . Recurring check. This condition was already verified in previous line.

V3031 . An excessive check can be simplified. The operator '||' operator is surrounded by opposite expressions 'x' and '!x'.

V3032 . Waiting on this expression is unreliable, as compiler may optimize some of the variables. Use volatile variable(s) or synchronization primitives to avoid this.

V3033 . It is possible that this 'else' branch must apply to the previous 'if' statement.

Page 50: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V3034 . Consider inspecting the expression. Probably the '!=' should be used here.

V3035 . Consider inspecting the expression. Probably the '+=' should be used here.

V3036 . Consider inspecting the expression. Probably the '-=' should be used here.

V3037 . An odd sequence of assignments of this kind: A = B; B = A;.

V3038 . The 'first' argument of 'Foo' function is equal to the 'second' argument

V3039 . Consider inspecting the 'Foo' function call. Defining an absolute path to the file or directory is considered a poor style.

V3040 . The expression contains a suspicious mix of integer and real types.

V3041 . The expression was implicitly cast from integer type to real type. Consider utilizing an explicit type cast to avoid the loss of a fractional part.

Release history for old versionsPlease read release history for old versions here.

Old PVS-Studio Release History (before 6.00)

PVS-Studio 5.31 (November 3, 2015)

PVS-Studio 5.30 (October 29, 2015)

PVS-Studio 5.29 (September 22, 2015)

PVS-Studio 5.28 (August 10, 2015)

PVS-Studio 5.27 (July 28, 2015)

PVS-Studio 5.26 (June 30, 2015)

PVS-Studio 5.25 (May 12, 2015)

PVS-Studio 5.24 (April 10, 2015)

PVS-Studio 5.23 (March 17, 2015)

PVS-Studio 5.22 (February 17, 2015)

PVS-Studio 5.21 (December 11, 2014)

PVS-Studio 5.20 (November 12, 2014)

Page 51: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

PVS-Studio 5.19 (September 18, 2014)

PVS-Studio 5.18 (July 30, 2014)

PVS-Studio 5.17 (May 20, 2014)

PVS-Studio 5.16 (April 29, 2014)

PVS-Studio 5.15 (April 14, 2014)

PVS-Studio 5.14 (March 12, 2014)

PVS-Studio 5.13 (February 5, 2014)

PVS-Studio 5.12 (December 23, 2013)

PVS-Studio 5.11 (November 6, 2013)

PVS-Studio 5.10 (October 7, 2013)

PVS-Studio 5.06 (August 13, 2013)

PVS-Studio 5.05 (May 28, 2013)

PVS-Studio 5.04 (May 14, 2013)

PVS-Studio 5.03 (April 16, 2013)

PVS-Studio 5.02 (March 6, 2013)

PVS-Studio 5.01 (February 13, 2013)

PVS-Studio 5.00 (January 31, 2013)

PVS-Studio 4.77 (December 11, 2012)

PVS-Studio 4.76 (November 23, 2012)

PVS-Studio 4.75 (November 12, 2012)

PVS-Studio 4.74 (October 16, 2012)

PVS-Studio 4.73 (September 17, 2012)

PVS-Studio 4.72 (August 30, 2012)

PVS-Studio 4.71 (July 20, 2012)

PVS-Studio 4.70 (July 3, 2012)

PVS-Studio 4.62 (May 30, 2012)

PVS-Studio 4.61 (May 22, 2012)

PVS-Studio 4.60 (April 18, 2012)

PVS-Studio 4.56 (March 14, 2012)

PVS-Studio 4.55 (February 28, 2012)

PVS-Studio 4.54 (February 1, 2012)

PVS-Studio 4.53 (January 19, 2012)

PVS-Studio 4.52 (December 28, 2011)

Page 52: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

PVS-Studio 4.51 (December 22, 2011)

PVS-Studio 4.50 (December 15, 2011)

PVS-Studio 4.39 (November 25, 2011)

PVS-Studio 4.38 (October 12, 2011)

PVS-Studio 4.37 (September 20, 2011)

PVS-Studio 4.36 (August 31, 2011)

PVS-Studio 4.35 (August 12, 2011)

PVS-Studio 4.34 (July 29, 2011)

PVS-Studio 4.33 (July 21, 2011)

PVS-Studio 4.32 (July 15, 2011)

PVS-Studio 4.31 (July 6, 2011)

PVS-Studio 4.30 (June 23, 2011)

PVS-Studio 4.21 (May 20, 2011)

PVS-Studio 4.20 (April 29, 2011)

PVS-Studio 4.17 (April 15, 2011)

PVS-Studio 4.16 (April 1, 2011)

PVS-Studio 4.15 (March 17, 2011)

PVS-Studio 4.14 (March 2, 2011)

PVS-Studio 4.13 (February 11, 2011)

PVS-Studio 4.12 (February 7, 2011)

PVS-Studio 4.11 (January 28, 2011)

PVS-Studio 4.10 (January 17, 2011)

PVS-Studio 4.00 (December 24, 2010)

PVS-Studio 4.00 BETA (November 24, 2010)

PVS-Studio 3.64 (27 September 2010)

PVS-Studio 3.63 (10 September 2010)

PVS-Studio 3.62 (16 August 2010)

PVS-Studio 3.61 (22 July 2010)

PVS-Studio 3.60 (10 June 2010)

PVS-Studio 3.53 (7 May 2010)

PVS-Studio 3.52 (27 April 2010)

PVS-Studio 3.51 (16 April 2010)

PVS-Studio 3.50 (26 March 2010)

Page 53: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

PVS-Studio 3.44 (21 January 2010)

PVS-Studio 3.43 (28 December 2009)

PVS-Studio 3.42 (9 December 2009)

PVS-Studio 3.41 (30 November 2009)

PVS-Studio 3.40 (23 November 2009)

PVS-Studio 3.30 (25 September 2009)

PVS-Studio 3.20 (7 September 2009)

PVS-Studio 3.10 (10 August 2009)

PVS-Studio 3.00 (27 July 2009)

VivaMP 1.10 (20 April 2009)

VivaMP 1.00 (10 March 2009)

VivaMP 1.00 beta (27 November 2008)

Viva64 2.30 (20 April 2009)

Viva64 2.22 (10 Mach 2009)

Viva64 2.21 (27 November 2008)

Viva64 2.20 (15 October 2008)

Viva64 2.10 (05 September 2008)

Viva64 2.0 (09 July 2008)

Viva64 1.80 (03 February 2008)

Viva64 1.70 (20 December 2007)

Viva64 1.60 (28 August 2007)

Viva64 1.50 (15 May 2007)

Viva64 1.40 (1 May 2007)

Viva64 1.30 (17 March 2007)

Viva64 1.20 (26 January 2007)

Viva64 1.10 (16 January 2007)

Viva64 1.00 (31 December 2006)

Please read actual release history here.

PVS-Studio 5.31 (November 3, 2015) False positive quantity is reduced in some diagnostics.

PVS-Studio 5.30 (October 29, 2015)

Page 54: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Double click navigation support on multiple-line messages was added.

An access error during the Visual C++ preprocessor start for a check of files, using #import directive was removed.

An error of Compiler Monitoring preprocessing more than 10 minutes, corrected.

Incorrect installer's work, operating on systems that have 2015 Visual Studio only, was corrected.

New diagnostic - V728. An excessive check can be simplified. The '||' operator is surrounded by opposite expressions 'x' and '!x'.

New diagnostic - V729. Function body contains the 'X' label that is not used by any 'goto' statements.

New diagnostic - V730. Not all members of a class are initialized inside the constructor.

New diagnostic - V731. The variable of char type is compared with pointer to string.

New diagnostic - V732. Unary minus operator does not modify a bool type value.

New diagnostic - V733. It is possible that macro expansion resulted in incorrect evaluation order.

PVS-Studio 5.29 (September 22, 2015) Visual Studio 2015 supported.

Windows 10 supported.

New diagnostic - V727. Return value of 'wcslen' function is not multiplied by 'sizeof(wchar_t)'.

PVS-Studio 5.28 (August 10, 2015) New interface of the settings pages Detectable Errors, Don't Check Files, and

Keyword Message Filering.

A new utility PlogConverter was added to convert XML plog files into formats txt, html, and CSV. Check the documentation for details.

PVS-Studio 5.27 (July 28, 2015) New diagnostic - V207. A 32-bit variable is utilized as a reference to a

pointer. A write outside the bounds of this variable may occur.

Page 55: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

New diagnostic - V726. An attempt to free memory containing the 'int A[10]' array by using the 'free(A)' function.

New feature - Analyzer Work Statistics (Diagrams). PVS-Studio analyzer can gather its' operational statistics - the number of detected messages (including suppressed ones) across different severity levels and rule sets. Gathered statistics can be filtered and represented as a diagram in a Microsoft Excel file, showing the change dynamics for messages in the project under analysis. See details in documentation.

Analysis of preprocessed files removed from Standalone.

PVS-Studio 5.26 (June 30, 2015) New diagnostic - V723. Function returns a pointer to the internal string

buffer of a local object, which will be destroyed.

New diagnostic - V724. Converting integers or pointers to BOOL can lead to a loss of high-order bits. Non-zero value can become 'FALSE'.

New diagnostic - V725. A dangerous cast of 'this' to 'void*' type in the 'Base' class, as it is followed by a subsequent cast to 'Class' type.

Message Suppression support was implemented for CLMonitoring/Standalone.

2nd and 3rd levels of analyzer warnings are accessible in Trial Mode.

PVS-Studio 5.25 (May 12, 2015) New diagnostic - V722. An abnormality within similar comparisons. It is

possible that a typo is present inside the expression.

Improved the responsiveness of Quick Filters and Analyzer\Levels buttons in Output Window.

'False Alarms' output window filter was moved into settings.

Fix for 'An item with the same key has already been added' error when using message suppression

PVS-Studio 5.24 (April 10, 2015) New diagnostic - V721. The VARIANT_BOOL type is utilized incorrectly.

The true value (VARIANT_TRUE) is defined as -1.

New trial mode. Please refer here.

A new message suppression mechanism now can be utilized together with command line mode for project files (vcproj/vcxproj) to organize a distribution of analysis logs with newly discovered warnings (in plain text

Page 56: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

and html formats) by email. More details on command line mode and utilizing analyzer within continuous integration systems.

PVS-Studio 5.23 (March 17, 2015) 64-bit analysis is greatly improved. Now if you want to fix major 64-bit

issues just fix all 64 Level 1 messages.

You can use PVS-Studio-Updater.exe for automatic update of PVS-Studio on build-server. See details here.

New diagnostic - V719. The switch statement does not cover all values of the enum.

New diagnostic - V720. It is advised to utilize the 'SuspendThread' function only when developing a debugger (see documentation for details).

New diagnostic - V221. Suspicious sequence of types castings: pointer -> memsize -> 32-bit integer.

New diagnostic - V2013. Consider inspecting the correctness of handling the N argument in the 'Foo' function.

PVS-Studio 5.22 (February 17, 2015) New diagnostic - V718. The 'Foo' function should not be called from

'DllMain' function.

Fix for CLMonitoring operation on C++/CLI projects.

Memory leak fix for CLMonitoring of long-running processes.

Include\symbol reference search for Standalone.

Message Suppression memory usage optimization.

Message Suppression correctly handles multi-project analyzer messages (as, for example, messages generated on common h files on different IDE projects).

Several crucial improvements in (Message Suppression).

PVS-Studio 5.21 (December 11, 2014) We are cancelling support for the Embarcadero RAD Studio IDE.

We are cancelling support for OpenMP diagnostics (VivaMP rule set)

New diagnostic - V711. It is dangerous to create a local variable within a loop with a same name as a variable controlling this loop.

Page 57: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

New diagnostic - V712. Be advised that compiler may delete this cycle or make it infinity. Use volatile variable(s) or synchronization primitives to avoid this.

New diagnostic - V713. The pointer was utilized in the logical expression before it was verified against nullptr in the same logical expression.

New diagnostic - V714. Variable is not passed into foreach loop by a reference, but its value is changed inside of the loop.

New diagnostic - V715. The 'while' operator has empty body. Suspicious pattern detected.

New diagnostic - V716. Suspicious type conversion: HRESULT -> BOOL (BOOL -> HRESULT).

New diagnostic - V717. It is strange to cast object of base class V to derived class U.

PVS-Studio 5.20 (November 12, 2014) New diagnostic - V706. Suspicious division: sizeof(X) / Value. Size of every

element in X array does not equal to divisor.

New diagnostic - V707. Giving short names to global variables is considered to be bad practice.

New diagnostic - V708. Dangerous construction is used: 'm[x] = m.size()', where 'm' is of 'T' class. This may lead to undefined behavior.

New diagnostic - V709. Suspicious comparison found: 'a == b == c'. Remember that 'a == b == c' is not equal to 'a == b && b == c.

New diagnostic - V710. Suspicious declaration found. There is no point to declare constant reference to a number.

New diagnostic - V2012. Possibility of decreased performance. It is advised to pass arguments to std::unary_function/std::binary_function template as references.

New feature - Mass Suppression of Analyzer Messages. Sometimes, during deployment of static analysis, especially at large-scale projects, the developer has no desire (or even has no means of) to correct hundreds or even thousands of analyzer's messages which were generated on the existing source code base. In this situation, the need arises to "suppress" all of the analyzer's messages generated on the current state of the code, and, from that point, to be able to see only the messages related to the newly written or modified code. As such code was not yet thoroughly debugged and tested, it can potentially contain a large number of errors.

PVS-Studio 5.19 (September 18, 2014)

Page 58: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

New diagnostic - V698. strcmp()-like functions can return not only the values -1, 0 and 1, but any values.

New diagnostic - V699. Consider inspecting the 'foo = bar = baz ? .... : ....' expression. It is possible that 'foo = bar == baz ? .... : ....' should be used here instead.

New diagnostic - V700. Consider inspecting the 'T foo = foo = x;' expression. It is odd that variable is initialized through itself.

New diagnostic - V701. realloc() possible leak: when realloc() fails in allocating memory, original pointer is lost. Consider assigning realloc() to a temporary pointer.

New diagnostic - V702. Classes should always be derived from std::exception (and alike) as 'public'.

New diagnostic - V703. It is odd that the 'foo' field in derived class overwrites field in base class.

New diagnostic - V704. 'this == 0' comparison should be avoided - this comparison is always false on newer compilers.

New diagnostic - V705. It is possible that 'else' block was forgotten or commented out, thus altering the program's operation logics.

PVS-Studio 5.18 (July 30, 2014) ClMonitoring - automatic detection of compiler's platform.

ClMonitoring - performance increase resulting from the reduction of an impact of antiviral software during preprocessing of analyzed files.

ClMonitoring - incorrect handling of 64-bit processes resulting from a system update for .NET Framework 4 was fixed.

New diagnostic - V695. Range intersections are possible within conditional expressions.

New diagnostic - V696. The 'continue' operator will terminate 'do { ... } while (FALSE)' loop because the condition is always false.

New diagnostic - V697. A number of elements in the allocated array is equal to size of a pointer in bytes.

New diagnostic - V206. Explicit conversion from 'void *' to 'int *'.

New diagnostic - V2011. Consider inspecting signed and unsigned function arguments. See NN argument of function 'Foo' in derived class and base class.

PVS-Studio 5.17 (May 20, 2014)

Page 59: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

New diagnostic - V690. The class implements a copy constructor/operator=, but lacks the the operator=/copy constructor.

New diagnostic - V691. Empirical analysis. It is possible that a typo is present inside the string literal. The 'foo' word is suspicious.

New diagnostic - V692. An inappropriate attempt to append a null character to a string. To determine the length of a string by 'strlen' function correctly, a string ending with a null terminator should be used in the first place.

New diagnostic - V693. Consider inspecting conditional expression of the loop. It is possible that 'i < X.size()' should be used instead of 'X.size()'.

New diagnostic - V694. The condition (ptr - const_value) is only false if the value of a pointer equals a magic constant.

New diagnostic - V815. Decreased performance. Consider replacing the expression 'AA' with 'BB'.

New diagnostic - V2010. Handling of two different exception types is identical.

PVS-Studio 5.16 (April 29, 2014) Support of C++/CLI projects was greatly improved.

TFSRipper plugin was removed.

Fix for crash in Standalone when installing in non-default location on a 64-bit system.

Fixed issue with hiding of diagnostic messages in some case.

PVS-Studio 5.15 (April 14, 2014) New diagnostic - V689. The destructor of the 'Foo' class is not declared as a

virtual. It is possible that a smart pointer will not destroy an object correctly.

Several crucial improvements in Compiler Monitoring in PVS-Studio.

PVS-Studio 5.14 (March 12, 2014) New option "DIsable 64-bit Analysis" in Specific Analyzer Settings option

page can improve analysis speed and decrease .plog file size.

New feature: Compiler Monitoring in PVS-Studio.

Fixed problem with incremental analysis notification with auto hide PVS-Studio Output Window.

Page 60: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

New diagnostic - V687. Size of an array calculated by the sizeof() operator was added to a pointer. It is possible that the number of elements should be calculated by sizeof(A)/sizeof(A[0]).

New diagnostic - V688. The 'foo' local variable possesses the same name as one of the class members, which can result in a confusion.

PVS-Studio 5.13 (February 5, 2014) Support for Embarcadero RAD Studio XE5 was implemented.

New diagnostic - V684. A value of variable is not modified. Consider inspecting the expression. It is possible that '1' should be present instead of '0'.

New diagnostic - V685. Consider inspecting the return statement. The expression contains a comma.

New diagnostic - V686. A pattern was detected: A || (A && ...). The expression is excessive or contains a logical error.

PVS-Studio 5.12 (December 23, 2013) Fix for the issue with SolutionDir property when direct integration of the

analyzer into MSBuild system is utilized.

The analysis can now be launched from within the context menu of Solution Explorer tool window.

The 'ID' column will now be hidden by default in the PVS-Studio Output toolwindow. It is possible to enable it again by using the Show Columns -> ID context menu command.

New diagnostic - V682. Suspicious literal is present: '/r'. It is possible that a backslash should be used here instead: '\r'.

New diagnostic - V683. Consider inspecting the loop expression. It is possible that the 'i' variable should be incremented instead of the 'n' variable.

PVS-Studio 5.11 (November 6, 2013) Support for the release version of Microsoft Visual Studio 2013 was

implemented.

New diagnostic - V680. The 'delete A, B' expression only destroys the 'A' object. Then the ',' operator returns a resulting value from the right side of the expression.

New diagnostic - V681. The language standard does not define an order in which the 'Foo' functions will be called during evaluation of arguments.

Page 61: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

PVS-Studio 5.10 (October 7, 2013) Fixed the issue with the analyzer when Visual Studio is called with the

parameter /useenv: devenv.exe /useenv.

VS2012 has finally got support for Clang so that it can be used as the preprocessor. It means that PVS-Studio users will see a significant performance boost in VS2012.

Several crucial improvements were made to the analyzer's performance when parsing code in VS2012.

The PVS-Studio distribution package now ships with a new application Standalone.

You can now export analysis results into a .CSV-file to handle them in Excel.

Support of precompiled headers in Visual Studio and MSBuild was greatly improved.

New diagnostic - V676. It is incorrect to compare the variable of BOOL type with TRUE.

New diagnostic - V677. Custom declaration of a standard type. The declaration from system header files should be used instead.

New diagnostic - V678. An object is used as an argument to its own method. Consider checking the first actual argument of the 'Foo' function.

New diagnostic - V679. The 'X' variable was not initialized. This variable is passed by a reference to the 'Foo' function in which its value will be utilized.

PVS-Studio 5.06 (August 13, 2013) Fix for incorrect number of verified files when using 'Check Open File(s)'

command in Visual Studio 2010.

New diagnostic - V673. More than N bits are required to store the value, but the expression evaluates to the T type which can only hold K bits.

New diagnostic - V674. The expression contains a suspicious mix of integer and real types.

New diagnostic - V675. Writing into the read-only memory.

New diagnostic - V814. Decreased performance. The 'strlen' function was called multiple times inside the body of a loop.

PVS-Studio 5.05 (May 28, 2013)

Page 62: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Support for proxy server with authorization was implemented for trial extension window.

An issue with using certain special characters in diagnostic message filters was resolved.

A portion of 'Common Analyzer Settings' page options and all of the options from 'Customer Specific Settings' page were merged together into the new page: Specific Analyzer Settings.

A new SaveModifiedLog option was implemented. It allows you to define the behavior of 'Save As' dialog for a new\modified analysis report log (always ask, save automatically, do not save).

Customer diagnostics (V20xx) were assigned to a separate diagnostics group (CS - Customer Specific).

A new menu command was added: "Check Open File(s)". It allows starting the analysis on all of the C/C++ source files that are currently open in IDE text editor.

PVS-Studio 5.04 (May 14, 2013) Support has been implemented for C++Builder XE4. Now PVS-Studio

supports the following versions of C++Builder: XE4, XE3 Update 1, XE2, XE, 2010, 2009.

New diagnostic - V669. The argument is a non-constant reference. The analyzer is unable to determine the position at which this argument is being modified. It is possible that the function contains an error.

New diagnostic - V670. An uninitialized class member is used to initialize another member. Remember that members are initialized in the order of their declarations inside a class.

New diagnostic - V671. It is possible that the 'swap' function interchanges a variable with itself.

New diagnostic - V672. There is probably no need in creating a new variable here. One of the function's arguments possesses the same name and this argument is a reference.

New diagnostic - V128. A variable of the memsize type is read from a stream. Consider verifying the compatibility of 32 and 64 bit versions of the application in the context of a stored data.

New diagnostic - V813. Decreased performance. The argument should probably be rendered as a constant pointer/reference.

New diagnostic - V2009. Consider passing the 'Foo' argument as a constant pointer/reference.

Page 63: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

PVS-Studio 5.03 (April 16, 2013) Enhanced analysis/interface performance when checking large projects and

generating a large number of diagnostic messages (the total number of unfiltered messages).

Fixed the issue with incorrect integration of the PVS-Studio plugin into the C++Builder 2009/2010/XE environments after installation.

Fixed the bug with the trial-mode.

The analyzer can now be set to generate relative paths to source files in its log files.

The analyzer now supports direct integration into the MSBuild build system.

Integrated Help Language option added to Customer's Settings page. The setting allows you to select a language to be used for integrated help on the diagnostic messages (a click to the message error code in PVS-Studio output window) and online documentation (the PVS-Studio -> Help -> Open PVS-Studio Documentation (html, online) menu command), which are also available at our site. This setting will not change the language of IDE plug-in's interface and messages produced by the analyzer.

Fix for Command line analysis mode in Visual Studio 2012 in the case of project background loading.

New diagnostic - V665. Possibly, the usage of '#pragma warning(default: X)' is incorrect in this context. The '#pragma warning(push/pop)' should be used instead.

New diagnostic - V666. Consider inspecting NN argument of the function 'Foo'. It is possible that the value does not correspond with the length of a string which was passed with the YY argument.

New diagnostic - V667. The 'throw' operator does not possess any arguments and is not situated within the 'catch' block.

New diagnostic - V668. There is no sense in testing the pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error.

New diagnostic -V812. Decreased performance. Ineffective use of the 'count' function. It can possibly be replaced by the call to the 'find' function.

PVS-Studio 5.02 (March 6, 2013) Incorrect navigation in C++Builder modules that contain several

header/source files was fixed.

Page 64: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The option for inserting user-specified comments while performing false alarm mark-ups (for example, to provide the automatic documentation generation systems with appropriate descriptions) was implemented.

An issue of incorrectly starting up a C++ preprocessor for some of the files utilizing precompiled headers was fixed.

New diagnostic - V663. Infinite loop is possible. The 'cin.eof()' condition is insufficient to break from the loop. Consider adding the 'cin.fail()' function call to the conditional expression.

New diagnostic - V664. The pointer is being dereferenced on the initialization list before it is verified against null inside the body of the constructor function.

New diagnostic - V811. Decreased performance. Excessive type casting: string -> char * -> string.

PVS-Studio 5.01 (February 13, 2013) Support has been implemented for several previous versions of C++Builder.

Now PVS-Studio supports the following versions of C++Builder: XE3 Update 1, XE2, XE, 2010, 2009.

A bug in C++Builder version with incremental analysis starting-up incorrectly in several situations was fixed.

Occasional incorrect placement of false alarm markings for C++Builder version was fixed.

Incorrect display of localized filenames containing regional-specific characters in C++Builder version was fixed.

An issue with opening source files during diagnostic message navigation in C++Builder version was resolved.

The issue was fixed of system includes paths being resolved incompletely when starting the preprocessor for the analyzer in C++ Builder versions.

New diagnostic - V661. A suspicious expression 'A[B < C]'. Probably meant 'A[B] < C'.

New diagnostic - V662. Consider inspecting the loop expression. Different containers are utilized for setting up initial and final values of the iterator.

PVS-Studio 5.00 (January 31, 2013) Support for the integration to Embarcadero RAD Studio, or Embarcadero C+

+ Builder to be more precise, was added! As of this moment, PVS-Studio diagnostics capabilities are available to the users of C++ Builder. While in the past PVS-Studio could be conveniently utilized only from within Visual Studio environment, but now C++ developers who choses Embarcadero

Page 65: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

products will be able to fully utilize PVS-Studio static analyzer as well. Presently, the supported versions are XE2 and XE3, including the XE3 Update 1 with 64-bit C++ compiler.

Microsoft Design Language (formerly known as Metro Language) C++/CX Windows 8 Store (WinRT) projects on x86/ARM platforms and Windows Phone 8 projects support was implemented.

A fix for the users of Clang-preprocessor in Visual Studio version was implemented. Previously it was impossible to use Clang as a preprocessor while analyzing projects utilizing the Boost library because of the preprocessing errors. Now these issues were resolved. This significantly decreased the time it takes to analyze Boost projects with the help of Clang preprocessor.

The obsolete Viva64 options page was removed.

V004 message text was modified to provide a more correct description.

New diagnostic - V810. Decreased performance. The 'A' function was called several times with identical arguments. The result should possibly be saved to a temporary variable, which then could be used while calling the 'B' function.

New diagnostic - V2008. Cyclomatic complexity: NN. Consider refactoring the 'Foo' function.

New diagnostic - V657. It's odd that this function always returns one and the same value of NN.

New diagnostic - V658. A value is being subtracted from the unsigned variable. This can result in an overflow. In such a case, the comparison operation can potentially behave unexpectedly.

New diagnostic - V659. Declarations of functions with 'Foo' name differ in the 'const' keyword only, but the bodies of these functions have different composition. This is suspicious and can possibly be an error.

New diagnostic - V660. The program contains an unused label and a function call: 'CC:AA()'. It's possible that the following was intended: 'CC::AA()'.

PVS-Studio 4.77 (December 11, 2012) Acquisition of compilation parameters for VS2012 and VS2010 was

improved through expansion of support for MSBuild-based projects.

New diagnostic - V654. The condition of loop is always true/false.

New diagnostic - V655. The strings was concatenated but are not utilized. Consider inspecting the expression.

New diagnostic - V656. Variables are initialized through the call to the same function. It's probably an error or un-optimized code.

Page 66: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

New diagnostic - V809. Verifying that a pointer value is not NULL is not required. The 'if (ptr != NULL)' check can be removed.

PVS-Studio 4.76 (November 23, 2012) Some bugs were fixed.

PVS-Studio 4.75 (November 12, 2012) An issue with checking Qt-based projects which manifested itself under

certain conditions was solved (details in blog).

New diagnostic - V646. Consider inspecting the application's logic. It's possible that 'else' keyword is missing.

New diagnostic - V647. The value of 'A' type is assigned to the pointer of 'B' type.

New diagnostic - V648. Priority of the '&&' operation is higher than that of the '||' operation.

New diagnostic - V649. There are two 'if' statements with identical conditional expressions. The first 'if' statement contains function return. This means that the second 'if' statement is senseless.

New diagnostic - V650. Type casting operation is utilized 2 times in succession. Next, the '+' operation is executed. Probably meant: (T1)((T2)a + b).

New diagnostic - V651. An odd operation of the 'sizeof(X)/sizeof(T)' kind is performed, where 'X' is of the 'class' type.

New diagnostic - V652. The operation is executed 3 or more times in succession.

New diagnostic - V653. A suspicious string consisting of two parts is used for array initialization. It is possible that a comma is missing.

New diagnostic - V808. An array/object was declared but was not utilized.

New diagnostic - V2007. This expression can be simplified. One of the operands in the operation equals NN. Probably it is a mistake.

PVS-Studio 4.74 (October 16, 2012) New option "Incremental Results Display Depth was added. This setting

defines the mode of message display level in PVS-Studio Output window for the results of incremental analysis. Setting the display level depth here (correspondingly, Level 1 only; Levels 1 and 2; Levels 1, 2 and 3) will enable automatic activation of these display levels on each incremental

Page 67: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

analysis procedure. The "Preserve_Current_Levels" on the other hand will preserve the existing display setting.

New option "External Tool Path" was added. This field allows defining an absolute path to any external tool, which could then be executed with the "Send this message to external tool" context menu command of the PVS-Studio Output window. The mentioned menu command is available only for a single simultaneously selected message from the results table, allowing the passing of the command line parameters specified in the ExternalToolCommandLine field to the utility from here. The detailed description of this mode together with usage examples is available here.

PVS-Studio 4.73 (September 17, 2012) Issues with incorrect processing of some Visual Studio 2012 C++11

constructs were fixed.

A complete support for Visual Studio 2012 themes was implemented.

The search field for the 'Project' column was added to the PVS-Studio Output Window quick filters.

The included Clang external preprocessor was updated.

Support for the TenAsys INtime platform was implemented.

PVS-Studio 4.72 (August 30, 2012) Support for the release version of Microsoft Visual Studio 2012 was

implemented.

A new version of SourceGrid component will be utilized, solving several issues with PVS-Studio Output Window operation.

Support for diagnostics of issues inside STL library using STLport was implemented.

New diagnostic - V637. Two opposite conditions were encountered. The second condition is always false.

New diagnostic - V638. A terminal null is present inside a string. The '\0xNN' characters were encountered. Probably meant: '\xNN'.

New diagnostic - V639. Consider inspecting the expression for function call. It is possible that one of the closing ')' brackets was positioned incorrectly.

New diagnostic - V640. Consider inspecting the application's logic. It is possible that several statements should be braced.

New diagnostic - V641. The size of the allocated memory buffer is not a multiple of the element size.

Page 68: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

New diagnostic - V642. Saving the function result inside the 'byte' type variable is inappropriate. The significant bits could be lost breaking the program's logic.

New diagnostic - V643. Unusual pointer arithmetic. The value of the 'char' type is being added to the string pointer.

New diagnostic - V644. A suspicious function declaration. It is possible that the T type object was meant to be created.

New diagnostic - V645. The function call could lead to the buffer overflow. The bounds should not contain the size of the buffer, but a number of characters it can hold.

PVS-Studio 4.71 (July 20, 2012) New diagnostic - V629. Consider inspecting the expression. Bit shifting of

the 32-bit value with a subsequent expansion to the 64-bit type.

New diagnostic - V630. The 'malloc' function is used to allocate memory for an array of objects which are classes containing constructors/destructors.

New diagnostic - V631. Consider inspecting the 'Foo' function call. Defining an absolute path to the file or directory is considered a poor style.

New diagnostic - V632. Consider inspecting the NN argument of the 'Foo' function. It is odd that the argument is of the 'T' type.

New diagnostic - V633. Consider inspecting the expression. Probably the '!=' should be used here.

New diagnostic - V634. The priority of the '+' operation is higher than that of the '<<' operation. It's possible that parentheses should be used in the expression.

New diagnostic - V635. Consider inspecting the expression. The length should probably be multiplied by the sizeof(wchar_t).

PVS-Studio 4.70 (July 3, 2012) Visual Studio 2012 RC support was implemented. At present the analyzer

does not provide a complete support for every new syntax construct introduced with Visual Studio 2012 RC. Also, there is an additional issue concerning the speed of the analysis, as we utilize Clang preprocessor to improve the analyzer's performance. Currently, Clang is unable to preprocess some of the new Visual C++ 2012 header files, and that means that the notably slower cl.exe preprocessor from Visual C++ will have to be utilized most of the time instead. In the default mode the correct preprocessor will be set by PVS-Studio automatically so it will not require any interaction from the user. Despite the aforementioned issues, PVS-Studio can now be fully utilized from Visual Studio 2012 RC IDE.

Page 69: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

New diagnostic - V615. An odd explicit conversion from 'float *' type to 'double *' type.

New diagnostic - V616. The 'Foo' named constant with the value of 0 is used in the bitwise operation.

New diagnostic - V617. Consider inspecting the condition. An argument of the '|' bitwise operation always contains a non-zero value.

New diagnostic - V618. It's dangerous to call the 'Foo' function in such a manner, as the line being passed could contain format specification. The example of the safe code: printf("%s", str);.

New diagnostic - V619. An array is being utilized as a pointer to single object.

New diagnostic - V620. It's unusual that the expression of sizeof(T)*N kind is being summed with the pointer to T type.

New diagnostic - V621. Consider inspecting the 'for' operator. It's possible that the loop will be executed incorrectly or won't be executed at all.

New diagnostic - V622. Consider inspecting the 'switch' statement. It's possible that the first 'case' operator in missing.

New diagnostic - V623. Consider inspecting the '?:' operator. A temporary object is being created and subsequently destroyed.

New diagnostic - V624. The constant NN is being utilized. The resulting value could be inaccurate. Consider using the M_NN constant from <math.h>.

New diagnostic - V625. Consider inspecting the 'for' operator. Initial and final values of the iterator are the same.

New diagnostic - V626. Consider checking for misprints. It's possible that ',' should be replaced by ';'.

New diagnostic - V627. Consider inspecting the expression. The argument of sizeof() is the macro which expands to a number.

New diagnostic - V628. It's possible that the line was commented out improperly, thus altering the program's operation logics.

New diagnostic - V2006. Implicit type conversion from enum type to integer type.

PVS-Studio 4.62 (May 30, 2012) The support for the MinGW gcc preprocessor was implemented, enabling the

verification of such projects as the ones which allow their compilation through MinGW compilers. Also, integration of the analyzer into build systems of such projects is similar to utilization of the analyzer with other projects lacking MSVC .sln files as it is described in detail in the

Page 70: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

corresponding documentation. As a reminder, the project which does include .sln file could be verified through command line in a regular way as well, not requiring the direct integration of the analyzer into the its' build system.

PVS-Studio 4.61 (May 22, 2012) Navigation for messages containing references to multiple lines was

improved. Some of diagnostic messages (V595 for example) are related to several lines of source code at once. Previously, the 'Line' column of PVS-Studio Output Window contained only a single line number while other lines were only mentioned in the text of such message itself. This was inconvenient for the navigation. As of this version the fields of the 'Line' column could contain several line numbers allowing navigation for each individual line.

A new build of Clang is included which contains several minor bug fixes. PVS-Studio uses Clang as an alternative preprocessor. Please note that PVS-Studio does not utilize Clang static analysis diagnostics.

New diagnostic - V612. An unconditional 'break/continue/return/goto' within a loop.

New diagnostic - V613. Strange pointer arithmetic with 'malloc/new'.

New diagnostic - V614. Uninitialized variable 'Foo' used.

PVS-Studio 4.60 (April 18, 2012) A new "Optimization" (OP) group allows the diagnostics of potential

optimizations. It is a static analysis rule set for identification of C/C++/C++11 source code sections which could be optimized. It should be noted that the analyzer solves the task of optimization for the narrow area of micro-optimizations. A full list of diagnostic cases is available in the documentation (codes V801-V807).

A total number of false positive messages for the 64-bit analyzer (Viva64) was decreased substantially.

Messages will not be produced for autogenerated files (MIDL).

Logics behind prompting save dialog for analysis report were improved.

Issue with Visual Studio Chinese localized version was fixed (the zh locale).

New diagnostic V610. Undefined behavior. Check the shift operator.

New diagnostic V611. The memory allocation and deallocation methods are incompatible.

PVS-Studio 4.56 (March 14, 2012)

Page 71: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

TraceMode option was added to Common Analyzer Settings. This setting could be used to specify the tracing mode (logging of a program's execution path).

An issue concerning the verification of Itanium-based projects was fixed.

An issue concerning the calling of the 64-bit version of clang.exe instead of the 32-bit one from within the 32-bit Windows while checking the project with selected x64 architecture was fixed.

A number of cores to be used for incremental analysis were changed. As of now the regular analysis (Check Solution/project/file) will utilize the exact number of cores specified in the settings. The incremental analysis will use a different value: if the number of cores from the settings is greater than (number of system cores - 1) and there is more than one core in the system then the (number of system cores - 1) will be utilized for it; otherwise the value from the settings will be used. Simply put the incremental analysis will utilize one core less compared to the regular one for the purpose of easing the load on the system.

New diagnostic V608. Recurring sequence of explicit type casts.

New diagnostic V609. Divide or mod by zero.

PVS-Studio 4.55 (February 28, 2012) New trial extension window.

A crash which occurs after reloading current project while code analysis is running was fixed.

The installer (in case it is the first-time installation) now provides the option to enable PVS-Studio incremental analysis. In case PVS-Studio was installed on system before this option will not be displayed. Incremental analysis could be enabled or disabled through the "Incremental Analysis after Build" PVS-Studio menu command.

As of now the default number of threads for analysis is equal to the number of processors minus one. This could be modified through the 'ThreadCount' option in PVS-Studio settings.

New article in documentation: "PVS-Studio's incremental analysis mode".

Additional functionality for the command line version mode — it is now possible to process several files at once, similar to the compiler batch mode (cl.exe file1.cpp file2.cpp). A more detailed description on command line mode is available in the documentation.

A support for Microsoft Visual Studio ARMV4 project types was removed.

New diagnostic V604. It is odd that the number of iterations in the loop equals to the size of the pointer.

Page 72: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

New diagnostic V605. Consider verifying the expression. An unsigned value is compared to the number - NN.

New diagnostic V606. Ownerless token 'Foo'.

New diagnostic V607. Ownerless expression 'Foo'.

PVS-Studio 4.54 (February 1, 2012) New trial mode was implemented. As of now only a total number of clicks

on messages will be limited. More details can be found in our blog or documentation.

New menu command "Disable Incremental Analysis until IDE restart" was added. Sometimes disabling the incremental analysis can be convenient, for instance when editing some core h-files, as it forces a large number of files to be recompiled. But it should not be disabled permanently, only temporary, as one can easily forget to turn it on again later. This command is also available in the system tray during incremental analysis.

New diagnostic V602. Consider inspecting this expression. '<' possibly should be replaced with '<<'.

New diagnostic V603. The object was created but it is not being used. If you wish to call constructor, 'this->Foo::Foo(....)' should be used.

New diagnostic V807. Decreased performance. Consider creating a pointer/reference to avoid using the same expression repeatedly.

New article in documentation: "PVS-Studio menu commands".

PVS-Studio 4.53 (January 19, 2012) New command for team work: "Add TODO comment for Task List". PVS-

Studio allows you to automatically generate the special TODO comment containing all the information required to analyze the code fragment marked by it, and to insert it into the source code. Such comment will immediately appear inside the Visual Studio Task List window.

New diagnostic V599. The virtual destructor is not present, although the 'Foo' class contains virtual functions.

New diagnostic V600. Consider inspecting the condition. The 'Foo' pointer is always not equal to NULL.

New diagnostic V601. An odd implicit type casting.

PVS-Studio 4.52 (December 28, 2011) Changes were introduced to the .sln-file independent analyzer command line

mode. It is now possible to start the analysis in several processes

Page 73: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

simultaneously, the output file (--output-file) will not be lost. The entire command line of arguments including the filename should be passed into the cl-params argument: --cl-params $(CFLAGS) $**.

The "Analysis aborted by timeout" error was fixed, it could have been encountered while checking .sln file through PVS-Studio.exe command line mode.

New diagnostic V597. The compiler could delete the 'memset' function call, which is used to flush 'Foo' buffer. The RtlSecureZeroMemory() function should be used to erase the private data.

New diagnostic V598. The 'memset/memcpy' function is used to nullify/copy the fields of 'Foo' class. Virtual method table will be damaged by this.

PVS-Studio 4.51 (December 22, 2011) The issue concerning the #import directive when using Clang preprocessor

was fixed. #import is supported by Clang differently from Microsoft Visual C++, therefore it is impossible to use Clang with such files. This directive is now automatically detected, and Visual C++ preprocessor is used for these files.

'Don't Check Files'  settings used for file and directory exclusions were significantly revised. As of now the folders to be excluded (either by their full and relative paths or my a mask) could be specified independently, as well as the files to be excluded (by their name, extension or a mask as well).

Some libraries were added to the default exclusion paths. This can be modified on the 'Don't Check Files' page.

PVS-Studio 4.50 (December 15, 2011) An external preprocessor is being utilized to preprocess files with PVS-

Studio. It is only Microsoft Visual C++ preprocessor that had been employed for this task in the past. But in 4.50 version of PVS-Studio the support for the Clang preprocessor had been added, as its performance is significantly higher and it lacks some of the Microsoft's preprocessor shortcomings (although it also possesses issues of its own). Still, the utilization of Clang preprocessor provides an increase of operational performance by 1.5-1.7 times in most cases. However there is an aspect that should be considered. The preprocessor to be used can be specified from within the PVS-Studio Options -> Common Analyzer Settings -> Preprocessor field. The available options are: VisualCPP, Clang and VisualCPPAfterClang. The first two of these are self evident. The third one indicates that Clang will be used at first, and if preprocessing errors are encountered, the same file will be preprocessed by the Visual C++ preprocessor instead. This option is a default one (VisualCPPAfterClang).

Page 74: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

By default the analyzer will not produce diagnostic messages for libpng and zlib libraries (it is still possible to re-enable them).

New diagnostic V596. The object was created but it is not being used. The 'throw' keyword could be missing.

PVS-Studio 4.39 (November 25, 2011) New diagnostics were implemented (V594, V595).

By default the analyzer will not produce diagnostic messages for Boost library (it is still possible to re-enable them).

Progress dialog will not be shown anymore during incremental analysis, an animated tray icon, which itself will allow pausing or aborting the analysis, will be used instead.

New "Don't Check Files and hide all messages from ..." command was added to the output window context menu. This command allows you to filter the messages and afterwards prevent the verification of files from the specified directories. The list of filtered directories can be reviewed in "Don't Check Files" options page.

The detection of Intel C++ Compiler integration have been revamped - PVS-Studio will not run on projects using this compiler, it is required to replace the compiler with Visual C++ one.

"Quick Filters" functionality was implemented. It allows filtering all the messages which do not meet the specified filtering settings.

PVS-Studio 4.38 (October 12, 2011) Speed increase (up to 25% for quad core computers).

"Navigate to ID" command added to the context menu of PVS-Studio window.

New "Find in PVS-Studio Output" tool window allows searching of keywords in analysis results.

New diagnostic rules added (V2005).

Options button on PVS-Studio Output Window was renamed to Suppression and now contain only three tab pages.

PVS-Studio 4.37 (September 20, 2011) New diagnostic rules added (V008, V2003, V2004).

Now you can export PVS-Studio analysis report to text file.

We use extended build number in some case.

Page 75: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

PVS-Studio 4.36 (August 31, 2011) New diagnostic rules added (V588, V589, V590, V591, V592, V593).

Changes in PVS-Studio menu.

PVS-Studio 4.35 (August 12, 2011) New diagnostic rules added (V583, V584, V806, V585, V586, V587).

PVS-Studio 4.34 (July 29, 2011) Now 64-bit analysis disabled by default.

Now Incremental Analysis enabled by default.

Changes of behavior in trial mode.

PVS_STUDIO predefined macro was added.

Fixed problem with Incremental Analysis on localized versions of Visual Studio.

Balloon notification and tray icon (after analysis finished) was added.

New diagnostic rules added (V582).

Changed image to display on the left side of the wizard in the Setup program.

PVS-Studio 4.33 (July 21, 2011) Incremental Analysis feature now available for all versions of Microsoft

Visual Studio (2005/2008/2010).

Speed increase (up to 20% for quad core computers).

New diagnostic rules added (V127, V579, V580, V581).

PVS-Studio 4.32 (July 15, 2011) Changes in PVS-Studio's licensing policy.

Dynamic balancing of CPU usage.

Stop Analysis button work faster.

PVS-Studio 4.31 (July 6, 2011) Fixed problem related to interaction with other extensions (including Visual

Assist).

Page 76: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

New diagnostic rules added (V577, V578, V805).

PVS-Studio 4.30 (June 23, 2011) The full-fledged support for analyzer's operation through command line was

implemented. It is possible to verify independent files or sets of files launching the analyzer from Makefile. Also the analyzer's messages can be viewed not only on screen (for each file), but they also can be saved into single file, which later can be opened in Visual Studio and the regular processing of the analysis' results can be performed, complete with setting up error codes, message filters, code navigation, sorting etc. Details.

New important mode of operation: Incremental Analysis. As of this moment PVS-Studio can automatically launch the analysis of modified files which are required to be rebuilt using 'Build' command in Visual Studio. All of developers in a team can now detect issues in newly written code without the inconvenience of manually launching the source code analysis - it happens automatically. Incremental Analysis operates similar to Visual Studio IntelliSence. The feature is available only in Visual Studio 2010. Details.

"Check Selected Item(s)" command was added.

Changes in starting "Check Solution" via command line. Details.

New diagnostic rules added (V576).

PVS-Studio 4.21 (May 20, 2011) New diagnostic rules added (V220, V573, V574, V575).

TFS 2005/2008/2010 integration was added.

PVS-Studio 4.20 (April 29, 2011) New diagnostic rules added (V571, V572).

Experimental support for ARMV4/ARMV4I platforms for Visual Studio 2005/2008 (Windows Mobile 5/6, PocketPC 2003, Smartphone 2003).

New "Show License Expired Message" option.

PVS-Studio 4.17 (April 15, 2011) New diagnostic rules added (V007, V570, V804)

Incorrect display of analysis time in some locales has been fixed.

New "Analysis Timeout" option. This setting allows you to set the time limit, by reaching which the analysis of individual files will be aborted with V006 error, or to completely disable analysis termination by timeout.

Page 77: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

New "Save File After False Alarm Mark" option. It allows to save or not to save a file each time after marking it as False Alarm.

New "Use Solution Folder As Initial" option. It defines the folder which is opened while saving the analysis results file.

PVS-Studio 4.16 (April 1, 2011) It is possible now to define a list of files to be analyzed while launching the

tool from command line. This can be used, for example, to check only the files which were updated by a revision control system. Details.

"Check only Files Modified In" option has been added into tool's settings. This option allows you to define the time interval in which the presence of modifications in analyzed files will be controlled using "Date Modified" file attribute. In other words, this approach would allow for verification of "all files modified today". Details.

PVS-Studio 4.15 (March 17, 2011) There are much fewer false alarms in 64-bit analysis.

Changes in the interface of safe-type definition.

The error of processing stdafx.h in some special cases is fixed.

Handling of the report file was improved.

The progress dialogue was improved: you can see the elapsed time and the remaining time.

PVS-Studio 4.14 (March 2, 2011) There are much fewer false alarms in 64-bit analysis.

New diagnostic rules were added (V566, V567, V568, V569, V803).

A new column "Asterisk" was added in the PVS-Studio message window - you may use it to mark interesting diagnoses with the asterisk to discuss them with your colleagues later. The marks are saved in the log file.

Now you may access PVS-Studio options not only from the menu (in the usual settings dialogue) but in the PVS-Studio window as well. This makes the process of setting the tool quicker and more convenient.

Now you may save and restore PVS-Studio settings. It enables you to transfer the settings between different computers and workplaces. We also added the "Default settings" command.

The state of PVS-Studio window's buttons (enabled/disabled) is saved when you launch Microsoft Visual Studio for the next time.

Page 78: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

PVS-Studio 4.13 (February 11, 2011) New diagnostic rules are added V563, V564, and V565).

The "Check for updates" command is added into the PVS-Studio menu.

The "Hide all VXXX errors" command is added into context menu in PVS-Studio window. If you wish to enable the display of VXXX error messages again you can do it through PVS-Studio->Options->Detectable errors page.

Suppressing false positives located within macro statements (#define) is added.

PVS-Studio 4.12 (February 7, 2011) New diagnostic rules are added (V006, V204, V205, V559, V560, V561, and

V562).

Changes in V201 and V202 diagnostic rules.

PVS-Studio 4.11 (January 28, 2011) V401 rule changed to V802.

Fixed bug with copying messages to clipboard.

PVS-Studio 4.10 (January 17, 2011) New diagnostic rules are added (V558).

PVS-Studio 4.00 (December 24, 2010) New diagnostic rules are added (V546-V557).

The issue of processing property sheets in Visual Studio 2010 is fixed.

The error of traversing projects' tree is fixed.

The "Project" field is added into the PVS-Studio window - it shows the project the current diagnostic message refers to.

The issue of installing PVS-Studio for Visual Studio 2010 is fixed - now PVS-Studio is installed not only for the current user but for all the users.

The crash is fixed occurring when trying to save an empty report file.

The issue of absent safe_types.txt file is fixed.

The error is fixed which occurred when trying to check files included into the project but actually absent from the hard disk (for instance, autogenerated files).

Page 79: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Indication of processing the project's tree is added.

The file with PVS-Studio's analysis results (.plog extension) is now loaded by double-click.

The licensing policy is changed.

PVS-Studio 4.00 BETA (November 24, 2010) A new set of general-purpose static analysis rules (V501-V545, V801).

New diagnostic rules are added (V124-V126).

Changes in the licensing policy.

A new window for diagnostic messages generated by the analyzer.

Speed increase.

PVS-Studio 3.64 (27 September 2010) Major documentation update, new sections was added.

PVS-Studio 3.63 (10 September 2010) Fixed bug which occurred sometimes during analysis of files located on non-

system partitions.

Fixed bug in calculation of macros' values for certain individual files (and not the whole project).

"What Is It?" feature was removed.

Issues examples for 64-bit code (PortSample) and parallel code (ParallelSample) are merged into single OmniSample example, which is described particularly in documentation.

Fixed crash related to presence of unloaded project in Visual Studio solution.

PVS-Studio 3.62 (16 August 2010) New rule V123: Allocation of memory by the pattern

"(X*)malloc(sizeof(Y))"

The analysis of the code from command line (without Visual Studio project) is improved.

Diagnostic messages from tli/tlh files do not produced by default.

PVS-Studio 3.61 (22 July 2010)

Page 80: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Fixed crash in VS2010 with EnableAllWarnings key enabled in project settings.

Fixed bug related to analysis projects that does excluded from build in Configuration Manager.

The analysis of the code is considerably improved.

PVS-Studio 3.60 (10 June 2010) New rule V122: Memsize type is used in the struct/class.

New rule V303: The function is deprecated in the Win64 system. It is safer to use the NewFOO function.

New rule V2001: Consider using the extended version of the FOO function here.

New rule V2002: Consider using the 'Ptr' version of the FOO function here.

PVS-Studio 3.53 (7 May 2010) "What Is It?" feature is added. Now you can ask PVS-Studio developers

about diagnistic messages produced by our analyzer.

The analysis of the code related to usage of unnamed structures is considerably improved.

Fixed bug in structure size evaluation in certain cases.

PVS-Studio 3.52 (27 April 2010) New online help has been added. The previous help system integrated into

MSDN. It was not very convenient for some reasons (both for us and users). Now PVS-Studio will open the help system on our site. We refused to integrate it into MSDN anymore. As before, the pdf-version

of the documentation is also available.

We stopped supporting Windows 2000.

The settings page "Exclude From Analysis" was deleted - there is now the page "Don't Check Files" instead.

Work in Visual Studio 2010 was improved.

We eliminated the issue of integration into VS2010 when reinstalling.

We fixed work of the function "Mark As False Alarm" with read-only files.

PVS-Studio 3.51 (16 April 2010)

Page 81: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

PVS-Studio supports Visual Studio 2010 RTM.

New rule: V003: Unrecognized error found...

New rule: V121: Implicit conversion of the type of 'new' operator's argument to size_t type.

You may specify filemasks on the tab "Don't Check Files" to exclude some files from analysis.

"Exclude From Analysis" option page improved.

MoreThan2Gb option removed from "Viva64" option page (this option is deprecated).

If you want check code from command line then you must indicate analyzer type (Viva64 or VivaMP).

Priority of analyzer's process is reduced. Now you can work on computer more suitable while analysis is running.

PVS-Studio 3.50 (26 March 2010) PVS-Studio supports Visual Studio 2010 RC. Although Visual Studio has

not been released officially yet, we have already added the support for this environment into the analyzer. Now PVS-Studio integrates into Visual Studio 2010 and can analyze projects in this environment. Help system in Visual Studio 2010 has been changed, so the Help section of PVS-Studio does not integrate into the documentation yet as it is done in Visual Studio 2005/2008. But you still may use online-Help. Support of Visual Studio 2010 RC is not complete.

A new PDF-version of Help system is available. Now we ship a 50-page PDF-document in the PVS-Studio distribution kit. It is a full copy of our Help system (that integrates into MSDN in Visual Studio 2005/2008 and is available online).

PVS-Studio now has a new mechanism that automatically checks for new versions of the tool on our site. Checking for the updates is managed through the new option CheckForNewVersions in the settings tab called "Common Analyzer Settings". If the option CheckForNewVersions is set to True, a special text file is downloaded from www.viva64.com site when you launch code testing (the commands Check Current File, Check Current Project, Check Solution in PVS-Studio menu). This file contains the number of the latest PVS-Studio version available on the site. If the version on the site is newer than the version installed on the user computer, the user will be asked for a permission to update the tool. If the user agrees, a special separate application PVS-Studio-Updater will be launched that will automatically download and install the new PVS-Studio distribution kit. If the option CheckForNewVersions is set to False, it will not check for the updates.

Page 82: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

We have implemented the support for the standard C++0x at the level it was done in Visual Studio 2010. Now it supports lambda expressions, auto, decltype, static_assert, nullptr, etc. In the future, as C++0x support in Visual C++ is developing, the analyzer PVS-Studio will also provide support for the new C++ language capabilities.

Now you can check solutions with PVS-Studio from the command line instead of Visual Studio environment. Note that we still mean that the checking will be performed from Visual Studio involving the files of projects (.vcproj) and solutions (.sln) but it will be launched from the command line instead of IDE. This way of launching the tool may be useful when you need to regularly check the code with the help of build systems or continuous integration systems.

New rule V1212: Data race risk. When accessing the array 'foo' in a parallel loop, different indexes are used for writing and reading.

We added a code signature certificate in the new version of our tool. It is done for you to be sure that the distribution kit is authentic, and get fewer warnings from the operating system when installing the application.

PVS-Studio 3.44 (21 January 2010) Partial support of code testing for Itanium processors. Now the code that

builds in Visual Studio Team System for Itanium processors may be also tested with the analyzer. Analysis can be performed on x86 and x64 systems but analysis on Itanium is not implemented yet.

We reduced the number of the analyzer's false alarms when analyzing an array access. Now, in some cases, the analyzer "understands" the ranges of values in the for loop and does not generate unnecessary warnings on accessing arrays with these indexes. For example: for (int i = 0; i < 8; i++) arr[i] = foo(); // no warning from the analyzer.

The number of the analyzer's false alarms is reduced - we introduced a list of data types that do not form large arrays. For example, HWND, CButton. Users may compose their own type lists.

The installer error is corrected that occurs when installing the program into a folder different than the folder by default.

PVS-Studio 3.43 (28 December 2009) Option ShowAllErrorsInString removed (now it always has the value true).

New rule V120: Member operator[] of object 'foo' declared with 32-bit type argument, but called with memsize type argument.

New rule V302: Member operator[] of 'foo' class has a 32-bit type argument. Use memsize-type here.

Page 83: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Operator[] analysis enhanced.

Error of long removal of the program in case of recurrent installation "over the program again" corrected.

Fixed problem related to analysis files with "^" character in filename.

PVS-Studio 3.42 (9 December 2009) Errors diagnostics with magic numbers enhanced. Now in a message about a

problem, more information is given out; this allows to use filters in a more flexible way.

Error during work with precompiled header files of special type corrected.

Option DoTemplateInstantiate is now turned on by default.

Error with preprocessor hang-up at large number of preprocessor messages corrected.

Analysis of operator[] enhanced.

PVS-Studio 3.41 (30 November 2009) Error of same name files analysis during work on a multicore machine

corrected.

Error of incorrect diagnostics of some types of cast-expressions corrected.

Parsing of overloaded functions in the analyzer improved considerably.

Diagnostics of incorrect use of time_t type added.

Processing of special parameters in the settings of Visual C++ project files added.

PVS-Studio 3.40 (23 November 2009) A new feature "Mark as False Alarm" has been added. Due to it, it is now

possible to mark those lines in the source code in which false alarm of the code analyzer happens. After such marking, the analyzer will not output any diagnostic messages for such code any more. This allows to use the analyzer constantly and more conveniently in the process of software development for new code verification.

Project Property Sheets support added, a procedure of easy-to-use Visual Studio projects setup.

During the verification of parallel programs, the analyzer can walk the code twice, this will allow to collect more information and carry out more precise diagnostics of some errors.

Page 84: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

PVS-Studio 3.30 (25 September 2009) In PVS-Studio, the possibility of testing 32-bit projects for estimating the

complexity and cost of code migration to 64-bit systems has been added.

A new rule for 64-bit code analysis has been added, V118: malloc() function accepts a dangerous expression in the capacity of an argument.

A new rule for 64-bit code analysis has been added, V119: More than one sizeof() operators are used in one expression.

A new rule for parallel code analysis has been added, V1211: The use of 'flush' directive has no sense for private '%1%' variable, and can reduce performance.

Combined operation with Intel C++ Compiler has been improved (crash at the attempt of code verification with installed Intel C++ Compiler has been corrected.)

Localized versions of Visual Studio support has been enhanced.

PVS-Studio 3.20 (7 September 2009) The error of incorrect output of some messages in Visual Studio localized

versions has been corrected.

Log-file loading improved.

Critical errors processing improved - now it is easy to inform us on possible tools problems.

Installer operation improved.

Project files walking error corrected.

PVS-Studio 3.10 (10 August 2009) Templates instantiating support has been added. Now the search of potential

errors is carried out not simply by template body (as it was earlier), but also template parameters substitution is made for more thorough diagnostics.

The code analyzer can work in the mode of Linux environment simulation. We have added the support of various data models. That is why, now it is possible to verify cross platform programs on a Windows system the way it would be carried out on a Linux system.

The error connected with incorrect functioning of the analyzer of parallel errors in 32-bit environment has been corrected.

The work of the analyzer with templates has been considerably improved.

Page 85: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

PVS-Studio 3.00 (27 July 2009) Software products Viva64 and VivaMP are united into one program complex

PVS-Studio.

The new version is a significantly upgraded software product.

Operation of the unit of integration into Visual Studio is much more stable.

Operation rate in multi-processor systems is increased: analysis is performed in several threads, and the number of the analyzer's operating threads can be set with the help of "Thread Count" option. By default the number of threads corresponds to the number of cores in the processor but it can be reduced.

A possibility to operate the analyzer from the command line is added. A new option "Remove Intermediate Files" is added into the settings of the program which allows you not to remove command files created during the code analyzer's operation. These command files can be launched separately without launching Visual Studio to perform analysis. Besides, when creating new command files you can perform by analogy analysis of the whole project without using Visual Studio.

It became more simple, convenient and quick to operate diagnosis of separate errors. Now you can enable and disable the function of showing separate errors in the analysis' results. What is the most important is that changing of the message list is performed automatically without the necessity of relaunching analysis. Having performed analysis you can scroll through the list of errors or simply disable showing of those errors which are not relevant to you project.

Operating with error filters has been improved greatly. Filters for hiding some messages are now defined simply as a list of strings. Like in case of diagnosing separate errors, using filters doesn't demand relaunching analysis.

Change of licensing policy. Although PVS-Studio is a single product, we provide licensing both for separate analysis units such as Viva64 and VivaMP and for all the units together. Besides, there are licenses for one user or for a team of developers. All these changes are reflected in registration keys.

Support of localized versions of Visual Studio has been improved greatly.

Help system for a new version of PVS-Studio integrating into MSDN has been modified and improved greatly. Description of new sections allows you to master operation with the software product better.

Graphic design of the software product has been improved. New icons and graphics in the installer make the analyzer's appearance more beautiful.

VivaMP 1.10 (20 April 2009)

Page 86: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analysis of the code containing calls of the class static functions has been improved.

New diagnostic rules for the analysis of errors connected with the exceptions V1301, V1302, V1303 have been implemented.

The error of the incorrect display of the analysis progress indicator on machines with non-standard DPI has been corrected.

Some other enhancements have been implemented.

VivaMP 1.00 (10 March 2009) VivaMP 1.00 release.

VivaMP 1.00 beta (27 November 2008) First public beta version release on the Internet.

Viva64 2.30 (20 April 2009) New diagnostic rule V401 has been implemented.

Constants processing has been improved, in a number of cases, this reduces the quantity of false diagnostic warnings.

The error of the incorrect display of the analysis progress indicator on machines with non-standard DPI has been corrected.

A number of errors have been corrected.

Viva64 2.22 (10 Mach 2009) Collaboration of Viva64 and VivaMP is improved.

Analyzer performance is improved up to 10%.

Viva64 2.21 (27 November 2008) Collaboration of Viva64 and VivaMP is added.

Viva64 2.20 (15 October 2008) Diagnosis of potentially unsafe constructions is improved. As the result the

number of the code analyzer's "false alarms" is reduced approximately by 20%. Now the developer will spend less time to analyze the code diagnosed as potentially unsafe.

Page 87: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Help system is amended. It has been extended and new examples have been added. As diagnosis of potentially unsafe constructions is improved in this version Help system has been also supplemented with explanations concerning the constructions which are now considered safe.

The speed of a project's structure analysis is raised. Now the same work is performed 10 times quicker. As the result the total time of the whole project's analysis is reduced.

C++ template analysis is improved. It's not a secret that far not all the code analyzers understand templates. We're constantly working to improve diagnosis of potentially unsafe constructions in templates. Such an improvement is made in this version.

Format of some code analyzer's messages is amended to make it possible to set filters more accurately. Thus now, for example, the analyzer doesn't only inform about an incorrect index type while accessing an array but also shows the name of the array itself. If the developer is sure that such an array cannot cause problems in 64-bit mode at all he can filter all the messages concerning this array's name.

Viva64 2.10 (05 September 2008) Visual C++ 2008 Service Pack 1 support is added.

Viva64 2.0 (09 July 2008) Visual C++ 2008 Feature Pack (and TR1) support is added.

Pedantic mode is added which allows you to find constructions potentially dangerous but rarely causing errors.

Diagnosis of template functions is improved

Viva64 1.80 (03 February 2008) Visual Studio 2008 is fully supported now.

Source code analysis speed is increased.

Installer is improved. Now you can install Viva64 without administrator privileges for personal usage.

Viva64 1.70 (20 December 2007) The support of a new diagnostic message (V117) is added. Memsize type

used in union.

Fixed critical bug related to detection of more than one errors in source line.

Page 88: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Fixed bug in type evaluation in some complex syntax.

User Interface is improved. Now you can see a common analysis progress indicator.

Visual Studio 2008 support is added (BETA).

Viva64 1.60 (28 August 2007) The support of a new diagnostic message (V112) is added. Dangerous magic

number used.

The support of a new diagnostic message (V115) is added. Memsize type used for throw.

The support of a new diagnostic message (V116) is added. Memsize type used for catch.

The restriction of a trial version is changed. In each analyzed file the location of only some errors is shown.

Viva64 1.50 (15 May 2007) C source analysis is fully supported. Now C source code may be analyzed

correctly.

Viva64 1.40 (1 May 2007) Message Suppression feature added. You can adjust filters on the Message

Suppression page of the Viva64 settings to ignore some of the warning messages. For example, you can adjust filters to skip messages with particular error codes and messages including names of specific variables and functions.

Ability to save/load analysis results added.

Analysis results representation improved. The results are now displayed in the Visual Studio standard Error List window, just like the compiler messages.

Viva64 1.30 (17 March 2007) Representation of the process of the code analysis is improved. Unnecessary

windows switching are removed, a general progress bar is created.

Toolbar with Viva64 commands is added.

The user now can point the analyzer if its program is using more than 2GB of RAM. On using less than 2GB some warning messages are disabled.

Page 89: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The support of a new diagnostic message (V113) is added. Implicit type conversion from memsize to double type or vice versa.

The support of a new diagnostic message (V114) is added. Dangerous explicit type pointer conversion.

The support of a new diagnostic message (V203) is added. Explicit type conversion from memsize to double type or vice versa.

Viva64 1.20 (26 January 2007) Filtration of repeating error messages is added. It is useful when there are

errors in header files. Earlier if *.h file with an error included into different *.cpp files the warning message about the error in the *.h file was shown several times. Now there is only one message about in the *.h file shown.

Now Viva64 informs about the number of errors found after the code analysis. You can always see:

- how much code is left to be checked;

- how many errors are corrected already;

- which modules contain the largest number of errors.

Support of some hot keys is added. Now you can interrupt the analyzer's work with the help of Ctrl+Break. In case you want to check the current file just press Ctrl+Shift+F7.

There are some errors of the analyzer's work corrected.

Viva64 1.10 (16 January 2007) With the help of the Viva64 analyzer itself we've prepared the 64-bit version

of Viva64 at once! But you should not care about the choose of the right version during the installation. The installer will find out itself which version should be installed for your operation system.

The support of a new rule is added. Now the parameters of the functions with the variable number of arguments are checked (V111-error code).

There is no unnecessary diagnosis of the address to the array item with the help of enum values.

There is no unnecessary diagnosis of the constructions of type int a = sizeof(int).

The Help System is improved.

Viva64 1.00 (31 December 2006) First public release on the Internet.

Page 90: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Limitation of the analyzerAnalyzer doesn't fully support diagnosis of errors while using some C/C++

constructions. It may cause false warning messages or absence of messages in some

cases.

Analyzer doesn't fully support some language extensions implemented in Visual C+

+. Neither is there support of some aspects of modern C++ standard.

The analyzer does not work with files in Unicode format either, nor with files

containing Unicode symbols in their paths.

Main limitations:

Incomplete support of complex templates (for example, with partial specialization);

Incomplete support of overloaded functions;

Analysis of managed code is not implemented;

msclr namespace is not supported;

We should mention that in practice these limitations don't influence the code

analysis quality and you just should be aware of their existence.

Common information on working with the PVS-Studio analyzer

Abstract

System requirements and installation of PVS-Studio

Introduction into PVS-Studio

Fixing errors

How to work with the list of diagnostic messages

Is it necessary to fix all the potential errors the analyzer informs about?

Abstract

Page 91: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The article is a tutorial on working with the PVS-Studio code analyzer. This section

contains examples of how to perform most common tasks when working with the

PVS-Studio analyzer.

System requirements and installation of PVS-StudioThe PVS-Studio analyzer is intended to work on the Windows platform. It

integrates into Microsoft Visual Studio 2017, 2015, 2013, 2012, 2010, 2008, 2005

development environments. You may learn about the system requirements for the

analyzer in the corresponding section of the documentation.

After you obtain the PVS-Studio installation package, you may start installing the

program.

Figure 1 - Installation of PVS-Studio

After approval of the license agreement, integration options will be presented for

various supported versions of Microsoft Visual Studio (figure 2). Integration

options which are unavailable on a particular system will be greyed-out. In case

Page 92: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

different versions of the IDE or several IDEs are present on the system, it is

possible to integrate the analyzer into every version available.

Figure 2 –PVS-Studio integration options for various IDEs

To make sure that the PVS-Studio tool was correctly installed, you may open the

About window (Help/About menu item). The PVS-Studio analyzer must be present

in the list of installed components (Figures 3, 4).

Page 93: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Figure 3 - About Microsoft Visual Studio window with the PVS-Studio component installed

Before you begin working in the program, we also recommend you to unpack the

collection of samples into any folder you want from Start\PVS-Studio\. (Project

samples, file Samples.zip)

With the help of this examples you may study defects that can be identified by the

PVS-Studio analyzer. OmniSample contains samples of issues occurring when

porting software from 32-bit systems to 64-bit ones and also allow you to see what

happens with parallel programs that have "parallel" errors. Further description in

this article will be based on this sample collection.

Introduction into PVS-StudioLet's open the MSVSSamples project inside Microsoft Visual Studio (on any IDE

version available). After the project is opened, let's start the analysis for all files by

"PVS-Studio -> Check Solution / Check Project Group" IDE main menu command

(figure 4).

Page 94: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Figure 4 – launching analysis by PVS-Studio

After launching the verification, the progress bar will appear with the buttons Pause

(to pause the analysis) and Stop (to terminate the analysis). Potentially dangerous

constructs will be displayed in the list of detected errors during the analysis

procedure (Figure 5).

Figure 5 - Project analysis

The term "a potentially dangerous construct" means that the analyzer considers a

particular code line a defect. Whether this line is a real defect in an application or

not is determined only by the programmer who knows the application. You must

correctly understand this principle of working with code analyzers: no tool can

completely replace a programmer when solving the task of fixing errors in

programs. Only the programmer who relies on his knowledge can do this. But the

tool can and must help him with it. That is why the main task of the code analyzer is

to reduce the number of code fragments the programmer must look through and

decide what to do with them.

Page 95: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Once the code analysis is over, you may look through the messages.

Fixing errorsAfter getting the list of diagnostic messages from the analyzer, you may study them.

Let's look at the first error:

error V579 The memset function receives the pointer and its size as arguments. It is

possibly a mistake. Inspect the third argument. sample1.cpp 7

And here is the corresponding source code:

data->num = 10000;

data->sum = 10000;

memset(data, 0, sizeof(data));

The issue here is that the 'data' structure will not be filled with zeroes completely.

The mistake is that the 'sizeof(data)' expression is incorrect. To fix the issue, the

correct size of the ’data' structure should be passed to the 'memset' function:

memset(data, 0, sizeof(*data));

You may learn about this diagnostics type in the help system. If you click on the

cell containing the code of an error in the 'Code' column, you will see a window

with the description for this error (figure 6 is for Microsoft Visual Studio IDE):

Figure 6 - Detailed description of an error and ways of fixing it

Page 96: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

After applying the fix, let's restart the analysis to see that there is one less item in

the diagnostic warnings. It means that the issue is fixed. In the same way we should

review all the diagnostic messages and fix those fragments in the code where

problems are possible.

How to work with the list of diagnostic messagesOf course, in real large projects, there will be not dozens but hundreds or even

thousands of diagnostic messages and it will be a hard task to review them all. To

make it easier, the PVS-Studio analyzer provides several mechanisms. The first

mechanism is filtering by the error code. The second is filtering by the contents of

the diagnostic messages' text. The third is filtering by file paths. Let's examine

examples of using filtering systems.

Suppose you are sure that the diagnostic messages with the code V112 (using magic

numbers) are never real errors in your application. In this case you may turn off the

display of these diagnostic warnings in the analyzer's settings:

Figure 7 - Turning off some diagnostic messages by code

Page 97: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

After that, all the diagnostic warnings with the code V112 will disappear from the

error list. Note that you do not need to restart the analyzer. If you turn on these

messages again, they will appear in the list without relaunching the analysis as well.

Now let's study another way of filtering by the text of diagnostic messages. Let's

return to our example. One of the issues found by the analyzer is that the 'N >= 0'

expression always equals true:

V547 Expression 'N >= 0' is always true. Unsigned type value is always >= 0.

sample1.cpp 18

Here is the corresponding source code:

if (TEST(N))

{

data->num = N;

}

Of course it is possible to just fix this code, and te diagnostic message will

disappear. Here the problem is, the macro that is used here can become correct on a

different platform. So, if such a code occurs frequently and there is an absolute

certainty about its correctness, then it is possible to disable the display of messages

containing the 'Expression 'N >= 0' is always true' string. It can be accomplished

through the MessageSuppression options page:

Page 98: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Figure 8 - Turning off some diagnostic messages by their text

After that, all the diagnostic messages whose text contains that expression will

disappear from the error list, without the necessity of restarting the code analyzer.

You may get turn them on back by simply deleting the expression from the filter.

The last mechanism of reducing the number of diagnostic messages is filtering by

masks of project files' names and file paths.

Suppose your project employs the Boost library. The analyzer will certainly inform

you about potential issues in this library. But if you are sure that these messages are

not relevant for your project, you may simply add the path to the folder with Boost

on the page Don't check files (Figure 9):

Page 99: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Figure 9 - Setting message filtering by file location and names

After that, the diagnostic messages referring to the files in this folder will not be

shown. This option requires restarting the analysis.

Also, PVS-Studio has the "Mark as False Alarm" function. It enables you to mark

those lines in your source code which cause the analyzer to generate false alarms.

After marking the code, the analyzer will not produce diagnostic warnings on this

code. This function makes it more convenient to use the analyzer permanently

during the software development process when verifying newly written code.

Thus, in the following example, we turned off the diagnostic messages with the

code V640:

for (int i = 0; i < m; ++i)

for (int j = 0; j < n; ++j)

matrix[i][j] = Square(i) + 2*Square(j);

cout << "Matrix initialization." << endl; //-V640

...

This function is described in more detail in the section "Suppression of false

alarms".

Page 100: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

There are also some other methods to influence the display of diagnostic messages

by changing the code analyzer's settings but they are beyond the scope of this

article. We recommend you to refer to the documentation on the code analyzer's

settings.

Is it necessary to fix all the potential errors the analyzer informs about?When you have reviewed all the messages generated by the code analyzer, you will

find both real errors and constructs which are not errors. The point is that the

analyzer cannot detect 100% exactly all the errors in programs without producing

the so called "false alarms". Only the programmer who knows and understands the

program can determine if there is an error in each particular case. The code analyzer

just significantly reduces the number of code fragments the developer needs to

review.

So, there is certainly no reason for correcting all the potential issues the code

analyzer refers to.

But you must attempt to fix as many fragments as possible. It is especially relevant

when the static analyzer is used to verify an application not once, for instance, when

you port it to a 64-bit system, but regularly with the purpose to find new errors and

inefficient constructs brought into a project. In this case, correcting fragments which

are really not errors and setting the analyzer for suppressing particular types of

errors will significantly reduce time for the next launch of the analyzer.

Analyzer Modes

Analyzer's diagnostics units

Grouping analyzer's messages by their significance levels

Analyzer's diagnostics unitsPVS-Studio consists of several units used for analysis. At present they are:

Page 101: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

the unit for general analysis diagnostics;

the unit for diagnosing possible optimizations;

the unit for diagnosing problems in 64-bit code (Viva64);

To enable/disable the display of diagnostic messages belonging to one particular

analyzer unit you can use special check buttons (GA, OP, 64), as shown below

(Figure 1):

Figure 1 — PVS-Studio diagnostics units

It is not necessary to restart the analysis anew after changing the display settings for

diagnostics units.

Grouping analyzer's messages by their significance levelsAll messages produced by the analyzer are distributed among 4 groups: the 'Fails'

group and the 3 significance levels – Level1, Level 2 and Level 3, as you can see

below (figure 2):

Figure 2 — Analyzer's messages being distributed among significance groups

The 'Fails' group contains the massages related to analyzer's operational errors (for

instance the V001, V003 messages etc.), and also any unprocessed output produced

by auxiliary utilities employed by the analyzer (preprocessor, cmd command line

processor), which they themselves pass into stdout/stderr. For example, the 'Fails'

group can contain preprocessor's source code compilation error, file access errors

(the file was not found or it was blocked by antiviral software) etc.

Page 102: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

All analyzer's diagnostics messages containing the potential issues in the source

code are distributed among significance level groups. The importance of any of the

messages produced by the static analyzer can be estimated from 2 distinctive

parameters: criticality of the potential issue reported and the rate of false positives

generation with it. According to these criteria each diagnostics message produced

by the analyzer is assigned to one out of three significance levels. In this way, the

Level 1 contains the most critical diagnostics which have the highest probability of

being the real issues in the source code, and the Level 3 — the low-critical

diagnostics or diagnostics with the highest false-positives generation. It's worth

noting that the error's code not necessarily completely ties it to a certain significance

level. The distribution of messages among these level groups is highly dependent of

the context inside which they were generated.

Initially the third and second levels are disabled by default. To enable them you

should use the corresponding check buttons, as was shown in figure 2.

Suppression of false alarms

Abstract

Suppression of individual false positives

Suppression of multiple false positives by using the group filtering mechanism

Demonstration of the Mark as False Alarm function using OmniSample project as example

Implementation of the false alarm suppression function

Suppressing false positives located within C/C++ macro statements (#define) and for other code fragments

Mass suppression of false positives through diagnostic configuration files (pvsconfig)

Other means of filtering messages in the PVS-Studio analyzer

Possible issues

AbstractThis section describes analyzer's message suppression features. It provides ways to

control both the separate analyzer messages under specific source code lines and

Page 103: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

whole groups of messages related, for example, to the use of C/C++ macros. The

described method, by using comments of a special format, allows disabling

individual analyzer rules or modifying text of analyzer's messages.

Features described in the following section are applicable to both C/C++ and C# PVS-Studio analyzers, if the contrary is not stated explicitly.

Suppression of individual false positivesAny code analyzer always produces a lot of the so called "false alarms" besides

helpful messages. These are situations when it is absolutely obvious to the

programmer that the code does not have an error but it is not obvious to the

analyzer. Such messages are called false alarms. Consider a sample of code:

ptrdiff_t value;

fread(&value, 4, 1, f);

char RGBA[4];

There will be two V112 warnings generated for this code since the magic constant 4

is used here. In the first case, it is an error because the size of a variable of the

ptrdiff_t type will not equal four bytes in the 64-bit system. In the second case,

number 4 signifies the number of color components and is safe. In PVS-Studio,

beginning with the version 3.40, we have implemented the capability to mark an

error message generated by PVS-Studio as a false alarm. You may do this either

manually or with the help of the corresponding context menu command.

Appearance of the "Mark as False Alarm" option in PVS-Studio greatly extends the

potential of integrating the code analyzer into the software development process at

the stage of everyday permanent use, which allows you not only port applications to

the 64-bit platform but also make sure that there are no dangerous issues in new

code you have just developed.

To suppress a false alarm, you may add a special comment into the code:

char RGBA[4]; //-V112

Now the analyzer will not generate the V112 warning on this line.

Page 104: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

You may type the comment suppressing warnings into the code by yourself. You

may also use a special command provided by PVS-Studio. The user is provided

with two commands available from the PVS-Studio's context menu (see Figure 1).

Figure 1 - Commands to work with the mechanism of false alarm suppression

Let's study the available commands concerning False Alarm suppression:

1. Mark selected errors as False Alarm. You may choose one or more false alarms

in the list (see Figure 2) and use this command to mark the corresponding code as

safe.

Figure 2 - Choosing warnings before executing the Mark Selected errors as False Alarms command

2. Remove False Alarm marks from selected errors. This command removes the

comment that marks code as safe. This function might be helpful if, for instance,

you were in a hurry and marked some code fragment as safe by mistake. Like in the

previous case, you must choose the required messages from the list.

Suppression of multiple false positives by using the group filtering mechanism

Page 105: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

It is possible that certain kinds of diagnostics are not essential for the project being

analyzed (For example, if you are not interested in the errors relating to explicit type

casting — V201, V202, V203 codes, e.t.c.), or one of the diagnostics produces

warnings for the source code which, you have no doubt in it, is correct. In such a

situation one could utilize the group suppression mechanism, which is based on

filtering the analysis output results. The list of available filtering modes can be

accessed through the 'PVS-Studio -> Options' menu item.

The group filtering modes include Detectable Errors, Don't Check

Files and Keyword Message Filtering.

Utilizing the "Hide all Vxxx errors" context menu command (see in figure 1) it is

possible to disable the display of all the errors belonging to a certain code. To

enable the display of these errors again you should select the "Detectable Errors"

options page and set the required code as True.

The suppression of multiple messages through filters does not require restarting of

the analysis, the filtering results will appear in PVS-Studio output window

immediately.

Demonstration of the Mark as False Alarm function using OmniSample project as exampleLet's show how to use the Mark as False Alarm function with a code example.

After opening an example project, let's launch analysis of the solution using the

"PVS-Studio -> Check -> Solution " command. As the analysis is over, the list of

detected issues will appear (Figure 3).

Figure 3 - list of issues detected by PVS-Studio

You need to review and study this message list.

Page 106: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Next, let's study the following message of the analyzer:

V547 Expression 'N >= 0' is always true. Unsigned type value is always >= 0.

sample1.cpp 18

Let's assume that the source code corresponding to this message (and this line

number) is:

if (TEST(N))

{

data->num = N;

}

Certainly, it is possible to just simply rewrite this fragment for this message to

disappear. The issue is that the macro expression, which is utilized here, can be

correct on different platforms. So if you are completely confident that the code is

correct, it is possible to "disable" the display of this particular type of messages

(V547) in this particular line (18) of this file (sample1.cpp). To do this, you should

select the corresponding error message in the PVS-Studio window and choose the

"Mark Selected Errors as False Alarm" command in the PVS-Studio context menu

(Figure 4).

Figure 4 - "Mark Selected Errors as False Alarm" command

After that, the " //-V547" comment will be automatically added into the code:

if (TEST(N))//-V547

This comment informs the code analyzer that it must not generate the message

about this error in this line when analyzing the project next time.

Page 107: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

You may add this comment manually as well without using the "Mark selected

errors as False Alarms" command, but you must follow the note's format: two

slashes, minus (without a space), error code.

After marking the message as a false alarm, the message will disappear from error

list. You may enable the display of messages marked as 'False Alarms' in PVS-

Studio error list by changing the value of 'PVS-Studio -> Options... -> Specific

Analyzer Settings -> DisplayFalseAlarms' settings option.

You may remove the comment using the "Remove False Alarm marks from selected

errors" command, having chosen the error message in the PVS-Studio window

beforehand. You may also remove the comment manually.

Figure 5 - "Remove False Alarm Marks from selected errors" command

So, we have marked one error as a "false alarm". Let's re-launch the analysis and

see that we get one less message. Note that the message we have marked as a "false

alarm" is absent in the PVS-Studio window this time.

If you need to see all the messages in the PVS-Studio window (including the false

alarms), you may enable their display once again in the options dialog.

You may mark several messages at once. To do this, you should choose them in the

PVS-Studio window (Figure 6).

Figure 6 - Choosing several messages to mark in the PVS-Studio window

We do not recommend you to mark messages as false alarms without preliminarily

reviewing the corresponding code fragments since it contradicts the ideology of

Page 108: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

static analysis. Only the programmer can determine if a particular error message is

false or not.

Implementation of the false alarm suppression functionUsually compilers employ #pragma-directives to suppress individual error

messages. Consider a code sample:

unsigned arraySize = n * sizeof(float);

The compiler generates the following message:

warning C4267: 'initializing' : conversion from 'size_t' to 'unsigned int', possible

loss of data

x64Sample.cpp 151

This message can be suppressed with the following construct:

#pragma warning (disable:4267)

To be more exact, it is better to arrange the code in the following way to suppress

this particular message:

#pragma warning(push)

#pragma warning (disable:4267)

unsigned arraySize = n * sizeof(float);

#pragma warning(pop)

The PVS-Studio analyzer uses comments of a special kind. Suppression of the PVS-

Studio's message for the same code line will look in the following way:

unsigned arraySize = n * sizeof(INT_PTR); //-V103

This approach was chosen to make the target code cleaner. The point is that PVS-

Studio can inform about issues in the middle of multi-line expressions as, for

instance, in this sample:

Page 109: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

size_t n = 100;

for (unsigned i = 0;

i < n; // the analyzer will inform of the issue here

i++)

{

// ...

}

To suppress this message using the comment, you just need to write:

size_t n = 100;

for (unsigned i = 0;

i < n; //-V104

i++)

{

// ...

}

But if we had to add a #pragma-directive into this expression, the code would look

much less clear.

Storage of the marking in source code lets you modify it without the risk to lose

information about lines with errors.

It is also possible to use a separate base where we could store information in the

following approximate pattern: error code, file name, line number. This pattern is

implemented in the different PVS-Studio feature known as 'Message Suppression'.

Suppressing false positives located within C/C++ macro statements (#define) and for other code fragmentsIt goes without saying that the analyzer can locate potential problems within macro

statements (#define) and produce diagnostic messages accordingly. But at the same

time these messages will be produced by analyzer at such positions where the macro

is being used, i.e. where placement of macro's body into the code is actually

happening. An example:

Page 110: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

#define TEST_MACRO \

int a = 0; \

size_t b = 0; \

b = a;

void func1()

{

TEST_MACRO // V101 here

}

void func2()

{

TEST_MACRO // V101 here

}

To suppress these messages you can use the "Mark as False Alarm" command.

Then the code containing suppression commands will look like this:

#define TEST_MACRO \

int a = 0; \

size_t b = 0; \

b = a;

void func1()

{

TEST_MACRO //-V101

}

void func2()

{

TEST_MACRO //-V101

}

But in case the macro is being utilized quite frequently, marking it everywhere as

False Alarm is quite inconvenient. It is possible to add a special marking to the code

manually to make the analyzer mark the diagnostics inside this macro as False

Alarms automatically. With this marking the code will look like this:

Page 111: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

//-V:TEST_MACRO:101

#define TEST_MACRO \

int a = 0; \

size_t b = 0; \

b = a;

void func1()

{

TEST_MACRO

}

void func2()

{

TEST_MACRO

}

During the verification of such a code the messages concerning issues within macro

will be immediately marked as False Alarms. Also, it is possible to select several

diagnostics at once, separating them by comma:

//-V:TEST_MACRO:101, 105, 201

Please note that if the macro contains another nested macro inside it then the name

of top level macro should be specified for automated marking.

#define NO_ERROR 0

#define VB_NODATA ((long)(77))

size_t stat;

#define CHECK_ERROR_STAT \

if( stat != NO_ERROR && stat != VB_NODATA ) \

return stat;

size_t testFunc()

{

{

Page 112: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

CHECK_ERROR_STAT // #1

}

{

CHECK_ERROR_STAT // #2

}

return VB_NODATA; // #3

}

In the example mentioned above the V126 diagnostics appears at three positions. To

automatically mark it as False Alarm one should add the following code at positions

#1 and #2:

//-V:CHECK_ERROR_STAT:126

To make it work at #3 you should additionally specify this:

//-V:VB_NODATA:126

Unfortunately to simply specify "to mark V126 inside VB_NODATA macro" and

not to specify anything for CHECK_ERROR_STAT macro is impossible because

of technical specifics of preprocessing mechanism.

Everything that is written in this section about macros is also true for any code

fragment. For example, if you want to suppress all the warnings of the V103

diagnostic for the call of the function 'MyFunction', you should add such a string:

//-V:MyFunction:103

Mass suppression of false positives through diagnostic configuration files (pvsconfig)Analyzer messages can be manipulated and filtered through the comments of as

special format. Such comments can be placed either in the special diagnostic

configuration files (pvsconfig) for all analyzers, or directly inside the source code

(but only for C/C++ analyzer).

Page 113: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The diagnostic configuration files are plain text files which are added to any Visual

Studio project or solution. To add the configuration file, select the project or

solution in question in the Solution Explorer window inside Visual Studio IDE, and

select a context menu item 'Add New Item...'. In the following window, select the

'PVS-Studio Filters File' template (figure 7):

Figure 7 - adding diagnostic configuration file to a solution.

Because of the specifics of some Visual Studio versions, the 'PVS-Studio Filters File' file template may be absent in some versions and editions of Visual Studio for projects and\or solutions. In such a case, it is possible to use add diagnostic configuration file as a simple text file by specifying the 'pvsconfig' extension manually. Make sure that after the file is added, it is set as non-buildable in its' compilation properties.

When a configuration file is added to a project, it will be valid for all the source

files in this project. A solution configuration file will affect all the source files in all

of the projects added to that solution.

In addition, pvsconfig file can be placed in the user data folder (%AppData%\PVS-

Studio\) - this file will be automatically used by analyzer, without the need to

modify any of your project\solution files.

Page 114: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The 'pvsconfig' files utilize quite a simple syntax. Any line starting with the '#'

character is considered a comment and ignored. The filters themselves are written as

one-line C++/C# comments, i.e. every filter should start with '//' characters.

In case of C/C++ code, the filters can also be specified directly in the source code.

Please note, that this is not supported for C# projects!

Next, let's review different variants of diagnostic configurations and filters.

Filtering analyzer messages by a fragment of source code (for example, macro,

variable and function names)

Let us assume that the following structure exists:

struct MYRGBA

{

unsigned data;

};

Also there are several functions that are utilizing it:

void f1(const struct MYRGBA aaa)

{

}

long int f2(int b, const struct MYRGBA aaa)

{

return int();

}

long int f3(float b, const struct MYRGBA aaa, char c)

{

return int();

}

The analyzer produces three V801: "Decreased performance. It is better to redefine

the N function argument as a reference" messages concerning these functions. Such

a message will be a false one for the source code in question, as the compiler will

Page 115: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

optimize the code by itself, thus negating the issue. Of course it is possible to mark

every single message as a False Alarm using the "Mark As False Alarm" option. But

there is a better way. Adding this line into the sources will suffice:

//-V:MYRGBA:801

For C/C++ projects, we advise you to add such a line into .h file near the declaration

of the structure, but if this is somehow impossible (for example the structure is

located within the system file) you could add this line into the stdafx.h as well.

And then, every one of these V801 messages will be automatically marked as false

alarm after re-verification.

It's not only single words that the described mechanism of warning suppression can

be applied. That's why it may be very useful sometimes.

Let's examine a few examples:

//-V:<<:128

This comment will suppress the V128 warning in all the lines which contain the <<

operator.

buf << my_vector.size();

If you want the V128 warning to be suppressed only when writing data into the 'log'

object, you can use the following comment:

//-V:log<<:128

buf << my_vector.size(); // Warning untouched

log << my_vector.size(); // Warning suppressed

Note. Notice that the comment text string must not contain spaces.

Correct: //-V:log<<:128

Incorrect: //-V:log <<:128

When searching for the substring, spaces are ignored. But don't worry: a comment

like the following one will be treated correctly:

Page 116: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

//-V:ABC:501

AB C = x == x; // Warning untouched

AB y = ABC == ABC; // Warning suppressed

Complete warning disabling

Our analyzer allows the user to completely disable output of any warning through a

special comment. In this case, you should specify the number of the diagnostic you

want to turn off, after a double colon. The syntax pattern is as follows:

//-V::(number) - to disable one diagnostic

To disable a number of diagnostics, you can list their numbers separating them by

commas. The syntax pattern is the following:

//-V::(number1),(number2),...,(numberN) - to disable a number of diagnostics

To turn off all the diagnostics of C++ or C# analyzer use the following form:

//-V::C++ or //-V::C#

For example, if you want to ignore warning V122, you insert the following

comment in the beginning of a file:

//-V::122

If you want to disable warnings V502, V502, and V525, then the comment will look

like this:

//-V::502,507,525

Since the analyzer won't output the warnings you have specified, this might

significantly reduce the size of the analysis log when too many false positives are

generated for some diagnostic.

Changing an output message's text

This section doesn't refer to false positive suppression but may help sometimes to

get more precise messages.

Page 117: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

You can specify that one or more entities should be replaced with some other one(s)

in certain messages. This enables the analyzer to generate warnings taking into

account the project's specifics. The control comment has the following format:

//+Vnnn:RENAME:{Aaaa:Bbbb},{<foo.h>:<myfoo.h>},{100:200},......

In all the messages Vnnn, the following replacements will be done:

Aaaa will be replaced with Bbbb.

<foo.h> will be replaced with <myfoo.h>.

The number 100 will be replaced with 200.

The working principle of this mechanism is best to be explained by an example.

When coming across the number 3.1415 in code, the V624 diagnostic suggests

replacing it with M_PI from the <math.h> library. But suppose our project uses a

special math library and it is this library that we should use mathematical constants

from. So the programmer may add the following comment in a global file (for

example StdAfx.h):

//+V624:RENAME:{M_PI:OUR_PI},{<math.h>:"math/MMath.h"}

After that the analyzer will be warning that the OUR_PI constant from the header

file "math/MMath.h" should be used.

You can also extend messages generated by PVS-Studio. The control comment has

the following format:

//+Vnnn:ADD:{ Message}

The string specified by the programmer will be added to the end of every message

with the number Vnnn.

Take diagnostic V2003, for example. The message associated with it is: "V2003 -

Explicit conversion from 'float/double' type to signed integer type.". You can reflect

some specifics of the project in the message and extend it by adding the following

comment:

Page 118: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

//+V2003:ADD:{ Consider using boost::numeric_cast instead.}

From now on, the analyzer will be generating a modified message: "V2003 -

Explicit conversion from 'float/double' type to signed integer type. Consider using

boost::numeric_cast instead.".

Other means of filtering messages in the PVS-Studio analyzerThe analyzer also provides three more methods of error messages filtering.

First, you may disable diagnosis of some errors by their code. You may do this

using the "Settings: Detectable Errors" tab. On the tab of detected errors, you may

specify the numbers of errors that must not be shown in the analysis report.

Sometimes it is reasonable to remove errors with particular codes from the report.

For instance, if you are sure that errors related to explicit type conversion (codes

V201, V202, V203) are not relevant for your project, you may hide them.

Second, you may disable analysis of some project's parts (some folders or project

files). This is the "Settings: Don't Check Files" tab. On this tab, you may insert

information about libraries whose files' inclusions (through the #include directive)

must not be analyzed. This might be needed to reduce the number of unnecessary

diagnostic messages. Suppose your project employs the Boost library. Although the

analyzer generates diagnostic messages on some code from this library, you are sure

that it is rather safe and well written. So, perhaps there is no need to get warnings

concerning its code. In this case, you may disable analysis of the library's files by

specifying the path to it on the settings page. Besides, you may add file masks to

exclude some files from analysis. The analyzer will not check files meeting the

mask conditions. For instance, you may use this method to exclude autogenerated

files from analysis.

Path masks for files which are mentioned in the latest generated PVS-Studio report

in the output window could be appended to the 'Don't Check Files' list using the

"Don't check files and hide all messages from..." context menu command for the

currently selected message (Figure 8).

Page 119: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Figure 8 — Appending path masks through the context menu

This command allows the appending either of a single selected file or of the whole

directory mask containing such a file.

Third, you may suppress separate messages by their text. On the "Settings: Message

Suppression" tab, you may set filtering of errors by their text and not their code. If

necessary, you may hide error messages containing particular words or phrases in

the report. For instance, if the report contains errors that refer to the names of the

functions printf and scanf and you think that there cannot be any errors related to

them, you should simply add these two words using the editor of suppressed

messages.

Possible issuesThere might be some issues when using the "Mark as False Alarm" function.

Sometimes the analyzer "misses" the number of a line with an error. For instance,

the analyzer says that there is an error in line 57 while line 57 is empty at all. Then

it is the code, for instance, one line above (line 56) that causes the error.

The reason is that the code analyzer uses the preprocessor from Visual C++ that

experiences problems when dealing with multi-line macros (#define). These

problems were eliminated in Visual Studio 2005 Service Pack 1 and later versions.

Page 120: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Another issue of the preprocessor refers to multi-line #pragma-directives of a

particular type that also cause confusion with line numbering. Unfortunately, this

error has not been fixed in any version of Visual Studio yet.

So, markers arranged automatically might sometimes appear in false places. In this

case, the analyzer will again produce the same error warnings because it will fail to

find the markers. To solve this issue, you should mark messages you experience

troubles with manually. PVS-Studio always informs about such errors with the

message "V002. Some diagnostic messages may contain incorrect line number".

Like in case of any other procedure involving mass processing of files, you must

remember about possible access conflicts when marking messages as false alarms.

Since some files might be opened in an external editor and modified there during

file marking, the result of joint processing of such files cannot be predicted. That is

why we recommend you either to have copies of source code or use version control

systems.

Handling the diagnostic messages list

Navigation and sorting

Message filtering

Quick jumps to individual messages

Managing the Visual Studio Task List

While handling the large number of messages (and the first-time verification of

large-scale projects, when filters have not been set yet and false positives haven't

been marked, the number of generated messages can come close to tens of

thousands), it is reasonable to use the navigational, searching and filtering

mechanisms integrated into PVS-Studio output window.

Navigation and sorting

Page 121: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The main purpose of PVS-Studio output window is to simplify the analyzed

project's source code navigation and reviewing of potentially dangerous fragments

in it. Double-clicking any of the messages in the list will automatically open the file

corresponding to this message in the code editor and will place the cursor on the

desired line. The quick navigation buttons (see figure 1) allow for an easy review of

the potentially dangerous fragments in the source code without the need of constant

IDE windows switching.

Figure 1 — Quick navigation buttons

To present the analysis results, PVS-Studio output window utilizes a virtual grid,

which is capable of fast rendering and sorting of generated messages even for huge

large-scale projects (virtual grid allows you to handle a list containing hundreds of

thousands of messages without any considerable hits to performance). The far left

grid column can be used to mark messages you deem interesting, for instance the

ones you wish to review later. This column allows sorting as well, so it won't be a

problem to locate all the messages marked this way. The "Show columns" context

menu item can be used to configure the column display in the grid (figure 2):

Figure 2 — Configuring the output window grid

Page 122: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The grid supports multiline selection with standard Ctrl and Shift hotkeys, while the

line selection persists even after the grid is resorted on any column. The "Copy

selected messages to clipboard" context menu item (or Ctrl+C hotkey) allows you to

copy the contents of all selected lines to a system clipboard.

Message filteringPVS-Studio output window filtering mechanisms make it possible to quickly find

and display either a single diagnostic message or the whole groups of these

messages. The window's toolstrip contains several toggle buttons which can be used

to turn the display of their corresponding message groups on or off (figure 3).

Figure 3— message filtration groups

All of these switches could be subdivided into 3 sets: filters corresponding to the

message importance (levels 3, 2, 1 and fail, in the order of increased importance),

filters corresponding to type of message diagnostics rule set (64-bit, Optimization

and General Purpose), and filters corresponding to False Alarm markings within the

source code. Turning these filters off will momentarily hide all of their

corresponding messages inside the output list.

The quick filtering mechanism (quick filters) allows you to filter the analysis report

by the keywords that you can specify. The quick filtering panel could be opened

with the "Quick Filters" button on the output window's toolstrip (figure 4).

Figure 4— quick filtering panel

Page 123: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Quick filtering allows the display of messages according to the filters by 3

keywords: by the message's code, by the message's text and by the file containing

this message. For example, it is possible to display all the messages containing the

word 'odd' from the 'command.cpp' file. Changes to the output list are applied

momentarily after the keyword edit box loses focus. The 'Reset Filters' button will

erase all of the currently applied filtering keywords.

All of the filtering mechanisms described above could be combined together, for

example filtering the level of displayed messages and the file which should contain

them at the same time, while simultaneously excluding all the messages marked as

false positives.

Quick jumps to individual messagesIn case there is a need to navigate to an individual message in the grid, it is possible

to use the quick jumping dialog, which can be accessed through the "Navigate to

ID..." context menu item (figure 5):

Figure 5 - evoking of the quick jumping dialog

Page 124: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Figure 6 - Navigate to ID dialog

Each of the messages in PVS-Studio output list possesses a unique identifier — the

serial number under which this message was added into the grid, which itself is

displayed in the ID column. The quick navigation dialog allows you to select and

auto-focus the message with the designated ID, regardless of current grid's selection

and sorting. You also may note that the IDs of the messages contained within the

grid are not necessarily strictly sequential, as a fraction them could be hidden by the

filtering mechanism, so navigation to such messages is impossible.

Managing the Visual Studio Task ListThe large-scale projects are often developed by a distributed team, so a single

person isn't able to judge every message static analyzer generates for false-positives,

and even more so, is unable to correct the corresponding sections of the source

code. In this case it makes sense to delegate such messages to a developer who is

directly responsible for the code fragment in question.

PVS-Studio allows you to automatically generate the special TODO comment

containing all the information required to analyze the code fragment marked by it,

and to insert it into the source code. Such comment will immediately appear inside

the Visual Studio Task List window (in Visual Studio 2010 the comments' parsing

should be enabled in the settings: Tools->Options->Text Editor->C++->Formatting-

>Enumerate Comment Tasks->true) on condition that the ' Tools->Options-

>Environment->Task List->Tokens' list does contain the corresponding TODO

token (it is present there by default). The comment could be inserted using the 'Add

TODO comment for selected messages' command of the context menu (figure 7):

Page 125: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Figure 7 - inserting the TODO comment

The TODO comment will be inserted into the line which is responsible for

generation of analyzer's message and will contain the error's code, analyzer message

itself and a link to the online documentation for this type of error. Such a comment

could be easily located by anyone possessing an access to the sources thanks to the

Visual Studio Task List. And with the help of the comment's text itself the potential

issue could be detected and corrected even by the developer who does not have

PVS-Studio installed or does not possess the analyzer's report for the full project

(figure 8).

Figure 8 —Visual Studio Task List

The Task List Window could be accessed through the View->Other Windows-

>Task List menu. The TODO comments are displayed in the 'Comments' section of

the window.

Page 126: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Analyzing Visual C++ (.vcxproj) and Visual C# (.csproj) projects from the command line

Starting analysis on sln and csproj/vcxproj files

Command line tool exit codes

Running analysis from the command line for C/C++ projects built in build systems other than Visual Studio's

How PVS-Studio settings affect the command line launch; analysis results (plog file) filtering and conversion

Regular use of PVS-Studio and integration with "daily builds"

Automatic update and installation of the PVS-Studio distribution

Conclusion

In addition to using PVS-Studio directly from Visual Studio, you can also run

analysis of MSBuild (i.e. Visual C++ and Visual C#)) projects from the command

line. This can be useful for setting up regular automatic analyzer runs. For example,

during the "night builds" on the build server.

Starting analysis on sln and csproj/vcxproj filesTo analyze C++/C# projects or solutions (sln) files that contain such projects, you

can use command line module of the PVS-Studio analyzer directly without having

to start the IDE (devenv.exe) process and open in it projects that you are intending

to check.

The PVS-Studio_Cmd.exe tool can be found in PVS-Studio installation directory

(default path is ' c:\Program Files (x86)\PVS-Studio\ ').The '--help' command

displays all available arguments of cmd analyzer:

PVS-Studio_Cmd.exe --help

The main arguments of the analyzer:

Page 127: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

--target: required parameter. Allows you to specify the target for analysis (sln or csproj/vcxproj file);

--output: path to the plog file, where the analysis results will be written. If this parameter is missing, a plog file will be created next to the file indicated in the target;

--platform and --configuration: platform and configuration of the target which will be analyzed. If these parameters are not specified, then the first available "platform|configuration" pair will be chosen (when checking the sln file) or "Debug|AnyCPU" (when analyzing a single .csproj project);

--settings: path to the configuration file of PVS-Studio. If the parameter is missing, IDE settings of PVS-Studio plug-in will be used, located in the folder "c:\Users\%UserName%\AppData\Roaming\PVS-Studio\Settings.xml". Note that for the C# analyzer to work properly, your settings, passed through this flag, should contain your registration information (if you are using the default settings from the AppData, your registration information can be entered via the PVS-Studio plug-in in Visual Studio);

--progress: allows to enable the detailed logging of the analysis progress to StdOut (disabled by the default)

--suppressAll: append all un-suppressed messages to the 'suppress' files of corresponding projects (disabled by default). If this marker is set, all the diagnostic messages will be added into the database of suppressed messages after saving the analysis log file. Using this mode, you will only see messages generated for newly written/changed code at each analyzer run; that is, new messages will be written into the new log and get immediately suppressed so that you don't see them at the next run. However, if you need to view old messages (without having to re-run the analysis on the project), you can view them anytime by opening the complete-log file which is automatically saved in the same folder with the new-messages log file. To learn more about the message suppression mode, see this documentation section.

--incremental: enable the incremental analysis mode. The following operating modes of incremental analysis are available:

o Scan – scan dependencies to determine source files for incremental analysis. Note that at this step the incremental analysis will not be started. Perform this step before building the target.

o Analyze – run the incremental analysis on the target. This step should be executed after executing the Scan step, and can be executed both before and after building the target. PVS-Studio will analyze only those files that were modified since the last build.

o ScanAndAnalyze – scan dependencies to determine source files for incremental analysis and analyze the files that were modified since last build.

Page 128: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Refer to the PVS-Studio's incremental analysis mode topic for more details about

incremental analysis in PVS-Studio.

--msBuildProperties: set or override the specified project-level properties. Use a vertical bar (|) to separate multiple project-level properties: --msBuildProperties WarningLevel=2|OutDir=bin\OUT32\

Here is an example of running the analysis for Visual Studio solution "My

Solution":

PVS-Studio_Cmd.exe --target "mysolution.sln" --platform "Any CPU"

--configuration "Release" --output "mylog.plog"

--settings "pvs.xml" --progress true

The command line version of the PVS-Studio analyzer supports all settings on

filtering/disabling messages available in the IDE plugin for Visual Studio. You can

either set them manually in the xml file, that is passed through the --settings

argument, or use the settings specified through the UI plugin, without passing this

argument. Note that the IDE plug-in of PVS-Studio uses an individual set of

settings for each user in the system.

Command line tool exit codesThe PVS-Studio_Cmd utility defines several non-zero exit codes, which do not

necessarily indicate some issue with the operation of the tool itself, i.e. even when

the tool returns something other when zero it does not always mean that the tool has

'crashed'. The exit code is a bit mask that represents all states that occurred during

the PVS-Studio_Cmd utility operation. For example, the tool will return non-zero

code (it will be 256 actually) when the analyzer finds some potential issues in the

code being analyzed. Such behavior allows you to handle this situation individually,

for example, when the policy of using the analyzer on a build server does not allow

analyzer issues to be present in the code that was committed to a revision control

system. Consider another example: during analysis there were found some issues in

code, and one of the source files is missing on disk. In this case the exit code of the

PVS-Studio_Cmd utility will be 264 (8 - some of the analyzed source files or

project files were not found, 256 - some issues were found in the source code), or,

in binary representation 100001000.

Page 129: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Next, let's examine all possible PVS-Studio_Cmd state codes that form a bit mask

exit code.

'0' - analysis was successfully completed, no issues were found in the source code

'1' - error (crash) during analysis of some source file(s)

'2' - general (nonspecific) error in the analyzer's operation, a possible handled exception

'4' - some of the command line arguments passed to the tool were incorrect

'8' - some of the analyzed source files or project files were not found

'16' - specified configuration and (or) platform were not found in a solution file

'32' - solution file is not supported

'64' - incorrect extension of analyzed project

'128' - incorrect or out-of-date analyzer license

'256' - some issues were found in the source code

Let us provide an example of a Windows batch script that decodes the PVS-

Studio_Cmd utility exit code:

@echo off

"C:\Program Files (x86)\PVS-Studio\PVS-Studio_Cmd.exe"

-t "YourSolution.sln" -o "YourSolution.plog"

set /A FilesFail = "(%errorlevel% & 1) / 1"

set /A GeneralExeption = "(%errorlevel% & 2) / 2"

set /A IncorrectArguments = "(%errorlevel% & 4) / 4"

set /A FileNotFound = "(%errorlevel% & 8) / 8"

set /A IncorrectCfg = "(%errorlevel% & 16) / 16"

set /A InvalidSolution = "(%errorlevel% & 32) / 32"

set /A IncorrectExtension = "(%errorlevel% & 64) / 64"

set /A IncorrectLicense = "(%errorlevel% & 128) / 128"

set /A AnalysisDiff = "(%errorlevel% & 256) / 256"

if %FilesFail% == 1 echo FilesFail

Page 130: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

if %GeneralExeption% == 1 echo GeneralExeption

if %IncorrectArguments% == 1 echo IncorrectArguments

if %FileNotFound% == 1 echo FileNotFound

if %IncorrectCfg% == 1 echo IncorrectConfiguration

if %InvalidSolution% == 1 echo IncorrectCfg

if %IncorrectExtension% == 1 echo IncorrectExtension

if %IncorrectLicense% == 1 echo IncorrectLicense

if %AnalysisDiff% == 1 echo AnalysisDiff

Running analysis from the command line for C/C++ projects built in build systems other than Visual Studio'sIf your C/C++ project doesn't use Visual Studio's standard build system (MSBuild)

or even uses a custom build system \ make files through Visual Studio NMake

projects, you won't be able to analyze this project with the PVS-Studio_Cmd.

If this is the case, you can use the compiler monitoring system that allows you to

analyze projects regardless of the build systems they use by "intercepting"

compilation process invocations (the supported compilers are CL, MinGW GCC,

and Clang). The compilation monitoring system can be used both from the

command line and through the UI of the Standalone utility.

You can also integrate the invocation of command line analyzer kernel directly into

your build system. Notice that this will require you to define the PVS-Studio.exe

analyzer kernel call for each file to be compiled, just like in the case when calling

the C++ compiler.

How PVS-Studio settings affect the command line launch; analysis results (plog file) filtering and conversionWhen running project analysis from the command line, by default, the same settings

are used as in the case when it is run from the IDE (Visual Studio). The number of

processor cores to be used for analysis depends on the number specified in the

Page 131: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

analyzer settings. You can also specify the settings file directly with '--settings'

argument, as was described above.

As for the filtering systems (Keyword Message Filtering and Detectable Errors),

they will not be used when running the analysis from the command line. It means

you'll see all the messages in the log file anyway, regardless of the parameters you

have specified. However, when loading the log file in the IDE, the filters will be

applied. This is because filters are applied dynamically to the results (including the

case when you analyze your project from the IDE as well). It's very convenient

since you may want to switch off some of the messages you've got (for example

V201). You only need to disable them in the settings to have the corresponding

messages disappear from the list WITHOUT you having to rescan the project.

The plog file format (XML) is not intended for directly displaying to or read by a

human. However, if you need to filter off the analysis results and convert them into

a "readable" format, you can use the PlogConverter utility available within the PVS-

Studio distribution.

PlogConverter allows you to convert one or more plog files into the following

formats:

Light-weight text file with analysis results. It can be useful when you need to output the analysis results (for example new messages) into the log of the build system or continuous integration server.

HTML file with hyperlinks to the source files. A file in this format is convenient to send via e-mail to all the developers involved.

CSV table with analysis results.

Text file with a summary table of the number of messages across different levels and diagnostic rule sets.

The format of the log file is defined by the command line parameters. You can also

use them to filter the results by rule sets, levels, and individual error codes.

Here's an example of the plog converter command line launch (in one line):

PlogConverter.exe test1.plog test2.plog -o "C:\Results" -r "C:\Test"

-a GA:1,2,3;64:1 -t Html,Txt,Totals -d V101,V105,V122

Page 132: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The plog converter will be launched for the test1.plog and test2.plog files in the

current folder and the error messages from all specified plog files will be merged

and saved into the Results folder on disk C; only messages from the General

Analysis (GA) rule set of levels 1, 2, and 3, and from the 64-bit diagnostic rule set

(64) of level 1 will be used. Diagnostics V101, V105, and V122 will be filtered off

from the list. The results will be saved into a text and HTML files as well as a

summary text file for all the diagnostics (with the mentioned parameters taken into

account). The original plog file will remain unchanged.

A detailed help on all the parameters of the PlogConverter utility can be accessed

by executing the following command:

PlogConverter.exe --help

If you need to convert the plog file into some specific format, you can do it by

parsing the file as an XML document yourself. Notice that the PlogConverter utility

comes with the source files (in C#) which can be found in the PlogConverter_src.zip

archive. You can alter the parsing algorithm of plog file structure from our utility to

create an analysis results output format of your own.

Regular use of PVS-Studio and integration with "daily builds"The process of long-term use of the PVS-Studio code analyzer is comprised of two

stages: integration and regular use. When integrating PVS-Studio into an existing

large-scale project, programmers will usually read the analyzer messages and either

fix the code or mark it using the "Mark as False Alarm" and "Message Suppression"

features. Having sorted out all the messages, they will then re-analyze the code once

again to get 0 warnings (given that the messages marked as false positives or

suppressed through suppress files are set to stay hidden). It means that the

integration stage is over and the stage of regular use sets in.

From this point on, all the new code added into the project will be analyzed by

PVS-Studio. Actually, the ENTIRE code base will be analyzed, but you'll be seeing

Page 133: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

only new messages. Errors will be found in freshly written/fixed code or old code

that has been left unmarked.

The option of running the analysis from the command line is useful when

developers need to launch it regularly (daily, for instance). The procedure looks as

follows:

Running the analysis from the command line.

Sending the resulting log file to all the developers involved.

This way, regularly running PVS-Studio on your project will help you avoid new

bugs in the code.

Automatic update and installation of the PVS-Studio distributionTo install the PVS-Studio distribution from the command line in a "quiet" mode

(i.e. without displaying the UI and dialog boxes), you need to pass the following

parameters into it (in one line):

PVS-Studio_setup.exe /VERYSILENT /SUPPRESSMSGBOXES

/COMPONENTS=Core,Standalone,MSVS,MSVS\2010,

MSVS\2012,MSVS\2013,MSVS\2015

The /COMPONENTS argument allows specifying the components to be installed

(Standalone utility, plugins for different IDEs) and is optional.

Keep in mind that it is required to avoid running Visual Studio (the devenv.exe

process) while installing PVS-Studio.

The PVS-Studio-Updater.exe utility can check for analyzer updates and download

and install them on a local machine. To run the update utility in the "quiet" mode,

you can use the same parameters as for the distribution installation process:

PVS-Studio-Updater.exe /VERYSILENT /SUPPRESSMSGBOXES

Page 134: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

If there are no updates available on the server, the utility will terminate with '0' exit

code. Since PVS-Studio-Updater.exe performs local installation, you must avoid

running the devenv.exe process while it is working as well.

ConclusionRunning PVS-Studio daily from the command line can help you significantly

increase the quality of your code. Sticking with this approach, you will get 0

diagnostic messages every day if your code is correct. And should there be any

incorrect edits of the old (already scanned and fixed) code, the next run of PVS-

Studio will reveal any fresh defects. Newly written code will be regularly scanned

in automatic mode (i.e. independently of the programmer).

Direct integration of the analyzer into build automation systems (C/C++)

Abstract

PVS-Studio analyzer independent mode

An example of using the analyzer independent mode with Makefile project

Specifics of using PVS-Studio while launching from command line

Incremental analysis in independent command line mode

Using Microsoft IntelliSense with analyzer in independent mode

Differences in behavior of PVS-Studio.exe console version while processing one file or several files at once

Conclusion

AbstractWe recommend utilizing PVS-Studio analyzer through the Microsoft Visual Studio

development environments, into which the tool is perfectly integrated. But

sometimes you can face situations when command line launch is required, for

instance in case of the cross-platform build system based on makefiles.

Page 135: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

In case you possess project (.vcproj/.vcxproj) and solution (.sln) files, and command

line execution is required for the sake of daily code checks, for instance, we advise

you to examine the article "Analyzing MSBuild projects from the command line".

PVS-Studio analyzer independent modeSo, how does a code analyzer work (be it PVS-Studio or any other tool)?

When the analyzer user gives a command to check some file (for example, file.cpp),

the analyzer performs preprocessing of this file at first. As a result, all the macros

are defined and #include-files are arranged.

The preprocessed i-file can now be parsed by the code analyzer. Pay attention that

the analyzer cannot parse a file which has not been preprocessed, for it won't have

information about the types, functions and classes being used. Operation of any

code analyzer includes at least two steps: preprocessing and analysis itself.

It is possible that C++ sources do not have project files associated with them, for

example it is possible in case of multiplatform software or old projects which are

built using command line batch utilities. Various Make systems are often employed

to control building process in such cases, Microsoft NMake or GNU Make for

instance.

To analyze such projects it is necessary to embed the direct call for the analyzer

(the 'programfiles%\PVS-Studio\x64\PVS-Studio.exe' file) into building

process and to pass all arguments required for preprocessing to it. In fact the

analyzer should be called for the same files for which the compiler (cl.exe in case of

Visual C++) is being called.

The PVS-Studio analyzer should be called in batch mode for each C/C++ file or for

a whole group of files (files with c/cpp/cxx etc. extensions, the analyzer shouldn't be

called for header h files) with the following arguments:

PVS-Studio.exe --cl-params %ClArgs%

--source-file %cppFile% --cfg %cfgPath% --output-file %ExtFilePath%

Page 136: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

%ClArgs% — arguments which are passed to cl.exe compiler during regular

compilation, including the path to source file (or files).

%cppFile% — path to analyzed C/C++ file or paths to a collection of C/C++ files

(the filenames should be separated by spaces)

%ClArgs% and %cppFile% parameters should be passed to PVS-Studio analyzer

inthe same way in which they are passed to the compiler, i.e. the full path to the

source file should be passed twice, in each param.

%cfgPath% — path to PVS-Studio.cfg configuration file. This file is shared

between all C/C++ files and can be created manually (the example will be presented

below)

%ExtFilePath% — optional argument, a path to the external file in which the

results of analyzer's work will be stored. In case this argument is missing, the

analyzer will output the error messages into stdout. The results generated here can

be viewed in Visual Studio's 'PVS-Studio' toolwindow using 'PVS-Studio/Load

Analysis Report' menu command (selecting 'Unparsed output' as a file type). Please

note, that starting from PVS-Studio version 4.52, the analyzer supports multi-

process (PVS-Studio.exe) output into a single file (specified through --output-file)

in command line independent mode. This allows several analyzer processes to be

launched simultaneously during the compilation performed by a makefile based

system. The output file will not be rewritten and lost, as file blocking mechanism

had been utilized.

Consider this example for starting the analyzer in independent mode for a single

file, utilizing the Visual C++ preprocessor (cl.exe):

PVS-Studio.exe --cl-params "C:\Test\test.cpp" /D"WIN32" /I"C:/Test/"

--source-file "C:\Test\test.cpp" --cfg "C:\Test\PVS-Studio.cfg"

--output-file "C:\Test\test.log"

The PVS-Studio.cfg (the --cfg parameter) configuration file should include the

following lines:

Page 137: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

exclude-path = C:\Program Files (x86)\Microsoft Visual Studio 10.0

vcinstalldir = C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\

platform = Win32

preprocessor = visualcpp

language = C++

Let's review these parameters:

The exclude-path parameter contains the directories for which the analysis will not be performed. If the Visual Studio directory is not included here, the analyzer will generate error messages for its' header .h-files. But you of course cannot modify them. Therefore we recommend you to always add this path to the exclusions. It is also possible to set multiple exclude-path parameters.

The vcinstalldir parameter indicates the directory in which the utilized preprocessor is located. The supported preprocessors are: Microsoft Visual C++ (cl.exe), Clang(clang.exe) and MinGW (gcc.exe).

The platform parameter points to the correct version of the compiler — Win32, x64, Itanium or ARMV4. It is usually Win32 or x64.

The preprocessor parameter indicates which preprocessor should be located at vcinstalldir. Supported values are: visualcpp, clang, gcc. Generally, one should select the preprocessor according to the compiler being used by the build automation system in question.

The 'language' parameter determines the version of C/C++ language within the code of the file being verified (--source-file) which is expected by the analyzer during its' parsing process. Possible values are: C, C++, C++CX, C++CLI. As each of the supported language variants does contain specific key words, the incorrect assignment of this parameter could potentially lead to the V001 parsing error messages.

You can filter diagnostics messages generated by analyzer using analyzer-errors and

analysis-mode parameters (set them in cfg file of pass through command line).

These parameters are optional.

The analyzers-errors parameter allows you to set the codes for errors in which you are interested. For example: analyzer-errors=V112 V111. We do not recommend setting this parameter.

The analysis-mode parameter allows you to control the analyzers being used. Values: 0 - full analysis (by default), 1 - only 64 bit analysis, 4 - only general-purpose analysis, 8 - only optimization analysis. The recommended value is 4.

Page 138: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The full list of command line switches will be displayed with this argument:

PVS-Studio.exe --help

An example of using the analyzer independent mode with Makefile projectFor example let's take the Makefile project which is build using VisualC++

compiler and it is declared in the project's makefile like this:

$(CC) $(CFLAGS) $<

The $(CC) macro calls cl.exe, the compilation parameters $(CFLAGS) are passed to

it and finally all C/C++ files on which the current build target is dependent are

inserted using the $< macro. Thereby the cl.exe compiler will be called with

required compilation parameters for all source files.

Let's modify this script in such a way that every file is analyzed with PVS-Studio

before the compiler is called:

$(PVS) --source-file $< --cl-params $(CFLAGS) $<

--cfg "C:\CPP\PVS-Studio.cfg"

$(CC) $(CFLAGS) $<

$(PVS) - path to analyzer's executable (%programfiles%\PVS-Studio\x64\PVS-

Studio.exe). Take into account that the Visual C++ compiler is being called after the

analyzer on the next line with the same arguments as before. This is done to allow

for all targets to be built correctly so the build would not stop because of the lack

of .obj-files.

Specifics of using PVS-Studio while launching from command linePVS-Studio tool has been developed to work within the framework of Visual Studio

environment. And launching it from the command line is the function that is

additional to the main working mode. However, all of analyzer's diagnostic

capabilities are available.

Page 139: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Also, when launching from the command line, the mechanisms of error-warning

filtration are not available (see: diagnosable errors and suppression of separate

warnings).

However the error messages generated in this mode could be easily redirected into

the external file with the help of --output-file command line switch. This file will

contain the unprocessed and unfiltered analyzer output.

Such a file could be viewed in PVS-Studio IDE toolwindow using 'Load Analysis

Report' menu command (selecting 'Unparsed output' as a file type) and afterwards it

could be saved in a standard PVS-Studio log file (plog) format. This allows you to

avoid the duplication of error messages and also to use all of the standard filtering

mechanisms for them.

Incremental analysis in independent command line modeThe users who are familiar with PVS-Studio incremental analysis mode within the

IDE naturally will miss this feature in the command line mode. But fortunately,

almost any build system could provide an incremental analysis just "out of the box",

because by invoking "make" we recompile only file which were modified. So the

incremental analysis will be automatically provided by using the independent

command line version.

Using Microsoft IntelliSense with analyzer in independent modeAlthough it is possible to open the unfiltered text file containing analyzer diagnostic

messages from within the IDE into PVS-Studio Output window (which itself will

allow you to use file navigation and filtering mechanisms), you will only be able to

use the code text editor inside the Visual Studio itself, as the additional IntelliSense

functionality will be unavailable (that is, autocompletion, type declarations and

function navigation, etc.). And all this is quite inconvenient, especially while you

are handling the analysis results, even more so with the large projects, forcing you

Page 140: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

to search class and method declarations manually. As a result the time for handling

a single diagnostic message will be greatly increased.

To solve this issue, you need to create an empty Visual C++ project (Makefile

based one for instance) in the same directory with C++ files being verified by the

analyzer (vcproj/vcxproj file should be created in the root folder which is above

every file verified). After creating an empty project you should enable the 'Show All

Files' mode for it (the button is in the upper part of the Solution Explorer window),

which will display all the underlying files in the Solution Explorer tree view. Then

you will be able to use the 'Include in Project' context menu command to add all the

necessary c, cpp and h files into your project (You will also probably have to add

include directory paths for some files, for instance the ones containing third-party

library includes). If including only a fraction of the files verified, you also should

remember that IntelliSense possibly will not recognize some of the types from

within these files, as these types could be defined right in the missing files which

were not included by you.

Page 141: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Figure 1 — including files into the project

The project file we created could not be used to build or verify the sources with

PVS-Studio, but still it will substantially simplify handling of the analysis results.

Such a project could also be saved and then used later with the next iteration of

analyzer diagnostics results in independent mode.

Differences in behavior of PVS-Studio.exe console version while processing one file or several files at once

Page 142: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The cl.exe compiler is able to process source files as either one at a time or as a

whole group of files at once. In the first case the compiler is called several times for

each file:

cl.exe ... file1.cpp

cl.exe ... file2.cpp

cl.exe ... file2.cpp

In the second case it is called just once:

cl.exe ... file1.cpp file2.cpp file3.cpp

Both of these modes are supported by the PVS-Studio.exe console version as

demonstrated above in the examples.

It could be helpful for a user to understand the analyzer's logics behind theses two

modes. If launched individually, PVS-Studio.exe will firstly invoke the

preprocessor for each file and the preprocessed file will be analyzed after it. But

when processing several files at once, PVS-Studio.exe will firstly preprocess all

these files and then separate instances of PVS-Studio.exe will be invoked

individually for each one of the resulting preprocessed files.

ConclusionAlthough the abilities included into PVS-Studio are enough to use the tool from the

command line, of course it can be greatly improved. We are ready to improve the

mechanism of launching PVS-Studio from the command line to the level allowing

making it convenient for you to use the tool in your particular project. Please refer

to our customer support.

Direct integration of PVS-Studio into MSBuild's build process. MSBuild integration mode in Visual Studio IDE

Page 143: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The direct integration of analyzer into MSBuild scenarios (projects) is obsolete. Command line analysis of projects with PVS-Studio is described in the following section.

Using PVS-Studio with continuous integration systems

Abstract

Introduction

Using the "Mark As False Alarm" function during testing automation

Integrating PVS-Studio into build automation process

An example of integrating PVS-Studio plug-in for Visual Studio with CruiseControl .NET

Integrating with Hudson

AbstractThis article illustrates techniques required to employ the use of PVS-Studio static

code analyzer together with continuous integration systems. Also provided are

configuration guidelines for PVS-Studio launching modes.

IntroductionContinuous integration (CI) is the practice of software development implying

frequent building and subsequent testing for the most recent versions of designed

application. Generally, continuous integration systems interact directly with

revision control and allow for a significant increase in integration process's

reliability while decreasing its laboriousness by automating entire building-testing

phase.

For the purpose of deployment within continuous integration, PVS-Studio can

launch the analysis for its target project in "silent" mode from command line shell.

As such, integrating PVS-Studio into periodic build process allows for an effective

utilization of analysis's results during collaborate project development process.

Page 144: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Using the "Mark As False Alarm" function during testing automationWhile launching from command line shell, PVS-Studio will always analyze the

whole target project. The reason for such an approach is that modifications in a

single header file can affect multiple cpp files, thus checking only modified files

will not be sufficient.

Keep in mind that static analysis will always generate a lot of so called "false

alarms", so to suppress that "noise" it is imperative to employ "Mark as False

Alarm" functionality while using PVS-Studio during regular source code

verification. Thus PVS-Studio operation can be subdivided into 2 steps: integration

and exploitation. During the integration stage developer should review all generated

error messages and either correct corresponding code or mark it as false alarm.

Afterwards, the analyzer would generate error messages only for newly added code.

Integrating PVS-Studio into build automation processIn the light of PVS-Studio being an extension to Microsoft Visual Studio IDEs, it is

possible to integrate the analyzer into any CI system capable of executing builds for

these IDEs via command line commands with arguments. Also, direct support for

IDE building process is not required from the CI system, the ability to pass

commands to operating system's shell and process stdout stream will suffice.

Launching the analyzer from command line is described in detail in this separate

article.

The resulting XML-log file can be loaded into IDE (by double-clicking it directly

from file manager or through PVS-Studio/Load Analysis Report menu item) and

used to navigate through errors in analyzed source code. As the log file itself is not

conveniently formatted to be reviewed manually, analyzer will also create the plain

text file containing a list of all errors (not marked as false alarms). This file is

intuitive and can be conveniently included into continuous integration system's

Page 145: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

general logs, which in turn can be published, for example, by e-mail to all

concerned developers.

In case the verification of all the files from all projects is not an appropriate option,

PVS-Studio is able to analyze only the files which were modified during the

predefined time interval. This time interval can be defined at the

SpecificAnalyzerSettings option page. Alternatively one can also explicitly specify

the files for analysis using the batch mode.

An example of integrating PVS-Studio plug-in for Visual Studio with CruiseControl .NETPVS-Studio integration with CruiseControl.NET can be achieved by addition of

"Executable" task into build project. This task will launch cmd.exe Windows shell

and pass the required build arguments into it. Below is the fragment of XML server

configuration file, which contains such a task.

<exec>

<description>PVS-Studio check solution example</description>

<executable>&CMD_PATH;</executable>

<baseDirectory>&DEVENV_PATH;<baseDirectory>

<buildArgs>

<item>

/c "c:\Program Files (x86)\PVS-Studio\PVS-Studio_Cmd.exe"

-t "C:\sample\VSSol.sln"

</item>

</buildArgs>

</exec>

The XML analysis log (plog) could be converted to plain text or html formats using

PlogCOnverter unility. These analysis results then can be published by any

available publisher tasks, for example by being moved into common builder

directory or by e-mailing build server logs by any third-party tool. The inclusion of

these results into general builder logs can be performed by such Executable task:

<exec>

Page 146: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

<description>PVS-Studio load error log</description>

<executable>&CMD_PATH;</executable>

<baseDirectory>&PROJECT_ROOT;</baseDirectory>

<buildArgs>

<item>

/c type result-log.plog.txt

</item>

</buildArgs>

<buildTimeoutSeconds>0</buildTimeoutSeconds>

</exec>

Integrating with HudsonIntegrating with Hudson can be performed by adding "execute Windows shell

command" build step into build project (Job). An example of the command to be

added, as follows:

"c:\Program Files (x86)\PVS-Studio\PVS-Studio_Cmd.exe"

-t "C:\sample\VSSol.sln" -o vssol.plog

After that, the resulting XML log file can be converted with PlogConverter:

c:\Program Files (x86)\PVS-Studio\PlogConverter.exe

vssol.plog

Text file holding the list of generated errors (not marked as false alarms) can be

included into build server general log by addition of another command:

type vssol.plog.txt

Afterwards the resulting build log can be published with the help of any available

Hudson publisher tools. For example, to publish results by e-mail, you can use ext-

mail plug-in, adding $BUILD_LOG token into message body:

${BUILD_LOG, maxLines=5000}

Predefined PVS_STUDIO macro

Page 147: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Among the numerous filtration and message suppression methods of PVS-Studio

analyzer is the PVS_STUDIO predefined macro. Its purpose is to prevent the

marked source code from being analyzed. For example, let the analyzer produce the

message for the following code:

int rawArray[5];

rawArray[-1] = 0;

However, if you will 'wrap' it using this macro the message will not be generated.

int rawArray[5];

#ifndef PVS_STUDIO

rawArray[-1] = 0;

#endif

The PVS_STUDIO macro is automatically inserted while checking the code from

the IDE. But if you are using PVS-Studio from the command line, the macro will

not be passed by default to the analyzer and this should be done manually.

PVS-Studio: Troubleshooting

The basic PVS-Studio's operation principles you should know

I can't check a file/project with the IDE PVS-Studio plugin

Source files are preprocessed incorrectly when running analysis from the IDE plugin. Error V008

IDE plugin crashes and generates the 'PVS-Studio internal error' message

Unhandled IDE crash when utilizing PVS-Studio

PVS-Studio.exe crash

The V001/V003 errors

The analyzer cannot locate errors in an incorrect code or generates too many false positives

Issues with handling PVS-Studio analysis report from within the IDE plugin

Code analysis running from the IDE plugin is slow. Not all the logical processors are being utilized

I get the message "Files with C or C++ source code for analysis not found." when checking a group of projects or one C/C++ project

Page 148: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

I cannot install the IDE PVS-Studio plugin for the Visual Studio Express Edition

Errors of the "Cannot open include file", "use the /MD switch for _AFXDLL builds" kinds on projects that could be successfully compiled in Visual Studio. Insertion of incorrect precompiled headers during preprocessing

'PVS-Studio is unable to continue due to IDE being busy' message under Windows 8. 'Library not registered' errors

The basic PVS-Studio's operation principles you should knowPVS-Studio is composed of 2 basic components: the command-line analyzer (PVS-

Studio.exe) and an IDE plugin through which the former is integrated into one of

the supported development environments (Microsoft Visual Studio). The way

command-line analyzer operates is quite similar to that of a compiler, that is, each

file being analyzed is assigned to a separate analyzer instance that, in turn, is called

with parameters which, in particular, include the original compilation arguments of

the source file itself. Afterwards, the analyzer invokes a required preprocessor (also

in accordance with the one that is used to compile the file being analyzed) and then

analyzes the resulting temporary preprocessed file, i.e. the file in which all of the

include and define directives were expanded.

Thus, the command-line analyzer - just like a compiler (for example Visual C++

cl.exe compiler) - is not designed to be used directly by the end user. To continue

with the analogy, compilers in most cases are employed indirectly, through a special

build system. Such a build system prepares launch parameters for each of the file to

be built and also usually optimizes the building process by parallelizing it among all

the available logic processors. The IDE PVS-Studio plugin operates in a similar

fashion.

However, IDE plug-in is not the only method for the employment of PVS-

Studio.exe command line analyzer. As mentioned above, the command-line

analyzer is very similar to a compiler regarding its usage principles. Therefore,

it can be directly integrated, if necessary, into a build system along with a compiler.

This way of using the tool may be convenient when dealing with a build scenario

which is not supported by PVS-Studio - for example, when utilizing a custom-made

Page 149: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

build system or an IDE other than Visual Studio. Note that PVS-Studio.exe supports

analysis of source files intended to be compiled with gcc, clang, bcc32, and cl

compilers (including the support for specific keywords and constructs).

For instance, if you build your project in the Eclipse IDE with gcc, you can

integrate PVS-Studio into your makefile build scripts. The only restriction is that

PVS-Studio.exe can only operate under Windows NT operating systems family.

Besides IDE plugins, our distribution kit also includes a plugin for the Microsoft

MSBuild build system which is utilized by Visual C++ projects in the Visual Studio

IDE starting with version 2010. Don't confuse it with the plugin for the Visual

Studio IDE itself!

Thus, you can analyze projects in Visual Studio (version 2010 or higher) in two

different ways: either directly through our IDE plugin, or by integrating the analysis

process into the build system (through the plugin for MSBuild). Of course, nothing

prevents you, if the need arises, from creating your own static analysis plugin, be it

for MSBuild or any other build system, or even integrating PVS-Studio.exe's call

directly, if possible, into build scripts like in the case of makefile-based ones.

I can't check a file/project with the IDE PVS-Studio pluginIf PVS-Studio plug-in generates the message "C/C++ source code was not found"

for your file, make sure that the file you are trying to analyze is included into the

project for the build (PVS-Studio ignores files excluded from the build). If you get

this message on the whole project, make sure that the type of your C/C++ project is

supported by the analyzer. In Visual Studio, PVS-Studio supports only Visual C++

projects for versions 2005 and higher, as well as their corresponding MSBuild

Platform Toolsets. Project extensions using other compilers (for example projects

for the C++ compiler by Intel) or build parameters (Windows DDK drivers) are not

supported. Despite the fact that the command-line analyzer PVS-Studio.exe in itself

supports analysis of the source code intended for the gcc/clang compilers, IDE

project extensions utilizing these compilers are not supported. As an alternative, in

Page 150: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

case of Visual Studio IDE, you may try direct integration into the MSBuild build

system.

If your case is not covered by the ones described above, please contact our support

service. If it is possible, please send us the temporary configuration files for the files

you are having troubles with. You can get them by setting the option 'PVS-Studio -

> Options -> Common Analyzer Settings -> Remove Intermediate Files' to 'False'.

After that, the files with the name pattern %SourceFilename.cpp%.PVS-Studio.cfg

will appear in the same directory where your project file (.vcxproj) is located. If

possible, create an empty test project reproducing your issue and send it to us as

well.

Source files are preprocessed incorrectly when running analysis from the IDE plugin. Error V008If, having checked your file/project, PVS-Studio generates the V008 message

and/or a preprocessor error message (by clang/cl/bcc32 preprocessors) in the results

window, make sure that the file(s) you are trying to analyze can be compiled

without errors. PVS-Studio requires compilable C/C++ source files to be able to

operate properly, while linking errors do not matter.

The V008 error means that preprocessor returned a non-zero exit code after

finishing its work. The V008 message is usually accompanied by a message

generated by a preprocessor itself describing the reason for the error (for example, it

failed to find an include file). Note that, for the purpose of optimization, our Visual

Studio IDE plugin utilizes a special dual-preprocessing mode: it will first try to

preprocess the file with the faster clang preprocessor and then, in case of a failure

(clang doesn't support certain Visual C++ specific constructs), launches the standard

cl.exe preprocessor. If you get clang's preprocessing errors, try setting the plugin to

use only the cl.exe preprocessor (PVS-Studio -> Options -> Common Analyzer

Settings -> Preprocessor).

If you are sure that your files can be correctly built by the IDE/build system,

perhaps the reason for the issue is that some compilation parameters are incorrectly

Page 151: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

passed into the PVS-Studio.exe analyzer. In this case, please contact our support

service and send us the temporary configuration files for these files. You can get

them by setting the option 'PVS-Studio -> Options -> Common Analyzer Settings -

> Remove Intermediate Files' to 'False'. After that, files with the name pattern

%SourceFilename.cpp%.PVS-Studio.cfg will appear in the same directory where

your project file is located. If possible, create an empty test project reproducing

your issue and send it to us as well.

IDE plugin crashes and generates the 'PVS-Studio internal error' messageIf plugin crashes and generates the dialog box entitled 'PVS-Studio Internal Error',

please contact our support service and send us the analyzer's crash stack (you can

obtain it from the crash dialog box).

If the issue occurs regularly, then please send us the plugin's trace log together with

the crash stack. You can obtain the trace log by enabling the tracing mode through

the 'PVS-Studio -> Options -> Common Analyzer Settings -> TraceMode (Verbose

mode)' setting. The trace log will be saved into the default user directory

Application Data\Roaming\PVS-Studio under the name PVSTracexxxx_yyy.log,

where xxxx is PID of the process devenv.exe / bds.exe, while yyy is the log number

for this process.

Unhandled IDE crash when utilizing PVS-StudioIf you encounter regular crashes of your IDE which are presumably caused by PVS-

Studio's operation, please check the Windows system event logs (in the Event

Viewer) and contact our support service to provide us with the crash signature and

stack (if available) for the application devenv.exe \ bds.exe (the 'Error' message

level) which can be found in the Windows Logs -> Application list.

PVS-Studio.exe crash

Page 152: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

If you encounter regular unhandled crashes of the PVS-Studio.exe analyzer, please

repeat the steps described in the section "IDE crashes when PVS-Studio is running",

but for the PVS-Studio.exe process.

The V001/V003 errorsThe error V003 actually means that PVS-Studio.exe has failed to check the file

because of a handled internal exception. If you discover V003 error messages in the

analyzer log, please send us an intermediate file (an i-file containing all the

expanded include and define directives) generated by the preprocessor for the file

that triggers the v003 error (you can find its name in the file field). You can get this

file by setting the 'PVS-Studio -> Options -> Common Analyzer Settings ->

Remove Intermediate Files' option to 'False'. Intermediate files with the name

pattern SourceFileName.i will appear, after restarting the analysis, in the directory

of the project that you are checking (i.e. in the same directory where the

vcproj/vcxproj/cbproj files are located).

The analyzer may sometimes fail to perform a complete analysis of a source file. It

is not always the analyzer's fault - see the documentation section on the V001 error

to learn more about this issue. No matter what was the cause of a V001 message, it

is usually not critical. Incomplete file parsing is insignificant from the analysis

viewpoint. PVS-Studio simply skips a function/class with an error and continues

with the analysis. It's only a very small portion of code which is left unchecked. If

this portion contains fragments you consider relevant, you may send us an i-file for

this source file as well.

The analyzer cannot locate errors in an incorrect code or generates too many false positivesIf it seems to you that the analyzer fails to find errors in a code fragment that surely

contains them or, on the contrary, generates false positives for a code fragment

which you believe to be correct, please send us the preprocessor's temporary file.

You can get it by setting the 'PVS-Studio -> Options -> Common Analyzer Settings

-> Remove Intermediate Files' option to 'False'. Intermediate files with the name

Page 153: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

pattern SourceFileName.i will appear, after you restart the analysis, in the directory

of the project you are checking (i.e. in the same directory where

ycproj/vcxproj/cbproj files are located). Please attach the source file's code fragment

that you have issues with as well.

We will consider adding a diagnostic rule for your sample or revise the current

diagnostics to reduce the number of false positives in your code.

Issues with handling PVS-Studio analysis report from within the IDE pluginIf you encounter any issues when handling the analyzer-generated log file within

the window of our IDE plugin, namely: navigation on the analyzed source files is

performed incorrectly and/or these files are not available for navigation at all; false

positive markers or comments are added in wrong places of your code, and the like

- please contact our support service to provide us with the plugin's trace log. You

can get it by enabling the tracing mode through the 'PVS-Studio -> Options ->

Common Analyzer Settings -> TraceMode' option (Verbose mode). The trace log

will be saved into the default user directory Application Data\Roaming\PVS-Studio

under the name PVSTracexxxx_yyy.log, where xxxx is PID of the devenv.exe /

bds.exe process, while yyy is the log number for this process.

Also, if it is possible, create an empty test project reproducing your trouble and

attach it to the letter too.

Code analysis running from the IDE plugin is slow. Not all the logical processors are being utilizedThe PVS-Studio plugin can parallelize code analysis at the level of source files, that

is, you can have analysis for any files you need to check (even within one project)

running in parallel. The plugin by default sets the number of threads into which the

analysis process is parallelized according to the number of processors in your

system. You may change this number through the option PVS-Studio -> Options ->

Common Analyzer Settings -> ThreadCount.

Page 154: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

If it seems to you that not all of the available logical processors in your system are

being utilized, you can increase the number of threads used for parallel analysis. But

keep in mind that static analysis, unlike compilation, requires a large amount of

memory: each analyzer instance needs about 1.5 Gbytes.

If your system, even though possessing a multi-core processor, doesn't meet these

requirements, you may encounter a sharp performance degradation caused by the

analyzer having to rely on a swap file. In this case, we recommend you to reduce

the number of parallel threads of the analyzer to meet the requirement of 1.5 Gbytes

per thread, even if this number is smaller than the number of processor cores in your

system.

Keep in mind that when you have many concurrent threads, your HDD, which

stores temporary preprocessed *.i files, may become a bottleneck itself, as these

files may grow in size quite quickly. One of the methods to significantly reduce the

analysis time is to utilize SSD disks or a RAID array.

A performance loss may also be caused by poorly configured antivirus software.

Because the PVS-Studio plugin launches quite a large number of analyzer and the

cmd.exe instances, your antivirus may find this behavior suspicious. To optimize

the analysis time, we recommend you to add PVS-Studio.exe, as well as all of the

related directories, to the exceptions list of your antivirus or disable real-time

protection while the analysis is running.

If you happen to utilize the Security Essentials antivirus (which has become a part

of Windows Defender starting with Windows 8), you may face a sharp performance

degradation on certain projects/configurations. Please refer to this article on our

blog for details concerning this issue.

I get the message "Files with C or C++ source code for analysis not found." when checking a group of projects or one C/C++ project

Page 155: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Projects excluded from the general build in the Configuration Manager window of

the Visual Studio environment are not analyzed.

For the PVS-Studio analyzer to analyze C/C++ projects correctly, they must be

compilable in Visual C++ and buildable without errors. That's why when checking a

group of projects or an individual project, PVS-Studio will check only those

projects which are included into the general build.

Projects excluded from the build won't be analyzed. If none of the projects is

included into the build or you try to analyze one project that was not included into

the build, the message "Files with C or C++ source code for analysis not found" will

be generated, and analysis won't start. Use the Configuration Manager for the

current Visual Studio solution to see which projects are included and which are

excluded from the general build.

I cannot install the IDE PVS-Studio plugin for the Visual Studio Express EditionUnfortunately, none of the free Visual Studio Express Edition versions supports

IDE extensions - this is the limitation of this particular edition of Visual Studio. As

Page 156: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

an alternative, however, you may try direct integration of the analyzer into the

MSBuild build system for Visual Studio versions starting with version 2010.

Errors of the "Cannot open include file", "use the /MD switch for _AFXDLL builds" kinds on projects that could be successfully compiled in Visual Studio. Insertion of incorrect precompiled headers during preprocessingIf you are encountering errors with missing includes, incorrect compiler switches

(for example, the /MD switch) or macros while running static analysis on a project

which can be compiled in Visual Studio IDE without such errors, then it is possible

that this behavior is a manifestation of an incorrect precompiled header files being

inserted during the preprocessing.

This issue arises because of the divergent behavior of Visual C++ compiler (cl.exe)

in its' compiler and preprocessor modes. During a normal build, the compiler

operates in the "regular" mode (i.e. the compilation results in the object, binary

files). However, to perform static analysis, PVS-Studio invokes the compiler in the

preprocessor mode. In this mode the compiler performs the expansion of macros

and include directives.

But, when the compiled file utilizes a precompiled header, the compiler will use a

header itself when it encounters the #include directive. It will use the previously

generated pch file instead. However, in the preprocessing mode, the compiler will

ignore the precompiled pch entirely and will try expanding such #include in a

"regular way", i.e. by inserting the contents of the header file in question.

It is a common practice to use precompiled headers with the same name in multiple

projects (the most common one being stdafx.h). This, because of the disparities in

the compiler behavior described earlier, often leads to the header from an incorrect

project being included into the source file. There are several reasons why this can

happen. For example, a correct pch is specified for a file, but the Includes contain

several paths containing several different stdafx.h files, and the incorrect one

possesses a higher priority for being included (that is, its' include path occurs earlier

Page 157: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

on the compiler's command line). Another possible scenario is the one in which

several projects include the same C++ source file. This file could be built with

different options in different projects, and it uses the different pch files as well. But

since this is just a single file in your file system, one of the stdafx.h files from one

of the projects it is included into could be located in the same directory as the

source file itself. And if the stdafx.h is included into this source file by the #include

directive using the quotes, then the preprocessor will always use the header file

from the same directory as this file, regardless of the includes passed through the

command line.

Insertion of the incorrect precompiled header file will not always lead to the

preprocessing errors. However, if one of the projects, for example, utilized MFC,

and the other one is not, ore the projects possess a different set of Includes, the

precompiled headers will be incompatible, and one of the preprocessing errors

described in the title of this section will occur. As a result, you will not be able to

perform static analysis on such a file.

Unfortunately, it is impossible to bypass this issue on the analyzer's side, as it

concerns the external preprocessor, that is, the cl.exe. If you are encountering it on

one of your projects, then it is possible to solve it by one of the methods described

below, depending on the causes that lead to it.

In case the precompiled header was incorrectly inserted because of the position of

its' include path on the compiler's command line, you can simply move a path for

the correct header file to the first position on the command line.

If the incorrect header file was inserted because of its' location in the same directory

as the source file into which it is included, then you can use the #include directive

with pointy brackets, for example:

#include <stdafx.h>

While using this form, the compiler will ignore the files form the current directory

when it performs the insertion.

Page 158: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

'PVS-Studio is unable to continue due to IDE being busy' message under Windows 8. 'Library not registered' errorsWhen checking large (more than 1000 source files) projects with PVS-Studio under

Windows 8, while using Visual Studio 2010 or newer versions, sometimes the

errors of the 'Library not registered' kind can appear or analyzer can even halt the

analysis process altogether with 'PVS-Studio is unable to continue due to IDE being

busy' message.

Such errors can be caused by several factors: incorrect installation of Visual Studio

and compatibility conflicts between different versions of IDE present within a

system. Even if your system currently possesses a single IDE installation, but a

different version was present in the past, it is possible that this previous version was

uninstalled incorrectly or incompletely. In particular, the compatibility conflict can

arise from simultaneously having installations of one of Visual Studio

2010\2012\2013\2015 and Visual Studio 2005 and\or 2008 on your system.

Unfortunately, PVS-Studio is unable to 'work around' these issues by itself, as they

are caused by conflicts in COM interfaces, which are utilized by Visual Studio API.

If you are one of such issues, then you have several different ways of dealing with

it. Using PVS-Studio under a system with a 'clean' Visual Studio installation should

resolve the issue. However, if it not an option, you can try analyzing your project in

several go's, part by part. It is also worth noting that the issue at hand most often

arises in the situation when PVS-Studio performs analysis simultaneously with

some other IDE background operation (for example, when IntelliSense performs

#include parsing). If you wait for this background operation to finish, then it will

possibly allow you to analyze your whole project.

Another option is to use alternative methods of running the analyzer to check your

files. If you perform the direct integration of the analyzer into your project files, it

will allow you to circumvent Visual Studio APIs altogether. You can also check any

project by using the compiler monitoring mode from Standalone tool.

Page 159: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

After installing Visual Studio IDE on a machine with a previously installed

PVS-Studio analyzer, the newly installed Visual Studio version lacks the 'PVS-

Studio' menu item

Unfortunately, the specifics of Visual Studio extensibility implementation prevents

PVS-Studio from automatically 'picking up' newly installed Visual Studio in case it

happened after the installation of PVS-Studio itself.

Here is an example of such a situation. Let's assume that before the installation of

PVS-Studio, the machine have only Visual Studio 2013 installed on it. After

installing the analyzer, Visual Studio 2013 menu will contain the 'PVS-Studio' item

(if the corresponding option was selected during the installation), which allows you

to check your projects in this IDE. Now, if Visual Studio 2015 is installed on this

machine next (after PVS-Studio was already installed), the menu of this IDE

version will not contain 'PVS-Studio' item.

In order to add analyzer IDE integration to the newly installed Visual Studio, it is

necessary to re-launch PVS-Studio installer (PVS-Studio_Setup.exe file). If you do

not have this file already, you can download it from our site. The checkbox besides

the required IDE version on the Visual Studio selection installer page will be

enabled after the corresponding Visual Studio version is installed.

Tips on speeding up PVS-Studio

Use a multi-core computer with a large amount of memory

Use an SSD both for the system and the project to be analyzed

Configure (or turn off) your antivirus

In Visual Studio, if possible, use Clang as the preprocessor instead of Visual C++ (it can be chosen in the PVS-Studio settings)

Exclude libraries you don't need from analysis (can be set in the PVS-Studio settings)

Consider only checking files which were modified in the last several days

Conclusion

Page 160: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Any static code analyzer works slower than a compiler. It is determined by the fact

that the compiler must work very quickly, though to the detriment of analysis depth.

Static analyzers have to store the parse tree to be able to gather more information.

Storing the parse tree increases memory consumption, while a lot of checks turn the

tree traverse operation into a resource-intensive and slow process. Well, actually it

all is not so much crucial, since analysis is a rarer operation than compilation and

users can wait a bit. However, we always want our tools to work faster. The article

contains tips on how to significantly increase PVS-Studio's speed.

At first let's enumerate all the recommendations so that users learn right away how

they can make the analyzer work faster:

1. Use a multi-core computer with a large amount of memory.

2. Use an SSD both for the system and the project to be analyzed.

3. Configure (or turn off) your antivirus.

4. If possible, use Clang as the preprocessor instead of Visual C++ (it can be chosen in the PVS-Studio settings).

5. Exclude libraries you don't need from analysis (can be set in the PVS-Studio settings).

Let's consider all these recommendations in detail, explaining why they allow the

tool to work faster.

Use a multi-core computer with a large amount of memoryPVS-Studio has been supporting multi-thread operation for a long time already

(starting with version 3.00 released in 2009). Parallelization is performed at the file

level. If analysis is run on four cores, the tool is checking four files at a time. This

level of parallelism enables you to get a significant performance boost. Judging by

our measurements, there is a marked difference between the four-thread and one-

thread analysis modes of test projects. One-thread analysis takes 3 hours and 11

minutes, while four-thread analysis takes 1 hour and 11 minutes (these data were

obtained on a four-core computer with 8 Gbytes of memory). That is, the difference

is 2.7 times.

Page 161: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

It is recommended that you have at least one Gbyte of memory for each analyzer's

thread. Otherwise (when there are many threads and little memory), the swap file

will be used, which will slow down the analysis process. If necessary, you may

restrict the number of the analyzer's threads in the PVS-Studio settings: Options ->

Common Analyzer Settings -> Thread Count (documentation). By default, the

number of threads launched corresponds to the number of cores available in the

system.

We recommend that you use a computer with four cores and eight Gbytes of

memory or better.

Use an SSD both for the system and the project to be analyzedStrange as it may seem, a slow hard disk is a bottleneck for the code analyzer's

work. But we must explain the mechanism of its work for you to understand why it

is so. To analyze a file, the tool must first preprocess it, i.e. expand all the #define's,

include all the #include's and so on. The preprocessed file has an average size of 10

Mbytes and is written on the disk into the project folder. Only then the analyzer

reads and parses it. The file's size is growing because of that very inclusion of the

contents of the #include-files read from the system folders.

I can't give exact results of measuring the influence of an SSD on the analysis speed

because you have to test absolutely identical computers with only hard disks

different. But visually the speed-up is great.

Configure (or turn off) your antivirusJudging by the character of its work, the analyzer is a complex and suspicious

program from the viewpoint of an antivirus. Let's specify right away that we don't

mean that the analyzer is recognized as a virus - we check this regularly. Besides,

we use a code certificate signature. Let's go back to description of the code

analyzer's work.

For each file being analyzed a separate analyzer's process is run (the PVS-

Studio.exe module). If a project contains 3000 files, the same number of PVS-

Page 162: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Studio.exe's instances will be launched. PVS-Studio.exe calls Visual C++

environment variable setting (files vcvars*.bat) for its purposes. It also creates a lot

of preprocessed files (*.i) (one for each file being compiled) for the time of its

work. Auxiliary command (.cmd) files are being used.

Although all these actions are not a virus activity, it still makes any antivirus spend

many resources on meaningless check of the same things.

We recommend that you add the following exceptions in the antivirus's settings:

1. Do not scan system folders with Visual Studio:

1. C:\Program Files (x86)\Microsoft Visual Studio 8

2. C:\Program Files (x86)\Microsoft Visual Studio 9.0

3. C:\Program Files (x86)\Microsoft Visual Studio 10.0

4. etc.

2. Do not scan the PVS-Studio folder:

1. C:\Program Files (x86)\PVS-Studio

3. Do not scan the project folder:

1. For example, C:\Users\UserName\Documents\MyProject

4. Do not scan Visual Studio .exe files:

1. C:\Program Files (x86)\Microsoft Visual Studio 8\Common7\IDE\devenv.exe

2. C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\IDE\devenv.exe

3. C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\devenv.exe

4. etc.

5. Do not scan the cl.exe compiler's .exe files (of different versions):

1. C:\Program Files (x86)\Microsoft Visual Studio 8\VC\bin\cl.exe

2. C:\Program Files (x86)\Microsoft Visual Studio 8\VC\bin\x86_amd64\cl.exe

3. C:\Program Files (x86)\Microsoft Visual Studio 8\VC\bin\amd64\cl.exe

4. C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\cl.exe

Page 163: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

5. C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\x86_amd64\cl.exe

6. C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\amd64\cl.exe

7. C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\cl.exe

8. C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\x86_amd64\cl.exe

9. C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\amd64\cl.exe

10. etc.

6. Do not scan PVS-Studio and Clang .exe files (of different versions):

1. C:\Program Files (x86)\PVS-Studio\x86\PVS-Studio.exe

2. C:\Program Files (x86)\PVS-Studio\x86\clang.exe

3. C:\Program Files (x86)\PVS-Studio\x64\PVS-Studio.exe

4. C:\Program Files (x86)\PVS-Studio\x64\clang.exe

Perhaps this list is too excessive but we give it in this complete form so that you

know regardless of a particular antivirus what files and processes do not need to be

scanned.

Sometimes there can be no antivirus at all (for instance, on a computer intended

specially to build code and run a code analyzer). In this case the speed will be the

highest. Even if you have specified the above mentioned exceptions in your

antivirus, it still will spend some time on scanning them.

Our test measurements show that an aggressive antivirus might slow down the code

analyzer's work twice or more.

In Visual Studio, if possible, use Clang as the preprocessor instead of Visual C++ (it can be chosen in the PVS-Studio settings)PVS-Studio exploits an external preprocessor. Earlier we used only one

preprocessor by Microsoft Visual C++. In PVS-Studio 4.50 we added support of

another preprocessor Clang that works much faster and doesn't have certain weak

points of the Microsoft preprocessor (although it does have its own). However,

Page 164: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

using the Clang preprocessor will in most cases make the analyzer's work faster 1.5-

1.7 times.

But there is one thing you should remember. You may specify the preprocessor to

be used in the PVS-Studio settings: Options -> Common Analyzer Settings ->

Preprocessor (documentation). There are three alternatives available: VisualCPP,

Clang and VisualCPPAfterClang. The first two are obvious. What the third

alternative is concerned, it means that Clang will be used first and if some errors

occur during preprocessing, the file will be then preprocessed again by Visual C++.

It is this option (VisualCPPAfterClang) which is chosen by default.

If your project is analyzed with Clang without any problems, you may use the

default option VisualCPPAfterClang or Clang - it doesn't matter. But if your project

can be checked only with Visual C++, you'd better specify this option so that the

analyzer doesn't launch Clang in vain trying to preprocess your files.

Exclude libraries you don't need from analysis (can be set in the PVS-Studio settings)Any large software project uses a lot of third-party libraries such as zlib, libjpeg,

Boost, etc. Sometimes these libraries are built separately, and in this case the main

project has access only to the header and library (lib) files. And sometimes libraries

are integrated very firmly into a project and virtually become part of it. In this case

the main project is compiled together with the code files of these libraries.

The PVS-Studio analyzer can be set to not check code of third-party libraries: even

if there are some errors there, you most likely won't fix them. But if you exclude

such folders from analysis, you can significantly enhance the analysis speed in

general.

It is also reasonable to exclude code that surely will not be changed for a long time

from analysis.

To exclude some folders or separate files from analysis use the PVS-Studio settings

-> Don't Check Files (documentation).

Page 165: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

To exclude folders you can specify in the folder list either one common folder like

c:\external-libs, or list some of the folders: c:\external-libs\zlib, c:\external-libs\

libjpeg, etc. You can specify a full path, a relative path or a mask. For example, you

can just specify zlib and libjpeg in the folder list - this will be automatically

considered as a folder with mask *zlib* and *libjpeg*. To learn more, please see

the documentation.

Consider only checking files which were modified in the last several daysWhile using the analyzer regularly, and that means promptly fixing all the issues

found in the verified project and suppressing all of false positive warnings, it could

be quite inconvenient if such verification process takes an unacceptable amount of

time because of the sheer size of the project being verified. The total time required

to complete such process could be reduced by the exclusion of files which were not

modified in the course of a specified amount of time.

For instance, if the project is regularly verified and revised weekly in such a mode,

then excluding all of the files which hadn't been changed in the last 7 days does

make sense. Because if these files were indeed verified a week ago and all the

issues found inside them were fixed or marked as false positives, then any

additional recurring checks on them should not produce anything new at all.

Excluding all these files from the analysis will reduce a total time required for each

consecutive check in case the verified project is indeed a large one and consists of a

huge amount of source files.

It is possible to set up such verification interval using the 'Check only Files

Modified In' setting in the PVS-Studio Common Analyzer Settings options window.

By default PVS-Studio will verify all source files, regardless of date and time of

their modifications.

When using this mode, it is also advisable to perform a repeated verification of all

the source files in the project each time a new version of the PVS-Studio is released,

as this new version could contain new diagnostics and also the improvements for

the old ones.

Page 166: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The operation mode described above does possess several limitations imposed on it

by different peculiarities of file system, Visual C++ compiler and version control

system being utilized. These limitations are described in more detail on

the documentation page for this mode.

ConclusionLet's once again list the methods of speeding up PVS-Studio:

1. Use a multi-core computer with a large amount of memory.

2. Use an SSD both for the system and the project to be analyzed (Update: for PVS-Studio versions 5.22 and above, deploying the project itself on SSD does not improve the overall analysis time).

3. Configure (or turn off) your antivirus.

4. If possible, use Clang as the preprocessor instead of Visual C++ (it can be chosen in the PVS-Studio settings).

5. Exclude libraries you don't need from analysis (can be set in the PVS-Studio settings).

6. Consider only checking files which were modified in the last several days

The greatest effect can be achieved when applying a maximum number of these

recommendations simultaneously.

PVS-Studio's incremental analysis mode

Introduction

Two tasks solved by incremental analysis

Using incremental analysis

Incremental analysis workflow in the IDE

Incremental analysis support in the command line module

Disadvantages of incremental analysis in Microsoft Visual Studio

Introduction

Page 167: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

One of the main problems when using a static analyzer is the necessity to spend

much time on analyzing project files after each project modification. It is especially

relevant to large projects that are actively developing.

Total analysis can be launched regularly in separate cases - for instance, once a day

during night builds. But the greatest effect of using the analyzer can be achieved

only through earlier detection and fix of found defects. The earlier you can find an

error, the less amount of code you will have to fix. That is, the most proper way to

use a static analyzer is to analyze a new code right after it is written. Having to

launch analysis of all the modified files manually and waiting for it to finish each

time surely complicates this scheme. It is incompatible with the intense

development and debugging of new code. It's just inconvenient, after all. But PVS-

Studio offers a solution of this issue.

Two tasks solved by incremental analysis1. Automation of the analyzer launch procedure right after compilation on the

developer's computer.

2. Gaining profit from integrating static analysis in a large project WITHOUT having to analyze the whole project. You can virtually start integrating static code analysis checking only that code (those files) currently modified by developers and don't get into a heap of old files which are not modified.

Using incremental analysisPVS-Studio's incremental analysis mode allows you to solve problems regarding

regular launch of static analysis. The user starts getting practical benefit without

having to perform primary analysis of the whole project and marking a lot of false

positives in unused code. This mode virtually makes PVS-Studio similar to the

/analyze switch in certain Visual Studio versions regarding usability. It also

provides you with a more convenient interface and use cases. Now you can just

work on your code, compile it and get messages from time to time about possible

issues. Since the analyzer works in background (you can set the number of

processor cores to perform analysis using the ThreadCount option), PVS-Studio

doesn't interfere with work of other programs. And it means that you can easily

Page 168: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

install the tool on computers of many (or all) developers in your department to

detect issues in the code right after they appear.

You can turn on the afterbuild incremental analysis in the PVS-Studio menu:

Incremental Analysis After Build/Incremental Analysis After Make (Figure 1). This

option is on by default.

Figure 1 — Managing PVS-Studio's incremental analysis mode

Once the incremental analysis option is enabled, PVS-Studio will automatically

perform background analysis of all the modified files after building the project. If

PVS-Studio detects such modifications, incremental analysis will be launched

automatically, and an animated PVS-Studio icon will appear in the notification area

(Figure 2). Note that new icons may be often hidden in Windows's notification area.

Figure 2 — PVS-Studio performing incremental analysis

The notification area's shortcut menu allows you to pause or abort current check

(commands Pause and Abort respectively). You may find it useful sometimes to

turn off incremental analysis for some time. For instance, you may need it when

editing base h-files, which causes recompilation of many files. If you wish to turn

off incremental analysis just for some time, you may use commands of the shortcut

Page 169: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

menu and PVS-Studio's main menu 'Disable incremental analysis until IDE restart'

(Figures 1 and 2).

If the analyzer detects errors in code while performing incremental analysis, the

number of detected errors will be shown in the title of the PVS-Studio's window tab

in background. Clicking on the icon in the notification area (or the window itself)

opens the PVS-Studio Output window where you can start handling the errors

without even waiting for analysis to finish.

Figure 3 – The result of incremental analysis: 15 suspect fragments of code are found

Keep in mind that after the first total check of your project you should review all the

diagnostic messages for the required files and fix the errors found in your code.

Regarding the rest messages, you should either mark them as false positives or

disable those messages or analyzer types which are not relevant to your project.

This approach will allow you to get a message list clear of meaningless and

unnecessary messages.

Incremental analysis workflow in the IDETo determine the presence of modifications in the source code files, PVS-Studio

controls the state of object files (obj/o files), generated by the C++ compiler for

every C/C++ file, or the state of assemblies for C# projects. Before the actual build

in the environment, the PVS-Studio plugin for Visual Studio captures the object

files and their modifications for all the compilable files of the project. To determine

the modified files in the C/C++ projects, which require incremental analysis, the

analyzer uses MSBuild tools to work with the file tracking logs (*.tlog files). This

approach, firstly, enables us to obtain modified files to be analyzed incrementally,

in the same way as MSBuild does, and secondly, eliminates the need to make the

correspondence between the header files and the source code. The file tracking logs

aren't created for C# projects. That's why, to determine the files which require the

Page 170: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

incremental analysis, the plugin makes the correspondence between the source code

and the binary assembly file, which is the result of the project compilation and

captures those files that were modified after the assembly was built. After the build,

the incremental analysis starts automatically in the background mode.

Incremental analysis support in the command line moduleThe incremental analysis mode for Visual Studio solutions is also available in the

command line module (PVS-Studio_Cmd.exe). This mode allows increasing the

speed of the static analysis on the continuous integration server. Consider the

following scenario of using the static analyzer. PVS-Studio static analyzer is

installed on the machines of the developers, who do the incremental analysis after

the local build of the solution, and on the continuous integration server, where the

static analysis is performed for the whole code during the night build. Suppose, that

also the system of continuous integration is configured for the automatic

incremental build of the solution after the changes get detected in the version

control system. In other words, the build of the solution on the continuous

integration server occurs several times a day. In this case, performing the static

analysis of the whole code will significantly increase the build time, which will

make the use of static analysis during numerous daily builds almost impossible.

Then we may have a situation when a developer makes an error in the code and

commits it to the version control system without checking the code with the static

code analyzer; the same say the build goes to the testers who detect this defect. In

this case, the cost of eliminating this defect goes up. The incremental analysis mode

that implements the approaches similar to the approaches of MSBuild for the

incremental build, allows to solve this problem.

There are following modes of incremental analysis available:

Scan – analyze all dependencies to determine, which files will be analyzed incrementally. There will be no immediate analysis. This step should be done right before the solution of the project is built. The scan results will be written to the temporary directories .pvs-studio, located in the same directories as the project files.

Page 171: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Analyze – perform incremental analysis. This step should be done after Scan and can be performed both before and after the build of the solution or the project. The static analysis will be performed only for the files that have been modified since the previous build. If the option Remove Intermediate Files is set as True in the PVS-Studio settings, then the temporary .pvs-studio directories will be removed.

ScanAndAnalyze – analyze all the dependencies to determine which files should be analyzed incrementally and perform incremental analysis of the modified files with the source code. This step should be performed before the build of the project/solution.

The arguments of the command line module (PVS-Studio_Cmd.exe) to start the

incremental analysis are given in the section Analyzing Visual C++ (.vcxproj) and

Visual C# (.csproj) projects from the command line.

Disadvantages of incremental analysis in Microsoft Visual StudioIf PVS-Studio uses the CL.exe C++ preprocessor integrated into Visual Studio, an

unpleasant file lock issue might occur in rare cases. This is how it may look: you

edit a file and compile it. Incremental analysis is launched. At this moment you

notice some small defect, fix it quickly and try to recompile the file again. Visual

Studio warns you that the edited file cannot be written because some program has

locked it. This is CL.exe which is performing the preprocessing operation now.

Unfortunately, we cannot manage this lock in any way. If you come across this

issue, you should wait a few seconds and continue the aborted work. Note that this

situation is much rarer when using the Clang preprocessor (Preprocessor settings

option). That's why you'd better use Clang as the preprocessor when possible.

Deployment of PVS-Studio in large teams

Compatibility testing

Unattended deployment

Deploying licenses

Customizing settings

Page 172: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Usually deployment of software product within medium to large scale organization

is a tricky process. Here are main concerns:

1. Compatibility - new application should be compatible with pre-existing ecosystem - both software and hardware. Larger organizations even have their own testing labs to ensure smooth deployments

2. Deployment of software itself - all software that is aimed to be used by businesses or other large organizations should support unattended installation and removal

3. Licensing - usually special efforts are required to maintain proper licensing to ensure that needed number of licenses are available for all users of application within organization

4. Custom set up - many organizations require some customized settings to be applied to all instances of the application used within organization

This article describes in detail how PVS-Studio could address all these concerns

Compatibility testingFor PVS-Studio compatibility testing is not a problem - it is rather light-weight

extension to Visual Studio and does not have any incompatibility issues.

However as many companies require formal compatibility testing to be performed

one might use either trial version which is readily available from the web-site, or

require special free testing license.

Please contact us to discuss various possibilities.

Unattended deploymentAs for most of other software setting up PVS-Studio requires administrative

privileges.

Unattended setup is performed by specifying command line parameters, for

example:

PVS-Studio_Setup[.exe] /verysilent /suppressmsgboxes /norestart

PVS-Studio may require a reboot if, for example, files that require update are

locked. To install PVS-Studio without reboot, use the NORESTART flag. Please

Page 173: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

also note that if PVS-Studio installer is started in a silent mode, the computer may

be rebooted without any warnings or dialogs.

By default, all available PVS-Studio components will be installed. In case this is

undesirable, the required components can be selected by the ' COMPONENTS'

switch (following is a list of all possible components):

PVS-Studio_setup.exe /verysilent /norestart /components= Core,

Standalone,MSVS,MSVS\2005,MSVS\2008,MSVS\2010,MSVS\2012,MSVS\2013

Components with MSVS prefix in their name are corresponding to Microsoft Visual

Studio plug-in extensions. The 'Core' component is a mandatory one; it contains a

core command-line analyzer engine, which is required for all of the IDE extension

plug-ins to operate.

During installation of PVS-Studio all instances of Visual Studio should be shut

down, however to prevent user's data loss PVS-Studio does not shut down Visual

Studio.

The installer will exit with '100' if it is unable to install the extension (*.vsix) for

any of the selected versions of Visual Studio.

The PVS-Studio-Updater.exe can perform check for analyzer updates, and, if an

update is available, it can download it and perform an installation on a local system.

To start the updater tool "silently", the same arguments can be utilized:

PVS-Studio-Updater.exe /VERYSILENT /SUPPRESSMSGBOXES

If there are no updates on the server, the updater will exit with the code '0'. As PVS-

Studio-Updater.exe performs a local deployment of PVS-Studio, devenv.exe should

not be running at the time of the update as well.

If you connect to Internet via a proxy with authentication, PVS-Studio-Updater.exe

will prompt you for proxy credentials. If the proxy credentials are correct, PVS-

Studio-Updater.exe will save them in the Windows Credential Manager and will use

these credentials to check for updates in future.

Page 174: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Please also note that PVS-Studio-Updater.exe should not be started from its'

installation folder ("C:\Program Files (x86)\PVS-Studio" by default), as this will

lead to the subsequent failure of the installer - 'PVS-Studio-Updater.exe' file will be

blocked by the running updater process. We recommend copying PVS-Studio-

Updater.exe to the temporary folder before running it:

copy /Y PVS-Studio-Updater.exe %TEMP%

%TEMP%\PVS-Studio-Updater.exe /VERYSILENT /SUPPRESSMSGBOXES

Deploying licensesDeployment of licenses is usually performed right after unattended installation.

You can enter license info via PVS-Studio Options. Please open PVS-Studio

Options (PVS-Studio Menu -> Options...) from your running IDE instance and

choose the "Registration" page.

If you want deploy PVS-Studio for many computers then you can install license

without manual entering. It should place valid PVS-Studio.lic license file along with

Settings.xml into folder under user's profile.

If many users share one desktop each one should have its own license.

Destination folder is:

%USERPROFILE%\AppData\Roaming\PVS-Studio\PVS-Studio.lic

%USERPROFILE%\AppData\Roaming\PVS-Studio\Settings.xml

Customizing settingsDeployment of custom settings is as simple as deployment of license.

Path to file that contains all application's settings is:

%USERPROFILE%\AppData\Roaming\PVS-Studio\Settings.xml

It is user-editable xml file, but it also could be edited by through PVS-Studio IDE

plug-in on a target machine.

Page 175: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Please note that all settings that should be kept as default values could be omitted

from Setting.xml file.

If you have any questions please feel free to contact our customer support.

Using external tools with PVS-Studio. Integration with bug tracking systems

Configuring and utilizing external tools through PVS-Studio

Use cases for the employment of external tools

Integration into the issue tracking system by the example of Fossil distributed system

To provide a more convenient way of handling the analysis results, PVS-Studio

allows the utilization of any external application through a simple and intuitive

command line interface. Such interface allows the transfer of any information on the

specified diagnostic message "to the outside", for instance to the external text editor

for a report compilation or even to the issue tracking system directly. In the case of

a relatively large project this could substantially streamline the team-based handling

of the static analysis results.

Configuring and utilizing external tools through PVS-StudioThe PVS-Studio Output window context menu command 'Send this message to

external tool' is responsible for executing the user-specified external tool. It should

be noted that this command is available for a single simultaneously selected

message only.

Page 176: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Figure 1 - Launching the external tool for a selected message in the results' log through context menu.

Configuring the path to the external tool and its' command line arguments is

possible through the ExternaToolPath and ExternalToolCommandLine fields in the

Visual Studio settings dialog, PVS-Studio -> Specific Analyzer Settings page. By

default, after fresh installation PVS-Studio settings will contain the following values

for these fields:

C:\Windows\system32\cmd.exe

/c echo %code %message %filename [%line] >> "C:\Users\eremeev\

Documents\PVSStudioExternaltoolExample.txt" && notepad

"C:\Users\eremeev\Documents\PVSStudioExternaltoolExample.txt"

In this example the cmd.exe command line interpreter is used as an external tool, to

which 2 commands are passed. The first one writes several fields from the currently

selected table row using several formatting parameters (%code, %message, etc.) to

the text file PVSStudioExternaltoolExample.txt. The second command opens this

file in a text editor.

Before being passed to the external tool, the formatting parameters will be replaced

by their corresponding context values, wrapped in double quotes. Let's review all

available formatting parameters:

%code —code of the diagnostic message (Code column)

%message —body of the diagnostic message (Message column)

%filename — name of the file containing the diagnostic message (File column)

%line —number of the line containing the diagnostic message (Line column)

%filepath — full path to the file containing the diagnostic message

%project — project containing the file with the diagnostic message (Project column)

Page 177: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

%comment — user supplied comment and/or a text currently selected in Visual Studio code editor

A special attention should be given to the %comment% parameter, which allows the

user defined comment and\or fragments of the analyzed project's source code to be

inserted into the resulting command line. This could prove useful while writing an

article or bug report for an issue tracking system. After including this parameter into

the ExternalToolCommandLine setting field, the behavior of the context menu

command will change: it will no longer start immediately executing the specified

external tool. Instead it will display the PVS-Studio External Tool Comment Editor

IDE toolwindow (Figure 2).

Figure 2 — Comment editor for the external tool.

Moreover, if a document was open inside the IDE at the moment this window was

called, then any text selection which was present inside such a document will be

automatically pasted into the comment editor itself. The editor of course also allows

this fragment to be edited or supplemented with any additional user provided

comments.

After pressing the 'Start External Tool' button the user external tool will be

launched, while the %comment formatting parameter will be replaced by the

contents of this window's editor field. It should be considered that all special

characters, such as newline symbols, will also be inserted to the resulting command

line. The double quote characters inside the text will be escaped by PVS-Studio

automatically. And as the whole argument is surrounded by double quotes, it should

Page 178: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

be passed to the external tool as a single argument, without being broken into

multiple ones.

Additionally, the "Escape Newline Characters" checkbox escapes all CR and LF

special characters with a standard \r and \n sequences in case the presences of these

characters in the command line is undesirable or unallowable.

Use cases for the employment of external toolsUsing external tools together with PVS-Studio could be helpful either for an

individual developer handling the diagnostic report or for the team-based utilization

of the analyzer. Let's review some typical use-cases of using different external tools

while handling the analyzer's report.

The most basic scenario is to write the messages which the developer found

interesting to the external text file. For example, the developer needs to compile a

report an article describing the errors in the project that he or she identified and/or

fixed, of course excluding all of the unavoidable false positives. The easiest way to

go about it to use the standard output redirection operators, as was demonstrated

earlier in PVS-Studio default settings. Of course, such a method does not permit the

passing of user comments containing several special characters, such as newlines,

so for a more robust solution the employment of a special-purpose utility with the

ability to correctly process such command arguments is recommended.

However, using the analyzer on a comparably large project produces a report that

cannot be processed by a single individual alone, and so existing issues cannot be

unambiguously separated from false positives. Quite often individual developers are

also unable to perform the analysis locally on their own code base, as if, for

example, the verification for the whole project could only be performed on a

separate build server during nightly builds. But even when the real error is

identified and subsequently fixed, quite often all the information concerning it is

lost, as the developer responsible will not bother creating a separate bug report in

the project's issue tracking system. And assuming that the error was present in the

Page 179: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

code for a relatively long period, the behavior changes that result from it being fixed

could potentially affect other parts of the system that is being developed.

In this kind of situation, the PVS-Studio Output window could be configured to

pass the information on identified or fixed errors into project's issue tracking system

by setting the "Send to external tool" command to call for an appropriate local

desktop client. Such reduction in the effort of appending reports to the Project's

issue database allows for a more streamlined handling of the analysis results from

within PVS-Studio window, without constant distractions by the necessity to

manually fill the report forms for a bug tracker.

The presence of static analyzer's reports on identified potential issues inside the bug

tracking system allows for an easy redistribution of tasks on fixing of

aforementioned issues among the developers and tracking of their progress by

recording every error that was fixed in this way. Such method would provide a

formalized and distributed way for handling static analysis results, increasing its'

usage effectiveness and allowing for the estimation of the contribution by the

analyzer to the whole effort of the project's development and debugging.

Integration into the issue tracking system by the example of Fossil distributed systemLet's examine the PVS-Studio issue-tracking integration use-case by the example of

the Fossil system. Fossil is a distributed file revision control and issue tracking

system which is based on SQLite DBMS. Because of Fossil being a distributed

system, it operates on a full local repository, which in turn allows for a direct

interaction with it by PVS-Studio plugin.

Let's review Fossil client's command line arguments necessary for adding a PVS-

Studio generated issue report to the Fossil's database (in a single line).

C:\Fossil\Fossil.exe

ticket add title %message comment %comment status new

type PVS-Studio -q -R MyRepo

Page 180: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

In the example above we've created a ticket record in the Fossil's local repository

with a 'PVS-Studio' type. The diagnostic message body was used as the ticket's title

(%message parameter). A fragment of source code was used as a ticket comment

(%comment parameter). After the "Send this message to external tool" is executed,

the issue report of the following kind will be appended to the local repository:

Figure 3 — Issue report on the error identified by PVS-Studio inside the Fossil's tracking system.

In our example we've operated on a local repository of a distributed system. As for

the centralized systems, such as Bugzilla and Trac, with remote web-server based

repositories, some kind of external local desktop client for corresponding system

will be required to realize the interaction with PVS-Studio. Of course this client is

required to provide a command-line interface and must be able to communicate

with a remote server containing the system's repository. The examples of such

clients for the tracker systems we've mentioned earlier are tracshell and Bugzproxy.

Relative paths in PVS-Studio log filesWhen generating diagnostic messages, PVS-Studio by default generates absolute, or

full, paths to the files where errors have been found. That's why, when saving the

report, it's these full paths that get into the resulting file (XML plog file). It may

cause some troubles in the future - for example when you need to handle this log

file on a different computer. As you know, paths to source files may be different on

two computers. This will lead to you being unable to open files and use the

integrated mechanism of code navigation in such a log file.

Page 181: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Although this problem can be solved by editing the paths in the XML report

manually, it's much more convenient to get the analyzer to generate messages with

relative paths right away, i.e. paths specified in relation to some fixed directory (for

example, the root directory of the project source files' tree). This way of path

generation will allow you to get a log file with correct paths on any other computer

- you will only need to change the root in relation to which all the paths in the PVS-

Studio log file are expanded. The setting SourceTreeRoot found on the page PVS-

Studio -> Options -> Specific Analyzer Settings serves to tell PVS-Studio to

automatically generate relative paths as described and replace their root with the

new one.

Let's have a look at an example of how this mechanism is used. The

SourceTreeRoot option's field is empty by default, and the analyzer always

generates full paths in its diagnostic messages. Assume that the project being

checked is located in the "C:\MyProjects\Project1" directory. We can take the path

"C:\MyProjects\" as the root of the project source files' tree and add it into the field

SourceTreeRoot, and start analysis after that (Figure 1).

Figure 1 — Specifying the tree root for source files

Now that analysis is over, PVS-Studio will automatically replace the root directory

we've defined with a special marker. It means that in a message for the file C:\

Page 182: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

MyProjects\Project1\main.cpp, the path to this file will be defined as |?|Project1\

main.cpp. Messages for the files outside the specified root directory won't be

affected. That is, a message for the file C:\MyCommonLib\lib1.cpp will contain the

absolute path to this file.

In the future, when handling this log file in the IDE PVS-Studio plugin, the marker

|?| will be automatically replaced with the value specified in the SourceTreeRoot

setting's field - for instance, when using the False Alarm function or message

navigation. If you need to handle this log file on another computer, you'll just need

to define a new path to the root of the source files' tree (for example, C:\Users\User\

Projects\) in the IDE plugin's settings. The plugin will correctly expand the full

paths in automated mode.

This option can also be used in the Independent mode of the analyzer, when it is

integrated directly into a build system (make, msbuild, and so on). It will allow you

to separate the process of full analysis of source files and further investigation of

analysis results, which might be especially helpful when working on a large project.

For example, you can perform a one-time complete check of the whole project on

the build server, while analysis results will be studied by several developers on their

local computers.

Compiler Monitoring System in PVS-Studio

Introduction

Working principles

Getting started with CLMonitor.exe

Compiler monitoring from Standalone

Conclusion

Introduction

Page 183: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The PVS-Studio Compiler Monitoring system (CLMonitoring) was designed for

"seamless" integration of the PVS-Studio static analyzer into any build system

under Windows that employs one of the preprocessors supported by the PVS-

Studio.exe command-line analyzer (Visual C++, GCC, Clang, Borland C++) for

compilation.

To perform correct analysis of the source C/C++ files, the PVS-Studio.exe analyzer

needs intermediate .i files which are actually the output of the preprocessor

containing all the headers included into the source files and expanded macros. This

requirement defines why one can't "just take and check" the source files on the disk

- besides these files themselves, the analyzer will also need some information

necessary for generating those .i files. Note that PVS-Studio doesn't include a

preprocessor itself, so it has to rely on an external preprocessor in its work.

As the name suggests, the Compiler Monitoring system is based on "monitoring"

compiler launches when building a project, which allows the analyzer to gather all

the information essential for analysis (that is, necessary to generate the preprocessed

.i files) of the source files being built. In its turn, it allows the user to check the

project by simply rebuilding it, without having to modify his build scripts in any

way.

This monitoring system consists of a compiler monitoring server (the command-line

utility CLMonitor.exe) and an UI client integrated into the Standalone version of

PVS-Studio and responsible for launching the analysis (CLMonitor.exe can be also

used as a client when launched from the command line).

In the current version, the system doesn't analyze the hierarchy of the running

processes; instead, it just monitors all the running processes in the system. It means

that it will also know if a number of projects are being built in parallel and monitor

them.

Working principlesCLMonitor.exe server monitors launches of processes corresponding to the target

compiler (for example cl.exe for Visual C++ and g++.exe for GCC) and collects

Page 184: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

information about the environment of these processes. Monitoring server will

intercept compiler invocations only for the same user it was itself launched under.

This information is essential for a correct launch of static analysis to follow and

includes the following data:

the process main folder

the full process launch string (i.e. the name and all the launch arguments of the exe file)

the full path to the process exe file

the process environment system variables

Once the project is built, the CLMonitor.exe server must send a signal to stop

monitoring. It can be done either from CLMonitor.exe itself (if it was launched as a

client) or from Standalone's interface.

When the server stops monitoring, it will use the collected information about the

processes to generate the corresponding intermediate files for the compiled files.

And only then the PVS-Studio.exe analyzer itself is launched to carry out the

analysis of those intermediate files and output a standard PVS-Studio's report you

can work with both from the Standalone version and any of the PVS-Studio IDE

plugins.

Getting started with CLMonitor.exeNote: in this section, we will discuss how to use CLMonitor.exe to integrate the

analysis into an automated build system. If you only to check some of your projects

manually, consider using the UI version of Standalone as described in the next

section.

CLMonitor.exe is a monitoring server directly responsible for monitoring compiler

launches. It must be launched prior to the project build process. After launching the

server in monitoring mode, it will trace the invocations of supported compilers.

The supported compilers are:

Microsoft Visual C++ (cl.exe) compilers

C/C++ compilers from GNU Compiler Collection (gcc.exe, g++.exe)

Page 185: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Clang (clang.exe) compiler and other Clang-based compilers (for example Borland C++ 64 - bcc64.exe)

32-bit Borland C++ (bcc32.exe) compiler

But if you want the analysis to be integrated directly into your build system (or a

continuous integration system and the like), you can't "just" launch the monitoring

server because its process blocks the flow of the build process while active. That's

why you need to launch CLMonitor.exe with the monitor argument in this case:

CLMonitor.exe monitor

In this mode, CLMonitor will launch itself in the monitoring mode and then

terminate, while the build system will be able to continue its work. At the same

time, the second CLMonitor process (launched from the first one) will stay running

and monitoring the build process.

Since there are no consoles attached to the CLMonitor process in this mode, the

monitoring server will - in addition to the standard stdin\stdout threads - output its

messages into a Windows event log (Event Logs -> Windows Logs -> Application).

Note: for the monitoring server to run correctly, it must be launched with the same

privileges as the compiler processes themselves.

To ensure correct logging of messages in the system event logs, you need to launch

the CLMonitor.exe process with elevated (administrative) privileges at least once. If

it has never been launched with such privileges, it will not be allowed to write the

error messages into the system log.

Notice that the server only records messages about its own runtime errors (handled

exceptions) into the system logs, not the analyzer-generated diagnostic messages!

Once the build is finished, run CLMonitor.exe in the client mode so that it can

generate the preprocessed files and call the static analyzer itself:

CLMonitor.exe analyze -l "c:\test.plog"

As the -l argument, the full path to the analyzer's log file must be passed.

Page 186: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

When running as a client, CLMonitor.exe will connect to the already running server

and start generating the preprocessed files. The client will receive the information

on all of the compiler invocations that were detected and then the server will

terminate. The client, in its turn, will launch preprocessing and PVS-Studio.exe

analyzer for all the source files which have been monitored.

When finished, CLMonitor.exe will save a log file (C:\test.plog) which can be

viewed in any PVS-Studio IDE plugin or the Standalone version (PVS-Studio ->

Open/Save -> Open Analysis Report).

You can also use the analyzer message suppression mechanism with CLMonitor

through the -u argument:

CLMonitor.exe analyze -l "c:\ptest.plog" -u "c:\ptest.suppress" -s

The -u argument specifies a full path to the suppress file, generated through the

'Message Suppression' dialog in Standalone (Tools|Message Suppression...). The

optional -s argument allows you to append the suppress file specified through the -u

with newly generated messages from the current analysis run.

Compiler monitoring from StandaloneFor the "manual" check of individual projects with CLMonitor, you can use the

interface of the Standalone version which can be launched from the Start menu

(PVS-Studio -> Standalone).

To start monitoring, open the dialog box: Tools -> Analyze Your Files... (Figure 1):

Page 187: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Figure 1 - The compiler monitoring start dialog box

Click "Start Monitoring" button. CLMonitor.exe process will be launched and the

environment main window will be minimized.

Start building your project, and when it's done, click the "Stop Monitoring" button

in the bottom right-hand corner of the window (Figure 2):

Figure 2 - The monitoring management dialog box

If the monitoring server has successfully tracked all the compiler launches, the

preprocessed files will be generated first and then they will be analyzed. When the

analysis is finished, you will see a standard PVS-Studio's report (Figure 3):

Page 188: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Figure 3 - The resulting output of the monitoring server and the analyzer

The report can be saved as an XML file (a .plog file): File -> Save PVS-Studio Log

As...

ConclusionDespite the convenience of the "seamless" analysis integration into the automated

build process (through CLMonitor.exe) employed in this mode, one still should

keep in mind the natural restrictions inherent in this mode - particularly, that a

100% capture of all the compiler launches during the build process is not

guaranteed, which failure may be caused both by the influence of the external

environment (for example antivirus software) and the hardware-software

environment specifics (for example the compiler may terminate too quickly when

running on an SSD disk while CPU's performance is too low to "catch up with" this

launch).

That's why we recommend you to provide whenever possible a complete

integration of the PVS-Studio static analyzer with your build system (in case you

use a build system other than MSBuild) or use the corresponding PVS-Studio IDE

plugin.

Page 189: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Mass Suppression of Analyzer Messages

Operation principles

Utilizing message suppression

Sometimes, during deployment of static analysis, especially at large-scale projects,

the developer has no desire (or even has no means of) to correct hundreds or even

thousands of analyzer's messages which were generated on the existing source code

base. In this situation, the need arises to "suppress" all of the analyzer's messages

generated on the current state of the code, and, from that point, to be able to see

only the messages related to the newly written or modified code. As such code was

not yet thoroughly debugged and tested, it can potentially contain a large number of

errors.

If instead you want to hide only some individual messages (false positives for

example), the false alarm suppression feature should be used instead. You should

also remember that each individual type of diagnostic or an isolated group of

analyzer messages could be hidden by utilizing the message filtering feature of the

PVS-Studio output window.

Operation principlesMessage suppression is based upon utilization of a special analyzer "message base"

files (the files with suppress extension), which are located beside project files of

your IDE (for example, vcproj and vcxproj files of Microsoft Visual Studio) or are

added to project files as noncombilable items. These files contain analyzer

messages marked as "suppressed" (marking analyzer messages through IDE plug-in

interface will be described in the next section).

On every subsequent analysis of such a project by PVS-Studio, its IDE plug-in will

be watching for such suppression files and in case such file is found, the messages

contained in the "base file" will not appear in the analyzer's output. It should be also

noted that modifying the source file upon which the messages were generated, the

Page 190: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

displacement of code lines in particular, would not result in the re-appearance of

these messages. Only by modifying the code line at which the message was

originally generated will make the message reappear, as such a message becomes

the "new" one.

The "suppression base" files are stored in the simple XML format, which allows

you to easily read\modify them, or even utilize these files at the level of your team

through the revision control software.

Utilizing message suppressionTo suppress analyzer messages you will first need to run your projects through the

analyzer (PVS-Studio -> Check -> Solution). Next, open 'Analyzer Message

Suppression' window through the 'PVS-Studio -> Suppress Messages...' menu item

Figure 1 - message suppression

Page 191: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Click the 'Suppress Messages' button to mark all analyzer messages in the output

window. After confirmation all of the messages will be appended to the 'suppess'

base files for the corresponding projects. The 'Suppression Message Base Files in

Current Solution' list show all of the 'suppress' files for the solution that is currently

open in Visual Studio. Individual suppression files can be deleted by selecting them

and pressing the 'Delete Selected Files' button.

After the file is generated, you can add it to the corresponding project as

noncompilable\text item with 'Add|Existing Item...' menu command. If the project

includes at least one suppress file, the files besides the project file itself are ignored.

Addign suppress files to projects allows you to keep suppress files and project files

in different directories. Only one suppress file per project is supported - other will

be ignored.

Suppressed messages will not appear in the output window during subsequent

analyzer runs, which will allow you to concentrate more on a newly written code.

However, despite these messages not appearing on the list, they are actually still in

there.

To enable the display of the messages marked as 'suppressed', use the 'Display

Suppressed Messages in PVS-Studio Output Window' checkbox (figure 1). The

suppressed message will be displayed in the list as strikethrough ones. You can un-

mark the message by 'Un-Suppress Selected Messages' item from the context menu.

Page 192: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Figure 2 - removing messages from "suppressed"

The 'suppression' mark will be removed from the selected messages, and these

messages themselves will be removed from the 'suppress' base files, if the

corresponding project is opened inside the IDE.

Standalone

Introduction

Analyzing source files with the help of the compiler process monitoring system

Working with the list of diagnostic messages

Navigation and search in the source code

IntroductionPVS-Studio can be used independently from the Visual Studio IDE. The core of the

analyzer is a command-line utility allowing analysis of C/C++ files that can be

compiled by Visual C++, Borland (Embarcadero) C++, GCC, or Clang. For this

reason, we developed a standalone application implemented as a shell for the

command-line utility and simplifying the work with the analyzer-generated message

log.

PVS-Studio provides a convenient plug-in for the Visual Studio environment,

allowing "one-click" analysis of this IDE's vcproj/vcxproj-projects. There are,

however, a few other build systems out there which we also should provide support

for. Although PVS-Studio's analyzer core doesn't depend on any particular format

used by this or that build system (such as, for example, MSBuild, GNU Make,

NMake, CMake, ninja, and so on), the users would have to carry out a few steps on

their own to be able to integrate PVS-Studio's static analysis into a build system

other than VCBuild/MSBuild projects supported by Visual Studio. These steps are

as follows:

1. First, the user would need to integrate a call to PVS-Studio.exe directly into the build script (if available) of a particular system. Otherwise, the user will need to modify the build system itself. To learn more about it,

Page 193: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

read this documentation section. It should be noted right off that this way of using the analyzer is not always convenient or even plausible as the user is not always permitted to modify the build script of the project they are currently working with.

2. After PVS-Studio's static analysis has been integrated into the build system, the user needs to somehow view and analyze the analyzer's output. This, in its turn, may require creating a special utility to convert the analyzer's log into a format convenient for the user. Note that when you have Visual Studio installed, you can at any time use the PVS-Studio plug-in for this IDE to view the report generated by the analyzer's core.

3. Finally, when the analyzer finds genuine bugs in the code, the user needs a functionality enabling them to fix those bugs in the source files of the project under analysis.

All these issues can be resolved by using the Standalone tool.

Figure 1 - Standalone

Standalone enables "seamless" code analysis regardless of the compiler or build

system one is using, and then allows you to work with the analysis results through a

user interface similar to that implemented in the PVS-Studio plug-in for Visual

Studio. The Standalone version also allows the user to work with the analyzer's log

obtained through direct integration of the tool into the build system when there is no

Visual Studio installed. These features are discussed below.

Page 194: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Analyzing source files with the help of the compiler process monitoring systemStandalone provides a user interface for a compilation monitoring system. The

monitoring system itself (the console utility CLMonitor.exe) can be used

independently of the Standalone version - for example when you need to integrate

static analysis into an automated build system. To learn more about the use of the

compiler monitoring system, see this documentation section.

To start monitoring compiler invocations, open the corresponding dialog: Tools ->

Analyze Your Files... (Figure 2):

Figure 2 - Build process monitoring start dialog

Click on "Start Monitoring". After that, CLMonitor.exe will be called while the

main window of the tool will be minimized.

Run the build and after it is finished, click on the "Stop Monitoring" button in the

window in the bottom right corner of the screen (Figure 3):

Page 195: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Figure 3 - Compiler monitoring dialog

If the monitoring server has successfully tracked the compiler invocations, static

analysis will be launched for the source files. When it is finished, you will get a

regular PVS-Studio's analysis report (Figure 4):

Figure 4 - Results of the monitoring server's and static analyzer's work

The analysis results can be saved into an XML file (with the plog extension) for

further use through the menu command 'File -> Save PVS-Studio Log As...'.

Working with the list of diagnostic messagesOnce you have got the analysis report with the analyzer-generated warnings, you

can start viewing the messages and fixing the code. You can also load a report

obtained earlier into the Standalone version. To do this, use the menu command

'File|Open PVS-Studio Log...'.

Various message suppression and filtering mechanisms available in this utility are

identical to those employed in the Visual Studio plug-in and are available in the

settings window 'Tools|Options...' (Figure 5).

Page 196: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Figure 5 - Analysis settings and message filtering mechanisms

In the Analyzer Output window, you can navigate through the analyzer's warnings,

mark messages as false positives, and add filters for messages. The message

handling interface in the Standalone version is identical to that of the output

window in the Visual Studio plug-in. To see a detailed description of the message

output window, see this documentation section.

Navigation and search in the source codeAlthough the built-in editor of the Standalone version does not provide a navigation

and autocomplete system as powerful and comfortable as Microsoft Intellisence in

the Visual Studio environment or other similar systems, Standalone still offers

several search mechanisms that can simplify your work with the analysis results.

Besides regular text search in a currently opened file (Ctrl + F), Standalone also

offers the Code Search dialog for text search in opened files and folders of the file

Page 197: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

system. This dialog can be accessed through the menu command 'Edit|Find &

Replace|Search in Source Files...' (Figure 6):

Figure 6 - Standalone's search dialog

The dialog supports search in the current file, all of the currently opened files, or

any folder of the file system. You can at any moment stop the search by clicking on

the Cancel button in the modal window that will show up after the search starts.

Once the first match is found, the results will start to be output right away into the

child window Code Search Results (Figure 7):

Page 198: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Figure 7 - Results of text search in project source files

Of course, regular text search may be inconvenient or long when you need to find

some identifier's or macro's declarations and/or uses. In this case, you can use the

mechanism of dependency search and navigation through #include macros.

Dependency search in files allows you to search for a character/macro in those

particular files that directly participated in compilation, or to be more exact, in the

follow-up preprocessing when being checked by the analyzer. To run the

dependency search, click on the character whose uses you want to find to open the

context menu (Figure 8):

Page 199: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Figure 8 - Dependency search for a character

The search results, just like with the text search, will be output into a separate child

window: 'Find Symbol Results'. You can at any moment stop the search by clicking

on the Cancel button in the status bar of the Standalone version's main window,

near the progress indicator.

Navigation through the #include macros allows you to open in the Standalone

version's code editor files added into the current file through such a macro. To open

an include macro, you also need to use the editor's context menu (Figure 9):

Page 200: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Figure 9 - Navigation through include macros

Keep in mind that information about dependencies is not available for every source

file opened in Standalone. When the dependencies base is not available for the

utility, the above mentioned context menu items will be inactive, too.

The dependencies base is created only when analysis is run directly from the

Standalone version itself. When opening a random C/C++ source file, the utility

won't have this information. Note that when saving the analyzer's output as a plog

file, this output having been obtained in the Standalone version itself, a special dpn

file, associated with the plog file and containing dependencies of the analyzed files,

will be created in the same folder. While present near the plog file, the dpn file

enables the dependency search when viewing the plog file in the Standalone

version.

Analyzer Work Statistics (Diagrams)

Introduction

Gathering analyzer launch statistics

Statistics filtering and representation in Microsoft Excel

Page 201: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

IntroductionThe PVS-Studio analyzer provides a work statistics gathering feature to see the

number of detected messages (including suppressed ones) across different severity

levels and rule sets. Gathered statistics can be filtered and represented as a diagram

in a Microsoft Excel file, showing the change dynamics for messages in the project

under analysis.

Gathering analyzer launch statisticsPVS-Studio can save launch statistics when analyzing source code through the

Microsoft Visual Studio plugin (supported in versions starting with Visual Studio

2010). To enable the statistics saving feature, use the Save Solution Statistics option

available on the Specific Analyzer Settings page which can be accessed through the

'PVS-Studio|Options...' menu item of the plugin.

The statistics are saved in the folder '%AppData%/PVS-Studio/Statistics'. For each

analyzed Visual Studio solution, an associated subfolder is created with the same

name. For each solution analysis launch, once the analysis is over, an individual

statistics file is created which contains the analysis results (when analyzing Visual

Studio projects from the command line, the statistics are also collected). The

statistics file contains the information about the number of output messages (both

new and old ones hidden by means of the message suppression mechanism) in each

PVS-Studio rule set (General Analysis, Optimization, 64-bit Analysis), for each

error and error severity level. Messages marked as false positives are not included

into the statistics.

Each Visual Studio solution analysis launch is saved into an xml.zip file, which is a

usual zip archive containing a simple-format xml file. Thanks to the open format,

you can interpret these files on your own or use the PVS-Studio plugin's UI, which

is described in detail further in this article.

Statistics filtering and representation in Microsoft Excel

Page 202: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

PVS-Studio provides an interface to filter the gathered analysis launch statistics and

represent them by means of Microsoft Excel.

To use this dialog, you need to have Microsoft Excel (2007 or better) installed on

your computer as well as the Visual Studio Tools for Office runtime (installed

together with the Visual Studio IDE by default).

You can open the statistics filtering dialog by clicking on the 'PVS-Studio|Analysis

Statistics...' menu item (also available in Standalone):

Figure 1 - PVS-Studio analyzer launch statistics filtering dialog

The 'Include Suppressed Messages' checkbox allows showing/hiding suppressed

analyzer messages. Messages disabled on the Detectable Errors (PVS-Studio|

Options...) settings page are also filtered off when making the Excel document (but

xml.zip statistics files themselves contain the complete information about all the

error codes).

The PVS-Studio statistics filtering dialog includes only the "freshest" data per day.

That is, if you ran analysis several times during the day, only the latest statistics file

will be used (it is specified in the xml statistics file). However, the complete

Page 203: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

statistics are saved for every launch and can be found in the

folder '%AppData%/PVS-Studio/Statistics/%SolutionName%', if necessary.

Once you have selected the required solutions in the list, set up the filters, and

specified the time span you want to see the statistics for, an Excel document with

the corresponding statistics data is created and can be opened by clicking on the

'Show in Excel' button (Figure 2).

Figure 2 - Statistics across message rule sets

The 'statistics across message rule sets' diagram shows the change dynamics for the

total number of messages for each of the analyzer's rule sets, according to the filters

set up previously.

Though opened through the PVS-Studio dialog, these diagrams are ordinary Excel

documents providing the complete functionality of Excel's interface (filtering,

scaling, etc.) and can be saved for further use.

Installing and updating PVS-Studio on Linux

Page 204: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Install from repositories

o For debian-based systems:

o For yum-based systems:

o For zypper-based systems:

Manual installation

o Deb package

o Rpm package

o Archive

PVS-Studio is distributed as Deb/Rpm packages or an archive. Using the

installation from the repository, you will be able to receive updates about the release

of a new version of the program.

The distribution kit includes the following files:

pvs-studio - the kernel of the analyzer;

pvs-studio-analyzer - a utility for checking projects without integration;

plog-converter - a utility for converting the analyzer report to different formats;

plog-converter-source.tgz - the source code of the plog-converter utility.

You can install the analyzer using the following methods:

Install from repositoriesFor debian-based systems:wget -q -O - http://files.viva64.com/etc/pubkey.txt | \

sudo apt-key add -

sudo wget -O /etc/apt/sources.list.d/viva64.list \

http://files.viva64.com/etc/viva64.list

Page 205: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

sudo apt-get update

sudo apt-get install pvs-studio

For yum-based systems:wget -O /etc/yum.repos.d/viva64.repo \

http://files.viva64.com/etc/viva64.repo

yum update

yum install pvs-studio

For zypper-based systems:sudo zypper ar -f http://files.viva64.com/rpm viva64

sudo zypper update

sudo zypper install pvs-studio

Manual installationDeb packagesudo gdebi pvs-studio-VERSION.deb

or

sudo dpkg -i pvs-studio-VERSION.deb

sudo apt-get -f install

Rpm package$ sudo dnf install pvs-studio-VERSION.rpm

or

sudo zypper install pvs-studio-VERSION.rpm

or

Page 206: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

sudo yum install pvs-studio-VERSION.rpm

Archivetar -xzf pvs-studio-VERSION.tgz

sudo ./install.sh

How to run PVS-Studio on Linux

Introduction

Installing and updating PVS-Studio

Supported versions of GCC and Clang

Quick run

o CMake-project

o CMake/Ninja-project

o Any project

o If you use cross compilers

Configuration file *.cfg

Preprocessor parameters

Integration of PVS-Studio into a build system

o Setting up PVS-Studio for the source file analysis

o Setting up PVS-Studio to analyze a preprocessed file

o Integration into Makefile/Makefile.am

o Integration into CMake/CLion/QtCreator

o Integration into QMake

Integration of PVS-Studio with Continuous Integration systems

Filtering and viewing the analyzer report

Page 207: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

o Plog Converter Utility

o Usage

o Viewing the analyzer report in QtCreator

o Viewing the analyzer report in Vim/gVim

o Viewing the analyzer report in GNU Emacs

o Viewing the analyzer report in LibreOffice Calc

o Configuration file

o Adding custom output formats

o Compilation from the source code and set up

Common problems and their solutions

Conclusion

IntroductionPVS-Studio for Linux is a static code analysis console application for C/C++. To

work with this program you should have the GCC or Clang compiler installed,

the PVS-Studio.lic license file and the config file PVS-Studio.cfg. A new run of the

analyzer is performed for every code file. The analysis results of several source

code files can be added to one analyzer report or displayed in stdout.

The analyzer has two main modes in the Linux environment:

1. checking source code files (*.cpp, *.c and so on.);

2. checking the preprocessed files (* .i).

Installing and updating PVS-StudioExamples of commands to install the analyzer from the packages and repositories

are given on this page.

Supported versions of GCC and ClangStarting with the compiler version having the preprocessing support (flag - E), you

can use the PVS-Studio static code analyzer to check your files.

Page 208: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

By default the preprocessor is taken from the CC/CXX environment variables. If

they aren't set, then the program uses gcc and g++, or clang and clang++, available

through the PATH environment variable.

Quick runThe best way to use the analyzer is to integrate it into your build system, namely

near the compiler call. However, if you want to run the analyzer for a quick test on a

small project, use the pvs-studio-analyzer utility.

Important. The project should be successfully compiled and built before analysis.

CMake-project

To check the CMake-project we use the JSON Compilation Database format. To get

the file compile_commands.json necessary for the analyzer, you should add one flag

to the CMake call:

$ cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=On <src-tree-root>

Cmake supports the generation of a JSON Compilation Database for Unix

Makefiles.

The analysis starts with the following commands:

$ pvs-studio-analyzer analyze -l /path/to/PVS-Studio.lic

-o /path/to/project.log -e /path/to/exclude-path -j<N>

$ plog-converter -a GA:1,2 -t tasklist

-o /path/to/project.tasks /path/to/project.log

It is important to understand that all files to be analyzed should be compiled. If your

project actively uses code generation, then this project should be built before

analysis, otherwise there may be errors during preprocessing.

CMake/Ninja-project

To check the Ninja-project we use the JSON Compilation Database format. To get

the necessary file compile_commands.json for the analyzer, you must execute the

following commands:

Page 209: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

$ cmake -GNinja <src-tree-root>

$ ninja -t compdb

The analysis is run with the help of following commands:

$ pvs-studio-analyzer analyze -l /path/to/PVS-Studio.lic

-o /path/to/project.log -e /path/to/exclude-path -j<N>

$ plog-converter -a GA:1,2 -t tasklist

-o /path/to/project.tasks /path/to/project.log

Any project

This utility requires the strace utility.

This can be built with the help of the command:

$ pvs-studio-analyzer trace -- make

You can use any other build command with all the necessary parameters instead

of make, for example:

$ pvs-studio-analyzer trace -- make debug -j2

After you build your project, you should execute the commands:

$ pvs-studio-analyzer analyze -l /path/to/PVS-Studio.lic

-o /path/to/project.log -e /path/to/exclude-path -j<N>

$ plog-converter -a GA:1,2 -t tasklist

-o /path/to/project.tasks /path/to/project.log

Analyzer warnings will be saved into the specified project.tasks file. You may see

various ways to view and filter the report file in the section "Filtering and viewing

the analyzer report" within this document.

If your project isn't CMake or you have problems with the strace utility, you may

try generating the file compile_commands.json with the help of the Bear utility.

This file will help the analyzer to check a project successfully only in cases where

the environment variables don't influence the file compilation.

Page 210: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

If you use cross compilers

In this case, the compilers may have special names and the analyzer will not be able

to find them. To analyze such a project, you must explicitly list the names of the

compilers without the paths:

$ pvs-studio-analyzer trace -- ...

$ pvs-studio-analyzer analyze ... --compiler COMPILER_NAME

--compiler gcc --compiler g++ --compiler COMPILER_NAME

$ plog-converter ...

Also, when you use cross compilers, the directory with the header files of the

compiler will be changed. It's necessary to exclude such directories from the

analysis with the help of -e flag, so that the analyzer doesn't issue warnings for these

files.

$ pvs-studio-analyzer ... -e /path/to/exclude-path ...

There shouldn't be any issues with the cross compilers during the integration of the

analyzer into the build system.

Configuration file *.cfgDuring integration of the analyzer into the build system, you should pass it a

settings file (*.cfg). You may choose any name for the configuration file, but it

should be written with a "--cfg" flag.

The settings file with the name PVS-Studio.cfg, which is located in the same

directory as the executable file of the analyzer, can be downloaded automatically

without passing through the command-line parameters.

Possible values for the settings in the configuration file:

exclude-path (optional) specifies the directory whose files it is not necessary to check. Usually these are directories of system files or link libraries. There can be several exclude-path parameters.

platform (required) specifies the platform. Possible variants: linux32 or linux64.

Page 211: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

preprocessor (required) specifies the preprocessor. Possible variants: gcc or clang.

language (required) parameter specifies the version of the C/C++ languages that the analyzer expects to see in the code of the file to be analyzed (--source-file). Possible variants: C, C++. Incorrect setting of this parameter can lead to V001 errors, because every supported language variant has certain specific keywords.

lic-file (required) contains the absolute path to the license file.

analysis-mode (optional) defines the type of warnings. It is recommended that you use the value "4" (General Analysis, suitable for most users).

output-file (optional) parameter specifies the full path to the file, where the report of the analyzer's work will be stored. If this parameter is missing in the configuration file, all messages concerning the errors found will be displayed in the console.

sourcetree-root (optional) by default, during the generation of diagnostic messages, PVS-Studio issues absolute, full paths to the files, where PVS-Studio detected errors. Using this setting you can specify the root part of the path that the analyzer will automatically replace with a special marker. For example, the absolute path to the file /home/project/main.cpp will be replaced with a relative path |?|/main.cpp, if /home/project was specified as the root.

cl-params (optional) should contain original parameters of the source file compilation. Illegal parameters for the analyzer's duties are given in the documentation section "Preprocessor parameters"

source-file (required) contains the absolute path to the source file to be analyzed.

skip-cl-exe (optional) shows the analyzer that the preprocessing stage can be skipped, and the analysis can be started.

i-file (optional) contains the absolute path to the preprocessed file.

An important note:

You don't need to create a new config file to check each file. Simply save the

existing settings, for example, lic-file, etc. Modifiable parameters can be passed to

the analyzer directly, like this for example:

$ pvs-studio --cfg PVS-Studio.cfg --source-file

/home/user/Documents/src/source.cpp --cl-params -c -Wall

-std=c++11 /home/user/Documents/src/source.cpp

Page 212: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Preprocessor parametersThe analyzer checks not the source files, but preprocessed files. This method allows

the analyzer perform a more in-depth and qualitative analysis of the source code.

In this regard, we have several restrictions for the compilation parameters being

passed. These are parameters that hinder the compiler run in the preprocessor mode,

or damage the preprocessor output. For example, the "-o" parameter should not be

passed to --cl-params, because the analyzer redirects the preprocessor output to the

text file (preprocessed), instead of the binary object file. A number of debugging

and optimization flags, for example,-O2, -O3, -g3, -ggdb3 and others, create

changes which affect the preprocessor output. Information about invalid parameters

will be displayed by the analyzer when they are detected.

This fact does not presuppose any changes in the settings of project to be checked,

but part of the parameters should be excluded for the analyzer to run in properly.

Integration of PVS-Studio into a build systemSetting up PVS-Studio for the source file analysis

To check the source file in cases where there is no preprocessed file, it is necessary

to create a config file as follows:

exclude-path = /usr/include/

platform = linux64

preprocessor = gcc

analysis-mode=4

language = C++

lic-file = /home/user/Documents/PVS-Studio/PVS-Studio.lic

output-file = /home/user/Documents/src/project.log

cl-params = -c -Wall -std=c++11 /home/user/Documents/src/source.cpp

source-file = /home/user/Documents/src/source.cpp

Setting up PVS-Studio to analyze a preprocessed file

If you have already got the preprocessed file, you can start checking it right after

editing the configuration file as follows:

Page 213: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

exclude-path = /usr/include/

skip-cl-exe = yes

platform = linux64

preprocessor = gcc

analysis-mode=4

language = C++

lic-file = /home/user/Documents/PVS-Studio/PVS-Studio.lic

output-file = /home/user/Documents/src/project.log

source-file = /home/user/Documents/src/source.cpp

i-file = /home/user/Documents/src/source.i

Integration into Makefile/Makefile.am

The call of the executable file of the analyzer should be performed in the same

location as the compiler call. The compiler launch line should be fully duplicated

during the call of the analyzer with the flag --cl-params, except the output

parameters, for example -o:

.cpp.o:

$(CXX) $(CFLAGS) $(DFLAGS) $(INCLUDES) $< -o $@

pvs-studio --cfg $(CFG_PATH) --source-file $< --language C++

--cl-params $(CFLAGS) $(DFLAGS) $(INCLUDES) $<

Important notes:

1. The path to the source file that is present in the original compilation line must be duplicated with the flag --source-file;

2. The analyzer settings should not be duplicated in the command line parameters and the configuration file;

3. Checking of several files with one analyzer call is not supported.

Integration into CMake/CLion/QtCreator

You can use the PVS-Studio.cmake module for projects that use cmake. Suggested

content of CMakeLists.txt, using this module.

include(PVS-Studio.cmake)

pvs_studio_add_target(TARGET analyze ALL

Page 214: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

FORMAT tasklist

PREPROCESSOR gcc

LOG "/path/to/report.tasks"

ANALYZE main_target subtarget:path/to/subtarget

LICENSE "/path/to/PVS-Studio.lic"

CXX_FLAGS ${PREPROCESSOR_ADDITIONAL_FLAGS}

C_FLAGS ${PREPROCESSOR_ADDITIONAL_FLAGS}

CONFIG "/path/to/PVS-Studio.cfg")

All settings are optional. The ALL parameter indicates that the analysis starts when

you build the project (Build All).

This method supports incremental build: the resulting log will contain errors from

the latest versions of the files.

You can add an additional command that will convert the log to the required format

with the help of the plog-converter utility:

include(PVS-Studio.cmake)

pvs_studio_add_target(TARGET analyze

OUTPUT FORMAT errorfile

ANALYZE target

LOG target.plog

LICENSE "/path/to/PVS-Studio.lic")

Figure 1 shows an example of analyzer warnings viewed in CLion:

Page 215: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Figure 1 - PVS-Studio warnings viewed in CLion

Figure 2 demonstrates an example of analyzer warnings viewed in QtCreator:

Figure 2 - PVS-Studio warnings viewed in QtCreator

Integration into QMake

Page 216: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

You can use the PVS-Studio.pri file for qmake-projects. It works in a similar way to

the CMake-module described above.

pvs_studio.target = pvs

pvs_studio.output = true

pvs_studio.license = /path/to/PVS-Studio.lic

pvs_studio.cxxflags = -std=c++14

pvs_studio.sources = $${SOURCES}

include(PVS-Studio.pri)

Integration of PVS-Studio with Continuous Integration systemsAny of the following methods of integration of the analysis into a build system can

be automated in the system Continuous Integration. This can be done in Jenkins,

TeamCity and others by setting automatic analysis launch and notification of the

found errors.

Filtering and viewing the analyzer reportPlog Converter Utility

To convert the analyzer bug report to different formats (*.xml, *.tasks and so on)

you can use the Plog Converter, which can be found open source.

Usage

Enter the following in the command line of the terminal:

$ plog-converter [options] <path to the file with PVS-Studio log>

All options can be specified in random order.

Available options:

-t - utility output format.

-o - the path to the file that will be used for output. If it is missing, the output will be redirected to the standard output device.

Page 217: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

-s - path to the configuration file. The file is similar to the analyzer configuration file PVS-Studio.cfg. Information on the root of the project and the excluded directories (exclude-path) is taken from this file.

-r - a path to the project directory.

-a - set of filtered diagnostic rules. The full list: GA, 64, OP, CS. The can be used together with specified levels, for example: "-a GA:1,2;64:1;CS".

-d - a list of excluded diagnostics, separated by a space: "-dV595, V730".

-e - use stderr instead of stdout.

At this point, the available formats are:

xml;

csv  - file stores tabular data (numbers and text) in plain text;

errorfile is the output format of the gcc and clang;

tasklist  - an error format that can be opened in QtCreator.

The result of execution of the utility, is a file containing messages of a specified

format, filtered by the rules that are set in the configuration file.

Viewing the analyzer report in QtCreator

The following is an example of a command which would be suitable for most users,

for opening the report in QtCreator:

$ plog-converter -a GA:1,2 -t tasklist

-o /path/to/project.tasks /path/to/project.log

Figure 3 demonstrates an example of a .tasks file, viewed in QtCreator:

Page 218: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Figure 3 - A .tasks file viewed in QtCreator

Viewing the analyzer report in Vim/gVim

An example of commands to open the report in gVim editor:

$ plog-converter -a GA:1,2 -t errorfile

-o /path/to/project.err /path/to/project.log

$ gvim /path/to/project.err

:set makeprg=cat\ %

:silent make

:cw

The figure 4 demonstrates an example of viewing an .err file in gVim:

Page 219: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Figure 4-viewing the .err file in gVim

Viewing the analyzer report in GNU Emacs

An example of commands to open the report in Emacs editor:

$ plog-converter -a GA:1,2 -t errorfile

-o /path/to/project.err /path/to/project.log

$ emacs

M-x compile

cat /path/to/project.err 2>&1

Figure 5 demonstrates an example of viewing an .err file in Emacs:

Page 220: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Figure 5 - viewing the .err file in Emacs

Viewing the analyzer report in LibreOffice Calc

An example of commands to convert the report in CSV format:

$ plog-converter -a GA:1,2 -t csv

-o /path/to/project.csv /path/to/project.log

After opening the file project.csv in LibreOffice Calc, you must add the

autofilter:Menu Bar --> Data --> AutoFilter. Figure 6 demonstrates an example of

viewing an .csv file in LibreOffice Calc:

Page 221: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Figure 6-viewing an .csv file in LibreOffice Calc

Configuration file

More settings can be saved into a configuration file with the following options:

enabled-analyzers - an option similar to the -a option in the console string parameters.

sourcetree-root - a string that specifies the path to the root of the source code of the analyzed file. If set incorrectly, the result of the utility's work will be difficult to handle.

errors-off - globally disabled warning numbers that are enumerated with spaces.

exclude-path - a file, the path to which contains a value from this option, will not be initialized.

disabled-keywords- keywords. Messages, pointing to strings which contain these keywords, will be excluded from processing.

The option name is separated from the values by a '=' symbol. Each option is

specified on a separate string. Comments are written on separate strings; insert #

before the comment.

Adding custom output formats

Page 222: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

To add your own output format, follow these steps:

Create your own output class, making it an heir from the IOutput class, and redefine

the virtual method void write(const AnalyzerMessage& msg). Describe the message

output in the correct format for this method. The fields of

the AnalyzerMessage structure are defined in the analyzermessage.h file. The

following actions are the same as for the existing output classes (XMLOutput, for

example).

In OutputFactory::OutputFactory in m_outputs add your format by analogy with

the one that is already specified there. As a variant - add it through the

method OutputFactory::registerOutput.

The format will be available as the utility option -t after these actions.

Compilation from the source code and set up

To compile the utility, you'll need g++ 5.4 or higher, and CMake. No other

additional libraries are required.

mkdir build

cd build

cmake -DCMAKE_BUILD_TYPE=Release ...

make -j8

sudo make install

Common problems and their solutions1. The strace utility issues the following message:

strace: invalid option -- 'y'

You must update the strace program version. Analysis of a project without

integrating it into a build system is a complex task, this option allows the analyzer

to get important information about the compilation of a project.

2. The strace utility issues the following message:

Page 223: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

strace: umovestr: short read (512 < 2049) @0x7ffe...: Bad address

Such errors occur in the system processes, and do not affect the project analysis.

3. The strace utility issues the following message:

No compilation units found

The analyzer could not find files for analysis. Perhaps you are using cross compilers

to build the project. See the section "If you use cross compilers" in this

documentation.

4. The analyzer report has strings like this:

r-vUVbw<6y|D3 h22y|D3xJGy|D3pzp(=a'(ah9f(ah9fJ}*wJ}*}x(->'2h_u(ah

The analyzer saves the report in the intermediate format. To view this report, you

must convert it to a readable format using a plog-converter utility, which is installed

together with the analyzer.

5. The analyzer issues the following error:

Incorrect parameter syntax:

The ... parameter does not support multiple instances.

One of the parameters of the analyzer is set incorrectly several times.

This can happen if part of the analyzer parameters are specified in the configuration

file, and part of them were passed through the command line parameters. At the

same time, some parameter was accidentally specified several times.

If you use pvs-studio-analyzer, then almost all the parameters are detected

automatically, this is why it can work without a configuration file. Duplication of

such parameters can also cause this error.

6. The analyzer issues the warning:

V001 A code fragment from 'path/to/file' cannot be analyzed.

Page 224: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

If the analyzer is unable to parse some code fragment, it skips it and issues the V001

warning. Such a situation doesn't influence the analysis of other files, but if this

code is in the header file, then the number of such warnings can be very high. Send

us a preprocessed file (.i) for the code fragment, causing this issue, so that we can

add support for it.

ConclusionIf you have any questions or problems with running the analyzer, feel free to contact

us.

Integration of PVS-Studio analysis results into SonarQube

Introduction

Installation and usage of sonar-pvs-studio-plugin

o Supported versions of SonarQube

o Creating a Quality Profile and adding diagnostics

o Running code analysis and importing the results into SonarQube

o Using filters for the message analysis

Sonar-pvs-studio-plugin support in the PVS-Studio_Cmd.exe command line tool

o An example of SonarQube scanner configuration file

Restrictions

Introduction

Page 225: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

SonarQube is an open source platform for continuous inspection of code quality. It

supports a large variety of programming languages and allows getting the reports on

such metrics as code duplication, compliance with coding standards, test coverage,

code complexity, potential bugs and so on. SonarQube provides comfortable

visualization of analysis results and tracking the project development dynamics in

real-time.

The SonarQube homepage is presented in the figure 1:

Figure 1. SonarQube home page.

The demonstration of the SonarQube abilities is available by the

address https://sonarqube.com.

PVS-Studio provides a plugin for SonarQube to import the analysis results - sonar-

pvs-studio-plugin. Using the plugin allows you to import issues found by PVS-

Studio Analyzer to the SonarQube server's base. With the help of the SonarQube

Web interface, you can filter the analyzer messages, navigate the source code for

closer inspection of the potential error, assign tasks and monitor its progress,

analyze the dynamics of error quantity and assess the code quality of the project.

Installation and usage of sonar-pvs-studio-pluginPVS-Studio can be integrated with the SonarQube platform only if you have an Enterprise license.

Page 226: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Please contact us to order a license.

The instructions on the installing and running the SonarQube server are available

at Installing the Server. After installing the SonarQube server, copy the file sonar-

pvs-studio-plugin.jar from the directory, where PVS-Studio is installed to the

directory $SONARQUBE_HOME\extensions\plugins. After that, restart SonarQube

server.

Supported versions of SonarQube

The sonar-pvs-studio-plugin plugin supports SonarQube starting with version 4.5.7

and higher.

Creating a Quality Profile and adding diagnostics

Quality Profile is a collection of diagnostics that are executed during the analysis.

After the installation of the plugin, create a Quality Profile, containing PVS-Studio

diagnostics:

Log in to the SonarQube server as a sonar-administrator.

Go to the tab Quality Profiles:

Press Create to create a new profile, enter the profile name and select C/C++/C# language:

If you want to use PVS-Studio profile as a default for the analysis of all C/C++ and C# projects, press Set as Default.

Page 227: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

After creating the profile, we should add diagnostic rules to it. To do it, go to the Rules tab, choose the filter Repository and activate PVS-Studio repository:

To add all the rules from the repository to the Quality Profile, press Bulk Change and choose Activate In.... In the dialog window, choose the created PVS-Studio profile and press Apply.

If we want to assign a Quality Profile manually for the project, go to the section Quality Profiles of the project administration settings and choose the created profile for C/C++/C# languages:

The repository, containing the descriptions of analyzer diagnostics, is embedded

inside the plugin. Every new release of PVS-Studio may contain new diagnostics

Page 228: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

added, that's why update the plugin version on the SonarQube server and add new

diagnostics to the PVS-Studio Quality Profile.

The SonarQube Web API allows you to automate this process. Suppose that your

build server performs PVS-Studio update automatically (refer to the Deployment of

PVS-Studio in large teams article to learn how to accomplish this). To update

the sonar-pvs-studio-plugin plugin and add new diagnostics to Quality Profile

without using the web interface, perform the following steps:

Copy the sonar-pvs-studio-plugin.jar file from the PVS-Studio installation directory to the $SONARQUBE_HOME\extensions\plugins directory.

Restart the SonarQube server.

Assume that the SonarQube server installed in the C:\Sonarqube\ directory and

started as a Windows service. PVS-Studio installed in the C:\Program Files (x86)\

PVS-Studio\ directory. Than a script for automatic update of PVS-Studio and

the sonar-pvs-studio-plugin plugin will be as follows:

set PVS-Studio_Dir="C:\Program Files (x86)\PVS-Studio"

set SQDir="C:\Sonarqube\extensions\plugins\"

rem Update PVS-Studio

cd /d "C:\temp\"

xcopy %PVS-Studio_Dir%\PVS-Studio-Updater.exe . /Y

call PVS-Studio-Updater.exe /VERYSILENT /SUPPRESSMSGBOXES

del PVS-Studio-Updater.exe

rem Stop the SonarQube server

sc stop SonarQube

rem Wait until the server is stopped

ping -n 60 127.0.0.1 >nul

xcopy %PVS-Studio_Dir%\sonar-pvs-studio-plugin.jar %SQDir% /Y

sc start SonarQube

Page 229: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

rem Wait until the server is started

ping -n 60 127.0.0.1 >nul

Find a key of a Quality Profile, where new diagnostics should be activated. You can obtain this key by using the api/qualityprofiles/search GET request, for example (in one line):

curl http://localhost:9000/api/qualityprofiles/search

-v -u admin:admin

The server reply has the following format:

{

"profiles": [

{

"key":"c++-sonar-way-90129",

"name":"Sonar way",

"language":"c++",

"languageName":"c++",

"isInherited":false,

"isDefault":true,

"activeRuleCount":674,

"rulesUpdatedAt":"2016-07-28T12:50:55+0000"

},

{

"key":"c-c++-c-pvs-studio-60287",

"name":"PVS-Studio",

"language":"c/c++/c#",

"languageName":"c/c++/c#",

"isInherited":false,

"isDefault":true,

"activeRuleCount":347,

"rulesUpdatedAt":"2016-08-05T09:02:21+0000"

}

]

}

Page 230: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Assume that we need to add new diagnostics to the PVS-Studio profile. Its key is c-

c++-c-pvs-studio-60287.

Execute the api/qualityprofiles/activate_rules POST request and specify the profile_key (mandatory) and tags (optional) parameters. The mandatory parameter profile_key defines a quality profile in SonarQube, where diagnostics will be activated. In our example this parameter has a value of c-c++-c-pvs-studio-60287.

Please note that the profile key may contain special characters so that it is required to perform an URL encoding. In our example the profile key c-c++-pvs-studio-60287 should be converted to c-c%2B%2B-c-pvs-studio-60287.

In the tags parameter pass tags of the diagnostics that need to be activated in the

profile. To activate all diagnostics, use the pvs-studio tag.

The POST request that allows you to add all the diagnostics to the PVS-Studio

profile is shown below (in one line):

curl --request POST -v -u admin:admin -data

"profile_key=c-c%2B%2B-c-pvs-studio-60287&tags=pvs-studio"

http://localhost:9000/api/qualityprofiles/activate_rules

Running code analysis and importing the results into SonarQube

The analysis process of the source code with SonarQube is described in the

article Analyzing Source Code. To import the results of PVS-Studio analysis,

specify in the sonar.pvs-studio.reportPath property a path to the .plog file, for

example (in one line):

sonar-scanner.bat

-Dsonar.projectKey=org.sonarqube:ABackup.sln

-Dsonar.projectName=ABackup

-Dsonar.projectVersion=1.0

-Dsonar.pvs-studio.reportPath=

D:\\SelfTester\\src\\ABackup\\ABackup\\ABackup.plog

SonarQube scanner calls the sonar-pvs-studio-plugin, that will write the analysis

results from the .plog-file to the SonarQube database.

Page 231: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The messages marked as false alarms in the .plog file won't be imported to

SonarQube.

By default the SonarQube server deletes messages that have been closed for 30

days. We suggest disabling this option, so that after quite a long time period (a year,

for example) you can check how many issues reported by PVS-Studio, have been

fixed.

Using filters for the message analysis

All PVS-Studio messages added to the SonarQube, have a Bug type.

In PVS-Studio static analyzer, diagnostics are split into four groups: General

analysis, diagnosis of micro-optimizations, diagnostics of 64-bit errors and

diagnostics that are implemented at the request of users. Additional group is the

messages related to the problems during the work of the analyzer.

In the SonarQube we kept the division of the messages by the diagnostic groups by

using tags. Here are the PVS-Studio diagnostics and their tags in SonarQube:

A group of diagnostics in PVS-Studio Tag in Sonarqube

General-analysis diagnostics pvs-studio#ga

Micro-optimizations diagnostics pvs-studio#op

Diagnosis of 64-bit errors pvs-studio#64

Customer specific requests pvs-studio#cs

Problems related to the code analyzer pvs-studio#fails

Thus, to view the messages, for example from the group "General-analysis

diagnostics", choose the tag pvs-studio#ga. Choose tag pvs-studio to display all the

PVS-Studio analyzer messages.

PVS-Studio messages have the following severity levels: High, Medium, Low, and

Fails. In SonarQube these are Critical, Major, Minor and Info levels respectively.

For example, to view all the messages from the group "diagnostics of general

Page 232: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

analysis" with the High severity level, choose the tag pvs-studio#ga and the level of

messages (Severity) Critical in SonarQube.

If the source or header file is included in several projects (modules, according to

SonarQube terminology), the messages about the errors in this file will be added to

every module in SonarQube. This will increase the total number of errors and cause

difficulties during the analysis and correction of these errors. For example, one

header file is included into two projects. The message about an error in this file is

displayed in two project modules in SonarQube. The developer fixes an issue in one

module, marks it as "fixed" and moves on to the correction of the same problem in

the same file, which is displayed in a different module. The message has a status

"Open" and the developer may need some time to realize that this error is already

fixed and this is just a duplicate message.

We decided to exclude a possibility of adding duplicate messages in sonar-pvs-

studio-plugin to avoid such situations. The messages get added for the first file, that

is indexed by a SonarQube scanner. If this file is seen in other modules during the

import, the messages won't be added.

Sonar-pvs-studio-plugin support in the PVS-Studio_Cmd.exe command line toolTo import PVS-Studio analysis results into SonarQube, you should install the

plugin sonar-pvs-studio-plugin, add PVS-Studio diagnostics from the plugin

repository to Quality Profile and pass the path to the .plog-file in the

property sonar.pvs-studio.reportPath when launching the SonarQube scanner.

To analyze MSBuild projects, the SonarQube developers recommend

using SonarQube Scanner for MSBuild. This scanner is a wrapper for a

standard SonarQube scanner, which simplifies the process of creating the sonar-

project.properties configuration file for the scanner, by automatically adding

modules to it (projects in solution) and writing the paths to the source files to be

analyzed.

Page 233: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

However, we've discovered important, from our point of view, limitations of the

SonarQube Scanner for MSBuild.

Firstly, when analyzing C/C++ projects, this scanner will add to the list of files to

analyze only those files that are added through the

properties ClCompile and ClInclude of the .vcxproj project file. If, for example, the

header file isn't explicitly included into the project and included from the code of

one of the source files, then this file will be ignored and the analysis results will not

be displayed in SonarQube for this file.

Secondly, SonarQube Scanner for MSBuild doesn't add the source files for analysis,

if they are located higher up in the directory tree, than the directory where this

project file itself is located. Messages for such files will also be missing in

SonarQube.

Based on these limits, we recommend using a standard scanner SonarQube for the

import of PVS-Studio analysis results. Use of this scanner presupposes creating a

configuration file sonar-project.properties manually. Using and setting the scanner

is described in the article Analyzing with SonarQube Scanner.

By default, SonarQube scanner indexes the source files for analysis, when they are

located in the directory tree lower than the solution file (.sln) or the project file

(.vcxproj/.csproj). To perform the analysis of projects with complex structure,

where the source files can be located higher in the directory tree, than the solution

file or the project, set the common parent directory for all the source files (in

extreme cases this can be the root of a disk); in the

property sonar.sources, enumerate the directories where there will be the source

files for analysis (or specify full paths to the source files).

The process of adding source files paths to sonar.sources property for large projects

can be rather time consuming, and quite non-trivial, taking into account all the

included header files. To make this task easier, we created the support mode of

configuration files of SonarQube scanner in the command line module PVS-

Studio_Cmd.exe. To activate this mode, run PVS-Studio_Cmd.exe module with the

argument --sonarqubedata (or -q). In this mode the analyzer will write paths to all

Page 234: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

the source and header files involved in the analysis to the sonar-

project.properties file. The file sonar-project.properties is located in the working

directory, from where the PVS-Studio_Cmd.exe tool was run from. If the file was

absent at the time of the tool launch, it will be created. Otherwise, the contents of

the sonar.sources property will be appended with new paths to the files; the existing

values will be preserved.

If the property sonar.projectBaseDir isn't set in the sonar-project.properties file, the

module PVS-Studio_Cmd.exe will specify a common directory for all the source

files and write it to the config file.

If you want PVS-Studio_Cmd.exe to modify the contents of the sonar-

project.properties file, do not turn on the support mode of the scanner SonarQube in

PVS-Studio_Cmd.exe and edit the contents of this file manually.

An example of SonarQube scanner configuration file

Let's look at the example of a configuration file sonar-project.properties:

sonar.projectKey=org.sonarqube:Sample.sln

sonar.projectName=Sample

sonar.projectVersion=1.0

sonar.pvs-studio.reportPath=D:\\Sample\\Sample.plog

sonar.host.url=http://localhost:9000

sonar.projectBaseDir=d:\\

sonar.sources=\

d:\\sample\\deltemp.cpp,\

d:\\lib\\cpplib\\fxfix.h,\

d:\\lib\\cpplib\\kitcpp.h,\

d:\\lib\\cpplib\\textmsgs.h,\

d:\\sample\\abackup.h,\

d:\\sample\\deltemp.h

Restrictions

Page 235: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

1. All the source files for the analysis should be located on a single logical disk. This restriction is imposed by the SonarQube platform. The source files, located on the disks, that are different from the disk, specified in the sonar.projectBaseDir property, will not be indexed and the messages, found in these files will be ignored.

2. If the modules are specified in the sonar-project.properties, then PVS-Studio_Cmd.exe won't update the list of files for the analysis, defined in the sonar.sources property. This restriction is related to the fact that the user can specify random module names in the sonar.modules property, and in general case it will be impossible to match these names with the projects from the solution. Upon an attempt to update this file, PVS-Studio_Cmd.exe will issue a warning ''SonarQube modules definition was found in the sonar-project.properties file. A list of source files for analysis will not be updated. Refer to PVS-Studio documentation for more details.''.

3. Using sonar-pvs-studio-plugin with another plugins for the analysis of C/C++ or C# code. SonarQube platform doesn't allow using two or more plugins, checking files with identical extensions during the analysis of one project.

Managing the Analysis Results (.plog file)

Converting the analysis results

Notifying the developer team

Summary

The analysis results that PVS-Studio generates as its output after it has finished

checking a project (either from the Visual Studio plugin or in command-line batch

mode) are typically presented as an XML log file (".plog"). You can view this file

in the PVS-Studio plugin for Visual Studio or in the Standalone version. This

format, however, is not convenient for viewing the file directly in a text editor,

sending it via email, and so on. PVS-Studio package comes with a number of

utilities that allow you to manage plog file in a number of ways.

Converting the analysis resultsWhen opening log file in a text editor, a user has to deal with XML markup. To

convert the analysis results into a more convenient format, use PlogConverter

utility, which comes with PVS-Studio and can be found in the PVS-Studio

Page 236: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

installation directory ("C:\Program Files (x86)\PVS-Studio" by default). Use the "'--

help" option to display the basic information about the utility:

PlogConverter.exe --help

Let's take a closer look at the utility's parameters:

--renderTypes (or -r): defines the possible formats into which the ".plog" file can be converted. The supported formats are HTML, Totals, Txt, Csv, and Plog. When not defined explicitly, the log file will be converted into all of these formats.

Html: converts the log file into an html file (convenient for automatic delivery to addressees on your mailing list).

Txt: converts the log file into a text file.

Csv: converts the log file into a comma-separated file (convenient for viewing in Microsoft Excel, for example).

Totals: outputs the statistics on the issued warnings across the types (GA, OP, 64, CS) and severity levels.

Plog: merges several log files into one.

You can combine different format options by separating them with ',' (no spaces),

for example:

PlogConverter.exe Drive:\Path\To\Plog --renderTypes=Html,Csv,Totals

or

PlogConverter.exe Drive:\Path\To\Plog -r Html,Csv,Totals

--analyzer (or -a): filters the warnings by a specified mask. The mask format:

MessageType:MessageLevels

"MessageType" can be set to one of the following types: GA, OP, 64, CS, Fail

"MessageLevels" can be set to values from 1 to 3

You can combine different masks by separating the options with ";" (no spaces), for

example (written in one line):

Page 237: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

PlogConverter.exe Drive:\Path\To\Plog --renderTypes=Html,Csv,Totals

--analyzer=GA:1,2;64:1

or

PlogConverter.exe Drive:\Path\To\Plog -r Html,Csv,Totals

-a GA:1,2;64:1

The command format reflects the following logic: convert ".plog" into Html, Csv,

and Totals formats, keeping only the general-analysis warnings (GA) of the 1-st and

2-nd levels and 64-bit warnings (64) of the 1-st level.

--excludedCodes (or -e): creates a list of warnings (separated with ",") that shouldn't be included into the resulting log file. For example, you don't want the V101, V102, and V200 warnings to be included (written in one line):

PlogConverter.exe Drive:\Path\To\Plog --renderTypes=Html,Csv,Totals

--excludedCodes=V101,V102,V200

or

PlogConverter.exe Drive:\Path\To\Plog -r Html,Csv,Totals

-d V101,V102,V200

--settings (or -s): defines the path to the PVS-Studio configuration file. PlogConverter will read your custom settings for the warnings you want turned off specified in the configuration file. This parameter in fact extends the list of the warnings that you want to be excluded defined by the --excludedCodes parameter.

--srcRoot (or -r): specifies the replacer of the SourceTreeRoot marker. If the path to the project's root directory was replaced with the SourceTreeRoot marker (|?|), this parameter becomes obligatory (otherwise the utility won't be able to find the project files).

--outputDir (or -o): defines the directory where the converted log files will be created. If not specified, the files will be created in the same directory where "PlogConverter.exe" is located.

--outputNameTemplate (or -n): specifies the filename template without extension. All the converted log files will have the same name but different extensions (".txt", ".html", ".csv", or ".plog" depending on the --renderTypes parameter).

Page 238: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Notifying the developer teamOnce you have the converted log files, you can send them to other people involved

in the development (team leaders, development manager, and so on). This process

can be automated by including the analysis step into scheduled "night" builds,

where the log file will be converted into the required format and sent to the

specified addressees.

Here is an example. Once you have a "fresh" analysis report converted into an

HTML file, run SendEmail utility. We are interested in the following basic

parameters:

-f : message sender;

-t : message recipient. You can specify more than one recipient;

-s : SMTP server;

-u : message subject;

-o username="" : username for authorization;

-o password="" : password for authorization;

-o message-charset=utf-8 : specifies the UTF-8 character set;

-o message-file="PVS-Studio_report.html" : HTML file to be sent to the addressees.

You can also inform the developers by using the BlameNotifier utility, which comes

with the PVS-Studio package. It is based on the following mechanism: on finishing

the analysis, the analyzer generates a ".plog" file, which is then passed to

BlameNotifier with some additional parameters. The utility finds the files with

potential errors and forms an individual HTML report for each "guilty" developer.

Another option is to send a complete log file with all the warnings sorted by the

names of the developers responsible for the code that triggered those warnings.

BlameNotifier utility can be found in the PVS-Studio install directory ("C:\Program

Files (x86)\PVS-Studio" by default). Use the "--help" option to display the basic

information about the utility:

BlameNotifier.exe --help

Page 239: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

BlameNotifier utility is available only if you have an Enterprise license. Please contact us to order a license.

Let's take a closer look at the utility's parameters:

--VCS (or -v), obligatory parameter: the type of the version control system that the utility will be dealing with. Supported systems: Git, Svn, Mercurial.

--recipientsList (or -r), obligatory parameter: the path to the text file with the mailing list. File description format:

# Recipients of complete log file

username_1 *email_1

...

username_1 *email_N

# Recipients of individually assigned warnings

username_1 email_1

...

username_N email_N

Comments could be written with the "#" character. For recipients of the complete

report, you need to add the "*" character before or after their email addresses. The

complete log file will include all the warnings sorted by the developers.

--server (or -x), obligatory parameter: SMTP server for mail sending.

--sender (or -s), obligatory parameter: sender's email address.

--login (or -l), obligatory parameter: username for authorization.

--password (or -w): password for authorization.

--port (or -p): mail delivery port (25 by default).

--maxTasks (or -m): the maximum number of concurrently running blame-processes. By default or when set to a negative number, BlameNotifier will be using 2 * N processes (where N is the number of processor cores).

--progress (or -g): turn logging on/off. Off by default.

BlameNotifier can also use the parameters of PlogConverter, namely (see the

descriptions in the corresponding section above):

--analyzer (or -a);

--excludedCodes (or -e);

--srcRoot (or -t);

Page 240: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

--settings (or -c).

This feature allows you to filter the analysis results before sending them.

For example (written in one line):

BlameNotifier.exe "Drive:\Path\To\Plog" --VCS=Git

--recipientsList="Drive:\Path\To\recipientsList.txt"

--server="smtp-20.1gb.ru"

--sender=... --login=... --password=...

--srcRoot="..." --maxTasks=40

SummaryDespite the built-in log-viewing features of PVS-Studio, there are other ways to

view the analysis log. You can convert the XML file with the analyzer warnings

into one of the formats that can be conveniently opened in other applications (html,

txt, csv) by using PlogConverter utility. A converted report can be automatically

sent on a daily basis to the persons involved in the development to inform them

about the analyzer warnings (SendEmail utility). In addition, BlameNotifier utility

can be used to automate the process of finding the developers responsible for

writing code that triggered certain warnings. BlameNotifier will send html messages

to these developers and also prepare a complete report for "special" persons with the

warnings sorted by the "guilty" developers.

Settings: GeneralWhen developing PVS-Studio we assigned primary importance to the simplicity of

use. We took into account our experience of working with traditional lint-like code

analyzers. And that is why one of the main advantages of PVS-Studio over other

code analyzers is that you can start using it immediately. Besides, PVS-Studio has

been designed in such a way that the developer using the analyzer would not have to

set it up at all. We managed to solve this task: a developer has a powerful code

analyzer which you need not to set up at the first launch.

Page 241: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

But you should understand that the code analyzer is a powerful tool which needs

competent use. It is this competent use of the analyzer (thanks to the settings

system) that allows you to achieve significant results. Operation of the code

analyzer implies that there should be a tool (a program) which performs routine

work of searching potentially unsafe constructions in code and a master (a

developer) who can make decisions on the basis of what he knows about the project

being verified. Thus, for example, the developer can inform the analyzer that:

some error types are not important for analysis and do not need to be shown (with the help of settings of Settings: Detectable Errors);

the project does not contain incorrect type conversions (by disabling the corresponding diagnostic messages, Settings: Detectable Errors);

Correct setting of these parameters can greatly reduce the number of diagnostic

messages produced by the code analyzer. It means that if the developer helps the

analyzer and gives it some additional information by using the settings, the analyzer

will in its turn reduce the number of places in the code which the developer must

pay attention to when examining the analysis results.

PVS-Studio setting can be accessed through the PVS-Studio -> Options command

in the IDE main menu. When selecting this command you will see the dialogue of

PVS-Studio options.

Each settings page is extensively described in PVS-Studio documentation.

Settings: Common Analyzer Settings

Check For New Versions

Preprocessor (only for Visual Studio)

Remove Intermediate Files

Thread Count

The tab of the analyzer's general settings displays the settings which do not depend

on the particular analysis unit being used.

Page 242: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Check For New VersionsThe analyzer can automatically check for updates on www.viva64.com site. It uses

our update module.

If the CheckForNewVersions option is set to True, a special text file is downloaded

from www.viva64.com site when you launch code checking (the commands Check

Current File, Check Current Project, Check Solution in PVS-Studio menu). This file

contains the number of the latest PVS-Studio version available on the site. If the

version on the site is newer than the version installed on the user computer, the user

will be asked for a permission to update the program. If the user agrees, a special

separate application PVS-Studio-Updater will be launched that will automatically

download and install the new PVS-Studio distribution kit. If the option

CheckForNewVersions is set to False, it will not check for the updates.

Preprocessor (only for Visual Studio)An external preprocessor is being utilized to preprocess files with PVS-Studio.

When working from under Visual Studio IDE, it was only the native Microsoft

Visual C++ preprocessor that had been employed for this task in the past. But in

4.50 version of PVS-Studio the support for the Clang independent preprocessor had

been added, as its performance is significantly higher and it lacks some of the

Microsoft's preprocessor shortcomings (although it also possesses issues of its

own). Still, the utilization of Clang preprocessor provides an increase of operational

performance by 1.5-1.7 times in most cases.

However there is an aspect that should considered. The preprocessor to be used can

be specified from within the PVS-Studio Options -> Common Analyzer Settings ->

Preprocessor field. The available options are: VisualCPP, Clang and

VisualCPPAfterClang. The first two of these are self-evident. The third one

indicates that Clang will be used at first, and if preprocessing errors are

encountered, the same file will be preprocessed by the Visual C++ preprocessor

instead. This option is a default one (VisualCPPAfterClang).

Remove Intermediate Files

Page 243: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer creates a lot of temporary command files for its operation to launch

the analysis unit itself, to perform preprocessing and to manage the whole process

of analysis. Such files are created for each project file being analyzed. Usually they

are not of interest for a user and are removed after the analysis process. But in some

cases it can be useful to look through these files. So you can indicate to the analyzer

not to remove them. In this case you can launch the analyzer outside the IDE from

the command line.

Thread CountAnalysis of files is performed faster on multi-core computers. Thus, on a 4-core

computer the analyzer can use all the four cores for its operation. But if for some

reasons you need to limit the number of cores being used you can do this by

selecting the needed number. The number of cores minus one will be used as a

default value.

Settings: Detectable ErrorsThis settings page allows you to manage the displaying of various types of PVS-

Studio messages in the analysis results list.

All the diagnostic messages output by the analyzer are split into several groups. The

display (show/hide) of each message type can be handled individually, while the

following actions are available for a whole message group:

Disabled – to completely disable an entire message group. Errors from this group will not be displayed in the analysis results list (PVS-Studio output window). Enabling the group again will require to re-run an analysis;

Show All – to show all the messages of a group in the analysis results list;

Hide All – to hide all the messages of a group in the analysis results list.

It may be sometimes useful to hide errors with certain codes in the list. For instance,

if you know for sure that errors with the codes V505 and V506 are irrelevant for

your project, you can hide them in the list by unticking the corresponding

checkboxes.

Page 244: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Please mind that you don't need to relaunch the analysis when using the options

"Show All" and "Hide All"! The analyzer always generates all the message types

found in the project, while whether they should be shown or hidden in the list is

defined by the settings on this page. When enabling/disabling error displaying, they

will be shown/hidden in the analysis results list right away, without you having to

re-analyze the whole project.

Complete disabling of message groups can be used to enhance the analyzer's

performance and get the analysis reports (plog-files) of smaller sizes.

Settings: Don't Check FilesYou may specify file masks to exclude some of the files or folders from analysis on

the tab "Don't Check Files". The analyzer will not check those files that meet the

masks' conditions.

Using this technique, you may, for instance, exclude autogenerated files from the

analysis. Besides, you may define the files to be excluded from analysis by the

name of the folder they are located in.

A mask is defined with the help of wild card match types. The '*' (any number of

any characters) wild card can be used, the '?' symbol is not supported.

The case of a character is irrelevant. The '*' wildcard character could only be

inserted at the beginning or at the end of the mask, therefore the masks of the 'a*b'

kind are not supported. After exclusion masks were specified, the messages from

files corresponding to these masks should disappear from PVS-Studio Output

window, and the next time then analysis is started these files will be excluded from

it. Thereby the total time of the entire project's analysis could be substantially

decreased by excluding files and directories with these masks.

2 types of masks could be specified: the Path masks and the File name masks. The

masks specified from within the FileNameMasks list are used to filter messages by

the names of the corresponding files only and ignoring these files' location. The

masks from the PathMasks list, on the other hand, are used to filter messages by

Page 245: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

taking into account their location within the filesystem on the disk and could be

used to suppress diagnostics either from the single file or even from the whole

directories and subdirectories. To filter the messages from one specific file, the full

path to it should be added to the PathMasks list, but to filter files sharing the same

name (or with the names complying to the wildcard mask), such names or masks

should be inserted into the FileNameMask list.

Valid masks examples for the FileNameMask property:

*ex.c — all files with the names ending with "ex" characters and "c" extension will

be excluded.

*.cpp — all files possessing the "cpp" extension will be excluded

stdafx.cpp — every file possessing such name will be excluded from analysis

regardless of its location within the filesystem

Valid masks examples for the PathMasks property:

c:\Libs\ — all files located in this directory and its subdirectories will be excluded

\Libs\ or *\Libs\* — all files located in the directories with path containing the Libs

subdirectory will be excluded.

Libs or *Libs* — the files possessing within their paths the subdirectory with the

'Libs' chars in its name will be excluded. Also the files with names containing the

'libs' characters will be excluded as well, for example 'c:\project\mylibs.cpp.' To

avoid confusion we advise you always to specify folders with slash separators.

c:\proj\includes.cpp — a single file located in the c:\proj\ folder with the specified

name will be excluded from the analysis.

Settings: Keyword Message FilteringIn the keyword filtering tab you can filter analyzer messages by the text they

contain.

Page 246: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

When it's necessary you may hide from analyzer's report the diagnosed errors

containing particular words or phrases. For example, if the report contains errors in

which names of printf and scanf functions are indicated and you consider that there

can be no errors relating to them just add these two words using the message

suppression editor.

Please note! When changing the list of the hidden messages you don't need to restart

analysis of the project. The analyzer always generates all the diagnostic messages

and the display of various messages is managed with the help of this settings tab.

When modifying message filters the changes will immediately appear in the report

and you won't need to launch analysis of the whole project again.

Settings: RegistrationOpen PVS-Studio settings page. (PVS-Studio Menu -> Options...).

In the registration tab the licensing information is entered.

After purchasing the analyzer you receive registration information: the name and

the serial number. These data must be entered in this tab. In the Application field

the licensing mode will be indicated.

Information on the licensing conditions is located in the ordering page on site and in

the "Registration" section of the Help system.

Settings: Specific Analyzer Settings

Analysis Timeout

Display False Alarms

False Alarm Comment

Integrated Help Language

Save File After False Alarm Mark

Use Solution/Project Group Folder As Initial

External Tool Command Line

Page 247: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

External Tool Path

Incremental Results Display Depth

Show Tray Icon

Disable 64bit Analysis

Source Tree Root

Trace Mode

Save Modified Log

Output Log Filter

Save Solution Statistics

The "Specific Analyzer Settings" tab contains additional advanced settings.

Analysis TimeoutThis setting allows you to set the time limit, by reaching which the analysis of

individual files will be aborted with V006. File cannot be processed. Analysis

aborted by timeout error, or to completely disable analysis termination by timeout.

We strongly advise you to consult the description of the error cited above before

modifying this setting. The timeout is often caused by the shortage of RAM. In such

a case it is reasonable not to increase the time but to decrease the number of parallel

threads being utilized. This can lead to a substantial increase in performance in case

the processor possesses numerous cores but RAM capacity is insufficient.

Display False AlarmsAllows enabling the display of messages marked as 'False Alarms' in the PVS-

Studio output window. This option will take effect immediately, without the need to

re-run the analysis. When this option is set to 'true', an 'FA' indicator containing the

number of false alarms on the output window panel will become visible.

False Alarm CommentAllows specifying a text fragment (a comment, for example) which will be

automatically inserted to the source file when using the False Alarm marking

mechanism, on a line preceding the one being marked.

Page 248: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

System environment variables, specified in the %name% format, will be

automatically evaluated before the insertion commences. A special

%PVSMESSAGE% variable can be utilized to insert the text of the message that is

being marked itself into the source.

Integrated Help LanguageThe setting allows you to select a language to be used for integrated help on the

diagnostic messages (a click to the message error code in PVS-Studio output

window) and online documentation (the PVS-Studio -> Help -> Open PVS-Studio

Documentation (html, online) menu command), which are also available at our site.

This setting will not change the language of IDE plug-in's interface and messages

produced by the analyzer.

Save File After False Alarm MarkMarking the message as False alarm requires the modification of source code files.

By default the analyzer will save each source code file after making every such

mark. However, if such frequent savings of files are undesirable (for example if the

files are being stored on different machine in LAN), they can be disabled using this

setting.

Exercise caution while modifying this setting because the not saving the files after

marking them with false alarms can lead to a loss of work in case of IDE being

closed.

Use Solution/Project Group Folder As InitialBy default PVS-Studio offers saving report file (.plog) inside the same folder as the

current solution file.

Modifying this setting allows you to restore the usual behavior of Windows file

dialogs, i.e. the dialog will remember the last folder that was opened in it and will

use this folder as initial.

External Tool Command Line

Page 249: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

This field contains command line arguments that will be passed to the external tool

specified in the settings' ExternalToolPath field when the "Send this message to

external tool" context menu command is utilized for a single selected diagnostic

message inside the PVS-Studio Output window. The formatting parameters could

also be used within this line; they will be replaced by values from their

corresponding fields from within the output results table. The following parameters

could be used:

%code —code of the diagnostic message (Code column)

%message —body of the diagnostic message (Message column)

%filename — name of the file containing the diagnostic message (File column)

%line —number of the line containing the diagnostic message (Line column)

%filepath — full path to the file containing the diagnostic message

%project — project containing the file with the diagnostic message (Project column)

%comment — user supplied comment and/or a text currently selected in the IDE code editor

External Tool PathThis field allows defining an absolute path to any external tool, which could then be

executed with the "Send this message to external tool" context menu command of

the PVS-Studio Output window. The mentioned menu command is available only

for a single simultaneously selected message from the results table, allowing the

passing of the command line parameters specified in the

ExternalToolCommandLine field to the utility from here. The detailed description

of this mode together with usage examples is available here.

Incremental Results Display DepthThis setting defines the mode of message display level in PVS-Studio Output

window for the results of incremental analysis. Setting the display level depth here

(correspondingly, Level 1 only; Levels 1 and 2; Levels 1, 2 and 3) will enable

automatic activation of these display levels on each incremental analysis procedure.

Page 250: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The "Preserve_Current_Levels" on the other hand will preserve the existing display

setting.

This setting could be handful for periodic combined use of incremental and regular

analysis modes, as the accidental disabling of, for example, level 1 diagnostics

during the review of a large analysis log will also result in the concealment of

portion of incremental analysis log afterwards. As the incremental analysis operates

in the background, such situation could potentially lead to the loss of positives on

existing issues within the project source code.

Perform Custom Build Step

Setting this option to 'true' enables the execution of actions specified in the 'Custom

Build Step' section of Visual Studio project file (vcproj/vcxproj). It should be noted

that the analyzer requires a fully-compilable code for its correct operation. So, if,

for example, the 'Custom Build Section' contains actions used to auto-generate some

header files, these actions should be executed (by enabling this setting) before

starting the project's analysis. However, in case this step performs some actions

concerning the linking process, for instance, then such actions will be irrelevant to

code analysis. The 'Custom Build Step' actions are specified at the level of the

project and will be executed by PVS-Studio during initial scanning of the project

file tree. If this setting is enabled and its execution results in a non-zero exit code,

the analysis of the corresponding project file will not be started.

Show Tray IconThis setting allows you to control the notifications of PVS-Studio analyzer

operations. In case PVS-Studio output window contains error messages after

performing the analysis (the messages potentially can be concealed by various

filters as false alarms, by the names of files being verified and so on; such messages

will not be present in PVS-Studio window), the analyzer will inform you about their

presence with popup message in the Windows notification area (System tray).

Single mouse click on this message or PVS-Studio tray icon will open the output

window containing the messages which were found by the analyzer.

Page 251: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Disable 64bit AnalysisThis setting allows to completely disable the generation of diagnostics related to 64-

bit analysis. This will prevent the appearance of 64-bit diagnostic messages in the

analyzer's output window even then the '64' button on the window's toolstrip panel

is enabled.

It is possible than on certain specific projects the analyzer generates a huge number

of messages related to 64-bit diagnostics (in the order of hundreds of thousands),

which, in turn, can negatively impact the 'responsiveness' of the user interface.

Therefore, if you are not planning to utilize the 64-bit analysis feature, you can

completely disable it by using this setting.

Source Tree RootBy default, PVS-Studio will produce diagnostic messages containing absolute paths

to the files being verified. This setting could be utilized for specifying the 'root'

section of the path, which will be replaced by a special marker in case the path to

the file within the analyzer's diagnostic message also starts from this 'root'. For

example, the absolute path to the C:\Projects\Project1\main.cpp file will be replaced

to a relative path |?|Project1\main.cpp, if the 'C:\Projects\' was specified as a 'root'.

When handling PVS-Studio log containing messages with paths in such relative

format, IDE plug-in will automatically replace the |?| with this setting's value.

Thereby, utilizing this setting allows you to handle PVS-Studio report on any local

machine with the access to the verified sources, regardless of the sources' location

in the file system structure.

A detailed description of the mode is available here.

Trace ModeThe setting allows you to select the tracing mode (logging of a program's execution

path) for PVS-Studio IDE extension packages (the plug-ins for Visual Studio IDEs).

There are several verbosity levels of the tracing (The Verbose mode is the most

detailed one). When tracing is enabled PVS-Studio will automatically create a log

Page 252: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

file with the 'log' extension which will be located in the LocalAppData\PVS-Studio

directory (for example c:\Users\admin\AppData\Roaming\PVS-Studio\

PVSTrace2168_000.log). Similarly, each of the running IDE processes will use a

separate file to store its' logging results.

Automatic Settings Import

This option allows you to enable the automatic import of settings (xml files)

from %AppData%\PVS-Studio\SettingsImports\' directory. The settings will be

imported on each update from stored settings, i.e. when Visual Studio or PVS-

Studio command line is started, when the settings are rest, etc. When importing

settings, flag-style options (true\false) and all options containing a single value (a

string, for example), will be overwritten by the settings from SettingsImports. The

options containing several valued (for example, the excluded directories), will be

merged.

If the SettingsImports folder contains several xml files, these files will be applied to

the current settings in a sequential manner, according to their names.

Save Modified LogThis setting specifies whether the 'Save log' confirmation prompt should be

displayed before starting the analysis or loading another log file, in case output

window already contains new, unsaved or modified analysis results. Setting the

option to 'Yes' will enable automatic saving of analysis results to the current log file

(after it was selected once in the 'Save File' dialog). Setting the option to 'No' will

force IDE plug-in to discard any of the analysis results.

Output Log FilterThis setting allows you to configure the contents of text and html log files, which

are generated when using PVS-Studio code analyzer IDE-plugin from command

line.

Save Solution Statistics

Page 253: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Controls whether the analysis run statistics will be saved to '%AppData%\PVS-

Studio\Statistics' folder. The statistics can be reviewed in the 'PVS-Studio|Analysis

Statistics...' dialog.

V001. A code fragment from 'file' cannot be analyzed.The analyzer sometimes fails to diagnose a file with source code completely. There

may be three reasons for that:

1) An error in code

There is a template class or template function with an error. If this function is not

instantiated, the compiler fails to detect some errors in it. In other words, such an

error does not hamper compilation. PVS-Studio tries to find potential errors even in

classes and functions that are not used anywhere. If the analyzer cannot parse some

code, it will generate the V001 warning. Consider a code sample:

template <class T>

class A

{

public:

void Foo()

{

//forget ;

int x

}

};

Visual C++ will compile this code if the A class is not used anywhere. But it

contains an error, which hampers PVS-Studio's work.

2) An error in the Visual C++'s preprocessor

Page 254: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer uses the Visual C++'s preprocessor while working. From time to time

this preprocessor makes errors when generating preprocessed "*.i" files. As a result,

the analyzer receives incorrect data. Here is a sample:

hWnd = CreateWindow (

wndclass.lpszClassName, // window class name

__T("NcFTPBatch"), // window caption

WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,

// window style

100, // initial x position

100, // initial y position

450, // initial x size

100, // initial y size

NULL, // parent window handle

NULL, // window menu handle

hInstance, // program instance handle

NULL); // creation parameters

if (hWnd == NULL) {

...

Visual C++'s preprocessor turned this code fragment into:

hWnd = // window class name// window caption// window style//

initial x position// initial y position// initial x size//

initial y size// parent window handle// window menu handle//

program instance handleCreateWindowExA(0L,

wndclass.lpszClassName, "NcFTPBatch", 0x00000000L | 0x00C00000L |

0x00080000L | 0x00020000L, 100, 100,450, 100, ((void *)0),

((void *)0), hInstance, ((void *)0)); // creation parameters

if (hWnd == NULL) {

...

It turns out that we have the following code:

hWnd = // a long comment

if (hWnd == NULL) {

...

Page 255: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

This code is incorrect and PVS-Studio will inform you about it. Of course it is a

defect of PVS-Studio, so we will eliminate it in time.

It is necessary to note that Visual C++ successfully compiles this code because the

algorithms it uses for compilation purposes and generation of preprocessed "*.i"

files are different.

3) Defects inside PVS-Studio

On rare occasions PVS-Studio fails to parse complex template code.

Whatever the reason for generating the V001 warning, it is not crucial. Usually

incomplete parse of a file is not very significant from the viewpoint of analysis.

PVS-Studio simply skips a function/class with an error and continues analyzing the

file. Only a small code fragment is left unanalyzed.

V002. Some diagnostic messages may contain incorrect line number.The analyzer can sometimes issue an error "Some diagnostic messages may contain

incorrect line number". This occurs when it encounters multiline #pragma

directives, on all supported versions of Microsoft Visual Studio.

Any code analyzer works only with preprocessed files, i.e. with those files in which

all (#define) macros are expanded and all included files are substituted (#include).

Also in the pre-processed file there is information about the substituted files and

their positions. That means, in the preprocessed files there is information about line

numbers.

Preprocessing is carried out in any case. For the user this procedure looks quite

transparent. Sometimes the preprocessor is a part of the code analyzer and

sometimes (like in the case with PVS-Studio) external preprocessor is used. In PVS-

Studio we use the preprocessor by Microsoft Visual Studio or Clang. The analyzer

starts the command line compiler cl.exe/clang.exe for each C/C++ file being

processed and generates a preprocessed file with "i" extension.

Page 256: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Here is one the situation where the message "Some diagnostic messages may

contain incorrect line number" is issued and a failure in positioning diagnostic

messages occurs. It happens because of multiline directives #pragma of a special

type. Here is an example of correct code:

#pragma warning(push)

void test()

{

int a = 0;

size_t b = a; // PVS-Studio will inform about the error here

}

If #pragma directive is written in two lines, PVS-Studio will point to an error in an

incorrect fragment (there will be shift by one line):

#pragma \

warning(push)

void test()

{

int a = 0; // PVS-Studio will show the error here,

size_t b = a; // actually, however, the error should be here.

}

However, in another case there will be no error caused by the multiline #pragma

directive:

#pragma warning \

(push)

void test()

{

int a = 0;

size_t b = a; // PVS-Studio will inform about the error in this line

}

Our recommendation here is either not to use the multiline #pragma directives at all,

or to use them in such a way that they can be correctly processed.

Page 257: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The code analyzer tries to detect a failure in lines numbering in the processed file.

This mechanism is a heuristic one and it cannot guarantee correct determination of

diagnostic messages positioning in the program code. However, if it is possible to

find out that a particular file contains multiline pragmas, and there exists a

positioning error, then the message "Some diagnostic messages may contain

incorrect line number" is issued.

This mechanism works in the following way.

The analyzer opens the source C/C++ file and searches for the very last token. It

selects only those tokens that are not shorter than three symbols in order to ignore

closing parentheses, etc. E.g., for the following code the "return" operator will be

considered as the last token:

01 #include "stdafx.h"

02

03 int foo(int a)

04 {

05 assert(a >= 0 &&

06 a <= 1000);

07 int b = a + 1;

08 return b;

09 }

Having found the last token, the analyzer will determine the number of the line

which contains it. In this very case it is line 8. Further on, the analyzer searches for

the last token in the file which has already been preprocessed. If the last tokens do

not coincide, then most likely the macro in the end of file was not expanded; the

analyzer is unable to understand whether the lines are arranged correctly, and

ignores the given situation. However, such situations occur very rarely and last

tokens almost always coincide in the source and preprocessed files. If it is so, the

line number is determined, in which the token in the preprocessed file is situated.

Thus, we have the line numbers in which the last token is located in the source file

and in the preprocessed file. If these line numbers do not coincide, then there has

been a failure in lines numbering. In this case, the analyzer will notify the user

Page 258: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

about it with the message "Some diagnostic messages may contain incorrect line

number".

Please consider that if a multiline #pragma-directive is situated in the file after all

the dangerous code areas are found, then all the line numbers for the found errors

will be correct. Even though the analyzer issues the message "Some diagnostic

messages may contain incorrect line number for file", this will not prevent you from

analyzing the diagnostic messages issued by it.

Please pay attention that this error may lead to incorrect work of the code analyzer,

although it is not an error of PVS-Studio itself.

V003. Unrecognized error found...Message V003 means that a critical error occurred in the analyzer. It is most likely

that in this case you will not see any warning messages concerning the file being

checked at all.

Although the message V003 is very rare, we will appreciate if you help us fix the

issue that caused this message to appear. To do this, please send us a preprocessed i-

file that caused the error and its corresponding configuration launch files (*.PVS-

Studio.cfg and *.PVS-Studio.cmd) to this e-mail [email protected].

Note. A preprocessed i-file is generated from a source file (for example, file.cpp)

when the preprocessor finishes its work. To get this file you should set the option

RemoveIntermediateFiles to False on the tab "Common Analyzer Settings" in PVS-

Studio settings and restart the analysis of this one file. After that you can find the

corresponding i-file in the project folder (for example, file.i and its corresponding

file.PVS-Studio.cfg and file.PVS-Studio.cmd).

V004. Diagnostics from the 64-bit rule set are not entirely accurate without the appropriate 64-bit

Page 259: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

compiler. Consider utilizing 64-bit compiler if possible.When detecting issues of 64-bit code, it is 64-bit configuration of a project that the

analyzer must always test. For it is 64-bit configuration where data types are

correctly expanded and branches like "#ifdef WIN64" are selected, and so on. It is

incorrect to try to detect issues of 64-bit code in a 32-bit configuration.

But sometimes it may be helpful to test the 32-bit configuration of a project. You

can do it in case when there is no 64-bit configuration yet but you need to estimate

the scope of work on porting the code to a 64-bit platform. In this case you can test

a project in 32-bit mode. Testing the 32-bit configuration instead of the 64-bit one

will show how many diagnostic warnings the analyzer will generate when testing

the 64-bit configuration. Our experiments show that of course far not all the

diagnostic warnings are generated when testing the 32-bit configuration. But about

95% of them in the 32-bit mode coincide with those in the 64-bit mode. It allows

you to estimate the necessary scope of work.

Pay attention! Even if you correct all the errors detected when testing the 32-bit

configuration of a project, you cannot consider the code fully compatible with 64-

bit systems. You need to perform the final testing of the project in its 64-bit

configuration.

The V004 message is generated only once for each project checked in the 32-bit

configuration. The warning refers to the file which will be the first to be analyzed

when checking the project. It is done for the purpose to avoid displaying a lot of

similar warnings in the report.

V005. Cannot determine active configuration for project. Please

Page 260: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

check projects and solution configurations.This issue with PVS-Studio is caused by the mismatch of selected project's platform

configurations declared in the solution file (Vault.sln) and platform

configurations declared in the project file itself. For example, the solution file may

contain lines of this particular kind for concerned project:

{F56ECFEC-45F9-4485-8A1B-6269E0D27E49}.Release|x64.ActiveCfg = Release|

x64

However, the project file itself may lack the declaration of Release|x64

configuration. Therefore trying to check this particular project, PVS-Studio is

unable to locate the 'Release|x64' configuration. The following line is expected to be

automatically generated by IDE in the solution file for such a case:

{F56ECFEC-45F9-4485-8A1B-6269E0D27E49}.Release|x64.ActiveCfg = Release|

Win32

In automatically generated solution file the solution's active platform configuration

(Release|x64.ActiveCfg) is set equal to one of project's existing configurations (I.e.

in this particular case Release|Win32). Such a situation is expected and can be

handled by PVS-Studio correctly.

V006. File cannot be processed. Analysis aborted by timeout.Message V006 is generated when an analyzer cannot process a file for a particular

time period and aborts. Such situation might happen in two cases.

The first reason - an error inside the analyzer that does not allow it to parse some

code fragment. It happens rather seldom, yet it is possible. Although message V006

appears rather seldom, we would appreciate if you help us eliminate the issue which

causes the message to appear. If you have worked with projects in C/C++, please

Page 261: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

send your preprocessed i-file where this issue occurs and its corresponding

configuration launch files (*.PVS-Studio.cfg and *.PVS-Studio.cmd) to the

address [email protected].

Note. A preprocessed i-file is generated from a source file (for example, file.cpp)

when the preprocessor finishes its work. To get this file you should set the option

RemoveIntermediateFiles to False on the tab "Common Analyzer Settings" in PVS-

Studio settings and restart the analysis of this one file. After that you can find the

corresponding i-file in the project folder (for example, file.i and its corresponding

file.PVS-Studio.cfg and file.PVS-Studio.cmd).

The second possible reason is the following: although the analyzer could process

the file correctly, it does not have enough time to do that because it gets too few

system resources due to high processor load. By default, the number of threads

spawned for analysis is equal to the number of processor cores. For example, if we

have four cores in our machine, the tool will start analysis of four files at once. Each

instance of an analyzer's process requires about 1.5 Gbytes of memory. If your

computer does not have enough memory, the tool will start using the swap file and

analysis will run slowly and fail to fit into the required time period. Besides, you

may encounter this problem when you have other "heavy" applications running on

your computer simultaneously with the analyzer.

To solve this issue, you may directly restrict the number of cores to be used for

analysis in the PVS-Studio settings (ThreadCount option on the "Common Analyzer

Settings" tab).

V007. Deprecated CLR switch was detected. Incorrect diagnostics are possible.The V007 message appears when the projects utilizing the C++/Common Language

Infrastructure Microsoft specification, containing one of the deprecated /clr

compiler switches, are selected for analysis. Although you may continue analyzing

Page 262: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

such a project, PVS-Studio does not officially support these compiler flags. It is

possible that some analyzer errors will be incorrect.

V008. Unable to start the analysis on this file.PVS-Studio was unable to start the analysis of the designated file. This message

indicates that an external C++ preprocessor, started by the analyzer to create a

preprocessed source code file, exited with a non-zero error code. Moreover, std

error can also contain detailed description of this error, which can be viewed in

PVS-Studio Output Window for this file.

There could be several reasons for the V008 error:

1) The source code is not compilable

If the C++ sources code is not compilable for some reason (a missing header file for

example), then the preprocessor will exit with non-zero error code and the "fatal

compilation error" type message will be outputted into std error. PVS-Studio is

unable to initiate the analysis in case C++ file hadn't been successfully

preprocessed. To resolve this error you should ensure the compilability of the file

being analyzed.

2) The preprocessor's executable file had been damaged\locked

Such a situation is possible when the preprocessor's executable file had been

damaged or locked by system antiviral software. In this case the PVS-Studio Output

window could also contain the error messages of this kind: "The system cannot

execute the specified program". To resolve it you should verify the integrity of the

utilized preprocessor's executable and lower the security policies' level of system's

antiviral software.

3) One of PVS-Studio auxiliary command files had been locked.

PVS-Studio analyzer is not launching the C++ preprocessor directly, but with the

help of its own pre-generated command files. In case of strict system security

Page 263: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

policies, antiviral software could potentially block the correct initialization of C++

preprocessor. This could be also resolved by easing the system security policies

toward the analyzer.

V009. To use free version of PVS-Studio, source code files are required to start with a special comment.You entered a free license key allowing you to use the analyzer in free mode. To be

able to run the tool with this key, you need to add special comments to your source

files with the following extensions: .c, .cc, .cpp, .cp, .cxx, .c++, .cs. Header files do

not need to be modified.

You can insert the comments manually or by using a special open-source utility

available at GitHub: how-to-use-pvs-studio-free.

Types of comments:

Comments for students (academic license):

// This is a personal academic project. Dear PVS-Studio, please check it.

// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com

Comments for open-source non-commercial projects:

// This is an open source non-commercial project. Dear PVS-Studio, please check it.

// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com

Comments for individual developers:

// This is an independent project of an individual developer. Dear PVS-Studio,

please check it.

Page 264: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com

Some developers might not want additional commented lines not related to the

project in their files. It is their right, and they can simply choose not to use the

analyzer. Another option is to purchase a commercial license and use the tool

without any limitations. We consider your adding these comments as your way to

say thank you to us for the granted license and help us promote our product.

If you have any questions, please contact our support.

V051. Some of the references in project are missing or incorrect. The analysis results could be incomplete. Consider making the project fully compilable and building it before analysis.A V051 message indicates that the C# project, loaded in the analyzer, contains

compilation errors. These usually include unknown data types, namespaces, and

assemblies (dll files), and generally occur when you try to analyze a project that has

dependent assemblies of nuget packages absent on the local machine, or third-party

libraries absent among the projects of the current solution.

Despite this error, the analyzer will try to scan the part of the code that doesn't

contain unknown types, but results of such analysis may be incomplete, as some of

the messages may be lost. The reason is that most diagnostics can work properly

only when the analyzer has complete information about all the data types contained

in the source files to be analyzed, including the types implemented in third-party

assemblies.

Even if rebuilding of dependency files is provided for in the build scenario of the

project, the analyzer won't automatically rebuild the entire project. That's why we

Page 265: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

recommend that, before scanning it, you ensure that the project is fully compilable,

including making sure that all the dependency assemblies (dll files) are present.

Sometimes the analyzer may mistakenly generate this message on a fully

compilable project, with all the dependencies present. It may happen, for example,

when the project uses a non-standard MSBuild scenario - say, csproj files are

importing some additional props and target files. In this case, you can ignore the

V051 message or turn it off in the analyzer settings.

V052. A critical error had occurred.The appearance of V052 message means that a critical error had occurred inside the

analyzer. It is most likely that several source files will not be analyzed.

Although the V052 message is quite rare, we will appreciate if you can help us

fixing the issue that had cause it. To accomplish this, please send the exception

stack from PVS-Studio output window (or the message from StdErr in case the

command line version was utilized) to [email protected].

V101. Implicit assignment type conversion to memsize type.The analyzer detected a potential error relating to implicit type conversion while

executing the assignment operator "=". The error may consist in incorrect

calculating of the value of the expression to the right of the assignment operator

"=". An example of the code causing the warning message:

size_t a;

unsigned b;

...

a = b; // V101

The operation of converting a 32-bit type to memsize-type is safe in itself as there is

no data loss. For example, you can always save the value of unsigned-type variable

Page 266: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

into a variable of size_t type. But the presence of this type conversion may indicate

a hidden error made before.

The first cause of the error occurrence on a 64-bit system may be the change of the

expression calculation process. Let's consider an example:

unsigned a = 10;

int b = -11;

ptrdiff_t c = a + b; //V101

cout << c << endl;

On a 32-bit system this code will display the value -1, while on a 64-bit system it

will be 4294967295. This behaviour fully meets the rules of type converion in C++

but most likely it will cause an error in a real code.

Let's explain the example. According to C++ rules a+b expression has unsigned

type and contains the value 0xFFFFFFFFu. On a 32-bit system ptrdiff_t type is a

sign 32-bit type. After 0xFFFFFFFFu value is assigned to the 32-bit sign variable it

will contain the value -1. On a 64-bit system ptrdiff_t type is a sign 64-bit type. It

means 0xFFFFFFFFu value will be represented as it is. That is, the value of the

variable after assignment will be 4294967295.

The error may be corrected by excluding mixed use of memsize and non-memsize-

types in one expression. An example of code correction:

size_t a = 10;

ptrdiff_t b = -11;

ptrdiff_t c = a + b;

cout << c << endl;

A more proper way of correction is to refuse using sign and non-sign data types

together.

The second cause of the error may be an overflow occurring in 32-bit data types. In

this case the error may stand before the assignment operator but you can detect it

only indirectly. Such errors occur in code allocating large memory sizes. Let's

consider an example:

Page 267: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

unsigned Width = 1800;

unsigned Height = 1800;

unsigned Depth = 1800;

// Real error is here

unsigned CellCount = Width * Height * Depth;

// Here we get a diagnostic message V101

size_t ArraySize = CellCount * sizeof(char);

cout << ArraySize << endl;

void *Array = malloc(ArraySize);

Suppose that we decided to process data arrays of more than 4 Gb on a 64-bit

system. In this case the given code will cause allocation of a wrong memory size.

The programmer is planning to allocate 5832000000 memory bytes but he gets only

1537032704 instead. It happens because of an overflow occurring while calculating

Width * Height * Depth expression. Unfortunately, we cannot diagnose the error in

the line containing this expression but we can indirectly indicate the presence of the

error detecting type conversion in the line:

size_t ArraySize = CellCount * sizeof(char); //V101

To correct the error you should use types allowing you to store the necessary range

of values. Mind that correction of the following kind is not appropriate:

size_t CellCount = Width * Height * Depth;

We still have the overflow here. Let's consider two examples of proper code

correction:

// 1)

unsigned Width = 1800;

unsigned Height = 1800;

unsigned Depth = 1800;

size_t CellCount =

static_cast<size_t>(Width) *

static_cast<size_t>(Height) *

static_cast<size_t>(Depth);

// 2)

Page 268: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

size_t Width = 1800;

size_t Height = 1800;

size_t Depth = 1800;

size_t CellCount = Width * Height * Depth;

You should keep in mind that the error can be situated not only higher but even in

another module. Let's give a corresponding example. Here the error consists in

incorrect index calculation when the array's size exceeds 4 Gb.

Suppose that the application uses a large one-dimensional array and CalcIndex

function allows you to address this array as a two-dimensional one.

extern unsigned ArrayWidth;

unsigned CalcIndex(unsigned x, unsigned y) {

return x + y * ArrayWidth;

}

...

const size_t index = CalcIndex(x, y); //V101

The analyzer will warn about the problem in the line: const size_t index =

CalcIndex(x, y). But the error is in incorrect implementation of CalcIndex function.

If we take CalcIndex separately it is absolutely correct. The output and input values

have unsigned type. Calculations are also performed only with unsigned types

participating. There are no explicit or implicit type conversions and the analyzer has

no opportunity to detect a logic problem relating to CalcIndex function. The error

consists in that the result returned by the function and possibly the result of the

input values was chosen incorrectly. The function's result must have memsize type.

Fortunately, the analyzer managed to detect implicit conversion of CalcIndex

function's result to size_t type. It allows you to analyze the situation and bring

necessary changes into the program. Correction of the error may be, for example,

the following:

extern size_t ArrayWidth;

size_t CalcIndex(size_t x, size_t y) {

return x + y * ArrayWidth;

Page 269: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

}

...

const size_t index = CalcIndex(x, y);

If you are sure that the code is correct and the array's size will never reach 4 Gb you

can suppress the analyzer's warning message by explicit type conversion:

extern unsigned ArrayWidth;

unsigned CalcIndex(unsigned x, unsigned y) {

return x + y * ArrayWidth;

}

...

const size_t index = static_cast<size_t>(CalcIndex(x, y));

In some cases the analyzer can understand itself that an overflow is impossible and

the message won't be displayed.

Let's consider the last example related to incorrect shift operations

ptrdiff_t SetBitN(ptrdiff_t value, unsigned bitNum) {

ptrdiff_t mask = 1 << bitNum; //V101

return value | mask;

}

The expression " mask = 1 << bitNum " is unsafe because this code cannot set the

high-order bits of the 64-bit variable mask into ones. If you try to use SetBitN

function for setting, for example, the 33rd bit, an overflow will occur when

performing the shift operation and you will not get the result you've expected.

V102. Usage of non memsize type for pointer arithmetic.The analyzer found a possible error in pointer arithmetic. The error may be caused

by an overflow during the determination of the expression.

Let's take up the first example.

Page 270: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

short a16, b16, c16;

char *pointer;

...

pointer += a16 * b16 * c16;

The given example works correctly with pointers if the value of the expression a16

* b16 * c16 does not excess INT_MAX (2Gb). This code could always work

correctly on the 32-bit platform because the program never allocated large-sized

arrays. On the 64-bit platform the programmer using the previous code while

working with an array of a large size would be disappointed. Suppose, we would

like to shift the pointer value in 3000000000 bytes, and the

variables a16, b16 and c16 have values 3000, 1000 and 1000 correspondingly.

During the determination of the expression a16 * b16 * c16 all the variables,

according to the C++ rules, will be converted to type int, and only then the

multiplication will take place. While multiplying an overflow will occur, and the

result of this would be the number -1294967296. The incorrect expression result

will be extended to type ptrdiff_t and pointer determination will be launched. As a

result, we'll face an abnormal program termination while trying to use the incorrect

pointer.

To prevent such errors one should use memsize types. In our case it will be correct

to change the types of the variables a16, b16, c16 or to use the explicit type

conversion to type ptrdiff_t as follows:

short a16, b16, c16;

char *pointer;

...

pointer += static_cast<ptrdiff_t>(a16) *

static_cast<ptrdiff_t>(b16) *

static_cast<ptrdiff_t>(c16)

It's worth mentioning that it is not always incorrect not to use memsize type in

pointer arithmetic. Let's examine the following situation:

char ch;

short a16;

Page 271: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

int *pointer;

...

int *decodePtr = pointer + ch * a16;

The analyzer does not show a message on it because it is correct. There are no

determinations which may cause an overflow and the result of this expression will

be always correct on the 32-bit platform as well as on the 64-bit platform.

V103. Implicit type conversion from memsize type to 32-bit type.The analyzer found a possible error related to the implicit memsize-type conversion

to 32-bit type. The error consists in the loss of high bits in 64-bit type which causes

the loss of the value.

The compiler also diagnoses such type conversions and shows warnings.

Unfortunately, such warnings are often switched off, especially when the project

contains a great deal of the previous legacy code or old libraries are used. In order

not to make a programmer look through hundreds and thousands of such warnings,

showed by the compiler, the analyzer informs only about those which may be the

cause of the incorrect work of the code on the 64-bit platform.

The first example.

Our application works with videos and we want to calculate what file-size we'll

need in order to store all the shots kept in memory into a file.

size_t Width, Height, FrameCount;

...

unsigned BufferSizeForWrite = Width * Height * FrameCount *

sizeof(RGBStruct);

Earlier the general size of the shots in memory could never excess 4 Gb (practically

2-3 Gb depending on the kind of OS Windows). On the 64-bit platform we have an

opportunity to store much more shots in memory, and let's suppose that their

general size is 10 Gb. After putting the result of the expression Width * Height *

Page 272: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

FrameCount * sizeof(RGBStruct) into the variable BufferSizeForWrite, we'll

truncate high bits and will deal with the incorrect value.

The correct solution will be to change the type of the

variable BufferSizeForWrite into type size_t.

size_t Width, Height, FrameCount;

...

size_t BufferSizeForWrite = Width * Height * FrameCount *

sizeof(RGBStruct);

The second example.

Saving of the result of pointers subtraction.

char *ptr_1, *ptr_2;

...

int diff = ptr_2 - ptr_1;

If pointers differ more than in one INT_MAX byte (2 Gb) a value cutoff during the

assignment will occur. As a result the variable diff will have an incorrect value. For

the storing of the given value we should use type ptrdiff_t or another memsize type.

char *ptr_1, *ptr_2;

...

ptrdiff_t diff = ptr_2 - ptr_1;

When you are sure about the correctness of the code and the implicit type

conversion does not cause errors while changing over to the 64-bit platform, you

may use the explicit type conversion in order to avoid error messages showed in this

line. For example:

unsigned BitCount = static_cast<unsigned>(sizeof(RGBStruct) * 8);

If you suspect that the code contains incorrect explicit conversions of memsize

types to 32-bit types about which the analyzer does not warn, you can use the V202.

Page 273: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

As was said before analyzer informs only about those type conversions which can

cause incorrect code work on a 64-bit platform. The code given below won't be

considered incorrect though there occurs conversion of memsize type to int type:

int size = sizeof(float);

V104. Implicit type conversion to memsize type in an arithmetic expression.The analyzer found a possible error inside an arithmetic expression and this error is

related to the implicit type conversion to memsize type. The error of an overflow

may be caused by the changing of the permissible interval of the values of the

variables included into the expression.

The first example.

The incorrect comparison expression. Let's examine the code:

size_t n;

unsigned i;

// Infinite loop (n > UINT_MAX).

for (i = 0; i != n; ++i) { ... }

In this example the error are shown which are related to the implicit conversion of

type unsigned to type size_t while performing the comparison operation.

On the 64-bit platform you may have an opportunity to process a larger data size

and the value of the variable n may excess the number UINT_MAX (4 Gb). As a

result, the condition i != n will be always true and that will cause an eternal cycle.

An example of the corrected code:

size_t n;

size_t i;

for (i = 0; i != n; ++i) { ... }

Page 274: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The second example.

char *begin, *end;

int bufLen, bufCount;

...

ptrdiff_t diff = begin - end + bufLen * bufCount;

The implicit conversion of type int to type ptrdiff_t often indicates an error. One

should pay attention that the conversion takes place not while performing operator

"=" (for the expression begin - end + bufLen * bufCount has type ptrdiff_t), but

inside this expression. The subexpression begin - end according to C++ rules has

type ptrdiff_t, and the right bufLen * bufCount type int. While changing over to 64-

bit platform the program may begin to process a larger data size which may result in

an overflow while determining the subexpression bufLen * bufCount.

You should change the type of the variables bufLen and bufCount into memsize

type or use the explicit type conversion, as follows:

char *begin, *end;

int bufLen, bufCount;

...

ptrdiff_t diff = begin - end +

ptrdiff_t(bufLen) * ptrdiff_t(bufCount);

Let's notice that the implicit conversion to memsize type inside the expressions is

not always incorrect. Let's examine the following situation:

size_t value;

char c1, c2;

size_t result = value + c1 * c2;

The analyzer does not show error message although the conversion of

type int to size_t occurs in this case, for there can be no overflow while determining

the subexpression c1 * c2.

Page 275: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

If you suspect that the program may contain errors related to the incorrect explicit

type conversion in expressions, you may use the V201. Here is an example when

the explicit type conversion to type size_t hides an error:

int i;

size_t st;

...

st = size_t(i * i * i) * st;

V105. N operand of '?:' operation: implicit type conversion to memsize type.The analyzer found a possible error inside an arithmetic expression related to

the implicit type conversion to memsize type. An overflow error may be caused by

the changing of the permissible interval of the values of the variables included into

the expression. This warning is almost equivalent to warning V104 with the

exception that the implicit type conversion occurs due to the use of ?: operation.

Let's give an example of the implicit type conversion while using operation:

int i32;

float f = b != 1 ? sizeof(int) : i32;

In the arithmetic expression the ternary operation ?: is used which has three

operands:

b != 1 - the first operand;

sizeof(int) - the second operand;

i32 - the third operand.

The result of the expression b != 1 ? sizeof(int) : i32 is the value of

type size_t which is then converted into type float value. Thus, the implicit type

conversion realized for the 3rd operand of ?: operation.

Let's examine an example of the incorrect code:

Page 276: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

bool useDefaultVolume;

size_t defaultVolume;

unsigned width, height, depth;

...

size_t volume = useDefaultVolume ?

defaultVolume :

width * height * depth;

Let's suppose, we're developing an application of computational modeling which

requires three-dimensional calculation area. The number of calculating elements

which are used is determined according to the variable useDefaultSize value and is

assigned on default or by multiplication of length, height and depth of the

calculating area. On the 32-bit platform the size of memory which was already

allocated, cannot excess 2-3 Gb (depending on the kind of OS Windows) and as

consequence the result of the expression width * height * depth will be always

correct. On the 64-bit platform, using the opportunity to deal with a larger memory

size, the number of calculating elements may excess the value UINT_MAX (4 Gb).

In this case an overflow will occur while determining the expression width * height

* depth because the result of this expression had type unsigned.

Correction of the code may consist in the changing of the type of the

variables width, height and depth to memsize type as follows:

...

size_t width, height, depth;

...

size_t volume = useDefaultVolume ?

defaultVolume :

width * height * depth;

Or in use of the explicit type conversion:

unsigned width, height, depth;

...

size_t volume = useDefaultVolume ?

defaultVolume :

Page 277: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

size_t(width) * size_t(height) * size_t(depth);

In addition, we advise to read the description of a similar warning V104, where one

can learn about other effects of the implicit type conversion to memsize type.

V106. Implicit type conversion N argument of function 'foo' to memsize type.The analyzer found a possible error related to the implicit actual function argument

conversion to memsize type.

The first example.

The program deals with large arrays using container CArray from library MFC. On

the 64-bit platform the number of array items may excess the

value INT_MAX (2Gb), which will make the work of the following code impossible:

CArray<int, int> myArray;

...

int invalidIndex = 0;

INT_PTR validIndex = 0;

while (validIndex != myArray.GetSize()) {

myArray.SetAt(invalidIndex, 123);

++invalidIndex;

++validIndex;

}

The given code fills all the array myArray items with value 123. It seems to be

absolutely correct and the compiler won't show any warnings in spite of its

impossibility to work on the 64-bit platform. The error consists in the use of

type int as an index of the variable invalidIndex. When the value of the

variable invalidIndex excesses INT_MAX an overflow will occur and it will receive

value "-1". The analyzer diagnoses this error and warns that the implicit

conversion of the first argument of the function SetAt to memsize type (here it is

Page 278: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

type INT_PTR) occurs. When seeing such a warning you may correct the error

replacing int type with a more appropriate one.

The given example is significant because it is rather unfair to blame a programmer

for the ineffective code. The reason is that GetAt function in class CArray in the

previous MFC library version was declared as follows:

void SetAt(int nIndex, ARG_TYPE newElement);

And in the new version:

void SetAt(INT_PTR nIndex, ARG_TYPE newElement);

Even the Microsoft developers creating MFC could not take into account all the

possible consequences of the use of int type for indexing in the array and we can

forgive the common developer who has written this code.

Here is the correct variant:

...

INT_PTR invalidIndex = 0;

INT_PTR validIndex = 0;

while (validIndex != myArray.GetSize()) {

myArray.SetAt(invalidIndex, 123);

++invalidIndex;

++validIndex;

}

The second example.

The program determines the necessary data array size and then allocated it using

function malloc as follows:

unsigned GetArraySize();

...

unsigned size = GetArraySize();

void *p = malloc(size);

Page 279: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer will warn about the line void *p = malloc(size);. Looking through the

definition of function malloc we will see that its formal argument assigning the size

of the allocated memory is represented by type size_t. But in the program the

variable size of unsigned type is used as the actual argument. If your program on the

64-bit platform needs an array more than UINT_MAX bytes (4Gb), we can be sure

that the given code is incorrect for type unsigned cannot keep a value more

than UINT_MAX. The program correction consists in changing the types of the

variables and functions used in the determination of the data array size. In the given

example we should replace unsigned type with one of memsize types, and also if it

is necessary modify the function GetArraySize code.

...

size_t GetArraySize();

...

size_t size = GetArraySize();

void *p = malloc(size);

The analyzer show warnings on the implicit type conversion only if it may cause an

error during program port on the 64-bit platform. Here it is the code which contains

the implicit type conversion but does not cause errors:

void MyFoo(SSIZE_T index);

...

char c = 'z';

MyFoo(0);

MyFoo(c);

If you are sure that the implicit type conversion of the actual function argument is

absolutely correct you may use the explicit type conversion to suppress the

analyzer's warnings as follows:

typedef size_t TYear;

void MyFoo(TYear year);

int year;

...

MyFoo(static_cast<TYear>(year));

Page 280: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Sometimes the explicit type conversion may hide an error. In this case you may use

the V201.

V107. Implicit type conversion N argument of function 'foo' to 32-bit type.The analyzer found a possible error related to the implicit conversion of the actual

function argument which has memsize type to 32-bit type.

Let's examine an example of the code which contains the function for searching for

the max array item:

float FindMaxItem(float *array, int arraySize) {

float max = -FLT_MAX;

for (int i = 0; i != arraySize; ++i) {

float item = *array++;

if (max < item)

max = item;

}

return max;

}

...

float *beginArray;

float *endArray;

float maxValue = FindMaxItem(beginArray, endArray - beginArray);

This code may work successfully on the 32-bit platform but it won't be able to

process arrays containing more than INT_MAX (2Gb) items on the 64-bit

architecture. This limitation is caused by the use of int type for the

argument arraySize. Pay attention that the function code looks absolutely correct

not only from the compiler's point of view but also from that of the analyzer. There

is no type conversion in this function and one cannot find the possible problem.

Page 281: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer will warn about the implicit conversion of memsize type to a 32-bit

type during the invocation of FindMaxItem function. Let's try to find out why it

happens so. According to C++ rules the result of the subtraction of two pointers has

type ptrdiff_t. When invocating FindMaxItem function the implicit conversion

of ptrdiff_t type to int type occurs which will cause the loss of the high bits. This

may be the reason for the incorrect program behavior while processing a large data

size.

The correct solution will be to replace int type with ptrdiff_t type for it will allow to

keep the whole range of values. The corrected code:

float FindMaxItem(float *array, ptrdiff_t arraySize) {

float max = -FLT_MAX;

for (ptrdiff_t i = 0; i != arraySize; ++i) {

float item = *array++;

if (max < item)

max = item;

}

return max;

}

Analyzer tries as far as possible to recognize safe type conversions and keep from

displaying warning messages on them. For example, the analyzer won't give a

warning message on FindMaxItem function's call in the following code:

float Arr[1000];

float maxValue =

FindMaxItem(Arr, sizeof(Arr)/sizeof(float));

When you are sure that the code is correct and the implicit type conversion of the

actual function argument does not cause errors you may use the explicit type

conversion so that to avoid showing warning messages. An example:

extern int nPenStyle

extern size_t nWidth;

extern COLORREF crColor;

Page 282: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

...

// Call constructor CPen::CPen(int, int, COLORREF)

CPen myPen(nPenStyle, static_cast<int>(nWidth), crColor);

In that case if you suspect that the code contains incorrect explicit conversions of

memsize types to 32-bit types about which the analyzer does not warn, you may use

the V202.

V108. Incorrect index type: 'foo[not a memsize-type]'. Use memsize type instead.The analyzer found a possible error of indexing large arrays. The error may consist

in the incorrect index determination.

The first example.

extern char *longString;

extern bool *isAlnum;

...

unsigned i = 0;

while (*longString) {

isAlnum[i] = isalnum(*longString++);

++i;

}

The given code is absolutely correct for the 32-bit platform where it is actually

impossible to process arrays more than UINT_MAX bytes (4Gb). On the 64-bit

platform it is possible to process an array with the size more than 4 Gb that is

sometimes very convenient. The error consists in the use of the variable

of unsigned type for indexing the array isAlnum. When we fill the

first UINT_MAX of the items the variable i overflow will occur and it will equal

zero. As the result we'll begin to rewrite the array isAlnum items which are situated

in the beginning and some items will be left unassigned.

Page 283: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The correction is to replace the variable i type with memsize type:

...

size_t i = 0;

while (*longString)

isAlnum[i++] = isalnum(*longString++);

The second example.

class Region {

float *array;

int Width, Height, Depth;

float Region::GetCell(int x, int y, int z) const;

...

};

float Region::GetCell(int x, int y, int z) const {

return array[x + y * Width + z * Width * Height];

}

For computational modeling programs the main memory size is an important

source, and the possibility to use more than 4 Gb of memory on the 64-bit

architecture increases calculating possibilities greatly. In such programs one-

dimensional arrays are often used which are then dealt with as three-dimensional

ones. There are functions for that which similar to GetCell that provides access to

the necessary items of the calculation area. But the given code may deal correctly

with arrays containing not more than INT_MAX (2Gb) items. The reason is in the

use of 32-bit int types which participate in calculating the item index. If the number

of items in the array array excesses INT_MAX (2 Gb) an overflow will occur and

the index value will be determined incorrectly. Programmers often make a mistake

trying to correct the code in the following way:

float Region::GetCell(int x, int y, int z) const {

return array[static_cast<ptrdiff_t>(x) + y * Width +

z * Width * Height];

}

Page 284: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

They know that according to C++ rules the expression for calculating the index will

have ptrdiff_t type and because of it hope to avoid the overflow. Unfortunately, the

overflow may occur inside the subexpression y * Width or z * Width * Height for to

determine them int type is still used.

If you want to correct the code without changing the types of the variables included

into the expression you should convert each variable explicitly to memsize type:

float Region::GetCell(int x, int y, int z) const {

return array[ptrdiff_t(x) +

ptrdiff_t(y) * ptrdiff_t(Width) +

ptrdiff_t(z) * ptrdiff_t(Width) *

ptrdiff_t(Height)];

}

Another decision is to replace the variables types with memsize type:

class Region {

float *array;

ptrdiff_t Width, Height, Depth;

float

Region::GetCell(ptrdiff_t x, ptrdiff_t y, ptrdiff_t z) const;

...

};

float Region::GetCell(ptrdiff_t x, ptrdiff_t y, ptrdiff_t z) const

{

return array[x + y * Width + z * Width * Height];

}

If you use expressions which type is different from memsize type for indexing but

are sure about the code correctness, you may use the explicit type conversion to

suppress the analyzer's warning messages as follows:

bool *Seconds;

int min, sec;

...

bool flag = Seconds[static_cast<size_t>(min * 60 + sec)];

Page 285: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

If you suspect that the program may contain errors related to the incorrect explicit

type conversion in expressions you may use the V201.

The analyzer tries as far as possible to understand when using non-memsize-type as

the array's index is safe and keep from displaying warnings in such cases. As the

result the analyzer's behaviour can sometimes seem strange. In such situations we

ask you not to hurry and try to analyze the situation. Let's consider the following

code:

char Arr[] = { '0', '1', '2', '3', '4' };

char *p = Arr + 2;

cout << p[0u + 1] << endl;

cout << p[0u - 1] << endl; //V108

This code works correctly in 32-bit mode and displays numbers 3 and 1. While

testing this code we'll get a warning message only on one line with the expression

"p[0u - 1]". And it's absolutely right. If you compile and launch this example in 64-

bit mode you'll see that the value 3 will be displayed and after that a program crash

will occur.

The error relates to that indexing of "p[0u - 1]" is incorrect on a 64-bit system and

this is what analyzer warns about. According to C++ rules "0u - 1" expression will

have unsigned type and equal 0xFFFFFFFFu. On a 32-bit architecture addition of

an index with this number will be the same as substraction of 1. And on a 64-bit

system 0xFFFFFFFFu value will be justly added to the index and memory will be

addressed outside the array.

Of course indexing to arrays with the use of such types as int and unsigned is often

safe. In this case analyzer's warnings may seem inappropriate. But you should keep

in mind that such code still may be unsafe in case of its modernization for

processing a different data set. The code with int and unsigned types can appear to

be less efficient than it is possible on a 64-bit architecture.

If you are sure that indexation is correct you use "Suppression of false alarms" or

use filters. You can use explicit type conversion in the code:

Page 286: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

for (int i = 0; i != n; ++i)

Array[static_cast<ptrdiff_t>(i)] = 0;

V109. Implicit type conversion of return value to memsize type.The analyzer found a possible error related to the implicit conversion of the return

value type. The error may consist in the incorrect determination of the return value.

Let's examine an example.

extern int Width, Height, Depth;

size_t GetIndex(int x, int y, int z) {

return x + y * Width + z * Width * Height;

}

...

array[GetIndex(x, y, z)] = 0.0f;

If the code deals with large arrays (more than INT_MAX items) it will behave

incorrectly and we will address not those items of the array array that we want. But

the analyzer won't show a warning message on the line array[GetIndex(x, y, z)] =

0.0f; for it is absolutely correct. The analyzer informs about a possible error inside

the function and is right for the error is located exactly there and is related to the

arithmetic overflow. In spite of the facte that we return the type size_t value the

expression x + y * Width + z * Width * Height is determined with the use of

type int.

To correct the error we should use the explicit conversion of all the variables

included into the expression to memsize types.

extern int Width, Height, Depth;

size_t GetIndex(int x, int y, int z) {

return (size_t)(x) +

(size_t)(y) * (size_t)(Width) +

(size_t)(z) * (size_t)(Width) * (size_t)(Height);

}

Page 287: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Another variant of correction is the use of other types for the variables included into

the expression.

extern size_t Width, Height, Depth;

size_t GetIndex(size_t x, size_t y, size_t z) {

return x + y * Width + z * Width * Height;

}

When you are sure that the code is correct and the implicit type conversion does not

cause errors while porting to the 64-bit architecture you may use the explicit type

conversion so that to avoid showing of the warning messages in this line. For

example:

DWORD_PTR Calc(unsigned a) {

return (DWORD_PTR)(10 * a);

}

In case you suspect that the code contains incorrect explicit type conversions to

memsize types about which the analyzer does not show warnings you may use

the V201.

V110. Implicit type conversion of return value from memsize type to 32-bit type.The analyzer found a possible error related to the implicit conversion of the return

value. The error consists in dropping of the high bits in the 64-bit type which causes

the loss of value.

Let's examine an example.

extern char *begin, *end;

unsigned GetSize() {

return end - begin;

}

Page 288: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The result of the end - begin expression has type ptrdiff_t. But as the function

returns type unsigned the implicit type conversion occurs which causes the loss of

the result high bits. Thus, if the pointers begin and end refer to the beginning and

the end of the array according to a larger UINT_MAX (4Gb), the function will return

the incorrect value.

The correction consists in modifying the program in such a way so that the arrays

sizes are kept and transported in memsize types. In this case the correct code of

the GetSize function should look as follows:

extern char *begin, *end;

size_t GetSize() {

return end - begin;

}

In some cases the analyzer won't display a warning message on type conversion if it

is obviously correct. For example, the analyzer won't display a warning message on

the following code where despite the fact that sizeof() operator's result is size_t type

it can be safely placed into unsigned type:

unsigned GetSize() {

return sizeof(double);

}

When you are sure that the code is correct and the implicit type conversion does not

cause errors while porting to the 64-bit architecture you may use the explicit type

conversion so that to avoid showing of the warning messages. For example:

unsigned GetBitCount() {

return static_cast<unsigned>(sizeof(TypeRGBA) * 8);

}

If you suspect that the code contains incorrect explicit conversions of the return

values types about which the analyzer does not warn you may use the V202.

Page 289: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V111. Call of function 'foo' with variable number of arguments. N argument has memsize type.The analyzer found a possible error related to the transfer of the actual argument

of memsize type into the function with variable number of arguments. The possible

error may consist in the change of demands made to the function on the 64-bit

system.

Let's examine an example.

const char *invalidFormat = "%u";

size_t value = SIZE_MAX;

printf(invalidFormat, value);

The given code does not take into account that size_t type does not coincide

with unsigned type on the 64-bit platform. It will cause the printing of the incorrect

result in case if value > UINT_MAX. The analyzer warns you that memsize type is

used as an actual argument. It means that you should check the

line invalidFormat assigning the printing format. The correct variant may look as

follows:

const char *validFormat = "%Iu";

size_t value = SIZE_MAX;

printf(validFormat, value);

In the code of a real application, this error can occur in the following form, e.g.:

wsprintf(szDebugMessage,

_T("%s location %08x caused an access violation.\r\n"),

readwrite,

Exception->m_pAddr);

The second example.

Page 290: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

char buf[9];

sprintf(buf, "%p", pointer);

The author of this inaccurate code did not take into account that the pointer size

may excess 32 bits later. As a result, this code will cause buffer overflow on the 64-

bit architecture. After checking the code on which the V111 warning message is

shown you may choose one of the two ways: to increase the buffer size or rewrite

the code using safe constructions.

char buf[sizeof(pointer) * 2 + 1];

sprintf(buf, "%p", pointer);

// --- or ---

std::stringstream s;

s << pointer;

The third example.

char buf[9];

sprintf_s(buf, sizeof(buf), "%p", pointer);

While examining the second example you could rightly notice that in order to

prevent the overflow you should use functions with security enhancements. In this

case the buffer overflow won't occur but unfortunately the correct result won't be

shown as well.

If the arguments types did not change their digit capacity the code is considered to

be correct and warning messages won't be shown. The example:

printf("%d", 10*5);

CString str;

size_t n = sizeof(float);

str.Format(StrFormat, static_cast<int>(n));

Unfortunately, we often cannot distinguish the correct code from the incorrect one

while diagnosing the described type of errors. This warning message will be shown

on many of calls of the functions with variable items number even when the call is

absolutely correct. It is related to the principal danger of using such C++

Page 291: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

constructions. Most frequent problems are the problems with the use of variants of

the following functions: printf, scanf, CString::Format. The generally accepted

practice is to refuse them and to use safe programming methods. For example, you

may replace printf with cout and sprintf with boost::format or std::stringstream.

Note. Eliminating false positives when working with formatted output

functions

The V111 diagnostic is very simple. When the analyzer has no information about a

variadic function, it warns you about every case when variable of memsize-type is

passed to that function. When it does have the information, the more accurate

diagnostic V576 joins in, controlling the output of the V111 diagnostic. If V576

finds nothing, no V111 warning is issued either.

Therefore, you can reduce the number of false positives by providing the analyzer

with information about the format functions. The analyzer is already familiar with

such typical functions as 'printf', 'sprintf', etc., so it is user-implemented functions

that you want to annotate. See the description of the V576 diagnostic for details

about annotating functions.

Consider the following example. You may ask, "Why does not the analyzer output a

V111 warning in case N1, but does that in case N2?"

void OurLoggerFn(wchar_t const* const _Format, ...)

{

....

}

void Foo(size_t length)

{

wprintf( L"%Iu", length ); // N1

OurLoggerFn( L"%Iu", length ); // N2

}

The reason is that the analyzer knows how standard function 'wprintf' works, while

it knows nothing about 'OurLoggerFn', so it prefers to be overcautious and issues a

Page 292: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

warning about passing a memsize-type variable ('size_t' in this case) as an actual

argument to a variadic function.

To eliminate the V111 warning, annotate the 'OurLoggerFn' function as follows:

//+V576, function:OurLoggerFn, format_arg:1, ellipsis_arg:2

void OurLoggerFn(wchar_t const* const _Format, ...)

.....

V112. Dangerous magic number N used.The analyzer found the use of a dangerous magic number. The possible error may

consist in the use of numeric literal as special values or size of memsize type.

Let's examine the first example.

size_t ArraySize = N * 4;

size_t *Array = (size_t *)malloc(ArraySize);

A programmer while writing the program relied on that the size size_t will be

always equal 4 and wrote the calculation of the array size "N * 4". This code dose

not take into account that size_t on the 64-bit system will have 8 bytes and will

allocate less memory than it is necessary. The correction of the code consists in the

use of sizeof operator instead of a constant 4.

size_t ArraySize = N * sizeof(size_t);

size_t *Array = (size_t *)malloc(ArraySize);

The second example.

size_t n = static_cast<size_t>(-1);

if (n == 0xffffffffu) { ... }

Sometimes as an error code or other special marker the value "-1" is used which is

written as "0xffffffff". On the 64-bit platform the written expression is incorrect and

one should evidently use the value "-1".

Page 293: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

size_t n = static_cast<size_t>(-1);

if (n == static_cast<size_t>(-1)) { ... }

Let's list magic numbers which may influence the efficiency of an application while

porting it on the 64-bit system and due to this are diagnosed by analyzer.

You should study the code thoroughly in order to see if there are magic constants

and replace them with safe constants and expressions. For this purpose you may

use sizeof() operator, special value from <limits.h>, <inttypes.h> etc.

In some cases magic constants are not considered unsafe. For example, there will be

no warning on this code:

float Color[4];

V113. Implicit type conversion from memsize to double type or vice versa.The analyzer found a possible error related to the implicit

conversion of memsize type to double type of vice versa. The possible error may

consist in the impossibility of storing the whole value range of memsize type in

variables of double type.

Page 294: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Let's study an example.

SIZE_T size = SIZE_MAX;

double tmp = size;

size = tmp; // x86: size == SIZE_MAX

// x64: size != SIZE_MAX

Double type has size 64 bits and is compatible IEEE-754 standard on 32-bit and 64-

bit systems. Some programmers use double type to store and work with integer

types.

The given example may be justified on a 32-bit system for double type has 52

significant bits and is capable to store a 32-bit integer value without a loss. But

while trying to store an integer number in a variable of double type the exact value

can be lost (see picture).

If an approximate value can be used for the work algorithm in your program no

corrections are needed. But we would like to warn you about the results of the

change of behavior of a code like this on 64-bit systems. In any case it is not

recommended to mix integer arithmetic with floating point arithmetic.

V114. Dangerous explicit type pointer conversion.The analyzer found a possible error related to the dangerous explicit type

conversion of a pointer of one type to a pointer of another. The error may consist in

the incorrect work with the objects to which the analyzer refers.

Let's examine an example. It contains the explicit type conversion of a int pointer to

a size_t pointer.

Page 295: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

int array[4] = { 1, 2, 3, 4 };

size_t *sizetPtr = (size_t *)(array);

cout << sizetPtr[1] << endl;

As you can see the result of the program output is different in 32-bit and 64-bit

variants. On the 32-bit system the access to the array items is correct for the sizes

of size_t and int types coincide and we see the output "2". On the 64-bit system we

got "17179869187" in output for it is this value 17179869187 which stays in the

first item of array sizetPtr.

The correction of the situation described consists in refusing dangerous type

conversions with the help of the program modernization. Another variant is to

create a new array and to copy into it the values from the original array.

Of course not all the explicit conversions of pointer types are dangerous. In the

following example the work result does not depend on the system capacity

for enum type and int type have the same size on the 32-bit system and the 64-bit

system as well. So the analyzer won't show any warning messages on this code.

int array[4] = { 1, 2, 3, 4 };

enum ENumbers { ZERO, ONE, TWO, THREE, FOUR };

ENumbers *enumPtr = (ENumbers *)(array);

cout << enumPtr[1] << endl;

V115. Memsize type is used for throw.The analyzer found a possible error related to the use of memsize type for throwing

an exception. The error may consist in the incorrect exception handling.

Let's examine an example of the code which contains throw and catch operators.

char *ptr1, *ptr2;

...

try {

throw ptr2 - ptr1;

Page 296: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

}

catch(int) {

Foo();

}

On 64-bit system the exception handler will not work and the function Foo () will

not be called. This results from the fact that expression "ptr2 - ptr1" has

type ptrdiff_t which on 64-bit system does not equivalent with type int.

The correction of the situation described consists in use of correct type for catch of

exception. In this case is necessary use of ptrdiff_t type, as noted below.

try {

throw ptr2 - ptr1;

}

catch(ptrdiff_t) {

Foo();

}

More right correction will consist in refusal of similar practice of programming. We

recommend to use special classes for sending information about the error.

V116. Memsize type is used for catch.The analyzer found a possible error related to the use of memsize type for catching

exception. The error may consist in the incorrect exception handling.

Let's examine an example of the code which contains throw and catch operators.

try {

try {

throw UINT64(-1);

}

catch(size_t) {

cout << "x64 portability issues" << endl;

Page 297: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

}

}

catch(UINT64) {

cout << "OK" << endl;

}

The work result on the 32-bit system: OK

The work result on the 64-bit system: x64 portability issues

This behavior change is connected with what on 64-bit system the size_t type is

equivalent to UINT64.

Correction of the described situation consists in change of a code for achievement

of necessary logic of work.

More right correction will consist in refusal of similar practice of programming. We

recommend using special classes for sending information about the error.

V117. Memsize type is used in the union.The analyzer found a possible error related to the use of memsize inside a union.

The error may occur while working with such unions without taking into account

the size changes of memsize types on the 64-bit system.

One should be attentive to the unions which contain pointers and other members of

memsize type.

The first example.

Sometimes one needs to work with a pointer as with an integer. The code in the

example is convenient because the explicit type conversions are not used for work

with the pointer number form.

union PtrNumUnion {

char *m_p;

Page 298: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

unsigned m_n;

} u;

...

u.m_p = str;

u.m_n += delta;

This code is correct on 32-bit systems and is incorrect on 64-bit ones. Changing

the m_n member on the 64-bit system we work only with a part of the m_p pointer.

One should use that type which would conform with the pointer size as follows.

union PtrNumUnion {

char *m_p;

size_t m_n; //type fixed

} u;

The second example.

Another frequent case of use of a union is the representation of one member as a set

of smaller ones. For example, we may need to split the size_t type value into bytes

for realization of the table algorithm of counting zero bits in a byte.

union SizetToBytesUnion {

size_t value;

struct {

unsigned char b0, b1, b2, b3;

} bytes;

} u;

SizetToBytesUnion u;

u.value = value;

size_t zeroBitsN = TranslateTable[u.bytes.b0] +

TranslateTable[u.bytes.b1] +

TranslateTable[u.bytes.b2] +

TranslateTable[u.bytes.b3];

A fundamental algorithmic error is made here which is based on the supposition that

the size_t type consists of 4 bytes. The automatic search of algorithmic errors is not

Page 299: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

possible on the current stage of development of static analyzers but Viva64 provides

search of all the unions which contain memsize types. Looking through the list of

such potentially dangerous unions a user can find logical errors. On finding the

union given in the example a user can detect an algorithmic error and rewrite the

code in the following way.

union SizetToBytesUnion {

size_t value;

unsigned char bytes[sizeof(value)];

} u;

SizetToBytesUnion u;

u.value = value;

size_t zeroBitsN = 0;

for (size_t i = 0; i != sizeof(u.bytes); ++i)

zeroBitsN += TranslateTable[u.bytes[i]];

This warning message is similar to the warning V122.

V118. malloc() function accepts a dangerous expression in the capacity of an argument.The analyzer detected a potential error relating to using a dangerous expression

serving as an actual argument for malloc function. The error may lie in incorrect

suggestions about types' sizes defined as numerical constants.

The analyzer considers suspicious those expressions which contain constant literals

multiple of four but which lack sizeof() operator.

Example 1.

An incorrect code of memory allocation for a matrix 3x3 of items of size_t type

may look as follows:

Page 300: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

size_t *pMatrix = (size_t *)malloc(36); // V118

Although this code could work very well in a 32-bit system, using number 36 is

incorrect. When compiling a 64-bit version 72 bytes must be allocated. You may

use sizeof () operator to correct this error:

size_t *pMatrix = (size_t *)malloc(9 * sizeof(size_t));

Example 2.

The following code based on the suggestion that the size of Item structure is 12

bytes is also incorrect for a 64-bit system:

struct Item {

int m_a;

int m_b;

Item *m_pParent;

};

Item *items = (Item *)malloc(GetArraySize() * 12); // V118

Correction of this error also consists in using sizeof() operator to correctly calculate

the size of the structure:

Item *items = (Item *)malloc(GetArraySize() * sizeof(Item));

These errors are simple and easy to correct. But they are nevertheless dangerous and

difficult to find in case of large applications. That's why diagnosis of such errors is

implemented as a separate rule.

Presence of a constant in an expression which is a parameter for malloc() function

does not necessarily means that V118 warning will be always shown on it. If

sizeof() operator participates in the expression this construction is safe. Here is an

example of a code which the analyzer considers safe:

int *items = (int *)malloc(sizeof(int) * 12);

Page 301: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V119. More than one sizeof() operator is used in one expression.The analyzer detected an unsafe arithmetic expression containing several sizeof()

operators. Such expressions can potentially contain errors relating to incorrect

calculations of the structures' sizes without taking into account field alignment.

Example:

struct MyBigStruct {

unsigned m_numberOfPointers;

void *m_Pointers[1];

};

size_t n2 = 1000;

void *p;

p = malloc(sizeof(unsigned) + n2 * sizeof(void *));

To calculate the size of the structure which will contain 1000 pointers, an arithmetic

expression is used which is correct at first sight. The sizes of the base types are

defined by sizeof() operators. It is good but not sufficient for correct calculation of

the necessary memory size. You should also take into account field alignment.

This example is correct for a 32-bit mode for the sizes of the pointers and unsigned

type coincide. They are both 4 bytes. The pointers and unsigned type are aligned

also at the boundary of four bytes. So the necessary memory size will be calculated

correctly.

In a 64-bit code the size of the pointer is 8 bytes. Pointers are aligned at the

boundary of 8 bytes as well. It leads to that after m_numberOfPointers variable 4

additional bytes will be situated at the boundary of 8 bytes to align the pointers.

To calculate the correct size you should use offsetof function:

p = malloc(offsetof(MyBigStruct, m_Pointers) +

n * sizeof(void *));

Page 302: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

In many cases using several sizeof() operators in one expression is correct and the

analyzer ignores such constructions. Here is an example of safe expressions with

several sizeof operators:

int MyArray[] = { 1, 2, 3 };

size_t MyArraySize =

sizeof(MyArray) / sizeof(MyArray[0]);

assert(sizeof(unsigned) < sizeof(size_t));

size_t strLen = sizeof(String) - sizeof(TCHAR);

V120. Member operator[] of object 'foo' declared with 32-bit type argument, but called with memsize type argument.The analyzer detected a potential error of working with classes that contain

operator[]. Classes with an overloaded operator[] are usually a kind of an array

where the index of the item being called is operator[] argument. If operator[] has a

32-bit type formal argument but memsize-type is used as an actual argument, it

might indicate an error. Let us consider an example leading to the warning V120:

class MyArray {

int m_arr[10];

public:

int &operator;[](unsigned i) { return m_arr[i]; }

} Object;

size_t k = 1;

Object[k] = 44; //V120

This example does not contain an error but might indicate an architecture

shortcoming. You should either work with MyArray using 32-bit indexes or modify

operator[] so that it takes an argument of size_t type. The latter is preferable

because memsize-types not only serve to make a program safer but sometimes

allow the compiler to build a more efficient code.

Page 303: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The related diagnostic warnings are V108 and V302.

V121. Implicit conversion of the type of 'new' operator's argument to size_t type.The analyzer detected a potential error related to calling the operator new. A value

of a non-memsize type is passed to the operator "new" as an argument. The operator

new takes values of the type size_t, and passing a 32-bit actual argument may signal

a potential overflow that may occur when calculating the memory amount being

allocated. Here is an example:

unsigned a = 5;

unsigned b = 1024;

unsigned c = 1024;

unsigned d = 1024;

char *ptr = new char[a*b*c*d]; //V121

Here you may see an overflow occurring when calculating the expression

"a*b*c*d". As a result, the program allocates less memory than it should. To correct

the code, use the type size_t:

size_t a = 5;

size_t b = 1024;

size_t c = 1024;

size_t d = 1024;

char *ptr = new char[a*b*c*d]; //Ok

The error will not be diagnosed if the value of the argument is defined as a safe 32-

bit constant value. Here is an example of safe code:

char *ptr = new char[100];

const int size = 3*3;

char *p2 = new char[size];

This warning message is similar to the warning V106.

Page 304: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V122. Memsize type is used in the struct/class.Sometimes you might need to find all the fields in the structures that have

a memsize-type. You can find such fields using the V122 diagnostic rule.

The necessity to view all the memsize-fields might appear when you port a program

that has structure serialization, for example, into a file. Consider an example:

struct Header

{

unsigned m_version;

size_t m_bodyLen;

};

...

size_t size = fwrite(&header, 1, sizeof(header), file);

...

This code writes a different number of bytes into the file depending on the mode it

is compiled in - either Win32 or Win64. This might violate compatibility of files'

formats or cause other errors.

The task of automating the detection of such errors is almost impossible to solve.

However, if there are some reasons to suppose that the code might contain such

errors, developers can once check all the structures that participate in serialization.

It is for this purpose that you may need a check with the V122 rule. By default it is

disabled since it generates false warnings in more than 99% of cases.

In the example above, the V122 message will be produced on the line "size_t

m_bodyLen;". To correct this code, you may use types of fixed size:

struct Header

{

My_UInt32 m_version;

My_UInt32 m_bodyLen;

};

Page 305: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

...

size_t size = fwrite(&header, 1, sizeof(header), file);

...

Let's consider other examples where the V122 message will be generated:

class X

{

int i;

DWORD_PTR a; //V122

DWORD_PTR b[3]; //V122

float c[3][4];

float *ptr; //V122

};

V117 is a related diagnostic message.

Note. If you are sure that structures containing pointers will never serialize, you

may use this comment:

//-V122_NOPTR

It will suppress all warnings related to pointers.

This comment should be added into the header file included into all the other files.

For example, such is the "stdafx.h" file. If you add this comment into a "*.cpp" file,

it will affect only this particular file.

V123. Allocation of memory by the pattern "(X*)malloc(sizeof(Y))" where the sizes of X and Y types are not equal.The analyzer found a potential error related to the operation of memory allocation.

When calculating the amount of memory to be allocated, the sizeof(X) operator is

Page 306: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

used. The result returned by the memory allocation function is converted to a

different type, "(Y *)", instead of "(X *)". It may indicate allocation of insufficient

or excessive amount of memory.

Consider the first example:

int **ArrayOfPointers = (int **)malloc(n * sizeof(int));

The misprint in the 64-bit program here will cause allocation of memory twice less

than necessary. In the 32-bit program, the sizes of the "int" type and "pointer to int"

coincide and the program works correctly despite the misprint.

This is the correct version of the code:

int **ArrayOfPointers = (int **)malloc(n * sizeof(int *));

Consider another example where more memory is allocated than needed:

unsigned *p = (unsigned *)malloc(len * sizeof(size_t));

A program with such code will most probably work correctly both in the 32-bit and

64-bit versions. But in the 64-bit version, it will allocate more memory than it

needs. This is the correct code:

unsigned *p = (unsigned *)malloc(len * sizeof(unsigned));

In some cases the analyzer does not generate a warning although the types X and Y

do not coincide. Here is an example of such correct code:

BYTE *simpleBuf = (BYTE *)malloc(n * sizeof(float));

V124. Function 'Foo' writes/reads 'N' bytes. The alignment rules and type sizes have been changed. Consider reviewing this value.

Page 307: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer detected a potential error: the size of data being written or read is

defined by a constant. When the code is compiled in the 64-bit mode, the sizes of

some data and their alignment boundaries will change. The sizes of base types and

their alignment boundaries are shown in the picture:

The analyzer examines code fragments where the size of data being written or read

is defined explicitly. The programmer must review these fragments. Here is a code

sample:

Page 308: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

size_t n = fread(buf, 1, 40, f_in);

Constant 40 may be an incorrect value from the viewpoint of the 64-bit system.

Perhaps you should write it so:

size_t n = fread(buf, 1, 10 * sizeof(size_t), f_in);

V125. It is not advised to declare type 'T' as 32-bit type.The analyzer detected a potential error: 64-bit code contains definitions of reserved

types, the latter being defined as 32-bit ones. For example:

typedef unsigned size_t;

typedef __int32 INT_PTR;

Such type definitions may cause various errors since these types have different sizes

in different parts of the program and libraries. For instance, the size_t type is

defined in the stddef.h header file for the C language and in the cstddef file for the

C++ language.

References:

1. Knowledge Base. Is there a way to make the type size_t 32-bit in a 64-bit program? http://www.viva64.com/en/k/0021/

2. Knowledge Base. Is size_t a standard type in C++? And in C? http://www.viva64.com/en/k/0022/

V126. Be advised that the size of the type 'long' varies between LLP64/LP64 data models.This diagnostic message lets you find all the 'long' types used in a program.

Page 309: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Of course, presence of the 'long' type in a program is not an error in itself. But you

may need to review all the fragments of the program text where this type is used

when you create portable 64-bit code that must work well in Windows and Linux.

Windows and Linux use different data models for the 64-bit architecture. A data

model means correlations of sizes of base data types such as int, float, pointer, etc.

Windows uses the LLP64 data model while Linux uses the LP64 data model. In

these models, the sizes of the 'long' type are different.

In Windows (LLP64), the size of the 'long' type is 4 bytes.

In Linux (LP64), the size of the 'long' type is 8 bytes.

The difference of the 'long' type's sizes may make files' formats incompatible or

cause errors when developing code executed in Linux and Windows. So if you

want, you may use PVS-Studio to review all the code fragments where the 'long'

type is used.

References:

1. Terminology. Data model. http://www.viva64.com/en/t/0012/

V127. An overflow of the 32-bit variable is possible inside a long cycle which utilizes a memsize-type loop counter.The analyzer detected a potential error: a 32-bit variable might overflow in a long

loop. Of course, the analyzer will not be able to find all the possible cases when

variable overflows in loops occur. But it will help you find some incorrect type

constructs. For example:

int count = 0;

for (size_t i = 0; i != N; i++)

{

Page 310: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

if ((A[i] & MASK) != 0)

count++;

}

This code works well in a 32-bit program. The variable of the 'int' type is enough to

count the number of some items in the array. But in a 64-bit program the number of

these items may exceed INT_MAX and an overflow of the 'count' variable will

occur. This is what the analyzer warns you about by generating the V127 message.

This is the correct code:

size_t count = 0;

for (size_t i = 0; i != N; i++)

{

if ((A[i] & MASK) != 0)

count++;

}

The analyzer also contains several additional checks to make false reports fewer.

For instance, the V127 warning will not be generated when we deal with a short

loop. Here you are a sample of code the analyzer considers safe:

int count = 0;

for (size_t i = 0; i < 100; i++)

{

if ((A[i] & MASK) != 0)

count++;

}

V128. A variable of the memsize type is read from a stream. Consider verifying the compatibility of 32 and 64 bit versions of the

Page 311: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

application in the context of a stored data.The analyzer has detected a potential error related to data incompatibility between

the 32-bit and 64-bit versions of an application, when memsize-variables are being

written to or read from the stream. The error is this: data written to the binary file in

the 32-bit program version will be read incorrectly by the 64-bit one.

For example:

std::vector<int> v;

....

ofstream os("myproject.dat", ios::binary);

....

os << v.size();

The 'size()' function returns a value of the size_t type whose size is different in 32-

bit and 64-bit applications. Consequently, different numbers of bytes will be written

to the file.

There exist many ways to avoid the data incompatibility issue. The simplest and

crudest one is to strictly define the size of types being written and read. For

example:

std::vector<int> v;

....

ofstream os("myproject.dat", ios::binary);

....

os << static_cast<__int64>(v.size());

A strictly defined cast to 64-bit types cannot be called a nice solution, of course.

The reason is that this method won't let the program read data written by the old 32-

bit program version. On the other hand, if data are defined to be read and written as

32-bit values, we face another problem: the 64-bit program version won't be able to

write information about arrays consisting of more than 2^32 items. This may be a

Page 312: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

disappointing limitation, as 64-bit software is usually created to handle huge data

arrays.

A way out can be found through introducing a notion of the version of saved data.

For example, 32-bit applications can open files created by the 32-bit version of your

program, while 64-bit applications can handle data generated both by the 32-bit and

64-bit versions.

One more way to solve the compatibility problem is to store data in the text format

or the XML format.

Note that this compatibility issue is irrelevant in many programs. If your application

doesn't create projects and other files to be opened on other computers, you may

turn off the V128 diagnostic.

You also shouldn't worry if the stream is used to print values on the screen. PVS-

Studio tries to detect these situations and avoid generating the message. False

positives are, however, still possible. If you get them, use one of the false positive

suppression mechanisms described in the documentation.

Additional features

According to users demand, we added a possibility to manually point out functions,

which saves or loads data. When somewhere in code a memsize-type is passed to

one of these functions, this code considered dangerous.

Addition format is as follows: just above function prototype (or near its realization,

or in standard header file) user should add a special comment. Let us start with the

usage example:

//+V128, function:write, non_memsize:2

void write(string name, char);

void write(string name, int32);

void write(string name, int64);

foo()

{

  write("zz", array.size()); // warning V128

Page 313: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

}

Format:

"function" key represents name of the function to be checked by analyzer. This key is necessary – without this key addition, of course, would not work.

"class" key – non-necessary key that allows to enter class name to which this function belongs (i.e. class method). Without specifying it analyzer will check any function with given name, with specifying – only ones that belongs to the particular class.

"namespace" key – non-necessary key that allows to enter namespace name to which function belongs. Again, without specifying it analyzer will check any function with given name, with specifying – only ones that belongs to the particular namespace. Key will correctly work with the "class" key – analyzer then will check any class method with given name that belongs to particular namespace.

"non_memsize" key allows specifying number of argument that should not allow type, which size changes depending on architecture. Number counts from one, not from zero. There is a technical restriction – this number should not exceed 14. There may be multiple "non-memsize" keys if there is a need to check multiple function arguments.

Warning level in case of user functions is always first.

At last, here is full usage example:

// Warns when in method C of class B

// from A namespace memsize-type value

// is put as a second or third argument.

//+V128,namespace:A,class:B,function:C,non_memsize:3,non_memsize:2

V201. Explicit conversion from 32-bit integer type to memsize type.It informs about the presence of the explicit type conversion from 32-bit integer

type to memsize type which may hide one of the following

errors: V101, V102, V104, V105, V106, V108, V109. You may address to the

given warnings list to find out the cause of showing the diagnosis message V201.

Page 314: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The V201 warning applied to conversions of 32-bit integer types to pointers before.

Such conversions are rather dangerous, so we singled them out into a separate

diagnostic rule V204.

Keep in mind that most of the warnings of this type will be likely shown on the

correct code. Here are some examples of the correct and incorrect code on which

this warning will be shown.

The examples of the incorrect code.

int i;

ptrdiff_t n;

...

for (i = 0; (ptrdiff_t)(i) != n; ++i) { //V201

...

}

unsigned width, height, depth;

...

size_t arraySize = size_t(width * height * depth); //V201

The examples of the correct code.

const size_t seconds = static_cast<size_t>(60 * 60); //V201

unsigned *array;

...

size_t sum = 0;

for (size_t i = 0; i != n; i++) {

sum += static_cast<size_t>(array[i] / 4); //V201

}

unsigned width, height, depth;

...

size_t arraySize =

size_t(width) * size_t(height) * size_t(depth); //V201

Page 315: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V202. Explicit conversion from memsize type to 32-bit integer type.It informs about the presence of the explicit integer memsize type conversion to 32-

bit type which may hide one of the following errors: V103, V107, V110. You may

see the given warnings list to find out the cause of showing the warning message

V202.

The V202 warning applied to conversions of pointers to 32-bit integer types before.

Such conversions are rather dangerous, so we singled them out into a separate

rule V205.

Keep in mind that most of the warnings of this type will be likely shown on the

correct code. Here are some examples of the correct and incorrect code on which

this warning will be shown.

The examples of the incorrect code.

size_t n;

...

for (unsigned i = 0; i != (unsigned)n; ++i) { //V202

...

}

UINT_PTR width, height, depth;

...

UINT arraySize = UINT(width * height * depth); //V202

The examples of the correct code.

const unsigned bits =

unsigned(sizeof(object) * 8); //V202

extern size_t nPane;

extern HICON hIcon;

BOOL result =

Page 316: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

SetIcon(static_cast<int>(nPane), hIcon); //V202

V203. Explicit type conversion from memsize to double type or vice versa.The analyzer found a possible error related to the explicit conversion

of memsize type into double type and vice versa. The possible error may consist in

the impossibility to save the whole range of values of memsize type in variables

of double type.

This error is completely similar to error V113. The difference is in that the explicit

type conversion is used as in a further example:

SIZE_T size = SIZE_MAX;

double tmp = static_cast<double>(size);

size = static_cast<SIZE_T>(tmp); // x86: size == SIZE_T

// x64: size != SIZE_T

To study this kind of errors see the description of error V113.

V204. Explicit conversion from 32-bit integer type to pointer type.This warning informs you about an explicit conversion of a 32-bit integer type to a

pointer type. We used the V201 diagnostic rule before to diagnose this situation.

But explicit conversion of the 'int' type to pointer is much more dangerous than

conversion of 'int' to 'intptr_t'. That is why we created a separate rule to search for

explicit type conversions when handling pointers.

Here is a sample of incorrect code.

int n;

float *ptr;

Page 317: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

...

ptr = (float *)(n);

The 'int' type's size is 4 bytes in a 64-bit program, so it cannot store a pointer whose

size is 8 bytes. Type conversion like in the sample above usually signals an error.

What is very unpleasant about such errors is that they can hide for a long time

before you reveal them. A program might store pointers in 32-bit variables and

work correctly for some time as long as all the objects created in the program are

located in low-order addresses of memory.

If you need to store a pointer in an integer variable for some reason, you'd better

use memsize-types. For instance: size_t, ptrdiff_t, intptr_t, uintptr_t.

This is the correct code:

intptr_t n;

float *ptr;

...

ptr = (float *)(n);

However, there is a specific case when you may store a pointer in 32-bit types. I am

speaking about handles which are used in Windows to work with various system

objects. Here are examples of such types: HANDLE, HWND, HMENU,

HPALETTE, HBITMAP, etc. Actually these types are pointers. For instance,

HANDLE is defined in header files as "typedef void *HANDLE;".

Although handles are 64-bit pointers, only the less significant 32 bits are employed

in them for the purpose of better compatibility (for example, to enable 32-bit and

64-bit processes interact with each other). For details, see "Microsoft Interface

Definition Language (MIDL): 64-Bit Porting Guide" (USER and GDI handles are

sign extended 32b values).

Such pointers can be stored in 32-bit data types (for instance, int, DWORD). To cast

such pointers to 32-bit types and vice versa special functions are used:

void * Handle64ToHandle( const void * POINTER_64 h )

Page 318: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

void * POINTER_64 HandleToHandle64( const void *h )

long HandleToLong ( const void *h )

unsigned long HandleToUlong ( const void *h )

void * IntToPtr ( const int i )

void * LongToHandle ( const long h )

void * LongToPtr ( const long l )

void * Ptr64ToPtr ( const void * POINTER_64 p )

int PtrToInt ( const void *p )

long PtrToLong ( const void *p )

void * POINTER_64 PtrToPtr64 ( const void *p )

short PtrToShort ( const void *p )

unsigned int PtrToUint ( const void *p )

unsigned long PtrToUlong ( const void *p )

unsigned short PtrToUshort ( const void *p )

void * UIntToPtr ( const unsigned int ui )

void * ULongToPtr ( const unsigned long ul )

Additional materials on this topic:

1. Knowledge Base. How to correctly cast a pointer to int in a 64-bit application? http://www.viva64.com/en/k/0005/

V205. Explicit conversion of pointer type to 32-bit integer type.This warning informs you about an explicit conversion of a pointer type to a 32-bit

integer type. We used the V202 diagnostic rule before to diagnose this situation. But

explicit conversion of a pointer to the 'int' type is much more dangerous than

conversion of 'intptr_t' to 'int'. That is why we created a separate rule to search for

explicit type conversions when handling pointers.

Here is a sample of incorrect code.

int n;

float *ptr;

...

n = (int)ptr;

Page 319: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The 'int' type's size is 4 bytes in a 64-bit program, so it cannot store a pointer whose

size is 8 bytes. Type conversion like in the sample above usually signals an error.

What is very unpleasant about such errors is that they can hide for a long time

before you reveal them. A program might store pointers in 32-bit variables and

work correctly for some time as long as all the objects created in the program are

located in low-order addresses of memory.

If you need to store a pointer in an integer variable for some reason, you'd better

use memsize-types. For instance: size_t, ptrdiff_t, intptr_t, uintptr_t.

This is the correct code:

intptr_t n;

float *ptr;

...

n = (intptr_t)ptr;

However, there is a specific case when you may store a pointer in 32-bit types. I am

speaking about handles which are used in Windows to work with various system

objects. Here are examples of such types: HANDLE, HWND, HMENU,

HPALETTE, HBITMAP, etc. Actually these types are pointers. For instance,

HANDLE is defined in header files as "typedef void *HANDLE;".

Although handles are 64-bit pointers, only the less significant 32 bits are employed

in them for the purpose of better compatibility (for example, to enable 32-bit and

64-bit processes interact with each other). For details, see "Microsoft Interface

Definition Language (MIDL): 64-Bit Porting Guide" (USER and GDI handles are

sign extended 32b values).

Such pointers can be stored in 32-bit data types (for instance, int, DWORD). To cast

such pointers to 32-bit types and vice versa special functions are used:

void * Handle64ToHandle( const void * POINTER_64 h )

void * POINTER_64 HandleToHandle64( const void *h )

long HandleToLong ( const void *h )

unsigned long HandleToUlong ( const void *h )

Page 320: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

void * IntToPtr ( const int i )

void * LongToHandle ( const long h )

void * LongToPtr ( const long l )

void * Ptr64ToPtr ( const void * POINTER_64 p )

int PtrToInt ( const void *p )

long PtrToLong ( const void *p )

void * POINTER_64 PtrToPtr64 ( const void *p )

short PtrToShort ( const void *p )

unsigned int PtrToUint ( const void *p )

unsigned long PtrToUlong ( const void *p )

unsigned short PtrToUshort ( const void *p )

void * UIntToPtr ( const unsigned int ui )

void * ULongToPtr ( const unsigned long ul )

Let's take a look at the following example:

HANDLE h = Get();

UINT uId = (UINT)h;

The analyzer does not generate the message here, though HANDLE is nothing but a

pointer. Values of this pointer always fit into 32 bits. Just make sure you take care

when working with them in future. Keep in mind that non-valid handles are

declared in the following way:

#define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1)

That's why it would be incorrect to write the next line like this:

if (HANDLE(uID) == INVALID_HANDLE_VALUE)

Since the 'uID' variable is unsigned, the pointer's value will equal

0x00000000FFFFFFFF, not 0xFFFFFFFFFFFFFFFF.

The analyzer will generate the V204 warning for a suspicious check when unsigned

turns into a pointer.

Additional materials on this topic:

Page 321: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

1. Knowledge Base. How to correctly cast a pointer to int in a 64-bit application? http://www.viva64.com/en/k/0005/

V206. Explicit conversion from 'void *' to 'int *'.This warning informs you about an explicit conversion of the 'void *' or 'byte *'

pointer to a function pointer or 32/64-bit integer pointer. Or vice versa.

Of course, the type conversion like that is not in itself an error. Let's figure out what

for we have implemented this diagnostic.

It is a pretty frequent situation when a pointer to some memory buffer is passed into

another part of the program through a void * or byte * pointer. There may be

different reasons for doing so; it usually indicates a poor code design, but this

question is out of the scope of this paper. Function pointers are often stored as void

* pointers, too.

So, assume we have an array/function pointer saved as void * in some part of the

program while it is cast back in another part. When porting such a code, you may

get unpleasant errors: a type may change in one place but stay unchanged in some

other place.

For example:

size_t array[20];

void *v = array;

....

unsigned* sizes = (unsigned*)(v);

This code works well in the 32-bit mode as the sizes of the 'unsigned' and 'size_t'

types coincide. In the 64-bit mode, however, their sizes are different and the

program will behave unexpectedly. See also pattern 6, changing an array type.

The analyzer will point out the line with the explicit type conversion where you will

discover an error if study it carefully. The fixed code may look like this:

Page 322: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

unsigned array[20];

void *v = array;

....

unsigned* sizes = (unsigned*)(v);

or like this:

size_t array[20];

void *v = array;

....

size_t* sizes = (size_t*)(v);

A similar error may occur when working with function pointers.

void Do(void *ptr, unsigned a)

{

typedef void (*PtrFoo)(DWORD);

PtrFoo f = (PtrFoo)(ptr);

f(a);

}

void Foo(DWORD_PTR a) { /*... */ }

void Call()

{

Do(Foo, 1);

}

The fixed code:

typedef void (*PtrFoo)(DWORD_PTR);

Note. The analyzer knows about the plenty of cases when explicit type conversion

is safe. For instance, it doesn't worry about explicit type conversion of a void *

pointer returned by the malloc() function:

int *p = (int *)malloc(sizeof(int) * N);

Page 323: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

As said in the beginning, explicit type conversion is not in itself an error. That's

why, despite numbers of exceptions to this rule, the analyzer still generates quite a

lot of false V206 warnings. It doesn't know if there are any other fragments in the

program where these pointers are used incorrectly, so it has to generate warnings on

every potentially dangerous type conversion.

For instance, I've cited two examples of incorrect code and ways to fix them above.

Even after they are fixed, the analyzer will keep generating false positives on the

already correct code.

You can use the following approach to handle this warning: carefully study all the

V206 messages once and then disable this diagnostic in the settings. If there are few

false positives, use one of the false positive suppression methods.

V207. A 32-bit variable is utilized as a reference to a pointer. A write outside the bounds of this variable may occur.This warning informs you about an explicit conversion of a 32-bit integer variable

to the reference to pointer type.

Let's start with a simple synthetic example:

int A;

(int *&)A = pointer;

Suppose we need for some reason to write a pointer into an integer variable. To do

this, we can cast the integer 'A' variable to the 'int *&' type (reference to pointer).

This code can work well in a 32-bit system as the 'int' type and the pointer have the

same sizes. But in a 64-bit system, writing outside the 'A' variable's memory bounds

will occur, which will in its turn lead to undefined behavior.

To fix the bug, we need to use one of the memsize-types - for example intptr_t:

Page 324: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

intptr_t A;

(intptr_t *&)A = pointer;

Now let's discuss a more complicated example, based on code taken from a real-life

application:

enum MyEnum { VAL1, VAL2 };

void Get(void*& data) {

static int value;

data = &value;

}

void M() {

MyEnum e;

Get((void*&)e);

....

}

There is a function which returns values of the pointer type. One of the returned

values is written into a variable of the 'enum' type. We won't discuss now the reason

for doing so; we are rather interested in the fact that this code used to work right in

the 32-bit mode while its 64-bit version doesn't - the Get() function changes not

only the 'e' variable but the nearby memory as well.

V220. Suspicious sequence of types castings: memsize -> 32-bit integer -> memsize.The warning informs you about a strange sequence of type conversions.

A memsize-type is explicitly cast to a 32-bit integer type and then is again cast to a

memsize-type either explicitly or implicitly. Such a sequence of conversions leads

to a loss of high-order bits. Usually it signals a crucial error.

Consider this sample:

char *p1;

Page 325: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

char *p2;

ptrdiff_t n;

...

n = int(p1 - p2);

We have an unnecessary conversion to the 'int' type here. It must not be here and

even might cause a failure if p1 and p2 pointers are more than INT_MAX items

away from each other in a 64-bit program.

This is the correct code:

char *p1;

char *p2;

ptrdiff_t n;

...

n = p1 - p2;

Let's consider another sample:

BOOL SetItemData(int nItem, DWORD_PTR dwData);

...

CItemData *pData = new CItemData;

...

CListCtrl::SetItemData(nItem, (DWORD)pData);

This code will cause an error if the CltemData object is created beyond the four

low-order Gbytes of memory. This is the correct code:

BOOL SetItemData(int nItem, DWORD_PTR dwData);

...

CItemData *pData = new CItemData;

...

CListCtrl::SetItemData(nItem, (DWORD_PTR)pData);

One should keep in mind that the analyzer does not generate the warning when

conversion is done over such data types as HANDLE, HWND, HCURSOR, and so

on. Although these types are in fact pointers (void *), their values always fit into the

least significant 32 bits. It is done on purpose so that these handles could be passed

Page 326: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

between 32-bit and 64-bit processes. For details see How to correctly cast a pointer

to int in a 64-bit application?

Have a look at the following example:

typedef void * HANDLE;

HANDLE GetHandle(DWORD nStdHandle);

int _open_osfhandle(intptr_t _OSFileHandle, int _Flags);

....

int fh = _open_osfhandle((int)GetHandle(sh), 0);

We are dealing with a conversion of the following kind:

HANDLE -> int -> intptr_t

That is, the pointer is first cast to the 32-bit 'int' type and then is extended to

'intptr_t'. It doesn’t look nice. The programmer should rather have written it like

"(intptr_t)GetHandle(STD_OUTPUT_HANDLE)". But there is still no error here as

values of the HANDLE type fit into 'int'. That’s why the analyzer keeps silent.

If it were written like this:

int fh = _open_osfhandle((unsigned)GetHandle(sh), 0);

the analyzer would generate the message. Mixing signed and unsigned types

together spoils it all. Suppose GetHandle() returns INVALID_HANDLE_VALUE.

This value is defined in the system headers in the following way:

#define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1)

Now, what we get after the conversion (intptr_t)(unsigned)((HANDLE)

(LONG_PTR)-1) is:

-1 -> 0xffffffffffffffff -> HANDLE -> 0xffffffffu -> 0x00000000fffffffff

The value -1 has turned into 4294967295. The programmer may fail to notice and

take this into account and the program will keep running incorrectly if the

GetHandle() function returns INVALID_HANDLE_VALUE. Because of that, the

analyzer will generate the warning in the second case.

Page 327: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V221. Suspicious sequence of types castings: pointer -> memsize -> 32-bit integer.This warning informs the programmer about the presence of a strange sequence of

type conversions. A pointer is explicitly cast to a memsize-type and then again,

explicitly or implicitly, to the 32-bit integer type. This sequence of conversions

causes a loss of the most significant bits. It usually indicates a serious error in the

code.

Take a look at the following example:

int *p = Foo();

unsigned a, b;

a = size_t(p);

b = unsigned(size_t(p));

In both cases, the pointer is cast to the 'unsigned' type, causing its most significant

part to be truncated. If you then cast the variable 'a' or 'b' to a pointer again, the

resulting pointer is likely to be incorrect.

The difference between the variables 'a' and 'b' is only in that the second case is

harder to diagnose. In the first case, the compiler will warn you about the loss of the

most significant bits, but keep silent in the second case as what is used there is an

explicit type conversion.

To fix the error, we should store pointers in memsize-types only, for example in

variables of the size_t type:

int *p = Foo();

size_t a, b;

a = size_t(p);

b = size_t(p);

Page 328: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

There may be difficulties with understanding why the analyzer generates the

warning on the following code pattern:

BOOL Foo(void *ptr)

{

return (INT_PTR)ptr;

}

You see, the BOOL type is nothing but a 32-bit 'int' type. So we are dealing with a

sequence of type conversions:

pointer -> INT_PTR -> int.

You may think there's actually no error here because what matters to us is only

whether or not the pointer is equal to zero. But the error is real. It's just that

programmers sometimes confuse the ways the types BOOL and bool behave.

Assume we have a 64-bit variable whose value equals 0x000012300000000.

Casting it to bool and BOOL will have different results:

int64_t v = 0x000012300000000ll;

bool b = (bool)(v); // true

BOOL B = (BOOL)(v); // FALSE

In the case of 'BOOL', the most significant bits will be simply truncated and the

non-zero value will turn to 0 (FALSE).

It's just the same with the pointer. When explicitly cast to BOOL, its most

significant bits will get truncated and the non-zero pointer will turn to the integer 0

(FALSE). Although low, there is still some probability of this event. Therefore,

code like that is incorrect.

To fix it, we can go two ways. The first one is to use the 'bool' type:

bool Foo(void *ptr)

{

return (INT_PTR)ptr;

Page 329: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

}

But of course it's better and easier to do it like this:

bool Foo(void *ptr)

{

return ptr != nullptr;

}

The method shown above is not always applicable. For instance, there is no 'bool'

type in the C language. So here's the second way to fix the error:

BOOL Foo(void *ptr)

{

return ptr != NULL;

}

Keep in mind that the analyzer does not generate the warning when conversion is

done over such data types as HANDLE, HWND, HCURSOR, and so on. Although

these are in fact pointers (void *), their values always fit into the least significant 32

bits. It is done on purpose so that these handles could be passed between 32-bit and

64-bit processes. For details, see: How to correctly cast a pointer to int in a 64-bit

application?

V301. Unexpected function overloading behavior. See N argument of function 'foo' in derived class 'derived' and base class 'base'.The analyzer found a possible error related to the changes in the overriding virtual

functions behavior.

The example of the change in the virtual function behavior.

Page 330: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

class CWinApp {

...

virtual void WinHelp(DWORD_PTR dwData, UINT nCmd);

...

};

class CSampleApp : public CWinApp {

...

virtual void WinHelp(DWORD dwData, UINT nCmd);

...

};

It is the common example which the developer may face while porting his

application to the 64-bit architecture. Let's follow the life-cycle of the developing of

some application. Suppose it was being developed for Visual Studio 6.0. at first

when the function WinHelp in class CWinApp had the following prototype:

virtual void WinHelp(DWORD dwData, UINT nCmd = HELP_CONTEXT);

It would be absolutely correct to implement the overlap of the virtual function in

class CSampleApp, as it is shown in the example. Then the project was placed into

Visual Studio 2005 where the prototype of the function in

class CWinApp underwent changes that consist in replacing DWORD type

with DWORD_PTR type. On the 32-bit platform this program will continue to work

properly for here DWORD and DWORD_PTR types coincide. Troubles will occur

while compliling this code for the 64-bit platform. We get two functions with the

same names but with different parameters the result of which is that the user's code

won't be called.

The analyzer allows to find such errors the correction of which is not difficult. It is

enough to change the function prototype in the successor class as follows:

class CSampleApp : public CWinApp {

...

virtual void WinHelp(DWORD_PTR dwData, UINT nCmd);

...

};

Page 331: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V302. Member operator[] of 'foo' class has a 32-bit type argument. Use memsize-type here.The analyzer detected a potential error of working with classes that contain

operator[]. Classes with an overloaded operator[] are usually a kind of an array

where the index of the item being called is operator[] argument. If operator[] has a

32-bit type argument it might indicate an error. Let us consider an example leading

to the warning V302:

class MyArray {

std::vector<float> m_arr;

...

float &operator[](int i) //V302

{

DoSomething();

return m_arr[i];

}

} A;

...

int x = 2000;

int y = 2000;

int z = 2000;

A[x * y * z] = 33;

If the class is designed to work with many arguments, implementing operator[] like

this is incorrect because it does not allow addressing the items whose numbers are

more than UINT_MAX. To diagnose the error in the example above you should

point to the potentially incorrect operator[]. The expression "x * y * z" does not

look suspicious because there is no implicit type conversion. When we correct

operator[] in the following way:

float &operator[](ptrdiff_t i);

Page 332: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

PVS-Studio analyzer warns about a potential error in the line "A[x * y * z] = 33;"

and now we can make the code absolutely correct. Here is an example of the

corrected code:

class MyArray {

std::vector<float> m_arr;

...

float &operator[](ptrdiff_t i) //V302

{

DoSomething();

return m_arr[i];

}

} A;

...

ptrdiff_t x = 2000;

ptrdiff_t y = 2000;

ptrdiff_t z = 2000;

A[x * y * z] = 33;

The related diagnostic warnings are V108 and V120.

V303. The function is deprecated in the Win64 system. It is safer to use the 'foo' function.

EnumProcessModules

SetWindowLong

GetFileSize

You should replace some functions with their new versions when porting an

application to 64-bit systems. Otherwise, the 64-bit application might work

incorrectly. The analyzer warns about the use of deprecated functions in code and

offers versions to replace them.

Let's consider several examples of deprecated functions:

Page 333: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

EnumProcessModulesExtract from MSDN:

To control whether a 64-bit application enumerates 32-bit modules, 64-bit modules,

or both types of modules, use the EnumProcessModulesEx function.

SetWindowLongExtract from MSDN:

This function has been superseded by the SetWindowLongPtr function. To write

code that is compatible with both 32-bit and 64-bit versions of Windows, use

the SetWindowLongPtr function.

GetFileSizeExtract from MSDN:

When lpFileSizeHigh is NULL, the results returned for large files are ambiguous,

and you will not be able to determine the actual size of the file. It is recommended

that you use GetFileSizeEx instead.

V501. There are identical sub-expressions to the left and to the right of the 'foo' operator.The analyzer found a code fragment that most probably has a logic error. There is

an operator (<, >, <=, >=, ==, !=, &&, ||, -, /) in the program text to the left and to

the right of which there are identical subexpressions.

Consider an example:

if (a.x != 0 && a.x != 0)

Page 334: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

In this case, the '&&' operator is surrounded by identical subexpressions "a.x != 0"

and it allows us to detect an error made through inattention. The correct code that

will not look suspicious to the analyzer looks in the following way:

if (a.x != 0 && a.y != 0)

Consider another example of an error detected by the analyzer in the code of a real

application:

class Foo {

int iChilds[2];

...

bool hasChilds() const { return(iChilds > 0 || iChilds > 0); }

...

}

In this case, the code is senseless though it is compiled successfully and without any

warnings. Correct code must look as follows:

bool hasChilds() const { return(iChilds[0] > 0 || iChilds[1] > 0);}

The analyzer does not generate the warning in all the cases when there are identical

subexpressions to the left and to the right of the operator.

The first exception refers to those constructs where the increment operator ++, the

decrement operator - or += and -= operator are used. Here is an example taken from

a real application:

do {

} while (*++scan == *++match && *++scan == *++match &&

*++scan == *++match && *++scan == *++match &&

*++scan == *++match && *++scan == *++match &&

*++scan == *++match && *++scan == *++match &&

scan < strend);

The analyzer considers this code safe.

Page 335: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The second exception refers to comparison of two equal numbers. Programmers

often employ this method to disable some program branches. Here is an example:

#if defined(_OPENMP)

#include <omp.h>

#else

#define omp_get_thread_num() 0

...

#endif

...

if (0 == omp_get_thread_num()) {

The last exception refers to comparison that uses macros:

#define _WINVER_NT4_ 0x0004

#define _WINVER_95_ 0x0004

...

UINT winver = g_App.m_pPrefs->GetWindowsVersion();

if(winver == _WINVER_95_ || winver == _WINVER_NT4_)

You should keep in mind that the analyzer might generate a warning on a correct

construct in some cases. For instance, the analyzer does not consider side effects

when calling functions:

if (wr.takeChar() == '\0' && wr.takeChar() == '\0')

Another example of a false alarm was noticed during unit-tests of some project - in

the part of it where the correctness of the overloaded operator '==' was checked:

CHECK(VDStringA() == VDStringA(), true);

CHECK(VDStringA("abc") == VDStringA("abc"), true);

The diagnostic message isn't generated if two identical expressions of 'float' or

'double' types are being compared. Such a comparison allows to identify the value

as NaN. The example of code implementing the verification of this kind:

bool isnan(double X) { return X != X; }

Page 336: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V502. Perhaps the '?:' operator works in a different way than it was expected. The '?:' operator has a lower priority than the 'foo' operator.The analyzer found a code fragment that most probably has a logic error. The

program text has an expression that contains the ternary operator '?:' and might be

calculated in a different way than the programmer expects.

The '?:' operator has a lower priority than operators ||, &&, |, ^, &, !=, ==, >=, <=, >,

<, >>, <<, -, +, %, /, *. One might forget about it and write an incorrect code like

the following one:

bool bAdd = ...;

size_t rightLen = ...;

size_t newTypeLen = rightLen + bAdd ? 1 : 0;

Having forgotten that the '+' operator has a higher priority than the '?:' operator, the

programmer expects that the code is equivalent to "rightLen + (bAdd ? 1 : 0)". But

actually the code is equivalent to the expression "(rightLen + bAdd) ? 1 : 0".

The analyzer diagnoses the probable error by checking:

1) If there is a variable or subexpression of the bool type to the left of the '?:'

operator.

2) If this subexpression is compared to / added to / multiplied by... the variable

whose type is other than bool.

If these conditions hold, it is highly probable that there is an error in this code and

the analyzer will generate the warning message we are discussing.

Here are some other examples of incorrect code:

Page 337: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

bool b;

int x, y, z, h;

...

x = y < b ? z : h;

x = y + (z != h) ? 1 : 2;

The programmer most likely wanted to have the following correct code:

bool b;

int x, y, z, h;

...

x = y < (b ? z : h);

x = y + ((z != h) ? 1 : 2);

If there is a type other than bool to the left of the '?:' operator, the analyzer thinks

that the code is written in the C style (where there is no bool) or that it is written

using class objects and therefore the analyzer cannot find out if this code is

dangerous or not.

Here is an example of correct code in the C style that the analyzer considers correct

too:

int conditions1;

int conditions2;

int conditions3;

...

char x = conditions1 + conditions2 + conditions3 ? 'a' : 'b';

V503. This is a nonsensical comparison: pointer < 0.The analyzer found a code fragment that has a nonsensical comparison. It is most

probable that this code has a logic error. Here is an example:

class MyClass {

public:

Page 338: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

CObj *Find(const char *name);

...

} Storage;

if (Storage.Find("foo") < 0)

ObjectNotFound();

It seems almost incredible that such a code can exist in a program. However, the

reason for its appearance might be quite simple. Suppose we have the following

code in our program:

class MyClass {

public:

// If the object is not found, the function

// Find() returns -1.

ptrdiff_t Find(const char *name);

CObj *Get(ptrdiff_t index);

...

} Storage;

...

ptrdiff_t index = Storage.Find("ZZ");

if (index >= 0)

Foo(Storage.Get(index));

...

if (Storage.Find("foo") < 0)

ObjectNotFound();

This is correct yet not very smart code. During the refactoring process, the MyClass

class may be rewritten in the following way:

class MyClass {

public:

CObj *Find(const char *name);

...

} Storage;

Page 339: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

After this modernization of the class, you should fix all the places in the program

which use the Find() function. You cannot miss the first code fragment since it will

not be compiled, so it will be certainly fixed:

CObj *obj = Storage.Find("ZZ");

if (obj != nullptr)

Foo(obj);

The second code fragment is compiled well and you might miss it easily and

therefore make the error we are discussing:

if (Storage.Find("foo") < 0)

ObjectNotFound();

V504. It is highly probable that the semicolon ';' is missing after 'return' keyword.The analyzer found a code fragment where the semicolon ';' is probably missing.

Here is an example of code that causes generating the V504 diagnostic message:

void Foo();

void Foo2(int *ptr)

{

if (ptr == NULL)

return

Foo();

...

}

The programmer intended to terminate the function's operation if the pointer ptr ==

NULL. But the programmer forgot to write the semicolon ';' after the return operator

which causes the call of the Foo() function. The functions Foo() and Foo2() do not

return anything and therefore the code is compiled without errors and warnings.

Page 340: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Most probably, the programmer intended to write:

void Foo();

void Foo2(int *ptr)

{

if (ptr == NULL)

return;

Foo();

...

}

But if the initial code is still correct, it is better to rewrite it in the following way:

void Foo2(int *ptr)

{

if (ptr == NULL)

{

Foo();

return;

}

...

}

The analyzer considers the code safe if the "if" operator is absent or the function

call is located in the same line with the "return" operator. You might quite often see

such code in programs. Here are examples of safe code:

void CPagerCtrl::RecalcSize()

{

return

(void)::SendMessageW((m_hWnd), (0x1400 + 2), 0, 0);

}

void Trace(unsigned int n, std::string const &s)

{ if (n) return TraceImpl(n, s); Trace0(s); }

Page 341: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V505. The 'alloca' function is used inside the loop. This can quickly overflow stack.The analyzer detected a use of the alloca function inside a loop. Since the alloca

function uses stack memory, its repeated call in the loop body might unexpectedly

cause a stack overflow.

Here is an example of dangerous code:

for (size_t i = 0; i < n; ++i)

if (wcscmp(strings[i], A2W(pszSrc[i])) == 0)

{

...

}

The _alloca function is used inside the A2W macro. Whether this code will cause an

error or not depends upon the length of the processed strings, their number and size

of the available stack.

V506. Pointer to local variable 'X' is stored outside the scope of this variable. Such a pointer will become invalid.The analyzer found a potential error related to storing a pointer of a local variable.

The warning is generated if the lifetime of an object is less than that of the pointer

referring to it.

The first example:

class MyClass

Page 342: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

{

size_t *m_p;

void Foo() {

size_t localVar;

...

m_p = &localVar;

}

};

In this case, the address of the local variable is saved inside the class into the m_p

variable and can be then used by mistake in a different function when the localVar

variable is destructed.

The second example:

void Get(float **x)

{

float f;

...

*x = &f;

}

The Get() function will return the pointer to the local variable that will not exist by

the moment.

This message is similar to V507 message.

V507. Pointer to local array 'X' is stored outside the scope of this array. Such a pointer will become invalid.The analyzer found a potential error related to storing a pointer of a local array. The

warning is generated if the lifetime of an array is less than that of the pointer

referring to it.

Page 343: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The first example:

class MyClass1

{

int *m_p;

void Foo()

{

int localArray[33];

...

m_p = localArray;

}

};

The localArray array is created in the stack and the localArray array will no longer

exist after the Foo() function terminates. However, the pointer to this array will be

saved in the m_p variable and can be used by mistake, which will cause an error.

The second example:

struct CVariable {

...

char name[64];

};

void CRendererContext::RiGeometryV(int n, char *tokens[])

{

for (i=0;i<n;i++)

{

CVariable var;

if (parseVariable(&var, NULL, tokens[i])) {

tokens[i] = var.name;

}

}

In this example, the pointer to the array situated in a variable of the CVariable type

is saved in an external array. As a result, the "tokens" array will contain pointers to

non-existing objects after the function RiGeometryV terminates.

Page 344: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The V507 warning does not always indicate an error. Below is an abridged code

fragment that the analyzer considers dangerous although this code is correct:

png_infop info_ptr = png_create_info_struct(png_ptr);

...

BYTE trans[256];

info_ptr->trans = trans;

...

png_destroy_write_struct(&png_ptr, &info_ptr);

In this code, the lifetime of the info_ptr object coincides with the lifetime of trans.

The object is created inside png_create_info_struct () and destroyed inside

png_destroy_write_struct(). The analyzer cannot make out this case and supposes

that the png_ptr object comes from outside. Here is an example where the analyzer

could be right:

void Foo()

{

png_infop info_ptr;

info_ptr = GetExternInfoPng();

BYTE trans[256];

info_ptr->trans = trans;

}

This message is similar to V506 message.

V508. The use of 'new type(n)' pattern was detected. Probably meant: 'new type[n]'.The analyzer found code that might contain a misprint and therefore lead to an

error. There is only one object of integer type that is dynamically created and

initialized. It is highly probable that round brackets are used instead of square

brackets by misprint. Here is an example:

Page 345: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

int n;

...

int *P1 = new int(n);

Memory is allocated for one object of the int type. It is rather strange. Perhaps the

correct code should look like this:

int n;

...

int *P1 = new int[n];

The analyzer generates the warning only if memory is allocated for simple types.

The argument in the brackets must be of integer type in this case. As a result, the

analyzer will not generate the warning on the following correct code:

float f = 1.0f;

float *f2 = new float(f);

MyClass *p = new MyClass(33);

V509. The 'throw' operator inside the destructor should be placed within the try..catch block. Raising exception inside the destructor is illegal.In case an exception is thrown in a C++ program stack unwinding begins which

causes objects to be destroyed by calling their destructors. If a destructor invoked

during stack unwinding throws another exception and that exception propagates

outside the destructor the C++ runtime immediately terminates the program by

calling terminate() function. Therefore destructors should never let exceptions

propagate - each exception thrown within a destructor should be handled in that

destructor.

Page 346: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer found a destructor containing the throw operator outside the try..catch

block. Here is an example:

LocalStorage::~LocalStorage()

{

...

if (!FooFree(m_index))

throw Err("FooFree", GetLastError());

...

}

This code must be rewritten so that the programmer is informed about the error that

has occurred in the destructor without using the exception mechanism. If the error is

not crucial, it can be ignored:

LocalStorage::~LocalStorage()

{

try {

...

if (!FooFree(m_index))

throw Err("FooFree", GetLastError());

...

}

catch (...)

{

assert(false);

}

}

Exceptions may be thrown when calling the 'new' operator as well. If memory

cannot be allocated, the 'bad_alloc' exception will be thrown. For example:

A::~A()

{

...

int *localPointer = new int[MAX_SIZE];

...

Page 347: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

}

An exception may be thrown when using dynamic_cast<Type> while handling

references. If types cannot be cast, the 'bad_cast' exception will be thrown. For

example:

B::~B()

{

...

UserType &type = dynamic_cast<UserType&>(baseType);

...

}

To fix these errors you should rewrite the code so that 'new' or 'dynamic_cast' are

put into the 'try{...}' block.

Additional materials on this topic:

1. Bjarne Stroustrup's C++ Style and Technique FAQ. Can I throw an exception from a constructor? From a destructor? http://www.stroustrup.com/bs_faq2.html

2. Throwing destructors. http://www.kolpackov.net/projects/c++/eh/dtor-1.xhtml

V510. The 'Foo' function is not expected to receive class-type variable as 'N' actual argument.

Note about C++11

Note one specific thing about using the CString class from the MFC library

Related materials

There are functions in whose description it is impossible to specify the number and

types of all the acceptable parameters. In this case, the list of formal arguments ends

with the ellipsis (...) that means: "and perhaps some more arguments". Here is an

Page 348: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

example of an ellipsis function: "int printf(const char* ...);". Only POD-types can

serve as actual arguments for ellipsis.

POD is an abbreviation for "Plain Old Data", i.e. "Plain data in C style". The

following types and structures refer to POD-types:

1. all the built-in arithmetic types (including wchar_t and bool);

2. types defined with the enum key word;

3. pointers;

4. POD-structures (struct or class) and POD-unions that meet the following requirements:

1. do not contain user constructors, destructor or copying assignment operator;

2. do not have base classes;

3. do not contain virtual functions;

4. do not contain protected or private non-static data members;

5. do not contain non-static data members of non-POD-types (or arrays of such types) and references.

If a class object is passed to an ellipsis function, this almost always indicates an

error in program. The V510 rule helps detect incorrect code of the following kind:

wchar_t buf[100];

std::wstring ws(L"12345");

swprintf(buf, L"%s", ws);

The object's contents are saved into the stack instead of the pointer to the string.

This code will cause generating "abracadabra" in the buffer or a program crash.

The correct version of the code must look this way:

wchar_t buf[100];

std::wstring ws(L"12345");

swprintf(buf, L"%s", ws.c_str());

Page 349: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Since you might pass anything you like into functions with a variable number of

arguments, almost all the books on C++ programming do not recommend using

them. They suggest employing safe mechanisms instead, for instance, boost::format.

Note about C++11In new standard, it is said that:

C++11 5.2.2/7: Passing a potentially-evaluated argument of class type having a

non-trivial copy constructor, a non-trivial move constructor, or a non-trivial

destructor, with no corresponding parameter, is conditionally-supported with

implementation-defined semantics.

Thus, it is possible to pass into function' ellipsis "more various kinds" of objects.

However, we decided not to change anything in this rule. In 99% of cases

transferring a complex class as an argument is a misprint or another kind of error.

This code should be reviewed. In case of inconvenience caused by large amount of

false alarms related to this warning, it is possible to mark these functions to

suppress it massively. An example:

//-V:MySuperPrint:510

It is possible to read about multiple warning suppression in details in section

"Suppression of false alarms".

Note one specific thing about using the CString class from the MFC libraryWe must see an error similar to the one mentioned above in the following code:

CString s;

CString arg(L"OK");

s.Format(L"Test CString: %s\n", arg);

The correct version of the code must look in the following way:

s.Format(L"Test CString: %s\n", arg.GetString());

Page 350: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Or, as MSDN suggests [1], you may use the explicit cast operator LPCTSTR

implemented in the CString class to get a pointer to the string. Here is a sample of

correct code from MSDN:

CString kindOfFruit = "bananas";

int howmany = 25;

printf("You have %d %s\n", howmany, (LPCTSTR)kindOfFruit);

However, the first version "s.Format(L"Test CString: %s\n", arg);" is actually

correct as well like the others. This topic is discussed in detail in the article "Big

Brother helps you" [2].

The MFC developers implemented the CString class in a special way so that you

could pass it into functions of the printf and Format types. It is done rather

intricately and if you want to make it out, study implementation of the CStringT

class in the source codes.

So, the analyzer makes an exception for the CStringT type and considers the

following code correct:

CString s;

CString arg(L"OK");

s.Format(L"Test CString: %s\n", arg);

Related materials1. MSDN. CString Operations Relating to C-Style

Strings. https://msdn.microsoft.com/en-us/library/awkwbzyc(VS.71).aspx

2. OOO "Program Verification Systems" blog. Big Brother helps you. http://www.viva64.com/en/b/0073/

V511. The sizeof() operator returns size of the pointer, and not of the array, in given expression.

Page 351: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

There is one specific feature of the language you might easily forget about and

make a mistake. Look at the following code fragment:

char A[100];

void Foo(char B[100])

{

}

In this code, the A object is an array and the sizeof(A) expression will return value

100.

The B object is simply a pointer. Value 100 in the square brackets indicates to the

programmer that he is working with an array of 100 items. But it is not an array of a

hundred items which is passed into the function - it is only the pointer. So, the

sizeof(B) expression will return value 4 or 8 (the size of the pointer in a 32-bit/64-

bit system).

The V511 warning is generated when the size of a pointer is calculated which is

passed as an argument in the format "TypeName ArrayName[N]". Such code is

most likely to have an error. Look at the sample:

void Foo(float array[3])

{

size_t n = sizeof(array) / sizeof(array[0]);

for (size_t i = 0; i != n; i++)

array[i] = 1.0f;

}

The function will not fill the whole array with value 1.0f but only 1 or 2 items

depending on the system's capacity.

Win32: sizeof(array) / sizeof(array[0]) = 4/4 = 1.

Win64: sizeof(array) / sizeof(array[0]) = 8/4 = 2.

To avoid such errors, we must explicitly pass the array's size. Here is correct code:

void Foo(float *array, size_t arraySize)

Page 352: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

{

for (size_t i = 0; i != arraySize; i++)

array[i] = 1.0f;

}

Another way is to use a reference to the array:

void Foo(float (&array)[3])

{

size_t n = sizeof(array) / sizeof(array[0]);

for (size_t i = 0; i != n; i++)

array[i] = 1.0f;

}

V512. A call of the 'Foo' function will lead to a buffer overflow or underflow.The analyzer found a potential error related to memory buffer filling, copying or

comparison. The error might cause a buffer overflow or, vice versa, buffer

underflow.

This is a rather common kind of errors that occurs due to misprints or inattention.

What is unpleasant about such errors is that a program might work well for a long

time. Due to sheer luck, acceptable values might be found in uninitialized memory.

The area of writable memory might not be used.

Let's study two samples taken from real applications.

Sample N1.

MD5Context *ctx;

...

memset(ctx, 0, sizeof(ctx));

Page 353: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Here the misprint causes release of only a part of the structure and not the whole

structure. The error is in calculation of the pointer's size and not the whole structure

MD5Context. Here is the correct version of the code:

MD5Context *ctx;

...

memset(ctx, 0, sizeof(*ctx));

Sample N2.

#define CONT_MAP_MAX 50

int _iContMap[CONT_MAP_MAX];

memset(_iContMap, -1, CONT_MAP_MAX);

In this sample, the size of the buffer to be filled is also defined incorrectly. This is

the correct version:

#define CONT_MAP_MAX 50

int _iContMap[CONT_MAP_MAX];

memset(_iContMap, -1, CONT_MAP_MAX * sizeof(int));

Sample N3.

struct MyTime

{

....

int time;

};

MyTime s;

time((time_t*)&s.time);

In this sample the type 's.time' is also specified incorrectly. In case there is a 64-bit

time_t, we'll get an overflow. This is the correct version:

struct MyTime

{

....

time_t time;

Page 354: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

};

MyTime s;

time(&s.time);

Note on the strncpy function.

Some programmers are surprised that the analyzer generates the V512 warning on

the following code:

char buf[5];

strncpy(buf, "X", 100);

It may seem at first sight that the function is to copy only 2 bytes (the 'X' character

and the terminal null). But an array overrun will really occur here. The author of

this code has forgotten one thing about the 'strncpy' function. Here is a quotation

from the description of this function on the MSDN website: If count is greater than

the length of strSource, the destination string is padded with null characters up to

length count.

Note about false positives warning.

It turns out for some reason that for some projects the analyzer generates a lot of

false positives warning about buffer underflows. Sometimes, on the contrary, all the

warnings about buffer overflows appear to be false positives. In this case you may

use the fine setting of the diagnostic rule.

It can be done by adding the following comments into the code text where you

need:

//-V512_UNDERFLOW_OFF

//-V512_OVERFLOW_OFF

The first comment disables warnings about underflows, while the second disables

warnings about overflows. If you add both, it will be identical to completely

disabling the V512 diagnostic rule.

Page 355: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

These comments should be added into the header file included into all the other

files. For instance, such is the "stdafx.h" file. If you add the comments into the

"*.cpp" file, they will affect only this particular file.

V513. Use _beginthreadex/_endthreadex functions instead of CreateThread/ExitThread functions.A use of the CreateThread function or ExitThread function is detected in a program.

If CRT (C run-time library) functions are used in concurrent threads, you should

call the functions _beginthreadex/_endthreadex instead of

CreateThread/ExitThread.

Below is an extract from the 6-th chapter of the book "Advanced Windows: creating

efficient Win32-applications considering the specifics of the 64-bit Windows" by

Jeffrey Richter / 4-th issue.

CreateThread is a Windows-function creating a thread. But never call it if you write

your code in C/C++. You should use the function _beginthreadex from the Visual

C++ library instead.

To make multi-threaded applications using C/C++ (CRT) library work correctly,

you should create a special data structure and link it to every thread from which the

library functions are called. Moreover, they must know that when you address them,

they must look through this data block in the master thread in order not to damage

data in some other thread.

So how does the system know that it must create this data block together with

creating a new thread? The answer is very simple - it doesn't know and never will

like to. Only you are fully responsible for it. If you use functions which are unsafe in

multi-threaded environment, you should create threads with the library function

_beginthreadex and not Windows-function CreateThread.

Page 356: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Note that the _beginthreadex function exists only in multi-threaded versions of the

C/C++ library. When linking a project to a single-threaded library, the linker will

generate an error message "unresolved external symbol". Of course, it is done

intentionally since the single-threaded library cannot work correctly in a multi-

threaded application. Note also that Visual Studio chooses the single-threaded

library by default when creating a new project. This way is not the safest one, so

you should choose yourself one of the multi-threaded versions of the C/C++ library

for multi-threaded applications.

Correspondingly, you must use the function _endthreadex to destruct a thread

created with the function _beginthreadex.

Additional materials on this topic:

1. Discussion at StackOverflow. "Windows threading: _beginthread vs _beginthreadex vs CreateThread C++". http://stackoverflow.com/questions/331536/windows-threading-beginthread-vs-beginthreadex-vs-createthread-c

2. Discussion at CodeGuru Forum. "_beginthread vs CreateThread". http://forums.codeguru.com/showthread.php?371305.html

3. Discussion at MSDN forum. "CreateThread vs _beginthreadex". https://social.msdn.microsoft.com/Forums/vstudio/en-US/c727ae29-5a7a-42b6-ad0b-f6b21c1180b2/createthread-vs-beginthreadex?forum=vclanguage

V514. Dividing sizeof a pointer by another value. There is a probability of logical error presence.The analyzer found a potential error related to division of the pointer's size by some

value. Division of the pointer's size is rather a strange operation since it has no

practical sense and most likely indicates an error or misprint in code.

Consider an example:

Page 357: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

const size_t StrLen = 16;

LPTSTR dest = new TCHAR[StrLen];

TCHAR src[StrLen] = _T("string for V514");

_tcsncpy(dest, src, sizeof(dest)/sizeof(dest[0]));

In the "sizeof(dest)/sizeof(dest[0])" expression, the pointer's size is divided by the

size of the element the pointer refers to. As a result, we might get different numbers

of copied bytes depending on sizes of the pointer and TCHAR type - but never the

number the programmer expected.

Taking into account that the _tcsncpy function is unsafe in itself, correct and safer

code may look in the following way:

const size_t StrLen = 16;

LPTSTR dest = new TCHAR[StrLen];

TCHAR src[StrLen] = _T("string for V514");

_tcsncpy_s(dest, StrLen, src, StrLen);

V515. The 'delete' operator is applied to non-pointer.In code, the delete operator is applied to a class object instead of the pointer. It is

most likely to be an error.

Consider a code sample:

CString str;

...

delete str;

The 'delete' operator can be applied to an object of the CString type since the

CString class can be automatically cast to the pointer. Such code might cause an

exception or unexpected program behavior.

Correct code might look so:

CString *pstr = new CString;

Page 358: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

...

delete pstr;

In some cases, applying the 'delete' operator to class objects is not an error. You

may encounter such code, for instance, when working with the

QT::QbasicAtomicPointer class. The analyzer ignores calls of the 'delete' operator

to objects of this type. If you know other similar classes it is a normal practice to

apply the 'delete' operator to, please tell us about them. We will add them into

exceptions.

V516. Consider inspecting an odd expression. Non-null function pointer is compared to null.Code contains a construct comparing a non-null pointer to a function with null. It is

most probably that there is a misprint in code – parentheses are missing.

Consider this example:

int Foo();

void Use()

{

if (Foo == 0)

{

//...

}

}

The condition "Foo == 0" is meaningless. The address of the 'Foo' function never

equals zero, so the comparison result will always be 'false'. In the code we consider,

the programmer missed parentheses by accident. This is the correct version of the

code:

if (Foo() == 0)

{

Page 359: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

//...

}

If there is an explicit taking of address, the code is considered correct. For example:

int Foo();

void Use()

{

if (&Foo != NULL)

//...

}

V517. The use of 'if (A) {...} else if (A) {...}' pattern was detected. There is a probability of logical error presence.The analyzer detected a possible error in a construct consisting of conditional

statements. Consider the sample:

if (a == 1)

Foo1();

else if (a == 2)

Foo2();

else if (a == 1)

Foo3();

In this sample, the 'Foo3()' function will never get control. Most likely, we deal with

a logical error and the correct code should look as follows:

if (a == 1)

Foo1();

else if (a == 2)

Foo2();

else if (a == 3)

Page 360: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Foo3()

In practice, such an error might look in the following way:

if (radius < THRESH * 5)

*yOut = THRESH * 10 / radius;

else if (radius < THRESH * 5)

*yOut = -3.0f / (THRESH * 5.0f) * (radius - THRESH * 5.0f) + 3.0f;

else

*yOut = 0.0f;

It is difficult to say how a correct comparison condition must look, but the error in

this code is evident.

V518. The 'malloc' function allocates strange amount of memory calculated by 'strlen(expr)'. Perhaps the correct variant is strlen(expr) + 1.The analyzer found a potential error related to allocating insufficient amount of

memory. The string's length is calculated in code and the memory buffer of a

corresponding size is allocated but the terminal '\0' is not allowed for.

Consider this example:

char *p = (char *)malloc(strlen(src));

strcpy(p, src);

In this case, it is just +1 which is missing. The correct version is:

char *p = (char *)malloc(strlen(src) + 1);

strcpy(p, src);

Page 361: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Here is another example of incorrect code detected by the analyzer in one

application:

if((t=(char *)realloc(next->name, strlen(name+1))))

{

next->name=t;

strcpy(next->name,name);

}

The programmer was inattentive and made a mistake when writing the right bracket

')'. As a result, we will allocate 2 bytes less memory than necessary. This is the

correct code:

if((t=(char *)realloc(next->name, strlen(name)+1)))

V519. The 'x' variable is assigned values twice successively. Perhaps this is a mistake.The analyzer detected a potential error related to assignment of a value two times

successively to the same variable while the variable itself is not used between these

assignments.

Consider this sample:

A = GetA();

A = GetB();

The fact that the 'A' variable is assigned values twice might signal an error. Most

probably, the code should look this way:

A = GetA();

B = GetB();

If the variable is used between assignments, the analyzer considers this code

correct:

Page 362: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

A = 1;

A = A + 1;

A = Foo(A);

Let's see how such an error may look in practice. The following sample is taken

from a real application where a user class CSize is implemented:

class CSize : public SIZE

{

...

CSize(POINT pt) { cx = pt.x; cx = pt.y; }

The correct version is the following:

CSize(POINT pt) { cx = pt.x; cy = pt.y; }

Let's study one more example. The second line was written for the purpose of

debugging or checking how text of a different color would look. And it seems that

the programmer forgot to remove the second line then:

m_clrSample = GetSysColor(COLOR_WINDOWTEXT);

m_clrSample = RGB(60,0,0);

Sometimes the analyzer generates false alarms when writing into variables is used

for the purpose of debugging. Here is an example of such code:

status = Foo1();

status = Foo2();

In this case, we may suppress false alarms using the "//-V519" comment. We may

also remove meaningless assignments from the code. And the last thing. Perhaps

this code is still incorrect, so we have to check the value of the 'status' variable.

V520. The comma operator ',' in array index expression.

Page 363: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer found a potential error that may be caused by a misprint. An

expression containing the ',' operator is used as an index for an array.

Here is a sample of suspicious code:

float **array_2D;

array_2D[getx() , gety()] = 0;

Most probably, it was meant to be:

array_2D[ getx() ][ gety() ] = 0;

Such errors might appear if the programmer worked earlier with a programming

language where array indexes are separated by commas.

Let's look at a sample of an error found by the analyzer in one project:

float **m;

TextOutput &t = ...

...

t.printf("%10.5f, %10.5f, %10.5f,\n%10.5f, %10.5f, %10.5f,\n%10.5f,

%10.5f, %10.5f)",

m[0, 0], m[0, 1], m[0, 2],

m[1, 0], m[1, 1], m[1, 2],

m[2, 0], m[2, 1], m[2, 2]);

Since the printf function of the TextOutput class works with a variable number of

arguments, it cannot check whether pointers will be passed to it instead of values of

the float type. As a result, we will get rubbish displayed instead of matrix items'

values. This is the correct code:

t.printf("%10.5f, %10.5f, %10.5f,\n%10.5f, %10.5f, %10.5f,\n%10.5f,

%10.5f, %10.5f)",

m[0][0], m[0][1], m[0][2],

m[1][0], m[1][1], m[1][2],

m[2][0], m[2][1], m[2][2]);

Page 364: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V521. Such expressions using the ',' operator are dangerous. Make sure the expression is correct.The comma operator ',' is used to execute expressions to the both sides of it in the

left-to-right order and get the value of the right expression.

The analyzer found an expression in code that uses the ',' operator in a suspicious

way. It is highly probable that the program text contains a misprint.

Consider the following sample:

float Foo()

{

double A;

A = 1,23;

float f = 10.0f;

return 3,f;

}

In this code, the A variable will be assigned value 1 instead of 1.23. According to

C/C++ rules, the "A = 1,23" expression equals "(A = 1),23". Also, the Foo()

function will return value 10.0f instead of 3.0f. In the both cases, the error is related

to using the ',' character instead of the '.' character.

This is the corrected version:

float Foo()

{

double A;

A = 1.23;

float f = 10.0f;

return 3.f;

}

Page 365: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Note. There were cases when the analyzer could not make out the code and

generated V521 warnings for absolutely safe constructs. It is usually related to

usage of template classes or complex macros. If you noted such a false alarm when

working with the analyzer, please tell the developers about it. To suppress false

alarms, you may use the comment of the "//-V521" type.

V522. Dereferencing of the null pointer might take place.The analyzer detected a fragment of code that might cause using a null pointer.

Let's study several examples the analyzer generates the V522 diagnostic message

for:

if (pointer != 0 || pointer->m_a) { ... }

if (pointer == 0 && pointer->x()) { ... }

if (array == 0 && array[3]) { ... }

if (!pointer && pointer->x()) { ... }

In all the conditions, there is a logical error that leads to dereferencing of the null

pointer. The error may be introduced into the code during code refactoring or

through a misprint.

Correct versions:

if (pointer != 0 && pointer->m_a) { ... }

if (pointer != 0 && pointer->x()) { ... }

if (array != 0 && array[3]) { ... }

if (pointer && pointer->x()) { ... }

These are simple cases, of course. In practice, operations of pointer check and

pointer use may be located in different places. If the analyzer generates the V522

warning, study the code above and try to understand why the pointer might be a null

pointer.

Here is a code sample where pointer check and pointer use are in different strings

Page 366: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

if (ptag == NULL) {

SysPrintf("SPR1 Tag BUSERR\n");

psHu32(DMAC_STAT)|= 1<<15;

spr1->chcr = ( spr1->chcr & 0xFFFF ) |

( (*ptag) & 0xFFFF0000 );

return;

}

The analyzer will warn you about the danger in the "( (*ptag) & 0xFFFF0000 )"

string. It's either an incorrectly written condition here or there should be a different

variable instead of 'ptag'.

Sometimes programmers deliberately use null pointer dereferencing for the testing

purpose. For example, analyzer will produce the warning for those places that

contain this macro:

/// This generate a coredump when we need a

/// method to be compiled but not usabled.

#define elxFIXME { char * p=0; *p=0; }

Extraneous warnings can be turned off by using the "//-V522" comment in those

strings that contain the 'elxFIXME' macro. Or, as an alternative, you can write a

comment of a special kind beside the macro:

//-V:elxFIXME:522

The comment can be written both before and after the macro - it doesn't matter. To

learn more about methods of suppressing false positives, follow here.

V523. The 'then' statement is equivalent to the 'else' statement.The analyzer found a case when the true and false statements of the 'if' operator

coincide completely. This often signals a logical error.

Here is an example:

Page 367: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

if (X)

Foo_A();

else

Foo_A();

Whether the X condition is false or true, the Foo_A() function will be called

anyway.

This is the correct version of the code:

if (X)

Foo_A();

else

Foo_B();

Here is an example of such an error taken from a real application:

if (!_isVertical)

Flags |= DT_BOTTOM;

else

Flags |= DT_BOTTOM;

Presence of two empty statements is considered correct and safe. You might often

see such constructs when using macros. This is a sample of safe code:

if (exp) {

} else {

}

Also the analyzer thinks that it is suspicious, if the 'if' statement does not contain the

'else' block, and the code written next is identical to the conditional statement block.

At the same time, this code block ends with a return, break, etc.

Suspicious code snippet:

if (X)

{

doSomething();

Page 368: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Foo_A();

return;

}

doSomething();

Foo_A();

return;

Perhaps the programmer forgot to edit the copied code fragment or wrote excessive

code.

V524. It is odd that the body of 'Foo_1' function is fully equivalent to the body of 'Foo_2' function.This warning is generated when the analyzer detects two functions implemented in

the same way. Presence of two identical functions is not an error in itself but you

should study them.

The sense of such diagnosis is detecting the following type of errors:

class Point

{

...

float GetX() { return m_x; }

float GetY() { return m_x; }

};

The misprint in the code causes the two functions different in sense to perform the

same actions. This is the correct version:

float GetX() { return m_x; }

float GetY() { return m_y; }

Identity of the bodies of functions GetX() and GetY() in this sample obviously

signals an error. However, the percentage of false alarms will be too great if the

analyzer generates warnings for all identical functions, so it is guided by a range of

Page 369: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

exceptions when it must not warn the programmer about identical function bodies.

Here are some of them:

The analyzer does not report about identity of functions' bodies if they do not use variables except for arguments. For example: "bool IsXYZ() { return true; }".

Functions use static objects and therefore have different inner states. For example: "int Get() { static int x = 1; return x++; }"

Functions are type cast operators.

Functions with identical bodies are repeated more than twice.

And so on.

However, in some cases the analyzer cannot understand that identical function

bodies are not an error. This is code which is diagnosed as dangerous but really it is

not:

PolynomialMod2 Plus(const PolynomialMod2 &b) const

{return Xor(b);}

PolynomialMod2 Minus(const PolynomialMod2 &b) const

{return Xor(b);}

You can suppress false alarms using several methods. If false alarms refer to files of

external libraries, you may add this library (i.e. its path) to exceptions. If false

alarms refer to your own code, you may use the comment of the "//-V524" type to

suppress false warnings. If there are too many false alarms, you may completely

disable this diagnosis in the analyzer's settings. You may also modify the code so

that one function calls another with the same code.

The last method is often the best since it, first, reduces the amount of code and,

second, makes it easier to support. You need to edit only one function instead of the

both functions. This is a sample of real code where the programmer could benefit

from calling one function from another:

static void PreSave(void) {

int x;

for(x=0;x<TotalSides;x++) {

int b;

Page 370: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

for(b=0; b<65500; b++)

diskdata[x][b] ^= diskdatao[x][b];

}

}

static void PostSave (void) {

int x;

for(x=0;x<TotalSides;x++) {

int b;

for(b=0; b<65500; b++)

diskdata[x][b] ^= diskdatao[x][b];

}

}

This code should be replaced with the following:

static void PreSave(void) {

int x;

for(x=0;x<TotalSides;x++) {

int b;

for(b=0; b<65500; b++)

diskdata[x][b] ^= diskdatao[x][b];

}

}

static void PostSave (void) {

PreSave();

}

We did not fix the error in this sample, but the V524 warning disappeared after

refactoring and the code got simpler.

V525. The code containing the collection of similar blocks. Check

Page 371: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

items X, Y, Z, ... in lines N1, N2, N3, ...The analyzer detected code that might contain a misprint. This code can be split into

smaller similar fragments. Although they look similar, they differ in some way. It is

highly probable that this code was created with the Copy-Paste method. The V525

message is generated if the analyzer suspects that some element was not fixed in the

copied text. The error might be located in one of the lines whose numbers are listed

in the V525 message.

Disadvantages of the V525 message:

1) This diagnostic rule is based on heuristic methods and often produces false

alarms.

2) Implementation of the rule's heuristic algorithm is complicated and occupies

more than 1000 lines of C++ code. That is why it is difficult to describe in

documentation. So it may be hard for the user to understand why the V525 message

was generated.

3) The diagnostic message refers not to one line but several lines. The analyzer

cannot point out only one line since the error may be in any of them.

Advantages of the V525 message:

1) It can detect errors which are too hard to notice during code review.

Let's study an artificial sample at first:

...

float rgba[4];

rgba[0] = object.GetR();

rgba[1] = object.GetG();

rgba[2] = object.GetB();

rgba[3] = object.GetR();

Page 372: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The 'rgba' array presents color and transparency of some object. When writing the

code that fills the array, we wrote the line "rgba[0] = object.GetR();" at first. Then

we copied and changed this line several times. But in the last line, we missed some

changes, so it is the 'GetR()' function which is called instead of the 'GetA()'

function. The analyzer generates the following warning on this code:

V525: The code containing the collection of similar blocks. Check items 'GetR',

'GetG', 'GetB', 'GetR' in lines 12, 13, 14, 15.

If you review lines 12, 13, 14 and 15, you will find the error. This is the correct

code:

rgba[3] = object.GetA();

Now let's study several samples taken from real applications. The first sample:

tbb[0].iBitmap = 0;

tbb[0].idCommand = IDC_TB_EXIT;

tbb[0].fsState = TBSTATE_ENABLED;

tbb[0].fsStyle = BTNS_BUTTON;

tbb[0].dwData = 0;

tbb[0].iString = -1;

...

tbb[6].iBitmap = 6;

tbb[6].idCommand = IDC_TB_SETTINGS;

tbb[6].fsState = TBSTATE_ENABLED;

tbb[6].fsStyle = BTNS_BUTTON;

tbb[6].dwData = 0;

tbb[6].iString = -1;

tbb[7].iBitmap = 7;

tbb[7].idCommand = IDC_TB_CALC;

tbb[7].fsState = TBSTATE_ENABLED;

tbb[7].fsStyle = BTNS_BUTTON;

tbb[6].dwData = 0;

tbb[7].iString = -1;

Page 373: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The code fragment is far not complete. More than half of it was cut out. The

fragment was being written through copying and editing the code. No wonder that

an incorrect index was lost in such a large fragment. The analyzer generates the

following diagnostic message: "The code containing the collection of similar

blocks. Check items '0', '1', '2', '3', '4', '5', '6', '6' in lines 589, 596, 603, 610, 617,

624, 631, 638". If we review these lines, we will find and correct the index '6'

repeated twice. This is the correct code:

tbb[7].iBitmap = 7;

tbb[7].idCommand = IDC_TB_CALC;

tbb[7].fsState = TBSTATE_ENABLED;

tbb[7].fsStyle = BTNS_BUTTON;

tbb[7].dwData = 0;

tbb[7].iString = -1;

The second sample:

pPopup->EnableMenuItem(

ID_CONTEXT_EDITTEXT,MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);

pPopup->EnableMenuItem(

ID_CONTEXT_CLOSEALL, MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);

pPopup->EnableMenuItem(

ID_CONTEXT_CLOSE, MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);

pPopup->EnableMenuItem(

ID_CONTEXT_SAVELAYOUT, MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);

pPopup->EnableMenuItem(

ID_CONTEXT_RESIZE, MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);

pPopup->EnableMenuItem(

ID_CONTEXT_REFRESH, MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);

pPopup->EnableMenuItem(

ID_CONTEXT_EDITTEXT, MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);

pPopup->EnableMenuItem(

ID_CONTEXT_SAVE, MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);

pPopup->EnableMenuItem(

ID_CONTEXT_EDITIMAGE,MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);

pPopup->EnableMenuItem(

Page 374: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

ID_CONTEXT_CLONE,MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);

It is very difficult to find an error in this code while reviewing it. But there is an

error here: the state of the same menu item 'ID_CONTEXT_EDITTEXT' is

modified twice. Let's mark the two repeated lines:

------------------------------

pPopup->EnableMenuItem(

ID_CONTEXT_EDITTEXT,MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);

------------------------------

pPopup->EnableMenuItem(

ID_CONTEXT_CLOSEALL, MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);

pPopup->EnableMenuItem(

ID_CONTEXT_CLOSE, MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);

pPopup->EnableMenuItem(

ID_CONTEXT_SAVELAYOUT, MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);

pPopup->EnableMenuItem(

ID_CONTEXT_RESIZE, MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);

pPopup->EnableMenuItem(

ID_CONTEXT_REFRESH, MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);

------------------------------

pPopup->EnableMenuItem(

ID_CONTEXT_EDITTEXT, MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);

------------------------------

pPopup->EnableMenuItem(

ID_CONTEXT_SAVE, MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);

pPopup->EnableMenuItem(

ID_CONTEXT_EDITIMAGE,MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);

pPopup->EnableMenuItem(

ID_CONTEXT_CLONE,MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);

Maybe it is a small error and one of the lines is just unnecessary. Or maybe the

programmer forgot to change the state of some other menu item.

Unfortunately, the analyzer often makes a mistake while carrying out this diagnosis

and generates false alarms. This is an example of code causing a false alarm:

Page 375: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

switch (i) {

case 0: f1 = 2; f2 = 3; break;

case 1: f1 = 0; f2 = 3; break;

case 2: f1 = 1; f2 = 3; break;

case 3: f1 = 1; f2 = 2; break;

case 4: f1 = 2; f2 = 0; break;

case 5: f1 = 0; f2 = 1; break;

}

The analyzer does not like a correct column of numbers: 2, 0, 1, 1, 2, 0. In such

cases, you may enable the warning suppression mechanism by typing the

comment //-V525 in the end of the line:

switch (i) {

case 0: f1 = 2; f2 = 3; break; //-V525

case 1: f1 = 0; f2 = 3; break;

case 2: f1 = 1; f2 = 3; break;

case 3: f1 = 1; f2 = 2; break;

case 4: f1 = 2; f2 = 0; break;

case 5: f1 = 0; f2 = 1; break;

}

If there are too many false alarms, you may disable this diagnostic rule in the

analyzer's settings. We will also appreciate if you write to our support service about

cases when false alarms are generated and we will try to improve the diagnosis

algorithm. Please attach corresponding code fragments to your letters.

V526. The 'strcmp' function returns 0 if corresponding strings are equal. Consider examining the condition for mistakes.This message is a kind of recommendation. It rarely diagnoses a logical error but

helps make code more readable for young developers.

Page 376: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer detected a construct comparing two strings that can be written in a

clearer way. Such functions as strcmp, strncmp and wcsncmp return 0 if strings

identical. It may cause logical errors in program. Look at a code sample:

if (strcmp(s1, s2))

This condition will hold if the strings ARE NOT IDENTICAL. Perhaps you

remember well what strcmp() returns, but a person who rarely works with string

functions might think that the strcmp() function returns the value of type 'bool'.

Then he will read this code in this way: "the condition is true if the strings match".

You'd better not save on more characters in the program text and write the code this

way:

if (strcmp(s1, s2) != 0)

This text tells the programmer that the strcmp() function returns some numeric

value, not the bool type. This code ensures that the programmer will understand it

properly.

If you do not want to get this diagnostic message, you may disable it in the analyzer

settings.

V527. It is odd that the 'zero' value is assigned to pointer. Probably meant: *ptr = zero.This error occurs in two similar cases.

1) The analyzer found a potential error: a pointer to bool type is assigned false

value. It is highly probable that the pointer dereferencing operation is missing. For

example:

float Get(bool *retStatus)

{

Page 377: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

...

if (retStatus != nullptr)

retStatus = false;

...

}

The '*' operator is missing in this code. The operation of nulling the retStatus

pointer will be performed instead of status return. This is the correct code:

if (retStatus != nullptr)

*retStatus = false;

2) The analyzer found a potential error: a pointer referring to the char/wchar_t type

is assigned value '\0' or L'\0'. It is highly probable that the pointer dereferencing

operation is missing. For example:

char *cp;

...

cp = '\0';

This is the correct code:

char *cp;

...

*cp = '\0';

V528. It is odd that pointer is compared with the 'zero' value. Probably meant: *ptr != zero.This error occurs in two similar cases.

1) The analyzer found a potential error: a pointer to bool type is compared to false

value. It is highly probable that the pointer dereferencing operation is missing. For

example:

Page 378: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

bool *pState;

...

if (pState != false)

...

The '*' operator is missing in this code. As a result, we compare the pState pointer's

value to the null pointer. This is the correct code:

bool *pState;

...

if (*pState != false)

...

2) The analyzer found a potential error: a pointer to the char/wchar_t type is

compared to value '\0' or L'\0'. It is highly probable that the pointer dereferencing

operation is missing. For example:

char *cp;

...

if (cp != '\0')

This is the correct code:

char *cp;

...

if (*cp != '\0')

V529. Odd semicolon ';' after 'if/for/while' operator.The analyzer detected a potential error: a semicolon ';' stands after the 'if', 'for' or

'while' operator. For example:

for (i = 0; i < n; i++);

{

Foo(i);

}

Page 379: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

This is the correct code:

for (i = 0; i < n; i++)

{

Foo(i);

}

Using a semicolon ';' right after the for or while operator is not an error in itself and

you may see it quite often in code. So the analyzer eliminates many cases relying on

some additional factors. For instance, the following code sample is considered safe:

for (depth = 0, cur = parent; cur; depth++, cur = cur->parent)

;

V530. The return value of function 'Foo' is required to be utilized.Calls of some functions are senseless if their results are not used. Let's study the

first sample:

void VariantValue::Clear()

{

m_vtype = VT_NULL;

m_bvalue = false;

m_ivalue = 0;

m_fvalue = 0;

m_svalue.empty();

m_tvalue = 0;

}

This value emptying code is taken from a real application. The error here is the

following: by accident, the string::empty() function is called instead of the

string::clear() function and the line's content remains unchanged. The analyzer

diagnoses this error relying on knowledge that the result of the string::empty()

function must be used. For instance, it must be compared to something or written

into a variable.

Page 380: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

This is the correct code:

void VariantValue::Clear()

{

m_vtype = VT_NULL;

m_bvalue = false;

m_ivalue = 0;

m_fvalue = 0;

m_svalue.clear();

m_tvalue = 0;

}

The second sample:

void unregisterThread() {

Guard<TaskQueue> g(_taskQueue);

std::remove(_threads.begin(), _threads.end(),

ThreadImpl::current());

}

The std::remove function does not remove elements from the container. It only

shifts the elements and brings the iterator back to the beginning of the trash.

Suppose we have the vector<int> container that contains elements 1,2,3,1,2,3,1,2,3.

If we execute the code "remove( v.begin(), v.end(), 2 )", the container will contain

elements 1,3,1,3,?,?,?, where ? is some trash. The function will bring the iterator

back to the first senseless element, so if we want to remove these trash elements, we

must write the code this way: "v.erase(remove(v.begin(), v.end(), 2), v.end())".

As you may see from this explanation, the result std::remove must be used. This is

the correct code:

void unregisterThread() {

Guard<TaskQueue> g(_taskQueue);

auto trash = std::remove(_threads.begin(), _threads.end(),

ThreadImpl::current());

_threads.erase(trash, _threads.end());

}

Page 381: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

There are very many functions whose results must be used. Among them are the

following: malloc, realloc, fopen, isalpha, atof, strcmp and many, many others. An

unused result signals an error which is usually caused by a misprint. However, the

analyzer warns only about errors related to using the STL library. There are two

reasons for that:

1) It is much more difficult to make a mistake by not using the result of the fopen()

function than confuse std::clear() and std::empty().

2) This functionality duplicates the capabilities of Code Analysis for C/C++

included into some Visual Studio editions (see warning C6031). But these warnings

are not implemented in Visual Studio for STL functions.

If you want to propose extending the list of functions supported by analyzer, contact

our support service. We will appreciate if you give interesting samples and advice.

Additional features

You can specify the names of user functions for which it should be checked if their

return values are used.

To enable this option, you need to insert a special comment near the function

prototype (or in the common header file), for example:

//+V530, namespace:MyNamespace, class:MyClass, function:MyFunc

namespace MyNamespace {

class MyClass {

int MyFunc();

}

....

obj.MyFunc(); // warning V530

}

Format:

function parameter defines the function name.

class parameter defines the class name if the function is defined in a class.

Page 382: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

namespace parameter defines the namespace name if the function or class method are defined in a particular namespace.

V531. It is odd that a sizeof() operator is multiplied by sizeof().Code where a value returned by the sizeof() operator is multiplied by another

sizeof() operator most always signals an error. It is unreasonable to multiply the size

of one object by the size of another object. Such errors usually occur when working

with strings.

Let's study a real code sample:

TCHAR szTemp[256];

DWORD dwLen =

::LoadString(hInstDll, dwID, szTemp,

sizeof(szTemp) * sizeof(TCHAR));

The LoadString function takes the buffer's size in characters as the last argument. In

the Unicode version of the application, we will tell the function that the buffer's size

is larger than it is actually. This may cause a buffer overflow. Note that if we fix the

code in the following way, it will not become correct at all:

TCHAR szTemp[256];

DWORD dwLen =

::LoadString(hInstDll, dwID, szTemp, sizeof(szTemp));

Here is a quotation from MSDN on this topic:

Using this function incorrectly can compromise the security of your application.

Incorrect use includes specifying the wrong size in the nBufferMax parameter. For

example, if lpBuffer points to a buffer szBuffer which is declared as TCHAR

szBuffer[100], then sizeof(szBuffer) gives the size of the buffer in bytes, which could

lead to a buffer overflow for the Unicode version of the function. Buffer overflow

situations are the cause of many security problems in applications. In this case,

Page 383: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

using sizeof(szBuffer)/sizeof(TCHAR) or sizeof(szBuffer)/sizeof(szBuffer[0]) would

give the proper size of the buffer.

This is the correct code:

TCHAR szTemp[256];

DWORD dwLen =

::LoadString(hInstDll, dwID, szTemp,

sizeof(szTemp) / sizeof(TCHAR));

Here is another correct code:

const size_t BUF_LEN = 256;

TCHAR szTemp[BUF_LEN];

DWORD dwLen =

::LoadString(hInstDll, dwID, szTemp, BUF_LEN);

V532. Consider inspecting the statement of '*pointer++' pattern. Probably meant: '(*pointer)++'.The analyzer detected a potential error: a pointer dereferencing operation is present

in code but the value the pointer refers to is not used in any way.

Let's study this sample:

int *p;

...

*p++;

The "*p++" expression performs the following actions. The "p" pointer is

incremented by one, but before that a value of the "int" type is fetched from

memory. This value is not used in any way, which is strange. It looks as if the

dereferencing operation "*" is unnecessary. There are several ways of correcting the

code:

Page 384: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

1) We may remove the unnecessary dereferencing operation - the "*p++;"

expression is equal to "p++;":

int *p;

...

p++;

2) If the developer intended to increment the value instead of the pointer, we should

write it so:

int *p;

...

(*p)++;

If the "*p++" expression's result is used, the analyzer considers the code correct.

This is a sample of safe code:

while(*src)

*dest++ = *src++;

Let's study a sample taken from a real application:

STDMETHODIMP CCustomAutoComplete::Next(

ULONG celt, LPOLESTR *rgelt, ULONG *pceltFetched)

{

...

if (pceltFetched != NULL)

*pceltFetched++;

...

In this case, parentheses are missing. This is the correct code:

if (pceltFetched != NULL)

(*pceltFetched)++;

V533. It is likely that a wrong variable is being incremented inside

Page 385: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

the 'for' operator. Consider reviewing 'X'.The analyzer detected a potential error: a variable referring to an outer loop and

located inside the 'for' operator is incremented.

This is the simplest form of this error:

for (size_t i = 0; i != 5; i++)

for (size_t j = 0; j != 5; i++)

A[i][j] = 0;

It is the 'i' variable which is incremented instead of 'j' in the inner loop. Such an

error might be not so visible in a real application. This is the correct code:

for (size_t i = 0; i != 5; i++)

for (size_t j = 0; j != 5; j++)

A[i][j] = 0;

V533. It is likely that a wrong variable is being incremented inside the 'for' operator. Consider reviewing 'X'.The analyzer detected a potential error: a variable referring to an outer loop and

located inside the 'for' operator is incremented.

This is the simplest form of this error:

for (size_t i = 0; i != 5; i++)

for (size_t j = 0; j != 5; i++)

A[i][j] = 0;

Page 386: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

It is the 'i' variable which is incremented instead of 'j' in the inner loop. Such an

error might be not so visible in a real application. This is the correct code:

for (size_t i = 0; i != 5; i++)

for (size_t j = 0; j != 5; j++)

A[i][j] = 0;

V534. It is likely that a wrong variable is being compared inside the 'for' operator. Consider reviewing 'X'.The analyzer detected a potential error: a variable referring to an outer loop is used

in the condition of the 'for' operator.

This is the simplest form of this error:

for (size_t i = 0; i != 5; i++)

for (size_t j = 0; i != 5; j++)

A[i][j] = 0;

It is the comparison 'i != 5' that is performed instead of 'j != 5' in the inner loop.

Such an error might be not so visible in a real application. This is the correct code:

for (size_t i = 0; i != 5; i++)

for (size_t j = 0; j != 5; j++)

A[i][j] = 0;

V535. The variable 'X' is being used for this loop and for the outer loop.The analyzer detected a potential error: a nested loop is arranged by a variable

which is also used in an outer loop. In a schematic form, this error looks in the

following way:

Page 387: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

size_t i, j;

for (i = 0; i != 5; i++)

for (i = 0; i != 5; i++)

A[i][j] = 0;

Of course, this is an artificial sample, so we may easily see the error, but in a real

application, the error might be not so apparent. This is the correct code:

size_t i, j;

for (i = 0; i != 5; i++)

for (j = 0; j != 5; j++)

A[i][j] = 0;

Using one variable both for the outer and inner loops is not always a mistake.

Consider a sample of correct code the analyzer won't generate the warning for:

for(c = lb; c <= ub; c++)

{

if (!(xlb <= xlat(c) && xlat(c) <= ub))

{

Range * r = new Range(xlb, xlb + 1);

for (c = lb + 1; c <= ub; c++)

r = doUnion(

r, new Range(xlat(c), xlat(c) + 1));

return r;

}

}

In this code, the inner loop "for (c = lb + 1; c <= ub; c++)" is arranged by the "c"

variable. The outer loop also uses the "c" variable. But there is no error here. After

the inner loop is executed, the "return r;" operator will perform exit from the

function.

Page 388: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V536. Be advised that the utilized constant value is represented by an octal form.Using constants in the octal number system is not an error in itself. This system is

convenient when handling bits and is used in code that interacts with a network or

external devices. However, an average programmer uses this number system rarely

and therefore may make a mistake by writing 0 before a number forgetting that it

makes this value an octal number.

The analyzer warns about octal constants if there are no other octal constants

nearby. Such "single" octal constants are usually errors.

Let's study a sample taken from a real application. It is rather large but it illustrates

the sense of the issue very well.

inline

void elxLuminocity(const PixelRGBf& iPixel,

LuminanceCell< PixelRGBf >& oCell)

{

oCell._luminance = 0.2220f*iPixel._red +

0.7067f*iPixel._blue +

0.0713f*iPixel._green;

oCell._pixel = iPixel;

}

inline

void elxLuminocity(const PixelRGBi& iPixel,

LuminanceCell< PixelRGBi >& oCell)

{

oCell._luminance = 2220*iPixel._red +

7067*iPixel._blue +

0713*iPixel._green;

oCell._pixel = iPixel;

Page 389: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

}

It is hard to find the error while reviewing this code, but it does have an error. The

first function elxLuminocity is correct and handles values of the 'float' type. There

are the following constants in the code: 0.2220f, 0.7067f, 0.0713f. The second

function is similar to the first but it handles integer values. All the integer values are

multiplied by 10000. Here are they: 2220, 7067, 0713. The error is that the last

constant "0713" is defined in the octal number system and its value is 459, not 713.

This is the correct code:

oCell._luminance = 2220*iPixel._red +

7067*iPixel._blue +

713*iPixel._green;

As it was said above, the warning of octal constants is generated only if there are no

other octal constants nearby. That is why the analyzer considers the following

sample safe and does not produce any warnings for it:

static unsigned short bytebit[8] = {

01, 02, 04, 010, 020, 040, 0100, 0200 };

V537. Consider reviewing the correctness of 'X' item's usage.The analyzer detected a potential misprint in code. This rule tries to diagnose an

error of the following type using the heuristic method:

int x = static_cast<int>(GetX()) * n;

int y = static_cast<int>(GetX()) * n;

In the second line, the GetX() function is used instead of GetY(). This is the correct

code:

int x = static_cast<int>(GetX()) * n;

int y = static_cast<int>(GetY()) * n;

Page 390: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

To detect this suspicious fragment, the analyzer followed this logic: we have a line

containing a name that includes the "X" fragment. Beside it, there is a line that has

an antipode name with "Y". But this second line has "X" as well. Since this

condition and some other conditions hold, the construct must be reviewed by the

programmer. This code would not be considered dangerous if, for instance, there

were no variables "x" and "y" to the left. This is a code sample the analyzer ignores:

array[0] = GetX() / 2;

array[1] = GetX() / 2;

Unfortunately, this rule often produces false alarms since the analyzer does not

know how the program is organized and what the code's purpose is. This is a sample

of a false alarm:

halfWidth -= borderWidth + 2;

halfHeight -= borderWidth + 2;

The analyzer supposed that the second line must be presented by a different

expression, for instance, "halfHeight -= borderHeight + 2". But actually there is no

error here. The border's size is equal in both vertical and horizontal positions. There

is just no borderHeight constant. However, such high-level abstractions are not clear

to the analyzer. To suppress this warning, you may type the "//-V537" comment into

the code.

V538. The line contains control character 0x0B (vertical tabulation).There are ASCII control characters in the program text. The following character

refers to them:

0x0B - LINE TABULATION (vertical tabulation) - Moves the typing point to the

next vertical tabulation position. In terminals, this character is usually equivalent to

line feed.

Page 391: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Such characters are allowed to be present in program text and such text is

successfully compiled in Visual C++. However, these characters must have

appeared in the program text by accident and you'd better get rid of them. There are

two reasons for that:

1) If such a control character stands in the first lines of a file, the Visual Studio

environment cannot understand the file's format and opens it with the Notepad

application instead of its own embedded editor.

2) Some external tools working with program texts may incorrectly process files

containing the above mentioned control characters.

0x0B characters are invisible in the Visual Studio 2010 editor. To find and delete

them in a line, you may open the file in the Notepad application or any other editor

that can display such control characters.

V539. Consider inspecting iterators which are being passed as arguments to function 'Foo'.The analyzer detected code handling containers which is likely to have an error.

You should examine this code fragment.

Let's study several samples demonstrating cases when this warning is generated:

Sample 1.

void X(std::vector<int> &X, std::vector<int> &Y)

{

std::for_each (X.begin(), X.end(), SetValue);

std::for_each (Y.begin(), X.end(), SetValue);

}

Two arrays are filled with some values in the function. Due to the misprint, the

"std::for_each" function, being called for the second time, receives iterators from

Page 392: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

different containers, which causes an error during program execution. This is the

correct code:

std::for_each (X.begin(), X.end(), SetValue);

std::for_each (Y.begin(), Y.end(), SetValue);

Sample 2.

std::includes(a.begin(), a.end(), a.begin(), a.end());

This code is strange. The programmer most probably intended to process two

different chains instead of one. This is the correct code:

std::includes(a.begin(), a.end(), b.begin(), b.end());

V540. Member 'x' should point to string terminated by two 0 characters.In Windows API, there are structures where string-pointers must end with a double

zero. For example, such is the lpstrFilter member in the OPENFILENAME

structure.

Here is the description of lpstrFilter in MSDN:

LPCTSTR

A buffer containing pairs of null-terminated filter strings. The last string in the

buffer must be terminated by two NULL characters.

It follows from this description that we must add one more zero at the end of the

string. For example: lpstrFilter = "All Files\0*.*\0";

However, many programmers forget about this additional zero. This is a sample of

incorrect code we found in one application:

lofn.lpstrFilter = L"Equalizer Preset (*.feq)\0*.feq";

Page 393: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

This code will cause generating rubbish in the filter field in the file dialogue. This is

the correct code:

lofn.lpstrFilter = L"Equalizer Preset (*.feq)\0*.feq\0";

We added 0 at the end of the string manually while the compiler will add one more

zero. Some programmers write this way to make it clearer:

lofn.lpstrFilter = L"Equalizer Preset (*.feq)\0*.feq\0\0";

But here we will get three zeroes instead of two. It is unnecessary yet well visible to

the programmer.

There are also some other structures besides OPENFILENAME where you might

make such mistakes. For instance, the strings lpstrGroupNames and

lpstrCardNames in structures OPENCARD_SEARCH_CRITERIA,

OPENCARDNAME must end with a double zero too.

V541. It is dangerous to print the string into itself.The analyzer detected a potential error: a string gets printed inside itself. This may

lead to unexpected results. Look at this sample:

char s[100] = "test";

sprintf(s, "N = %d, S = %s", 123, s);

In this code, the 's' buffer is used simultaneously as a buffer for a new string and as

one of the elements making up the text. The programmer intends to get this string:

N = 123, S = test

But actually this code will cause creating the following string:

N = 123, S = N = 123, S =

Page 394: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

In other cases, such code may cause even a program crash. To fix the code, we

should use a new buffer to save the result. This is the correct code:

char s1[100] = "test";

char s2[100];

sprintf(s2, "N = %d, S = %s", 123, s1);

V542. Consider inspecting an odd type cast: 'Type1' to ' Type2'.The analyzer found a very suspicious explicit type conversion. This type conversion

may signal an error. You should review the corresponding code fragment.

For example:

typedef unsigned char Byte;

void Process(wchar_t ch);

void Process(wchar_t *str);

void Foo(Byte *buf, size_t nCount)

{

for (size_t i = 0; i < nCount; ++i)

{

Process((wchar_t *)buf[i]);

}

}

There is the Process function that can handle both separate characters and strings.

There is also the 'Foo' function which receives a buffer-pointer at the input. This

buffer is handled as an array of characters of the wchar_t type. But the code

contains an error, so the analyzer warns you that the 'char' type is explicitly cast to

the ' wchar_t *' type. The reason is that the "(wchar_t *)buf[i]" expression is

equivalent to "(wchar_t *)(buf[i])". A value of the 'char' type is first fetched out of

the array and then cast to a pointer. This is the correct code:

Page 395: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Process(((wchar_t *)buf)[i]);

However, strange type conversions are not always errors. Consider a sample of safe

code taken from a real application:

wchar_t *destStr = new wchar_t[len+1];

...

for (int j = 0 ; j < nbChar ; j++)

{

if (Case == UPPERCASE)

destStr[j] =

(wchar_t)::CharUpperW((LPWSTR)destStr[j]);

...

Here you may see an explicit conversion of the 'wchar_t' type to 'LPWSTR' and

vice versa. The point is that Windows API and the CharUpperW function can

handle an input value both as a pointer and a character. This is the function's

prototype:

LPTSTR WINAPI CharUpperW(__inout LPWSTR lpsz);

If the high-order part of the pointer is 0, the input value is considered a character.

Otherwise, the function processes the string.

The analyzer knows about the CharUpperW function's behavior and considers this

code safe. But it may produce a false alarm in some other similar situation.

V543. It is odd that value 'X' is assigned to the variable 'Y' of HRESULT type.The analyzer detected a potential error related to handling a variable of the

HRESULT type.

HRESULT is a 32-bit value divided into three different fields: severity code, device

code and error code. Such special constants as S_OK, E_FAIL, E_ABORT, etc.

Page 396: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

serve to handle HRESULT-values while the SUCCEEDED and FAILED macros

are used to check HRESULT-values.

The V543 warning is generated if the analyzer detects an attempt to write value -1,

true or false into a variable of the HRESULT type. Consider this sample:

HRESULT h;

...

if (bExceptionCatched)

{

ShowPluginErrorMessage(pi, errorText);

h = -1;

}

Writing of value "-1" is incorrect. If you want to report about some unspecified

error, you should use value 0x80004005L (Unspecified failure). This constant and

the like are described in "WinError.h". This is the correct code:

if (bExceptionCatched)

{

ShowPluginErrorMessage(pi, errorText);

h = E_FAIL;

}

References:

1. MSDN. Common HRESULT Values.

2. Wikipedia. HRESULT.

V544. It is odd that the value 'X' of HRESULT type is compared with 'Y'.The analyzer detected a potential error related to handling a variable of the

HRESULT type.

HRESULT is a 32-bit value divided into three different fields: severity code, device

code and error code. Such special constants as S_OK, E_FAIL, E_ABORT, etc.

Page 397: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

serve to handle HRESULT-values while the SUCCEEDED and FAILED macros

are used to check HRESULT-values.

The V544 warning is generated if the analyzer detects an attempt to compare a

variable of the HRESULT type to -1, true or false. Consider this sample:

HRESULT hr;

...

if (hr == -1)

{

}

Comparison of the variable to "-1" is incorrect. Error codes may differ. For instance,

these may be 0x80000002L (Ran out of memory), 0x80004005L (unspecified

failure), 0x80070005L (General access denied error) and so on. To check the

HRESULT -value in this case, we must use the FAILED macro defined in

"WinError.h". This is the correct code:

if (FAILED(hr))

{

}

References:

1. MSDN. Common HRESULT Values.

2. Wikipedia. HRESULT.

V545. Such conditional expression of 'if' statement is incorrect for the HRESULT type value 'Foo'. The SUCCEEDED or FAILED macro should be used instead.

Page 398: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer detected a potential error related to handling a variable of the

HRESULT type.

HRESULT is a 32-bit value divided into three different fields: severity code, device

code and error code. Such special constants as S_OK, E_FAIL, E_ABORT, etc.

serve to handle HRESULT-values while the SUCCEEDED and FAILED macros

are used to check HRESULT-values.

The V545 warning is generated if a variable of the HRESULT type is used in the 'if'

operator as a bool-variable. Consider this sample:

HRESULT hr;

...

if (hr)

{

}

'HRESULT' and 'bool' are two types absolutely different in meaning. This sample of

comparison is incorrect. The HRESULT type can have many states including 0L

(S_OK), 0x80000002L (Ran out of memory), 0x80004005L (unspecified failure)

and so on. Note that the code of the state S_OK is 0.

To check the HRESULT-value, we must use macro SUCCEEDED or FAILED

defined in "WinError.h". These are correct versions of code:

if (FAILED(hr))

{

}

if (SUCCEEDED(hr))

{

}

References:

1. MSDN. Common HRESULT Values.

2. Wikipedia. HRESULT.

Page 399: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V546. Member of a class is initialized by itself: 'Foo(Foo)'.The analyzer detected a misprint in the fragment when a class member is being

initialized by itself. Consider an example of a constructor:

C95(int field) : Field(Field)

{

int Field;

...

}

The names of the argument and the class member here differ only in one letter.

Because of that, the programmer misprinted here causing the Field member to

remain uninitialized. This is the correct code:

C95(int field) : Field(field)

{

int Field;

...

}

V547. Expression is always true/false.The analyzer detected a potential error: a condition is always true or false. Such

conditions do not always signal an error but still you must review such code

fragments.

Consider a code sample:

LRESULT CALLBACK GridProc(HWND hWnd,

UINT message, WPARAM wParam, LPARAM lParam)

{

...

Page 400: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

if (wParam<0)

{

BGHS[SelfIndex].rows = 0;

}

else

{

BGHS[SelfIndex].rows = MAX_ROWS;

}

...

}

The "BGHS[SelfIndex].rows = 0;" branch here will never be executed because the

wParam variable has an unsigned type WPARAM which is defined as "typedef

UINT_PTR WPARAM".

Either this code contains a logical error or we may reduce it to one line:

"BGHS[SelfIndex].rows = MAX_ROWS;".

Now let's examine a code sample which is correct yet potentially dangerous and

contains a meaningless comparison:

unsigned int a = _ttoi(LPCTSTR(str1));

if((0 > a) || (a > 255))

{

return(FALSE);

}

The programmer wanted to implement the following algorithm.

1) Convert a string into a number.

2) If the number lies outside the range [0..255], return FALSE.

The error here is in using the 'unsigned' type. If the _ttoi function returns a negative

value, it will turn into a large positive value. For instance, value "-3" will become

4294967293. The comparison '0 > a' will always return true. The program works

correctly only because the range of values [0..255] is checked by the 'a > 255'

condition.

Page 401: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer will generate the following warning for this code fragment: "V547

Expression '0 > a' is always false. Unsigned type value is never < 0."

We should correct this fragment this way:

int a = _ttoi(LPCTSTR(str1));

if((0 > a) || (a > 255))

{

return(FALSE);

}

Let's consider one special case. The analyzer generates the warning:

V547 Expression 's == "Abcd"' is always false. To compare strings you should use

strcmp() function.

for this code:

const char *s = "Abcd";

void Test()

{

if (s == "Abcd")

cout << "TRUE" << endl;

else

cout << "FALSE" << endl;

}

But it is not quite true. This code still can print "TRUE" when the 's' variable and

Test() function are defined in one module. The compiler does not produce a lot of

identical constant strings but uses one string. As a result, the code sometimes seems

quite operable. However, you must understand that this code is very bad and you

should use special functions for comparison.

Another example:

if (lpszHelpFile != 0)

{

pwzHelpFile = ((_lpa_ex = lpszHelpFile) == 0) ?

Page 402: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

0 : Foo(lpszHelpFile);

...

}

This code works quite correctly but it is too tangled. The "((_lpa_ex = lpszHelpFile)

== 0)" condition is always false, as the lpszHelpFile pointer is always not equal to

zero. This code is difficult to read and should be rewritten.

This is the simplified code:

if (lpszHelpFile != 0)

{

_lpa_ex = lpszHelpFile;

pwzHelpFile = Foo(lpszHelpFile);

...

}

Another example:

SOCKET csd;

csd = accept(nsd, (struct sockaddr *) &sa_client, &clen);

if (csd < 0)

....

The accept function in Visual Studio header files returns a value that has the

unsigned SOCKET type. That's why the check 'csd < 0' is invalid since its result is

always false. The returned values must be explicitly compared to different

constants, for instance, SOCKET_ERROR:

if (csd == SOCKET_ERROR)

The analyzer warns you far not of all the conditions which are always false or true.

It diagnoses only those cases when an error is highly probable. Let's consider some

samples that the analyzer considers absolutely correct:

// 1) Eternal loop

while (true)

{

Page 403: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

...

}

// 2) Macro expanded in the Release version

// MY_DEBUG_LOG("X=", x);

0 && ("X=", x);

// 3) assert(false);

if (error) {

assert(false);

return -1;

}

V548. Consider reviewing type casting. TYPE X[][] in not equivalent to TYPE **X.The analyzer detected a potential error related to an explicit type conversion. An

array defined as "type Array[3][4]" is cast to type "type **". This type conversion is

most likely to be meaningless.

Types "type[a][b]" and "type **" are different data structures. Type[a][b] is a single

memory area that you can handle as a two-dimensional array. Type ** is an array of

pointers referring to some memory areas.

Here is an example:

void Foo(char **names, size_t count)

{

for(size_t i=0; i<count; i++)

printf("%s\n", names[i]);

}

void Foo2()

{

Page 404: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

char names[32][32];

...

Foo((char **)names, 32); //Crash

}

This is the correct code:

void Foo2()

{

char names[32][32];

...

char *names_p[32];

for(size_t i=0; i<32; i++)

names_p[i] = names[i];

Foo(names_p, 32); //OK

}

V549. The 'first' argument of 'Foo' function is equal to the 'second' argument.The analyzer detected a potential error in the program: coincidence of two actual

arguments of a function. Passing the same value as two arguments is a normal thing

for many functions. But if you deal with such functions as memmove, memcpy,

strstr and strncmp, you must check the code.

Here is a sample from a real application:

#define str_cmp(s1, s2) wcscmp(s1, s2)

...

v = abs(str_cmp(a->tdata, a->tdata));

The misprint here causes the wcscmp function to perform comparison of a string

from itself. This is the correct code:

v = abs(str_cmp(a->tdata, b->tdata));

Page 405: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer generates the warning if the following functions are being handled:

memcpy, memmove, memcmp, _memicmp, strstr, strspn, strtok, strcmp, strncmp,

wcscmp, _stricmp, wcsncmp, etc. If you found a similar error that analyzer fails to

diagnose, please tell us the name of the function that must not take same values as

the first and second arguments.

V550. An odd precise comparison. It's probably better to use a comparison with defined precision: fabs(A - B) < Epsilon or fabs(A - B) > Epsilon.The analyzer detected a potential error: the == or != operator is used to compare

floating point numbers. Precise comparison might often cause an error.

Consider this sample:

double a = 0.5;

if (a == 0.5) //OK

x++;

double b = sin(M_PI / 6.0);

if (b == 0.5) //ERROR

x++;

The first comparison 'a == 0.5' is true. The second comparison 'b == 0.5' may be

both true and false. The result of the 'b == 0.5' expression depends upon the

processor, compiler's version and settings being used. For instance, the 'b' variable's

value was 0.49999999999999994 when we used the Visual C++ 2010 compiler. A

more correct version of this code looks this way:

double b = sin(M_PI / 6.0);

if (fabs(b - 0.5) < DBL_EPSILON)

Page 406: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

x++;

In this case, the comparison with error presented by DBL_EPSILON is true because

the result of the sin() function lies within the range [-1, 1]. But if we handle values

larger than several units, errors like FLT_EPSILON and DBL_EPSILON will be

too small. And vice versa, if we handle values like 0.00001, these errors will be too

big. Each time you must choose errors adequate to the range of possible values.

Question: how do I compare two double-variables then?

double a = ...;

double b = ...;

if (a == b) // how?

{

}

There is no single right answer. In most cases, you may compare two variables of

the double type by writing the following code:

if (fabs(a-b) <= DBL_EPSILON * fmax(fabs(a), fabs(b)))

{

}

But be careful with this formula - it works only for numbers with the same sign.

Besides, if you have a row with many calculations, there is an error constantly

accumulating, which might cause the DBL_EPSILON constant to appear a too

small value.

Well, can I perform precise comparison of floating point values?

Sometimes, yes. But rather rarely. You may perform such comparison if the values

you are comparing are one and the same value in its sense.

Here is a sample where you may use precise comparison:

// -1 - value is not initialized.

float val = -1.0f;

if (Foo1())

Page 407: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

val = 123.0f;

if (val == -1.0f) //OK

{

}

In this case, the comparison with value "-1" is permissible because it is this very

value which we used to initialize the variable before.

We cannot cover the topic of comparing float/double types within the scope of

documentation, so please refer to additional sources given at the end of this article.

The analyzer can only point to potentially dangerous code fragments where

comparison may result unexpectedly. But it is only the programmer who may

understand whether these code fragments really contain errors. We cannot also give

precise recommendations in the documentation since tasks where floating point

types are used are too diverse.

The diagnostic message isn't generated if two identical expressions of 'float' or

'double' types are being compared. Such a comparison allows to identify the value

as NaN. The example of code implementing the verification of this kind:

bool isnan(double X) { return X != X; }

References:

1. Bruce Dawson. Comparing floating point numbers.

2. Bruce Dawson. Comparing floating point numbers, 2012 Edition.

3. Viva64 Blog. 64-bit programs and floating-point calculations.

4. Wikipedia. Floating point.

5. CodeGuru Forums. C++ General: How is floating point representated?

V551. The code under this 'case' label is unreachable.

Page 408: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer detected a potential error: one of the switch() operator's branches

never gets control. The reason is that the switch() operator's argument cannot accept

the value defined in the case operator. Consider this sample:

char ch = strText[i];

switch (ch)

{

case '<':

strHTML += "&lt;";

bLastCharSpace = FALSE;

nNonbreakChars++;

break;

case '>':

strHTML += "&gt;";

bLastCharSpace = FALSE;

nNonbreakChars++;

break;

case 0xB7:

case 0xBB:

strHTML += ch;

strHTML += "<wbr>";

bLastCharSpace = FALSE;

nNonbreakChars = 0;

break;

...

}

The branch following "case 0xB7:" and "case 0xBB:" in this code will never get

control. The 'ch' variable has the 'char' type and therefore the range of its values is [-

128..127]. The comparisons "ch == 0xB7" and "ch==0xBB" will always be false.

To make the code correct, we must cast the 'ch' variable to the 'unsigned char' type:

unsigned char ch = strText[i];

switch (ch)

{

...

case 0xB7:

Page 409: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

case 0xBB:

strHTML += ch;

strHTML += "<wbr>";

bLastCharSpace = FALSE;

nNonbreakChars = 0;

break;

...

}

V552. A bool type variable is being incremented. Perhaps another variable should be incremented instead.The analyzer detected a potentially dangerous construct in code where a variable of

the bool type is being incremented:

bool bValue = false;

...

bValue++;

First, the C++ language's standard reads:

The use of an operand of type bool with the postfix ++ operator is deprecated.

It means that we should not use such a construct.

Second, it is better to assign the 'true' value explicitly to this variable. This code is

clearer:

bValue = true;

Third, it might be that there is a misprint in the code and the programmer actually

intended to increment a different variable. For example:

bool bValue = false;

Page 410: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

int iValue = 1;

...

if (bValue)

bValue++;

A wrong variable was used by accident here while it was meant to be this code:

bool bValue = false;

int iValue = 1;

...

if (bValue)

iValue++;

V553. The length of function's body or class's declaration is more than 2000 lines long. You should consider refactoring the code.The analyzer detected a class definition or function body that occupies more than

2000 lines. This class or function does not necessarily contain errors yet the

probability is very high. The larger a function is, the more probable it is to make an

error and the more difficult it is to debug. The larger a class is, the more difficult it

is to examine its interfaces.

This message is a good opportunity to find time for code refactoring at last. Yes,

you always have to do something urgent but the larger you functions and classes

are, the more time you will spend on supporting the old code and eliminating errors

in it instead of writing a new functionality.

References:

1. Steve McConnell, "Code Complete, 2nd Edition" Microsoft Press, Paperback, 2nd edition, Published June 2004, 914 pages, ISBN: 0-7356-1967-0. (Part 7.4. How Long Can a Routine Be?).

Page 411: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V554. Incorrect use of smart pointer.Analyzer located an issue then the usage of smart pointer could lead to undefined

behavior, in particular to the heap damage, abnormal program termination or

incomplete objects destruction. The error here is that different methods will be used

to allocate and free memory.

Consider a sample:

void Foo()

{

struct A

{

A() { cout << "A()" << endl; }

~A() { cout << "~A()" << endl; }

};

std::unique_ptr<A> p(new A[3]);

}

By default, the unique_ptr class uses the 'delete' operator to release memory. That is

why only one object of the 'A' class will be destroyed and the following text will be

displayed:

A()

A()

A()

~A()

To fix this error, we must specify that the class must use the 'delete []' operator.

Here is the correct code:

std::unique_ptr<A[]> p(new A[3]);

Page 412: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Now the same number of constructors and destructors will be called and we will see

this text:

A()

A()

A()

~A()

~A()

~A()

Consider another sample:

std::unique_ptr<int []> p((int *)malloc(sizeof(int) * 5));

The function 'malloc()' is used to allocate memory while the 'delete []' operator is

used to release it. It is incorrect and we must specify that the 'free()' function must

be used to release memory. This is the correct code:

int *d =(int *)std::malloc(sizeof(int) * 5);

unique_ptr<int, void (*)(void*)> p(d, std::free);

Additional materials on this topic:

1. Discussion at StackOverflow. "How could pairing new[] with delete possibly lead to memory leak only?".

V555. The expression of the 'A - B > 0' kind will work as 'A != B'.The analyzer detected a potential error in an expression of "A - B > 0" type. It is

highly probable that the condition is wrong if the "A - B" subexpression has the

unsigned type.

The "A - B > 0" condition holds in all the cases when 'A' is not equal to 'B'. It means

that we may write the "A != B" expression instead of "A - B > 0". However, the

programmer must have intended to implement quite a different thing.

Consider this sample:

Page 413: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

unsigned int *B;

...

if (B[i]-70 > 0)

The programmer wanted to check whether the i-item of the B array is above 70. He

could write it this way: "B[i] > 70". But he, proceeding from some reasons, wrote it

this way: "B[i]-70 > 0" and made a mistake. He forgot that items of the 'B' array

have the 'unsigned' type. It means that the "B[i]-70" expression has the 'unsigned'

type too. So it turns out that the condition is always true except for the case when

the 'B[i]' item equals to 70.

Let's clarify this case.

If 'B[i]' is above 70, then "B[i]-70" is above 0.

If 'B[i]' is below 70, then we will get an overflow of the unsigned type and a very

large value as a result. Let B[i] == 50. Then "B[i]-70" = 50u - 70u = 0xFFFFFFECu

= 4294967276. Surely, 4294967276 > 0.

A demonstration sample:

unsigned A;

A = 10; cout << "A=10 " << (A-70 > 0) << endl;

A = 70; cout << "A=70 " << (A-70 > 0) << endl;

A = 90; cout << "A=90 " << (A-70 > 0) << endl;

// Will be printed

A=10 1

A=70 0

A=90 1

The first way to correct the code:

unsigned int *B;

...

if (B[i] > 70)

The second way to correct the code:

Page 414: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

int *B;

...

if (B[i]-70 > 0)

Note that an expression of the "A - B > 0" type far not always signals an error.

Consider a sample where the analyzer generates a false alarm:

// Functions GetLength() and GetPosition() return

// value of size_t type.

while ((inStream.GetLength() - inStream.GetPosition()) > 0)

{ ... }

GetLength() is always above or equal to GetPosition() here, so the code is correct.

To suppress the false alarm, we may add the comment //-V555 or rewrite the code

in the following way:

while (inStream.GetLength() != inStream.GetPosition())

{ ... }

Here is another case when no error occurs.

__int64 A;

__uint32 B;

...

if (A - B > 0)

The "A - B" subexpression here has the signed type __int64 and no error occurs.

The analyzer does not generate warnings in such cases.

V556. The values of different enum types are compared.The analyzer detected a potential error: code contains comparison of enum values

which have different types.

Consider a sample:

Page 415: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

enum ErrorTypeA { E_OK, E_FAIL };

enum ErrorTypeB { E_ERROR, E_SUCCESS };

void Foo(ErrorTypeB status) {

if (status == E_OK)

{ ... }

}

The programmer used a wrong name in the comparison by accident, so the

program's logic is disrupted. This is the correct code:

void Foo(ErrorTypeB status) {

if (status == E_SUCCESS)

{ ... }

}

Comparison of values of different enum types is not necessarily an error, but you

must review such code.

V557. Array overrun is possible.The analyzer detected a potential memory access outside an array. The most

common case is an error occurring when writing the '\0' character after the last

array's item. Let's examine a sample of this error:

struct IT_SAMPLE

{

unsigned char filename[14];

...

};

static int it_riff_dsmf_process_sample(

IT_SAMPLE * sample, const unsigned char * data)

{

memcpy( sample->filename, data, 13 );

sample->filename[ 14 ] = 0;

...

}

Page 416: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The last array's item has index 13, not 14. That is why the correct code is this one:

sample->filename[13] = 0;

Of course, you'd better use an expression involving the sizeof() operator instead of

constant index' value in such cases. However, remember that you may make a

mistake in this case too. For example:

typedef wchar_t letter;

letter name[30];

...

name[sizeof(name) - 1] = L'\0';

At first sight, the "sizeof(name) - 1" expression is right. But the programmer forgot

that he handled the 'wchar_t' type and not 'char'. As a result, the '\0' character is

written far outside the array's boundaries. This is the correct code:

name[sizeof(name) / sizeof(*name) - 1] = L'\0';

To simplify writing of such constructs, you may use this special macro:

#define str_len(arg) ((sizeof(arg) / sizeof(arg[0])) - 1)

name[str_len(name)] = L'\0';

The analyzer detects some errors when the index is represented by a variable whose

value might run out of the array's boundaries. For example:

int buff[25];

for (int i=0; i <= 25; i++)

buff[i] = 10;

This is the correct code:

int buff[25];

for (int i=0; i < 25; i++)

buff[i] = 10;

Note that the analyzer might make mistakes when handling such value ranges and

generate false alarms.

Page 417: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V558. Function returns the pointer/reference to temporary local object.The analyzer detected an issue when a function returns a pointer to a local object.

This object will be destroyed when leaving the function, so you will not be able to

use the pointer to it anymore. In a most common case, this diagnostic message is

generated against the following code:

float *F()

{

float f = 1.0;

return &f;

}

Of course, the error would hardly be present in such a form in real code. Let's

consider a more real example.

int *Foo()

{

int A[10];

// ...

if (err)

return 0;

int *B = new int[10];

memcpy(B, A, sizeof(A));

return A;

}

Here, we handled the temporary array A. On some condition, we must return the

pointer to the new array B. But the misprint causes the A array to be returned,

which will cause unexpected behavior of the program or crash. This is the correct

code:

Page 418: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

int *Foo()

{

...

int *B = new int[10];

memcpy(B, A, sizeof(A));

return B;

}

V559. Suspicious assignment inside the conditional expression of 'if/while/for' statement.The analyzer detected an issue that has to do with using the assignment operator '='

in the conditional expression of an 'if' or 'while' statement. Such a construct usually

indicates the presence of a mistake. It is very likely that the programmer intended to

use the '==' operator instead of '='.

Consider the following example:

const int MAX_X = 100;

int x;

...

if (x = MAX_X)

{ ... }

There is a typo in this code: the value of the 'x' variable will be modified instead of

being compared with the constant MAX_X:

if (x == MAX_X)

{ ... }

Using assignments inside conditions is not always an error, of course. This

technique is used by many programmers to make code shorter. However, it is a bad

style because it takes a long time to find out if such a construct results from a typo

or the programmer's intention to make the code shorter.

Page 419: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Instead of using assignments inside conditional expressions, we recommend

implementing them as a separate operation or enclosing them in additional

parentheses:

while ((x = Foo()))

{

...

}

Code like this will be interpreted by both the analyzer and most compilers as

correct. Besides, it tells other programmers that there is no error here.

V560. A part of conditional expression is always true/false.The analyzer detected a potential error inside a logical condition. A part of a logical

condition is always true and therefore is considered dangerous.

Consider this sample:

#define REO_INPLACEACTIVE (0x02000000L)

...

if (reObj.dwFlags && REO_INPLACEACTIVE)

m_pRichEditOle->InPlaceDeactivate();

The programmer wanted to check some particular bit in the dwFlags variable. But

he made a misprint by writing the '&&' operator instead of '&' operator. This is the

correct code:

if (reObj.dwFlags & REO_INPLACEACTIVE)

m_pRichEditOle->InPlaceDeactivate();

Let's examine another sample:

if (a = 10 || a == 20)

Page 420: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The programmer wrote the assignment operator '=' instead of comparison operator

'==' by accident. From the viewpoint of the C++ language, this expression is

identical to an expression like "if (a = (10 || a == 20))".

The analyzer considers the "10 || a == 20" expression dangerous because its left part

is a constant. This is the correct code:

if (a == 10 || a == 20)

Sometimes the V560 warning indicates just a surplus code, not an error. Consider

the following sample:

if (!mainmenu) {

if (freeze || winfreeze ||

(mainmenu && gameon) ||

(!gameon && gamestarted))

drawmode = normalmode;

}

The analyzer will warn you that the 'mainmenu' variable in the (mainmenu &&

gameon) subexpression is always equal to 0. It follows from the check above " if (!

mainmenu)". This code can be quite correct. But it is surplus and should be

simplified. It will make the program clearer to other developers.

This is the simplified code:

if (!mainmenu) {

if (freeze || winfreeze ||

(!gameon && gamestarted))

drawmode = normalmode;

}

Some C++ constructs are considered safe even if a part of an expression inside them

is a constant. Here are some samples when the analyzer considers the code safe:

a subexpression contains operators sizeof(): if (a == b && sizeof(T) < sizeof(__int64)) {};

an expression is situated inside a macro: assert(false);

Page 421: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

two numerical constants are being compared: if (MY_DEFINE_BITS_COUNT == 4) {};

etc.

V561. It's probably better to assign value to 'foo' variable than to declare it anew.The analyzer detected a potential error: there is a variable in code which is defined

and initialized but not being used further. Besides, there is a variable in the exterior

scope which also has the same name and type. It is highly probable that the

programmer intended to use an already existing variable instead of defining a new

one.

Let's examine this sample:

BOOL ret = TRUE;

if (m_hbitmap)

BOOL ret = picture.SaveToFile(fptr);

The programmer defined a new variable 'ret' by accident, which causes the previous

variable to always have the TRUE value regardless if the picture is saved into a file

successfully or not. This is the correct code:

BOOL ret = TRUE;

if (m_hbitmap)

ret = picture.SaveToFile(fptr);

V562. It's odd to compare a bool type value with a value of N.The analyzer detected an issue when a value of the bool type is compared to a

number. Most likely, there is an error.

Consider this sample:

Page 422: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

if (0 < A < 5)

The programmer not familiar with the C++ language well wanted to use this code to

check whether the value lies within the range between 0 and 5. Actually, the

calculations will be performed in the following sequence: ((0 < A) < 5). The result

of the "0 < A" expression has the bool type and therefore is always below 5.

This is the correct code for the check:

if (0 < A && A < 5)

The previous example resembles a mistake usually made by students. But even

skilled developers are not secure from such errors.

Let's consider another sample:

if (! (fp = fopen(filename, "wb")) == -1) {

perror("opening image file failed");

exit(1);

}

Here we have 2 errors of different types at once. First, the "fopen" function returns

the pointer and compares the returned value to NULL. The programmer confused

the "fopen" function with "open" function, the latter being that very function that

returns "-1" if there is an error. Second, the negation operation "!" is executed first

and only then the value is compared to "-1". There is no sense in comparing a value

of the bool type to "-1" and that is why the analyzer warned us about the code.

This is the correct code:

if ( (fp = fopen(filename, "wb")) == NULL) {

perror("opening image file failed");

exit(1);

}

Page 423: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V563. It is possible that this 'else' branch must apply to the previous 'if' statement.The analyzer detected a potential error in logical conditions: code's logic does not

coincide with the code editing.

Consider this sample:

if (X)

if (Y) Foo();

else

z = 1;

The code editing disorientates you so it seems that the "z = 1" assignment takes

place if X == false. But the 'else' branch refers to the nearest operator 'if'. In other

words, this code is actually analogous to the following code:

if (X)

{

if (Y)

Foo();

else

z = 1;

}

So, the code does not work the way it seems at first sight.

If you get the V563 warning, it may mean one of the two following things:

1) Your code is badly edited and there is no error actually. In this case you need to

edit the code so that it becomes clearer and the V563 warning is not generated. Here

is a sample of correct editing:

if (X)

if (Y)

Page 424: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Foo();

else

z = 1;

2) A logical error has been found. Then you may correct the code, for instance, this

way:

if (X) {

if (Y)

Foo();

} else {

z = 1;

}

V564. The '&' or '|' operator is applied to bool type value. You've probably forgotten to include parentheses or intended to use the '&&' or '||' operator.The analyzer detected a potential error: operators '&' and '|' handle bool-type values.

Such expressions are not necessarily errors but they usually signal misprints or

condition errors.

Consider this sample:

int a, b;

#define FLAG 0x40

...

if (a & FLAG == b)

{

}

Page 425: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

This example is a classic one. A programmer may be easily mistaken in operations'

priorities. It seems that computing runs in this sequence: "(a & FLAG) == b". But

actually it is "a & (FLAG == b)". Most likely, it is an error.

The analyzer will generate a warning here because it is odd to use the '&' operator

for variables of int and bool types.

If it turns out that the code does contain an error, you may fix it the following way:

if ((a & FLAG) == b)

Of course, the code might appear correct and work as it was intended. But still

you'd better rewrite it to make it clearer. Use the && operator or additional

brackets:

if (a && FLAG == b)

if (a & (FLAG == b))

The V564 warning will not be generated after these corrections are done while the

code will get easier to read.

Consider another sample:

#define SVF_CASTAI 0x00000010

if ( !ent->r.svFlags & SVF_CASTAI ) {

...

}

Here we have an obvious error. It is the "!ent->r.svFlags" subexpression that will be

calculated at first and we will get either true of false. But it does not matter: whether

we execute "true & 0x00000010" operation or "false & 0x00000010" operation, the

result will be the same. The condition in this sample is always false.

This is the correct code:

if ( ! (ent->r.svFlags & SVF_CASTAI) )

Page 426: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Note. The analyzer will not generate the warning if there are bool-type values to the

left and to the right of the '&' or '|' operator. Although such code does not look too

smart, still it is correct. Here is a code sample the analyzer considers safe:

bool X, Y;

...

if (X | Y)

{ ... }

V565. An empty exception handler. Silent suppression of exceptions can hide the presence of bugs in source code during testing.An exception handler was found that does not do anything. Consider this code:

try {

...

}

catch (MyExcept &)

{

}

Of course, this code is not necessarily incorrect. But it is very odd to suppress an

exception by doing nothing. Such exception handling might conceal defects in the

program and complicate the testing process.

You must react to exceptions somehow. For instance, you may add "assert(false)" at

least:

try {

...

}

catch (MyExcept &)

{

Page 427: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

assert(false);

}

Programmers sometimes use such constructs to return control from a number of

nested loops or recursive functions. But it is bad practice because exceptions are

very resource-intensive operations. They must be used according to their intended

purpose, i.e. for possible contingencies that must be handled on a higher level.

The only thing where you may simply suppress exceptions is destructors. A

destructor must not throw exceptions. But it is often not quite clear what to do with

exceptions in destructors and the exception handler might well remain empty. The

analyzer does not warn you about empty handlers inside destructors:

CClass::~ CClass()

{

try {

DangerousFreeResource();

}

catch (...) {

}

}

V566. The integer constant is converted to pointer. Possibly an error or a bad coding style.The analyzer detected an explicit conversion of a numerical value to the pointer

type. This warning is usually generated for code fragments where numbers are used

for flagging objects' states. Such methods are not necessarily errors but usually

signal a bad code design. Consider this sample:

const DWORD SHELL_VERSION = 0x4110400;

...

char *ptr = (char*) SHELL_VERSION;

Page 428: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

...

if (ptr == (char*) SHELL_VERSION)

The constant value which marks some special state is saved into the pointer. This

code might work well for a long time, but if an object is created by the address

0x4110400, we will not determine if this is a magic flag or just an object. If you

want to use a special flag, you'd better write it so:

const DWORD SHELL_VERSION = 0x4110400;

...

char *ptr = (char*)(&SHELL_VERSION);

...

if (ptr == (char*)(&SHELL_VERSION))

Note. To make false alarms fewer, the V566 message is not generated for a range of

cases. For instance, it does not appear if values -1, 0, 0xcccccccc and 0xdeadbeef

are magic numbers; if a number lies within the range between 0 and 65535 and is

cast to a string pointer. This enables us to skip correct code fragments like the

following one:

CString sMessage( (LPCSTR)IDS_FILE_WAS_CHANGED ) ;

This method of loading a string from resources is rather popular but certainly you'd

better use MAKEINTRESOURCE. There are some other exceptions as well.

V567. Undefined behavior. The variable is modified while being used twice between sequence points.The analyzer detected an expression leading to undefined behavior. A variable is

used several times between two sequence points while its value is changing. We

cannot predict the result of such an expression. Let's consider the notions

"undefined behavior" and "sequence point" in detail.

Page 429: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Undefined behavior is a feature of some programming languages — most famously

C/C++. In these languages, to simplify the specification and allow some flexibility

in implementation, the specification leaves the results of certain operations

specifically undefined.

For example, in C the use of any automatic variable before it has been initialized

yields undefined behavior, as do division by zero and indexing an array outside of

its defined bounds. This specifically frees the compiler to do whatever is easiest or

most efficient, should such a program be submitted. In general, any behavior

afterwards is also undefined. In particular, it is never required that the compiler

diagnose undefined behavior — therefore, programs invoking undefined behavior

may appear to compile and even run without errors at first, only to fail on another

system, or even on another date. When an instance of undefined behavior occurs, so

far as the language specification is concerned anything could happen, maybe

nothing at all.

A sequence point in imperative programming defines any point in a computer

program's execution at which it is guaranteed that all side effects of previous

evaluations will have been performed, and no side effects from subsequent

evaluations have yet been performed. They are often mentioned in reference to C

and C++, because the result of some expressions can depend on the order of

evaluation of their subexpressions. Adding one or more sequence points is one

method of ensuring a consistent result, because this restricts the possible orders of

evaluation.

Sequence points come into play when the same variable is modified more than once

within a single expression. An often-cited example is the expression i=i++, which

both assigns i to itself and increments i. The final value of i is ambiguous, because,

depending on the language semantics, the increment may occur before, after or

interleaved with the assignment. The definition of a particular language might

specify one of the possible behaviors or simply say the behavior is undefined. In C

and C++, evaluating such an expression yields undefined behavior.

C and C++ define the following sequence points:

Page 430: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

1. Between evaluation of the left and right operands of the && (logical AND), || (logical OR), and comma operators. For example, in the expression *p++ != 0 && *q++ != 0, all side effects of the sub-expression *p++ != 0 are completed before any attempt to access q.

2. Between the evaluation of the first operand of the ternary "question-mark" operator and the second or third operand. For example, in the expression a = (*p++) ? (*p++) : 0 there is a sequence point after the first *p++, meaning it has already been incremented by the time the second instance is executed.

3. At the end of a full expression. This category includes expression statements (such as the assignment a=b;), return statements, the controlling expressions of if, switch, while, or do-while statements, and all three expressions in a for statement.

4. Before a function is entered in a function call. The order in which the arguments are evaluated is not specified, but this sequence point means that all of their side effects are complete before the function is entered. In the expression f(i++) + g(j++) + h(k++), f is called with a parameter of the original value of i, but i is incremented before entering the body of f. Similarly, j and k are updated before entering g and h respectively. However, it is not specified in which order f(), g(), h() are executed, nor in which order i, j, k are incremented. The values of j and k in the body of f are therefore undefined.[3] Note that a function call f(a,b,c) is not a use of the comma operator and the order of evaluation for a, b, and c is unspecified.

5. At a function return, after the return value is copied into the calling context. (This sequence point is only specified in the C++ standard; it is present only implicitly in C[4].)

6. At the end of an initializer; for example, after the evaluation of 5 in the declaration int a = 5;.

7. In C++, overloaded operators act as functions, so a call of an overloaded operator is a sequence point.

Now let's consider several samples causing undefined behavior:

int i, j;

...

X[i]=++i;

X[i++] = i;

j = i + X[++i];

i = 6 + i++ + 2000;

j = i++ + ++i;

i = ++i + ++i;

Page 431: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

We cannot predict the calculation results in all these cases. Of course, these samples

are artificial and we can notice the danger right away. So let's examine a code

sample taken from a real application:

while (!(m_pBitArray[m_nCurrentBitIndex >> 5] &

Powers_of_Two_Reversed[m_nCurrentBitIndex++ & 31]))

{}

return (m_nCurrentBitIndex - BitInitial - 1);

The compiler can calculate either of the left or right arguments of the '&' operator

first. It means that the m_nCurrentBitIndex variable might be already incremented

by one when calculating "m_pBitArray[m_nCurrentBitIndex >> 5]". Or it might

still be not incremented.

This code may work well for a long time. However, you should keep in mind that it

will behave correctly only when it is built in some particular compiler version with

a fixed set of compilation options. This is the correct code:

while (!(m_pBitArray[m_nCurrentBitIndex >> 5] &

Powers_of_Two_Reversed[m_nCurrentBitIndex & 31]))

{ ++m_nCurrentBitIndex; }

return (m_nCurrentBitIndex - BitInitial);

This code does not contain ambiguities anymore. We also got rid of the magic

constant "-1".

Programmers often think that undefined behavior may occur only when using

postincrement, while preincrement is safe. It's not so. Further is an example from a

discussion on this subject.

Question:

I downloaded the trial version of your studio, ran it on my project and got this

warning: V567 Undefined behavior. The 'i_acc' variable is modified while being

used twice between sequence points.

The code

Page 432: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

i_acc = (++i_acc) % N_acc;

It seems to me that there is no undefined behavior because the i_acc variable does

not participate in the expression twice.

Answer:

There is undefined behavior here. It's another thing that the probability of error

occurrence is rather small in this case. The '=' operator is not a sequence point. It

means that the compiler might first put the value of the i_acc variable into the

register and then increment the value in the register. After that it calculates the

expression and writes the result into the i_acc variable and then again writes a

register with the incremented value into the same variable. As a result we will get a

code like this:

REG = i_acc;

REG++;

i_acc = (REG) % N_acc;

i_acc = REG;

The compiler has the absolute right to do so. Of course, in practice it will most

likely increment the variable's value at once, and everything will be calculated as

the programmer expects. But you should not rely on that.

Consider one more situation with function calls.

The order of calculating function arguments is not defined. If a variable changing

over time serves as arguments, the result will be unpredictable. This is unspecified

behavior. Consider this sample:

int A = 0;

Foo(A = 2, A);

The 'Foo' function may be called both with the arguments (2, 0) and with the

arguments (2, 2). The order in which the function arguments will be calculated

depends on the compiler and optimization settings.

References

Page 433: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

1. Wikipedia. Undefined behavior.

2. Wikipedia. Sequence point.

3. Klaus Kreft & Angelika Langer. Sequence Points and Expression Evaluation in C++.

4. Discussion at Bytes.com. Sequence points.

5. Discussion at StackOverflow.com. Why is a = (a+b) - (b=a) a bad choice for swapping two integers?

V568. It's odd that the argument of sizeof() operator is the expression.The analyzer detected a potential error: a suspicious expression serves as an

argument of the sizeof() operator. Suspicious expressions can be arranged in two

groups:

1. An expression attempts to change some variable.

The sizeof() operator calculates the expression's type and returns the size of this

type. But the expression itself is not calculated. Here is a sample of suspicious code:

int A;

...

size_t size = sizeof(A++);

This code does not increment the 'A' variable. If you need to increment 'A', you'd

better rewrite the code in the following way:

size_t size = sizeof(A);

A++;

2. Operations of addition, multiplication and the like are used in the expression.

Complex expressions signal errors. These errors are usually related to misprints. For

example:

SendDlgItemMessage(

hwndDlg, RULE_INPUT_1 + i, WM_GETTEXT,

Page 434: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

sizeof(buff - 1), (LPARAM) input_buff);

The programmer wrote "sizeof(buff - 1)" instead of "sizeof(buff) - 1". This is the

correct code:

SendDlgItemMessage(

hwndDlg, RULE_INPUT_1 + i, WM_GETTEXT,

sizeof(buff) - 1, (LPARAM) input_buff);

Here is another sample of a misprint in program text:

memset(tcmpt->stepsizes, 0,

sizeof(tcmpt->numstepsizes * sizeof(uint_fast16_t)));

The correct code:

memset(tcmpt->stepsizes, 0,

tcmpt->numstepsizes * sizeof(uint_fast16_t));

3. The argument of the sizeof() operator is a pointer to a class. In most cases this

shows that the programmer forgot to dereference the pointer.

Example:

class MyClass

{

public:

int a, b, c;

size_t getSize() const

{

return sizeof(this);

}

};

The getSize() method returns the size of the pointer, not of the object. Here is a

correct variant:

size_t getSize() const

{

Page 435: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

return sizeof(*this);

}

V569. Truncation of constant value.The analyzer detected a potential error: a constant value is truncated when it is

assigned into a variable. Consider this sample:

int A[100];

unsigned char N = sizeof(A);

The size of the 'A' array (in Win32/Win64) is 400 bytes. The value range for

unsigned char is 0..255. Consequently, the 'N' variable cannot store the size of the

'A' array.

The V569 warning tells you that you have chosen a wrong type to store this size or

that you actually intended to calculate the number of items in the array instead of

the array's size.

If you have chosen a wrong type, you may correct the code this way:

size_t N = sizeof(A);

If you intended to calculate the number of items in the array, you should rewrite the

code this way:

unsigned char N = sizeof(A) / sizeof(*A);

V570. The variable is assigned to itself.The analyzer detected a potential error: a variable is assigned to itself. Consider this

sample:

dst.m_a = src.m_a;

dst.m_b = dst.m_b;

Page 436: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The value of the 'dst.m_b' variable will not change because of the misprint. This is

the correct code:

dst.m_a = src.m_a;

dst.m_b = src.m_b;

The analyzer issues a warning not only for the copy assignment, but for the move

assignment too.

dst.m_a = std::move(src.m_a);

The analyzer does not produce the warning every time it detects assignment of a

variable to itself. For example, if the variables are enclosed in parentheses. This

method is often used to suppress compiler-generated warnings. For example:

int Foo(int foo)

{

UNREFERENCED_PARAMETER(foo);

return 1;

}

The UNREFERENCED_PARAMETER macro is defined in the WinNT.h file in the

following way:

#define UNREFERENCED_PARAMETER(P) \

{ \

(P) = (P); \

}

The analyzer knows about such cases and will not generate the V570 warning on

assignment like this:

(foo) = (foo);

Note. If V570 warning shows on macro that should not be changed, it is possible to

use macro suppression mechanism. Special comment in the file that is used in the

whole project (for instance, StdAfx.h file) may be enough for that. Example:

Page 437: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

//-V:MY_MACROS:V570

V571. Recurring check. This condition was already verified in previous line.The analyzer detected a potential error: one and the same condition is checked

twice. Consider two samples:

// Example N1:

if (A == B)

{

if (A == B)

...

}

// Example N2:

if (A == B) {

} else {

if (A == B)

...

}

In the first case, the second check "if (A==B)" is always true. In the second case,

the second check is always false.

It is highly probable that this code has an error. For instance, a wrong variable name

is used because of a misprint. This is the correct code:

// Example N1:

if (A == B)

{

if (A == C)

...

}

Page 438: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

// Example N2:

if (A == B) {

} else {

if (A == C)

...

}

V572. It is odd that the object which was created using 'new' operator is immediately cast to another type.The analyzer detected a potential error: an object created by the 'new' operator is

explicitly cast to a different type. For example:

T_A *p = (T_A *)(new T_B());

...

delete p;

There are three possible ways of how this code has appeared and what to do with it.

1) T_B was not inherited from the T_A class.

Most probable, it is an unfortunate misprint or crude error. The way of correcting it

depends upon the purpose of the code.

2) T_B is inherited from the T_A class. The T_A class does not have a virtual

destructor.

In this case you cannot cast T_B to T_A because you will not be able to correctly

destroy the created object then. This is the correct code:

T_B *p = new T_B();

...

delete p;

Page 439: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

3) T_B is inherited from the T_A class. The T_A class has a virtual destructor.

In this case the code is correct but the explicit type conversion is meaningless. We

can write it in a simpler way:

T_A *p = new T_B();

...

delete p;

There can be other cases when the V572 warning is generated. Let's consider a code

sample taken from a real application:

DWORD CCompRemoteDriver::Open(HDRVR,

char *, LPVIDEO_OPEN_PARMS)

{

return (DWORD)new CCompRemote();

}

The program handles the pointer as a descriptor for its purposes. To do that, it

explicitly converts the pointer to the DWORD type. This code will work correctly

in 32-bit systems but might fail in a 64-bit program. You may avoid the 64-bit

error using a more suitable data type DWORD_PTR:

DWORD_PTR CCompRemoteDriver::Open(HDRVR,

char *, LPVIDEO_OPEN_PARMS)

{

return (DWORD_PTR)new CCompRemote();

}

Sometimes the V572 warning may be aroused by an atavism remaining since the

time when the code was written in C. Let's consider such a sample:

struct Joint {

...

};

joints=(Joint*)new Joint[n]; //malloc(sizeof(Joint)*n);

Page 440: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The comment tells us that the 'malloc' function was used earlier to allocate memory.

Now it is the 'new' operator which is used for this purpose. But the programmers

forgot to remove the type conversion. The code is correct but the type conversion is

needless here. We may write a shorter code:

joints = new Joint[n];

V573. Uninitialized variable 'Foo' was used. The variable was used to initialize itself.The analyzer detected a potential error: a variable being declared is used to initialize

itself. Let's consider a simple synthetic sample:

int X = X + 1;

The X variable will be initialized by a random value. Of course, this sample is

farfetched yet it is simple and good to show the warning's meaning. In practice,

such an error might occur in more complex expressions. Consider this sample:

void Class::Foo(const std::string &FileName)

{

if (FileName.empty())

return;

std::string FullName = m_Dir + std::string("\\") + FullName;

...

}

Because of the misprint in the expression, it is the FullName name which is used

instead of FileName. This is the correct code:

std::string FullName = m_Dir + std::string("\\") + FileName;

Page 441: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V574. The pointer is used simultaneously as an array and as a pointer to single object.The analyzer detected a potential error: a variable is used simultaneously as a

pointer to a single object and as an array. Let's study a sample of the error the

analyzer has found in itself:

TypeInfo *factArgumentsTypeInfo =

new (GC_QuickAlloc) TypeInfo[factArgumentsCount];

for (size_t i = 0; i != factArgumentsCount; ++i)

{

Typeof(factArguments[i], factArgumentsTypeInfo[i]);

factArgumentsTypeInfo->Normalize();

}

It is suspicious that we handle the factArgumentsTypeInfo variable as the

"factArgumentsTypeInfo[i]" array and as a pointer to the single object

"factArgumentsTypeInfo ->". Actually we should call the Normalize() function for

all the items. This is the fixed code:

TypeInfo *factArgumentsTypeInfo =

new (GC_QuickAlloc) TypeInfo[factArgumentsCount];

for (size_t i = 0; i != factArgumentsCount; ++i)

{

Typeof(factArguments[i], factArgumentsTypeInfo[i]);

factArgumentsTypeInfo[i].Normalize();

}

V575. Function receives an odd argument.The analyzer found a potential error: the function receives a very odd value as an

actual argument.

Page 442: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Consider the sample:

bool Matrix4::operator==(const Matrix4& other) const {

if (memcmp(this, &other, sizeof(Matrix4) == 0))

return true;

...

We deal with a misprint here: one round bracket is in a wrong place. Unfortunately,

this error is not clearly visible and might exist in the code for a long time. Because

of this misprint the size of memory being compared is calculated with the

"sizeof(Matrix4) == 0" expression. Since the result of the expression is 'false', 0

bytes of memory are compared. This is the fixed code:

bool Matrix4::operator==(const Matrix4& other) const {

if (memcmp(this, &other, sizeof(Matrix4)) == 0)

return true;

...

Note. NULL is odd argument.

Sometimes programmers use constructs like the one below to calculate the amount

of memory to be allocated for a buffer:

const char* format = getLocalizedString(id, resource);

int len = ::vsprintf(NULL, format, args);

char* buf = (char*) alloca(len);

::vsprintf(buf, format, args);

But one should keep in mind that the call ::vsprintf(NULL, format, args) is

incorrect. Here's what MSDN has to say about it:

int vsprintf(*buffer, char *format, va_list argptr);

....

vsprintf and vswprintf return the number of characters written, not including the

terminating null character, or a negative value if an output error occurs. If buffer or

format is a null pointer, these functions invoke the invalid parameter handler, as

Page 443: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

described in Parameter Validation. If execution is allowed to continue, these

functions return -1 and set errno to EINVAL.

V576. Incorrect format. Consider checking the N actual argument of the 'Foo' function.The analyzer has detected a potential issue with the application of formatted output

functions. (printf, sprintf, wprintf etc.) The formatting string doesn't correspond

with actual arguments passed into the function. Let's review a simple example:

int A = 10;

double B = 20.0;

printf("%i %i\n", A, B);

According to the formatting string the 'printf' function is expecting two actual

arguments of the 'int' type. However the second argument's value is of the 'double'

type. Such an inconsistency leads to undefined behavior of a program. For example,

it can lead to the output of senseless values.

The correct version:

int A = 10;

double B = 20.0;

printf("%i %f\n", A, B);

It's possible to cite countless examples of 'printf' function's incorrect use. Let's

review some of the typical examples that are the most frequently encountered in

applications.

Address printout.

The value of a pointer is quite commonly printed using these lines:

int *ptr = new int[100];

printf("0x%0.8X\n", ptr);

Page 444: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

This source code is invalid because it will function properly only on systems which

have their pointer size equal to size of 'int' type. For example In Win64 this code

will print only the low-order part of the 'ptr' pointer. The correct version:

int *ptr = new int[100];

printf("0x%p\n", ptr);

The analyzer has detected the potential issue with an odd value being passed as the

function's actual argument.

Unused arguments.

You can often encounter function calls in which some of these function's arguments

are being unused.

For instance:

int nDOW;

#define KEY_ENABLED "Enabled"

...

wsprintf(cDowKey, L"EnableDOW%d", nDOW, KEY_ENABLED);

It is obvious that the KEY_ENABLED parameter is unnecessary here or the source

code should look like this:

wsprintf(cDowKey, L"EnableDOW%d%s", nDOW, KEY_ENABLED);

Insufficient number of arguments.

A little more dangerous is the situation in which the number of arguments passed to

the function is less than necessary. This can easily lead to the memory access error,

buffer overflow or senseless printout. Let's review an example of memory allocation

function taken from a real life application:

char* salloc(register int nbytes)

{

register char* p;

p = (char*) malloc((unsigned)nbytes);

Page 445: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

if (p == (char *)NULL)

{

fprintf(stderr, "%s: out of memory\n");

exit(1);

}

return (p);

}

If 'malloc' returns NULL, the program will not be able to report the shortage of

memory and to be terminated correctly. It instead will be terminated emergently and

it will output the senseless text. In any case such a behavior will complicate analysis

of the program's inoperability.

Confusion with signed/unsigned

Developers often employ the character printing specificator ('%i' for example) to

output the variables of unsigned type. And vice versa. This error usually is not

critical and is encountered so often than it has a low priority in analyzer. In many

cases such source code works flawlessly and fails only with large or negative

values. Let us examine the code which is not correct, but successfully works:

int A = 10;

printf("A = %u\n", A);

for (unsigned i = 0; i != 5; ++i)

printf("i = %d\n", i);

Although there is an inconsistency here, this code outputs correct values in practice.

Of course it's better not to do this and to write correctly:

int A = 10;

printf("A = %d\n", A);

for (unsigned i = 0; i != 5; ++i)

printf("i = %u\n", i);

The error will manifest itself in case there are large or negative values in the

program. An Example:

Page 446: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

int A = -1;

printf("A = %u", A);

Instead of "A=-1" string the program will print "A=4294967295". The correct

version:

printf("A = %i", A);

Wide character strings

Visual Studio has one displeasing feature when it interprets the string format in a

non-standard way to print wide characters. Therefore, the analyzer can diagnose

errors in code like the following sample:

const wchar_t *p = L"abcdef";

wprintf(L"%S", p);

In Visual C++, "S" is meant to be used to print a string of the "const char *" type, so

from its viewpoint, the correct version of the code above should look like this:

wprintf(L"%s", p);

Starting with Visual Studio 2015, the developers offer a solution to this issue for the

sake of compatibility. To make your code compatible with ISO C (C99), you need

to specify the _CRT_STDIO_ISO_WIDE_SPECIFIERS macro for the

preprocessor.

In that case, the code:

const wchar_t *p = L"abcdef";

wprintf(L"%S", p);

will be treated as correct.

PVS-Studio knows about the _CRT_STDIO_ISO_WIDE_SPECIFIERS macro and

takes it into account when performing the analysis.

Page 447: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

By the way, if you have the ISO C compatibility mode enabled (i.e. declared the

_CRT_STDIO_ISO_WIDE_SPECIFIERS macro), you can restore the old-type

conversion in certain places by using the "%Ts" format specifier.

This story with wide characters is quite complicated and is outside the scope of this

documentation. To figure it all out, see the following resources:

Bug 1121290 - distinguish specifier s and ls in the printf family of functions

MBCS to Unicode conversion in swprintf

Visual Studio swprintf is making all my %s formatters want wchar_t * instead of char *

Additional features

It is possible to point to the names of user-defined functions whose format should

be checked. It is assumed that formatting principle is equal to the one of printf()

function.

User should write a comment of special kind near function prototype (or near its

implementation, or in standard header file). Let start with the usage example:

//+V576, function:Mylog, format_arg:1, ellipsis_arg:2

Mylog("%f", time(NULL)); // warning V576

Format:

"function", "class" and "namespace" keys determines function name, class name (if it's required to analyze only methods of some class) and namespace name (if it's required to analyze only functions or class members of some namespace).

"format_arg" key determines number of function argument that contains format string. This argument is necessary. Numbers counts from one, not from zero, and should not exceed 14.

"ellipsis_arg" key determines number of function argument with ellipsis (three dots). This number is bound by the same restrictions as the one given by format_arg key. In addition, ellipsis_arg number should be greater than format_arg (because ellipsis can only be the last argument). This key is also nessesary.

At last, here is full usage example:

Page 448: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

// Warn when in C method of class B from A namespace

// arguments, counting from third one, does not

// correspond to the format line in the second argument

//+V576,namespace:A,class:B,function:C,format_arg:2,ellipsis_arg:3

Additional reference:

1. Wikipedia. Printf.

2. MSDN. Format Specification Fields: printf and wprintf Functions.

V577. Label is present inside a switch(). It is possible that these are misprints and 'default:' operator should be used instead.The analyzer detected a potential error inside the switch operator. A label is used

whose name is similar to 'default'. A misprint is probable. Consider this sample:

int c = 10;

int r = 0;

switch(c){

case 1:

r = 3; break;

case 2:

r = 7; break;

defalt:

r = 8; break;

}

It seems that after the code's work is done, the value of the 'r' variable will be 8.

Actually the 'r' variable will still equal zero. The point is that "defalt" is a label, not

the "default" operator. This is the correct code:

int c = 10;

int r = 0;

Page 449: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

switch(c){

case 1:

r = 3; break;

case 2:

r = 7; break;

default:

r = 8; break;

}

V578. An odd bitwise operation detected. Consider verifying it.The analyzer detected a potential error in an expression handling bits. A part of the

expression is meaningless or excessive. Usually such errors occur due to a misprint.

Consider this sample:

if (up & (PARAMETER_DPDU | PARAMETER_DPDU | PARAMETER_NG))

The PARAMETER_DPDU constant is used twice here. In a correct code there must

be two different constants: PARAMETER_DPDU and PARAMETER_DPDV. The

letter 'U' resembles 'V' and that is why this misprint has occurred. This is the correct

code:

if (up & (PARAMETER_DPDU | PARAMETER_DPDV | PARAMETER_NG))

Another example. There is no error here but the code is excessive:

if (((pfds[i].dwFlags & pPFD->dwFlags) & pPFD->dwFlags)

!= pPFD->dwFlags)

This is a shorter code:

if ((pfds[i].dwFlags & pPFD->dwFlags) != pPFD->dwFlags)

This diagnostic also generates a warning when the label name begins with "case". A

space character is most probably missing. For example, the label "case1:" should be

written as "case 1:".

Page 450: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V579. The 'Foo' function receives the pointer and its size as arguments. It is possibly a mistake. Inspect the N argument.The analyzer detected an odd function call in code. A pointer and the size of the

pointer are passed into a function as its arguments. Actually it is a common case

when developers want to pass a buffer size instead of a pointer size into a function.

Let's see how an error like that can appear in code. Assume we had the following

code in the beginning:

char buf[100];

...

memset(buf, 0, sizeof(buf));

The code is correct. The memset() function clears an array of 100 bytes. Then the

code was changed and the buffer became variable-sized. The programmer forgot to

change the code of buffer clearing:

char *buf = new char[N];

...

memset(buf, 0, sizeof(buf));

Now the code is incorrect. The sizeof() operator returns the pointer size instead of

the size of the buffer with data. As a result, the memset() function clears only part

of the array.

Let's consider another sample taken from a real application:

apr_size_t ap_regerror(int errcode,

const ap_regex_t *preg, char *errbuf,

apr_size_t errbuf_size)

{

...

Page 451: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

apr_snprintf(errbuf, sizeof errbuf,

"%s%s%-6d", message, addmessage,

(int)preg->re_erroffset);

...

}

It is not easy to notice the error in this code. The apr_snprintf() function accepts the

'errbuf' pointer and the size of this pointer 'sizeof errbuf' as arguments. The analyzer

considers this code odd and is absolutely right. The buffer size is stored in the

'errbuf_size' variable and it is this variable that should be used. This is the correct

code:

apr_snprintf(errbuf, errbuf_size,

"%s%s%-6d", message, addmessage,

(int)preg->re_erroffset);

V580. An odd explicit type casting. Consider verifying it.The analyzer detected an odd explicit type conversion. It may be either an error or a

potential error.

Consider this sample:

DWORD errCode = 0;

void* dwErrParams[MAX_MESSAGE_PARAMS];

dwErrParams[0] = *((void**)&errCode);

The code contains a 64-bit error. The 'DWORD' type is cast to 'void *' type. This

code works incorrectly in 64-bit systems where the pointer's size does not coincide

with the size of the DWORD type. This is the correct code:

DWORD_PTR errCode = 0;

void* dwErrParams[MAX_MESSAGE_PARAMS];

dwErrParams[0] = (void *)errCode;

Page 452: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V581. The conditional expressions of the 'if' statements situated alongside each other are identical.The analyzer detected code where there are two 'if' operators with identical close to

each other. This is either a potential error or excessive code.

Consider the following sample:

if (strlen(S_1) == SIZE)

Foo(A);

if (strlen(S_1) == SIZE)

Foo(B);

Whether this code contains an error or not, depends upon what exactly the

programmer intended to do. If the second condition must calculate the length of the

other string, then it is an error. This is the correct code:

if (strlen(S_1) == SIZE)

Foo(A);

if (strlen(S_2) == SIZE)

Foo(B);

Maybe the code is correct, but it is inefficient in this case because it has to calculate

the length of one and the same string twice. This is the optimized code:

if (strlen(S_1) == SIZE) {

Foo(A);

Foo(B);

}

V582. Consider reviewing the source code which operates the container.

Page 453: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer detected a potential error related to handling a fixed-sized container.

One of our users advised us to implement this diagnostic. This is how he has

formulated the task.

In order to handle arrays of a fixed size, we use the following template class:

template<class T_, int numElements > class idArray

{

public:

int Num() const { return numElements; };

.....

inline const T_ & operator[]( int index ) const {

idassert( index >= 0 );

idassert( index < numElements );

return ptr[index];

};

inline T_ & operator[]( int index ) {

idassert( index >= 0 );

idassert( index < numElements );

return ptr[index];

};

private:

T_ ptr[numElements];

};

It has no performance overhead in release builds, but does index range checking in

debug builds. Here is an example of incorrect code:

idArray<int, 1024> newArray;

newArray[-1] = 0;

newArray[1024] = 0;

The errors will be detected on launching the debug version. But we would like to be

able to detect such errors using static analysis at the compilation time.

It is this type of issues that the V582 diagnostic is intended to detect. If a class is

used in a program that makes use of a fixed-sized container's functionality, the

Page 454: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

analyzer tries to make sure that the index does not go beyond its boundaries. Here

are examples of this diagnostic:

idArray<float, 16> ArrA;

idArray<float, 8> ArrB;

for (size_t i = 0; i != 16; i++)

ArrA[i] = 1.0f;

for (size_t i = 0; i != 16; i++)

ArrB[i] = 1.0f;

The analyzer will generate the following message on this code:

V582 Consider reviewing the source code which operates the 'ArrB' container. The

value of the index belongs to the range: [0..15].

The error here is that the both loops handle 16 items, although the second array

contains only 8 items. This is the correct code:

for (size_t i = 0; i != 16; i++)

ArrA[i] = 1.0f;

for (size_t i = 0; i != 8; i++)

ArrB[i] = 1.0f;

Note that passing of too large or too small indexes does not necessarily indicate an

error in the program. For instance, the [] operator can be implemented in the

following way:

inline T_ & operator[]( int index ) {

if (index < 0) index = 0;

if (index >= numElements) index = numElements - 1;

return ptr[index];

};

If you use such classes and get too many false reports, you should turn off the V582

diagnostic.

Note. The analyzer does not possess an AI and its capabilities of searching for

defects when handling containers are limited. We are working on improving the

Page 455: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

algorithms, so if you have noticed obviously false reports or, on the contrary, cases

when the analyzer does not generate the warning, please write to us and send us the

corresponding code sample.

V583. The '?:' operator, regardless of its conditional expression, always returns one and the same value.Analyzer found a potential error with utilization of "?:" ternary operator. Regardless

of its conditional expression, the same operation will be performed. It is quite

possible that a misprint is present in the source code.

Let's review the most basic example:

int A = B ? C : C;

In all cases the value of C variable will be assigned to the A variable.

Let's review how such a mistake could appear in the source code of real-life

application:

fovRadius[0] =

tan(DEG2RAD((rollAngleClamped % 2 == 0 ?

cg.refdef.fov_x : cg.refdef.fov_x) * 0.52)) * sdist;

The code here is formatted. In the program's sources this is a single line of code and

it is not surprising that the misprint could be overlooked quite easily. The essence of

an error is that the member of the "fov_x" structure is used twice.

The correct code:

fovRadius[0] =

tan(DEG2RAD((rollAngleClamped % 2 == 0 ?

cg.refdef.fov_x : cg.refdef.fov_y) * 0.52)) * sdist;

Page 456: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V584. The same value is present on both sides of the operator. The expression is incorrect or it can be simplified.Analyzer found an expression that can be simplified. The possibility of a misprint

presence in it is quite high. Let's review an example:

float SizeZ;

if (SizeZ + 1 < SizeZ)

The analyzer thinks that this condition contains a mistake because it is practically

senseless. Most likely another check was implied. The correct variant:

if (SizeZ + 1 < maxSizeZ)

Of course programmers sometimes utilize some tricks which are formally correct

but do appear quite odd. The analyzer tries to detect such situations if possible and

not to produce warnings. For instance the analyzer considers such checks as being

safe:

//overflow test for summation

int a, b;

if (a + b < a)

//Verifying that x does not equals zero, +infinity, -infinity

double X;

if (X * 0.5f != X)

V585. An attempt to release the memory in which the 'Foo' local variable is stored.

Page 457: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Analyzer detected an attempt to release the memory occupied by the local variable.

Such errors could be produced in case of careless refactoring or as misprints.

Let's review an example of the incorrect code:

void Foo()

{

int *p;

...

free(&p);

}

The corrected code:

void Foo()

{

int *p;

...

free(p);

}

V586. The 'Foo' function is called twice for deallocation of the same resource.Analyzer detected a potential error of recurrent resource deallocation. The resource

mentioned could be a memory space, some file or, for example, an HBRUSH

object.

Let's review the example of incorrect code:

float *p1 = (float *)malloc(N * sizeof(float));

float *p2 = (float *)malloc(K * sizeof(float));

...

free(p1);

free(p1);

Page 458: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

There is a misprint in application's source code which causes the double

deallocation of same memory space. It is hard to predict the consequences of such

code's execution. It's possible that such a program would crash. Or it will continue

its execution but memory leak will occur.

The correct example:

float *p1 = (float *)malloc(N * sizeof(float));

float *p2 = (float *)malloc(K * sizeof(float));

...

free(p1);

free(p2);

Sometimes an error of double resource deallocation is not a dangerous one:

vector<unsigned> m_arrStack;

...

m_arrStack.clear();

m_arrBlock.clear();

m_arrStack.clear();

Accidently the array is emptied twice. The code operates correctly but still it should

be reviewed and corrected. During its study, it could be discovered that another

array dissaalocation should have been performed nevertheless.

The correct example:

vector<unsigned> m_arrStack;

...

m_arrStack.clear();

m_arrBlock.clear();

V587. An odd sequence of assignments of this kind: A = B; B = A;.

Page 459: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Analyzer detected a potential error concerning the senseless mutual assignment of

variables.

Let's review an example:

int A, B, C;

...

A = B;

C = 10;

B = A;

Here the assignment "B=A" lacks any sort of practical utility. It is possibly a

misprint or just an unnecessary operation. The correct code:

A = B;

C = 10;

B = A_2;

An example stated above is a synthetic one. Let's see how such an error could

appear in the source code of a real-life application:

// Swap; exercises counters

{

RCPFooRef temp = f2;

f2 = f3;

f3 = f2;

}

The correct code:

// Swap; exercises counters

{

RCPFooRef temp = f2;

f2 = f3;

f3 = temp;

}

Page 460: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V588. The expression of the 'A =+ B' kind is utilized. Consider reviewing it, as it is possible that 'A += B' was meant.The analyzer detected a potential error: there is a sequence of '=+' characters. It

might be a misprint and you should use the '+=' operator.

Consider the following example:

size_t size, delta;

...

size=+delta;

This code may be correct, but it is highly probable that there is a misprint and the

programmer actually intended to use the '+=' operator. This is the fixed code:

size_t size, delta;

...

size+=delta;

If this code is correct, you may remove '+' or type in an additional space to prevent

showing the V588 warning. The following is an example of correct code where the

warning is not generated:

size = delta;

size = +delta;

Note. To search for misprints of the 'A =- B' kind, we use the V589 diagnostic rule.

This check is implemented separately since a lot of false reports are probable and

you may want to disable it.

V589. The expression of the 'A =- B' kind is utilized. Consider reviewing

Page 461: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

it, as it is possible that 'A -= B' was meant.The analyzer detected a potential error: there is a sequence of '=-' characters in code.

It might be a misprint and you should use the '-=' operator.

Consider this sample:

size_t size, delta;

...

size =- delta;

This code may be correct, but it is highly probable that there is a misprint and the

programmer actually intended to use the '-=' operator. This is the fixed code:

size_t size, delta;

...

size -= delta;

If the code is correct, you may type in an additional space between the characters '='

and '-' to remove the V589 warning. This is an example of correct code where the

warning is not generated:

size = -delta;

To make false reports fewer, there are some specific exceptions to the V589 rule.

For instance, the analyzer will not generate the warning if a programmer does not

use spaces between variables and operators. Here you are some samples of code the

analyzer considers safe:

A=-B;

int Z =- 1;

N =- N;

Note. To search for misprints of the 'A =+ B' type, the V588 diagnostic check is

used.

Page 462: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V590. Consider inspecting this expression. The expression is excessive or contains a misprint.The analyzer detected a potential error: there is an excessive comparison in code.

Let me explain this by a simple example:

if (Aa == 10 && Aa != 3)

The condition will hold if 'Aa == 10'. The second part of the expression is

meaningless. On studying the code, you may come to one of the two conclusions:

1) The expression can be simplified. This is the fixed code:

if (Aa == 10)

2) The expression has an error. This is the fixed code:

if (Aa == 10 && Bb != 3)

Here is an example of how this error may look in a real application:

int appliedSize, appliedSign;

...

if(appliedSize == 'b' && appliedSize != 's' && ...)

...

The expression has a misprint which is the reason why the appliedSize variable is

used twice while appliedSign is not used at all. This is the fixed code:

int appliedSize, appliedSign;

...

if(appliedSize == 'b' && appliedSign != 's' && ...)

...

Let's study another example from practice. We have no error here, but the

expression is excessive, which might make the code less readable:

Page 463: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

while (*pBuff == ' ' && *pBuff != '\0')

pBuff++;

The " *pBuff != '\0' " check is meaningless. This is the shortened code:

while (*pBuff == ' ')

pBuff++;

V591. Non-void function should return a value.The analyzer detected a function that returns a random value. It might be an error.

Consider this sample:

int main (int argc, char** argv)

{

...

printf("FINISH\r\n");

}

The main() function returns an integer number which is accepted by the calling

process. If main() does not return a value explicitly, the calling process gets a

nominally undefined value. This is the correct code:

int main (int argc, char** argv)

{

...

printf("FINISH\r\n");

return retCode;

}

A more interesting and dangerous case is when we deal with code of functions

where an undefined value is returned only sometimes. Consider the following

sample:

BOOL IsInterestingString(char *s)

Page 464: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

{

if (s == NULL)

return FALSE;

if (strlen(s) < 4)

return;

return (s[0] == '#') ? TRUE : FALSE;

}

There is a misprint in the code. If a string's length is less than 4 characters, the

function will return an undefined value. This is the correct code:

BOOL IsInterestingString(char *s)

{

if (s == NULL)

return FALSE;

if (strlen(s) < 4)

return FALSE;

return (s[0] == '#') ? TRUE : FALSE;

}

Note. The analyzer tries to determine cases when absence of a returned value is not

an error. Here is an example of code analyzer will consider safe:

int Foo()

{

...

exit(10);

}

V592. The expression was enclosed by parentheses twice: ((expression)). One pair of parentheses is unnecessary or misprint is present.

Page 465: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer detected double parentheses enclosing an expression. It is probable

that one of the brackets is in a wrong place.

Note that the analyzer does not search for code fragments where parentheses are

used twice. For instance, the analyzer considers the check "if ((A = B))" safe.

Additional parentheses are used here to suppress warnings of some compilers. You

cannot arrange parentheses in this expression so that an error occurs.

The analyzer tries to find cases when you may change an expression's meaning by

changing a location of one bracket. Consider the following sample:

if((!osx||howmanylevels))

This code is suspicious. The purpose of additional parentheses here is not clear.

Perhaps the expression should look this way:

if(!(osx||howmanylevels))

Even if the expression is correct, we still should remove the additional parentheses.

There are two reasons for that.

1) A person reading the code may doubt that it is correct on seeing double

parentheses.

2) If you remove additional parentheses, the analyzer will stop generating a false

report.

V593. Consider reviewing the expression of the 'A = B == C' kind. The expression is calculated as following: 'A = (B == C)'.The analyzer detected a potential error in an expression that is most probably

working in a way other than intended by the programmer. Most often you may see

errors of this type in expressions where an assignment operation and operation of

Page 466: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

checking a function's result are performed simultaneously. Consider a simple

example:

if (handle = Foo() != -1)

While creating this code, the programmer usually wants the actions to be performed

in the following order:

if ((handle = Foo()) != -1)

But the priority of the '!=' operator is higher than that of the '=' operator. That is why

the expression will be calculated in the following way:

if (handle = (Foo() != -1))

To fix the error, you may use parentheses, or rather not be stingy with code lines.

Your program's text will become more readable if you write it this way:

handle = Foo();

if (handle != -1)

Let's see how such an error might look in a real application:

if (hr = AVIFileGetStream(pfileSilence,

&paviSilence, typeAUDIO, 0) != AVIERR_OK)

{

ErrMsg("Unable to load silence stream");

return hr;

}

The check in the code where the error has occurred works correctly and we will get

the message "Unable to load silence stream". The trouble is that the 'hr' variable will

store value 1 and not the error's code. This is the fixed code:

if ((hr = AVIFileGetStream(pfileSilence,

&paviSilence, typeAUDIO, 0)) != AVIERR_OK)

{

ErrMsg("Unable to load silence stream");

Page 467: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

return hr;

}

The analyzer does not always generate warnings on detecting a construct of the "if

(x = a == b)" kind. For instance, the analyzer understands that the following code is

safe:

char *from;

char *to;

bool result;

...

if (result = from == to)

{}

Note. If the analyzer still generates a false alarm, you may use two methods to

suppress it:

1) Add one more pair of parentheses. For example: "if (x = (a == b))".

2) Use a comment to suppress the warning. For example: "if (x = a == b) //-V593".

V594. The pointer steps out of array's bounds.The analyzer has detected a potential error of pointer handling. There is an

expression in the program, on calculating which a pointer leaves array bounds. Here

is a simple example to clarify this point:

int A[10];

fill(A, A + sizeof(A), 33);

We want all the array items to be assigned value 33. The error is this: the "A +

sizeof(A)" pointer points far outside the array's bounds. As a result, we will change

more memory cells than intended. A result of such an error is unpredictable.

This is the correct code:

Page 468: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

int A[10];

fill(A, A + sizeof(A) / sizeof(A[0]), 33);

V595. The pointer was utilized before it was verified against nullptr. Check lines: N1, N2.The analyzer has detected a potential error that may cause dereferencing of a null

pointer.

The analyzer has noticed the following situation in the code: a pointer is being used

first and only then it is checked whether or not this is a NULL pointer. It means one

of the two things:

1) An error occurs if the pointer is equal to NULL.

2) The program works correctly, since the pointer is never equal to NULL. The

check is not necessary in this case.

Let's consider the first case. There is an error.

buf = Foo();

pos = buf->pos;

if (!buf) return -1;

If the 'buf' pointer is equal to NULL, the 'buf->pos ' expression will cause an error.

The analyzer will generate a warning for this code mentioning 2 lines: the first line

is the place where the pointer is used; the second line is the place where the pointer

is compared to NULL.

This is the correct code:

buf = Foo();

if (!buf) return -1;

pos = buf->pos;

Page 469: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Let's consider the second case. There is no error.

void F(MyClass *p)

{

if (!IsOkPtr(p))

return;

printf("%s", p->Foo());

if (p) p->Clear();

}

This code is always correct. The pointer is never equal to NULL. But the analyzer

does not understand this situation and generates a warning. To make it disappear,

you should remove the check "if (p)". It has no sense and can only confuse a

programmer reading this code.

This is the correct code:

void F(MyClass *p)

{

if (!IsOkPtr(p))

return;

printf("%s", p->Foo());

p->Clear();

}

When the analyzer is mistaken, you may use (apart from changing the code) a

comment to suppress warnings. For example: "p->Foo(); //-V595".

Note N1.

Some users report that the analyzer generates the V595 warning on correct code like

in the following sample:

static int Foo(int *dst, int *src)

{

*dst = *src; // V595 !

if (src == 0)

return 0;

Page 470: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

return Foo(dst, src);

}

...

int a = 1, b = 2;

int c = Foo(&a, &b);

Yes, analyzer produces a false-positive warning here. The code is correct and the

'src' pointer cannot be equal to NULL at the moment when assignment "*dst = *src"

is performed. Perhaps we will implement an exception for such cases in future but

we won't hurry. Though there is no error, the analyzer has detected a surplus code:

the function can be shortened and the V595 warning will stop appearing, while the

code will become simpler.

This is the better code:

int Foo(int *dst, int *src)

{

assert(dst && src);

*dst = *src;

return Foo(dst, src);

}

Note N2.

Sometimes programmers write code like this:

int *x=&p->m_x; //V595

if (p==NULL) return(OV_EINVAL);

In this fragment, a pointer to a class member is calculated. This pointer is not

dereferenced and one may find it strange that the analyzer generates the V595

warning here. But this code actually leads to undefined behavior. It's only sheer luck

that the program works properly. One can't calculate the "&p->m_x" expression if

the 'p' pointer is null.

A similar issue may occur when sorting an array:

int array[10];

Page 471: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

std::sort(&array[0], &array[10]); // Undefined behavior

&array[10] will cause undefined behavior as the array[10] item lies outside the

array boundaries. However, it is legal to use pointer arithmetic: you can use a

pointer addressing the last array item. So the fixed code may look like this:

int array[10];

std::sort(array, array+10); //ok

Related materials1. Andrey Karpov. Explanation on Diagnostic

V595. http://www.viva64.com/en/b/0353/

V596. The object was created but it is not being used. The 'throw' keyword could be missing.The analyzer has detected a strange use of the std::exception class or derived class.

The analyzer generates this warning when an object of the std::exception /

CException type is created but not being used. For example:

if (name.empty())

std::logic_error("Name mustn't be empty");

The error is this: the key word 'throw' is missing by accident. As a result, this code

does not generate an exception in case of an error. This is the fixed code:

if (name.empty())

throw std::logic_error("Name mustn't be empty");

V597. The compiler could delete the 'memset' function call, which is used to flush 'Foo' buffer. The

Page 472: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

RtlSecureZeroMemory() function should be used to erase the private data.The analyzer has detected a potential error: an array containing private information

is not cleared.

Consider the following code sample.

void Foo()

{

char password[MAX_PASSWORD_LEN];

InputPassword(password);

ProcessPassword(password);

memset(password, 0, sizeof(password));

}

The function on the stack creates a temporary buffer intended for password storage.

When we finish working with the password, we want to clear this buffer. If you

don't do this, the password will remain in memory, which might lead to unpleasant

consequences. Article about this: "Overwriting memory - why?".

Unfortunately, the code above may leave the buffer uncleared. Note that the

'password' array is cleared at the end and is not used anymore. That's why when

building the Release version of the application, the compiler will most likely delete

the call of the memset() function. The compiler has an absolute right to do that. This

change does not affect the observed behavior which is described in the Standard as

a sequence of calls of input-output functions and volatile data read-write functions.

That is, from the viewpoint of the C/C++ language removing the call of the

memset() function does not change anything!

To clear buffers containing private information you should use a special

function RtlSecureZeroMemory() or memset_s() (see also "Safe Clearing of Private

Data").

Page 473: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

This is the fixed code:

void Foo()

{

char password[MAX_PASSWORD_LEN];

InputPassword(password);

ProcessPassword(password);

RtlSecureZeroMemory(password, sizeof(password));

}

It seems that in practice the compiler cannot delete a call of such an important

function as memset(). You might think that we speak of some exotic compilers. It's

not so. Take the Visual C++ 10 compiler included into Visual Studio 2010, for

instance.

Let's consider the two functions.

void F1()

{

TCHAR buf[100];

_stprintf(buf, _T("Test: %d"), 123);

MessageBox(NULL, buf, NULL, MB_OK);

memset(buf, 0, sizeof(buf));

}

void F2()

{

TCHAR buf[100];

_stprintf(buf, _T("Test: %d"), 123);

MessageBox(NULL, buf, NULL, MB_OK);

RtlSecureZeroMemory(buf, sizeof(buf));

}

The functions differ in the way they clear the buffer. The first one uses the

memset() function, and the second the RtlSecureZeroMemory() function. Let's

compile the optimized code enabling the "/O2" switch for the Visual C++ 10

compiler. Look at the assembler code we've got as a result:

Page 474: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Figure 1. The memset() function is removed.

Figure 2. The RtlSecureZeroMemory() function fills memory with nulls.

As you can see from the assembler code, the memset() function was deleted by the

compiler during optimization, while the RtlSecureZeroMemory() function was

arranged into the code, thus clearing the array successfully.

Page 475: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Additional materials on this topic:

1. Safe Clearing of Private Data

2. Security, security! But do you test it?

3. Zero and forget - caveats of zeroing memory in C .

V598. The 'memset/memcpy' function is used to nullify/copy the fields of 'Foo' class. Virtual table pointer will be damaged by this.The analyzer has detected that such low-level functions as memset() or memcpy()

are used to handle a class. It is inadmissible when a class has pointer to a virtual

method table. The memset()/memcpy() functions might rewrite virtual table pointer

(VPTR), and the program behavior will become undefined.

Consider the following code sample.

class MyClass

{

int A, B, C;

char buf[100];

MyClass();

virtual ~MyClass() {}

};

MyClass::MyClass()

{

memset(this, 0, sizeof(*this));

}

Note that there is a virtual destructor in the class. It means that the class has a

virtual table pointer. The programmer was too lazy to clear the class members

separately and used the memset() function for that purpose. It will spoil the VPTR,

since the memset() function does not know anything about it.

Page 476: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

This is the correct code:

MyClass:: MyClass() : A(0), B(0), C(0)

{

memset(buf, 0, sizeof(buf));

}

V599. The virtual destructor is not present, although the 'Foo' class contains virtual functions.The analyzer has found a potential error: a virtual destructor is absent in a class. The

following conditions must hold for the analyzer to generate the V599 warning:

1) A class object is destroyed by the delete operator.

2) The class has at least one virtual function.

Presence of virtual functions indicates that the class may be used polymorphically.

In this case a virtual destructor is necessary to correctly destroy the object.

Consider the following code sample.

class Father

{

public:

Father() {}

~Father() {}

virtual void Foo() { ... }

};

class Son : public Father

{

public:

int* buffer;

Son() : Father() { buffer = new int[1024]; }

Page 477: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

~Son() { delete[] buffer; }

virtual void Foo() { ... }

};

...

Father* object = new Son();

delete object; // Call ~Father()!!

The code is incorrect and leads to memory leak. At the moment of deleting the

object only the destructor in the 'Father' class is called. To call the 'Son' class'

destructor you should make the destructor virtual.

This is the correct code:

class Father

{

public:

Father() {}

virtual ~Father() {}

virtual void Foo() { ... }

};

The V599 diagnostic message helps to detect far not all the issues related to absence

of virtual destructors. Here is the corresponding example:

You develop a library. It contains the XXX class which has virtual functions but no

virtual destructor. You don't handle this class in the library yourself, so the analyzer

won't warn you about the danger. The problem might occur at the side of a

programmer who uses your library and whose classes are inheritance of the XXX

class.

The C4265: 'class' : class has virtual functions, but destructor is not

virtual diagnostic message implemented in Visual C++ allows you to detect much

more issues. This is a very useful message. But it is turned off by default. I cannot

say why. This subject was discussed on the StackOverflow site: Why is C4265

Visual C++ warning (virtual member function and no virtual destructor) off by

default? Unfortunately, nobody managed to give a reasonable explanation.

Page 478: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

We suppose that C4265 gives many false positives in code where the mixin pattern

is used. When using this pattern, a lot of interface classes appear which contain

virtual functions but they don't need a virtual destructor.

We can say that the V599 diagnostic rule is a special case of C4265. It produces

fewer false reports but, unfortunately, allows you to detect fewer defects. If you

want to analyze your code more thoroughly, turn on the C4265 warning.

P. S.

Unfortunately, ALWAYS declaring a destructor as a virtual one is not a good

programming practice. It leads to additional overhead costs, since the class has to

store a pointer to the Virtual Method Table.

P.P.S.

The related diagnostic warning are V689.

Additional resources:1. Wikipedia. Virtual method table.

2. Wikipedia. Virtual function.

3. Wikipedia. Destructor.

4. Discussion on StackOverflow. When to use virtual destructors?

5. The Old New Thing. When should your destructor be virtual?

V600. Consider inspecting the condition. The 'Foo' pointer is always not equal to NULL.The analyzer has detected a comparison of an array address to null. This

comparison is meaningless and might signal an error in the program.

Consider the following code sample.

void Foo()

Page 479: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

{

short T_IND[8][13];

...

if (T_IND[1][j]==0 && T_IND[5]!=0)

T_buf[top[0]]= top[1]*T_IND[6][j];

...

}

The program handles a two-dimensional array. The code is difficult to read, so the

error is not visible at first sight. But the analyzer will warn you that the "T_IND[5]!

=0" comparison is meaningless. The pointer "T_IND[5]" is always not equal to

zero.

After studying the V600 warnings you may find errors which are usually caused by

misprints. For instance, it may turn out that the code above should be written in the

following way:

if (T_IND[1][j]==0 && T_IND[5][j]!=0)

The V600 warning doesn't always indicate a real error. Careless refactoring is often

the reason for generating the V600 warning. Let's examine the most common case.

This is how the code looked at first:

int *p = (int *)malloc(sizeof(int) *ARRAY_SIZE);

...

if (!p)

return false;

...

free(p);

Then it underwent some changes. It appeared that the ARRAY_SIZE value was

small and the array was able to be created on the stack. As a result, we have the

following code:

int p[ARRAY_SIZE];

...

if (!p)

Page 480: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

return false;

...

The V600 warning is generated here. But the code is correct. It simply turns out that

the "if (!p)" check has become meaningless and can be removed.

V601. An odd implicit type casting.The analyzer has detected an odd implicit type conversion. Such a type conversion

might signal an error or carelessly written code.

Let's consider the first example.

std::string str;

bool bstr;

...

str = true;

Any programmer will be surprised on seeing an assignment of the 'true' value to a

variable of the 'std::string' type. But this construct is quite permissible and working.

The programmer just made a mistake here and wrote a wrong variable.

This is the correct code:

std::string str;

bool bstr;

...

bstr = true;

Consider the second example:

bool Ret(int *p)

{

if (!p)

return "p1";

...

}

Page 481: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The string literal "p1" turns into the 'true' variable and is returned from the function.

It is a very odd code.

We cannot give you general recommendations on fixing such code, since every case

must be considered individually.

V602. Consider inspecting this expression. '<' possibly should be replaced with '<<'.The analyzer has detected a potential error that may be caused by a misprint. It is

highly probable that the '<<' operator must be used instead of '<' in an expression.

Consider the following code sample.

void Foo(unsigned nXNegYNegZNeg, unsigned nXNegYNegZPos,

unsigned nXNegYPosZNeg, unsigned nXNegYPosZPos)

{

unsigned m_nIVSampleDirBitmask =

(1 << nXNegYNegZNeg) | (1 < nXNegYNegZPos) |

(1 << nXNegYPosZNeg) | (1 << nXNegYPosZPos);

...

}

The code contains an error, since it is the '<' operator that is written by accident in

the expression. This is the correct code:

unsigned m_nIVSampleDirBitmask =

(1 << nXNegYNegZNeg) | (1 << nXNegYNegZPos) |

(1 << nXNegYPosZNeg) | (1 << nXNegYPosZPos);

Note.

The analyzer considers comparisons ('<', '>') odd if their result is used in binary

operations such as '&', '|' or '^'. The diagnostic is more complex but we hope you

Page 482: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

understand the point in general. On finding such expressions the analyzer emits the

V602 warning.

If the analyzer produces a false positive error, you may suppress it using the "//-

V602" comment. But in most cases you'd better rewrite this code. It's not a good

practice to handle expressions of the 'bool' type using binary operators: it makes the

code unevident and less readable.

V603. The object was created but it is not being used. If you wish to call constructor, 'this->Foo::Foo(....)' should be used.The analyzer has detected a potential error: incorrect use of a constructor.

Programmers often make mistakes trying to call a constructor explicitly to initialize

an object.

Consider a typical sample taken from a real application:

class CSlideBarGroup

{

public:

CSlideBarGroup(CString strName, INT iIconIndex,

CListBoxST* pListBox);

CSlideBarGroup(CSlideBarGroup& Group);

...

};

CSlideBarGroup::CSlideBarGroup(CSlideBarGroup& Group)

{

CSlideBarGroup(Group.GetName(), Group.GetIconIndex(),

Group.GetListBox());

}

Page 483: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

There are two constructors in the class. To reduce the source code's size the

programmer decided to call one constructor from the other. But this code does quite

the other thing than intended.

The following happens: a new unnamed object of the CSlideBarGroup type is

created and gets destroyed right after. As a result, the class fields remain

uninitialized.

The correct way is to create an initialization function and call it from the

constructors. This is the correct code:

class CSlideBarGroup

{

void Init(CString strName, INT iIconIndex,

CListBoxST* pListBox);

public:

CSlideBarGroup(CString strName, INT iIconIndex,

CListBoxST* pListBox)

{

Init(strName, iIconIndex, pListBox);

}

CSlideBarGroup(CSlideBarGroup& Group)

{

Init(Group.GetName(), Group.GetIconIndex(),

Group.GetListBox());

}

...

};

If you still want to call the constructor, you may write it in this way:

CSlideBarGroup::CSlideBarGroup(CSlideBarGroup& Group)

{

this->CSlideBarGroup::CSlideBarGroup(

Group.GetName(), Group.GetIconIndex(), Group.GetListBox());

}

Another identical code:

Page 484: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

CSlideBarGroup::CSlideBarGroup(CSlideBarGroup& Group)

{

new (this) CSlideBarGroup(

Group.GetName(), Group.GetIconIndex(),

Group.GetListBox());

}

The code of the given samples is very dangerous and you should understand

well how they work!

You may do more harm than good with this code. Consider the following samples

showing where such a constructor call is admissible and where it is not.

class SomeClass

{

int x,y;

public:

SomeClass() { SomeClass(0,0); }

SomeClass(int xx, int yy) : x(xx), y(yy) {}

};

The code contains an error. In the 'SomeClass() ' constructor, a temporary object is

created. As a result, the 'x' and 'y' fields remain uninitialized. You can fix the code

in this way:

class SomeClass

{

int x,y;

public:

SomeClass() { new (this) SomeClass(0,0); }

SomeClass(int xx, int yy) : x(xx), y(yy) {}

};

This code will work well. It is safe and working because the class contains primary

data types and is not a descendant of other classes. In this case the double

constructor call is not harmful.

Consider another code where the explicit constructor call causes an error:

Page 485: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

class Base

{

public:

char *ptr;

std::vector vect;

Base() { ptr = new char[1000]; }

~Base() { delete [] ptr; }

};

class Derived : Base

{

Derived(Foo foo) { }

Derived(Bar bar) {

new (this) Derived(bar.foo);

}

}

When we call the "new (this) Derived(bar.foo);" constructor, the Base object is

already created and the fields are initialized. The repeated constructor call will lead

to double initialization; we will write a pointer to the newly allocated memory area

into 'ptr'. As a result we will get memory leak. And if you take double initialization

of an object of the std::vector type, you cannot predict its result at all. But one thing

is obvious: this code is inadmissible.

In conclusion, I want to note it once again that you'd better create an initialization

function instead of explicitly calling a constructor. Explicit constructor call is

needed only in very rare cases.

Explicit call of one constructor from the other in C++11 (delegation)The new standard allows you to perform call of constructors from other constructors

(known as delegation). It enables you to create constructors that use behavior of

other constructors without added code.

This is an example of correct code:

Page 486: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

class MyClass {

int m_x;

public:

MyClass(int X) : m_x(X) {}

MyClass() : MyClass(33) {}

};

The MyClass constructor without arguments calls a constructor of the same class

with an integer argument.

C++03 considers an object to be constructed when its constructor finishes

executing, but C++11 considers an object constructed once any constructor finishes

execution. Since multiple constructors will be allowed to execute, this will mean

that each delegate constructor will be executing on a fully constructed object of its

own type. Derived class constructors will execute after all delegation in their base

classes is complete.

Additional information

1. Discussion on StackOverflow. C++'s "placement new".

2. Discussion on StackOverflow. Using new (this) to reuse constructors.

V604. It is odd that the number of iterations in the loop equals to the size of the pointer.The analyzer has detected a potential error in a construct that comprises a loop. The

loop is odd because the number of iterations in it equals to the sizeof(pointer). It is

highly probable that the number of iterations should correspond to the size of the

array the pointer refers to.

Let's see how such an error might occur. This is how the program looked at first:

char A[N];

for (size_t i=0; i < sizeof(A); ++i)

Page 487: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

A[i] = 0;

Then the program code underwent some changes and the 'A' array has become a

variable-sized array. The code has become incorrect:

char *A = (char *)malloc(N);

for (size_t i=0; i < sizeof(A); ++i)

A[i] = 0;

Now the "sizeof(A)" expression returns the pointer size, not the array's size.

This is the correct code:

char *A = (char *)malloc(N);

for (size_t i=0; i < N; ++i)

A[i] = 0;

V605. Consider verifying the expression. An unsigned value is compared to the number - NN.The analyzer has detected a potential error in an expression where an unsigned

variable is compared to a negative number. This is a rather rare situation and such a

comparison is not always an error. However, getting the V605 warning is a good

reason to review the code.

This is an example of code the V605 warning will be generated for:

unsigned u = ...;

if (u < -1)

{ ... }

V606. Ownerless token 'Foo'.The analyzer has detected a potential error: an extra lexeme in the code. Such "lost"

lexemes most often occur in the code when the key word return is missing.

Page 488: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Consider this sample:

bool Run(int *p)

{

if (p == NULL)

false;

...

}

The developer forgot to write "return" here. The code compiles well but has no

practical sense.

This is the correct code:

bool Run(int *p)

{

if (p == NULL)

return false;

...

}

V607. Ownerless expression 'Foo'.The analyzer has detected a potential error: an extra expression in the code. Such

"lost" expressions most often occur in the code when the key word return is missing

or due to careless code refactoring.

Consider this sample:

void Run(int &a, int b, int c, bool X)

{

if (X)

a = b + c;

else

b - c;

}

Page 489: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The program text is incomplete because of the misprint. It compiles well but has no

practical sense.

This is the correct code:

void Run(int &a, int b, int c, bool X)

{

if (X)

a = b + c;

else

a = b - c;

}

Sometimes "lost" expressions do have practical sense. For example, the analyzer

won't generate the warning for the following code:

struct A {};

struct B : public A {};

...

void Foo(B *p)

{

static_cast<A*>(p);

...

}

The "static_cast<A*>(p);" expression here checks that the 'B' class is a inherits of

the 'A' class. If it is not so, a compilation error will occur.

As another example, we can cite the following code intended to suppress the

compiler-generated warnings about unused variables:

void Foo(int a, int b)

{

a, b;

}

The analyzer won't generate the V607 warning in this case.

Page 490: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V608. Recurring sequence of explicit type casts.The analyzer has detected repeating sequences consisting of explicit type

conversion operators. This code usually appears because of misprints and doesn't

lead to errors. But it's reasonable to check those code fragments the analyzer

generates the V608 warning for. Perhaps there is an error, or the code can be

simplified at least.

Consider this sample:

m_hIcon = AfxGetApp()->LoadStandardIcon(

MAKEINTRESOURCE(IDI_ASTERISK));

The analyzer generates the warning for this code: V608 "Recurring sequence of

explicit type casts: (LPSTR)(ULONG_PTR)(WORD) (LPSTR)(ULONG_PTR)

(WORD)."

Let's find out where we get the two chains "(LPSTR)(ULONG_PTR)(WORD)"

from.

The constant value IDI_ASTERISK is a macro of the following kind:

#define IDI_ASTERISK MAKEINTRESOURCE(32516)

It means that the above cited code is equivalent to the following code:

m_hIcon = AfxGetApp()->LoadStandardIcon(

MAKEINTRESOURCE(MAKEINTRESOURCE(32516)));

The MAKEINTRESOURCE macro is expanded into (LPSTR)((DWORD)

((WORD)(i))). As a result, we get the following sequence:

m_hIcon = AfxGetApp()->LoadStandardIcon(

(LPSTR)((DWORD)((WORD)((LPSTR)((DWORD)((WORD)((32516))))))

);

Page 491: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

This code will work correctly but it is surplus and can be rewritten without extra

type conversions:

m_hIcon = AfxGetApp()->LoadStandardIcon(IDI_ASTERISK);

V609. Divide or mod by zero.The analyzer has detected a situation when division by zero may occur.

Consider this sample:

for (int i = -10; i != 10; ++i)

{

Foo(X / i);

}

While executing the loop, the 'i' variable will acquire a value equal to 0. At this

moment, an operation of division by zero will occur. To fix it we need to

specifically handle the case when the 'i' iterator equals zero.

This is the correct code:

for (int i = -10; i != 10; ++i)

{

if (i != 0)

Foo(X / i);

}

V610. Undefined behavior. Check the shift operator.The analyzer has detected a shift operator that causes undefined

behavior/unspecified behavior.

This is how the C++11 standard describes shift operators' work:

Page 492: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The shift operators << and >> group left-to-right.shift-expression << additive-

expressionshift-expression >> additive-expression

The operands shall be of integral or unscoped enumeration type and integral

promotions are performed.1. The type of the result is that of the promoted left

operand. The behavior is undefined if the right operand is negative, or greater than

or equal to the length in bits of the promoted left operand.2. The value of E1 << E2

is E1 left-shifted E2 bit positions; vacated bits are zero-filled. If E1 has an unsigned

type, the value of the result is E1 * 2^E2, reduced modulo one more than the

maximum value representable in the result type. Otherwise, if E1 has a signed type

and non-negative value, and E1*2^E2 is representable in the result type, then that

is the resulting value; otherwise, the behavior is undefined.3. The value of E1 >>

E2 is E1 right-shifted E2 bit positions. If E1 has an unsigned type or if E1 has a

signed type and a non-negative value, the value of the result is the integral part of

the quotient of E1/2^E2. If E1 has a signed type and a negative value, the resulting

value is implementation-defined.

Let's give some code samples that cause undefined or unspecified behavior:

int A = 1;

int B;

B = A << -3; // undefined behavior

B = A << 100; // undefined behavior

B = -1 << 5; // undefined behavior

B = -1 >> 5; // unspecified behavior

These are, of course, simplified samples. In real applications, it's more complicated.

Consider a sample taken from practice:

SZ_RESULT

SafeReadDirectUInt64(ISzInStream *inStream, UInt64 *value)

{

int i;

*value = 0;

for (i = 0; i < 8; i++)

{

Page 493: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Byte b;

RINOK(SafeReadDirectByte(inStream, &b));

*value |= ((UInt32)b << (8 * i));

}

return SZ_OK;

}

The function tries to read a 64-bit value byte-by-byte. Unfortunately, it will fail if

the number was larger than 0x00000000FFFFFFFF. Note the shift "(UInt32)b << (8

* i)". The size of the left operand is 32 bits. The shift takes from 0 to 56 bits. In

practice, it will cause the high-order part of the 64-bit value to remain filled with

zeroes. Theoretically, it is undefined behavior here and the result cannot be

predicted.

This is the correct code:

*value |= ((UInt64)b << (8 * i));

To learn more on the issue we've discussed, please read the article "Wade not in

unknown waters. Part three".

Let's examine the situation with the negative left operand in detail. Such a code

usually seems to work correctly. You might think that although this is undefined

behavior, all the compilers should handle the code in the same way. It's not so. It'd

be more correct to say that most compilers do that in the same way. If you are

concerned about code portability, you shouldn't use negative value shifts.

Here is an example to prove my words. You may get an unexpected result when

using the GCC compiler for the MSP430 microprocessor. Such a situation is

described here. Though the programmer blames the compiler, we in fact have that

very case when the compiler acts in a different way than we're used to.

Nevertheless, we understand when programmers want the warning to be disabled

for the cases when the left operand is negative. For this purpose, you may type in a

special comment somewhere in the program text:

//-V610_LEFT_SIGN_OFF

Page 494: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

This comment should be added into the header file included into all the other files.

For example, such is the "stdafx.h" file. If you add this comment into a "*.cpp" file,

it will affect only this particular file.

V611. The memory allocation and deallocation methods are incompatible.The analyzer has detected a potential error: memory is allocated and released

through incompatible methods. For example, the analyzer will warn you if memory

is allocated through the 'new' operator and released through the 'free' function.

Consider an example of incorrect code:

int *p = (int *)malloc(sizeof(int) * N);

...

...

delete [] p;

This is the fixed code:

int *p = (int *)malloc(sizeof(int) * N);

...

...

free(p);

V612. An unconditional 'break/continue/return/goto' within a loop.The analyzer has detected an odd loop. One of the following operators is used in the

loop body: break, continue, return, goto. These operators are executed always

without any conditions.

Page 495: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Consider the following corresponding examples:

do {

X();

break;

} while (Foo();)

for (i = 0; i < 10; i++) {

continue;

Foo();

}

for (i = 0; i < 10; i++) {

x = x + 1;

return;

}

while (*p != 0) {

x += *p++;

goto endloop;

}

endloop:

The above shown examples of loops are artificial, of course, and of little interest to

us. Now let's look at a code fragment found in one real application. We have

abridged the function code to make it clearer.

int DvdRead(....)

{

....

for (i=lsn; i<(lsn+sectors); i++){

....

// switch (mode->datapattern){

// case CdSecS2064:

((u32*)buf)[0] = i + 0x30000;

memcpy_fast((u8*)buf+12, buff, 2048);

buf = (char*)buf + 2064; break;

Page 496: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

// default:

// return 0;

// }

}

....

}

Some of the lines in the function are commented out. The trouble is that the

programmer forgot to comment out the "break" operator.

When there were no comments, "break" was inside the "switch" body. Then

"switch" was commented out and the "break" operator started to finish the loop

earlier than it should. As a result, the loop body is executed only once.

This is the correct code:

buf = (char*)buf + 2064; // break;

Note that the V612 diagnostic rule is rather complicated: a lot of cases are

accounted for, when using the break/continue/return/goto operator is quite correct.

Let's examine a few cases when the V612 warning don't generated.

1) Presence of a condition.

while (*p != 0) {

if (Foo(p))

break;

}

2) Special methods used in macros usually:

do { Foo(x); return 1; } while(0);

3) Passing the 'continue' operator using 'goto':

for (i = 0; i < 10; i++) {

if (x == 7) goto skipcontinue;

continue;

skipcontinue: Foo(x);

Page 497: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

}

There are other methods possible which are used in practice and are unknown to us.

If you have noticed that the analyzer generates false V612 warnings, please write to

us and send us the corresponding samples. We will study them and try to make

exceptions to these cases.

V613. Strange pointer arithmetic with 'malloc/new'.The analyzer has detected a potential error in the code allocating memory. A pointer

returned by the 'malloc' function or any other similar function is summed up with

some number. It is very strange and it's highly probable that the code contains a

misprint.

Consider this sample:

a = ((int *)(malloc(sizeof(int)*(3+5)))+2);

The expression contains many extraneous parentheses and the programmer must

have got mixed up in them. Let's simplify this code to make it clearer:

a = (int *)malloc(sizeof(int)*8);

a += 2;

It's very strange to add number 2 to the pointer. Even if it should be so and the code

is correct, it is very dangerous. For example, you might easily forget that memory

should be free this way: "free(a - 2);".

This is the correct code:

a = (int *)malloc(sizeof(int)*(3+5+2));

V614. Uninitialized variable 'Foo' used.

Page 498: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer has detected use of an uninitialized variable. Using uninitialized

variables has unpredictable results. What is dangerous about such defects is that

they may hide for years until chance gets suitable values gathered in uninitialized

variables.

Consider the following simple example:

int Aa = Get();

int Ab;

if (Ab) // Ab - uninitialized variable

Ab = Foo();

else

Ab = 0;

Whether or not the Foo() function is called depends on a combination of various

circumstances. Usually errors of using uninitialized variables occur through

misprints. For example, it may appear that a different variable should be used in this

place. This is the correct code:

int Aa = Get();

int Ab;

if (Aa) // OK

Ab = Foo();

else

Ab = 0;

It is not only using simple types that the V614 warning is generated. The analyzer

may show the warning for variables of the class type which have a constructor and

are initialized, as a matter of fact. However, using them without preliminary

assignment doesn't have sense. Smart pointers and iterators are examples of such

classes.

Have a look at the following samples:

std::auto_ptr<CLASS> ptr;

ptr->Foo();

Page 499: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

std::list<T>::iterator it;

*it = X;

This is the correct code:

std::auto_ptr<CLASS> ptr(Get());

ptr->Foo();

std::list<T>::iterator it;

it = Get();

*it = X;

It happens that the analyzer generates false V614 warnings. But sometimes it

happens through the fault of programmers themselves who write tricky code. Have

a look at a code sample taken from a real application:

virtual size_t _fread(const void *ptr, size_t bytes){

size_t ret = ::fread((void*)ptr, 1, bytes, fp);

if(ret < bytes)

failbit = true;

return ret;

}

int read32le(uint32 *Bufo, EMUFILE *fp)

{

uint32 buf;

if(fp->_fread(&buf,4)<4) // False alarm: V614

return 0;

....

}

Note that the buffer reading the data from the file is declared as "const void *ptr".

For the code to compile, the programmer uses an explicit conversion of the pointer

to the type "(void*)". We don’t know what made the programmer write this code.

The meaningless "const" qualifier confuses the analyzer: it thinks that the _fread()

function will use the 'buf' variable only for reading. Since the 'buf' variable is not

initialized, the analyzer generates the warning.

Page 500: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The code works, but it cannot be called smart. It should be rewritten: first, it will

become shorter and clearer; second, it will stop triggering the V614 warning.

This is the fixed code:

virtual size_t _fread(void *ptr, size_t bytes){

size_t ret = ::fread(ptr, 1, bytes, fp);

if(ret < bytes)

failbit = true;

return ret;

}

V615. An odd explicit conversion from 'float *' type to 'double *' type.The analyzer has detected an odd pointer type conversion. Among such strange

cases are situations when programmers try to cast a float-pointer to a double-pointer

or vice versa. The point is that float and double types have different sizes and this

type conversion most likely indicates an error.

Consider a simplest example:

float *A;

double* B = (double*)(A);

Incompatibility between the sizes of the types being cast causes 'B' to point to a

number format incorrect for the double type. Such pointer type conversion errors

occur because of misprints or through inattention. For example, it may appear that a

different data type or a different pointer should be used in such a code fragment.

This is the correct code:

double *A;

double* B = A;

Page 501: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V616. The 'Foo' named constant with the value of 0 is used in the bitwise operation.The analyzer has detected use of a zero constant in the bitwise operation AND (&).

The result of such an expression is always zero. It may lead to an incorrect logic of

program execution when such an expression is used in conditions or loops. Consider

a simplest example:

enum { FirstValue, SecondValue };

int Flags = GetFlags();

if (Flags & FirstValue)

{...}

The expression in the 'if' operator's condition always equals zero. It causes an

incorrect logic of program execution. Errors related to using zero constants in

bitwise operations usually occur because of misprints or incorrect constant

declaration. For example, it may appear that another constant should be used in such

a fragment. This is the correct code:

enum { FirstValue, SecondValue };

int Flags = GetFlags();

if (Flags & SecondValue)

{...}

Another correct variant of this code is the following sample where the constant is

declared as a non-zero constant. For example:

enum { FirstValue = 1, SecondValue };

int Flags = GetFlags();

if (Flags & FirstValue)

{...}

Page 502: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V617. Consider inspecting the condition. An argument of the '|' bitwise operation always contains a non-zero value.The analyzer has detected use of a non-zero constant in the bitwise operation OR (|).

The result of this expression is always a non-zero value. It may cause incorrect logic

of program execution when such an expression is used in conditions or loops.

Consider a simplest example:

enum { FirstValue, SecondValue };

int Flags = GetFlags();

if (Flags | SecondValue)

{...}

The expression in the 'if' operator's condition is always true. Errors related to using

non-zero constants in bitwise operations occur because of misprints. For example, it

may appear that another bitwise operation, for example &, should be used in such a

fragment. This is the correct code:

enum { FirstValue, SecondValue };

int Flags = GetFlags();

if (Flags & SecondValue)

{...}

Consider a code sample the analyzer has found in one real application:

#define PSP_HIDEHEADER 0x00000800

BOOL CResizablePageEx::NeedsRefresh(....)

{

if (m_psp.dwFlags | PSP_HIDEHEADER)

return TRUE;

...

return

Page 503: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

CResizableLayout::NeedsRefresh(layout, rectOld, rectNew);

}

It's obvious that the 'if' operator will always execute the 'return TRUE;' branch,

which is incorrect. This is the fixed code:

#define PSP_HIDEHEADER 0x00000800

BOOL CResizablePageEx::NeedsRefresh(....)

{

if (m_psp.dwFlags & PSP_HIDEHEADER)

return TRUE;

...

return

CResizableLayout::NeedsRefresh(layout, rectOld, rectNew);

}

V618. It's dangerous to call the 'Foo' function in such a manner, as the line being passed could contain format specification. The example of the safe code: printf("%s", str);The analyzer has detected that a formatted output function call might cause an

incorrect result. Moreover, such a code can be used for an attack (see this article for

details).

The string is output directly without using the "%s" specifier. As a result, if there is

a command character added into the string accidentally or deliberately, it will cause

a program failure. Consider a simplest example:

char *p;

...

Page 504: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

printf(p);

The call of the printf(p) function is incorrect, as there is no format string of the "%s"

kind. If there are format specifications to be found in the 'p' string, this output will

be most likely incorrect. The following code is safe:

char *p;

...

printf ("%s", p);

The V618 warning might seem insignificant. But actually this is a very important

thing when creating quality and safe programs.

Keep in mind that you may come across format specifications (%i, %p and so on) in

a string quite unexpectedly. It may occur accidentally when user inputs incorrect

data. It may also occur deliberately when incorrect data are input consciously.

Absence of the "%s" specifier may cause program crash or output of private data

somewhere outside the program. Before you turn off the V618 diagnostic, we insist

that you read the article "Wade not in unknown waters. Part two". Corrections to the

code you'll have to make will be too few to ignore this type of defects.

Note. The analyzer tries not to generate the V618 warning when a function call

cannot have any bad consequences. Here is an example when the analyzer won't

show you the warning:

printf("Hello!");

V619. An array is being utilized as a pointer to single object.The analyzer has detected that the '->' operator is applied to a variable defined as a

data array. Such a code might indicate incorrect use of data structures leading to

incorrect filling of the structure fields. Consider a sample of incorrect code:

struct Struct {

int r;

Page 505: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

};

...

Struct ms[10];

for (int i = 0; i < 10; i++)

{

ms->r = 0;

...

}

Using it in this way is incorrect, as only the first array item will be initialized.

Perhaps there is a misprint here or some other variable should be used. This is the

correct code:

Struct ms[10];

for (int i = 0; i < 10; i++)

{

ms[i].r = 0;

...

}

V620. It's unusual that the expression of sizeof(T)*N kind is being summed with the pointer to T type.The analyzer has detected that a variable of the pointer type is added to an

expression containing the sizeof(T) operator. Using the operator in such a way

might indicate incorrect address arithmetic. Consider a simplest example:

int *p;

size_t N = 5;

...

p = p + sizeof(int)*N;

Page 506: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

This use is incorrect. It is expected that we will move by N items in the data

structure. Instead, a 20-item shift occurs, as sizeof(int) value is 4 in 32-bit programs.

As a result, we'll get the following: "p = p + 20;". Perhaps there is a misprint or

other mistake. This is the correct code:

int *p;

size_t N = 5;

...

p = p + N;

Note. The analyzer considers the code correct if the char type is being handled in it.

Consider a sample where the analyzer won't generate the warning:

char *c;

size_t N = 5;

...

c = c + sizeof(float)*N;

V621. Consider inspecting the 'for' operator. It's possible that the loop will be executed incorrectly or won't be executed at all.The analyzer has detected a potential error: odd initial and finite counter values are

used in the 'for' operator. It may cause incorrect loop execution and break the

program execution logic. Consider the following example:

signed char i;

for (i = -10; i < 100; i--)

{

...

};

Page 507: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Perhaps there is a misprint here causing the initial and finite values to be mixed up.

The error may also occur if operators '++' and '--' are mixed up.

This is the correct code:

for (i = -10; i < 100; i++)

{

...

};

The following code is also correct:

for (i = 100; i > -10; i--)

{

...

};

Consider the following code sample found by the analyzer in a real application:

void CertificateRequest::Build()

{

...

uint16 authCount = 0;

for (int j = 0; j < authCount; j++) {

int sz = REQUEST_HEADER + MIN_DIS_SIZE;

...

}

}

The 'authCount' variable is initialized by an incorrect value or perhaps there is even

some other variable to be used here.

V622. Consider inspecting the 'switch' statement. It's possible

Page 508: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

that the first 'case' operator is missing.The analyzer has detected a potential error: the first operator in the 'switch'

operator's block is not the 'case' operator. It causes the code fragment never to get

control. Consider this example:

char B = '0';

int I;

...

switch(I)

{

B = '1';

break;

case 2:

B = '2';

break;

default:

B = '3';

break;

}

Assignment "B = '1';" will never be performed. This is the correct code:

switch(I)

{

case 1:

B = '1';

break;

case 2:

B = '2';

break;

default:

B = '3';

break;

}

Page 509: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V623. Consider inspecting the '?:' operator. A temporary object is being created and subsequently destroyed.The analyzer has detected a possible error occurring when handling the ternary

operator '?:'. If, while handling the '?:' operator, an object of the class type and any

other type which can be cast to this class are used together, temporary objects are

created. The temporary objects will be destroyed after exiting the '?:' operator. An

error occurs if we save the result into a pointer-variable in this case. Consider this

example:

CString s1(L"1");

wchar_t s2[] = L"2";

bool a = false;

...

const wchar_t *s = a ? s1 : s2;

The result of executing this code is the 's' variable pointing to the data stored inside

a temporary object. The trouble is that this object is already destroyed!

This is the correct code:

wchar_t s1[] = L"1";

wchar_t s2[] = L"2";

bool a = false;

...

const wchar_t *s = a ? s1 : s2;

This is another code variant which is correct too:

CString s1(L"1");

wchar_t s2[] = L"2";

bool a = false;

Page 510: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

...

CString s = a ? s1 : s2;

The V623 warning demands better attention from the programmer. The trouble is

that errors of this type can hide very well. A code containing such errors may work

successfully for many years. However, it's only an illusion of correct operation.

Actually it is the released memory which is being used. The fact that there are

correct data in memory is just a matter of luck. The program behavior can change

any moment. It can occur as you switch to another compiler version, or after code

refactoring, or when a new object appears which uses the same memory area. Let's

study this by example.

Let's write, compile and run the following code:

bool b = false;

CComBSTR A("ABCD");

wchar_t *ptr = b ? A : L"Test OK";

wcout << ptr << endl;

This code was compiled with Visual Studio 2010 and it printed "Test OK". It seems

to be working well. But let's edit the code a bit:

bool b = false;

CComBSTR A("ABCD");

wchar_t *ptr = b ? A : L"Test OK";

wchar_t *tmp = b ? A : L"Error!";

wcout << ptr << endl;

It seems that the string where the 'tmp' variable is being initialized won't change

anything. But it's not true. The program now prints the text: "Error!".

The point is that the new temporary object was using the same memory area as the

previous one. By the way, note that this code can work quite successfully in certain

circumstances. Everything depends on luck and phase of Moon. It's impossible to

predict where temporary objects will be created, so don't refuse fixing the code

proceeding from the idea "this code has been working right for several years, so it

has no errors".

Page 511: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V624. The constant NN is being utilized. The resulting value could be inaccurate. Consider using the M_NN constant from <math.h>.The analyzer has detected a potential error occurring when handling constants of the

double type. Perhaps poor accuracy constants are used for mathematical

calculations. Consider this sample:

double pi = 3.141592654;

This way of writing the constant is not quite correct and you'd better use

mathematical constants from the header file 'math.h'. This is the correct code:

#include <math.h>

...

double pi = M_PI;

The analyzer doesn't show the warning when constants are written explicitly in the

'float' format. It is determined by the fact that the 'float' type has fewer significant

digits than the 'double' type. Here is an example:

float f = 3.14159f; //ok

V625. Consider inspecting the 'for' operator. Initial and final values of the iterator are the same.The analyzer has detected a potential error: initial and finite counter values coincide

in the 'for' operator. Using the 'for' operator in such a way will cause the loop to be

executed only once or not be executed at all. Consider the following example:

Page 512: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

void beginAndEndForCheck(size_t beginLine, size_t endLine)

{

for (size_t i = beginLine; i < beginLine; ++i)

{

...

}

The loop body is never executed. Most likely, there's a misprint and "i < beginLine"

should be replaced with the correct expression "i < endLine". This is the correct

code:

for (size_t i = beginLine; i < endLine; ++i)

{

...

}

Another example:

for (size_t i = A; i <= A; ++i)

...

This loop's body will be executed only once. This is hardly what the programmer

intended.

V626. Consider checking for misprints. It's possible that ',' should be replaced by ';'.The analyzer has detected a potential error: comma ',' is written by accident instead

of semicolon ';'. This misprint can lead to an incorrect logic of program execution.

Consider an example:

int a;

int b;

...

Page 513: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

if (a == 2)

a++,

b = a;

This code will result in executing the "b = a;" expression only when the 'if'

operator's condition holds. This is most likely a misprint and ',' should be replaced

with ';'. This is the correct code:

if (a == 2)

a++;

b = a;

The analyzer won't generate the message if formatting of a code fragment

demonstrates deliberate use of the ',' operator. Here is a code sample:

if (a == 2)

a++,

b = a;

if (a == 2)

a++, b = a;

V627. Consider inspecting the expression. The argument of sizeof() is the macro which expands to a number.The analyzer has detected a potential error: a macro expanding into a number serves

as an argument for the 'sizeof' operator. Using the operator in such a way can cause

allocation of memory amount of incorrect size or other defects.

Consider an example:

#define NPOINT 100

...

Page 514: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

char *point = (char *)malloc(sizeof(NPOINT));

Executing this code will result in allocation of insufficient memory amount. This is

the correct code:

#define NPOINT 100

...

char *point = (char *)malloc(NPOINT);

V628. It's possible that the line was commented out improperly, thus altering the program's operation logics.The analyzer has detected a potential error: two 'if' operators in a row are divided by

a commented out line. It's highly probable that a code fragment was commented

carelessly. The programmer's carelessness has caused a significant change of the

program execution logic. Consider this sample:

if(!hwndTasEdit)

//hwndTasEdit = getTask()

if(hwndTasEdit)

{

...

}

The program has become meaningless. The condition of the second 'if' operator

never holds. This is the correct code:

//if(!hwndTasEdit)

//hwndTasEdit = getTask()

if(hwndTasEdit)

{

...

Page 515: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

}

The analyzer doesn't generate the warning for code where code formatting

demonstrates deliberate use of two 'if' operators in a row divided by a comment line.

Here is an example:

if (Mail == ready)

// comment

if (findNewMail)

{

...

}

V629. Consider inspecting the expression. Bit shifting of the 32-bit value with a subsequent expansion to the 64-bit type.The analyzer has detected a potential error in an expression containing a shift

operation: a 32-bit value is shifted in the program. The resulting 32-bit value is then

explicitly or implicitly cast to a 64-bit type.

Consider an example of incorrect code:

unsigned __int64 X;

X = 1u << N;

This code causes undefined behavior if the N value is higher than 32. In practice, it

means that you cannot use this code to write a value higher than 0x80000000 into

the 'X' variable.

You can fix the code by making the type of the left argument 64-bit.

This is the correct code:

Page 516: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

unsigned __int64 X;

X = 1ui64 << N;

Note that the V629 diagnostic doesn't refer to 64-bit errors. By 64-bit errors those

cases are meant when the 32-bit version of a program works correctly, while the 64-

bit version doesn't.

The case we consider here causes an error both in the 32-bit and 64-bit versions.

That's why the V629 diagnostic refers to general analysis rules.

The analyzer will not generate the warning if the result of an expression with the

shift operation fits into a 32-bit type. It means that significant bits don't get lost and

the code is correct.

This is an example of safe code:

char W = 7;

long long Q = W << 10;

The code works in the following way. At first, the 'W' variable is extended to the

32-bit 'int' type. Then a shift operation is performed and we get the value

0x00001C00. This number fits into a 32-bit type, which means that no error occurs.

At the last step this value is extended to the 64-bit 'long long' type and written into

the 'Q' variable.

V630. The 'malloc' function is used to allocate memory for an array of objects which are classes containing constructors/destructors.The analyzer has detected a potential error caused by using one of the dynamic

memory allocation functions such as malloc, calloc, realloc. The allocated memory

is being handled as an object array that has a constructor or a destructor. When

memory is allocated for the class in this way, the code does not call the constructor.

Page 517: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

When memory is released through the 'free()' function, the code does not call the

destructor. This is quite odd: such a code might cause handling uninitialized

variables and other errors.

Consider an example of incorrect code:

class CL

{

int num;

public:

CL() : num(0) {...}

...

};

...

CL *pCL = (CL*)malloc(sizeof(CL) * 10);

As a result, the 'num' variable won't be initialized. Of course, you can call the

constructor for each object "manually", but a more correct way is to use the 'new'

operator.

This is the fixed code:

CL *pCL = new CL[10];

V631. Consider inspecting the 'Foo' function call. Defining an absolute path to the file or directory is considered a poor style.The analyzer has detected a potential error that occurs when calling a function

intended to handle files. An absolute path to a file or directory is passed into a

function in one of the actual arguments. Using a function in such a way is

dangerous, as there may be cases that this path doesn't exist on the user's computer.

Consider an example of incorrect code:

Page 518: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

FILE *text = fopen("c:\\TEMP\\text.txt", "r");

A better way is to get the path to a file on certain conditions.

This is the correct code:

string fullFilePath = GetFilePath() + "text.txt";

FILE *text = fopen(fullFilePath.c_str(), "r");

V632. Consider inspecting the NN argument of the 'Foo' function. It is odd that the argument is of the 'T' type.The analyzer has detected a potential error: an odd argument is passed into a

function. An argument having the format of a floating-point number has been

passed into a function, although it was awaiting an integer type. It is incorrect

because the argument value will be cast to an integer type.

Consider the following sample:

double buf[N];

...

memset(buf, 1.0, sizeof(buf));

The programmer intended to fill the array with values '1.0'. But this code will fill

the array with garbage.

The second argument of the 'memset' function has an integer type. This argument

defines the value to fill each byte of the array with.

Value '1.0' will be cast to the integer value '1'. The 'buf' data array will be filled

byte-by-byte with "one" values. This result is different from what we get when

filling each array item with value '1.0'.

This is the fixed code:

Page 519: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

double buf[N];

...

for (size_t i = 0; i != N; ++i)

buf[i] = 1.0;

V633. Consider inspecting the expression. Probably the '!=' should be used here.The analyzer has detected a potential error. The '!=' or '==!' operator should be

probably used instead of the '=!' operator. Such errors most often occur through

misprints.

Consider an example of incorrect code:

int A, B;

...

if (A =! B)

{

...

}

It's most probably that this code should check that the 'A' variable is not equal to 'B'.

If so, the correct code should look like follows:

if (A != B)

{

...

}

The analyzer accounts for formatting in the expression. That's why if it is exactly

assignment you need to perform - not comparison - you should specify it through

parentheses or blanks. The following code samples are considered correct:

if (A = !B)

Page 520: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

...

if (A=(!B))

...

V634. The priority of the '+' operation is higher than that of the '<<' operation. It's possible that parentheses should be used in the expression.The analyzer has detected a potential error occurring because of the addition,

subtraction, division and multiplication operations having a higher priority than the

shift operation. Programmers often forget about this, which sometimes causes an

expression to have quite a different result than they expect.

Consider an example of incorrect code:

int X = 1<<4 + 2;

The programmer most likely expected that the result of shifting '1' by '4' would be

added to '2'. But according to operation priorities in C/C++, addition is performed

first and shifting is performed after that.

We can recommend you to write parentheses in all expressions containing operators

that you use rarely. Even if some of these parentheses turn out to be unnecessary,

it's OK. On the other hand, your code will become more readable and

comprehensible and less error-prone.

This is the correct code:

int X = (1<<4) + 2;

How to remove a false warning if it is really that very sequence of calculations you

intended: addition first, then the shift?

Page 521: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

There are 3 ways to do it:

1) The worst way. You can use the "//-V634" comment to suppress the warning in a

certain line.

int X = 1<<4 + 2; //-V634

2) You can add additional parentheses:

int X = 1<<(4 + 2);

3) You can specify your intention using blanks:

int X = 1 << 4+2;

References:

1. Terminology. Operation priorities in C/C++. http://www.viva64.com/en/t/0064/

V635. Consider inspecting the expression. The length should probably be multiplied by the sizeof(wchar_t).The analyzer has detected a potential error: a memory amount of incorrect size is

allocated to store a string in the UNICODE format.

This error usually occurs when the 'strlen' or 'wcslen' function is used to calculate an

array size. Programmers often forget to multiply the resulting number of characters

by sizeof(wchar_t). As a result, an array overrun may occur.

Consider an example of incorrect code:

wchar_t src[] = L"abc";

wchar_t *dst = (wchar_t *)malloc(wcslen(src) + 1);

wcscpy(dst, src);

Page 522: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

In this case, it's just 4 bytes that will be allocated. Since the 'wchar_t' type's size is 2

or 4 bytes depending on the data model, this memory amount may appear

insufficient. To correct the mistake you should multiply the expression inside

'malloc' by 'sizeof(wchar_t)'.

This is the correct code:

wchar_t *dst =

(wchar_t *)malloc((wcslen(src) + 1) * sizeof(wchar_t));

V636. The expression was implicitly cast from integer type to real type. Consider utilizing an explicit type cast to avoid overflow or loss of a fractional part.An expression contains a multiplication or division operation over integer data

types. The resulting value is implicitly cast to a floating-point type. When detecting

this, the analyzer warns you about a potential error that may cause an overflow or

calculation of an incorrect result. Below are examples of possible errors.

Case one. Overflow.

int LX = 1000;

int LY = 1000;

int LZ = 1000;

int Density = 10;

double Mass = LX * LY * LZ * Density;

We want to calculate an object's mass relying on its density and volume. We know

that the resulting value may be a large one. That's why we declare the 'Mass'

variable as the 'double' type. But this code doesn't take into account that there are

Page 523: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

variables of the 'int' type which are multiplied. As a result, we'll get an integer

overflow in the right part of the expression and the result will be incorrect.

There are two ways to fix the issue. The first way is to change the variables' types:

double LX = 1000.0;

double LY = 1000.0;

double LZ = 1000.0;

double Density = 10.0;

double Mass = LX * LY * LZ * Density;

The second way is to use an explicit type conversion:

int LX = 1000;

int LY = 1000;

int LZ = 1000;

int Density = 10;

double Mass = (double)(LX) * LY * LZ * Density;

We can cast only the first variable to the 'double' type - that'll be enough. Since the

multiplication operation refers to left-associative operators, calculation will be

executed in the following way: (((double)(LX) * LY) * LZ) * Density.

Consequently, each of the operands will be cast to the 'double' type before

multiplication and we will get a correct result.

P.S. Let me remind you that it will be incorrect if you try to solve the issue in the

following way: Mass = (double)(ConstMass) + LX * LY * LZ * Density. The

expression to the right of the '=' operator will have the 'double' type, but it's still

variables of the 'int' type that will be multiplied.

Case two. Loss of accuracy.

int totalTime = 1700;

int operationNum = 900;

double averageTime = totalTime / operationNum;

The programmer may be expecting that the 'averageTime' variable will have value

'1.888(8)', but the result will equal '1.0' when executing the program. It happens

Page 524: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

because the division operation is performed over integer types and only then is cast

to the floating-point type.

Like in the previous case, we may fix the error in two ways.

The first way is to change the variables' types:

double totalTime = 1700;

double operationNum = 900;

double averageTime = totalTime / operationNum;

The second way is to use an implicit type conversion.

int totalTime = 1700;

int operationNum = 900;

double averageTime = (double)(totalTime) / operationNum;

Note

Certainly, in some cases it's exactly division of integers that you need to execute. In

such cases you can use the following comment to suppress false positives:

//-V636

See also: Documentation. Suppression of false alarms.

V637. Two opposite conditions were encountered. The second condition is always false.The analyzer has detected a potential logic error in the program. The error is this:

two conditional operators in a sequence contain mutually exclusive conditions. Here

are examples of mutually exclusive conditions:

'A == B' and 'A != B';

'B < C' and 'B > C';

'X == Y' and 'X < Y';

Page 525: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

etc.

This error usually occurs as a consequence of a misprint or poor refactoring. As a

result, program execution logic is violated.

Consider an example of incorrect code:

if (A == B)

if (B != A)

B = 5;

In this case, the "B = 5;" statement will never be executed. Most likely, an incorrect

variable is used in the first or in the second condition. We need to find out the

program execution logic.

This is the fixed code:

if (A == B)

if (B != C)

B = 5;

V638. A terminal null is present inside a string. The '\0xNN' characters were encountered. Probably meant: '\xNN'.The analyzer has detected a potential error: there is a terminal null character inside a

string.

This error usually occurs through a misprint. For example, the "\0x0A" sequence is

considered by the program as a sequence of four bytes: { '\0', 'x', '0', 'A' }.

If you want to define the character code in the hexadecimal form, the 'x' character

should stand right after the '\' character. If you write "\0", the program will consider

it as zero (in the octal format). See also:

Page 526: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

MSDN. C Character Constants.

MSDN. Escape Sequences.

Consider an example of incorrect code:

const char *s = "string\0x0D\0x0A";

If you try to print this string, the control characters intended to translate the string

will not be used. The output functions will stop at the line-end character '\0'. To fix

this bug you should replace "\0x0D\0x0A" with "\x0D\x0A".

This is the fixed code:

const char *s = "string\x0D\x0A";

V639. Consider inspecting the expression for function call. It is possible that one of the closing ')' brackets was positioned incorrectly.The analyzer has detected a potential error: a suspicious function call is present

which is followed by commas and expressions. Perhaps these expressions should be

part of the function call.

This error usually occurs if a function is called inside a conditional operator and the

function has arguments by default. In this case you may easily make a mistake

writing a closing parenthesis in a wrong place. What is dangerous about these errors

is that the code is compiled and executed without errors. Consider the following

sample of incorrect code:

bool rTuple(int a, bool Error = true);

...

if (rTuple(exp), false)

{

...

Page 527: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

}

The closing parenthesis put in a wrong place will cause two errors at once:

1) The 'Error' argument will equal 'true' when calling the 'rTuple' function, though

the programmer meant it to be 'false'.

2) The comma operator ',' returns the value of the right part. It means that the

(rTuple(exp), false) condition will always be 'false'

This is the fixed code:

if (rTuple(exp, false))

{

...

}

V640. The code's operational logic does not correspond with its formatting.The analyzer has detected a potential error: code formatting following a conditional

operator doesn't correspond to the program execution logic. It's highly probable that

opening and closing curly brackets are missing.

Consider the following sample of incorrect code:

if (a == 1)

b = c; d = b;

In this case, the 'd = b;' assignment will be executed all the time regardless of the 'a

== 1' condition.

If the code contains a mistake, it can be fixed through adding curly brackets. This is

the fixed code:

Page 528: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

if (a == 1)

{ b = c; d = b; }

Another example of incorrect code:

if (a == 1)

b = c;

d = b;

To fix the error here, we should use curly brackets too. This is the fixed code:

if (a == 1)

{

b = c;

d = b;

}

If the code is correct, it should be formatted in the following way, for the V640

warning not to be generated:

if (a == 1)

b = c;

d = b;

This type of errors can be often seen in programs that actively use macros. Consider

the following error found in one real application:

#define DisposeSocket(a) shutdown(a, 2); closesocket(a)

...

if (sockfd > 0)

(void) DisposeSocket(sockfd);

The call of the 'closesocket(a);' function will be executed all the time. This will lead

to a fault if the 'sockfd' variable is <= 0.

The error can be fixed by using curly brackets in the macro. But you'd better create

a full-fledged function: code without macros is safer and more convenient to debug.

This is what the correct code may look like:

Page 529: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

inline void DisposeSocket(int a) {

shutdown(a, 2);

closesocket(a);

}

...

if (sockfd > 0)

DisposeSocket(sockfd);

V641. The size of the allocated memory buffer is not a multiple of the element size.The analyzer has detected a potential error: an incorrect memory amount is

allocated for storing array items.

This error usually occurs if the size of allocated memory is defined by a constant

whose value is not multiple of one array item's size. To determine the array size we

should use the 'sizeof( T ) * N' expression where 'T' is the type of one array item, 'N'

is the number of array items. Incorrect memory allocation may result in array

overrun.

Consider an example of incorrect code:

int *p;

p = (int *)malloc(70);

In this case 70 bytes will be allocated. Perhaps the programmer needed 80 bytes and

just made a misprint.

This is the correct code:

p = (int *)malloc(80);

It may be also that the programmer needed to allocate memory to store 70 items. If

this is the case, the fixed code will look like this:

Page 530: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

p = (int *)malloc(sizeof(int) * 70);

V642. Saving the function result inside the 'byte' type variable is inappropriate. The significant bits could be lost breaking the program's logic.The analyzer has detected a potential error: a function result is saved into a variable

whose size is only 8 or 16 bits. It may be inadmissible for some functions that return

a status of the 'int' type: significant bits may get lost.

Consider the following example of incorrect code:

char c = memcmp(buf1, buf2, n);

if (c != 0)

{

...

}

The 'memcmp' function returns the following values of the 'int' type:

< 0 - buf1 less than buf2;

0 - buf1 identical to buf2;

> 0 - buf1 greater than buf2;

Note that "> 0" means any numbers, not 1. It can be 2, 3, 100, 256, 1024, 5555 and

so on. It means that this result cannot be stored in a 'char'-variable, as significant

bits may be thrown off, which will violate the program execution logic.

What is dangerous about such errors is that the returned value may depend on the

architecture and an implementation of a particular function on this architecture. For

instance, the program may work correctly in the 32-bit mode and incorrectly in the

64-bit mode.

Page 531: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

This is the fixed code:

int c = memcmp(buf1, buf2, n);

if (c != 0)

{

...

}

Some of you might think that this danger is farfetched. But this error caused a

severe vulnerability in MySQL/MariaDB up to versions 5.1.61, 5.2.11, 5.3.5,

5.5.22. The point is that when a MySQL /MariaDB user logins, the token (SHA of

the password and hash) is calculated and compared to the expected value returned

by the 'memcmp' function. On some platforms the returned value might fall out of

the range [-128..127]. As a result, in 1 case in 256 the procedure of comparing the

hash with the expected value always returns 'true' regardless of the hash. It means

that an intruder can use a simple bash-command to get root access to the vulnerable

MySQL server even if he/she doesn't know the password. This breach is caused by

the following code contained in the file 'sql/password.c':

typedef char my_bool;

...

my_bool check(...) {

return memcmp(...);

}

This issue is described in more detail here: Security vulnerability in

MySQL/MariaDB.

V643. Unusual pointer arithmetic. The value of the 'char' type is being added to the string pointer.The analyzer has detected a potential error: incorrect addition of a character

constant to a string literal pointer.

Page 532: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

This error usually occurs when the programmer tries to unite a string literal with a

character.

Consider a simple example of incorrect code:

std::string S = "abcd" + 'x';

The programmer expected to get the "abcdx" string, but actually value 120 will be

added to the pointer to the "abcd" string. This will surely lead to the string literal

overrun. To prevent this bug you should avoid such arithmetic operations over

string and character variables.

This is the correct code:

std::string S = std::string("abcd") + 'x';

V644. A suspicious function declaration. It is possible that the T type object was meant to be created.The analyzer has detected a potential error: creating an object of the 'T' type in an

incorrect way.

This error usually occurs when an argument of a call of a constructor of a certain

type is missing. In this case, we'll get a declaration of a function returning the 'T'

type instead of creating an object of the type we need. This error usually occurs

when using auxiliary classes that simplify mutex locking and unlocking. For

example, such is the 'QMutexLocker' class in the 'Qt' library that simplifies handling

of the 'QMutex class'.

Consider an example of incorrect code:

QMutex mutex;

...

Page 533: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

QMutexLocker lock();

++objectVarCounter;

What is dangerous about these errors is that code is compiled and executed without

errors. But you won't get the result you need. That is, other threads using the

'objectVarCounter' variable are not locked. That's why such errors take much time

and effort to catch.

This is the fixed code:

QMutex mutex;

...

QMutexLocker lock(&mutex);

++objectVarCounter;

V645. The function call could lead to the buffer overflow. The bounds should not contain the size of the buffer, but a number of characters it can hold.The analyzer has detected a potential error related to string concatenation. The error

might cause buffer overflow. What is unpleasant about these errors is that the

program may work stably for a long time as long as the function receives only short

strings.

This type of vulnerabilities is characteristic of such functions as 'strncat', 'wcsncat',

etc. [1].

This is the 'strncat' function's description:

char *strncat(

char *strDest,

const char *strSource,

Page 534: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

size_t count

);

where:

'destination' is the recipient string;

'source' is the source string;

'count' is number of characters to append.

The 'strncat' function is perhaps one of the most dangerous string functions. The

danger occurs because its mechanism differs from what programmers expect.

The third argument points at the number of remaining characters that can be placed

into it, not the buffer size. Here is a quotation from the function's description in

MSDN:

strncat does not check for sufficient space in strDest; it is therefore a potential

cause of buffer overruns. Keep in mind that count limits the number of characters

appended; it is not a limit on the size of strDest.

Unfortunately, programmers often forget it and use strncat in an inappropriate way.

We can distinguish two types of mistakes:

1) Developers think that the 'count' argument is the 'strDest' buffer's size. As a

result, proceeding from this misinterpretation they write the following incorrect

code:

char newProtoFilter[2048] = "....";

strncat(newProtoFilter, szTemp, 2048);

strncat(newProtoFilter, "|", 2048);

The programmer believes that he/she is protecting the code against an overflow by

passing number 2048 as the third argument. But it's wrong. The programmer is

actually telling the code that up to 2048 characters more can be added to the string!

2) People forget that the strncat function will add terminal 0 after copying. Here is

an example of dangerous code:

Page 535: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

char filename[NNN];

...

strncat(filename,

dcc->file_info.filename,

sizeof(filename) - strlen(filename));

At first sight you may think the programmer has protected the program from the

'filename' buffer overflow. It's not so. The programmer has subtracted the string

length from the array size. It means that if the string is already filled completely, the

"sizeof(filename) - strlen(filename)" expression will return one. As a result, one

more character will be added to the string, while the terminal null will be written

outside the buffer.

Let's clarify this error by a simpler example:

char buf[5] = "ABCD";

strncat(buf, "E", 5 - strlen(buf));

The buffer doesn't have any more space for new characters. It contains 4 characters

and the terminal null. The "5 - strlen(buf)" expression equals 1. The strncpy()

function will copy the "E" character into the last item of the 'buf' array. The terminal

0 will be written outside the buffer!

To fix the above cited code fragments we need to rewrite them in the following

way:

// Sample N1

char newProtoFilter[2048] = "....";

strncat(newProtoFilter, szTemp,

2048 - 1 - strlen(newProtoFilter));

strncat(newProtoFilter, "|",

2048 - 1 - strlen(newProtoFilter));

// Sample N2

char filename[NNN];

...

strncat(filename,

Page 536: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

dcc->file_info.filename,

sizeof(filename) - strlen(filename) - 1);

This code cannot be called smart or really safe. It's a much better solution to refuse

using functions like 'strncat' in favor of safer ones. For example, use the std::string

class or such functions as strncat_s and so on [2].

References1. MSDN. strncat, _strncat_l, wcsncat, wcsncat_l, _mbsncat _mbsncat_l

2. MSDN. strncat_s, _strncat_s_l, wcsncat_s, _wcsncat_s_l, _mbsncat_s, _mbsncat_s_l

V646. Consider inspecting the application's logic. It's possible that 'else' keyword is missing.The if operator is located in the same line as the closing parenthesis referring to the

previous if. Perhaps, the key word 'else' is missing here, and the program works in a

different way than expected.

Have a look at a simple example of incorrect code:

if (A == 1) {

Foo1(1);

} if (A == 2) {

Foo2(2);

} else {

Foo3(3);

}

If the 'A' variable takes value 1, not only the 'Foo1' function will be called, but the

'Foo3' function as well. Note the program execution logic: maybe this is what the

programmer actually expects it to do. Otherwise, the key word 'else' should be

added.

Page 537: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

This is the fixed code:

if (A == 1) {

Foo1(1);

} else if (A == 2) {

Foo2(2);

} else {

Foo3(3);

}

The analyzer also considers the code correct when the 'then' part of the first 'if'

operator contains the unconditional operator 'return' - because the program

execution logic is not broken in this case, while it's just a bit incorrect code

formatting. Here is an example of such a code:

if (A == 1) {

Foo1(1);

return;

} if (A == 2) {

Foo2(2);

} else {

Foo3(3);

}

If there is no error, the V646 warning can be avoided by moving the 'if' operator

onto the next line. For example:

if (A == 1) {

Foo1(1);

}

if (A == 2) {

Foo2(2);

} else {

Foo3(3);

}

Page 538: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

In the samples cited above, the error is clearly seen and seems improbable to be

found in real applications. But if your code is quite complex, it becomes very easy

not to notice the missing 'else' operator. Here is a sample of this error taken from a

real application:

if( 1 == (dst->nChannels) ) {

ippiCopy_16s_C1MR((Ipp16s*)pDstCh, dstStep,

(Ipp16s*)pDst, dst->widthStep, roi, pMask, roi.width);

} if( 3 == (dst->nChannels) ) { //V646

ippiCopy_16s_C3R((Ipp16s*)pDst-coi, dst->widthStep,

(Ipp16s*)pTmp, dst->widthStep, roi);

ippiCopy_16s_C1C3R((Ipp16s*)pDstCh, dstStep,

(Ipp16s*)pTmp+coi, dst->widthStep, roi);

ippiCopy_16s_C3MR((Ipp16s*)pTmp, dst->widthStep,

(Ipp16s*)pDst-coi, dst->widthStep, roi, pMask, roi.width);

} else {

ippiCopy_16s_C4R((Ipp16s*)pDst-coi, dst->widthStep,

(Ipp16s*)pTmp, dst->widthStep, roi);

ippiCopy_16s_C1C4R((Ipp16s*)pDstCh, dstStep,

(Ipp16s*)pTmp+coi, dst->widthStep, roi);

ippiCopy_16s_C4MR((Ipp16s*)pTmp, dst->widthStep,

(Ipp16s*)pDst-coi, dst->widthStep, roi, pMask, roi.width);

}

This code is very hard to read and comprehend. But the analyzer always stays

focused.

In this sample, the conditions '3 == (dst->nChannels)' and '1 == (dst->nChannels)'

cannot be executed simultaneously, while the code formatting indicates that the key

word 'else' is missing. This is what the correct code should look like:

if( 1 == (dst->nChannels) ) {

....

} else if( 3 == (dst->nChannels) ) {

....

} else {

....

Page 539: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

}

V647. The value of 'A' type is assigned to the pointer of 'B' type.The analyzer has detected an incorrect pointer operation: an integer value or

constant is written into a pointer to the integer type. Either the variable address

should be most likely written into the pointer, or the value should be written by the

address the pointer refers to.

Consider an example of incorrect code:

void foo()

{

int *a = GetPtr();

int b = 10;

a = b; // <=

Foo(a);

}

In this case, value 10 is assigned to the 'a' pointer. We will actually get an invalid

pointer. To fix this, we should dereference the 'a' pointer or take the address of the

'b' variable.

This is the fixed code:

void foo()

{

int *a = GetPtr();

int b = 10;

*a = b;

Foo(a);

}

The following code variant is correct too:

void foo()

Page 540: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

{

int *a = GetPtr();

int b = 10;

a = &b;

Foo(a);

}

The analyzer considers it safe when a variable of the pointer type is used to store

such magic numbers as -1, 0xcccccccc, 0xbadbeef, 0xdeadbeef, 0xfeeefeee,

0xcdcdcdcd, and so on. These values are often used for the debugging purpose or as

special markers.

Note.

This error is possible only in the C language. In C++, you cannot implicitly cast an

integer value to the pointer (except for 0).

V648. Priority of the '&&' operation is higher than that of the '||' operation.The analyzer has detected a potential error: the priority of the '&&' logical operation

is higher than that of the '||' operation. Programmers often forget this, which causes

the result of a logical expression using these operations to be quite different from

what was expected.

Consider the following sample of incorrect code:

if ( c == 'l' || c == 'L' &&

!( token->subtype & TT_LONG ) )

{ .... }

The programmer most likely expected that equality of the 'c' variable and the value

'l' or 'L' would be checked first, and only then the '&&' operation would be

Page 541: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

executed. But according to the Operation priorities in C/C++, the '&&' operation is

executed first, and only then, the '||' operation.

We recommend that you add parentheses in every expression that contains operators

you use rarely, or whenever you're not sure about the priorities. Even if parentheses

appear to be unnecessary, it's ok. At the same time, you code will become easier to

comprehend and less error-prone.

This is the fixed code:

if ( ( c == 'l' || c == 'L' ) &&

!( token->subtype & TT_LONG ) )

How to get rid of a false warning in case it was this very sequence you actually

intended: first '&&', then '||'?

There are several ways:

1) Bad way. You may add the "//-V648" comment into the corresponding line to

suppress the warning.

if ( c == 'l' || c == 'L' && //-V648

!( token->subtype & TT_LONG ) )

2) Good way. You may write additional parentheses:

if ( c == 'l' || ( c == 'L' &&

!( token->subtype & TT_LONG ) ) )

These will help other programmers understand that the code is correct.

V649. There are two 'if' statements with identical conditional expressions. The first 'if' statement contains function return. This

Page 542: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

means that the second 'if' statement is senseless.The analyzer has detected an issue when the 'then' part of the 'if' operator never gets

control. It happens because there is another 'if' before which contains the same

condition whose 'then' part contains the unconditional 'return' operator. It may

signal both a logical error in the program and an unnecessary second 'if' operator.

Consider the following example of incorrect code:

if (l >= 0x06C0 && l <= 0x06CE) return true;

if (l >= 0x06D0 && l <= 0x06D3) return true;

if (l == 0x06D5) return true; <<<---

if (l >= 0x06E5 && l <= 0x06E6) return true;

if (l >= 0x0905 && l <= 0x0939) return true;

if (l == 0x06D5) return true; <<<---

if (l >= 0x0958 && l <= 0x0961) return true;

if (l >= 0x0985 && l <= 0x098C) return true;

In this case, the 'l == 0x06D5' condition is doubled, and we just need to remove one

of them to fix the code. However, it may be that the value being checked in the

second case should be different from the first one.

This is the fixed code:

if (l >= 0x06C0 && l <= 0x06CE) return true;

if (l >= 0x06D0 && l <= 0x06D3) return true;

if (l == 0x06D5) return true;

if (l >= 0x06E5 && l <= 0x06E6) return true;

if (l >= 0x0905 && l <= 0x0939) return true;

if (l >= 0x0958 && l <= 0x0961) return true;

if (l >= 0x0985 && l <= 0x098C) return true;

The V649 warning may indirectly point to errors of quite a different type. Have a

look at this interesting sample:

Page 543: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

AP4_Result AP4_StscAtom::WriteFields(AP4_ByteStream& stream)

{

AP4_Result result;

AP4_Cardinal entry_count = m_Entries.ItemCount();

result = stream.WriteUI32(entry_count);

for (AP4_Ordinal i=0; i<entry_count; i++) {

stream.WriteUI32(m_Entries[i].m_FirstChunk);

if (AP4_FAILED(result)) return result;

stream.WriteUI32(m_Entries[i].m_SamplesPerChunk);

if (AP4_FAILED(result)) return result;

stream.WriteUI32(m_Entries[i].m_SampleDescriptionIndex);

if (AP4_FAILED(result)) return result;

}

return result;

}

The checks 'if (AP4_FAILED(result)) return result;' in the loop are meaningless.

The error is this: the 'result' variable is not changed when reading data from files.

This is the fixed code:

AP4_Result AP4_StscAtom::WriteFields(AP4_ByteStream& stream)

{

AP4_Result result;

AP4_Cardinal entry_count = m_Entries.ItemCount();

result = stream.WriteUI32(entry_count);

for (AP4_Ordinal i=0; i<entry_count; i++) {

result = stream.WriteUI32(m_Entries[i].m_FirstChunk);

if (AP4_FAILED(result)) return result;

result = stream.WriteUI32(m_Entries[i].m_SamplesPerChunk);

if (AP4_FAILED(result)) return result;

result = stream.WriteUI32(m_Entries[i].m_SampleDescriptionIndex);

if (AP4_FAILED(result)) return result;

}

Page 544: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

return result;

}

V650. Type casting operation is utilized 2 times in succession. Next, the '+' operation is executed. Probably meant: (T1)((T2)a + b).The analyzer has detected a potential error in an expression with address arithmetic.

Addition/subtraction operations are performed over an expression which is a double

type conversion. It may be a misprint: the programmer forgot to put the first type

conversion and addition operation into brackets.

Consider an example of incorrect code:

ptr = (int *)(char *)p + offset_in_bytes;

The programmer was most likely expecting the 'p' variable to be cast to the 'char *'

type, the shift in bytes added to it after that. Then the new pointer was expected to

be cast to the 'int *' type.

But the missing parentheses turn this expression into a double type conversion and

addition of the shift to the 'int'-pointer. The result will be different from the

expected one. Such an error might well cause an array overrun.

This is the fixed code:

ptr = (int *)((char *)p + offset_in_bytes);

V651. An odd operation of the 'sizeof(X)/sizeof(T)' kind is

Page 545: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

performed, where 'X' is of the 'class' type.The analyzer has detected a potential error in an expression of the

'sizeof(X)/sizeof(X[0])' kind. The strange thing is that the 'X' object is a class

instance.

The 'sizeof(X)/sizeof(X[0]) ' is usually used to calculate the number of items in the

'X' array. The error might occur during careless code refactoring. The 'X' variable

was an ordinary array at first and was then replaced with a container class, while

calculation of the items number remained the same.

Consider an example of incorrect code:

#define countof( x ) (sizeof(x)/sizeof(x[0]))

Container<int, 4> arr;

for( int i = 0; i < countof(arr); i++ )

{ .... }

The programmer expected the code to calculate the number of the items of the 'arr'

variable. But the resulting value is the class size divided by the size of the 'int'-

variable. Most likely, this value is not in any way related to the number of data

items being stored in the container.

This is the fixed code:

const size_t count = 4;

Container<int, count> arr;

for( int i = 0; i < arr.size(); i++ )

{ .... }

V652. The operation is executed 3 or more times in succession.

Page 546: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer has detected a potential error: one of the operations '!', '~', '-', or '+' is

repeated three or more times. This error may occur because of a misprint. Doubling

operators like this is meaningless and may contain an error.

Consider the following sample of incorrect code:

if(B &&

C && !!!

D) { .... }

This error must have occurred because of a misprint. For instance, comment

delimiters could have been omitted or an odd operator symbol could have been

typed.

This is the fixed code:

if (B &&

C && //!!!

D) { .... }

The following code variant is correct too:

if (B &&

C && !!D) { .... }

This method is often used to cast integer types to the 'bool' type.

V653. A suspicious string consisting of two parts is used for the initialization. It is possible that a comma is missing.The analyzer has detected a potential error: two strings are concatenated into one

when declaring an array, consisting of string literals. The error may be a

consequence of a misprint when a comma is missing between two string literals. It

Page 547: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

may stay unnoticed for a long time: for example, it reveals itself on rare occasions

when an array of string literals is used to form error messages.

Have a look at an example of incorrect code:

const char *Array [] = {

"Min", "Max", "1",

"Begin", "End" "2" };

A comma is missing between the literals "End" and "2"; that's why they will be

united into one string literal "End2". To fix it, you should separate the string literals

with a comma.

This is the fixed code:

const char *Array [] = {

"Min", "Max", "1",

"Begin", "End", "2" };

The analyzer doesn't generate the warning message if the concatenated string

appears to be too long (more than 50 characters) or consists of more than two

fragments. This method is often used by programmers to format code with long

string literals.

V654. The condition of loop is always true/false.The analyzer has detected an issue when the condition in the 'for' or 'while' operator

is always true or always false. It usually indicates presence of errors. It's highly

probable that the programmer made a misprint when writing the code and this

fragment should be examined.

Consider an example of incorrect code:

for (i = 0; 1 < 50; i++)

{ .... }

Page 548: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

There is a misprint there. In the condition, the constant '1' is written instead of the 'i'

variable. This code is easy to fix:

for (i = 0; i < 50; i++)

{ .... }

The analyzer won't generate the warning message if the condition is defined

explicitly as a constant expression '1' or '0', 'true' or 'false'. For example:

while (true)

{ .... }

V655. The strings were concatenated but are not utilized. Consider inspecting the expression.The analyzer has detected a potential error: an unused concatenation of string

variables in the code was found. The types of these variables are as follows:

std::string, CString, QString, wxString. These expressions most often appear in the

code when an assignment operator is missing or as a result of careless code

refactoring.

Consider the following sample of incorrect code:

void Foo(std::string &s1, const std::string &s2)

{

s1 + s2;

}

The code contains a misprint: '+' is written instead of '+='. The code compiles well

but is senseless. This is the fixed code:

void Foo(std::string &s1, const std::string &s2)

{

s1 += s2;

}

Page 549: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V656. Variables are initialized through the call to the same function. It's probably an error or un-optimized code.The analyzer has detected a potential error: two different variables are initialized by

the same expression. Only those expressions using function calls are considered

dangerous by the analyzer.

Here is the simplest case:

x = X();

y = X();

The following three situations are possible:

1) The code has an error, and we should fix the error by replacing 'X()' with 'Y()'.

2) The code is correct but slow. If the 'X()' function requires too many calculations,

you'd better replace it with 'y = x;'.

3) The code is correct and fast, or the 'X()' function is reading values from the file.

To get rid of false positives produced by the analyzer in this case, we may use the

comment "//-V654".

Now let's take a real-life sample:

while (....)

{

if ( strstr( token, "playerscale" ) )

{

token = CommaParse( &text_p );

skin->scale[0] = atof( token );

skin->scale[1] = atof( token );

continue;

Page 550: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

}

}

There's no error in this code, but it is not the best one. It can be rewritten so that the

unnecessary call of the 'atof' function is eliminated. Considering that the assignment

operation is inside a loop and can be called many times, this change may give a

significant performance gain of the function. This is the fixed code:

while (....)

{

if ( strstr( token, "playerscale" ) )

{

token = CommaParse( &text_p );

skin->scale[1] = skin->scale[0] = atof( token );

continue;

}

}

One more sample:

String path, name;

SplitFilename(strSavePath, &path, &name, NULL);

CString spath(path.c_str());

CString sname(path.c_str());

We definitely have an error here: the 'path' variable is used twice - to initialize the

variables 'spath' and 'sname'. But we can see from the program's logic that the

'name' variable should be used to initialize the 'sname' variable. This is the fixed

code:

....

CString spath(path.c_str());

CString sname(name.c_str());

Page 551: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V657. It's odd that this function always returns one and the same value of NN.The analyzer has detected a strange function: it doesn't have any state and doesn't

change any global variables. At the same time, it has several return points returning

one and the same numerical value.

This code is very odd and might signal a possible error. The function is most likely

intended to return different values.

Consider the following simple example:

int Foo(int a)

{

if (a == 33)

return 1;

return 1;

}

This code contains an error. Let's change one of the returned values to fix it. You

can usually identify the necessary returned values only when you know the

operation logic of the whole application in general

This is the fixed code:

int Foo(int a)

{

if (a == 33)

return 1;

return 2;

}

If the code is correct, you may get rid of the false positive using the "//-V657"

comment.

Page 552: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V658. A value is being subtracted from the unsigned variable. This can result in an overflow. In such a case, the comparison operation can potentially behave unexpectedly.The analyzer has detected a potential overrun.

The following operations are executed:

some value is being subtracted from an unsigned variable;

the result is compared to a certain value (operators <, <=, >, >= are used).

If an overrun occurs during the subtraction, the check result might be different from

what the programmer expects.

Consider the simplest case:

unsigned A = ...;

int B = ...;

if (A - B > 1)

Array[A - B] = 'x';

The programmer believes that this check will protect the code against an array

overrun. But this check won't help if A < B.

Let A = 3 and B = 5;

Then 0x00000003u - 0x00000005i = FFFFFFFEu

The "A - B" expression has the "unsigned int" type according to the C++ standards.

It means that "A - B" will equal FFFFFFFEu. This number is higher than one. As a

result, memory outside the array's boundaries will be addressed.

Page 553: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

There are two ways to fix the code. First, we may use variables of signed types to

participate in calculations:

intptr_t A = ...;

intptr_t B = ...;

if (A - B > 1)

Array[A - B] = 'x';

Second, we can change the condition. How exactly it should be done depends on the

result we want to get and the input values. If B >= 0, we just need to write the

following code:

unsigned A = ...;

int B = ...;

if (A > B + 1)

Array[A - B] = 'x';

If the code is correct, you may turn off the diagnostic message for this line using the

"//-V658" comment.

V659. Declarations of functions with 'Foo' name differ in the 'const' keyword only, but the bodies of these functions have different composition. This is suspicious and can possibly be an error.The analyzer has detected two functions with identical names in the code. The

functions are different in the constancy parameter.

Function declarations may differ in:

the constancy of the returned value;

the constancy of arguments;

Page 554: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

the constancy of the function itself (in case of class methods).

Although the names of the functions coincide, they work differently. It may be a

sign of an error.

Consider a simple case:

class CLASS {

DATA *m_data;

public:

char operator[](size_t index) const {

if (!m_data || index >= m_data->len)

throw MyException;

return m_data->data[index];

}

char &operator[](size_t index) {

return m_data->data[index];

}

};

The constant function 'operator[]' contains a check so that an exception is thrown in

case of an error. A non-constant function doesn't contain such a check. This is most

likely a slip-up that should be fixed.

The analyzer takes into account a set of different situations when the differences in

function bodies are reasonable. But we cannot account for all the exceptional cases.

So, if the analyzer has generated a false positive, you can suppress it using the "//-

V659" comment.

V660. The program contains an unused label and a function call: 'CC:AA()'. It's possible that the following was intended: 'CC::AA()'.

Page 555: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer has detected a potential error when the programmer makes a misprint

writing ':' instead of '::'.

An unused label is found in the code of a class method. This label is followed by a

function call. The analyzer considers it dangerous when a function with such a

name is placed inside one of the base classes.

Consider the following sample:

class Employee {

public:

void print() const {}

};

class Manager: public Employee {

void print() const;

};

void Manager::print() const {

Employee:print();

}

The line 'Employee:print();' is very likely to be incorrect. The error is this: unlike it

was intended, the function from the own class 'Manager' is called instead of the

function from the 'Employee' class. To fix the error we just need to replace ':' with

'::'.

This is the fixed code:

void Manager::print() const {

Employee::print();

}

Here's one more sample:

namespace Abcd

{

void Foo() {}

Page 556: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

}

class Employee {

void Foo() {}

void X() { Abcd:Foo(); }

};

The error here is this: the function within the scope of 'Abcd' should have been

called. This error is easy to fix:

void X() { Abcd::Foo(); }

V661. A suspicious expression 'A[B < C]'. Probably meant 'A[B] < C'.The analyzer has detected a suspicious code fragment where an array item is being

accessed. A logical expression is used as an array index.

Here are examples of such expressions: Array[A >= B], Array[A != B]. Perhaps the

closing square bracket is in the wrong place. These errors usually occur through

misprints.

Consider an example of incorrect code:

if ((bs->inventory[INVENTORY_ROCKETLAUNCHER] <= 0 ||

bs->inventory[INVENTORY_ROCKETS < 10]) && <<== ERROR!

(bs->inventory[INVENTORY_RAILGUN] <= 0 ||

bs->inventory[INVENTORY_SLUGS] < 10)) {

return qfalse;

}

This code is compilable but works incorrectly. It's highly probable that the

following text should be written instead:

if ((bs->inventory[INVENTORY_ROCKETLAUNCHER] <= 0 ||

bs->inventory[INVENTORY_ROCKETS] < 10) &&

(bs->inventory[INVENTORY_RAILGUN] <= 0 ||

Page 557: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

bs->inventory[INVENTORY_SLUGS] < 10)) {

return qfalse;

}

Note. The analyzer doesn't generate the warning all the time a logical expression is

placed inside square brackets. It is sometimes justified. For instance, such an

exception is the case when an array consists of only two items:

int A[2];

A[x != y] = 1;

V662. Consider inspecting the loop expression. Different containers are utilized for setting up initial and final values of the iterator.The analyzer has detected a suspicious loop. The A container is used to initialize the

iterator. Then this iterator is compared to the end of the B container. It's highly

probable that it is a misprint and the code is incorrect.

Here is a sample for which this warning will be generated:

void useVector(vector<int> &v1, vector<int> &v2)

{

vector<int>::iterator it;

for (it = v1.begin(); it != v2.end(); ++it)

*it = rand();

....

}

The array is being filled in the 'for' loop. Different variables (v1 and v2) are used to

initialize the iterator and to check the bounds. If the references v1 and v2 actually

point to different arrays, it will cause an error at the program execution stage.

Page 558: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The error is very easy to fix. You need to use one and the same container in the both

cases. This is the fixed code:

void useVector(vector<int> &v1, vector<int> &v2)

{

vector<int>::iterator it;

for (it = v1.begin(); it != v1.end(); ++it)

*it = rand();

....

}

If the variables v1 and v2 refer to one and the same container, the code is correct.

You can use the false positive suppression mechanism of analyzer in this case.

However, code refactoring seems a better solution to this issue. The current code

may confuse not only the analyzer, but also those programmers who will maintain it

in the future.

V663. Infinite loop is possible. The 'cin.eof()' condition is insufficient to break from the loop. Consider adding the 'cin.fail()' function call to the conditional expression.The analyzer has detected a potential error that may lead to an infinite loop. When

you deal with the 'std::istream' class, calling the 'eof()' function is not enough to

terminate the loop. If data reading fails, a call of the 'eof()' function will always

return 'false'. You need an additional check of the value returned by the 'fail()'

function to terminate the loop in this case.

Have a look at an example of incorrect code:

while (!cin.eof())

{

Page 559: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

int x;

cin >> x;

}

You can fix the error by making the condition a bit more complex:

while (!cin.eof() && !cin.fail())

{

int x;

cin >> x;

}

V664. The pointer is being dereferenced on the initialization list before it is verified against null inside the body of the constructor function.The pointer is being dereferenced in the constructor initialization list and then

checked inside the constructor body for not being a null pointer. It may signal a

hidden error that may stay unnoticed for a long time.

Consider a sample of incorrect code:

Layer(const Canvas *canvas) :

about(canvas->name, canvas->coord)

{

if (canvas)

{

....

}

}

Page 560: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

When dereferencing a null pointer, undefined behavior occurs, i.e. normal execution

of the program becomes impossible. To fix the error you should move the

initialization operation into the constructor body in the code block where the pointer

is known to not be equal to zero. Here is the fixed code:

Layer(const Canvas *canvas)

{

if (canvas)

{

about.set(canvas->name, canvas->coord);

}

}

V665. Possibly, the usage of '#pragma warning(default: X)' is incorrect in this context. The '#pragma warning(push/pop)' should be used instead.The analyzer has detected an incorrect sequence of '#pragma warning' directives in

the code.

Programmers often assume that warnings disabled with the "pragma

warning(disable: X)" directive earlier will start working again after using the

"pragma warning(default : X)" directive. It's not so. The 'pragma warning(default :

X)' directive sets the 'X' warning to the DEFAULT state which is quite not the same

thing.

Imagine that a file is compiled with the /Wall switch used. The C4061 warning must

be generated in this case. If you add the "#pragma warning(default : 4061)"

directive, this warning will not be displayed, as it is turned off by default.

Page 561: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The correct way to return the previous state of a warning is to use directives

"#pragma warning(push[ ,n ])" and "#pragma warning(pop)". See the Visual C++

documentation for descriptions of these directives: Pragma Directives. Warnings.

Here's an example of incorrect code:

#pragma warning(disable: 4001)

....

//Correct code triggering the 4001 warning

....

#pragma warning(default: 4001)

The 4001 warning will be set to the default state in this sample. But the programmer

must have intended to return the previous state used before it had been disabled. For

this purpose, we should use the 'pragma warning(push)' directive before turning off

the warning and the 'pragma warning(pop)' directive after the correct code.

This is the fixed code:

#pragma warning(push)

#pragma warning(disable: 4001)

....

// Correct code triggering the 4001 warning

....

#pragma warning(pop)

Library developers should pay special attention to the V665 warning. Careless

warning customization may cause a whole lot of troubles on the library users' side.

Good article about this theme: "So, You Want to Suppress This Warning in Visual

C++".

V666. Consider inspecting NN argument of the function 'Foo'. It is possible that the value does not

Page 562: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

correspond with the length of a string which was passed with the YY argument.The analyzer suspects that an incorrect argument has been passed into a function.

An argument whose numerical value doesn't coincide with the string length found in

the previous argument is considered incorrect. The analyzer draws this conclusion

examining pairs of arguments consisting of a string literal and an integer constant.

Analysis is performed over all the function calls of the same name.

Here's an example of incorrect code:

if (!_strnicmp(szDir, "My Documents", 11)) // <<== Error!

nFolder = 1;

if (!_strnicmp(szDir, "Desktop", 7))

nFolder = 2;

if (!_strnicmp(szDir, "Network Favorites", 17))

nFolder = 3;

In this case, the value 11 in the first function call is incorrect. Because of that,

comparison will be successful if the 'szDir' variable points to the string literal "My

Document". To fix the code you should just change the string length to a correct

value, i.e. 12.

This is the fixed code:

if (!_strnicmp(szDir, "My Documents", 12))

nFolder = 1;

The V666 diagnostic is of empirical character. If you want to understand the point

of it, you will have to read a complicated explanation. It's not obligatory, but if you

choose not to read, then please check the function arguments very attentively. If you

are sure that the code is absolutely correct, you may disable the diagnostic message

output by adding the comment "//-V666".

Page 563: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Let's try to figure out how this diagnostic rule works. Look at the following code:

foo("1234", 1, 4);

foo("123", 2, 3);

foo("321", 2, 2);

The analyzer will choose pairs of arguments: a string literal and a numerical value.

For these, the analyzer will examine all the calls of this function and build a table of

coincidence between the string length and numerical argument.

{ { "1234", 1 }, { "1234", 4 } } -> { false, true }

{ { "123", 2 }, { "123", 3 } } -> { false, true }

{ { "321", 2 }, { "321", 2 } } -> { false, false }

The first column is of no interest to us. It doesn't seem to be the string length. But

the second column seems to represent the string length, and one of the calls contains

an error.

This description is pretty sketchy, of course, but it allows you to grasp the general

principle behind the diagnostic. Such an analysis is certainly not ideal, and false

positives are inevitable. But it also lets you find interesting bugs sometimes.

V667. The 'throw' operator does not possess any arguments and is not situated within the 'catch' block.The analyzer has detected that the 'throw' operator doesn't have arguments and is

not located inside the 'catch' block. This code may be an error. The 'throw' operator

without arguments is used inside the 'catch' block to pass on an exception it has

caught to the upper level. According to the standard, a call of the 'throw' operator

without an argument will cause the 'std::terminate()' function to be called if the

exception is still not caught. It means that the program will be terminated.

Here's an example of incorrect code:

Page 564: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

try

{

if (ok)

return;

throw;

}

catch (...)

{

}

We should pass the argument to the 'throw' operator to fix the error.

This is the fixed code:

try

{

if (ok)

return;

throw exception("Test");

}

catch (...)

{

}

However, calling the 'throw' operator outside the 'catch' block is not always an error.

For example, if a function is being called from the 'catch' block and serves to pass

on the exception to the upper level, no error will occur. But the analyzer may fail to

distinguish between the two ways of behavior and will generate the diagnostic

message for both. This is an example of such code:

void error()

{

try

{

....

if (ok)

return;

Page 565: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

throw; <<== no error here actually

}

catch (...)

{

throw;

}

}

void foo()

{

try

{

....

if (ok)

return;

throw exception("Test");

}

catch (...)

{

error();

}

}

In this case you may suppress the diagnostic message output by adding the

comment '//-V667'.

V668. There is no sense in testing the pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error.

Page 566: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer has detected an issue when the value of the pointer returned by the

'new' operator is compared to zero. It usually means that the program will behave in

an unexpected way if memory cannot be allocated.

If the 'new' operator has failed to allocate memory, the exception std::bad_alloc() is

thrown, according to the C++ standard. It's therefore pointless to check the pointer

for being a null pointer. Take a look at a simple example:

MyStatus Foo()

{

int *p = new int[100];

if (!p)

return ERROR_ALLOCATE;

...

return OK;

}

The 'p' pointer will never equal zero. The function will never return the constant

value ERROR_ALLOCATE. If memory cannot be allocated, an exception will be

generated. We may choose to fix the code in the simplest way:

MyStatus Foo()

{

try

{

int *p = new int[100];

...

}

catch(const std::bad_alloc &)

{

return ERROR_ALLOCATE;

}

return OK;

}

Note, however, that the fixed code shown above is very poor. The philosophy of

exception handling is quite different: it is due to the fact that they allow us to avoid

Page 567: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

numerous checks and returned statuses that exceptions are used. We should rather

let the exception leave the 'Foo' function and process it somewhere else at a higher

level. Unfortunately, discussion of how to use exceptions lies outside the scope of

the documentation.

Let's see what such an error may look like in real life. Here's a code fragment taken

from a real-life application:

// For each processor; spawn a CPU thread to access details.

hThread = new HANDLE [nProcessors];

dwThreadID = new DWORD [nProcessors];

ThreadInfo = new PTHREADINFO [nProcessors];

// Check to see if the memory allocation happenned.

if ((hThread == NULL) ||

(dwThreadID == NULL) ||

(ThreadInfo == NULL))

{

char * szMessage = new char [128];

sprintf(szMessage,

"Cannot allocate memory for "

"threads and CPU information structures!");

MessageBox(hDlg, szMessage, APP_TITLE, MB_OK|MB_ICONSTOP);

delete szMessage;

return false;

}

The user will never see the error message window. If memory cannot be allocated,

the program will crash or generate an inappropriate message, having processed the

exception in some other place.

A common reason for issues of that kind is a change of the 'new' operator's

behavior. In the times of Visual C++ 6.0, the 'new' operator is return NULL in case

of an error. Later Visual C++ versions follow the standard and generate an

exception. Keep this behavior change in mind. Thus, if you are adapting an old

project for building it by a contemporary compiler, you should be especially

attentive to the V668 diagnostic.

Page 568: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Note N1. The analyzer will not generate the warning if placement new or "new

(std::nothrow) T" is used. For example:

T * p = new (std::nothrow) T; // OK

if (!p) {

// An error has occurred.

// No storage has been allocated and no object constructed.

...

}

Note N2. You can link your project with nothrownew.obj. The 'new' operator won't

throw an exception in this case. Driver developers, for instance, employ this

capability. For details see: new and delete operators. Just turn off the V668 warning

in this case.

References:

1. Wikipedia. Placement syntax.

2. Microsoft Support. Operator new does not throw a bad_alloc exception on failure in Visual C++.

3. StackOverflow. Will new return NULL in any case?

V669. The argument is a non-constant reference. The analyzer is unable to determine the position at which this argument is being modified. It is possible that the function contains an error.The analyzer has detected that an argument is being passed by reference into a

function but not modified inside the function body. This may indicate an error

which is caused, for example, by a misprint. Consider a sample of incorrect code:

Page 569: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

void foo(int &a, int &b, int c)

{

a = b == c;

}

Because of a misprint, the assignment operator ('=') has turned into the comparison

operator ('=='). As a result, the 'b' variable is used only for reading, although this is

a non-constant reference. The way of fixing the code is chosen individually in each

particular case. The important thing is that such a code requires more thorough

investigation.

This is the fixed code:

void foo(int &a, int &b, int c)

{

a = b = c;

}

Note. The analyzer might make mistakes when trying to figure out whether or not a

variable is modified inside the function body. If you get an obvious false positive,

please send us the corresponding code fragment for us to study it.

You may also add the comment "//-V669" to suppress the false positive in a

particular line.

V670. An uninitialized class member is used to initialize another member. Remember that members are initialized in the order of their declarations inside a class.The analyzer has detected a possible error in the class constructor's initialization list.

According to the language standard, class members are initialized in the constructor

in the same order as they are declared inside the class. In our case, the program

Page 570: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

contains a constructor where initialization of one class member depends on the

other. At the same time, a variable used for initialization is not yet initialized itself.

Here is an example of such a constructor:

class Foo

{

int foo;

int bar;

Foo(int i) : bar(i), foo(bar + 1) { }

};

The 'foo' variable is initialized first! The variable 'bar' is not yet initialized at this

moment. To fix the bug, we need to put the declaration of the 'foo' class member

before the declaration of the 'bar' class member. This is the fixed code:

class Foo

{

int bar;

int foo;

Foo(int i) : bar(i), foo(bar + 1) { }

};

If the sequence of class fields cannot be changed, you need to change the

initialization expressions:

class Foo

{

int foo;

int bar;

Foo(int i) : bar(i), foo(i + 1) { }

};

V671. It is possible that the 'swap' function interchanges a variable with itself.

Page 571: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer has detected a potential error that may occur when calling the 'swap'

function. The function receives identical actual arguments, which is very strange.

The programmer must have made a misprint.

Have a look at this example:

int arg1, arg2;

....

swap(arg1, arg1);

....

A misprint causes the swap() function to swap the value of the 'arg1' variable for

itself. The code should be fixed in the following way:

swap(arg1, arg2);

The following sample is also considered suspicious:

MyClass arg1, arg2;

....

arg1.Swap(arg1);

....

It can be fixed in the following way:

arg1.Swap(arg2);

V672. There is probably no need in creating a new variable here. One of the function's arguments possesses the same name and this argument is a reference.The analyzer has detected a possible error: a variable is being declared whose name

coincides with that of one of the arguments. If the argument is a reference, the

Page 572: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

whole situation is quite strange. The analyzer also imposes some other conditions to

reduce the number of false positives, but there's no point describing them in the

documentation.

To understand this type of errors better, have a look at the following sample:

bool SkipFunctionBody(Body*& body, bool t)

{

body = 0;

if (t)

{

Body *body = 0;

if (!SkipFunctionBody(body, true))

return false;

body = new Body(body);

return true;

}

return false;

}

The function requires a temporary variable to handle the SkipFunctionBody ()

function. Because of inattention, the programmer once again declares a temporary

variable 'body' inside the 'if' block. It means that this local variable will be modified

inside the 'if' block instead of the 'body' argument. When leaving the function, the

'body' variable's value will be always NULL. The error might reveal itself further,

somewhere else in the program, when null pointer dereferencing takes place. We

need to create a local variable with a different name to fix the error. This is the fixed

code:

bool SkipFunctionBody(Body*& body, bool t)

{

body = 0;

if (t)

{

Body *tmp_body = 0;

if (!SkipFunctionBody(tmp_body, true))

Page 573: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

return false;

body = new Body(tmp_body);

return true;

}

return false;

}

V673. More than N bits are required to store the value, but the expression evaluates to the T type which can only hold K bits.The analyzer has detected a potential error in an expression using shift operations.

Shift operations cause an overflow and loss of the high-order bits' values.

Let's start with a simple example:

std::cout << (77u << 26);

The value of the "77u << 26" expression equals 5167382528 (0x134000000) and is

of the 'int' type at the same time. It means that the high-order bits will be truncated

and you'll get the value 872415232 (0x34000000) printed on the screen.

Overflows caused by shift operations usually indicate a logic error or misprint in the

code. It may be, for example, that the programmer intended to define the number

'77u' as an octal number. If this is the case, the correct code should look like this:

std::cout << (077u << 26);

No overflow occurs now; the value of the "77u << 26" expression is 4227858432

(0xFC000000).

If you need to have the number 5167382528 printed, the number 77 must be defined

as a 64-bit type. For example:

Page 574: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

std::cout << (77ui64 << 26);

Now let's see what errors we may come across in real life. The two samples shown

below are taken from real applications.

Example 1.

typedef __UINT64 Ipp64u;

#define MAX_SAD 0x07FFFFFF

....

Ipp64u uSmallestSAD;

uSmallestSAD = ((Ipp64u)(MAX_SAD<<8));

The programmer wants the value 0x7FFFFFF00 to be written into the 64-bit

variable uSmallestSAD. But the variable will store the value 0xFFFFFF00 instead,

as the high-order bits will be truncated because of the MAX_SAD<<8 expression

being of the 'int' type. The programmer knew that and decided to use an explicit

type conversion. Unfortunately, he made a mistake when arranging parentheses.

This is a good example to demonstrate that such bugs can easily be caused by

ordinary mistakes. This is the fixed code:

uSmallestSAD = ((Ipp64u)(MAX_SAD))<<8;

Example 2.

#define MAKE_HRESULT(sev,fac,code) \

((HRESULT) \

(((unsigned long)(sev)<<31) | \

((unsigned long)(fac)<<16) | \

((unsigned long)(code))) )

*hrCode = MAKE_HRESULT(3, FACILITY_ITF, messageID);

The function must generate an error message in a HRESULT-variable. The

programmer uses the macro MAKE_HRESULT for this purpose, but in a wrong

way. He suggested that the range for the first argument 'severity' was to be from 0 to

Page 575: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

3 and must have mixed these figures up with the values needed for the mechanism

of error code generation used by the functions GetLastError()/SetLastError().

The macro MAKE_HRESULT can only take either 0 (success) or 1 (failure) as the

first argument. For details on this issue see the topic on the CodeGuru website's

forum: Warning! MAKE_HRESULT macro doesn't work.

Since the number 3 is passed as the first actual argument, an overflow occurs. The

number 3 "turns into" 1, and it's only thanks to this that the error doesn't affect

program execution. I've given you this example deliberately just to show that it's a

frequent thing when your code works because of mere luck, not because it is

correct.

The fixed code:

*hrCode = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, messageID);

V674. The expression contains a suspicious mix of integer and real types.The analyzer has detected a potential error in an expression where integer and real

data types are used together. Real types are data types such as float/double/long

double.

Let's start with a simple case. A literal of the 'double' type is implicitly cast to an

integer, which may indicate a software bug in the code.

int a = 1.1;

This fragment is meaningless. The variable should be most likely initialized with

some other value.

The example shown above is an artificial one and therefore of no interest to us. Let's

examine some real-life cases.

Page 576: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Example 1.

int16u object_layer_width;

int16u object_layer_height;

if (object_layer_width == 0 ||

object_layer_height == 0 ||

object_layer_width/object_layer_height < 0.1 ||

object_layer_width/object_layer_height > 10)

An integer value is compared to the constant '0.1', and that's very strange. Assume

the variables have the following values:

object_layer_width = 20;

object_layer_height = 100;

The programmer expects that division of these numbers will give '0.2'; it fits into

the range [0.1..10].

But in fact the division result will be 0. Division is performed over integer data

types, and though the result is extended to the type 'double' when compared to '0.1' a

bit later, it is too late. To fix the code we need to perform an explicit type

conversion beforehand:

if (object_layer_width == 0 ||

object_layer_height == 0 ||

(double)object_layer_width/object_layer_height < 0.1 ||

(double)object_layer_width/object_layer_height > 10.0)

Example 2.

// be_aas_reach.c

ladderface1vertical =

abs( DotProduct( plane1->normal, up ) ) < 0.1;

The argument of the abs() function is of the 'double' type. The code seems to

execute correctly at first sight, and one may think it was just "silly" of the analyzer

to attack this good code.

Page 577: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

But let's examine the issue closer. Look how the function abs() is declared in header

files.

int __cdecl abs( int _X);

#ifdef __cplusplus

extern "C++" {

inline long __CRTDECL abs(__in long _X) { .... }

inline double __CRTDECL abs(__in double _X) { .... }

inline float __CRTDECL abs(__in float _X) { .... }

}

#endif

Yes, abs() functions are overloaded for different types in C++. But we are dealing

with a C code (see the file: be_aas_reach.c).

It means that a 'float'-type expression will be implicitly cast to the 'int' type. The

abs() function will also return a value of the 'int' type. Comparing a value of the 'int'

type to '0.1' is meaningless. And this is what analyzer warns you about.

In C applications, you need another function to calculate the absolute value

correctly:

double __cdecl fabs(__in double _X);

The fixed code:

ladderface1vertical =

fabs( DotProduct( plane1->normal, up ) ) < 0.1;

V675. Writing into the read-only memory.The analyzer has detected an attempt of writing into read-only memory.

Have a look at the following sample:

char *s = "A_string";

Page 578: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

if (x)

s[0] = 'B';

The pointer 's' refers to a memory area which is read-only. Changing this area will

cause undefined behavior of the program which will most probably take form of

an access violation.

This is the fixed code:

char s[] = "A_string";

if (x)

s[0] = 'B';

The 's' array is created on the stack, and a string from read-only memory is copied

into it. Now you can safely change the 's' string.

P.S.

If "A_string" is "const char *", why should this type be implicitly cast to "char *"?

This is done due to compatibility reasons. There exists a TOO large amount of

legacy code in C where non-constant pointers are used, and C++ standard/compiler

developers didn't dare to break the backward compatibility with that code.

V676. It is incorrect to compare the variable of BOOL type with TRUE.The analyzer has detected an issue when a BOOL value is compared to the TRUE

constant (or 1). This is a potential error, since the value "true" may be presented by

any non-zero number.

Let's recall the difference between the types 'bool' and 'BOOL'.

The following construct:

bool x = ....;

if (x == true) ....

Page 579: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

is absolutely correct. The 'bool' type may take only two values: true and false.

When dealing with the BOOL type, such checks are inadmissible. The BOOL type

is actually the 'int' type, which means that it can store values other than zero and

one. Any non-zero value is considered to be "true".

Values other than 1 may be returned, for example, by functions from Windows

SDK.

The constants FALSE/TRUE are declared in the following way:

#define FALSE 0

#define TRUE 1

It means that the following comparison may fail:

BOOL ret = Some_SDK_Function();

if (TRUE == ret)

{

// do something

}

It is not guaranteed that it is 1 that the function Some_SDK_Function() will return,

if executed successfully. The correct code should look this:

if (FALSE != ret)

or:

if (ret)

For more information on this subject, I recommend you to study FAQ on the

website CodeGuru: Visual C++ General: What is the difference between 'BOOL'

and 'bool'?

When found in a real application, the error may look something like this:

if (CDialog::OnInitDialog() != TRUE )

return FALSE;

Page 580: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The CDialog::OnInitDialog() function's description reads:

If OnInitDialog returns nonzero, Windows sets the input focus to the default

location, the first control in the dialog box. The application can return 0 only if it

has explicitly set the input focus to one of the controls in the dialog box.

Notice that there is not a word about TRUE or 1. The fixed code should be like this:

if (CDialog::OnInitDialog() == FALSE)

return FALSE;

This code may run successfully for a long time, but no one can say for sure that it

will be always like that.

A few words concerning false positives. The programmer may be sometimes

absolutely sure that a BOOL variable will always have 0 or 1. In this case, you may

suppress a false positive using one of the several techniques. However, you'd still

better fix your code: it will be more reliable from the viewpoint of future

refactoring.

This diagnostic is close to the V642 diagnostic.

V677. Custom declaration of a standard type. The declaration from system header files should be used instead.The analyzer has found a custom declaration of a standard data type in your

program. This is an excessive code which may potentially cause errors. You should

use system files containing declarations of the standard types.

Below is an example of incorrect type declaration:

typedef unsigned *PSIZE_T;

Page 581: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The PSIZE_T type is declared as a pointer to the 'unsigned' type. This declaration

may cause issues when trying to build a 64-bit application: the program won't

compile or will behave in a different way than expected. This is how the PSIZE_T

type is declared in the file "BaseTsd.h": "typedef ULONG_PTR SIZE_T,

*PSIZE_T;". You should include the corresponding header file instead of changing

the type declaration.

This is the fixed code:

#include <BaseTsd.h>

V678. An object is used as an argument to its own method. Consider checking the first actual argument of the 'Foo' function.The analyzer has detected a call function of the following kind:

A.Foo(A);

This code may probably contain an error. For example, an incorrect variable name

is used because of a misprint. The correct code should look like this then:

A.Foo(B);

or like this:

B.Foo(A);

Let's see how such misprints may affect the code in real life. Here's a fragment from

a real application:

CXMLAttribute* pAttr1 =

m_pXML->GetAttribute(CXMLAttribute::schemaName);

CXMLAttribute* pAttr2 =

Page 582: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

pXML->GetAttribute(CXMLAttribute::schemaName);

if ( pAttr1 && pAttr2 &&

!pAttr1->GetValue().CompareNoCase(pAttr1->GetValue()))

....

This code should compare two attributes. But a misprint causes the value "pAttr1-

>GetValue()" to be compared to itself.

This is the fixed code:

if ( pAttr1 && pAttr2 &&

!pAttr1->GetValue().CompareNoCase(pAttr2->GetValue()))

V679. The 'X' variable was not initialized. This variable is passed by a reference to the 'Foo' function in which its value will be utilized.The analyzer has detected an issue when an uninitialized variable is being passed

into a function by reference or by pointer. The function tries to read a value from

this variable.

Here is an example.

void Copy(int &x, int &y)

{

x = y;

}

void Foo()

{

int x, y;

x = 1;

Copy(x, y);

}

Page 583: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

This is a very simple artificial sample, of course, but it explains the point very well.

The 'y' variable is uninitialized. A reference to this variable is passed into the

Copy() function which tries to read from this uninitialized variable.

The fixed code may look like this:

void Copy(int &x, int &y)

{

x = y;

}

void Foo()

{

int x, y;

y = 1;

Copy(x, y);

}

V680. The 'delete A, B' expression only destroys the 'A' object. Then the ',' operator returns a resulting value from the right side of the expression.The analyzer has detected a strange construct of the following pattern:

delete p1, p2;

It could have been written by an unskillful programmer or a programmer who has

not dealt with C++ for a long time. At first you might think that this code deletes

two objects whose addresses are stored in the pointers 'p1' and 'p2'. But actually we

have two operators here: one is 'delete', the other is the comma operator ','.

The 'delete' operator is executed first, and then the ',' operator returns the value of

the second argument (i.e. 'p2').

Page 584: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

In other words, this construct is identical to this one: (delete p1), p2;

The correct code should look like this:

delete p1;

delete p2;

Note. The analyzer won't generate the warning if the comma operator is used

deliberately for certain purposes. Here's an example of safe code:

if (x)

delete p, p = nullptr;

After deleting the object, the pointer is set to null. The ',' operator is used to unite

the two operations so that one doesn't have to use curly braces.

V681. The language standard does not define an order in which the 'Foo' functions will be called during evaluation of arguments.The analyzer has detected a potential error in a sequence of function calls.

According to the C++ standard, the order of calculating a function's actual

arguments is not determined. In the expression 'A(B(), C())', you can't tell for sure

which of the two functions 'B()' and 'C()' will be called first - it depends on the

compiler, compilation parameters, and so on.

This may cause troubles on rare occasions. The analyzer warns you about code

fragments that look most strange. Unfortunately, we had to deliberately limit the

number of occasions this warning is generated to avoid too many false positives.

You see, actual arguments are too often represented by calls of other functions,

which is in most cases absolutely safe.

Here's an example of code PVS-Studio will warn you about:

Page 585: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Point ReadPoint()

{

return Point(ReadFixed(), ReadFixed());

}

This code may cause the X and Y values to be swapped, since it is not known which

of the two will be calculated first.

This is the fixed code:

Point ReadPoint()

{

float x = ReadFixed();

return Point(x, ReadFixed());

}

V682. Suspicious literal is present: '/r'. It is possible that a backslash should be used here instead: '\r'.The analyzer has detected a potential error when a forward slash is used.

It's easy to make a mistake mixing up the forward slash and backward slash

characters.

For example:

if (x == '/n')

The programmer intended to compare the variable 'x' to the code 0xA (line feed) but

made a mistake and wrote a forward slash. It results in the variable being compared

to the value 0x2F6E.

This is the fixed code:

if (x == '\n')

Page 586: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Such a mistake is usually made when working with the following escape sequences:

newline - \n

horizontal tab - \t

vertical tab - \v

backspace - \b

carriage return - \r

form feed - \f

alert - \a

backslash - \\

the null character - \0

References:1. MSDN. C++ Character Literals

V683. Consider inspecting the loop expression. It is possible that the 'i' variable should be incremented instead of the 'n' variable.The analyzer has detected a potential error in a loop: there may be a typo which

causes a wrong variable to be incremented/decremented.

For example:

void Foo(float *Array, size_t n)

{

for (size_t i = 0; i != n; ++n)

{

....

}

}

Page 587: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The variable 'n' is incremented instead of the variable 'i'. It results in an unexpected

program behavior.

This is the fixed code:

for (size_t i = 0; i != n; ++i)

V684. A value of variable is not modified. Consider inspecting the expression. It is possible that '1' should be present instead of '0'.The analyzer has detected a suspicious expression which is used to change certain

bits of a variable, but the variable actually remains unchanged.

Here is an example of suspicious code:

MCUCR&=~(0<<SE);

This code is taken from the firmware for the ATtiny2313 microcontroller. The SE

bit must be set to one so that the microcontroller switches to sleep mode when

receiving the SLEEP command. To avoid accidental switch to sleep mode, it is

recommended to set the SE bit to one immediately before calling the SLEEP

command and reset it after wake-up. It is this reset on wake-up that the programmer

wanted to implement. But he made a typo causing the value of the MCUCR register

to remain unchanged. So it appears that although the program works, it is not

reliable.

This is the fixed code:

MCUCR&=~(1<<SE);

Note. Sometimes the V684 warning generates a set of multiple false positives. These

are usually triggered by large and complex macros. See the corresponding section

Page 588: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

of the documentation to find out the methods of suppressing false positives in

macros.

V685. Consider inspecting the return statement. The expression contains a comma.The analyzer has found that a value returned by a function might be incorrect as it

contains the comma operator ','. This is not necessarily an error, but this code should

be checked.

Here is an example of suspicious code:

int Foo()

{

return 1, 2;

}

The function will return the value 2. The number 1 is redundant in this code and

won't affect the program behavior in any way.

If it is just a typo, the redundant value should be eliminated:

int Foo()

{

return 2;

}

But it may be possible sometimes that such a return value contains a genuine error.

That's why the analyzer tracks such constructs. For example, a function call may

have been accidentally removed during refactoring.

If this is the case, the code can be fixed in the following way:

int Foo()

{

Page 589: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

return X(1, 2);

}

Comma is sometimes useful when working with the 'return' operator. For example,

the following code can be shortened by using a comma.

The lengthy code:

if (A)

{

printf("hello");

return X;

}

The shorter code:

if (A)

return printf("hello"), X; // No warning triggered

We do not find the shorter code version smart and do not recommend using it.

However, this is a frequent practice and such code does have sense, so the analyzer

doesn't generate the warning if the expression to the left of the comma affects the

program behavior.

V686. A pattern was detected: A || (A && ...). The expression is excessive or contains a logical error.The analyzer has detected an expression that can be simplified. In some cases, it

may also mean that such an expression contains a logical error.

Here is an example of suspicious code:

int k,n,j;

...

if (n || (n && j))

Page 590: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

This expression is redundant. If "n==0", the condition is always false. If "n!=0", the

condition is always true. That is, the condition does not depend on the 'j' variable

and therefore can be simplified:

if (n)

Sometimes such redundancy may indicate a typo. Imagine, for instance, that the

condition must actually be like this one:

if (k || (n && j))

Now, the following is a more realistic example which actually caused us to

implement this diagnostic:

const char *Name = ....;

if (Name || (Name && Name[0] == 0))

Here we have both an error and redundancy. The condition must be executed if the

string referred to by the 'Name' pointer is empty. An empty string can be referred to

by a null pointer.

Because of a mistake, the condition will be executed whenever Name != nullptr.

This is the fixed code:

if (!Name || (Name && Name[0] == 0))

We've got rid of the error, but we can also eliminate unnecessary check:

if (!Name || Name[0] == 0)

V687. Size of an array calculated by the sizeof() operator was added to a pointer. It is possible that the number of elements should be calculated by sizeof(A)/sizeof(A[0]).

Page 591: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer has detected an issue when an array size is added to a pointer, which is

strange. Perhaps it is an error, and it is actually the number of the array items

instead of its size that should be added to the pointer.

Note. It is safe to work with arrays consisting of bytes (char/unsigned char).

An example of the error:

int A[10];

...

std::sort(A, A + sizeof(A));

The function's first argument is a random-access iterator addressing the position of

the first element in the range to be sorted.

The function's second argument is a random-access iterator addressing the position

one past the final element in the range to be sorted.

The function call is incorrect: by mistake, the array size is added to the pointer

which results in the function trying to sort more elements than necessary.

To fix the bug, the code should be rewritten so that the pointer is summed with the

number of array items:

int A[10];

...

std::sort(A, A + sizeof(A) / sizeof(A[0]));

V688. The 'foo' local variable possesses the same name as one of the class members, which can result in a confusion.The analyzer has detected an issue when the name of a local variable coincides with

the name of a class member. It is not an error in most cases, but such code may be

Page 592: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

EXTREMELY dangerous as it is exposed to errors that may occur after refactoring.

The programmer assumes he is working with a class member while actually using

the local variable.

An example of the error:

class M

{

int x;

void F() { int x = 1; foo(x); }

....

};

The class contains a member named 'x'. The same name is used for the local

variable in the F() function.

The error is clearly seen in a small sample like that, so you may find the V688

diagnostic uninteresting. But when you work with large functions, such a careless

choice of names for variables may cause much trouble to developers maintaining

the code.

We just need to choose another name for the local variable to avoid the error:

class M

{

int x;

void F() { int value = 1; foo(value); }

....

};

Another solution is to use the 'm_' prefix in the names of class members:

class M

{

int m_x;

void F() { int x = 1; foo(x); }

....

};

Page 593: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer generates this warning in certain cases only. It employs certain

heuristics mechanisms to avoid false positives. For example, it won't react to the

following code:

class M

{

int value;

void SetValue(int value) { this->value = value; }

....

};

V689. The destructor of the 'Foo' class is not declared as a virtual. It is possible that a smart pointer will not destroy an object correctly.The analyzer has detected an issue when a smart pointer may destroy an object

incorrectly. This error is caused by a missing virtual destructor in the base class.

For example:

class Base

{

public:

~Base() { }

};

class Derived : public Base

{

public:

Derived()

{

data = new int[5];

}

~Derived()

Page 594: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

{

delete [] data;

}

int* data;

};

void GO()

{

std::auto_ptr<Base> smartPtr(new Derived);

}

Notice that the object created in this code belongs to the 'Derived' class. However,

the smart pointer stores a reference to the Base class. The destructor in the Base

class is not virtual, and that's why an error will occur when the smart pointer tries to

destroy an object it has been storing.

The fixed code of the Base class:

class Base

{

public:

virtual ~Base() { }

};

P.S.

The V599 diagnostic message is related to this one.

References:

1. Wikipedia. Virtual method table.

2. Wikipedia. Virtual function.

3. Wikipedia. Destructor.

4. Discussion on StackOverflow. When to use virtual destructors?

5. The Old New Thing. When should your destructor be virtual?

Page 595: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V690. The class implements a copy constructor/operator=, but lacks the operator=/copy constructor.

The Law of The Big Two

Note

The analyzer has detected a class where:

a copy constructor is implemented but the operator = is not;

the operator = is implemented but a copy constructor is not.

Handling such classes is very dangerous. In other words, we are dealing with the

violated "Law of The Big Two". We will discuss this law a bit further.

Let's examine an example of a dangerous class. It is pretty long, but the only thing

we are concerned with now is that the class has the assignment operator but lacks a

copy constructor.

class MyArray

{

char *m_buf;

size_t m_size;

void Clear() { delete [] m_buf; }

public:

MyArray() : m_buf(0), m_size(0) {}

~MyArray() { Clear(); }

void Allocate(size_t s)

{ Clear(); m_buf = new char[s]; m_size = s; }

void Copy(const MyArray &a)

{ Allocate(a.m_size);

memcpy(m_buf, a.m_buf, a.m_size * sizeof(char)); }

char &operator[](size_t i) { return m_buf[i]; }

MyArray &operator =(const MyArray &a)

Page 596: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

{ Copy(a); return *this; }

};

We are not going to discuss how practical and useful this class is; it's just an

example and what we care about is that the following code fragment will work well:

{

MyArray A;

A.Allocate(100);

MyArray B;

B = A;

}

The assignment operator is successfully copying the array.

The next code fragment will cause undefined behavior: the application will either

crash or its operation will be violated otherwise.

{

MyArray A;

A.Allocate(100);

MyArray C(A);

}

The point is that the class lacks a copy constructor. When creating the 'C' object, the

pointer to the array will be simply copied, which will cause double memory freeing

when destroying the objects A and C.

A similar trouble will occur when a copy constructor is present but the assignment

operator is absent.

To fix the class, we need to implement a copy constructor:

MyArray &operator =(const MyArray &a)

{ Copy(a); return *this; }

MyArray(const MyArray &a) : m_buf(0), m_size(0)

{ Copy(a); }

Page 597: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

If the analyzer generates the V690 warning, please don't be lazy to implement an

absent method. Do so even if the code works well currently and you are sure you

remember the class' specifics. Some time later, you will forget about the missing

operator= or a copy constructor, and you or your colleagues will make a mistake

which will be difficult to find. When class fields are copied automatically, it's a

usual thing that such classes "almost work". Troubles reveal themselves later in

absolutely different places of code.

The Law of The Big Two

As it was said in the beginning, the V690 diagnostic rule detects classes that violate

"The Law of The Big Two". Let's examine this in detail. But we should start with

"The rule of three" first. Here is an extract from Wikipedia:

The rule of three (also known as the Law of The Big Three or The Big Three) is a

rule of thumb in C++ that claims that if a class defines one of the following it

should probably explicitly define all three:

destructor;

copy constructor;

copy assignment operator.

These three functions are special member functions. If one of these functions is used

without first being declared by the programmer it will be implicitly implemented by

the compiler with the default semantics of performing the said operation on all the

members of the class. The default semantics are:

Destructor - Call the destructors of all the object's class-type members

Copy constructor - Construct all the object's members from the corresponding members of the copy constructor's argument, calling the copy constructors of the object's class-type members, and doing a plain assignment of all non-class type (e.g., int or pointer) data members

Copy assignment operator - Assign all the object's members from the corresponding members of the assignment operator's argument, calling the copy assignment operators of the object's class-type members, and doing a plain assignment of all non-class type (e.g., int or pointer) data members.

Page 598: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The Rule of Three claims that if one of these had to be defined by the programmer,

it means that the compiler-generated version does not fit the needs of the class in

one case and it will probably not fit in the other cases either. The term "Rule of

three" was coined by Marshall Cline in 1991.

An amendment to this rule is that if Resource Acquisition Is Initialization (RAII) is

used for the class members, the destructor may be left undefined (also known as The

Law of The Big Two).

Because implicitly-generated constructors and assignment operators simply copy

all class data members, one should define explicit copy constructors and copy

assignment operators for classes that encapsulate complex data structures or have

external references such as pointers, since only the pointer gets copied, not the

object it points to. In the case that this default behavior is actually the intended

behavior, an explicit declaration can prevent ambiguity.

"The Law of The Big Two" itself is discussed in detail in the following article: The

Law of The Big Two.

As you can see, "The Law of The Big Two" is very important - that's why we have

implemented the corresponding diagnostic in our code analyzer.

Note

Does the V690 diagnostic always reveal genuine errors? No, it doesn't. Sometimes

we deal not with an error but just a redundant function. Take a look at the following

sample taken from a real application:

struct wdiff {

int start[2];

int end[2];

wdiff(int s1=0, int e1=0, int s2=0, int e2=0)

{

if (s1>e1) e1=s1-1;

if (s2>e2) e2=s2-1;

start[0] = s1;

start[1] = s2;

Page 599: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

end[0] = e1;

end[1] = e2;

}

wdiff(const wdiff & src)

{

for (int i=0; i<2; ++i)

{

start[i] = src.start[i];

end[i] = src.end[i];

}

}

};

This class has a copy constructor but lacks the assignment operator. But it's alright:

the arrays 'start' and 'end' consist of simple types 'int' and will be correctly copied by

the compiler. To eliminate the V690 warning here, we need to remove the

meaningless copy operator. The compiler will build the code, copying the class

members in no way slower, if not even faster.

The fixed code:

struct wdiff {

int start[2];

int end[2];

wdiff(int s1=0, int e1=0, int s2=0, int e2=0)

{

if (s1>e1) e1=s1-1;

if (s2>e2) e2=s2-1;

start[0] = s1;

start[1] = s2;

end[0] = e1;

end[1] = e2;

}

};

Page 600: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V691. Empirical analysis. It is possible that a typo is present inside the string literal. The 'foo' word is suspicious.Whenever the analyzer detects two identical string literals, it will try to figure out if

it is a consequence of poor Copy-Paste. We want to warn you right away that this

diagnostic is based on an empirical algorithm and therefore may produce strange

false positives sometimes.

Take a look at the following example:

static const wchar_t left_str[] = L"Direction: left.";

static const wchar_t right_str[] = L"Direction: right.";

static const wchar_t up_str[] = L"Direction: up.";

static const wchar_t down_str[] = L"Direction: up.";

The code was written with the help of the Copy-Paste method. The programmer

forgot to replace the string literal "up" with "down" at the end of the block. The

analyzer will suspect something is wrong and point out the strange word "up" in the

last line.

The fixed code:

static const wchar_t left_str[] = L"Direction: left.";

static const wchar_t right_str[] = L"Direction: right.";

static const wchar_t up_str[] = L"Direction: up.";

static const wchar_t down_str[] = L"Direction: down.";;

V692. An inappropriate attempt to append a null character to a string. To determine the length of a string

Page 601: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

by 'strlen' function correctly, a string ending with a null terminator should be used in the first place.The analyzer has detected an interesting error pattern. In order to write a terminal

null at the end of a string, the programmer uses the strlen() function to calculate its

length. The result will be unpredictable. The string must be already null-terminated

for the strlen() function to work properly.

For example:

char *linkname;

....

linkname[strlen(linkname)] = '\0';

This code doesn't make any sense: the null terminator will be written right into that

very cell where 0 was found. At the same time, the strlen() function may reach far

beyond the buffer, leading to undefined behavior.

To fix the code, we should use some other method to calculate the string length:

char *linkname;

size_t len;

....

linkname[len] = '\0';

V693. Consider inspecting conditional expression of the loop. It is possible that 'i < X.size()' should be used instead of 'X.size()'.The analyzer has detected a typo in the loop termination condition.

Page 602: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

For example:

for (size_t i = 0; v.size(); ++i)

sum += v[i];

If the 'v' array is not empty, an infinite loop will occur.

The fixed code:

for (size_t i = 0; i < v.size(); ++i)

sum += v[i];

V694. The condition (ptr - const_value) is only false if the value of a pointer equals a magic constant.The analyzer has detected a very suspicious condition: a constant value is added to

or subtracted from a pointer. The result is then compared to zero. Such code is very

likely to contain a typo.

Take a look at the following example with addition:

int *p = ...;

if (p + 2)

This condition will be always true. The only case when the expression evaluates to

0 is when you deliberately write the magic number "-2" into the pointer.

The fixed code:

int *p = ...;

if (*p + 2)

Now let's examine an example with subtraction:

Page 603: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

char *begin = ...;

char *end = ...;

....

const size_t ibegin = 1;

....

if (end - ibegin)

It is the variable 'begin' that should have been subtracted from the variable 'end'.

Because of the poor variable naming, the programmer used by mistake the constant

integer variable 'ibegin'.

The fixed code:

char *begin = ...;

char *end = ...;

....

if (end - begin)

Note. This warning is generated only when the pointer is "actual" - e.g. pointing to

a memory area allocated through the "malloc()" function. If the analyzer does not

know what the pointer equals to, it won't generate the warning in order to avoid

unnecessary false positives. It does happen sometimes that programmers pass

"magic numbers" in pointers and conditions of the (ptr - 5 == 0) pattern do make

sense.

V695. Range intersections are possible within conditional expressions.The analyzer has detected a potential error in a condition. The program must

perform different actions depending on which range of values a certain variable

meets. For this purpose, the following construct is used in the code:

if ( MIN_A < X && X < MAX_A ) {

....

Page 604: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

} else if ( MIN_B < X && X < MAX_B ) {

....

}

The analyzer generates the warning when the ranges checked in conditions overlap.

For example:

if ( 0 <= X && X < 10)

FooA();

else if ( 10 <= X && X < 20)

FooB();

else if ( 20 <= X && X < 300)

FooC();

else if ( 30 <= X && X < 40)

FooD();

The code contains a typo. The programmer's fingers faltered at some moment and

he wrote "20 <= X && X < 300" instead of "20 <= X && X < 30" by mistake. If

the X variable stores, for example, the value 35, it will be the function FooC() that

will be called instead of FooD().

The fixed code:

if ( 0 <= X && X < 10)

FooA();

else if ( 10 <= X && X < 20)

FooB();

else if ( 20 <= X && X < 30)

FooC();

else if ( 30 <= X && X < 40)

FooD();

Here is another example:

const int nv_ab = 5;

const int nv_bc = 10;

const int nv_re = 15;

const int nv_we = 20;

Page 605: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

const int nv_tw = 25;

const int nv_ww = 30;

....

if (n < nv_ab) { AB(); }

else if (n < nv_bc) { BC(); }

else if (n < nv_re) { RE(); }

else if (n < nv_tw) { TW(); } // <=

else if (n < nv_we) { WE(); } // <=

else if (n < nv_ww) { WW(); }

Depending on the value of the 'n' variable, different actions are performed. Poor

variable naming may confuse a programmer - and so it did in this example. The 'n'

variable should have been compared to 'nv_we' first and only then to 'nv_tw'.

To make the mistake clear, let's substitute the values of the constants into the code:

if (n < 5) { AB(); }

else if (n < 10) { BC(); }

else if (n < 15) { RE(); }

else if (n < 25) { TW(); }

else if (n < 20) { WE(); } // Condition is always false

else if (n < 30) { WW(); }

The fixed code:

if (n < nv_ab) { AB(); }

else if (n < nv_bc) { BC(); }

else if (n < nv_re) { RE(); }

else if (n < nv_we) { WE(); } // <=

else if (n < nv_tw) { TW(); } // <=

else if (n < nv_ww) { WW(); }

V696. The 'continue' operator will terminate 'do { ... } while (FALSE)'

Page 606: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

loop because the condition is always false.The analyzer has detected code that may mislead the programmer. Not every

programmer is aware that the continue operator in the "do { ... } while(0)" loop will

terminate the loop instead of continuing it.

This is what the standard has to say about it:

§6.6.2 in the standard: "The continue statement (...) causes control to pass to the

loop-continuation portion of the smallest enclosing iteration-statement, that is, to

the end of the loop." (Not to the beginning.)

Thus, after calling the 'continue' operator, the (0) condition will be checked and the

loop will terminate because the condition is false.

For example:

int i = 1;

do {

std::cout << i;

i++;

if(i < 3) continue;

std::cout << 'A';

} while(false);

The programmer would expect the program to print "12A", but it will actually print

"1".

Even if the code was written that way consciously, you'd better change it. For

example, you may use the 'break' operator:

int i=1;

do {

std::cout << i;

i++;

Page 607: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

if(i < 3) break;

std::cout << 'A';

} while(false);

The code looks clearer now. You can see right away that the loop will terminate if

the (i < 3) condition is true. Besides, the analyzer won't generate the warning on this

code.

If the code is incorrect, it needs to be rewritten. I cannot give any precise

recommendations about that; it all depends on the code execution logic. For

instance, if you want to get "12A" printed, you'd better write the following code:

for (i = 1; i < 3; ++i)

std::cout << i;

std::cout << 'A';

V697. A number of elements in the allocated array is equal to size of a pointer in bytes.The number of items in an array allocated by the 'new' operator equals the pointer

size in bytes, which makes this code fragment very suspicious.

Take a look at an example demonstrating how such a fragment is introduced into

the code. At first, the program contained a fixed array consisting of bytes. We

needed to create an array of the same size but consisting of float items. As a result,

we wrote the following code:

void Foo()

{

char A[10];

....

float *B = new float[sizeof(A)];

....

}

Page 608: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

We won't discuss the quality of this code now; what we are interested in is that the

'A' array has become dynamic too as a result of refactoring. The fragment where the

'B' array is created was forgotten to be changed. Because of that, we get the

following incorrect code:

void Foo(size_t n)

{

char *A = new char[n];

....

float *B = new float[sizeof(A)];

....

}

The number of items in the 'B' array is 4 or 8, depending on the platform bitness. It

is this problem that the analyzer detects.

The fixed code:

void Foo(size_t n)

{

char *A = new char[n];

....

float *B = new float[n];

....

}

V698. strcmp()-like functions can return not only the values -1, 0 and 1, but any values.The analyzer has detected a comparison of the result of strcmp() or similar function

to 1 or -1. The C/C++ language specification, however, says that the strcmp()

function can return any positive or negative value when strings are not equal – not

only 1 or -1.

Page 609: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Depending on the implementation, the strcmp() function can return the following

values when strings are not equal:

-1 or any negative number if the first string is less than the second in the lexicographical order;

1 or any positive number if the first string is larger than the second.

Whether constructs like strcmp() == 1 will work right depends on libraries, the

compiler and its settings, the operating system and its bitness, and so on; in this case

you should always write strcmp() > 0.

For example, below is a fragment of incorrect code:

std::vector<char *> vec;

....

std::sort(vec.begin(), vec.end(), [](

const char * a, const char * b)

{

return strcmp(a, b) == 1;

});

When you change over to a different compiler, target operating system or

application bitness, the code may start working improperly.

The fixed code:

std::vector<char *> vec;

....

std::sort(vec.begin(), vec.end(), [](

const char * a, const char * b)

{

return strcmp(a, b) > 0;

});

The analyzer also considers code incorrect when it compares results of two strcmp()

functions. Such code is very rare but always needs examining.

Page 610: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V699. Consider inspecting the 'foo = bar = baz ? .... : ....' expression. It is possible that 'foo = bar == baz ? .... : ....' should be used here instead.The analyzer has detected an expression of the 'foo = bar = baz ? xyz : zzy' pattern.

It is very likely to be an error: the programmer actually meant it to be 'foo = bar ==

baz ? xyz : zzy' but made a mistake causing the code to do assignment instead of

comparison.

For example, take a look at the following incorrect code fragment:

int newID = currentID = focusedID ? focusedID : defaultID;

The programmer made a mistake writing an assignment operator instead of

comparison operator. The fixed code should look like this:

int newID = currentID == focusedID ? focusedID : defaultID;

Note that the code below won't trigger the warning because the expression before

the ternary operator is obviously of the bool type, which makes the analyzer assume

it was written so on purpose.

result = tmpResult = someVariable == someOtherVariable? 1 : 0;

This fragment is quite clear. It is equivalent to the following lengthier one:

if (someVariable == someOtherVariable)

tmpResult = 1;

else

tmpResult = 0;

result = tmpResult;

Page 611: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V700. Consider inspecting the 'T foo = foo = x;' expression. It is odd that variable is initialized through itself.The analyzer has detected an expression of the 'T foo = foo = X' pattern. The

variable being initialized is itself taking part in the assignment. Unlike the issue

diagnosed by the V593 rule, the foo variable here is initialized by an X expression;

however, this code is very suspicious: the programmer should have most likely

meant something else.

Here is an example of incorrect code:

int a = a = 3;

It's hard to say for sure what was actually meant here. Probably the correct code

should look as follows:

int a = 3;

It is also possible that the programmer wanted to initialize the variable through

assigning a value to another variable:

int a = b = 3;

V701. realloc() possible leak: when realloc() fails in allocating memory, original pointer is lost. Consider assigning realloc() to a temporary pointer.

Page 612: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer has detected an expression of the 'foo = realloc(foo, ...)' pattern. This

expression is potentially dangerous: it is recommended to save the result of the

realloc function into a different variable.

The realloc(ptr, ...) function is used to change the size of some memory block.

When it succeeds to do so without moving the data, the resulting pointer will

coincide with the source ptr. When changing a memory block's size is impossible

without moving it, the function will return the pointer to the new block while the

old one will be freed. But when changing a memory block's size is currently

impossible at all even with moving it, the function will return a null pointer. This

situation may occur when allocating a large data array whose size is comparable to

RAM size, and also when the memory is highly segmented. This third scenario is

just what makes it potentially dangerous: if realloc(ptr, ...) returns a null pointer, the

data block at the ptr address won't change in size. The main problem is that using a

construct of the "ptr = realloc(ptr, ...)" pattern may cause losing the ptr pointer to

this data block.

For example, see the following incorrect code taken from a real-life application:

void buffer::resize(unsigned int newSize)

{

if (capacity < newSize)

{

capacity = newSize;

ptr = (unsigned char *)realloc(ptr, capacity);

}

}

The realloc(...) function changes the buffer size when the required buffer size is

larger than the current one. But what will happen if realloc() fails to allocate

memory? It will result in writing NULL into ptr, which by itself is enough to cause

a lot of troubles, but more than that, the pointer to the source memory area will be

lost. The correct code looks as follows:

void buffer::resize(unsigned int newSize)

{

Page 613: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

if (capacity < newSize)

{

capacity = newSize;

unsigned char * tmp = (unsigned char *)realloc(ptr, capacity);

if (tmp == NULL)

{

/* Handle exception; maybe throw something */

} else

ptr = tmp;

}

}

V702. Classes should always be derived from std::exception (and alike) as 'public'.The analyzer has detected a class derived from the std::exception class (or other

similar classes) through the private or protected modifier. What is dangerous about

such inheritance is that in case of nonpublic inheritance, the attempt to catch a

std::exception will fail.

The error is often a result of the programmer forgetting to specify an inheritance

type. According to the language rules, the default inheritance type is private

inheritance. It results in exception handlers behaving other way than expected.

For example, see the following incorrect code:

class my_exception_t : std::exception // <=

{

public:

explicit my_exception_t() { }

virtual const int getErrorCode() const throw() { return 42; }

};

....

try

Page 614: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

{ throw my_exception_t(); }

catch (const std::exception & error)

{ /* Can't get there */ }

catch (...)

{ /* This code executed instead */ }

The code to catch all the standard and user exceptions "catch (const std::exception

& error)" won't work properly because private inheritance does not allow for

implicit type conversion.

To make the code run correctly, we need to add the public modifier before the base

class std::exception in the list of base classes:

class my_exception_t : public std::exception

{

....

}

V703. It is odd that the 'foo' field in derived class overwrites field in base class.The analyzer has detected that a descendant class contains a field whose type and

name coincide with those of some field of the parent class. Such a declaration may

be incorrect as the inheritance technology in itself implies inheriting all the parent

class' fields by the descendant, while declaring in the latter a field with the same

name only complicates the code and confuses programmers who will be

maintaining the code in future.

For example, see the following incorrect code:

class U {

public:

int x;

};

Page 615: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

class V : public U {

public:

int x; // <=

int z;

};

This code may be dangerous since there are two x variables in the V class: 'V::x'

proper and 'U::x'. The possible consequences of this code are illustrated by the

following sample:

int main() {

V vClass;

vClass.x = 1;

U *uClassPtr = &vClass;

std::cout << uClassPtr->x << std::endl; // <=

....

}

This code will cause outputting an uninitialized variable.

To fix the error, we just need to delete the variable declaration in the descendant

class:

class U {

public:

int x;

};

class V : public U {

public:

int z;

};

There are a few arguable cases the analyzer doesn't consider incorrect:

conflicting fields have different types;

at least one of the conflicting fields is declared as static;

Page 616: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

the base class' field is declared as private;

private inheritance is used;

the field is expanded through define;

the field has one of the special names like "reserved" (such names point out that the variable is actually used to reserve some part of the class structure in the memory for future use).

We recommend that you always do code refactoring for all the places triggering the

V703 warning. Using variables with the same name both in the base and descendant

classes is far not always an error. But such code is still very dangerous. Even if the

program runs well now, it's very easy to make a mistake when modifying classes

later.

V704. 'this == 0' comparison should be avoided - this comparison is always false on newer compilers.The analyzer has detected an expression of the 'this == 0' pattern. This expression

may work well in some cases but it is extremely dangerous due to certain reasons.

Here is a simple example:

class CWindow {

HWND handle;

public:

HWND GetSafeHandle() const

{

return this == 0 ? 0 : handle;

}

};

Calling the CWindow::GetSafeHandle() method for the null pointer 'this' will

generally lead to undefined behavior, according to the C++ standard. But since this

class' fields are not being accessed while executing the method, it may run well. On

the other hand, two negative scenarios are possible when executing this code. First,

Page 617: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

since the this pointer can never be null, according to the C++ standard, the compiler

may optimize the method call by reducing it to the following line:

return handle;

Second, suppose we've got the following code fragment:

class CWindow {

.... // CWindow from the previous example

};

class MyWindowAdditions {

unsigned long long x; // 8 bytes

};

class CMyWindow: public MyWindowAdditions, public CWindow {

....

};

....

void foo()

{

CMyWindow * nullWindow = NULL;

nullWindow->GetSafeHandle();

}

This code will cause reading from the memory at the address 0x00000008. You can

make sure it's true by adding the following line:

std::cout << nullWindow->handle << std::endl;

What you will get on the screen is the address 0x00000008, for the source pointer

NULL (0x00000000) has been shifted in such a way as to point to the beginning of

the CWindow class' subobject. For this purpose, it needs to be shifted by

sizeof(MyWindowAdditions) bytes.

What's most interesting, the "this == 0" check turns absolutely meaningless now.

The 'this' pointer is always equal to the 0x00000008 value at least.

On the other hand, the error won't reveal itself if you swap the base classes in

CMyWindow's declaration:

Page 618: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

class CMyWindow: public CWindow, public MyWindowAdditions{

....

};

All this may cause very vague errors.

Unfortunately, fixing the code is far from trivial. Theoretically, a correct way out in

such cases is to change the class method to static. This will require editing a lot of

other places where this method call is used.

class CWindow {

HWND handle;

public:

static HWND GetSafeHandle(CWindow * window)

{

return window == 0 ? 0 : window->handle;

}

};

Another way is to use the Null Object pattern which will also require plenty of

work.

class CWindow {

HWND handle;

public:

HWND GetSafeHandle() const

{

return handle;

}

};

class CNullWindow : public CWindow {

public:

HWND GetSafeHandle() const

{

return nullptr;

}

};

Page 619: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

....

void foo(void)

{

CNullWindow nullWindow;

CWindow * windowPtr = &nullWindow;

// Output: 0

std::cout << windowPtr->GetSafeHandle() << std::endl;

}

It should be noted that this defect is extremely dangerous because one is usually too

short of time to care about solving it, for it all seems to "work well as it is", while

refactoring is too expensive. But code working stably for years may suddenly fail

after a slightest change of circumstances: building for a different operating system,

changing to a different compiler version (including update), and so on. The

following example is quite illustrative: the GCC compiler, starting with version

4.9.0, has learned to throw away the check for null of the pointer dereferenced a bit

earlier in the code (see the V595 diagnostic):

int wtf( int* to, int* from, size_t count ) {

memmove( to, from, count );

if( from != 0 ) // <= condition is always true after optimization

return *from;

return 0;

}

There are quite a lot of real-life examples of problem code turned broken because of

undefined behavior. Here are a few of them to underline the importance of the

problem.

Example No. 1. A vulnerability in the Linux core

struct sock *sk = tun->sk; // initialize sk with tun->sk

....

if (!tun) // <= always false

return POLLERR; // if tun is NULL return error

Page 620: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Example No. 2. Incorrect work of srandomdev():

struct timeval tv;

unsigned long junk; // <= not initialized on purpose

gettimeofday(&tv, NULL);

// LLVM: analogue of srandom() of uninitialized variable,

// i.e. tv.tv_sec, tv.tv_usec and getpid() are not taken into account.

srandom((getpid() << 16) ^ tv.tv_sec ^ tv.tv_usec ^ junk);

Example No. 3. An artificial example that demonstrates very clearly both compilers'

aggressive optimization policy concerning undefined behavior and new ways to

"shoot yourself in the foot":

#include <stdio.h>

#include <stdlib.h>

int main() {

int *p = (int*)malloc(sizeof(int));

int *q = (int*)realloc(p, sizeof(int));

*p = 1;

*q = 2;

if (p == q)

printf("%d %d\n", *p, *q); // <= Clang r160635: Output: 1 2

}

As far as we know, none of the compilers has ignored the call of the this == 0 check

as of the implementation date of this diagnostic, but it's just a matter of time

because the C++ standard clearly reads (§9.3.1/1): "If a nonstatic member function

of a class X is called for an object that is not of type X, or of a type derived from X,

the behavior is undefined.". In other words, the result of calling any nonstatic

function for a class with this == 0 is undefined. As I've said, it's just a matter of time

for compilers to start substituting false instead of (this == 0) during compilation.

V705. It is possible that 'else' block was forgotten or commented out,

Page 621: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

thus altering the program's operation logics.This diagnostic is similar to V628 but deals with the else branch of the if operator.

The analyzer has detected a suspicious code fragment which may be a forgotten or

incorrectly commented else block.

This issue is best explained on examples.

if (!x)

t = x;

else

z = t;

In this case, code formatting doesn't meet its logic: the z = t expression will execute

only if (x == 0), which is hardly what the programmer wanted. A similar situation

may occur when a code fragment is not commented properly:

if (!x)

t = x;

else

//t = -1;

z = t;

In this case, we either need to fix the formatting by turning it into something more

readable or fix the logic error by adding a missing branch of the if operator.

However, there are cases when it's difficult to figure out if such code is incorrect or

it's just stylization. The analyzer tries to reduce the number of false positives related

to stylization through heuristic analysis. For example, the following code won't

trigger the diagnostic rule:

if (x == 1)

t = 42;

else

Page 622: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

if (x == 2)

t = 84;

else

#ifdef __extended__x

if (x == 3)

t = 741;

else

#endif

t = 0;

V706. Suspicious division: sizeof(X) / Value. Size of every element in X array does not equal to divisor.The analyzer has detected a suspicious division of one sizeof() operator's result by

another sizeof() operator or number, sizeof() being applied to an array and the item

size not coinciding with the divisor. The code is very likely to contain an error.

An example:

size_t A[10];

n = sizeof(A) / sizeof(unsigned);

In the 32-bit build mode, the sizes of the types unsigned and size_t coincide and 'n'

will equal ten. In the 64-bit build mode, however, the size of the size_t type is 8

bytes while that of the unsigned type is just 4 bytes. As a result, the n variable will

equal 20, which is hardly what the programmer wanted.

Code like the following one will also be considered incorrect:

Page 623: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

size_t A[9];

n = sizeof(A) / 7;

In the 32-bit mode, the array's size is 4 * 9 = 36 bytes. Dividing 36 by 7 is very

strange. So what did the programmer actually want to do? Something is obviously

wrong with this code.

No concrete recommendations can be given on how to deal with issues like that

because each particular case needs to be approached individually as reasons may

vary: a type size might have been changed or an array size defined incorrectly, and

so on. This error often results from typos or simply inattention.

The analyzer won't generate this warning if the array is of the char or uchar type

since such arrays are often used as buffers to store some data of other types. The

following is an example of code the analyzer treats as safe:

char A[9];

n = sizeof(A) / 3;

V707. Giving short names to global variables is considered to be bad practice.The analyzer has detected a globally declared variable with a short name. Even if it

won't cause any errors, it indicates a bad programming practice and makes the

program text less comprehensible.

An example:

int i;

The problem about short variable names is that there is a large risk you'll make a

mistake and use a global variable instead of a local one inside a function's or class

method's body. For instance, instead of:

Page 624: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

void MyFunc()

{

for (i = 0; i < N; i++)

AnotherFunc();

....

}

the following must be written:

void MyFunc()

{

for (int i = 0; i < N; i++)

AnotherFunc();

....

}

In cases like this, the analyzer will suggest changing the variable name to a longer

one. The smallest length to satisfy the analyzer is three characters. It also won't

generate the warning for variables with the names PI, SI, CR, LF.

The analyzer doesn't generate the warning for variables with short names if they

represent structures. Although it's a bad programming practice as well, accidentally

using a structure in an incorrect way is less likely. For example, if the programmer

by mistake writes the following code:

struct T { int a, b; } i;

void MyFunc()

{

for (i = 0; i < N; i++)

AnotherFunc();

....

}

it simply won't compile.

Page 625: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

However, the analyzer does get angry about constants with short names. They

cannot be changed, but nothing prevents one from using them in an incorrect check.

For example:

const float E = 2.71828;

void Foo()

{

S *e = X[i];

if (E)

{

e->Foo();

}

....

}

The fixed code:

const float E = 2.71828;

void Foo()

{

S *e = X[i];

if (e)

{

e->Foo();

}

....

}

But an even better way is to use a longer name or wrap such constants in a special

namespace:

namespace Const

{

const float E = 2.71828;

}

Page 626: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V708. Dangerous construction is used: 'm[x] = m.size()', where 'm' is of 'T' class. This may lead to undefined behavior.The analyzer has detected an instant of undefined behavior related to containers of

the map type or similar to it.

An example of incorrect code:

std::map<size_t, size_t> m;

....

m[0] = m.size();

This code fragment leads to undefined behavior as the calculation sequence for the

operands of the assignment operator is not defined. In case the object already

contains an item associated with zero, no troubles will occur. However, if it is

absent, program may go on to execute in two different ways depending on the

version of the compiler, operating system and so on.

Suppose the compiler will first calculate the right operand of the assignment

operator and only after that the left one. Since the container is empty, m.size()

returns zero. Zero is then associated with zero and we've got m[0] == 0.

Now suppose the compiler will first calculate the left operand and only then the

right one. It is m[0] that will be taken first. Since nothing is associated with zero, an

empty association will be created. Then m.size() is calculated. Since the container is

not empty anymore, m.size() returns one. After that, one is associated with zero.

And the result will be m[0] == 1.

A correct way to fix this code is to use a temporary variable and associate some

value with zero in advance:

std::map<size_t, size_t> m;

Page 627: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

....

m[0] = 0;

const size_t mapSize = m.size();

m[0] = mapSize;

Despite that this situation is not likely to occur often in real code, it is dangerous in

that the code fragment leading to undefined behavior is usually very difficult to

spot.

V709. Suspicious comparison found: 'a == b == c'. Remember that 'a == b == c' is not equal to 'a == b && b == c'.The analyzer has detected a logical expression of the a == b == c pattern.

Unfortunately, programmers tend to forget every now and then that rules of the C

and C++ languages do not coincide with mathematical rules (and, at first glance,

common sense), and believe they can use this comparison to check if three variables

are equal. But actually, a bit different thing will be calculated instead of that.

Let's check an example.

if (a == b == c) ....

Let a == 2, b == 2 and c == 2. The first comparison (a == b) is true as 2 == 2. As a

result, this comparison returns the value true (1). But the second comparison (... = c)

will return the value false because true != 2. To have a comparison of three (or

more) variables done correctly, one should use the following expression:

if (a == b && b == c) ....

In this case, a == b will return true, b == c will return true and the result of the

logical operation AND will also be true.

Page 628: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

However, expressions looking similar to incorrect ones are often used to make code

shorter. The analyzer won't generate the warning for cases when:

1) The third variable is of the bool, BOOL, etc. types or by itself equals 0, 1, true or

false. In this case, the error is very unlikely - the code is almost surely to be correct:

bool compare(int a, int b, bool res)

{

return a == b == res;

}

2) The expression contains parentheses. In this case, it is obvious that the

programmer understands the expression's logic perfectly well and wants it to be

executed exactly the way it is written:

if ((a == b) == c) ....

In case the analyzer has generated a false V709 waning, we recommend that you

add parentheses into the code to eliminate it, like in the example above. Thus you

will indicate to other programmers that the code is correct.

V710. Suspicious declaration found. There is no point to declare constant reference to a number.The analyzer has detected a suspicious code fragment where a constant reference to

a numerical literal is created. This operation doesn't have any practical sense and is

most likely to be a consequence of some typo. It may involve using a wrong macro

or something else.

A couple of examples:

const int & u = 7;

const double & v = 4.2;

Page 629: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

You'd better not suppress this warning but eliminate it by deleting the ampersand

character thus turning the reference into a regular constant value (of course, you

should check first that it all was meant exactly that way):

const int u = 7;

const double v = 4.2;

V711. It is dangerous to create a local variable within a loop with a same name as a variable controlling this loop.The analyzer has detected a variable declared inside a loop body so that its name

coincides with that of the loop control variable. Although it's not always critical for

for and foreach (C++11) loops, it is still a bad programming style. For do {} while

and while {} loops, however, it's much more dangerous as the new variable inside

the loop body may accidentally get changed instead of the variable in the loop

condition.

An example:

int ret;

....

while (ret != 0)

{

int ret;

ret = SomeFunctionCall();

while (ret != 0)

{

DoSomeJob();

ret--;

}

ret--;

}

Page 630: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

In this situation, an infinite loop may occur since the external variable 'ret' in the

loop body is not changed at all. An obvious solution in this case is to change the

name of the internal variable:

int ret;

....

while (ret != 0)

{

int innerRet;

innerRet = SomeFunctionCall();

while (innerRet != 0)

{

DoSomeJob();

innerRet--;

}

ret--;

}

The analyzer doesn't generate the V711 warning for each and every case when a

variable has the same name as that used in the loop body. For example, below is a

code sample that won't trigger the warning:

int ret;

....

while (--ret != 0)

{

int ret;

ret = SomeFunctionCall();

while (ret != 0)

{

DoSomeJob();

ret--;

}

}

Page 631: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Neither does the analyzer generate the warning when suspicious variables are

obviously of non-corresponding types (say, a class and a pointer to int). There are

much fewer chances to make a mistake in such cases.

V712. Be advised that compiler may delete this cycle or make it infinity. Use volatile variable(s) or synchronization primitives to avoid this.The analyzer has detected a loop with an empty body which may be turned into an

infinite loop or removed completely from the program text by the analyzer in the

course of optimization. Such loops are usually used when awaiting some external

event.

An example:

bool AllThreadsCompleted = false; // Global variable

....

while (!AllThreadsCompleted);

In this case, the optimizing compiler will make the loop infinite. Let's take a look at

the assembler code from the debug version:

; 8 : AllThreadsCompleted = false;

mov BYTE PTR ?AllThreadsCompleted@@3_NA, 0

; AllThreadsCompleted

$LN2@main:

; 9 :

; 10 : while (!AllThreadsCompleted);

Page 632: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

movzx eax, BYTE PTR ?AllThreadsCompleted@@3_NA

; AllThreadsCompleted

test eax, eax

jne SHORT $LN1@main

jmp SHORT $LN2@main

$LN1@main:

The check is evidently present here. Now let's look at the release version:

$LL2@main:

; 8 : AllThreadsCompleted = false;

; 9 :

; 10 : while (!AllThreadsCompleted);

jmp SHORT $LL2@main

Now the jump was optimized into a non-conditional one. Such differences between

debug and release versions are often a source of complicated and hard-to-detect

errors.

There are several ways to solve this issue. If this variable is really meant to be used

to control the logic of a multi-threaded program, one should rather use the operating

system's synchronization means such as mutexes and semaphores. Another way of

fixing it is to add the 'volatile' modifier to the variable declaration to prohibit

optimization:

volatile bool AllThreadsCompleted; // Global variable

....

while (!AllThreadsCompleted);

Check the corresponding assembler code in the release version:

$LL2@main:

; 9 :

; 10 : while (!AllThreadsCompleted);

Page 633: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

movzx eax, BYTE PTR ?AllThreadsCompleted@@3_NC

; AllThreadsCompleted

test al, al

je SHORT $LL2@main

However, the V712 diagnostic message sometimes "misses the target" and points to

those fragments where no infinite loop should exist at all. In such cases, an empty

loop is probably caused by a typo. Then this diagnostic often (but not always)

intersects with the V715 diagnostic.

V713. The pointer was utilized in the logical expression before it was verified against nullptr in the same logical expression.The analyzer has detected an issue when a pointer is checked for being nullptr after

having been used. Unlike the V595 diagnostic, this one covers the range of one

logical statement.

Here's an incorrect example.

if (P->x != 0 && P != nullptr) ....

In this case, the second check doesn't make any sense. If 'P' equals nullptr, a

memory access error will occur when trying to dereference the null pointer.

Something is obviously wrong in this code. The easiest way out is to swap the

checks in the logical statement:

if (P != nullptr && P->x != 0) ....

However, it is always recommended in such cases to additionally carry out code

review to find out if that is exactly what the programmer wanted. Perhaps the

pointer by itself cannot be nullptr and the check is therefore excessive. Or perhaps a

Page 634: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

wrong variable is dereferenced or checked for being nullptr. Such cases have to be

approached individually and there's no general recommendation to give on that.

V714. Variable is not passed into foreach loop by a reference, but its value is changed inside of the loop.The analyzer has detected a suspicious situation: there is a foreach loop in the code,

the loop control variable being assigned some value. At the same time, the loop

control variable is passed by value. It is more likely to have been meant to be passed

by reference.

An example:

for (auto t : myvector)

t = 17;

It will cause copying the 't' variable at each iteration and changing the local copy,

which is hardly what the programmer wanted. Most likely, he intended to change

the values in the 'myvector' container. A correct version of this code fragment

should look as follows:

for (auto & t : myvector)

t = 17;

This diagnostic detects only the simplest cases of incorrect use of the foreach loop,

where there's a higher risk of making a mistake. In more complex constructs, the

programmer is more likely to have a clear idea of what he's doing, so you can see

constructs like the following one sometimes used in real-life code:

for (auto t : myvector)

{

function(t); // t used by value

// t is used as local variable further on

t = anotherFunction();

Page 635: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

if (t)

break;

}

The analyzer won't generate the V714 warning on this code.

V715. The 'while' operator has empty body. Suspicious pattern detected.The analyzer has detected a strange code fragment with an unusually placed while

operator with an empty body. The 'while' operator is standing after a closing

parenthesis associated with the body of an 'if', 'for' or another 'while' operator. Such

errors may occur when dealing with complex code with a high nesting level. This

diagnostic may sometimes intersect with the V712 diagnostic.

An example from a real-life application:

while (node != NULL) {

if ((node->hashCode == code) &&

(node->entry.key == key)) {

return true;

}

node = node->next;

} while (node != NULL);

This sample is totally correct from the viewpoint of the C++ language's syntax: the

first 'while' loop ends with a closing curly brace and is followed by a second while

loop with an empty body. Moreover, the second loop will never become an infinite

one as Node will surely be not equal to NULL after leaving the first loop. However,

it is obvious that something is wrong with this code. Perhaps the programmer

wanted to write a while loop at first but then changed his mind and made a do ....

while loop but for some reason didn't change the first condition to do. Or maybe it

was the do .... while loop that appeared first and then was replaced with while but

Page 636: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

only partially. Anyway, there is only one conclusion to draw from this: the code

needs reviewing and then rewriting in such a way as to get rid of the meaningless

while loop.

If the code is written right as it was supposed to, we recommend that instead of

marking it as a false positive you rather move while to the next line in order to

explicitly specify that it doesn't refer to the previous block and thus simplify the job

of other programmers to maintain the project in future.

V716. Suspicious type conversion: HRESULT -> BOOL (BOOL -> HRESULT).Analyzer has found a code that explicitly or implicitly casts value from BOOL type

to HRESULT type or vice versa. Whilst this operation is possible in terms of C++

language, it does not have any practical meaning. HRESULT type is meant to keep

a return status. It has relatively difficult format and it does not have anything

common with BOOL type.

It is possible to provide an example from real-life application:

BOOL WINAPI DXUT_Dynamic_D3D10StateBlockMaskGetSetting(....)

{

if( DXUT_EnsureD3D10APIs() &&

s_DynamicD3D10StateBlockMaskGetSetting != NULL )

....

else

return E_FAIL;

}

The main danger here is in the fact that HRESULT type is, actually, 'long' type,

while BOOL type is 'int'. These types can be easily cast to each other and compiler

does not find anything suspicious in code above.

Page 637: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

However, from programmer's point of view, these types differs. While BOOL type

is a logical variable, HRESULT type haves difficult structure and should signal

about an operation result: was operation successful, which result operation returned

after successful execution, in case of error - where the error occurred, in which

circumstances etc.

Let us talk about HRESULT type. First bit, counting from left side (i.e. the most

significant bit) keeps whether operation was successful or not: if it was successful,

first bit is set to zero, if not - to one. Next four bits describes kind of error. Next

eleven bits describes the module that ran into exception. Last sixteen bits, most

insignificant ones, describes operation status: on unsuccessful execution it may hold

error code, on successful - its status. MSDN provides detailed description in this

article.

BOOL type should be equal to zero to represent logical value "false"; otherwise, it

represents logical value "true". Speaking other way, these types look like each other

in terms of types and their conversion to each other, but conversion operation makes

no sense. Initial idea of HRESULT type is to keep not only information about

success or failure, but also some additional information about function call. Value

S_FALSE of HRESULT type can be the most dangerous, because it is equal to 0x1.

The fact that on successful run non-zero values is rare may be a reason to painful

debugging in search for errors that shows up from time to time.

We encourage usage of SUCCEEDED and FAILED macro to control function

return value.

HRESULT someFunction(int x);

....

BOOL failure = FAILED(someFunction(q));

In other cases, refactoring is not as simple as it looks and at least require some

serious code analysis.

Let us stress that again. Remember that:

FALSE == 0

Page 638: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

TRUE == 1

S_OK == 0

S_FALSE == 1

E_FAIL == 0x80004005

etc.

Never mix up HRESULT and BOOL. Mixing these types is a serious error in

program's operation logic. To check HRESULT values use special macro.

Related V543 diagnostics tries to search situations, where logical 'true' or 'false' is

put into a variable of HRESULT type.

V717. It is suspicious to cast object of base class V to derived class U.Analyzer has found a code that utilizes an unusual type cast: pointer to base class

object is cast to pointer to derived class, and pointer to base class actually points to

the object of base class.

Casting pointers from the derived class to the base class is a typical situation.

However, casting pointers from base class to one of its derivatives sometimes can

be erroneous. When types were cast improperly, an attempt to access one of

derivative' members may lead to Access Violation or to anything else.

Sometimes programmers makes errors by casting a pointer to base class into pointer

to derived class. An example from real application:

typedef struct avatarCacheEntry { .... };

struct CacheNode : public avatarCacheEntry,

public MZeroedObject

{

....

BOOL loaded;

DWORD dwFlags;

int pa_format;

....

Page 639: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

};

avatarCacheEntry tmp;

....

CacheNode *cc = arCache.find((CacheNode*)&tmp);

// Now on accessing any derived class fields, for instance,

// cc->loaded, access violation will occur.

Unfortunately, it this case it is hard to advice something specific to fix incorrect

code - it is likely that refactoring with goals of improving code quality, increasing

readability and preventing future mistakes should be required. For instance, if there

is no need to access class new fields, it is possible to replace the pointer to the base

class with the pointer to derived class.

Code below is considered correct:

base * foo() { .... }

derived *y = (derived *)foo();

The idea here is simple: foo() function actually may always return a pointer to one

of classes derived from base class, and casting its result to the derived class is pretty

common. In general, analyzer shows V717 warning only in case when it is know

that it is pointer exactly to the base class being casted to the derived class. However,

analyzer would not show V717 warning in case when there are no new non-static

members in the derived class (nevertheless, it is still not good, but it is closer to

violation of good coding style rather than to actual error):

struct derived : public base

{

static int b;

void bar();

};

....

base x;

derived *y = (derived *)(&x);

Page 640: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V718. The 'Foo' function should not be called from 'DllMain' function.Many of the functions cannot be called within the DllMain() function as it may

cause a program to hang or lead to other issues. This diagnostic message indicates

that the analyzer has detected a dangerous call of this kind.

There is a good description of the issue with DllMain at MSDN: Dynamic-Link

Library Best Practices. Below are a few excerpts from it:

DllMain is called while the loader-lock is held. Therefore, significant restrictions

are imposed on the functions that can be called within DllMain. As such, DllMain is

designed to perform minimal initialization tasks, by using a small subset of the

Microsoft Windows API. You cannot call any function in DllMain that directly or

indirectly tries to acquire the loader lock. Otherwise, you will introduce the

possibility that your application deadlocks or crashes. An error in a DllMain

implementation can jeopardize the entire process and all of its threads.

The ideal DllMain would be just an empty stub. However, given the complexity of

many applications, this is generally too restrictive. A good rule of thumb for

DllMain is to postpone as much initialization as possible. Lazy initialization

increases robustness of the application because this initialization is not performed

while the loader lock is held. Also, lazy initialization enables you to safely use

much more of the Windows API.

Some initialization tasks cannot be postponed. For example, a DLL that depends on

a configuration file should fail to load if the file is malformed or contains garbage.

For this type of initialization, the DLL should attempt the action and fail quickly

rather than waste resources by completing other work.

You should never perform the following tasks from within DllMain:

Call LoadLibrary or LoadLibraryEx (either directly or indirectly). This can cause a deadlock or a crash.

Page 641: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Call GetStringTypeA, GetStringTypeEx, or GetStringTypeW (either directly or indirectly). This can cause a deadlock or a crash.

Synchronize with other threads. This can cause a deadlock.

Acquire a synchronization object that is owned by code that is waiting to acquire the loader lock. This can cause a deadlock.

Initialize COM threads by using CoInitializeEx. Under certain conditions, this function can call LoadLibraryEx.

Call the registry functions. These functions are implemented in Advapi32.dll. If Advapi32.dll is not initialized before your DLL, the DLL can access uninitialized memory and cause the process to crash.

Call CreateProcess. Creating a process can load another DLL.

Call ExitThread. Exiting a thread during DLL detach can cause the loader lock to be acquired again, causing a deadlock or a crash.

Call CreateThread. Creating a thread can work if you do not synchronize with other threads, but it is risky.

Create a named pipe or other named object (Windows 2000 only). In Windows 2000, named objects are provided by the Terminal Services DLL. If this DLL is not initialized, calls to the DLL can cause the process to crash.

Use the memory management function from the dynamic C Run-Time (CRT). If the CRT DLL is not initialized, calls to these functions can cause the process to crash.

Call functions in User32.dll or Gdi32.dll. Some functions load another DLL, which may not be initialized.

Use managed code.

V719. The switch statement does not cover all values of the enum.The analyzer has detected a suspicious 'switch' operator. The choice of an option is

made through an enum-variable. While doing so, however, not all the possible cases

are considered. Take a look at the following example:

enum TEnum { A, B, C, D, E, F };

....

TEnum x = foo();

switch (x)

{

Page 642: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

case A: Y(11); break;

case B: Y(22); break;

case C: Y(33); break;

case D: Y(44); break;

case E: Y(55); break;

}

The TEnum enumeration contains 6 named constants. But inside the 'switch'

operator, only 5 of them are used. It's highly probable that this is an error.

This error often occurs as a result of careless refactoring. The programmer added

the 'F' constant into 'TEnum' and fixed some of the 'switch' but forgot about the

others. It resulted in the 'F' value being processed incorrectly.

The analyzer will warn about the non-used 'F' constant. Then the programmer can

fix the mistake:

switch (x)

{

case A: Y(11); break;

case B: Y(22); break;

case C: Y(33); break;

case D: Y(44); break;

case E: Y(55); break;

case F: Y(66); break;

}

It's far not always that the analyzer generates the warning for cases when some of

the constants of an enum are not used in 'switch'. Otherwise, there would be too

many false positives. There are a number of empirical exceptions to the rule. Here

are the basic ones:

A default-branch;

The enum contains only 1 or 2 constants;

More than 4 constants are not used in switch;

The name of the missing constant contains None, Unknown, etc.

Page 643: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The missing constant is the very last one in the enum and its name contains "end", "num", "count" and the like.

The user can explicitly define a list of names for the last item in an enum. In this

case, the analyzer will only use these user-defined names instead of the list of

default names such as "num" or "count". The comment to control the behavior of

the V719 diagnostic is as follows:

//-V719_COUNT_NAME=ABCD,FOO

You can add this comment into one of the files included into all the other ones - for

example StdAfx.h.

Introduced exceptions is a deliberate decision, use-proven in practice. The only

thing we should discuss in more detail is the case when warnings are not generated

when there is a 'default' branch. This exception is not always good.

On the one hand, the analyzer must not go mad about non-used constants when a

'default' is present in the code. There would be too many false positives otherwise

and users would simply turn off this diagnostic. On the other hand, it's quite a

typical situation when you need to consider all the options in 'switch' while the

'default' branch is used to catch alert conditions. For example:

enum TEnum { A, B, C, D, E, F };

....

TEnum x = foo();

switch (x)

{

case A: Y(11); break;

case B: Y(22); break;

case C: Y(33); break;

case D: Y(44); break;

case E: Y(55); break;

default:

throw MyException("Ouch! One of the cases is missing!");

}

Page 644: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The error can be detected only at runtime. Sure, one would like this issue to be

diagnosed by the analyzer as well. In the most crucial code fragments, you may do

the following:

enum TEnum { A, B, C, D, E, F };

....

TEnum x = foo();

switch (x)

{

case A: Y(11); break;

case B: Y(22); break;

case C: Y(33); break;

case D: Y(44); break;

case E: Y(55); break;

#ifndef PVS_STUDIO

default:

throw MyException("Ouch! One of the cases is missing!");

#endif

}

What is used here is a predefined PVS-Studio macro. This macro is absent during

compilation, so when compiling the exe file, the 'default' branch remains where it is

and an exception is thrown if an error occurs.

When checking the code with PVS-Studio, the PVS_STUDIO macro is predefined

and this prevents the analyzer from noticing the default-branch. Therefore, it will

check 'switch', detect the non-used 'F' constant, and generate the warning.

The fixed code:

switch (x)

{

case A: Y(11); break;

case B: Y(22); break;

case C: Y(33); break;

case D: Y(44); break;

case E: Y(55); break;

Page 645: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

case F: Y(66); break;

#ifndef PVS_STUDIO

default:

throw MyException("Ouch! One of the cases is missing!");

#endif

}

The approach described above doesn't look neat. But if you worry about some of the

'switch' and want to make sure you have protected it, this method is quite

applicable.

V720. It is advised to utilize the 'SuspendThread' function only when developing a debugger (see documentation for details).

Why you should never suspend a thread

The SuspendThread function suspends a thread, but it does so asynchronously

The analyzer has detected that the SuspendThread() or Wow64SuspendThread()

function is used in the program. Calling these functions is in itself not an error. But

developers tend to use them inappropriately. It may result in the program's

misbehavior.

The SuspendThread() function is designed to assist the development of debuggers

and other similar applications. If you use this function in your application for

syncing tasks, it's highly probable that your program contains an error.

The problem with the misuse of the SuspendThread() function is discussed in the

following articles:

1. Why you should never suspend a thread .

Page 646: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

2. The SuspendThread function suspends a thread, but it does so asynchronously.

Please read them. If you find that the SuspendThread() function is used incorrectly

in your code, then you need to rewrite it. If everything is OK, simply turn off the

V720 diagnostic in the analyzer's settings.

Articles published on the Internet sometimes disappear or change their location.

Therefore, we cite the text of both articles in the documentation, just in case.

Why you should never suspend a threadIt's almost as bad as terminating a thread.

Instead of just answering a question, I'm going to ask you the questions and see if

you can come up with the answers.

Consider the following program, in (gasp) C#:

using System.Threading;

using SC = System.Console;

class Program {

public static void Main() {

Thread t = new Thread(new ThreadStart(Program.worker));

t.Start();

SC.WriteLine("Press Enter to suspend");

SC.ReadLine();

t.Suspend();

SC.WriteLine("Press Enter to resume");

SC.ReadLine();

t.Resume();

}

static void worker() {

for (;;) SC.Write("{0}\r", System.DateTime.Now);

}

}

Page 647: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

When you run this program and hit Enter to suspend, the program hangs. But if you

change the worker function to just "for(;;) {}" the program runs fine. Let's see if we

can figure out why.

The worker thread spends nearly all its time calling System.Console.WriteLine, so

when you call Thread.Suspend(), the worker thread is almost certainly inside the

System.Console.WriteLine code.

Q: Is the System.Console.WriteLine method threadsafe?

Okay, I'll answer this one: Yes. I didn't even have to look at any documentation to

figure this out. This program calls it from two different threads without any

synchronization, so it had better be threadsafe or we would be in a lot of trouble

already even before we get around to suspending the thread.

Q: How does one typically make an object threadsafe?

Q: What is the result of suspending a thread in the middle of a threadsafe operation?

Q: What happens if - subsequently - you try to access that same object (in this case,

the console) from another thread?

These results are not specific to C#. The same logic applies to Win32 or any other

threading model. In Win32, the process heap is a threadsafe object, and since it's

hard to do very much in Win32 at all without accessing the heap, suspending a

thread in Win32 has a very high chance of deadlocking your process.

So why is there even a SuspendThread function in the first place?

Debuggers use it to freeze all the threads in a process while you are debugging it.

Debuggers can also use it to freeze all but one thread in a process, so you can focus

on just one thread at a time. This doesn't create deadlocks in the debugger since the

debugger is a separate process.

The SuspendThread function suspends a thread, but it does so asynchronously

Page 648: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Okay, so a colleague decided to ignore that advice because he was running some

experiments with thread safety and interlocked operations, and suspending a thread

was a convenient way to open up race windows.

While running these experiments, he observed some strange behavior.

LONG lValue;

DWORD CALLBACK IncrementerThread(void *)

{

while (1) {

InterlockedIncrement(&lValue);

}

return 0;

}

// This is just a test app, so we will abort() if anything

// happens we don't like.

int __cdecl main(int, char **)

{

DWORD id;

HANDLE thread = CreateThread(NULL, 0, IncrementerThread, NULL, 0,

&id);

if (thread == NULL) abort();

while (1) {

if (SuspendThread(thread) == (DWORD)-1) abort();

if (InterlockedOr(&lValue, 0) != InterlockedOr(&lValue, 0))

{

printf("Huh? The variable lValue was modified by a suspended

thread?\n");

}

ResumeThread(thread);

}

return 0;

Page 649: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

}

The strange thing is that the "Huh?" message was being printed. How can a

suspended thread modify a variable? Is there some way that InterlockedIncrement

can start incrementing a variable, then get suspended, and somehow finish the

increment later?

The answer is simpler than that. The SuspendThread function tells the scheduler to

suspend the thread but does not wait for an acknowledgment from the scheduler that

the suspension has actually occurred. This is sort of alluded to in the documentation

for Suspend Thread  which says:

This function is primarily designed for use by debuggers. It is not intended to

be used for thread synchronization.

You are not supposed to use SuspendThread to synchronize two threads because

there is no actual synchronization guarantee. What is happening is that the Suspend-

Thread signals the scheduler to suspend the thread and returns immediately. If the

scheduler is busy doing something else, it may not be able to handle the suspend

request immediately, so the thread being suspended gets to run on borrowed time

until the scheduler gets around to processing the suspend request, at which point it

actually gets suspended.

If you want to make sure the thread really is suspended, you need to perform a

synchronous operation that is dependent on the fact that the thread is suspended.

This forces the suspend request to be processed since it is a prerequisite for your

operation, and since your operation is synchronous, you know that by the time it

returns, the suspend has definitely occurred.

The traditional way of doing this is to call GetThreadContext, since this requires the

kernel to read from the context of the suspended thread, which has as a prerequisite

that the context be saved in the first place, which has as a prerequisite that the

thread be suspended.

Page 650: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V721. The VARIANT_BOOL type is utilized incorrectly. The true value (VARIANT_TRUE) is defined as -1.The analyzer has detected an incorrect use of the VARIANT_BOOL type. The

reason is that the value true (VARIANT_TRUE) is designated as -1. Many

programmers are unaware of this detail and tend to use this type incorrectly.

This is how the VARIANT_TRUE type and constants denoting "true" and "false"

are declared:

typedef short VARIANT_BOOL;

#define VARIANT_TRUE ((VARIANT_BOOL)-1)

#define VARIANT_FALSE ((VARIANT_BOOL)0)

Let's take a look at a few examples when the VARIANT_TRUE type is used

incorrectly. In all the cases, the programmer expects the condition to be true, while

it is actually always false.

Example 1.

VARIANT_BOOL variantBoolTrue = VARIANT_TRUE;

if (variantBoolTrue == true) //false

If we substitute the value into the expression, we'll get ((short)(-1) == true). When

this expression is evaluated, 'true' will turn into '1'. The condition (-1 == 1) is false.

The correct code:

if (variantBoolTrue == VARIANT_TRUE)

Example 2.

VARIANT_BOOL variantBoolTrue = TRUE;

if (variantBoolTrue == VARIANT_TRUE) //false

Page 651: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The programmer made a mistake here and used TRUE instead of

VARIANT_TRUE. It will result in the variantBoolTrue variable being assigned the

value 1. This value is illegal for variables of the VARIANT_BOOL type.

If we substitute the value into the expression, we will get (1 == (short)(-1)).

The correct code:

VARIANT_BOOL variantBoolTrue = VARIANT_TRUE;

Example 3.

bool bTrue = true;

if (bTrue == VARIANT_TRUE) //false

Let's expand the expression: (true == (short)(-1)). When it is evaluated, 'true' will

turn into '1'. The condition (1 == -1) is false.

It's not easy to suggest a correct version of this code as it is just fundamentally

incorrect. One can't mix variables of the 'bool' type and values of the

'VARIANT_TRUE' type.

There are numbers of other examples like these to be found in code. For instance,

when a function's formal argument is of the VARIANT_BOOL type but it is the

'true' value that will be passed as the actual one. Another example is when a

function returns an incorrect value. And so on and so forth.

The most important thing you should keep in mind is that you can't mix the

VARIANT_BOOL type with the types BOOL, bool, and BOOLEAN.

References:

1. MSDN. VARIANT_BOOL.

2. The Old New Thing. BOOL vs. VARIANT_BOOL vs. BOOLEAN vs. bool.

V722. An abnormality within similar comparisons. It is possible that a

Page 652: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

typo is present inside the expression.The analyzer found suspicious condition that may contain an error. The diagnosis is

empirical, that is why it is easier to demonstrate it on the example than to explain

the working principle of the analyzer. Consider the real example:

if (obj.m_p == p &&

obj.m_forConstPtrOp == forConstVarOp &&

obj.m_forConstPtrOp == forConstPtrOp)

Because of the similarity of the variable names, there is a typo in the code. An error

is located on the second line. The variable 'forConstVarOp' should be compared

with 'm_forConstVarOp' rather than with 'm_forConstPtrOp'. It is difficult to notice

the error even when reading this text. Please, pay attention to 'Var' and 'Ptr' within

the variable names.

The right variant:

if (obj.m_p == p &&

obj.m_forConstVarOp == forConstVarOp &&

obj.m_forConstPtrOp == forConstPtrOp)

If the analyzer issued the warning V722, then carefully read the corresponding

code. Sometimes it is difficult to notice a typo.

V723. Function returns a pointer to the internal string buffer of a local object, which will be destroyed.The analyzer has detected an issue when a function returns a pointer to the internal

string buffer of a local object. This object will be automatically destroyed together

with its buffer after leaving the function, so you won't be able to use the pointer to

Page 653: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

it. The most common and simple code example triggering this message looks like

this:

const char* Foo()

{

std::string str = "local";

return str.c_str();

}

In this code, the Foo() function returns a C-string stored in the internal buffer of the

str object which will be automatically destroyed. As a result, we'll get an incorrect

pointer that will cause undefined behavior when we try to use it. The fixed code

should look as follows:

const char* Foo()

{

static std::string str = "static";

return str.c_str();

}

V724. Converting integers or pointers to BOOL can lead to a loss of high-order bits. Non-zero value can become 'FALSE'.The analyzer has detected an issue when casting pointers or integer variables to the

BOOL type may cause a loss of the most significant bits. As a result, a non-zero

value which actually means TRUE may unexpectedly turn to FALSE.

In programs, the BOOL (gboolean, UBool, etc.) type is interpreted as an integer

type. Any value other than zero is interpreted as true, and zero as false. Therefore, a

loss of the most significant bits resulting from type conversion will cause an error in

the program execution logic.

Page 654: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

For example:

typedef long BOOL;

__int64 lLarge = 0x12300000000i64;

BOOL bRes = (BOOL) lLarge;

In this code, a non-zero variable is truncated to zero when being cast to BOOL,

which renders it FALSE.

Here are a few other cases of improper type conversion:

int *p;

size_t s;

long long w;

BOOL x = (BOOL)p;

BOOL y = s;

BOOL z = (BOOL)s;

BOOL q = (BOOL)w;

To fix errors like these, we need to perform a check for a non-zero value before

BOOL conversion.

Here are the various ways to fix these issues:

int *p;

size_t s;

long long w;

BOOL x = p != nullptr;

BOOL y = s != 0;

BOOL z = s ? TRUE : FALSE;

BOOL q = !!w;

V725. A dangerous cast of 'this' to 'void*' type in the 'Base' class, as it

Page 655: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

is followed by a subsequent cast to 'Class' type.The analyzer has detected a dangerous conversion of the "this" pointer to the

"void*" type followed by a conversion of "void*" back to the class type. Casting

"this" to "void*" is not in itself an error, but in certain cases the reverse conversion

(from "void*" to the class pointer) is, which may be dangerous as the resulting

pointer may appear incorrect.

The diagnostic description is pretty large and complex, but unfortunately I cannot

help it. So please read it carefully to the end.

Let's discuss an example where "this" is cast to "void*" and after that the reverse

conversion to the class type takes place:

class A

{

public:

A() : firstPart(1){}

void printFirstPart() { std::cout << firstPart << " "; }

private:

int firstPart;

};

class B

{

public:

B() : secondPart(2){}

void* GetAddr() const { return (void*)this; }

void printSecondPart() { std::cout << secondPart << " "; }

private:

int secondPart;

};

class C: public A, public B

Page 656: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

{

public:

C() : A(), B(), thirdPart(3){}

void printThirdPart() { std::cout << thirdPart << " "; }

private:

int thirdPart;

};

void func()

{

C someObject;

someObject.printFirstPart();

someObject.printSecondPart();

someObject.printThirdPart();

void *pointerToObject = someObject.GetAddr();

....

auto pointerC = static_cast<C*>(pointerToObject);

pointerC->printFirstPart();

pointerC->printSecondPart();

pointerC->printThirdPart();

}

We would expect to get the following output:

1 2 3 1 2 3

But what will be actually printed is something like this:

1 2 3 2 3 -858993460

So, we get an incorrect output for all the data after the mentioned conversion

sequence. The trouble is that the "pointerC" pointer is now pointing to the memory

block allocated for object B, instead of the beginning of the C object as it did

before.

Page 657: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

This error may seem farfetched and unreal. But it is only obvious because the

example above is short and simple. In real-life programs with complex class

hierarchies, it may be far more confusing and vague. What makes this issue

especially tricky is that when the "GetAddr()" function is stored in class A,

everything works right, but if you store it in class B, then it doesn't. That may be

quite embarrassing. So let's figure it all out.

To make it easier for you to understand the reason behind the error, we need to find

out how objects of classes created through multiple inheritance are constructed and

arranged in memory.

A schematic example is shown in Figure 1.

Figure 1 - Arrangement of an object of a class created through multiple inheritance in memory

As you can see from this figure, the object of class C (which is the one created

through multiple inheritance) consists of the objects of classes A and B plus a part

of object C.

Each of the "this" pointers contains the address of the beginning of the memory

block allocated for the corresponding object. Figure 2 shows where "this" pointers

point to for all the three objects.

Page 658: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Figure 2 - "this" pointers and memory blocks

Since the C-class object consists of three parts, its "this" pointer will be pointing not

to the memory block added to the base classes, but to the beginning of the entire

continuous memory block. That is, "this" pointers for classes A and C will coincide

in this case.

The "this" pointer for the B-class object points to where the memory block allocated

for it starts, but at the same time, the address of the beginning of this memory block

is different from that of the memory block allocated for the C-class object.

So, when calling the "GetAddr()" method, we will get the address of object B and

then, after casting the resulting pointer back to type "C*", we will get an incorrect

pointer.

In other words, if the "GetAddr()" function were stored in class A, the program

would work as expected. But when it is stored in B, we get an error.

To avoid errors like this, the programmer should carefully consider if they really

need to cast "this" to "void*", and if the answer is certainly yes, then they must

carefully check the inheritance hierarchy as well as any further reverse conversions

from "void*" to the class pointer type.

References:

1. Joost's Dev Blog. Hardcore C++: why "this" sometimes doesn't equal "this".

Page 659: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V726. An attempt to free memory containing the 'int A[10]' array by using the 'free(A)' function.The analyzer has detected incorrect code, where an attempt is made to delete an

array through the free() or other similar function while no corresponding special

functions, such as malloc(), have been used to allocate the memory for this array.

This issue leads to undefined behavior.

For example:

class A

{

int x;

int a[50];

public:

A(){}

~A(){ free(a); }

};

Since the memory hasn't been allocated in any special way, it shouldn't be freed by

calling special functions either as it will be freed automatically once the object is

destroyed. Therefore, the correct code should look like this:

class A

{

int x;

int a[50];

public:

A(){}

~A(){}

};

Page 660: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V727. Return value of 'wcslen' function is not multiplied by 'sizeof(wchar_t)'The analyzer has detected an expression which it believes to be used for calculating

the size (in bytes) of a buffer intended for storing a string. This expression is written

with an error.

When solving the task of calculating the size of a char string, the standard solution

is to use the "strlen(str) + 1" construct. The strlen() function calculates the length of

some string, while 1 is used to reserve one byte for the null character. But when

dealing with strings of the types wchar_t, char16_t, or char32_t, always remember

to multiply the "strlen(str) + 1" expression by the size of one character, i.e.

'sizeof(T)'.

Let's examine a few synthetic error samples.

Example No. 1:

wchar_t *str = L"Test";

size_t size = wcslen(str) + 1 * sizeof(wchar_t);

Because of the missing parentheses, 'sizeof' is multiplied by 1 first and then the

resulting value is added to 'strln(str)' function. The correct code should look as

follows:

size_t size = (wcslen(str) + 1) * sizeof(wchar_t);

Example No. 2:

The expression may be written in a different order, when it is the function result

which is multiplied by 'sizeof' first and then the resulting value is added to 1.

Page 661: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

.... = malloc(sizeof(wchar_t) * wcslen(str) + 1);

It may also happen that you remember in the middle of writing the code that you

should multiply the string length by "sizeof(wchar_t)" but add 1 out of habit. It will

result in allocating 1 byte less memory than required.

The correct versions of the code look as follows:

.... = malloc(wcslen(str) * sizeof(wchar_t) + 1 * sizeof(wchar_t));

.... = malloc((wcslen(str) + 1) * sizeof(wchar_t));

V728. An excessive check can be simplified. The '||' operator is surrounded by opposite expressions 'x' and '!x'.The analyzer has detected code that can be simplified. The left and right operands of

the '||' operation are expressions with opposite meanings. This code is redundant and

can be simplified by reducing the number of checks.

Here's an example of redundant code:

if (!Name || (Name && Name[0] == 0))

In the "Name && Name[0] == 0" expression, the 'Name' check is excessive because

before it, the expression '!Name', which is opposite to it, is checked, these

expressions being separated by the '||' operation. Consequently, the excessive check

in the parentheses can be omitted to simplify the code:

if (!Name || Name[0] == 0)

Page 662: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Redundancy may indicate there is an error in the code: it might be that a wrong

variable is used in the expression, so the correct version of the code should really

look something like this:

if (!Foo || (Name && Name[0] == 0))

The analyzer outputs this warning not only for 'x' and '!x' constructs, but for other

expressions with opposite meanings as well. For example:

if (a > 5 || (a <= 5 && b))

V729. Function body contains the 'X' label that is not used by any 'goto' statements.The analyzer has detected that a function body contains a label with no 'goto'

statement referring to it. It might be the programmer's mistake, resulting in a jump

to a wrong label somewhere in the code.

Here's a synthetic example of incorrect code:

string SomeFunc(const string &fStr)

{

string str;

while(true)

{

getline(cin,str);

if (str == fStr)

goto retRes;

else if(str == "stop")

goto retRes;

}

retRes:

return str;

badRet:

Page 663: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

return "fail";

}

The function body contains the 'badRet' label, which no 'goto' statement refers to,

while another label in this function, 'retRes', has an associated 'goto' statement. The

programmer made a mistake and duplicated the jump to the 'retRes' label instead of

the 'badRet' label.

The correct version of this code can look as follows:

string SomeFunc(const string &fStr)

{

string str;

while(true)

{

getline(cin,str);

if (str == fStr)

goto retRes;

else if(str == "stop")

goto badRet;

}

retRes:

return str;

badRet:

return "fail";

}

Here's another example of this error:

int DeprecatedFunc(size_t lhs, size_t rhs, bool cond)

{

if (cond)

return lhs*3+rhs;

else

return lhs*2 + rhs*7;

badLbl:

return -1;

Page 664: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

}

For this code, the analyzer will output a low-severity-level warning as the 'badLbl'

label is a leftover after some changes in the function, while all the 'goto' statements

referring to it were deleted.

The analyzer won't output the warning when the function body contains a 'goto'

statement referring to the label in question, this statement being commented out or

excluded through the '#ifdef' directive.

V730. Not all members of a class are initialized inside the constructor.The analyzer has detected a constructor that doesn't initialize some of the class

members. Here's a simple synthetic example:

struct MyPoint

{

int m_x, m_y;

MyPoint() { m_x = 0; }

void Print() { cout << m_x << " " << m_y; }

};

MyPoint Point;

Point.Print();

When creating the Point object, a constructor will be called that won't initialize the

'm_y' member. Accordingly, when calling the Print function, an uninitialized

variable will be used. The consequences of this are unpredictable.

The correct version of the constructor should look like this:

MyPoint() { m_x = 0; m_y = 0; }

We have discussed a simple synthetic example, where a bug can be easily spotted.

However, in real-life code, things may be much more complicated. Search of

uninitialized class members is implemented through a set of empirical algorithms.

Page 665: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Firstly, class members can be initialized in a large variety of ways, and it's

sometimes difficult for the analyzer to figure out whether or not a class member has

been initialized. Secondly, not all the members should be initialized all the time,

and the analyzer may output false positive warnings as it doesn't know the

programmer's intentions.

Search of uninitialized class members is a difficult and thankless task. This matter is

discussed in more detail in the article "In search of uninitialized class members". So

please be understanding when you get false positives and use the false positive

suppression mechanisms the analyzer provides.

You can suppress a warning by marking the constructor with the comment "//-

V730". Another way is to use a special database for false positives. As a last resort,

when there are too many of them, consider disabling the V730 diagnostic

altogether.

But these are extreme measures. In practice, it might make sense to exclude from

analysis individual structure members that don't need to be initialized in the

constructor. Here's another synthetic example:

const size_t MAX_STACK_SIZE = 100;

class Stack

{

size_t m_size;

int m_array[MAX_STACK_SIZE];

public:

Stack() : m_size(0) {}

void Push(int value)

{

if (m_size == MAX_STACK_SIZE)

throw std::exception("overflow");

m_array[m_size++] = value;

}

int Pop()

{

Page 666: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

if (m_size == 0)

throw std::exception("underflow");

return m_array[--m_size];

}

};

This class implements a stack. The 'm_array' array is not initialized in the

constructor, and that's correct because the stack is considered originally empty.

The analyzer will output warning V730 as it can't figure out how this class works.

You can help it by marking the 'm_array' member with the comment //-

V730_NOINIT to specify that the 'm_array' array doesn't need to be necessarily

initialized.

From that point on, the analyzer won't produce the warning when analyzing this

code:

class Stack

{

size_t m_size;

int m_array[MAX_STACK_SIZE]; //-V730_NOINIT

public:

Stack() : m_size(0) {}

.....

};

V731. The variable of char type is compared with pointer to string.The analyzer has detected a comparison of a char variable with a pointer to a string.

The reason why the variable is used that way is in using double quotes (") instead of

single quotes (') by mistake.

Here's an example for this error pattern:

char ch = 'd';

Page 667: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

....

if(ch == "\n")

....

The inattentive author of this code wanted to compare the 'ch' variable with a new

string's character but used quotes of a wrong type. This resulted in the value of the

'ch' variable being compared to the "\n" string's address. Code like that can compile

and execute well in C but usually makes no sense. The correct version of the code

sample above should use single quotes instead of double ones:

char ch = 'd';

....

if(ch == '\n')

....

The same kind of mistake can be also made when initializing or assigning a value to

a variable, causing this variable to store the least significant byte of the address of

the string being assigned.

char ch = "d";

The correct version of the code should use single quotes.

char ch = 'd';

V732. Unary minus operator does not modify a bool type value.The analyzer has detected an issue when the unary minus operator is applied to a

value of type bool, BOOL, _Bool, and the like.

Consider the following example:

bool a;

....

Page 668: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

bool b = -a;

This code doesn't make sense. The expressions in it are evaluated based on the

following logic:

If a == false then 'false' turns into an int value 0. The '-' operator is then applied to

this value, without affecting it of course, so it is 0 (i.e. false) that will be written into

'b'.

If a == true then 'true' turns into an int value 1. The '-' operator is then applied to it,

resulting in value -1. However, -1 != 0; therefore, we'll still get value 'true' when

writing -1 into a variable of the bool type.

So 'false' will remain 'false' and 'true' will remain 'true'.

The correct version of the assignment operation in the code above should use the '!'

operator:

bool a;

....

bool b = !a;

Consider another example (BOOL is nothing but the int type):

BOOL a;

....

BOOL b = -a;

The unary minus can change the numerical value of a variable of type BOOL, but

not its logical value. Any non-zero value will stand for 'true', while zero will still

refer to 'false'.

Correct code:

BOOL a;

....

BOOL b = !a;

Note. Some programmers deliberately use constructs of the following pattern:

Page 669: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

int val = Foo();

int s;

s = -(val<0);

The analyzer does produce warnings on constructs like that. There's no error here,

but we still do not recommend writing your code that way.

Depending on the 'val' value, the 's' variable will be assigned either 0 or -1.

Applying the unary minus to a logical expression only makes the code less

comprehensible. Using the ternary operator instead would be more appropriate here.

s = (val < 0) ? -1 : 0;

V733. It is possible that macro expansion resulted in incorrect evaluation order.The analyzer has detected a potential error that has to do with the use of macros

expanding into arithmetic expressions. One normally expects that the subexpression

passed as a parameter into a macro will be executed first in the resulting expression.

However, it may not be so, and this results in bugs that are difficult to diagnose.

Consider this example:

#define RShift(a) a >> 3

....

y = RShift(x & 0xFFF);

If we expand the macro, we'll get the following expression:

y = x & 0xFFF >> 3;

Operation ">>" has higher priority than "&". That's why the expression will be

evaluated as "x & (0xFFF >> 3)", while the programmer expected it to be "(x &

0xFFF) >> 3".

Page 670: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

To fix this, we need to put parentheses around the 'a' argument:

#define RShift(a) (a) >> 3

However, there is one more improvement we should make. It is helpful to

parenthesize the whole expression in the macro as well. This is considered good

style and can help avoid some other errors. This is what the final improved version

of the sample code looks like:

#define RShift(a) ((a) >> 3)

V734. An excessive expression. Examine the substrings "abc" and "abcd".The analyzer detected a potential bug, connected with the fact that a longer and

shorter substrings are searched in the expression. With all that a shorter string is a

part of a longer one. As a result, one of the comparisons is redundant or there is a

bug here.

Consider the following example:

if (strstr(a, "abc") != NULL || strstr(a, "abcd") != NULL)

If substring "abc" is found, the check will not execute any further. If substring "abc"

is not found, then searching for longer substring "abcd" does not make sense either.

To fix this error, we need to make sure that the substrings were defined correctly or

delete extra checks, for example:

if (strstr(a, "abc") != NULL)

Here's another example:

if (strstr(a, "abc") != NULL)

Foo1();

Page 671: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

else if (strstr(a, "abcd") != NULL)

Foo2();

In this code, function Foo2() will never be called. We can fix the error by reversing

the check order to make the program search for the longer substring first and then

search for the shorter one:

if (strstr(a, "abcd") != NULL)

Foo2();

else if (strstr(a, "abc") != NULL)

Foo1();

V735. Possibly an incorrect HTML. The "</XX" closing tag was encountered, while the "</YY" tag was expected.The analyzer has detected a string literal containing HTML markup with errors: a

closing tag required for an element does not correspond with its opening tag.

Consider the following example:

string html = "<B><I>This is a text, in bold italics.</B>";

In this code, the opening tag "<I>" must be matched with closing tag "</I>";

instead, closing tag "</B>" is encountered further in the string. This is an error,

which renders this part of the HTML code invalid.

To fix the error, correct sequences of opening and closing tags must be ensured.

This is what the fixed version of the code should look like:

string html = "<B><I>This is a text, in bold italics.</I></B>";

Page 672: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V736. The behavior is undefined for arithmetic or comparisons with pointers that do not point to members of the same array.The behavior is undefined if arithmetic or comparison operations are applied to

pointers that point to items belonging to different arrays.

Consider the following example:

int a[10], b[20];

fill(a, b);

if (&a[1] > &b[2])

There is some bug in this code. For example, it could have been affected by bad

"find and replace" in some lines. Assume that the '&' operators are unnecessary

here. Then the fixed version should look like this:

if (a[1] > b[2])

V737. It is possible that ',' comma is missing at the end of the string.The analyzer suspects that a comma may be missing in the array initialization list.

Consider the following example:

int a[3][6] = { { -1, -2, -3

-4, -5, -6 },

{ ..... },

{ ..... } };

A comma was omitted by mistake after the value "-3", followed by "-4". As a result,

they form a single expression, "-3-4". This code compiles well, but the array turns

Page 673: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

out to be initialized incorrectly. The values "-5" and "-6" will be written into wrong

positions, and 0 will be written into the last item.

That is, the array will actually be initialized in the following way:

int a[3][6] = { { -1, -2, -7,

-5, -6, 0 },

..............

The fixed version of the code (with the missing comma restored) should look like

this:

int a[3][6] = { { -1, -2, -3,

-4, -5, -6 },

..............

V738. Temporary anonymous object is used.The analyzer detected that a temporary anonymous object is used which is created

as a result of executing the postfix ++ or -- operator. It does make sense sometimes,

but it is certainly an error when such temporary object is changed or its address is

retrieved.

Consider the following example:

vector<float>::iterator it = foo();

it++ = x;

In this code, a temporary copy of an iterator is created. Then the iterator is

incremented. After that, the assignment operator is applied to the temporary object.

This code doesn't make sense; the author obviously wanted it to do something else.

For example, they may have intended to execute the assignment operation first and

only then the increment operation.

In that case, the fixed version of the code should look like this:

Page 674: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

it = x;

it++;

However, postfix operations are not efficient with iterators, and a better version

would be the following:

it = x;

++it;

An alternative version:

it = x + 1;

Here's another example:

const vector<int>::iterator *itp = &it++;

The 'itp' pointer can't be used as it points to a temporary unnamed object already

destroyed. The correct version:

++it;

const vector<int>::iterator *itp = &it;

V739. EOF should not be compared with a value of the 'char' type. Consider using the 'int' type.The analyzer detected that the EOF constant is compared with a variable of type

'char' or 'unsigned char'. Such comparison implies that some of the characters won't

be processed correctly.

Let's see how EOF is defined:

#define EOF (-1)

That is, EOF is actually but the value '-1' of type 'int'. Let's see what complications

may occur. The first example:

Page 675: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

unsigned char c;

while ((c = getchar()) != EOF)

{ .... }

The unsigned variable 'c' can never refer to the negative value '-1', so the expression

((c = getchar) != EOF) is always true and an infinite loop occurs. An error like that

would be noticed and fixed right off in a real program, so there's no need to discuss

the 'unsigned char' type further.

Here's a more interesting case:

signed char c;

while ((c = getchar()) != EOF)

{ .... }

The getchar() function returns values of type 'int', namely numbers within the range

0 - 255 or the value -1 (EOF). The read value is assigned to a variable of type 'char'.

This operation causes the character with the code 0xFF (255) to turn into -1 and be

interpreted just the same way as the end of a file (EOF).

Users who use Extended ASCII Codes sometimes face an issue when one of the

characters of their alphabet is incorrectly processed by programs.

For example, the last letter of the Russian alphabet is encoded with that very value

0xFF in the Windows-1251 encoding and is interpreted as EOF by some programs.

The fixed version of the code should look like this:

int c;

while ((c = getchar()) != EOF)

V740. Because NULL is defined as 0, the exception is of the 'int' type. Keyword 'nullptr' could be used for 'pointer' type exception.

Page 676: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer detected that an exception of type 'int' will be thrown while the

programmer wanted it to be of type 'pointer'.

Consider the following example:

if (unknown_error)

throw NULL;

If an unknown error occurs, the programmer wants the program to throw a null

pointer. However, they didn't take into account that NULL is actually but an

ordinary 0. This is how the NULL macro is defined in C++ programs:

#define NULL 0

The value '0' is of type 'int', so the exception to be thrown will also be of type 'int'.

We're not concerned with the fact that using pointers for exception throwing is bad

and dangerous for now – suppose one really needs to do it exactly that way. Then

the fixed version of the code above should look like this:

if (unknown_error)

throw nullptr;

Why one shouldn't use pointers when working with exceptions is very well

explained in the following book:

Stephen C. Dewhurst. C++ Gotchas. Avoiding Common Problems in Coding and

Design. – Addison-Wesley Professional. – 352 pp.: ill., ISBN-10 0321125185.

V741. The following pattern is used: throw (a, b);. It is possible that type name was omitted: throw MyException(a, b);.

Page 677: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer detected the throw keyword followed by a pair of parentheses with

various values inside separated by commas. It is very likely that the programmer

forgot to specify the type of the exception to be thrown.

Consider the following example:

throw ("foo", 123);

Although the code looks strange, it compiles successfully. In this case, executing

the comma operator ',' results in the value 123. Therefore, an exception of type 'int'

will be thrown.

In other words, the code above is equivalent to the following:

throw 123;

Correct code:

throw MyException("foo", 123);

V742. Function receives an address of a 'char' type variable instead of pointer to a buffer.The analyzer detected an error that has to do with passing the address of a variable

of type 'char' to a string-handling function, which expects a pointer to a buffer of

characters instead. It may lead to runtime errors since functions working with

pointers to buffers of characters expect a number of characters and, sometimes, a

null terminator at the end of the buffer.

Consider the following example:

const char a = 'f';

size_t len = strlen(&a);

Page 678: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

In this code, a function that should return the length of a string receives a pointer to

variable 'a'. As a result, the whole memory block following the variable's address

until a null terminator is found is treated as a string. The outcome of executing this

function is undefined; it may return a random value or raise a memory access error.

This bug pattern is very uncommon and usually results from bad code editing or

mass replacement of substrings.

To fix the error, one should use a data set corresponding with a buffer of characters

or use functions processing single characters.

The fixed version of the code above should look like this:

const char a[] = "f";

size_t len = strlen(a);

V743. The memory areas must not overlap. Use 'memmove' function.The analyzer detected an error that has to do with using function memcpy when

dealing with overlapping source and destination memory blocks, in which case the

behavior is undefined [1, 2].

Consider the following example:

void func(int *x){

memcpy(x, x+2, 10 * sizeof(int));

}

In this case, the source pointer (x+2) is offset from the destination by 8 bytes

(sizeof(int) * 2). Copying 40 bytes from the source into the destination will lead to

partial overlapping of the source memory block.

To fix this error, one should use a special function, memmove(...), or revise the

offset between the source and destination blocks to avoid their overlapping.

Example of correct code:

Page 679: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

void func(int *x){

memmove(x, x+2, 10 * sizeof(int));

}

References:

1. StackOverflow. What is the difference between memmove and memcpy? Answer.

2. StackOverflow. memcpy() vs memmove().

V744. Temporary object is immediately destroyed after being created. Consider naming the object.The analyzer detected an error that has to do with the programmer forgetting to

name a newly created object. In that case, a temporary anonymous object will be

created and destroyed right afterwards. Sometimes programmers may want it that

way deliberately, and there's nothing bad about this practice; but it's obviously an

error when dealing with such classes as 'CWaitCursor' or 'CMultiLock'.

Consider the following example:

void func(){

CMutex mtx;

CSingleLock(&mtx, TRUE);

foo();

}

In this code, a temporary anonymous object of type 'CSingleLock' will be created

and destroyed right off, even before the foo() function is called. In this example, the

programmer wanted to make sure that the execution of the foo() function would be

synched, but actually it will be called without synching, and it may cause serious

errors.

Page 680: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

To avoid bugs like that, make sure you name objects you're creating.

Example of correct code:

void func(){

CMutex mtx;

CSingleLock lock(&mtx, TRUE);

foo();

}

V745. A 'wchar_t *' type string is incorrectly converted to 'BSTR' type string.The analyzer detected that a string of type "wchar_t *" is handled as a string of type

BSTR. It is very strange, and this code is very likely to be incorrect. To figure out

why such string handling is dangerous, let's first recall what the BSTR type is.

Actually, we will quote the article from MSDN. I know, people don't like reading

MSDN documentation, but we'll have to. We need to understand the danger behind

errors of this type – and diagnostic V745 does indicate serious errors in most cases.

typedef wchar_t OLECHAR;

typedef OLECHAR * BSTR;

A BSTR (Basic string or binary string) is a string data type that is used by COM,

Automation, and Interop functions. Use the BSTR data type in all interfaces that

will be accessed from script.

1. Length prefix. A four-byte integer that contains the number of bytes in the following data string. It appears immediately before the first character of the data string. This value does not include the terminating null character.

2. Data string. A string of Unicode characters. May contain multiple embedded null characters.

3. Terminator. Two null characters.

Page 681: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

A BSTR is a pointer. The pointer points to the first character of the data string, not

to the length prefix.

BSTRs are allocated using COM memory allocation functions, so they can be

returned from methods without concern for memory allocation.

The following code is incorrect:

BSTR MyBstr = L"I am a happy BSTR";

This code builds (compiles and links) correctly, but it will not function properly

because the string does not have a length prefix. If you use a debugger to examine

the memory location of this variable, you will not see a four-byte length prefix

preceding the data string.

Instead, use the following code:

BSTR MyBstr = SysAllocString(L"I am a happy BSTR");

A debugger that examines the memory location of this variable will now reveal a

length prefix containing the value 34. This is the expected value for a 17-byte

single-character string that is converted to a wide-character string through the

inclusion of the "L" string modifier. The debugger will also show a two-byte

terminating null character (0x0000) that appears after the data string.

If you pass a simple Unicode string as an argument to a COM function that is

expecting a BSTR, the COM function will fail.

I hope this excerpt from MSDN has explained well enough why one should not mix

BSTR strings and ordinary strings of type "wchar_t *".

Also, keep in mind that the analyzer can't tell for sure if there is a real error in the

code or not. If an incorrect BSTR string is passed somewhere outside the code, it

will cause a failure. But if a BSTR string is cast back to "wchar_t *", all is fine.

What is meant here is the code of the following pattern:

wchar_t *wstr = Foo();

BSTR tmp = wstr;

Page 682: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

wchar_t *wstr2 = tmp;

True, there's no real error here. But this code still "smells" and has to be fixed.

When fixed, it won't bewilder the programmer maintaining the code, and neither

will it trigger the analyzer's warning. Use proper data types:

wchar_t *wstr = Foo();

wchar_t *tmp = wstr;

wchar_t *wstr2 = tmp;

We also recommend reading the sources mentioned at the end of the article: they

will help you figure out what BSTR strings are all about and how to cast them to

strings of other types.

Here's another example:

wchar_t *wcharStr = L"123";

wchar_t *foo = L"12345";

int n = SysReAllocString(&wcharStr, foo);

This is the description of function SysReAllocString:

INT SysReAllocString(BSTR *pbstr, const OLECHAR *psz);

Reallocates a previously allocated string to be the size of a second string and copies

the second string into the reallocated memory.

As you see, the function expects, as its first argument, a pointer to a variable

referring to the address of a BSTR string. Instead, it receives a pointer to an

ordinary string. Since the "wchar_t **" type is actually the same thing as "BSTR *",

the code compiles correctly. In practice, however, it doesn't make sense and will

cause a runtime error.

The fixed version of the code:

BSTR wcharStr = SysAllocString(L"123");

wchar_t *foo = L"12345";

int n = SysReAllocString(&wcharStr, foo);

Page 683: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

References:

1. MSDN. BSTR.

2. StackOverfow. Static code analysis for detecting passing a wchar_t* to BSTR.

3. StackOverfow. BSTR to std::string (std::wstring) and vice versa.

4. Robert Pittenger. Guide to BSTR and CString Conversions.

V746. Type slicing. An exception should be caught by reference rather than by value.The analyzer detected a potential error that has to do with catching an exception by

value. It is much better and safer to catch exceptions by reference.

Catching exceptions by value causes two types of issues. We'll discuss each of them

separately.

Issue No. 1. Slicing.

class Exception_Base {

....

virtual void Print() { .... }

};

class Exception_Ex : public Exception_Base { .... };

try

{

if (error) throw Exception_Ex(1, 2, 3);

}

catch (Exception_Base e)

{

e.Print();

throw e;

}

Page 684: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

2 classes are declared here: an exception of a base type and an extended exception

derived from the first one.

An extended exception is generated. The programmer wants to catch it, print its

information, and then re-throw it.

The exception is caught by value. It means that a copy constructor will be used to

create a new object, 'e', of type Exception_Base, and it will lead to 2 errors at once.

Firstly, some of the information about the exception will get lost; everything stored

in Exception_Ex won't be available anymore. The virtual function Print() will only

allow printing the basic information about the exception.

Secondly, what will be re-thrown is a new exception of type Exception_Base.

Therefore, the information passed on will be sliced.

The fixed version of that code is as follows:

catch (Exception_Base &e)

{

e.Print();

throw;

}

Now the Print() function will print all the necessary information. The "throw"

statement will re-throw the already existing exception, and the information won't

get lost (sliced).

Issue No. 2. Changing a temporary object.

catch (std::string s)

{

s += "Additional info";

throw;

}

The programmer wants to catch the exception, add some information to it, and re-

throw it. The problem here is that it is the 's' variable that gets changed instead

Page 685: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

while the "throw;" statement re-throws the original exception. Therefore, the

information about the exception won't be changed.

Correct code:

catch (std::string &s)

{

s += "Additional info";

throw;

}

The pros of catching exceptions by reference are discussed in the following topics:

1. StackOverflow. C++ catch blocks - catch exception by value or reference?

2. StackOverflow. Catch exception by pointer in C++.

3. Stephen C. Dewhurst. C++ Gotchas. Avoiding Common Problems in Coding and Design. – Addison-Wesley Professional. – 352 pp.: ill., ISBN-10 0321125185.

V747. An odd expression inside parenthesis. It is possible that a function name is missing.The analyzer detected a suspicious expression in parentheses consisting of various

variables and values separated by commas. However, it doesn't look like the comma

operators ',' are used to reduce the code.

Consider the following example:

if (memcmp(a, b, c) < 0 && (x, y, z) < 0)

When writing the program, the author forgot to write the function name, 'memcmp'.

However, the code still compiles successfully, although it doesn't work as intended.

In the right part, executing two comma operators results in variable 'z'. It is this

variable that is compared with zero. So, this code turns out to be equivalent to the

following:

Page 686: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

if (memcmp(a, b, c) < 0 && z < 0)

Correct code:

if (memcmp(a, b, c) < 0 && memcmp(x, y, z) < 0)

Note. Sometimes, the ',' operator is used to reduce code. That's why the analyzer

doesn't always output the warning about commas inside parentheses. For example,

it treats the following code as correct:

if (((std::cin >> A), A) && .....)

We do not recommend writing complex expressions like this because it is going to

make it difficult for your colleagues to read such code. But there is no apparent

error either. It's just that the developer wanted to combine the operations of

retrieving a value and checking it in one expression.

Here's another similar example:

if (a)

return (b = foo(), fooo(b), b);

V748. Memory for 'getline' function should be allocated only by 'malloc' or 'realloc' functions. Consider inspecting the first parameter of 'getline' function.The analyzer detected an error that has to do with allocating memory for

the getline() function without using the function malloc()/realloc(). The getline()

function is written in such a way that if the already allocated memory is not enough,

getline() will call realloc() to expand the memory block (ISO/IEC TR 24731-2).

That's why memory can be allocated only using functions malloc() or realloc().

Page 687: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Consider the following example:

char* buf = new char[count];

getline(&buf, &count, stream);

In this code, memory for the function getline() is allocated using the new operator.

If getline() needs more storage than that already allocated, it will call the realloc()

function. The result of such call is unpredictable.

To fix the code, we need to rewrite it so that only functions malloc() or realloc() are

used to allocate memory for the getline() function.

Correct code:

char* buf = (char*)malloc(count * sizeof(char));

getline(&buf, &count, stream);

V749. Destructor of the object will be invoked a second time after leaving the object's scope.The analyzer detected an error that has to do with calling a destructor for the second

time. When an object is created on the stack, the destructor will be called when the

object leaves the scope. The analyzer detected a direct call to the destructor for this

object.

Consider the following example:

void func(){

X a;

a.~X();

foo();

}

In this code, the destructor for the 'a' object is called directly. But when the 'func'

function finished, the destructor for 'a' will be called once again.

Page 688: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

To fix this error, we need to remove incorrect code or adjust the code according to

the memory management model used.

Correct code:

void func(){

X a;

foo();

}

V750. BSTR string becomes invalid. Notice that BSTR strings store their length before start of the text.The analyzer detected that inadmissible operations are executed over a BSTR string.

A pointer of type BSTR must always refer to the first character of the string; if you

shift the pointer by at least one character, you'll get an invalid BSTR string.

It means that code like the following example is very dangerous:

BSTR str = foo();

str++;

'str' can no longer be used as a BSTR string. If you need to skip one character, use

the following code instead:

BSTR str = foo();

BSTR newStr = SysAllocString(str + 1);

If you don't need the BSTR string, rewrite the code in the following way:

BSTR str = foo();

const wchar_t *newStr = str;

newStr++;

Another version:

BSTR str = foo();

Page 689: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

const wchar_t *newStr = str + 1;

To figure out why one must not change the value of a BSTR pointer, let's see

the article form MSDN.

typedef wchar_t OLECHAR;

typedef OLECHAR * BSTR;

A BSTR (Basic string or binary string) is a string data type that is used by COM,

Automation, and Interop functions. Use the BSTR data type in all interfaces that

will be accessed from script.

1. Length prefix. A four-byte integer that contains the number of bytes in the following data string. It appears immediately before the first character of the data string. This value does not include the terminating null character.

2. Data string. A string of Unicode characters. May contain multiple embedded null characters.

3. Terminator. Two null characters.

A BSTR is a pointer. The pointer points to the first character of the data string, not

to the length prefix.

BSTRs are allocated using COM memory allocation functions, so they can be

returned from methods without concern for memory allocation.

The following code is incorrect:

BSTR MyBstr = L"I am a happy BSTR";

This code builds (compiles and links) correctly, but it will not function properly

because the string does not have a length prefix. If you use a debugger to examine

the memory location of this variable, you will not see a four-byte length prefix

preceding the data string.

Instead, use the following code:

BSTR MyBstr = SysAllocString(L"I am a happy BSTR");

Page 690: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

A debugger that examines the memory location of this variable will now reveal a

length prefix containing the value 34. This is the expected value for a 17-byte

single-character string that is converted to a wide-character string through the

inclusion of the "L" string modifier. The debugger will also show a two-byte

terminating null character (0x0000) that appears after the data string.

If you pass a simple Unicode string as an argument to a COM function that is

expecting a BSTR, the COM function will fail.

I hope this excerpt has explained well enough why one can't simply change a

pointer of type BSTR.

When using code like this:

BSTR str = foo();

str += 3;

the BSTR string gets spoiled. The pointer now refers somewhere to the middle of

the string instead of its first character. So, if we attempt to read the string length at a

negative offset, we'll get a random value. More specifically, the previous characters

will be interpreted as the string length.

References:

1. MSDN. BSTR.

2. StackOverfow. Static code analysis for detecting passing a wchar_t* to BSTR.

3. StackOverfow. BSTR to std::string (std::wstring) and vice versa.

4. Robert Pittenger. Guide to BSTR and CString Conversions.

V751. Parameter is not used inside method's body.The analyzer detected a suspicious method where one of the parameters is never

used while another parameter is used several times. It may indicate an error in the

code. Consider the following example:

Page 691: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

static bool CardHasLock(int width, int height)

{

const double xScale = 0.051;

const double yScale = 0.0278;

int lockWidth = (int)floor(width * xScale);

int lockHeight = (int)floor(width * yScale);

....

}

The 'height' parameter is never used in the method body while the 'width' parameter

is used twice, including the initialization of the 'lockHeight' variable. There is very

likely an error here and the code initializing the 'lockHeight' variable should

actually look like this:

int lockHeight = (int)floor(height * yScale);

V752. Creating an object with placement new requires a buffer of large size.The analyzer detected an attempt to create an object using 'placement new' while the

size of the allocated storage is not large enough to store this object. This issue will

result in using additional memory outside the allocated block and may cause a crash

or incorrect program behavior.

Consider the following example:

struct T { float x, y, z, q; };

char buf[12];

T *p = new (buf) T;

In this code, the programmer is trying to store an object of size 16 bytes in the 'buf'

buffer of size 12 bytes. When using this object, the memory outside the buffer

bounds will be changed. The result of such change is unpredictable.

Page 692: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

To fix this error, we need to adjust the buffer size or make sure that the offset from

the beginning of the buffer is specified correctly.

Fixed code:

struct T { float x, y, z, q; };

char buf[sizeof(T)];

T *p = new (buf) T;

V753. The '&=' operation always sets a value of 'Foo' variable to zero.The analyzer detected that applying a bitwise "AND" operator to a variable results

in setting its value to zero, which is strange because a simpler way to get a null

value is by using an assignment operation. If this operation participates in a series of

computations, it is likely to execute incorrectly – for example, it is applied to a

wrong variable, or a wrong constant is used as the right operand because of a typo.

There are several scenarios when this warning is triggered.

The first case is when the operator is sequentially applied to a variable with

unknown value and the right operand is represented by such constants that lead to

the expression evaluating to zero:

void foo(int A)

{

A &= 0xf0;

....

A &= 1;

// 'A' now always equals 0.

}

Page 693: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Executing these two operations will result in a null value regardless of the initial

value of the 'A' variable. This code probably contains an error, and the programmer

needs to check the correctness of the constants used.

The second case deals with applying the operator to a variable whose value is

known:

void foo()

{

int C;

....

C = 1;

....

C &= 2;

// C == 0

}

In this case, the result is a null value, too. Like in the previous case, the programmer

needs to check the correctness of the constants used.

The diagnostic can also be triggered by the following code, which is quite common:

void foo()

{

int flags;

....

flags = 1;

....

flags &= ~flags;

....

}

This technique is sometimes used by programmers to reset a set of flags. We believe

that this technique is unjustified and may confuse your colleagues. A simple

assignment is more preferable:

Page 694: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

void foo()

{

int flags;

....

flags = 1;

....

flags = 0;

....

}

V754. The expression of 'foo(foo(x))' pattern is excessive or contains an error.The analyzer detected a function that receives a call to itself as an argument.

Consider the following example:

char lower_ch = tolower(tolower(ch));

The second function call is redundant. Perhaps this sample contains a typo and the

programmer actually meant to call to some other function instead. If there is no

mistake, then the extra call should be removed because expressions like that look

suspicious:

char lower_ch = tolower(ch);

Another example:

if (islower(islower(ch)))

do_something();

The 'islower' function returns a value of type 'int' and can be used as an argument to

itself. This expression contains an error and serves no purpose.

Page 695: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V755. Copying from unsafe data source. Buffer overflow is possible.The analyzer detected that data from an unsafe source are copied to a buffer of a

fixed size.

Command-line arguments of unknown length are one example of such a source:

char *tmp = (char*)malloc(1024);

....

strcat(tmp, argv[0]);

If the size of the data being copied exceeds that of the buffer, it will overflow. To

avoid this error, the required size of the storage to be allocated should be computed

in advance:

char *src = GetData();

char *tmp = (char*)malloc(strlen(src) + strlen(argv[0]) + 1);

....

strcpy(tmp, src);

strcat(tmp, argv[0]);

You can also use the "realloc()" function to allocate memory as needed. In C++,

you can use such classes as "std::string" to handle strings.

The warning is not triggered when the data source is unknown:

char *src = GetData();

char *tmp = malloc(1024);

....

strcat(tmp, src);

Page 696: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V756. The 'X' counter is not used inside a nested loop. Consider inspecting usage of 'Y' counter.The analyzer detected a possible error in two or more nested 'for' loops, when the

counter of one of the loops is not used because of a typo.

Consider the following synthetic example of incorrect code:

for (int i = 0; i < N; i++)

for (int j = 0; j < M; j++)

sum += matrix[i][i];

The programmer wanted to process all the elements of a matrix and find their sum

but made a mistake and wrote variable 'i' instead of 'j' when indexing into the

matrix.

Fixed version:

for (int i = 0; i < N; i++)

for (int j = 0; j < M; j++)

sum += matrix[i][j];

Unlike diagnostics V533, and V534, this one deals with indexing errors only in loop

bodies.

V757. It is possible that an incorrect variable is compared with null after type conversion using 'dynamic_cast'.The analyzer has detected a potential error that may lead to memory access by a null

pointer.

Page 697: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The situation that the analyzer detected deals with the following algorithm. An

object of the base class is first cast to a derived class by using the 'dynamic_cast'

operator. Then the same object is checked for a nullptr value, though it is the object

of the derived class that this check should have been applied to.

Here's an example. In this code, the 'baseObj' object may not be an instance of the

'Derived' class, in which case, when calling the 'Func' function, the null pointer will

be dereferenced. The analyzer will output a warning pointing out two lines. The first

line is the spot where the object of the base class is checked for nullptr; the second

is where it is cast to an object of the derived class.

Base *baseObj;

Derived *derivedObj = dynamic_cast<Derived *>(baseObj);

if (baseObj != nullptr)

{

derivedObj->Func();

}

It is most likely the object of the derived class that the programmer intended to

check for nullptr before using it. This is the fixed version of the code:

Base *baseObj;

Derived *derivedObj = dynamic_cast<Derived *>(baseObj);

if (derivedObj != nullptr)

{

derivedObj->Func();

}

V758. Reference invalidated, because of the destruction of the temporary object 'unique_ptr', returned by function.

Page 698: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer detected a reference that may become invalid. The object this

reference refers to is managed by smart pointer 'unique_ptr', which is returned from

a function by value. After the function has returned, the temporary object of type

'unique_ptr' will be destroyed together with the object managed by it. Therefore, the

reference to this object will become invalid. An attempt to use such a reference

leads to undefined behavior.

Consider the following example:

std::unique_ptr<A> Foo()

{

std::unique_ptr<A> pa(new A());

return pa;

}

void Foo2()

{

const A &ra = *Foo();

ra.foo();

}

The reference refers to an object managed by smart pointer 'unique_ptr'. After the

function has returned, the temporary object 'unique_ptr' is destroyed, thus making

the reference invalid.

To avoid issues like that, one should avoid using references with such objects and

instead rewrite the 'foo()' function in the following way:

void Foo2()

{

A a(*Foo());

a.foo();

}

This solution allows us to create a new object of type 'A' instead of using the

reference. Note that starting with C++11 a move constructor can be used to

initialize variable 'a' to prevent any performance losses.

Page 699: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The following solution is also possible:

void Foo2()

{

std::unique_ptr<A> pa = Foo();

pa->foo();

}

This code transfers the ownership of an object of type 'A' to another object.

V759. Violated order of exception handlers. Exception caught by handler for base class.The analyzer detected multiple exception handlers arranged in a wrong order. The

handler for base-class exceptions is placed before the handler for derived-class

exceptions; therefore, every exception that must be caught by the derived-class

handler will be caught by the base-class handler.

Consider the following example:

class Exception { .... };

class DerivedException : public Exception { ... };

void foo()

{

throw DerivedException;

}

void bar()

{

try

{

foo();

}

catch (Exception&)

{

Page 700: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

// Every exception of type DerivedException will get here

}

catch (DerivedException&)

{

// Code of this handler will never execute

}

}

Since 'Exception' is the base class for the 'DerivedException' class, all exceptions

thrown by the 'foo()' function are caught by the first handler.

To fix this error, we need to swap the handlers:

void bar()

{

try

{

foo();

}

catch (DerivedException&)

{

// Catches exceptions of type DerivedException

}

catch (Exception&)

{

// Catches exceptions of type Exception

}

}

With this fix, each handler will catch only those exceptions it was meant to.

V760. Two identical text blocks detected. The second block starts with NN string.

Page 701: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer detected a code fragment that may contain a typo. It is very likely that

this code was written using the Copy-Paste technique. Warning V760 is triggered

when the analyzer detects two identical text blocks following one another. This

diagnostic basically relies on heuristics and, therefore, may produce false positives.

Consider the following example:

void Example(int *a, int *b, size_t n)

{

....

for (size_t i = 0; i != n; i++)

a[i] = 0;

for (size_t i = 0; i != n; i++)

a[i] = 0;

....

}

This code was written using the Copy-Paste technique, and the programmer forgot

to change the array name in the second block. This is what the code was meant to

look like:

void Example(int *a, int *b, size_t n)

{

....

for (size_t i = 0; i != n; i++)

a[i] = 0;

for (size_t i = 0; i != n; i++)

b[i] = 0;

....

}

This message is not generated for more than two identical blocks, for example:

void Foo();

void Example()

{

....

Page 702: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Foo();

Foo();

Foo();

Foo();

....

}

Sometimes the reason for generating the warning is not obvious. Consider this

example:

switch(t) {

case '!': InvokeMethod(&obj_Sylia, "!", 1); break;

case '~': InvokeMethod(&obj_Sylia, "~", 1); break;

case '+': InvokeMethod(&obj_Sylia, "+", 1); break;

case '-': InvokeMethod(&obj_Sylia, "-", 1); break;

break;

default:

SCRIPT_ERROR(PARSE_ERROR);

}

We need to take a closer look: in this example, we are dealing with very short

repeated block, the 'break' statement. One of its instances is not necessary. This

defect does not cause a real bug, but the extra 'break' should be removed:

switch(t) {

case '!': InvokeMethod(&obj_Sylia, "!", 1); break;

case '~': InvokeMethod(&obj_Sylia, "~", 1); break;

case '+': InvokeMethod(&obj_Sylia, "+", 1); break;

case '-': InvokeMethod(&obj_Sylia, "-", 1); break;

default:

SCRIPT_ERROR(PARSE_ERROR);

}

Note

Page 703: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Code duplication is not in itself an error. However, even when there is no real bug,

the V760 warning can be treated as a hint that you should put identical code blocks

in a function. See also diagnostic V761.

V761. NN identical blocks were found.The analyzer detected code that could be refactored. This diagnostic looks for three

or more identical code blocks. Such repeated code is unlikely to be incorrect, but it

is better to factor it out in a separate function.

If your code employs a lot of local variables, use lambda functions to capture data

by reference.

This diagnostic can be triggered multiple times by code that uses numerous manual

optimizations (for example manual loop unrolling). If you find the V761 diagnostic

irrelevant to your project, turn it off.

Consider the following synthetic example:

void process(char *&buf);

void func(size_t n, char *arr)

{

size_t i;

i = n;

while (i--)

arr[i] = 1;

for (i = 0; i != 10; i++)

arr[i] = 'a';

process(arr);

i = n;

while (i--)

arr[i] = 1;

Page 704: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

for (i = 0; i != 10; i++)

arr[i] = 'a';

process(arr);

i = n;

while (i--)

arr[i] = 1;

for (i = 0; i != 10; i++)

arr[i] = 'a';

process(arr);

i = n;

while (i--)

arr[i] = 1;

for (i = 0; i != 10; i++)

arr[i] = 'a';

process(arr);

}

It is a good solution to factor out the common code in a separate function:

void process(char*& buf);

void func_impl(size_t i, size_t *&arr)

{

while (i--)

arr[i] = 1;

for (i = 0; i != 10; i++)

arr[i] = 'a';

process(arr);

}

void func(size_t n, char *arr)

{

for (size_t i = 0; i < 4; ++i)

func_impl(n, arr);

}

Page 705: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

See also diagnostic V760.

V762. Consider inspecting virtual function arguments. See NN argument of function 'Foo' in derived class and base class.This diagnostic detects errors related to overriding of virtual functions and is

generated in two situations.

Situation 1. A base class includes a virtual function with a parameter of some type.

There is also a derived class with the same function, but its corresponding

parameter is of another type. The types involved can be integer, enumerations, or

pointers or references to the base and derived classes.

The diagnostic helps detect errors that occur during extensive refactoring, when you

change the function type in one of the classes but forget to change it in the other.

Consider the following example:

struct Q { virtual int x(short) { return 1; } };

struct W : public Q { int x(int) { return 2; } };

This code should actually look like this:

struct Q { virtual int x(short) { return 1; } };

struct W : public Q { int x(short) { return 2; } };

If there are two functions 'x' with arguments 'int' and 'short' in the base class, the

analyzer will not generate the V761 warning.

Situation 2. The diagnostic is triggered when an argument has been added to or

removed from a function in the base class, while the number of arguments in the

function declaration in one of the derived classes is left unchanged.

Consider the following example:

Page 706: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

struct Q { virtual int x(int, int=3) { return 1; } };

struct W : public Q { int x(int) { return 2; } };

Fixed code:

struct Q { virtual int x(int, int=3) { return 1; } };

struct W : public Q { int x(int, int) { return 2; } };

Here is an example of how errors in this scenario can occur. There is a hierarchy of

classes. At some point, an argument is added to a function of the base or a derived

class, which results in declaring a new function that is not related to the function of

the base class in any way.

Such declaration looks strange and might be a sign of an error. Perhaps the

programmer forgot to fix one of the classes or did not take into account that the

function was virtual. However, the analyzer cannot understand if this code is correct

based on the function's logic. If this behavior is intended and is not an error, use one

of the false-positive suppression mechanisms to suppress the warning.

Consider the following example:

struct CA

{

virtual void Do(int Arg);

};

struct CB : CA

{

virtual void Do(int Arg1, double Arg2);

};

To avoid errors like that when using the C++11 standard and better, use the

'override' keyword, which will help avoid signature mismatch at the compilation

stage.

V763. Parameter is always rewritten in function body before being used.

Page 707: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer detected a potential error in the body of a function: one of the function

parameters is overwritten before being used, which results in losing the value

passed to the function.

Consider the following example:

void Foo(Node A, Node B)

{

A = SkipParenthesize(A);

B = SkipParenthesize(A); // <=

AnalyzeNode(A);

AnalyzeNode(B);

}

The 'A' and 'B' parameters are mixed up because of a typo, which leads to assigning

a wrong value to the 'B' variable. The fixed code should look like this:

void Foo(Node A, Node B)

{

A = SkipParenthesize(A);

B = SkipParenthesize(B);

AnalyzeNode(A);

AnalyzeNode(B);

}

V764. Possible incorrect order of arguments passed to function.The analyzer detected a suspicious sequence of arguments being passed to a

function: some of the arguments' names do not correspond with the names of the

parameters they are meant to represent. It may indicate an error when passing

values to a function.

Let we have the following declaration of the function:

void SetRGB(unsigned r, unsigned g, unsigned b);

Page 708: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Here's an example of incorrect code:

void Foo(){

unsigned R = 0, G = 0, B = 0;

....

SetRGB(R, B, G);

....

}

When defining the object color, the programmer accidentally swapped the blue and

green color parameters.

The fixed version of the code should look like this:

SetRGB(R, G, B);

V765. A compound assignment expression 'X += X + N' is suspicious. Consider inspecting it for a possible error.The analyzer detected a potential error in an arithmetic or logical expression: a

variable is used both in the left and the right parts of a compound-assignment

expression. Consider the following example:

void Foo(int x, int y, int z)

{

x += x + y;

....

}

This code is likely to contain a typo and was probably meant to look like this:

void Foo(int x, int y, int z)

{

Page 709: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

x = x + y;

....

}

Or like this:

void Foo(int x, int y, int z)

{

x += z + y;

....

}

It is true that programmers use expressions like these as a tricky means to multiply a

number by two, but such code is strange and needs to be checked. Such expressions

look rather complicated and probably should be rewritten in a simpler and clearer

way:

void Foo(int x, int y, int z)

{

x = x * 2 + y;

....

}

There are also more suspicious expressions that need to be inspected:

void Foo(int x, int y)

{

x -= x + y;

}

This expression can be simplified in the following way:

x -= x + y;

x = x - (x + y);

x = -y;

It is not clear if this behavior is intended or caused by a typo. In any case, this code

should be checked.

Page 710: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V766. An item with the same key has already been added.The analyzer detected the following strange situation: items are being added to a

dictionary (containers of type 'map', etc.) or set (containers of type 'set', etc.) while

having the same keys that are already present in these containers, which will result

in ignoring the newly added items. This issue may be a sign of a typo and result in

incorrect filling of the container.

Consider the following example with incorrect dictionary initialization:

map<char, int> dict = map<char, int>{

make_pair('a', 10),

make_pair('b', 20),

make_pair('a', 30) // <=

};

The programmer made a typo in the last line of the code performing dictionary

initialization, as the 'a' key is already in the dictionary. As a result, this dictionary

will contain 2 values, and the 'a' key will have the value 10.

To fix the error, we need to use a correct key value:

map<char, int> dict = map<char, int>{

make_pair('a', 10),

make_pair('b', 20),

make_pair('c', 30)

};

A similar error may occur when initializing a set:

set<string> someSet = set<string>{

"First",

"Second",

"Third",

"First", // <=

Page 711: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

"Fifth"

};

A typo results in an attempt to write string 'First' instead of the 'Fourth' key to the

'someSet' set, but since this key is already in the set, it will be ignored.

To fix this error, we need to fix the initialization list:

set<string> someSet = set<string>{

"First",

"Second",

"Third",

"Fourth",

"Fifth"

};

V767. Suspicious access to element by a constant index inside a loop.The analyzer detected a possible error that has to do with accessing an element of an

array or container by the same constant index at each iteration of a 'for' loop.

Consider the following example:

void Foo(vector<size_t> &vect)

{

for (size_t i = 0; i < vect.size(); i++)

vect[0] *= 2;

}

The programmer intended this function to change all the values in a vector but made

a typo that causes the vector elements to be accessed using the constant value 0

instead of the loop counter 'i'. It will result in changing only one value (unless the

vector is empty).

To fix this error, we need to rewrite the line where the container's elements are

accessed:

Page 712: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

void Foo(vector<size_t> &vect)

{

for (size_t i = 0; i < vect.size(); i++)

vect[i] *= 2;

}

V768. The variable is of enum type. It is odd that it is used as a variable of a Boolean-type.The analyzer detected a suspicious code fragment where a named constant from an

enumeration or a variable of type 'enum' is used as a Boolean value. It is very likely

to be a logic error.

Consider the following example:

enum Offset { left=10, right=15, top=20, bottom=25 };

void func(Offset offset)

{

....

if (offset || i < 10)

{

....

}

}

In this code, the 'offset' variable of type 'enum' is used as a Boolean value, but since

all the values in the 'Offset' enumeration are non-zero, the condition will always be

true. The analyzer warns us that the expression is incorrect and should be fixed, for

example like this:

void func(Offset offset)

{

....

if (offset == top || i < 10)

Page 713: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

{

....

}

}

Here is one more example. Suppose we have the following enumeration:

enum NodeKind

{

NK_Identifier = 64,

....

};

And the following class:

class Node

{

public:

NodeKind _kind;

bool IsKind(ptrdiff_t kind) const { return _kind == kind; }

};

The error then may look something like this:

void foo(Node node)

{

if (node.IsKind(!NK_Identifier))

return;

....

}

The programmer expects the function to return if the current node is not an

identifier. However, the '!NK_Identifier' expression evaluates to '0', while no such

elements are found in the 'NodeKind' enumeration. As a result, the 'IsKind' method

will always return 'false' and the function will continue running no matter if the

current node is an identifier or not.

The fixed code should look like this:

Page 714: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

void foo(Node node)

{

if (!node.IsKind(NK_Identifier))

return;

....

}

V769. The pointer in the expression equals nullptr. The resulting value is meaningless and should not be used.The analyzer detected a strange operation involving a null pointer and making the

resulting pointer meaningless. Such behavior indicates a logic error.

Consider the following example:

void foo(bool isEmpty, char *str)

{

char *begin = isEmpty ? str : nullptr;

char *end = begin + strlen(str);

....}

If the 'begin' pointer equals nullptr, the "nullptr + len" expression does not make

sense: you cannot use it anyway. Perhaps the variable will not be used anymore. In

that case, the code should be refactored so that this operation is never applied to a

null pointer, as the programmer who will be dealing with the code may forget that

the variable should not be used and attempt to access the data pointed to by the

incorrect pointer, which will lead to errors.

The code above can be modified in the following way:

void foo(bool isEmpty, char *str)

{

Page 715: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

char *begin = isEmpty ? str : nullptr;

if (begin != nullptr)

{

char *end = begin + strlen(str);

....

}

....

}

V770. Possible use of a left shift operator instead of a comparison operator.The analyzer detected a potential typo that deals with using operators '<<' and '<<='

instead of '<' and '<=', respectively, in a loop condition.

Consider the following example:

void Foo(std::vector<int> vec)

{

for (size_t i = 0; i << vec.size(); i++) // <=

{

// Something

}

}

The "i << vec.size()" expression evaluates to zero, which is obviously an error

because the loop body will not execute even once. Fixed code:

void Foo(std::vector<int> vec)

{

for (size_t i = 0; i < vec.size(); i++)

{

// Something

}

Page 716: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

}

Note. Using right-shift operations (>>, >>=) is considered a normal situation, as

they are used in various algorithms, for example computing the number of bits with

the value 1, for example:

size_t num;

unsigned short var = N;

for (num = var & 1 ; var >>= 1; num += var & 1);

V771. The '?:' operator uses constants from different enums.The analyzer detected a possible error that has to do with the ternary operator '?:'

using constants from different enumerations as its second and third operands.

Consider the following example:

enum OnlyOdd { Not_Odd, Odd };

enum OnlyEven { Not_Even, Even };

int isEven(int a)

{

return (a % 2) == 0 ? Even : Odd;

}

This function checks if the number passed as an argument is even, but its return

value is evaluated using constants from two different enums (OnlyEven::Even and

OnlyOdd::Odd) cast to 'int'. This mistake will cause the function to return 1 (true)

all the time regardless of the 'a' argument's actual value. This is what the fixed code

should look like:

enum OnlyOdd { Not_Odd, Odd };

enum OnlyEven { Not_Even, Even };

Page 717: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

int isEven(int a)

{

return (a % 2) == 0 ? Even : Not_Even;

}

Note. Using two different unnamed enumerations is considered a normal practice,

for example:

enum

{

FLAG_FIRST = 0x01 << 0,

FLAG_SECOND = 0x01 << 1,

....

};

enum

{

FLAG_RW = FLAG_FIRST | FLAG_SECOND,

....

};

....

bool condition = ...;

int foo = condition ? FLAG_SECOND : FLAG_RW; // no V771

....

V772. Calling the 'delete' operator for a void pointer will cause undefined behavior.The analyzer detected a possible error that has to do with using the 'delete' or 'delete

[]' operator together with a non-typed pointer (void*). As specified by the C++

standard (section $5.3.5/3), such use of 'delete' results in undefined behavior.

Consider the following example:

Page 718: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

class Example

{

int *buf;

public:

Example(size_t n = 1024) { buf = new int[n]; }

~Example() { delete[] buf; }

};

....

void *ptr = new Example();

....

delete ptr;

....

What is dangerous about this code is that the compiler does not actually know the

type of the 'ptr' pointer. Therefore, deleting a non-typed pointer may cause various

defects, for example, a memory leak, as the 'delete' operator will not call the

destructor for the object of type 'Example' pointed to by 'ptr'.

If you really mean to use a non-typed pointer, then you need to cast it to the original

type before using 'delete' ('delete[]'), for example:

....

void *ptr = new Example();

....

delete (Example*)ptr;

....

Otherwise, it is recommended that you use only typed pointers with 'delete'

('delete[]') to avoid errors:

....

Example *ptr = new Example();

....

delete ptr;

....

Page 719: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V773. The function was exited without releasing the pointer/handle. A memory/resource leak is possible.The analyzer detected a potential memory leak. This situation occurs when memory

allocated by using 'malloc' or 'new' remains unreleased after use.

Consider the following example:

int *NewInt()

{

int *p = new int;

....

return p;

}

int Test()

{

int *p = NewInt();

int res = *p;

return res;

}

In this code, memory allocation is put into a call to another function. Therefore, the

allocated storage needs to be released accordingly after the call.

This is the fixed code, without the memory leak:

int *NewInt()

{

int *p = new int;

....

return p;

}

Page 720: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

int Test()

{

int *p = NewInt();

int res = *p;

delete p;

return res;

}

Errors of this kind are often found in error handlers because they are generally

poorly tested and treated without due care by programmers when doing code

reviews. For example:

int Test()

{

int *p = (int*)malloc(sizeof(int));

int *q = (int*)malloc(sizeof(int));

if (p == nullptr || q == nullptr)

{

std::cerr << "No memory";

return -1;

}

int res = *p + *q;

free(p);

free(q);

return res;

}

A situation may occur that the 'p' pointer would point to allocated memory, while 'q'

would be 'nullptr'. If this happens, the allocated memory will not be released. By the

way, an opposite problem is also possible: in a parallel program, you may encounter

a situation when memory allocation fails on the first attempt but succeeds on the

second.

Besides the memory leaks, the analyzer is able to find resource leaks: unclosed

descriptors, files, etc. Such errors aren't different from each other, that's why

everything said above refers to them as well. Here is a small example:

Page 721: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

void LoadBuffer(char *buf, size_t len)

{

FILE* f = fopen("my_file.bin", "rb");

fread(buf, sizeof(char), len, f);

}

Note. In modern C++, it is better to avoid manual resource management and use

smart pointers instead. For example, we recommend using 'std::unique_ptr': it will

ensure correct memory release in all the function return points. This solution is also

exception-safe.

V774. The pointer was used after the memory was released.The analyzer detected the use of a pointer that points to released buffer. This is

considered undefined behavior and can lead to various complications. Some

possible scenarios:

writing to memory pointed to by such a pointer can spoil some other object;

reading from memory pointed to by such a pointer can result in returning random values;

handling such a pointer will result in a crash.

Consider the following example:

for (node *p = head; p != nullptr; p = p->next)

{

delete p;

}

In this code, the 'p' pointer, which gets deleted in the loop body, will be

dereferenced when evaluating the 'p = p->next' expression. The expression must be

evaluated first, and only then can the storage be released. This is what the fixed

code should look like:

node *p = head;

Page 722: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

while (p != nullptr)

{

node *prev = p;

p = p->next;

delete prev;

}

What makes errors of this kind especially annoying is that programs may appear to

work properly for a long time and break after slight refactoring, adding a new

variable, switching to another compiler, and so on.

V775. It is odd that the BSTR data type is compared using a relational operator.The analyzer detected a suspicious comparison operation involving an element of

BSTR-type and relational operators: >, <, >=, <=.

BSTR (basic string or binary string) is a string data type used in COM, Automation,

and Interop functions. This data type consists of a length prefix, a data string, and

a terminal null.

The BSTR type is a pointer that always points to the first character of the data

string, not the length prefix. For this reason, every BSTR object is unique and one

BSTR object cannot be part of another, unlike ordinary strings.

Page 723: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

However, an ordinary string can be part of a BSTR object (but never vice versa), so

comparisons of the 

"wchar_t* > BSTR" kind are valid.

Consider the following example:

void func(BSTR a, BSTR b)

{

if (a > b)

{

....

}

}

This code is incorrect because comparison of the pointers 'a' and 'b' is a meaningless

operation.

More about BSTR on MSDN.

V776. Potentially infinite loop. The variable in the loop exit condition does not change its value between iterations.The analyzer detected a potentially infinite loop with its exit condition depending

on a variable whose value never changes between iterations.

Consider the following example:

int Do(int x);

int n = Foo();

Page 724: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

int x = 0;

while (x < n)

{

Do(x);

}

The loop's exit condition depends on variable 'x' whose value will always be zero,

so the 'x < 10' check will always evaluate to "true", causing an infinite loop. A

correct version of this code could look like this:

int Do(int x);

int n = Foo();

int x = 0;

while (x < n)

{

x = Do(x);

}

Here is another example where the loop exit condition depends on a variable whose

value, in its turn, changes depending on other variables that never change inside the

loop. Suppose we have the following method:

int Foo(int a)

{

int j = 0;

while (true)

{

if (a >= 32)

{

return j * a;

}

if (j == 10)

{

j = 0;

}

Page 725: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

j++;

}

}

The loop's exit condition depends on the 'a' parameter. If 'a' does not pass the 'a >=

32' check, the loop will become infinite, as the value of 'a' does not change between

iterations. This is one of the ways to fix this code:

int Foo(int a)

{

int j = 0;

while (true)

{

if (a >= 32)

{

return j * a;

}

if (j == 10)

{

j = 0;

a++; // <=

}

j++;

}

}

In the fixed version, the local variable 'j' controls how the 'a' parameter's value

changes.

V777. Dangerous widening type conversion from an array of derived-class objects to a base-class pointer.

Page 726: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer detected a possible error that has to do with accessing an array

consisting of objects of a derived class by using a pointer to the base class.

Attempting to access an element with a nonzero index through a pointer to the base

class will result in an error.

Consider the following example:

class Base

{

int buf[10];

public:

virtual void Foo() { ... }

virtual ~Base() { }

};

class Derived : public Base

{

char buf[10];

public:

virtual void Foo() override { ... }

virtual ~Derived() { }

};

....

size_t n = 5;

Base *ptr = new Derived[n]; // <=

....

for (size_t i = 0; i < n; ++i)

(ptr + i)->Foo();

....

This code uses a base class "Base" and a class derived from it, "Derived". Each

object of these classes occupies 48 and 64 bytes respectively (due to class alignment

on an 8-byte boundary; the compiler used is MSVC, 64-bit). When "i >= 1", the

pointer has to be offset by "i * 64" bytes each time when accessing an element with

Page 727: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

a nonzero index, but since the array is accessed through a pointer to the "Base" base

class, the offset will actually be "i * 48" bytes.

This is how the pointer's offset was meant to be computed:

This is how it is actually computed:

In fact, the program starts handling objects containing random data.

This is the fixed code:

....

size_t n = 5;

Derived *ptr = new Derived[n]; // <=

....

for (size_t i = 0; i < n; ++i)

(ptr + i)->Foo();

....

It is also a mistake to cast a pointer that refers to the pointer to the derived class to a

pointer that refers to the pointer to the base class:

Page 728: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

....

Derived arr[3];

Derived *pDerived = arr;

Class5 **ppDerived = &pDerived;

....

Base **ppBase = (Derived**)ppDerived; // <=

....

To ensure that an array of derived-class objects is properly stored in a polymorphic

way, the objects have to be arranged as shown below:

This is what the correct version of this code should look like:

....

size_t n = 5;

Base **ppBase = new Base*[n]; // <=

for (size_t i = 0; i < n; ++i)

ppBase[i] = new Derived();

....

If you want to emphasize that you are going to handle one object only, use the

following code:

....

Derived *derived = new Derived[n];

Base *base = &derived[i];

....

Page 729: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

This code is considered safe by the analyzer and does not trigger a warning.

It is also considered a valid practice to use such a pointer to access an array

consisting of a single object of the derived class.

....

Derived arr[1];

Derived *new_arr = new Derived[1];

Derived *malloc_arr = static_cast<Base*>(malloc(sizeof(Derived)));

....

Base *base = arr;

base = new_arr;

base = malloc_arr;

....

Note. If the base and derived classes are of the same size, it is valid to access an

array of derived-class objects though a pointer to the base class. However, this

practice is still not recommended for use.

V778. Two similar code fragments were found. Perhaps, this is a typo and 'X' variable should be used instead of 'Y'.The analyzer detected a possible typo in a code fragment that was very likely

written by using the Copy-Paste technique.

The V778 diagnostic looks for two adjacent code blocks with similar structure and

different variable names. It is designed to detect situations where a code block is

copied to make another block and the programmer forgets to change the names of

some of the variables in the resulting block.

Consider the following example:

void Example(int a, int b)

Page 730: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

{

....

if (a > 50)

doSomething(a);

else if (a > 40)

doSomething2(a);

else

doSomething3(a);

if (b > 50)

doSomething(b);

else if (a > 40) // <=

doSomething2(b);

else

doSomething3(b);

....

}

This code was written by using Copy-Paste. The programmer skipped one of the

instances of the 'a' variable that was to be replaced with 'b'. The fixed code should

look like this:

void Example(int a, int b)

{

....

if (a > 50)

doSomething(a);

else if (a > 40)

doSomething2(a);

else

doSomething3(a);

if (b > 50)

doSomething(b);

else if (b > 40)

doSomething2(b);

else

Page 731: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

doSomething3(b);

....

}

The following example is taken from a real project:

....

if(erendlinen>239) erendlinen=239;

if(srendlinen>erendlinen) srendlinen=erendlinen;

if(erendlinep>239) erendlinep=239;

if(srendlinep>erendlinen) srendlinep=erendlinep; // <=

....

Unlike the previous example, the problem in this one is not clearly visible. The

variables have similar names, which makes it much more difficult to diagnose the

error. In the second block, variable 'erendlinep' should be used instead of

'erendlinen'.

Obviously, 'erendlinen' and 'erendlinep' are poorly chosen variable names. An error

like that is almost impossible to catch during code review. Well, even with the

analyzer pointing at it directly, it is still not easy to notice. Therefore, take your time

and make sure to examine the code closely when getting a V778 warning.

V779. Unreachable code detected. It is possible that an error is present.The analyzer detected code that will never be executed. It may signal the presence

of a logic error.

This diagnostic is designed to find blocks of code that will never get control.

Consider the following example:

void Error()

{

Page 732: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

....

exit(1);

}

FILE* OpenFile(const char *filename)

{

FILE *f = fopen(filename, "w");

if (f == nullptr)

{

Error();

printf("No such file: %s", filename);

}

return f;

}

The 'printf(....)' function will never print the error message, as the 'Error()' function

does not return control. The exact way of fixing this error depends on the logic

intended by the programmer. The function could be meant to return control, or

maybe the expressions are executed in the wrong order and the code was actually

meant to look like this:

FILE* OpenFile(const char *filename)

{

FILE *f = fopen(filename, "w");

if (f == nullptr)

{

printf("No such file: %s", filename);

Error();

}

return f;

}

Here is another example:

void f(char *s, size_t n)

{

for (size_t i = 0; i < n; ++i)

Page 733: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

{

if (s[i] == '\0')

break;

else

return;

s[i] = toupper(s[i]);

}

}

The code after the 'if' statement will be skipped, since neither of the branches

returns control. A possible solution is to enclose the code in one of the branches or

delete the noreturn expression.

Here is an example of how the code above could be fixed:

void f(char *s, size_t n)

{

for (size_t i = 0; i < n; ++i)

{

if (s[i] == '\0')

break;

s[i] = toupper(s[i]);

}

}

When a function implementation is stored in another file, the analyzer needs a clue

to understand that the function always terminates the program. Otherwise, it could

miss the error. You can use annotations when declaring the function to give the

analyzer that clue:

[[noreturn]] void my_abort(); // C++11

__declspec(noreturn) void my_abort(); // MSVC

__attribute__((noreturn)) void my_abort(); // GCC

Page 734: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer does not output the warning in certain cases even though there is

formally an error. For example:

int test()

{

throw 0;

return 0;

}

The reason why it skips code like this is that programmers often use it to suppress

compiler warnings or messages from other analyzers.

V780. The object of non-passive (non-PDS) type cannot be used with the function.The analyzer detected a dangerous use of composite types. If an object is not a

Passive Data Structure (PDS), you cannot use low-level functions for memory

manipulation such as 'memset', 'memcpy', etc., as this may break the class' logic and

cause a memory leak, double release of the same resource, or undefined behavior.

Classes that cannot be handled that way include std::vector, std::string, and other

similar containers.

This diagnostic can sometimes help to detect typos. Consider the following

example:

struct Buffer {

std::vector<char>* m_data;

void load(char *buf, size_t len) {

m_data->resize(len);

memcpy(&m_data[0], buf, len);

}

};

Page 735: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The 'memcpy' function copies data to the object pointed to by 'm_data' instead of

the container. The code must be rewritten in the following way:

memcpy(&(*m_data)[0], buf, len);

An alternative version:

memcpy(m_data->data(), buf, len);

This error also appears when using memset/memcpy with structures whose fields

are non-PDS objects. Consider the following example:

struct Buffer {

std::vector<char> m_data;

....

};

void F() {

Buffer a;

memset(&a, 0, sizeof(Buffer));

....

}

We recommend using value initialization to avoid errors like that. This technique

works correctly both with POD-data and objects with a non-trivial constructor.

To copy the data, you can use the copy constructor generated by the compiler or

write one of your own.

The analyzer also looks for structures that can be dangerous when using

memset/memcpy with them because of their logic or the way they are represented in

memory. The first case deals with classes that include pointers, constructors, and

destructors at once. If a class performs non-trivial pointer handling (for example,

memory or resource management), you cannot use memcpy/memset with it. For

example:

struct Buffer {

char *buf;

Page 736: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Buffer() : buf(new char[16]) {}

~Buffer() { delete[] buf; }

};

Buffer buf1, buf2;

memcpy(&buf1, &buf2, sizeof(Buffer));

The second case deals with classes that are not standard-layout:

struct BufferImpl {

virtual bool read(char *, size_t) { return false; }

};

struct Buffer {

BufferImpl impl;

};

Buffer buf1, buf2;

memcpy(&buf1, &buf2, sizeof(Buffer));

V781. The value of the variable is checked after it was used. Perhaps there is a mistake in program logic. Check lines: N1, N2.The analyzer detected the following issue in the code. The value of a variable is first

used as the size or index of an array and only then is compared with 0 or the array

size. This issue may indicate the presence of a logic error or typo in one of the

comparisons.

Consider the following example:

int idx = GetPos(buf);

Page 737: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

buf[idx] = 42;

if (idx < 0) return -1;

If the value of 'idx' turns out to be less than zero, an attempt to evaluate the

'buf[idx]' expression will result in an error. The analyzer will output a warning for

this code pointing at two lines: the first line is where the variable is used and the

second is where its value is compared with another value.

This is what the fixed version of the code looks like:

int idx = GetPos(buf);

if (idx < 0) return -1;

buf[idx] = 42;

The analyzer also outputs the warning when the variable is compared with the array

size:

int buf[10];

buf[idx] = 42;

if (idx < countof(buf)) return -1;

Fixed code:

int buf[10];

if (idx < countof(buf)) return -1;

buf[idx] = 42;

Besides the indexes, the analyzer also takes into account how variables are used as

arguments to functions that work with non-negative values (memset, malloc, etc.).

Consider the following example:

bool Foo(char *A, int size_A, char *B, int size_B)

{

if (size_A <= 0)

return false;

memset(A, 0, size_A);

....

if (size_A <= 0) // Error

Page 738: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

return false;

memset(B, 0, size_B);

....

}

This code contains a typo that will be detected in an indirect way. There are actually

no problems with the 'A' array, but the programmer made a mistake checking the

size of the 'B' array, which causes 'size_A' to be checked only after the 'A' array has

been used.

Fixed code:

bool Foo(char *A, int size_A, char *B, int size_B)

{

if (size_A <= 0)

return false;

memset(A, 0, size_A);

....

if (size_B <= 0) // FIX

return false;

memset(B, 0, size_B);

....

}

V782. It is pointless to compute the distance between the elements of different arrays.The analyzer detected meaningless code computing the distance between the

elements of different arrays. Consider the following example:

ptrdiff_t offset()

{

char path[9] = "test.txt";

char resources[9] = "resr.txt";

Page 739: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

return path - resources;

}

Subtracting the addresses of the two arrays allocated on the stack is pointless and

very likely to be an error.

To discuss all the suspicious operations involving pointers to arrays, it is convenient

to divide pointers into two imaginary groups:

Group 'A' includes non-shifted pointers to stack-allocated arrays as well as arrays allocated using 'new' or 'malloc()'.

Group 'B' includes shifted pointers to arrays allocated using 'new' or 'malloc' or pointers that the analyzer has no information about.

Based on this division, we get a table of operations on pointers to arrays evaluation

of which makes no sense (Table 1).

Table 1 – Meaningless pointer operations.

V783. Dereferencing of invalid iterator 'X' might take place.The analyzer detected a code fragment that may result in using an invalid iterator.

Consider the following examples that trigger this diagnostic message:

if (iter != vec.end() || *iter == 42) { ... }

if (iter == vec.end() && *iter == 42) { ... }

There is a logic error in all the conditions above that leads to dereferencing an

invalid iterator. This error usually appears during code refactoring or because of a

typo.

The fixed versions:

Page 740: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

if (iter != vec.end() && *iter == 42) { ... }

if (iter == vec.end() || *iter == 42) { ... }

Of course, these are very simple cases. In practice, the check and the code using the

iterator are often found in different lines. If you got the V783 warning, check the

code above and try to find out why what made the analyzer treat the iterator as

invalid.

Here is an example where the iterator is checked and used in different lines:

if (iter == vec.end()) {

std::cout << "Error: " << *iter << std::endl;

throw std::runtime_error("foo");

}

The analyzer will warn you about the issue in the '*iter' expression. Either it is an

incorrect condition or some other variable should be used instead of 'iter'.

The analyzer can also detect cases when the iterator is used before being checked.

Consider the following example:

std::cout << "Element is " << *iter << std::endl;

if (iter == vec.end()) {

throw std::runtime_error("");

}

The check here is meaningless because the possibly invalid iterator has been already

dereferenced. There is a missing check:

if (iter != vec.end()) {

std::cout << "Element is " << *iter << std::endl;

}

if (iter == vec.end()) {

throw std::runtime_error("");

}

Page 741: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V784. The size of the bit mask is less than the size of the first operand. This will cause the loss of the higher bits.The analyzer detected a suspicious operation performed on a bit mask: the bit mask

is represented by a variable whose size is less than that of the other operand. This

guarantees the loss of the value of high-order bits.

Consider a few examples that trigger this warning:

unsigned long long x;

unsigned y;

....

x &= ~y;

Let’s see in detail what happens to the bits after each operation using the following

expression as an example:

x = 0xffff'ffff'ffff'ffff;

y = 0xff;

x &= ~y;

A result like that is usually different from what the programmer expected:

0xffff’ffff’ffff’ff00 – expected result

0x0000’0000’ffff’ff00 – actual result

The code can be fixed by explicitly casting the 'y' variable to the type of the 'x'

variable:

Page 742: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

x &= ~(unsigned long long)y;

In this case, the type conversion will be executed first, followed by the negation.

After that, all the most significant bits will be set to one. The following table shows

how the result of the code above will change with the new order of computations:

The analyzer also outputs the warning for code like this:

unsigned long long x;

unsigned y;

....

x &= y;

Even though no additional operations are performed here, this code still looks

suspicious. We recommend using explicit type conversion to make the code’s

behavior clearer to both the analyzer and your colleagues.

V785. Constant expression in switch statement.The analyzer detected a constant expression in a 'switch' statement. This usually

indicates the presence of a logic error in the code.

Consider the following synthetic example:

int i = 1;

switch (i)

{

....

}

The condition of the 'switch' statement is represented by a variable whose value can

be computed at compilation time. This situation could have resulted from code

Page 743: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

refactoring: the previous version of the code changed the variable’s value but then it

was modified and the variable turned out to be no longer assigned any value.

The analyzer does not issue the warning when the variable is constant or when the

condition employs macros. Such constructs are usually used deliberately to switch

on/off various features of the program at compilation time.

For example, they could perform different actions depending on what operating

system the code was compiled for:

switch (MY_PROJ_OS)

{

case MY_PROJ_WINDOWS:

....

case MY_PROJ_LINUX:

....

case MY_PROJ_MACOS:

....

}

V786. Assigning the value C to the X variable looks suspicious. The value range of the variable: [A, B].The analyzer detected that a variable is assigned a value that is beyond its value

range.

Consider a few examples that trigger this warning:

bool b;

....

b = 100;

Assigning the value 100 to a variable of type bool makes no sense. This may be a

typo, and some other variable was probably meant to be used instead of 'b'.

Page 744: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Another example:

struct S

{

int flag : 1;

}

....

S s;

s.flag = 1;

The 'flag' bit field can take values from the range [-1, 0], not [0, 1], as it might seem

at first. The reason is that this variable is signed. If you need a bit field with the

range [0, 1], make it unsigned:

struct S

{

unsigned flag : 1;

}

....

S s;

s.flag = 1;

V787. A wrong variable is probably used as an index in the for statement.The analyzer detected a loop counter used as an index in the loop termination

condition. Such code looks suspicious.

Consider the following example:

for (int i = 0; i < n; ++i)

for (int j = 0; j < arr[j]; ++j)

....

The programmer must have intended to use the variable 'i' instead of 'j':

Page 745: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

for (int i = 0; i < n; ++i)

for (int j = 0; j < arr[i]; ++j)

....

V801. Decreased performance. It is better to redefine the N function argument as a reference. Consider replacing 'const T' with 'const .. &T' / 'const .. *T'.The analyzer detected a construct that can be optimized. An object of type class or

structure is passed to a function. This object is passed by value but is not modified

because there is the key word const. Perhaps you should pass this object using a

constant reference in the C++ language or a pointer in the C language.

For example:

bool IsA(const std::string s)

{

return s == A;

}

When calling this function, the copy constructor will be called for the std::string

class. If objects are often copied this way, this may significantly reduce the

application's performance. You may easily optimize the code by adding the

reference:

bool IsA(const std::string &s)

{

return s == A;

}

Page 746: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer doesn't output the message if it is a plain old data (POD) structure

whose size is not larger than that of the size of pointer. Passing such a structure by

reference won't give any performance gain.

References:

1. Wikipedia. Reference (C++).

2. Bjarne Stroustrup. The C++ Programming Language (Third Edition and Special Edition). 11.6 - Large Objects.

V802. On 32-bit/64-bit platform, structure size can be reduced from N to K bytes by rearranging the fields according to their sizes in decreasing order.The analyzer detected a construct which can be optimized. There is a data structure

in program code that might cause inefficient use of memory.

Let's examine a sample of such a structure the analyzer considers inefficient:

struct LiseElement {

bool m_isActive;

char *m_pNext;

int m_value;

};

This structure occupies 24 bytes in 64-bit code because of data alignment. But if

you change the field sequence, its size will be only 16 bytes. This is the optimized

structure:

struct LiseElement {

char *m_pNext;

int m_value;

bool m_isActive;

Page 747: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

};

Of course, field rearrangement is not always possible or necessary. But if you use

millions of such structures, it is reasonable to optimize memory being consumed.

Additional reduction of structures' sizes may increase the application's performance

because fewer memory accesses will be needed at the same number of items.

Note that the structure described above always occupies 12 bytes in a 32-bit

program regardless of the field sequence. That is why the V802 message will not be

shown when checking the 32-bit configuration.

Surely there might be opposite cases when you can optimize a structure's size in the

32-bit configuration and cannot do that in the 64-bit configuration. Here is a sample

of such a structure:

struct T_2

{

int *m_p1;

__int64 m_x;

int *m_p2;

}

This structure occupies 24 bytes in the 32-bit program because of the alignment. If

we rearrange the fields as shown below, its size will be only 16 bytes.

struct T_2

{

__int64 m_x;

int *m_p1;

int *m_p2;

}

It does not matter how fields are arranged in the 'T_2' structure in the 64-bit

configuration: it will occupy 24 bytes anyway.

Page 748: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The method of reducing structures' sizes is rather simple. You just need to arrange

fields in descending order of their sizes. In this case, fields will be arranged without

unnecessary gaps. For instance, take this structure of 40 bytes in a 64-bit program:

struct MyStruct

{

int m_int;

size_t m_size_t;

short m_short;

void *m_ptr;

char m_char;

};

By simply sorting the sequence of fields in descending order of their sizes:

struct MyStructOpt

{

void *m_ptr;

size_t m_size_t;

int m_int;

short m_short;

char m_char;

};

we get a structure with the size of 24 bytes.

The analyzer does not always generate messages about inefficient structures

because it tries to make unnecessary warnings fewer. For instance, the analyzer

does not generate this warning for complex descendant classes since there are

usually rather few of such objects. For example:

class MyWindow : public CWnd {

bool m_isActive;

size_t m_sizeX, m_sizeY;

char m_color[3];

...

};

Page 749: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

This structure's size may be reduced but it does not give your practical benefit.

V803. Decreased performance. It is more effective to use the prefix form of ++it. Replace iterator++ with ++iterator.The analyzer detected a construct which may be optimized. An iterator is changed

in the program code by the increment/decrement postfix operator. Since the

previous iterator's value is not used, you may replace the postfix operator with the

prefix one. In some cases, the prefix operator will work faster than the postfix one,

especially in Debug-versions.

Example:

std::vector<size_t>::const_iterator it;

for (it = a.begin(); it != a.end(); it++)

{ ... }

This code is faster:

std::vector<size_t>::const_iterator it;

for (it = a.begin(); it != a.end(); ++it)

{ ... }

The prefix increment operator changes the object's state and returns itself already

changed. The prefix operator in the iterator's class to handle std::vector might look

as follows:

_Myt& operator++()

{ // preincrement

++_Myptr;

return (*this);

}

Page 750: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The situation with the postfix increment operator is more complicated. The object's

state must change but it is the previous state which is returned. So an additional

temporary object is created:

_Myt operator++(int)

{ // postincrement

_Myt _Tmp = *this;

++*this;

return (_Tmp);

}

If we want only to increment the iterator's value, it appears that the prefix version is

preferable. So here you are one of the tips on micro-optimization of software: write

"for (it = a.begin(); it != a.end(); ++it)" instead of "for (it = a.begin(); it != a.end();

it++)". In the latter case, an unnecessary temporary object is created, which reduces

performance.

To study all these questions in detail, refer to the book by Scott Meyers "Efficient

use of C++. 35 new recommendations on improving your programs and projects"

(Rule 6. Distinguish between prefix increment and decrement operators) [1].

You may also study the results of speed measurements in the post "Is it reasonable

to use the prefix increment operator ++it instead of postfix operator it++ for

iterators?" [2].

References1. Meyers, Scott. More Effective C++: 35 New Ways to Improve Your

Programs and Designs. Addison-Wesley, Reading, Mass., 1996. ISBN-10: 020163371X. ISBN-13: 9780201633719.

2. Andrey Karpov. Is it reasonable to use the prefix increment operator ++it instead of postfix operator it++ for iterators? http://www.viva64.com/en/b/0093/

V804. Decreased performance. The 'Foo' function is called twice in the

Page 751: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

specified expression to calculate length of the same string.The analyzer detected a construct which can be potentially optimized. Length of one

and the same string is calculated twice in one expression. For length calculation

such functions as strlen, lstrlen, _mbslen, etc. are used. If this expression is

calculated many times or strings have large lengths, this code fragment should be

optimized.

For optimization purposes, you may preliminary calculate the string length and

place it into a temporary variable.

For example:

if ((strlen(directory) > 0) &&

(directory[strlen(directory)-1] != '\\'))

Most likely, this code processes only one string and it does not need optimization.

But if the code is called very often, we should rewrite it. This is a better version of

the code:

size_t directoryLen = strlen(directory);

if ((directoryLen > 0) && (directory[directoryLen-1] != '\\'))

Sometimes the V804 warning helps to detect much more crucial errors. Consider

this sample:

if (strlen(str_1) > 4 && strlen(str_1) > 8)

An incorrect variable name is used here. This is the correct code:

if (strlen(str_1) > 4 && strlen(str_2) > 8)

V805. Decreased performance. It is inefficient to identify an empty

Page 752: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

string by using 'strlen(str) > 0' construct. A more efficient way is to check: str[0] != '\0'The analyzer detected a construct that can be optimized. To determine whether a

code string is empty or not, the strlen function or some other identical function is

used. For example:

if (strlen(strUrl) > 0)

This code is correct, but if it is used inside a long loop or if we handle long strings,

such a check might be inefficient. To check if a string is empty or not, we just have

to compare the first character of the string with 0. This is an optimized code:

if (strUrl[0] != '\0')

Sometimes the V805 warning helps to detect excessive code. In one application we

have found a code fragment like the following one:

string path;

...

if (strlen(path.c_str()) != 0)

Most likely, this code appeared during careless refactoring when the type of the

path variable had been changed from a simple pointer to std::string. This is a shorter

and faster code:

if (!path.empty())

V806. Decreased performance. The expression of strlen(MyStr.c_str()) kind can be rewritten as MyStr.length().

Page 753: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Analyzer found a construct which potentially can be optimized. The length of a

string located in the container is calculated by using the strlen() function or by

function similar to it. This operation is excessive, as the container possesses a

special function for string length calculation.

Let's review this example:

static UINT GetSize(const std::string& rStr)

{

return (strlen(rStr.c_str()) + 1 );

}

This code belongs to a real-life application. Usually such funny code fragments are

created during careless refactoring. This code is slow and, even more, quite possibly

even unnecessary. When it is required you can just write "string::length() + 1".

Nevertheless, if you are willing to create a special function for calculating the size

of a null-terminated string, it should appear as follows:

inline size_t GetSize(const std::string& rStr)

{

return rStr.length() + 1;

}

RemarkOne should remember that "strlen(MyString.c_str())" and "MyString.length()"

operations will not always generate the same result. The differences will appear in

case a string contains null characters besides the terminal one. However such

situations can be viewed as a very bad design practice, so the V806 warning

message is a great reason to consider the possibility of refactoring. Even if the

developer who created this code understands its' operational principles quite well,

nevertheless it will be hard to understand this code for his colleagues. They will

wonder about the purpose of such a style and could potentially replace the call to

"strlen()" function with "length()", thus creating a bug in the program. So one

should not be lazy and should replace it with such a code in which operational

Page 754: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

principles are clear and intelligible to even an outsider developer. For instance, if

the string contains null characters, than there is a high probability that it is not a

string at all but an array of bytes. An in such a case the std::vector or your own

custom classes should be used instead.

V807. Decreased performance. Consider creating a pointer/reference to avoid using the same expression repeatedly.The analyzer has detected code which can be optimized. The code contains

homogeneous message chains intended to get access to some object. The following

constructs are understood by a message chain:

Get(1)->m_point.x

X.Foo().y

next->next->Foo()->Z

If a message chain is repeated more than twice, perhaps you should consider code

refactoring.

Look at this example:

Some->getFoo()->doIt1();

Some->getFoo()->doIt2();

Some->getFoo()->doIt3();

If the 'getFoo()' function works slowly or if this code is placed inside a loop, you

should rewrite this code. For example, you may create a temporary pointer:

Foo* a = Some->getFoo();

a->doIt1();

a->doIt2();

a->doIt3();

Page 755: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Of course, it is not always possible to write it in this way. And moreover, such

refactoring does not always give you a performance gain. There exist too many

various alternatives, so we cannot give you any general recommendations.

But presence of message chains usually indicates careless code. To improve such

code you can use several methods of refactoring:

1. Hide Delegate

2. Extract Method

3. Move Method

V808. An array/object was declared but was not utilized.The analyzer has detected a code that can be simplified. A function code contains

local variables which are not used anywhere. The analyzer generates this warning in

the following cases:

1. An object array is created but not used. It means that the function uses more stack memory than necessary. First, it may lead to a stack overflow. Second, it may reduce the efficiency of the microprocessor cache.

2. Class objects are created but not used. The analyzer doesn't warn about all such objects, but only about those which certainly don't need to be created without using them. For instance, these are std::string or CString. Creation and destruction of such objects is just a waste of processor time and stack memory.

The analyzer doesn't generate the warning if variables of built-in types are created:

the compiler handles this very well. It also helps to avoid a lot of false positives.

Consider this sample:

void Foo()

{

int A[100];

string B[100];

DoSomething(A);

}

Page 756: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The array of items of the 'string' type is declared but not used, while it still requires

memory to be allocated for it and calling constructors and destructors. To optimize

this code, we just need to delete the declaration of the unused local variable or

array. This is the fixed code:

void Foo()

{

int A[100];

DoSomething(A);

}

V809. Verifying that a pointer value is not NULL is not required. The 'if (ptr != NULL)' check can be removed.The analyzer has detected a code fragment that can be simplified. The 'free()'

function and 'delete' operator handle the null pointer correctly. So we can remove

the pointer check.

Here's an example:

if (pointer != 0)

delete pointer;

The check is excess in this case, as the 'delete' operator processes the null pointer

correctly. This is how to fix the code:

delete pointer;

We cannot call this fix a true optimization, of course. But it allows us to delete an

unnecessary string to make the code shorter and clearer.

There's only one case when the pointer check does have sense: when the 'free()'

function or 'delete' operator are called VERY many times, and the pointer, at the

Page 757: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

same time, ALMOST ALWAYS equals zero. If user code contains the check,

system functions won't be called. It will even reduce the run time a bit.

But in practice, a null pointer almost always indicates some error. If the program

works normally, pointers won't equal zero in 99.99% of cases. That's why the check

can be removed.

V810. Decreased performance. The 'A' function was called several times with identical arguments. The result should possibly be saved to a temporary variable, which then could be used while calling the 'B' function.The analyzer has found some code that can be optimized. The code contains a call

of a function which accepts as its arguments several calls of one and the same

function with identical arguments.

Consider the following sample:

....

init(cos(-roty), sin(-roty),

-sin(-roty), cos(-roty));

....

The call of such a function works slowly, while this effect will be intensified if this

code fragment is placed inside a loop. You'd better rewrite this code. For instance,

you may create a temporary variable:

....

double cos_r = cos(-roty);

Page 758: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

double sin_r = sin(-roty);

init(cos_r, sin_r, -sin_r, cos_r);

....

You cannot always change the code that way, of course. Moreover, this refactoring

doesn't always guarantee that you get a performance gain. But such optimizations

may be very helpful sometimes.

V811. Decreased performance. Excessive type casting: string -> char * -> string.The analyzer has detected a code that can be optimized: the code contains an

excessive operation when a 'std::string' object is created, and we can eliminate this.

We use the 'c_str()' function to take a pointer to a character array from the

'std::string' object. Then we construct a new object of the 'std::string' type from

these characters. For instance, it can happen if the non-optimal expression is:

a function call argument;

an assignment operation operand;

a 'return' operation operand.

Here is a sample for the case with a function call:

void foo(const std::string &s)

{

....

}

....

void bar()

{

std::string str;

....

foo(str.c_str());

Page 759: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

}

The code is very easy to improve: you just need to remove the call of the 'c_str()'

method:

....

void bar()

{

std::string str;

....

foo(str);

}

This is a sample of incorrect code for the case with an assignment operator:

std::string str;

....

std::string s = str.c_str();

And this is an incorrect code for the 'return' operator:

std::string foo(const std::string &str)

{

....

return str.c_str();

}

The errors in the last two cases are fixed in the same way as with the function call.

V812. Decreased performance. Ineffective use of the 'count' function. It can possibly be replaced by the call to the 'find' function.The analyzer has detected a construct that can be optimized: a call of the 'count' or

'count_if' function from the standard library is compared to zero. A slowdown may

Page 760: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

occur here, as these functions need to process the whole container to count the

number of the necessary items. If the value returned by the function is compared to

zero, we are interested to know if there is at least 1 item we look for or if there are

no such items at all. This operation may be done in a more efficient way by using

calls of the 'find' or 'find_if' functions.

Here's an example of non-optimal code:

void foo(const std::multiset<int> &ms)

{

if (ms.count(10) != 0)

{

....

}

}

To make it faster we need to replace the non-optimal expression with a similar one

using a more appropriate function - 'find' in this case. This is the optimized code:

void foo(const std::multiset<int> &ms)

{

if (ms.find(10) != ms.end())

{

....

}

}

The following code sample is also non-optimal:

void foo(const std::vector<int> &v)

{

if (count(v.begin(), v.end(), 10) != 0)

{

....

}

}

Page 761: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Optimization can be done in the same way as in the previous example. This is what

the optimized code will look like:

void foo(const std::vector<int> &v)

{

if (find(v.begin(), v.end(), 10) != v.end())

{

....

}

}

V813. Decreased performance. The argument should probably be rendered as a constant pointer/reference.The analyzer has detected a construct that can be optimized: an argument, which is

a structure or a class, is passed into a function by value. The analyzer checks the

body function and finds out that the argument is not modified. For the purpose of

optimization, it can be passed as a constant reference. It may enhance the program's

performance, as it is only the address that will be copied instead of the whole class

object when calling the function. This optimization is especially noticeable when

the class contains a large amount of data.

For example:

void foo(Point p)

{

float x = p.x;

float y = p.y;

float z = p.z;

.... 'p' argument is not used further in any way....

}

Page 762: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

This code is very easy to fix - you just need to change the function declaration:

void foo(const Point &p)

{

float x = p.x;

float y = p.y;

float z = p.z;

.... 'p' argument is not used further in any way....

}

The analyzer doesn't generate the warning if structures are very small.

Note N1. The user can specify the minimal structure size starting with which the

analyzer should generate its warnings.

For example, to prevent it from generating messages for structures whose size is

equal to or less than 32 bytes, you can add the following comment into the code:

//-V813_MINSIZE=33

The number 33 determines the structure size starting with which the analyzer will

generate messages.

You can also write this comment in one of the global files (for example

in StdAfx.h) so that it affects the whole project.

Default value: 9.

Note N2. The analyzer may make mistakes when trying to figure out whether or not

a variable is being modified inside the function body. If you have noticed an

obvious false positive, please send us the corresponding code sample for us to study

it.

If the code is correct, you can turn off the false warning by adding the comment "//-

V813".

Page 763: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V814. Decreased performance. The 'strlen' function was called multiple times inside the body of a loop.The analyzer has detected a construct which can be optimized. Each loop's iteration

calls the function strlen(S) or other similar function. The string 'S' is not changed;

therefore, its length can be calculated beforehand. Sometimes you may get a

significant performance boost due to this optimization.

Example 1.

for (;;) {

{

....

segment = next_segment + strlen("]]>");

....

}

The length of the "]]>" string is being calculated multiple times in the loop. Though

the string is short and the function strlen() works fast, you risk getting a slow-down

for no obvious reason if the loop iterates millions of times. You can fix the defect in

the following way:

const size_t suffixLen = strlen("]]>");

for (;;) {

{

....

segment = next_segment + suffixLen;

....

}

Or rather use a macro like this:

#define LiteralStrLen(S) (sizeof(S) / sizeof(S[0]) - 1)

....

Page 764: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

segment = next_segment + LiteralStrLen("]]>");

If you work with C++, create a templated function:

template <typename T, size_t N>

char (&ArraySizeHelper(T (&array)[N]))[N];

template <typename T, size_t N>

size_t LiteralStrLen(T (&array)[N]) {

return sizeof(ArraySizeHelper(array)) - 1;

}

....

segment = next_segment + LiteralStrLen("]]>");

Example 2.

for(j=0; j<(int)lstrlen(text); j++)

{

if(text[j]=='\n')

{

lines++;

}

}

This code fragment counts the number of lines in a text and is taken from one real

application.

If the text is large enough, the algorithm becomes quite inefficient. With each loop

iteration, the program calculates the text length to compare it to the variable 'j'.

This is the optimized code:

const int textLen = lstrlen(text);

for(j=0; j<textLen; j++)

{

if(text[j]=='\n')

{

lines++;

Page 765: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

}

}

V815. Decreased performance. Consider replacing the expression 'AA' with 'BB'.The analyzer has detected a construct that can be optimized: in string classes,

operators are implemented that allow more efficient string clearing or checking a

string for being empty.

For example:

bool f(const std::string &s)

{

if (s == "")

return false;

....

}

This code can be improved a bit. The object of the 'std::string' class knows the

length of the string it is storing, but it is unknown which string it is intended to be

compared to. That's why a loop is called for string comparing. A much easier and

better way is to simply check that the string length is 0 - it can be done with the help

of the 'empty()' function:

if (s.empty())

return false;

A similar situation: we need to clear a string in the code fragment below, and it can

be improved:

wstring str;

...

str = L"";

Page 766: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The better version:

wstring str;

...

str.clear();

Note. The recommendations given are arguable. Such optimizations give little

benefit, while the risk is increasing of making a typo and using a wrong function.

The reason for that is poor function naming. For example, the 'empty()' function in

the 'std::string' class checks the string for being empty. In the class 'CString', the

'Empty()' function clears the string. The same name for both but these functions do

different things. That's why you may use the constructs = "", == "", != "" to make

the code more comprehensible.

The choice is up to you. If you don't like the V815 diagnostic rule, you can turn it

off in the settings.

V816. It is more efficient to catch exception by reference rather than by value.The analyzer detected a construct that can be optimized. It is more efficient to catch

exceptions by reference rather than by value: it will help avoid copying objects.

Consider the following example:

catch (MyException x)

{

Dump(x);

}

This code can be improved a bit. In its original form, a new object of type

MyException is created when catching the exception. It can be avoided by catching

the exception by reference. It makes even more sense when the object is "heavy".

The fixed version of the code:

Page 767: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

catch (MyException &x)

{

Dump(x);

}

Catching exceptions by reference is good not only from the optimization's

viewpoint; it helps avoid some other issues as well – for example slicing. However,

discussion of these issues is beyond the scope of this diagnostic's description. Errors

related to slicing are detected by diagnostic V746.

The pros of catching exceptions by reference are discussed in the following sources:

StackOverflow. C++ catch blocks - catch exception by value or reference?

StackOverflow. Catch exception by pointer in C++.

V817. It is more efficient to search for 'X' character rather than a string.The analyzer detected a function that looks for a character in a string and can be

optimized. Consider the following example of inefficient code:

bool isSharpPresent(const std::string& str)

{

return str.find("#") != std::string::npos;

}

In this code, it is better to use an overridden version of the 'find()' function that

receives a character instead of a string.

Optimized code:

bool isSharpPresent(const std::string& str)

{

return str.find('#') != std::string::npos;

}

Page 768: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The following example also uses inefficient code that can be optimized:

const char* GetSharpSubStr(const char* str)

{

return strstr(str, "#");

}

In this code, it is better to use the function 'strchr()' to search for a character instead

of a string:

const char* GetSharpSubStr(const char* str)

{

return strchr(str, '#');

}

V2001. Consider using the extended version of the 'foo'function here.This diagnostic warning was added on users' request.

The analyzer allows you to detect calls of functions that have "extended" analogues.

By the term "extended functions" we understand functions that have the Ex suffix.

Here are some examples of extended functions: VirtualAllocEx, SleepEx,

GetDCEx, LoadLibraryEx, FindResourceEx.

Consider the following source code:

void foo();

void fooEx(float x);

void foo2();

...

void test()

{

foo(); // V2001

foo2(); // OK

}

Page 769: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

In the fragment where the "foo" function is called, the V2001 diagnostic message

will be produced since there is another function with the same name but ending with

"Ex". The "foo2" function does not have an alternative version and therefore no

diagnostic message will be generated concerning it.

The V2001 message will be also generated in the following case:

void fooA(char *p);

void fooExA(char *p, int x);

...

void test()

{

fooA(str); // V2001

}

V2002 is a related diagnostic message.

V2002. Consider using the 'Ptr' version of the 'foo' function here.This diagnostic message was added on users' request.

The analyzer allows you to detect calls of functions that have 'Ptr' analogues. By

this term we mean functions whose name has the Ptr suffix. Here are some

examples of extended functions: SetClassLongPtr, DSA_GetItemPtr.

Consider the following source code:

void foo(int a);

void fooPtr(int a, bool b);

void foo2();

...

void test()

{

foo(1); // V2002

foo2(); // OK

Page 770: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

}

In the fragment where the "foo" function is called, the V2002 diagnostic message

will be produced since there is another function with the same name but ending with

"Ptr". The "foo2" function does not have an alternative version and therefore no

diagnostic message will be generated concerning it.

The V2002 message will be also generated in the following case:

void fooA(char *p);

void fooPtrA(char *p, int x);

...

void test()

{

fooA(str); // V2002

}

V2001 is a related diagnostic message.

V2003. Explicit conversion from 'float/double' type to signed integer type.This diagnostic warning was added at the request of users.

The analyzer allows you to detect all the explicit floating-point type conversions to

integer signed types.

Consider some examples of constructs the analyzer will generate this diagnostic

message on:

float f;

double d;

long double ld;

int i;

short s;

Page 771: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

...

i = int(f); // V2003

s = static_cast<short>(d); // V2003

i = (int)ld; // V2003

V2004 is a related diagnostic message.

V2004. Explicit conversion from 'float/double' type to unsigned integer type.This diagnostic warning was added at the request of users.

The analyzer allows you to detect all the explicit floating-point type conversions to

integer unsigned types.

Consider some examples of constructs the analyzer will generate this diagnostic

message on:

float f;

double d;

long double ld;

unsigned u;

size_t s;

...

u = unsigned(f); // V2004

s = static_cast<size_t>(d); // V2004

u = (unsigned)ld; // V2004

V2003 is a related diagnostic message.

V2005. C-style explicit type casting is utilized. Consider using:

Page 772: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

static_cast/const_cast/reinterpret_cast.This diagnostic warning has been added at the request of users.

The analyzer allows you to detect explicit type conversions written in the old C

language style in a C++ program. It is safer in the C++ language to convert types

using operators static_cast, const_cast and reinterpret_cast.

The V2005 diagnostic rule helps to perform code refactoring and replace the old

type conversion style with a new one. Sometimes it helps to detect errors.

Here are examples of constructs that will trigger this diagnostic message:

int i;

double d;

size_t s;

void *p;

...

i = int(p); //V2005

d = (double)d; //V2005

s = (size_t)(i); //V2005

The V2005 diagnostic message is not generated in three cases.

1. This is a C program.

2. The conversion target type is void. This type conversion is safe and is used to

emphasize that there is a result which is not used anyhow. For example:

(void)fclose(f);

3. The type conversion is located inside a macro. If the analyzer generated the

warning for macros, there would be a lot of reports when different system constants

and macros are used. And you cannot fix them anyway. Here you are some

examples:

Page 773: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

#define FAILED(hr) ((HRESULT)(hr) < 0)

#define SRCCOPY (DWORD)0x00CC0020

#define RGB(r,g,b)\

((COLORREF)(((BYTE)(r)|((WORD)((BYTE)(g))<<8))\

|(((DWORD)(BYTE)(b))<<16)))

Special settings of the V2005 diagnostic

On an additional request of our users, we have implemented the feature allowing

you to manage the V2005 diagnostic's behavior. In the general header file or

pvsconfig-file you should write a special comment. Here is an example of usage:

//+V2005 ALL

Three modes are available:

a) Default mode: each type conversion in the C style triggers a warning prompting

you to use such constructs as static_cast, const_cast and reinterpret_cast instead of a

type conversion.

b) ALL: each type conversion in the C style causes the analyzer to display a

recommendation about what keyword(s) should be used instead. In rare cases,

generating single wrong recommendations is possible due to conversion of complex

template types. Another rare situation is also possible that the analyzer fails to

detect the conversion type and displays an ordinary warning without specifying the

exact conversion type.

//+V2005 ALL

c) NO_SIMPLE_CAST: this mode is similar to the previous one, but in this case

warning is generated only when at least one type in conversion is pointer or when

conversion type predicted is more complex than static_cast.

//+V2005 NO_SIMPLE_CAST

References:

1. Terminology. Explicit type conversion.

Page 774: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

2. Wikipedia. Type conversion.

V2006. Implicit type conversion from enum type to integer type.This diagnostic warning was added on users' request.

The analyzer allows you to detect all the implicit conversions of enum-types to

integer types.

The V2006 diagnostic rule helps perform code refactoring and sometimes find

errors.

Here are examples of constructs the analyzer generates this diagnostic message for:

enum Orientation {

Horizontal = 0x1,

Vertical = 0x2

};

...

Orientation orientation = Horizontal;

int pos = orientation; // V2006

if (pos == Vertical) // Ok

{

...

}

The V2006 diagnostic message is not generated if two values of the enum type are

compared or if bitwise operations are executed on them.

V2007. This expression can be simplified. One of the operands in the operation equals NN. Probably it is a mistake.

Page 775: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

This diagnostic message was added on users' request.

The analyzer allows you to detect some strange binary operations:

operations '^', '+', '-', '<<', '>>' where one of the operands equals 0;

the '&' operation where one of the operands equals -1;

operations '*', '/', '%' where one of the operands equals 1;

The V2007 diagnostic rule helps to perform code refactoring and sometimes detect

errors.

These are examples of constructs that cause this diagnostic message to appear:

int X = 1 ^ 0;

int Y = 2 / X;

This code can be simplified. For example:

int X = 1;

int Y = 2;

To reduce the number of false positives, we have added several exceptions. For

example, the V2007 diagnostic message is not generated when the strange

expression is located inside a macro or is an array index.

V2008. Cyclomatic complexity: NN. Consider refactoring the 'Foo' function.This diagnostic message was added on users' request.

The analyzer calculates and displays the "Cyclomatic complexity" values for

functions. The cyclomatic complexity is one of the metrics for source code used to

estimate the complexity of a program.

Page 776: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The extremely high significance of the cyclomatic complexity parameter indicates

that you should pay special attention to the function that has caused the diagnostic

message to be shown. It's highly probable that these functions need refactoring.

Messages are generated only for those functions whose cyclomatic complexity

exceeds the threshold value. It is set at 50 by default.

You can change the threshold value through adding the comment

//-V2008_CYCLOMATIC_COMPLEXITY=N

into your code, where N is the new threshold value of the cyclomatic complexity.

The value must be higher than 1. The comment affects the code within the bounds

of the compilation unit. That's why if you want to specify the threshold value for the

whole project, write this comment in one of the base header files, for example,

stdafx.h.

There is one more additional option enabling the modified method of cyclomatic

complexity calculation:

//-V2008_MODIFIED_CYCLOMATIC_COMPLEXITY

This comment sets the analyzer to take the cyclomatic complexity of the switch()

operator as one. The number of "case x:" does not matter.

V2009. Consider passing the 'Foo' argument as a constant pointer/reference.This diagnostic message was added on users' request. The analyzer suggests that a

function argument should be made a constant one. This warning is generated in the

following cases:

The argument is an instance of a structure or a class which is passed into the function by reference but not modified inside the function body;

The argument is a non-constant pointer, but it is used only for data reading.

Page 777: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

This diagnostic may help you in code refactoring or preventing software errors in

the future.

Consider the following sample:

void foo(int *a)

{

int b = a[0] + a[1] + a[2];

.... 'a' variable is not used anymore

}

It is better to make the 'a' pointer a constant one. First, it makes it clear that the

argument is used for data reading only. Second, making the 'foo()' function constant

enables us to make other variables and functions constant too.

This is the fixed code:

void foo(const int *a)

{

int b = a[0] + a[1] + a[2];

.... 'a' variable is not used anymore

}

Note. The analyzer may make mistakes when trying to figure out whether or not a

variable is being modified inside the function body. If you have noticed an obvious

false positive, please send us the corresponding code sample for us to study it.

Messages generated by the analyzer may sometimes seem pretty strange. Let's

discuss one of these cases in detail:

typedef struct tagPOINT {

int x, y;

} POINT, *PPOINT;

void foo(const PPOINT a, const PPOINT b) {

a->x = 1; // Data can be changed

a = b; // Compilation error

}

Page 778: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer suggests that the pointer should be made constant. It seems strange,

since there is the keyword 'const' in the code. But 'const' actually indicates that the

argument is constant, while the memory addresses the pointers refer to are available

for modification.

To make the data themselves constant, we should do the following thing:

....

typedef const POINT *CPPOINT;

void foo(const CPPOINT a, const CPPOINT b) {

a->x = 1; // Compilation error

a = b; // Compilation error

}

V2010. Handling of two different exception types is identical.This diagnostic has been implemented on users' request. It detects the issue when

handlers for different exception types do the same job. It may be an error or it may

signal that the code can be reduced.

For example:

try

{

....

}

catch (AllocationError &e)

{

WriteLog("Memory Allocation Error");

return false;

}

catch (IOError &e)

{

WriteLog("Memory Allocation Error");

Page 779: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

return false;

}

This code fragment was written using the Copy-Paste method, which leads to

writing an incorrect error message into the log in case of a reading from file error.

The code should actually look something like this:

try

{

....

}

catch (AllocationError &e)

{

WriteLog("Memory Allocation Error");

return false;

}

catch (IOError &e)

{

WriteLog("IO Error: %u", e.ErrorCode());

return false;

}

Here is another example. The code below is correct but can be reduced:

try

{

....

}

catch (std::exception &)

{

Disconnect();

}

catch (CException &)

{

Disconnect();

}

catch (...)

Page 780: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

{

Disconnect();

}

Since all the handlers are identical and catch exceptions of all types, the code can be

shortened:

try

{

....

}

catch (...)

{

Disconnect();

}

Another example.

class DBException : public std::exception { ... };

class SocketException : public DBException { ... };

class AssertionException : public DBException { ... };

....

try

{

....

}

catch (SocketException& e){

errorLog.push_back(e.what());

continue;

}

catch (AssertionException& e) {

errorLog.push_back(e.what());

continue;

}

catch(std::exception& e){

errorLog.push_back(e.what());

continue;

Page 781: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

}

There are a few classes inherited from the 'std::exception' class. All the exception

handlers are identical. Notice that they also catch exceptions of the 'std::exception'

type among others. This code is redundant. We may leave only one handler for

'std::exception', and it will catch and handle all the rest exceptions alike as they are

inherited from 'std::exception'. The 'what()' method is virtual, so a correct error

message will be saved into 'errorLog'.

The simplified code:

try

{

....

}

catch(std::exception& e){

errorLog.push_back(e.what());

continue;

}

V2011. Consider inspecting signed and unsigned function arguments. See NN argument of function 'Foo' in derived class and base class.This diagnostic rule was added on our users' request. It is used to detect the

following issue: the base class has a virtual function with one of the arguments of

the signed type. The derived class contains the same function but with an unsigned

argument. Or you may get a reverse situation: the base class contains an unsigned

argument while the derived contains a signed one.

This diagnostic is used to detect errors when – during a large refactoring – the

programmer changes the function type in one of the classes but forgets to change it

in the other class.

Page 782: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

For example:

struct Q { virtual int x(unsigned) { return 1; } };

struct W : public Q { int x(int) { return 2; } };

The code should actually look like this:

struct Q { virtual int x(unsigned) { return 1; } };

struct W : public Q { int x(unsigned) { return 2; } };

If your base class has two 'x' functions with the arguments of the 'int' and "unsigned'

types, the analyzer won't generate the V2011 warning.

V2012. Possibility of decreased performance. It is advised to pass arguments to std::unary_function/std::binary_function template as references.This diagnostic rule was added at our users' request.

The analyzer has detected a class inherited from std::unary_function or

std::binary_function, its template's parameters containing classes passed by value. It

is obvious that passing a class object by value (especially a "heavy" object, with

many fields or complex constructor) may cause additional time and memory

expenses. Of course, passing an object by value is not always bad. It may make

sense when you need to save an original object and work with an altered copy. But

sometimes the code where an object is passed by value appears through a mistake

and therefore is a bad solution.

Let's check an example. The functor we have in it will copy two objects of the

std::string type each time it is called instead of passing them by value:

class example : public std::binary_function

Page 783: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

<std::string, std::string, bool>

{

public:

result_type operator()(

first_argument_type first,

second_argument_type second)

{

return first == second;

};

};

The simplest solution in this case is of course to pass the template parameters by

reference instead of value:

class example : public std::binary_function

<const std::string &, const std::string &, bool> ....

Another case when the analyzer won't generate the warning is when all the

arguments not passed by reference are changed in the function body:

class example : public std::binary_function

<std::string, std::string, bool>

{

public:

result_type operator()(

first_argument_type first,

second_argument_type second)

{

std::replace(first.begin(), first.end(), 'u', 'v');

std::replace(second.begin(), second.end(), 'a', 'b');

return first == second;

};

};

Page 784: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V2013. Consider inspecting the correctness of handling the N argument in the 'Foo' function.This diagnostic message was added on users' request. It is quite specific and was

implemented to solve one particular task that is hardly of interest to a wide

audience.

It can be sometimes useful to find all the calls of COM-interfaces where a pointer to

a certain class is explicitly cast to an integer pointer or just an integer type. Some of

our users wish to have a means to check if passed data are processed correctly on

the COM-server's part.

Assume we have a container containing an array of items of the unsigned type. It is

passed into a function that interprets it as an array of size_t items. The data in such

code will be interpreted correctly in the 32-bit system and incorrectly in the 64-bit

one. For example:

MyVector<unsigned> V;

pInterface->Foo((unsigned char *)(&V));

....

void IMyClass::Foo(unsigned char *p)

{

MyVector<size_t> *V = (V *)(p);

....

}

This is in fact a 64-bit error. We decided not to include it into the set of 64-bit

diagnostic rules as it is just too specific. This diagnostic allows you to find

potentially dangerous calls and it is then up to you to manually review all the

methods accepting the data and figure out if there is an error in your code or not.

Page 785: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V3001. There are identical sub-expressions to the left and to the right of the 'foo' operator.The analyzer has detected a code fragment that is very likely to have a logical error

in it. The program text contains an operator (<, >, <=, >=, ==, !=, &&, ||, -, /, &, |, ^)

whose both operands are identical subexpressions.

Consider this example:

if (a.x != 0 && a.x != 0)

In this case, the '&&' operator is surrounded by identical subexpressions "a.x != 0",

which enables the analyzer to detect a mistake made through carelessness. A correct

version of this code, which won't trigger the diagnostic, should look as follows:

if (a.x != 0 && a.y != 0)

Here's another example of a mistake detected by the analyzer in an application's

code:

class Foo {

List<int> Childs { get; set; }

...

public bool hasChilds() { return(Childs[0] > 0 || Childs[0] > 0); }

...

}

In this case, although the code compiles well and without any warnings, it just

doesn't make sense. Its correct version should look like this:

public bool hasChilds(){ return(Childs[0] > 0 || Childs[1] > 0);}

The analyzer compares the code blocks, taking into account inversion of the

expression's parts in relation to the operator. For example, it will detect the error in

the following code:

Page 786: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

if (Name.Length > maxLength && maxLength < Name.Length)

V3002. The switch statement does not cover all values of the enum.The analyzer has detected a 'switch' statement where selection is done for a variable

of the enum type, some of the enumeration elements missing in the 'switch'

statement. This may indicate an error.

Consider this example:

public enum Actions { Add, Remove, Replace, Move, Reset };

public void SomeMethod(Actions act)

{

switch (act)

{

case Actions.Add: Calculate(1); break;

case Actions.Remove: Calculate(2); break;

case Actions.Replace: Calculate(3); break;

case Actions.Move: Calculate(5); break;

}

}

The 'Actions' enumeration in this code contains 5 named constants, while the

'switch' statement, selecting among the values of this enumeration, only selects

among 4 of them. This is very likely a mistake.

It may be that the programmer added a new constant during refactoring but forgot to

add it into the list of cases in the 'switch' statement, or simply skipped it by mistake,

as it sometimes happens with large enumerations. This results in incorrect

processing of the missing value.

The correct version of this code should look like this:

public void SomeMethod(Actions act)

{

Page 787: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

switch (act)

{

case Actions.Add: Calculate(1); break;

case Actions.Remove: Calculate(2); break;

case Actions.Replace: Calculate(3); break;

case Actions.Move: Calculate(5); break;

case Actions.Reset: Calculate(6); break;

}

}

Or this:

public void SomeMethod(Actions act)

{

switch (act)

{

case Actions.Add: Calculate(1); break;

case Actions.Remove: Calculate(2); break;

case Actions.Replace: Calculate(3); break;

case Actions.Move: Calculate(5); break;

default: Calculate(10); break;

}

}

The analyzer doesn't output the warning every time there are missing enumeration

elements in the 'switch' statement; otherwise, there would be too many false

positives. There are a number of empirical exceptions from this rule, the main of

which are the following:

A default-branch is present;

The missing constant's name includes the words "None", "Unknown", and the like.

The missing constant is the very last in the enumeration and its name includes the words "end", "num", "count", and the like.

The enumeration consists of only 1 or 2 constants;

And so on.

Page 788: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V3003. The use of 'if (A) {...} else if (A) {...}' pattern was detected. There is a probability of logical error presence.The analyzer has detected a potential error in a construct consisting of conditional

statements. Consider the following example:

if (a == 1)

Foo1();

else if (a == 2)

Foo2();

else if (a == 1)

Foo3();

In this code, the 'Foo3()' method will never get control. We are most likely dealing

with a logical error here and the correct version of this code should look as follows:

if (a == 1)

Foo1();

else if (a == 2)

Foo2();

else if (a == 3)

Foo3();

In practice though, errors of this type can take more complicated forms, as shown

below.

For example, the analyzer has found the following incorrect construct.

....

} else if (b.NodeType == ExpressionType.Or ||

b.NodeType == ExpressionType.OrEqual){

current.Condition = ConstraintType.Or;

} else if(...) {

Page 789: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

....

} else if (b.NodeType == ExpressionType.OrEqual ||

b.NodeType == ExpressionType.Or){

current.Condition = ConstraintType.Or |

ConstraintType.Equal;

} else if(....

In this example, the uppermost if statement checks the following condition:

b.NodeType == ExpressionType.Or ||

b.NodeType == ExpressionType.OrEqual.

while the lowermost if statement checks a condition with the same logic but written

in a reverse order, which is hardly noticeable for a human yet results in a runtime

error.

b.NodeType == ExpressionType.OrEqual ||

b.NodeType == ExpressionType.Or

V3004. The 'then' statement is equivalent to the 'else' statement.The analyzer has detected a suspicious code fragment with an 'if' statement whose

both true- and false-statements are absolutely identical. It is often a sign of an error.

For example:

if (condition)

result = FirstFunc(val);

else

result = FirstFunc(val);

Regardless of the variable's value, the same actions will be performed. This code is

obviously incorrect and should have looked something like this:

if (condition)

result = FirstFunc(val);

Page 790: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

else

result = SecondFunc(val);

V3005. The 'x' variable is assigned to itself.The analyzer has detected a potential error when a variable is assigned to itself.

Consider the following example taken from a real-life application:

public GridAnswerData(

int questionId, int answerId, int sectionNumber,

string fieldText, AnswerTypeMode typeMode)

{

this.QuestionId = this.QuestionId;

this.AnswerId = answerId;

this.FieldText = fieldText;

this.TypeMode = typeMode;

this.SectionNumber = sectionNumber;

}

As seen from the code, the programmer intended to change the values of an object's

properties according to the parameters accepted in the method, but mistakenly

assigned to the 'QuestionId' property its own value instead of the 'questionId'

argument's value.

The correct version of this code should have looked as follows:

public GridAnswerData(

int questionId, int answerId, int sectionNumber,

string fieldText, AnswerTypeMode typeMode)

{

this.QuestionId = questionId;

this.AnswerId = answerId;

this.FieldText = fieldText;

this.TypeMode = typeMode;

this.SectionNumber = sectionNumber;

Page 791: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

}

V3006. The object was created but it is not being used. The 'throw' keyword could be missing.The analyzer has detected a potential error when an instance of a class derived from

System.Exception is created but not being used in any way.

Here's an example of incorrect code:

public void DoSomething(int index)

{

if (index < 0)

new ArgumentOutOfRangeException();

else

....

}

In this fragment, the 'throw' statement is missing, so executing this code will only

result in creating an instance of a class derived from System.Exception without it

being used in any way, and the exception won't be generated. The correct version of

this code should look something like this:

public void DoSomething(int index)

{

if (index < 0)

throw new ArgumentOutOfRangeException();

else

....

}

V3007. Odd semicolon ';' after 'if/for/while' operator.

Page 792: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer has detected a potential error when a semicolon ';' is used after

statement 'for', 'while', or 'if'. Consider the following example:

int i = 0;

....

for(i = 0; i < arr.Count(); ++i);

arr[i] = i;

In this code, the programmer wanted the assignment operation to process all of the

array's items but added a semicolon by mistake after the closing parenthesis of the

loop. It results in the assignment operation being executed only once. Moreover, it

also causes an array index out of bounds error.

The correct version of the code should look as follows:

int i = 0;

....

for(i = 0; i < arr.Count(); ++i)

arr[i] = i;

The presence of a semicolon ';' after said statements does not always indicate an

error, of course. Sometimes a loop body is not required to execute the needed

statements, and the use of a semicolon is justified in such code. For example:

int i;

for (i = 0; !char.IsWhiteSpace(str[i]); ++i) ;

Console.WriteLine(i);

The analyzer won't output a warning in this and some other cases.

V3008. The 'x' variable is assigned values twice successively. Perhaps this is a mistake.

Page 793: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer has detected an error that has to do with assigning values to one and

the same variable twice in a row, while this variable is not used in any way between

the assignments.

Consider this example:

A = GetA();

A = GetB();

The 'A' variable being assigned values twice might indicate a bug. The code should

have most probably looked like this:

A = GetA();

B = GetB();

Cases when the variable is used between the assignments are treated as correct and

do not trigger the warning:

A = 1;

A = Foo(A);

The following is an example of the bug taken from a real-life application:

....

if (bool.TryParse(setting, out value))

_singleSignOn = value;

_singleSignOn = false;

....

A correct version of this code should look like this:

....

if (bool.TryParse(setting, out value))

_singleSignOn = value;

else

_singleSignOn = false;

....

Page 794: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer might output false positives sometimes. This happens when such

variable assignments are used for debugging purposes. For example:

status = Foo1();

status = Foo2();

The false positive in this code can be handled in a number of ways:

You can suppress it by inserting comment "//-V3008".

You can forbid the analyzer to output diagnostic V3008 for any case where variable 'status' is used. To do that, insert comment "//-V:status:3008".

You can remove idle assignments from the code.

Perhaps this code is incorrect, so we have to check the value of the 'status' variable.

V3009. It's odd that this method always returns one and the same value of NN.The analyzer has detected a strange method: it does not have any state and does not

change any global variables. At the same time, it has several return points returning

the same numerical, string, enum, constant or read only field value.

This code is very odd and might signal a possible error. The method is most likely

intended to return different values.

Consider the following simple example:

int Foo(int a)

{

if (a == 33)

return 1;

return 1;

}

Page 795: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

This code contains an error. Let's change one of the returned values to fix it. You

can usually identify the necessary returned values only when you know the

operation logic of the whole application in general

This is the fixed code:

int Foo(int a)

{

if (a == 33)

return 1;

return 2;

}

If the code is correct, you may get rid of the false positive using the "//-V3009"

comment.

V3010. The return value of function 'Foo' is required to be utilized.The analyzer has detected a suspicious call on a method whose return value is not

used. Calling certain methods doesn't make sense without using their return values.

Consider the following example:

public List<CodeCoverageSequencePoint> SequencePoints

{ get; private set; }

....

this.SequencePoints.OrderBy(item => item.Line);

In this code, extension method 'OrderBy' is called for the 'SequencePoints'

collection. This method sorts the collection by the specified criteria and returns its

sorted copy. Since the 'OrderBy' method doesn't modify the 'SequencePoints'

collection, it makes no sense calling it without saving the collection returned.

The correct version of the code above should look as follows:

var orderedList = this.SequencePoints.OrderBy(

Page 796: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

item => item.Line).ToList();

V3011. Two opposite conditions were encountered. The second condition is always false.The analyzer has detected a potential logical error: two conditional statements

executed in sequence contain mutually exclusive conditions.

Examples of such conditions:

"A == B" and "A != B";

"A > B" and "A <= B";

"A < B" and "B < A";

and so on.

This error can occur as a result of a typo or bad refactoring.

Consider the following example of incorrect code:

if (x == y)

if (y != x)

DoSomething(x, y);

In this fragment, the 'DoSomething' method will never be called because the second

condition will always be false when the first one is true. One of the variables used in

the comparison is probably wrong. In the second condition, for example, variable 'z'

should have been used instead of 'x':

if (x == y)

if (y != z)

DoSomething(x, y);

V3012. The '?:' operator, regardless of its conditional expression,

Page 797: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

always returns one and the same value.The analyzer has detected a potential error when using the ternary operator "?:".

Regardless of the condition's result, one and the same statement will be executed.

There is very likely a typo somewhere in the code.

Consider the following, simplest, example:

int A = B ? C : C;

In either case, the A variable will be assigned the value of the C variable.

Let's see what such an error may look like in real-life code:

fovRadius[0] = Math.Tan((rollAngleClamped % 2 == 0 ?

cg.fov_x : cg.fov_x) * 0.52) * sdist;

This code has been formatted. In reality, though, it may be written in one line, so it's

no wonder that a typo may stay unnoticed. The error here has to do with the

member of the "fov_x" class being used both times. The correct version of this code

should look as follows:

fovRadius[0] = Math.Tan((rollAngleClamped % 2 == 0 ?

cg.fov_x : cg.fov_y) * 0.52) * sdist;

V3013. It is odd that the body of 'Foo_1' function is fully equivalent to the body of 'Foo_2' function.The analyzer outputs this warning when it detects two functions implemented in the

same way. The presence of two identical functions in code is not an error in itself,

but such code should be inspected.

This diagnostic is meant for detecting the following type of bugs:

Page 798: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

class Point

{

....

float GetX() { return m_x; }

float GetY() { return m_x; }

};

A typo makes two different functions do the same thing. This is the correct version

of this code:

float GetX() { return m_x; }

float GetY() { return m_y; }

In the example above, the bodies of the functions GetX() and GetY() being alike is

obviously a sign of a bug. However, there would be too many false positives if we

set the analyzer to output this warning every time it encounters functions with

identical bodies. That's why it relies on a number of exceptions for cases when it

shouldn't output the warning. Such cases include the following:

Functions with identical bodies use no other variables but arguments. For example: "bool IsXYZ() { return true; }";

Functions with identical bodies are repeated more than twice;

The functions' bodies consist of only the throw() statement;

Etc.

There are a number of ways to handle the false positives. If they relate to the files of

external libraries or tests, you can add the path to these files or folders into the

exception list. If they relate to your own code, you can add the "//-V3013" comment

to suppress them. If there are too many false positives, you can disable this

diagnostic completely from the analyzer's settings. Also, you may want to modify

the code so that one function calls another.

The following is a code sample from a real-life application where functions meant

to do different work are implemented in the same way:

public void Pause(FrameworkElement target)

{

Page 799: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

if (Storyboard != null)

{

Storyboard.Pause(target);

}

}

public void Stop(FrameworkElement target)

{

if (Storyboard != null)

{

Storyboard.Stop(target);

}

}

public void Resume(FrameworkElement target)

{

if (Storyboard != null)

{

Storyboard.Pause(target);

}

}

Having made a few copies of one function, the programmer forgot to modify the

last of them, function Resume().

The correct version of this fragment should look like this:

public void Resume(FrameworkElement target)

{

if (Storyboard != null)

{

Storyboard.Resume(target);

}

}

Page 800: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V3014. It is likely that a wrong variable is being incremented inside the 'for' operator. Consider reviewing 'X'.The analyzer detected a potential error: a variable referring to an outer loop and

located inside the 'for' operator is incremented.

This is the simplest form of this error:

for (int i = 0; i < 5; i++)

for (int j = 0; j < 5; i++)

A[i][j] = 0;

It is the 'i' variable which is incremented instead of 'j' in the inner loop. Such an

error might be not so visible in a real application. This is the correct code:

for (int i = 0; i < 5; i++)

for (int j = 0; j < 5; j++)

A[i][j] = 0;

V3015. It is likely that a wrong variable is being compared inside the 'for' operator. Consider reviewing 'X'.The analyzer detected a potential error: a variable referring to an outer loop is used

in the condition of the 'for' operator.

This is the simplest form of this error:

for (int i = 0; i < 5; i++)

Page 801: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

for (int j = 0; i < 5; j++)

A[i][j] = 0;

It is the comparison 'i < 5' that is performed instead of 'j < 5' in the inner loop. Such

an error might be not so visible in a real application. This is the correct code:

for (int i = 0; i < 5; i++)

for (int j = 0; j < 5; j++)

A[i][j] = 0;

V3016. The variable 'X' is being used for this loop and for the outer loop.The analyzer detected a potential error: a nested loop is arranged by a variable

which is also used in an outer loop. In a schematic form, this error looks in the

following way:

int i = 0, j = 0;

for (i = 0; i < 5; i++)

for (i = 0; i < 5; i++)

A[i][j] = 0;

Of course, this is an artificial sample, so we may easily see the error, but in a real

application, the error might be not so apparent. This is the correct code:

int i = 0, j = 0;

for (i = 0; i < 5; i++)

for (j = 0; j < 5; j++)

A[i][j] = 0;

Using one variable both for the outer and inner loops is not always a mistake.

Consider a sample of correct code the analyzer won't generate the warning for:

for(c = lb; c <= ub; c++)

{

if (!(xlb <= xlat(c) && xlat(c) <= ub))

Page 802: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

{

Range r = new Range(xlb, xlb + 1);

for (c = lb + 1; c <= ub; c++)

r = DoUnion(r, new Range(xlat(c), xlat(c) + 1));

return r;

}

}

In this code, the inner loop "for (c = lb + 1; c <= ub; c++)" is arranged by the "c"

variable. The outer loop also uses the "c" variable. But there is no error here. After

the inner loop is executed, the "return r;" operator will perform exit from the

function.

V3017. A pattern was detected: A || (A && ...). The expression is excessive or contains a logical error.The analyzer has detected an expression that can be reduced. Such redundancy may

be a sign of a logical error. Consider this example:

bool firstCond, secondCod, thirdCond;

....

if (firstCond || (firstCond && thirdCond))

....

This expression is redundant. If 'firstCond == true', the condition will always be true

regardless of what value the 'thirdCond' variable refers to; and if 'firstCond ==

false', the condition will always be false – again, irrespective of the 'thirdCond'

variable.

Perhaps the programmer made a mistake and wrote a wrong variable in the second

subexpression. Then the correct version of this code should look like this:

if (firstCond || (secondCod && thirdCond))

Page 803: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V3018. Consider inspecting the application's logic. It's possible that 'else' keyword is missing.The analyzer has detected a code fragment where an 'if' statement occupies the same

line as the closing brace of the previous 'if' statement. The 'else' keyword may be

missing in this line, and this causes the program to work differently than expected.

Consider the following example:

if (cond1) {

Method1(val);

} if (cond2) {

Method2(val);

} else {

Method3(val);

}

If the 'cond1' condition is true, not only will method 'Method1' be called, but

method 'Method2' or 'Method3' as well. If it is exactly this logic that was intended,

the code formatting should be fixed by moving the second 'if' statement to the next

line:

if (cond1) {

Method1(val);

}

if (cond2) {

Method2(val);

} else {

Method3(val);

}

This code formatting is more conventional and won't make other programmers

suspect a bug. Besides, the analyzer will stop outputting the warning, too.

Page 804: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

But if it's not the behavior that the programmer really intended, then there is an

execution logic error, so the keyword 'else' must be added. Correct code in this case

will look as follows:

if (cond1) {

Method1(val);

} else if (cond2) {

Method2(val);

} else {

Method3(val);

}

V3019. It is possible that an incorrect variable is compared with null after type conversion using 'as' keyword.The analyzer has detected a potential error that may lead to memory access by a null

reference.

The situation that the analyzer detected deals with the following algorithm. An

object of the base class is first cast to a derived class by using the 'as' operator. Then

the same object is checked for a null value, though it is the object of the derived

class that this check should have been applied to.

Here's an example. In this code, the baseObj object may not be an instance of the

Derived class, in which case, when calling the Func function, the program will

crash, raising the NullReferenceException. The analyzer will output a warning

pointing out two lines. The first line is the spot where the object of the base class is

checked for null; the second is where it is cast to an object of the derived class.

Base baseObj;

Derived derivedObj = baseObj as Derived;

if (baseObj != null)

Page 805: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

{

derivedObj.Func();

}

It is most likely the object of the derived class that the programmer intended to

check for null before using it. This is the fixed version of the code:

Base baseObj;

Derived derivedObj = baseObj as Derived;

if (derivedObj != null)

{

derivedObj.Func();

}

V3020. An unconditional 'break/continue/return/goto' within a loop.The analyzer has detected a suspicious loop where one of the following statements

is used: continue, break, return, goto, or throw. These statements are executed all

the time, irrespective of any conditions. For example:

while (k < max)

{

if (k == index)

value = Calculate(k);

break;

++k;

}

In this code, the 'break' statement doesn't belong to the 'if' statement, which will

cause it to execute all the time, regardless of whether or not the 'k == index'

condition is true, and the loop body will iterate only once. The correct version of

this code should look like this:

Page 806: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

while (k < max)

{

if (k == index)

{

value = Calculate(k);

break;

}

++k;

}

V3021. There are two 'if' statements with identical conditional expressions. The first 'if' statement contains method return. This means that the second 'if' statement is senseless.The analyzer has detected an issue when the 'then' part of the 'if' operator never gets

control. It happens because there is another 'if' before which contains the same

condition whose 'then' part contains the unconditional 'return' operator. It may

signal both a logical error in the program and an unnecessary second 'if' operator.

Consider the following example of incorrect code:

if (l >= 0x06C0 && l <= 0x06CE) return true;

if (l >= 0x06D0 && l <= 0x06D3) return true;

if (l == 0x06D5) return true; // <=

if (l >= 0x06E5 && l <= 0x06E6) return true;

if (l >= 0x0905 && l <= 0x0939) return true;

if (l == 0x06D5) return true; // <=

if (l >= 0x0958 && l <= 0x0961) return true;

if (l >= 0x0985 && l <= 0x098C) return true;

Page 807: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

In this case, the 'l == 0x06D5' condition is doubled, and we just need to remove one

of them to fix the code. However, it may be that the value being checked in the

second case should be different from the first one.

This is the fixed code:

if (l >= 0x06C0 && l <= 0x06CE) return true;

if (l >= 0x06D0 && l <= 0x06D3) return true;

if (l == 0x06D5) return true;

if (l >= 0x06E5 && l <= 0x06E6) return true;

if (l >= 0x0905 && l <= 0x0939) return true;

if (l >= 0x0958 && l <= 0x0961) return true;

if (l >= 0x0985 && l <= 0x098C) return true;

V3022. Expression is always true/false.The analyzer has detected a possible error that has to do with a condition which is

always either true or false. Such conditions do not necessarily indicate a bug, but

they need reviewing.

Consider the following example:

string niceUrl = GetUrl();

if (niceUrl != "#" || niceUrl != "") {

Process(niceUrl);

} else {

HandleError();

}

The analyzer outputs the following warning:

"V3022 Expression 'niceUrl != "#" || niceUrl != ""' is always true. Probably the '&&'

operator should be used here. "

The else branch in this code will never be executed because regardless of what

value the niceUrl variable refers to, one of the two comparisons with a string will

Page 808: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

always be true. To fix this error, we need to use operator && instead of ||. This is

the fixed version of the code:

string niceUrl = GetUrl();

if (niceUrl != "#" && niceUrl != "") {

Process(niceUrl);

} else {

HandleError();

}

Now let's discuss a code sample with a meaningless comparison. It's not necessarily

a bug, but this code should be reviewed:

byte type = reader.ReadByte();

if (type < 0)

recordType = RecordType.DocumentEnd;

else

recordType = GetRecordType(type);

The error here is in comparing an unsigned variable with zero. This sample will

trigger the warning "V3022 Expression 'type < 0' is always false. Unsigned type

value is always >= 0." The code either contains an unnecessary comparison or

incorrectly handles the situation of reaching the end of the document.

The analyzer doesn't warn about every condition that is always true or false; it only

diagnoses those cases when a bug is highly probable. Here are some examples of

code that the analyzer treats as correct:

// 1) Code block temporarily not compiled

if (false && CheckCondition())

{

...

}

// 2) Expressions inside Debug.Assert()

public enum Actions { None, Start, Stop }

...

Page 809: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Debug.Assert(Actions.Start > 0);

V3023. Consider inspecting this expression. The expression is excessive or contains a misprint.The analyzer has detected a suspicious code fragment with a redundant comparison.

There may be a superfluous check, in which case the expression can be simplified,

or an error, which should be fixed. Consider the following example:

if (firstVal == 3 && firstVal != 5)

This code is redundant as the condition will be true if 'firstVal == 3', so the second

part of the expression just makes no sense.

There are two possible explanations here:

1) The second check is just unnecessary and the expression can be simplified. If so,

the correct version of that code should look like this:

if (firstVal == 3)

2) There is a bug in the expression; the programmer wanted to use a different

variable instead of 'firstVal'. Then the correct version of the code should look as

follows:

if (firstVal == 3 && secondVal != 5)

V3024. An odd precise comparison. Consider using a comparison with defined precision: Math.Abs(A - B) < Epsilon or Math.Abs(A - B) > Epsilon.

Page 810: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer has detected a suspicious code fragment where floating-point numbers

are compared using operator '==' or '!='. Such code may contain a bug.

Let's discuss an example of correct code first (which will, however, trigger the

warning anyway):

double a = 0.5;

if (a == 0.5) //ok

++x;

This comparison is correct. Before executing it, the 'a' variable is explicitly

initialized to value '0.5', and it is this value the comparison is done over. The

expression will evaluate to 'true'.

So, strict comparisons are permitted in certain cases - but not all the time. Here's an

example of incorrect code:

double b = Math.Sin(Math.PI / 6.0);

if (b == 0.5) //err

++x;

The 'b == 0.5' condition proves false because the 'Math.Sin(Math.PI / 6.0)'

expression evaluates to 0.49999999999999994. This number is very close but still

not equal to '0.5'.

One way to fix this is to compare the difference of the two values against some

reference value (i.e. amount of error, which in this case is expressed by variable

'epsilon'):

double b = Math.Sin(Math.PI / 6.0);

if (Math.Abs(b - 0.5) < epsilon) //ok

++x;

You should estimate the error amount appropriately, depending on what values are

being compared.

Page 811: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer points out those code fragments where floating-point numbers are

compared using operator '!=' or '==', but it's the programmer alone who can figure

out whether or not such comparison is incorrect.

References:

1. Stack Overflow - Comparing double values in C#

V3025. Incorrect format. Consider checking the N format items of the 'Foo' function.The analyzer has detected a possible error related to use of formatting methods:

String.Format, Console.WriteLine, Console.Write, etc. The format string does not

correspond with actual arguments passed to the method. Here are some simple

examples:

Unused arguments.

int A = 10, B = 20;

double C = 30.0;

Console.WriteLine("{0} < {1}", A, B, C);

Format item {2} is not specified, so variable 'C' won't be used.

Possible correct versions of the code:

//Remove extra argument

Console.WriteLine("{0} < {1}", A, B);

//Fix format string

Console.WriteLine("{0} < {1} < {2}", A, B, C);

Number of arguments passed is less than expected.

int A = 10, B = 20;

double C = 30.0;

Page 812: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Console.WriteLine("{0} < {1} < {2}", A, B);

Console.WriteLine("{1} < {2}", A, B);

A much more dangerous situation occurs when a function receives fewer arguments

than expected. This will raise a FormatException exception.

Possible correct versions of the code:

//Add missing argument

Console.WriteLine("{0} < {1} < {2}", A, B, C);

//Fix indices in format string

Console.WriteLine("{0} < {1}", A, B);

The analyzer doesn't output the warning given that:

1. The number of format items specified matches the number of arguments.

2. The format object is used a number of times:

int row = 10;

Console.WriteLine("Line: {0}; Index: {0}", row);

Here is an example of this bug in a real-life application:

var sql = string.Format(

"SELECT {0} FROM (SELECT ROW_NUMBER() " +

" OVER (ORDER BY {2}) AS Row, {0} FROM {3} {4}) AS Paged ",

columns, pageSize, orderBy, TableName, where);

The function receives 5 formatting objects, but the 'pageSize' variable is not used as

format item {1} is missing.

V3026. The constant NN is being utilized. The resulting value could be inaccurate. Consider using the KK constant.

Page 813: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer has detected an issue that deals with using constants of poor accuracy

in mathematical calculations. Consider this example:

double pi = 3.141592654;

This way of writing the pi constant is not quite correct. It's preferable to use

mathematical constants from the static class Math:

double pi = Math.PI;

The analyzer doesn't output the warning for cases when constants are explicitly

defined as of 'float' type. The reason is that type 'float' has fewer significant

positions than type 'double'. That is why the following code won't trigger the

warning:

float f = 3.14159f; //ok

V3027. The variable was utilized in the logical expression before it was verified against null in the same logical expression.The analyzer has detected an issue that has to do with checking a variable for 'null'

after it has been used (in a method call, attribute access, and so on). This diagnostic

operates within one logical expression.

Consider the following example:

if (rootDoc.Text.Trim() == documentName.Trim() && rootDoc != null)

In this code, attribute 'Text' is accessed first (moreover, method 'Trim' is called for

this attribute), and only then the 'rootDoc' reference is checked for 'null'. If it proves

to be equal to 'null', a 'NullReferenceException' will be raised. This bug can be

fixed by having the referenced checked first and only then accessing the object's

attribute:

Page 814: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

if (rootDoc != null && rootDoc.Text.Trim() == documentName.Trim())

This is the simplest way to fix the error. However, you should carefully examine the

code to figure out how to fix it best in every particular case.

V3028. Consider inspecting the 'for' operator. Initial and final values of the iterator are the same.The analyzer has detected a potential error: initial and finite counter values coincide

in the 'for' operator. Using the 'for' operator in such a way will cause the loop to be

executed only once or not be executed at all. Consider the following example:

void BeginAndEndForCheck(int beginLine, int endLine)

{

for (int i = beginLine; i < beginLine; i++)

{

...

}

The loop body is never executed. Most likely, there is a misprint and "i <

beginLine" should be replaced with the correct expression "i < endLine". This is the

correct code:

for (int i = beginLine; i < endLine; i++)

{

...

}

Another example:

for (int i = A; i <= A; i++)

...

This loop's body will be executed only once. This is probably not what the

programmer intended.

Page 815: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V3029. The conditional expressions of the 'if' statements situated alongside each other are identical.The analyzer has detected two 'if' statements with identical conditions following

each other. This code is either redundant or incorrect.

Consider the following example:

public void Logging(string S_1, string S_2)

{

if (!String.IsNullOrEmpty(S_1))

Print(S_1);

if (!String.IsNullOrEmpty(S_1))

Print(S_2);

}

There is an error in the second condition, where the 'S_1' variable is checked for the

second time whereas it is variable 'S_2' that should be checked instead.

This is what the correct version of the code looks like:

public void Logging(string S_1, string S_2)

{

if (!String.IsNullOrEmpty(S_1))

Print(S_1);

if (!String.IsNullOrEmpty(S_2))

Print(S_2);

}

This diagnostic does not always point out a bug; often, it deals with just redundant

code:

public void Logging2(bool toFile, string S_1, string S_2)

{

if(toFile)

Page 816: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Print(S_1);

if (toFile)

Print(S_2);

}

This code is correct but somewhat inefficient since it checks one and the same

variable twice. We suggest rewriting it as follows:

public void Logging2(bool toFile, string S_1, string S_2)

{

if(toFile)

{

Print(S_1);

Print(S_2);

}

}

V3030. Recurring check. This condition was already verified in previous line.The analyzer has detected a possible error that has to do with one and the same

condition being checked twice. Consider the following two examples:

// Example N1:

if (A == B)

{

if (A == B)

....

}

// Example N2:

if (A == B) {

} else {

if (A == B)

Page 817: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

....

}

The second "if (A == B)" condition is always true in the first case and always false

in the second.

This code is very likely to contain an error – for example a wrong variable name is

used because of a typo. Correct versions of the examples above should look like

this:

// Example N1:

if (A == B)

{

if (A == C)

....

}

// Example N2:

if (A == B) {

} else {

if (A == C)

....

}

V3031. An excessive check can be simplified. The operator '||' operator is surrounded by opposite expressions 'x' and '!x'.The analyzer has detected a code fragment that can be simplified. In this code,

expressions with opposite meanings are used as operands of the '||' operator. This

code is redundant and, therefore, can be simplified by using fewer checks.

Consider this example:

Page 818: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

if (str == null || (str != null && str == "Unknown"))

In the "str != null && str == "Unknown"" expression, the condition "str != null" is

redundant since an opposite condition, "str == null", is checked before it, while both

expressions act as operands of operator '||'. So the superfluous check inside the

parentheses can be left out to make the code shorter:

if (str == null || str == "Unknown"))

Redundancy may be a sign of an error – for example use of a wrong variable. If this

is the case, the fixed version of the code above should look like this:

if (cond || (str != null && str == "Unknown"))

Sometimes the condition is written in a reversed order and on first glance can not be

simplified:

if ((s != null && s == "Unknown") || s == null)

It seems that we can't get rid neither of a (s!=null) nor of a (s==null) check. This is

not the case. This expression and the case described above, can be simplified:

if (s == null || s == "Unknown")

V3032. Waiting on this expression is unreliable, as compiler may optimize some of the variables. Use volatile variable(s) or synchronization primitives to avoid this.

Page 819: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer has detected a loop that may turn into an infinite one due to compiler-

driven optimization. Such loops are usually used when the program is waiting for an

external event.

Consider the following example:

private int _a;

public void Foo()

{

var task = new Task(Bar);

task.Start();

Thread.Sleep(10000);

_a = 0;

task.Wait();

}

public void Bar()

{

_a = 1;

while (_a == 1);

}

If this code is compiled and executed in Debug configuration, the program will

terminate correctly. But when compiled in Release mode, it will hang at the while

loop. The reason is that the compiler will "cache" the value referred to by the '_a'

variable.

This difference between Debug and Release versions may lead to complicated and

hard-to-detect bugs, which can be fixed in a number of ways. For example, if the

variable in question is really used to control the logic of a multithreaded program,

special synchronization means such as mutexes or semaphores should be used

instead. Another way is to add modifier 'volatile' to the variable definition:

private volatile int _a;

...

Page 820: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Note that these means alone do not secure the sample code completely since Bar() is

not guaranteed to start executing before the '_a' variable is assigned 0. We discussed

this example only to demonstrate a potentially dangerous situation related to

compiler optimizations. To make that code completely safe, additional

synchronization is required before the _a = 0 expression to ensure that the _a = 1

expression has been executed.

V3033. It is possible that this 'else' branch must apply to the previous 'if' statement.The analyzer detected a potential error in logical conditions: code's logic does not

coincide with the code formatting.

Consider this sample:

if (X)

if (Y) Foo();

else

z = 1;

The code formatting disorientates you so it seems that the "z = 1" assignment takes

place if X == false. But the 'else' branch refers to the nearest operator 'if'. In other

words, this code is actually analogous to the following code:

if (X)

{

if (Y)

Foo();

else

z = 1;}

So, the code does not work the way it seems at first sight.

If you get the V3033 warning, it may mean one of the two following things:

Page 821: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

1) Your code is badly formatted and there is no error actually. In this case you need

to edit the code so that it becomes clearer and the V3033 warning is not generated.

Here is a sample of correct editing:

if (X)

if (Y)

Foo();

else

z = 1;

2) A logical error has been found. Then you may correct the code, for instance, this

way:

if (X) {

if (Y)

Foo();

} else {

z = 1;

}

V3034. Consider inspecting the expression. Probably the '!=' should be used here.The analyzer has detected a potential error. The '!=' or '== !' operator should be

probably used instead of the '=!' operator. Such errors most often occur through

misprints.

Consider an example of incorrect code:

bool a, b;

...

if (a =! b)

{

...

Page 822: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

}

It's most probably that this code should check that the 'a' variable is not equal to 'b'.

If so, the correct code should look like follows:

if (a != b)

{

...

}

The analyzer accounts for formatting in the expression. That's why if it is exactly

assignment you need to perform - not comparison - you should specify it through

parentheses or blanks. The following code samples are considered correct:

if (a = !b)

...

if (a=(!b))

...

V3035. Consider inspecting the expression. Probably the '+=' should be used here.The analyzer detected a potential error: there is a sequence of '=+' characters in

code. It might be a misprint and you should use the '+=' operator.

Consider the following example:

int size, delta;

...

size=+delta;

This code may be correct, but it is highly probable that there is a misprint and the

programmer actually intended to use the '+=' operator. This is the fixed code:

int size, delta;

Page 823: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

...

size+=delta;

If this code is correct, you may remove '+' or type in an additional space to prevent

showing the V3035 warning. The following is an example of correct code where the

warning is not generated:

size = delta;

size = +delta;

Note. To search for misprints of the 'A =- B' kind, we use the V3036 diagnostic

rule. This check is implemented separately since a lot of false reports are probable

and you may want to disable it.

V3036. Consider inspecting the expression. Probably the '-=' should be used here.The analyzer detected a potential error: there is a sequence of '=-' characters in code.

It might be a misprint and you should use the '-=' operator.

Consider this sample:

int size, delta;

...

size =- delta;

This code may be correct, but it is highly probable that there is a misprint and the

programmer actually intended to use the '-=' operator. This is the fixed code:

int size, delta;

...

size -= delta;

Page 824: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

If the code is correct, you may type in an additional space between the characters '='

and '-' to remove the V3036 warning. This is an example of correct code where the

warning is not generated:

size = -delta;

To make false reports fewer, there are some specific exceptions to the V3036 rule.

For instance, the analyzer will not generate the warning if a programmer does not

use spaces between variables and operators. Here are some samples of code the

analyzer considers safe:

A=-B;

int Z =- 1;

N =- N;

Note. To search for misprints of the 'A =+ B' type, the V3035 diagnostic check is

used.

V3037. An odd sequence of assignments of this kind: A = B; B = A;The analyzer has detected a possible error that has to do with meaningless variable

assignments.

Consider this example:

int a, b, c;

...

a = b;

c = 10;

b = a;

The "B = A" assignment statement in this code does not make sense. It might be a

typo or just unnecessary operation. This is what the correct version of the code

should look like:

Page 825: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

a = b;

c = 10;

b = a_2;

V3038. The argument was passed to method several times. It is possible that another argument should be passed instead.The analyzer detected a possible error that has to do with passing two identical

arguments to a method. It is a normal practice to pass one value as two arguments to

many methods, so we implemented this diagnostic with certain restrictions.

The warning is triggered when arguments passed to the method and the method's

parameters have a common pattern by which they can be described. Consider the

following example:

void Do(int mX, int mY, int mZ)

{

// Some action

}

void Foo(Vecor3i vec)

{

Do(vec.x, vec.y, vec.y);

}

Note the 'Do' method's signature and its call: the 'vec.y' argument is passed twice,

while the 'mZ' parameter is likely to correspond to argument 'vec.z'. The fixed

version could look like this:

Do(vec.x, vec.y, vec.z);

Page 826: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The diagnostic suggests possible correct versions of one of the duplicate arguments,

and if the suggested variable is within the scope of the caller, a warning will be

displayed with information about the suspected typo and the correct argument.

V3038 The 'vec.y' argument was passed to 'Do' method several times. It is possible

that the 'vec.z' argument should be passed to 'mZ' parameter.

Another suspicious situation is passing identical arguments to such functions as

'Math.Min', 'Math.Max', 'string.Equals', etc..

Consider the following example:

int count, capacity;

....

size = Math.Max(count, count);

A typo causes the 'Math.Max' function to compare a variable with itself. This is the

fixed version:

size = Math.Max(count, capacity);

If you have encountered an error of this kind that the analyzer failed to diagnose,

please email us and specify the name of the function that you do not want to receive

one variable for several arguments.

Here is another example of an error found in real-life code:

return invariantString

.Replace(@"\", @"\\")

.Replace("'", @"\'")

.Replace("\"", @"""");

The programmer seems to be unfamiliar with the specifics of string literals preceded

by the '@' character, which was the cause of a subtle error when writing the

sequence @"""". Based on the code, it seems the programmer wanted to have two

quotation marks added in succession. However, because of the mistake, one

quotation mark will be replaced by another. There are two ways to fix this error.

The first solution:

Page 827: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

.Replace("\"", "\"\"")

The second solution:

.Replace("\"", @"""""")

V3039. Consider inspecting the 'Foo' function call. Defining an absolute path to the file or directory is considered a poor style.The analyzer has detected a possible error in a call to a function intended for file

handling. The error has to do with an absolute path to a file or directory being

passed to the function as one of the arguments. Passing absolute paths as arguments

can be dangerous since such paths may not exist on the user's computer.

Consider the following example:

String[] file = File.ReadAllLines(

@"C:\Program Files\MyProgram\file.txt");

A better solution is to get the path to the file based on certain conditions.

This is what the fixed version of the code should look like:

String appPath = Path.GetDirectoryName(

Assembly.GetExecutingAssembly().Location);

String[] fileContent = File.ReadAllLines(

Path.Combine(appPath, "file.txt"));

In this code, the file will be looked up in the application's directory.

Page 828: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V3040. The expression contains a suspicious mix of integer and real typesThe analyzer detected a possible error in an expression where integer and real data

types are used together. Real data types include types 'float' and 'double'.

Consider the following example taken from a real application:

public long ElapsedMilliseconds { get; }

....

var minutes = watch.ElapsedMilliseconds / 1000 / 60;

Assert.IsTrue(minutes >= 0.95 && minutes <= 1.05);

The 'minutes' variable is of type 'long', and comparing it with values 0.95 and 1.05

does not make sense. The only integer value that fits into this range is 1.

The programmer probably expected the result of integer division operation to be a

value of type 'double', but it is not so. In the example above, integer division

produces an integer value, which is assigned to the 'minutes' variable.

This code can be fixed by explicitly casting the number of milliseconds to type

'double', before the division operation:

var minutes = (double)watch.ElapsedMilliseconds / 1000 / 60;

Assert.IsTrue(minutes >= 0.95 && minutes <= 1.05);

The quotient will now be more accurate, and the 'minutes' variable will be of type

'double'.

V3041. The expression was implicitly cast from integer type to real type. Consider utilizing an

Page 829: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

explicit type cast to avoid the loss of a fractional part.The analyzer detected a possible error that has to do with a result of integer division

being implicitly cast to type float. Such cast may lead to inaccurate result.

Consider the following example:

int totalTime = 1700;

int operationNum = 900;

double averageTime = totalTime / operationNum;

The programmer expects the 'averageTime' variable to refer to value '1.888(8)', but

because the division operation is applied to integer values and only then is the

resulting value cast to type float, the variable will actually refer to '1.0'.

As in the previous case, there are two ways to fix the error.

One way is to change the variables' types:

double totalTime = 1700;

double operationNum = 900;

double averageTime = totalTime / operationNum;

Another way is to use explicit type cast.

int totalTime = 1700;

int operationNum = 900;

double averageTime = (double)(totalTime) / operationNum;

V3042. Possible NullReferenceException. The '?.' and '.' operators are used for

Page 830: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

accessing members of the same object.The analyzer has detected that members of one object are accessed in two different

ways – using operators "?." and ".". When accessing a part of an expression through

"?.", it is assumed that the preceding member may be null; therefore, trying to

access this member using operator "." will cause a crash.

Consider the following example:

if (A?.X == X || A.X == maxX)

...

The programmer's inattention may result in a situation when the first check will

return false and the second check will raise a NullReferenceException if "A" is null.

The fixed code should look like this:

if (A?.X == X || A?.X == maxX)

...

And here is another example of this error, taken from a real application:

return node.IsKind(SyntaxKind.IdentifierName) &&

node?.Parent?.FirstAncestorOrSelf<....>() != null;

In the second part of the condition, it is assumed that "node" may be null:

"node?.Parent"; but there is no such check when calling function "IsKind".

V3043. The code's operational logic does not correspond with its formatting.

Page 831: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer detected a possible error: the formatting of the code after a conditional

statement does not correspond with the program's execution logic. Opening and

closing braces may be missing.

Consider the following example:

if (a == 1)

b = c; d = b;

In this code, the assignment 'd = b;' will be executed all the time regardless of the 'a

== 1' condition.

If it is really an error, the code can be fixed by adding the braces:

if (a == 1)

{ b = c; d = b; }

Here is one more example of incorrect code:

if (a == 1)

b = c;

d = b;

Again, we need to put in the braces to fix the error:

if (a == 1)

{

b = c;

d = b;

}

If it is not an error, the code should be formatted in the following way to prevent the

displaying of warning V3043:

if (a == 1)

b = c;

d = b;

Page 832: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V3044. WPF: writing and reading are performed on a different Dependency Properties.The analyzer detected a possible error related to dependency property registration.

The property that performs writing into/reading from properties was defined

incorrectly.

class A : DependencyObject

{

public static readonly DependencyProperty CurrentTimeProperty =

DependencyProperty.Register("CurrentTime", ....);

public static readonly DependencyProperty OtherProperty =

DependencyProperty.Register("Other", ....);

public DateTime CurrentTime {

get { return (DateTime)GetValue(CurrentTimeProperty); }

set { SetValue(OtherProperty, value); } }

}

....

Because of copy-paste, the methods GetValue and SetValue, used in the definitions

of the get and set access methods of the CurrentTime property, work with different

dependency properties. As a result, when reading from CurrentTime, the value will

be retrieved from the CurrentTimeProperty dependency property, but when writing

a value into CurrentTime, it will be written into 'OtherProperty'.

A correct way to address the dependency property in the code above is as follows:

public DateTime CurrentTime {

get { return (DateTime)GetValue(CurrentTimeProperty); }

set { SetValue(CurrentTimeProperty, value); } }

}

Page 833: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V3045. WPF: the names of the property registered for DependencyProperty, and of the property used to access it, do not correspond with each other.The analyzer detected a possible error related to dependency property registration.

A wrong name was defined for the property used to access the registered

dependency property.

class A : DependencyObject

{

public static readonly DependencyProperty ColumnRulerPenProperty =

DependencyProperty.Register("ColumnRulerBrush", ....);

public DateTime ColumnRulerPen {

get { return (DateTime)GetValue(ColumnRulerPenProperty); }

set { SetValue(ColumnRulerPenProperty, value); }

}

....

Because of renaming, a wrong name was defined for the property used for writing

into the ColumnRulerPenProperty dependency property. In the example above,

taken from a real application, the name ColumnRulerPen is used instead of

ColumnRulerBrush (as suggested by the Register function's parameters).

Implementing dependency properties in a way like that may cause problems

because, when accessing the ColumnRulerPen property from the XAML markup for

the first time, the value will be successfully read, but it won't update as this property

changes.

A correct property definition in the code above should look like this:

Page 834: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

public DateTime ColumnRulerBrush {

get { return (DateTime)GetValue(CurrentTimeProperty); }

set { SetValue(CurrentTimeProperty, value); }

}

In real programs, the following version of incorrect dependency property name

definition is also common:

public static readonly DependencyProperty WedgeAngleProperty =

DependencyProperty.Register("WedgeAngleProperty", ....);

It is supposed that the word "Property" will be missing from the string literal:

public static readonly DependencyProperty WedgeAngleProperty =

DependencyProperty.Register("WedgeAngle", ....);

V3046. WPF: the type registered for DependencyProperty does not correspond with the type of the property used to access it.The analyzer detected a possible error related to dependency property registration.

When registering a dependency property, a wrong type was specified for its values.

In the following example, it is property CurrentTimeProperty:

class A : DependencyObject

{

public static readonly DependencyProperty CurrentTimeProperty =

DependencyProperty.Register("CurrentTime", typeof(int),....);

public DateTime CurrentTime

{

get { return (DateTime)GetValue(CurrentTimeProperty); }

set { SetValue(CurrentTimeProperty, value); }

}

Page 835: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

....

Because of using copy-paste when registering the dependency property, type int

was mistakenly specified as the type of values taken by the property. Trying to write

into or read from CurrentTimeProperty within the CurrentTime property will raise

an error.

A correct way to register the dependency property in the code above is as follows:

public static readonly DependencyProperty CurrentTimeProperty =

DependencyProperty.Register("CurrentTime", typeof(DateTime),....);

This diagnostic also checks if the type of a dependency property being registered

and the type of its default value correspond with each other.

public static readonly DependencyProperty CurrentTimeProperty =

DependencyProperty.Register("CurrentTime", typeof(DateTime),

typeof(A),

new FrameworkPropertyMetadata(132));

In this example, the default value is 132 while type DateTime is specified as the

type of values that the property can take.

V3047. WPF: A class containing registered property does not correspond with a type that is passed as the ownerType.type.The analyzer detected a potential error related to dependency property registration.

When registering a dependency property, the owner type specified for this property

refers to a class different from the one the property is originally defined in.

class A : DependencyObject { .... }

class B : DependencyObject

{

Page 836: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

public static readonly DependencyProperty CurrentTimeProperty =

DependencyProperty.Register("CurrentTime", typeof(DateTime),

typeof(A));

....

Because of using copy-paste when registering the dependency property, class 'A'

was mistakenly specified as its owner while this property was actually defined in

class 'B'.

A correct way to register this dependency property is as follows:

class B : DependencyObject

{

public static readonly DependencyProperty CurrentTimeProperty =

DependencyProperty.Register("CurrentTime", typeof(DateTime),

typeof(B));

V3048. WPF: several Dependency Properties are registered with a same name within the owner type.The analyzer detected a possible error related to dependency property registration.

Two dependency properties were registered under the same name within one class.

class A : DependencyObject

{

public static readonly DependencyProperty CurrentTimeProperty =

DependencyProperty.Register("CurrentTime",....);

public static readonly DependencyProperty OtherProperty =

DependencyProperty.Register("CurrentTime",....);

....

Because of copy-paste, the OtherProperty dependency property was registered

under the name 'CurrentTime' instead of 'Other' as intended by the developer.

Page 837: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

A correct way to register the dependency properties in the code above is as follows:

public static readonly DependencyProperty CurrentTimeProperty =

DependencyProperty.Register("CurrentTime",....);

public static readonly DependencyProperty OtherProperty =

DependencyProperty.Register("Other",....);

V3049. WPF: readonly field of 'DependencyProperty' type is not initialized.The analyzer detected a possible error related to dependency property registration.

A dependency property was defined but wasn't initialized: it will cause an error

when trying to access the property using SetValue / GetValue.

class A : DependencyObject

{

public static readonly DependencyProperty CurrentTimeProperty;

static A(){ /* CurrentTimeProperty not initialized */ }

....

Bad refactoring or copy-paste may result in leaving a dependency property

unregistered. The following is the fixed version of the code above:

class A : DependencyObject

{

public static readonly DependencyProperty CurrentTimeProperty;

static A()

{

CurrentTimeProperty =

DependencyProperty.Register("CurrentTime", typeof(DateTime),

typeof(A));

}

....

Page 838: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V3050. Possibly an incorrect HTML. The </XX> closing tag was encountered, while the </YY> tag was expected.The analyzer has detected a string literal containing HTML markup with errors: a

closing tag required for an element does not correspond with its opening tag.

Consider the following example:

string html = "<B><I>This is a text, in bold italics.</B>";

In this code, the opening tag "<I>" must be matched with closing tag "</I>";

instead, closing tag "</B>" is encountered further in the string. This is an error,

which renders this part of the HTML code invalid.

To fix the error, correct sequences of opening and closing tags must be ensured.

This is what the fixed version of the code should look like:

string html = "<B><I>This is a text, in bold italics.</I></B>";

V3051. An excessive type cast or check. The object is already of the same type.An expression with a redundant operator 'as' or 'is' was detected. It makes no sense

casting an object to or checking its compatibility with its own type. Such operations

are usually just redundant code, but sometimes they may indicate a bug. To figure

out what this bug pattern is about, let's discuss a few examples.

A synthetic example:

Page 839: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

public void SomeMethod(String str)

{

var localStr = str as String;

....

}

When initializing the 'localStr' variable, object 'str' is explicitly cast to type 'String',

although it's not necessary since 'str' is already of type 'String'.

The fixed version would then look like this:

public void SomeMethod(String str)

{

String localStr = str;

....

}

Instead of explicitly specifying the 'localStr' object type, the programmer could have

kept the keyword 'var' here, but explicit type specification makes the program

clearer.

The following is a more interesting example:

public object FindName(string name, FrameworkElement templatedParent);

....

lineArrow = (Grid)Template.FindName("lineArrow", this) as Grid;

if (lineArrow != null);

....

Let's examine the line with casts closer to see what's happening:

1. Method 'FindName' returns an object of type 'object', which the programmer tries to explicitly cast to type 'Grid'.

2. If this cast fails, an 'InvalidCastException' will be raised.

3. If, on the contrary, the cast is successful, the object will be again cast to the same type, 'Grid', using the 'as' operator. Then the cast is guaranteed to be successful, and this cast is redundant.

4. As a result, if the cast fails, 'lineArrow' will never be assigned the value 'null'.

Page 840: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

As suggested by the next line, it is assumed that 'lineArrow' may refer to the 'null'

value, so it is exactly the 'as' operator that is supposed to be used. As explained

before, 'lineArrow' can't take the value 'null' if the cast fails. Therefore, it's not just a

redundant cast – it's an apparent error.

To solve this issue, we can remove the extra cast operation from the code:

lineArrow = Template.FindName("lineArrow", this) as Grid;

if (lineArrow != null);

V3052. The original exception object was swallowed. Stack of original exception could be lost.The analyzer detected that the original object of a caught exception was not used

properly when re-throwing from a catch block. This issue makes some errors hard

to detect since the stack of the original exception is lost.

Further we will discuss a couple of examples of incorrect code. The first example:

public Asn1Object ToAsn1Object()

{

try

{

return Foo(_constructed, _tagNumber);

}

catch (IOException e)

{

throw new ParsingException(e.Message);

}

}

In this code, the programmer wanted to transform the caught I/O exception into a

new exception of type ParsingException. However, only the message from the first

exception is included, so some of the information is lost.

Page 841: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The fixed version of the code:

public Asn1Object ToAsn1Object()

{

try

{

return Foo(_constructed, _tagNumber);

}

catch (IOException e)

{

throw new ParsingException(e.Message, e);

}

}

In the fixed version, the original exception is re-thrown as an inner one, so all the

information about the original error is saved.

Here's the second example:

private int ReadClearText(byte[] buffer, int offset, int count)

{

int pos = offset;

try

{

....

}

catch (IOException ioe)

{

if (pos == offset) throw ioe;

}

return pos - offset;

}

In this case, the caught I/O exception is thrown again, completely erasing the stack

of the original error. To avoid this defect, we just need to re-throw the original

exception.

The fixed version of the code:

Page 842: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

private int ReadClearText(byte[] buffer, int offset, int count)

{

int pos = offset;

try

{

....

}

catch (IOException ioe)

{

if (pos == offset) throw;

}

return pos - offset;

}

V3053. An excessive expression. Examine the substrings "abc" and "abcd".The analyzer detected a potential bug, connected with the fact that a longer and

shorter substrings are searched in the expression. With all that a shorter string is a

part of a longer one. As a result, one of the comparisons is redundant or there is a

bug here.

Consider the following example:

if (str.Contains("abc") || str.Contains("abcd"))

If substring "abc" is found, the check will not execute any further. If substring "abc"

is not found, then searching for longer substring "abcd" does not make sense either.

To fix this error, we need to make sure that the substrings were defined correctly or

delete extra checks, for example:

if (str.Contains("abc"))

Here's another example:

Page 843: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

if (str.Contains("abc"))

Foo1();

else if (str.Contains("abcd"))

Foo2();

In this code, function Foo2() will never be called. We can fix the error by reversing

the check order to make the program search for the longer substring first and then

search for the shorter one:

if (str.Contains("abcd"))

Foo2();

else if (str.Contains("abc"))

Foo1();

V3054. Potentially unsafe double-checked locking. Use volatile variable(s) or synchronization primitives to avoid this.The analyzer detected a possible error related to unsafe use of the "double-checked

locking" pattern. This software design pattern is used to reduce the overhead of

acquiring a lock by first testing the locking criterion without actually acquiring the

lock. Only if the locking criterion check indicates that locking is required, does the

actual locking logic proceed. That is, locking will be performed only if really

needed.

Consider the following example of unsafe implementation of this pattern in C#:

private MyClass _singleton = null;

public MyClass Singleton

{

get

{

if(_singleton == null)

Page 844: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

lock(_locker)

{

if(_singleton == null)

{

MyClass instance = new MyClass();

instance.Initialize();

_singleton = instance;

}

}

return _singleton;

}

}

In this example, the pattern is used to implement "lazy initialization" – that is,

initialization is delayed until a variable's value is needed for the first time. This code

will work correctly in a program that uses a singleton object from one thread. To

ensure safe initialization in a multithreaded program, a construct with the lock

statement is usually used. However, it's not enough in our example.

Note the call to method 'Initialize()' of the 'Instance' object. When building the

program in Release mode, the compiler may optimize this code and invert the order

of assigning the value to the '_singleton' variable and calling to the 'Initialize()'

method. In that case, another thread accessing 'Singleton' at the same time as the

initializing thread may get access to the object before initialization is over.

Here's another example of using the double-checked locking pattern:

private MyClass _singleton = null;

bool _initialized = false;

public MyClass Singleton;

{

get

{

if(!_initialized)

lock(_locker)

{

Page 845: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

if(!_initialized)

{

_singleton = new MyClass();

_initialized = true;

}

}

return _singleton;

}

}

Like in the previous example, compiler optimization of the order of assigning

values to variables '_singleton' and '_initialized' may cause errors. That is, the

'_initialized' variable will be assigned the value 'true' first, and only then will a new

object, MyClass(), be created and the reference to it be assigned to '_singleton'.

Such inversion may cause an error when accessing the object from a parallel thread.

It turns out that the '_singleton' variable will not be specified yet while the

'_initialized' flag will be already set to 'true'.

One of the dangers of these errors is the seeming correctness of the program's

functioning. Such false impression occurs because this problem won't occur very

often and will depend on the architecture of the processor used, CLR version, and

so on.

There are several ways to ensure thread-safety when using the pattern. The simplest

way is to mark the variable checked in the if condition with the 'volatile' keyword:

private volatile MyClass _singleton = null;

public MyClass Singleton

{

get

{

if(_singleton == null)

lock(_locker)

{

if(_singleton == null)

{

Page 846: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

MyClass instance = new MyClass();

instance.Initialize();

_singleton = instance;

}

}

return _singleton;

}

}

The volatile keyword will prevent the variable from being affected by possible

compiler optimizations related to swapping write/read instructions and caching its

value in processor registers.

For performance reasons, it's not always a good solution to declare a variable as

volatile. In that case, you can use the following methods to access the variable:

'Thread.VolatileRead', 'Thread.VolatileWrite', and 'Thread.MemoryBarrier'. These

methods will put barriers for reading/writing memory only where necessary.

Finally, you can implement "lazy initialization" using the Lazy<T> class, which

was designed specifically for this purpose and is available in .NET starting with

version 4.

V3055. Suspicious assignment inside the condition expression of 'if/while/for' operator.The analyzer detected an issue that has to do with using the assignment operator '='

with boolean operands inside the conditions of statements if/while/do while/for. It is

very likely that the '==' operator was meant to be used instead.

Consider the following example:

void foo(bool b1, bool b2)

{

if (b1 = b2)

Page 847: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

....

There is a typo in this code. It will result in changing the value of variable b1

instead of comparing variables b1 and b2. The fixed version of this code should

look like this:

if (b1 == b2)

If you want to do assignment inside an 'if' statement to save on code size, it is

recommended that you parenthesize the assignment statement: it is a common

programming technique described in books and recognized by different compilers

and code analyzers.

A condition with additional parentheses tells programmers and code analyzers that

there is no error:

if ((b1 = b2))

Furthermore, not only do additional parentheses make code easier to read, but they

also prevent mistakes related to operation precedence, as in the following example:

if ((a = b) || a == c)

{ }

Without parentheses, the part 'b || a == c' would be evaluated first, according to

operation precedence, and then the result of this expression would be assigned to

variable 'a'. This behavior may be different from what the programmer expected.

V3056. Consider reviewing the correctness of 'X' item's usage.The analyzer detected a possible typo in the code. This diagnostic relies on a

heuristic algorithm to detect errors of the following pattern:

int x = GetX() * n;

int y = GetX() * n;

Page 848: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

In the second line, function GetX() is used instead of GetY(). The fixed version:

int x = GetX() * n;

int y = GetY() * n;

To detect this error, the analyzer uses the following logic. There is a line with a

name containing fragment "X". Nearby is a line with an antipode name containing

fragment "Y". But the second line also contains the name with "X". If this and a few

other conditions are true, this construct is treated as dangerous and the analyzer

suggests reviewing it. If, for example, there were no variables "x" and "y" in the left

part, the warning wouldn't be triggered. Here is an example that the analyzer would

ignore:

array[0] = GetX() / 2;

array[1] = GetX() / 2;

Unfortunately, this diagnostic produces false positives since the analyzer doesn't

know the program structure and the purpose of the code. Consider, for example, the

following test code:

var t1 = new Thread { Name = "Thread 1" };

var t2 = new Thread { Name = "Thread 2" };

var m1 = new Message { Name = "Thread 1: Message 1", Thread = t1};

var m2 = new Message { Name = "Thread 1: Message 2", Thread = t1};

var m3 = new Message { Name = "Thread 2: Message 1", Thread = t2};

The analyzer assumes that variable 'm2' was declared using copy-paste and it led to

an error: variable 't1' is used instead of 't2'. But there is no error actually. As the

messages suggest, this code tests the printing of messages 'm1' and 'm2' from thread

't1' and of message 'm3' from thread 't2'. For cases like this, the analyzer allows you

to suppress the warning by adding the comment "//-V3056" or through other false-

positive suppression mechanisms.

V3057. Function receives an odd argument.

Page 849: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer detected a possible error that has to do with passing a suspicious value

as an argument to a function.

Consider the following examples:

Invalid characters in a path

string GetLogPath(string root)

{

return System.IO.Path.Combine(root, @"\my|folder\log.txt");

}

A path containing invalid character '|' is passed to function Combine(). It will result

in an ArgumentException.

The fixed version:

string GetLogPath(string root)

{

return System.IO.Path.Combine(root, @"\my\folder\log.txt");

}

Invalid index

var pos = mask.IndexOf('\0');

if (pos != 0)

asciiname = mask.Substring(0, pos);

IndexOf() returns the position of a specified argument. If the argument is not found,

the function returns the value '-1'. And passing a negative index to function

Substring() results in an ArgumentOutOfRangeException.

The fixed version:

var pos = mask.IndexOf('\0');

if (pos > 0)

asciiname = mask.Substring(0, pos);

Suspicious argument to format function

Page 850: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

string.Format(mask, 1, 2, mask);

The string.Format() function replaces one or more format items in a specified string.

An attempt to write the same string into the format string is treated as suspicious by

the analyzer.

V3058. An item with the same key has already been added.The analyzer detected an issue that has to do with adding values to a dictionary for a

key already present in this dictionary. It will cause raising an ArgumentException at

runtime with the message: "An item with the same key has already been added."

Consider the following example:

var mimeTypes = new Dictionary<string, string>();

mimeTypes.Add(".aif", "audio/aiff");

mimeTypes.Add(".aif", "audio/x-aiff");// ArgumentException

In this code, an ArgumentException will be raised when attempting to add a value

for the «.aif» key for the second time.

To make this code correct, we must avoid duplicates of keys when filling the

dictionary:

var mimeTypes = new Dictionary<string, string>();

mimeTypes.Add(".aif", "audio/aiff");

V3059. Consider adding '[Flags]' attribute to the enum.The analyzer detected a suspicious enumeration whose members participate in

bitwise operations or have values that are powers of 2. The enumeration itself,

however, is not marked with the [Flags] attribute.

Page 851: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

If one of these conditions is true, the [Flags] attribute must be set for the

enumeration if you want to use it as a bit flag: it will give you some advantages

when working with this enumeration.

For a better understanding of how using the [Flags] attribute with enumerations

changes the program behavior, let's discuss a couple of examples:

enum Suits { Spades = 1, Clubs = 2, Diamonds = 4, Hearts = 8 }

// en1: 5

var en1 = (Suits.Spades | Suits.Diamonds);

Without the [Flags] attribute, executing the OR bitwise operation over the members

with the values '1' and '4' will result in the value '5'.

It changes when [Flags] is specified:

[Flags]

enum SuitsFlags { Spades = 1, Clubs = 2, Diamonds = 4, Hearts = 8 }

// en2: SuitsFlags.Spades | SuitsFlags.Diamonds;

var en2 = (SuitsFlags.Spades | SuitsFlags.Diamonds);

In this case, the result of the OR operation is treated not as a single integer value,

but as a set of bits containing the values 'SuitsFlags.Spades' and

'SuitsFlags.Diamonds'.

If you call to method 'ToString' for objects 'en1' and 'en2', the results will be

different, too. This method attempts to convert numerical values to their character

equivalents, but the value '5' has no such equivalent. However, when the 'ToString'

method discovers that the enumeration is used with the [Flags] attribute, it treats the

numerical values as sets of bit flags. Therefore, calling to the 'ToString' method for

objects 'en1' and 'en2' will result in the following:

String str1 = en1.ToString(); // "5"

String str2 = en2.ToString(); // "SuitsFlags.Spades |

// SuitsFlags.Diamonds"

Page 852: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

In a similar way, numerical values are obtained from a string using static methods

'Parse' and 'TryParse' of class 'Enum'.

Another advantage of the [Flags] attribute is that it makes the debugging process

easier, too. The value of the 'en2' variable will be displayed as a set of named

constants, not as simply a number:

References:

1. What does the [Flags] Enum Attribute mean in C#?

2. CLR via C#. Jeffrey Richter. Chapter 15 - Enumerated Types and Bit Flags.

V3060. A value of variable is not modified. Consider inspecting the expression. It is possible that other value should be present instead of '0'.The analyzer detected a suspicious bitwise expression. This expression was meant

to change certain bits in a variable, but the value this variable refers to will actually

stay unchanged.

Consider the following example:

A &= ~(0 << Y);

A = A & ~(0 << Y);

The programmer wanted to clear a certain bit in the variable's value but made a

mistake and wrote 0 instead of 1.

Page 853: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Both expressions evaluate to the same result, so let's examine the second line as a

clearer example. Suppose we have the following values of the variables in bit

representation:

A = 0..0101

A = 0..0101 & ~(0..0000 << 0..00001)

Shifting the value 0 by one bit to the left won't change anything; we'll get the

following expression:

A = 0..0101 & ~0..0000

Then, the bitwise negation operation will be executed, resulting in the following

expression:

A = 0..0101 & 11111111

After executing the bitwise "AND" operation, the original and resulting expressions

will turn out to be the same:

A = 0..0101

The fixed version of the code should look like this:

A &= ~(1 << Y);

A = A & ~(1 << Y);

V3061. Parameter 'A' is always rewritten in method body before being used.The analyzer detected a possible error in a method's body. One of the method's

parameters is rewritten before being used; therefore, the value passed to the method

is simply lost.

This error can manifest itself in a number of ways. Consider the following example:

Page 854: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

void Foo1(Node A, Node B)

{

A = SkipParenthesize(A);

B = SkipParenthesize(A);

// do smt...

}

There is a typo here that will result in the 'B' object being assigned an incorrect

value. The fixed code should look like this:

void Foo1(Node A, Node B)

{

A = SkipParenthesize(A);

B = SkipParenthesize(B);

// do smt...

}

However, this bug can take trickier forms:

void Foo2(List<Int32> list, Int32 count)

{

list = new List<Int32>(count);

for (Int32 i = 0; i < count; ++i)

list.Add(GetElem(i));

}

This method was meant to initialize a list with some values. But what actually takes

place is copying of the reference ('list'), which stores the address of the memory

block in the heap where the list (or 'null' if memory wasn't allocated) is stored.

Therefore, when we allocate memory for the list once again, the memory block's

address is written into a local copy of the reference while the original reference

(outside the method) remains unchanged. It results in additional work on memory

allocation, list initialization, and subsequent garbage collection.

The error has to do with a missing 'out' modifier. This is the fixed version of the

code:

Page 855: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

void Foo2(out List<Int32> list, Int32 count)

{

list = new List<Int32>(count);

for (Int32 i = 0; i < count; ++i)

list.Add(GetElem(i));

}

V3062. An object is used as an argument to its own method. Consider checking the first actual argument of the 'Foo' methodThe analyzer detected a method call of the following pattern:

A.Foo(A);

This suspicious code is very likely to contain an error – for example a typo that

results in using a wrong variable name. The fixed version of this code should look

like this:

A.Foo(B);

or this:

B.Foo(A);

And here's an example from a real application:

private bool CanRenameAttributePrefix(....)

{

....

var nameWithoutAttribute =

this.RenameSymbol.Name.GetWithoutAttributeSuffix(isCaseSensitive:

true);

var triggerText = GetSpanText(document,

Page 856: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

triggerSpan,

cancellationToken);

// nameWithoutAttribute, triggerText - String

return triggerText.StartsWith(triggerText);

}

The return value in this code will always be the value 'true' because the method that

checks whether a string starts with a substring receives, as its argument, the string

itself ('triggerText'). The programmer must have meant the following check instead:

return triggerText.StartsWith(nameWithoutAttribute);

V3063. A part of conditional expression is always true/false if it is evaluated.The analyzer detected a possible error inside a logical condition a part of which is

always true/false and is considered to be suspicious.

Consider the following example:

uint i = length;

while ((i >= 0) && (n[i] == 0)) i--;

The "i >= 0" condition is always true because the 'i' variable is of type uint, so if 'i'

reaches zero, the while loop won't stop and 'i' will take the maximum value of type

uint. An attempt of further access to the 'n' array will result in raising an

OverflowException.

The fixed code:

int i = length;

while ((i >= 0) && (n[i] == 0)) i--;

Here's another example:

Page 857: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

public static double Cos(double d)

{

// -9223372036854775295 <= d <= 9223372036854775295

Contract.Ensures(

!(-9223372036854775295 <= d || d <= 9223372036854775295) ||

Contract.Result<double>() >= -1.0);

The programmer wanted to make sure that the d variable belongs to the specified

range (it is stated in the comment before the check) but made a typo and wrote the

'||' operator instead of '&&'. The fixed code:

Contract.Ensures(

!(-9223372036854775295 <= d && d <= 9223372036854775295) ||

Contract.Result<double>() >= -1.0);

Sometimes the V3063 warning detects simply redundant code rather than an error.

For example:

if (@char < 0x20 || @char > 0x7e) {

if (@char > 0x7e

|| (@char >= 0x01 && @char <= 0x08)

|| (@char >= 0x0e && @char <= 0x1f)

|| @char == 0x27

|| @char == 0x2d)

The analyzer will warn us that the subexpressions @char == 0x27 and @char ==

0x2d are always false because of the preceding if statement. This code may work

quite well, but it is redundant and we'd better simplify it. It will make the program

easier to read for other developers.

This is the simplified version of the code:

if (@char < 0x20 || @char > 0x7e) {

if (@char > 0x7e

|| (@char >= 0x01 && @char <= 0x08)

|| (@char >= 0x0e && @char <= 0x1f))

Page 858: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V3064. Division or mod division by zero.The analyzer detected a potential division by zero.

Consider the following example:

if (maxHeight >= 0)

{

fx = height / maxHeight;

}

It is checked in the condition if the value of the maxHeight variable is non-negative.

If this value equals 0, a division by zero will occur inside the if statement's body. To

fix this issue, we must ensure that the division operation is executed only when

maxHeight refers to a positive number.

The fixed version of the code:

if (maxHeight > 0)

{

fx = height / maxHeight;

}

V3065. Parameter is not utilized inside method's body.The analyzer detected a suspicious situation when one parameter of a method is

never used while another parameter is used several times. It may be a sign of an

error. Consider the following example:

private static bool CardHasLock(int width, int height)

{

const double xScale = 0.051;

const double yScale = 0.0278;

Page 859: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

int lockWidth = (int)Math.Round(height * xScale);

int lockHeight = (int)Math.Round(height * yScale);

....

}

The 'width' parameter is never used in the method body while the 'height' parameter

is used twice, including the initialization of the 'lockWidth' variable. This code is

very likely to contain an error and the 'lockWidth' variable should be actually

initialized in the following way:

int lockWidth = (int)Math.Round(width * xScale);

V3066. Possible incorrect order of arguments passed to method.The analyzer detected a suspicious sequence of arguments passed to a method.

Perhaps, some arguments are misplaced.

An example of suspicious code:

void SetARGB(byte a, byte r, byte g, byte b)

{ .... }

void Foo(){

byte A = 0, R = 0, G = 0, B = 0;

....

SetARGB(A, R, B, G);

....

}

When defining the object color, the programmer accidentally swapped the blue and

green color parameters.

The fixed version of the code should look like this:

SetARGB(A, R, G, B);

Page 860: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Here's an example from a real project:

public virtual string Qualify(string catalog,

string schema,

string table)

{ .... }

public Table AddDenormalizedTable(....) {

string key = subselect ??

dialect.Qualify(schema, catalog, name);

....

}

As logic suggests, the code should actually look like this:

public Table AddDenormalizedTable(....) {

string key = subselect ??

dialect.Qualify(catalog, schema, name);

....

}

V3067. It is possible that 'else' block was forgotten or commented out, thus altering the program's operation logics.The analyzer has detected a suspicious code fragment which may be a forgotten or

incorrectly commented else block.

This issue is best explained on examples.

if (!x)

t = x;

else

z = t;

Page 861: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

In this case, code formatting doesn't meet its logic: the z = t expression will execute

only if (x == 0), which is hardly what the programmer wanted. A similar situation

may occur when a code fragment is not commented properly:

if (!x)

t = x;

else

//t = -1;

z = t;

In this case, we either need to fix the formatting by turning it into something more

readable or fix the logic error by adding a missing branch of the if operator.

Sometimes there are cases when it's hard to say whether this code is incorrect or if it

is peculiar code formatting. The analyzer tries to decrease the number of false

positives related to code formatting, by not issuing warnings when the code is

formatted with both spaces and tabs; with that the number of tabs is various in

different strings.

V3068. Calling overrideable class member from constructor is dangerous.The analyzer detected a potential error inside a class constructor - invoking an

overridable method (virtual or abstract). The following example shows how such

call can lead to an error:

abstract class Base

{

protected Base()

{

Initialize();

}

protected virtual void Initialize()

Page 862: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

{

...

}

}

class Derived : Base

{

Logger _logger;

public Derived(Logger logger)

{

_logger = logger;

}

protected override void Initialize()

{

_logger.Log("Initializing");

base.Initialize();

}

}

In this code, the constructor of abstract class Base contains a call to virtual

method Initialize. In the Derived class, which is derived from the Base class, we

override the Initialize method and utilize the _logger field in this overridden

method. The _logger field itself is initialized in the Derived class's constructor.

However, when creating an instance of the Derived class, the constructor of the less

derived type in the inheritance sequence will be executed first (Base class in our

case). But when calling to the Initialize method from Base's constructor, we'll be

executing the Initialize method of the object created at runtime, i.e.

the Derived class. Note that when executing the Initialize method, the _logger field

will not be initialized yet, so creating an instance of the Derived class in our

example will cause a NullReferenceException.

Therefore, invoking overridable methods in a constructor may result in executing

methods of an object whose initialization is not complete yet.

Page 863: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

To fix the analyzer warning, either mark the method you are calling (or the class

that contains it) as sealed or remove the virtual keyword from its definition.

If you do want the program to behave as described above when initializing an object

and you want to hide the analyzer's warning, mark the message as a false positive.

For details about warning-suppression methods, see the documentation.

V3069. It's possible that the line was commented out improperly, thus altering the program's operation logics.The analyzer detected a possible error that has to do with two 'if' statements

following in series and separated by a commented-out line that is very likely to

contain meaningful code. The programmer's inattention has resulted in a significant

change in the program's execution logic. Consider the following example:

if(!condition)

//condition = GetCondition();

if(condition)

{

...

}

The program has become meaningless; the condition of the second 'if' statement

never executes. The fixed version should look like this:

//if(!condition)

//condition = GetCondition();

if(condition)

{

...

}

Page 864: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V3070. Uninitialized variables are used when initializing the 'A' variable.The analyzer detected a possible error that has to do with initializing a class

member to a value different from the one the programmer expected. Consider the

following example:

class AClass {

static int A = B + 1;

static int B = 10;

}

In this code, the 'A' field will be initialized to the value '1', not '11', as the

programmer may have expected. The reason is that the 'B' field will be referring to

'0' when the 'A' field will be initialized. It has to do with the fact that all the

members of a type (class or structure) are initialized to default values at first ('0' for

numeric types, 'false' for the Boolean type, and 'null' for reference types). And only

then will they be initialized to the values defined by the programmer. To solve this

issue, we need to change the order in which the fields are processed:

class AClass {

static int B = 10;

static int A = B + 1;

}

This way, the 'B' field will be referring to the value '10' when the 'A' field will be

initialized, as intended.

V3071. The object is returned from inside 'using' block. 'Dispose' will be invoked before exiting method.

Page 865: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer detected that a function returns an object that is being used in the

'using' statement.

Consider the following example:

public FileStream Foo(string path)

{

using (FileStream fs = File.Open(path, FileMode.Open))

{

return fs;

}

}

Since the variable was initialized in the using block, method Dispose will be called

for this variable before exiting the function. Therefore, it may not be safe to use the

object that will be returned by the function.

The Dispose method will be called because the code above will be modified by the

compiler into the following code:

public FileStream Foo(string path)

{

FileStream fs = File.Open(path, FileMode.Open)

try

{

return fs;

}

finally

{

if (fs != null)

((IDisposable)fs).Dispose();

}

}

The fixed version may look something like this:

public FileStream Foo(string path)

{

Page 866: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

return File.Open(path, FileMode.Open)

}

V3072. The 'A' class containing IDisposable members does not itself implement IDisposable.The analyzer detected that a class, which does not implement 'IDisposable'

interface, contains fields or properties of a type that does implement 'IDisposable'.

Such code indicates that a programmer probably forgot to release resources after

using an object of their class.

Consider the following example:

class Logger

{

FileStream fs;

public Logger() {

fs = File.OpenWrite("....");

}

}

In this code, the wrapper class, which allows writing the log to a file, does not

implement 'IDisposable' interface. At the same time, it contains a variable of type

'FileStream', which enables writing to a file. In this case, the 'fs' variable will be

holding the file until the Finalize method of the 'fs' object is called (it will happen

when the object is being cleared by the garbage collector). As a result, we get an

access error, behaving like a heisenbug and occurring, for example, when

attempting to open the same file from a different stream.

This issue can be fixed in a number of ways. The most correct one is as follows:

class Logger : IDisposable

{

FileStream fs;

Page 867: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

public Logger() {

fs = File.OpenWrite("....");

}

public void Dispose() {

fs.Dispose();

}

}

However, the program logic does not always allow you implement 'IDisposable' in

the 'Logger' class. The analyzer checks many scenarios and reduces the number of

false positives. In our code above, for example, we can simply close 'FileStream',

which writes to a file, from a separate function:

class Logger

{

FileStream fs;

public Logger() {

fs = File.OpenWrite("....");

}

public void Close() {

fs.Close();

}

}

V3073. Not all IDisposable members are properly disposed. Call 'Dispose' when disposing 'A' class.The analyzer detected a possible error in a class implementing the 'IDisposable'

interface. The 'Dispose' method is not called in the 'Dispose' method of the class on

some of the fields whose type implements the 'IDisposable' interface. It is very

likely that the programmer forgot to free some resources after use.

Consider the following example:

Page 868: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

class Logger : IDisposable

{

FileStream fs;

public Logger() {

fs = File.OpenWrite("....");

}

public void Dispose() { }

}

This code uses a wrapper class, 'Logger', implementing the 'IDisposable' interface,

which allows writing to a log file. This class, in its turn, contains variable 'fs', which

is used to perform the writing. Since the programmer forgot to call method 'Dispose'

or 'Close' in the 'Dispose' method of the 'Logger' class, the following error may

occur.

Suppose an object of the 'Logger' class was created in the 'using' block:

using(Logger logger = new Logger()){

....

}

As a result, method 'Dispose' will be called on the 'logger' object before leaving the

'using' block.

Such use implies that all the resources used by the object of class 'Logger' have

been freed and you can use them again.

In our case, however, the 'fs' stream, writing to a file, won't be closed; and when

trying to access this file again from another stream, for example, an access error

may occur.

It is a heisenbug because the 'fs' object will free the opened file as this object is

being cleared by the garbage collector. However, clearing of this object is a non-

deterministic event; it's not guaranteed to take place after the 'logger' object leaves

the 'using' block. A file access error occurs if the file is opened before the garbage

collector has cleared the 'fs' object.

Page 869: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

To solve this issue, we just need to call 'fs.Dispose()' in the 'Dispose' method of the

'Logger' class:

class Logger : IDisposable

{

FileStream fs;

public Logger() {

fs = File.OpenWrite("....");

}

public void Dispose() {

fs.Dispose();

}

}

This solution guarantees that the file opened by the 'fs' object will be freed by the

moment of leaving the 'using' block.

V3074. The 'A' class contains 'Dispose' method. Consider making it implement 'IDisposable' interface.

Scenario one

Scenario two

The analyzer detected a method named 'Dispose' in a class that does not implement

the 'IDisposable' interface. The code may behave in two different ways in the case

of this error.

Scenario one

The most common situation deals with mere non-compliance with the Microsoft

coding conventions, which specify that method 'Dispose' is an implementation of

Page 870: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

the standard 'IDisposable' interface and is used for deterministic disposal of

resources, including unmanaged resources.

Consider the following example:

class Logger

{

....

public void Dispose()

{

....

}

}

By convention, method 'Dispose' is used for resource freeing, and its presence

implies that the class itself implements the 'IDisposable' interface. There are two

ways to solve this issue.

1) Add an implementation of the 'IDisposable' interface to the class declaration:

class Logger : IDisposable

{

....

public void Dispose()

{

....

}

}

This solution allows using objects of class 'Logger' in the 'using' block, which

guarantees to call the 'Dispose' method when leaving the block.

using(Logger logger = new Logger()){

....

}

2) Choose a neutral name for your method, for example 'Close':

Page 871: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

class Logger

{

....

public void Close()

{

....

}

}

Scenario two

The second scenario when this warning is triggered implies a potential threat of

incorrect method call when the class is cast to the 'IDisposable' interface.

Consider the following example:

class A : IDisposable

{

public void Dispose()

{

Console.WriteLine("Dispose A");

}

}

class B : A

{

public new void Dispose()

{

Console.WriteLine("Dispose B");

}

}

If an object of class 'B' is cast to the 'IDisposable' interface or is used in the 'using'

block, as, for example, in the following code:

using(B b = new B()){

....

}

Page 872: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

then the 'Dispose' method will be called from class 'A'. That is, the 'B' class'

resources won't be released.

To ensure that the method is correctly called from class 'B', we need to additionally

implement the 'IDisposable' interface in it: then the 'Dispose' method will be called

exactly from the 'B' class when its object is cast to the 'IDisposable' interface or

used it in the 'using' block.

Fixed code:

class B : A, IDisposable

{

public new void Dispose()

{

Console.WriteLine("Dispose B");

base.Dispose();

}

}

V3075. The operation is executed 2 or more times in succession.The analyzer detected a possible error that has to do with executing operation '!', '~',

'-', or '+' two or more times in succession. This error may be caused by a typo. The

resulting expression makes no sense and may lead to incorrect behavior.

Consider the following example:

if (!(( !filter )))

{

....

}

This error most likely appeared during code refactoring. For example, a part of a

complex logical expression was removed while the negation of the whole result

wasn't. As a result, we've got an expression with an opposite meaning.

Page 873: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The fixed version of the code may look like this:

if ( filter )

{

....

}

or this:

if ( !filter )

{

....

}

V3076. Comparison with 'double.NaN' is meaningless. Use 'double.IsNaN()' method instead.The analyzer detected that a variable of type float or double is compared with a

float.NaN or double.NaN value. As stated in the documentation, if two double.NaN

values are tested for equality by using the == operator, the result is false. So, no

matter what value of type double is compared with double.NaN, the result is always

false.

Consider the following example:

void Func(double d) {

if (d == double.NaN) {

....

}

}

It's incorrect to test the value for NaN using operators == and !=. Instead, method

float.IsNaN() or double.IsNaN() should be used. The fixed version of the code:

void Func(double d) {

Page 874: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

if (double.IsNaN(d)) {

....

}

}

V3077. Property setter / event accessor does not utilize its 'value' parameter.The analyzer detected a possible error that deals with property and event accessors

not using their 'value' parameter.

Consider the following example:

private bool _visible;

public bool IsVisible

{

get { return _visible; }

set { _visible = true; }

}

When setting a new value for the "IsVisible" property, the programmer intended to

save the result into the "_visible" variable but made a mistake. As a result, changing

the property won't affect the object state in any way.

This is the fixed version:

public bool IsVisible

{

get { return _visible; }

set { _visible = value; }

}

Code of the following pattern will also trigger the warning:

public bool Unsafe {

Page 875: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

get { return (flags & Flags.Unsafe) != 0; }

set { flags |= Flags.Unsafe; }

}

In this case, the 'set' method is used to change the flag state and there's no error.

However, using a property like that may be misleading, as the assignments

"myobj.Unsafe = true" and "myobj.Unsafe = false" will have the same result.

To reset the state of the internal variable, it is better to use a function rather than a

property:

public bool Unsafe

{

get { return (flags & Flags.Unsafe) != 0; }

}

public void SetUnsafe()

{

flags |= Flags.Unsafe;

}

If you can't do without the property, mark this line with special comment "//-

V3077" to tell the analyzer not to output the warning on this property in future:

public bool Unsafe {

get { return (flags & Flags.Unsafe) != 0; }

set { flags |= Flags.Unsafe; } //-V3077

}

For a complete overview of all false-positive suppression mechanisms, see

the documentation.

V3078. Original sorting order will be lost after repetitive call to 'OrderBy'

Page 876: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

method. Use 'ThenBy' method to preserve the original sorting.The analyzer detected a possible error that has to do with using method 'OrderBy' or

'OrderByDescending' for a collection of type 'IOrderedEnumerable'. It may be

related to the fact that this method was called twice while the second call should

actually have been done to method 'ThenBy' or 'ThenByDescending'. The problem

about this issue is that calling 'OrderBy...' for the second time won't take into

account the first sort order of the collection, unlike 'ThenBy...'.

Consider the following example:

var seq = points.OrderBy(item => item.Line)

.OrderBy(item => item.Column);

In this code, a sequence is first sorted by lines, and then the resulting collection is

re-sorted by columns. The second sort ignores the result of the first sort.

To keep the previous sort result, one must call the 'ThenBy' method for the second

sort.

var seq = points.OrderBy(item => item.Line)

.ThenBy(item => item.Column);

A similar mistake can be made when using query syntax:

var seq = from item in points

orderby item.Line

orderby item.Column

select item;

The second sort will lead to losing the first result, too. The code can be fixed as

follows:

var seq = from item in points

orderby item.Line, item.Column

Page 877: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

select item;

V3079. 'ThreadStatic' attribute is applied to a non-static 'A' field and will be ignored.The analyzer detected a suspicious declaration of a non-static field, to which the

'ThreadStatic' attribute is applied.

Using this attribute with a static field allows one to set an individual value for this

field for each thread. Besides, it prohibits simultaneous access to this field by

different threads, thus eliminating the possibility of mutual exclusion when

addressing the field. However, this attribute is ignored when used with a non-static

field.

Consider the following example:

[ThreadStatic]

bool m_knownThread;

This field looks like a flag that must have individual values for each thread. But

since the field is not static, applying the 'ThreadStatic' attribute to it does not make

sense. If the program's logic does imply that the field must have a unique value for

each thread (as suggested by its name and the presence of the 'ThreadStatic'

attribute), there is probably an error in this code.

To fix the error, we need to add the 'static' modifier to the field declaration:

[ThreadStatic]

static bool m_knownThread;

References:

MSDN - ThreadStaticAttribute Class.

Page 878: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V3080. Possible null dereference.The analyzer detected a code fragment that may cause a null-dereference issue.

Consider the following examples, which trigger the V3080 diagnostic message:

if (obj != null || obj.Func()) { ... }

if (obj == null && obj.Func()) { ... }

if (list == null && list[3].Func()) { ... }

All the conditions contain a logical mistake that results in null dereference. This

mistake appears as the result of bad code refactoring or a typo.

The following are the fixed versions of the samples above:

if (obj == null || obj.Func()) { .... }

if (obj != null && obj.Func()) { .... }

if (list != null && list[3].Func()) { .... }

These are very simple situations, of course. In real-life code, an object may be

tested for null and used in different lines. If you see the V3080 warning, examine

the code above the line that triggered it and try to find out why the reference is null.

Here's an example where an object is checked and used in different lines:

if (player == null) {

....

var identity = CreateNewIdentity(player.DisplayName);

....

}

The analyzer will warn you about the issue in the line inside the 'if' block. There is

either an incorrect condition or some other variable should have been used instead

of 'player'.

Sometimes programmers forget that when testing two objects for null, one of them

may appear null and the other non-null. It will result in evaluating the entire

condition, and null dereference. For example:

Page 879: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

if ((text == null && newText == null) || text.Equals(newText)) {

....

}

This condition can be rewritten in the following way:

if ((text == null && newText == null) ||

(text != null && newText != null && text.Equals(newText))) {

....

}

Another way to make this mistake is to use the logical AND operator (&) instead of

conditional AND (&&). One must remember that, firstly, both parts of the

expression are always evaluated when using logical AND, and, secondly, the

priority of logical AND is higher than that of conditional AND.

For example:

public static bool HasCookies {

get {

var context = HttpContext;

return context != null

&& context.Request != null & context.Request.Cookies != null

&& context.Response != null && context.Response.Cookies != null;

}

}

In this code, 'context.Request.Cookies' will be referenced even if 'context.Request'

is null.

V3081. The 'X' counter is not used inside a nested loop. Consider inspecting usage of 'Y' counter.The analyzer detected a possible error in two or more nested 'for' loops, when the

counter of one of the loops is not used because of a typo.

Page 880: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Consider the following synthetic example of incorrect code:

for (int i = 0; i < N; i++)

for (int j = 0; j < M; j++)

sum += matrix[i, i];

The programmer wanted to process all the elements of a matrix and find their sum

but made a mistake and wrote variable 'i' instead of 'j' when indexing into the

matrix.

Fixed version:

for (int i = 0; i < N; i++)

for (int j = 0; j < M; j++)

sum += matrix[i, j];

Unlike diagnostics V3014, V3015, and V3016, this one deals with indexing errors

only in loop bodies.

V3082. The 'Thread' object is created but is not started. It is possible that a call to 'Start' method is missing.The analyzer detected a suspicious code fragment where an object of type 'Thread'

is created but the new thread is not started. Consider the following example:

void Foo(ThreadStart action)

{

Thread thread = new Thread(action);

thread.Name = "My Thread";

}

In this code, an object of type 'Thread' is created, and the reference to it is written

into the 'thread' variable. However, the thread itself is not started or passed

Page 881: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

anywhere. Therefore, the created object, which will not have been used in any way,

will simply be erased the next time garbage collection occurs.

To fix this error, we need to call the object's 'Start' method, which will start the

thread. The fixed code should look something like this:

void Foo(ThreadStart action)

{

Thread thread = new Thread(action);

thread.Name = "My Thread";

thread.Start();

}

V3083. Unsafe invocation of event, NullReferenceException is possible. Consider assigning event to a local variable before invoking it.The analyzer detected a potentially unsafe call to an event handler that may result in

NullReferenceException. Consider the following example:

public event EventHandler MyEvent;

void OnMyEvent(EventArgs e)

{

if (MyEvent != null)

MyEvent(this, e);

}

In this code, the 'MyEvent' field is tested for null, and then the corresponding event

is invoked. The null check helps to prevent an exception if there are no event

subscribers at the moment when the event is invoked (in this case, MyEvent will be

null).

Suppose, however, there is one subscriber to the 'MyEvent' event. Then, at the

moment between the null check and the call to the event handler by the 'MyEvent()'

Page 882: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

invocation, the subscriber may unsubscribe from the event - for example on a

different thread:

MyEvent -= OnMyEventHandler;

Now, if the 'OnMyEventHandler' handler was the only subscriber to 'MyEvent'

event, the 'MyEvent' field will have a null value, but because in our hypothetical

example the null check has already executed on another thread where the event is to

be invoked, the line 'MyEvent()' will be executed. This situation will cause a

NullReferenceException.

Therefore, a null check alone is not enough to ensure safe event invocation. There

are many ways to avoid the potential error described above. Let's see what these

ways are.

The first solution is to create a temporary local variable to store a reference to event

handlers of our event:

public event EventHandler MyEvent;

void OnMyEvent(EventArgs e)

{

EventHandler handler = MyEvent;

if (handler != null)

handler(this, e);

}

This solution will allow calling event handlers without raising the exception. Even

if the event subscriber gets unsubscribed at the point between testing 'handler' for

null and invoking it, as in our first example, the 'handler' variable will still be

storing the reference to the original handler, and this handler will be invoked

correctly despite the fact that the 'MyEvent' event no longer contains this handler.

Another way to avoid the error is to assign an empty handler, with an anonymous

method or lambda expression, to the event field at its initialization:

public event EventHandler MyEvent = (sender, args) => {};

Page 883: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

This solution guarantees that the 'MyEvent' field will never have a null value, as

such anonymous method cannot be unsubscribed (unless it's stored in a separate

variable, of course). It also enables us to do without a null check before invoking

the event.

Finally, starting with C# version 6.0 (Visual Studio 2015), you can use the '?.'

operator to ensure safe event invocation:

MyEvent?.Invoke(this, e);

V3084. Anonymous function is used to unsubscribe from event. No handlers will be unsubscribed, as a separate delegate instance is created for each anonymous function declaration.The analyzer detected a possible error that has to do with using anonymous

functions to unsubscribe from an event. Consider the following example:

public event EventHandler MyEvent;

void Subscribe()

{

MyEvent += (sender, e) => HandleMyEvent(e);

}

void UnSubscribe()

{

MyEvent -= (sender, e) => HandleMyEvent(e);

}

In this example, methods 'Subscribe' and 'UnSubscribe' are declared respectively for

subscribing to and unsubscribing from the 'MyEvent' event. A lambda expression is

Page 884: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

used as an event handler. Subscription to the event will be successfully fulfilled in

the 'Subscribe' method, and the handler (the anonymous function) will be added to

the event.

However, the 'UnSubscribe' method will fail to unsubscribe the handler previously

subscribed in the 'Subscribe' method. After executing this method, the 'MyEvent'

event will still be containing the handler added in 'Subscribe'.

This behavior is explained by the fact that every declaration of an anonymous

function results in creating a separate delegate instance – of type EventHandler in

our case. So, what is subscribed in the 'Subscribe' method is 'delegate 1' while

'delegate 2' gets unsubscribed in the 'Unsubscribe' method, despite these two

delegates having identical bodies. Since our event contains only 'delegate 1' by the

time the handler is unsubscribed, unsubscribing from 'delegate 2' will not affect the

value of 'MyEvent'.

To correctly subscribe to events using anonymous functions (when subsequent

unsubscription is required), you can keep the lambda handler in a separate variable,

using it both to subscribe to and unsubscribe from an event:

public event EventHandler MyEvent;

EventHandler _handler;

void Subscribe()

{

_handler = (sender, e) => HandleMyEvent(sender, e);

MyEvent += _handler;

}

void UnSubscribe()

{

MyEvent -= _handler;

}

V3085. The name of 'X' field/property in a nested type is

Page 885: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

ambiguous. The outer type contains static field/property with identical name.The analyzer detected that a nested class contains a field or property with the same

name as a static/constant field or property in the outer class.

Consider the following example:

class Outside

{

public static int index;

public class Inside

{

public int index; // <= Field with the same name

public void Foo()

{

index = 10;

}

}

}

A construct like that may result in incorrect program behavior. The following

scenario is the most dangerous. Suppose that there was no 'index' field in the 'Inside'

class at first. It means that it was the static variable 'index' in the 'Outside' class that

the 'Foo' function used to change. Now that we have added the 'index' field to the

'Inside' class and the name of the outer class is not specified explicitly, the 'Foo'

function will be changing the 'index' field in the nested class. The code, naturally,

will start working differently from what the programmer expected, although it

won’t trigger any compiler warnings.

The error can be fixed by renaming the variable:

class Outside

{

Page 886: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

public static int index;

public class Inside

{

public int insideIndex;

public void Foo()

{

index = 10;

}

}

}

V3086. Variables are initialized through the call to the same function. It's probably an error or un-optimized code.The analyzer detected a possible error that deals with two different variables being

initialized by the same expression. Not all of such expressions are treated as unsafe

but only those where function calls are used (or too long expressions).

Here is the simplest case:

x = X();

y = X();

Three scenarios are possible:

1. The code contains an error, which should be fixed by replacing 'X()' with 'Y()'.

2. The code is correct but works slowly. If the 'X()' function is required to perform multiple calculations, a better way is to write 'y = x;'.

3. The code is correct and works with proper speed, or the 'X()' function reads the value from a file. To suppress the false positive in this case, use the comment "//-V3086".

Now consider the following example from real code:

Page 887: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

string frameworkPath =

Path.Combine(tmpRootDirectory, frameworkPathPattern);

string manifestFile =

Path.Combine(frameworkPath, "sdkManifest.xml");

string frameworkPath2 =

Path.Combine(tmpRootDirectory, frameworkPathPattern2);

string manifestFile2 =

Path.Combine(frameworkPath, "sdkManifest.xml");

There is a copy-paste error in this code, which is not easy to notice at first. Actually,

it deals with mistakenly passing the first part of the path to the 'Path.Combine'

function when receiving the 'manifestFile2' string. The code logic suggests that

variable 'frameworkPath2' should be used instead of the originally used

'frameworkPath' variable.

The fixed code should look like this:

string manifestFile2 =

Path.Combine(frameworkPath2, "sdkManifest.xml");

V3087. Type of variable enumerated in 'foreach' is not guaranteed to be castable to the type of collection's elements.The analyzer detected a possible error in a 'foreach' loop. It is very likely that an

InvalidCastException will be raised when iterating through the 'IEnumarable<T>'

collection.

Consider the following example:

List<object> numbers = new List<object>();

....

Page 888: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

numbers.Add(1.0);

....

foreach (int a in numbers)

Console.WriteLine(a);

In this code, the 'numbers' collection's template is initialized to type 'object', which

allows adding objects of any type to it.

It is defined in the loop iterating through this collection that the iterated collection

members must be of type 'int'. If an object of another type is found in the collection,

it will be cast to the required type, which operation may result in

'InvalidCastException'. In our example, the exception occurs because the value of

type 'double', boxed in a collection element of type 'object', cannot be unboxed to

type 'int'.

To fix this error, we can cast the collection-template type and the element type in

the 'foreach' loop to a single type:

Solution 1:

List<object> numbers = new List<object>();

....

foreach (object a in numbers)

Solution 2:

List<int> numbers = new List<int>();

....

foreach (int a in numbers)

This error can often be observed when working with a collection of base-interface

elements while the programmer specifies in the loop the type of one of the

interfaces or classes implementing this base interface:

void Foo1(List<ITrigger> triggers){

....

foreach (IOperableTrigger trigger in triggers)

....

Page 889: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

}

void Foo2(List<ITrigger> triggers){

....

foreach (IMutableTrigger trigger in triggers)

....

}

To iterate through the objects of only one particular type in a collection, you can

filter them in advance using the 'OfType' function:

void Foo1(List<ITrigger> triggers){

....

foreach (IOperableTrigger trigger in

triggers.OfType<IOperableTrigger>())

....

}

void Foo2(List<ITrigger> triggers){

....

foreach (IMutableTrigger trigger in

triggers.OfType<IMutableTrigger>())

....

}

This solution guarantees that the 'foreach' loop will iterate only through objects of

proper type, making 'InvalidCastException' impossible.

V3088. The expression was enclosed by parentheses twice: ((expression)). One pair of parentheses is unnecessary or misprint is present.The analyzer detected an expression enclosed in double parentheses. It is very likely

that one of the parentheses is misplaced.

Page 890: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Note that the analyzer does not simply look for code fragments with double

parentheses; it looks for those cases when placing one of them differently can

change the meaning of the whole expression. Consider the following example:

if((!isLowLevel|| isTopLevel))

This code looks suspicious: there is no apparent reason for using additional

parentheses here. Perhaps the expression was actually meant to look like this:

if(!(isLowLevel||isTopLevel))

Even if the code is correct, it is better to remove the extra pair of parentheses. There

are two reasons:

1. Those programmers who will be reading the code may be confused by the double parentheses and doubt its correctness.

2. Removing the extra parentheses will make the analyzer stop reporting the false positive.

V3089. Initializer of a field marked by [ThreadStatic] attribute will be called once on the first accessing thread. The field will have default value on different threads.The analyzer detected a suspicious code fragment where a field marked with the

'[ThreadStatic]' attribute is initialized at declaration or in a static constructor.

If the field is initialized at declaration, it will be initialized to this value only in the

first accessing thread. In every next thread, the field will be set to the default value.

A similar situation is observed when initializing the field in a static constructor: the

constructor executes only once, and the field will be initialized only in the thread

where the static constructor executes.

Page 891: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Consider the following example, which deals with field initialization at declaration:

class SomeClass

{

[ThreadStatic]

public static Int32 field = 42;

}

class EntryPoint

{

static void Main(string[] args)

{

new Task(() => { var a = SomeClass.field; }).Start(); // a == 42

new Task(() => { var a = SomeClass.field; }).Start(); // a == 0

new Task(() => { var a = SomeClass.field; }).Start(); // a == 0

}

}

When the first thread accesses the 'field' field, the latter will be initialized to the

value specified by the programmer. That is, the 'a' variable, as well as the 'field'

field, will be set to the value '42'.

From that moment on, as new threads start and access the field, it will be initialized

to the default value ('0' in this case), so the 'a' variable will be set to '0' in all the

subsequent threads.

As mentioned earlier, initializing the field in a static constructor does not solve the

problem, as the constructor will be called only once (when initializing the type), so

the problem remains.

It can be dealt with by wrapping the field in a property with additional field

initialization logic. It helps solve the problem, but only partially: when the field is

accessed instead of the property (for example inside a class), there is still a risk of

getting an incorrect value.

class SomeClass

{

Page 892: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

[ThreadStatic]

private static Int32 field = 42;

public static Int32 Prop

{

get

{

if (field == default(Int32))

field = 42;

return field;

}

set

{

field = value;

}

}

}

class EntryPoint

{

static void Main(string[] args)

{

new Task(() => { var a = SomeClass.Prop; }).Start(); // a == 42

new Task(() => { var a = SomeClass.Prop; }).Start(); // a == 42

new Task(() => { var a = SomeClass.Prop; }).Start(); // a == 42

}

}

V3090. Unsafe locking on an object.The analyzer detected a code fragment with unsafe locking on an object. This

diagnostic is triggered in the following situations:

1. locking on 'this';

2. locking on instances of classes 'Type', 'MemberInfo', 'ParameterInfo', 'String', 'Thread';

Page 893: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

3. locking on a public member of the current class;

4. locking on an object that resulted from boxing;

5. locking on newly created objects.

The first three scenarios may cause a deadlock, while the last one causes thread

synchronization to fail. The common problem of the first three scenarios is that the

object being locked on is public: it can be locked on elsewhere and the programmer

will never know about that after locking on it for the first time. As a result, a

deadlock may occur.

Locking on 'this' is unsafe when the class is not private: an object can be locked on

in any part of the program after creating its instance.

For the same reason, it is unsafe to lock on public class members.

To avoid these issues, you just need to lock on, for example, a private class field

instead.

Here is an example of unsafe code where the 'lock' statement is used to lock on

'this':

class A

{

void Foo()

{

lock(this)

{

// do smt

}

}

}

To avoid possible deadlocks, we should lock on, for example, a private field

instead:

class A

{

private Object locker = new Object();

Page 894: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

void Foo()

{

lock(locker)

{

// do smt

}

}

}

Locking on instances of classes 'Type', 'MemberInfo', and 'ParameterInfo' is a bit

more dangerous, as a deadlock is more likely to occur. Using the 'typeof' operator or

methods 'GetType', 'GetMember', etc. on different instances of one type will

produce the same result: getting the same instance of the class.

Objects of types 'String' and 'Thread' need to be discussed separately.

Objects of these types can be accessed from anywhere in the program, even from a

different application domain, which makes a deadlock even more likely. To avoid

this issue, do not lock on instances of these types.

Let's see how a deadlock occurs. Suppose we have an application (Sample.exe) with

the following code:

static void Main(string[] args)

{

var thread = new Thread(() => Process());

thread.Start();

thread.Join();

}

static void Process()

{

String locker = "my locker";

lock (locker)

{

....

}

}

Page 895: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

There is also another application with the following code:

String locker = "my locker";

lock (locker)

{

AppDomain domain = AppDomain.CreateDomain("test");

domain.ExecuteAssembly(@"C:\Sample.exe");

}

Executing this code will result in a deadlock, as it uses an instance of the 'String'

type as a locking object.

We create a new domain within the same process and attempt to execute an

assembly from another file (Sample.exe) in that domain, which results in both 'lock'

statements locking on the same string literal. String literals get interned, so we will

get two references to the same object. As a result, both 'lock' statements lock on the

same object, causing a deadlock.

This error could occur within one domain as well.

A similar problem is observed when working with the 'Thread' type, an instance of

which can be easily created by using the 'Thread.CurrentThread' property, for

example.

To avoid this issue, do not lock on objects of types 'Thread' and 'String'.

Locking on an object of a value type prevents threads from being synchronized.

Note that the 'lock' statement does not allow setting a lock on objects of value types,

but it cannot protect the 'Monitor' class with its methods 'Enter' and 'TryEnter' from

being locked on.

The methods 'Enter' and 'TryEnter' expect an object of type 'Object' as an argument,

so if an object of a value type is passed, it will be 'boxed', which means that a new

object will be created and locked on every time; therefore, the lock will be set (and

released) on these new objects. As a result, thread synchronization will fail.

Consider the following example:

Page 896: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

sealed class A

{

private Int32 m_locker = 10;

void Foo()

{

Monitor.Enter(m_locker);

// Do smt...

Monitor.Exit(m_locker);

}

}

The programmer wanted to set a lock on the private field 'm_locker', but it will be

actually set (and released) on the newly created objects resulting from 'boxing' of

the original object.

To fix this error, we just need to change the type of the 'm_locker' field to a valid

reference type, for example, 'Object'. In that case, the fixed code would look like

this:

sealed class A

{

private Object m_locker = new Object();

void Foo()

{

Monitor.Enter(m_locker);

// Do smt...

Monitor.Exit(m_locker);

}

}

A similar error will appear when the 'lock' construction is used, if there will be

packing of an object in the result of casting:

Int32 val = 10;

lock ((Object)val)

Page 897: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

{ .... }

In this code there will be locking on objects, obtained in the result of boxing. There

will be no thread synchronization, because new objects will be created after the

boxing.

Locking on the newly created objects will be erroneous. An example of such code

may be as follows:

lock (new Object())

{ .... }

or as this:

lock (obj = new Object())

{ .... }

Locking will be also done on different objects, because new objects are created

every time the code is executed. Therefore, the threads will not be synchronized.

V3091. Empirical analysis. It is possible that a typo is present inside the string literal. The 'foo' word is suspicious.When the analyzer detects two identical string literals, it tries to figure out if they

result from misuse of Copy-Paste. Be warned that this diagnostic is based on an

empirical algorithm and may sometimes generate strange false positives.

Consider the following example:

string left_str = "Direction: left.";

string right_str = "Direction: right.";

string up_str = "Direction: up.";

string down_str = "Direction: up.";

Page 898: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

This code was written using the Copy-Paste technique. At the end, the programmer

forgot to change the string literal from "up" to "down". The analyzer treats this code

as incorrect and points out the suspicious word "up" in the last line.

Fixed code:

string left_str = "Direction: left.";

string right_str = "Direction: right.";

string up_str = "Direction: up.";

string down_str = "Direction: down.";

V3092. Range intersections are possible within conditional expressions.The analyzer has detected a potential error in a condition. The program must

perform different actions depending on which range of values a certain variable

meets. For this purpose, the following construct is used in the code:

if ( MIN_A < X && X < MAX_A ) {

....

} else if ( MIN_B < X && X < MAX_B ) {

....

}

The analyzer generates the warning when the ranges checked in conditions overlap.

For example:

if ( 0 <= X && X < 10)

FooA();

else if ( 10 <= X && X < 20)

FooB();

else if ( 20 <= X && X < 300)

FooC();

else if ( 30 <= X && X < 40)

Page 899: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

FooD();

The code contains a typo. The programmer's fingers faltered at some moment and

he wrote "20 <= X && X < 300" instead of "20 <= X && X < 30" by mistake. If

the X variable stores, for example, the value 35, it will be the function FooC() that

will be called instead of FooD().

The fixed code:

if ( 0 <= X && X < 10)

FooA();

else if ( 10 <= X && X < 20)

FooB();

else if ( 20 <= X && X < 30)

FooC();

else if ( 30 <= X && X < 40)

FooD();

V3093. The operator evaluates both operands. Perhaps a short-circuit operator should be used instead.The analyzer detected a possible error that has to do with the programmer confusing

operator '&' with '&&' or '|' with '||' when using them to form a logical expression.

Conditional operators AND ('&&') / OR ('||') evaluate the second operand only when

necessary (see Short circuit) while operators '&' and '|' always evaluate both

operands. It is very likely that the code author did not intend it to work that way.

Consider the following example:

if ((i < a.m_length) & (a[i] % 2 == 0))

{

sum += a[i];

}

Page 900: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Suppose the 'a' object is a container; the number of elements in it is stored in the

'm_length' member. We need to find the sum of even elements, making sure that we

do not go beyond the array boundaries.

Because of a typo, our example uses operator '&' instead of '&&'. It will result in an

array-index-out-of-bounds error when evaluating the '(a[i] % 2 == 0)' subexpression

if index 'i' appears to be greater than or equal to 'a.m_length'. Regardless of whether

the left part of the expression is true or false, the right part will be evaluated

anyway.

Fixed code:

if ((i < a. m_length) && (a[i] % 2 == 0))

{

sum += a[i];

}

Here is another example of incorrect code:

if (x > 0 | BoolFunc())

{

....

}

The call to the BoolFunc() function will execute all the time, even when the (x >

0) condition is true.

Fixed code:

if (x > 0 || BoolFunc())

{

....

}

Code fragments detected by diagnostic V3093 do not always contain errors, but

they do deal with expressions that are non-optimal from the viewpoint of

performance (especially when they use calls to complex functions).

Page 901: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

If, however, the conditional expression is correct and written as you intended, you

can mark this fragment with special comment "//-V3093" so that the analyzer does

not output the warning:

if (x > 0 | BoolFunc()) //-V3093

{

....

}

To learn more about false-positive-suppression techniques, see the documentation.

V3094. Possible exception when deserializing type. The Ctor(SerializationInfo, StreamingContext) constructor is missing.The analyzer detected a suspicious class implementing the 'ISerializable' interface

but lacking a serialization constructor.

A serialization constructor is used for object deserialization and receives 2

parameters of types 'SerializationInfo' and 'StreamingContext'. When inheriting

from this interface, the programmer is obliged to implement method

'GetObjectData' but is not obliged to implement a serialization constructor.

However, if this constructor is missing, a 'SerializationException' will be raised.

Consider the following example. Suppose we have declared a method to handle

object serialization and deserialization:

static void Foo(MemoryStream ms, BinaryFormatter bf, C1 obj)

{

bf.Serialize(ms, obj);

ms.Position = 0;

Page 902: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

obj = (C1)bf.Deserialize(ms);

}

The 'C1' class itself is declared as follows:

[Serializable]

sealed class C1 : ISerializable

{

public C1()

{ }

public void GetObjectData(SerializationInfo info,

StreamingContext context)

{

info.AddValue("field", field, typeof(String));

}

private String field;

}

When attempting to deserialize the object, a 'SerializationException' will be thrown.

To ensure correct deserialization of an object of type 'C1', a special constructor is

required. A correct class declaration should then look like this:

[Serializable]

sealed class C1 : ISerializable

{

public C1()

{ }

private C1(SerializationInfo info, StreamingContext context)

{

field = (String)info.GetValue("field", typeof(String));

}

public void GetObjectData(SerializationInfo info,

StreamingContext context)

Page 903: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

{

info.AddValue("field", field, typeof(String));

}

private String field;

}

Note. This diagnostic has an additional parameter, which can be configured in the

configuration file (*.pvsconfig). It has the following syntax:

//+V3094:CONF:{ IncludeBaseTypes: true }

With this parameter on, the analyzer examines not only how the 'ISerializable'

interface is implemented by the class itself, but also how it is implemented by any

of the base classes. This option is off by default.

To learn more about configuration files, see this page.

V3095. The object was used before it was verified against null. Check lines: N1, N2.The analyzer has detected a potential error that may cause access by a null

reference.

The analyzer has noticed the following situation in the code: an object is being used

first and only then it is checked whether this is a null reference. It means one of the

following things:

1) An error occurs if the object is equal to null.

2) The program works correctly, since the object is never equal to null. The check is

not necessary in this case.

Let's consider the first case. There is an error.

Page 904: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

obj = Foo();

result = obj.Func();

if (obj == null) return -1;

If the 'obj' object is equal to null, the 'obj.Func()' expression will cause an error. The

analyzer will generate a warning for this code mentioning 2 lines: the first line is the

place where the object is used; the second line is the place where the object is

compared to null.

This is the correct code:

obj = Foo();

if (obj == null) return -1;

result = obj.Func();

Let's consider the second case. There is no error.

Stream stream = CreateStream();

while (stream.CanRead)

{

....

}

if (stream != null)

stream.Close();

This code is always correct. Stream object is never equal to null. But the analyzer

does not understand this situation and generates a warning. To make it disappear,

you should remove the check "if (stream != null)". It has no sense and can only

confuse a programmer while reading this code.

This is the correct code:

Stream stream = CreateStream();

while (stream.CanRead)

{

....

}

stream.Close();

Page 905: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

When the analyzer is wrong, you may use (apart from changing the code) a

comment to suppress warnings. For example: "obj.Foo(); //-V3095".

V3096. Possible exception when serializing type. [Serializable] attribute is missing.The analyzer detected a type that implements the 'ISerializable' interface but is not

marked with the [Serializable] attribute. Attempting to serialize instances of this

type will cause raising a 'SerializationException'. Implementation of the

'ISerializable' interface is not enough for the CLR to know at runtime that the type

is serializable; it must be additionally marked with the [Serializable] attribute.

Consider the following example. Suppose we have a method to perform object

serialization and deserialization:

static void Foo(MemoryStream ms, BinaryFormatter bf, C1 obj)

{

bf.Serialize(ms, obj);

ms.Position = 0;

obj = (C1)bf.Deserialize(ms);

}

The 'C1' class is declared in the following way:

sealed class C1 : ISerializable

{

public C1()

{ }

private C1(SerializationInfo info, StreamingContext context)

{

field = (String)info.GetValue("field", typeof(String));

}

Page 906: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

public void GetObjectData(SerializationInfo info,

StreamingContext context)

{

info.AddValue("field", field, typeof(String));

}

private String field = "Some field";

}

When trying to serialize an instance of this type, a 'SerializationException' will be

raised. To solve the issue, we must decorate this class with the [Serializable]

attribute. Therefore, a correct class declaration should look like this:

[Serializable]

sealed class C1 : ISerializable

{

public C1()

{ }

private C1(SerializationInfo info, StreamingContext context)

{

field = (String)info.GetValue("field", typeof(String));

}

public void GetObjectData(SerializationInfo info,

StreamingContext context)

{

info.AddValue("field", field, typeof(String));

}

private String field = "Some field";

}

Note. This diagnostic has one additional parameter, which you can configure in the

configuration file (*.pvsconfig). It has the following syntax:

//+V3096:CONF:{ IncludeBaseTypes: true }

Page 907: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

With this parameter on, the analyzer examines not only how the 'ISerializable'

interface is implemented by the class itself, but also how it is implemented by any

of the base classes. This option is off by default.

To learn more about configuration files, see this page.

V3097. Possible exception: type marked by [Serializable] contains non-serializable members not marked by [NonSerialized].The analyzer detected a suspicious class marked with the [Serializable] attribute and

containing members of non-serializable types (i.e. types that are themselves not

marked with this attribute). At the same time, these members are not marked with

the [NonSerialized] attribute. The presence of such members may lead to raising a

'SerializationException' for some standard classes when attempting to serialize an

instance of such a class.

Consider the following example. Suppose we have declared a method to handle

object serialization and deserialization:

static void Foo(MemoryStream ms, BinaryFormatter bf, C1 obj)

{

bf.Serialize(ms, obj);

ms.Position = 0;

obj = (C1)bf.Deserialize(ms);

}

We have also declared classes 'C1' and 'NonSerializedClass':

sealed class NonSerializedClass { }

[Serializable]

class C1

Page 908: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

{

private Int32 field1;

private NotSerializedClass field2;

}

When attempting to serialize an instance of the 'C1' class, a 'SerializationException'

will be thrown, as marking a class with the [Serializable] attribute implies that all of

its fields are to be serialized while the type of the 'field2' field is not serializable,

which will result in raising the exception. To resolve this issue, the 'field2' field

must be decorated with the [NonSerialized] attribute. A correct declaration of the

'C1' class will then look like this:

[Serializable]

class C1

{

private Int32 field1;

[NonSerialized]

private NotSerializedClass field2;

}

Properties are handled a bit differently. Consider the following example:

[Serializable]

class C2

{

private Int32 field1;

public NonSerializedClass Prop { get; set; }

}

You cannot apply the [NonSerialized] attribute to properties. Nevertheless, the

exception will be thrown anyway when attempting to serialize a class like the one in

the code above using, for example, 'BinaryFormatter'. The reason is that the

compiler expands auto-implemented properties into a field and corresponding "get"

and possibly "set" accessors. What will be serialized in this case is not the property

Page 909: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

itself but the field generated by the compiler. This issue is similar to the one with

field serialization discussed above.

The error can be fixed by explicitly implementing the property through some field.

A correct version of the code will then look like this:

[Serializable]

class C2

{

private Int32 field1;

[NonSerialized]

private NonSerializedClass nsField;

public NonSerializedClass Prop

{

get { return nsField; }

set { nsField = value; }

}

}

V3098. The 'continue' operator will terminate 'do { ... } while (false)' loop because the condition is always false.The analyzer detected a code fragment that may mislead programmers reading it.

Not all developers know that using the "continue" statement in a "do { ... }

while(false)" loop will terminate it instead of continuing its execution.

So, after executing the 'continue' statement, the '(false)' condition will be checked

and the loop will terminate because the condition is false.

Consider the following example:

int i = 1;

Page 910: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

do

{

Console.Write(i);

i++;

if (i < 3)

continue;

Console.Write('A');

} while (false);

The programmer may expect the program to print '12A', but it will actually print '1'.

Even if the code was intended to work that way and there is no error, it is still

recommended to revise it. For example, you can use the 'break' statement:

int i=1;

do {

Console.Write(i);

i++;

if(i < 3)

break;

Console.Write('A');

} while(false);

The code has become clearer; one can immediately see that the loop will terminate

if the "(i < 3)" condition is true. In addition, it won’t trigger the analyzer warning

anymore.

If the code is incorrect, it must be fixed. There are no set rules as to how exactly it

should be rewritten since it depends on the code’s execution logic. For example, if

you need the program to print '12A', it is better to rewrite this fragment as follows:

for (i = 1; i < 3; ++i)

Console.Write(i);

Console.Write('A');

Page 911: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V3099. Not all the members of type are serialized inside 'GetObjectData' method.The analyzer detected a suspicious implementation of method 'GetObjectData',

where some of the serializable type members are left unserialized. This error may

result in incorrect object deserialization or raising a 'SerializationException'.

Consider the following example. Suppose we have declared a method to handle

object serialization and deserialization.

static void Foo(BinaryFormatter bf, MemoryStream ms, Derived obj)

{

bf.Serialize(ms, obj);

ms.Position = 0;

obj = (Derived)bf.Deserialize(ms);

}

Declaration of class 'Base':

abstract class Base

{

public Int32 Prop { get; set; }

}

Declaration of class 'Derived':

[Serializable]

sealed class Derived : Base, ISerializable

{

public String StrProp { get; set; }

public Derived() { }

private Derived(SerializationInfo info,

StreamingContext context)

Page 912: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

{

StrProp = info.GetString(nameof(StrProp));

}

public void GetObjectData(SerializationInfo info,

StreamingContext context)

{

info.AddValue(nameof(StrProp), StrProp);

}

}

When declaring the 'Derived' class, the programmer forgot to serialize the 'Prop'

property of the base class, which will result in incomplete saving of the object's

state when it is serialized. When the object is deserialized, the 'Prop' property will

be set to the default value, which is 0 in this case.

To ensure that the object's state is saved in full during serialization, we need to

modify the code by specifying in the implementation of method 'GetObjectData'

that the 'Prop' property's value should be stored in an object of type

'SerializationInfo', and in the serialization constructor that it should retrieve that

value.

The fixed implementation of method 'GetObjectData' and 'Derived' class'

serialization constructor should look like this:

private Derived(SerializationInfo info,

StreamingContext context)

{

StrProp = info.GetString(nameof(StrProp));

Prop = info.GetInt32(nameof(Prop));

}

public void GetObjectData(SerializationInfo info,

StreamingContext context)

{

info.AddValue(nameof(StrProp), StrProp);

Page 913: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

info.AddValue(nameof(Prop), Prop);

}

In the example that we've discussed above, the developer of the base class didn't

cater for its serialization. If there is enabled and the type implements the

'ISerializable' interface, then for the correct serialization of the members of the base

class we should call the method 'GetObjectData' of the base class from the derived

one:

public override void GetObjectData(SerializationInfo info,

StreamingContext context)

{

base.GetObjectData(info, context);

....

}

Additional information

Custom Serialization

V3100. NullReferenceException is possible. Unhandled exceptions in destructor lead to termination of runtime.The analyzer detected a block of code that may lead to raising a

NullReferenceException in a class destructor (finalizer) when executed.

The body of a class destructor is a critical spot of the program. Starting with .NET

version 2.0, throwing an unhandled exception in the destructor body will cause it to

crash. An exception that has left the destructor cannot be handled afterwards.

What follows from this explanation is that when addressing objects inside a

destructor, you should test them for null in advance to avoid a crash.

Page 914: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Consider the following example:

class A

{

public List<int> numbers { get; set; }

~A()

{

if (numbers.Count > 0) {

....

}

}

}

Since the 'numbers' collection was not initialized at declaration time, the 'numbers'

field is not guaranteed to contain the reference to the object of class 'A' when this

object is finalized. Therefore, we should additionally test the collection for null or

wrap the call to the field into a try/catch block.

A correct version of the code above should look like this:

~A()

{

if (numbers != null)

{

if (numbers.Count > 0)

{

....

}

}

}

Starting with C# version 6.0, you can use the '?.' operator to reduce the check to the

following code:

~A()

{

if (numbers?.Count > 0) {

Page 915: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

....

}

}

V3101. Potential resurrection of 'this' object instance from destructor. Without re-registering for finalization, destructor will not be called a second time on resurrected object.The analyzer detected a suspicious destructor that deals with potentially incorrect

object "resurrection".

The object destructor is invoked by the .NET garbage collector immediately before

reclaiming the object. Destructor declaration is not obligatory in .NET Framework

languages, as the garbage collector will reclaim the object anyway, even without its

destructor being declared explicitly. Destructors are usually used when one needs to

release unmanaged resources used by .NET objects before freeing these objects.

File-system handles are one example of such resources, which cannot be released

automatically by the garbage collector.

However, immediately before an object is reclaimed, the user can (intentionally or

unintentionally) "resurrect" it before the garbage collector reclaims its memory. As

you remember, the garbage collector frees objects that have become inaccessible,

i.e. there are no references to these objects left. However, if you assign a reference

to such an object from its destructor to a global static variable, for example, then the

object will become visible to other parts of the program again, i.e. will be

"resurrected". This operation may be executed multiple times.

The following example shows how such "resurrection" occurs:

Page 916: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

class HeavyObject

{

private HeavyObject()

{

HeavyObject.Bag.Add(this);

}

...

public static ConcurrentBag<HeavyObject> Bag;

~HeavyObject()

{

if (HeavyObject.Bag != null)

HeavyObject.Bag.Add(this);

}

}

Suppose we have object "HeavyObject", creation of which is a highly resource-

intensive operation. Besides, this object cannot be used from different parts of the

program simultaneously. Suppose also that we can create just a few instances of

such objects at once. In our example, the "HeavyObject" type has open static field

"Bag", which is a collection that will be used to store all the created instances of

"HeavyObject" objects (they are be added to the collection in the constructor). This

will allow getting an instance of type "HeavyObject" from anywhere in the

program:

HeavyObject heavy;

HeavyObject.Bag.TryTake(out heavy);

Method "TryTake" will also delete the "heavy" instance from the "Bag" collection.

That is, we can use only a limited number of instances of type "HeavyObject" (its

constructor is closed) created in advance. Now, suppose we do not need the "heavy"

instance created by the "TryTake" method anymore and all references to this object

have been deleted. Then, some time later, the garbage collector will invoke the

object's destructor, where this object will be again added to the "Bag" collection, i.e.

"resurrected" and made available to the user, without having to re-create it.

Page 917: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

However, our example contains an error that will make the code work differently

from what is described above. This error deals with an assumption that the

"resurrected" object's destructor will be invoked each time the object becomes

invisible to the program (i.e. there are no references to it left). What will actually

happen is that the destructor will be called only once, i.e. the object will be "lost"

the next (a second) time the garbage collector attempts to reclaim it.

To ensure correct work of the destructor when the object is "resurrected", this object

must be re-registered using method GC.ReRegisterForFinalize:

~HeavyObject()

{

if (HeavyObject.Bag != null)

{

GC.ReRegisterForFinalize(this);

HeavyObject.Bag.Add(this);

}

}

This solution guarantees that the destructor will be called each time before the

garbage collector tries to reclaim the object.

V3102. Suspicious access to element by a constant index inside a loop.The analyzer detected a possible error that has to do with trying to access the

elements of an array or list using the same constant index at each iteration of a 'for'

loop.

Consider the following example:

ParameterInfo[] parameters = method.GetParameters();

for (int i = 0; i < parameters.Length; i++)

{

Type parameterType = parameters[0].ParameterType;

Page 918: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

....

}

In this code, the programmer wanted the value of the i-th element of the 'parameters'

array to be assigned to variable 'parameterType' at each loop iteration, but because

of a typo only the first element is accessed all the time. Another explanation is that

the programmer probably used the element at index zero for debugging and then

forgot to change the index value.

Fixed code:

ParameterInfo[] parameters = method.GetParameters();

for (int i = 0; i < parameters.Length; i++)

{

Type parameterType = parameters[i].ParameterType;

....

}

Here is one more example, taken from a real application:

if (method != null && method.SequencePoints.Count > 0)

{

CodeCoverageSequence firstSequence = method.SequencePoints[0];

int line = firstSequence.Line;

int column = firstSequence.Column;

for (int i = 1; i < method.SequencePoints.Count; ++i)

{

CodeCoverageSequence sequencePoint = method.SequencePoints[0];

if (line > sequencePoint.Line)

{

line = sequencePoint.Line;

column = sequencePoint.Column;

}

}

// ....

Page 919: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

}

In this code, the programmer wrote a separate block of code to access the first

element of the 'method.SequencePoints' list while the other elements are processed

in a loop. However, the programmer copied the line accessing the first element into

the loop body and changed only the variable name from 'firstSequence' to

'sequencePoint' but forgot about the index.

Fixed code:

if (method != null && method.SequencePoints.Count > 0)

{

CodeCoverageSequence firstSequence = method.SequencePoints[0];

int line = firstSequence.Line;

int column = firstSequence.Column;

for (int i = 1; i < method.SequencePoints.Count; ++i)

{

CodeCoverageSequence sequencePoint = method.SequencePoints[i];

if (line > sequencePoint.Line)

{

line = sequencePoint.Line;

column = sequencePoint.Column;

}

}

// ....

}

V3103. A private Ctor(SerializationInfo, StreamingContext) constructor in unsealed type will not be accessible when deserializing derived types.

Page 920: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer detected a serialization constructor with a strange access modifier.

The following cases are treated as suspicious:

the constructor is declared with the 'public' access modifier;

the constructor is declared with the 'private' access modifier, but the type is unsealed.

A serialization constructor is called when an object is deserialized, and must not be

called outside the type (except when called by a derived class), so it should not be

declared as 'public' or 'internal'.

If a constructor is declared with the 'private' access modifier but the class is not

sealed, derived classes will not be able to call this constructor; therefore,

deserialization of the members of the base class will be impossible.

Consider the following example:

[Serializable]

class C1 : ISerializable

{

....

private C1(SerializationInfo info, StreamingContext context)

{

....

}

....

}

The 'C1' class is unsealed, but the serialization constructor is declared as 'private'.

As a result, derived classes will not be able to call this constructor and, therefore,

the object will not be deserialized correctly. To fix this error, the access modifier

should be changed to 'protected':

[Serializable]

class C1 : ISerializable

{

....

Page 921: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

protected C1(SerializationInfo info, StreamingContext context)

{

....

}

....

}

Note. This diagnostic has an additional parameter, which can be configured in the

configuration file (*.pvsconfig). It has the following syntax:

//+V3103:CONF:{ IncludeBaseTypes: true }

With this parameter on, the analyzer examines not only how the 'ISerializable'

interface is implemented by the class itself, but also how it is implemented by any

of the base classes. This option is off by default.

To learn more about configuration files, see this page.

V3104. 'GetObjectData' implementation in unsealed type is not virtual, incorrect serialization of derived type is possible.The analyzer detected an unsealed class implementing the 'ISerializable' interface

but lacking virtual method 'GetObjectData'. As a result, serialization errors are

possible in derived classes.

Consider the following example. Suppose we have declared a base class and a class

inheriting from it as follows:

[Serializable]

class Base : ISerializable

{

....

public void GetObjectData(SerializationInfo info,

Page 922: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

StreamingContext context)

{

....

}

}

[Serializable]

sealed class Derived : Base

{

....

public new void GetObjectData(SerializationInfo info,

StreamingContext context)

{

....

}

}

There is also the following code to manage object serialization:

void Foo(BinaryFormatter bf, MemoryStream ms)

{

Base obj = new Derived();

bf.Serialize(ms, obj);

ms.Position = 0;

Derived derObj = (Derived)bf.Deserialize(ms);

}

The object will be serialized incorrectly because the 'GetObjectData' method will be

called from the base class, not the derived one. Therefore, the members of the

derived class will not be serialized. Attempting to retrieve the values of the

members added by method 'GetObjectData' of the derived class when deserializing

the 'SerializationInfo' object will cause raising an exception because there are no

such values in the object.

To fix this error, the 'GetObjectData' method must be declared as 'virtual' in the base

class, and as 'override' in the derived one. The fixed code will then look like this:

Page 923: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

[Serializable]

class Base : ISerializable

{

....

public virtual void GetObjectData(SerializationInfo info,

StreamingContext context)

{

....

}

}

[Serializable]

sealed class Derived : Base

{

....

public override void GetObjectData(SerializationInfo info,

StreamingContext context)

{

....

}

}

If the class contains only an explicit implementation of the interface, an implicit

implementation of virtual method 'GetObjectData' is also required. Consider the

following example. Suppose we have declared the classes as follows:

[Serializable]

class Base : ISerializable

{

....

void ISerializable.GetObjectData(SerializationInfo info,

StreamingContext context)

{

....

}

}

Page 924: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

[Serializable]

sealed class Derived : Base, ISerializable

{

....

public void GetObjectData(SerializationInfo info,

StreamingContext context)

{

....

}

}

You cannot call to the 'GetObjectData' method of the base class from the derived

class. Therefore, some of the members will not be serialized. To fix the error,

virtual method 'GetObjectData' must be implicitly implemented in addition to the

explicit interface implementation. The fixed code will then look like this:

[Serializable]

class Base : ISerializable

{

....

void ISerializable.GetObjectData(SerializationInfo info,

StreamingContext context)

{

GetObjectData(info, context);

}

public virtual void GetObjectData(SerializationInfo info,

StreamingContext context)

{

....

}

}

[Serializable]

sealed class Derived : Base

{

Page 925: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

....

public override void GetObjectData(SerializationInfo info,

StreamingContext context)

{

....

base.GetObjectData(info, context);

}

}

If the class is not expected to have any descendants, declare it as 'sealed'.

V3105. The 'a' variable was used after it was assigned through null-conditional operator. NullReferenceException is possible.This diagnostic warns you about the risk of getting 'NullReferenceException' and is

triggered when a variable’s field is accessed without first testing that variable for

null. The point here is that the value the variable refers to is computed using the

null-conditional operator.

Consider the following example:

public int Foo (Person person)

{

string parentName = person?.Parent.ToString();

return parentName.Length;

}

When initializing the 'parentName' object, we assume that 'person' may be null. In

that case, the 'ToString()' function will not be executed, and the 'parentName'

variable will be assigned a null value. An attempt to read the 'Length' property from

this variable will result in throwing 'NullReferenceException'.

This is what a correct version of the code above may look like:

Page 926: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

public int Foo (Person person)

{

string parentName = person?.Parent.ToString();

return parentName?.Length ?? 0;

}

Now the function will return the string length if the 'parentName' variable does not

refer to null, and 0 if it does.

V3106. Possibly index is out of bound.When indexing into a variable of type 'array', 'list', or 'string', an

'IndexOutOfRangeException' exception may be thrown if the index value is

outbound the valid range. The analyzer can detect some of such errors.

For example, it may happen when iterating through an array in a loop:

int[] buff = new int[25];

for (int i = 0; i <= 25; i++)

buff[i] = 10;

Keep in mind that the first item's index is 0 and the last item's index is the array size

minus one. Fixed code:

int[] buff = new int[25];

for (int i = 0; i < 25; i++)

buff[i] = 10;

Errors like that are found not only in loops but in conditions with incorrect index

checks as well:

void ProcessOperandTypes(ushort opCodeValue, byte operandType)

{

var OneByteOperandTypes = new byte[0xff];

if (opCodeValue < 0x100)

{

Page 927: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

OneByteOperandTypes[opCodeValue] = operandType;

}

...

}

Fixed version:

void ProcessOperandTypes(ushort opCodeValue, byte operandType)

{

var OneByteOperandTypes = new byte[0xff];

if (value < 0xff)

{

OneByteOperandTypes[value] = operandType;

}

...

}

Programmers also make mistakes of this type when accessing a particular item of an

array or list.

void Initialize(List<string> config)

{

...

if (config.Count == 16)

{

var result = new Dictionary<string, string>();

result.Add("Base State", config[0]);

...

result.Add("Sorted Descending Header Style", config[16]);

}

...

}

In this example, the programmer made a mistake in the number of entries in the

'config' list. The fixed version should look like this:

void Initialize(List<string> config)

{

Page 928: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

...

if (config.Count == 17)

{

var result = new Dictionary<string, string>();

result.Add("Base State", config[0]);

...

result.Add("Sorted Descending Header Style", config[16]);

}

...

}

V3107. Identical expressions to the left and to the right of compound assignment.The analyzer detected identical subexpressions to the left and to the right of a

compound assignment operator. This operation may be incorrect or meaningless, or

can be simplified.

Consider the following example:

x += x + 5;

Perhaps the programmer simply wanted to add the value 5 to the 'x' variable. In that

case, the fixed code would look like this:

x = x + 5;

Or perhaps they wanted to add the value 5 but wrote an extra 'x' variable by

mistake. Then the code should look like this:

x += 5;

However, it is also possible that the code is written correctly, but it looks too

complicated and should be simplified:

Page 929: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

x = x * 2 + 5;

Now consider the following example:

x += x;

This operation is equivalent to multiplying the value of a variable by two. This is

what a clearer version would look like:

x *= 2;

Here is one more expression:

y += top - y;

We are trying to add the difference of the variables 'top' and 'y' to the 'y' variable.

Resolving this expression produces the following result:

y = y + top – y;

It can be simplified, as the 'y' variable is subtracted from itself, which does not

make sense:

y = top;

V3108. It is not recommended to return null or throw exceptions from 'ToString()' method.The analyzer detected that an overridden 'ToString()' method returns 'null' or throws

an exception.

Consider the following example:

public override string ToString()

{

return null;

Page 930: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

}

It is very likely that this method will be called to get a string representation of an

instance at runtime or during debugging. Since the programmer is not likely to test

the function’s return result for 'null', using it may lead to throwing

'NullReferenceException'. If you need to return an empty or unknown value of an

instance’s string representation, return an empty string:

public override string ToString()

{

return string.Empty;

}

Another example of poor implementations of the 'ToString()' method is when it

throws exceptions:

public override string ToString()

{

if(hasError)

throw new Exception();

....

}

It is very likely that this method will be called by the user of the class at a point

where exceptions are not expected to be thrown and handled, for example in a

destructor.

If you want the method to issue an error message when generating an object’s string

representation, return its text as a string or log the error in some way:

public override string ToString()

{

if(hasError)

{

LogError();

return "Error encountered";

}

Page 931: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

....

}

V3109. The same sub-expression is present on both sides of the operator. The expression is incorrect or it can be simplified.The analyzer detected identical subexpressions in the left and the right part of an

expression with a comparison operator. This operation is incorrect or meaningless,

or can be simplified.

Consider the following example:

if ((x – y) >= (x - z)) {};

The 'x' variable in this fragment is obviously not necessary and can be removed

from both parts of the expression. This is what the simplified version of the code

would look like:

if (y <= z) {};

The next example:

if (x1 == x1 + 1) {};

This code contains a true error, as the expression will be false at any value of the

'x1' variable. Perhaps the programmer made a typo, and the code was actually meant

to look like this:

if (x2 == x1 + 1) {};

One more example:

if (x < x * y) {};

Page 932: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

This expression can also be simplified by removing the 'x' variable:

if (y > 1) {};

V3110. Possible infinite recursion.The analyzer detected a possible infinite recursion. It will most likely result in a

stack overflow and raising a 'StackOverflow' exception.

Consider the following example. Suppose we have property 'MyProperty' and field

'_myProperty' related to that property. A typo could result in the following error:

private string _myProperty;

public string MyProperty

{

get { return MyProperty; } // <=

set { _myProperty = value; }

}

When specifying the value to be returned in the property accessor method, the

'MyProperty' property is accessed instead of the '_myProperty' field, which leads to

an infinite recursion when getting the property value. This is what the fixed code

should look like:

private string _myProperty;

public string MyProperty

{

get { return _myProperty; }

set { _myProperty = value; }

}

Another example:

class Node

{

Node parent;

public void Foo()

Page 933: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

{

// some code

parent.Foo(); // <=

}

}

It seems that the programmer intended to iterate through all the 'parent' fields but

did not provide for a recursion termination condition. This issue is trickier than the

previous one, as it may result not only in a stack overflow but a null dereference

error as well when reaching the topmost parent entity. This is what the fixed code

could look like:

class Node

{

Node parent;

public void Foo()

{

// some code

if (parent != null)

parent.Foo();

}

}

A third example. Suppose there is a method with the 'try - catch - finally' construct.

void Foo()

{

try

{

// some code;

return;

}

finally

{

Foo(); // <=

}

}

Page 934: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

It seems that the programmer did not take into account that the 'finally' block would

be executed both when throwing an exception inside the 'try' block and when

leaving the method through the 'return' statement. The 'finally' block, therefore, will

always recursively call to the 'Foo' method. To make the recursion work properly, a

condition should be specified before the method call:

void Foo()

{

try

{

// some code;

return;

}

finally

{

if (condition)

Foo();

}

}

V3111. Checking value for null will always return false when generic type is instantiated with a value type.The analyzer detected a comparison of a generic-type value with 'null'. If the

generic type used has no constraints, it can be instantiated both with value and

reference types. In the case of a value type, the check will always return 'false'

because value types cannot have a value of null.

Consider the following example:

class Node<T>

{

Page 935: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

T value;

void LazyInit(T newValue)

{

if (value == null) // <=

{

value = newValue;

}

}

}

If 'T' is defined as a value type, the body of the 'if' statement will never execute and

the 'value' variable will fail to be initialized to the value passed, so its value will

always remain the 'default' value of 'T'.

Use constraints if you need to handle objects of reference types only. For example,

you can use a constraint on the generic type 'T' in the code above so that it could be

instantiated only with reference types:

class Node<T> where T : class // <=

{

T value;

void LazyInit(T newValue)

{

if (value == null)

{

value = newValue;

}

}

}

If you want the generic type to work with both value and reference types and you

want the check to work with values of both, test the value for the type’s default

value instead of 'null':

class Node<T>

{

T value;

Page 936: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

void LazyInit(T newValue)

{

if (object.Equals(value, default(T))) // <=

{

value = newValue;

}

}

}

In this case, the check will work properly with both reference and value types.

However, if you want to apply it only to reference types with a null value (without

constraints on the 'T' type), do the following:

class Node<T>

{

T value;

void LazyInit(T newValue)

{

if (typeof(T).IsClass && // <=

object.Equals(value, default(T)))

{

value = newValue;

}

}

}

The 'IsClass' method will return 'true' if the generic type was instantiated with a

reference type, so only reference-type values will be tested for the type’s default

value, like in the previous example.

V3112. An abnormality within similar comparisons. It is possible that a typo is present inside the expression.

Page 937: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer found suspicious condition that may contain an error. The diagnosis is

empirical, that is why it is easier to demonstrate it on the example than to explain

the working principle of the analyzer. Consider this example:

if (m_a != a ||

m_b != b ||

m_b != c) // <=

{

....

}

Because of the similarity of the variable names, there is a typo in the code. An error

is located on the third line. The variable 'c' should be compared with 'm_c' rather

than with 'm_b'. It is difficult to notice the error even when reading this text. Please,

pay attention to the variable names.

The right variant:

if (m_a != a ||

m_b != b ||

m_c != c) // <=

{

....

}

If the analyzer issued the warning V3112, then carefully read the corresponding

code. Sometimes it is difficult to notice a typo.

V3113. Consider inspecting the loop expression. It is possible that different variables are used inside initializer and iterator.The analyzer detected a 'for' operator whose iterator section contains an increment

or decrement operation with a variable that is not the counter of that loop.

Page 938: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Consider the following expression:

for (int i = 0; i != N; ++N)

This code is very likely to be incorrect: the 'i' variable should be used instead of 'N'

in the increment operation '++N':

for (int i = 0; i != N; ++i)

Another example:

for (int i = N; i >= 0; --N)

This code is also incorrect. The 'i' variable should be decremented instead of 'N':

for (int i = N; i >= 0; --i)

V3114. IDisposable object is not disposed before method returns.To understand what kind of issues this diagnostic detects, we should recall some

theory.

The garbage collector automatically releases the memory allocated to a managed

object when that object is no longer used and there are no strong references to it left.

However, it is not possible to predict when garbage collection will occur (unless

you run it manually). Furthermore, the garbage collector has no knowledge of

unmanaged resources such as window handles, or open files and streams. Such

resources are usually released using the 'Dispose' method.

The analyzer relies on that information and issues a warning when detecting a local

variable whose object implements the 'IDisposable' interface and is not passed

outside that local variable’s scope. After the object is used, its 'Dispose' method is

not called to release the unmanaged resources held by it.

If that object contains a handle (for example a file), it will remain in the memory

until the next garbage-collection session, which will occur in an indeterminate

Page 939: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

amount of time up to the point of program termination. As a result, the file may stay

locked for indefinite time, affecting normal operation of other programs or the

operating system.

Consider the following example:

string Foo()

{

var stream = new StreamReader(@"С:\temp.txt");

return stream.ReadToEnd();

}

In this case, the 'StreamReader' object will be storing the handle of an open file even

after control leaves the 'Foo' method, keeping that file locked to other programs and

the operating system until the garbage collector cleans it up.

To avoid this problem, make sure you have your resources released in time by using

the 'Dispose' method, as shown below:

string Foo()

{

var stream = new StreamReader(@"С:\temp.txt");

var result = stream.ReadToEnd();

stream.Dispose();

return result;

}

For more certainty, however, we recommend that you use a 'using' statement to

ensure that the resources held by an object will be released after use:

string Foo()

{

using (var stream = new StreamReader(@"С:\temp.txt"))

{

return stream.ReadToEnd();

}

Page 940: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

}

The compiler will expand the 'using' block into a 'try-finally' statement and insert a

call to the 'Dispose' method into the 'finally' block to guarantee that the object will

be collected even in case of exceptions.

V3115. It is not recommended to throw exceptions from 'Equals(object obj)' method.The analyzer detected that overridden method 'Equals(object obj)' might throw an

exception.

Consider the following example:

public override bool Equals(object obj)

{

return obj.GetType() == this.GetType();

}

If the 'obj' argument is null, a 'NullReferenceException' will be thrown. The

programmer must have forgotten about this scenario when implementing the

method. Use a null check to make this code work properly:

public override bool Equals(object obj)

{

if (obj == null)

return false;

return obj.GetType() == this.GetType();

}

Another poor practice when implementing the 'Equals(object obj)' method is to

explicitly throw an exception from it. For example:

public override bool Equals(object obj)

Page 941: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

{

if (obj == null)

throw new InvalidOperationException("Invalid argument.");

return obj == this;

}

This method is very likely to be called in such a block of code where exception

throwing and handling are not expected.

If one of the objects does not meet the conditions, return 'false':

public override bool Equals(object obj)

{

if (obj == null)

return false;

return obj == this;

}

V3116. Consider inspecting the 'for' operator. It's possible that the loop will be executed incorrectly or won't be executed at all.The analyzer detected a 'for' statement with incorrect bounds of the iterator.

Consider the following example:

for (int i = 0; i < 100; --i)

This code is obviously incorrect: the value of the 'i' variable will always be less than

100, at least until it overflows. This behavior is hardly what the programmer

expected. To fix this error, we need either to replace the decrement operation '--i'

with increment operation '++i':

Page 942: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

for (int i = 0; i < 100; ++i)

or to specify the appropriate bounds for the 'i' variable using the relational operator

'>' or '!= ':

for (int i = 99; i >= 0; --i)

for (int i = 99; i != -1; --i)

Which solution is the right one is up to the author of the code to decide depending

on the particular situation.

V3117. Constructor parameter is not used.The analyzer detected a constructor with an unused parameter. For example:

public class MyClass

{

protected string _logPath;

public String LogPath { get { return _logPath; } }

public MyClass(String logPath) // <=

{

_logPath = LogPath;

}

}

It seems that the programmer made a typo and wrote 'LogPath' instead of 'logPath',

which resulted in not using the constructor's parameter anywhere in the code. The

fixed version:

public class MyClass

{

protected string _logPath;

public String LogPath { get { return _logPath; } }

Page 943: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

public MyClass(String logPath) // <=

{

_logPath = logPath;

}

}

Consider one more example.

public class MyClass

{

public MyClass(String logPath) // <=

{

//_logPath = logPath;

}

}

If you deliberately avoid using a constructor's parameter, we recommend that you

mark the constructor with the 'Obsolete' attribute.

public class MyClass

{

[Obsolete]

public MyClass(String logPath) // <=

{

//_logPath = logPath;

}

}

V3118. A component of TimeSpan is used, which does not represent full time interval. Possibly 'Total*' value was intended instead.

Page 944: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer detected an expression accessing the property 'Milliseconds',

'Seconds', 'Minutes', or 'Hours' of an object of type 'TimeSpan', which represents a

time interval between several dates or other time intervals.

This expression is incorrect if you expect it to return the total number of time units

in the interval represented by the object, as the property you are accessing will

return only part of that interval.

Consider the following example:

var t1 = DateTime.Now;

await SomeOperation(); // 2 minutes 10 seconds

var t2 = DateTime.Now;

Console.WriteLine("Execute time: {0}sec", (t2 - t1).Seconds);

// Result - "Execute time: 10sec"

We write the date and time before executing an operation to the 't1' variable, and the

date and time after executing the operation to the 't2' variable. Suppose that it takes

exactly 2 minutes 10 seconds for the 'SomeOperation' method to execute. Then we

want to output the difference between the two variables in seconds, i.e. the time

interval of operation execution. In our example, it is 130 seconds, but the 'Seconds'

property will return only 10 seconds. The fixed code should look like this:

var t1 = DateTime.Now;

await SomeOperation(); // 2 minutes 10 seconds

var t2 = DateTime.Now;

Console.WriteLine("Execute time: {0}sec", (t2 - t1).TotalSeconds);

// Result - "Execute time: 130sec"

We need to use the 'TotalSeconds' property to get the total number of seconds in the

time interval.

V3119. Calling a virtual (overridden) event may lead to unpredictable behavior. Consider

Page 945: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

implementing event accessors explicitly or use 'sealed' keyword.The analyzer detected usage of a virtual or overridden event. If this event is

overridden in a derived class, it may lead to unpredictable behavior. MSDN does

not recommend using overridden virtual events: "Do not declare virtual events in a

base class and override them in a derived class. The C# compiler does not handle

these correctly and it is unpredictable whether a subscriber to the derived event will

actually be subscribing to the base class

event". https://msdn.microsoft.com/en-us/library/hy3sefw3.aspx?

f=255&MSPPError=-2147217396.

Consider the following example:

class Base

{

public virtual event Action MyEvent;

public void FooBase() { MyEvent?.Invoke(); }

}

class Child: Base

{

public override event Action MyEvent;

public void FooChild() { MyEvent?.Invoke(); }

}

static void Main()

{

var child = new Child();

child.MyEvent += () => Console.WriteLine("Handler");

child.FooChild();

child.FooBase();

}

Even though both methods FooChild() and FooBase() are called, the Main() method

will print only one line:

Page 946: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Handler

If we used a debugger or test output, we could see that the MyEvent variable's value

was null when calling to child.FooBase(). It means that the subscriber to

the MyEvent event in the Child class, which is derived from Base and overrides this

event, did not subscribe to the MyEvent event in the base class. This behavior seems

to contradict the behavior of virtual methods, for example, but it can be explained

by the specifics of event implementation in C#. When declaring an event, the

compiler automatically creates two accessor methods to handle it, add and remove,

and also a delegate field where delegates are added to\removed from when

subscribing to\unsubscribing from events. For a virtual event, the base and derived

classes will have individual (not virtual) fields associated with this event.

This issue can be avoided by declaring event accessors explicitly:

class Base

{

public virtual Action _myEvent { get; set; }

public virtual event Action MyEvent

{

add

{

_myEvent += value;

}

remove

{

_myEvent -= value;

}

}

public void FooBase() { _myEvent?.Invoke(); }

}

We strongly recommend that you do not use virtual or overridden events in the way

shown by the first example. If you still have to use overridden events (for example,

when deriving from an abstract class), use them carefully, allowing for the possible

Page 947: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

undefined behavior. Declare accessors add and remove explicitly, or use the 'sealed'

keyword when declaring a class or event.

V3120. Potentially infinite loop. The variable in the loop exit condition does not change its value between iterations.The analyzer detected a potentially infinite loop with its exit condition depending

on a variable whose value never changes between iterations.

Consider the following example:

int x = 0;

while (x < 10)

{

Do(x);

}

The loop's exit condition depends on variable 'x' whose value will always be zero,

so the 'x < 10' check will always evaluate to "true", causing an infinite loop. A

correct version of this code could look like this:

int x = 0;

while (x < 10)

{

x = Do(x);

}

Here is another example where the loop exit condition depends on a variable whose

value, in its turn, changes depending on other variables that never change inside the

loop. Suppose we have the following method:

int Foo(int a)

{

Page 948: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

int j = 0;

while (true)

{

if (a >= 32)

{

return j * a;

}

if (j == 10)

{

j = 0;

}

j++;

}

}

The loop's exit condition depends on the 'a' parameter. If 'a' does not pass the 'a >=

32' check, the loop will become infinite, as the value of 'a' does not change between

iterations. This is one of the ways to fix this code:

int Foo(int a)

{

int j = 0;

while (true)

{

if (a >= 32)

{

return j * a;

}

if (j == 10)

{

j = 0;

a++; // <=

}

j++;

}

Page 949: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

}

In the fixed version, the local variable 'j' controls how the 'a' parameter's value

changes.

V3121. An enumeration was declared with 'Flags' attribute, but no initializers were set to override default values.The analyzer detected an enumeration declared with the 'Flags'

(System.FlagsAttribute) attribute but lacking initializers for overriding the default

values of the enumeration constants. Consider the following example:

[Flags]

enum DeclarationModifiers

{

Static,

New,

Const,

Volatile

}

When declared with the 'Flags' attribute, an enumeration behaves not just as a set of

named, mutually exclusive constants, but as a bit field, i.e. a set of flags whose

values are normally defined as powers of 2, and the enumeration is handled by

combining the elements with a bitwise OR operation:

DeclarationModifiers result = DeclarationModifiers.New |

DeclarationModifiers.Const;

If no initializers were set for the values of such an enumeration (default values are

used instead), the values might overlap when combined. The example above is very

likely to be incorrect and can be fixed in the following way:

Page 950: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

[Flags]

enum DeclarationModifiers

{

Static = 1,

New = 2,

Const = 4,

Volatile = 8

}

Now the enumeration meets all the requirements for a bit field.

However, programmers sometimes leave the default values of the elements in such

an enumeration on purpose, but then they should allow for every possible

combination of values. For example:

[Flags]

enum Colors

{

None, // = 0 by default

Red, // = 1 by default

Green, // = 2 by default

Red_Green // = 3 by default

}

In this example, the programmer allowed for the overlapping values: a combination

of 'Colors.Red' and 'Colors.Green' yields the value 'Colors.Red_Green', as expected.

There is no error in this code, but it is only the code author who can establish this

fact.

The following example shows the difference between the output of two

enumerations marked with the 'Flags' attribute, one with and the other without value

initialization:

[Flags]

enum DeclarationModifiers

{

Static, // = 0 by default

Page 951: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

New, // = 1 by default

Const, // = 2 by default

Volatile // = 3 by default

}

[Flags]

enum DeclarationModifiers_Good

{

Static = 1,

New = 2,

Const = 4,

Volatile = 8

}

static void Main(....)

{

Console.WriteLine(DeclarationModifiers.New |

DeclarationModifiers.Const);

Console.WriteLine(DeclarationModifiers_Good.New |

DeclarationModifiers_Good.Const);

}

The corresponding outputs:

Volatile

New, Const

Since the 'DeclarationModifiers' enumeration uses default values, combining the

constants 'DeclarationModifiers.New' and 'DeclarationModifiers.Const' results in

the value 3, overlapping the constant 'DeclarationModifiers.Volatile', which the

programmer might not expect. For the 'DeclarationModifiers_Good' enumeration,

on the contrary, a combination of the flags DeclarationModifiers_Good.New ' and

'DeclarationModifiers_Good.Const' results in a correct value, which is a

combination of both, as planned.

Page 952: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V3122. Uppercase (lowercase) string is compared with a different lowercase (uppercase) string.The analyzer detected a comparison of two strings whose characters are in different

cases. Consider the following example:

void Some(string s)

{

if (s.ToUpper() == "abcde")

{

....

}

}

After casting the 's' variable's value to upper case, the resulting string is compared

with a string where all the characters are lowercase. As this comparison is always

false, this code is incorrect and can be fixed in the following way:

void Some(string s)

{

if (s.ToLower() == "abcde")

{

....

}

}

Consider another example:

void Some()

{

string s = "abcde";

....

if (s.Contains("AbCdE"))

{

Page 953: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

....

}

}

While all the characters of the 's' variable's value are lowercase, an attempt is made

to check if the string contains a mixed-case substring. Obviously, the 'Contains'

method will always return 'false', which also indicates an error.

V3123. Perhaps the '??' operator works differently from what was expected. Its priority is lower than that of other operators in its left part.The analyzer detected a code fragment that is very likely to contain a logic error.

The code uses an expression with the operator '??' or '?:' that may be evaluated

differently from what the programmer intended.

The '??' and '?:' operators have lower precedence than the operators ||, &&, |, ^, &, !

=, ==, +, -, %, /, *. Programmers sometimes forget about this and write faulty code

like in the following example:

public bool Equals(Edit<TNode> other)

{

return _kind == other._kind

&& (_node == null) ? other._node == null :

node.Equals(other._node);

}

Since the '&&' operator's precedence is higher than that of '?:', the '_kind ==

other._kind && (_node == null)' expression will be evaluated in the first place. To

avoid errors like that, make sure to enclose the whole expression with the '?:'

operator in parentheses:

Page 954: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

public bool Equals(Edit<TNode> other)

{

return _kind == other._kind

&& ((_node == null) ? other._node == null :

node.Equals(other._node));

}

The next example of incorrect code uses the '??' operator:

public override int GetHashCode()

{

return ValueTypes.Aggregate(...)

^ IndexMap?.Aggregate(...) ?? 0;

}

The '^' operator's precedence is higher than that of '??', so if 'IndexMap' is found to

be null, the left operand of the '??' operator will also have the value of "null", which

means that the function will always return 0 regardless of the contents of the

'ValueTypes' collection.

Like in the case with the '?:' operator, it is recommended that you enclose

expressions with the '??' operator in parentheses:

public override int GetHashCode()

{

return ValueTypes.Aggregate(...)

^ (IndexMap?.Aggregate(...) ?? 0);

}

From now on, the 'GetHashCode()' function will return different values depending

on the contents of the 'ValueTypes' collection even when 'IndexMap' is equal to

'null'.

V3124. Appending an element and checking for key uniqueness is

Page 955: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

performed on two different variables.The analyzer detected a suspicious code fragment where a key is tested for being

present in one dictionary, while the new element is appended to another. This

situation may indicate a typo or a logic error. Consider the following example:

Dictionary<string, string> dict = new Dictionary<string, string>();

Dictionary<string, string> _dict = new Dictionary<string, string>();

....

void Add(string key, string val)

{

if (!dict.ContainsKey(key))

_dict.Add(key, val);

}

There may be two programming mistakes at once here. The first mistake has to do

with appending an element to a wrong dictionary, which may distort the program's

logic. The second deals with checking if the 'key' key is present in the 'dict'

dictionary instead of '_dict'. If '_dict' already contains a value associated with the

'key' key, an 'ArgumentException' will be thrown when executing the

'_dict.Add(key, val)' statement. There are two ways to fix this construct (both imply

that the key is tested for the same dictionary the new element is appended to):

Dictionary<string, string> dict = new Dictionary<string, string>();

Dictionary<string, string> _dict = new Dictionary<string, string>();

....

void Add1(string key, string val)

{

if (!_dict.ContainsKey(key))

_dict.Add(key, val);

}

...

void Add2(string key, string val)

{

Page 956: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

if (!dict.ContainsKey(key))

dict.Add(key, val);

}

V3125. The object was used after it was verified against null. Check lines: N1, N2.The analyzer detected a possible error that may lead to a null dereference.

The following situation was detected. An object is tested for 'null' first and then

used without such a check. It implies one of the two scenarios:

1) An exception will be thrown if the object turns out to be null.

2) The program runs correctly all the time, as the object is never null, and the check

is therefore unnecessary.

The first scenario is illustrated by the following example, where an exception is

likely to be thrown.

obj = Foo();

if (obj != null)

obj.Func1();

obj.Func2();

If the 'obj' object turns out to be null, evaluating the 'obj.Func2()' expression will

result in an exception. The analyzer displays a warning on this code, mentioning 2

lines. The first line is where the object is used; the second is where it is tested for

'null'.

Fixed code:

obj = Foo();

if (obj != null) {

obj.Func1();

Page 957: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

obj.Func2();

}

The second scenario is illustrated by the following example. The list is iterated in a

safe way, so the check can be omitted:

List<string> list = CreateNotEmptyList();

if (list == null || list.Count == 0) { .... }

foreach (string item in list) { .... }

This code works properly all the time. The 'list' list is never empty. However, the

analyzer failed to figure this out and produced a warning. To remove the warning,

delete the "if (list == null || list.Count == 0)" check: this operation is meaningless

and may confuse the programmer who will be maintaining the code.

Fixed code:

List<string> list = CreateNotEmptyList();

foreach (string item in list) { .... }

Instead of changing the code, you can add a special comment to suppress false

warnings. For the example above, you would have to use the following comment:

"obj.Foo(); //-V3125".

V3126. Type implementing IEquatable<T> interface does not override 'GetHashCode' method.The analyzer detected a user type that implements the 'IEquatable<T>' interface but

does not override the 'GetHashCode' method. This issue can cause incorrect output

when using such a type with, for example, methods from 'System.Linq.Enumerable',

such as 'Distinct', 'Except', 'Intersect', or 'Union'. The following example uses

method 'Distinct':

class Test : IEquatable<Test>

Page 958: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

{

private string _data;

public Test(string data)

{

_data = data;

}

public override string ToString()

{

return _data;

}

public bool Equals(Test other)

{

return _data.Equals(other._data);

}

}

static void Main()

{

var list = new List<Test>();

list.Add(new Test("ab"));

list.Add(new Test("ab"));

list.Add(new Test("a"));

list.Distinct().ToList().ForEach(item => Console.WriteLine(item));

}

Executing this program will result in the following output:

ab

ab

a

Even though the 'Test' type implements the 'IEquatable<Test>' interface (method

'Equals' is declared), it is not enough. When executed, the program fails to output

the expected result, and the collection contains duplicate elements. To eliminate this

defect, you need to override the 'GetHashCode' method in the declaration of the

'Test' type:

class Test : IEquatable<Test>

Page 959: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

{

private string _data;

public Test(string data)

{

_data = data;

}

public override string ToString()

{

return _data;

}

public bool Equals(Test other)

{

return _data.Equals(other._data);

}

public override int GetHashCode()

{

return _data.GetHashCode();

}

}

static void Main()

{

var list = new List<Test>();

list.Add(new Test("ab"));

list.Add(new Test("ab"));

list.Add(new Test("a"));

list.Distinct().ToList().ForEach(item => Console.WriteLine(item));

}

This time, the program will output the following:

ab

a

This result is correct: the collection contains unique elements only.

Page 960: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V3127. Two similar code fragments were found. Perhaps this is a typo and 'X' variable should be used instead of 'Y'.The analyzer detected a code fragment probably containing a typo. It is very likely

that this code was written by using the Copy-Paste technique.

The V3127 diagnostic looks for two adjacent code blocks similar in structure and

different in one variable, which is used several times in the first block but only once

in the second. This discrepancy suggests that the programmer forgot to change that

variable to the proper one. The diagnostic is designed to detect situations where a

code block is copied to make another block and the programmer forgets to change

the names of some of the variables in the resulting block.

Consider the following example:

if (x > 0)

{

Do1(x);

Do2(x);

}

if (y > 0)

{

Do1(y);

Do2(x); // <=

}

In the second block, the programmer must have intended to use variable 'y', not 'x':

if (x > 0)

{

Do1(x);

Do2(x);

Page 961: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

}

if (y > 0)

{

Do1(y);

Do2(y);

}

The following example is more complex.

....

if(erendlinen>239) erendlinen=239;

if(srendlinen>erendlinen) srendlinen=erendlinen;

if(erendlinep>239) erendlinep=239;

if(srendlinep>erendlinen) srendlinep=erendlinep; // <=

....

The defect in this example is not that easy to see. The variables have similar names,

which makes it much more difficult to diagnose the error. In the second block,

variable 'erendlinep' should be used instead of 'erendlinen'.

This is what the fixed code should look like:

....

if(erendlinen>239) erendlinen=239;

if(srendlinen>erendlinen) srendlinen=erendlinen;

if(erendlinep>239) erendlinep=239;

if(srendlinep>erendlinep) srendlinep=erendlinep; // <=

....

Obviously, 'erendlinen' and 'erendlinep' are poorly chosen variable names. An error

like that is almost impossible to catch when carrying out code review. Even with the

analyzer pointing at it directly, it is still not easy to notice. Therefore, take your time

and make sure to examine the code closely when encountering a V3127 warning.

Page 962: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

V3128. The field (property) is used before it is initialized in constructor.The analyzer detected a field (property) which is used before it is initialized in the

class constructor. Consider the following example:

class Test

{

List<int> mylist;

Test()

{

int count = mylist.Count; // <=

....

mylist = new List<int>();

}

}

In the constructor of the 'Test' class, property 'Count' of the list 'mylist' is accessed,

while the list itself is initialized later. Executing this code fragment would lead to a

null reference exception. To avoid it, the list must be initialized first, for example, at

declaration:

class Test

{

List<int> mylist = new List<int>();

Test()

{

int count = mylist.Count;

....

}

}

Here is another example:

class Test2

{

Page 963: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

int myint;

Test2(int param)

{

Foo(myint); // <=

....

myint = param;

}

}

In this code, field 'myint', whose default value is 0, is passed to the 'Foo' method.

This could be done on purpose, and then there is no error. However, executing code

like that can cause unexpected behavior in certain cases. A better solution is to

explicitly initialize the 'myint' field, even to a default value of 0:

class Test2

{

int myint = 0;

Test2(int param)

{

Foo(myint);

....

myint = param;

}

}

Now both the analyzer and other programmers can see that the code author took

care of the 'myint' field and initialized it.

V3129. The value of the captured variable will be overwritten on the next iteration of the loop in each instance of anonymous function that captures it.

Page 964: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer detected a potential error related to anonymous function closure of a

variable which is used as a loop iterator. At the compile time, the captured variable

will be wrapped in a container class, and from this point on only one instance of this

class will be passed to all anonymous functions for each iteration of a loop. Most

likely, the programmer will expect different values of an iterator inside every

anonymous function instead of the last value, which is an unobvious behavior and

may cause an error.

Let's take a closer look at this situation using the following example:

void Foo()

{

var actions = new List<Action>();

for (int i = 0; i < 10; i++)

{

actions.Add(() => Console.Write(i)); // <=

}

// SOME ACTION

actions.ForEach(x => x());

}

It is popularly believed that after executing the 'Foo' method, the numbers from 0 to

9 will be displayed on a console, because logically after the 'i' variable is enclosed

in anonymous function, after compiling an anonymous container class will be

created, and a value of the 'i' variable will be copied into one of its fields. But

actually, the number 10 will be printed on console 10 times. This is caused by the

fact that an anonymous container class will be created immediately after the 'i'

variable declaration and not before the anonymous function declaration. As a result,

all instances of the anonymous function on each loop enclose not the current value

of the iterator, but a reference to the anonymous container class, which contains the

last value of the iterator. It is also important to note that during the compilation, the

'i' variable declaration will be placed before the loop.

Page 965: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Fig.1. Comparison of C# and IL code

To avoid this error, ensure that the anonymous function encloses a local variable for

the current iteration. The corrected code would look like:

void Foo()

{

var actions = new List<Action>();

for (int i = 0; i < 10; i++)

{

var curIndex = i;

actions.Add(() => Console.Write(curIndex)); // <=

}

// SOME ACTION

actions.ForEach(x => x());

}

Therefore, we copy the value of the iterator to the local variable at each iteration

and, as we already know, the anonymous container class will be created during the

declaration of the enclosed variable, in our case - during the declaration of the

'curIndex' variable containing the current iterator value.

Let's examine a suspicious code fragment from the 'CodeContracts' project:

var tasks = new Task<int>[assemblies.Length];

Console.WriteLine("We start the analyses");

Page 966: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

for (var i = 0; i < tasks.Length; i++)

{

tasks[i] = new Task<int>(() => CallClousotEXE(i, args)); // <=

tasks[i].Start();

}

Console.WriteLine("We wait");

Task.WaitAll(tasks);

Despite the fact that the task (Task) is created and started during the same iteration,

it will not be started immediately. Therefore, the chances are high that the task will

start after the current iteration which will cause an error.

For example, if we run the given piece of code in a synthetic test we will see that all

tasks were started after the loop is complete, and, thereby, the 'i' variable in all tasks

is equal to the last iterator value (10).

The corrected code would look like:

var tasks = new Task<int>[10];

Console.WriteLine("We start the analyses");

for (var i = 0; i < tasks.Length; i++)

{

var index = i;

tasks[i] = new Task<int>(() => CallClousotEXE(index, args));

tasks[i].Start();

}

Console.WriteLine("We wait");

Task.WaitAll(tasks);

V3130. Priority of the '&&' operator is higher than that of the '||' operator. Possible missing parentheses.

Page 967: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer has detected a potential error: the priority of the '&&' logical operator

is higher than that of the '||' operator. Programmers often forget this, which causes

the result of a logical expression using these operators to be quite different from

what was expected.

Consider the following sample of incorrect code:

if (c == 'l' || c == 'L' && !token.IsKeyword)

{ .... }

The programmer most likely expected that equality of the 'c' variable and the value

'l' or 'L' would be checked first, and only then the '&&' operation would be

executed. But according to the C# operator precedence, the '&&' operation is

executed first, and only then, the '||' operation.

We recommend that you add parentheses in every expression that contains operators

you use rarely, or whenever you're not sure about the priorities. Even if parentheses

appear to be unnecessary, it's ok. At the same time, you code will become easier to

comprehend and less error-prone.

This is the fixed code:

if ((c == 'l' || c == 'L') && !token.IsKeyword)

{ .... }

How to get rid of a false positive in case it was this very sequence you actually

intended: first '&&', then '||'?

There are several ways:

1) Bad way. You may add the "//-V3130" comment into the corresponding line to

suppress the warning.

if (c == 'l' || c == 'L' && !token.IsKeyword) //-V3130

{ .... }

2) Good way. You may write additional parentheses:

Page 968: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

if (c == 'l' || (c == 'L' && !token.IsKeyword))

{ .... }

These will help other programmers understand that the code is correct.

V3131. The expression is checked for compatibility with type 'A' but is cast to type 'B'.The analyzer detected a likely error that has to do with checking if an expression is

compatible with one type and casting it to another type inside the body of the

conditional statement.

Consider the following example:

if (obj is A)

{

return (B)obj;

}

The programmer must have made a mistake, since a type conversion like that is

very likely to cause a bug. What was actually meant is either to check the

expression for type 'B' or cast it to type 'A'.

This is what the correct version could look like:

if (obj is B)

{

return (B)obj;

}

V3132. A terminal null is present inside a string. '\0xNN' character

Page 969: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

sequence was encountered. Probably meant: '\xNN'.The analyzer detected a likely error that has to do with the presence of a terminal

null inside a string.

This error is typically caused by a typo. For example, the sequence "\0x0A" will be

interpreted as the following four-byte sequence: { '\0', 'x', '0', 'A' }.

If you want to specify a character code in hexadecimal format, you need to write the

'x' character immediately after the '\' character. If you write the sequence "\0"

instead, the compiler will interpret it as zero (in octal format). See also:

MSDN. C Character Constants.

MSDN. Escape Sequences.

Consider the following example:

String s = "string\0x0D\0x0A";

When trying to print this string, the newline escape characters will not be processed.

Printing functions will stop at the null terminator, '\0'. To fix this error, the sequence

"\0x0D\0x0A" needs to be replaced with "\x0D\x0A".

Fixed code:

String s = "string\x0D\x0A";

V3133. Postfix increment/decrement is meaningless because this variable is overwritten.

Page 970: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The analyzer detected a likely error that has to do with using a postfix increment or

decrement in an assignment to the same variable.

Consider the following example:

int i = 5;

// Some code

i = i++;

The increment operation here will not affect the expression result and the 'i' variable

will be assigned the value 5 after executing this code.

This is explained by the fact that postfix increment and decrement operations are

executed after evaluating the right operand of the assignment operator, while the

result of the assignment is temporarily cached and is assigned later to the left part of

the expression after the increment/decrement operation has executed. Therefore, the

result of the increment/decrement is overwritten with the result of the whole

expression.

To better understand the mechanics of this behavior, consider the IL code of the

example above:

-======- START OF OPERATION "int i = 5" -======-

// Declaring local variable 'i'

// Current stack => []

.locals init ([0] int32 i)

// Passing value 5 to the top of stack

// Current stack => [5]

IL_0001: ldc.i4.5

// Assigning value 5 from stack to variable 'i'

// Current stack => []

IL_0002: stloc.0

-======- END OF OPERATION "int i = 5" -======-

-======- START OF OPERATION "i = i++" -======-

Page 971: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

// Passing value of variable 'i' to the top of stack

// Current stack => [5]

IL_0003: ldloc.0

-======- START OF OPERATION "i++" -======-

// Copying top value on stack

// Current stack => [5, 5]

IL_0004: dup

// Passing value 1 to the top of stack

// Current stack => [1, 5, 5]

IL_0005: ldc.i4.1

// Adding two top values from stack (5 + 1)

// Result (6) is passed to the top of stack

// Current stack => [6, 5]

IL_0006: add

// Assigning value 6 from stack to variable 'i'

// Current stack => [5]

IL_0007: stloc.0

-======- END OF OPERATION "i++" -======-

// Assigning value 5 from stack to variable 'i'

// Current stack => []

IL_0008: stloc.0

-======- END OF OPERATION "i = i++" -======-

As for the correct version of this code, it can look differently depending on the

intended behavior.

This error may be a typo and the programmer unintentionally wrote variable 'i'

twice in the assignment statement. Then the correct version could look as follows:

int i = 5;

// Some code

q = i++;

Page 972: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Another scenario is that the programmer did not know that the postfix increment

operator adds one to the value of the variable but returns its initial value. Then the

assignment statement is redundant and the fixed code could look like this:

int i = 5;

// Some code

i++;

This example may look more like a synthetic test and you may think nobody really

writes code that way, but this error can actually be found in serious projects. Here is

an example taken from MSBuild project.

_parsePoint =

ScanForPropertyExpressionEnd(expression, parsePoint++);

Incrementing the '_parsePoint' variable is pointless because the increment operation

will be executed after passing the initial value of this variable to method

'ScanForPropertyExpressionEnd' and will not affect the result of this method in any

way. The programmer must have confused postfix and prefix increments. In that

case, the correct version of this code could look as follows:

_parsePoint =

ScanForPropertyExpressionEnd(expression, ++_parsePoint);

V3134. Shift by N bits is greater than the size of type.The analyzer detected a likely error that has to do with shifting an whole number

value by 'N' bits, 'N' being greater than the length of this type in bits.

Consider the following example:

UInt32 x = ....;

UInt32 y = ....;

UInt64 result = (x << 32) + y;

Page 973: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The programmer intended to form a 64-bit value from two 32-bit ones by shifting 'x'

by 32 bits and adding the most significant and the least significant parts. However,

as 'x' is a 32-bit value at the moment when the shift operation is performed, shifting

by 32 bits will be equivalent to shifting by 0 bits, which will lead to an incorrect

result.

This is what the fixed version of the code could look like:

UInt32 x = ....;

UInt32 y = ....;

UInt64 result = ((UInt64)x << 32) + y;

Now consider the following example from a real project:

static long GetLong(byte[] bits)

{

return ((bits[0] & 0xff) << 0)

| ((bits[1] & 0xff) << 8)

| ((bits[2] & 0xff) << 16)

| ((bits[3] & 0xff) << 24)

| ((bits[4] & 0xff) << 32)

| ((bits[5] & 0xff) << 40)

| ((bits[6] & 0xff) << 48)

| ((bits[7] & 0xff) << 56);

}

In the 'GetLong' method, an array of bytes is cast to a 64-bit value. Since bitwise

shift operations are defined only for 32-bit and 64-bit values, each byte will be

implicitly cast to 'Int32'. The bitwise shift range for a 32-bit value is [0..31], so the

cast will be performed correctly only for the first 4 bytes of the array.

If the byte array was formed from a 64-bit value (for example 'Int64.MaxValue'),

then casting the array back to Int64 using this method will result in an error if the

original value was beyond the range [Int32.MinValue....Int32.MaxValue].

Page 974: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

For a better understanding of this, let’s see what happens when this code is executed

over the value '289077008695033855' as an example. When cast to an array of

bytes, this value will look as follows:

289077008695033855 => [255, 255, 255, 255, 1, 2, 3, 4]

After passing this array to method 'GetLong', each byte will be implicitly cast to

Int32 before executing the shift operation. Let’s shift each element separately, so we

can see where the problem is:

As you can see, each shift is performed over a 32-bit value, which causes range

overlapping and, therefore, leads to an incorrect result. This happens because when

attempting to shift a 32-bit value by more than 32 bits, they are shifted in a circle

(shifting by 32, 40, 48, and 56 bits is equivalent to shifting by 0, 8, 16, and 24 bits

respectively).

The fixed version of the code above could look like this:

static long GetLong(byte[] bits)

{

return ((long)(bits[0] & 0xff) << 0)

| ((long)(bits[1] & 0xff) << 8)

| ((long)(bits[2] & 0xff) << 16)

| ((long)(bits[3] & 0xff) << 24)

| ((long)(bits[4] & 0xff) << 32)

| ((long)(bits[5] & 0xff) << 40)

| ((long)(bits[6] & 0xff) << 48)

| ((long)(bits[7] & 0xff) << 56);

}

Page 975: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

If we now examine each shift operation separately, we will see that the shifts are

performed over 64-bit values, which prevents range overlapping.

Credits and acknowledgementsWindows, Visual Studio, Visual C++ are either registered trademarks or trademarks

of Microsoft Corporation in the United States and/or other countries.

Embarcadero, the Embarcadero Technologies logos and all other Embarcadero

Technologies product or service names are trademarks, servicemarks, and/or

registered trademarks of Embarcadero Technologies, Inc. and are protected by the

laws of the United States and other countries. All other trademarks are property of

their respective owners.

Other product and company names mentioned herein may be the trademarks of their

respective owners.

PVS-Studio uses SourceGrid control (sourcegrid.codeplex.com). Bellow you can

read Source Grid License.

SourceGrid LICENSE (MIT style)

Copyright (c) 2009 Davide Icardi

Permission is hereby granted, free of charge, to any person obtaining a copy of this

software and associated documentation files (the "Software"), to deal in the

Software without restriction, including without limitation the rights to use, copy,

modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,

and to permit persons to whom the Software is furnished to do so, subject to the

following conditions:

Page 976: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The above copyright notice and this permission notice shall be included in all copies

or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY

KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE

WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR

PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE

AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,

DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF

CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN

CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS

IN THE SOFTWARE.

Portions of PVS-Studio are based in part of OpenC++. Bellow you can read

OpenC++ Copyright Notice.

*** Copyright Notice

Copyright (c) 1995, 1996 Xerox Corporation.

All Rights Reserved.

Use and copying of this software and preparation of derivative works based upon

this software are permitted. Any copy of this software or of any derivative work

must include the above copyright notice of Xerox Corporation, this paragraph and

the one after it. Any distribution of this software or derivative works must comply

with all applicable United States export control laws.

This software is made available AS IS, and XEROX CORPORATION

DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING

WITHOUT LIMITATION THE IMPLIED WARRANTIES OF

MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND

NOTWITHSTANDING ANY OTHER PROVISION CONTAINED HEREIN,

ANY LIABILITY FOR DAMAGES RESULTING FROM THE SOFTWARE OR

ITS USE IS EXPRESSLY DISCLAIMED, WHETHER ARISING IN

CONTRACT, TORT (INCLUDING NEGLIGENCE) OR STRICT LIABILITY,

Page 977: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

EVEN IF XEROX CORPORATION IS ADVISED OF THE POSSIBILITY OF

SUCH DAMAGES.

*** Copyright Notice

Copyright (C) 1997-2001 Shigeru Chiba, Tokyo Institute of Technology.

Permission to use, copy, distribute and modify this software and its documentation

for any purpose is hereby granted without fee, provided that the above copyright

notice appear in all copies and that both that copyright notice and this permission

notice appear in supporting documentation.

Shigeru Chiba makes no representations about the suitability of this software for

any purpose. It is provided "as is" without express or implied warranty.

*** Copyright Notice

Permission to use, copy, distribute and modify this software and its documentation

for any purpose is hereby granted without fee, provided that the above copyright

notice appear in all copies and that both that copyright notice and this permission

notice appear in supporting documentation. Other Contributors make no

representations about the suitability of this software for any purpose. It is provided

"as is" without express or implied warranty.

2001-2003 (C) Copyright by Other Contributors.

PVS-Studio can use Clang as preprocessor. Read Clang/LLVM license:

===========================================================

===================

LLVM Release License

===========================================================

===================

University of Illinois/NCSA

Open Source License

Page 978: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Copyright (c) 2007-2011 University of Illinois at Urbana-Champaign.

All rights reserved.

Developed by:

LLVM Team

University of Illinois at Urbana-Champaign

http://llvm.org

Permission is hereby granted, free of charge, to any person obtaining a copy of this

software and associated documentation files (the "Software"), to deal with the

Software without restriction, including without limitation the rights to use, copy,

modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,

and to permit persons to whom the Software is furnished to do so, subject to the

following conditions:

* Redistributions of source code must retain the above copyright notice, this list of

conditions and the following disclaimers.

* Redistributions in binary form must reproduce the above copyright notice, this list

of conditions and the following disclaimers in the documentation and/or other

materials provided with the distribution.

* Neither the names of the LLVM Team, University of Illinois at Urbana-

Champaign, nor the names of its contributors may be used to endorse or promote

products derived from this Software without specific prior written permission.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY

KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE

WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR

PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE

CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY

CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF

CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN

Page 979: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS

WITH THE SOFTWARE.

===========================================================

===================

The LLVM software contains code written by third parties. Such software will have

its own individual LICENSE.TXT file in the directory in which it appears. This file

will describe the copyrights, license, and restrictions which apply to that code.

The disclaimer of warranty in the University of Illinois Open Source License

applies to all code in the LLVM Distribution, and nothing in any of the other

licenses gives permission to use the names of the LLVM Team or the University of

Illinois to endorse or promote products derived from this Software.

The following pieces of software have additional or alternate copyrights, licenses,

and/or restrictions:

Program Directory

------- ---------

<none yet>

Standalone uses ScintillaNET. This is ScintillaNET license.

ScintillaNET is based on the Scintilla component by Neil Hodgson.

ScintillaNET is released on this same license.

The ScintillaNET bindings are Copyright 2002-2006 by Garrett Serack

<[email protected]>

All Rights Reserved

Permission to use, copy, modify, and distribute this software and its documentation

for any purpose and without fee is hereby granted, provided that the above

copyright notice appear in all copies and that both that copyright notice and this

permission notice appear in supporting documentation.

Page 980: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

GARRETT SERACK AND ALL EMPLOYERS PAST AND PRESENT

DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,

INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND

FITNESS, IN NO EVENT SHALL GARRETT SERACK AND ALL

EMPLOYERS PAST AND PRESENT BE LIABLE FOR ANY SPECIAL,

INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES

WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,

WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER

TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE

USE OR PERFORMANCE OF THIS SOFTWARE.

The license for Scintilla is as follows:

-----------------------------------------------------------------------

Copyright 1998-2006 by Neil Hodgson <[email protected]>

All Rights Reserved

Permission to use, copy, modify, and distribute this software and its documentation

for any purpose and without fee is hereby granted, provided that the above

copyright notice appear in all copies and that both that copyright notice and this

permission notice appear in supporting documentation.

NEIL HODGSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS

SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF

MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL NEIL

HODGSON BE LIABLE FOR ANY SPECIAL, INDIRECT OR

CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER

RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN

ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,

ARISING OUT OF OR IN CONNECTION WITH THE USE OR

PERFORMANCE OF THIS SOFTWARE.

Standalone uses DockPanel_Suite. This is DockPanel_Suite license:

Page 981: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

The MIT License

Copyright (c) 2007 Weifen Luo (email: [email protected])

Permission is hereby granted, free of charge, to any person obtaining a copy of this

software and associated documentation files (the "Software"), to deal in the

Software without restriction, including without limitation the rights to use, copy,

modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,

and to permit persons to whom the Software is furnished to do so, subject to the

following conditions:

The above copyright notice and this permission notice shall be included in all copies

or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY

KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE

WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR

PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE

AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,

DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF

CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN

CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS

IN THE SOFTWARE.

PVS-Studio uses Font Awesome. Bellow you can read Font Awesome License.

This Font Software is licensed under the SIL Open Font License, Version 1.1.

This license is copied below, and is also available with a FAQ at:

http://scripts.sil.org/OFL

-----------------------------------------------------------

SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007

-----------------------------------------------------------

Page 982: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

PREAMBLE

The goals of the Open Font License (OFL) are to stimulate worldwide development

of collaborative font projects, to support the font creation efforts of academic and

linguistic communities, and to provide a free and open framework in which fonts

may be shared and improved in partnership with others.

The OFL allows the licensed fonts to be used, studied, modified and redistributed

freely as long as they are not sold by themselves. The fonts, including any

derivative works, can be bundled, embedded, redistributed and/or sold with any

software provided that any reserved names are not used by derivative works. The

fonts and derivatives, however, cannot be released under any other type of license.

The requirement for fonts to remain under this license does not apply to any

document created using the fonts or their derivatives.

DEFINITIONS

"Font Software" refers to the set of files released by the Copyright Holder(s) under

this license and clearly marked as such. This may include source files, build scripts

and documentation. "Reserved Font Name" refers to any names specified as such

after the copyright statement(s).

"Original Version" refers to the collection of Font Software components as

distributed by the Copyright Holder(s).

"Modified Version" refers to any derivative made by adding to, deleting, or

substituting -- in part or in whole -- any of the components of the Original Version,

by changing formats or by porting the Font Software to a new environment.

"Author" refers to any designer, engineer, programmer, technical writer or other

person who contributed to the Font Software.

PERMISSION & CONDITIONS

Permission is hereby granted, free of charge, to any person obtaining a copy of the

Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell

Page 983: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

modified and unmodified copies of the Font Software, subject to the following

conditions:

1) Neither the Font Software nor any of its individual components, in Original or

Modified Versions, may be sold by itself.

2) Original or Modified Versions of the Font Software may be bundled,

redistributed and/or sold with any software, provided that each copy contains the

above copyright notice and this license. These can be included either as stand-alone

text files, human-readable headers or in the appropriate machine-readable metadata

fields within text or binary files as long as those fields can be easily viewed by the

user.

3) No Modified Version of the Font Software may use the Reserved Font Name(s)

unless explicit written permission is granted by the corresponding Copyright

Holder. This restriction only applies to the primary font name as presented to the

users.

4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software

shall not be used to promote, endorse or advertise any Modified Version, except to

acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or

with their explicit written permission.

5) The Font Software, modified or unmodified, in part or in whole, must be

distributed entirely under this license, and must not be distributed under any other

license. The requirement for fonts to remain under this license does not apply to any

document created using the Font Software.

TERMINATION

This license becomes null and void if any of the above conditions are not met.

DISCLAIMER

THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF

ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO

ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A

Page 984: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT,

PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE

COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR

OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT,

INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN

ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF

THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER

DEALINGS IN THE FONT SOFTWARE.

PVS-Studio uses GNU C Library. GNU C Library is licensed under GNU LESSER

GENERAL PUBLIC LICENSE Version 2.1. PVS-Studio provides object code in

accordance with section 6.a of GNU LESSER GENERAL PUBLIC LICENSE.

Bellow you can read GNU C Library License.

GNU LESSER GENERAL PUBLIC LICENSE

Version 2.1, February 1999

Copyright (C) 1991, 1999 Free Software Foundation, Inc.

51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

Everyone is permitted to copy and distribute verbatim copies

of this license document, but changing it is not allowed.

[This is the first released version of the Lesser GPL. It also counts

as the successor of the GNU Library Public License, version 2, hence

the version number 2.1.]

Preamble

The licenses for most software are designed to take away your freedom to share and

change it. By contrast, the GNU General Public Licenses are intended to guarantee

your freedom to share and change free software--to make sure the software is free

for all its users.

Page 985: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

This license, the Lesser General Public License, applies to some specially

designated software packages--typically libraries--of the Free Software Foundation

and other authors who decide to use it. You can use it too, but we suggest you first

think carefully about whether this license or the ordinary General Public License is

the better strategy to use in any particular case, based on the explanations below.

When we speak of free software, we are referring to freedom of use, not price. Our

General Public Licenses are designed to make sure that you have the freedom to

distribute copies of free software (and charge for this service if you wish); that you

receive source code or can get it if you want it; that you can change the software

and use pieces of it in new free programs; and that you are informed that you can do

these things.

To protect your rights, we need to make restrictions that forbid distributors to deny

you these rights or to ask you to surrender these rights. These restrictions translate

to certain responsibilities for you if you distribute copies of the library or if you

modify it.

For example, if you distribute copies of the library, whether gratis or for a fee, you

must give the recipients all the rights that we gave you. You must make sure that

they, too, receive or can get the source code. If you link other code with the library,

you must provide complete object files to the recipients, so that they can relink them

with the library after making changes to the library and recompiling it. And you

must show them these terms so they know their rights.

We protect your rights with a two-step method: (1) we copyright the library, and (2)

we offer you this license, which gives you legal permission to copy, distribute

and/or modify the library.

To protect each distributor, we want to make it very clear that there is no warranty

for the free library. Also, if the library is modified by someone else and passed on,

the recipients should know that what they have is not the original version, so that

the original author's reputation will not be affected by problems that might be

introduced by others.

Page 986: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Finally, software patents pose a constant threat to the existence of any free program.

We wish to make sure that a company cannot effectively restrict the users of a free

program by obtaining a restrictive license from a patent holder. Therefore, we insist

that any patent license obtained for a version of the library must be consistent with

the full freedom of use specified in this license.

Most GNU software, including some libraries, is covered by the ordinary GNU

General Public License. This license, the GNU Lesser General Public License,

applies to certain designated libraries, and is quite different from the ordinary

General Public License. We use this license for certain libraries in order to permit

linking those libraries into non-free programs.

When a program is linked with a library, whether statically or using a shared

library, the combination of the two is legally speaking a combined work, a

derivative of the original library. The ordinary General Public License therefore

permits such linking only if the entire combination fits its criteria of freedom. The

Lesser General Public License permits more lax criteria for linking other code with

the library.

We call this license the "Lesser" General Public License because it does Less to

protect the user's freedom than the ordinary General Public License. It also provides

other free software developers Less of an advantage over competing non-free

programs. These disadvantages are the reason we use the ordinary General Public

License for many libraries. However, the Lesser license provides advantages in

certain special circumstances.

For example, on rare occasions, there may be a special need to encourage the widest

possible use of a certain library, so that it becomes a de-facto standard. To achieve

this, non-free programs must be allowed to use the library. A more frequent case is

that a free library does the same job as widely used non-free libraries. In this case,

there is little to gain by limiting the free library to free software only, so we use the

Lesser General Public License.

In other cases, permission to use a particular library in non-free programs enables a

greater number of people to use a large body of free software. For example,

Page 987: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

permission to use the GNU C Library in non-free programs enables many more

people to use the whole GNU operating system, as well as its variant, the

GNU/Linux operating system.

Although the Lesser General Public License is Less protective of the users' freedom,

it does ensure that the user of a program that is linked with the Library has the

freedom and the wherewithal to run that program using a modified version of the

Library.

The precise terms and conditions for copying, distribution and modification follow.

Pay close attention to the difference between a "work based on the library" and a

"work that uses the library". The former contains code derived from the library,

whereas the latter must be combined with the library in order to run.

TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND

MODIFICATION

0. This License Agreement applies to any software library or other program which

contains a notice placed by the copyright holder or other authorized party saying it

may be distributed under the terms of this Lesser General Public License (also

called "this License"). Each licensee is addressed as "you".

A "library" means a collection of software functions and/or data prepared so as to be

conveniently linked with application programs (which use some of those functions

and data) to form executables.

The "Library", below, refers to any such software library or work which has been

distributed under these terms. A "work based on the Library" means either the

Library or any derivative work under copyright law: that is to say, a work

containing the Library or a portion of it, either verbatim or with modifications

and/or translated straightforwardly into another language. (Hereinafter, translation

is included without limitation in the term "modification".)

"Source code" for a work means the preferred form of the work for making

modifications to it. For a library, complete source code means all the source code

Page 988: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

for all modules it contains, plus any associated interface definition files, plus the

scripts used to control compilation and installation of the library.

Activities other than copying, distribution and modification are not covered by this

License; they are outside its scope. The act of running a program using the Library

is not restricted, and output from such a program is covered only if its contents

constitute a work based on the Library (independent of the use of the Library in a

tool for writing it). Whether that is true depends on what the Library does and what

the program that uses the Library does.

1. You may copy and distribute verbatim copies of the Library's complete source

code as you receive it, in any medium, provided that you conspicuously and

appropriately publish on each copy an appropriate copyright notice and disclaimer

of warranty; keep intact all the notices that refer to this License and to the absence

of any warranty; and distribute a copy of this License along with the Library.

You may charge a fee for the physical act of transferring a copy, and you may at

your option offer warranty protection in exchange for a fee.

2. You may modify your copy or copies of the Library or any portion of it, thus

forming a work based on the Library, and copy and distribute such modifications or

work under the terms of Section 1 above, provided that you also meet all of these

conditions:

a) The modified work must itself be a software library.

b) You must cause the files modified to carry prominent notices stating that you

changed the files and the date of any change.

c) You must cause the whole of the work to be licensed at no charge to all third

parties under the terms of this License.

d) If a facility in the modified Library refers to a function or a table of data to be

supplied by an application program that uses the facility, other than as an argument

passed when the facility is invoked, then you must make a good faith effort to

ensure that, in the event an application does not supply such function or table, the

Page 989: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

facility still operates, and performs whatever part of its purpose remains

meaningful.

(For example, a function in a library to compute square roots has a purpose that is

entirely well-defined independent of the application. Therefore, Subsection 2d

requires that any application-supplied function or table used by this function must

be optional: if the application does not supply it, the square root function must still

compute square roots.)

These requirements apply to the modified work as a whole. If identifiable sections

of that work are not derived from the Library, and can be reasonably considered

independent and separate works in themselves, then this License, and its terms, do

not apply to those sections when you distribute them as separate works. But when

you distribute the same sections as part of a whole which is a work based on the

Library, the distribution of the whole must be on the terms of this License, whose

permissions for other licensees extend to the entire whole, and thus to each and

every part regardless of who wrote it.

Thus, it is not the intent of this section to claim rights or contest your rights to work

written entirely by you; rather, the intent is to exercise the right to control the

distribution of derivative or collective works based on the Library.

In addition, mere aggregation of another work not based on the Library with the

Library (or with a work based on the Library) on a volume of a storage or

distribution medium does not bring the other work under the scope of this License.

3. You may opt to apply the terms of the ordinary GNU General Public License

instead of this License to a given copy of the Library. To do this, you must alter all

the notices that refer to this License, so that they refer to the ordinary GNU General

Public License, version 2, instead of to this License. (If a newer version than

version 2 of the ordinary GNU General Public License has appeared, then you can

specify that version instead if you wish.) Do not make any other change in these

notices.

Page 990: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

Once this change is made in a given copy, it is irreversible for that copy, so the

ordinary GNU General Public License applies to all subsequent copies and

derivative works made from that copy.

This option is useful when you wish to copy part of the code of the Library into a

program that is not a library.

4. You may copy and distribute the Library (or a portion or derivative of it, under

Section 2) in object code or executable form under the terms of Sections 1 and 2

above provided that you accompany it with the complete corresponding machine-

readable source code, which must be distributed under the terms of Sections 1 and 2

above on a medium customarily used for software interchange.

If distribution of object code is made by offering access to copy from a designated

place, then offering equivalent access to copy the source code from the same place

satisfies the requirement to distribute the source code, even though third parties are

not compelled to copy the source along with the object code.

5. A program that contains no derivative of any portion of the Library, but is

designed to work with the Library by being compiled or linked with it, is called a

"work that uses the Library". Such a work, in isolation, is not a derivative work of

the Library, and therefore falls outside the scope of this License.

However, linking a "work that uses the Library" with the Library creates an

executable that is a derivative of the Library (because it contains portions of the

Library), rather than a "work that uses the library". The executable is therefore

covered by this License. Section 6 states terms for distribution of such executables.

When a "work that uses the Library" uses material from a header file that is part of

the Library, the object code for the work may be a derivative work of the Library

even though the source code is not. Whether this is true is especially significant if

the work can be linked without the Library, or if the work is itself a library. The

threshold for this to be true is not precisely defined by law.

If such an object file uses only numerical parameters, data structure layouts and

accessors, and small macros and small inline functions (ten lines or less in length),

Page 991: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

then the use of the object file is unrestricted, regardless of whether it is legally a

derivative work. (Executables containing this object code plus portions of the

Library will still fall under Section 6.)

Otherwise, if the work is a derivative of the Library, you may distribute the object

code for the work under the terms of Section 6. Any executables containing that

work also fall under Section 6, whether or not they are linked directly with the

Library itself.

6. As an exception to the Sections above, you may also combine or link a "work that

uses the Library" with the Library to produce a work containing portions of the

Library, and distribute that work under terms of your choice, provided that the terms

permit modification of the work for the customer's own use and reverse engineering

for debugging such modifications.

You must give prominent notice with each copy of the work that the Library is used

in it and that the Library and its use are covered by this License. You must supply a

copy of this License. If the work during execution displays copyright notices, you

must include the copyright notice for the Library among them, as well as a reference

directing the user to the copy of this License. Also, you must do one of these things:

a) Accompany the work with the complete corresponding machine-readable source

code for the Library including whatever changes were used in the work (which must

be distributed under Sections 1 and 2 above); and, if the work is an executable

linked with the Library, with the complete machine-readable "work that uses the

Library", as object code and/or source code, so that the user can modify the Library

and then relink to produce a modified executable containing the modified Library.

(It is understood that the user who changes the contents of definitions files in the

Library will not necessarily be able to recompile the application to use the modified

definitions.)

b) Use a suitable shared library mechanism for linking with the Library. A suitable

mechanism is one that (1) uses at run time a copy of the library already present on

the user's computer system, rather than copying library functions into the

executable, and (2) will operate properly with a modified version of the library, if

Page 992: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

the user installs one, as long as the modified version is interface-compatible with

the version that the work was made with.

c) Accompany the work with a written offer, valid for at least three years, to give

the same user the materials specified in Subsection 6a, above, for a charge no more

than the cost of performing this distribution.

d) If distribution of the work is made by offering access to copy from a designated

place, offer equivalent access to copy the above specified materials from the same

place.

e) Verify that the user has already received a copy of these materials or that you

have already sent this user a copy.

For an executable, the required form of the "work that uses the Library" must

include any data and utility programs needed for reproducing the executable from it.

However, as a special exception, the materials to be distributed need not include

anything that is normally distributed (in either source or binary form) with the major

components (compiler, kernel, and so on) of the operating system on which the

executable runs, unless that component itself accompanies the executable.

It may happen that this requirement contradicts the license restrictions of other

proprietary libraries that do not normally accompany the operating system. Such a

contradiction means you cannot use both them and the Library together in an

executable that you distribute.

7. You may place library facilities that are a work based on the Library side-by-side

in a single library together with other library facilities not covered by this License,

and distribute such a combined library, provided that the separate distribution of the

work based on the Library and of the other library facilities is otherwise permitted,

and provided that you do these two things:

a) Accompany the combined library with a copy of the same work based on the

Library, uncombined with any other library facilities. This must be distributed under

the terms of the Sections above.

Page 993: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

b) Give prominent notice with the combined library of the fact that part of it is a

work based on the Library, and explaining where to find the accompanying

uncombined form of the same work.

8. You may not copy, modify, sublicense, link with, or distribute the Library except

as expressly provided under this License. Any attempt otherwise to copy, modify,

sublicense, link with, or distribute the Library is void, and will automatically

terminate your rights under this License. However, parties who have received

copies, or rights, from you under this License will not have their licenses terminated

so long as such parties remain in full compliance.

9. You are not required to accept this License, since you have not signed it.

However, nothing else grants you permission to modify or distribute the Library or

its derivative works. These actions are prohibited by law if you do not accept this

License. Therefore, by modifying or distributing the Library (or any work based on

the Library), you indicate your acceptance of this License to do so, and all its terms

and conditions for copying, distributing or modifying the Library or works based on

it.

10. Each time you redistribute the Library (or any work based on the Library), the

recipient automatically receives a license from the original licensor to copy,

distribute, link with or modify the Library subject to these terms and conditions.

You may not impose any further restrictions on the recipients' exercise of the rights

granted herein. You are not responsible for enforcing compliance by third parties

with this License.

11. If, as a consequence of a court judgment or allegation of patent infringement or

for any other reason (not limited to patent issues), conditions are imposed on you

(whether by court order, agreement or otherwise) that contradict the conditions of

this License, they do not excuse you from the conditions of this License. If you

cannot distribute so as to satisfy simultaneously your obligations under this License

and any other pertinent obligations, then as a consequence you may not distribute

the Library at all. For example, if a patent license would not permit royalty-free

redistribution of the Library by all those who receive copies directly or indirectly

Page 994: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

through you, then the only way you could satisfy both it and this License would be

to refrain entirely from distribution of the Library.

If any portion of this section is held invalid or unenforceable under any particular

circumstance, the balance of the section is intended to apply, and the section as a

whole is intended to apply in other circumstances.

It is not the purpose of this section to induce you to infringe any patents or other

property right claims or to contest validity of any such claims; this section has the

sole purpose of protecting the integrity of the free software distribution system

which is implemented by public license practices. Many people have made

generous contributions to the wide range of software distributed through that system

in reliance on consistent application of that system; it is up to the author/donor to

decide if he or she is willing to distribute software through any other system and a

licensee cannot impose that choice.

This section is intended to make thoroughly clear what is believed to be a

consequence of the rest of this License.

12. If the distribution and/or use of the Library is restricted in certain countries

either by patents or by copyrighted interfaces, the original copyright holder who

places the Library under this License may add an explicit geographical distribution

limitation excluding those countries, so that distribution is permitted only in or

among countries not thus excluded. In such case, this License incorporates the

limitation as if written in the body of this License.

13. The Free Software Foundation may publish revised and/or new versions of the

Lesser General Public License from time to time. Such new versions will be similar

in spirit to the present version, but may differ in detail to address new problems or

concerns.

Each version is given a distinguishing version number. If the Library specifies a

version number of this License which applies to it and "any later version", you have

the option of following the terms and conditions either of that version or of any later

version published by the Free Software Foundation. If the Library does not specify

Page 995: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

a license version number, you may choose any version ever published by the Free

Software Foundation.

14. If you wish to incorporate parts of the Library into other free programs whose

distribution conditions are incompatible with these, write to the author to ask for

permission. For software which is copyrighted by the Free Software Foundation,

write to the Free Software Foundation; we sometimes make exceptions for this. Our

decision will be guided by the two goals of preserving the free status of all

derivatives of our free software and of promoting the sharing and reuse of software

generally.

NO WARRANTY

15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS

NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY

APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING

THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE

LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER

EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE

IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A

PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND

PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY

PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY

SERVICING, REPAIR OR CORRECTION.

16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED

TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER

PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS

PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING

ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES

ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY

(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING

RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD

PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY

Page 996: sq-software.comsq-software.com/.../uploads/2017/05/PVS-Handbook.docx  · Web viewThe compiler could delete the 'memset' function call, ... Analysis aborted by timeout ... After deciding

OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS

BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

END OF TERMS AND CONDITIONS