After some trial and error, the solution so far looks like this:
code_info(x, tp=Tuple) = Base.CodeInfo[]
code_info(fun::Function, t=Tuple) = Base.code_lowered(fun, t)
function code_info(s::Symbol, tp=Tuple)
local fun
try
fun = eval(s)
catch
return Base.CodeInfo[]
else
code_info(fun, tp)
end
end
function code_info(v::AbstractVector, t=Tuple)
unique(mapreduce(x->code_info(x, t), vcat, v,init=Base.CodeInfo[]))
end
global_refs(x) = Core.GlobalRef[]
global_refs(r::GlobalRef) = [r]
global_refs(ex::Expr) = global_refs(ex.args)
global_refs(ci::Base.CodeInfo) = global_refs(ci.code)
function global_refs(cis::AbstractVector)::Vector{GlobalRef}
refs = mapreduce(global_refs, vcat, cis, init=Core.GlobalRef[])
unique(refs)
end
Using these functions, I can check for the “bad” (or “good”) calls:
function search_main_calls(fun::Symbol, sym::Symbol, types=Tuple; recursion_limit=5)
for k in 1:recursion_limit
ci = code_info(fun, types)
refs = global_refs(ci)
if any(r.name===sym for r in refs if r.mod===Main)
return :found
end
fun = [r.name for r in refs if r.mod===Main]
end
return :not_found
end
This does not try to recurse into standard library and detects your attempt to cheat:
julia> search_main_calls(:my_sum, :sum)
:found
If there’re any counterexamples to that solution, I’d appreciate hearing about them.