Skip to content

Commit

Permalink
REPLCompletions: allow field completions for Union results (JuliaLa…
Browse files Browse the repository at this point in the history
…ng#50503)

This generalizes the idea of JuliaLang#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`.
  • Loading branch information
aviatesk authored Jul 12, 2023
1 parent e64d201 commit 4995d3f
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 25 deletions.
44 changes: 23 additions & 21 deletions stdlib/REPL/src/REPLCompletions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ function complete_symbol(@nospecialize(ex), name::String, @nospecialize(ffunc),
# as excluding Main.Main.Main, etc., because that's most likely not what
# the user wants
p = let mod=mod, modname=nameof(mod)
s->(!Base.isdeprecated(mod, s) && s != modname && ffunc(mod, s)::Bool)
(s::Symbol) -> !Base.isdeprecated(mod, s) && s != modname && ffunc(mod, s)::Bool
end
# Looking for a binding in a module
if mod == context_module
Expand All @@ -192,25 +192,32 @@ function complete_symbol(@nospecialize(ex), name::String, @nospecialize(ffunc),
end
else
# Looking for a member of a type
if t isa DataType && t != Any
# Check for cases like Type{typeof(+)}
if Base.isType(t)
t = typeof(t.parameters[1])
end
# Only look for fields if this is a concrete type
if isconcretetype(t)
fields = fieldnames(t)
for field in fields
isa(field, Symbol) || continue # Tuple type has ::Int field name
s = string(field)
if startswith(s, name)
push!(suggestions, FieldCompletion(t, field))
end
add_field_completions!(suggestions, name, t)
end
return suggestions
end

function add_field_completions!(suggestions::Vector{Completion}, name::String, @nospecialize(t))
if isa(t, Union)
add_field_completions!(suggestions, name, t.a)
add_field_completions!(suggestions, name, t.b)
elseif t isa DataType && t != Any
# Check for cases like Type{typeof(+)}
if Base.isType(t)
t = typeof(t.parameters[1])
end
# Only look for fields if this is a concrete type
if isconcretetype(t)
fields = fieldnames(t)
for field in fields
isa(field, Symbol) || continue # Tuple type has ::Int field name
s = string(field)
if startswith(s, name)
push!(suggestions, FieldCompletion(t, field))
end
end
end
end
suggestions
end

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

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

Expand Down
12 changes: 8 additions & 4 deletions stdlib/REPL/test/replcompletions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1875,11 +1875,15 @@ let s = "`abc`.e"
end

# Test completion for a case when type inference returned `Union` of the same types
function union_somes()
return rand() < 0.5 ? Some(1) : Some(2.)
end
let s = "union_somes()."
union_somes(a, b) = rand() < 0.5 ? Some(a) : Some(b)
let s = "union_somes(1, 1.0)."
c, r, res = test_complete_context(s, @__MODULE__)
@test res
@test "value" in c
end
union_some_ref(a, b) = rand() < 0.5 ? Some(a) : Ref(b)
let s = "union_some_ref(1, 1.0)."
c, r, res = test_complete_context(s, @__MODULE__)
@test res
@test "value" in c && "x" in c
end

0 comments on commit 4995d3f

Please sign in to comment.