Finding `Expr`s with docstrings

How do I detect when an Expr has a docstring?

For example, if

expr = quote
    """Here's a docstring!"""
    const a=1
end

I can look at the result to see that it is translated into a macrocall of @doc (after skipping the first, blank line:

julia> expr2 = expr.args[2]
:(#= REPL[1]:2 =# Core.@doc "Here's a docstring!" const a = 1)

julia> expr2.head
:macrocall

julia> expr2.args
4-element Vector{Any}:
 :(Core.var"@doc")
 :(#= REPL[1]:2 =#)
 "Here's a docstring!"
 :(const a = 1)

All this makes sense.

But now, suppose I want to detect that some Expr with :macrocall head is a call to @doc. The obvious thing to try, given the above, is to test the first element of expr2.args:

julia> expr2.args[1] == :(Core.var"@doc")
false

I literally just copied the expression, but it’s not that?!


Okay, dumping it shows me that expr2.args[1] is actually a GlobalRef with name field Symbol("@doc"). So I could do

julia> expr2.args[1] isa GlobalRef && expr2.args[1].name==Symbol("@doc")
true

This seems really ugly. Is this the best way? Will I miss some possibilities?

You could use inexpr from MacroTools.jl:

julia> inexpr(expr, Core.GlobalRef(Core, Symbol("@doc")))
true

It looks like inexpr will find such an expression anywhere inside a larger expression, but other than that is this different in any significant way from testing for equality like I did? In particular, I’m worried about how minor changes in syntax always seem to drastically change an Expr.


On that note, I found a similar expression that would fail this test and mine:

julia> expr = quote
           @doc """Here's a docstring!"""
           const a=1
       end;

julia> inexpr(expr, Core.GlobalRef(Core, Symbol("@doc")))
false

All I did was add @doc. :roll_eyes:

I think there is no way around it. In your second example you would have to say

julia> inexpr(expr, Symbol("@doc"))
true

But that doesn’t work for

expr = quote
    Base.@doc """Here's a docstring!"""
    const a=1
end

I doubt that there is a universal solution.