Skip to content

Commit bc7bd3a

Browse files
committed
warn-if-doc-error option
#feat
1 parent baa64ff commit bc7bd3a

17 files changed

+242
-30
lines changed

docs/mrdocs.schema.json

+10
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,16 @@
450450
"title": "Verbose output",
451451
"type": "boolean"
452452
},
453+
"warn-if-doc-error": {
454+
"default": true,
455+
"description": "When set to `true`, MrDocs outputs a warning message if the documentation of a symbol has errors such as duplicate parameters and parameters that don't exist.",
456+
"enum": [
457+
true,
458+
false
459+
],
460+
"title": "Warn if documentation has errors",
461+
"type": "boolean"
462+
},
453463
"warn-if-undocumented": {
454464
"default": true,
455465
"description": "When set to `true`, MrDocs outputs a warning message if a symbol that passes all filters is not documented. This option is only effective when `extract-all` is set to `false`.",

src/lib/AST/ASTVisitor.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -3334,7 +3334,8 @@ checkUndocumented(
33343334
{
33353335
if (config_->warnIfUndocumented)
33363336
{
3337-
undocumented_.erase({id, extractName(D)});
3337+
auto const it = undocumented_.find(id);
3338+
undocumented_.erase(it);
33383339
}
33393340
return {};
33403341
}

src/lib/Lib/ConfigOptions.json

+7
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,13 @@
485485
"details": "When set to `true`, MrDocs outputs a warning message if a symbol that passes all filters is not documented. This option is only effective when `extract-all` is set to `false`.",
486486
"type": "bool",
487487
"default": true
488+
},
489+
{
490+
"name": "warn-if-doc-error",
491+
"brief": "Warn if documentation has errors",
492+
"details": "When set to `true`, MrDocs outputs a warning message if the documentation of a symbol has errors such as duplicate parameters and parameters that don't exist.",
493+
"type": "bool",
494+
"default": true
488495
}
489496
]
490497
},

src/lib/Lib/CorpusImpl.cpp

+1-6
Original file line numberDiff line numberDiff line change
@@ -600,12 +600,7 @@ CorpusImpl::finalize()
600600
report::debug("Finalizing javadoc");
601601
JavadocFinalizer finalizer(*this);
602602
finalizer.build();
603-
604-
if (!config->extractAll && config->warnIfUndocumented)
605-
{
606-
finalizer.warnUndocumented();
607-
}
608-
undocumented_.clear();
603+
finalizer.emitWarnings();
609604
}
610605

611606

src/lib/Metadata/Finalizers/JavadocFinalizer.cpp

+127
Original file line numberDiff line numberDiff line change
@@ -509,4 +509,131 @@ checkExists(SymbolID const& id) const
509509
MRDOCS_ASSERT(corpus_.info_.contains(id));
510510
}
511511

512+
void
513+
JavadocFinalizer::
514+
emitWarnings() const
515+
{
516+
MRDOCS_CHECK_OR(corpus_.config->warnings);
517+
warnUndocumented();
518+
warnDocErrors();
519+
}
520+
521+
void
522+
JavadocFinalizer::
523+
warnUndocumented() const
524+
{
525+
MRDOCS_CHECK_OR(!corpus_.config->extractAll);
526+
MRDOCS_CHECK_OR(corpus_.config->warnIfUndocumented);
527+
for (auto& [id, name] : corpus_.undocumented_)
528+
{
529+
if (Info const* I = corpus_.find(id))
530+
{
531+
MRDOCS_CHECK_OR(!I->javadoc || I->Extraction == ExtractionMode::Regular);
532+
}
533+
warn("{}: Symbol is undocumented", name);
534+
}
535+
corpus_.undocumented_.clear();
536+
}
537+
538+
void
539+
JavadocFinalizer::
540+
warnDocErrors() const
541+
{
542+
MRDOCS_CHECK_OR(corpus_.config->warnIfDocError);
543+
for (auto const& I : corpus_.info_)
544+
{
545+
MRDOCS_CHECK_OR_CONTINUE(I->Extraction == ExtractionMode::Regular);
546+
MRDOCS_CHECK_OR_CONTINUE(I->isFunction());
547+
warnParamErrors(dynamic_cast<FunctionInfo const&>(*I));
548+
}
549+
}
550+
551+
namespace {
552+
/* Get a list of all parameter names in javadoc
553+
554+
The javadoc parameter names can contain a single parameter or
555+
a list of parameters separated by commas. This function
556+
returns a list of all parameter names in the javadoc.
557+
*/
558+
SmallVector<std::string_view, 32>
559+
getJavadocParamNames(Javadoc const& javadoc)
560+
{
561+
SmallVector<std::string_view, 32> result;
562+
for (auto const& javadocParam: javadoc.params)
563+
{
564+
auto const& paramNamesStr = javadocParam.name;
565+
for (auto paramNames = std::views::split(paramNamesStr, ',');
566+
auto const& paramName: paramNames)
567+
{
568+
result.push_back(trim(std::string_view(paramName.begin(), paramName.end())));
569+
}
570+
}
571+
return result;
572+
}
573+
574+
}
575+
576+
void
577+
JavadocFinalizer::
578+
warnParamErrors(FunctionInfo const& I) const
579+
{
580+
MRDOCS_CHECK_OR(I.javadoc);
581+
582+
// Check for duplicate javadoc parameters
583+
auto javadocParamNames = getJavadocParamNames(*I.javadoc);
584+
std::ranges::sort(javadocParamNames);
585+
auto [firstDup, lastUnique] = std::ranges::unique(javadocParamNames);
586+
auto duplicateParamNames = std::ranges::subrange(firstDup, lastUnique);
587+
auto [firstDupDup, _] = std::ranges::unique(duplicateParamNames);
588+
for (auto uniqueDuplicateParamNames = std::ranges::subrange(firstDup, firstDupDup);
589+
std::string_view duplicateParamName: uniqueDuplicateParamNames)
590+
{
591+
auto primaryLoc = getPrimaryLocation(I);
592+
warn(
593+
"{}:{}\n"
594+
"{}: Duplicate parameter documentation for '{}'",
595+
primaryLoc->FullPath,
596+
primaryLoc->LineNumber,
597+
corpus_.Corpus::qualifiedName(I),
598+
duplicateParamName);
599+
}
600+
javadocParamNames.erase(lastUnique, javadocParamNames.end());
601+
602+
// Check for function parameters that are not documented in javadoc
603+
auto paramNames =
604+
I.Params |
605+
std::views::transform(&Param::Name) |
606+
std::views::filter([](std::string_view const& name) { return !name.empty(); });
607+
for (auto const& paramName: paramNames)
608+
{
609+
if (std::ranges::find(javadocParamNames, paramName) == javadocParamNames.end())
610+
{
611+
auto primaryLoc = getPrimaryLocation(I);
612+
warn(
613+
"{}:{}\n"
614+
"{}: Missing documentation for parameter '{}'",
615+
primaryLoc->FullPath,
616+
primaryLoc->LineNumber,
617+
corpus_.Corpus::qualifiedName(I),
618+
paramName);
619+
}
620+
}
621+
622+
// Check for documented parameters that don't exist in the function
623+
for (std::string_view javadocParamName: javadocParamNames)
624+
{
625+
if (std::ranges::find(paramNames, javadocParamName) == paramNames.end())
626+
{
627+
auto primaryLoc = getPrimaryLocation(I);
628+
warn(
629+
"{}:{}\n"
630+
"{}: Documented parameter '{}' does not exist",
631+
primaryLoc->FullPath,
632+
primaryLoc->LineNumber,
633+
corpus_.Corpus::qualifiedName(I),
634+
javadocParamName);
635+
}
636+
}
637+
}
638+
512639
} // clang::mrdocs

src/lib/Metadata/Finalizers/JavadocFinalizer.hpp

+10-11
Original file line numberDiff line numberDiff line change
@@ -53,17 +53,7 @@ class JavadocFinalizer
5353
}
5454

5555
void
56-
warnUndocumented() const
57-
{
58-
for (auto& [id, name] : corpus_.undocumented_)
59-
{
60-
if (Info const* I = corpus_.find(id))
61-
{
62-
MRDOCS_CHECK_OR(!I->javadoc || I->Extraction == ExtractionMode::Regular);
63-
}
64-
warn("{}: Symbol is undocumented", name);
65-
}
66-
}
56+
emitWarnings() const;
6757

6858
void
6959
operator()(Info& I)
@@ -182,6 +172,15 @@ class JavadocFinalizer
182172
MRDOCS_CHECK_OR(corpus_.config->warnings);
183173
return log(report::Level::warn, format, std::forward<Args>(args)...);
184174
}
175+
176+
void
177+
warnUndocumented() const;
178+
179+
void
180+
warnDocErrors() const;
181+
182+
void
183+
warnParamErrors(FunctionInfo const& I) const;
185184
};
186185

187186
} // clang::mrdocs

test-files/golden-tests/javadoc/inline/styled.adoc

+11
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,17 @@ compare(<<A,A>> const& other) const noexcept;
7676

7777
`&hyphen;1` if `&ast;this &lt; other`, `0` if `this &equals;&equals; other`, and 1 if `this &gt; other`&period;
7878

79+
=== Parameters
80+
81+
82+
|===
83+
| Name | Description
84+
85+
| *other*
86+
| The other object to compare against&period;
87+
88+
|===
89+
7990

8091

8192
[.small]#Created with https://www.mrdocs.com[MrDocs]#

test-files/golden-tests/javadoc/inline/styled.cpp

+5-3
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@
22
33
Paragraph with `code`, *bold* text, and _italic_ text.
44
5-
We can also escape these markers: \`, \*, and \_.
5+
We can also escape these markers: \`, \*, and \_.
66
77
*/
88
struct A {
99
/** Compare function
1010
11-
@return `-1` if `*this < other`, `0` if
11+
@param other The other object to compare against.
12+
13+
@return `-1` if `*this < other`, `0` if
1214
`this == other`, and 1 if `this > other`.
13-
*/
15+
*/
1416
int compare(A const& other) const noexcept;
1517
};
1618

test-files/golden-tests/javadoc/inline/styled.html

+18
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,24 @@ <h3>Synopsis</h3>
9191
<h3>Return Value</h3>
9292
<p><code>-1</code><span> if </span><code>*this &lt; other</code> <span>, </span><code>0</code><span> if </span><code>this &#x3D;&#x3D; other</code> <span>, and 1 if </span><code>this &gt; other</code> <span>.</span></p>
9393

94+
</div>
95+
<div>
96+
<h3>Parameters</h3>
97+
<table>
98+
<thead>
99+
<tr>
100+
<th>Name</th>
101+
<th>Description</th>
102+
</tr>
103+
</thead>
104+
<tbody>
105+
<tr>
106+
<td><strong>other</strong></td>
107+
<td><p><span>The other object to compare against.</span></p>
108+
</td>
109+
</tr>
110+
</tbody>
111+
</table>
94112
</div>
95113
</div>
96114

test-files/golden-tests/javadoc/inline/styled.xml

+4-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
</para>
2323
</doc>
2424
<function name="compare" exception-spec="noexcept" id="CKPqawUTt6FNIN9qEFTMcdnvPkI=">
25-
<file short-path="styled.cpp" source-path="styled.cpp" line="14"/>
25+
<file short-path="styled.cpp" source-path="styled.cpp" line="16"/>
2626
<attr id="is-const"/>
2727
<return>
2828
<type name="int"/>
@@ -48,6 +48,9 @@
4848
<mono>this &gt; other</mono>
4949
<text>.</text>
5050
</returns>
51+
<param name="other">
52+
<text>The other object to compare against.</text>
53+
</param>
5154
</doc>
5255
</function>
5356
</struct>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
warn-if-doc-error: false
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
warn-if-doc-error: false

test-files/golden-tests/javadoc/ref/operator-param.adoc

+13-2
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ bool
7373
| Name | Description
7474

7575
| *ch*
76-
| The character to test&period;
76+
| The signed character to test&period;
7777

7878
|===
7979

@@ -93,6 +93,17 @@ bool
9393
operator()(char ch) const noexcept;
9494
----
9595

96+
=== Parameters
97+
98+
99+
|===
100+
| Name | Description
101+
102+
| *ch*
103+
| The signed character to test&period;
104+
105+
|===
106+
96107
[#A-operator_call-0b]
97108
== <<A,A>>::operator()
98109

@@ -125,7 +136,7 @@ This function returns true if the character is in the set, otherwise
125136
| Name | Description
126137

127138
| *ch*
128-
| The character to test&period;
139+
| The unsigned character to test&period;
129140

130141
|===
131142

test-files/golden-tests/javadoc/ref/operator-param.cpp

+6-2
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,17 @@ struct A {
55
character is in the set, otherwise
66
it returns false.
77
8-
@param ch The character to test.
8+
@param ch The unsigned character to test.
99
*/
1010
constexpr
1111
bool
1212
operator()(unsigned char ch) const noexcept;
1313

14-
/// @copydoc A::operator()(unsigned char) const
14+
/**
15+
@copydoc A::operator()(unsigned char) const
16+
17+
@param ch The signed character to test.
18+
*/
1519
constexpr
1620
bool
1721
operator()(char ch) const noexcept;

0 commit comments

Comments
 (0)