mirror of
https://github.com/github/codeql.git
synced 2026-04-30 03:05:15 +02:00
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:
@@ -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()) }
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
@@ -1,4 +0,0 @@
|
||||
import csharp
|
||||
|
||||
from Callable c
|
||||
select c, c.getABody()
|
||||
@@ -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 ... |
|
||||
|
||||
@@ -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 |
|
||||
@@ -1,5 +0,0 @@
|
||||
import csharp
|
||||
import semmle.code.csharp.ExprOrStmtParent
|
||||
|
||||
from MultiImplementationsParent p
|
||||
select p.toString(), p.getALocation()
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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. |
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
| A.cs:3:7:3:7 | C |
|
||||
| B.cs:3:7:3:7 | C |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
Reference in New Issue
Block a user