Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Docs.undocumented_names #52413

Merged
merged 17 commits into from
Dec 31, 2023
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 ([#TBD]).
Expand Down
18 changes: 17 additions & 1 deletion base/docs/Docs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -675,4 +675,20 @@ function hasdoc(binding::Docs.Binding, sig::Type = Union{})
end


"""
undocumented_names(mod::Module; all=false)
jariji marked this conversation as resolved.
Show resolved Hide resolved

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)
Copy link
Member

@stevengj stevengj Jan 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just realized that this filters out macro symbols @foo, which we don't want to exclude. Probably should be

str = string(sym)
!hasdoc(mod, sym) && (Base.isidentifier(sym) || (startswith(str, "@") && Base.isidentifier(str[2:end])))

end
end

end
4 changes: 2 additions & 2 deletions base/docs/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down Expand Up @@ -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}
Expand Down
2 changes: 1 addition & 1 deletion base/reflection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
10 changes: 10 additions & 0 deletions doc/src/base/base.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion doc/src/manual/documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
jariji marked this conversation as resolved.
Show resolved Hide resolved

## Writing Documentation

Expand Down
19 changes: 19 additions & 0 deletions test/docs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down