mirror of
https://github.com/github/codeql.git
synced 2026-05-01 19:55:15 +02:00
Merge pull request #1577 from asger-semmle/infername
Approved by xiemaisi
This commit is contained in:
@@ -27,3 +27,5 @@
|
||||
|
||||
## Changes to QL libraries
|
||||
|
||||
- The `getName()` predicate on functions and classes now gets a name
|
||||
inferred from the context if the function or class was not declared with a name.
|
||||
|
||||
@@ -32,8 +32,15 @@ class ClassOrInterface extends @classorinterface, TypeParameterized {
|
||||
/** Gets the identifier naming the declared type, if any. */
|
||||
Identifier getIdentifier() { none() } // Overridden in subtypes.
|
||||
|
||||
/** Gets the name of the defined class or interface, if any. */
|
||||
string getName() { result = getIdentifier().getName() }
|
||||
/**
|
||||
* Gets the name of the defined class or interface, possibly inferred
|
||||
* from the context if this is an anonymous class expression.
|
||||
*
|
||||
* Has no result if no name could be determined.
|
||||
*/
|
||||
string getName() {
|
||||
result = getIdentifier().getName() // Overridden in ClassExpr
|
||||
}
|
||||
|
||||
/** Gets the nearest enclosing function or toplevel in which this class or interface occurs. */
|
||||
StmtContainer getContainer() { result = this.(ExprOrStmt).getContainer() }
|
||||
@@ -203,8 +210,8 @@ class ClassDefinition extends @classdefinition, ClassOrInterface, AST::ValueNode
|
||||
*/
|
||||
private string inferNameFromVarDef() {
|
||||
// in ambiguous cases like `let C = class D {}`, prefer `D` to `C`
|
||||
if exists(getName())
|
||||
then result = "class " + getName()
|
||||
if exists(getIdentifier())
|
||||
then result = "class " + getIdentifier().getName()
|
||||
else
|
||||
exists(VarDef vd | this = vd.getSource() |
|
||||
result = "class " + vd.getTarget().(VarRef).getName()
|
||||
@@ -272,6 +279,26 @@ class ClassDeclStmt extends @classdeclstmt, ClassDefinition, Stmt {
|
||||
* ```
|
||||
*/
|
||||
class ClassExpr extends @classexpr, ClassDefinition, Expr {
|
||||
override string getName() {
|
||||
result = ClassDefinition.super.getName()
|
||||
or
|
||||
not exists(getIdentifier()) and
|
||||
(
|
||||
exists(VarDef vd | this = vd.getSource() | result = vd.getTarget().(VarRef).getName())
|
||||
or
|
||||
exists(ValueProperty p |
|
||||
this = p.getInit() and
|
||||
result = p.getName()
|
||||
)
|
||||
or
|
||||
exists(AssignExpr assign, PropAccess prop |
|
||||
this = assign.getRhs().getUnderlyingValue() and
|
||||
prop = assign.getLhs() and
|
||||
result = prop.getPropertyName()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isImpure() { none() }
|
||||
|
||||
/** Gets the nearest enclosing function or toplevel in which this class expression occurs. */
|
||||
|
||||
@@ -751,9 +751,6 @@ class SpreadProperty extends Property {
|
||||
* ```
|
||||
*/
|
||||
class FunctionExpr extends @functionexpr, Expr, Function {
|
||||
/** Gets the name of this function expression, if any. */
|
||||
override string getName() { result = getId().getName() }
|
||||
|
||||
/** Holds if this function expression is a property setter. */
|
||||
predicate isSetter() { exists(PropertySetter s | s.getInit() = this) }
|
||||
|
||||
|
||||
@@ -80,8 +80,35 @@ class Function extends @function, Parameterized, TypeParameterized, StmtContaine
|
||||
/** Gets the identifier specifying the name of this function, if any. */
|
||||
VarDecl getId() { result = getChildExpr(-1) }
|
||||
|
||||
/** Gets the name of this function, if any. */
|
||||
string getName() { result = getId().getName() }
|
||||
/**
|
||||
* Gets the name of this function if it has one, or a name inferred from its context.
|
||||
*
|
||||
* For named functions such as `function f() { ... }`, this is just the declared
|
||||
* name. For functions assigned to variables or properties (including class
|
||||
* members), this is the name of the variable or property. If no meaningful name
|
||||
* can be inferred, there is no result.
|
||||
*/
|
||||
string getName() {
|
||||
result = getId().getName()
|
||||
or
|
||||
not exists(getId()) and
|
||||
(
|
||||
exists(VarDef vd | this = vd.getSource() | result = vd.getTarget().(VarRef).getName())
|
||||
or
|
||||
exists(Property p |
|
||||
this = p.getInit() and
|
||||
result = p.getName()
|
||||
)
|
||||
or
|
||||
exists(AssignExpr assign, PropAccess prop |
|
||||
this = assign.getRhs().getUnderlyingValue() and
|
||||
prop = assign.getLhs() and
|
||||
result = prop.getPropertyName()
|
||||
)
|
||||
or
|
||||
exists(ClassOrInterface c | this = c.getMember(result).getInit())
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the variable holding this function. */
|
||||
Variable getVariable() { result = getId().getVariable() }
|
||||
@@ -274,8 +301,8 @@ class Function extends @function, Parameterized, TypeParameterized, StmtContaine
|
||||
*/
|
||||
private string inferNameFromVarDef() {
|
||||
// in ambiguous cases like `var f = function g() {}`, prefer `g` to `f`
|
||||
if exists(getName())
|
||||
then result = "function " + getName()
|
||||
if exists(getId())
|
||||
then result = "function " + getId().getName()
|
||||
else
|
||||
exists(VarDef vd | this = vd.getSource() |
|
||||
result = "function " + vd.getTarget().(VarRef).getName()
|
||||
|
||||
@@ -340,12 +340,12 @@ class FunctionNode extends DataFlow::ValueNode, DataFlow::SourceNode {
|
||||
int getNumParameter() { result = count(astNode.getAParameter()) }
|
||||
|
||||
/** Gets the last parameter of this function. */
|
||||
ParameterNode getLastParameter() { result = getParameter(getNumParameter()-1) }
|
||||
ParameterNode getLastParameter() { result = getParameter(getNumParameter() - 1) }
|
||||
|
||||
/** Holds if the last parameter of this function is a rest parameter. */
|
||||
predicate hasRestParameter() { astNode.hasRestParameter() }
|
||||
|
||||
/** Gets the name of this function, if it has one. */
|
||||
/** Gets the unqualified name of this function, if it has one or one can be determined from the context. */
|
||||
string getName() { result = astNode.getName() }
|
||||
|
||||
/** Gets a data flow node corresponding to a return value of this function. */
|
||||
@@ -576,7 +576,7 @@ class ClassNode extends DataFlow::SourceNode {
|
||||
ClassNode() { this = impl }
|
||||
|
||||
/**
|
||||
* Gets the name of the class, if it has one.
|
||||
* Gets the unqualified name of the class, if it has one or one can be determined from the context.
|
||||
*/
|
||||
string getName() { result = impl.getName() }
|
||||
|
||||
@@ -651,16 +651,12 @@ class ClassNode extends DataFlow::SourceNode {
|
||||
/**
|
||||
* Gets a direct super class of this class.
|
||||
*/
|
||||
ClassNode getADirectSuperClass() {
|
||||
result.getAClassReference().flowsTo(getASuperClassNode())
|
||||
}
|
||||
ClassNode getADirectSuperClass() { result.getAClassReference().flowsTo(getASuperClassNode()) }
|
||||
|
||||
/**
|
||||
* Gets a direct subclass of this class.
|
||||
*/
|
||||
final ClassNode getADirectSubClass() {
|
||||
this = result.getADirectSuperClass()
|
||||
}
|
||||
final ClassNode getADirectSubClass() { this = result.getADirectSuperClass() }
|
||||
|
||||
/**
|
||||
* Gets the receiver of an instance member or constructor of this class.
|
||||
@@ -674,16 +670,12 @@ class ClassNode extends DataFlow::SourceNode {
|
||||
/**
|
||||
* Gets the abstract value representing the class itself.
|
||||
*/
|
||||
AbstractValue getAbstractClassValue() {
|
||||
result = this.(AnalyzedNode).getAValue()
|
||||
}
|
||||
AbstractValue getAbstractClassValue() { result = this.(AnalyzedNode).getAValue() }
|
||||
|
||||
/**
|
||||
* Gets the abstract value representing an instance of this class.
|
||||
*/
|
||||
AbstractValue getAbstractInstanceValue() {
|
||||
result = AbstractInstance::of(getAstNode())
|
||||
}
|
||||
AbstractValue getAbstractInstanceValue() { result = AbstractInstance::of(getAstNode()) }
|
||||
|
||||
/**
|
||||
* Gets a dataflow node that refers to this class object.
|
||||
@@ -692,9 +684,7 @@ class ClassNode extends DataFlow::SourceNode {
|
||||
t.start() and
|
||||
result.(AnalyzedNode).getAValue() = getAbstractClassValue()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 |
|
||||
result = getAClassReference(t2).track(t2, t)
|
||||
)
|
||||
exists(DataFlow::TypeTracker t2 | result = getAClassReference(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -725,9 +715,7 @@ class ClassNode extends DataFlow::SourceNode {
|
||||
|
||||
pragma[noinline]
|
||||
private DataFlow::SourceNode getAnInstanceReferenceAux(DataFlow::TypeTracker t) {
|
||||
exists(DataFlow::TypeTracker t2 |
|
||||
result = getAnInstanceReference(t2).track(t2, t)
|
||||
)
|
||||
exists(DataFlow::TypeTracker t2 | result = getAnInstanceReference(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -846,11 +834,12 @@ module ClassNode {
|
||||
*/
|
||||
class FunctionStyleClass extends Range, DataFlow::ValueNode {
|
||||
override Function astNode;
|
||||
|
||||
AbstractFunction function;
|
||||
|
||||
FunctionStyleClass() {
|
||||
function.getFunction() = astNode and
|
||||
exists (DataFlow::PropRef read |
|
||||
exists(DataFlow::PropRef read |
|
||||
read.getPropertyName() = "prototype" and
|
||||
read.getBase().analyze().getAValue() = function
|
||||
)
|
||||
@@ -902,15 +891,13 @@ module ClassNode {
|
||||
|
||||
override FunctionNode getStaticMethod(string name) { result = getAPropertySource(name) }
|
||||
|
||||
override FunctionNode getAStaticMethod() {
|
||||
result = getAPropertySource()
|
||||
}
|
||||
override FunctionNode getAStaticMethod() { result = getAPropertySource() }
|
||||
|
||||
/**
|
||||
* Gets a reference to the prototype of this class.
|
||||
*/
|
||||
DataFlow::SourceNode getAPrototypeReference() {
|
||||
exists (DataFlow::SourceNode base | base.analyze().getAValue() = function |
|
||||
exists(DataFlow::SourceNode base | base.analyze().getAValue() = function |
|
||||
result = base.getAPropertyRead("prototype")
|
||||
or
|
||||
result = base.getAPropertySource("prototype")
|
||||
|
||||
@@ -32,6 +32,7 @@ test_ClassDefinition_getName
|
||||
| points.js:1:1:18:1 | class P ... ;\\n }\\n} | Point |
|
||||
| points.js:20:1:33:1 | class C ... ;\\n }\\n} | ColouredPoint |
|
||||
| staticConstructor.js:1:1:3:1 | class M ... r"; }\\n} | MyClass |
|
||||
| tst.js:1:9:4:1 | class { ... */ }\\n} | A |
|
||||
| tst.js:6:1:8:1 | class B ... t); }\\n} | B |
|
||||
| tst.js:11:1:14:1 | class C ... () {}\\n} | C |
|
||||
test_MethodDefinitions
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
getName
|
||||
| tst.js:16:1:19:1 | class C ... ss C"\\n} | C |
|
||||
| tst.js:21:2:23:1 | class D ... lass"\\n} | D |
|
||||
| tst.js:25:11:25:18 | class {} | E |
|
||||
| tst.js:26:11:29:1 | class G ... ss G"\\n} | G |
|
||||
| tst.js:34:9:34:16 | class {} | Foo |
|
||||
#select
|
||||
| tst.js:16:1:19:1 | class C ... ss C"\\n} | class C |
|
||||
| tst.js:21:2:23:1 | class D ... lass"\\n} | class D |
|
||||
| tst.js:22:12:22:18 | class{} | anonymous class |
|
||||
| tst.js:25:11:25:18 | class {} | class E |
|
||||
| tst.js:26:11:29:1 | class G ... ss G"\\n} | class G |
|
||||
| tst.js:34:9:34:16 | class {} | anonymous class |
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import javascript
|
||||
|
||||
query string getName(ClassDefinition c) { result = c.getName() }
|
||||
|
||||
from ClassDefinition c
|
||||
select c, c.describe()
|
||||
|
||||
@@ -1,3 +1,26 @@
|
||||
getName
|
||||
| tst.js:1:1:1:15 | function f() {} | f |
|
||||
| tst.js:2:2:2:16 | function g() {} | g |
|
||||
| tst.js:4:9:4:22 | function () {} | h |
|
||||
| tst.js:5:9:5:14 | x => x | k |
|
||||
| tst.js:6:9:6:23 | function n() {} | n |
|
||||
| tst.js:9:6:9:18 | function() {} | p |
|
||||
| tst.js:10:6:10:20 | function f() {} | f |
|
||||
| tst.js:11:8:11:12 | () {} | x |
|
||||
| tst.js:12:8:12:13 | (v) {} | x |
|
||||
| tst.js:13:4:13:8 | () {} | m |
|
||||
| tst.js:17:14:17:18 | () {} | constructor |
|
||||
| tst.js:18:4:18:8 | () {} | n |
|
||||
| tst.js:22:17:22:16 | () {} | constructor |
|
||||
| tst.js:22:21:22:20 | (...arg ... rgs); } | constructor |
|
||||
| tst.js:25:17:25:16 | () {} | constructor |
|
||||
| tst.js:26:19:26:18 | () {} | constructor |
|
||||
| tst.js:27:8:27:12 | () {} | y |
|
||||
| tst.js:28:8:28:13 | (v) {} | y |
|
||||
| tst.js:31:9:31:21 | function() {} | foo |
|
||||
| tst.js:32:12:32:24 | function() {} | Hey |
|
||||
| tst.js:34:15:34:14 | () {} | constructor |
|
||||
#select
|
||||
| tst.js:1:1:1:15 | function f() {} | function f |
|
||||
| tst.js:2:2:2:16 | function g() {} | function g |
|
||||
| tst.js:3:2:3:14 | function() {} | anonymous function |
|
||||
@@ -17,3 +40,6 @@
|
||||
| tst.js:26:19:26:18 | () {} | default constructor of class G |
|
||||
| tst.js:27:8:27:12 | () {} | getter method for property y of class G |
|
||||
| tst.js:28:8:28:13 | (v) {} | setter method for property y of class G |
|
||||
| tst.js:31:9:31:21 | function() {} | anonymous function |
|
||||
| tst.js:32:12:32:24 | function() {} | anonymous function |
|
||||
| tst.js:34:15:34:14 | () {} | default constructor of anonymous class |
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import javascript
|
||||
|
||||
query string getName(Function f) { result = f.getName() }
|
||||
|
||||
from Function f
|
||||
select f, f.describe()
|
||||
|
||||
@@ -27,3 +27,8 @@ const E = class {}, // "class E", "default constructor of class E"
|
||||
get y() {} // "getter method for property y of class G"
|
||||
set y(v) {} // "setter method for property y of class G"
|
||||
};
|
||||
|
||||
o.foo = function() {}; // "method foo"
|
||||
o["Hey"] = function() {}; // "anonymous function"
|
||||
|
||||
o.Foo = class {}; // "class Foo"
|
||||
|
||||
Reference in New Issue
Block a user