QL code and tests for C#/C++/JavaScript.

This commit is contained in:
Pavel Avgustinov
2018-08-02 17:53:23 +01:00
commit b55526aa58
10684 changed files with 581163 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
| tst.js:5:2:5:5 | else | This else branch belongs to $@, but its indentation suggests it belongs to $@. | tst.js:3:3:3:4 | if | this if statement | tst.js:2:2:2:3 | if | this other if statement |
| tst.js:21:4:21:7 | else | This else branch belongs to $@, but its indentation suggests it belongs to $@. | tst.js:19:3:19:4 | if | this if statement | tst.js:18:2:18:3 | if | this other if statement |
| tst.js:40:2:40:5 | else | This else branch belongs to $@, but its indentation suggests it belongs to $@. | tst.js:38:3:38:4 | if | this if statement | tst.js:37:7:37:8 | if | this other if statement |

View File

@@ -0,0 +1 @@
Statements/DanglingElse.ql

View File

@@ -0,0 +1,79 @@
function bad1() {
if (cond1())
if (cond2())
return 23;
else
return 42;
}
function good1() {
if (cond1())
if (cond2())
return 23;
else
return 42;
}
function bad2() {
if (cond1()) {
if (cond2()) {
return 23;
} else {
return 42;
}}
}
function good2() {
if (cond1())
if (cond2())
return 23;
else
return 42;
}
function bad3() {
if (cond1())
return 23;
else if (cond2())
if (cond2())
return 42;
else
return 42;
}
function good3() {
if (cond1())
if (cond2())
return 23;
else
return 42;
}
function good4() {
if (cond1()) {
if (cond2())
return 23;
} else
return 42;
}
function good5() {
if (cond1())
(function inner() {
if (cond2())
return 23;
else
return 42;
}());
}
function good6() {
if (true) {
} else if (true) {
if (true) {
} else if (true) {
} else {
}
}
}

View File

@@ -0,0 +1,2 @@
| tst.js:2:1:2:9 | while(c ... reak;\\n} | This loop executes at most once. |
| tst.js:13:3:13:29 | for (; ... et;\\n } | This loop executes at most once. |

View File

@@ -0,0 +1 @@
Statements/EphemeralLoop.ql

View File

@@ -0,0 +1,3 @@
// EMSCRIPTEN_START_ASM
do { ; } while(0);
// EMSCRIPTEN_END_ASM

View File

@@ -0,0 +1,32 @@
// NOT OK
while(c){
switch(c){
case "/":
break;
default:
}
break;
}
// NOT OK
function f() {
for (; k < numprecincts;) {
var packet = createPacket(resolution, k, l);
k++;
return packet;
}
}
// OK
var oHasProps = false;
for (var p in o) {
oHasProps = true;
break;
}
// OK
while(c){
if (c === '"')
break;
console.log(c);
}

View File

@@ -0,0 +1,3 @@
| tst.js:3:2:3:11 | if (foo ... n true; | $@ may implicitly return 'undefined' here, while $@ an explicit value is returned. | tst.js:2:1:5:1 | functio ... true;\\n} | Function f | tst.js:4:3:4:14 | return true; | elsewhere |
| tst.js:101:2:101:11 | if (foo ... n true; | $@ may implicitly return 'undefined' here, while $@ an explicit value is returned. | tst.js:100:9:103:1 | functio ... true;\\n} | Function u | tst.js:102:3:102:14 | return true; | elsewhere |
| tst.js:107:2:107:7 | if (b)\\n\\t\\treturn 1; | $@ may implicitly return 'undefined' here, while $@ an explicit value is returned. | tst.js:106:1:109:1 | functio ... rn 1;\\n} | Function v | tst.js:108:3:108:11 | return 1; | elsewhere |

View File

@@ -0,0 +1 @@
Statements/ImplicitReturn.ql

View File

@@ -0,0 +1,109 @@
// NOT OK
function f() {
if (foo())
return true;
}
// OK
function g() {
if (foo())
return true;
return false;
}
// OK
function h() {
open();
try {
return bar();
} finally {
close();
}
}
// OK
function k(x) {
switch (x) {
case 1:
return 23;
case 2:
return;
}
}
// OK
function l() {
return 23;
alert("Hi");
}
// OK
function m() {
if (foo())
return true;
throw new Error("not foo!");
}
// OK
function n() {
if (foo())
return true;
throwError("not foo!");
}
function throwError(msg) {
throw new Error(msg);
}
function foo() {
return true;
}
//
// OK: dual-use constructor
function Point(x, y) {
if (!(this instanceof Point))
return new Point(x, y);
this.x = x;
this.y = y;
}
// OK: infinite loops
function q(n) {
for (var i=0;;++i)
if (i>2*n)
return i;
}
function r(n) {
while (true) {
if (n-->0)
return 23;
}
}
function s(n) {
do {
if (n++>56)
return n-2;
} while(1);
}
function t(n) {
do {
if (n++>56)
return n-2;
} while("true");
}
// NOT OK
var u = function() {
if (foo())
return true;
};
// NOT OK
function v(b) {
if (b)
return 1;
}

View File

@@ -0,0 +1,3 @@
| tst.js:6:1:7:1 | for (j ... -j) {\\n} | This loop counts downward, but its variable is bounded upward. |
| tst.js:10:1:11:1 | for (va ... ++) {\\n} | This loop counts upward, but its variable is bounded downward. |
| tst.js:18:1:19:13 | for (i= ... i] = 0; | This loop counts downward, but its variable is bounded upward. |

View File

@@ -0,0 +1 @@
Statements/InconsistentLoopOrientation.ql

View File

@@ -0,0 +1,19 @@
// OK
for (j = i - 1; j >= 0; --j) {
}
// NOT OK
for (j = i + 1; j < strLength; --j) {
}
// NOT OK
for (var i = 0, l = c.length; i > l; i ++) {
}
// OK
for (i=lower-1; i>=0; --i)
a[i] = 0;
// NOT OK
for (i=upper+1; i<a.length; --i)
a[i] = 0;

View File

@@ -0,0 +1 @@
| tst.js:3:3:3:9 | return; | This return statement implicitly returns 'undefined', whereas $@ returns an explicit value. | tst.js:4:2:4:11 | return 42; | another return statement in the same function |

View File

@@ -0,0 +1 @@
Statements/InconsistentReturn.ql

View File

@@ -0,0 +1,5 @@
function f() {
if (someCond())
return;
return 42;
}

View File

@@ -0,0 +1 @@
| nonCaseLabelInSwitch.js:4:3:4:7 | case2 | Non-case labels in switch statements are confusing. |

View File

@@ -0,0 +1 @@
Statements/LabelInCase.ql

View File

@@ -0,0 +1,19 @@
switch (a) {
case 0:
case 1:
case2:
f();
break;
default:
g();
}
switch (a) {
case 0:
case 1:
case2:
f();
break;
default:
g();
}

View File

@@ -0,0 +1,4 @@
| tst.html:7:9:7:12 | x--; | The indentation of this statement suggests that it is controlled by $@, while in fact it is not. | tst.html:5:7:5:16 | if (x > ... x--; | this statement |
| tst.js:4:3:4:6 | g(); | The indentation of this statement suggests that it is controlled by $@, while in fact it is not. | tst.js:2:2:2:12 | if (cond())\\n\\t\\tf(); | this statement |
| tst.js:25:3:25:6 | h(); | The indentation of this statement suggests that it is controlled by $@, while in fact it is not. | tst.js:21:2:21:12 | if (con ... \\n\\t\\tg(); | this statement |
| tst.js:37:3:37:6 | g(); | The indentation of this statement suggests that it is controlled by $@, while in fact it is not. | tst.js:35:2:35:15 | while ( ... \\n\\t\\tf(); | this statement |

View File

@@ -0,0 +1 @@
Statements/MisleadingIndentationAfterControlStmt.ql

View File

@@ -0,0 +1,4 @@
if (foo)
{
}
;

View File

@@ -0,0 +1,12 @@
<html>
<head>
<title>Title</title>
<script>
if (x > 0)
x--;
x--;
</script>
</head>
<body>
</body>
</html>

View File

@@ -0,0 +1,122 @@
function bad1() {
if (cond())
f();
g();
}
function good1() {
if (cond()) {
f();
}
g();
}
function good2() {
if (cond())
f();
g();
}
function bad2() {
if (cond())
f();
else
g();
h();
}
function good3() {
if (cond())
f();
g();
}
function wbad1() {
while (cond())
f();
g();
}
function wgood1() {
while (cond()) {
f();
}
g();
}
function wgood2() {
while (cond())
f();
g();
}
function wgood3() {
while (cond())
f();
g();
}
function dgood() {
do
f();
while (false);
g();
}
function tgood() {
try {
if (cond())
throw new Error();
} finally {}
f();
}
function good4() {
if (cond())
f();
g();
}
function good5() {
try {
} catch (e) {
}
}
function good6() {
try {
} catch (e) {
};
}
function good7() {
if (e) {
try {
} catch (e) {
}
}
}
function good8() {
if (e) {
try {
} catch (e) {
};
}
}
function good9() {
if(e){
try{
} catch(e){
};
}
}
function good10() {
if(e){
try{
}catch(e){
};
}
}

View File

@@ -0,0 +1 @@
| tst.js:3:16:3:18 | i>5 | This for statement uses the same loop variable as an enclosing $@. | tst.js:1:1:9:1 | for (va ... , k);\\n} | for statement |

View File

@@ -0,0 +1 @@
Statements/NestedLoopsSameVariable.ql

View File

@@ -0,0 +1,9 @@
for (var i=0; i<10; ++i) {
// NOT OK
for (var j=i; i>5; --i)
f(i, j);
// OK
for (var k=0; k<i; ++k)
f(i, k);
}

View File

@@ -0,0 +1,2 @@
| tst.js:3:2:3:15 | return x = 23; | The assignment to x is useless, since it is a local variable and will go out of scope. |
| tst.js:9:2:9:15 | return x = 23; | The assignment to x is useless, since it is a local variable and will go out of scope. |

View File

@@ -0,0 +1 @@
Statements/ReturnAssignsLocal.ql

View File

@@ -0,0 +1,61 @@
// NOT OK
function f(x) {
return x = 23;
}
// NOT OK
function g() {
var x;
return x = 23;
}
// OK
function h() {
return x = 23;
}
// OK
function k() {
try {
return x = 23;
} catch(x) {}
}
// OK: the return statement assigns to a global, not the catch variable
function l() {
try {
throw new Error();
} catch(x) {}
return x = 23;
}
// OK
function m() {
var x = 23;
return function() {
return x = x + 19;
};
}
// OK
function n(x) {
global_getter = function() {
return x;
};
return x = 23;
}
// OK
function p() {
var x;
return {
g: function g() {
return function h() {
return x = 42;
}
},
h: function() {
return x;
}
};
}

View File

@@ -0,0 +1,2 @@
| tst.html:9:66:9:78 | return false; | Return statement outside function. |
| tst.js:2:1:2:10 | return 42; | Return statement outside function. |

View File

@@ -0,0 +1 @@
Statements/ReturnOutsideFunction.ql

View File

@@ -0,0 +1,5 @@
// not a syntax error, but still NOT OK
return 42;
// semmle-extractor-options: --platform
// semmle-extractor-options: node

View File

@@ -0,0 +1,14 @@
<html>
<head>
<title>Test</title>
<script>
function validateForm() { return true; }
</script>
</head>
<body>
<form name="f" onSubmit="validateForm(this);" action="javascript:return false;" method="post" enctype="multipart/form-data">
</form>
<form name="g" onsubmit="return validateForm(this);" method="post">
</form>
</body>
</html>

View File

@@ -0,0 +1,2 @@
// NOT OK
return 42;

View File

@@ -0,0 +1 @@
| tst.js:4:7:4:11 | let x | For loop variable x is not used in the loop body. |

View File

@@ -0,0 +1 @@
Statements/SuspiciousUnusedLoopIterationVariable.ql

View File

@@ -0,0 +1,129 @@
// BAD
function countOccurrences(xs, p) {
var count = 0;
for (let x of xs)
if (p())
++count;
return count;
}
// OK
function countOccurrences(xs, p) {
var count = 0;
for (let x of xs)
if (p(x))
++count;
return count;
}
// OK
function countOccurrences(xs, p) {
var count = 0;
for (let unused of xs)
if (p())
++count;
return count;
}
// OK
function isEmpty(o) {
for (var x in o)
return false;
return true;
}
// OK
function getNumElt(o) {
var count = 0;
for (var x of o)
++count;
return count;
}
// OK
function getNumElt(o) {
var count = 0;
for (var x of o) {
++count;
}
return count;
}
// OK
function getNumElt(o) {
var count = 0;
for (var x of o)
count = count + 1;
return count;
}
// OK
function getNumElt(o) {
var count = 0;
for (var x of o)
count = 1 + count;
return count;
}
// OK
function getNumElt(o) {
var count = 0;
for (var x of o) {
console.log("Counting...");
count += 1;
}
return count;
}
// OK
function f(o) {
for (var p in o)
(function() {
console.log(p);
})();
}
// OK
function lastProp(o) {
var key;
for (key in obj);
return key;
}
// OK
function g() {
for (var unused in {"toString": null})
hasDontEnumBug = false;
}
// OK
function is_empty(obj) {
var empty = true;
for (var key in obj) {
empty = false;
break;
}
return empty;
}
// OK
function f(objs) {
var non_empties = 0;
for (var obj in objs) {
for (var key in obj) {
non_empties += 1;
break;
}
}
return non_empties;
}
// OK: dead loops are not flagged
function countOccurrencesDead(xs, p) {
return;
var count = 0;
for (let x of xs)
if (p())
++count;
return count;
}

View File

@@ -0,0 +1,3 @@
| tst.js:5:2:5:12 | var a = 42; | This statement is unreachable. |
| tst.js:38:1:38:4 | f(); | This statement is unreachable. |
| tst.js:63:2:63:3 | y; | This statement is unreachable. |

View File

@@ -0,0 +1 @@
Statements/UnreachableStatement.ql

View File

@@ -0,0 +1,2 @@
class A {
}

View File

@@ -0,0 +1,75 @@
;
function f() {
return 23;
var a = 42;
}
function g(x) {
switch(x) {
case 0:
return 23;
break;
default:
return 42;
}
}
function h(i) {
while(true) {
if (!f(i++)) {
break;;
}
}
}
function k() {
try {
h();
} catch(e) {
;
}
for (var p in {});
for (var i=0; i<10; ++i);
}
throw new Error();
f();
function l(x) {
switch(x) {
default:
return 42;
case 0:
return 23;
}
}
function m(x) {
switch(x) {
case 0:
return 23;
default:
return 42;
case 1:
return 56;
}
}
if (true)
x;
else
y;
function f(){
if (x) {
return;; // trailing ';' is unreachable, but alert is squelched
}
if (x) {
return y;
} else {
return z;
}; // ';' is unreachable, but alert is squelched
}

View File

@@ -0,0 +1,5 @@
export function foo(x: number): number {
let y : A = x;
return y;
type A = number; // OK.
}

View File

@@ -0,0 +1,12 @@
| UselessConditional.js:5:7:5:12 | !lines | This expression always evaluates to false. |
| UselessConditional.js:12:34:12:79 | (v = ne ... k] = v) | This logical 'and' expression can be replaced with a comma expression. |
| UselessConditional.js:17:9:17:9 | a | Variable 'a' always evaluates to false here. |
| UselessConditional.js:18:9:18:9 | b | Variable 'b' always evaluates to false here. |
| UselessConditional.js:21:9:21:9 | a | Variable 'a' always evaluates to false here. |
| UselessConditional.js:22:9:22:9 | b | Variable 'b' always evaluates to false here. |
| UselessConditional.js:26:6:26:6 | x | Variable 'x' always evaluates to true here. |
| UselessConditional.js:27:7:27:13 | new X() | This expression always evaluates to true. |
| UselessConditional.js:28:7:28:7 | x | Variable 'x' always evaluates to true here. |
| UselessConditional.js:29:8:29:8 | x | Variable 'x' always evaluates to true here. |
| UselessConditional.js:30:8:30:14 | new X() | This expression always evaluates to true. |
| UselessConditional.js:33:7:33:7 | x | Variable 'x' always evaluates to false here. |

View File

@@ -0,0 +1,47 @@
function getLastLine(input) {
var lines = [], nextLine;
while ((nextLine = readNextLine(input)))
lines.push(nextLine);
if (!lines)
throw new Error("No lines!");
return lines[lines.length-1];
}
function lookup(cache, k) {
var v;
return k in cache ? cache[k] : (v = new Entry(recompute())) && (cache[k] = v);
}
function test(a, b) {
if (!a && !b) {
if (a);
if (b);
}
if (!(a || b)) {
if (a);
if (b);
}
var x = new X();
if(x){}
if (new X()){}
if((x)){}
if(((x))){}
if ((new X())){}
x = 0n;
if (x) // NOT OK
;
}
async function awaitFlow(){
var v;
if (y)
v = await f()
if (v) { // OK
}
}
// semmle-extractor-options: --experimental

View File

@@ -0,0 +1 @@
Statements/UselessConditional.ql

View File

@@ -0,0 +1,8 @@
function getLastLine(input) {
var lines = [], nextLine;
while ((nextLine = readNextLine(input)))
lines.push(nextLine);
if (!lines.length)
throw new Error("No lines!");
return lines[lines.length-1];
}