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

wip: REPLCompletions: allow completions for using-ed names #42092

Closed
wants to merge 4 commits into from

Conversation

NHDaly
Copy link
Member

@NHDaly NHDaly commented Sep 2, 2021

Add parameter to names(m::Module) to return the bindings that come from using statements inside that module.

This was actually a bit trickier than I expected it to be, because there are a few oddities to consider:

  • Bindings that are exported from Foo, which come from using Foo, are added to m's bindings table lazily, whenever they're first referenced.
    • This means that it's not enough to allow returning names that have a different parent module, but weren't ->imported, because it means the names returned from names(m::Module, usings=true) would change over time as functions are called that perform the binding lookup!
    • So instead, I added logic to walk over all the modules in the m->usings list, and fetch all of their exported names.
  • We have to take special care not to return names exported from Base and Core, to keep consistent with how names() currently works.
    • Though we could consider allowing these if all=true? i.e. should names(m, all=true, usings=true) return names that come from Base and Core? We did write all=true, and we're not returning all the names... but also, it's very noisy / probably useless to return all of them.
  • We don't want to print submodules that are also usings twice

Here's a nice example that I think showcases all the edgecases:

julia> module M1
           const m1_x = 1
           export m1_x
       end
Main.M1

julia> module A
           module B
               f(x) = 1
               secret = 1
               module Inner2 end
           end
           module C
               x = 1
               y = 2
               export y
           end
           using .B: f
           using .C
           using ..M1
       end
Main.A

julia> names(A)
1-element Vector{Symbol}:
 :A

julia> names(A, imported=true)
1-element Vector{Symbol}:
 :A

julia> names(A, usings=true)
6-element Vector{Symbol}:
 :A
 :C
 :M1
 :f
 :m1_x
 :y

julia> names(A, all=true)
7-element Vector{Symbol}:
 Symbol("#eval")
 Symbol("#include")
 :A
 :B
 :C
 :eval
 :include

julia> names(A, all=true, usings=true)
11-element Vector{Symbol}:
 Symbol("#eval")
 Symbol("#include")
 :A
 :B
 :C
 :M1
 :eval
 :f
 :include
 :m1_x
 :y

Fixes #36529.


@NHDaly
Copy link
Member Author

NHDaly commented Sep 2, 2021

Open questions for reviewers:

  1. I think the other alternative would have been to change the behavior of imported=true to also return these, but i think that's bad because it's a breaking change? I'm not super sure if there's anyone who would want just the usings or just the imports though...? Maybe?
  2. Any preference on the name of the parameter? Maybe usinged would be more consistent with imported, but that's also not a real word. Can anyone suggest a better alternative?

Thanks! 😊

@DilumAluthge
Copy link
Member

2. Any preference on the name of the parameter? Maybe usinged would be more consistent with imported, but that's also not a real word. Can anyone suggest a better alternative?

  • from_using
  • via_using

NHDaly added a commit to NHDaly/LookingGlass.jl that referenced this pull request Sep 2, 2021
…dules

```julia
julia> module TestMod42092
       module M1
           const m1_x = 1
           export m1_x
       end
       module M2
           const m2_x = 1
           export m2_x
       end
       module A
           module B
               f(x) = 1
               secret = 1
               module Inner2 end
           end
           module C
               x = 1
               y = 2
               export y
           end
           using .B: f
           using .C
           using ..M1
           import ..M2
       end
       end # module TestMod42092
Main.TestMod42092

julia> LookingGlass.module_recursive_globals(TestMod42092.A, imported=true, usings=true)
Dict{Tuple{Module, Symbol}, Int64} with 7 entries:
  (Main.TestMod42092.M2, :m2_x)    => 1
  (Main.TestMod42092.A, :y)        => 2
  (Main.TestMod42092.A.C, :y)      => 2
  (Main.TestMod42092.A.B, :secret) => 1
  (Main.TestMod42092.A.C, :x)      => 1
  (Main.TestMod42092.A, :m1_x)     => 1
  (Main.TestMod42092.M1, :m1_x)    => 1
```

NOTE: the `usings=true` option is in PR against Julia 1.8. See:
JuliaLang/julia#42092
@aviatesk
Copy link
Member

aviatesk commented Sep 3, 2021

  • Bindings that are exported from Foo, which come from using Foo, are added to m's bindings table lazily, whenever they're first referenced.

yeah, I think the manual binding lookup is valid here in terms of the functionality of names.
No matter how lazily the actual binding is resolved, there are the same name and names can just return it ?

  • We have to take special care not to return names exported from Base and Core, to keep consistent with how names() currently works.

I think we can add bindings that are explicitly usinged at least ? I found this special casing leads to a completion failure for Base bindings, e.g.

julia> using Base: @aggressive_constantprop

julia> @aggressive_ # tab completion doesn't work w/ this PR

Here is the corresponding test case I added in #42094:

let # should for `Base` binding
M = Module()
@assert count(==("@aggressive_constprop"), test_complete_context1("@aggressive_", M)) 1
@eval M using Base: @aggressive_constprop
@test count(==("@aggressive_constprop"), test_complete_context1("@aggressive_", M)) == 1
end

I'm not sure what is the most consistent behavior, but at least I want to remove this special casing for every case ?

julia/src/module.c

Lines 815 to 817 in e0ac286

// Modules implicitly get all exported names from Base and Core as if they had
// written `using Base`, but we don't show those via `names()`.
b->owner != jl_base_module && b->owner != jl_core_module);

2. Any preference on the name of the parameter? Maybe usinged would be more consistent with imported, but that's also not a real word. Can anyone suggest a better alternative?

I don't have much opinion on naming in general, but usings sounds okay to me :p


/cc @pfitzseb The LS might also be interested in this ?

@pfitzseb
Copy link
Member

pfitzseb commented Sep 3, 2021

The LS might also be interested in this ?

Yeah, for sure. We have a bunch of code that hacks around this issue in the SymbolServer.

@pfitzseb
Copy link
Member

Any chance we can rebase and merge this? Just ran into another issue which needs this PR or something like it to fix.

@lgoettgens
Copy link
Contributor

What is the state of this? It would be great to get this into 1.12.

@NHDaly
Copy link
Member Author

NHDaly commented Apr 5, 2024

I haven't looked at this in a couple of years. :( I'm sorry I let it linger.

@pfitzseb: would you be able to clean this up and bring it to merge? I think the naming question seems to have an answer above, so someone just needs to apply that and then rebase and merge?

@aviatesk i don't remember everything we were talking about before, but do you have any remaining unaddressed concerns?

@DilumAluthge
Copy link
Member

DilumAluthge commented May 25, 2024

@NHDaly What's the current status here? Are we waiting on action from @pfitzseb and @aviatesk?

@pfitzseb
Copy link
Member

pfitzseb commented May 27, 2024

This should fix the tests (and behaviour). Basically, unresolved bindings should never show up in the output, which the additional check ensures.

I don't have write permissions for this repo, so I'll just post the diff here.

cc @aviatesk for a sanity check :)

diff --git a/src/module.c b/src/module.c
index e8ea5c7375..1292ed7cf9 100644
--- a/src/module.c
+++ b/src/module.c
@@ -992,7 +992,7 @@ JL_DLLEXPORT jl_value_t *jl_module_usings(jl_module_t *m)
 }
 
 uint8_t _binding_is_from_explicit_using(jl_binding_t *b) {
-    return (b->owner != b && !b->imported &&
+    return (jl_atomic_load_relaxed(&b->owner) != NULL && b->owner != b && !b->imported &&
             // Modules implicitly get all exported names from Base and Core as if they had
             // written `using Base`, but we don't show those via `names()`.
             // b->owner->value != (jl_value_t*)jl_base_module && b->owner->value != (jl_value_t*)jl_core_module);
diff --git a/test/reflection.jl b/test/reflection.jl
index 0ecf817ddd..e9e35b1ff4 100644
--- a/test/reflection.jl
+++ b/test/reflection.jl
@@ -188,14 +188,16 @@ let
                                         Symbol("@test_deprecated"), Symbol("@test_logs"), Symbol("@test_nowarn"), Symbol("@test_skip"),
                                         Symbol("@test_throws"), Symbol("@test_warn"), Symbol("@testset"), :GenericArray, :GenericDict,
                                         :GenericOrder, :GenericSet, :GenericString, :Test, :TestSetException, :detect_ambiguities, :detect_unbound_args,
-                                        :TestMod7648, :TestModSub9475, :TestMod7648, :a9475, :foo9475, :c7648, :foo7648, :foo7648_nomethods, :Foo7648])
+                                        :TestMod7648, :TestModSub9475, :TestMod7648, :a9475, :foo9475, :c7648, :foo7648, :foo7648_nomethods,
+                                        :Foo7648, Symbol("@__MODULE__"), :Base, :LogRecord, :(==), :(===), :TestLogger])
     @test Set(names(TestMod7648, all = true, usings = true)) == Set([:x36529, :Test,  Symbol("@inferred"), Symbol("@test"), Symbol("@test_broken"),
                                         Symbol("@test_deprecated"), Symbol("@test_logs"), Symbol("@test_nowarn"), Symbol("@test_skip"),
                                         Symbol("@test_throws"), Symbol("@test_warn"), Symbol("@testset"), :GenericArray, :GenericDict,
                                         :GenericOrder, :GenericSet, :GenericString, :Test, :TestSetException, :detect_ambiguities, :detect_unbound_args,
                                         :TestMod7648, :TestModSub9475, :a9475, :foo9475, :c7648, :d7648, :f7648,
                                         :foo7648, Symbol("#foo7648"), :foo7648_nomethods, Symbol("#foo7648_nomethods"),
-                                        :Foo7648, :eval, Symbol("#eval"), :include, Symbol("#include")])
+                                        :Foo7648, :eval, Symbol("#eval"), :include, Symbol("#include"), Symbol("@__MODULE__"), :Base,
+                                        :LogRecord, :(==), :(===), :TestLogger])
     @test isconst(TestMod7648, :c7648)
     @test !isconst(TestMod7648, :d7648)
 end

@aviatesk aviatesk force-pushed the nhd/names-usings branch 2 times, most recently from 073b98c to d5b0b9c Compare May 27, 2024 14:29
@aviatesk
Copy link
Member

This PR should probably be merged along with the enhancement for REPL completions, so I'll merge #42094 into this one for now.

aviatesk added a commit that referenced this pull request May 27, 2024
Add `usings` to names in REPL completions.

Follow up to #42092, rest of
#36529.

This enables the REPL to use the names imported via `usings` in
completions.

---------

Co-authored-by: Shuhei Kadowaki <[email protected]>
@aviatesk aviatesk changed the title Add parameter to names(m::Module) to return the bindings that come from using statements inside that module. REPLCompletions: allow completions for using-ed names May 27, 2024
@aviatesk aviatesk changed the title REPLCompletions: allow completions for using-ed names wip: REPLCompletions: allow completions for using-ed names May 27, 2024
@aviatesk aviatesk force-pushed the nhd/names-usings branch 2 times, most recently from d90359f to 15ea438 Compare May 28, 2024 17:43
NHDaly and others added 3 commits May 29, 2024 14:21
This commit adds a new keyword argument to `names(mod)`, allowing it to
return names that are `using`-ed. Additionally, as an example
application of this feature, it aims to improve completion capabilities
by using this functionality in REPLCompletions.

Co-authored-by: Shuhei Kadowaki <[email protected]>
Co-Authored-By: Sebastian Pfitzner <[email protected]>
@aviatesk
Copy link
Member

I'd like to replace this with #54609.

@aviatesk aviatesk closed this May 29, 2024
@aviatesk aviatesk deleted the nhd/names-usings branch May 29, 2024 08:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
5 participants