Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 225e96d

Browse files
committed
JS: Resolve qualified name of JSDoc types
1 parent 13da242 commit 225e96d

1 file changed

Lines changed: 106 additions & 1 deletion

File tree

javascript/ql/src/semmle/javascript/JSDoc.qll

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,13 @@ class JSDocTypeExpr extends @jsdoc_type_expr, JSDocTypeExprParent, TypeAnnotatio
132132
}
133133

134134
override Stmt getEnclosingStmt() {
135-
result.getDocumentation() = getJSDocComment()
135+
exists(Documentable astNode | astNode.getDocumentation() = getJSDocComment() |
136+
result = astNode
137+
or
138+
result = astNode.(ExprOrType).getEnclosingStmt()
139+
or
140+
result = astNode.(Property).getObjectExpr().getEnclosingStmt()
141+
)
136142
}
137143

138144
override StmtContainer getContainer() { result = getEnclosingStmt().getContainer() }
@@ -205,7 +211,44 @@ class JSDocNamedTypeExpr extends @jsdoc_named_type_expr, JSDocTypeExpr {
205211

206212
override predicate isRawFunction() { getName() = "Function" }
207213

214+
/**
215+
* Holds if this name consists of the unqualified name `prefix`
216+
* followed by a (possibly empty) `suffix`.
217+
*
218+
* For example:
219+
* - `foo.bar.Baz` has prefix `foo` and suffix `.bar.Baz`.
220+
* - `Baz` has prefix `Baz` and an empty suffix.
221+
*/
222+
predicate hasNameParts(string prefix, string suffix) {
223+
exists(string regex, string name | regex = "([^.]+)(.*)" |
224+
name = getName() and
225+
prefix = name.regexpCapture(regex, 1) and
226+
suffix = name.regexpCapture(regex, 2)
227+
)
228+
}
229+
230+
pragma[noinline]
231+
pragma[nomagic]
232+
private predicate hasNamePartsAndEnv(string prefix, string suffix, JSDoc::Environment env) {
233+
// Force join ordering
234+
hasNameParts(prefix, suffix) and
235+
env.isContainerInScope(getContainer())
236+
}
237+
238+
/**
239+
* Gets the qualified name of this name by resolving its prefix, if any.
240+
*/
241+
private string resolvedName() {
242+
exists(string prefix, string suffix, JSDoc::Environment env |
243+
hasNamePartsAndEnv(prefix, suffix, env) and
244+
result = env.resolveAlias(prefix) + suffix
245+
)
246+
}
247+
208248
override predicate hasQualifiedName(string globalName) {
249+
globalName = resolvedName()
250+
or
251+
not exists(resolvedName()) and
209252
globalName = getName()
210253
}
211254
}
@@ -353,3 +396,65 @@ class JSDocError extends @jsdoc_error {
353396
/** Gets a textual representation of this element. */
354397
string toString() { jsdoc_errors(this, _, _, result) }
355398
}
399+
400+
module JSDoc {
401+
/**
402+
* A statement container which may declare JSDoc name aliases.
403+
*/
404+
class Environment extends StmtContainer {
405+
/**
406+
* Gets the fully qualified name aliased by the given unqualified name
407+
* within this container.
408+
*/
409+
string resolveAlias(string alias) {
410+
result = GlobalAccessPath::getAccessPath(getNodeFromAlias(alias))
411+
}
412+
413+
/**
414+
* Gets a data flow node from which the type name `alias` should
415+
* be resolved.
416+
*/
417+
DataFlow::Node getNodeFromAlias(string alias) {
418+
isPrefixUsedInFile(alias, getFile()) and // Restrict size of predicate
419+
exists(VarDef def, VarRef ref |
420+
def.getContainer() = this and
421+
ref = def.getTarget().(BindingPattern).getABindingVarRef() and
422+
ref.getName() = alias
423+
|
424+
exists(PropertyPattern p |
425+
ref = p.getValuePattern() and
426+
result.getAstNode() = p
427+
)
428+
or
429+
result = DataFlow::valueNode(def.(Stmt))
430+
or
431+
ref = def.getTarget() and
432+
result = def.getSource().flow()
433+
)
434+
}
435+
436+
/**
437+
* Holds if the alises declared in this environment should be in scope
438+
* within the given container.
439+
*
440+
* Specifically, this holds if this environment declares at least one
441+
* alias and is an ancestor of `container`.
442+
*/
443+
final predicate isContainerInScope(StmtContainer container) {
444+
exists(resolveAlias(_)) and // restrict size of predicate
445+
container = this
446+
or
447+
isContainerInScope(container.getEnclosingContainer())
448+
}
449+
}
450+
451+
/**
452+
* Holds if a JSDoc type in `f` referenced the given local name.
453+
*/
454+
private predicate isPrefixUsedInFile(string name, File f) {
455+
exists(JSDocNamedTypeExpr t |
456+
t.hasNameParts(name, _) and
457+
t.getFile() = f
458+
)
459+
}
460+
}

0 commit comments

Comments
 (0)