# Can Julia's macros recurse?

**URL:** https://discourse.julialang.org/t/can-julias-macros-recurse/74024
**Category:** General Usage
**Tags:** macros, metaprogramming, graphs
**Created:** [January 4, 2022, 11:00am UTC](https://discourse.julialang.org/t/can-julias-macros-recurse/74024 "2022-01-04T11:00:40Z")
**Posts on this page:** 3
**Page:** 1

<div class="post-metadata">

### Author: ![danielsoutar](https://avatars.discourse-cdn.com/v4/letter/d/e480ec/32.png) [@danielsoutar](https://discourse.julialang.org/u/danielsoutar)
#### Post date: [January 4, 2022, 11:00am UTC](https://discourse.julialang.org/t/can-julias-macros-recurse/74024/1 "2022-01-04T11:00:40Z")

</div>

Hi there, stoked to be finally be using Julia. I had a question regarding how powerful Julia’s macros can be. I already asked on Stack Overflow, but figured worth pinging here as well.

Basically, I was wondering if it was possible to extract a function’s reachable call graph without fully compiling the code, i.e. with macros. By ‘reachable call graph’, I mean all of the functions (more generally, functors) found in the body of the function that are potentially reachable, and the functions/functors in the bodies of those, and so on. This would be much easier if macros can recurse into the functors found in the caller’s body.

Some code might demonstrate this idea better:

```julia
do_something(x::Int) = println(x*2)
do_something(x::String) = println(x)

function foo(a, b)::Bool
    do_something_with(a)
    do_something_with(b)
    return true
end

# Ideally something like this would be accessible from Base
functions_in_codebase = [:do_something, :foo]

# Not correct, but gets the idea across
macro access_call_graph(f)
    tokens = f.text.tokens
    graph = [f]
    for t in tokens
        go_deeper = t in functions_in_codebase
        !go_deeper && append!(graph, access_call_graph(get_function_for(t))...)
    end
    return graph
end

@access_call_graph(foo)
# Should get something like the following, nesting notwithstanding:
# foo
# do_something, do_something

```

If you can access any kind of call-graph at parse-time, you could use this information to compute the intersection of a Git diff’s changed/modified functions and the union of its call graphs with those of tests, thus allowing automatic filtering and only running tests that are functionally affected by your change. Something that achieves the same as what [Mozilla did with Firefox](https://hacks.mozilla.org/2020/07/testing-firefox-more-efficiently-with-machine-learning/). Instead of ML however we’d be leveraging the expressiveness of Julia itself 🙂

I am aware this sounds like the ramblings of a mad lunatic and just the issue of multiple dispatch alone is terrifying but I’d love to know if something like this could conceivably work.

---

<div class="post-metadata">

### Author: ![jzr](https://avatars.discourse-cdn.com/v4/letter/j/eb9ed0/32.png) [@jzr](https://discourse.julialang.org/u/jzr)
#### Post date: [January 4, 2022, 11:26am UTC](https://discourse.julialang.org/t/can-julias-macros-recurse/74024/2 "2022-01-04T11:26:28Z")

</div>

Macros don’t do what you’re saying but other tools do. Check out Cthulu.jl

---

<div class="post-metadata">

### Author: ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)
#### Post date: [January 4, 2022, 12:52pm UTC](https://discourse.julialang.org/t/can-julias-macros-recurse/74024/3 "2022-01-04T12:52:06Z")

</div>

> [@danielsoutar](#):
>
> This would be much easier if macros can recurse into the functors found in the caller’s body.

Recursively traversing the _expression_ tree is pretty common and convenient. The easiest way to do it is for the macro to call a function to walk the expression tree. See, for example, [how the `@views` macro is implemented](https://github.com/JuliaLang/julia/blob/2ebbb2b3a0b54eae66549bd058ae334cb3642e50/base/views.jl#L140-L244). The [MacroTools.jl package](https://github.com/FluxML/MacroTools.jl) provides some helpful abstractions for this.

That being said, at _parse_ time you don’t have access to the _call graph_ — the parser (and hence macros) only knows how things are “spelled”, not what anything _means_ (e.g. it doesn’t know what called functions refer to). To access the call graph, you need something at a later stage of compilation/interpretation, e.g. Cthulu.jl or Cassette.jl.
