Skip to content

Commit 3c2ee37

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 9990c97 commit 3c2ee37

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])])
@@ -206,54 +206,35 @@ function methodswith(t::Type; supertypes::Bool=false)
206206
end
207207

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

256-
subtypes(m::Module, x::Type) = _subtypes_in([m], x)
237+
subtypes(m::Module, x::Type) = _subtypes_in!([m], x)
257238

258239
"""
259240
subtypes(T::DataType)
@@ -270,7 +251,7 @@ julia> subtypes(Integer)
270251
Unsigned
271252
```
272253
"""
273-
subtypes(x::Type) = _subtypes_in(Base.loaded_modules_array(), x)
254+
subtypes(x::Type) = _subtypes_in!(Base.loaded_modules_array(), x)
274255

275256
"""
276257
supertypes(T::Type)

0 commit comments

Comments
 (0)