mirror of
https://github.com/github/codeql.git
synced 2026-04-29 18:55:14 +02:00
JS: Resolve qualified name of JSDoc types
This commit is contained in:
@@ -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,7 +211,44 @@ 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()
|
||||
}
|
||||
}
|
||||
@@ -353,3 +396,65 @@ 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) {
|
||||
isPrefixUsedInFile(alias, getFile()) and // Restrict size of predicate
|
||||
exists(VarDef def, VarRef ref |
|
||||
def.getContainer() = this and
|
||||
ref = def.getTarget().(BindingPattern).getABindingVarRef() and
|
||||
ref.getName() = alias
|
||||
|
|
||||
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()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the alises 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())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a JSDoc type in `f` referenced the given local name.
|
||||
*/
|
||||
private predicate isPrefixUsedInFile(string name, File f) {
|
||||
exists(JSDocNamedTypeExpr t |
|
||||
t.hasNameParts(name, _) and
|
||||
t.getFile() = f
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user