Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

reflection: allow names to return using-ed names #54609

Merged
merged 1 commit into from
May 31, 2024
Merged

reflection: allow names to return using-ed names #54609

merged 1 commit into from
May 31, 2024

Conversation

aviatesk
Copy link
Member

@aviatesk aviatesk commented May 29, 2024

This commit makes it possible for names to return using-ed names as well:

julia> using Base: @assume_effects

julia> Symbol("@assume_effects") in names(@__MODULE__; usings=true)
true

Currently, to find all names available in a module A, the following steps are needed:

  1. Use names(A; all=true, imported=true) to get the names defined by A and the names explicitly imported by A.
  2. Use jl_module_usings(A) to get the list of modules A has using-ed and then use names() to get the names exported by those modules.

This method is implemented in e.g. REPL completions, but it has a problem: it could not get the names explicitly using-ed by using B: ... (#36529, #40356, JuliaDebug/Infiltrator.jl#106, etc.).

This commit adds a new keyword argument usings::Bool=false to names(A; ...), which, when usings=true is specified, returns all names introduced by using in A.
In other words, usings=true not only returns explicitly using-ed names but also incorporates step 2 above into the implementation of names.

By using this new option, we can now use
names(A; all=true, imported=true, usings=true) to know all names available in A, without implementing the two-fold steps on application side.
As example application, this new feature will be used to simplify and enhance the implementation of REPL completions.

src/module.c Outdated Show resolved Hide resolved
src/module.c Outdated
// Add the name of `usinged` itself, unless the user requested `all=true` and it's
// a submodule of `m`, since then its name would have already been added by
// `all=true`, since it's a binding in `m`.
if (!all || usinged->parent != m) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this is enough to avoid duplicates. For example the name might also be exported. We could also de-duplicate in the caller though.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah... I've been thinking about how to eliminate the duplication, and it seems it might be better to start by organizing the interface of names. With the current design and implementation, all/imported and usings are not necessarily orthogonal, so the duplication is inherent in the definitions.
By defining names as names(x::Module; non_public::Bool=false, generated::Bool=false, imported::Bool=false, usings::Bool=false) and refactoring the code, I think we can make the handling of such duplications more clear.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The refactoring mentioned above involves more changes, so..., for now in this PR, I want to either 1) have names return a Set to eliminate duplicates on the names side (technically this is a breaking change?), or 2) add a warning about the possibility of duplicates in the names docstring. Which do you think is better?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've decided to go with the second option. Regarding the initial comment, I've found that this code itself is not necessary, so I will just remove it. The duplication issue remains (such as when there is a name conflicting with using-ed one), but at least there is a warning, so it shouldn't be a big problem.

@aviatesk aviatesk force-pushed the avi/36529 branch 2 times, most recently from f01d4e3 to ca41f1a Compare May 30, 2024 16:42
@aviatesk
Copy link
Member Author

Further refactoring might be needed for names, but this PR should be fine in the sense of adding new functionality. I plan to merge it once the tests pass without issues.

@aviatesk aviatesk force-pushed the avi/36529 branch 2 times, most recently from edb44e5 to 9f2e685 Compare May 30, 2024 19:17
This commit makes it possible for `names` to return `using`-ed names
as well:
```julia
julia> using Base: @assume_effects

julia> Symbol("@assume_effects") in names(@__MODULE__; usings=true)
true
```

Currently, to find all names available in a module `A`, the following
steps are needed:
1. Use `names(A; all=true, imported=true)` to get the names defined by
   `A` and the names explicitly `import`ed by `A`.
2. Use `jl_module_usings(A)` to get the list of modules `A` has
   `using`-ed and then use `names()` to get the names `export`ed by
   those modules.

This method is implemented in e.g. REPL completions, but it has a
problem: it could not get the names explicitly `using`-ed by
`using B: ...` (#36529, #40356, JuliaDebug/Infiltrator.jl#106, etc.).

This commit adds a new keyword argument `usings::Bool=false` to
`names(A; ...)`, which, when `usings=true` is specified, returns all
names introduced by `using` in `A`.
In other words, `usings=true` not only returns explicitly `using`-ed
names but also incorporates step 2 above into the implementation of
`names`.

By using this new option, we can now use
`names(A; all=true, imported=true, usings=true)` to know all names
available in `A`, without implementing the two-fold steps on application
side.
As example application, this new feature will be used to simplify and
enhance the implementation of REPL completions.

- fixes #36529

Co-authored-by: Nathan Daly <NHDaly@gmail.com>
Co-authored-by: Sebastian Pfitzner <pfitzseb@gmail.com>
@aviatesk aviatesk merged commit 9f5c2cf into master May 31, 2024
2 of 3 checks passed
@aviatesk aviatesk deleted the avi/36529 branch May 31, 2024 05:42

Get a vector of the public names of a `Module`, excluding deprecated names.
If `all` is true, then the list also includes non-public names defined in the module,
deprecated names, and compiler-generated names.
If `imported` is true, then names explicitly imported from other modules
are also included. Names are returned in sorted order.
are also included.
If `usings` is true, then names explicitly imported via `using` are also included.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also implicitly?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing that out. I plan to address this issue as well in #54659.

@lassepe
Copy link
Contributor

lassepe commented Jun 11, 2024

It seems a bit confusing that names(A; all=true) does not return all names. But I guess that cannot be fixed in a non-breaking way?

@aviatesk
Copy link
Member Author

Yeah it would be a breaking change.

lazarusA pushed a commit to lazarusA/julia that referenced this pull request Jul 12, 2024
This commit makes it possible for `names` to return `using`-ed names as
well:
```julia
julia> using Base: @assume_effects

julia> Symbol("@assume_effects") in names(@__MODULE__; usings=true)
true
```

Currently, to find all names available in a module `A`, the following
steps are needed:
1. Use `names(A; all=true, imported=true)` to get the names defined by
`A` and the names explicitly `import`ed by `A`.
2. Use `jl_module_usings(A)` to get the list of modules `A` has
`using`-ed and then use `names()` to get the names `export`ed by those
modules.

This method is implemented in e.g. REPL completions, but it has a
problem: it could not get the names explicitly `using`-ed by `using B:
...` (JuliaLang#36529, JuliaLang#40356, JuliaDebug/Infiltrator.jl#106, etc.).

This commit adds a new keyword argument `usings::Bool=false` to
`names(A; ...)`, which, when `usings=true` is specified, returns all
names introduced by `using` in `A`.
In other words, `usings=true` not only returns explicitly `using`-ed
names but also incorporates step 2 above into the implementation of
`names`.

By using this new option, we can now use
`names(A; all=true, imported=true, usings=true)` to know all names
available in `A`, without implementing the two-fold steps on application
side.
As example application, this new feature will be used to simplify and
enhance the implementation of REPL completions.

- fixes JuliaLang#36529

Co-authored-by: Nathan Daly <NHDaly@gmail.com>
Co-authored-by: Sebastian Pfitzner <pfitzseb@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Visibility of usinged names in a module
3 participants