Skip to content

Commit 590a4e1

Browse files
gdallefingolfinlgoettgens
authored
feat: add test to detect public names without a docstring (#313)
Co-authored-by: Max Horn <[email protected]> Co-authored-by: Lars Göttgens <[email protected]>
1 parent 3e90d04 commit 590a4e1

File tree

9 files changed

+181
-1
lines changed

9 files changed

+181
-1
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## Version [v0.8.12] - unreleased
9+
10+
### Changed
11+
12+
- Add `test_undocumented_names` to verify that all public symbols have docstrings (not including the module itself). This test is not enabled by default in `test_all`. ([#313])
13+
814
## Version [v0.8.11] - 2025-02-06
915

1016
### Changed

docs/make.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ makedocs(;
2929
"deps_compat.md",
3030
"piracies.md",
3131
"persistent_tasks.md",
32+
"undocumented_names.md",
3233
],
3334
"release-notes.md",
3435
],

docs/src/undocumented_names.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Undocumented names
2+
3+
## [Test function](@id test_undocumented_names)
4+
5+
```@docs
6+
Aqua.test_undocumented_names
7+
```

src/Aqua.jl

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module Aqua
22

3-
using Base: PkgId, UUID
3+
using Base: Docs, PkgId, UUID
44
using Pkg: Pkg, TOML, PackageSpec
55
using Test
66

@@ -23,6 +23,7 @@ include("stale_deps.jl")
2323
include("deps_compat.jl")
2424
include("piracies.jl")
2525
include("persistent_tasks.jl")
26+
include("undocumented_names.jl")
2627

2728
"""
2829
test_all(testtarget::Module)
@@ -37,6 +38,7 @@ Run the following tests:
3738
* [`test_deps_compat(testtarget)`](@ref test_deps_compat)
3839
* [`test_piracies(testtarget)`](@ref test_piracies)
3940
* [`test_persistent_tasks(testtarget)`](@ref test_persistent_tasks)
41+
* [`test_undocumented_names(testtarget)`](@ref test_undocumented_names)
4042
4143
The keyword argument `\$x` (e.g., `ambiguities`) can be used to
4244
control whether or not to run `test_\$x` (e.g., `test_ambiguities`).
@@ -52,6 +54,7 @@ passed to `\$x` to specify the keyword arguments for `test_\$x`.
5254
- `deps_compat = true`
5355
- `piracies = true`
5456
- `persistent_tasks = true`
57+
- `undocumented_names = false`
5558
"""
5659
function test_all(
5760
testtarget::Module;
@@ -63,6 +66,7 @@ function test_all(
6366
deps_compat = true,
6467
piracies = true,
6568
persistent_tasks = true,
69+
undocumented_names = false,
6670
)
6771
if ambiguities !== false
6872
@testset "Method ambiguity" begin
@@ -105,6 +109,13 @@ function test_all(
105109
test_persistent_tasks(testtarget; askwargs(persistent_tasks)...)
106110
end
107111
end
112+
@testset "Undocumented names" begin
113+
if undocumented_names !== false
114+
isempty(askwargs(undocumented_names)) ||
115+
error("Keyword arguments not supported")
116+
test_undocumented_names(testtarget; askwargs(undocumented_names)...)
117+
end
118+
end
108119
end
109120

110121
end # module

src/undocumented_names.jl

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
"""
2+
test_undocumented_names(module::Module)
3+
4+
Test that all public names in `module` and its recursive submodules have a docstring (not including `module` itself).
5+
6+
!!! tip
7+
On all Julia versions, public names include the exported names.
8+
On Julia versions >= 1.11, public names also include the names annotated with the `public` keyword.
9+
10+
!!! warning
11+
When running this Aqua test in Julia versions before 1.11, it does nothing.
12+
Thus if you use continuous integration tests, make sure those are configured
13+
to use Julia >= 1.11 in order to benefit from this test.
14+
15+
# Keyword Arguments
16+
- `broken::Bool = false`: If true, it uses `@test_broken` instead of
17+
`@test` and shortens the error message.
18+
"""
19+
function test_undocumented_names(m::Module; broken::Bool = false)
20+
@static if VERSION >= v"1.11"
21+
# exclude the module name itself because it has the README as auto-generated docstring (https://github.com/JuliaLang/julia/pull/39093)
22+
undocumented_names = Symbol[]
23+
walkmodules(m) do x
24+
append!(undocumented_names, Docs.undocumented_names(x))
25+
end
26+
undocumented_names = filter(n -> n != nameof(m), undocumented_names)
27+
if broken
28+
@test_broken isempty(undocumented_names)
29+
else
30+
@test isempty(undocumented_names)
31+
end
32+
else
33+
undocumented_names = Symbol[]
34+
end
35+
if !isempty(undocumented_names)
36+
printstyled(
37+
stderr,
38+
"Undocumented names detected:\n";
39+
bold = true,
40+
color = Base.error_color(),
41+
)
42+
!broken && show(stderr, MIME"text/plain"(), undocumented_names)
43+
println(stderr)
44+
end
45+
end
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
module PkgWithUndocumentedNames
2+
3+
"""
4+
documented_function
5+
"""
6+
function documented_function end
7+
8+
function undocumented_function end
9+
10+
"""
11+
DocumentedStruct
12+
"""
13+
struct DocumentedStruct end
14+
15+
struct UndocumentedStruct end
16+
17+
export documented_function, DocumentedStruct
18+
export undocumented_function, UndocumentedStruct
19+
20+
end # module
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module PkgWithUndocumentedNamesInSubmodule
2+
3+
"""
4+
DocumentedStruct
5+
"""
6+
struct DocumentedStruct end
7+
8+
module SubModule
9+
10+
struct UndocumentedStruct end
11+
12+
end
13+
14+
end # module
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
"""
2+
PkgWithoutUndocumentedNames
3+
"""
4+
module PkgWithoutUndocumentedNames
5+
6+
"""
7+
documented_function
8+
"""
9+
function documented_function end
10+
11+
"""
12+
DocumentedStruct
13+
"""
14+
struct DocumentedStruct end
15+
16+
export documented_function, DocumentedStruct
17+
18+
end # module

test/test_undocumented_names.jl

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
module TestUndocumentedNames
2+
3+
include("preamble.jl")
4+
5+
import PkgWithUndocumentedNames
6+
import PkgWithUndocumentedNamesInSubmodule
7+
import PkgWithoutUndocumentedNames
8+
9+
# Pass
10+
results = @testtestset begin
11+
Aqua.test_undocumented_names(PkgWithoutUndocumentedNames)
12+
end
13+
if VERSION >= v"1.11"
14+
@test length(results) == 1
15+
@test results[1] isa Test.Pass
16+
else
17+
@test length(results) == 0
18+
end
19+
20+
# Fail
21+
println("### Expected output START ###")
22+
results = @testtestset begin
23+
Aqua.test_undocumented_names(PkgWithUndocumentedNames)
24+
end
25+
println("### Expected output END ###")
26+
if VERSION >= v"1.11"
27+
@test length(results) == 1
28+
@test results[1] isa Test.Fail
29+
else
30+
@test length(results) == 0
31+
end
32+
33+
println("### Expected output START ###")
34+
results = @testtestset begin
35+
Aqua.test_undocumented_names(PkgWithUndocumentedNamesInSubmodule)
36+
end
37+
println("### Expected output END ###")
38+
if VERSION >= v"1.11"
39+
@test length(results) == 1
40+
@test results[1] isa Test.Fail
41+
else
42+
@test length(results) == 0
43+
end
44+
45+
# Broken
46+
println("### Expected output START ###")
47+
results = @testtestset begin
48+
Aqua.test_undocumented_names(PkgWithUndocumentedNames; broken = true)
49+
end
50+
println("### Expected output END ###")
51+
if VERSION >= v"1.11"
52+
@test length(results) == 1
53+
@test results[1] isa Test.Broken
54+
else
55+
@test length(results) == 0
56+
end
57+
58+
end # module

0 commit comments

Comments
 (0)