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

JuliaLang / julia / 1391

29 Dec 2025 07:41PM UTC coverage: 76.638% (+0.02%) from 76.621%
1391

push

buildkite

web-flow
🤖 Bump StyledStrings stdlib 9bb8ffd → a033d46 (#60503)

Co-authored-by: KristofferC <1282691+KristofferC@users.noreply.github.com>

62409 of 81433 relevant lines covered (76.64%)

22974996.93 hits per line

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

94.44
/stdlib/InteractiveUtils/src/macros.jl
1
# This file is a part of Julia. License is MIT: https://julialang.org/license
2

3
# macro wrappers for various reflection functions
4

5
using Base: insert!, replace_ref_begin_end!,
6
    infer_return_type, infer_exception_type, infer_effects, code_ircode, isexpr
7

8
# defined in Base so it's possible to time all imports, including InteractiveUtils and its deps
9
# via. `Base.@time_imports` etc.
10
import Base: @time_imports, @trace_compile, @trace_dispatch
11

12
typesof_expr(args::Vector{Any}, where_params::Union{Nothing, Vector{Any}} = nothing) = rewrap_where(:($make_tuple_type(Any[$(Any[esc(reescape(get_typeof, a)) for a in args]...)])), where_params)
1,013✔
13
typesof_expr_unescaped(args::Vector{Any}, where_params::Union{Nothing, Vector{Any}} = nothing) = rewrap_where(:($make_tuple_type(Any[$(Any[reescape(get_typeof, a) for a in args]...)])), where_params)
255✔
14

15
function make_tuple_type(types::Vector{Any})
1,730✔
16
    vararg = -1
1,730✔
17
    for i in eachindex(types)
1,730✔
18
        i == 1 && continue # ignore function type
5,999✔
19
        type = types[i]
4,275✔
20
        if isa(type, Core.TypeofVararg)
4,275✔
21
            vararg !== -1 && throw(ArgumentError("More than one `Core.Vararg` type present in argument tuple ($type detected after $(types[vararg])); if provided, it must be unique"))
78✔
22
            vararg = i
75✔
23
            if isdefined(type, :N)
75✔
24
                n = length(types) - vararg + 1
12✔
25
                n > type.N && throw(ArgumentError("Expected at most $(type.N) types after `$type`, found $n instead"))
12✔
26
            end
27
        elseif vararg !== -1
4,197✔
28
            ref = types[vararg]
51✔
29
            if isdefined(ref, :T) && !skip_type_check(ref.T) && !skip_type_check(type)
51✔
30
                !(type <: ref.T) && throw(ArgumentError("Inconsistent type `$type` detected after `$ref`; `$type <: $(ref.T)` must hold"))
30✔
31
            end
32
        end
33
    end
10,256✔
34
    vararg === -1 && return Tuple{types...}
1,712✔
35
    return Tuple{@view(types[1:vararg])...}
57✔
36
end
37

38
skip_type_check(@nospecialize(T)) = Core.has_free_typevars(T)
81✔
39

40
function extract_where_parameters(ex::Expr)
41
    isexpr(ex, :where) || return ex, nothing
2,023✔
42
    ex.args[1], ex.args[2:end]
66✔
43
end
44

45
function rewrap_where(ex::Expr, where_params::Union{Nothing, Vector{Any}})
54✔
46
    isnothing(where_params) && return ex
1,268✔
47
    Expr(:where, ex, esc.(where_params)...)
54✔
48
end
49

50
function reescape(f::Function, @nospecialize ex)
4,475✔
51
    isa(ex, Expr) || return f(ex)
6,828✔
52
    unescaped = Meta.unescape(ex)
2,122✔
53
    new = f(unescaped)
2,122✔
54
    return Meta.reescape(new, ex)
2,122✔
55
end
56

57
get_typeof(ex::Ref) = ex[]
533✔
58
function get_typeof(@nospecialize ex)
3,484✔
59
    isexpr(ex, :(::), 1) && return ex.args[1]
3,484✔
60
    isexpr(ex, :(::), 2) && return ex.args[2]
3,085✔
61
    if isexpr(ex, :..., 1)
2,977✔
62
        splatted = ex.args[1]
458✔
63
        isexpr(splatted, :(::)) && return Expr(:curly, :(Core.Vararg), splatted.args[end])
458✔
64
        return :(Any[Core.Typeof(x) for x in $splatted]...)
428✔
65
    end
66
    return :(Core.Typeof($ex))
2,519✔
67
end
68

69
function is_broadcasting_call(ex)
2,239✔
70
    isa(ex, Expr) || return false
2,284✔
71
    # Standard broadcasting: f.(x)
72
    isexpr(ex, :.) && length(ex.args) ≥ 2 && isexpr(ex.args[2], :tuple) && return true
2,239✔
73
    # Infix broadcasting: x .+ y, x .<< y, etc.
74
    if isexpr(ex, :call)
2,164✔
75
        f = ex.args[1]
1,642✔
76
        f == :.. && return false
3,284✔
77
        string(f)[1] == '.' && return true
1,636✔
78
    end
79
    return false
2,083✔
80
end
81
is_broadcasting_expr(ex) = is_broadcasting_call(ex) || is_broadcasting_assignment(ex)
4,373✔
82
function is_broadcasting_assignment(ex)
2,200✔
83
    isa(ex, Expr) || return false
2,245✔
84
    isexpr(ex, :.) && return false
2,200✔
85
    head = string(ex.head)
2,122✔
86
    # x .= y, x .+= y, x .<<= y, etc.
87
    head[begin] == '.' && head[end] == '=' && return true
2,206✔
88
    return false
2,044✔
89
end
90

91
"""
92
Transform a dot expression into one where each argument has been replaced by a
93
variable "xj" (with j an integer from 1 to the returned i).
94
The list `args` contains the original arguments that have been replaced.
95
"""
96
function recursive_dotcalls!(ex, args, i=1)
324✔
97
    if is_broadcasting_expr(ex)
597✔
98
        if is_broadcasting_assignment(ex)
111✔
99
            (start, branches) = (1, ex.args)
27✔
100
        elseif isexpr(ex, :.)
87✔
101
            (start, branches) = (1, ex.args[2].args)
42✔
102
        else
103
            (start, branches) = (2, ex.args)
45✔
104
        end
105
        for j in start:length(branches)::Int
111✔
106
            branch, i = recursive_dotcalls!(branches[j], args, i)
222✔
107
            branches[j] = branch
222✔
108
        end
333✔
109
        return ex, i
111✔
110
    elseif isexpr(ex, :parameters)
213✔
111
        for j in eachindex(ex.args)
21✔
112
            param, i = recursive_dotcalls!(ex.args[j], args, i)
21✔
113
            ex.args[j] = param
21✔
114
        end
21✔
115
        return ex, i
21✔
116
    end
117
    newarg = Symbol('x', i)
192✔
118
    if isexpr(ex, :...)
192✔
119
        newarg = Expr(:..., newarg)
6✔
120
        push!(args, only(ex.args))
6✔
121
    elseif isexpr(ex, :kw)
186✔
122
        newarg = Expr(:kw, ex.args[1], newarg)
42✔
123
        push!(args, ex.args[end])
42✔
124
    else
125
        push!(args, ex)
144✔
126
    end
127
    return newarg, i+1
192✔
128
end
129

130
function extract_farg(@nospecialize arg)
458✔
131
    !isexpr(arg, :(::), 1) && return arg
458✔
132
    fT = arg.args[1]
×
133
    :($construct_callable($fT))
×
134
end
135

136
function construct_callable(@nospecialize(func::Type))
×
137
    # Support function singleton types such as `(::typeof(f))(args...)`
138
    Base.issingletontype(func) && isdefined(func, :instance) && return func.instance
×
139
    # Don't support type annotations otherwise, we don't want to give wrong answers
140
    # for callables such as `(::Returns{Int})(args...)` where using `Returns{Int}`
141
    # would give us code for the constructor, not for the callable object.
142
    throw(ArgumentError("If the function type is explicitly provided via a type annotation, it must be a singleton whose only instance is the callable object.
×
143
                         To remove this restriction, the reflection macro must set `use_signature_tuple = true` if the reflection function supports a single signature tuple type argument, such as `Tuple{typeof(f), argtypes...}`"))
144
end
145

146
function separate_kwargs(exs::Vector{Any})
476✔
147
    args = []
476✔
148
    kwargs = []
476✔
149
    for ex in exs
476✔
150
        if isexpr(ex, :kw)
1,431✔
151
            push!(kwargs, ex)
12✔
152
        elseif isexpr(ex, :parameters)
1,419✔
153
            for kw in ex.args
464✔
154
                push!(kwargs, kw)
485✔
155
            end
485✔
156
        else
157
            push!(args, ex)
955✔
158
        end
159
    end
1,431✔
160
    args, kwargs
476✔
161
end
162

163
function are_kwargs_valid(kwargs::Vector{Any})
164
    for kwarg in kwargs
476✔
165
        isexpr(kwarg, :..., 1) && continue
497✔
166
        isexpr(kwarg, :kw, 2) && isa(kwarg.args[1], Symbol) && continue
57✔
167
        isexpr(kwarg, :(::), 2) && continue
12✔
168
        isa(kwarg, Symbol) && continue
6✔
169
        isexpr(kwarg, :escape) && continue
3✔
170
        isexpr(kwarg, :var"hygienic-scope") && continue
3✔
171
        return false
3✔
172
    end
494✔
173
    return true
473✔
174
end
175

176
# Generate an expression that merges `kwargs` onto a single `NamedTuple`
177
function generate_merged_namedtuple_type(kwargs::Vector{Any})
473✔
178
    nts = Any[]
473✔
179
    ntargs = Pair{Symbol, Any}[]
473✔
180
    for ex in kwargs
473✔
181
        if isexpr(ex, :..., 1)
494✔
182
            if !isempty(ntargs)
440✔
183
                # Construct a `NamedTuple` containing the previous parameters.
184
                push!(nts, generate_namedtuple_type(ntargs))
12✔
185
                empty!(ntargs)
12✔
186
            end
187
            push!(nts, Expr(:call, typeof_nt, ex.args[1]))
440✔
188
        elseif isexpr(ex, :kw, 2)
54✔
189
            push!(ntargs, ex.args[1]::Symbol => reescape(get_typeof, ex.args[2]))
45✔
190
        elseif isexpr(ex, :(::), 2)
9✔
191
            push!(ntargs, ex.args[1]::Symbol => reescape(get_typeof, ex))
6✔
192
        else
193
            push!(ntargs, ex => reescape(get_typeof, ex))
3✔
194
        end
195
    end
494✔
196
    !isempty(ntargs) && push!(nts, generate_namedtuple_type(ntargs))
473✔
197
    return :($merge_namedtuple_types($(nts...)))
473✔
198
end
199

200
function generate_namedtuple_type(ntargs::Vector{Pair{Symbol, Any}})
54✔
201
    names = Expr(:tuple)
54✔
202
    tt = Expr(:curly, :Tuple)
54✔
203
    for (name, type) in ntargs
54✔
204
        push!(names.args, QuoteNode(name))
54✔
205
        push!(tt.args, type)
54✔
206
    end
54✔
207
    return :(NamedTuple{$names, $tt})
54✔
208
end
209

210
typeof_nt(nt::NamedTuple) = typeof(nt)
24✔
211
typeof_nt(nt::Base.Pairs) = typeof(values(nt))
1,157✔
212

213
function merge_namedtuple_types(nt::Type{<:NamedTuple}, nts::Type{<:NamedTuple}...)
1,153✔
214
    @nospecialize
1,214✔
215
    isempty(nts) && return nt
1,214✔
216
    names = Symbol[]
18✔
217
    types = Any[]
18✔
218
    for nt in (nt, nts...)
18✔
219
        for (name, type) in zip(fieldnames(nt), fieldtypes(nt))
39✔
220
            i = findfirst(==(name), names)
63✔
221
            if isnothing(i)
51✔
222
                push!(names, name)
27✔
223
                push!(types, type)
27✔
224
            else
225
                types[i] = type
12✔
226
            end
227
        end
39✔
228
    end
57✔
229
    return NamedTuple{Tuple(names), Tuple{types...}}
18✔
230
end
231

232
function gen_call(fcn, args, where_params, kws; use_signature_tuple::Bool, not_an_opaque_closure::Bool = true)
2,026✔
233
    f, args... = args
1,990✔
234
    args = collect(Any, args)
1,990✔
235
    if !use_signature_tuple
1,013✔
236
        f = esc(reescape(extract_farg, f))
458✔
237
        tt = typesof_expr(args, where_params)
458✔
238
        return :($fcn($f, $tt; $(kws...)))
458✔
239
    end
240
    # We use a signature tuple only if we are sure we won't get an opaque closure as first argument.
241
    # If we do get one, we have to use the 2-argument form.
242
    if isexpr(f, :(::)) || not_an_opaque_closure
1,101✔
243
        # We have a type, not a value, so not an opaque closure.
244
        sigt = typesof_expr(Any[f, args...], where_params)
300✔
245
        return :($fcn($sigt; $(kws...)))
300✔
246
    end
247
    tt = typesof_expr(args, where_params)
255✔
248
    sigt = typesof_expr_unescaped(Any[:f, esc.(args)...], where_params)
255✔
249
    return quote
255✔
250
        f = $(esc(f))
6✔
251
        if isa(f, Core.OpaqueClosure)
6✔
252
            $fcn(f, $tt; $(kws...))
253
        else
254
            $fcn($sigt; $(kws...))
12✔
255
        end
256
    end
257
end
258

259
function expand_ref_begin_end!(f::Function, ex, __module__::Module)
57✔
260
    arr = ex.args[1]
57✔
261
    args = copy(ex.args)
114✔
262
    new = replace_ref_begin_end!(__module__, ex)
57✔
263
    modified = ex.args .≠ args
114✔
264
    if any(modified) && (isexpr(arr, :(::), 1) || isexpr(arr, :(::), 2) || isexpr(arr, :..., 1))
81✔
265
        return Expr(:call, :error, "`begin` or `end` cannot be used with a type-annotated left-hand side argument for an indexing syntax")
6✔
266
    end
267
    call = f(ex)
51✔
268
    !any(modified) && return call
102✔
269
    fixup_hygiene_for_ref_temporary!(new)
24✔
270
    # We have to mutate `ex`, then return `new` which evaluates `arr` before use.
271
    ex.head = call.head
24✔
272
    ex.args = call.args
24✔
273
    return new
24✔
274
end
275

276
function fixup_hygiene_for_ref_temporary!(ex)
24✔
277
    # Match the local variable `##S#...` so we may escape its definition.
278
    # We don't want to use `escs = 1` in `replace_ref_begin_end_!` because
279
    # then we delegate escaping to this function, whereas we otherwise manage
280
    # ourselves the escaping in all other code paths.
281
    isexpr(ex, :block) || return
24✔
282
    decl = ex.args[1]
24✔
283
    isexpr(decl, :local, 1) || return
24✔
284
    assignment = decl.args[1]
24✔
285
    isexpr(assignment, :(=), 2) || return
24✔
286
    variable = assignment.args[1]
24✔
287
    startswith(string(variable), "##S#") || return
24✔
288
    decl.args[1] = esc(assignment)
24✔
289
end
290

291
is_code_macro(fcn) = startswith(string(fcn), "code_")
21✔
292

293
"""
294
    gen_call_with_extracted_types(__module__, fcn, ex, kws = Expr[]; is_source_reflection = !is_code_macro(fcn), supports_binding_reflection = false, use_signature_tuple = false)
295

296
Destructures the input expression `ex` into a function call or a binding access, then generates a call to either:
297
- `fcn(f, tt; kws...)`
298
- `fcn(sigt; kws...)` # if `use_signature_tuple = true`
299
- `fcn(mod, name; kws...)` # if `supports_binding_reflection = true`
300

301
## `fcn` API requirements
302

303
`fcn` is a user function expected to satisfy the following API:
304
- `fcn(f, tt)`: `f` is a value (such as `sum`, unlike `typeof(sum)`), and `tt := Tuple{argtypes...}`
305
  is a `Tuple` holding argument types. `f` may be a `Core.OpaqueClosure`.
306

307
If `use_signature_tuple = true`:
308
- `fcn(sigt)`: `sigt := Tuple{typeof(f), argtypes...}` represents the low-level signature tuple to be used for introspection.
309

310
If `supports_binding_reflection = true`:
311
- `fcn(mod::Module, name::Symbol)`: `name` is the name of a binding that may or may not exist in `mod`.
312

313
!!! warning
314
    This function is not public and may be subject to breaking changes. However, we recognize that it may
315
    be very convenient for macro developers, and as it is already used by a certain number of packages,
316
    we will do our best to avoid breakages.
317

318
## Examples
319

320
Here are a few usage patterns that may help you get started.
321

322
For most "code" macros (`@code_typed`, `@code_llvm`, `@code_native` etc):
323
```julia
324
    gen_call_with_extracted_types(__module__, fcn, ex, kws; is_source_reflection = false, use_signature_tuple = true #= may be false =#)
325
```
326

327
For source reflection macros (`@which`, `@edit`, `@less` etc):
328
```julia
329
    gen_call_with_extracted_types(__module__, fcn, ex, kws; is_source_reflection = true, use_signature_tuple = true #= may be false =#)
330
```
331

332
# Extended help
333

334
## Type annotations
335

336
Type annotations may be used instead of concrete values for the callable or for any of the arguments. The generated code
337
will directly use the right-hand side of the type annotation instead of extracting the type of a value at runtime.
338

339
This is particularly useful for callable objects (notably, for those that are hard to construct by hand on the spot),
340
or when wanting to provide a type that is not concrete. However, support for callable objects requires setting
341
`use_signature_tuple` to true, which is not a default (see the corresponding section below).
342

343
Constraints on type parameters are also supported with a `where` syntax, enabling these patterns:
344
- `f(x::Vector{T}, y::T) where {T}`
345
- `(::Returns{T})() where {T<:Real}`
346
- `(::MyPolynomial{N,T})(::T, ::AbstractArray{T,N}) where {N,T}`
347

348
Type-annotated expressions may be mixed with runtime values, as in `x + ::Float64`.
349

350
## Broadcasting
351

352
When `ex` is a broadcasting expression (a broadcasted assignment `a .+= b` or a broadcasted function call `a .+ b`),
353
there is no actual function that corresponds to this expression because lowering maps it to more than one call.
354

355
If `is_source_reflection` is true, we assume that `fcn` uses provenance information (e.g. used by `@edit` to go
356
to a source location, or `@which` to get the method matching the input). In this case, we don't have a clear
357
semantic source to give (shall it be `broadcasted`, or `materialize`, or something else?), so we return a throwing
358
expression.
359

360
However, if provenance is not of interest, we define an intermediate function on the spot that performs the broadcast,
361
then carry on using this function. For example, for the input expression `a .+ b`, we emit the anonymous function
362
`(a, b) -> a .+ b` then call `fcn` just as if the user had issued a call to this anonymous function. That should be the
363
desired behavior for most macros that want to map an expression to the corresponding generated code, as in `@code_typed`
364
or `@code_llvm` for instance.
365

366
## Binding reflection
367

368
Expressions of the form `a.b` (or `a.b.c` and so on) are by default interpreted as calls to `getproperty`.
369
However, if the value corresponding to the left-hand side (`a`, `a.b`, etc) is a module, some implementations
370
may instead be interested in the binding lookup, instead of the function call. If that is the case,
371
`supports_binding_reflection` may be set to `true` which will emit a call to `fcn(a, :b)` (or `fcn(a.b, :c)` etc).
372

373
## Tuple signature type
374

375
If `use_signature_tuple = true`, then a single tuple consisting of `Tuple{ft, argtypes...}` will be formed
376
and provided to `fcn`. `fcn` is then expected to use `ft` as the callable type with no further transformation.
377

378
This behavior is required to enable support type-annotated callable objects.
379

380
To understand this requirement, we'll use `code_typed` as an example. `code_typed(f, ())` interprets its input as the signature
381
`Tuple{typeof(f)}`, and `code_typed(Returns{Int}, ())` interprets that as the signature `Tuple{Type{Returns{Int}}}`, corresponding
382
to the type constructor.
383
To remove the ambiguity, `code_typed` must support an implementation that directly accepts a function type. This implementation
384
is assumed to be the method for `fcn(sigt::Type{<:Tuple})`.
385
"""
386
function gen_call_with_extracted_types(__module__, fcn, ex0, kws = Expr[]; is_source_reflection = !is_code_macro(fcn), supports_binding_reflection = false, use_signature_tuple = false)
2,520✔
387
    # Ignore assignments (e.g. `@edit a = f(x)` gets turned into `@edit f(x)`)
388
    if isa(ex0, Expr) && ex0.head === :(=) && isa(ex0.args[1], Symbol)
1,052✔
389
        return gen_call_with_extracted_types(__module__, fcn, ex0.args[2], kws; is_source_reflection, supports_binding_reflection, use_signature_tuple)
12✔
390
    end
391
    _where_params = nothing
1,040✔
392
    if isa(ex0, Expr)
1,040✔
393
        ex0, _where_params = extract_where_parameters(ex0)
2,023✔
394
    end
395
    where_params = _where_params
1,040✔
396
    if isa(ex0, Expr)
1,040✔
397
        if ex0.head === :do && isexpr(get(ex0.args, 1, nothing), :call)
1,028✔
398
            # Normalize `f(args...) do ... end` calls to `f(do_anonymous_function, args...)`
399
            if length(ex0.args) != 2
6✔
400
                return Expr(:call, :error, "ill-formed do call")
×
401
            end
402
            i = findlast(@nospecialize(a)->(isexpr(a, :kw) || isexpr(a, :parameters)), ex0.args[1].args)
30✔
403
            args = copy(ex0.args[1].args)
6✔
404
            insert!(args, (isnothing(i) ? 2 : 1+i::Int), ex0.args[2])
6✔
405
            ex0 = Expr(:call, args...)
6✔
406
        end
407
        if is_broadcasting_expr(ex0) && !is_source_reflection
1,996✔
408
            # Manually wrap top-level broadcasts in a function.
409
            # We don't do that if `fcn` reflects into the source,
410
            # because that destroys provenance information.
411
            args = Any[]
81✔
412
            ex, i = recursive_dotcalls!(copy(ex0), args)
81✔
413
            xargs = [Symbol('x', j) for j in 1:i-1]
81✔
414
            dotfuncname = gensym("dotfunction")
81✔
415
            call = gen_call(fcn, Any[dotfuncname, args...], where_params, kws; use_signature_tuple)
81✔
416
            return quote
81✔
417
                let $(esc(:($dotfuncname($(xargs...)) = $ex)))
418
                    $call
419
                end
420
            end
421
        elseif isexpr(ex0, :.) && is_source_reflection
947✔
422
            # If `ex0` has the form A.B (or some chain A.B.C.D) and `fcn` reflects into the source,
423
            # `A` (or `A.B.C`) may be a module, in which case `fcn` is probably more interested in
424
            # the binding rather than the `getproperty` call.
425
            # If binding reflection is not supported, we generate an error; `getproperty(::Module, field)`
426
            # is not going to be interesting to reflect into, so best to allow future non-breaking support
427
            # for binding reflection in case the macro may eventually support that.
428
            fully_qualified_symbol = true
15✔
429
            ex1 = ex0
15✔
430
            while ex1 isa Expr && ex1.head === :.
36✔
431
                fully_qualified_symbol = (length(ex1.args) == 2 &&
21✔
432
                                            ex1.args[2] isa QuoteNode &&
433
                                            ex1.args[2].value isa Symbol)
434
                fully_qualified_symbol || break
21✔
435
                ex1 = ex1.args[1]
21✔
436
            end
21✔
437
            fully_qualified_symbol &= ex1 isa Symbol
15✔
438
            if fully_qualified_symbol || isexpr(ex1, :(::), 1)
18✔
439
                call_reflection = gen_call(fcn, [getproperty; ex0.args], where_params, kws; use_signature_tuple)
15✔
440
                isexpr(ex0.args[1], :(::), 1) && return call_reflection
15✔
441
                if supports_binding_reflection
12✔
442
                    binding_reflection = :($fcn(arg1, $(ex0.args[2]); $(kws...)))
6✔
443
                else
444
                    binding_reflection = :(error("expression is not a function call"))
6✔
445
                end
446
                return quote
12✔
447
                    local arg1 = $(esc(ex0.args[1]))
448
                    if isa(arg1, Module)
449
                        $binding_reflection
450
                    else
451
                        $call_reflection
452
                    end
453
                end
454
            end
455
        end
456
        if is_broadcasting_expr(ex0)
1,861✔
457
            return Expr(:call, :error, "dot expressions are not lowered to "
6✔
458
                * "a single function call, so @$fcn cannot analyze "
459
                * "them. You may want to use Meta.@lower to identify "
460
                * "which function call to target.")
461
        end
462
        if any(@nospecialize(a)->(isexpr(a, :kw) || isexpr(a, :parameters)), ex0.args)
7,290✔
463
            args, kwargs = separate_kwargs(ex0.args)
476✔
464
            are_kwargs_valid(kwargs) || return quote
479✔
465
                error("keyword argument format unrecognized; they must be of the form `x` or `x = <value>`")
466
                $(esc(ex0)) # trigger syntax errors if any
467
            end
468
            nt = generate_merged_namedtuple_type(kwargs)
473✔
469
            nt = Ref(nt) # ignore `get_typeof` handling
473✔
470
            return gen_call(fcn, Any[Core.kwcall, nt, args...], where_params, kws; use_signature_tuple)
473✔
471
        elseif ex0.head === :call
450✔
472
            args = copy(ex0.args)
612✔
473
            if ex0.args[1] === :^ && length(ex0.args) >= 3 && isa(ex0.args[3], Int)
306✔
474
                pushfirst!(args, Base.literal_pow)
102✔
475
                args[4] = :(Val($(ex0.args[3])))
51✔
476
            end
477
            return gen_call(fcn, args, where_params, kws; use_signature_tuple, not_an_opaque_closure = false)
306✔
478
        elseif ex0.head === :(=) && length(ex0.args) == 2
144✔
479
            lhs, rhs = ex0.args
15✔
480
            if isa(lhs, Expr)
15✔
481
                if lhs.head === :(.)
15✔
482
                    return gen_call(fcn, Any[Base.setproperty!, lhs.args..., rhs], where_params, kws; use_signature_tuple)
6✔
483
                elseif lhs.head === :ref
9✔
484
                    return expand_ref_begin_end!(lhs, __module__) do ex
9✔
485
                        gen_call(fcn, Any[setindex!, ex.args[1], rhs, ex.args[2:end]...], where_params, kws; use_signature_tuple)
9✔
486
                    end
487
                end
488
            end
489
        elseif ex0.head === :vcat || ex0.head === :typed_vcat
246✔
490
            if ex0.head === :vcat
30✔
491
                f, hf = Base.vcat, Base.hvcat
12✔
492
                args = ex0.args
12✔
493
            else
494
                f, hf = Base.typed_vcat, Base.typed_hvcat
18✔
495
                args = ex0.args[2:end]
18✔
496
            end
497
            if any(@nospecialize(a)->isa(a,Expr) && a.head === :row, args)
96✔
498
                rows = Any[ (isa(x,Expr) && x.head === :row ? x.args : Any[x]) for x in args ]
18✔
499
                lens = map(length, rows)
18✔
500
                args = Any[Expr(:tuple, lens...); vcat(rows...)]
18✔
501
                ex0.head === :typed_vcat && pushfirst!(args, ex0.args[1])
18✔
502
                return gen_call(fcn, Any[hf, args...], where_params, kws; use_signature_tuple)
18✔
503
            else
504
                return gen_call(fcn, Any[f, ex0.args...], where_params, kws; use_signature_tuple)
12✔
505
            end
506
        elseif ex0.head === :ref
99✔
507
            return expand_ref_begin_end!(ex0, __module__) do ex
48✔
508
                gen_call(fcn, Any[getindex, ex.args...], where_params, kws; use_signature_tuple)
42✔
509
            end
510
        else
511
            for (head, f) in Any[:hcat => Base.hcat,
51✔
512
                                 :(.) => Base.getproperty,
513
                                 :vect => Base.vect,
514
                                 Symbol("'") => Base.adjoint,
515
                                 :typed_hcat => Base.typed_hcat,
516
                                 :string => string]
517
                ex0.head === head || continue
201✔
518
                return gen_call(fcn, Any[f, ex0.args...], where_params, kws; use_signature_tuple)
36✔
519
            end
165✔
520
        end
521
    end
522
    if isa(ex0, Expr) && ex0.head === :macrocall # Make @edit @time 1+2 edit the macro by using the types of the *expressions*
27✔
523
        args = [#=__source__::=#LineNumberNode, #=__module__::=#Module, Core.Typeof.(ex0.args[3:end])...]
15✔
524
        return gen_call(fcn, Any[ex0.args[1], Ref.(args)...], where_params, kws; use_signature_tuple)
15✔
525
    end
526

527
    ex = Meta.lower(__module__, ex0)
12✔
528
    isa(ex, Expr) || return Expr(:call, :error, "expression is not a function call or symbol")
24✔
529

530
    return Expr(:call, :error, "expression is not a function call, \
×
531
                                    or is too complex for @$fcn to analyze; \
532
                                    break it down to simpler parts if possible. \
533
                                    In some cases, you may want to use Meta.@lower.")
534
end
535

536
"""
537
Same behaviour as `gen_call_with_extracted_types` except that keyword arguments
538
of the form "foo=bar" are passed on to the called function as well.
539
The keyword arguments must be given before the mandatory argument.
540
"""
541
function gen_call_with_extracted_types_and_kwargs(__module__, fcn, ex0; is_source_reflection = !is_code_macro(fcn), supports_binding_reflection = false, use_signature_tuple = false)
420✔
542
    kws = Expr[]
348✔
543
    arg = ex0[end] # Mandatory argument
348✔
544
    for i in 1:length(ex0)-1
348✔
545
        x = ex0[i]
72✔
546
        if x isa Expr && x.head === :(=) # Keyword given of the form "foo=bar"
72✔
547
            if length(x.args) != 2
72✔
548
                return Expr(:call, :error, "Invalid keyword argument: $x")
×
549
            end
550
            push!(kws, Expr(:kw, esc(x.args[1]), esc(x.args[2])))
72✔
551
        else
552
            return Expr(:call, :error, "@$fcn expects only one non-keyword argument")
×
553
        end
554
    end
72✔
555
    return gen_call_with_extracted_types(__module__, fcn, arg, kws; is_source_reflection, supports_binding_reflection, use_signature_tuple)
348✔
556
end
557

558
for fname in [:which, :less, :edit, :functionloc]
559
    @eval begin
560
        macro ($fname)(ex0)
276✔
561
            gen_call_with_extracted_types(__module__, $(Expr(:quote, fname)), ex0, Expr[];
276✔
562
                                          is_source_reflection = true,
563
                                          supports_binding_reflection = $(fname === :which),
564
                                          use_signature_tuple = true)
565
        end
566
    end
567
end
568

569
macro which(ex0::Symbol)
3✔
570
    ex0 = QuoteNode(ex0)
3✔
571
    return :(which($__module__, $ex0))
3✔
572
end
573

574
for fname in [:code_warntype, :code_llvm, :code_native,
575
              :infer_return_type, :infer_effects, :infer_exception_type]
576
    @eval macro ($fname)(ex0...)
18✔
577
        gen_call_with_extracted_types_and_kwargs(__module__, $(QuoteNode(fname)), ex0; is_source_reflection = false, use_signature_tuple = $(in(fname, [:code_warntype, :code_llvm, :code_native])))
18✔
578
    end
579
end
580

581
for fname in [:code_typed, :code_lowered, :code_ircode]
582
    @eval macro ($fname)(ex0...)
297✔
583
        thecall = gen_call_with_extracted_types_and_kwargs(__module__, $(QuoteNode(fname)), ex0; is_source_reflection = false, use_signature_tuple = true)
297✔
584
        quote
297✔
585
            local results = $thecall
3✔
586
            length(results) == 1 ? results[1] : results
3✔
587
        end
588
    end
589
end
590

591
"""
592
    @functionloc
593

594
Applied to a function or macro call, it evaluates the arguments to the specified call, and
595
returns a tuple `(filename,line)` giving the location for the method that would be called for those arguments.
596
It calls out to the [`functionloc`](@ref) function.
597
"""
598
:@functionloc
599

600
"""
601
    @which
602

603
Applied to a function or macro call, it evaluates the arguments to the specified call, and
604
returns the `Method` object for the method that would be called for those arguments. Applied
605
to a variable, it returns the module in which the variable was bound. It calls out to the
606
[`which`](@ref) function.
607

608
See also: [`@less`](@ref), [`@edit`](@ref).
609
"""
610
:@which
611

612
"""
613
    @less
614

615
Evaluates the arguments to the function or macro call, determines their types, and calls the [`less`](@ref)
616
function on the resulting expression.
617

618
See also: [`@edit`](@ref), [`@which`](@ref), [`@code_lowered`](@ref).
619
"""
620
:@less
621

622
"""
623
    @edit
624

625
Evaluates the arguments to the function or macro call, determines their types, and calls the [`edit`](@ref)
626
function on the resulting expression.
627

628
See also: [`@less`](@ref), [`@which`](@ref).
629
"""
630
:@edit
631

632
"""
633
    @code_typed
634

635
Evaluates the arguments to the function or macro call, determines their types, and calls
636
[`code_typed`](@ref) on the resulting expression. Use the optional argument `optimize` with
637

638
    @code_typed optimize=true foo(x)
639

640
to control whether additional optimizations, such as inlining, are also applied.
641

642
See also: [`code_typed`](@ref), [`@code_warntype`](@ref), [`@code_lowered`](@ref), [`@code_llvm`](@ref), [`@code_native`](@ref).
643
"""
644
:@code_typed
645

646
"""
647
    @code_lowered
648

649
Evaluates the arguments to the function or macro call, determines their types, and calls
650
[`code_lowered`](@ref) on the resulting expression.
651

652
See also: [`code_lowered`](@ref), [`@code_warntype`](@ref), [`@code_typed`](@ref), [`@code_llvm`](@ref), [`@code_native`](@ref).
653
"""
654
:@code_lowered
655

656
"""
657
    @code_warntype
658

659
Evaluates the arguments to the function or macro call, determines their types, and calls
660
[`code_warntype`](@ref) on the resulting expression.
661

662
See also: [`code_warntype`](@ref), [`@code_typed`](@ref), [`@code_lowered`](@ref), [`@code_llvm`](@ref), [`@code_native`](@ref).
663
"""
664
:@code_warntype
665

666
"""
667
    @code_llvm
668

669
Evaluates the arguments to the function or macro call, determines their types, and calls
670
[`code_llvm`](@ref) on the resulting expression.
671
Set the optional keyword arguments `raw`, `dump_module`, `debuginfo`, `optimize`
672
by putting them and their value before the function call, like this:
673

674
    @code_llvm raw=true dump_module=true debuginfo=:default f(x)
675
    @code_llvm optimize=false f(x)
676

677
`optimize` controls whether additional optimizations, such as inlining, are also applied.
678
`raw` makes all metadata and dbg.* calls visible.
679
`debuginfo` may be one of `:source` (default) or `:none`,  to specify the verbosity of code comments.
680
`dump_module` prints the entire module that encapsulates the function.
681

682
See also: [`code_llvm`](@ref), [`@code_warntype`](@ref), [`@code_typed`](@ref), [`@code_lowered`](@ref), [`@code_native`](@ref).
683
"""
684
:@code_llvm
685

686
"""
687
    @code_native
688

689
Evaluates the arguments to the function or macro call, determines their types, and calls
690
[`code_native`](@ref) on the resulting expression.
691

692
Set any of the optional keyword arguments `syntax`, `debuginfo`, `binary` or `dump_module`
693
by putting it before the function call, like this:
694

695
    @code_native syntax=:intel debuginfo=:default binary=true dump_module=false f(x)
696

697
* Set assembly syntax by setting `syntax` to `:intel` (default) for Intel syntax or `:att` for AT&T syntax.
698
* Specify verbosity of code comments by setting `debuginfo` to `:source` (default) or `:none`.
699
* If `binary` is `true`, also print the binary machine code for each instruction precedented by an abbreviated address.
700
* If `dump_module` is `false`, do not print metadata such as rodata or directives.
701

702
See also: [`code_native`](@ref), [`@code_warntype`](@ref), [`@code_typed`](@ref), [`@code_lowered`](@ref), [`@code_llvm`](@ref).
703
"""
704
:@code_native
705

706
"""
707
    @time_imports
708

709
A macro to execute an expression and produce a report of any time spent importing packages and their
710
dependencies. Any compilation time will be reported as a percentage, and how much of which was recompilation, if any.
711

712
One line is printed per package or package extension. The duration shown is the time to import that package itself, not including the time to load any of its dependencies.
713

714
On Julia 1.9+ [package extensions](@ref man-extensions) will show as Parent → Extension.
715

716
!!! note
717
    During the load process a package sequentially imports all of its dependencies, not just its direct dependencies.
718

719
```julia-repl
720
julia> @time_imports using CSV
721
     50.7 ms  Parsers 17.52% compilation time
722
      0.2 ms  DataValueInterfaces
723
      1.6 ms  DataAPI
724
      0.1 ms  IteratorInterfaceExtensions
725
      0.1 ms  TableTraits
726
     17.5 ms  Tables
727
     26.8 ms  PooledArrays
728
    193.7 ms  SentinelArrays 75.12% compilation time
729
      8.6 ms  InlineStrings
730
     20.3 ms  WeakRefStrings
731
      2.0 ms  TranscodingStreams
732
      1.4 ms  Zlib_jll
733
      1.8 ms  CodecZlib
734
      0.8 ms  Compat
735
     13.1 ms  FilePathsBase 28.39% compilation time
736
   1681.2 ms  CSV 92.40% compilation time
737
```
738

739
!!! compat "Julia 1.8"
740
    This macro requires at least Julia 1.8
741

742
"""
743
:@time_imports
744

745
"""
746
    @trace_compile
747

748
A macro to execute an expression and show any methods that were compiled (or recompiled in yellow),
749
like the julia args `--trace-compile=stderr --trace-compile-timing` but specifically for a call.
750

751
```julia-repl
752
julia> @trace_compile rand(2,2) * rand(2,2)
753
#=   39.1 ms =# precompile(Tuple{typeof(Base.rand), Int64, Int64})
754
#=  102.0 ms =# precompile(Tuple{typeof(Base.:(*)), Array{Float64, 2}, Array{Float64, 2}})
755
2×2 Matrix{Float64}:
756
 0.421704  0.864841
757
 0.211262  0.444366
758
```
759

760
!!! compat "Julia 1.12"
761
    This macro requires at least Julia 1.12
762

763
"""
764
:@trace_compile
765

766
"""
767
    @trace_dispatch
768

769
A macro to execute an expression and report methods that were compiled via dynamic dispatch,
770
like the julia arg `--trace-dispatch=stderr` but specifically for a call.
771

772
!!! compat "Julia 1.12"
773
    This macro requires at least Julia 1.12
774

775
"""
776
:@trace_dispatch
777

778
"""
779
    @activate Component
780

781
Activate a newly loaded copy of an otherwise builtin component. The `Component`
782
to be activated will be resolved using the ordinary rules of module resolution
783
in the current environment.
784

785
When using `@activate`, additional options for a component may be specified in
786
square brackets `@activate Compiler[:option1, :option]`
787

788
Currently `Compiler` and `JuliaLowering` are the only available components that
789
may be activatived.
790

791
For `@activate Compiler`, the following options are available:
792
1. `:reflection` - Activate the compiler for reflection purposes only.
793
                   The ordinary reflection functionality in `Base` and `InteractiveUtils`.
794
                   Will use the newly loaded compiler. Note however, that these reflection
795
                   functions will still interact with the ordinary native cache (both loading
796
                   and storing). An incorrect compiler implementation may thus corrupt runtime
797
                   state if reflection is used. Use external packages like `Cthulhu.jl`
798
                   introspecting compiler behavior with a separated cache partition.
799

800
2. `:codegen`   - Activate the compiler for internal codegen purposes. The new compiler
801
                  will be invoked whenever the runtime requests compilation.
802

803
`@activate Compiler` without options is equivalent to `@activate Compiler[:reflection]`.
804

805
"""
806
macro activate(what)
112✔
807
    options = Symbol[]
112✔
808
    if isexpr(what, :ref)
112✔
809
        Component = what.args[1]
×
810
        for i = 2:length(what.args)
×
811
            arg = what.args[i]
×
812
            if !isa(arg, QuoteNode) || !isa(arg.value, Symbol)
×
813
                error("Usage Error: Option $arg is not a symbol")
×
814
            end
815
            push!(options, arg.value)
×
816
        end
×
817
    else
818
        Component = what
112✔
819
    end
820
    if !isa(Component, Symbol)
112✔
821
        error("Usage Error: Component $Component is not a symbol")
×
822
    end
823
    allowed_components = (:Compiler, :JuliaLowering)
112✔
824
    if !(Component in allowed_components)
112✔
825
        error("Usage Error: Component $Component is not recognized. Expected one of $allowed_components")
×
826
    end
827
    if Component === :Compiler && isempty(options)
112✔
828
        push!(options, :reflection)
112✔
829
    end
830
    options = map(options) do opt
112✔
831
        Expr(:kw, opt, true)
112✔
832
    end
833
    return :(Base.require($__module__, $(QuoteNode(Component))).activate!(; $(options...)))
112✔
834
end
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