Skip to content

Commit 4995d3f

Browse files
authored
REPLCompletions: allow field completions for Union results (#50503)
This generalizes the idea of #50483 and enables field completions when the result type is inferred to be `Union{A,B}` where `A` is not necessarily the same type as `B`.
1 parent e64d201 commit 4995d3f

File tree

2 files changed

+31
-25
lines changed

2 files changed

+31
-25
lines changed

stdlib/REPL/src/REPLCompletions.jl

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ function complete_symbol(@nospecialize(ex), name::String, @nospecialize(ffunc),
170170
# as excluding Main.Main.Main, etc., because that's most likely not what
171171
# the user wants
172172
p = let mod=mod, modname=nameof(mod)
173-
s->(!Base.isdeprecated(mod, s) && s != modname && ffunc(mod, s)::Bool)
173+
(s::Symbol) -> !Base.isdeprecated(mod, s) && s != modname && ffunc(mod, s)::Bool
174174
end
175175
# Looking for a binding in a module
176176
if mod == context_module
@@ -192,25 +192,32 @@ function complete_symbol(@nospecialize(ex), name::String, @nospecialize(ffunc),
192192
end
193193
else
194194
# Looking for a member of a type
195-
if t isa DataType && t != Any
196-
# Check for cases like Type{typeof(+)}
197-
if Base.isType(t)
198-
t = typeof(t.parameters[1])
199-
end
200-
# Only look for fields if this is a concrete type
201-
if isconcretetype(t)
202-
fields = fieldnames(t)
203-
for field in fields
204-
isa(field, Symbol) || continue # Tuple type has ::Int field name
205-
s = string(field)
206-
if startswith(s, name)
207-
push!(suggestions, FieldCompletion(t, field))
208-
end
195+
add_field_completions!(suggestions, name, t)
196+
end
197+
return suggestions
198+
end
199+
200+
function add_field_completions!(suggestions::Vector{Completion}, name::String, @nospecialize(t))
201+
if isa(t, Union)
202+
add_field_completions!(suggestions, name, t.a)
203+
add_field_completions!(suggestions, name, t.b)
204+
elseif t isa DataType && t != Any
205+
# Check for cases like Type{typeof(+)}
206+
if Base.isType(t)
207+
t = typeof(t.parameters[1])
208+
end
209+
# Only look for fields if this is a concrete type
210+
if isconcretetype(t)
211+
fields = fieldnames(t)
212+
for field in fields
213+
isa(field, Symbol) || continue # Tuple type has ::Int field name
214+
s = string(field)
215+
if startswith(s, name)
216+
push!(suggestions, FieldCompletion(t, field))
209217
end
210218
end
211219
end
212220
end
213-
suggestions
214221
end
215222

216223
const sorted_keywords = [
@@ -580,11 +587,6 @@ function repl_eval_ex(@nospecialize(ex), context_module::Module)
580587

581588
result = frame.result.result
582589
result === Union{} && return nothing # for whatever reason, callers expect this as the Bottom and/or Top type instead
583-
if isa(result, Union)
584-
# unswitch `Union` of same `UnionAll` instances to `UnionAll` of `Union`s
585-
# so that we can use the field information of the `UnionAll`
586-
return CC.unswitchtypeunion(result)
587-
end
588590
return result
589591
end
590592

stdlib/REPL/test/replcompletions.jl

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1875,11 +1875,15 @@ let s = "`abc`.e"
18751875
end
18761876

18771877
# Test completion for a case when type inference returned `Union` of the same types
1878-
function union_somes()
1879-
return rand() < 0.5 ? Some(1) : Some(2.)
1880-
end
1881-
let s = "union_somes()."
1878+
union_somes(a, b) = rand() < 0.5 ? Some(a) : Some(b)
1879+
let s = "union_somes(1, 1.0)."
18821880
c, r, res = test_complete_context(s, @__MODULE__)
18831881
@test res
18841882
@test "value" in c
18851883
end
1884+
union_some_ref(a, b) = rand() < 0.5 ? Some(a) : Ref(b)
1885+
let s = "union_some_ref(1, 1.0)."
1886+
c, r, res = test_complete_context(s, @__MODULE__)
1887+
@test res
1888+
@test "value" in c && "x" in c
1889+
end

0 commit comments

Comments
 (0)