use tree_sitter::Node;
use crate::{
model::{
DeclareKind, DefineKind, EnumValueDef, Expression, FunctionDoc, GObjectType,
GObjectTypeKind, GType, InterfaceImplementation, Statement, VirtualFunction,
},
parser::Parser,
};
impl Parser {
pub(super) fn extract_gobject_from_identifier(
&self,
_id_node: Node,
parent: Node,
source: &[u8],
macro_name: &str,
) -> Option<GObjectType> {
let mut arg_values = Vec::new();
if parent.kind() == "function_declarator" {
let mut cursor = parent.walk();
for child in parent.children(&mut cursor) {
if child.kind() == "parameter_list" {
let mut params_cursor = child.walk();
for param in child.children(&mut params_cursor) {
if param.kind() == "parameter_declaration" {
let mut param_cursor = param.walk();
for param_child in param.children(&mut param_cursor) {
if param_child.kind() == "type_identifier"
|| param_child.kind() == "identifier"
{
if let Ok(text) =
std::str::from_utf8(&source[param_child.byte_range()])
{
arg_values.push(text);
}
break;
}
}
} else if param.kind() == "identifier" || param.kind() == "type_identifier"
{
if let Ok(text) = std::str::from_utf8(&source[param.byte_range()]) {
arg_values.push(text);
}
}
}
break;
}
}
} else {
self.collect_identifiers(parent, source, &mut arg_values);
}
tracing::debug!(
"extract_gobject_from_identifier for {} (parent: {}): collected identifiers: {:?}",
macro_name,
parent.kind(),
arg_values
);
arg_values.retain(|name| *name != macro_name);
if macro_name.starts_with("G_DECLARE_") && arg_values.len() >= 5 {
let type_name = arg_values[0];
let function_prefix = arg_values[1];
let module_prefix = arg_values[2];
let type_prefix = arg_values[3];
let parent_type = arg_values[4];
let type_macro = GType::Identifier(format!("{}_TYPE_{}", module_prefix, type_prefix));
let declare_kind = match macro_name {
"G_DECLARE_FINAL_TYPE" => DeclareKind::Final,
"G_DECLARE_DERIVABLE_TYPE" => DeclareKind::Derivable,
"G_DECLARE_INTERFACE" => DeclareKind::Interface,
_ => return None,
};
return Some(GObjectType {
type_name: type_name.to_owned(),
type_macro: Some(type_macro),
function_prefix: function_prefix.to_owned(),
parent_type: Some(parent_type.to_owned()),
flags: None,
kind: GObjectTypeKind::Declare {
kind: declare_kind,
module_prefix: module_prefix.to_owned(),
type_prefix: type_prefix.to_owned(),
},
interfaces: Vec::new(),
has_private: false,
manually_registered: false,
code_block_statements: Vec::new(),
export_macros: Vec::new(),
doc: None,
properties: Vec::new(),
signals: Vec::new(),
location: self.node_location(parent),
});
}
if (macro_name == "G_DEFINE_BOXED_TYPE" || macro_name == "G_DEFINE_BOXED_TYPE_WITH_CODE")
&& arg_values.len() >= 4
{
let type_name = arg_values[0];
let function_prefix = arg_values[1];
let copy_func = arg_values[2];
let free_func = arg_values[3];
let (interfaces, has_private, code_block_statements) =
if macro_name.ends_with("_WITH_CODE") {
self.extract_code_block_info_from_parent(parent, source, &arg_values)
} else {
(Vec::new(), false, Vec::new())
};
return Some(GObjectType {
type_name: type_name.to_owned(),
type_macro: None,
function_prefix: function_prefix.to_owned(),
parent_type: None,
flags: None,
kind: GObjectTypeKind::DefineBoxed {
copy_func: copy_func.to_owned(),
free_func: free_func.to_owned(),
},
interfaces,
has_private,
manually_registered: false,
code_block_statements,
export_macros: Vec::new(),
doc: None,
properties: Vec::new(),
signals: Vec::new(),
location: self.node_location(parent),
});
}
if macro_name == "G_DEFINE_QUARK" && !arg_values.is_empty() {
let func_prefix = (*arg_values.last().unwrap()).to_owned();
let quark_name = {
let mut cursor = parent.walk();
parent
.children(&mut cursor)
.find(|c| c.kind() == "argument_list")
.and_then(|al| {
let mut cursor = al.walk();
al.children(&mut cursor).find(tree_sitter::Node::is_named)
})
.and_then(|n| std::str::from_utf8(&source[n.byte_range()]).ok())
.unwrap_or("")
.to_owned()
};
return Some(GObjectType {
type_name: quark_name.clone(),
type_macro: None,
function_prefix: func_prefix.clone(),
parent_type: None,
flags: None,
kind: GObjectTypeKind::DefineQuark {
quark_name,
func_prefix,
},
interfaces: Vec::new(),
has_private: false,
manually_registered: false,
code_block_statements: Vec::new(),
export_macros: Vec::new(),
doc: None,
properties: Vec::new(),
signals: Vec::new(),
location: self.node_location(parent),
});
}
if (macro_name == "G_DEFINE_ENUM_TYPE" || macro_name == "G_DEFINE_FLAGS_TYPE")
&& arg_values.len() >= 2
{
let type_name = arg_values[0];
let function_prefix = arg_values[1];
let values = self.extract_enum_value_defs(parent, source);
let kind = if macro_name == "G_DEFINE_ENUM_TYPE" {
GObjectTypeKind::DefineEnum { values }
} else {
GObjectTypeKind::DefineFlags { values }
};
return Some(GObjectType {
type_name: type_name.to_owned(),
type_macro: None,
function_prefix: function_prefix.to_owned(),
parent_type: None,
flags: None,
kind,
interfaces: Vec::new(),
has_private: false,
manually_registered: false,
code_block_statements: Vec::new(),
export_macros: Vec::new(),
doc: None,
properties: Vec::new(),
signals: Vec::new(),
location: self.node_location(parent),
});
}
if macro_name == "G_DEFINE_POINTER_TYPE" && arg_values.len() >= 2 {
let type_name = arg_values[0];
let function_prefix = arg_values[1];
return Some(GObjectType {
type_name: type_name.to_owned(),
type_macro: None,
function_prefix: function_prefix.to_owned(),
parent_type: None,
flags: None,
kind: GObjectTypeKind::Define(DefineKind::Pointer),
interfaces: Vec::new(),
has_private: false,
manually_registered: false,
code_block_statements: Vec::new(),
export_macros: Vec::new(),
doc: None,
properties: Vec::new(),
signals: Vec::new(),
location: self.node_location(parent),
});
}
if macro_name.starts_with("G_DEFINE_") && arg_values.len() >= 3 {
let type_name = arg_values[0];
let function_prefix = arg_values[1];
let parent_type = arg_values[2];
let has_private_from_macro = matches!(
macro_name,
"G_DEFINE_TYPE_WITH_PRIVATE"
| "G_DEFINE_FINAL_TYPE_WITH_PRIVATE"
| "G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE"
);
let kind = match macro_name {
"G_DEFINE_TYPE" => GObjectTypeKind::Define(DefineKind::Type),
"G_DEFINE_TYPE_WITH_PRIVATE" => {
GObjectTypeKind::Define(DefineKind::TypeWithPrivate)
}
"G_DEFINE_ABSTRACT_TYPE" => GObjectTypeKind::Define(DefineKind::AbstractType),
"G_DEFINE_TYPE_WITH_CODE" => GObjectTypeKind::Define(DefineKind::TypeWithCode),
"G_DEFINE_FINAL_TYPE" => GObjectTypeKind::Define(DefineKind::FinalType),
"G_DEFINE_FINAL_TYPE_WITH_CODE" => {
GObjectTypeKind::Define(DefineKind::FinalTypeWithCode)
}
"G_DEFINE_FINAL_TYPE_WITH_PRIVATE" => {
GObjectTypeKind::Define(DefineKind::FinalTypeWithPrivate)
}
"G_DEFINE_ABSTRACT_TYPE_WITH_CODE" => {
GObjectTypeKind::Define(DefineKind::AbstractTypeWithCode)
}
"G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE" => {
GObjectTypeKind::Define(DefineKind::AbstractTypeWithPrivate)
}
"G_DEFINE_INTERFACE" => GObjectTypeKind::Define(DefineKind::Interface),
"G_DEFINE_INTERFACE_WITH_CODE" => {
GObjectTypeKind::Define(DefineKind::InterfaceWithCode)
}
"G_DEFINE_TYPE_EXTENDED" => GObjectTypeKind::Define(DefineKind::TypeExtended),
_ => return None,
};
let extended_flags = if macro_name == "G_DEFINE_TYPE_EXTENDED" {
Some(extract_nth_expression_text(parent, source, 3))
} else {
None
};
let (interfaces, has_private_from_code, code_block_statements) =
if macro_name.ends_with("_WITH_CODE") || macro_name == "G_DEFINE_TYPE_EXTENDED" {
self.extract_code_block_info_from_parent(parent, source, &arg_values)
} else {
(Vec::new(), false, Vec::new())
};
return Some(GObjectType {
type_name: type_name.to_owned(),
type_macro: None,
function_prefix: function_prefix.to_owned(),
parent_type: Some(parent_type.to_owned()),
flags: extended_flags,
kind,
interfaces,
has_private: has_private_from_macro || has_private_from_code,
manually_registered: false,
code_block_statements,
export_macros: Vec::new(),
doc: None,
properties: Vec::new(),
signals: Vec::new(),
location: self.node_location(parent),
});
}
if arg_values.len() >= 2 {
let (type_name, function_prefix) = if arg_values[0].contains('_')
&& arg_values[0].chars().next().is_some_and(char::is_lowercase)
{
(arg_values[0], arg_values[0])
} else {
(arg_values[0], arg_values[1])
};
return Some(GObjectType {
type_name: type_name.to_owned(),
type_macro: None,
function_prefix: function_prefix.to_owned(),
parent_type: None,
flags: None,
kind: GObjectTypeKind::DefineCustom {
macro_name: macro_name.to_owned(),
},
interfaces: Vec::new(),
has_private: false,
manually_registered: false,
code_block_statements: Vec::new(),
export_macros: Vec::new(),
doc: None,
properties: Vec::new(),
signals: Vec::new(),
location: self.node_location(parent),
});
}
None
}
pub(crate) fn extract_vfuncs(&self, body_node: Node, source: &[u8]) -> Vec<VirtualFunction> {
let mut vfuncs = Vec::new();
let mut cursor = body_node.walk();
for child in body_node.children(&mut cursor) {
if child.kind() == "field_declaration" {
if let Some(vfunc) = self.extract_vfunc_from_field(child, source) {
vfuncs.push(vfunc);
}
}
}
vfuncs
}
fn extract_vfunc_from_field(&self, field_node: Node, source: &[u8]) -> Option<VirtualFunction> {
let mut cursor = field_node.walk();
for child in field_node.children(&mut cursor) {
if child.kind() == "function_declarator" {
return self.extract_function_pointer(child, field_node, source);
}
}
None
}
fn extract_function_pointer(
&self,
func_decl: Node,
field_node: Node,
source: &[u8],
) -> Option<VirtualFunction> {
let declarator = func_decl.child_by_field_name("declarator")?;
let name = extract_pointer_declarator_name(declarator, source)?;
let return_type = self.extract_return_type(field_node, source);
let mut parameters = Vec::new();
if let Some(params_node) = func_decl.child_by_field_name("parameters") {
parameters = self.extract_parameters(params_node, source);
}
Some(VirtualFunction {
name: name.to_owned(),
return_type,
parameters,
doc: FunctionDoc::from_node(field_node, source),
location: self.node_location(field_node),
})
}
fn extract_code_block_info_from_parent(
&self,
parent: Node,
source: &[u8],
_arg_values: &[&str],
) -> (Vec<InterfaceImplementation>, bool, Vec<Statement>) {
let mut interfaces = Vec::new();
let mut has_private = false;
let code_statements = Vec::new();
let code_block = {
let mut cursor = parent.walk();
parent
.children(&mut cursor)
.find(|c| c.kind() == "gobject_code_block")
};
if let Some(block) = code_block {
let mut cursor = block.walk();
for item in block.children(&mut cursor) {
if item.kind() != "gobject_code_block_item" {
continue;
}
let mut item_cursor = item.walk();
let mut children = item.children(&mut item_cursor);
let name_node = children.find(|c| c.kind() == "identifier");
let args_node = {
let mut item_cursor2 = item.walk();
item.children(&mut item_cursor2)
.find(|c| c.kind() == "argument_list")
};
self.process_code_block_call(
name_node,
args_node,
source,
&mut interfaces,
&mut has_private,
);
}
} else {
self.find_code_block_calls_recursive(parent, source, &mut interfaces, &mut has_private);
}
(interfaces, has_private, code_statements)
}
fn process_code_block_call(
&self,
name_node: Option<Node>,
args_node: Option<Node>,
source: &[u8],
interfaces: &mut Vec<InterfaceImplementation>,
has_private: &mut bool,
) {
let name = name_node
.and_then(|n| std::str::from_utf8(&source[n.byte_range()]).ok())
.unwrap_or("");
match name {
"G_ADD_PRIVATE" => {
*has_private = true;
}
"G_IMPLEMENT_INTERFACE" => {
if let Some(args) = args_node {
let mut iface_args = Vec::new();
self.collect_identifiers(args, source, &mut iface_args);
if !iface_args.is_empty() {
interfaces.push(InterfaceImplementation {
interface_type: GType::Identifier(iface_args[0].to_owned()),
init_function: iface_args.get(1).map(std::string::ToString::to_string),
});
}
}
}
_ => {}
}
}
fn find_code_block_calls_recursive(
&self,
node: Node,
source: &[u8],
interfaces: &mut Vec<InterfaceImplementation>,
has_private: &mut bool,
) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() == "call_expression" {
let func_node = child.child_by_field_name("function");
let args_node = child.child_by_field_name("arguments");
self.process_code_block_call(func_node, args_node, source, interfaces, has_private);
}
self.find_code_block_calls_recursive(child, source, interfaces, has_private);
}
}
fn extract_enum_value_defs(&self, parent: Node, source: &[u8]) -> Vec<EnumValueDef> {
let mut values = Vec::new();
let arg_list = {
let mut cursor = parent.walk();
parent
.children(&mut cursor)
.find(|c| c.kind() == "argument_list")
};
let Some(arg_list) = arg_list else {
return values;
};
let mut cursor = arg_list.walk();
for child in arg_list.children(&mut cursor) {
if child.kind() != "call_expression" {
continue;
}
let func_node = child.child_by_field_name("function");
let func_name = func_node
.and_then(|n| std::str::from_utf8(&source[n.byte_range()]).ok())
.unwrap_or("");
if func_name != "G_DEFINE_ENUM_VALUE" {
continue;
}
let Some(args) = child.child_by_field_name("arguments") else {
continue;
};
let mut name = None;
let mut nick = None;
let mut args_cursor = args.walk();
for arg in args.children(&mut args_cursor) {
if (arg.kind() == "identifier" || arg.kind() == "type_identifier") && name.is_none()
{
name = std::str::from_utf8(&source[arg.byte_range()]).ok();
} else if arg.kind() == "string_literal" && nick.is_none() {
let raw = std::str::from_utf8(&source[arg.byte_range()]).unwrap_or("");
nick = Some(raw.trim_matches('"'));
}
}
if let (Some(n), Some(k)) = (name, nick) {
values.push(EnumValueDef {
name: n.to_owned(),
nick: k.to_owned(),
});
}
}
values
}
pub(super) fn collect_identifiers<'a>(
&self,
node: Node,
source: &'a [u8],
result: &mut Vec<&'a str>,
) {
if (node.kind() == "identifier" || node.kind() == "type_identifier")
&& let Ok(text) = std::str::from_utf8(&source[node.byte_range()])
{
result.push(text);
return;
}
if Self::is_expression_node(&node)
&& let Some(expr) = self.parse_expression(node, source)
{
collect_identifiers_from_expr(&expr, source, result);
return;
}
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
self.collect_identifiers(child, source, result);
}
}
}
fn extract_nth_expression_text(parent: Node, source: &[u8], n: usize) -> String {
let mut expr_count = 0;
let mut cursor = parent.walk();
for child in parent.children(&mut cursor) {
if child.is_named() && child.kind() != "gobject_code_block" {
if expr_count == 0 {
expr_count += 1;
continue;
}
if expr_count - 1 == n {
return std::str::from_utf8(&source[child.byte_range()])
.unwrap_or("0")
.trim()
.to_owned();
}
expr_count += 1;
}
}
"0".to_owned()
}
fn collect_identifiers_from_expr<'a>(
expr: &Expression,
source: &'a [u8],
result: &mut Vec<&'a str>,
) {
expr.walk(&mut |e| {
if let Expression::Identifier(id) = e
&& let Ok(text) =
std::str::from_utf8(&source[id.location.start_byte..id.location.end_byte])
{
result.push(text);
}
});
}
fn extract_pointer_declarator_name<'a>(declarator: Node, source: &'a [u8]) -> Option<&'a str> {
if declarator.kind() == "parenthesized_declarator" {
let mut cursor = declarator.walk();
for child in declarator.children(&mut cursor) {
if child.kind() == "pointer_declarator" {
return extract_pointer_declarator_name(child, source);
} else if child.kind() == "identifier" || child.kind() == "field_identifier" {
return std::str::from_utf8(&source[child.byte_range()]).ok();
}
}
} else if declarator.kind() == "pointer_declarator" {
if let Some(inner) = declarator.child_by_field_name("declarator") {
if inner.kind() == "identifier" || inner.kind() == "field_identifier" {
return std::str::from_utf8(&source[inner.byte_range()]).ok();
}
return extract_pointer_declarator_name(inner, source);
}
} else if declarator.kind() == "identifier" || declarator.kind() == "field_identifier" {
return std::str::from_utf8(&source[declarator.byte_range()]).ok();
}
None
}