Skip to content

Commit 0e0a974

Browse files
vtjnashKristofferC
authored andcommitted
InteractiveUtils: recursive correctly in varinfo, et al. (#42061)
* InteractiveUtils: recursive correctly in varinfo, et al. Fixes #42045 (cherry picked from commit a163e37)
1 parent a3a9182 commit 0e0a974

File tree

1 file changed

+48
-67
lines changed

1 file changed

+48
-67
lines changed

stdlib/InteractiveUtils/src/InteractiveUtils.jl

Lines changed: 48 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -34,40 +34,40 @@ The memory consumption estimate is an approximate lower bound on the size of the
3434
- `sortby` : the column to sort results by. Options are `:name` (default), `:size`, and `:summary`.
3535
"""
3636
function varinfo(m::Module=Main, pattern::Regex=r""; all::Bool = false, imported::Bool = false, sortby::Symbol = :name, recursive::Bool = false)
37-
@assert sortby in [:name, :size, :summary] "Unrecognized `sortby` value `:$sortby`. Possible options are `:name`, `:size`, and `:summary`"
38-
function _populate_rows(m2::Module, allrows, include_self::Bool, prep::String)
39-
newrows = Any[
40-
let
41-
value = getfield(m2, v)
42-
ssize_str, ssize = if value===Base || value===Main || value===Core
37+
sortby in (:name, :size, :summary) || throw(ArgumentError("Unrecognized `sortby` value `:$sortby`. Possible options are `:name`, `:size`, and `:summary`"))
38+
rows = Vector{Any}[]
39+
workqueue = [(m, ""),]
40+
while !isempty(workqueue)
41+
m2, prep = popfirst!(workqueue)
42+
for v in names(m2; all, imported)
43+
if !isdefined(m2, v) || !occursin(pattern, string(v))
44+
continue
45+
end
46+
value = getfield(m2, v)
47+
isbuiltin = value === Base || value === Main || value === Core
48+
if recursive && !isbuiltin && isa(value, Module) && value !== m2 && nameof(value) === v && parentmodule(value) === m2
49+
push!(workqueue, (value, "$prep$v."))
50+
end
51+
ssize_str, ssize = if isbuiltin
4352
("", typemax(Int))
4453
else
4554
ss = summarysize(value)
4655
(format_bytes(ss), ss)
4756
end
48-
Any[string(prep, v), ssize_str, summary(value), ssize]
49-
end
50-
for v in names(m2; all, imported)
51-
if (string(v) != split(string(m2), ".")[end] || include_self) && isdefined(m2, v) && occursin(pattern, string(v)) ]
52-
append!(allrows, newrows)
53-
if recursive
54-
for row in newrows
55-
if row[3] == "Module" && !in(split(row[1], ".")[end], [split(string(m2), ".")[end], "Base", "Main", "Core"])
56-
_populate_rows(getfield(m2, Symbol(split(row[1], ".")[end])), allrows, false, prep * "$(row[1]).")
57-
end
58-
end
57+
push!(rows, Any[string(prep, v), ssize_str, summary(value), ssize])
5958
end
60-
return allrows
6159
end
62-
rows = _populate_rows(m, Vector{Any}[], true, "")
63-
if sortby == :name
64-
col, reverse = 1, false
65-
elseif sortby == :size
66-
col, reverse = 4, true
67-
elseif sortby == :summary
68-
col, reverse = 3, false
60+
let (col, rev) = if sortby == :name
61+
1, false
62+
elseif sortby == :size
63+
4, true
64+
elseif sortby == :summary
65+
3, false
66+
else
67+
@assert "unreachable"
68+
end
69+
sort!(rows; by=r->r[col], rev)
6970
end
70-
rows = sort!(rows, by=r->r[col], rev=reverse)
7171
pushfirst!(rows, Any["name", "size", "summary"])
7272

7373
return Markdown.MD(Any[Markdown.Table(map(r->r[1:3], rows), Symbol[:l, :r, :l])])
@@ -208,54 +208,35 @@ function methodswith(t::Type; supertypes::Bool=false)
208208
end
209209

210210
# subtypes
211-
function _subtypes(m::Module, x::Type, sts=Base.IdSet{Any}(), visited=Base.IdSet{Module}())
212-
push!(visited, m)
211+
function _subtypes_in!(mods::Array, x::Type)
213212
xt = unwrap_unionall(x)
214-
if !isa(xt, DataType)
215-
return sts
213+
if !isabstracttype(x) || !isa(xt, DataType)
214+
# Fast path
215+
return Type[]
216216
end
217-
xt = xt::DataType
218-
for s in names(m, all = true)
219-
if isdefined(m, s) && !isdeprecated(m, s)
220-
t = getfield(m, s)
221-
if isa(t, DataType)
222-
t = t::DataType
223-
if t.name.name === s && supertype(t).name == xt.name
224-
ti = typeintersect(t, x)
225-
ti != Bottom && push!(sts, ti)
226-
end
227-
elseif isa(t, UnionAll)
228-
t = t::UnionAll
229-
tt = unwrap_unionall(t)
230-
isa(tt, DataType) || continue
231-
tt = tt::DataType
232-
if tt.name.name === s && supertype(tt).name == xt.name
233-
ti = typeintersect(t, x)
234-
ti != Bottom && push!(sts, ti)
217+
sts = Vector{Any}()
218+
while !isempty(mods)
219+
m = pop!(mods)
220+
xt = xt::DataType
221+
for s in names(m, all = true)
222+
if isdefined(m, s) && !isdeprecated(m, s)
223+
t = getfield(m, s)
224+
dt = isa(t, UnionAll) ? unwrap_unionall(t) : t
225+
if isa(dt, DataType)
226+
if dt.name.name === s && dt.name.module == m && supertype(dt).name == xt.name
227+
ti = typeintersect(t, x)
228+
ti != Bottom && push!(sts, ti)
229+
end
230+
elseif isa(t, Module) && nameof(t) === s && parentmodule(t) === m && t !== m
231+
t === Base || push!(mods, t) # exclude Base, since it also parented by Main
235232
end
236-
elseif isa(t, Module)
237-
t = t::Module
238-
in(t, visited) || _subtypes(t, x, sts, visited)
239233
end
240234
end
241235
end
242-
return sts
243-
end
244-
245-
function _subtypes_in(mods::Array, x::Type)
246-
if !isabstracttype(x)
247-
# Fast path
248-
return Type[]
249-
end
250-
sts = Base.IdSet{Any}()
251-
visited = Base.IdSet{Module}()
252-
for m in mods
253-
_subtypes(m, x, sts, visited)
254-
end
255-
return sort!(collect(sts), by=string)
236+
return permute!(sts, sortperm(map(string, sts)))
256237
end
257238

258-
subtypes(m::Module, x::Type) = _subtypes_in([m], x)
239+
subtypes(m::Module, x::Type) = _subtypes_in!([m], x)
259240

260241
"""
261242
subtypes(T::DataType)
@@ -274,7 +255,7 @@ julia> subtypes(Integer)
274255
Unsigned
275256
```
276257
"""
277-
subtypes(x::Type) = _subtypes_in(Base.loaded_modules_array(), x)
258+
subtypes(x::Type) = _subtypes_in!(Base.loaded_modules_array(), x)
278259

279260
"""
280261
supertypes(T::Type)

0 commit comments

Comments
 (0)