11module Piracy
22
3- using Test: @test , @test_broken
4- using .. Aqua: walkmodules
5-
6- const DEFAULT_PKGS = (Base. PkgId (Base), Base. PkgId (Core))
7-
8- function all_methods! (
9- mod:: Module ,
10- done_callables:: Base.IdSet{Any} , # cached to prevent duplicates
11- result:: Vector{Method} ,
12- filter_default:: Bool ,
13- ):: Vector{Method}
14- for name in names (mod; all = true , imported = true )
15- # names can list undefined symbols which cannot be eval'd
16- isdefined (mod, name) || continue
17-
18- # Skip closures
19- startswith (String (name), " #" ) && continue
20- val = getfield (mod, name)
21-
22- if ! in (val, done_callables)
23- # In old versions of Julia, Vararg errors when methods is called on it
24- val === Vararg && continue
25- for method in methods (val)
26- # Default filtering removes all methods defined in DEFAULT_PKGs,
27- # since these may pirate each other.
28- if ! (filter_default && in (Base. PkgId (method. module), DEFAULT_PKGS))
29- push! (result, method)
30- end
31- end
32- push! (done_callables, val)
3+ import Test
4+
5+ # based on Test/Test.jl#detect_ambiguities
6+ # https://github.com/JuliaLang/julia/blob/v1.9.1/stdlib/Test/src/Test.jl#L1838-L1896
7+ function all_methods (mods:: Module... ; skip_deprecated:: Bool )
8+ meths = Method[]
9+ mods = collect (mods):: Vector{Module}
10+
11+ function examine (mt:: Core.MethodTable )
12+ examine (Base. MethodList (mt))
13+ end
14+ function examine (ml:: Base.MethodList )
15+ for m in ml
16+ Test. is_in_mods (m. module, true , mods) || continue
17+ push! (meths, m)
3318 end
3419 end
35- result
36- end
3720
38- function all_methods (mod:: Module ; filter_default:: Bool = true )
39- result = Method[]
40- done_callables = Base. IdSet ()
41- walkmodules (mod) do mod
42- all_methods! (mod, done_callables, result, filter_default)
21+ work = Base. loaded_modules_array ()
22+ filter! (mod -> mod === parentmodule (mod), work) # some items in loaded_modules_array are not top modules (really just Base)
23+ while ! isempty (work)
24+ mod = pop! (work)
25+ for name in names (mod; all = true )
26+ (skip_deprecated && Base. isdeprecated (mod, name)) && continue
27+ isdefined (mod, name) || continue
28+ f = Base. unwrap_unionall (getfield (mod, name))
29+ if isa (f, Module) && f != = mod && parentmodule (f) === mod && nameof (f) === name
30+ push! (work, f)
31+ elseif isa (f, DataType) &&
32+ isdefined (f. name, :mt ) &&
33+ parentmodule (f) === mod &&
34+ nameof (f) === name &&
35+ f. name. mt != = Symbol. name. mt &&
36+ f. name. mt != = DataType. name. mt
37+ examine (f. name. mt)
38+ end
39+ end
4340 end
44- return result
41+ examine (Symbol. name. mt)
42+ examine (DataType. name. mt)
43+ return meths
4544end
4645
4746# #################################
@@ -141,7 +140,7 @@ function is_foreign_method(@nospecialize(T::DataType), pkg::Base.PkgId; treat_as
141140
142141 # fallback to general code
143142 return ! (T in treat_as_own) &&
144- ! (T <: Function && T. instance in treat_as_own) &&
143+ ! (T <: Function && isdefined (T, :instance ) && T. instance in treat_as_own) &&
145144 is_foreign (T, pkg; treat_as_own = treat_as_own)
146145end
147146
165164hunt (mod:: Module ; from:: Module = mod, kwargs... ) =
166165 hunt (Base. PkgId (mod); from = from, kwargs... )
167166
168- function hunt (pkg:: Base.PkgId ; from:: Module , kwargs... )
169- filter (all_methods (from)) do method
167+ function hunt (pkg:: Base.PkgId ; from:: Module , skip_deprecated :: Bool = true , kwargs... )
168+ filter (all_methods (from; skip_deprecated = skip_deprecated )) do method
170169 Base. PkgId (method. module) === pkg && is_pirate (method; kwargs... )
171170 end
172171end
@@ -182,6 +181,7 @@ See [Julia documentation](https://docs.julialang.org/en/v1/manual/style-guide/#A
182181# Keyword Arguments
183182- `broken::Bool = false`: If true, it uses `@test_broken` instead of
184183 `@test`.
184+ - `skip_deprecated::Bool = true`: If true, it does not check deprecated methods.
185185- `treat_as_own = Union{Function, Type}[]`: The types in this container
186186 are considered to be "owned" by the module `m`. This is useful for
187187 testing packages that deliberately commit some type piracy, e.g. modules
0 commit comments