Skip to content

Commit 9116ec7

Browse files
committed
pass correct paths through to docstrings
fix #22105
1 parent 67b9b3e commit 9116ec7

File tree

7 files changed

+96
-80
lines changed

7 files changed

+96
-80
lines changed

Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ $(build_private_libdir)/%.$(SHLIB_EXT): $(build_private_libdir)/%.o
186186

187187
CORE_SRCS := $(addprefix $(JULIAHOME)/, \
188188
base/boot.jl base/coreimg.jl \
189+
base/docs/core.jl \
189190
base/abstractarray.jl \
190191
base/array.jl \
191192
base/bool.jl \

base/boot.jl

+2-2
Original file line numberDiff line numberDiff line change
@@ -337,15 +337,15 @@ end
337337

338338
# docsystem basics
339339
macro doc(x...)
340-
atdoc(x...)
340+
atdoc(__source__, x...)
341341
end
342342
macro __doc__(x)
343343
Expr(:escape, Expr(:block, Expr(:meta, :doc), x))
344344
end
345345
macro doc_str(s)
346346
Expr(:escape, s)
347347
end
348-
atdoc = (str, expr) -> Expr(:escape, expr)
348+
atdoc = (source, str, expr) -> Expr(:escape, expr)
349349
atdoc!(λ) = global atdoc = λ
350350

351351

base/docs/Docs.jl

+37-37
Original file line numberDiff line numberDiff line change
@@ -169,10 +169,10 @@ _docstr(doc::DocStr, data) = (doc.data = merge(data, doc.data); doc)
169169
macro ref(x)
170170
binding = bindingexpr(namify(x))
171171
typesig = signature(x)
172-
esc(docexpr(binding, typesig))
172+
esc(docexpr(__source__, binding, typesig))
173173
end
174174

175-
docexpr(args...) = Expr(:call, docstr, args...)
175+
docexpr(__source__, args...) = Expr(:call, docstr, args...)
176176

177177
function formatdoc(d::DocStr)
178178
buffer = IOBuffer()
@@ -482,26 +482,27 @@ isfield(x) = isexpr(x, :.) &&
482482
# =========================
483483

484484
"""
485-
Docs.metadata(expr)
485+
Docs.metadata(source, expr)
486486
487487
Build a `Dict` expression containing metadata captured from the expression `expr`.
488488
489489
Fields that may be included in the returned `Dict`:
490490
491-
- `:path`: String representing the file where `expr` is defined.
491+
- `:path`: Symbol representing the file where `expr` is defined.
492492
- `:linenumber`: Linenumber where `expr` is defined.
493493
- `:module`: Module where the docstring is defined.
494494
- `:fields`: `Dict` of all field docs found in `expr`. Only for concrete types.
495495
"""
496-
function metadata(expr)
496+
function metadata(__source__, expr)
497497
args = []
498498
# Filename and linenumber of the docstring.
499-
push!(args, :($(Pair)(:path, $(Base).@__FILE__)))
500-
push!(args, :($(Pair)(:linenumber, $(unsafe_load(cglobal(:jl_lineno, Cint))))))
499+
__file__ = isa(__source__.file, Symbol) ? String(__source__.file) : ""
500+
push!(args, Pair(:path, __file__))
501+
push!(args, Pair(:linenumber, __source__.line))
501502
# Module in which the docstring is defined.
502503
push!(args, :($(Pair)(:module, $(current_module)())))
503-
# Field docs for concrete types.
504504
if isexpr(expr, :type)
505+
# Field docs for concrete types.
505506
fields = []
506507
tmp = nothing
507508
for each in expr.args[3].args
@@ -518,37 +519,36 @@ function metadata(expr)
518519
:($(Dict)($(args...)))
519520
end
520521

521-
function keyworddoc(str, def)
522-
docstr = esc(docexpr(lazy_iterpolate(str), metadata(def)))
523-
:($(keywords)[$(esc(quot(def.name)))] = $docstr)
522+
function keyworddoc(__source__, str, def)
523+
docstr = esc(docexpr(__source__, lazy_iterpolate(str), metadata(__source__, def)))
524+
return :($(keywords)[$(esc(quot(def.name)))] = $docstr)
524525
end
525526

526-
function objectdoc(str, def, expr, sig = :(Union{}))
527+
function objectdoc(__source__, str, def, expr, sig = :(Union{}))
527528
binding = esc(bindingexpr(namify(expr)))
528-
docstr = esc(docexpr(lazy_iterpolate(str), metadata(expr)))
529+
docstr = esc(docexpr(__source__, lazy_iterpolate(str), metadata(__source__, expr)))
529530
quote
530531
$(esc(def))
531532
$(doc!)($binding, $docstr, $(esc(sig)))
532533
end
533534
end
534535

535-
function calldoc(str, def)
536+
function calldoc(__source__, str, def)
536537
args = def.args[2:end]
537538
if isempty(args) || all(validcall, args)
538-
objectdoc(str, nothing, def, signature(def))
539+
objectdoc(__source__, str, nothing, def, signature(def))
539540
else
540541
docerror(def)
541542
end
542543
end
543544
validcall(x) = isa(x, Symbol) || isexpr(x, (:(::), :..., :kw, :parameters))
544545

545-
function moduledoc(meta, def, def′)
546+
function moduledoc(__source__, meta, def, def′)
546547
name = namify(def′)
547548
docex = Expr(:call, doc!, bindingexpr(name),
548-
docexpr(lazy_iterpolate(meta), metadata(name))
549-
)
549+
docexpr(__source__, lazy_iterpolate(meta), metadata(__source__, name)))
550550
if def === nothing
551-
esc(:(eval($name, $(quot(docex)))))
551+
esc(:($eval($name, $(quot(docex)))))
552552
else
553553
def = unblock(def)
554554
block = def.args[3].args
@@ -562,18 +562,18 @@ function moduledoc(meta, def, def′)
562562
end
563563

564564
# Shares a single doc, `meta`, between several expressions from the tuple expression `ex`.
565-
function multidoc(meta, ex, define)
565+
function multidoc(__source__, meta, ex, define)
566566
out = Expr(:toplevel)
567-
str = docexpr(lazy_iterpolate(meta), metadata(ex))
567+
str = docexpr(__source__, lazy_iterpolate(meta), metadata(__source__, ex))
568568
ref = Ref{DocStr}()
569569
for (n, arg) in enumerate(ex.args)
570570
# The first `arg` to be documented needs to also create the docstring for the group.
571571
# Subsequent `arg`s just need `ref` to be able to find the docstring without having
572572
# to create an entirely new one each.
573573
docstr = n === 1 ? :($(ref)[] = $str) : :($(ref)[])
574-
push!(out.args, :(@doc($docstr, $arg, $define)))
574+
push!(out.args, docm(__source__, docstr, arg, define))
575575
end
576-
esc(out)
576+
return out
577577
end
578578

579579
"""
@@ -649,7 +649,7 @@ isquotedmacrocall(x) =
649649
isbasicdoc(x) = isexpr(x, :.) || isa(x, Union{QuoteNode, Symbol})
650650
is_signature(x) = isexpr(x, :call) || (isexpr(x, :(::), 2) && isexpr(x.args[1], :call)) || isexpr(x, :where)
651651

652-
function docm(meta, ex, define = true)
652+
function docm(source::LineNumberNode, meta, ex, define = true)
653653
# Some documented expressions may be decorated with macro calls which obscure the actual
654654
# expression. Expand the macro calls and remove extra blocks.
655655
x = unblock(macroexpand(ex))
@@ -665,7 +665,7 @@ function docm(meta, ex, define = true)
665665
# "..."
666666
# kw"if", kw"else"
667667
#
668-
isa(x, Base.BaseDocs.Keyword) ? keyworddoc(meta, x) :
668+
isa(x, Base.BaseDocs.Keyword) ? keyworddoc(source, meta, x) :
669669

670670
# Method / macro definitions and "call" syntax.
671671
#
@@ -675,38 +675,38 @@ function docm(meta, ex, define = true)
675675
# function f end
676676
# f(...)
677677
#
678-
isexpr(x, FUNC_HEADS) && is_signature(x.args[1]) ? objectdoc(meta, def, x, signature(x)) :
679-
isexpr(x, :function) && !isexpr(x.args[1], :call) ? objectdoc(meta, def, x) :
680-
isexpr(x, :call) ? calldoc(meta, x) :
678+
isexpr(x, FUNC_HEADS) && is_signature(x.args[1]) ? objectdoc(source, meta, def, x, signature(x)) :
679+
isexpr(x, :function) && !isexpr(x.args[1], :call) ? objectdoc(source, meta, def, x) :
680+
isexpr(x, :call) ? calldoc(source, meta, x) :
681681

682682
# Type definitions.
683683
#
684684
# type T ... end
685685
# abstract T
686686
# bitstype N T
687687
#
688-
isexpr(x, [:type, :abstract, :bitstype]) ? objectdoc(meta, def, x) :
688+
isexpr(x, [:type, :abstract, :bitstype]) ? objectdoc(source, meta, def, x) :
689689

690690
# "Bindings". Names that resolve to objects with different names, ie.
691691
#
692692
# const T = S
693693
# T = S
694694
# global T = S
695695
#
696-
isexpr(x, BINDING_HEADS) && !isexpr(x.args[1], :call) ? objectdoc(meta, def, x) :
696+
isexpr(x, BINDING_HEADS) && !isexpr(x.args[1], :call) ? objectdoc(source, meta, def, x) :
697697

698698
# Quoted macrocall syntax. `:@time` / `:(Base.@time)`.
699-
isquotedmacrocall(x) ? objectdoc(meta, def, x) :
699+
isquotedmacrocall(x) ? objectdoc(source, meta, def, x) :
700700
# Modules and baremodules.
701-
isexpr(x, :module) ? moduledoc(meta, def, x) :
701+
isexpr(x, :module) ? moduledoc(source, meta, def, x) :
702702
# Document several expressions with the same docstring. `a, b, c`.
703-
isexpr(x, :tuple) ? multidoc(meta, x, define) :
703+
isexpr(x, :tuple) ? multidoc(source, meta, x, define) :
704704
# Errors generated by calling `macroexpand` are passed back to the call site.
705705
isexpr(x, :error) ? esc(x) :
706706
# When documenting macro-generated code we look for embedded `@__doc__` calls.
707707
__doc__!(meta, x, define) ? esc(x) :
708708
# Any "basic" expression such as a bare function or module name or numeric literal.
709-
isbasicdoc(x) ? objectdoc(meta, nothing, x) :
709+
isbasicdoc(x) ? objectdoc(source, meta, nothing, x) :
710710

711711
# All other expressions are undocumentable and should be handled on a case-by-case basis
712712
# with `@__doc__`. Unbound string literals are also undocumentable since they cannot be
@@ -725,9 +725,9 @@ function docerror(ex)
725725
:($(error)($txt, "\n"))
726726
end
727727

728-
function docm(ex)
728+
function docm(source::LineNumberNode, ex)
729729
if isexpr(ex, :->)
730-
docm(ex.args...)
730+
docm(source, ex.args...)
731731
elseif haskey(keywords, ex)
732732
parsedoc(keywords[ex])
733733
elseif isa(ex, Union{Expr, Symbol})
@@ -764,7 +764,7 @@ function loaddocs(docs)
764764
for (mod, ex, str, file, line) in docs
765765
data = Dict(:path => string(file), :linenumber => line)
766766
doc = docstr(str, data)
767-
docstring = eval(mod, Expr(:body, Expr(:return, Expr(:call, QuoteNode(docm), QuoteNode(doc), QuoteNode(ex), false)))) # expand the real @doc macro now (using a hack because macroexpand takes current-module as an implicit argument)
767+
docstring = eval(mod, Expr(:body, Expr(:return, Expr(:call, QuoteNode(docm), QuoteNode(LineNumberNode(line, file)), QuoteNode(doc), QuoteNode(ex), false)))) # expand the real @doc macro now (using a hack because macroexpand takes current-module as an implicit argument)
768768
eval(mod, Expr(:macrocall, unescape, nothing, docstring))
769769
end
770770
empty!(docs)

base/docs/core.jl

+6-9
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,8 @@ module CoreDocs
44

55
import ..esc, ..push!, ..getindex, ..current_module, ..unsafe_load, ..Csize_t
66

7-
function doc!(str, ex)
8-
ptr = unsafe_load(Core.Intrinsics.cglobal(:jl_filename, Ptr{UInt8}))
9-
len = ccall(:strlen, Csize_t, (Ptr{UInt8},), ptr)
10-
file = ccall(:jl_symbol_n, Any, (Ptr{UInt8}, Csize_t), ptr, len)
11-
line = unsafe_load(Core.Intrinsics.cglobal(:jl_lineno, Int32)) # Cint
12-
push!(DOCS, (current_module(), ex, str, file, line))
7+
function doc!(source::LineNumberNode, str, ex)
8+
push!(DOCS, (current_module(), ex, str, source.file, source.line))
139
end
1410
const DOCS = Array{Any, 1}()
1511

@@ -18,11 +14,12 @@ isexpr(x, h) = isa(x, Expr) && x.head === h
1814
lazy_iterpolate(s::AbstractString) = Expr(:call, Core.svec, s)
1915
lazy_iterpolate(x) = isexpr(x, :string) ? Expr(:call, Core.svec, x.args...) : x
2016

21-
function docm(str, x)
22-
out = esc(Expr(:call, doc!, lazy_iterpolate(str), Expr(:quote, x)))
17+
function docm(source::LineNumberNode, str, x)
18+
out = esc(Expr(:call, doc!, QuoteNode(source), lazy_iterpolate(str), Expr(:quote, x)))
2319
isexpr(x, :module) ? Expr(:toplevel, out, esc(x)) :
2420
isexpr(x, :call) ? out : Expr(:block, esc(x), out)
2521
end
26-
docm(x) = isexpr(x, :->) ? docm(x.args[1], x.args[2].args[2]) : error("invalid '@doc'.")
22+
docm(source::LineNumberNode, x) =
23+
isexpr(x, :->) ? docm(source, x.args[1], x.args[2].args[2]) : error("invalid '@doc'.")
2724

2825
end

base/markdown/Markdown.jl

+9-11
Original file line numberDiff line numberDiff line change
@@ -37,25 +37,23 @@ function mdexpr(s, flavor = :julia)
3737
esc(toexpr(md))
3838
end
3939

40-
function docexpr(s, flavor = :julia)
41-
quote
42-
let md = $(mdexpr(s, flavor))
43-
md.meta[:path] = @__FILE__
44-
md.meta[:module] = current_module()
45-
md
46-
end
47-
end
40+
function docexpr(source::LineNumberNode, s, flavor = :julia)
41+
:($doc_str($(mdexpr(s, flavor)), $(QuoteNode(source))))
4842
end
4943

5044
macro md_str(s, t...)
5145
mdexpr(s, t...)
5246
end
5347

54-
doc_str(md, file, mod) = (md.meta[:path] = file; md.meta[:module] = mod; md)
55-
doc_str(md::AbstractString, file, mod) = doc_str(parse(md), file, mod)
48+
function doc_str(md, source::LineNumberNode)
49+
md.meta[:path] = isa(source.file, Symbol) ? String(source.file) : ""
50+
md.meta[:module] = current_module()
51+
md
52+
end
53+
doc_str(md::AbstractString, source::LineNumberNode) = doc_str(parse(md), source)
5654

5755
macro doc_str(s::AbstractString, t...)
58-
:($(doc_str)($(mdexpr(s, t...)), $(Base).@__FILE__, $(current_module)()))
56+
docexpr(__source__, s, t...)
5957
end
6058

6159
function Base.display(d::Base.REPL.REPLDisplay, md::Vector{MD})

base/precompile.jl

+3-3
Original file line numberDiff line numberDiff line change
@@ -743,7 +743,7 @@ precompile(Tuple{getfield(Base.Docs, Symbol("#@repl")), Base.TTY, Symbol})
743743
precompile(Tuple{typeof(Base.Docs.repl), Base.TTY, Symbol})
744744
precompile(Tuple{typeof(Base.Docs._repl), Symbol})
745745
precompile(Tuple{getfield(Core, Symbol("#@doc")), Symbol})
746-
precompile(Tuple{typeof(Base.Docs.docm), Symbol})
746+
precompile(Tuple{typeof(Base.Docs.docm), LineNumberNode, Symbol})
747747
precompile(Tuple{typeof(Base._setindex!), Base.Dict{Any, Any}, Base.Markdown.Config, Symbol, Int64})
748748
precompile(Tuple{Type{Base.Markdown.MD}})
749749
precompile(Tuple{typeof(Base.setindex!), Base.Dict{Any, Any}, Base.Markdown.Config, Symbol})
@@ -1720,8 +1720,8 @@ precompile(Tuple{typeof(Base.Distributed.send_msg), Base.Distributed.Worker, Bas
17201720
precompile(Tuple{typeof(Base.Distributed.send_msg_), Base.Distributed.Worker, Base.Distributed.MsgHeader, Base.Distributed.RemoteDoMsg, Bool})
17211721
precompile(Tuple{typeof(Base.Distributed.terminate_all_workers)})
17221722
precompile(Tuple{typeof(Base.Distributed.test_existing_ref), Base.Distributed.Future})
1723-
precompile(Tuple{typeof(Base.Docs.docm), String, Expr})
1724-
precompile(Tuple{typeof(Base.Docs.docm), String, Expr, Bool})
1723+
precompile(Tuple{typeof(Base.Docs.docm), LineNumberNode, String, Expr})
1724+
precompile(Tuple{typeof(Base.Docs.docm), LineNumberNode, String, Expr, Bool})
17251725
precompile(Tuple{typeof(Base.Docs.keyworddoc), String, Base.BaseDocs.Keyword})
17261726
precompile(Tuple{typeof(Base.Docs.objectdoc), String, Expr, Expr, Expr})
17271727
precompile(Tuple{typeof(Base.FastMath.make_fastmath), Expr})

test/docs.jl

+38-18
Original file line numberDiff line numberDiff line change
@@ -481,24 +481,26 @@ end
481481

482482
# Issue #16359. Error message for invalid doc syntax.
483483

484-
for each in [ # valid syntax
485-
:(f()),
486-
:(f(x)),
487-
:(f(x::Int)),
488-
:(f(x...)),
489-
:(f(x = 1)),
490-
:(f(; x = 1))
491-
]
492-
@test Meta.isexpr(Docs.docm("...", each), :block)
493-
end
494-
for each in [ # invalid syntax
495-
:(f("...")),
496-
:(f(1, 2)),
497-
:(f(() -> ()))
498-
]
499-
result = Docs.docm("...", each)
500-
@test Meta.isexpr(result, :call)
501-
@test result.args[1] === error
484+
let __source__ = LineNumberNode(0)
485+
for each in [ # valid syntax
486+
:(f()),
487+
:(f(x)),
488+
:(f(x::Int)),
489+
:(f(x...)),
490+
:(f(x = 1)),
491+
:(f(; x = 1))
492+
]
493+
@test Meta.isexpr(Docs.docm(__source__, "...", each), :block)
494+
end
495+
for each in [ # invalid syntax
496+
:(f("...")),
497+
:(f(1, 2)),
498+
:(f(() -> ()))
499+
]
500+
result = Docs.docm(__source__, "...", each)
501+
@test Meta.isexpr(result, :call)
502+
@test result.args[1] === error
503+
end
502504
end
503505

504506
# Issue #15424. Non-markdown docstrings.
@@ -1012,3 +1014,21 @@ end
10121014
"""
10131015
)
10141016

1017+
# issue #22105
1018+
module I22105
1019+
lineno = @__LINE__
1020+
"""foo docs"""
1021+
function foo end
1022+
end
1023+
1024+
let foo_docs = meta(I22105)[@var(I22105.foo)].docs
1025+
@test length(foo_docs) === 1
1026+
@test isa(first(foo_docs), Pair)
1027+
local docstr = first(foo_docs).second
1028+
@test isa(docstr, DocStr)
1029+
@test docstr.data[:path] == Base.source_path()
1030+
@test docstr.data[:linenumber] == I22105.lineno + 1
1031+
@test docstr.data[:module] === I22105
1032+
@test docstr.data[:typesig] === Union{}
1033+
@test docstr.data[:binding] == Binding(I22105, :foo)
1034+
end

0 commit comments

Comments
 (0)