Skip to content

Commit 0af0b60

Browse files
asgerfCopilot
andcommitted
Index exposed QL predicate signatures
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 32c995d commit 0af0b60

1 file changed

Lines changed: 122 additions & 0 deletions

File tree

  • shared/tree-sitter-extractor/src/generator

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

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -828,11 +828,78 @@ fn class_supertypes<'a>(
828828
supertypes
829829
}
830830

831+
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
832+
struct PredicateSignature<'a> {
833+
name: &'a str,
834+
arity: usize,
835+
}
836+
837+
fn field_predicate_signatures(field: &node_types::Field) -> Vec<PredicateSignature<'_>> {
838+
let getter_arity = match field.storage {
839+
node_types::Storage::Table {
840+
has_index: true, ..
841+
} => 1,
842+
_ => 0,
843+
};
844+
let mut signatures = vec![PredicateSignature {
845+
name: &field.getter_name,
846+
arity: getter_arity,
847+
}];
848+
if let Some(any_getter_name) = &field.any_getter_name {
849+
signatures.push(PredicateSignature {
850+
name: any_getter_name,
851+
arity: 0,
852+
});
853+
}
854+
signatures
855+
}
856+
857+
/// Builds an index of the field getter signatures exposed by each generated
858+
/// class, including getters inherited from supertypes.
859+
fn compute_exposed_predicate_signatures(
860+
nodes: &node_types::NodeTypeMap,
861+
) -> std::collections::BTreeMap<node_types::TypeName, BTreeSet<PredicateSignature<'_>>> {
862+
let mut exposed = nodes
863+
.iter()
864+
.map(|(type_name, node)| {
865+
let fields = match &node.kind {
866+
node_types::EntryKind::Union { fields, .. }
867+
| node_types::EntryKind::Table { fields, .. } => fields.as_slice(),
868+
node_types::EntryKind::Token { .. } => &[],
869+
};
870+
let signatures = fields.iter().flat_map(field_predicate_signatures).collect();
871+
(type_name.clone(), signatures)
872+
})
873+
.collect::<std::collections::BTreeMap<_, BTreeSet<_>>>();
874+
875+
loop {
876+
let mut changed = false;
877+
for (supertype, node) in nodes {
878+
let node_types::EntryKind::Union { members, .. } = &node.kind else {
879+
continue;
880+
};
881+
let inherited = exposed.get(supertype).cloned().unwrap_or_default();
882+
for member in members {
883+
let member_signatures = exposed.entry(member.clone()).or_default();
884+
let previous_len = member_signatures.len();
885+
member_signatures.extend(inherited.iter().copied());
886+
changed |= member_signatures.len() != previous_len;
887+
}
888+
}
889+
if !changed {
890+
break;
891+
}
892+
}
893+
894+
exposed
895+
}
896+
831897
/// Converts the given node types into CodeQL classes wrapping the dbscheme.
832898
pub fn convert_nodes(nodes: &node_types::NodeTypeMap) -> Vec<ql::TopLevel<'_>> {
833899
let mut classes = Vec::new();
834900
let mut token_kinds = BTreeSet::new();
835901
let direct_supertypes = compute_direct_supertypes(nodes);
902+
let _exposed_predicate_signatures = compute_exposed_predicate_signatures(nodes);
836903
for (type_name, node) in nodes {
837904
if let node_types::EntryKind::Token { .. } = &node.kind
838905
&& type_name.named
@@ -1051,3 +1118,58 @@ pub fn create_print_ast_module(nodes: &node_types::NodeTypeMap) -> ql::TopLevel<
10511118
overlay: None,
10521119
})
10531120
}
1121+
1122+
#[cfg(test)]
1123+
mod tests {
1124+
use super::*;
1125+
1126+
#[test]
1127+
fn indexes_predicate_signatures_exposed_by_classes() {
1128+
let yaml = r#"
1129+
supertypes:
1130+
callable:
1131+
subtypes: [function_like]
1132+
fields:
1133+
parameter*: parameter
1134+
body?: block
1135+
function_like:
1136+
subtypes: [function]
1137+
fields:
1138+
name: identifier
1139+
named:
1140+
function:
1141+
parameter*: parameter
1142+
body?: block
1143+
name: identifier
1144+
parameter:
1145+
block:
1146+
identifier:
1147+
"#;
1148+
let json = yeast::node_types_yaml::convert(yaml).unwrap();
1149+
let nodes = node_types::read_node_types_str("test", &json).unwrap();
1150+
let signatures = compute_exposed_predicate_signatures(&nodes);
1151+
let function = signatures
1152+
.get(&node_types::TypeName {
1153+
kind: "function".to_owned(),
1154+
named: true,
1155+
})
1156+
.unwrap();
1157+
1158+
assert!(function.contains(&PredicateSignature {
1159+
name: "getParameter",
1160+
arity: 1,
1161+
}));
1162+
assert!(function.contains(&PredicateSignature {
1163+
name: "getAParameter",
1164+
arity: 0,
1165+
}));
1166+
assert!(function.contains(&PredicateSignature {
1167+
name: "getBody",
1168+
arity: 0,
1169+
}));
1170+
assert!(function.contains(&PredicateSignature {
1171+
name: "getName",
1172+
arity: 0,
1173+
}));
1174+
}
1175+
}

0 commit comments

Comments
 (0)