Skip to content
29 changes: 23 additions & 6 deletions src/docchecks.jl
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ end

function missingbindings(doc::Document)
@debug "checking for missing docstrings."
bindings = allbindings(doc.user.checkdocs, doc.blueprint.modules)
for object in keys(doc.internal.objects)
bindings = allbindings(doc.user.checkdocs, doc.blueprint.modules, doc.user.checkdocs_ignored_modules)
# Sort keys to ensure deterministic iteration order
for object in sort!(collect(keys(doc.internal.objects)); by = o -> (string(o.binding), string(o.signature)))
if !is_canonical(object)
continue
end
Expand Down Expand Up @@ -69,21 +70,37 @@ function missingbindings(doc::Document)
return bindings
end

function allbindings(checkdocs::Symbol, mods)
function allbindings(checkdocs::Symbol, mods, ignored_modules = Module[])
out = Dict{Binding, Set{Type}}()
for m in mods
allbindings(checkdocs, m, out)
allbindings(checkdocs, m, out, ignored_modules)
end
return out
end

function allbindings(checkdocs::Symbol, mod::Module, out = Dict{Binding, Set{Type}}())
for (binding, doc) in meta(mod)
function allbindings(checkdocs::Symbol, mod::Module, out = Dict{Binding, Set{Type}}(), ignored_modules = Module[])
# Sort the metadata entries to ensure deterministic iteration order
metadata = collect(meta(mod))
sort!(metadata; by = entry -> string(entry[1]))
for (binding, doc) in metadata
# The keys of the docs meta dictionary should always be Docs.Binding objects in
# practice. However, the key type is Any, so it is theoretically possible that
# some non-binding metadata gets added to the dict. So on the off-chance that has
# happened, we simply ignore those entries.
isa(binding, Docs.Binding) || continue
# Skip bindings from ignored modules or their submodules
any(ignored_modules) do ignored
# Check if binding.mod is the ignored module or a submodule of it
current = binding.mod
while current != Main && current != Base
current == ignored && return true
parent = parentmodule(current)
# Guard against infinite loops if parentmodule returns itself
parent == current && break
current = parent
end
return false
end && continue
# We only consider a name exported only if it actually exists in the module, either
# by virtue of being defined there, or if it has been brought into the scope with
# import/using.
Expand Down
Loading