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

JuliaLang / julia / #37732

30 Mar 2024 04:18AM UTC coverage: 80.965% (+0.1%) from 80.848%
#37732

push

local

web-flow
no need to check whether mq_master is nil in the GC work-stealing loop (#53899)

This is not needed after https://github.com/JuliaLang/julia/pull/53355.

70159 of 86653 relevant lines covered (80.97%)

14962901.56 hits per line

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

23.42
/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
import Base: typesof, insert!, replace_ref_begin_end!, infer_effects
6

7
separate_kwargs(args...; kwargs...) = (args, values(kwargs))
704✔
8

9
"""
10
Transform a dot expression into one where each argument has been replaced by a
11
variable "xj" (with j an integer from 1 to the returned i).
12
The list `args` contains the original arguments that have been replaced.
13
"""
14
function recursive_dotcalls!(ex, args, i=1)
×
15
    if !(ex isa Expr) || ((ex.head !== :. || !(ex.args[2] isa Expr)) &&
×
16
                          (ex.head !== :call || string(ex.args[1])[1] != '.'))
17
        newarg = Symbol('x', i)
×
18
        if Meta.isexpr(ex, :...)
×
19
            push!(args, only(ex.args))
×
20
            return Expr(:..., newarg), i+1
×
21
        else
22
            push!(args, ex)
×
23
            return newarg, i+1
×
24
        end
25
    end
26
    (start, branches) = ex.head === :. ? (1, ex.args[2].args) : (2, ex.args)
×
27
    length_branches = length(branches)::Int
×
28
    for j in start:length_branches
×
29
        branch, i = recursive_dotcalls!(branches[j], args, i)
×
30
        branches[j] = branch
×
31
    end
×
32
    return ex, i
×
33
end
34

35
function gen_call_with_extracted_types(__module__, fcn, ex0, kws=Expr[])
157✔
36
    if Meta.isexpr(ex0, :ref)
284✔
37
        ex0 = replace_ref_begin_end!(ex0)
×
38
    end
39
    # assignments get bypassed: @edit a = f(x) <=> @edit f(x)
40
    if isa(ex0, Expr) && ex0.head == :(=) && isa(ex0.args[1], Symbol) && isempty(kws)
152✔
41
        return gen_call_with_extracted_types(__module__, fcn, ex0.args[2])
×
42
    end
43
    if isa(ex0, Expr)
152✔
44
        if ex0.head === :do && Meta.isexpr(get(ex0.args, 1, nothing), :call)
152✔
45
            if length(ex0.args) != 2
152✔
46
                return Expr(:call, :error, "ill-formed do call")
×
47
            end
48
            i = findlast(a->(Meta.isexpr(a, :kw) || Meta.isexpr(a, :parameters)), ex0.args[1].args)
338✔
49
            args = copy(ex0.args[1].args)
128✔
50
            insert!(args, (isnothing(i) ? 2 : 1+i::Int), ex0.args[2])
128✔
51
            ex0 = Expr(:call, args...)
24✔
52
        end
53
        if ex0.head === :. || (ex0.head === :call && ex0.args[1] !== :.. && string(ex0.args[1])[1] == '.')
328✔
54
            codemacro = startswith(string(fcn), "code_")
×
55
            if codemacro && (ex0.head === :call || ex0.args[2] isa Expr)
×
56
                # Manually wrap a dot call in a function
57
                args = Any[]
×
58
                ex, i = recursive_dotcalls!(copy(ex0), args)
×
59
                xargs = [Symbol('x', j) for j in 1:i-1]
×
60
                dotfuncname = gensym("dotfunction")
×
61
                dotfuncdef = Expr(:local, Expr(:(=), Expr(:call, dotfuncname, xargs...), ex))
×
62
                return quote
×
63
                    $(esc(dotfuncdef))
64
                    local args = $typesof($(map(esc, args)...))
65
                    $(fcn)($(esc(dotfuncname)), args; $(kws...))
66
                end
67
            elseif !codemacro
×
68
                fully_qualified_symbol = true # of the form A.B.C.D
×
69
                ex1 = ex0
×
70
                while ex1 isa Expr && ex1.head === :.
×
71
                    fully_qualified_symbol = (length(ex1.args) == 2 &&
×
72
                                              ex1.args[2] isa QuoteNode &&
73
                                              ex1.args[2].value isa Symbol)
74
                    fully_qualified_symbol || break
×
75
                    ex1 = ex1.args[1]
×
76
                end
×
77
                fully_qualified_symbol &= ex1 isa Symbol
×
78
                if fully_qualified_symbol
×
79
                    return quote
×
80
                        local arg1 = $(esc(ex0.args[1]))
81
                        if isa(arg1, Module)
82
                            $(if string(fcn) == "which"
83
                                  :(which(arg1, $(ex0.args[2])))
×
84
                              else
85
                                  :(error("expression is not a function call"))
×
86
                              end)
87
                        else
88
                            local args = $typesof($(map(esc, ex0.args)...))
89
                            $(fcn)(Base.getproperty, args)
90
                        end
91
                    end
92
                else
93
                    return Expr(:call, :error, "dot expressions are not lowered to "
×
94
                                * "a single function call, so @$fcn cannot analyze "
95
                                * "them. You may want to use Meta.@lower to identify "
96
                                * "which function call to target.")
97
                end
98
            end
99
        end
100
        if any(a->(Meta.isexpr(a, :kw) || Meta.isexpr(a, :parameters)), ex0.args)
627✔
101
            return quote
×
102
                local arg1 = $(esc(ex0.args[1]))
103
                local args, kwargs = $separate_kwargs($(map(esc, ex0.args[2:end])...))
104
                $(fcn)(Core.kwcall,
105
                       Tuple{typeof(kwargs), Core.Typeof(arg1), map(Core.Typeof, args)...};
106
                       $(kws...))
107
            end
108
        elseif ex0.head === :call
×
109
            return Expr(:call, fcn, esc(ex0.args[1]),
×
110
                        Expr(:call, typesof, map(esc, ex0.args[2:end])...),
111
                        kws...)
112
        elseif ex0.head === :(=) && length(ex0.args) == 2
×
113
            lhs, rhs = ex0.args
×
114
            if isa(lhs, Expr)
×
115
                if lhs.head === :(.)
×
116
                    return Expr(:call, fcn, Base.setproperty!,
×
117
                                Expr(:call, typesof, map(esc, lhs.args)..., esc(rhs)), kws...)
118
                elseif lhs.head === :ref
×
119
                    return Expr(:call, fcn, Base.setindex!,
×
120
                                Expr(:call, typesof, esc(lhs.args[1]), esc(rhs), map(esc, lhs.args[2:end])...), kws...)
121
                end
122
            end
123
        elseif ex0.head === :vcat || ex0.head === :typed_vcat
×
124
            if ex0.head === :vcat
×
125
                f, hf = Base.vcat, Base.hvcat
×
126
                args = ex0.args
×
127
            else
128
                f, hf = Base.typed_vcat, Base.typed_hvcat
×
129
                args = ex0.args[2:end]
×
130
            end
131
            if any(a->isa(a,Expr) && a.head === :row, args)
×
132
                rows = Any[ (isa(x,Expr) && x.head === :row ? x.args : Any[x]) for x in args ]
×
133
                lens = map(length, rows)
×
134
                return Expr(:call, fcn, hf,
×
135
                            Expr(:call, typesof,
136
                                 (ex0.head === :vcat ? [] : Any[esc(ex0.args[1])])...,
137
                                 Expr(:tuple, lens...),
138
                                 map(esc, vcat(rows...))...), kws...)
139
            else
140
                return Expr(:call, fcn, f,
×
141
                            Expr(:call, typesof, map(esc, ex0.args)...), kws...)
142
            end
143
        else
144
            for (head, f) in (:ref => Base.getindex, :hcat => Base.hcat, :(.) => Base.getproperty, :vect => Base.vect, Symbol("'") => Base.adjoint, :typed_hcat => Base.typed_hcat, :string => string)
×
145
                if ex0.head === head
×
146
                    return Expr(:call, fcn, f,
×
147
                                Expr(:call, typesof, map(esc, ex0.args)...), kws...)
148
                end
149
            end
×
150
        end
151
    end
152
    if isa(ex0, Expr) && ex0.head === :macrocall # Make @edit @time 1+2 edit the macro by using the types of the *expressions*
×
153
        return Expr(:call, fcn, esc(ex0.args[1]), Tuple{#=__source__=#LineNumberNode, #=__module__=#Module, Any[ Core.Typeof(a) for a in ex0.args[3:end] ]...}, kws...)
×
154
    end
155

156
    ex = Meta.lower(__module__, ex0)
×
157
    if !isa(ex, Expr)
×
158
        return Expr(:call, :error, "expression is not a function call or symbol")
×
159
    end
160

161
    exret = Expr(:none)
×
162
    if ex.head === :call
×
163
        if any(e->(isa(e, Expr) && e.head === :(...)), ex0.args) &&
×
164
            (ex.args[1] === GlobalRef(Core,:_apply_iterate) ||
165
             ex.args[1] === GlobalRef(Base,:_apply_iterate))
166
            # check for splatting
167
            exret = Expr(:call, ex.args[2], fcn,
×
168
                        Expr(:tuple, esc(ex.args[3]),
169
                            Expr(:call, typesof, map(esc, ex.args[4:end])...)))
170
        else
171
            exret = Expr(:call, fcn, esc(ex.args[1]),
×
172
                         Expr(:call, typesof, map(esc, ex.args[2:end])...), kws...)
173
        end
174
    end
175
    if ex.head === :thunk || exret.head === :none
×
176
        exret = Expr(:call, :error, "expression is not a function call, "
×
177
                                  * "or is too complex for @$fcn to analyze; "
178
                                  * "break it down to simpler parts if possible. "
179
                                  * "In some cases, you may want to use Meta.@lower.")
180
    end
181
    return exret
×
182
end
183

184
"""
185
Same behaviour as `gen_call_with_extracted_types` except that keyword arguments
186
of the form "foo=bar" are passed on to the called function as well.
187
The keyword arguments must be given before the mandatory argument.
188
"""
189
function gen_call_with_extracted_types_and_kwargs(__module__, fcn, ex0)
20✔
190
    kws = Expr[]
20✔
191
    arg = ex0[end] # Mandatory argument
20✔
192
    for i in 1:length(ex0)-1
20✔
193
        x = ex0[i]
×
194
        if x isa Expr && x.head === :(=) # Keyword given of the form "foo=bar"
×
195
            if length(x.args) != 2
×
196
                return Expr(:call, :error, "Invalid keyword argument: $x")
×
197
            end
198
            push!(kws, Expr(:kw, esc(x.args[1]), esc(x.args[2])))
×
199
        else
200
            return Expr(:call, :error, "@$fcn expects only one non-keyword argument")
×
201
        end
202
    end
×
203
    return gen_call_with_extracted_types(__module__, fcn, arg, kws)
20✔
204
end
205

206
for fname in [:which, :less, :edit, :functionloc]
207
    @eval begin
208
        macro ($fname)(ex0)
5✔
209
            gen_call_with_extracted_types(__module__, $(Expr(:quote, fname)), ex0)
5✔
210
        end
211
    end
212
end
213

214
macro which(ex0::Symbol)
215
    ex0 = QuoteNode(ex0)
216
    return :(which($__module__, $ex0))
217
end
218

219
for fname in [:code_warntype, :code_llvm, :code_native, :infer_effects]
220
    @eval begin
221
        macro ($fname)(ex0...)
222
            gen_call_with_extracted_types_and_kwargs(__module__, $(Expr(:quote, fname)), ex0)
223
        end
224
    end
225
end
226

227
macro code_typed(ex0...)
2✔
228
    thecall = gen_call_with_extracted_types_and_kwargs(__module__, :code_typed, ex0)
2✔
229
    quote
2✔
230
        local results = $thecall
231
        length(results) == 1 ? results[1] : results
232
    end
233
end
234

235
macro code_lowered(ex0...)
9✔
236
    thecall = gen_call_with_extracted_types_and_kwargs(__module__, :code_lowered, ex0)
9✔
237
    quote
9✔
238
        local results = $thecall
239
        length(results) == 1 ? results[1] : results
240
    end
241
end
242

243
macro time_imports(ex)
244
    quote
245
        try
246
            Base.Threads.atomic_add!(Base.TIMING_IMPORTS, 1)
247
            $(esc(ex))
248
        finally
249
            Base.Threads.atomic_sub!(Base.TIMING_IMPORTS, 1)
250
        end
251
    end
252
end
253

254
"""
255
    @functionloc
256

257
Applied to a function or macro call, it evaluates the arguments to the specified call, and
258
returns a tuple `(filename,line)` giving the location for the method that would be called for those arguments.
259
It calls out to the [`functionloc`](@ref) function.
260
"""
261
:@functionloc
262

263
"""
264
    @which
265

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

271
See also: [`@less`](@ref), [`@edit`](@ref).
272
"""
273
:@which
274

275
"""
276
    @less
277

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

281
See also: [`@edit`](@ref), [`@which`](@ref), [`@code_lowered`](@ref).
282
"""
283
:@less
284

285
"""
286
    @edit
287

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

291
See also: [`@less`](@ref), [`@which`](@ref).
292
"""
293
:@edit
294

295
"""
296
    @code_typed
297

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

301
    @code_typed optimize=true foo(x)
302

303
to control whether additional optimizations, such as inlining, are also applied.
304

305
See also: [`code_typed`](@ref), [`@code_warntype`](@ref), [`@code_lowered`](@ref), [`@code_llvm`](@ref), [`@code_native`](@ref).
306
"""
307
:@code_typed
308

309
"""
310
    @code_lowered
311

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

315
See also: [`code_lowered`](@ref), [`@code_warntype`](@ref), [`@code_typed`](@ref), [`@code_llvm`](@ref), [`@code_native`](@ref).
316
"""
317
:@code_lowered
318

319
"""
320
    @code_warntype
321

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

325
See also: [`code_warntype`](@ref), [`@code_typed`](@ref), [`@code_lowered`](@ref), [`@code_llvm`](@ref), [`@code_native`](@ref).
326
"""
327
:@code_warntype
328

329
"""
330
    @code_llvm
331

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

337
    @code_llvm raw=true dump_module=true debuginfo=:default f(x)
338
    @code_llvm optimize=false f(x)
339

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

345
See also: [`code_llvm`](@ref), [`@code_warntype`](@ref), [`@code_typed`](@ref), [`@code_lowered`](@ref), [`@code_native`](@ref).
346
"""
347
:@code_llvm
348

349
"""
350
    @code_native
351

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

355
Set any of the optional keyword arguments `syntax`, `debuginfo`, `binary` or `dump_module`
356
by putting it before the function call, like this:
357

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

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

365
See also: [`code_native`](@ref), [`@code_warntype`](@ref), [`@code_typed`](@ref), [`@code_lowered`](@ref), [`@code_llvm`](@ref).
366
"""
367
:@code_native
368

369
"""
370
    @time_imports
371

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

375
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.
376

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

379
!!! note
380
    During the load process a package sequentially imports all of its dependencies, not just its direct dependencies.
381

382
```julia-repl
383
julia> @time_imports using CSV
384
     50.7 ms  Parsers 17.52% compilation time
385
      0.2 ms  DataValueInterfaces
386
      1.6 ms  DataAPI
387
      0.1 ms  IteratorInterfaceExtensions
388
      0.1 ms  TableTraits
389
     17.5 ms  Tables
390
     26.8 ms  PooledArrays
391
    193.7 ms  SentinelArrays 75.12% compilation time
392
      8.6 ms  InlineStrings
393
     20.3 ms  WeakRefStrings
394
      2.0 ms  TranscodingStreams
395
      1.4 ms  Zlib_jll
396
      1.8 ms  CodecZlib
397
      0.8 ms  Compat
398
     13.1 ms  FilePathsBase 28.39% compilation time
399
   1681.2 ms  CSV 92.40% compilation time
400
```
401

402
!!! compat "Julia 1.8"
403
    This macro requires at least Julia 1.8
404

405
"""
406
:@time_imports
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