• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JuliaLang / julia / #37663

25 Oct 2023 09:38PM UTC coverage: 85.415% (-2.6%) from 87.999%
#37663

push

local

web-flow
Make the static analyzer happier in aarch64-darwin (#51861)

71554 of 83772 relevant lines covered (85.42%)

12665921.06 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.55
/stdlib/REPL/src/REPLCompletions.jl
1
# This file is a part of Julia. License is MIT: https://julialang.org/license
2

3
module REPLCompletions
4

5
export completions, shell_completions, bslash_completions, completion_text
6

7
using Core: CodeInfo, MethodInstance, CodeInstance, Const
8
const CC = Core.Compiler
9
using Base.Meta
10
using Base: propertynames, something
11

12
abstract type Completion end
13

14
struct TextCompletion <: Completion
15
    text::String
4✔
16
end
17

18
struct KeywordCompletion <: Completion
19
    keyword::String
158✔
20
end
21

22
struct KeyvalCompletion <: Completion
23
    keyval::String
15✔
24
end
25

26
struct PathCompletion <: Completion
27
    path::String
7,103✔
28
end
29

30
struct ModuleCompletion <: Completion
31
    parent::Module
380,259✔
32
    mod::String
33
end
34

35
struct PackageCompletion <: Completion
36
    package::String
198✔
37
end
38

39
struct PropertyCompletion <: Completion
40
    value
77✔
41
    property::Symbol
42
end
43

44
struct FieldCompletion <: Completion
45
    typ::DataType
18✔
46
    field::Symbol
47
end
48

49
struct MethodCompletion <: Completion
50
    tt # may be used by an external consumer to infer return type, etc.
51
    method::Method
52
    MethodCompletion(@nospecialize(tt), method::Method) = new(tt, method)
2,264✔
53
end
54

55
struct BslashCompletion <: Completion
56
    bslash::String
5,164✔
57
end
58

59
struct ShellCompletion <: Completion
60
    text::String
61
end
62

63
struct DictCompletion <: Completion
64
    dict::AbstractDict
124✔
65
    key::String
66
end
67

68
struct KeywordArgumentCompletion <: Completion
69
    kwarg::String
47✔
70
end
71

72
# interface definition
73
function Base.getproperty(c::Completion, name::Symbol)
2,966✔
74
    if name === :text
8,277,068✔
75
        return getfield(c, :text)::String
4✔
76
    elseif name === :keyword
8,275,267✔
77
        return getfield(c, :keyword)::String
1,797✔
78
    elseif name === :path
8,268,284✔
79
        return getfield(c, :path)::String
57,719✔
80
    elseif name === :parent
8,268,284✔
81
        return getfield(c, :parent)::Module
×
82
    elseif name === :mod
9,006✔
83
        return getfield(c, :mod)::String
8,259,278✔
84
    elseif name === :package
9,006✔
85
        return getfield(c, :package)::String
2,504✔
86
    elseif name === :property
6,502✔
87
        return getfield(c, :property)::Symbol
203✔
88
    elseif name === :field
6,299✔
89
        return getfield(c, :field)::Symbol
30✔
90
    elseif name === :method
5,460✔
91
        return getfield(c, :method)::Method
2,266✔
92
    elseif name === :bslash
296✔
93
        return getfield(c, :bslash)::String
5,164✔
94
    elseif name === :text
296✔
95
        return getfield(c, :text)::String
×
96
    elseif name === :key
296✔
97
        return getfield(c, :key)::String
124✔
98
    elseif name === :kwarg
172✔
99
        return getfield(c, :kwarg)::String
89✔
100
    end
101
    return getfield(c, name)
83✔
102
end
103

104
_completion_text(c::TextCompletion) = c.text
4✔
105
_completion_text(c::KeywordCompletion) = c.keyword
1,797✔
106
_completion_text(c::KeyvalCompletion) = c.keyval
83✔
107
_completion_text(c::PathCompletion) = c.path
6,962✔
108
_completion_text(c::ModuleCompletion) = c.mod
8,259,278✔
109
_completion_text(c::PackageCompletion) = c.package
2,504✔
110
_completion_text(c::PropertyCompletion) = sprint(Base.show_sym, c.property)
203✔
111
_completion_text(c::FieldCompletion) = sprint(Base.show_sym, c.field)
30✔
112
_completion_text(c::MethodCompletion) = repr(c.method)
797✔
113
_completion_text(c::BslashCompletion) = c.bslash
5,164✔
114
_completion_text(c::ShellCompletion) = c.text
×
115
_completion_text(c::DictCompletion) = c.key
124✔
116
_completion_text(c::KeywordArgumentCompletion) = c.kwarg*'='
89✔
117

118
completion_text(c) = _completion_text(c)::String
8,277,035✔
119

120
const Completions = Tuple{Vector{Completion}, UnitRange{Int}, Bool}
121

122
function completes_global(x, name)
1,844,997✔
123
    return startswith(x, name) && !('#' in x)
1,844,997✔
124
end
125

126
function appendmacro!(syms, macros, needle, endchar)
20,484✔
127
    for macsym in macros
38,742✔
128
        s = String(macsym)
51,964✔
129
        if endswith(s, needle)
51,964✔
130
            from = nextind(s, firstindex(s))
3,445✔
131
            to = prevind(s, sizeof(s)-sizeof(needle)+1)
3,445✔
132
            push!(syms, s[from:to]*endchar)
6,890✔
133
        end
134
    end
51,964✔
135
end
136

137
function filtered_mod_names(ffunc::Function, mod::Module, name::AbstractString, all::Bool = false, imported::Bool = false)
10,242✔
138
    ssyms = names(mod, all = all, imported = imported)
18,929✔
139
    all || filter!(Base.Fix1(Base.isexported, mod), ssyms)
18,929✔
140
    filter!(ffunc, ssyms)
10,242✔
141
    macros = filter(x -> startswith(String(x), "@" * name), ssyms)
1,855,239✔
142
    syms = String[sprint((io,s)->Base.show_sym(io, s; allow_macroname=true), s) for s in ssyms if completes_global(String(s), name)]
387,056✔
143
    appendmacro!(syms, macros, "_str", "\"")
10,242✔
144
    appendmacro!(syms, macros, "_cmd", "`")
10,242✔
145
    return [ModuleCompletion(mod, sym) for sym in syms]
10,242✔
146
end
147

148
# REPL Symbol Completions
149
function complete_symbol(@nospecialize(ex), name::String, @nospecialize(ffunc), context_module::Module=Main)
1,631✔
150
    mod = context_module
×
151

152
    lookup_module = true
×
153
    t = Union{}
×
154
    val = nothing
×
155
    if ex !== nothing
1,631✔
156
        res = repl_eval_ex(ex, context_module)
679✔
157
        res === nothing && return Completion[]
679✔
158
        if res isa Const
670✔
159
            val = res.val
648✔
160
            if isa(val, Module)
648✔
161
                mod = val
603✔
162
                lookup_module = true
603✔
163
            else
164
                lookup_module = false
×
165
                t = typeof(val)
693✔
166
            end
167
        else
168
            lookup_module = false
×
169
            t = CC.widenconst(res)
22✔
170
        end
171
    end
172

173
    suggestions = Completion[]
1,622✔
174
    if lookup_module
1,622✔
175
        # We will exclude the results that the user does not want, as well
176
        # as excluding Main.Main.Main, etc., because that's most likely not what
177
        # the user wants
178
        p = let mod=mod, modname=nameof(mod)
1,555✔
179
            (s::Symbol) -> !Base.isdeprecated(mod, s) && s != modname && ffunc(mod, s)::Bool
1,872,101✔
180
        end
181
        # Looking for a binding in a module
182
        if mod == context_module
1,555✔
183
            # Also look in modules we got through `using`
184
            mods = ccall(:jl_module_usings, Any, (Any,), context_module)::Vector
1,262✔
185
            for m in mods
1,262✔
186
                append!(suggestions, filtered_mod_names(p, m::Module, name))
11,749✔
187
            end
9,949✔
188
            append!(suggestions, filtered_mod_names(p, mod, name, true, true))
1,982✔
189
        else
190
            append!(suggestions, filtered_mod_names(p, mod, name, true, false))
1,848✔
191
        end
192
    elseif val !== nothing # looking for a property of an instance
67✔
193
        for property in propertynames(val, false)
45✔
194
            # TODO: support integer arguments (#36872)
195
            if property isa Symbol && startswith(string(property), name)
164✔
196
                push!(suggestions, PropertyCompletion(val, property))
77✔
197
            end
198
        end
83✔
199
    elseif field_completion_eligible(t)
22✔
200
        # Looking for a member of a type
201
        add_field_completions!(suggestions, name, t)
13✔
202
    end
203
    return suggestions
1,622✔
204
end
205

206
function add_field_completions!(suggestions::Vector{Completion}, name::String, @nospecialize(t))
15✔
207
    if isa(t, Union)
15✔
208
        add_field_completions!(suggestions, name, t.a)
2✔
209
        add_field_completions!(suggestions, name, t.b)
2✔
210
    else
211
        @assert isconcretetype(t)
15✔
212
        fields = fieldnames(t)
15✔
213
        for field in fields
15✔
214
            isa(field, Symbol) || continue # Tuple type has ::Int field name
18✔
215
            s = string(field)
18✔
216
            if startswith(s, name)
36✔
217
                push!(suggestions, FieldCompletion(t, field))
18✔
218
            end
219
        end
18✔
220
    end
221
end
222

223
const GENERIC_PROPERTYNAMES_METHOD = which(propertynames, (Any,))
224

225
function field_completion_eligible(@nospecialize t)
4✔
226
    if isa(t, Union)
26✔
227
        return field_completion_eligible(t.a) && field_completion_eligible(t.b)
2✔
228
    end
229
    isconcretetype(t) || return false
31✔
230
    # field completion is correct only when `getproperty` fallbacks to `getfield`
231
    match = Base._which(Tuple{typeof(propertynames),t}; raise=false)
17✔
232
    match === nothing && return false
17✔
233
    return match.method === GENERIC_PROPERTYNAMES_METHOD
17✔
234
end
235

236
function complete_from_list(T::Type, list::Vector{String}, s::Union{String,SubString{String}})
1,226✔
237
    r = searchsorted(list, s)
1,226✔
238
    i = first(r)
1,226✔
239
    n = length(list)
1,226✔
240
    while i <= n && startswith(list[i],s)
2,563✔
241
        r = first(r):i
173✔
242
        i += 1
173✔
243
    end
173✔
244
    Completion[T(kw) for kw in list[r]]
1,226✔
245
end
246

247
const sorted_keywords = [
248
    "abstract type", "baremodule", "begin", "break", "catch", "ccall",
249
    "const", "continue", "do", "else", "elseif", "end", "export",
250
    "finally", "for", "function", "global", "if", "import",
251
    "let", "local", "macro", "module", "mutable struct",
252
    "primitive type", "quote", "return", "struct",
253
    "try", "using", "while"]
254

255
complete_keyword(s::Union{String,SubString{String}}) = complete_from_list(KeywordCompletion, sorted_keywords, s)
544✔
256

257
const sorted_keyvals = ["false", "true"]
258

259
complete_keyval(s::Union{String,SubString{String}}) = complete_from_list(KeyvalCompletion, sorted_keyvals, s)
682✔
260

261
function complete_path(path::AbstractString, pos::Int;
1,136✔
262
                       use_envpath=false, shell_escape=false,
263
                       string_escape=false)
264
    @assert !(shell_escape && string_escape)
568✔
265
    if Base.Sys.isunix() && occursin(r"^~(?:/|$)", path)
568✔
266
        # if the path is just "~", don't consider the expanded username as a prefix
267
        if path == "~"
2✔
268
            dir, prefix = homedir(), ""
2✔
269
        else
270
            dir, prefix = splitdir(homedir() * path[2:end])
2✔
271
        end
272
    else
273
        dir, prefix = splitdir(path)
566✔
274
    end
275
    local files
×
276
    try
568✔
277
        if isempty(dir)
568✔
278
            files = readdir()
302✔
279
        elseif isdir(dir)
266✔
280
            files = readdir(dir)
107✔
281
        else
282
            return Completion[], 0:-1, false
568✔
283
        end
284
    catch
285
        return Completion[], 0:-1, false
×
286
    end
287

288
    matches = Set{String}()
409✔
289
    for file in files
414✔
290
        if startswith(file, prefix)
80,996✔
291
            p = joinpath(dir, file)
6,742✔
292
            is_dir = try isdir(p) catch; false end
20,228✔
293
            push!(matches, is_dir ? joinpath(file, "") : file)
6,742✔
294
        end
295
    end
44,656✔
296

297
    if use_envpath && length(dir) == 0
409✔
298
        # Look for files in PATH as well
299
        local pathdirs = split(ENV["PATH"], @static Sys.iswindows() ? ";" : ":")
18✔
300

301
        for pathdir in pathdirs
18✔
302
            local actualpath
×
303
            try
228✔
304
                actualpath = realpath(pathdir)
260✔
305
            catch
306
                # Bash doesn't expect every folder in PATH to exist, so neither shall we
307
                continue
32✔
308
            end
309

310
            if actualpath != pathdir && in(actualpath,pathdirs)
244✔
311
                # Remove paths which (after resolving links) are in the env path twice.
312
                # Many distros eg. point /bin to /usr/bin but have both in the env path.
313
                continue
48✔
314
            end
315

316
            local filesinpath
×
317
            try
148✔
318
                filesinpath = readdir(pathdir)
149✔
319
            catch e
320
                # Bash allows dirs in PATH that can't be read, so we should as well.
321
                if isa(e, Base.IOError) || isa(e, Base.ArgumentError)
1✔
322
                    continue
1✔
323
                else
324
                    # We only handle IOError and ArgumentError here
325
                    rethrow()
×
326
                end
327
            end
328

329
            for file in filesinpath
195✔
330
                # In a perfect world, we would filter on whether the file is executable
331
                # here, or even on whether the current user can execute the file in question.
332
                if startswith(file, prefix) && isfile(joinpath(pathdir, file))
62,673✔
333
                    push!(matches, file)
589✔
334
                end
335
            end
34,285✔
336
        end
246✔
337
    end
338

339
    function do_escape(s)
409✔
340
        return shell_escape ? replace(s, r"(\s|\\)" => s"\\\0") :
10,825✔
341
               string_escape ? escape_string(s, ('\"','$')) :
342
               s
343
    end
344

345
    matchList = Completion[PathCompletion(do_escape(s)) for s in matches]
7,369✔
346
    startpos = pos - lastindex(do_escape(prefix)) + 1
543✔
347
    # The pos - lastindex(prefix) + 1 is correct due to `lastindex(prefix)-lastindex(prefix)==0`,
348
    # hence we need to add one to get the first index. This is also correct when considering
349
    # pos, because pos is the `lastindex` a larger string which `endswith(path)==true`.
350
    return matchList, startpos:pos, !isempty(matchList)
409✔
351
end
352

353
function complete_expanduser(path::AbstractString, r)
138✔
354
    expanded =
138✔
355
        try expanduser(path)
139✔
356
        catch e
357
            e isa ArgumentError || rethrow()
1✔
358
            path
139✔
359
        end
360
    return Completion[PathCompletion(expanded)], r, path != expanded
138✔
361
end
362

363
# Returns a range that includes the method name in front of the first non
364
# closed start brace from the end of the string.
365
function find_start_brace(s::AbstractString; c_start='(', c_end=')')
5,358✔
366
    r = reverse(s)
2,679✔
367
    i = firstindex(r)
×
368
    braces = in_comment = 0
×
369
    in_single_quotes = in_double_quotes = in_back_ticks = false
×
370
    while i <= ncodeunits(r)
62,107✔
371
        c, i = iterate(r, i)
120,208✔
372
        if c == '#' && i <= ncodeunits(r) && iterate(r, i)[1] == '='
60,104✔
373
            c, i = iterate(r, i) # consume '='
8✔
374
            new_comments = 1
×
375
            # handle #=#=#=#, by counting =# pairs
376
            while i <= ncodeunits(r) && iterate(r, i)[1] == '#'
6✔
377
                c, i = iterate(r, i) # consume '#'
6✔
378
                iterate(r, i)[1] == '=' || break
3✔
379
                c, i = iterate(r, i) # consume '='
4✔
380
                new_comments += 1
2✔
381
            end
2✔
382
            if c == '='
4✔
383
                in_comment += new_comments
3✔
384
            else
385
                in_comment -= new_comments
5✔
386
            end
387
        elseif !in_single_quotes && !in_double_quotes && !in_back_ticks && in_comment == 0
60,100✔
388
            if c == c_start
58,362✔
389
                braces += 1
930✔
390
            elseif c == c_end
57,432✔
391
                braces -= 1
254✔
392
            elseif c == '\''
57,178✔
393
                in_single_quotes = true
16✔
394
            elseif c == '"'
57,162✔
395
                in_double_quotes = true
271✔
396
            elseif c == '`'
56,891✔
397
                in_back_ticks = true
58,362✔
398
            end
399
        else
400
            if in_single_quotes &&
1,738✔
401
                c == '\'' && i <= ncodeunits(r) && iterate(r, i)[1] != '\\'
402
                in_single_quotes = false
15✔
403
            elseif in_double_quotes &&
1,723✔
404
                c == '"' && i <= ncodeunits(r) && iterate(r, i)[1] != '\\'
405
                in_double_quotes = false
228✔
406
            elseif in_back_ticks &&
1,495✔
407
                c == '`' && i <= ncodeunits(r) && iterate(r, i)[1] != '\\'
408
                in_back_ticks = false
7✔
409
            elseif in_comment > 0 &&
1,488✔
410
                c == '=' && i <= ncodeunits(r) && iterate(r, i)[1] == '#'
411
                # handle =#=#=#=, by counting #= pairs
412
                c, i = iterate(r, i) # consume '#'
6✔
413
                old_comments = 1
×
414
                while i <= ncodeunits(r) && iterate(r, i)[1] == '='
4✔
415
                    c, i = iterate(r, i) # consume '='
4✔
416
                    iterate(r, i)[1] == '#' || break
2✔
417
                    c, i = iterate(r, i) # consume '#'
2✔
418
                    old_comments += 1
1✔
419
                end
1✔
420
                if c == '#'
3✔
421
                    in_comment -= old_comments
2✔
422
                else
423
                    in_comment += old_comments
1✔
424
                end
425
            end
426
        end
427
        braces == 1 && break
60,104✔
428
    end
59,428✔
429
    braces != 1 && return 0:-1, -1
2,679✔
430
    method_name_end = reverseind(s, i)
676✔
431
    startind = nextind(s, something(findprev(in(non_identifier_chars), s, method_name_end), 0))::Int
1,042✔
432
    return (startind:lastindex(s), method_name_end)
676✔
433
end
434

435
struct REPLInterpreterCache
436
    dict::IdDict{MethodInstance,CodeInstance}
×
437
end
438
REPLInterpreterCache() = REPLInterpreterCache(IdDict{MethodInstance,CodeInstance}())
×
439
const REPL_INTERPRETER_CACHE = REPLInterpreterCache()
440

441
function get_code_cache()
×
442
    # XXX Avoid storing analysis results into the cache that persists across precompilation,
443
    #     as [sys|pkg]image currently doesn't support serializing externally created `CodeInstance`.
444
    #     Otherwise, `CodeInstance`s created by `REPLInterpreter`, that are much less optimized
445
    #     that those produced by `NativeInterpreter`, will leak into the native code cache,
446
    #     potentially causing runtime slowdown.
447
    #     (see https://github.com/JuliaLang/julia/issues/48453).
448
    if Base.generating_output()
493✔
449
        return REPLInterpreterCache()
×
450
    else
451
        return REPL_INTERPRETER_CACHE
493✔
452
    end
453
end
454

455
struct REPLInterpreter <: CC.AbstractInterpreter
456
    limit_aggressive_inference::Bool
457
    world::UInt
458
    inf_params::CC.InferenceParams
459
    opt_params::CC.OptimizationParams
460
    inf_cache::Vector{CC.InferenceResult}
461
    code_cache::REPLInterpreterCache
462
    function REPLInterpreter(limit_aggressive_inference::Bool=false;
986✔
463
                             world::UInt = Base.get_world_counter(),
464
                             inf_params::CC.InferenceParams = CC.InferenceParams(;
465
                                 aggressive_constant_propagation=true,
466
                                 unoptimize_throw_blocks=false),
467
                             opt_params::CC.OptimizationParams = CC.OptimizationParams(),
468
                             inf_cache::Vector{CC.InferenceResult} = CC.InferenceResult[],
469
                             code_cache::REPLInterpreterCache = get_code_cache())
470
        return new(limit_aggressive_inference, world, inf_params, opt_params, inf_cache, code_cache)
493✔
471
    end
472
end
473
CC.InferenceParams(interp::REPLInterpreter) = interp.inf_params
208,196✔
474
CC.OptimizationParams(interp::REPLInterpreter) = interp.opt_params
×
475
CC.get_world_counter(interp::REPLInterpreter) = interp.world
59,382✔
476
CC.get_inference_cache(interp::REPLInterpreter) = interp.inf_cache
46,548✔
477
CC.code_cache(interp::REPLInterpreter) = CC.WorldView(interp.code_cache, CC.WorldRange(interp.world))
41,853✔
478
CC.get(wvc::CC.WorldView{REPLInterpreterCache}, mi::MethodInstance, default) = get(wvc.cache.dict, mi, default)
68,488✔
479
CC.getindex(wvc::CC.WorldView{REPLInterpreterCache}, mi::MethodInstance) = getindex(wvc.cache.dict, mi)
×
480
CC.haskey(wvc::CC.WorldView{REPLInterpreterCache}, mi::MethodInstance) = haskey(wvc.cache.dict, mi)
3,036✔
481
CC.setindex!(wvc::CC.WorldView{REPLInterpreterCache}, ci::CodeInstance, mi::MethodInstance) = setindex!(wvc.cache.dict, ci, mi)
3,036✔
482

483
# REPLInterpreter is only used for type analysis, so it should disable optimization entirely
484
CC.may_optimize(::REPLInterpreter) = false
×
485

486
# REPLInterpreter analyzes a top-level frame, so better to not bail out from it
487
CC.bail_out_toplevel_call(::REPLInterpreter, ::CC.InferenceLoopState, ::CC.InferenceState) = false
×
488

489
# `REPLInterpreter` aggressively resolves global bindings to enable reasonable completions
490
# for lines like `Mod.a.|` (where `|` is the cursor position).
491
# Aggressive binding resolution poses challenges for the inference cache validation
492
# (until https://github.com/JuliaLang/julia/issues/40399 is implemented).
493
# To avoid the cache validation issues, `REPLInterpreter` only allows aggressive binding
494
# resolution for top-level frame representing REPL input code and for child uncached frames
495
# that are constant propagated from the top-level frame ("repl-frame"s). This works, even if
496
# those global bindings are not constant and may be mutated in the future, since:
497
# a.) "repl-frame"s are never cached, and
498
# b.) mutable values are never observed by any cached frames.
499
#
500
# `REPLInterpreter` also aggressively concrete evaluate `:inconsistent` calls within
501
# "repl-frame" to provide reasonable completions for lines like `Ref(Some(42))[].|`.
502
# Aggressive concrete evaluation allows us to get accurate type information about complex
503
# expressions that otherwise can not be constant folded, in a safe way, i.e. it still
504
# doesn't evaluate effectful expressions like `pop!(xs)`.
505
# Similarly to the aggressive binding resolution, aggressive concrete evaluation doesn't
506
# present any cache validation issues because "repl-frame" is never cached.
507

508
# `REPLInterpreter` is specifically used by `repl_eval_ex`, where all top-level frames are
509
# `repl_frame` always. However, this assumption wouldn't stand if `REPLInterpreter` were to
510
# be employed, for instance, by `typeinf_ext_toplevel`.
511
is_repl_frame(sv::CC.InferenceState) = sv.linfo.def isa Module && !sv.cached
342✔
512

513
function is_call_graph_uncached(sv::CC.InferenceState)
261,606✔
514
    sv.cached && return false
356,941✔
515
    parent = sv.parent
269,247✔
516
    parent === nothing && return true
269,247✔
517
    return is_call_graph_uncached(parent::CC.InferenceState)
261,606✔
518
end
519

520
# aggressive global binding resolution within `repl_frame`
521
function CC.abstract_eval_globalref(interp::REPLInterpreter, g::GlobalRef,
×
522
                                    sv::CC.InferenceState)
523
    if (interp.limit_aggressive_inference ? is_repl_frame(sv) : is_call_graph_uncached(sv))
59,703✔
524
        if CC.isdefined_globalref(g)
5,109✔
525
            return Const(ccall(:jl_get_globalref_value, Any, (Any,), g))
5,104✔
526
        end
527
        return Union{}
5✔
528
    end
529
    return @invoke CC.abstract_eval_globalref(interp::CC.AbstractInterpreter, g::GlobalRef,
54,476✔
530
                                              sv::CC.InferenceState)
531
end
532

533
function is_repl_frame_getproperty(sv::CC.InferenceState)
×
534
    def = sv.linfo.def
×
535
    def isa Method || return false
×
536
    def.name === :getproperty || return false
×
537
    sv.cached && return false
×
538
    return is_repl_frame(sv.parent)
×
539
end
540

541
# aggressive global binding resolution for `getproperty(::Module, ::Symbol)` calls within `repl_frame`
542
function CC.builtin_tfunction(interp::REPLInterpreter, @nospecialize(f),
17,406✔
543
                              argtypes::Vector{Any}, sv::CC.InferenceState)
544
    if f === Core.getglobal && (interp.limit_aggressive_inference ? is_repl_frame_getproperty(sv) : is_call_graph_uncached(sv))
17,472✔
545
        if length(argtypes) == 2
48✔
546
            a1, a2 = argtypes
48✔
547
            if isa(a1, Const) && isa(a2, Const)
48✔
548
                a1val, a2val = a1.val, a2.val
48✔
549
                if isa(a1val, Module) && isa(a2val, Symbol)
48✔
550
                    g = GlobalRef(a1val, a2val)
48✔
551
                    if CC.isdefined_globalref(g)
48✔
552
                        return Const(ccall(:jl_get_globalref_value, Any, (Any,), g))
48✔
553
                    end
554
                    return Union{}
×
555
                end
556
            end
557
        end
558
    end
559
    return @invoke CC.builtin_tfunction(interp::CC.AbstractInterpreter, f::Any,
17,358✔
560
                                        argtypes::Vector{Any}, sv::CC.InferenceState)
561
end
562

563
# aggressive concrete evaluation for `:inconsistent` frames within `repl_frame`
564
function CC.concrete_eval_eligible(interp::REPLInterpreter, @nospecialize(f),
35,859✔
565
                                   result::CC.MethodCallResult, arginfo::CC.ArgInfo,
566
                                   sv::CC.InferenceState)
567
    if (interp.limit_aggressive_inference ? is_repl_frame(sv) : is_call_graph_uncached(sv))
35,908✔
568
        neweffects = CC.Effects(result.effects; consistent=CC.ALWAYS_TRUE)
2,492✔
569
        result = CC.MethodCallResult(result.rt, result.edgecycle, result.edgelimited,
4,982✔
570
                                     result.edge, neweffects)
571
    end
572
    ret = @invoke CC.concrete_eval_eligible(interp::CC.AbstractInterpreter, f::Any,
71,718✔
573
                                            result::CC.MethodCallResult, arginfo::CC.ArgInfo,
574
                                            sv::CC.InferenceState)
575
    if ret === :semi_concrete_eval
35,859✔
576
        # while the base eligibility check probably won't permit semi-concrete evaluation
577
        # for `REPLInterpreter` (given it completely turns off optimization),
578
        # this ensures we don't inadvertently enter irinterp
579
        ret = :none
×
580
    end
581
    return ret
35,859✔
582
end
583

584
# allow constant propagation for mutable constants
585
function CC.const_prop_argument_heuristic(interp::REPLInterpreter, arginfo::CC.ArgInfo, sv::CC.InferenceState)
×
586
    if !interp.limit_aggressive_inference
35,335✔
587
        any(@nospecialize(a)->isa(a, Const), arginfo.argtypes) && return true # even if mutable
70,629✔
588
    end
589
    return @invoke CC.const_prop_argument_heuristic(interp::CC.AbstractInterpreter, arginfo::CC.ArgInfo, sv::CC.InferenceState)
69✔
590
end
591

592
function resolve_toplevel_symbols!(src::Core.CodeInfo, mod::Module)
×
593
    @ccall jl_resolve_globals_in_ir(
493✔
594
        #=jl_array_t *stmts=# src.code::Any,
595
        #=jl_module_t *m=# mod::Any,
596
        #=jl_svec_t *sparam_vals=# Core.svec()::Any,
597
        #=int binding_effects=# 0::Int)::Cvoid
598
    return src
×
599
end
600

601
# lower `ex` and run type inference on the resulting top-level expression
602
function repl_eval_ex(@nospecialize(ex), context_module::Module; limit_aggressive_inference::Bool=false)
2,610✔
603
    if (isexpr(ex, :toplevel) || isexpr(ex, :tuple)) && !isempty(ex.args)
2,609✔
604
        # get the inference result for the last expression
605
        ex = ex.args[end]
3✔
606
    end
607
    lwr = try
1,305✔
608
        Meta.lower(context_module, ex)
1,310✔
609
    catch # macro expansion failed, etc.
610
        return nothing
5✔
611
    end
612
    if lwr isa Symbol
1,300✔
613
        return isdefined(context_module, lwr) ? Const(getfield(context_module, lwr)) : nothing
675✔
614
    end
615
    lwr isa Expr || return Const(lwr) # `ex` is literal
727✔
616
    isexpr(lwr, :thunk) || return nothing # lowered to `Expr(:error, ...)` or similar
553✔
617
    src = lwr.args[1]::Core.CodeInfo
493✔
618

619
    # construct top-level `MethodInstance`
620
    mi = ccall(:jl_new_method_instance_uninit, Ref{Core.MethodInstance}, ());
493✔
621
    mi.specTypes = Tuple{}
493✔
622

623
    mi.def = context_module
493✔
624
    resolve_toplevel_symbols!(src, context_module)
493✔
625
    @atomic mi.uninferred = src
493✔
626

627
    interp = REPLInterpreter(limit_aggressive_inference)
986✔
628
    result = CC.InferenceResult(mi)
493✔
629
    frame = CC.InferenceState(result, src, #=cache=#:no, interp)
493✔
630

631
    # NOTE Use the fixed world here to make `REPLInterpreter` robust against
632
    #      potential invalidations of `Core.Compiler` methods.
633
    Base.invoke_in_world(COMPLETION_WORLD[], CC.typeinf, interp, frame)
493✔
634

635
    result = frame.result.result
493✔
636
    result === Union{} && return nothing # for whatever reason, callers expect this as the Bottom and/or Top type instead
493✔
637
    return result
485✔
638
end
639

640
# `COMPLETION_WORLD[]` will be initialized within `__init__`
641
# (to allow us to potentially remove REPL from the sysimage in the future).
642
# Note that inference from the `code_typed` call below will use the current world age
643
# rather than `typemax(UInt)`, since `Base.invoke_in_world` uses the current world age
644
# when the given world age is higher than the current one.
645
const COMPLETION_WORLD = Ref{UInt}(typemax(UInt))
646

647
# Generate code cache for `REPLInterpreter` now:
648
# This code cache will be available at the world of `COMPLETION_WORLD`,
649
# assuming no invalidation will happen before initializing REPL.
650
# Once REPL is loaded, `REPLInterpreter` will be resilient against future invalidations.
651
code_typed(CC.typeinf, (REPLInterpreter, CC.InferenceState))
652

653
# Method completion on function call expression that look like :(max(1))
654
MAX_METHOD_COMPLETIONS::Int = 40
655
function _complete_methods(ex_org::Expr, context_module::Module, shift::Bool)
309✔
656
    funct = repl_eval_ex(ex_org.args[1], context_module)
309✔
657
    funct === nothing && return 2, nothing, [], Set{Symbol}()
309✔
658
    funct = CC.widenconst(funct)
305✔
659
    args_ex, kwargs_ex, kwargs_flag = complete_methods_args(ex_org, context_module, true, true)
604✔
660
    return kwargs_flag, funct, args_ex, kwargs_ex
305✔
661
end
662

663
function complete_methods(ex_org::Expr, context_module::Module=Main, shift::Bool=false)
×
664
    kwargs_flag, funct, args_ex, kwargs_ex = _complete_methods(ex_org, context_module, shift)::Tuple{Int, Any, Vector{Any}, Set{Symbol}}
138✔
665
    out = Completion[]
138✔
666
    kwargs_flag == 2 && return out # one of the kwargs is invalid
138✔
667
    kwargs_flag == 0 && push!(args_ex, Vararg{Any}) # allow more arguments if there is no semicolon
126✔
668
    complete_methods!(out, funct, args_ex, kwargs_ex, shift ? -2 : MAX_METHOD_COMPLETIONS, kwargs_flag == 1)
183✔
669
    return out
126✔
670
end
671

672
MAX_ANY_METHOD_COMPLETIONS::Int = 10
673
function complete_any_methods(ex_org::Expr, callee_module::Module, context_module::Module, moreargs::Bool, shift::Bool)
13✔
674
    out = Completion[]
13✔
675
    args_ex, kwargs_ex, kwargs_flag = try
13✔
676
        # this may throw, since we set default_any to false
677
        complete_methods_args(ex_org, context_module, false, false)
13✔
678
    catch ex
679
        ex isa ArgumentError || rethrow()
×
680
        return out
13✔
681
    end
682
    kwargs_flag == 2 && return out # one of the kwargs is invalid
13✔
683

684
    # moreargs determines whether to accept more args, independently of the presence of a
685
    # semicolon for the ".?(" syntax
686
    moreargs && push!(args_ex, Vararg{Any})
13✔
687

688
    seen = Base.IdSet()
13✔
689
    for name in names(callee_module; all=true)
13✔
690
        if !Base.isdeprecated(callee_module, name) && isdefined(callee_module, name) && !startswith(string(name), '#')
2,652✔
691
            func = getfield(callee_module, name)
650✔
692
            if !isa(func, Module)
650✔
693
                funct = Core.Typeof(func)
1,183✔
694
                if !in(funct, seen)
637✔
695
                    push!(seen, funct)
611✔
696
                    complete_methods!(out, funct, args_ex, kwargs_ex, MAX_ANY_METHOD_COMPLETIONS, false)
611✔
697
                end
698
            elseif callee_module === Main && isa(func, Module)
26✔
699
                callee_module2 = func
×
700
                for name in names(callee_module2)
×
701
                    if !Base.isdeprecated(callee_module2, name) && isdefined(callee_module2, name) && !startswith(string(name), '#')
×
702
                        func = getfield(callee_module, name)
×
703
                        if !isa(func, Module)
×
704
                            funct = Core.Typeof(func)
×
705
                            if !in(funct, seen)
×
706
                                push!(seen, funct)
×
707
                                complete_methods!(out, funct, args_ex, kwargs_ex, MAX_ANY_METHOD_COMPLETIONS, false)
×
708
                            end
709
                        end
710
                    end
711
                end
×
712
            end
713
        end
714
    end
1,339✔
715

716
    if !shift
13✔
717
        # Filter out methods where all arguments are `Any`
718
        filter!(out) do c
2✔
719
            isa(c, TextCompletion) && return false
12✔
720
            isa(c, MethodCompletion) || return true
12✔
721
            sig = Base.unwrap_unionall(c.method.sig)::DataType
12✔
722
            return !all(T -> T === Any || T === Vararg{Any}, sig.parameters[2:end])
20✔
723
        end
724
    end
725

726
    return out
13✔
727
end
728

729
function detect_invalid_kwarg!(kwargs_ex::Vector{Symbol}, @nospecialize(x), kwargs_flag::Int, possible_splat::Bool)
×
730
    n = isexpr(x, :kw) ? x.args[1] : x
57✔
731
    if n isa Symbol
55✔
732
        push!(kwargs_ex, n)
41✔
733
        return kwargs_flag
41✔
734
    end
735
    possible_splat && isexpr(x, :...) && return kwargs_flag
14✔
736
    return 2 # The kwarg is invalid
10✔
737
end
738

739
function detect_args_kwargs(funargs::Vector{Any}, context_module::Module, default_any::Bool, broadcasting::Bool)
318✔
740
    args_ex = Any[]
318✔
741
    kwargs_ex = Symbol[]
318✔
742
    kwargs_flag = 0
×
743
    # kwargs_flag is:
744
    # * 0 if there is no semicolon and no invalid kwarg
745
    # * 1 if there is a semicolon and no invalid kwarg
746
    # * 2 if there are two semicolons or more, or if some kwarg is invalid, which
747
    #        means that it is not of the form "bar=foo", "bar" or "bar..."
748
    for i in (1+!broadcasting):length(funargs)
482✔
749
        ex = funargs[i]
295✔
750
        if isexpr(ex, :parameters)
460✔
751
            kwargs_flag = ifelse(kwargs_flag == 0, 1, 2) # there should be at most one :parameters
59✔
752
            for x in ex.args
89✔
753
                kwargs_flag = detect_invalid_kwarg!(kwargs_ex, x, kwargs_flag, true)
46✔
754
            end
62✔
755
        elseif isexpr(ex, :kw)
401✔
756
            kwargs_flag = detect_invalid_kwarg!(kwargs_ex, ex, kwargs_flag, false)
22✔
757
        else
758
            if broadcasting
214✔
759
                # handle broadcasting, but only handle number of arguments instead of
760
                # argument types
761
                push!(args_ex, Any)
5✔
762
            else
763
                argt = repl_eval_ex(ex, context_module)
209✔
764
                if argt !== nothing
209✔
765
                    push!(args_ex, CC.widenconst(argt))
183✔
766
                elseif default_any
26✔
767
                    push!(args_ex, Any)
26✔
768
                else
769
                    throw(ArgumentError("argument not found"))
×
770
                end
771
            end
772
        end
773
    end
426✔
774
    return args_ex, Set{Symbol}(kwargs_ex), kwargs_flag
318✔
775
end
776

777
is_broadcasting_expr(ex::Expr) = ex.head === :. && isexpr(ex.args[2], :tuple)
1,799✔
778

779
function complete_methods_args(ex::Expr, context_module::Module, default_any::Bool, allow_broadcasting::Bool)
×
780
    if allow_broadcasting && is_broadcasting_expr(ex)
305✔
781
        return detect_args_kwargs((ex.args[2]::Expr).args, context_module, default_any, true)
6✔
782
    end
783
    return detect_args_kwargs(ex.args, context_module, default_any, false)
312✔
784
end
785

786
function complete_methods!(out::Vector{Completion}, @nospecialize(funct), args_ex::Vector{Any}, kwargs_ex::Set{Symbol}, max_method_completions::Int, exact_nargs::Bool)
906✔
787
    # Input types and number of arguments
788
    t_in = Tuple{funct, args_ex...}
906✔
789
    m = Base._methods_by_ftype(t_in, nothing, max_method_completions, Base.get_world_counter(),
906✔
790
        #=ambig=# true, Ref(typemin(UInt)), Ref(typemax(UInt)), Ptr{Int32}(C_NULL))
791
    if !isa(m, Vector)
906✔
792
        push!(out, TextCompletion(sprint(Base.show_signature_function, funct) * "( too many methods, use SHIFT-TAB to show )"))
4✔
793
        return
4✔
794
    end
795
    for match in m
1,373✔
796
        # TODO: if kwargs_ex, filter out methods without kwargs?
797
        push!(out, MethodCompletion(match.spec_types, match.method))
2,264✔
798
    end
2,264✔
799
    # TODO: filter out methods with wrong number of arguments if `exact_nargs` is set
800
end
801

802
include("latex_symbols.jl")
803
include("emoji_symbols.jl")
804

805
const non_identifier_chars = [" \t\n\r\"\\'`\$><=:;|&{}()[],+-*/?%^~"...]
806
const whitespace_chars = [" \t\n\r"...]
807
# "\"'`"... is added to whitespace_chars as non of the bslash_completions
808
# characters contain any of these characters. It prohibits the
809
# bslash_completions function to try and complete on escaped characters in strings
810
const bslash_separators = [whitespace_chars..., "\"'`"...]
811

812
const subscripts = Dict(k[3]=>v[1] for (k,v) in latex_symbols if startswith(k, "\\_") && length(k)==3)
813
const subscript_regex = Regex("^\\\\_[" * join(isdigit(k) || isletter(k) ? "$k" : "\\$k" for k in keys(subscripts)) * "]+\\z")
814
const superscripts = Dict(k[3]=>v[1] for (k,v) in latex_symbols if startswith(k, "\\^") && length(k)==3)
815
const superscript_regex = Regex("^\\\\\\^[" * join(isdigit(k) || isletter(k) ? "$k" : "\\$k" for k in keys(superscripts)) * "]+\\z")
816

817
# Aux function to detect whether we're right after a
818
# using or import keyword
819
function afterusing(string::String, startpos::Int)
1,490✔
820
    (isempty(string) || startpos == 0) && return false
1,490✔
821
    str = string[1:prevind(string,startpos)]
2,658✔
822
    isempty(str) && return false
1,487✔
823
    rstr = reverse(str)
1,171✔
824
    r = findfirst(r"\s(gnisu|tropmi)\b", rstr)
1,171✔
825
    r === nothing && return false
1,171✔
826
    fr = reverseind(str, last(r))
30✔
827
    return occursin(r"^\b(using|import)\s*((\w+[.])*\w+\s*,\s*)*$", str[fr:end])
30✔
828
end
829

830
function close_path_completion(str, startpos, r, paths, pos)
135✔
831
    length(paths) == 1 || return false  # Only close if there's a single choice...
254✔
832
    _path = str[startpos:prevind(str, first(r))] * (paths[1]::PathCompletion).path
25✔
833
    path = expanduser(unescape_string(replace(_path, "\\\$"=>"\$", "\\\""=>"\"")))
16✔
834
    # ...except if it's a directory...
835
    try
16✔
836
        isdir(path)
17✔
837
    catch e
838
        e isa Base.IOError || rethrow() # `path` cannot be determined to be a file
17✔
839
    end && return false
840
    # ...and except if there's already a " at the cursor.
841
    return lastindex(str) <= pos || str[nextind(str, pos)] != '"'
6✔
842
end
843

844
function bslash_completions(string::String, pos::Int)
1,905✔
845
    slashpos = something(findprev(isequal('\\'), string, pos), 0)
1,955✔
846
    if (something(findprev(in(bslash_separators), string, pos), 0) < slashpos &&
1,933✔
847
        !(1 < slashpos && (string[prevind(string, slashpos)]=='\\')))
848
        # latex / emoji symbol substitution
849
        s = string[slashpos:pos]
96✔
850
        latex = get(latex_symbols, s, "")
69✔
851
        if !isempty(latex) # complete an exact match
48✔
852
            return (true, (Completion[BslashCompletion(latex)], slashpos:pos, true))
21✔
853
        elseif occursin(subscript_regex, s)
27✔
854
            sub = map(c -> subscripts[c], s[3:end])
8✔
855
            return (true, (Completion[BslashCompletion(sub)], slashpos:pos, true))
1✔
856
        elseif occursin(superscript_regex, s)
26✔
857
            sup = map(c -> superscripts[c], s[3:end])
8✔
858
            return (true, (Completion[BslashCompletion(sup)], slashpos:pos, true))
1✔
859
        end
860
        emoji = get(emoji_symbols, s, "")
27✔
861
        if !isempty(emoji)
25✔
862
            return (true, (Completion[BslashCompletion(emoji)], slashpos:pos, true))
2✔
863
        end
864
        # return possible matches; these cannot be mixed with regular
865
        # Julian completions as only latex / emoji symbols contain the leading \
866
        if startswith(s, "\\:") # emoji
44✔
867
            namelist = Iterators.filter(k -> startswith(k, s), keys(emoji_symbols))
2,371✔
868
        else # latex
869
            namelist = Iterators.filter(k -> startswith(k, s), keys(latex_symbols))
104,984✔
870
        end
871
        return (true, (Completion[BslashCompletion(name) for name in sort!(collect(namelist))], slashpos:pos, true))
23✔
872
    end
873
    return (false, (Completion[], 0:-1, false))
1,857✔
874
end
875

876
function dict_identifier_key(str::String, tag::Symbol, context_module::Module=Main)
2,039✔
877
    if tag === :string
2,039✔
878
        str_close = str*"\""
158✔
879
    elseif tag === :cmd
1,880✔
880
        str_close = str*"`"
5✔
881
    else
882
        str_close = str
×
883
    end
884
    frange, end_of_identifier = find_start_brace(str_close, c_start='[', c_end=']')
2,038✔
885
    isempty(frange) && return (nothing, nothing, nothing)
2,038✔
886
    objstr = str[1:end_of_identifier]
212✔
887
    objex = Meta.parse(objstr, raise=false, depwarn=false)
106✔
888
    objt = repl_eval_ex(objex, context_module)
106✔
889
    isa(objt, Core.Const) || return (nothing, nothing, nothing)
130✔
890
    obj = objt.val
82✔
891
    isa(obj, AbstractDict) || return (nothing, nothing, nothing)
83✔
892
    length(obj)::Int < 1_000_000 || return (nothing, nothing, nothing)
81✔
893
    begin_of_key = something(findnext(!isspace, str, nextind(str, end_of_identifier) + 1), # +1 for [
156✔
894
                             lastindex(str)+1)
895
    return (obj, str[begin_of_key:end], begin_of_key)
81✔
896
end
897

898
# This needs to be a separate non-inlined function, see #19441
899
@noinline function find_dict_matches(identifier::AbstractDict, partial_key)
80✔
900
    matches = String[]
80✔
901
    for key in keys(identifier)
122✔
902
        rkey = repr(key)
933✔
903
        startswith(rkey,partial_key) && push!(matches,rkey)
1,584✔
904
    end
1,448✔
905
    return matches
80✔
906
end
907

908
# Identify an argument being completed in a method call. If the argument is empty, method
909
# suggestions will be provided instead of argument completions.
910
function identify_possible_method_completion(partial, last_idx)
2,622✔
911
    fail = 0:-1, Expr(:nothing), 0:-1, 0
2,622✔
912

913
    # First, check that the last punctuation is either ',', ';' or '('
914
    idx_last_punct = something(findprev(x -> ispunct(x) && x != '_' && x != '!', partial, last_idx), 0)::Int
18,587✔
915
    idx_last_punct == 0 && return fail
2,622✔
916
    last_punct = partial[idx_last_punct]
4,258✔
917
    last_punct == ',' || last_punct == ';' || last_punct == '(' || return fail
4,048✔
918

919
    # Then, check that `last_punct` is only followed by an identifier or nothing
920
    before_last_word_start = something(findprev(in(non_identifier_chars), partial, last_idx), 0)
1,414✔
921
    before_last_word_start == 0 && return fail
707✔
922
    all(isspace, @view partial[nextind(partial, idx_last_punct):before_last_word_start]) || return fail
787✔
923

924
    # Check that `last_punct` is either the last '(' or placed after a previous '('
925
    frange, method_name_end = find_start_brace(@view partial[1:idx_last_punct])
627✔
926
    method_name_end ∈ frange || return fail
772✔
927

928
    # Strip the preceding ! operators, if any, and close the expression with a ')'
929
    s = replace(partial[frange], r"\G\!+([^=\(]+)" => s"\1"; count=1) * ')'
964✔
930
    ex = Meta.parse(s, raise=false, depwarn=false)
482✔
931
    isa(ex, Expr) || return fail
482✔
932

933
    # `wordrange` is the position of the last argument to complete
934
    wordrange = nextind(partial, before_last_word_start):last_idx
620✔
935
    return frange, ex, wordrange, method_name_end
482✔
936
end
937

938
# Provide completion for keyword arguments in function calls
939
function complete_keyword_argument(partial, last_idx, context_module)
1,659✔
940
    frange, ex, wordrange, = identify_possible_method_completion(partial, last_idx)
1,659✔
941
    fail = Completion[], 0:-1, frange
1,659✔
942
    ex.head === :call || is_broadcasting_expr(ex) || return fail
3,150✔
943

944
    kwargs_flag, funct, args_ex, kwargs_ex = _complete_methods(ex, context_module, true)::Tuple{Int, Any, Vector{Any}, Set{Symbol}}
171✔
945
    kwargs_flag == 2 && return fail # one of the previous kwargs is invalid
171✔
946

947
    methods = Completion[]
169✔
948
    complete_methods!(methods, funct, Any[Vararg{Any}], kwargs_ex, -1, kwargs_flag == 1)
169✔
949
    # TODO: use args_ex instead of Any[Vararg{Any}] and only provide kwarg completion for
950
    # method calls compatible with the current arguments.
951

952
    # For each method corresponding to the function call, provide completion suggestions
953
    # for each keyword that starts like the last word and that is not already used
954
    # previously in the expression. The corresponding suggestion is "kwname=".
955
    # If the keyword corresponds to an existing name, also include "kwname" as a suggestion
956
    # since the syntax "foo(; kwname)" is equivalent to "foo(; kwname=kwname)".
957
    last_word = partial[wordrange] # the word to complete
338✔
958
    kwargs = Set{String}()
169✔
959
    for m in methods
169✔
960
        m::MethodCompletion
1,457✔
961
        possible_kwargs = Base.kwarg_decl(m.method)
1,457✔
962
        current_kwarg_candidates = String[]
1,457✔
963
        for _kw in possible_kwargs
2,656✔
964
            kw = String(_kw)
472✔
965
            if !endswith(kw, "...") && startswith(kw, last_word) && _kw ∉ kwargs_ex
797✔
966
                push!(current_kwarg_candidates, kw)
67✔
967
            end
968
        end
730✔
969
        union!(kwargs, current_kwarg_candidates)
1,457✔
970
    end
1,626✔
971

972
    suggestions = Completion[KeywordArgumentCompletion(kwarg) for kwarg in kwargs]
216✔
973

974
    # Only add these if not in kwarg space. i.e. not in `foo(; `
975
    if kwargs_flag == 0
169✔
976
        append!(suggestions, complete_symbol(nothing, last_word, Returns(true), context_module))
138✔
977
        append!(suggestions, complete_keyval(last_word))
138✔
978
    end
979

980
    return sort!(suggestions, by=completion_text), wordrange
169✔
981
end
982

983
function project_deps_get_completion_candidates(pkgstarts::String, project_file::String)
1✔
984
    loading_candidates = String[]
1✔
985
    d = Base.parsed_toml(project_file)
1✔
986
    pkg = get(d, "name", nothing)::Union{String, Nothing}
2✔
987
    if pkg !== nothing && startswith(pkg, pkgstarts)
2✔
988
        push!(loading_candidates, pkg)
1✔
989
    end
990
    deps = get(d, "deps", nothing)::Union{Dict{String, Any}, Nothing}
2✔
991
    if deps !== nothing
1✔
992
        for (pkg, _) in deps
2✔
993
            startswith(pkg, pkgstarts) && push!(loading_candidates, pkg)
2✔
994
        end
1✔
995
    end
996
    return Completion[PackageCompletion(name) for name in loading_candidates]
1✔
997
end
998

999
function complete_identifiers!(suggestions::Vector{Completion}, @nospecialize(ffunc::Function), context_module::Module, string::String, name::String, pos::Int, dotpos::Int, startpos::Int, comp_keywords=false)
1,493✔
1000
    ex = nothing
3✔
1001
    if comp_keywords
1,493✔
1002
        append!(suggestions, complete_keyword(name))
544✔
1003
        append!(suggestions, complete_keyval(name))
544✔
1004
    end
1005
    if dotpos > 1 && string[dotpos] == '.'
2,622✔
1006
        s = string[1:prevind(string, dotpos)]
1,358✔
1007
        # First see if the whole string up to `pos` is a valid expression. If so, use it.
1008
        ex = Meta.parse(s, raise=false, depwarn=false)
679✔
1009
        if isexpr(ex, :incomplete)
725✔
1010
            s = string[startpos:pos]
1,090✔
1011
            # Heuristic to find the start of the expression. TODO: This would be better
1012
            # done with a proper error-recovering parser.
1013
            if 0 < startpos <= lastindex(string) && string[startpos] == '.'
1,090✔
1014
                i = prevind(string, startpos)
1✔
1015
                while 0 < i
1✔
1016
                    c = string[i]
2✔
1017
                    if c in (')', ']')
4✔
1018
                        if c == ')'
×
1019
                            c_start = '('
×
1020
                            c_end = ')'
×
1021
                        elseif c == ']'
×
1022
                            c_start = '['
×
1023
                            c_end = ']'
×
1024
                        end
1025
                        frange, end_of_identifier = find_start_brace(string[1:prevind(string, i)], c_start=c_start, c_end=c_end)
×
1026
                        isempty(frange) && break # unbalanced parens
×
1027
                        startpos = first(frange)
×
1028
                        i = prevind(string, startpos)
×
1029
                    elseif c in ('\'', '\"', '\`')
6✔
1030
                        s = "$c$c"*string[startpos:pos]
×
1031
                        break
×
1032
                    else
1033
                        break
×
1034
                    end
1035
                    s = string[startpos:pos]
×
1036
                end
×
1037
            end
1038
            if something(findlast(in(non_identifier_chars), s), 0) < something(findlast(isequal('.'), s), 0)
1,090✔
1039
                lookup_name, name = rsplit(s, ".", limit=2)
545✔
1040
                name = String(name)
545✔
1041

1042
                ex = Meta.parse(lookup_name, raise=false, depwarn=false)
545✔
1043
            end
1044
            isexpr(ex, :incomplete) && (ex = nothing)
873✔
1045
        elseif isexpr(ex, :call) && length(ex.args) > 1
180✔
1046
            isinfix = s[end] != ')'
37✔
1047
            # A complete call expression that does not finish with ')' is an infix call.
1048
            if !isinfix
19✔
1049
                # Handle infix call argument completion of the form bar + foo(qux).
1050
                frange, end_of_identifier = find_start_brace(@view s[1:prevind(s, end)])
14✔
1051
                isinfix = Meta.parse(@view(s[frange[1]:end]), raise=false, depwarn=false) == ex.args[end]
14✔
1052
            end
1053
            if isinfix
19✔
1054
                ex = ex.args[end]
8✔
1055
            end
1056
        elseif isexpr(ex, :macrocall) && length(ex.args) > 1
161✔
1057
            # allow symbol completions within potentially incomplete macrocalls
1058
            if s[end] ≠ '`' && s[end] ≠ ')'
43✔
1059
                ex = ex.args[end]
12✔
1060
            end
1061
        end
1062
    end
1063
    append!(suggestions, complete_symbol(ex, name, ffunc, context_module))
1,493✔
1064
    return sort!(unique(suggestions), by=completion_text), (dotpos+1):pos, true
1,493✔
1065
end
1066

1067
function completions(string::String, pos::Int, context_module::Module=Main, shift::Bool=true)
2,414✔
1068
    # First parse everything up to the current position
1069
    partial = string[1:pos]
4,463✔
1070
    inc_tag = Base.incomplete_tag(Meta.parse(partial, raise=false, depwarn=false))
2,050✔
1071

1072
    # ?(x, y)TAB lists methods you can call with these objects
1073
    # ?(x, y TAB lists methods that take these objects as the first two arguments
1074
    # MyModule.?(x, y)TAB restricts the search to names in MyModule
1075
    rexm = match(r"(\w+\.|)\?\((.*)$", partial)
2,050✔
1076
    if rexm !== nothing
2,050✔
1077
        # Get the module scope
1078
        if isempty(rexm.captures[1])
26✔
1079
            callee_module = context_module
×
1080
        else
1081
            modname = Symbol(rexm.captures[1][1:end-1])
13✔
1082
            if isdefined(context_module, modname)
13✔
1083
                callee_module = getfield(context_module, modname)
13✔
1084
                if !isa(callee_module, Module)
13✔
1085
                    callee_module = context_module
13✔
1086
                end
1087
            else
1088
                callee_module = context_module
×
1089
            end
1090
        end
1091
        moreargs = !endswith(rexm.captures[2], ')')
14✔
1092
        callstr = "_(" * rexm.captures[2]
13✔
1093
        if moreargs
13✔
1094
            callstr *= ')'
7✔
1095
        end
1096
        ex_org = Meta.parse(callstr, raise=false, depwarn=false)
13✔
1097
        if isa(ex_org, Expr)
13✔
1098
            return complete_any_methods(ex_org, callee_module::Module, context_module, moreargs, shift), (0:length(rexm.captures[1])+1) .+ rexm.offset, false
13✔
1099
        end
1100
    end
1101

1102
    # if completing a key in a Dict
1103
    identifier, partial_key, loc = dict_identifier_key(partial, inc_tag, context_module)
2,117✔
1104
    if identifier !== nothing
2,037✔
1105
        matches = find_dict_matches(identifier, partial_key)
80✔
1106
        length(matches)==1 && (lastindex(string) <= pos || string[nextind(string,pos)] != ']') && (matches[1]*=']')
84✔
1107
        length(matches)>0 && return Completion[DictCompletion(identifier, match) for match in sort!(matches)], loc::Int:pos, true
80✔
1108
    end
1109

1110
    ffunc = Returns(true)
×
1111
    suggestions = Completion[]
1,973✔
1112

1113
    # Check if this is a var"" string macro that should be completed like
1114
    # an identifier rather than a string.
1115
    # TODO: It would be nice for the parser to give us more information here
1116
    # so that we can lookup the macro by identity rather than pattern matching
1117
    # its invocation.
1118
    varrange = findprev("var\"", string, pos)
1,973✔
1119

1120
    if varrange !== nothing
1,973✔
1121
        ok, ret = bslash_completions(string, pos)
3✔
1122
        ok && return ret
3✔
1123
        startpos = first(varrange) + 4
3✔
1124
        dotpos = something(findprev(isequal('.'), string, first(varrange)-1), 0)
5✔
1125
        return complete_identifiers!(Completion[], ffunc, context_module, string,
3✔
1126
            string[startpos:pos], pos, dotpos, startpos)
1127
    elseif inc_tag === :cmd
1,970✔
1128
        m = match(r"[\t\n\r\"`><=*?|]| (?!\\)", reverse(partial))
1✔
1129
        startpos = nextind(partial, reverseind(partial, m.offset))
2✔
1130
        r = startpos:pos
1✔
1131

1132
        # This expansion with "\\ "=>' ' replacement and shell_escape=true
1133
        # assumes the path isn't further quoted within the cmd backticks.
1134
        expanded = complete_expanduser(replace(string[r], r"\\ " => " "), r)
2✔
1135
        expanded[3] && return expanded  # If user expansion available, return it
1✔
1136

1137
        paths, r, success = complete_path(replace(string[r], r"\\ " => " "), pos,
2✔
1138
                                          shell_escape=true)
1139

1140
        return sort!(paths, by=p->p.path), r, success
1✔
1141
    elseif inc_tag === :string
1,969✔
1142
        # Find first non-escaped quote
1143
        m = match(r"\"(?!\\)", reverse(partial))
137✔
1144
        startpos = nextind(partial, reverseind(partial, m.offset))
274✔
1145
        r = startpos:pos
159✔
1146

1147
        expanded = complete_expanduser(string[r], r)
252✔
1148
        expanded[3] && return expanded  # If user expansion available, return it
137✔
1149

1150
        path_prefix = try
135✔
1151
            unescape_string(replace(string[r], "\\\$"=>"\$", "\\\""=>"\""))
248✔
1152
        catch
1153
            nothing
135✔
1154
        end
1155
        if !isnothing(path_prefix)
270✔
1156
            paths, r, success = complete_path(path_prefix, pos, string_escape=true)
135✔
1157

1158
            if close_path_completion(string, startpos, r, paths, pos)
135✔
1159
                paths[1] = PathCompletion((paths[1]::PathCompletion).path * "\"")
5✔
1160
            end
1161

1162
            # Fallthrough allowed so that Latex symbols can be completed in strings
1163
            success && return sort!(paths, by=p->p.path), r, success
50,871✔
1164
        end
1165
    end
1166

1167
    ok, ret = bslash_completions(string, pos)
1,897✔
1168
    ok && return ret
1,897✔
1169

1170
    # Make sure that only bslash_completions is working on strings
1171
    inc_tag === :string && return Completion[], 0:-1, false
1,854✔
1172
    if inc_tag === :other
1,798✔
1173
        frange, ex, wordrange, method_name_end = identify_possible_method_completion(partial, pos)
963✔
1174
        if last(frange) != -1 && all(isspace, @view partial[wordrange]) # no last argument to complete
963✔
1175
            if ex.head === :call
138✔
1176
                return complete_methods(ex, context_module, shift), first(frange):method_name_end, false
135✔
1177
            elseif is_broadcasting_expr(ex)
3✔
1178
                return complete_methods(ex, context_module, shift), first(frange):(method_name_end - 1), false
3✔
1179
            end
1180
        end
1181
    elseif inc_tag === :comment
835✔
1182
        return Completion[], 0:-1, false
1✔
1183
    end
1184

1185
    # Check whether we can complete a keyword argument in a function call
1186
    kwarg_completion, wordrange = complete_keyword_argument(partial, pos, context_module)
3,149✔
1187
    isempty(wordrange) || return kwarg_completion, wordrange, !isempty(kwarg_completion)
1,828✔
1188

1189
    dotpos = something(findprev(isequal('.'), string, pos), 0)
2,292✔
1190
    startpos = nextind(string, something(findprev(in(non_identifier_chars), string, pos), 0))
2,659✔
1191
    # strip preceding ! operator
1192
    if (m = match(r"\G\!+", partial, startpos)) isa RegexMatch
1,490✔
1193
        startpos += length(m.match)
2✔
1194
    end
1195

1196
    name = string[max(startpos, dotpos+1):pos]
2,596✔
1197
    comp_keywords = !isempty(name) && startpos > dotpos
1,490✔
1198
    if afterusing(string, startpos)
1,490✔
1199
        # We're right after using or import. Let's look only for packages
1200
        # and modules we can reach from here
1201

1202
        # If there's no dot, we're in toplevel, so we should
1203
        # also search for packages
1204
        s = string[startpos:pos]
33✔
1205
        if dotpos <= startpos
18✔
1206
            for dir in Base.load_path()
17✔
1207
                if basename(dir) in Base.project_names && isfile(dir)
93✔
1208
                    append!(suggestions, project_deps_get_completion_candidates(s, dir))
1✔
1209
                end
1210
                isdir(dir) || continue
37✔
1211
                for pname in readdir(dir)
18✔
1212
                    if pname[1] != '.' && pname != "METADATA" &&
2,008✔
1213
                        pname != "REQUIRE" && startswith(pname, s)
1214
                        # Valid file paths are
1215
                        #   <Mod>.jl
1216
                        #   <Mod>/src/<Mod>.jl
1217
                        #   <Mod>.jl/src/<Mod>.jl
1218
                        if isfile(joinpath(dir, pname))
197✔
1219
                            endswith(pname, ".jl") && push!(suggestions,
×
1220
                                                            PackageCompletion(pname[1:prevind(pname, end-2)]))
1221
                        else
1222
                            mod_name = if endswith(pname, ".jl")
197✔
1223
                                pname[1:prevind(pname, end-2)]
×
1224
                            else
1225
                                pname
197✔
1226
                            end
1227
                            if isfile(joinpath(dir, pname, "src",
197✔
1228
                                               "$mod_name.jl"))
1229
                                push!(suggestions, PackageCompletion(mod_name))
196✔
1230
                            end
1231
                        end
1232
                    end
1233
                end
1,040✔
1234
            end
54✔
1235
        end
1236
        ffunc = (mod,x)->(Base.isbindingresolved(mod, x) && isdefined(mod, x) && isa(getfield(mod, x), Module))
20,312✔
1237
        comp_keywords = false
×
1238
    end
1239

1240
    startpos == 0 && (pos = -1)
1,490✔
1241
    dotpos < startpos && (dotpos = startpos - 1)
1,490✔
1242
    return complete_identifiers!(suggestions, ffunc, context_module, string,
1,490✔
1243
        name, pos, dotpos, startpos, comp_keywords)
1244
end
1245

1246
function shell_completions(string, pos)
452✔
1247
    # First parse everything up to the current position
1248
    scs = string[1:pos]
904✔
1249
    local args, last_parse
×
1250
    try
452✔
1251
        args, last_parse = Base.shell_parse(scs, true)::Tuple{Expr,UnitRange{Int}}
469✔
1252
    catch
1253
        return Completion[], 0:-1, false
17✔
1254
    end
1255
    ex = args.args[end]::Expr
435✔
1256
    # Now look at the last thing we parsed
1257
    isempty(ex.args) && return Completion[], 0:-1, false
435✔
1258
    arg = ex.args[end]
435✔
1259
    if all(s -> isa(s, AbstractString), ex.args)
1,307✔
1260
        arg = arg::AbstractString
432✔
1261
        # Treat this as a path
1262

1263
        # As Base.shell_parse throws away trailing spaces (unless they are escaped),
1264
        # we need to special case here.
1265
        # If the last char was a space, but shell_parse ignored it search on "".
1266
        ignore_last_word = arg != " " && scs[end] == ' '
863✔
1267
        prefix = ignore_last_word ? "" : join(ex.args)
847✔
1268

1269
        # Also try looking into the env path if the user wants to complete the first argument
1270
        use_envpath = !ignore_last_word && length(args.args) < 2
432✔
1271

1272
        return complete_path(prefix, pos, use_envpath=use_envpath, shell_escape=true)
432✔
1273
    elseif isexpr(arg, :incomplete) || isexpr(arg, :error)
5✔
1274
        partial = scs[last_parse]
4✔
1275
        ret, range = completions(partial, lastindex(partial))
2✔
1276
        range = range .+ (first(last_parse) - 1)
2✔
1277
        return ret, range, true
2✔
1278
    end
1279
    return Completion[], 0:-1, false
1✔
1280
end
1281

1282
function UndefVarError_hint(io::IO, ex::UndefVarError)
3✔
1283
    var = ex.var
3✔
1284
    if var === :or
3✔
1285
        print(io, "\nsuggestion: Use `||` for short-circuiting boolean OR.")
×
1286
    elseif var === :and
3✔
1287
        print(io, "\nsuggestion: Use `&&` for short-circuiting boolean AND.")
×
1288
    elseif var === :help
3✔
1289
        println(io)
×
1290
        # Show friendly help message when user types help or help() and help is undefined
1291
        show(io, MIME("text/plain"), Base.Docs.parsedoc(Base.Docs.keywords[:help]))
×
1292
    elseif var === :quit
3✔
1293
        print(io, "\nsuggestion: To exit Julia, use Ctrl-D, or type exit() and press enter.")
×
1294
    end
1295
end
1296

1297
function __init__()
11✔
1298
    Base.Experimental.register_error_hint(UndefVarError_hint, UndefVarError)
11✔
1299
    COMPLETION_WORLD[] = Base.get_world_counter()
11✔
1300
    nothing
11✔
1301
end
1302

1303
end # module
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc