mirror of
https://github.com/github/codeql.git
synced 2026-05-02 20:25:13 +02:00
Merge pull request #9744 from github/aeisenberg/move-contextual-queries
This commit is contained in:
61
javascript/ql/lib/Declarations/Declarations.qll
Normal file
61
javascript/ql/lib/Declarations/Declarations.qll
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Provides predicates for finding variable references and declarations
|
||||
* in a given function or toplevel.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* Classification of variable references; all references have kind `Ref`,
|
||||
* but only declarations have kind `Decl`.
|
||||
*
|
||||
* Note that references that are not declarations are called accesses elsewhere,
|
||||
* but they are not treated specially in this context.
|
||||
*/
|
||||
newtype RefKind =
|
||||
Ref() or
|
||||
Decl()
|
||||
|
||||
/**
|
||||
* Gets a reference to `var` (if `kind` is `Ref()`) or declaration of
|
||||
* `var` (if `kind` is `Decl()`) in `sc`.
|
||||
*/
|
||||
VarRef refInContainer(Variable var, RefKind kind, StmtContainer sc) {
|
||||
result.getVariable() = var and
|
||||
result.getContainer() = sc and
|
||||
(kind = Decl() implies result instanceof VarDecl)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the textually first reference to `var` (if `kind` is `Ref()`) or
|
||||
* declaration of `var` (if `kind` is `Decl()`) in `sc`.
|
||||
*/
|
||||
VarRef firstRefInContainer(Variable var, RefKind kind, StmtContainer sc) {
|
||||
result =
|
||||
min(refInContainer(var, kind, sc) as ref
|
||||
order by
|
||||
ref.getLocation().getStartLine(), ref.getLocation().getStartColumn()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to `var` (if `kind` is `Ref()`) or declaration of
|
||||
* `var` (if `kind` is `Decl()`) in `tl`.
|
||||
*/
|
||||
VarRef refInTopLevel(Variable var, RefKind kind, TopLevel tl) {
|
||||
result.getVariable() = var and
|
||||
result.getTopLevel() = tl and
|
||||
(kind = Decl() implies result instanceof VarDecl)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the textually first reference to `var` (if `kind` is `Ref()`) or
|
||||
* declaration of `var` (if `kind` is `Decl()`) in `tl`.
|
||||
*/
|
||||
VarRef firstRefInTopLevel(Variable var, RefKind kind, TopLevel tl) {
|
||||
result =
|
||||
min(refInTopLevel(var, kind, tl) as ref
|
||||
order by
|
||||
ref.getLocation().getStartLine(), ref.getLocation().getStartColumn()
|
||||
)
|
||||
}
|
||||
22
javascript/ql/lib/IDEContextual.qll
Normal file
22
javascript/ql/lib/IDEContextual.qll
Normal file
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Provides shared predicates related to contextual queries in the code viewer.
|
||||
*/
|
||||
|
||||
import semmle.files.FileSystem
|
||||
|
||||
/**
|
||||
* Returns the `File` matching the given source file name as encoded by the VS
|
||||
* Code extension.
|
||||
*/
|
||||
cached
|
||||
File getFileBySourceArchiveName(string name) {
|
||||
// The name provided for a file in the source archive by the VS Code extension
|
||||
// has some differences from the absolute path in the database:
|
||||
// 1. colons are replaced by underscores
|
||||
// 2. there's a leading slash, even for Windows paths: "C:/foo/bar" ->
|
||||
// "/C_/foo/bar"
|
||||
// 3. double slashes in UNC prefixes are replaced with a single slash
|
||||
// We can handle 2 and 3 together by unconditionally adding a leading slash
|
||||
// before replacing double slashes.
|
||||
name = ("/" + result.getAbsolutePath().replaceAll(":", "_")).replaceAll("//", "/")
|
||||
}
|
||||
13
javascript/ql/lib/definitions.ql
Normal file
13
javascript/ql/lib/definitions.ql
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @name Jump-to-definition links
|
||||
* @description Generates use-definition pairs that provide the data
|
||||
* for jump-to-definition in the code viewer.
|
||||
* @kind definitions
|
||||
* @id js/jump-to-definition
|
||||
*/
|
||||
|
||||
import definitions
|
||||
|
||||
from Locatable e, AstNode def, string kind
|
||||
where def = definitionOf(e, kind)
|
||||
select e, def, kind
|
||||
181
javascript/ql/lib/definitions.qll
Normal file
181
javascript/ql/lib/definitions.qll
Normal file
@@ -0,0 +1,181 @@
|
||||
/**
|
||||
* Provides classes and predicates related to jump-to-definition links
|
||||
* in the code viewer.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import IDEContextual
|
||||
private import Declarations.Declarations
|
||||
|
||||
/**
|
||||
* Gets the kind of reference that `r` represents.
|
||||
*
|
||||
* References in callee position have kind `"M"` (for "method"), all
|
||||
* others have kind `"V"` (for "variable").
|
||||
*
|
||||
* For example, in the expression `f(x)`, `f` has kind `"M"` while
|
||||
* `x` has kind `"V"`.
|
||||
*/
|
||||
private string refKind(RefExpr r) {
|
||||
if exists(InvokeExpr invk | r = invk.getCallee().getUnderlyingReference())
|
||||
then result = "M"
|
||||
else result = "V"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a class, function or object literal `va` may refer to.
|
||||
*/
|
||||
private AstNode lookupDef(VarAccess va) {
|
||||
exists(AbstractValue av | av = va.analyze().getAValue() |
|
||||
result = av.(AbstractClass).getClass() or
|
||||
result = av.(AbstractFunction).getFunction() or
|
||||
result = av.(AbstractObjectLiteral).getObjectExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `va` is of kind `kind` and `def` is the unique class,
|
||||
* function or object literal it refers to.
|
||||
*/
|
||||
private predicate variableDefLookup(VarAccess va, AstNode def, string kind) {
|
||||
count(lookupDef(va)) = 1 and
|
||||
def = lookupDef(va) and
|
||||
kind = refKind(va)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if variable access `va` is of kind `kind` and refers to the
|
||||
* variable declaration.
|
||||
*
|
||||
* For example, in the statement `var x = 42, y = x;`, the initializing
|
||||
* expression of `y` is a variable access `x` of kind `"V"` that refers to
|
||||
* the declaration `x = 42`.
|
||||
*/
|
||||
private predicate variableDeclLookup(VarAccess va, VarDecl decl, string kind) {
|
||||
// restrict to declarations in same file to avoid accidentally picking up
|
||||
// unrelated global definitions
|
||||
decl = firstRefInTopLevel(va.getVariable(), Decl(), va.getTopLevel()) and
|
||||
kind = refKind(va)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if path expression `path`, which appears in a CommonJS `require`
|
||||
* call or an ES 2015 import statement, imports module `target`; `kind`
|
||||
* is always "I" (for "import").
|
||||
*
|
||||
* For example, in the statement `var a = require("./a")`, the path expression
|
||||
* `"./a"` imports a module `a` in the same folder.
|
||||
*/
|
||||
private predicate importLookup(AstNode path, Module target, string kind) {
|
||||
kind = "I" and
|
||||
(
|
||||
exists(Import i |
|
||||
path = i.getImportedPath() and
|
||||
target = i.getImportedModule()
|
||||
)
|
||||
or
|
||||
exists(ReExportDeclaration red |
|
||||
path = red.getImportedPath() and
|
||||
target = red.getReExportedModule()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a node that may write the property read by `prn`.
|
||||
*/
|
||||
private AstNode getAWrite(DataFlow::PropRead prn) {
|
||||
exists(DataFlow::AnalyzedNode base, DefiniteAbstractValue baseVal, string propName |
|
||||
base = prn.getBase() and
|
||||
propName = prn.getPropertyName() and
|
||||
baseVal = base.getAValue().getAPrototype*()
|
||||
|
|
||||
// write to a property on baseVal
|
||||
exists(AnalyzedPropertyWrite apw |
|
||||
result = apw.getAstNode() and
|
||||
apw.writes(baseVal, propName, _)
|
||||
)
|
||||
or
|
||||
// non-static class members aren't covered by `AnalyzedPropWrite`, so have to be handled
|
||||
// separately
|
||||
exists(ClassDefinition c, MemberDefinition m |
|
||||
m = c.getMember(propName) and
|
||||
baseVal.(AbstractInstance).getConstructor().(AbstractClass).getClass() = c and
|
||||
result = m.getNameExpr()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `prop` is the property name expression of a property read that
|
||||
* may read the property written by `write`. Furthermore, `write` must be the
|
||||
* only such property write. Parameter `kind` is always bound to `"M"`
|
||||
* at the moment.
|
||||
*/
|
||||
private predicate propertyLookup(Expr prop, AstNode write, string kind) {
|
||||
exists(DataFlow::PropRead prn | prop = prn.getPropertyNameExpr() |
|
||||
count(getAWrite(prn)) = 1 and
|
||||
write = getAWrite(prn) and
|
||||
kind = "M"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `ref` is an identifier that refers to a type declared at `decl`.
|
||||
*/
|
||||
private predicate typeLookup(AstNode ref, AstNode decl, string kind) {
|
||||
exists(TypeAccess typeAccess |
|
||||
ref = typeAccess.getIdentifier() and
|
||||
decl = typeAccess.getTypeName().getADefinition() and
|
||||
kind = "T"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `ref` is the callee name of an invocation of `decl`.
|
||||
*/
|
||||
private predicate typedInvokeLookup(AstNode ref, AstNode decl, string kind) {
|
||||
not variableDefLookup(ref, decl, _) and
|
||||
not propertyLookup(ref, decl, _) and
|
||||
exists(InvokeExpr invoke, Expr callee |
|
||||
callee = invoke.getCallee().getUnderlyingReference() and
|
||||
(ref = callee.(Identifier) or ref = callee.(DotExpr).getPropertyNameExpr()) and
|
||||
decl = invoke.getResolvedCallee() and
|
||||
kind = "M"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `ref` is a JSDoc type annotation referring to a class defined at `decl`.
|
||||
*/
|
||||
private predicate jsdocTypeLookup(JSDocNamedTypeExpr ref, AstNode decl, string kind) {
|
||||
decl = ref.getClass().getAstNode() and
|
||||
kind = "T"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an element, of kind `kind`, that element `e` uses, if any.
|
||||
*
|
||||
* The `kind` is a string representing what kind of use it is:
|
||||
* - `"M"` for function and method calls
|
||||
* - `"T"` for uses of types
|
||||
* - `"V"` for variable accesses
|
||||
* - `"I"` for imports
|
||||
*/
|
||||
cached
|
||||
AstNode definitionOf(Locatable e, string kind) {
|
||||
variableDefLookup(e, result, kind)
|
||||
or
|
||||
// prefer definitions over declarations
|
||||
not variableDefLookup(e, _, _) and variableDeclLookup(e, result, kind)
|
||||
or
|
||||
importLookup(e, result, kind)
|
||||
or
|
||||
propertyLookup(e, result, kind)
|
||||
or
|
||||
typeLookup(e, result, kind)
|
||||
or
|
||||
typedInvokeLookup(e, result, kind)
|
||||
or
|
||||
jsdocTypeLookup(e, result, kind)
|
||||
}
|
||||
16
javascript/ql/lib/localDefinitions.ql
Normal file
16
javascript/ql/lib/localDefinitions.ql
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* @name Jump-to-definition links
|
||||
* @description Generates use-definition pairs that provide the data
|
||||
* for jump-to-definition in the code viewer.
|
||||
* @kind definitions
|
||||
* @id js/ide-jump-to-definition
|
||||
* @tags ide-contextual-queries/local-definitions
|
||||
*/
|
||||
|
||||
import definitions
|
||||
|
||||
external string selectedSourceFile();
|
||||
|
||||
from Locatable e, AstNode def, string kind
|
||||
where def = definitionOf(e, kind) and e.getFile() = getFileBySourceArchiveName(selectedSourceFile())
|
||||
select e, def, kind
|
||||
17
javascript/ql/lib/localReferences.ql
Normal file
17
javascript/ql/lib/localReferences.ql
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @name Find-references links
|
||||
* @description Generates use-definition pairs that provide the data
|
||||
* for find-references in the code viewer.
|
||||
* @kind definitions
|
||||
* @id js/ide-find-references
|
||||
* @tags ide-contextual-queries/local-references
|
||||
*/
|
||||
|
||||
import definitions
|
||||
|
||||
external string selectedSourceFile();
|
||||
|
||||
from Locatable e, AstNode def, string kind
|
||||
where
|
||||
def = definitionOf(e, kind) and def.getFile() = getFileBySourceArchiveName(selectedSourceFile())
|
||||
select e, def, kind
|
||||
28
javascript/ql/lib/printAst.ql
Normal file
28
javascript/ql/lib/printAst.ql
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* @name Print AST
|
||||
* @description Outputs a representation of a file's Abstract Syntax Tree. This
|
||||
* query is used by the VS Code extension.
|
||||
* @id js/print-ast
|
||||
* @kind graph
|
||||
* @tags ide-contextual-queries/print-ast
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import semmle.javascript.PrintAst
|
||||
import definitions
|
||||
|
||||
/**
|
||||
* Gets the source file to generate an AST from.
|
||||
*/
|
||||
external string selectedSourceFile();
|
||||
|
||||
class PrintAstConfigurationOverride extends PrintAstConfiguration {
|
||||
/**
|
||||
* Holds if the location matches the selected file in the VS Code extension and
|
||||
* the element is not a synthetic constructor.
|
||||
*/
|
||||
override predicate shouldPrint(Locatable e, Location l) {
|
||||
super.shouldPrint(e, l) and
|
||||
l.getFile() = getFileBySourceArchiveName(selectedSourceFile())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user