mirror of
https://github.com/github/codeql.git
synced 2026-04-30 03:05:15 +02:00
Merge pull request #1613 from asger-semmle/canonical-name-defs
Approved by xiemaisi
This commit is contained in:
@@ -68,7 +68,7 @@ predicate variableDeclLookup(VarAccess va, VarDecl decl, string kind) {
|
||||
* For example, in the statement `var a = require("./a")`, the path expression
|
||||
* `"./a"` imports a module `a` in the same folder.
|
||||
*/
|
||||
predicate importLookup(PathExpr path, Module target, string kind) {
|
||||
predicate importLookup(ASTNode path, Module target, string kind) {
|
||||
kind = "I" and
|
||||
(
|
||||
exists(Import i |
|
||||
@@ -147,7 +147,15 @@ predicate typedInvokeLookup(ASTNode ref, ASTNode decl, string kind) {
|
||||
)
|
||||
}
|
||||
|
||||
from ASTNode ref, ASTNode decl, string kind
|
||||
/**
|
||||
* Holds if `ref` is a JSDoc type annotation referring to a class defined at `decl`.
|
||||
*/
|
||||
predicate jsdocTypeLookup(JSDocNamedTypeExpr ref, ASTNode decl, string kind) {
|
||||
decl = ref.getClass().getAstNode() and
|
||||
kind = "T"
|
||||
}
|
||||
|
||||
from Locatable ref, ASTNode decl, string kind
|
||||
where
|
||||
variableDefLookup(ref, decl, kind)
|
||||
or
|
||||
@@ -161,4 +169,6 @@ where
|
||||
typeLookup(ref, decl, kind)
|
||||
or
|
||||
typedInvokeLookup(ref, decl, kind)
|
||||
or
|
||||
jsdocTypeLookup(ref, decl, kind)
|
||||
select ref, decl, kind
|
||||
|
||||
@@ -25,6 +25,7 @@ import semmle.javascript.Extend
|
||||
import semmle.javascript.Externs
|
||||
import semmle.javascript.Files
|
||||
import semmle.javascript.Functions
|
||||
import semmle.javascript.GlobalAccessPaths
|
||||
import semmle.javascript.HTML
|
||||
import semmle.javascript.HtmlSanitizers
|
||||
import semmle.javascript.JSDoc
|
||||
|
||||
@@ -317,6 +317,7 @@ class ExprOrStmt extends @exprorstmt, ControlFlowNode, ASTNode { }
|
||||
*/
|
||||
class StmtContainer extends @stmt_container, ASTNode {
|
||||
/** Gets the innermost enclosing container in which this container is nested. */
|
||||
cached
|
||||
StmtContainer getEnclosingContainer() { none() }
|
||||
|
||||
/**
|
||||
|
||||
180
javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll
Normal file
180
javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll
Normal file
@@ -0,0 +1,180 @@
|
||||
/**
|
||||
* Provides predicates for associating qualified names with data flow nodes.
|
||||
*/
|
||||
import javascript
|
||||
|
||||
module GlobalAccessPath {
|
||||
/**
|
||||
* A local variable with exactly one definition, not counting implicit initialization.
|
||||
*/
|
||||
private class EffectivelyConstantVariable extends LocalVariable {
|
||||
EffectivelyConstantVariable() {
|
||||
strictcount(SsaExplicitDefinition ssa | ssa.getSourceVariable() = this) = 1
|
||||
}
|
||||
|
||||
/** Gets the SSA definition of this variable. */
|
||||
SsaExplicitDefinition getSsaDefinition() {
|
||||
result.getSourceVariable() = this
|
||||
}
|
||||
|
||||
/** Gets the data flow node representing the value of this variable, if one exists. */
|
||||
DataFlow::Node getValue() {
|
||||
result = getSsaDefinition().getRhsNode()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the global access path referred to by `node`.
|
||||
*
|
||||
* This holds for direct references as well as for aliases
|
||||
* established through local data flow.
|
||||
*
|
||||
* Examples:
|
||||
* ```
|
||||
* function f() {
|
||||
* let v = foo.bar; // reference to 'foo.bar'
|
||||
* v.baz; // reference to 'foo.bar.baz'
|
||||
* }
|
||||
*
|
||||
* (function(ns) {
|
||||
* ns.x; // reference to 'NS.x'
|
||||
* })(NS = NS || {});
|
||||
* ```
|
||||
*/
|
||||
string fromReference(DataFlow::Node node) {
|
||||
result = fromReference(node.getImmediatePredecessor())
|
||||
or
|
||||
exists(EffectivelyConstantVariable var |
|
||||
var.isCaptured() and
|
||||
node.asExpr() = var.getAnAccess() and
|
||||
result = fromReference(var.getValue())
|
||||
)
|
||||
or
|
||||
node.accessesGlobal(result) and
|
||||
result != "undefined"
|
||||
or
|
||||
not node.accessesGlobal(_) and
|
||||
exists(DataFlow::PropRead prop | node = prop |
|
||||
result = fromReference(prop.getBase()) + "." + prop.getPropertyName()
|
||||
)
|
||||
or
|
||||
exists(Closure::ClosureNamespaceAccess acc | node = acc |
|
||||
result = acc.getClosureNamespace()
|
||||
)
|
||||
or
|
||||
exists(DataFlow::PropertyProjection proj | node = proj |
|
||||
proj.isSingletonProjection() and
|
||||
result = fromReference(proj.getObject()) + "." + proj.getASelector()
|
||||
)
|
||||
or
|
||||
// Treat 'e || {}' as having the same name as 'e'
|
||||
exists(LogOrExpr e | node.asExpr() = e |
|
||||
e.getRightOperand().(ObjectExpr).getNumProperty() = 0 and
|
||||
result = fromReference(e.getLeftOperand().flow())
|
||||
)
|
||||
or
|
||||
// Treat 'e && e.f' as having the same name as 'e.f'
|
||||
exists(LogAndExpr e, Expr lhs, PropAccess rhs | node.asExpr() = e |
|
||||
lhs = e.getLeftOperand() and
|
||||
rhs = e.getRightOperand() and
|
||||
(
|
||||
exists(Variable v |
|
||||
lhs = v.getAnAccess() and
|
||||
rhs.getBase() = v.getAnAccess()
|
||||
)
|
||||
or
|
||||
exists(string name |
|
||||
lhs.(PropAccess).getQualifiedName() = name and
|
||||
rhs.getBase().(PropAccess).getQualifiedName() = name
|
||||
)
|
||||
) and
|
||||
result = fromReference(rhs.flow())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `rhs` is the right-hand side of a self-assignment.
|
||||
*
|
||||
* This usually happens in defensive initialization, for example:
|
||||
* ```
|
||||
* foo = foo || {};
|
||||
* ```
|
||||
*/
|
||||
private predicate isSelfAssignment(DataFlow::Node rhs) {
|
||||
fromRhs(rhs) = fromReference(rhs)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is an assignment to `accessPath` in `file`, not counting
|
||||
* self-assignments.
|
||||
*/
|
||||
private predicate isAssignedInFile(string accessPath, File file) {
|
||||
exists(DataFlow::Node rhs |
|
||||
fromRhs(rhs) = accessPath and
|
||||
not isSelfAssignment(rhs) and
|
||||
// Note: Avoid unneeded materialization of DataFlow::Node.getFile()
|
||||
rhs.getAstNode().getFile() = file
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `accessPath` is only assigned to from one file, not counting
|
||||
* self-assignments.
|
||||
*/
|
||||
predicate isAssignedInUniqueFile(string accessPath) {
|
||||
strictcount(File f | isAssignedInFile(accessPath, f)) = 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the global access path `node` is being assigned to, if any.
|
||||
*
|
||||
* Only holds for the immediate right-hand side of an assignment or property, not
|
||||
* for nodes that transitively flow there.
|
||||
*
|
||||
* For example, the class nodes below all map to `foo.bar`:
|
||||
* ```
|
||||
* foo.bar = class {};
|
||||
*
|
||||
* foo = { bar: class {} };
|
||||
*
|
||||
* (function(f) {
|
||||
* f.bar = class {}
|
||||
* })(foo = foo || {});
|
||||
* ```
|
||||
*/
|
||||
string fromRhs(DataFlow::Node node) {
|
||||
exists(DataFlow::SourceNode base, string baseName, string name |
|
||||
node = base.getAPropertyWrite(name).getRhs() and
|
||||
result = baseName + "." + name
|
||||
|
|
||||
baseName = fromReference(base)
|
||||
or
|
||||
baseName = fromRhs(base)
|
||||
)
|
||||
or
|
||||
exists(AssignExpr assign |
|
||||
node = assign.getRhs().flow() and
|
||||
result = assign.getLhs().(GlobalVarAccess).getName()
|
||||
)
|
||||
or
|
||||
exists(FunctionDeclStmt fun |
|
||||
node = DataFlow::valueNode(fun) and
|
||||
result = fun.getId().(GlobalVarDecl).getName()
|
||||
)
|
||||
or
|
||||
exists(ClassDeclStmt cls |
|
||||
node = DataFlow::valueNode(cls) and
|
||||
result = cls.getIdentifier().(GlobalVarDecl).getName()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the global access path referenced by or assigned to `node`.
|
||||
*/
|
||||
string getAccessPath(DataFlow::Node node) {
|
||||
result = fromReference(node)
|
||||
or
|
||||
not exists(fromReference(node)) and
|
||||
result = fromRhs(node)
|
||||
}
|
||||
}
|
||||
@@ -132,7 +132,13 @@ class JSDocTypeExpr extends @jsdoc_type_expr, JSDocTypeExprParent, TypeAnnotatio
|
||||
}
|
||||
|
||||
override Stmt getEnclosingStmt() {
|
||||
result.getDocumentation() = getJSDocComment()
|
||||
exists(Documentable astNode | astNode.getDocumentation() = getJSDocComment() |
|
||||
result = astNode
|
||||
or
|
||||
result = astNode.(ExprOrType).getEnclosingStmt()
|
||||
or
|
||||
result = astNode.(Property).getObjectExpr().getEnclosingStmt()
|
||||
)
|
||||
}
|
||||
|
||||
override StmtContainer getContainer() { result = getEnclosingStmt().getContainer() }
|
||||
@@ -205,9 +211,60 @@ class JSDocNamedTypeExpr extends @jsdoc_named_type_expr, JSDocTypeExpr {
|
||||
|
||||
override predicate isRawFunction() { getName() = "Function" }
|
||||
|
||||
/**
|
||||
* Holds if this name consists of the unqualified name `prefix`
|
||||
* followed by a (possibly empty) `suffix`.
|
||||
*
|
||||
* For example:
|
||||
* - `foo.bar.Baz` has prefix `foo` and suffix `.bar.Baz`.
|
||||
* - `Baz` has prefix `Baz` and an empty suffix.
|
||||
*/
|
||||
predicate hasNameParts(string prefix, string suffix) {
|
||||
exists(string regex, string name | regex = "([^.]+)(.*)" |
|
||||
name = getName() and
|
||||
prefix = name.regexpCapture(regex, 1) and
|
||||
suffix = name.regexpCapture(regex, 2)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
pragma[nomagic]
|
||||
private predicate hasNamePartsAndEnv(string prefix, string suffix, JSDoc::Environment env) {
|
||||
// Force join ordering
|
||||
hasNameParts(prefix, suffix) and
|
||||
env.isContainerInScope(getContainer())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the qualified name of this name by resolving its prefix, if any.
|
||||
*/
|
||||
private string resolvedName() {
|
||||
exists(string prefix, string suffix, JSDoc::Environment env |
|
||||
hasNamePartsAndEnv(prefix, suffix, env) and
|
||||
result = env.resolveAlias(prefix) + suffix
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasQualifiedName(string globalName) {
|
||||
globalName = resolvedName()
|
||||
or
|
||||
not exists(resolvedName()) and
|
||||
globalName = getName()
|
||||
}
|
||||
|
||||
override DataFlow::ClassNode getClass() {
|
||||
exists(string name |
|
||||
hasQualifiedName(name) and
|
||||
result.hasQualifiedName(name)
|
||||
)
|
||||
or
|
||||
// Handle case where a local variable has a reference to the class,
|
||||
// but the class doesn't have a globally qualified name.
|
||||
exists(string alias, JSDoc::Environment env |
|
||||
hasNamePartsAndEnv(alias, "", env) and
|
||||
result.getAClassReference().flowsTo(env.getNodeFromAlias(alias))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -234,6 +291,10 @@ class JSDocAppliedTypeExpr extends @jsdoc_applied_type_expr, JSDocTypeExpr {
|
||||
override predicate hasQualifiedName(string globalName) {
|
||||
getHead().hasQualifiedName(globalName)
|
||||
}
|
||||
|
||||
override DataFlow::ClassNode getClass() {
|
||||
result = getHead().getClass()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -247,6 +308,10 @@ class JSDocNullableTypeExpr extends @jsdoc_nullable_type_expr, JSDocTypeExpr {
|
||||
predicate isPrefix() { jsdoc_prefix_qualifier(this) }
|
||||
|
||||
override JSDocTypeExpr getAnUnderlyingType() { result = getTypeExpr().getAnUnderlyingType() }
|
||||
|
||||
override DataFlow::ClassNode getClass() {
|
||||
result = getTypeExpr().getClass()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -260,6 +325,10 @@ class JSDocNonNullableTypeExpr extends @jsdoc_non_nullable_type_expr, JSDocTypeE
|
||||
predicate isPrefix() { jsdoc_prefix_qualifier(this) }
|
||||
|
||||
override JSDocTypeExpr getAnUnderlyingType() { result = getTypeExpr().getAnUnderlyingType() }
|
||||
|
||||
override DataFlow::ClassNode getClass() {
|
||||
result = getTypeExpr().getClass()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -330,6 +399,10 @@ class JSDocOptionalParameterTypeExpr extends @jsdoc_optional_type_expr, JSDocTyp
|
||||
JSDocTypeExpr getUnderlyingType() { result = getChild(0) }
|
||||
|
||||
override JSDocTypeExpr getAnUnderlyingType() { result = getUnderlyingType().getAnUnderlyingType() }
|
||||
|
||||
override DataFlow::ClassNode getClass() {
|
||||
result = getUnderlyingType().getClass()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -353,3 +426,62 @@ class JSDocError extends @jsdoc_error {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { jsdoc_errors(this, _, _, result) }
|
||||
}
|
||||
|
||||
module JSDoc {
|
||||
/**
|
||||
* A statement container which may declare JSDoc name aliases.
|
||||
*/
|
||||
class Environment extends StmtContainer {
|
||||
/**
|
||||
* Gets the fully qualified name aliased by the given unqualified name
|
||||
* within this container.
|
||||
*/
|
||||
string resolveAlias(string alias) {
|
||||
result = GlobalAccessPath::getAccessPath(getNodeFromAlias(alias))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a data flow node from which the type name `alias` should
|
||||
* be resolved.
|
||||
*/
|
||||
DataFlow::Node getNodeFromAlias(string alias) {
|
||||
exists(VarDef def, VarRef ref |
|
||||
def.getContainer() = this and
|
||||
ref = def.getTarget().(BindingPattern).getABindingVarRef() and
|
||||
ref.getName() = alias and
|
||||
isTypenamePrefix(ref.getName()) // restrict size of predicate
|
||||
|
|
||||
exists(PropertyPattern p |
|
||||
ref = p.getValuePattern() and
|
||||
result.getAstNode() = p
|
||||
)
|
||||
or
|
||||
result = DataFlow::valueNode(def.(Stmt))
|
||||
or
|
||||
ref = def.getTarget() and
|
||||
result = def.getSource().flow()
|
||||
or
|
||||
result = DataFlow::parameterNode(def)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the aliases declared in this environment should be in scope
|
||||
* within the given container.
|
||||
*
|
||||
* Specifically, this holds if this environment declares at least one
|
||||
* alias and is an ancestor of `container`.
|
||||
*/
|
||||
final predicate isContainerInScope(StmtContainer container) {
|
||||
exists(resolveAlias(_)) and // restrict size of predicate
|
||||
container = this
|
||||
or
|
||||
isContainerInScope(container.getEnclosingContainer())
|
||||
}
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate isTypenamePrefix(string name) {
|
||||
any(JSDocNamedTypeExpr expr).hasNameParts(name, _)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -521,6 +521,14 @@ class SsaExplicitDefinition extends SsaDefinition, TExplicitDef {
|
||||
) {
|
||||
getDef().getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the data flow node representing the incoming value assigned at this definition,
|
||||
* if any.
|
||||
*/
|
||||
DataFlow::Node getRhsNode() {
|
||||
result = DataFlow::defSourceNode(getDef(), getSourceVariable())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -105,4 +105,11 @@ class TypeAnnotation extends @type_annotation, Locatable {
|
||||
* Note that this has no result for JSDoc type annotations.
|
||||
*/
|
||||
Type getType() { none() }
|
||||
|
||||
/**
|
||||
* Gets the class referenced by this type annotation, if any.
|
||||
*
|
||||
* This unfolds nullability modifiers and generic type applications.
|
||||
*/
|
||||
DataFlow::ClassNode getClass() { none() }
|
||||
}
|
||||
|
||||
@@ -550,6 +550,10 @@ class TypeExpr extends ExprOrType, @typeexpr, TypeAnnotation {
|
||||
override StmtContainer getContainer() { result = ExprOrType.super.getContainer() }
|
||||
|
||||
override TopLevel getTopLevel() { result = ExprOrType.super.getTopLevel() }
|
||||
|
||||
override DataFlow::ClassNode getClass() {
|
||||
result.getAstNode() = getType().(ClassType).getClass()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -161,6 +161,66 @@ module DataFlow {
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() }
|
||||
|
||||
/**
|
||||
* Gets the immediate predecessor of this node, if any.
|
||||
*
|
||||
* A node with an immediate predecessor can usually only have the value that flows
|
||||
* into its from its immediate predecessor, currently with two exceptions:
|
||||
*
|
||||
* - An immediately-invoked function expression with a single return expression `e`
|
||||
* has `e` as its immediate predecessor, even if the function can fall over the
|
||||
* end and return `undefined`.
|
||||
*
|
||||
* - A destructuring property pattern or element pattern with a default value has
|
||||
* both the `PropRead` and its default value as immediate predecessors.
|
||||
*/
|
||||
cached
|
||||
DataFlow::Node getImmediatePredecessor() {
|
||||
// Use of variable -> definition of variable
|
||||
exists(SsaVariable var |
|
||||
this = DataFlow::valueNode(var.getAUse()) and
|
||||
result.(DataFlow::SsaDefinitionNode).getSsaVariable() = var
|
||||
)
|
||||
or
|
||||
// Refinement of variable -> original definition of variable
|
||||
exists(SsaRefinementNode refinement |
|
||||
this.(DataFlow::SsaDefinitionNode).getSsaVariable() = refinement.getVariable() and
|
||||
result.(DataFlow::SsaDefinitionNode).getSsaVariable() = refinement.getAnInput()
|
||||
)
|
||||
or
|
||||
// Definition of variable -> RHS of definition
|
||||
exists(SsaExplicitDefinition def |
|
||||
this = TSsaDefNode(def) and
|
||||
result = def.getRhsNode()
|
||||
)
|
||||
or
|
||||
// IIFE call -> return value of IIFE
|
||||
// Note: not sound in case function falls over end and returns 'undefined'
|
||||
exists(Function fun |
|
||||
localCall(this.asExpr(), fun) and
|
||||
result = fun.getAReturnedExpr().flow() and
|
||||
strictcount(fun.getAReturnedExpr()) = 1
|
||||
)
|
||||
or
|
||||
// IIFE parameter -> IIFE call
|
||||
exists(Parameter param |
|
||||
this = DataFlow::parameterNode(param) and
|
||||
localArgumentPassing(result.asExpr(), param)
|
||||
)
|
||||
or
|
||||
// `{ x } -> e` in `let { x } = e`
|
||||
exists(DestructuringPattern pattern |
|
||||
this = TDestructuringPatternNode(pattern)
|
||||
|
|
||||
exists(VarDef def |
|
||||
pattern = def.getTarget() and
|
||||
result = DataFlow::valueNode(def.getDestructuringSource())
|
||||
)
|
||||
or
|
||||
result = patternPropRead(pattern)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1089,6 +1149,27 @@ module DataFlow {
|
||||
nd = TExceptionalFunctionReturnNode(function)
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL. DO NOT USE.
|
||||
*
|
||||
* Gets the `PropRead` node corresponding to the value stored in the given
|
||||
* binding pattern due to destructuring.
|
||||
*
|
||||
* For example, in `let { p: value } = f()`, the `value` pattern maps to a `PropRead`
|
||||
* extracting the `p` property.
|
||||
*/
|
||||
private DataFlow::PropRead patternPropRead(BindingPattern value) {
|
||||
exists(PropertyPattern prop |
|
||||
value = prop.getValuePattern() and
|
||||
result = TPropNode(prop)
|
||||
)
|
||||
or
|
||||
exists(ArrayPattern array |
|
||||
value = array.getAnElement() and
|
||||
result = TElementPatternNode(array, value)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A classification of flows that are not modeled, or only modeled incompletely, by
|
||||
* `DataFlowNode`:
|
||||
@@ -1243,10 +1324,12 @@ module DataFlow {
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL. DO NOT USE.
|
||||
*
|
||||
* Gets the data flow node representing the source of the definition of `v` at `def`,
|
||||
* if any.
|
||||
*/
|
||||
private Node defSourceNode(VarDef def, SsaSourceVariable v) {
|
||||
Node defSourceNode(VarDef def, SsaSourceVariable v) {
|
||||
exists(BindingPattern lhs, VarRef r |
|
||||
lhs = def.getTarget() and r = lhs.getABindingVarRef() and r.getVariable() = v
|
||||
|
|
||||
|
||||
@@ -724,6 +724,18 @@ class ClassNode extends DataFlow::SourceNode {
|
||||
DataFlow::SourceNode getAnInstanceReference() {
|
||||
result = getAnInstanceReference(DataFlow::TypeTracker::end())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this class is exposed in the global scope through the given qualified name.
|
||||
*/
|
||||
pragma[noinline]
|
||||
predicate hasQualifiedName(string name) {
|
||||
exists(DataFlow::Node rhs |
|
||||
getAClassReference().flowsTo(rhs) and
|
||||
name = GlobalAccessPath::fromRhs(rhs) and
|
||||
GlobalAccessPath::isAssignedInUniqueFile(name)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module ClassNode {
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
test_fromReference
|
||||
| other_ns.js:2:11:2:12 | ns | NS |
|
||||
| other_ns.js:3:3:3:4 | ns | NS |
|
||||
| other_ns.js:3:3:3:8 | ns.foo | NS.foo |
|
||||
| other_ns.js:3:3:3:12 | ns.foo.bar | NS.foo.bar |
|
||||
| other_ns.js:4:4:4:5 | NS | NS |
|
||||
| other_ns.js:4:4:4:16 | NS = NS \|\| {} | NS |
|
||||
| other_ns.js:4:9:4:10 | NS | NS |
|
||||
| other_ns.js:4:9:4:16 | NS \|\| {} | NS |
|
||||
| other_ns.js:6:1:6:8 | Conflict | Conflict |
|
||||
| test.js:2:7:2:17 | v | foo.bar |
|
||||
| test.js:2:11:2:13 | foo | foo |
|
||||
| test.js:2:11:2:17 | foo.bar | foo.bar |
|
||||
| test.js:3:3:3:3 | v | foo.bar |
|
||||
| test.js:3:3:3:7 | v.baz | foo.bar.baz |
|
||||
| test.js:4:7:4:24 | { baz, a, b: {c} } | foo.bar |
|
||||
| test.js:4:7:4:28 | c | foo.bar.b.c |
|
||||
| test.js:4:9:4:11 | baz | foo.bar.baz |
|
||||
| test.js:4:14:4:14 | a | foo.bar.a |
|
||||
| test.js:4:17:4:22 | b: {c} | foo.bar.b |
|
||||
| test.js:4:20:4:22 | {c} | foo.bar.b |
|
||||
| test.js:4:21:4:21 | c | foo.bar.b.c |
|
||||
| test.js:4:28:4:28 | v | foo.bar |
|
||||
| test.js:5:11:5:11 | c | foo.bar.b.c |
|
||||
| test.js:5:11:5:13 | c.d | foo.bar.b.c.d |
|
||||
| test.js:7:7:7:16 | w | window |
|
||||
| test.js:7:11:7:16 | window | window |
|
||||
| test.js:8:13:8:18 | window | window |
|
||||
| test.js:8:13:8:20 | window.x | x |
|
||||
| test.js:8:13:8:22 | window.x.y | x.y |
|
||||
| test.js:9:13:9:18 | global | global |
|
||||
| test.js:9:13:9:20 | global.x | x |
|
||||
| test.js:9:13:9:22 | global.x.y | x.y |
|
||||
| test.js:10:13:10:13 | w | window |
|
||||
| test.js:10:13:10:15 | w.x | x |
|
||||
| test.js:10:13:10:17 | w.x.y | x.y |
|
||||
| test.js:12:7:12:25 | notUnique | foo.bar |
|
||||
| test.js:12:19:12:21 | foo | foo |
|
||||
| test.js:12:19:12:25 | foo.bar | foo.bar |
|
||||
| test.js:13:7:13:15 | something | something |
|
||||
| test.js:14:5:14:23 | notUnique | bar.baz |
|
||||
| test.js:14:17:14:19 | bar | bar |
|
||||
| test.js:14:17:14:23 | bar.baz | bar.baz |
|
||||
| test.js:22:11:22:12 | ns | NS |
|
||||
| test.js:23:3:23:4 | ns | NS |
|
||||
| test.js:23:3:23:8 | ns.foo | NS.foo |
|
||||
| test.js:23:3:23:12 | ns.foo.bar | NS.foo.bar |
|
||||
| test.js:24:4:24:5 | NS | NS |
|
||||
| test.js:24:4:24:16 | NS = NS \|\| {} | NS |
|
||||
| test.js:24:9:24:10 | NS | NS |
|
||||
| test.js:24:9:24:16 | NS \|\| {} | NS |
|
||||
| test.js:26:1:26:8 | Conflict | Conflict |
|
||||
| test.js:33:7:33:18 | { bar = {} } | foo |
|
||||
| test.js:33:7:33:24 | bar | foo.bar |
|
||||
| test.js:33:9:33:16 | bar = {} | foo.bar |
|
||||
| test.js:33:22:33:24 | foo | foo |
|
||||
| test.js:34:11:34:13 | bar | foo.bar |
|
||||
| test.js:34:11:34:17 | bar.baz | foo.bar.baz |
|
||||
test_fromRhs
|
||||
| other_ns.js:4:9:4:16 | NS \|\| {} | NS |
|
||||
| other_ns.js:6:12:6:13 | {} | Conflict |
|
||||
| test.js:1:1:20:1 | functio ... ss {}\\n} | f |
|
||||
| test.js:24:9:24:16 | NS \|\| {} | NS |
|
||||
| test.js:26:12:26:13 | {} | Conflict |
|
||||
| test.js:28:1:28:20 | class GlobalClass {} | GlobalClass |
|
||||
| test.js:30:1:30:28 | functio ... on() {} | globalFunction |
|
||||
| test.js:32:1:35:1 | functio ... .baz'\\n} | destruct |
|
||||
test_assignedUnique
|
||||
| GlobalClass |
|
||||
| destruct |
|
||||
| f |
|
||||
| globalFunction |
|
||||
@@ -0,0 +1,13 @@
|
||||
import javascript
|
||||
|
||||
query string test_fromReference(DataFlow::Node node) {
|
||||
result = GlobalAccessPath::fromReference(node)
|
||||
}
|
||||
|
||||
query string test_fromRhs(DataFlow::Node node) {
|
||||
result = GlobalAccessPath::fromRhs(node)
|
||||
}
|
||||
|
||||
query string test_assignedUnique() {
|
||||
GlobalAccessPath::isAssignedInUniqueFile(result)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// use NS from another file, that doesn't truly assign to it
|
||||
(function(ns) {
|
||||
ns.foo.bar; // 'NS.foo.bar'
|
||||
})(NS = NS || {});
|
||||
|
||||
Conflict = {}; // assigned in multiple files
|
||||
35
javascript/ql/test/library-tests/GlobalAccessPaths/test.js
Normal file
35
javascript/ql/test/library-tests/GlobalAccessPaths/test.js
Normal file
@@ -0,0 +1,35 @@
|
||||
function f() {
|
||||
let v = foo.bar; // 'foo.bar'
|
||||
v.baz; // 'foo.bar.baz'
|
||||
let { baz, a, b: {c} } = v;
|
||||
let d = c.d; // 'foo.bar.b.c.d'
|
||||
|
||||
let w = window;
|
||||
let xy1 = window.x.y; // 'x.y'
|
||||
let xy2 = global.x.y; // 'x.y'
|
||||
let xy3 = w.x.y; // 'x.y'
|
||||
|
||||
let notUnique = foo.bar;
|
||||
if (something()) {
|
||||
notUnique = bar.baz;
|
||||
}
|
||||
notUnique.x; // No global access path
|
||||
|
||||
function localFunction() {}
|
||||
class LocalClass {}
|
||||
}
|
||||
|
||||
(function(ns) {
|
||||
ns.foo.bar; // 'NS.foo.bar'
|
||||
})(NS = NS || {});
|
||||
|
||||
Conflict = {}; // assigned in multiple files
|
||||
|
||||
class GlobalClass {}
|
||||
|
||||
function globalFunction() {}
|
||||
|
||||
function destruct() {
|
||||
let { bar = {} } = foo;
|
||||
let v = bar.baz; // 'foo.bar.baz'
|
||||
}
|
||||
15
javascript/ql/test/library-tests/JSDoc/NameResolution/bar.js
Normal file
15
javascript/ql/test/library-tests/JSDoc/NameResolution/bar.js
Normal file
@@ -0,0 +1,15 @@
|
||||
function test() {
|
||||
let x = ns.very.long.namespace;
|
||||
|
||||
/**
|
||||
* @param {x.Foo} foo
|
||||
*/
|
||||
function f(foo) {}
|
||||
}
|
||||
|
||||
(function(iife) {
|
||||
/**
|
||||
* @param {iife.Foo} foo
|
||||
*/
|
||||
function f(foo) {}
|
||||
})(IIFE);
|
||||
@@ -0,0 +1,13 @@
|
||||
goog.module('test');
|
||||
|
||||
let net = goog.require('goog.net');
|
||||
|
||||
let { SomeType } = net;
|
||||
|
||||
/**
|
||||
* @param {goog.net.SomeType} xio
|
||||
* @param {net.SomeType} xio2
|
||||
* @param {SomeType} xio3
|
||||
*/
|
||||
function test(xio, xio2, xio3) {
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
ns = ns || {};
|
||||
|
||||
ns.very.long.namespace.Foo = class {};
|
||||
@@ -0,0 +1,5 @@
|
||||
| bar.js:5:14:5:18 | x.Foo | ns.very.long.namespace.Foo |
|
||||
| bar.js:12:14:12:21 | iife.Foo | IIFE.Foo |
|
||||
| closure.js:8:12:8:28 | goog.net.SomeType | goog.net.SomeType |
|
||||
| closure.js:9:12:9:23 | net.SomeType | goog.net.SomeType |
|
||||
| closure.js:10:12:10:19 | SomeType | goog.net.SomeType |
|
||||
@@ -0,0 +1,5 @@
|
||||
import javascript
|
||||
|
||||
query string test_hasQualifiedName(JSDocNamedTypeExpr expr) {
|
||||
expr.hasQualifiedName(result)
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import Parameter_getDocumentation
|
||||
import JSDocRestParameterTypeExpr
|
||||
import getDocumentation
|
||||
import JSDocRecordTypeExpr
|
||||
import JSDoc
|
||||
import JSDoc_test
|
||||
import JSDocTag
|
||||
import ObjectExpr_getDocumentation
|
||||
import JSDocFunctionTypeExpr
|
||||
@@ -0,0 +1,14 @@
|
||||
| tst.js:2:7:2:13 | a = g() | a | tst.js:2:11:2:13 | g() |
|
||||
| tst.js:4:7:4:24 | { propB: b } = g() | b | tst.js:4:9:4:16 | propB: b |
|
||||
| tst.js:6:7:6:34 | { propC ... } = g() | c | tst.js:6:9:6:16 | propC: c |
|
||||
| tst.js:6:7:6:34 | { propC ... } = g() | d | tst.js:6:19:6:26 | propD: d |
|
||||
| tst.js:8:7:8:41 | { array ... } = g() | elm1 | tst.js:8:22:8:25 | elm1 |
|
||||
| tst.js:8:7:8:41 | { array ... } = g() | elm2 | tst.js:8:28:8:31 | elm2 |
|
||||
| tst.js:17:3:17:22 | ({ propB: b }) = g() | b | tst.js:17:6:17:13 | propB: b |
|
||||
| tst.js:19:3:19:32 | ({ prop ... ) = g() | c | tst.js:19:6:19:13 | propC: c |
|
||||
| tst.js:19:3:19:32 | ({ prop ... ) = g() | d | tst.js:19:16:19:23 | propD: d |
|
||||
| tst.js:21:3:21:22 | [ elm1, elm2 ] = g() | elm1 | tst.js:21:5:21:8 | elm1 |
|
||||
| tst.js:21:3:21:22 | [ elm1, elm2 ] = g() | elm2 | tst.js:21:11:21:14 | elm2 |
|
||||
| tst.js:31:12:31:23 | [elm1, elm2] | elm1 | tst.js:31:13:31:16 | elm1 |
|
||||
| tst.js:31:12:31:23 | [elm1, elm2] | elm2 | tst.js:31:19:31:22 | elm2 |
|
||||
| tst.js:31:26:31:40 | { prop: value } | value | tst.js:31:28:31:38 | prop: value |
|
||||
@@ -0,0 +1,4 @@
|
||||
import javascript
|
||||
|
||||
from SsaExplicitDefinition def
|
||||
select def, def.getSourceVariable(), def.getRhsNode()
|
||||
35
javascript/ql/test/library-tests/SSA/GetRhsNode/tst.js
Normal file
35
javascript/ql/test/library-tests/SSA/GetRhsNode/tst.js
Normal file
@@ -0,0 +1,35 @@
|
||||
function f() {
|
||||
let a = g();
|
||||
|
||||
let { propB: b } = g();
|
||||
|
||||
let { propC: c, propD: d } = g();
|
||||
|
||||
let { arrayProp: [ elm1, elm2 ] } = g();
|
||||
|
||||
a; // Ensure variables are live.
|
||||
b;
|
||||
c;
|
||||
d;
|
||||
elm1;
|
||||
elm2;
|
||||
|
||||
({ propB: b }) = g();
|
||||
|
||||
({ propC: c, propD: d }) = g();
|
||||
|
||||
[ elm1, elm2 ] = g();
|
||||
|
||||
a;
|
||||
b;
|
||||
c;
|
||||
d;
|
||||
elm1;
|
||||
elm2;
|
||||
}
|
||||
|
||||
function h([elm1, elm2], { prop: value }) {
|
||||
elm1;
|
||||
elm2;
|
||||
value;
|
||||
}
|
||||
Reference in New Issue
Block a user