Scope of module docstring

Why does the following work?

"""
$(EXPORTS)
"""
module Foo
using DocStringExtensions
export bar
bar() = 1
end

Specifically, is the module docstring somehow scoped within the module, so that EXPORTS works?

I suspect that this is the case, however I cannot find it in the Julia docs.

(see a more extensive example).

The docstring is attached from the inside of the module expression:

julia> Meta.@lower begin
       """
       $(EXPORTS)
       """
       module Foo
       using DocStringExtensions
       export bar
       bar() = 1
       end
       end
:($(Expr(:thunk, CodeInfo(
    @ REPL[2]:2 within `top-level scope`
1 ─ %1 = $(Expr(:toplevel, :(module Foo
  #= REPL[2]:5 =#
  #= REPL[2]:6 =#
  using DocStringExtensions
  #= REPL[2]:7 =#
  export bar
  #= REPL[2]:8 =#
  bar() = begin
          #= REPL[2]:8 =#
          1
      end
  (Base.Docs.doc!)(Foo, (Base.Docs.Binding)((var"@__MODULE__")(), :Foo), (Base.Docs.docstr)((Core.svec)(EXPORTS, "\n"), (Dict{Symbol, Any})(:path => "REPL[2]", :linenumber => 2, (Pair)(:module, Foo))))
  end)))
└──      return %1
))))
3 Likes

Thanks. Do you know if this is documented (ie something that users can rely on), or an accidental implementation feature? I could not find it in the docs.

I don’t think so. I wasn’t aware of this behavior until you posted.

1 Like

It’s in the docs by implication:

"..."
module M end

module M

"..."
M

end

Adds docstring "..." to the Module M. Adding the docstring above the Module is the preferred syntax, however both are equivalent.

The docstring definitely belongs to the scope of the documented module in the 2nd example, and that must also be the case in the 1st example for equivalence. None of the sections for other scope-introducing blocks share this pattern.

A minimal base Julia-only example:

julia> module A
         x = "a"
         "$x"
         module B
           x = "b"
         end
       end
Main.A

julia> @doc A.B
  b

I think it is better to document this explicitly, so I made a tiny PR.