@@ -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.
832898pub 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