Merge master into next.

master as of dc3c5a684c
Version numbers resolved in favour of `next`.
C++ expected output file updated to accept test output.
This commit is contained in:
Aditya Sharad
2018-10-30 17:11:17 +00:00
128 changed files with 2600 additions and 1072 deletions

View File

@@ -25,25 +25,6 @@ predicate deadStoreOfLocal(VarDef vd, PurelyLocalVariable v) {
not exists (SsaExplicitDefinition ssa | ssa.defines(vd, v))
}
/**
* Holds if `e` is an expression evaluating to `null` or `undefined`.
*
* This includes not only direct references to `null` and `undefined`, but
* also `void` expressions and assignments of the form `x = rhs`, where `rhs`
* is itself an expression evaluating to `null` or `undefined`.
*/
predicate isNullOrUndef(Expr e) {
exists (Expr inner |
inner = e.stripParens() |
// `null` or `undefined`
inner instanceof NullLiteral or
inner.(VarAccess).getName() = "undefined" or
inner instanceof VoidExpr or
// recursive case to catch multi-assignments of the form `x = y = null`
isNullOrUndef(inner.(AssignExpr).getRhs())
)
}
/**
* Holds if `e` is an expression that may be used as a default initial value,
* such as `0` or `-1`, or an empty object or array literal.
@@ -57,9 +38,7 @@ predicate isDefaultInit(Expr e) {
// initialising to an empty array or object literal, even if unnecessary,
// can convey useful type information to the reader
e.(ArrayExpr).getSize() = 0 or
e.(ObjectExpr).getNumProperty() = 0 or
// recursive case
isDefaultInit(e.(AssignExpr).getRhs())
e.(ObjectExpr).getNumProperty() = 0
}
from VarDef dead, PurelyLocalVariable v // captured variables may be read by closures, so don't flag them
@@ -77,9 +56,9 @@ where deadStoreOfLocal(dead, v) and
not fd = outer.getBody().(BlockStmt).getAStmt()
) and
// don't flag overwrites with `null` or `undefined`
not isNullOrUndef(dead.getSource()) and
not SyntacticConstants::isNullOrUndefined(dead.getSource()) and
// don't flag default inits that are later overwritten
not (isDefaultInit(dead.getSource()) and dead.isOverwritten(v)) and
not (isDefaultInit(dead.getSource().(Expr).getUnderlyingValue()) and dead.isOverwritten(v)) and
// don't flag assignments in externs
not dead.(ASTNode).inExternsFile() and
// don't flag exported variables

View File

@@ -56,10 +56,17 @@ predicate isPropertyFilter(UnusedLocal v) {
predicate isReactImportForJSX(UnusedLocal v) {
exists (ImportSpecifier is |
is.getLocal() = v.getADeclaration() and
exists (JSXNode jsx | jsx.getTopLevel() = is.getTopLevel()) |
v.getName() = "React" or
// also accept legacy `@jsx` pragmas
exists (JSXNode jsx | jsx.getTopLevel() = is.getTopLevel())
|
v.getName() = "React"
or
// legacy `@jsx` pragmas
exists (JSXPragma p | p.getTopLevel() = is.getTopLevel() | p.getDOMName() = v.getName())
or
// JSX pragma from a .babelrc file
exists (Babel::TransformReactJsxConfig plugin |
plugin.appliesTo(is.getTopLevel()) and
plugin.getJsxFactoryVariableName() = v.getName())
)
}

View File

@@ -14,4 +14,4 @@ from Directive d
where not d instanceof KnownDirective and
// but exclude attribute top-levels: `<a href="javascript:'some-attribute-string'">`
not (d.getParent() instanceof CodeInAttribute)
select d, "Unknown directive: '" + d.getDirectiveText() + "'."
select d, "Unknown directive: '" + truncate(d.getDirectiveText(), 20, " ... (truncated)") + "'."

View File

@@ -67,7 +67,27 @@ private cached module Internal {
}
cached predicate defAt(BasicBlock bb, int i, Variable v, VarDef d) {
v = d.getAVariable() and bbIndex(bb, d, i)
exists (VarRef lhs |
lhs = d.getTarget().(BindingPattern).getABindingVarRef() and
v = lhs.getVariable() |
lhs = d.getTarget() and
bbIndex(bb, d, i)
or
exists (PropertyPattern pp |
lhs = pp.getValuePattern() and
bbIndex(bb, pp, i)
)
or
exists (ObjectPattern op |
lhs = op.getRest() and
bbIndex(bb, lhs, i)
)
or
exists (ArrayPattern ap |
lhs = ap.getAnElement() and
bbIndex(bb, lhs, i)
)
)
}
cached predicate reachableBB(BasicBlock bb) {

View File

@@ -127,14 +127,32 @@ module SyntacticConstants {
class WrappedConstant extends SyntacticConstant {
WrappedConstant() {
stripParens() instanceof SyntacticConstant or
this.(SeqExpr).getLastOperand() instanceof SyntacticConstant or
this.(TypeAssertion).getExpression() instanceof SyntacticConstant or
this.(Assignment).getRhs() instanceof SyntacticConstant
getUnderlyingValue() instanceof SyntacticConstant
}
}
/**
* Holds if `c` evaluates to `undefined`.
*/
predicate isUndefined(SyntacticConstant c) {
c.getUnderlyingValue() instanceof UndefinedConstant
}
/**
* Holds if `c` evaluates to `null`.
*/
predicate isNull(SyntacticConstant c) {
c.getUnderlyingValue() instanceof NullConstant
}
/**
* Holds if `c` evaluates to `null` or `undefined`.
*/
predicate isNullOrUndefined(SyntacticConstant c) {
isUndefined(c) or isNull(c)
}
}
/**

View File

@@ -98,7 +98,8 @@ private predicate lvalAux(Expr l, ControlFlowNode def) {
exists (ArrayPattern ap | lvalAux(ap, def) | l = ap.getAnElement().stripParens())
or
exists (ObjectPattern op | lvalAux(op, def) |
l = op.getAPropertyPattern().getValuePattern().stripParens()
l = op.getAPropertyPattern().getValuePattern().stripParens() or
l = op.getRest().stripParens()
)
}

View File

@@ -45,8 +45,45 @@ class ExprOrType extends @exprortype, Documentable {
)
}
/** Gets this expression or type, with any surrounding parentheses removed. */
/**
* Gets this expression or type, with any surrounding parentheses removed.
*
* Also see `getUnderlyingValue` and `getUnderlyingReference`.
*/
ExprOrType stripParens() { result = this }
/**
* Gets the innermost reference that this expression evaluates to, if any.
*
* Examples:
*
* - a variable or property access: the access itself.
* - a parenthesized expression `(e)`: the underlying reference of `e`.
* - a TypeScript type assertion `e as T`: the underlying reference of `e`.
*
* Also see `getUnderlyingValue` and `stripParens`.
*/
Expr getUnderlyingReference() {
none()
}
/**
* Gets the innermost expression that this expression evaluates to.
*
* Examples:
*
* - a parenthesised expression `(e)`: the underlying value of `e`.
* - a sequence expression `e1, e2`: the underlying value of `e2`.
* - an assignment expression `v = e`: the underlying value of `e`.
* - a TypeScript type assertion `e as T`: the underlying value of `e`.
* - any other expression: the expression itself.
*
* Also see `getUnderlyingReference` and `stripParens`.
*/
Expr getUnderlyingValue() {
result = this
}
}
/** An expression. */
@@ -210,6 +247,15 @@ class ParExpr extends @parexpr, Expr {
override predicate isImpure() {
getExpression().isImpure()
}
override Expr getUnderlyingValue() {
result = getExpression().getUnderlyingValue()
}
override Expr getUnderlyingReference() {
result = getExpression().getUnderlyingReference()
}
}
/** A `null` literal. */
@@ -617,6 +663,11 @@ class SeqExpr extends @seqexpr, Expr {
override string getStringValue() {
result = getLastOperand().getStringValue()
}
override Expr getUnderlyingValue() {
result = getLastOperand().getUnderlyingValue()
}
}
/** A conditional expression. */
@@ -862,6 +913,11 @@ class PropAccess extends @propaccess, Expr {
override ControlFlowNode getFirstControlFlowNode() {
result = getBase().getFirstControlFlowNode()
}
override Expr getUnderlyingReference() {
result = this
}
}
/** A dot expression. */
@@ -1284,10 +1340,17 @@ class Assignment extends @assignment, Expr {
override ControlFlowNode getFirstControlFlowNode() {
result = getLhs().getFirstControlFlowNode()
}
}
/** A simple assignment expression. */
class AssignExpr extends @assignexpr, Assignment {}
class AssignExpr extends @assignexpr, Assignment {
override Expr getUnderlyingValue() {
result = getRhs().getUnderlyingValue()
}
}
/** A compound assign expression. */
abstract class CompoundAssignExpr extends Assignment {}

View File

@@ -122,10 +122,7 @@ private cached module Internal {
}
or TPhi(ReachableJoinBlock bb, SsaSourceVariable v) {
liveAtEntry(bb, v) and
exists (ReachableBasicBlock defbb, SsaDefinition def |
def.definesAt(defbb, _, v) and
bb.inDominanceFrontierOf(defbb)
)
inDefDominanceFrontier(bb, v)
}
or TRefinement(ReachableBasicBlock bb, int i, GuardControlFlowNode guard, SsaSourceVariable v) {
bb.getNode(i) = guard and
@@ -133,6 +130,17 @@ private cached module Internal {
liveAtEntry(bb, v)
}
/**
* Holds if `bb` is in the dominance frontier of a block containing a definition of `v`.
*/
pragma[noinline]
private predicate inDefDominanceFrontier(ReachableJoinBlock bb, SsaSourceVariable v) {
exists (ReachableBasicBlock defbb, SsaDefinition def |
def.definesAt(defbb, _, v) and
bb.inDominanceFrontierOf(defbb)
)
}
/**
* Holds if `v` is a captured variable which is declared in `declContainer` and read in
* `useContainer`.
@@ -216,6 +224,13 @@ private cached module Internal {
ref(bb, i, v, tp)
}
/**
* Gets the maximum rank among all references to `v` in basic block `bb`.
*/
private int maxRefRank(ReachableBasicBlock bb, SsaSourceVariable v) {
result = max(refRank(bb, _, v, _))
}
/**
* Holds if variable `v` is live after the `i`th node of basic block `bb`, where
* `i` is the index of a node that may assign or capture `v`.
@@ -230,8 +245,8 @@ private cached module Internal {
or
// this is the last reference to `v` inside `bb`, but `v` is live at entry
// to a successor basic block of `bb`
r = max(refRank(bb, _, v, _)) and
liveAtEntry(bb.getASuccessor(), v)
r = maxRefRank(bb, v) and
liveAtSuccEntry(bb, v)
)
}
@@ -248,6 +263,13 @@ private cached module Internal {
// there is no reference to `v` inside `bb`, but `v` is live at entry
// to a successor basic block of `bb`
not exists(refRank(bb, _, v, _)) and
liveAtSuccEntry(bb, v)
}
/**
* Holds if `v` is live at the beginning of any successor of basic block `bb`.
*/
private predicate liveAtSuccEntry(ReachableBasicBlock bb, SsaSourceVariable v) {
liveAtEntry(bb.getASuccessor(), v)
}
@@ -311,25 +333,32 @@ private cached module Internal {
)
}
/**
* Gets an SSA definition of `v` that reaches the end of the immediate dominator of `bb`.
*/
pragma[noinline]
private SsaDefinition getDefReachingEndOfImmediateDominator(ReachableBasicBlock bb, SsaSourceVariable v) {
result = getDefReachingEndOf(bb.getImmediateDominator(), v)
}
/**
* Gets an SSA definition of `v` that reaches the end of basic block `bb`.
*/
cached SsaDefinition getDefReachingEndOf(ReachableBasicBlock bb, SsaSourceVariable v) {
bb.getASuccessor().localIsLiveAtEntry(v) and
(
exists (int lastRef | lastRef = max(int i | ssaRef(bb, i, v, _)) |
result = getLocalDefinition(bb, lastRef, v)
or
result.definesAt(bb, lastRef, v)
)
exists (int lastRef | lastRef = max(int i | ssaRef(bb, i, v, _)) |
result = getLocalDefinition(bb, lastRef, v)
or
/* In SSA form, the (unique) reaching definition of a use is the closest
* definition that dominates the use. If two definitions dominate a node
* then one must dominate the other, so we can find the reaching definition
* by following the idominance relation backwards. */
result = getDefReachingEndOf(bb.getImmediateDominator(), v) and
not exists (SsaDefinition ssa | ssa.definesAt(bb, _, v))
result.definesAt(bb, lastRef, v) and
liveAtSuccEntry(bb, v)
)
or
/* In SSA form, the (unique) reaching definition of a use is the closest
* definition that dominates the use. If two definitions dominate a node
* then one must dominate the other, so we can find the reaching definition
* by following the idominance relation backwards. */
result = getDefReachingEndOfImmediateDominator(bb, v) and
not exists (SsaDefinition ssa | ssa.definesAt(bb, _, v)) and
liveAtSuccEntry(bb, v)
}
/**

View File

@@ -1315,6 +1315,15 @@ class TypeAssertion extends Expr, @typeassertion {
override ControlFlowNode getFirstControlFlowNode() {
result = getExpression().getFirstControlFlowNode()
}
override Expr getUnderlyingValue() {
result = getExpression().getUnderlyingValue()
}
override Expr getUnderlyingReference() {
result = getExpression().getUnderlyingReference()
}
}
/**

View File

@@ -12,15 +12,25 @@ string capitalize(string s) {
result = s.charAt(0).toUpperCase() + s.suffix(1)
}
/**
* Gets the pluralization for `n` occurrences of `noun`.
*
* For example, the pluralization of `"function"` for `n = 2` is `"functions"`.
*/
/**
* Gets the pluralization for `n` occurrences of `noun`.
*
* For example, the pluralization of `"function"` for `n = 2` is `"functions"`.
*/
bindingset[noun, n]
string pluralize(string noun, int n) {
if n = 1 then
result = noun
else
result = noun + "s"
}
}
/**
* Gets `str` or a truncated version of `str` with `explanation` appended if its length exceeds `maxLength`.
*
* For example, the truncation of `"long_string"` for `maxLength = 5` and explanation `" ..."` is `"long_ ..."`.
*/
bindingset[str, maxLength, explanation]
string truncate(string str, int maxLength, string explanation) {
if str.length() > maxLength then result = str.prefix(maxLength) + explanation else result = str
}

View File

@@ -298,6 +298,11 @@ class VarRef extends @varref, Identifier, BindingPattern, LexicalRef {
override VarRef getABindingVarRef() { result = this }
override predicate isImpure() { none() }
override Expr getUnderlyingReference() {
result = this
}
}
/** An identifier that refers to a variable in a non-declaring position. */

View File

@@ -28,6 +28,62 @@ module Babel {
result.(JSONArray).getElementStringValue(0) = pluginName
)
}
/**
* Gets a file affected by this Babel configuration.
*/
Container getAContainerInScope() {
result = getFile().getParentContainer()
or
result = getAContainerInScope().getAChildContainer() and
// File-relative .babelrc search stops at any package.json or .babelrc file.
not result.getAChildContainer() = any(PackageJSON pkg).getFile() and
not result.getAChildContainer() = any(Config pkg).getFile()
}
/**
* Holds if this configuration applies to `tl`.
*/
predicate appliesTo(TopLevel tl) {
tl.getFile() = getAContainerInScope()
}
}
/**
* Configuration object for a Babel plugin.
*/
class Plugin extends JSONValue {
Config cfg;
string pluginName;
Plugin() {
this = cfg.getPluginConfig(pluginName)
}
/** Gets the name of the plugin being installed. */
string getPluginName() {
result = pluginName
}
/** Gets the enclosing Babel configuration object. */
Config getConfig() {
result = cfg
}
/** Gets the options value passed to the plugin, if any. */
JSONValue getOptions() {
result = this.(JSONArray).getElementValue(1)
}
/** Gets a named option from the option object, if present. */
JSONValue getOption(string name) {
result = getOptions().(JSONObject).getPropValue(name)
}
/** Holds if this plugin applies to `tl`. */
predicate appliesTo(TopLevel tl) {
cfg.appliesTo(tl)
}
}
/**
@@ -38,11 +94,9 @@ module Babel {
* each path is of the form `{ "rootPathPrefix": "...", "rootPathSuffix": "..." }` and explicitly
* specifies a mapping from a path prefix to a root.
*/
class RootImportConfig extends JSONArray {
Config cfg;
class RootImportConfig extends Plugin {
RootImportConfig() {
this = cfg.getPluginConfig("babel-plugin-root-import")
pluginName = "babel-plugin-root-import"
}
/**
@@ -62,15 +116,16 @@ module Babel {
*/
private JSONObject getARootPathSpec() {
// ["babel-plugin-root-import", <spec>]
result = getElementValue(1) and
result = getOptions() and
exists(result.getPropValue("rootPathSuffix"))
or
exists (JSONArray pathSpecs |
// ["babel-plugin-root-import", [ <spec>... ] ]
pathSpecs = getElementValue(1)
pathSpecs = getOptions()
or
// ["babel-plugin-root-import", { "paths": [ <spec> ... ] }]
pathSpecs = getElementValue(1).(JSONObject).getPropValue("paths") |
pathSpecs = getOption("paths")
|
result = pathSpecs.getElementValue(_)
)
}
@@ -95,20 +150,13 @@ module Babel {
Folder getFolder() {
result = getFile().getParentContainer()
}
/**
* Holds if this configuration applies to `tl`.
*/
predicate appliesTo(TopLevel tl) {
tl.getFile().getParentContainer+() = getFolder()
}
}
/**
* An import path expression that may be transformed by `babel-plugin-root-import`.
*/
private class BabelRootTransformedPathExpr extends PathExpr, Expr {
RootImportConfig cfg;
RootImportConfig plugin;
string rawPath;
string prefix;
string mappedPrefix;
@@ -116,16 +164,16 @@ module Babel {
BabelRootTransformedPathExpr() {
this instanceof PathExpr and
cfg.appliesTo(getTopLevel()) and
plugin.appliesTo(getTopLevel()) and
rawPath = getStringValue() and
prefix = rawPath.regexpCapture("(.)/(.*)", 1) and
suffix = rawPath.regexpCapture("(.)/(.*)", 2) and
mappedPrefix = cfg.getRoot(prefix)
mappedPrefix = plugin.getRoot(prefix)
}
/** Gets the configuration that applies to this path. */
RootImportConfig getConfig() {
result = cfg
RootImportConfig getPlugin() {
result = plugin
}
override string getValue() {
@@ -134,7 +182,7 @@ module Babel {
override Folder getSearchRoot(int priority) {
priority = 0 and
result = cfg.getFolder()
result = plugin.getFolder()
}
}
@@ -149,7 +197,24 @@ module Babel {
}
override Folder getARootFolder() {
result = pathExpr.getConfig().getFolder()
result = pathExpr.getPlugin().getFolder()
}
}
/**
* A configuration object for the `transform-react-jsx` plugin.
*
* The plugin option `{"pragma": xxx}` specifies a variable name used to instantiate
* JSX elements.
*/
class TransformReactJsxConfig extends Plugin {
TransformReactJsxConfig() {
pluginName = "transform-react-jsx"
}
/** Gets the name of the variable used to create JSX elements. */
string getJsxFactoryVariableName() {
result = getOption("pragma").(JSONString).getValue()
}
}
}

View File

@@ -579,20 +579,20 @@
| destructuring | 3 | o | 4 | p |
| destructuring | 4 | p | 5 | {\\n v ... = p;\\n} |
| destructuring | 5 | {\\n v ... = p;\\n} | 6 | var\\n ... } = o; |
| destructuring | 6 | var\\n ... } = o; | 7 | {\\n ... \\n } |
| destructuring | 6 | var\\n ... } = o; | 10 | o |
| destructuring | 7 | {\\n ... } = o | 11 | var\\n ... ] = p; |
| destructuring | 7 | {\\n ... \\n } | 8 | x |
| destructuring | 8 | x | 8 | x |
| destructuring | 8 | x | 9 | y |
| destructuring | 9 | y | 7 | {\\n ... } = o |
| destructuring | 9 | y | 9 | y |
| destructuring | 9 | y | 10 | o |
| destructuring | 10 | o | 7 | {\\n ... } = o |
| destructuring | 11 | var\\n ... ] = p; | 12 | [\\n ... \\n ] |
| destructuring | 10 | o | 7 | {\\n ... \\n } |
| destructuring | 11 | var\\n ... ] = p; | 16 | p |
| destructuring | 12 | [\\n ... ] = p | 17 | exit node of functio ... = p;\\n} |
| destructuring | 12 | [\\n ... \\n ] | 13 | a |
| destructuring | 13 | a | 15 | c |
| destructuring | 15 | c | 16 | p |
| destructuring | 16 | p | 12 | [\\n ... ] = p |
| destructuring | 15 | c | 12 | [\\n ... ] = p |
| destructuring | 16 | p | 12 | [\\n ... \\n ] |
| enum | 1 | 'value' | 1 | a = 'value' |
| enum | 1 | E | 1 | a |
| enum | 1 | a | 1 | 'value' |

View File

@@ -58,7 +58,6 @@
| tst.js:47:5:47:5 | 1 |
| tst.js:48:1:48:7 | x.p = 1 |
| tst.js:48:7:48:7 | 1 |
| tst.js:49:1:49:6 | x += 1 |
| tst.js:49:6:49:6 | 1 |
| tst.ts:1:13:1:21 | <number>1 |
| tst.ts:1:21:1:21 | 1 |

View File

@@ -59,6 +59,8 @@
| d.js:1:1:4:0 | exports object of module d |
| d.js:1:1:4:0 | module object of module d |
| d.js:1:18:3:1 | object literal |
| destructuring.js:1:1:4:1 | function f |
| destructuring.js:1:1:4:1 | instance of function f |
| e.js:1:1:6:0 | exports object of module e |
| e.js:1:1:6:0 | module object of module e |
| es2015.js:1:1:50:0 | exports object of module es2015 |

View File

@@ -68,6 +68,8 @@
| classAccessors.js:11:9:11:11 | myY | classAccessors.js:11:15:11:20 | this.y | file://:0:0:0:0 | indefinite value (heap) |
| classAccessors.js:12:9:12:11 | myZ | classAccessors.js:12:15:12:20 | this.z | file://:0:0:0:0 | indefinite value (call) |
| classAccessors.js:12:9:12:11 | myZ | classAccessors.js:12:15:12:20 | this.z | file://:0:0:0:0 | indefinite value (heap) |
| destructuring.js:2:7:2:24 | { x, y = (z = x) } | destructuring.js:2:28:2:28 | o | file://:0:0:0:0 | indefinite value (call) |
| destructuring.js:3:7:3:8 | z1 | destructuring.js:3:12:3:12 | z | file://:0:0:0:0 | indefinite value (heap) |
| es2015.js:1:5:1:7 | Sup | es2015.js:1:11:6:1 | class { ... ;\\n }\\n} | es2015.js:1:11:6:1 | class Sup |
| es2015.js:4:9:4:12 | ctor | es2015.js:4:16:4:25 | new.target | file://:0:0:0:0 | indefinite value (call) |
| es2015.js:19:7:19:11 | _args | es2015.js:19:15:19:18 | args | file://:0:0:0:0 | object |

View File

@@ -0,0 +1,4 @@
function f(o) {
var { x, y = (z = x) } = o, z;
var z1 = z;
}

View File

@@ -38,6 +38,8 @@
| classAccessors.js:10:9:10:11 | myX | classAccessors.js:10:15:10:20 | this.x | boolean, class, date, function, null, number, object, regular expression,string or undefined |
| classAccessors.js:11:9:11:11 | myY | classAccessors.js:11:15:11:20 | this.y | boolean, class, date, function, null, number, object, regular expression,string or undefined |
| classAccessors.js:12:9:12:11 | myZ | classAccessors.js:12:15:12:20 | this.z | boolean, class, date, function, null, number, object, regular expression,string or undefined |
| destructuring.js:2:7:2:24 | { x, y = (z = x) } | destructuring.js:2:28:2:28 | o | boolean, class, date, function, null, number, object, regular expression,string or undefined |
| destructuring.js:3:7:3:8 | z1 | destructuring.js:3:12:3:12 | z | boolean, class, date, function, null, number, object, regular expression,string or undefined |
| es2015.js:1:5:1:7 | Sup | es2015.js:1:11:6:1 | class { ... ;\\n }\\n} | class |
| es2015.js:4:9:4:12 | ctor | es2015.js:4:16:4:25 | new.target | boolean, class, date, function, null, number, object, regular expression,string or undefined |
| es2015.js:19:7:19:11 | _args | es2015.js:19:15:19:18 | args | object |

View File

@@ -0,0 +1 @@
| y | | X | XX | XXy |

View File

@@ -0,0 +1,3 @@
import semmle.javascript.Util
select truncate("X", 0, "y"), truncate("", 2, "y"), truncate("X", 2, "y"), truncate("XX", 2, "y"), truncate("XXX", 2, "y")

View File

@@ -1,2 +1,4 @@
| tst.js:3:5:3:13 | arguments | Redefinition of arguments. |
| tst.js:7:7:7:15 | arguments | Redefinition of arguments. |
| tst.js:11:11:11:19 | arguments | Redefinition of arguments. |
| tst.js:12:11:12:19 | arguments | Redefinition of arguments. |

View File

@@ -6,3 +6,8 @@ function f() {
function g(x, y) {
var arguments = [y, x]; // NOT OK
}
(function (){
for ([arguments] of o);
for ([arguments = 4] of o);
});

View File

@@ -4,3 +4,4 @@
| tst.js:7:1:7:6 | y = 23 | Assignment to variable y, which is $@ constant. | tst.js:1:1:1:21 | const x ... y = 42; | declared |
| tst.js:10:5:10:10 | y = -1 | Assignment to variable y, which is $@ constant. | tst.js:1:1:1:21 | const x ... y = 42; | declared |
| tst.js:13:1:13:3 | ++x | Assignment to variable x, which is $@ constant. | tst.js:1:1:1:21 | const x ... y = 42; | declared |
| tst.js:25:10:25:14 | [ c ] | Assignment to variable c, which is $@ constant. | tst.js:24:5:24:19 | const c = null; | declared |

View File

@@ -18,4 +18,9 @@ var z = 56;
z = 72;
// OK
const s = "hi";
const s = "hi";
(function (){
const c = null;
for ([ c ] of o);
});

View File

@@ -153,3 +153,7 @@ function v() {
z2 = 42;
return x + y + z1 + z2;
});
(function() {
for (var a = (x, -1) in v = a, o);
});

View File

@@ -4,3 +4,6 @@
| test.js:54:10:54:10 | z | Variable z is used like a local variable, but is missing a declaration. |
| test.js:60:6:60:6 | y | Variable y is used like a local variable, but is missing a declaration. |
| test.js:66:2:66:2 | z | Variable z is used like a local variable, but is missing a declaration. |
| test.js:72:9:72:20 | unresolvable | Variable unresolvable is used like a local variable, but is missing a declaration. |
| tst3.js:7:10:7:10 | x | Variable x is used like a local variable, but is missing a declaration. |
| tst3.js:7:16:7:19 | rest | Variable rest is used like a local variable, but is missing a declaration. |

View File

@@ -66,4 +66,10 @@ function r() {
z = {};
for (var p in z)
;
}
}
(function() {
for ([ unresolvable ] of o) {
unresolvable;
}
});

View File

@@ -2,3 +2,8 @@ function sc_alert(i) {
for(;i;) ;
foo;
}
function f(o) {
for({x, ...rest} of o)
console.log(x in rest);
}

View File

@@ -0,0 +1,5 @@
{
"plugins": [
["transform-react-jsx", { "pragma": "h" }]
]
}

View File

@@ -0,0 +1,4 @@
import { h } from 'preact'; // OK - JSX element uses 'h' after babel compilation
import { q } from 'preact'; // NOT OK - not used
export default (<div>Hello</div>);

View File

@@ -1,6 +1,8 @@
| Babelrc/importPragma.jsx:2:1:2:27 | import ... react'; | Unused import q. |
| decorated.ts:1:1:1:126 | import ... where'; | Unused import actionHandler. |
| decorated.ts:4:10:4:12 | fun | Unused function fun. |
| externs.js:6:5:6:13 | iAmUnused | Unused variable iAmUnused. |
| importWithoutPragma.jsx:1:1:1:27 | import ... react'; | Unused import h. |
| multi-imports.js:1:1:1:29 | import ... om 'x'; | Unused imports a, b, d. |
| multi-imports.js:2:1:2:42 | import ... om 'x'; | Unused imports alphabetically, ordered. |
| typeoftype.ts:9:7:9:7 | y | Unused variable y. |

View File

@@ -0,0 +1,3 @@
import { h } from 'preact'; // NOT OK - not in scope of .babelrc file
export default (<div>Hello</div>);

View File

@@ -87,4 +87,11 @@ function l() {
1n + 1; // NOT OK, but not currently flagged
(function(){
let sum = 0;
for ({value} of async(o)) {
sum += value;
}
});
// semmle-extractor-options: --experimental

View File

@@ -15,3 +15,15 @@
if (x !== undefined)
x.p;
})();
(function() {
var v0
for({ v0 } of o) {
v0.p;
}
var v1;
for({ v1 = x } of o) {
v1.p;
}
});

View File

@@ -11,3 +11,5 @@
| UnknownDirective.js:12:5:12:17 | "use struct;" | Unknown directive: 'use struct;'. |
| UnknownDirective.js:13:5:13:17 | "Use Strict"; | Unknown directive: 'Use Strict'. |
| UnknownDirective.js:14:5:14:14 | "use bar"; | Unknown directive: 'use bar'. |
| UnknownDirective.js:38:5:38:17 | "[0, 0, 0];"; | Unknown directive: '[0, 0, 0];'. |
| UnknownDirective.js:39:5:39:65 | "[0, 0, ... , 0];"; | Unknown directive: '[0, 0, 0, 0, 0, 0, 0 ... (truncated)'. |

View File

@@ -33,3 +33,8 @@ function good() {
"deps foo"; // OK
"deps bar"; // OK
}
function data() {
"[0, 0, 0];"; // NOT OK
"[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];"; // NOT OK
}

View File

@@ -127,3 +127,9 @@ function countOccurrencesDead(xs, p) {
++count;
return count;
}
(function(a) {
for([a] of o) {
a;
}
});

View File

@@ -15,6 +15,7 @@
| UselessConditional.js:65:5:65:5 | x | Variable 'x' always evaluates to true here. |
| UselessConditional.js:76:13:76:13 | x | Variable 'x' always evaluates to true here. |
| UselessConditional.js:82:13:82:13 | x | Variable 'x' always evaluates to true here. |
| UselessConditional.js:89:10:89:16 | x, true | This expression always evaluates to true. |
| UselessConditionalGood.js:58:12:58:13 | x2 | Variable 'x2' always evaluates to false here. |
| UselessConditionalGood.js:69:12:69:13 | xy | Variable 'xy' always evaluates to false here. |
| UselessConditionalGood.js:85:12:85:13 | xy | Variable 'xy' always evaluates to false here. |

View File

@@ -84,4 +84,9 @@ async function awaitFlow(){
}
f3(true);
});
(function() {
if ((x, true));
});
// semmle-extractor-options: --experimental