From 4995d3f5c0b234561d3b5b516adb5cb6ab1e5bd7 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Wed, 12 Jul 2023 15:09:15 +0900 Subject: [PATCH] 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`. --- stdlib/REPL/src/REPLCompletions.jl | 44 +++++++++++++++-------------- stdlib/REPL/test/replcompletions.jl | 12 +++++--- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index 2bdb201fa59e0..bcb6d30e85201 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -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 @@ -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 = [ @@ -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 diff --git a/stdlib/REPL/test/replcompletions.jl b/stdlib/REPL/test/replcompletions.jl index 73b4cf7dd5d40..4f03f5ab79894 100644 --- a/stdlib/REPL/test/replcompletions.jl +++ b/stdlib/REPL/test/replcompletions.jl @@ -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