mirror of
https://github.com/github/codeql.git
synced 2026-07-19 18:28:13 +02:00
C#: Autoformat QLL files
This commit is contained in:
@@ -4,28 +4,18 @@ private import semmle.code.csharp.frameworks.System
|
||||
private import semmle.code.csharp.frameworks.system.web.UI
|
||||
|
||||
class DisposableType extends RefType {
|
||||
DisposableType() {
|
||||
this.getABaseType+() instanceof SystemIDisposableInterface
|
||||
}
|
||||
DisposableType() { this.getABaseType+() instanceof SystemIDisposableInterface }
|
||||
}
|
||||
|
||||
class DisposableField extends Field {
|
||||
DisposableField() {
|
||||
this.getType() instanceof DisposableType
|
||||
}
|
||||
DisposableField() { this.getType() instanceof DisposableType }
|
||||
}
|
||||
|
||||
class WebControl extends RefType {
|
||||
WebControl() {
|
||||
this.getBaseClass*() instanceof SystemWebUIControlClass
|
||||
}
|
||||
WebControl() { this.getBaseClass*() instanceof SystemWebUIControlClass }
|
||||
}
|
||||
|
||||
class WebPage extends RefType {
|
||||
WebPage() {
|
||||
this.getBaseClass*() instanceof SystemWebUIPageClass
|
||||
}
|
||||
}
|
||||
class WebPage extends RefType { WebPage() { this.getBaseClass*() instanceof SystemWebUIPageClass } }
|
||||
|
||||
/**
|
||||
* Holds if `f` is an auto-disposed web control.
|
||||
@@ -37,15 +27,13 @@ class WebPage extends RefType {
|
||||
* `UnloadRecursive()`.
|
||||
*/
|
||||
predicate isAutoDisposedWebControl(Field f) {
|
||||
f.getType() instanceof WebControl
|
||||
and
|
||||
f.getType() instanceof WebControl and
|
||||
f.getDeclaringType() = any(RefType t |
|
||||
t instanceof WebControl or
|
||||
t instanceof WebPage
|
||||
)
|
||||
t instanceof WebControl or
|
||||
t instanceof WebPage
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* An object creation that creates an `IDisposable` instance into the local scope.
|
||||
*/
|
||||
@@ -56,13 +44,13 @@ class LocalScopeDisposableCreation extends Call {
|
||||
// Type extends IDisposable
|
||||
t instanceof DisposableType and
|
||||
// Within function, not field or instance initializer
|
||||
exists(this.getEnclosingCallable()) |
|
||||
exists(this.getEnclosingCallable())
|
||||
|
|
||||
// Either an ordinary object creation
|
||||
this instanceof ObjectCreation
|
||||
or
|
||||
// Or a creation using a factory method
|
||||
exists(Method create |
|
||||
this.getTarget() = create |
|
||||
exists(Method create | this.getTarget() = create |
|
||||
create.hasName("Create") and
|
||||
create.isStatic() and
|
||||
create.getDeclaringType().getSourceDeclaration() = t.getSourceDeclaration()
|
||||
@@ -74,20 +62,17 @@ class LocalScopeDisposableCreation extends Call {
|
||||
* Gets an expression that, if it is disposed of, will imply that the object
|
||||
* created by this creation is disposed of as well.
|
||||
*/
|
||||
Expr getADisposeTarget() {
|
||||
result = getADisposeTarget0().asExpr()
|
||||
}
|
||||
Expr getADisposeTarget() { result = getADisposeTarget0().asExpr() }
|
||||
|
||||
private DataFlow::Node getADisposeTarget0() {
|
||||
result = exprNode(this)
|
||||
or
|
||||
exists(DataFlow::Node mid |
|
||||
mid = this.getADisposeTarget0() |
|
||||
exists(DataFlow::Node mid | mid = this.getADisposeTarget0() |
|
||||
localFlowStep(mid, result)
|
||||
or
|
||||
result.asExpr() = any(LocalScopeDisposableCreation other |
|
||||
other.getAnArgument() = mid.asExpr()
|
||||
)
|
||||
other.getAnArgument() = mid.asExpr()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,45 +3,188 @@ import semmle.code.csharp.commons.GeneratedCode
|
||||
import semmle.code.csharp.frameworks.System
|
||||
|
||||
/*
|
||||
*
|
||||
* Counting nontrivial literal occurrences
|
||||
*
|
||||
*/
|
||||
|
||||
private predicate trivialPositiveIntValue(string s) {
|
||||
s="0" or s="1" or s="2" or s="3" or s="4" or s="5" or s="6" or s="7" or s="8" or
|
||||
s="9" or s="10" or s="11" or s="12" or s="13" or s="14" or s="15" or s="16" or s="17" or
|
||||
s="18" or s="19" or s="20"
|
||||
|
||||
or
|
||||
|
||||
s="16" or s="32" or s="64" or s="128" or s="256" or s="512" or s="1024" or
|
||||
s="2048" or s="4096" or s="16384" or s="32768" or s="65536" or
|
||||
s="1048576" or s="2147483648" or s="4294967296"
|
||||
|
||||
or
|
||||
|
||||
s="15" or s="31" or s="63" or s="127" or s="255" or s="511" or s="1023" or
|
||||
s="2047" or s="4095" or s="16383" or s="32767" or s="65535" or
|
||||
s="1048577" or s="2147483647" or s="4294967295"
|
||||
|
||||
or
|
||||
|
||||
s = "0x00000001" or s = "0x00000002" or s = "0x00000004" or s = "0x00000008" or s = "0x00000010" or s = "0x00000020" or s = "0x00000040" or s = "0x00000080" or s = "0x00000100" or s = "0x00000200" or s = "0x00000400" or s = "0x00000800" or s = "0x00001000" or s = "0x00002000" or s = "0x00004000" or s = "0x00008000" or s = "0x00010000" or s = "0x00020000" or s = "0x00040000" or s = "0x00080000" or s = "0x00100000" or s = "0x00200000" or s = "0x00400000" or s = "0x00800000" or s = "0x01000000" or s = "0x02000000" or s = "0x04000000" or s = "0x08000000" or s = "0x10000000" or s = "0x20000000" or s = "0x40000000" or s = "0x80000000" or
|
||||
s = "0x00000001" or s = "0x00000003" or s = "0x00000007" or s = "0x0000000f" or s = "0x0000001f" or s = "0x0000003f" or s = "0x0000007f" or s = "0x000000ff" or s = "0x000001ff" or s = "0x000003ff" or s = "0x000007ff" or s = "0x00000fff" or s = "0x00001fff" or s = "0x00003fff" or s = "0x00007fff" or s = "0x0000ffff" or s = "0x0001ffff" or s = "0x0003ffff" or s = "0x0007ffff" or s = "0x000fffff" or s = "0x001fffff" or s = "0x003fffff" or s = "0x007fffff" or s = "0x00ffffff" or s = "0x01ffffff" or s = "0x03ffffff" or s = "0x07ffffff" or s = "0x0fffffff" or s = "0x1fffffff" or s = "0x3fffffff" or s = "0x7fffffff" or s = "0xffffffff" or
|
||||
s = "0x0001" or s = "0x0002" or s = "0x0004" or s = "0x0008" or s = "0x0010" or s = "0x0020" or s = "0x0040" or s = "0x0080" or s = "0x0100" or s = "0x0200" or s = "0x0400" or s = "0x0800" or s = "0x1000" or s = "0x2000" or s = "0x4000" or s = "0x8000" or
|
||||
s = "0x0001" or s = "0x0003" or s = "0x0007" or s = "0x000f" or s = "0x001f" or s = "0x003f" or s = "0x007f" or s = "0x00ff" or s = "0x01ff" or s = "0x03ff" or s = "0x07ff" or s = "0x0fff" or s = "0x1fff" or s = "0x3fff" or s = "0x7fff" or s = "0xffff" or
|
||||
s = "0x01" or s = "0x02" or s = "0x04" or s = "0x08" or s = "0x10" or s = "0x20" or s = "0x40" or s = "0x80" or
|
||||
s = "0x01" or s = "0x03" or s = "0x07" or s = "0x0f" or s = "0x1f" or s = "0x3f" or s = "0x7f" or s = "0xff" or
|
||||
s = "0x00"
|
||||
|
||||
or
|
||||
|
||||
s = "10" or s = "100" or s = "1000" or s = "10000" or s = "100000" or s = "1000000" or s = "10000000" or s = "100000000" or s = "1000000000"
|
||||
s = "0" or
|
||||
s = "1" or
|
||||
s = "2" or
|
||||
s = "3" or
|
||||
s = "4" or
|
||||
s = "5" or
|
||||
s = "6" or
|
||||
s = "7" or
|
||||
s = "8" or
|
||||
s = "9" or
|
||||
s = "10" or
|
||||
s = "11" or
|
||||
s = "12" or
|
||||
s = "13" or
|
||||
s = "14" or
|
||||
s = "15" or
|
||||
s = "16" or
|
||||
s = "17" or
|
||||
s = "18" or
|
||||
s = "19" or
|
||||
s = "20" or
|
||||
s = "16" or
|
||||
s = "32" or
|
||||
s = "64" or
|
||||
s = "128" or
|
||||
s = "256" or
|
||||
s = "512" or
|
||||
s = "1024" or
|
||||
s = "2048" or
|
||||
s = "4096" or
|
||||
s = "16384" or
|
||||
s = "32768" or
|
||||
s = "65536" or
|
||||
s = "1048576" or
|
||||
s = "2147483648" or
|
||||
s = "4294967296" or
|
||||
s = "15" or
|
||||
s = "31" or
|
||||
s = "63" or
|
||||
s = "127" or
|
||||
s = "255" or
|
||||
s = "511" or
|
||||
s = "1023" or
|
||||
s = "2047" or
|
||||
s = "4095" or
|
||||
s = "16383" or
|
||||
s = "32767" or
|
||||
s = "65535" or
|
||||
s = "1048577" or
|
||||
s = "2147483647" or
|
||||
s = "4294967295" or
|
||||
s = "0x00000001" or
|
||||
s = "0x00000002" or
|
||||
s = "0x00000004" or
|
||||
s = "0x00000008" or
|
||||
s = "0x00000010" or
|
||||
s = "0x00000020" or
|
||||
s = "0x00000040" or
|
||||
s = "0x00000080" or
|
||||
s = "0x00000100" or
|
||||
s = "0x00000200" or
|
||||
s = "0x00000400" or
|
||||
s = "0x00000800" or
|
||||
s = "0x00001000" or
|
||||
s = "0x00002000" or
|
||||
s = "0x00004000" or
|
||||
s = "0x00008000" or
|
||||
s = "0x00010000" or
|
||||
s = "0x00020000" or
|
||||
s = "0x00040000" or
|
||||
s = "0x00080000" or
|
||||
s = "0x00100000" or
|
||||
s = "0x00200000" or
|
||||
s = "0x00400000" or
|
||||
s = "0x00800000" or
|
||||
s = "0x01000000" or
|
||||
s = "0x02000000" or
|
||||
s = "0x04000000" or
|
||||
s = "0x08000000" or
|
||||
s = "0x10000000" or
|
||||
s = "0x20000000" or
|
||||
s = "0x40000000" or
|
||||
s = "0x80000000" or
|
||||
s = "0x00000001" or
|
||||
s = "0x00000003" or
|
||||
s = "0x00000007" or
|
||||
s = "0x0000000f" or
|
||||
s = "0x0000001f" or
|
||||
s = "0x0000003f" or
|
||||
s = "0x0000007f" or
|
||||
s = "0x000000ff" or
|
||||
s = "0x000001ff" or
|
||||
s = "0x000003ff" or
|
||||
s = "0x000007ff" or
|
||||
s = "0x00000fff" or
|
||||
s = "0x00001fff" or
|
||||
s = "0x00003fff" or
|
||||
s = "0x00007fff" or
|
||||
s = "0x0000ffff" or
|
||||
s = "0x0001ffff" or
|
||||
s = "0x0003ffff" or
|
||||
s = "0x0007ffff" or
|
||||
s = "0x000fffff" or
|
||||
s = "0x001fffff" or
|
||||
s = "0x003fffff" or
|
||||
s = "0x007fffff" or
|
||||
s = "0x00ffffff" or
|
||||
s = "0x01ffffff" or
|
||||
s = "0x03ffffff" or
|
||||
s = "0x07ffffff" or
|
||||
s = "0x0fffffff" or
|
||||
s = "0x1fffffff" or
|
||||
s = "0x3fffffff" or
|
||||
s = "0x7fffffff" or
|
||||
s = "0xffffffff" or
|
||||
s = "0x0001" or
|
||||
s = "0x0002" or
|
||||
s = "0x0004" or
|
||||
s = "0x0008" or
|
||||
s = "0x0010" or
|
||||
s = "0x0020" or
|
||||
s = "0x0040" or
|
||||
s = "0x0080" or
|
||||
s = "0x0100" or
|
||||
s = "0x0200" or
|
||||
s = "0x0400" or
|
||||
s = "0x0800" or
|
||||
s = "0x1000" or
|
||||
s = "0x2000" or
|
||||
s = "0x4000" or
|
||||
s = "0x8000" or
|
||||
s = "0x0001" or
|
||||
s = "0x0003" or
|
||||
s = "0x0007" or
|
||||
s = "0x000f" or
|
||||
s = "0x001f" or
|
||||
s = "0x003f" or
|
||||
s = "0x007f" or
|
||||
s = "0x00ff" or
|
||||
s = "0x01ff" or
|
||||
s = "0x03ff" or
|
||||
s = "0x07ff" or
|
||||
s = "0x0fff" or
|
||||
s = "0x1fff" or
|
||||
s = "0x3fff" or
|
||||
s = "0x7fff" or
|
||||
s = "0xffff" or
|
||||
s = "0x01" or
|
||||
s = "0x02" or
|
||||
s = "0x04" or
|
||||
s = "0x08" or
|
||||
s = "0x10" or
|
||||
s = "0x20" or
|
||||
s = "0x40" or
|
||||
s = "0x80" or
|
||||
s = "0x01" or
|
||||
s = "0x03" or
|
||||
s = "0x07" or
|
||||
s = "0x0f" or
|
||||
s = "0x1f" or
|
||||
s = "0x3f" or
|
||||
s = "0x7f" or
|
||||
s = "0xff" or
|
||||
s = "0x00" or
|
||||
s = "10" or
|
||||
s = "100" or
|
||||
s = "1000" or
|
||||
s = "10000" or
|
||||
s = "100000" or
|
||||
s = "1000000" or
|
||||
s = "10000000" or
|
||||
s = "100000000" or
|
||||
s = "1000000000"
|
||||
}
|
||||
|
||||
private predicate trivialIntValue(string s) {
|
||||
trivialPositiveIntValue(s) or
|
||||
trivialPositiveIntValue(s)
|
||||
or
|
||||
exists(string pos | trivialPositiveIntValue(pos) and s = "-" + pos)
|
||||
}
|
||||
|
||||
@@ -50,7 +193,15 @@ private predicate intTrivial(Literal lit) {
|
||||
}
|
||||
|
||||
private predicate powerOfTen(float f) {
|
||||
f = 10 or f = 100 or f = 1000 or f = 10000 or f = 100000 or f = 1000000 or f = 10000000 or f = 100000000 or f = 1000000000
|
||||
f = 10 or
|
||||
f = 100 or
|
||||
f = 1000 or
|
||||
f = 10000 or
|
||||
f = 100000 or
|
||||
f = 1000000 or
|
||||
f = 10000000 or
|
||||
f = 100000000 or
|
||||
f = 1000000000
|
||||
}
|
||||
|
||||
private predicate floatTrivial(Literal lit) {
|
||||
@@ -59,16 +210,12 @@ private predicate floatTrivial(Literal lit) {
|
||||
lit.getValue() = value and
|
||||
f = value.toFloat() and
|
||||
(f.abs() <= 20.0 or powerOfTen(f))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate stringTrivial(StringLiteral lit) {
|
||||
lit.getValue().length() < 8
|
||||
}
|
||||
private predicate stringTrivial(StringLiteral lit) { lit.getValue().length() < 8 }
|
||||
|
||||
private predicate small(Literal lit) {
|
||||
lit.getValue().length() <= 1
|
||||
}
|
||||
private predicate small(Literal lit) { lit.getValue().length() <= 1 }
|
||||
|
||||
private predicate trivial(Literal lit) {
|
||||
intTrivial(lit) or
|
||||
@@ -81,10 +228,8 @@ private predicate trivial(Literal lit) {
|
||||
}
|
||||
|
||||
private predicate literalIsConstantInitializer(Literal literal, string value, Field f) {
|
||||
literal.getValue() = value
|
||||
and
|
||||
literal = f.getInitializer().stripCasts()
|
||||
and
|
||||
literal.getValue() = value and
|
||||
literal = f.getInitializer().stripCasts() and
|
||||
(
|
||||
f.isReadOnly() and f.isStatic()
|
||||
or
|
||||
@@ -93,19 +238,16 @@ private predicate literalIsConstantInitializer(Literal literal, string value, Fi
|
||||
}
|
||||
|
||||
private predicate literalInArrayInitializer(Literal literal) {
|
||||
exists(ArrayInitializer arrayInit |
|
||||
arrayInit.getAnElement().stripCasts() = literal
|
||||
)
|
||||
exists(ArrayInitializer arrayInit | arrayInit.getAnElement().stripCasts() = literal)
|
||||
}
|
||||
|
||||
private predicate literalInAttribute(Literal literal) {
|
||||
literal.getParent+() instanceof Attribute
|
||||
}
|
||||
private predicate literalInAttribute(Literal literal) { literal.getParent+() instanceof Attribute }
|
||||
|
||||
private predicate literalInGetHashCode(Literal literal) {
|
||||
exists(Method m, Method getHashCode |
|
||||
m = literal.getEnclosingCallable() and
|
||||
getHashCode = any(SystemObjectClass c).getGetHashCodeMethod() |
|
||||
getHashCode = any(SystemObjectClass c).getGetHashCodeMethod()
|
||||
|
|
||||
m = getHashCode.getAnOverrider*()
|
||||
)
|
||||
}
|
||||
@@ -121,8 +263,10 @@ private predicate relevantLiteral(Literal literal, string value) {
|
||||
}
|
||||
|
||||
private predicate valueOccurrenceCount(string value, int n) {
|
||||
n = strictcount(Location loc | exists(Literal lit | relevantLiteral(lit, value) | lit.getLocation()=loc))
|
||||
and n > 20
|
||||
n = strictcount(Location loc |
|
||||
exists(Literal lit | relevantLiteral(lit, value) | lit.getLocation() = loc)
|
||||
) and
|
||||
n > 20
|
||||
}
|
||||
|
||||
private predicate occurenceCount(Literal lit, string value, int n) {
|
||||
@@ -131,24 +275,23 @@ private predicate occurenceCount(Literal lit, string value, int n) {
|
||||
relevantLiteral(lit, value)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* Literals repeated frequently
|
||||
*
|
||||
*/
|
||||
|
||||
private predicate check(Literal lit, string value, int n, File f) {
|
||||
// Check that the literal is nontrivial
|
||||
not trivial(lit) and
|
||||
// Check that it is repeated a number of times
|
||||
occurenceCount(lit, value, n) and n > 20 and
|
||||
occurenceCount(lit, value, n) and
|
||||
n > 20 and
|
||||
f = lit.getFile()
|
||||
}
|
||||
|
||||
private predicate checkWithFileCount(string value, int overallCount, int fileCount, File f) {
|
||||
fileCount = strictcount(Location loc | exists(Literal lit |
|
||||
check(lit, value, overallCount, f) | lit.getLocation()=loc) )
|
||||
fileCount = strictcount(Location loc |
|
||||
exists(Literal lit | check(lit, value, overallCount, f) | lit.getLocation() = loc)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate start(Literal lit, int startLine) {
|
||||
@@ -164,32 +307,34 @@ private predicate firstOccurrence(Literal lit, string value, int n) {
|
||||
check(lit2, value, n, f) and
|
||||
start(lit, start1) and
|
||||
start(lit2, start2) and
|
||||
start2 < start1)
|
||||
start2 < start1
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isFloat(Literal lit) {
|
||||
lit.getType() instanceof FloatingPointType
|
||||
}
|
||||
private predicate isFloat(Literal lit) { lit.getType() instanceof FloatingPointType }
|
||||
|
||||
predicate isNumber(Literal lit) {
|
||||
lit.getType() instanceof IntegralType
|
||||
or isFloat(lit)
|
||||
lit.getType() instanceof IntegralType or
|
||||
isFloat(lit)
|
||||
}
|
||||
|
||||
predicate magicConstant(Literal e, string msg) {
|
||||
exists(string value, int n | firstOccurrence(e, value, n)
|
||||
and msg = "Magic constant: literal '" + value + "' is repeated " + n.toString() + " times and should be encapsulated in a constant.")
|
||||
exists(string value, int n |
|
||||
firstOccurrence(e, value, n) and
|
||||
msg = "Magic constant: literal '" + value + "' is repeated " + n.toString() +
|
||||
" times and should be encapsulated in a constant."
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* Literals where there is a defined constant with the same value
|
||||
*
|
||||
*/
|
||||
|
||||
private predicate relevantField(Field f, string value) {
|
||||
exists(Literal lit | not trivial(lit) and value = lit.getValue() and literalIsConstantInitializer(lit, value, f))
|
||||
exists(Literal lit |
|
||||
not trivial(lit) and value = lit.getValue() and literalIsConstantInitializer(lit, value, f)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate relevantDeclaration(Declaration d, Literal lit, string value) {
|
||||
@@ -207,44 +352,43 @@ private predicate hasAccessModifier(Field field) {
|
||||
|
||||
//Default accessibility rules http://msdn.microsoft.com/en-us/library/ba0a1yw2%28v=vs.71%29.aspx
|
||||
private predicate isPublic(Field field) {
|
||||
field.isPublic() or
|
||||
(not hasAccessModifier(field) and (field.getDeclaringType() instanceof Interface or field.getDeclaringType() instanceof Enum))
|
||||
field.isPublic()
|
||||
or
|
||||
not hasAccessModifier(field) and
|
||||
(field.getDeclaringType() instanceof Interface or field.getDeclaringType() instanceof Enum)
|
||||
}
|
||||
|
||||
private predicate isPrivate(Field field) {
|
||||
field.isPrivate() or
|
||||
(not hasAccessModifier(field) and (field.getDeclaringType() instanceof Class or field.getDeclaringType() instanceof Struct))
|
||||
field.isPrivate()
|
||||
or
|
||||
not hasAccessModifier(field) and
|
||||
(field.getDeclaringType() instanceof Class or field.getDeclaringType() instanceof Struct)
|
||||
}
|
||||
|
||||
private predicate isVisible(Field field, string value, Declaration fromDeclaration) {
|
||||
//public
|
||||
(
|
||||
relevantField(field, value) and
|
||||
field.getDeclaringType().isPublic() and
|
||||
isPublic(field) and
|
||||
relevantDeclaration(fromDeclaration, _, value)
|
||||
)
|
||||
relevantField(field, value) and
|
||||
field.getDeclaringType().isPublic() and
|
||||
isPublic(field) and
|
||||
relevantDeclaration(fromDeclaration, _, value)
|
||||
or
|
||||
//in same class
|
||||
(
|
||||
relevantField(field, value) and
|
||||
exists(RefType t |
|
||||
t = field.getDeclaringType() and
|
||||
t = fromDeclaration.getDeclaringType())
|
||||
and relevantDeclaration(fromDeclaration, _, value)
|
||||
)
|
||||
relevantField(field, value) and
|
||||
exists(RefType t |
|
||||
t = field.getDeclaringType() and
|
||||
t = fromDeclaration.getDeclaringType()
|
||||
) and
|
||||
relevantDeclaration(fromDeclaration, _, value)
|
||||
or
|
||||
//in subclass and not private
|
||||
(
|
||||
relevantField(field, value) and
|
||||
not isPrivate(field) and
|
||||
exists(RefType sup, RefType sub |
|
||||
sup = field.getDeclaringType() and
|
||||
sub.getABaseType+() = sup and
|
||||
sub = fromDeclaration.getDeclaringType()
|
||||
) and
|
||||
relevantDeclaration(fromDeclaration, _, value)
|
||||
)
|
||||
relevantField(field, value) and
|
||||
not isPrivate(field) and
|
||||
exists(RefType sup, RefType sub |
|
||||
sup = field.getDeclaringType() and
|
||||
sub.getABaseType+() = sup and
|
||||
sub = fromDeclaration.getDeclaringType()
|
||||
) and
|
||||
relevantDeclaration(fromDeclaration, _, value)
|
||||
//internal and in same assembly
|
||||
//TODO: internal (I don't think we can do this yet, as we don't associate elements in source to assemblies)
|
||||
}
|
||||
@@ -252,24 +396,23 @@ private predicate isVisible(Field field, string value, Declaration fromDeclarati
|
||||
/**
|
||||
* Literal.getValue() restricted to literals which aren't used as constant initialisers.
|
||||
*/
|
||||
private string magicLiteralValue(Literal l) {
|
||||
relevantLiteral(l, result)
|
||||
}
|
||||
private string magicLiteralValue(Literal l) { relevantLiteral(l, result) }
|
||||
|
||||
private predicate canUseFieldInsteadOfLiteral(Field constField, Literal magicLiteral, string value) {
|
||||
exists(Literal initLiteral |
|
||||
literalIsConstantInitializer(initLiteral, value, constField) and
|
||||
value = initLiteral.getValue() and
|
||||
value = magicLiteralValue(magicLiteral)
|
||||
and exists(Declaration d |
|
||||
value = magicLiteralValue(magicLiteral) and
|
||||
exists(Declaration d |
|
||||
relevantDeclaration(d, magicLiteral, value) and
|
||||
isVisible(constField, value, d)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate literalInsteadOfConstant(Literal magicLiteral, string value, string message, Field constField) {
|
||||
predicate literalInsteadOfConstant(
|
||||
Literal magicLiteral, string value, string message, Field constField
|
||||
) {
|
||||
canUseFieldInsteadOfLiteral(constField, magicLiteral, value) and
|
||||
message =
|
||||
"Literal value '" + value + "' used instead of constant $@."
|
||||
message = "Literal value '" + value + "' used instead of constant $@."
|
||||
}
|
||||
|
||||
@@ -1,120 +1,78 @@
|
||||
// Various utilities for writing concurrency queries.
|
||||
|
||||
import csharp
|
||||
|
||||
class WaitCall extends MethodCall
|
||||
{
|
||||
WaitCall()
|
||||
{
|
||||
class WaitCall extends MethodCall {
|
||||
WaitCall() {
|
||||
getTarget().hasName("Wait") and
|
||||
getTarget().getDeclaringType().hasQualifiedName("System.Threading.Monitor")
|
||||
}
|
||||
|
||||
Expr getExpr()
|
||||
{
|
||||
result = getArgument(0)
|
||||
}
|
||||
Expr getExpr() { result = getArgument(0) }
|
||||
}
|
||||
|
||||
class WaitStmt extends ExprStmt
|
||||
{
|
||||
WaitStmt()
|
||||
{
|
||||
getExpr() instanceof WaitCall
|
||||
}
|
||||
class WaitStmt extends ExprStmt {
|
||||
WaitStmt() { getExpr() instanceof WaitCall }
|
||||
|
||||
Expr getLock()
|
||||
{
|
||||
result = ((WaitCall)getExpr()).getExpr()
|
||||
}
|
||||
Expr getLock() { result = getExpr().(WaitCall).getExpr() }
|
||||
|
||||
// If we are waiting on a variable
|
||||
Variable getWaitVariable()
|
||||
{
|
||||
result.getAnAccess() = getLock()
|
||||
}
|
||||
Variable getWaitVariable() { result.getAnAccess() = getLock() }
|
||||
|
||||
// If we are waiting on 'this'
|
||||
predicate isWaitThis()
|
||||
{
|
||||
getLock() instanceof ThisAccess
|
||||
}
|
||||
predicate isWaitThis() { getLock() instanceof ThisAccess }
|
||||
|
||||
// If we are waiting on a typeof()
|
||||
Type getWaitTypeObject()
|
||||
{
|
||||
result = ((TypeofExpr)getLock()).getTypeAccess().getTarget()
|
||||
}
|
||||
Type getWaitTypeObject() { result = getLock().(TypeofExpr).getTypeAccess().getTarget() }
|
||||
}
|
||||
|
||||
class SynchronizedMethodAttribute extends Attribute
|
||||
{
|
||||
SynchronizedMethodAttribute()
|
||||
{
|
||||
class SynchronizedMethodAttribute extends Attribute {
|
||||
SynchronizedMethodAttribute() {
|
||||
getType().hasQualifiedName("System.Runtime.CompilerServices.MethodImplAttribute") and
|
||||
exists( MemberConstantAccess a, MemberConstant mc |
|
||||
a=getArgument(0) and
|
||||
a.getTarget()=mc and
|
||||
exists(MemberConstantAccess a, MemberConstant mc |
|
||||
a = getArgument(0) and
|
||||
a.getTarget() = mc and
|
||||
mc.hasName("Synchronized") and
|
||||
mc.getDeclaringType().hasQualifiedName("System.Runtime.CompilerServices.MethodImplOptions")
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// A method with attribute [MethodImpl(MethodImplOptions.Synchronized)]
|
||||
class SynchronizedMethod extends Method
|
||||
{
|
||||
SynchronizedMethod()
|
||||
{
|
||||
getAnAttribute() instanceof SynchronizedMethodAttribute
|
||||
}
|
||||
class SynchronizedMethod extends Method {
|
||||
SynchronizedMethod() { getAnAttribute() instanceof SynchronizedMethodAttribute }
|
||||
|
||||
predicate isLockThis()
|
||||
{
|
||||
not isStatic()
|
||||
}
|
||||
predicate isLockThis() { not isStatic() }
|
||||
|
||||
Type getLockTypeObject()
|
||||
{
|
||||
isStatic() and result=getDeclaringType()
|
||||
}
|
||||
Type getLockTypeObject() { isStatic() and result = getDeclaringType() }
|
||||
}
|
||||
|
||||
abstract class LockedBlock extends BlockStmt
|
||||
{
|
||||
abstract class LockedBlock extends BlockStmt {
|
||||
abstract predicate isLockThis();
|
||||
|
||||
abstract Variable getLockVariable();
|
||||
|
||||
abstract Type getLockTypeObject();
|
||||
|
||||
Stmt getALockedStmt()
|
||||
{
|
||||
Stmt getALockedStmt() {
|
||||
// Do this instead of getParent+, because we don't want to escape
|
||||
// delegates and lambdas
|
||||
result.getParent() = this
|
||||
or exists( Stmt mid | mid=getALockedStmt() and result.getParent()=mid )
|
||||
or
|
||||
exists(Stmt mid | mid = getALockedStmt() and result.getParent() = mid)
|
||||
}
|
||||
}
|
||||
|
||||
class LockStmtBlock extends LockedBlock
|
||||
{
|
||||
LockStmtBlock()
|
||||
{
|
||||
exists( LockStmt s | this=s.getBlock() )
|
||||
class LockStmtBlock extends LockedBlock {
|
||||
LockStmtBlock() { exists(LockStmt s | this = s.getBlock()) }
|
||||
|
||||
override predicate isLockThis() { exists(LockStmt s | this = s.getBlock() and s.isLockThis()) }
|
||||
|
||||
override Variable getLockVariable() {
|
||||
exists(LockStmt s | this = s.getBlock() and result = s.getLockVariable())
|
||||
}
|
||||
|
||||
override predicate isLockThis()
|
||||
{
|
||||
exists( LockStmt s | this=s.getBlock() and s.isLockThis() )
|
||||
}
|
||||
|
||||
override Variable getLockVariable()
|
||||
{
|
||||
exists( LockStmt s | this=s.getBlock() and result=s.getLockVariable() )
|
||||
}
|
||||
|
||||
override Type getLockTypeObject()
|
||||
{
|
||||
exists( LockStmt s | this=s.getBlock() and result=s.getLockTypeObject() )
|
||||
override Type getLockTypeObject() {
|
||||
exists(LockStmt s | this = s.getBlock() and result = s.getLockTypeObject())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,32 +82,24 @@ class LockStmtBlock extends LockedBlock
|
||||
class LockingCall extends MethodCall {
|
||||
LockingCall() {
|
||||
this.getTarget() = any(Method m |
|
||||
m.getDeclaringType().hasQualifiedName("System.Threading", "Monitor") and
|
||||
m.getName().matches("%Enter%")
|
||||
) or
|
||||
m.getDeclaringType().hasQualifiedName("System.Threading", "Monitor") and
|
||||
m.getName().matches("%Enter%")
|
||||
) or
|
||||
this.getTarget().hasName("EnterReadLock") or
|
||||
this.getTarget().hasName("EnterWriteLock")
|
||||
}
|
||||
}
|
||||
|
||||
class SynchronizedMethodBlock extends LockedBlock
|
||||
{
|
||||
SynchronizedMethodBlock()
|
||||
{
|
||||
exists( SynchronizedMethod m | this=m.getStatementBody() )
|
||||
}
|
||||
override predicate isLockThis()
|
||||
{
|
||||
exists( SynchronizedMethod m | this=m.getStatementBody() and m.isLockThis() )
|
||||
class SynchronizedMethodBlock extends LockedBlock {
|
||||
SynchronizedMethodBlock() { exists(SynchronizedMethod m | this = m.getStatementBody()) }
|
||||
|
||||
override predicate isLockThis() {
|
||||
exists(SynchronizedMethod m | this = m.getStatementBody() and m.isLockThis())
|
||||
}
|
||||
|
||||
override Variable getLockVariable()
|
||||
{
|
||||
none()
|
||||
}
|
||||
override Variable getLockVariable() { none() }
|
||||
|
||||
override Type getLockTypeObject()
|
||||
{
|
||||
exists( SynchronizedMethod m | this=m.getStatementBody() and result=m.getLockTypeObject() )
|
||||
override Type getLockTypeObject() {
|
||||
exists(SynchronizedMethod m | this = m.getStatementBody() and result = m.getLockTypeObject())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* In C#, fields are only one kind of data member -- indexers and properties
|
||||
* give rise to very similar concepts that we may want to treat uniformly.
|
||||
*/
|
||||
|
||||
import csharp
|
||||
|
||||
/**
|
||||
@@ -32,9 +33,7 @@ class DataMember extends Member {
|
||||
* A data member which is also a collection.
|
||||
*/
|
||||
class CollectionMember extends DataMember {
|
||||
CollectionMember() {
|
||||
this.getType().(ValueOrRefType).getABaseType*().hasName("ICollection")
|
||||
}
|
||||
CollectionMember() { this.getType().(ValueOrRefType).getABaseType*().hasName("ICollection") }
|
||||
|
||||
/**
|
||||
* Gets an expression corresponding to a read or write of this collection member.
|
||||
@@ -60,8 +59,11 @@ class CollectionMember extends DataMember {
|
||||
// A write of a field or property can be a method call to certain methods...
|
||||
exists(MethodCall call | call = result |
|
||||
call.getQualifier() = this.getAnAccess() and
|
||||
call.getTarget().getName().regexpMatch("Add.*|Append|Clear.*|Delete|"
|
||||
+ "(Try)?Dequeue|Enqueue|Insert.*|(Try)?Pop|Push|(Try?)Remove.*|Replace.*|SafeDelete|Set.*|")
|
||||
call
|
||||
.getTarget()
|
||||
.getName()
|
||||
.regexpMatch("Add.*|Append|Clear.*|Delete|" +
|
||||
"(Try)?Dequeue|Enqueue|Insert.*|(Try)?Pop|Push|(Try?)Remove.*|Replace.*|SafeDelete|Set.*|")
|
||||
)
|
||||
or
|
||||
// ... or an indexer access that is in an assignment position
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/**
|
||||
* A library for reasoning about thread creation events in C#.
|
||||
*/
|
||||
|
||||
import Concurrency
|
||||
|
||||
/**
|
||||
@@ -12,7 +13,11 @@ class ThreadStartingCallable extends Callable {
|
||||
this.(Method).getQualifiedName() = "System.Threading.Tasks.Task.Run" or
|
||||
this.(Constructor).getDeclaringType().getQualifiedName() = "System.Threading.Thread" or
|
||||
this.(Method).getQualifiedName() = "System.Threading.Thread.Start" or
|
||||
this.(Constructor).getDeclaringType().getQualifiedName().matches("System.Threading.Tasks.Task<%>")
|
||||
this
|
||||
.(Constructor)
|
||||
.getDeclaringType()
|
||||
.getQualifiedName()
|
||||
.matches("System.Threading.Tasks.Task<%>")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,9 +31,7 @@ abstract class ConcurrentEntryPoint extends Callable { }
|
||||
/**
|
||||
* Methods annotated with the `async` keyword are concurrent entry points.
|
||||
*/
|
||||
class AsyncMethod extends ConcurrentEntryPoint {
|
||||
AsyncMethod() { this.(Modifiable).isAsync() }
|
||||
}
|
||||
class AsyncMethod extends ConcurrentEntryPoint { AsyncMethod() { this.(Modifiable).isAsync() } }
|
||||
|
||||
/**
|
||||
* Lambdas or methods passed into the thread or task creation library functions
|
||||
@@ -38,7 +41,8 @@ class LibraryTask extends ConcurrentEntryPoint {
|
||||
LibraryTask() {
|
||||
exists(Call c, Expr arg |
|
||||
c.getTarget() instanceof ThreadStartingCallable and
|
||||
arg = c.getAnArgument() |
|
||||
arg = c.getAnArgument()
|
||||
|
|
||||
this = arg or
|
||||
this = arg.(DelegateCreation).getArgument().(CallableAccess).getTarget()
|
||||
)
|
||||
|
||||
@@ -7,7 +7,8 @@ MethodCall getAnAccessByReflection(Method m) {
|
||||
typeof.getTypeAccess().getType() = m.getDeclaringType() and
|
||||
result.getTarget().hasName("GetMethod") and
|
||||
result.getQualifier() = typeof and
|
||||
result.getArgument(0).getValue() = m.getName())
|
||||
result.getArgument(0).getValue() = m.getName()
|
||||
)
|
||||
}
|
||||
|
||||
Expr getAnAccessByDynamicCall(Method m) {
|
||||
@@ -18,15 +19,18 @@ Expr getAnAccessByDynamicCall(Method m) {
|
||||
receiverType instanceof DynamicType
|
||||
or
|
||||
m.getDeclaringType().getABaseType*() = receiverType
|
||||
)
|
||||
and result=call)
|
||||
or exists(MethodCall mc, Method target |
|
||||
) and
|
||||
result = call
|
||||
)
|
||||
or
|
||||
exists(MethodCall mc, Method target |
|
||||
target = mc.getTarget() and
|
||||
target.hasName("InvokeMember") and
|
||||
target.getDeclaringType().hasQualifiedName("System.Type") and
|
||||
mc.getArgument(0).(StringLiteral).getValue() = m.getName() and
|
||||
mc.getArgument(3).getType().(RefType).hasMethod(m) and
|
||||
result=mc)
|
||||
result = mc
|
||||
)
|
||||
}
|
||||
|
||||
Expr getAMethodAccess(Method m) {
|
||||
@@ -37,14 +41,14 @@ Expr getAMethodAccess(Method m) {
|
||||
}
|
||||
|
||||
predicate potentiallyAccessedByForEach(Method m) {
|
||||
m.hasName("GetEnumerator")
|
||||
and m.getDeclaringType().getABaseType+().hasQualifiedName("System.Collections.IEnumerable")
|
||||
m.hasName("GetEnumerator") and
|
||||
m.getDeclaringType().getABaseType+().hasQualifiedName("System.Collections.IEnumerable")
|
||||
}
|
||||
|
||||
predicate isRecursivelyLiveExpression(Expr e)
|
||||
{
|
||||
exists(Callable c | c=e.getEnclosingCallable() |
|
||||
isRecursivelyLiveMethod(c) or not c instanceof Method )
|
||||
predicate isRecursivelyLiveExpression(Expr e) {
|
||||
exists(Callable c | c = e.getEnclosingCallable() |
|
||||
isRecursivelyLiveMethod(c) or not c instanceof Method
|
||||
)
|
||||
}
|
||||
|
||||
predicate isRecursivelyLiveMethod(Method m) {
|
||||
@@ -69,28 +73,27 @@ predicate isRecursivelyLiveMethod(Method m) {
|
||||
)
|
||||
}
|
||||
|
||||
predicate nunitValueSource(Method m)
|
||||
{
|
||||
exists(ValueSourceAttribute attribute | attribute.getSourceMethod()=m)
|
||||
predicate nunitValueSource(Method m) {
|
||||
exists(ValueSourceAttribute attribute | attribute.getSourceMethod() = m)
|
||||
}
|
||||
|
||||
predicate nunitTestCaseSource(Declaration f)
|
||||
{
|
||||
exists(TestCaseSourceAttribute attribute | attribute.getSourceDeclaration()=f)
|
||||
predicate nunitTestCaseSource(Declaration f) {
|
||||
exists(TestCaseSourceAttribute attribute | attribute.getSourceDeclaration() = f)
|
||||
}
|
||||
|
||||
predicate isDeadMethod(Method m) {
|
||||
not isRecursivelyLiveMethod(m)
|
||||
and m.isSourceDeclaration()
|
||||
not isRecursivelyLiveMethod(m) and
|
||||
m.isSourceDeclaration()
|
||||
}
|
||||
|
||||
predicate isDeadField(Field f) {
|
||||
f.isPrivate()
|
||||
and not f.getDeclaringType() instanceof AnonymousClass
|
||||
and f.getSourceDeclaration() = f
|
||||
and not nunitTestCaseSource(f)
|
||||
and forall(FieldAccess fc | fc.getTarget().getSourceDeclaration() = f |
|
||||
f.isPrivate() and
|
||||
not f.getDeclaringType() instanceof AnonymousClass and
|
||||
f.getSourceDeclaration() = f and
|
||||
not nunitTestCaseSource(f) and
|
||||
forall(FieldAccess fc | fc.getTarget().getSourceDeclaration() = f |
|
||||
isDeadMethod(fc.getEnclosingCallable())
|
||||
or (not fc instanceof FieldRead and not fc.isRefArgument())
|
||||
or
|
||||
not fc instanceof FieldRead and not fc.isRefArgument()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import csharp
|
||||
|
||||
class SourceDeclaration extends Declaration {
|
||||
SourceDeclaration() {
|
||||
this.isSourceDeclaration()
|
||||
}
|
||||
}
|
||||
class SourceDeclaration extends Declaration { SourceDeclaration() { this.isSourceDeclaration() } }
|
||||
|
||||
/** A parameter that has come from source code. */
|
||||
class SourceParameter extends SourceDeclaration, Parameter { }
|
||||
@@ -26,98 +22,87 @@ class SourceMethodOrConstructor extends SourceDeclaration, Callable {
|
||||
}
|
||||
|
||||
/** Gets an XML comment bound to this declaration. */
|
||||
XmlComment getADeclarationXmlComment(Declaration d)
|
||||
{
|
||||
result=getADeclarationCommentBlock(d).getAChild()
|
||||
XmlComment getADeclarationXmlComment(Declaration d) {
|
||||
result = getADeclarationCommentBlock(d).getAChild()
|
||||
}
|
||||
|
||||
/** Gets a comment block bound to this declaration. */
|
||||
CommentBlock getADeclarationCommentBlock(Declaration d)
|
||||
{
|
||||
CommentBlock getADeclarationCommentBlock(Declaration d) {
|
||||
// getAfter() is slightly more relaxed than getElement(),
|
||||
// because sometimes CommentBlocks
|
||||
// get split up or don't have spaces before them so aren't
|
||||
// automatically attributed to the declaration.
|
||||
result.getAfter()=d
|
||||
result.getAfter() = d
|
||||
}
|
||||
|
||||
/** Whether the declaration has a documentation comment. */
|
||||
predicate declarationHasXmlComment(Declaration d)
|
||||
{
|
||||
exists(getADeclarationXmlComment(d))
|
||||
}
|
||||
predicate declarationHasXmlComment(Declaration d) { exists(getADeclarationXmlComment(d)) }
|
||||
|
||||
/** Whether a declaration should have documentation. */
|
||||
predicate isDocumentationNeeded(Modifiable decl)
|
||||
{
|
||||
decl.getSourceDeclaration()=decl // Exclude constructed types and methods
|
||||
and not exists(decl.(Attributable).getAnAttribute()) // An attribute may serve to document
|
||||
and decl.isPublic()
|
||||
and
|
||||
predicate isDocumentationNeeded(Modifiable decl) {
|
||||
decl.getSourceDeclaration() = decl and // Exclude constructed types and methods
|
||||
not exists(decl.(Attributable).getAnAttribute()) and // An attribute may serve to document
|
||||
decl.isPublic() and
|
||||
(
|
||||
// The documentation of the overridden method (e.g. in an interface) is sufficient.
|
||||
exists(SourceMethod m | decl=m | not m.overrides() )
|
||||
or exists(ValueOrRefType t | decl=t | t.fromSource() )
|
||||
or decl instanceof SourceConstructor
|
||||
exists(SourceMethod m | decl = m | not m.overrides())
|
||||
or
|
||||
exists(ValueOrRefType t | decl = t | t.fromSource())
|
||||
or
|
||||
decl instanceof SourceConstructor
|
||||
)
|
||||
}
|
||||
|
||||
/** An XML comment containing a `<returns>` tag. */
|
||||
class ReturnsXmlComment extends XmlComment
|
||||
{
|
||||
ReturnsXmlComment() { getOpenTag(_)="returns" }
|
||||
class ReturnsXmlComment extends XmlComment {
|
||||
ReturnsXmlComment() { getOpenTag(_) = "returns" }
|
||||
|
||||
predicate hasBody(int offset) { hasBody("returns",offset) }
|
||||
predicate hasBody(int offset) { hasBody("returns", offset) }
|
||||
|
||||
predicate isOpenTag(int offset) { "returns"=getOpenTag(offset) }
|
||||
predicate isOpenTag(int offset) { "returns" = getOpenTag(offset) }
|
||||
|
||||
predicate isEmptyTag(int offset) { "returns"=getEmptyTag(offset) }
|
||||
predicate isEmptyTag(int offset) { "returns" = getEmptyTag(offset) }
|
||||
}
|
||||
|
||||
/** An XML comment containing an `<exception>` tag. */
|
||||
class ExceptionXmlComment extends XmlComment
|
||||
{
|
||||
ExceptionXmlComment() { getOpenTag(_)="exception" }
|
||||
class ExceptionXmlComment extends XmlComment {
|
||||
ExceptionXmlComment() { getOpenTag(_) = "exception" }
|
||||
|
||||
string getCref(int offset) { result=getAttribute("exception","cref",offset) }
|
||||
string getCref(int offset) { result = getAttribute("exception", "cref", offset) }
|
||||
|
||||
predicate hasBody(int offset) { hasBody("exception",offset) }
|
||||
predicate hasBody(int offset) { hasBody("exception", offset) }
|
||||
}
|
||||
|
||||
/** An XML comment containing a `<param>` tag. */
|
||||
class ParamXmlComment extends XmlComment
|
||||
{
|
||||
ParamXmlComment() { getOpenTag(_)="param" }
|
||||
class ParamXmlComment extends XmlComment {
|
||||
ParamXmlComment() { getOpenTag(_) = "param" }
|
||||
|
||||
string getName(int offset) { getAttribute("param","name",offset)=result }
|
||||
string getName(int offset) { getAttribute("param", "name", offset) = result }
|
||||
|
||||
predicate hasBody(int offset) { hasBody("param",offset) }
|
||||
predicate hasBody(int offset) { hasBody("param", offset) }
|
||||
}
|
||||
|
||||
/** An XML comment containing a `<typeparam>` tag. */
|
||||
class TypeparamXmlComment extends XmlComment
|
||||
{
|
||||
TypeparamXmlComment() { getOpenTag(_)="typeparam" }
|
||||
class TypeparamXmlComment extends XmlComment {
|
||||
TypeparamXmlComment() { getOpenTag(_) = "typeparam" }
|
||||
|
||||
string getName(int offset) { getAttribute("typeparam","name",offset)=result }
|
||||
string getName(int offset) { getAttribute("typeparam", "name", offset) = result }
|
||||
|
||||
predicate hasBody(int offset) { hasBody("typeparam",offset) }
|
||||
predicate hasBody(int offset) { hasBody("typeparam", offset) }
|
||||
}
|
||||
|
||||
/** An XML comment containing a `<summary>` tag. */
|
||||
class SummaryXmlComment extends XmlComment
|
||||
{
|
||||
SummaryXmlComment() { getOpenTag(_)="summary" }
|
||||
class SummaryXmlComment extends XmlComment {
|
||||
SummaryXmlComment() { getOpenTag(_) = "summary" }
|
||||
|
||||
predicate hasBody(int offset) { hasBody("summary",offset) }
|
||||
predicate hasBody(int offset) { hasBody("summary", offset) }
|
||||
|
||||
predicate isOpenTag(int offset) { "summary"=getOpenTag(offset) }
|
||||
predicate isOpenTag(int offset) { "summary" = getOpenTag(offset) }
|
||||
|
||||
predicate isEmptyTag(int offset) { "summary"=getEmptyTag(offset) }
|
||||
predicate isEmptyTag(int offset) { "summary" = getEmptyTag(offset) }
|
||||
}
|
||||
|
||||
/** An XML comment containing an `<inheritdoc>` tag. */
|
||||
class InheritDocXmlComment extends XmlComment
|
||||
{
|
||||
InheritDocXmlComment() { getOpenTag(_)="inheritdoc" }
|
||||
class InheritDocXmlComment extends XmlComment {
|
||||
InheritDocXmlComment() { getOpenTag(_) = "inheritdoc" }
|
||||
}
|
||||
|
||||
@@ -6,143 +6,115 @@
|
||||
import csharp
|
||||
|
||||
//#################### PREDICATES ####################
|
||||
|
||||
Stmt firstStmt(ForeachStmt fes)
|
||||
{
|
||||
if fes.getBody() instanceof BlockStmt then
|
||||
result = fes.getBody().(BlockStmt).getStmt(0)
|
||||
else
|
||||
result = fes.getBody()
|
||||
Stmt firstStmt(ForeachStmt fes) {
|
||||
if fes.getBody() instanceof BlockStmt
|
||||
then result = fes.getBody().(BlockStmt).getStmt(0)
|
||||
else result = fes.getBody()
|
||||
}
|
||||
|
||||
predicate isEnumerableType(ValueOrRefType t)
|
||||
{
|
||||
t.hasQualifiedName("System.Linq.Enumerable")
|
||||
}
|
||||
predicate isEnumerableType(ValueOrRefType t) { t.hasQualifiedName("System.Linq.Enumerable") }
|
||||
|
||||
predicate isIEnumerableType(ValueOrRefType t)
|
||||
{
|
||||
predicate isIEnumerableType(ValueOrRefType t) {
|
||||
t.getQualifiedName().matches("System.Collections.Generic.IEnumerable%")
|
||||
}
|
||||
|
||||
predicate missedAllOpportunity(ForeachStmt fes)
|
||||
{
|
||||
predicate missedAllOpportunity(ForeachStmt fes) {
|
||||
exists(IfStmt is |
|
||||
// The loop contains an if statement with no else case, and nothing else.
|
||||
is = firstStmt(fes)
|
||||
and numStmts(fes) = 1
|
||||
and not exists(is.getElse())
|
||||
|
||||
is = firstStmt(fes) and
|
||||
numStmts(fes) = 1 and
|
||||
not exists(is.getElse()) and
|
||||
// The if statement accesses the loop variable.
|
||||
and is.getCondition().getAChildExpr*() = fes.getVariable().getAnAccess()
|
||||
|
||||
is.getCondition().getAChildExpr*() = fes.getVariable().getAnAccess() and
|
||||
// The then case of the if assigns false to something and breaks out of the loop.
|
||||
and exists(Assignment a, BoolLiteral bl |
|
||||
a = is.getThen().getAChild*()
|
||||
and bl = a.getRValue()
|
||||
and bl.toString() = "false"
|
||||
)
|
||||
and exists(BreakStmt bs |
|
||||
bs = is.getThen().getAChild*()
|
||||
)
|
||||
exists(Assignment a, BoolLiteral bl |
|
||||
a = is.getThen().getAChild*() and
|
||||
bl = a.getRValue() and
|
||||
bl.toString() = "false"
|
||||
) and
|
||||
exists(BreakStmt bs | bs = is.getThen().getAChild*())
|
||||
)
|
||||
}
|
||||
|
||||
predicate missedCastOpportunity(ForeachStmt fes, LocalVariableDeclStmt s)
|
||||
{
|
||||
s = firstStmt(fes)
|
||||
and forex(VariableAccess va | va = fes.getVariable().getAnAccess() | va = s.getAVariableDeclExpr().getAChildExpr*())
|
||||
and exists(CastExpr ce |
|
||||
ce = s.getAVariableDeclExpr().getInitializer()
|
||||
and ce.getExpr() = fes.getVariable().getAnAccess()
|
||||
predicate missedCastOpportunity(ForeachStmt fes, LocalVariableDeclStmt s) {
|
||||
s = firstStmt(fes) and
|
||||
forex(VariableAccess va | va = fes.getVariable().getAnAccess() |
|
||||
va = s.getAVariableDeclExpr().getAChildExpr*()
|
||||
) and
|
||||
exists(CastExpr ce |
|
||||
ce = s.getAVariableDeclExpr().getInitializer() and
|
||||
ce.getExpr() = fes.getVariable().getAnAccess()
|
||||
)
|
||||
}
|
||||
|
||||
predicate missedOfTypeOpportunity(ForeachStmt fes, LocalVariableDeclStmt s)
|
||||
{
|
||||
s = firstStmt(fes)
|
||||
and forex(VariableAccess va | va = fes.getVariable().getAnAccess() | va = s.getAVariableDeclExpr().getAChildExpr*())
|
||||
and exists(AsExpr ae |
|
||||
ae = s.getAVariableDeclExpr().getInitializer()
|
||||
and ae.getExpr() = fes.getVariable().getAnAccess()
|
||||
predicate missedOfTypeOpportunity(ForeachStmt fes, LocalVariableDeclStmt s) {
|
||||
s = firstStmt(fes) and
|
||||
forex(VariableAccess va | va = fes.getVariable().getAnAccess() |
|
||||
va = s.getAVariableDeclExpr().getAChildExpr*()
|
||||
) and
|
||||
exists(AsExpr ae |
|
||||
ae = s.getAVariableDeclExpr().getInitializer() and
|
||||
ae.getExpr() = fes.getVariable().getAnAccess()
|
||||
)
|
||||
}
|
||||
|
||||
predicate missedSelectOpportunity(ForeachStmt fes, LocalVariableDeclStmt s)
|
||||
{
|
||||
s = firstStmt(fes)
|
||||
and forex(VariableAccess va | va = fes.getVariable().getAnAccess() | va = s.getAVariableDeclExpr().getAChildExpr*())
|
||||
and not(s.getAVariableDeclExpr().getInitializer() instanceof Cast)
|
||||
predicate missedSelectOpportunity(ForeachStmt fes, LocalVariableDeclStmt s) {
|
||||
s = firstStmt(fes) and
|
||||
forex(VariableAccess va | va = fes.getVariable().getAnAccess() |
|
||||
va = s.getAVariableDeclExpr().getAChildExpr*()
|
||||
) and
|
||||
not s.getAVariableDeclExpr().getInitializer() instanceof Cast
|
||||
}
|
||||
|
||||
predicate missedWhereOpportunity(ForeachStmt fes, IfStmt is)
|
||||
{
|
||||
predicate missedWhereOpportunity(ForeachStmt fes, IfStmt is) {
|
||||
// The very first thing the foreach loop does is test its iteration variable.
|
||||
is = firstStmt(fes)
|
||||
and exists(VariableAccess va |
|
||||
va.getTarget() = fes.getVariable()
|
||||
and va = is.getCondition().getAChildExpr*()
|
||||
)
|
||||
|
||||
is = firstStmt(fes) and
|
||||
exists(VariableAccess va |
|
||||
va.getTarget() = fes.getVariable() and
|
||||
va = is.getCondition().getAChildExpr*()
|
||||
) and
|
||||
// It then either (a) continues, or (b) performs the entire body of the loop within the condition.
|
||||
and
|
||||
(
|
||||
is.getThen() instanceof ContinueStmt
|
||||
or
|
||||
(
|
||||
not exists(is.getElse())
|
||||
and numStmts(fes) = 1
|
||||
)
|
||||
not exists(is.getElse()) and
|
||||
numStmts(fes) = 1
|
||||
)
|
||||
}
|
||||
|
||||
int numStmts(ForeachStmt fes)
|
||||
{
|
||||
if fes.getBody() instanceof BlockStmt then
|
||||
result = count(fes.getBody().(BlockStmt).getAStmt())
|
||||
else
|
||||
result = 1
|
||||
int numStmts(ForeachStmt fes) {
|
||||
if fes.getBody() instanceof BlockStmt
|
||||
then result = count(fes.getBody().(BlockStmt).getAStmt())
|
||||
else result = 1
|
||||
}
|
||||
|
||||
//#################### CLASSES ####################
|
||||
|
||||
/** A LINQ Any(...) call. */
|
||||
class AnyCall extends MethodCall
|
||||
{
|
||||
AnyCall()
|
||||
{
|
||||
class AnyCall extends MethodCall {
|
||||
AnyCall() {
|
||||
exists(Method m |
|
||||
m = getTarget()
|
||||
and isEnumerableType(m.getDeclaringType())
|
||||
and m.hasName("Any")
|
||||
m = getTarget() and
|
||||
isEnumerableType(m.getDeclaringType()) and
|
||||
m.hasName("Any")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A variable of type IEnumerable<T>, for some T. */
|
||||
class IEnumerableSequence extends Variable
|
||||
{
|
||||
IEnumerableSequence()
|
||||
{
|
||||
isIEnumerableType(getType())
|
||||
}
|
||||
class IEnumerableSequence extends Variable {
|
||||
IEnumerableSequence() { isIEnumerableType(getType()) }
|
||||
}
|
||||
|
||||
/** A LINQ Select(...) call. */
|
||||
class SelectCall extends ExtensionMethodCall
|
||||
{
|
||||
SelectCall()
|
||||
{
|
||||
class SelectCall extends ExtensionMethodCall {
|
||||
SelectCall() {
|
||||
exists(Method m |
|
||||
m = getTarget()
|
||||
and isEnumerableType(m.getDeclaringType())
|
||||
and m.hasName("Select")
|
||||
m = getTarget() and
|
||||
isEnumerableType(m.getDeclaringType()) and
|
||||
m.hasName("Select")
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the anonymous function expression supplied as the argument to the Select (if possible). */
|
||||
AnonymousFunctionExpr getFunctionExpr()
|
||||
{
|
||||
result = getArgument(1)
|
||||
}
|
||||
AnonymousFunctionExpr getFunctionExpr() { result = getArgument(1) }
|
||||
}
|
||||
|
||||
@@ -16,26 +16,35 @@ class MicrosoftAdvisory4021279 extends Vulnerability {
|
||||
override string getUrl() { result = "https://github.com/dotnet/corefx/issues/19535" }
|
||||
|
||||
override predicate matchesRange(string name, Version affected, Version fixed) {
|
||||
name = "System.Text.Encodings.Web" and (
|
||||
name = "System.Text.Encodings.Web" and
|
||||
(
|
||||
affected = "4.0.0" and fixed = "4.0.1"
|
||||
or
|
||||
affected = "4.3.0" and fixed = "4.3.1"
|
||||
) or
|
||||
name = "System.Net.Http" and (
|
||||
)
|
||||
or
|
||||
name = "System.Net.Http" and
|
||||
(
|
||||
affected = "4.1.1" and fixed = "4.1.2"
|
||||
or
|
||||
affected = "4.3.1" and fixed = "4.3.2"
|
||||
) or
|
||||
name = "System.Net.Http.WinHttpHandler" and (
|
||||
)
|
||||
or
|
||||
name = "System.Net.Http.WinHttpHandler" and
|
||||
(
|
||||
affected = "4.0.1" and fixed = "4.0.2"
|
||||
or
|
||||
affected = "4.3.0" and fixed = "4.3.1"
|
||||
) or
|
||||
name = "System.Net.Security" and (
|
||||
)
|
||||
or
|
||||
name = "System.Net.Security" and
|
||||
(
|
||||
affected = "4.0.0" and fixed = "4.0.1"
|
||||
or
|
||||
affected = "4.3.0" and fixed = "4.3.1"
|
||||
) or (
|
||||
)
|
||||
or
|
||||
(
|
||||
name = "Microsoft.AspNetCore.Mvc"
|
||||
or
|
||||
name = "Microsoft.AspNetCore.Mvc.Core"
|
||||
@@ -63,7 +72,8 @@ class MicrosoftAdvisory4021279 extends Vulnerability {
|
||||
name = "Microsoft.AspNetCore.Mvc.ViewFeatures"
|
||||
or
|
||||
name = "Microsoft.AspNetCore.Mvc.WebApiCompatShim"
|
||||
) and (
|
||||
) and
|
||||
(
|
||||
affected = "1.0.0" and fixed = "1.0.4"
|
||||
or
|
||||
affected = "1.1.0" and fixed = "1.1.3"
|
||||
@@ -81,7 +91,8 @@ class CVE_2017_8700 extends Vulnerability {
|
||||
name = "Microsoft.AspNetCore.Mvc.Core"
|
||||
or
|
||||
name = "Microsoft.AspNetCore.Mvc.Cors"
|
||||
) and (
|
||||
) and
|
||||
(
|
||||
affected = "1.0.0" and fixed = "1.0.6"
|
||||
or
|
||||
affected = "1.1.0" and fixed = "1.1.6"
|
||||
@@ -132,7 +143,8 @@ class CVE_2018_8409 extends Vulnerability {
|
||||
name = "System.IO.Pipelines" and affected = "4.5.0" and fixed = "4.5.1"
|
||||
or
|
||||
(name = "Microsoft.AspNetCore.All" or name = "Microsoft.AspNetCore.App") and
|
||||
affected = "2.1.0" and fixed = "2.1.4"
|
||||
affected = "2.1.0" and
|
||||
fixed = "2.1.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,7 +154,8 @@ class CVE_2018_8171 extends Vulnerability {
|
||||
override string getUrl() { result = "https://github.com/aspnet/Announcements/issues/310" }
|
||||
|
||||
override predicate matchesRange(string name, Version affected, Version fixed) {
|
||||
name = "Microsoft.AspNetCore.Identity" and (
|
||||
name = "Microsoft.AspNetCore.Identity" and
|
||||
(
|
||||
affected = "1.0.0" and fixed = "1.0.6"
|
||||
or
|
||||
affected = "1.1.0" and fixed = "1.1.6"
|
||||
@@ -166,7 +179,8 @@ class CVE_2018_8356 extends Vulnerability {
|
||||
name = "System.ServiceModel.Http"
|
||||
or
|
||||
name = "System.ServiceModel.NetTcp"
|
||||
) and (
|
||||
) and
|
||||
(
|
||||
affected = "4.0.0" and fixed = "4.1.3"
|
||||
or
|
||||
affected = "4.3.0" and fixed = "4.3.3"
|
||||
@@ -180,7 +194,8 @@ class CVE_2018_8356 extends Vulnerability {
|
||||
name = "System.ServiceModel.Duplex"
|
||||
or
|
||||
name = "System.ServiceModel.Security"
|
||||
) and (
|
||||
) and
|
||||
(
|
||||
affected = "4.0.0" and fixed = "4.0.4"
|
||||
or
|
||||
affected = "4.3.0" and fixed = "4.3.3"
|
||||
@@ -190,7 +205,8 @@ class CVE_2018_8356 extends Vulnerability {
|
||||
affected = "4.5.0" and fixed = "4.5.3"
|
||||
)
|
||||
or
|
||||
name = "System.ServiceModel.NetTcp" and (
|
||||
name = "System.ServiceModel.NetTcp" and
|
||||
(
|
||||
affected = "4.0.0" and fixed = "4.1.3"
|
||||
or
|
||||
affected = "4.3.0" and fixed = "4.3.3"
|
||||
@@ -208,13 +224,15 @@ class ASPNETCore_Jul18 extends Vulnerability {
|
||||
override string getUrl() { result = "https://github.com/aspnet/Announcements/issues/311" }
|
||||
|
||||
override predicate matchesRange(string name, Version affected, Version fixed) {
|
||||
name = "Microsoft.AspNetCore.Server.Kestrel.Core" and (
|
||||
name = "Microsoft.AspNetCore.Server.Kestrel.Core" and
|
||||
(
|
||||
affected = "2.0.0" and fixed = "2.0.4"
|
||||
or
|
||||
affected = "2.1.0" and fixed = "2.1.2"
|
||||
)
|
||||
or
|
||||
name = "Microsoft.AspNetCore.All" and (
|
||||
name = "Microsoft.AspNetCore.All" and
|
||||
(
|
||||
affected = "2.0.0" and fixed = "2.0.9"
|
||||
or
|
||||
affected = "2.1.0" and fixed = "2.1.2"
|
||||
@@ -232,7 +250,8 @@ class CVE_2018_8292 extends Vulnerability {
|
||||
override string getUrl() { result = "https://github.com/dotnet/announcements/issues/88" }
|
||||
|
||||
override predicate matchesVersion(string name, Version affected, Version fixed) {
|
||||
name = "System.Net.Http" and (
|
||||
name = "System.Net.Http" and
|
||||
(
|
||||
affected = "2.0" or
|
||||
affected = "4.0.0" or
|
||||
affected = "4.1.0" or
|
||||
@@ -265,12 +284,14 @@ class CVE_2018_0786 extends Vulnerability {
|
||||
name = "System.ServiceModel.Security"
|
||||
or
|
||||
name = "System.Private.ServiceModel"
|
||||
) and (
|
||||
) and
|
||||
(
|
||||
affected = "4.4.0" and fixed = "4.4.1"
|
||||
or
|
||||
affected = "4.3.0" and fixed = "4.3.1"
|
||||
)
|
||||
or (
|
||||
or
|
||||
(
|
||||
name = "System.ServiceModel.Primitives"
|
||||
or
|
||||
name = "System.ServiceModel.Http"
|
||||
@@ -281,7 +302,8 @@ class CVE_2018_0786 extends Vulnerability {
|
||||
) and
|
||||
affected = "4.1.0" and
|
||||
fixed = "4.1.1"
|
||||
or (
|
||||
or
|
||||
(
|
||||
name = "System.ServiceModel.Duplex"
|
||||
or
|
||||
name = "System.ServiceModel.Security"
|
||||
|
||||
@@ -6,6 +6,7 @@ import csharp
|
||||
*/
|
||||
class Package extends XMLElement {
|
||||
string name;
|
||||
|
||||
Version version;
|
||||
|
||||
Package() {
|
||||
@@ -19,18 +20,12 @@ class Package extends XMLElement {
|
||||
}
|
||||
|
||||
/** Gets the name of the package, for example `System.IO.Pipelines`. */
|
||||
string getPackageName() {
|
||||
result = name
|
||||
}
|
||||
string getPackageName() { result = name }
|
||||
|
||||
/** Gets the version of the package, for example `4.5.1`. */
|
||||
Version getVersion() {
|
||||
result = version
|
||||
}
|
||||
Version getVersion() { result = version }
|
||||
|
||||
override string toString() {
|
||||
result = name + " " + version
|
||||
}
|
||||
override string toString() { result = name + " " + version }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,8 +58,7 @@ abstract class Vulnerability extends string {
|
||||
*/
|
||||
bindingset[name, version]
|
||||
predicate isVulnerable(string name, Version version, Version fixed) {
|
||||
exists(Version affected, string n |
|
||||
name.toLowerCase() = n.toLowerCase() |
|
||||
exists(Version affected, string n | name.toLowerCase() = n.toLowerCase() |
|
||||
matchesRange(n, affected, fixed) and
|
||||
version.compareTo(fixed) < 0 and
|
||||
version.compareTo(affected) >= 0
|
||||
@@ -77,7 +71,7 @@ abstract class Vulnerability extends string {
|
||||
|
||||
bindingset[name, version]
|
||||
private Version getUltimateFix(string name, Version version) {
|
||||
result = max(Version fix | any(Vulnerability v).isVulnerable(name, version, fix))
|
||||
result = max(Version fix | any(Vulnerability v).isVulnerable(name, version, fix))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,9 +80,7 @@ private Version getUltimateFix(string name, Version version) {
|
||||
class VulnerablePackage extends Package {
|
||||
Vulnerability vuln;
|
||||
|
||||
VulnerablePackage() {
|
||||
vuln.isVulnerable(this.getPackageName(), this.getVersion(), _)
|
||||
}
|
||||
VulnerablePackage() { vuln.isVulnerable(this.getPackageName(), this.getVersion(), _) }
|
||||
|
||||
/** Gets the vulnerability of this package. */
|
||||
Vulnerability getVulnerability() { result = vuln }
|
||||
|
||||
@@ -14,16 +14,13 @@
|
||||
import csharp
|
||||
|
||||
/** An element that should be in the generated code. */
|
||||
abstract class GeneratedElement extends Element {
|
||||
}
|
||||
abstract class GeneratedElement extends Element { }
|
||||
|
||||
/** A member that should be in the generated code. */
|
||||
abstract class GeneratedMember extends Member, GeneratedElement {
|
||||
}
|
||||
abstract class GeneratedMember extends Member, GeneratedElement { }
|
||||
|
||||
/** A type that should be in the generated code. */
|
||||
private abstract class GeneratedType extends ValueOrRefType, GeneratedElement {
|
||||
|
||||
abstract private class GeneratedType extends ValueOrRefType, GeneratedElement {
|
||||
GeneratedType() {
|
||||
(
|
||||
this instanceof Interface
|
||||
@@ -35,12 +32,9 @@ private abstract class GeneratedType extends ValueOrRefType, GeneratedElement {
|
||||
this instanceof Enum
|
||||
or
|
||||
this instanceof DelegateType
|
||||
)
|
||||
and
|
||||
not this instanceof ConstructedType
|
||||
and
|
||||
not this.getALocation() instanceof ExcludedAssembly
|
||||
and
|
||||
) and
|
||||
not this instanceof ConstructedType and
|
||||
not this.getALocation() instanceof ExcludedAssembly and
|
||||
this.fromLibrary()
|
||||
}
|
||||
|
||||
@@ -66,53 +60,37 @@ private abstract class GeneratedType extends ValueOrRefType, GeneratedElement {
|
||||
}
|
||||
|
||||
private string stubAbstractModifier() {
|
||||
if this.(Class).isAbstract() then
|
||||
result = "abstract "
|
||||
else
|
||||
result = ""
|
||||
if this.(Class).isAbstract() then result = "abstract " else result = ""
|
||||
}
|
||||
|
||||
private string stubStaticModifier() {
|
||||
if this.isStatic() then result = "static "
|
||||
else result = ""
|
||||
if this.isStatic() then result = "static " else result = ""
|
||||
}
|
||||
|
||||
private string stubAttributes() {
|
||||
if this.getAnAttribute().getType().getQualifiedName() = "System.FlagsAttribute" then
|
||||
result = "[System.Flags]\n"
|
||||
else
|
||||
result = ""
|
||||
if this.getAnAttribute().getType().getQualifiedName() = "System.FlagsAttribute"
|
||||
then result = "[System.Flags]\n"
|
||||
else result = ""
|
||||
}
|
||||
|
||||
private string stubComment() {
|
||||
result = "// Generated from `" + this.getQualifiedName() + "` in `" +
|
||||
min(this.getLocation().toString()) + "`\n"
|
||||
min(this.getLocation().toString()) + "`\n"
|
||||
}
|
||||
|
||||
private string stubAccessibilityModifier() {
|
||||
if this.isPublic() then
|
||||
result = "public "
|
||||
else
|
||||
result = ""
|
||||
if this.isPublic() then result = "public " else result = ""
|
||||
}
|
||||
|
||||
/** Gets the entire C# stub code for this type. */
|
||||
final string getStub() {
|
||||
if this.isDuplicate() then
|
||||
result = ""
|
||||
if this.isDuplicate()
|
||||
then result = ""
|
||||
else
|
||||
result = this.stubComment() +
|
||||
this.stubAttributes() +
|
||||
this.stubAbstractModifier() +
|
||||
this.stubStaticModifier() +
|
||||
this.stubAccessibilityModifier() +
|
||||
this.stubKeyword() + " " +
|
||||
this.getUndecoratedName() +
|
||||
stubGenericArguments(this) +
|
||||
stubBaseTypesString() +
|
||||
"\n{\n" +
|
||||
stubMembers() +
|
||||
"}\n\n"
|
||||
result = this.stubComment() + this.stubAttributes() + this.stubAbstractModifier() +
|
||||
this.stubStaticModifier() + this.stubAccessibilityModifier() + this.stubKeyword() + " " +
|
||||
this.getUndecoratedName() + stubGenericArguments(this) + stubBaseTypesString() + "\n{\n" +
|
||||
stubMembers() + "}\n\n"
|
||||
}
|
||||
|
||||
private ValueOrRefType getAnInterestingBaseType() {
|
||||
@@ -122,25 +100,26 @@ private abstract class GeneratedType extends ValueOrRefType, GeneratedElement {
|
||||
}
|
||||
|
||||
private string stubBaseTypesString() {
|
||||
if this instanceof Enum then
|
||||
result = ""
|
||||
else if exists(getAnInterestingBaseType()) then
|
||||
result = " : " +
|
||||
concat(int i, ValueOrRefType t |
|
||||
t = this.getAnInterestingBaseType() and (if t instanceof Class then i=0 else i=1) |
|
||||
stubClassName(t), ", " order by i
|
||||
)
|
||||
if this instanceof Enum
|
||||
then result = ""
|
||||
else
|
||||
result = ""
|
||||
if exists(getAnInterestingBaseType())
|
||||
then
|
||||
result = " : " +
|
||||
concat(int i, ValueOrRefType t |
|
||||
t = this.getAnInterestingBaseType() and
|
||||
(if t instanceof Class then i = 0 else i = 1)
|
||||
|
|
||||
stubClassName(t), ", "
|
||||
order by
|
||||
i
|
||||
)
|
||||
else result = ""
|
||||
}
|
||||
|
||||
private string stubMembers() {
|
||||
result = concat(stubMember(this.getAGeneratedMember()))
|
||||
}
|
||||
private string stubMembers() { result = concat(stubMember(this.getAGeneratedMember())) }
|
||||
|
||||
private GeneratedMember getAGeneratedMember() {
|
||||
result.getDeclaringType() = this
|
||||
}
|
||||
private GeneratedMember getAGeneratedMember() { result.getDeclaringType() = this }
|
||||
|
||||
final Type getAGeneratedType() {
|
||||
result = getAnInterestingBaseType()
|
||||
@@ -160,11 +139,9 @@ private abstract class GeneratedType extends ValueOrRefType, GeneratedElement {
|
||||
* This is extended in client code to identify the actual
|
||||
* declarations that should be generated.
|
||||
*/
|
||||
abstract class GeneratedDeclaration extends Declaration {
|
||||
}
|
||||
abstract class GeneratedDeclaration extends Declaration { }
|
||||
|
||||
private class IndirectType extends GeneratedType {
|
||||
|
||||
IndirectType() {
|
||||
this.getASubType() instanceof GeneratedType
|
||||
or
|
||||
@@ -174,14 +151,14 @@ private class IndirectType extends GeneratedType {
|
||||
or
|
||||
exists(GeneratedType t | this = getAContainedType(t.getAGeneratedType()).getSourceDeclaration())
|
||||
or
|
||||
exists(GeneratedDeclaration decl | decl.(Member).getDeclaringType().getSourceDeclaration() = this)
|
||||
exists(GeneratedDeclaration decl |
|
||||
decl.(Member).getDeclaringType().getSourceDeclaration() = this
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class RootGeneratedType extends GeneratedType {
|
||||
RootGeneratedType() {
|
||||
this = any(GeneratedDeclaration decl).getSourceDeclaration()
|
||||
}
|
||||
RootGeneratedType() { this = any(GeneratedDeclaration decl).getSourceDeclaration() }
|
||||
}
|
||||
|
||||
private Type getAContainedType(Type t) {
|
||||
@@ -191,9 +168,7 @@ private Type getAContainedType(Type t) {
|
||||
}
|
||||
|
||||
private class RootGeneratedMember extends GeneratedMember {
|
||||
RootGeneratedMember() {
|
||||
this = any(GeneratedDeclaration d).getSourceDeclaration()
|
||||
}
|
||||
RootGeneratedMember() { this = any(GeneratedDeclaration d).getSourceDeclaration() }
|
||||
}
|
||||
|
||||
private predicate declarationExists(Virtualizable m) {
|
||||
@@ -223,31 +198,26 @@ private class GeneratedNamespace extends Namespace, GeneratedElement {
|
||||
}
|
||||
|
||||
private string getPreamble() {
|
||||
if this.isGlobalNamespace() then
|
||||
result = ""
|
||||
else
|
||||
result = "namespace " + this.getName() + "\n{\n"
|
||||
if this.isGlobalNamespace()
|
||||
then result = ""
|
||||
else result = "namespace " + this.getName() + "\n{\n"
|
||||
}
|
||||
|
||||
private string getPostAmble() {
|
||||
if this.isGlobalNamespace() then
|
||||
result = ""
|
||||
else
|
||||
result = "}\n"
|
||||
}
|
||||
private string getPostAmble() { if this.isGlobalNamespace() then result = "" else result = "}\n" }
|
||||
|
||||
final string getStubs() {
|
||||
result = getPreamble() +
|
||||
getTypeStubs() +
|
||||
getSubNamespaces() +
|
||||
getPostAmble()
|
||||
result = getPreamble() + getTypeStubs() + getSubNamespaces() + getPostAmble()
|
||||
}
|
||||
|
||||
/** Gets the `n`th generated child namespace, indexed from 0. */
|
||||
pragma[nomagic]
|
||||
final GeneratedNamespace getChildNamespace(int n) {
|
||||
result.getParentNamespace() = this and
|
||||
result.getName() = rank[n+1](GeneratedNamespace g | g.getParentNamespace() = this | g.getName())
|
||||
result.getName() = rank[n + 1](GeneratedNamespace g |
|
||||
g.getParentNamespace() = this
|
||||
|
|
||||
g.getName()
|
||||
)
|
||||
}
|
||||
|
||||
final int getChildNamespaceCount() {
|
||||
@@ -260,7 +230,9 @@ private class GeneratedNamespace extends Namespace, GeneratedElement {
|
||||
}
|
||||
|
||||
private string getTypeStubs() {
|
||||
result = concat(string s | s = any(GeneratedType gt | gt.getDeclaringNamespace() = this).getStub())
|
||||
result = concat(string s |
|
||||
s = any(GeneratedType gt | gt.getDeclaringNamespace() = this).getStub()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,25 +240,32 @@ private class GeneratedNamespace extends Namespace, GeneratedElement {
|
||||
* Specify assemblies to exclude.
|
||||
* Do not generate any types from these assemblies.
|
||||
*/
|
||||
abstract class ExcludedAssembly extends Assembly {
|
||||
}
|
||||
abstract class ExcludedAssembly extends Assembly { }
|
||||
|
||||
/** Exclude types from these standard assemblies. */
|
||||
private class DefaultLibs extends ExcludedAssembly {
|
||||
DefaultLibs() {
|
||||
this.getName() = "System.Private.CoreLib"
|
||||
or this.getName() = "mscorlib"
|
||||
or this.getName() = "System.Runtime"
|
||||
this.getName() = "System.Private.CoreLib" or
|
||||
this.getName() = "mscorlib" or
|
||||
this.getName() = "System.Runtime"
|
||||
}
|
||||
}
|
||||
|
||||
private string stubAccessibility(Member m) {
|
||||
if m.getDeclaringType() instanceof Interface or exists(m.(Virtualizable).getExplicitlyImplementedInterface()) then result = ""
|
||||
else if m.isPublic() then result = "public "
|
||||
else if m.isProtected() then result = "protected "
|
||||
else if m.isPrivate() then result = "private "
|
||||
else if m.isInternal() then result = "internal "
|
||||
else result = "unknown-accessibility"
|
||||
if
|
||||
m.getDeclaringType() instanceof Interface or
|
||||
exists(m.(Virtualizable).getExplicitlyImplementedInterface())
|
||||
then result = ""
|
||||
else
|
||||
if m.isPublic()
|
||||
then result = "public "
|
||||
else
|
||||
if m.isProtected()
|
||||
then result = "protected "
|
||||
else
|
||||
if m.isPrivate()
|
||||
then result = "private "
|
||||
else if m.isInternal() then result = "internal " else result = "unknown-accessibility"
|
||||
}
|
||||
|
||||
private string stubModifiers(Member m) {
|
||||
@@ -294,71 +273,121 @@ private string stubModifiers(Member m) {
|
||||
}
|
||||
|
||||
private string stubStatic(Member m) {
|
||||
if m.(Modifiable).isStatic() then result = "static "
|
||||
else result = ""
|
||||
if m.(Modifiable).isStatic() then result = "static " else result = ""
|
||||
}
|
||||
|
||||
private string stubOverride(Member m) {
|
||||
if m.getDeclaringType() instanceof Interface then result = ""
|
||||
else if m.(Virtualizable).isVirtual() then result = "virtual "
|
||||
else if m.(Virtualizable).isAbstract() then result = "abstract "
|
||||
else if m.(Virtualizable).isOverride() then result = "override "
|
||||
else result = ""
|
||||
if m.getDeclaringType() instanceof Interface
|
||||
then result = ""
|
||||
else
|
||||
if m.(Virtualizable).isVirtual()
|
||||
then result = "virtual "
|
||||
else
|
||||
if m.(Virtualizable).isAbstract()
|
||||
then result = "abstract "
|
||||
else if m.(Virtualizable).isOverride() then result = "override " else result = ""
|
||||
}
|
||||
|
||||
private string stubQualifiedNamePrefix(ValueOrRefType t) {
|
||||
if t.getParent() instanceof GlobalNamespace then result=""
|
||||
else if t.getParent() instanceof Namespace then result = t.getParent().(Namespace).getQualifiedName() + "."
|
||||
else result = stubQualifiedNamePrefix(t.getParent()) + "."
|
||||
if t.getParent() instanceof GlobalNamespace
|
||||
then result = ""
|
||||
else
|
||||
if t.getParent() instanceof Namespace
|
||||
then result = t.getParent().(Namespace).getQualifiedName() + "."
|
||||
else result = stubQualifiedNamePrefix(t.getParent()) + "."
|
||||
}
|
||||
|
||||
language[monotonicAggregates]
|
||||
private string stubClassName(Type t) {
|
||||
if t instanceof ObjectType then
|
||||
result = "object"
|
||||
else if t instanceof StringType then
|
||||
result = "string"
|
||||
else if t instanceof IntType then
|
||||
result = "int"
|
||||
else if t instanceof BoolType then
|
||||
result = "bool"
|
||||
else if t instanceof VoidType then
|
||||
result = "void"
|
||||
else if t instanceof FloatType then
|
||||
result = "float"
|
||||
else if t instanceof DoubleType then
|
||||
result = "double"
|
||||
else if t instanceof NullableType then
|
||||
result = stubClassName(t.(NullableType).getUnderlyingType()) + "?"
|
||||
else if t instanceof TypeParameter then
|
||||
result = t.getName()
|
||||
else if t instanceof ArrayType then
|
||||
result = stubClassName(t.(ArrayType).getElementType()) + "[]"
|
||||
else if t instanceof PointerType then
|
||||
result = stubClassName(t.(PointerType).getReferentType()) + "*"
|
||||
else if t instanceof TupleType then
|
||||
result = "(" + concat(int i, Type element | element = t.(TupleType).getElementType(i) | stubClassName(element), "," order by i) + ")"
|
||||
else if t instanceof ValueOrRefType then
|
||||
result = stubQualifiedNamePrefix(t) + t.getUndecoratedName() + stubGenericArguments(t)
|
||||
if t instanceof ObjectType
|
||||
then result = "object"
|
||||
else
|
||||
result = "<error>"
|
||||
if t instanceof StringType
|
||||
then result = "string"
|
||||
else
|
||||
if t instanceof IntType
|
||||
then result = "int"
|
||||
else
|
||||
if t instanceof BoolType
|
||||
then result = "bool"
|
||||
else
|
||||
if t instanceof VoidType
|
||||
then result = "void"
|
||||
else
|
||||
if t instanceof FloatType
|
||||
then result = "float"
|
||||
else
|
||||
if t instanceof DoubleType
|
||||
then result = "double"
|
||||
else
|
||||
if t instanceof NullableType
|
||||
then result = stubClassName(t.(NullableType).getUnderlyingType()) + "?"
|
||||
else
|
||||
if t instanceof TypeParameter
|
||||
then result = t.getName()
|
||||
else
|
||||
if t instanceof ArrayType
|
||||
then result = stubClassName(t.(ArrayType).getElementType()) + "[]"
|
||||
else
|
||||
if t instanceof PointerType
|
||||
then result = stubClassName(t.(PointerType).getReferentType()) + "*"
|
||||
else
|
||||
if t instanceof TupleType
|
||||
then
|
||||
result = "(" +
|
||||
concat(int i, Type element |
|
||||
element = t.(TupleType).getElementType(i)
|
||||
|
|
||||
stubClassName(element), ","
|
||||
order by
|
||||
i
|
||||
) + ")"
|
||||
else
|
||||
if t instanceof ValueOrRefType
|
||||
then
|
||||
result = stubQualifiedNamePrefix(t) + t.getUndecoratedName() +
|
||||
stubGenericArguments(t)
|
||||
else result = "<error>"
|
||||
}
|
||||
|
||||
language[monotonicAggregates]
|
||||
private string stubGenericArguments(ValueOrRefType t) {
|
||||
if t instanceof UnboundGenericType then
|
||||
result = "<" + concat(int n | exists(t.(UnboundGenericType).getTypeParameter(n)) | t.(UnboundGenericType).getTypeParameter(n).getName(),"," order by n) + ">"
|
||||
else if t instanceof ConstructedType then
|
||||
result = "<" + concat(int n | exists(t.(ConstructedType).getTypeArgument(n)) | stubClassName(t.(ConstructedType).getTypeArgument(n)),"," order by n) + ">"
|
||||
if t instanceof UnboundGenericType
|
||||
then
|
||||
result = "<" +
|
||||
concat(int n |
|
||||
exists(t.(UnboundGenericType).getTypeParameter(n))
|
||||
|
|
||||
t.(UnboundGenericType).getTypeParameter(n).getName(), ","
|
||||
order by
|
||||
n
|
||||
) + ">"
|
||||
else
|
||||
result = ""
|
||||
if t instanceof ConstructedType
|
||||
then
|
||||
result = "<" +
|
||||
concat(int n |
|
||||
exists(t.(ConstructedType).getTypeArgument(n))
|
||||
|
|
||||
stubClassName(t.(ConstructedType).getTypeArgument(n)), ","
|
||||
order by
|
||||
n
|
||||
) + ">"
|
||||
else result = ""
|
||||
}
|
||||
|
||||
private string stubGenericMethodParams(Method m) {
|
||||
if m instanceof UnboundGenericMethod then
|
||||
result = "<" + concat(int n, TypeParameter param | param = m.(UnboundGenericMethod).getTypeParameter(n) | param.getName(), "," order by n) + ">"
|
||||
else
|
||||
result = ""
|
||||
if m instanceof UnboundGenericMethod
|
||||
then
|
||||
result = "<" +
|
||||
concat(int n, TypeParameter param |
|
||||
param = m.(UnboundGenericMethod).getTypeParameter(n)
|
||||
|
|
||||
param.getName(), ","
|
||||
order by
|
||||
n
|
||||
) + ">"
|
||||
else result = ""
|
||||
}
|
||||
|
||||
private string stubImplementation(Virtualizable c) {
|
||||
@@ -368,17 +397,29 @@ private string stubImplementation(Virtualizable c) {
|
||||
}
|
||||
|
||||
private string stubParameters(Parameterizable p) {
|
||||
result = concat(int i, Parameter param | param = p.getParameter(i) and not param.getType() instanceof ArglistType |
|
||||
stubParameterModifiers(param) + stubClassName(param.getType()) + " " + param.getName() + stubDefaultValue(param), ", " order by i)
|
||||
result = concat(int i, Parameter param |
|
||||
param = p.getParameter(i) and not param.getType() instanceof ArglistType
|
||||
|
|
||||
stubParameterModifiers(param) + stubClassName(param.getType()) + " " + param.getName() +
|
||||
stubDefaultValue(param), ", "
|
||||
order by
|
||||
i
|
||||
)
|
||||
}
|
||||
|
||||
private string stubParameterModifiers(Parameter p) {
|
||||
if p.isOut() then result = "out "
|
||||
else if p.isRef() then result = "ref "
|
||||
else if p.isParams() then result = "params "
|
||||
else if p.isIn() then result = "" // Only C# 7.1 so ignore
|
||||
else if p.hasExtensionMethodModifier() then result = "this "
|
||||
else result = ""
|
||||
if p.isOut()
|
||||
then result = "out "
|
||||
else
|
||||
if p.isRef()
|
||||
then result = "ref "
|
||||
else
|
||||
if p.isParams()
|
||||
then result = "params "
|
||||
else
|
||||
if p.isIn()
|
||||
then result = "" // Only C# 7.1 so ignore
|
||||
else if p.hasExtensionMethodModifier() then result = "this " else result = ""
|
||||
}
|
||||
|
||||
private string stubDefaultValue(Parameter p) {
|
||||
@@ -395,37 +436,41 @@ private string stubExplicitImplementation(Member c) {
|
||||
|
||||
private string stubMember(Member m) {
|
||||
exists(Method c | m = c and not m.getDeclaringType() instanceof Enum |
|
||||
result = " " + stubModifiers(c) + stubClassName(c.getReturnType()) + " " +
|
||||
stubExplicitImplementation(c) + c.getName() + stubGenericMethodParams(c) +
|
||||
"(" + stubParameters(c) + ")" + stubImplementation(c) + ";\n"
|
||||
result = " " + stubModifiers(c) + stubClassName(c.getReturnType()) + " " +
|
||||
stubExplicitImplementation(c) + c.getName() + stubGenericMethodParams(c) + "(" +
|
||||
stubParameters(c) + ")" + stubImplementation(c) + ";\n"
|
||||
)
|
||||
or
|
||||
exists(Operator op | m = op and not m.getDeclaringType() instanceof Enum and not op instanceof ConversionOperator |
|
||||
exists(Operator op |
|
||||
m = op and not m.getDeclaringType() instanceof Enum and not op instanceof ConversionOperator
|
||||
|
|
||||
result = " " + stubModifiers(op) + stubClassName(op.getReturnType()) + " operator " +
|
||||
op.getName() +"(" + stubParameters(op) + ") => throw null;\n"
|
||||
op.getName() + "(" + stubParameters(op) + ") => throw null;\n"
|
||||
)
|
||||
or
|
||||
exists(ConversionOperator op | m = op |
|
||||
result = " " + stubModifiers(op) + stubExplicit(op) + "operator " +
|
||||
stubClassName(op.getReturnType()) + "(" + stubParameters(op) +
|
||||
") => throw null;\n"
|
||||
stubClassName(op.getReturnType()) + "(" + stubParameters(op) + ") => throw null;\n"
|
||||
)
|
||||
or
|
||||
result = " " + m.(EnumConstant).getName() + ",\n"
|
||||
or
|
||||
exists(Property p | m = p |
|
||||
result = " " + stubModifiers(m) + stubClassName(p.getType()) + " " +
|
||||
stubExplicitImplementation(p) + p.getName() + " { " + stubGetter(p) + stubSetter(p) + "}\n"
|
||||
stubExplicitImplementation(p) + p.getName() + " { " + stubGetter(p) + stubSetter(p) + "}\n"
|
||||
)
|
||||
or exists(Constructor c | m = c and not c.getDeclaringType() instanceof Enum |
|
||||
or
|
||||
exists(Constructor c | m = c and not c.getDeclaringType() instanceof Enum |
|
||||
result = " " + stubModifiers(m) + c.getName() + "(" + stubParameters(c) +
|
||||
") => throw null;\n"
|
||||
") => throw null;\n"
|
||||
)
|
||||
or exists(Indexer i | m = i |
|
||||
or
|
||||
exists(Indexer i | m = i |
|
||||
result = " " + stubModifiers(m) + stubClassName(i.getType()) + " this[" + stubParameters(i) +
|
||||
"] { " + stubGetter(i) + stubSetter(i) + "}\n"
|
||||
"] { " + stubGetter(i) + stubSetter(i) + "}\n"
|
||||
)
|
||||
or exists(Field f | f = m and not f instanceof EnumConstant |
|
||||
or
|
||||
exists(Field f | f = m and not f instanceof EnumConstant |
|
||||
result = " " + stubModifiers(m) + stubClassName(f.getType()) + " " + f.getName() + ";\n"
|
||||
)
|
||||
}
|
||||
@@ -437,36 +482,34 @@ private string stubExplicit(ConversionOperator op) {
|
||||
}
|
||||
|
||||
private string stubGetter(DeclarationWithGetSetAccessors p) {
|
||||
if exists(p.getGetter()) then (
|
||||
if p.isAbstract() or p.getDeclaringType() instanceof Interface then
|
||||
result = "get; "
|
||||
else
|
||||
result = "get => throw null; "
|
||||
) else
|
||||
result = ""
|
||||
if exists(p.getGetter())
|
||||
then
|
||||
if p.isAbstract() or p.getDeclaringType() instanceof Interface
|
||||
then result = "get; "
|
||||
else result = "get => throw null; "
|
||||
else result = ""
|
||||
}
|
||||
|
||||
private string stubSetter(DeclarationWithGetSetAccessors p) {
|
||||
if exists(p.getSetter()) then (
|
||||
if p.isAbstract() or p.getDeclaringType() instanceof Interface then
|
||||
result = "set; "
|
||||
else
|
||||
result = "set => throw null; "
|
||||
) else
|
||||
result = ""
|
||||
if exists(p.getSetter())
|
||||
then
|
||||
if p.isAbstract() or p.getDeclaringType() instanceof Interface
|
||||
then result = "set; "
|
||||
else result = "set => throw null; "
|
||||
else result = ""
|
||||
}
|
||||
|
||||
private string stubSemmleExtractorOptions() {
|
||||
result = concat(string s |
|
||||
exists(CommentLine comment |
|
||||
s = "// original-extractor-options:" + comment.getText().regexpCapture("\\w*semmle-extractor-options:(.*)", 1) + "\n"
|
||||
exists(CommentLine comment |
|
||||
s = "// original-extractor-options:" +
|
||||
comment.getText().regexpCapture("\\w*semmle-extractor-options:(.*)", 1) + "\n"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the generated C# code. */
|
||||
string generatedCode() {
|
||||
result = "// This file contains auto-generated code.\n" +
|
||||
stubSemmleExtractorOptions() + "\n" +
|
||||
any(GeneratedNamespace ns | ns.isGlobalNamespace()).getStubs()
|
||||
result = "// This file contains auto-generated code.\n" + stubSemmleExtractorOptions() + "\n" +
|
||||
any(GeneratedNamespace ns | ns.isGlobalNamespace()).getStubs()
|
||||
}
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
|
||||
import semmle.code.cil.CIL as CIL
|
||||
|
||||
@@ -35,13 +35,10 @@ import semmle.code.csharp.dataflow.TaintTracking
|
||||
import semmle.code.csharp.dataflow.SSA
|
||||
|
||||
/** DEPRECATED: Use `ControlFlow` instead. */
|
||||
deprecated
|
||||
module ControlFlowGraph {
|
||||
deprecated module ControlFlowGraph {
|
||||
import semmle.code.csharp.controlflow.ControlFlowGraph
|
||||
import ControlFlow
|
||||
}
|
||||
|
||||
/** Whether the source was extracted without a build command. */
|
||||
predicate extractionIsStandalone() {
|
||||
exists(SourceFile f | f.extractedStandalone())
|
||||
}
|
||||
predicate extractionIsStandalone() { exists(SourceFile f | f.extractedStandalone()) }
|
||||
|
||||
267
csharp/ql/src/external/CodeDuplication.qll
vendored
267
csharp/ql/src/external/CodeDuplication.qll
vendored
@@ -1,9 +1,6 @@
|
||||
import csharp
|
||||
|
||||
private
|
||||
string relativePath(File file) {
|
||||
result = file.getRelativePath().replaceAll("\\", "/")
|
||||
}
|
||||
private string relativePath(File file) { result = file.getRelativePath().replaceAll("\\", "/") }
|
||||
|
||||
/**
|
||||
* Holds if the `index`-th token of block `copy` is in file `file`, spanning
|
||||
@@ -17,111 +14,86 @@ predicate tokenLocation(File file, int sl, int sc, int ec, int el, Copy copy, in
|
||||
tokens(copy, index, sl, sc, ec, el)
|
||||
}
|
||||
|
||||
class Copy extends @duplication_or_similarity
|
||||
{
|
||||
private
|
||||
int lastToken() {
|
||||
result = max(int i | tokens(this, i, _, _, _, _) | i)
|
||||
}
|
||||
class Copy extends @duplication_or_similarity {
|
||||
private int lastToken() { result = max(int i | tokens(this, i, _, _, _, _) | i) }
|
||||
|
||||
int tokenStartingAt(Location loc) {
|
||||
tokenLocation(loc.getFile(), loc.getStartLine(), loc.getStartColumn(),
|
||||
_, _, this, result)
|
||||
tokenLocation(loc.getFile(), loc.getStartLine(), loc.getStartColumn(), _, _, this, result)
|
||||
}
|
||||
|
||||
int tokenEndingAt(Location loc) {
|
||||
tokenLocation(loc.getFile(), _, _,
|
||||
loc.getEndLine(), loc.getEndColumn(), this, result)
|
||||
tokenLocation(loc.getFile(), _, _, loc.getEndLine(), loc.getEndColumn(), this, result)
|
||||
}
|
||||
|
||||
int sourceStartLine() {
|
||||
tokens(this, 0, result, _, _, _)
|
||||
}
|
||||
int sourceStartLine() { tokens(this, 0, result, _, _, _) }
|
||||
|
||||
int sourceStartColumn() {
|
||||
tokens(this, 0, _, result, _, _)
|
||||
}
|
||||
int sourceStartColumn() { tokens(this, 0, _, result, _, _) }
|
||||
|
||||
int sourceEndLine() {
|
||||
tokens(this, lastToken(), _, _, result, _)
|
||||
}
|
||||
int sourceEndLine() { tokens(this, lastToken(), _, _, result, _) }
|
||||
|
||||
int sourceEndColumn() {
|
||||
tokens(this, lastToken(), _, _, _, result)
|
||||
}
|
||||
int sourceEndColumn() { tokens(this, lastToken(), _, _, _, result) }
|
||||
|
||||
int sourceLines() {
|
||||
result = this.sourceEndLine() + 1 - this.sourceStartLine()
|
||||
}
|
||||
int sourceLines() { result = this.sourceEndLine() + 1 - this.sourceStartLine() }
|
||||
|
||||
int getEquivalenceClass() {
|
||||
duplicateCode(this, _, result) or similarCode(this, _, result)
|
||||
}
|
||||
int getEquivalenceClass() { duplicateCode(this, _, result) or similarCode(this, _, result) }
|
||||
|
||||
File sourceFile() {
|
||||
exists(string name |
|
||||
duplicateCode(this, name, _) or similarCode(this, name, _) |
|
||||
name.replaceAll("\\", "/") = relativePath(result))
|
||||
exists(string name | duplicateCode(this, name, _) or similarCode(this, name, _) |
|
||||
name.replaceAll("\\", "/") = relativePath(result)
|
||||
)
|
||||
}
|
||||
|
||||
predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) {
|
||||
sourceFile().getAbsolutePath() = filepath and
|
||||
startline = sourceStartLine() and
|
||||
startcolumn = sourceStartColumn() and
|
||||
endline = sourceEndLine() and
|
||||
endcolumn = sourceEndColumn()
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
sourceFile().getAbsolutePath() = filepath and
|
||||
startline = sourceStartLine() and
|
||||
startcolumn = sourceStartColumn() and
|
||||
endline = sourceEndLine() and
|
||||
endcolumn = sourceEndColumn()
|
||||
}
|
||||
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
class DuplicateBlock extends Copy, @duplication
|
||||
{
|
||||
override string toString() {
|
||||
result = "Duplicate code: " + sourceLines() + " duplicated lines."
|
||||
}
|
||||
class DuplicateBlock extends Copy, @duplication {
|
||||
override string toString() { result = "Duplicate code: " + sourceLines() + " duplicated lines." }
|
||||
}
|
||||
|
||||
class SimilarBlock extends Copy, @similarity
|
||||
{
|
||||
class SimilarBlock extends Copy, @similarity {
|
||||
override string toString() {
|
||||
result = "Similar code: " + sourceLines() + " almost duplicated lines."
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Method sourceMethod() {
|
||||
method_location(result, _) and numlines(result, _, _, _)
|
||||
}
|
||||
Method sourceMethod() { method_location(result, _) and numlines(result, _, _, _) }
|
||||
|
||||
int numberOfSourceMethods(Class c) {
|
||||
result = count(Method m | m = sourceMethod() and m.getDeclaringType() = c)
|
||||
}
|
||||
|
||||
private
|
||||
predicate blockCoversStatement(int equivClass, int first, int last, Stmt stmt) {
|
||||
private predicate blockCoversStatement(int equivClass, int first, int last, Stmt stmt) {
|
||||
exists(DuplicateBlock b, Location loc |
|
||||
stmt.getLocation() = loc and
|
||||
first = b.tokenStartingAt(loc) and
|
||||
last = b.tokenEndingAt(loc) and
|
||||
b.getEquivalenceClass() = equivClass)
|
||||
b.getEquivalenceClass() = equivClass
|
||||
)
|
||||
}
|
||||
|
||||
private
|
||||
Stmt statementInMethod(Method m) {
|
||||
private Stmt statementInMethod(Method m) {
|
||||
result.getEnclosingCallable() = m and
|
||||
not result instanceof BlockStmt
|
||||
}
|
||||
|
||||
private
|
||||
predicate duplicateStatement(Method m1, Method m2, Stmt s1, Stmt s2) {
|
||||
private predicate duplicateStatement(Method m1, Method m2, Stmt s1, Stmt s2) {
|
||||
exists(int equivClass, int first, int last |
|
||||
s1 = statementInMethod(m1) and
|
||||
s2 = statementInMethod(m2) and
|
||||
blockCoversStatement(equivClass, first, last, s1) and
|
||||
blockCoversStatement(equivClass, first, last, s2) and
|
||||
s1 != s2 and m1 != m2
|
||||
s1 != s2 and
|
||||
m1 != m2
|
||||
)
|
||||
}
|
||||
|
||||
@@ -138,37 +110,38 @@ predicate duplicateMethod(Method m, Method other) {
|
||||
}
|
||||
|
||||
predicate similarLines(File f, int line) {
|
||||
exists(SimilarBlock b |
|
||||
b.sourceFile() = f and line in [b.sourceStartLine() .. b.sourceEndLine()]
|
||||
)
|
||||
exists(SimilarBlock b | b.sourceFile() = f and line in [b.sourceStartLine() .. b.sourceEndLine()])
|
||||
}
|
||||
|
||||
private
|
||||
predicate similarLinesPerEquivalenceClass(int equivClass, int lines, File f)
|
||||
{
|
||||
lines = strictsum(SimilarBlock b, int toSum | (b.sourceFile() = f and b.getEquivalenceClass() = equivClass) and (toSum = b.sourceLines()) | toSum)
|
||||
private predicate similarLinesPerEquivalenceClass(int equivClass, int lines, File f) {
|
||||
lines = strictsum(SimilarBlock b, int toSum |
|
||||
(b.sourceFile() = f and b.getEquivalenceClass() = equivClass) and
|
||||
toSum = b.sourceLines()
|
||||
|
|
||||
toSum
|
||||
)
|
||||
}
|
||||
|
||||
private pragma[noopt]
|
||||
predicate similarLinesCovered(File f, int coveredLines, File otherFile) {
|
||||
pragma[noopt]
|
||||
private predicate similarLinesCovered(File f, int coveredLines, File otherFile) {
|
||||
exists(int numLines | numLines = f.getNumberOfLines() |
|
||||
exists(int coveredApprox |
|
||||
coveredApprox = strictsum(int num |
|
||||
exists(int equivClass |
|
||||
similarLinesPerEquivalenceClass(equivClass, num, f) and
|
||||
similarLinesPerEquivalenceClass(equivClass, num, otherFile) and
|
||||
f != otherFile
|
||||
)
|
||||
exists(int coveredApprox |
|
||||
coveredApprox = strictsum(int num |
|
||||
exists(int equivClass |
|
||||
similarLinesPerEquivalenceClass(equivClass, num, f) and
|
||||
similarLinesPerEquivalenceClass(equivClass, num, otherFile) and
|
||||
f != otherFile
|
||||
)
|
||||
) and
|
||||
exists(int n, int product | product = coveredApprox * 100 and n = product / numLines | n > 75)
|
||||
) and
|
||||
exists (int n, int product| product = coveredApprox * 100 and n = product / numLines | n > 75)
|
||||
) and
|
||||
exists(int notCovered |
|
||||
notCovered = count(int j |
|
||||
j in [1 .. numLines] and
|
||||
not similarLines(f, j)
|
||||
) and
|
||||
coveredLines = numLines - notCovered
|
||||
)
|
||||
exists(int notCovered |
|
||||
notCovered = count(int j |
|
||||
j in [1 .. numLines] and
|
||||
not similarLines(f, j)
|
||||
) and
|
||||
coveredLines = numLines - notCovered
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -178,32 +151,35 @@ predicate duplicateLines(File f, int line) {
|
||||
)
|
||||
}
|
||||
|
||||
private
|
||||
predicate duplicateLinesPerEquivalenceClass(int equivClass, int lines, File f)
|
||||
{
|
||||
lines = strictsum(DuplicateBlock b, int toSum | (b.sourceFile() = f and b.getEquivalenceClass() = equivClass) and (toSum = b.sourceLines()) | toSum)
|
||||
private predicate duplicateLinesPerEquivalenceClass(int equivClass, int lines, File f) {
|
||||
lines = strictsum(DuplicateBlock b, int toSum |
|
||||
(b.sourceFile() = f and b.getEquivalenceClass() = equivClass) and
|
||||
toSum = b.sourceLines()
|
||||
|
|
||||
toSum
|
||||
)
|
||||
}
|
||||
|
||||
private pragma[noopt]
|
||||
predicate duplicateLinesCovered(File f, int coveredLines, File otherFile) {
|
||||
pragma[noopt]
|
||||
private predicate duplicateLinesCovered(File f, int coveredLines, File otherFile) {
|
||||
exists(int numLines | numLines = f.getNumberOfLines() |
|
||||
exists(int coveredApprox |
|
||||
coveredApprox = strictsum(int num |
|
||||
exists(int equivClass |
|
||||
duplicateLinesPerEquivalenceClass(equivClass, num, f) and
|
||||
duplicateLinesPerEquivalenceClass(equivClass, num, otherFile) and
|
||||
f != otherFile
|
||||
)
|
||||
exists(int coveredApprox |
|
||||
coveredApprox = strictsum(int num |
|
||||
exists(int equivClass |
|
||||
duplicateLinesPerEquivalenceClass(equivClass, num, f) and
|
||||
duplicateLinesPerEquivalenceClass(equivClass, num, otherFile) and
|
||||
f != otherFile
|
||||
)
|
||||
) and
|
||||
exists(int n, int product | product = coveredApprox * 100 and n = product / numLines | n > 75)
|
||||
) and
|
||||
exists (int n, int product| product = coveredApprox * 100 and n = product / numLines | n > 75)
|
||||
) and
|
||||
exists(int notCovered |
|
||||
notCovered = count(int j |
|
||||
j in [1 .. numLines] and
|
||||
not duplicateLines(f, j)
|
||||
) and
|
||||
coveredLines = numLines - notCovered
|
||||
)
|
||||
exists(int notCovered |
|
||||
notCovered = count(int j |
|
||||
j in [1 .. numLines] and
|
||||
not duplicateLines(f, j)
|
||||
) and
|
||||
coveredLines = numLines - notCovered
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -230,54 +206,59 @@ pragma[noopt]
|
||||
predicate duplicateAnonymousClass(AnonymousClass c, AnonymousClass other) {
|
||||
exists(int numDup |
|
||||
numDup = strictcount(Method m1 |
|
||||
exists(Method m2 |
|
||||
duplicateMethod(m1, m2) and
|
||||
m1 = sourceMethod() and
|
||||
m1.getDeclaringType() = c and
|
||||
c instanceof AnonymousClass and
|
||||
m2.getDeclaringType() = other and
|
||||
other instanceof AnonymousClass and
|
||||
c != other
|
||||
)
|
||||
) and
|
||||
exists(Method m2 |
|
||||
duplicateMethod(m1, m2) and
|
||||
m1 = sourceMethod() and
|
||||
m1.getDeclaringType() = c and
|
||||
c instanceof AnonymousClass and
|
||||
m2.getDeclaringType() = other and
|
||||
other instanceof AnonymousClass and
|
||||
c != other
|
||||
)
|
||||
) and
|
||||
numDup = numberOfSourceMethods(c) and
|
||||
numDup = numberOfSourceMethods(other) and
|
||||
forall(Type t | c.getABaseType() = t | t = other.getABaseType())
|
||||
)
|
||||
}
|
||||
|
||||
private pragma[noopt]
|
||||
predicate mostlyDuplicateClassBase(Class c, Class other, int numDup, int total) {
|
||||
pragma[noopt]
|
||||
private predicate mostlyDuplicateClassBase(Class c, Class other, int numDup, int total) {
|
||||
numDup = strictcount(Method m1 |
|
||||
exists(Method m2 |
|
||||
duplicateMethod(m1, m2) and
|
||||
m1 = sourceMethod() and
|
||||
m1.getDeclaringType() = c and
|
||||
m2.getDeclaringType() = other and
|
||||
other instanceof Class and
|
||||
c != other
|
||||
)
|
||||
) and
|
||||
exists(Method m2 |
|
||||
duplicateMethod(m1, m2) and
|
||||
m1 = sourceMethod() and
|
||||
m1.getDeclaringType() = c and
|
||||
m2.getDeclaringType() = other and
|
||||
other instanceof Class and
|
||||
c != other
|
||||
)
|
||||
) and
|
||||
total = numberOfSourceMethods(c) and
|
||||
exists (int n, int product | product = 100 * numDup and n = product / total | n > 80) and
|
||||
exists(int n, int product | product = 100 * numDup and n = product / total | n > 80) and
|
||||
not c instanceof AnonymousClass and
|
||||
not other instanceof AnonymousClass
|
||||
}
|
||||
|
||||
predicate mostlyDuplicateClass(Class c, Class other, string message) {
|
||||
exists(int numDup, int total |
|
||||
mostlyDuplicateClassBase(c,other,numDup,total) and
|
||||
mostlyDuplicateClassBase(c, other, numDup, total) and
|
||||
(
|
||||
(total != numDup
|
||||
and exists(string s1, string s2, string s3, string name | s1 = " out of " and s2 = " methods in " and s3 = " are duplicated in $@." and name = c.getName() |
|
||||
message = numDup + s1 + total + s2 + name + s3
|
||||
)
|
||||
total != numDup and
|
||||
exists(string s1, string s2, string s3, string name |
|
||||
s1 = " out of " and
|
||||
s2 = " methods in " and
|
||||
s3 = " are duplicated in $@." and
|
||||
name = c.getName()
|
||||
|
|
||||
message = numDup + s1 + total + s2 + name + s3
|
||||
)
|
||||
or
|
||||
(total = numDup
|
||||
and exists(string s1, string s2, string name | s1 = "All methods in " and s2 = " are identical in $@." and name = c.getName() |
|
||||
message = s1 + name + s2
|
||||
)
|
||||
total = numDup and
|
||||
exists(string s1, string s2, string name |
|
||||
s1 = "All methods in " and s2 = " are identical in $@." and name = c.getName()
|
||||
|
|
||||
message = s1 + name + s2
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -291,15 +272,13 @@ predicate classLevelDuplication(Class c, Class other) {
|
||||
duplicateAnonymousClass(c, other) or mostlyDuplicateClass(c, other, _)
|
||||
}
|
||||
|
||||
Element whitelistedDuplicateElement()
|
||||
{
|
||||
Element whitelistedDuplicateElement() {
|
||||
result instanceof UsingNamespaceDirective or
|
||||
result instanceof UsingStaticDirective
|
||||
}
|
||||
|
||||
predicate whitelistedLineForDuplication(File file, int line) {
|
||||
exists(Location loc |
|
||||
loc = whitelistedDuplicateElement().getLocation() |
|
||||
exists(Location loc | loc = whitelistedDuplicateElement().getLocation() |
|
||||
line = loc.getStartLine() and file = loc.getFile()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
16
csharp/ql/src/external/DefectFilter.qll
vendored
16
csharp/ql/src/external/DefectFilter.qll
vendored
@@ -1,14 +1,20 @@
|
||||
import csharp
|
||||
|
||||
external predicate defectResults(int id, string queryPath, string file, int startline, int startcol, int endline, int endcol, string message);
|
||||
external predicate defectResults(
|
||||
int id, string queryPath, string file, int startline, int startcol, int endline, int endcol,
|
||||
string message
|
||||
);
|
||||
|
||||
class DefectResult extends int {
|
||||
|
||||
DefectResult() { defectResults(this, _, _, _, _, _, _, _) }
|
||||
|
||||
string getQueryPath() { defectResults(this, result, _, _, _, _, _, _) }
|
||||
|
||||
File getFile() { exists(string path | defectResults(this, _, path, _, _, _, _, _) and result.getAbsolutePath() = path) }
|
||||
File getFile() {
|
||||
exists(string path |
|
||||
defectResults(this, _, path, _, _, _, _, _) and result.getAbsolutePath() = path
|
||||
)
|
||||
}
|
||||
|
||||
int getStartLine() { defectResults(this, _, _, result, _, _, _, _) }
|
||||
|
||||
@@ -21,7 +27,7 @@ class DefectResult extends int {
|
||||
string getMessage() { defectResults(this, _, _, _, _, _, _, result) }
|
||||
|
||||
string getURL() {
|
||||
result = "file://" + getFile().getAbsolutePath() + ":" + getStartLine() + ":" + getStartColumn() + ":" + getEndLine() + ":" + getEndColumn()
|
||||
result = "file://" + getFile().getAbsolutePath() + ":" + getStartLine() + ":" + getStartColumn()
|
||||
+ ":" + getEndLine() + ":" + getEndColumn()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
29
csharp/ql/src/external/ExternalArtifact.qll
vendored
29
csharp/ql/src/external/ExternalArtifact.qll
vendored
@@ -1,15 +1,18 @@
|
||||
import csharp
|
||||
|
||||
class ExternalDefect extends @externalDefect, Element {
|
||||
|
||||
string getQueryPath() { exists(string path | externalDefects(this, path, _, _, _) and
|
||||
result = path.replaceAll("\\", "/")) }
|
||||
string getQueryPath() {
|
||||
exists(string path |
|
||||
externalDefects(this, path, _, _, _) and
|
||||
result = path.replaceAll("\\", "/")
|
||||
)
|
||||
}
|
||||
|
||||
string getMessage() { externalDefects(this, _, _, result, _) }
|
||||
|
||||
float getSeverity() { externalDefects(this, _, _, _, result) }
|
||||
|
||||
override Location getLocation() { externalDefects(this,_,result,_,_) }
|
||||
override Location getLocation() { externalDefects(this, _, result, _, _) }
|
||||
|
||||
override string toString() {
|
||||
result = getQueryPath() + ": " + getLocation() + " - " + getMessage()
|
||||
@@ -17,20 +20,16 @@ class ExternalDefect extends @externalDefect, Element {
|
||||
}
|
||||
|
||||
class ExternalMetric extends @externalMetric, Element {
|
||||
|
||||
string getQueryPath() { externalMetrics(this, result, _, _) }
|
||||
|
||||
float getValue() { externalMetrics(this, _, _, result) }
|
||||
|
||||
override Location getLocation() { externalMetrics(this,_,result,_) }
|
||||
override Location getLocation() { externalMetrics(this, _, result, _) }
|
||||
|
||||
override string toString() {
|
||||
result = getQueryPath() + ": " + getLocation() + " - " + getValue()
|
||||
}
|
||||
override string toString() { result = getQueryPath() + ": " + getLocation() + " - " + getValue() }
|
||||
}
|
||||
|
||||
class ExternalData extends @externalDataElement {
|
||||
|
||||
string getDataPath() { externalData(this, result, _, _) }
|
||||
|
||||
string getQueryPath() { result = getDataPath().regexpReplaceAll("\\.[^.]*$", ".ql") }
|
||||
@@ -45,16 +44,13 @@ class ExternalData extends @externalDataElement {
|
||||
|
||||
date getFieldAsDate(int index) { result = getField(index).toDate() }
|
||||
|
||||
string toString() {
|
||||
result = getQueryPath() + ": " + buildTupleString(0)
|
||||
}
|
||||
string toString() { result = getQueryPath() + ": " + buildTupleString(0) }
|
||||
|
||||
private string buildTupleString(int start) {
|
||||
(start = getNumFields() - 1 and result = getField(start))
|
||||
start = getNumFields() - 1 and result = getField(start)
|
||||
or
|
||||
(start < getNumFields() - 1 and result = getField(start) + "," + buildTupleString(start+1))
|
||||
start < getNumFields() - 1 and result = getField(start) + "," + buildTupleString(start + 1)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,4 +66,3 @@ class DefectExternalData extends ExternalData {
|
||||
|
||||
string getMessage() { result = getField(1) }
|
||||
}
|
||||
|
||||
|
||||
16
csharp/ql/src/external/MetricFilter.qll
vendored
16
csharp/ql/src/external/MetricFilter.qll
vendored
@@ -1,14 +1,20 @@
|
||||
import csharp
|
||||
|
||||
external predicate metricResults(int id, string queryPath, string file, int startline, int startcol, int endline, int endcol, float value);
|
||||
external predicate metricResults(
|
||||
int id, string queryPath, string file, int startline, int startcol, int endline, int endcol,
|
||||
float value
|
||||
);
|
||||
|
||||
class MetricResult extends int {
|
||||
|
||||
MetricResult() { metricResults(this, _, _, _, _, _, _, _) }
|
||||
|
||||
string getQueryPath() { metricResults(this, result, _, _, _, _, _, _) }
|
||||
|
||||
File getFile() { exists(string path | metricResults(this, _, path, _, _, _, _, _) and result.getAbsolutePath() = path) }
|
||||
File getFile() {
|
||||
exists(string path |
|
||||
metricResults(this, _, path, _, _, _, _, _) and result.getAbsolutePath() = path
|
||||
)
|
||||
}
|
||||
|
||||
int getStartLine() { metricResults(this, _, _, result, _, _, _, _) }
|
||||
|
||||
@@ -31,7 +37,7 @@ class MetricResult extends int {
|
||||
float getValue() { metricResults(this, _, _, _, _, _, _, result) }
|
||||
|
||||
string getURL() {
|
||||
result = "file://" + getFile().getAbsolutePath() + ":" + getStartLine() + ":" + getStartColumn() + ":" + getEndLine() + ":" + getEndColumn()
|
||||
result = "file://" + getFile().getAbsolutePath() + ":" + getStartLine() + ":" + getStartColumn()
|
||||
+ ":" + getEndLine() + ":" + getEndColumn()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
40
csharp/ql/src/external/VCS.qll
vendored
40
csharp/ql/src/external/VCS.qll
vendored
@@ -1,7 +1,6 @@
|
||||
import csharp
|
||||
|
||||
class Commit extends @svnentry {
|
||||
|
||||
Commit() {
|
||||
svnaffectedfiles(this, _, _) and
|
||||
exists(date svnDate, date snapshotDate |
|
||||
@@ -31,41 +30,31 @@ class Commit extends @svnentry {
|
||||
|
||||
string getAnAffectedFilePath() { result = getAnAffectedFilePath(_) }
|
||||
|
||||
File getAnAffectedFile(string action) {
|
||||
svnaffectedfiles(this,result,action)
|
||||
}
|
||||
File getAnAffectedFile(string action) { svnaffectedfiles(this, result, action) }
|
||||
|
||||
File getAnAffectedFile() { exists(string action | result = this.getAnAffectedFile(action)) }
|
||||
|
||||
predicate isRecent() { recentCommit(this) }
|
||||
|
||||
int daysToNow() {
|
||||
exists(date now | snapshotDate(now) |
|
||||
result = getDate().daysTo(now) and result >= 0
|
||||
)
|
||||
exists(date now | snapshotDate(now) | result = getDate().daysTo(now) and result >= 0)
|
||||
}
|
||||
|
||||
int getRecentAdditionsForFile(File f) {
|
||||
svnchurn(this, f, result, _)
|
||||
}
|
||||
int getRecentAdditionsForFile(File f) { svnchurn(this, f, result, _) }
|
||||
|
||||
int getRecentDeletionsForFile(File f) {
|
||||
svnchurn(this, f, _, result)
|
||||
}
|
||||
int getRecentDeletionsForFile(File f) { svnchurn(this, f, _, result) }
|
||||
|
||||
int getRecentChurnForFile(File f) {
|
||||
exists (int added, int deleted | svnchurn(this,f,added,deleted) and result = added + deleted)
|
||||
exists(int added, int deleted | svnchurn(this, f, added, deleted) and result = added + deleted)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Author extends string {
|
||||
Author() { exists(Commit e | this = e.getAuthor()) }
|
||||
Author() { exists(Commit e | this = e.getAuthor()) }
|
||||
|
||||
Commit getACommit() { result.getAuthor() = this }
|
||||
|
||||
File getAnEditedFile() { result = this.getACommit().getAnAffectedFile() }
|
||||
Commit getACommit() { result.getAuthor() = this }
|
||||
|
||||
File getAnEditedFile() { result = this.getACommit().getAnAffectedFile() }
|
||||
}
|
||||
|
||||
predicate recentCommit(Commit e) {
|
||||
@@ -73,20 +62,17 @@ predicate recentCommit(Commit e) {
|
||||
snapshotDate(snapshotDate) and
|
||||
e.getDate() = commitDate and
|
||||
days = commitDate.daysTo(snapshotDate) and
|
||||
days >= 0 and days <= 60
|
||||
days >= 0 and
|
||||
days <= 60
|
||||
)
|
||||
}
|
||||
|
||||
date firstChange(File f) {
|
||||
result = min(Commit e, date toMin | (f = e.getAnAffectedFile()) and (toMin = e.getDate()) | toMin)
|
||||
result = min(Commit e, date toMin | f = e.getAnAffectedFile() and toMin = e.getDate() | toMin)
|
||||
}
|
||||
|
||||
predicate firstCommit(Commit e) {
|
||||
not exists(File f | f = e.getAnAffectedFile() |
|
||||
firstChange(f) < e.getDate()
|
||||
)
|
||||
not exists(File f | f = e.getAnAffectedFile() | firstChange(f) < e.getDate())
|
||||
}
|
||||
|
||||
predicate artificialChange(Commit e) {
|
||||
firstCommit(e) or e.getChangeSize() >= 50000
|
||||
}
|
||||
predicate artificialChange(Commit e) { firstCommit(e) or e.getChangeSize() >= 50000 }
|
||||
|
||||
@@ -40,36 +40,22 @@ class AspAttribute extends AspElement, @asp_attribute { }
|
||||
*/
|
||||
class AspOpenTag extends AspElement, @asp_open_tag {
|
||||
/** Either `>` or `/>`, depending on whether it's an empty tag. */
|
||||
private string closeAngle() {
|
||||
if isEmpty()
|
||||
then result = "/>"
|
||||
else result = ">"
|
||||
}
|
||||
private string closeAngle() { if isEmpty() then result = "/>" else result = ">" }
|
||||
|
||||
/** Gets the `i`th attribute of this open tag. */
|
||||
AspAttribute getAttribute(int i) {
|
||||
asp_tag_attribute(this, i, _, result)
|
||||
}
|
||||
AspAttribute getAttribute(int i) { asp_tag_attribute(this, i, _, result) }
|
||||
|
||||
/** Gets the attribute named `name`, if any. */
|
||||
AspAttribute getAttributeByName(string name) {
|
||||
asp_tag_attribute(this, _, name, result)
|
||||
}
|
||||
AspAttribute getAttributeByName(string name) { asp_tag_attribute(this, _, name, result) }
|
||||
|
||||
/** Gets the name of this open tag. */
|
||||
string getName() {
|
||||
asp_tag_name(this, result)
|
||||
}
|
||||
string getName() { asp_tag_name(this, result) }
|
||||
|
||||
/** Holds if this open tag has an attribute. */
|
||||
predicate hasAttribute() {
|
||||
asp_tag_attribute(this, _, _, _)
|
||||
}
|
||||
predicate hasAttribute() { asp_tag_attribute(this, _, _, _) }
|
||||
|
||||
/** Holds if this open tag is empty. */
|
||||
predicate isEmpty() {
|
||||
asp_tag_isempty(this)
|
||||
}
|
||||
predicate isEmpty() { asp_tag_isempty(this) }
|
||||
|
||||
override string toString() {
|
||||
if hasAttribute()
|
||||
@@ -89,13 +75,9 @@ class AspOpenTag extends AspElement, @asp_open_tag {
|
||||
*/
|
||||
class AspCloseTag extends AspElement, @asp_close_tag {
|
||||
/** Gets the name of this close tag. */
|
||||
string getName() {
|
||||
result = getBody()
|
||||
}
|
||||
string getName() { result = getBody() }
|
||||
|
||||
override string toString() {
|
||||
result = "</" + getName() + ">"
|
||||
}
|
||||
override string toString() { result = "</" + getName() + ">" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -111,7 +93,7 @@ class AspCode extends AspAttribute, @asp_code { }
|
||||
class AspBlockCode extends AspCode {
|
||||
AspBlockCode() { not asp_code_inline(this) }
|
||||
|
||||
override string toString() { result="<% ... %>" }
|
||||
override string toString() { result = "<% ... %>" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -120,7 +102,7 @@ class AspBlockCode extends AspCode {
|
||||
class AspInlineCode extends AspCode {
|
||||
AspInlineCode() { asp_code_inline(this) }
|
||||
|
||||
override string toString() { result="<%= ... %>" }
|
||||
override string toString() { result = "<%= ... %>" }
|
||||
}
|
||||
|
||||
/** A comment, for example `<!-- TODO -->`. */
|
||||
@@ -146,33 +128,25 @@ class AspServerComment extends AspComment {
|
||||
* ```
|
||||
*/
|
||||
class AspDataBinding extends AspAttribute, @asp_data_binding {
|
||||
override string toString() { result="<%# ... %>" }
|
||||
override string toString() { result = "<%# ... %>" }
|
||||
}
|
||||
|
||||
/** A directive, for example `<%@ Page Language="C#" %>`. */
|
||||
class AspDirective extends AspElement, @asp_directive {
|
||||
/** Gets the `i`th attribute of this directive. */
|
||||
AspAttribute getAttribute(int i) {
|
||||
asp_directive_attribute(this, i, _, result)
|
||||
}
|
||||
AspAttribute getAttribute(int i) { asp_directive_attribute(this, i, _, result) }
|
||||
|
||||
/** Gets the attribute named `name`, if any. */
|
||||
AspAttribute getAttributeByName(string name) {
|
||||
asp_directive_attribute(this, _, name, result)
|
||||
}
|
||||
AspAttribute getAttributeByName(string name) { asp_directive_attribute(this, _, name, result) }
|
||||
|
||||
/**
|
||||
* Gets the name of this directive, for example `Page` in
|
||||
* `<%@ Page Language="C#" %>`.
|
||||
*/
|
||||
string getName() {
|
||||
asp_directive_name(this, result)
|
||||
}
|
||||
string getName() { asp_directive_name(this, result) }
|
||||
|
||||
/** Holds if this directive has an attribute. */
|
||||
predicate hasAttribute() {
|
||||
exists(getAttribute(_))
|
||||
}
|
||||
predicate hasAttribute() { exists(getAttribute(_)) }
|
||||
|
||||
override string toString() {
|
||||
if hasAttribute()
|
||||
@@ -191,9 +165,7 @@ class AspQuotedString extends AspAttribute, @asp_quoted_string {
|
||||
}
|
||||
|
||||
/** Arbitrary text. It will be inserted into the document as is. */
|
||||
class AspText extends AspElement, @asp_text {
|
||||
override string toString() { result=getBody() }
|
||||
}
|
||||
class AspText extends AspElement, @asp_text { override string toString() { result = getBody() } }
|
||||
|
||||
/** An XML directive, such as a `DOCTYPE` declaration. */
|
||||
class AspXmlDirective extends AspElement, @asp_xml_directive {
|
||||
@@ -209,9 +181,7 @@ class PageDirective extends AspDirective {
|
||||
/**
|
||||
* Gets the 'CodeBehind' class from which this page inherits.
|
||||
*/
|
||||
ValueOrRefType getInheritedType() {
|
||||
result.getQualifiedName() = getInheritedTypeQualifiedName()
|
||||
}
|
||||
ValueOrRefType getInheritedType() { result.getQualifiedName() = getInheritedTypeQualifiedName() }
|
||||
|
||||
private string getInheritedTypeQualifiedName() {
|
||||
/*
|
||||
@@ -222,15 +192,17 @@ class PageDirective extends AspDirective {
|
||||
* - `CodeBehindFile`/`CodeFile`: used by tooling, but not semantically
|
||||
* relevant at runtime
|
||||
*/
|
||||
|
||||
exists(string inherits | inherits = getAttributeByName("Inherits").getBody() |
|
||||
if inherits.indexOf(".") != -1
|
||||
then result = inherits
|
||||
else exists(string className | className = getAttributeByName("ClassName").getBody() |
|
||||
// take everything up to and including the last .
|
||||
className.prefix(className.indexOf(".", count(className.indexOf("."))-1, 0)+1) + inherits = result
|
||||
)
|
||||
else
|
||||
exists(string className | className = getAttributeByName("ClassName").getBody() |
|
||||
// take everything up to and including the last .
|
||||
className.prefix(className.indexOf(".", count(className.indexOf(".")) - 1, 0) + 1) +
|
||||
inherits = result
|
||||
)
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,22 +211,17 @@ class PageDirective extends AspDirective {
|
||||
*/
|
||||
class CodeBehindFile extends File {
|
||||
CodeBehindFile() {
|
||||
getExtension() = "aspx"
|
||||
and
|
||||
getExtension() = "aspx" and
|
||||
exists(PageDirective pageDir | pageDir.getLocation().getFile() = this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `PageDirective` that defines this page.
|
||||
*/
|
||||
PageDirective getPageDirective() {
|
||||
result.getLocation().getFile() = this
|
||||
}
|
||||
PageDirective getPageDirective() { result.getLocation().getFile() = this }
|
||||
|
||||
/**
|
||||
* Gets the 'CodeBehind' class from which this page inherits.
|
||||
*/
|
||||
ValueOrRefType getInheritedType() {
|
||||
result = getPageDirective().getInheritedType()
|
||||
}
|
||||
ValueOrRefType getInheritedType() { result = getPageDirective().getInheritedType() }
|
||||
}
|
||||
|
||||
@@ -1,39 +1,29 @@
|
||||
/**
|
||||
* Provides classes and predicates related to ASP.NET Web.config files.
|
||||
*/
|
||||
|
||||
import csharp
|
||||
|
||||
/**
|
||||
* A `Web.config` file.
|
||||
*/
|
||||
class WebConfigXML extends XMLFile {
|
||||
WebConfigXML() {
|
||||
getName().matches("%Web.config")
|
||||
}
|
||||
}
|
||||
class WebConfigXML extends XMLFile { WebConfigXML() { getName().matches("%Web.config") } }
|
||||
|
||||
/** A `<configuration>` tag in an ASP.NET configuration file. */
|
||||
class ConfigurationXMLElement extends XMLElement {
|
||||
|
||||
ConfigurationXMLElement() {
|
||||
this.getName().toLowerCase() = "configuration"
|
||||
}
|
||||
|
||||
ConfigurationXMLElement() { this.getName().toLowerCase() = "configuration" }
|
||||
}
|
||||
|
||||
/** A `<location>` tag in an ASP.NET configuration file. */
|
||||
class LocationXMLElement extends XMLElement {
|
||||
|
||||
LocationXMLElement() {
|
||||
this.getParent() instanceof ConfigurationXMLElement and
|
||||
this.getName().toLowerCase() = "location"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** A `<system.web>` tag in an ASP.NET configuration file. */
|
||||
class SystemWebXMLElement extends XMLElement {
|
||||
|
||||
SystemWebXMLElement() {
|
||||
(
|
||||
this.getParent() instanceof ConfigurationXMLElement
|
||||
@@ -42,12 +32,10 @@ class SystemWebXMLElement extends XMLElement {
|
||||
) and
|
||||
this.getName().toLowerCase() = "system.web"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** A `<system.webServer>` tag in an ASP.NET configuration file. */
|
||||
class SystemWebServerXMLElement extends XMLElement {
|
||||
|
||||
SystemWebServerXMLElement() {
|
||||
(
|
||||
this.getParent() instanceof ConfigurationXMLElement
|
||||
@@ -56,26 +44,20 @@ class SystemWebServerXMLElement extends XMLElement {
|
||||
) and
|
||||
this.getName().toLowerCase() = "system.webserver"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** A `<customErrors>` tag in an ASP.NET configuration file. */
|
||||
class CustomErrorsXMLElement extends XMLElement {
|
||||
|
||||
CustomErrorsXMLElement() {
|
||||
this.getParent() instanceof SystemWebXMLElement and
|
||||
this.getName().toLowerCase() = "customerrors"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** A `<httpRuntime>` tag in an ASP.NET configuration file. */
|
||||
class HttpRuntimeXMLElement extends XMLElement {
|
||||
|
||||
HttpRuntimeXMLElement() {
|
||||
this.getParent() instanceof SystemWebXMLElement and
|
||||
this.getName().toLowerCase() = "httpruntime"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -8,95 +8,88 @@ private import CIL
|
||||
|
||||
/** An instruction that accesses a variable. */
|
||||
class Access extends Instruction, @cil_access {
|
||||
|
||||
/** Gets the declaration referenced by this instruction. */
|
||||
Variable getTarget() { cil_access(this,result) }
|
||||
|
||||
Variable getTarget() { cil_access(this, result) }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that accesses a variable.
|
||||
* This class is provided for consistency with the C# data model.
|
||||
*/
|
||||
class VariableAccess extends Access, @cil_access {
|
||||
}
|
||||
class VariableAccess extends Access, @cil_access { }
|
||||
|
||||
/** An instruction that reads a variable. */
|
||||
class ReadAccess extends VariableAccess, Expr, @cil_read_access {
|
||||
override Type getType() { result=getTarget().getType() }
|
||||
override Type getType() { result = getTarget().getType() }
|
||||
}
|
||||
|
||||
/** An instruction yielding an address. */
|
||||
class ReadRef extends Expr, @cil_read_ref {
|
||||
}
|
||||
class ReadRef extends Expr, @cil_read_ref { }
|
||||
|
||||
/** An instruction that reads the address of a variable. */
|
||||
class ReadRefAccess extends ReadAccess, ReadRef {
|
||||
}
|
||||
class ReadRefAccess extends ReadAccess, ReadRef { }
|
||||
|
||||
/** An instruction that writes a variable. */
|
||||
class WriteAccess extends VariableAccess, @cil_write_access {
|
||||
Expr getExpr() { none() }
|
||||
}
|
||||
class WriteAccess extends VariableAccess, @cil_write_access { Expr getExpr() { none() } }
|
||||
|
||||
/** An instruction that accesses a parameter. */
|
||||
class ParameterAccess extends StackVariableAccess, @cil_arg_access {
|
||||
override Parameter getTarget() { result=StackVariableAccess.super.getTarget() }
|
||||
override Parameter getTarget() { result = StackVariableAccess.super.getTarget() }
|
||||
}
|
||||
|
||||
/** An instruction that reads a parameter. */
|
||||
class ParameterReadAccess extends ParameterAccess, ReadAccess {
|
||||
override int getPopCount() { result=0 }
|
||||
override int getPopCount() { result = 0 }
|
||||
}
|
||||
|
||||
/** An instruction that writes to a parameter. */
|
||||
class ParameterWriteAccess extends ParameterAccess, WriteAccess {
|
||||
override int getPopCount() { result=1 }
|
||||
override Expr getExpr() { result=getOperand(0) }
|
||||
override int getPopCount() { result = 1 }
|
||||
|
||||
override Expr getExpr() { result = getOperand(0) }
|
||||
}
|
||||
|
||||
/** An access to the `this` parameter. */
|
||||
class ThisAccess extends ParameterReadAccess {
|
||||
ThisAccess() {
|
||||
this.getTarget() instanceof ThisParameter
|
||||
}
|
||||
ThisAccess() { this.getTarget() instanceof ThisParameter }
|
||||
}
|
||||
|
||||
/** An instruction that accesses a stack variable. */
|
||||
class StackVariableAccess extends VariableAccess, @cil_stack_access {
|
||||
override StackVariable getTarget() { result=VariableAccess.super.getTarget() }
|
||||
override StackVariable getTarget() { result = VariableAccess.super.getTarget() }
|
||||
}
|
||||
|
||||
/** An instruction that accesses a local variable. */
|
||||
class LocalVariableAccess extends StackVariableAccess, @cil_local_access {
|
||||
override LocalVariable getTarget() { result=StackVariableAccess.super.getTarget() }
|
||||
override LocalVariable getTarget() { result = StackVariableAccess.super.getTarget() }
|
||||
}
|
||||
|
||||
/** An instruction that writes to a local variable. */
|
||||
class LocalVariableWriteAccess extends LocalVariableAccess, WriteAccess {
|
||||
override int getPopCount() { result=1 }
|
||||
override Expr getExpr() { result=getOperand(0) }
|
||||
override string getExtra() { result="L" + getTarget().getIndex() }
|
||||
override int getPopCount() { result = 1 }
|
||||
|
||||
override Expr getExpr() { result = getOperand(0) }
|
||||
|
||||
override string getExtra() { result = "L" + getTarget().getIndex() }
|
||||
}
|
||||
|
||||
/** An instruction that reads a local variable. */
|
||||
class LocalVariableReadAccess extends LocalVariableAccess, ReadAccess {
|
||||
override int getPopCount() { result=0 }
|
||||
override int getPopCount() { result = 0 }
|
||||
}
|
||||
|
||||
/** An instruction that accesses a field. */
|
||||
class FieldAccess extends VariableAccess, @cil_field_access {
|
||||
override Field getTarget() { result=VariableAccess.super.getTarget() }
|
||||
override string getExtra() { result=getTarget().getName() }
|
||||
override Field getTarget() { result = VariableAccess.super.getTarget() }
|
||||
|
||||
override string getExtra() { result = getTarget().getName() }
|
||||
|
||||
/** Gets the qualifier of the access, if any. */
|
||||
abstract Expr getQualifier();
|
||||
}
|
||||
|
||||
/** An instruction that reads a field. */
|
||||
abstract class FieldReadAccess extends FieldAccess, ReadAccess {
|
||||
}
|
||||
abstract class FieldReadAccess extends FieldAccess, ReadAccess { }
|
||||
|
||||
/** An instruction that writes a field. */
|
||||
abstract class FieldWriteAccess extends FieldAccess, WriteAccess {
|
||||
}
|
||||
abstract class FieldWriteAccess extends FieldAccess, WriteAccess { }
|
||||
|
||||
@@ -5,12 +5,11 @@ private import semmle.code.csharp.Location as CS
|
||||
|
||||
/** An attribute to a declaration, such as a method, field, type or parameter. */
|
||||
class Attribute extends Element, @cil_attribute {
|
||||
|
||||
/** Gets the declaration this attribute is attached to. */
|
||||
Declaration getDeclaration() { cil_attribute(this,result,_) }
|
||||
Declaration getDeclaration() { cil_attribute(this, result, _) }
|
||||
|
||||
/** Gets the constructor used to construct this attribute. */
|
||||
Method getConstructor() { cil_attribute(this,_,result) }
|
||||
Method getConstructor() { cil_attribute(this, _, result) }
|
||||
|
||||
/** Gets the type of this attribute. */
|
||||
Type getType() { result = getConstructor().getDeclaringType() }
|
||||
@@ -24,7 +23,7 @@ class Attribute extends Element, @cil_attribute {
|
||||
string getNamedArgument(string name) { cil_attribute_named_argument(this, name, result) }
|
||||
|
||||
/** Gets an argument of this attribute, if any. */
|
||||
string getAnArgument() { result=getArgument(_) or result=getNamedArgument(_) }
|
||||
string getAnArgument() { result = getArgument(_) or result = getNamedArgument(_) }
|
||||
|
||||
override CS::Location getLocation() { result=getDeclaration().getLocation() }
|
||||
override CS::Location getLocation() { result = getDeclaration().getLocation() }
|
||||
}
|
||||
|
||||
@@ -10,14 +10,10 @@ private import CIL
|
||||
*/
|
||||
class BasicBlock extends Internal::TBasicBlockStart {
|
||||
/** Gets an immediate successor of this basic block, if any. */
|
||||
BasicBlock getASuccessor() {
|
||||
result.getFirstNode() = getLastNode().getASuccessor()
|
||||
}
|
||||
BasicBlock getASuccessor() { result.getFirstNode() = getLastNode().getASuccessor() }
|
||||
|
||||
/** Gets an immediate predecessor of this basic block, if any. */
|
||||
BasicBlock getAPredecessor() {
|
||||
result.getASuccessor() = this
|
||||
}
|
||||
BasicBlock getAPredecessor() { result.getASuccessor() = this }
|
||||
|
||||
/**
|
||||
* Gets an immediate `true` successor, if any.
|
||||
@@ -35,9 +31,7 @@ class BasicBlock extends Internal::TBasicBlockStart {
|
||||
* The basic block on line 2 is an immediate `true` successor of the
|
||||
* basic block on line 1.
|
||||
*/
|
||||
BasicBlock getATrueSuccessor() {
|
||||
result.getFirstNode() = getLastNode().getTrueSuccessor()
|
||||
}
|
||||
BasicBlock getATrueSuccessor() { result.getFirstNode() = getLastNode().getTrueSuccessor() }
|
||||
|
||||
/**
|
||||
* Gets an immediate `false` successor, if any.
|
||||
@@ -55,32 +49,22 @@ class BasicBlock extends Internal::TBasicBlockStart {
|
||||
* The basic block on line 2 is an immediate `false` successor of the
|
||||
* basic block on line 1.
|
||||
*/
|
||||
BasicBlock getAFalseSuccessor() {
|
||||
result.getFirstNode() = getLastNode().getFalseSuccessor()
|
||||
}
|
||||
BasicBlock getAFalseSuccessor() { result.getFirstNode() = getLastNode().getFalseSuccessor() }
|
||||
|
||||
/** Gets the control flow node at a specific (zero-indexed) position in this basic block. */
|
||||
ControlFlowNode getNode(int pos) { Internal::bbIndex(getFirstNode(), result, pos) }
|
||||
|
||||
/** Gets a control flow node in this basic block. */
|
||||
ControlFlowNode getANode() {
|
||||
result = getNode(_)
|
||||
}
|
||||
ControlFlowNode getANode() { result = getNode(_) }
|
||||
|
||||
/** Gets the first control flow node in this basic block. */
|
||||
ControlFlowNode getFirstNode() {
|
||||
this = Internal::TBasicBlockStart(result)
|
||||
}
|
||||
ControlFlowNode getFirstNode() { this = Internal::TBasicBlockStart(result) }
|
||||
|
||||
/** Gets the last control flow node in this basic block. */
|
||||
ControlFlowNode getLastNode() {
|
||||
result = getNode(length() - 1)
|
||||
}
|
||||
ControlFlowNode getLastNode() { result = getNode(length() - 1) }
|
||||
|
||||
/** Gets the length of this basic block. */
|
||||
int length() {
|
||||
result = strictcount(getANode())
|
||||
}
|
||||
int length() { result = strictcount(getANode()) }
|
||||
|
||||
/**
|
||||
* Holds if this basic block strictly dominates basic block `bb`.
|
||||
@@ -103,9 +87,7 @@ class BasicBlock extends Internal::TBasicBlockStart {
|
||||
* basic block on line 4 (all paths from the entry point of `M`
|
||||
* to `return s.Length;` must go through the null check).
|
||||
*/
|
||||
predicate strictlyDominates(BasicBlock bb) {
|
||||
bbIDominates+(this, bb)
|
||||
}
|
||||
predicate strictlyDominates(BasicBlock bb) { bbIDominates+(this, bb) }
|
||||
|
||||
/**
|
||||
* Holds if this basic block dominates basic block `bb`.
|
||||
@@ -165,10 +147,7 @@ class BasicBlock extends Internal::TBasicBlockStart {
|
||||
/**
|
||||
* Holds if this basic block dominates a predecessor of `df`.
|
||||
*/
|
||||
private
|
||||
predicate dominatesPredecessor(BasicBlock df) {
|
||||
dominates(df.getAPredecessor())
|
||||
}
|
||||
private predicate dominatesPredecessor(BasicBlock df) { dominates(df.getAPredecessor()) }
|
||||
|
||||
/**
|
||||
* Gets the basic block that immediately dominates this basic block, if any.
|
||||
@@ -192,9 +171,7 @@ class BasicBlock extends Internal::TBasicBlockStart {
|
||||
* to `return s.Length;` must go through the null check, and the null check
|
||||
* is an immediate predecessor of `return s.Length;`).
|
||||
*/
|
||||
BasicBlock getImmediateDominator() {
|
||||
bbIDominates(result, this)
|
||||
}
|
||||
BasicBlock getImmediateDominator() { bbIDominates(result, this) }
|
||||
|
||||
/**
|
||||
* Holds if this basic block strictly post-dominates basic block `bb`.
|
||||
@@ -220,9 +197,7 @@ class BasicBlock extends Internal::TBasicBlockStart {
|
||||
* line 3 (all paths to the exit point of `M` from `return s.Length;`
|
||||
* must go through the `WriteLine` call).
|
||||
*/
|
||||
predicate strictlyPostDominates(BasicBlock bb) {
|
||||
bbIPostDominates+(this, bb)
|
||||
}
|
||||
predicate strictlyPostDominates(BasicBlock bb) { bbIPostDominates+(this, bb) }
|
||||
|
||||
/**
|
||||
* Holds if this basic block post-dominates basic block `bb`.
|
||||
@@ -261,23 +236,20 @@ class BasicBlock extends Internal::TBasicBlockStart {
|
||||
* even if this basic block is syntactically inside a `while` loop if the
|
||||
* necessary back edges are unreachable.
|
||||
*/
|
||||
predicate inLoop() {
|
||||
this.getASuccessor+() = this
|
||||
}
|
||||
predicate inLoop() { this.getASuccessor+() = this }
|
||||
|
||||
/** Gets a textual representation of this basic block. */
|
||||
string toString() {
|
||||
result = getFirstNode().toString()
|
||||
}
|
||||
string toString() { result = getFirstNode().toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal implementation details.
|
||||
*/
|
||||
private cached module Internal {
|
||||
cached
|
||||
private module Internal {
|
||||
/** Internal representation of basic blocks. */
|
||||
cached newtype TBasicBlock =
|
||||
TBasicBlockStart(ControlFlowNode cfn) { startsBB(cfn) }
|
||||
cached
|
||||
newtype TBasicBlock = TBasicBlockStart(ControlFlowNode cfn) { startsBB(cfn) }
|
||||
|
||||
/** Holds if `cfn` starts a new basic block. */
|
||||
private predicate startsBB(ControlFlowNode cfn) {
|
||||
@@ -285,7 +257,9 @@ private cached module Internal {
|
||||
or
|
||||
cfn.isJoin()
|
||||
or
|
||||
exists(ControlFlowNode pred | pred = cfn.getAPredecessor() | strictcount(pred.getASuccessor()) > 1)
|
||||
exists(ControlFlowNode pred | pred = cfn.getAPredecessor() |
|
||||
strictcount(pred.getASuccessor()) > 1
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -312,18 +286,13 @@ private cached module Internal {
|
||||
* Holds if the first node of basic block `succ` is a control flow
|
||||
* successor of the last node of basic block `pred`.
|
||||
*/
|
||||
private predicate succBB(BasicBlock pred, BasicBlock succ) {
|
||||
succ = pred.getASuccessor()
|
||||
}
|
||||
private predicate succBB(BasicBlock pred, BasicBlock succ) { succ = pred.getASuccessor() }
|
||||
|
||||
/** Holds if `dom` is an immediate dominator of `bb`. */
|
||||
predicate bbIDominates(BasicBlock dom, BasicBlock bb) =
|
||||
idominance(entryBB/1, succBB/2)(_, dom, bb)
|
||||
predicate bbIDominates(BasicBlock dom, BasicBlock bb) = idominance(entryBB/1, succBB/2)(_, dom, bb)
|
||||
|
||||
/** Holds if `pred` is a basic block predecessor of `succ`. */
|
||||
private predicate predBB(BasicBlock succ, BasicBlock pred) {
|
||||
succBB(pred, succ)
|
||||
}
|
||||
private predicate predBB(BasicBlock succ, BasicBlock pred) { succBB(pred, succ) }
|
||||
|
||||
/** Holds if `dom` is an immediate post-dominator of `bb`. */
|
||||
predicate bbIPostDominates(BasicBlock dom, BasicBlock bb) =
|
||||
@@ -333,34 +302,24 @@ predicate bbIPostDominates(BasicBlock dom, BasicBlock bb) =
|
||||
* An entry basic block, that is, a basic block whose first node is
|
||||
* the entry node of a callable.
|
||||
*/
|
||||
class EntryBasicBlock extends BasicBlock {
|
||||
EntryBasicBlock() { entryBB(this) }
|
||||
}
|
||||
class EntryBasicBlock extends BasicBlock { EntryBasicBlock() { entryBB(this) } }
|
||||
|
||||
/** Holds if `bb` is an entry basic block. */
|
||||
private predicate entryBB(BasicBlock bb) {
|
||||
bb.getFirstNode() instanceof EntryPoint
|
||||
}
|
||||
private predicate entryBB(BasicBlock bb) { bb.getFirstNode() instanceof EntryPoint }
|
||||
|
||||
/**
|
||||
* An exit basic block, that is, a basic block whose last node is
|
||||
* an exit node.
|
||||
*/
|
||||
class ExitBasicBlock extends BasicBlock {
|
||||
ExitBasicBlock() { exitBB(this) }
|
||||
}
|
||||
class ExitBasicBlock extends BasicBlock { ExitBasicBlock() { exitBB(this) } }
|
||||
|
||||
/** Holds if `bb` is an exit basic block. */
|
||||
private predicate exitBB(BasicBlock bb) {
|
||||
not exists(bb.getLastNode().getASuccessor())
|
||||
}
|
||||
private predicate exitBB(BasicBlock bb) { not exists(bb.getLastNode().getASuccessor()) }
|
||||
|
||||
/**
|
||||
* A basic block with more than one predecessor.
|
||||
*/
|
||||
class JoinBlock extends BasicBlock {
|
||||
JoinBlock() { getFirstNode().isJoin() }
|
||||
}
|
||||
class JoinBlock extends BasicBlock { JoinBlock() { getFirstNode().isJoin() } }
|
||||
|
||||
/** A basic block that terminates in a condition, splitting the subsequent control flow. */
|
||||
class ConditionBlock extends BasicBlock {
|
||||
@@ -412,9 +371,9 @@ class ConditionBlock extends BasicBlock {
|
||||
* that `this` strictly dominates `controlled` so that isn't necessary to check
|
||||
* directly.
|
||||
*/
|
||||
|
||||
exists(BasicBlock succ |
|
||||
isCandidateSuccessor(succ, testIsTrue)
|
||||
and
|
||||
isCandidateSuccessor(succ, testIsTrue) and
|
||||
succ.dominates(controlled)
|
||||
)
|
||||
}
|
||||
@@ -424,11 +383,7 @@ class ConditionBlock extends BasicBlock {
|
||||
testIsTrue = true and succ = getATrueSuccessor()
|
||||
or
|
||||
testIsTrue = false and succ = getAFalseSuccessor()
|
||||
)
|
||||
and
|
||||
forall(BasicBlock pred |
|
||||
pred = succ.getAPredecessor() and pred != this |
|
||||
succ.dominates(pred)
|
||||
)
|
||||
) and
|
||||
forall(BasicBlock pred | pred = succ.getAPredecessor() and pred != this | succ.dominates(pred))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,14 +6,10 @@ private import CIL
|
||||
private import csharp as CS
|
||||
|
||||
private newtype ConsistencyCheck =
|
||||
MissingEntityCheck()
|
||||
or
|
||||
TypeCheck(Type t)
|
||||
or
|
||||
CfgCheck(ControlFlowNode n)
|
||||
or
|
||||
DeclarationCheck(Declaration d)
|
||||
or
|
||||
MissingEntityCheck() or
|
||||
TypeCheck(Type t) or
|
||||
CfgCheck(ControlFlowNode n) or
|
||||
DeclarationCheck(Declaration d) or
|
||||
MissingCSharpCheck(CS::Declaration d)
|
||||
|
||||
/**
|
||||
@@ -21,6 +17,7 @@ private newtype ConsistencyCheck =
|
||||
*/
|
||||
abstract class ConsistencyViolation extends ConsistencyCheck {
|
||||
abstract string toString();
|
||||
|
||||
abstract string getMessage();
|
||||
}
|
||||
|
||||
@@ -29,6 +26,7 @@ abstract class ConsistencyViolation extends ConsistencyCheck {
|
||||
*/
|
||||
abstract class DisabledCheck extends ConsistencyCheck {
|
||||
DisabledCheck() { none() }
|
||||
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
@@ -37,40 +35,45 @@ abstract class DisabledCheck extends ConsistencyCheck {
|
||||
*/
|
||||
class CfgViolation extends ConsistencyViolation, CfgCheck {
|
||||
ControlFlowNode getNode() { this = CfgCheck(result) }
|
||||
|
||||
override string toString() { result = getNode().toString() }
|
||||
override abstract string getMessage();
|
||||
|
||||
abstract override string getMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* A consistency violation in a specific instruction.
|
||||
*/
|
||||
class InstructionViolation extends CfgViolation, CfgCheck {
|
||||
InstructionViolation() { exists(Instruction i | this=CfgCheck(i)) }
|
||||
InstructionViolation() { exists(Instruction i | this = CfgCheck(i)) }
|
||||
|
||||
/** Gets the instruction containing the violation. */
|
||||
Instruction getInstruction() { this = CfgCheck(result) }
|
||||
|
||||
private string getInstructionsUpTo() {
|
||||
result = concat(Instruction i |
|
||||
i.getIndex() <= this.getInstruction().getIndex() and
|
||||
i.getImplementation() = this.getInstruction().getImplementation() |
|
||||
i.toString() + " [push: " + i.getPushCount() + ", pop: " + i.getPopCount() + "]", "; " order by i.getIndex()
|
||||
)
|
||||
i.getIndex() <= this.getInstruction().getIndex() and
|
||||
i.getImplementation() = this.getInstruction().getImplementation()
|
||||
|
|
||||
i.toString() + " [push: " + i.getPushCount() + ", pop: " + i.getPopCount() + "]", "; "
|
||||
order by
|
||||
i.getIndex()
|
||||
)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = getInstruction().getImplementation().getMethod().toStringWithTypes() + ": " + getInstruction().toString() + ", " + getInstructionsUpTo()
|
||||
result = getInstruction().getImplementation().getMethod().toStringWithTypes() + ": " +
|
||||
getInstruction().toString() + ", " + getInstructionsUpTo()
|
||||
}
|
||||
override abstract string getMessage();
|
||||
|
||||
abstract override string getMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* A literal that does not have exactly one `getValue()`.
|
||||
*/
|
||||
class MissingValue extends InstructionViolation {
|
||||
MissingValue() {
|
||||
exists(Literal l | l = this.getInstruction() | count(l.getValue()) != 1)
|
||||
}
|
||||
MissingValue() { exists(Literal l | l = this.getInstruction() | count(l.getValue()) != 1) }
|
||||
|
||||
override string getMessage() { result = "Literal has invalid getValue()" }
|
||||
}
|
||||
@@ -79,23 +82,25 @@ class MissingValue extends InstructionViolation {
|
||||
* A call that does not have exactly one `getTarget()`.
|
||||
*/
|
||||
class MissingCallTarget extends InstructionViolation {
|
||||
MissingCallTarget() {
|
||||
exists(Call c | c = this.getInstruction() | count(c.getTarget())!=1)
|
||||
}
|
||||
MissingCallTarget() { exists(Call c | c = this.getInstruction() | count(c.getTarget()) != 1) }
|
||||
|
||||
override string getMessage() { result = "Call has invalid target" }
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* An instruction that has not been assigned a specific QL class.
|
||||
*/
|
||||
class MissingOpCode extends InstructionViolation {
|
||||
MissingOpCode() { not exists(this.getInstruction().getOpcodeName()) }
|
||||
override string getMessage() { result = "Opcode " + this.getInstruction().getOpcode() + " is missing a QL class" }
|
||||
override string toString() { result = "Unknown instruction in " + getInstruction().getImplementation().getMethod().toString() }
|
||||
}
|
||||
|
||||
override string getMessage() {
|
||||
result = "Opcode " + this.getInstruction().getOpcode() + " is missing a QL class"
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "Unknown instruction in " + getInstruction().getImplementation().getMethod().toString()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that is missing an operand. It means that there is no instruction which pushes
|
||||
@@ -107,15 +112,19 @@ class MissingOpCode extends InstructionViolation {
|
||||
*/
|
||||
class MissingOperand extends InstructionViolation {
|
||||
MissingOperand() {
|
||||
exists(Instruction i, int op | i=getInstruction() and op in [0..i.getPopCount()-1] |
|
||||
not exists(i.getOperand(op)) and not i instanceof DeadInstruction)
|
||||
exists(Instruction i, int op | i = getInstruction() and op in [0 .. i.getPopCount() - 1] |
|
||||
not exists(i.getOperand(op)) and not i instanceof DeadInstruction
|
||||
)
|
||||
}
|
||||
|
||||
int getMissingOperand() {
|
||||
result in [0..getInstruction().getPopCount()-1] and not exists(getInstruction().getOperand(result))
|
||||
result in [0 .. getInstruction().getPopCount() - 1] and
|
||||
not exists(getInstruction().getOperand(result))
|
||||
}
|
||||
|
||||
override string getMessage() { result = "This instruction is missing operand " + getMissingOperand() }
|
||||
override string getMessage() {
|
||||
result = "This instruction is missing operand " + getMissingOperand()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -123,11 +132,8 @@ class MissingOperand extends InstructionViolation {
|
||||
* These should not exist, however it turns out that the Mono compiler sometimes
|
||||
* emits them.
|
||||
*/
|
||||
class DeadInstruction extends Instruction
|
||||
{
|
||||
DeadInstruction() {
|
||||
not exists(EntryPoint e | e.getASuccessor+() = this)
|
||||
}
|
||||
class DeadInstruction extends Instruction {
|
||||
DeadInstruction() { not exists(EntryPoint e | e.getASuccessor+() = this) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -137,11 +143,10 @@ class DeadInstruction extends Instruction
|
||||
* Disabled, because Mono compiler sometimes emits dead instructions.
|
||||
*/
|
||||
class DeadInstructionViolation extends InstructionViolation, DisabledCheck {
|
||||
DeadInstructionViolation() {
|
||||
getInstruction() instanceof DeadInstruction
|
||||
}
|
||||
DeadInstructionViolation() { getInstruction() instanceof DeadInstruction }
|
||||
|
||||
override string getMessage() { result = "This instruction is not reachable" }
|
||||
|
||||
override string getMessage() { result="This instruction is not reachable" }
|
||||
override string toString() { result = InstructionViolation.super.toString() }
|
||||
}
|
||||
|
||||
@@ -156,11 +161,12 @@ class InvalidBranchSuccessors extends InstructionViolation {
|
||||
InvalidBranchSuccessors() {
|
||||
// Mono compiler sometimes generates branches to the next instruction, which is just wrong.
|
||||
// However it is valid CIL.
|
||||
exists(YesNoBranch i | i = getInstruction() | not count(i.getASuccessor()) in [1..2])
|
||||
exists(YesNoBranch i | i = getInstruction() | not count(i.getASuccessor()) in [1 .. 2])
|
||||
}
|
||||
|
||||
override string getMessage() {
|
||||
result = "Conditional branch has " + count(this.getInstruction().getASuccessor()) + " successors"
|
||||
result = "Conditional branch has " + count(this.getInstruction().getASuccessor()) +
|
||||
" successors"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,7 +176,8 @@ class InvalidBranchSuccessors extends InstructionViolation {
|
||||
class OnlyYesNoBranchHasTrueFalseSuccessors extends InstructionViolation {
|
||||
OnlyYesNoBranchHasTrueFalseSuccessors() {
|
||||
exists(Instruction i | i = getInstruction() |
|
||||
(exists(i.getTrueSuccessor()) or exists(i.getFalseSuccessor())) and not i instanceof YesNoBranch
|
||||
(exists(i.getTrueSuccessor()) or exists(i.getFalseSuccessor())) and
|
||||
not i instanceof YesNoBranch
|
||||
)
|
||||
}
|
||||
|
||||
@@ -185,7 +192,9 @@ class UnconditionalBranchSuccessors extends InstructionViolation {
|
||||
exists(UnconditionalBranch i | i = getInstruction() | count(i.getASuccessor()) != 1)
|
||||
}
|
||||
|
||||
override string getMessage() { result = "Unconditional branch has " + count(getInstruction().getASuccessor()) + " successors" }
|
||||
override string getMessage() {
|
||||
result = "Unconditional branch has " + count(getInstruction().getASuccessor()) + " successors"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -206,6 +215,7 @@ class NoFalseSuccessor extends InstructionViolation {
|
||||
NoFalseSuccessor() {
|
||||
exists(YesNoBranch i | i = getInstruction() | not exists(i.getFalseSuccessor()))
|
||||
}
|
||||
|
||||
override string getMessage() { result = "Missing a false successor" }
|
||||
}
|
||||
|
||||
@@ -214,7 +224,9 @@ class NoFalseSuccessor extends InstructionViolation {
|
||||
*/
|
||||
class TrueSuccessorIsSuccessor extends InstructionViolation {
|
||||
TrueSuccessorIsSuccessor() {
|
||||
exists(Instruction i | i = getInstruction() | exists(i.getTrueSuccessor()) and not i.getTrueSuccessor() = i.getASuccessor())
|
||||
exists(Instruction i | i = getInstruction() |
|
||||
exists(i.getTrueSuccessor()) and not i.getTrueSuccessor() = i.getASuccessor()
|
||||
)
|
||||
}
|
||||
|
||||
override string getMessage() { result = "True successor isn't a successor" }
|
||||
@@ -225,7 +237,9 @@ class TrueSuccessorIsSuccessor extends InstructionViolation {
|
||||
*/
|
||||
class FalseSuccessorIsSuccessor extends InstructionViolation {
|
||||
FalseSuccessorIsSuccessor() {
|
||||
exists(Instruction i | i = getInstruction() | exists(i.getTrueSuccessor()) and not i.getTrueSuccessor() = i.getASuccessor())
|
||||
exists(Instruction i | i = getInstruction() |
|
||||
exists(i.getTrueSuccessor()) and not i.getTrueSuccessor() = i.getASuccessor()
|
||||
)
|
||||
}
|
||||
|
||||
override string getMessage() { result = "True successor isn't a successor" }
|
||||
@@ -235,9 +249,7 @@ class FalseSuccessorIsSuccessor extends InstructionViolation {
|
||||
* An access that does not have exactly one target.
|
||||
*/
|
||||
class AccessMissingTarget extends InstructionViolation {
|
||||
AccessMissingTarget() {
|
||||
exists(Access i | i = getInstruction() | count(i.getTarget()) != 1)
|
||||
}
|
||||
AccessMissingTarget() { exists(Access i | i = getInstruction() | count(i.getTarget()) != 1) }
|
||||
|
||||
override string getMessage() { result = "Access has invalid getTarget()" }
|
||||
}
|
||||
@@ -250,7 +262,7 @@ class CatchHandlerMissingType extends CfgViolation {
|
||||
exists(CatchHandler h | h = getNode() | not exists(h.getCaughtType()))
|
||||
}
|
||||
|
||||
override string getMessage () { result = "Catch handler missing caught type" }
|
||||
override string getMessage() { result = "Catch handler missing caught type" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -258,31 +270,33 @@ class CatchHandlerMissingType extends CfgViolation {
|
||||
*/
|
||||
class MissingStackSize extends CfgViolation {
|
||||
MissingStackSize() {
|
||||
exists(ControlFlowNode node | node=getNode() |
|
||||
not exists(node.getStackSizeAfter())
|
||||
or not exists(node.getStackSizeBefore())
|
||||
)
|
||||
and not getNode() instanceof DeadInstruction
|
||||
exists(ControlFlowNode node | node = getNode() |
|
||||
not exists(node.getStackSizeAfter()) or
|
||||
not exists(node.getStackSizeBefore())
|
||||
) and
|
||||
not getNode() instanceof DeadInstruction
|
||||
}
|
||||
|
||||
override string getMessage() { result = "Inconsistent stack size" }
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A CFG node that does not have exactly one stack size.
|
||||
* Disabled because inconsistent stack sizes have been observed.
|
||||
*/
|
||||
class InvalidStackSize extends CfgViolation, DisabledCheck {
|
||||
InvalidStackSize() {
|
||||
exists(ControlFlowNode node | node=getNode() |
|
||||
count(node.getStackSizeAfter()) != 1
|
||||
or count(node.getStackSizeBefore()) != 1
|
||||
)
|
||||
and not getNode() instanceof DeadInstruction
|
||||
exists(ControlFlowNode node | node = getNode() |
|
||||
count(node.getStackSizeAfter()) != 1 or
|
||||
count(node.getStackSizeBefore()) != 1
|
||||
) and
|
||||
not getNode() instanceof DeadInstruction
|
||||
}
|
||||
|
||||
override string getMessage() { result = "Inconsistent stack sizes " + count(getNode().getStackSizeBefore()) + " before and " + count(getNode().getStackSizeAfter()) + " after" }
|
||||
override string getMessage() {
|
||||
result = "Inconsistent stack sizes " + count(getNode().getStackSizeBefore()) + " before and " +
|
||||
count(getNode().getStackSizeAfter()) + " after"
|
||||
}
|
||||
|
||||
override string toString() { result = CfgViolation.super.toString() }
|
||||
}
|
||||
@@ -292,10 +306,12 @@ class InvalidStackSize extends CfgViolation, DisabledCheck {
|
||||
*/
|
||||
class InconsistentPopCount extends CfgViolation {
|
||||
InconsistentPopCount() {
|
||||
exists(ControlFlowNode node | node = getNode() | count(node.getPopCount()) !=1 )
|
||||
exists(ControlFlowNode node | node = getNode() | count(node.getPopCount()) != 1)
|
||||
}
|
||||
|
||||
override string getMessage() { result = "Cfg node has " + count(getNode().getPopCount()) + " pop counts" }
|
||||
override string getMessage() {
|
||||
result = "Cfg node has " + count(getNode().getPopCount()) + " pop counts"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -306,14 +322,17 @@ class InconsistentPushCount extends CfgViolation {
|
||||
exists(ControlFlowNode node | node = getNode() | count(node.getPushCount()) != 1)
|
||||
}
|
||||
|
||||
override string getMessage() { result = "Cfg node has " + count(getNode().getPushCount()) + " push counts" }
|
||||
override string getMessage() {
|
||||
result = "Cfg node has " + count(getNode().getPushCount()) + " push counts"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A return instruction that does not have a stack size of 0 after it.
|
||||
*/
|
||||
class InvalidReturn extends CfgViolation {
|
||||
InvalidReturn() { getNode() instanceof Return and getNode().getStackSizeAfter()!=0 }
|
||||
InvalidReturn() { getNode() instanceof Return and getNode().getStackSizeAfter() != 0 }
|
||||
|
||||
override string getMessage() { result = "Return has invalid stack size" }
|
||||
}
|
||||
|
||||
@@ -321,7 +340,8 @@ class InvalidReturn extends CfgViolation {
|
||||
* A throw instruction that does not have a stack size of 0 after it.
|
||||
*/
|
||||
class InvalidThrow extends CfgViolation {
|
||||
InvalidThrow() { getNode() instanceof Throw and getNode().getStackSizeAfter()!=0 }
|
||||
InvalidThrow() { getNode() instanceof Throw and getNode().getStackSizeAfter() != 0 }
|
||||
|
||||
override string getMessage() { result = "Throw has invalid stack size" }
|
||||
}
|
||||
|
||||
@@ -330,9 +350,10 @@ class InvalidThrow extends CfgViolation {
|
||||
*/
|
||||
class StaticFieldTarget extends InstructionViolation {
|
||||
StaticFieldTarget() {
|
||||
exists(FieldAccess i | i=getInstruction() |
|
||||
(i instanceof Opcodes::Stfld or i instanceof Opcodes::Stfld) and i.getTarget().isStatic()
|
||||
)
|
||||
exists(FieldAccess i | i = getInstruction() |
|
||||
(i instanceof Opcodes::Stfld or i instanceof Opcodes::Stfld) and
|
||||
i.getTarget().isStatic()
|
||||
)
|
||||
}
|
||||
|
||||
override string getMessage() { result = "Inconsistent static field" }
|
||||
@@ -353,12 +374,12 @@ class BranchWithoutTarget extends InstructionViolation {
|
||||
* A consistency violation in a type.
|
||||
*/
|
||||
class TypeViolation extends ConsistencyViolation, TypeCheck {
|
||||
|
||||
/** Gets the type containing the violation. */
|
||||
Type getType() { this=TypeCheck(result) }
|
||||
Type getType() { this = TypeCheck(result) }
|
||||
|
||||
override string toString() { result = getType().toString() }
|
||||
override abstract string getMessage();
|
||||
|
||||
abstract override string getMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -377,14 +398,18 @@ class TypeIsBothConstructedAndUnbound extends TypeViolation {
|
||||
*/
|
||||
class TypeParameterMismatch extends TypeViolation {
|
||||
TypeParameterMismatch() {
|
||||
getType().(ConstructedGeneric).getNumberOfTypeArguments() != getType().getUnboundType().(UnboundGeneric).getNumberOfTypeParameters()
|
||||
getType().(ConstructedGeneric).getNumberOfTypeArguments() != getType()
|
||||
.getUnboundType()
|
||||
.(UnboundGeneric)
|
||||
.getNumberOfTypeParameters()
|
||||
}
|
||||
|
||||
override string getMessage() {
|
||||
result = "Constructed type (" + getType().toStringWithTypes() + ") has " +
|
||||
getType().(ConstructedGeneric).getNumberOfTypeArguments() + " type arguments and unbound type (" +
|
||||
getType().getUnboundType().toStringWithTypes() + ") has " +
|
||||
getType().getUnboundType().(UnboundGeneric).getNumberOfTypeParameters() + " type parameters"
|
||||
getType().(ConstructedGeneric).getNumberOfTypeArguments() +
|
||||
" type arguments and unbound type (" + getType().getUnboundType().toStringWithTypes() +
|
||||
") has " + getType().getUnboundType().(UnboundGeneric).getNumberOfTypeParameters() +
|
||||
" type parameters"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -392,11 +417,11 @@ class TypeParameterMismatch extends TypeViolation {
|
||||
* A consistency violation in a method.
|
||||
*/
|
||||
class MethodViolation extends ConsistencyViolation, DeclarationCheck {
|
||||
|
||||
/** Gets the method containing the violation. */
|
||||
Method getMethod() { this = DeclarationCheck(result) }
|
||||
|
||||
override string toString() { result = getMethod().toString() }
|
||||
|
||||
override string getMessage() { none() }
|
||||
}
|
||||
|
||||
@@ -404,11 +429,16 @@ class MethodViolation extends ConsistencyViolation, DeclarationCheck {
|
||||
* A constructed method that does not match its unbound method.
|
||||
*/
|
||||
class ConstructedMethodTypeParams extends MethodViolation {
|
||||
|
||||
ConstructedMethodTypeParams() { getMethod().(ConstructedGeneric).getNumberOfTypeArguments() != getMethod().getSourceDeclaration().(UnboundGeneric).getNumberOfTypeParameters() }
|
||||
ConstructedMethodTypeParams() {
|
||||
getMethod().(ConstructedGeneric).getNumberOfTypeArguments() != getMethod()
|
||||
.getSourceDeclaration()
|
||||
.(UnboundGeneric)
|
||||
.getNumberOfTypeParameters()
|
||||
}
|
||||
|
||||
override string getMessage() {
|
||||
result = "The constructed method " + getMethod().toStringWithTypes() + " does not match unbound method " + getMethod().getSourceDeclaration().toStringWithTypes()
|
||||
result = "The constructed method " + getMethod().toStringWithTypes() +
|
||||
" does not match unbound method " + getMethod().getSourceDeclaration().toStringWithTypes()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -424,6 +454,7 @@ abstract class MissingEntityViolation extends ConsistencyViolation, MissingEntit
|
||||
*/
|
||||
class MissingObjectViolation extends MissingEntityViolation {
|
||||
MissingObjectViolation() { not exists(ObjectType o) }
|
||||
|
||||
override string getMessage() { result = "Object missing" }
|
||||
}
|
||||
|
||||
@@ -433,7 +464,10 @@ class MissingObjectViolation extends MissingEntityViolation {
|
||||
class InvalidOverride extends MethodViolation {
|
||||
InvalidOverride() {
|
||||
exists(Method base | base = getMethod().getOverriddenMethod() |
|
||||
not getMethod().getDeclaringType().getABaseType+() = getMethod().getOverriddenMethod().getDeclaringType())
|
||||
not getMethod().getDeclaringType().getABaseType+() = getMethod()
|
||||
.getOverriddenMethod()
|
||||
.getDeclaringType()
|
||||
)
|
||||
}
|
||||
|
||||
override string getMessage() { result = "Overridden method is not in a base type" }
|
||||
@@ -443,9 +477,7 @@ class InvalidOverride extends MethodViolation {
|
||||
* A pointer type that does not have a pointee type.
|
||||
*/
|
||||
class InvalidPointerType extends TypeViolation {
|
||||
InvalidPointerType() {
|
||||
exists(PointerType p | p = getType() | count(p.getReferentType()) != 1)
|
||||
}
|
||||
InvalidPointerType() { exists(PointerType p | p = getType() | count(p.getReferentType()) != 1) }
|
||||
|
||||
override string getMessage() { result = "Invalid Pointertype.getPointeeType()" }
|
||||
}
|
||||
@@ -454,9 +486,8 @@ class InvalidPointerType extends TypeViolation {
|
||||
* An array with an invalid `getElementType`.
|
||||
*/
|
||||
class ArrayTypeMissingElement extends TypeViolation {
|
||||
ArrayTypeMissingElement() {
|
||||
exists(ArrayType t | t = getType() | count(t.getElementType())!=1)
|
||||
}
|
||||
ArrayTypeMissingElement() { exists(ArrayType t | t = getType() | count(t.getElementType()) != 1) }
|
||||
|
||||
override string getMessage() { result = "Invalid ArrayType.getElementType()" }
|
||||
}
|
||||
|
||||
@@ -464,9 +495,8 @@ class ArrayTypeMissingElement extends TypeViolation {
|
||||
* An array with an invalid `getRank`.
|
||||
*/
|
||||
class ArrayTypeInvalidRank extends TypeViolation {
|
||||
ArrayTypeInvalidRank() {
|
||||
exists(ArrayType t | t = getType() | not t.getRank() > 0)
|
||||
}
|
||||
ArrayTypeInvalidRank() { exists(ArrayType t | t = getType() | not t.getRank() > 0) }
|
||||
|
||||
override string getMessage() { result = "Invalid ArrayType.getRank()" }
|
||||
}
|
||||
|
||||
@@ -474,7 +504,7 @@ class ArrayTypeInvalidRank extends TypeViolation {
|
||||
* A violation in a `Member`.
|
||||
*/
|
||||
abstract class DeclarationViolation extends ConsistencyViolation, DeclarationCheck {
|
||||
override abstract string getMessage();
|
||||
abstract override string getMessage();
|
||||
|
||||
/** Gets the member containing the potential violation. */
|
||||
Declaration getDeclaration() { this = DeclarationCheck(result) }
|
||||
@@ -487,7 +517,7 @@ abstract class DeclarationViolation extends ConsistencyViolation, DeclarationChe
|
||||
*/
|
||||
class PropertyWithNoAccessors extends DeclarationViolation {
|
||||
PropertyWithNoAccessors() {
|
||||
exists(Property p | p=getDeclaration() | not exists(p.getAnAccessor()))
|
||||
exists(Property p | p = getDeclaration() | not exists(p.getAnAccessor()))
|
||||
}
|
||||
|
||||
override string getMessage() { result = "Property has no accessors" }
|
||||
@@ -500,15 +530,14 @@ class ExprPushCount extends InstructionViolation {
|
||||
ExprPushCount() {
|
||||
this.getInstruction() instanceof Expr and
|
||||
not this.getInstruction() instanceof Opcodes::Dup and
|
||||
if
|
||||
this.getInstruction() instanceof Call
|
||||
then
|
||||
not this.getInstruction().getPushCount() in [0..1]
|
||||
else
|
||||
this.getInstruction().(Expr).getPushCount() != 1
|
||||
if this.getInstruction() instanceof Call
|
||||
then not this.getInstruction().getPushCount() in [0 .. 1]
|
||||
else this.getInstruction().(Expr).getPushCount() != 1
|
||||
}
|
||||
|
||||
override string getMessage() { result = "Instruction has unexpected push count " + this.getInstruction().getPushCount() }
|
||||
override string getMessage() {
|
||||
result = "Instruction has unexpected push count " + this.getInstruction().getPushCount()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -518,12 +547,13 @@ class ExprPushCount extends InstructionViolation {
|
||||
class ExprMissingType extends InstructionViolation {
|
||||
ExprMissingType() {
|
||||
// Don't have types for the following op codes:
|
||||
not getInstruction() instanceof Opcodes::Ldftn
|
||||
and not getInstruction() instanceof Opcodes::Localloc
|
||||
and not getInstruction() instanceof Opcodes::Ldvirtftn
|
||||
and not getInstruction() instanceof Opcodes::Arglist
|
||||
and not getInstruction() instanceof Opcodes::Refanytype
|
||||
and this.getInstruction().getPushCount() = 1 and count(this.getInstruction().getType()) != 1
|
||||
not getInstruction() instanceof Opcodes::Ldftn and
|
||||
not getInstruction() instanceof Opcodes::Localloc and
|
||||
not getInstruction() instanceof Opcodes::Ldvirtftn and
|
||||
not getInstruction() instanceof Opcodes::Arglist and
|
||||
not getInstruction() instanceof Opcodes::Refanytype and
|
||||
this.getInstruction().getPushCount() = 1 and
|
||||
count(this.getInstruction().getType()) != 1
|
||||
}
|
||||
|
||||
override string getMessage() { result = "Expression is missing getType()" }
|
||||
@@ -533,8 +563,14 @@ class ExprMissingType extends InstructionViolation {
|
||||
* An instruction that has a push count of 0, yet is still used as an operand
|
||||
*/
|
||||
class InvalidExpressionViolation extends InstructionViolation {
|
||||
InvalidExpressionViolation() { getInstruction().getPushCount()=0 and exists(Instruction expr | getInstruction() = expr.getAnOperand()) }
|
||||
override string getMessage() { result = "This instruction is used as an operand but pushes no values" }
|
||||
InvalidExpressionViolation() {
|
||||
getInstruction().getPushCount() = 0 and
|
||||
exists(Instruction expr | getInstruction() = expr.getAnOperand())
|
||||
}
|
||||
|
||||
override string getMessage() {
|
||||
result = "This instruction is used as an operand but pushes no values"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -543,21 +579,22 @@ class InvalidExpressionViolation extends InstructionViolation {
|
||||
*/
|
||||
class TypeMultiplyDefined extends TypeViolation, DisabledCheck {
|
||||
TypeMultiplyDefined() {
|
||||
this.getType().getParent().getName()="System"
|
||||
and
|
||||
not this.getType() instanceof ConstructedGeneric
|
||||
and
|
||||
not this.getType() instanceof ArrayType
|
||||
and
|
||||
this.getType().isPublic()
|
||||
and
|
||||
count(Type t | not t instanceof ConstructedGeneric and t.toStringWithTypes() = this.getType().toStringWithTypes()) != 1
|
||||
this.getType().getParent().getName() = "System" and
|
||||
not this.getType() instanceof ConstructedGeneric and
|
||||
not this.getType() instanceof ArrayType and
|
||||
this.getType().isPublic() and
|
||||
count(Type t |
|
||||
not t instanceof ConstructedGeneric and
|
||||
t.toStringWithTypes() = this.getType().toStringWithTypes()
|
||||
) != 1
|
||||
}
|
||||
|
||||
override string getMessage() {
|
||||
result="This type (" + getType().toStringWithTypes() + ") has " +
|
||||
count(Type t | not t instanceof ConstructedGeneric and t.toStringWithTypes() = this.getType().toStringWithTypes()) +
|
||||
" entities"
|
||||
result = "This type (" + getType().toStringWithTypes() + ") has " +
|
||||
count(Type t |
|
||||
not t instanceof ConstructedGeneric and
|
||||
t.toStringWithTypes() = this.getType().toStringWithTypes()
|
||||
) + " entities"
|
||||
}
|
||||
|
||||
override string toString() { result = TypeViolation.super.toString() }
|
||||
@@ -567,18 +604,19 @@ class TypeMultiplyDefined extends TypeViolation, DisabledCheck {
|
||||
* A C# declaration which is expected to have a corresponding CIL declaration, but for some reason does not.
|
||||
*/
|
||||
class MissingCilDeclaration extends ConsistencyViolation, MissingCSharpCheck {
|
||||
|
||||
MissingCilDeclaration()
|
||||
{
|
||||
MissingCilDeclaration() {
|
||||
exists(CS::Declaration decl | this = MissingCSharpCheck(decl) |
|
||||
expectedCilDeclaration(decl)
|
||||
and not exists(Declaration d | decl = d.getCSharpDeclaration())
|
||||
)
|
||||
expectedCilDeclaration(decl) and
|
||||
not exists(Declaration d | decl = d.getCSharpDeclaration())
|
||||
)
|
||||
}
|
||||
|
||||
CS::Declaration getDeclaration() { this=MissingCSharpCheck(result) }
|
||||
CS::Declaration getDeclaration() { this = MissingCSharpCheck(result) }
|
||||
|
||||
override string getMessage() { result = "Cannot locate CIL for " + getDeclaration().toStringWithTypes() + " of class " + getDeclaration().getAQlClass() }
|
||||
override string getMessage() {
|
||||
result = "Cannot locate CIL for " + getDeclaration().toStringWithTypes() + " of class " +
|
||||
getDeclaration().getAQlClass()
|
||||
}
|
||||
|
||||
override string toString() { result = getDeclaration().toStringWithTypes() }
|
||||
}
|
||||
@@ -586,20 +624,20 @@ class MissingCilDeclaration extends ConsistencyViolation, MissingCSharpCheck {
|
||||
/**
|
||||
* Holds if the C# declaration is expected to have a CIl declaration.
|
||||
*/
|
||||
private predicate expectedCilDeclaration(CS::Declaration decl)
|
||||
{
|
||||
decl=decl.getSourceDeclaration()
|
||||
and not decl instanceof CS::ArrayType
|
||||
and decl.getALocation() instanceof CS::Assembly
|
||||
and not decl.(CS::Modifiable).isInternal()
|
||||
and not decl.(CS::Constructor).getNumberOfParameters() = 0 // These are sometimes implicit
|
||||
and not decl.(CS::Method).getReturnType() instanceof CS::UnknownType
|
||||
and not exists(CS::Parameter p | p = decl.(CS::Parameterizable).getAParameter() | not expectedCilDeclaration(p))
|
||||
and not decl instanceof CS::AnonymousClass
|
||||
and (decl instanceof CS::Parameter implies expectedCilDeclaration(decl.(CS::Parameter).getType()))
|
||||
and (decl instanceof CS::Parameter implies expectedCilDeclaration(decl.getParent()))
|
||||
and (decl instanceof CS::Member implies expectedCilDeclaration(decl.getParent()))
|
||||
and
|
||||
private predicate expectedCilDeclaration(CS::Declaration decl) {
|
||||
decl = decl.getSourceDeclaration() and
|
||||
not decl instanceof CS::ArrayType and
|
||||
decl.getALocation() instanceof CS::Assembly and
|
||||
not decl.(CS::Modifiable).isInternal() and
|
||||
not decl.(CS::Constructor).getNumberOfParameters() = 0 and // These are sometimes implicit
|
||||
not decl.(CS::Method).getReturnType() instanceof CS::UnknownType and
|
||||
not exists(CS::Parameter p | p = decl.(CS::Parameterizable).getAParameter() |
|
||||
not expectedCilDeclaration(p)
|
||||
) and
|
||||
not decl instanceof CS::AnonymousClass and
|
||||
(decl instanceof CS::Parameter implies expectedCilDeclaration(decl.(CS::Parameter).getType())) and
|
||||
(decl instanceof CS::Parameter implies expectedCilDeclaration(decl.getParent())) and
|
||||
(decl instanceof CS::Member implies expectedCilDeclaration(decl.getParent())) and
|
||||
(
|
||||
decl instanceof CS::Field
|
||||
or
|
||||
@@ -625,10 +663,10 @@ private predicate expectedCilDeclaration(CS::Declaration decl)
|
||||
class MemberWithInvalidName extends DeclarationViolation {
|
||||
MemberWithInvalidName() {
|
||||
exists(string name | name = getDeclaration().(Member).getName() |
|
||||
exists(name.indexOf("."))
|
||||
and not name = ".ctor"
|
||||
and not name = ".cctor"
|
||||
)
|
||||
exists(name.indexOf(".")) and
|
||||
not name = ".ctor" and
|
||||
not name = ".cctor"
|
||||
)
|
||||
}
|
||||
|
||||
override string getMessage() { result = "Invalid name " + getDeclaration().(Member).getName() }
|
||||
@@ -638,31 +676,34 @@ class ConstructedSourceDeclarationMethod extends MethodViolation {
|
||||
Method method;
|
||||
|
||||
ConstructedSourceDeclarationMethod() {
|
||||
method = getMethod()
|
||||
and method = method.getSourceDeclaration()
|
||||
and (method instanceof ConstructedGeneric or method.getDeclaringType() instanceof ConstructedGeneric)
|
||||
method = getMethod() and
|
||||
method = method.getSourceDeclaration() and
|
||||
(
|
||||
method instanceof ConstructedGeneric or
|
||||
method.getDeclaringType() instanceof ConstructedGeneric
|
||||
)
|
||||
}
|
||||
|
||||
override string getMessage() { result= "Source declaration " + method.toStringWithTypes() + " is constructed" }
|
||||
override string getMessage() {
|
||||
result = "Source declaration " + method.toStringWithTypes() + " is constructed"
|
||||
}
|
||||
}
|
||||
|
||||
/** A declaration with multiple labels. */
|
||||
class DeclarationWithMultipleLabels extends DeclarationViolation {
|
||||
DeclarationWithMultipleLabels() {
|
||||
exists(Declaration d |
|
||||
this = DeclarationCheck(d) |
|
||||
strictcount(d.getLabel()) > 1
|
||||
)
|
||||
exists(Declaration d | this = DeclarationCheck(d) | strictcount(d.getLabel()) > 1)
|
||||
}
|
||||
|
||||
override string getMessage() { result = "Multiple labels " + concat(getDeclaration().getLabel(), ", ") }
|
||||
override string getMessage() {
|
||||
result = "Multiple labels " + concat(getDeclaration().getLabel(), ", ")
|
||||
}
|
||||
}
|
||||
|
||||
/** A declaration without a label. */
|
||||
class DeclarationWithoutLabel extends DeclarationViolation {
|
||||
DeclarationWithoutLabel() {
|
||||
exists(Declaration d |
|
||||
this = DeclarationCheck(d) |
|
||||
exists(Declaration d | this = DeclarationCheck(d) |
|
||||
d.isSourceDeclaration() and
|
||||
not d instanceof TypeParameter and
|
||||
not exists(d.getLabel()) and
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
private import CIL
|
||||
|
||||
/** A node in the control flow graph. */
|
||||
class ControlFlowNode extends @cil_controlflow_node
|
||||
{
|
||||
class ControlFlowNode extends @cil_controlflow_node {
|
||||
/** Gets a textual representation of this control flow node. */
|
||||
string toString() { none() }
|
||||
|
||||
@@ -15,22 +14,23 @@ class ControlFlowNode extends @cil_controlflow_node
|
||||
* This value is either 0 or 1, except for the instruction `dup`
|
||||
* which pushes 2 values onto the stack.
|
||||
*/
|
||||
int getPushCount() { result=0 }
|
||||
int getPushCount() { result = 0 }
|
||||
|
||||
/** Gets the number of items this node pops from the stack. */
|
||||
int getPopCount() { result=0 }
|
||||
int getPopCount() { result = 0 }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final Instruction getASuccessor() { result=getASuccessorType(_) }
|
||||
final Instruction getASuccessor() { result = getASuccessorType(_) }
|
||||
|
||||
/** Gets a true successor of this node, if any. */
|
||||
final Instruction getTrueSuccessor() { result=getASuccessorType(any(TrueFlow f)) }
|
||||
final Instruction getTrueSuccessor() { result = getASuccessorType(any(TrueFlow f)) }
|
||||
|
||||
/** Gets a false successor of this node, if any. */
|
||||
final Instruction getFalseSuccessor() { result=getASuccessorType(any(FalseFlow f)) }
|
||||
final Instruction getFalseSuccessor() { result = getASuccessorType(any(FalseFlow f)) }
|
||||
|
||||
/** Gets a successor to this node, of type `type`, if any. */
|
||||
cached Instruction getASuccessorType(FlowType t) { none() }
|
||||
cached
|
||||
Instruction getASuccessorType(FlowType t) { none() }
|
||||
|
||||
/** Gets a predecessor of this node, if any. */
|
||||
ControlFlowNode getAPredecessor() { result.getASuccessor() = this }
|
||||
@@ -47,20 +47,17 @@ class ControlFlowNode extends @cil_controlflow_node
|
||||
i < result.getPushCount()
|
||||
or
|
||||
// Transitive predecessor pushes the operand
|
||||
exists(ControlFlowNode mid, int pushes |
|
||||
this.getOperandRec(mid, i, pushes) |
|
||||
exists(ControlFlowNode mid, int pushes | this.getOperandRec(mid, i, pushes) |
|
||||
pushes - mid.getStackDelta() < result.getPushCount() and
|
||||
result = mid.getAPredecessor()
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets an operand of this instruction, if any. */
|
||||
ControlFlowNode getAnOperand() { result=getOperand(_) }
|
||||
ControlFlowNode getAnOperand() { result = getOperand(_) }
|
||||
|
||||
/** Gets an expression that consumes the output of this instruction on the stack. */
|
||||
Instruction getParentExpr() {
|
||||
this=result.getAnOperand()
|
||||
}
|
||||
Instruction getParentExpr() { this = result.getAnOperand() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` is a transitive predecessor of this instruction, this
|
||||
@@ -70,15 +67,13 @@ class ControlFlowNode extends @cil_controlflow_node
|
||||
*/
|
||||
private predicate getOperandRec(ControlFlowNode pred, int i, int pushes) {
|
||||
// Invariant: no node is a push for operand `i`
|
||||
pushes >= pred.getPushCount()
|
||||
and
|
||||
pushes >= pred.getPushCount() and
|
||||
(
|
||||
i in [0 .. this.getPopCount() - 1] and
|
||||
pred = this.getAPredecessor() and
|
||||
pushes = i
|
||||
or
|
||||
exists(ControlFlowNode mid, int pushes0 |
|
||||
this.getOperandRec(mid, i, pushes0) |
|
||||
exists(ControlFlowNode mid, int pushes0 | this.getOperandRec(mid, i, pushes0) |
|
||||
pushes = pushes0 - mid.getStackDelta() and
|
||||
// This is a guard to prevent ill formed programs
|
||||
// and other logic errors going into an infinite loop.
|
||||
@@ -88,21 +83,16 @@ class ControlFlowNode extends @cil_controlflow_node
|
||||
)
|
||||
}
|
||||
|
||||
private int getStackDelta() { result = getPushCount()-getPopCount() }
|
||||
private int getStackDelta() { result = getPushCount() - getPopCount() }
|
||||
|
||||
/** Gets the stack size before this instruction. */
|
||||
int getStackSizeBefore()
|
||||
{
|
||||
result = getAPredecessor().getStackSizeAfter()
|
||||
}
|
||||
int getStackSizeBefore() { result = getAPredecessor().getStackSizeAfter() }
|
||||
|
||||
/** Gets the stack size after this instruction. */
|
||||
final int getStackSizeAfter()
|
||||
{
|
||||
final int getStackSizeAfter() {
|
||||
// This is a guard to prevent ill formed programs
|
||||
// and other logic errors going into an infinite loop.
|
||||
result in [0..getImplementation().getStackSize()]
|
||||
and
|
||||
result in [0 .. getImplementation().getStackSize()] and
|
||||
result = getStackSizeBefore() + getStackDelta()
|
||||
}
|
||||
|
||||
@@ -110,10 +100,11 @@ class ControlFlowNode extends @cil_controlflow_node
|
||||
MethodImplementation getImplementation() { none() }
|
||||
|
||||
/** Gets the type of the item pushed onto the stack, if any. */
|
||||
cached Type getType() { none() }
|
||||
cached
|
||||
Type getType() { none() }
|
||||
|
||||
/** Holds if this control flow node has more than one predecessor. */
|
||||
predicate isJoin() { count(getAPredecessor())>1 }
|
||||
predicate isJoin() { count(getAPredecessor()) > 1 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -122,27 +113,22 @@ class ControlFlowNode extends @cil_controlflow_node
|
||||
* Handlers are control flow nodes because they push the handled exception onto the stack.
|
||||
*/
|
||||
class EntryPoint extends ControlFlowNode, @cil_entry_point {
|
||||
override int getStackSizeBefore() { result=0 }
|
||||
override int getStackSizeBefore() { result = 0 }
|
||||
}
|
||||
|
||||
private newtype TFlowType = TNormalFlow() or TTrueFlow() or TFalseFlow()
|
||||
private newtype TFlowType =
|
||||
TNormalFlow() or
|
||||
TTrueFlow() or
|
||||
TFalseFlow()
|
||||
|
||||
/** A type of control flow. Either normal flow (`NormalFlow`), true flow (`TrueFlow`) or false flow (`FalseFlow`). */
|
||||
abstract class FlowType extends TFlowType {
|
||||
abstract string toString();
|
||||
}
|
||||
abstract class FlowType extends TFlowType { abstract string toString(); }
|
||||
|
||||
/** Normal control flow. */
|
||||
class NormalFlow extends FlowType, TNormalFlow {
|
||||
override string toString() { result="" }
|
||||
}
|
||||
class NormalFlow extends FlowType, TNormalFlow { override string toString() { result = "" } }
|
||||
|
||||
/** True control flow. */
|
||||
class TrueFlow extends FlowType, TTrueFlow {
|
||||
override string toString() { result="true" }
|
||||
}
|
||||
class TrueFlow extends FlowType, TTrueFlow { override string toString() { result = "true" } }
|
||||
|
||||
/** False control flow. */
|
||||
class FalseFlow extends FlowType, TTrueFlow {
|
||||
override string toString() { result="false" }
|
||||
}
|
||||
class FalseFlow extends FlowType, TTrueFlow { override string toString() { result = "false" } }
|
||||
|
||||
@@ -21,19 +21,15 @@ class DataFlowNode extends @cil_dataflow_node {
|
||||
* `tt` is the tainting that occurs during this step.
|
||||
*/
|
||||
predicate getALocalFlowSucc(DataFlowNode sink, TaintType tt) {
|
||||
localExactStep(this, sink) and tt=TExactValue()
|
||||
localExactStep(this, sink) and tt = TExactValue()
|
||||
or
|
||||
localTaintStep(this, sink) and tt=TTaintedValue()
|
||||
localTaintStep(this, sink) and tt = TTaintedValue()
|
||||
}
|
||||
|
||||
private predicate flowsToStep(DataFlowNode sink) {
|
||||
this.getALocalFlowSucc(sink, TExactValue())
|
||||
}
|
||||
private predicate flowsToStep(DataFlowNode sink) { this.getALocalFlowSucc(sink, TExactValue()) }
|
||||
|
||||
/** Holds if this node flows to `sink` in zero or more steps. */
|
||||
predicate flowsTo(DataFlowNode sink) {
|
||||
this.flowsToStep*(sink)
|
||||
}
|
||||
predicate flowsTo(DataFlowNode sink) { this.flowsToStep*(sink) }
|
||||
|
||||
/** Gets the method that contains this dataflow node. */
|
||||
Method getMethod() { none() }
|
||||
@@ -49,57 +45,68 @@ private newtype TTaintType =
|
||||
/** Describes how data is tainted. */
|
||||
class TaintType extends TTaintType {
|
||||
string toString() {
|
||||
this=TExactValue() and result = "exact"
|
||||
or this=TTaintedValue() and result = "tainted"
|
||||
this = TExactValue() and result = "exact"
|
||||
or
|
||||
this = TTaintedValue() and result = "tainted"
|
||||
}
|
||||
}
|
||||
|
||||
/** A taint type where the data is untainted. */
|
||||
class Untainted extends TaintType, TExactValue {
|
||||
}
|
||||
class Untainted extends TaintType, TExactValue { }
|
||||
|
||||
/** A taint type where the data is tainted. */
|
||||
class Tainted extends TaintType, TTaintedValue {
|
||||
}
|
||||
class Tainted extends TaintType, TTaintedValue { }
|
||||
|
||||
private predicate localExactStep(DataFlowNode src, DataFlowNode sink) {
|
||||
src = sink.(Opcodes::Dup).getAnOperand()
|
||||
or DefUse::defUse(_, src, sink)
|
||||
or src=sink.(ParameterReadAccess).getTarget()
|
||||
or src=sink.(Conversion).getExpr()
|
||||
or src=sink.(WriteAccess).getExpr()
|
||||
or src=sink.(Method).getAnImplementation().getAnInstruction().(Return)
|
||||
or src=sink.(Return).getExpr()
|
||||
or src=sink.(ConditionalBranch).getAnOperand()
|
||||
or src=sink.(Parameter).getAWrite()
|
||||
or exists(VariableUpdate update | update.getVariable().(Parameter)=sink and src=update.getSource())
|
||||
or
|
||||
DefUse::defUse(_, src, sink)
|
||||
or
|
||||
src = sink.(ParameterReadAccess).getTarget()
|
||||
or
|
||||
src = sink.(Conversion).getExpr()
|
||||
or
|
||||
src = sink.(WriteAccess).getExpr()
|
||||
or
|
||||
src = sink.(Method).getAnImplementation().getAnInstruction().(Return)
|
||||
or
|
||||
src = sink.(Return).getExpr()
|
||||
or
|
||||
src = sink.(ConditionalBranch).getAnOperand()
|
||||
or
|
||||
src = sink.(Parameter).getAWrite()
|
||||
or
|
||||
exists(VariableUpdate update |
|
||||
update.getVariable().(Parameter) = sink and src = update.getSource()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate localTaintStep(DataFlowNode src, DataFlowNode sink) {
|
||||
src=sink.(BinaryArithmeticExpr).getAnOperand()
|
||||
or src=sink.(Opcodes::Neg).getOperand()
|
||||
or src=sink.(UnaryBitwiseOperation).getOperand()
|
||||
src = sink.(BinaryArithmeticExpr).getAnOperand() or
|
||||
src = sink.(Opcodes::Neg).getOperand() or
|
||||
src = sink.(UnaryBitwiseOperation).getOperand()
|
||||
}
|
||||
|
||||
private cached module DefUse {
|
||||
cached
|
||||
private module DefUse {
|
||||
/**
|
||||
* A classification of variable references into reads and writes.
|
||||
*/
|
||||
private newtype RefKind = Read() or Write()
|
||||
private newtype RefKind =
|
||||
Read() or
|
||||
Write()
|
||||
|
||||
/**
|
||||
* Holds if the `i`th node of basic block `bb` is a reference to `v`,
|
||||
* either a read (when `k` is `Read()`) or a write (when `k` is `Write()`).
|
||||
*/
|
||||
private predicate ref(BasicBlock bb, int i, StackVariable v, RefKind k) {
|
||||
exists(ReadAccess ra |
|
||||
bb.getNode(i) = ra |
|
||||
exists(ReadAccess ra | bb.getNode(i) = ra |
|
||||
ra.getTarget() = v and
|
||||
k = Read()
|
||||
)
|
||||
or
|
||||
exists(VariableUpdate vu |
|
||||
bb.getNode(i) = vu |
|
||||
exists(VariableUpdate vu | bb.getNode(i) = vu |
|
||||
vu.getVariable() = v and
|
||||
k = Write()
|
||||
)
|
||||
@@ -156,10 +163,7 @@ private cached module DefUse {
|
||||
private predicate defReachesEndOfBlock(BasicBlock bb, VariableUpdate vu, StackVariable v) {
|
||||
liveAtExit(bb, v) and
|
||||
(
|
||||
exists(int last |
|
||||
last = max(refRank(bb, _, v, _)) |
|
||||
defReachesRank(bb, vu, last, v)
|
||||
)
|
||||
exists(int last | last = max(refRank(bb, _, v, _)) | defReachesRank(bb, vu, last, v))
|
||||
or
|
||||
exists(BasicBlock pred |
|
||||
pred = bb.getAPredecessor() and
|
||||
@@ -184,8 +188,7 @@ private cached module DefUse {
|
||||
/** Holds if the update `def` can be used at the read `use`. */
|
||||
cached
|
||||
predicate defUse(StackVariable target, DataFlowNode def, ReadAccess use) {
|
||||
exists(VariableUpdate vu |
|
||||
def = vu.getSource() |
|
||||
exists(VariableUpdate vu | def = vu.getSource() |
|
||||
defReachesReadWithinBlock(target, vu, use)
|
||||
or
|
||||
exists(BasicBlock bb, int i |
|
||||
@@ -198,26 +201,26 @@ private cached module DefUse {
|
||||
}
|
||||
}
|
||||
|
||||
library abstract class VariableUpdate extends Instruction {
|
||||
abstract library class VariableUpdate extends Instruction {
|
||||
abstract Expr getSource();
|
||||
|
||||
abstract Variable getVariable();
|
||||
}
|
||||
|
||||
private class VariableWrite extends VariableUpdate, WriteAccess {
|
||||
override Expr getSource() { result=getExpr() }
|
||||
override Variable getVariable() { result=getTarget() }
|
||||
override Expr getSource() { result = getExpr() }
|
||||
|
||||
override Variable getVariable() { result = getTarget() }
|
||||
}
|
||||
|
||||
private class MethodOutOrRefTarget extends VariableUpdate, Call {
|
||||
int parameterIndex;
|
||||
|
||||
MethodOutOrRefTarget() {
|
||||
this.getTarget().getRawParameter(parameterIndex).hasOutFlag()
|
||||
}
|
||||
MethodOutOrRefTarget() { this.getTarget().getRawParameter(parameterIndex).hasOutFlag() }
|
||||
|
||||
override Variable getVariable() {
|
||||
result=this.getRawArgument(parameterIndex).(ReadAccess).getTarget()
|
||||
result = this.getRawArgument(parameterIndex).(ReadAccess).getTarget()
|
||||
}
|
||||
|
||||
override Expr getSource() { result=this }
|
||||
override Expr getSource() { result = this }
|
||||
}
|
||||
|
||||
@@ -10,9 +10,8 @@ private import semmle.code.csharp.Member as CS
|
||||
* A declaration. Either a member (`Member`) or a variable (`Variable`).
|
||||
*/
|
||||
class Declaration extends DotNet::Declaration, Element, @cil_declaration {
|
||||
|
||||
/** Gets an attribute (for example `[Obsolete]`) of this declaration, if any. */
|
||||
Attribute getAnAttribute() { result.getDeclaration()=this }
|
||||
Attribute getAnAttribute() { result.getDeclaration() = this }
|
||||
|
||||
/**
|
||||
* Gets the C# declaration corresponding to this CIL declaration, if any.
|
||||
@@ -23,21 +22,19 @@ class Declaration extends DotNet::Declaration, Element, @cil_declaration {
|
||||
result = toCSharpTypeParameter(this)
|
||||
}
|
||||
|
||||
override Declaration getSourceDeclaration() { result=this }
|
||||
override Declaration getSourceDeclaration() { result = this }
|
||||
|
||||
/** Holds if this declaration is a source declaration. */
|
||||
final predicate isSourceDeclaration() { this=getSourceDeclaration() }
|
||||
final predicate isSourceDeclaration() { this = getSourceDeclaration() }
|
||||
}
|
||||
|
||||
private CS::Declaration toCSharpNonTypeParameter(Declaration d) {
|
||||
result.getLabel() = d.getLabel()
|
||||
}
|
||||
private CS::Declaration toCSharpNonTypeParameter(Declaration d) { result.getLabel() = d.getLabel() }
|
||||
|
||||
private CS::TypeParameter toCSharpTypeParameter(TypeParameter tp) {
|
||||
toCSharpTypeParameterJoin(tp, result.getIndex(), result.getGeneric())
|
||||
}
|
||||
|
||||
pragma [noinline, nomagic]
|
||||
pragma[noinline, nomagic]
|
||||
private predicate toCSharpTypeParameterJoin(TypeParameter tp, int i, CS::UnboundGeneric ug) {
|
||||
exists(TypeContainer tc |
|
||||
tp.getIndex() = i and
|
||||
@@ -87,17 +84,20 @@ class Property extends DotNet::Property, Member, @cil_property {
|
||||
override ValueOrRefType getDeclaringType() { cil_property(this, result, _, _) }
|
||||
|
||||
/** Gets the getter of this property, if any. */
|
||||
override Getter getGetter() { this=result.getProperty() }
|
||||
override Getter getGetter() { this = result.getProperty() }
|
||||
|
||||
/** Gets the setter of this property, if any. */
|
||||
override Setter getSetter() { this=result.getProperty() }
|
||||
override Setter getSetter() { this = result.getProperty() }
|
||||
|
||||
/** Gets an accessor of this property. */
|
||||
Accessor getAnAccessor() { result=getGetter() or result=getSetter() }
|
||||
Accessor getAnAccessor() { result = getGetter() or result = getSetter() }
|
||||
|
||||
override string toString() { result="property " + getName() }
|
||||
override string toString() { result = "property " + getName() }
|
||||
|
||||
override string toStringWithTypes() { result=getType().toStringWithTypes() + " " + getDeclaringType().toStringWithTypes() + "." + getName() }
|
||||
override string toStringWithTypes() {
|
||||
result = getType().toStringWithTypes() + " " + getDeclaringType().toStringWithTypes() + "." +
|
||||
getName()
|
||||
}
|
||||
}
|
||||
|
||||
/** A property that is trivial (wraps a field). */
|
||||
@@ -107,16 +107,16 @@ class TrivialProperty extends Property {
|
||||
}
|
||||
|
||||
/** Gets the underlying field of this property. */
|
||||
Field getField() { result=getGetter().(TrivialGetter).getField() }
|
||||
Field getField() { result = getGetter().(TrivialGetter).getField() }
|
||||
}
|
||||
|
||||
/** An event. */
|
||||
class Event extends DotNet::Event, Member, @cil_event
|
||||
{
|
||||
class Event extends DotNet::Event, Member, @cil_event {
|
||||
override string getName() { cil_event(this, _, result, _) }
|
||||
|
||||
/** Gets the type of this event. */
|
||||
Type getType() { cil_event(this, _, _, result) }
|
||||
|
||||
override ValueOrRefType getDeclaringType() { cil_event(this, result, _, _) }
|
||||
|
||||
/** Gets the add event accessor. */
|
||||
@@ -128,7 +128,9 @@ class Event extends DotNet::Event, Member, @cil_event
|
||||
/** Gets the raiser. */
|
||||
Method getRaiser() { cil_raiser(this, result) }
|
||||
|
||||
override string toString() { result="event " + getName() }
|
||||
override string toString() { result = "event " + getName() }
|
||||
|
||||
override string toStringWithTypes() { result=getDeclaringType().toStringWithTypes() + "." + getName() }
|
||||
override string toStringWithTypes() {
|
||||
result = getDeclaringType().toStringWithTypes() + "." + getName()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/** Provides classes for generic types and methods. */
|
||||
|
||||
private import CIL
|
||||
private import dotnet
|
||||
|
||||
@@ -15,24 +16,16 @@ class Generic extends DotNet::Generic, Declaration, TypeContainer {
|
||||
|
||||
/** An unbound generic type or method. */
|
||||
class UnboundGeneric extends Generic, DotNet::UnboundGeneric {
|
||||
UnboundGeneric() {
|
||||
cil_type_parameter(this, _, _)
|
||||
}
|
||||
UnboundGeneric() { cil_type_parameter(this, _, _) }
|
||||
|
||||
final override TypeParameter getTypeParameter(int n) {
|
||||
cil_type_parameter(this, n, result)
|
||||
}
|
||||
final override TypeParameter getTypeParameter(int n) { cil_type_parameter(this, n, result) }
|
||||
}
|
||||
|
||||
/** A constructed generic type or method. */
|
||||
class ConstructedGeneric extends Generic, DotNet::ConstructedGeneric {
|
||||
ConstructedGeneric() {
|
||||
cil_type_argument(this, _, _)
|
||||
}
|
||||
ConstructedGeneric() { cil_type_argument(this, _, _) }
|
||||
|
||||
final override Type getTypeArgument(int n) {
|
||||
cil_type_argument(this, n, result)
|
||||
}
|
||||
final override Type getTypeArgument(int n) { cil_type_argument(this, n, result) }
|
||||
}
|
||||
|
||||
/** An unbound generic type. */
|
||||
@@ -48,7 +41,5 @@ class ConstructedType extends ConstructedGeneric, Type { }
|
||||
class ConstructedMethod extends ConstructedGeneric, Method {
|
||||
final override UnboundGenericMethod getUnboundGeneric() { result = getUnboundMethod() }
|
||||
|
||||
final override Location getLocation() {
|
||||
result = getUnboundGeneric().getLocation()
|
||||
}
|
||||
final override Location getLocation() { result = getUnboundGeneric().getLocation() }
|
||||
}
|
||||
|
||||
@@ -19,70 +19,65 @@ private import CIL
|
||||
* Either a finally handler (`FinallyHandler`), filter handler (`FilterHandler`),
|
||||
* catch handler (`CatchHandler`), or a fault handler (`FaultHandler`).
|
||||
*/
|
||||
class Handler extends Element, EntryPoint, @cil_handler
|
||||
{
|
||||
override MethodImplementation getImplementation() { cil_handler(this,result,_,_,_,_,_) }
|
||||
class Handler extends Element, EntryPoint, @cil_handler {
|
||||
override MethodImplementation getImplementation() { cil_handler(this, result, _, _, _, _, _) }
|
||||
|
||||
/** Gets the 0-based index of this handler. Handlers are evaluated in this sequence. */
|
||||
int getIndex() { cil_handler(this, _, result, _, _, _, _) }
|
||||
|
||||
/** Gets the first instruction in the `try` block of this handler. */
|
||||
Instruction getTryStart() { cil_handler(this,_,_,_,result,_,_) }
|
||||
Instruction getTryStart() { cil_handler(this, _, _, _, result, _, _) }
|
||||
|
||||
/** Gets the last instruction in the `try` block of this handler. */
|
||||
Instruction getTryEnd() { cil_handler(this,_,_,_,_,result,_) }
|
||||
Instruction getTryEnd() { cil_handler(this, _, _, _, _, result, _) }
|
||||
|
||||
/** Gets the first instruction in the `catch`/`finally` block. */
|
||||
Instruction getHandlerStart() { cil_handler(this,_,_,_,_,_,result) }
|
||||
Instruction getHandlerStart() { cil_handler(this, _, _, _, _, _, result) }
|
||||
|
||||
/**
|
||||
* Holds if the instruction `i` is in the scope of this handler.
|
||||
*/
|
||||
predicate isInScope(Instruction i) {
|
||||
i.getImplementation() = getImplementation()
|
||||
and
|
||||
i.getIndex() in [getTryStart().getIndex()..getTryEnd().getIndex()]
|
||||
i.getImplementation() = getImplementation() and
|
||||
i.getIndex() in [getTryStart().getIndex() .. getTryEnd().getIndex()]
|
||||
}
|
||||
|
||||
override string toString() { none() }
|
||||
|
||||
override Instruction getASuccessorType(FlowType t) {
|
||||
result = getHandlerStart()
|
||||
and
|
||||
result = getHandlerStart() and
|
||||
t instanceof NormalFlow
|
||||
}
|
||||
|
||||
/** Gets the type of the caught exception, if any. */
|
||||
Type getCaughtType() { cil_handler_type(this,result) }
|
||||
Type getCaughtType() { cil_handler_type(this, result) }
|
||||
|
||||
override Location getLocation() { result=getTryStart().getLocation() }
|
||||
override Location getLocation() { result = getTryStart().getLocation() }
|
||||
}
|
||||
|
||||
/** A handler corresponding to a `finally` block. */
|
||||
class FinallyHandler extends Handler, @cil_finally_handler
|
||||
{
|
||||
class FinallyHandler extends Handler, @cil_finally_handler {
|
||||
override string toString() { result = "finally {...}" }
|
||||
}
|
||||
|
||||
/** A handler corresponding to a `where()` clause. */
|
||||
class FilterHandler extends Handler, @cil_filter_handler
|
||||
{
|
||||
override string toString() { result="where (...)" }
|
||||
class FilterHandler extends Handler, @cil_filter_handler {
|
||||
override string toString() { result = "where (...)" }
|
||||
|
||||
/** Gets the filter clause - the start of a sequence of instructions to evaluate the filter function. */
|
||||
Instruction getFilterClause() { cil_handler_filter(this, result) }
|
||||
override int getPushCount() { result=1 }
|
||||
|
||||
override int getPushCount() { result = 1 }
|
||||
}
|
||||
|
||||
/** A handler corresponding to a `catch` clause. */
|
||||
class CatchHandler extends Handler, @cil_catch_handler
|
||||
{
|
||||
override string toString() { result="catch(" + getCaughtType().getName() + ") {...}" }
|
||||
override int getPushCount() { result=1 }
|
||||
class CatchHandler extends Handler, @cil_catch_handler {
|
||||
override string toString() { result = "catch(" + getCaughtType().getName() + ") {...}" }
|
||||
|
||||
override int getPushCount() { result = 1 }
|
||||
}
|
||||
|
||||
/** A handler for memory faults. */
|
||||
class FaultHandler extends Handler, @cil_fault_handler
|
||||
{
|
||||
override string toString() { result="fault {...}" }
|
||||
class FaultHandler extends Handler, @cil_fault_handler {
|
||||
override string toString() { result = "fault {...}" }
|
||||
}
|
||||
|
||||
@@ -3,16 +3,13 @@
|
||||
private import CIL
|
||||
|
||||
/** An instruction. */
|
||||
class Instruction extends Element, ControlFlowNode, DataFlowNode, @cil_instruction
|
||||
{
|
||||
override string toString() { result=getIndex() + ": " + getOpcodeName() + getExtraStr() }
|
||||
class Instruction extends Element, ControlFlowNode, DataFlowNode, @cil_instruction {
|
||||
override string toString() { result = getIndex() + ": " + getOpcodeName() + getExtraStr() }
|
||||
|
||||
/** Gets the method containing this instruction. */
|
||||
override MethodImplementation getImplementation() {
|
||||
cil_instruction(this, _, _, result)
|
||||
}
|
||||
override MethodImplementation getImplementation() { cil_instruction(this, _, _, result) }
|
||||
|
||||
override Method getMethod() { result=getImplementation().getMethod() }
|
||||
override Method getMethod() { result = getImplementation().getMethod() }
|
||||
|
||||
/**
|
||||
* Gets the index of this instruction.
|
||||
@@ -21,7 +18,7 @@ class Instruction extends Element, ControlFlowNode, DataFlowNode, @cil_instructi
|
||||
int getIndex() { cil_instruction(this, _, result, _) }
|
||||
|
||||
/** Gets the opcode of this instruction. */
|
||||
final int getOpcode() { cil_instruction(this,result,_,_) }
|
||||
final int getOpcode() { cil_instruction(this, result, _, _) }
|
||||
|
||||
/** Gets the opcode name of this instruction, for example `ldnull`. */
|
||||
string getOpcodeName() { none() }
|
||||
@@ -30,9 +27,7 @@ class Instruction extends Element, ControlFlowNode, DataFlowNode, @cil_instructi
|
||||
string getExtra() { none() }
|
||||
|
||||
private string getExtraStr() {
|
||||
if exists(getExtra())
|
||||
then result = " " + getExtra()
|
||||
else result = ""
|
||||
if exists(getExtra()) then result = " " + getExtra() else result = ""
|
||||
}
|
||||
|
||||
/** Gets the declaration accessed by this instruction, if any. */
|
||||
@@ -40,11 +35,9 @@ class Instruction extends Element, ControlFlowNode, DataFlowNode, @cil_instructi
|
||||
|
||||
/** Gets a successor instruction to this instruction. */
|
||||
override Instruction getASuccessorType(FlowType t) {
|
||||
t instanceof NormalFlow
|
||||
and
|
||||
canFlowNext()
|
||||
and
|
||||
result = this.getImplementation().getInstruction(getIndex()+1)
|
||||
t instanceof NormalFlow and
|
||||
canFlowNext() and
|
||||
result = this.getImplementation().getInstruction(getIndex() + 1)
|
||||
}
|
||||
|
||||
/** Holds if this instruction passes control flow into the next instruction. */
|
||||
@@ -54,22 +47,20 @@ class Instruction extends Element, ControlFlowNode, DataFlowNode, @cil_instructi
|
||||
* Gets the `i`th handler that applies to this instruction.
|
||||
* Indexed from 0.
|
||||
*/
|
||||
Handler getHandler(int i)
|
||||
{
|
||||
result.isInScope(this)
|
||||
and
|
||||
result.getIndex() = rank[i+1](int hi | exists(Handler h | h.isInScope(this) and hi=h.getIndex()))
|
||||
Handler getHandler(int i) {
|
||||
result.isInScope(this) and
|
||||
result.getIndex() = rank[i + 1](int hi |
|
||||
exists(Handler h | h.isInScope(this) and hi = h.getIndex())
|
||||
)
|
||||
}
|
||||
|
||||
override Type getType() { result=ControlFlowNode.super.getType() }
|
||||
override Type getType() { result = ControlFlowNode.super.getType() }
|
||||
|
||||
override Location getALocation() {
|
||||
cil_instruction_location(this, result) // The source code, if available
|
||||
cil_instruction_location(this, result) // The source code, if available
|
||||
or
|
||||
result = getImplementation().getLocation() // The containing assembly
|
||||
result = getImplementation().getLocation() // The containing assembly
|
||||
}
|
||||
|
||||
override Location getLocation() {
|
||||
result = Element.super.getLocation()
|
||||
}
|
||||
override Location getLocation() { result = Element.super.getLocation() }
|
||||
}
|
||||
|
||||
@@ -10,49 +10,56 @@ private import dotnet
|
||||
* An instruction that pushes a value onto the stack.
|
||||
*/
|
||||
class Expr extends DotNet::Expr, Instruction, @cil_expr {
|
||||
override int getPushCount() { result=1 }
|
||||
override Type getType() { result=Instruction.super.getType() }
|
||||
override Method getEnclosingCallable() { result=getImplementation().getMethod() }
|
||||
override int getPushCount() { result = 1 }
|
||||
|
||||
override Type getType() { result = Instruction.super.getType() }
|
||||
|
||||
override Method getEnclosingCallable() { result = getImplementation().getMethod() }
|
||||
|
||||
/**
|
||||
* The "parent" of a CIL expression is taken to be the instruction
|
||||
* that consumes the value pushed by this instruction.
|
||||
*/
|
||||
override Expr getParent() { this=result.getAnOperand() }
|
||||
override Expr getParent() { this = result.getAnOperand() }
|
||||
}
|
||||
|
||||
/** An instruction that changes control flow. */
|
||||
class Branch extends Instruction, @cil_jump {
|
||||
/** Gets the instruction that is jumped to. */
|
||||
Instruction getTarget() { cil_jump(this, result) }
|
||||
override string getExtra() { result=getTarget().getIndex() + ":" }
|
||||
|
||||
override string getExtra() { result = getTarget().getIndex() + ":" }
|
||||
}
|
||||
|
||||
/** An instruction that unconditionally jumps to another instruction. */
|
||||
class UnconditionalBranch extends Branch, @cil_unconditional_jump {
|
||||
override Instruction getASuccessorType(FlowType t) { t instanceof NormalFlow and result=getTarget() }
|
||||
override Instruction getASuccessorType(FlowType t) {
|
||||
t instanceof NormalFlow and result = getTarget()
|
||||
}
|
||||
|
||||
override predicate canFlowNext() { none() }
|
||||
}
|
||||
|
||||
/** An instruction that jumps to a target based on a condition. */
|
||||
class ConditionalBranch extends Branch, @cil_conditional_jump {
|
||||
override Instruction getASuccessorType(FlowType t) {
|
||||
t instanceof TrueFlow and result = getTarget() or
|
||||
t instanceof FalseFlow and result = getImplementation().getInstruction(getIndex()+1)
|
||||
t instanceof TrueFlow and result = getTarget()
|
||||
or
|
||||
t instanceof FalseFlow and result = getImplementation().getInstruction(getIndex() + 1)
|
||||
}
|
||||
override int getPushCount() { result=0 }
|
||||
|
||||
override int getPushCount() { result = 0 }
|
||||
}
|
||||
|
||||
/** An expression with two operands. */
|
||||
class BinaryExpr extends Expr, @cil_binary_expr {
|
||||
override int getPopCount() { result=2 }
|
||||
}
|
||||
class BinaryExpr extends Expr, @cil_binary_expr { override int getPopCount() { result = 2 } }
|
||||
|
||||
/** An expression with one operand. */
|
||||
class UnaryExpr extends Expr, @cil_unary_expr {
|
||||
override int getPopCount() { result=1 }
|
||||
override int getPopCount() { result = 1 }
|
||||
|
||||
/** Gets the operand of this unary expression. */
|
||||
Expr getOperand() { result=getOperand(0) }
|
||||
Expr getOperand() { result = getOperand(0) }
|
||||
}
|
||||
|
||||
/** A binary expression that compares two values. */
|
||||
@@ -63,10 +70,16 @@ class ComparisonOperation extends BinaryExpr, @cil_comparison_operation {
|
||||
/** A binary arithmetic expression. */
|
||||
class BinaryArithmeticExpr extends BinaryExpr, @cil_binary_arithmetic_operation {
|
||||
override Type getType() {
|
||||
exists(Type t0, Type t1 | t0=getOperand(0).getType().getUnderlyingType() and t1=getOperand(1).getType().getUnderlyingType() |
|
||||
t0=t1 and result=t0 or
|
||||
t0.getConversionIndex() < t1.getConversionIndex() and result=t1 or
|
||||
t0.getConversionIndex() > t1.getConversionIndex() and result=t0)
|
||||
exists(Type t0, Type t1 |
|
||||
t0 = getOperand(0).getType().getUnderlyingType() and
|
||||
t1 = getOperand(1).getType().getUnderlyingType()
|
||||
|
|
||||
t0 = t1 and result = t0
|
||||
or
|
||||
t0.getConversionIndex() < t1.getConversionIndex() and result = t1
|
||||
or
|
||||
t0.getConversionIndex() > t1.getConversionIndex() and result = t0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,16 +98,14 @@ class UnaryBitwiseOperation extends UnaryExpr, @cil_unary_bitwise_operation {
|
||||
/** A unary expression that converts a value from one primitive type to another. */
|
||||
class Conversion extends UnaryExpr, @cil_conversion_operation {
|
||||
/** Gets the expression being converted. */
|
||||
Expr getExpr() { result=getOperand(0) }
|
||||
Expr getExpr() { result = getOperand(0) }
|
||||
}
|
||||
|
||||
/** A branch that leaves the scope of a `Handler`. */
|
||||
class Leave extends UnconditionalBranch, @cil_leave_any {
|
||||
}
|
||||
class Leave extends UnconditionalBranch, @cil_leave_any { }
|
||||
|
||||
/** An expression that pushes a literal value onto the stack. */
|
||||
class Literal extends DotNet::Literal, Expr, @cil_literal
|
||||
{
|
||||
class Literal extends DotNet::Literal, Expr, @cil_literal {
|
||||
/** Gets the pushed value. */
|
||||
override string getValue() { cil_value(this, result) }
|
||||
|
||||
@@ -104,73 +115,74 @@ class Literal extends DotNet::Literal, Expr, @cil_literal
|
||||
/** An integer literal. */
|
||||
class IntLiteral extends Literal, @cil_ldc_i {
|
||||
override string getExtra() { none() }
|
||||
|
||||
override IntType getType() { exists(result) }
|
||||
}
|
||||
|
||||
/** An expression that pushes a `float`/`Single`. */
|
||||
class FloatLiteral extends Literal, @cil_ldc_r {
|
||||
}
|
||||
class FloatLiteral extends Literal, @cil_ldc_r { }
|
||||
|
||||
/** An expression that pushes a `null` value onto the stack. */
|
||||
class NullLiteral extends Literal, @cil_ldnull {
|
||||
}
|
||||
class NullLiteral extends Literal, @cil_ldnull { }
|
||||
|
||||
/** A branch with one operand. */
|
||||
class UnaryBranch extends ConditionalBranch, @cil_unary_jump {
|
||||
override int getPopCount() { result=1 }
|
||||
override int getPushCount() { result=0 }
|
||||
override int getPopCount() { result = 1 }
|
||||
|
||||
override int getPushCount() { result = 0 }
|
||||
}
|
||||
|
||||
/** A branch with two operands. */
|
||||
class BinaryBranch extends ConditionalBranch, @cil_binary_jump {
|
||||
override int getPopCount() { result=2 }
|
||||
override int getPushCount() { result=0 }
|
||||
override int getPopCount() { result = 2 }
|
||||
|
||||
override int getPushCount() { result = 0 }
|
||||
}
|
||||
|
||||
/** A call. */
|
||||
class Call extends Expr, DotNet::Call, @cil_call_any {
|
||||
|
||||
/** Gets the method that is called. */
|
||||
override Method getTarget() { cil_access(this, result) }
|
||||
|
||||
override Method getARuntimeTarget() {
|
||||
result = getTarget().getAnOverrider*()
|
||||
}
|
||||
override Method getARuntimeTarget() { result = getTarget().getAnOverrider*() }
|
||||
|
||||
override string getExtra() { result=getTarget().getQualifiedName() }
|
||||
override string getExtra() { result = getTarget().getQualifiedName() }
|
||||
|
||||
/**
|
||||
* Gets the return type of the call. Methods that do not return a value
|
||||
* return the `void` type, `System.Void`, although the value of `getPushCount` is
|
||||
* 0 in this case.
|
||||
*/
|
||||
override Type getType() { result=getTarget().getReturnType() }
|
||||
override Type getType() { result = getTarget().getReturnType() }
|
||||
|
||||
// The number of items popped/pushed from the stack
|
||||
// depends on the target of the call.
|
||||
override int getPopCount() { result=getTarget().getCallPopCount() }
|
||||
override int getPushCount() { result=getTarget().getCallPushCount() }
|
||||
override int getPopCount() { result = getTarget().getCallPopCount() }
|
||||
|
||||
override int getPushCount() { result = getTarget().getCallPushCount() }
|
||||
|
||||
/**
|
||||
* Holds if this is a "tail call", meaning that control does not return to the
|
||||
* calling method.
|
||||
*/
|
||||
predicate isTailCall() {
|
||||
getImplementation().getInstruction(getIndex()-1) instanceof Opcodes::Tail
|
||||
getImplementation().getInstruction(getIndex() - 1) instanceof Opcodes::Tail
|
||||
}
|
||||
|
||||
/** Holds if this call is virtual and could go to an overriding method. */
|
||||
predicate isVirtual() { none() }
|
||||
|
||||
override Expr getRawArgument(int i) { result=getOperand(getPopCount() - i - 1) }
|
||||
override Expr getRawArgument(int i) { result = getOperand(getPopCount() - i - 1) }
|
||||
|
||||
/** Gets the qualifier of this call, if any. */
|
||||
Expr getQualifier() { result=getRawArgument(0) and not getTarget().isStatic() }
|
||||
Expr getQualifier() { result = getRawArgument(0) and not getTarget().isStatic() }
|
||||
|
||||
override Expr getArgument(int i) {
|
||||
if getTarget().isStatic()
|
||||
then result=getRawArgument(i)
|
||||
else (result=getRawArgument(i+1) and i>=0)
|
||||
then result = getRawArgument(i)
|
||||
else (
|
||||
result = getRawArgument(i + 1) and i >= 0
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getArgumentForParameter(DotNet::Parameter param) {
|
||||
@@ -183,24 +195,20 @@ class Call extends Expr, DotNet::Call, @cil_call_any {
|
||||
/** A tail call. */
|
||||
class TailCall extends Call {
|
||||
TailCall() { this.isTailCall() }
|
||||
|
||||
override predicate canFlowNext() { none() }
|
||||
}
|
||||
|
||||
/** A call to a static target. */
|
||||
class StaticCall extends Call {
|
||||
StaticCall() { not this.isVirtual() }
|
||||
}
|
||||
class StaticCall extends Call { StaticCall() { not this.isVirtual() } }
|
||||
|
||||
/** A call to a virtual target. */
|
||||
class VirtualCall extends Call {
|
||||
VirtualCall() { this.isVirtual() }
|
||||
}
|
||||
class VirtualCall extends Call { VirtualCall() { this.isVirtual() } }
|
||||
|
||||
/** A read of an array element. */
|
||||
class ReadArrayElement extends BinaryExpr, @cil_read_array {
|
||||
|
||||
/** Gets the array being read. */
|
||||
Expr getArray() { result=getOperand(1) }
|
||||
Expr getArray() { result = getOperand(1) }
|
||||
|
||||
/** Gets the index into the array. */
|
||||
Expr getArrayIndex() { result = getOperand(0) }
|
||||
@@ -208,34 +216,36 @@ class ReadArrayElement extends BinaryExpr, @cil_read_array {
|
||||
|
||||
/** A write of an array element. */
|
||||
class WriteArrayElement extends Instruction, @cil_write_array {
|
||||
override int getPushCount() { result=0 }
|
||||
override int getPopCount() { result=3 }
|
||||
override int getPushCount() { result = 0 }
|
||||
|
||||
override int getPopCount() { result = 3 }
|
||||
}
|
||||
|
||||
/** A `return` statement. */
|
||||
class Return extends Instruction, @cil_ret {
|
||||
/** Gets the expression being returned, if any. */
|
||||
Expr getExpr() { result=getOperand(0) }
|
||||
Expr getExpr() { result = getOperand(0) }
|
||||
|
||||
override predicate canFlowNext() { none() }
|
||||
}
|
||||
|
||||
/** A `throw` statement. */
|
||||
class Throw extends Instruction, DotNet::Throw, @cil_throw_any {
|
||||
override Expr getExpr() { result=getOperand(0) }
|
||||
override Expr getExpr() { result = getOperand(0) }
|
||||
|
||||
override predicate canFlowNext() { none() }
|
||||
}
|
||||
|
||||
/** Stores a value at an address/location. */
|
||||
class StoreIndirect extends Instruction, @cil_stind {
|
||||
override int getPopCount() { result=2 }
|
||||
override int getPopCount() { result = 2 }
|
||||
|
||||
/** Gets the location to store the value at. */
|
||||
Expr getAddress() { result=getOperand(1) }
|
||||
Expr getAddress() { result = getOperand(1) }
|
||||
|
||||
/** Gets the value to store. */
|
||||
Expr getExpr() { result=getOperand(0) }
|
||||
Expr getExpr() { result = getOperand(0) }
|
||||
}
|
||||
|
||||
/** Loads a value from an address/location. */
|
||||
class LoadIndirect extends UnaryExpr, @cil_ldind {
|
||||
}
|
||||
class LoadIndirect extends UnaryExpr, @cil_ldind { }
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,53 +13,45 @@ private import dotnet
|
||||
* An implementation of a method in an assembly.
|
||||
*/
|
||||
class MethodImplementation extends EntryPoint, @cil_method_implementation {
|
||||
|
||||
/** Gets the method of this implementation. */
|
||||
Method getMethod() { cil_method_implementation(this,result,_) }
|
||||
Method getMethod() { cil_method_implementation(this, result, _) }
|
||||
|
||||
override MethodImplementation getImplementation() { result=this }
|
||||
override MethodImplementation getImplementation() { result = this }
|
||||
|
||||
/** Gets the location of this implementation. */
|
||||
Assembly getLocation() { cil_method_implementation(this,_,result) }
|
||||
Assembly getLocation() { cil_method_implementation(this, _, result) }
|
||||
|
||||
/** Gets the instruction at index `index`. */
|
||||
Instruction getInstruction(int index) {
|
||||
cil_instruction(result,_,index,this)
|
||||
}
|
||||
Instruction getInstruction(int index) { cil_instruction(result, _, index, this) }
|
||||
|
||||
/** Gets the `n`th local variable of this implementation. */
|
||||
LocalVariable getLocalVariable(int n) { cil_local_variable(result, this, n, _) }
|
||||
|
||||
/** Gets a local variable of this implementation, if any. */
|
||||
LocalVariable getALocalVariable() { result=getLocalVariable(_) }
|
||||
LocalVariable getALocalVariable() { result = getLocalVariable(_) }
|
||||
|
||||
/** Gets an instruction in this implementation, if any. */
|
||||
Instruction getAnInstruction() {
|
||||
result = getInstruction(_)
|
||||
}
|
||||
Instruction getAnInstruction() { result = getInstruction(_) }
|
||||
|
||||
/** Gets the total number of instructions in this implementation. */
|
||||
int getNumberOfInstructions() {
|
||||
result = count(getAnInstruction())
|
||||
}
|
||||
int getNumberOfInstructions() { result = count(getAnInstruction()) }
|
||||
|
||||
/** Gets the `i`th handler in this implementation. */
|
||||
Handler getHandler(int i) {
|
||||
result.getImplementation()=this and result.getIndex()=i
|
||||
}
|
||||
Handler getHandler(int i) { result.getImplementation() = this and result.getIndex() = i }
|
||||
|
||||
/** Gets a handler in this implementation, if any. */
|
||||
Handler getAHandler() { result.getImplementation()=this }
|
||||
Handler getAHandler() { result.getImplementation() = this }
|
||||
|
||||
override Instruction getASuccessorType(FlowType t) { t instanceof NormalFlow and result.getImplementation()=this and result.getIndex()=0 }
|
||||
override Instruction getASuccessorType(FlowType t) {
|
||||
t instanceof NormalFlow and result.getImplementation() = this and result.getIndex() = 0
|
||||
}
|
||||
|
||||
/** Gets the maximum stack size of this implementation. */
|
||||
int getStackSize() { cil_method_stack_size(this, result) }
|
||||
|
||||
override string toString() { result=getMethod().toString() }
|
||||
override string toString() { result = getMethod().toString() }
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A method, which corresponds to any callable in C#, including constructors,
|
||||
* destructors, operators, accessors and so on.
|
||||
@@ -69,7 +61,7 @@ class Method extends DotNet::Callable, Element, Member, TypeContainer, DataFlowN
|
||||
* Gets a method implementation, if any. Note that there can
|
||||
* be several implementations in different assemblies.
|
||||
*/
|
||||
MethodImplementation getAnImplementation() { result.getMethod()=this }
|
||||
MethodImplementation getAnImplementation() { result.getMethod() = this }
|
||||
|
||||
override Method getMethod() { result = this }
|
||||
|
||||
@@ -92,12 +84,10 @@ class Method extends DotNet::Callable, Element, Member, TypeContainer, DataFlowN
|
||||
override Parameter getRawParameter(int n) { cil_parameter(result, this, n, _) }
|
||||
|
||||
override Parameter getParameter(int n) {
|
||||
if isStatic()
|
||||
then result=getRawParameter(n)
|
||||
else (result=getRawParameter(n+1) and n>=0)
|
||||
}
|
||||
if isStatic() then result = getRawParameter(n) else (result = getRawParameter(n + 1) and n >= 0)
|
||||
}
|
||||
|
||||
override Type getType() { result=getReturnType() }
|
||||
override Type getType() { result = getReturnType() }
|
||||
|
||||
/** Gets the return type of this method. */
|
||||
override Type getReturnType() { cil_method(this, _, _, result) }
|
||||
@@ -106,15 +96,13 @@ class Method extends DotNet::Callable, Element, Member, TypeContainer, DataFlowN
|
||||
predicate returnsVoid() { getReturnType() instanceof VoidType }
|
||||
|
||||
/** Gets the number of stack items pushed in a call to this method. */
|
||||
int getCallPushCount() { if returnsVoid() then result=0 else result=1 }
|
||||
int getCallPushCount() { if returnsVoid() then result = 0 else result = 1 }
|
||||
|
||||
/** Gets the number of stack items popped in a call to this method. */
|
||||
int getCallPopCount() { result=count(getRawParameter(_)) }
|
||||
int getCallPopCount() { result = count(getRawParameter(_)) }
|
||||
|
||||
/** Gets a method called by this method. */
|
||||
Method getACallee() {
|
||||
result = getAnImplementation().getAnInstruction().(Call).getTarget()
|
||||
}
|
||||
Method getACallee() { result = getAnImplementation().getAnInstruction().(Call).getTarget() }
|
||||
|
||||
/** Holds if this method is `virtual`. */
|
||||
predicate isVirtual() { cil_virtual(this) }
|
||||
@@ -136,16 +124,16 @@ class Method extends DotNet::Callable, Element, Member, TypeContainer, DataFlowN
|
||||
override Method getSourceDeclaration() { result = getUnboundMethod() }
|
||||
|
||||
/** Holds if this method is an instance constructor. */
|
||||
predicate isInstanceConstructor() { isSpecial() and getName()=".ctor" }
|
||||
predicate isInstanceConstructor() { isSpecial() and getName() = ".ctor" }
|
||||
|
||||
/** Holds if this method is a static class constructor. */
|
||||
predicate isStaticConstructor() { isSpecial() and getName()=".cctor" }
|
||||
predicate isStaticConstructor() { isSpecial() and getName() = ".cctor" }
|
||||
|
||||
/** Holds if this method is a constructor (static or instance). */
|
||||
predicate isConstructor() { isStaticConstructor() or isInstanceConstructor() }
|
||||
|
||||
/** Holds if this method is a destructor/finalizer. */
|
||||
predicate isFinalizer() { getOverriddenMethod*().getQualifiedName()="System.Object.Finalize" }
|
||||
predicate isFinalizer() { getOverriddenMethod*().getQualifiedName() = "System.Object.Finalize" }
|
||||
|
||||
/** Holds if this method is an operator. */
|
||||
predicate isOperator() { isSpecial() and getName().matches("op\\_%") }
|
||||
@@ -163,10 +151,10 @@ class Method extends DotNet::Callable, Element, Member, TypeContainer, DataFlowN
|
||||
predicate isRemove() { isSpecial() and getName().matches("remove\\_%") }
|
||||
|
||||
/** Holds if this method is an implicit conversion operator. */
|
||||
predicate isImplicitConversion() { isSpecial() and getName()="op_Implicit" }
|
||||
predicate isImplicitConversion() { isSpecial() and getName() = "op_Implicit" }
|
||||
|
||||
/** Holds if this method is an explicit conversion operator. */
|
||||
predicate isExplicitConversion() { isSpecial() and getName()="op_Explicit" }
|
||||
predicate isExplicitConversion() { isSpecial() and getName() = "op_Explicit" }
|
||||
|
||||
/** Holds if this method is a conversion operator. */
|
||||
predicate isConversion() { isImplicitConversion() or isExplicitConversion() }
|
||||
@@ -175,14 +163,10 @@ class Method extends DotNet::Callable, Element, Member, TypeContainer, DataFlowN
|
||||
* Gets a method that is overridden, either in a base class
|
||||
* or in an interface.
|
||||
*/
|
||||
Method getOverriddenMethod() {
|
||||
cil_implements(this, result)
|
||||
}
|
||||
Method getOverriddenMethod() { cil_implements(this, result) }
|
||||
|
||||
/** Gets a method that overrides this method, if any. */
|
||||
final Method getAnOverrider() {
|
||||
result.getOverriddenMethod() = this
|
||||
}
|
||||
final Method getAnOverrider() { result.getOverriddenMethod() = this }
|
||||
|
||||
override predicate hasBody() { exists(getAnImplementation()) }
|
||||
|
||||
@@ -192,19 +176,13 @@ class Method extends DotNet::Callable, Element, Member, TypeContainer, DataFlowN
|
||||
}
|
||||
|
||||
/** A destructor/finalizer. */
|
||||
class Destructor extends Method, DotNet::Destructor {
|
||||
Destructor() { this.isFinalizer() }
|
||||
}
|
||||
class Destructor extends Method, DotNet::Destructor { Destructor() { this.isFinalizer() } }
|
||||
|
||||
/** A constructor. */
|
||||
class Constructor extends Method, DotNet::Constructor {
|
||||
Constructor() { this.isConstructor() }
|
||||
}
|
||||
class Constructor extends Method, DotNet::Constructor { Constructor() { this.isConstructor() } }
|
||||
|
||||
/** A static/class constructor. */
|
||||
class StaticConstructor extends Constructor {
|
||||
StaticConstructor() { this.isStaticConstructor() }
|
||||
}
|
||||
class StaticConstructor extends Constructor { StaticConstructor() { this.isStaticConstructor() } }
|
||||
|
||||
/** An instance constructor. */
|
||||
class InstanceConstructor extends Constructor {
|
||||
@@ -214,8 +192,7 @@ class InstanceConstructor extends Constructor {
|
||||
/** A method that always returns the `this` parameter. */
|
||||
class ChainingMethod extends Method {
|
||||
ChainingMethod() {
|
||||
forex(Return ret |
|
||||
ret = getAnImplementation().getAnInstruction() |
|
||||
forex(Return ret | ret = getAnImplementation().getAnInstruction() |
|
||||
ret.getExpr() instanceof ThisAccess
|
||||
)
|
||||
}
|
||||
@@ -229,8 +206,9 @@ abstract class Accessor extends Method {
|
||||
|
||||
/** A getter. */
|
||||
class Getter extends Accessor {
|
||||
Getter() { cil_getter(_,this) }
|
||||
override Property getProperty() { cil_getter(result,this) }
|
||||
Getter() { cil_getter(_, this) }
|
||||
|
||||
override Property getProperty() { cil_getter(result, this) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -240,22 +218,23 @@ class Getter extends Accessor {
|
||||
class TrivialGetter extends Method {
|
||||
TrivialGetter() {
|
||||
exists(MethodImplementation impl | impl = getAnImplementation() |
|
||||
impl.getInstruction(0) instanceof ThisAccess
|
||||
and impl.getInstruction(1) instanceof FieldReadAccess
|
||||
and impl.getInstruction(2) instanceof Return
|
||||
impl.getInstruction(0) instanceof ThisAccess and
|
||||
impl.getInstruction(1) instanceof FieldReadAccess and
|
||||
impl.getInstruction(2) instanceof Return
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the underlying field of this getter. */
|
||||
Field getField() {
|
||||
getAnImplementation().getAnInstruction().(FieldReadAccess).getTarget()=result
|
||||
getAnImplementation().getAnInstruction().(FieldReadAccess).getTarget() = result
|
||||
}
|
||||
}
|
||||
|
||||
/** A setter. */
|
||||
class Setter extends Accessor {
|
||||
Setter() { cil_setter(_,this) }
|
||||
override Property getProperty() { cil_setter(result,this) }
|
||||
Setter() { cil_setter(_, this) }
|
||||
|
||||
override Property getProperty() { cil_setter(result, this) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -265,9 +244,9 @@ class Setter extends Accessor {
|
||||
class TrivialSetter extends Method {
|
||||
TrivialSetter() {
|
||||
exists(MethodImplementation impl | impl = getAnImplementation() |
|
||||
impl.getInstruction(0) instanceof ThisAccess
|
||||
and impl.getInstruction(1).(ParameterReadAccess).getTarget().getIndex() = 1
|
||||
and impl.getInstruction(2) instanceof FieldWriteAccess
|
||||
impl.getInstruction(0) instanceof ThisAccess and
|
||||
impl.getInstruction(1).(ParameterReadAccess).getTarget().getIndex() = 1 and
|
||||
impl.getInstruction(2) instanceof FieldWriteAccess
|
||||
)
|
||||
}
|
||||
|
||||
@@ -285,5 +264,5 @@ class Operator extends Method {
|
||||
Operator() { this.isOperator() }
|
||||
|
||||
/** Gets the name of the implementing method (for compatibility with C# data model). */
|
||||
string getFunctionName() { result=getName() }
|
||||
string getFunctionName() { result = getName() }
|
||||
}
|
||||
|
||||
@@ -19,10 +19,14 @@ class TypeContainer extends DotNet::NamedElement, @cil_type_container {
|
||||
|
||||
/** A namespace. */
|
||||
class Namespace extends DotNet::Namespace, TypeContainer, @namespace {
|
||||
override string toString() { result=getQualifiedName() }
|
||||
override string toString() { result = getQualifiedName() }
|
||||
|
||||
override Namespace getParent() { result = this.getParentNamespace() }
|
||||
|
||||
override Namespace getParentNamespace() { parent_namespace(this, result) }
|
||||
override string getName() { namespaces(this,result) }
|
||||
|
||||
override string getName() { namespaces(this, result) }
|
||||
|
||||
override Location getLocation() { none() }
|
||||
}
|
||||
|
||||
@@ -31,14 +35,16 @@ class Namespace extends DotNet::Namespace, TypeContainer, @namespace {
|
||||
*/
|
||||
class Type extends DotNet::Type, Declaration, TypeContainer, @cil_type {
|
||||
override TypeContainer getParent() { cil_type(this, _, _, result, _) }
|
||||
|
||||
override string getName() { cil_type(this, result, _, _, _) }
|
||||
|
||||
override string toString() { result = getQualifiedName() }
|
||||
|
||||
/** Gets the containing type of this type, if any. */
|
||||
override Type getDeclaringType() { result = getParent() }
|
||||
|
||||
/** Gets a member of this type, if any. */
|
||||
Member getAMember() { result.getDeclaringType()=this }
|
||||
Member getAMember() { result.getDeclaringType() = this }
|
||||
|
||||
/**
|
||||
* Gets the unbound generic type of this type, or `this` if the type
|
||||
@@ -69,8 +75,7 @@ class Type extends DotNet::Type, Declaration, TypeContainer, @cil_type {
|
||||
* faster to compute.
|
||||
*/
|
||||
predicate isSystemType(string name) {
|
||||
exists(Namespace system |
|
||||
this.getParent() = system |
|
||||
exists(Namespace system | this.getParent() = system |
|
||||
system.getName() = "System" and
|
||||
system.getParentNamespace() instanceof DotNet::GlobalNamespace and
|
||||
name = this.getName()
|
||||
@@ -87,10 +92,9 @@ class Type extends DotNet::Type, Declaration, TypeContainer, @cil_type {
|
||||
predicate isPrivate() { cil_private(this) }
|
||||
|
||||
/** Gets the machine type used to store this type. */
|
||||
Type getUnderlyingType() { result=this }
|
||||
Type getUnderlyingType() { result = this }
|
||||
|
||||
// Class hierarchy
|
||||
|
||||
/** Gets the immediate base class of this class, if any. */
|
||||
Type getBaseClass() { cil_base_class(this, result) }
|
||||
|
||||
@@ -101,14 +105,14 @@ class Type extends DotNet::Type, Declaration, TypeContainer, @cil_type {
|
||||
Type getABaseType() { result = getBaseClass() or result = getABaseInterface() }
|
||||
|
||||
/** Gets an immediate subtype of this type, if any. */
|
||||
Type getASubtype() { result.getABaseType()=this }
|
||||
Type getASubtype() { result.getABaseType() = this }
|
||||
|
||||
/** Gets the namespace directly containing this type, if any. */
|
||||
Namespace getNamespace() { result=getParent() }
|
||||
Namespace getNamespace() { result = getParent() }
|
||||
|
||||
/**
|
||||
* Gets an index for implicit conversions. A type can be converted to another numeric type
|
||||
* of a higher index.
|
||||
*/
|
||||
int getConversionIndex() { result=0 }
|
||||
int getConversionIndex() { result = 0 }
|
||||
}
|
||||
|
||||
@@ -11,7 +11,8 @@ class TypeParameter extends DotNet::TypeParameter, Type, @cil_typeparameter {
|
||||
|
||||
/** Gets the generic type/method declaring this type parameter. */
|
||||
TypeContainer getGeneric() { cil_type_parameter(result, _, this) }
|
||||
override Location getLocation() { result=getParent().getLocation() }
|
||||
|
||||
override Location getLocation() { result = getParent().getLocation() }
|
||||
|
||||
/** Holds if this type parameter has the `new` constraint. */
|
||||
predicate isDefaultConstructible() { cil_typeparam_new(this) }
|
||||
@@ -28,14 +29,17 @@ class TypeParameter extends DotNet::TypeParameter, Type, @cil_typeparameter {
|
||||
predicate isContravariant() { cil_typeparam_contravariant(this) }
|
||||
|
||||
/** Gets a type constraint on this type parameter, if any. */
|
||||
Type getATypeConstraint() { cil_typeparam_constraint(this,result) }
|
||||
Type getATypeConstraint() { cil_typeparam_constraint(this, result) }
|
||||
}
|
||||
|
||||
/** A value or reference type. */
|
||||
class ValueOrRefType extends DotNet::ValueOrRefType, Type, @cil_valueorreftype {
|
||||
override ValueOrRefType getDeclaringType() { result = getParent() }
|
||||
|
||||
override string getUndecoratedName() { result = getName() }
|
||||
|
||||
override Namespace getDeclaringNamespace() { result = getNamespace() }
|
||||
|
||||
override ValueOrRefType getABaseType() { result = Type.super.getABaseType() }
|
||||
}
|
||||
|
||||
@@ -48,31 +52,28 @@ class Enum extends ValueOrRefType {
|
||||
}
|
||||
|
||||
/** A `class`. */
|
||||
class Class extends ValueOrRefType {
|
||||
Class() { this.isClass() }
|
||||
}
|
||||
class Class extends ValueOrRefType { Class() { this.isClass() } }
|
||||
|
||||
/** An `interface`. */
|
||||
class Interface extends ValueOrRefType {
|
||||
Interface() { this.isInterface() }
|
||||
}
|
||||
class Interface extends ValueOrRefType { Interface() { this.isInterface() } }
|
||||
|
||||
/** An array. */
|
||||
class ArrayType extends DotNet::ArrayType, Type, @cil_array_type {
|
||||
override Type getElementType() { cil_array_type(this,result,_) }
|
||||
override Type getElementType() { cil_array_type(this, result, _) }
|
||||
|
||||
/** Gets the rank of this array. */
|
||||
int getRank() { cil_array_type(this,_,result) }
|
||||
int getRank() { cil_array_type(this, _, result) }
|
||||
|
||||
override string toStringWithTypes() { result = DotNet::ArrayType.super.toStringWithTypes() }
|
||||
|
||||
override Location getLocation() { result=getElementType().getLocation() }
|
||||
override Location getLocation() { result = getElementType().getLocation() }
|
||||
|
||||
override ValueOrRefType getABaseType() { result = Type.super.getABaseType() }
|
||||
}
|
||||
|
||||
/** A pointer type. */
|
||||
class PointerType extends DotNet::PointerType, PrimitiveType, @cil_pointer_type {
|
||||
override Type getReferentType() { cil_pointer_type(this,result) }
|
||||
override Type getReferentType() { cil_pointer_type(this, result) }
|
||||
|
||||
override IntType getUnderlyingType() { any() }
|
||||
|
||||
@@ -86,164 +87,194 @@ class PointerType extends DotNet::PointerType, PrimitiveType, @cil_pointer_type
|
||||
}
|
||||
|
||||
/** A primitive type, built into the runtime. */
|
||||
abstract class PrimitiveType extends Type {
|
||||
}
|
||||
abstract class PrimitiveType extends Type { }
|
||||
|
||||
/**
|
||||
* A primitive numeric type.
|
||||
* Either an integral type (`IntegralType`) or a floating point type (`FloatingPointType`).
|
||||
*/
|
||||
abstract class NumericType extends PrimitiveType, ValueOrRefType {
|
||||
}
|
||||
abstract class NumericType extends PrimitiveType, ValueOrRefType { }
|
||||
|
||||
/** A floating point type. Either single precision (`FloatType`) or double precision (`DoubleType`). */
|
||||
abstract class FloatingPointType extends NumericType {
|
||||
}
|
||||
abstract class FloatingPointType extends NumericType { }
|
||||
|
||||
/**
|
||||
* An integral numeric type. Either a signed integral type (`SignedIntegralType`)
|
||||
* or an unsigned integral type (`UnsignedIntegralType`).
|
||||
*/
|
||||
abstract class IntegralType extends NumericType {
|
||||
}
|
||||
abstract class IntegralType extends NumericType { }
|
||||
|
||||
/** A signed integral type. */
|
||||
abstract class SignedIntegralType extends IntegralType {
|
||||
}
|
||||
abstract class SignedIntegralType extends IntegralType { }
|
||||
|
||||
/** An unsigned integral type. */
|
||||
abstract class UnsignedIntegralType extends IntegralType {
|
||||
}
|
||||
abstract class UnsignedIntegralType extends IntegralType { }
|
||||
|
||||
/** The `void` type, `System.Void`. */
|
||||
class VoidType extends PrimitiveType {
|
||||
VoidType() { this.isSystemType("Void") }
|
||||
override string toString() { result="void" }
|
||||
override string toStringWithTypes() { result="void" }
|
||||
|
||||
override string toString() { result = "void" }
|
||||
|
||||
override string toStringWithTypes() { result = "void" }
|
||||
}
|
||||
|
||||
/** The type `System.Int32`. */
|
||||
class IntType extends SignedIntegralType {
|
||||
IntType() { this.isSystemType("Int32") }
|
||||
override string toStringWithTypes() { result="int" }
|
||||
|
||||
override string toStringWithTypes() { result = "int" }
|
||||
|
||||
override int getConversionIndex() { result = 8 }
|
||||
override IntType getUnderlyingType() { result=this }
|
||||
|
||||
override IntType getUnderlyingType() { result = this }
|
||||
}
|
||||
|
||||
/** The type `System.IntPtr`. */
|
||||
class IntPtrType extends PrimitiveType {
|
||||
IntPtrType() { this.isSystemType("IntPtr") }
|
||||
|
||||
override IntType getUnderlyingType() { any() }
|
||||
}
|
||||
|
||||
/** The type `System.UIntPtr`. */
|
||||
class UIntPtrType extends PrimitiveType {
|
||||
UIntPtrType() { this.isSystemType("UIntPtr") }
|
||||
|
||||
override IntType getUnderlyingType() { any() }
|
||||
}
|
||||
|
||||
/** The type `System.UInt32`. */
|
||||
class UIntType extends UnsignedIntegralType {
|
||||
UIntType() { this.isSystemType("UInt32") }
|
||||
override string toStringWithTypes() { result="uint" }
|
||||
override int getConversionIndex() { result=7 }
|
||||
|
||||
override string toStringWithTypes() { result = "uint" }
|
||||
|
||||
override int getConversionIndex() { result = 7 }
|
||||
|
||||
override IntType getUnderlyingType() { any() }
|
||||
}
|
||||
|
||||
/** The type `System.SByte`. */
|
||||
class SByteType extends SignedIntegralType {
|
||||
SByteType() { this.isSystemType("SByte") }
|
||||
override string toStringWithTypes() { result="sbyte" }
|
||||
override int getConversionIndex() { result=2 }
|
||||
|
||||
override string toStringWithTypes() { result = "sbyte" }
|
||||
|
||||
override int getConversionIndex() { result = 2 }
|
||||
}
|
||||
|
||||
/** The type `System.Byte`. */
|
||||
class ByteType extends UnsignedIntegralType {
|
||||
ByteType() { this.isSystemType("Byte") }
|
||||
override string toStringWithTypes() { result="byte" }
|
||||
override int getConversionIndex() { result=1 }
|
||||
|
||||
override string toStringWithTypes() { result = "byte" }
|
||||
|
||||
override int getConversionIndex() { result = 1 }
|
||||
|
||||
override SByteType getUnderlyingType() { any() }
|
||||
}
|
||||
|
||||
/** The type `System.Int16`. */
|
||||
class ShortType extends SignedIntegralType {
|
||||
ShortType() { this.isSystemType("Int16") }
|
||||
override string toStringWithTypes() { result="short" }
|
||||
override int getConversionIndex() { result=4 }
|
||||
|
||||
override string toStringWithTypes() { result = "short" }
|
||||
|
||||
override int getConversionIndex() { result = 4 }
|
||||
}
|
||||
|
||||
/** The type `System.UInt16`. */
|
||||
class UShortType extends UnsignedIntegralType {
|
||||
UShortType() { this.isSystemType("UInt16") }
|
||||
override string toStringWithTypes() { result="ushort" }
|
||||
override int getConversionIndex() { result=3 }
|
||||
|
||||
override string toStringWithTypes() { result = "ushort" }
|
||||
|
||||
override int getConversionIndex() { result = 3 }
|
||||
|
||||
override ShortType getUnderlyingType() { any() }
|
||||
}
|
||||
|
||||
/** The type `System.Int64`. */
|
||||
class LongType extends SignedIntegralType {
|
||||
LongType() { this.isSystemType("Int64") }
|
||||
override string toStringWithTypes() { result="long" }
|
||||
override int getConversionIndex() { result=10 }
|
||||
|
||||
override string toStringWithTypes() { result = "long" }
|
||||
|
||||
override int getConversionIndex() { result = 10 }
|
||||
}
|
||||
|
||||
/** The type `System.UInt64`. */
|
||||
class ULongType extends UnsignedIntegralType {
|
||||
ULongType() { this.isSystemType("UInt64") }
|
||||
override string toStringWithTypes() { result="ulong" }
|
||||
override int getConversionIndex() { result=9 }
|
||||
|
||||
override string toStringWithTypes() { result = "ulong" }
|
||||
|
||||
override int getConversionIndex() { result = 9 }
|
||||
|
||||
override LongType getUnderlyingType() { any() }
|
||||
}
|
||||
|
||||
/** The type `System.Decimal`. */
|
||||
class DecimalType extends SignedIntegralType {
|
||||
DecimalType() { this.isSystemType("Decimal") }
|
||||
override string toStringWithTypes() { result="decimal" }
|
||||
override int getConversionIndex() { result=13 }
|
||||
|
||||
override string toStringWithTypes() { result = "decimal" }
|
||||
|
||||
override int getConversionIndex() { result = 13 }
|
||||
}
|
||||
|
||||
/** The type `System.String`. */
|
||||
class StringType extends PrimitiveType, ValueOrRefType {
|
||||
StringType() { this.isSystemType("String") }
|
||||
override string toStringWithTypes() { result="string" }
|
||||
|
||||
override string toStringWithTypes() { result = "string" }
|
||||
}
|
||||
|
||||
/** The type `System.Object`. */
|
||||
class ObjectType extends ValueOrRefType {
|
||||
ObjectType() { this.isSystemType("Object") }
|
||||
override string toStringWithTypes() { result="object" }
|
||||
|
||||
override string toStringWithTypes() { result = "object" }
|
||||
}
|
||||
|
||||
/** The type `System.Boolean`. */
|
||||
class BoolType extends PrimitiveType, ValueOrRefType {
|
||||
BoolType() { this.isSystemType("Boolean") }
|
||||
override string toStringWithTypes() { result="bool" }
|
||||
|
||||
override string toStringWithTypes() { result = "bool" }
|
||||
|
||||
override IntType getUnderlyingType() { any() }
|
||||
}
|
||||
|
||||
/** The type `System.Double`. */
|
||||
class DoubleType extends FloatingPointType {
|
||||
DoubleType() { this.isSystemType("Double") }
|
||||
override string toStringWithTypes() { result="double" }
|
||||
override int getConversionIndex() { result=12 }
|
||||
|
||||
override string toStringWithTypes() { result = "double" }
|
||||
|
||||
override int getConversionIndex() { result = 12 }
|
||||
}
|
||||
|
||||
/** The type `System.Single`. */
|
||||
class FloatType extends FloatingPointType {
|
||||
FloatType() { this.isSystemType("Single") }
|
||||
override string toStringWithTypes() { result="float" }
|
||||
override int getConversionIndex() { result=4 }
|
||||
|
||||
override string toStringWithTypes() { result = "float" }
|
||||
|
||||
override int getConversionIndex() { result = 4 }
|
||||
}
|
||||
|
||||
/** The type `System.Char`. */
|
||||
class CharType extends IntegralType {
|
||||
CharType() { this.isSystemType("Char") }
|
||||
override string toStringWithTypes() { result="char" }
|
||||
override int getConversionIndex() { result=6 }
|
||||
|
||||
override string toStringWithTypes() { result = "char" }
|
||||
|
||||
override int getConversionIndex() { result = 6 }
|
||||
|
||||
override IntType getUnderlyingType() { any() }
|
||||
}
|
||||
|
||||
/** The type `System.Type`. */
|
||||
class SystemType extends ValueOrRefType {
|
||||
SystemType() { this.isSystemType("Type") }
|
||||
}
|
||||
class SystemType extends ValueOrRefType { SystemType() { this.isSystemType("Type") } }
|
||||
|
||||
@@ -14,13 +14,13 @@ class Variable extends DotNet::Variable, Declaration, DataFlowNode, @cil_variabl
|
||||
override string toStringWithTypes() { none() }
|
||||
|
||||
/** Gets an access to this variable, if any. */
|
||||
VariableAccess getAnAccess() { result.getTarget()=this }
|
||||
VariableAccess getAnAccess() { result.getTarget() = this }
|
||||
|
||||
/** Gets a read access to this variable, if any. */
|
||||
ReadAccess getARead() { result=getAnAccess() }
|
||||
ReadAccess getARead() { result = getAnAccess() }
|
||||
|
||||
/** Gets a write access to this variable, if any. */
|
||||
WriteAccess getAWrite() { result=getAnAccess() }
|
||||
WriteAccess getAWrite() { result = getAnAccess() }
|
||||
|
||||
override string toString() { result = Declaration.super.toString() }
|
||||
|
||||
@@ -28,8 +28,7 @@ class Variable extends DotNet::Variable, Declaration, DataFlowNode, @cil_variabl
|
||||
}
|
||||
|
||||
/** A stack variable. Either a local variable (`LocalVariable`) or a parameter (`Parameter`). */
|
||||
class StackVariable extends Variable, @cil_stack_variable {
|
||||
}
|
||||
class StackVariable extends Variable, @cil_stack_variable { }
|
||||
|
||||
/**
|
||||
* A local variable.
|
||||
@@ -37,33 +36,35 @@ class StackVariable extends Variable, @cil_stack_variable {
|
||||
* Each method in CIL has a number of typed local variables, in addition to the evaluation stack.
|
||||
*/
|
||||
class LocalVariable extends StackVariable, @cil_local_variable {
|
||||
override string toString() { result="Local variable " + getIndex() + " of method " + getImplementation().getMethod() }
|
||||
override string toString() {
|
||||
result = "Local variable " + getIndex() + " of method " + getImplementation().getMethod()
|
||||
}
|
||||
|
||||
/** Gets the method implementation defining this local variable. */
|
||||
MethodImplementation getImplementation() { this=result.getALocalVariable() }
|
||||
MethodImplementation getImplementation() { this = result.getALocalVariable() }
|
||||
|
||||
/** Gets the index number of this local variable. This is not usually significant. */
|
||||
int getIndex() { this = getImplementation().getLocalVariable(result) }
|
||||
|
||||
override Type getType() { cil_local_variable(this, _, _, result) }
|
||||
|
||||
override Location getLocation() { result=getImplementation().getLocation() }
|
||||
override Location getLocation() { result = getImplementation().getLocation() }
|
||||
|
||||
override Method getMethod() { result=getImplementation().getMethod() }
|
||||
override Method getMethod() { result = getImplementation().getMethod() }
|
||||
}
|
||||
|
||||
/** A method parameter. */
|
||||
class Parameter extends DotNet::Parameter, StackVariable, @cil_parameter {
|
||||
|
||||
/** Gets the method declaring this parameter. */
|
||||
override Method getMethod() { this=result.getARawParameter() }
|
||||
override Method getMethod() { this = result.getARawParameter() }
|
||||
|
||||
override Method getCallable() { result=getMethod() }
|
||||
override Method getCallable() { result = getMethod() }
|
||||
|
||||
/** Gets the index of this parameter. */
|
||||
int getIndex() { cil_parameter(this, _, result, _) }
|
||||
|
||||
override string toString() { result="Parameter " + getIndex() + " of " + getMethod() }
|
||||
override string toString() { result = "Parameter " + getIndex() + " of " + getMethod() }
|
||||
|
||||
override Type getType() { cil_parameter(this, _, _, result) }
|
||||
|
||||
/**
|
||||
@@ -86,9 +87,11 @@ class Parameter extends DotNet::Parameter, StackVariable, @cil_parameter {
|
||||
|
||||
override string toStringWithTypes() { result = getPrefix() + getType().toStringWithTypes() }
|
||||
|
||||
private string getPrefix() { if isOut() then result="out " else if isRef() then result="ref " else result="" }
|
||||
private string getPrefix() {
|
||||
if isOut() then result = "out " else if isRef() then result = "ref " else result = ""
|
||||
}
|
||||
|
||||
override Location getLocation() { result=getMethod().getLocation() }
|
||||
override Location getLocation() { result = getMethod().getLocation() }
|
||||
|
||||
override ParameterAccess getAnAccess() { result.getTarget() = this }
|
||||
|
||||
@@ -105,17 +108,24 @@ class Parameter extends DotNet::Parameter, StackVariable, @cil_parameter {
|
||||
/** A parameter corresponding to `this`. */
|
||||
class ThisParameter extends Parameter {
|
||||
ThisParameter() {
|
||||
not this.getMethod().isStatic()
|
||||
and this.getIndex()=0
|
||||
not this.getMethod().isStatic() and
|
||||
this.getIndex() = 0
|
||||
}
|
||||
}
|
||||
|
||||
/** A field. */
|
||||
class Field extends DotNet::Field, Variable, Member, @cil_field {
|
||||
override string toString() { result=getName() }
|
||||
override string toStringWithTypes() { result=getDeclaringType().toStringWithTypes() + "." + getName() }
|
||||
override string getName() { cil_field(this,_,result,_) }
|
||||
override string toString() { result = getName() }
|
||||
|
||||
override string toStringWithTypes() {
|
||||
result = getDeclaringType().toStringWithTypes() + "." + getName()
|
||||
}
|
||||
|
||||
override string getName() { cil_field(this, _, result, _) }
|
||||
|
||||
override Type getType() { cil_field(this, _, _, result) }
|
||||
|
||||
override ValueOrRefType getDeclaringType() { cil_field(this, result, _, _) }
|
||||
override Location getLocation() { result=getDeclaringType().getLocation() }
|
||||
|
||||
override Location getLocation() { result = getDeclaringType().getLocation() }
|
||||
}
|
||||
|
||||
@@ -27,9 +27,7 @@ class Assignable extends Declaration, @assignable {
|
||||
* property (`Property`), an indexer (`Indexer`), or an event (`Event`).
|
||||
*/
|
||||
class AssignableMember extends Member, Assignable {
|
||||
override AssignableMemberAccess getAnAccess() {
|
||||
result = Assignable.super.getAnAccess()
|
||||
}
|
||||
override AssignableMemberAccess getAnAccess() { result = Assignable.super.getAnAccess() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -38,9 +36,7 @@ class AssignableMember extends Member, Assignable {
|
||||
* (`IndexerAccess`), or an event access (`EventAccess`).
|
||||
*/
|
||||
class AssignableMemberAccess extends MemberAccess, AssignableAccess {
|
||||
override AssignableMember getTarget() {
|
||||
result = AssignableAccess.super.getTarget()
|
||||
}
|
||||
override AssignableMember getTarget() { result = AssignableAccess.super.getTarget() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,8 +66,7 @@ class AssignableRead extends AssignableAccess {
|
||||
this.isRefArgument()
|
||||
or
|
||||
this = any(AssignableDefinitions::AddressOfDefinition def).getTargetAccess()
|
||||
)
|
||||
and
|
||||
) and
|
||||
not this = any(NameOfExpr noe).getAChildExpr*()
|
||||
}
|
||||
|
||||
@@ -100,8 +95,7 @@ class AssignableRead extends AssignableAccess {
|
||||
* - The read of `this.Field` on line 11 is next to the read on line 10.
|
||||
*/
|
||||
AssignableRead getANextRead() {
|
||||
forex(ControlFlow::Node cfn |
|
||||
cfn = result.getAControlFlowNode() |
|
||||
forex(ControlFlow::Node cfn | cfn = result.getAControlFlowNode() |
|
||||
Ssa::Internal::adjacentReadPairSameVar(this.getAControlFlowNode(), cfn)
|
||||
)
|
||||
}
|
||||
@@ -113,9 +107,7 @@ class AssignableRead extends AssignableAccess {
|
||||
*
|
||||
* This is the transitive closure of `getANextRead()`.
|
||||
*/
|
||||
AssignableRead getAReachableRead() {
|
||||
result = this.getANextRead+()
|
||||
}
|
||||
AssignableRead getAReachableRead() { result = this.getANextRead+() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,11 +128,7 @@ class AssignableRead extends AssignableAccess {
|
||||
* ```
|
||||
*/
|
||||
class AssignableWrite extends AssignableAccess {
|
||||
AssignableWrite() {
|
||||
exists(AssignableDefinition def |
|
||||
def.getTargetAccess() = this
|
||||
)
|
||||
}
|
||||
AssignableWrite() { exists(AssignableDefinition def | def.getTargetAccess() = this) }
|
||||
}
|
||||
|
||||
/** INTERNAL: Do not use. */
|
||||
@@ -164,8 +152,7 @@ module AssignableInternal {
|
||||
left = ae.getLValue() and
|
||||
right = ae.getRValue()
|
||||
or
|
||||
exists(TupleExpr l, TupleExpr r, int i |
|
||||
tupleAssignmentPair(ae, l, r) |
|
||||
exists(TupleExpr l, TupleExpr r, int i | tupleAssignmentPair(ae, l, r) |
|
||||
left = l.getArgument(i) and
|
||||
right = r.getArgument(i)
|
||||
)
|
||||
@@ -196,12 +183,10 @@ module AssignableInternal {
|
||||
* recursion error).
|
||||
*/
|
||||
private Expr getArgumentForParameter(Call c, Parameter p) {
|
||||
exists(Callable callable |
|
||||
p = callable.getAParameter() |
|
||||
exists(Callable callable | p = callable.getAParameter() |
|
||||
callable = c.(MethodCall).getTarget() or
|
||||
callable = c.(ObjectCreation).getTarget()
|
||||
)
|
||||
and
|
||||
) and
|
||||
(
|
||||
// Appears in the positional part of the call
|
||||
result = c.getArgument(p.getPosition()) and
|
||||
@@ -212,7 +197,8 @@ module AssignableInternal {
|
||||
)
|
||||
}
|
||||
|
||||
pragma [noinline] // predicate folding to get proper join-order
|
||||
// predicate folding to get proper join-order
|
||||
pragma[noinline]
|
||||
private Expr getExplicitArgument(Call c, string name) {
|
||||
result = c.getAnArgument() and
|
||||
result.getExplicitArgumentName() = name
|
||||
@@ -223,8 +209,7 @@ module AssignableInternal {
|
||||
* the target callable is non-overridable and from source.
|
||||
*/
|
||||
private predicate isAnalyzableRefCall(Call c, AssignableAccess aa, Parameter p) {
|
||||
exists(Callable callable |
|
||||
callable = getRefCallTarget(c, aa, p) |
|
||||
exists(Callable callable | callable = getRefCallTarget(c, aa, p) |
|
||||
not callable.(Virtualizable).isOverridableOrImplementable() and
|
||||
callable.hasBody()
|
||||
)
|
||||
@@ -236,8 +221,7 @@ module AssignableInternal {
|
||||
* recursion.
|
||||
*/
|
||||
private predicate isNonAnalyzableRefCall(Call c, AssignableAccess aa, Parameter p) {
|
||||
exists(Callable callable |
|
||||
callable = getRefCallTarget(c, aa, p) |
|
||||
exists(Callable callable | callable = getRefCallTarget(c, aa, p) |
|
||||
callable.(Virtualizable).isOverridableOrImplementable() or
|
||||
not callable.hasBody()
|
||||
)
|
||||
@@ -259,14 +243,12 @@ module AssignableInternal {
|
||||
* any assignments to `p`.
|
||||
*/
|
||||
private predicate parameterReachesWithoutDef(Parameter p, ControlFlow::BasicBlock bb) {
|
||||
not basicBlockRefParamDef(bb, p)
|
||||
and
|
||||
not basicBlockRefParamDef(bb, p) and
|
||||
(
|
||||
isAnalyzableRefCall(_, _, p) and
|
||||
p.getCallable().getEntryPoint() = bb.getFirstNode()
|
||||
or
|
||||
exists(ControlFlow::BasicBlock mid |
|
||||
parameterReachesWithoutDef(p, mid) |
|
||||
exists(ControlFlow::BasicBlock mid | parameterReachesWithoutDef(p, mid) |
|
||||
bb = mid.getASuccessor()
|
||||
)
|
||||
)
|
||||
@@ -277,45 +259,33 @@ module AssignableInternal {
|
||||
bb.getANode() = getAnAnalyzableRefDef(_, _, p).getAControlFlowNode()
|
||||
}
|
||||
|
||||
private cached module Cached {
|
||||
cached newtype TAssignableDefinition =
|
||||
TAssignmentDefinition(Assignment a) {
|
||||
not a.getLValue() instanceof TupleExpr
|
||||
}
|
||||
or
|
||||
TTupleAssignmentDefinition(AssignExpr ae, Expr leaf) {
|
||||
tupleAssignmentDefinition(ae, leaf)
|
||||
}
|
||||
or
|
||||
cached
|
||||
private module Cached {
|
||||
cached
|
||||
newtype TAssignableDefinition =
|
||||
TAssignmentDefinition(Assignment a) { not a.getLValue() instanceof TupleExpr } or
|
||||
TTupleAssignmentDefinition(AssignExpr ae, Expr leaf) { tupleAssignmentDefinition(ae, leaf) } or
|
||||
TOutRefDefinition(AssignableAccess aa) {
|
||||
aa.isOutArgument()
|
||||
or
|
||||
isRelevantRefCall(_, aa)
|
||||
}
|
||||
or
|
||||
TMutationDefinition(MutatorOperation mo)
|
||||
or
|
||||
} or
|
||||
TMutationDefinition(MutatorOperation mo) or
|
||||
TLocalVariableDefinition(LocalVariableDeclExpr lvde) {
|
||||
not lvde.hasInitializer() and
|
||||
not exists(getTupleSource(TTupleAssignmentDefinition(_, lvde))) and
|
||||
not lvde = any(IsPatternExpr ipe).getVariableDeclExpr() and
|
||||
not lvde = any(TypeCase tc).getVariableDeclExpr()
|
||||
}
|
||||
or
|
||||
} or
|
||||
TImplicitParameterDefinition(Parameter p) {
|
||||
exists(Callable c |
|
||||
p = c.getAParameter() |
|
||||
exists(Callable c | p = c.getAParameter() |
|
||||
c.hasBody() or
|
||||
c.(Constructor).hasInitializer()
|
||||
)
|
||||
}
|
||||
or
|
||||
TAddressOfDefinition(AddressOfExpr aoe)
|
||||
or
|
||||
TIsPatternDefinition(IsPatternExpr ipe)
|
||||
or
|
||||
TTypeCasePatternDefinition(TypeCase tc)
|
||||
or
|
||||
} or
|
||||
TAddressOfDefinition(AddressOfExpr aoe) or
|
||||
TIsPatternDefinition(IsPatternExpr ipe) or
|
||||
TTypeCasePatternDefinition(TypeCase tc) or
|
||||
TInitializer(Assignable a, Expr e) {
|
||||
e = a.(Field).getInitializer() or
|
||||
e = a.(Property).getInitializer()
|
||||
@@ -324,9 +294,9 @@ module AssignableInternal {
|
||||
/**
|
||||
* Gets the source expression assigned in tuple definition `def`, if any.
|
||||
*/
|
||||
cached Expr getTupleSource(TTupleAssignmentDefinition def) {
|
||||
exists(AssignExpr ae, Expr leaf |
|
||||
def = TTupleAssignmentDefinition(ae, leaf) |
|
||||
cached
|
||||
Expr getTupleSource(TTupleAssignmentDefinition def) {
|
||||
exists(AssignExpr ae, Expr leaf | def = TTupleAssignmentDefinition(ae, leaf) |
|
||||
tupleAssignmentPair(ae, leaf, result)
|
||||
)
|
||||
}
|
||||
@@ -334,48 +304,44 @@ module AssignableInternal {
|
||||
/**
|
||||
* Holds if the `ref` assignment to `aa` via call `c` is uncertain.
|
||||
*/
|
||||
cached predicate isUncertainRefCall(Call c, AssignableAccess aa) {
|
||||
isRelevantRefCall(c, aa)
|
||||
and
|
||||
exists(ControlFlow::BasicBlock bb, Parameter p |
|
||||
isAnalyzableRefCall(c, aa, p) |
|
||||
cached
|
||||
predicate isUncertainRefCall(Call c, AssignableAccess aa) {
|
||||
isRelevantRefCall(c, aa) and
|
||||
exists(ControlFlow::BasicBlock bb, Parameter p | isAnalyzableRefCall(c, aa, p) |
|
||||
parameterReachesWithoutDef(p, bb) and
|
||||
bb.getLastNode() = p.getCallable().getExitPoint()
|
||||
)
|
||||
}
|
||||
|
||||
// Not defined by dispatch in order to avoid too conservative negative recursion error
|
||||
cached Assignable getTarget(AssignableDefinition def) {
|
||||
cached
|
||||
Assignable getTarget(AssignableDefinition def) {
|
||||
result = def.getTargetAccess().getTarget()
|
||||
or
|
||||
exists(Expr leaf |
|
||||
def = TTupleAssignmentDefinition(_, leaf) |
|
||||
exists(Expr leaf | def = TTupleAssignmentDefinition(_, leaf) |
|
||||
result = leaf.(LocalVariableDeclExpr).getVariable()
|
||||
)
|
||||
or
|
||||
def = any(AssignableDefinitions::ImplicitParameterDefinition p |
|
||||
result = p.getParameter()
|
||||
)
|
||||
def = any(AssignableDefinitions::ImplicitParameterDefinition p | result = p.getParameter())
|
||||
or
|
||||
def = any(AssignableDefinitions::LocalVariableDefinition decl |
|
||||
result = decl.getDeclaration().getVariable()
|
||||
)
|
||||
result = decl.getDeclaration().getVariable()
|
||||
)
|
||||
or
|
||||
def = any(AssignableDefinitions::IsPatternDefinition is |
|
||||
result = is.getDeclaration().getVariable()
|
||||
)
|
||||
result = is.getDeclaration().getVariable()
|
||||
)
|
||||
or
|
||||
def = any(AssignableDefinitions::TypeCasePatternDefinition case |
|
||||
result = case.getDeclaration().getVariable()
|
||||
)
|
||||
result = case.getDeclaration().getVariable()
|
||||
)
|
||||
or
|
||||
def = any(AssignableDefinitions::InitializerDefinition init |
|
||||
result = init.getAssignable()
|
||||
)
|
||||
def = any(AssignableDefinitions::InitializerDefinition init | result = init.getAssignable())
|
||||
}
|
||||
|
||||
// Not defined by dispatch in order to avoid too conservative negative recursion error
|
||||
cached AssignableAccess getTargetAccess(AssignableDefinition def) {
|
||||
cached
|
||||
AssignableAccess getTargetAccess(AssignableDefinition def) {
|
||||
def = TAssignmentDefinition(any(Assignment a | a.getLValue() = result))
|
||||
or
|
||||
def = TTupleAssignmentDefinition(_, result)
|
||||
@@ -391,14 +357,13 @@ module AssignableInternal {
|
||||
* Gets the argument for the implicit `value` parameter in the accessor call
|
||||
* `ac`, if any.
|
||||
*/
|
||||
cached Expr getAccessorCallValueArgument(AccessorCall ac) {
|
||||
exists(AssignExpr ae |
|
||||
tupleAssignmentDefinition(ae, ac) |
|
||||
cached
|
||||
Expr getAccessorCallValueArgument(AccessorCall ac) {
|
||||
exists(AssignExpr ae | tupleAssignmentDefinition(ae, ac) |
|
||||
tupleAssignmentPair(ae, ac, result)
|
||||
)
|
||||
or
|
||||
exists(Assignment a |
|
||||
ac = a.getLValue() |
|
||||
exists(Assignment a | ac = a.getLValue() |
|
||||
result = a.getRValue() and
|
||||
not a.(AssignOperation).hasExpandedAssignment()
|
||||
)
|
||||
@@ -406,7 +371,6 @@ module AssignableInternal {
|
||||
}
|
||||
import Cached
|
||||
}
|
||||
|
||||
private import AssignableInternal
|
||||
|
||||
/**
|
||||
@@ -431,9 +395,7 @@ class AssignableDefinition extends TAssignableDefinition {
|
||||
* the definitions of `x` and `y` in `M(out x, out y)` and `(x, y) = (0, 1)`
|
||||
* relate to the same call to `M` and assignment node, respectively.
|
||||
*/
|
||||
ControlFlow::Node getAControlFlowNode() {
|
||||
result = this.getExpr().getAControlFlowNode()
|
||||
}
|
||||
ControlFlow::Node getAControlFlowNode() { result = this.getExpr().getAControlFlowNode() }
|
||||
|
||||
/**
|
||||
* Gets the underlying expression that updates the targeted assignable when
|
||||
@@ -505,10 +467,8 @@ class AssignableDefinition extends TAssignableDefinition {
|
||||
* `AssignableRead.getANextRead()`.
|
||||
*/
|
||||
AssignableRead getAFirstRead() {
|
||||
forex(ControlFlow::Node cfn |
|
||||
cfn = result.getAControlFlowNode() |
|
||||
exists(Ssa::ExplicitDefinition def |
|
||||
result = def.getAFirstReadAtNode(cfn) |
|
||||
forex(ControlFlow::Node cfn | cfn = result.getAControlFlowNode() |
|
||||
exists(Ssa::ExplicitDefinition def | result = def.getAFirstReadAtNode(cfn) |
|
||||
this = def.getADefinition()
|
||||
)
|
||||
)
|
||||
@@ -521,17 +481,13 @@ class AssignableDefinition extends TAssignableDefinition {
|
||||
*
|
||||
* This is the equivalent with `getAFirstRead().getANextRead*()`.
|
||||
*/
|
||||
AssignableRead getAReachableRead() {
|
||||
result = this.getAFirstRead().getANextRead*()
|
||||
}
|
||||
AssignableRead getAReachableRead() { result = this.getAFirstRead().getANextRead*() }
|
||||
|
||||
/** Gets a textual representation of this assignable definition. */
|
||||
string toString() { none() }
|
||||
|
||||
/** Gets the location of this assignable definition. */
|
||||
Location getLocation() {
|
||||
result = this.getExpr().getLocation()
|
||||
}
|
||||
Location getLocation() { result = this.getExpr().getLocation() }
|
||||
}
|
||||
|
||||
/** Provides different types of `AssignableDefinition`s. */
|
||||
@@ -542,14 +498,10 @@ module AssignableDefinitions {
|
||||
class AssignmentDefinition extends AssignableDefinition, TAssignmentDefinition {
|
||||
Assignment a;
|
||||
|
||||
AssignmentDefinition() {
|
||||
this = TAssignmentDefinition(a)
|
||||
}
|
||||
AssignmentDefinition() { this = TAssignmentDefinition(a) }
|
||||
|
||||
/** Gets the underlying assignment. */
|
||||
Assignment getAssignment() {
|
||||
result = a
|
||||
}
|
||||
Assignment getAssignment() { result = a }
|
||||
|
||||
override Expr getExpr() { result = a }
|
||||
|
||||
@@ -558,9 +510,7 @@ module AssignableDefinitions {
|
||||
not a instanceof AssignOperation
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = a.toString()
|
||||
}
|
||||
override string toString() { result = a.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -569,16 +519,13 @@ module AssignableDefinitions {
|
||||
*/
|
||||
class TupleAssignmentDefinition extends AssignableDefinition, TTupleAssignmentDefinition {
|
||||
AssignExpr ae;
|
||||
|
||||
Expr leaf;
|
||||
|
||||
TupleAssignmentDefinition() {
|
||||
this = TTupleAssignmentDefinition(ae, leaf)
|
||||
}
|
||||
TupleAssignmentDefinition() { this = TTupleAssignmentDefinition(ae, leaf) }
|
||||
|
||||
/** Gets the underlying assignment. */
|
||||
AssignExpr getAssignment() {
|
||||
result = ae
|
||||
}
|
||||
AssignExpr getAssignment() { result = ae }
|
||||
|
||||
/**
|
||||
* Gets the evaluation order of this definition among the other definitions
|
||||
@@ -587,9 +534,12 @@ module AssignableDefinitions {
|
||||
*/
|
||||
int getEvaluationOrder() {
|
||||
leaf = rank[result + 1](Expr leaf0 |
|
||||
exists(TTupleAssignmentDefinition(ae, leaf0)) |
|
||||
leaf0 order by leaf0.getLocation().getStartLine(), leaf0.getLocation().getStartColumn()
|
||||
)
|
||||
exists(TTupleAssignmentDefinition(ae, leaf0))
|
||||
|
|
||||
leaf0
|
||||
order by
|
||||
leaf0.getLocation().getStartLine(), leaf0.getLocation().getStartColumn()
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getExpr() { result = ae }
|
||||
@@ -598,9 +548,7 @@ module AssignableDefinitions {
|
||||
result = getTupleSource(this) // need not exist
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = ae.toString()
|
||||
}
|
||||
override string toString() { result = ae.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -610,14 +558,10 @@ module AssignableDefinitions {
|
||||
class OutRefDefinition extends AssignableDefinition, TOutRefDefinition {
|
||||
AssignableAccess aa;
|
||||
|
||||
OutRefDefinition() {
|
||||
this = TOutRefDefinition(aa)
|
||||
}
|
||||
OutRefDefinition() { this = TOutRefDefinition(aa) }
|
||||
|
||||
/** Gets the underlying call. */
|
||||
Call getCall() {
|
||||
result.getAnArgument() = aa
|
||||
}
|
||||
Call getCall() { result.getAnArgument() = aa }
|
||||
|
||||
/**
|
||||
* Gets the index of this definition among the other definitions in the
|
||||
@@ -625,14 +569,13 @@ module AssignableDefinitions {
|
||||
* the definitions of `x` and `y` are 0 and 1, respectively.
|
||||
*/
|
||||
int getIndex() {
|
||||
exists(ControlFlow::BasicBlock bb, int i |
|
||||
bb.getNode(i).getElement() = aa |
|
||||
exists(ControlFlow::BasicBlock bb, int i | bb.getNode(i).getElement() = aa |
|
||||
i = rank[result + 1](int j, OutRefDefinition def |
|
||||
bb.getNode(j).getElement() = def.getTargetAccess() and
|
||||
this.getCall() = def.getCall()
|
||||
bb.getNode(j).getElement() = def.getTargetAccess() and
|
||||
this.getCall() = def.getCall()
|
||||
|
|
||||
j
|
||||
)
|
||||
j
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -642,13 +585,9 @@ module AssignableDefinitions {
|
||||
not isUncertainRefCall(this.getCall(), this.getTargetAccess())
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = aa.toString()
|
||||
}
|
||||
override string toString() { result = aa.toString() }
|
||||
|
||||
override Location getLocation() {
|
||||
result = aa.getLocation()
|
||||
}
|
||||
override Location getLocation() { result = aa.getLocation() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -657,20 +596,14 @@ module AssignableDefinitions {
|
||||
class MutationDefinition extends AssignableDefinition, TMutationDefinition {
|
||||
MutatorOperation mo;
|
||||
|
||||
MutationDefinition() {
|
||||
this = TMutationDefinition(mo)
|
||||
}
|
||||
MutationDefinition() { this = TMutationDefinition(mo) }
|
||||
|
||||
/** Gets the underlying mutator operation. */
|
||||
MutatorOperation getMutatorOperation() {
|
||||
result = mo
|
||||
}
|
||||
MutatorOperation getMutatorOperation() { result = mo }
|
||||
|
||||
override Expr getExpr() { result = mo }
|
||||
|
||||
override string toString() {
|
||||
result = mo.toString()
|
||||
}
|
||||
override string toString() { result = mo.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -679,20 +612,14 @@ module AssignableDefinitions {
|
||||
class LocalVariableDefinition extends AssignableDefinition, TLocalVariableDefinition {
|
||||
LocalVariableDeclExpr lvde;
|
||||
|
||||
LocalVariableDefinition() {
|
||||
this = TLocalVariableDefinition(lvde)
|
||||
}
|
||||
LocalVariableDefinition() { this = TLocalVariableDefinition(lvde) }
|
||||
|
||||
/** Gets the underlying local variable declaration. */
|
||||
LocalVariableDeclExpr getDeclaration() {
|
||||
result = lvde
|
||||
}
|
||||
LocalVariableDeclExpr getDeclaration() { result = lvde }
|
||||
|
||||
override Expr getExpr() { result = lvde }
|
||||
|
||||
override string toString() {
|
||||
result = lvde.toString()
|
||||
}
|
||||
override string toString() { result = lvde.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -702,32 +629,20 @@ module AssignableDefinitions {
|
||||
class ImplicitParameterDefinition extends AssignableDefinition, TImplicitParameterDefinition {
|
||||
Parameter p;
|
||||
|
||||
ImplicitParameterDefinition() {
|
||||
this = TImplicitParameterDefinition(p)
|
||||
}
|
||||
ImplicitParameterDefinition() { this = TImplicitParameterDefinition(p) }
|
||||
|
||||
/** Gets the underlying parameter. */
|
||||
Parameter getParameter() {
|
||||
result = p
|
||||
}
|
||||
Parameter getParameter() { result = p }
|
||||
|
||||
override ControlFlow::Node getAControlFlowNode() {
|
||||
result = p.getCallable().getEntryPoint()
|
||||
}
|
||||
override ControlFlow::Node getAControlFlowNode() { result = p.getCallable().getEntryPoint() }
|
||||
|
||||
override Parameter getElement() { result = p }
|
||||
|
||||
override Callable getEnclosingCallable() {
|
||||
result = p.getCallable()
|
||||
}
|
||||
override Callable getEnclosingCallable() { result = p.getCallable() }
|
||||
|
||||
override string toString() {
|
||||
result = p.toString()
|
||||
}
|
||||
override string toString() { result = p.toString() }
|
||||
|
||||
override Location getLocation() {
|
||||
result = this.getTarget().getLocation()
|
||||
}
|
||||
override Location getLocation() { result = this.getTarget().getLocation() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -736,20 +651,14 @@ module AssignableDefinitions {
|
||||
class AddressOfDefinition extends AssignableDefinition, TAddressOfDefinition {
|
||||
AddressOfExpr aoe;
|
||||
|
||||
AddressOfDefinition() {
|
||||
this = TAddressOfDefinition(aoe)
|
||||
}
|
||||
AddressOfDefinition() { this = TAddressOfDefinition(aoe) }
|
||||
|
||||
/** Gets the underlying address-of expression. */
|
||||
AddressOfExpr getAddressOf() {
|
||||
result = aoe
|
||||
}
|
||||
AddressOfExpr getAddressOf() { result = aoe }
|
||||
|
||||
override Expr getExpr() { result = aoe }
|
||||
|
||||
override string toString() {
|
||||
result = aoe.toString()
|
||||
}
|
||||
override string toString() { result = aoe.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -758,24 +667,16 @@ module AssignableDefinitions {
|
||||
class IsPatternDefinition extends AssignableDefinition, TIsPatternDefinition {
|
||||
IsPatternExpr ipe;
|
||||
|
||||
IsPatternDefinition() {
|
||||
this = TIsPatternDefinition(ipe)
|
||||
}
|
||||
IsPatternDefinition() { this = TIsPatternDefinition(ipe) }
|
||||
|
||||
/** Gets the underlying local variable declaration. */
|
||||
LocalVariableDeclExpr getDeclaration() {
|
||||
result = ipe.getVariableDeclExpr()
|
||||
}
|
||||
LocalVariableDeclExpr getDeclaration() { result = ipe.getVariableDeclExpr() }
|
||||
|
||||
override Expr getExpr() { result = this.getDeclaration() }
|
||||
|
||||
override Expr getSource() {
|
||||
result = ipe.getExpr()
|
||||
}
|
||||
override Expr getSource() { result = ipe.getExpr() }
|
||||
|
||||
override string toString() {
|
||||
result = this.getDeclaration().toString()
|
||||
}
|
||||
override string toString() { result = this.getDeclaration().toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -793,14 +694,10 @@ module AssignableDefinitions {
|
||||
class TypeCasePatternDefinition extends AssignableDefinition, TTypeCasePatternDefinition {
|
||||
TypeCase tc;
|
||||
|
||||
TypeCasePatternDefinition() {
|
||||
this = TTypeCasePatternDefinition(tc)
|
||||
}
|
||||
TypeCasePatternDefinition() { this = TTypeCasePatternDefinition(tc) }
|
||||
|
||||
/** Gets the underlying local variable declaration. */
|
||||
LocalVariableDeclExpr getDeclaration() {
|
||||
result = tc.getVariableDeclExpr()
|
||||
}
|
||||
LocalVariableDeclExpr getDeclaration() { result = tc.getVariableDeclExpr() }
|
||||
|
||||
override Expr getExpr() { result = this.getDeclaration() }
|
||||
|
||||
@@ -808,9 +705,7 @@ module AssignableDefinitions {
|
||||
result = any(SwitchStmt ss | ss.getATypeCase() = tc).getCondition()
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = this.getDeclaration().toString()
|
||||
}
|
||||
override string toString() { result = this.getDeclaration().toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -825,27 +720,18 @@ module AssignableDefinitions {
|
||||
*/
|
||||
class InitializerDefinition extends AssignableDefinition, TInitializer {
|
||||
Assignable a;
|
||||
|
||||
Expr e;
|
||||
|
||||
InitializerDefinition() {
|
||||
this = TInitializer(a, e)
|
||||
}
|
||||
InitializerDefinition() { this = TInitializer(a, e) }
|
||||
|
||||
/** Gets the assignable (field or property) being initialized. */
|
||||
Assignable getAssignable() {
|
||||
result = a
|
||||
}
|
||||
Assignable getAssignable() { result = a }
|
||||
|
||||
override Expr getSource() {
|
||||
result = e
|
||||
}
|
||||
override Expr getSource() { result = e }
|
||||
|
||||
override string toString() {
|
||||
result = e.toString()
|
||||
}
|
||||
override string toString() { result = e.toString() }
|
||||
|
||||
override Location getLocation() {
|
||||
result = e.getLocation()
|
||||
}
|
||||
override Location getLocation() { result = e.getLocation() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,7 @@ private import semmle.code.csharp.ExprOrStmtParent
|
||||
* a destructor (`Destructor`), a callable accessor (`CallableAccessor`), a value or reference type
|
||||
* (`ValueOrRefType`), or a declaration with accessors (`DeclarationWithAccessors`).
|
||||
*/
|
||||
class Attributable extends @attributable
|
||||
{
|
||||
class Attributable extends @attributable {
|
||||
/** Gets an attribute attached to this element, if any. */
|
||||
final Attribute getAnAttribute() { result.getTarget() = this }
|
||||
|
||||
@@ -36,7 +35,6 @@ class Attributable extends @attributable
|
||||
* ```
|
||||
*/
|
||||
class Attribute extends TopLevelExprParent, @attribute {
|
||||
|
||||
/** Gets the type of this attribute. */
|
||||
Class getType() { attributes(this, getTypeRef(result), _) }
|
||||
|
||||
@@ -49,13 +47,8 @@ class Attribute extends TopLevelExprParent, @attribute {
|
||||
override Location getALocation() { attribute_location(this, result) }
|
||||
|
||||
override string toString() {
|
||||
exists(string type, string name |
|
||||
type = getType().toString() |
|
||||
if type.matches("%Attribute") then
|
||||
name = type.prefix(type.length() - 9)
|
||||
else
|
||||
name = type
|
||||
and
|
||||
exists(string type, string name | type = getType().toString() |
|
||||
(if type.matches("%Attribute") then name = type.prefix(type.length() - 9) else name = type) and
|
||||
result = "[" + name + "(...)]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -28,9 +28,7 @@ class Callable extends DotNet::Callable, Parameterizable, ExprOrStmtParent, @cal
|
||||
/** Holds if this callable returns a `ref readonly`. */
|
||||
predicate returnsRefReadonly() { ref_readonly_returns(this) }
|
||||
|
||||
override Callable getSourceDeclaration() {
|
||||
result = Parameterizable.super.getSourceDeclaration()
|
||||
}
|
||||
override Callable getSourceDeclaration() { result = Parameterizable.super.getSourceDeclaration() }
|
||||
|
||||
/**
|
||||
* Gets the body of this callable, if any.
|
||||
@@ -88,9 +86,7 @@ class Callable extends DotNet::Callable, Parameterizable, ExprOrStmtParent, @cal
|
||||
}
|
||||
|
||||
/** Gets the statement body of this callable, if any. */
|
||||
final BlockStmt getStatementBody() {
|
||||
result = this.getAChildStmt()
|
||||
}
|
||||
final BlockStmt getStatementBody() { result = this.getAChildStmt() }
|
||||
|
||||
/**
|
||||
* Gets a statement body of this callable, if any.
|
||||
@@ -120,17 +116,13 @@ 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()`.
|
||||
*/
|
||||
final BlockStmt getAStatementBody() {
|
||||
stmt_parent_top_level(result, _, this)
|
||||
}
|
||||
final BlockStmt getAStatementBody() { stmt_parent_top_level(result, _, this) }
|
||||
|
||||
/** 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)
|
||||
}
|
||||
final Expr getExpressionBody() { result = this.getChildExpr(0) }
|
||||
|
||||
/**
|
||||
* Gets an expression body of this callable (if any), specified by `=>`.
|
||||
@@ -159,22 +151,16 @@ class Callable extends DotNet::Callable, Parameterizable, ExprOrStmtParent, @cal
|
||||
*
|
||||
* to the same assembly, 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 getAnExpressionBody() { expr_parent_top_level_adjusted(result, 0, this) }
|
||||
|
||||
/** Holds if this callable has an expression body. */
|
||||
final predicate hasExpressionBody() { exists(getExpressionBody()) }
|
||||
|
||||
/** Gets the entry point in the control graph for this callable. */
|
||||
ControlFlow::Nodes::EntryNode getEntryPoint() {
|
||||
result.getCallable() = this
|
||||
}
|
||||
ControlFlow::Nodes::EntryNode getEntryPoint() { result.getCallable() = this }
|
||||
|
||||
/** Gets the exit point in the control graph for this callable. */
|
||||
ControlFlow::Nodes::ExitNode getExitPoint() {
|
||||
result.getCallable() = this
|
||||
}
|
||||
ControlFlow::Nodes::ExitNode getExitPoint() { result.getCallable() = this }
|
||||
|
||||
/**
|
||||
* Gets the enclosing callable of this callable, if any.
|
||||
@@ -214,40 +200,26 @@ class Callable extends DotNet::Callable, Parameterizable, ExprOrStmtParent, @cal
|
||||
}
|
||||
|
||||
override predicate canReturn(DotNet::Expr e) {
|
||||
exists(ReturnStmt ret |
|
||||
ret.getEnclosingCallable() = this |
|
||||
e = ret.getExpr()
|
||||
)
|
||||
exists(ReturnStmt ret | ret.getEnclosingCallable() = this | e = ret.getExpr())
|
||||
or
|
||||
e = getExpressionBody()
|
||||
}
|
||||
|
||||
/** Holds if this callable can yield return the expression `e`. */
|
||||
predicate canYieldReturn(Expr e) {
|
||||
exists(YieldReturnStmt yield |
|
||||
yield.getEnclosingCallable() = this |
|
||||
e = yield.getExpr()
|
||||
)
|
||||
exists(YieldReturnStmt yield | yield.getEnclosingCallable() = this | e = yield.getExpr())
|
||||
}
|
||||
|
||||
override string toStringWithTypes() {
|
||||
result = getName() + "(" + parameterTypesToString() + ")"
|
||||
}
|
||||
override string toStringWithTypes() { result = getName() + "(" + parameterTypesToString() + ")" }
|
||||
|
||||
/** Gets a `Call` that has this callable as a target. */
|
||||
Call getACall() { this = result.getTarget() }
|
||||
|
||||
override Parameter getParameter(int n) {
|
||||
result = Parameterizable.super.getParameter(n)
|
||||
}
|
||||
override Parameter getParameter(int n) { result = Parameterizable.super.getParameter(n) }
|
||||
|
||||
override Parameter getAParameter() {
|
||||
result = Parameterizable.super.getAParameter()
|
||||
}
|
||||
override Parameter getAParameter() { result = Parameterizable.super.getAParameter() }
|
||||
|
||||
override int getNumberOfParameters() {
|
||||
result = Parameterizable.super.getNumberOfParameters()
|
||||
}
|
||||
override int getNumberOfParameters() { result = Parameterizable.super.getNumberOfParameters() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -260,31 +232,22 @@ class Callable extends DotNet::Callable, Parameterizable, ExprOrStmtParent, @cal
|
||||
* ```
|
||||
*/
|
||||
class Method extends Callable, Virtualizable, Attributable, @method {
|
||||
|
||||
/** Gets the name of this method. */
|
||||
override string getName() { methods(this,result,_,_,_) }
|
||||
override string getName() { methods(this, result, _, _, _) }
|
||||
|
||||
override ValueOrRefType getDeclaringType() { methods(this,_,result,_,_) }
|
||||
override ValueOrRefType getDeclaringType() { methods(this, _, result, _, _) }
|
||||
|
||||
override Type getReturnType() { methods(this,_,_,getTypeRef(result),_) }
|
||||
override Type getReturnType() { methods(this, _, _, getTypeRef(result), _) }
|
||||
|
||||
override Method getSourceDeclaration() { methods(this,_,_,_,result) }
|
||||
override Method getSourceDeclaration() { methods(this, _, _, _, result) }
|
||||
|
||||
override Method getOverridee() {
|
||||
result = Virtualizable.super.getOverridee()
|
||||
}
|
||||
override Method getOverridee() { result = Virtualizable.super.getOverridee() }
|
||||
|
||||
override Method getAnOverrider() {
|
||||
result = Virtualizable.super.getAnOverrider()
|
||||
}
|
||||
override Method getAnOverrider() { result = Virtualizable.super.getAnOverrider() }
|
||||
|
||||
override Method getImplementee() {
|
||||
result = Virtualizable.super.getImplementee()
|
||||
}
|
||||
override Method getImplementee() { result = Virtualizable.super.getImplementee() }
|
||||
|
||||
override Method getAnImplementor() {
|
||||
result = Virtualizable.super.getAnImplementor()
|
||||
}
|
||||
override Method getAnImplementor() { result = Virtualizable.super.getAnImplementor() }
|
||||
|
||||
override Method getAnUltimateImplementee() {
|
||||
result = Virtualizable.super.getAnUltimateImplementee()
|
||||
@@ -297,14 +260,11 @@ class Method extends Callable, Virtualizable, Attributable, @method {
|
||||
override Location getALocation() { method_location(this, result) }
|
||||
|
||||
/** Holds if this method is an extension method. */
|
||||
predicate isExtensionMethod() {
|
||||
getParameter(0).hasExtensionMethodModifier()
|
||||
}
|
||||
predicate isExtensionMethod() { getParameter(0).hasExtensionMethodModifier() }
|
||||
|
||||
/** Gets the type of the `params` parameter of this method, if any. */
|
||||
Type getParamsType() {
|
||||
exists(Parameter last |
|
||||
last = getParameter(getNumberOfParameters() - 1) |
|
||||
exists(Parameter last | last = getParameter(getNumberOfParameters() - 1) |
|
||||
last.isParams() and
|
||||
result = last.getType().(ArrayType).getElementType()
|
||||
)
|
||||
@@ -314,9 +274,7 @@ class Method extends Callable, Virtualizable, Attributable, @method {
|
||||
predicate hasParams() { exists(getParamsType()) }
|
||||
|
||||
// Remove when `Callable.isOverridden()` is removed
|
||||
override predicate isOverridden() {
|
||||
Virtualizable.super.isOverridden()
|
||||
}
|
||||
override predicate isOverridden() { Virtualizable.super.isOverridden() }
|
||||
|
||||
override predicate fromSource() {
|
||||
Callable.super.fromSource() and
|
||||
@@ -328,9 +286,7 @@ class Method extends Callable, Virtualizable, Attributable, @method {
|
||||
override Location getLocation() { result = Callable.super.getLocation() }
|
||||
|
||||
override Parameter getRawParameter(int i) {
|
||||
if this.isStatic()
|
||||
then result = this.getParameter(i)
|
||||
else result = this.getParameter(i-1)
|
||||
if this.isStatic() then result = this.getParameter(i) else result = this.getParameter(i - 1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,13 +300,9 @@ class Method extends Callable, Virtualizable, Attributable, @method {
|
||||
* ```
|
||||
*/
|
||||
class ExtensionMethod extends Method {
|
||||
ExtensionMethod() {
|
||||
this.isExtensionMethod()
|
||||
}
|
||||
ExtensionMethod() { this.isExtensionMethod() }
|
||||
|
||||
override predicate isStatic() {
|
||||
any()
|
||||
}
|
||||
override predicate isStatic() { any() }
|
||||
|
||||
/** Gets the type being extended by this method. */
|
||||
Type getExtendedType() { result = getParameter(0).getType() }
|
||||
@@ -366,8 +318,7 @@ class ExtensionMethod extends Method {
|
||||
* ```
|
||||
*/
|
||||
class Constructor extends DotNet::Constructor, Callable, Member, Attributable, @constructor {
|
||||
|
||||
override string getName() { constructors(this,result,_,_) }
|
||||
override string getName() { constructors(this, result, _, _) }
|
||||
|
||||
override Type getReturnType() {
|
||||
exists(this) and // needed to avoid compiler warning
|
||||
@@ -393,9 +344,9 @@ class Constructor extends DotNet::Constructor, Callable, Member, Attributable, @
|
||||
/** Holds if this constructor has an initializer. */
|
||||
predicate hasInitializer() { exists(getInitializer()) }
|
||||
|
||||
override ValueOrRefType getDeclaringType() { constructors(this,_,result,_) }
|
||||
override ValueOrRefType getDeclaringType() { constructors(this, _, result, _) }
|
||||
|
||||
override Constructor getSourceDeclaration() { constructors(this,_,_,result) }
|
||||
override Constructor getSourceDeclaration() { constructors(this, _, _, result) }
|
||||
|
||||
override Location getALocation() { constructor_location(this, result) }
|
||||
|
||||
@@ -406,9 +357,7 @@ class Constructor extends DotNet::Constructor, Callable, Member, Attributable, @
|
||||
override Location getLocation() { result = Callable.super.getLocation() }
|
||||
|
||||
override Parameter getRawParameter(int i) {
|
||||
if this.isStatic()
|
||||
then result = this.getParameter(i)
|
||||
else result = this.getParameter(i-1)
|
||||
if this.isStatic() then result = this.getParameter(i) else result = this.getParameter(i - 1)
|
||||
}
|
||||
|
||||
override string getUndecoratedName() { result = ".ctor" }
|
||||
@@ -425,7 +374,6 @@ class Constructor extends DotNet::Constructor, Callable, Member, Attributable, @
|
||||
* ```
|
||||
*/
|
||||
class StaticConstructor extends Constructor {
|
||||
|
||||
StaticConstructor() { this.isStatic() }
|
||||
|
||||
override string getUndecoratedName() { result = ".cctor" }
|
||||
@@ -441,10 +389,7 @@ class StaticConstructor extends Constructor {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class InstanceConstructor extends Constructor {
|
||||
|
||||
InstanceConstructor() { not this.isStatic() }
|
||||
}
|
||||
class InstanceConstructor extends Constructor { InstanceConstructor() { not this.isStatic() } }
|
||||
|
||||
/**
|
||||
* A destructor, for example `~C() { }` on line 2 in
|
||||
@@ -456,8 +401,7 @@ class InstanceConstructor extends Constructor {
|
||||
* ```
|
||||
*/
|
||||
class Destructor extends DotNet::Destructor, Callable, Member, Attributable, @destructor {
|
||||
|
||||
override string getName() { destructors(this,result,_,_) }
|
||||
override string getName() { destructors(this, result, _, _) }
|
||||
|
||||
override Type getReturnType() {
|
||||
exists(this) and // needed to avoid compiler warning
|
||||
@@ -466,9 +410,9 @@ class Destructor extends DotNet::Destructor, Callable, Member, Attributable, @de
|
||||
|
||||
override string getUndecoratedName() { result = "Finalize" }
|
||||
|
||||
override ValueOrRefType getDeclaringType() { destructors(this,_,result,_) }
|
||||
override ValueOrRefType getDeclaringType() { destructors(this, _, result, _) }
|
||||
|
||||
override Destructor getSourceDeclaration() { destructors(this,_,_,result) }
|
||||
override Destructor getSourceDeclaration() { destructors(this, _, _, result) }
|
||||
|
||||
override Location getALocation() { destructor_location(this, result) }
|
||||
|
||||
@@ -484,19 +428,18 @@ class Destructor extends DotNet::Destructor, Callable, Member, Attributable, @de
|
||||
* (`BinaryOperator`), or a conversion operator (`ConversionOperator`).
|
||||
*/
|
||||
class Operator extends Callable, Member, Attributable, @operator {
|
||||
|
||||
/** Gets the assembly name of this operator. */
|
||||
string getAssemblyName() { operators(this,result,_,_,_,_) }
|
||||
string getAssemblyName() { operators(this, result, _, _, _, _) }
|
||||
|
||||
override string getName() { operators(this,_,result,_,_,_) }
|
||||
override string getName() { operators(this, _, result, _, _, _) }
|
||||
|
||||
string getFunctionName() { none() }
|
||||
|
||||
override ValueOrRefType getDeclaringType() { operators(this,_,_,result,_,_) }
|
||||
override ValueOrRefType getDeclaringType() { operators(this, _, _, result, _, _) }
|
||||
|
||||
override Type getReturnType() { operators(this,_,_,_,getTypeRef(result),_) }
|
||||
override Type getReturnType() { operators(this, _, _, _, getTypeRef(result), _) }
|
||||
|
||||
override Operator getSourceDeclaration() { operators(this,_,_,_,_,result) }
|
||||
override Operator getSourceDeclaration() { operators(this, _, _, _, _, result) }
|
||||
|
||||
override Location getALocation() { operator_location(this, result) }
|
||||
|
||||
@@ -507,7 +450,6 @@ class Operator extends Callable, Member, Attributable, @operator {
|
||||
override Parameter getRawParameter(int i) { result = getParameter(i) }
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A user-defined unary operator - an operator taking one operand.
|
||||
*
|
||||
@@ -518,7 +460,6 @@ class Operator extends Callable, Member, Attributable, @operator {
|
||||
* (`DecrementOperator`).
|
||||
*/
|
||||
class UnaryOperator extends Operator {
|
||||
|
||||
UnaryOperator() {
|
||||
this.getNumberOfParameters() = 1 and
|
||||
not this instanceof ConversionOperator
|
||||
@@ -535,7 +476,6 @@ class UnaryOperator extends Operator {
|
||||
* ```
|
||||
*/
|
||||
class PlusOperator extends UnaryOperator {
|
||||
|
||||
PlusOperator() { this.getName() = "+" }
|
||||
|
||||
override string getFunctionName() { result = "op_UnaryPlus" }
|
||||
@@ -551,7 +491,6 @@ class PlusOperator extends UnaryOperator {
|
||||
* ```
|
||||
*/
|
||||
class MinusOperator extends UnaryOperator {
|
||||
|
||||
MinusOperator() { this.getName() = "-" }
|
||||
|
||||
override string getFunctionName() { result = "op_UnaryNegation" }
|
||||
@@ -567,7 +506,6 @@ class MinusOperator extends UnaryOperator {
|
||||
* ```
|
||||
*/
|
||||
class NotOperator extends UnaryOperator {
|
||||
|
||||
NotOperator() { this.getName() = "!" }
|
||||
|
||||
override string getFunctionName() { result = "op_LogicalNot" }
|
||||
@@ -583,7 +521,6 @@ class NotOperator extends UnaryOperator {
|
||||
* ```
|
||||
*/
|
||||
class ComplementOperator extends UnaryOperator {
|
||||
|
||||
ComplementOperator() { this.getName() = "~" }
|
||||
|
||||
override string getFunctionName() { result = "op_OnesComplement" }
|
||||
@@ -599,7 +536,6 @@ class ComplementOperator extends UnaryOperator {
|
||||
* ```
|
||||
*/
|
||||
class IncrementOperator extends UnaryOperator {
|
||||
|
||||
IncrementOperator() { this.getName() = "++" }
|
||||
|
||||
override string getFunctionName() { result = "op_Increment" }
|
||||
@@ -615,7 +551,6 @@ class IncrementOperator extends UnaryOperator {
|
||||
* ```
|
||||
*/
|
||||
class DecrementOperator extends UnaryOperator {
|
||||
|
||||
DecrementOperator() { this.getName() = "--" }
|
||||
|
||||
override string getFunctionName() { result = "op_Decrement" }
|
||||
@@ -631,7 +566,6 @@ class DecrementOperator extends UnaryOperator {
|
||||
* ```
|
||||
*/
|
||||
class FalseOperator extends UnaryOperator {
|
||||
|
||||
FalseOperator() { this.getName() = "false" }
|
||||
|
||||
override string getFunctionName() { result = "op_False" }
|
||||
@@ -647,7 +581,6 @@ class FalseOperator extends UnaryOperator {
|
||||
* ```
|
||||
*/
|
||||
class TrueOperator extends UnaryOperator {
|
||||
|
||||
TrueOperator() { this.getName() = "true" }
|
||||
|
||||
override string getFunctionName() { result = "op_True" }
|
||||
@@ -666,10 +599,7 @@ class TrueOperator extends UnaryOperator {
|
||||
* a greater than operator (`GTOperator`), a less than or equals operator
|
||||
* (`LEOperator`), or a greater than or equals operator (`GEOperator`).
|
||||
*/
|
||||
class BinaryOperator extends Operator {
|
||||
|
||||
BinaryOperator() { this.getNumberOfParameters() = 2 }
|
||||
}
|
||||
class BinaryOperator extends Operator { BinaryOperator() { this.getNumberOfParameters() = 2 } }
|
||||
|
||||
/**
|
||||
* A user-defined addition operator (`+`), for example
|
||||
@@ -681,7 +611,6 @@ class BinaryOperator extends Operator {
|
||||
* ```
|
||||
*/
|
||||
class AddOperator extends BinaryOperator {
|
||||
|
||||
AddOperator() { this.getName() = "+" }
|
||||
|
||||
override string getFunctionName() { result = "op_Addition" }
|
||||
@@ -697,7 +626,6 @@ class AddOperator extends BinaryOperator {
|
||||
* ```
|
||||
*/
|
||||
class SubOperator extends BinaryOperator {
|
||||
|
||||
SubOperator() { this.getName() = "-" }
|
||||
|
||||
override string getFunctionName() { result = "op_Subtraction" }
|
||||
@@ -713,7 +641,6 @@ class SubOperator extends BinaryOperator {
|
||||
* ```
|
||||
*/
|
||||
class MulOperator extends BinaryOperator {
|
||||
|
||||
MulOperator() { this.getName() = "*" }
|
||||
|
||||
override string getFunctionName() { result = "op_Multiply" }
|
||||
@@ -729,7 +656,6 @@ class MulOperator extends BinaryOperator {
|
||||
* ```
|
||||
*/
|
||||
class DivOperator extends BinaryOperator {
|
||||
|
||||
DivOperator() { this.getName() = "/" }
|
||||
|
||||
override string getFunctionName() { result = "op_Division" }
|
||||
@@ -745,7 +671,6 @@ class DivOperator extends BinaryOperator {
|
||||
* ```
|
||||
*/
|
||||
class RemOperator extends BinaryOperator {
|
||||
|
||||
RemOperator() { this.getName() = "%" }
|
||||
|
||||
override string getFunctionName() { result = "op_Modulus" }
|
||||
@@ -761,7 +686,6 @@ class RemOperator extends BinaryOperator {
|
||||
* ```
|
||||
*/
|
||||
class AndOperator extends BinaryOperator {
|
||||
|
||||
AndOperator() { this.getName() = "&" }
|
||||
|
||||
override string getFunctionName() { result = "op_BitwiseAnd" }
|
||||
@@ -777,7 +701,6 @@ class AndOperator extends BinaryOperator {
|
||||
* ```
|
||||
*/
|
||||
class OrOperator extends BinaryOperator {
|
||||
|
||||
OrOperator() { this.getName() = "|" }
|
||||
|
||||
override string getFunctionName() { result = "op_BitwiseOr" }
|
||||
@@ -793,7 +716,6 @@ class OrOperator extends BinaryOperator {
|
||||
* ```
|
||||
*/
|
||||
class XorOperator extends BinaryOperator {
|
||||
|
||||
XorOperator() { this.getName() = "^" }
|
||||
|
||||
override string getFunctionName() { result = "op_ExclusiveOr" }
|
||||
@@ -809,7 +731,6 @@ class XorOperator extends BinaryOperator {
|
||||
* ```
|
||||
*/
|
||||
class LShiftOperator extends BinaryOperator {
|
||||
|
||||
LShiftOperator() { this.getName() = "<<" }
|
||||
|
||||
override string getFunctionName() { result = "op_LeftShift" }
|
||||
@@ -825,7 +746,6 @@ class LShiftOperator extends BinaryOperator {
|
||||
* ```
|
||||
*/
|
||||
class RShiftOperator extends BinaryOperator {
|
||||
|
||||
RShiftOperator() { this.getName() = ">>" }
|
||||
|
||||
override string getFunctionName() { result = "op_RightShift" }
|
||||
@@ -841,7 +761,6 @@ class RShiftOperator extends BinaryOperator {
|
||||
* ```
|
||||
*/
|
||||
class EQOperator extends BinaryOperator {
|
||||
|
||||
EQOperator() { this.getName() = "==" }
|
||||
|
||||
override string getFunctionName() { result = "op_Equality" }
|
||||
@@ -857,7 +776,6 @@ class EQOperator extends BinaryOperator {
|
||||
* ```
|
||||
*/
|
||||
class NEOperator extends BinaryOperator {
|
||||
|
||||
NEOperator() { this.getName() = "!=" }
|
||||
|
||||
override string getFunctionName() { result = "op_Inequality" }
|
||||
@@ -873,7 +791,6 @@ class NEOperator extends BinaryOperator {
|
||||
* ```
|
||||
*/
|
||||
class LTOperator extends BinaryOperator {
|
||||
|
||||
LTOperator() { this.getName() = "<" }
|
||||
|
||||
override string getFunctionName() { result = "op_LessThan" }
|
||||
@@ -889,7 +806,6 @@ class LTOperator extends BinaryOperator {
|
||||
* ```
|
||||
*/
|
||||
class GTOperator extends BinaryOperator {
|
||||
|
||||
GTOperator() { this.getName() = ">" }
|
||||
|
||||
override string getFunctionName() { result = "op_GreaterThan" }
|
||||
@@ -905,7 +821,6 @@ class GTOperator extends BinaryOperator {
|
||||
* ```
|
||||
*/
|
||||
class LEOperator extends BinaryOperator {
|
||||
|
||||
LEOperator() { this.getName() = "<=" }
|
||||
|
||||
override string getFunctionName() { result = "op_LessThanOrEqual" }
|
||||
@@ -921,7 +836,6 @@ class LEOperator extends BinaryOperator {
|
||||
* ```
|
||||
*/
|
||||
class GEOperator extends BinaryOperator {
|
||||
|
||||
GEOperator() { this.getName() = ">=" }
|
||||
|
||||
override string getFunctionName() { result = "op_GreaterThanOrEqual" }
|
||||
@@ -937,7 +851,6 @@ class GEOperator extends BinaryOperator {
|
||||
* ```
|
||||
*/
|
||||
class ConversionOperator extends Operator {
|
||||
|
||||
ConversionOperator() {
|
||||
this.getName() = "implicit conversion" or
|
||||
this.getName() = "explicit conversion"
|
||||
@@ -960,10 +873,7 @@ class ConversionOperator extends Operator {
|
||||
* ```
|
||||
*/
|
||||
class ImplicitConversionOperator extends ConversionOperator {
|
||||
|
||||
ImplicitConversionOperator() {
|
||||
this.getName() = "implicit conversion"
|
||||
}
|
||||
ImplicitConversionOperator() { this.getName() = "implicit conversion" }
|
||||
|
||||
override string getFunctionName() { result = "op_Implicit" }
|
||||
}
|
||||
@@ -978,10 +888,7 @@ class ImplicitConversionOperator extends ConversionOperator {
|
||||
* ```
|
||||
*/
|
||||
class ExplicitConversionOperator extends ConversionOperator {
|
||||
|
||||
ExplicitConversionOperator() {
|
||||
this.getName() = "explicit conversion"
|
||||
}
|
||||
ExplicitConversionOperator() { this.getName() = "explicit conversion" }
|
||||
|
||||
override string getFunctionName() { result = "op_Explicit" }
|
||||
}
|
||||
@@ -1001,41 +908,25 @@ class ExplicitConversionOperator extends ConversionOperator {
|
||||
* ```
|
||||
*/
|
||||
class LocalFunction extends Callable, @local_function {
|
||||
override string getName() {
|
||||
local_functions(this, result, _, _)
|
||||
}
|
||||
override string getName() { local_functions(this, result, _, _) }
|
||||
|
||||
override LocalFunction getSourceDeclaration() {
|
||||
local_functions(this, _, _, result)
|
||||
}
|
||||
override LocalFunction getSourceDeclaration() { local_functions(this, _, _, result) }
|
||||
|
||||
override Type getReturnType() {
|
||||
local_functions(this, _, result, _)
|
||||
}
|
||||
override Type getReturnType() { local_functions(this, _, result, _) }
|
||||
|
||||
override Element getParent() {
|
||||
result = getStatement().getParent()
|
||||
}
|
||||
override Element getParent() { result = getStatement().getParent() }
|
||||
|
||||
/** Gets the local function statement defining this function. */
|
||||
LocalFunctionStmt getStatement() {
|
||||
result.getLocalFunction() = getSourceDeclaration()
|
||||
}
|
||||
LocalFunctionStmt getStatement() { result.getLocalFunction() = getSourceDeclaration() }
|
||||
|
||||
override Callable getEnclosingCallable() {
|
||||
result = this.getStatement().getEnclosingCallable()
|
||||
}
|
||||
override Callable getEnclosingCallable() { result = this.getStatement().getEnclosingCallable() }
|
||||
|
||||
override predicate hasQualifiedName(string qualifier, string name) {
|
||||
qualifier = this.getEnclosingCallable().getQualifiedName() and
|
||||
name = this.getName()
|
||||
}
|
||||
|
||||
override Location getALocation() {
|
||||
result = getStatement().getALocation()
|
||||
}
|
||||
override Location getALocation() { result = getStatement().getALocation() }
|
||||
|
||||
override Parameter getRawParameter(int i) {
|
||||
result = getParameter(i)
|
||||
}
|
||||
override Parameter getRawParameter(int i) { result = getParameter(i) }
|
||||
}
|
||||
|
||||
@@ -19,20 +19,14 @@ import csharp
|
||||
* callable), the return type of `c` must be a subtype of `c`'s
|
||||
* declaring type, and `c` must be non-static.
|
||||
*/
|
||||
predicate designedForChaining(Callable c) {
|
||||
not nonChaining(c)
|
||||
}
|
||||
predicate designedForChaining(Callable c) { not nonChaining(c) }
|
||||
|
||||
private predicate nonChaining(Callable c) {
|
||||
exists(Type t | t = c.getDeclaringType() | not t instanceof RefType)
|
||||
or
|
||||
exists(Method override |
|
||||
override.getOverridee() = c |
|
||||
nonChaining(override)
|
||||
)
|
||||
exists(Method override | override.getOverridee() = c | nonChaining(override))
|
||||
or
|
||||
exists(Getter override |
|
||||
override.getDeclaration().getOverridee() = c.(Getter).getDeclaration() |
|
||||
exists(Getter override | override.getDeclaration().getOverridee() = c.(Getter).getDeclaration() |
|
||||
nonChaining(override)
|
||||
)
|
||||
or
|
||||
@@ -47,25 +41,24 @@ private predicate nonChainingBody(Callable c) {
|
||||
}
|
||||
|
||||
private Expr returnedValue(Callable c) {
|
||||
exists(Expr e |
|
||||
c.canReturn(e) |
|
||||
if e instanceof ConditionalExpr then
|
||||
exists(Expr e | c.canReturn(e) |
|
||||
if e instanceof ConditionalExpr
|
||||
then
|
||||
result = e.(ConditionalExpr).getThen() or
|
||||
result = e.(ConditionalExpr).getElse()
|
||||
else
|
||||
result = e
|
||||
else result = e
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `c` can return a non-`this` value. */
|
||||
private predicate nonChainingReturn(Callable c) {
|
||||
exists(Expr ret |
|
||||
ret = returnedValue(c) |
|
||||
exists(Expr ret | ret = returnedValue(c) |
|
||||
// The result of a call is returned
|
||||
exists(Callable other |
|
||||
other = ret.(Call).getTarget() |
|
||||
nonChaining(other) or
|
||||
exists(MethodCall mc | mc = ret | not mc.hasThisQualifier()) or
|
||||
exists(Callable other | other = ret.(Call).getTarget() |
|
||||
nonChaining(other)
|
||||
or
|
||||
exists(MethodCall mc | mc = ret | not mc.hasThisQualifier())
|
||||
or
|
||||
exists(MemberAccess ma | ma = ret | not ma.hasThisQualifier())
|
||||
)
|
||||
or
|
||||
|
||||
@@ -14,22 +14,21 @@ import Location
|
||||
* Either a single line comment (`SingleLineComment`), an XML comment (`XmlComment`),
|
||||
* or a line in a multi-line comment (`MultilineComment`).
|
||||
*/
|
||||
class CommentLine extends @commentline
|
||||
{
|
||||
class CommentLine extends @commentline {
|
||||
/** Gets a textual representation of this comment line. */
|
||||
string toString() { none() }
|
||||
|
||||
/** Gets the location of this comment line. */
|
||||
Location getLocation() { commentline_location(this,result) }
|
||||
Location getLocation() { commentline_location(this, result) }
|
||||
|
||||
/** Gets the containing comment block. */
|
||||
CommentBlock getParent() { result.getAChild() = this }
|
||||
|
||||
/** Gets the text in the comment, trimmed to remove comment markers and leading and trailing whitespace. */
|
||||
string getText() { commentline(this,_,result,_) }
|
||||
string getText() { commentline(this, _, result, _) }
|
||||
|
||||
/** Gets the raw text of the comment, including the comment markers. */
|
||||
string getRawText() { commentline(this,_,_,result) }
|
||||
string getRawText() { commentline(this, _, _, result) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -40,9 +39,8 @@ class CommentLine extends @commentline
|
||||
* public int Succ(int x) => x + 1;
|
||||
* ```
|
||||
*/
|
||||
class SinglelineComment extends CommentLine, @singlelinecomment
|
||||
{
|
||||
override string toString() { result="// ..." }
|
||||
class SinglelineComment extends CommentLine, @singlelinecomment {
|
||||
override string toString() { result = "// ..." }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -54,9 +52,8 @@ class SinglelineComment extends CommentLine, @singlelinecomment
|
||||
* a comment * /
|
||||
* ```
|
||||
*/
|
||||
class MultilineComment extends CommentLine, @multilinecomment
|
||||
{
|
||||
override string toString() { result="/* ... */" }
|
||||
class MultilineComment extends CommentLine, @multilinecomment {
|
||||
override string toString() { result = "/* ... */" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,79 +66,79 @@ class MultilineComment extends CommentLine, @multilinecomment
|
||||
* /// </summary>
|
||||
* ```
|
||||
*/
|
||||
class XmlComment extends CommentLine, @xmldoccomment
|
||||
{
|
||||
override string toString() { result="/// ..." }
|
||||
class XmlComment extends CommentLine, @xmldoccomment {
|
||||
override string toString() { result = "/// ..." }
|
||||
|
||||
private string xmlAttributeRegex()
|
||||
{ result = "(" + xmlIdentifierRegex() + ")(?:\\s*=\\s*[\"']([^\"']*)[\"'])" }
|
||||
private string xmlAttributeRegex() {
|
||||
result = "(" + xmlIdentifierRegex() + ")(?:\\s*=\\s*[\"']([^\"']*)[\"'])"
|
||||
}
|
||||
|
||||
private string xmlIdentifierRegex()
|
||||
{ result = "\\w+" }
|
||||
private string xmlIdentifierRegex() { result = "\\w+" }
|
||||
|
||||
private string xmlTagOpenRegex()
|
||||
{ result = "<\\s*" + xmlIdentifierRegex() }
|
||||
private string xmlTagOpenRegex() { result = "<\\s*" + xmlIdentifierRegex() }
|
||||
|
||||
private string xmlTagIntroRegex()
|
||||
{ result = xmlTagOpenRegex() + "(?:\\s*" + xmlAttributeRegex() +")*" }
|
||||
private string xmlTagIntroRegex() {
|
||||
result = xmlTagOpenRegex() + "(?:\\s*" + xmlAttributeRegex() + ")*"
|
||||
}
|
||||
|
||||
private string xmlTagCloseRegex()
|
||||
{ result = "</\\s*" + xmlIdentifierRegex() + "\\s*>" }
|
||||
private string xmlTagCloseRegex() { result = "</\\s*" + xmlIdentifierRegex() + "\\s*>" }
|
||||
|
||||
/** Gets the text inside the XML element at character offset `offset`. */
|
||||
private string getElement(int offset)
|
||||
{
|
||||
result = getText().regexpFind(xmlTagIntroRegex(),_,offset)
|
||||
private string getElement(int offset) {
|
||||
result = getText().regexpFind(xmlTagIntroRegex(), _, offset)
|
||||
}
|
||||
|
||||
/** Gets the name of the opening tag at offset `offset`. */
|
||||
string getOpenTag(int offset)
|
||||
{
|
||||
exists(int offset1,int offset2 |
|
||||
result = getElement(offset1).regexpFind(xmlIdentifierRegex(),0,offset2)
|
||||
and offset = offset1+offset2 )
|
||||
string getOpenTag(int offset) {
|
||||
exists(int offset1, int offset2 |
|
||||
result = getElement(offset1).regexpFind(xmlIdentifierRegex(), 0, offset2) and
|
||||
offset = offset1 + offset2
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the name of the closing tag at offset `offset`. */
|
||||
string getCloseTag(int offset)
|
||||
{
|
||||
string getCloseTag(int offset) {
|
||||
exists(int offset1, int offset2 |
|
||||
result = getText().regexpFind(xmlTagCloseRegex(),_,offset1).
|
||||
regexpFind(xmlIdentifierRegex(),0,offset2)
|
||||
and offset = offset1+offset2 )
|
||||
result = getText()
|
||||
.regexpFind(xmlTagCloseRegex(), _, offset1)
|
||||
.regexpFind(xmlIdentifierRegex(), 0, offset2) and
|
||||
offset = offset1 + offset2
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the name of the empty tag at offset `offset`. */
|
||||
string getEmptyTag(int offset)
|
||||
{
|
||||
string getEmptyTag(int offset) {
|
||||
exists(int offset1, int offset2 |
|
||||
(
|
||||
result=getText().regexpFind(xmlTagIntroRegex() + "\\s*/>",_,offset1).
|
||||
regexpFind(xmlIdentifierRegex(),0,offset2)
|
||||
or result=getText().regexpFind(xmlTagIntroRegex() + "\\s*>\\s*</" + xmlIdentifierRegex() + "\\s*>",_,offset1).
|
||||
regexpFind(xmlIdentifierRegex(),0,offset2)
|
||||
) and offset=offset1+offset2 )
|
||||
result = getText()
|
||||
.regexpFind(xmlTagIntroRegex() + "\\s*/>", _, offset1)
|
||||
.regexpFind(xmlIdentifierRegex(), 0, offset2) or
|
||||
result = getText()
|
||||
.regexpFind(xmlTagIntroRegex() + "\\s*>\\s*</" + xmlIdentifierRegex() + "\\s*>", _,
|
||||
offset1)
|
||||
.regexpFind(xmlIdentifierRegex(), 0, offset2)
|
||||
) and
|
||||
offset = offset1 + offset2
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the XML attribute value for an XML element,
|
||||
* for a given XML attribute name `key` and element offset `offset`.
|
||||
*/
|
||||
string getAttribute(string element, string key, int offset)
|
||||
{
|
||||
exists(int offset1, int offset2, string elt, string pair |
|
||||
elt = getElement(offset1) |
|
||||
element = elt.regexpFind(xmlIdentifierRegex(),0,offset2)
|
||||
and offset = offset1+offset2
|
||||
and pair = elt.regexpFind(xmlAttributeRegex(),_,_)
|
||||
and key = pair.regexpCapture(xmlAttributeRegex(),1)
|
||||
and result = pair.regexpCapture(xmlAttributeRegex(),2) )
|
||||
string getAttribute(string element, string key, int offset) {
|
||||
exists(int offset1, int offset2, string elt, string pair | elt = getElement(offset1) |
|
||||
element = elt.regexpFind(xmlIdentifierRegex(), 0, offset2) and
|
||||
offset = offset1 + offset2 and
|
||||
pair = elt.regexpFind(xmlAttributeRegex(), _, _) and
|
||||
key = pair.regexpCapture(xmlAttributeRegex(), 1) and
|
||||
result = pair.regexpCapture(xmlAttributeRegex(), 2)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if the XML element at the given offset is not empty. */
|
||||
predicate hasBody(string element, int offset)
|
||||
{
|
||||
element=getOpenTag(offset) and not element=getEmptyTag(offset)
|
||||
predicate hasBody(string element, int offset) {
|
||||
element = getOpenTag(offset) and not element = getEmptyTag(offset)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,40 +151,39 @@ class XmlComment extends CommentLine, @xmldoccomment
|
||||
* /// </summary>
|
||||
* ```
|
||||
*/
|
||||
class CommentBlock extends @commentblock
|
||||
{
|
||||
class CommentBlock extends @commentblock {
|
||||
/** Gets a textual representation of this comment block. */
|
||||
string toString() { result=getChild(0).toString() }
|
||||
string toString() { result = getChild(0).toString() }
|
||||
|
||||
/** Gets the location of this comment block */
|
||||
Location getLocation() { commentblock_location(this,result) }
|
||||
Location getLocation() { commentblock_location(this, result) }
|
||||
|
||||
/** Gets the number of lines in this comment block. */
|
||||
int getNumLines() { result=count(getAChild()) }
|
||||
int getNumLines() { result = count(getAChild()) }
|
||||
|
||||
/** Gets the `c`th child of this comment block (numbered from 0). */
|
||||
CommentLine getChild(int c) { commentblock_child(this,result,c) }
|
||||
CommentLine getChild(int c) { commentblock_child(this, result, c) }
|
||||
|
||||
/** Gets a comment line in this comment block. */
|
||||
CommentLine getAChild() { commentblock_child(this,result,_) }
|
||||
CommentLine getAChild() { commentblock_child(this, result, _) }
|
||||
|
||||
/** Gets the `Element` that contains this comment block, if any. */
|
||||
Element getParent() { commentblock_binding(this,result,0) }
|
||||
Element getParent() { commentblock_binding(this, result, 0) }
|
||||
|
||||
/** Gets the `Element` that this comment block most probably refers to. */
|
||||
Element getElement() { commentblock_binding(this,result,1) }
|
||||
Element getElement() { commentblock_binding(this, result, 1) }
|
||||
|
||||
/** Gets the `Element` before this comment block, if any. */
|
||||
Element getBefore() { commentblock_binding(this,result,2) }
|
||||
Element getBefore() { commentblock_binding(this, result, 2) }
|
||||
|
||||
/** Gets the `Element` after this comment, if any. */
|
||||
Element getAfter(){ commentblock_binding(this,result,3) }
|
||||
Element getAfter() { commentblock_binding(this, result, 3) }
|
||||
|
||||
/**
|
||||
* Gets an `Element` possibly associated with this comment.
|
||||
* This is a superset of `getElement()`.
|
||||
*/
|
||||
Element getAnElement() { commentblock_binding(this,result,_) }
|
||||
Element getAnElement() { commentblock_binding(this, result, _) }
|
||||
|
||||
/** Gets a line of text in this comment block. */
|
||||
string getALine() { result = getAChild().getText() }
|
||||
@@ -196,24 +192,19 @@ class CommentBlock extends @commentblock
|
||||
predicate isOrphan() { not exists(getElement()) }
|
||||
|
||||
/** Holds if this block consists entirely of XML comments. */
|
||||
predicate isXmlCommentBlock()
|
||||
{
|
||||
forall(CommentLine l | l = getAChild() | l instanceof XmlComment )
|
||||
predicate isXmlCommentBlock() {
|
||||
forall(CommentLine l | l = getAChild() | l instanceof XmlComment)
|
||||
}
|
||||
|
||||
/** Gets a `CommentLine` containing text. */
|
||||
CommentLine getANonEmptyLine()
|
||||
{
|
||||
result = getAChild() and result.getText().length()!=0
|
||||
}
|
||||
CommentLine getANonEmptyLine() { result = getAChild() and result.getText().length() != 0 }
|
||||
|
||||
/** Gets a `CommentLine` that might contain code. */
|
||||
CommentLine getAProbableCodeLine()
|
||||
{
|
||||
CommentLine getAProbableCodeLine() {
|
||||
// Logic taken verbatim from Java query CommentedCode.qll
|
||||
result=getAChild() and
|
||||
exists(string trimmed |
|
||||
trimmed = result.getText().regexpReplaceAll("\\s*//.*$", "") |
|
||||
trimmed.matches("%;") or trimmed.matches("%{") or trimmed.matches("%}"))
|
||||
result = getAChild() and
|
||||
exists(string trimmed | trimmed = result.getText().regexpReplaceAll("\\s*//.*$", "") |
|
||||
trimmed.matches("%;") or trimmed.matches("%{") or trimmed.matches("%}")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,27 +55,20 @@ private predicate implicitConversionNonNull(Type fromType, Type toType) {
|
||||
}
|
||||
|
||||
private Type getTypeArgument(UnboundGenericType ugt, ConstructedType ct, int i, TypeParameter tp) {
|
||||
ct.getUnboundGeneric() = ugt
|
||||
and
|
||||
not ugt instanceof AnonymousClass
|
||||
and
|
||||
tp = ugt.getTypeParameter(i)
|
||||
and
|
||||
ct.getUnboundGeneric() = ugt and
|
||||
not ugt instanceof AnonymousClass and
|
||||
tp = ugt.getTypeParameter(i) and
|
||||
result = ct.getTypeArgument(i)
|
||||
}
|
||||
|
||||
/** A type that is an element type of an array type. */
|
||||
private class ArrayElementType extends Type {
|
||||
ArrayElementType() {
|
||||
this = any(ArrayType at).getElementType()
|
||||
}
|
||||
ArrayElementType() { this = any(ArrayType at).getElementType() }
|
||||
}
|
||||
|
||||
/** A type that is an argument in a constructed type. */
|
||||
private class TypeArgument extends Type {
|
||||
TypeArgument() {
|
||||
this = any(ConstructedType ct).getATypeArgument()
|
||||
}
|
||||
TypeArgument() { this = any(ConstructedType ct).getATypeArgument() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,14 +90,13 @@ predicate convIdentity(Type fromType, Type toType) {
|
||||
|
||||
private module Identity {
|
||||
private class IdentityConvertibleType extends Type {
|
||||
IdentityConvertibleType() {
|
||||
isIdentityConvertible(this)
|
||||
}
|
||||
IdentityConvertibleType() { isIdentityConvertible(this) }
|
||||
}
|
||||
|
||||
private class IdentityConvertibleArrayType extends IdentityConvertibleType, ArrayType { }
|
||||
|
||||
private class IdentityConvertibleConstructedType extends IdentityConvertibleType, ConstructedType { }
|
||||
private class IdentityConvertibleConstructedType extends IdentityConvertibleType, ConstructedType {
|
||||
}
|
||||
|
||||
/**
|
||||
* A type is (strictly) identity convertible if it contains at least one `object`
|
||||
@@ -130,20 +122,24 @@ private module Identity {
|
||||
convIdentityStrictConstructedType(fromType, toType)
|
||||
}
|
||||
|
||||
private predicate convIdentityObjectDynamic(ObjectType fromType, DynamicType toType) {
|
||||
any()
|
||||
}
|
||||
private predicate convIdentityObjectDynamic(ObjectType fromType, DynamicType toType) { any() }
|
||||
|
||||
private predicate convIdentityStrictArrayType(IdentityConvertibleArrayType fromType, IdentityConvertibleArrayType toType) {
|
||||
private predicate convIdentityStrictArrayType(
|
||||
IdentityConvertibleArrayType fromType, IdentityConvertibleArrayType toType
|
||||
) {
|
||||
convIdentityStrictArrayTypeJoin(fromType, toType, toType.getDimension(), toType.getRank())
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate convIdentityStrictArrayTypeJoin(IdentityConvertibleArrayType fromType, IdentityConvertibleArrayType toType, int dim, int rnk) {
|
||||
private predicate convIdentityStrictArrayTypeJoin(
|
||||
IdentityConvertibleArrayType fromType, IdentityConvertibleArrayType toType, int dim, int rnk
|
||||
) {
|
||||
convIdentityStrictArrayElementType(fromType, toType.getElementType(), dim, rnk)
|
||||
}
|
||||
|
||||
private predicate convIdentityStrictArrayElementType(IdentityConvertibleArrayType fromType, ArrayElementType aet, int dim, int rnk) {
|
||||
private predicate convIdentityStrictArrayElementType(
|
||||
IdentityConvertibleArrayType fromType, ArrayElementType aet, int dim, int rnk
|
||||
) {
|
||||
convIdentityStrict(fromType.getElementType(), aet) and
|
||||
dim = fromType.getDimension() and
|
||||
rnk = fromType.getRank()
|
||||
@@ -155,22 +151,20 @@ private module Identity {
|
||||
*/
|
||||
private int getTypeArgumentCount(UnboundGenericType ugt, int i) {
|
||||
result = strictcount(Type arg |
|
||||
exists(IdentityConvertibleConstructedType ct |
|
||||
ct.getUnboundGeneric() = ugt |
|
||||
arg = ct.getTypeArgument(i)
|
||||
exists(IdentityConvertibleConstructedType ct | ct.getUnboundGeneric() = ugt |
|
||||
arg = ct.getTypeArgument(i)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private int rnk(UnboundGenericType ugt, int i) {
|
||||
result = rank[i + 1](int j, int k |
|
||||
j = getTypeArgumentCount(ugt, k) |
|
||||
k order by j, k
|
||||
)
|
||||
result = rank[i + 1](int j, int k | j = getTypeArgumentCount(ugt, k) | k order by j, k)
|
||||
}
|
||||
|
||||
/** Gets the 'i'th type argument, ranked by size, of constructed type `t`. */
|
||||
private Type getTypeArgumentRanked(UnboundGenericType ugt, IdentityConvertibleConstructedType t, int i) {
|
||||
private Type getTypeArgumentRanked(
|
||||
UnboundGenericType ugt, IdentityConvertibleConstructedType t, int i
|
||||
) {
|
||||
result = getTypeArgument(ugt, t, rnk(ugt, i), _)
|
||||
}
|
||||
|
||||
@@ -183,14 +177,17 @@ private module Identity {
|
||||
exists(int j |
|
||||
fromTypeArgument = getTypeArgumentRanked(_, _, i) and
|
||||
toTypeArgument = getTypeArgumentRanked(_, _, j) and
|
||||
i <= j and j <= i
|
||||
|
|
||||
i <= j and
|
||||
j <= i
|
||||
|
|
||||
convIdentity(fromTypeArgument, toTypeArgument)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate convTypeArgumentsSomeUnbound(UnboundGenericType ugt, TypeArgument fromTypeArgument, TypeArgument toTypeArgument, int i) {
|
||||
private predicate convTypeArgumentsSomeUnbound(
|
||||
UnboundGenericType ugt, TypeArgument fromTypeArgument, TypeArgument toTypeArgument, int i
|
||||
) {
|
||||
convTypeArguments(fromTypeArgument, toTypeArgument, i) and
|
||||
fromTypeArgument = getTypeArgumentRanked(ugt, _, i)
|
||||
}
|
||||
@@ -201,13 +198,18 @@ private module Identity {
|
||||
* based on unbound generic type `ugt`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate convTypeArgumentsSameUnbound(UnboundGenericType ugt, TypeArgument fromTypeArgument, TypeArgument toTypeArgument, int i) {
|
||||
private predicate convTypeArgumentsSameUnbound(
|
||||
UnboundGenericType ugt, TypeArgument fromTypeArgument, TypeArgument toTypeArgument, int i
|
||||
) {
|
||||
convTypeArgumentsSomeUnbound(ugt, fromTypeArgument, toTypeArgument, i) and
|
||||
toTypeArgument = getTypeArgumentRanked(ugt, _, i)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate convIdentitySingle0(UnboundGenericType ugt, IdentityConvertibleConstructedType toType, TypeArgument fromTypeArgument, TypeArgument toTypeArgument) {
|
||||
private predicate convIdentitySingle0(
|
||||
UnboundGenericType ugt, IdentityConvertibleConstructedType toType,
|
||||
TypeArgument fromTypeArgument, TypeArgument toTypeArgument
|
||||
) {
|
||||
convTypeArgumentsSameUnbound(ugt, fromTypeArgument, toTypeArgument, 0) and
|
||||
toTypeArgument = getTypeArgumentRanked(ugt, toType, 0) and
|
||||
ugt.getNumberOfTypeParameters() = 1
|
||||
@@ -217,22 +219,32 @@ private module Identity {
|
||||
* Holds if the type arguments of types `fromType` and `toType` are identity
|
||||
* convertible, and the number of type arguments is 1.
|
||||
*/
|
||||
predicate convIdentitySingle(UnboundGenericType ugt, IdentityConvertibleConstructedType fromType, IdentityConvertibleConstructedType toType) {
|
||||
predicate convIdentitySingle(
|
||||
UnboundGenericType ugt, IdentityConvertibleConstructedType fromType,
|
||||
IdentityConvertibleConstructedType toType
|
||||
) {
|
||||
exists(TypeArgument fromTypeArgument, TypeArgument toTypeArgument |
|
||||
convIdentitySingle0(ugt, toType, fromTypeArgument, toTypeArgument) |
|
||||
convIdentitySingle0(ugt, toType, fromTypeArgument, toTypeArgument)
|
||||
|
|
||||
fromTypeArgument = getTypeArgumentRanked(ugt, fromType, 0)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate convIdentityMultiple01Aux0(UnboundGenericType ugt, IdentityConvertibleConstructedType toType, TypeArgument fromTypeArgument0, TypeArgument toTypeArgument0, TypeArgument toTypeArgument1) {
|
||||
private predicate convIdentityMultiple01Aux0(
|
||||
UnboundGenericType ugt, IdentityConvertibleConstructedType toType,
|
||||
TypeArgument fromTypeArgument0, TypeArgument toTypeArgument0, TypeArgument toTypeArgument1
|
||||
) {
|
||||
convTypeArgumentsSameUnbound(ugt, fromTypeArgument0, toTypeArgument0, 0) and
|
||||
toTypeArgument0 = getTypeArgumentRanked(ugt, toType, 0) and
|
||||
toTypeArgument1 = getTypeArgumentRanked(ugt, toType, 1)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate convIdentityMultiple01Aux1(UnboundGenericType ugt, IdentityConvertibleConstructedType fromType, TypeArgument fromTypeArgument0, TypeArgument fromTypeArgument1, TypeArgument toTypeArgument1) {
|
||||
private predicate convIdentityMultiple01Aux1(
|
||||
UnboundGenericType ugt, IdentityConvertibleConstructedType fromType,
|
||||
TypeArgument fromTypeArgument0, TypeArgument fromTypeArgument1, TypeArgument toTypeArgument1
|
||||
) {
|
||||
convTypeArgumentsSameUnbound(ugt, fromTypeArgument1, toTypeArgument1, 1) and
|
||||
fromTypeArgument0 = getTypeArgumentRanked(ugt, fromType, 0) and
|
||||
fromTypeArgument1 = getTypeArgumentRanked(ugt, fromType, 1)
|
||||
@@ -242,23 +254,37 @@ private module Identity {
|
||||
* Holds if the first two ranked type arguments of types `fromType` and `toType`
|
||||
* are identity convertible.
|
||||
*/
|
||||
private predicate convIdentityMultiple01(UnboundGenericType ugt, IdentityConvertibleConstructedType fromType, IdentityConvertibleConstructedType toType) {
|
||||
exists(Type fromTypeArgument0, Type toTypeArgument0, Type fromTypeArgument1, Type toTypeArgument1 |
|
||||
convIdentityMultiple01Aux0(ugt, toType, fromTypeArgument0, toTypeArgument0, toTypeArgument1) |
|
||||
convIdentityMultiple01Aux1(ugt, fromType, fromTypeArgument0, fromTypeArgument1, toTypeArgument1)
|
||||
private predicate convIdentityMultiple01(
|
||||
UnboundGenericType ugt, IdentityConvertibleConstructedType fromType,
|
||||
IdentityConvertibleConstructedType toType
|
||||
) {
|
||||
exists(
|
||||
Type fromTypeArgument0, Type toTypeArgument0, Type fromTypeArgument1, Type toTypeArgument1
|
||||
|
|
||||
convIdentityMultiple01Aux0(ugt, toType, fromTypeArgument0, toTypeArgument0, toTypeArgument1)
|
||||
|
|
||||
convIdentityMultiple01Aux1(ugt, fromType, fromTypeArgument0, fromTypeArgument1,
|
||||
toTypeArgument1)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate convIdentityMultiple2Aux(UnboundGenericType ugt, IdentityConvertibleConstructedType toType, int i, TypeArgument fromTypeArgument, TypeArgument toTypeArgument) {
|
||||
private predicate convIdentityMultiple2Aux(
|
||||
UnboundGenericType ugt, IdentityConvertibleConstructedType toType, int i,
|
||||
TypeArgument fromTypeArgument, TypeArgument toTypeArgument
|
||||
) {
|
||||
convTypeArgumentsSameUnbound(ugt, fromTypeArgument, toTypeArgument, i) and
|
||||
toTypeArgument = getTypeArgumentRanked(ugt, toType, i) and
|
||||
i >= 2
|
||||
}
|
||||
|
||||
private predicate convIdentityMultiple2(UnboundGenericType ugt, IdentityConvertibleConstructedType fromType, IdentityConvertibleConstructedType toType, int i) {
|
||||
private predicate convIdentityMultiple2(
|
||||
UnboundGenericType ugt, IdentityConvertibleConstructedType fromType,
|
||||
IdentityConvertibleConstructedType toType, int i
|
||||
) {
|
||||
exists(TypeArgument fromTypeArgument, TypeArgument toTypeArgument |
|
||||
convIdentityMultiple2Aux(ugt, toType, i, fromTypeArgument, toTypeArgument) |
|
||||
convIdentityMultiple2Aux(ugt, toType, i, fromTypeArgument, toTypeArgument)
|
||||
|
|
||||
fromTypeArgument = getTypeArgumentRanked(ugt, fromType, i)
|
||||
)
|
||||
}
|
||||
@@ -268,15 +294,21 @@ private module Identity {
|
||||
* `fromType` and `toType` are identity convertible.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate convIdentityMultiple(UnboundGenericType ugt, IdentityConvertibleConstructedType fromType, IdentityConvertibleConstructedType toType, int i) {
|
||||
predicate convIdentityMultiple(
|
||||
UnboundGenericType ugt, IdentityConvertibleConstructedType fromType,
|
||||
IdentityConvertibleConstructedType toType, int i
|
||||
) {
|
||||
convIdentityMultiple01(ugt, fromType, toType) and i = 1
|
||||
or
|
||||
convIdentityMultiple(ugt, fromType, toType, i - 1) and
|
||||
convIdentityMultiple2(ugt, fromType, toType, i)
|
||||
}
|
||||
|
||||
private predicate convIdentityStrictConstructedType(IdentityConvertibleConstructedType fromType, IdentityConvertibleConstructedType toType) {
|
||||
/* Semantically equivalent with
|
||||
private predicate convIdentityStrictConstructedType(
|
||||
IdentityConvertibleConstructedType fromType, IdentityConvertibleConstructedType toType
|
||||
) {
|
||||
/*
|
||||
* Semantically equivalent with
|
||||
* ```
|
||||
* ugt = fromType.getUnboundGeneric()
|
||||
* and
|
||||
@@ -292,6 +324,7 @@ private module Identity {
|
||||
* but performance is improved by explicitly evaluating the `i`th argument
|
||||
* only when all preceding arguments are convertible.
|
||||
*/
|
||||
|
||||
fromType != toType and
|
||||
(
|
||||
convIdentitySingle(_, fromType, toType)
|
||||
@@ -409,9 +442,7 @@ private predicate convNumericChar(SimpleType toType) {
|
||||
toType instanceof DecimalType
|
||||
}
|
||||
|
||||
private predicate convNumericFloat(SimpleType toType) {
|
||||
toType instanceof DoubleType
|
||||
}
|
||||
private predicate convNumericFloat(SimpleType toType) { toType instanceof DoubleType }
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
@@ -421,10 +452,8 @@ private predicate convNumericFloat(SimpleType toType) {
|
||||
* 6.1.4: Implicit nullable conversions.
|
||||
*/
|
||||
predicate convNullableType(ValueOrRefType fromType, NullableType toType) {
|
||||
exists(ValueType vt1, ValueType vt2 |
|
||||
implicitConversionNonNull(vt1, vt2) |
|
||||
toType.getUnderlyingType() = vt2
|
||||
and
|
||||
exists(ValueType vt1, ValueType vt2 | implicitConversionNonNull(vt1, vt2) |
|
||||
toType.getUnderlyingType() = vt2 and
|
||||
(
|
||||
fromType = vt1
|
||||
or
|
||||
@@ -437,9 +466,9 @@ predicate convNullableType(ValueOrRefType fromType, NullableType toType) {
|
||||
* This is a deliberate, small Cartesian product, so we have manually lifted it to force the
|
||||
* evaluator to evaluate it in its entirety, rather than trying to optimize it in context.
|
||||
*/
|
||||
|
||||
pragma[noinline]
|
||||
private
|
||||
predicate defaultNullConversion(Type fromType, Type toType) {
|
||||
private predicate defaultNullConversion(Type fromType, Type toType) {
|
||||
fromType instanceof NullType and convNullType(toType)
|
||||
}
|
||||
|
||||
@@ -466,6 +495,7 @@ private predicate convRefTypeNonNull(Type fromType, Type toType) {
|
||||
* This is a deliberate, small cartesian product, so we have manually lifted it to force the
|
||||
* evaluator to evaluate it in its entirety, rather than trying to optimize it in context.
|
||||
*/
|
||||
|
||||
pragma[noinline]
|
||||
private predicate defaultDynamicConversion(Type fromType, Type toType) {
|
||||
fromType instanceof RefType and toType instanceof DynamicType
|
||||
@@ -475,6 +505,7 @@ private predicate defaultDynamicConversion(Type fromType, Type toType) {
|
||||
* This is a deliberate, small cartesian product, so we have manually lifted it to force the
|
||||
* evaluator to evaluate it in its entirety, rather than trying to optimize it in context.
|
||||
*/
|
||||
|
||||
pragma[noinline]
|
||||
private predicate defaultDelegateConversion(RefType fromType, RefType toType) {
|
||||
fromType instanceof DelegateType and toType = any(SystemDelegateClass c).getABaseType*()
|
||||
@@ -489,8 +520,7 @@ private predicate convRefTypeRefType(RefType fromType, RefType toType) {
|
||||
or
|
||||
defaultDelegateConversion(fromType, toType)
|
||||
or
|
||||
exists(Type t |
|
||||
convIdentity(fromType, t) or convRefTypeRefType(fromType, t) |
|
||||
exists(Type t | convIdentity(fromType, t) or convRefTypeRefType(fromType, t) |
|
||||
convIdentity(t, toType) or convVariance(t, toType)
|
||||
)
|
||||
}
|
||||
@@ -499,6 +529,7 @@ private predicate convRefTypeRefType(RefType fromType, RefType toType) {
|
||||
* This is a deliberate, small cartesian product, so we have manually lifted it to force the
|
||||
* evaluator to evaluate it in its entirety, rather than trying to optimize it in context.
|
||||
*/
|
||||
|
||||
pragma[noinline]
|
||||
private predicate defaultArrayConversion(Type fromType, RefType toType) {
|
||||
fromType instanceof ArrayType and toType = any(SystemArrayClass c).getABaseType*()
|
||||
@@ -513,12 +544,10 @@ private predicate convArrayTypeRefType(ArrayType fromType, RefType toType) {
|
||||
fromType.getDimension() = 1 and
|
||||
fromType.getRank() = 1 and
|
||||
argumentType = getIListTypeArgument(ci) and
|
||||
exists(ArrayElementType elementType |
|
||||
elementType = fromType.getElementType() |
|
||||
exists(ArrayElementType elementType | elementType = fromType.getElementType() |
|
||||
convIdentity(elementType, argumentType) or
|
||||
convRefTypeNonNull(elementType, argumentType)
|
||||
)
|
||||
and
|
||||
) and
|
||||
toType = ci.getABaseType*()
|
||||
)
|
||||
}
|
||||
@@ -567,8 +596,7 @@ predicate convBoxing(Type fromType, Type toType) {
|
||||
or
|
||||
convBoxingTypeParameter(fromType, toType)
|
||||
or
|
||||
exists(Type t |
|
||||
convBoxing(fromType, t) |
|
||||
exists(Type t | convBoxing(fromType, t) |
|
||||
convIdentity(t, toType) or
|
||||
convVariance(t, toType)
|
||||
)
|
||||
@@ -605,13 +633,17 @@ private class SignedIntegralConstantExpr extends Expr {
|
||||
}
|
||||
|
||||
private predicate convConstantIntExpr(SignedIntegralConstantExpr e, SimpleType toType) {
|
||||
exists(int n |
|
||||
n = e.getValue().toInt() |
|
||||
toType = any(SByteType t | n in [t.minValue() .. t.maxValue()]) or
|
||||
toType = any(ByteType t | n in [t.minValue() .. t.maxValue()]) or
|
||||
toType = any(ShortType t | n in [t.minValue() .. t.maxValue()]) or
|
||||
toType = any(UShortType t | n in [t.minValue() .. t.maxValue()]) or
|
||||
toType instanceof UIntType and n >= 0 or
|
||||
exists(int n | n = e.getValue().toInt() |
|
||||
toType = any(SByteType t | n in [t.minValue() .. t.maxValue()])
|
||||
or
|
||||
toType = any(ByteType t | n in [t.minValue() .. t.maxValue()])
|
||||
or
|
||||
toType = any(ShortType t | n in [t.minValue() .. t.maxValue()])
|
||||
or
|
||||
toType = any(UShortType t | n in [t.minValue() .. t.maxValue()])
|
||||
or
|
||||
toType instanceof UIntType and n >= 0
|
||||
or
|
||||
toType instanceof ULongType and n >= 0
|
||||
)
|
||||
}
|
||||
@@ -637,8 +669,7 @@ private predicate convBoxingTypeParameter(TypeParameter fromType, Type toType) {
|
||||
private predicate convTypeParameter(TypeParameter fromType, Type toType) {
|
||||
toType = convTypeParameterBase(fromType)
|
||||
or
|
||||
exists(Type t |
|
||||
convTypeParameter(fromType, t) |
|
||||
exists(Type t | convTypeParameter(fromType, t) |
|
||||
convIdentity(t, toType) or
|
||||
convVariance(t, toType)
|
||||
)
|
||||
@@ -663,8 +694,7 @@ private Type convTypeParameterBase(TypeParameter tp) {
|
||||
private Class effectiveBaseClassCandidate(TypeParameter tp) {
|
||||
not hasPrimaryConstraints(tp) and result instanceof ObjectType
|
||||
or
|
||||
exists(TypeParameterConstraints tpc |
|
||||
tpc = tp.getConstraints() |
|
||||
exists(TypeParameterConstraints tpc | tpc = tp.getConstraints() |
|
||||
tpc.hasValueTypeConstraint() and result instanceof SystemValueTypeClass
|
||||
or
|
||||
result = tpc.getClassConstraint()
|
||||
@@ -677,8 +707,7 @@ private Class effectiveBaseClassCandidate(TypeParameter tp) {
|
||||
|
||||
/** 10.1.5: Whether type parameter `tp` has primary constraints. */
|
||||
private predicate hasPrimaryConstraints(TypeParameter tp) {
|
||||
exists(TypeParameterConstraints tpc |
|
||||
tpc = tp.getConstraints() |
|
||||
exists(TypeParameterConstraints tpc | tpc = tp.getConstraints() |
|
||||
exists(tpc.getClassConstraint())
|
||||
or
|
||||
tpc.hasRefTypeConstraint()
|
||||
@@ -689,8 +718,7 @@ private predicate hasPrimaryConstraints(TypeParameter tp) {
|
||||
|
||||
/** 10.1.5: The effective interface set of a type parameter `tp` */
|
||||
private Interface effectiveInterfaceSet(TypeParameter tp) {
|
||||
exists(TypeParameterConstraints tpc |
|
||||
tpc = tp.getConstraints() |
|
||||
exists(TypeParameterConstraints tpc | tpc = tp.getConstraints() |
|
||||
result = tpc.getAnInterfaceConstraint()
|
||||
or
|
||||
result = effectiveInterfaceSet(tpc.getATypeParameterConstraint())
|
||||
@@ -717,7 +745,8 @@ predicate convConversionOperator(Type fromType, Type toType) {
|
||||
|
||||
/** 13.1.3.2: Variance conversion. */
|
||||
private predicate convVariance(ConstructedType fromType, ConstructedType toType) {
|
||||
/* Semantically equivalent with
|
||||
/*
|
||||
* Semantically equivalent with
|
||||
* ```
|
||||
* ugt = fromType.getUnboundGeneric()
|
||||
* and
|
||||
@@ -737,6 +766,7 @@ private predicate convVariance(ConstructedType fromType, ConstructedType toType)
|
||||
* but performance is improved by explicitly evaluating the `i`th argument
|
||||
* only when all preceding arguments are convertible.
|
||||
*/
|
||||
|
||||
Variance::convVarianceSingle(_, fromType, toType)
|
||||
or
|
||||
exists(UnboundGenericType ugt |
|
||||
@@ -753,18 +783,15 @@ private module Variance {
|
||||
private predicate isVarianceConvertible(ConstructedType ct, int i) {
|
||||
exists(TypeParameter tp, Type t |
|
||||
tp = ct.getUnboundGeneric().getTypeParameter(i) and
|
||||
t = ct.getTypeArgument(i) |
|
||||
(
|
||||
// Anything that is not a type parameter is potentially convertible
|
||||
// to/from another type; if the `i`th type parameter is invariant,
|
||||
// `t` must be strictly identity convertible
|
||||
not t instanceof TypeParameter
|
||||
and
|
||||
(tp.isIn() or tp.isOut() or Identity::convIdentityStrict(t, _))
|
||||
)
|
||||
t = ct.getTypeArgument(i)
|
||||
|
|
||||
// Anything that is not a type parameter is potentially convertible
|
||||
// to/from another type; if the `i`th type parameter is invariant,
|
||||
// `t` must be strictly identity convertible
|
||||
not t instanceof TypeParameter and
|
||||
(tp.isIn() or tp.isOut() or Identity::convIdentityStrict(t, _))
|
||||
or
|
||||
exists(TypeParameter s |
|
||||
s = t |
|
||||
exists(TypeParameter s | s = t |
|
||||
// A type parameter with implicit reference conversion
|
||||
exists(convTypeParameterBase(s)) and s.isRefType() and tp.isOut()
|
||||
or
|
||||
@@ -775,9 +802,7 @@ private module Variance {
|
||||
}
|
||||
|
||||
private class VarianceConvertibleConstructedType extends ConstructedType {
|
||||
VarianceConvertibleConstructedType() {
|
||||
isVarianceConvertible(this, _)
|
||||
}
|
||||
VarianceConvertibleConstructedType() { isVarianceConvertible(this, _) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -786,22 +811,20 @@ private module Variance {
|
||||
*/
|
||||
private int getTypeArgumentCount(UnboundGenericType ugt, int i) {
|
||||
result = strictcount(Type arg |
|
||||
exists(VarianceConvertibleConstructedType ct |
|
||||
ct.getUnboundGeneric() = ugt |
|
||||
arg = ct.getTypeArgument(i)
|
||||
exists(VarianceConvertibleConstructedType ct | ct.getUnboundGeneric() = ugt |
|
||||
arg = ct.getTypeArgument(i)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private int rnk(UnboundGenericType ugt, int i) {
|
||||
result = rank[i + 1](int j, int k |
|
||||
j = getTypeArgumentCount(ugt, k) |
|
||||
k order by j, k
|
||||
)
|
||||
result = rank[i + 1](int j, int k | j = getTypeArgumentCount(ugt, k) | k order by j, k)
|
||||
}
|
||||
|
||||
/** Gets the 'i'th type argument, ranked by size, of constructed type `t`. */
|
||||
private Type getTypeArgumentRanked(UnboundGenericType ugt, VarianceConvertibleConstructedType t, int i, TypeParameter tp) {
|
||||
private Type getTypeArgumentRanked(
|
||||
UnboundGenericType ugt, VarianceConvertibleConstructedType t, int i, TypeParameter tp
|
||||
) {
|
||||
result = getTypeArgument(ugt, t, rnk(ugt, i), tp)
|
||||
}
|
||||
|
||||
@@ -825,19 +848,25 @@ private module Variance {
|
||||
toType = getATypeArgumentRankedIn(i)
|
||||
}
|
||||
|
||||
private newtype TVariance = TNone() or TIn() or TOut()
|
||||
private newtype TVariance =
|
||||
TNone() or
|
||||
TIn() or
|
||||
TOut()
|
||||
|
||||
/**
|
||||
* Holds if `fromTypeArgument` is convertible to `toTypeArgument`, with variance
|
||||
* `v`, and both types are the `i`th type argument in _some_ constructed type.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate convTypeArguments(TypeArgument fromTypeArgument, TypeArgument toTypeArgument, int i, TVariance v) {
|
||||
private predicate convTypeArguments(
|
||||
TypeArgument fromTypeArgument, TypeArgument toTypeArgument, int i, TVariance v
|
||||
) {
|
||||
exists(int j |
|
||||
fromTypeArgument = getTypeArgumentRanked(_, _, i, _) and
|
||||
toTypeArgument = getTypeArgumentRanked(_, _, j, _) and
|
||||
i <= j and j <= i
|
||||
|
|
||||
i <= j and
|
||||
j <= i
|
||||
|
|
||||
convIdentity(fromTypeArgument, toTypeArgument) and
|
||||
v = TNone()
|
||||
or
|
||||
@@ -850,9 +879,12 @@ private module Variance {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate convTypeArgumentsSomeUnbound(UnboundGenericType ugt, TypeArgument fromTypeArgument, TypeArgument toTypeArgument, int i) {
|
||||
private predicate convTypeArgumentsSomeUnbound(
|
||||
UnboundGenericType ugt, TypeArgument fromTypeArgument, TypeArgument toTypeArgument, int i
|
||||
) {
|
||||
exists(TypeParameter tp, TVariance v |
|
||||
convTypeArguments(fromTypeArgument, toTypeArgument, i, v) |
|
||||
convTypeArguments(fromTypeArgument, toTypeArgument, i, v)
|
||||
|
|
||||
fromTypeArgument = getTypeArgumentRanked(ugt, _, i, tp) and
|
||||
(v = TIn() implies tp.isIn()) and
|
||||
(v = TOut() implies tp.isOut())
|
||||
@@ -865,13 +897,18 @@ private module Variance {
|
||||
* based on unbound generic type `ugt`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate convTypeArgumentsSameUnbound(UnboundGenericType ugt, TypeArgument fromTypeArgument, TypeArgument toTypeArgument, int i) {
|
||||
private predicate convTypeArgumentsSameUnbound(
|
||||
UnboundGenericType ugt, TypeArgument fromTypeArgument, TypeArgument toTypeArgument, int i
|
||||
) {
|
||||
convTypeArgumentsSomeUnbound(ugt, fromTypeArgument, toTypeArgument, i) and
|
||||
toTypeArgument = getTypeArgumentRanked(ugt, _, i, _)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate convVarianceSingle0(UnboundGenericType ugt, VarianceConvertibleConstructedType toType, TypeArgument fromTypeArgument, TypeArgument toTypeArgument) {
|
||||
private predicate convVarianceSingle0(
|
||||
UnboundGenericType ugt, VarianceConvertibleConstructedType toType,
|
||||
TypeArgument fromTypeArgument, TypeArgument toTypeArgument
|
||||
) {
|
||||
convTypeArgumentsSameUnbound(ugt, fromTypeArgument, toTypeArgument, 0) and
|
||||
toTypeArgument = getTypeArgumentRanked(ugt, toType, 0, _) and
|
||||
ugt.getNumberOfTypeParameters() = 1
|
||||
@@ -881,22 +918,32 @@ private module Variance {
|
||||
* Holds if the type arguments of types `fromType` and `toType` are variance
|
||||
* convertible, and the number of type arguments is 1.
|
||||
*/
|
||||
predicate convVarianceSingle(UnboundGenericType ugt, VarianceConvertibleConstructedType fromType, VarianceConvertibleConstructedType toType) {
|
||||
predicate convVarianceSingle(
|
||||
UnboundGenericType ugt, VarianceConvertibleConstructedType fromType,
|
||||
VarianceConvertibleConstructedType toType
|
||||
) {
|
||||
exists(TypeArgument fromTypeArgument, TypeArgument toTypeArgument |
|
||||
convVarianceSingle0(ugt, toType, fromTypeArgument, toTypeArgument) |
|
||||
convVarianceSingle0(ugt, toType, fromTypeArgument, toTypeArgument)
|
||||
|
|
||||
fromTypeArgument = getTypeArgumentRanked(ugt, fromType, 0, _)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate convVarianceMultiple01Aux0(UnboundGenericType ugt, VarianceConvertibleConstructedType toType, TypeArgument fromTypeArgument0, TypeArgument toTypeArgument0, TypeArgument toTypeArgument1) {
|
||||
private predicate convVarianceMultiple01Aux0(
|
||||
UnboundGenericType ugt, VarianceConvertibleConstructedType toType,
|
||||
TypeArgument fromTypeArgument0, TypeArgument toTypeArgument0, TypeArgument toTypeArgument1
|
||||
) {
|
||||
convTypeArgumentsSameUnbound(ugt, fromTypeArgument0, toTypeArgument0, 0) and
|
||||
toTypeArgument0 = getTypeArgumentRanked(ugt, toType, 0, _) and
|
||||
toTypeArgument1 = getTypeArgumentRanked(ugt, toType, 1, _)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate convVarianceMultiple01Aux1(UnboundGenericType ugt, VarianceConvertibleConstructedType fromType, TypeArgument fromTypeArgument0, TypeArgument fromTypeArgument1, TypeArgument toTypeArgument1) {
|
||||
private predicate convVarianceMultiple01Aux1(
|
||||
UnboundGenericType ugt, VarianceConvertibleConstructedType fromType,
|
||||
TypeArgument fromTypeArgument0, TypeArgument fromTypeArgument1, TypeArgument toTypeArgument1
|
||||
) {
|
||||
convTypeArgumentsSameUnbound(ugt, fromTypeArgument1, toTypeArgument1, 1) and
|
||||
fromTypeArgument0 = getTypeArgumentRanked(ugt, fromType, 0, _) and
|
||||
fromTypeArgument1 = getTypeArgumentRanked(ugt, fromType, 1, _)
|
||||
@@ -906,23 +953,38 @@ private module Variance {
|
||||
* Holds if the first two ranked type arguments of types `fromType` and `toType`
|
||||
* are variance convertible.
|
||||
*/
|
||||
private predicate convVarianceMultiple01(UnboundGenericType ugt, VarianceConvertibleConstructedType fromType, VarianceConvertibleConstructedType toType) {
|
||||
exists(TypeArgument fromTypeArgument0, TypeArgument toTypeArgument0, TypeArgument fromTypeArgument1, TypeArgument toTypeArgument1 |
|
||||
convVarianceMultiple01Aux0(ugt, toType, fromTypeArgument0, toTypeArgument0, toTypeArgument1) |
|
||||
convVarianceMultiple01Aux1(ugt, fromType, fromTypeArgument0, fromTypeArgument1, toTypeArgument1)
|
||||
private predicate convVarianceMultiple01(
|
||||
UnboundGenericType ugt, VarianceConvertibleConstructedType fromType,
|
||||
VarianceConvertibleConstructedType toType
|
||||
) {
|
||||
exists(
|
||||
TypeArgument fromTypeArgument0, TypeArgument toTypeArgument0, TypeArgument fromTypeArgument1,
|
||||
TypeArgument toTypeArgument1
|
||||
|
|
||||
convVarianceMultiple01Aux0(ugt, toType, fromTypeArgument0, toTypeArgument0, toTypeArgument1)
|
||||
|
|
||||
convVarianceMultiple01Aux1(ugt, fromType, fromTypeArgument0, fromTypeArgument1,
|
||||
toTypeArgument1)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate convVarianceMultiple2Aux(UnboundGenericType ugt, VarianceConvertibleConstructedType toType, int i, TypeArgument fromTypeArgument, TypeArgument toTypeArgument) {
|
||||
private predicate convVarianceMultiple2Aux(
|
||||
UnboundGenericType ugt, VarianceConvertibleConstructedType toType, int i,
|
||||
TypeArgument fromTypeArgument, TypeArgument toTypeArgument
|
||||
) {
|
||||
convTypeArgumentsSameUnbound(ugt, fromTypeArgument, toTypeArgument, i) and
|
||||
toTypeArgument = getTypeArgumentRanked(ugt, toType, i, _) and
|
||||
i >= 2
|
||||
}
|
||||
|
||||
private predicate convVarianceMultiple2(UnboundGenericType ugt, VarianceConvertibleConstructedType fromType, VarianceConvertibleConstructedType toType, int i) {
|
||||
private predicate convVarianceMultiple2(
|
||||
UnboundGenericType ugt, VarianceConvertibleConstructedType fromType,
|
||||
VarianceConvertibleConstructedType toType, int i
|
||||
) {
|
||||
exists(TypeArgument fromTypeArgument, TypeArgument toTypeArgument |
|
||||
convVarianceMultiple2Aux(ugt, toType, i, fromTypeArgument, toTypeArgument) |
|
||||
convVarianceMultiple2Aux(ugt, toType, i, fromTypeArgument, toTypeArgument)
|
||||
|
|
||||
fromTypeArgument = getTypeArgumentRanked(ugt, fromType, i, _)
|
||||
)
|
||||
}
|
||||
@@ -932,7 +994,10 @@ private module Variance {
|
||||
* `fromType` and `toType` are variance convertible.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate convVarianceMultiple(UnboundGenericType ugt, VarianceConvertibleConstructedType fromType, VarianceConvertibleConstructedType toType, int i) {
|
||||
predicate convVarianceMultiple(
|
||||
UnboundGenericType ugt, VarianceConvertibleConstructedType fromType,
|
||||
VarianceConvertibleConstructedType toType, int i
|
||||
) {
|
||||
convVarianceMultiple01(ugt, fromType, toType) and i = 1
|
||||
or
|
||||
convVarianceMultiple(ugt, fromType, toType, i - 1) and
|
||||
|
||||
@@ -48,15 +48,11 @@ class Element extends DotNet::Element, @element {
|
||||
|
||||
/** Gets the number of children of this element. */
|
||||
pragma[nomagic]
|
||||
int getNumberOfChildren() {
|
||||
result = count(int i | exists(this.getChild(i)))
|
||||
}
|
||||
int getNumberOfChildren() { result = count(int i | exists(this.getChild(i))) }
|
||||
|
||||
/**
|
||||
* Gets the index of this element among its parent's
|
||||
* other children (zero-based).
|
||||
*/
|
||||
int getIndex() {
|
||||
exists(Element parent | parent.getChild(result) = this)
|
||||
}
|
||||
int getIndex() { exists(Element parent | parent.getChild(result) = this) }
|
||||
}
|
||||
|
||||
@@ -18,13 +18,15 @@ private import semmle.code.csharp.ExprOrStmtParent
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*/
|
||||
cached module Internal {
|
||||
cached
|
||||
module Internal {
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Holds if `c` is the enclosing callable of statement `s`.
|
||||
*/
|
||||
cached predicate enclosingCallable(Stmt s, Callable c) {
|
||||
cached
|
||||
predicate enclosingCallable(Stmt s, Callable c) {
|
||||
/*
|
||||
* Compute the enclosing callable for a statement. This walks up through
|
||||
* enclosing statements until it hits a callable. It's unambiguous, since
|
||||
@@ -32,6 +34,7 @@ cached module Internal {
|
||||
* or the body of an anonymous function declaration, in each of which cases the
|
||||
* non-statement parent is in fact the enclosing callable.
|
||||
*/
|
||||
|
||||
c.getAChildStmt+() = s
|
||||
}
|
||||
|
||||
@@ -45,21 +48,22 @@ cached module Internal {
|
||||
*
|
||||
* Holds if `s` is the enclosing statement of expression `e`.
|
||||
*/
|
||||
cached predicate enclosingStmt(Expr e, Stmt s) {
|
||||
cached
|
||||
predicate enclosingStmt(Expr e, Stmt s) {
|
||||
/*
|
||||
* Compute the enclosing statement for an expression. Note that this need
|
||||
* not exist, since expressions can occur in contexts where they have no
|
||||
* enclosing statement (examples include field initialisers, both inline
|
||||
* and explicit on constructor definitions, and annotation arguments).
|
||||
*/
|
||||
|
||||
getAChildExpr+(s) = e
|
||||
}
|
||||
|
||||
private predicate childExprOfCallable(Callable parent, Expr child) {
|
||||
child = getAChildExpr(parent)
|
||||
or
|
||||
exists(Expr mid |
|
||||
childExprOfCallable(parent, mid) |
|
||||
exists(Expr mid | childExprOfCallable(parent, mid) |
|
||||
not mid instanceof Callable and
|
||||
child = getAChildExpr(mid)
|
||||
)
|
||||
@@ -70,7 +74,8 @@ cached module Internal {
|
||||
*
|
||||
* Holds if `c` is the enclosing callable of expression `e`.
|
||||
*/
|
||||
cached predicate exprEnclosingCallable(Expr e, Callable c) {
|
||||
cached
|
||||
predicate exprEnclosingCallable(Expr e, Callable c) {
|
||||
/*
|
||||
* Compute the enclosing callable of an expression. Note that expressions in
|
||||
* lambda functions should have the lambdas as enclosing callables, and their
|
||||
@@ -78,6 +83,7 @@ cached module Internal {
|
||||
* lambda; thus, it is *not* safe to go up to the enclosing statement and
|
||||
* take its own enclosing callable.
|
||||
*/
|
||||
|
||||
childExprOfCallable(c, e)
|
||||
or
|
||||
not childExprOfCallable(_, e) and
|
||||
|
||||
@@ -16,12 +16,11 @@ import Type
|
||||
* ```
|
||||
*/
|
||||
class Event extends DeclarationWithAccessors, @event {
|
||||
override string getName() { events(this, result, _, _, _) }
|
||||
|
||||
override string getName() { events(this,result,_,_,_) }
|
||||
override ValueOrRefType getDeclaringType() { events(this, _, result, _, _) }
|
||||
|
||||
override ValueOrRefType getDeclaringType() { events(this,_,result,_,_) }
|
||||
|
||||
override DelegateType getType() { events(this,_,_,getTypeRef(result),_) }
|
||||
override DelegateType getType() { events(this, _, _, getTypeRef(result), _) }
|
||||
|
||||
/** Gets an `add` or `remove` accessor of this event, if any. */
|
||||
EventAccessor getAnEventAccessor() { result.getDeclaration() = this }
|
||||
@@ -43,23 +42,15 @@ class Event extends DeclarationWithAccessors, @event {
|
||||
not this.getAnEventAccessor().hasBody()
|
||||
}
|
||||
|
||||
override Event getSourceDeclaration() { events(this,_,_,_,result) }
|
||||
override Event getSourceDeclaration() { events(this, _, _, _, result) }
|
||||
|
||||
override Event getOverridee() {
|
||||
result = DeclarationWithAccessors.super.getOverridee()
|
||||
}
|
||||
override Event getOverridee() { result = DeclarationWithAccessors.super.getOverridee() }
|
||||
|
||||
override Event getAnOverrider() {
|
||||
result = DeclarationWithAccessors.super.getAnOverrider()
|
||||
}
|
||||
override Event getAnOverrider() { result = DeclarationWithAccessors.super.getAnOverrider() }
|
||||
|
||||
override Event getImplementee() {
|
||||
result = DeclarationWithAccessors.super.getImplementee()
|
||||
}
|
||||
override Event getImplementee() { result = DeclarationWithAccessors.super.getImplementee() }
|
||||
|
||||
override Event getAnImplementor() {
|
||||
result = DeclarationWithAccessors.super.getAnImplementor()
|
||||
}
|
||||
override Event getAnImplementor() { result = DeclarationWithAccessors.super.getAnImplementor() }
|
||||
|
||||
override Event getAnUltimateImplementee() {
|
||||
result = DeclarationWithAccessors.super.getAnUltimateImplementee()
|
||||
@@ -69,7 +60,7 @@ class Event extends DeclarationWithAccessors, @event {
|
||||
result = DeclarationWithAccessors.super.getAnUltimateImplementor()
|
||||
}
|
||||
|
||||
override Location getALocation() { event_location(this,result) }
|
||||
override Location getALocation() { event_location(this, result) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,13 +83,13 @@ class EventAccessor extends Accessor, @event_accessor {
|
||||
result instanceof VoidType
|
||||
}
|
||||
|
||||
override string getAssemblyName() { event_accessors(this,_,result,_,_) }
|
||||
override string getAssemblyName() { event_accessors(this, _, result, _, _) }
|
||||
|
||||
override EventAccessor getSourceDeclaration() { event_accessors(this,_,_,_,result) }
|
||||
override EventAccessor getSourceDeclaration() { event_accessors(this, _, _, _, result) }
|
||||
|
||||
override Event getDeclaration() { event_accessors(this,_,_,result,_) }
|
||||
override Event getDeclaration() { event_accessors(this, _, _, result, _) }
|
||||
|
||||
override Location getALocation() { event_accessor_location(this,result) }
|
||||
override Location getALocation() { event_accessor_location(this, result) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,5 +123,5 @@ class AddEventAccessor extends EventAccessor, @add_event_accessor {
|
||||
* ```
|
||||
*/
|
||||
class RemoveEventAccessor extends EventAccessor, @remove_event_accessor {
|
||||
override string getName() { result = "remove" + "_" + getDeclaration().getName() }
|
||||
override string getName() { result = "remove" + "_" + getDeclaration().getName() }
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*
|
||||
* Provides logic for calculating the child relation on expressions and statements.
|
||||
*/
|
||||
|
||||
import csharp
|
||||
|
||||
/**
|
||||
@@ -14,9 +15,7 @@ import csharp
|
||||
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())
|
||||
)
|
||||
parent = any(Getter g | expr_parent_top_level(child, i, g.getDeclaration()))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -51,12 +50,12 @@ predicate expr_parent_top_level_adjusted(Expr child, int i, @top_level_exprorstm
|
||||
* ```
|
||||
*/
|
||||
private predicate expr_parent_adjusted(Expr child, int i, ControlFlowElement parent) {
|
||||
exists(AssignExpr ae |
|
||||
ae = parent.(AssignOperation).getExpandedAssignment() |
|
||||
exists(AssignExpr ae | ae = parent.(AssignOperation).getExpandedAssignment() |
|
||||
i = 0 and
|
||||
exists(Expr right |
|
||||
// right = `x + y`
|
||||
expr_parent(right, 0, ae) |
|
||||
expr_parent(right, 0, ae)
|
||||
|
|
||||
expr_parent(child, 1, right)
|
||||
)
|
||||
or
|
||||
@@ -86,9 +85,7 @@ class ExprOrStmtParent extends Element, @exprorstmt_parent {
|
||||
}
|
||||
|
||||
/** Gets a child expression of this element, if any. */
|
||||
final Expr getAChildExpr() {
|
||||
result = this.getChildExpr(_)
|
||||
}
|
||||
final Expr getAChildExpr() { result = this.getChildExpr(_) }
|
||||
|
||||
/** Gets the `i`th child statement of this element (zero-based). */
|
||||
final Stmt getChildStmt(int i) {
|
||||
@@ -97,9 +94,7 @@ class ExprOrStmtParent extends Element, @exprorstmt_parent {
|
||||
}
|
||||
|
||||
/** Gets a child statement of this element, if any. */
|
||||
final Stmt getAChildStmt() {
|
||||
result = this.getChildStmt(_)
|
||||
}
|
||||
final Stmt getAChildStmt() { result = this.getChildStmt(_) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,19 +103,13 @@ class ExprOrStmtParent extends Element, @exprorstmt_parent {
|
||||
* An element that can have a child top-level expression.
|
||||
*/
|
||||
class TopLevelExprParent extends Element, @top_level_expr_parent {
|
||||
final override Expr getChild(int i) {
|
||||
result = this.getChildExpr(i)
|
||||
}
|
||||
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) { result = getTopLevelChild(this, i) }
|
||||
|
||||
/** Gets a child expression of this element, if any. */
|
||||
final Expr getAChildExpr() {
|
||||
result = this.getChildExpr(_)
|
||||
}
|
||||
final Expr getAChildExpr() { result = this.getChildExpr(_) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -159,8 +148,7 @@ class MultiImplementationsParent extends ExprOrStmtParent {
|
||||
MultiImplementationsParent() {
|
||||
exists(int i |
|
||||
strictcount(File f |
|
||||
exists(ControlFlowElement implementation, Location l |
|
||||
f = l.getFile() |
|
||||
exists(ControlFlowElement implementation, Location l | f = l.getFile() |
|
||||
stmt_parent_top_level(implementation, i, this) and
|
||||
stmt_location(implementation, l)
|
||||
or
|
||||
@@ -179,8 +167,7 @@ class MultiImplementationsParent extends ExprOrStmtParent {
|
||||
* element.
|
||||
*/
|
||||
private File getAnImplementation(int i, ControlFlowElement cfe) {
|
||||
exists(Location l |
|
||||
result = l.getFile() |
|
||||
exists(Location l | result = l.getFile() |
|
||||
stmt_parent_top_level(cfe, i, this) and
|
||||
stmt_location(cfe, l)
|
||||
or
|
||||
@@ -231,8 +218,9 @@ class MultiImplementationsParent extends ExprOrStmtParent {
|
||||
File getBestFile() {
|
||||
exists(ValueOrRefType t |
|
||||
result = max(this.getAnImplementationFileInTopLevelType(_, t) as file
|
||||
order by getImplementationSize(t, file), file.toString()
|
||||
)
|
||||
order by
|
||||
getImplementationSize(t, file), file.toString()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -242,8 +230,7 @@ class MultiImplementationsParent extends ExprOrStmtParent {
|
||||
* be the actual run-time implementation.
|
||||
*/
|
||||
ControlFlowElement getBestChild(int i) {
|
||||
exists(File f, ValueOrRefType t |
|
||||
f = getBestFile() |
|
||||
exists(File f, ValueOrRefType t | f = getBestFile() |
|
||||
f = this.getAnImplementationInTopLevelType(i, result, t)
|
||||
)
|
||||
}
|
||||
@@ -255,11 +242,9 @@ class MultiImplementationsParent extends ExprOrStmtParent {
|
||||
* Holds if accessor `a` has an auto-implementation in file `f`.
|
||||
*/
|
||||
predicate hasAccessorAutoImplementation(Accessor a, File f) {
|
||||
exists(SourceLocation sl |
|
||||
sl = a.getALocation() |
|
||||
exists(SourceLocation sl | sl = a.getALocation() |
|
||||
f = sl.getFile() and
|
||||
not exists(ControlFlowElement cfe, Location l |
|
||||
sl.getFile() = l.getFile() |
|
||||
not exists(ControlFlowElement cfe, Location l | sl.getFile() = l.getFile() |
|
||||
stmt_parent_top_level(cfe, _, a) and
|
||||
stmt_location(cfe, l)
|
||||
or
|
||||
@@ -293,15 +278,9 @@ private ValueOrRefType getDeclaringType(Declaration d) {
|
||||
or
|
||||
fields(d, _, _, result, _, _)
|
||||
or
|
||||
exists(DeclarationWithAccessors decl |
|
||||
d = decl.getAnAccessor() |
|
||||
result = getDeclaringType(decl)
|
||||
)
|
||||
exists(DeclarationWithAccessors decl | d = decl.getAnAccessor() | result = getDeclaringType(decl))
|
||||
or
|
||||
exists(Parameterizable p |
|
||||
params(d, _, _, _, _, p, _) |
|
||||
result = getDeclaringType(p)
|
||||
)
|
||||
exists(Parameterizable p | params(d, _, _, _, _, p, _) | result = getDeclaringType(p))
|
||||
}
|
||||
|
||||
private ControlFlowElement getAChild(ControlFlowElement cfe) {
|
||||
@@ -311,42 +290,45 @@ private ControlFlowElement getAChild(ControlFlowElement 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)
|
||||
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
|
||||
)
|
||||
// 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)
|
||||
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)
|
||||
}
|
||||
|
||||
cached module ExprOrStmtParentCached {
|
||||
cached
|
||||
module ExprOrStmtParentCached {
|
||||
cached
|
||||
ControlFlowElement getTopLevelChild(ExprOrStmtParent p, int i) {
|
||||
result = p.(MultiImplementationsParent).getBestChild(i)
|
||||
@@ -366,7 +348,8 @@ cached module ExprOrStmtParentCached {
|
||||
predicate mustHaveLocationInFile(Declaration d, File f) {
|
||||
exists(MultiImplementationsParent p, ValueOrRefType t |
|
||||
t = getTopLevelDeclaringType(p) and
|
||||
f = p.getBestFile() |
|
||||
f = p.getBestFile()
|
||||
|
|
||||
t = getTopLevelDeclaringType(d) or d = t or d = p
|
||||
)
|
||||
}
|
||||
@@ -385,17 +368,13 @@ cached module ExprOrStmtParentCached {
|
||||
result = e.getALocation().(SourceLocation) and
|
||||
(mustHaveLocationInFile(e, _) implies mustHaveLocationInFile(e, result.getFile()))
|
||||
or
|
||||
(
|
||||
hasNoSourceLocation(e)
|
||||
and
|
||||
result = min(Location l | l = e.getALocation() | l order by l.getFile().toString())
|
||||
)
|
||||
hasNoSourceLocation(e) and
|
||||
result = min(Location l | l = e.getALocation() | l order by l.getFile().toString())
|
||||
}
|
||||
|
||||
cached
|
||||
string getURL(Element e) {
|
||||
exists(Location l, string path, int a, int b, int c, int d |
|
||||
l = bestLocation(e) |
|
||||
exists(Location l, string path, int a, int b, int c, int d | l = bestLocation(e) |
|
||||
l.hasLocationInfo(path, a, b, c, d) and
|
||||
toUrl(path, a, b, c, d, result)
|
||||
)
|
||||
|
||||
@@ -46,8 +46,9 @@ class Container extends @container {
|
||||
* if the root folder is not a reflexive, transitive parent of this container.
|
||||
*/
|
||||
string getRelativePath() {
|
||||
exists (string absPath, string pref |
|
||||
absPath = getAbsolutePath() and sourceLocationPrefix(pref) |
|
||||
exists(string absPath, string pref |
|
||||
absPath = getAbsolutePath() and sourceLocationPrefix(pref)
|
||||
|
|
||||
absPath = pref and result = ""
|
||||
or
|
||||
absPath = pref.regexpReplaceAll("/$", "") + "/" + result and
|
||||
@@ -99,9 +100,7 @@ class Container extends @container {
|
||||
* <tr><td>"/tmp/x.tar.gz"</td><td>"gz"</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
string getExtension() {
|
||||
result = getAbsolutePath().regexpCapture(".*/([^/]*?)(\\.([^.]*))?", 3)
|
||||
}
|
||||
string getExtension() { result = getAbsolutePath().regexpCapture(".*/([^/]*?)(\\.([^.]*))?", 3) }
|
||||
|
||||
/**
|
||||
* Gets the stem of this container, that is, the prefix of its base name up to
|
||||
@@ -120,24 +119,16 @@ class Container extends @container {
|
||||
* <tr><td>"/tmp/x.tar.gz"</td><td>"x.tar"</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
string getStem() {
|
||||
result = getAbsolutePath().regexpCapture(".*/([^/]*?)(?:\\.([^.]*))?", 1)
|
||||
}
|
||||
string getStem() { result = getAbsolutePath().regexpCapture(".*/([^/]*?)(?:\\.([^.]*))?", 1) }
|
||||
|
||||
/** Gets the parent container of this file or folder, if any. */
|
||||
Container getParentContainer() {
|
||||
containerparent(result, this)
|
||||
}
|
||||
Container getParentContainer() { containerparent(result, this) }
|
||||
|
||||
/** Gets a file or sub-folder in this container. */
|
||||
Container getAChildContainer() {
|
||||
this = result.getParentContainer()
|
||||
}
|
||||
Container getAChildContainer() { this = result.getParentContainer() }
|
||||
|
||||
/** Gets a file in this container. */
|
||||
File getAFile() {
|
||||
result = getAChildContainer()
|
||||
}
|
||||
File getAFile() { result = getAChildContainer() }
|
||||
|
||||
/** Gets the file in this container that has the given `baseName`, if any. */
|
||||
File getFile(string baseName) {
|
||||
@@ -146,9 +137,7 @@ class Container extends @container {
|
||||
}
|
||||
|
||||
/** Gets a sub-folder in this container. */
|
||||
Folder getAFolder() {
|
||||
result = getAChildContainer()
|
||||
}
|
||||
Folder getAFolder() { result = getAChildContainer() }
|
||||
|
||||
/** Gets the sub-folder in this container that has the given `baseName`, if any. */
|
||||
Folder getFolder(string baseName) {
|
||||
@@ -170,60 +159,40 @@ class Container extends @container {
|
||||
}
|
||||
|
||||
/** Gets a sub-folder contained in this container. */
|
||||
Folder getASubFolder() {
|
||||
result = getAChildContainer()
|
||||
}
|
||||
Folder getASubFolder() { result = getAChildContainer() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of the path of this container.
|
||||
*
|
||||
* This is the absolute path of the container.
|
||||
*/
|
||||
string toString() {
|
||||
result = getAbsolutePath()
|
||||
}
|
||||
string toString() { result = getAbsolutePath() }
|
||||
}
|
||||
|
||||
/** A folder. */
|
||||
class Folder extends Container, @folder {
|
||||
override string getAbsolutePath() {
|
||||
folders(this, result, _)
|
||||
}
|
||||
override string getAbsolutePath() { folders(this, result, _) }
|
||||
|
||||
override string getURL() {
|
||||
result = "folder://" + getAbsolutePath()
|
||||
}
|
||||
override string getURL() { result = "folder://" + getAbsolutePath() }
|
||||
}
|
||||
|
||||
/** A file. */
|
||||
class File extends Container, @file {
|
||||
override string getAbsolutePath() {
|
||||
files(this, result, _, _, _)
|
||||
}
|
||||
override string getAbsolutePath() { files(this, result, _, _, _) }
|
||||
|
||||
/** Gets the number of lines in this file. */
|
||||
int getNumberOfLines() {
|
||||
numlines(this, result, _, _)
|
||||
}
|
||||
int getNumberOfLines() { numlines(this, result, _, _) }
|
||||
|
||||
/** Gets the number of lines containing code in this file. */
|
||||
int getNumberOfLinesOfCode() {
|
||||
numlines(this, _, result, _)
|
||||
}
|
||||
int getNumberOfLinesOfCode() { numlines(this, _, result, _) }
|
||||
|
||||
/** Gets the number of lines containing comments in this file. */
|
||||
int getNumberOfLinesOfComments() {
|
||||
numlines(this, _, _, result)
|
||||
}
|
||||
int getNumberOfLinesOfComments() { numlines(this, _, _, result) }
|
||||
|
||||
override string getURL() {
|
||||
result = "file://" + this.getAbsolutePath() + ":0:0:0:0"
|
||||
}
|
||||
override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" }
|
||||
|
||||
/** Holds if this file contains source code. */
|
||||
predicate fromSource() {
|
||||
this.getNumberOfLinesOfCode() > 0
|
||||
}
|
||||
predicate fromSource() { this.getNumberOfLinesOfCode() > 0 }
|
||||
|
||||
/** Holds if this file is a library. */
|
||||
predicate fromLibrary() {
|
||||
@@ -236,20 +205,15 @@ class File extends Container, @file {
|
||||
* A source file can come from a PDB and from regular extraction
|
||||
* in the same snapshot.
|
||||
*/
|
||||
predicate isPdbSourceFile() {
|
||||
file_extraction_mode(this, 2)
|
||||
}
|
||||
predicate isPdbSourceFile() { file_extraction_mode(this, 2) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A source file.
|
||||
*/
|
||||
class SourceFile extends File {
|
||||
|
||||
SourceFile() { this.fromSource() }
|
||||
|
||||
/** Holds if the file was extracted without building the source code. */
|
||||
predicate extractedStandalone() {
|
||||
file_extraction_mode(this, 1)
|
||||
}
|
||||
predicate extractedStandalone() { file_extraction_mode(this, 1) }
|
||||
}
|
||||
|
||||
@@ -37,13 +37,9 @@ class Generic extends DotNet::Generic, Declaration, @generic {
|
||||
class UnboundGeneric extends DotNet::UnboundGeneric, Generic {
|
||||
UnboundGeneric() { is_generic(this) }
|
||||
|
||||
override TypeParameter getTypeParameter(int n) {
|
||||
type_parameters(result, n, this, _)
|
||||
}
|
||||
override TypeParameter getTypeParameter(int n) { type_parameters(result, n, this, _) }
|
||||
|
||||
override ConstructedGeneric getAConstructedGeneric() {
|
||||
result.getUnboundGeneric() = this
|
||||
}
|
||||
override ConstructedGeneric getAConstructedGeneric() { result.getUnboundGeneric() = this }
|
||||
|
||||
override TypeParameter getATypeParameter() {
|
||||
result = DotNet::UnboundGeneric.super.getATypeParameter()
|
||||
@@ -59,24 +55,20 @@ class UnboundGeneric extends DotNet::UnboundGeneric, Generic {
|
||||
class ConstructedGeneric extends DotNet::ConstructedGeneric, Generic {
|
||||
ConstructedGeneric() { is_constructed(this) }
|
||||
|
||||
override UnboundGeneric getUnboundGeneric() {
|
||||
constructed_generic(this, result)
|
||||
}
|
||||
override UnboundGeneric getUnboundGeneric() { constructed_generic(this, result) }
|
||||
|
||||
override UnboundGeneric getSourceDeclaration() {
|
||||
result = getUnboundGeneric().getSourceDeclaration()
|
||||
}
|
||||
|
||||
override int getNumberOfTypeArguments() {
|
||||
// getTypeArgument() could fail if the type does not exist in the database
|
||||
result = count(int i | type_arguments(_, i, this))
|
||||
// getTypeArgument() could fail if the type does not exist in the database
|
||||
result = count(int i | type_arguments(_, i, this))
|
||||
}
|
||||
|
||||
override Type getTypeArgument(int i) { none() }
|
||||
|
||||
override Type getATypeArgument() {
|
||||
result=getTypeArgument(_)
|
||||
}
|
||||
override Type getATypeArgument() { result = getTypeArgument(_) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,13 +97,11 @@ class UnboundGenericType extends ValueOrRefType, UnboundGeneric {
|
||||
* arguments being the corresponding type parameter.
|
||||
*/
|
||||
ConstructedType getInstanceType() {
|
||||
result = this.getAConstructedGeneric()
|
||||
and forall(TypeParameter tp, int i | tp = this.getTypeParameter(i) | tp = result.getTypeArgument(i))
|
||||
result = this.getAConstructedGeneric() and
|
||||
forall(TypeParameter tp, int i | tp = this.getTypeParameter(i) | tp = result.getTypeArgument(i))
|
||||
}
|
||||
|
||||
override Location getALocation() {
|
||||
type_location(this, result)
|
||||
}
|
||||
override Location getALocation() { type_location(this, result) }
|
||||
|
||||
/** Gets the name of this generic type without the `<...>` brackets. */
|
||||
string getNameWithoutBrackets() {
|
||||
@@ -134,17 +124,14 @@ class UnboundGenericType extends ValueOrRefType, UnboundGeneric {
|
||||
*/
|
||||
class TypeParameter extends DotNet::TypeParameter, Type, @type_parameter {
|
||||
/** Gets the constraints on this type parameter, if any. */
|
||||
TypeParameterConstraints getConstraints() {
|
||||
result.getTypeParameter() = this
|
||||
}
|
||||
TypeParameterConstraints getConstraints() { result.getTypeParameter() = this }
|
||||
|
||||
/**
|
||||
* Holds if this type parameter is guaranteed to always be instantiated
|
||||
* to a reference type.
|
||||
*/
|
||||
predicate isRefType() {
|
||||
exists(TypeParameterConstraints tpc |
|
||||
tpc = getConstraints() |
|
||||
exists(TypeParameterConstraints tpc | tpc = getConstraints() |
|
||||
tpc.hasRefTypeConstraint() or
|
||||
exists(tpc.getClassConstraint()) or
|
||||
tpc.getATypeParameterConstraint().isRefType()
|
||||
@@ -157,50 +144,48 @@ class TypeParameter extends DotNet::TypeParameter, Type, @type_parameter {
|
||||
* to a value type.
|
||||
*/
|
||||
predicate isValueType() {
|
||||
exists(TypeParameterConstraints tpc |
|
||||
tpc = getConstraints() |
|
||||
exists(TypeParameterConstraints tpc | tpc = getConstraints() |
|
||||
tpc.hasValueTypeConstraint() or
|
||||
tpc.getATypeParameterConstraint().isValueType()
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if this type parameter is contravariant. */
|
||||
predicate isIn() {
|
||||
type_parameters(this, _, _, 2)
|
||||
}
|
||||
predicate isIn() { type_parameters(this, _, _, 2) }
|
||||
|
||||
/** Holds if this type parameter is covariant. */
|
||||
predicate isOut() {
|
||||
type_parameters(this, _, _, 1)
|
||||
}
|
||||
predicate isOut() { type_parameters(this, _, _, 1) }
|
||||
|
||||
/** Gets a type that was supplied for this parameter. */
|
||||
Type getASuppliedType()
|
||||
{
|
||||
/* A type parameter either comes from the source declaration
|
||||
or from a partially constructed generic.
|
||||
Type getASuppliedType() {
|
||||
/*
|
||||
* A type parameter either comes from the source declaration
|
||||
* or from a partially constructed generic.
|
||||
*
|
||||
* When from a source declaration, return type arguments from all ConstructedGenerics,
|
||||
* and when from a partially constructed UnboundGeneric, return type arguments from
|
||||
* directly ConstructedGenerics.
|
||||
*
|
||||
* e.g.
|
||||
*
|
||||
* class A<T1> { class B<T2> { } }
|
||||
*
|
||||
* A<T1>.B<T2> is the UnboundGenericClass source declaration,
|
||||
* A<int>.B<T2> is a partially constructed UnboundGenericClass and
|
||||
* A<int>.B<int> is a ConstructedGenericClass.
|
||||
*/
|
||||
|
||||
When from a source declaration, return type arguments from all ConstructedGenerics,
|
||||
and when from a partially constructed UnboundGeneric, return type arguments from
|
||||
directly ConstructedGenerics.
|
||||
|
||||
e.g.
|
||||
|
||||
class A<T1> { class B<T2> { } }
|
||||
|
||||
A<T1>.B<T2> is the UnboundGenericClass source declaration,
|
||||
A<int>.B<T2> is a partially constructed UnboundGenericClass and
|
||||
A<int>.B<int> is a ConstructedGenericClass.
|
||||
*/
|
||||
exists(ConstructedGeneric c, UnboundGeneric u, int tpi |
|
||||
this = u.getTypeParameter(tpi) and
|
||||
(u = c.getUnboundGeneric() or u = c.getSourceDeclaration()) and
|
||||
result = c.getTypeArgument(tpi) )
|
||||
result = c.getTypeArgument(tpi)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a non-type-parameter type that was transitively supplied for this parameter. */
|
||||
Type getAnUltimatelySuppliedType() {
|
||||
result = getASuppliedType() and not result instanceof TypeParameter or
|
||||
result = getASuppliedType() and not result instanceof TypeParameter
|
||||
or
|
||||
result = getASuppliedType().(TypeParameter).getAnUltimatelySuppliedType()
|
||||
}
|
||||
|
||||
@@ -222,7 +207,6 @@ class TypeParameter extends DotNet::TypeParameter, Type, @type_parameter {
|
||||
* ```
|
||||
*/
|
||||
class TypeParameterConstraints extends Element, @type_parameter_constraints {
|
||||
|
||||
/** Gets a specific interface constraint (if any). */
|
||||
Interface getAnInterfaceConstraint() {
|
||||
specific_type_parameter_constraints(this, getTypeRef(result))
|
||||
@@ -234,38 +218,24 @@ class TypeParameterConstraints extends Element, @type_parameter_constraints {
|
||||
}
|
||||
|
||||
/** Gets the specific class constraint (if any). */
|
||||
Class getClassConstraint() {
|
||||
specific_type_parameter_constraints(this, getTypeRef(result))
|
||||
}
|
||||
Class getClassConstraint() { specific_type_parameter_constraints(this, getTypeRef(result)) }
|
||||
|
||||
override Location getALocation() {
|
||||
type_parameter_constraints_location(this, result)
|
||||
}
|
||||
override Location getALocation() { type_parameter_constraints_location(this, result) }
|
||||
|
||||
/** Gets the type parameter to which these constraints apply. */
|
||||
TypeParameter getTypeParameter() {
|
||||
type_parameter_constraints(this, result)
|
||||
}
|
||||
TypeParameter getTypeParameter() { type_parameter_constraints(this, result) }
|
||||
|
||||
/** Holds if these constraints include a constructor constraint. */
|
||||
predicate hasConstructorConstraint() {
|
||||
general_type_parameter_constraints(this, 3)
|
||||
}
|
||||
predicate hasConstructorConstraint() { general_type_parameter_constraints(this, 3) }
|
||||
|
||||
/** Holds if these constraints include a general reference type constraint. */
|
||||
predicate hasRefTypeConstraint() {
|
||||
general_type_parameter_constraints(this, 1)
|
||||
}
|
||||
predicate hasRefTypeConstraint() { general_type_parameter_constraints(this, 1) }
|
||||
|
||||
/** Holds if these constraints include a general value type constraint. */
|
||||
predicate hasValueTypeConstraint() {
|
||||
general_type_parameter_constraints(this, 2)
|
||||
}
|
||||
predicate hasValueTypeConstraint() { general_type_parameter_constraints(this, 2) }
|
||||
|
||||
/** Gets a textual representation of these constraints. */
|
||||
override string toString() {
|
||||
result = "where " + this.getTypeParameter().toString() + ": ..."
|
||||
}
|
||||
override string toString() { result = "where " + this.getTypeParameter().toString() + ": ..." }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -364,7 +334,8 @@ class UnboundGenericDelegateType extends DelegateType, UnboundGenericType {
|
||||
}
|
||||
|
||||
override string toStringWithTypes() {
|
||||
result = getNameWithoutBrackets() + "<" + this.typeParametersToString() + ">(" + parameterTypesToString() + ")"
|
||||
result = getNameWithoutBrackets() + "<" + this.typeParametersToString() + ">(" +
|
||||
parameterTypesToString() + ")"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -384,17 +355,11 @@ class ConstructedType extends ValueOrRefType, ConstructedGeneric {
|
||||
result = ConstructedGeneric.super.getSourceDeclaration()
|
||||
}
|
||||
|
||||
override Location getALocation() {
|
||||
result = this.getSourceDeclaration().getALocation()
|
||||
}
|
||||
override Location getALocation() { result = this.getSourceDeclaration().getALocation() }
|
||||
|
||||
override Type getTypeArgument(int n) {
|
||||
type_arguments(getTypeRef(result), n, getTypeRef(this))
|
||||
}
|
||||
override Type getTypeArgument(int n) { type_arguments(getTypeRef(result), n, getTypeRef(this)) }
|
||||
|
||||
override UnboundGenericType getUnboundGeneric() {
|
||||
constructed_generic(this, getTypeRef(result))
|
||||
}
|
||||
override UnboundGenericType getUnboundGeneric() { constructed_generic(this, getTypeRef(result)) }
|
||||
|
||||
override string toStringWithTypes() {
|
||||
result = getUnboundGeneric().getNameWithoutBrackets() + "<" + this.typeArgumentsToString() + ">"
|
||||
@@ -516,7 +481,8 @@ class UnboundGenericMethod extends Method, UnboundGeneric {
|
||||
}
|
||||
|
||||
override string toStringWithTypes() {
|
||||
result = getName() + "<" + this.typeParametersToString() + ">" + "(" + parameterTypesToString() + ")"
|
||||
result = getName() + "<" + this.typeParametersToString() + ">" + "(" + parameterTypesToString() +
|
||||
")"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -539,20 +505,15 @@ class UnboundGenericMethod extends Method, UnboundGeneric {
|
||||
* corresponds to `UnboundGenericType`.
|
||||
*/
|
||||
class ConstructedMethod extends Method, ConstructedGeneric {
|
||||
override Location getALocation() {
|
||||
result = this.getSourceDeclaration().getALocation()
|
||||
}
|
||||
override Location getALocation() { result = this.getSourceDeclaration().getALocation() }
|
||||
|
||||
override Type getTypeArgument(int n) {
|
||||
type_arguments(getTypeRef(result), n, this)
|
||||
}
|
||||
override Type getTypeArgument(int n) { type_arguments(getTypeRef(result), n, this) }
|
||||
|
||||
override UnboundGenericMethod getUnboundGeneric() {
|
||||
constructed_generic(this, result)
|
||||
}
|
||||
override UnboundGenericMethod getUnboundGeneric() { constructed_generic(this, result) }
|
||||
|
||||
override string toStringWithTypes() {
|
||||
result = getName() + "<" + this.typeArgumentsToString() + ">" + "(" + parameterTypesToString() + ")"
|
||||
result = getName() + "<" + this.typeArgumentsToString() + ">" + "(" + parameterTypesToString() +
|
||||
")"
|
||||
}
|
||||
|
||||
override UnboundGenericMethod getSourceDeclaration() {
|
||||
@@ -571,8 +532,7 @@ class ConstructedMethod extends Method, ConstructedGeneric {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class UnboundLocalFunction extends LocalFunction, UnboundGeneric
|
||||
{
|
||||
class UnboundLocalFunction extends LocalFunction, UnboundGeneric {
|
||||
override ConstructedLocalFunction getAConstructedGeneric() {
|
||||
result = UnboundGeneric.super.getAConstructedGeneric()
|
||||
}
|
||||
@@ -591,10 +551,9 @@ class UnboundLocalFunction extends LocalFunction, UnboundGeneric
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class ConstructedLocalFunction extends LocalFunction, ConstructedGeneric
|
||||
{
|
||||
class ConstructedLocalFunction extends LocalFunction, ConstructedGeneric {
|
||||
override UnboundLocalFunction getSourceDeclaration() {
|
||||
result=LocalFunction.super.getSourceDeclaration()
|
||||
result = LocalFunction.super.getSourceDeclaration()
|
||||
}
|
||||
|
||||
override UnboundLocalFunction getUnboundGeneric() {
|
||||
@@ -607,9 +566,7 @@ class ConstructedLocalFunction extends LocalFunction, ConstructedGeneric
|
||||
* not a generic method or an unbound generic method (`UnboundGenericMethod`).
|
||||
*/
|
||||
class NonConstructedMethod extends Method {
|
||||
NonConstructedMethod() {
|
||||
not this instanceof ConstructedMethod
|
||||
}
|
||||
NonConstructedMethod() { not this instanceof ConstructedMethod }
|
||||
|
||||
/**
|
||||
* Gets a method that either equals this non-constructed method, or
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
* Do not use the predicates in this library directly; use the methods
|
||||
* of the class `Virtualizable` instead.
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import Conversion
|
||||
private import dispatch.Dispatch
|
||||
@@ -76,7 +77,8 @@ private Virtualizable getAnImplementedInterfaceMemberForSubType(Virtualizable m,
|
||||
)
|
||||
}
|
||||
|
||||
pragma [noinline] // predicate folding for proper join order
|
||||
// predicate folding for proper join order
|
||||
pragma[noinline]
|
||||
private predicate hasMemberCompatibleWithInterfaceMember(ValueOrRefType t, Virtualizable m) {
|
||||
m = getACompatibleInterfaceMember(t.getAMember())
|
||||
}
|
||||
@@ -121,15 +123,16 @@ private DeclarationWithAccessors getACompatibleInterfaceAccessor(DeclarationWith
|
||||
}
|
||||
|
||||
private DeclarationWithAccessors getACompatibleInterfaceAccessorCandidate(DeclarationWithAccessors d) {
|
||||
getACompatibleInterfaceAccessorAux(result, d.getDeclaringType(), d.getName())
|
||||
and
|
||||
not d instanceof Indexer
|
||||
and
|
||||
getACompatibleInterfaceAccessorAux(result, d.getDeclaringType(), d.getName()) and
|
||||
not d instanceof Indexer and
|
||||
d.isPublic()
|
||||
}
|
||||
|
||||
pragma [noinline] // predicate folding for proper join-order
|
||||
private predicate getACompatibleInterfaceAccessorAux(DeclarationWithAccessors d, ValueOrRefType t, string name) {
|
||||
// predicate folding for proper join-order
|
||||
pragma[noinline]
|
||||
private predicate getACompatibleInterfaceAccessorAux(
|
||||
DeclarationWithAccessors d, ValueOrRefType t, string name
|
||||
) {
|
||||
t = getAPossibleImplementor(d.getDeclaringType()) and
|
||||
name = d.getName()
|
||||
}
|
||||
@@ -155,8 +158,7 @@ private predicate getACompatibleInterfaceAccessorAux(DeclarationWithAccessors d,
|
||||
*/
|
||||
pragma[nomagic]
|
||||
ValueOrRefType getAPossibleImplementor(Interface i) {
|
||||
result.getASubType*().getABaseInterface+() = i
|
||||
and
|
||||
result.getASubType*().getABaseInterface+() = i and
|
||||
not result instanceof Interface
|
||||
}
|
||||
|
||||
@@ -166,36 +168,33 @@ ValueOrRefType getAPossibleImplementor(Interface i) {
|
||||
* the interface indexer is accessed.
|
||||
*/
|
||||
private Indexer getACompatibleInterfaceIndexer(Indexer i) {
|
||||
result = getACompatibleInterfaceIndexerCandidate(i)
|
||||
and
|
||||
convIdentity(i.getType(), result.getType())
|
||||
and
|
||||
forex(int j |
|
||||
j in [0 .. i.getNumberOfParameters() - 1] |
|
||||
result = getACompatibleInterfaceIndexerCandidate(i) and
|
||||
convIdentity(i.getType(), result.getType()) and
|
||||
forex(int j | j in [0 .. i.getNumberOfParameters() - 1] |
|
||||
convIdentity(i.getParameter(j).getType(), result.getParameter(j).getType())
|
||||
)
|
||||
}
|
||||
|
||||
private Indexer getACompatibleInterfaceIndexerCandidate(Indexer i) {
|
||||
getACompatibleInterfaceIndexerAux(result, i.getDeclaringType())
|
||||
and
|
||||
getACompatibleInterfaceIndexerAux(result, i.getDeclaringType()) and
|
||||
i.isPublic()
|
||||
}
|
||||
|
||||
pragma [noinline] // predicate folding for proper join-order
|
||||
// predicate folding for proper join-order
|
||||
pragma[noinline]
|
||||
private predicate getACompatibleInterfaceIndexerAux(Indexer i, ValueOrRefType t) {
|
||||
t = getAPossibleImplementor(i.getDeclaringType())
|
||||
}
|
||||
|
||||
private Method getACompatibleInterfaceMethod(Method m) {
|
||||
result = getAnInterfaceMethodCandidate(m)
|
||||
and
|
||||
forex(int i |
|
||||
i in [0 .. m.getNumberOfParameters()] |
|
||||
result = getAnInterfaceMethodCandidate(m) and
|
||||
forex(int i | i in [0 .. m.getNumberOfParameters()] |
|
||||
exists(Type t1, Type t2 |
|
||||
t1 = getArgumentOrReturnType(m, i) and
|
||||
t2 = getArgumentOrReturnType(result, i) |
|
||||
convIdentity(t1, t2) or
|
||||
t2 = getArgumentOrReturnType(result, i)
|
||||
|
|
||||
convIdentity(t1, t2)
|
||||
or
|
||||
// In the case where both `m` and `result` are unbound generic methods,
|
||||
// we need to do check for structural equality modulo the method type
|
||||
// parameters
|
||||
@@ -211,13 +210,16 @@ private Method getACompatibleInterfaceMethod(Method m) {
|
||||
* in a type that is a possible implementor type for the interface type.
|
||||
*/
|
||||
private Method getAnInterfaceMethodCandidate(Method m) {
|
||||
getAPotentialInterfaceMethodAux(result, m.getDeclaringType(), m.getName(), m.getNumberOfParameters())
|
||||
and
|
||||
getAPotentialInterfaceMethodAux(result, m.getDeclaringType(), m.getName(),
|
||||
m.getNumberOfParameters()) and
|
||||
m.isPublic()
|
||||
}
|
||||
|
||||
pragma [nomagic] // predicate folding for proper join-order
|
||||
private predicate getAPotentialInterfaceMethodAux(Method m, ValueOrRefType t, string name, int params) {
|
||||
// predicate folding for proper join-order
|
||||
pragma[nomagic]
|
||||
private predicate getAPotentialInterfaceMethodAux(
|
||||
Method m, ValueOrRefType t, string name, int params
|
||||
) {
|
||||
t = getAPossibleImplementor(m.getDeclaringType()) and
|
||||
name = m.getName() and
|
||||
params = m.getNumberOfParameters()
|
||||
@@ -239,37 +241,37 @@ private Type getArgumentOrReturnType(Method m, int i) {
|
||||
* Here, compatibility means that the two types are structurally equal
|
||||
* modulo identity conversions and method type parameters.
|
||||
*/
|
||||
private predicate structurallyCompatible(UnboundGenericMethod m1, UnboundGenericMethod m2, Type t1, Type t2) {
|
||||
private predicate structurallyCompatible(
|
||||
UnboundGenericMethod m1, UnboundGenericMethod m2, Type t1, Type t2
|
||||
) {
|
||||
candidateTypes(m1, m2, t1, t2) and
|
||||
(
|
||||
// Base case: identity convertible
|
||||
convIdentity(t1, t2)
|
||||
or
|
||||
// Base case: method type parameter (at same index)
|
||||
exists(int i |
|
||||
structurallyCompatibleTypeParameter(m1, m2, i, t1, m2.getTypeParameter(i))
|
||||
)
|
||||
exists(int i | structurallyCompatibleTypeParameter(m1, m2, i, t1, m2.getTypeParameter(i)))
|
||||
or
|
||||
// Recursive case
|
||||
(
|
||||
(
|
||||
t1 instanceof PointerType and t2 instanceof PointerType
|
||||
or
|
||||
t1 instanceof NullableType and t2 instanceof NullableType
|
||||
or
|
||||
t1.(ArrayType).hasSameShapeAs((ArrayType)t2)
|
||||
or
|
||||
t1.(ConstructedType).getUnboundGeneric() = t2.(ConstructedType).getUnboundGeneric()
|
||||
)
|
||||
and
|
||||
structurallyCompatibleChildren(m1, m2, t1, t2, t1.getNumberOfChildren() - 1)
|
||||
)
|
||||
t1 instanceof PointerType and t2 instanceof PointerType
|
||||
or
|
||||
t1 instanceof NullableType and t2 instanceof NullableType
|
||||
or
|
||||
t1.(ArrayType).hasSameShapeAs(t2.(ArrayType))
|
||||
or
|
||||
t1.(ConstructedType).getUnboundGeneric() = t2.(ConstructedType).getUnboundGeneric()
|
||||
) and
|
||||
structurallyCompatibleChildren(m1, m2, t1, t2, t1.getNumberOfChildren() - 1)
|
||||
)
|
||||
}
|
||||
|
||||
// Predicate folding to force joining on `candidateTypes` first
|
||||
pragma [nomagic] // to prevent `private#structurallyCompatibleTypeParameter#fbbfb`
|
||||
private predicate structurallyCompatibleTypeParameter(UnboundGenericMethod m1, UnboundGenericMethod m2, int i, Type t1, Type t2) {
|
||||
// to prevent `private#structurallyCompatibleTypeParameter#fbbfb`
|
||||
pragma[nomagic]
|
||||
private predicate structurallyCompatibleTypeParameter(
|
||||
UnboundGenericMethod m1, UnboundGenericMethod m2, int i, Type t1, Type t2
|
||||
) {
|
||||
candidateTypes(m1, m2, t1, t2) and
|
||||
t1 = m1.getTypeParameter(i)
|
||||
}
|
||||
@@ -277,7 +279,9 @@ private predicate structurallyCompatibleTypeParameter(UnboundGenericMethod m1, U
|
||||
/**
|
||||
* Holds if the `i+1` first children of types `x` and `y` are compatible.
|
||||
*/
|
||||
private predicate structurallyCompatibleChildren(UnboundGenericMethod m1, UnboundGenericMethod m2, Type t1, Type t2, int i) {
|
||||
private predicate structurallyCompatibleChildren(
|
||||
UnboundGenericMethod m1, UnboundGenericMethod m2, Type t1, Type t2, int i
|
||||
) {
|
||||
exists(Type t3, Type t4 |
|
||||
i = 0 and
|
||||
candidateChildren(t1, t2, i, t3, t4) and
|
||||
@@ -293,8 +297,7 @@ private predicate structurallyCompatibleChildren(UnboundGenericMethod m1, Unboun
|
||||
|
||||
// manual magic predicate used to enumerate types relevant for structural comparison
|
||||
private predicate candidateTypes(UnboundGenericMethod m1, UnboundGenericMethod m2, Type t1, Type t2) {
|
||||
m2 = getAnInterfaceMethodCandidate(m1)
|
||||
and
|
||||
m2 = getAnInterfaceMethodCandidate(m1) and
|
||||
(
|
||||
exists(int i |
|
||||
t1 = getArgumentOrReturnType(m1, i) and
|
||||
@@ -309,7 +312,8 @@ private predicate candidateTypes(UnboundGenericMethod m1, UnboundGenericMethod m
|
||||
)
|
||||
}
|
||||
|
||||
pragma [noinline] // predicate folding for proper join-order
|
||||
// predicate folding for proper join-order
|
||||
pragma[noinline]
|
||||
private predicate candidateChildren(Type t1, Type t2, int i, Type t3, Type t4) {
|
||||
candidateTypes(_, _, t1, t2) and
|
||||
t3 = t1.getChild(i) and
|
||||
|
||||
@@ -18,9 +18,7 @@ private import Attribute
|
||||
/**
|
||||
* A location of a program element.
|
||||
*/
|
||||
class Location extends @location
|
||||
{
|
||||
|
||||
class Location extends @location {
|
||||
/** Gets the file of the location. */
|
||||
File getFile() { none() }
|
||||
|
||||
@@ -31,7 +29,11 @@ class Location extends @location
|
||||
* For more information, see
|
||||
* [LGTM locations](https://lgtm.com/help/ql/locations).
|
||||
*/
|
||||
predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) { none() }
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this location. */
|
||||
string toString() { none() }
|
||||
@@ -54,41 +56,40 @@ class Location extends @location
|
||||
* within the file.
|
||||
*/
|
||||
class SourceLocation extends Location, @location_default {
|
||||
override File getFile() { locations_default(this, result, _, _, _, _) }
|
||||
|
||||
override File getFile() {
|
||||
locations_default(this,result,_,_,_,_)
|
||||
}
|
||||
|
||||
override predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) {
|
||||
exists(File f |
|
||||
locations_default(this, f, startline, startcolumn, endline, endcolumn) |
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
exists(File f | locations_default(this, f, startline, startcolumn, endline, endcolumn) |
|
||||
filepath = f.getAbsolutePath()
|
||||
)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
exists(string filepath, int startline, int startcolumn, int endline, int endcolumn |
|
||||
this.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) |
|
||||
this.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
|
|
||||
result = filepath + ":" + startline + ":" + startcolumn + ":" + endline + ":" + endcolumn
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
bindingset[version]
|
||||
private int versionField(string version, int field)
|
||||
{
|
||||
private int versionField(string version, int field) {
|
||||
exists(string format |
|
||||
format = "(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)" or
|
||||
format = "(\\d+)\\.(\\d+)\\.(\\d+)" or
|
||||
format = "(\\d+)\\.(\\d+)" |
|
||||
result = version.regexpCapture(format, field).toInt())
|
||||
and result >= 0
|
||||
and result <= 255
|
||||
format = "(\\d+)\\.(\\d+)"
|
||||
|
|
||||
result = version.regexpCapture(format, field).toInt()
|
||||
) and
|
||||
result >= 0 and
|
||||
result <= 255
|
||||
}
|
||||
|
||||
/** An assembly version, for example `4.0.0.0` or `4.5`. */
|
||||
class Version extends string
|
||||
{
|
||||
class Version extends string {
|
||||
bindingset[this]
|
||||
Version() { exists(versionField(this, 1)) }
|
||||
|
||||
@@ -97,13 +98,9 @@ class Version extends string
|
||||
* If the field is unspecified in the version string, then the result is `0`.
|
||||
*/
|
||||
bindingset[this]
|
||||
int getField(int field)
|
||||
{
|
||||
field in [1..4]
|
||||
and
|
||||
if exists(versionField(this, field))
|
||||
then result = versionField(this, field)
|
||||
else result = 0
|
||||
int getField(int field) {
|
||||
field in [1 .. 4] and
|
||||
if exists(versionField(this, field)) then result = versionField(this, field) else result = 0
|
||||
}
|
||||
|
||||
/** Gets the major version, for example `1` in `1.2.3.4`. */
|
||||
@@ -133,10 +130,9 @@ class Version extends string
|
||||
* For example, `4.0.0.0` is earlier than `4.5`.
|
||||
*/
|
||||
bindingset[this, other]
|
||||
predicate isEarlierThan(Version other)
|
||||
{
|
||||
predicate isEarlierThan(Version other) {
|
||||
exists(int i | this.getField(i) < other.getField(i) |
|
||||
forall(int j | j in [1..i-1] | this.getField(j) = other.getField(j))
|
||||
forall(int j | j in [1 .. i - 1] | this.getField(j) = other.getField(j))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -150,9 +146,7 @@ class Version extends string
|
||||
int compareTo(Version other) {
|
||||
if this.isEarlierThan(other)
|
||||
then result = -1
|
||||
else if other.isEarlierThan(this)
|
||||
then result = 1
|
||||
else result = 0
|
||||
else if other.isEarlierThan(this) then result = 1 else result = 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,21 +154,22 @@ class Version extends string
|
||||
* A .NET assembly location.
|
||||
*/
|
||||
class Assembly extends Location, Attributable, @assembly {
|
||||
|
||||
/** Gets the full name of this assembly, including its version and public token. */
|
||||
string getFullName() { assemblies(this,_,result,_,_) }
|
||||
string getFullName() { assemblies(this, _, result, _, _) }
|
||||
|
||||
/** Gets the name of this assembly. */
|
||||
string getName() { assemblies(this,_,_,result,_) }
|
||||
string getName() { assemblies(this, _, _, result, _) }
|
||||
|
||||
/** Gets the version of this assembly. */
|
||||
Version getVersion() { assemblies(this,_,_,_,result) }
|
||||
Version getVersion() { assemblies(this, _, _, _, result) }
|
||||
|
||||
override File getFile() { assemblies(this,result,_,_,_) }
|
||||
override File getFile() { assemblies(this, result, _, _, _) }
|
||||
|
||||
override string toString() { result = this.getFullName() }
|
||||
|
||||
override predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) {
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
exists(File f |
|
||||
assemblies(this, f, _, _, _) and
|
||||
filepath = f.getAbsolutePath() and
|
||||
|
||||
@@ -16,7 +16,7 @@ class Declaration extends DotNet::Declaration, Element, @declaration {
|
||||
override ValueOrRefType getDeclaringType() { none() }
|
||||
|
||||
/** Holds if this declaration is unconstructed and in source code. */
|
||||
predicate isSourceDeclaration() { fromSource() and this=getSourceDeclaration() }
|
||||
predicate isSourceDeclaration() { fromSource() and this = getSourceDeclaration() }
|
||||
|
||||
override string toString() { result = this.getName() }
|
||||
|
||||
@@ -40,9 +40,7 @@ class Declaration extends DotNet::Declaration, Element, @declaration {
|
||||
* Holds if this declaration has been generated by the compiler, for example
|
||||
* implicit constructors or accessors.
|
||||
*/
|
||||
predicate isCompilerGenerated() {
|
||||
compiler_generated(this)
|
||||
}
|
||||
predicate isCompilerGenerated() { compiler_generated(this) }
|
||||
}
|
||||
|
||||
/** A declaration that can have a modifier. */
|
||||
@@ -140,29 +138,23 @@ class Virtualizable extends Member, @virtualizable {
|
||||
* to members that can be declared on an interface, i.e. methods, properties,
|
||||
* indexers and events.
|
||||
*/
|
||||
Interface getExplicitlyImplementedInterface() {
|
||||
explicitly_implements(this, getTypeRef(result))
|
||||
}
|
||||
Interface getExplicitlyImplementedInterface() { explicitly_implements(this, getTypeRef(result)) }
|
||||
|
||||
/**
|
||||
* Holds if this member implements an interface member explicitly.
|
||||
*/
|
||||
predicate implementsExplicitInterface() {
|
||||
exists(getExplicitlyImplementedInterface())
|
||||
}
|
||||
predicate implementsExplicitInterface() { exists(getExplicitlyImplementedInterface()) }
|
||||
|
||||
/** Holds if this member can be overridden or implemented. */
|
||||
predicate isOverridableOrImplementable() {
|
||||
not isSealed()
|
||||
and
|
||||
not getDeclaringType().isSealed()
|
||||
and
|
||||
(
|
||||
isVirtual() or
|
||||
isOverride() or
|
||||
isAbstract() or
|
||||
getDeclaringType() instanceof Interface
|
||||
)
|
||||
not isSealed() and
|
||||
not getDeclaringType().isSealed() and
|
||||
(
|
||||
isVirtual() or
|
||||
isOverride() or
|
||||
isAbstract() or
|
||||
getDeclaringType() instanceof Interface
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the member that is immediately overridden by this member, if any. */
|
||||
@@ -273,7 +265,8 @@ class Virtualizable extends Member, @virtualizable {
|
||||
*/
|
||||
Virtualizable getAnUltimateImplementee() {
|
||||
exists(Virtualizable implementation, ValueOrRefType implementationType |
|
||||
implements(implementation, result, implementationType) |
|
||||
implements(implementation, result, implementationType)
|
||||
|
|
||||
this = implementation
|
||||
or
|
||||
getOverridee+() = implementation and
|
||||
@@ -333,15 +326,14 @@ class Parameterizable extends Declaration, @parameterizable {
|
||||
private string parameterTypeToString(int i) {
|
||||
exists(Parameter p, string prefix |
|
||||
p = getParameter(i) and
|
||||
result = prefix + p.getType().toStringWithTypes() |
|
||||
if p.isOut() then
|
||||
prefix = "out "
|
||||
else if p.isRef() then
|
||||
prefix = "ref "
|
||||
else if p.isParams() then
|
||||
prefix = "params "
|
||||
result = prefix + p.getType().toStringWithTypes()
|
||||
|
|
||||
if p.isOut()
|
||||
then prefix = "out "
|
||||
else
|
||||
prefix = ""
|
||||
if p.isRef()
|
||||
then prefix = "ref "
|
||||
else if p.isParams() then prefix = "params " else prefix = ""
|
||||
)
|
||||
}
|
||||
|
||||
@@ -349,11 +341,8 @@ class Parameterizable extends Declaration, @parameterizable {
|
||||
* Gets the types of the parameters of this declaration as a
|
||||
* comma-separated string.
|
||||
*/
|
||||
language [monotonicAggregates]
|
||||
language[monotonicAggregates]
|
||||
string parameterTypesToString() {
|
||||
result = concat(int i |
|
||||
exists(getParameter(i)) |
|
||||
parameterTypeToString(i), ", " order by i asc
|
||||
)
|
||||
result = concat(int i | exists(getParameter(i)) | parameterTypeToString(i), ", " order by i)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import Element
|
||||
* A modifier, for example `static`, `virtual`, `public` and so on.
|
||||
*/
|
||||
class Modifier extends Element, @modifier {
|
||||
|
||||
/** Gets the name of this modifier, for example `public`. */
|
||||
string getName() { modifiers(this, result) }
|
||||
|
||||
|
||||
@@ -106,7 +106,8 @@ class Namespace extends DotNet::Namespace, TypeContainer, @namespace {
|
||||
DelegateType getADelegate() { result = this.getATypeDeclaration() }
|
||||
|
||||
override predicate fromSource() {
|
||||
exists(ValueOrRefType t | t.getNamespace() = this and t.fromSource()) or
|
||||
exists(ValueOrRefType t | t.getNamespace() = this and t.fromSource())
|
||||
or
|
||||
exists(Namespace n | n.getParentNamespace() = this and n.fromSource())
|
||||
}
|
||||
|
||||
@@ -115,18 +116,13 @@ class Namespace extends DotNet::Namespace, TypeContainer, @namespace {
|
||||
/** Gets a declaration of this namespace, if any. */
|
||||
NamespaceDeclaration getADeclaration() { result.getNamespace() = this }
|
||||
|
||||
override Location getALocation() {
|
||||
result = this.getADeclaration().getALocation()
|
||||
}
|
||||
override Location getALocation() { result = this.getADeclaration().getALocation() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The global namespace. This is the root of all namespaces.
|
||||
*/
|
||||
class GlobalNamespace extends Namespace {
|
||||
|
||||
GlobalNamespace() { this.hasName("") }
|
||||
}
|
||||
class GlobalNamespace extends Namespace { GlobalNamespace() { this.hasName("") } }
|
||||
|
||||
/**
|
||||
* An explicit namespace declaration in a source file. For example:
|
||||
@@ -138,7 +134,6 @@ class GlobalNamespace extends Namespace {
|
||||
* ```
|
||||
*/
|
||||
class NamespaceDeclaration extends Element, @namespace_declaration {
|
||||
|
||||
/**
|
||||
* Gets the declared namespace, for example `N1.N2` in
|
||||
*
|
||||
@@ -148,7 +143,7 @@ class NamespaceDeclaration extends Element, @namespace_declaration {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
Namespace getNamespace() { namespace_declarations(this,result) }
|
||||
Namespace getNamespace() { namespace_declarations(this, result) }
|
||||
|
||||
/**
|
||||
* Gets the parent namespace declaration, if any. In the following example,
|
||||
@@ -168,7 +163,9 @@ class NamespaceDeclaration extends Element, @namespace_declaration {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
NamespaceDeclaration getParentNamespaceDeclaration() { parent_namespace_declaration(this,result) }
|
||||
NamespaceDeclaration getParentNamespaceDeclaration() {
|
||||
parent_namespace_declaration(this, result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a child namespace declaration, if any. In the following example,
|
||||
@@ -182,8 +179,10 @@ class NamespaceDeclaration extends Element, @namespace_declaration {
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
NamespaceDeclaration getAChildNamespaceDeclaration() { parent_namespace_declaration(result,this) }
|
||||
*/
|
||||
NamespaceDeclaration getAChildNamespaceDeclaration() {
|
||||
parent_namespace_declaration(result, this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a type directly declared within this namespace declaration.
|
||||
|
||||
@@ -12,15 +12,12 @@ private import dotnet
|
||||
* A declaration that may have accessors. Either an event (`Event`), a property
|
||||
* (`Property`), or an indexer (`Indexer`).
|
||||
*/
|
||||
class DeclarationWithAccessors extends AssignableMember, Virtualizable, Attributable, @declaration_with_accessors {
|
||||
class DeclarationWithAccessors extends AssignableMember, Virtualizable, Attributable,
|
||||
@declaration_with_accessors {
|
||||
/** Gets an accessor of this declaration. */
|
||||
Accessor getAnAccessor() {
|
||||
result.getDeclaration() = this
|
||||
}
|
||||
Accessor getAnAccessor() { result.getDeclaration() = this }
|
||||
|
||||
override DeclarationWithAccessors getOverridee() {
|
||||
result = Virtualizable.super.getOverridee()
|
||||
}
|
||||
override DeclarationWithAccessors getOverridee() { result = Virtualizable.super.getOverridee() }
|
||||
|
||||
override DeclarationWithAccessors getAnOverrider() {
|
||||
result = Virtualizable.super.getAnOverrider()
|
||||
@@ -53,7 +50,8 @@ class DeclarationWithAccessors extends AssignableMember, Virtualizable, Attribut
|
||||
* A declaration that may have a `get` accessor and a `set` accessor. Either a
|
||||
* property (`Property`) or an indexer (`Indexer`).
|
||||
*/
|
||||
class DeclarationWithGetSetAccessors extends DeclarationWithAccessors, TopLevelExprParent, @assignable_with_accessors {
|
||||
class DeclarationWithGetSetAccessors extends DeclarationWithAccessors, TopLevelExprParent,
|
||||
@assignable_with_accessors {
|
||||
/** Gets the `get` accessor of this declaration, if any. */
|
||||
Getter getGetter() { result = getAnAccessor() }
|
||||
|
||||
@@ -103,9 +101,7 @@ class DeclarationWithGetSetAccessors extends DeclarationWithAccessors, TopLevelE
|
||||
}
|
||||
|
||||
/** Gets the expression body of this declaration, if any. */
|
||||
Expr getExpressionBody() {
|
||||
result = this.getChildExpr(0)
|
||||
}
|
||||
Expr getExpressionBody() { result = this.getChildExpr(0) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,17 +114,11 @@ class DeclarationWithGetSetAccessors extends DeclarationWithAccessors, TopLevelE
|
||||
* ```
|
||||
*/
|
||||
class Property extends DotNet::Property, DeclarationWithGetSetAccessors, @property {
|
||||
override string getName() {
|
||||
properties(this, result, _, _, _)
|
||||
}
|
||||
override string getName() { properties(this, result, _, _, _) }
|
||||
|
||||
override ValueOrRefType getDeclaringType() {
|
||||
properties(this, _, result, _, _)
|
||||
}
|
||||
override ValueOrRefType getDeclaringType() { properties(this, _, result, _, _) }
|
||||
|
||||
override Type getType() {
|
||||
properties(this, _, _, getTypeRef(result), _)
|
||||
}
|
||||
override Type getType() { properties(this, _, _, getTypeRef(result), _) }
|
||||
|
||||
/**
|
||||
* Holds if this property is automatically implemented. For example, `P1`
|
||||
@@ -157,13 +147,9 @@ class Property extends DotNet::Property, DeclarationWithGetSetAccessors, @proper
|
||||
not this.getAnAccessor().hasBody()
|
||||
}
|
||||
|
||||
override Property getSourceDeclaration() {
|
||||
properties(this, _, _, _, result)
|
||||
}
|
||||
override Property getSourceDeclaration() { properties(this, _, _, _, result) }
|
||||
|
||||
override Property getOverridee() {
|
||||
result = DeclarationWithGetSetAccessors.super.getOverridee()
|
||||
}
|
||||
override Property getOverridee() { result = DeclarationWithGetSetAccessors.super.getOverridee() }
|
||||
|
||||
override Property getAnOverrider() {
|
||||
result = DeclarationWithGetSetAccessors.super.getAnOverrider()
|
||||
@@ -185,13 +171,9 @@ class Property extends DotNet::Property, DeclarationWithGetSetAccessors, @proper
|
||||
result = DeclarationWithGetSetAccessors.super.getAnUltimateImplementor()
|
||||
}
|
||||
|
||||
override PropertyAccess getAnAccess() {
|
||||
result.getTarget() = this
|
||||
}
|
||||
override PropertyAccess getAnAccess() { result.getTarget() = this }
|
||||
|
||||
override Location getALocation() {
|
||||
property_location(this, result)
|
||||
}
|
||||
override Location getALocation() { property_location(this, result) }
|
||||
|
||||
override Expr getAnAssignedValue() {
|
||||
result = DeclarationWithGetSetAccessors.super.getAnAssignedValue()
|
||||
@@ -200,13 +182,15 @@ class Property extends DotNet::Property, DeclarationWithGetSetAccessors, @proper
|
||||
* For library types, we don't know about assignments in constructors. We instead assume that
|
||||
* arguments passed to parameters of constructors with suitable names.
|
||||
*/
|
||||
|
||||
getDeclaringType().fromLibrary() and
|
||||
exists(Parameter param, Constructor c, string propertyName |
|
||||
propertyName = getName() and
|
||||
c = getDeclaringType().getAConstructor() and
|
||||
param = c.getAParameter() and
|
||||
// Find a constructor parameter with the same name, but with a lower case initial letter.
|
||||
param.hasName(propertyName.charAt(0).toLowerCase() + propertyName.suffix(1)) |
|
||||
param.hasName(propertyName.charAt(0).toLowerCase() + propertyName.suffix(1))
|
||||
|
|
||||
result = param.getAnAssignedArgument()
|
||||
)
|
||||
}
|
||||
@@ -238,6 +222,7 @@ class Property extends DotNet::Property, DeclarationWithGetSetAccessors, @proper
|
||||
}
|
||||
|
||||
override Getter getGetter() { result = DeclarationWithGetSetAccessors.super.getGetter() }
|
||||
|
||||
override Setter getSetter() { result = DeclarationWithGetSetAccessors.super.getSetter() }
|
||||
}
|
||||
|
||||
@@ -253,26 +238,16 @@ class Property extends DotNet::Property, DeclarationWithGetSetAccessors, @proper
|
||||
* ```
|
||||
*/
|
||||
class Indexer extends DeclarationWithGetSetAccessors, Parameterizable, @indexer {
|
||||
override string getName() {
|
||||
indexers(this, result, _, _, _)
|
||||
}
|
||||
override string getName() { indexers(this, result, _, _, _) }
|
||||
|
||||
/** Gets the dimension of this indexer, that is, its number of parameters. */
|
||||
int getDimension() {
|
||||
result = getNumberOfParameters()
|
||||
}
|
||||
int getDimension() { result = getNumberOfParameters() }
|
||||
|
||||
override ValueOrRefType getDeclaringType() {
|
||||
indexers(this, _, result, _, _)
|
||||
}
|
||||
override ValueOrRefType getDeclaringType() { indexers(this, _, result, _, _) }
|
||||
|
||||
override Type getType() {
|
||||
indexers(this, _, _, getTypeRef(result), _)
|
||||
}
|
||||
override Type getType() { indexers(this, _, _, getTypeRef(result), _) }
|
||||
|
||||
override IndexerAccess getAnAccess() {
|
||||
result.getTarget() = this
|
||||
}
|
||||
override IndexerAccess getAnAccess() { result.getTarget() = this }
|
||||
|
||||
/**
|
||||
* Gets the expression body of this indexer, if any. For example, the
|
||||
@@ -288,13 +263,9 @@ class Indexer extends DeclarationWithGetSetAccessors, Parameterizable, @indexer
|
||||
result = DeclarationWithGetSetAccessors.super.getExpressionBody()
|
||||
}
|
||||
|
||||
override Indexer getSourceDeclaration() {
|
||||
indexers(this, _, _, _, result)
|
||||
}
|
||||
override Indexer getSourceDeclaration() { indexers(this, _, _, _, result) }
|
||||
|
||||
override Indexer getOverridee() {
|
||||
result = DeclarationWithGetSetAccessors.super.getOverridee()
|
||||
}
|
||||
override Indexer getOverridee() { result = DeclarationWithGetSetAccessors.super.getOverridee() }
|
||||
|
||||
override Indexer getAnOverrider() {
|
||||
result = DeclarationWithGetSetAccessors.super.getAnOverrider()
|
||||
@@ -316,13 +287,9 @@ class Indexer extends DeclarationWithGetSetAccessors, Parameterizable, @indexer
|
||||
result = DeclarationWithGetSetAccessors.super.getAnUltimateImplementor()
|
||||
}
|
||||
|
||||
override Location getALocation() {
|
||||
indexer_location(this, result)
|
||||
}
|
||||
override Location getALocation() { indexer_location(this, result) }
|
||||
|
||||
override string toStringWithTypes() {
|
||||
result = getName() + "[" + parameterTypesToString() + "]"
|
||||
}
|
||||
override string toStringWithTypes() { result = getName() + "[" + parameterTypesToString() + "]" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -330,14 +297,10 @@ class Indexer extends DeclarationWithGetSetAccessors, Parameterizable, @indexer
|
||||
* accessor (`EventAccessor`).
|
||||
*/
|
||||
class Accessor extends Callable, Modifiable, Attributable, @callable_accessor {
|
||||
override ValueOrRefType getDeclaringType() {
|
||||
result = this.getDeclaration().getDeclaringType()
|
||||
}
|
||||
override ValueOrRefType getDeclaringType() { result = this.getDeclaration().getDeclaringType() }
|
||||
|
||||
/** Gets the assembly name of this accessor. */
|
||||
string getAssemblyName() {
|
||||
accessors(this, _, result, _, _)
|
||||
}
|
||||
string getAssemblyName() { accessors(this, _, result, _, _) }
|
||||
|
||||
/**
|
||||
* Gets the declaration that this accessor belongs to. For example, both
|
||||
@@ -352,9 +315,7 @@ class Accessor extends Callable, Modifiable, Attributable, @callable_accessor {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
DeclarationWithAccessors getDeclaration() {
|
||||
accessors(this, _ , _, result, _)
|
||||
}
|
||||
DeclarationWithAccessors getDeclaration() { accessors(this, _, _, result, _) }
|
||||
|
||||
/**
|
||||
* Gets an explicit access modifier of this accessor, if any. For example,
|
||||
@@ -370,9 +331,7 @@ class Accessor extends Callable, Modifiable, Attributable, @callable_accessor {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
AccessModifier getAnAccessModifier() {
|
||||
result = Modifiable.super.getAModifier()
|
||||
}
|
||||
AccessModifier getAnAccessModifier() { result = Modifiable.super.getAModifier() }
|
||||
|
||||
/**
|
||||
* Gets a modifier of this accessor, if any.
|
||||
@@ -398,17 +357,11 @@ class Accessor extends Callable, Modifiable, Attributable, @callable_accessor {
|
||||
not (result instanceof AccessModifier and exists(getAnAccessModifier()))
|
||||
}
|
||||
|
||||
override Accessor getSourceDeclaration() {
|
||||
accessors(this, _, _, _, result)
|
||||
}
|
||||
override Accessor getSourceDeclaration() { accessors(this, _, _, _, result) }
|
||||
|
||||
override Location getALocation() {
|
||||
accessor_location(this, result)
|
||||
}
|
||||
override Location getALocation() { accessor_location(this, result) }
|
||||
|
||||
override string toString() {
|
||||
result = getName()
|
||||
}
|
||||
override string toString() { result = getName() }
|
||||
|
||||
override Location getLocation() { result = Callable.super.getLocation() }
|
||||
}
|
||||
@@ -427,13 +380,9 @@ class Accessor extends Callable, Modifiable, Attributable, @callable_accessor {
|
||||
* ```
|
||||
*/
|
||||
class Getter extends Accessor, @getter {
|
||||
override string getName() {
|
||||
result = "get" + "_" + getDeclaration().getName()
|
||||
}
|
||||
override string getName() { result = "get" + "_" + getDeclaration().getName() }
|
||||
|
||||
override Type getReturnType() {
|
||||
result = getDeclaration().getType()
|
||||
}
|
||||
override Type getReturnType() { result = getDeclaration().getType() }
|
||||
|
||||
/**
|
||||
* Gets the field used in the trival implementation of this getter, if any.
|
||||
@@ -476,9 +425,7 @@ class Getter extends Accessor, @getter {
|
||||
* ```
|
||||
*/
|
||||
class Setter extends Accessor, @setter {
|
||||
override string getName() {
|
||||
result = "set" + "_" + getDeclaration().getName()
|
||||
}
|
||||
override string getName() { result = "set" + "_" + getDeclaration().getName() }
|
||||
|
||||
override Type getReturnType() {
|
||||
exists(this) and // needed to avoid compiler warning
|
||||
@@ -516,8 +463,7 @@ class Setter extends Accessor, @setter {
|
||||
/**
|
||||
* Gets an access to the special `value` parameter available in setters.
|
||||
*/
|
||||
private
|
||||
ParameterAccess accessToValue() {
|
||||
private ParameterAccess accessToValue() {
|
||||
result.getTarget().hasName("value") and
|
||||
result.getEnclosingCallable() instanceof Setter
|
||||
}
|
||||
@@ -547,8 +493,7 @@ ParameterAccess accessToValue() {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class TrivialProperty extends Property
|
||||
{
|
||||
class TrivialProperty extends Property {
|
||||
TrivialProperty() {
|
||||
isAutoImplemented()
|
||||
or
|
||||
@@ -560,9 +505,7 @@ class TrivialProperty extends Property
|
||||
* A `Property` which holds a type with an indexer.
|
||||
*/
|
||||
class IndexerProperty extends Property {
|
||||
IndexerProperty() {
|
||||
exists(getType().(RefType).getABaseType*().getAnIndexer())
|
||||
}
|
||||
IndexerProperty() { exists(getType().(RefType).getABaseType*().getAnIndexer()) }
|
||||
|
||||
/**
|
||||
* Gets a call to the indexer of the type returned by this property, for a value returned by this
|
||||
@@ -573,8 +516,7 @@ class IndexerProperty extends Property {
|
||||
IndexerCall getAnIndexerCall() {
|
||||
result = getType().(RefType).getAnIndexer().getAnAccessor().getACall() and
|
||||
// The qualifier of this indexer call should be a value returned from an access of this property
|
||||
exists(Expr qualifier |
|
||||
qualifier = result.(IndexerAccess).getQualifier() |
|
||||
exists(Expr qualifier | qualifier = result.(IndexerAccess).getQualifier() |
|
||||
DataFlow::localFlow(DataFlow::exprNode(this.getAnAccess()), DataFlow::exprNode(qualifier))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -25,11 +25,8 @@ private import semmle.code.csharp.frameworks.System
|
||||
* (`FixedStmt`).
|
||||
*/
|
||||
class Stmt extends ControlFlowElement, @stmt {
|
||||
|
||||
/** Gets the enclosing callable of this statement. */
|
||||
override Callable getEnclosingCallable() {
|
||||
enclosingCallable(this, result)
|
||||
}
|
||||
override Callable getEnclosingCallable() { enclosingCallable(this, result) }
|
||||
|
||||
override string toString() { result = "Statement" }
|
||||
|
||||
@@ -54,7 +51,6 @@ class Stmt extends ControlFlowElement, @stmt {
|
||||
* ```
|
||||
*/
|
||||
class BlockStmt extends Stmt, @block_stmt {
|
||||
|
||||
/** Gets a statement in this block, if any. */
|
||||
Stmt getAStmt() { result.getParent() = this }
|
||||
|
||||
@@ -74,10 +70,9 @@ class BlockStmt extends Stmt, @block_stmt {
|
||||
predicate isEmpty() { not exists(this.getAStmt()) }
|
||||
|
||||
override Stmt stripSingletonBlocks() {
|
||||
if getNumberOfStmts() = 1 then
|
||||
result = getAChildStmt().stripSingletonBlocks()
|
||||
else
|
||||
result = this
|
||||
if getNumberOfStmts() = 1
|
||||
then result = getAChildStmt().stripSingletonBlocks()
|
||||
else result = this
|
||||
}
|
||||
|
||||
override string toString() { result = "{...}" }
|
||||
@@ -97,7 +92,6 @@ class BlockStmt extends Stmt, @block_stmt {
|
||||
* ```
|
||||
*/
|
||||
class ExprStmt extends Stmt, @expr_stmt {
|
||||
|
||||
/** Gets the expression in this expression statement. */
|
||||
Expr getExpr() { result.getParent() = this }
|
||||
|
||||
@@ -110,7 +104,6 @@ class ExprStmt extends Stmt, @expr_stmt {
|
||||
* Either an `if` statement (`IfStmt`) or a `switch` statement (`SwitchStmt`).
|
||||
*/
|
||||
class SelectionStmt extends Stmt, @cond_stmt {
|
||||
|
||||
/** Gets the condition of this selection statement. */
|
||||
Expr getCondition() { none() }
|
||||
}
|
||||
@@ -129,7 +122,6 @@ class SelectionStmt extends Stmt, @cond_stmt {
|
||||
* The `else` part is optional.
|
||||
*/
|
||||
class IfStmt extends SelectionStmt, @if_stmt {
|
||||
|
||||
override Expr getCondition() { result = this.getChild(0) }
|
||||
|
||||
/** Gets the `then` (true) branch of this `if` statement. */
|
||||
@@ -151,7 +143,6 @@ class IfStmt extends SelectionStmt, @if_stmt {
|
||||
* ```
|
||||
*/
|
||||
class SwitchStmt extends SelectionStmt, @switch_stmt {
|
||||
|
||||
override Expr getCondition() { result = this.getChild(0) }
|
||||
|
||||
/**
|
||||
@@ -217,16 +208,14 @@ class SwitchStmt extends SelectionStmt, @switch_stmt {
|
||||
Stmt getAStmt() { result = this.getStmt(_) }
|
||||
}
|
||||
|
||||
private cached module SwithStmtInternal {
|
||||
cached
|
||||
private module SwithStmtInternal {
|
||||
cached
|
||||
CaseStmt getCase(SwitchStmt ss, int i) {
|
||||
exists(int index, int rankIndex |
|
||||
result = ss.getChildStmt(index) and
|
||||
rankIndex = i + 1 and
|
||||
index = rank[rankIndex](int j, CaseStmt cs |
|
||||
cs = ss.getChildStmt(j) |
|
||||
j
|
||||
)
|
||||
index = rank[rankIndex](int j, CaseStmt cs | cs = ss.getChildStmt(j) | j)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -236,12 +225,13 @@ private cached module SwithStmtInternal {
|
||||
result = ss.getChildStmt(index) and
|
||||
rankIndex = i + 1 and
|
||||
index = rank[rankIndex](int j, Stmt s |
|
||||
// `getChild` includes both labeled statements and the targeted
|
||||
// statements of labeled statement as separate children, but we
|
||||
// only want the labeled statement
|
||||
s = getLabeledStmt(ss, j) |
|
||||
j
|
||||
)
|
||||
// `getChild` includes both labeled statements and the targeted
|
||||
// statements of labeled statement as separate children, but we
|
||||
// only want the labeled statement
|
||||
s = getLabeledStmt(ss, j)
|
||||
|
|
||||
j
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -271,9 +261,7 @@ class CaseStmt extends Stmt, @case {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
Expr getCondition() {
|
||||
result = this.getChild(2)
|
||||
}
|
||||
Expr getCondition() { result = this.getChild(2) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -318,9 +306,7 @@ class ConstCase extends LabeledStmt, CaseStmt {
|
||||
* ```
|
||||
*/
|
||||
class TypeCase extends LabeledStmt, CaseStmt {
|
||||
TypeCase() {
|
||||
this.getChild(1) instanceof TypeAccess
|
||||
}
|
||||
TypeCase() { this.getChild(1) instanceof TypeAccess }
|
||||
|
||||
/**
|
||||
* Gets the local variable declaration of this type case, if any. For example,
|
||||
@@ -337,9 +323,7 @@ class TypeCase extends LabeledStmt, CaseStmt {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
LocalVariableDeclExpr getVariableDeclExpr() {
|
||||
result = getChild(0)
|
||||
}
|
||||
LocalVariableDeclExpr getVariableDeclExpr() { result = getChild(0) }
|
||||
|
||||
/**
|
||||
* Gets the type access of this case, for example access to `string` or
|
||||
@@ -354,9 +338,7 @@ class TypeCase extends LabeledStmt, CaseStmt {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
TypeAccess getTypeAccess() {
|
||||
result = getChild(1)
|
||||
}
|
||||
TypeAccess getTypeAccess() { result = getChild(1) }
|
||||
|
||||
/**
|
||||
* Gets the type being checked by this case. For example, the type being checked
|
||||
@@ -371,17 +353,14 @@ class TypeCase extends LabeledStmt, CaseStmt {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
Type getCheckedType() {
|
||||
result = getTypeAccess().getType()
|
||||
}
|
||||
Type getCheckedType() { result = getTypeAccess().getType() }
|
||||
|
||||
override string toString() {
|
||||
exists(string var |
|
||||
if exists(this.getVariableDeclExpr()) then
|
||||
var = " " + this.getVariableDeclExpr().getName()
|
||||
else
|
||||
var = ""
|
||||
|
|
||||
if exists(this.getVariableDeclExpr())
|
||||
then var = " " + this.getVariableDeclExpr().getName()
|
||||
else var = ""
|
||||
|
|
||||
result = "case " + this.getTypeAccess().getType().getName() + var + ":"
|
||||
)
|
||||
}
|
||||
@@ -399,7 +378,6 @@ class TypeCase extends LabeledStmt, CaseStmt {
|
||||
* ```
|
||||
*/
|
||||
class DefaultCase extends CaseStmt {
|
||||
|
||||
DefaultCase() { not exists(Expr e | e.getParent() = this) }
|
||||
|
||||
override string toString() { result = "default:" }
|
||||
@@ -413,7 +391,6 @@ class DefaultCase extends CaseStmt {
|
||||
* (`ForeachStmt`).
|
||||
*/
|
||||
class LoopStmt extends Stmt, @loop_stmt {
|
||||
|
||||
/** Gets the body of this loop statement. */
|
||||
Stmt getBody() { result.getParent() = this }
|
||||
|
||||
@@ -431,7 +408,6 @@ class LoopStmt extends Stmt, @loop_stmt {
|
||||
* ```
|
||||
*/
|
||||
class WhileStmt extends LoopStmt, @while_stmt {
|
||||
|
||||
override Expr getCondition() { result.getParent() = this }
|
||||
|
||||
override string toString() { result = "while (...) ..." }
|
||||
@@ -448,7 +424,6 @@ class WhileStmt extends LoopStmt, @while_stmt {
|
||||
* ```
|
||||
*/
|
||||
class DoStmt extends LoopStmt, @do_stmt {
|
||||
|
||||
override Expr getCondition() { result.getParent() = this }
|
||||
|
||||
override string toString() { result = "do ... while (...);" }
|
||||
@@ -464,7 +439,6 @@ class DoStmt extends LoopStmt, @do_stmt {
|
||||
* ```
|
||||
*/
|
||||
class ForStmt extends LoopStmt, @for_stmt {
|
||||
|
||||
/**
|
||||
* Gets an initializer expression of this `for` loop, if any.
|
||||
*
|
||||
@@ -490,7 +464,9 @@ class ForStmt extends LoopStmt, @for_stmt {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
Expr getInitializer(int n) { exists(int i | result = this.getChild(i) and n = -1-i and i <= -1) }
|
||||
Expr getInitializer(int n) {
|
||||
exists(int i | result = this.getChild(i) and n = -1 - i and i <= -1)
|
||||
}
|
||||
|
||||
override Expr getCondition() { result = this.getChild(0) }
|
||||
|
||||
@@ -518,7 +494,7 @@ class ForStmt extends LoopStmt, @for_stmt {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
Expr getUpdate(int n) { exists(int i | result = this.getChild(i) and n = i-1 and i >= 1) }
|
||||
Expr getUpdate(int n) { exists(int i | result = this.getChild(i) and n = i - 1 and i >= 1) }
|
||||
|
||||
override string toString() { result = "for (...;...;...) ..." }
|
||||
}
|
||||
@@ -532,8 +508,7 @@ class ForStmt extends LoopStmt, @for_stmt {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class ForeachStmt extends LoopStmt, @foreach_stmt {
|
||||
|
||||
class ForeachStmt extends LoopStmt, @foreach_stmt {
|
||||
/**
|
||||
* Gets the local variable of this `foreach` loop, if any.
|
||||
*
|
||||
@@ -572,8 +547,9 @@ class ForeachStmt extends LoopStmt, @foreach_stmt {
|
||||
* ```
|
||||
*/
|
||||
LocalVariableDeclExpr getVariableDeclExpr(int i) {
|
||||
result = getVariableDeclTuple().getArgument(i) or
|
||||
i=0 and result = this.getChild(0)
|
||||
result = getVariableDeclTuple().getArgument(i)
|
||||
or
|
||||
i = 0 and result = this.getChild(0)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -665,9 +641,7 @@ class JumpStmt extends Stmt, @jump_stmt { }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class BreakStmt extends JumpStmt, @break_stmt {
|
||||
override string toString() { result = "break;" }
|
||||
}
|
||||
class BreakStmt extends JumpStmt, @break_stmt { override string toString() { result = "break;" } }
|
||||
|
||||
/**
|
||||
* A `continue` statement, for example line 4 in
|
||||
@@ -690,7 +664,6 @@ class ContinueStmt extends JumpStmt, @continue_stmt {
|
||||
*
|
||||
* Either a `goto` label (`GotoLabelStmt`), a `goto case` (`GotoCaseStmt`), or
|
||||
* a `goto default` (`GotoDefaultStmt`).
|
||||
*
|
||||
*/
|
||||
class GotoStmt extends JumpStmt, @goto_any_stmt { }
|
||||
|
||||
@@ -707,7 +680,6 @@ class GotoStmt extends JumpStmt, @goto_any_stmt { }
|
||||
* ```
|
||||
*/
|
||||
class GotoLabelStmt extends GotoStmt, @goto_stmt {
|
||||
|
||||
/** Gets the label that this `goto` statement jumps to. */
|
||||
string getLabel() { exprorstmt_name(this, result) }
|
||||
|
||||
@@ -715,8 +687,7 @@ class GotoLabelStmt extends GotoStmt, @goto_stmt {
|
||||
|
||||
/** Gets the target statement that this `goto` statement jumps to. */
|
||||
LabeledStmt getTarget() {
|
||||
result.getEnclosingCallable() = getEnclosingCallable()
|
||||
and
|
||||
result.getEnclosingCallable() = getEnclosingCallable() and
|
||||
result.getLabel() = getLabel()
|
||||
}
|
||||
}
|
||||
@@ -738,7 +709,6 @@ class GotoLabelStmt extends GotoStmt, @goto_stmt {
|
||||
* ```
|
||||
*/
|
||||
class GotoCaseStmt extends GotoStmt, @goto_case_stmt {
|
||||
|
||||
/** Gets the constant expression that this `goto case` statement jumps to. */
|
||||
Expr getExpr() { result = this.getChild(0) }
|
||||
|
||||
@@ -803,9 +773,7 @@ class ThrowStmt extends JumpStmt, ThrowElement, @throw_stmt {
|
||||
* and may be thrown as an exception.
|
||||
*/
|
||||
class ExceptionClass extends Class {
|
||||
ExceptionClass() {
|
||||
getBaseClass*() instanceof SystemExceptionClass
|
||||
}
|
||||
ExceptionClass() { getBaseClass*() instanceof SystemExceptionClass }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -818,7 +786,6 @@ class ExceptionClass extends Class {
|
||||
* ```
|
||||
*/
|
||||
class ReturnStmt extends JumpStmt, @return_stmt {
|
||||
|
||||
/** Gets the expression being returned, if any. */
|
||||
Expr getExpr() { result.getParent() = this }
|
||||
|
||||
@@ -832,7 +799,6 @@ class ReturnStmt extends JumpStmt, @return_stmt {
|
||||
* `yield break` statement (`YieldBreakStmt`).
|
||||
*/
|
||||
class YieldStmt extends JumpStmt, @yield_stmt {
|
||||
|
||||
/** Gets the expression being yielded, if any. */
|
||||
Expr getExpr() { result.getParent() = this }
|
||||
}
|
||||
@@ -893,7 +859,6 @@ class YieldReturnStmt extends YieldStmt {
|
||||
* ```
|
||||
*/
|
||||
class TryStmt extends Stmt, @try_stmt {
|
||||
|
||||
/** Gets the block of this `try` statement. */
|
||||
BlockStmt getBlock() { result = this.getChild(0) }
|
||||
|
||||
@@ -901,7 +866,7 @@ class TryStmt extends Stmt, @try_stmt {
|
||||
CatchClause getACatchClause() { result = this.getAChild() }
|
||||
|
||||
/** Gets the `n`th catch clause of this `try` statement. */
|
||||
CatchClause getCatchClause(int n) { exists(int i | result = this.getChild(i) and n = i-1) }
|
||||
CatchClause getCatchClause(int n) { exists(int i | result = this.getChild(i) and n = i - 1) }
|
||||
|
||||
/** Gets the `finally` block of this `try` statement, if any. */
|
||||
BlockStmt getFinally() { result = this.getChild(-1) }
|
||||
@@ -912,18 +877,14 @@ class TryStmt extends Stmt, @try_stmt {
|
||||
override string toString() { result = "try {...} ..." }
|
||||
|
||||
/** Gets the `catch` clause that handles an exception of type `ex`, if any. */
|
||||
CatchClause getAnExceptionHandler(ExceptionClass ex) {
|
||||
result = clauseHandlesException(ex, 0)
|
||||
}
|
||||
CatchClause getAnExceptionHandler(ExceptionClass ex) { result = clauseHandlesException(ex, 0) }
|
||||
|
||||
/**
|
||||
* Holds if catch clause `cc` definitely handles exceptions of type `ex`.
|
||||
*/
|
||||
predicate definitelyHandles(ExceptionClass ex, CatchClause cc) {
|
||||
cc=getACatchClause()
|
||||
and
|
||||
not exists(cc.getFilterClause())
|
||||
and
|
||||
cc = getACatchClause() and
|
||||
not exists(cc.getFilterClause()) and
|
||||
(
|
||||
cc.getCaughtExceptionType() = ex.getBaseClass*()
|
||||
or
|
||||
@@ -932,26 +893,23 @@ class TryStmt extends Stmt, @try_stmt {
|
||||
}
|
||||
|
||||
private predicate maybeHandles(ExceptionClass ex, CatchClause cc) {
|
||||
cc=getACatchClause()
|
||||
and
|
||||
cc = getACatchClause() and
|
||||
cc.getCaughtExceptionType().getBaseClass*() = ex
|
||||
}
|
||||
|
||||
private CatchClause clauseHandlesException(ExceptionClass ex, int n) {
|
||||
exists(CatchClause clause |
|
||||
clause = getCatchClause(n) |
|
||||
if
|
||||
definitelyHandles(ex, clause)
|
||||
then
|
||||
result = clause
|
||||
else if
|
||||
maybeHandles(ex, clause)
|
||||
then
|
||||
result = clause or
|
||||
result = clauseHandlesException(ex, n+1)
|
||||
else // Does not handle
|
||||
result = clauseHandlesException(ex, n+1)
|
||||
)
|
||||
exists(CatchClause clause | clause = getCatchClause(n) |
|
||||
if definitelyHandles(ex, clause)
|
||||
then result = clause
|
||||
else
|
||||
if maybeHandles(ex, clause)
|
||||
then
|
||||
result = clause or
|
||||
result = clauseHandlesException(ex, n + 1)
|
||||
else
|
||||
// Does not handle
|
||||
result = clauseHandlesException(ex, n + 1)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1003,7 +961,7 @@ class CatchClause extends Stmt, @catch {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
ExceptionClass getCaughtExceptionType() { catch_type(this,getTypeRef(result),_) }
|
||||
ExceptionClass getCaughtExceptionType() { catch_type(this, getTypeRef(result), _) }
|
||||
|
||||
/**
|
||||
* Gets the `catch` filter clause, if any. For example, the filter expression
|
||||
@@ -1018,7 +976,7 @@ class CatchClause extends Stmt, @catch {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
Expr getFilterClause() { result=getChild(2) }
|
||||
Expr getFilterClause() { result = getChild(2) }
|
||||
|
||||
/** Holds if this `catch` clause has a filter. */
|
||||
predicate hasFilterClause() { exists(getFilterClause()) }
|
||||
@@ -1050,8 +1008,7 @@ class CatchClause extends Stmt, @catch {
|
||||
* The exception variable (`ex`) is optional.
|
||||
*/
|
||||
class SpecificCatchClause extends CatchClause {
|
||||
|
||||
SpecificCatchClause() { catch_type(this,_,1) }
|
||||
SpecificCatchClause() { catch_type(this, _, 1) }
|
||||
|
||||
/** Gets the local variable of this catch clause, if any. */
|
||||
LocalVariable getVariable() { result = this.getVariableDeclExpr().getVariable() }
|
||||
@@ -1077,11 +1034,9 @@ class SpecificCatchClause extends CatchClause {
|
||||
* ```
|
||||
*/
|
||||
class GeneralCatchClause extends CatchClause {
|
||||
|
||||
GeneralCatchClause() { catch_type(this,_,2) }
|
||||
GeneralCatchClause() { catch_type(this, _, 2) }
|
||||
|
||||
override string toString() { result = "catch {...}" }
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1095,7 +1050,6 @@ class GeneralCatchClause extends CatchClause {
|
||||
* ```
|
||||
*/
|
||||
class CheckedStmt extends Stmt, @checked_stmt {
|
||||
|
||||
/** Gets the block of this `checked` statement. */
|
||||
BlockStmt getBlock() { result.getParent() = this }
|
||||
|
||||
@@ -1113,7 +1067,6 @@ class CheckedStmt extends Stmt, @checked_stmt {
|
||||
* ```
|
||||
*/
|
||||
class UncheckedStmt extends Stmt, @unchecked_stmt {
|
||||
|
||||
/** Gets the block of this unchecked statement. */
|
||||
BlockStmt getBlock() { result.getParent() = this }
|
||||
|
||||
@@ -1130,7 +1083,6 @@ class UncheckedStmt extends Stmt, @unchecked_stmt {
|
||||
* ```
|
||||
*/
|
||||
class LockStmt extends Stmt, @lock_stmt {
|
||||
|
||||
/** Gets the expression being locked. */
|
||||
Expr getExpr() { result.getParent() = this }
|
||||
|
||||
@@ -1140,31 +1092,22 @@ class LockStmt extends Stmt, @lock_stmt {
|
||||
override string toString() { result = "lock (...) {...}" }
|
||||
|
||||
/** Gets the variable being locked, if any. */
|
||||
Variable getLockVariable()
|
||||
{
|
||||
result.getAnAccess() = getExpr()
|
||||
}
|
||||
Variable getLockVariable() { result.getAnAccess() = getExpr() }
|
||||
|
||||
/** Gets a statement in the scope of this `lock` statement. */
|
||||
Stmt getALockedStmt()
|
||||
{
|
||||
Stmt getALockedStmt() {
|
||||
// Do this instead of getParent+, because we don't want to escape
|
||||
// delegates and lambdas
|
||||
result.getParent() = this
|
||||
or exists( Stmt mid | mid=getALockedStmt() and result.getParent()=mid )
|
||||
or
|
||||
exists(Stmt mid | mid = getALockedStmt() and result.getParent() = mid)
|
||||
}
|
||||
|
||||
/** Holds if this statement is of the form `lock(this) { ... }`. */
|
||||
predicate isLockThis()
|
||||
{
|
||||
getExpr() instanceof ThisAccess
|
||||
}
|
||||
predicate isLockThis() { getExpr() instanceof ThisAccess }
|
||||
|
||||
/** Gets the type `T` if this statement is of the form `lock(typeof(T)) { ... }`. */
|
||||
Type getLockTypeObject()
|
||||
{
|
||||
result = ((TypeofExpr)getExpr()).getTypeAccess().getTarget()
|
||||
}
|
||||
Type getLockTypeObject() { result = getExpr().(TypeofExpr).getTypeAccess().getTarget() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1177,7 +1120,6 @@ class LockStmt extends Stmt, @lock_stmt {
|
||||
* ```
|
||||
*/
|
||||
class UsingStmt extends Stmt, @using_stmt {
|
||||
|
||||
/** Gets the `i`th local variable of this `using` statement. */
|
||||
LocalVariable getVariable(int i) { result = this.getVariableDeclExpr(i).getVariable() }
|
||||
|
||||
@@ -1201,9 +1143,7 @@ class UsingStmt extends Stmt, @using_stmt {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
Expr getExpr() {
|
||||
result = this.getChild(0)
|
||||
}
|
||||
Expr getExpr() { result = this.getChild(0) }
|
||||
|
||||
/**
|
||||
* Gets an expression that is used in this `using` statement. Either an
|
||||
@@ -1247,7 +1187,6 @@ class UsingStmt extends Stmt, @using_stmt {
|
||||
* ```
|
||||
*/
|
||||
class LocalVariableDeclStmt extends Stmt, @decl_stmt {
|
||||
|
||||
/**
|
||||
* Gets a local variable declaration, for example `x = null` and
|
||||
* `y = ""` in
|
||||
@@ -1273,7 +1212,6 @@ class LocalVariableDeclStmt extends Stmt, @decl_stmt {
|
||||
LocalVariableDeclExpr getVariableDeclExpr(int n) { result = this.getChild(n) }
|
||||
|
||||
override string toString() { result = "... ...;" }
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1286,7 +1224,6 @@ class LocalVariableDeclStmt extends Stmt, @decl_stmt {
|
||||
* ```
|
||||
*/
|
||||
class LocalConstantDeclStmt extends LocalVariableDeclStmt, @const_decl_stmt {
|
||||
|
||||
/**
|
||||
* Gets a local constant declaration, for example `x = 1` and `y = 2` in
|
||||
*
|
||||
@@ -1322,10 +1259,7 @@ class LocalConstantDeclStmt extends LocalVariableDeclStmt, @const_decl_stmt {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class EmptyStmt extends Stmt, @empty_stmt {
|
||||
|
||||
override string toString() { result = ";" }
|
||||
}
|
||||
class EmptyStmt extends Stmt, @empty_stmt { override string toString() { result = ";" } }
|
||||
|
||||
/**
|
||||
* An `unsafe` statement, for example
|
||||
@@ -1340,7 +1274,6 @@ class EmptyStmt extends Stmt, @empty_stmt {
|
||||
* ```
|
||||
*/
|
||||
class UnsafeStmt extends Stmt, @unsafe_stmt {
|
||||
|
||||
/** Gets the block of this unsafe statement. */
|
||||
BlockStmt getBlock() { result.getParent() = this }
|
||||
|
||||
@@ -1360,7 +1293,6 @@ class UnsafeStmt extends Stmt, @unsafe_stmt {
|
||||
* ```
|
||||
*/
|
||||
class FixedStmt extends Stmt, @fixed_stmt {
|
||||
|
||||
/** Gets the `i`th local variable of this `fixed` statement. */
|
||||
LocalVariable getVariable(int i) { result = this.getVariableDeclExpr(i).getVariable() }
|
||||
|
||||
@@ -1392,8 +1324,7 @@ class FixedStmt extends Stmt, @fixed_stmt {
|
||||
* exit: ...
|
||||
* ```
|
||||
*/
|
||||
class LabelStmt extends LabeledStmt, @label_stmt {
|
||||
}
|
||||
class LabelStmt extends LabeledStmt, @label_stmt { }
|
||||
|
||||
/**
|
||||
* A labeled statement.
|
||||
@@ -1401,7 +1332,6 @@ class LabelStmt extends LabeledStmt, @label_stmt {
|
||||
* Either a `case` statement (`ConstCase`) or a label statement (`LabelStmt`).
|
||||
*/
|
||||
class LabeledStmt extends Stmt, @labeled_stmt {
|
||||
|
||||
/**
|
||||
* Gets the next statement after this labeled statement.
|
||||
*
|
||||
@@ -1413,8 +1343,10 @@ class LabeledStmt extends Stmt, @labeled_stmt {
|
||||
* ```
|
||||
*/
|
||||
Stmt getStmt() {
|
||||
exists(int i | this = this.getParent().getChild(i)
|
||||
and result = this.getParent().getChild(i+1))
|
||||
exists(int i |
|
||||
this = this.getParent().getChild(i) and
|
||||
result = this.getParent().getChild(i + 1)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the label of this statement. */
|
||||
@@ -1438,11 +1370,8 @@ class LabeledStmt extends Stmt, @labeled_stmt {
|
||||
* ```
|
||||
*/
|
||||
class LocalFunctionStmt extends Stmt, @local_function_stmt {
|
||||
|
||||
/** Gets the local function defined by this statement. */
|
||||
LocalFunction getLocalFunction() {
|
||||
local_function_stmts(this, result)
|
||||
}
|
||||
LocalFunction getLocalFunction() { local_function_stmts(this, result) }
|
||||
|
||||
override string toString() { result = getLocalFunction().getName() + "(...)" }
|
||||
}
|
||||
|
||||
@@ -18,19 +18,14 @@ private import dotnet
|
||||
* type (`UnknownType`), or a type parameter (`TypeParameter`).
|
||||
*/
|
||||
class Type extends DotNet::Type, Member, TypeContainer, @type {
|
||||
|
||||
override string getName() { types(this,_,result) }
|
||||
override string getName() { types(this, _, result) }
|
||||
|
||||
override Type getSourceDeclaration() { result = this }
|
||||
|
||||
/** Holds if this type is implicitly convertible to `that` type. */
|
||||
predicate isImplicitlyConvertibleTo(Type that) {
|
||||
implicitConversion(this, that)
|
||||
}
|
||||
predicate isImplicitlyConvertibleTo(Type that) { implicitConversion(this, that) }
|
||||
|
||||
override Location getALocation() {
|
||||
type_location(this, result)
|
||||
}
|
||||
override Location getALocation() { type_location(this, result) }
|
||||
|
||||
override Type getChild(int n) { none() }
|
||||
|
||||
@@ -50,7 +45,6 @@ class Type extends DotNet::Type, Member, TypeContainer, @type {
|
||||
* Either a value type (`ValueType`) or a reference type (`RefType`).
|
||||
*/
|
||||
class ValueOrRefType extends DotNet::ValueOrRefType, Type, Attributable, @value_or_ref_type {
|
||||
|
||||
/**
|
||||
* Holds if this type has the qualified name `qualifier`.`name`.
|
||||
*
|
||||
@@ -59,37 +53,35 @@ class ValueOrRefType extends DotNet::ValueOrRefType, Type, Attributable, @value_
|
||||
*/
|
||||
override predicate hasQualifiedName(string qualifier, string name) {
|
||||
name = this.getName() and
|
||||
if exists(this.getDeclaringType()) then
|
||||
qualifier = this.getDeclaringType().getQualifiedName()
|
||||
else
|
||||
qualifier = this.getNamespace().getQualifiedName()
|
||||
if exists(this.getDeclaringType())
|
||||
then qualifier = this.getDeclaringType().getQualifiedName()
|
||||
else qualifier = this.getNamespace().getQualifiedName()
|
||||
}
|
||||
|
||||
/** Gets the namespace containing this type. */
|
||||
Namespace getNamespace() {
|
||||
if exists(this.getDeclaringType()) then
|
||||
result = this.getDeclaringType().getNamespace()
|
||||
else
|
||||
result.getATypeDeclaration() = this
|
||||
if exists(this.getDeclaringType())
|
||||
then result = this.getDeclaringType().getNamespace()
|
||||
else result.getATypeDeclaration() = this
|
||||
}
|
||||
|
||||
override Namespace getDeclaringNamespace() {
|
||||
this = result.getATypeDeclaration()
|
||||
}
|
||||
override Namespace getDeclaringNamespace() { this = result.getATypeDeclaration() }
|
||||
|
||||
override ValueOrRefType getDeclaringType() { none() }
|
||||
|
||||
override string getUndecoratedName() {
|
||||
if
|
||||
this.getName().indexOf("<")>0
|
||||
if this.getName().indexOf("<") > 0
|
||||
then
|
||||
exists(string name, int p | name = this.getName() and p = min(int p2 | p2 = name.indexOf("<") and p2>0) | result=name.substring(0, p))
|
||||
else
|
||||
result = this.getName()
|
||||
exists(string name, int p |
|
||||
name = this.getName() and p = min(int p2 | p2 = name.indexOf("<") and p2 > 0)
|
||||
|
|
||||
result = name.substring(0, p)
|
||||
)
|
||||
else result = this.getName()
|
||||
}
|
||||
|
||||
/** Gets a nested child type, if any. */
|
||||
NestedType getAChildType() { nested_types(result,this,_) }
|
||||
NestedType getAChildType() { nested_types(result, this, _) }
|
||||
|
||||
/**
|
||||
* Gets the source namespace declaration in which this type is declared, if any.
|
||||
@@ -108,7 +100,9 @@ class ValueOrRefType extends DotNet::ValueOrRefType, Type, Attributable, @value_
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
NamespaceDeclaration getParentNamespaceDeclaration() { parent_namespace_declaration(this,result) }
|
||||
NamespaceDeclaration getParentNamespaceDeclaration() {
|
||||
parent_namespace_declaration(this, result)
|
||||
}
|
||||
|
||||
/** Gets the immediate base class of this class, if any. */
|
||||
Class getBaseClass() { extend(this, getTypeRef(result)) }
|
||||
@@ -222,8 +216,7 @@ class ValueOrRefType extends DotNet::ValueOrRefType, Type, Attributable, @value_
|
||||
}
|
||||
|
||||
private predicate hasNonOverriddenMember(Member m) {
|
||||
isNonOverridden(m)
|
||||
and
|
||||
isNonOverridden(m) and
|
||||
(
|
||||
m = getAMember()
|
||||
or
|
||||
@@ -241,7 +234,8 @@ class ValueOrRefType extends DotNet::ValueOrRefType, Type, Attributable, @value_
|
||||
not memberOverrides(v)
|
||||
}
|
||||
|
||||
pragma [noinline] // Predicate folding for proper join-order
|
||||
// Predicate folding for proper join-order
|
||||
pragma[noinline]
|
||||
private predicate memberOverrides(Virtualizable v) {
|
||||
getAMember().(Virtualizable).getOverridee() = v
|
||||
}
|
||||
@@ -280,25 +274,21 @@ class ValueOrRefType extends DotNet::ValueOrRefType, Type, Attributable, @value_
|
||||
StaticConstructor getStaticConstructor() { result = getAMember() }
|
||||
|
||||
/** Gets a nested type of this type, if any. */
|
||||
NestedType getANestedType() { result = getAMember() }
|
||||
NestedType getANestedType() { result = getAMember() }
|
||||
|
||||
/** Gets the number of types that directly depend on this type. */
|
||||
int getAfferentCoupling() {
|
||||
afferentCoupling(this, result)
|
||||
}
|
||||
int getAfferentCoupling() { afferentCoupling(this, result) }
|
||||
|
||||
/** Gets the number of types that this type directly depends upon. */
|
||||
int getEfferentCoupling() {
|
||||
efferentCoupling(this, result)
|
||||
}
|
||||
int getEfferentCoupling() { efferentCoupling(this, result) }
|
||||
|
||||
/** Gets the length of *some* path to the root of the hierarchy. */
|
||||
int getADepth() {
|
||||
(this.hasQualifiedName("System", "Object") and result = 0)
|
||||
this.hasQualifiedName("System", "Object") and result = 0
|
||||
or
|
||||
(result = this.getABaseType().getADepth() + 1
|
||||
//prevent recursion on cyclic inheritance (only for incorrect databases)
|
||||
and not (this = this.getABaseType+()))
|
||||
result = this.getABaseType().getADepth() + 1 and
|
||||
//prevent recursion on cyclic inheritance (only for incorrect databases)
|
||||
not this = this.getABaseType+()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -306,14 +296,10 @@ class ValueOrRefType extends DotNet::ValueOrRefType, Type, Attributable, @value_
|
||||
* `object` in the type hierarchy. Types that are very deeply nested
|
||||
* may be difficult to maintain.
|
||||
*/
|
||||
int getInheritanceDepth() {
|
||||
result = max(this.getADepth())
|
||||
}
|
||||
int getInheritanceDepth() { result = max(this.getADepth()) }
|
||||
|
||||
/** Gets the number of (direct or indirect) base types. */
|
||||
int getNumberOfAncestors() {
|
||||
result = count(this.getABaseType+())
|
||||
}
|
||||
int getNumberOfAncestors() { result = count(this.getABaseType+()) }
|
||||
|
||||
/**
|
||||
* Gets the Henderson-Sellers lack of cohesion metric.
|
||||
@@ -325,9 +311,7 @@ class ValueOrRefType extends DotNet::ValueOrRefType, Type, Attributable, @value_
|
||||
* Types with a high Henderson-Sellers metric have methods
|
||||
* which access many fields, and can be a maintainability problem.
|
||||
*/
|
||||
float getLackOfCohesionHS() {
|
||||
lackOfCohesionHS(this, result)
|
||||
}
|
||||
float getLackOfCohesionHS() { lackOfCohesionHS(this, result) }
|
||||
|
||||
/**
|
||||
* Gets the Chidamber and Kemerer lack of cohesion metric.
|
||||
@@ -340,9 +324,7 @@ class ValueOrRefType extends DotNet::ValueOrRefType, Type, Attributable, @value_
|
||||
* Types with a high Chidamber and Kemerer metric have multiple
|
||||
* responsibilities so could be refactored.
|
||||
*/
|
||||
float getLackOfCohesionCK() {
|
||||
lackOfCohesionCK(this, result)
|
||||
}
|
||||
float getLackOfCohesionCK() { lackOfCohesionCK(this, result) }
|
||||
|
||||
/**
|
||||
* Gets the *response* of this type, which is defined as the total number
|
||||
@@ -351,18 +333,17 @@ class ValueOrRefType extends DotNet::ValueOrRefType, Type, Attributable, @value_
|
||||
*/
|
||||
int getResponse() {
|
||||
result = sum(Callable c |
|
||||
c.getDeclaringType() = this |
|
||||
count(Call call |
|
||||
call.getEnclosingCallable() = c and
|
||||
not call instanceof MemberAccess // property, indexer, and event accesses are not counted
|
||||
c.getDeclaringType() = this
|
||||
|
|
||||
count(Call call |
|
||||
call.getEnclosingCallable() = c and
|
||||
not call instanceof MemberAccess // property, indexer, and event accesses are not counted
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the number of callables in this type. */
|
||||
int getNumberOfCallables() {
|
||||
result = count(Callable c | this.getAMember() = c)
|
||||
}
|
||||
int getNumberOfCallables() { result = count(Callable c | this.getAMember() = c) }
|
||||
|
||||
override ValueOrRefType getSourceDeclaration() {
|
||||
result = this and
|
||||
@@ -371,7 +352,7 @@ class ValueOrRefType extends DotNet::ValueOrRefType, Type, Attributable, @value_
|
||||
// We must use `nested_types` here, rather than overriding `getSourceDeclaration`
|
||||
// in the class `NestedType` below. Otherwise, the overrides in `UnboundGenericType`
|
||||
// and its subclasses will not work
|
||||
nested_types(this,_,result)
|
||||
nested_types(this, _, result)
|
||||
}
|
||||
|
||||
override string toString() { result = Type.super.toString() }
|
||||
@@ -391,18 +372,16 @@ class ValueOrRefType extends DotNet::ValueOrRefType, Type, Attributable, @value_
|
||||
* ```
|
||||
*/
|
||||
class NestedType extends ValueOrRefType {
|
||||
NestedType() { nested_types(this, _, _) }
|
||||
|
||||
NestedType() { nested_types(this,_,_) }
|
||||
|
||||
override ValueOrRefType getDeclaringType() { nested_types(this,result,_) }
|
||||
override ValueOrRefType getDeclaringType() { nested_types(this, result, _) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A non-nested type, that is declared directly in a namespace.
|
||||
*/
|
||||
class NonNestedType extends ValueOrRefType {
|
||||
|
||||
NonNestedType() { exists(Namespace n | parent_namespace(this,n)) }
|
||||
NonNestedType() { exists(Namespace n | parent_namespace(this, n)) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -437,7 +416,6 @@ class ValueType extends ValueOrRefType, @value_type { }
|
||||
* (`FloatingPointType`), or a decimal type (`DecimalType`).
|
||||
*/
|
||||
class SimpleType extends ValueType, @simple_type {
|
||||
|
||||
/** Gets the minimum integral value of this type, if any. */
|
||||
int minValue() { none() }
|
||||
|
||||
@@ -451,7 +429,6 @@ class SimpleType extends ValueType, @simple_type {
|
||||
* The Boolean type, `bool`.
|
||||
*/
|
||||
class BoolType extends SimpleType, @bool_type {
|
||||
|
||||
override string toStringWithTypes() { result = "bool" }
|
||||
}
|
||||
|
||||
@@ -459,12 +436,11 @@ class BoolType extends SimpleType, @bool_type {
|
||||
* The Unicode character type, `char`.
|
||||
*/
|
||||
class CharType extends SimpleType, @char_type {
|
||||
|
||||
override string toStringWithTypes() { result = "char" }
|
||||
|
||||
override int minValue() { result=0 }
|
||||
override int minValue() { result = 0 }
|
||||
|
||||
override int maxValue() { result=65535 }
|
||||
override int maxValue() { result = 65535 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -473,8 +449,7 @@ class CharType extends SimpleType, @char_type {
|
||||
* Either a signed integral type (`SignedIntegralType`) or an unsigned integral type
|
||||
* (`UnsignedIntegralType`).
|
||||
*/
|
||||
class IntegralType extends SimpleType, @integral_type {
|
||||
}
|
||||
class IntegralType extends SimpleType, @integral_type { }
|
||||
|
||||
/**
|
||||
* An unsigned integral type.
|
||||
@@ -483,8 +458,7 @@ class IntegralType extends SimpleType, @integral_type {
|
||||
* or a `ulong` (`ULongType`).
|
||||
*/
|
||||
class UnsignedIntegralType extends IntegralType, @unsigned_integral_type {
|
||||
|
||||
override int minValue() { result=0 }
|
||||
override int minValue() { result = 0 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -493,50 +467,45 @@ class UnsignedIntegralType extends IntegralType, @unsigned_integral_type {
|
||||
* Either an `sbyte` (`SByteType`), a `short` (`ShortType`), an `int`
|
||||
* (`IntType`), or a `long` (`LongType`).
|
||||
*/
|
||||
class SignedIntegralType extends IntegralType, @signed_integral_type {
|
||||
}
|
||||
class SignedIntegralType extends IntegralType, @signed_integral_type { }
|
||||
|
||||
/**
|
||||
* The signed byte type, `sbyte`.
|
||||
*/
|
||||
class SByteType extends SignedIntegralType, @sbyte_type {
|
||||
|
||||
override string toStringWithTypes() { result = "sbyte" }
|
||||
|
||||
override int minValue() { result=-128 }
|
||||
override int minValue() { result = -128 }
|
||||
|
||||
override int maxValue() { result=127 }
|
||||
override int maxValue() { result = 127 }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `short` type.
|
||||
*/
|
||||
class ShortType extends SignedIntegralType, @short_type {
|
||||
|
||||
override string toStringWithTypes() { result = "short" }
|
||||
|
||||
override int minValue() { result=-32768 }
|
||||
override int minValue() { result = -32768 }
|
||||
|
||||
override int maxValue() { result=32767 }
|
||||
override int maxValue() { result = 32767 }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `int` type.
|
||||
*/
|
||||
class IntType extends SignedIntegralType, @int_type {
|
||||
|
||||
override string toStringWithTypes() { result = "int" }
|
||||
|
||||
override int minValue() { result=-2147483647-1 }
|
||||
override int minValue() { result = -2147483647 - 1 }
|
||||
|
||||
override int maxValue() { result=2147483647 }
|
||||
override int maxValue() { result = 2147483647 }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `long` type.
|
||||
*/
|
||||
class LongType extends SignedIntegralType, @long_type {
|
||||
|
||||
override string toStringWithTypes() { result = "long" }
|
||||
}
|
||||
|
||||
@@ -545,7 +514,8 @@ class LongType extends SignedIntegralType, @long_type {
|
||||
*/
|
||||
class ByteType extends UnsignedIntegralType, @byte_type {
|
||||
override string toStringWithTypes() { result = "byte" }
|
||||
override int maxValue() { result=255 }
|
||||
|
||||
override int maxValue() { result = 255 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -553,14 +523,14 @@ class ByteType extends UnsignedIntegralType, @byte_type {
|
||||
*/
|
||||
class UShortType extends UnsignedIntegralType, @ushort_type {
|
||||
override string toStringWithTypes() { result = "ushort" }
|
||||
override int maxValue() { result=65535 }
|
||||
|
||||
override int maxValue() { result = 65535 }
|
||||
}
|
||||
|
||||
/**
|
||||
* The unsigned int type, `uint`.
|
||||
*/
|
||||
class UIntType extends UnsignedIntegralType, @uint_type {
|
||||
|
||||
override string toStringWithTypes() { result = "uint" }
|
||||
}
|
||||
|
||||
@@ -568,7 +538,6 @@ class UIntType extends UnsignedIntegralType, @uint_type {
|
||||
* The unsigned long type, `ulong`.
|
||||
*/
|
||||
class ULongType extends UnsignedIntegralType, @ulong_type {
|
||||
|
||||
override string toStringWithTypes() { result = "ulong" }
|
||||
}
|
||||
|
||||
@@ -577,14 +546,12 @@ class ULongType extends UnsignedIntegralType, @ulong_type {
|
||||
*
|
||||
* Either a `float` (`FloatType`) or a `double` (`DoubleType`).
|
||||
*/
|
||||
class FloatingPointType extends SimpleType, @floating_point_type {
|
||||
}
|
||||
class FloatingPointType extends SimpleType, @floating_point_type { }
|
||||
|
||||
/**
|
||||
* The `float` type.
|
||||
*/
|
||||
class FloatType extends FloatingPointType, @float_type {
|
||||
|
||||
override string toStringWithTypes() { result = "float" }
|
||||
}
|
||||
|
||||
@@ -592,7 +559,6 @@ class FloatType extends FloatingPointType, @float_type {
|
||||
* The `double` type.
|
||||
*/
|
||||
class DoubleType extends FloatingPointType, @double_type {
|
||||
|
||||
override string toStringWithTypes() { result = "double" }
|
||||
}
|
||||
|
||||
@@ -600,7 +566,6 @@ class DoubleType extends FloatingPointType, @double_type {
|
||||
* The high-precision decimal type, `decimal`.
|
||||
*/
|
||||
class DecimalType extends SimpleType, @decimal_type {
|
||||
|
||||
override string toStringWithTypes() { result = "decimal" }
|
||||
}
|
||||
|
||||
@@ -615,7 +580,6 @@ class DecimalType extends SimpleType, @decimal_type {
|
||||
* ```
|
||||
*/
|
||||
class Enum extends ValueType, @enum_type {
|
||||
|
||||
/**
|
||||
* Gets the underlying integral type of this enum, not to be confused with
|
||||
* its base type which is always `System.Enum`.
|
||||
@@ -673,16 +637,13 @@ class Struct extends ValueType, @struct_type {
|
||||
* `null` (`NullType`), `dynamic` (`DynamicType`), or an array (`ArrayType`).
|
||||
*/
|
||||
class RefType extends ValueOrRefType, @ref_type {
|
||||
|
||||
/** Gets the number of overridden methods (NORM) of this reference type. */
|
||||
int getNumberOverridden() {
|
||||
result = count(this.getAnOverrider())
|
||||
}
|
||||
int getNumberOverridden() { result = count(this.getAnOverrider()) }
|
||||
|
||||
/** Gets a member that overrides a non-abstract member in a super type, if any. */
|
||||
private Virtualizable getAnOverrider() {
|
||||
getAMember() = result and
|
||||
exists(Virtualizable v | result.getOverridee() = v and not v.isAbstract())
|
||||
getAMember() = result and
|
||||
exists(Virtualizable v | result.getOverridee() = v and not v.isAbstract())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -694,18 +655,14 @@ class RefType extends ValueOrRefType, @ref_type {
|
||||
* This measures the inheritance complexity of this type.
|
||||
*/
|
||||
float getSpecialisationIndex() {
|
||||
this.getNumberOfCallables() != 0
|
||||
and
|
||||
result = (this.getNumberOverridden() * this.getInheritanceDepth())
|
||||
/
|
||||
((float)this.getNumberOfCallables())
|
||||
this.getNumberOfCallables() != 0 and
|
||||
result = (this.getNumberOverridden() * this.getInheritanceDepth()) /
|
||||
this.getNumberOfCallables().(float)
|
||||
}
|
||||
}
|
||||
|
||||
// Helper predicate to avoid slow "negation_body"
|
||||
private predicate isNonOverridden(Member m) {
|
||||
not m.(Virtualizable).isOverridden()
|
||||
}
|
||||
private predicate isNonOverridden(Member m) { not m.(Virtualizable).isOverridden() }
|
||||
|
||||
/**
|
||||
* A `class`, for example
|
||||
@@ -716,8 +673,7 @@ private predicate isNonOverridden(Member m) {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class Class extends RefType, @class_type {
|
||||
}
|
||||
class Class extends RefType, @class_type { }
|
||||
|
||||
/**
|
||||
* A class generated by the compiler from an anonymous object creation.
|
||||
@@ -728,15 +684,12 @@ class Class extends RefType, @class_type {
|
||||
* new { X = 0, Y = 0 };
|
||||
* ```
|
||||
*/
|
||||
class AnonymousClass extends Class {
|
||||
AnonymousClass() { this.getName().matches("<%") }
|
||||
}
|
||||
class AnonymousClass extends Class { AnonymousClass() { this.getName().matches("<%") } }
|
||||
|
||||
/**
|
||||
* The `object` type, `System.Object`.
|
||||
*/
|
||||
class ObjectType extends Class {
|
||||
|
||||
ObjectType() { this.hasQualifiedName("System.Object") }
|
||||
|
||||
override string toStringWithTypes() { result = "object" }
|
||||
@@ -746,7 +699,6 @@ class ObjectType extends Class {
|
||||
* The `string` type, `System.String`.
|
||||
*/
|
||||
class StringType extends Class {
|
||||
|
||||
StringType() { this.hasQualifiedName("System.String") }
|
||||
|
||||
override string toStringWithTypes() { result = "string" }
|
||||
@@ -761,9 +713,7 @@ class StringType extends Class {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class Interface extends RefType, @interface_type {
|
||||
|
||||
}
|
||||
class Interface extends RefType, @interface_type { }
|
||||
|
||||
/**
|
||||
* A `delegate` type, for example
|
||||
@@ -774,7 +724,7 @@ class Interface extends RefType, @interface_type {
|
||||
*/
|
||||
class DelegateType extends RefType, Parameterizable, @delegate_type {
|
||||
/** Gets the return type of this delegate. */
|
||||
Type getReturnType() { delegate_return_type(this,getTypeRef(result)) }
|
||||
Type getReturnType() { delegate_return_type(this, getTypeRef(result)) }
|
||||
|
||||
/** Holds if this delegate returns a `ref`. */
|
||||
predicate returnsRef() { ref_returns(this) }
|
||||
@@ -792,46 +742,42 @@ class NullType extends RefType, @null_type { }
|
||||
* A nullable type, for example `int?`.
|
||||
*/
|
||||
class NullableType extends ValueType, DotNet::ConstructedGeneric, @nullable_type {
|
||||
|
||||
/**
|
||||
* Gets the underlying value type of this nullable type.
|
||||
* For example `int` in `int?`.
|
||||
*/
|
||||
Type getUnderlyingType() { nullable_underlying_type(this,getTypeRef(result)) }
|
||||
Type getUnderlyingType() { nullable_underlying_type(this, getTypeRef(result)) }
|
||||
|
||||
override string toStringWithTypes() {
|
||||
result = getUnderlyingType().toStringWithTypes() + "?"
|
||||
}
|
||||
override string toStringWithTypes() { result = getUnderlyingType().toStringWithTypes() + "?" }
|
||||
|
||||
override Type getChild(int n) { result = getUnderlyingType() and n = 0 }
|
||||
|
||||
override Location getALocation() { result = getUnderlyingType().getALocation() }
|
||||
|
||||
override Type getTypeArgument(int p) { p=0 and result = getUnderlyingType() }
|
||||
override Type getTypeArgument(int p) { p = 0 and result = getUnderlyingType() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An array type, for example `int[]`.
|
||||
*/
|
||||
class ArrayType extends DotNet::ArrayType, RefType, @array_type {
|
||||
|
||||
/**
|
||||
* Gets the dimension of this array type. For example `int[][]` is of
|
||||
* dimension 2, while `int[]` is of dimension 1.
|
||||
*/
|
||||
int getDimension() { array_element_type(this,result,_,_) }
|
||||
int getDimension() { array_element_type(this, result, _, _) }
|
||||
|
||||
/**
|
||||
* Gets the rank of this array type. For example `int[,]` is of
|
||||
* rank 2, while `int[]` is of rank 1.
|
||||
*/
|
||||
int getRank() { array_element_type(this,_,result,_) }
|
||||
int getRank() { array_element_type(this, _, result, _) }
|
||||
|
||||
/** Holds if this array is multi-dimensional. */
|
||||
predicate isMultiDimensional() { this.getRank() > 1 }
|
||||
|
||||
/** Gets the element type of this array, for example `int` in `int[]`. */
|
||||
override Type getElementType() { array_element_type(this,_,_,getTypeRef(result)) }
|
||||
override Type getElementType() { array_element_type(this, _, _, getTypeRef(result)) }
|
||||
|
||||
/** Holds if this array type has the same shape (dimension and rank) as `that` array type. */
|
||||
predicate hasSameShapeAs(ArrayType that) {
|
||||
@@ -840,23 +786,19 @@ class ArrayType extends DotNet::ArrayType, RefType, @array_type {
|
||||
}
|
||||
|
||||
private string getRankString(int i) {
|
||||
i in [0 .. getRank() - 1]
|
||||
and
|
||||
if i = getRank() - 1 then
|
||||
result = ""
|
||||
else (
|
||||
result = "," + getRankString(i + 1)
|
||||
)
|
||||
i in [0 .. getRank() - 1] and
|
||||
if i = getRank() - 1 then result = "" else result = "," + getRankString(i + 1)
|
||||
}
|
||||
|
||||
private string getDimensionString(Type elementType) {
|
||||
exists(Type et, string res |
|
||||
et = getElementType() and
|
||||
res = "[" + getRankString(0) + "]" and
|
||||
if et instanceof ArrayType then
|
||||
result = res + et.(ArrayType).getDimensionString(elementType)
|
||||
else
|
||||
(result = res and elementType = et)
|
||||
if et instanceof ArrayType
|
||||
then result = res + et.(ArrayType).getDimensionString(elementType)
|
||||
else (
|
||||
result = res and elementType = et
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -880,7 +822,7 @@ class ArrayType extends DotNet::ArrayType, RefType, @array_type {
|
||||
* A pointer type, for example `char*`.
|
||||
*/
|
||||
class PointerType extends DotNet::PointerType, Type, @pointer_type {
|
||||
override Type getReferentType() { pointer_referent_type(this,getTypeRef(result)) }
|
||||
override Type getReferentType() { pointer_referent_type(this, getTypeRef(result)) }
|
||||
|
||||
override string toStringWithTypes() { result = DotNet::PointerType.super.toStringWithTypes() }
|
||||
|
||||
@@ -897,7 +839,6 @@ class PointerType extends DotNet::PointerType, Type, @pointer_type {
|
||||
* The `dynamic` type.
|
||||
*/
|
||||
class DynamicType extends RefType, @dynamic_type {
|
||||
|
||||
override string toStringWithTypes() { result = "dynamic" }
|
||||
}
|
||||
|
||||
@@ -905,7 +846,6 @@ class DynamicType extends RefType, @dynamic_type {
|
||||
* The `__arglist` type, modeling varargs invocation in C++.
|
||||
*/
|
||||
class ArglistType extends Type, @arglist_type {
|
||||
|
||||
override string toStringWithTypes() { result = "__arglist" }
|
||||
}
|
||||
|
||||
@@ -913,55 +853,48 @@ class ArglistType extends Type, @arglist_type {
|
||||
* A type that could not be resolved. This could happen if an indirect reference
|
||||
* is not available at compilation time.
|
||||
*/
|
||||
class UnknownType extends Type, @unknown_type {
|
||||
}
|
||||
class UnknownType extends Type, @unknown_type { }
|
||||
|
||||
/**
|
||||
* A type representing a tuple. For example, `(int, bool, string)`.
|
||||
*/
|
||||
class TupleType extends ValueType, @tuple_type {
|
||||
|
||||
/** Gets the underlying type of this tuple, which is of type `System.ValueTuple`. */
|
||||
ConstructedStruct getUnderlyingType() {
|
||||
tuple_underlying_type(this, result)
|
||||
}
|
||||
ConstructedStruct getUnderlyingType() { tuple_underlying_type(this, result) }
|
||||
|
||||
/**
|
||||
* Gets the `n`th element of this tuple, indexed from 0.
|
||||
* For example, the 0th (first) element of `(int, string)` is the field `Item1 int`.
|
||||
*/
|
||||
Field getElement(int n) {
|
||||
tuple_element(this, n, result)
|
||||
}
|
||||
Field getElement(int n) { tuple_element(this, n, result) }
|
||||
|
||||
/**
|
||||
* Gets the type of the `n`th element of this tuple, indexed from 0.
|
||||
* For example, the 0th (first) element type of `(int, string)` is `int`.
|
||||
*/
|
||||
Type getElementType(int n) {
|
||||
result = getElement(n).getType()
|
||||
}
|
||||
Type getElementType(int n) { result = getElement(n).getType() }
|
||||
|
||||
/** Gets an element of this tuple. */
|
||||
Field getAnElement() {
|
||||
result = getElement(_)
|
||||
}
|
||||
Field getAnElement() { result = getElement(_) }
|
||||
|
||||
override Location getALocation() {
|
||||
type_location(this, result)
|
||||
}
|
||||
override Location getALocation() { type_location(this, result) }
|
||||
|
||||
/**
|
||||
* Gets the arity of this tuple. For example, the arity of
|
||||
* `(int, int, double)` is 3.
|
||||
*/
|
||||
int getArity() {
|
||||
result=count(getAnElement())
|
||||
}
|
||||
int getArity() { result = count(getAnElement()) }
|
||||
|
||||
language [monotonicAggregates]
|
||||
language[monotonicAggregates]
|
||||
override string toStringWithTypes() {
|
||||
result = "(" + concat(int i | exists(getElement(i)) | getElement(i).getType().toStringWithTypes(), ", " order by i) + ")"
|
||||
result = "(" +
|
||||
concat(int i |
|
||||
exists(getElement(i))
|
||||
|
|
||||
getElement(i).getType().toStringWithTypes(), ", "
|
||||
order by
|
||||
i
|
||||
) + ")"
|
||||
}
|
||||
|
||||
override string getLabel() { result = getUnderlyingType().getLabel() }
|
||||
@@ -973,6 +906,7 @@ class TupleType extends ValueType, @tuple_type {
|
||||
*/
|
||||
class TypeMention extends @type_mention {
|
||||
Type type;
|
||||
|
||||
@type_mention_parent parent;
|
||||
|
||||
TypeMention() { type_mention(this, getTypeRef(type), parent) }
|
||||
|
||||
@@ -34,9 +34,7 @@ class UsingDirective extends Element, @using_directive {
|
||||
parent_namespace_declaration(this, result)
|
||||
}
|
||||
|
||||
override Location getALocation() {
|
||||
using_directive_location(this, result)
|
||||
}
|
||||
override Location getALocation() { using_directive_location(this, result) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -47,7 +45,7 @@ class UsingNamespaceDirective extends UsingDirective, @using_namespace_directive
|
||||
* Gets the target of this namespace `using` directive, for example `System`
|
||||
* in `using System`.
|
||||
*/
|
||||
Namespace getImportedNamespace() { using_namespace_directives(this,result) }
|
||||
Namespace getImportedNamespace() { using_namespace_directives(this, result) }
|
||||
|
||||
override string toString() { result = "using ...;" }
|
||||
}
|
||||
@@ -60,9 +58,7 @@ class UsingStaticDirective extends UsingDirective, @using_static_directive {
|
||||
* Gets the target of this type `using` directive, for example
|
||||
* `System.Console` in `using static System.Console`.
|
||||
*/
|
||||
ValueOrRefType getTarget() {
|
||||
using_static_directives(this, getTypeRef(result))
|
||||
}
|
||||
ValueOrRefType getTarget() { using_static_directives(this, getTypeRef(result)) }
|
||||
|
||||
override string toString() { result = "using static ...;" }
|
||||
}
|
||||
|
||||
@@ -14,46 +14,56 @@ private import semmle.code.csharp.ExprOrStmtParent
|
||||
* A variable. Either a variable with local scope (`LocalScopeVariable`) or a field (`Field`).
|
||||
*/
|
||||
class Variable extends Assignable, DotNet::Variable, @variable {
|
||||
|
||||
/** Gets the type that declares this variable, if any. */
|
||||
override ValueOrRefType getDeclaringType() {
|
||||
fields(this,_,_,result,_,_) or
|
||||
exists(Expr e | localvars(this, _, _, _, _, e) and result = e.getEnclosingStmt().getEnclosingCallable().getDeclaringType()) or
|
||||
exists(Callable parent | params(this, _, _, _, _ , parent, _) and result = parent.getDeclaringType()) or
|
||||
exists(Indexer indexer | params(this, _, _, _, _, indexer, _) and result = indexer.getDeclaringType()) or
|
||||
exists(@delegate_type t | params(this, _, _, _, _, t, _) and result = t) or
|
||||
exists(Expr parent | params(this, _, _, _, _, parent, _) and result = parent.getEnclosingCallable().getDeclaringType())
|
||||
fields(this, _, _, result, _, _)
|
||||
or
|
||||
exists(Expr e |
|
||||
localvars(this, _, _, _, _, e) and
|
||||
result = e.getEnclosingStmt().getEnclosingCallable().getDeclaringType()
|
||||
)
|
||||
or
|
||||
exists(Callable parent |
|
||||
params(this, _, _, _, _, parent, _) and result = parent.getDeclaringType()
|
||||
)
|
||||
or
|
||||
exists(Indexer indexer |
|
||||
params(this, _, _, _, _, indexer, _) and result = indexer.getDeclaringType()
|
||||
)
|
||||
or
|
||||
exists(@delegate_type t | params(this, _, _, _, _, t, _) and result = t)
|
||||
or
|
||||
exists(Expr parent |
|
||||
params(this, _, _, _, _, parent, _) and
|
||||
result = parent.getEnclosingCallable().getDeclaringType()
|
||||
)
|
||||
}
|
||||
|
||||
override Variable getSourceDeclaration() { result = this }
|
||||
|
||||
override string getName() {
|
||||
params(this,result,_,_,_,_,_) or
|
||||
localvars(this,_,result,_,_,_) or
|
||||
fields(this,_,result,_,_,_)
|
||||
params(this, result, _, _, _, _, _) or
|
||||
localvars(this, _, result, _, _, _) or
|
||||
fields(this, _, result, _, _, _)
|
||||
}
|
||||
|
||||
/** Gets the type of this variable. For example `int` in `int x`. */
|
||||
override Type getType() {
|
||||
params(this,_,getTypeRef(result),_,_,_,_) or
|
||||
localvars(this,_,_,_,getTypeRef(result),_) or
|
||||
fields(this,_,_,_,getTypeRef(result),_)
|
||||
params(this, _, getTypeRef(result), _, _, _, _) or
|
||||
localvars(this, _, _, _, getTypeRef(result), _) or
|
||||
fields(this, _, _, _, getTypeRef(result), _)
|
||||
}
|
||||
|
||||
override Location getALocation() {
|
||||
param_location(this,result) or
|
||||
localvar_location(this,result) or
|
||||
field_location(this,result)
|
||||
param_location(this, result) or
|
||||
localvar_location(this, result) or
|
||||
field_location(this, result)
|
||||
}
|
||||
|
||||
override VariableAccess getAnAccess() {
|
||||
result.getTarget() = this
|
||||
}
|
||||
override VariableAccess getAnAccess() { result.getTarget() = this }
|
||||
|
||||
/** Gets the expression used to initialise this variable, if any. */
|
||||
Expr getInitializer() {
|
||||
none()
|
||||
}
|
||||
Expr getInitializer() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,9 +87,7 @@ class LocalScopeVariable extends Variable, @local_scope_variable {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
predicate isCaptured() {
|
||||
exists(this.getACapturingCallable())
|
||||
}
|
||||
predicate isCaptured() { exists(this.getACapturingCallable()) }
|
||||
|
||||
/**
|
||||
* Gets a callable that captures this variable, if any. For example,
|
||||
@@ -115,8 +123,8 @@ class LocalScopeVariable extends Variable, @local_scope_variable {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class Parameter extends DotNet::Parameter, LocalScopeVariable, Attributable, TopLevelExprParent, @parameter {
|
||||
|
||||
class Parameter extends DotNet::Parameter, LocalScopeVariable, Attributable, TopLevelExprParent,
|
||||
@parameter {
|
||||
/**
|
||||
* Gets the position of this parameter. For example, the position of `x` is
|
||||
* 0 and the position of `y` is 1 in
|
||||
@@ -127,7 +135,7 @@ class Parameter extends DotNet::Parameter, LocalScopeVariable, Attributable, Top
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
override int getPosition() { params(this,_,_,result,_,_,_) }
|
||||
override int getPosition() { params(this, _, _, result, _, _, _) }
|
||||
|
||||
override int getIndex() { result = this.getPosition() }
|
||||
|
||||
@@ -141,7 +149,7 @@ class Parameter extends DotNet::Parameter, LocalScopeVariable, Attributable, Top
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
predicate isValue() { params(this,_,_,_,0,_,_) }
|
||||
predicate isValue() { params(this, _, _, _, 0, _, _) }
|
||||
|
||||
/**
|
||||
* Holds if this parameter is a reference parameter. For example, `p`
|
||||
@@ -153,7 +161,7 @@ class Parameter extends DotNet::Parameter, LocalScopeVariable, Attributable, Top
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
override predicate isRef() { params(this,_,_,_,1,_,_) }
|
||||
override predicate isRef() { params(this, _, _, _, 1, _, _) }
|
||||
|
||||
/**
|
||||
* Holds if this parameter is an output parameter. For example, `p`
|
||||
@@ -165,7 +173,7 @@ class Parameter extends DotNet::Parameter, LocalScopeVariable, Attributable, Top
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
override predicate isOut() { params(this,_,_,_,2,_,_) }
|
||||
override predicate isOut() { params(this, _, _, _, 2, _, _) }
|
||||
|
||||
/**
|
||||
* Holds if this parameter is a value type that is passed in by reference.
|
||||
@@ -177,7 +185,7 @@ class Parameter extends DotNet::Parameter, LocalScopeVariable, Attributable, Top
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
predicate isIn() { params(this,_,_,_,5,_,_) }
|
||||
predicate isIn() { params(this, _, _, _, 5, _, _) }
|
||||
|
||||
/** Holds if this parameter is an output or reference parameter. */
|
||||
predicate isOutOrRef() { isOut() or isRef() }
|
||||
@@ -192,7 +200,7 @@ class Parameter extends DotNet::Parameter, LocalScopeVariable, Attributable, Top
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
predicate isParams() { params(this,_,_,_,3,_,_) }
|
||||
predicate isParams() { params(this, _, _, _, 3, _, _) }
|
||||
|
||||
/**
|
||||
* Holds this parameter is the first parameter of an extension method.
|
||||
@@ -205,12 +213,12 @@ class Parameter extends DotNet::Parameter, LocalScopeVariable, Attributable, Top
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
predicate hasExtensionMethodModifier() { params(this,_,_,_,4,_,_) }
|
||||
predicate hasExtensionMethodModifier() { params(this, _, _, _, 4, _, _) }
|
||||
|
||||
/** Gets the declaring element of this parameter. */
|
||||
Parameterizable getDeclaringElement() { params(this,_,_,_,_,result,_) }
|
||||
Parameterizable getDeclaringElement() { params(this, _, _, _, _, result, _) }
|
||||
|
||||
override Parameter getSourceDeclaration() { params(this,_,_,_,_,_,result) }
|
||||
override Parameter getSourceDeclaration() { params(this, _, _, _, _, _, result) }
|
||||
|
||||
override string toString() { result = this.getName() }
|
||||
|
||||
@@ -232,9 +240,7 @@ class Parameter extends DotNet::Parameter, LocalScopeVariable, Attributable, Top
|
||||
predicate hasDefaultValue() { exists(getDefaultValue()) }
|
||||
|
||||
/** Gets the callable to which this parameter belongs, if any. */
|
||||
override Callable getCallable() {
|
||||
result.getAParameter() = this
|
||||
}
|
||||
override Callable getCallable() { result.getAParameter() = this }
|
||||
|
||||
/**
|
||||
* Gets an argument which is assigned to this parameter in a call to the
|
||||
@@ -258,9 +264,7 @@ class Parameter extends DotNet::Parameter, LocalScopeVariable, Attributable, Top
|
||||
* The assigned arguments to `x` are `1` and `4`, the assigned argument to
|
||||
* `y` is `5`, and the assigned arguments to `z` are `3` and `6`, respectively.
|
||||
*/
|
||||
Expr getAnAssignedArgument() {
|
||||
result = getCallable().getACall().getArgumentForParameter(this)
|
||||
}
|
||||
Expr getAnAssignedArgument() { result = getCallable().getACall().getArgumentForParameter(this) }
|
||||
|
||||
/** Holds if this parameter is potentially overwritten in the body of its callable. */
|
||||
predicate isOverwritten() {
|
||||
@@ -310,7 +314,6 @@ class ImplicitAccessorParameter extends Parameter {
|
||||
* ```
|
||||
*/
|
||||
class LocalVariable extends LocalScopeVariable, @local_variable {
|
||||
|
||||
/** Gets the declaration of this local variable. */
|
||||
LocalVariableDeclExpr getVariableDeclExpr() { result.getVariable() = this }
|
||||
|
||||
@@ -343,16 +346,14 @@ class LocalVariable extends LocalScopeVariable, @local_variable {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
predicate isImplicitlyTyped() { localvars(this,_,_,1,_,_) }
|
||||
predicate isImplicitlyTyped() { localvars(this, _, _, 1, _, _) }
|
||||
|
||||
/** Gets the enclosing callable of this local variable. */
|
||||
Callable getEnclosingCallable() {
|
||||
result = this.getVariableDeclExpr().getEnclosingCallable()
|
||||
}
|
||||
Callable getEnclosingCallable() { result = this.getVariableDeclExpr().getEnclosingCallable() }
|
||||
|
||||
override Callable getCallable() { result = getEnclosingCallable() }
|
||||
|
||||
override predicate isRef() { localvars(this,3,_,_,_,_) }
|
||||
override predicate isRef() { localvars(this, 3, _, _, _, _) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -367,7 +368,6 @@ class LocalVariable extends LocalScopeVariable, @local_variable {
|
||||
* ```
|
||||
*/
|
||||
class LocalConstant extends LocalVariable, @local_constant {
|
||||
|
||||
/** Gets the declaration of this local constant. */
|
||||
override LocalConstantDeclExpr getVariableDeclExpr() { result.getVariable() = this }
|
||||
|
||||
@@ -384,7 +384,8 @@ class LocalConstant extends LocalVariable, @local_constant {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class Field extends Variable, AssignableMember, Attributable, TopLevelExprParent, DotNet::Field, @field {
|
||||
class Field extends Variable, AssignableMember, Attributable, TopLevelExprParent, DotNet::Field,
|
||||
@field {
|
||||
/** Gets the initializer of this field, if any. */
|
||||
override Expr getInitializer() { result = this.getChildExpr(0) }
|
||||
|
||||
@@ -394,7 +395,7 @@ class Field extends Variable, AssignableMember, Attributable, TopLevelExprParent
|
||||
/** Holds if this field is `readonly`. */
|
||||
predicate isReadOnly() { this.hasModifier("readonly") }
|
||||
|
||||
override Field getSourceDeclaration() { fields(this,_,_,_,_,result) }
|
||||
override Field getSourceDeclaration() { fields(this, _, _, _, _, result) }
|
||||
|
||||
override FieldAccess getAnAccess() { result = Variable.super.getAnAccess() }
|
||||
|
||||
@@ -415,7 +416,6 @@ class Field extends Variable, AssignableMember, Attributable, TopLevelExprParent
|
||||
* ```
|
||||
*/
|
||||
class MemberConstant extends Field, @constant {
|
||||
|
||||
/** Gets the computed value of this constant. */
|
||||
string getValue() { constant_value(this, result) }
|
||||
}
|
||||
@@ -431,7 +431,6 @@ class MemberConstant extends Field, @constant {
|
||||
* ```
|
||||
*/
|
||||
class EnumConstant extends MemberConstant {
|
||||
|
||||
EnumConstant() { this.getDeclaringType() instanceof Enum }
|
||||
|
||||
/** Gets the `enum` that declares this constant. */
|
||||
@@ -448,9 +447,7 @@ class EnumConstant extends MemberConstant {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
IntegralType getUnderlyingType() {
|
||||
result = this.getDeclaringEnum().getUnderlyingType()
|
||||
}
|
||||
IntegralType getUnderlyingType() { result = this.getDeclaringEnum().getUnderlyingType() }
|
||||
|
||||
/**
|
||||
* Holds if this `enum` constant has an explicit value. If not,
|
||||
|
||||
@@ -9,7 +9,7 @@ import semmle.code.csharp.Location
|
||||
library class XMLLocatableElement extends @xmllocatable, Element {
|
||||
override string toString() { result = this.(XMLLocatable).toString() }
|
||||
|
||||
override Location getALocation() { result = this.(XMLLocatable).getALocation() }
|
||||
override Location getALocation() { result = this.(XMLLocatable).getALocation() }
|
||||
}
|
||||
|
||||
/** An XML element that has a location. */
|
||||
@@ -19,15 +19,17 @@ class XMLLocatable extends @xmllocatable {
|
||||
}
|
||||
|
||||
/** Gets the source location for this element. */
|
||||
Location getALocation() { xmllocations(this,result) }
|
||||
Location getALocation() { xmllocations(this, result) }
|
||||
|
||||
/**
|
||||
* Whether this element has the specified location information,
|
||||
* including file path, start line, start column, end line and end column.
|
||||
*/
|
||||
predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) {
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
exists(File f, Location l | l = this.getALocation() |
|
||||
locations_default(l,f,startline,startcolumn,endline,endcolumn) and
|
||||
locations_default(l, f, startline, startcolumn, endline, endcolumn) and
|
||||
filepath = f.getAbsolutePath()
|
||||
)
|
||||
}
|
||||
@@ -48,45 +50,45 @@ class XMLParent extends @xmlparent {
|
||||
abstract string getName();
|
||||
|
||||
/** Gets the file to which this XML parent belongs. */
|
||||
XMLFile getFile() { result = this or xmlElements(this,_,_,_,result) }
|
||||
XMLFile getFile() { result = this or xmlElements(this, _, _, _, result) }
|
||||
|
||||
/** Gets the child element at a specified index of this XML parent. */
|
||||
XMLElement getChild(int index) { xmlElements(result, _, this, index, _) }
|
||||
|
||||
/** Gets a child element of this XML parent. */
|
||||
XMLElement getAChild() { xmlElements(result,_,this,_,_) }
|
||||
XMLElement getAChild() { xmlElements(result, _, this, _, _) }
|
||||
|
||||
/** Gets a child element of this XML parent with the given `name`. */
|
||||
XMLElement getAChild(string name) { xmlElements(result,_,this,_,_) and result.hasName(name) }
|
||||
XMLElement getAChild(string name) { xmlElements(result, _, this, _, _) and result.hasName(name) }
|
||||
|
||||
/** Gets a comment that is a child of this XML parent. */
|
||||
XMLComment getAComment() { xmlComments(result,_,this,_) }
|
||||
XMLComment getAComment() { xmlComments(result, _, this, _) }
|
||||
|
||||
/** Gets a character sequence that is a child of this XML parent. */
|
||||
XMLCharacters getACharactersSet() { xmlChars(result,_,this,_,_,_) }
|
||||
XMLCharacters getACharactersSet() { xmlChars(result, _, this, _, _, _) }
|
||||
|
||||
/** Gets the depth in the tree. (Overridden in XMLElement.) */
|
||||
int getDepth() { result = 0 }
|
||||
|
||||
/** Gets the number of child XML elements of this XML parent. */
|
||||
int getNumberOfChildren() {
|
||||
result = count(XMLElement e | xmlElements(e,_,this,_,_))
|
||||
}
|
||||
int getNumberOfChildren() { result = count(XMLElement e | xmlElements(e, _, this, _, _)) }
|
||||
|
||||
/** Gets the number of places in the body of this XML parent where text occurs. */
|
||||
int getNumberOfCharacterSets() {
|
||||
result = count(int pos | xmlChars(_,_,this,pos,_,_))
|
||||
}
|
||||
int getNumberOfCharacterSets() { result = count(int pos | xmlChars(_, _, this, pos, _, _)) }
|
||||
|
||||
/** Append all the character sequences of this XML parent from left to right, separated by a space. */
|
||||
string allCharactersString() {
|
||||
result = concat(string chars, int pos | xmlChars(_, chars, this, pos, _, _) | chars, " " order by pos)
|
||||
result = concat(string chars, int pos |
|
||||
xmlChars(_, chars, this, pos, _, _)
|
||||
|
|
||||
chars, " "
|
||||
order by
|
||||
pos
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the text value contained in this XML parent. */
|
||||
string getTextValue() {
|
||||
result = allCharactersString()
|
||||
}
|
||||
string getTextValue() { result = allCharactersString() }
|
||||
|
||||
/** Gets a printable representation of this XML parent. */
|
||||
string toString() { result = this.getName() }
|
||||
@@ -94,63 +96,59 @@ class XMLParent extends @xmlparent {
|
||||
|
||||
/** An XML file. */
|
||||
class XMLFile extends XMLParent, File {
|
||||
XMLFile() {
|
||||
xmlEncoding(this,_)
|
||||
}
|
||||
XMLFile() { xmlEncoding(this, _) }
|
||||
|
||||
/** Gets a printable representation of this XML file. */
|
||||
override string toString() { result = XMLParent.super.toString() }
|
||||
|
||||
/** Gets the name of this XML file. */
|
||||
override string getName() { files(this,result,_,_,_) }
|
||||
override string getName() { files(this, result, _, _, _) }
|
||||
|
||||
/** Gets the path of this XML file. */
|
||||
string getPath() { files(this,_,result,_,_) }
|
||||
string getPath() { files(this, _, result, _, _) }
|
||||
|
||||
/** Gets the path of the folder that contains this XML file. */
|
||||
string getFolder() {
|
||||
result = this.getPath().substring(0, this.getPath().length()-this.getName().length())
|
||||
result = this.getPath().substring(0, this.getPath().length() - this.getName().length())
|
||||
}
|
||||
|
||||
/** Gets the encoding of this XML file. */
|
||||
string getEncoding() { xmlEncoding(this,result) }
|
||||
string getEncoding() { xmlEncoding(this, result) }
|
||||
|
||||
/** Gets the XML file itself. */
|
||||
override
|
||||
XMLFile getFile() { result = this }
|
||||
override XMLFile getFile() { result = this }
|
||||
|
||||
/** Gets a top-most element in an XML file. */
|
||||
XMLElement getARootElement() { result = this.getAChild() }
|
||||
|
||||
/** Gets a DTD associated with this XML file. */
|
||||
XMLDTD getADTD() { xmlDTDs(result,_,_,_,this) }
|
||||
XMLDTD getADTD() { xmlDTDs(result, _, _, _, this) }
|
||||
}
|
||||
|
||||
/** A "Document Type Definition" of an XML file. */
|
||||
class XMLDTD extends XMLLocatable, @xmldtd {
|
||||
/** Gets the name of the root element of this DTD. */
|
||||
string getRoot() { xmlDTDs(this,result,_,_,_) }
|
||||
string getRoot() { xmlDTDs(this, result, _, _, _) }
|
||||
|
||||
/** Gets the public ID of this DTD. */
|
||||
string getPublicId() { xmlDTDs(this,_,result,_,_) }
|
||||
string getPublicId() { xmlDTDs(this, _, result, _, _) }
|
||||
|
||||
/** Gets the system ID of this DTD. */
|
||||
string getSystemId() { xmlDTDs(this,_,_,result,_) }
|
||||
string getSystemId() { xmlDTDs(this, _, _, result, _) }
|
||||
|
||||
/** Holds if this DTD is public. */
|
||||
predicate isPublic() { not xmlDTDs(this,_,"",_,_) }
|
||||
predicate isPublic() { not xmlDTDs(this, _, "", _, _) }
|
||||
|
||||
/** Gets the parent of this DTD. */
|
||||
XMLParent getParent() { xmlDTDs(this,_,_,_,result) }
|
||||
XMLParent getParent() { xmlDTDs(this, _, _, _, result) }
|
||||
|
||||
/** Gets a printable representation of this DTD. */
|
||||
override string toString() {
|
||||
(this.isPublic() and result = this.getRoot() + " PUBLIC '" +
|
||||
this.getPublicId() + "' '" +
|
||||
this.getSystemId() + "'") or
|
||||
(not this.isPublic() and result = this.getRoot() +
|
||||
" SYSTEM '" +
|
||||
this.getSystemId() + "'")
|
||||
this.isPublic() and
|
||||
result = this.getRoot() + " PUBLIC '" + this.getPublicId() + "' '" + this.getSystemId() + "'"
|
||||
or
|
||||
not this.isPublic() and
|
||||
result = this.getRoot() + " SYSTEM '" + this.getSystemId() + "'"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,48 +158,40 @@ class XMLElement extends @xmlelement, XMLParent, XMLLocatable {
|
||||
predicate hasName(string name) { name = getName() }
|
||||
|
||||
/** Gets the name of this XML element. */
|
||||
override string getName() { xmlElements(this,result,_,_,_) }
|
||||
override string getName() { xmlElements(this, result, _, _, _) }
|
||||
|
||||
/** Gets the XML file in which this XML element occurs. */
|
||||
override
|
||||
XMLFile getFile() { xmlElements(this,_,_,_,result) }
|
||||
override XMLFile getFile() { xmlElements(this, _, _, _, result) }
|
||||
|
||||
/** Gets the parent of this XML element. */
|
||||
XMLParent getParent() { xmlElements(this,_,result,_,_) }
|
||||
XMLParent getParent() { xmlElements(this, _, result, _, _) }
|
||||
|
||||
/** Gets the index of this XML element among its parent's children. */
|
||||
int getIndex() { xmlElements(this, _, _, result, _) }
|
||||
|
||||
/** Holds if this XML element has a namespace. */
|
||||
predicate hasNamespace() { xmlHasNs(this,_,_) }
|
||||
predicate hasNamespace() { xmlHasNs(this, _, _) }
|
||||
|
||||
/** Gets the namespace of this XML element, if any. */
|
||||
XMLNamespace getNamespace() { xmlHasNs(this,result,_) }
|
||||
XMLNamespace getNamespace() { xmlHasNs(this, result, _) }
|
||||
|
||||
/** Gets the index of this XML element among its parent's children. */
|
||||
int getElementPositionIndex() { xmlElements(this,_,_,result,_) }
|
||||
int getElementPositionIndex() { xmlElements(this, _, _, result, _) }
|
||||
|
||||
/** Gets the depth of this element within the XML file tree structure. */
|
||||
override
|
||||
int getDepth() { result = this.getParent().getDepth() + 1 }
|
||||
override int getDepth() { result = this.getParent().getDepth() + 1 }
|
||||
|
||||
/** Gets an XML attribute of this XML element. */
|
||||
XMLAttribute getAnAttribute() { result.getElement() = this }
|
||||
|
||||
/** Gets the attribute with the specified `name`, if any. */
|
||||
XMLAttribute getAttribute(string name) {
|
||||
result.getElement() = this and result.getName() = name
|
||||
}
|
||||
XMLAttribute getAttribute(string name) { result.getElement() = this and result.getName() = name }
|
||||
|
||||
/** Holds if this XML element has an attribute with the specified `name`. */
|
||||
predicate hasAttribute(string name) {
|
||||
exists(XMLAttribute a| a = this.getAttribute(name))
|
||||
}
|
||||
predicate hasAttribute(string name) { exists(XMLAttribute a | a = this.getAttribute(name)) }
|
||||
|
||||
/** Gets the value of the attribute with the specified `name`, if any. */
|
||||
string getAttributeValue(string name) {
|
||||
result = this.getAttribute(name).getValue()
|
||||
}
|
||||
string getAttributeValue(string name) { result = this.getAttribute(name).getValue() }
|
||||
|
||||
/** Gets a printable representation of this XML element. */
|
||||
override string toString() { result = XMLParent.super.toString() }
|
||||
@@ -210,19 +200,19 @@ class XMLElement extends @xmlelement, XMLParent, XMLLocatable {
|
||||
/** An attribute that occurs inside an XML element. */
|
||||
class XMLAttribute extends @xmlattribute, XMLLocatable {
|
||||
/** Gets the name of this attribute. */
|
||||
string getName() { xmlAttrs(this,_,result,_,_,_) }
|
||||
string getName() { xmlAttrs(this, _, result, _, _, _) }
|
||||
|
||||
/** Gets the XML element to which this attribute belongs. */
|
||||
XMLElement getElement() { xmlAttrs(this,result,_,_,_,_) }
|
||||
XMLElement getElement() { xmlAttrs(this, result, _, _, _, _) }
|
||||
|
||||
/** Holds if this attribute has a namespace. */
|
||||
predicate hasNamespace() { xmlHasNs(this,_,_) }
|
||||
predicate hasNamespace() { xmlHasNs(this, _, _) }
|
||||
|
||||
/** Gets the namespace of this attribute, if any. */
|
||||
XMLNamespace getNamespace() { xmlHasNs(this,result,_) }
|
||||
XMLNamespace getNamespace() { xmlHasNs(this, result, _) }
|
||||
|
||||
/** Gets the value of this attribute. */
|
||||
string getValue() { xmlAttrs(this,_,_,result,_,_) }
|
||||
string getValue() { xmlAttrs(this, _, _, result, _, _) }
|
||||
|
||||
/** Gets a printable representation of this XML attribute. */
|
||||
override string toString() { result = this.getName() + "=" + this.getValue() }
|
||||
@@ -231,27 +221,28 @@ class XMLAttribute extends @xmlattribute, XMLLocatable {
|
||||
/** A namespace used in an XML file */
|
||||
class XMLNamespace extends XMLLocatable, @xmlnamespace {
|
||||
/** Gets the prefix of this namespace. */
|
||||
string getPrefix() { xmlNs(this,result,_,_) }
|
||||
string getPrefix() { xmlNs(this, result, _, _) }
|
||||
|
||||
/** Gets the URI of this namespace. */
|
||||
string getURI() { xmlNs(this,_,result,_) }
|
||||
string getURI() { xmlNs(this, _, result, _) }
|
||||
|
||||
/** Holds if this namespace has no prefix. */
|
||||
predicate isDefault() { this.getPrefix() = "" }
|
||||
|
||||
override string toString() {
|
||||
(this.isDefault() and result = this.getURI()) or
|
||||
(not this.isDefault() and result = this.getPrefix() + ":" + this.getURI())
|
||||
this.isDefault() and result = this.getURI()
|
||||
or
|
||||
not this.isDefault() and result = this.getPrefix() + ":" + this.getURI()
|
||||
}
|
||||
}
|
||||
|
||||
/** A comment of the form `<!-- ... -->` is an XML comment. */
|
||||
class XMLComment extends @xmlcomment, XMLLocatable {
|
||||
/** Gets the text content of this XML comment. */
|
||||
string getText() { xmlComments(this,result,_,_) }
|
||||
string getText() { xmlComments(this, result, _, _) }
|
||||
|
||||
/** Gets the parent of this XML comment. */
|
||||
XMLParent getParent() { xmlComments(this,_,result,_) }
|
||||
XMLParent getParent() { xmlComments(this, _, result, _) }
|
||||
|
||||
/** Gets a printable representation of this XML comment. */
|
||||
override string toString() { result = this.getText() }
|
||||
@@ -263,13 +254,13 @@ class XMLComment extends @xmlcomment, XMLLocatable {
|
||||
*/
|
||||
class XMLCharacters extends @xmlcharacters, XMLLocatable {
|
||||
/** Gets the content of this character sequence. */
|
||||
string getCharacters() { xmlChars(this,result,_,_,_,_) }
|
||||
string getCharacters() { xmlChars(this, result, _, _, _, _) }
|
||||
|
||||
/** Gets the parent of this character sequence. */
|
||||
XMLParent getParent() { xmlChars(this,_,result,_,_,_) }
|
||||
XMLParent getParent() { xmlChars(this, _, result, _, _, _) }
|
||||
|
||||
/** Holds if this character sequence is CDATA. */
|
||||
predicate isCDATA() { xmlChars(this,_,_,_,1,_) }
|
||||
predicate isCDATA() { xmlChars(this, _, _, _, 1, _) }
|
||||
|
||||
/** Gets a printable representation of this XML character sequence. */
|
||||
override string toString() { result = this.getCharacters() }
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/** Provides classes for assertions. */
|
||||
|
||||
private import semmle.code.csharp.frameworks.system.Diagnostics
|
||||
private import semmle.code.csharp.frameworks.test.VisualStudio
|
||||
private import semmle.code.csharp.frameworks.System
|
||||
@@ -11,29 +12,23 @@ abstract class AssertMethod extends Method {
|
||||
abstract int getAssertionIndex();
|
||||
|
||||
/** Gets the parameter being asserted. */
|
||||
final Parameter getAssertedParameter() {
|
||||
result = this.getParameter(this.getAssertionIndex())
|
||||
}
|
||||
final Parameter getAssertedParameter() { result = this.getParameter(this.getAssertionIndex()) }
|
||||
|
||||
/** Gets the exception being thrown if the assertion fails, if any. */
|
||||
abstract ExceptionClass getExceptionClass();
|
||||
}
|
||||
|
||||
/** A positive assertion method. */
|
||||
abstract class AssertTrueMethod extends AssertMethod {
|
||||
}
|
||||
abstract class AssertTrueMethod extends AssertMethod { }
|
||||
|
||||
/** A negated assertion method. */
|
||||
abstract class AssertFalseMethod extends AssertMethod {
|
||||
}
|
||||
abstract class AssertFalseMethod extends AssertMethod { }
|
||||
|
||||
/** A `null` assertion method. */
|
||||
abstract class AssertNullMethod extends AssertMethod {
|
||||
}
|
||||
abstract class AssertNullMethod extends AssertMethod { }
|
||||
|
||||
/** A non-`null` assertion method. */
|
||||
abstract class AssertNonNullMethod extends AssertMethod {
|
||||
}
|
||||
abstract class AssertNonNullMethod extends AssertMethod { }
|
||||
|
||||
/** An assertion, that is, a call to an assertion method. */
|
||||
class Assertion extends MethodCall {
|
||||
@@ -45,17 +40,14 @@ class Assertion extends MethodCall {
|
||||
AssertMethod getAssertMethod() { result = target }
|
||||
|
||||
/** Gets the expression that this assertion pertains to. */
|
||||
Expr getExpr() {
|
||||
result = this.getArgumentForParameter(target.getAssertedParameter())
|
||||
}
|
||||
Expr getExpr() { result = this.getArgumentForParameter(target.getAssertedParameter()) }
|
||||
|
||||
pragma[nomagic]
|
||||
private JoinBlockPredecessor getAPossiblyDominatedPredecessor(JoinBlock jb) {
|
||||
// Only calculate dominance by explicit recursion for split nodes;
|
||||
// all other nodes can use regular CFG dominance
|
||||
this instanceof ControlFlow::Internal::SplitControlFlowElement and
|
||||
exists(BasicBlock bb |
|
||||
bb = this.getAControlFlowNode().getBasicBlock() |
|
||||
exists(BasicBlock bb | bb = this.getAControlFlowNode().getBasicBlock() |
|
||||
result = bb.getASuccessor*()
|
||||
) and
|
||||
result.getASuccessor() = jb and
|
||||
@@ -65,8 +57,7 @@ class Assertion extends MethodCall {
|
||||
pragma[nomagic]
|
||||
private predicate isPossiblyDominatedJoinBlock(JoinBlock jb) {
|
||||
exists(this.getAPossiblyDominatedPredecessor(jb)) and
|
||||
forall(BasicBlock pred |
|
||||
pred = jb.getAPredecessor() |
|
||||
forall(BasicBlock pred | pred = jb.getAPredecessor() |
|
||||
pred = this.getAPossiblyDominatedPredecessor(jb)
|
||||
or
|
||||
jb.dominates(pred)
|
||||
@@ -77,16 +68,15 @@ class Assertion extends MethodCall {
|
||||
private predicate strictlyDominatesSplit(BasicBlock bb) {
|
||||
this.getAControlFlowNode().getBasicBlock().immediatelyDominates(bb)
|
||||
or
|
||||
if bb instanceof JoinBlock then
|
||||
if bb instanceof JoinBlock
|
||||
then
|
||||
this.isPossiblyDominatedJoinBlock(bb) and
|
||||
forall(BasicBlock pred |
|
||||
pred = this.getAPossiblyDominatedPredecessor(bb) |
|
||||
forall(BasicBlock pred | pred = this.getAPossiblyDominatedPredecessor(bb) |
|
||||
this.strictlyDominatesSplit(pred)
|
||||
or
|
||||
this.getAControlFlowNode().getBasicBlock() = pred
|
||||
)
|
||||
else
|
||||
this.strictlyDominatesSplit(bb.getAPredecessor())
|
||||
else this.strictlyDominatesSplit(bb.getAPredecessor())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -111,7 +101,8 @@ class FailingAssertion extends Assertion {
|
||||
FailingAssertion() {
|
||||
exists(AssertMethod am, Expr e |
|
||||
am = this.getAssertMethod() and
|
||||
e = this.getExpr() |
|
||||
e = this.getExpr()
|
||||
|
|
||||
am instanceof AssertTrueMethod and
|
||||
e.getValue() = "false"
|
||||
or
|
||||
@@ -140,9 +131,7 @@ class SystemDiagnosticsDebugAssertTrueMethod extends AssertTrueMethod {
|
||||
|
||||
/** A Visual Studio assertion method. */
|
||||
class VSTestAssertTrueMethod extends AssertTrueMethod {
|
||||
VSTestAssertTrueMethod() {
|
||||
this = any(VSTestAssertClass c).getIsTrueMethod()
|
||||
}
|
||||
VSTestAssertTrueMethod() { this = any(VSTestAssertClass c).getIsTrueMethod() }
|
||||
|
||||
override int getAssertionIndex() { result = 0 }
|
||||
|
||||
@@ -151,9 +140,7 @@ class VSTestAssertTrueMethod extends AssertTrueMethod {
|
||||
|
||||
/** A Visual Studio negated assertion method. */
|
||||
class VSTestAssertFalseMethod extends AssertFalseMethod {
|
||||
VSTestAssertFalseMethod() {
|
||||
this = any(VSTestAssertClass c).getIsFalseMethod()
|
||||
}
|
||||
VSTestAssertFalseMethod() { this = any(VSTestAssertClass c).getIsFalseMethod() }
|
||||
|
||||
override int getAssertionIndex() { result = 0 }
|
||||
|
||||
@@ -162,9 +149,7 @@ class VSTestAssertFalseMethod extends AssertFalseMethod {
|
||||
|
||||
/** A Visual Studio `null` assertion method. */
|
||||
class VSTestAssertNullMethod extends AssertNullMethod {
|
||||
VSTestAssertNullMethod() {
|
||||
this = any(VSTestAssertClass c).getIsNullMethod()
|
||||
}
|
||||
VSTestAssertNullMethod() { this = any(VSTestAssertClass c).getIsNullMethod() }
|
||||
|
||||
override int getAssertionIndex() { result = 0 }
|
||||
|
||||
@@ -173,9 +158,7 @@ class VSTestAssertNullMethod extends AssertNullMethod {
|
||||
|
||||
/** A Visual Studio non-`null` assertion method. */
|
||||
class VSTestAssertNonNullMethod extends AssertNonNullMethod {
|
||||
VSTestAssertNonNullMethod() {
|
||||
this = any(VSTestAssertClass c).getIsNotNullMethod()
|
||||
}
|
||||
VSTestAssertNonNullMethod() { this = any(VSTestAssertClass c).getIsNotNullMethod() }
|
||||
|
||||
override int getAssertionIndex() { result = 0 }
|
||||
|
||||
@@ -185,13 +168,13 @@ class VSTestAssertNonNullMethod extends AssertNonNullMethod {
|
||||
/** A method that forwards to another assertion method. */
|
||||
class ForwarderAssertMethod extends AssertMethod {
|
||||
Assertion a;
|
||||
|
||||
Parameter p;
|
||||
|
||||
ForwarderAssertMethod() {
|
||||
p = this.getAParameter() and
|
||||
strictcount(AssignableDefinition def | def.getTarget() = p) = 1 and
|
||||
forex(ControlFlowElement body |
|
||||
body = this.getABody() |
|
||||
forex(ControlFlowElement body | body = this.getABody() |
|
||||
bodyAsserts(this, body, a) and
|
||||
a.getExpr() = p.getAnAccess()
|
||||
)
|
||||
@@ -227,33 +210,23 @@ private Stmt getAnAssertingStmt(Assertion a) {
|
||||
|
||||
/** A method that forwards to a positive assertion method. */
|
||||
class ForwarderAssertTrueMethod extends ForwarderAssertMethod, AssertTrueMethod {
|
||||
ForwarderAssertTrueMethod() {
|
||||
this.getUnderlyingAssertMethod() instanceof AssertTrueMethod
|
||||
}
|
||||
ForwarderAssertTrueMethod() { this.getUnderlyingAssertMethod() instanceof AssertTrueMethod }
|
||||
}
|
||||
|
||||
/** A method that forwards to a negated assertion method. */
|
||||
class ForwarderAssertFalseMethod extends ForwarderAssertMethod, AssertFalseMethod {
|
||||
ForwarderAssertFalseMethod() {
|
||||
this.getUnderlyingAssertMethod() instanceof AssertFalseMethod
|
||||
}
|
||||
ForwarderAssertFalseMethod() { this.getUnderlyingAssertMethod() instanceof AssertFalseMethod }
|
||||
}
|
||||
|
||||
/** A method that forwards to a `null` assertion method. */
|
||||
class ForwarderAssertNullMethod extends ForwarderAssertMethod, AssertNullMethod {
|
||||
ForwarderAssertNullMethod() {
|
||||
this.getUnderlyingAssertMethod() instanceof AssertNullMethod
|
||||
}
|
||||
ForwarderAssertNullMethod() { this.getUnderlyingAssertMethod() instanceof AssertNullMethod }
|
||||
}
|
||||
|
||||
/** A method that forwards to a non-`null` assertion method. */
|
||||
class ForwarderAssertNonNullMethod extends ForwarderAssertMethod, AssertNonNullMethod {
|
||||
ForwarderAssertNonNullMethod() {
|
||||
this.getUnderlyingAssertMethod() instanceof AssertNonNullMethod
|
||||
}
|
||||
ForwarderAssertNonNullMethod() { this.getUnderlyingAssertMethod() instanceof AssertNonNullMethod }
|
||||
}
|
||||
|
||||
/** Holds if expression `e` appears in an assertion. */
|
||||
predicate isExprInAssertion(Expr e) {
|
||||
e = any(Assertion a).getExpr().getAChildExpr*()
|
||||
}
|
||||
predicate isExprInAssertion(Expr e) { e = any(Assertion a).getExpr().getAChildExpr*() }
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/** Provides classes for collections. */
|
||||
|
||||
import csharp
|
||||
|
||||
private string modifyMethodName() {
|
||||
@@ -82,11 +83,13 @@ private string genericCollectionTypeName() {
|
||||
/** A collection type. */
|
||||
class CollectionType extends RefType {
|
||||
CollectionType() {
|
||||
exists(RefType base |
|
||||
base = this.getABaseType*() |
|
||||
exists(RefType base | base = this.getABaseType*() |
|
||||
base.hasQualifiedName(collectionNamespaceName(), collectionTypeName())
|
||||
or
|
||||
base.(ConstructedType).getUnboundGeneric().hasQualifiedName(genericCollectionNamespaceName(), genericCollectionTypeName())
|
||||
base
|
||||
.(ConstructedType)
|
||||
.getUnboundGeneric()
|
||||
.hasQualifiedName(genericCollectionNamespaceName(), genericCollectionTypeName())
|
||||
)
|
||||
or
|
||||
this instanceof ArrayType
|
||||
@@ -138,10 +141,7 @@ private string noAddMethodName() {
|
||||
/** Holds if `a` is an access that does not modify a collection. */
|
||||
private predicate readonlyAccess(Access a) {
|
||||
// A read-only method call
|
||||
exists(MethodCall mc |
|
||||
mc.getQualifier() = a |
|
||||
mc.getTarget().hasName(readonlyMethodName())
|
||||
)
|
||||
exists(MethodCall mc | mc.getQualifier() = a | mc.getTarget().hasName(readonlyMethodName()))
|
||||
or
|
||||
// Any property access
|
||||
a = any(PropertyAccess pa).getQualifier()
|
||||
@@ -155,19 +155,14 @@ class NoAddAccess extends Access {
|
||||
NoAddAccess() {
|
||||
readonlyAccess(this)
|
||||
or
|
||||
exists(MethodCall mc |
|
||||
mc.getQualifier() = this |
|
||||
mc.getTarget().hasName(noAddMethodName())
|
||||
)
|
||||
exists(MethodCall mc | mc.getQualifier() = this | mc.getTarget().hasName(noAddMethodName()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** An access that initializes an empty collection. */
|
||||
class EmptyInitializationAccess extends Access {
|
||||
EmptyInitializationAccess() {
|
||||
exists(AssignableDefinition def |
|
||||
def.getTargetAccess() = this |
|
||||
exists(AssignableDefinition def | def.getTargetAccess() = this |
|
||||
def.getSource() instanceof EmptyCollectionCreation
|
||||
)
|
||||
}
|
||||
|
||||
@@ -84,49 +84,44 @@ private newtype TComparisonTest =
|
||||
kind.isLessThanEquals() and
|
||||
first = co.getLeftOperand() and
|
||||
second = co.getRightOperand()
|
||||
}
|
||||
or
|
||||
} or
|
||||
TEqualsCall(MethodCall mc) {
|
||||
exists(Method m |
|
||||
m = mc.getTarget() |
|
||||
exists(Method m | m = mc.getTarget() |
|
||||
m instanceof EqualsMethod or
|
||||
m instanceof IEquatableEqualsMethod
|
||||
)
|
||||
}
|
||||
or
|
||||
} or
|
||||
TStaticEqualsCall(MethodCall mc) {
|
||||
exists(Method m |
|
||||
m = mc.getTarget() |
|
||||
exists(Method m | m = mc.getTarget() |
|
||||
m = any(SystemObjectClass c).getStaticEqualsMethod() or
|
||||
m = any(SystemObjectClass c).getReferenceEqualsMethod()
|
||||
)
|
||||
}
|
||||
or
|
||||
} or
|
||||
TCompareToCall(MethodCall mc) {
|
||||
exists(Method m, Method target |
|
||||
target = mc.getTarget() and
|
||||
(target = m or target = m.getAnUltimateImplementor()) |
|
||||
(target = m or target = m.getAnUltimateImplementor())
|
||||
|
|
||||
m = any(SystemIComparableInterface i).getCompareToMethod()
|
||||
or
|
||||
m = any(SystemIComparableTInterface i).getAConstructedGeneric().getAMethod() and
|
||||
m.getSourceDeclaration() = any(SystemIComparableTInterface i).getCompareToMethod()
|
||||
)
|
||||
}
|
||||
or
|
||||
} or
|
||||
TCompareCall(MethodCall mc) {
|
||||
exists(Method m, Method target |
|
||||
target = mc.getTarget() and
|
||||
(target = m or target = m.getAnUltimateImplementor()) |
|
||||
(target = m or target = m.getAnUltimateImplementor())
|
||||
|
|
||||
m = any(SystemCollectionsIComparerInterface i).getCompareMethod()
|
||||
or
|
||||
m = any(SystemCollectionsGenericIComparerTInterface i).getAConstructedGeneric().getAMethod() and
|
||||
m.getSourceDeclaration() = any(SystemCollectionsGenericIComparerTInterface i).getCompareMethod()
|
||||
m.getSourceDeclaration() = any(SystemCollectionsGenericIComparerTInterface i)
|
||||
.getCompareMethod()
|
||||
)
|
||||
}
|
||||
or
|
||||
} or
|
||||
TComparisonOperatorCall(OperatorCall oc, ComparisonKind kind, Expr first, Expr second) {
|
||||
exists(Operator o |
|
||||
o = oc.getTarget() |
|
||||
exists(Operator o | o = oc.getTarget() |
|
||||
o instanceof EQOperator and
|
||||
kind.isEquality() and
|
||||
first = oc.getArgument(0) and
|
||||
@@ -157,15 +152,16 @@ private newtype TComparisonTest =
|
||||
first = oc.getArgument(0) and
|
||||
second = oc.getArgument(1)
|
||||
)
|
||||
}
|
||||
or
|
||||
} or
|
||||
TCompareWithConstant(ComparisonTest outer, ComparisonKind kind, Expr first, Expr second) {
|
||||
exists(ComparisonKind outerKind, ComparisonTest compare, int i |
|
||||
compare.getComparisonKind().isCompare() and
|
||||
outerKind = outer.getComparisonKind() and
|
||||
outer.getAnArgument() = compare.getExpr() and
|
||||
i = outer.getAnArgument().getValue().toInt() |
|
||||
outerKind.isEquality() and (
|
||||
i = outer.getAnArgument().getValue().toInt()
|
||||
|
|
||||
outerKind.isEquality() and
|
||||
(
|
||||
// `x.CompareTo(y) == 0` => `x = y`
|
||||
i = 0 and
|
||||
kind.isEquality() and
|
||||
@@ -185,8 +181,10 @@ private newtype TComparisonTest =
|
||||
second = compare.getSecondArgument()
|
||||
)
|
||||
or
|
||||
outerKind.isLessThan() and (
|
||||
outer.getFirstArgument() = compare.getExpr() and (
|
||||
outerKind.isLessThan() and
|
||||
(
|
||||
outer.getFirstArgument() = compare.getExpr() and
|
||||
(
|
||||
// `x.CompareTo(y) < 1` => `x <= y`
|
||||
i = 1 and
|
||||
kind.isLessThanEquals() and
|
||||
@@ -200,7 +198,8 @@ private newtype TComparisonTest =
|
||||
second = compare.getSecondArgument()
|
||||
)
|
||||
or
|
||||
outer.getSecondArgument() = compare.getExpr() and (
|
||||
outer.getSecondArgument() = compare.getExpr() and
|
||||
(
|
||||
// `-1 < x.CompareTo(y)` => `x >= y`
|
||||
i = -1 and
|
||||
kind.isLessThanEquals() and
|
||||
@@ -215,8 +214,10 @@ private newtype TComparisonTest =
|
||||
)
|
||||
)
|
||||
or
|
||||
outerKind.isLessThanEquals() and (
|
||||
outer.getFirstArgument() = compare.getExpr() and (
|
||||
outerKind.isLessThanEquals() and
|
||||
(
|
||||
outer.getFirstArgument() = compare.getExpr() and
|
||||
(
|
||||
// `x.CompareTo(y) <= 0` => `x <= y`
|
||||
i = 0 and
|
||||
kind.isLessThanEquals() and
|
||||
@@ -230,7 +231,8 @@ private newtype TComparisonTest =
|
||||
second = compare.getSecondArgument()
|
||||
)
|
||||
or
|
||||
outer.getSecondArgument() = compare.getExpr() and (
|
||||
outer.getSecondArgument() = compare.getExpr() and
|
||||
(
|
||||
// `0 <= x.CompareTo(y)` => `x >= y`
|
||||
i = 0 and
|
||||
kind.isLessThanEquals() and
|
||||
@@ -262,12 +264,18 @@ class ComparisonTest extends TComparisonTest {
|
||||
|
||||
/** Gets the comparison kind. */
|
||||
final ComparisonKind getComparisonKind() {
|
||||
this = TComparisonOperation(_, result, _, _) or
|
||||
this = TEqualsCall(_) and result.isEquality() or
|
||||
this = TCompareToCall(_) and result.isCompare() or
|
||||
this = TStaticEqualsCall(_) and result.isEquality() or
|
||||
this = TCompareCall(_) and result.isCompare() or
|
||||
this = TComparisonOperatorCall(_, result, _, _) or
|
||||
this = TComparisonOperation(_, result, _, _)
|
||||
or
|
||||
this = TEqualsCall(_) and result.isEquality()
|
||||
or
|
||||
this = TCompareToCall(_) and result.isCompare()
|
||||
or
|
||||
this = TStaticEqualsCall(_) and result.isEquality()
|
||||
or
|
||||
this = TCompareCall(_) and result.isCompare()
|
||||
or
|
||||
this = TComparisonOperatorCall(_, result, _, _)
|
||||
or
|
||||
this = TCompareWithConstant(_, result, _, _)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,25 +4,26 @@
|
||||
* Provides functionality for validating that the database and library are in a
|
||||
* consistent state.
|
||||
*/
|
||||
|
||||
import csharp
|
||||
|
||||
private predicate missingLocation(Element e, string m) {
|
||||
(
|
||||
e instanceof Declaration
|
||||
or e instanceof Expr
|
||||
or e instanceof Stmt
|
||||
)
|
||||
and not e instanceof ImplicitAccessorParameter
|
||||
and not e instanceof NullType
|
||||
and not e instanceof Parameter // Bug in Roslyn - params occasionally lack locations
|
||||
and not e.(Operator).getDeclaringType() instanceof IntType // Roslyn quirk
|
||||
and not e instanceof Constructor
|
||||
and not e instanceof ArrayType
|
||||
and not e instanceof UnknownType
|
||||
and not e instanceof ArglistType
|
||||
and not exists(TupleType t | e=t or e=t.getAField())
|
||||
and not exists(e.getLocation())
|
||||
and m = "Element does not have a location"
|
||||
e instanceof Declaration or
|
||||
e instanceof Expr or
|
||||
e instanceof Stmt
|
||||
) and
|
||||
not e instanceof ImplicitAccessorParameter and
|
||||
not e instanceof NullType and
|
||||
not e instanceof Parameter and // Bug in Roslyn - params occasionally lack locations
|
||||
not e.(Operator).getDeclaringType() instanceof IntType and // Roslyn quirk
|
||||
not e instanceof Constructor and
|
||||
not e instanceof ArrayType and
|
||||
not e instanceof UnknownType and
|
||||
not e instanceof ArglistType and
|
||||
not exists(TupleType t | e = t or e = t.getAField()) and
|
||||
not exists(e.getLocation()) and
|
||||
m = "Element does not have a location"
|
||||
}
|
||||
|
||||
private predicate inconsistentGeneric(ConstructedGeneric g, string m) {
|
||||
@@ -40,21 +41,18 @@ module SsaChecks {
|
||||
import Ssa
|
||||
|
||||
predicate nonUniqueSsaDef(AssignableRead read, string m) {
|
||||
exists(ControlFlow::Node cfn |
|
||||
strictcount(Definition def | def.getAReadAtNode(cfn) = read) > 1
|
||||
)
|
||||
and
|
||||
exists(ControlFlow::Node cfn | strictcount(Definition def | def.getAReadAtNode(cfn) = read) > 1) and
|
||||
m = "Read is associated with multiple SSA definitions"
|
||||
}
|
||||
|
||||
predicate notDominatedByDef(AssignableRead read, string m) {
|
||||
exists(Definition def, BasicBlock bb, ControlFlow::Node rnode, ControlFlow::Node dnode, int i |
|
||||
def.getAReadAtNode(rnode) = read |
|
||||
def.getAReadAtNode(rnode) = read
|
||||
|
|
||||
def.definesAt(bb, i) and
|
||||
dnode = bb.getNode(max(int j | j = i or j = 0)) and
|
||||
not dnode.dominates(rnode)
|
||||
)
|
||||
and
|
||||
) and
|
||||
m = "Read is not dominated by SSA definition"
|
||||
}
|
||||
|
||||
@@ -63,14 +61,14 @@ module SsaChecks {
|
||||
// local variables should not have an SSA definition, as that would imply that
|
||||
// the declaration is live (can reach a use without passing through a definition)
|
||||
exists(ExplicitDefinition def |
|
||||
d = def.getADefinition().(AssignableDefinitions::LocalVariableDefinition).getDeclaration() |
|
||||
d = def.getADefinition().(AssignableDefinitions::LocalVariableDefinition).getDeclaration()
|
||||
|
|
||||
not d = any(ForeachStmt fs).getVariableDeclExpr() and
|
||||
not d = any(SpecificCatchClause scc).getVariableDeclExpr() and
|
||||
not d.getVariable().getType() instanceof Struct and
|
||||
not d = any(TypeCase ts).getVariableDeclExpr() and
|
||||
not d = any(IsPatternExpr ipe).getVariableDeclExpr()
|
||||
)
|
||||
and
|
||||
) and
|
||||
m = "Local variable declaration has unexpected SSA definition"
|
||||
}
|
||||
|
||||
@@ -83,8 +81,7 @@ module SsaChecks {
|
||||
|
||||
module CfgChecks {
|
||||
predicate multipleSuccessors(ControlFlowElement cfe, string m) {
|
||||
exists(ControlFlow::Node cfn |
|
||||
cfn = cfe.getAControlFlowNode() |
|
||||
exists(ControlFlow::Node cfn | cfn = cfe.getAControlFlowNode() |
|
||||
strictcount(cfn.getASuccessorByType(any(ControlFlow::SuccessorTypes::NormalSuccessor e))) > 1 and
|
||||
m = "Multiple (non-conditional/exceptional) successors"
|
||||
)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/** Provides logic for determining constant expressions. */
|
||||
|
||||
import csharp
|
||||
private import semmle.code.csharp.commons.ComparisonTest
|
||||
private import semmle.code.csharp.commons.StructuralComparison as StructuralComparison
|
||||
@@ -7,21 +8,25 @@ private import semmle.code.csharp.commons.StructuralComparison as StructuralComp
|
||||
* Holds if `e` is a condition that always evaluates to Boolean value `b`.
|
||||
*/
|
||||
predicate isConstantCondition(Expr e, boolean b) {
|
||||
forex(ControlFlow::Node cfn |
|
||||
cfn = e.getAControlFlowNode() |
|
||||
exists(cfn.getASuccessorByType(any(ControlFlow::SuccessorTypes::BooleanSuccessor t | t.getValue() = b))) and
|
||||
forex(ControlFlow::Node cfn | cfn = e.getAControlFlowNode() |
|
||||
exists(
|
||||
cfn
|
||||
.getASuccessorByType(any(ControlFlow::SuccessorTypes::BooleanSuccessor t |
|
||||
t.getValue() = b
|
||||
))
|
||||
) and
|
||||
strictcount(ControlFlow::SuccessorType t | exists(cfn.getASuccessorByType(t))) = 1
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if comparison operation `co` is constant with the Boolean value `b`.
|
||||
* For example, the comparison `x > x` is constantly `false` in
|
||||
*
|
||||
* ```
|
||||
* int MaxWrong(int x, int y) => x > x ? x : y;
|
||||
* ```
|
||||
*/
|
||||
* Holds if comparison operation `co` is constant with the Boolean value `b`.
|
||||
* For example, the comparison `x > x` is constantly `false` in
|
||||
*
|
||||
* ```
|
||||
* int MaxWrong(int x, int y) => x > x ? x : y;
|
||||
* ```
|
||||
*/
|
||||
predicate isConstantComparison(ComparisonOperation co, boolean b) {
|
||||
co.getValue() = "true" and
|
||||
b = true
|
||||
@@ -38,26 +43,18 @@ predicate isConstantComparison(ComparisonOperation co, boolean b) {
|
||||
private module ConstantComparisonOperation {
|
||||
private import semmle.code.csharp.commons.ComparisonTest
|
||||
|
||||
private SimpleType convertedType(Expr expr) {
|
||||
result = expr.stripImplicitCasts().getType()
|
||||
}
|
||||
private SimpleType convertedType(Expr expr) { result = expr.stripImplicitCasts().getType() }
|
||||
|
||||
private int maxValue(Expr expr) {
|
||||
if
|
||||
convertedType(expr) instanceof IntegralType and exists(expr.getValue())
|
||||
then
|
||||
result = expr.getValue().toInt()
|
||||
else
|
||||
result = convertedType(expr).maxValue()
|
||||
if convertedType(expr) instanceof IntegralType and exists(expr.getValue())
|
||||
then result = expr.getValue().toInt()
|
||||
else result = convertedType(expr).maxValue()
|
||||
}
|
||||
|
||||
private int minValue(Expr expr) {
|
||||
if
|
||||
convertedType(expr) instanceof IntegralType and exists(expr.getValue())
|
||||
then
|
||||
result = expr.getValue().toInt()
|
||||
else
|
||||
result = convertedType(expr).minValue()
|
||||
if convertedType(expr) instanceof IntegralType and exists(expr.getValue())
|
||||
then result = expr.getValue().toInt()
|
||||
else result = convertedType(expr).minValue()
|
||||
}
|
||||
|
||||
/** Holds if the comparison test `cmp` is constant with the value `value`. */
|
||||
@@ -66,7 +63,7 @@ private module ConstantComparisonOperation {
|
||||
e = cmp.getExpr() and
|
||||
l = cmp.getFirstArgument() and
|
||||
r = cmp.getSecondArgument()
|
||||
|
|
||||
|
|
||||
cmp.getComparisonKind().isLessThan() and
|
||||
maxValue(l) < minValue(r) and
|
||||
value = true
|
||||
@@ -85,13 +82,17 @@ private module ConstantComparisonOperation {
|
||||
or
|
||||
// Operands are unequal
|
||||
(maxValue(l) < minValue(r) or maxValue(r) < minValue(l)) and
|
||||
(cmp.getComparisonKind().isInequality() and value = true or cmp.getComparisonKind().isEquality() and value = false)
|
||||
(
|
||||
cmp.getComparisonKind().isInequality() and value = true
|
||||
or
|
||||
cmp.getComparisonKind().isEquality() and value = false
|
||||
)
|
||||
or
|
||||
exists(LocalScopeVariable v |
|
||||
l.(VariableRead).getTarget() = v and
|
||||
r.(VariableRead).getTarget() = v and
|
||||
not v.getType() instanceof FloatingPointType // One of the arguments may be NaN
|
||||
|
|
||||
|
|
||||
cmp.getComparisonKind().isLessThan() and value = false
|
||||
or
|
||||
cmp.getComparisonKind().isLessThanEquals() and value = true
|
||||
@@ -105,24 +106,19 @@ private module ConstantComparisonOperation {
|
||||
}
|
||||
|
||||
private class StructuralComparisonConfig extends StructuralComparison::StructuralComparisonConfiguration {
|
||||
StructuralComparisonConfig() {
|
||||
this = "CompareIdenticalValues"
|
||||
}
|
||||
StructuralComparisonConfig() { this = "CompareIdenticalValues" }
|
||||
|
||||
override predicate candidate(Element x, Element y) {
|
||||
exists(ComparisonTest ct |
|
||||
x = ct.getFirstArgument()
|
||||
and
|
||||
x = ct.getFirstArgument() and
|
||||
y = ct.getSecondArgument()
|
||||
)
|
||||
}
|
||||
|
||||
ComparisonTest getComparisonTest() {
|
||||
exists(Element x, Element y |
|
||||
result.getFirstArgument() = x
|
||||
and
|
||||
result.getSecondArgument() = y
|
||||
and
|
||||
result.getFirstArgument() = x and
|
||||
result.getSecondArgument() = y and
|
||||
same(x, y)
|
||||
)
|
||||
}
|
||||
@@ -144,20 +140,17 @@ predicate comparesIdenticalValues(ComparisonTest ct) {
|
||||
*/
|
||||
predicate comparesIdenticalValuesNan(ComparisonTest ct, string builtin) {
|
||||
comparesIdenticalValues(ct) and
|
||||
exists(FloatingPointType fpt, string type, string neg |
|
||||
fpt = ct.getAnArgument().getType() |
|
||||
exists(FloatingPointType fpt, string type, string neg | fpt = ct.getAnArgument().getType() |
|
||||
(
|
||||
fpt instanceof DoubleType and type = "double"
|
||||
or
|
||||
fpt instanceof FloatType and type = "float"
|
||||
)
|
||||
and
|
||||
) and
|
||||
(
|
||||
ct.getComparisonKind().isEquality() and neg = "!"
|
||||
or
|
||||
ct.getComparisonKind().isInequality() and neg = ""
|
||||
)
|
||||
and
|
||||
) and
|
||||
builtin = neg + type + ".IsNaN()"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -37,21 +37,24 @@ private predicate disposedCilVariable(CIL::Variable variable) {
|
||||
private predicate disposedCSharpVariable(Variable variable) {
|
||||
// A C# parameter is disposed if its corresponding CIL parameter is disposed
|
||||
exists(CIL::Method m, CIL::Parameter p, int i |
|
||||
disposedCilVariable(p) and p = m.getRawParameter(i) |
|
||||
disposedCilVariable(p) and p = m.getRawParameter(i)
|
||||
|
|
||||
variable = any(Callable c2 | c2.matchesHandle(m)).getRawParameter(i)
|
||||
)
|
||||
or
|
||||
// Call to a method that disposes it
|
||||
exists(Call call, int arg, VariableRead read |
|
||||
read.getTarget() = variable and
|
||||
read = call.getArgument(arg) |
|
||||
read = call.getArgument(arg)
|
||||
|
|
||||
disposedCSharpVariable(call.getTarget().getParameter(arg))
|
||||
)
|
||||
or
|
||||
// Call to `Dispose`
|
||||
exists(MethodCall call, VariableRead read |
|
||||
read.getTarget() = variable and
|
||||
read = call.getQualifier() |
|
||||
read = call.getQualifier()
|
||||
|
|
||||
isDisposeMethod(call.getTarget())
|
||||
)
|
||||
or
|
||||
@@ -60,8 +63,8 @@ private predicate disposedCSharpVariable(Variable variable) {
|
||||
or
|
||||
// A variable is disposed if it's assigned to another variable that is disposed
|
||||
exists(AssignableDefinition assign |
|
||||
assign.getSource() = variable.getAnAccess()
|
||||
and disposedCSharpVariable(assign.getTarget())
|
||||
assign.getSource() = variable.getAnAccess() and
|
||||
disposedCSharpVariable(assign.getTarget())
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,39 +6,28 @@ private import semmle.code.csharp.frameworks.system.codedom.Compiler
|
||||
/** A source file that contains generated code. */
|
||||
abstract class GeneratedCodeFile extends SourceFile { }
|
||||
|
||||
/** An auto-generated Designer file.*/
|
||||
/** An auto-generated Designer file. */
|
||||
class DesignerFile extends GeneratedCodeFile {
|
||||
DesignerFile() {
|
||||
this.getBaseName().toLowerCase().matches("%.designer.cs")
|
||||
}
|
||||
DesignerFile() { this.getBaseName().toLowerCase().matches("%.designer.cs") }
|
||||
}
|
||||
|
||||
/** An auto-generated SpecFlow feature file. */
|
||||
class FeatureFile extends GeneratedCodeFile {
|
||||
FeatureFile() {
|
||||
this.getBaseName().toLowerCase().matches("%.feature.cs")
|
||||
}
|
||||
FeatureFile() { this.getBaseName().toLowerCase().matches("%.feature.cs") }
|
||||
}
|
||||
|
||||
/** An auto-generated XAML code file. */
|
||||
class XamlFile extends GeneratedCodeFile {
|
||||
XamlFile() {
|
||||
this.getBaseName().matches("%.g.cs")
|
||||
}
|
||||
}
|
||||
class XamlFile extends GeneratedCodeFile { XamlFile() { this.getBaseName().matches("%.g.cs") } }
|
||||
|
||||
/** An auto-generated XSD code file. */
|
||||
class AutogenXsdFile extends GeneratedCodeFile {
|
||||
AutogenXsdFile() {
|
||||
this.getBaseName().matches("%.autogen.cs")
|
||||
}
|
||||
AutogenXsdFile() { this.getBaseName().matches("%.autogen.cs") }
|
||||
}
|
||||
|
||||
/** An auto-generated CodeDOM file. */
|
||||
class GeneratedAttributeFile extends GeneratedCodeFile {
|
||||
GeneratedAttributeFile() {
|
||||
exists(Attribute a |
|
||||
a.getType() instanceof SystemCodeDomCompilerGeneratedCodeAttributeClass |
|
||||
exists(Attribute a | a.getType() instanceof SystemCodeDomCompilerGeneratedCodeAttributeClass |
|
||||
a.getFile() = this
|
||||
)
|
||||
}
|
||||
@@ -46,20 +35,16 @@ class GeneratedAttributeFile extends GeneratedCodeFile {
|
||||
|
||||
/** A file auto-generated by `sgen.exe`. */
|
||||
class GeneratedNamespaceFile extends GeneratedCodeFile {
|
||||
GeneratedNamespaceFile()
|
||||
{
|
||||
exists(Namespace n |
|
||||
n.getATypeDeclaration().getFile() = this |
|
||||
GeneratedNamespaceFile() {
|
||||
exists(Namespace n | n.getATypeDeclaration().getFile() = this |
|
||||
n.getQualifiedName() = "Microsoft.Xml.Serialization.GeneratedAssembly"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A file contining comments suggesting it contains generated code.*/
|
||||
/** A file contining comments suggesting it contains generated code. */
|
||||
class GeneratedCommentFile extends GeneratedCodeFile {
|
||||
GeneratedCommentFile() {
|
||||
this = any(GeneratedCodeComment c).getLocation().getFile()
|
||||
}
|
||||
GeneratedCommentFile() { this = any(GeneratedCodeComment c).getLocation().getFile() }
|
||||
}
|
||||
|
||||
/** A comment line that indicates generated code. */
|
||||
@@ -70,12 +55,13 @@ abstract class GeneratedCodeComment extends CommentLine { }
|
||||
*/
|
||||
class GenericGeneratedCodeComment extends GeneratedCodeComment {
|
||||
GenericGeneratedCodeComment() {
|
||||
exists(string line, string entity, string was, string automatically |
|
||||
line = getText() |
|
||||
exists(string line, string entity, string was, string automatically | line = getText() |
|
||||
entity = "file|class|interface|art[ei]fact|module|script" and
|
||||
was = "was|is|has been" and
|
||||
automatically = "automatically |mechanically |auto[- ]?" and
|
||||
line.regexpMatch("(?i).*\\bThis (" + entity + ") (" + was + ") (" + automatically + ")?generated\\b.*")
|
||||
line
|
||||
.regexpMatch("(?i).*\\bThis (" + entity + ") (" + was + ") (" + automatically +
|
||||
")?generated\\b.*")
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -83,8 +69,7 @@ class GenericGeneratedCodeComment extends GeneratedCodeComment {
|
||||
/** A comment warning against modifications. */
|
||||
class DontModifyMarkerComment extends GeneratedCodeComment {
|
||||
DontModifyMarkerComment() {
|
||||
exists(string line |
|
||||
line = getText() |
|
||||
exists(string line | line = getText() |
|
||||
line.regexpMatch("(?i).*\\bGenerated by\\b.*\\bDo not edit\\b.*") or
|
||||
line.regexpMatch("(?i).*\\bAny modifications to this file will be lost\\b.*")
|
||||
)
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
/** Provides classes associated with logging frameworks. */
|
||||
|
||||
import csharp
|
||||
|
||||
/** A logger type that extends from an `ILogger` type. */
|
||||
class LoggerType extends RefType {
|
||||
LoggerType() {
|
||||
this.getABaseType*().hasName("ILogger")
|
||||
}
|
||||
}
|
||||
class LoggerType extends RefType { LoggerType() { this.getABaseType*().hasName("ILogger") } }
|
||||
|
||||
@@ -7,7 +7,6 @@ private import semmle.code.csharp.frameworks.Format
|
||||
private import semmle.code.csharp.frameworks.System
|
||||
private import semmle.code.csharp.frameworks.system.Text
|
||||
|
||||
|
||||
/**
|
||||
* An expression that appears in a context where an implicit `ToString()`
|
||||
* invocation will take place, unless the expression is already a string.
|
||||
@@ -26,23 +25,23 @@ class ImplicitToStringExpr extends Expr {
|
||||
ImplicitToStringExpr() {
|
||||
exists(Parameter p, Method m |
|
||||
this = getAnAssignedArgumentOrParam(p) and
|
||||
m = p.getCallable() |
|
||||
m = p.getCallable()
|
||||
|
|
||||
m = any(SystemTextStringBuilderClass c).getAMethod() and
|
||||
m.getName().regexpMatch("Append(Line)?")
|
||||
or
|
||||
p instanceof StringFormatItemParameter and
|
||||
not p.getType() = any(ArrayType at |
|
||||
at.getElementType() instanceof ObjectType and
|
||||
this.getType().isImplicitlyConvertibleTo(at)
|
||||
)
|
||||
at.getElementType() instanceof ObjectType and
|
||||
this.getType().isImplicitlyConvertibleTo(at)
|
||||
)
|
||||
or
|
||||
m = any(SystemClass c | c.hasName("Console")).getAMethod() and
|
||||
m.getName().regexpMatch("Write(Line)?") and
|
||||
p.getPosition() = 0
|
||||
)
|
||||
or
|
||||
exists(AddExpr add, Expr o |
|
||||
o = add.getAnOperand() |
|
||||
exists(AddExpr add, Expr o | o = add.getAnOperand() |
|
||||
o.stripImplicitCasts().getType() instanceof StringType and
|
||||
this = add.getOtherOperand(o)
|
||||
)
|
||||
@@ -55,8 +54,7 @@ private Expr getAnAssignedArgumentOrParam(Parameter p) {
|
||||
result = p.getAnAssignedArgument()
|
||||
or
|
||||
p.isParams() and
|
||||
exists(MethodCall mc, int i |
|
||||
mc.getTarget().getAParameter() = p |
|
||||
exists(MethodCall mc, int i | mc.getTarget().getAParameter() = p |
|
||||
result = mc.getArgument(i) and
|
||||
i >= p.getPosition()
|
||||
)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Provides functionality for performing structural comparison of
|
||||
* expressions and statements.
|
||||
*/
|
||||
|
||||
import csharp
|
||||
|
||||
/** Gets the declaration referenced by the expression `e`, if any. */
|
||||
@@ -21,10 +22,9 @@ private int elementKind(Element e) {
|
||||
}
|
||||
|
||||
private int getNumberOfActualChildren(Element e) {
|
||||
if e.(MemberAccess).targetIsThisInstance() then
|
||||
result = e.getNumberOfChildren() - 1
|
||||
else
|
||||
result = e.getNumberOfChildren()
|
||||
if e.(MemberAccess).targetIsThisInstance()
|
||||
then result = e.getNumberOfChildren() - 1
|
||||
else result = e.getNumberOfChildren()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,68 +61,53 @@ abstract class StructuralComparisonConfiguration extends string {
|
||||
(
|
||||
candidate(x, y)
|
||||
or
|
||||
exists(Element xParent, Element yParent, int i |
|
||||
candidateInternal(xParent, yParent) |
|
||||
hasChild(xParent, i, x)
|
||||
and
|
||||
exists(Element xParent, Element yParent, int i | candidateInternal(xParent, yParent) |
|
||||
hasChild(xParent, i, x) and
|
||||
hasChild(yParent, i, y)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma [noinline]
|
||||
private predicate hasChild(Element e, int i, Element child) {
|
||||
child = e.getChild(i)
|
||||
}
|
||||
pragma[noinline]
|
||||
private predicate hasChild(Element e, int i, Element child) { child = e.getChild(i) }
|
||||
|
||||
private predicate sameByValue(Expr x, Expr y) {
|
||||
sameByValueAux(x, y, y.getValue())
|
||||
}
|
||||
private predicate sameByValue(Expr x, Expr y) { sameByValueAux(x, y, y.getValue()) }
|
||||
|
||||
pragma [noinline]
|
||||
pragma[noinline]
|
||||
private predicate sameByValueAux(Expr x, Expr y, string value) {
|
||||
candidateInternal(x, y) and
|
||||
value = x.getValue()
|
||||
}
|
||||
|
||||
pragma [nomagic]
|
||||
pragma[nomagic]
|
||||
private predicate sameByStructure(Element x, Element y) {
|
||||
// At least one of `x` and `y` must not have a value, they must have
|
||||
// the same kind, and the same number of children
|
||||
sameByStructureCandidate(x, y, elementKind(y), getNumberOfActualChildren(y))
|
||||
and
|
||||
sameByStructureCandidate(x, y, elementKind(y), getNumberOfActualChildren(y)) and
|
||||
// If one of them has a reference attribute, they should both reference
|
||||
// the same node
|
||||
(exists(referenceAttribute(x)) implies
|
||||
referenceAttribute(x) = referenceAttribute(y))
|
||||
and
|
||||
(exists(referenceAttribute(x)) implies referenceAttribute(x) = referenceAttribute(y)) and
|
||||
// x is a member access on `this` iff y is
|
||||
(x.(MemberAccess).targetIsThisInstance() implies
|
||||
y.(MemberAccess).targetIsThisInstance())
|
||||
and
|
||||
(y.(MemberAccess).targetIsThisInstance() implies
|
||||
x.(MemberAccess).targetIsThisInstance())
|
||||
and
|
||||
(x.(MemberAccess).targetIsThisInstance() implies y.(MemberAccess).targetIsThisInstance()) and
|
||||
(y.(MemberAccess).targetIsThisInstance() implies x.(MemberAccess).targetIsThisInstance()) and
|
||||
// All of their corresponding children must be structurally equal
|
||||
forall(int i, Element xc |
|
||||
xc = x.getChild(i) and
|
||||
// exclude `this` qualifier, which has been checked above
|
||||
not (i = -1 and x.(MemberAccess).targetIsThisInstance()) |
|
||||
not (i = -1 and x.(MemberAccess).targetIsThisInstance())
|
||||
|
|
||||
sameInternal(xc, y.getChild(i))
|
||||
)
|
||||
}
|
||||
|
||||
private predicate sameByStructureCandidate(Element x, Element y, int elementKind, int children) {
|
||||
candidateInternal(x, y)
|
||||
and
|
||||
elementKind = elementKind(x)
|
||||
and
|
||||
children = getNumberOfActualChildren(x)
|
||||
and
|
||||
candidateInternal(x, y) and
|
||||
elementKind = elementKind(x) and
|
||||
children = getNumberOfActualChildren(x) and
|
||||
not (x.(Expr).hasValue() and y.(Expr).hasValue())
|
||||
}
|
||||
|
||||
pragma [nomagic]
|
||||
pragma[nomagic]
|
||||
private predicate sameInternal(Element x, Element y) {
|
||||
sameByValue(x, y)
|
||||
or
|
||||
@@ -135,8 +120,7 @@ abstract class StructuralComparisonConfiguration extends string {
|
||||
* `candidate(x, y)` must hold.
|
||||
*/
|
||||
predicate same(Element x, Element y) {
|
||||
candidate(x, y)
|
||||
and
|
||||
candidate(x, y) and
|
||||
sameInternal(x, y)
|
||||
}
|
||||
}
|
||||
@@ -188,68 +172,53 @@ module Internal {
|
||||
(
|
||||
candidate(x, y)
|
||||
or
|
||||
exists(Element xParent, Element yParent, int i |
|
||||
candidateInternal(xParent, yParent) |
|
||||
hasChild(xParent, i, x)
|
||||
and
|
||||
exists(Element xParent, Element yParent, int i | candidateInternal(xParent, yParent) |
|
||||
hasChild(xParent, i, x) and
|
||||
hasChild(yParent, i, y)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma [noinline]
|
||||
private predicate hasChild(Element e, int i, Element child) {
|
||||
child = e.getChild(i)
|
||||
}
|
||||
pragma[noinline]
|
||||
private predicate hasChild(Element e, int i, Element child) { child = e.getChild(i) }
|
||||
|
||||
private predicate sameByValue(Expr x, Expr y) {
|
||||
sameByValueAux(x, y, y.getValue())
|
||||
}
|
||||
private predicate sameByValue(Expr x, Expr y) { sameByValueAux(x, y, y.getValue()) }
|
||||
|
||||
pragma [noinline]
|
||||
pragma[noinline]
|
||||
private predicate sameByValueAux(Expr x, Expr y, string value) {
|
||||
candidateInternal(x, y) and
|
||||
value = x.getValue()
|
||||
}
|
||||
|
||||
pragma [nomagic]
|
||||
pragma[nomagic]
|
||||
private predicate sameByStructure(Element x, Element y) {
|
||||
// At least one of `x` and `y` must not have a value, they must have
|
||||
// the same kind, and the same number of children
|
||||
sameByStructureCandidate(x, y, elementKind(y), getNumberOfActualChildren(y))
|
||||
and
|
||||
sameByStructureCandidate(x, y, elementKind(y), getNumberOfActualChildren(y)) and
|
||||
// If one of them has a reference attribute, they should both reference
|
||||
// the same node
|
||||
(exists(referenceAttribute(x)) implies
|
||||
referenceAttribute(x) = referenceAttribute(y))
|
||||
and
|
||||
(exists(referenceAttribute(x)) implies referenceAttribute(x) = referenceAttribute(y)) and
|
||||
// x is a member access on `this` iff y is
|
||||
(x.(MemberAccess).targetIsThisInstance() implies
|
||||
y.(MemberAccess).targetIsThisInstance())
|
||||
and
|
||||
(y.(MemberAccess).targetIsThisInstance() implies
|
||||
x.(MemberAccess).targetIsThisInstance())
|
||||
and
|
||||
(x.(MemberAccess).targetIsThisInstance() implies y.(MemberAccess).targetIsThisInstance()) and
|
||||
(y.(MemberAccess).targetIsThisInstance() implies x.(MemberAccess).targetIsThisInstance()) and
|
||||
// All of their corresponding children must be structurally equal
|
||||
forall(int i, Element xc |
|
||||
xc = x.getChild(i) and
|
||||
// exclude `this` qualifier, which has been checked above
|
||||
not (i = -1 and x.(MemberAccess).targetIsThisInstance()) |
|
||||
not (i = -1 and x.(MemberAccess).targetIsThisInstance())
|
||||
|
|
||||
sameInternal(xc, y.getChild(i))
|
||||
)
|
||||
}
|
||||
|
||||
private predicate sameByStructureCandidate(Element x, Element y, int elementKind, int children) {
|
||||
candidateInternal(x, y)
|
||||
and
|
||||
elementKind = elementKind(x)
|
||||
and
|
||||
children = getNumberOfActualChildren(x)
|
||||
and
|
||||
candidateInternal(x, y) and
|
||||
elementKind = elementKind(x) and
|
||||
children = getNumberOfActualChildren(x) and
|
||||
not (x.(Expr).hasValue() and y.(Expr).hasValue())
|
||||
}
|
||||
|
||||
pragma [nomagic]
|
||||
pragma[nomagic]
|
||||
private predicate sameInternal(Element x, Element y) {
|
||||
sameByValue(x, y)
|
||||
or
|
||||
@@ -262,8 +231,7 @@ module Internal {
|
||||
* `candidate(x, y)` must hold.
|
||||
*/
|
||||
predicate same(Element x, Element y) {
|
||||
candidate(x, y)
|
||||
and
|
||||
candidate(x, y) and
|
||||
sameInternal(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,9 +25,7 @@ class TargetFrameworkAttribute extends Attribute {
|
||||
* ```
|
||||
* is `".NETFramework,Version=v4.6.1"`.
|
||||
*/
|
||||
string getFrameworkName() {
|
||||
result = this.getArgument(0).getValue()
|
||||
}
|
||||
string getFrameworkName() { result = this.getArgument(0).getValue() }
|
||||
|
||||
private string frameworkCapture(int n) {
|
||||
result = this.getFrameworkName().regexpCapture("([^,]+),Version=v(.+)", n)
|
||||
@@ -40,9 +38,7 @@ class TargetFrameworkAttribute extends Attribute {
|
||||
* ```
|
||||
* is `".NETFramework"`. Other framework types include `".NETStandard"` and `".NETCoreApp"`.
|
||||
*/
|
||||
string getFrameworkType() {
|
||||
result = this.frameworkCapture(1)
|
||||
}
|
||||
string getFrameworkType() { result = this.frameworkCapture(1) }
|
||||
|
||||
/**
|
||||
* Gets the framework version of this attribute. For example, the framework version of
|
||||
@@ -52,29 +48,20 @@ class TargetFrameworkAttribute extends Attribute {
|
||||
* is `"4.6.1"`. Note that you can use the `Version` class to compare versions, for example
|
||||
* `target.getFrameworkVersion().isEarlierThan("4.6")`.
|
||||
*/
|
||||
Version getFrameworkVersion() {
|
||||
result = this.frameworkCapture(2)
|
||||
}
|
||||
Version getFrameworkVersion() { result = this.frameworkCapture(2) }
|
||||
|
||||
/** Holds if the framework type is .NET Desktop. */
|
||||
predicate isNetFramework() {
|
||||
this.getFrameworkType() = ".NETFramework"
|
||||
}
|
||||
predicate isNetFramework() { this.getFrameworkType() = ".NETFramework" }
|
||||
|
||||
/** Holds if the framework type is .NET Standard. */
|
||||
predicate isNetStandard() {
|
||||
this.getFrameworkType() = ".NETStandard" }
|
||||
predicate isNetStandard() { this.getFrameworkType() = ".NETStandard" }
|
||||
|
||||
/** Holds if the framework type is .NET Core. */
|
||||
predicate isNetCore() {
|
||||
this.getFrameworkType() = ".NETCoreApp"
|
||||
}
|
||||
predicate isNetCore() { this.getFrameworkType() = ".NETCoreApp" }
|
||||
|
||||
/**
|
||||
* Holds if element `e` specifies this target version, because it is
|
||||
* compiled in the assembly of this attribute.
|
||||
*/
|
||||
predicate hasElement(Element e) {
|
||||
assembly = e.getALocation()
|
||||
}
|
||||
predicate hasElement(Element e) { assembly = e.getALocation() }
|
||||
}
|
||||
|
||||
@@ -8,9 +8,8 @@ class MainMethod extends Method {
|
||||
this.hasName("Main") and
|
||||
this.isStatic() and
|
||||
(this.getReturnType() instanceof VoidType or this.getReturnType() instanceof IntType) and
|
||||
if this.getNumberOfParameters() = 1 then
|
||||
this.getParameter(0).getType().(ArrayType).getElementType() instanceof StringType
|
||||
else
|
||||
this.getNumberOfParameters() = 0
|
||||
if this.getNumberOfParameters() = 1
|
||||
then this.getParameter(0).getType().(ArrayType).getElementType() instanceof StringType
|
||||
else this.getNumberOfParameters() = 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,9 +11,7 @@ private import ControlFlow::SuccessorTypes
|
||||
*/
|
||||
class BasicBlock extends TBasicBlockStart {
|
||||
/** Gets an immediate successor of this basic block, if any. */
|
||||
BasicBlock getASuccessor() {
|
||||
result.getFirstNode() = getLastNode().getASuccessor()
|
||||
}
|
||||
BasicBlock getASuccessor() { result.getFirstNode() = getLastNode().getASuccessor() }
|
||||
|
||||
/** Gets an immediate successor of this basic block of a given type, if any. */
|
||||
BasicBlock getASuccessorByType(ControlFlow::SuccessorType t) {
|
||||
@@ -21,9 +19,7 @@ class BasicBlock extends TBasicBlockStart {
|
||||
}
|
||||
|
||||
/** Gets an immediate predecessor of this basic block, if any. */
|
||||
BasicBlock getAPredecessor() {
|
||||
result.getASuccessor() = this
|
||||
}
|
||||
BasicBlock getAPredecessor() { result.getASuccessor() = this }
|
||||
|
||||
/** Gets an immediate predecessor of this basic block of a given type, if any. */
|
||||
BasicBlock getAPredecessorByType(ControlFlow::SuccessorType t) {
|
||||
@@ -46,9 +42,7 @@ class BasicBlock extends TBasicBlockStart {
|
||||
* The basic block on line 2 is an immediate `true` successor of the
|
||||
* basic block on line 1.
|
||||
*/
|
||||
BasicBlock getATrueSuccessor() {
|
||||
result.getFirstNode() = getLastNode().getATrueSuccessor()
|
||||
}
|
||||
BasicBlock getATrueSuccessor() { result.getFirstNode() = getLastNode().getATrueSuccessor() }
|
||||
|
||||
/**
|
||||
* Gets an immediate `false` successor, if any.
|
||||
@@ -66,9 +60,7 @@ class BasicBlock extends TBasicBlockStart {
|
||||
* The basic block on line 2 is an immediate `false` successor of the
|
||||
* basic block on line 1.
|
||||
*/
|
||||
BasicBlock getAFalseSuccessor() {
|
||||
result.getFirstNode() = getLastNode().getAFalseSuccessor()
|
||||
}
|
||||
BasicBlock getAFalseSuccessor() { result.getFirstNode() = getLastNode().getAFalseSuccessor() }
|
||||
|
||||
/**
|
||||
* Gets an immediate `null` successor, if any.
|
||||
@@ -86,8 +78,7 @@ class BasicBlock extends TBasicBlockStart {
|
||||
* The node on line 2 is an immediate `null` successor of the node
|
||||
* `x` on line 1.
|
||||
*/
|
||||
deprecated
|
||||
BasicBlock getANullSuccessor() {
|
||||
deprecated BasicBlock getANullSuccessor() {
|
||||
result.getFirstNode() = getLastNode().getANullSuccessor()
|
||||
}
|
||||
|
||||
@@ -106,8 +97,7 @@ class BasicBlock extends TBasicBlockStart {
|
||||
* The node `x?.M()`, representing the call to `M`, is a non-`null` successor
|
||||
* of the node `x`.
|
||||
*/
|
||||
deprecated
|
||||
BasicBlock getANonNullSuccessor() {
|
||||
deprecated BasicBlock getANonNullSuccessor() {
|
||||
result.getFirstNode() = getLastNode().getANonNullSuccessor()
|
||||
}
|
||||
|
||||
@@ -115,29 +105,19 @@ class BasicBlock extends TBasicBlockStart {
|
||||
ControlFlow::Node getNode(int pos) { bbIndex(getFirstNode(), result, pos) }
|
||||
|
||||
/** Gets a control flow node in this basic block. */
|
||||
ControlFlow::Node getANode() {
|
||||
result = getNode(_)
|
||||
}
|
||||
ControlFlow::Node getANode() { result = getNode(_) }
|
||||
|
||||
/** Gets the first control flow node in this basic block. */
|
||||
ControlFlow::Node getFirstNode() {
|
||||
this = TBasicBlockStart(result)
|
||||
}
|
||||
ControlFlow::Node getFirstNode() { this = TBasicBlockStart(result) }
|
||||
|
||||
/** Gets the last control flow node in this basic block. */
|
||||
ControlFlow::Node getLastNode() {
|
||||
result = getNode(length() - 1)
|
||||
}
|
||||
ControlFlow::Node getLastNode() { result = getNode(length() - 1) }
|
||||
|
||||
/** Gets the callable that this basic block belongs to. */
|
||||
Callable getCallable() {
|
||||
result = this.getAPredecessor().getCallable()
|
||||
}
|
||||
Callable getCallable() { result = this.getAPredecessor().getCallable() }
|
||||
|
||||
/** Gets the length of this basic block. */
|
||||
int length() {
|
||||
result = strictcount(getANode())
|
||||
}
|
||||
int length() { result = strictcount(getANode()) }
|
||||
|
||||
/**
|
||||
* Holds if this basic block immediately dominates basic block `bb`.
|
||||
@@ -160,9 +140,7 @@ class BasicBlock extends TBasicBlockStart {
|
||||
* basic block on line 4 (all paths from the entry point of `M`
|
||||
* to `return s.Length;` must go through the null check).
|
||||
*/
|
||||
predicate immediatelyDominates(BasicBlock bb) {
|
||||
bbIDominates(this, bb)
|
||||
}
|
||||
predicate immediatelyDominates(BasicBlock bb) { bbIDominates(this, bb) }
|
||||
|
||||
/**
|
||||
* Holds if this basic block strictly dominates basic block `bb`.
|
||||
@@ -185,9 +163,7 @@ class BasicBlock extends TBasicBlockStart {
|
||||
* basic block on line 4 (all paths from the entry point of `M`
|
||||
* to `return s.Length;` must go through the null check).
|
||||
*/
|
||||
predicate strictlyDominates(BasicBlock bb) {
|
||||
bbIDominates+(this, bb)
|
||||
}
|
||||
predicate strictlyDominates(BasicBlock bb) { bbIDominates+(this, bb) }
|
||||
|
||||
/**
|
||||
* Holds if this basic block dominates basic block `bb`.
|
||||
@@ -247,10 +223,7 @@ class BasicBlock extends TBasicBlockStart {
|
||||
/**
|
||||
* Holds if this basic block dominates a predecessor of `df`.
|
||||
*/
|
||||
private
|
||||
predicate dominatesPredecessor(BasicBlock df) {
|
||||
dominates(df.getAPredecessor())
|
||||
}
|
||||
private predicate dominatesPredecessor(BasicBlock df) { dominates(df.getAPredecessor()) }
|
||||
|
||||
/**
|
||||
* Gets the basic block that immediately dominates this basic block, if any.
|
||||
@@ -274,9 +247,7 @@ class BasicBlock extends TBasicBlockStart {
|
||||
* to `return s.Length;` must go through the null check, and the null check
|
||||
* is an immediate predecessor of `return s.Length;`).
|
||||
*/
|
||||
BasicBlock getImmediateDominator() {
|
||||
bbIDominates(result, this)
|
||||
}
|
||||
BasicBlock getImmediateDominator() { bbIDominates(result, this) }
|
||||
|
||||
/**
|
||||
* Holds if this basic block strictly post-dominates basic block `bb`.
|
||||
@@ -302,9 +273,7 @@ class BasicBlock extends TBasicBlockStart {
|
||||
* line 3 (all paths to the exit point of `M` from `return s.Length;`
|
||||
* must go through the `WriteLine` call).
|
||||
*/
|
||||
predicate strictlyPostDominates(BasicBlock bb) {
|
||||
bbIPostDominates+(this, bb)
|
||||
}
|
||||
predicate strictlyPostDominates(BasicBlock bb) { bbIPostDominates+(this, bb) }
|
||||
|
||||
/**
|
||||
* Holds if this basic block post-dominates basic block `bb`.
|
||||
@@ -343,28 +312,23 @@ class BasicBlock extends TBasicBlockStart {
|
||||
* even if this basic block is syntactically inside a `while` loop if the
|
||||
* necessary back edges are unreachable.
|
||||
*/
|
||||
predicate inLoop() {
|
||||
this.getASuccessor+() = this
|
||||
}
|
||||
predicate inLoop() { this.getASuccessor+() = this }
|
||||
|
||||
/** Gets a textual representation of this basic block. */
|
||||
string toString() {
|
||||
result = getFirstNode().toString()
|
||||
}
|
||||
string toString() { result = getFirstNode().toString() }
|
||||
|
||||
/** Gets the location of this basic block. */
|
||||
Location getLocation() {
|
||||
result = getFirstNode().getLocation()
|
||||
}
|
||||
Location getLocation() { result = getFirstNode().getLocation() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal implementation details.
|
||||
*/
|
||||
private cached module Internal {
|
||||
cached
|
||||
private module Internal {
|
||||
/** Internal representation of basic blocks. */
|
||||
cached newtype TBasicBlock =
|
||||
TBasicBlockStart(ControlFlow::Node cfn) { startsBB(cfn) }
|
||||
cached
|
||||
newtype TBasicBlock = TBasicBlockStart(ControlFlow::Node cfn) { startsBB(cfn) }
|
||||
|
||||
/** Holds if `cfn` starts a new basic block. */
|
||||
private predicate startsBB(ControlFlow::Node cfn) {
|
||||
@@ -372,7 +336,9 @@ private cached module Internal {
|
||||
or
|
||||
cfn.isJoin()
|
||||
or
|
||||
exists(ControlFlow::Node pred | pred = cfn.getAPredecessor() | strictcount(pred.getASuccessor()) > 1)
|
||||
exists(ControlFlow::Node pred | pred = cfn.getAPredecessor() |
|
||||
strictcount(pred.getASuccessor()) > 1
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -398,9 +364,7 @@ private cached module Internal {
|
||||
* Holds if the first node of basic block `succ` is a control flow
|
||||
* successor of the last node of basic block `pred`.
|
||||
*/
|
||||
private predicate succBB(BasicBlock pred, BasicBlock succ) {
|
||||
succ = pred.getASuccessor()
|
||||
}
|
||||
private predicate succBB(BasicBlock pred, BasicBlock succ) { succ = pred.getASuccessor() }
|
||||
|
||||
/** Holds if `dom` is an immediate dominator of `bb`. */
|
||||
cached
|
||||
@@ -408,9 +372,7 @@ private cached module Internal {
|
||||
idominance(entryBB/1, succBB/2)(_, dom, bb)
|
||||
|
||||
/** Holds if `pred` is a basic block predecessor of `succ`. */
|
||||
private predicate predBB(BasicBlock succ, BasicBlock pred) {
|
||||
succBB(pred, succ)
|
||||
}
|
||||
private predicate predBB(BasicBlock succ, BasicBlock pred) { succBB(pred, succ) }
|
||||
|
||||
/** Holds if `dom` is an immediate post-dominator of `bb`. */
|
||||
cached
|
||||
@@ -426,9 +388,7 @@ private import Internal
|
||||
class EntryBasicBlock extends BasicBlock {
|
||||
EntryBasicBlock() { entryBB(this) }
|
||||
|
||||
override Callable getCallable() {
|
||||
result.getEntryPoint() = this.getFirstNode()
|
||||
}
|
||||
override Callable getCallable() { result.getEntryPoint() = this.getFirstNode() }
|
||||
}
|
||||
|
||||
/** Holds if `bb` is an entry basic block. */
|
||||
@@ -440,32 +400,22 @@ private predicate entryBB(BasicBlock bb) {
|
||||
* An exit basic block, that is, a basic block whose last node is
|
||||
* the exit node of a callable.
|
||||
*/
|
||||
class ExitBasicBlock extends BasicBlock {
|
||||
ExitBasicBlock() { exitBB(this) }
|
||||
}
|
||||
class ExitBasicBlock extends BasicBlock { ExitBasicBlock() { exitBB(this) } }
|
||||
|
||||
/** Holds if `bb` is an exit basic block. */
|
||||
private predicate exitBB(BasicBlock bb) {
|
||||
bb.getLastNode() instanceof ControlFlow::Nodes::ExitNode
|
||||
}
|
||||
private predicate exitBB(BasicBlock bb) { bb.getLastNode() instanceof ControlFlow::Nodes::ExitNode }
|
||||
|
||||
/** A basic block with more than one predecessor. */
|
||||
class JoinBlock extends BasicBlock {
|
||||
JoinBlock() { getFirstNode().isJoin() }
|
||||
}
|
||||
class JoinBlock extends BasicBlock { JoinBlock() { getFirstNode().isJoin() } }
|
||||
|
||||
/** A basic block that is an immediate predecessor of a join block. */
|
||||
class JoinBlockPredecessor extends BasicBlock {
|
||||
JoinBlockPredecessor() {
|
||||
this.getASuccessor() instanceof JoinBlock
|
||||
}
|
||||
JoinBlockPredecessor() { this.getASuccessor() instanceof JoinBlock }
|
||||
}
|
||||
|
||||
/** A basic block that terminates in a condition, splitting the subsequent control flow. */
|
||||
class ConditionBlock extends BasicBlock {
|
||||
ConditionBlock() {
|
||||
this.getLastNode().isCondition()
|
||||
}
|
||||
ConditionBlock() { this.getLastNode().isCondition() }
|
||||
|
||||
/**
|
||||
* Holds if basic block `succ` is immediately controlled by this basic
|
||||
@@ -476,10 +426,7 @@ class ConditionBlock extends BasicBlock {
|
||||
pragma[nomagic]
|
||||
predicate immediatelyControls(BasicBlock succ, ConditionalSuccessor s) {
|
||||
succ = this.getASuccessorByType(s) and
|
||||
forall(BasicBlock pred |
|
||||
pred = succ.getAPredecessor() and pred != this |
|
||||
succ.dominates(pred)
|
||||
)
|
||||
forall(BasicBlock pred | pred = succ.getAPredecessor() and pred != this | succ.dominates(pred))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -521,10 +468,8 @@ class ConditionBlock extends BasicBlock {
|
||||
* that `this` strictly dominates `controlled` so that isn't necessary to check
|
||||
* directly.
|
||||
*/
|
||||
exists(BasicBlock succ |
|
||||
this.immediatelyControls(succ, s) |
|
||||
succ.dominates(controlled)
|
||||
)
|
||||
|
||||
exists(BasicBlock succ | this.immediatelyControls(succ, s) | succ.dominates(controlled))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -533,8 +478,7 @@ class ConditionBlock extends BasicBlock {
|
||||
* the callable entry point by going via the `null` edge (`isNull = true`)
|
||||
* or non-`null` edge (`isNull = false`) out of this basic block.
|
||||
*/
|
||||
deprecated
|
||||
predicate controlsNullness(BasicBlock controlled, boolean isNull) {
|
||||
deprecated predicate controlsNullness(BasicBlock controlled, boolean isNull) {
|
||||
this.controls(controlled, any(NullnessSuccessor s | s.getValue() = isNull))
|
||||
}
|
||||
|
||||
@@ -573,8 +517,9 @@ class ConditionBlock extends BasicBlock {
|
||||
//
|
||||
// In the former case, `x` and `y` control `A`, in the latter case
|
||||
// only `x & y` controls `A` if we do not take sub conditions into account.
|
||||
deprecated
|
||||
predicate controlsSubCond(BasicBlock controlled, boolean testIsTrue, Expr cond, boolean condIsTrue) {
|
||||
deprecated predicate controlsSubCond(
|
||||
BasicBlock controlled, boolean testIsTrue, Expr cond, boolean condIsTrue
|
||||
) {
|
||||
impliesSub(getLastNode().getElement(), cond, testIsTrue, condIsTrue) and
|
||||
controls(controlled, any(BooleanSuccessor s | s.getValue() = testIsTrue))
|
||||
}
|
||||
@@ -584,22 +529,23 @@ class ConditionBlock extends BasicBlock {
|
||||
* Holds if `e2` is a sub expression of (Boolean) expression `e1`, and
|
||||
* if `e1` has value `b1` then `e2` must have value `b2`.
|
||||
*/
|
||||
deprecated
|
||||
private predicate impliesSub(Expr e1, Expr e2, boolean b1, boolean b2) {
|
||||
if e1 instanceof LogicalNotExpr then (
|
||||
impliesSub(e1.(LogicalNotExpr).getOperand(), e2, b1.booleanNot(), b2)
|
||||
)
|
||||
else if e1 instanceof BitwiseAndExpr or e1 instanceof LogicalAndExpr then (
|
||||
impliesSub(e1.(BinaryOperation).getAnOperand(), e2, b1, b2) and
|
||||
b1 = true
|
||||
)
|
||||
else if e1 instanceof BitwiseOrExpr or e1 instanceof LogicalOrExpr then (
|
||||
impliesSub(e1.(BinaryOperation).getAnOperand(), e2, b1, b2) and
|
||||
b1 = false
|
||||
) else (
|
||||
e1.getType() instanceof BoolType and
|
||||
e1 = e2 and
|
||||
b1 = b2 and
|
||||
(b1 = true or b1 = false)
|
||||
)
|
||||
deprecated private predicate impliesSub(Expr e1, Expr e2, boolean b1, boolean b2) {
|
||||
if e1 instanceof LogicalNotExpr
|
||||
then impliesSub(e1.(LogicalNotExpr).getOperand(), e2, b1.booleanNot(), b2)
|
||||
else
|
||||
if e1 instanceof BitwiseAndExpr or e1 instanceof LogicalAndExpr
|
||||
then
|
||||
impliesSub(e1.(BinaryOperation).getAnOperand(), e2, b1, b2) and
|
||||
b1 = true
|
||||
else
|
||||
if e1 instanceof BitwiseOrExpr or e1 instanceof LogicalOrExpr
|
||||
then (
|
||||
impliesSub(e1.(BinaryOperation).getAnOperand(), e2, b1, b2) and
|
||||
b1 = false
|
||||
) else (
|
||||
e1.getType() instanceof BoolType and
|
||||
e1 = e2 and
|
||||
b1 = b2 and
|
||||
(b1 = true or b1 = false)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -25,41 +25,22 @@ private import semmle.code.csharp.frameworks.System
|
||||
|
||||
// Internal representation of completions
|
||||
private newtype TCompletion =
|
||||
TNormalCompletion()
|
||||
or
|
||||
TNormalCompletion() or
|
||||
TBooleanCompletion(boolean outerValue, boolean innerValue) {
|
||||
(outerValue = true or outerValue = false) and
|
||||
(innerValue = true or innerValue = false)
|
||||
}
|
||||
or
|
||||
TNullnessCompletion(boolean isNull) {
|
||||
isNull = true or isNull = false
|
||||
}
|
||||
or
|
||||
TMatchingCompletion(boolean isMatch) {
|
||||
isMatch = true or isMatch = false
|
||||
}
|
||||
or
|
||||
TEmptinessCompletion(boolean isEmpty) {
|
||||
isEmpty = true or isEmpty = false
|
||||
}
|
||||
or
|
||||
TReturnCompletion()
|
||||
or
|
||||
TBreakCompletion()
|
||||
or
|
||||
TBreakNormalCompletion()
|
||||
or
|
||||
TContinueCompletion()
|
||||
or
|
||||
TGotoLabelCompletion(GotoLabelStmt goto)
|
||||
or
|
||||
TGotoCaseCompletion(GotoCaseStmt goto)
|
||||
or
|
||||
TGotoDefaultCompletion()
|
||||
or
|
||||
TThrowCompletion(ExceptionClass ec)
|
||||
or
|
||||
} or
|
||||
TNullnessCompletion(boolean isNull) { isNull = true or isNull = false } or
|
||||
TMatchingCompletion(boolean isMatch) { isMatch = true or isMatch = false } or
|
||||
TEmptinessCompletion(boolean isEmpty) { isEmpty = true or isEmpty = false } or
|
||||
TReturnCompletion() or
|
||||
TBreakCompletion() or
|
||||
TBreakNormalCompletion() or
|
||||
TContinueCompletion() or
|
||||
TGotoLabelCompletion(GotoLabelStmt goto) or
|
||||
TGotoCaseCompletion(GotoCaseStmt goto) or
|
||||
TGotoDefaultCompletion() or
|
||||
TThrowCompletion(ExceptionClass ec) or
|
||||
TExitCompletion()
|
||||
|
||||
/**
|
||||
@@ -81,9 +62,9 @@ class Completion extends TCompletion {
|
||||
predicate isValidFor(ControlFlowElement cfe) {
|
||||
this.(ThrowCompletion).getExceptionClass() = cfe.(TriedControlFlowElement).getAThrownException()
|
||||
or
|
||||
if mustHaveBooleanCompletion(cfe) then
|
||||
exists(boolean value |
|
||||
isBooleanConstant(cfe, value) |
|
||||
if mustHaveBooleanCompletion(cfe)
|
||||
then
|
||||
exists(boolean value | isBooleanConstant(cfe, value) |
|
||||
this = TBooleanCompletion(value, value)
|
||||
)
|
||||
or
|
||||
@@ -95,26 +76,24 @@ class Completion extends TCompletion {
|
||||
// non-`null` completion)
|
||||
mustHaveNullnessCompletion(cfe) and
|
||||
this = TNullnessCompletion(true)
|
||||
else if mustHaveNullnessCompletion(cfe) then
|
||||
exists(boolean value |
|
||||
isNullnessConstant(cfe, value) |
|
||||
this = TNullnessCompletion(value)
|
||||
)
|
||||
or
|
||||
not isNullnessConstant(cfe, _) and
|
||||
this = TNullnessCompletion(_)
|
||||
else if mustHaveMatchingCompletion(cfe) then
|
||||
exists(boolean value |
|
||||
isMatchingConstant(cfe, value) |
|
||||
this = TMatchingCompletion(value)
|
||||
)
|
||||
or
|
||||
not isMatchingConstant(cfe, _) and
|
||||
this = TMatchingCompletion(_)
|
||||
else if mustHaveEmptinessCompletion(cfe) then
|
||||
this = TEmptinessCompletion(_)
|
||||
else
|
||||
this = TNormalCompletion()
|
||||
if mustHaveNullnessCompletion(cfe)
|
||||
then
|
||||
exists(boolean value | isNullnessConstant(cfe, value) | this = TNullnessCompletion(value))
|
||||
or
|
||||
not isNullnessConstant(cfe, _) and
|
||||
this = TNullnessCompletion(_)
|
||||
else
|
||||
if mustHaveMatchingCompletion(cfe)
|
||||
then
|
||||
exists(boolean value | isMatchingConstant(cfe, value) | this = TMatchingCompletion(value))
|
||||
or
|
||||
not isMatchingConstant(cfe, _) and
|
||||
this = TMatchingCompletion(_)
|
||||
else
|
||||
if mustHaveEmptinessCompletion(cfe)
|
||||
then this = TEmptinessCompletion(_)
|
||||
else this = TNormalCompletion()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -147,14 +126,13 @@ private predicate isBooleanConstant(Expr e, boolean value) {
|
||||
*/
|
||||
private predicate isNullnessConstant(Expr e, boolean value) {
|
||||
mustHaveNullnessCompletion(e) and
|
||||
exists(Expr stripped |
|
||||
stripped = e.stripCasts() |
|
||||
exists(Expr stripped | stripped = e.stripCasts() |
|
||||
stripped.getType() = any(ValueType t |
|
||||
not t instanceof NullableType and
|
||||
// Extractor bug: the type of `x?.Length` is reported as `int`, but it should
|
||||
// be `int?`
|
||||
not getQualifier*(stripped).(QualifiableExpr).isConditional()
|
||||
) and
|
||||
not t instanceof NullableType and
|
||||
// Extractor bug: the type of `x?.Length` is reported as `int`, but it should
|
||||
// be `int?`
|
||||
not getQualifier*(stripped).(QualifiableExpr).isConditional()
|
||||
) and
|
||||
value = false
|
||||
or
|
||||
stripped instanceof NullLiteral and
|
||||
@@ -176,22 +154,17 @@ private Expr getQualifier(QualifiableExpr e) {
|
||||
* non-matches (`value = false`).
|
||||
*/
|
||||
private predicate isMatchingConstant(Expr e, boolean value) {
|
||||
exists(SwitchStmt ss |
|
||||
mustHaveMatchingCompletion(ss, e) |
|
||||
exists(Expr stripped |
|
||||
stripped = ss.getCondition().stripCasts() |
|
||||
exists(SwitchStmt ss | mustHaveMatchingCompletion(ss, e) |
|
||||
exists(Expr stripped | stripped = ss.getCondition().stripCasts() |
|
||||
exists(ConstCase cc, string strippedValue |
|
||||
cc = ss.getAConstCase() and
|
||||
e = cc.getExpr() and
|
||||
strippedValue = stripped.getValue() |
|
||||
if strippedValue = e.getValue() then
|
||||
value = true
|
||||
else
|
||||
value = false
|
||||
strippedValue = stripped.getValue()
|
||||
|
|
||||
if strippedValue = e.getValue() then value = true else value = false
|
||||
)
|
||||
or
|
||||
exists(TypeCase tc, Type t, Type strippedType |
|
||||
tc = ss.getATypeCase() |
|
||||
exists(TypeCase tc, Type t, Type strippedType | tc = ss.getATypeCase() |
|
||||
e = tc.getTypeAccess() and
|
||||
t = e.getType() and
|
||||
strippedType = stripped.getType() and
|
||||
@@ -207,36 +180,34 @@ private predicate isMatchingConstant(Expr e, boolean value) {
|
||||
|
||||
/** A control flow element that is inside a `try` block. */
|
||||
private class TriedControlFlowElement extends ControlFlowElement {
|
||||
TriedControlFlowElement() {
|
||||
this = any(TryStmt try).getATriedElement()
|
||||
}
|
||||
TriedControlFlowElement() { this = any(TryStmt try).getATriedElement() }
|
||||
|
||||
/**
|
||||
* Gets an exception class that is potentially thrown by this element, if any.
|
||||
*/
|
||||
ExceptionClass getAThrownException() {
|
||||
this = any(UnaryOperation uo |
|
||||
not uo instanceof UnaryBitwiseOperation and
|
||||
uo.getType() instanceof IntegralType and
|
||||
result instanceof SystemOverflowExceptionClass
|
||||
)
|
||||
not uo instanceof UnaryBitwiseOperation and
|
||||
uo.getType() instanceof IntegralType and
|
||||
result instanceof SystemOverflowExceptionClass
|
||||
)
|
||||
or
|
||||
this = any(CastExpr ce |
|
||||
ce.getType() instanceof IntegralType and
|
||||
result instanceof SystemOverflowExceptionClass
|
||||
or
|
||||
invalidCastCandidate(ce) and
|
||||
result instanceof SystemInvalidCastExceptionClass
|
||||
)
|
||||
ce.getType() instanceof IntegralType and
|
||||
result instanceof SystemOverflowExceptionClass
|
||||
or
|
||||
invalidCastCandidate(ce) and
|
||||
result instanceof SystemInvalidCastExceptionClass
|
||||
)
|
||||
or
|
||||
this instanceof Call and
|
||||
result instanceof SystemExceptionClass
|
||||
or
|
||||
this = any(MemberAccess ma |
|
||||
not ma.isConditional() and
|
||||
ma.hasQualifier() and
|
||||
result instanceof SystemNullReferenceExceptionClass
|
||||
)
|
||||
not ma.isConditional() and
|
||||
ma.hasQualifier() and
|
||||
result instanceof SystemNullReferenceExceptionClass
|
||||
)
|
||||
or
|
||||
this instanceof DelegateCreation and
|
||||
result instanceof SystemOutOfMemoryExceptionClass
|
||||
@@ -245,27 +216,27 @@ private class TriedControlFlowElement extends ControlFlowElement {
|
||||
result instanceof SystemOutOfMemoryExceptionClass
|
||||
or
|
||||
this = any(AddExpr ae |
|
||||
ae.getType() instanceof StringType and
|
||||
result instanceof SystemOutOfMemoryExceptionClass
|
||||
or
|
||||
ae.getType() instanceof IntegralType and
|
||||
result instanceof SystemOverflowExceptionClass
|
||||
)
|
||||
ae.getType() instanceof StringType and
|
||||
result instanceof SystemOutOfMemoryExceptionClass
|
||||
or
|
||||
ae.getType() instanceof IntegralType and
|
||||
result instanceof SystemOverflowExceptionClass
|
||||
)
|
||||
or
|
||||
this = any(SubExpr se |
|
||||
se.getType() instanceof IntegralType and
|
||||
result instanceof SystemOverflowExceptionClass
|
||||
)
|
||||
se.getType() instanceof IntegralType and
|
||||
result instanceof SystemOverflowExceptionClass
|
||||
)
|
||||
or
|
||||
this = any(MulExpr me |
|
||||
me.getType() instanceof IntegralType and
|
||||
result instanceof SystemOverflowExceptionClass
|
||||
)
|
||||
me.getType() instanceof IntegralType and
|
||||
result instanceof SystemOverflowExceptionClass
|
||||
)
|
||||
or
|
||||
this = any(DivExpr de |
|
||||
not de.getDenominator().getValue().toInt() != 0 and
|
||||
result instanceof SystemDivideByZeroExceptionClass
|
||||
)
|
||||
not de.getDenominator().getValue().toInt() != 0 and
|
||||
result instanceof SystemDivideByZeroExceptionClass
|
||||
)
|
||||
or
|
||||
this instanceof RemExpr and
|
||||
result instanceof SystemDivideByZeroExceptionClass
|
||||
@@ -278,7 +249,7 @@ private class TriedControlFlowElement extends ControlFlowElement {
|
||||
}
|
||||
}
|
||||
|
||||
pragma [noinline]
|
||||
pragma[noinline]
|
||||
private predicate invalidCastCandidate(CastExpr ce) {
|
||||
ce.getType() = ce.getExpr().getType().(ValueOrRefType).getASubType+()
|
||||
}
|
||||
@@ -301,28 +272,15 @@ private predicate mustHaveBooleanCompletion(Expr e) {
|
||||
* `B` is the Boolean completion of `A && B`.
|
||||
*/
|
||||
private predicate inBooleanContext(Expr e, boolean isBooleanCompletionForParent) {
|
||||
exists(IfStmt is |
|
||||
is.getCondition() = e |
|
||||
isBooleanCompletionForParent = false
|
||||
)
|
||||
exists(IfStmt is | is.getCondition() = e | isBooleanCompletionForParent = false)
|
||||
or
|
||||
exists(LoopStmt ls |
|
||||
ls.getCondition() = e |
|
||||
isBooleanCompletionForParent = false
|
||||
)
|
||||
exists(LoopStmt ls | ls.getCondition() = e | isBooleanCompletionForParent = false)
|
||||
or
|
||||
exists(CaseStmt cs |
|
||||
cs.getCondition() = e |
|
||||
isBooleanCompletionForParent = false
|
||||
)
|
||||
exists(CaseStmt cs | cs.getCondition() = e | isBooleanCompletionForParent = false)
|
||||
or
|
||||
exists(SpecificCatchClause scc |
|
||||
scc.getFilterClause() = e |
|
||||
isBooleanCompletionForParent = false
|
||||
)
|
||||
exists(SpecificCatchClause scc | scc.getFilterClause() = e | isBooleanCompletionForParent = false)
|
||||
or
|
||||
exists(LogicalNotExpr lne |
|
||||
lne.getAnOperand() = e |
|
||||
exists(LogicalNotExpr lne | lne.getAnOperand() = e |
|
||||
inBooleanContext(lne, _) and
|
||||
isBooleanCompletionForParent = true
|
||||
)
|
||||
@@ -354,8 +312,7 @@ private predicate inBooleanContext(Expr e, boolean isBooleanCompletionForParent)
|
||||
isBooleanCompletionForParent = true
|
||||
)
|
||||
or
|
||||
exists(NullCoalescingExpr nce |
|
||||
nce.getAnOperand() = e |
|
||||
exists(NullCoalescingExpr nce | nce.getAnOperand() = e |
|
||||
inBooleanContext(nce, _) and
|
||||
isBooleanCompletionForParent = true
|
||||
)
|
||||
@@ -379,25 +336,19 @@ private predicate mustHaveNullnessCompletion(Expr e) {
|
||||
* is the nullness completion of `A ?? B`.
|
||||
*/
|
||||
private predicate inNullnessContext(Expr e, boolean isNullnessCompletionForParent) {
|
||||
exists(NullCoalescingExpr nce |
|
||||
e = nce.getLeftOperand() |
|
||||
isNullnessCompletionForParent = false
|
||||
)
|
||||
exists(NullCoalescingExpr nce | e = nce.getLeftOperand() | isNullnessCompletionForParent = false)
|
||||
or
|
||||
exists(QualifiableExpr qe |
|
||||
qe.isConditional() |
|
||||
exists(QualifiableExpr qe | qe.isConditional() |
|
||||
e = qe.getChildExpr(-1) and
|
||||
isNullnessCompletionForParent = false
|
||||
)
|
||||
or
|
||||
exists(ConditionalExpr ce |
|
||||
inNullnessContext(ce, _) |
|
||||
exists(ConditionalExpr ce | inNullnessContext(ce, _) |
|
||||
(e = ce.getThen() or e = ce.getElse()) and
|
||||
isNullnessCompletionForParent = true
|
||||
)
|
||||
or
|
||||
exists(NullCoalescingExpr nce |
|
||||
inNullnessContext(nce, _) |
|
||||
exists(NullCoalescingExpr nce | inNullnessContext(nce, _) |
|
||||
e = nce.getRightOperand() and
|
||||
isNullnessCompletionForParent = true
|
||||
)
|
||||
@@ -442,9 +393,7 @@ predicate foreachEmptiness(ForeachStmt fs, ControlFlowElement cfe) {
|
||||
* Holds if a normal completion of `cfe` must be an emptiness completion. Thats is,
|
||||
* whether `cfe` determines whether to execute the body of a `foreach` statement.
|
||||
*/
|
||||
private predicate mustHaveEmptinessCompletion(ControlFlowElement cfe) {
|
||||
foreachEmptiness(_, cfe)
|
||||
}
|
||||
private predicate mustHaveEmptinessCompletion(ControlFlowElement cfe) { foreachEmptiness(_, cfe) }
|
||||
|
||||
/**
|
||||
* A completion that represents normal evaluation of a statement or an
|
||||
@@ -456,9 +405,7 @@ abstract class NormalCompletion extends Completion { }
|
||||
* A class to make `TNormalCompletion` a `NormalCompletion`
|
||||
*/
|
||||
class SimpleCompletion extends NormalCompletion, TNormalCompletion {
|
||||
override string toString() {
|
||||
result = "normal"
|
||||
}
|
||||
override string toString() { result = "normal" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -480,20 +427,14 @@ class BooleanCompletion extends ConditionalCompletion, TBooleanCompletion {
|
||||
/** Gets the value that this expression completes with. */
|
||||
boolean getOuterValue() { this = TBooleanCompletion(result, _) }
|
||||
|
||||
override string toString() {
|
||||
result = getOuterValue() + "/" + getInnerValue()
|
||||
}
|
||||
override string toString() { result = getOuterValue() + "/" + getInnerValue() }
|
||||
}
|
||||
|
||||
/** A Boolean `true` completion. */
|
||||
class TrueCompletion extends BooleanCompletion {
|
||||
TrueCompletion() { getOuterValue() = true }
|
||||
}
|
||||
class TrueCompletion extends BooleanCompletion { TrueCompletion() { getOuterValue() = true } }
|
||||
|
||||
/** A Boolean `false` completion. */
|
||||
class FalseCompletion extends BooleanCompletion {
|
||||
FalseCompletion() { getOuterValue() = false }
|
||||
}
|
||||
class FalseCompletion extends BooleanCompletion { FalseCompletion() { getOuterValue() = false } }
|
||||
|
||||
/**
|
||||
* A completion that represents evaluation of an expression that is either
|
||||
@@ -503,12 +444,7 @@ class NullnessCompletion extends ConditionalCompletion, TNullnessCompletion {
|
||||
/** Holds if the last sub expression of this expression evaluates to `null`. */
|
||||
predicate isNull() { this = TNullnessCompletion(true) }
|
||||
|
||||
override string toString() {
|
||||
if this.isNull() then
|
||||
result = "null"
|
||||
else
|
||||
result = "non-null"
|
||||
}
|
||||
override string toString() { if this.isNull() then result = "null" else result = "non-null" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -519,12 +455,7 @@ class MatchingCompletion extends ConditionalCompletion, TMatchingCompletion {
|
||||
/** Holds if there is a match. */
|
||||
predicate isMatch() { this = TMatchingCompletion(true) }
|
||||
|
||||
override string toString() {
|
||||
if this.isMatch() then
|
||||
result = "match"
|
||||
else
|
||||
result = "no-match"
|
||||
}
|
||||
override string toString() { if this.isMatch() then result = "match" else result = "no-match" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -535,12 +466,7 @@ class EmptinessCompletion extends ConditionalCompletion, TEmptinessCompletion {
|
||||
/** Holds if the emptiness test evaluates to `true`. */
|
||||
predicate isEmpty() { this = TEmptinessCompletion(true) }
|
||||
|
||||
override string toString() {
|
||||
if this.isEmpty() then
|
||||
result = "empty"
|
||||
else
|
||||
result = "non-empty"
|
||||
}
|
||||
override string toString() { if this.isEmpty() then result = "empty" else result = "non-empty" }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,9 +28,7 @@ class ControlFlowElement extends ExprOrStmtParent, @control_flow_element {
|
||||
* several `ControlFlow::Node`s, for example to represent the continuation
|
||||
* flow in a `try/catch/finally` construction.
|
||||
*/
|
||||
Node getAControlFlowNode() {
|
||||
result.getElement() = this
|
||||
}
|
||||
Node getAControlFlowNode() { result.getElement() = this }
|
||||
|
||||
/**
|
||||
* Gets a first control flow node executed within this element.
|
||||
@@ -50,14 +48,10 @@ class ControlFlowElement extends ExprOrStmtParent, @control_flow_element {
|
||||
* Holds if this element is live, that is this element can be reached
|
||||
* from the entry point of its enclosing callable.
|
||||
*/
|
||||
predicate isLive() {
|
||||
exists(this.getAControlFlowNode())
|
||||
}
|
||||
predicate isLive() { exists(this.getAControlFlowNode()) }
|
||||
|
||||
/** Holds if the current element is reachable from `src`. */
|
||||
predicate reachableFrom(ControlFlowElement src) {
|
||||
this = src.getAReachableElement()
|
||||
}
|
||||
predicate reachableFrom(ControlFlowElement src) { this = src.getAReachableElement() }
|
||||
|
||||
/** Gets an element that is reachable from this element. */
|
||||
ControlFlowElement getAReachableElement() {
|
||||
@@ -96,11 +90,11 @@ class ControlFlowElement extends ExprOrStmtParent, @control_flow_element {
|
||||
// Only calculate dominance by explicit recursion for split nodes;
|
||||
// all other nodes can use regular CFG dominance
|
||||
this instanceof ControlFlow::Internal::SplitControlFlowElement and
|
||||
exists(ConditionBlock cb |
|
||||
cb.getLastNode() = this.getAControlFlowNode() |
|
||||
exists(ConditionBlock cb | cb.getLastNode() = this.getAControlFlowNode() |
|
||||
succ = cb.getASuccessorByType(s) and
|
||||
forall(BasicBlock pred, SuccessorType t |
|
||||
pred = succ.getAPredecessorByType(t) and pred != cb |
|
||||
forall(BasicBlock pred, SuccessorType t |
|
||||
pred = succ.getAPredecessorByType(t) and pred != cb
|
||||
|
|
||||
succ.dominates(pred)
|
||||
or
|
||||
// `pred` might be another split of `cfe`
|
||||
@@ -112,9 +106,10 @@ class ControlFlowElement extends ExprOrStmtParent, @control_flow_element {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private JoinBlockPredecessor getAPossiblyControlledPredecessor(JoinBlock controlled, ConditionalSuccessor s) {
|
||||
exists(BasicBlock mid |
|
||||
this.immediatelyControlsBlockSplit(mid, s) |
|
||||
private JoinBlockPredecessor getAPossiblyControlledPredecessor(
|
||||
JoinBlock controlled, ConditionalSuccessor s
|
||||
) {
|
||||
exists(BasicBlock mid | this.immediatelyControlsBlockSplit(mid, s) |
|
||||
result = mid.getASuccessor*()
|
||||
) and
|
||||
result.getASuccessor() = controlled and
|
||||
@@ -124,8 +119,7 @@ class ControlFlowElement extends ExprOrStmtParent, @control_flow_element {
|
||||
pragma[nomagic]
|
||||
private predicate isPossiblyControlledJoinBlock(JoinBlock controlled, ConditionalSuccessor s) {
|
||||
exists(this.getAPossiblyControlledPredecessor(controlled, s)) and
|
||||
forall(BasicBlock pred |
|
||||
pred = controlled.getAPredecessor() |
|
||||
forall(BasicBlock pred | pred = controlled.getAPredecessor() |
|
||||
pred = this.getAPossiblyControlledPredecessor(controlled, s)
|
||||
or
|
||||
controlled.dominates(pred)
|
||||
@@ -136,14 +130,13 @@ class ControlFlowElement extends ExprOrStmtParent, @control_flow_element {
|
||||
private predicate controlsBlockSplit(BasicBlock controlled, ConditionalSuccessor s) {
|
||||
this.immediatelyControlsBlockSplit(controlled, s)
|
||||
or
|
||||
if controlled instanceof JoinBlock then
|
||||
if controlled instanceof JoinBlock
|
||||
then
|
||||
this.isPossiblyControlledJoinBlock(controlled, s) and
|
||||
forall(BasicBlock pred |
|
||||
pred = this.getAPossiblyControlledPredecessor(controlled, s) |
|
||||
forall(BasicBlock pred | pred = this.getAPossiblyControlledPredecessor(controlled, s) |
|
||||
this.controlsBlock(pred, s)
|
||||
)
|
||||
else
|
||||
this.controlsBlockSplit(controlled.getAPredecessor(), s)
|
||||
else this.controlsBlockSplit(controlled.getAPredecessor(), s)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -166,8 +159,7 @@ class ControlFlowElement extends ExprOrStmtParent, @control_flow_element {
|
||||
predicate controlsBlock(BasicBlock controlled, ConditionalSuccessor s) {
|
||||
this.controlsBlockSplit(controlled, s)
|
||||
or
|
||||
exists(ConditionBlock cb |
|
||||
cb.getLastNode() = this.getAControlFlowNode() |
|
||||
exists(ConditionBlock cb | cb.getLastNode() = this.getAControlFlowNode() |
|
||||
cb.controls(controlled, s)
|
||||
)
|
||||
}
|
||||
@@ -188,10 +180,10 @@ class ControlFlowElement extends ExprOrStmtParent, @control_flow_element {
|
||||
*
|
||||
* as control flow splitting is taken into account.
|
||||
*/
|
||||
pragma[inline] // potentially very large predicate, so must be inlined
|
||||
// potentially very large predicate, so must be inlined
|
||||
pragma[inline]
|
||||
predicate controlsElement(ControlFlowElement controlled, ConditionalSuccessor s) {
|
||||
forex(BasicBlock bb |
|
||||
bb = controlled.getAControlFlowNode().getBasicBlock() |
|
||||
forex(BasicBlock bb | bb = controlled.getAControlFlowNode().getBasicBlock() |
|
||||
this.controlsBlock(bb, s)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -7,17 +7,11 @@ private import semmle.code.csharp.dataflow.DelegateDataFlow
|
||||
private import dotnet
|
||||
|
||||
// Internal representation of call contexts
|
||||
cached private newtype TCallContext =
|
||||
TEmptyCallContext()
|
||||
or
|
||||
TArgCallContext(DotNet::Call c, int i) {
|
||||
exists(c.getArgument(i))
|
||||
}
|
||||
or
|
||||
TDynamicAccessorArgCallContext(DynamicAccessorCall dac, int i) {
|
||||
exists(dac.getArgument(i))
|
||||
}
|
||||
or
|
||||
cached
|
||||
private newtype TCallContext =
|
||||
TEmptyCallContext() or
|
||||
TArgCallContext(DotNet::Call c, int i) { exists(c.getArgument(i)) } or
|
||||
TDynamicAccessorArgCallContext(DynamicAccessorCall dac, int i) { exists(dac.getArgument(i)) } or
|
||||
TDelegateToLibraryCallableArgCallContext(DelegateArgumentToLibraryCallable arg, int i) {
|
||||
exists(arg.getDelegateType().getParameter(i))
|
||||
}
|
||||
@@ -55,47 +49,37 @@ abstract class ArgumentCallContext extends CallContext {
|
||||
/** An argument of an ordinary call. */
|
||||
class CallArgumentCallContext extends ArgumentCallContext, TArgCallContext {
|
||||
DotNet::Call c;
|
||||
|
||||
int arg;
|
||||
|
||||
CallArgumentCallContext() {
|
||||
this = TArgCallContext(c, arg)
|
||||
}
|
||||
CallArgumentCallContext() { this = TArgCallContext(c, arg) }
|
||||
|
||||
override predicate isArgument(DotNet::Expr call, int i) {
|
||||
call = c and
|
||||
i = arg
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = c.getArgument(arg).toString()
|
||||
}
|
||||
override string toString() { result = c.getArgument(arg).toString() }
|
||||
|
||||
override Location getLocation() {
|
||||
result = c.getArgument(arg).getLocation()
|
||||
}
|
||||
override Location getLocation() { result = c.getArgument(arg).getLocation() }
|
||||
}
|
||||
|
||||
/** An argument of a dynamic accessor call. */
|
||||
class DynamicAccessorArgumentCallContext extends ArgumentCallContext, TDynamicAccessorArgCallContext {
|
||||
DynamicAccessorCall dac;
|
||||
|
||||
int arg;
|
||||
|
||||
DynamicAccessorArgumentCallContext() {
|
||||
this = TDynamicAccessorArgCallContext(dac, arg)
|
||||
}
|
||||
DynamicAccessorArgumentCallContext() { this = TDynamicAccessorArgCallContext(dac, arg) }
|
||||
|
||||
override predicate isArgument(DotNet::Expr call, int i) {
|
||||
call = dac and
|
||||
i = arg
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = dac.getArgument(arg).toString()
|
||||
}
|
||||
override string toString() { result = dac.getArgument(arg).toString() }
|
||||
|
||||
override Location getLocation() {
|
||||
result = dac.getArgument(arg).getLocation()
|
||||
}
|
||||
override Location getLocation() { result = dac.getArgument(arg).getLocation() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -107,8 +91,10 @@ class DynamicAccessorArgumentCallContext extends ArgumentCallContext, TDynamicAc
|
||||
* in the database, so the delegate argument `y => y` is used to
|
||||
* represent the call.
|
||||
*/
|
||||
class DelegateArgumentToLibraryCallableArgumentContext extends ArgumentCallContext, TDelegateToLibraryCallableArgCallContext {
|
||||
class DelegateArgumentToLibraryCallableArgumentContext extends ArgumentCallContext,
|
||||
TDelegateToLibraryCallableArgCallContext {
|
||||
DotNet::Expr delegate;
|
||||
|
||||
int arg;
|
||||
|
||||
DelegateArgumentToLibraryCallableArgumentContext() {
|
||||
@@ -120,11 +106,7 @@ class DelegateArgumentToLibraryCallableArgumentContext extends ArgumentCallConte
|
||||
i = arg
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "argument " + arg + " of " + delegate.toString()
|
||||
}
|
||||
override string toString() { result = "argument " + arg + " of " + delegate.toString() }
|
||||
|
||||
override Location getLocation() {
|
||||
result = delegate.getLocation()
|
||||
}
|
||||
override Location getLocation() { result = delegate.getLocation() }
|
||||
}
|
||||
|
||||
@@ -16,19 +16,17 @@ private class DelegateFlowSource extends DataFlow::ExprNode {
|
||||
|
||||
DelegateFlowSource() {
|
||||
this.getExpr() = any(Expr e |
|
||||
c = e.(AnonymousFunctionExpr) or
|
||||
c = e.(CallableAccess).getTarget().getSourceDeclaration()
|
||||
)
|
||||
c = e.(AnonymousFunctionExpr) or
|
||||
c = e.(CallableAccess).getTarget().getSourceDeclaration()
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the callable that is referenced in this delegate flow source. */
|
||||
Callable getCallable() {
|
||||
result = c
|
||||
}
|
||||
Callable getCallable() { result = c }
|
||||
}
|
||||
|
||||
/** A sink of flow for a delegate expression. */
|
||||
private abstract class DelegateFlowSink extends DataFlow::ExprNode {
|
||||
abstract private class DelegateFlowSink extends DataFlow::ExprNode {
|
||||
/**
|
||||
* Gets an actual run-time target of this delegate call in the given call
|
||||
* context, if any. The call context records the *last* call required to
|
||||
@@ -93,19 +91,16 @@ private abstract class DelegateFlowSink extends DataFlow::ExprNode {
|
||||
library class DelegateCallExpr extends DelegateFlowSink {
|
||||
DelegateCall dc;
|
||||
|
||||
DelegateCallExpr() {
|
||||
this.getExpr() = dc.getDelegateExpr()
|
||||
}
|
||||
DelegateCallExpr() { this.getExpr() = dc.getDelegateExpr() }
|
||||
|
||||
/** Gets the delegate call that this expression belongs to. */
|
||||
DelegateCall getDelegateCall() {
|
||||
result = dc
|
||||
}
|
||||
DelegateCall getDelegateCall() { result = dc }
|
||||
}
|
||||
|
||||
/** A delegate expression that is passed as the argument to a library callable. */
|
||||
library class DelegateArgumentToLibraryCallable extends Expr {
|
||||
DelegateType dt;
|
||||
|
||||
Call call;
|
||||
|
||||
DelegateArgumentToLibraryCallable() {
|
||||
@@ -118,14 +113,10 @@ library class DelegateArgumentToLibraryCallable extends Expr {
|
||||
}
|
||||
|
||||
/** Gets the call that this argument belongs to. */
|
||||
Call getCall() {
|
||||
result = call
|
||||
}
|
||||
Call getCall() { result = call }
|
||||
|
||||
/** Gets the delegate type of this argument. */
|
||||
DelegateType getDelegateType() {
|
||||
result = dt
|
||||
}
|
||||
DelegateType getDelegateType() { result = dt }
|
||||
|
||||
/**
|
||||
* Gets an actual run-time target of this delegate call in the given call
|
||||
@@ -133,8 +124,7 @@ library class DelegateArgumentToLibraryCallable extends Expr {
|
||||
* resolve the target, if any. Example:
|
||||
*/
|
||||
Callable getARuntimeTarget(CallContext context) {
|
||||
exists(DelegateArgumentToLibraryCallableSink sink |
|
||||
sink.getExpr() = this |
|
||||
exists(DelegateArgumentToLibraryCallableSink sink | sink.getExpr() = this |
|
||||
result = sink.getARuntimeTarget(context)
|
||||
)
|
||||
}
|
||||
@@ -151,14 +141,10 @@ private class DelegateArgumentToLibraryCallableSink extends DelegateFlowSink {
|
||||
library class AddEventSource extends DelegateFlowSink {
|
||||
AddEventExpr ae;
|
||||
|
||||
AddEventSource() {
|
||||
this.getExpr() = ae.getRValue()
|
||||
}
|
||||
AddEventSource() { this.getExpr() = ae.getRValue() }
|
||||
|
||||
/** Gets the event that this delegate is added to. */
|
||||
Event getEvent() {
|
||||
result = ae.getTarget()
|
||||
}
|
||||
Event getEvent() { result = ae.getTarget() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -169,15 +155,16 @@ library class AddEventSource extends DelegateFlowSink {
|
||||
* `node` goes through a returned expression. The call context `lastCall`
|
||||
* records the last call on the path from `node` to `sink`, if any.
|
||||
*/
|
||||
private predicate flowsFrom(DelegateFlowSink sink, DataFlow::Node node, boolean isReturned, CallContext lastCall) {
|
||||
private predicate flowsFrom(
|
||||
DelegateFlowSink sink, DataFlow::Node node, boolean isReturned, CallContext lastCall
|
||||
) {
|
||||
// Base case
|
||||
sink = node and
|
||||
isReturned = false and
|
||||
lastCall instanceof EmptyCallContext
|
||||
or
|
||||
// Local flow
|
||||
exists(DataFlow::Node mid |
|
||||
flowsFrom(sink, mid, isReturned, lastCall) |
|
||||
exists(DataFlow::Node mid | flowsFrom(sink, mid, isReturned, lastCall) |
|
||||
DataFlow::localFlowStep(node, mid) or
|
||||
node.asExpr() = mid.asExpr().(DelegateCreation).getArgument()
|
||||
)
|
||||
@@ -191,7 +178,9 @@ private predicate flowsFrom(DelegateFlowSink sink, DataFlow::Node node, boolean
|
||||
)
|
||||
or
|
||||
// Flow into a callable (non-delegate call)
|
||||
exists(DataFlow::Internal::ExplicitParameterNode mid, CallContext prevLastCall, Expr call, Parameter p |
|
||||
exists(
|
||||
DataFlow::Internal::ExplicitParameterNode mid, CallContext prevLastCall, Expr call, Parameter p
|
||||
|
|
||||
flowsFrom(sink, mid, isReturned, prevLastCall) and
|
||||
isReturned = false and
|
||||
p = mid.getParameter() and
|
||||
@@ -200,7 +189,10 @@ private predicate flowsFrom(DelegateFlowSink sink, DataFlow::Node node, boolean
|
||||
)
|
||||
or
|
||||
// Flow into a callable (delegate call)
|
||||
exists(DataFlow::Internal::ExplicitParameterNode mid, CallContext prevLastCall, DelegateCall dc, Callable c, Parameter p, int i |
|
||||
exists(
|
||||
DataFlow::Internal::ExplicitParameterNode mid, CallContext prevLastCall, DelegateCall dc,
|
||||
Callable c, Parameter p, int i
|
||||
|
|
||||
flowsFrom(sink, mid, isReturned, prevLastCall) and
|
||||
isReturned = false and
|
||||
flowIntoDelegateCall(dc, c, node.asExpr(), i) and
|
||||
@@ -229,7 +221,7 @@ private predicate flowsFrom(DelegateFlowSink sink, DataFlow::Node node, boolean
|
||||
* `prevLastCall` is the previous last call, so the result is the
|
||||
* previous call if it exists, otherwise `call` is the last call.
|
||||
*/
|
||||
bindingset [call, i]
|
||||
bindingset[call, i]
|
||||
private CallContext getLastCall(CallContext prevLastCall, Expr call, int i) {
|
||||
prevLastCall instanceof EmptyCallContext and
|
||||
result.(ArgumentCallContext).isArgument(call, i)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -50,9 +50,9 @@ class AlwaysNullExpr extends Expr {
|
||||
this instanceof NullLiteral
|
||||
or
|
||||
this = any(ConditionalExpr ce |
|
||||
ce.getThen() instanceof AlwaysNullExpr and
|
||||
ce.getElse() instanceof AlwaysNullExpr
|
||||
)
|
||||
ce.getThen() instanceof AlwaysNullExpr and
|
||||
ce.getElse() instanceof AlwaysNullExpr
|
||||
)
|
||||
or
|
||||
this.(AssignExpr).getRValue() instanceof AlwaysNullExpr
|
||||
or
|
||||
@@ -65,9 +65,7 @@ class NonNullExpr extends Expr {
|
||||
NonNullExpr() {
|
||||
G::Internal::nonNullValue(this)
|
||||
or
|
||||
exists(NonNullExpr mid |
|
||||
G::Internal::nonNullValueImplied(mid, this)
|
||||
)
|
||||
exists(NonNullExpr mid | G::Internal::nonNullValueImplied(mid, this))
|
||||
or
|
||||
this instanceof G::NullGuardedExpr
|
||||
or
|
||||
@@ -79,17 +77,16 @@ class NonNullExpr extends Expr {
|
||||
private predicate nonNullDef(Ssa::Definition v) {
|
||||
v.(Ssa::ExplicitDefinition).getADefinition().getSource() instanceof NonNullExpr
|
||||
or
|
||||
exists(AssignableDefinition ad |
|
||||
ad = v.(Ssa::ExplicitDefinition).getADefinition() |
|
||||
exists(AssignableDefinition ad | ad = v.(Ssa::ExplicitDefinition).getADefinition() |
|
||||
ad instanceof AssignableDefinitions::IsPatternDefinition
|
||||
or
|
||||
ad instanceof AssignableDefinitions::TypeCasePatternDefinition
|
||||
or
|
||||
ad = any(AssignableDefinitions::LocalVariableDefinition d |
|
||||
d.getExpr() = any(SpecificCatchClause scc).getVariableDeclExpr()
|
||||
or
|
||||
d.getExpr() = any(ForeachStmt fs).getAVariableDeclExpr()
|
||||
)
|
||||
d.getExpr() = any(SpecificCatchClause scc).getVariableDeclExpr()
|
||||
or
|
||||
d.getExpr() = any(ForeachStmt fs).getAVariableDeclExpr()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -105,9 +102,10 @@ private predicate dereferenceAt(BasicBlock bb, int i, Ssa::Definition def, Deref
|
||||
* Holds if `e` having abstract value `vExpr` implies that SSA definition `def`
|
||||
* has abstract value `vDef`.
|
||||
*/
|
||||
private predicate exprImpliesSsaDef(Expr e, G::AbstractValue vExpr, Ssa::Definition def, G::AbstractValue vDef) {
|
||||
exists(G::Internal::Guard g |
|
||||
G::Internal::impliesSteps(e, vExpr, g, vDef) |
|
||||
private predicate exprImpliesSsaDef(
|
||||
Expr e, G::AbstractValue vExpr, Ssa::Definition def, G::AbstractValue vDef
|
||||
) {
|
||||
exists(G::Internal::Guard g | G::Internal::impliesSteps(e, vExpr, g, vDef) |
|
||||
g = def.getARead()
|
||||
or
|
||||
g = def.(Ssa::ExplicitDefinition).getADefinition().getTargetAccess()
|
||||
@@ -120,7 +118,8 @@ private predicate exprImpliesSsaDef(Expr e, G::AbstractValue vExpr, Ssa::Definit
|
||||
*/
|
||||
private predicate ensureNotNullAt(BasicBlock bb, int i, Ssa::Definition def) {
|
||||
exists(Expr e, G::AbstractValue v, NullValue nv |
|
||||
G::Internal::asserts(bb.getNode(i).getElement(), e, v) |
|
||||
G::Internal::asserts(bb.getNode(i).getElement(), e, v)
|
||||
|
|
||||
exprImpliesSsaDef(e, v, def, nv) and
|
||||
not nv.isNull()
|
||||
)
|
||||
@@ -130,7 +129,9 @@ private predicate ensureNotNullAt(BasicBlock bb, int i, Ssa::Definition def) {
|
||||
* Holds if the `i`th node of basic block `bb` is a dereference `d` of SSA
|
||||
* definition `def`, and `def` may potentially be `null`.
|
||||
*/
|
||||
private predicate potentialNullDereferenceAt(BasicBlock bb, int i, Ssa::Definition def, Dereference d) {
|
||||
private predicate potentialNullDereferenceAt(
|
||||
BasicBlock bb, int i, Ssa::Definition def, Dereference d
|
||||
) {
|
||||
dereferenceAt(bb, i, def, d) and
|
||||
not exists(int j | ensureNotNullAt(bb, j, def) | j < i)
|
||||
}
|
||||
@@ -142,16 +143,16 @@ private predicate potentialNullDereferenceAt(BasicBlock bb, int i, Ssa::Definiti
|
||||
* If the returned element takes the `s` branch, then `def` is guaranteed to be
|
||||
* `null` if `nv.isNull()` holds, and non-`null` otherwise.
|
||||
*/
|
||||
private ControlFlowElement getANullCheck(Ssa::Definition def, SuccessorTypes::ConditionalSuccessor s, NullValue nv) {
|
||||
exists(Expr e, G::AbstractValue v |
|
||||
v.branch(result, s, e) |
|
||||
exprImpliesSsaDef(e, v, def, nv)
|
||||
)
|
||||
private ControlFlowElement getANullCheck(
|
||||
Ssa::Definition def, SuccessorTypes::ConditionalSuccessor s, NullValue nv
|
||||
) {
|
||||
exists(Expr e, G::AbstractValue v | v.branch(result, s, e) | exprImpliesSsaDef(e, v, def, nv))
|
||||
}
|
||||
|
||||
private predicate isMaybeNullArgument(Ssa::ExplicitDefinition def, MaybeNullExpr arg) {
|
||||
exists(AssignableDefinitions::ImplicitParameterDefinition pdef, Parameter p |
|
||||
pdef = def.getADefinition() |
|
||||
pdef = def.getADefinition()
|
||||
|
|
||||
p = pdef.getParameter().getSourceDeclaration() and
|
||||
p.getAnAssignedArgument() = arg and
|
||||
not arg.getEnclosingCallable().getEnclosingCallable*() instanceof TestMethod
|
||||
@@ -161,14 +162,11 @@ private predicate isMaybeNullArgument(Ssa::ExplicitDefinition def, MaybeNullExpr
|
||||
/** Holds if `def` is an SSA definition that may be `null`. */
|
||||
private predicate defMaybeNull(Ssa::Definition def, string msg, Element reason) {
|
||||
// A variable compared to `null` might be `null`
|
||||
exists(G::DereferenceableExpr de |
|
||||
de = def.getARead() |
|
||||
exists(G::DereferenceableExpr de | de = def.getARead() |
|
||||
reason = de.getANullCheck(_, true) and
|
||||
msg = "as suggested by $@ null check" and
|
||||
not de = any(Ssa::PseudoDefinition pdef).getARead() and
|
||||
strictcount(Element e |
|
||||
e = any(Ssa::Definition def0 | de = def0.getARead()).getElement()
|
||||
) = 1 and
|
||||
strictcount(Element e | e = any(Ssa::Definition def0 | de = def0.getARead()).getElement()) = 1 and
|
||||
not nonNullDef(def) and
|
||||
// Don't use a check as reason if there is a `null` assignment
|
||||
// or argument
|
||||
@@ -181,16 +179,14 @@ private predicate defMaybeNull(Ssa::Definition def, string msg, Element reason)
|
||||
msg = "because of $@ null argument"
|
||||
or
|
||||
// If the source of a variable is `null` then the variable may be `null`
|
||||
exists(AssignableDefinition adef |
|
||||
adef = def.(Ssa::ExplicitDefinition).getADefinition() |
|
||||
exists(AssignableDefinition adef | adef = def.(Ssa::ExplicitDefinition).getADefinition() |
|
||||
adef.getSource() instanceof MaybeNullExpr and
|
||||
reason = adef.getExpr() and
|
||||
msg = "because of $@ assignment"
|
||||
)
|
||||
or
|
||||
// A variable of nullable type may be null
|
||||
exists(Dereference d |
|
||||
dereferenceAt(_, _, def, d) |
|
||||
exists(Dereference d | dereferenceAt(_, _, def, d) |
|
||||
d.hasNullableType() and
|
||||
not def instanceof Ssa::PseudoDefinition and
|
||||
reason = def.getSourceVariable().getAssignable() and
|
||||
@@ -203,7 +199,9 @@ private predicate defMaybeNull(Ssa::Definition def, string msg, Element reason)
|
||||
* be `null` in basic block `bb2`. The SSA definitions share the same source
|
||||
* variable.
|
||||
*/
|
||||
private predicate defNullImpliesStep(Ssa::Definition def1, BasicBlock bb1, Ssa::Definition def2, BasicBlock bb2) {
|
||||
private predicate defNullImpliesStep(
|
||||
Ssa::Definition def1, BasicBlock bb1, Ssa::Definition def2, BasicBlock bb2
|
||||
) {
|
||||
exists(Ssa::SourceVariable v |
|
||||
defMaybeNull(v.getAnSsaDefinition(), _, _) and
|
||||
def1.getSourceVariable() = v
|
||||
@@ -221,7 +219,8 @@ private predicate defNullImpliesStep(Ssa::Definition def1, BasicBlock bb1, Ssa::
|
||||
not ensureNotNullAt(bb1, _, def1) and
|
||||
bb2 = bb1.getASuccessor() and
|
||||
not exists(SuccessorTypes::ConditionalSuccessor s, NullValue nv |
|
||||
bb1.getLastNode() = getANullCheck(def1, s, nv).getAControlFlowNode() |
|
||||
bb1.getLastNode() = getANullCheck(def1, s, nv).getAControlFlowNode()
|
||||
|
|
||||
bb2 = bb1.getASuccessorByType(s) and
|
||||
not nv.isNull()
|
||||
)
|
||||
@@ -235,8 +234,7 @@ private predicate defMaybeNullInBlock(Ssa::Definition def, BasicBlock bb) {
|
||||
defMaybeNull(def, _, _) and
|
||||
bb = def.getBasicBlock()
|
||||
or
|
||||
exists(BasicBlock mid, Ssa::Definition midDef |
|
||||
defMaybeNullInBlock(midDef, mid) |
|
||||
exists(BasicBlock mid, Ssa::Definition midDef | defMaybeNullInBlock(midDef, mid) |
|
||||
defNullImpliesStep(midDef, mid, def, bb)
|
||||
)
|
||||
}
|
||||
@@ -246,8 +244,7 @@ private predicate defMaybeNullInBlock(Ssa::Definition def, BasicBlock bb) {
|
||||
* dereference.
|
||||
*/
|
||||
private predicate nullDerefCandidateVariable(Ssa::SourceVariable v) {
|
||||
exists(Ssa::Definition def, BasicBlock bb |
|
||||
potentialNullDereferenceAt(bb, _, def, _) |
|
||||
exists(Ssa::Definition def, BasicBlock bb | potentialNullDereferenceAt(bb, _, def, _) |
|
||||
defMaybeNullInBlock(def, bb) and
|
||||
v = def.getSourceVariable()
|
||||
)
|
||||
@@ -272,14 +269,12 @@ private newtype TPathNode =
|
||||
nullDerefCandidateVariable(def.getSourceVariable()) and
|
||||
defMaybeNull(def, msg, reason) and
|
||||
if isMaybeNullArgument(def, reason) then isNullArgument = true else isNullArgument = false
|
||||
}
|
||||
or
|
||||
} or
|
||||
TInternalPathNode(Ssa::Definition def, BasicBlock bb) {
|
||||
succStep(_, def, bb)
|
||||
or
|
||||
succNullArgument(_, def, bb)
|
||||
}
|
||||
or
|
||||
} or
|
||||
TSinkPathNode(Ssa::Definition def, BasicBlock bb, int i, Dereference d) {
|
||||
potentialNullDereferenceAt(bb, i, def, d) and
|
||||
(
|
||||
@@ -314,8 +309,11 @@ abstract class PathNode extends TPathNode {
|
||||
|
||||
private class SourcePathNode extends PathNode, TSourcePathNode {
|
||||
private Ssa::Definition def;
|
||||
|
||||
private string msg;
|
||||
|
||||
private Element reason;
|
||||
|
||||
private boolean isNullArgument;
|
||||
|
||||
SourcePathNode() { this = TSourcePathNode(def, msg, reason, isNullArgument) }
|
||||
@@ -341,22 +339,17 @@ private class SourcePathNode extends PathNode, TSourcePathNode {
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
if isNullArgument = true then
|
||||
result = reason.toString()
|
||||
else
|
||||
result = def.toString()
|
||||
if isNullArgument = true then result = reason.toString() else result = def.toString()
|
||||
}
|
||||
|
||||
override Location getLocation() {
|
||||
if isNullArgument = true then
|
||||
result = reason.getLocation()
|
||||
else
|
||||
result = def.getLocation()
|
||||
if isNullArgument = true then result = reason.getLocation() else result = def.getLocation()
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalPathNode extends PathNode, TInternalPathNode {
|
||||
private Ssa::Definition def;
|
||||
|
||||
private BasicBlock bb;
|
||||
|
||||
InternalPathNode() { this = TInternalPathNode(def, bb) }
|
||||
@@ -376,8 +369,11 @@ private class InternalPathNode extends PathNode, TInternalPathNode {
|
||||
|
||||
private class SinkPathNode extends PathNode, TSinkPathNode {
|
||||
private Ssa::Definition def;
|
||||
|
||||
private BasicBlock bb;
|
||||
|
||||
private int i;
|
||||
|
||||
private Dereference d;
|
||||
|
||||
SinkPathNode() { this = TSinkPathNode(def, bb, i, d) }
|
||||
@@ -400,10 +396,8 @@ private class SinkPathNode extends PathNode, TSinkPathNode {
|
||||
* for `Dereference::is[First]MaybeNull()`.
|
||||
*/
|
||||
module PathGraph {
|
||||
query predicate nodes(PathNode n) {
|
||||
n.getASuccessor*() instanceof SinkPathNode
|
||||
}
|
||||
|
||||
query predicate nodes(PathNode n) { n.getASuccessor*() instanceof SinkPathNode }
|
||||
|
||||
query predicate edges(PathNode pred, PathNode succ) {
|
||||
nodes(pred) and
|
||||
nodes(succ) and
|
||||
@@ -431,12 +425,13 @@ private predicate defReaches(Ssa::Definition def, ControlFlow::Node cfn, boolean
|
||||
exists(def.getAFirstReadAtNode(cfn)) and
|
||||
(always = true or always = false)
|
||||
or
|
||||
exists(ControlFlow::Node mid |
|
||||
defReaches(def, mid, always) |
|
||||
exists(ControlFlow::Node mid | defReaches(def, mid, always) |
|
||||
Ssa::Internal::adjacentReadPairSameVar(mid, cfn) and
|
||||
not mid = any(Dereference d |
|
||||
if always = true then d.isAlwaysNull(def.getSourceVariable()) else d.isMaybeNull(def, _, _, _, _)
|
||||
).getAControlFlowNode()
|
||||
if always = true
|
||||
then d.isAlwaysNull(def.getSourceVariable())
|
||||
else d.isMaybeNull(def, _, _, _, _)
|
||||
).getAControlFlowNode()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -446,20 +441,21 @@ private predicate defReaches(Ssa::Definition def, ControlFlow::Node cfn, boolean
|
||||
*/
|
||||
class Dereference extends G::DereferenceableExpr {
|
||||
Dereference() {
|
||||
if this.hasNullableType() then (
|
||||
if this.hasNullableType()
|
||||
then (
|
||||
// Strictly speaking, these throw `InvalidOperationException`s and not
|
||||
// `NullReferenceException`s
|
||||
this = any(PropertyAccess pa | pa.getTarget().hasName("Value")).getQualifier()
|
||||
or
|
||||
exists(Type underlyingType |
|
||||
this = any(CastExpr ce | ce.getTargetType() = underlyingType).getExpr() |
|
||||
this = any(CastExpr ce | ce.getTargetType() = underlyingType).getExpr()
|
||||
|
|
||||
underlyingType = this.getType().(NullableType).getUnderlyingType()
|
||||
or
|
||||
underlyingType = this.getType() and
|
||||
not underlyingType instanceof NullableType
|
||||
)
|
||||
)
|
||||
else (
|
||||
) else (
|
||||
this = any(QualifiableExpr qe | not qe.isConditional()).getQualifier() and
|
||||
not this instanceof ThisAccess and
|
||||
not this instanceof BaseAccess and
|
||||
@@ -472,11 +468,15 @@ class Dereference extends G::DereferenceableExpr {
|
||||
exists(ExtensionMethodCall emc, Parameter p |
|
||||
this = emc.getArgumentForParameter(p) and
|
||||
p.hasExtensionMethodModifier() and
|
||||
not emc.isConditional() |
|
||||
not emc.isConditional()
|
||||
|
|
||||
p.fromSource() // assume all non-source extension methods perform a dereference
|
||||
implies
|
||||
exists(Ssa::ExplicitDefinition def, AssignableDefinitions::ImplicitParameterDefinition pdef |
|
||||
pdef = def.getADefinition() |
|
||||
exists(
|
||||
Ssa::ExplicitDefinition def, AssignableDefinitions::ImplicitParameterDefinition pdef
|
||||
|
|
||||
pdef = def.getADefinition()
|
||||
|
|
||||
p.getSourceDeclaration() = pdef.getParameter() and
|
||||
def.getARead() instanceof Dereference
|
||||
)
|
||||
@@ -485,8 +485,7 @@ class Dereference extends G::DereferenceableExpr {
|
||||
}
|
||||
|
||||
private predicate isAlwaysNull0(Ssa::Definition def) {
|
||||
forall(Ssa::Definition input |
|
||||
input = getAnUltimateDefinition(def) |
|
||||
forall(Ssa::Definition input | input = getAnUltimateDefinition(def) |
|
||||
input.(Ssa::ExplicitDefinition).getADefinition().getSource() instanceof AlwaysNullExpr
|
||||
) and
|
||||
not nonNullDef(def) and
|
||||
@@ -503,15 +502,12 @@ class Dereference extends G::DereferenceableExpr {
|
||||
// Exclude fields, properties, and captured variables, as they may not have an
|
||||
// accurate SSA representation
|
||||
v.getAssignable() = any(LocalScopeVariable lsv |
|
||||
strictcount(Callable c |
|
||||
c = any(AssignableDefinition ad | ad.getTarget() = lsv).getEnclosingCallable()
|
||||
) = 1
|
||||
) and
|
||||
strictcount(Callable c |
|
||||
c = any(AssignableDefinition ad | ad.getTarget() = lsv).getEnclosingCallable()
|
||||
) = 1
|
||||
) and
|
||||
(
|
||||
forex(Ssa::Definition def0 |
|
||||
this = def0.getARead() |
|
||||
this.isAlwaysNull0(def0)
|
||||
)
|
||||
forex(Ssa::Definition def0 | this = def0.getARead() | this.isAlwaysNull0(def0))
|
||||
or
|
||||
exists(NullValue nv |
|
||||
this.(G::GuardedExpr).mustHaveValue(nv) and
|
||||
@@ -535,7 +531,9 @@ class Dereference extends G::DereferenceableExpr {
|
||||
* Holds if this expression dereferences SSA definition `def`, which may
|
||||
* be `null`.
|
||||
*/
|
||||
predicate isMaybeNull(Ssa::Definition def, SourcePathNode source, SinkPathNode sink, string msg, Element reason) {
|
||||
predicate isMaybeNull(
|
||||
Ssa::Definition def, SourcePathNode source, SinkPathNode sink, string msg, Element reason
|
||||
) {
|
||||
source.getASuccessor*() = sink and
|
||||
msg = source.getMessage() and
|
||||
reason = source.getReason() and
|
||||
@@ -549,7 +547,9 @@ class Dereference extends G::DereferenceableExpr {
|
||||
* be `null`, and this expression can be reached from `def` without passing
|
||||
* through another such dereference.
|
||||
*/
|
||||
predicate isFirstMaybeNull(Ssa::Definition def, SourcePathNode source, SinkPathNode sink, string msg, Element reason) {
|
||||
predicate isFirstMaybeNull(
|
||||
Ssa::Definition def, SourcePathNode source, SinkPathNode sink, string msg, Element reason
|
||||
) {
|
||||
this.isMaybeNull(def, source, sink, msg, reason) and
|
||||
defReaches(def, this.getAControlFlowNode(), false)
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -27,8 +27,7 @@ module BaseSsa {
|
||||
bb.getNode(i) = def.getAControlFlowNode() and
|
||||
v = def.getTarget() and
|
||||
// In cases like `(x, x) = (0, 1)`, we discard the first (dead) definition of `x`
|
||||
not exists(TupleAssignmentDefinition first, TupleAssignmentDefinition second |
|
||||
first = def |
|
||||
not exists(TupleAssignmentDefinition first, TupleAssignmentDefinition second | first = def |
|
||||
second.getAssignment() = first.getAssignment() and
|
||||
second.getEvaluationOrder() > first.getEvaluationOrder() and
|
||||
second.getTarget() = v
|
||||
@@ -40,14 +39,15 @@ module BaseSsa {
|
||||
* variable `v` in an SSA representation.
|
||||
*/
|
||||
private predicate needsPhiNode(BasicBlock bb, SimpleLocalScopeVariable v) {
|
||||
exists(BasicBlock def |
|
||||
def.inDominanceFrontier(bb) |
|
||||
exists(BasicBlock def | def.inDominanceFrontier(bb) |
|
||||
defAt(def, _, _, v) or
|
||||
needsPhiNode(def, v)
|
||||
)
|
||||
}
|
||||
|
||||
private newtype SsaRefKind = SsaRead() or SsaDef()
|
||||
private newtype SsaRefKind =
|
||||
SsaRead() or
|
||||
SsaDef()
|
||||
|
||||
/**
|
||||
* Holds if the `i`th node of basic block `bb` is a reference to `v`,
|
||||
@@ -80,11 +80,10 @@ module BaseSsa {
|
||||
* `bb` reaches the reference at rank `rnk`, without passing through another
|
||||
* definition of `v`, including phi nodes.
|
||||
*/
|
||||
private predicate defReachesRank(BasicBlock bb, AssignableDefinition def, SimpleLocalScopeVariable v, int rnk) {
|
||||
exists(int i |
|
||||
rnk = ssaRefRank(bb, i, v, SsaDef()) |
|
||||
defAt(bb, i, def, v)
|
||||
)
|
||||
private predicate defReachesRank(
|
||||
BasicBlock bb, AssignableDefinition def, SimpleLocalScopeVariable v, int rnk
|
||||
) {
|
||||
exists(int i | rnk = ssaRefRank(bb, i, v, SsaDef()) | defAt(bb, i, def, v))
|
||||
or
|
||||
defReachesRank(bb, def, v, rnk - 1) and
|
||||
rnk = ssaRefRank(bb, _, v, SsaRead())
|
||||
@@ -112,7 +111,8 @@ module BaseSsa {
|
||||
* Gets a read of the SSA definition for variable `v` at definition `def`. That is,
|
||||
* a read that is guaranteed to read the value assigned at definition `def`.
|
||||
*/
|
||||
cached AssignableRead getARead(AssignableDefinition def, SimpleLocalScopeVariable v) {
|
||||
cached
|
||||
AssignableRead getARead(AssignableDefinition def, SimpleLocalScopeVariable v) {
|
||||
exists(BasicBlock bb, int i, int rnk |
|
||||
result.getAControlFlowNode() = bb.getNode(i) and
|
||||
rnk = ssaRefRank(bb, i, v, SsaRead())
|
||||
|
||||
@@ -18,8 +18,7 @@ module Steps {
|
||||
private AssignableRead getARead(AssignableDefinition def) {
|
||||
result = BaseSsa::getARead(def, _)
|
||||
or
|
||||
exists(LocalScopeVariable v |
|
||||
def.getTarget() = v |
|
||||
exists(LocalScopeVariable v | def.getTarget() = v |
|
||||
result = v.getAnAccess() and
|
||||
strictcount(AssignableDefinition def0 | def0.getTarget() = v) = 1
|
||||
)
|
||||
@@ -27,7 +26,8 @@ module Steps {
|
||||
exists(Field f |
|
||||
def.getTarget() = f and
|
||||
result = f.getAnAccess() and
|
||||
strictcount(AssignableDefinition def0 | def0.getTarget() = f) = 1 |
|
||||
strictcount(AssignableDefinition def0 | def0.getTarget() = f) = 1
|
||||
|
|
||||
f.isReadOnly() or
|
||||
f.isConst() or
|
||||
isEffectivelyInternalOrPrivate(f)
|
||||
@@ -53,29 +53,26 @@ module Steps {
|
||||
}
|
||||
|
||||
private predicate flowIn(Parameter p, Expr pred, AssignableRead succ) {
|
||||
exists(AssignableDefinitions::ImplicitParameterDefinition def, Call c |
|
||||
succ = getARead(def) |
|
||||
exists(AssignableDefinitions::ImplicitParameterDefinition def, Call c | succ = getARead(def) |
|
||||
pred = getArgumentForOverridderParameter(c, p) and
|
||||
p.getSourceDeclaration() = def.getParameter()
|
||||
)
|
||||
}
|
||||
|
||||
private Expr getArgumentForOverridderParameter(Call call, Parameter p) {
|
||||
exists(Parameter base, Callable callable |
|
||||
result = call.getArgumentForParameter(base) |
|
||||
exists(Parameter base, Callable callable | result = call.getArgumentForParameter(base) |
|
||||
base = callable.getAParameter() and
|
||||
isOverriderParameter(callable, p, base.getPosition())
|
||||
)
|
||||
}
|
||||
|
||||
pragma [noinline]
|
||||
pragma[noinline]
|
||||
private predicate isOverriderParameter(Callable c, Parameter p, int i) {
|
||||
(
|
||||
p = c.getAParameter() or
|
||||
p = c.(Method).getAnOverrider+().getAParameter() or
|
||||
p = c.(Method).getAnUltimateImplementor().getAParameter()
|
||||
)
|
||||
and
|
||||
) and
|
||||
i = p.getPosition()
|
||||
}
|
||||
|
||||
@@ -130,13 +127,9 @@ module Steps {
|
||||
* ```
|
||||
*/
|
||||
predicate stepOpen(Expr pred, Expr succ) {
|
||||
exists(AssignableDefinition def |
|
||||
succ = getARead(def) |
|
||||
pred = def.getSource()
|
||||
)
|
||||
exists(AssignableDefinition def | succ = getARead(def) | pred = def.getSource())
|
||||
or
|
||||
exists(Parameter p |
|
||||
flowIn(p, pred, succ) |
|
||||
exists(Parameter p | flowIn(p, pred, succ) |
|
||||
isEffectivelyInternalOrPrivateCallable(p.getCallable())
|
||||
)
|
||||
or
|
||||
|
||||
@@ -39,35 +39,29 @@ class DispatchCall extends Internal::TDispatchCall {
|
||||
|
||||
/** Internal implementation details. */
|
||||
private module Internal {
|
||||
private cached module Cached {
|
||||
cached
|
||||
private module Cached {
|
||||
/** Internal representation of calls. */
|
||||
cached
|
||||
newtype TDispatchCall =
|
||||
TDispatchMethodCall(MethodCall mc) {
|
||||
not isReflectionCall(mc, _, _, _, _) and
|
||||
not mc.isLateBound()
|
||||
}
|
||||
or
|
||||
TDispatchAccessorCall(AccessorCall ac)
|
||||
or
|
||||
} or
|
||||
TDispatchAccessorCall(AccessorCall ac) or
|
||||
TDispatchReflectionCall(MethodCall mc, string name, Expr object, Expr qualifier, int args) {
|
||||
isReflectionCall(mc, name, object, qualifier, args)
|
||||
}
|
||||
or
|
||||
TDispatchDynamicMethodCall(DynamicMethodCall dmc)
|
||||
or
|
||||
TDispatchDynamicOperatorCall(DynamicOperatorCall doc)
|
||||
or
|
||||
TDispatchDynamicMemberAccess(DynamicMemberAccess dma)
|
||||
or
|
||||
TDispatchDynamicElementAccess(DynamicElementAccess dea)
|
||||
or
|
||||
TDispatchDynamicEventAccess(AssignArithmeticOperation aao, DynamicMemberAccess dma, string name) {
|
||||
} or
|
||||
TDispatchDynamicMethodCall(DynamicMethodCall dmc) or
|
||||
TDispatchDynamicOperatorCall(DynamicOperatorCall doc) or
|
||||
TDispatchDynamicMemberAccess(DynamicMemberAccess dma) or
|
||||
TDispatchDynamicElementAccess(DynamicElementAccess dea) or
|
||||
TDispatchDynamicEventAccess(
|
||||
AssignArithmeticOperation aao, DynamicMemberAccess dma, string name
|
||||
) {
|
||||
isPotentialEventCall(aao, dma, name)
|
||||
}
|
||||
or
|
||||
TDispatchDynamicObjectCreation(DynamicObjectCreation doc)
|
||||
or
|
||||
} or
|
||||
TDispatchDynamicObjectCreation(DynamicObjectCreation doc) or
|
||||
TDispatchStaticCall(Call c) {
|
||||
c = any(ObjectCreation oc | not oc.isLateBound())
|
||||
or
|
||||
@@ -79,19 +73,13 @@ private module Internal {
|
||||
}
|
||||
|
||||
cached
|
||||
Expr getCall(DispatchCall dc) {
|
||||
result = dc.(DispatchCallImpl).getCall()
|
||||
}
|
||||
Expr getCall(DispatchCall dc) { result = dc.(DispatchCallImpl).getCall() }
|
||||
|
||||
cached
|
||||
Expr getArgument(DispatchCall dc, int i) {
|
||||
result = dc.(DispatchCallImpl).getArgument(i)
|
||||
}
|
||||
Expr getArgument(DispatchCall dc, int i) { result = dc.(DispatchCallImpl).getArgument(i) }
|
||||
|
||||
cached
|
||||
Expr getQualifier(DispatchCall dc) {
|
||||
result = dc.(DispatchCallImpl).getQualifier()
|
||||
}
|
||||
Expr getQualifier(DispatchCall dc) { result = dc.(DispatchCallImpl).getQualifier() }
|
||||
|
||||
cached
|
||||
RuntimeCallable getADynamicTarget(DispatchCall dc) {
|
||||
@@ -100,9 +88,7 @@ private module Internal {
|
||||
}
|
||||
import Cached
|
||||
|
||||
Callable getAStaticTarget(DispatchCall dc) {
|
||||
result = dc.(DispatchCallImpl).getAStaticTarget()
|
||||
}
|
||||
Callable getAStaticTarget(DispatchCall dc) { result = dc.(DispatchCallImpl).getAStaticTarget() }
|
||||
|
||||
/**
|
||||
* Holds if `mc` is a reflection call to a method named `name`, where
|
||||
@@ -110,7 +96,9 @@ private module Internal {
|
||||
* static method is invoked) and `qualifier` is the qualifier of the
|
||||
* reflection call.
|
||||
*/
|
||||
private predicate isReflectionCall(MethodCall mc, string name, Expr object, Expr qualifier, int args) {
|
||||
private predicate isReflectionCall(
|
||||
MethodCall mc, string name, Expr object, Expr qualifier, int args
|
||||
) {
|
||||
exists(SystemTypeClass stc |
|
||||
mc.getTarget() = stc.getInvokeMemberMethod() and
|
||||
mc.getArgument(0).getValue() = name and
|
||||
@@ -155,11 +143,14 @@ private module Internal {
|
||||
* potential event access and `name` is the name of the relevant event
|
||||
* accessor.
|
||||
*/
|
||||
private predicate isPotentialEventCall(AssignArithmeticOperation aao, DynamicMemberAccess dma, string name) {
|
||||
private predicate isPotentialEventCall(
|
||||
AssignArithmeticOperation aao, DynamicMemberAccess dma, string name
|
||||
) {
|
||||
exists(DynamicOperatorCall doc, AssignExpr ae |
|
||||
ae = aao.getExpandedAssignment() and
|
||||
dma = ae.getLValue() and
|
||||
doc = ae.getRValue() |
|
||||
doc = ae.getRValue()
|
||||
|
|
||||
aao instanceof AssignAddExpr and
|
||||
name = "add_" + dma.getLateBoundTargetName()
|
||||
or
|
||||
@@ -169,7 +160,7 @@ private module Internal {
|
||||
}
|
||||
|
||||
/** A call. */
|
||||
private abstract class DispatchCallImpl extends TDispatchCall {
|
||||
abstract private class DispatchCallImpl extends TDispatchCall {
|
||||
/** Gets a textual representation of this call. */
|
||||
string toString() { none() }
|
||||
|
||||
@@ -180,9 +171,7 @@ private module Internal {
|
||||
abstract Expr getArgument(int i);
|
||||
|
||||
/** Gets the number of arguments of this call. */
|
||||
int getNumberOfArguments() {
|
||||
result = count(int i | exists(getArgument(i)))
|
||||
}
|
||||
int getNumberOfArguments() { result = count(int i | exists(getArgument(i))) }
|
||||
|
||||
/** Gets the qualifier of this call, if any. */
|
||||
abstract Expr getQualifier();
|
||||
@@ -199,8 +188,7 @@ private module Internal {
|
||||
* the qualifier is guaranteed not to be a subtype of `qualifierType`.
|
||||
*/
|
||||
predicate hasQualifierType(Type qualifierType, boolean isExactType) {
|
||||
exists(Type trackedType |
|
||||
trackedType = getAPossibleType(this.getQualifier(), isExactType) |
|
||||
exists(Type trackedType | trackedType = getAPossibleType(this.getQualifier(), isExactType) |
|
||||
qualifierType = trackedType and
|
||||
not qualifierType instanceof TypeParameter
|
||||
or
|
||||
@@ -213,18 +201,15 @@ private module Internal {
|
||||
* that does not contain type parameters.
|
||||
*/
|
||||
TypeWithoutTypeParameters getANonExactQualifierTypeWithoutTypeParameters() {
|
||||
exists(Type qualifierType |
|
||||
hasQualifierType(qualifierType, false) |
|
||||
exists(Type qualifierType | hasQualifierType(qualifierType, false) |
|
||||
// Qualifier type contains no type parameters: use it
|
||||
result = qualifierType
|
||||
or
|
||||
// Qualifier type is a constructed type where all type arguments are type
|
||||
// parameters: use the unbound generic type
|
||||
exists(ConstructedType ct |
|
||||
ct = qualifierType
|
||||
and
|
||||
forex(Type arg | arg = ct.getATypeArgument() | arg instanceof TypeParameter)
|
||||
and
|
||||
ct = qualifierType and
|
||||
forex(Type arg | arg = ct.getATypeArgument() | arg instanceof TypeParameter) and
|
||||
result = ct.getUnboundGeneric()
|
||||
)
|
||||
or
|
||||
@@ -249,8 +234,13 @@ private module Internal {
|
||||
*/
|
||||
private Type getAPossibleType(Expr e, boolean isExact) {
|
||||
exists(ValueOrRefType qualifierType, FieldOrProperty fp, boolean qualifierTypeIsExact |
|
||||
qualifierType = getAPossibleTypeDynamicMemberAccessQualifier(e, qualifierTypeIsExact, fp) |
|
||||
(if qualifierTypeIsExact = true then qualifierType.hasMember(fp) else fp.getDeclaringType().isImplicitlyConvertibleTo(qualifierType)) and
|
||||
qualifierType = getAPossibleTypeDynamicMemberAccessQualifier(e, qualifierTypeIsExact, fp)
|
||||
|
|
||||
(
|
||||
if qualifierTypeIsExact = true
|
||||
then qualifierType.hasMember(fp)
|
||||
else fp.getDeclaringType().isImplicitlyConvertibleTo(qualifierType)
|
||||
) and
|
||||
result = fp.getType() and
|
||||
isExact = false
|
||||
)
|
||||
@@ -259,7 +249,9 @@ private module Internal {
|
||||
result = getASourceType(e, isExact)
|
||||
}
|
||||
|
||||
private Type getAPossibleTypeDynamicMemberAccessQualifier(DynamicMemberAccess dma, boolean isExact, FieldOrProperty fp) {
|
||||
private Type getAPossibleTypeDynamicMemberAccessQualifier(
|
||||
DynamicMemberAccess dma, boolean isExact, FieldOrProperty fp
|
||||
) {
|
||||
result = getAPossibleType(dma.getQualifier(), isExact) and
|
||||
fp.getName() = dma.getLateBoundTargetName()
|
||||
}
|
||||
@@ -278,8 +270,7 @@ private module Internal {
|
||||
or
|
||||
t.getAChild*() instanceof DynamicType
|
||||
or
|
||||
exists(Type other |
|
||||
other != t |
|
||||
exists(Type other | other != t |
|
||||
not other instanceof DynamicType and
|
||||
other.isImplicitlyConvertibleTo(t)
|
||||
)
|
||||
@@ -299,7 +290,8 @@ private module Internal {
|
||||
or
|
||||
exists(Assignable a |
|
||||
a instanceof Field or
|
||||
a instanceof Property |
|
||||
a instanceof Property
|
||||
|
|
||||
succ.(AssignableRead) = a.getAnAccess() and
|
||||
pred = a.getAnAssignedValue() and
|
||||
a = any(Modifiable m | m.isEffectivelyInternal() or m.isEffectivelyPrivate())
|
||||
@@ -314,12 +306,9 @@ private module Internal {
|
||||
typeMayBeImprecise(succ.getType())
|
||||
}
|
||||
|
||||
private predicate stepTC(Expr succ, Expr pred) =
|
||||
fastTC(stepExpr/2)(succ, pred)
|
||||
private predicate stepTC(Expr succ, Expr pred) = fastTC(stepExpr/2)(succ, pred)
|
||||
|
||||
private class Source extends Expr {
|
||||
Source() { not stepExpr(this, _) }
|
||||
}
|
||||
private class Source extends Expr { Source() { not stepExpr(this, _) } }
|
||||
|
||||
private class Sink extends Expr {
|
||||
Sink() {
|
||||
@@ -329,7 +318,8 @@ private module Internal {
|
||||
or
|
||||
this = any(DispatchReflectionOrDynamicCall c).getArgument(_)
|
||||
or
|
||||
this = any(MethodCall mc | mc.getTarget() = any(SystemObjectClass c).getGetTypeMethod()).getQualifier()
|
||||
this = any(MethodCall mc | mc.getTarget() = any(SystemObjectClass c).getGetTypeMethod())
|
||||
.getQualifier()
|
||||
or
|
||||
this = any(DispatchCallImpl c).getQualifier()
|
||||
}
|
||||
@@ -347,7 +337,8 @@ private module Internal {
|
||||
Type getASourceType(Sink e, boolean isExact) {
|
||||
exists(Source s |
|
||||
s = e.getASource() or
|
||||
s = e |
|
||||
s = e
|
||||
|
|
||||
result = s.getType() and
|
||||
if hasExactType(s) then isExact = true else isExact = false
|
||||
)
|
||||
@@ -362,25 +353,18 @@ private module Internal {
|
||||
* into account.
|
||||
*/
|
||||
private class DispatchMethodCall extends DispatchCallImpl, TDispatchMethodCall {
|
||||
override MethodCall getCall() {
|
||||
this = TDispatchMethodCall(result)
|
||||
}
|
||||
override MethodCall getCall() { this = TDispatchMethodCall(result) }
|
||||
|
||||
override Expr getArgument(int i) {
|
||||
exists(Call call, Parameter p |
|
||||
call = this.getCall() |
|
||||
exists(Call call, Parameter p | call = this.getCall() |
|
||||
p = call.getTarget().getParameter(i) and
|
||||
result = call.getArgumentForParameter(p)
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getQualifier() {
|
||||
result = getCall().getQualifier()
|
||||
}
|
||||
override Expr getQualifier() { result = getCall().getQualifier() }
|
||||
|
||||
override Method getAStaticTarget() {
|
||||
result = getCall().getTarget()
|
||||
}
|
||||
override Method getAStaticTarget() { result = getCall().getTarget() }
|
||||
|
||||
override RuntimeMethod getADynamicTarget() {
|
||||
result = getViableInherited()
|
||||
@@ -489,29 +473,19 @@ private module Internal {
|
||||
* into account.
|
||||
*/
|
||||
private class DispatchAccessorCall extends DispatchCallImpl, TDispatchAccessorCall {
|
||||
override AccessorCall getCall() {
|
||||
this = TDispatchAccessorCall(result)
|
||||
}
|
||||
override AccessorCall getCall() { this = TDispatchAccessorCall(result) }
|
||||
|
||||
override Expr getArgument(int i) {
|
||||
result = getCall().getArgument(i)
|
||||
}
|
||||
override Expr getArgument(int i) { result = getCall().getArgument(i) }
|
||||
|
||||
override Expr getQualifier() {
|
||||
result = getCall().(MemberAccess).getQualifier()
|
||||
}
|
||||
override Expr getQualifier() { result = getCall().(MemberAccess).getQualifier() }
|
||||
|
||||
override Accessor getAStaticTarget() {
|
||||
result = getCall().getTarget()
|
||||
}
|
||||
override Accessor getAStaticTarget() { result = getCall().getTarget() }
|
||||
|
||||
override RuntimeAccessor getADynamicTarget() {
|
||||
result = getADynamicTargetCandidate()
|
||||
and
|
||||
result = getADynamicTargetCandidate() and
|
||||
// Calls to accessors may have `dynamic` expression arguments,
|
||||
// so we need to check that the types match
|
||||
forall(Type argumentType, int i |
|
||||
hasDynamicArg(i, argumentType) |
|
||||
forall(Type argumentType, int i | hasDynamicArg(i, argumentType) |
|
||||
argumentType.isImplicitlyConvertibleTo(result.getParameter(i).getType())
|
||||
)
|
||||
}
|
||||
@@ -576,7 +550,7 @@ private module Internal {
|
||||
)
|
||||
}
|
||||
|
||||
pragma [noinline]
|
||||
pragma[noinline]
|
||||
private predicate hasQualifierTypeSourceDecl(SourceDeclarationType t) {
|
||||
exists(Type qualifierType |
|
||||
this.hasQualifierType(qualifierType, _) and
|
||||
@@ -629,7 +603,7 @@ private module Internal {
|
||||
}
|
||||
|
||||
/** An internal helper class for comparing two types modulo type parameters. */
|
||||
private abstract class StructurallyComparedModuloTypeParameters extends Type {
|
||||
abstract private class StructurallyComparedModuloTypeParameters extends Type {
|
||||
/** Gets a candidate for comparison against this type. */
|
||||
abstract Type getACandidate();
|
||||
|
||||
@@ -657,9 +631,7 @@ private module Internal {
|
||||
* by different types at different positions. For example,
|
||||
* `C<int, string>` is considered a potential instantiation of `C<T, T>`.
|
||||
*/
|
||||
TypeWithoutTypeParameters getAPotentialInstance() {
|
||||
equalsModuloTypeParameters(result)
|
||||
}
|
||||
TypeWithoutTypeParameters getAPotentialInstance() { equalsModuloTypeParameters(result) }
|
||||
}
|
||||
|
||||
private Type candidateInternal(Type t) {
|
||||
@@ -674,8 +646,7 @@ private module Internal {
|
||||
|
||||
/** Holds if types `x` and `y` are equal modulo type parameters. */
|
||||
private predicate sameModuloTypeParameters(Type x, Type y) {
|
||||
y = candidateInternal(x)
|
||||
and
|
||||
y = candidateInternal(x) and
|
||||
(
|
||||
x = y
|
||||
or
|
||||
@@ -683,21 +654,18 @@ private module Internal {
|
||||
or
|
||||
y instanceof TypeParameter
|
||||
or
|
||||
// They must have the same kind.
|
||||
(
|
||||
// They must have the same kind.
|
||||
(
|
||||
x instanceof PointerType and y instanceof PointerType
|
||||
or
|
||||
x instanceof NullableType and y instanceof NullableType
|
||||
or
|
||||
x.(ArrayType).hasSameShapeAs((ArrayType)y)
|
||||
or
|
||||
x.(ConstructedType).getUnboundGeneric() = y.(ConstructedType).getUnboundGeneric()
|
||||
)
|
||||
and
|
||||
// All of their corresponding children must be structurally equal
|
||||
sameChildrenModuloTypeParameters(x, y, x.getNumberOfChildren() - 1)
|
||||
)
|
||||
x instanceof PointerType and y instanceof PointerType
|
||||
or
|
||||
x instanceof NullableType and y instanceof NullableType
|
||||
or
|
||||
x.(ArrayType).hasSameShapeAs(y.(ArrayType))
|
||||
or
|
||||
x.(ConstructedType).getUnboundGeneric() = y.(ConstructedType).getUnboundGeneric()
|
||||
) and
|
||||
// All of their corresponding children must be structurally equal
|
||||
sameChildrenModuloTypeParameters(x, y, x.getNumberOfChildren() - 1)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -713,8 +681,7 @@ private module Internal {
|
||||
sameModuloTypeParameters(xChild, yChild)
|
||||
)
|
||||
or
|
||||
exists(Type xChild, Type yChild |
|
||||
sameChildrenModuloTypeParameters(x, y, i - 1) |
|
||||
exists(Type xChild, Type yChild | sameChildrenModuloTypeParameters(x, y, i - 1) |
|
||||
candidateChildren(x, i, xChild, yChild) and
|
||||
yChild = y.getChild(i) and
|
||||
sameModuloTypeParameters(xChild, yChild)
|
||||
@@ -722,8 +689,7 @@ private module Internal {
|
||||
}
|
||||
|
||||
private predicate candidateChildren(Type x, int i, Type xChild, Type yChild) {
|
||||
exists(Type y |
|
||||
y = candidateInternal(x) |
|
||||
exists(Type y | y = candidateInternal(x) |
|
||||
xChild = x.getChild(i) and
|
||||
yChild = y.getChild(i)
|
||||
)
|
||||
@@ -735,22 +701,19 @@ private module Internal {
|
||||
*/
|
||||
private class QualifierTypeWithTypeParameters extends StructurallyComparedModuloTypeParameters {
|
||||
QualifierTypeWithTypeParameters() {
|
||||
containsTypeParameters()
|
||||
and
|
||||
any(DispatchCallImpl dc).hasQualifierType(this, false)
|
||||
and
|
||||
containsTypeParameters() and
|
||||
any(DispatchCallImpl dc).hasQualifierType(this, false) and
|
||||
exists(Type arg | arg = getAChild() | not arg instanceof TypeParameter)
|
||||
}
|
||||
|
||||
override ConstructedType getACandidate() {
|
||||
not result.containsTypeParameters()
|
||||
and
|
||||
not result.containsTypeParameters() and
|
||||
this.(ConstructedType).getUnboundGeneric() = result.getUnboundGeneric()
|
||||
}
|
||||
}
|
||||
|
||||
/** A reflection-based call or a call using dynamic types. */
|
||||
private abstract class DispatchReflectionOrDynamicCall extends DispatchCallImpl {
|
||||
abstract private class DispatchReflectionOrDynamicCall extends DispatchCallImpl {
|
||||
/** Gets the name of the callable being called in this call. */
|
||||
abstract string getName();
|
||||
|
||||
@@ -792,8 +755,7 @@ private module Internal {
|
||||
this.hasQualifierType(result, true)
|
||||
or
|
||||
// Non-exact qualifier type
|
||||
exists(Type qualifierType |
|
||||
this.hasNonExactQualifierType(qualifierType) |
|
||||
exists(Type qualifierType | this.hasNonExactQualifierType(qualifierType) |
|
||||
result = getANonExactQualifierSubType(qualifierType)
|
||||
)
|
||||
}
|
||||
@@ -807,8 +769,7 @@ private module Internal {
|
||||
* `dynamic`).
|
||||
*/
|
||||
private predicate hasUnknownQualifierType() {
|
||||
exists(Type qualifierType |
|
||||
this.hasNonExactQualifierType(qualifierType) |
|
||||
exists(Type qualifierType | this.hasNonExactQualifierType(qualifierType) |
|
||||
isUnknownType(qualifierType)
|
||||
)
|
||||
}
|
||||
@@ -818,18 +779,16 @@ private module Internal {
|
||||
* names and number of parameters. This set is further reduced in
|
||||
* `getADynamicTarget()` by taking type information into account.
|
||||
*/
|
||||
|
||||
override Callable getAStaticTarget() {
|
||||
result = getACallableWithMatchingName()
|
||||
and
|
||||
result = getACallableWithMatchingName() and
|
||||
exists(int minArgs |
|
||||
minArgs = count(Parameter p |
|
||||
p = result.getAParameter() and
|
||||
not p.hasDefaultValue() and
|
||||
not p.isParams()
|
||||
)
|
||||
and
|
||||
getNumberOfArguments() >= minArgs
|
||||
and
|
||||
p = result.getAParameter() and
|
||||
not p.hasDefaultValue() and
|
||||
not p.isParams()
|
||||
) and
|
||||
getNumberOfArguments() >= minArgs and
|
||||
(result.(Method).hasParams() or getNumberOfArguments() <= result.getNumberOfParameters())
|
||||
)
|
||||
}
|
||||
@@ -857,13 +816,12 @@ private module Internal {
|
||||
* conflicting types (for example, `Tuple<int, string>` is considered
|
||||
* compatible with `Tuple<T, T>`).
|
||||
*/
|
||||
|
||||
override RuntimeCallable getADynamicTarget() {
|
||||
// Condition 1
|
||||
result = getADynamicTargetCandidate()
|
||||
and
|
||||
result = getADynamicTargetCandidate() and
|
||||
// Condition 2
|
||||
forall(int i |
|
||||
i in [0 .. getNumberOfArguments() - 1] |
|
||||
forall(int i | i in [0 .. getNumberOfArguments() - 1] |
|
||||
result = getADynamicTargetCandidateWithCompatibleArg(i)
|
||||
)
|
||||
}
|
||||
@@ -873,13 +831,13 @@ private module Internal {
|
||||
result = getADynamicTargetCandidateWithCompatibleArg2(i)
|
||||
}
|
||||
|
||||
pragma [noinline]
|
||||
pragma[noinline]
|
||||
private RuntimeCallable getADynamicTargetCandidateWithCompatibleArg1(int i) {
|
||||
result = this.getADynamicTargetCandidate()
|
||||
and
|
||||
result = this.getADynamicTargetCandidate() and
|
||||
exists(Type parameterType, Type argumentType |
|
||||
parameterType = this.getAParameterType(result, i) and
|
||||
argumentType = getAPossibleType(this.getArgument(i), _) |
|
||||
argumentType = getAPossibleType(this.getArgument(i), _)
|
||||
|
|
||||
argumentType.isImplicitlyConvertibleTo(parameterType)
|
||||
or
|
||||
argumentType instanceof NullType and
|
||||
@@ -891,10 +849,8 @@ private module Internal {
|
||||
}
|
||||
|
||||
private Type getAParameterType(RuntimeCallable c, int i) {
|
||||
exists(this.getArgument(i))
|
||||
and
|
||||
c = this.getADynamicTargetCandidate()
|
||||
and
|
||||
exists(this.getArgument(i)) and
|
||||
c = this.getADynamicTargetCandidate() and
|
||||
(
|
||||
result = c.getParameter(i).getType()
|
||||
or
|
||||
@@ -903,18 +859,16 @@ private module Internal {
|
||||
)
|
||||
}
|
||||
|
||||
pragma [noinline]
|
||||
pragma[noinline]
|
||||
private RuntimeCallable getADynamicTargetCandidateWithCompatibleArg2(int i) {
|
||||
result = this.getADynamicTargetCandidate()
|
||||
and
|
||||
exists(Type parameterType, Type t |
|
||||
parameterType = this.getAParameterType(result, i) |
|
||||
result = this.getADynamicTargetCandidate() and
|
||||
exists(Type parameterType, Type t | parameterType = this.getAParameterType(result, i) |
|
||||
this.argumentConvConstExpr(i, t) and
|
||||
t.isImplicitlyConvertibleTo(parameterType)
|
||||
)
|
||||
}
|
||||
|
||||
pragma [noinline]
|
||||
pragma[noinline]
|
||||
private predicate argumentConvConstExpr(int i, Type t) {
|
||||
convConstantExpr(this.getArgument(i), t)
|
||||
}
|
||||
@@ -931,8 +885,7 @@ private module Internal {
|
||||
* type of one of the arguments.
|
||||
*/
|
||||
RuntimeCallable getADynamicTargetCandidate() {
|
||||
result = getAStaticTarget()
|
||||
and
|
||||
result = getAStaticTarget() and
|
||||
(
|
||||
result = getADynamicTargetCandidateInstanceMethod(this.getAQualifierType())
|
||||
or
|
||||
@@ -948,14 +901,14 @@ private module Internal {
|
||||
)
|
||||
}
|
||||
|
||||
pragma [noinline]
|
||||
pragma[noinline]
|
||||
private RuntimeOperator getADynamicTargetCandidateOperator() {
|
||||
result = getAStaticTarget() and
|
||||
result.getDeclaringType() = result.getAParameter().getType()
|
||||
}
|
||||
}
|
||||
|
||||
pragma [noinline]
|
||||
pragma[noinline]
|
||||
private RuntimeInstanceMethod getADynamicTargetCandidateInstanceMethod(RefType qualifierType) {
|
||||
exists(DispatchReflectionOrDynamicCall c |
|
||||
result = c.getAStaticTarget() and
|
||||
@@ -963,7 +916,7 @@ private module Internal {
|
||||
)
|
||||
}
|
||||
|
||||
pragma [noinline]
|
||||
pragma[noinline]
|
||||
private RuntimeInstanceAccessor getADynamicTargetCandidateInstanceAccessor(RefType qualifierType) {
|
||||
exists(DispatchReflectionOrDynamicCall c |
|
||||
result = c.getAStaticTarget() and
|
||||
@@ -976,7 +929,7 @@ private module Internal {
|
||||
t instanceof ObjectType
|
||||
}
|
||||
|
||||
pragma [noinline]
|
||||
pragma[noinline]
|
||||
private RefType getANonExactQualifierSubType(Type qualifierType) {
|
||||
isRelevantReflectionOrDynamicQualifierType(result) and
|
||||
result.isImplicitlyConvertibleTo(qualifierType) and
|
||||
@@ -984,29 +937,32 @@ private module Internal {
|
||||
not result instanceof NullType // null types are implicitly convertible to all types, but not actual subtypes
|
||||
}
|
||||
|
||||
pragma [noinline]
|
||||
pragma[noinline]
|
||||
private predicate isRelevantReflectionOrDynamicQualifierType(RefType rt) {
|
||||
rt.hasCallable(any(DispatchReflectionOrDynamicCall c).getAStaticTarget())
|
||||
}
|
||||
|
||||
private predicate reflectionOrDynamicArgEqualsParamModuloTypeParameters(Type argumentType, Type parameterType) {
|
||||
exists(Type t |
|
||||
reflectionOrDynamicArgumentTypeIsImplicitlyConvertibleTo(argumentType,t) |
|
||||
t.(ReflectionOrDynamicCallArgumentWithTypeParameters).equalsModuloTypeParameters(parameterType)
|
||||
private predicate reflectionOrDynamicArgEqualsParamModuloTypeParameters(
|
||||
Type argumentType, Type parameterType
|
||||
) {
|
||||
exists(Type t | reflectionOrDynamicArgumentTypeIsImplicitlyConvertibleTo(argumentType, t) |
|
||||
t
|
||||
.(ReflectionOrDynamicCallArgumentWithTypeParameters)
|
||||
.equalsModuloTypeParameters(parameterType)
|
||||
)
|
||||
}
|
||||
|
||||
pragma [noinline]
|
||||
private predicate reflectionOrDynamicArgumentTypeIsImplicitlyConvertibleTo(Type argumentType, Type t) {
|
||||
pragma[noinline]
|
||||
private predicate reflectionOrDynamicArgumentTypeIsImplicitlyConvertibleTo(
|
||||
Type argumentType, Type t
|
||||
) {
|
||||
isReflectionOrDynamicArgumentType(argumentType) and
|
||||
argumentType.isImplicitlyConvertibleTo(t)
|
||||
}
|
||||
|
||||
pragma [noinline]
|
||||
pragma[noinline]
|
||||
private predicate isReflectionOrDynamicArgumentType(Type t) {
|
||||
exists(DispatchReflectionOrDynamicCall c |
|
||||
t = getAPossibleType(c.getArgument(_), _)
|
||||
)
|
||||
exists(DispatchReflectionOrDynamicCall c | t = getAPossibleType(c.getArgument(_), _))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1017,17 +973,19 @@ private module Internal {
|
||||
*/
|
||||
private predicate isReflectionOrDynamicCallArgumentWithTypeParameters(Type argType, Type paramType) {
|
||||
exists(DispatchReflectionOrDynamicCall call, Parameter p, int i, int j |
|
||||
p = call.getAStaticTarget().getParameter(i)
|
||||
and
|
||||
if p.isParams() then (
|
||||
j >= i and
|
||||
paramType = p.getType().(ArrayType).getElementType()
|
||||
) else (
|
||||
i = j and
|
||||
paramType = p.getType()
|
||||
)
|
||||
and
|
||||
argType = getAPossibleType(call.getArgument(j), _) |
|
||||
p = call.getAStaticTarget().getParameter(i) and
|
||||
(
|
||||
if p.isParams()
|
||||
then (
|
||||
j >= i and
|
||||
paramType = p.getType().(ArrayType).getElementType()
|
||||
) else (
|
||||
i = j and
|
||||
paramType = p.getType()
|
||||
)
|
||||
) and
|
||||
argType = getAPossibleType(call.getArgument(j), _)
|
||||
|
|
||||
argType.containsTypeParameters()
|
||||
or
|
||||
paramType.containsTypeParameters()
|
||||
@@ -1050,14 +1008,11 @@ private module Internal {
|
||||
}
|
||||
|
||||
/** A call using reflection. */
|
||||
private class DispatchReflectionCall extends DispatchReflectionOrDynamicCall, TDispatchReflectionCall {
|
||||
override MethodCall getCall() {
|
||||
this = TDispatchReflectionCall(result, _, _, _, _)
|
||||
}
|
||||
private class DispatchReflectionCall extends DispatchReflectionOrDynamicCall,
|
||||
TDispatchReflectionCall {
|
||||
override MethodCall getCall() { this = TDispatchReflectionCall(result, _, _, _, _) }
|
||||
|
||||
override string getName() {
|
||||
this = TDispatchReflectionCall(_, result, _, _, _)
|
||||
}
|
||||
override string getName() { this = TDispatchReflectionCall(_, result, _, _, _) }
|
||||
|
||||
override Expr getQualifier() {
|
||||
this = TDispatchReflectionCall(_, _, result, _, _) and
|
||||
@@ -1067,10 +1022,8 @@ private module Internal {
|
||||
/** Gets the type containing the static callable being called, if any. */
|
||||
private ValueOrRefType getStaticType() {
|
||||
exists(Expr object, Expr qualifier |
|
||||
this = TDispatchReflectionCall(_, _, object, qualifier, _)
|
||||
and
|
||||
object instanceof NullLiteral
|
||||
and
|
||||
this = TDispatchReflectionCall(_, _, object, qualifier, _) and
|
||||
object instanceof NullLiteral and
|
||||
(
|
||||
result = getAMethodCallArgSource(qualifier).(TypeofExpr).getTypeAccess().getType()
|
||||
or
|
||||
@@ -1095,48 +1048,38 @@ private module Internal {
|
||||
// Does not take named arguments into account
|
||||
override Expr getArgument(int i) {
|
||||
exists(int args, ArrayCreation ac |
|
||||
this = TDispatchReflectionCall(_, _, _, _, args)
|
||||
and
|
||||
ac = getAMethodCallArgSource(getCall().getArgument(args))
|
||||
and
|
||||
this = TDispatchReflectionCall(_, _, _, _, args) and
|
||||
ac = getAMethodCallArgSource(getCall().getArgument(args)) and
|
||||
result = ac.getInitializer().getElement(i)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A method call using dynamic types. */
|
||||
private class DispatchDynamicMethodCall extends DispatchReflectionOrDynamicCall, TDispatchDynamicMethodCall {
|
||||
override DynamicMethodCall getCall() {
|
||||
this = TDispatchDynamicMethodCall(result)
|
||||
}
|
||||
private class DispatchDynamicMethodCall extends DispatchReflectionOrDynamicCall,
|
||||
TDispatchDynamicMethodCall {
|
||||
override DynamicMethodCall getCall() { this = TDispatchDynamicMethodCall(result) }
|
||||
|
||||
override string getName() {
|
||||
result = getCall().getLateBoundTargetName()
|
||||
}
|
||||
override string getName() { result = getCall().getLateBoundTargetName() }
|
||||
|
||||
override Expr getQualifier() {
|
||||
result = getCall().getQualifier()
|
||||
}
|
||||
override Expr getQualifier() { result = getCall().getQualifier() }
|
||||
|
||||
override RuntimeMethod getADynamicTargetCandidate() {
|
||||
if exists(getCall().getTarget()) then
|
||||
if exists(getCall().getTarget())
|
||||
then
|
||||
// static method call
|
||||
result = getCall().getTarget()
|
||||
else
|
||||
result = DispatchReflectionOrDynamicCall.super.getADynamicTargetCandidate()
|
||||
else result = DispatchReflectionOrDynamicCall.super.getADynamicTargetCandidate()
|
||||
}
|
||||
|
||||
// Does not take named arguments into account
|
||||
override Expr getArgument(int i) {
|
||||
result = getCall().getArgument(i)
|
||||
}
|
||||
override Expr getArgument(int i) { result = getCall().getArgument(i) }
|
||||
}
|
||||
|
||||
/** An operator call using dynamic types. */
|
||||
private class DispatchDynamicOperatorCall extends DispatchReflectionOrDynamicCall, TDispatchDynamicOperatorCall {
|
||||
override DynamicOperatorCall getCall() {
|
||||
this = TDispatchDynamicOperatorCall(result)
|
||||
}
|
||||
private class DispatchDynamicOperatorCall extends DispatchReflectionOrDynamicCall,
|
||||
TDispatchDynamicOperatorCall {
|
||||
override DynamicOperatorCall getCall() { this = TDispatchDynamicOperatorCall(result) }
|
||||
|
||||
override string getName() {
|
||||
exists(Operator o |
|
||||
@@ -1145,33 +1088,25 @@ private module Internal {
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getQualifier() {
|
||||
none()
|
||||
}
|
||||
override Expr getQualifier() { none() }
|
||||
|
||||
override Expr getArgument(int i) {
|
||||
result = getCall().getArgument(i)
|
||||
}
|
||||
override Expr getArgument(int i) { result = getCall().getArgument(i) }
|
||||
}
|
||||
|
||||
/** A (potential) call to a property accessor using dynamic types. */
|
||||
private class DispatchDynamicMemberAccess extends DispatchReflectionOrDynamicCall, TDispatchDynamicMemberAccess {
|
||||
override DynamicMemberAccess getCall() {
|
||||
this = TDispatchDynamicMemberAccess(result)
|
||||
}
|
||||
private class DispatchDynamicMemberAccess extends DispatchReflectionOrDynamicCall,
|
||||
TDispatchDynamicMemberAccess {
|
||||
override DynamicMemberAccess getCall() { this = TDispatchDynamicMemberAccess(result) }
|
||||
|
||||
override string getName() {
|
||||
exists(DynamicMemberAccess dma |
|
||||
dma = getCall() |
|
||||
exists(DynamicMemberAccess dma | dma = getCall() |
|
||||
result = "get_" + dma.(DynamicMemberRead).getLateBoundTargetName()
|
||||
or
|
||||
result = "set_" + dma.(DynamicMemberWrite).getLateBoundTargetName()
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getQualifier() {
|
||||
result = getCall().getQualifier()
|
||||
}
|
||||
override Expr getQualifier() { result = getCall().getQualifier() }
|
||||
|
||||
override Expr getArgument(int i) {
|
||||
// Only calls to setters have an argument
|
||||
@@ -1184,27 +1119,22 @@ private module Internal {
|
||||
}
|
||||
|
||||
/** A (potential) call to an indexer accessor using dynamic types. */
|
||||
private class DispatchDynamicElementAccess extends DispatchReflectionOrDynamicCall, TDispatchDynamicElementAccess {
|
||||
override DynamicElementAccess getCall() {
|
||||
this = TDispatchDynamicElementAccess(result)
|
||||
}
|
||||
private class DispatchDynamicElementAccess extends DispatchReflectionOrDynamicCall,
|
||||
TDispatchDynamicElementAccess {
|
||||
override DynamicElementAccess getCall() { this = TDispatchDynamicElementAccess(result) }
|
||||
|
||||
override string getName() {
|
||||
exists(DynamicElementAccess dea |
|
||||
dea = getCall() |
|
||||
exists(DynamicElementAccess dea | dea = getCall() |
|
||||
dea instanceof DynamicElementRead and result = "get_Item"
|
||||
or
|
||||
dea instanceof DynamicElementWrite and result = "set_Item"
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getQualifier() {
|
||||
result = getCall().getQualifier()
|
||||
}
|
||||
override Expr getQualifier() { result = getCall().getQualifier() }
|
||||
|
||||
override Expr getArgument(int i) {
|
||||
exists(DynamicElementAccess dea |
|
||||
dea = getCall() |
|
||||
exists(DynamicElementAccess dea | dea = getCall() |
|
||||
result = dea.getIndex(i)
|
||||
or
|
||||
// Calls to setters have an extra argument
|
||||
@@ -1216,71 +1146,51 @@ private module Internal {
|
||||
}
|
||||
|
||||
/** A (potential) call to an event accessor using dynamic types. */
|
||||
private class DispatchDynamicEventAccess extends DispatchReflectionOrDynamicCall, TDispatchDynamicEventAccess {
|
||||
private class DispatchDynamicEventAccess extends DispatchReflectionOrDynamicCall,
|
||||
TDispatchDynamicEventAccess {
|
||||
override AssignArithmeticOperation getCall() {
|
||||
this = TDispatchDynamicEventAccess(result, _, _)
|
||||
}
|
||||
|
||||
override string getName() {
|
||||
this = TDispatchDynamicEventAccess(_, _, result)
|
||||
}
|
||||
override string getName() { this = TDispatchDynamicEventAccess(_, _, result) }
|
||||
|
||||
override Expr getQualifier() {
|
||||
result = any(DynamicMemberAccess dma | this = TDispatchDynamicEventAccess(_, dma, _)).getQualifier()
|
||||
result = any(DynamicMemberAccess dma | this = TDispatchDynamicEventAccess(_, dma, _))
|
||||
.getQualifier()
|
||||
}
|
||||
|
||||
override Expr getArgument(int i) {
|
||||
i = 0 and result = getCall().getRValue()
|
||||
}
|
||||
override Expr getArgument(int i) { i = 0 and result = getCall().getRValue() }
|
||||
}
|
||||
|
||||
/** A call to a constructor using dynamic types. */
|
||||
private class DispatchDynamicObjectCreation extends DispatchReflectionOrDynamicCall, TDispatchDynamicObjectCreation {
|
||||
override DynamicObjectCreation getCall() {
|
||||
this = TDispatchDynamicObjectCreation(result)
|
||||
}
|
||||
private class DispatchDynamicObjectCreation extends DispatchReflectionOrDynamicCall,
|
||||
TDispatchDynamicObjectCreation {
|
||||
override DynamicObjectCreation getCall() { this = TDispatchDynamicObjectCreation(result) }
|
||||
|
||||
override string getName() {
|
||||
none()
|
||||
}
|
||||
override string getName() { none() }
|
||||
|
||||
override Expr getQualifier() {
|
||||
none()
|
||||
}
|
||||
override Expr getQualifier() { none() }
|
||||
|
||||
override Expr getArgument(int i) {
|
||||
result = getCall().getArgument(i)
|
||||
}
|
||||
override Expr getArgument(int i) { result = getCall().getArgument(i) }
|
||||
|
||||
override RuntimeCallable getADynamicTargetCandidate() {
|
||||
result = getCall().getTarget()
|
||||
}
|
||||
override RuntimeCallable getADynamicTargetCandidate() { result = getCall().getTarget() }
|
||||
}
|
||||
|
||||
/** A call where the target can be resolved statically. */
|
||||
private class DispatchStaticCall extends DispatchCallImpl, TDispatchStaticCall {
|
||||
override Call getCall() {
|
||||
this = TDispatchStaticCall(result)
|
||||
}
|
||||
override Call getCall() { this = TDispatchStaticCall(result) }
|
||||
|
||||
override Expr getQualifier() {
|
||||
none()
|
||||
}
|
||||
override Expr getQualifier() { none() }
|
||||
|
||||
override Expr getArgument(int i) {
|
||||
exists(Call call, Parameter p |
|
||||
call = this.getCall() |
|
||||
exists(Call call, Parameter p | call = this.getCall() |
|
||||
p = call.getTarget().getParameter(i) and
|
||||
result = call.getArgumentForParameter(p)
|
||||
)
|
||||
}
|
||||
|
||||
override Callable getAStaticTarget() {
|
||||
result = getCall().getTarget()
|
||||
}
|
||||
override Callable getAStaticTarget() { result = getCall().getTarget() }
|
||||
|
||||
override RuntimeCallable getADynamicTarget() {
|
||||
result = getCall().getTarget()
|
||||
}
|
||||
override RuntimeCallable getADynamicTarget() { result = getCall().getTarget() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Provides classes that define callables that can be overridden or
|
||||
* implemented.
|
||||
*/
|
||||
|
||||
import csharp
|
||||
|
||||
/**
|
||||
@@ -134,8 +135,7 @@ class OverridableCallable extends Callable {
|
||||
* - `C2.M = C2.M.getInherited(C3)`.
|
||||
*/
|
||||
Callable getInherited(SourceDeclarationType t) {
|
||||
exists(Callable sourceDecl |
|
||||
result = getInherited1(t, sourceDecl) |
|
||||
exists(Callable sourceDecl | result = getInherited1(t, sourceDecl) |
|
||||
hasSourceDeclarationCallable(t, sourceDecl)
|
||||
)
|
||||
}
|
||||
@@ -159,13 +159,13 @@ class OverridableCallable extends Callable {
|
||||
sourceDecl = result.getSourceDeclaration()
|
||||
}
|
||||
|
||||
pragma [noinline]
|
||||
pragma[noinline]
|
||||
private ValueOrRefType hasOverrider(Callable c) {
|
||||
c = this.getAnOverrider*() and
|
||||
result = c.getDeclaringType()
|
||||
}
|
||||
|
||||
pragma [noinline]
|
||||
pragma[noinline]
|
||||
private ValueOrRefType hasOverridingImplementor(Callable c) {
|
||||
c = this.getAnOverridingImplementor() and
|
||||
result = c.getDeclaringType()
|
||||
@@ -207,10 +207,8 @@ class OverridableCallable extends Callable {
|
||||
}
|
||||
|
||||
private TypeWithoutTypeParameters getAnOverriderBaseType(ValueOrRefType t) {
|
||||
exists(getAnOverriderAux(t))
|
||||
and
|
||||
exists(Type t0 |
|
||||
t0 = t.getABaseType*() |
|
||||
exists(getAnOverriderAux(t)) and
|
||||
exists(Type t0 | t0 = t.getABaseType*() |
|
||||
result = t0
|
||||
or
|
||||
result = t0.(ConstructedType).getUnboundGeneric()
|
||||
@@ -238,8 +236,7 @@ class OverridableCallable extends Callable {
|
||||
}
|
||||
|
||||
private predicate declaredInTypeWithTypeParameters() {
|
||||
exists(ValueOrRefType t |
|
||||
t = getDeclaringType() |
|
||||
exists(ValueOrRefType t | t = getDeclaringType() |
|
||||
t.containsTypeParameters()
|
||||
or
|
||||
// Access to a local callable, `this.M()`, in a generic class
|
||||
@@ -250,27 +247,18 @@ class OverridableCallable extends Callable {
|
||||
}
|
||||
}
|
||||
|
||||
pragma [noinline]
|
||||
pragma[noinline]
|
||||
private predicate hasSourceDeclarationCallable(ValueOrRefType t, Callable sourceDecl) {
|
||||
exists(Callable c |
|
||||
t.hasCallable(c) |
|
||||
sourceDecl = c.getSourceDeclaration()
|
||||
)
|
||||
exists(Callable c | t.hasCallable(c) | sourceDecl = c.getSourceDeclaration())
|
||||
}
|
||||
|
||||
/** An overridable method. */
|
||||
class OverridableMethod extends Method, OverridableCallable {
|
||||
override Method getAnOverrider() {
|
||||
result = Method.super.getAnOverrider()
|
||||
}
|
||||
override Method getAnOverrider() { result = Method.super.getAnOverrider() }
|
||||
|
||||
override Method getAnImplementor(ValueOrRefType t) {
|
||||
result = Method.super.getAnImplementor(t)
|
||||
}
|
||||
override Method getAnImplementor(ValueOrRefType t) { result = Method.super.getAnImplementor(t) }
|
||||
|
||||
override Method getAnUltimateImplementor() {
|
||||
result = Method.super.getAnUltimateImplementor()
|
||||
}
|
||||
override Method getAnUltimateImplementor() { result = Method.super.getAnUltimateImplementor() }
|
||||
|
||||
override Method getInherited(SourceDeclarationType t) {
|
||||
result = OverridableCallable.super.getInherited(t)
|
||||
@@ -283,9 +271,7 @@ class OverridableMethod extends Method, OverridableCallable {
|
||||
|
||||
/** An overridable accessor. */
|
||||
class OverridableAccessor extends Accessor, OverridableCallable {
|
||||
override Accessor getAnOverrider() {
|
||||
overrides(result, this)
|
||||
}
|
||||
override Accessor getAnOverrider() { overrides(result, this) }
|
||||
|
||||
override Accessor getAnImplementor(ValueOrRefType t) {
|
||||
exists(Virtualizable implementor, int kind |
|
||||
@@ -337,14 +323,10 @@ private int getAccessorKind(Accessor a) {
|
||||
|
||||
/** A type not containing type parameters. */
|
||||
class TypeWithoutTypeParameters extends Type {
|
||||
TypeWithoutTypeParameters() {
|
||||
not containsTypeParameters()
|
||||
}
|
||||
TypeWithoutTypeParameters() { not containsTypeParameters() }
|
||||
}
|
||||
|
||||
/** A source declared type. */
|
||||
class SourceDeclarationType extends TypeWithoutTypeParameters {
|
||||
SourceDeclarationType() {
|
||||
this = getSourceDeclaration()
|
||||
}
|
||||
SourceDeclarationType() { this = getSourceDeclaration() }
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* those callables that may actually be called at run-time (that is, neither
|
||||
* abstract callables nor callables defined in an interface).
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import cil
|
||||
private import dotnet
|
||||
@@ -25,17 +26,11 @@ class RuntimeMethod extends RuntimeCallable {
|
||||
this instanceof CIL::Method
|
||||
}
|
||||
|
||||
predicate isStatic() {
|
||||
this.(Method).isStatic() or this.(CIL::Method).isStatic()
|
||||
}
|
||||
predicate isStatic() { this.(Method).isStatic() or this.(CIL::Method).isStatic() }
|
||||
}
|
||||
|
||||
/** A run-time instance method. */
|
||||
class RuntimeInstanceMethod extends RuntimeMethod {
|
||||
RuntimeInstanceMethod() {
|
||||
not isStatic()
|
||||
}
|
||||
}
|
||||
class RuntimeInstanceMethod extends RuntimeMethod { RuntimeInstanceMethod() { not isStatic() } }
|
||||
|
||||
/** A run-time operator. */
|
||||
class RuntimeOperator extends Operator, RuntimeCallable { }
|
||||
@@ -45,7 +40,5 @@ class RuntimeAccessor extends Accessor, RuntimeCallable { }
|
||||
|
||||
/** A run-time instance accessor. */
|
||||
class RuntimeInstanceAccessor extends RuntimeAccessor {
|
||||
RuntimeInstanceAccessor() {
|
||||
not isStatic()
|
||||
}
|
||||
RuntimeInstanceAccessor() { not isStatic() }
|
||||
}
|
||||
|
||||
@@ -23,16 +23,17 @@ class Access extends Expr, @access_expr {
|
||||
/**
|
||||
* An internal helper module to calculate accesses and their targets.
|
||||
*/
|
||||
private cached module AccessImpl {
|
||||
cached predicate isAccess(@access_expr ae) {
|
||||
cached
|
||||
private module AccessImpl {
|
||||
cached
|
||||
predicate isAccess(@access_expr ae) {
|
||||
isLocalVariableAccess(ae) implies exists(getLocalVariableAccessTarget(ae))
|
||||
}
|
||||
|
||||
private predicate isLocalVariableAccess(@local_variable_access a) {
|
||||
any()
|
||||
}
|
||||
private predicate isLocalVariableAccess(@local_variable_access a) { any() }
|
||||
|
||||
cached LocalVariable getLocalVariableAccessTarget(@local_variable_access a) {
|
||||
cached
|
||||
LocalVariable getLocalVariableAccessTarget(@local_variable_access a) {
|
||||
result = getLocalVariableAccessTargetNoDecl(a) or
|
||||
result = getLocalVariableAccessTargetDecl(a)
|
||||
}
|
||||
@@ -74,9 +75,7 @@ private cached module AccessImpl {
|
||||
* ```
|
||||
*/
|
||||
class ThisAccess extends Access, @this_access_expr {
|
||||
override Class getTarget() {
|
||||
result = this.getEnclosingCallable().getDeclaringType()
|
||||
}
|
||||
override Class getTarget() { result = this.getEnclosingCallable().getDeclaringType() }
|
||||
|
||||
override string toString() { result = "this access" }
|
||||
}
|
||||
@@ -108,8 +107,7 @@ class BaseAccess extends Access, @base_access_expr {
|
||||
*/
|
||||
class MemberAccess extends Access, QualifiableExpr, @member_access_expr {
|
||||
override predicate hasImplicitThisQualifier() {
|
||||
QualifiableExpr.super.hasImplicitThisQualifier()
|
||||
and
|
||||
QualifiableExpr.super.hasImplicitThisQualifier() and
|
||||
not exists(MemberInitializer mi | mi.getLValue() = this)
|
||||
}
|
||||
|
||||
@@ -132,17 +130,13 @@ class AssignableAccess extends Access, @assignable_access_expr {
|
||||
* Holds if this access passes the assignable being accessed as an `out`
|
||||
* argument in a method call.
|
||||
*/
|
||||
predicate isOutArgument() {
|
||||
expr_argument(this, 2)
|
||||
}
|
||||
predicate isOutArgument() { expr_argument(this, 2) }
|
||||
|
||||
/**
|
||||
* Holds if this access passes the assignable being accessed as a `ref`
|
||||
* argument in a method call.
|
||||
*/
|
||||
predicate isRefArgument() {
|
||||
expr_argument(this, 1)
|
||||
}
|
||||
predicate isRefArgument() { expr_argument(this, 1) }
|
||||
|
||||
/**
|
||||
* Holds if this access passes the assignable being accessed as an `out`
|
||||
@@ -157,9 +151,7 @@ class AssignableAccess extends Access, @assignable_access_expr {
|
||||
* Holds if this access passes the assignable being accessed as an `in`
|
||||
* argument in a method call.
|
||||
*/
|
||||
predicate isInArgument() {
|
||||
expr_argument(this, 3)
|
||||
}
|
||||
predicate isInArgument() { expr_argument(this, 3) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -167,9 +159,7 @@ class AssignableAccess extends Access, @assignable_access_expr {
|
||||
* (`LocalScopeVariableAccess`) or an access to a field (`FieldAccess`).
|
||||
*/
|
||||
class VariableAccess extends AssignableAccess, @variable_access_expr {
|
||||
override Variable getTarget() {
|
||||
expr_access(this, result)
|
||||
}
|
||||
override Variable getTarget() { expr_access(this, result) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,13 +168,9 @@ class VariableAccess extends AssignableAccess, @variable_access_expr {
|
||||
* access to a field (`FieldRead`).
|
||||
*/
|
||||
class VariableRead extends VariableAccess, AssignableRead {
|
||||
override VariableRead getANextRead() {
|
||||
result = AssignableRead.super.getANextRead()
|
||||
}
|
||||
override VariableRead getANextRead() { result = AssignableRead.super.getANextRead() }
|
||||
|
||||
override VariableRead getAReachableRead() {
|
||||
result = AssignableRead.super.getAReachableRead()
|
||||
}
|
||||
override VariableRead getAReachableRead() { result = AssignableRead.super.getAReachableRead() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -199,9 +185,7 @@ class VariableWrite extends VariableAccess, AssignableWrite { }
|
||||
* (`ParameterAccess`) or an access to a local variable (`LocalVariableAccess`).
|
||||
*/
|
||||
class LocalScopeVariableAccess extends VariableAccess, @local_scope_variable_access_expr {
|
||||
override LocalScopeVariable getTarget() {
|
||||
expr_access(this, result)
|
||||
}
|
||||
override LocalScopeVariable getTarget() { expr_access(this, result) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -210,9 +194,7 @@ class LocalScopeVariableAccess extends VariableAccess, @local_scope_variable_acc
|
||||
* (`LocalVariableRead`).
|
||||
*/
|
||||
class LocalScopeVariableRead extends LocalScopeVariableAccess, VariableRead {
|
||||
override LocalScopeVariableRead getANextRead() {
|
||||
result = VariableRead.super.getANextRead()
|
||||
}
|
||||
override LocalScopeVariableRead getANextRead() { result = VariableRead.super.getANextRead() }
|
||||
|
||||
override LocalScopeVariableRead getAReachableRead() {
|
||||
result = VariableRead.super.getAReachableRead()
|
||||
@@ -236,13 +218,9 @@ class LocalScopeVariableWrite extends LocalScopeVariableAccess, VariableWrite {
|
||||
* ```
|
||||
*/
|
||||
class ParameterAccess extends LocalScopeVariableAccess, @parameter_access_expr {
|
||||
override Parameter getTarget() {
|
||||
expr_access(this, result)
|
||||
}
|
||||
override Parameter getTarget() { expr_access(this, result) }
|
||||
|
||||
override string toString() {
|
||||
result = "access to parameter " + this.getTarget().getName()
|
||||
}
|
||||
override string toString() { result = "access to parameter " + this.getTarget().getName() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -256,9 +234,7 @@ class ParameterAccess extends LocalScopeVariableAccess, @parameter_access_expr {
|
||||
* ```
|
||||
*/
|
||||
class ParameterRead extends ParameterAccess, LocalScopeVariableRead {
|
||||
override ParameterRead getANextRead() {
|
||||
result = LocalScopeVariableRead.super.getANextRead()
|
||||
}
|
||||
override ParameterRead getANextRead() { result = LocalScopeVariableRead.super.getANextRead() }
|
||||
|
||||
override ParameterRead getAReachableRead() {
|
||||
result = LocalScopeVariableRead.super.getAReachableRead()
|
||||
@@ -289,9 +265,7 @@ class ParameterWrite extends ParameterAccess, VariableWrite { }
|
||||
* ```
|
||||
*/
|
||||
class LocalVariableAccess extends LocalScopeVariableAccess, @local_variable_access {
|
||||
override LocalVariable getTarget() {
|
||||
result = AccessImpl::getLocalVariableAccessTarget(this)
|
||||
}
|
||||
override LocalVariable getTarget() { result = AccessImpl::getLocalVariableAccessTarget(this) }
|
||||
|
||||
override string toString() {
|
||||
// If this access is also a local variable declaration (that is, an `out` declared
|
||||
@@ -313,9 +287,7 @@ class LocalVariableAccess extends LocalScopeVariableAccess, @local_variable_acce
|
||||
* ```
|
||||
*/
|
||||
class LocalVariableRead extends LocalVariableAccess, LocalScopeVariableRead {
|
||||
override LocalVariableRead getANextRead() {
|
||||
result = LocalScopeVariableRead.super.getANextRead()
|
||||
}
|
||||
override LocalVariableRead getANextRead() { result = LocalScopeVariableRead.super.getANextRead() }
|
||||
|
||||
override LocalVariableRead getAReachableRead() {
|
||||
result = LocalScopeVariableRead.super.getAReachableRead()
|
||||
@@ -350,13 +322,9 @@ class LocalVariableWrite extends LocalVariableAccess, VariableWrite { }
|
||||
* ```
|
||||
*/
|
||||
class FieldAccess extends AssignableMemberAccess, VariableAccess, @field_access_expr {
|
||||
override Field getTarget() {
|
||||
expr_access(this, result)
|
||||
}
|
||||
override Field getTarget() { expr_access(this, result) }
|
||||
|
||||
override string toString() {
|
||||
result = "access to field " + this.getTarget().getName()
|
||||
}
|
||||
override string toString() { result = "access to field " + this.getTarget().getName() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -420,9 +388,7 @@ library class PropertyAccessExpr extends Expr, @property_access_expr {
|
||||
/** Gets the target of this property access. */
|
||||
Property getProperty() { expr_access(this, result) }
|
||||
|
||||
override string toString() {
|
||||
result = "access to property " + this.getProperty().getName()
|
||||
}
|
||||
override string toString() { result = "access to property " + this.getProperty().getName() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -457,13 +423,9 @@ class PropertyAccess extends AssignableMemberAccess, PropertyAccessExpr {
|
||||
* ```
|
||||
*/
|
||||
class PropertyRead extends PropertyAccess, AssignableRead {
|
||||
override PropertyRead getANextRead() {
|
||||
result = AssignableRead.super.getANextRead()
|
||||
}
|
||||
override PropertyRead getANextRead() { result = AssignableRead.super.getANextRead() }
|
||||
|
||||
override PropertyRead getAReachableRead() {
|
||||
result = AssignableRead.super.getAReachableRead()
|
||||
}
|
||||
override PropertyRead getAReachableRead() { result = AssignableRead.super.getAReachableRead() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -497,9 +459,7 @@ class PropertyWrite extends PropertyAccess, AssignableWrite { }
|
||||
* ```
|
||||
*/
|
||||
class TrivialPropertyAccess extends PropertyAccess {
|
||||
TrivialPropertyAccess() {
|
||||
this.getTarget() instanceof TrivialProperty
|
||||
}
|
||||
TrivialPropertyAccess() { this.getTarget() instanceof TrivialProperty }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -517,9 +477,7 @@ class TrivialPropertyAccess extends PropertyAccess {
|
||||
* ```
|
||||
*/
|
||||
class VirtualPropertyAccess extends PropertyAccess {
|
||||
VirtualPropertyAccess() {
|
||||
this.targetIsOverridableOrImplementable()
|
||||
}
|
||||
VirtualPropertyAccess() { this.targetIsOverridableOrImplementable() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -582,9 +540,7 @@ library class IndexerAccessExpr extends Expr, @indexer_access_expr {
|
||||
* ```
|
||||
*/
|
||||
class IndexerAccess extends AssignableMemberAccess, ElementAccess, IndexerAccessExpr {
|
||||
override Indexer getTarget() {
|
||||
result = getIndexer()
|
||||
}
|
||||
override Indexer getTarget() { result = getIndexer() }
|
||||
|
||||
override Indexer getQualifiedDeclaration() {
|
||||
result = ElementAccess.super.getQualifiedDeclaration()
|
||||
@@ -606,13 +562,9 @@ class IndexerAccess extends AssignableMemberAccess, ElementAccess, IndexerAccess
|
||||
* ```
|
||||
*/
|
||||
class IndexerRead extends IndexerAccess, AssignableRead {
|
||||
override IndexerRead getANextRead() {
|
||||
result = AssignableRead.super.getANextRead()
|
||||
}
|
||||
override IndexerRead getANextRead() { result = AssignableRead.super.getANextRead() }
|
||||
|
||||
override IndexerRead getAReachableRead() {
|
||||
result = AssignableRead.super.getAReachableRead()
|
||||
}
|
||||
override IndexerRead getAReachableRead() { result = AssignableRead.super.getAReachableRead() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -645,9 +597,7 @@ class IndexerWrite extends IndexerAccess, AssignableWrite { }
|
||||
* }
|
||||
*/
|
||||
class VirtualIndexerAccess extends IndexerAccess {
|
||||
VirtualIndexerAccess() {
|
||||
targetIsOverridableOrImplementable()
|
||||
}
|
||||
VirtualIndexerAccess() { targetIsOverridableOrImplementable() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -656,13 +606,9 @@ class VirtualIndexerAccess extends IndexerAccess {
|
||||
*/
|
||||
library class EventAccessExpr extends Expr, @event_access_expr {
|
||||
/** Gets the target of this event access. */
|
||||
Event getEvent() {
|
||||
expr_access(this, result)
|
||||
}
|
||||
Event getEvent() { expr_access(this, result) }
|
||||
|
||||
override string toString() {
|
||||
result = "access to event " + this.getEvent().getName()
|
||||
}
|
||||
override string toString() { result = "access to event " + this.getEvent().getName() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -741,9 +687,7 @@ class EventWrite extends EventAccess, AssignableWrite { }
|
||||
* ```
|
||||
*/
|
||||
class VirtualEventAccess extends EventAccess {
|
||||
VirtualEventAccess() {
|
||||
targetIsOverridableOrImplementable()
|
||||
}
|
||||
VirtualEventAccess() { targetIsOverridableOrImplementable() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -753,9 +697,7 @@ class VirtualEventAccess extends EventAccess {
|
||||
* Note that a callable access is *not* the same as a call.
|
||||
*/
|
||||
class CallableAccess extends Access, @method_access_expr {
|
||||
override Callable getTarget() {
|
||||
expr_access(this, result)
|
||||
}
|
||||
override Callable getTarget() { expr_access(this, result) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -774,17 +716,11 @@ class CallableAccess extends Access, @method_access_expr {
|
||||
* Note that a method access is *not* the same as a method call (`MethodCall`).
|
||||
*/
|
||||
class MethodAccess extends MemberAccess, CallableAccess {
|
||||
MethodAccess() {
|
||||
expr_access(this, any(Method m))
|
||||
}
|
||||
MethodAccess() { expr_access(this, any(Method m)) }
|
||||
|
||||
override Method getTarget() {
|
||||
expr_access(this, result)
|
||||
}
|
||||
override Method getTarget() { expr_access(this, result) }
|
||||
|
||||
override string toString() {
|
||||
result = "access to method " + this.getTarget().getName()
|
||||
}
|
||||
override string toString() { result = "access to method " + this.getTarget().getName() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -803,17 +739,11 @@ class MethodAccess extends MemberAccess, CallableAccess {
|
||||
* (`LocalFunctionCall`).
|
||||
*/
|
||||
class LocalFunctionAccess extends CallableAccess {
|
||||
LocalFunctionAccess() {
|
||||
expr_access(this, any(LocalFunction f))
|
||||
}
|
||||
LocalFunctionAccess() { expr_access(this, any(LocalFunction f)) }
|
||||
|
||||
override LocalFunction getTarget() {
|
||||
expr_access(this, result)
|
||||
}
|
||||
override LocalFunction getTarget() { expr_access(this, result) }
|
||||
|
||||
override string toString() {
|
||||
result = "access to local function " + this.getTarget().getName()
|
||||
}
|
||||
override string toString() { result = "access to local function " + this.getTarget().getName() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -833,9 +763,7 @@ class LocalFunctionAccess extends CallableAccess {
|
||||
* ```
|
||||
*/
|
||||
class VirtualMethodAccess extends MethodAccess {
|
||||
VirtualMethodAccess() {
|
||||
targetIsOverridableOrImplementable()
|
||||
}
|
||||
VirtualMethodAccess() { targetIsOverridableOrImplementable() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -852,9 +780,7 @@ class VirtualMethodAccess extends MethodAccess {
|
||||
class TypeAccess extends MemberAccess, @type_access_expr {
|
||||
override Type getTarget() { result = this.getType() }
|
||||
|
||||
override string toString() {
|
||||
result = "access to type " + this.getTarget().getName()
|
||||
}
|
||||
override string toString() { result = "access to type " + this.getTarget().getName() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*
|
||||
* All arithmetic operations have the common base class `ArithmeticOperation`.
|
||||
*/
|
||||
|
||||
import Expr
|
||||
|
||||
/**
|
||||
@@ -71,14 +72,14 @@ class PreDecrExpr extends DecrementOperation, @pre_decr_expr { }
|
||||
* A postfix increment operation, for example `x++`.
|
||||
*/
|
||||
class PostIncrExpr extends IncrementOperation, @post_incr_expr {
|
||||
override string toString() { result = "..." + this.getOperator() }
|
||||
override string toString() { result = "..." + this.getOperator() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A postfix decrement operation, for example `x--`.
|
||||
*/
|
||||
class PostDecrExpr extends DecrementOperation, @post_decr_expr {
|
||||
override string toString() { result = "..." + this.getOperator() }
|
||||
override string toString() { result = "..." + this.getOperator() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -131,4 +132,3 @@ class DivExpr extends BinaryArithmeticOperation, @div_expr {
|
||||
class RemExpr extends BinaryArithmeticOperation, @rem_expr {
|
||||
override string getOperator() { result = "%" }
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,8 @@ import Expr
|
||||
*/
|
||||
class Assignment extends Operation, @assign_expr {
|
||||
Assignment() {
|
||||
this instanceof LocalVariableDeclExpr implies
|
||||
this instanceof LocalVariableDeclExpr
|
||||
implies
|
||||
// Same as `this.(LocalVariableDeclExpr).hasInitializer()` but avoids
|
||||
// negative recursion
|
||||
expr_parent(_, 0, this)
|
||||
@@ -37,17 +38,11 @@ class Assignment extends Operation, @assign_expr {
|
||||
class LocalVariableDeclAndInitExpr extends LocalVariableDeclExpr, Assignment {
|
||||
override string getOperator() { result = "=" }
|
||||
|
||||
override LocalVariable getTargetVariable() {
|
||||
result = getVariable()
|
||||
}
|
||||
override LocalVariable getTargetVariable() { result = getVariable() }
|
||||
|
||||
override LocalVariableAccess getLValue() {
|
||||
result = Assignment.super.getLValue()
|
||||
}
|
||||
override LocalVariableAccess getLValue() { result = Assignment.super.getLValue() }
|
||||
|
||||
override string toString() {
|
||||
result = LocalVariableDeclExpr.super.toString() + " = ..."
|
||||
}
|
||||
override string toString() { result = LocalVariableDeclExpr.super.toString() + " = ..." }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,9 +71,7 @@ class AssignOperation extends Assignment, @assign_op_expr {
|
||||
* If an expanded version exists, then it is used in the control
|
||||
* flow graph.
|
||||
*/
|
||||
AssignExpr getExpandedAssignment() {
|
||||
expr_parent(result, 2, this)
|
||||
}
|
||||
AssignExpr getExpandedAssignment() { expr_parent(result, 2, this) }
|
||||
|
||||
/**
|
||||
* Holds if this assignment operation has an expanded version.
|
||||
@@ -89,9 +82,7 @@ class AssignOperation extends Assignment, @assign_op_expr {
|
||||
* If an expanded version exists, then it is used in the control
|
||||
* flow graph.
|
||||
*/
|
||||
predicate hasExpandedAssignment() {
|
||||
exists(getExpandedAssignment())
|
||||
}
|
||||
predicate hasExpandedAssignment() { exists(getExpandedAssignment()) }
|
||||
|
||||
override string toString() { result = "... " + this.getOperator() + " ..." }
|
||||
}
|
||||
@@ -140,7 +131,6 @@ class AssignRemExpr extends AssignArithmeticOperation, @assign_rem_expr {
|
||||
override string getOperator() { result = "%=" }
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A bitwise assignment operation. Either a bitwise-and assignment
|
||||
* operation (`AssignAndExpr`), a bitwise-or assignment
|
||||
@@ -149,8 +139,7 @@ class AssignRemExpr extends AssignArithmeticOperation, @assign_rem_expr {
|
||||
* operation (`AssignLShiftExpr`), or a right-shift assignment
|
||||
* operation (`AssignRShiftExpr`).
|
||||
*/
|
||||
class AssignBitwiseOperation extends AssignOperation, @assign_bitwise_expr {
|
||||
}
|
||||
class AssignBitwiseOperation extends AssignOperation, @assign_bitwise_expr { }
|
||||
|
||||
/**
|
||||
* A bitwise-and assignment operation, for example `x &= y`.
|
||||
@@ -162,7 +151,7 @@ class AssignAndExpr extends AssignBitwiseOperation, @assign_and_expr {
|
||||
/**
|
||||
* A bitwise-or assignment operation, for example `x |= y`.
|
||||
*/
|
||||
class AssignOrExpr extends AssignBitwiseOperation, @assign_or_expr {
|
||||
class AssignOrExpr extends AssignBitwiseOperation, @assign_or_expr {
|
||||
override string getOperator() { result = "|=" }
|
||||
}
|
||||
|
||||
@@ -176,14 +165,14 @@ class AssignXorExpr extends AssignBitwiseOperation, @assign_xor_expr {
|
||||
/**
|
||||
* A left-shift assignment operation, for example `x <<= y`.
|
||||
*/
|
||||
class AssignLShiftExpr extends AssignBitwiseOperation, @assign_lshift_expr {
|
||||
class AssignLShiftExpr extends AssignBitwiseOperation, @assign_lshift_expr {
|
||||
override string getOperator() { result = "<<=" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A right-shift assignment operation, for example `x >>= y`.
|
||||
*/
|
||||
class AssignRShiftExpr extends AssignBitwiseOperation, @assign_rshift_expr {
|
||||
class AssignRShiftExpr extends AssignBitwiseOperation, @assign_rshift_expr {
|
||||
override string getOperator() { result = ">>=" }
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*
|
||||
* All bitwise operations have the common base class `BitwiseOperation`.
|
||||
*/
|
||||
|
||||
import Expr
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*
|
||||
* All calls have the common base class `Call`.
|
||||
*/
|
||||
|
||||
import Expr
|
||||
import semmle.code.csharp.Callable
|
||||
import semmle.code.csharp.dataflow.CallContext as CallContext
|
||||
@@ -40,27 +41,17 @@ class Call extends DotNet::Call, Expr, @call {
|
||||
*/
|
||||
override Callable getTarget() { none() }
|
||||
|
||||
override Expr getArgument(int i) {
|
||||
result = this.getChild(i) and i >= 0
|
||||
}
|
||||
override Expr getArgument(int i) { result = this.getChild(i) and i >= 0 }
|
||||
|
||||
override Expr getRawArgument(int i) {
|
||||
result = this.getArgument(i)
|
||||
}
|
||||
override Expr getRawArgument(int i) { result = this.getArgument(i) }
|
||||
|
||||
override Expr getAnArgument() {
|
||||
result = getArgument(_)
|
||||
}
|
||||
override Expr getAnArgument() { result = getArgument(_) }
|
||||
|
||||
/** Gets the number of arguments of this call. */
|
||||
int getNumberOfArguments() {
|
||||
result = count(this.getAnArgument())
|
||||
}
|
||||
int getNumberOfArguments() { result = count(this.getAnArgument()) }
|
||||
|
||||
/** Holds if this call has no arguments. */
|
||||
predicate hasNoArguments() {
|
||||
not exists(this.getAnArgument())
|
||||
}
|
||||
predicate hasNoArguments() { not exists(this.getAnArgument()) }
|
||||
|
||||
/**
|
||||
* Gets the argument for this call associated with the parameter `p`, if any.
|
||||
@@ -86,8 +77,7 @@ class Call extends DotNet::Call, Expr, @call {
|
||||
* ```
|
||||
*/
|
||||
override Expr getArgumentForParameter(DotNet::Parameter p) {
|
||||
getTarget().getAParameter() = p
|
||||
and
|
||||
getTarget().getAParameter() = p and
|
||||
(
|
||||
// Appears in the positional part of the call
|
||||
result = this.getImplicitArgument(p.getPosition()) and
|
||||
@@ -111,20 +101,21 @@ class Call extends DotNet::Call, Expr, @call {
|
||||
* of the targeted callable.
|
||||
*/
|
||||
private predicate hasMultipleParamsArguments() {
|
||||
exists(Parameter p |
|
||||
p = this.getTarget().getAParameter() |
|
||||
exists(Parameter p | p = this.getTarget().getAParameter() |
|
||||
p.isParams() and
|
||||
exists(this.getArgument(any(int i | i > p.getPosition())))
|
||||
)
|
||||
}
|
||||
|
||||
pragma [noinline] // predicate folding to get proper join-order
|
||||
// predicate folding to get proper join-order
|
||||
pragma[noinline]
|
||||
private Expr getImplicitArgument(int pos) {
|
||||
result = getArgument(pos) and
|
||||
not exists(result.getExplicitArgumentName())
|
||||
}
|
||||
|
||||
pragma [noinline] // predicate folding to get proper join-order
|
||||
// predicate folding to get proper join-order
|
||||
pragma[noinline]
|
||||
private Expr getExplicitArgument(string name) {
|
||||
result = getAnArgument() and
|
||||
result.getExplicitArgumentName() = name
|
||||
@@ -248,25 +239,19 @@ class Call extends DotNet::Call, Expr, @call {
|
||||
* Gets an argument that corresponds to a parameter of a potential
|
||||
* run-time target of this call.
|
||||
*/
|
||||
Expr getARuntimeArgument() {
|
||||
result = getRuntimeArgument(_)
|
||||
}
|
||||
Expr getARuntimeArgument() { result = getRuntimeArgument(_) }
|
||||
|
||||
/**
|
||||
* Gets the number of arguments that correspond to a parameter of a potential
|
||||
* run-time target of this call.
|
||||
*/
|
||||
int getNumberOfRuntimeArguments() {
|
||||
result = count(getARuntimeArgument())
|
||||
}
|
||||
int getNumberOfRuntimeArguments() { result = count(getARuntimeArgument()) }
|
||||
|
||||
/**
|
||||
* Holds if this call has no arguments that correspond to a parameter of a
|
||||
* potential (run-time) target of this call.
|
||||
*/
|
||||
predicate hasNoRuntimeArguments() {
|
||||
not exists(getARuntimeArgument())
|
||||
}
|
||||
predicate hasNoRuntimeArguments() { not exists(getARuntimeArgument()) }
|
||||
|
||||
override string toString() { result = "call" }
|
||||
}
|
||||
@@ -307,21 +292,18 @@ private predicate isValidExplicitParamsType(Parameter p, Type t) {
|
||||
* ```
|
||||
*/
|
||||
class MethodCall extends Call, QualifiableExpr, LateBindableExpr, @method_invocation_expr {
|
||||
override Method getTarget() {
|
||||
expr_call(this, result)
|
||||
}
|
||||
override Method getTarget() { expr_call(this, result) }
|
||||
|
||||
override Method getQualifiedDeclaration() {
|
||||
result = getTarget()
|
||||
}
|
||||
override Method getQualifiedDeclaration() { result = getTarget() }
|
||||
|
||||
override string toString() {
|
||||
result = "call to method " + this.getTarget().getName()
|
||||
}
|
||||
override string toString() { result = "call to method " + this.getTarget().getName() }
|
||||
|
||||
override Expr getRawArgument(int i) {
|
||||
if exists(getQualifier())
|
||||
then (i=0 and result = getQualifier() or result = getArgument(i-1))
|
||||
then
|
||||
i = 0 and result = getQualifier()
|
||||
or
|
||||
result = getArgument(i - 1)
|
||||
else result = getArgument(i)
|
||||
}
|
||||
}
|
||||
@@ -345,21 +327,13 @@ class MethodCall extends Call, QualifiableExpr, LateBindableExpr, @method_invoca
|
||||
* 6), in order to be properly matched with the parameter `i` on line 2.
|
||||
*/
|
||||
class ExtensionMethodCall extends MethodCall {
|
||||
ExtensionMethodCall() {
|
||||
this.getTarget() instanceof ExtensionMethod
|
||||
}
|
||||
ExtensionMethodCall() { this.getTarget() instanceof ExtensionMethod }
|
||||
|
||||
override TypeAccess getQualifier() {
|
||||
result = this.getChildExpr(-1)
|
||||
}
|
||||
override TypeAccess getQualifier() { result = this.getChildExpr(-1) }
|
||||
|
||||
override Expr getArgument(int i) {
|
||||
exists(int j |
|
||||
result = this.getChildExpr(j) |
|
||||
if isOrdinaryStaticCall() then
|
||||
(j = i and j >= 0)
|
||||
else
|
||||
(j = i - 1 and j >= -1)
|
||||
exists(int j | result = this.getChildExpr(j) |
|
||||
if isOrdinaryStaticCall() then (j = i and j >= 0) else (j = i - 1 and j >= -1)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -401,13 +375,10 @@ class ExtensionMethodCall extends MethodCall {
|
||||
* ```
|
||||
*/
|
||||
class VirtualMethodCall extends MethodCall {
|
||||
|
||||
VirtualMethodCall() {
|
||||
not getQualifier() instanceof BaseAccess
|
||||
and
|
||||
not getQualifier() instanceof BaseAccess and
|
||||
getTarget().isOverridableOrImplementable()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -427,17 +398,11 @@ class VirtualMethodCall extends MethodCall {
|
||||
* ```
|
||||
*/
|
||||
class ConstructorInitializer extends Call, @constructor_init_expr {
|
||||
override Constructor getTarget() {
|
||||
expr_call(this, result)
|
||||
}
|
||||
override Constructor getTarget() { expr_call(this, result) }
|
||||
|
||||
override Constructor getARuntimeTarget() {
|
||||
result = Call.super.getARuntimeTarget()
|
||||
}
|
||||
override Constructor getARuntimeTarget() { result = Call.super.getARuntimeTarget() }
|
||||
|
||||
override string toString() {
|
||||
result = "call to constructor " + this.getTarget().getName()
|
||||
}
|
||||
override string toString() { result = "call to constructor " + this.getTarget().getName() }
|
||||
|
||||
/**
|
||||
* Gets the constructor that this initializer call belongs to. For example,
|
||||
@@ -460,7 +425,7 @@ class ConstructorInitializer extends Call, @constructor_init_expr {
|
||||
override Expr getRawArgument(int i) {
|
||||
if this.getTarget().isStatic()
|
||||
then result = this.getArgument(i)
|
||||
else result = this.getArgument(i-1)
|
||||
else result = this.getArgument(i - 1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -481,17 +446,11 @@ class ConstructorInitializer extends Call, @constructor_init_expr {
|
||||
* ```
|
||||
*/
|
||||
class OperatorCall extends Call, LateBindableExpr, @operator_invocation_expr {
|
||||
override Operator getTarget() {
|
||||
expr_call(this, result)
|
||||
}
|
||||
override Operator getTarget() { expr_call(this, result) }
|
||||
|
||||
override Operator getARuntimeTarget() {
|
||||
result = Call.super.getARuntimeTarget()
|
||||
}
|
||||
override Operator getARuntimeTarget() { result = Call.super.getARuntimeTarget() }
|
||||
|
||||
override string toString() {
|
||||
result = "call to operator " + this.getTarget().getName()
|
||||
}
|
||||
override string toString() { result = "call to operator " + this.getTarget().getName() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -511,13 +470,13 @@ class OperatorCall extends Call, LateBindableExpr, @operator_invocation_expr {
|
||||
* ```
|
||||
*/
|
||||
class MutatorOperatorCall extends OperatorCall {
|
||||
MutatorOperatorCall() { mutator_invocation_mode(this,_) }
|
||||
MutatorOperatorCall() { mutator_invocation_mode(this, _) }
|
||||
|
||||
/** Holds if the operator is in prefix position. */
|
||||
predicate isPrefix() { mutator_invocation_mode(this,1) }
|
||||
predicate isPrefix() { mutator_invocation_mode(this, 1) }
|
||||
|
||||
/** Holds if the operator is in postfix position. */
|
||||
predicate isPostfix() { mutator_invocation_mode(this,2) }
|
||||
predicate isPostfix() { mutator_invocation_mode(this, 2) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -548,7 +507,8 @@ class DelegateCall extends Call, @delegate_invocation_expr {
|
||||
or
|
||||
exists(AddEventSource aes, CallContext::CallContext cc2 |
|
||||
aes = this.getAnAddEventSource() and
|
||||
result = aes.getARuntimeTarget(cc2) |
|
||||
result = aes.getARuntimeTarget(cc2)
|
||||
|
|
||||
aes = this.getAnAddEventSourceSameEnclosingCallable() and
|
||||
cc = cc2
|
||||
or
|
||||
@@ -572,13 +532,9 @@ class DelegateCall extends Call, @delegate_invocation_expr {
|
||||
result.getExpr().getEnclosingCallable() != this.getEnclosingCallable()
|
||||
}
|
||||
|
||||
override Callable getARuntimeTarget() {
|
||||
result = getARuntimeTarget(_)
|
||||
}
|
||||
override Callable getARuntimeTarget() { result = getARuntimeTarget(_) }
|
||||
|
||||
override Expr getRuntimeArgument(int i) {
|
||||
result = getArgument(i)
|
||||
}
|
||||
override Expr getRuntimeArgument(int i) { result = getArgument(i) }
|
||||
|
||||
/**
|
||||
* Gets the delegate expression of this delegate call. For example, the
|
||||
@@ -594,13 +550,9 @@ class DelegateCall extends Call, @delegate_invocation_expr {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
Expr getDelegateExpr() {
|
||||
result = this.getChild(-1)
|
||||
}
|
||||
Expr getDelegateExpr() { result = this.getChild(-1) }
|
||||
|
||||
override string toString() {
|
||||
result = "delegate call"
|
||||
}
|
||||
override string toString() { result = "delegate call" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -613,9 +565,7 @@ class AccessorCall extends Call, QualifiableExpr, @call_access_expr {
|
||||
|
||||
override Expr getArgument(int i) { none() }
|
||||
|
||||
override Accessor getARuntimeTarget() {
|
||||
result = Call.super.getARuntimeTarget()
|
||||
}
|
||||
override Accessor getARuntimeTarget() { result = Call.super.getARuntimeTarget() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -634,8 +584,7 @@ class AccessorCall extends Call, QualifiableExpr, @call_access_expr {
|
||||
*/
|
||||
class PropertyCall extends AccessorCall, PropertyAccessExpr {
|
||||
override Accessor getTarget() {
|
||||
exists(PropertyAccess pa, Property p |
|
||||
pa = this and p = getProperty() |
|
||||
exists(PropertyAccess pa, Property p | pa = this and p = getProperty() |
|
||||
pa instanceof AssignableRead and result = p.getGetter()
|
||||
or
|
||||
pa instanceof AssignableWrite and result = p.getSetter()
|
||||
@@ -647,9 +596,7 @@ class PropertyCall extends AccessorCall, PropertyAccessExpr {
|
||||
result = AssignableInternal::getAccessorCallValueArgument(this)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = PropertyAccessExpr.super.toString()
|
||||
}
|
||||
override string toString() { result = PropertyAccessExpr.super.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -670,8 +617,7 @@ class PropertyCall extends AccessorCall, PropertyAccessExpr {
|
||||
*/
|
||||
class IndexerCall extends AccessorCall, IndexerAccessExpr {
|
||||
override Accessor getTarget() {
|
||||
exists(IndexerAccess ia, Indexer i |
|
||||
ia = this and i = getIndexer() |
|
||||
exists(IndexerAccess ia, Indexer i | ia = this and i = getIndexer() |
|
||||
ia instanceof AssignableRead and result = i.getGetter()
|
||||
or
|
||||
ia instanceof AssignableWrite and result = i.getSetter()
|
||||
@@ -713,7 +659,8 @@ class EventCall extends AccessorCall, EventAccessExpr {
|
||||
override EventAccessor getTarget() {
|
||||
exists(Event e, AddOrRemoveEventExpr aoree |
|
||||
e = getEvent() and
|
||||
aoree.getLValue() = this |
|
||||
aoree.getLValue() = this
|
||||
|
|
||||
aoree instanceof AddEventExpr and result = e.getAddEventAccessor()
|
||||
or
|
||||
aoree instanceof RemoveEventExpr and result = e.getRemoveEventAccessor()
|
||||
@@ -728,9 +675,7 @@ class EventCall extends AccessorCall, EventAccessExpr {
|
||||
)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = EventAccessExpr.super.toString()
|
||||
}
|
||||
override string toString() { result = EventAccessExpr.super.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -747,11 +692,7 @@ class EventCall extends AccessorCall, EventAccessExpr {
|
||||
* ```
|
||||
*/
|
||||
class LocalFunctionCall extends Call, @local_function_invocation_expr {
|
||||
override LocalFunction getTarget() {
|
||||
expr_call(this, result)
|
||||
}
|
||||
override LocalFunction getTarget() { expr_call(this, result) }
|
||||
|
||||
override string toString() {
|
||||
result = "call to local function " + getTarget().getName()
|
||||
}
|
||||
override string toString() { result = "call to local function " + getTarget().getName() }
|
||||
}
|
||||
|
||||
@@ -21,16 +21,12 @@ class EqualityOperation extends ComparisonOperation, @equality_op_expr { }
|
||||
/**
|
||||
* An equals operation, for example `x == y`.
|
||||
*/
|
||||
class EQExpr extends EqualityOperation, @eq_expr {
|
||||
override string getOperator() { result = "==" }
|
||||
}
|
||||
class EQExpr extends EqualityOperation, @eq_expr { override string getOperator() { result = "==" } }
|
||||
|
||||
/**
|
||||
* A not-equals operation, for example `x != y`.
|
||||
*/
|
||||
class NEExpr extends EqualityOperation, @ne_expr {
|
||||
override string getOperator() { result = "!=" }
|
||||
}
|
||||
class NEExpr extends EqualityOperation, @ne_expr { override string getOperator() { result = "!=" } }
|
||||
|
||||
/**
|
||||
* A relational operation. Either a greater-than operation (`GTExpr`),
|
||||
@@ -60,7 +56,9 @@ class RelationalOperation extends ComparisonOperation, @rel_op_expr {
|
||||
*/
|
||||
class GTExpr extends RelationalOperation, @gt_expr {
|
||||
override string getOperator() { result = ">" }
|
||||
|
||||
override Expr getGreaterOperand() { result = getLeftOperand() }
|
||||
|
||||
override Expr getLesserOperand() { result = getRightOperand() }
|
||||
}
|
||||
|
||||
@@ -69,7 +67,9 @@ class GTExpr extends RelationalOperation, @gt_expr {
|
||||
*/
|
||||
class LTExpr extends RelationalOperation, @lt_expr {
|
||||
override string getOperator() { result = "<" }
|
||||
|
||||
override Expr getGreaterOperand() { result = getRightOperand() }
|
||||
|
||||
override Expr getLesserOperand() { result = getLeftOperand() }
|
||||
}
|
||||
|
||||
@@ -78,7 +78,9 @@ class LTExpr extends RelationalOperation, @lt_expr {
|
||||
*/
|
||||
class GEExpr extends RelationalOperation, @ge_expr {
|
||||
override string getOperator() { result = ">=" }
|
||||
|
||||
override Expr getGreaterOperand() { result = getLeftOperand() }
|
||||
|
||||
override Expr getLesserOperand() { result = getRightOperand() }
|
||||
}
|
||||
|
||||
@@ -87,6 +89,8 @@ class GEExpr extends RelationalOperation, @ge_expr {
|
||||
*/
|
||||
class LEExpr extends RelationalOperation, @le_expr {
|
||||
override string getOperator() { result = "<=" }
|
||||
|
||||
override Expr getGreaterOperand() { result = getRightOperand() }
|
||||
|
||||
override Expr getLesserOperand() { result = getLeftOperand() }
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/**
|
||||
* Provides expression classes for creating various types of object.
|
||||
*/
|
||||
|
||||
import Expr
|
||||
import semmle.code.csharp.Callable
|
||||
|
||||
@@ -43,9 +44,7 @@ class ObjectInitializer extends ObjectOrCollectionInitializer, @object_init_expr
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
MemberInitializer getAMemberInitializer() {
|
||||
result = getMemberInitializer(_)
|
||||
}
|
||||
MemberInitializer getAMemberInitializer() { result = getMemberInitializer(_) }
|
||||
|
||||
/**
|
||||
* Gets the `i`th member initializer of this object initializer. For example,
|
||||
@@ -69,14 +68,10 @@ class ObjectInitializer extends ObjectOrCollectionInitializer, @object_init_expr
|
||||
}
|
||||
|
||||
/** Gets the number of member initializers of this object initializer. */
|
||||
int getNumberOfMemberInitializers() {
|
||||
result = count(this.getAMemberInitializer())
|
||||
}
|
||||
int getNumberOfMemberInitializers() { result = count(this.getAMemberInitializer()) }
|
||||
|
||||
/** Holds if this object initializer has no member initializers. */
|
||||
predicate hasNoMemberInitializers() {
|
||||
not exists(this.getAMemberInitializer())
|
||||
}
|
||||
predicate hasNoMemberInitializers() { not exists(this.getAMemberInitializer()) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,14 +89,10 @@ class ObjectInitializer extends ObjectOrCollectionInitializer, @object_init_expr
|
||||
* ```
|
||||
*/
|
||||
class MemberInitializer extends AssignExpr {
|
||||
MemberInitializer() {
|
||||
this.getParent() instanceof ObjectInitializer
|
||||
}
|
||||
MemberInitializer() { this.getParent() instanceof ObjectInitializer }
|
||||
|
||||
/** Gets the initialized member. */
|
||||
Member getInitializedMember() {
|
||||
result.getAnAccess() = this.getLValue()
|
||||
}
|
||||
Member getInitializedMember() { result.getAnAccess() = this.getLValue() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,9 +117,7 @@ class CollectionInitializer extends ObjectOrCollectionInitializer, @collection_i
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
ElementInitializer getAnElementInitializer() {
|
||||
result = getElementInitializer(_)
|
||||
}
|
||||
ElementInitializer getAnElementInitializer() { result = getElementInitializer(_) }
|
||||
|
||||
/**
|
||||
* Gets the `i`th element initializer of this collection initializer, for
|
||||
@@ -148,14 +137,10 @@ class CollectionInitializer extends ObjectOrCollectionInitializer, @collection_i
|
||||
}
|
||||
|
||||
/** Gets the number of element initializers of this collection initializer. */
|
||||
int getNumberOfElementInitializers() {
|
||||
result = count(this.getAnElementInitializer())
|
||||
}
|
||||
int getNumberOfElementInitializers() { result = count(this.getAnElementInitializer()) }
|
||||
|
||||
/** Holds if this collection initializer has no element initializers. */
|
||||
predicate hasNoElementInitializers() {
|
||||
not exists(this.getAnElementInitializer())
|
||||
}
|
||||
predicate hasNoElementInitializers() { not exists(this.getAnElementInitializer()) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -190,9 +175,7 @@ class ObjectCreation extends Call, LateBindableExpr, @object_creation_expr {
|
||||
|
||||
override Constructor getTarget() { expr_call(this, result) }
|
||||
|
||||
override Constructor getARuntimeTarget() {
|
||||
result = Call.super.getARuntimeTarget()
|
||||
}
|
||||
override Constructor getARuntimeTarget() { result = Call.super.getARuntimeTarget() }
|
||||
|
||||
/**
|
||||
* Holds if this constructor call includes an object initializer or an
|
||||
@@ -213,14 +196,12 @@ class ObjectCreation extends Call, LateBindableExpr, @object_creation_expr {
|
||||
*/
|
||||
ObjectOrCollectionInitializer getInitializer() { result = this.getChild(-1) }
|
||||
|
||||
override string toString() {
|
||||
result = "object creation of type " + this.getType().getName()
|
||||
}
|
||||
override string toString() { result = "object creation of type " + this.getType().getName() }
|
||||
|
||||
override Expr getRawArgument(int i) {
|
||||
if this.getTarget().isStatic()
|
||||
then result = this.getArgument(i)
|
||||
else result = this.getArgument(i-1)
|
||||
else result = this.getArgument(i - 1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,13 +217,9 @@ class ObjectCreation extends Call, LateBindableExpr, @object_creation_expr {
|
||||
* ```
|
||||
*/
|
||||
class AnonymousObjectCreation extends ObjectCreation {
|
||||
AnonymousObjectCreation() {
|
||||
this.getObjectType() instanceof AnonymousClass
|
||||
}
|
||||
AnonymousObjectCreation() { this.getObjectType() instanceof AnonymousClass }
|
||||
|
||||
override ObjectInitializer getInitializer() {
|
||||
result = this.getChild(-1)
|
||||
}
|
||||
override ObjectInitializer getInitializer() { result = this.getChild(-1) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -252,9 +229,7 @@ class AnonymousObjectCreation extends ObjectCreation {
|
||||
*/
|
||||
class DelegateCreation extends Expr, @delegate_creation_expr {
|
||||
/** Gets the delegate type of this delegate creation. */
|
||||
DelegateType getDelegateType() {
|
||||
result = this.getType()
|
||||
}
|
||||
DelegateType getDelegateType() { result = this.getType() }
|
||||
|
||||
/**
|
||||
* Gets the argument of this delegate creation. Either a callable access
|
||||
@@ -263,9 +238,7 @@ class DelegateCreation extends Expr, @delegate_creation_expr {
|
||||
*/
|
||||
Expr getArgument() { result = this.getAChild() }
|
||||
|
||||
override string toString() {
|
||||
result = "delegate creation of type " + this.getType().getName()
|
||||
}
|
||||
override string toString() { result = "delegate creation of type " + this.getType().getName() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -322,9 +295,7 @@ class ArrayInitializer extends Expr, @array_init_expr {
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
Expr getAnElement() {
|
||||
result = getElement(_)
|
||||
}
|
||||
Expr getAnElement() { result = getElement(_) }
|
||||
|
||||
/**
|
||||
* Gets the `i`th element of this array initializer, for example the second
|
||||
@@ -344,9 +315,7 @@ class ArrayInitializer extends Expr, @array_init_expr {
|
||||
}
|
||||
|
||||
/** Gets the number of elements in this array initializer. */
|
||||
int getNumberOfElements() {
|
||||
result = count(this.getAnElement())
|
||||
}
|
||||
int getNumberOfElements() { result = count(this.getAnElement()) }
|
||||
|
||||
/** Holds if this array initializer has no elements. */
|
||||
predicate hasNoElements() { not exists(this.getAnElement()) }
|
||||
@@ -369,9 +338,7 @@ class ArrayCreation extends Expr, @array_creation_expr {
|
||||
* new int[5, 10]
|
||||
* ```
|
||||
*/
|
||||
Expr getALengthArgument() {
|
||||
result = getLengthArgument(_)
|
||||
}
|
||||
Expr getALengthArgument() { result = getLengthArgument(_) }
|
||||
|
||||
/**
|
||||
* Gets the `i`th dimension's length argument of this array creation, for
|
||||
@@ -387,9 +354,7 @@ class ArrayCreation extends Expr, @array_creation_expr {
|
||||
}
|
||||
|
||||
/** Gets the number of length arguments of this array creation. */
|
||||
int getNumberOfLengthArguments() {
|
||||
result = count(this.getALengthArgument())
|
||||
}
|
||||
int getNumberOfLengthArguments() { result = count(this.getALengthArgument()) }
|
||||
|
||||
/** Holds if the created array is implicitly sized by the initializer. */
|
||||
predicate isImplicitlySized() { not explicitly_sized_array_creation(this) }
|
||||
@@ -403,9 +368,7 @@ class ArrayCreation extends Expr, @array_creation_expr {
|
||||
/** Holds if the type of the created array is inferred from its initializer. */
|
||||
predicate isImplicitlyTyped() { implicitly_typed_array_creation(this) }
|
||||
|
||||
override string toString() {
|
||||
result = "array creation of type " + this.getType().getName()
|
||||
}
|
||||
override string toString() { result = "array creation of type " + this.getType().getName() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -415,9 +378,7 @@ class ArrayCreation extends Expr, @array_creation_expr {
|
||||
class AnonymousFunctionExpr extends Expr, Callable, @anonymous_function_expr {
|
||||
override string getName() { result = "<anonymous>" }
|
||||
|
||||
override Type getReturnType() {
|
||||
result = getType().(DelegateType).getReturnType()
|
||||
}
|
||||
override Type getReturnType() { result = getType().(DelegateType).getReturnType() }
|
||||
|
||||
override AnonymousFunctionExpr getSourceDeclaration() { result = this }
|
||||
|
||||
|
||||
@@ -14,11 +14,7 @@ private import semmle.code.csharp.dispatch.Dispatch
|
||||
* dynamic member access (`DynamicMemberAccess`), a dynamic accessor call
|
||||
* (`DynamicAccessorCall`), or a dynamic element access (`DynamicElementAccess`).
|
||||
*/
|
||||
class DynamicExpr extends LateBindableExpr {
|
||||
DynamicExpr() {
|
||||
isLateBound()
|
||||
}
|
||||
}
|
||||
class DynamicExpr extends LateBindableExpr { DynamicExpr() { isLateBound() } }
|
||||
|
||||
/**
|
||||
* A constructor call where one of the arguments is a `dynamic` expression, for
|
||||
@@ -67,9 +63,7 @@ class DynamicObjectCreation extends DynamicExpr, ObjectCreation {
|
||||
* may not be known at compile-time (as in the example above).
|
||||
*/
|
||||
class DynamicMethodCall extends DynamicExpr, MethodCall {
|
||||
override string toString() {
|
||||
result = "dynamic call to method " + getLateBoundTargetName()
|
||||
}
|
||||
override string toString() { result = "dynamic call to method " + getLateBoundTargetName() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,9 +91,7 @@ class DynamicMethodCall extends DynamicExpr, MethodCall {
|
||||
* target operator may not be known at compile-time (as in the example above).
|
||||
*/
|
||||
class DynamicOperatorCall extends DynamicExpr, OperatorCall {
|
||||
override string toString() {
|
||||
result = "dynamic call to operator " + getLateBoundTargetName()
|
||||
}
|
||||
override string toString() { result = "dynamic call to operator " + getLateBoundTargetName() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -187,10 +179,9 @@ class DynamicAccess extends DynamicExpr {
|
||||
* of the member being accessed may not be known (it can be either a field, a
|
||||
* property, or an event).
|
||||
*/
|
||||
class DynamicMemberAccess extends DynamicAccess, MemberAccess, AssignableAccess, @dynamic_member_access_expr {
|
||||
override string toString() {
|
||||
result = "dynamic access to member " + getLateBoundTargetName()
|
||||
}
|
||||
class DynamicMemberAccess extends DynamicAccess, MemberAccess, AssignableAccess,
|
||||
@dynamic_member_access_expr {
|
||||
override string toString() { result = "dynamic access to member " + getLateBoundTargetName() }
|
||||
|
||||
// The target is unknown when the qualifier is a `dynamic` expression
|
||||
override DynamicMember getTarget() { none() }
|
||||
@@ -303,10 +294,7 @@ class DynamicAccessorCall extends DynamicAccess {
|
||||
Accessor target;
|
||||
|
||||
DynamicAccessorCall() {
|
||||
exists(DispatchCall dc |
|
||||
dc.getCall() = this |
|
||||
target = dc.getADynamicTarget()
|
||||
)
|
||||
exists(DispatchCall dc | dc.getCall() = this | target = dc.getADynamicTarget())
|
||||
}
|
||||
|
||||
/** Gets an actual run-time target of this dynamic accessor call. */
|
||||
|
||||
@@ -47,20 +47,16 @@ class Expr extends DotNet::Expr, ControlFlowElement, @expr {
|
||||
override Type getType() { expressions(this, _, getTypeRef(result)) }
|
||||
|
||||
/** Gets the value of this expression, if any */
|
||||
override string getValue() { expr_value(this,result) }
|
||||
override string getValue() { expr_value(this, result) }
|
||||
|
||||
/** Holds if this expression has a value. */
|
||||
predicate hasValue() { exists(getValue()) }
|
||||
|
||||
/** Gets the enclosing statement of this expression, if any. */
|
||||
final Stmt getEnclosingStmt() {
|
||||
enclosingStmt(this, result)
|
||||
}
|
||||
final Stmt getEnclosingStmt() { enclosingStmt(this, result) }
|
||||
|
||||
/** Gets the enclosing callable of this expression, if any. */
|
||||
override Callable getEnclosingCallable() {
|
||||
exprEnclosingCallable(this, result)
|
||||
}
|
||||
override Callable getEnclosingCallable() { exprEnclosingCallable(this, result) }
|
||||
|
||||
/**
|
||||
* Holds if this expression is generated by the compiler and does not appear
|
||||
@@ -87,9 +83,7 @@ class Expr extends DotNet::Expr, ControlFlowElement, @expr {
|
||||
* argument for, if any. For example, if this expression is `0` in
|
||||
* `M(second: 1, first: 0)` then the result is `"first"`.
|
||||
*/
|
||||
string getExplicitArgumentName() {
|
||||
expr_argument_name(this, result)
|
||||
}
|
||||
string getExplicitArgumentName() { expr_argument_name(this, result) }
|
||||
|
||||
override Element getParent() { result = ControlFlowElement.super.getParent() }
|
||||
}
|
||||
@@ -110,9 +104,7 @@ class LateBindableExpr extends Expr, @late_bindable_expr {
|
||||
}
|
||||
|
||||
/** Gets the name of the target that is late bound, if any. */
|
||||
string getLateBoundTargetName() {
|
||||
dynamic_member_name(this, result)
|
||||
}
|
||||
string getLateBoundTargetName() { dynamic_member_name(this, result) }
|
||||
}
|
||||
|
||||
private predicate isDynamicMemberAccess(@dynamic_member_access_expr e) { any() }
|
||||
@@ -124,7 +116,7 @@ private predicate isDynamicElementAccess(@dynamic_element_access_expr e) { any()
|
||||
*/
|
||||
class LocalVariableDeclExpr extends Expr, @local_var_decl_expr {
|
||||
/** Gets the local variable being declared. */
|
||||
LocalVariable getVariable() { localvars(result,_,_,_,_,this) }
|
||||
LocalVariable getVariable() { localvars(result, _, _, _, _, this) }
|
||||
|
||||
/** Gets the name of the variable being declared. */
|
||||
string getName() { result = this.getVariable().getName() }
|
||||
@@ -155,22 +147,16 @@ class LocalVariableDeclExpr extends Expr, @local_var_decl_expr {
|
||||
* Holds if this variable declaration is defined as an `out` argument,
|
||||
* for example `M(out int x)`.
|
||||
*/
|
||||
predicate isOutArgument() {
|
||||
expr_argument(this, 2)
|
||||
}
|
||||
predicate isOutArgument() { expr_argument(this, 2) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A local constant declaration, for example `const int i = 0`.
|
||||
*/
|
||||
class LocalConstantDeclExpr extends LocalVariableDeclExpr {
|
||||
LocalConstantDeclExpr() {
|
||||
super.getVariable() instanceof LocalConstant
|
||||
}
|
||||
LocalConstantDeclExpr() { super.getVariable() instanceof LocalConstant }
|
||||
|
||||
override LocalConstant getVariable() {
|
||||
localvars(result,_,_,_,_,this)
|
||||
}
|
||||
override LocalConstant getVariable() { localvars(result, _, _, _, _, this) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -281,9 +267,9 @@ class IsExpr extends Expr, @is_expr {
|
||||
/**
|
||||
* An `is` type expression, for example, `x is string` or `x is string s`.
|
||||
*/
|
||||
class IsTypeExpr extends IsExpr
|
||||
{
|
||||
class IsTypeExpr extends IsExpr {
|
||||
TypeAccess typeAccess;
|
||||
|
||||
IsTypeExpr() { typeAccess = this.getChild(1) }
|
||||
|
||||
/**
|
||||
@@ -302,9 +288,9 @@ class IsTypeExpr extends IsExpr
|
||||
/**
|
||||
* An `is` pattern expression, for example `x is string s`.
|
||||
*/
|
||||
class IsPatternExpr extends IsTypeExpr
|
||||
{
|
||||
class IsPatternExpr extends IsTypeExpr {
|
||||
LocalVariableDeclExpr typeDecl;
|
||||
|
||||
IsPatternExpr() { typeDecl = this.getChild(2) }
|
||||
|
||||
/**
|
||||
@@ -317,9 +303,9 @@ class IsPatternExpr extends IsTypeExpr
|
||||
/**
|
||||
* An `is` constant expression, for example `x is 5`.
|
||||
*/
|
||||
class IsConstantExpr extends IsExpr
|
||||
{
|
||||
class IsConstantExpr extends IsExpr {
|
||||
Expr constant;
|
||||
|
||||
IsConstantExpr() { constant = this.getChild(3) }
|
||||
|
||||
/** Gets the constant expression, for example `5` in `x is 5`. */
|
||||
@@ -353,10 +339,7 @@ class Cast extends Expr {
|
||||
override Expr stripCasts() { result = this.getExpr().stripCasts() }
|
||||
|
||||
override Expr stripImplicitCasts() {
|
||||
if this.isImplicit() then
|
||||
result = this.getExpr().stripImplicitCasts()
|
||||
else
|
||||
result = this
|
||||
if this.isImplicit() then result = this.getExpr().stripImplicitCasts() else result = this
|
||||
}
|
||||
}
|
||||
|
||||
@@ -371,11 +354,7 @@ class Cast extends Expr {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class ImplicitCast extends Cast {
|
||||
ImplicitCast() {
|
||||
this.isImplicit()
|
||||
}
|
||||
}
|
||||
class ImplicitCast extends Cast { ImplicitCast() { this.isImplicit() } }
|
||||
|
||||
/**
|
||||
* An explicit cast. For example, the explicit cast from `object` to `string`
|
||||
@@ -387,25 +366,17 @@ class ImplicitCast extends Cast {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class ExplicitCast extends Cast {
|
||||
ExplicitCast() {
|
||||
not this instanceof ImplicitCast
|
||||
}
|
||||
}
|
||||
class ExplicitCast extends Cast { ExplicitCast() { not this instanceof ImplicitCast } }
|
||||
|
||||
/**
|
||||
* An `as` expression, for example `x as string`.
|
||||
*/
|
||||
class AsExpr extends Cast, @as_expr {
|
||||
override string toString() { result = "... as ..." }
|
||||
}
|
||||
class AsExpr extends Cast, @as_expr { override string toString() { result = "... as ..." } }
|
||||
|
||||
/**
|
||||
* A cast expression, for example `(string) x`.
|
||||
*/
|
||||
class CastExpr extends Cast, @cast_expr {
|
||||
override string toString() { result = "(...) ..." }
|
||||
}
|
||||
class CastExpr extends Cast, @cast_expr { override string toString() { result = "(...) ..." } }
|
||||
|
||||
/**
|
||||
* A `typeof` expression, for example `typeof(string)`.
|
||||
@@ -431,9 +402,7 @@ class DefaultValueExpr extends Expr, @default_expr {
|
||||
TypeAccess getTypeAccess() { result = this.getChild(0) }
|
||||
|
||||
override string toString() {
|
||||
if exists(getTypeAccess())
|
||||
then result = "default(...)"
|
||||
else result = "default"
|
||||
if exists(getTypeAccess()) then result = "default(...)" else result = "default"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -449,9 +418,7 @@ class SizeofExpr extends UnaryOperation, @sizeof_expr {
|
||||
|
||||
override string getOperator() { result = "sizeof(..)" }
|
||||
|
||||
override string toString() {
|
||||
result = "sizeof(..)"
|
||||
}
|
||||
override string toString() { result = "sizeof(..)" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -514,13 +481,9 @@ class AddressOfExpr extends UnaryOperation, @address_of_expr {
|
||||
*/
|
||||
class AwaitExpr extends Expr, @await_expr {
|
||||
/** Gets the expression being awaited. */
|
||||
Expr getExpr() {
|
||||
result = getChild(0)
|
||||
}
|
||||
Expr getExpr() { result = getChild(0) }
|
||||
|
||||
override string toString() {
|
||||
result = "await ..."
|
||||
}
|
||||
override string toString() { result = "await ..." }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -535,17 +498,13 @@ class AwaitExpr extends Expr, @await_expr {
|
||||
* ```
|
||||
*/
|
||||
class NameOfExpr extends Expr, @nameof_expr {
|
||||
override string toString() {
|
||||
result = "nameof(...)"
|
||||
}
|
||||
override string toString() { result = "nameof(...)" }
|
||||
|
||||
/**
|
||||
* Gets the access in this `nameof` expression, for example `x.F` in
|
||||
* `nameof(x.F)`.
|
||||
*/
|
||||
Access getAccess() {
|
||||
result = this.getChild(0)
|
||||
}
|
||||
Access getAccess() { result = this.getChild(0) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -558,9 +517,7 @@ class NameOfExpr extends Expr, @nameof_expr {
|
||||
* ```
|
||||
*/
|
||||
class InterpolatedStringExpr extends Expr, @interpolated_string_expr {
|
||||
override string toString() {
|
||||
result = "$\"...\""
|
||||
}
|
||||
override string toString() { result = "$\"...\"" }
|
||||
|
||||
/**
|
||||
* Gets the insert at index `i` in this interpolated string, if any. For
|
||||
@@ -579,19 +536,13 @@ class InterpolatedStringExpr extends Expr, @interpolated_string_expr {
|
||||
* `$"Hello, {name}!"`. Note that there is no text element at index `i = 1`,
|
||||
* but instead an insert (`getInsert(1)` gets the insert).
|
||||
*/
|
||||
StringLiteral getText(int i) {
|
||||
result = getChild(i)
|
||||
}
|
||||
StringLiteral getText(int i) { result = getChild(i) }
|
||||
|
||||
/** Gets an insert in this interpolated string. */
|
||||
Expr getAnInsert() {
|
||||
result = getInsert(_)
|
||||
}
|
||||
Expr getAnInsert() { result = getInsert(_) }
|
||||
|
||||
/** Gets a text element in this interpolated string. */
|
||||
StringLiteral getAText() {
|
||||
result = getText(_)
|
||||
}
|
||||
StringLiteral getAText() { result = getText(_) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -621,9 +572,7 @@ class ThrowElement extends ControlFlowElement, DotNet::Throw, @throw_element {
|
||||
* `return i != 0 ? 1 / i : throw new ArgumentException("i");`
|
||||
*/
|
||||
class ThrowExpr extends Expr, ThrowElement, @throw_expr {
|
||||
override string toString() {
|
||||
result = "throw ..."
|
||||
}
|
||||
override string toString() { result = "throw ..." }
|
||||
|
||||
/**
|
||||
* Gets the expression of the exception being thrown.
|
||||
@@ -655,9 +604,7 @@ class QualifiableExpr extends Expr, @qualifiable_expr {
|
||||
final predicate hasQualifier() { exists(getQualifier()) }
|
||||
|
||||
/** Holds if this expression has an implicit `this` qualifier. */
|
||||
predicate hasImplicitThisQualifier() {
|
||||
this.getQualifier().(ThisAccess).isImplicit()
|
||||
}
|
||||
predicate hasImplicitThisQualifier() { this.getQualifier().(ThisAccess).isImplicit() }
|
||||
|
||||
/** Holds if this call has an implicit or explicit `this` qualifier. */
|
||||
predicate hasThisQualifier() {
|
||||
@@ -691,15 +638,12 @@ class QualifiableExpr extends Expr, @qualifiable_expr {
|
||||
* Holds if the target of this expression can be overridden or implemented.
|
||||
*/
|
||||
predicate targetIsOverridableOrImplementable() {
|
||||
not this.getQualifier() instanceof BaseAccess
|
||||
and
|
||||
not this.getQualifier() instanceof BaseAccess and
|
||||
this.getQualifiedDeclaration().(Virtualizable).isOverridableOrImplementable()
|
||||
}
|
||||
|
||||
/** Holds if this expression has a conditional qualifier `?.` */
|
||||
predicate isConditional() {
|
||||
conditional_access(this)
|
||||
}
|
||||
predicate isConditional() { conditional_access(this) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -717,22 +661,16 @@ class QualifiableExpr extends Expr, @qualifiable_expr {
|
||||
* ```
|
||||
*/
|
||||
class TupleExpr extends Expr, @tuple_expr {
|
||||
override string toString() { result="(..., ...)" }
|
||||
override string toString() { result = "(..., ...)" }
|
||||
|
||||
/** Gets the `i`th argument of this tuple. */
|
||||
Expr getArgument(int i) {
|
||||
result = getChild(i)
|
||||
}
|
||||
Expr getArgument(int i) { result = getChild(i) }
|
||||
|
||||
/** Gets an argument of this tuple. */
|
||||
Expr getAnArgument() {
|
||||
result = getArgument(_)
|
||||
}
|
||||
Expr getAnArgument() { result = getArgument(_) }
|
||||
|
||||
/** Holds if this tuple is a read access. */
|
||||
predicate isReadAccess() {
|
||||
not exists(AssignExpr e | this = e.getLValue().getAChildExpr*())
|
||||
}
|
||||
predicate isReadAccess() { not exists(AssignExpr e | this = e.getLValue().getAChildExpr*()) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -745,19 +683,12 @@ class TupleExpr extends Expr, @tuple_expr {
|
||||
* ```
|
||||
*/
|
||||
class RefExpr extends Expr, @ref_expr {
|
||||
|
||||
/** Gets the expression being referenced. */
|
||||
Expr getExpr() {
|
||||
result = getChild(0)
|
||||
}
|
||||
Expr getExpr() { result = getChild(0) }
|
||||
|
||||
override string toString() {
|
||||
result = "ref ..."
|
||||
}
|
||||
override string toString() { result = "ref ..." }
|
||||
|
||||
override Type getType() {
|
||||
result = getExpr().getType()
|
||||
}
|
||||
override Type getType() { result = getExpr().getType() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -767,6 +698,4 @@ class RefExpr extends Expr, @ref_expr {
|
||||
* (var name, _, _) = GetDetails();
|
||||
* ```
|
||||
*/
|
||||
class DiscardExpr extends Expr, @discard_expr {
|
||||
override string toString() { result="_" }
|
||||
}
|
||||
class DiscardExpr extends Expr, @discard_expr { override string toString() { result = "_" } }
|
||||
|
||||
@@ -15,6 +15,7 @@ private import dotnet
|
||||
*/
|
||||
class Literal extends DotNet::Literal, Expr, @literal_expr {
|
||||
override string toString() { result = this.getValue() }
|
||||
|
||||
override string getValue() { result = Expr.super.getValue() }
|
||||
}
|
||||
|
||||
@@ -88,8 +89,7 @@ class DecimalLiteral extends RealLiteral, @decimal_literal_expr { }
|
||||
* A `string` literal, for example `"Hello, World!"`.
|
||||
*/
|
||||
class StringLiteral extends DotNet::StringLiteral, Literal, @string_literal_expr {
|
||||
override string toString() {
|
||||
result = "\"" + getValue().replaceAll("\"", "\\\"") + "\"" }
|
||||
override string toString() { result = "\"" + getValue().replaceAll("\"", "\\\"") + "\"" }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*
|
||||
* All logical operations have the common base class `LogicalOperation`.
|
||||
*/
|
||||
|
||||
import Expr
|
||||
|
||||
/**
|
||||
@@ -10,9 +11,7 @@ import Expr
|
||||
* a binary logical operation (`BinaryLogicalOperation`), or a ternary logical
|
||||
* operation (`TernaryLogicalOperation`).
|
||||
*/
|
||||
class LogicalOperation extends Operation, @log_expr {
|
||||
override string getOperator() { none() }
|
||||
}
|
||||
class LogicalOperation extends Operation, @log_expr { override string getOperator() { none() } }
|
||||
|
||||
/**
|
||||
* A unary logical operation, that is, a logical 'not' (`LogicalNotExpr`).
|
||||
@@ -31,7 +30,7 @@ class LogicalNotExpr extends UnaryLogicalOperation, @log_not_expr {
|
||||
* a logical 'or' (`LogicalAndExpr`), or a null-coalescing operation
|
||||
* (`NullCoalescingExpr`).
|
||||
*/
|
||||
class BinaryLogicalOperation extends LogicalOperation, BinaryOperation, @bin_log_op_expr{
|
||||
class BinaryLogicalOperation extends LogicalOperation, BinaryOperation, @bin_log_op_expr {
|
||||
override string getOperator() { none() }
|
||||
}
|
||||
|
||||
@@ -66,7 +65,7 @@ class NullCoalescingExpr extends BinaryLogicalOperation, @null_coalescing_expr {
|
||||
* A ternary logical operation, that is, a ternary conditional expression
|
||||
* (`ConditionalExpr`).
|
||||
*/
|
||||
class TernaryLogicalOperation extends LogicalOperation, TernaryOperation, @ternary_log_op_expr{ }
|
||||
class TernaryLogicalOperation extends LogicalOperation, TernaryOperation, @ternary_log_op_expr { }
|
||||
|
||||
/**
|
||||
* A conditional expression, for example `s != null ? s.Length : -1`
|
||||
@@ -83,12 +82,12 @@ class ConditionalExpr extends TernaryLogicalOperation, @conditional_expr {
|
||||
Expr getCondition() { result = this.getChild(0) }
|
||||
|
||||
/** Gets the "then" expression of this conditional expression. */
|
||||
Expr getThen() {result = this.getChild(1) }
|
||||
Expr getThen() { result = this.getChild(1) }
|
||||
|
||||
/** Gets the "else" expression of this conditional expression. */
|
||||
Expr getElse() { result = this.getChild(2) }
|
||||
|
||||
override string getOperator() { result = "?" }
|
||||
|
||||
override string toString() { result = "... ? ... : ..." }
|
||||
override string toString() { result = "... ? ... : ..." }
|
||||
}
|
||||
|
||||
@@ -9,8 +9,7 @@ private import semmle.code.csharp.frameworks.system.Text
|
||||
/** A method that formats a string, for example `string.Format()`. */
|
||||
class FormatMethod extends Method {
|
||||
FormatMethod() {
|
||||
exists(Class declType |
|
||||
declType = this.getDeclaringType() |
|
||||
exists(Class declType | declType = this.getDeclaringType() |
|
||||
this.getParameter(0).getType() instanceof SystemIFormatProviderInterface and
|
||||
this.getParameter(1).getType() instanceof StringType and
|
||||
this.getNumberOfParameters() >= 3 and
|
||||
@@ -27,20 +26,22 @@ class FormatMethod extends Method {
|
||||
or
|
||||
this = any(SystemTextStringBuilderClass c).getAppendFormatMethod()
|
||||
or
|
||||
(this.hasName("Write") or this.hasName("WriteLine")) and
|
||||
(
|
||||
(this.hasName("Write") or this.hasName("WriteLine")) and
|
||||
(
|
||||
declType.hasQualifiedName("System.Console")
|
||||
or
|
||||
declType.hasQualifiedName("System.IO.TextWriter")
|
||||
or
|
||||
declType.hasQualifiedName("System.Diagnostics.Debug") and
|
||||
this.getParameter(1).getType() instanceof ArrayType
|
||||
)
|
||||
declType.hasQualifiedName("System.Console")
|
||||
or
|
||||
declType.hasQualifiedName("System.IO.TextWriter")
|
||||
or
|
||||
declType.hasQualifiedName("System.Diagnostics.Debug") and
|
||||
this.getParameter(1).getType() instanceof ArrayType
|
||||
)
|
||||
or
|
||||
declType.hasQualifiedName("System.Diagnostics.Trace") and
|
||||
(this.hasName("TraceError") or this.hasName("TraceInformation") or this.hasName("TraceWarning"))
|
||||
(
|
||||
this.hasName("TraceError") or
|
||||
this.hasName("TraceInformation") or
|
||||
this.hasName("TraceWarning")
|
||||
)
|
||||
or
|
||||
this.hasName("TraceInformation") and
|
||||
declType.hasQualifiedName("System.Diagnostics.TraceSource")
|
||||
@@ -60,13 +61,14 @@ class FormatMethod extends Method {
|
||||
* `string.Format(IFormatProvider, String, Object)` is `1`.
|
||||
*/
|
||||
int getFormatArgument() {
|
||||
if this.getParameter(0).getType() instanceof SystemIFormatProviderInterface then
|
||||
result = 1
|
||||
if this.getParameter(0).getType() instanceof SystemIFormatProviderInterface
|
||||
then result = 1
|
||||
else
|
||||
if this.hasName("Assert") and this.getDeclaringType().hasQualifiedName("System.Diagnostics.Debug") then
|
||||
result = 2
|
||||
else
|
||||
result = 0
|
||||
if
|
||||
this.hasName("Assert") and
|
||||
this.getDeclaringType().hasQualifiedName("System.Diagnostics.Debug")
|
||||
then result = 2
|
||||
else result = 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,14 +80,16 @@ class FormatMethod extends Method {
|
||||
class StringFormatItemParameter extends Parameter {
|
||||
StringFormatItemParameter() {
|
||||
// Parameter of a format method
|
||||
exists(FormatMethod m, int pos |
|
||||
m = this.getCallable() |
|
||||
exists(FormatMethod m, int pos | m = this.getCallable() |
|
||||
pos = this.getPosition() and
|
||||
pos > m.getFormatArgument()
|
||||
)
|
||||
or
|
||||
// Parameter of a source method that forwards to `string.Format()`
|
||||
exists(AssignableDefinitions::ImplicitParameterDefinition def, ParameterRead pr, StringFormatItemParameter other |
|
||||
exists(
|
||||
AssignableDefinitions::ImplicitParameterDefinition def, ParameterRead pr,
|
||||
StringFormatItemParameter other
|
||||
|
|
||||
def.getParameter() = this and
|
||||
pr = def.getAReachableRead() and
|
||||
pr.getAControlFlowNode().postDominates(this.getCallable().getEntryPoint()) and
|
||||
@@ -96,16 +100,11 @@ class StringFormatItemParameter extends Parameter {
|
||||
}
|
||||
|
||||
private Type getParameterType(Parameter p) {
|
||||
if p.isParams() then
|
||||
result = p.getType().(ArrayType).getElementType()
|
||||
else
|
||||
result = p.getType()
|
||||
if p.isParams() then result = p.getType().(ArrayType).getElementType() else result = p.getType()
|
||||
}
|
||||
|
||||
/** Regex for a valid insert. */
|
||||
private string getFormatInsertRegex() {
|
||||
result = "\\{(\\d+)\\s*(,\\s*-?\\d+\\s*)?(:[^{}]+)?\\}"
|
||||
}
|
||||
private string getFormatInsertRegex() { result = "\\{(\\d+)\\s*(,\\s*-?\\d+\\s*)?(:[^{}]+)?\\}" }
|
||||
|
||||
/**
|
||||
* Regex for a valid token in the string.
|
||||
@@ -113,35 +112,28 @@ private string getFormatInsertRegex() {
|
||||
* Note that the format string can be tokenised using this regex.
|
||||
*/
|
||||
private string getValidFormatTokenRegex() {
|
||||
result="[^{}]|\\{\\{|\\}\\}|" + getFormatInsertRegex()
|
||||
result = "[^{}]|\\{\\{|\\}\\}|" + getFormatInsertRegex()
|
||||
}
|
||||
|
||||
/** Regex for the entire format string */
|
||||
private string getValidFormatRegex() {
|
||||
result = "(" + getValidFormatTokenRegex() + ")*+"
|
||||
}
|
||||
private string getValidFormatRegex() { result = "(" + getValidFormatTokenRegex() + ")*+" }
|
||||
|
||||
/** A literal with a valid format string. */
|
||||
class ValidFormatString extends StringLiteral {
|
||||
ValidFormatString() {
|
||||
this.getValue().regexpMatch(getValidFormatRegex())
|
||||
}
|
||||
ValidFormatString() { this.getValue().regexpMatch(getValidFormatRegex()) }
|
||||
|
||||
/** Gets the token at the given position in the string. */
|
||||
string getToken(int outPosition) {
|
||||
result = this.getValue().regexpFind(getValidFormatTokenRegex(), _, outPosition)
|
||||
}
|
||||
|
||||
|
||||
/**Gets the insert number at the given position in the string.*/
|
||||
/**Gets the insert number at the given position in the string. */
|
||||
int getInsert(int position) {
|
||||
result = this.getToken(position).regexpCapture(getFormatInsertRegex(), 1).toInt()
|
||||
}
|
||||
|
||||
/** Gets any insert number in the string.*/
|
||||
int getAnInsert(){
|
||||
result = this.getInsert(_)
|
||||
}
|
||||
/** Gets any insert number in the string. */
|
||||
int getAnInsert() { result = this.getInsert(_) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,20 +142,22 @@ class ValidFormatString extends StringLiteral {
|
||||
* Such a literal would cause `string.Format()` to throw an exception.
|
||||
*/
|
||||
class InvalidFormatString extends StringLiteral {
|
||||
InvalidFormatString() {
|
||||
not this.getValue().regexpMatch(getValidFormatRegex())
|
||||
}
|
||||
InvalidFormatString() { not this.getValue().regexpMatch(getValidFormatRegex()) }
|
||||
|
||||
/** Gets the offset of the first error. */
|
||||
int getInvalidOffset() {
|
||||
result = this.getValue().regexpFind(getValidFormatRegex(),0,0).length()
|
||||
result = this.getValue().regexpFind(getValidFormatRegex(), 0, 0).length()
|
||||
}
|
||||
|
||||
override string getURL() {
|
||||
exists(string filepath, int startline, int startcolumn, int endline, int endcolumn, int oldstartcolumn, int padding |
|
||||
exists(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn,
|
||||
int oldstartcolumn, int padding
|
||||
|
|
||||
this.getLocation().hasLocationInfo(filepath, startline, oldstartcolumn, endline, endcolumn) and
|
||||
startcolumn = padding + oldstartcolumn + getInvalidOffset() and
|
||||
toUrl(filepath, startline, startcolumn, endline, endcolumn, result) |
|
||||
toUrl(filepath, startline, startcolumn, endline, endcolumn, result)
|
||||
|
|
||||
// Single-line string literal beginning " or @"
|
||||
// Figure out the correct indent.
|
||||
startline = endline and
|
||||
@@ -183,9 +177,7 @@ private module FormatFlow {
|
||||
private class FormatConfiguration extends DataFlow::Configuration {
|
||||
FormatConfiguration() { this = "format" }
|
||||
|
||||
override predicate isSource(DataFlow::Node n) {
|
||||
n.asExpr() instanceof StringLiteral
|
||||
}
|
||||
override predicate isSource(DataFlow::Node n) { n.asExpr() instanceof StringLiteral }
|
||||
|
||||
override predicate isSink(DataFlow::Node n) {
|
||||
exists(FormatCall c | n.asExpr() = c.getFormatExpr())
|
||||
@@ -194,7 +186,8 @@ private module FormatFlow {
|
||||
|
||||
predicate hasFlow(StringLiteral lit, Expr format) {
|
||||
exists(DataFlow::Node n1, DataFlow::Node n2, FormatConfiguration conf |
|
||||
n1.asExpr() = lit and n2.asExpr() = format |
|
||||
n1.asExpr() = lit and n2.asExpr() = format
|
||||
|
|
||||
conf.hasFlow(n1, n2)
|
||||
)
|
||||
}
|
||||
@@ -205,24 +198,16 @@ private module FormatFlow {
|
||||
* to `string.Format()`.
|
||||
*/
|
||||
class FormatCall extends MethodCall {
|
||||
FormatCall() {
|
||||
this.getTarget() instanceof FormatMethod
|
||||
}
|
||||
FormatCall() { this.getTarget() instanceof FormatMethod }
|
||||
|
||||
/** Gets the expression used as the format string. */
|
||||
Expr getFormatExpr() {
|
||||
result = this.getArgument(this.getFormatArgument())
|
||||
}
|
||||
Expr getFormatExpr() { result = this.getArgument(this.getFormatArgument()) }
|
||||
|
||||
/** Gets the argument number containing the format string. */
|
||||
int getFormatArgument() {
|
||||
result = this.getTarget().(FormatMethod).getFormatArgument()
|
||||
}
|
||||
int getFormatArgument() { result = this.getTarget().(FormatMethod).getFormatArgument() }
|
||||
|
||||
/** Gets the argument number of the first supplied insert. */
|
||||
int getFirstArgument() {
|
||||
result = this.getFormatArgument() + 1
|
||||
}
|
||||
int getFirstArgument() { result = this.getFormatArgument() + 1 }
|
||||
|
||||
/** Holds if the arguments are supplied in an array, not individually. */
|
||||
predicate hasArrayExpr() {
|
||||
@@ -234,9 +219,7 @@ class FormatCall extends MethodCall {
|
||||
* Gets a format string. Global data flow analysis is applied to retrieve all
|
||||
* sources that can reach this method call.
|
||||
*/
|
||||
StringLiteral getAFormatSource() {
|
||||
FormatFlow::hasFlow(result, this.getFormatExpr())
|
||||
}
|
||||
StringLiteral getAFormatSource() { FormatFlow::hasFlow(result, this.getFormatExpr()) }
|
||||
|
||||
/**
|
||||
* Gets the number of supplied arguments (excluding the format string and format
|
||||
@@ -249,9 +232,7 @@ class FormatCall extends MethodCall {
|
||||
}
|
||||
|
||||
/** Gets an index if a supplied argument. */
|
||||
int getASuppliedArgument() {
|
||||
result in [0 .. this.getSuppliedArguments() - 1]
|
||||
}
|
||||
int getASuppliedArgument() { result in [0 .. this.getSuppliedArguments() - 1] }
|
||||
|
||||
/** Get the expression supplied at the given position. */
|
||||
Expr getSuppliedExpr(int index) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/** Provides definitions related to the namespace `Microsoft`. */
|
||||
|
||||
import csharp
|
||||
|
||||
/** The `Microsoft` namespace. */
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
/** Provides definitions related to the namespace `Moq`. */
|
||||
|
||||
import csharp
|
||||
|
||||
/** The `Moq.Language` Namespace. */
|
||||
class MoqLanguageNamespace extends Namespace {
|
||||
MoqLanguageNamespace() {
|
||||
this.hasQualifiedName("Moq.Language")
|
||||
}
|
||||
MoqLanguageNamespace() { this.hasQualifiedName("Moq.Language") }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -23,11 +22,9 @@ class ReturnsMethod extends Method {
|
||||
Expr getAReturnedExpr() {
|
||||
exists(MethodCall mc, Expr arg |
|
||||
mc = getACall() and
|
||||
arg = mc.getArgument(0) |
|
||||
if arg instanceof LambdaExpr then
|
||||
arg.(LambdaExpr).canReturn(result)
|
||||
else
|
||||
result = arg
|
||||
arg = mc.getArgument(0)
|
||||
|
|
||||
if arg instanceof LambdaExpr then arg.(LambdaExpr).canReturn(result) else result = arg
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -35,8 +32,7 @@ class ReturnsMethod extends Method {
|
||||
/** An object creation that is returned by a mock. */
|
||||
class ReturnedByMockObject extends ObjectCreation {
|
||||
ReturnedByMockObject() {
|
||||
exists(Variable v |
|
||||
this = v.getAnAssignedValue() |
|
||||
exists(Variable v | this = v.getAnAssignedValue() |
|
||||
v.getAnAccess() = any(ReturnsMethod rm).getAReturnedExpr().getAChild*()
|
||||
)
|
||||
}
|
||||
@@ -45,6 +41,6 @@ class ReturnedByMockObject extends ObjectCreation {
|
||||
* Gets a value used to initialize a member of this object creation.
|
||||
*/
|
||||
Expr getAMemberInitializationValue() {
|
||||
result = this.getInitializer().(ObjectInitializer).getAMemberInitializer().getRValue()
|
||||
result = this.getInitializer().(ObjectInitializer).getAMemberInitializer().getRValue()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/** Provides definitions related to SQL frameworks. */
|
||||
|
||||
import csharp
|
||||
private import semmle.code.csharp.frameworks.system.Data
|
||||
private import semmle.code.csharp.frameworks.system.data.Entity
|
||||
@@ -15,44 +16,38 @@ class CommandTextAssignmentSqlExpr extends SqlExpr, AssignExpr {
|
||||
CommandTextAssignmentSqlExpr() {
|
||||
exists(Property p, SystemDataIDbCommandInterface i, Property text |
|
||||
p = this.getLValue().(PropertyAccess).getTarget() and
|
||||
text = i.getCommandTextProperty() |
|
||||
text = i.getCommandTextProperty()
|
||||
|
|
||||
p.overridesOrImplementsOrEquals(text)
|
||||
)
|
||||
}
|
||||
|
||||
override
|
||||
Expr getSql() { result = getRValue() }
|
||||
override Expr getSql() { result = getRValue() }
|
||||
}
|
||||
|
||||
/** A construction of an `IDbCommand` object. */
|
||||
class IDbCommandConstructionSqlExpr extends SqlExpr, ObjectCreation {
|
||||
IDbCommandConstructionSqlExpr() {
|
||||
exists(InstanceConstructor ic |
|
||||
ic = this.getTarget() |
|
||||
ic.getDeclaringType().getABaseType*() instanceof SystemDataIDbCommandInterface
|
||||
and
|
||||
exists(InstanceConstructor ic | ic = this.getTarget() |
|
||||
ic.getDeclaringType().getABaseType*() instanceof SystemDataIDbCommandInterface and
|
||||
ic.getParameter(0).getType() instanceof StringType
|
||||
)
|
||||
}
|
||||
|
||||
override
|
||||
Expr getSql() { result = getArgument(0) }
|
||||
override Expr getSql() { result = getArgument(0) }
|
||||
}
|
||||
|
||||
/** A construction of an `SqlDataAdapter` object. */
|
||||
class SqlDataAdapterConstructionSqlExpr extends SqlExpr, ObjectCreation {
|
||||
SqlDataAdapterConstructionSqlExpr() {
|
||||
exists(InstanceConstructor ic |
|
||||
ic = this.getTarget()
|
||||
and
|
||||
ic.getDeclaringType() instanceof SystemDataSqlClientSqlDataAdapterClass
|
||||
and
|
||||
ic = this.getTarget() and
|
||||
ic.getDeclaringType() instanceof SystemDataSqlClientSqlDataAdapterClass and
|
||||
ic.getParameter(0).getType() instanceof StringType
|
||||
)
|
||||
}
|
||||
|
||||
override
|
||||
Expr getSql() { result = getArgument(0) }
|
||||
override Expr getSql() { result = getArgument(0) }
|
||||
}
|
||||
|
||||
/** A `MySql.Data.MySqlClient.MySqlHelper` method. */
|
||||
@@ -61,8 +56,7 @@ class MySqlHelperMethodCallSqlExpr extends SqlExpr, MethodCall {
|
||||
this.getQualifier().getType().(Class).hasQualifiedName("MySql.Data.MySqlClient", "MySqlHelper")
|
||||
}
|
||||
|
||||
override
|
||||
Expr getSql() {
|
||||
override Expr getSql() {
|
||||
exists(int i |
|
||||
result = getArgument(i) and
|
||||
this.getTarget().getParameter(i).hasName("commandText") and
|
||||
@@ -74,11 +68,14 @@ class MySqlHelperMethodCallSqlExpr extends SqlExpr, MethodCall {
|
||||
/** A `Microsoft.ApplicationBlocks.Data.SqlHelper` method. */
|
||||
class MicrosoftSqlHelperMethodCallSqlExpr extends SqlExpr, MethodCall {
|
||||
MicrosoftSqlHelperMethodCallSqlExpr() {
|
||||
this.getQualifier().getType().(Class).hasQualifiedName("Microsoft.ApplicationBlocks.Data", "SqlHelper")
|
||||
this
|
||||
.getQualifier()
|
||||
.getType()
|
||||
.(Class)
|
||||
.hasQualifiedName("Microsoft.ApplicationBlocks.Data", "SqlHelper")
|
||||
}
|
||||
|
||||
override
|
||||
Expr getSql() {
|
||||
override Expr getSql() {
|
||||
exists(int i |
|
||||
result = getArgument(i) and
|
||||
this.getTarget().getParameter(i).hasName("commandText") and
|
||||
@@ -93,10 +90,7 @@ class SystemDataEntityDbSetSqlExpr extends SqlExpr, MethodCall {
|
||||
this.getTarget() = any(SystemDataEntity::DbSet dbSet).getSqlQueryMethod()
|
||||
}
|
||||
|
||||
override
|
||||
Expr getSql() {
|
||||
result = getArgumentForName("sql")
|
||||
}
|
||||
override Expr getSql() { result = getArgumentForName("sql") }
|
||||
}
|
||||
|
||||
/** A call to a method in `System.Data.Entity.Database` that executes SQL. */
|
||||
@@ -109,8 +103,5 @@ class SystemDataEntityDatabaseSqlExpr extends SqlExpr, MethodCall {
|
||||
)
|
||||
}
|
||||
|
||||
override
|
||||
Expr getSql() {
|
||||
result = getArgumentForName("sql")
|
||||
}
|
||||
override Expr getSql() { result = getArgumentForName("sql") }
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/** Provides definitions related to the namespace `System`. */
|
||||
|
||||
import csharp
|
||||
private import system.Reflection
|
||||
|
||||
@@ -11,144 +12,94 @@ class SystemNamespace extends Namespace {
|
||||
}
|
||||
|
||||
/** A class in the `System` namespace. */
|
||||
class SystemClass extends Class {
|
||||
SystemClass() {
|
||||
this.getNamespace() instanceof SystemNamespace
|
||||
}
|
||||
}
|
||||
class SystemClass extends Class { SystemClass() { this.getNamespace() instanceof SystemNamespace } }
|
||||
|
||||
/** An unbound generic class in the `System` namespace. */
|
||||
class SystemUnboundGenericClass extends UnboundGenericClass {
|
||||
SystemUnboundGenericClass() {
|
||||
this.getNamespace() instanceof SystemNamespace
|
||||
}
|
||||
SystemUnboundGenericClass() { this.getNamespace() instanceof SystemNamespace }
|
||||
}
|
||||
|
||||
/** An interface in the `System` namespace. */
|
||||
class SystemInterface extends Interface {
|
||||
SystemInterface() {
|
||||
this.getNamespace() instanceof SystemNamespace
|
||||
}
|
||||
SystemInterface() { this.getNamespace() instanceof SystemNamespace }
|
||||
}
|
||||
|
||||
/** An unbound generic interface in the `System` namespace. */
|
||||
class SystemUnboundGenericInterface extends UnboundGenericInterface {
|
||||
SystemUnboundGenericInterface() {
|
||||
this.getNamespace() instanceof SystemNamespace
|
||||
}
|
||||
SystemUnboundGenericInterface() { this.getNamespace() instanceof SystemNamespace }
|
||||
}
|
||||
|
||||
/** A delegate type in the `System` namespace. */
|
||||
class SystemDelegateType extends DelegateType {
|
||||
SystemDelegateType() {
|
||||
this.getNamespace() instanceof SystemNamespace
|
||||
}
|
||||
SystemDelegateType() { this.getNamespace() instanceof SystemNamespace }
|
||||
}
|
||||
|
||||
/** An unbound generic delegate type in the `System` namespace. */
|
||||
class SystemUnboundGenericDelegateType extends UnboundGenericDelegateType {
|
||||
SystemUnboundGenericDelegateType() {
|
||||
this.getNamespace() instanceof SystemNamespace
|
||||
}
|
||||
SystemUnboundGenericDelegateType() { this.getNamespace() instanceof SystemNamespace }
|
||||
}
|
||||
|
||||
/** The `System.Action` delegate type. */
|
||||
class SystemActionDelegateType extends SystemDelegateType {
|
||||
SystemActionDelegateType() {
|
||||
this.getName() = "Action"
|
||||
}
|
||||
SystemActionDelegateType() { this.getName() = "Action" }
|
||||
}
|
||||
|
||||
/** The `System.Action<T1, ..., Tn>` delegate type. */
|
||||
class SystemActionTDelegateType extends SystemUnboundGenericDelegateType {
|
||||
SystemActionTDelegateType() {
|
||||
this.getName().regexpMatch("Action<,*>")
|
||||
}
|
||||
SystemActionTDelegateType() { this.getName().regexpMatch("Action<,*>") }
|
||||
}
|
||||
|
||||
/** `System.Array` class. */
|
||||
class SystemArrayClass extends SystemClass {
|
||||
SystemArrayClass() {
|
||||
this.hasName("Array")
|
||||
}
|
||||
}
|
||||
class SystemArrayClass extends SystemClass { SystemArrayClass() { this.hasName("Array") } }
|
||||
|
||||
/** The `System.Boolean` structure. */
|
||||
class SystemBooleanStruct extends BoolType {
|
||||
/** Gets the `Parse(string)` method. */
|
||||
Method getParseMethod() {
|
||||
result.getDeclaringType() = this
|
||||
and
|
||||
result.hasName("Parse")
|
||||
and
|
||||
result.getNumberOfParameters() = 1
|
||||
and
|
||||
result.getParameter(0).getType() instanceof StringType
|
||||
and
|
||||
result.getDeclaringType() = this and
|
||||
result.hasName("Parse") and
|
||||
result.getNumberOfParameters() = 1 and
|
||||
result.getParameter(0).getType() instanceof StringType and
|
||||
result.getReturnType() instanceof BoolType
|
||||
}
|
||||
|
||||
/** Gets the `TryParse(string, out bool)` method. */
|
||||
Method getTryParseMethod() {
|
||||
result.getDeclaringType() = this
|
||||
and
|
||||
result.hasName("TryParse")
|
||||
and
|
||||
result.getNumberOfParameters() = 2
|
||||
and
|
||||
result.getParameter(0).getType() instanceof StringType
|
||||
and
|
||||
result.getParameter(1).getType() instanceof BoolType
|
||||
and
|
||||
result.getDeclaringType() = this and
|
||||
result.hasName("TryParse") and
|
||||
result.getNumberOfParameters() = 2 and
|
||||
result.getParameter(0).getType() instanceof StringType and
|
||||
result.getParameter(1).getType() instanceof BoolType and
|
||||
result.getReturnType() instanceof BoolType
|
||||
}
|
||||
}
|
||||
|
||||
/** The `System.Convert` class. */
|
||||
class SystemConvertClass extends SystemClass {
|
||||
SystemConvertClass() {
|
||||
this.hasName("Convert")
|
||||
}
|
||||
}
|
||||
class SystemConvertClass extends SystemClass { SystemConvertClass() { this.hasName("Convert") } }
|
||||
|
||||
/** `System.Delegate` class. */
|
||||
class SystemDelegateClass extends SystemClass {
|
||||
SystemDelegateClass() {
|
||||
this.hasName("Delegate")
|
||||
}
|
||||
}
|
||||
class SystemDelegateClass extends SystemClass { SystemDelegateClass() { this.hasName("Delegate") } }
|
||||
|
||||
/** The `System.DivideByZeroException` class. */
|
||||
class SystemDivideByZeroExceptionClass extends SystemClass {
|
||||
SystemDivideByZeroExceptionClass() {
|
||||
this.hasName("DivideByZeroException")
|
||||
}
|
||||
SystemDivideByZeroExceptionClass() { this.hasName("DivideByZeroException") }
|
||||
}
|
||||
|
||||
/** The `System.Enum` class. */
|
||||
class SystemEnumClass extends SystemClass {
|
||||
SystemEnumClass() {
|
||||
this.hasName("Enum")
|
||||
}
|
||||
}
|
||||
class SystemEnumClass extends SystemClass { SystemEnumClass() { this.hasName("Enum") } }
|
||||
|
||||
/** The `System.Exception` class. */
|
||||
class SystemExceptionClass extends SystemClass {
|
||||
SystemExceptionClass() {
|
||||
this.hasName("Exception")
|
||||
}
|
||||
SystemExceptionClass() { this.hasName("Exception") }
|
||||
}
|
||||
|
||||
/** The `System.Func<T1, ..., Tn, TResult>` delegate type. */
|
||||
class SystemFuncDelegateType extends SystemUnboundGenericDelegateType {
|
||||
SystemFuncDelegateType() {
|
||||
this.getName().regexpMatch("Func<,*>")
|
||||
}
|
||||
SystemFuncDelegateType() { this.getName().regexpMatch("Func<,*>") }
|
||||
|
||||
/** Gets one of the `Ti` input type parameters. */
|
||||
TypeParameter getAnInputTypeParameter() {
|
||||
exists(int i |
|
||||
i in [0 .. this.getNumberOfTypeParameters() - 2] |
|
||||
exists(int i | i in [0 .. this.getNumberOfTypeParameters() - 2] |
|
||||
result = this.getTypeParameter(i)
|
||||
)
|
||||
}
|
||||
@@ -161,236 +112,170 @@ class SystemFuncDelegateType extends SystemUnboundGenericDelegateType {
|
||||
|
||||
/** The `System.IComparable` interface. */
|
||||
class SystemIComparableInterface extends SystemInterface {
|
||||
SystemIComparableInterface() {
|
||||
this.hasName("IComparable")
|
||||
}
|
||||
SystemIComparableInterface() { this.hasName("IComparable") }
|
||||
|
||||
/** Gets the `CompareTo(object)` method. */
|
||||
Method getCompareToMethod() {
|
||||
result.getDeclaringType() = this
|
||||
and
|
||||
result.hasName("CompareTo")
|
||||
and
|
||||
result.getNumberOfParameters() = 1
|
||||
and
|
||||
result.getParameter(0).getType() instanceof ObjectType
|
||||
and
|
||||
result.getDeclaringType() = this and
|
||||
result.hasName("CompareTo") and
|
||||
result.getNumberOfParameters() = 1 and
|
||||
result.getParameter(0).getType() instanceof ObjectType and
|
||||
result.getReturnType() instanceof IntType
|
||||
}
|
||||
}
|
||||
|
||||
/** The `System.IComparable<T>` interface. */
|
||||
class SystemIComparableTInterface extends SystemUnboundGenericInterface {
|
||||
SystemIComparableTInterface() {
|
||||
this.hasName("IComparable<>")
|
||||
}
|
||||
SystemIComparableTInterface() { this.hasName("IComparable<>") }
|
||||
|
||||
/** Gets the `CompareTo(T)` method. */
|
||||
Method getCompareToMethod() {
|
||||
result.getDeclaringType() = this
|
||||
and
|
||||
result.hasName("CompareTo")
|
||||
and
|
||||
result.getNumberOfParameters() = 1
|
||||
and
|
||||
result.getParameter(0).getType() = getTypeParameter(0)
|
||||
and
|
||||
result.getDeclaringType() = this and
|
||||
result.hasName("CompareTo") and
|
||||
result.getNumberOfParameters() = 1 and
|
||||
result.getParameter(0).getType() = getTypeParameter(0) and
|
||||
result.getReturnType() instanceof IntType
|
||||
}
|
||||
}
|
||||
|
||||
/** The `System.IEquatable<T>` interface. */
|
||||
class SystemIEquatableTInterface extends SystemUnboundGenericInterface {
|
||||
SystemIEquatableTInterface() {
|
||||
this.hasName("IEquatable<>")
|
||||
}
|
||||
SystemIEquatableTInterface() { this.hasName("IEquatable<>") }
|
||||
|
||||
/** Gets the `Equals(T)` method. */
|
||||
Method getEqualsMethod() {
|
||||
result.getDeclaringType() = this
|
||||
and
|
||||
result.hasName("Equals")
|
||||
and
|
||||
result.getNumberOfParameters() = 1
|
||||
and
|
||||
result.getParameter(0).getType() = getTypeParameter(0)
|
||||
and
|
||||
result.getDeclaringType() = this and
|
||||
result.hasName("Equals") and
|
||||
result.getNumberOfParameters() = 1 and
|
||||
result.getParameter(0).getType() = getTypeParameter(0) and
|
||||
result.getReturnType() instanceof BoolType
|
||||
}
|
||||
}
|
||||
|
||||
/** The `System.IFormatProvider` interface. */
|
||||
class SystemIFormatProviderInterface extends SystemInterface {
|
||||
SystemIFormatProviderInterface() {
|
||||
this.hasName("IFormatProvider")
|
||||
}
|
||||
SystemIFormatProviderInterface() { this.hasName("IFormatProvider") }
|
||||
}
|
||||
|
||||
/** The `System.Int32` structure. */
|
||||
class SystemInt32Struct extends IntType {
|
||||
/** Gets the `Parse(string, ...)` method. */
|
||||
Method getParseMethod() {
|
||||
result.getDeclaringType() = this
|
||||
and
|
||||
result.hasName("Parse")
|
||||
and
|
||||
result.getParameter(0).getType() instanceof StringType
|
||||
and
|
||||
result.getDeclaringType() = this and
|
||||
result.hasName("Parse") and
|
||||
result.getParameter(0).getType() instanceof StringType and
|
||||
result.getReturnType() instanceof IntType
|
||||
}
|
||||
|
||||
/** Gets the `TryParse(string, ..., out int)` method. */
|
||||
Method getTryParseMethod() {
|
||||
result.getDeclaringType() = this
|
||||
and
|
||||
result.hasName("TryParse")
|
||||
and
|
||||
result.getParameter(0).getType() instanceof StringType
|
||||
and
|
||||
result.getParameter(result.getNumberOfParameters() - 1).getType() instanceof IntType
|
||||
and
|
||||
result.getDeclaringType() = this and
|
||||
result.hasName("TryParse") and
|
||||
result.getParameter(0).getType() instanceof StringType and
|
||||
result.getParameter(result.getNumberOfParameters() - 1).getType() instanceof IntType and
|
||||
result.getReturnType() instanceof BoolType
|
||||
}
|
||||
}
|
||||
|
||||
/** The `System.InvalidCastException` class. */
|
||||
class SystemInvalidCastExceptionClass extends SystemClass {
|
||||
SystemInvalidCastExceptionClass() {
|
||||
this.hasName("InvalidCastException")
|
||||
}
|
||||
SystemInvalidCastExceptionClass() { this.hasName("InvalidCastException") }
|
||||
}
|
||||
|
||||
/** The `System.Lazy<T>` class. */
|
||||
class SystemLazyClass extends SystemUnboundGenericClass {
|
||||
SystemLazyClass() {
|
||||
this.hasName("Lazy<>")
|
||||
and
|
||||
this.hasName("Lazy<>") and
|
||||
this.getNumberOfTypeParameters() = 1
|
||||
}
|
||||
|
||||
/** Gets the `Value` property. */
|
||||
Property getValueProperty() {
|
||||
result.getDeclaringType() = this
|
||||
and
|
||||
result.hasName("Value")
|
||||
and
|
||||
result.getDeclaringType() = this and
|
||||
result.hasName("Value") and
|
||||
result.getType() = getTypeParameter(0)
|
||||
}
|
||||
}
|
||||
|
||||
/** The `System.NullReferenceException` class. */
|
||||
class SystemNullReferenceExceptionClass extends SystemClass {
|
||||
SystemNullReferenceExceptionClass() {
|
||||
this.hasName("NullReferenceException")
|
||||
}
|
||||
SystemNullReferenceExceptionClass() { this.hasName("NullReferenceException") }
|
||||
}
|
||||
|
||||
/** The `System.Object` class. */
|
||||
class SystemObjectClass extends SystemClass {
|
||||
SystemObjectClass() {
|
||||
this instanceof ObjectType
|
||||
}
|
||||
SystemObjectClass() { this instanceof ObjectType }
|
||||
|
||||
/** Gets the `Equals(object)` method. */
|
||||
Method getEqualsMethod() {
|
||||
result.getDeclaringType() = this
|
||||
and
|
||||
result.hasName("Equals")
|
||||
and
|
||||
result.getNumberOfParameters() = 1
|
||||
and
|
||||
result.getParameter(0).getType() instanceof ObjectType
|
||||
and
|
||||
result.getDeclaringType() = this and
|
||||
result.hasName("Equals") and
|
||||
result.getNumberOfParameters() = 1 and
|
||||
result.getParameter(0).getType() instanceof ObjectType and
|
||||
result.getReturnType() instanceof BoolType
|
||||
}
|
||||
|
||||
/** Gets the `Equals(object, object)` method. */
|
||||
Method getStaticEqualsMethod() {
|
||||
result.isStatic()
|
||||
and
|
||||
result.getDeclaringType() = this
|
||||
and
|
||||
result.hasName("Equals")
|
||||
and
|
||||
result.getNumberOfParameters() = 2
|
||||
and
|
||||
result.getParameter(0).getType() instanceof ObjectType
|
||||
and
|
||||
result.getParameter(1).getType() instanceof ObjectType
|
||||
and
|
||||
result.isStatic() and
|
||||
result.getDeclaringType() = this and
|
||||
result.hasName("Equals") and
|
||||
result.getNumberOfParameters() = 2 and
|
||||
result.getParameter(0).getType() instanceof ObjectType and
|
||||
result.getParameter(1).getType() instanceof ObjectType and
|
||||
result.getReturnType() instanceof BoolType
|
||||
}
|
||||
|
||||
/** Gets the `ReferenceEquals(object, object)` method. */
|
||||
Method getReferenceEqualsMethod() {
|
||||
result.isStatic()
|
||||
and
|
||||
result.getDeclaringType() = this
|
||||
and
|
||||
result.hasName("ReferenceEquals")
|
||||
and
|
||||
result.getNumberOfParameters() = 2
|
||||
and
|
||||
result.getParameter(0).getType() instanceof ObjectType
|
||||
and
|
||||
result.getParameter(1).getType() instanceof ObjectType
|
||||
and
|
||||
result.isStatic() and
|
||||
result.getDeclaringType() = this and
|
||||
result.hasName("ReferenceEquals") and
|
||||
result.getNumberOfParameters() = 2 and
|
||||
result.getParameter(0).getType() instanceof ObjectType and
|
||||
result.getParameter(1).getType() instanceof ObjectType and
|
||||
result.getReturnType() instanceof BoolType
|
||||
}
|
||||
|
||||
/** Gets the `GetHashCode()` method. */
|
||||
Method getGetHashCodeMethod() {
|
||||
result.getDeclaringType() = this
|
||||
and
|
||||
result.hasName("GetHashCode")
|
||||
and
|
||||
result.hasNoParameters()
|
||||
and
|
||||
result.getDeclaringType() = this and
|
||||
result.hasName("GetHashCode") and
|
||||
result.hasNoParameters() and
|
||||
result.getReturnType() instanceof IntType
|
||||
}
|
||||
|
||||
/** Gets the `GetType()` method. */
|
||||
Method getGetTypeMethod() {
|
||||
result.getDeclaringType() = this
|
||||
and
|
||||
result.hasName("GetType")
|
||||
and
|
||||
result.hasNoParameters()
|
||||
and
|
||||
result.getDeclaringType() = this and
|
||||
result.hasName("GetType") and
|
||||
result.hasNoParameters() and
|
||||
result.getReturnType() instanceof SystemTypeClass
|
||||
}
|
||||
|
||||
/** Gets the `ToString()` method. */
|
||||
Method getToStringMethod() {
|
||||
result.getDeclaringType() = this
|
||||
and
|
||||
result.hasName("ToString")
|
||||
and
|
||||
result.getNumberOfParameters() = 0
|
||||
and
|
||||
result.getDeclaringType() = this and
|
||||
result.hasName("ToString") and
|
||||
result.getNumberOfParameters() = 0 and
|
||||
result.getReturnType() instanceof StringType
|
||||
}
|
||||
}
|
||||
|
||||
/** The `System.OutOfMemoryException` class. */
|
||||
class SystemOutOfMemoryExceptionClass extends SystemClass {
|
||||
SystemOutOfMemoryExceptionClass() {
|
||||
this.hasName("OutOfMemoryException")
|
||||
}
|
||||
SystemOutOfMemoryExceptionClass() { this.hasName("OutOfMemoryException") }
|
||||
}
|
||||
|
||||
/** The `System.OverflowException` class. */
|
||||
class SystemOverflowExceptionClass extends SystemClass {
|
||||
SystemOverflowExceptionClass() {
|
||||
this.hasName("OverflowException")
|
||||
}
|
||||
SystemOverflowExceptionClass() { this.hasName("OverflowException") }
|
||||
}
|
||||
|
||||
/** The `System.Predicate<T>` delegate type. */
|
||||
class SystemPredicateDelegateType extends SystemUnboundGenericDelegateType {
|
||||
SystemPredicateDelegateType() {
|
||||
this.hasName("Predicate<>")
|
||||
and
|
||||
this.hasName("Predicate<>") and
|
||||
this.getNumberOfTypeParameters() = 1
|
||||
}
|
||||
}
|
||||
@@ -411,157 +296,113 @@ class SystemStringClass extends StringType {
|
||||
|
||||
/** Gets the `Replace(string/char, string/char)` method. */
|
||||
Method getReplaceMethod() {
|
||||
result.getDeclaringType() = this
|
||||
and
|
||||
result.hasName("Replace")
|
||||
and
|
||||
result.getNumberOfParameters() = 2
|
||||
and
|
||||
result.getDeclaringType() = this and
|
||||
result.hasName("Replace") and
|
||||
result.getNumberOfParameters() = 2 and
|
||||
result.getReturnType() instanceof StringType
|
||||
}
|
||||
|
||||
/** Gets a `Format(...)` method. */
|
||||
Method getFormatMethod() {
|
||||
result.getDeclaringType() = this
|
||||
and
|
||||
result.hasName("Format")
|
||||
and
|
||||
result.getNumberOfParameters() in [2 .. 5]
|
||||
and
|
||||
result.getDeclaringType() = this and
|
||||
result.hasName("Format") and
|
||||
result.getNumberOfParameters() in [2 .. 5] and
|
||||
result.getReturnType() instanceof StringType
|
||||
}
|
||||
|
||||
/** Gets a `Split(...)` method. */
|
||||
Method getSplitMethod() {
|
||||
result.getDeclaringType() = this
|
||||
and
|
||||
result.hasName("Split")
|
||||
and
|
||||
result.getNumberOfParameters() in [1 .. 3]
|
||||
and
|
||||
result.getDeclaringType() = this and
|
||||
result.hasName("Split") and
|
||||
result.getNumberOfParameters() in [1 .. 3] and
|
||||
result.getReturnType().(ArrayType).getElementType() instanceof StringType
|
||||
}
|
||||
|
||||
/** Gets a `Substring(...)` method. */
|
||||
Method getSubstringMethod() {
|
||||
result.getDeclaringType() = this
|
||||
and
|
||||
result.hasName("Substring")
|
||||
and
|
||||
result.getNumberOfParameters() in [1 .. 2]
|
||||
and
|
||||
result.getDeclaringType() = this and
|
||||
result.hasName("Substring") and
|
||||
result.getNumberOfParameters() in [1 .. 2] and
|
||||
result.getReturnType() instanceof StringType
|
||||
}
|
||||
|
||||
/** Gets a `Concat(...)` method. */
|
||||
Method getConcatMethod() {
|
||||
result.getDeclaringType() = this
|
||||
and
|
||||
result.hasName("Concat")
|
||||
and
|
||||
result.getDeclaringType() = this and
|
||||
result.hasName("Concat") and
|
||||
result.getReturnType() instanceof StringType
|
||||
}
|
||||
|
||||
/** Gets the `Copy()` method. */
|
||||
Method getCopyMethod() {
|
||||
result.getDeclaringType() = this
|
||||
and
|
||||
result.hasName("Copy")
|
||||
and
|
||||
result.getNumberOfParameters() = 1
|
||||
and
|
||||
result.getParameter(0).getType() instanceof StringType
|
||||
and
|
||||
result.getDeclaringType() = this and
|
||||
result.hasName("Copy") and
|
||||
result.getNumberOfParameters() = 1 and
|
||||
result.getParameter(0).getType() instanceof StringType and
|
||||
result.getReturnType() instanceof StringType
|
||||
}
|
||||
|
||||
/** Gets a `Join(string, ...)` method. */
|
||||
Method getJoinMethod() {
|
||||
result.getDeclaringType() = this
|
||||
and
|
||||
result.hasName("Join")
|
||||
and
|
||||
result.getNumberOfParameters() > 1
|
||||
and
|
||||
result.getParameter(0).getType() instanceof StringType
|
||||
and
|
||||
result.getDeclaringType() = this and
|
||||
result.hasName("Join") and
|
||||
result.getNumberOfParameters() > 1 and
|
||||
result.getParameter(0).getType() instanceof StringType and
|
||||
result.getReturnType() instanceof StringType
|
||||
}
|
||||
|
||||
/** Gets the `Clone()` method. */
|
||||
Method getCloneMethod() {
|
||||
result.getDeclaringType() = this
|
||||
and
|
||||
result.hasName("Clone")
|
||||
and
|
||||
result.getNumberOfParameters() = 0
|
||||
and
|
||||
result.getDeclaringType() = this and
|
||||
result.hasName("Clone") and
|
||||
result.getNumberOfParameters() = 0 and
|
||||
result.getReturnType() instanceof ObjectType
|
||||
}
|
||||
|
||||
/** Gets the `Insert(int, string)` method. */
|
||||
Method getInsertMethod() {
|
||||
result.getDeclaringType() = this
|
||||
and
|
||||
result.hasName("Insert")
|
||||
and
|
||||
result.getNumberOfParameters() = 2
|
||||
and
|
||||
result.getParameter(0).getType() instanceof IntType
|
||||
and
|
||||
result.getParameter(1).getType() instanceof StringType
|
||||
and
|
||||
result.getDeclaringType() = this and
|
||||
result.hasName("Insert") and
|
||||
result.getNumberOfParameters() = 2 and
|
||||
result.getParameter(0).getType() instanceof IntType and
|
||||
result.getParameter(1).getType() instanceof StringType and
|
||||
result.getReturnType() instanceof StringType
|
||||
}
|
||||
|
||||
/** Gets the `Normalize(...)` method. */
|
||||
Method getNormalizeMethod() {
|
||||
result.getDeclaringType() = this
|
||||
and
|
||||
result.hasName("Normalize")
|
||||
and
|
||||
result.getNumberOfParameters() in [0 .. 1]
|
||||
and
|
||||
result.getDeclaringType() = this and
|
||||
result.hasName("Normalize") and
|
||||
result.getNumberOfParameters() in [0 .. 1] and
|
||||
result.getReturnType() instanceof StringType
|
||||
}
|
||||
|
||||
/** Gets a `Remove(...)` method. */
|
||||
Method getRemoveMethod() {
|
||||
result.getDeclaringType() = this
|
||||
and
|
||||
result.hasName("Remove")
|
||||
and
|
||||
result.getNumberOfParameters() in [1 .. 2]
|
||||
and
|
||||
result.getDeclaringType() = this and
|
||||
result.hasName("Remove") and
|
||||
result.getNumberOfParameters() in [1 .. 2] and
|
||||
result.getReturnType() instanceof StringType
|
||||
}
|
||||
|
||||
/** Gets the `IsNullOrEmpty(string)` method. */
|
||||
Method getIsNullOrEmptyMethod() {
|
||||
result.getDeclaringType() = this
|
||||
and
|
||||
result.hasName("IsNullOrEmpty")
|
||||
and
|
||||
result.isStatic()
|
||||
and
|
||||
result.getNumberOfParameters() = 1
|
||||
and
|
||||
result.getDeclaringType() = this and
|
||||
result.hasName("IsNullOrEmpty") and
|
||||
result.isStatic() and
|
||||
result.getNumberOfParameters() = 1 and
|
||||
result.getReturnType() instanceof BoolType
|
||||
}
|
||||
}
|
||||
|
||||
/** A `ToString()` method. */
|
||||
class ToStringMethod extends Method {
|
||||
ToStringMethod() {
|
||||
this = any(SystemObjectClass c).getToStringMethod().getAnOverrider*()
|
||||
}
|
||||
ToStringMethod() { this = any(SystemObjectClass c).getToStringMethod().getAnOverrider*() }
|
||||
}
|
||||
|
||||
/** The `System.Type` class */
|
||||
class SystemTypeClass extends SystemClass {
|
||||
SystemTypeClass() {
|
||||
this.hasName("Type")
|
||||
}
|
||||
SystemTypeClass() { this.hasName("Type") }
|
||||
|
||||
/** Gets the `FullName` property. */
|
||||
Property getFullNameProperty() {
|
||||
@@ -572,95 +413,71 @@ class SystemTypeClass extends SystemClass {
|
||||
|
||||
/** Gets the `InvokeMember(string, ...)` method. */
|
||||
Method getInvokeMemberMethod() {
|
||||
result.getDeclaringType() = this
|
||||
and
|
||||
result.hasName("InvokeMember")
|
||||
and
|
||||
result.getParameter(0).getType() instanceof StringType
|
||||
and
|
||||
result.getParameter(3).getType() instanceof ObjectType
|
||||
and
|
||||
result.getParameter(4).getType().(ArrayType).getElementType() instanceof ObjectType
|
||||
and
|
||||
result.getDeclaringType() = this and
|
||||
result.hasName("InvokeMember") and
|
||||
result.getParameter(0).getType() instanceof StringType and
|
||||
result.getParameter(3).getType() instanceof ObjectType and
|
||||
result.getParameter(4).getType().(ArrayType).getElementType() instanceof ObjectType and
|
||||
result.getReturnType() instanceof ObjectType
|
||||
}
|
||||
|
||||
/** Gets the `GetMethod(string, ...)` method. */
|
||||
Method getGetMethodMethod() {
|
||||
result.getDeclaringType() = this
|
||||
and
|
||||
result.hasName("GetMethod")
|
||||
and
|
||||
result.getParameter(0).getType() instanceof StringType
|
||||
and
|
||||
result.getDeclaringType() = this and
|
||||
result.hasName("GetMethod") and
|
||||
result.getParameter(0).getType() instanceof StringType and
|
||||
result.getReturnType() instanceof SystemReflectionMethodInfoClass
|
||||
}
|
||||
}
|
||||
|
||||
/** The `System.Uri` class. */
|
||||
class SystemUriClass extends SystemClass {
|
||||
SystemUriClass() {
|
||||
this.hasName("Uri")
|
||||
}
|
||||
SystemUriClass() { this.hasName("Uri") }
|
||||
|
||||
/** Gets the `PathAndQuery` property. */
|
||||
Property getPathAndQueryProperty() {
|
||||
result.getDeclaringType() = this
|
||||
and
|
||||
result.hasName("PathAndQuery")
|
||||
and
|
||||
result.getDeclaringType() = this and
|
||||
result.hasName("PathAndQuery") and
|
||||
result.getType() instanceof StringType
|
||||
}
|
||||
|
||||
/** Gets the `Query` property. */
|
||||
Property getQueryProperty() {
|
||||
result.getDeclaringType() = this
|
||||
and
|
||||
result.hasName("Query")
|
||||
and
|
||||
result.getDeclaringType() = this and
|
||||
result.hasName("Query") and
|
||||
result.getType() instanceof StringType
|
||||
}
|
||||
|
||||
/** Gets the `OriginalString` property. */
|
||||
Property getOriginalStringProperty() {
|
||||
result.getDeclaringType() = this
|
||||
and
|
||||
result.hasName("OriginalString")
|
||||
and
|
||||
result.getDeclaringType() = this and
|
||||
result.hasName("OriginalString") and
|
||||
result.getType() instanceof StringType
|
||||
}
|
||||
}
|
||||
|
||||
/** The `System.ValueType` class. */
|
||||
class SystemValueTypeClass extends SystemClass {
|
||||
SystemValueTypeClass() {
|
||||
this.hasName("ValueType")
|
||||
}
|
||||
SystemValueTypeClass() { this.hasName("ValueType") }
|
||||
}
|
||||
|
||||
/** The `System.IntPtr` type. */
|
||||
class SystemIntPtrType extends ValueType {
|
||||
SystemIntPtrType() {
|
||||
this = any(SystemNamespace n).getATypeDeclaration()
|
||||
and
|
||||
this = any(SystemNamespace n).getATypeDeclaration() and
|
||||
this.hasName("IntPtr")
|
||||
}
|
||||
}
|
||||
|
||||
/** The `System.IDisposable` interface. */
|
||||
class SystemIDisposableInterface extends SystemInterface {
|
||||
SystemIDisposableInterface() {
|
||||
this.hasName("IDisposable")
|
||||
}
|
||||
SystemIDisposableInterface() { this.hasName("IDisposable") }
|
||||
|
||||
/** Gets the `Dispose()` method. */
|
||||
Method getDisposeMethod() {
|
||||
result.getDeclaringType() = this
|
||||
and
|
||||
result.hasName("Dispose")
|
||||
and
|
||||
result.getNumberOfParameters() = 0
|
||||
and
|
||||
result.getDeclaringType() = this and
|
||||
result.hasName("Dispose") and
|
||||
result.getNumberOfParameters() = 0 and
|
||||
result.getReturnType() instanceof VoidType
|
||||
}
|
||||
}
|
||||
@@ -668,8 +485,7 @@ class SystemIDisposableInterface extends SystemInterface {
|
||||
/** A method that overrides `int object.GetHashCode()`. */
|
||||
class GetHashCodeMethod extends Method {
|
||||
GetHashCodeMethod() {
|
||||
exists(Method m |
|
||||
m = any(SystemObjectClass c).getGetHashCodeMethod() |
|
||||
exists(Method m | m = any(SystemObjectClass c).getGetHashCodeMethod() |
|
||||
this = m.getAnOverrider*()
|
||||
)
|
||||
}
|
||||
@@ -678,10 +494,7 @@ class GetHashCodeMethod extends Method {
|
||||
/** A method that overrides `bool object.Equals(object)`. */
|
||||
class EqualsMethod extends Method {
|
||||
EqualsMethod() {
|
||||
exists(Method m |
|
||||
m = any(SystemObjectClass c).getEqualsMethod() |
|
||||
this = m.getAnOverrider*()
|
||||
)
|
||||
exists(Method m | m = any(SystemObjectClass c).getEqualsMethod() | this = m.getAnOverrider*())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -690,7 +503,8 @@ class IEquatableEqualsMethod extends Method {
|
||||
IEquatableEqualsMethod() {
|
||||
exists(Method m |
|
||||
m = any(SystemIEquatableTInterface i).getAConstructedGeneric().getAMethod() and
|
||||
m.getSourceDeclaration() = any(SystemIEquatableTInterface i).getEqualsMethod() |
|
||||
m.getSourceDeclaration() = any(SystemIEquatableTInterface i).getEqualsMethod()
|
||||
|
|
||||
this = m or getAnUltimateImplementee() = m
|
||||
)
|
||||
}
|
||||
@@ -703,9 +517,7 @@ class IEquatableEqualsMethod extends Method {
|
||||
* or an implementation of `IEquatable<T>.Equals(T)` which is called
|
||||
* from the `object.Equals(object)` method inherited by `t`.
|
||||
*/
|
||||
predicate implementsEquals(ValueOrRefType t) {
|
||||
getInvokedEqualsMethod(t).getDeclaringType() = t
|
||||
}
|
||||
predicate implementsEquals(ValueOrRefType t) { getInvokedEqualsMethod(t).getDeclaringType() = t }
|
||||
|
||||
/**
|
||||
* Gets the equals method that will be invoked on a value `x`
|
||||
@@ -725,9 +537,7 @@ Method getInvokedEqualsMethod(ValueOrRefType t) {
|
||||
)
|
||||
}
|
||||
|
||||
private EqualsMethod getInheritedEqualsMethod(ValueOrRefType t) {
|
||||
t.hasMethod(result)
|
||||
}
|
||||
private EqualsMethod getInheritedEqualsMethod(ValueOrRefType t) { t.hasMethod(result) }
|
||||
|
||||
/**
|
||||
* Equals method `eq` is inherited by `t`, `t` overrides `IEquatable<T>.Equals(T)`
|
||||
@@ -754,7 +564,8 @@ private IEquatableEqualsMethod getInvokedIEquatableEqualsMethod(ValueOrRefType t
|
||||
eq = getInheritedEqualsMethod(t.getBaseClass()) and
|
||||
exists(IEquatableEqualsMethod ieem |
|
||||
result = ieem.getAnOverrider*() and
|
||||
eq.getDeclaringType() = ieem.getDeclaringType() |
|
||||
eq.getDeclaringType() = ieem.getDeclaringType()
|
||||
|
|
||||
not ieem.fromSource()
|
||||
or
|
||||
callsEqualsMethod(eq, ieem)
|
||||
@@ -772,8 +583,7 @@ private predicate callsEqualsMethod(EqualsMethod eq, IEquatableEqualsMethod ieem
|
||||
/** A method that implements `void IDisposable.Dispose()`. */
|
||||
class DisposeMethod extends Method {
|
||||
DisposeMethod() {
|
||||
exists(Method m |
|
||||
m = any(SystemIDisposableInterface i).getDisposeMethod() |
|
||||
exists(Method m | m = any(SystemIDisposableInterface i).getDisposeMethod() |
|
||||
this = m or this.getAnUltimateImplementee() = m
|
||||
)
|
||||
}
|
||||
@@ -796,9 +606,7 @@ library class DisposeBoolMethod extends Method {
|
||||
* or an implementation of a method `Dispose(bool)` which is called
|
||||
* from the `IDisposable.Dispose()` method inherited by `t`.
|
||||
*/
|
||||
predicate implementsDispose(ValueOrRefType t) {
|
||||
getInvokedDisposeMethod(t).getDeclaringType() = t
|
||||
}
|
||||
predicate implementsDispose(ValueOrRefType t) { getInvokedDisposeMethod(t).getDeclaringType() = t }
|
||||
|
||||
/**
|
||||
* Gets the dispose method that will be invoked on a value `x`
|
||||
@@ -818,9 +626,7 @@ Method getInvokedDisposeMethod(ValueOrRefType t) {
|
||||
)
|
||||
}
|
||||
|
||||
private DisposeMethod getInheritedDisposeMethod(ValueOrRefType t) {
|
||||
t.hasMethod(result)
|
||||
}
|
||||
private DisposeMethod getInheritedDisposeMethod(ValueOrRefType t) { t.hasMethod(result) }
|
||||
|
||||
/**
|
||||
* Dispose method `disp` is inherited by `t`, `t` overrides a `void Dispose(bool)`
|
||||
@@ -847,7 +653,8 @@ private DisposeBoolMethod getInvokedDiposeBoolMethod(ValueOrRefType t, DisposeMe
|
||||
disp = getInheritedDisposeMethod(t.getBaseClass()) and
|
||||
exists(DisposeBoolMethod dbm |
|
||||
result = dbm.getAnOverrider*() and
|
||||
disp.getDeclaringType() = dbm.getDeclaringType() |
|
||||
disp.getDeclaringType() = dbm.getDeclaringType()
|
||||
|
|
||||
not disp.fromSource()
|
||||
or
|
||||
exists(MethodCall callToDerivedDispose |
|
||||
@@ -859,21 +666,13 @@ private DisposeBoolMethod getInvokedDiposeBoolMethod(ValueOrRefType t, DisposeMe
|
||||
|
||||
/** A struct in the `System` namespace. */
|
||||
class SystemStruct extends Struct {
|
||||
SystemStruct() {
|
||||
this.getNamespace() instanceof SystemNamespace
|
||||
}
|
||||
SystemStruct() { this.getNamespace() instanceof SystemNamespace }
|
||||
}
|
||||
|
||||
/** `System.Guid` struct. */
|
||||
class SystemGuid extends SystemStruct {
|
||||
SystemGuid() {
|
||||
this.hasName("Guid")
|
||||
}
|
||||
}
|
||||
class SystemGuid extends SystemStruct { SystemGuid() { this.hasName("Guid") } }
|
||||
|
||||
/** The `System.NotImplementedException` class. */
|
||||
class SystemNotImplementedExceptionClass extends SystemClass {
|
||||
SystemNotImplementedExceptionClass() {
|
||||
this.hasName("NotImplementedException")
|
||||
}
|
||||
SystemNotImplementedExceptionClass() { this.hasName("NotImplementedException") }
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user