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

JuliaLang / julia / 1253

29 Aug 2025 03:48AM UTC coverage: 74.226% (-4.0%) from 78.243%
1253

push

buildkite

web-flow
make NEWS-update.jl more robust in trimming existing links (#59305)

The `NEWS-update.jl` script trims away any existing links from the end
of the `NEWS.md` file, so that it is safe to run multiple times. This PR
makes that trimming a bit more robust by trimming only `[#...]:`
patterns that start at the beginning of a line.

(I ran into this in re-using the same script for another project.)

63334 of 85326 relevant lines covered (74.23%)

14850782.87 hits per line

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

95.03
/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(:(Tuple{$(Any[esc(reescape(get_typeof, a)) for a in args]...)}), where_params)
626✔
13
typesof_expr_unescaped(args::Vector{Any}, where_params::Union{Nothing, Vector{Any}} = nothing) = rewrap_where(:(Tuple{$(Any[reescape(get_typeof, a) for a in args]...)}), where_params)
128✔
14

15
function extract_where_parameters(ex::Expr)
16
    isexpr(ex, :where) || return ex, nothing
1,262✔
17
    ex.args[1], ex.args[2:end]
20✔
18
end
19

20
function rewrap_where(ex::Expr, where_params::Union{Nothing, Vector{Any}})
12✔
21
    isnothing(where_params) && return ex
754✔
22
    Expr(:where, ex, esc.(where_params)...)
12✔
23
end
24

25
function reescape(f::Function, @nospecialize ex)
2,552✔
26
    isa(ex, Expr) || return f(ex)
3,956✔
27
    unescaped = Meta.unescape(ex)
1,148✔
28
    new = f(unescaped)
1,148✔
29
    return Meta.reescape(new, ex)
1,148✔
30
end
31

32
get_typeof(ex::Ref) = ex[]
348✔
33
function get_typeof(@nospecialize ex)
1,906✔
34
    isexpr(ex, :(::), 1) && return ex.args[1]
1,906✔
35
    isexpr(ex, :(::), 2) && return ex.args[2]
1,720✔
36
    if isexpr(ex, :..., 1)
1,700✔
37
        splatted = ex.args[1]
286✔
38
        isexpr(splatted, :(::), 1) && return Expr(:curly, :(Core.Vararg), splatted.args[1])
286✔
39
        return :(Any[Core.Typeof(x) for x in $splatted]...)
278✔
40
    end
41
    return :(Core.Typeof($ex))
1,414✔
42
end
43

44
function is_broadcasting_call(ex)
1,394✔
45
    isa(ex, Expr) || return false
1,424✔
46
    # Standard broadcasting: f.(x)
47
    isexpr(ex, :.) && length(ex.args) ≥ 2 && isexpr(ex.args[2], :tuple) && return true
1,394✔
48
    # Infix broadcasting: x .+ y, x .<< y, etc.
49
    if isexpr(ex, :call)
1,344✔
50
        f = ex.args[1]
996✔
51
        f == :.. && return false
1,992✔
52
        string(f)[1] == '.' && return true
992✔
53
    end
54
    return false
1,290✔
55
end
56
is_broadcasting_expr(ex) = is_broadcasting_call(ex) || is_broadcasting_assignment(ex)
2,718✔
57
function is_broadcasting_assignment(ex)
1,368✔
58
    isa(ex, Expr) || return false
1,398✔
59
    isexpr(ex, :.) && return false
1,368✔
60
    head = string(ex.head)
1,316✔
61
    # x .= y, x .+= y, x .<<= y, etc.
62
    head[begin] == '.' && head[end] == '=' && return true
1,372✔
63
    return false
1,264✔
64
end
65

66
"""
67
Transform a dot expression into one where each argument has been replaced by a
68
variable "xj" (with j an integer from 1 to the returned i).
69
The list `args` contains the original arguments that have been replaced.
70
"""
71
function recursive_dotcalls!(ex, args, i=1)
216✔
72
    if is_broadcasting_expr(ex)
398✔
73
        if is_broadcasting_assignment(ex)
74✔
74
            (start, branches) = (1, ex.args)
16✔
75
        elseif isexpr(ex, :.)
58✔
76
            (start, branches) = (1, ex.args[2].args)
28✔
77
        else
78
            (start, branches) = (2, ex.args)
30✔
79
        end
80
        for j in start:length(branches)::Int
74✔
81
            branch, i = recursive_dotcalls!(branches[j], args, i)
148✔
82
            branches[j] = branch
148✔
83
        end
222✔
84
        return ex, i
74✔
85
    elseif isexpr(ex, :parameters)
142✔
86
        for j in eachindex(ex.args)
14✔
87
            param, i = recursive_dotcalls!(ex.args[j], args, i)
14✔
88
            ex.args[j] = param
14✔
89
        end
14✔
90
        return ex, i
14✔
91
    end
92
    newarg = Symbol('x', i)
128✔
93
    if isexpr(ex, :...)
128✔
94
        newarg = Expr(:..., newarg)
4✔
95
        push!(args, only(ex.args))
4✔
96
    elseif isexpr(ex, :kw)
124✔
97
        newarg = Expr(:kw, ex.args[1], newarg)
28✔
98
        push!(args, ex.args[end])
28✔
99
    else
100
        push!(args, ex)
96✔
101
    end
102
    return newarg, i+1
128✔
103
end
104

105
function extract_farg(@nospecialize arg)
298✔
106
    !isexpr(arg, :(::), 1) && return arg
298✔
107
    fT = arg.args[1]
×
108
    :($construct_callable($fT))
×
109
end
110

111
function construct_callable(@nospecialize(func::Type))
112
    # Support function singleton types such as `(::typeof(f))(args...)`
113
    Base.issingletontype(func) && isdefined(func, :instance) && return func.instance
114
    # Don't support type annotations otherwise, we don't want to give wrong answers
115
    # for callables such as `(::Returns{Int})(args...)` where using `Returns{Int}`
116
    # would give us code for the constructor, not for the callable object.
117
    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.
2✔
118
                         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...}`"))
119
end
120

121
function separate_kwargs(exs::Vector{Any})
310✔
122
    args = []
310✔
123
    kwargs = []
310✔
124
    for ex in exs
310✔
125
        if isexpr(ex, :kw)
932✔
126
            push!(kwargs, ex)
8✔
127
        elseif isexpr(ex, :parameters)
924✔
128
            for kw in ex.args
302✔
129
                push!(kwargs, kw)
316✔
130
            end
316✔
131
        else
132
            push!(args, ex)
622✔
133
        end
134
    end
932✔
135
    args, kwargs
310✔
136
end
137

138
function are_kwargs_valid(kwargs::Vector{Any})
139
    for kwarg in kwargs
310✔
140
        isexpr(kwarg, :..., 1) && continue
324✔
141
        isexpr(kwarg, :kw, 2) && isa(kwarg.args[1], Symbol) && continue
38✔
142
        isexpr(kwarg, :(::), 2) && continue
8✔
143
        isa(kwarg, Symbol) && continue
4✔
144
        isexpr(kwarg, :escape) && continue
2✔
145
        isexpr(kwarg, :var"hygienic-scope") && continue
2✔
146
        return false
2✔
147
    end
322✔
148
    return true
308✔
149
end
150

151
# Generate an expression that merges `kwargs` onto a single `NamedTuple`
152
function generate_merged_namedtuple_type(kwargs::Vector{Any})
308✔
153
    nts = Any[]
308✔
154
    ntargs = Pair{Symbol, Any}[]
308✔
155
    for ex in kwargs
308✔
156
        if isexpr(ex, :..., 1)
322✔
157
            if !isempty(ntargs)
286✔
158
                # Construct a `NamedTuple` containing the previous parameters.
159
                push!(nts, generate_namedtuple_type(ntargs))
8✔
160
                empty!(ntargs)
8✔
161
            end
162
            push!(nts, Expr(:call, typeof_nt, ex.args[1]))
286✔
163
        elseif isexpr(ex, :kw, 2)
36✔
164
            push!(ntargs, ex.args[1]::Symbol => reescape(get_typeof, ex.args[2]))
30✔
165
        elseif isexpr(ex, :(::), 2)
6✔
166
            push!(ntargs, ex.args[1]::Symbol => reescape(get_typeof, ex))
4✔
167
        else
168
            push!(ntargs, ex => reescape(get_typeof, ex))
2✔
169
        end
170
    end
322✔
171
    !isempty(ntargs) && push!(nts, generate_namedtuple_type(ntargs))
308✔
172
    return :($merge_namedtuple_types($(nts...)))
308✔
173
end
174

175
function generate_namedtuple_type(ntargs::Vector{Pair{Symbol, Any}})
36✔
176
    names = Expr(:tuple)
36✔
177
    tt = Expr(:curly, :Tuple)
36✔
178
    for (name, type) in ntargs
36✔
179
        push!(names.args, QuoteNode(name))
36✔
180
        push!(tt.args, type)
36✔
181
    end
36✔
182
    return :(NamedTuple{$names, $tt})
36✔
183
end
184

185
typeof_nt(nt::NamedTuple) = typeof(nt)
16✔
186
typeof_nt(nt::Base.Pairs) = typeof(values(nt))
710✔
187

188
function merge_namedtuple_types(nt::Type{<:NamedTuple}, nts::Type{<:NamedTuple}...)
706✔
189
    @nospecialize
748✔
190
    isempty(nts) && return nt
748✔
191
    names = Symbol[]
12✔
192
    types = Any[]
12✔
193
    for nt in (nt, nts...)
12✔
194
        for (name, type) in zip(fieldnames(nt), fieldtypes(nt))
26✔
195
            i = findfirst(==(name), names)
42✔
196
            if isnothing(i)
34✔
197
                push!(names, name)
18✔
198
                push!(types, type)
18✔
199
            else
200
                types[i] = type
8✔
201
            end
202
        end
26✔
203
    end
38✔
204
    return NamedTuple{Tuple(names), Tuple{types...}}
12✔
205
end
206

207
function gen_call(fcn, args, where_params, kws; use_signature_tuple::Bool, not_an_opaque_closure::Bool = true)
1,252✔
208
    f, args... = args
1,228✔
209
    args = collect(Any, args)
1,228✔
210
    if !use_signature_tuple
626✔
211
        f = esc(reescape(extract_farg, f))
298✔
212
        tt = typesof_expr(args, where_params)
298✔
213
        return :($fcn($f, $tt; $(kws...)))
298✔
214
    end
215
    # We use a signature tuple only if we are sure we won't get an opaque closure as first argument.
216
    # If we do get one, we have to use the 2-argument form.
217
    if isexpr(f, :(::)) || not_an_opaque_closure
650✔
218
        # We have a type, not a value, so not an opaque closure.
219
        sigt = typesof_expr(Any[f, args...], where_params)
200✔
220
        return :($fcn($sigt; $(kws...)))
200✔
221
    end
222
    tt = typesof_expr(args, where_params)
128✔
223
    sigt = typesof_expr_unescaped(Any[:f, esc.(args)...], where_params)
128✔
224
    return quote
128✔
225
        f = $(esc(f))
4✔
226
        if isa(f, Core.OpaqueClosure)
4✔
227
            $fcn(f, $tt; $(kws...))
228
        else
229
            $fcn($sigt; $(kws...))
4✔
230
        end
231
    end
232
end
233

234
function expand_ref_begin_end!(f::Function, ex, __module__::Module)
38✔
235
    arr = ex.args[1]
38✔
236
    args = copy(ex.args)
76✔
237
    new = replace_ref_begin_end!(__module__, ex)
38✔
238
    modified = ex.args .≠ args
76✔
239
    if any(modified) && (isexpr(arr, :(::), 1) || isexpr(arr, :(::), 2) || isexpr(arr, :..., 1))
54✔
240
        return Expr(:call, :error, "`begin` or `end` cannot be used with a type-annotated left-hand side argument for an indexing syntax")
4✔
241
    end
242
    call = f(ex)
34✔
243
    !any(modified) && return call
68✔
244
    fixup_hygiene_for_ref_temporary!(new)
16✔
245
    # We have to mutate `ex`, then return `new` which evaluates `arr` before use.
246
    ex.head = call.head
16✔
247
    ex.args = call.args
16✔
248
    return new
16✔
249
end
250

251
function fixup_hygiene_for_ref_temporary!(ex)
16✔
252
    # Match the local variable `##S#...` so we may escape its definition.
253
    # We don't want to use `escs = 1` in `replace_ref_begin_end_!` because
254
    # then we delegate escaping to this function, whereas we otherwise manage
255
    # ourselves the escaping in all other code paths.
256
    isexpr(ex, :block) || return
16✔
257
    decl = ex.args[1]
16✔
258
    isexpr(decl, :local, 1) || return
16✔
259
    assignment = decl.args[1]
16✔
260
    isexpr(assignment, :(=), 2) || return
16✔
261
    variable = assignment.args[1]
16✔
262
    startswith(string(variable), "##S#") || return
16✔
263
    decl.args[1] = esc(assignment)
16✔
264
end
265

266
is_code_macro(fcn) = startswith(string(fcn), "code_")
14✔
267

268
"""
269
    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)
270

271
Destructures the input expression `ex` into a function call or a binding access, then generates a call to either:
272
- `fcn(f, tt; kws...)`
273
- `fcn(sigt; kws...)` # if `use_signature_tuple = true`
274
- `fcn(mod, name; kws...)` # if `supports_binding_reflection = true`
275

276
## `fcn` API requirements
277

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

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

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

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

293
## Examples
294

295
Here are a few usage patterns that may help you get started.
296

297
For most "code" macros (`@code_typed`, `@code_llvm`, `@code_native` etc):
298
```julia
299
    gen_call_with_extracted_types(__module__, fcn, ex, kws; is_source_reflection = false, use_signature_tuple = true #= may be false =#)
300
```
301

302
For source reflection macros (`@which`, `@edit`, `@less` etc):
303
```julia
304
    gen_call_with_extracted_types(__module__, fcn, ex, kws; is_source_reflection = true, use_signature_tuple = true #= may be false =#)
305
```
306

307
# Extended help
308

309
## Type annotations
310

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

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

318
Constraints on type parameters are also supported with a `where` syntax, enabling these patterns:
319
- `f(x::Vector{T}, y::T) where {T}`
320
- `(::Returns{T})() where {T<:Real}`
321
- `(::MyPolynomial{N,T})(::T, ::AbstractArray{T,N}) where {N,T}`
322

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

325
## Broadcasting
326

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

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

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

341
## Binding reflection
342

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

348
## Tuple signature type
349

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

353
This behavior is required to enable support type-annotated callable objects.
354

355
To understand this requirement, we'll use `code_typed` as an example. `code_typed(f, ())` interprets its input as the signature
356
`Tuple{typeof(f)}`, and `code_typed(Returns{Int}, ())` interprets that as the signature `Tuple{Type{Returns{Int}}}`, corresponding
357
to the type constructor.
358
To remove the ambiguity, `code_typed` must support an implementation that directly accepts a function type. This implementation
359
is assumed to be the method for `fcn(sigt::Type{<:Tuple})`.
360
"""
361
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)
1,574✔
362
    # Ignore assignments (e.g. `@edit a = f(x)` gets turned into `@edit f(x)`)
363
    if isa(ex0, Expr) && ex0.head === :(=) && isa(ex0.args[1], Symbol)
652✔
364
        return gen_call_with_extracted_types(__module__, fcn, ex0.args[2], kws; is_source_reflection, supports_binding_reflection, use_signature_tuple)
8✔
365
    end
366
    where_params = nothing
644✔
367
    if isa(ex0, Expr)
644✔
368
        ex0, where_params = extract_where_parameters(ex0)
1,262✔
369
    end
370
    if isa(ex0, Expr)
644✔
371
        if ex0.head === :do && isexpr(get(ex0.args, 1, nothing), :call)
636✔
372
            # Normalize `f(args...) do ... end` calls to `f(do_anonymous_function, args...)`
373
            if length(ex0.args) != 2
4✔
374
                return Expr(:call, :error, "ill-formed do call")
×
375
            end
376
            i = findlast(@nospecialize(a)->(isexpr(a, :kw) || isexpr(a, :parameters)), ex0.args[1].args)
20✔
377
            args = copy(ex0.args[1].args)
4✔
378
            insert!(args, (isnothing(i) ? 2 : 1+i::Int), ex0.args[2])
4✔
379
            ex0 = Expr(:call, args...)
4✔
380
        end
381
        if is_broadcasting_expr(ex0) && !is_source_reflection
1,232✔
382
            # Manually wrap top-level broadcasts in a function.
383
            # We don't do that if `fcn` reflects into the source,
384
            # because that destroys provenance information.
385
            args = Any[]
54✔
386
            ex, i = recursive_dotcalls!(copy(ex0), args)
54✔
387
            xargs = [Symbol('x', j) for j in 1:i-1]
54✔
388
            dotfuncname = gensym("dotfunction")
54✔
389
            call = gen_call(fcn, Any[dotfuncname, args...], where_params, kws; use_signature_tuple)
54✔
390
            return quote
54✔
391
                let $(esc(:($dotfuncname($(xargs...)) = $ex)))
392
                    $call
393
                end
394
            end
395
        elseif isexpr(ex0, :.) && is_source_reflection
582✔
396
            # If `ex0` has the form A.B (or some chain A.B.C.D) and `fcn` reflects into the source,
397
            # `A` (or `A.B.C`) may be a module, in which case `fcn` is probably more interested in
398
            # the binding rather than the `getproperty` call.
399
            # If binding reflection is not supported, we generate an error; `getproperty(::Module, field)`
400
            # is not going to be interesting to reflect into, so best to allow future non-breaking support
401
            # for binding reflection in case the macro may eventually support that.
402
            fully_qualified_symbol = true
10✔
403
            ex1 = ex0
10✔
404
            while ex1 isa Expr && ex1.head === :.
24✔
405
                fully_qualified_symbol = (length(ex1.args) == 2 &&
14✔
406
                                            ex1.args[2] isa QuoteNode &&
407
                                            ex1.args[2].value isa Symbol)
408
                fully_qualified_symbol || break
14✔
409
                ex1 = ex1.args[1]
14✔
410
            end
14✔
411
            fully_qualified_symbol &= ex1 isa Symbol
10✔
412
            if fully_qualified_symbol || isexpr(ex1, :(::), 1)
12✔
413
                call_reflection = gen_call(fcn, [getproperty; ex0.args], where_params, kws; use_signature_tuple)
20✔
414
                isexpr(ex0.args[1], :(::), 1) && return call_reflection
10✔
415
                if supports_binding_reflection
8✔
416
                    binding_reflection = :($fcn(arg1, $(ex0.args[2]); $(kws...)))
4✔
417
                else
418
                    binding_reflection = :(error("expression is not a function call"))
4✔
419
                end
420
                return quote
8✔
421
                    local arg1 = $(esc(ex0.args[1]))
422
                    if isa(arg1, Module)
423
                        $binding_reflection
424
                    else
425
                        $call_reflection
426
                    end
427
                end
428
            end
429
        end
430
        if is_broadcasting_expr(ex0)
1,142✔
431
            return Expr(:call, :error, "dot expressions are not lowered to "
4✔
432
                * "a single function call, so @$fcn cannot analyze "
433
                * "them. You may want to use Meta.@lower to identify "
434
                * "which function call to target.")
435
        end
436
        if any(@nospecialize(a)->(isexpr(a, :kw) || isexpr(a, :parameters)), ex0.args)
4,108✔
437
            args, kwargs = separate_kwargs(ex0.args)
310✔
438
            are_kwargs_valid(kwargs) || return quote
312✔
439
                error("keyword argument format unrecognized; they must be of the form `x` or `x = <value>`")
440
                $(esc(ex0)) # trigger syntax errors if any
441
            end
442
            nt = generate_merged_namedtuple_type(kwargs)
308✔
443
            nt = Ref(nt) # ignore `get_typeof` handling
308✔
444
            return gen_call(fcn, Any[Core.kwcall, nt, args...], where_params, kws; use_signature_tuple)
308✔
445
        elseif ex0.head === :call
258✔
446
            args = copy(ex0.args)
324✔
447
            if ex0.args[1] === :^ && length(ex0.args) >= 3 && isa(ex0.args[3], Int)
162✔
448
                pushfirst!(args, Base.literal_pow)
68✔
449
                args[4] = :(Val($(ex0.args[3])))
34✔
450
            end
451
            return gen_call(fcn, args, where_params, kws; use_signature_tuple, not_an_opaque_closure = false)
162✔
452
        elseif ex0.head === :(=) && length(ex0.args) == 2
96✔
453
            lhs, rhs = ex0.args
10✔
454
            if isa(lhs, Expr)
10✔
455
                if lhs.head === :(.)
10✔
456
                    return gen_call(fcn, Any[Base.setproperty!, lhs.args..., rhs], where_params, kws; use_signature_tuple)
4✔
457
                elseif lhs.head === :ref
6✔
458
                    return expand_ref_begin_end!(lhs, __module__) do ex
6✔
459
                        gen_call(fcn, Any[setindex!, ex.args[1], rhs, ex.args[2:end]...], where_params, kws; use_signature_tuple)
6✔
460
                    end
461
                end
462
            end
463
        elseif ex0.head === :vcat || ex0.head === :typed_vcat
164✔
464
            if ex0.head === :vcat
20✔
465
                f, hf = Base.vcat, Base.hvcat
8✔
466
                args = ex0.args
8✔
467
            else
468
                f, hf = Base.typed_vcat, Base.typed_hvcat
12✔
469
                args = ex0.args[2:end]
12✔
470
            end
471
            if any(@nospecialize(a)->isa(a,Expr) && a.head === :row, args)
64✔
472
                rows = Any[ (isa(x,Expr) && x.head === :row ? x.args : Any[x]) for x in args ]
12✔
473
                lens = map(length, rows)
12✔
474
                args = Any[Expr(:tuple, lens...); vcat(rows...)]
12✔
475
                ex0.head === :typed_vcat && pushfirst!(args, ex0.args[1])
12✔
476
                return gen_call(fcn, Any[hf, args...], where_params, kws; use_signature_tuple)
12✔
477
            else
478
                return gen_call(fcn, Any[f, ex0.args...], where_params, kws; use_signature_tuple)
8✔
479
            end
480
        elseif ex0.head === :ref
66✔
481
            return expand_ref_begin_end!(ex0, __module__) do ex
32✔
482
                gen_call(fcn, Any[getindex, ex.args...], where_params, kws; use_signature_tuple)
28✔
483
            end
484
        else
485
            for (head, f) in Any[:hcat => Base.hcat,
34✔
486
                                 :(.) => Base.getproperty,
487
                                 :vect => Base.vect,
488
                                 Symbol("'") => Base.adjoint,
489
                                 :typed_hcat => Base.typed_hcat,
490
                                 :string => string]
491
                ex0.head === head || continue
134✔
492
                return gen_call(fcn, Any[f, ex0.args...], where_params, kws; use_signature_tuple)
24✔
493
            end
110✔
494
        end
495
    end
496
    if isa(ex0, Expr) && ex0.head === :macrocall # Make @edit @time 1+2 edit the macro by using the types of the *expressions*
18✔
497
        args = [#=__source__::=#LineNumberNode, #=__module__::=#Module, Core.Typeof.(ex0.args[3:end])...]
10✔
498
        return gen_call(fcn, Any[ex0.args[1], Ref.(args)...], where_params, kws; use_signature_tuple)
10✔
499
    end
500

501
    ex = Meta.lower(__module__, ex0)
8✔
502
    isa(ex, Expr) || return Expr(:call, :error, "expression is not a function call or symbol")
16✔
503

504
    return Expr(:call, :error, "expression is not a function call, \
×
505
                                    or is too complex for @$fcn to analyze; \
506
                                    break it down to simpler parts if possible. \
507
                                    In some cases, you may want to use Meta.@lower.")
508
end
509

510
"""
511
Same behaviour as `gen_call_with_extracted_types` except that keyword arguments
512
of the form "foo=bar" are passed on to the called function as well.
513
The keyword arguments must be given before the mandatory argument.
514
"""
515
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)
238✔
516
    kws = Expr[]
190✔
517
    arg = ex0[end] # Mandatory argument
190✔
518
    for i in 1:length(ex0)-1
190✔
519
        x = ex0[i]
48✔
520
        if x isa Expr && x.head === :(=) # Keyword given of the form "foo=bar"
48✔
521
            if length(x.args) != 2
48✔
522
                return Expr(:call, :error, "Invalid keyword argument: $x")
×
523
            end
524
            push!(kws, Expr(:kw, esc(x.args[1]), esc(x.args[2])))
48✔
525
        else
526
            return Expr(:call, :error, "@$fcn expects only one non-keyword argument")
×
527
        end
528
    end
48✔
529
    return gen_call_with_extracted_types(__module__, fcn, arg, kws; is_source_reflection, supports_binding_reflection, use_signature_tuple)
190✔
530
end
531

532
for fname in [:which, :less, :edit, :functionloc]
533
    @eval begin
534
        macro ($fname)(ex0)
184✔
535
            gen_call_with_extracted_types(__module__, $(Expr(:quote, fname)), ex0, Expr[];
184✔
536
                                          is_source_reflection = true,
537
                                          supports_binding_reflection = $(fname === :which),
538
                                          use_signature_tuple = true)
539
        end
540
    end
541
end
542

543
macro which(ex0::Symbol)
2✔
544
    ex0 = QuoteNode(ex0)
2✔
545
    return :(which($__module__, $ex0))
2✔
546
end
547

548
for fname in [:code_warntype, :code_llvm, :code_native,
549
              :infer_return_type, :infer_effects, :infer_exception_type]
550
    @eval macro ($fname)(ex0...)
12✔
551
        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])))
12✔
552
    end
553
end
554

555
for fname in [:code_typed, :code_lowered, :code_ircode]
556
    @eval macro ($fname)(ex0...)
156✔
557
        thecall = gen_call_with_extracted_types_and_kwargs(__module__, $(QuoteNode(fname)), ex0; is_source_reflection = false, use_signature_tuple = true)
156✔
558
        quote
156✔
559
            local results = $thecall
2✔
560
            length(results) == 1 ? results[1] : results
2✔
561
        end
562
    end
563
end
564

565
"""
566
    @functionloc
567

568
Applied to a function or macro call, it evaluates the arguments to the specified call, and
569
returns a tuple `(filename,line)` giving the location for the method that would be called for those arguments.
570
It calls out to the [`functionloc`](@ref) function.
571
"""
572
:@functionloc
573

574
"""
575
    @which
576

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

582
See also: [`@less`](@ref), [`@edit`](@ref).
583
"""
584
:@which
585

586
"""
587
    @less
588

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

592
See also: [`@edit`](@ref), [`@which`](@ref), [`@code_lowered`](@ref).
593
"""
594
:@less
595

596
"""
597
    @edit
598

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

602
See also: [`@less`](@ref), [`@which`](@ref).
603
"""
604
:@edit
605

606
"""
607
    @code_typed
608

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

612
    @code_typed optimize=true foo(x)
613

614
to control whether additional optimizations, such as inlining, are also applied.
615

616
See also: [`code_typed`](@ref), [`@code_warntype`](@ref), [`@code_lowered`](@ref), [`@code_llvm`](@ref), [`@code_native`](@ref).
617
"""
618
:@code_typed
619

620
"""
621
    @code_lowered
622

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

626
See also: [`code_lowered`](@ref), [`@code_warntype`](@ref), [`@code_typed`](@ref), [`@code_llvm`](@ref), [`@code_native`](@ref).
627
"""
628
:@code_lowered
629

630
"""
631
    @code_warntype
632

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

636
See also: [`code_warntype`](@ref), [`@code_typed`](@ref), [`@code_lowered`](@ref), [`@code_llvm`](@ref), [`@code_native`](@ref).
637
"""
638
:@code_warntype
639

640
"""
641
    @code_llvm
642

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

648
    @code_llvm raw=true dump_module=true debuginfo=:default f(x)
649
    @code_llvm optimize=false f(x)
650

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

656
See also: [`code_llvm`](@ref), [`@code_warntype`](@ref), [`@code_typed`](@ref), [`@code_lowered`](@ref), [`@code_native`](@ref).
657
"""
658
:@code_llvm
659

660
"""
661
    @code_native
662

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

666
Set any of the optional keyword arguments `syntax`, `debuginfo`, `binary` or `dump_module`
667
by putting it before the function call, like this:
668

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

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

676
See also: [`code_native`](@ref), [`@code_warntype`](@ref), [`@code_typed`](@ref), [`@code_lowered`](@ref), [`@code_llvm`](@ref).
677
"""
678
:@code_native
679

680
"""
681
    @time_imports
682

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

686
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.
687

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

690
!!! note
691
    During the load process a package sequentially imports all of its dependencies, not just its direct dependencies.
692

693
```julia-repl
694
julia> @time_imports using CSV
695
     50.7 ms  Parsers 17.52% compilation time
696
      0.2 ms  DataValueInterfaces
697
      1.6 ms  DataAPI
698
      0.1 ms  IteratorInterfaceExtensions
699
      0.1 ms  TableTraits
700
     17.5 ms  Tables
701
     26.8 ms  PooledArrays
702
    193.7 ms  SentinelArrays 75.12% compilation time
703
      8.6 ms  InlineStrings
704
     20.3 ms  WeakRefStrings
705
      2.0 ms  TranscodingStreams
706
      1.4 ms  Zlib_jll
707
      1.8 ms  CodecZlib
708
      0.8 ms  Compat
709
     13.1 ms  FilePathsBase 28.39% compilation time
710
   1681.2 ms  CSV 92.40% compilation time
711
```
712

713
!!! compat "Julia 1.8"
714
    This macro requires at least Julia 1.8
715

716
"""
717
:@time_imports
718

719
"""
720
    @trace_compile
721

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

725
```julia-repl
726
julia> @trace_compile rand(2,2) * rand(2,2)
727
#=   39.1 ms =# precompile(Tuple{typeof(Base.rand), Int64, Int64})
728
#=  102.0 ms =# precompile(Tuple{typeof(Base.:(*)), Array{Float64, 2}, Array{Float64, 2}})
729
2×2 Matrix{Float64}:
730
 0.421704  0.864841
731
 0.211262  0.444366
732
```
733

734
!!! compat "Julia 1.12"
735
    This macro requires at least Julia 1.12
736

737
"""
738
:@trace_compile
739

740
"""
741
    @trace_dispatch
742

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

746
!!! compat "Julia 1.12"
747
    This macro requires at least Julia 1.12
748

749
"""
750
:@trace_dispatch
751

752
"""
753
    @activate Component
754

755
Activate a newly loaded copy of an otherwise builtin component. The `Component`
756
to be activated will be resolved using the ordinary rules of module resolution
757
in the current environment.
758

759
When using `@activate`, additional options for a component may be specified in
760
square brackets `@activate Compiler[:option1, :option]`
761

762
Currently `Compiler` and `JuliaLowering` are the only available components that
763
may be activatived.
764

765
For `@activate Compiler`, the following options are available:
766
1. `:reflection` - Activate the compiler for reflection purposes only.
767
                   The ordinary reflection functionality in `Base` and `InteractiveUtils`.
768
                   Will use the newly loaded compiler. Note however, that these reflection
769
                   functions will still interact with the ordinary native cache (both loading
770
                   and storing). An incorrect compiler implementation may thus corrupt runtime
771
                   state if reflection is used. Use external packages like `Cthulhu.jl`
772
                   introspecting compiler behavior with a separated cache partition.
773

774
2. `:codegen`   - Activate the compiler for internal codegen purposes. The new compiler
775
                  will be invoked whenever the runtime requests compilation.
776

777
`@activate Compiler` without options is equivalent to `@activate Compiler[:reflection]`.
778

779
"""
780
macro activate(what)
76✔
781
    options = Symbol[]
76✔
782
    if isexpr(what, :ref)
76✔
783
        Component = what.args[1]
×
784
        for i = 2:length(what.args)
×
785
            arg = what.args[i]
×
786
            if !isa(arg, QuoteNode) || !isa(arg.value, Symbol)
×
787
                error("Usage Error: Option $arg is not a symbol")
×
788
            end
789
            push!(options, arg.value)
×
790
        end
×
791
    else
792
        Component = what
76✔
793
    end
794
    if !isa(Component, Symbol)
76✔
795
        error("Usage Error: Component $Component is not a symbol")
×
796
    end
797
    allowed_components = (:Compiler, :JuliaLowering)
76✔
798
    if !(Component in allowed_components)
76✔
799
        error("Usage Error: Component $Component is not recognized. Expected one of $allowed_components")
×
800
    end
801
    if Component === :Compiler && isempty(options)
76✔
802
        push!(options, :reflection)
76✔
803
    end
804
    options = map(options) do opt
76✔
805
        Expr(:kw, opt, true)
76✔
806
    end
807
    return :(Base.require($__module__, $(QuoteNode(Component))).activate!(; $(options...)))
76✔
808
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