From 42538bb14c3571830d2393490b2785ec60bb805f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 27 Aug 2026 12:51:35 +0000 Subject: [PATCH 1/6] Hoist identical fields from union members to abstract predicates Co-authored-by: aschackmull <28296824+aschackmull@users.noreply.github.com> --- .../tree-sitter-extractor/src/generator/ql.rs | 14 +- .../src/generator/ql_gen.rs | 457 +++++++++++++++--- 2 files changed, 391 insertions(+), 80 deletions(-) diff --git a/shared/tree-sitter-extractor/src/generator/ql.rs b/shared/tree-sitter-extractor/src/generator/ql.rs index f114e251af21..7a0efca7eb8f 100644 --- a/shared/tree-sitter-extractor/src/generator/ql.rs +++ b/shared/tree-sitter-extractor/src/generator/ql.rs @@ -109,7 +109,7 @@ impl fmt::Display for Class<'_> { is_final: false, return_type: None, formal_parameters: vec![], - body: charpred.clone(), + body: Some(charpred.clone()), overlay: None, } )?; @@ -307,7 +307,9 @@ pub struct Predicate<'a> { pub is_final: bool, pub return_type: Option>, pub formal_parameters: Vec>, - pub body: Expression<'a>, + /// The body of the predicate, or `None` if this is an `abstract` + /// predicate declaration with no body. + pub body: Option>, pub overlay: Option, } @@ -333,6 +335,9 @@ impl fmt::Display for Predicate<'_> { if self.overridden { write!(f, "override ")?; } + if self.body.is_none() { + write!(f, "abstract ")?; + } match &self.return_type { None => write!(f, "predicate ")?, Some(return_type) => write!(f, "{return_type} ")?, @@ -344,7 +349,10 @@ impl fmt::Display for Predicate<'_> { } write!(f, "{param}")?; } - write!(f, ") {{ {} }}", self.body)?; + match &self.body { + Some(body) => write!(f, ") {{ {body} }}")?, + None => write!(f, ");")?, + } Ok(()) } diff --git a/shared/tree-sitter-extractor/src/generator/ql_gen.rs b/shared/tree-sitter-extractor/src/generator/ql_gen.rs index 237ed9ddb968..7f6f182d036b 100644 --- a/shared/tree-sitter-extractor/src/generator/ql_gen.rs +++ b/shared/tree-sitter-extractor/src/generator/ql_gen.rs @@ -1,3 +1,4 @@ +use std::collections::BTreeMap; use std::collections::BTreeSet; use crate::{generator::ql, node_types}; @@ -20,14 +21,14 @@ pub fn create_ast_node_class<'a>( is_final: false, return_type: Some(ql::Type::String), formal_parameters: vec![], - body: ql::Expression::Equals( + body: Some(ql::Expression::Equals( Box::new(ql::Expression::Var("result")), Box::new(ql::Expression::Dot( Box::new(ql::Expression::Var("this")), "getAPrimaryQlClass", vec![], )), - ), + )), overlay: None, }; let get_location = ql::Predicate { @@ -38,10 +39,10 @@ pub fn create_ast_node_class<'a>( is_final: true, return_type: Some(ql::Type::Normal("L::Location")), formal_parameters: vec![], - body: ql::Expression::Pred( + body: Some(ql::Expression::Pred( node_location_table, vec![ql::Expression::Var("this"), ql::Expression::Var("result")], - ), + )), overlay: None, }; let get_a_field_or_child = create_none_predicate( @@ -58,14 +59,14 @@ pub fn create_ast_node_class<'a>( is_final: true, return_type: Some(ql::Type::Facade("AstNode")), formal_parameters: vec![], - body: ql::Expression::Pred( + body: Some(ql::Expression::Pred( node_parent_table, vec![ ql::Expression::Var("this"), ql::Expression::Var("result"), ql::Expression::Var("_"), ], - ), + )), overlay: None, }; let get_parent_index = ql::Predicate { @@ -78,14 +79,14 @@ pub fn create_ast_node_class<'a>( is_final: true, return_type: Some(ql::Type::Int), formal_parameters: vec![], - body: ql::Expression::Pred( + body: Some(ql::Expression::Pred( node_parent_table, vec![ ql::Expression::Var("this"), ql::Expression::Var("_"), ql::Expression::Var("result"), ], - ), + )), overlay: None, }; let get_a_primary_ql_class = ql::Predicate { @@ -98,10 +99,10 @@ pub fn create_ast_node_class<'a>( is_final: false, return_type: Some(ql::Type::String), formal_parameters: vec![], - body: ql::Expression::Equals( + body: Some(ql::Expression::Equals( Box::new(ql::Expression::Var("result")), Box::new(ql::Expression::String("???")), - ), + )), overlay: None, }; let get_primary_ql_classes = ql::Predicate { @@ -116,7 +117,7 @@ pub fn create_ast_node_class<'a>( is_final: false, return_type: Some(ql::Type::String), formal_parameters: vec![], - body: ql::Expression::Equals( + body: Some(ql::Expression::Equals( Box::new(ql::Expression::Var("result")), Box::new(ql::Expression::Aggregate { name: "concat", @@ -129,7 +130,7 @@ pub fn create_ast_node_class<'a>( )), second_expr: Some(Box::new(ql::Expression::String(","))), }), - ), + )), overlay: None, }; ql::Class { @@ -163,7 +164,12 @@ pub fn create_token_class<'a>(token_type: &'a str, tokeninfo: &'a str) -> ql::Cl is_final: true, return_type: Some(ql::Type::String), formal_parameters: vec![], - body: create_get_field_expr_for_column_storage("result", tokeninfo, 1, tokeninfo_arity), + body: Some(create_get_field_expr_for_column_storage( + "result", + tokeninfo, + 1, + tokeninfo_arity, + )), overlay: None, }; let to_string = ql::Predicate { @@ -176,14 +182,14 @@ pub fn create_token_class<'a>(token_type: &'a str, tokeninfo: &'a str) -> ql::Cl is_final: true, return_type: Some(ql::Type::String), formal_parameters: vec![], - body: ql::Expression::Equals( + body: Some(ql::Expression::Equals( Box::new(ql::Expression::Var("result")), Box::new(ql::Expression::Dot( Box::new(ql::Expression::Var("this")), "getValue", vec![], )), - ), + )), overlay: None, }; ql::Class { @@ -223,12 +229,12 @@ pub fn create_trivia_token_class<'a>( is_final: true, return_type: Some(ql::Type::String), formal_parameters: vec![], - body: create_get_field_expr_for_column_storage( + body: Some(create_get_field_expr_for_column_storage( "result", trivia_tokeninfo, 1, trivia_tokeninfo_arity, - ), + )), overlay: None, }; let to_string = ql::Predicate { @@ -241,14 +247,14 @@ pub fn create_trivia_token_class<'a>( is_final: true, return_type: Some(ql::Type::String), formal_parameters: vec![], - body: ql::Expression::Equals( + body: Some(ql::Expression::Equals( Box::new(ql::Expression::Var("result")), Box::new(ql::Expression::Dot( Box::new(ql::Expression::Var("this")), "getValue", vec![], )), - ), + )), overlay: None, }; ql::Class { @@ -306,7 +312,7 @@ fn create_none_predicate<'a>( is_final: false, return_type, formal_parameters: Vec::new(), - body: ql::Expression::Pred("none", vec![]), + body: Some(ql::Expression::Pred("none", vec![])), overlay: None, } } @@ -324,10 +330,10 @@ fn create_get_a_primary_ql_class(class_name: &str, is_final: bool) -> ql::Predic is_final, return_type: Some(ql::Type::String), formal_parameters: vec![], - body: ql::Expression::Equals( + body: Some(ql::Expression::Equals( Box::new(ql::Expression::Var("result")), Box::new(ql::Expression::String(class_name)), - ), + )), overlay: None, } } @@ -342,13 +348,13 @@ pub fn create_is_overlay_predicate() -> ql::Predicate<'static> { return_type: None, overlay: Some(ql::OverlayAnnotation::Local), formal_parameters: vec![], - body: ql::Expression::Pred( + body: Some(ql::Expression::Pred( "databaseMetadata", vec![ ql::Expression::String("isOverlay"), ql::Expression::String("true"), ], - ), + )), } } @@ -368,7 +374,7 @@ pub fn create_get_node_file_predicate<'a>( name: "node", param_type: ql::Type::At(ast_node_name), }], - body: ql::Expression::Aggregate { + body: Some(ql::Expression::Aggregate { name: "exists", vars: vec![ql::FormalParameter { name: "loc", @@ -390,7 +396,7 @@ pub fn create_get_node_file_predicate<'a>( ], )), second_expr: None, - }, + }), } } @@ -415,7 +421,7 @@ pub fn create_discardable_ast_node_predicate(ast_node_name: &str) -> ql::Predica param_type: ql::Type::At(ast_node_name), }, ], - body: ql::Expression::And(vec![ + body: Some(ql::Expression::And(vec![ ql::Expression::Negation(Box::new(ql::Expression::Pred("isOverlay", vec![]))), ql::Expression::Equals( Box::new(ql::Expression::Var("file")), @@ -424,7 +430,7 @@ pub fn create_discardable_ast_node_predicate(ast_node_name: &str) -> ql::Predica vec![ql::Expression::Var("node")], )), ), - ]), + ])), } } @@ -444,7 +450,7 @@ pub fn create_discard_ast_node_predicate(ast_node_name: &str) -> ql::Predicate<' name: "node", param_type: ql::Type::At(ast_node_name), }], - body: ql::Expression::Aggregate { + body: Some(ql::Expression::Aggregate { name: "exists", vars: vec![ ql::FormalParameter { @@ -468,7 +474,7 @@ pub fn create_discard_ast_node_predicate(ast_node_name: &str) -> ql::Predicate<' ql::Expression::Pred("overlayChangedFiles", vec![ql::Expression::Var("path")]), ])), second_expr: None, - }, + }), } } @@ -493,7 +499,7 @@ pub fn create_discardable_location_predicate() -> ql::Predicate<'static> { param_type: ql::Type::At("location_default"), }, ], - body: ql::Expression::And(vec![ + body: Some(ql::Expression::And(vec![ ql::Expression::Negation(Box::new(ql::Expression::Pred("isOverlay", vec![]))), ql::Expression::Pred( "locations_default", @@ -506,7 +512,7 @@ pub fn create_discardable_location_predicate() -> ql::Predicate<'static> { ql::Expression::Var("_"), ], ), - ]), + ])), } } @@ -529,7 +535,7 @@ pub fn create_discard_location_predicate() -> ql::Predicate<'static> { name: "loc", param_type: ql::Type::At("location_default"), }], - body: ql::Expression::Aggregate { + body: Some(ql::Expression::Aggregate { name: "exists", vars: vec![ ql::FormalParameter { @@ -553,7 +559,7 @@ pub fn create_discard_location_predicate() -> ql::Predicate<'static> { ql::Expression::Pred("overlayChangedFiles", vec![ql::Expression::Var("path")]), ])), second_expr: None, - }, + }), } } @@ -760,7 +766,7 @@ fn create_field_getters<'a>( is_final: true, return_type: return_type.clone(), formal_parameters, - body, + body: Some(body), overlay: None, }]; @@ -773,14 +779,14 @@ fn create_field_getters<'a>( is_final: true, return_type, formal_parameters: vec![], - body: ql::Expression::Equals( + body: Some(ql::Expression::Equals( Box::new(ql::Expression::Var("result")), Box::new(ql::Expression::Dot( Box::new(ql::Expression::Var("this")), &field.getter_name, vec![ql::Expression::Var("_")], )), - ), + )), overlay: None, }); } @@ -828,6 +834,83 @@ fn class_supertypes<'a>( supertypes } +/// Returns whether `a` and `b` have the same signature, i.e. the same name, +/// return type, and formal parameters. Predicates with the same signature can +/// override one another. +fn same_predicate_signature(a: &ql::Predicate, b: &ql::Predicate) -> bool { + a.name == b.name && a.return_type == b.return_type && a.formal_parameters == b.formal_parameters +} + +/// Computes, for each tree-sitter supertype (union) node, the list of +/// predicates that are guaranteed to be defined identically (in terms of +/// name, return type, and formal parameters, though not necessarily body) by +/// every one of its members. These are the predicates that can be hoisted to +/// an `abstract` predicate on the union's class, with the corresponding +/// predicates on its members becoming `override`s. +/// +/// The result for a given node is memoized in `cache`, and also used to +/// answer the query for any other node that (directly, or transitively +/// through further supertypes) has that node as a member. +fn compute_exposed_predicates<'a>( + type_name: &node_types::TypeName, + nodes: &'a node_types::NodeTypeMap, + field_predicates: &BTreeMap<&node_types::TypeName, Vec>>, + cache: &mut BTreeMap>>, +) -> Vec> { + if let Some(exposed) = cache.get(type_name) { + return exposed.clone(); + } + let exposed = match nodes.get(type_name).map(|node| &node.kind) { + Some(node_types::EntryKind::Table { .. }) => { + field_predicates.get(type_name).cloned().unwrap_or_default() + } + Some(node_types::EntryKind::Union { members }) => { + let mut members = members.iter(); + let mut common = match members.next() { + Some(first) => compute_exposed_predicates(first, nodes, field_predicates, cache), + None => Vec::new(), + }; + for member in members { + let member_predicates = + compute_exposed_predicates(member, nodes, field_predicates, cache); + common.retain(|predicate| { + member_predicates + .iter() + .any(|other| same_predicate_signature(predicate, other)) + }); + } + common + } + Some(node_types::EntryKind::Token { .. }) | None => Vec::new(), + }; + cache.insert(type_name.clone(), exposed.clone()); + exposed +} + +/// Returns whether `predicate` (declared, or about to be declared, on the +/// class for `type_name`) is already exposed by one of `type_name`'s direct +/// supertypes, and therefore must be marked as an `override` (for a concrete +/// predicate) or can be omitted entirely (for an `abstract` one, since it's +/// already inherited). +fn is_predicate_inherited( + predicate: &ql::Predicate, + type_name: &node_types::TypeName, + direct_supertypes: &BTreeMap>, + exposed_by_class_name: &BTreeMap<&str, Vec>, +) -> bool { + direct_supertypes.get(type_name).is_some_and(|supertypes| { + supertypes.iter().any(|supertype| { + exposed_by_class_name + .get(supertype) + .is_some_and(|predicates| { + predicates + .iter() + .any(|other| same_predicate_signature(predicate, other)) + }) + }) + }) +} + /// Converts the given node types into CodeQL classes wrapping the dbscheme. pub fn convert_nodes(nodes: &node_types::NodeTypeMap) -> Vec> { let mut classes = Vec::new(); @@ -841,6 +924,88 @@ pub fn convert_nodes(nodes: &node_types::NodeTypeMap) -> Vec> { } } + // First, compute the field-getter predicates (and the expressions used by + // `getAFieldOrChild`) for every table node, without yet knowing whether + // any of them will need to be marked `override`. These are needed both + // to build the final classes below, and to figure out which fields are + // shared identically by all the members of a supertype. + let mut field_predicates: BTreeMap<&node_types::TypeName, Vec>> = + BTreeMap::new(); + let mut get_child_exprs: BTreeMap<&node_types::TypeName, Vec>> = + BTreeMap::new(); + for (type_name, node) in nodes { + if let node_types::EntryKind::Table { + name: main_table_name, + fields, + } = &node.kind + { + if fields.is_empty() { + panic!("Encountered node '{}' with no fields", type_name.kind); + } + + // Count how many columns there will be in the main table. There + // will be one for the id, plus one for each field that's stored + // as a column. + let main_table_arity = 1 + fields + .iter() + .filter(|&f| matches!(f.storage, node_types::Storage::Column { .. })) + .count(); + + let mut main_table_column_index: usize = 0; + let mut predicates = Vec::new(); + let mut exprs = Vec::new(); + for field in fields { + let (get_preds, get_child_expr) = create_field_getters( + main_table_name, + main_table_arity, + &mut main_table_column_index, + field, + nodes, + ); + predicates.extend(get_preds); + if let Some(get_child_expr) = get_child_expr { + exprs.push(get_child_expr) + } + } + field_predicates.insert(type_name, predicates); + get_child_exprs.insert(type_name, exprs); + } + } + + // Next, for every supertype (union) node, compute the predicates that are + // guaranteed to be defined identically (in name, return type, and formal + // parameters) by every one of its members. Such predicates can be hoisted + // to an `abstract` predicate on the supertype's class, with the + // corresponding predicates on its members becoming `override`s. + let mut exposed_predicates_cache: BTreeMap>> = + BTreeMap::new(); + for (type_name, node) in nodes { + if let node_types::EntryKind::Union { .. } = &node.kind { + compute_exposed_predicates( + type_name, + nodes, + &field_predicates, + &mut exposed_predicates_cache, + ); + } + } + let exposed_by_class_name: BTreeMap<&str, Vec>> = nodes + .iter() + .filter_map(|(type_name, node)| { + if let node_types::EntryKind::Union { .. } = &node.kind { + Some(( + node.ql_class_name.as_str(), + exposed_predicates_cache + .get(type_name) + .cloned() + .unwrap_or_default(), + )) + } else { + None + } + }) + .collect(); + for (type_name, node) in nodes { match &node.kind { node_types::EntryKind::Token { kind_id: _ } => { @@ -865,7 +1030,31 @@ pub fn convert_nodes(nodes: &node_types::NodeTypeMap) -> Vec> { } node_types::EntryKind::Union { members: _ } => { // It's a tree-sitter supertype node, so we're wrapping a dbscheme - // union type. + // union type. Any predicate that's identically defined by every + // member becomes an `abstract` predicate here, unless it's + // already inherited (and thus abstract) via one of this + // supertype's own direct supertypes. + let predicates = exposed_predicates_cache + .get(type_name) + .cloned() + .unwrap_or_default() + .into_iter() + .filter(|predicate| { + !is_predicate_inherited( + predicate, + type_name, + &direct_supertypes, + &exposed_by_class_name, + ) + }) + .map(|predicate| ql::Predicate { + overridden: false, + is_private: false, + is_final: false, + body: None, + ..predicate + }) + .collect(); classes.push(ql::TopLevel::Class(ql::Class { qldoc: None, name: &node.ql_class_name, @@ -879,25 +1068,10 @@ pub fn convert_nodes(nodes: &node_types::NodeTypeMap) -> Vec> { &direct_supertypes, ), characteristic_predicate: None, - predicates: vec![], + predicates, })); } - node_types::EntryKind::Table { - name: main_table_name, - fields, - } => { - if fields.is_empty() { - panic!("Encountered node '{}' with no fields", type_name.kind); - } - - // Count how many columns there will be in the main table. There - // will be one for the id, plus one for each field that's stored - // as a column. - let main_table_arity = 1 + fields - .iter() - .filter(|&f| matches!(f.storage, node_types::Storage::Column { .. })) - .count(); - + node_types::EntryKind::Table { .. } => { let main_class_name = &node.ql_class_name; let mut main_class = ql::Class { qldoc: Some(format!("A class representing `{}` nodes.", type_name.kind)), @@ -915,26 +1089,30 @@ pub fn convert_nodes(nodes: &node_types::NodeTypeMap) -> Vec> { predicates: vec![create_get_a_primary_ql_class(main_class_name, true)], }; - let mut main_table_column_index: usize = 0; - let mut get_child_exprs: Vec = Vec::new(); - - // Iterate through the fields, creating: - // - classes to wrap union types if fields need them, - // - predicates to access the fields, - // - the QL expressions to access the fields that will be part of getAFieldOrChild. - for field in fields { - let (get_preds, get_child_expr) = create_field_getters( - main_table_name, - main_table_arity, - &mut main_table_column_index, - field, - nodes, - ); - main_class.predicates.extend(get_preds); - if let Some(get_child_expr) = get_child_expr { - get_child_exprs.push(get_child_expr) - } - } + // A field getter that's identically defined (in signature) by + // every member of one of this node's direct supertypes is an + // override of the corresponding `abstract` predicate declared + // there. + main_class.predicates.extend( + field_predicates + .get(type_name) + .cloned() + .unwrap_or_default() + .into_iter() + .map(|predicate| { + let overridden = predicate.overridden + || is_predicate_inherited( + &predicate, + type_name, + &direct_supertypes, + &exposed_by_class_name, + ); + ql::Predicate { + overridden, + ..predicate + } + }), + ); main_class.predicates.push(ql::Predicate { qldoc: Some(String::from("Gets a field or child node of this node.")), @@ -944,7 +1122,9 @@ pub fn convert_nodes(nodes: &node_types::NodeTypeMap) -> Vec> { is_final: true, return_type: Some(ql::Type::Facade("AstNode")), formal_parameters: vec![], - body: ql::Expression::Or(get_child_exprs), + body: Some(ql::Expression::Or( + get_child_exprs.get(type_name).cloned().unwrap_or_default(), + )), overlay: None, }); @@ -1038,7 +1218,7 @@ pub fn create_print_ast_module(nodes: &node_types::NodeTypeMap) -> ql::TopLevel< param_type: ql::Type::Int, }, ], - body: ql::Expression::Or(disjuncts), + body: Some(ql::Expression::Or(disjuncts)), overlay: None, }; @@ -1051,3 +1231,126 @@ pub fn create_print_ast_module(nodes: &node_types::NodeTypeMap) -> ql::TopLevel< overlay: None, }) } + +#[cfg(test)] +mod tests { + use super::*; + + /// Builds a small node-types fixture in which a supertype (`loop_stmt`) + /// has three members that all share an identical `body?: block` field, + /// as well as a `condition`/`iterable` field that differs in name across + /// members (and so should *not* be hoisted). + fn loop_stmt_fixture() -> node_types::NodeTypeMap { + let json = r#"[ + { + "type": "loop_stmt", + "named": true, + "subtypes": [ + {"type": "for_each_stmt", "named": true}, + {"type": "while_stmt", "named": true}, + {"type": "do_while_stmt", "named": true} + ] + }, + { + "type": "block", + "named": true, + "fields": {}, + "children": {"multiple": true, "required": false, "types": [{"type": "expr_placeholder", "named": true}]} + }, + { + "type": "expr_placeholder", + "named": true, + "fields": {}, + "children": null + }, + { + "type": "for_each_stmt", + "named": true, + "fields": { + "body": {"multiple": false, "required": false, "types": [{"type": "block", "named": true}]}, + "iterable": {"multiple": false, "required": true, "types": [{"type": "expr_placeholder", "named": true}]} + }, + "children": null + }, + { + "type": "while_stmt", + "named": true, + "fields": { + "body": {"multiple": false, "required": false, "types": [{"type": "block", "named": true}]}, + "condition": {"multiple": false, "required": true, "types": [{"type": "expr_placeholder", "named": true}]} + }, + "children": null + }, + { + "type": "do_while_stmt", + "named": true, + "fields": { + "body": {"multiple": false, "required": false, "types": [{"type": "block", "named": true}]}, + "condition": {"multiple": false, "required": true, "types": [{"type": "expr_placeholder", "named": true}]} + }, + "children": null + } + ]"#; + node_types::read_node_types_str("test", json).unwrap() + } + + #[test] + fn hoists_field_shared_by_all_members_of_a_supertype() { + let nodes = loop_stmt_fixture(); + let classes = convert_nodes(&nodes); + + fn find_class<'a, 'b>(classes: &'a [ql::TopLevel<'b>], name: &str) -> &'a ql::Class<'b> { + classes + .iter() + .find_map(|c| match c { + ql::TopLevel::Class(class) if class.name == name => Some(class), + _ => None, + }) + .unwrap_or_else(|| panic!("no class named {name}")) + } + fn find_predicate<'a, 'b>(class: &'a ql::Class<'b>, name: &str) -> &'a ql::Predicate<'b> { + class + .predicates + .iter() + .find(|p| p.name == name) + .unwrap_or_else(|| panic!("class {} has no predicate {name}", class.name)) + } + + // `LoopStmt` should have gained an abstract `getBody` predicate, + // since all of its members have an identically-shaped `body` field. + let loop_stmt = find_class(&classes, "LoopStmt"); + let get_body = find_predicate(loop_stmt, "getBody"); + assert!(get_body.body.is_none(), "getBody should be abstract"); + assert!(!get_body.overridden); + assert!(matches!( + get_body.return_type, + Some(ql::Type::Facade("Block")) + )); + assert!(get_body.formal_parameters.is_empty()); + + // None of the members have an identically-shaped `condition` or + // `iterable` field (the name differs between `for_each_stmt` and the + // other two), so no such predicate should be hoisted. + assert!( + !loop_stmt + .predicates + .iter() + .any(|p| p.name == "getCondition" || p.name == "getIterable") + ); + + // Each member should still define `getBody`, but now as an override + // of the abstract predicate declared on `LoopStmt`. + for member in ["ForEachStmt", "WhileStmt", "DoWhileStmt"] { + let class = find_class(&classes, member); + let get_body = find_predicate(class, "getBody"); + assert!( + get_body.body.is_some(), + "{member}'s getBody should have a body" + ); + assert!( + get_body.overridden, + "{member}'s getBody should be an override" + ); + } + } +} From d0589f79150a707f949f6f45c1f3212a11c7f435 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 27 Aug 2026 12:55:51 +0000 Subject: [PATCH 2/6] Reorder abstract/override modifiers, merge exposed-predicate loops Co-authored-by: aschackmull <28296824+aschackmull@users.noreply.github.com> --- .../tree-sitter-extractor/src/generator/ql.rs | 6 +++--- .../src/generator/ql_gen.rs | 20 +++---------------- 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/shared/tree-sitter-extractor/src/generator/ql.rs b/shared/tree-sitter-extractor/src/generator/ql.rs index 7a0efca7eb8f..2d91d6c66f67 100644 --- a/shared/tree-sitter-extractor/src/generator/ql.rs +++ b/shared/tree-sitter-extractor/src/generator/ql.rs @@ -332,12 +332,12 @@ impl fmt::Display for Predicate<'_> { if self.is_final { write!(f, "final ")?; } - if self.overridden { - write!(f, "override ")?; - } if self.body.is_none() { write!(f, "abstract ")?; } + if self.overridden { + write!(f, "override ")?; + } match &self.return_type { None => write!(f, "predicate ")?, Some(return_type) => write!(f, "{return_type} ")?, diff --git a/shared/tree-sitter-extractor/src/generator/ql_gen.rs b/shared/tree-sitter-extractor/src/generator/ql_gen.rs index 7f6f182d036b..8822cdf3630c 100644 --- a/shared/tree-sitter-extractor/src/generator/ql_gen.rs +++ b/shared/tree-sitter-extractor/src/generator/ql_gen.rs @@ -979,32 +979,18 @@ pub fn convert_nodes(nodes: &node_types::NodeTypeMap) -> Vec> { // corresponding predicates on its members becoming `override`s. let mut exposed_predicates_cache: BTreeMap>> = BTreeMap::new(); + let mut exposed_by_class_name: BTreeMap<&str, Vec>> = BTreeMap::new(); for (type_name, node) in nodes { if let node_types::EntryKind::Union { .. } = &node.kind { - compute_exposed_predicates( + let exposed = compute_exposed_predicates( type_name, nodes, &field_predicates, &mut exposed_predicates_cache, ); + exposed_by_class_name.insert(node.ql_class_name.as_str(), exposed); } } - let exposed_by_class_name: BTreeMap<&str, Vec>> = nodes - .iter() - .filter_map(|(type_name, node)| { - if let node_types::EntryKind::Union { .. } = &node.kind { - Some(( - node.ql_class_name.as_str(), - exposed_predicates_cache - .get(type_name) - .cloned() - .unwrap_or_default(), - )) - } else { - None - } - }) - .collect(); for (type_name, node) in nodes { match &node.kind { From 79de6fd9e2e213cae1ca9e65ba4f728b064a452e Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Fri, 4 Sep 2026 07:57:24 +0200 Subject: [PATCH 3/6] tweak ql_gen.rs --- .../src/generator/ql_gen.rs | 85 +++++++++---------- 1 file changed, 40 insertions(+), 45 deletions(-) diff --git a/shared/tree-sitter-extractor/src/generator/ql_gen.rs b/shared/tree-sitter-extractor/src/generator/ql_gen.rs index 8822cdf3630c..b142392407c0 100644 --- a/shared/tree-sitter-extractor/src/generator/ql_gen.rs +++ b/shared/tree-sitter-extractor/src/generator/ql_gen.rs @@ -851,40 +851,39 @@ fn same_predicate_signature(a: &ql::Predicate, b: &ql::Predicate) -> bool { /// The result for a given node is memoized in `cache`, and also used to /// answer the query for any other node that (directly, or transitively /// through further supertypes) has that node as a member. -fn compute_exposed_predicates<'a>( - type_name: &node_types::TypeName, +fn compute_exposed_predicates<'a, 'b>( + type_name: &'a node_types::TypeName, nodes: &'a node_types::NodeTypeMap, field_predicates: &BTreeMap<&node_types::TypeName, Vec>>, - cache: &mut BTreeMap>>, -) -> Vec> { - if let Some(exposed) = cache.get(type_name) { - return exposed.clone(); - } - let exposed = match nodes.get(type_name).map(|node| &node.kind) { - Some(node_types::EntryKind::Table { .. }) => { - field_predicates.get(type_name).cloned().unwrap_or_default() - } - Some(node_types::EntryKind::Union { members }) => { - let mut members = members.iter(); - let mut common = match members.next() { - Some(first) => compute_exposed_predicates(first, nodes, field_predicates, cache), - None => Vec::new(), - }; - for member in members { - let member_predicates = - compute_exposed_predicates(member, nodes, field_predicates, cache); - common.retain(|predicate| { - member_predicates - .iter() - .any(|other| same_predicate_signature(predicate, other)) - }); + cache: &'b mut BTreeMap<&'a node_types::TypeName, Vec>>, +) -> &'b Vec> { + if !cache.contains_key(type_name) { + let exposed = match nodes.get(type_name).map(|node| &node.kind) { + Some(node_types::EntryKind::Table { .. }) => { + field_predicates.get(type_name).cloned().unwrap_or_default() } - common - } - Some(node_types::EntryKind::Token { .. }) | None => Vec::new(), - }; - cache.insert(type_name.clone(), exposed.clone()); - exposed + Some(node_types::EntryKind::Union { members }) => { + let mut members = members.iter(); + let mut common = match members.next() { + Some(first) => compute_exposed_predicates(first, nodes, field_predicates, cache).clone(), + None => Vec::new(), + }; + for member in members { + let member_predicates = + compute_exposed_predicates(member, nodes, field_predicates, cache); + common.retain(|predicate| { + member_predicates + .iter() + .any(|other| same_predicate_signature(predicate, other)) + }); + } + common + } + Some(node_types::EntryKind::Token { .. }) | None => Vec::new(), + }; + cache.insert(type_name, exposed); + } + cache.get(type_name).unwrap() } /// Returns whether `predicate` (declared, or about to be declared, on the @@ -977,7 +976,7 @@ pub fn convert_nodes(nodes: &node_types::NodeTypeMap) -> Vec> { // parameters) by every one of its members. Such predicates can be hoisted // to an `abstract` predicate on the supertype's class, with the // corresponding predicates on its members becoming `override`s. - let mut exposed_predicates_cache: BTreeMap>> = + let mut exposed_predicates_cache: BTreeMap<&node_types::TypeName, Vec>> = BTreeMap::new(); let mut exposed_by_class_name: BTreeMap<&str, Vec>> = BTreeMap::new(); for (type_name, node) in nodes { @@ -988,7 +987,7 @@ pub fn convert_nodes(nodes: &node_types::NodeTypeMap) -> Vec> { &field_predicates, &mut exposed_predicates_cache, ); - exposed_by_class_name.insert(node.ql_class_name.as_str(), exposed); + exposed_by_class_name.insert(node.ql_class_name.as_str(), exposed.clone()); } } @@ -1017,24 +1016,20 @@ pub fn convert_nodes(nodes: &node_types::NodeTypeMap) -> Vec> { node_types::EntryKind::Union { members: _ } => { // It's a tree-sitter supertype node, so we're wrapping a dbscheme // union type. Any predicate that's identically defined by every - // member becomes an `abstract` predicate here, unless it's - // already inherited (and thus abstract) via one of this - // supertype's own direct supertypes. + // member becomes an `abstract` predicate here. let predicates = exposed_predicates_cache .get(type_name) .cloned() .unwrap_or_default() .into_iter() - .filter(|predicate| { - !is_predicate_inherited( - predicate, - type_name, - &direct_supertypes, - &exposed_by_class_name, - ) - }) .map(|predicate| ql::Predicate { - overridden: false, + overridden: + is_predicate_inherited( + &predicate, + type_name, + &direct_supertypes, + &exposed_by_class_name, + ), is_private: false, is_final: false, body: None, From c532cb861e71e6bad8da28e13659674e3fdbe1ae Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 4 Sep 2026 06:09:40 +0000 Subject: [PATCH 4/6] Merge exposed-predicates cache/by-class-name maps into a single map Co-authored-by: aschackmull <28296824+aschackmull@users.noreply.github.com> --- .../src/generator/ql_gen.rs | 66 +++++++++---------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/shared/tree-sitter-extractor/src/generator/ql_gen.rs b/shared/tree-sitter-extractor/src/generator/ql_gen.rs index b142392407c0..0c1b58ee96af 100644 --- a/shared/tree-sitter-extractor/src/generator/ql_gen.rs +++ b/shared/tree-sitter-extractor/src/generator/ql_gen.rs @@ -848,24 +848,30 @@ fn same_predicate_signature(a: &ql::Predicate, b: &ql::Predicate) -> bool { /// an `abstract` predicate on the union's class, with the corresponding /// predicates on its members becoming `override`s. /// -/// The result for a given node is memoized in `cache`, and also used to -/// answer the query for any other node that (directly, or transitively -/// through further supertypes) has that node as a member. +/// The result for a given node is memoized in `cache` (keyed by its QL class +/// name), and also used to answer the query for any other node that +/// (directly, or transitively through further supertypes) has that node as a +/// member. The same cache also serves as the answer to "what does the class +/// named X expose?", used by `is_predicate_inherited`. fn compute_exposed_predicates<'a, 'b>( type_name: &'a node_types::TypeName, nodes: &'a node_types::NodeTypeMap, field_predicates: &BTreeMap<&node_types::TypeName, Vec>>, - cache: &'b mut BTreeMap<&'a node_types::TypeName, Vec>>, + cache: &'b mut BTreeMap<&'a str, Vec>>, ) -> &'b Vec> { - if !cache.contains_key(type_name) { - let exposed = match nodes.get(type_name).map(|node| &node.kind) { + let node = nodes.get(type_name); + let class_name = node.map_or(type_name.kind.as_str(), |node| node.ql_class_name.as_str()); + if !cache.contains_key(class_name) { + let exposed = match node.map(|node| &node.kind) { Some(node_types::EntryKind::Table { .. }) => { field_predicates.get(type_name).cloned().unwrap_or_default() } Some(node_types::EntryKind::Union { members }) => { let mut members = members.iter(); let mut common = match members.next() { - Some(first) => compute_exposed_predicates(first, nodes, field_predicates, cache).clone(), + Some(first) => { + compute_exposed_predicates(first, nodes, field_predicates, cache).clone() + } None => Vec::new(), }; for member in members { @@ -881,9 +887,9 @@ fn compute_exposed_predicates<'a, 'b>( } Some(node_types::EntryKind::Token { .. }) | None => Vec::new(), }; - cache.insert(type_name, exposed); + cache.insert(class_name, exposed); } - cache.get(type_name).unwrap() + cache.get(class_name).unwrap() } /// Returns whether `predicate` (declared, or about to be declared, on the @@ -895,17 +901,15 @@ fn is_predicate_inherited( predicate: &ql::Predicate, type_name: &node_types::TypeName, direct_supertypes: &BTreeMap>, - exposed_by_class_name: &BTreeMap<&str, Vec>, + exposed_predicates: &BTreeMap<&str, Vec>, ) -> bool { direct_supertypes.get(type_name).is_some_and(|supertypes| { supertypes.iter().any(|supertype| { - exposed_by_class_name - .get(supertype) - .is_some_and(|predicates| { - predicates - .iter() - .any(|other| same_predicate_signature(predicate, other)) - }) + exposed_predicates.get(supertype).is_some_and(|predicates| { + predicates + .iter() + .any(|other| same_predicate_signature(predicate, other)) + }) }) }) } @@ -976,18 +980,15 @@ pub fn convert_nodes(nodes: &node_types::NodeTypeMap) -> Vec> { // parameters) by every one of its members. Such predicates can be hoisted // to an `abstract` predicate on the supertype's class, with the // corresponding predicates on its members becoming `override`s. - let mut exposed_predicates_cache: BTreeMap<&node_types::TypeName, Vec>> = - BTreeMap::new(); - let mut exposed_by_class_name: BTreeMap<&str, Vec>> = BTreeMap::new(); + let mut exposed_predicates: BTreeMap<&str, Vec>> = BTreeMap::new(); for (type_name, node) in nodes { if let node_types::EntryKind::Union { .. } = &node.kind { - let exposed = compute_exposed_predicates( + compute_exposed_predicates( type_name, nodes, &field_predicates, - &mut exposed_predicates_cache, + &mut exposed_predicates, ); - exposed_by_class_name.insert(node.ql_class_name.as_str(), exposed.clone()); } } @@ -1017,19 +1018,18 @@ pub fn convert_nodes(nodes: &node_types::NodeTypeMap) -> Vec> { // It's a tree-sitter supertype node, so we're wrapping a dbscheme // union type. Any predicate that's identically defined by every // member becomes an `abstract` predicate here. - let predicates = exposed_predicates_cache - .get(type_name) + let predicates = exposed_predicates + .get(node.ql_class_name.as_str()) .cloned() .unwrap_or_default() .into_iter() .map(|predicate| ql::Predicate { - overridden: - is_predicate_inherited( - &predicate, - type_name, - &direct_supertypes, - &exposed_by_class_name, - ), + overridden: is_predicate_inherited( + &predicate, + type_name, + &direct_supertypes, + &exposed_predicates, + ), is_private: false, is_final: false, body: None, @@ -1086,7 +1086,7 @@ pub fn convert_nodes(nodes: &node_types::NodeTypeMap) -> Vec> { &predicate, type_name, &direct_supertypes, - &exposed_by_class_name, + &exposed_predicates, ); ql::Predicate { overridden, From 51ec7cc1293e0c09dc26eb6580cad5995b167312 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 4 Sep 2026 07:28:53 +0000 Subject: [PATCH 5/6] Detect cycles in supertype hierarchy to avoid infinite recursion Co-authored-by: aschackmull <28296824+aschackmull@users.noreply.github.com> --- .../src/generator/ql_gen.rs | 62 ++++++++++++++++++- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/shared/tree-sitter-extractor/src/generator/ql_gen.rs b/shared/tree-sitter-extractor/src/generator/ql_gen.rs index 0c1b58ee96af..64f3072e639f 100644 --- a/shared/tree-sitter-extractor/src/generator/ql_gen.rs +++ b/shared/tree-sitter-extractor/src/generator/ql_gen.rs @@ -853,15 +853,31 @@ fn same_predicate_signature(a: &ql::Predicate, b: &ql::Predicate) -> bool { /// (directly, or transitively through further supertypes) has that node as a /// member. The same cache also serves as the answer to "what does the class /// named X expose?", used by `is_predicate_inherited`. +/// +/// `visiting` tracks the chain of supertypes whose computation is currently +/// in progress, so that a cycle in the "is a member of" relation (which would +/// otherwise cause infinite recursion) can be detected and reported as an +/// error instead. fn compute_exposed_predicates<'a, 'b>( type_name: &'a node_types::TypeName, nodes: &'a node_types::NodeTypeMap, field_predicates: &BTreeMap<&node_types::TypeName, Vec>>, cache: &'b mut BTreeMap<&'a str, Vec>>, + visiting: &mut Vec<&'a str>, ) -> &'b Vec> { let node = nodes.get(type_name); let class_name = node.map_or(type_name.kind.as_str(), |node| node.ql_class_name.as_str()); if !cache.contains_key(class_name) { + if let Some(cycle_start) = visiting.iter().position(|&name| name == class_name) { + let mut cycle: Vec<&str> = visiting[cycle_start..].to_vec(); + cycle.push(class_name); + panic!( + "Found a cycle in the supertype hierarchy: {}. A supertype's members must not \ + (directly or transitively) include the supertype itself.", + cycle.join(" -> ") + ); + } + visiting.push(class_name); let exposed = match node.map(|node| &node.kind) { Some(node_types::EntryKind::Table { .. }) => { field_predicates.get(type_name).cloned().unwrap_or_default() @@ -870,13 +886,19 @@ fn compute_exposed_predicates<'a, 'b>( let mut members = members.iter(); let mut common = match members.next() { Some(first) => { - compute_exposed_predicates(first, nodes, field_predicates, cache).clone() + compute_exposed_predicates(first, nodes, field_predicates, cache, visiting) + .clone() } None => Vec::new(), }; for member in members { - let member_predicates = - compute_exposed_predicates(member, nodes, field_predicates, cache); + let member_predicates = compute_exposed_predicates( + member, + nodes, + field_predicates, + cache, + visiting, + ); common.retain(|predicate| { member_predicates .iter() @@ -887,6 +909,7 @@ fn compute_exposed_predicates<'a, 'b>( } Some(node_types::EntryKind::Token { .. }) | None => Vec::new(), }; + visiting.pop(); cache.insert(class_name, exposed); } cache.get(class_name).unwrap() @@ -988,6 +1011,7 @@ pub fn convert_nodes(nodes: &node_types::NodeTypeMap) -> Vec> { nodes, &field_predicates, &mut exposed_predicates, + &mut Vec::new(), ); } } @@ -1334,4 +1358,36 @@ mod tests { ); } } + + /// Builds a node-types fixture where two supertypes declare each other + /// as (transitive) members, forming a cycle in the "is a member of" + /// relation. + fn cyclic_supertypes_fixture() -> node_types::NodeTypeMap { + let json = r#"[ + { + "type": "a_stmt", + "named": true, + "subtypes": [ + {"type": "b_stmt", "named": true} + ] + }, + { + "type": "b_stmt", + "named": true, + "subtypes": [ + {"type": "a_stmt", "named": true} + ] + } + ]"#; + node_types::read_node_types_str("test", json).unwrap() + } + + #[test] + #[should_panic(expected = "cycle")] + fn detects_cycle_in_supertype_hierarchy() { + let nodes = cyclic_supertypes_fixture(); + // This must not recurse infinitely; it should panic with a + // descriptive error instead. + convert_nodes(&nodes); + } } From a72b7e43aa2410a8c825c941c86794a349f03cf5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 4 Sep 2026 07:36:47 +0000 Subject: [PATCH 6/6] Apply remaining changes Co-authored-by: aschackmull <28296824+aschackmull@users.noreply.github.com> --- go/extractor/go.mod | 7 +-- .../package-not-found-with-go-mod/work/go.mod | 4 -- .../package-not-found-with-go-mod/work/go.sum | 1 - .../go-mod-without-version/src/go.mod | 1 + .../test-extraction/src/go.sum | 45 ------------------- .../traced-extraction/src/go.sum | 45 ------------------- .../src/subdir2/go.mod | 4 +- .../src/subdir2/go.sum | 31 +------------ .../extractor-tests/go-mod-comments/go.mod | 3 +- go/ql/test/go.mod | 1 - .../semmle/go/dependencies/codeql-go/go.mod | 5 --- .../semmle/go/dependencies/sweb/go.mod | 26 ----------- 12 files changed, 7 insertions(+), 166 deletions(-) diff --git a/go/extractor/go.mod b/go/extractor/go.mod index 74a25627fdce..52b26de4caef 100644 --- a/go/extractor/go.mod +++ b/go/extractor/go.mod @@ -8,14 +8,11 @@ toolchain go1.27.0 // bazel run @rules_go//go -- mod tidy // when adding or removing dependencies, run // bazel mod tidy -require ( - golang.org/x/mod v0.40.0 - golang.org/x/tools v0.49.0 -) - require ( github.com/stretchr/testify v1.11.1 + golang.org/x/mod v0.40.0 golang.org/x/sys v0.47.0 + golang.org/x/tools v0.49.0 ) require ( diff --git a/go/ql/integration-tests/diagnostics/package-not-found-with-go-mod/work/go.mod b/go/ql/integration-tests/diagnostics/package-not-found-with-go-mod/work/go.mod index 52f47d8d3e25..eb0b661feb98 100644 --- a/go/ql/integration-tests/diagnostics/package-not-found-with-go-mod/work/go.mod +++ b/go/ql/integration-tests/diagnostics/package-not-found-with-go-mod/work/go.mod @@ -1,7 +1,3 @@ go 1.19 -require ( - github.com/linode/linode-docs-theme v0.0.0-20220622135843-166f108e1933 -) - module test diff --git a/go/ql/integration-tests/diagnostics/package-not-found-with-go-mod/work/go.sum b/go/ql/integration-tests/diagnostics/package-not-found-with-go-mod/work/go.sum index c4c3e8d972df..e69de29bb2d1 100644 --- a/go/ql/integration-tests/diagnostics/package-not-found-with-go-mod/work/go.sum +++ b/go/ql/integration-tests/diagnostics/package-not-found-with-go-mod/work/go.sum @@ -1 +0,0 @@ -github.com/linode/linode-docs-theme v0.0.0-20220622135843-166f108e1933 h1:QchGQS6xESuyjdlNJEjvq2ftGX0sCTAhPhD5hAOJVMI= diff --git a/go/ql/integration-tests/go-mod-without-version/src/go.mod b/go/ql/integration-tests/go-mod-without-version/src/go.mod index 95a82c8aaf51..ded1135300c5 100644 --- a/go/ql/integration-tests/go-mod-without-version/src/go.mod +++ b/go/ql/integration-tests/go-mod-without-version/src/go.mod @@ -4,3 +4,4 @@ require golang.org/x/sys v0.18.0 // indirect module test +go 1.24.13 diff --git a/go/ql/integration-tests/test-extraction/src/go.sum b/go/ql/integration-tests/test-extraction/src/go.sum index a8e1b59ae4b1..e69de29bb2d1 100644 --- a/go/ql/integration-tests/test-extraction/src/go.sum +++ b/go/ql/integration-tests/test-extraction/src/go.sum @@ -1,45 +0,0 @@ -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= -golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/go/ql/integration-tests/traced-extraction/src/go.sum b/go/ql/integration-tests/traced-extraction/src/go.sum index a8e1b59ae4b1..e69de29bb2d1 100644 --- a/go/ql/integration-tests/traced-extraction/src/go.sum +++ b/go/ql/integration-tests/traced-extraction/src/go.sum @@ -1,45 +0,0 @@ -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= -golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/go/ql/integration-tests/two-go-mods-one-failure/src/subdir2/go.mod b/go/ql/integration-tests/two-go-mods-one-failure/src/subdir2/go.mod index 7a2ca787004f..f33ad2ef3e86 100644 --- a/go/ql/integration-tests/two-go-mods-one-failure/src/subdir2/go.mod +++ b/go/ql/integration-tests/two-go-mods-one-failure/src/subdir2/go.mod @@ -1,7 +1,5 @@ go 1.14 -require ( - github.com/microsoft/go-mssqldb v0.12.0 -) +require github.com/microsoft/go-mssqldb v0.12.0 module subdir2 diff --git a/go/ql/integration-tests/two-go-mods-one-failure/src/subdir2/go.sum b/go/ql/integration-tests/two-go-mods-one-failure/src/subdir2/go.sum index 432407e3db02..506dc22b5e60 100644 --- a/go/ql/integration-tests/two-go-mods-one-failure/src/subdir2/go.sum +++ b/go/ql/integration-tests/two-go-mods-one-failure/src/subdir2/go.sum @@ -1,30 +1 @@ -github.com/Azure/go-autorest v13.3.2+incompatible h1:VxzPyuhtnlBOzc4IWCZHqpyH2d+QMLQEuy3wREyY4oc= -github.com/Azure/go-autorest/autorest v0.9.0 h1:MRvx8gncNaXJqOoLmhNjUAKh33JJF8LyxPhomEtOsjs= -github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= -github.com/Azure/go-autorest/autorest v0.9.4 h1:1cM+NmKw91+8h5vfjgzK4ZGLuN72k87XVZBWyGwNjUM= -github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= -github.com/Azure/go-autorest/autorest/adal v0.8.1 h1:pZdL8o72rK+avFWl+p9nE8RWi1JInZrWJYlnpfXJwHk= -github.com/Azure/go-autorest/autorest/adal v0.8.1/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= -github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= -github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSWlm5Ew6bxipnr/tbM= -github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= -github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= -github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= -github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k= -github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= -github.com/microsoft/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73 h1:OGNva6WhsKst5OZf7eZOklDztV3hwtTHovdrLHV+MsA= -github.com/microsoft/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= -github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= -github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 h1:ULYEB3JvPRE/IfO+9uO7vKV/xzVTO7XPAwm8xbf4w2g= -golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +github.com/microsoft/go-mssqldb v0.12.0 h1:nuQ0ygjq+dPZx78vkGH98aXZsk8tIdWHJaFV7ydhnqs= diff --git a/go/ql/test/extractor-tests/go-mod-comments/go.mod b/go/ql/test/extractor-tests/go-mod-comments/go.mod index fadc7257586e..f9e9c3f644f0 100644 --- a/go/ql/test/extractor-tests/go-mod-comments/go.mod +++ b/go/ql/test/extractor-tests/go-mod-comments/go.mod @@ -1,6 +1,7 @@ // atthetop module smowton/test // endofline + // onitsown go 1.14 -// afterwards +// afterwards diff --git a/go/ql/test/go.mod b/go/ql/test/go.mod index 2420613ecee8..f90aed12a7bf 100644 --- a/go/ql/test/go.mod +++ b/go/ql/test/go.mod @@ -1,4 +1,3 @@ module github.com/github/codeql-go/ql/test go 1.21 - diff --git a/go/ql/test/library-tests/semmle/go/dependencies/codeql-go/go.mod b/go/ql/test/library-tests/semmle/go/dependencies/codeql-go/go.mod index 06ecc65d84ae..eac8c4d8e452 100644 --- a/go/ql/test/library-tests/semmle/go/dependencies/codeql-go/go.mod +++ b/go/ql/test/library-tests/semmle/go/dependencies/codeql-go/go.mod @@ -1,8 +1,3 @@ module github.com/github/codeql-go go 1.13 - -require ( - golang.org/x/mod v0.2.0 - golang.org/x/tools v0.0.0-20200302225559-9b52d559c609 -) diff --git a/go/ql/test/library-tests/semmle/go/dependencies/sweb/go.mod b/go/ql/test/library-tests/semmle/go/dependencies/sweb/go.mod index 096134411f9b..876fde8baa2b 100644 --- a/go/ql/test/library-tests/semmle/go/dependencies/sweb/go.mod +++ b/go/ql/test/library-tests/semmle/go/dependencies/sweb/go.mod @@ -35,29 +35,3 @@ replace google.golang.org/appengine => github.com/golang/appengine v1.6.1 replace golang.org/x/mobile => github.com/golang/mobile v0.0.0-20190607214518-6fa95d984e88 replace golang.org/x/image => github.com/golang/image v0.0.0-20190622003408-7e034cad6442 - -require ( - github.com/Joker/jade v1.0.0 // indirect - github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398 // indirect - github.com/aymerick/raymond v2.0.2+incompatible // indirect - github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 // indirect - github.com/fatih/structs v1.1.0 // indirect - github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4 // indirect - github.com/iris-contrib/blackfriday v2.0.0+incompatible // indirect - github.com/iris-contrib/formBinder v0.0.0-20190104093907-fbd5963f41e1 // indirect - github.com/iris-contrib/go.uuid v2.0.0+incompatible // indirect - github.com/json-iterator/go v1.1.6 // indirect - github.com/kataras/golog v0.0.0-20190624001437-99c81de45f40 // indirect - github.com/kataras/iris v11.1.1+incompatible - github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d // indirect - github.com/klauspost/compress v1.7.2 // indirect - github.com/klauspost/cpuid v1.2.1 // indirect - github.com/microcosm-cc/bluemonday v1.0.2 // indirect - github.com/mitchellh/go-homedir v1.1.0 - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.1 // indirect - github.com/ryanuber/columnize v2.1.0+incompatible // indirect - github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect - github.com/spf13/cobra v0.0.5 - github.com/spf13/viper v1.4.0 -)