Skip to content

Commit 8fbe5c0

Browse files
authored
Merge pull request #261 from github/getPrimaryQlClasses
Implement getPrimaryQlClasses
2 parents 8ce7fdc + 060060b commit 8fbe5c0

File tree

6 files changed

+80
-29
lines changed

6 files changed

+80
-29
lines changed

generator/src/ql.rs

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -127,12 +127,13 @@ pub enum Expression<'a> {
127127
Or(Vec<Expression<'a>>),
128128
Equals(Box<Expression<'a>>, Box<Expression<'a>>),
129129
Dot(Box<Expression<'a>>, &'a str, Vec<Expression<'a>>),
130-
Aggregate(
131-
&'a str,
132-
Vec<FormalParameter<'a>>,
133-
Box<Expression<'a>>,
134-
Box<Expression<'a>>,
135-
),
130+
Aggregate {
131+
name: &'a str,
132+
vars: Vec<FormalParameter<'a>>,
133+
range: Option<Box<Expression<'a>>>,
134+
expr: Box<Expression<'a>>,
135+
second_expr: Option<Box<Expression<'a>>>,
136+
},
136137
}
137138

138139
impl<'a> fmt::Display for Expression<'a> {
@@ -188,15 +189,31 @@ impl<'a> fmt::Display for Expression<'a> {
188189
}
189190
write!(f, ")")
190191
}
191-
Expression::Aggregate(n, vars, range, term) => {
192-
write!(f, "{}(", n)?;
193-
for (index, var) in vars.iter().enumerate() {
194-
if index > 0 {
195-
write!(f, ", ")?;
192+
Expression::Aggregate {
193+
name,
194+
vars,
195+
range,
196+
expr,
197+
second_expr,
198+
} => {
199+
write!(f, "{}(", name)?;
200+
if vars.len() > 0 {
201+
for (index, var) in vars.iter().enumerate() {
202+
if index > 0 {
203+
write!(f, ", ")?;
204+
}
205+
write!(f, "{}", var)?;
196206
}
197-
write!(f, "{}", var)?;
207+
write!(f, " | ")?;
208+
}
209+
if let Some(range) = range {
210+
write!(f, "{} | ", range)?;
198211
}
199-
write!(f, " | {} | {})", range, term)
212+
write!(f, "{}", expr)?;
213+
if let Some(second_expr) = second_expr {
214+
write!(f, ", {}", second_expr)?;
215+
}
216+
write!(f, ")")
200217
}
201218
}
202219
}

generator/src/ql_gen.rs

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,27 @@ pub fn create_ast_node_class<'a>(ast_node: &'a str, ast_node_parent: &'a str) ->
7979
Box::new(ql::Expression::String("???")),
8080
),
8181
};
82+
let get_primary_ql_classes = ql::Predicate {
83+
qldoc: Some(
84+
"Gets a comma-separated list of the names of the primary CodeQL \
85+
classes to which this element belongs."
86+
.to_owned(),
87+
),
88+
name: "getPrimaryQlClasses",
89+
overridden: false,
90+
return_type: Some(ql::Type::String),
91+
formal_parameters: vec![],
92+
body: ql::Expression::Equals(
93+
Box::new(ql::Expression::Var("result")),
94+
Box::new(ql::Expression::Aggregate {
95+
name: "concat",
96+
vars: vec![],
97+
range: None,
98+
expr: Box::new(ql::Expression::Pred("getAPrimaryQlClass", vec![])),
99+
second_expr: Some(Box::new(ql::Expression::String(","))),
100+
}),
101+
),
102+
};
82103
ql::Class {
83104
qldoc: Some(String::from("The base class for all AST nodes")),
84105
name: "AstNode",
@@ -92,6 +113,7 @@ pub fn create_ast_node_class<'a>(ast_node: &'a str, ast_node_parent: &'a str) ->
92113
get_parent_index,
93114
get_a_field_or_child,
94115
get_a_primary_ql_class,
116+
get_primary_ql_classes,
95117
],
96118
}
97119
}
@@ -410,15 +432,16 @@ fn create_field_getters<'a>(
410432
})
411433
.collect();
412434
(
413-
ql::Expression::Aggregate(
414-
"exists",
415-
vec![ql::FormalParameter {
435+
ql::Expression::Aggregate {
436+
name: "exists",
437+
vars: vec![ql::FormalParameter {
416438
name: "value",
417439
param_type: ql::Type::Int,
418440
}],
419-
Box::new(get_value),
420-
Box::new(ql::Expression::Or(disjuncts)),
421-
),
441+
range: Some(Box::new(get_value)),
442+
expr: Box::new(ql::Expression::Or(disjuncts)),
443+
second_expr: None,
444+
},
422445
// Since the getter returns a string and not an AstNode, it won't be part of getAFieldOrChild:
423446
None,
424447
)

ql/consistency-queries/AstConsistency.ql

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,11 @@
11
import codeql.ruby.AST
22
import codeql.ruby.ast.internal.Synthesis
33

4-
private string getAPrimaryQlClass(AstNode node) {
5-
result = node.getAPrimaryQlClass()
6-
or
7-
not exists(node.getAPrimaryQlClass()) and result = "(none)"
8-
}
9-
104
query predicate missingParent(AstNode node, string cls) {
115
not exists(node.getParent()) and
126
node.getLocation().getFile().getExtension() != "erb" and
137
not node instanceof Toplevel and
14-
cls = getAPrimaryQlClass(node)
8+
cls = node.getPrimaryQlClasses()
159
}
1610

1711
pragma[noinline]
@@ -22,7 +16,7 @@ private AstNode parent(AstNode child, int desugarLevel) {
2216

2317
query predicate multipleParents(AstNode node, AstNode parent, string cls) {
2418
parent = node.getParent() and
25-
cls = getAPrimaryQlClass(parent) and
19+
cls = parent.getPrimaryQlClasses() and
2620
exists(AstNode one, AstNode two, int desugarLevel |
2721
one = parent(node, desugarLevel) and
2822
two = parent(node, desugarLevel) and

ql/lib/codeql/ruby/AST.qll

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ class AstNode extends TAstNode {
3232
*/
3333
string getAPrimaryQlClass() { result = "???" }
3434

35+
/**
36+
* Gets a comma-separated list of the names of the primary CodeQL classes to
37+
* which this element belongs.
38+
*/
39+
final string getPrimaryQlClasses() { result = concat(this.getAPrimaryQlClass(), ",") }
40+
3541
/** Gets the enclosing module, if any. */
3642
ModuleBase getEnclosingModule() {
3743
exists(Scope::Range s |

ql/lib/codeql/ruby/ast/internal/TreeSitter.qll

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ module Ruby {
2626

2727
/** Gets the name of the primary QL class for this element. */
2828
string getAPrimaryQlClass() { result = "???" }
29+
30+
/** Gets a comma-separated list of the names of the primary CodeQL classes to which this element belongs. */
31+
string getPrimaryQlClasses() { result = concat(getAPrimaryQlClass(), ",") }
2932
}
3033

3134
/** A token. */
@@ -1864,6 +1867,9 @@ module Erb {
18641867

18651868
/** Gets the name of the primary QL class for this element. */
18661869
string getAPrimaryQlClass() { result = "???" }
1870+
1871+
/** Gets a comma-separated list of the names of the primary CodeQL classes to which this element belongs. */
1872+
string getPrimaryQlClasses() { result = concat(getAPrimaryQlClass(), ",") }
18671873
}
18681874

18691875
/** A token. */

ql/lib/codeql/ruby/regexp/RegExpTreeView.qll

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,15 @@ class RegExpParent extends TRegExpParent {
1818

1919
/**
2020
* Gets the name of a primary CodeQL class to which this regular
21-
* expression
22-
* term belongs.
21+
* expression term belongs.
2322
*/
2423
string getAPrimaryQlClass() { result = "RegExpParent" }
24+
25+
/**
26+
* Gets a comma-separated list of the names of the primary CodeQL classes to
27+
* which this regular expression term belongs.
28+
*/
29+
final string getPrimaryQlClasses() { result = concat(this.getAPrimaryQlClass(), ",") }
2530
}
2631

2732
class RegExpLiteral extends TRegExpLiteral, RegExpParent {

0 commit comments

Comments
 (0)