Repository: JuliaLang/julia
File: stdlib/InteractiveUtils/src/macros.jl (Julia)

Code before:
    end
    if ex.head === :thunk || exret.head === :none
        exret = Expr(:call, :error, "expression is not a function call, \
                                    or is too complex for @$fcn to analyze; \
                                    break it down to simpler parts if possible. \
                                    In some cases, you may want to use Meta.@lower.")
    end
    return exret
end

"""
Same behaviour as `gen_call_with_extracted_types` except that keyword arguments
of the form "foo=bar" are passed on to the called function as well.
The keyword arguments must be given before the mandatory argument.
"""
function gen_call_with_extracted_types_and_kwargs(__module__, fcn, ex0; is_source_reflection = !is_code_macro(fcn), supports_binding_reflection = false)
    kws = Expr[]
    arg = ex0[end] # Mandatory argument
    for i in 1:length(ex0)-1
        x = ex0[i]
        if x isa Expr && x.head === :(=) # Keyword given of the form "foo=bar"
            if length(x.args) != 2
                return Expr(:call, :error, "Invalid keyword argument: $x")
            end
            push!(kws, Expr(:kw, esc(x.args[1]), esc(x.args[2])))
        else
            return Expr(:call, :error, "@$fcn expects only one non-keyword argument")
        end
    end
    return gen_call_with_extracted_types(__module__, fcn, arg, kws; is_source_reflection, supports_binding_reflection)
end

for fname in [:which, :less, :edit, :functionloc]
    @eval begin
        macro ($fname)(ex0)
            gen_call_with_extracted_types(__module__, $(Expr(:quote, fname)), ex0, Expr[];
                                          is_source_reflection = true,
                                          supports_binding_reflection = $(fname === :which))
        end
    end
end

macro which(ex0::Symbol)
    ex0 = QuoteNode(ex0)
    return :(which($__module__, $ex0))
end

for fname in [:code_warntype, :code_llvm, :code_native,
              :infer_return_type, :infer_effects, :infer_exception_type]
    @eval macro ($fname)(ex0...)
        gen_call_with_extracted_types_and_kwargs(__module__, $(QuoteNode(fname)), ex0; is_source_reflection = false)

Review comment (bug):
Style (and performance) nit: we tend to use `...` instead of `;` for explicit concat, and an explicit `Any` type:
```
                call_reflection = gen_call(fcn, Any[getproperty, ex0.args...], where_params, kws; use_signature_tuple)
```

This seems to have been detected as a issue elsewhere already, but then incorrectly worked around by splatting `ex0.args` to undo the mistake here:
```
function gen_call(fcn, args, where_params, kws; use_signature_tuple::Bool, not_an_opaque_closure::Bool = true)
    f, args... = args
    args = collect(Any, args)
```

Code after:
            dotfuncname = gensym("dotfunction")
            call = gen_call(fcn, Any[dotfuncname, args...], where_params, kws; use_signature_tuple)
            return quote
                let $(esc(:($dotfuncname($(xargs...)) = $ex)))
                    $call
                end
            end
        elseif isexpr(ex0, :.) && is_source_reflection
            # If `ex0` has the form A.B (or some chain A.B.C.D) and `fcn` reflects into the source,
            # `A` (or `A.B.C`) may be a module, in which case `fcn` is probably more interested in
            # the binding rather than the `getproperty` call.
            # If binding reflection is not supported, we generate an error; `getproperty(::Module, field)`
            # is not going to be interesting to reflect into, so best to allow future non-breaking support
            # for binding reflection in case the macro may eventually support that.
            fully_qualified_symbol = true
            ex1 = ex0
            while ex1 isa Expr && ex1.head === :.
                fully_qualified_symbol = (length(ex1.args) == 2 &&
                                            ex1.args[2] isa QuoteNode &&
                                            ex1.args[2].value isa Symbol)
                fully_qualified_symbol || break
                ex1 = ex1.args[1]
            end
            fully_qualified_symbol &= ex1 isa Symbol
            if fully_qualified_symbol || isexpr(ex1, :(::), 1)
                call_reflection = gen_call(fcn, [getproperty; ex0.args], where_params, kws; use_signature_tuple)
                isexpr(ex0.args[1], :(::), 1) && return call_reflection
                if supports_binding_reflection
                    binding_reflection = :($fcn(arg1, $(ex0.args[2]); $(kws...)))
                else
                    binding_reflection = :(error("expression is not a function call"))
                end
                return quote
                    local arg1 = $(esc(ex0.args[1]))
                    if isa(arg1, Module)
                        $binding_reflection
                    else
                        $call_reflection
                    end
                end
            end
        end
        if is_broadcasting_expr(ex0)
            return Expr(:call, :error, "dot expressions are not lowered to "
                * "a single function call, so @$fcn cannot analyze "
                * "them. You may want to use Meta.@lower to identify "
                * "which function call to target.")
        end
        if any(@nospecialize(a)->(isexpr(a, :kw) || isexpr(a, :parameters)), ex0.args)
            args, kwargs = separate_kwargs(ex0.args)
            are_kwargs_valid(kwargs) || return quote