# Pirate Hunter

**URL:** <https://discourse.julialang.org/t/pirate-hunter/20402>\
**Category:** Tooling\
**Created:** [February 3, 2019, 3:08pm UTC](https://discourse.julialang.org/t/pirate-hunter/20402 "2019-02-03T15:08:08Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![oxinabox](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oxinabox/32/206603_2.png) [@oxinabox](https://discourse.julialang.org/u/oxinabox)\
**Post date:** [February 3, 2019, 3:08pm UTC](https://discourse.julialang.org/t/pirate-hunter/20402/1 "2019-02-03T15:08:08Z")

</div>

I made a script to hunt out type piracy.  
It isn’t perfect, but its not bad.

invoke it by calling `hunt` on a module, function or list of methods.  
Or if you just call `hunt()` from the REPL it will search all modules currenlty loaded from `Main`.  
There are [27 pirate actions detected within the standard libraries.](https://github.com/JuliaLang/julia/issues/30945)

These will likely show up when searching your own modules as you probably reference at very least some standard libraries at some point.  
So you might want to do some post processing to filter tthem out

```julia
# PirateHunter.jl

###############

function all_methods(mainmod::Module=Main)
    found_methods = Vector{Method}()

    # We begin with the assumption nothing in Core.Compiler should be indexed
    done_modules = Set{Module}()
    done_functions = Set()
        

    function index(mod::Module)
        mod ∈ done_modules && return
        push!(done_modules, mod)
        @info "Indexing" mod=mod

        for name in names(mod; all=true)
            val = try
                String(name)[1]=='#' && continue
                Core.eval(mod, name)
            catch err
                @warn "while working out what things was, had error" thing=name mod=mod exception=err
            end

            val isa Core.Compiler.BitArray{1} && continue # This type is unhashable and unprintable

            index(val)
        end
    end

    function index_func(f)
        try
            f ∈ done_functions && return
            push!(done_functions, f)
            append!(found_methods, methods(f))
        catch err
            @warn exception=err
        end
    end

    function index(x)
        index_func(x) 
    end

    function index(x::Function)
        index_func(x)
        index_func(typeof(x)) # all functionas have a type
    end

    #########
    # Invoke it
    index(mainmod) # first arg is dummy

    @info "indexing complete" length(found_methods)
    return found_methods
end

##################################

union_types(U) = getproperty.(U, propertynames(U))

get_module(x) = get_module(typeof(x))
get_module(meth::Method) = meth.module
get_module(U::UnionAll) = get_module(U.body)
get_module(T::DataType) = T.name.module
function get_module(U::Union)
    modules = unique(get_module, union_types)
    if length(modules)==1
        return first(modules)
    else
        error("There is not a common module for all types in $U")
    end
end

hunt(mod::Module=Main) = hunt(all_methods(mod))
hunt(f) = hunt(methods(f))

function hunt(methods::Union{Base.MethodList, AbstractVector{Method}})
    function is_pirate(meth::Method)
        method_module = get_module(meth)
        method_pkg = Base.PkgId(method_module)

        meth.sig isa UnionAll && return false # TODO: Deal with this properly
        length(meth.sig.parameters) < 2 && return false # can't pirate without args

        function_type = meth.sig.parameters[1]
        local function_module
        try
            function_module = get_module(function_type)
            function_pkg = Base.PkgId(function_module)
            Base.PkgId(function_module) == method_pkg && return false # can't pirate own function

            # Base and Core can't pirate each other
            ((function_pkg == Base.PkgId(Base) && method_pkg == Base.PkgId(Core)) ||
            function_pkg == Base.PkgId(Core) && method_pkg == Base.PkgId(Base)) && return false
        catch err
            # Not actually a big issue, as this can not lead to a difference result
            @warn "Failed to find function's module" function_type exception=err
            function_module = "The module that defined $(function_type)"
        end

        
        function is_own(arg_type::DataType)
            try
                arg_module = get_module(arg_type)
                arg_pkg = Base.PkgId(arg_module)
                arg_pkg == method_pkg && return true
            catch err
                @warn "Failed to find arg types 's module" arg_type exception=err
            end

            return any(is_own, arg_type.parameters)
        end
        

        is_own(U::Union) = all(is_own, union_types(U))
        is_own(::UnionAll) = return true # TODO: Deal with this properly
        is_own(x) = is_own(typeof(x)) # for Int, Symbol and other bit types that show up in type-params

        arg_types_type = meth.sig.parameters[2:end]
        any(is_own, arg_types_type) && return false
        @error "Pirate Found!" meth pirate=method_module victim=function_module
        return true # has failed to prove that they are not a pirate.
    end

    return filter(is_pirate, methods)
end

```

---

<div class="post-metadata">

**Author:** ![chakravala](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chakravala/32/6832_2.png) [@chakravala](https://discourse.julialang.org/u/chakravala)\
**Post date:** [February 3, 2019, 3:14pm UTC](https://discourse.julialang.org/t/pirate-hunter/20402/2 "2019-02-03T15:14:51Z")

</div>

Package it.

However, I do get this error when I test it on my own package

```nohighlight
julia> hunt(Grassmann)
┌ Info: Indexing
└ mod = Grassmann
┌ Warning: while working out what things was, had error
│ thing = :isudal
│ mod = Grassmann
│ exception = UndefVarError: isudal not defined
└ @ Main REPL[2]:24
┌ Info: indexing complete
└ length(found_methods) = 271
ERROR: MethodError: no method matching hunt(::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method, ::Method)
Closest candidates are:
  hunt(::Any) at REPL[10]:1
Stacktrace:
 [1] hunt(::Module) at ./REPL[9]:1
 [2] top-level scope at none:0

```

---

<div class="post-metadata">

**Author:** ![oxinabox](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oxinabox/32/206603_2.png) [@oxinabox](https://discourse.julialang.org/u/oxinabox)\
**Post date:** [February 3, 2019, 3:23pm UTC](https://discourse.julialang.org/t/pirate-hunter/20402/3 "2019-02-03T15:23:29Z")

</div>

You’ll get many warning when using it,  
because the way it searches for things is not exactly nice.  
It goes looking for methods everywhere.

I see I had an errant `...`, in one of the hunt methods.  
Will be fixed in 30s

---

<div class="post-metadata">

**Author:** ![johnh](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/johnh/32/3615_2.png) [@johnh](https://discourse.julialang.org/u/johnh)\
**Post date:** [February 3, 2019, 10:10pm UTC](https://discourse.julialang.org/t/pirate-hunter/20402/4 "2019-02-03T22:10:52Z")

</div>

Does it work with Arrrrrrray tyoes?

---

<div class="post-metadata">

**Author:** ![Ralph\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ralph_smith/32/10344_2.png) [@Ralph\_Smith](https://discourse.julialang.org/u/Ralph_Smith)\
**Post date:** [February 4, 2019, 12:29am UTC](https://discourse.julialang.org/t/pirate-hunter/20402/5 "2019-02-04T00:29:08Z")

</div>

> [@johnh](#):
>
> Does it work with Arrrrrrray types?

Alas, the `UnionAll` omission

> [@oxinabox](#):
>
> `is_own(::UnionAll) = return true # TODO: Deal with this properly`

overlooks things like

```julia
function (*)(A::AbstractMatrix, B::AbstractMatrix)

```

which is in `LinearAlgebra`. We matrix-wranglers are proud privateers, but look forward to elaboration of @oxinabox’ functions to make sure we plunder only deserving victims.

---

<div class="post-metadata">

**Author:** ![oxinabox](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oxinabox/32/206603_2.png) [@oxinabox](https://discourse.julialang.org/u/oxinabox)\
**Post date:** [February 4, 2019, 9:15am UTC](https://discourse.julialang.org/t/pirate-hunter/20402/6 "2019-02-04T09:15:47Z")

</div>

I should move this to a package so that I can take PRs.  
I am sure someone can workout the logic behind how to check `UnionAll`s

---

<div class="post-metadata">

**Author:** ![oxinabox](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oxinabox/32/206603_2.png) [@oxinabox](https://discourse.julialang.org/u/oxinabox)\
**Post date:** [June 30, 2021, 6:57pm UTC](https://discourse.julialang.org/t/pirate-hunter/20402/7 "2021-06-30T18:57:00Z")

</div>

I bet this PR in ExprTools would make us able to handle all the UnionAlls.  
[https://github.com/invenia/ExprTools.jl/pull/19](https://github.com/invenia/ExprTools.jl/pull/19)  
That and the fact that I have in writing ExprTools got uncomfortably familiar with the internals of `Method`s

---

<div class="post-metadata">

**Author:** ![jzr](https://avatars.discourse-cdn.com/v4/letter/j/eb9ed0/32.png) [@jzr](https://discourse.julialang.org/u/jzr)\
**Post date:** [January 11, 2022, 9:03pm UTC](https://discourse.julialang.org/t/pirate-hunter/20402/8 "2022-01-11T21:03:40Z")

</div>

Is there a packaged version of PirateHunter somewhere?

---

<div class="post-metadata">

**Author:** ![oxinabox](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oxinabox/32/206603_2.png) [@oxinabox](https://discourse.julialang.org/u/oxinabox)\
**Post date:** [January 12, 2022, 1:28pm UTC](https://discourse.julialang.org/t/pirate-hunter/20402/9 "2022-01-12T13:28:55Z")

</div>

not by me, but @davidanthoff or @ZacLN might know of one since VS-Code can detect piracy

---

<div class="post-metadata">

**Author:** ![oxinabox](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oxinabox/32/206603_2.png) [@oxinabox](https://discourse.julialang.org/u/oxinabox)\
**Post date:** [May 24, 2024, 1:57pm UTC](https://discourse.julialang.org/t/pirate-hunter/20402/10 "2024-05-24T13:57:26Z")

</div>

It is now available in Aqua.jl  
in an improved form that fixes the corner cases I left out
