C#: Make Callable::get[Expression|Statement]Body() return all possible implementations

Previosly, we returned only the body belonging to "the most likely" implementation,
based on a CFG size heuristics. However, now that more callables are mapped to the
same entity, it makes more sense to treat such callables (to some extent) like
partial methods. This means, for instance, that data flow will branch out to all possible
implementations, much like we do for virtual dispatch.
This commit is contained in:
Tom Hvitved
2020-08-27 14:29:20 +02:00
parent afbbafe132
commit 8a0355720a
16 changed files with 228 additions and 452 deletions

View File

@@ -32,23 +32,15 @@ class Callable extends DotNet::Callable, Parameterizable, ExprOrStmtParent, @cal
* Gets the body of this callable, if any.
*
* The body is either a `BlockStmt` or an `Expr`.
*/
final ControlFlowElement getBody() {
result = this.getStatementBody() or
result = this.getExpressionBody()
}
/**
* Gets a body of this callable, if any.
*
* Unlike `getBody()`, this predicate may return multiple bodies, in the case
* where the same callable is compiled multiple times. For example, if we
* compile both `A.cs`
* Normally, each callable will have at most one body, except in the case where
* the same callable is compiled multiple times. For example, if we compile
* both `A.cs`
*
* ```csharp
* namespaces N {
* public class C {
* public int M() => 0;
* public int M() { return 0; }
* }
* }
* ```
@@ -58,19 +50,24 @@ class Callable extends DotNet::Callable, Parameterizable, ExprOrStmtParent, @cal
* ```csharp
* namespaces N {
* public class C {
* public int M() { return 1; }
* public int M() => 1;
* }
* }
* ```
*
* to the same assembly, then both `0` and `{ return 1; }` are bodies of `N.C.M()`.
* then both `{ return 0; }` and `1` are bodies of `N.C.M()`.
*/
final ControlFlowElement getABody() {
result = this.getAStatementBody() or
result = this.getAnExpressionBody()
final ControlFlowElement getBody() {
result = this.getStatementBody() or
result = this.getExpressionBody()
}
override predicate hasBody() { exists(getBody()) }
/**
* DEPRECATED: Use `getBody()` instead.
*/
deprecated final ControlFlowElement getABody() { result = this.getBody() }
override predicate hasBody() { exists(this.getBody()) }
/**
* Holds if this callable has a non-empty body. That is, either it has
@@ -79,19 +76,15 @@ class Callable extends DotNet::Callable, Parameterizable, ExprOrStmtParent, @cal
predicate hasNonEmptyBody() {
this.hasExpressionBody()
or
this.hasStatementBody() and
not this.getStatementBody().stripSingletonBlocks().(BlockStmt).isEmpty()
this.getStatementBody().stripSingletonBlocks() = any(Stmt s | not s.(BlockStmt).isEmpty())
}
/** Gets the statement body of this callable, if any. */
final BlockStmt getStatementBody() { result = this.getAChildStmt() }
/**
* Gets a statement body of this callable, if any.
* Gets the statement body of this callable, if any.
*
* Unlike `getStatementBody()`, this predicate may return multiple bodies, in
* the case where the same callable is compiled multiple times. For example,
* if we compile both `A.cs`
* Normally, each callable will have at most one statement body, except in the
* case where the same callable is compiled multiple times. For example, if
* we compile both `A.cs`
*
* ```csharp
* namespaces N {
@@ -111,23 +104,25 @@ class Callable extends DotNet::Callable, Parameterizable, ExprOrStmtParent, @cal
* }
* ```
*
* to the same assembly, then both `{ return 0; }` and `{ return 1; }` are
* statement bodies of `N.C.M()`.
* then both `{ return 0; }` and `{ return 1; }` are statement bodies of
* `N.C.M()`.
*/
final BlockStmt getAStatementBody() { stmt_parent_top_level(result, _, this) }
final BlockStmt getStatementBody() { result = this.getAChildStmt() }
/**
* DEPRECATED: Use `getStatementBody` instead.
*/
final BlockStmt getAStatementBody() { result = this.getStatementBody() }
/** Holds if this callable has a statement body. */
final predicate hasStatementBody() { exists(getStatementBody()) }
/** Gets the expression body of this callable (if any), specified by `=>`. */
final Expr getExpressionBody() { result = this.getChildExpr(0) }
/**
* Gets an expression body of this callable (if any), specified by `=>`.
* Gets the expression body of this callable (if any), specified by `=>`.
*
* Unlike `getExpressionBody()`, this predicate may return multiple bodies, in
* the case where the same callable is compiled multiple times. For example,
* if we compile both `A.cs`
* Normally, each callable will have at most one expression body, except in the
* case where the same callable is compiled multiple times. For example, if
* we compile both `A.cs`
*
* ```csharp
* namespaces N {
@@ -147,9 +142,17 @@ class Callable extends DotNet::Callable, Parameterizable, ExprOrStmtParent, @cal
* }
* ```
*
* to the same assembly, then both `0` and `1` are expression bodies of `N.C.M()`.
* then both `0` and `1` are expression bodies of `N.C.M()`.
*/
final Expr getAnExpressionBody() { expr_parent_top_level_adjusted(result, 0, this) }
final Expr getExpressionBody() {
result = this.getAChildExpr() and
not result = this.(Constructor).getInitializer()
}
/**
* DEPRECATED: Use `getExpressionBody()` instead.
*/
deprecated final Expr getAnExpressionBody() { result = this.getExpressionBody() }
/** Holds if this callable has an expression body. */
final predicate hasExpressionBody() { exists(getExpressionBody()) }

View File

@@ -6,74 +6,6 @@
import csharp
/**
* INTERNAL: Do not use.
*
* The `expr_parent_top_level()` relation extended to include a relation
* between getters and expression bodies in properties such as `int P => 0`.
*/
predicate expr_parent_top_level_adjusted(Expr child, int i, @top_level_exprorstmt_parent parent) {
expr_parent_top_level(child, i, parent)
or
parent = any(Getter g | expr_parent_top_level(child, i, g.getDeclaration())) and
i = 0
}
/**
* The `expr_parent()` relation adjusted for expandable assignments. For example,
* the assignment `x += y` is extracted as
*
* ```
* +=
* |
* 2
* |
* =
* / \
* 1 0
* / \
* x +
* / \
* 1 0
* / \
* x y
* ```
*
* in order to be able to retrieve the expanded assignment `x = x + y` as the 2nd
* child. This predicate changes the diagram above into
*
* ```
* +=
* / \
* 1 0
* / \
* x y
* ```
*/
private predicate expr_parent_adjusted(Expr child, int i, ControlFlowElement parent) {
if parent instanceof AssignOperation
then
parent =
any(AssignOperation ao |
exists(AssignExpr ae | ae = ao.getExpandedAssignment() |
i = 0 and
exists(Expr right |
// right = `x + y`
expr_parent(right, 0, ae)
|
expr_parent(child, 1, right)
)
or
i = 1 and
expr_parent(child, 1, ae)
)
or
not ao.hasExpandedAssignment() and
expr_parent(child, i, parent)
)
else expr_parent(child, i, parent)
}
/**
* INTERNAL: Do not use.
*
@@ -88,7 +20,7 @@ class ExprOrStmtParent extends Element, @exprorstmt_parent {
/** Gets the `i`th child expression of this element (zero-based). */
final Expr getChildExpr(int i) {
expr_parent_adjusted(result, i, this) or
result = getTopLevelChild(this, i)
expr_parent_top_level_adjusted(result, i, this)
}
/** Gets a child expression of this element, if any. */
@@ -97,7 +29,7 @@ class ExprOrStmtParent extends Element, @exprorstmt_parent {
/** Gets the `i`th child statement of this element (zero-based). */
final Stmt getChildStmt(int i) {
stmt_parent(result, i, this) or
result = getTopLevelChild(this, i)
stmt_parent_top_level(result, i, this)
}
/** Gets a child statement of this element, if any. */
@@ -113,242 +45,19 @@ class TopLevelExprParent extends Element, @top_level_expr_parent {
final override Expr getChild(int i) { result = this.getChildExpr(i) }
/** Gets the `i`th child expression of this element (zero-based). */
final Expr getChildExpr(int i) { result = getTopLevelChild(this, i) }
final Expr getChildExpr(int i) { expr_parent_top_level_adjusted(result, i, this) }
/** Gets a child expression of this element, if any. */
final Expr getAChildExpr() { result = this.getChildExpr(_) }
}
/**
* INTERNAL: Do not use.
*
* An element that can have a child statement or expression, and where we have
* encountered multiple potential implementations at compile-time. For example,
* if we compile both `A.cs`
*
* ```csharp
* namespaces N {
* public class C {
* public int M() => 0;
* }
* }
* ```
*
* and later `B.cs`
*
* ```csharp
* namespaces N {
* public class C {
* public int M() => 1;
* }
* }
* ```
*
* then the method `N.C.M` has two implementations, returning `0` and `1`,
* respectively.
*
* The implementation used at run-time is in this case unknown (indeed, it could
* be a third implementation not encountered during compilation), so we make a
* guess for the "most likely" implementation in `getBestChild()`.
*/
class MultiImplementationsParent extends ExprOrStmtParent {
MultiImplementationsParent() {
exists(int i |
strictcount(File f |
exists(ControlFlowElement implementation, Location l | f = l.getFile() |
stmt_parent_top_level(implementation, i, this) and
stmt_location(implementation, l)
or
expr_parent_top_level_adjusted(implementation, i, this) and
expr_location(implementation, l)
)
or
hasAccessorAutoImplementation(this, f) and
i = 0
) > 1
)
}
/**
* Gets a file that contains an implementation `cfe` for the `i`th child of this
* element.
*/
private File getAnImplementation(int i, ControlFlowElement cfe) {
exists(Location l | result = l.getFile() |
stmt_parent_top_level(cfe, i, this) and
stmt_location(cfe, l)
or
expr_parent_top_level_adjusted(cfe, i, this) and
expr_location(cfe, l)
)
}
/**
* Gets a file that contains an implementation `cfe` for the `i`th child of this
* element, where `t` is the top-level type containing this element (that is,
* `t` is not a nested type).
*/
File getAnImplementationInTopLevelType(int i, ControlFlowElement cfe, ValueOrRefType t) {
result = this.getAnImplementation(i, cfe) and
t = getTopLevelDeclaringType(this)
}
/**
* Gets a file that contains an auto-implementation for this element, where
* `t` is the top-level type containing this element (that is, `t` is not a
* nested type).
*/
File getAnAutoImplementationFileInTopLevelType(ValueOrRefType t) {
hasAccessorAutoImplementation(this, result) and
t = getTopLevelDeclaringType(this)
}
private File getAnImplementationFileInTopLevelType(int i, ValueOrRefType t) {
result = getAnImplementationInTopLevelType(i, _, t)
or
result = this.getAnAutoImplementationFileInTopLevelType(t) and
i = 0
}
/**
* Gets the file containing the "best" implementation of this element, that is, the
* file considered most likely to contain the actual run-time implementation.
*
* The heuristics we use is to choose the implementation belonging to the top-level type
* with the most control flow elements (excluding `throw` elements). In the case of a tie,
* we arbitrarily choose the implementation belonging to the last file (in lexicographic
* order).
*
* By counting elements for the top-level type, we ensure that all definitions belonging
* to the same top-level type will get implementations belonging to the same file.
*/
File getBestFile() {
exists(ValueOrRefType t |
result =
max(this.getAnImplementationFileInTopLevelType(_, t) as file
order by
getImplementationSize(t, file), file.toString()
)
)
}
/**
* Gets the i`th child of this element. Only the "best" child among all the possible
* run-time implementations is returned, namely the child considered most likely to
* be the actual run-time implementation.
*/
ControlFlowElement getBestChild(int i) {
exists(File f, ValueOrRefType t | f = getBestFile() |
f = this.getAnImplementationInTopLevelType(i, result, t)
)
}
}
/** Gets the top-level type containing declaration `d`. */
private ValueOrRefType getTopLevelDeclaringType(Declaration d) {
result = getDeclaringType+(d) and
not result instanceof NestedType
}
/** Gets the declaring type of element `e`. */
private ValueOrRefType getDeclaringType(Declaration d) {
methods(d, _, result, _, _)
or
constructors(d, _, result, _)
or
destructors(d, _, result, _)
or
operators(d, _, _, result, _, _)
or
properties(d, _, result, _, _)
or
indexers(d, _, result, _, _)
or
nested_types(d, result, _)
or
fields(d, _, _, result, _, _)
or
exists(DeclarationWithAccessors decl | d = decl.getAnAccessor() | result = getDeclaringType(decl))
or
exists(Parameterizable p | params(d, _, _, _, _, p, _) | result = getDeclaringType(p))
}
private ControlFlowElement getAChild(ControlFlowElement cfe) {
expr_parent_adjusted(result, _, cfe) or
stmt_parent(result, _, cfe)
}
private int getImplementationSize0(ValueOrRefType t, File f) {
result =
strictcount(ControlFlowElement cfe |
exists(MultiImplementationsParent p, ControlFlowElement child |
cfe = getAChild*(child) and
not cfe = getAChild*(any(ThrowElement te))
|
f = p.getAnImplementationInTopLevelType(_, child, t)
or
// Merge stats for partial implementations belonging to the same folder
t.isPartial() and
f = p.getAnImplementationInTopLevelType(_, _, t) and
exists(File fOther, MultiImplementationsParent pOther |
f.getParentContainer() = fOther.getParentContainer()
|
fOther = pOther.getAnImplementationInTopLevelType(_, child, t)
)
)
)
}
private int getImplementationSize1(ValueOrRefType t, File f) {
result =
strictsum(MultiImplementationsParent p, int c |
// Count each auto-implemented accessor as size 4 (getter) or 5 (setter)
f = p.getAnAutoImplementationFileInTopLevelType(t) and
if p instanceof Getter then c = 4 else c = 5
|
c
)
}
private int getImplementationSize(ValueOrRefType t, File f) {
if exists(getImplementationSize0(t, f))
then
if exists(getImplementationSize1(t, f))
then result = getImplementationSize0(t, f) + getImplementationSize1(t, f)
else result = getImplementationSize0(t, f)
else result = getImplementationSize1(t, f)
}
/**
* Holds if declaration `d` should have a location in file `f`, because it is part of a
* type with multiple implementations, where the most likely run-time implementation is
* in `f`.
*/
private predicate mustHaveLocationInFile(Declaration d, File f) {
exists(MultiImplementationsParent p, ValueOrRefType t |
t = getTopLevelDeclaringType(p) and
f = p.getBestFile()
|
t = getTopLevelDeclaringType(d) or d = t or d = p
)
}
private predicate hasNoSourceLocation(Element e) { not e.getALocation() instanceof SourceLocation }
cached
private module Cached {
cached
ControlFlowElement getTopLevelChild(ExprOrStmtParent p, int i) {
result = p.(MultiImplementationsParent).getBestChild(i)
or
not p instanceof MultiImplementationsParent and
(stmt_parent_top_level(result, i, p) or expr_parent_top_level_adjusted(result, i, p))
}
cached
Location bestLocation(Element e) {
result = e.getALocation().(SourceLocation) and
(mustHaveLocationInFile(e, _) implies mustHaveLocationInFile(e, result.getFile()))
result = e.getALocation().(SourceLocation)
or
hasNoSourceLocation(e) and
result = min(Location l | l = e.getALocation() | l order by l.getFile().toString())
@@ -360,20 +69,71 @@ private module Cached {
/**
* INTERNAL: Do not use.
*
* Holds if accessor `a` has an auto-implementation in file `f`.
* The `expr_parent_top_level()` relation extended to include a relation
* between getters and expression bodies in properties such as `int P => 0`.
*/
cached
predicate hasAccessorAutoImplementation(Accessor a, File f) {
exists(SourceLocation sl | sl = a.getALocation() |
f = sl.getFile() and
not exists(ControlFlowElement cfe, Location l | sl.getFile() = l.getFile() |
stmt_parent_top_level(cfe, _, a) and
stmt_location(cfe, l)
or
expr_parent_top_level_adjusted(cfe, 0, a) and
expr_location(cfe, l)
)
)
predicate expr_parent_top_level_adjusted(Expr child, int i, @top_level_exprorstmt_parent parent) {
expr_parent_top_level(child, i, parent)
or
parent = any(Getter g | expr_parent_top_level(child, i, g.getDeclaration())) and
i = 0
}
/**
* The `expr_parent()` relation adjusted for expandable assignments. For example,
* the assignment `x += y` is extracted as
*
* ```
* +=
* |
* 2
* |
* =
* / \
* 1 0
* / \
* x +
* / \
* 1 0
* / \
* x y
* ```
*
* in order to be able to retrieve the expanded assignment `x = x + y` as the 2nd
* child. This predicate changes the diagram above into
*
* ```
* +=
* / \
* 1 0
* / \
* x y
* ```
*/
cached
predicate expr_parent_adjusted(Expr child, int i, ControlFlowElement parent) {
if parent instanceof AssignOperation
then
parent =
any(AssignOperation ao |
exists(AssignExpr ae | ae = ao.getExpandedAssignment() |
i = 0 and
exists(Expr right |
// right = `x + y`
expr_parent(right, 0, ae)
|
expr_parent(child, 1, right)
)
or
i = 1 and
expr_parent(child, 1, ae)
)
or
not ao.hasExpandedAssignment() and
expr_parent(child, i, parent)
)
else expr_parent(child, i, parent)
}
}

View File

@@ -288,7 +288,7 @@ class ForwarderAssertMethod extends AssertMethod {
ForwarderAssertMethod() {
p = this.getAParameter() and
strictcount(AssignableDefinition def | def.getTarget() = p) = 1 and
forex(ControlFlowElement body | body = this.getABody() |
forex(ControlFlowElement body | body = this.getBody() |
bodyAsserts(this, body, a) and
a.getExpr() = p.getAnAccess()
)
@@ -306,7 +306,7 @@ class ForwarderAssertMethod extends AssertMethod {
pragma[noinline]
private predicate bodyAsserts(Callable c, ControlFlowElement body, Assertion a) {
c.getABody() = body and
c.getBody() = body and
body = getAnAssertingElement(a)
}

View File

@@ -55,10 +55,23 @@ private class ThrowingCall extends NonReturningCall {
override ThrowCompletion getACompletion() { result = c }
}
/** Holds if accessor `a` has an auto-implementation. */
private predicate hasAccessorAutoImplementation(Accessor a) {
exists(SourceLocation sl | sl = a.getALocation() |
not exists(ControlFlowElement cfe, Location l | sl.getFile() = l.getFile() |
stmt_parent_top_level(cfe, _, a) and
stmt_location(cfe, l)
or
expr_parent_top_level_adjusted(cfe, 0, a) and
expr_location(cfe, l)
)
)
}
abstract private class NonReturningCallable extends Callable {
NonReturningCallable() {
not exists(ReturnStmt ret | ret.getEnclosingCallable() = this) and
not hasAccessorAutoImplementation(this, _) and
not hasAccessorAutoImplementation(this) and
not exists(Virtualizable v | v.isOverridableOrImplementable() |
v = this or
v = this.(Accessor).getDeclaration()
@@ -80,7 +93,7 @@ private class DirectlyExitingCallable extends ExitingCallable {
private class IndirectlyExitingCallable extends ExitingCallable {
IndirectlyExitingCallable() {
forex(ControlFlowElement body | body = this.getABody() | body = getAnExitingElement())
forex(ControlFlowElement body | body = this.getBody() | body = getAnExitingElement())
}
}
@@ -104,11 +117,11 @@ private Stmt getAnExitingStmt() {
private class ThrowingCallable extends NonReturningCallable {
ThrowingCallable() {
forex(ControlFlowElement body | body = this.getABody() | body = getAThrowingElement(_))
forex(ControlFlowElement body | body = this.getBody() | body = getAThrowingElement(_))
}
/** Gets a valid completion for a call to this throwing callable. */
ThrowCompletion getACallCompletion() { this.getABody() = getAThrowingElement(result) }
ThrowCompletion getACallCompletion() { this.getBody() = getAThrowingElement(result) }
}
private predicate directlyThrows(ThrowElement te, ThrowCompletion c) {

View File

@@ -1,16 +1,60 @@
| A.cs:6:22:6:31 | get_P1 | A.cs:6:22:6:31 | throw ... |
| A.cs:6:22:6:31 | get_P1 | B.cs:5:22:5:22 | 0 |
| A.cs:7:21:7:23 | get_P2 | A.cs:7:25:7:39 | {...} |
| A.cs:7:21:7:23 | get_P2 | B.cs:6:25:6:37 | {...} |
| A.cs:7:41:7:43 | set_P2 | A.cs:7:45:7:59 | {...} |
| A.cs:7:41:7:43 | set_P2 | B.cs:6:43:6:45 | {...} |
| A.cs:8:16:8:16 | M | A.cs:8:23:8:32 | throw ... |
| A.cs:8:16:8:16 | M | B.cs:7:23:7:23 | 2 |
| A.cs:14:31:14:31 | get_Item | A.cs:14:31:14:31 | access to parameter i |
| A.cs:14:31:14:31 | get_Item | B.cs:14:31:14:40 | throw ... |
| A.cs:15:36:15:38 | get_Item | A.cs:15:40:15:52 | {...} |
| A.cs:15:36:15:38 | get_Item | B.cs:15:40:15:54 | {...} |
| A.cs:15:54:15:56 | set_Item | A.cs:15:58:15:60 | {...} |
| A.cs:15:54:15:56 | set_Item | B.cs:15:60:15:62 | {...} |
| A.cs:16:17:16:18 | M1 | A.cs:17:5:19:5 | {...} |
| A.cs:16:17:16:18 | M1 | B.cs:17:5:19:5 | {...} |
| A.cs:18:9:18:22 | M2 | A.cs:18:21:18:21 | 0 |
| A.cs:20:12:20:13 | C2 | A.cs:20:22:20:31 | {...} |
| A.cs:20:12:20:13 | C2 | B.cs:20:22:20:36 | {...} |
| A.cs:21:12:21:13 | C2 | A.cs:21:27:21:29 | {...} |
| A.cs:21:12:21:13 | C2 | B.cs:21:27:21:29 | {...} |
| A.cs:22:6:22:7 | ~C2 | A.cs:22:11:22:13 | {...} |
| A.cs:22:6:22:7 | ~C2 | B.cs:22:11:22:25 | {...} |
| A.cs:23:28:23:35 | implicit conversion | A.cs:23:50:23:53 | null |
| A.cs:23:28:23:35 | implicit conversion | B.cs:23:50:23:59 | throw ... |
| A.cs:30:21:30:23 | get_P3 | A.cs:30:28:30:37 | throw ... |
| A.cs:36:9:36:10 | M1 | A.cs:36:14:36:28 | {...} |
| A.cs:36:9:36:10 | M1 | B.cs:34:17:34:17 | 0 |
| A.cs:37:9:37:10 | M2 | A.cs:37:14:37:28 | {...} |
| A.cs:37:9:37:10 | M2 | C.cs:3:17:3:17 | 0 |
| B.cs:5:22:5:22 | get_P1 | A.cs:6:22:6:31 | throw ... |
| B.cs:5:22:5:22 | get_P1 | B.cs:5:22:5:22 | 0 |
| B.cs:6:21:6:23 | get_P2 | A.cs:7:25:7:39 | {...} |
| B.cs:6:21:6:23 | get_P2 | B.cs:6:25:6:37 | {...} |
| B.cs:6:39:6:41 | set_P2 | A.cs:7:45:7:59 | {...} |
| B.cs:6:39:6:41 | set_P2 | B.cs:6:43:6:45 | {...} |
| B.cs:7:16:7:16 | M | A.cs:8:23:8:32 | throw ... |
| B.cs:7:16:7:16 | M | B.cs:7:23:7:23 | 2 |
| B.cs:14:31:14:40 | get_Item | A.cs:14:31:14:31 | access to parameter i |
| B.cs:14:31:14:40 | get_Item | B.cs:14:31:14:40 | throw ... |
| B.cs:15:36:15:38 | get_Item | A.cs:15:40:15:52 | {...} |
| B.cs:15:36:15:38 | get_Item | B.cs:15:40:15:54 | {...} |
| B.cs:15:56:15:58 | set_Item | A.cs:15:58:15:60 | {...} |
| B.cs:15:56:15:58 | set_Item | B.cs:15:60:15:62 | {...} |
| B.cs:16:17:16:18 | M1 | A.cs:17:5:19:5 | {...} |
| B.cs:16:17:16:18 | M1 | B.cs:17:5:19:5 | {...} |
| B.cs:18:9:18:31 | M2 | B.cs:18:21:18:30 | throw ... |
| B.cs:20:12:20:13 | C2 | A.cs:20:22:20:31 | {...} |
| B.cs:20:12:20:13 | C2 | B.cs:20:22:20:36 | {...} |
| B.cs:21:12:21:13 | C2 | A.cs:21:27:21:29 | {...} |
| B.cs:21:12:21:13 | C2 | B.cs:21:27:21:29 | {...} |
| B.cs:22:6:22:7 | ~C2 | A.cs:22:11:22:13 | {...} |
| B.cs:22:6:22:7 | ~C2 | B.cs:22:11:22:25 | {...} |
| B.cs:23:28:23:35 | implicit conversion | A.cs:23:50:23:53 | null |
| B.cs:23:28:23:35 | implicit conversion | B.cs:23:50:23:59 | throw ... |
| B.cs:29:21:29:23 | get_P3 | A.cs:30:28:30:37 | throw ... |
| B.cs:34:9:34:10 | M1 | A.cs:36:14:36:28 | {...} |
| B.cs:34:9:34:10 | M1 | B.cs:34:17:34:17 | 0 |
| C.cs:3:9:3:10 | M2 | A.cs:37:14:37:28 | {...} |
| C.cs:3:9:3:10 | M2 | C.cs:3:17:3:17 | 0 |

View File

@@ -1,3 +1,11 @@
| A.cs:4:7:4:8 | C1 |
| A.cs:6:16:6:17 | P1 |
| A.cs:6:22:6:31 | get_P1 |
| A.cs:7:16:7:17 | P2 |
| A.cs:7:21:7:23 | get_P2 |
| A.cs:7:41:7:43 | set_P2 |
| A.cs:7:41:7:43 | value |
| A.cs:8:16:8:16 | M |
| A.cs:11:7:11:8 | C2 |
| A.cs:13:16:13:16 | F |
| A.cs:14:16:14:19 | Item |
@@ -24,6 +32,12 @@
| A.cs:24:20:24:22 | get_P |
| A.cs:24:25:24:27 | set_P |
| A.cs:24:25:24:27 | value |
| A.cs:28:7:28:8 | C3 |
| A.cs:30:16:30:17 | P3 |
| A.cs:30:21:30:23 | get_P3 |
| A.cs:34:15:34:16 | C4 |
| A.cs:36:9:36:10 | M1 |
| A.cs:37:9:37:10 | M2 |
| B.cs:3:7:3:8 | C1 |
| B.cs:5:16:5:17 | P1 |
| B.cs:5:22:5:22 | get_P1 |
@@ -32,7 +46,32 @@
| B.cs:6:39:6:41 | set_P2 |
| B.cs:6:39:6:41 | value |
| B.cs:7:16:7:16 | M |
| B.cs:11:7:11:8 | C2 |
| B.cs:13:16:13:16 | F |
| B.cs:14:16:14:19 | Item |
| B.cs:14:25:14:25 | i |
| B.cs:14:25:14:25 | i |
| B.cs:14:31:14:40 | get_Item |
| B.cs:15:19:15:22 | Item |
| B.cs:15:31:15:31 | s |
| B.cs:15:31:15:31 | s |
| B.cs:15:31:15:31 | s |
| B.cs:15:36:15:38 | get_Item |
| B.cs:15:56:15:58 | set_Item |
| B.cs:15:56:15:58 | value |
| B.cs:16:17:16:18 | M1 |
| B.cs:16:24:16:24 | i |
| B.cs:18:9:18:31 | M2 |
| B.cs:20:12:20:13 | C2 |
| B.cs:20:19:20:19 | i |
| B.cs:21:12:21:13 | C2 |
| B.cs:22:6:22:7 | ~C2 |
| B.cs:23:28:23:35 | implicit conversion |
| B.cs:23:44:23:44 | i |
| B.cs:24:16:24:16 | P |
| B.cs:24:20:24:22 | get_P |
| B.cs:24:25:24:27 | set_P |
| B.cs:24:25:24:27 | value |
| B.cs:27:7:27:8 | C3 |
| B.cs:29:16:29:17 | P3 |
| B.cs:29:21:29:23 | get_P3 |

View File

@@ -1,31 +0,0 @@
| A.cs:14:31:14:31 | get_Item | A.cs:14:31:14:31 | access to parameter i |
| A.cs:14:31:14:31 | get_Item | B.cs:14:31:14:40 | throw ... |
| A.cs:15:36:15:38 | get_Item | A.cs:15:40:15:52 | {...} |
| A.cs:15:36:15:38 | get_Item | B.cs:15:40:15:54 | {...} |
| A.cs:15:54:15:56 | set_Item | A.cs:15:58:15:60 | {...} |
| A.cs:15:54:15:56 | set_Item | B.cs:15:60:15:62 | {...} |
| A.cs:16:17:16:18 | M1 | A.cs:17:5:19:5 | {...} |
| A.cs:16:17:16:18 | M1 | B.cs:17:5:19:5 | {...} |
| A.cs:18:9:18:22 | M2 | A.cs:18:21:18:21 | 0 |
| A.cs:20:12:20:13 | C2 | A.cs:20:22:20:31 | {...} |
| A.cs:20:12:20:13 | C2 | B.cs:20:22:20:36 | {...} |
| A.cs:21:12:21:13 | C2 | A.cs:21:27:21:29 | {...} |
| A.cs:21:12:21:13 | C2 | B.cs:21:27:21:29 | {...} |
| A.cs:22:6:22:7 | ~C2 | A.cs:22:11:22:13 | {...} |
| A.cs:22:6:22:7 | ~C2 | B.cs:22:11:22:25 | {...} |
| A.cs:23:28:23:35 | implicit conversion | A.cs:23:50:23:53 | null |
| A.cs:23:28:23:35 | implicit conversion | B.cs:23:50:23:59 | throw ... |
| B.cs:5:22:5:22 | get_P1 | A.cs:6:22:6:31 | throw ... |
| B.cs:5:22:5:22 | get_P1 | B.cs:5:22:5:22 | 0 |
| B.cs:6:21:6:23 | get_P2 | A.cs:7:25:7:39 | {...} |
| B.cs:6:21:6:23 | get_P2 | B.cs:6:25:6:37 | {...} |
| B.cs:6:39:6:41 | set_P2 | A.cs:7:45:7:59 | {...} |
| B.cs:6:39:6:41 | set_P2 | B.cs:6:43:6:45 | {...} |
| B.cs:7:16:7:16 | M | A.cs:8:23:8:32 | throw ... |
| B.cs:7:16:7:16 | M | B.cs:7:23:7:23 | 2 |
| B.cs:18:9:18:31 | M2 | B.cs:18:21:18:30 | throw ... |
| B.cs:29:21:29:23 | get_P3 | A.cs:30:28:30:37 | throw ... |
| B.cs:34:9:34:10 | M1 | A.cs:36:14:36:28 | {...} |
| B.cs:34:9:34:10 | M1 | B.cs:34:17:34:17 | 0 |
| C.cs:3:9:3:10 | M2 | A.cs:37:14:37:28 | {...} |
| C.cs:3:9:3:10 | M2 | C.cs:3:17:3:17 | 0 |

View File

@@ -1,4 +0,0 @@
import csharp
from Callable c
select c, c.getABody()

View File

@@ -1 +1,4 @@
| A.cs:14:16:14:19 | Item | A.cs:14:31:14:31 | access to parameter i |
| A.cs:14:16:14:19 | Item | B.cs:14:31:14:40 | throw ... |
| B.cs:14:16:14:19 | Item | A.cs:14:31:14:31 | access to parameter i |
| B.cs:14:16:14:19 | Item | B.cs:14:31:14:40 | throw ... |

View File

@@ -1,60 +0,0 @@
| C2 | A.cs:20:12:20:13 | A.cs:20:12:20:13 |
| C2 | A.cs:21:12:21:13 | A.cs:21:12:21:13 |
| C2 | B.cs:20:12:20:13 | B.cs:20:12:20:13 |
| C2 | B.cs:21:12:21:13 | B.cs:21:12:21:13 |
| F | A.cs:13:16:13:16 | A.cs:13:16:13:16 |
| F | B.cs:13:16:13:16 | B.cs:13:16:13:16 |
| F | test.dll:0:0:0:0 | test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null |
| Item | A.cs:14:16:14:19 | A.cs:14:16:14:19 |
| Item | B.cs:14:16:14:19 | B.cs:14:16:14:19 |
| Item | test.dll:0:0:0:0 | test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null |
| M | A.cs:8:16:8:16 | A.cs:8:16:8:16 |
| M | B.cs:7:16:7:16 | B.cs:7:16:7:16 |
| M | test.dll:0:0:0:0 | test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null |
| M1 | A.cs:16:17:16:18 | A.cs:16:17:16:18 |
| M1 | A.cs:36:9:36:10 | A.cs:36:9:36:10 |
| M1 | B.cs:16:17:16:18 | B.cs:16:17:16:18 |
| M1 | B.cs:34:9:34:10 | B.cs:34:9:34:10 |
| M1 | test.dll:0:0:0:0 | test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null |
| M2 | A.cs:37:9:37:10 | A.cs:37:9:37:10 |
| M2 | C.cs:3:9:3:10 | C.cs:3:9:3:10 |
| M2 | test.dll:0:0:0:0 | test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null |
| P | A.cs:24:16:24:16 | A.cs:24:16:24:16 |
| P | B.cs:24:16:24:16 | B.cs:24:16:24:16 |
| P | test.dll:0:0:0:0 | test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null |
| P1 | A.cs:6:16:6:17 | A.cs:6:16:6:17 |
| P1 | B.cs:5:16:5:17 | B.cs:5:16:5:17 |
| P1 | test.dll:0:0:0:0 | test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null |
| get_Item | A.cs:14:31:14:31 | A.cs:14:31:14:31 |
| get_Item | A.cs:15:36:15:38 | A.cs:15:36:15:38 |
| get_Item | B.cs:14:31:14:40 | B.cs:14:31:14:40 |
| get_Item | B.cs:15:36:15:38 | B.cs:15:36:15:38 |
| get_Item | test.dll:0:0:0:0 | test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null |
| get_P | A.cs:24:20:24:22 | A.cs:24:20:24:22 |
| get_P | B.cs:24:20:24:22 | B.cs:24:20:24:22 |
| get_P | test.dll:0:0:0:0 | test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null |
| get_P1 | A.cs:6:22:6:31 | A.cs:6:22:6:31 |
| get_P1 | B.cs:5:22:5:22 | B.cs:5:22:5:22 |
| get_P1 | test.dll:0:0:0:0 | test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null |
| get_P2 | A.cs:7:21:7:23 | A.cs:7:21:7:23 |
| get_P2 | B.cs:6:21:6:23 | B.cs:6:21:6:23 |
| get_P2 | test.dll:0:0:0:0 | test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null |
| get_P3 | A.cs:30:21:30:23 | A.cs:30:21:30:23 |
| get_P3 | B.cs:29:21:29:23 | B.cs:29:21:29:23 |
| get_P3 | test.dll:0:0:0:0 | test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null |
| i | A.cs:16:24:16:24 | A.cs:16:24:16:24 |
| i | B.cs:16:24:16:24 | B.cs:16:24:16:24 |
| implicit conversion | A.cs:23:28:23:35 | A.cs:23:28:23:35 |
| implicit conversion | B.cs:23:28:23:35 | B.cs:23:28:23:35 |
| implicit conversion | test.dll:0:0:0:0 | test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null |
| set_Item | A.cs:15:54:15:56 | A.cs:15:54:15:56 |
| set_Item | B.cs:15:56:15:58 | B.cs:15:56:15:58 |
| set_Item | test.dll:0:0:0:0 | test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null |
| set_P | A.cs:24:25:24:27 | A.cs:24:25:24:27 |
| set_P | B.cs:24:25:24:27 | B.cs:24:25:24:27 |
| set_P | test.dll:0:0:0:0 | test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null |
| set_P2 | A.cs:7:41:7:43 | A.cs:7:41:7:43 |
| set_P2 | B.cs:6:39:6:41 | B.cs:6:39:6:41 |
| set_P2 | test.dll:0:0:0:0 | test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null |
| ~C2 | A.cs:22:6:22:7 | A.cs:22:6:22:7 |
| ~C2 | B.cs:22:6:22:7 | B.cs:22:6:22:7 |

View File

@@ -1,5 +0,0 @@
import csharp
import semmle.code.csharp.ExprOrStmtParent
from MultiImplementationsParent p
select p.toString(), p.getALocation()

View File

@@ -1 +1,4 @@
| A.cs:16:24:16:24 | i | A.cs:16:28:16:28 | 0 |
| A.cs:16:24:16:24 | i | B.cs:16:28:16:28 | 1 |
| B.cs:16:24:16:24 | i | A.cs:16:28:16:28 | 0 |
| B.cs:16:24:16:24 | i | B.cs:16:28:16:28 | 1 |

View File

@@ -1,2 +1,8 @@
| A.cs:6:16:6:17 | P1 | A.cs:6:22:6:31 | throw ... | body |
| A.cs:6:16:6:17 | P1 | B.cs:5:22:5:22 | 0 | body |
| A.cs:24:16:24:16 | P | A.cs:24:34:24:34 | 0 | initializer |
| A.cs:24:16:24:16 | P | B.cs:24:34:24:34 | 1 | initializer |
| B.cs:5:16:5:17 | P1 | A.cs:6:22:6:31 | throw ... | body |
| B.cs:5:16:5:17 | P1 | B.cs:5:22:5:22 | 0 | body |
| B.cs:24:16:24:16 | P | A.cs:24:34:24:34 | 0 | initializer |
| B.cs:24:16:24:16 | P | B.cs:24:34:24:34 | 1 | initializer |

View File

@@ -0,0 +1,2 @@
| A.cs:5:12:5:13 | M1 | This method has multiple bodies. |
| B.cs:5:12:5:13 | M1 | This method has multiple bodies. |

View File

@@ -1 +1,2 @@
| A.cs:3:7:3:7 | C |
| B.cs:3:7:3:7 | C |

View File

@@ -1,2 +1,4 @@
| A.cs:5:12:5:13 | M1 |
| A.cs:6:10:6:11 | M2 |
| B.cs:5:12:5:13 | M1 |
| B.cs:6:10:6:11 | M3 |