Skip to content

Commit 4bfb048

Browse files
committed
JS: Resolve JSDocLocalTypeAccess to a variable in scope
1 parent 9566265 commit 4bfb048

File tree

1 file changed

+44
-0
lines changed

1 file changed

+44
-0
lines changed

javascript/ql/lib/semmle/javascript/JSDoc.qll

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ class JSDoc extends @jsdoc, Locatable {
3333
result.getTitle() = title
3434
}
3535

36+
/** Gets the element to which this JSDoc comment is attached */
37+
Documentable getDocumentedElement() { result.getDocumentation() = this }
38+
3639
override string toString() { result = this.getComment().toString() }
3740
}
3841

@@ -299,6 +302,41 @@ class JSDocIdentifierTypeExpr extends @jsdoc_identifier_type_expr, JSDocTypeExpr
299302
override predicate isRawFunction() { this.getName() = "Function" }
300303
}
301304

305+
private AstNode getAncestorInScope(Documentable doc) {
306+
any(JSDocLocalTypeAccess t).getJSDocComment() = doc.getDocumentation() and // restrict to cases where we need this
307+
result = doc.getParent()
308+
or
309+
exists(AstNode mid |
310+
mid = getAncestorInScope(doc) and
311+
not mid = any(Scope s).getScopeElement() and
312+
result = mid.getParent()
313+
)
314+
}
315+
316+
private Scope getScope(Documentable doc) { result.getScopeElement() = getAncestorInScope(doc) }
317+
318+
pragma[nomagic]
319+
private predicate shouldResolveName(TopLevel top, string name) {
320+
exists(JSDocLocalTypeAccess access |
321+
access.getName() = name and
322+
access.getTopLevel() = top
323+
)
324+
}
325+
326+
private LexicalName getOwnLocal(Scope scope, string name, DeclarationSpace space) {
327+
scope = result.getScope() and
328+
name = result.getName() and
329+
space = result.getDeclarationSpace() and
330+
shouldResolveName(scope.getScopeElement().getTopLevel(), name) // restrict size of predicate
331+
}
332+
333+
private LexicalName resolveLocal(Scope scope, string name, DeclarationSpace space) {
334+
result = getOwnLocal(scope, name, space)
335+
or
336+
result = resolveLocal(scope.getOuterScope(), name, space) and
337+
not exists(getOwnLocal(scope, name, space))
338+
}
339+
302340
/**
303341
* An unqualified identifier in a JSDoc type expression.
304342
*
@@ -311,6 +349,12 @@ class JSDocIdentifierTypeExpr extends @jsdoc_identifier_type_expr, JSDocTypeExpr
311349
*/
312350
class JSDocLocalTypeAccess extends JSDocIdentifierTypeExpr {
313351
JSDocLocalTypeAccess() { not this = any(JSDocQualifiedTypeAccess a).getNameNode() }
352+
353+
/** Gets a variable, type-name, or namespace that this expression may resolve to. */
354+
LexicalName getALexicalName() {
355+
result =
356+
resolveLocal(getScope(this.getJSDocComment().getDocumentedElement()), this.getName(), _)
357+
}
314358
}
315359

316360
/**

0 commit comments

Comments
 (0)