mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
JS: Separate JSDoc qualified names into individual identifiers
This commit is contained in:
@@ -2,12 +2,12 @@ package com.semmle.js.ast.jsdoc;
|
||||
|
||||
import com.semmle.js.ast.SourceLocation;
|
||||
|
||||
/** A named JSDoc type. */
|
||||
public class NameExpression extends JSDocTypeExpression {
|
||||
/** An identifier in a JSDoc type. */
|
||||
public class Identifier extends JSDocTypeExpression {
|
||||
private final String name;
|
||||
|
||||
public NameExpression(SourceLocation loc, String name) {
|
||||
super(loc, "NameExpression");
|
||||
public Identifier(SourceLocation loc, String name) {
|
||||
super(loc, "Identifier");
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.semmle.js.ast.jsdoc;
|
||||
|
||||
import com.semmle.js.ast.SourceLocation;
|
||||
|
||||
/** A qualified name in a JSDoc type. */
|
||||
public class QualifiedNameExpression extends JSDocTypeExpression {
|
||||
private final JSDocTypeExpression base;
|
||||
private final Identifier name;
|
||||
|
||||
public QualifiedNameExpression(SourceLocation loc, JSDocTypeExpression base, Identifier name) {
|
||||
super(loc, "QualifiedNameExpression");
|
||||
this.base = base;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Visitor v) {
|
||||
v.visit(this);
|
||||
}
|
||||
|
||||
/** Returns the expression on the left side of the dot character. */
|
||||
public JSDocTypeExpression getBase() {
|
||||
return base;
|
||||
}
|
||||
|
||||
/** Returns the identifier on the right-hand side of the dot character. */
|
||||
public Identifier getNameNode() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String pp() {
|
||||
return base.pp() + "." + name.pp();
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,9 @@ public interface Visitor {
|
||||
|
||||
public void visit(JSDocTag nd);
|
||||
|
||||
public void visit(NameExpression nd);
|
||||
public void visit(Identifier nd);
|
||||
|
||||
public void visit(QualifiedNameExpression nd);
|
||||
|
||||
public void visit(NullableLiteral nd);
|
||||
|
||||
|
||||
@@ -9,13 +9,14 @@ import com.semmle.js.ast.jsdoc.JSDocComment;
|
||||
import com.semmle.js.ast.jsdoc.JSDocElement;
|
||||
import com.semmle.js.ast.jsdoc.JSDocTag;
|
||||
import com.semmle.js.ast.jsdoc.JSDocTypeExpression;
|
||||
import com.semmle.js.ast.jsdoc.NameExpression;
|
||||
import com.semmle.js.ast.jsdoc.Identifier;
|
||||
import com.semmle.js.ast.jsdoc.NonNullableType;
|
||||
import com.semmle.js.ast.jsdoc.NullLiteral;
|
||||
import com.semmle.js.ast.jsdoc.NullableLiteral;
|
||||
import com.semmle.js.ast.jsdoc.NullableType;
|
||||
import com.semmle.js.ast.jsdoc.OptionalType;
|
||||
import com.semmle.js.ast.jsdoc.ParameterType;
|
||||
import com.semmle.js.ast.jsdoc.QualifiedNameExpression;
|
||||
import com.semmle.js.ast.jsdoc.RecordType;
|
||||
import com.semmle.js.ast.jsdoc.RestType;
|
||||
import com.semmle.js.ast.jsdoc.TypeApplication;
|
||||
@@ -42,7 +43,7 @@ public class JSDocExtractor {
|
||||
jsdocTypeExprKinds.put("UndefinedLiteral", 2);
|
||||
jsdocTypeExprKinds.put("NullableLiteral", 3);
|
||||
jsdocTypeExprKinds.put("VoidLiteral", 4);
|
||||
jsdocTypeExprKinds.put("NameExpression", 5);
|
||||
jsdocTypeExprKinds.put("Identifier", 5);
|
||||
jsdocTypeExprKinds.put("TypeApplication", 6);
|
||||
jsdocTypeExprKinds.put("NullableType", 7);
|
||||
jsdocTypeExprKinds.put("NonNullableType", 8);
|
||||
@@ -52,6 +53,7 @@ public class JSDocExtractor {
|
||||
jsdocTypeExprKinds.put("FunctionType", 12);
|
||||
jsdocTypeExprKinds.put("OptionalType", 13);
|
||||
jsdocTypeExprKinds.put("RestType", 14);
|
||||
jsdocTypeExprKinds.put("QualifiedNameExpression", 15);
|
||||
}
|
||||
|
||||
private final TrapWriter trapwriter;
|
||||
@@ -122,10 +124,17 @@ public class JSDocExtractor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(NameExpression nd) {
|
||||
public void visit(Identifier nd) {
|
||||
visit((JSDocTypeExpression) nd);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(QualifiedNameExpression nd) {
|
||||
Label label = visit((JSDocTypeExpression) nd);
|
||||
visit(nd.getBase(), label, 0);
|
||||
visit(nd.getNameNode(), label, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(NullableLiteral nd) {
|
||||
visit((JSDocTypeExpression) nd);
|
||||
|
||||
@@ -10,13 +10,14 @@ import com.semmle.js.ast.jsdoc.FunctionType;
|
||||
import com.semmle.js.ast.jsdoc.JSDocComment;
|
||||
import com.semmle.js.ast.jsdoc.JSDocTag;
|
||||
import com.semmle.js.ast.jsdoc.JSDocTypeExpression;
|
||||
import com.semmle.js.ast.jsdoc.NameExpression;
|
||||
import com.semmle.js.ast.jsdoc.Identifier;
|
||||
import com.semmle.js.ast.jsdoc.NonNullableType;
|
||||
import com.semmle.js.ast.jsdoc.NullLiteral;
|
||||
import com.semmle.js.ast.jsdoc.NullableLiteral;
|
||||
import com.semmle.js.ast.jsdoc.NullableType;
|
||||
import com.semmle.js.ast.jsdoc.OptionalType;
|
||||
import com.semmle.js.ast.jsdoc.ParameterType;
|
||||
import com.semmle.js.ast.jsdoc.QualifiedNameExpression;
|
||||
import com.semmle.js.ast.jsdoc.RecordType;
|
||||
import com.semmle.js.ast.jsdoc.RestType;
|
||||
import com.semmle.js.ast.jsdoc.TypeApplication;
|
||||
@@ -827,10 +828,16 @@ public class JSDocParser {
|
||||
}
|
||||
|
||||
private JSDocTypeExpression parseNameExpression() throws ParseError {
|
||||
Object name = value;
|
||||
SourceLocation loc = loc();
|
||||
expect(Token.NAME);
|
||||
return finishNode(new NameExpression(loc, name.toString()));
|
||||
// Hacky initial implementation with wrong locations
|
||||
String[] parts = value.toString().split("\\.");
|
||||
JSDocTypeExpression node = finishNode(new Identifier(loc, parts[0]));
|
||||
for (int i = 1; i < parts.length; i++) {
|
||||
Identifier memberName = finishNode(new Identifier(loc, parts[i]));
|
||||
node = finishNode(new QualifiedNameExpression(loc, node, memberName));
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
// TypeExpressionList :=
|
||||
@@ -923,14 +930,14 @@ public class JSDocParser {
|
||||
|
||||
SourceLocation loc = loc();
|
||||
expr = parseTypeExpression();
|
||||
if (expr instanceof NameExpression && token == Token.COLON) {
|
||||
if (expr instanceof Identifier && token == Token.COLON) {
|
||||
// Identifier ':' TypeExpression
|
||||
consume(Token.COLON);
|
||||
expr =
|
||||
finishNode(
|
||||
new ParameterType(
|
||||
new SourceLocation(loc),
|
||||
((NameExpression) expr).getName(),
|
||||
((Identifier) expr).getName(),
|
||||
parseTypeExpression()));
|
||||
}
|
||||
if (token == Token.EQUAL) {
|
||||
@@ -1106,7 +1113,7 @@ public class JSDocParser {
|
||||
consume(Token.RBRACK, "expected an array-style type declaration (' + value + '[])");
|
||||
List<JSDocTypeExpression> expressions = new ArrayList<>();
|
||||
expressions.add(expr);
|
||||
NameExpression nameExpr = finishNode(new NameExpression(new SourceLocation(loc), "Array"));
|
||||
Identifier nameExpr = finishNode(new Identifier(new SourceLocation(loc), "Array"));
|
||||
return finishNode(new TypeApplication(loc, nameExpr, expressions));
|
||||
}
|
||||
|
||||
@@ -1527,9 +1534,9 @@ public class JSDocParser {
|
||||
// fixed at the end
|
||||
if (isParamTitle(this._title)
|
||||
&& this._tag.type != null
|
||||
&& this._tag.type instanceof NameExpression) {
|
||||
this._extra_name = ((NameExpression) this._tag.type).getName();
|
||||
this._tag.name = ((NameExpression) this._tag.type).getName();
|
||||
&& this._tag.type instanceof Identifier) {
|
||||
this._extra_name = ((Identifier) this._tag.type).getName();
|
||||
this._tag.name = ((Identifier) this._tag.type).getName();
|
||||
this._tag.type = null;
|
||||
} else {
|
||||
if (!this.addError("Missing or invalid tag name")) {
|
||||
@@ -1645,7 +1652,7 @@ public class JSDocParser {
|
||||
Position start = new Position(_tag.startLine, _tag.startColumn, _tag.startColumn);
|
||||
Position end = new Position(_tag.startLine, _tag.startColumn, _tag.startColumn);
|
||||
SourceLocation loc = new SourceLocation(_extra_name, start, end);
|
||||
this._tag.type = new NameExpression(loc, _extra_name);
|
||||
this._tag.type = new Identifier(loc, _extra_name);
|
||||
}
|
||||
this._tag.name = null;
|
||||
|
||||
|
||||
@@ -261,17 +261,14 @@ class JSDocVoidTypeExpr extends @jsdoc_void_type_expr, JSDocTypeExpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* A type expression referring to a named type.
|
||||
* An identifier in a JSDoc type expression, such as `Object` or `string`.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* string
|
||||
* Object
|
||||
* ```
|
||||
* Note that qualified names consist of multiple identifier nodes.
|
||||
*/
|
||||
class JSDocNamedTypeExpr extends @jsdoc_named_type_expr, JSDocTypeExpr {
|
||||
/** Gets the name of the type the expression refers to. */
|
||||
class JSDocIdentifierTypeExpr extends @jsdoc_identifier_type_expr, JSDocTypeExpr {
|
||||
/**
|
||||
* Gets the name of the identifier.
|
||||
*/
|
||||
string getName() { result = this.toString() }
|
||||
|
||||
override predicate isString() { this.getName() = "string" }
|
||||
@@ -300,6 +297,71 @@ class JSDocNamedTypeExpr extends @jsdoc_named_type_expr, JSDocTypeExpr {
|
||||
}
|
||||
|
||||
override predicate isRawFunction() { this.getName() = "Function" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An unqualified identifier in a JSDoc type expression.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* string
|
||||
* Object
|
||||
* ```
|
||||
*/
|
||||
class JSDocLocalTypeAccess extends JSDocIdentifierTypeExpr {
|
||||
JSDocLocalTypeAccess() { not this = any(JSDocQualifiedTypeAccess a).getNameNode() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A qualified type name in a JSDoc type expression, such as `X.Y`.
|
||||
*/
|
||||
class JSDocQualifiedTypeAccess extends @jsdoc_qualified_type_expr, JSDocTypeExpr {
|
||||
/**
|
||||
* Gets the base of this access, such as the `X` in `X.Y`.
|
||||
*/
|
||||
JSDocTypeExpr getBase() { result = this.getChild(0) }
|
||||
|
||||
/**
|
||||
* Gets the node naming the member being accessed, such as the `Y` node in `X.Y`.
|
||||
*/
|
||||
JSDocIdentifierTypeExpr getNameNode() { result = this.getChild(1) }
|
||||
|
||||
/**
|
||||
* Gets the name being accessed, such as `Y` in `X.Y`.
|
||||
*/
|
||||
string getName() { result = this.getNameNode().getName() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A type expression referring to a named type.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* string
|
||||
* Object
|
||||
* Namespace.Type
|
||||
* ```
|
||||
*/
|
||||
class JSDocNamedTypeExpr extends JSDocTypeExpr {
|
||||
JSDocNamedTypeExpr() {
|
||||
this instanceof JSDocLocalTypeAccess
|
||||
or
|
||||
this instanceof JSDocQualifiedTypeAccess
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name directly as it appears in this type, including any qualifiers.
|
||||
*
|
||||
* For example, for `X.Y` this gets the string `"X.Y"`.
|
||||
*/
|
||||
string getRawName() { result = this.toString() }
|
||||
|
||||
/**
|
||||
* DEPRECATED. Use `getRawName()` instead.
|
||||
*/
|
||||
deprecated string getName() { result = this.toString() }
|
||||
|
||||
/**
|
||||
* Holds if this name consists of the unqualified name `prefix`
|
||||
@@ -311,7 +373,7 @@ class JSDocNamedTypeExpr extends @jsdoc_named_type_expr, JSDocTypeExpr {
|
||||
*/
|
||||
predicate hasNameParts(string prefix, string suffix) {
|
||||
exists(string regex, string name | regex = "([^.]+)(.*)" |
|
||||
name = this.getName() and
|
||||
name = this.getRawName() and
|
||||
prefix = name.regexpCapture(regex, 1) and
|
||||
suffix = name.regexpCapture(regex, 2)
|
||||
)
|
||||
@@ -340,7 +402,7 @@ class JSDocNamedTypeExpr extends @jsdoc_named_type_expr, JSDocTypeExpr {
|
||||
globalName = this.resolvedName()
|
||||
or
|
||||
not exists(this.resolvedName()) and
|
||||
globalName = this.getName()
|
||||
globalName = this.getRawName()
|
||||
}
|
||||
|
||||
override DataFlow::ClassNode getClass() {
|
||||
|
||||
@@ -1001,7 +1001,7 @@ case @jsdoc_type_expr.kind of
|
||||
| 2 = @jsdoc_undefined_type_expr
|
||||
| 3 = @jsdoc_unknown_type_expr
|
||||
| 4 = @jsdoc_void_type_expr
|
||||
| 5 = @jsdoc_named_type_expr
|
||||
| 5 = @jsdoc_identifier_type_expr
|
||||
| 6 = @jsdoc_applied_type_expr
|
||||
| 7 = @jsdoc_nullable_type_expr
|
||||
| 8 = @jsdoc_non_nullable_type_expr
|
||||
@@ -1011,6 +1011,7 @@ case @jsdoc_type_expr.kind of
|
||||
| 12 = @jsdoc_function_type_expr
|
||||
| 13 = @jsdoc_optional_type_expr
|
||||
| 14 = @jsdoc_rest_type_expr
|
||||
| 15 = @jsdoc_qualified_type_expr
|
||||
;
|
||||
|
||||
#keyset[id, idx]
|
||||
|
||||
Reference in New Issue
Block a user