Merge pull request #1411 from ian-semmle/qlcfg3

C++: QL CFG: Use synthetic_destructor_call table rather than SyntheticDestructorCalls
This commit is contained in:
Nick Rolfe
2019-06-07 16:22:24 +01:00
committed by GitHub
9 changed files with 15843 additions and 12768 deletions

View File

@@ -80,7 +80,6 @@
*/
private import cpp
private import semmle.code.cpp.controlflow.internal.SyntheticDestructorCalls
/**
* A control-flow node. This class exists to provide a shorter name than
@@ -115,8 +114,7 @@ private class Node extends ControlFlowNodeBase {
*/
private predicate excludeNodeAndNodesBelow(Expr e) {
not exists(e.getParent()) and
not e instanceof DestructorCall and
not e instanceof SyntheticDestructorCall // Workaround for CPP-320
not e instanceof DestructorCall
or
// Constructor init lists should be evaluated, and we can change this in
// the future, but it would mean that a `Function` entry point is not
@@ -983,36 +981,65 @@ private predicate subEdgeIncludingDestructors(Pos p1, Node n1, Node n2, Pos p2)
// called, connect the "before destructors" node directly to the "after
// destructors" node. For performance, only do this when the nodes exist.
exists(Pos afterDtors | afterDtors.isAfterDestructors() | subEdge(afterDtors, n1, _, _)) and
not exists(getDestructorCallAfterNode(n1, 0)) and
not exists(getSynthesisedDestructorCallAfterNode(n1, 0)) and
p1.nodeBeforeDestructors(n1, n1) and
p2.nodeAfterDestructors(n2, n1)
or
exists(Node n |
// before destructors -> access(0)
p1.nodeBeforeDestructors(n1, n) and
p2.nodeAt(n2, getDestructorCallAfterNode(n, 0).getAccess())
or
// access(i) -> call(i)
exists(int i |
p1.nodeAt(n1, getDestructorCallAfterNode(n, i).getAccess()) and
p2.nodeAt(n2, getDestructorCallAfterNode(n, i))
)
or
// call(i) -> access(i+1)
exists(int i |
p1.nodeAt(n1, getDestructorCallAfterNode(n, i)) and
p2.nodeAt(n2, getDestructorCallAfterNode(n, i + 1).getAccess())
)
or
// call(max) -> after destructors end
// before destructors -> access(max)
exists(int maxCallIndex |
maxCallIndex = max(int i | exists(getDestructorCallAfterNode(n, i))) and
p1.nodeAt(n1, getDestructorCallAfterNode(n, maxCallIndex)) and
p2.nodeAfterDestructors(n2, n)
maxCallIndex = max(int i | exists(getSynthesisedDestructorCallAfterNode(n, i))) and
p1.nodeBeforeDestructors(n1, n) and
p2.nodeAt(n2, getSynthesisedDestructorCallAfterNode(n, maxCallIndex).getQualifier()))
or
// call(i+1) -> access(i)
exists(int i |
p1.nodeAt(n1, getSynthesisedDestructorCallAfterNode(n, i + 1)) and
p2.nodeAt(n2, getSynthesisedDestructorCallAfterNode(n, i).getQualifier())
)
or
// call(0) -> after destructors end
p1.nodeAt(n1, getSynthesisedDestructorCallAfterNode(n, 0)) and
p2.nodeAfterDestructors(n2, n)
)
}
/**
* Gets the synthetic constructor call for the `index`'th entity that
* should be destructed following `node`. Note that entities should be
* destructed in reverse construction order, so these should be called
* from highest to lowest index.
*
* The exact placement of that call in the CFG depends on the type of
* `node` as follows:
*
* - `Block`: after ordinary control flow falls off the end of the block
* without jumps or exceptions.
* - `ReturnStmt`: After the statement itself or after its operand (if
* present).
* - `ThrowExpr`: After the `throw` expression or after its operand (if
* present).
* - `JumpStmt` (`BreakStmt`, `ContinueStmt`, `GotoStmt`): after the statement.
* - A `ForStmt`, `WhileStmt`, `SwitchStmt`, or `IfStmt`: after control flow
* falls off the end of the statement without jumping. Destruction can occur
* here for `for`-loops that have an initializer (`for (C x = a; ...; ...)`)
* and for statements whose condition is a `ConditionDeclExpr`
* (`if (C x = a)`).
* - The `getUpdate()` of a `ForStmt`: after the `getUpdate()` expression. This
* can happen when the condition is a `ConditionDeclExpr`
* - `Handler`: On the edge out of the `Handler` for the case where the
* exception was not matched and is propagated to the next handler or
* function exit point.
* - `MicrosoftTryExceptStmt`: After the false-edge out of the `e` in
* `__except(e)`, before propagating the exception up to the next handler or
* function exit point.
* - `MicrosoftTryFinallyStmt`: On the edge following the `__finally` block for
* the case where an exception was thrown and needs to be propagated.
*/
DestructorCall getSynthesisedDestructorCallAfterNode(Node n, int i) {
synthetic_destructor_call(n, i, result)
}
/**
* An expression whose outgoing true/false sub-edges may come from different
* sub-nodes.

View File

@@ -1,247 +0,0 @@
/**
* Provides classes and predicates for approximating where compiler-generated
* destructor calls should be placed. This file can be removed when the
* extractor produces this information directly.
*/
private import cpp
private predicate isDeleteDestructorCall(DestructorCall c) {
exists(DeleteExpr del | c = del.getDestructorCall())
or
exists(DeleteArrayExpr del | c = del.getDestructorCall())
}
// Things we know about these calls
// - isCompilerGenerated() always holds
// - They all have a predecessor of type VariableAccess
// - They all have successors
// - After subtracting all the jumpy dtor calls for a particular variable
// (PrematureScopeExitNode), there's at most one call left, and that will be
// the ordinary one.
// - Except for ConditionDeclExpr! One chain should be directly connected to
// the false edge out of the parent, and the other should not.
class SyntheticDestructorCall extends FunctionCall {
SyntheticDestructorCall() {
(
this instanceof DestructorCall
or
// Workaround for CPP-320
exists(Function target |
target = this.(FunctionCall).getTarget() and
not exists(target.getName())
)
) and
not exists(this.getParent()) and
not isDeleteDestructorCall(this) and
not this.isUnevaluated() and
this.isCompilerGenerated()
}
VariableAccess getAccess() { successors(result, this) }
SyntheticDestructorCall getNext() { successors(this, result.getAccess()) }
SyntheticDestructorCall getPrev() { this = result.getNext() }
}
// Things we know about these blocks
// - If they follow a JumpStmt, the VariableAccesses of their calls never
// have multiple predecessors.
// - But after ReturnStmt, that may happen.
/**
* Describes a straight line of `SyntheticDestructorCall`s. Note that such
* lines can share tails.
*/
private class SyntheticDestructorBlock extends ControlFlowNodeBase {
SyntheticDestructorBlock() {
this = any(SyntheticDestructorCall call |
not exists(call.getPrev())
or
exists(ControlFlowNodeBase pred |
not pred instanceof SyntheticDestructorCall and
successors(pred, call.getAccess())
)
)
}
SyntheticDestructorCall getCall(int i) {
i = 0 and result = this
or
result = this.getCall(i - 1).getNext()
}
ControlFlowNode getAPredecessor() {
successors(result, this.(SyntheticDestructorCall).getAccess()) and
not result instanceof SyntheticDestructorCall
}
ControlFlowNode getSuccessor() {
successors(this.getCall(max(int i | exists(this.getCall(i)))), result)
}
}
private class PrematureScopeExitNode extends ControlFlowNodeBase {
PrematureScopeExitNode() {
this instanceof JumpStmt
or
this instanceof Handler
or
this instanceof ThrowExpr
or
this instanceof ReturnStmt
or
this instanceof MicrosoftTryExceptStmt
or
// Detecting exception edges out of a MicrosoftTryFinallyStmt is not
// implemented. It may not be easy to do. It'll be something like finding
// the first synthetic destructor call that crosses out of the scope of the
// statement and does not belong to some other `PrematureScopeExitNode`.
// Note that the exception destructors after __try can follow right after
// ordinary cleanup from the __finally block.
this instanceof MicrosoftTryFinallyStmt
}
SyntheticDestructorBlock getSyntheticDestructorBlock() {
result.getAPredecessor() = this
or
// StmtExpr not handled properly here.
result.getAPredecessor().(Expr).getParent+() = this.(ReturnStmt)
or
// Only handles post-order conditions. Won't work with
// short-circuiting operators.
falsecond_base(this.(MicrosoftTryExceptStmt).getCondition(),
result.(SyntheticDestructorCall).getAccess())
}
}
private class DestructedVariable extends LocalScopeVariable {
DestructedVariable() {
exists(SyntheticDestructorCall call | call.getAccess().getTarget() = this)
}
/**
* Gets the single destructor call that that corresponds to falling off the
* end of the scope of this variable.
*/
SyntheticDestructorCall getOrdinaryCall() {
exists(SyntheticDestructorBlock block |
block.getCall(_) = result and
not exists(PrematureScopeExitNode exit | exit.getSyntheticDestructorBlock() = block) and
result.getAccess().getTarget() = this
|
falsecond_base(getDeclaringLoop().getCondition(), block.getCall(0).getAccess())
or
not exists(this.getDeclaringLoop())
)
}
predicate hasPositionInScope(int x, int y, Stmt scope) {
exists(DeclStmt declStmt |
this = declStmt.getDeclaration(y) and
declStmt = scope.getChild(x)
)
or
exists(ConditionDeclExpr decl |
this = decl.getVariable() and
// These coordinates are chosen to place the destruction correctly
// relative to the destruction of other variables declared in
// `decl.getParent()`. Only a `for` loop can have other declarations in
// it. These show up as a `DeclStmt` with `x = 0`, so by choosing `x = 1`
// here we get the `ConditionDeclExpr` placed after all variables
// declared in the init statement of the `for` loop.
x = 1 and
y = 0 and
scope = decl.getParent()
)
or
exists(CatchBlock cb |
this = cb.getParameter() and
scope = cb and
// A `CatchBlock` is a `Block`, so there might be other variables
// declared in it. These coordinates are chosen to place the Parameter
// before any such declarations.
x = -1 and
y = 0
)
}
SyntheticDestructorCall getInnerScopeCall() {
exists(SyntheticDestructorBlock block |
block.getCall(_) = result and
not exists(PrematureScopeExitNode exit | exit.getSyntheticDestructorBlock() = block) and
result.getAccess().getTarget() = this
|
exists(Loop loop | loop = this.getDeclaringLoop() |
not falsecond_base(loop.getCondition(), block.getCall(0).getAccess())
)
)
}
predicate hasPositionInInnerScope(int x, int y, ControlFlowNodeBase scope) {
exists(ConditionDeclExpr decl |
this = decl.getVariable() and
// These coordinates are chosen to place the destruction correctly
// relative to the destruction of other variables in `scope`. Only in the
// `while` case can there be other variables in `scope`, and in that case
// `scope` will be a `Block`, whose smallest `x` coordinate can be 0.
x = -1 and
y = 0 and
(
scope = decl.getParent().(ForStmt).getUpdate()
or
scope = decl.getParent().(WhileStmt).getStmt()
)
)
}
private Loop getDeclaringLoop() {
exists(ConditionDeclExpr decl | this = decl.getVariable() and result = decl.getParent())
}
}
/**
* Gets the `index`'th synthetic destructor call that should follow `node`. The
* exact placement of that call in the CFG depends on the type of `node` as
* follows:
*
* - `Block`: after ordinary control flow falls off the end of the block
* without jumps or exceptions.
* - `ReturnStmt`: After the statement itself or after its operand (if
* present).
* - `ThrowExpr`: After the `throw` expression or after its operand (if
* present).
* - `JumpStmt` (`BreakStmt`, `ContinueStmt`, `GotoStmt`): after the statement.
* - A `ForStmt`, `WhileStmt`, `SwitchStmt`, or `IfStmt`: after control flow
* falls off the end of the statement without jumping. Destruction can occur
* here for `for`-loops that have an initializer (`for (C x = a; ...; ...)`)
* and for statements whose condition is a `ConditionDeclExpr`
* (`if (C x = a)`).
* - The `getUpdate()` of a `ForStmt`: after the `getUpdate()` expression. This
* can happen when the condition is a `ConditionDeclExpr`
* - `Handler`: On the edge out of the `Handler` for the case where the
* exception was not matched and is propagated to the next handler or
* function exit point.
* - `MicrosoftTryExceptStmt`: After the false-edge out of the `e` in
* `__except(e)`, before propagating the exception up to the next handler or
* function exit point.
* - `MicrosoftTryFinallyStmt`: On the edge following the `__finally` block for
* the case where an exception was thrown and needs to be propagated.
*/
SyntheticDestructorCall getDestructorCallAfterNode(ControlFlowNodeBase node, int index) {
result = rank[index + 1](SyntheticDestructorCall call, DestructedVariable var, int x, int y |
call = var.getOrdinaryCall() and
var.hasPositionInScope(x, y, node)
or
call = var.getInnerScopeCall() and
var.hasPositionInInnerScope(x, y, node)
|
call
order by
x desc, y desc
)
or
exists(SyntheticDestructorBlock block |
node.(PrematureScopeExitNode).getSyntheticDestructorBlock() = block and
result = block.getCall(index)
)
}

View File

@@ -181,17 +181,24 @@ tokens(
int endColumn : int ref
);
/*
* Mapping of header files to packages
/**
* Information about packages that provide code used during compilation.
* The `id` is just a unique identifier.
* The `namespace` is typically the name of the package manager that
* provided the package (e.g. "dpkg" or "yum").
* The `package_name` is the name of the package, and `version` is its
* version (as a string).
*/
external_packages(
unique int id: @external_package,
string namespace : string ref, // "dpkg", "yum", ...
string namespace : string ref,
string package_name : string ref,
string version : string ref
);
/**
* Holds if File `fileid` was provided by package `package`.
*/
header_to_external_package(
int fileid : @file ref,
int package : @external_package ref
@@ -1041,6 +1048,17 @@ exprconv(
compgenerated(unique int id: @element ref);
/**
* `destructor_call` destructs the `i`'th entity that should be
* destructed following `element`. Note that entities should be
* destructed in reverse construction order, so for a given `element`
* these should be called from highest to lowest `i`.
*/
synthetic_destructor_call(
int element: @element ref,
int i: int ref,
unique int destructor_call: @routineexpr ref
);
namespaces(
unique int id: @namespace,

File diff suppressed because it is too large Load Diff

View File

@@ -1,310 +1,53 @@
| ms_try_mix__ms_empty_finally_at_end_extractor | false | 14359 | 14359 | ms_empty_finally_at_end |
| ms_try_mix__ms_empty_finally_at_end_extractor | false | 14364 | 14364 | declaration |
| ms_try_mix__ms_empty_finally_at_end_extractor | false | 14368 | 14368 | 3 |
| ms_try_mix__ms_empty_finally_at_end_extractor | false | 14369 | 14369 | throw ... |
| ms_try_mix__ms_empty_finally_at_end_extractor | false | 14371 | 14371 | ExprStmt |
| ms_try_mix__ms_empty_finally_at_end_extractor | false | 14373 | 14373 | { ... } |
| ms_try_mix__ms_empty_finally_at_end_extractor | false | 14375 | 14375 | { ... } |
| ms_try_mix__ms_empty_finally_at_end_extractor | false | 14377 | 14377 | __try { ... } __finally { ... } |
| ms_try_mix__ms_empty_finally_at_end_extractor | false | 14379 | 14379 | return ... |
| ms_try_mix__ms_empty_finally_at_end_extractor | false | 14381 | 14381 | { ... } |
| ms_try_mix__ms_empty_finally_at_end_extractor | false | 14383 | 14383 | c201 |
| ms_try_mix__ms_empty_finally_at_end_extractor | false | 14385 | 14385 | call to c201.~C |
| ms_try_mix__ms_empty_finally_at_end_extractor | false | 14386 | 14386 | c201 |
| ms_try_mix__ms_empty_finally_at_end_extractor | false | 14387 | 14387 | call to c201.~C |
| ms_try_mix__ms_empty_finally_at_end_extractor | false | 14389 | 14389 | call to C |
| ms_try_mix__ms_empty_finally_at_end_extractor | false | 14394 | 14394 | 201 |
| ms_try_mix__ms_empty_finally_at_end_extractor | false | 14395 | 14395 | initializer for c201 |
| ms_try_mix__ms_empty_finally_at_end_extractor | true | 14364 | 14395 | |
| ms_try_mix__ms_empty_finally_at_end_extractor | true | 14368 | 14369 | |
| ms_try_mix__ms_empty_finally_at_end_extractor | true | 14369 | 14386 | |
| ms_try_mix__ms_empty_finally_at_end_extractor | true | 14371 | 14368 | |
| ms_try_mix__ms_empty_finally_at_end_extractor | true | 14373 | 14371 | |
| ms_try_mix__ms_empty_finally_at_end_extractor | true | 14375 | 14379 | |
| ms_try_mix__ms_empty_finally_at_end_extractor | true | 14375 | 14386 | |
| ms_try_mix__ms_empty_finally_at_end_extractor | true | 14377 | 14373 | |
| ms_try_mix__ms_empty_finally_at_end_extractor | true | 14379 | 14383 | |
| ms_try_mix__ms_empty_finally_at_end_extractor | true | 14381 | 14364 | |
| ms_try_mix__ms_empty_finally_at_end_extractor | true | 14383 | 14385 | |
| ms_try_mix__ms_empty_finally_at_end_extractor | true | 14385 | 14359 | |
| ms_try_mix__ms_empty_finally_at_end_extractor | true | 14386 | 14387 | |
| ms_try_mix__ms_empty_finally_at_end_extractor | true | 14387 | 14359 | |
| ms_try_mix__ms_empty_finally_at_end_extractor | true | 14387 | 14375 | |
| ms_try_mix__ms_empty_finally_at_end_extractor | true | 14389 | 14377 | |
| ms_try_mix__ms_empty_finally_at_end_extractor | true | 14394 | 14389 | |
| ms_try_mix__ms_empty_finally_at_end_extractor | true | 14395 | 14394 | |
| ms_try_mix__ms_empty_finally_at_end_ql | false | 14359 | 14359 | ms_empty_finally_at_end |
| ms_try_mix__ms_empty_finally_at_end_ql | false | 14364 | 14364 | declaration |
| ms_try_mix__ms_empty_finally_at_end_ql | false | 14368 | 14368 | 3 |
| ms_try_mix__ms_empty_finally_at_end_ql | false | 14369 | 14369 | throw ... |
| ms_try_mix__ms_empty_finally_at_end_ql | false | 14371 | 14371 | ExprStmt |
| ms_try_mix__ms_empty_finally_at_end_ql | false | 14373 | 14373 | { ... } |
| ms_try_mix__ms_empty_finally_at_end_ql | false | 14375 | 14375 | { ... } |
| ms_try_mix__ms_empty_finally_at_end_ql | false | 14377 | 14377 | __try { ... } __finally { ... } |
| ms_try_mix__ms_empty_finally_at_end_ql | false | 14379 | 14379 | return ... |
| ms_try_mix__ms_empty_finally_at_end_ql | false | 14381 | 14381 | { ... } |
| ms_try_mix__ms_empty_finally_at_end_ql | false | 14383 | 14383 | c201 |
| ms_try_mix__ms_empty_finally_at_end_ql | false | 14385 | 14385 | call to c201.~C |
| ms_try_mix__ms_empty_finally_at_end_ql | false | 14386 | 14386 | c201 |
| ms_try_mix__ms_empty_finally_at_end_ql | false | 14387 | 14387 | call to c201.~C |
| ms_try_mix__ms_empty_finally_at_end_ql | false | 14389 | 14389 | call to C |
| ms_try_mix__ms_empty_finally_at_end_ql | false | 14394 | 14394 | 201 |
| ms_try_mix__ms_empty_finally_at_end_ql | false | 14395 | 14395 | initializer for c201 |
| ms_try_mix__ms_empty_finally_at_end_ql | true | 14364 | 14395 | |
| ms_try_mix__ms_empty_finally_at_end_ql | true | 14368 | 14369 | |
| ms_try_mix__ms_empty_finally_at_end_ql | true | 14369 | 14386 | |
| ms_try_mix__ms_empty_finally_at_end_ql | true | 14371 | 14368 | |
| ms_try_mix__ms_empty_finally_at_end_ql | true | 14373 | 14371 | |
| ms_try_mix__ms_empty_finally_at_end_ql | true | 14375 | 14359 | |
| ms_try_mix__ms_empty_finally_at_end_ql | true | 14375 | 14379 | |
| ms_try_mix__ms_empty_finally_at_end_ql | true | 14377 | 14373 | |
| ms_try_mix__ms_empty_finally_at_end_ql | true | 14379 | 14383 | |
| ms_try_mix__ms_empty_finally_at_end_ql | true | 14381 | 14364 | |
| ms_try_mix__ms_empty_finally_at_end_ql | true | 14383 | 14385 | |
| ms_try_mix__ms_empty_finally_at_end_ql | true | 14385 | 14359 | |
| ms_try_mix__ms_empty_finally_at_end_ql | true | 14386 | 14387 | |
| ms_try_mix__ms_empty_finally_at_end_ql | true | 14387 | 14375 | |
| ms_try_mix__ms_empty_finally_at_end_ql | true | 14389 | 14377 | |
| ms_try_mix__ms_empty_finally_at_end_ql | true | 14394 | 14389 | |
| ms_try_mix__ms_empty_finally_at_end_ql | true | 14395 | 14394 | |
| ms_try_mix__ms_finally_mix_extractor | false | 14398 | 14398 | ms_finally_mix |
| ms_try_mix__ms_finally_mix_extractor | false | 14406 | 14406 | declaration |
| ms_try_mix__ms_finally_mix_extractor | false | 14409 | 14409 | call to C |
| ms_try_mix__ms_finally_mix_extractor | false | 14413 | 14413 | 106 |
| ms_try_mix__ms_finally_mix_extractor | false | 14414 | 14414 | initializer for c106 |
| ms_try_mix__ms_finally_mix_extractor | false | 14418 | 14418 | call to C |
| ms_try_mix__ms_finally_mix_extractor | false | 14422 | 14422 | 107 |
| ms_try_mix__ms_finally_mix_extractor | false | 14423 | 14423 | initializer for c107 |
| ms_try_mix__ms_finally_mix_extractor | false | 14426 | 14426 | declaration |
| ms_try_mix__ms_finally_mix_extractor | false | 14428 | 14428 | b2 |
| ms_try_mix__ms_finally_mix_extractor | false | 14430 | 14430 | (bool)... |
| ms_try_mix__ms_finally_mix_extractor | false | 14433 | 14433 | 2 |
| ms_try_mix__ms_finally_mix_extractor | false | 14434 | 14434 | throw ... |
| ms_try_mix__ms_finally_mix_extractor | false | 14436 | 14436 | ExprStmt |
| ms_try_mix__ms_finally_mix_extractor | false | 14438 | 14438 | { ... } |
| ms_try_mix__ms_finally_mix_extractor | false | 14440 | 14440 | if (...) ... |
| ms_try_mix__ms_finally_mix_extractor | false | 14442 | 14442 | declaration |
| ms_try_mix__ms_finally_mix_extractor | false | 14444 | 14444 | { ... } |
| ms_try_mix__ms_finally_mix_extractor | false | 14447 | 14447 | call to C |
| ms_try_mix__ms_finally_mix_extractor | false | 14451 | 14451 | 108 |
| ms_try_mix__ms_finally_mix_extractor | false | 14452 | 14452 | initializer for c108 |
| ms_try_mix__ms_finally_mix_extractor | false | 14455 | 14455 | declaration |
| ms_try_mix__ms_finally_mix_extractor | false | 14457 | 14457 | { ... } |
| ms_try_mix__ms_finally_mix_extractor | false | 14459 | 14459 | __try { ... } __finally { ... } |
| ms_try_mix__ms_finally_mix_extractor | false | 14461 | 14461 | declaration |
| ms_try_mix__ms_finally_mix_extractor | false | 14463 | 14463 | return ... |
| ms_try_mix__ms_finally_mix_extractor | false | 14465 | 14465 | { ... } |
| ms_try_mix__ms_finally_mix_extractor | false | 14467 | 14467 | c101 |
| ms_try_mix__ms_finally_mix_extractor | false | 14469 | 14469 | call to c101.~C |
| ms_try_mix__ms_finally_mix_extractor | false | 14470 | 14470 | c109 |
| ms_try_mix__ms_finally_mix_extractor | false | 14471 | 14471 | call to c109.~C |
| ms_try_mix__ms_finally_mix_extractor | false | 14472 | 14472 | c101 |
| ms_try_mix__ms_finally_mix_extractor | false | 14473 | 14473 | call to c101.~C |
| ms_try_mix__ms_finally_mix_extractor | false | 14474 | 14474 | c108 |
| ms_try_mix__ms_finally_mix_extractor | false | 14476 | 14476 | call to c108.~C |
| ms_try_mix__ms_finally_mix_extractor | false | 14477 | 14477 | c106 |
| ms_try_mix__ms_finally_mix_extractor | false | 14479 | 14479 | call to c106.~C |
| ms_try_mix__ms_finally_mix_extractor | false | 14480 | 14480 | c107 |
| ms_try_mix__ms_finally_mix_extractor | false | 14481 | 14481 | call to c107.~C |
| ms_try_mix__ms_finally_mix_extractor | false | 14482 | 14482 | c106 |
| ms_try_mix__ms_finally_mix_extractor | false | 14483 | 14483 | call to c106.~C |
| ms_try_mix__ms_finally_mix_extractor | false | 14485 | 14485 | call to C |
| ms_try_mix__ms_finally_mix_extractor | false | 14489 | 14489 | 101 |
| ms_try_mix__ms_finally_mix_extractor | false | 14490 | 14490 | initializer for c101 |
| ms_try_mix__ms_finally_mix_extractor | false | 14494 | 14494 | call to C |
| ms_try_mix__ms_finally_mix_extractor | false | 14498 | 14498 | 109 |
| ms_try_mix__ms_finally_mix_extractor | false | 14499 | 14499 | initializer for c109 |
| ms_try_mix__ms_finally_mix_extractor | true | 14406 | 14490 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14409 | 14440 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14413 | 14409 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14414 | 14413 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14418 | 14480 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14422 | 14418 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14423 | 14422 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14426 | 14414 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14428 | 14438 | T |
| ms_try_mix__ms_finally_mix_extractor | true | 14428 | 14442 | F |
| ms_try_mix__ms_finally_mix_extractor | true | 14433 | 14434 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14434 | 14482 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14436 | 14433 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14438 | 14436 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14440 | 14428 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14442 | 14423 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14444 | 14426 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14447 | 14474 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14451 | 14447 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14452 | 14451 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14455 | 14452 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14457 | 14455 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14459 | 14444 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14461 | 14499 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14463 | 14470 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14465 | 14406 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14467 | 14469 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14469 | 14398 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14470 | 14471 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14471 | 14467 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14472 | 14473 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14473 | 14398 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14474 | 14476 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14476 | 14461 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14476 | 14472 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14477 | 14479 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14479 | 14457 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14480 | 14481 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14481 | 14477 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14482 | 14483 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14483 | 14457 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14485 | 14459 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14489 | 14485 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14490 | 14489 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14494 | 14463 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14498 | 14494 | |
| ms_try_mix__ms_finally_mix_extractor | true | 14499 | 14498 | |
| ms_try_mix__ms_finally_mix_ql | false | 14398 | 14398 | ms_finally_mix |
| ms_try_mix__ms_finally_mix_ql | false | 14406 | 14406 | declaration |
| ms_try_mix__ms_finally_mix_ql | false | 14409 | 14409 | call to C |
| ms_try_mix__ms_finally_mix_ql | false | 14413 | 14413 | 106 |
| ms_try_mix__ms_finally_mix_ql | false | 14414 | 14414 | initializer for c106 |
| ms_try_mix__ms_finally_mix_ql | false | 14418 | 14418 | call to C |
| ms_try_mix__ms_finally_mix_ql | false | 14422 | 14422 | 107 |
| ms_try_mix__ms_finally_mix_ql | false | 14423 | 14423 | initializer for c107 |
| ms_try_mix__ms_finally_mix_ql | false | 14426 | 14426 | declaration |
| ms_try_mix__ms_finally_mix_ql | false | 14428 | 14428 | b2 |
| ms_try_mix__ms_finally_mix_ql | false | 14430 | 14430 | (bool)... |
| ms_try_mix__ms_finally_mix_ql | false | 14433 | 14433 | 2 |
| ms_try_mix__ms_finally_mix_ql | false | 14434 | 14434 | throw ... |
| ms_try_mix__ms_finally_mix_ql | false | 14436 | 14436 | ExprStmt |
| ms_try_mix__ms_finally_mix_ql | false | 14438 | 14438 | { ... } |
| ms_try_mix__ms_finally_mix_ql | false | 14440 | 14440 | if (...) ... |
| ms_try_mix__ms_finally_mix_ql | false | 14442 | 14442 | declaration |
| ms_try_mix__ms_finally_mix_ql | false | 14444 | 14444 | { ... } |
| ms_try_mix__ms_finally_mix_ql | false | 14447 | 14447 | call to C |
| ms_try_mix__ms_finally_mix_ql | false | 14451 | 14451 | 108 |
| ms_try_mix__ms_finally_mix_ql | false | 14452 | 14452 | initializer for c108 |
| ms_try_mix__ms_finally_mix_ql | false | 14455 | 14455 | declaration |
| ms_try_mix__ms_finally_mix_ql | false | 14457 | 14457 | { ... } |
| ms_try_mix__ms_finally_mix_ql | false | 14459 | 14459 | __try { ... } __finally { ... } |
| ms_try_mix__ms_finally_mix_ql | false | 14461 | 14461 | declaration |
| ms_try_mix__ms_finally_mix_ql | false | 14463 | 14463 | return ... |
| ms_try_mix__ms_finally_mix_ql | false | 14465 | 14465 | { ... } |
| ms_try_mix__ms_finally_mix_ql | false | 14467 | 14467 | c101 |
| ms_try_mix__ms_finally_mix_ql | false | 14469 | 14469 | call to c101.~C |
| ms_try_mix__ms_finally_mix_ql | false | 14470 | 14470 | c109 |
| ms_try_mix__ms_finally_mix_ql | false | 14471 | 14471 | call to c109.~C |
| ms_try_mix__ms_finally_mix_ql | false | 14472 | 14472 | c101 |
| ms_try_mix__ms_finally_mix_ql | false | 14473 | 14473 | call to c101.~C |
| ms_try_mix__ms_finally_mix_ql | false | 14474 | 14474 | c108 |
| ms_try_mix__ms_finally_mix_ql | false | 14476 | 14476 | call to c108.~C |
| ms_try_mix__ms_finally_mix_ql | false | 14477 | 14477 | c106 |
| ms_try_mix__ms_finally_mix_ql | false | 14479 | 14479 | call to c106.~C |
| ms_try_mix__ms_finally_mix_ql | false | 14480 | 14480 | c107 |
| ms_try_mix__ms_finally_mix_ql | false | 14481 | 14481 | call to c107.~C |
| ms_try_mix__ms_finally_mix_ql | false | 14482 | 14482 | c106 |
| ms_try_mix__ms_finally_mix_ql | false | 14483 | 14483 | call to c106.~C |
| ms_try_mix__ms_finally_mix_ql | false | 14485 | 14485 | call to C |
| ms_try_mix__ms_finally_mix_ql | false | 14489 | 14489 | 101 |
| ms_try_mix__ms_finally_mix_ql | false | 14490 | 14490 | initializer for c101 |
| ms_try_mix__ms_finally_mix_ql | false | 14494 | 14494 | call to C |
| ms_try_mix__ms_finally_mix_ql | false | 14498 | 14498 | 109 |
| ms_try_mix__ms_finally_mix_ql | false | 14499 | 14499 | initializer for c109 |
| ms_try_mix__ms_finally_mix_ql | true | 14406 | 14490 | |
| ms_try_mix__ms_finally_mix_ql | true | 14409 | 14440 | |
| ms_try_mix__ms_finally_mix_ql | true | 14413 | 14409 | |
| ms_try_mix__ms_finally_mix_ql | true | 14414 | 14413 | |
| ms_try_mix__ms_finally_mix_ql | true | 14418 | 14480 | |
| ms_try_mix__ms_finally_mix_ql | true | 14422 | 14418 | |
| ms_try_mix__ms_finally_mix_ql | true | 14423 | 14422 | |
| ms_try_mix__ms_finally_mix_ql | true | 14426 | 14414 | |
| ms_try_mix__ms_finally_mix_ql | true | 14428 | 14438 | T |
| ms_try_mix__ms_finally_mix_ql | true | 14428 | 14442 | F |
| ms_try_mix__ms_finally_mix_ql | true | 14433 | 14434 | |
| ms_try_mix__ms_finally_mix_ql | true | 14434 | 14482 | |
| ms_try_mix__ms_finally_mix_ql | true | 14436 | 14433 | |
| ms_try_mix__ms_finally_mix_ql | true | 14438 | 14436 | |
| ms_try_mix__ms_finally_mix_ql | true | 14440 | 14428 | |
| ms_try_mix__ms_finally_mix_ql | true | 14442 | 14423 | |
| ms_try_mix__ms_finally_mix_ql | true | 14444 | 14426 | |
| ms_try_mix__ms_finally_mix_ql | true | 14447 | 14474 | |
| ms_try_mix__ms_finally_mix_ql | true | 14451 | 14447 | |
| ms_try_mix__ms_finally_mix_ql | true | 14452 | 14451 | |
| ms_try_mix__ms_finally_mix_ql | true | 14455 | 14452 | |
| ms_try_mix__ms_finally_mix_ql | true | 14457 | 14455 | |
| ms_try_mix__ms_finally_mix_ql | true | 14459 | 14444 | |
| ms_try_mix__ms_finally_mix_ql | true | 14461 | 14499 | |
| ms_try_mix__ms_finally_mix_ql | true | 14463 | 14470 | |
| ms_try_mix__ms_finally_mix_ql | true | 14465 | 14406 | |
| ms_try_mix__ms_finally_mix_ql | true | 14467 | 14469 | |
| ms_try_mix__ms_finally_mix_ql | true | 14469 | 14398 | |
| ms_try_mix__ms_finally_mix_ql | true | 14470 | 14471 | |
| ms_try_mix__ms_finally_mix_ql | true | 14471 | 14467 | |
| ms_try_mix__ms_finally_mix_ql | true | 14472 | 14473 | |
| ms_try_mix__ms_finally_mix_ql | true | 14473 | 14398 | |
| ms_try_mix__ms_finally_mix_ql | true | 14474 | 14476 | |
| ms_try_mix__ms_finally_mix_ql | true | 14476 | 14398 | |
| ms_try_mix__ms_finally_mix_ql | true | 14476 | 14461 | |
| ms_try_mix__ms_finally_mix_ql | true | 14477 | 14479 | |
| ms_try_mix__ms_finally_mix_ql | true | 14479 | 14457 | |
| ms_try_mix__ms_finally_mix_ql | true | 14480 | 14481 | |
| ms_try_mix__ms_finally_mix_ql | true | 14481 | 14477 | |
| ms_try_mix__ms_finally_mix_ql | true | 14482 | 14483 | |
| ms_try_mix__ms_finally_mix_ql | true | 14483 | 14457 | |
| ms_try_mix__ms_finally_mix_ql | true | 14485 | 14459 | |
| ms_try_mix__ms_finally_mix_ql | true | 14489 | 14485 | |
| ms_try_mix__ms_finally_mix_ql | true | 14490 | 14489 | |
| ms_try_mix__ms_finally_mix_ql | true | 14494 | 14463 | |
| ms_try_mix__ms_finally_mix_ql | true | 14498 | 14494 | |
| ms_try_mix__ms_finally_mix_ql | true | 14499 | 14498 | |
| staticlocals__staticlocals_f2_extractor | false | 21781 | 21781 | f2 |
| staticlocals__staticlocals_f2_extractor | false | 21786 | 21786 | declaration |
| staticlocals__staticlocals_f2_extractor | false | 21788 | 21788 | declaration |
| staticlocals__staticlocals_f2_extractor | false | 21790 | 21790 | declaration |
| staticlocals__staticlocals_f2_extractor | false | 21792 | 21792 | declaration |
| staticlocals__staticlocals_f2_extractor | false | 21794 | 21794 | return ... |
| staticlocals__staticlocals_f2_extractor | false | 21796 | 21796 | { ... } |
| staticlocals__staticlocals_f2_extractor | false | 21798 | 21798 | call to C |
| staticlocals__staticlocals_f2_extractor | false | 21800 | 21800 | initializer for c |
| staticlocals__staticlocals_f2_extractor | false | 21802 | 21802 | call to addOne |
| staticlocals__staticlocals_f2_extractor | false | 21806 | 21806 | 2 |
| staticlocals__staticlocals_f2_extractor | false | 21809 | 21809 | initializer for j |
| staticlocals__staticlocals_f2_extractor | false | 21810 | 21810 | call to addOne |
| staticlocals__staticlocals_f2_extractor | false | 21815 | 21815 | 2 |
| staticlocals__staticlocals_f2_extractor | false | 21816 | 21816 | initializer for two |
| staticlocals__staticlocals_f2_extractor | false | 21819 | 21819 | two |
| staticlocals__staticlocals_f2_extractor | false | 21824 | 21824 | initializer for i |
| staticlocals__staticlocals_f2_extractor | true | 21786 | 21816 | |
| staticlocals__staticlocals_f2_extractor | true | 21788 | 21790 | |
| staticlocals__staticlocals_f2_extractor | true | 21790 | 21792 | |
| staticlocals__staticlocals_f2_extractor | true | 21792 | 21794 | |
| staticlocals__staticlocals_f2_extractor | true | 21794 | 21781 | |
| staticlocals__staticlocals_f2_extractor | true | 21796 | 21786 | |
| staticlocals__staticlocals_f2_extractor | true | 21815 | 21788 | |
| staticlocals__staticlocals_f2_extractor | true | 21816 | 21815 | |
| staticlocals__staticlocals_f2_ql | false | 21781 | 21781 | f2 |
| staticlocals__staticlocals_f2_ql | false | 21786 | 21786 | declaration |
| staticlocals__staticlocals_f2_ql | false | 21788 | 21788 | declaration |
| staticlocals__staticlocals_f2_ql | false | 21790 | 21790 | declaration |
| staticlocals__staticlocals_f2_ql | false | 21792 | 21792 | declaration |
| staticlocals__staticlocals_f2_ql | false | 21794 | 21794 | return ... |
| staticlocals__staticlocals_f2_ql | false | 21796 | 21796 | { ... } |
| staticlocals__staticlocals_f2_ql | false | 21798 | 21798 | call to C |
| staticlocals__staticlocals_f2_ql | false | 21800 | 21800 | initializer for c |
| staticlocals__staticlocals_f2_ql | false | 21802 | 21802 | call to addOne |
| staticlocals__staticlocals_f2_ql | false | 21806 | 21806 | 2 |
| staticlocals__staticlocals_f2_ql | false | 21809 | 21809 | initializer for j |
| staticlocals__staticlocals_f2_ql | false | 21810 | 21810 | call to addOne |
| staticlocals__staticlocals_f2_ql | false | 21815 | 21815 | 2 |
| staticlocals__staticlocals_f2_ql | false | 21816 | 21816 | initializer for two |
| staticlocals__staticlocals_f2_ql | false | 21819 | 21819 | two |
| staticlocals__staticlocals_f2_ql | false | 21824 | 21824 | initializer for i |
| staticlocals__staticlocals_f2_ql | true | 21786 | 21816 | |
| staticlocals__staticlocals_f2_ql | true | 21788 | 21790 | |
| staticlocals__staticlocals_f2_ql | true | 21790 | 21792 | |
| staticlocals__staticlocals_f2_ql | true | 21792 | 21794 | |
| staticlocals__staticlocals_f2_ql | true | 21792 | 21800 | |
| staticlocals__staticlocals_f2_ql | true | 21794 | 21781 | |
| staticlocals__staticlocals_f2_ql | true | 21796 | 21786 | |
| staticlocals__staticlocals_f2_ql | true | 21798 | 21794 | |
| staticlocals__staticlocals_f2_ql | true | 21800 | 21798 | |
| staticlocals__staticlocals_f2_ql | true | 21815 | 21788 | |
| staticlocals__staticlocals_f2_ql | true | 21816 | 21815 | |
| staticlocals__staticlocals_f2_extractor | false | 31268 | 31268 | f2 |
| staticlocals__staticlocals_f2_extractor | false | 31274 | 31274 | declaration |
| staticlocals__staticlocals_f2_extractor | false | 31277 | 31277 | declaration |
| staticlocals__staticlocals_f2_extractor | false | 31280 | 31280 | declaration |
| staticlocals__staticlocals_f2_extractor | false | 31283 | 31283 | declaration |
| staticlocals__staticlocals_f2_extractor | false | 31286 | 31286 | return ... |
| staticlocals__staticlocals_f2_extractor | false | 31289 | 31289 | { ... } |
| staticlocals__staticlocals_f2_extractor | false | 31292 | 31292 | call to C |
| staticlocals__staticlocals_f2_extractor | false | 31295 | 31295 | initializer for c |
| staticlocals__staticlocals_f2_extractor | false | 31298 | 31298 | call to addOne |
| staticlocals__staticlocals_f2_extractor | false | 31304 | 31304 | 2 |
| staticlocals__staticlocals_f2_extractor | false | 31309 | 31309 | initializer for j |
| staticlocals__staticlocals_f2_extractor | false | 31311 | 31311 | call to addOne |
| staticlocals__staticlocals_f2_extractor | false | 31319 | 31319 | 2 |
| staticlocals__staticlocals_f2_extractor | false | 31321 | 31321 | initializer for two |
| staticlocals__staticlocals_f2_extractor | false | 31325 | 31325 | two |
| staticlocals__staticlocals_f2_extractor | false | 31333 | 31333 | initializer for i |
| staticlocals__staticlocals_f2_extractor | true | 31274 | 31321 | |
| staticlocals__staticlocals_f2_extractor | true | 31277 | 31280 | |
| staticlocals__staticlocals_f2_extractor | true | 31280 | 31283 | |
| staticlocals__staticlocals_f2_extractor | true | 31283 | 31286 | |
| staticlocals__staticlocals_f2_extractor | true | 31286 | 31268 | |
| staticlocals__staticlocals_f2_extractor | true | 31289 | 31274 | |
| staticlocals__staticlocals_f2_extractor | true | 31319 | 31277 | |
| staticlocals__staticlocals_f2_extractor | true | 31321 | 31319 | |
| staticlocals__staticlocals_f2_ql | false | 31268 | 31268 | f2 |
| staticlocals__staticlocals_f2_ql | false | 31274 | 31274 | declaration |
| staticlocals__staticlocals_f2_ql | false | 31277 | 31277 | declaration |
| staticlocals__staticlocals_f2_ql | false | 31280 | 31280 | declaration |
| staticlocals__staticlocals_f2_ql | false | 31283 | 31283 | declaration |
| staticlocals__staticlocals_f2_ql | false | 31286 | 31286 | return ... |
| staticlocals__staticlocals_f2_ql | false | 31289 | 31289 | { ... } |
| staticlocals__staticlocals_f2_ql | false | 31292 | 31292 | call to C |
| staticlocals__staticlocals_f2_ql | false | 31295 | 31295 | initializer for c |
| staticlocals__staticlocals_f2_ql | false | 31298 | 31298 | call to addOne |
| staticlocals__staticlocals_f2_ql | false | 31304 | 31304 | 2 |
| staticlocals__staticlocals_f2_ql | false | 31309 | 31309 | initializer for j |
| staticlocals__staticlocals_f2_ql | false | 31311 | 31311 | call to addOne |
| staticlocals__staticlocals_f2_ql | false | 31319 | 31319 | 2 |
| staticlocals__staticlocals_f2_ql | false | 31321 | 31321 | initializer for two |
| staticlocals__staticlocals_f2_ql | false | 31325 | 31325 | two |
| staticlocals__staticlocals_f2_ql | false | 31333 | 31333 | initializer for i |
| staticlocals__staticlocals_f2_ql | true | 31274 | 31321 | |
| staticlocals__staticlocals_f2_ql | true | 31277 | 31280 | |
| staticlocals__staticlocals_f2_ql | true | 31280 | 31283 | |
| staticlocals__staticlocals_f2_ql | true | 31283 | 31286 | |
| staticlocals__staticlocals_f2_ql | true | 31283 | 31295 | |
| staticlocals__staticlocals_f2_ql | true | 31286 | 31268 | |
| staticlocals__staticlocals_f2_ql | true | 31289 | 31274 | |
| staticlocals__staticlocals_f2_ql | true | 31292 | 31286 | |
| staticlocals__staticlocals_f2_ql | true | 31295 | 31292 | |
| staticlocals__staticlocals_f2_ql | true | 31319 | 31277 | |
| staticlocals__staticlocals_f2_ql | true | 31321 | 31319 | |

View File

@@ -1,8 +1,3 @@
| ms_try_mix__ms_empty_finally_at_end | ms_try_mix.cpp:53:13:54:3 | { ... } | ms_try_mix.cpp:47:6:47:28 | ms_empty_finally_at_end | Standard edge, only from QL |
| ms_try_mix__ms_empty_finally_at_end | ms_try_mix.cpp:53:13:54:3 | { ... } | ms_try_mix.cpp:55:1:55:1 | c201 | Standard edge, only from extractor |
| ms_try_mix__ms_empty_finally_at_end | ms_try_mix.cpp:55:1:55:1 | call to c201.~C | ms_try_mix.cpp:47:6:47:28 | ms_empty_finally_at_end | Standard edge, only from extractor |
| ms_try_mix__ms_finally_mix | ms_try_mix.cpp:39:5:39:5 | call to c108.~C | ms_try_mix.cpp:27:6:27:19 | ms_finally_mix | Standard edge, only from QL |
| ms_try_mix__ms_finally_mix | ms_try_mix.cpp:39:5:39:5 | call to c108.~C | ms_try_mix.cpp:42:1:42:1 | c101 | Standard edge, only from extractor |
| staticlocals__staticlocals_f2 | file://:0:0:0:0 | call to C | staticlocals.cpp:30:1:30:1 | return ... | Standard edge, only from QL |
| staticlocals__staticlocals_f2 | file://:0:0:0:0 | initializer for c | file://:0:0:0:0 | call to C | Standard edge, only from QL |
| staticlocals__staticlocals_f2 | staticlocals.cpp:29:5:29:17 | declaration | file://:0:0:0:0 | initializer for c | Standard edge, only from QL |

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
description: Add synthetic_destructor_call
compatibility: partial