Skip to content

Commit 5c80716

Browse files
authored
Merge branch 'main' into qldoc-nodes
2 parents 0b74535 + 33ed98e commit 5c80716

15 files changed

Lines changed: 692 additions & 110 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

extractor/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ edition = "2018"
1010
flate2 = "1.0"
1111
node-types = { path = "../node-types" }
1212
tree-sitter = "0.19"
13-
tree-sitter-ql = { git = "https://github.com/tausbn/tree-sitter-ql.git", rev = "f95bfc0bc4f2cda7f6f0bf608360fdee7574b003" }
13+
tree-sitter-ql = { git = "https://github.com/tausbn/tree-sitter-ql.git", rev = "ef46f6c4c076cc06bb206686794cec26b5c544a7" }
1414
clap = "2.33"
1515
tracing = "0.1"
1616
tracing-subscriber = { version = "0.2", features = ["env-filter"] }

generator/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ edition = "2018"
1010
node-types = { path = "../node-types" }
1111
tracing = "0.1"
1212
tracing-subscriber = { version = "0.2", features = ["env-filter"] }
13-
tree-sitter-ql = { git = "https://github.com/tausbn/tree-sitter-ql.git", rev = "f95bfc0bc4f2cda7f6f0bf608360fdee7574b003" }
13+
tree-sitter-ql = { git = "https://github.com/tausbn/tree-sitter-ql.git", rev = "ef46f6c4c076cc06bb206686794cec26b5c544a7" }

generator/src/ql.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ impl<'a> fmt::Display for TopLevel<'a> {
1717

1818
#[derive(Clone, Eq, PartialEq, Hash)]
1919
pub struct Class<'a> {
20+
pub qldoc: Option<String>,
2021
pub name: &'a str,
2122
pub is_abstract: bool,
2223
pub supertypes: BTreeSet<Type<'a>>,
@@ -26,6 +27,9 @@ pub struct Class<'a> {
2627

2728
impl<'a> fmt::Display for Class<'a> {
2829
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
30+
if let Some(qldoc) = &self.qldoc {
31+
write!(f, "/** {} */", qldoc)?;
32+
}
2933
if self.is_abstract {
3034
write!(f, "abstract ")?;
3135
}
@@ -43,6 +47,7 @@ impl<'a> fmt::Display for Class<'a> {
4347
f,
4448
" {}\n",
4549
Predicate {
50+
qldoc: None,
4651
name: self.name.clone(),
4752
overridden: false,
4853
return_type: None,
@@ -176,6 +181,7 @@ impl<'a> fmt::Display for Expression<'a> {
176181

177182
#[derive(Clone, Eq, PartialEq, Hash)]
178183
pub struct Predicate<'a> {
184+
pub qldoc: Option<String>,
179185
pub name: &'a str,
180186
pub overridden: bool,
181187
pub return_type: Option<Type<'a>>,
@@ -185,6 +191,9 @@ pub struct Predicate<'a> {
185191

186192
impl<'a> fmt::Display for Predicate<'a> {
187193
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
194+
if let Some(qldoc) = &self.qldoc {
195+
write!(f, "/** {} */", qldoc)?;
196+
}
188197
if self.overridden {
189198
write!(f, "override ")?;
190199
}

generator/src/ql_gen.rs

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ pub fn write(language: &Language, classes: &[ql::TopLevel]) -> std::io::Result<(
2929
fn create_ast_node_class<'a>() -> ql::Class<'a> {
3030
// Default implementation of `toString` calls `this.getAPrimaryQlClass()`
3131
let to_string = ql::Predicate {
32+
qldoc: Some(String::from(
33+
"Gets a string representation of this element.",
34+
)),
3235
name: "toString",
3336
overridden: false,
3437
return_type: Some(ql::Type::String),
@@ -42,11 +45,20 @@ fn create_ast_node_class<'a>() -> ql::Class<'a> {
4245
)),
4346
),
4447
};
45-
let get_location =
46-
create_none_predicate("getLocation", false, Some(ql::Type::Normal("Location")));
47-
let get_a_field_or_child =
48-
create_none_predicate("getAFieldOrChild", false, Some(ql::Type::Normal("AstNode")));
48+
let get_location = create_none_predicate(
49+
Some(String::from("Gets the location of this element.")),
50+
"getLocation",
51+
false,
52+
Some(ql::Type::Normal("Location")),
53+
);
54+
let get_a_field_or_child = create_none_predicate(
55+
Some(String::from("Gets a field or child node of this node.")),
56+
"getAFieldOrChild",
57+
false,
58+
Some(ql::Type::Normal("AstNode")),
59+
);
4960
let get_parent = ql::Predicate {
61+
qldoc: Some(String::from("Gets the parent of this element.")),
5062
name: "getParent",
5163
overridden: false,
5264
return_type: Some(ql::Type::Normal("AstNode")),
@@ -61,6 +73,9 @@ fn create_ast_node_class<'a>() -> ql::Class<'a> {
6173
),
6274
};
6375
let get_parent_index = ql::Predicate {
76+
qldoc: Some(String::from(
77+
"Gets the index of this node among the children of its parent.",
78+
)),
6479
name: "getParentIndex",
6580
overridden: false,
6681
return_type: Some(ql::Type::Int),
@@ -75,6 +90,9 @@ fn create_ast_node_class<'a>() -> ql::Class<'a> {
7590
),
7691
};
7792
let get_a_primary_ql_class = ql::Predicate {
93+
qldoc: Some(String::from(
94+
"Gets the name of the primary QL class for this element.",
95+
)),
7896
name: "getAPrimaryQlClass",
7997
overridden: false,
8098
return_type: Some(ql::Type::String),
@@ -85,6 +103,7 @@ fn create_ast_node_class<'a>() -> ql::Class<'a> {
85103
),
86104
};
87105
ql::Class {
106+
qldoc: Some(String::from("The base class for all AST nodes")),
88107
name: "AstNode",
89108
is_abstract: false,
90109
supertypes: vec![ql::Type::AtType("ast_node")].into_iter().collect(),
@@ -103,20 +122,25 @@ fn create_ast_node_class<'a>() -> ql::Class<'a> {
103122
fn create_token_class<'a>() -> ql::Class<'a> {
104123
let tokeninfo_arity = 6;
105124
let get_value = ql::Predicate {
125+
qldoc: Some(String::from("Gets the value of this token.")),
106126
name: "getValue",
107127
overridden: false,
108128
return_type: Some(ql::Type::String),
109129
formal_parameters: vec![],
110130
body: create_get_field_expr_for_column_storage("result", "tokeninfo", 3, tokeninfo_arity),
111131
};
112132
let get_location = ql::Predicate {
133+
qldoc: Some(String::from("Gets the location of this token.")),
113134
name: "getLocation",
114135
overridden: true,
115136
return_type: Some(ql::Type::Normal("Location")),
116137
formal_parameters: vec![],
117138
body: create_get_field_expr_for_column_storage("result", "tokeninfo", 4, tokeninfo_arity),
118139
};
119140
let to_string = ql::Predicate {
141+
qldoc: Some(String::from(
142+
"Gets a string representation of this element.",
143+
)),
120144
name: "toString",
121145
overridden: true,
122146
return_type: Some(ql::Type::String),
@@ -127,6 +151,7 @@ fn create_token_class<'a>() -> ql::Class<'a> {
127151
),
128152
};
129153
ql::Class {
154+
qldoc: Some(String::from("A token.")),
130155
name: "Token",
131156
is_abstract: false,
132157
supertypes: vec![ql::Type::AtType("token"), ql::Type::Normal("AstNode")]
@@ -148,6 +173,7 @@ fn create_reserved_word_class<'a>() -> ql::Class<'a> {
148173
let class_name = "ReservedWord";
149174
let get_a_primary_ql_class = create_get_a_primary_ql_class(&class_name);
150175
ql::Class {
176+
qldoc: Some(String::from("A reserved word.")),
151177
name: class_name,
152178
is_abstract: false,
153179
supertypes: vec![ql::Type::AtType(db_name), ql::Type::Normal("Token")]
@@ -160,11 +186,13 @@ fn create_reserved_word_class<'a>() -> ql::Class<'a> {
160186

161187
/// Creates a predicate whose body is `none()`.
162188
fn create_none_predicate<'a>(
189+
qldoc: Option<String>,
163190
name: &'a str,
164191
overridden: bool,
165192
return_type: Option<ql::Type<'a>>,
166193
) -> ql::Predicate<'a> {
167194
ql::Predicate {
195+
qldoc: qldoc,
168196
name: name,
169197
overridden,
170198
return_type,
@@ -177,6 +205,9 @@ fn create_none_predicate<'a>(
177205
/// name.
178206
fn create_get_a_primary_ql_class<'a>(class_name: &'a str) -> ql::Predicate<'a> {
179207
ql::Predicate {
208+
qldoc: Some(String::from(
209+
"Gets the name of the primary QL class for this element.",
210+
)),
180211
name: "getAPrimaryQlClass",
181212
overridden: true,
182213
return_type: Some(ql::Type::String),
@@ -196,6 +227,7 @@ fn create_get_a_primary_ql_class<'a>(class_name: &'a str) -> ql::Predicate<'a> {
196227
/// `arity` - the total number of columns in the table
197228
fn create_get_location_predicate<'a>(def_table: &'a str, arity: usize) -> ql::Predicate<'a> {
198229
ql::Predicate {
230+
qldoc: Some(String::from("Gets the location of this element.")),
199231
name: "getLocation",
200232
overridden: true,
201233
return_type: Some(ql::Type::Normal("Location")),
@@ -220,6 +252,7 @@ fn create_get_location_predicate<'a>(def_table: &'a str, arity: usize) -> ql::Pr
220252
/// `def_table` - the name of the table that defines the entity and its text.
221253
fn create_get_text_predicate<'a>(def_table: &'a str) -> ql::Predicate<'a> {
222254
ql::Predicate {
255+
qldoc: Some(String::from("Gets the text content of this element.")),
223256
name: "getText",
224257
overridden: false,
225258
return_type: Some(ql::Type::String),
@@ -419,6 +452,10 @@ fn create_field_getters<'a>(
419452
};
420453
(
421454
ql::Predicate {
455+
qldoc: field
456+
.name
457+
.as_ref()
458+
.map(|name| format!("Gets the node corresponding to the field `{}`.", name)),
422459
name: &field.getter_name,
423460
overridden: false,
424461
return_type,
@@ -456,6 +493,7 @@ pub fn convert_nodes<'a>(nodes: &'a node_types::NodeTypeMap) -> Vec<ql::TopLevel
456493
supertypes.insert(ql::Type::AtType(&node.dbscheme_name));
457494
supertypes.insert(ql::Type::Normal("Token"));
458495
classes.push(ql::TopLevel::Class(ql::Class {
496+
qldoc: Some(format!("A class representing `{}` tokens.", type_name.kind)),
459497
name: &node.ql_class_name,
460498
is_abstract: false,
461499
supertypes,
@@ -468,6 +506,7 @@ pub fn convert_nodes<'a>(nodes: &'a node_types::NodeTypeMap) -> Vec<ql::TopLevel
468506
// It's a tree-sitter supertype node, so we're wrapping a dbscheme
469507
// union type.
470508
classes.push(ql::TopLevel::Class(ql::Class {
509+
qldoc: None,
471510
name: &node.ql_class_name,
472511
is_abstract: false,
473512
supertypes: vec![
@@ -501,6 +540,7 @@ pub fn convert_nodes<'a>(nodes: &'a node_types::NodeTypeMap) -> Vec<ql::TopLevel
501540

502541
let main_class_name = &node.ql_class_name;
503542
let mut main_class = ql::Class {
543+
qldoc: Some(format!("A class representing `{}` nodes.", type_name.kind)),
504544
name: &main_class_name,
505545
is_abstract: false,
506546
supertypes: vec![
@@ -543,6 +583,7 @@ pub fn convert_nodes<'a>(nodes: &'a node_types::NodeTypeMap) -> Vec<ql::TopLevel
543583
}
544584

545585
main_class.predicates.push(ql::Predicate {
586+
qldoc: Some(String::from("Gets a field or child node of this node.")),
546587
name: "getAFieldOrChild",
547588
overridden: true,
548589
return_type: Some(ql::Type::Normal("AstNode")),

ql/src/codeql_ql/ast/Ast.qll

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,15 @@ class AstNode extends TAstNode {
5959

6060
/** Gets the QLDoc comment for this AST node, if any. */
6161
QLDoc getQLDoc() { none() }
62+
63+
/**
64+
* Gets the predicate that contains this AST node.
65+
*/
66+
pragma[noinline]
67+
Predicate getEnclosingPredicate() {
68+
not this instanceof Predicate and
69+
toGenerated(result) = toGenerated(this).getParent+()
70+
}
6271
}
6372

6473
/** A toplevel QL program, i.e. a file. */
@@ -191,7 +200,14 @@ class Predicate extends TPredicate, AstNode, Declaration {
191200
/**
192201
* Gets the number of parameters.
193202
*/
194-
int getArity() { result = count(getParameter(_)) }
203+
int getArity() {
204+
not this.(ClasslessPredicate).getAlias() instanceof PredicateExpr and
205+
result = count(getParameter(_))
206+
or
207+
exists(PredicateExpr alias | alias = this.(ClasslessPredicate).getAlias() |
208+
result = alias.getArity()
209+
)
210+
}
195211

196212
/**
197213
* Gets the return type (if any) of the predicate.
@@ -1074,6 +1090,21 @@ class Float extends Literal {
10741090
float getValue() { result = lit.getChild().(Generated::Float).getValue().toFloat() }
10751091
}
10761092

1093+
/** A boolean literal */
1094+
class Boolean extends Literal {
1095+
Generated::Bool bool;
1096+
1097+
Boolean() { lit.getChild() = bool }
1098+
1099+
/** Holds if the value is `true` */
1100+
predicate isTrue() { bool.getChild() instanceof Generated::True }
1101+
1102+
/** Holds if the value is `false` */
1103+
predicate isFalse() { bool.getChild() instanceof Generated::False }
1104+
1105+
override string getAPrimaryQlClass() { result = "Boolean" }
1106+
}
1107+
10771108
/** A comparison symbol, such as `"<"` or `"="`. */
10781109
class ComparisonSymbol extends string {
10791110
ComparisonSymbol() {
@@ -1399,6 +1430,19 @@ class ExprAggregate extends TExprAggregate, Expr {
13991430
pred = indexedMember("getOrderBy", i) and result = this.getOrderBy(i)
14001431
)
14011432
}
1433+
1434+
override Type getType() {
1435+
exists(PrimitiveType prim | prim = result |
1436+
kind.regexpMatch("(strict)?count|sum|min|max|rank") and
1437+
result.getName() = "int"
1438+
or
1439+
kind.regexpMatch("(strict)?concat") and
1440+
result.getName() = "string"
1441+
)
1442+
or
1443+
not kind = ["count", "strictcount"] and
1444+
result = getExpr(0).getType()
1445+
}
14021446
}
14031447

14041448
/** An aggregate expression, such as `count` or `sum`. */
@@ -1449,12 +1493,21 @@ class Aggregate extends TAggregate, Expr {
14491493

14501494
override string getAPrimaryQlClass() { result = "Aggregate[" + kind + "]" }
14511495

1452-
override PrimitiveType getType() {
1453-
kind.regexpMatch("(strict)?count|sum|min|max|rank") and
1454-
result.getName() = "int"
1496+
override Type getType() {
1497+
exists(PrimitiveType prim | prim = result |
1498+
kind.regexpMatch("(strict)?count|sum|min|max|rank") and
1499+
result.getName() = "int"
1500+
or
1501+
kind.regexpMatch("(strict)?concat") and
1502+
result.getName() = "string"
1503+
)
1504+
or
1505+
kind = ["any", "min", "max"] and
1506+
not exists(getExpr(_)) and
1507+
result = getArgument(0).getTypeExpr().getResolvedType()
14551508
or
1456-
kind.regexpMatch("(strict)?concat") and
1457-
result.getName() = "string"
1509+
not kind = ["count", "strictcount"] and
1510+
result = getExpr(0).getType()
14581511
}
14591512

14601513
override AstNode getAChild(string pred) {
@@ -1583,6 +1636,13 @@ class ThisAccess extends Identifier {
15831636
override string getAPrimaryQlClass() { result = "ThisAccess" }
15841637
}
15851638

1639+
/** A use of `super`. */
1640+
class Super extends TSuper, Expr {
1641+
Super() { this = TSuper(_) }
1642+
1643+
override string getAPrimaryQlClass() { result = "SuperAccess" }
1644+
}
1645+
15861646
/** An access to `result`. */
15871647
class ResultAccess extends Identifier {
15881648
ResultAccess() { any(Generated::Result r).getParent() = id }

ql/src/codeql_ql/ast/internal/AstNodes.qll

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ newtype TAstNode =
2525
TExprAggregate(Generated::Aggregate agg) {
2626
agg.getChild(_) instanceof Generated::ExprAggregateBody
2727
} or
28+
TSuper(Generated::SuperRef sup) or
2829
TIdentifier(Generated::Variable var) or
2930
TAsExpr(Generated::AsExpr asExpr) { asExpr.getChild(1) instanceof Generated::VarName } or
3031
TPredicateCall(Generated::CallOrUnqualAggExpr call) or
@@ -64,7 +65,7 @@ class TBinOpExpr = TAddSubExpr or TMulDivModExpr;
6465

6566
class TExpr =
6667
TBinOpExpr or TLiteral or TAggregate or TExprAggregate or TIdentifier or TInlineCast or TCall or
67-
TUnaryExpr or TExprAnnotation or TDontCare or TRange or TSet or TAsExpr;
68+
TUnaryExpr or TExprAnnotation or TDontCare or TRange or TSet or TAsExpr or TSuper;
6869

6970
class TCall = TPredicateCall or TMemberCall or TNoneCall or TAnyCall;
7071

@@ -157,6 +158,8 @@ Generated::AstNode toGenerated(AST::AstNode n) {
157158
n = TNoneCall(result)
158159
or
159160
n = TAnyCall(result)
161+
or
162+
n = TSuper(result)
160163
}
161164

162165
class TPredicate = TCharPred or TClasslessPredicate or TClassPredicate;

0 commit comments

Comments
 (0)