Merge pull request #323 from esben-semmle/js/always-return-type-inference

JS: additional return type inference
This commit is contained in:
Max Schaefer
2018-11-07 08:25:28 +00:00
committed by GitHub
33 changed files with 442 additions and 24 deletions

View File

@@ -756,7 +756,7 @@ module DataFlow {
/**
* A data flow node representing an explicit (that is, non-reflective) function call.
*/
private class ExplicitCallNode extends CallNodeDef, ExplicitInvokeNode {
class ExplicitCallNode extends CallNodeDef, ExplicitInvokeNode {
override CallExpr astNode;
}

View File

@@ -91,17 +91,19 @@ class AnalyzedNode extends DataFlow::Node {
/** Gets a type inferred for this node. */
pragma[nomagic] InferredType getAType() {
result = getALocalValue().getType()
result = getAValue().getType()
}
/** Gets a primitive type to which the value of this node can be coerced. */
/**
* Gets a primitive type to which the value of this node can be coerced.
*/
PrimitiveType getAPrimitiveType() {
result = getALocalValue().toPrimitive().getType()
result = getAValue().toPrimitive().getType()
}
/** Gets a Boolean value that this node evaluates to. */
boolean getABooleanValue() {
result = getALocalValue().getBooleanValue()
result = getAValue().getBooleanValue()
}
/** Gets the unique Boolean value that this node evaluates to, if any. */
@@ -166,7 +168,7 @@ class AnalyzedNode extends DataFlow::Node {
/** Holds if the flow analysis can infer at least one abstract value for this node. */
predicate hasFlow() {
exists(getALocalValue())
exists(getAValue())
}
}

View File

@@ -242,18 +242,25 @@ private class AnalyzedBinaryExpr extends DataFlow::AnalyzedValueNode {
}
}
/**
* Gets a primitive type to which the local value of `e` can be coerced.
*/
private PrimitiveType getALocalPrimitiveType(Expr e) {
result = e.analyze().getALocalValue().toPrimitive().getType()
}
/**
* Holds if `e` may hold a string value.
*/
private predicate maybeString(Expr e) {
e.analyze().getAPrimitiveType() = TTString()
getALocalPrimitiveType(e) = TTString()
}
/**
* Holds if `e` may hold a non-string value.
*/
private predicate maybeNonString(Expr e) {
e.analyze().getAPrimitiveType() != TTString()
getALocalPrimitiveType(e) != TTString()
}
/**

View File

@@ -14,8 +14,7 @@ import semmle.javascript.dataflow.Configuration
*/
predicate shouldTrackProperties(AbstractValue obj) {
obj instanceof AbstractExportsObject or
obj instanceof AbstractModuleObject or
obj instanceof AbstractObjectLiteral
obj instanceof AbstractModuleObject
}
/**

View File

@@ -143,6 +143,23 @@ abstract class CallWithAnalyzedReturnFlow extends DataFlow::AnalyzedValueNode {
}
}
/**
* A call with inter-procedural type inference for the return value.
*
* Unlike `CallWithAnalyzedReturnFlow`, this only contributes to `getAValue()`, not `getALocalValue()`.
*/
abstract class CallWithNonLocalAnalyzedReturnFlow extends DataFlow::AnalyzedValueNode {
/**
* Gets a called function.
*/
abstract AnalyzedFunction getACallee();
override AbstractValue getAValue() {
result = getACallee().getAReturnValue()
}
}
/**
* Flow analysis for the return value of IIFEs.
*/
@@ -210,4 +227,30 @@ private class LocalFunctionCallWithAnalyzedReturnFlow extends CallWithAnalyzedRe
result = f.analyze()
}
}
}
pragma[noinline]
private predicate hasExplicitDefiniteCallee(DataFlow::Impl::ExplicitCallNode call, DataFlow::AnalyzedNode callee) {
callee = call.getCalleeNode() and
not callee.getALocalValue().isIndefinite(_)
}
/**
* Enables inter-procedural type inference for the return value of a call to a type-inferred callee.
*/
private class TypeInferredCalleeWithAnalyzedReturnFlow extends CallWithNonLocalAnalyzedReturnFlow {
DataFlow::FunctionNode fun;
TypeInferredCalleeWithAnalyzedReturnFlow() {
exists (DataFlow::AnalyzedNode calleeNode |
hasExplicitDefiniteCallee(this, calleeNode) and
calleeNode.getALocalValue().(AbstractFunction).getFunction().flow() = fun
)
}
override AnalyzedFunction getACallee() {
result = fun
}
}

View File

@@ -1,3 +1,4 @@
| advanced-callgraph.js:2:13:2:20 | source() | advanced-callgraph.js:6:22:6:22 | v |
| partialCalls.js:4:17:4:24 | source() | partialCalls.js:17:14:17:14 | x |
| partialCalls.js:4:17:4:24 | source() | partialCalls.js:20:14:20:14 | y |
| partialCalls.js:4:17:4:24 | source() | partialCalls.js:30:14:30:20 | x.value |

View File

@@ -0,0 +1,13 @@
(function(){
let x = source();
function nest1() {
function nest2(){
function nest3(v){
sink(v);
}
return nest3;
}
return nest2;
}
nest1()()(x);
});

View File

@@ -1,6 +1,23 @@
| tst.js:1:1:1:16 | (function(){})() | tst.js:1:2:1:13 | function(){} | file://:0:0:0:0 | undefined |
| tst.js:2:1:2:25 | (functi ... n; })() | tst.js:2:2:2:22 | functio ... turn; } | file://:0:0:0:0 | undefined |
| tst.js:3:1:3:30 | (functi ... e; })() | tst.js:3:2:3:27 | functio ... true; } | file://:0:0:0:0 | true |
| tst.js:4:1:4:51 | (functi ... e; })() | tst.js:4:2:4:48 | functio ... alse; } | file://:0:0:0:0 | false |
| tst.js:4:1:4:51 | (functi ... e; })() | tst.js:4:2:4:48 | functio ... alse; } | file://:0:0:0:0 | true |
| tst.js:5:1:5:27 | (functi ... x; })() | tst.js:5:2:5:24 | functio ... rn x; } | file://:0:0:0:0 | indefinite value (global) |
| tst.js:4:1:4:16 | (function(){})() | file://:0:0:0:0 | undefined |
| tst.js:5:1:5:25 | (functi ... n; })() | file://:0:0:0:0 | undefined |
| tst.js:6:1:6:30 | (functi ... e; })() | file://:0:0:0:0 | true |
| tst.js:7:1:7:57 | (functi ... e; })() | file://:0:0:0:0 | false |
| tst.js:7:1:7:57 | (functi ... e; })() | file://:0:0:0:0 | true |
| tst.js:8:1:8:33 | (functi ... n; })() | file://:0:0:0:0 | indefinite value (global) |
| tst.js:11:5:11:8 | f1() | file://:0:0:0:0 | undefined |
| tst.js:14:5:14:8 | f2() | file://:0:0:0:0 | undefined |
| tst.js:15:5:15:8 | f2() | file://:0:0:0:0 | undefined |
| tst.js:23:5:23:8 | f4() | tst.js:21:16:21:30 | function f5 |
| tst.js:23:5:23:10 | f4()() | file://:0:0:0:0 | undefined |
| tst.js:30:5:30:8 | f6() | tst.js:26:16:28:9 | function f7 |
| tst.js:30:5:30:10 | f6()() | tst.js:27:20:27:34 | function f8 |
| tst.js:36:5:36:9 | f10() | file://:0:0:0:0 | undefined |
| tst.js:39:5:39:9 | f11() | file://:0:0:0:0 | non-empty, non-numeric string |
| tst.js:39:5:39:9 | f11() | file://:0:0:0:0 | non-zero value |
| tst.js:45:5:45:9 | f13() | file://:0:0:0:0 | undefined |
| tst.js:48:5:48:9 | f14() | file://:0:0:0:0 | non-empty, non-numeric string |
| tst.js:48:5:48:9 | f14() | file://:0:0:0:0 | non-zero value |
| tst.js:61:5:61:9 | f15() | file://:0:0:0:0 | undefined |
| tst.js:64:5:64:9 | f16() | file://:0:0:0:0 | undefined |
| tst.js:84:17:84:22 | getF() | file://:0:0:0:0 | undefined |
| tst.js:89:17:89:22 | getG() | file://:0:0:0:0 | undefined |

View File

@@ -1,4 +1,6 @@
import javascript
from CallWithAnalyzedReturnFlow call
select call, call.getACallee(), call.getACallee().getAReturnValue()
from DataFlow::AnalyzedValueNode call
where call instanceof CallWithAnalyzedReturnFlow or
call instanceof CallWithNonLocalAnalyzedReturnFlow
select call, call.getAValue()

View File

@@ -0,0 +1,64 @@
| tst.js:1:18:1:24 | require | file://:0:0:0:0 | indefinite value (heap) |
| tst.js:2:16:2:22 | require | file://:0:0:0:0 | indefinite value (heap) |
| tst.js:4:1:4:14 | (function(){}) | tst.js:4:2:4:13 | anonymous function |
| tst.js:5:1:5:23 | (functi ... urn; }) | tst.js:5:2:5:22 | anonymous function |
| tst.js:6:1:6:28 | (functi ... rue; }) | tst.js:6:2:6:27 | anonymous function |
| tst.js:7:1:7:55 | (functi ... lse; }) | tst.js:7:2:7:54 | anonymous function |
| tst.js:8:1:8:31 | (functi ... own; }) | tst.js:8:2:8:30 | anonymous function |
| tst.js:11:5:11:6 | f1 | tst.js:10:5:10:19 | function f1 |
| tst.js:14:5:14:6 | f2 | tst.js:13:5:13:19 | function f2 |
| tst.js:15:5:15:6 | f2 | tst.js:13:5:13:19 | function f2 |
| tst.js:18:5:18:9 | o1.f3 | file://:0:0:0:0 | indefinite value (heap) |
| tst.js:18:5:18:9 | o1.f3 | tst.js:17:20:17:34 | function f3 |
| tst.js:23:5:23:6 | f4 | tst.js:20:5:22:5 | function f4 |
| tst.js:23:5:23:8 | f4() | tst.js:21:16:21:30 | function f5 |
| tst.js:30:5:30:6 | f6 | tst.js:25:5:29:5 | function f6 |
| tst.js:30:5:30:8 | f6() | tst.js:26:16:28:9 | function f7 |
| tst.js:30:5:30:10 | f6()() | tst.js:27:20:27:34 | function f8 |
| tst.js:32:14:32:20 | unknown | file://:0:0:0:0 | indefinite value (global) |
| tst.js:33:5:33:6 | f9 | file://:0:0:0:0 | indefinite value (call) |
| tst.js:36:5:36:7 | f10 | tst.js:35:24:35:36 | anonymous function |
| tst.js:36:5:36:7 | f10 | tst.js:35:39:35:51 | anonymous function |
| tst.js:39:5:39:7 | f11 | tst.js:38:24:38:48 | anonymous function |
| tst.js:39:5:39:7 | f11 | tst.js:38:51:38:78 | anonymous function |
| tst.js:41:39:41:45 | unknown | file://:0:0:0:0 | indefinite value (global) |
| tst.js:42:5:42:7 | f12 | file://:0:0:0:0 | indefinite value (call) |
| tst.js:42:5:42:7 | f12 | tst.js:41:24:41:36 | anonymous function |
| tst.js:45:5:45:7 | f13 | file://:0:0:0:0 | non-zero value |
| tst.js:45:5:45:7 | f13 | tst.js:44:24:44:36 | anonymous function |
| tst.js:48:5:48:7 | f14 | file://:0:0:0:0 | non-zero value |
| tst.js:48:5:48:7 | f14 | tst.js:47:34:47:56 | anonymous function |
| tst.js:48:5:48:7 | f14 | tst.js:47:59:47:86 | anonymous function |
| tst.js:50:5:50:16 | someFunction | file://:0:0:0:0 | indefinite value (global) |
| tst.js:51:5:51:25 | someObj ... eMethod | file://:0:0:0:0 | indefinite value (global) |
| tst.js:51:5:51:25 | someObj ... eMethod | file://:0:0:0:0 | indefinite value (heap) |
| tst.js:53:5:53:14 | someModule | file://:0:0:0:0 | indefinite value (call) |
| tst.js:53:5:53:14 | someModule | file://:0:0:0:0 | undefined |
| tst.js:54:5:54:25 | someMod ... eMethod | file://:0:0:0:0 | indefinite value (call) |
| tst.js:54:5:54:25 | someMod ... eMethod | file://:0:0:0:0 | indefinite value (heap) |
| tst.js:56:5:56:12 | myModule | file://:0:0:0:0 | indefinite value (call) |
| tst.js:56:5:56:12 | myModule | file://:0:0:0:0 | undefined |
| tst.js:56:5:56:12 | myModule | myModule.js:1:1:4:0 | exports object of module myModule |
| tst.js:56:5:56:12 | myModule | myModule.js:1:18:1:30 | anonymous function |
| tst.js:57:5:57:22 | myModule.myMethod1 | file://:0:0:0:0 | indefinite value (call) |
| tst.js:57:5:57:22 | myModule.myMethod1 | file://:0:0:0:0 | indefinite value (heap) |
| tst.js:58:5:58:22 | myModule.myMethod2 | file://:0:0:0:0 | indefinite value (call) |
| tst.js:58:5:58:22 | myModule.myMethod2 | file://:0:0:0:0 | indefinite value (heap) |
| tst.js:58:5:58:34 | myModul ... Method3 | file://:0:0:0:0 | indefinite value (call) |
| tst.js:58:5:58:34 | myModul ... Method3 | file://:0:0:0:0 | indefinite value (heap) |
| tst.js:61:5:61:7 | f15 | file://:0:0:0:0 | undefined |
| tst.js:61:5:61:7 | f15 | tst.js:60:24:60:36 | anonymous function |
| tst.js:64:5:64:7 | f16 | tst.js:63:24:63:36 | anonymous function |
| tst.js:64:5:64:7 | f16 | tst.js:63:39:63:40 | object literal |
| tst.js:68:9:68:11 | f17 | file://:0:0:0:0 | indefinite value (heap) |
| tst.js:68:9:68:11 | f17 | tst.js:67:31:67:42 | anonymous function |
| tst.js:73:9:73:11 | f18 | file://:0:0:0:0 | indefinite value (heap) |
| tst.js:73:9:73:11 | f18 | tst.js:72:31:72:42 | anonymous function |
| tst.js:77:5:77:7 | f19 | file://:0:0:0:0 | undefined |
| tst.js:80:5:80:7 | f20 | file://:0:0:0:0 | undefined |
| tst.js:80:5:80:7 | f20 | tst.js:79:24:79:25 | object literal |
| tst.js:84:17:84:20 | getF | tst.js:83:20:83:31 | function getF |
| tst.js:86:13:86:13 | f | file://:0:0:0:0 | indefinite value (call) |
| tst.js:86:13:86:13 | f | file://:0:0:0:0 | undefined |
| tst.js:89:17:89:20 | getG | tst.js:88:9:88:25 | function getG |
| tst.js:91:13:91:13 | g | file://:0:0:0:0 | undefined |

View File

@@ -0,0 +1,4 @@
import javascript
from DataFlow::InvokeNode call
select call.getCalleeNode(), call.getCalleeNode().analyze().getAValue()

View File

@@ -0,0 +1,47 @@
| tst.js:1:18:1:38 | require ... odule') | file://:0:0:0:0 | indefinite value (call) |
| tst.js:2:16:2:36 | require ... odule') | file://:0:0:0:0 | indefinite value (call) |
| tst.js:2:16:2:36 | require ... odule') | myModule.js:1:1:4:0 | exports object of module myModule |
| tst.js:2:16:2:36 | require ... odule') | myModule.js:1:18:1:30 | anonymous function |
| tst.js:4:1:4:16 | (function(){})() | file://:0:0:0:0 | undefined |
| tst.js:5:1:5:25 | (functi ... n; })() | file://:0:0:0:0 | undefined |
| tst.js:6:1:6:30 | (functi ... e; })() | file://:0:0:0:0 | true |
| tst.js:7:1:7:57 | (functi ... e; })() | file://:0:0:0:0 | false |
| tst.js:7:1:7:57 | (functi ... e; })() | file://:0:0:0:0 | true |
| tst.js:8:1:8:33 | (functi ... n; })() | file://:0:0:0:0 | indefinite value (global) |
| tst.js:11:5:11:8 | f1() | file://:0:0:0:0 | undefined |
| tst.js:14:5:14:8 | f2() | file://:0:0:0:0 | undefined |
| tst.js:15:5:15:8 | f2() | file://:0:0:0:0 | undefined |
| tst.js:18:5:18:11 | o1.f3() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:23:5:23:8 | f4() | tst.js:21:16:21:30 | function f5 |
| tst.js:23:5:23:10 | f4()() | file://:0:0:0:0 | undefined |
| tst.js:30:5:30:8 | f6() | tst.js:26:16:28:9 | function f7 |
| tst.js:30:5:30:10 | f6()() | tst.js:27:20:27:34 | function f8 |
| tst.js:30:5:30:12 | f6()()() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:32:14:32:22 | unknown() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:33:5:33:8 | f9() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:36:5:36:9 | f10() | file://:0:0:0:0 | undefined |
| tst.js:39:5:39:9 | f11() | file://:0:0:0:0 | non-empty, non-numeric string |
| tst.js:39:5:39:9 | f11() | file://:0:0:0:0 | non-zero value |
| tst.js:41:39:41:47 | unknown() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:42:5:42:9 | f12() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:45:5:45:9 | f13() | file://:0:0:0:0 | undefined |
| tst.js:48:5:48:9 | f14() | file://:0:0:0:0 | non-empty, non-numeric string |
| tst.js:48:5:48:9 | f14() | file://:0:0:0:0 | non-zero value |
| tst.js:50:5:50:18 | someFunction() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:51:5:51:27 | someObj ... ethod() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:53:5:53:16 | someModule() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:54:5:54:27 | someMod ... ethod() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:56:5:56:14 | myModule() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:57:5:57:24 | myModule.myMethod1() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:58:5:58:24 | myModule.myMethod2() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:58:5:58:36 | myModul ... thod3() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:61:5:61:9 | f15() | file://:0:0:0:0 | undefined |
| tst.js:64:5:64:9 | f16() | file://:0:0:0:0 | undefined |
| tst.js:68:9:68:13 | f17() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:73:9:73:13 | f18() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:77:5:77:9 | f19() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:80:5:80:9 | f20() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:84:17:84:22 | getF() | file://:0:0:0:0 | undefined |
| tst.js:86:13:86:15 | f() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:89:17:89:22 | getG() | file://:0:0:0:0 | undefined |
| tst.js:91:13:91:15 | g() | file://:0:0:0:0 | indefinite value (call) |

View File

@@ -0,0 +1,4 @@
import javascript
from DataFlow::InvokeNode call
select call, call.analyze().getAValue()

View File

@@ -0,0 +1,3 @@
module.exports = function() {}
module.exports.myMyMethod1 = function(){}
module.exports.myMyMethod2 = function(){ return function(){} }

View File

@@ -1,5 +1,95 @@
var someModule = require('someModule'),
myModule = require('./myModule');
(function(){})();
(function(){ return; })();
(function(){ return true; })();
(function(){ if (x) return true; return false; })();
(function(){ return x; })();
(function(){ if (unknown) return true; return false; })();
(function(){ return unknown; })();
(function(){
function f1(){}
f1();
function f2(){}
f2();
f2();
var o1 = { f3: function f3(){} };
o1.f3();
function f4(){
return function f5(){}
}
f4()();
function f6(){
return function f7(){
return function f8(){}
}
}
f6()()();
var f9 = unknown();
f9();
var f10 = unknown? function (){}: function (){};
f10();
var f11 = unknown? function (){ return 42; }: function (){ return "foo"; };
f11();
var f12 = unknown? function (){}: unknown();
f12();
var f13 = unknown? function (){}: 42;
f13(); // the call may crash, but the eventual return value is known!
var f14 = unknown? (unknown? function (){ return 42}: function (){ return "foo"; }): 42;
f14(); // the call may crash, but the eventual return value is known!
someFunction();
someObject.someMethod();
someModule();
someModule.someMethod();
myModule();
myModule.myMethod1();
myModule.myMethod2().myMethod3();
var f15 = unknown? function (){}: undefined;
f15(); // the call may crash, but the eventual return value is known!
var f16 = unknown? function (){}: {};
f16(); // the call may crash, but the eventual return value is known!
for (var f of { f: function(){} }) {
var f17 = unknown? f: function(){}
f17();
}
for (var f of unknown) {
var f18 = unknown? f: function(){};
f18();
}
var f19 = undefined;
f19();
var f20 = unknown? {}: undefined;
f20();
(function () {
var getF = function(){}
var f = getF();
(function () {
f();
});
function getG(){}
var g = getG();
(function () {
g();
});
});
});

View File

@@ -0,0 +1,16 @@
| tst.js:6:7:6:10 | true | 0 | file://:0:0:0:0 | true |
| tst.js:6:13:6:19 | UNKNOWN | 1 | file://:0:0:0:0 | indefinite value (global) |
| tst.js:6:22:6:31 | getKnown() | 2 | file://:0:0:0:0 | true |
| tst.js:6:34:6:45 | getUnknown() | 3 | file://:0:0:0:0 | indefinite value (global) |
| tst.js:6:48:6:55 | getKnown | 4 | tst.js:2:5:2:40 | function getKnown |
| tst.js:6:58:6:67 | getUnknown | 5 | tst.js:4:5:4:45 | function getUnknown |
| tst.js:9:14:9:23 | getKnown() | 0 | file://:0:0:0:0 | true |
| tst.js:9:26:9:37 | getUnknown() | 1 | file://:0:0:0:0 | indefinite value (global) |
| tst.js:10:14:10:30 | getKnown_indirect | 0 | tst.js:2:5:2:40 | function getKnown |
| tst.js:10:33:10:51 | getUnknown_indirect | 1 | tst.js:4:5:4:45 | function getUnknown |
| tst.js:11:14:11:18 | known | 0 | file://:0:0:0:0 | true |
| tst.js:11:21:11:27 | unknown | 1 | file://:0:0:0:0 | indefinite value (global) |
| tst.js:11:30:11:37 | gotKnown | 2 | file://:0:0:0:0 | indefinite value (call) |
| tst.js:11:40:11:49 | gotUnknown | 3 | file://:0:0:0:0 | indefinite value (call) |
| tst.js:11:52:11:70 | getKnown_indirect() | 4 | file://:0:0:0:0 | true |
| tst.js:11:73:11:93 | getUnkn ... irect() | 5 | file://:0:0:0:0 | indefinite value (global) |

View File

@@ -0,0 +1,5 @@
import javascript
from DataFlow::CallNode c, int i, DataFlow::Node arg
where arg = c.getArgument(i)
select arg, i, arg.analyze().getAValue()

View File

@@ -0,0 +1,13 @@
(function(){
function getKnown() { return true; }
function getUnknown() { return UNKNOWN; }
f(true, UNKNOWN, getKnown(), getUnknown(), getKnown, getUnknown);
function f(known, unknown, gotKnown, gotUnknown, getKnown_indirect, getUnknown_indirect) {
DUMP(getKnown(), getUnknown());
DUMP(getKnown_indirect, getUnknown_indirect);
DUMP(known, unknown, gotKnown, gotUnknown, getKnown_indirect(), getUnknown_indirect())
}
});

View File

@@ -18,6 +18,7 @@
| src/iterated-handlers.js:4:2:4:22 | functio ... res){} |
| src/middleware-attacher-getter.js:4:17:4:36 | function(req, res){} |
| src/middleware-attacher-getter.js:19:19:19:38 | function(req, res){} |
| src/middleware-attacher-getter.js:29:32:29:51 | function(req, res){} |
| src/middleware-attacher.js:3:13:3:32 | function(req, res){} |
| src/nodejs.js:3:19:3:38 | function(req, res){} |
| src/nodejs.js:5:22:5:41 | function(req, res){} |

View File

@@ -14,6 +14,7 @@
| src/handler-in-property.js:5:14:5:33 | function(req, res){} |
| src/handler-in-property.js:12:18:12:37 | function(req, res){} |
| src/middleware-attacher-getter.js:4:17:4:36 | function(req, res){} |
| src/middleware-attacher-getter.js:19:19:19:38 | function(req, res){} |
| src/middleware-attacher.js:3:13:3:32 | function(req, res){} |
| src/nodejs.js:3:19:3:38 | function(req, res){} |
| src/nodejs.js:8:14:8:33 | function(req, res){} |

View File

@@ -18,6 +18,7 @@
| src/iterated-handlers.js:4:2:4:22 | functio ... res){} |
| src/middleware-attacher-getter.js:4:17:4:36 | function(req, res){} |
| src/middleware-attacher-getter.js:19:19:19:38 | function(req, res){} |
| src/middleware-attacher-getter.js:29:32:29:51 | function(req, res){} |
| src/middleware-attacher.js:3:13:3:32 | function(req, res){} |
| src/nodejs.js:3:19:3:38 | function(req, res){} |
| src/nodejs.js:5:22:5:41 | function(req, res){} |

View File

@@ -18,6 +18,7 @@
| src/iterated-handlers.js:5:5:5:14 | app.use(h) |
| src/middleware-attacher-getter.js:4:9:4:37 | app.use ... res){}) |
| src/middleware-attacher-getter.js:14:9:14:18 | app.use(h) |
| src/middleware-attacher-getter.js:24:9:24:18 | app.use(h) |
| src/middleware-attacher.js:3:5:3:33 | app.use ... res){}) |
| src/nodejs.js:3:1:3:39 | http.cr ... res){}) |
| src/nodejs.js:8:1:8:34 | createS ... res){}) |

View File

@@ -18,6 +18,7 @@
| src/iterated-handlers.js:5:5:5:14 | app.use(h) |
| src/middleware-attacher-getter.js:4:9:4:37 | app.use ... res){}) |
| src/middleware-attacher-getter.js:14:9:14:18 | app.use(h) |
| src/middleware-attacher-getter.js:24:9:24:18 | app.use(h) |
| src/middleware-attacher.js:3:5:3:33 | app.use ... res){}) |
| src/nodejs.js:3:1:3:39 | http.cr ... res){}) |
| src/nodejs.js:5:1:5:42 | unknown ... res){}) |

View File

@@ -1,7 +1,7 @@
| src/bound-handler.js:4:1:4:28 | functio ... res){} | A `RouteHandlerCandidate` that did not get promoted to `RouteHandler`, and it is not used in a `RouteSetupCandidate`. |
| src/bound-handler.js:9:12:9:31 | function(req, res){} | A `RouteHandlerCandidate` that did not get promoted to `RouteHandler`, and it is not used in a `RouteSetupCandidate`. |
| src/iterated-handlers.js:4:2:4:22 | functio ... res){} | A `RouteHandlerCandidate` that did not get promoted to `RouteHandler`, and it is not used in a `RouteSetupCandidate`. |
| src/middleware-attacher-getter.js:19:19:19:38 | function(req, res){} | A `RouteHandlerCandidate` that did not get promoted to `RouteHandler`, and it is not used in a `RouteSetupCandidate`. |
| src/middleware-attacher-getter.js:29:32:29:51 | function(req, res){} | A `RouteHandlerCandidate` that did not get promoted to `RouteHandler`, and it is not used in a `RouteSetupCandidate`. |
| src/route-objects.js:7:19:7:38 | function(req, res){} | A `RouteHandlerCandidate` that did not get promoted to `RouteHandler`, and it is not used in a `RouteSetupCandidate`. |
| src/route-objects.js:8:12:10:5 | (req, res) {\\n\\n } | A `RouteHandlerCandidate` that did not get promoted to `RouteHandler`, and it is not used in a `RouteSetupCandidate`. |
| src/route-objects.js:20:16:22:9 | (req, r ... } | A `RouteHandlerCandidate` that did not get promoted to `RouteHandler`, and it is not used in a `RouteSetupCandidate`. |

View File

@@ -7,7 +7,7 @@ function getAttacher1 (app) {
var app = express();
getAttacher1(app);
confuse(getAttacher2); // disable the type inference
confuse(getAttacher2); // attempt to disable the type inference
function getAttacher2 (app) {
return function(h) {
@@ -17,4 +17,13 @@ function getAttacher2 (app) {
var app = express();
getAttacher2(app)(function(req, res){});
confuse(getAttacher2); // disable the type inference
confuse(getAttacher2); // attempt to disable the type inference
function getAttacher3 (app) {
return function(h) {
app.use(h);
};
}
var app = express();
(unknown || getAttacher3)(app)(function(req, res){}); // confuse the type inference

View File

@@ -1,3 +1,6 @@
| interprocedural.js:11:9:11:13 | known | Variable 'known' is of type string, but it is compared to $@ of type number. | interprocedural.js:11:19:11:20 | 42 | an expression |
| interprocedural.js:15:9:15:18 | getKnown() | This expression is of type string, but it is compared to $@ of type number. | interprocedural.js:15:24:15:25 | 42 | an expression |
| interprocedural.js:17:9:17:27 | getKnown_indirect() | This expression is of type string, but it is compared to $@ of type number. | interprocedural.js:17:33:17:34 | 42 | an expression |
| tst.js:2:5:2:17 | typeof window | This expression is of type string, but it is compared to $@ of type undefined. | tst.js:2:23:2:31 | undefined | 'undefined' |
| tst.js:10:28:10:34 | "Hello" | This expression is of type string, but it is compared to $@ of type number. | tst.js:10:39:10:39 | 0 | an expression |
| tst.js:20:1:20:4 | null | This expression is of type null, but it is compared to $@ of type number. | tst.js:20:9:20:9 | 0 | an expression |

View File

@@ -0,0 +1,32 @@
(function(){
function getUnknown() { return UNKNOWN; }
function getKnown() { return "foo"; }
function f(known, unknown, gotKnown, gotUnknown, getKnown_indirect, getUnknown_indirect) {
// disable the whitelist
known = known; unknown = unknown; gotKnown = gotKnown; gotUnknown = gotUnknown;
known === 42;
known == 42;
gotKnown === 42;
gotKnown == 42;
getKnown() === 42;
getKnown() == 42;
getKnown_indirect() === 42;
getKnown_indirect() == 42;
unknown === 42;
unknown == 42;
gotUnknown === 42;
gotUnknown == 42;
getUnknown() === 42;
getUnknown() == 42;
getUnknown_indirect() === 42;
getUnknown_indirect() == 42;
}
f("foo", UNKNOWN, getKnown(), getUnknown(), getKnown, getUnknown);
});

View File

@@ -9,3 +9,5 @@
| tst.js:73:5:73:5 | x | This expression will be implicitly converted from undefined to number. |
| tst.js:79:19:79:22 | name | This expression will be implicitly converted from undefined to string. |
| tst.js:85:3:85:3 | x | This expression will be implicitly converted from undefined to number. |
| tst.js:100:5:100:7 | f() | This expression will be implicitly converted from undefined to number. |
| tst.js:106:5:106:7 | g() | This expression will be implicitly converted from undefined to number. |

View File

@@ -94,4 +94,17 @@ function l() {
}
});
(function(){
function f() {
}
f()|0;
unknown()|0;
function g() {
}
g()|0;
g();
});
// semmle-extractor-options: --experimental

View File

@@ -0,0 +1,17 @@
(function () {
var getF = function(){}
var f = getF();
(function () {
f();
});
});
(function () {
var getF = unknown
if (false) {
var f = getF();
(function () {
f();
});
}
});

View File

@@ -1,3 +1,4 @@
| SuspiciousPropAccess.js:4:10:4:21 | result.value | The base expression of this property access is always undefined. |
| tst.js:32:32:32:38 | a(1)[0] | The base expression of this property access is always null. |
| tst.ts:19:3:19:5 | x.p | The base expression of this property access is always undefined. |
| typeassertion.ts:14:3:14:9 | z.field | The base expression of this property access is always null. |

View File

@@ -27,3 +27,7 @@
v1.p;
}
});
(function(){
function a(){return null;} a(1)[0];
});