Skip to content

Commit

Permalink
Fix a few cases of REPL completion crash (#43541)
Browse files Browse the repository at this point in the history
  • Loading branch information
Liozou authored Dec 26, 2021
1 parent a068e90 commit ee16fe6
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 9 deletions.
39 changes: 30 additions & 9 deletions stdlib/REPL/src/REPLCompletions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -466,11 +466,22 @@ function get_type(sym::Expr, fn::Module)
val, found = try_get_type(sym, fn)
found && return val, found
# https://github.com/JuliaLang/julia/issues/27184
if isexpr(sym, :macrocall)
newsym = if isexpr(sym, :macrocall)
_, found = get_type(first(sym.args), fn)
found || return Any, false
try
Meta.lower(fn, sym)
catch e
e isa LoadError && return Any, false
# If e is not a LoadError then Meta.lower crashed in an unexpected way.
# Since this is not a specific to the user code but an internal error,
# rethrow the error to allow reporting it.
rethrow()
end
else
Meta.lower(fn, sym)
end
return try_get_type(Meta.lower(fn, sym), fn)
return try_get_type(newsym, fn)
end

function get_type(sym, fn::Module)
Expand Down Expand Up @@ -611,6 +622,21 @@ function afterusing(string::String, startpos::Int)
return occursin(r"^\b(using|import)\s*((\w+[.])*\w+\s*,\s*)*$", str[fr:end])
end

function close_path_completion(str, startpos, r, paths, pos)
length(paths) == 1 || return false # Only close if there's a single choice...
_path = str[startpos:prevind(str, first(r))] * (paths[1]::PathCompletion).path
path = expanduser(replace(_path, r"\\ " => " "))
# ...except if it's a directory...
try
isdir(path)
catch e
e isa Base.IOError || rethrow() # `path` cannot be determined to be a file
end && return false
# ...and except if there's already a " at the cursor.
return lastindex(str) <= pos || str[nextind(str, pos)] != '"'
end


function bslash_completions(string::String, pos::Int)
slashpos = something(findprev(isequal('\\'), string, pos), 0)
if (something(findprev(in(bslash_separators), string, pos), 0) < slashpos &&
Expand Down Expand Up @@ -747,13 +773,8 @@ function completions(string::String, pos::Int, context_module::Module=Main)

paths, r, success = complete_path(replace(string[r], r"\\ " => " "), pos)

if inc_tag === :string &&
length(paths) == 1 && # Only close if there's a single choice,
!isdir(expanduser(replace(string[startpos:prevind(string, first(r))] * paths[1].path,
r"\\ " => " "))) && # except if it's a directory
(lastindex(string) <= pos ||
string[nextind(string,pos)] != '"') # or there's already a " at the cursor.
paths[1] = PathCompletion(paths[1].path * "\"")
if inc_tag === :string && close_path_completion(string, startpos, r, paths, pos)
paths[1] = PathCompletion((paths[1]::PathCompletion).path * "\"")
end

#Latex symbols can be completed for strings
Expand Down
78 changes: 78 additions & 0 deletions stdlib/REPL/test/replcompletions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,25 @@ let ex = quote
macro foobar()
:()
end
macro barfoo(ex)
ex
end
macro error_expanding()
error("cannot expand @error_expanding")
:()
end
macro error_lowering_conditional(a)
if isa(a, Number)
return a
end
throw(AssertionError("Not a Number"))
:()
end
macro error_throwing()
return quote
error("@error_throwing throws an error")
end
end

primitive type NonStruct 8 end
Base.propertynames(::NonStruct) = (:a, :b, :c)
Expand Down Expand Up @@ -813,6 +832,45 @@ let s, c, r
end
end

#test that it does not crash on files for which `stat` errors
let current_dir, forbidden
# Issue #36855
if !Sys.iswindows() || Sys.windows_version() >= Sys.WINDOWS_VISTA_VER
mktempdir() do path
selfsymlink = joinpath(path, "selfsymlink")
symlink(selfsymlink, selfsymlink)
@test try
stat(selfsymlink) # should crash with a IOError
false
catch e
e isa Base.IOError && occursin("ELOOP", e.msg)
end
c, r = test_complete("\"$(joinpath(path, "selfsym"))")
@test c == ["selfsymlink"]
end
end

# Issue #32797
forbidden = Sys.iswindows() ? "C:\\S" : "/root/x"
test_complete(forbidden); @test true # simply check that it did not crash

# Issue #19310
if Sys.iswindows()
current_dir = pwd()
cd("C:")
test_complete("C"); @test true
test_complete("C:"); @test true
test_complete("C:\\"); @test true
if isdir("D:")
cd("D:")
test_complete("C"); @test true
test_complete("C:"); @test true
test_complete("C:\\"); @test true
end
cd(current_dir)
end
end

#test that it can auto complete with spaces in file/path
mktempdir() do path
space_folder = randstring() * " α"
Expand Down Expand Up @@ -1161,6 +1219,26 @@ let
(test_complete("@Main.noexist."); @test true)
end

let # Check that completion does not crash on (possibly invalid) macro calls
(test_complete("@show."); @test true)
(test_complete("@macroexpand."); @test true)
(test_complete("@.."); @test true)
(test_complete("CompletionFoo.@foobar."); @test true)
(test_complete("CompletionFoo.@foobar()."); @test true)
(test_complete("CompletionFoo.@foobar(4)."); @test true)
(test_complete("CompletionFoo.@barfoo."); @test true)
(test_complete("CompletionFoo.@barfoo()."); @test true)
(test_complete("CompletionFoo.@barfoo(6)."); @test true)
(test_complete("CompletionFoo.@error_expanding."); @test true)
(test_complete("CompletionFoo.@error_expanding()."); @test true)
(test_complete("CompletionFoo.@error_lowering_conditional."); @test true)
(test_complete("CompletionFoo.@error_lowering_conditional()."); @test true)
(test_complete("CompletionFoo.@error_lowering_conditional(3)."); @test true)
(test_complete("CompletionFoo.@error_lowering_conditional('a')."); @test true)
(test_complete("CompletionFoo.@error_throwing."); @test true)
(test_complete("CompletionFoo.@error_throwing()."); @test true)
end

@testset "https://github.com/JuliaLang/julia/issues/40247" begin
# getfield type completion can work for complicated expression

Expand Down

0 comments on commit ee16fe6

Please sign in to comment.