Repository: JuliaLang/julia
File: JuliaLowering/src/compat.jl (Julia)

Code before:
        head = Symbol(k === K"unknown_head" ? st.name_val : untokenize(k))
        out = Expr(head)

        # (Move the following assumptions to the docs if they turn out accurate)
        # The only mandatory LineNumberNode is the second macrocall argument.
        # Other than that, optional linenodes may show up anywhere within:
        # - `block`, unless the block is the first child of `for` or `let`
        # - `toplevel`
        # Macro authors are responsible for handling any linenodes that follow
        # the rules above (but the presence of optional linenodes can't be
        # counted upon).
        need_lnns = head in (:block, :toplevel) && !suppress_linenodes
        for (i, c) in enumerate(children(st))
            need_lnns && push!(out.args, source_location(LineNumberNode, c))
            let suppress_c = i == 1 && (k == K"for" || k == K"let")
                push!(out.args, est_to_expr(c, suppress_c))
            end
        end
        # Add extra linenodes to some blocks for better provenance
        @stm st begin
            ([K"block"], when=!suppress_linenodes) ->
                push!(out.args, source_location(LineNumberNode, st))
            [K"module" _... [K"block" _...]] ->
                pushfirst!(out.args[end].args, source_location(LineNumberNode, st))
            [K"function" _ [K"block" _...]] ->
                pushfirst!(out.args[end].args, source_location(LineNumberNode, st))
            [K"macro" _ [K"block" _...]] ->
                pushfirst!(out.args[end].args, source_location(LineNumberNode, st))
            [K"for" _ [K"block" _...]] ->
                push!(out.args[end].args, source_location(
                    LineNumberNode, sourcefile(st), last_byte(st)))
            [K"while" _ [K"block" _...]] ->
                push!(out.args[end].args, source_location(
                    LineNumberNode, sourcefile(st), last_byte(st)))
            _ -> nothing
        end
        out
    end
end

#-------------------------------------------------------------------------------
# EST->DST

# .op => (. op)
function _dst_separate_dotop(st::SyntaxTree)
    k = kind(st)
    if k === K"Identifier" && is_dotted_operator(st.name_val)
        dotop_s = st.name_val
        op_s = dotop_s[nextind(dotop_s,1):end]
        op_leaf = setattr(mkleaf(st), :name_val, op_s)
        return @ast st._graph st [K"." op_leaf]

Review comment (suggestion):
```suggestion
            # all following cases ignore `suppress_linenodes`
            # (it only applies to the immediate expression, not transitively to children)
            [K"module" _... [K"block" _...]] ->
```

Code after:
        @assert !is_leaf(st) "est_to_expr should only be used pre-desugaring"
        # In a partially-expanded or quoted AST, there may be heads with no
        # corresponding kind
        head = Symbol(k === K"unknown_head" ? st.name_val : untokenize(k))
        out = Expr(head)

        # (Move the following assumptions to the docs if they turn out accurate)
        # The only mandatory LineNumberNode is the second macrocall argument.
        # Other than that, optional linenodes may show up anywhere within:
        # - `block`, unless the block is the first child of `for` or `let`
        # - `toplevel`
        # Macro authors are responsible for handling any linenodes that follow
        # the rules above (but the presence of optional linenodes can't be
        # counted upon).
        need_lnns = head in (:block, :toplevel) && !suppress_linenodes
        for (i, c) in enumerate(children(st))
            need_lnns && push!(out.args, source_location(LineNumberNode, c))
            let suppress_c = i == 1 && (k == K"for" || k == K"let")
                push!(out.args, est_to_expr(c, suppress_c))
            end
        end
        # Add extra linenodes to some blocks for better provenance
        @stm st begin
            ([K"block"], when=!suppress_linenodes) ->
                push!(out.args, source_location(LineNumberNode, st))
            [K"module" _... [K"block" _...]] ->
                pushfirst!(out.args[end].args, source_location(LineNumberNode, st))
            [K"function" _ [K"block" _...]] ->
                pushfirst!(out.args[end].args, source_location(LineNumberNode, st))
            [K"macro" _ [K"block" _...]] ->
                pushfirst!(out.args[end].args, source_location(LineNumberNode, st))
            [K"for" _ [K"block" _...]] ->
                push!(out.args[end].args, source_location(
                    LineNumberNode, sourcefile(st), last_byte(st)))
            [K"while" _ [K"block" _...]] ->
                push!(out.args[end].args, source_location(
                    LineNumberNode, sourcefile(st), last_byte(st)))
            _ -> nothing
        end
        out
    end
end

#-------------------------------------------------------------------------------
# EST->DST

# .op => (. op)
function _dst_separate_dotop(st::SyntaxTree)
    k = kind(st)
    if k === K"Identifier" && is_dotted_operator(st.name_val)
        dotop_s = st.name_val