mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
Merge pull request #946 from esben-semmle/js/captured-nodes-query-and-type-inference-1
JS: Captured Nodes, type inference + a query
This commit is contained in:
@@ -31,7 +31,7 @@
|
||||
| Incomplete URL substring sanitization | correctness, security, external/cwe/cwe-020 | Highlights URL sanitizers that are likely to be incomplete, indicating a violation of [CWE-020](https://cwe.mitre.org/data/definitions/20.html). Results shown on LGTM by default. |
|
||||
| Incorrect suffix check (`js/incorrect-suffix-check`) | correctness, security, external/cwe/cwe-020 | Highlights error-prone suffix checks based on `indexOf`, indicating a potential violation of [CWE-20](https://cwe.mitre.org/data/definitions/20.html). Results are shown on LGTM by default. |
|
||||
| Loop iteration skipped due to shifting (`js/loop-iteration-skipped-due-to-shifting`) | correctness | Highlights code that removes an element from an array while iterating over it, causing the loop to skip over some elements. Results are shown on LGTM by default. |
|
||||
| Unbound event handler receiver (`js/unbound-event-handler-receiver`) | Fewer false positive results | Additional ways that class methods can be bound are recognized. |
|
||||
| Unused property (`js/unused-property`) | maintainability | Highlights properties that are unused. Results are shown on LGTM by default. |
|
||||
| Useless comparison test (`js/useless-comparison-test`) | correctness | Highlights code that is unreachable due to a numeric comparison that is always true or always false. Results are shown on LGTM by default. |
|
||||
|
||||
## Changes to existing queries
|
||||
@@ -44,9 +44,10 @@
|
||||
| Insecure randomness | More results | This rule now flags insecure uses of `crypto.pseudoRandomBytes`. |
|
||||
| Reflected cross-site scripting | Fewer false-positive results. | This rule now recognizes custom sanitizers. |
|
||||
| Stored cross-site scripting | Fewer false-positive results. | This rule now recognizes custom sanitizers. |
|
||||
| Unbound event handler receiver (`js/unbound-event-handler-receiver`) | Fewer false positive results | Additional ways that class methods can be bound are recognized. |
|
||||
| Uncontrolled data used in network request | More results | This rule now recognizes host values that are vulnerable to injection. |
|
||||
| Unused parameter | Fewer false-positive results | This rule no longer flags parameters with leading underscore. |
|
||||
| Unused variable, import, function or class | Fewer false-positive results | This rule now flags fewer variables that are implictly used by JSX elements, and no longer flags variables with leading underscore. |
|
||||
| Unused variable, import, function or class | Fewer false-positive results | This rule now flags fewer variables that are implictly used by JSX elements, no longer flags variables with leading underscore, and no longer flags variables in dead code. |
|
||||
| Uncontrolled data used in path expression | Fewer false-positive results | This rule now recognizes the Express `root` option, which prevents path traversal. |
|
||||
| Unneeded defensive code | More true-positive results, fewer false-positive results. | This rule now recognizes additional defensive code patterns. |
|
||||
| Useless conditional | Fewer results | Additional defensive coding patterns are now ignored. |
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
+ semmlecode-javascript-queries/Declarations/DeadStoreOfProperty.ql: /Maintainability/Declarations
|
||||
+ semmlecode-javascript-queries/Declarations/DuplicateVarDecl.ql: /Maintainability/Declarations
|
||||
+ semmlecode-javascript-queries/Declarations/UnusedParameter.ql: /Maintainability/Declarations
|
||||
+ semmlecode-javascript-queries/Declarations/UnusedProperty.ql: /Maintainability/Declarations
|
||||
+ semmlecode-javascript-queries/Declarations/UnusedVariable.ql: /Maintainability/Declarations
|
||||
+ semmlecode-javascript-queries/Expressions/UnneededDefensiveProgramming.ql: /Maintainability/Expressions
|
||||
+ semmlecode-javascript-queries/LanguageFeatures/ArgumentsCallerCallee.ql: /Maintainability/Language Features
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* This library contains the majority of the 'js/unused-parameter' query implementation.
|
||||
* Provides classes and predicates for the 'js/unused-parameter' query.
|
||||
*
|
||||
* In order to suppress alerts that are similar to the 'js/unused-parameter' alerts,
|
||||
* `isAnAccidentallyUnusedParameter` should be used since it holds iff that alert is active.
|
||||
|
||||
34
javascript/ql/src/Declarations/UnusedProperty.qhelp
Normal file
34
javascript/ql/src/Declarations/UnusedProperty.qhelp
Normal file
@@ -0,0 +1,34 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
Unused object properties make code harder to maintain and use. Clients that are unaware that a
|
||||
property is unused may perform nontrivial computations to compute a value that is ultimately
|
||||
unused.
|
||||
</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>Remove the unused property.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>
|
||||
In this code, the function <code>f</code> initializes a property <code>prop_a</code> with a
|
||||
call to the function <code>expensiveComputation</code>, but later on this property is never read.
|
||||
Removing <code>prop</code> would improve code quality and performance.
|
||||
</p>
|
||||
|
||||
<sample src="examples/UnusedProperty.js" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>Coding Horror: <a href="http://blog.codinghorror.com/code-smells/">Code Smells</a>.</li>
|
||||
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
78
javascript/ql/src/Declarations/UnusedProperty.ql
Normal file
78
javascript/ql/src/Declarations/UnusedProperty.ql
Normal file
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* @name Unused property
|
||||
* @description Unused properties may be a symptom of a bug and should be examined carefully.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @id js/unused-property
|
||||
* @tags maintainability
|
||||
* @precision high
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import semmle.javascript.dataflow.LocalObjects
|
||||
import UnusedVariable
|
||||
import UnusedParameter
|
||||
import Expressions.ExprHasNoEffect
|
||||
|
||||
predicate hasUnknownPropertyRead(LocalObject obj) {
|
||||
// dynamic reads
|
||||
exists(DataFlow::PropRead r | obj.getAPropertyRead() = r | not exists(r.getPropertyName()))
|
||||
or
|
||||
// reflective reads
|
||||
obj.flowsToExpr(any(EnhancedForLoop l).getIterationDomain())
|
||||
or
|
||||
obj.flowsToExpr(any(InExpr l).getRightOperand())
|
||||
or
|
||||
obj.flowsToExpr(any(SpreadElement e).getOperand())
|
||||
or
|
||||
exists(obj.getAPropertyRead("hasOwnProperty"))
|
||||
or
|
||||
exists(obj.getAPropertyRead("propertyIsEnumerable"))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `obj` flows to an expression that must have a specific type.
|
||||
*/
|
||||
predicate flowsToTypeRestrictedExpression(LocalObject obj) {
|
||||
exists (Expr restricted, TypeExpr type |
|
||||
obj.flowsToExpr(restricted) and
|
||||
not type.isAny() |
|
||||
exists (TypeAssertion assertion |
|
||||
type = assertion.getTypeAnnotation() and
|
||||
restricted = assertion.getExpression()
|
||||
)
|
||||
or
|
||||
exists (BindingPattern v |
|
||||
type = v.getTypeAnnotation() and
|
||||
restricted = v.getAVariable().getAnAssignedExpr()
|
||||
)
|
||||
// no need to reason about writes to typed fields, captured nodes do not reach them
|
||||
)
|
||||
}
|
||||
|
||||
from DataFlow::PropWrite write, LocalObject obj, string name
|
||||
where
|
||||
write = obj.getAPropertyWrite(name) and
|
||||
not exists(obj.getAPropertyRead(name)) and
|
||||
// `obj` is the only base object for the write: it is not spurious
|
||||
not write.getBase().analyze().getAValue() != obj.analyze().getAValue() and
|
||||
not hasUnknownPropertyRead(obj) and
|
||||
// avoid reporting if the definition is unreachable
|
||||
write.getAstNode().getFirstControlFlowNode().getBasicBlock() instanceof ReachableBasicBlock and
|
||||
// avoid implicitly read properties
|
||||
not (
|
||||
name = "toString" or
|
||||
name = "valueOf" or
|
||||
name.matches("@@%") // @@iterator, for example
|
||||
) and
|
||||
// avoid flagging properties that a type system requires
|
||||
not flowsToTypeRestrictedExpression(obj) and
|
||||
// flagged by js/unused-local-variable
|
||||
not exists(UnusedLocal l | l.getAnAssignedExpr().getUnderlyingValue().flow() = obj) and
|
||||
// flagged by js/unused-parameter
|
||||
not exists(Parameter p | isAnAccidentallyUnusedParameter(p) |
|
||||
p.getDefault().getUnderlyingValue().flow() = obj
|
||||
) and
|
||||
// flagged by js/useless-expression
|
||||
not inVoidContext(obj.asExpr())
|
||||
select write, "Unused property " + name + "."
|
||||
@@ -10,23 +10,7 @@
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* A local variable that is neither used nor exported, and is not a parameter
|
||||
* or a function name.
|
||||
*/
|
||||
class UnusedLocal extends LocalVariable {
|
||||
UnusedLocal() {
|
||||
not exists(getAnAccess()) and
|
||||
not exists(Parameter p | this = p.getAVariable()) and
|
||||
not exists(FunctionExpr fe | this = fe.getVariable()) and
|
||||
not exists(ClassExpr ce | this = ce.getVariable()) and
|
||||
not exists(ExportDeclaration ed | ed.exportsAs(this, _)) and
|
||||
not exists(LocalVarTypeAccess type | type.getVariable() = this) and
|
||||
// common convention: variables with leading underscore are intentionally unused
|
||||
getName().charAt(0) != "_"
|
||||
}
|
||||
}
|
||||
import UnusedVariable
|
||||
|
||||
/**
|
||||
* Holds if `v` is mentioned in a JSDoc comment in the same file, and that file
|
||||
@@ -206,6 +190,10 @@ predicate unusedImports(ImportVarDeclProvider provider, string msg) {
|
||||
|
||||
from ASTNode sel, string msg
|
||||
where
|
||||
unusedNonImports(sel, msg) or
|
||||
unusedImports(sel, msg)
|
||||
(
|
||||
unusedNonImports(sel, msg) or
|
||||
unusedImports(sel, msg)
|
||||
) and
|
||||
// avoid reporting if the definition is unreachable
|
||||
sel.getFirstControlFlowNode().getBasicBlock() instanceof ReachableBasicBlock
|
||||
select sel, msg
|
||||
|
||||
22
javascript/ql/src/Declarations/UnusedVariable.qll
Normal file
22
javascript/ql/src/Declarations/UnusedVariable.qll
Normal file
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Provides classes and predicates for the 'js/unused-local-variable' query.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* A local variable that is neither used nor exported, and is not a parameter
|
||||
* or a function name.
|
||||
*/
|
||||
class UnusedLocal extends LocalVariable {
|
||||
UnusedLocal() {
|
||||
not exists(getAnAccess()) and
|
||||
not exists(Parameter p | this = p.getAVariable()) and
|
||||
not exists(FunctionExpr fe | this = fe.getVariable()) and
|
||||
not exists(ClassExpr ce | this = ce.getVariable()) and
|
||||
not exists(ExportDeclaration ed | ed.exportsAs(this, _)) and
|
||||
not exists(LocalVarTypeAccess type | type.getVariable() = this) and
|
||||
// common convention: variables with leading underscore are intentionally unused
|
||||
getName().charAt(0) != "_"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
function f() {
|
||||
var o = {
|
||||
prop_a: expensiveComputation(),
|
||||
prop_b: anotherComputation()
|
||||
};
|
||||
|
||||
return o.prop_b;
|
||||
}
|
||||
@@ -16,40 +16,7 @@ import javascript
|
||||
import DOMProperties
|
||||
import semmle.javascript.frameworks.xUnit
|
||||
import semmle.javascript.RestrictedLocations
|
||||
|
||||
/**
|
||||
* Holds if `e` appears in a syntactic context where its value is discarded.
|
||||
*/
|
||||
predicate inVoidContext(Expr e) {
|
||||
exists(ExprStmt parent |
|
||||
// e is a toplevel expression in an expression statement
|
||||
parent = e.getParent() and
|
||||
// but it isn't an HTML attribute or a configuration object
|
||||
not exists(TopLevel tl | tl = parent.getParent() |
|
||||
tl instanceof CodeInAttribute
|
||||
or
|
||||
// if the toplevel in its entirety is of the form `({ ... })`,
|
||||
// it is probably a configuration object (e.g., a require.js build configuration)
|
||||
tl.getNumChildStmt() = 1 and e.stripParens() instanceof ObjectExpr
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(SeqExpr seq, int i, int n |
|
||||
e = seq.getOperand(i) and
|
||||
n = seq.getNumOperands()
|
||||
|
|
||||
i < n - 1 or inVoidContext(seq)
|
||||
)
|
||||
or
|
||||
exists(ForStmt stmt | e = stmt.getUpdate())
|
||||
or
|
||||
exists(ForStmt stmt | e = stmt.getInit() |
|
||||
// Allow the pattern `for(i; i < 10; i++)`
|
||||
not e instanceof VarAccess
|
||||
)
|
||||
or
|
||||
exists(LogicalBinaryExpr logical | e = logical.getRightOperand() and inVoidContext(logical))
|
||||
}
|
||||
import ExprHasNoEffect
|
||||
|
||||
/**
|
||||
* Holds if `e` is of the form `x;` or `e.p;` and has a JSDoc comment containing a tag.
|
||||
|
||||
39
javascript/ql/src/Expressions/ExprHasNoEffect.qll
Normal file
39
javascript/ql/src/Expressions/ExprHasNoEffect.qll
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Provides classes and predicates for the 'js/useless-expression' query.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* Holds if `e` appears in a syntactic context where its value is discarded.
|
||||
*/
|
||||
predicate inVoidContext(Expr e) {
|
||||
exists(ExprStmt parent |
|
||||
// e is a toplevel expression in an expression statement
|
||||
parent = e.getParent() and
|
||||
// but it isn't an HTML attribute or a configuration object
|
||||
not exists(TopLevel tl | tl = parent.getParent() |
|
||||
tl instanceof CodeInAttribute
|
||||
or
|
||||
// if the toplevel in its entirety is of the form `({ ... })`,
|
||||
// it is probably a configuration object (e.g., a require.js build configuration)
|
||||
tl.getNumChildStmt() = 1 and e.stripParens() instanceof ObjectExpr
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(SeqExpr seq, int i, int n |
|
||||
e = seq.getOperand(i) and
|
||||
n = seq.getNumOperands()
|
||||
|
|
||||
i < n - 1 or inVoidContext(seq)
|
||||
)
|
||||
or
|
||||
exists(ForStmt stmt | e = stmt.getUpdate())
|
||||
or
|
||||
exists(ForStmt stmt | e = stmt.getInit() |
|
||||
// Allow the pattern `for(i; i < 10; i++)`
|
||||
not e instanceof VarAccess
|
||||
)
|
||||
or
|
||||
exists(LogicalBinaryExpr logical | e = logical.getRightOperand() and inVoidContext(logical))
|
||||
}
|
||||
@@ -583,7 +583,18 @@ module DataFlow {
|
||||
|
||||
override string getPropertyName() { result = prop.getName() }
|
||||
|
||||
override Node getRhs() { result = parameterNode(prop.getParameter()) }
|
||||
override Node getRhs() {
|
||||
exists(Parameter param, Node paramNode |
|
||||
param = prop.getParameter() and
|
||||
parameterNode(paramNode, param)
|
||||
|
|
||||
result = paramNode
|
||||
or
|
||||
// special case: there is no SSA flow step for unused parameters
|
||||
paramNode instanceof UnusedParameterNode and
|
||||
result = param.getDefault().flow()
|
||||
)
|
||||
}
|
||||
|
||||
override ControlFlowNode getWriteNode() { result = prop.getParameter() }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* Provides classes for the local objects that the dataflow library can reason about soundly.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* Holds if the dataflow library can not track flow through `escape` due to `cause`.
|
||||
*/
|
||||
private predicate isEscape(DataFlow::Node escape, string cause) {
|
||||
escape = any(DataFlow::InvokeNode invk).getAnArgument() and cause = "argument"
|
||||
or
|
||||
escape = any(DataFlow::FunctionNode fun).getAReturn() and cause = "return"
|
||||
or
|
||||
escape = any(ThrowStmt t).getExpr().flow() and cause = "throw"
|
||||
or
|
||||
escape = any(DataFlow::GlobalVariable v).getAnAssignedExpr().flow() and cause = "global"
|
||||
or
|
||||
escape = any(DataFlow::PropWrite write).getRhs() and cause = "heap"
|
||||
or
|
||||
escape = any(ExportDeclaration e).getSourceNode(_) and cause = "export"
|
||||
or
|
||||
exists (WithStmt with, Assignment assign |
|
||||
with.mayAffect(assign.getLhs()) and
|
||||
assign.getRhs().flow() = escape and
|
||||
cause = "heap"
|
||||
)
|
||||
}
|
||||
|
||||
private DataFlow::Node getAnEscape() {
|
||||
isEscape(result, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n` can flow to a `this`-variable.
|
||||
*/
|
||||
private predicate exposedAsReceiver(DataFlow::SourceNode n) {
|
||||
// pragmatic limitation: guarantee for object literals only
|
||||
not n instanceof DataFlow::ObjectLiteralNode
|
||||
or
|
||||
exists(AbstractValue v | n.getAPropertyWrite().getRhs().analyze().getALocalValue() = v |
|
||||
v.isIndefinite(_) or
|
||||
exists(ThisExpr dis | dis.getBinder() = v.(AbstractCallable).getFunction())
|
||||
)
|
||||
or
|
||||
n.flowsToExpr(any(FunctionBindExpr bind).getObject())
|
||||
or
|
||||
// technically, the builtin prototypes could have a `this`-using function through which this node escapes, but we ignore that here
|
||||
// (we also ignore `o['__' + 'proto__'] = ...`)
|
||||
exists(n.getAPropertyWrite("__proto__"))
|
||||
or
|
||||
// could check the assigned value of all affected variables, but it is unlikely to matter in practice
|
||||
exists(WithStmt with | n.flowsToExpr(with.getExpr()))
|
||||
}
|
||||
|
||||
/**
|
||||
* An object that is entirely local, in the sense that the dataflow
|
||||
* library models all of its flow.
|
||||
*
|
||||
* All uses of this node are modeled by `this.flowsTo(_)` and related predicates.
|
||||
*/
|
||||
class LocalObject extends DataFlow::SourceNode {
|
||||
LocalObject() {
|
||||
// pragmatic limitation: object literals only
|
||||
this instanceof DataFlow::ObjectLiteralNode and
|
||||
not flowsTo(getAnEscape()) and
|
||||
not exposedAsReceiver(this)
|
||||
}
|
||||
|
||||
predicate hasOwnProperty(string name) {
|
||||
// the property is defined in the initializer,
|
||||
any(DataFlow::PropWrite write).writes(this, name, _) and
|
||||
// and it is never deleted
|
||||
not exists(DeleteExpr del, DataFlow::PropRef ref |
|
||||
del.getOperand().flow() = ref and
|
||||
flowsTo(ref.getBase()) and
|
||||
(ref.getPropertyName() = name or not exists(ref.getPropertyName()))
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
import javascript
|
||||
import AbstractValuesImpl
|
||||
import semmle.javascript.dataflow.LocalObjects
|
||||
|
||||
/**
|
||||
* Flow analysis for `this` expressions inside functions.
|
||||
@@ -230,3 +231,49 @@ private class TypeInferredCalleeWithAnalyzedReturnFlow extends CallWithNonLocalA
|
||||
|
||||
override AnalyzedFunction getACallee() { result = fun }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `call` uses `receiver` as its only receiver value.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate hasDefiniteReceiver(
|
||||
DataFlow::MethodCallNode call, LocalObject receiver
|
||||
) {
|
||||
call = receiver.getAMethodCall() and
|
||||
exists (DataFlow::AnalyzedNode receiverNode, AbstractValue abstractCapturedReceiver |
|
||||
receiverNode = call.getReceiver() and
|
||||
not receiverNode.getALocalValue().isIndefinite(_) and
|
||||
abstractCapturedReceiver = receiver.analyze().getALocalValue() and
|
||||
forall(DataFlow::AbstractValue v |
|
||||
receiverNode.getALocalValue() = v |
|
||||
v = abstractCapturedReceiver
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables inter-procedural type inference for the return value of a
|
||||
* method call to a flow-insensitively type-inferred callee.
|
||||
*/
|
||||
private class TypeInferredMethodWithAnalyzedReturnFlow extends CallWithNonLocalAnalyzedReturnFlow {
|
||||
DataFlow::FunctionNode fun;
|
||||
|
||||
TypeInferredMethodWithAnalyzedReturnFlow() {
|
||||
exists(LocalObject obj, DataFlow::PropWrite write, string name |
|
||||
this.(DataFlow::MethodCallNode).getMethodName() = name and
|
||||
obj.hasOwnProperty(name) and
|
||||
hasDefiniteReceiver(this, obj) and
|
||||
// include all potential callees
|
||||
// by construction, there are no unknown methods on `obj`
|
||||
write = obj.getAPropertyWrite() and
|
||||
fun.flowsTo(write.getRhs()) and
|
||||
(
|
||||
not exists(write.getPropertyName())
|
||||
or
|
||||
write.getPropertyName() = name
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override AnalyzedFunction getACallee() { result = fun }
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
| method-calls.js:2:11:7:2 | {\\n\\t\\tm1: ... }; }\\n\\t} |
|
||||
| method-calls.js:11:23:11:24 | {} |
|
||||
| method-calls.js:16:11:16:12 | {} |
|
||||
| method-calls.js:25:11:25:17 | {m: f1} |
|
||||
| method-calls.js:29:11:29:17 | {m: f2} |
|
||||
| method-calls.js:34:12:34:18 | {m: f3} |
|
||||
| method-calls.js:38:15:38:21 | {m: f4} |
|
||||
| method-calls.js:46:16:46:28 | {m: () => 42} |
|
||||
| method-calls.js:50:17:50:29 | {m: () => 42} |
|
||||
| method-calls.js:54:16:54:28 | {m: () => 42} |
|
||||
| method-calls.js:57:16:57:28 | {m: () => 42} |
|
||||
| tst.js:3:18:3:19 | {} |
|
||||
| tst.js:4:18:4:36 | { f: function(){} } |
|
||||
| tst.js:5:18:5:34 | { [unknown]: 42 } |
|
||||
| tst.js:38:18:38:26 | { p: 42 } |
|
||||
| tst.js:42:18:42:33 | { p: 42, q: 42 } |
|
||||
| tst.js:52:23:52:24 | {} |
|
||||
| tst.js:56:20:56:35 | { p: 42, p: 42 } |
|
||||
| tst.js:59:20:59:28 | { p: 42 } |
|
||||
| tst.js:63:20:63:28 | { p: 42 } |
|
||||
| tst.js:68:19:68:27 | { p: 42 } |
|
||||
| tst.js:73:18:73:20 | { } |
|
||||
| tst.js:76:18:76:26 | { p: 42 } |
|
||||
| tst.js:77:18:77:28 | { p: true } |
|
||||
| tst.js:82:20:82:21 | {} |
|
||||
@@ -0,0 +1,4 @@
|
||||
import javascript
|
||||
import semmle.javascript.dataflow.LocalObjects
|
||||
|
||||
select any(LocalObject n)
|
||||
@@ -0,0 +1,22 @@
|
||||
| method-calls.js:2:11:7:2 | {\\n\\t\\tm1: ... }; }\\n\\t} | m1 |
|
||||
| method-calls.js:2:11:7:2 | {\\n\\t\\tm1: ... }; }\\n\\t} | m2 |
|
||||
| method-calls.js:2:11:7:2 | {\\n\\t\\tm1: ... }; }\\n\\t} | m3 |
|
||||
| method-calls.js:2:11:7:2 | {\\n\\t\\tm1: ... }; }\\n\\t} | m4 |
|
||||
| method-calls.js:25:11:25:17 | {m: f1} | m |
|
||||
| method-calls.js:29:11:29:17 | {m: f2} | m |
|
||||
| method-calls.js:34:12:34:18 | {m: f3} | m |
|
||||
| method-calls.js:38:15:38:21 | {m: f4} | m |
|
||||
| method-calls.js:46:16:46:28 | {m: () => 42} | m |
|
||||
| method-calls.js:50:17:50:29 | {m: () => 42} | m |
|
||||
| method-calls.js:54:16:54:28 | {m: () => 42} | m |
|
||||
| method-calls.js:57:16:57:28 | {m: () => 42} | m |
|
||||
| tst.js:4:18:4:36 | { f: function(){} } | f |
|
||||
| tst.js:38:18:38:26 | { p: 42 } | p |
|
||||
| tst.js:42:18:42:33 | { p: 42, q: 42 } | p |
|
||||
| tst.js:42:18:42:33 | { p: 42, q: 42 } | q |
|
||||
| tst.js:56:20:56:35 | { p: 42, p: 42 } | p |
|
||||
| tst.js:59:20:59:28 | { p: 42 } | p |
|
||||
| tst.js:63:20:63:28 | { p: 42 } | p |
|
||||
| tst.js:68:19:68:27 | { p: 42 } | p |
|
||||
| tst.js:76:18:76:26 | { p: 42 } | p |
|
||||
| tst.js:77:18:77:28 | { p: true } | p |
|
||||
@@ -0,0 +1,6 @@
|
||||
import javascript
|
||||
import semmle.javascript.dataflow.LocalObjects
|
||||
|
||||
from LocalObject src, string name
|
||||
where src.hasOwnProperty(name)
|
||||
select src, name
|
||||
@@ -0,0 +1,12 @@
|
||||
| method-calls.js:8:2:8:8 | o1.m1() | object |
|
||||
| method-calls.js:9:2:9:8 | o1.m2() | object |
|
||||
| method-calls.js:12:2:12:7 | o.m3() | boolean, class, date, function, null, number, object, regular expression,string or undefined |
|
||||
| method-calls.js:21:2:21:7 | o2.m() | boolean, class, date, function, null, number, object, regular expression,string or undefined |
|
||||
| method-calls.js:25:11:25:21 | {m: f1}.m() | object |
|
||||
| method-calls.js:30:11:30:16 | o2.m() | object |
|
||||
| method-calls.js:34:11:34:23 | ({m: f3}).m() | object |
|
||||
| method-calls.js:41:11:41:16 | o4.m() | boolean, class, date, function, null, number, object, regular expression,string or undefined |
|
||||
| method-calls.js:47:12:47:16 | o.m() | boolean, class, date, function, null, number, object, regular expression,string or undefined |
|
||||
| method-calls.js:51:12:51:16 | o.m() | boolean, class, date, function, null, number, object, regular expression,string or undefined |
|
||||
| method-calls.js:55:12:55:16 | o.m() | boolean, class, date, function, null, number, object, regular expression,string or undefined |
|
||||
| method-calls.js:60:12:60:16 | o.m() | boolean, class, date, function, null, number, object, regular expression,string or undefined |
|
||||
@@ -0,0 +1,4 @@
|
||||
import javascript
|
||||
|
||||
from DataFlow::MethodCallNode call
|
||||
select call, call.analyze().ppTypes()
|
||||
@@ -0,0 +1,4 @@
|
||||
| method-calls.js:25:11:25:21 | {m: f1}.m() | method-calls.js:26:2:26:3 | v1 | method-calls.js:24:23:24:24 | object literal |
|
||||
| method-calls.js:30:11:30:16 | o2.m() | method-calls.js:31:2:31:3 | v2 | method-calls.js:28:23:28:24 | object literal |
|
||||
| method-calls.js:34:11:34:23 | ({m: f3}).m() | method-calls.js:35:2:35:3 | v3 | method-calls.js:33:23:33:24 | object literal |
|
||||
| method-calls.js:41:11:41:16 | o4.m() | method-calls.js:42:2:42:3 | v4 | file://:0:0:0:0 | indefinite value (call) |
|
||||
@@ -0,0 +1,5 @@
|
||||
import javascript
|
||||
|
||||
from DataFlow::MethodCallNode call, DataFlow::Node use
|
||||
where call.flowsTo(use) and use != call and not exists(use.getASuccessor())
|
||||
select call, use, use.analyze().getAValue()
|
||||
@@ -0,0 +1,63 @@
|
||||
(function() {
|
||||
var o1 = {
|
||||
m1: function(){ return {}; },
|
||||
m2: function(){ return {}; },
|
||||
m3: function(){ return {}; },
|
||||
m4: function(){ return {}; }
|
||||
};
|
||||
o1.m1(); // analyzed precisely
|
||||
o1.m2(); // analyzed precisely
|
||||
unknown(o1.m2);
|
||||
var o = unknown? o1: {};
|
||||
o.m3(); // not analyzed precisely: `o1` is not the only receiver.
|
||||
var m4 = o.m4;
|
||||
m4(); // (not a method call)
|
||||
|
||||
var o2 = {};
|
||||
o2.m = function() { return {}; };
|
||||
// not analyzed precisely: `m` may be in the prototype since `m`
|
||||
// is not in the initializer, and we do not attempt to reason flow
|
||||
// sensitively beyond that at the moment
|
||||
o2.m();
|
||||
});
|
||||
(function(){
|
||||
function f1(){return {};}
|
||||
var v1 = {m: f1}.m(); // analyzed precisely
|
||||
v1 === true;
|
||||
|
||||
function f2(){return {};}
|
||||
var o2 = {m: f2};
|
||||
var v2 = o2.m(); // analyzed precisely
|
||||
v2 === true;
|
||||
|
||||
function f3(){return {};}
|
||||
var v3 = ({m: f3}).m(); // analyzed precisely
|
||||
v3 === true;
|
||||
|
||||
function f4(){return {};}
|
||||
var { o4 } = {m: f4};
|
||||
// not analyzed precisely: o4 is from a destructuring assignment
|
||||
// (and it is even `undefined` in this case)
|
||||
var v4 = o4.m();
|
||||
v4 === true;
|
||||
});
|
||||
|
||||
(function(){
|
||||
(function(o = {m: () => 42}){
|
||||
var v1 = o.m(); // not analyzed precisely: `o` may be `unknown`
|
||||
})(unknown);
|
||||
|
||||
function f(o = {m: () => 42}){
|
||||
var v2 = o.m(); // not analyzed precisely: `o` may be `unknown`
|
||||
};
|
||||
f(unknown);
|
||||
(function(o = {m: () => 42}){
|
||||
var v3 = o.m(); // not analyzed precisely: `o.m` may be `unknown`
|
||||
})({m: unknown});
|
||||
(function(o = {m: () => 42}){
|
||||
// not analyzed precisely: we only support unique receivers at
|
||||
// the moment
|
||||
var v4 = o.m();
|
||||
})({m: () => true});
|
||||
|
||||
});
|
||||
93
javascript/ql/test/library-tests/LocalObjects/tst.js
Normal file
93
javascript/ql/test/library-tests/LocalObjects/tst.js
Normal file
@@ -0,0 +1,93 @@
|
||||
(function capturedSource(){
|
||||
|
||||
let captured1 = {};
|
||||
let captured2 = { f: function(){} };
|
||||
let captured3 = { [unknown]: 42 };
|
||||
|
||||
unknown({});
|
||||
|
||||
function known(){}
|
||||
known({});
|
||||
|
||||
function known_escaping(e){unknown(e)}
|
||||
known_escaping({});
|
||||
|
||||
(function(){return {}});
|
||||
|
||||
(function(){throw {}});
|
||||
|
||||
global = {};
|
||||
|
||||
this.p = {};
|
||||
|
||||
let local_in_with;
|
||||
with (unknown) {
|
||||
local_in_with = {};
|
||||
}
|
||||
|
||||
with({}){}
|
||||
|
||||
({ m: function(){ this; } });
|
||||
({ m: unknown });
|
||||
let indirectlyUnknown = unknown? unknown: function(){};
|
||||
({ m: indirectlyUnknown });
|
||||
});
|
||||
|
||||
(function capturedProperty(){
|
||||
|
||||
let captured1 = { p: 42 };
|
||||
captured1.p;
|
||||
captured1.p;
|
||||
|
||||
let captured2 = { p: 42, q: 42 };
|
||||
captured2.p;
|
||||
captured2.p;
|
||||
captured2.q = 42;
|
||||
captured2 = 42;
|
||||
|
||||
let nonObject = function(){}
|
||||
nonObject.p = 42;
|
||||
nonObject.p;
|
||||
|
||||
let nonInitializer = {};
|
||||
nonInitializer.p = 42;
|
||||
nonInitializer.p;
|
||||
|
||||
let overridden1 = { p: 42, p: 42 };
|
||||
overridden1.p;
|
||||
|
||||
let overridden2 = { p: 42 };
|
||||
overridden2.p = 42;
|
||||
overridden2.p;
|
||||
|
||||
let overridden3 = { p: 42 };
|
||||
overridden3[x] = 42;
|
||||
overridden3.p;
|
||||
|
||||
function f(o) {
|
||||
let captured3 = { p: 42 };
|
||||
o = o || captured3;
|
||||
o.p;
|
||||
}
|
||||
|
||||
let captured4 = { };
|
||||
captured4.p;
|
||||
|
||||
let captured5 = { p: 42 },
|
||||
captured6 = { p: true };
|
||||
(unknown? captured5: captured6).p; // could support this with a bit of extra work
|
||||
|
||||
(function(semiCaptured7){
|
||||
if(unknown)
|
||||
semiCaptured7 = {};
|
||||
semiCaptured7.p = 42;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
(function (){
|
||||
let bound = {};
|
||||
bound::unknown();
|
||||
});
|
||||
|
||||
// semmle-extractor-options: --experimental
|
||||
6
javascript/ql/test/library-tests/LocalObjects/tst.ts
Normal file
6
javascript/ql/test/library-tests/LocalObjects/tst.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
class C {
|
||||
constructor(
|
||||
private readonly F: { timeout: number } = { timeout: 1500 }
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,10 @@
|
||||
| classes.ts:4:3:4:24 | instanc ... foo(); |
|
||||
| classes.ts:8:3:8:39 | constru ... eld) {} |
|
||||
| classes.ts:8:15:8:35 | public ... erField |
|
||||
| classes.ts:12:5:12:68 | constru ... + 42; } |
|
||||
| classes.ts:12:17:12:37 | public ... erField |
|
||||
| classes.ts:16:5:16:46 | constru ... {}) {} |
|
||||
| classes.ts:16:17:16:37 | public ... erField |
|
||||
| tst.js:3:5:3:8 | x: 4 |
|
||||
| tst.js:4:5:6:5 | func: f ... ;\\n } |
|
||||
| tst.js:7:5:9:5 | f() {\\n ... ;\\n } |
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
| classes.ts:4:3:4:24 | instanc ... foo(); | classes.ts:3:21:3:20 | this |
|
||||
| classes.ts:8:15:8:35 | public ... erField | classes.ts:8:3:8:2 | this |
|
||||
| classes.ts:12:17:12:37 | public ... erField | classes.ts:12:5:12:4 | this |
|
||||
| classes.ts:16:17:16:37 | public ... erField | classes.ts:16:5:16:4 | this |
|
||||
| tst.js:3:5:3:8 | x: 4 | tst.js:2:11:10:1 | {\\n x ... }\\n} |
|
||||
| tst.js:4:5:6:5 | func: f ... ;\\n } | tst.js:2:11:10:1 | {\\n x ... }\\n} |
|
||||
| tst.js:7:5:9:5 | f() {\\n ... ;\\n } | tst.js:2:11:10:1 | {\\n x ... }\\n} |
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
| classes.ts:4:3:4:24 | instanc ... foo(); | instanceField |
|
||||
| classes.ts:8:3:8:39 | constru ... eld) {} | constructor |
|
||||
| classes.ts:8:15:8:35 | public ... erField | parameterField |
|
||||
| classes.ts:12:5:12:68 | constru ... + 42; } | constructor |
|
||||
| classes.ts:12:17:12:37 | public ... erField | parameterField |
|
||||
| classes.ts:16:5:16:46 | constru ... {}) {} | constructor |
|
||||
| classes.ts:16:17:16:37 | public ... erField | parameterField |
|
||||
| tst.js:3:5:3:8 | x: 4 | x |
|
||||
| tst.js:4:5:6:5 | func: f ... ;\\n } | func |
|
||||
| tst.js:7:5:9:5 | f() {\\n ... ;\\n } | f |
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
| classes.ts:4:3:4:24 | instanc ... foo(); | classes.ts:4:19:4:23 | foo() |
|
||||
| classes.ts:8:3:8:39 | constru ... eld) {} | classes.ts:8:3:8:39 | constru ... eld) {} |
|
||||
| classes.ts:8:15:8:35 | public ... erField | classes.ts:8:22:8:35 | parameterField |
|
||||
| classes.ts:12:5:12:68 | constru ... + 42; } | classes.ts:12:5:12:68 | constru ... + 42; } |
|
||||
| classes.ts:12:17:12:37 | public ... erField | classes.ts:12:24:12:37 | parameterField |
|
||||
| classes.ts:16:5:16:46 | constru ... {}) {} | classes.ts:16:5:16:46 | constru ... {}) {} |
|
||||
| classes.ts:16:17:16:37 | public ... erField | classes.ts:16:24:16:37 | parameterField |
|
||||
| classes.ts:16:17:16:37 | public ... erField | classes.ts:16:41:16:42 | {} |
|
||||
| tst.js:3:5:3:8 | x: 4 | tst.js:3:8:3:8 | 4 |
|
||||
| tst.js:4:5:6:5 | func: f ... ;\\n } | tst.js:4:11:6:5 | functio ... ;\\n } |
|
||||
| tst.js:7:5:9:5 | f() {\\n ... ;\\n } | tst.js:7:6:9:5 | () {\\n ... ;\\n } |
|
||||
|
||||
@@ -7,3 +7,11 @@ class InstanceField {
|
||||
class ParameterField {
|
||||
constructor(public parameterField) {}
|
||||
}
|
||||
|
||||
class ParameterFieldInit {
|
||||
constructor(public parameterField = {}) { parameterField + 42; }
|
||||
}
|
||||
|
||||
class ParameterFieldInitUnused {
|
||||
constructor(public parameterField = {}) {}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
| classes.ts:3:21:3:20 | this | classes.ts:4:3:4:24 | instanc ... foo(); |
|
||||
| classes.ts:8:3:8:2 | this | classes.ts:8:15:8:35 | public ... erField |
|
||||
| classes.ts:12:5:12:4 | this | classes.ts:12:17:12:37 | public ... erField |
|
||||
| classes.ts:16:5:16:4 | this | classes.ts:16:17:16:37 | public ... erField |
|
||||
| tst.js:1:1:1:0 | this | tst.js:23:15:23:29 | this.someMethod |
|
||||
| tst.js:1:1:1:0 | this | tst.js:24:36:24:45 | this.state |
|
||||
| tst.js:2:11:10:1 | {\\n x ... }\\n} | tst.js:3:5:3:8 | x: 4 |
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
| classes.ts:3:21:3:20 | this | instanceField | classes.ts:4:3:4:24 | instanc ... foo(); |
|
||||
| classes.ts:8:3:8:2 | this | parameterField | classes.ts:8:15:8:35 | public ... erField |
|
||||
| classes.ts:12:5:12:4 | this | parameterField | classes.ts:12:17:12:37 | public ... erField |
|
||||
| classes.ts:16:5:16:4 | this | parameterField | classes.ts:16:17:16:37 | public ... erField |
|
||||
| tst.js:1:1:1:0 | this | someMethod | tst.js:23:15:23:29 | this.someMethod |
|
||||
| tst.js:1:1:1:0 | this | state | tst.js:24:36:24:45 | this.state |
|
||||
| tst.js:2:11:10:1 | {\\n x ... }\\n} | f | tst.js:7:5:9:5 | f() {\\n ... ;\\n } |
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
| classes.ts:3:21:3:20 | this | instanceField | classes.ts:4:19:4:23 | foo() |
|
||||
| classes.ts:8:3:8:2 | this | parameterField | classes.ts:8:22:8:35 | parameterField |
|
||||
| classes.ts:12:5:12:4 | this | parameterField | classes.ts:12:24:12:37 | parameterField |
|
||||
| classes.ts:12:5:12:4 | this | parameterField | classes.ts:12:41:12:42 | {} |
|
||||
| classes.ts:16:5:16:4 | this | parameterField | classes.ts:16:24:16:37 | parameterField |
|
||||
| classes.ts:16:5:16:4 | this | parameterField | classes.ts:16:41:16:42 | {} |
|
||||
| tst.js:2:11:10:1 | {\\n x ... }\\n} | f | tst.js:7:6:9:5 | () {\\n ... ;\\n } |
|
||||
| tst.js:2:11:10:1 | {\\n x ... }\\n} | func | tst.js:4:11:6:5 | functio ... ;\\n } |
|
||||
| tst.js:12:1:19:1 | class C ... ;\\n }\\n} | func | tst.js:13:14:15:3 | (x) {\\n ... x);\\n } |
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
| classes.ts:3:21:3:20 | this | classes.ts:4:3:4:24 | instanc ... foo(); |
|
||||
| classes.ts:8:3:8:2 | this | classes.ts:8:15:8:35 | public ... erField |
|
||||
| classes.ts:12:5:12:4 | this | classes.ts:12:17:12:37 | public ... erField |
|
||||
| classes.ts:16:5:16:4 | this | classes.ts:16:17:16:37 | public ... erField |
|
||||
| tst.js:2:11:10:1 | {\\n x ... }\\n} | tst.js:3:5:3:8 | x: 4 |
|
||||
| tst.js:2:11:10:1 | {\\n x ... }\\n} | tst.js:4:5:6:5 | func: f ... ;\\n } |
|
||||
| tst.js:2:11:10:1 | {\\n x ... }\\n} | tst.js:7:5:9:5 | f() {\\n ... ;\\n } |
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
| classes.ts:3:21:3:20 | this | instanceField | classes.ts:4:3:4:24 | instanc ... foo(); |
|
||||
| classes.ts:8:3:8:2 | this | parameterField | classes.ts:8:15:8:35 | public ... erField |
|
||||
| classes.ts:12:5:12:4 | this | parameterField | classes.ts:12:17:12:37 | public ... erField |
|
||||
| classes.ts:16:5:16:4 | this | parameterField | classes.ts:16:17:16:37 | public ... erField |
|
||||
| tst.js:2:11:10:1 | {\\n x ... }\\n} | f | tst.js:7:5:9:5 | f() {\\n ... ;\\n } |
|
||||
| tst.js:2:11:10:1 | {\\n x ... }\\n} | func | tst.js:4:5:6:5 | func: f ... ;\\n } |
|
||||
| tst.js:2:11:10:1 | {\\n x ... }\\n} | x | tst.js:3:5:3:8 | x: 4 |
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
| classes.ts:3:21:3:20 | this | instanceField | classes.ts:4:19:4:23 | foo() |
|
||||
| classes.ts:8:3:8:2 | this | parameterField | classes.ts:8:22:8:35 | parameterField |
|
||||
| classes.ts:12:5:12:4 | this | parameterField | classes.ts:12:24:12:37 | parameterField |
|
||||
| classes.ts:16:5:16:4 | this | parameterField | classes.ts:16:24:16:37 | parameterField |
|
||||
| classes.ts:16:5:16:4 | this | parameterField | classes.ts:16:41:16:42 | {} |
|
||||
| tst.js:2:11:10:1 | {\\n x ... }\\n} | f | tst.js:7:6:9:5 | () {\\n ... ;\\n } |
|
||||
| tst.js:2:11:10:1 | {\\n x ... }\\n} | func | tst.js:4:11:6:5 | functio ... ;\\n } |
|
||||
| tst.js:2:11:10:1 | {\\n x ... }\\n} | x | tst.js:3:8:3:8 | 4 |
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
| 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 | 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 |
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
| 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:18:5:18:11 | o1.f3() | 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 |
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
| tst.js:4:9:4:19 | unused1: 42 | Unused property unused1. |
|
||||
| tst.js:19:5:19:15 | unused9: 42 | Unused property unused9. |
|
||||
| tst.js:26:13:26:24 | unused11: 42 | Unused property unused11. |
|
||||
| tst.js:31:13:31:35 | used12_ ... lly: 42 | Unused property used12_butNotReally. |
|
||||
| tst.js:32:13:32:24 | unused12: 42 | Unused property unused12. |
|
||||
| tst.js:52:3:52:14 | unused14: 42 | Unused property unused14. |
|
||||
| tst.js:54:2:54:20 | captured14.unused14 | Unused property unused14. |
|
||||
| tst.js:55:2:55:20 | captured14.unused14 | Unused property unused14. |
|
||||
| tst.ts:24:21:24:25 | p: 42 | Unused property p. |
|
||||
@@ -0,0 +1 @@
|
||||
Declarations/UnusedProperty.ql
|
||||
@@ -0,0 +1,83 @@
|
||||
(function(){
|
||||
var captured1 = {
|
||||
used1: 42,
|
||||
unused1: 42
|
||||
};
|
||||
captured1.used1;
|
||||
|
||||
var unused2 = {
|
||||
unused2a: 42,
|
||||
unused2b: 42
|
||||
};
|
||||
|
||||
for (x.p in { used3: 42 });
|
||||
for (x.p of { used4: 42 });
|
||||
42 in { used5: 42 };
|
||||
f(...{used6: 42});
|
||||
[...{used7: 42}];
|
||||
({...{used8: 42}});
|
||||
({ unused9: 42 }) + "";
|
||||
({ used10: 42 }).hasOwnProperty;
|
||||
({ used10: 42 }).propertyIsEnumerable;
|
||||
|
||||
(function(){
|
||||
var captured11 = {
|
||||
used11: 42,
|
||||
unused11: 42
|
||||
};
|
||||
captured11.used11;
|
||||
|
||||
var captured12 = {
|
||||
used12_butNotReally: 42,
|
||||
unused12: 42
|
||||
};
|
||||
|
||||
throw x;
|
||||
|
||||
captured12.used12_butNotReally;
|
||||
|
||||
var captured13 = {
|
||||
used13: 42,
|
||||
unused13: 42
|
||||
};
|
||||
captured13.used13;
|
||||
});
|
||||
(function(options){
|
||||
if(unknown)
|
||||
options = {};
|
||||
options.output = 42;
|
||||
});
|
||||
|
||||
var captured14 = {
|
||||
unused14: 42
|
||||
};
|
||||
captured14.unused14 = 42;
|
||||
captured14.unused14 = 42;
|
||||
|
||||
|
||||
var captured15 = {
|
||||
semiUnused15: 42
|
||||
};
|
||||
captured15.semiUnused15 = 42;
|
||||
captured15.semiUnused15;
|
||||
});
|
||||
(function(unusedParam = {unusedProp: 42}){
|
||||
|
||||
});
|
||||
(function(){
|
||||
var unusedObj = {
|
||||
unusedProp: 42
|
||||
};
|
||||
});
|
||||
(function(){
|
||||
var unusedSpecials = {
|
||||
toString: function(){},
|
||||
valueOf: function(){},
|
||||
'@@iterator': function(){}
|
||||
};
|
||||
unusedSpecials.foo;
|
||||
});
|
||||
|
||||
(function(){
|
||||
({ unusedProp: 42 }, 42);
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
(function(){
|
||||
var o1: { p: int, q: int } = { p: 42, q: 42 };
|
||||
o1.q;
|
||||
|
||||
var o2 = <{ p: int, q: int }>{ p: 42, q: 42 };
|
||||
o2.q;
|
||||
|
||||
var o3: { p: int, q: int } = f();
|
||||
o3 = o3 || { p: 42, q: 42 };
|
||||
o3.q;
|
||||
|
||||
});
|
||||
|
||||
class C {
|
||||
private o: { p: int, q: int };
|
||||
|
||||
constructor() {
|
||||
this.o = { p: 42, q: 42 };
|
||||
this.o.q;
|
||||
}
|
||||
}
|
||||
|
||||
(function(){
|
||||
var o1: any = { p: 42, q: 42 };
|
||||
o1.q;
|
||||
var o2: any = { p: 42, q: 42 };
|
||||
var o3: { p: int, q: int } = o2;
|
||||
})
|
||||
@@ -0,0 +1,4 @@
|
||||
(function(){
|
||||
throw 42;
|
||||
var x = 42;
|
||||
});
|
||||
Reference in New Issue
Block a user