diff --git a/NEWS.md b/NEWS.md index 82ae28a3b420d..488be0f3209b4 100644 --- a/NEWS.md +++ b/NEWS.md @@ -84,6 +84,7 @@ New library features write the output to a stream rather than returning a string ([#48625]). * `sizehint!(s, n)` now supports an optional `shrink` argument to disable shrinking ([#51929]). * New function `Docs.hasdoc(module, symbol)` tells whether a name has a docstring ([#52139]). +* New function `Docs.undocumented_names(module; all)` returns a module's undocumented names ([#52413]). * Passing an `IOBuffer` as a stdout argument for `Process` spawn now works as expected, synchronized with `wait` or `success`, so a `Base.BufferStream` is no longer required there for correctness to avoid data races ([#52461]). diff --git a/base/docs/Docs.jl b/base/docs/Docs.jl index 87b4a45c4dc80..5e7126f47c28d 100644 --- a/base/docs/Docs.jl +++ b/base/docs/Docs.jl @@ -3,7 +3,7 @@ """ Docs -The `Docs` module provides the `@doc` macro which can be used to set and retrieve +The `Docs` module provides the [`@doc`](@ref) macro which can be used to set and retrieve documentation metadata for Julia objects. Please see the manual section on [documentation](@ref man-documentation) for more @@ -675,4 +675,20 @@ function hasdoc(binding::Docs.Binding, sig::Type = Union{}) end +""" + undocumented_names(mod::Module; all=false) + +Return an array of undocumented symbols in `module` (that is, lacking docstrings). +`all=false` returns only exported symbols; whereas `all=true` also includes +non-exported symbols, following the behavior of [`names`](@ref). Only valid identifiers +are included. Names are returned in sorted order. + +See also: [`names`](@ref), [`Docs.hasdoc`](@ref), [`Base.isidentifier`](@ref). +""" +function undocumented_names(mod::Module; all::Bool=false) + filter!(names(mod; all)) do sym + !hasdoc(mod, sym) && Base.isidentifier(sym) + end +end + end diff --git a/base/docs/utils.jl b/base/docs/utils.jl index 928dfde01ccf0..1ed576c7362ff 100644 --- a/base/docs/utils.jl +++ b/base/docs/utils.jl @@ -23,7 +23,7 @@ You can also use a stream for large amounts of data: `HTML` is currently exported to maintain backwards compatibility, but this export is deprecated. It is recommended to use - this type as `Docs.HTML` or to explicitly + this type as [`Docs.HTML`](@ref) or to explicitly import it from `Docs`. """ mutable struct HTML{T} @@ -81,7 +81,7 @@ You can also use a stream for large amounts of data: `Text` is currently exported to maintain backwards compatibility, but this export is deprecated. It is recommended to use - this type as `Docs.Text` or to explicitly + this type as [`Docs.Text`](@ref) or to explicitly import it from `Docs`. """ mutable struct Text{T} diff --git a/base/reflection.jl b/base/reflection.jl index 306f21cbc5f23..0295657eea1fc 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -79,7 +79,7 @@ Get an array of the public names of a `Module`, excluding deprecated names. If `all` is true, then the list also includes non-public names defined in the module, deprecated names, and compiler-generated names. If `imported` is true, then names explicitly imported from other modules -are also included. +are also included. Names are returned in sorted order. As a special case, all names defined in `Main` are considered \"public\", since it is not idiomatic to explicitly mark names from `Main` as public. diff --git a/doc/src/base/base.md b/doc/src/base/base.md index c5d7320148e97..7172474b40b39 100644 --- a/doc/src/base/base.md +++ b/doc/src/base/base.md @@ -462,6 +462,16 @@ Base.functionloc(::Method) Base.@locals ``` +## Documentation +(See also the [documentation](@ref man-documentation) chapter.) +```@docs +Base.@doc +Docs.HTML +Docs.Text +Docs.hasdoc +Docs.undocumented_names +``` + ## Code loading ```@docs diff --git a/doc/src/manual/documentation.md b/doc/src/manual/documentation.md index 169b27ead15f8..58b1819e97372 100644 --- a/doc/src/manual/documentation.md +++ b/doc/src/manual/documentation.md @@ -20,7 +20,8 @@ environments provide a way to access documentation directly: under the cursor. -`Docs.hasdoc(module, name)::Bool` tells whether a name has a docstring. +`Docs.hasdoc(module, name)::Bool` tells whether a name has a docstring. `Docs.undocumented_names(module; all)` +returns the undocumented names in a module. ## Writing Documentation diff --git a/test/docs.jl b/test/docs.jl index 1b26c9670e180..7ebef8832cd69 100644 --- a/test/docs.jl +++ b/test/docs.jl @@ -76,6 +76,25 @@ function break_me_docs end @test !isdefined(Base, :_this_name_doesnt_exist_) && !Docs.hasdoc(Base, :_this_name_doesnt_exist_) @test isdefined(Base, :_typed_vcat) && !Docs.hasdoc(Base, :_typed_vcat) +"This module has names without documentation." +module _ModuleWithUndocumentedNames +export f +f() = 1 +end + +"This module has some documentation." +module _ModuleWithSomeDocumentedNames +export f +"f() is 1." +f() = 1 +g() = 2 +end + +@test Docs.undocumented_names(_ModuleWithUndocumentedNames) == [:f] +@test isempty(Docs.undocumented_names(_ModuleWithSomeDocumentedNames)) +@test Docs.undocumented_names(_ModuleWithSomeDocumentedNames; all=true) == [:eval, :g, :include] + + # issue #11548 module ModuleMacroDoc