Skip to content

Commit 3849c9d

Browse files
authored
reflection: refine and accurately define the options for names (#54659)
This commit refines the keyword arguments that `names` can accept, allowing for more specific exclusions, such as excluding deprecated names or implicitly `using`-ed names. For the new behavior of `names`, please refer to the following docstring: > names(x::Module; non_public::Bool=false, imported::Bool=false, > usings_explicit::Bool=false, usings_implicit::Bool=false, > generated::Bool=false, deprecated::Bool=false, > [usings::Bool=false], [all::Bool=false]) -> Vector{Symbol} > > Return a vector of the sorted names of module `x`. > > By default, only public names defined within `x` are returned. > > By specifying the following keyword arguments, additional names can be included: > - `non_public=true`: includes names defined within `x` that are not public. > - `imported=true`: includes names explicitly imported from other modules via `import` statements. > - `usings_explicit=true`: includes names explicitly imported from other modules via `using` statements. > - `usings_implicit=true`: includes names implicitly imported from other modules via `using` statements (i.e., names exported by modules that `x` has `using`-ed). > - `generated=true`: includes names generated by the compiler frontend (specifically those starting with `#`). > - `deprecated=true`: includes deprecated names. Note that the previously existing options (`all` and `usings`) are retained as alias arguments, ensuring that the previous behavior of `names` is preserved. > Additionally, the following keyword arguments can be used as aliases to conveniently specify these options: > - `usings::Bool=true`: a shortcut to set both `usings_explicit` and `usings_implicit` to `true`. > - `all::Bool=true`: a shortcut to set `non_public`, `generated`, and `deprecated` to `true`. By utilizing these newly and accurately defined options, the implementation of downstream applications like REPL completions can be simplified.
1 parent 71fa11f commit 3849c9d

File tree

9 files changed

+85
-59
lines changed

9 files changed

+85
-59
lines changed

base/docs/Docs.jl

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -821,9 +821,8 @@ compiler-generated hidden symbols starting with `#`).
821821
See also: [`names`](@ref), [`Docs.hasdoc`](@ref), [`Base.ispublic`](@ref).
822822
"""
823823
function undocumented_names(mod::Module; private::Bool=false)
824-
filter!(names(mod; all=true)) do sym
825-
!hasdoc(mod, sym) && !startswith(string(sym), '#') &&
826-
(private || Base.ispublic(mod, sym))
824+
filter!(names(mod; non_public=true, deprecated=true)) do sym
825+
!hasdoc(mod, sym) && (private || Base.ispublic(mod, sym))
827826
end
828827
end
829828

base/reflection.jl

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -77,23 +77,35 @@ function fullname(m::Module)
7777
end
7878

7979
"""
80-
names(x::Module; all::Bool=false, imported::Bool=false, usings::Bool=false) -> Vector{Symbol}
80+
names(x::Module; non_public::Bool=false, imported::Bool=false,
81+
usings_explicit::Bool=false, usings_implicit::Bool=false,
82+
generated::Bool=false, deprecated::Bool=false,
83+
[usings::Bool=false], [all::Bool=false]) -> Vector{Symbol}
8184
82-
Get a vector of the public names of a `Module`, excluding deprecated names.
83-
If `all` is true, then the list also includes non-public names defined in the module,
84-
deprecated names, and compiler-generated names.
85-
If `imported` is true, then names explicitly imported from other modules
86-
are also included.
87-
If `usings` is true, then names explicitly imported via `using` are also included.
88-
Names are returned in sorted order.
85+
Return a vector of the sorted names of module `x`.
8986
90-
As a special case, all names defined in `Main` are considered \"public\",
91-
since it is not idiomatic to explicitly mark names from `Main` as public.
87+
By default, only public names defined within `x` are returned.
88+
89+
By specifying the following keyword arguments, additional names can be included:
90+
- `non_public=true`: includes names defined within `x` that are not public.
91+
- `imported=true`: includes names explicitly imported from other modules via `import` statements.
92+
- `usings_explicit=true`: includes names explicitly imported from other modules via `using` statements.
93+
- `usings_implicit=true`: includes names implicitly imported from other modules via `using` statements
94+
(i.e., names exported by modules that `x` has `using`-ed).
95+
- `generated=true`: includes names generated by the compiler frontend (specifically those starting with `#`).
96+
- `deprecated=true`: includes deprecated names.
97+
98+
Additionally, the following keyword arguments can be used as aliases to conveniently specify these options:
99+
- `usings::Bool=true`: a shortcut to set both `usings_explicit` and `usings_implicit` to `true`.
100+
- `all::Bool=true`: a shortcut to set `non_public`, `generated`, and `deprecated` to `true`.
92101
93102
!!! note
94-
`sym ∈ names(SomeModule)` does *not* imply `isdefined(SomeModule, sym)`.
95-
`names` may return symbols marked with `public` or `export`, even if
96-
they are not defined in the module.
103+
As a special case, all names defined in `Main` are considered \"public\",
104+
since it is not idiomatic to explicitly mark names from `Main` as public.
105+
106+
!!! note
107+
`sym ∈ names(x)` does *not* imply `isdefined(x, sym)`.
108+
`names` may return symbols that are only declared and not necessarily defined yet.
97109
98110
!!! warning
99111
`names` may return duplicate names. The duplication happens, e.g. if an `import`ed name
@@ -102,8 +114,16 @@ since it is not idiomatic to explicitly mark names from `Main` as public.
102114
See also: [`Base.isexported`](@ref), [`Base.ispublic`](@ref), [`Base.@locals`](@ref), [`@__MODULE__`](@ref).
103115
"""
104116
names(m::Module; kwargs...) = sort!(unsorted_names(m; kwargs...))
105-
unsorted_names(m::Module; all::Bool=false, imported::Bool=false, usings::Bool=false) =
106-
ccall(:jl_module_names, Array{Symbol,1}, (Any, Cint, Cint, Cint), m, all, imported, usings)
117+
function unsorted_names(m::Module; non_public::Bool=false, imported::Bool=false,
118+
usings_explicit::Bool=false, usings_implicit::Bool=false,
119+
generated::Bool=false, deprecated::Bool=false,
120+
usings::Bool=false, all::Bool=false)
121+
usings && (usings_explicit = usings_implicit = true)
122+
all && (non_public = generated = deprecated = true)
123+
return ccall(:jl_module_names, Array{Symbol,1},
124+
(Any, Cint, Cint, Cint, Cint, Cint, Cint),
125+
m, non_public, imported, usings_explicit, usings_implicit, generated, deprecated)
126+
end
107127

108128
"""
109129
isexported(m::Module, s::Symbol) -> Bool

base/summarysize.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,8 +178,8 @@ end
178178
function (ss::SummarySize)(obj::Module)
179179
haskey(ss.seen, obj) ? (return 0) : (ss.seen[obj] = true)
180180
size::Int = Core.sizeof(obj)
181-
for binding in names(obj, all = true)
182-
if isdefined(obj, binding) && !isdeprecated(obj, binding)
181+
for binding in names(obj, non_public=true, generated=true)
182+
if isdefined(obj, binding)
183183
value = getfield(obj, binding)
184184
if !isa(value, Module) || parentmodule(value) === obj
185185
size += ss(value)::Int

src/module.c

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,50 +1007,55 @@ void _append_symbol_to_bindings_array(jl_array_t* a, jl_sym_t *name) {
10071007
jl_array_ptr_set(a, jl_array_dim0(a)-1, (jl_value_t*)name);
10081008
}
10091009

1010-
void append_module_names(jl_array_t* a, jl_module_t *m, int all, int imported, int usings)
1010+
void append_module_names(jl_array_t* a, jl_module_t *m, int non_public, int imported, int usings_explicit, int generated, int deprecated)
10111011
{
10121012
jl_svec_t *table = jl_atomic_load_relaxed(&m->bindings);
10131013
for (size_t i = 0; i < jl_svec_len(table); i++) {
10141014
jl_binding_t *b = (jl_binding_t*)jl_svecref(table, i);
10151015
if ((void*)b == jl_nothing)
10161016
break;
10171017
jl_sym_t *asname = b->globalref->name;
1018-
int hidden = jl_symbol_name(asname)[0]=='#';
1018+
int isgenerated = jl_symbol_name(asname)[0]=='#';
10191019
int main_public = (m == jl_main_module && !(asname == jl_eval_sym || asname == jl_include_sym));
1020-
if (((b->publicp) ||
1021-
(imported && b->imported) ||
1022-
(usings && _binding_is_from_explicit_using(b)) ||
1023-
(jl_atomic_load_relaxed(&b->owner) == b && !b->imported && (all || main_public))) &&
1024-
(all || (!b->deprecated && !hidden)))
1020+
if (((jl_atomic_load_relaxed(&b->owner) == b) ? // check if this binding is owned by this module
1021+
// if it is owned by this module, check if the binding is public:
1022+
// one exception is bindings imported from other modules,
1023+
// which might also be owned by this module
1024+
(b->imported ? imported : (non_public || b->publicp || main_public)) :
1025+
// if this binding is external, then also include:
1026+
// - bindings imported from other modules, if `imported` is true
1027+
// - bindings usinged from other modules explicitly, if `usings_explicit` is true
1028+
((imported && b->imported) || (usings_explicit && _binding_is_from_explicit_using(b)))) &&
1029+
((generated || !isgenerated) && (deprecated || !b->deprecated))) {
10251030
_append_symbol_to_bindings_array(a, asname);
1031+
}
10261032
}
10271033
}
10281034

1029-
void append_exported_names(jl_array_t* a, jl_module_t *m, int all)
1035+
void append_exported_names(jl_array_t* a, jl_module_t *m, int deprecated)
10301036
{
10311037
jl_svec_t *table = jl_atomic_load_relaxed(&m->bindings);
10321038
for (size_t i = 0; i < jl_svec_len(table); i++) {
10331039
jl_binding_t *b = (jl_binding_t*)jl_svecref(table, i);
10341040
if ((void*)b == jl_nothing)
10351041
break;
1036-
if (b->exportp && (all || !b->deprecated))
1042+
if (b->exportp && (deprecated || !b->deprecated))
10371043
_append_symbol_to_bindings_array(a, b->globalref->name);
10381044
}
10391045
}
10401046

1041-
JL_DLLEXPORT jl_value_t *jl_module_names(jl_module_t *m, int all, int imported, int usings)
1047+
JL_DLLEXPORT jl_value_t *jl_module_names(jl_module_t *m, int non_public, int imported,
1048+
int usings_explicit, int usings_implicit,
1049+
int generated, int deprecated)
10421050
{
10431051
jl_array_t *a = jl_alloc_array_1d(jl_array_symbol_type, 0);
10441052
JL_GC_PUSH1(&a);
1045-
append_module_names(a, m, all, imported, usings);
1046-
if (usings) {
1053+
append_module_names(a, m, non_public, imported, usings_explicit, generated, deprecated);
1054+
if (usings_implicit)
10471055
// If `usings` is specified, traverse the list of `using`-ed modules and incorporate
10481056
// the names exported by those modules into the list.
1049-
for(int i=(int)m->usings.len-1; i >= 0; --i) {
1050-
jl_module_t *usinged = module_usings_getidx(m, i);
1051-
append_exported_names(a, usinged, all);
1052-
}
1053-
}
1057+
for (int i = 0; i < m->usings.len; i++)
1058+
append_exported_names(a, module_usings_getidx(m, i), deprecated);
10541059
JL_GC_POP();
10551060
return (jl_value_t*)a;
10561061
}

stdlib/InteractiveUtils/src/InteractiveUtils.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -261,8 +261,8 @@ function _subtypes_in!(mods::Array, x::Type)
261261
while !isempty(mods)
262262
m = pop!(mods)
263263
xt = xt::DataType
264-
for s in names(m, all = true)
265-
if isdefined(m, s) && !isdeprecated(m, s)
264+
for s in names(m, non_public=true)
265+
if isdefined(m, s)
266266
t = getfield(m, s)
267267
dt = isa(t, UnionAll) ? unwrap_unionall(t) : t
268268
if isa(dt, DataType)

stdlib/REPL/src/REPLCompletions.jl

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ end
138138
function append_filtered_mod_names!(ffunc::Function, suggestions::Vector{Completion},
139139
mod::Module, name::String, complete_internal_only::Bool)
140140
imported = usings = !complete_internal_only
141-
ssyms = names(mod; all=true, imported, usings)
141+
ssyms = names(mod; non_public=true, imported, usings)
142142
filter!(ffunc, ssyms)
143143
macros = filter(x -> startswith(String(x), "@" * name), ssyms)
144144
syms = String[sprint((io,s)->Base.show_sym(io, s; allow_macroname=true), s) for s in ssyms if completes_global(String(s), name)]
@@ -183,9 +183,7 @@ function complete_symbol!(suggestions::Vector{Completion},
183183
let modname = nameof(mod),
184184
is_main = mod===Main
185185
append_filtered_mod_names!(suggestions, mod, name, complete_internal_only) do s::Symbol
186-
if Base.isdeprecated(mod, s)
187-
return false
188-
elseif s === modname
186+
if s === modname
189187
return false # exclude `Main.Main.Main`, etc.
190188
elseif complete_modules_only && !completes_module(mod, s)
191189
return false
@@ -757,9 +755,9 @@ end
757755
MAX_ANY_METHOD_COMPLETIONS::Int = 10
758756
function recursive_explore_names!(seen::IdSet, callee_module::Module, initial_module::Module, exploredmodules::IdSet{Module}=IdSet{Module}())
759757
push!(exploredmodules, callee_module)
760-
for name in names(callee_module; all=true, imported=true)
761-
if !Base.isdeprecated(callee_module, name) && !startswith(string(name), '#') && isdefined(initial_module, name)
762-
func = getfield(callee_module, name)
758+
for name in names(callee_module; non_public=true, imported=true)
759+
if isdefined(initial_module, name) # TODO use `usings=true` instead here?
760+
func = getglobal(initial_module, name)
763761
if !isa(func, Module)
764762
funct = Core.Typeof(func)
765763
push!(seen, funct)

stdlib/REPL/src/docview.jl

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -873,11 +873,9 @@ print_correction(word, mod::Module) = print_correction(stdout, word, mod)
873873
moduleusings(mod) = ccall(:jl_module_usings, Any, (Any,), mod)
874874

875875
function accessible(mod::Module)
876-
bindings = Set(AccessibleBinding(s) for s in names(mod; all=true, imported=true)
877-
if !isdeprecated(mod, s))
876+
bindings = Set(AccessibleBinding(s) for s in names(mod; non_public=true, imported=true))
878877
for used in moduleusings(mod)
879-
union!(bindings, (AccessibleBinding(used, s) for s in names(used)
880-
if !isdeprecated(used, s)))
878+
union!(bindings, (AccessibleBinding(used, s) for s in names(used)))
881879
end
882880
union!(bindings, (AccessibleBinding(k) for k in keys(Base.Docs.keywords)))
883881
filter!(b -> !occursin('#', b.name), bindings)

stdlib/Test/src/Test.jl

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2076,8 +2076,7 @@ function detect_ambiguities(mods::Module...;
20762076
filter!(mod -> mod === parentmodule(mod), work) # some items in loaded_modules_array are not top modules (really just Base)
20772077
while !isempty(work)
20782078
mod = pop!(work)
2079-
for n in names(mod, all = true)
2080-
Base.isdeprecated(mod, n) && continue
2079+
for n in names(mod, non_public=true, generated=true)
20812080
if !isdefined(mod, n)
20822081
if is_in_mods(mod, recursive, mods)
20832082
if allowed_undefineds === nothing || GlobalRef(mod, n) allowed_undefineds
@@ -2147,8 +2146,7 @@ function detect_unbound_args(mods...;
21472146
filter!(mod -> mod === parentmodule(mod), work) # some items in loaded_modules_array are not top modules (really just Base)
21482147
while !isempty(work)
21492148
mod = pop!(work)
2150-
for n in names(mod, all = true)
2151-
Base.isdeprecated(mod, n) && continue
2149+
for n in names(mod, non_public=true, generated=true)
21522150
if !isdefined(mod, n)
21532151
if is_in_mods(mod, recursive, mods)
21542152
if allowed_undefineds === nothing || GlobalRef(mod, n) allowed_undefineds

test/reflection.jl

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ let
177177
@test Base.binding_module(TestMod7648, :d7648) == TestMod7648
178178
@test Base.binding_module(TestMod7648, :a9475) == TestMod7648.TestModSub9475
179179
@test Base.binding_module(TestMod7648.TestModSub9475, :b9475) == TestMod7648.TestModSub9475
180-
defaultset = Set(Symbol[:Foo7648, :TestMod7648, :a9475, :c7648, :f9475, :foo7648, :foo7648_nomethods])
180+
defaultset = Set(Symbol[:Foo7648, :TestMod7648, :c7648, :foo7648, :foo7648_nomethods])
181181
allset = defaultset Set(Symbol[
182182
Symbol("#eval"), Symbol("#foo7648"), Symbol("#foo7648_nomethods"), Symbol("#include"),
183183
:TestModSub9475, :d7648, :eval, :f7648, :include])
@@ -189,7 +189,7 @@ let
189189
:GenericSet, :GenericString, :LogRecord, :Test, :TestLogger, :TestSetException,
190190
:detect_ambiguities, :detect_unbound_args])
191191
usings_from_Base = delete!(Set(names(Module(); usings=true)), :anonymous) # the name of the anonymous module itself
192-
usings = Set(Symbol[:x36529, :TestModSub9475, :f54609]) usings_from_Test usings_from_Base
192+
usings = Set(Symbol[:x36529, :TestModSub9475, :a9475, :f9475, :f54609]) usings_from_Test usings_from_Base
193193
@test Set(names(TestMod7648)) == defaultset
194194
@test Set(names(TestMod7648, all=true)) == allset
195195
@test Set(names(TestMod7648, all=true, imported=true)) == allset imported
@@ -209,8 +209,16 @@ global unexported::Int = 0
209209
end
210210
using Base: @assume_effects
211211
using .Inner
212-
end
213-
let usings = names(Test54609Simple; usings=true)
212+
end # baremodule Test54609Simple
213+
let usings_explicit = names(Test54609Simple; usings_explicit=true)
214+
@test Symbol("@assume_effects") usings_explicit
215+
@test :exported usings_explicit
216+
@test :unexported usings_explicit
217+
usings_implicit = names(Test54609Simple; usings_implicit=true)
218+
@test Symbol("@assume_effects") usings_implicit
219+
@test :exported usings_implicit
220+
@test :unexported usings_implicit
221+
usings = names(Test54609Simple; usings=true)
214222
@test Symbol("@assume_effects") usings
215223
@test :Base usings
216224
@test :exported usings
@@ -230,7 +238,7 @@ let usings = names(Test54609Complex; usings=true)
230238
@test :exported_new usings
231239
@test :exported_old usings
232240
@test :_Test54609Complex usings # should include the `using`ed module itself
233-
usings_all = names(Test54609Complex; usings=true, all=true)
241+
usings_all = names(Test54609Complex; usings=true, deprecated=true)
234242
@test :exported_new usings_all
235243
@test :exported_old usings_all # deprecated names should be included with `all=true`
236244
end

0 commit comments

Comments
 (0)