Merge master into next.

Conflict in `cpp/ql/test/library-tests/sideEffects/functions/sideEffects.expected`,
resolved by accepting test output (combining changes).
This commit is contained in:
Aditya Sharad
2018-12-12 17:22:16 +00:00
345 changed files with 9800 additions and 2436 deletions

View File

@@ -25,4 +25,5 @@ where v.isStatic()
and not v instanceof MemberVariable
and not declarationHasSideEffects(v)
and not v.getAnAttribute().hasName("used")
and not v.getAnAttribute().hasName("unused")
select v, "Static variable " + v.getName() + " is never read"

View File

@@ -4,6 +4,8 @@
* @kind problem
* @id cpp/jpl-c/exit-nonterminating-loop
* @problem.severity warning
* @tags correctness
* external/jpl
*/
import cpp

View File

@@ -5,6 +5,8 @@
* @kind problem
* @id cpp/jpl-c/loop-bounds
* @problem.severity warning
* @tags correctness
* external/jpl
*/
import cpp

View File

@@ -4,6 +4,10 @@
* @kind problem
* @id cpp/jpl-c/recursion
* @problem.severity warning
* @tags maintainability
* readability
* testability
* external/jpl
*/
import cpp

View File

@@ -3,7 +3,9 @@
* @description Dynamic memory allocation (using malloc() or calloc()) should be confined to the initialization routines of a program.
* @kind problem
* @id cpp/jpl-c/heap-memory
* @problem.severity warning
* @problem.severity recommendation
* @tags resources
* external/jpl
*/
import cpp

View File

@@ -4,6 +4,9 @@
* @kind problem
* @id cpp/jpl-c/thread-safety
* @problem.severity warning
* @tags correctness
* concurrency
* external/jpl
*/
import cpp

View File

@@ -4,6 +4,9 @@
* @kind problem
* @id cpp/jpl-c/avoid-nested-semaphores
* @problem.severity warning
* @tags correctness
* concurrency
* external/jpl
*/
import Semaphores

View File

@@ -3,7 +3,9 @@
* @description The use of semaphores or locks to access shared data should be avoided.
* @kind problem
* @id cpp/jpl-c/avoid-semaphores
* @problem.severity warning
* @problem.severity recommendation
* @tags concurrency
* external/jpl
*/
import Semaphores

View File

@@ -4,6 +4,9 @@
* @kind problem
* @id cpp/jpl-c/out-of-order-locks
* @problem.severity warning
* @tags correctness
* concurrency
* external/jpl
*/
import Semaphores

View File

@@ -4,6 +4,9 @@
* @kind problem
* @id cpp/jpl-c/release-locks-when-acquired
* @problem.severity warning
* @tags correctness
* concurrency
* external/jpl
*/
import Semaphores

View File

@@ -4,6 +4,9 @@
* @kind problem
* @id cpp/jpl-c/simple-control-flow-goto
* @problem.severity warning
* @tags maintainability
* readability
* external/jpl
*/
import cpp

View File

@@ -4,6 +4,10 @@
* @kind problem
* @id cpp/jpl-c/simple-control-flow-jmp
* @problem.severity warning
* @tags correctness
* portability
* readability
* external/jpl
*/
import cpp

View File

@@ -3,7 +3,10 @@
* @description In an enumerator list, the = construct should not be used to explicitly initialize members other than the first, unless all items are explicitly initialized. An exception is the pattern to use the last element of an enumerator list to get the number of possible values.
* @kind problem
* @id cpp/jpl-c/enum-initialization
* @problem.severity warning
* @problem.severity recommendation
* @tags maintainability
* readability
* external/jpl
*/
import cpp

View File

@@ -4,6 +4,8 @@
* @kind problem
* @id cpp/jpl-c/extern-decls-in-header
* @problem.severity warning
* @tags maintainability
* external/jpl
*/
import cpp

View File

@@ -3,7 +3,11 @@
* @description Global variables that are not accessed outside their own file should be made static to promote information hiding.
* @kind problem
* @id cpp/jpl-c/limited-scope-file
* @problem.severity warning
* @problem.severity recommendation
* @precision low
* @tags maintainability
* modularity
* external/jpl
*/
import cpp

View File

@@ -3,7 +3,10 @@
* @description Global and file-scope variables that are accessed by only one function should be scoped within that function.
* @kind problem
* @id cpp/jpl-c/limited-scope-function
* @problem.severity warning
* @problem.severity recommendation
* @precision low
* @tags maintainability
* external/jpl
*/
import cpp

View File

@@ -3,7 +3,10 @@
* @description A local variable or parameter that hides a global variable of the same name.
* @kind problem
* @id cpp/jpl-c/limited-scope-local-hides-global
* @problem.severity warning
* @problem.severity recommendation
* @tags maintainability
* readability
* external/jpl
*/
import cpp

View File

@@ -4,6 +4,9 @@
* @kind problem
* @id cpp/jpl-c/checking-return-values
* @problem.severity warning
* @tags correctness
* reliability
* external/jpl
*/
import cpp

View File

@@ -4,6 +4,9 @@
* @kind problem
* @id cpp/jpl-c/checking-parameter-values
* @problem.severity warning
* @tags correctness
* reliability
* external/jpl
*/
import JPL_C.Tasks

View File

@@ -4,6 +4,9 @@
* @kind problem
* @id cpp/jpl-c/use-of-assertions-constant
* @problem.severity warning
* @tags maintainability
* reliability
* external/jpl
*/
import semmle.code.cpp.commons.Assertions

View File

@@ -3,7 +3,10 @@
* @description All functions of more than 10 lines should have at least one assertion.
* @kind problem
* @id cpp/jpl-c/use-of-assertions-density
* @problem.severity warning
* @problem.severity recommendation
* @tags maintainability
* reliability
* external/jpl
*/
import semmle.code.cpp.commons.Assertions

View File

@@ -4,6 +4,8 @@
* @kind problem
* @id cpp/jpl-c/use-of-assertions-non-boolean
* @problem.severity warning
* @tags correctness
* external/jpl
*/
import semmle.code.cpp.commons.Assertions

View File

@@ -4,6 +4,8 @@
* @kind problem
* @id cpp/jpl-c/use-of-assertions-side-effect
* @problem.severity warning
* @tags correctness
* external/jpl
*/
import semmle.code.cpp.commons.Assertions

View File

@@ -3,7 +3,10 @@
* @description Typedefs that indicate size and signedness should be used in place of the basic types.
* @kind problem
* @id cpp/jpl-c/basic-int-types
* @problem.severity warning
* @problem.severity recommendation
* @tags maintainability
* readability
* external/jpl
*/
import cpp

View File

@@ -3,7 +3,10 @@
* @description In compound expressions with multiple sub-expressions the intended order of evaluation shall be made explicit with parentheses.
* @kind problem
* @id cpp/jpl-c/compound-expressions
* @problem.severity warning
* @problem.severity recommendation
* @tags maintainability
* readability
* external/jpl
*/
import cpp

View File

@@ -4,6 +4,9 @@
* @kind problem
* @id cpp/jpl-c/no-boolean-side-effects
* @problem.severity warning
* @tags correctness
* readability
* external/jpl
*/
import cpp

View File

@@ -3,7 +3,10 @@
* @description The use of the preprocessor must be limited to inclusion of header files and simple macro definitions.
* @kind problem
* @id cpp/jpl-c/preprocessor-use
* @problem.severity warning
* @problem.severity recommendation
* @tags maintainability
* readability
* external/jpl
*/
import cpp

View File

@@ -3,7 +3,10 @@
* @description The use of conditional compilation directives must be kept to a minimum -- e.g. for header guards only.
* @kind problem
* @id cpp/jpl-c/preprocessor-use-ifdef
* @problem.severity warning
* @problem.severity recommendation
* @tags maintainability
* readability
* external/jpl
*/
import cpp

View File

@@ -3,7 +3,10 @@
* @description Macros must expand to complete syntactic units -- "#define MY_IF if(" is not legal.
* @kind problem
* @id cpp/jpl-c/preprocessor-use-partial
* @problem.severity warning
* @problem.severity recommendation
* @tags maintainability
* readability
* external/jpl
*/
import cpp

View File

@@ -3,7 +3,10 @@
* @description Macros are not allowed to use complex preprocessor features like variable argument lists and token pasting.
* @kind problem
* @id cpp/jpl-c/preprocessor-use-undisciplined
* @problem.severity warning
* @problem.severity recommendation
* @tags maintainability
* readability
* external/jpl
*/
import cpp

View File

@@ -3,7 +3,10 @@
* @description Macros shall not be #define'd within a function or a block.
* @kind problem
* @id cpp/jpl-c/macro-in-block
* @problem.severity warning
* @problem.severity recommendation
* @tags maintainability
* readability
* external/jpl
*/
import cpp

View File

@@ -3,7 +3,10 @@
* @description #undef shall not be used.
* @kind problem
* @id cpp/jpl-c/use-of-undef
* @problem.severity warning
* @problem.severity recommendation
* @tags maintainability
* readability
* external/jpl
*/
import cpp

View File

@@ -4,6 +4,9 @@
* @kind problem
* @id cpp/jpl-c/mismatched-ifdefs
* @problem.severity warning
* @tags maintainability
* readability
* external/jpl
*/
import cpp

View File

@@ -3,7 +3,10 @@
* @description Putting more than one statement on a single line hinders program understanding.
* @kind problem
* @id cpp/jpl-c/multiple-stmts-per-line
* @problem.severity warning
* @problem.severity recommendation
* @tags maintainability
* readability
* external/jpl
*/
import cpp

View File

@@ -3,7 +3,10 @@
* @description There should be no more than one variable declaration per line.
* @kind problem
* @id cpp/jpl-c/multiple-var-decls-per-line
* @problem.severity warning
* @problem.severity recommendation
* @tags maintainability
* readability
* external/jpl
*/
import cpp

View File

@@ -3,7 +3,10 @@
* @description Function length should be limited to what can be printed on a single sheet of paper (60 lines). Number of parameters is limited to 6 or fewer.
* @kind problem
* @id cpp/jpl-c/function-size-limits
* @problem.severity warning
* @problem.severity recommendation
* @tags maintainability
* readability
* external/jpl
*/
import cpp

View File

@@ -3,7 +3,10 @@
* @description The declaration of an object should contain no more than two levels of indirection.
* @kind problem
* @id cpp/jpl-c/declaration-pointer-nesting
* @problem.severity warning
* @problem.severity recommendation
* @tags maintainability
* readability
* external/jpl
*/
import cpp

View File

@@ -3,7 +3,10 @@
* @description Statements should contain no more than two levels of dereferencing per object.
* @kind problem
* @id cpp/jpl-c/pointer-dereference-in-stmt
* @problem.severity warning
* @problem.severity recommendation
* @tags maintainability
* readability
* external/jpl
*/
import cpp

View File

@@ -3,7 +3,10 @@
* @description Pointer dereference operations should not be hidden in macro definitions.
* @kind problem
* @id cpp/jpl-c/hidden-pointer-dereference-macro
* @problem.severity warning
* @problem.severity recommendation
* @tags maintainability
* readability
* external/jpl
*/
import cpp

View File

@@ -3,7 +3,10 @@
* @description Pointer indirection may not be hidden by typedefs -- "typedef int* IntPtr;" is not allowed.
* @kind problem
* @id cpp/jpl-c/hidden-pointer-indirection-typedef
* @problem.severity warning
* @problem.severity recommendation
* @tags maintainability
* readability
* external/jpl
*/
import cpp

View File

@@ -3,7 +3,11 @@
* @description Non-constant pointers to functions should not be used.
* @kind problem
* @id cpp/jpl-c/non-const-function-pointer
* @problem.severity warning
* @problem.severity recommendation
* @precision low
* @tags maintainability
* readability
* external/jpl
*/
import cpp

View File

@@ -4,6 +4,9 @@
* @kind problem
* @id cpp/jpl-c/function-pointer-conversions
* @problem.severity warning
* @precision low
* @tags correctness
* external/jpl
*/
import cpp

View File

@@ -3,7 +3,10 @@
* @description #include directives in a file shall only be preceded by other preprocessor directives or comments.
* @kind problem
* @id cpp/jpl-c/includes-first
* @problem.severity warning
* @problem.severity recommendation
* @tags maintainability
* readability
* external/jpl
*/
import cpp

View File

@@ -34,7 +34,6 @@ private Type baseType(Type t) {
)
// Make sure that the type has a size and that it isn't ambiguous.
and strictcount(result.getSize()) = 1
}
/**
@@ -98,6 +97,7 @@ predicate defSourceType(SsaDefinition def, LocalScopeVariable v,
| p = v and
def.definedByParameter(p) and
sourceType = p.getType().getUnspecifiedType() and
strictcount(p.getType()) = 1 and
isPointerType(sourceType) and
sourceLoc = p.getLocation())
}

View File

@@ -89,6 +89,7 @@ predicate defSourceType(SsaDefinition def, LocalScopeVariable v,
| p = v and
def.definedByParameter(p) and
sourceType = p.getType().getUnspecifiedType() and
strictcount(p.getType()) = 1 and
isPointerType(sourceType) and
sourceLoc = p.getLocation())
}

View File

@@ -89,6 +89,7 @@ predicate defSourceType(SsaDefinition def, LocalScopeVariable v,
| p = v and
def.definedByParameter(p) and
sourceType = p.getType().getUnspecifiedType() and
strictcount(p.getType()) = 1 and
isPointerType(sourceType) and
sourceLoc = p.getLocation())
}

View File

@@ -4,6 +4,11 @@
* @kind problem
* @id cpp/duplicate-block
* @problem.severity recommendation
* @precision medium
* @tags testability
* maintainability
* duplicate-code
* non-attributable
*/
import CodeDuplication

View File

@@ -96,13 +96,19 @@ private predicate exprReleases(Expr e, Expr released, string kind) {
) or exists(Function f, int arg |
// `e` is a call to a function that releases one of it's parameters,
// and `released` is the corresponding argument
e.(FunctionCall).getTarget() = f and
(
e.(FunctionCall).getTarget() = f or
e.(FunctionCall).getTarget().(MemberFunction).getAnOverridingFunction+() = f
) and
e.(FunctionCall).getArgument(arg) = released and
exprReleases(_, exprOrDereference(f.getParameter(arg).getAnAccess()), kind)
) or exists(Function f, ThisExpr innerThis |
// `e` is a call to a method that releases `this`, and `released`
// is the object that is called
e.(FunctionCall).getTarget() = f and
(
e.(FunctionCall).getTarget() = f or
e.(FunctionCall).getTarget().(MemberFunction).getAnOverridingFunction+() = f
) and
e.(FunctionCall).getQualifier() = exprOrDereference(released) and
innerThis.getEnclosingFunction() = f and
exprReleases(_, innerThis, kind)

View File

@@ -126,6 +126,7 @@ predicate bbIPostDominates(BasicBlock pDom, BasicBlock node) = idominance(bb_exi
* Holds if `dominator` is a strict dominator of `node` in the control-flow
* graph of basic blocks. Being strict means that `dominator != node`.
*/
pragma[nomagic] // magic prevents fastTC
predicate bbStrictlyDominates(BasicBlock dominator, BasicBlock node) {
bbIDominates+(dominator, node)
}
@@ -134,6 +135,7 @@ predicate bbStrictlyDominates(BasicBlock dominator, BasicBlock node) {
* Holds if `postDominator` is a strict post-dominator of `node` in the control-flow
* graph of basic blocks. Being strict means that `postDominator != node`.
*/
pragma[nomagic] // magic prevents fastTC
predicate bbStrictlyPostDominates(BasicBlock postDominator, BasicBlock node) {
bbIPostDominates+(postDominator, node)
}

View File

@@ -0,0 +1,19 @@
// semmle-extractor-options: --expect_errors
void functionBeforeError()
{
}
void functionWithError1()
{
aaaaaaaaaa(); // error
}
void functionWithError2()
{
int i = aaaaaaaaaa(); // error
}
void functionAfterError()
{
}

View File

@@ -43,6 +43,10 @@
| cpp.cpp:87:5:87:26 | functionAccessesStatic | int | false |
| cpp.cpp:93:6:93:14 | increment | int & -> void | false |
| cpp.cpp:97:6:97:16 | doIncrement | void | false |
| error.cpp:3:6:3:24 | functionBeforeError | void | true |
| error.cpp:7:6:7:23 | functionWithError1 | void | false |
| error.cpp:12:6:12:23 | functionWithError2 | void | false |
| error.cpp:17:6:17:23 | functionAfterError | void | true |
| file://:0:0:0:0 | operator= | __va_list_tag && -> __va_list_tag & | false |
| file://:0:0:0:0 | operator= | const __va_list_tag & -> __va_list_tag & | false |
| sideEffects.c:4:5:4:6 | f1 | int | true |

View File

@@ -0,0 +1,2 @@
| test.cpp:7:12:7:21 | staticVar5 | Static variable staticVar5 is never read |
| test.cpp:8:12:8:21 | staticVar6 | Static variable staticVar6 is never read |

View File

@@ -0,0 +1 @@
Best Practices/Unused Entities/UnusedStaticVariables.ql

View File

@@ -0,0 +1,18 @@
int globalVar; // GOOD (not static)
static int staticVar1; // GOOD (used)
static int staticVar2; // GOOD (used)
static int staticVar3 = 3; // GOOD (used)
static int staticVar4 = staticVar3; // GOOD (used)
static int staticVar5; // BAD (unused)
static int staticVar6 = 6; // BAD (unused)
static __attribute__((__unused__)) int staticVar7; // GOOD (unused but this is expected)
void f()
{
int *ptr = &staticVar4;
staticVar1 = staticVar2;
(*ptr) = 0;
}

View File

@@ -0,0 +1,3 @@
| test.c:18:2:18:10 | call to expression | This call does not go through a const function pointer. |
| test.c:19:2:19:10 | call to expression | This call does not go through a const function pointer. |
| test.c:20:2:20:10 | call to expression | This call does not go through a const function pointer. |

View File

@@ -0,0 +1 @@
JPL_C/LOC-4/Rule 29/NonConstFunctionPointer.ql

View File

@@ -0,0 +1,21 @@
// test.c
void myFunc1();
void myFunc2();
typedef void (*voidFunPointer)();
void test()
{
void (*funPtr1)() = &myFunc1;
const void (*funPtr2)() = &myFunc1;
const voidFunPointer funPtr3 = &myFunc1;
funPtr1 = &myFunc2;
funPtr2 = &myFunc2;
//funPtr3 = &myFunc2; --- this would be a compilation error
funPtr1(); // BAD
funPtr2(); // BAD
funPtr3(); // GOOD [FALSE POSITIVE]
}

View File

@@ -0,0 +1,6 @@
| test.c:11:16:11:23 | & ... | Function pointer converted to int *, which is not an integral type. |
| test.c:12:18:12:25 | & ... | Function pointer converted to void *, which is not an integral type. |
| test.c:17:11:17:17 | funPtr1 | Function pointer converted to int *, which is not an integral type. |
| test.c:18:12:18:18 | funPtr1 | Function pointer converted to void *, which is not an integral type. |
| test.c:29:18:29:24 | funPtr1 | Function pointer converted to int *, which is not an integral type. |
| test.c:30:20:30:26 | funPtr1 | Function pointer converted to void *, which is not an integral type. |

View File

@@ -0,0 +1 @@
JPL_C/LOC-4/Rule 30/FunctionPointerConversions.ql

View File

@@ -0,0 +1,32 @@
// test.c
void myFunc1();
typedef void (*voidFunPtr)();
void test()
{
void (*funPtr1)() = &myFunc1; // GOOD
voidFunPtr funPtr2 = &myFunc1; // GOOD
int *intPtr = &myFunc1; // BAD (function pointer -> int pointer)
void *voidPtr = &myFunc1; // BAD (function pointer -> void pointer)
int i = &myFunc1; // GOOD (permitted)
funPtr1 = funPtr1; // GOOD
funPtr2 = funPtr1; // GOOD
intPtr = funPtr1; // BAD (function pointer -> int pointer)
voidPtr = funPtr1; // BAD (function pointer -> void pointer)
i = funPtr1; // GOOD (permitted)
funPtr1 = funPtr2; // GOOD
funPtr2 = funPtr2; // GOOD
intPtr = funPtr2; // BAD (function pointer -> int pointer) [NOT DETECTED]
voidPtr = funPtr2; // BAD (function pointer -> void pointer) [NOT DETECTED]
i = funPtr2; // GOOD (permitted)
funPtr1 = (void (*)())funPtr1; // GOOD
funPtr2 = (voidFunPtr)funPtr1; // GOOD
intPtr = (int *)funPtr1; // BAD (function pointer -> int pointer)
voidPtr = (void *)funPtr1; // BAD (function pointer -> void pointer)
i = (int)funPtr1; // GOOD (permitted)
}

View File

@@ -1,5 +1,8 @@
| calls.cpp:8:5:8:5 | 1 | This expression has no effect. | calls.cpp:8:5:8:5 | 1 | |
| calls.cpp:12:5:12:16 | call to thingy | This expression has no effect (because $@ has no external side effects). | calls.cpp:7:15:7:20 | thingy | thingy |
| expr.cpp:8:2:8:2 | 0 | This expression has no effect. | expr.cpp:8:2:8:2 | 0 | |
| expr.cpp:9:7:9:7 | 0 | This expression has no effect. | expr.cpp:9:7:9:7 | 0 | |
| expr.cpp:10:2:10:5 | ... , ... | This expression has no effect. | expr.cpp:10:2:10:5 | ... , ... | |
| preproc.c:89:2:89:4 | call to fn4 | This expression has no effect (because $@ has no external side effects). | preproc.c:33:5:33:7 | fn4 | fn4 |
| preproc.c:94:2:94:4 | call to fn9 | This expression has no effect (because $@ has no external side effects). | preproc.c:78:5:78:7 | fn9 | fn9 |
| template.cpp:19:3:19:3 | call to operator++ | This expression has no effect (because $@ has no external side effects). | template.cpp:9:10:9:19 | operator++ | operator++ |

View File

@@ -0,0 +1,13 @@
namespace Expr {
int i;
void comma_expr_test()
{
i++, i++; // GOOD
0, i++; // BAD (first part)
i++, 0; // BAD (second part)
0, 0; // BAD (whole)
}
}

View File

@@ -0,0 +1,10 @@
struct MyStruct
{
int x, y, z, w;
};
void test(MyStruct *ptr)
{
MyStruct *new_ptr = ptr + 1; // GOOD
}

View File

@@ -0,0 +1,13 @@
// note the two different `MyStruct` definitions, in test_small.cpp and test_large.cpp. These are
// in different translation units and we assume they are never linked into the same program (which
// would result in undefined behaviour).
struct MyStruct
{
int x, y;
};
void test(MyStruct *ptr)
{
MyStruct *new_ptr = ptr + 1; // GOOD
}

View File

@@ -8,6 +8,7 @@
| DeleteThis.cpp:56:3:56:24 | ... = ... | Resource ptr10 is acquired by class MyClass3 but not released anywhere in this class. |
| DeleteThis.cpp:58:3:58:24 | ... = ... | Resource ptr12 is acquired by class MyClass3 but not released anywhere in this class. |
| DeleteThis.cpp:60:3:60:24 | ... = ... | Resource ptr14 is acquired by class MyClass3 but not released anywhere in this class. |
| DeleteThis.cpp:127:3:127:20 | ... = ... | Resource d is acquired by class MyClass9 but not released anywhere in this class. |
| ExternalOwners.cpp:49:3:49:20 | ... = ... | Resource a is acquired by class MyScreen but not released anywhere in this class. |
| ListDelete.cpp:21:3:21:21 | ... = ... | Resource first is acquired by class MyThingColection but not released anywhere in this class. |
| NoDestructor.cpp:23:3:23:20 | ... = ... | Resource n is acquired by class MyClass5 but not released anywhere in this class. |

View File

@@ -82,3 +82,67 @@ private:
MyClass2 *ptr10, *ptr11, *ptr12, *ptr13, *ptr14, *ptr15;
MyClass2 *ptr20;
};
class MyClass4
{
public:
virtual void Release() = 0;
};
class MyClass5 : public MyClass4
{
public:
void Release()
{
delete this;
}
};
class MyClass6 : public MyClass5
{
};
class MyClass7 : public MyClass4
{
public:
void Release()
{
// do nothing
}
};
class MyClass8 : public MyClass7
{
};
class MyClass9
{
public:
MyClass9()
{
a = new MyClass5(); // GOOD
b = new MyClass5(); // GOOD
c = new MyClass6(); // GOOD
d = new MyClass7(); // BAD
e = new MyClass7(); // BAD [NOT DETECTED]
f = new MyClass8(); // BAD [NOT DETECTED]
}
~MyClass9()
{
a->Release(); // MyClass5::Release()
b->Release(); // MyClass5::Release()
c->Release(); // MyClass5::Release()
d->Release(); // MyClass7::Release()
e->Release(); // MyClass7::Release()
f->Release(); // MyClass7::Release()
}
MyClass5 *a;
MyClass4 *b;
MyClass4 *c;
MyClass7 *d;
MyClass4 *e;
MyClass4 *f;
};