Skip to content

Commit eb30233

Browse files
authored
Merge pull request #19963 from github/nickrolfe/rb-discard-locations
Ruby/QL: add discard predicates for locations
2 parents 8c90250 + 7c5b186 commit eb30233

File tree

4 files changed

+118
-3
lines changed

4 files changed

+118
-3
lines changed

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,20 @@ import codeql.Locations as L
99
overlay[local]
1010
private predicate isOverlay() { databaseMetadata("isOverlay", "true") }
1111

12+
/** Holds if `loc` is in the `file` and is part of the overlay base database. */
13+
overlay[local]
14+
private predicate discardableLocation(@file file, @location_default loc) {
15+
not isOverlay() and locations_default(loc, file, _, _, _, _)
16+
}
17+
18+
/** Holds if `loc` should be discarded, because it is part of the overlay base and is in a file that was also extracted as part of the overlay database. */
19+
overlay[discard_entity]
20+
private predicate discardLocation(@location_default loc) {
21+
exists(@file file, string path | files(file, path) |
22+
discardableLocation(file, loc) and overlayChangedFiles(path)
23+
)
24+
}
25+
1226
module QL {
1327
/** The base class for all AST nodes */
1428
class AstNode extends @ql_ast_node {

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,20 @@ import codeql.Locations as L
99
overlay[local]
1010
private predicate isOverlay() { databaseMetadata("isOverlay", "true") }
1111

12+
/** Holds if `loc` is in the `file` and is part of the overlay base database. */
13+
overlay[local]
14+
private predicate discardableLocation(@file file, @location_default loc) {
15+
not isOverlay() and locations_default(loc, file, _, _, _, _)
16+
}
17+
18+
/** Holds if `loc` should be discarded, because it is part of the overlay base and is in a file that was also extracted as part of the overlay database. */
19+
overlay[discard_entity]
20+
private predicate discardLocation(@location_default loc) {
21+
exists(@file file, string path | files(file, path) |
22+
discardableLocation(file, loc) and overlayChangedFiles(path)
23+
)
24+
}
25+
1226
module Ruby {
1327
/** The base class for all AST nodes */
1428
class AstNode extends @ruby_ast_node {

shared/tree-sitter-extractor/src/generator/mod.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,11 @@ pub fn generate(
5151

5252
ql::write(
5353
&mut ql_writer,
54-
&[ql::TopLevel::Predicate(
55-
ql_gen::create_is_overlay_predicate(),
56-
)],
54+
&[
55+
ql::TopLevel::Predicate(ql_gen::create_is_overlay_predicate()),
56+
ql::TopLevel::Predicate(ql_gen::create_discardable_location_predicate()),
57+
ql::TopLevel::Predicate(ql_gen::create_discard_location_predicate()),
58+
],
5759
)?;
5860

5961
for language in languages {

shared/tree-sitter-extractor/src/generator/ql_gen.rs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,91 @@ pub fn create_discard_ast_node_predicate(ast_node_name: &str) -> ql::Predicate {
396396
}
397397
}
398398

399+
pub fn create_discardable_location_predicate() -> ql::Predicate<'static> {
400+
ql::Predicate {
401+
name: "discardableLocation",
402+
qldoc: Some(String::from(
403+
"Holds if `loc` is in the `file` and is part of the overlay base database.",
404+
)),
405+
overridden: false,
406+
is_private: true,
407+
is_final: false,
408+
overlay: Some(ql::OverlayAnnotation::Local),
409+
return_type: None,
410+
formal_parameters: vec![
411+
ql::FormalParameter {
412+
name: "file",
413+
param_type: ql::Type::At("file"),
414+
},
415+
ql::FormalParameter {
416+
name: "loc",
417+
param_type: ql::Type::At("location_default"),
418+
},
419+
],
420+
body: ql::Expression::And(vec![
421+
ql::Expression::Negation(Box::new(ql::Expression::Pred("isOverlay", vec![]))),
422+
ql::Expression::Pred(
423+
"locations_default",
424+
vec![
425+
ql::Expression::Var("loc"),
426+
ql::Expression::Var("file"),
427+
ql::Expression::Var("_"),
428+
ql::Expression::Var("_"),
429+
ql::Expression::Var("_"),
430+
ql::Expression::Var("_"),
431+
],
432+
),
433+
]),
434+
}
435+
}
436+
437+
/// Creates a discard predicate for `@location_default` entities. This is necessary because the
438+
/// tree-sitter extractors use `*` IDs for locations, which means that locations don't get shared
439+
/// between the base and overlay databases.
440+
pub fn create_discard_location_predicate() -> ql::Predicate<'static> {
441+
ql::Predicate {
442+
name: "discardLocation",
443+
qldoc: Some(String::from(
444+
"Holds if `loc` should be discarded, because it is part of the overlay base \
445+
and is in a file that was also extracted as part of the overlay database.",
446+
)),
447+
overridden: false,
448+
is_private: true,
449+
is_final: false,
450+
overlay: Some(ql::OverlayAnnotation::DiscardEntity),
451+
return_type: None,
452+
formal_parameters: vec![ql::FormalParameter {
453+
name: "loc",
454+
param_type: ql::Type::At("location_default"),
455+
}],
456+
body: ql::Expression::Aggregate {
457+
name: "exists",
458+
vars: vec![
459+
ql::FormalParameter {
460+
name: "file",
461+
param_type: ql::Type::At("file"),
462+
},
463+
ql::FormalParameter {
464+
name: "path",
465+
param_type: ql::Type::String,
466+
},
467+
],
468+
range: Some(Box::new(ql::Expression::Pred(
469+
"files",
470+
vec![ql::Expression::Var("file"), ql::Expression::Var("path")],
471+
))),
472+
expr: Box::new(ql::Expression::And(vec![
473+
ql::Expression::Pred(
474+
"discardableLocation",
475+
vec![ql::Expression::Var("file"), ql::Expression::Var("loc")],
476+
),
477+
ql::Expression::Pred("overlayChangedFiles", vec![ql::Expression::Var("path")]),
478+
])),
479+
second_expr: None,
480+
},
481+
}
482+
}
483+
399484
/// Returns an expression to get a field that's defined as a column in the parent's table.
400485
///
401486
/// # Arguments

0 commit comments

Comments
 (0)