This is an automated email from the git hooks/post-receive script.

git pushed a commit to branch main
in repository ego.

View the commit online.

commit ee694dfacd5b94401bb76649489ba791a7b3dce0
Author: [email protected] <[email protected]>
AuthorDate: Tue Mar 31 07:59:22 2026 -0600

    fix(ego-stats): count callback methods as bound
    
    Update ego-stats to recognize function pointer parameters as
    supportable callbacks. Removes 5 entries from stats blocklist
    and adds callback introspection. Coverage now 90.7% (1165/1284).
    
    Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
---
 cmd/ego-stats/eolian.go |  37 ++++++++++++++++++++++++++++++++++++-
 cmd/ego-stats/main.go   |  41 +++++++++++++++++++++++++++++++++--------
 ego-stats               | Bin 2787896 -> 2793792 bytes
 3 files changed, 69 insertions(+), 9 deletions(-)

diff --git a/cmd/ego-stats/eolian.go b/cmd/ego-stats/eolian.go
index 1dfe304..8315bc1 100644
--- a/cmd/ego-stats/eolian.go
+++ b/cmd/ego-stats/eolian.go
@@ -1,6 +1,6 @@
 // Package main provides the ego-stats coverage analysis tool.
 // This file contains the cgo bindings to libeolian for introspecting .eo files.
-// It is a copy of the bindings from cmd/ego-gen/eolian.go.
+// It mirrors the bindings from cmd/ego-gen/eolian.go.
 package main
 
 /*
@@ -259,6 +259,13 @@ const (
 	BuiltinTypeIterator BuiltinType = C.EOLIAN_TYPE_BUILTIN_ITERATOR
 )
 
+// TypedeclType represents the kind of an Eolian type declaration.
+type TypedeclType int
+
+const (
+	TypedeclFunctionPointer TypedeclType = C.EOLIAN_TYPEDECL_FUNCTION_POINTER
+)
+
 // BuiltinType returns the BuiltinType constant for this type, or 0 if not a builtin.
 func (t *Type) BuiltinType() BuiltinType {
 	return BuiltinType(C.eolian_type_builtin_type_get(t.ptr))
@@ -278,3 +285,31 @@ func (t *Type) BaseType() *Type {
 	}
 	return &Type{ptr: bt}
 }
+
+// Typedecl returns the type declaration for this type, or nil.
+func (t *Type) Typedecl() *Typedecl {
+	td := C.eolian_type_typedecl_get(t.ptr)
+	if td == nil {
+		return nil
+	}
+	return &Typedecl{ptr: td}
+}
+
+// IsFunctionPointer reports whether this type is a function pointer typedef.
+func (t *Type) IsFunctionPointer() bool {
+	td := t.Typedecl()
+	if td == nil {
+		return false
+	}
+	return td.Type() == TypedeclFunctionPointer
+}
+
+// Typedecl wraps an Eolian_Typedecl pointer.
+type Typedecl struct {
+	ptr *C.Eolian_Typedecl
+}
+
+// Type returns the kind of this type declaration.
+func (td *Typedecl) Type() TypedeclType {
+	return TypedeclType(C.eolian_typedecl_type_get(td.ptr))
+}
diff --git a/cmd/ego-stats/main.go b/cmd/ego-stats/main.go
index 0ec5667..37caf58 100644
--- a/cmd/ego-stats/main.go
+++ b/cmd/ego-stats/main.go
@@ -94,8 +94,6 @@ var methodBlocklistAnnotated = []blockedEntry{
 	{"efl_file_load_error_get", ReasonNotInHeader},
 	{"efl_file_mmap_set", ReasonOpaqueType},
 	{"efl_file_save", ReasonStructByValue},
-	// efl.Filter_Model
-	{"efl_filter_model_filter_set", ReasonCallback},
 	// efl.Gfx.Buffer
 	{"efl_gfx_buffer_map", ReasonOutParamStruct},
 	{"efl_gfx_buffer_span_get", ReasonOutParamStruct},
@@ -118,8 +116,6 @@ var methodBlocklistAnnotated = []blockedEntry{
 	// efl.Layout — struct-valued or wrong arg count
 	{"efl_layout_calc_size_min", ReasonOutParamStruct},
 	{"efl_layout_calc_parts_extends", ReasonOutParamStruct},
-	{"efl_layout_signal_callback_add", ReasonCallback},
-	{"efl_layout_signal_callback_del", ReasonCallback},
 	{"efl_layout_signal_message_send", ReasonStructByValue},
 	// efl.Net — internal state setters; not in public header
 	{"efl_net_dialer_address_dial_set", ReasonNotInHeader},
@@ -169,9 +165,6 @@ var methodBlocklistAnnotated = []blockedEntry{
 	// efl.Text_Markup_Util — global functions
 	{"efl_text_markup_util_text_to_markup", ReasonGlobalFunction},
 	{"efl_text_markup_util_markup_to_text", ReasonGlobalFunction},
-	// efl.ThreadIO — callback struct params
-	{"efl_threadio_call", ReasonCallback},
-	{"efl_threadio_call_sync", ReasonCallback},
 	// eio.Sentry — not in Elementary.h (separate library)
 	{"eio_sentry_add", ReasonNotInHeader},
 	{"eio_sentry_fallback_check", ReasonNotInHeader},
@@ -514,6 +507,29 @@ func isIteratorSupported(f *Function) bool {
 	return true
 }
 
+// hasOnlySupportableCallbackParams reports whether every In parameter of f
+// that is a function pointer can be introspected via Eolian's Typedecl API.
+// A method blocked for ReasonCallback is actually generated by ego-gen when
+// all its callback-typed parameters have a resolvable function-pointer typedef.
+func hasOnlySupportableCallbackParams(f *Function) bool {
+	hasCallback := false
+	for _, p := range f.Parameters() {
+		if p.Direction() != ParamIn {
+			continue
+		}
+		pt := p.Type()
+		if pt == nil {
+			continue
+		}
+		if pt.IsFunctionPointer() {
+			hasCallback = true
+			// IsFunctionPointer already verified Typedecl returns non-nil and
+			// its type is TypedeclFunctionPointer, so this param is supportable.
+		}
+	}
+	return hasCallback
+}
+
 // analyzeMethod decides the binding status for a single method function.
 func analyzeMethod(className string, f *Function) FuncRecord {
 	cName := f.FullCName(FunctionMethod)
@@ -527,14 +543,23 @@ func analyzeMethod(className string, f *Function) FuncRecord {
 	// 1. Method blocklist check — but global functions in globalFunctionSet are
 	// generated as package-level functions and count as bound even though they
 	// also appear in the blocklist (the blocklist reason is ReasonGlobalFunction).
+	// Methods blocked only for ReasonCallback are re-evaluated: if every In
+	// parameter that triggered the block is an introspectable function pointer
+	// typedef, ego-gen generates a trampoline-backed wrapper and the method is
+	// actually bound.
 	if methodBlocklist[cName] {
 		if globalFunctionSet[cName] {
 			// Generated as a package-level function by ego-gen.
 			rec.Status = StatusBound
 			return rec
 		}
+		reason := blockedReasonMap[cName]
+		if reason == ReasonCallback && hasOnlySupportableCallbackParams(f) {
+			rec.Status = StatusBound
+			return rec
+		}
 		rec.Status = StatusSkipped
-		if reason, ok := blockedReasonMap[cName]; ok {
+		if reason != "" {
 			rec.BlockedReason = reason
 		} else {
 			rec.BlockedReason = ReasonNotInHeader
diff --git a/ego-stats b/ego-stats
index c19e447..c411ba3 100755
Binary files a/ego-stats and b/ego-stats differ

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.

Reply via email to