Skip to content

Commit

Permalink
Fix ?(#TAB method search name exploration (#52555)
Browse files Browse the repository at this point in the history
Fix #52551.

This PR ensures that a `SomeModule.?(...#TAB` completion can only
suggests method `foo` such that `SomeModule.foo` exists (by checking
`isdefined(SomeModule, :foo)`). This is equivalent, I believe, to the
initial implementation of #38791,
less the bug.

Now that we have #51345, we may want to relax the above condition
somewhat to include public names present in modules loaded into
`SomeModule`, so that, for instance, a direct completion of `?(` would
include `@assume_effects`. This could be good for method exploration
because even though typing `@assume_effects` with no qualification in
`Main` will error, the error now includes the helpful message
```
Hint: a global variable of this name also exists in Base.
```
But that can wait for a later PR anyway, this one is just the bugfix.

The bug mentioned at
#52551 (comment)
dates from the initial #38791 so this could be backported as far back as
v1.8.

---------

Co-authored-by: Shuhei Kadowaki <[email protected]>
(cherry picked from commit a987f56)
  • Loading branch information
Liozou authored and KristofferC committed Dec 23, 2023
1 parent 5bf09bb commit e38f117
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 27 deletions.
50 changes: 23 additions & 27 deletions stdlib/REPL/src/REPLCompletions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export completions, shell_completions, bslash_completions, completion_text
using Core: CodeInfo, MethodInstance, CodeInstance, Const
const CC = Core.Compiler
using Base.Meta
using Base: propertynames, something
using Base: propertynames, something, IdSet

abstract type Completion end

Expand Down Expand Up @@ -659,6 +659,26 @@ function complete_methods(ex_org::Expr, context_module::Module=Main, shift::Bool
end

MAX_ANY_METHOD_COMPLETIONS::Int = 10
function recursive_explore_names!(seen::IdSet, callee_module::Module, initial_module::Module, exploredmodules::IdSet{Module}=IdSet{Module}())
push!(exploredmodules, callee_module)
for name in names(callee_module; all=true, imported=true)
if !Base.isdeprecated(callee_module, name) && !startswith(string(name), '#') && isdefined(initial_module, name)
func = getfield(callee_module, name)
if !isa(func, Module)
funct = Core.Typeof(func)
push!(seen, funct)
elseif isa(func, Module) && func exploredmodules
recursive_explore_names!(seen, func, initial_module, exploredmodules)
end
end
end
end
function recursive_explore_names(callee_module::Module, initial_module::Module)
seen = IdSet{Any}()
recursive_explore_names!(seen, callee_module, initial_module)
seen
end

function complete_any_methods(ex_org::Expr, callee_module::Module, context_module::Module, moreargs::Bool, shift::Bool)
out = Completion[]
args_ex, kwargs_ex, kwargs_flag = try
Expand All @@ -674,32 +694,8 @@ function complete_any_methods(ex_org::Expr, callee_module::Module, context_modul
# semicolon for the ".?(" syntax
moreargs && push!(args_ex, Vararg{Any})

seen = Base.IdSet()
for name in names(callee_module; all=true)
if !Base.isdeprecated(callee_module, name) && isdefined(callee_module, name) && !startswith(string(name), '#')
func = getfield(callee_module, name)
if !isa(func, Module)
funct = Core.Typeof(func)
if !in(funct, seen)
push!(seen, funct)
complete_methods!(out, funct, args_ex, kwargs_ex, MAX_ANY_METHOD_COMPLETIONS, false)
end
elseif callee_module === Main && isa(func, Module)
callee_module2 = func
for name in names(callee_module2)
if !Base.isdeprecated(callee_module2, name) && isdefined(callee_module2, name) && !startswith(string(name), '#')
func = getfield(callee_module, name)
if !isa(func, Module)
funct = Core.Typeof(func)
if !in(funct, seen)
push!(seen, funct)
complete_methods!(out, funct, args_ex, kwargs_ex, MAX_ANY_METHOD_COMPLETIONS, false)
end
end
end
end
end
end
for seen_name in recursive_explore_names(callee_module, callee_module)
complete_methods!(out, seen_name, args_ex, kwargs_ex, MAX_ANY_METHOD_COMPLETIONS, false)
end

if !shift
Expand Down
7 changes: 7 additions & 0 deletions stdlib/REPL/test/replcompletions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,10 @@ let ex = quote
struct WeirdNames end
Base.propertynames(::WeirdNames) = (Symbol("oh no!"), Symbol("oh yes!"))

# https://github.com/JuliaLang/julia/issues/52551#issuecomment-1858543413
export exported_symbol
exported_symbol(::WeirdNames) = nothing

end # module CompletionFoo
test_repl_comp_dict = CompletionFoo.test_dict
test_repl_comp_customdict = CompletionFoo.test_customdict
Expand Down Expand Up @@ -740,6 +744,9 @@ end

#TODO: @test_nocompletion("CompletionFoo.?(3; len2=5; ")

# https://github.com/JuliaLang/julia/issues/52551
@test !isempty(test_complete("?("))

#################################################################

# Test method completion with varargs
Expand Down

0 comments on commit e38f117

Please sign in to comment.