Skip to content

Commit e38f117

Browse files
LiozouKristofferC
authored andcommitted
Fix ?(#TAB method search name exploration (#52555)
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)
1 parent 5bf09bb commit e38f117

File tree

2 files changed

+30
-27
lines changed

2 files changed

+30
-27
lines changed

stdlib/REPL/src/REPLCompletions.jl

+23-27
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export completions, shell_completions, bslash_completions, completion_text
77
using Core: CodeInfo, MethodInstance, CodeInstance, Const
88
const CC = Core.Compiler
99
using Base.Meta
10-
using Base: propertynames, something
10+
using Base: propertynames, something, IdSet
1111

1212
abstract type Completion end
1313

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

661661
MAX_ANY_METHOD_COMPLETIONS::Int = 10
662+
function recursive_explore_names!(seen::IdSet, callee_module::Module, initial_module::Module, exploredmodules::IdSet{Module}=IdSet{Module}())
663+
push!(exploredmodules, callee_module)
664+
for name in names(callee_module; all=true, imported=true)
665+
if !Base.isdeprecated(callee_module, name) && !startswith(string(name), '#') && isdefined(initial_module, name)
666+
func = getfield(callee_module, name)
667+
if !isa(func, Module)
668+
funct = Core.Typeof(func)
669+
push!(seen, funct)
670+
elseif isa(func, Module) && func exploredmodules
671+
recursive_explore_names!(seen, func, initial_module, exploredmodules)
672+
end
673+
end
674+
end
675+
end
676+
function recursive_explore_names(callee_module::Module, initial_module::Module)
677+
seen = IdSet{Any}()
678+
recursive_explore_names!(seen, callee_module, initial_module)
679+
seen
680+
end
681+
662682
function complete_any_methods(ex_org::Expr, callee_module::Module, context_module::Module, moreargs::Bool, shift::Bool)
663683
out = Completion[]
664684
args_ex, kwargs_ex, kwargs_flag = try
@@ -674,32 +694,8 @@ function complete_any_methods(ex_org::Expr, callee_module::Module, context_modul
674694
# semicolon for the ".?(" syntax
675695
moreargs && push!(args_ex, Vararg{Any})
676696

677-
seen = Base.IdSet()
678-
for name in names(callee_module; all=true)
679-
if !Base.isdeprecated(callee_module, name) && isdefined(callee_module, name) && !startswith(string(name), '#')
680-
func = getfield(callee_module, name)
681-
if !isa(func, Module)
682-
funct = Core.Typeof(func)
683-
if !in(funct, seen)
684-
push!(seen, funct)
685-
complete_methods!(out, funct, args_ex, kwargs_ex, MAX_ANY_METHOD_COMPLETIONS, false)
686-
end
687-
elseif callee_module === Main && isa(func, Module)
688-
callee_module2 = func
689-
for name in names(callee_module2)
690-
if !Base.isdeprecated(callee_module2, name) && isdefined(callee_module2, name) && !startswith(string(name), '#')
691-
func = getfield(callee_module, name)
692-
if !isa(func, Module)
693-
funct = Core.Typeof(func)
694-
if !in(funct, seen)
695-
push!(seen, funct)
696-
complete_methods!(out, funct, args_ex, kwargs_ex, MAX_ANY_METHOD_COMPLETIONS, false)
697-
end
698-
end
699-
end
700-
end
701-
end
702-
end
697+
for seen_name in recursive_explore_names(callee_module, callee_module)
698+
complete_methods!(out, seen_name, args_ex, kwargs_ex, MAX_ANY_METHOD_COMPLETIONS, false)
703699
end
704700

705701
if !shift

stdlib/REPL/test/replcompletions.jl

+7
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,10 @@ let ex = quote
140140
struct WeirdNames end
141141
Base.propertynames(::WeirdNames) = (Symbol("oh no!"), Symbol("oh yes!"))
142142

143+
# https://github.com/JuliaLang/julia/issues/52551#issuecomment-1858543413
144+
export exported_symbol
145+
exported_symbol(::WeirdNames) = nothing
146+
143147
end # module CompletionFoo
144148
test_repl_comp_dict = CompletionFoo.test_dict
145149
test_repl_comp_customdict = CompletionFoo.test_customdict
@@ -740,6 +744,9 @@ end
740744

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

747+
# https://github.com/JuliaLang/julia/issues/52551
748+
@test !isempty(test_complete("?("))
749+
743750
#################################################################
744751

745752
# Test method completion with varargs

0 commit comments

Comments
 (0)