From 2902a40be5fe73ef59fd431a7ba160d62bcf6dcf Mon Sep 17 00:00:00 2001 From: alandefreitas Date: Fri, 7 Feb 2025 20:35:49 -0300 Subject: [PATCH 1/8] `warnings` option #feat --- docs/mrdocs.schema.json | 10 ++++++ src/lib/Lib/ConfigOptions.json | 35 +++++++++++++------ .../Metadata/Finalizers/JavadocFinalizer.cpp | 6 ++-- .../Metadata/Finalizers/JavadocFinalizer.hpp | 10 ++++++ .../config/auto-brief/auto-brief.yml | 3 +- .../config/auto-brief/no-auto-brief.yml | 3 +- .../golden-tests/javadoc/ref/broken-ref.yml | 1 + 7 files changed, 52 insertions(+), 16 deletions(-) create mode 100644 test-files/golden-tests/javadoc/ref/broken-ref.yml diff --git a/docs/mrdocs.schema.json b/docs/mrdocs.schema.json index 0c93c4391b..b67f85371e 100644 --- a/docs/mrdocs.schema.json +++ b/docs/mrdocs.schema.json @@ -439,6 +439,16 @@ ], "title": "Verbose output", "type": "boolean" + }, + "warnings": { + "default": true, + "description": "When set to `true`, MrDocs outputs warning messages during the generation of the documentation. It is usually recommended to enable warnings while writing the documentation.", + "enum": [ + true, + false + ], + "title": "Enable warning messages", + "type": "boolean" } }, "required": [], diff --git a/src/lib/Lib/ConfigOptions.json b/src/lib/Lib/ConfigOptions.json index 12c97c3ae6..7fe3fa13d8 100644 --- a/src/lib/Lib/ConfigOptions.json +++ b/src/lib/Lib/ConfigOptions.json @@ -416,18 +416,9 @@ ] }, { - "category": "Miscellaneous", - "brief": "Miscellaneous options", + "category": "Warnings", + "brief": "Warnings and progress messages", "options": [ - { - "name": "concurrency", - "command-line-only": true, - "brief": "Number of threads to use", - "details": "The desired level of concurrency: 0 for hardware-suggested.", - "type": "unsigned", - "default": 0, - "min-value": 0 - }, { "name": "verbose", "brief": "Verbose output", @@ -460,6 +451,28 @@ ], "default": "info" }, + { + "name": "warnings", + "brief": "Enable warning messages", + "details": "When set to `true`, MrDocs outputs warning messages during the generation of the documentation. It is usually recommended to enable warnings while writing the documentation.", + "type": "bool", + "default": true + } + ] + }, + { + "category": "Miscellaneous", + "brief": "Miscellaneous options", + "options": [ + { + "name": "concurrency", + "command-line-only": true, + "brief": "Number of threads to use", + "details": "The desired level of concurrency: 0 for hardware-suggested.", + "type": "unsigned", + "default": 0, + "min-value": 0 + }, { "name": "ignore-map-errors", "brief": "Continue if files are not mapped correctly", diff --git a/src/lib/Metadata/Finalizers/JavadocFinalizer.cpp b/src/lib/Metadata/Finalizers/JavadocFinalizer.cpp index 8bb11b5bff..9004039680 100644 --- a/src/lib/Metadata/Finalizers/JavadocFinalizer.cpp +++ b/src/lib/Metadata/Finalizers/JavadocFinalizer.cpp @@ -128,7 +128,7 @@ finalize(doc::Reference& ref) MRDOCS_ASSERT(current_context_); if (auto primaryLoc = getPrimaryLocation(*current_context_)) { - report::warn( + warn( "{}:{}\n{}: Failed to resolve reference to '{}'", primaryLoc->FullPath, primaryLoc->LineNumber, @@ -284,7 +284,7 @@ copyBriefAndDetails(Javadoc& javadoc) MRDOCS_ASSERT(current_context_); if (auto primaryLoc = getPrimaryLocation(*current_context_)) { - report::warn( + warn( "{}:{}\n" "{}: Failed to copy documentation from '{}'\n" " Note: Symbol '{}' not found.", @@ -306,7 +306,7 @@ copyBriefAndDetails(Javadoc& javadoc) { auto ctxPrimaryLoc = getPrimaryLocation(*current_context_); auto resPrimaryLoc = getPrimaryLocation(*res); - report::warn( + warn( "{}:{}\n" "{}: Failed to copy documentation from '{}'.\n" "No documentation available.\n" diff --git a/src/lib/Metadata/Finalizers/JavadocFinalizer.hpp b/src/lib/Metadata/Finalizers/JavadocFinalizer.hpp index 34a963e78b..5b770f5e22 100644 --- a/src/lib/Metadata/Finalizers/JavadocFinalizer.hpp +++ b/src/lib/Metadata/Finalizers/JavadocFinalizer.hpp @@ -159,6 +159,16 @@ class JavadocFinalizer return corpus_.find(id) != nullptr; })); } + + template + void + warn( + Located format, + Args&&... args) const + { + MRDOCS_CHECK_OR(corpus_.config->warnings); + return log(report::Level::warn, format, std::forward(args)...); + } }; } // clang::mrdocs diff --git a/test-files/golden-tests/config/auto-brief/auto-brief.yml b/test-files/golden-tests/config/auto-brief/auto-brief.yml index 95975683d2..ee5cfa7bd1 100644 --- a/test-files/golden-tests/config/auto-brief/auto-brief.yml +++ b/test-files/golden-tests/config/auto-brief/auto-brief.yml @@ -1 +1,2 @@ -auto-brief: true \ No newline at end of file +auto-brief: true +warnings: false \ No newline at end of file diff --git a/test-files/golden-tests/config/auto-brief/no-auto-brief.yml b/test-files/golden-tests/config/auto-brief/no-auto-brief.yml index 1784dab3aa..4ec9b956e7 100644 --- a/test-files/golden-tests/config/auto-brief/no-auto-brief.yml +++ b/test-files/golden-tests/config/auto-brief/no-auto-brief.yml @@ -1 +1,2 @@ -auto-brief: false \ No newline at end of file +auto-brief: false +warnings: false \ No newline at end of file diff --git a/test-files/golden-tests/javadoc/ref/broken-ref.yml b/test-files/golden-tests/javadoc/ref/broken-ref.yml new file mode 100644 index 0000000000..bc089c1ffb --- /dev/null +++ b/test-files/golden-tests/javadoc/ref/broken-ref.yml @@ -0,0 +1 @@ +warnings: false \ No newline at end of file From f00fb0690c5626b96f390250e2166120c5d68a15 Mon Sep 17 00:00:00 2001 From: alandefreitas Date: Fri, 7 Feb 2025 23:20:05 -0300 Subject: [PATCH 2/8] `extract-all` option #feat fix #758 --- docs/mrdocs.schema.json | 10 +++ src/lib/AST/ASTVisitor.cpp | 18 +++-- src/lib/Lib/ConfigOptions.json | 67 ++++++++++------ .../config/extract-all/no-extract-all.adoc | 58 ++++++++++++++ .../config/extract-all/no-extract-all.cpp | 9 +++ .../config/extract-all/no-extract-all.html | 78 +++++++++++++++++++ .../config/extract-all/no-extract-all.xml | 22 ++++++ .../config/extract-all/no-extract-all.yml | 1 + 8 files changed, 235 insertions(+), 28 deletions(-) create mode 100644 test-files/golden-tests/config/extract-all/no-extract-all.adoc create mode 100644 test-files/golden-tests/config/extract-all/no-extract-all.cpp create mode 100644 test-files/golden-tests/config/extract-all/no-extract-all.html create mode 100644 test-files/golden-tests/config/extract-all/no-extract-all.xml create mode 100644 test-files/golden-tests/config/extract-all/no-extract-all.yml diff --git a/docs/mrdocs.schema.json b/docs/mrdocs.schema.json index b67f85371e..0009087c2d 100644 --- a/docs/mrdocs.schema.json +++ b/docs/mrdocs.schema.json @@ -91,6 +91,16 @@ "title": "Symbol patterns to exclude", "type": "array" }, + "extract-all": { + "default": true, + "description": "When set to `true`, MrDocs extracts all symbols from the source code, even if no documentation is provided. When set to `false`, it's still recommendable to provide file and symbol filters so that only the desired symbols are traversed by MrDocs.", + "enum": [ + true, + false + ], + "title": "Extract all symbols", + "type": "boolean" + }, "file-patterns": { "default": [ "*.hpp", diff --git a/src/lib/AST/ASTVisitor.cpp b/src/lib/AST/ASTVisitor.cpp index e4e2791e3a..7903167d79 100644 --- a/src/lib/AST/ASTVisitor.cpp +++ b/src/lib/AST/ASTVisitor.cpp @@ -3271,6 +3271,11 @@ Expected< ASTVisitor:: upsert(DeclType const* D) { + using R = std::conditional_t< + std::same_as, + InfoTypeFor_t, + InfoTy>; + ExtractionMode const m = checkFilters(D); if (m == ExtractionMode::Dependency) { @@ -3286,14 +3291,17 @@ upsert(DeclType const* D) return Unexpected(Error("Explicit declaration in dependency mode")); } } + else if ( + !InfoParent && + mode_ == Regular && + !config_->extractAll && + !D->getASTContext().getRawCommentForDeclNoCache(D)) + { + return Unexpected(formatError("{}: Symbol is undocumented", extractName(D))); + } SymbolID const id = generateID(D); MRDOCS_CHECK_MSG(id, "Failed to extract symbol ID"); - - using R = std::conditional_t< - std::same_as, - InfoTypeFor_t, - InfoTy>; auto [I, isNew] = upsert(id); // Already populate the extraction mode diff --git a/src/lib/Lib/ConfigOptions.json b/src/lib/Lib/ConfigOptions.json index 7fe3fa13d8..a1092e9a68 100644 --- a/src/lib/Lib/ConfigOptions.json +++ b/src/lib/Lib/ConfigOptions.json @@ -159,22 +159,43 @@ } ] }, + { + "category": "Comment Parsing", + "brief": "Options to control how comments are parsed", + "details": "MrDocs extracts metadata from the comments in the source code. The following options control how comments are parsed.", + "options": [ + { + "name": "auto-brief", + "brief": "Use the first line of the comment as the brief", + "details": "When set to `true`, MrDocs uses the first line (until the first dot, question mark, or exclamation mark) of the comment as the brief of the symbol. When set to `false`, a explicit @brief command is required.", + "type": "bool", + "default": true + } + ] + }, { "category": "Metadata Extraction", "brief": "Metadata and C++ semantic constructs to extract", "details": "MrDocs extracts metadata and C++ semantic constructs from the source code to create the documentation. Semantic constructs are patterns not directly represented in the source code AST but can be inferred from the corpus, such as SFINAE. The following options control the extraction of metadata and C++ semantic constructs.", "options": [ { - "name": "sfinae", - "brief": "Detect and reduce SFINAE expressions", - "details": "When set to true, MrDocs detects SFINAE expressions in the source code and extracts them as part of the documentation. Expressions such as `std::enable_if<...>` are detected, removed, and documented as a requirement. MrDocs uses an algorithm that extracts SFINAE infomation from types by identifying inspecting the primary template and specializations to detect the result type and the controlling expressions in a specialization.", + "name": "extract-all", + "brief": "Extract all symbols", + "details": "When set to `true`, MrDocs extracts all symbols from the source code, even if no documentation is provided. When set to `false`, it's still recommendable to provide file and symbol filters so that only the desired symbols are traversed by MrDocs.", "type": "bool", "default": true }, { - "name": "overloads", - "brief": "Detect and group function overloads", - "details": "When set to `true`, MrDocs detects function overloads and groups them as a single symbol type.", + "name": "private-members", + "brief": "Extraction policy for private class members", + "details": "Determine whether private class members should be extracted", + "type": "bool", + "default": false + }, + { + "name": "private-bases", + "brief": "Extraction policy for private base classes", + "details": "Determine whether private base classes should be extracted", "type": "bool", "default": true }, @@ -191,20 +212,6 @@ ], "default": "copy-dependencies" }, - { - "name": "private-members", - "brief": "Extraction policy for private class members", - "details": "Determine whether private class members should be extracted", - "type": "bool", - "default": false - }, - { - "name": "private-bases", - "brief": "Extraction policy for private base classes", - "details": "Determine whether private base classes should be extracted", - "type": "bool", - "default": true - }, { "name": "anonymous-namespaces", "brief": "Extraction policy for anonymous namespaces", @@ -253,11 +260,25 @@ "details": "When set to `true`, relational operators are sorted last in the list of members of a record or namespace.", "type": "bool", "default": true + } + ] + }, + { + "category": "Semantic Constructs", + "brief": "C++ semantic constructs to extract", + "details": "Semantic constructs are patterns not directly represented in the source code AST but can be inferred from the corpus, such as SFINAE.", + "options": [ + { + "name": "sfinae", + "brief": "Detect and reduce SFINAE expressions", + "details": "When set to true, MrDocs detects SFINAE expressions in the source code and extracts them as part of the documentation. Expressions such as `std::enable_if<...>` are detected, removed, and documented as a requirement. MrDocs uses an algorithm that extracts SFINAE infomation from types by identifying inspecting the primary template and specializations to detect the result type and the controlling expressions in a specialization.", + "type": "bool", + "default": true }, { - "name": "auto-brief", - "brief": "Use the first line of the comment as the brief", - "details": "When set to `true`, MrDocs uses the first line (until the first dot, question mark, or exclamation mark) of the comment as the brief of the symbol. When set to `false`, a explicit @brief command is required.", + "name": "overloads", + "brief": "Detect and group function overloads", + "details": "When set to `true`, MrDocs detects function overloads and groups them as a single symbol type.", "type": "bool", "default": true } diff --git a/test-files/golden-tests/config/extract-all/no-extract-all.adoc b/test-files/golden-tests/config/extract-all/no-extract-all.adoc new file mode 100644 index 0000000000..b63008a4d3 --- /dev/null +++ b/test-files/golden-tests/config/extract-all/no-extract-all.adoc @@ -0,0 +1,58 @@ += Reference +:mrdocs: + +[#index] +== Global namespace + + +=== Functions + +[cols=2] +|=== +| Name | Description + +| <> +| Documented function + +| <> +| Sometimes documented function + +|=== + +[#docFunction] +== docFunction + + +Documented function + +=== Synopsis + + +Declared in `<no‐extract‐all.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +void +docFunction(); +---- + +[#sometimesDocFunction] +== sometimesDocFunction + + +Sometimes documented function + +=== Synopsis + + +Declared in `<no‐extract‐all.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +void +sometimesDocFunction(); +---- + + + +[.small]#Created with https://www.mrdocs.com[MrDocs]# diff --git a/test-files/golden-tests/config/extract-all/no-extract-all.cpp b/test-files/golden-tests/config/extract-all/no-extract-all.cpp new file mode 100644 index 0000000000..190a91ff9c --- /dev/null +++ b/test-files/golden-tests/config/extract-all/no-extract-all.cpp @@ -0,0 +1,9 @@ +void undocFunction(); + +/// Documented function +void docFunction(); + +void sometimesDocFunction(); + +/// Sometimes documented function +void sometimesDocFunction(); \ No newline at end of file diff --git a/test-files/golden-tests/config/extract-all/no-extract-all.html b/test-files/golden-tests/config/extract-all/no-extract-all.html new file mode 100644 index 0000000000..8dc8d1cab9 --- /dev/null +++ b/test-files/golden-tests/config/extract-all/no-extract-all.html @@ -0,0 +1,78 @@ + + +Reference + + +
+

Reference

+
+
+

Global namespace

+
+

Functions

+ + + + + + + + + + + +
NameDescription
docFunction Documented function + +
sometimesDocFunction Sometimes documented function + +
+
+
+
+

docFunction

+
+Documented function + + +
+
+
+

Synopsis

+
+Declared in <no-extract-all.cpp>
+
+
+void
+docFunction();
+
+
+
+
+
+
+

sometimesDocFunction

+
+Sometimes documented function + + +
+
+
+

Synopsis

+
+Declared in <no-extract-all.cpp>
+
+
+void
+sometimesDocFunction();
+
+
+
+
+ +
+
+

Created with MrDocs

+
+ + \ No newline at end of file diff --git a/test-files/golden-tests/config/extract-all/no-extract-all.xml b/test-files/golden-tests/config/extract-all/no-extract-all.xml new file mode 100644 index 0000000000..b376d1ac2c --- /dev/null +++ b/test-files/golden-tests/config/extract-all/no-extract-all.xml @@ -0,0 +1,22 @@ + + + + + + + + Documented function + + + + + + + + Sometimes documented function + + + + + diff --git a/test-files/golden-tests/config/extract-all/no-extract-all.yml b/test-files/golden-tests/config/extract-all/no-extract-all.yml new file mode 100644 index 0000000000..40c88c2c0b --- /dev/null +++ b/test-files/golden-tests/config/extract-all/no-extract-all.yml @@ -0,0 +1 @@ +extract-all: false \ No newline at end of file From 953da77088f1438e206da0d861f085c6411b6ff7 Mon Sep 17 00:00:00 2001 From: alandefreitas Date: Sun, 9 Feb 2025 23:00:01 -0300 Subject: [PATCH 3/8] `warn-if-undocumented` option #feat fix #758 --- docs/mrdocs.schema.json | 12 +++- src/lib/AST/ASTVisitor.cpp | 60 ++++++++++++++++--- src/lib/AST/ASTVisitor.hpp | 44 ++++++++++++++ src/lib/AST/ASTVisitorConsumer.cpp | 8 +-- src/lib/Lib/ConfigOptions.json | 9 ++- src/lib/Lib/CorpusImpl.cpp | 8 +++ src/lib/Lib/CorpusImpl.hpp | 10 +++- src/lib/Lib/ExecutionContext.cpp | 33 +++++++++- src/lib/Lib/ExecutionContext.hpp | 22 ++++--- src/lib/Lib/Info.hpp | 45 ++++++++++++++ .../Metadata/Finalizers/JavadocFinalizer.hpp | 13 ++++ .../config/extract-all/no-extract-all.yml | 3 +- 12 files changed, 238 insertions(+), 29 deletions(-) diff --git a/docs/mrdocs.schema.json b/docs/mrdocs.schema.json index 0009087c2d..222e1c4389 100644 --- a/docs/mrdocs.schema.json +++ b/docs/mrdocs.schema.json @@ -93,7 +93,7 @@ }, "extract-all": { "default": true, - "description": "When set to `true`, MrDocs extracts all symbols from the source code, even if no documentation is provided. When set to `false`, it's still recommendable to provide file and symbol filters so that only the desired symbols are traversed by MrDocs.", + "description": "When set to `true`, MrDocs extracts all symbols from the source code, even if no documentation is provided. MrDocs can only identify whether a symbol is ultimated documented after extracting information from all translation units. For this reason, when this option is set to `false`, it's still recommendable to provide file and symbol filters so that only the desired symbols are traversed and stored by MrDocs.", "enum": [ true, false @@ -450,6 +450,16 @@ "title": "Verbose output", "type": "boolean" }, + "warn-if-undocumented": { + "default": true, + "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`.", + "enum": [ + true, + false + ], + "title": "Warn if symbols are not documented", + "type": "boolean" + }, "warnings": { "default": true, "description": "When set to `true`, MrDocs outputs warning messages during the generation of the documentation. It is usually recommended to enable warnings while writing the documentation.", diff --git a/src/lib/AST/ASTVisitor.cpp b/src/lib/AST/ASTVisitor.cpp index 7903167d79..58ca19d2ff 100644 --- a/src/lib/AST/ASTVisitor.cpp +++ b/src/lib/AST/ASTVisitor.cpp @@ -73,7 +73,7 @@ void ASTVisitor:: build() { - // traverse the translation unit, only extracting + // Traverse the translation unit, only extracting // declarations which satisfy all filter conditions. // dependencies will be tracked, but not extracted TranslationUnitDecl const* TU = context_.getTranslationUnitDecl(); @@ -3291,16 +3291,10 @@ upsert(DeclType const* D) return Unexpected(Error("Explicit declaration in dependency mode")); } } - else if ( - !InfoParent && - mode_ == Regular && - !config_->extractAll && - !D->getASTContext().getRawCommentForDeclNoCache(D)) - { - return Unexpected(formatError("{}: Symbol is undocumented", extractName(D))); - } SymbolID const id = generateID(D); + MRDOCS_TRY(checkUndocumented(id, D)); + MRDOCS_CHECK_MSG(id, "Failed to extract symbol ID"); auto [I, isNew] = upsert(id); @@ -3314,4 +3308,52 @@ upsert(DeclType const* D) return upsertResult{std::ref(I), isNew}; } +template < + std::derived_from InfoTy, + std::derived_from DeclTy> +Expected +ASTVisitor:: +checkUndocumented( + SymbolID const& id, + DeclTy const* D) +{ + // If `extract-all` is enabled, we don't need to + // check for undocumented symbols + MRDOCS_CHECK_OR(!config_->extractAll, {}); + // If the symbol is a namespace, the `extract-all` + // doesn't apply to it + MRDOCS_CHECK_OR((!std::same_as), {}); + // If the symbol is not being extracted as a Regular + // symbol, we don't need to check for undocumented symbols + // These are expected to be potentially undocumented + MRDOCS_CHECK_OR(mode_ == Regular, {}); + // Check if the symbol is documented, ensure this symbol is not in the set + // of undocumented symbols in this translation unit and return + // without an error if it is + if (D->getASTContext().getRawCommentForDeclNoCache(D)) + { + if (config_->warnIfUndocumented) + { + undocumented_.erase({id, extractName(D)}); + } + return {}; + } + // If the symbol is undocumented, check if we haven't seen a + // documented version before. + auto const it = info_.find(id); + if (it != info_.end() && + it->get()->javadoc) + { + return {}; + } + // If the symbol is undocumented, and we haven't seen a documented + // version before, store this symbol in the set of undocumented + // symbols we've seen so far in this translation unit. + if (config_->warnIfUndocumented) + { + undocumented_.insert({id, extractName(D)}); + } + return Unexpected(Error("Undocumented")); +} + } // clang::mrdocs diff --git a/src/lib/AST/ASTVisitor.hpp b/src/lib/AST/ASTVisitor.hpp index ecd6e50264..90f8aa65fd 100644 --- a/src/lib/AST/ASTVisitor.hpp +++ b/src/lib/AST/ASTVisitor.hpp @@ -78,6 +78,29 @@ class ASTVisitor // An unordered set of all extracted Info declarations InfoSet info_; + /* The symbols we would extract if they were documented + + When `extract-all` is false, we only extract symbols + that are documented. If a symbol reappears in the + translation unit, we only extract the declaration + that's documented. + + When `extract-all` is false and `warn-if-undocumented` + is true, we also warn if a symbol is not documented. + However, because a symbol can appear multiple times + in multiple translation units, we cannot be sure + a symbol is undocumented until we have processed + all translation units. + + For this reason, this set stores the symbols that + are not documented but would otherwise have been + extracted as regular symbols in the current + translation unit. After symbols from all translation + units are merged, we will iterate these symbols + and warn if they are not documented. + */ + UndocumentedInfoSet undocumented_; + /* Struct to hold pre-processed file information. This struct stores information about a file, including its full path, @@ -288,6 +311,19 @@ class ASTVisitor return info_; } + /** Get the set of extracted Info declarations. + + This function returns a reference to the set of Info + declarations that have been extracted by the ASTVisitor. + + @return A reference to the InfoSet containing the extracted Info declarations. + */ + UndocumentedInfoSet& + undocumented() + { + return undocumented_; + } + private: // ================================================= // AST Traversal @@ -1156,6 +1192,14 @@ class ASTVisitor InfoTypeFor_t, InfoTy>>> upsert(DeclType const* D); + + template < + std::derived_from InfoTy, + std::derived_from DeclTy> + Expected + checkUndocumented( + SymbolID const& id, + DeclTy const* D); }; } // clang::mrdocs diff --git a/src/lib/AST/ASTVisitorConsumer.cpp b/src/lib/AST/ASTVisitorConsumer.cpp index bb4fd64509..91b049e7ef 100644 --- a/src/lib/AST/ASTVisitorConsumer.cpp +++ b/src/lib/AST/ASTVisitorConsumer.cpp @@ -15,8 +15,7 @@ #include "lib/AST/ASTVisitor.hpp" #include "lib/Support/Path.hpp" -namespace clang { -namespace mrdocs { +namespace clang::mrdocs { void ASTVisitorConsumer:: @@ -31,8 +30,7 @@ HandleTranslationUnit(ASTContext& Context) Context, *sema_); visitor.build(); - ex_.report(std::move(visitor.results()), std::move(diags)); + ex_.report(std::move(visitor.results()), std::move(diags), std::move(visitor.undocumented())); } -} // mrdocs -} // clang +} // clang::mrdocs diff --git a/src/lib/Lib/ConfigOptions.json b/src/lib/Lib/ConfigOptions.json index a1092e9a68..5e1e72ac19 100644 --- a/src/lib/Lib/ConfigOptions.json +++ b/src/lib/Lib/ConfigOptions.json @@ -181,7 +181,7 @@ { "name": "extract-all", "brief": "Extract all symbols", - "details": "When set to `true`, MrDocs extracts all symbols from the source code, even if no documentation is provided. When set to `false`, it's still recommendable to provide file and symbol filters so that only the desired symbols are traversed by MrDocs.", + "details": "When set to `true`, MrDocs extracts all symbols from the source code, even if no documentation is provided. MrDocs can only identify whether a symbol is ultimated documented after extracting information from all translation units. For this reason, when this option is set to `false`, it's still recommendable to provide file and symbol filters so that only the desired symbols are traversed and stored by MrDocs.", "type": "bool", "default": true }, @@ -478,6 +478,13 @@ "details": "When set to `true`, MrDocs outputs warning messages during the generation of the documentation. It is usually recommended to enable warnings while writing the documentation.", "type": "bool", "default": true + }, + { + "name": "warn-if-undocumented", + "brief": "Warn if symbols are not documented", + "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`.", + "type": "bool", + "default": true } ] }, diff --git a/src/lib/Lib/CorpusImpl.cpp b/src/lib/Lib/CorpusImpl.cpp index 4024f226f0..6632589a29 100644 --- a/src/lib/Lib/CorpusImpl.cpp +++ b/src/lib/Lib/CorpusImpl.cpp @@ -524,7 +524,9 @@ build( } MRDOCS_TRY(auto results, context.results()); + auto undocumented = context.undocumented(); corpus->info_ = std::move(results); + corpus->undocumented_ = std::move(undocumented); report::info( "Extracted {} declarations in {}", @@ -598,6 +600,12 @@ CorpusImpl::finalize() report::debug("Finalizing javadoc"); JavadocFinalizer finalizer(*this); finalizer.build(); + + if (!config->extractAll && config->warnIfUndocumented) + { + finalizer.warnUndocumented(); + } + undocumented_.clear(); } diff --git a/src/lib/Lib/CorpusImpl.hpp b/src/lib/Lib/CorpusImpl.hpp index 7d718ad609..d72a7e6046 100644 --- a/src/lib/Lib/CorpusImpl.hpp +++ b/src/lib/Lib/CorpusImpl.hpp @@ -16,15 +16,16 @@ #include "lib/Lib/ConfigImpl.hpp" #include "lib/Lib/Info.hpp" #include "lib/Support/Debug.hpp" -#include -#include -#include #include #include #include #include #include #include +#include +#include +#include +#include namespace clang::mrdocs { @@ -45,6 +46,9 @@ class CorpusImpl final : public Corpus // Info keyed on Symbol ID. InfoSet info_; + // Undocumented symbols + UndocumentedInfoSet undocumented_; + // Lookup cache // The key represents the context symbol ID. // The value is another map from the name to the Info. diff --git a/src/lib/Lib/ExecutionContext.cpp b/src/lib/Lib/ExecutionContext.cpp index 43d24df90c..1bcb1612ce 100644 --- a/src/lib/Lib/ExecutionContext.cpp +++ b/src/lib/Lib/ExecutionContext.cpp @@ -55,7 +55,8 @@ void InfoExecutionContext:: report( InfoSet&& results, - Diagnostics&& diags) + Diagnostics&& diags, + UndocumentedInfoSet&& undocumented) { InfoSet info = std::move(results); // KRYSTIAN TODO: read stage will be required to @@ -67,6 +68,7 @@ report( #endif std::unique_lock write_lock(mutex_); + // Add all new Info to the existing set. info_.merge(info); @@ -80,6 +82,26 @@ report( // Merge diagnostics and report any new messages. diags_.mergeAndReport(std::move(diags)); + + + + // Merge undocumented symbols and remove any symbols + // from undocumented that we can find in info_ with + // documentation from other translation units. + undocumented_.merge(undocumented); + for (auto it = undocumented_.begin(); it != undocumented_.end();) + { + auto infoIt = info_.find(it->first); + if (infoIt != info_.end() && + infoIt->get()->javadoc) + { + it = undocumented_.erase(it); + } + else + { + ++it; + } + } } void @@ -89,12 +111,19 @@ reportEnd(report::Level level) diags_.reportTotals(level); } -mrdocs::Expected +Expected InfoExecutionContext:: results() { return std::move(info_); } +UndocumentedInfoSet +InfoExecutionContext:: +undocumented() +{ + return std::move(undocumented_); +} + } // mrdocs } // clang diff --git a/src/lib/Lib/ExecutionContext.hpp b/src/lib/Lib/ExecutionContext.hpp index 3d67a8cc47..95ddf33648 100644 --- a/src/lib/Lib/ExecutionContext.hpp +++ b/src/lib/Lib/ExecutionContext.hpp @@ -23,8 +23,7 @@ #include #include -namespace clang { -namespace mrdocs { +namespace clang::mrdocs { /** A custom execution context for visitation. @@ -81,7 +80,8 @@ class ExecutionContext void report( InfoSet&& info, - Diagnostics&& diags) = 0; + Diagnostics&& diags, + UndocumentedInfoSet&& undocumented) = 0; /** Called when the execution is complete. @@ -104,6 +104,10 @@ class ExecutionContext virtual mrdocs::Expected results() = 0; + + virtual + UndocumentedInfoSet + undocumented() = 0; }; // ---------------------------------------------------------------- @@ -120,6 +124,7 @@ class InfoExecutionContext std::shared_mutex mutex_; Diagnostics diags_; InfoSet info_; + UndocumentedInfoSet undocumented_; public: using ExecutionContext::ExecutionContext; @@ -128,7 +133,8 @@ class InfoExecutionContext void report( InfoSet&& info, - Diagnostics&& diags) override; + Diagnostics&& diags, + UndocumentedInfoSet&& undocumented) override; /// @copydoc ExecutionContext::reportEnd void @@ -144,11 +150,13 @@ class InfoExecutionContext @return The results of the execution. */ - mrdocs::Expected + Expected results() override; + + UndocumentedInfoSet + undocumented() override; }; -} // mrdocs -} // clang +} // clang::mrdocs #endif diff --git a/src/lib/Lib/Info.hpp b/src/lib/Lib/Info.hpp index 17fec6b723..a59ae83de9 100644 --- a/src/lib/Lib/Info.hpp +++ b/src/lib/Lib/Info.hpp @@ -116,6 +116,51 @@ struct InfoPtrEqual using InfoSet = std::unordered_set< std::unique_ptr, InfoPtrHasher, InfoPtrEqual>; +struct SymbolIDNameHasher { + using is_transparent = void; + + std::size_t + operator()(SymbolID const& I) const { + return std::hash()(I); + } + + std::size_t + operator()(std::pair const& I) const { + return std::hash()(I.first); + } +}; + +struct SymbolIDNameEqual { + using is_transparent = void; + + bool + operator()( + std::pair const& a, + std::pair const& b) const + { + return a.first == b.first; + } + + bool + operator()( + std::pair const& a, + SymbolID const& b) const + { + return a.first == b; + } + + bool + operator()( + SymbolID const& a, + std::pair const& b) const + { + return a == b.first; + } +}; + +using UndocumentedInfoSet = std::unordered_set< + std::pair, SymbolIDNameHasher, SymbolIDNameEqual>; + } // clang::mrdocs #endif diff --git a/src/lib/Metadata/Finalizers/JavadocFinalizer.hpp b/src/lib/Metadata/Finalizers/JavadocFinalizer.hpp index 5b770f5e22..31347e948c 100644 --- a/src/lib/Metadata/Finalizers/JavadocFinalizer.hpp +++ b/src/lib/Metadata/Finalizers/JavadocFinalizer.hpp @@ -52,6 +52,19 @@ class JavadocFinalizer } } + void + warnUndocumented() const + { + for (auto& [id, name] : corpus_.undocumented_) + { + if (Info const* I = corpus_.find(id)) + { + MRDOCS_CHECK_OR(!I->javadoc || I->Extraction == ExtractionMode::Regular); + } + warn("{}: Symbol is undocumented", name); + } + } + void operator()(Info& I) { diff --git a/test-files/golden-tests/config/extract-all/no-extract-all.yml b/test-files/golden-tests/config/extract-all/no-extract-all.yml index 40c88c2c0b..f5fd13a707 100644 --- a/test-files/golden-tests/config/extract-all/no-extract-all.yml +++ b/test-files/golden-tests/config/extract-all/no-extract-all.yml @@ -1 +1,2 @@ -extract-all: false \ No newline at end of file +extract-all: false +warn-if-undocumented: false \ No newline at end of file From 6ea2dacc78caaf91bea3df312cddaa9f7e4719ae Mon Sep 17 00:00:00 2001 From: alandefreitas Date: Mon, 10 Feb 2025 14:26:42 -0300 Subject: [PATCH 4/8] `warn-if-doc-error` option #feat --- docs/mrdocs.schema.json | 10 ++ src/lib/AST/ASTVisitor.cpp | 3 +- src/lib/Lib/ConfigOptions.json | 7 + src/lib/Lib/CorpusImpl.cpp | 7 +- .../Metadata/Finalizers/JavadocFinalizer.cpp | 127 ++++++++++++++++++ .../Metadata/Finalizers/JavadocFinalizer.hpp | 21 ++- .../golden-tests/javadoc/inline/styled.adoc | 11 ++ .../golden-tests/javadoc/inline/styled.cpp | 8 +- .../golden-tests/javadoc/inline/styled.html | 18 +++ .../golden-tests/javadoc/inline/styled.xml | 5 +- .../javadoc/param/param-direction.yml | 1 + .../javadoc/param/param-duplicate.yml | 1 + .../javadoc/ref/operator-param.adoc | 15 ++- .../javadoc/ref/operator-param.cpp | 8 +- .../javadoc/ref/operator-param.html | 22 ++- .../javadoc/ref/operator-param.xml | 7 +- test-files/golden-tests/metadata/sfinae.yml | 1 + 17 files changed, 242 insertions(+), 30 deletions(-) create mode 100644 test-files/golden-tests/javadoc/param/param-direction.yml create mode 100644 test-files/golden-tests/javadoc/param/param-duplicate.yml create mode 100644 test-files/golden-tests/metadata/sfinae.yml diff --git a/docs/mrdocs.schema.json b/docs/mrdocs.schema.json index 222e1c4389..50d49a1280 100644 --- a/docs/mrdocs.schema.json +++ b/docs/mrdocs.schema.json @@ -450,6 +450,16 @@ "title": "Verbose output", "type": "boolean" }, + "warn-if-doc-error": { + "default": true, + "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.", + "enum": [ + true, + false + ], + "title": "Warn if documentation has errors", + "type": "boolean" + }, "warn-if-undocumented": { "default": true, "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`.", diff --git a/src/lib/AST/ASTVisitor.cpp b/src/lib/AST/ASTVisitor.cpp index 58ca19d2ff..26b39b0d6f 100644 --- a/src/lib/AST/ASTVisitor.cpp +++ b/src/lib/AST/ASTVisitor.cpp @@ -3334,7 +3334,8 @@ checkUndocumented( { if (config_->warnIfUndocumented) { - undocumented_.erase({id, extractName(D)}); + auto const it = undocumented_.find(id); + undocumented_.erase(it); } return {}; } diff --git a/src/lib/Lib/ConfigOptions.json b/src/lib/Lib/ConfigOptions.json index 5e1e72ac19..7a5a8f28d6 100644 --- a/src/lib/Lib/ConfigOptions.json +++ b/src/lib/Lib/ConfigOptions.json @@ -485,6 +485,13 @@ "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`.", "type": "bool", "default": true + }, + { + "name": "warn-if-doc-error", + "brief": "Warn if documentation has errors", + "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.", + "type": "bool", + "default": true } ] }, diff --git a/src/lib/Lib/CorpusImpl.cpp b/src/lib/Lib/CorpusImpl.cpp index 6632589a29..b9eda181fc 100644 --- a/src/lib/Lib/CorpusImpl.cpp +++ b/src/lib/Lib/CorpusImpl.cpp @@ -600,12 +600,7 @@ CorpusImpl::finalize() report::debug("Finalizing javadoc"); JavadocFinalizer finalizer(*this); finalizer.build(); - - if (!config->extractAll && config->warnIfUndocumented) - { - finalizer.warnUndocumented(); - } - undocumented_.clear(); + finalizer.emitWarnings(); } diff --git a/src/lib/Metadata/Finalizers/JavadocFinalizer.cpp b/src/lib/Metadata/Finalizers/JavadocFinalizer.cpp index 9004039680..f1f206531f 100644 --- a/src/lib/Metadata/Finalizers/JavadocFinalizer.cpp +++ b/src/lib/Metadata/Finalizers/JavadocFinalizer.cpp @@ -509,4 +509,131 @@ checkExists(SymbolID const& id) const MRDOCS_ASSERT(corpus_.info_.contains(id)); } +void +JavadocFinalizer:: +emitWarnings() const +{ + MRDOCS_CHECK_OR(corpus_.config->warnings); + warnUndocumented(); + warnDocErrors(); +} + +void +JavadocFinalizer:: +warnUndocumented() const +{ + MRDOCS_CHECK_OR(!corpus_.config->extractAll); + MRDOCS_CHECK_OR(corpus_.config->warnIfUndocumented); + for (auto& [id, name] : corpus_.undocumented_) + { + if (Info const* I = corpus_.find(id)) + { + MRDOCS_CHECK_OR(!I->javadoc || I->Extraction == ExtractionMode::Regular); + } + warn("{}: Symbol is undocumented", name); + } + corpus_.undocumented_.clear(); +} + +void +JavadocFinalizer:: +warnDocErrors() const +{ + MRDOCS_CHECK_OR(corpus_.config->warnIfDocError); + for (auto const& I : corpus_.info_) + { + MRDOCS_CHECK_OR_CONTINUE(I->Extraction == ExtractionMode::Regular); + MRDOCS_CHECK_OR_CONTINUE(I->isFunction()); + warnParamErrors(dynamic_cast(*I)); + } +} + +namespace { +/* Get a list of all parameter names in javadoc + + The javadoc parameter names can contain a single parameter or + a list of parameters separated by commas. This function + returns a list of all parameter names in the javadoc. + */ +SmallVector +getJavadocParamNames(Javadoc const& javadoc) +{ + SmallVector result; + for (auto const& javadocParam: javadoc.params) + { + auto const& paramNamesStr = javadocParam.name; + for (auto paramNames = std::views::split(paramNamesStr, ','); + auto const& paramName: paramNames) + { + result.push_back(trim(std::string_view(paramName.begin(), paramName.end()))); + } + } + return result; +} + +} + +void +JavadocFinalizer:: +warnParamErrors(FunctionInfo const& I) const +{ + MRDOCS_CHECK_OR(I.javadoc); + + // Check for duplicate javadoc parameters + auto javadocParamNames = getJavadocParamNames(*I.javadoc); + std::ranges::sort(javadocParamNames); + auto [firstDup, lastUnique] = std::ranges::unique(javadocParamNames); + auto duplicateParamNames = std::ranges::subrange(firstDup, lastUnique); + auto [firstDupDup, _] = std::ranges::unique(duplicateParamNames); + for (auto uniqueDuplicateParamNames = std::ranges::subrange(firstDup, firstDupDup); + std::string_view duplicateParamName: uniqueDuplicateParamNames) + { + auto primaryLoc = getPrimaryLocation(I); + warn( + "{}:{}\n" + "{}: Duplicate parameter documentation for '{}'", + primaryLoc->FullPath, + primaryLoc->LineNumber, + corpus_.Corpus::qualifiedName(I), + duplicateParamName); + } + javadocParamNames.erase(lastUnique, javadocParamNames.end()); + + // Check for function parameters that are not documented in javadoc + auto paramNames = + I.Params | + std::views::transform(&Param::Name) | + std::views::filter([](std::string_view const& name) { return !name.empty(); }); + for (auto const& paramName: paramNames) + { + if (std::ranges::find(javadocParamNames, paramName) == javadocParamNames.end()) + { + auto primaryLoc = getPrimaryLocation(I); + warn( + "{}:{}\n" + "{}: Missing documentation for parameter '{}'", + primaryLoc->FullPath, + primaryLoc->LineNumber, + corpus_.Corpus::qualifiedName(I), + paramName); + } + } + + // Check for documented parameters that don't exist in the function + for (std::string_view javadocParamName: javadocParamNames) + { + if (std::ranges::find(paramNames, javadocParamName) == paramNames.end()) + { + auto primaryLoc = getPrimaryLocation(I); + warn( + "{}:{}\n" + "{}: Documented parameter '{}' does not exist", + primaryLoc->FullPath, + primaryLoc->LineNumber, + corpus_.Corpus::qualifiedName(I), + javadocParamName); + } + } +} + } // clang::mrdocs diff --git a/src/lib/Metadata/Finalizers/JavadocFinalizer.hpp b/src/lib/Metadata/Finalizers/JavadocFinalizer.hpp index 31347e948c..ecf0adbb70 100644 --- a/src/lib/Metadata/Finalizers/JavadocFinalizer.hpp +++ b/src/lib/Metadata/Finalizers/JavadocFinalizer.hpp @@ -53,17 +53,7 @@ class JavadocFinalizer } void - warnUndocumented() const - { - for (auto& [id, name] : corpus_.undocumented_) - { - if (Info const* I = corpus_.find(id)) - { - MRDOCS_CHECK_OR(!I->javadoc || I->Extraction == ExtractionMode::Regular); - } - warn("{}: Symbol is undocumented", name); - } - } + emitWarnings() const; void operator()(Info& I) @@ -182,6 +172,15 @@ class JavadocFinalizer MRDOCS_CHECK_OR(corpus_.config->warnings); return log(report::Level::warn, format, std::forward(args)...); } + + void + warnUndocumented() const; + + void + warnDocErrors() const; + + void + warnParamErrors(FunctionInfo const& I) const; }; } // clang::mrdocs diff --git a/test-files/golden-tests/javadoc/inline/styled.adoc b/test-files/golden-tests/javadoc/inline/styled.adoc index e3fce39c3c..d87ef1eca9 100644 --- a/test-files/golden-tests/javadoc/inline/styled.adoc +++ b/test-files/golden-tests/javadoc/inline/styled.adoc @@ -76,6 +76,17 @@ compare(<> const& other) const noexcept; `‐1` if `*this < other`, `0` if `this == other`, and 1 if `this > other`. +=== Parameters + + +|=== +| Name | Description + +| *other* +| The other object to compare against. + +|=== + [.small]#Created with https://www.mrdocs.com[MrDocs]# diff --git a/test-files/golden-tests/javadoc/inline/styled.cpp b/test-files/golden-tests/javadoc/inline/styled.cpp index 07032ed5d7..1dd48096a7 100644 --- a/test-files/golden-tests/javadoc/inline/styled.cpp +++ b/test-files/golden-tests/javadoc/inline/styled.cpp @@ -2,15 +2,17 @@ Paragraph with `code`, *bold* text, and _italic_ text. - We can also escape these markers: \`, \*, and \_. + We can also escape these markers: \`, \*, and \_. */ struct A { /** Compare function - @return `-1` if `*this < other`, `0` if + @param other The other object to compare against. + + @return `-1` if `*this < other`, `0` if `this == other`, and 1 if `this > other`. - */ + */ int compare(A const& other) const noexcept; }; diff --git a/test-files/golden-tests/javadoc/inline/styled.html b/test-files/golden-tests/javadoc/inline/styled.html index f7493aea40..9eb874a385 100644 --- a/test-files/golden-tests/javadoc/inline/styled.html +++ b/test-files/golden-tests/javadoc/inline/styled.html @@ -91,6 +91,24 @@

Synopsis

Return Value

-1 if *this < other , 0 if this == other , and 1 if this > other .

+ +
+

Parameters

+ + + + + + + + + + + + + +
NameDescription
other

The other object to compare against.

+
diff --git a/test-files/golden-tests/javadoc/inline/styled.xml b/test-files/golden-tests/javadoc/inline/styled.xml index 6e0edddc70..50f72ec1d4 100644 --- a/test-files/golden-tests/javadoc/inline/styled.xml +++ b/test-files/golden-tests/javadoc/inline/styled.xml @@ -22,7 +22,7 @@ - + @@ -48,6 +48,9 @@ this > other . + + The other object to compare against. + diff --git a/test-files/golden-tests/javadoc/param/param-direction.yml b/test-files/golden-tests/javadoc/param/param-direction.yml new file mode 100644 index 0000000000..8101e71ed2 --- /dev/null +++ b/test-files/golden-tests/javadoc/param/param-direction.yml @@ -0,0 +1 @@ +warn-if-doc-error: false \ No newline at end of file diff --git a/test-files/golden-tests/javadoc/param/param-duplicate.yml b/test-files/golden-tests/javadoc/param/param-duplicate.yml new file mode 100644 index 0000000000..8101e71ed2 --- /dev/null +++ b/test-files/golden-tests/javadoc/param/param-duplicate.yml @@ -0,0 +1 @@ +warn-if-doc-error: false \ No newline at end of file diff --git a/test-files/golden-tests/javadoc/ref/operator-param.adoc b/test-files/golden-tests/javadoc/ref/operator-param.adoc index 77cf02384a..d4e0efd43b 100644 --- a/test-files/golden-tests/javadoc/ref/operator-param.adoc +++ b/test-files/golden-tests/javadoc/ref/operator-param.adoc @@ -73,7 +73,7 @@ bool | Name | Description | *ch* -| The character to test. +| The signed character to test. |=== @@ -93,6 +93,17 @@ bool operator()(char ch) const noexcept; ---- +=== Parameters + + +|=== +| Name | Description + +| *ch* +| The signed character to test. + +|=== + [#A-operator_call-0b] == <>::operator() @@ -125,7 +136,7 @@ This function returns true if the character is in the set, otherwise | Name | Description | *ch* -| The character to test. +| The unsigned character to test. |=== diff --git a/test-files/golden-tests/javadoc/ref/operator-param.cpp b/test-files/golden-tests/javadoc/ref/operator-param.cpp index 5fa08c65b8..3079927b04 100644 --- a/test-files/golden-tests/javadoc/ref/operator-param.cpp +++ b/test-files/golden-tests/javadoc/ref/operator-param.cpp @@ -5,13 +5,17 @@ struct A { character is in the set, otherwise it returns false. - @param ch The character to test. + @param ch The unsigned character to test. */ constexpr bool operator()(unsigned char ch) const noexcept; - /// @copydoc A::operator()(unsigned char) const + /** + @copydoc A::operator()(unsigned char) const + + @param ch The signed character to test. + */ constexpr bool operator()(char ch) const noexcept; diff --git a/test-files/golden-tests/javadoc/ref/operator-param.html b/test-files/golden-tests/javadoc/ref/operator-param.html index 4de577f0ec..c0f456fcbc 100644 --- a/test-files/golden-tests/javadoc/ref/operator-param.html +++ b/test-files/golden-tests/javadoc/ref/operator-param.html @@ -89,7 +89,7 @@

Parameters

ch -

The character to test.

+

The signed character to test.

@@ -112,6 +112,24 @@

Synopsis

+
+

Parameters

+ + + + + + + + + + + + + +
NameDescription
ch

The signed character to test.

+
+
@@ -152,7 +170,7 @@

Parameters

ch -

The character to test.

+

The unsigned character to test.

diff --git a/test-files/golden-tests/javadoc/ref/operator-param.xml b/test-files/golden-tests/javadoc/ref/operator-param.xml index 4fcbaa28c9..94ddafb5d4 100644 --- a/test-files/golden-tests/javadoc/ref/operator-param.xml +++ b/test-files/golden-tests/javadoc/ref/operator-param.xml @@ -5,7 +5,7 @@ - + @@ -16,6 +16,9 @@ + + The signed character to test. + @@ -37,7 +40,7 @@ This function returns true if the character is in the set, otherwise it returns false. - The character to test. + The unsigned character to test. diff --git a/test-files/golden-tests/metadata/sfinae.yml b/test-files/golden-tests/metadata/sfinae.yml new file mode 100644 index 0000000000..8101e71ed2 --- /dev/null +++ b/test-files/golden-tests/metadata/sfinae.yml @@ -0,0 +1 @@ +warn-if-doc-error: false \ No newline at end of file From b1dae5cad86c60e280f7c9b36ffa6b8de2bd0fb2 Mon Sep 17 00:00:00 2001 From: alandefreitas Date: Mon, 10 Feb 2025 14:49:35 -0300 Subject: [PATCH 5/8] `warn-no-paramdoc` option #feat --- docs/mrdocs.schema.json | 12 +++- src/lib/Lib/ConfigOptions.json | 9 ++- .../Metadata/Finalizers/JavadocFinalizer.cpp | 68 ++++++++++++++++--- .../Metadata/Finalizers/JavadocFinalizer.hpp | 6 ++ .../copy-dependencies.yml | 3 +- .../config/inherit-base-members/copy.yml | 3 +- .../config/inherit-base-members/never.yml | 3 +- .../config/inherit-base-members/reference.yml | 3 +- .../filters/symbol-name/extraction-mode.yml | 1 + .../javadoc/ref/operator-param.adoc | 10 +++ .../javadoc/ref/operator-param.cpp | 4 ++ .../javadoc/ref/operator-param.html | 10 +++ .../javadoc/ref/operator-param.xml | 10 ++- test-files/golden-tests/metadata/sfinae.yml | 3 +- 14 files changed, 126 insertions(+), 19 deletions(-) diff --git a/docs/mrdocs.schema.json b/docs/mrdocs.schema.json index 50d49a1280..368952dbe0 100644 --- a/docs/mrdocs.schema.json +++ b/docs/mrdocs.schema.json @@ -462,7 +462,7 @@ }, "warn-if-undocumented": { "default": true, - "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`.", + "description": "When set to `true`, MrDocs outputs a warning message if a symbol that passes all filters is not documented.", "enum": [ true, false @@ -470,6 +470,16 @@ "title": "Warn if symbols are not documented", "type": "boolean" }, + "warn-no-paramdoc": { + "default": true, + "description": "When set to `true`, MrDocs outputs a warning message if a named function parameter is not documented.", + "enum": [ + true, + false + ], + "title": "Warn if parameters are not documented", + "type": "boolean" + }, "warnings": { "default": true, "description": "When set to `true`, MrDocs outputs warning messages during the generation of the documentation. It is usually recommended to enable warnings while writing the documentation.", diff --git a/src/lib/Lib/ConfigOptions.json b/src/lib/Lib/ConfigOptions.json index 7a5a8f28d6..7c6ffeadf6 100644 --- a/src/lib/Lib/ConfigOptions.json +++ b/src/lib/Lib/ConfigOptions.json @@ -482,7 +482,7 @@ { "name": "warn-if-undocumented", "brief": "Warn if symbols are not documented", - "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`.", + "details": "When set to `true`, MrDocs outputs a warning message if a symbol that passes all filters is not documented.", "type": "bool", "default": true }, @@ -492,6 +492,13 @@ "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.", "type": "bool", "default": true + }, + { + "name": "warn-no-paramdoc", + "brief": "Warn if parameters are not documented", + "details": "When set to `true`, MrDocs outputs a warning message if a named function parameter is not documented.", + "type": "bool", + "default": true } ] }, diff --git a/src/lib/Metadata/Finalizers/JavadocFinalizer.cpp b/src/lib/Metadata/Finalizers/JavadocFinalizer.cpp index f1f206531f..eada631a67 100644 --- a/src/lib/Metadata/Finalizers/JavadocFinalizer.cpp +++ b/src/lib/Metadata/Finalizers/JavadocFinalizer.cpp @@ -105,7 +105,7 @@ operator()(InfoTy& I) } } -#define INFO(T) template void JavadocFinalizer::operator()(T##Info&); +#define INFO(T) template void JavadocFinalizer::operator()(T## Info&); #include void @@ -516,13 +516,13 @@ emitWarnings() const MRDOCS_CHECK_OR(corpus_.config->warnings); warnUndocumented(); warnDocErrors(); + warnNoParamDocs(); } void JavadocFinalizer:: warnUndocumented() const { - MRDOCS_CHECK_OR(!corpus_.config->extractAll); MRDOCS_CHECK_OR(corpus_.config->warnIfUndocumented); for (auto& [id, name] : corpus_.undocumented_) { @@ -599,10 +599,49 @@ warnParamErrors(FunctionInfo const& I) const } javadocParamNames.erase(lastUnique, javadocParamNames.end()); + // Check for documented parameters that don't exist in the function + auto paramNames = + std::views::transform(I.Params, &Param::Name) | + std::views::filter([](std::string_view const& name) { return !name.empty(); }); + for (std::string_view javadocParamName: javadocParamNames) + { + if (std::ranges::find(paramNames, javadocParamName) == paramNames.end()) + { + auto primaryLoc = getPrimaryLocation(I); + warn( + "{}:{}\n" + "{}: Documented parameter '{}' does not exist", + primaryLoc->FullPath, + primaryLoc->LineNumber, + corpus_.Corpus::qualifiedName(I), + javadocParamName); + } + } + +} + +void +JavadocFinalizer:: +warnNoParamDocs() const +{ + MRDOCS_CHECK_OR(corpus_.config->warnNoParamdoc); + for (auto const& I : corpus_.info_) + { + MRDOCS_CHECK_OR_CONTINUE(I->Extraction == ExtractionMode::Regular); + MRDOCS_CHECK_OR_CONTINUE(I->isFunction()); + MRDOCS_CHECK_OR_CONTINUE(I->javadoc); + warnNoParamDocs(dynamic_cast(*I)); + } +} + +void +JavadocFinalizer:: +warnNoParamDocs(FunctionInfo const& I) const +{ // Check for function parameters that are not documented in javadoc + auto javadocParamNames = getJavadocParamNames(*I.javadoc); auto paramNames = - I.Params | - std::views::transform(&Param::Name) | + std::views::transform(I.Params, &Param::Name) | std::views::filter([](std::string_view const& name) { return !name.empty(); }); for (auto const& paramName: paramNames) { @@ -619,21 +658,30 @@ warnParamErrors(FunctionInfo const& I) const } } - // Check for documented parameters that don't exist in the function - for (std::string_view javadocParamName: javadocParamNames) + // Check for undocumented return type + if (I.javadoc->returns.empty() && I.ReturnType) { - if (std::ranges::find(paramNames, javadocParamName) == paramNames.end()) + auto isVoid = [](TypeInfo const& returnType) -> bool + { + if (returnType.isNamed()) + { + auto const& namedReturnType = dynamic_cast(returnType); + return namedReturnType.Name->Name == "void"; + } + return false; + }; + if (!isVoid(*I.ReturnType)) { auto primaryLoc = getPrimaryLocation(I); warn( "{}:{}\n" - "{}: Documented parameter '{}' does not exist", + "{}: Missing documentation for return type", primaryLoc->FullPath, primaryLoc->LineNumber, - corpus_.Corpus::qualifiedName(I), - javadocParamName); + corpus_.Corpus::qualifiedName(I)); } } } + } // clang::mrdocs diff --git a/src/lib/Metadata/Finalizers/JavadocFinalizer.hpp b/src/lib/Metadata/Finalizers/JavadocFinalizer.hpp index ecf0adbb70..bfca60de33 100644 --- a/src/lib/Metadata/Finalizers/JavadocFinalizer.hpp +++ b/src/lib/Metadata/Finalizers/JavadocFinalizer.hpp @@ -181,6 +181,12 @@ class JavadocFinalizer void warnParamErrors(FunctionInfo const& I) const; + + void + warnNoParamDocs() const; + + void + warnNoParamDocs(FunctionInfo const& I) const; }; } // clang::mrdocs diff --git a/test-files/golden-tests/config/inherit-base-members/copy-dependencies.yml b/test-files/golden-tests/config/inherit-base-members/copy-dependencies.yml index 3fff044288..e31d214b0a 100644 --- a/test-files/golden-tests/config/inherit-base-members/copy-dependencies.yml +++ b/test-files/golden-tests/config/inherit-base-members/copy-dependencies.yml @@ -1,3 +1,4 @@ inherit-base-members: copy-dependencies exclude-symbols: - - excluded_base \ No newline at end of file + - excluded_base +warn-no-paramdoc: false \ No newline at end of file diff --git a/test-files/golden-tests/config/inherit-base-members/copy.yml b/test-files/golden-tests/config/inherit-base-members/copy.yml index a0b9576ff9..7e1ebf462e 100644 --- a/test-files/golden-tests/config/inherit-base-members/copy.yml +++ b/test-files/golden-tests/config/inherit-base-members/copy.yml @@ -1,3 +1,4 @@ inherit-base-members: copy-all exclude-symbols: - - excluded_base \ No newline at end of file + - excluded_base +warn-no-paramdoc: false \ No newline at end of file diff --git a/test-files/golden-tests/config/inherit-base-members/never.yml b/test-files/golden-tests/config/inherit-base-members/never.yml index ae20f219cc..ffb7e48ed9 100644 --- a/test-files/golden-tests/config/inherit-base-members/never.yml +++ b/test-files/golden-tests/config/inherit-base-members/never.yml @@ -1,3 +1,4 @@ inherit-base-members: never exclude-symbols: - - excluded_base \ No newline at end of file + - excluded_base +warn-no-paramdoc: false \ No newline at end of file diff --git a/test-files/golden-tests/config/inherit-base-members/reference.yml b/test-files/golden-tests/config/inherit-base-members/reference.yml index f13aa554cc..32866fc1b3 100644 --- a/test-files/golden-tests/config/inherit-base-members/reference.yml +++ b/test-files/golden-tests/config/inherit-base-members/reference.yml @@ -1,3 +1,4 @@ inherit-base-members: reference exclude-symbols: - - excluded_base \ No newline at end of file + - excluded_base +warn-no-paramdoc: false \ No newline at end of file diff --git a/test-files/golden-tests/filters/symbol-name/extraction-mode.yml b/test-files/golden-tests/filters/symbol-name/extraction-mode.yml index 6bd1b3b05f..08d310ddbd 100644 --- a/test-files/golden-tests/filters/symbol-name/extraction-mode.yml +++ b/test-files/golden-tests/filters/symbol-name/extraction-mode.yml @@ -29,3 +29,4 @@ implementation-defined: - 'excluded_ns::implementation_defined' - 'implementation_defined_ns' - 'implementation_defined' +warn-no-paramdoc: false \ No newline at end of file diff --git a/test-files/golden-tests/javadoc/ref/operator-param.adoc b/test-files/golden-tests/javadoc/ref/operator-param.adoc index d4e0efd43b..7c60c56348 100644 --- a/test-files/golden-tests/javadoc/ref/operator-param.adoc +++ b/test-files/golden-tests/javadoc/ref/operator-param.adoc @@ -93,6 +93,11 @@ bool operator()(char ch) const noexcept; ---- +=== Return Value + + +True if ch is in the set, otherwise false. + === Parameters @@ -129,6 +134,11 @@ This function returns true if the character is in the set, otherwise +=== Return Value + + +True if ch is in the set, otherwise false. + === Parameters diff --git a/test-files/golden-tests/javadoc/ref/operator-param.cpp b/test-files/golden-tests/javadoc/ref/operator-param.cpp index 3079927b04..5ccd8a2de7 100644 --- a/test-files/golden-tests/javadoc/ref/operator-param.cpp +++ b/test-files/golden-tests/javadoc/ref/operator-param.cpp @@ -6,6 +6,8 @@ struct A { it returns false. @param ch The unsigned character to test. + + @return True if ch is in the set, otherwise false. */ constexpr bool @@ -15,6 +17,8 @@ struct A { @copydoc A::operator()(unsigned char) const @param ch The signed character to test. + + @return True if ch is in the set, otherwise false. */ constexpr bool diff --git a/test-files/golden-tests/javadoc/ref/operator-param.html b/test-files/golden-tests/javadoc/ref/operator-param.html index c0f456fcbc..2db8b846d8 100644 --- a/test-files/golden-tests/javadoc/ref/operator-param.html +++ b/test-files/golden-tests/javadoc/ref/operator-param.html @@ -111,6 +111,11 @@

Synopsis

operator()(char ch) const noexcept; +
+
+

Return Value

+

True if ch is in the set, otherwise false.

+

Parameters

@@ -157,6 +162,11 @@

Description

This function returns true if the character is in the set, otherwise it returns false.

+
+
+

Return Value

+

True if ch is in the set, otherwise false.

+

Parameters

diff --git a/test-files/golden-tests/javadoc/ref/operator-param.xml b/test-files/golden-tests/javadoc/ref/operator-param.xml index 94ddafb5d4..b38c14b977 100644 --- a/test-files/golden-tests/javadoc/ref/operator-param.xml +++ b/test-files/golden-tests/javadoc/ref/operator-param.xml @@ -5,7 +5,7 @@ - + @@ -16,13 +16,16 @@ + + True if ch is in the set, otherwise false. + The signed character to test. - + @@ -39,6 +42,9 @@ This function returns true if the character is in the set, otherwise it returns false. + + True if ch is in the set, otherwise false. + The unsigned character to test. diff --git a/test-files/golden-tests/metadata/sfinae.yml b/test-files/golden-tests/metadata/sfinae.yml index 8101e71ed2..8061ae78c2 100644 --- a/test-files/golden-tests/metadata/sfinae.yml +++ b/test-files/golden-tests/metadata/sfinae.yml @@ -1 +1,2 @@ -warn-if-doc-error: false \ No newline at end of file +warn-if-doc-error: false +warn-no-paramdoc: false From b745b7f8776c1400dcae74d28f6259b6a1cc5c51 Mon Sep 17 00:00:00 2001 From: alandefreitas Date: Mon, 10 Feb 2025 14:57:07 -0300 Subject: [PATCH 6/8] `warn-if-undoc-enum-val` option #feat --- docs/mrdocs.schema.json | 10 ++++++++++ src/lib/Lib/ConfigOptions.json | 7 +++++++ .../Metadata/Finalizers/JavadocFinalizer.cpp | 20 +++++++++++++++++++ .../Metadata/Finalizers/JavadocFinalizer.hpp | 3 +++ test-files/golden-tests/metadata/enum.yml | 1 + 5 files changed, 41 insertions(+) create mode 100644 test-files/golden-tests/metadata/enum.yml diff --git a/docs/mrdocs.schema.json b/docs/mrdocs.schema.json index 368952dbe0..241021b13c 100644 --- a/docs/mrdocs.schema.json +++ b/docs/mrdocs.schema.json @@ -460,6 +460,16 @@ "title": "Warn if documentation has errors", "type": "boolean" }, + "warn-if-undoc-enum-val": { + "default": true, + "description": "When set to `true`, MrDocs outputs a warning message if an enum value is not documented.", + "enum": [ + true, + false + ], + "title": "Warn if enum values are not documented", + "type": "boolean" + }, "warn-if-undocumented": { "default": true, "description": "When set to `true`, MrDocs outputs a warning message if a symbol that passes all filters is not documented.", diff --git a/src/lib/Lib/ConfigOptions.json b/src/lib/Lib/ConfigOptions.json index 7c6ffeadf6..8f4f5ac276 100644 --- a/src/lib/Lib/ConfigOptions.json +++ b/src/lib/Lib/ConfigOptions.json @@ -499,6 +499,13 @@ "details": "When set to `true`, MrDocs outputs a warning message if a named function parameter is not documented.", "type": "bool", "default": true + }, + { + "name": "warn-if-undoc-enum-val", + "brief": "Warn if enum values are not documented", + "details": "When set to `true`, MrDocs outputs a warning message if an enum value is not documented.", + "type": "bool", + "default": true } ] }, diff --git a/src/lib/Metadata/Finalizers/JavadocFinalizer.cpp b/src/lib/Metadata/Finalizers/JavadocFinalizer.cpp index eada631a67..8b8bb7b5a3 100644 --- a/src/lib/Metadata/Finalizers/JavadocFinalizer.cpp +++ b/src/lib/Metadata/Finalizers/JavadocFinalizer.cpp @@ -517,6 +517,7 @@ emitWarnings() const warnUndocumented(); warnDocErrors(); warnNoParamDocs(); + warnUndocEnumValues(); } void @@ -683,5 +684,24 @@ warnNoParamDocs(FunctionInfo const& I) const } } +void +JavadocFinalizer:: +warnUndocEnumValues() const +{ + MRDOCS_CHECK_OR(corpus_.config->warnIfUndocEnumVal); + for (auto const& I : corpus_.info_) + { + MRDOCS_CHECK_OR_CONTINUE(I->isEnumConstant()); + MRDOCS_CHECK_OR_CONTINUE(I->Extraction == ExtractionMode::Regular); + MRDOCS_CHECK_OR_CONTINUE(!I->javadoc); + auto primaryLoc = getPrimaryLocation(*I); + warn( + "{}:{}\n" + "{}: Missing documentation for enum value", + primaryLoc->FullPath, + primaryLoc->LineNumber, + corpus_.Corpus::qualifiedName(*I)); + } +} } // clang::mrdocs diff --git a/src/lib/Metadata/Finalizers/JavadocFinalizer.hpp b/src/lib/Metadata/Finalizers/JavadocFinalizer.hpp index bfca60de33..e10fe98071 100644 --- a/src/lib/Metadata/Finalizers/JavadocFinalizer.hpp +++ b/src/lib/Metadata/Finalizers/JavadocFinalizer.hpp @@ -187,6 +187,9 @@ class JavadocFinalizer void warnNoParamDocs(FunctionInfo const& I) const; + + void + warnUndocEnumValues() const; }; } // clang::mrdocs diff --git a/test-files/golden-tests/metadata/enum.yml b/test-files/golden-tests/metadata/enum.yml new file mode 100644 index 0000000000..c5464a44cd --- /dev/null +++ b/test-files/golden-tests/metadata/enum.yml @@ -0,0 +1 @@ +warn-if-undoc-enum-val: false \ No newline at end of file From 9476d2907b15422bc7735e45e2418089fc363377 Mon Sep 17 00:00:00 2001 From: alandefreitas Date: Mon, 10 Feb 2025 15:05:46 -0300 Subject: [PATCH 7/8] `warn-broken-ref` option #feat --- docs/mrdocs.schema.json | 10 ++++ src/lib/Lib/ConfigOptions.json | 7 +++ .../Metadata/Finalizers/JavadocFinalizer.cpp | 60 +++++++++++-------- .../config/auto-brief/auto-brief.yml | 2 +- .../config/auto-brief/no-auto-brief.yml | 2 +- .../golden-tests/javadoc/ref/broken-ref.yml | 2 +- 6 files changed, 55 insertions(+), 28 deletions(-) diff --git a/docs/mrdocs.schema.json b/docs/mrdocs.schema.json index 241021b13c..34c23cdb6d 100644 --- a/docs/mrdocs.schema.json +++ b/docs/mrdocs.schema.json @@ -450,6 +450,16 @@ "title": "Verbose output", "type": "boolean" }, + "warn-broken-ref": { + "default": true, + "description": "When set to `true`, MrDocs outputs a warning message if a reference in the documentation is broken.", + "enum": [ + true, + false + ], + "title": "Warn if a documentation reference is broken", + "type": "boolean" + }, "warn-if-doc-error": { "default": true, "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.", diff --git a/src/lib/Lib/ConfigOptions.json b/src/lib/Lib/ConfigOptions.json index 8f4f5ac276..6d28ac3b79 100644 --- a/src/lib/Lib/ConfigOptions.json +++ b/src/lib/Lib/ConfigOptions.json @@ -506,6 +506,13 @@ "details": "When set to `true`, MrDocs outputs a warning message if an enum value is not documented.", "type": "bool", "default": true + }, + { + "name": "warn-broken-ref", + "brief": "Warn if a documentation reference is broken", + "details": "When set to `true`, MrDocs outputs a warning message if a reference in the documentation is broken.", + "type": "bool", + "default": true } ] }, diff --git a/src/lib/Metadata/Finalizers/JavadocFinalizer.cpp b/src/lib/Metadata/Finalizers/JavadocFinalizer.cpp index 8b8bb7b5a3..bf914dde36 100644 --- a/src/lib/Metadata/Finalizers/JavadocFinalizer.cpp +++ b/src/lib/Metadata/Finalizers/JavadocFinalizer.cpp @@ -118,6 +118,8 @@ finalize(doc::Reference& ref) ref.id = res->id; } if (res == nullptr && + corpus_.config->warnings && + corpus_.config->warnBrokenRef && // Only warn once per reference !warned_.contains({ref.string, current_context_->Name}) && // Ignore std:: references @@ -128,7 +130,7 @@ finalize(doc::Reference& ref) MRDOCS_ASSERT(current_context_); if (auto primaryLoc = getPrimaryLocation(*current_context_)) { - warn( + this->warn( "{}:{}\n{}: Failed to resolve reference to '{}'", primaryLoc->FullPath, primaryLoc->LineNumber, @@ -281,10 +283,13 @@ copyBriefAndDetails(Javadoc& javadoc) Info const* res = corpus_.lookup(current_context_->id, copied->string); if (!res) { - MRDOCS_ASSERT(current_context_); - if (auto primaryLoc = getPrimaryLocation(*current_context_)) + if (corpus_.config->warnings && + corpus_.config->warnBrokenRef && + !warned_.contains({copied->string, current_context_->Name})) { - warn( + MRDOCS_ASSERT(current_context_); + auto primaryLoc = getPrimaryLocation(*current_context_); + this->warn( "{}:{}\n" "{}: Failed to copy documentation from '{}'\n" " Note: Symbol '{}' not found.", @@ -304,21 +309,26 @@ copyBriefAndDetails(Javadoc& javadoc) } if (!res->javadoc) { - auto ctxPrimaryLoc = getPrimaryLocation(*current_context_); - auto resPrimaryLoc = getPrimaryLocation(*res); - warn( - "{}:{}\n" - "{}: Failed to copy documentation from '{}'.\n" - "No documentation available.\n" - " {}:{}\n" - " Note: No documentation available for '{}'.", - ctxPrimaryLoc->FullPath, - ctxPrimaryLoc->LineNumber, - corpus_.Corpus::qualifiedName(*current_context_), - copied->string, - resPrimaryLoc->FullPath, - resPrimaryLoc->LineNumber, - corpus_.Corpus::qualifiedName(*res)); + if (corpus_.config->warnings && + corpus_.config->warnBrokenRef && + !warned_.contains({copied->string, current_context_->Name})) + { + auto ctxPrimaryLoc = getPrimaryLocation(*current_context_); + auto resPrimaryLoc = getPrimaryLocation(*res); + this->warn( + "{}:{}\n" + "{}: Failed to copy documentation from '{}'.\n" + "No documentation available.\n" + " {}:{}\n" + " Note: No documentation available for '{}'.", + ctxPrimaryLoc->FullPath, + ctxPrimaryLoc->LineNumber, + corpus_.Corpus::qualifiedName(*current_context_), + copied->string, + resPrimaryLoc->FullPath, + resPrimaryLoc->LineNumber, + corpus_.Corpus::qualifiedName(*res)); + } continue; } @@ -531,7 +541,7 @@ warnUndocumented() const { MRDOCS_CHECK_OR(!I->javadoc || I->Extraction == ExtractionMode::Regular); } - warn("{}: Symbol is undocumented", name); + this->warn("{}: Symbol is undocumented", name); } corpus_.undocumented_.clear(); } @@ -590,7 +600,7 @@ warnParamErrors(FunctionInfo const& I) const std::string_view duplicateParamName: uniqueDuplicateParamNames) { auto primaryLoc = getPrimaryLocation(I); - warn( + this->warn( "{}:{}\n" "{}: Duplicate parameter documentation for '{}'", primaryLoc->FullPath, @@ -609,7 +619,7 @@ warnParamErrors(FunctionInfo const& I) const if (std::ranges::find(paramNames, javadocParamName) == paramNames.end()) { auto primaryLoc = getPrimaryLocation(I); - warn( + this->warn( "{}:{}\n" "{}: Documented parameter '{}' does not exist", primaryLoc->FullPath, @@ -649,7 +659,7 @@ warnNoParamDocs(FunctionInfo const& I) const if (std::ranges::find(javadocParamNames, paramName) == javadocParamNames.end()) { auto primaryLoc = getPrimaryLocation(I); - warn( + this->warn( "{}:{}\n" "{}: Missing documentation for parameter '{}'", primaryLoc->FullPath, @@ -674,7 +684,7 @@ warnNoParamDocs(FunctionInfo const& I) const if (!isVoid(*I.ReturnType)) { auto primaryLoc = getPrimaryLocation(I); - warn( + this->warn( "{}:{}\n" "{}: Missing documentation for return type", primaryLoc->FullPath, @@ -695,7 +705,7 @@ warnUndocEnumValues() const MRDOCS_CHECK_OR_CONTINUE(I->Extraction == ExtractionMode::Regular); MRDOCS_CHECK_OR_CONTINUE(!I->javadoc); auto primaryLoc = getPrimaryLocation(*I); - warn( + this->warn( "{}:{}\n" "{}: Missing documentation for enum value", primaryLoc->FullPath, diff --git a/test-files/golden-tests/config/auto-brief/auto-brief.yml b/test-files/golden-tests/config/auto-brief/auto-brief.yml index ee5cfa7bd1..9ffbd33c7a 100644 --- a/test-files/golden-tests/config/auto-brief/auto-brief.yml +++ b/test-files/golden-tests/config/auto-brief/auto-brief.yml @@ -1,2 +1,2 @@ auto-brief: true -warnings: false \ No newline at end of file +warn-broken-ref: false \ No newline at end of file diff --git a/test-files/golden-tests/config/auto-brief/no-auto-brief.yml b/test-files/golden-tests/config/auto-brief/no-auto-brief.yml index 4ec9b956e7..daec9b4925 100644 --- a/test-files/golden-tests/config/auto-brief/no-auto-brief.yml +++ b/test-files/golden-tests/config/auto-brief/no-auto-brief.yml @@ -1,2 +1,2 @@ auto-brief: false -warnings: false \ No newline at end of file +warn-broken-ref: false \ No newline at end of file diff --git a/test-files/golden-tests/javadoc/ref/broken-ref.yml b/test-files/golden-tests/javadoc/ref/broken-ref.yml index bc089c1ffb..bc099e2ca6 100644 --- a/test-files/golden-tests/javadoc/ref/broken-ref.yml +++ b/test-files/golden-tests/javadoc/ref/broken-ref.yml @@ -1 +1 @@ -warnings: false \ No newline at end of file +warn-broken-ref: false \ No newline at end of file From bdeabb90acf7cfeb7668b87904933b5005430fcb Mon Sep 17 00:00:00 2001 From: alandefreitas Date: Mon, 10 Feb 2025 15:26:39 -0300 Subject: [PATCH 8/8] `warn-as-error` option #feat --- docs/mrdocs.schema.json | 10 ++++++++++ src/lib/Lib/ConfigOptions.json | 7 +++++++ src/lib/Metadata/Finalizers/JavadocFinalizer.hpp | 3 ++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/docs/mrdocs.schema.json b/docs/mrdocs.schema.json index 34c23cdb6d..9f406b335b 100644 --- a/docs/mrdocs.schema.json +++ b/docs/mrdocs.schema.json @@ -450,6 +450,16 @@ "title": "Verbose output", "type": "boolean" }, + "warn-as-error": { + "default": false, + "description": "When set to `true`, MrDocs treats warnings as errors and stops the generation of the documentation.", + "enum": [ + true, + false + ], + "title": "Treat warnings as errors", + "type": "boolean" + }, "warn-broken-ref": { "default": true, "description": "When set to `true`, MrDocs outputs a warning message if a reference in the documentation is broken.", diff --git a/src/lib/Lib/ConfigOptions.json b/src/lib/Lib/ConfigOptions.json index 6d28ac3b79..934d59af57 100644 --- a/src/lib/Lib/ConfigOptions.json +++ b/src/lib/Lib/ConfigOptions.json @@ -513,6 +513,13 @@ "details": "When set to `true`, MrDocs outputs a warning message if a reference in the documentation is broken.", "type": "bool", "default": true + }, + { + "name": "warn-as-error", + "brief": "Treat warnings as errors", + "details": "When set to `true`, MrDocs treats warnings as errors and stops the generation of the documentation.", + "type": "bool", + "default": false } ] }, diff --git a/src/lib/Metadata/Finalizers/JavadocFinalizer.hpp b/src/lib/Metadata/Finalizers/JavadocFinalizer.hpp index e10fe98071..2da3a604b7 100644 --- a/src/lib/Metadata/Finalizers/JavadocFinalizer.hpp +++ b/src/lib/Metadata/Finalizers/JavadocFinalizer.hpp @@ -170,7 +170,8 @@ class JavadocFinalizer Args&&... args) const { MRDOCS_CHECK_OR(corpus_.config->warnings); - return log(report::Level::warn, format, std::forward(args)...); + auto const level = !corpus_.config->warnAsError ? report::Level::warn : report::Level::error; + return log(level, format, std::forward(args)...); } void