diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6a1875085..6839a306d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -691,8 +691,11 @@ jobs: for generator in "${generators[@]}"; do [[ $generator = xml && $variant = multi ]] && continue [[ $variant = multi ]] && multipage="true" || multipage="false" + # mrdocs boost.url demo mrdocs --config="$(pwd)/boost/libs/url/doc/mrdocs.yml" "../CMakeLists.txt" --output="$(pwd)/demos/boost-url/$variant/$generator" --multipage=$multipage --generate="$generator" --log-level=debug echo "Number of files in demos/boost-url/$variant/$generator: $(find demos/boost-url/$variant/$generator -type f | wc -l)" + # mrdocs documenting mrdocs demo + mrdocs --config="$(pwd)/docs/mrdocs.yml" "$(pwd)/CMakeLists.txt" --output="$(pwd)/demos/mrdocs/$variant/$generator" --multipage=$multipage --generate="$generator" --log-level=debug done done diff --git a/CMakeLists.txt b/CMakeLists.txt index e5f64ca6b..3195e21e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,6 +52,35 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang$") set(MRDOCS_CLANG ON) endif() +#------------------------------------------------- +# +# Docs build +# +#------------------------------------------------- +if (MRDOCS_DOCUMENTATION_BUILD) + # Glob all header files + set(INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include") + file(GLOB_RECURSE HEADER_FILES_LIST "${INCLUDE_DIR}/*.hpp") + + # Create a temporary source file that includes all header files + set(TEMP_CPP_FILE "${CMAKE_CURRENT_BINARY_DIR}/all_headers.cpp") + file(WRITE ${TEMP_CPP_FILE} "// This file is generated automatically by CMake\n\n") + foreach(HEADER_FILE ${HEADER_FILES_LIST}) + file(APPEND ${TEMP_CPP_FILE} "#include \"${HEADER_FILE}\"\n") + endforeach() + + # Create a custom target for MrDocs + add_library(mrdocs-documentation-build STATIC ${TEMP_CPP_FILE}) + + # Set any other target properties here + target_include_directories(mrdocs-documentation-build PRIVATE ${INCLUDE_DIR}) + target_link_libraries(mrdocs-documentation-build PRIVATE an_external_library) + target_compile_definitions(mrdocs-documentation-build PRIVATE MRDOCS_STATIC_LINK) + + # Don't create any other targets + return() +endif() + #------------------------------------------------- # # Dependencies @@ -294,6 +323,10 @@ if (MRDOCS_CLANG) ) endif () +if (MRDOCS_DOCUMENTATION_BUILD) + return() +endif() + #------------------------------------------------- # # Tool diff --git a/bootstrap.py b/bootstrap.py index d93d03aff..d91c1aa6f 100644 --- a/bootstrap.py +++ b/bootstrap.py @@ -2355,6 +2355,14 @@ def generate_run_configs(self): os.path.join(self.options.mrdocs_build_dir)], "cwd": self.options.mrdocs_src_dir }) + configs.append({ + "name": f"MrDocs Generate Config Info (docs)", + "script": os.path.join(self.options.mrdocs_src_dir, 'util', 'generate-config-info.py'), + "folder": "MrDocs Generate Config Info", + "args": [os.path.join(self.options.mrdocs_src_dir, 'src', 'lib', 'ConfigOptions.json'), + os.path.join(self.options.mrdocs_src_dir, 'docs', 'config-headers')], + "cwd": self.options.mrdocs_src_dir + }) configs.append({ "name": f"MrDocs Generate YAML Schema", "script": os.path.join(self.options.mrdocs_src_dir, 'util', 'generate-yaml-schema.py'), diff --git a/docs/config-headers/README.md b/docs/config-headers/README.md new file mode 100644 index 000000000..2c79c7f6f --- /dev/null +++ b/docs/config-headers/README.md @@ -0,0 +1 @@ +This directory includes a version of the configuration headers meant to be used for documentation purposes. These headers are generated by CMake when building the project, but they are not included in main header files. \ No newline at end of file diff --git a/docs/config-headers/include/mrdocs/PublicSettings.hpp b/docs/config-headers/include/mrdocs/PublicSettings.hpp new file mode 100644 index 000000000..007e09ac5 --- /dev/null +++ b/docs/config-headers/include/mrdocs/PublicSettings.hpp @@ -0,0 +1,1871 @@ +/* + * This file is generated automatically from the json file + * `src/lib/Lib/ConfigOptions.json`. Do not edit this file + * manually. Instead, edit the json file and run the script + * `util/generate-config-info.py` to regenerate this file. + */ + +#ifndef MRDOCS_PUBLIC_SETTINGS_HPP +#define MRDOCS_PUBLIC_SETTINGS_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace clang::mrdocs { + +struct PublicSettings { + //-------------------------------------------- + // Enums + //-------------------------------------------- + + /** Enum for "generator" options + + This enumeration value is valid for the `generator` option + + */ + enum class Generator { + Adoc, + Html, + Xml, + }; + + static + constexpr + std::string_view + toString(Generator const e) { + switch (e) { + case Generator::Adoc: + return "adoc"; + case Generator::Html: + return "html"; + case Generator::Xml: + return "xml"; + } + return ""; + } + + static + constexpr + bool + fromString(std::string_view const str, Generator& e) { + if (str == "adoc") + { + e = Generator::Adoc; + return true; + } + if (str == "html") + { + e = Generator::Html; + return true; + } + if (str == "xml") + { + e = Generator::Xml; + return true; + } + return false; + } + + /** Enum for "log-level" options + + This enumeration value is valid for the `log-level` option + + */ + enum class LogLevel { + Trace, + Debug, + Info, + Warn, + Error, + Fatal, + }; + + static + constexpr + std::string_view + toString(LogLevel const e) { + switch (e) { + case LogLevel::Trace: + return "trace"; + case LogLevel::Debug: + return "debug"; + case LogLevel::Info: + return "info"; + case LogLevel::Warn: + return "warn"; + case LogLevel::Error: + return "error"; + case LogLevel::Fatal: + return "fatal"; + } + return ""; + } + + static + constexpr + bool + fromString(std::string_view const str, LogLevel& e) { + if (str == "trace") + { + e = LogLevel::Trace; + return true; + } + if (str == "debug") + { + e = LogLevel::Debug; + return true; + } + if (str == "info") + { + e = LogLevel::Info; + return true; + } + if (str == "warn") + { + e = LogLevel::Warn; + return true; + } + if (str == "error") + { + e = LogLevel::Error; + return true; + } + if (str == "fatal") + { + e = LogLevel::Fatal; + return true; + } + return false; + } + + /** Enum for "base-member-inheritance" options + + This enumeration value is valid for the `inherit-base-members` option + + */ + enum class BaseMemberInheritance { + Never, + Reference, + CopyDependencies, + CopyAll, + }; + + static + constexpr + std::string_view + toString(BaseMemberInheritance const e) { + switch (e) { + case BaseMemberInheritance::Never: + return "never"; + case BaseMemberInheritance::Reference: + return "reference"; + case BaseMemberInheritance::CopyDependencies: + return "copy-dependencies"; + case BaseMemberInheritance::CopyAll: + return "copy-all"; + } + return ""; + } + + static + constexpr + bool + fromString(std::string_view const str, BaseMemberInheritance& e) { + if (str == "never") + { + e = BaseMemberInheritance::Never; + return true; + } + if (str == "reference") + { + e = BaseMemberInheritance::Reference; + return true; + } + if (str == "copy-dependencies") + { + e = BaseMemberInheritance::CopyDependencies; + return true; + } + if (str == "copy-all") + { + e = BaseMemberInheritance::CopyAll; + return true; + } + return false; + } + + /** Enum for "sort-symbol-by" options + + These enumeration values are valid for the following options: + + - sort-members-by + - sort-namespace-members-by + + */ + enum class SortSymbolBy { + Name, + Location, + }; + + static + constexpr + std::string_view + toString(SortSymbolBy const e) { + switch (e) { + case SortSymbolBy::Name: + return "name"; + case SortSymbolBy::Location: + return "location"; + } + return ""; + } + + static + constexpr + bool + fromString(std::string_view const str, SortSymbolBy& e) { + if (str == "name") + { + e = SortSymbolBy::Name; + return true; + } + if (str == "location") + { + e = SortSymbolBy::Location; + return true; + } + return false; + } + + //-------------------------------------------- + // Command Line Options + // + // Options that can only be provided via the command line + //-------------------------------------------- + + /** Configuration or compilation database files + + The inputs are configuration files or compilation database files that + used to generate the documentation. + + When the input ends with `mrdocs.yml`, it is interpreted as a + configuration file, the file is read and the options are used to + generate the documentation as if it was provided to the `config` + option. + + When the input ends with `compilation_database.json` or + `CMakeLists.txt`, it is interpreted as a compilation database file, + the file is read and the compiler flags are used to generate the + documentation as if it was provided to the `compilation-database` + option. + */ + std::vector cmdLineInputs; + + /** Mr.Docs configuration file + + The configuration file is a YAML file that contains the options used + to generate the documentation. + + The configuration file is read and the options are used to generate + the documentation. + + The configuration file can be used to specify the source code, the + output directory, the compilation database, the generator, and the + filters. + */ + std::string config; + + //-------------------------------------------- + // Paths + // + // Paths to the source code and output directories + //-------------------------------------------- + + /** Path to the root directory of the source code + + Path to the root directory of the source code. + + This path is used as a default for input files and a base for relative + paths formed from absolute paths. + + This should typically be the root directory of the git project, as + relative paths formed from it can be used to create links to these + source files in the repository. + + Templates use the `base-url` option to create links to the source + code. + */ + std::string sourceRoot; + + /** Directory or file for generating output + + Multipage generators expect a directory. + + Single page generators expect a file or a directory where the file + will be created. + + If the directory does not exist, it will be created. + */ + std::string output; + + /** Path to the compilation database + + Path to the compilation database or a build script to generate it. + + The compilation database is a JSON file that contains the compiler + commands used to build the source code. + + The compilation database is used to extract the compiler flags and the + source files used to build the source code and extract symbols. + + This option also accepts the path to a build script such as + CMakeLists.txt to be used to generate the compilation database. + + In this case, Mr.Docs will look for CMake in `PATH` or in `CMAKE_ROOT` + and run the script to generate the compilation database file. + */ + std::string compilationDatabase; + + //-------------------------------------------- + // Filters + // + // Filters to include or exclude files and symbols from the documentation + //-------------------------------------------- + + /** Input directories to extract symbols from + + Input directories to extract. + + Only symbols defined in files in these directories are extracted. + + The paths are relative to the mrdocs configuration file. + */ + std::vector input; + + /** Recursively include files from "input" paths + + Recursively include files. + + When set to true, Mr.Docs includes files in subdirectories of the + input directories. + + When set to false, Mr.Docs includes only the files in the input + directories. + */ + bool recursive = true; + + /** File patterns to include + + File patterns to include. + + Only the files that match these patterns are extracted. + + The patterns are relative to the input directories. + */ + std::vector filePatterns; + + /** Input directories to exclude + + Symbols defined in files in these directories are not extracted even + if they are in the list of include directories. + + When relative, the paths are relative to the directory of the mrdocs + configuration file. + + For instance, "include/experimental" will exclude all files in the + directory `/include/experimental`. + */ + std::vector exclude; + + /** File patterns to exclude + + File patterns to exclude. + + Files that match these patterns are not extracted even if they are in + the list of include directories. + + The patterns are relative to the configuration file. + + A single * will match all files in the directory. + + Double ** will match all files in the directory and its + subdirectories. + */ + std::vector excludePatterns; + + /** Symbol patterns to include + + If any patterns are defined here, only symbols that match one of these + patterns are extracted. + + The patterns are applied to the fully qualified name of the symbol + without any leading "::". + + A single "*" will match all symbols in the namespace. + + Double "**" will match all symbols in the namespace and its + subnamespaces. + + The patterns also support "?" for any chars, "[]" for charsets, + "[^]" for inverted charsets, and "{,...}" for + alternatives. + */ + std::vector includeSymbols; + + /** Symbol patterns to exclude + + A symbol that matches one of these patterns is not extracted even if + whitelisted by "include-symbols". + + See the documentation for "include-symbols" for the pattern syntax. + */ + std::vector excludeSymbols; + + /** Exposition only symbols rendered as "see-below". + + Symbols that match one of these filters are tagged as "see-below" in + the documentation, and so do symbols in scopes tagged as "see-below". + + This option is used to remove details about symbols that are + considered part of the private API of the project but the user might + need to interact with. + + In the documentation page for this symbol, the symbol is exposition + only: the synopsis of the implementation is rendered as "see-below" + and members of scopes (such as a namespace or record) are not listed. + + The rest of the documentation is rendered as usual to explain the + symbol. + + See the documentation for "include-symbol" for the pattern syntax. + */ + std::vector seeBelow; + + /** Symbols rendered as "implementation-defined" + + Symbols that match one of these filters are tagged as + "implementation-defined" in the documentation, and so do symbols in + scopes tagged as "implementation-defined". + + This option is used to exclude symbols from the documentation that are + considered part of the private API of the project. + + An "implementation-defined" symbol has no documentation page in the + output. + + If any other symbol refers to it, the reference is rendered as + "implementation-defined". + + See the documentation for "include-symbol" for the pattern syntax. + */ + std::vector implementationDefined; + + //-------------------------------------------- + // Semantic Parsing + // + // Options to control how the source code is parsed + //-------------------------------------------- + + /** Include path prefixes allowed to be missing + + Specifies path prefixes for include files that, if missing, will not + cause documentation generation to fail. + + Missing files with these prefixes are served as empty files from an + in-memory file system, allowing processing to continue. + + For example, use "llvm/" to forgive all includes from LLVM. + + If any such path is specified, MrDocs will attempt to synthesize + missing included types. + + Only simple sets of non-conflicting inferred types can be synthesized. + + For more complex types or for better control, provide a shim using the + "missing-include-shims" option. + */ + std::vector missingIncludePrefixes; + + /** Shims for forgiven missing include files + + Specifies a map of include file paths to shim contents. + + If a missing include file matches a forgiven prefix, MrDocs will use + the shim content from this map as the file contents. + + If no shim is provided for a forgiven file, an empty file is used by + default. + */ + std::map missingIncludeShims; + + //-------------------------------------------- + // Comment Parsing + // + // Options to control how comments are parsed + //-------------------------------------------- + + /** Use the first line of the comment as the brief + + When set to `true`, Mr.Docs 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. + */ + bool autoBrief = true; + + /** Automatically find non-member functions + + When set to `true`, Mr.Docs automatically finds non-member functions + that are related to the current class. + */ + bool autoRelates = true; + + /** Automatically provide missing documentation for special functions + and trivial metadata + + When set to `true`, Mr.Docs automatically provides documentation for + special functions, such as constructors, destructors, and operators. + + It also provides documentation for missing documentation metadata, + such as known types. + */ + bool autoFunctionMetadata = true; + + //-------------------------------------------- + // Metadata Extraction + // + // Metadata and C++ semantic constructs to extract + //-------------------------------------------- + + /** Extract all symbols + + 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. + */ + bool extractAll = true; + + /** Extraction policy for private class members + + Determine whether private class members should be extracted + */ + bool extractPrivate = false; + + /** Extraction policy for private virtual methods of a class + + Determine whether private virtual methods of a class should be + extracted + */ + bool extractPrivateVirtual = false; + + /** Extraction policy for private base classes + + Determine whether private base classes should be extracted + */ + bool extractPrivateBases = false; + + /** Extraction policy for static members of a file + + Determine whether static members of a file should be extracted. + + This option does not refer to static members of a class. + */ + bool extractStatic = false; + + /** Extraction policy for records defined locally in source files + + Determine whether records only defined locally in source files should + be extracted. + */ + bool extractLocalClasses = true; + + /** Extraction policy for anonymous namespaces + + Determine whether symbols in anonymous namespaces should be extracted. + */ + bool extractAnonymousNamespaces = true; + + /** Extraction policy for empty namespaces + + Determine whether empty namespaces without documentation should be + extracted. + */ + bool extractEmptyNamespaces = false; + + /** Determine how derived classes inherit base members + + Determine how derived classes inherit members of base classes. + + When set to `never`, derived classes do not inherit members of base + classes and only the relationship is stored. + + When set to `reference`, derived classes list members of base classes + but references are still linked to the base class. + + When set to `copy-dependencies`, a reference is created by default and + a copy is created when the base class is a dependency. + + When set to `copy-all`, a copy is created for each base symbol as if + it was declared in the derived class. + + If the base class is a dependency, the extraction mode is copied from + the new parent. + */ + BaseMemberInheritance inheritBaseMembers = BaseMemberInheritance::CopyDependencies; + + /** Implicit template specializations used as base classes are + extracted as dependencies + + When set to `true`, MrDocs extracts implicit template specializations + used as base classes as dependencies. + + This allows MrDocs to extract metadata that can later be used to + determine the members of the derived class, as specified by the + `inherit-base-members` option. + */ + bool extractImplicitSpecializations = true; + + /** Extraction policy for friend functions and classes + + Determine whether friend functions and classes should be extracted. + + When set to `true`, MrDocs extracts friend functions and classes. + + When set to `false`, friend functions and classes are not extracted. + */ + bool extractFriends = true; + + /** Sort the members of a record + + When set to `true`, sort the members of a record by the criterion + determined in the `sort-members-by` option. + + When set to `false`, the members are included in the declaration order + they are extracted. + */ + bool sortMembers = true; + + /** Determine how members of a record are sorted + + If `sort-members` is set to `true`, determine how members of a record + are sorted. + + When set to `name`, members are sorted by name. + + When set to `location`, members are sorted by their primary location + in the source code, considering the short name of the path and the + location in the file. + */ + SortSymbolBy sortMembersBy = SortSymbolBy::Name; + + /** Determine how members of a namespace are sorted + + Although members of namespaces are always sorted, determine how + members of a namespace are sorted. + + When set to `name`, members are sorted by name. + + When set to `location`, members are sorted by their primary location + in the source code, considering the short name of the path and the + location in the file. + */ + SortSymbolBy sortNamespaceMembersBy = SortSymbolBy::Name; + + /** Sort constructors first + + When set to `true`, constructors are sorted first in the list of + members of a record. + */ + bool sortMembersCtors1St = true; + + /** Sort destructors first + + When set to `true`, destructors are sorted first in the list of + members of a record. + */ + bool sortMembersDtors1St = true; + + /** Sort assignment operators first + + When set to `true`, assignment operators are sorted first in the list + of members of a record. + */ + bool sortMembersAssignment1St = true; + + /** Sort conversion operators last + + When set to `true`, conversion operators are sorted last in the list + of members of a record or namespace. + */ + bool sortMembersConversionLast = true; + + /** Sort relational operators last + + When set to `true`, relational operators are sorted last in the list + of members of a record or namespace. + */ + bool sortMembersRelationalLast = true; + + //-------------------------------------------- + // Semantic Constructs + // + // C++ semantic constructs to extract + //-------------------------------------------- + + /** Detect and reduce SFINAE expressions + + 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. + */ + bool sfinae = true; + + /** Detect and group function overloads + + When set to `true`, MrDocs detects function overloads and groups them + as a single symbol type. + + The documentation for this new symbol comes from the union of + non-ambiguous metadata from the functions. + */ + bool overloads = true; + + //-------------------------------------------- + // Generators + // + // Generators to create the documentation and their options + //-------------------------------------------- + + /** Generator used to create the documentation + + The generator is responsible for creating the documentation from the + extracted symbols. + + The generator uses the extracted symbols and the templates to create + the documentation. + + The generator can create different types of documentation such as + HTML, XML, and AsciiDoc. + */ + Generator generator = Generator::Adoc; + + /** Generate a multipage documentation + + Generates a multipage documentation. + + The output directory must be a directory. + + This option acts as a hint to the generator to create a multipage + documentation. + + Whether the hint is followed or not depends on the generator. + */ + bool multipage = true; + + /** Base URL for links to source code + + Base URL for links to source code. + + The base URL is used to create links to the source code in the + documentation. + + The base URL is combined with the path to the source file to create + the link. + */ + std::string baseUrl; + + /** Path to the Addons directory + + Path to the Addons directory. + + The Addons directory contains the template files used by generators to + create the documentation. + + When a custom Addons directory is not specified, the default templates + are used. + + The default templates are located at the `share/mrdocs/addons` + directory of the MrDocs installation. + + Users can create custom templates by copying the default templates to + a custom directory and specifying the custom directory using this + option. + */ + std::string addons; + + /** Path for the tagfile + + Specifies the full path (filename) where the generated tagfile should + be saved. + + If left empty, no tagfile will be generated. + */ + std::string tagfile; + + /** Use legible names + + Use legible names for ids in the documentation. + + When set to true, MrDocs uses legible names for symbols in the + documentation. + + These are symbols that are legible but still safe for URLs. + + When the option is set to false, MrDocs uses a hash of the symbol ID. + */ + bool legibleNames = true; + + /** Output an embeddable document + + Output an embeddable document, which excludes the header, the footer, + and everything outside the body of the document. + + This option is useful for producing documents that can be inserted + into an external template. + */ + bool embedded = false; + + /** Show namespace pages in the documentation + + When set to true, MrDocs creates a page for each namespace in the + documentation. + */ + bool showNamespaces = true; + + /** Use the global namespace page as an index for all symbols + + When set to true, the page for the global namespace will recursively + list all symbols in the documentation, not just those in the global + namespace. + + This makes the global namespace page act as an index for the entire + project. + */ + bool globalNamespaceIndex = true; + + //-------------------------------------------- + // Build options + // + // Options for building the source code + //-------------------------------------------- + + /** CMake arguments when generating the compilation database from + CMakeLists.txt + + When the compilation-database option is a CMakeLists.txt file, these + arguments are passed to the cmake command to generate the + compilation_database.json. + */ + std::string cmake; + + /** Additional defines passed to the compiler + + Additional defines passed to the compiler when building the source + code. + + These defines are added to the compilation database regardless of the + strategy to generate it. + */ + std::vector defines; + + /** Use the system C++ standard library + + To achieve reproducible results, MrDocs bundles the LibC++ headers. + + To use the C++ standard library available in the system instead, set + this option to true. + */ + bool useSystemStdlib = false; + + /** C++ Standard Library include paths + + When `use-system-stdlib` is disabled, the C++ standard library headers + are available in these paths. + */ + std::vector stdlibIncludes; + + /** Use the system C standard library + + To achieve reproducible results, MrDocs bundles the LibC headers with + its definitions. + + To use the C standard library available in the system instead, set + this option to true. + */ + bool useSystemLibc = false; + + /** Standard Library include paths + + When `use-system-libc` is disabled, the C standard library headers are + available in these paths. + */ + std::vector libcIncludes; + + /** System include paths + + System include paths. + + These paths are used to add directories to the system include search + path. + + The system include search path is used to search for system headers. + + The system headers are headers that are provided by the system and are + not part of the project. + + The system headers are used to provide the standard library headers + and other system headers. + + The system headers are not part of the project and are not checked for + warnings and errors. + */ + std::vector systemIncludes; + + /** Include paths + + Include paths. + + These paths are used to add directories to the include search path. + + The include search path is used to search for headers. + + The headers are used to provide declarations and definitions of + symbols. + + The headers are part of the project and are checked for warnings and + errors. + */ + std::vector includes; + + //-------------------------------------------- + // Warnings + // + // Warnings and progress messages + //-------------------------------------------- + + /** Verbose output + + Verbose output. + + When set to true, MrDocs outputs additional information during the + generation of the documentation. + */ + bool verbose = false; + + /** The minimum reporting level + + The reporting level determines the amount of information displayed + during the generation of the documentation. + + The value `-1` delegates the decision to the `log-level` option. + */ + unsigned report = -1; + + /** The minimum reporting level + + The reporting level determines the amount of information displayed + during the generation of the documentation. + */ + LogLevel logLevel = LogLevel::Info; + + /** Enable warning messages + + 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. + */ + bool warnings = true; + + /** Warn if symbols are not documented + + When set to `true`, MrDocs outputs a warning message if a symbol that + passes all filters is not documented. + */ + bool warnIfUndocumented = true; + + /** Warn if documentation has errors + + 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. + */ + bool warnIfDocError = true; + + /** Warn if parameters are not documented + + When set to `true`, MrDocs outputs a warning message if a named + function parameter is not documented. + */ + bool warnNoParamdoc = true; + + /** Warn if documented functions have unnamed parameters + + When set to `true`, MrDocs outputs a warning message if a documented + function has a parameter that is not named. + */ + bool warnUnnamedParam = false; + + /** Warn if enum values are not documented + + When set to `true`, MrDocs outputs a warning message if an enum value + is not documented. + */ + bool warnIfUndocEnumVal = true; + + /** Warn if a documentation reference is broken + + When set to `true`, MrDocs outputs a warning message if a reference in + the documentation is broken. + */ + bool warnBrokenRef = true; + + /** Treat warnings as errors + + When set to `true`, MrDocs treats warnings as errors and stops the + generation of the documentation. + */ + bool warnAsError = false; + + //-------------------------------------------- + // Miscellaneous + // + // Miscellaneous options + //-------------------------------------------- + + /** Number of threads to use + + The desired level of concurrency: 0 for hardware-suggested. + */ + unsigned concurrency = 0; + + /** Continue if files are not mapped correctly + + When set to true, MrDocs continues to generate the documentation even + if some files are not mapped correctly. + + Files are not mapped correctly when the source file is not found or + the compilation database does not contain the compiler flags for the + source file. + */ + bool ignoreMapErrors = false; + + /** Whether AST visitation failures should not stop the program + + When set to true, MrDocs continues to generate the documentation even + if there are AST visitation failures. + + AST visitation failures occur when the source code contains constructs + that are not supported by MrDocs. + */ + bool ignoreFailures = false; + + /** Load the configuration from a YAML string + + This function loads the values from the YAML string without + normalizing or validating them. + + After calling this function, call `normalize` to normalize + and validate the options. + + @param s The PublicSettings object to load the configuration into + @param configYaml The YAML string with the configuration + @return Expected with the error if any + */ + static + Expected + load( + PublicSettings &s, + std::string_view configYaml); + + /** Option Type + */ + enum class OptionType { + Enum, + Path, + ListSymbolGlob, + ListPath, + String, + Unsigned, + FilePath, + ListPathGlob, + DirPath, + ListString, + Bool, + MapStringString, + }; + + /** Option validation traits + */ + struct OptionProperties { + OptionType type = OptionType::String; + bool required = false; + bool commandLineSink = false; + bool commandLineOnly = false; + bool mustExist = true; + bool shouldExist = false; + std::optional minValue = std::nullopt; + std::optional maxValue = std::nullopt; + std::optional> filenameMapping = std::nullopt; + std::variant< + std::monostate, + LogLevel, + BaseMemberInheritance, + std::vector, + unsigned, + Generator, + std::map, + SortSymbolBy, + std::vector, + bool, + std::vector, + std::string> defaultValue = std::monostate(); + std::string relativeTo = {}; + std::optional deprecated = std::nullopt; + }; + + /** Normalize the configuration values with a visitor + + This function normalizes and validates the configuration values. + + @param dirs The reference directories to resolve paths + @param f The visitor + @return Expected with the error if any + */ + template + Expected + normalize( + ReferenceDirectories const& dirs, + F&& f) + { + // Configuration or compilation database files + MRDOCS_TRY(std::forward(f)(*this, "cmd-line-inputs", cmdLineInputs, dirs, OptionProperties{ + .type = OptionType::ListPath, + .required = false, + .commandLineSink = true, + .commandLineOnly = true, + .mustExist = true, + .shouldExist = true, + .filenameMapping = std::map{ + { "mrdocs.yml", "config" }, + { "compile_commands.json", "compilation-database" }, + { "CMakeLists.txt", "compilation-database" }, + }, + .relativeTo = "", + })); + // Mr.Docs configuration file + MRDOCS_TRY(std::forward(f)(*this, "config", config, dirs, OptionProperties{ + .type = OptionType::FilePath, + .required = true, + .commandLineOnly = true, + .mustExist = true, + .shouldExist = false, + .defaultValue = std::string("/mrdocs.yml"), + .relativeTo = "", + })); + // Path to the root directory of the source code + MRDOCS_TRY(std::forward(f)(*this, "source-root", sourceRoot, dirs, OptionProperties{ + .type = OptionType::DirPath, + .required = false, + .mustExist = true, + .shouldExist = false, + .defaultValue = std::string(""), + .relativeTo = "", + })); + // Directory or file for generating output + MRDOCS_TRY(std::forward(f)(*this, "output", output, dirs, OptionProperties{ + .type = OptionType::Path, + .required = false, + .mustExist = false, + .shouldExist = false, + .defaultValue = std::string("/reference-output"), + .relativeTo = "", + })); + // Path to the compilation database + MRDOCS_TRY(std::forward(f)(*this, "compilation-database", compilationDatabase, dirs, OptionProperties{ + .type = OptionType::FilePath, + .required = false, + .mustExist = true, + .shouldExist = false, + .relativeTo = "", + })); + // Input directories to extract symbols from + MRDOCS_TRY(std::forward(f)(*this, "input", input, dirs, OptionProperties{ + .type = OptionType::ListPath, + .required = false, + .mustExist = false, + .shouldExist = true, + .defaultValue = std::vector({"/."}), + .relativeTo = "", + })); + // Recursively include files from "input" paths + MRDOCS_TRY(std::forward(f)(*this, "recursive", recursive, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(true), + })); + // File patterns to include + MRDOCS_TRY(std::forward(f)(*this, "file-patterns", filePatterns, dirs, OptionProperties{ + .type = OptionType::ListPathGlob, + .required = false, + .defaultValue = std::vector({PathGlobPattern::create("*.hpp").value(), PathGlobPattern::create("*.h").value(), PathGlobPattern::create("*.hh").value(), PathGlobPattern::create("*.ipp").value(), PathGlobPattern::create("*.inc").value(), PathGlobPattern::create("*.cpp").value(), PathGlobPattern::create("*.cc").value(), PathGlobPattern::create("*.cxx").value(), PathGlobPattern::create("*.c").value(), PathGlobPattern::create("*.hxx").value()}), + })); + // Input directories to exclude + MRDOCS_TRY(std::forward(f)(*this, "exclude", exclude, dirs, OptionProperties{ + .type = OptionType::ListPath, + .required = false, + .mustExist = false, + .shouldExist = true, + .relativeTo = "", + })); + // File patterns to exclude + MRDOCS_TRY(std::forward(f)(*this, "exclude-patterns", excludePatterns, dirs, OptionProperties{ + .type = OptionType::ListPathGlob, + .required = false, + .relativeTo = "", + })); + // Symbol patterns to include + MRDOCS_TRY(std::forward(f)(*this, "include-symbols", includeSymbols, dirs, OptionProperties{ + .type = OptionType::ListSymbolGlob, + .required = false, + .relativeTo = "", + })); + // Symbol patterns to exclude + MRDOCS_TRY(std::forward(f)(*this, "exclude-symbols", excludeSymbols, dirs, OptionProperties{ + .type = OptionType::ListSymbolGlob, + .required = false, + })); + // Exposition only symbols rendered as "see-below". + MRDOCS_TRY(std::forward(f)(*this, "see-below", seeBelow, dirs, OptionProperties{ + .type = OptionType::ListSymbolGlob, + .required = false, + })); + // Symbols rendered as "implementation-defined" + MRDOCS_TRY(std::forward(f)(*this, "implementation-defined", implementationDefined, dirs, OptionProperties{ + .type = OptionType::ListSymbolGlob, + .required = false, + })); + // Include path prefixes allowed to be missing + MRDOCS_TRY(std::forward(f)(*this, "missing-include-prefixes", missingIncludePrefixes, dirs, OptionProperties{ + .type = OptionType::ListString, + .required = false, + })); + // Shims for forgiven missing include files + MRDOCS_TRY(std::forward(f)(*this, "missing-include-shims", missingIncludeShims, dirs, OptionProperties{ + .type = OptionType::MapStringString, + .required = false, + })); + // Use the first line of the comment as the brief + MRDOCS_TRY(std::forward(f)(*this, "auto-brief", autoBrief, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(true), + })); + // Automatically find non-member functions + MRDOCS_TRY(std::forward(f)(*this, "auto-relates", autoRelates, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(true), + })); + // Automatically provide missing documentation for special functions and trivial metadata + MRDOCS_TRY(std::forward(f)(*this, "auto-function-metadata", autoFunctionMetadata, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(true), + })); + // Extract all symbols + MRDOCS_TRY(std::forward(f)(*this, "extract-all", extractAll, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(true), + })); + // Extraction policy for private class members + MRDOCS_TRY(std::forward(f)(*this, "extract-private", extractPrivate, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(false), + })); + // Extraction policy for private virtual methods of a class + MRDOCS_TRY(std::forward(f)(*this, "extract-private-virtual", extractPrivateVirtual, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(false), + })); + // Extraction policy for private base classes + MRDOCS_TRY(std::forward(f)(*this, "extract-private-bases", extractPrivateBases, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(false), + })); + // Extraction policy for static members of a file + MRDOCS_TRY(std::forward(f)(*this, "extract-static", extractStatic, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(false), + })); + // Extraction policy for records defined locally in source files + MRDOCS_TRY(std::forward(f)(*this, "extract-local-classes", extractLocalClasses, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(true), + })); + // Extraction policy for anonymous namespaces + MRDOCS_TRY(std::forward(f)(*this, "extract-anonymous-namespaces", extractAnonymousNamespaces, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(true), + })); + // Extraction policy for empty namespaces + MRDOCS_TRY(std::forward(f)(*this, "extract-empty-namespaces", extractEmptyNamespaces, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(false), + })); + // Determine how derived classes inherit base members + MRDOCS_TRY(std::forward(f)(*this, "inherit-base-members", inheritBaseMembers, dirs, OptionProperties{ + .type = OptionType::Enum, + .required = false, + .defaultValue = BaseMemberInheritance(BaseMemberInheritance::CopyDependencies), + })); + // Implicit template specializations used as base classes are extracted as dependencies + MRDOCS_TRY(std::forward(f)(*this, "extract-implicit-specializations", extractImplicitSpecializations, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(true), + })); + // Extraction policy for friend functions and classes + MRDOCS_TRY(std::forward(f)(*this, "extract-friends", extractFriends, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(true), + })); + // Sort the members of a record + MRDOCS_TRY(std::forward(f)(*this, "sort-members", sortMembers, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(true), + })); + // Determine how members of a record are sorted + MRDOCS_TRY(std::forward(f)(*this, "sort-members-by", sortMembersBy, dirs, OptionProperties{ + .type = OptionType::Enum, + .required = false, + .defaultValue = SortSymbolBy(SortSymbolBy::Name), + })); + // Determine how members of a namespace are sorted + MRDOCS_TRY(std::forward(f)(*this, "sort-namespace-members-by", sortNamespaceMembersBy, dirs, OptionProperties{ + .type = OptionType::Enum, + .required = false, + .defaultValue = SortSymbolBy(SortSymbolBy::Name), + })); + // Sort constructors first + MRDOCS_TRY(std::forward(f)(*this, "sort-members-ctors-1st", sortMembersCtors1St, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(true), + })); + // Sort destructors first + MRDOCS_TRY(std::forward(f)(*this, "sort-members-dtors-1st", sortMembersDtors1St, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(true), + })); + // Sort assignment operators first + MRDOCS_TRY(std::forward(f)(*this, "sort-members-assignment-1st", sortMembersAssignment1St, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(true), + })); + // Sort conversion operators last + MRDOCS_TRY(std::forward(f)(*this, "sort-members-conversion-last", sortMembersConversionLast, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(true), + })); + // Sort relational operators last + MRDOCS_TRY(std::forward(f)(*this, "sort-members-relational-last", sortMembersRelationalLast, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(true), + })); + // Detect and reduce SFINAE expressions + MRDOCS_TRY(std::forward(f)(*this, "sfinae", sfinae, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(true), + })); + // Detect and group function overloads + MRDOCS_TRY(std::forward(f)(*this, "overloads", overloads, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(true), + })); + // Generator used to create the documentation + MRDOCS_TRY(std::forward(f)(*this, "generator", generator, dirs, OptionProperties{ + .type = OptionType::Enum, + .required = false, + .defaultValue = Generator(Generator::Adoc), + })); + // Generate a multipage documentation + MRDOCS_TRY(std::forward(f)(*this, "multipage", multipage, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(true), + })); + // Base URL for links to source code + MRDOCS_TRY(std::forward(f)(*this, "base-url", baseUrl, dirs, OptionProperties{ + .type = OptionType::String, + .required = false, + })); + // Path to the Addons directory + MRDOCS_TRY(std::forward(f)(*this, "addons", addons, dirs, OptionProperties{ + .type = OptionType::Path, + .required = false, + .mustExist = true, + .shouldExist = false, + .defaultValue = std::string("/share/mrdocs/addons"), + .relativeTo = "", + })); + // Path for the tagfile + MRDOCS_TRY(std::forward(f)(*this, "tagfile", tagfile, dirs, OptionProperties{ + .type = OptionType::FilePath, + .required = false, + .mustExist = false, + .shouldExist = false, + .defaultValue = std::string("/reference.tag.xml"), + .relativeTo = "", + })); + // Use legible names + MRDOCS_TRY(std::forward(f)(*this, "legible-names", legibleNames, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(true), + })); + // Output an embeddable document + MRDOCS_TRY(std::forward(f)(*this, "embedded", embedded, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(false), + })); + // Show namespace pages in the documentation + MRDOCS_TRY(std::forward(f)(*this, "show-namespaces", showNamespaces, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(true), + })); + // Use the global namespace page as an index for all symbols + MRDOCS_TRY(std::forward(f)(*this, "global-namespace-index", globalNamespaceIndex, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(true), + })); + // CMake arguments when generating the compilation database from CMakeLists.txt + MRDOCS_TRY(std::forward(f)(*this, "cmake", cmake, dirs, OptionProperties{ + .type = OptionType::String, + .required = false, + })); + // Additional defines passed to the compiler + MRDOCS_TRY(std::forward(f)(*this, "defines", defines, dirs, OptionProperties{ + .type = OptionType::ListString, + .required = false, + })); + // Use the system C++ standard library + MRDOCS_TRY(std::forward(f)(*this, "use-system-stdlib", useSystemStdlib, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(false), + })); + // C++ Standard Library include paths + MRDOCS_TRY(std::forward(f)(*this, "stdlib-includes", stdlibIncludes, dirs, OptionProperties{ + .type = OptionType::ListPath, + .required = false, + .mustExist = false, + .shouldExist = true, + .defaultValue = std::vector({"/share/mrdocs/headers/libcxx", "/share/mrdocs/headers/clang"}), + .relativeTo = "", + })); + // Use the system C standard library + MRDOCS_TRY(std::forward(f)(*this, "use-system-libc", useSystemLibc, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(false), + })); + // Standard Library include paths + MRDOCS_TRY(std::forward(f)(*this, "libc-includes", libcIncludes, dirs, OptionProperties{ + .type = OptionType::ListPath, + .required = false, + .mustExist = false, + .shouldExist = true, + .defaultValue = std::vector({"/share/mrdocs/headers/libc-stubs"}), + .relativeTo = "", + })); + // System include paths + MRDOCS_TRY(std::forward(f)(*this, "system-includes", systemIncludes, dirs, OptionProperties{ + .type = OptionType::ListPath, + .required = false, + .mustExist = false, + .shouldExist = true, + .relativeTo = "", + })); + // Include paths + MRDOCS_TRY(std::forward(f)(*this, "includes", includes, dirs, OptionProperties{ + .type = OptionType::ListPath, + .required = false, + .mustExist = false, + .shouldExist = true, + .relativeTo = "", + })); + // Verbose output + MRDOCS_TRY(std::forward(f)(*this, "verbose", verbose, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(false), + })); + // The minimum reporting level + MRDOCS_TRY(std::forward(f)(*this, "report", report, dirs, OptionProperties{ + .type = OptionType::Unsigned, + .required = false, + .defaultValue = static_cast(-1), + .deprecated = "Use `log-level` instead", + })); + // The minimum reporting level + MRDOCS_TRY(std::forward(f)(*this, "log-level", logLevel, dirs, OptionProperties{ + .type = OptionType::Enum, + .required = false, + .defaultValue = LogLevel(LogLevel::Info), + })); + // Enable warning messages + MRDOCS_TRY(std::forward(f)(*this, "warnings", warnings, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(true), + })); + // Warn if symbols are not documented + MRDOCS_TRY(std::forward(f)(*this, "warn-if-undocumented", warnIfUndocumented, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(true), + })); + // Warn if documentation has errors + MRDOCS_TRY(std::forward(f)(*this, "warn-if-doc-error", warnIfDocError, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(true), + })); + // Warn if parameters are not documented + MRDOCS_TRY(std::forward(f)(*this, "warn-no-paramdoc", warnNoParamdoc, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(true), + })); + // Warn if documented functions have unnamed parameters + MRDOCS_TRY(std::forward(f)(*this, "warn-unnamed-param", warnUnnamedParam, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(false), + })); + // Warn if enum values are not documented + MRDOCS_TRY(std::forward(f)(*this, "warn-if-undoc-enum-val", warnIfUndocEnumVal, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(true), + })); + // Warn if a documentation reference is broken + MRDOCS_TRY(std::forward(f)(*this, "warn-broken-ref", warnBrokenRef, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(true), + })); + // Treat warnings as errors + MRDOCS_TRY(std::forward(f)(*this, "warn-as-error", warnAsError, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(false), + })); + // Number of threads to use + MRDOCS_TRY(std::forward(f)(*this, "concurrency", concurrency, dirs, OptionProperties{ + .type = OptionType::Unsigned, + .required = false, + .commandLineOnly = true, + .defaultValue = static_cast(0), + })); + // Continue if files are not mapped correctly + MRDOCS_TRY(std::forward(f)(*this, "ignore-map-errors", ignoreMapErrors, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(false), + })); + // Whether AST visitation failures should not stop the program + MRDOCS_TRY(std::forward(f)(*this, "ignore-failures", ignoreFailures, dirs, OptionProperties{ + .type = OptionType::Bool, + .required = false, + .defaultValue = static_cast(false), + })); + return {}; + } + + /** Visit all options + + @param f The visitor + */ + template + void + visit(F&& f) + { + std::forward(f)("cmd-line-inputs", cmdLineInputs); + std::forward(f)("config", config); + std::forward(f)("source-root", sourceRoot); + std::forward(f)("output", output); + std::forward(f)("compilation-database", compilationDatabase); + std::forward(f)("input", input); + std::forward(f)("recursive", recursive); + std::forward(f)("file-patterns", filePatterns); + std::forward(f)("exclude", exclude); + std::forward(f)("exclude-patterns", excludePatterns); + std::forward(f)("include-symbols", includeSymbols); + std::forward(f)("exclude-symbols", excludeSymbols); + std::forward(f)("see-below", seeBelow); + std::forward(f)("implementation-defined", implementationDefined); + std::forward(f)("missing-include-prefixes", missingIncludePrefixes); + std::forward(f)("missing-include-shims", missingIncludeShims); + std::forward(f)("auto-brief", autoBrief); + std::forward(f)("auto-relates", autoRelates); + std::forward(f)("auto-function-metadata", autoFunctionMetadata); + std::forward(f)("extract-all", extractAll); + std::forward(f)("extract-private", extractPrivate); + std::forward(f)("extract-private-virtual", extractPrivateVirtual); + std::forward(f)("extract-private-bases", extractPrivateBases); + std::forward(f)("extract-static", extractStatic); + std::forward(f)("extract-local-classes", extractLocalClasses); + std::forward(f)("extract-anonymous-namespaces", extractAnonymousNamespaces); + std::forward(f)("extract-empty-namespaces", extractEmptyNamespaces); + std::forward(f)("inherit-base-members", inheritBaseMembers); + std::forward(f)("extract-implicit-specializations", extractImplicitSpecializations); + std::forward(f)("extract-friends", extractFriends); + std::forward(f)("sort-members", sortMembers); + std::forward(f)("sort-members-by", sortMembersBy); + std::forward(f)("sort-namespace-members-by", sortNamespaceMembersBy); + std::forward(f)("sort-members-ctors-1st", sortMembersCtors1St); + std::forward(f)("sort-members-dtors-1st", sortMembersDtors1St); + std::forward(f)("sort-members-assignment-1st", sortMembersAssignment1St); + std::forward(f)("sort-members-conversion-last", sortMembersConversionLast); + std::forward(f)("sort-members-relational-last", sortMembersRelationalLast); + std::forward(f)("sfinae", sfinae); + std::forward(f)("overloads", overloads); + std::forward(f)("generator", generator); + std::forward(f)("multipage", multipage); + std::forward(f)("base-url", baseUrl); + std::forward(f)("addons", addons); + std::forward(f)("tagfile", tagfile); + std::forward(f)("legible-names", legibleNames); + std::forward(f)("embedded", embedded); + std::forward(f)("show-namespaces", showNamespaces); + std::forward(f)("global-namespace-index", globalNamespaceIndex); + std::forward(f)("cmake", cmake); + std::forward(f)("defines", defines); + std::forward(f)("use-system-stdlib", useSystemStdlib); + std::forward(f)("stdlib-includes", stdlibIncludes); + std::forward(f)("use-system-libc", useSystemLibc); + std::forward(f)("libc-includes", libcIncludes); + std::forward(f)("system-includes", systemIncludes); + std::forward(f)("includes", includes); + std::forward(f)("verbose", verbose); + std::forward(f)("report", report); + std::forward(f)("log-level", logLevel); + std::forward(f)("warnings", warnings); + std::forward(f)("warn-if-undocumented", warnIfUndocumented); + std::forward(f)("warn-if-doc-error", warnIfDocError); + std::forward(f)("warn-no-paramdoc", warnNoParamdoc); + std::forward(f)("warn-unnamed-param", warnUnnamedParam); + std::forward(f)("warn-if-undoc-enum-val", warnIfUndocEnumVal); + std::forward(f)("warn-broken-ref", warnBrokenRef); + std::forward(f)("warn-as-error", warnAsError); + std::forward(f)("concurrency", concurrency); + std::forward(f)("ignore-map-errors", ignoreMapErrors); + std::forward(f)("ignore-failures", ignoreFailures); + } + + /** Visit all options + + @param f The visitor + */ + template + void + visit(F&& f) const + { + std::forward(f)("cmd-line-inputs", cmdLineInputs); + std::forward(f)("config", config); + std::forward(f)("source-root", sourceRoot); + std::forward(f)("output", output); + std::forward(f)("compilation-database", compilationDatabase); + std::forward(f)("input", input); + std::forward(f)("recursive", recursive); + std::forward(f)("file-patterns", filePatterns); + std::forward(f)("exclude", exclude); + std::forward(f)("exclude-patterns", excludePatterns); + std::forward(f)("include-symbols", includeSymbols); + std::forward(f)("exclude-symbols", excludeSymbols); + std::forward(f)("see-below", seeBelow); + std::forward(f)("implementation-defined", implementationDefined); + std::forward(f)("missing-include-prefixes", missingIncludePrefixes); + std::forward(f)("missing-include-shims", missingIncludeShims); + std::forward(f)("auto-brief", autoBrief); + std::forward(f)("auto-relates", autoRelates); + std::forward(f)("auto-function-metadata", autoFunctionMetadata); + std::forward(f)("extract-all", extractAll); + std::forward(f)("extract-private", extractPrivate); + std::forward(f)("extract-private-virtual", extractPrivateVirtual); + std::forward(f)("extract-private-bases", extractPrivateBases); + std::forward(f)("extract-static", extractStatic); + std::forward(f)("extract-local-classes", extractLocalClasses); + std::forward(f)("extract-anonymous-namespaces", extractAnonymousNamespaces); + std::forward(f)("extract-empty-namespaces", extractEmptyNamespaces); + std::forward(f)("inherit-base-members", inheritBaseMembers); + std::forward(f)("extract-implicit-specializations", extractImplicitSpecializations); + std::forward(f)("extract-friends", extractFriends); + std::forward(f)("sort-members", sortMembers); + std::forward(f)("sort-members-by", sortMembersBy); + std::forward(f)("sort-namespace-members-by", sortNamespaceMembersBy); + std::forward(f)("sort-members-ctors-1st", sortMembersCtors1St); + std::forward(f)("sort-members-dtors-1st", sortMembersDtors1St); + std::forward(f)("sort-members-assignment-1st", sortMembersAssignment1St); + std::forward(f)("sort-members-conversion-last", sortMembersConversionLast); + std::forward(f)("sort-members-relational-last", sortMembersRelationalLast); + std::forward(f)("sfinae", sfinae); + std::forward(f)("overloads", overloads); + std::forward(f)("generator", generator); + std::forward(f)("multipage", multipage); + std::forward(f)("base-url", baseUrl); + std::forward(f)("addons", addons); + std::forward(f)("tagfile", tagfile); + std::forward(f)("legible-names", legibleNames); + std::forward(f)("embedded", embedded); + std::forward(f)("show-namespaces", showNamespaces); + std::forward(f)("global-namespace-index", globalNamespaceIndex); + std::forward(f)("cmake", cmake); + std::forward(f)("defines", defines); + std::forward(f)("use-system-stdlib", useSystemStdlib); + std::forward(f)("stdlib-includes", stdlibIncludes); + std::forward(f)("use-system-libc", useSystemLibc); + std::forward(f)("libc-includes", libcIncludes); + std::forward(f)("system-includes", systemIncludes); + std::forward(f)("includes", includes); + std::forward(f)("verbose", verbose); + std::forward(f)("report", report); + std::forward(f)("log-level", logLevel); + std::forward(f)("warnings", warnings); + std::forward(f)("warn-if-undocumented", warnIfUndocumented); + std::forward(f)("warn-if-doc-error", warnIfDocError); + std::forward(f)("warn-no-paramdoc", warnNoParamdoc); + std::forward(f)("warn-unnamed-param", warnUnnamedParam); + std::forward(f)("warn-if-undoc-enum-val", warnIfUndocEnumVal); + std::forward(f)("warn-broken-ref", warnBrokenRef); + std::forward(f)("warn-as-error", warnAsError); + std::forward(f)("concurrency", concurrency); + std::forward(f)("ignore-map-errors", ignoreMapErrors); + std::forward(f)("ignore-failures", ignoreFailures); + } + +}; // struct PublicSettings + +/** Convert a PublicSettings::Generator" to a string + */ +constexpr +std::string_view +to_string(PublicSettings::Generator e) +{ + switch (e) + { + case PublicSettings::Generator::Adoc: + return "adoc"; + case PublicSettings::Generator::Html: + return "html"; + case PublicSettings::Generator::Xml: + return "xml"; + } + return {}; +} + +/** Convert a PublicSettings::LogLevel" to a string + */ +constexpr +std::string_view +to_string(PublicSettings::LogLevel e) +{ + switch (e) + { + case PublicSettings::LogLevel::Trace: + return "trace"; + case PublicSettings::LogLevel::Debug: + return "debug"; + case PublicSettings::LogLevel::Info: + return "info"; + case PublicSettings::LogLevel::Warn: + return "warn"; + case PublicSettings::LogLevel::Error: + return "error"; + case PublicSettings::LogLevel::Fatal: + return "fatal"; + } + return {}; +} + +/** Convert a PublicSettings::BaseMemberInheritance" to a string + */ +constexpr +std::string_view +to_string(PublicSettings::BaseMemberInheritance e) +{ + switch (e) + { + case PublicSettings::BaseMemberInheritance::Never: + return "never"; + case PublicSettings::BaseMemberInheritance::Reference: + return "reference"; + case PublicSettings::BaseMemberInheritance::CopyDependencies: + return "copy-dependencies"; + case PublicSettings::BaseMemberInheritance::CopyAll: + return "copy-all"; + } + return {}; +} + +/** Convert a PublicSettings::SortSymbolBy" to a string + */ +constexpr +std::string_view +to_string(PublicSettings::SortSymbolBy e) +{ + switch (e) + { + case PublicSettings::SortSymbolBy::Name: + return "name"; + case PublicSettings::SortSymbolBy::Location: + return "location"; + } + return {}; +} + +} // namespace clang::mrdocs + +#endif // MRDOCS_PUBLIC_SETTINGS_HPP diff --git a/docs/config-headers/src/tool/PublicToolArgs.hpp b/docs/config-headers/src/tool/PublicToolArgs.hpp new file mode 100644 index 000000000..73162f118 --- /dev/null +++ b/docs/config-headers/src/tool/PublicToolArgs.hpp @@ -0,0 +1,1024 @@ +/* + * This file is generated automatically from the json file + * `src/lib/Lib/ConfigOptions.json`. Do not edit this file + * manually. Instead, edit the json file and run the script + * `util/generate-config-info.py` to regenerate this file. + */ + +#ifndef MRDOCS_PUBLIC_TOOLARGS_HPP +#define MRDOCS_PUBLIC_TOOLARGS_HPP + +#include +#include +#include +#include + +namespace clang { +namespace mrdocs { + +struct PublicToolArgs { + + /// Construct the PublicToolArgs object + PublicToolArgs(); + + /// Override settings with unnormalized values from the command line + Expected + apply( + PublicSettings& s, + ReferenceDirectories const& dirs, + char const** argv); + + //-------------------------------------------- + // Option Categories + //-------------------------------------------- + /** Options that can only be provided via the command line + + The following options can be used to control the general behavior of Mr.Docs + and can only be provided via the command line. These include options to + specify inputs and the configuration file, which cannot be set on the + configuration file itself. + */ + llvm::cl::OptionCategory commandLineOptionsCat; + + /** Paths to the source code and output directories + */ + llvm::cl::OptionCategory pathsCat; + + /** Filters to include or exclude files and symbols from the documentation + */ + llvm::cl::OptionCategory filtersCat; + + /** Options to control how the source code is parsed + + Mr.Docs uses libclang to parse the source code and extract symbols. The + following options control how the source code is parsed. + */ + llvm::cl::OptionCategory semanticParsingCat; + + /** Options to control how comments are parsed + + Mr.Docs extracts metadata from the comments in the source code. The + following options control how comments are parsed. + */ + llvm::cl::OptionCategory commentParsingCat; + + /** Metadata and C++ semantic constructs to extract + + 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. + */ + llvm::cl::OptionCategory metadataExtractionCat; + + /** C++ semantic constructs to extract + + Semantic constructs are patterns not directly represented in the source code + AST but can be inferred from the corpus, such as SFINAE. + */ + llvm::cl::OptionCategory semanticConstructsCat; + + /** Generators to create the documentation and their options + */ + llvm::cl::OptionCategory generatorsCat; + + /** Options for building the source code + + When MrDocs is responsible to running the build scripts and generating the + compilation database, these options are used to build the source code. + */ + llvm::cl::OptionCategory buildOptionsCat; + + /** Warnings and progress messages + */ + llvm::cl::OptionCategory warningsCat; + + /** Miscellaneous options + */ + llvm::cl::OptionCategory miscellaneousCat; + + + //-------------------------------------------- + // Command Line Options + // + // Options that can only be provided via the command line + //-------------------------------------------- + + /** Configuration or compilation database files + + The inputs are configuration files or compilation database files that + used to generate the documentation. + + When the input ends with `mrdocs.yml`, it is interpreted as a + configuration file, the file is read and the options are used to + generate the documentation as if it was provided to the `config` + option. + + When the input ends with `compilation_database.json` or + `CMakeLists.txt`, it is interpreted as a compilation database file, + the file is read and the compiler flags are used to generate the + documentation as if it was provided to the `compilation-database` + option. + */ + llvm::cl::list cmdLineInputs; + + /** Mr.Docs configuration file + + The configuration file is a YAML file that contains the options used + to generate the documentation. + + The configuration file is read and the options are used to generate + the documentation. + + The configuration file can be used to specify the source code, the + output directory, the compilation database, the generator, and the + filters. + */ + llvm::cl::opt config; + + //-------------------------------------------- + // Paths + // + // Paths to the source code and output directories + //-------------------------------------------- + + /** Path to the root directory of the source code + + Path to the root directory of the source code. + + This path is used as a default for input files and a base for relative + paths formed from absolute paths. + + This should typically be the root directory of the git project, as + relative paths formed from it can be used to create links to these + source files in the repository. + + Templates use the `base-url` option to create links to the source + code. + */ + llvm::cl::opt sourceRoot; + + /** Directory or file for generating output + + Multipage generators expect a directory. + + Single page generators expect a file or a directory where the file + will be created. + + If the directory does not exist, it will be created. + */ + llvm::cl::opt output; + + /** Path to the compilation database + + Path to the compilation database or a build script to generate it. + + The compilation database is a JSON file that contains the compiler + commands used to build the source code. + + The compilation database is used to extract the compiler flags and the + source files used to build the source code and extract symbols. + + This option also accepts the path to a build script such as + CMakeLists.txt to be used to generate the compilation database. + + In this case, Mr.Docs will look for CMake in `PATH` or in `CMAKE_ROOT` + and run the script to generate the compilation database file. + */ + llvm::cl::opt compilationDatabase; + + //-------------------------------------------- + // Filters + // + // Filters to include or exclude files and symbols from the documentation + //-------------------------------------------- + + /** Input directories to extract symbols from + + Input directories to extract. + + Only symbols defined in files in these directories are extracted. + + The paths are relative to the mrdocs configuration file. + */ + llvm::cl::list input; + + /** Recursively include files from "input" paths + + Recursively include files. + + When set to true, Mr.Docs includes files in subdirectories of the + input directories. + + When set to false, Mr.Docs includes only the files in the input + directories. + */ + llvm::cl::opt recursive; + + /** File patterns to include + + File patterns to include. + + Only the files that match these patterns are extracted. + + The patterns are relative to the input directories. + */ + llvm::cl::list filePatterns; + + /** Input directories to exclude + + Symbols defined in files in these directories are not extracted even + if they are in the list of include directories. + + When relative, the paths are relative to the directory of the mrdocs + configuration file. + + For instance, "include/experimental" will exclude all files in the + directory `/include/experimental`. + */ + llvm::cl::list exclude; + + /** File patterns to exclude + + File patterns to exclude. + + Files that match these patterns are not extracted even if they are in + the list of include directories. + + The patterns are relative to the configuration file. + + A single * will match all files in the directory. + + Double ** will match all files in the directory and its + subdirectories. + */ + llvm::cl::list excludePatterns; + + /** Symbol patterns to include + + If any patterns are defined here, only symbols that match one of these + patterns are extracted. + + The patterns are applied to the fully qualified name of the symbol + without any leading "::". + + A single "*" will match all symbols in the namespace. + + Double "**" will match all symbols in the namespace and its + subnamespaces. + + The patterns also support "?" for any chars, "[]" for charsets, + "[^]" for inverted charsets, and "{,...}" for + alternatives. + */ + llvm::cl::list includeSymbols; + + /** Symbol patterns to exclude + + A symbol that matches one of these patterns is not extracted even if + whitelisted by "include-symbols". + + See the documentation for "include-symbols" for the pattern syntax. + */ + llvm::cl::list excludeSymbols; + + /** Exposition only symbols rendered as "see-below". + + Symbols that match one of these filters are tagged as "see-below" in + the documentation, and so do symbols in scopes tagged as "see-below". + + This option is used to remove details about symbols that are + considered part of the private API of the project but the user might + need to interact with. + + In the documentation page for this symbol, the symbol is exposition + only: the synopsis of the implementation is rendered as "see-below" + and members of scopes (such as a namespace or record) are not listed. + + The rest of the documentation is rendered as usual to explain the + symbol. + + See the documentation for "include-symbol" for the pattern syntax. + */ + llvm::cl::list seeBelow; + + /** Symbols rendered as "implementation-defined" + + Symbols that match one of these filters are tagged as + "implementation-defined" in the documentation, and so do symbols in + scopes tagged as "implementation-defined". + + This option is used to exclude symbols from the documentation that are + considered part of the private API of the project. + + An "implementation-defined" symbol has no documentation page in the + output. + + If any other symbol refers to it, the reference is rendered as + "implementation-defined". + + See the documentation for "include-symbol" for the pattern syntax. + */ + llvm::cl::list implementationDefined; + + //-------------------------------------------- + // Semantic Parsing + // + // Options to control how the source code is parsed + //-------------------------------------------- + + /** Include path prefixes allowed to be missing + + Specifies path prefixes for include files that, if missing, will not + cause documentation generation to fail. + + Missing files with these prefixes are served as empty files from an + in-memory file system, allowing processing to continue. + + For example, use "llvm/" to forgive all includes from LLVM. + + If any such path is specified, MrDocs will attempt to synthesize + missing included types. + + Only simple sets of non-conflicting inferred types can be synthesized. + + For more complex types or for better control, provide a shim using the + "missing-include-shims" option. + */ + llvm::cl::list missingIncludePrefixes; + + /** Shims for forgiven missing include files + + Specifies a map of include file paths to shim contents. + + If a missing include file matches a forgiven prefix, MrDocs will use + the shim content from this map as the file contents. + + If no shim is provided for a forgiven file, an empty file is used by + default. + */ + llvm::cl::list missingIncludeShims; + + //-------------------------------------------- + // Comment Parsing + // + // Options to control how comments are parsed + //-------------------------------------------- + + /** Use the first line of the comment as the brief + + When set to `true`, Mr.Docs 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. + */ + llvm::cl::opt autoBrief; + + /** Automatically find non-member functions + + When set to `true`, Mr.Docs automatically finds non-member functions + that are related to the current class. + */ + llvm::cl::opt autoRelates; + + /** Automatically provide missing documentation for special functions + and trivial metadata + + When set to `true`, Mr.Docs automatically provides documentation for + special functions, such as constructors, destructors, and operators. + + It also provides documentation for missing documentation metadata, + such as known types. + */ + llvm::cl::opt autoFunctionMetadata; + + //-------------------------------------------- + // Metadata Extraction + // + // Metadata and C++ semantic constructs to extract + //-------------------------------------------- + + /** Extract all symbols + + 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. + */ + llvm::cl::opt extractAll; + + /** Extraction policy for private class members + + Determine whether private class members should be extracted + */ + llvm::cl::opt extractPrivate; + + /** Extraction policy for private virtual methods of a class + + Determine whether private virtual methods of a class should be + extracted + */ + llvm::cl::opt extractPrivateVirtual; + + /** Extraction policy for private base classes + + Determine whether private base classes should be extracted + */ + llvm::cl::opt extractPrivateBases; + + /** Extraction policy for static members of a file + + Determine whether static members of a file should be extracted. + + This option does not refer to static members of a class. + */ + llvm::cl::opt extractStatic; + + /** Extraction policy for records defined locally in source files + + Determine whether records only defined locally in source files should + be extracted. + */ + llvm::cl::opt extractLocalClasses; + + /** Extraction policy for anonymous namespaces + + Determine whether symbols in anonymous namespaces should be extracted. + */ + llvm::cl::opt extractAnonymousNamespaces; + + /** Extraction policy for empty namespaces + + Determine whether empty namespaces without documentation should be + extracted. + */ + llvm::cl::opt extractEmptyNamespaces; + + /** Determine how derived classes inherit base members + + Determine how derived classes inherit members of base classes. + + When set to `never`, derived classes do not inherit members of base + classes and only the relationship is stored. + + When set to `reference`, derived classes list members of base classes + but references are still linked to the base class. + + When set to `copy-dependencies`, a reference is created by default and + a copy is created when the base class is a dependency. + + When set to `copy-all`, a copy is created for each base symbol as if + it was declared in the derived class. + + If the base class is a dependency, the extraction mode is copied from + the new parent. + */ + llvm::cl::opt inheritBaseMembers; + + /** Implicit template specializations used as base classes are + extracted as dependencies + + When set to `true`, MrDocs extracts implicit template specializations + used as base classes as dependencies. + + This allows MrDocs to extract metadata that can later be used to + determine the members of the derived class, as specified by the + `inherit-base-members` option. + */ + llvm::cl::opt extractImplicitSpecializations; + + /** Extraction policy for friend functions and classes + + Determine whether friend functions and classes should be extracted. + + When set to `true`, MrDocs extracts friend functions and classes. + + When set to `false`, friend functions and classes are not extracted. + */ + llvm::cl::opt extractFriends; + + /** Sort the members of a record + + When set to `true`, sort the members of a record by the criterion + determined in the `sort-members-by` option. + + When set to `false`, the members are included in the declaration order + they are extracted. + */ + llvm::cl::opt sortMembers; + + /** Determine how members of a record are sorted + + If `sort-members` is set to `true`, determine how members of a record + are sorted. + + When set to `name`, members are sorted by name. + + When set to `location`, members are sorted by their primary location + in the source code, considering the short name of the path and the + location in the file. + */ + llvm::cl::opt sortMembersBy; + + /** Determine how members of a namespace are sorted + + Although members of namespaces are always sorted, determine how + members of a namespace are sorted. + + When set to `name`, members are sorted by name. + + When set to `location`, members are sorted by their primary location + in the source code, considering the short name of the path and the + location in the file. + */ + llvm::cl::opt sortNamespaceMembersBy; + + /** Sort constructors first + + When set to `true`, constructors are sorted first in the list of + members of a record. + */ + llvm::cl::opt sortMembersCtors1St; + + /** Sort destructors first + + When set to `true`, destructors are sorted first in the list of + members of a record. + */ + llvm::cl::opt sortMembersDtors1St; + + /** Sort assignment operators first + + When set to `true`, assignment operators are sorted first in the list + of members of a record. + */ + llvm::cl::opt sortMembersAssignment1St; + + /** Sort conversion operators last + + When set to `true`, conversion operators are sorted last in the list + of members of a record or namespace. + */ + llvm::cl::opt sortMembersConversionLast; + + /** Sort relational operators last + + When set to `true`, relational operators are sorted last in the list + of members of a record or namespace. + */ + llvm::cl::opt sortMembersRelationalLast; + + //-------------------------------------------- + // Semantic Constructs + // + // C++ semantic constructs to extract + //-------------------------------------------- + + /** Detect and reduce SFINAE expressions + + 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. + */ + llvm::cl::opt sfinae; + + /** Detect and group function overloads + + When set to `true`, MrDocs detects function overloads and groups them + as a single symbol type. + + The documentation for this new symbol comes from the union of + non-ambiguous metadata from the functions. + */ + llvm::cl::opt overloads; + + //-------------------------------------------- + // Generators + // + // Generators to create the documentation and their options + //-------------------------------------------- + + /** Generator used to create the documentation + + The generator is responsible for creating the documentation from the + extracted symbols. + + The generator uses the extracted symbols and the templates to create + the documentation. + + The generator can create different types of documentation such as + HTML, XML, and AsciiDoc. + */ + llvm::cl::opt generator; + + /** Generate a multipage documentation + + Generates a multipage documentation. + + The output directory must be a directory. + + This option acts as a hint to the generator to create a multipage + documentation. + + Whether the hint is followed or not depends on the generator. + */ + llvm::cl::opt multipage; + + /** Base URL for links to source code + + Base URL for links to source code. + + The base URL is used to create links to the source code in the + documentation. + + The base URL is combined with the path to the source file to create + the link. + */ + llvm::cl::opt baseUrl; + + /** Path to the Addons directory + + Path to the Addons directory. + + The Addons directory contains the template files used by generators to + create the documentation. + + When a custom Addons directory is not specified, the default templates + are used. + + The default templates are located at the `share/mrdocs/addons` + directory of the MrDocs installation. + + Users can create custom templates by copying the default templates to + a custom directory and specifying the custom directory using this + option. + */ + llvm::cl::opt addons; + + /** Path for the tagfile + + Specifies the full path (filename) where the generated tagfile should + be saved. + + If left empty, no tagfile will be generated. + */ + llvm::cl::opt tagfile; + + /** Use legible names + + Use legible names for ids in the documentation. + + When set to true, MrDocs uses legible names for symbols in the + documentation. + + These are symbols that are legible but still safe for URLs. + + When the option is set to false, MrDocs uses a hash of the symbol ID. + */ + llvm::cl::opt legibleNames; + + /** Output an embeddable document + + Output an embeddable document, which excludes the header, the footer, + and everything outside the body of the document. + + This option is useful for producing documents that can be inserted + into an external template. + */ + llvm::cl::opt embedded; + + /** Show namespace pages in the documentation + + When set to true, MrDocs creates a page for each namespace in the + documentation. + */ + llvm::cl::opt showNamespaces; + + /** Use the global namespace page as an index for all symbols + + When set to true, the page for the global namespace will recursively + list all symbols in the documentation, not just those in the global + namespace. + + This makes the global namespace page act as an index for the entire + project. + */ + llvm::cl::opt globalNamespaceIndex; + + //-------------------------------------------- + // Build options + // + // Options for building the source code + //-------------------------------------------- + + /** CMake arguments when generating the compilation database from + CMakeLists.txt + + When the compilation-database option is a CMakeLists.txt file, these + arguments are passed to the cmake command to generate the + compilation_database.json. + */ + llvm::cl::opt cmake; + + /** Additional defines passed to the compiler + + Additional defines passed to the compiler when building the source + code. + + These defines are added to the compilation database regardless of the + strategy to generate it. + */ + llvm::cl::list defines; + + /** Use the system C++ standard library + + To achieve reproducible results, MrDocs bundles the LibC++ headers. + + To use the C++ standard library available in the system instead, set + this option to true. + */ + llvm::cl::opt useSystemStdlib; + + /** C++ Standard Library include paths + + When `use-system-stdlib` is disabled, the C++ standard library headers + are available in these paths. + */ + llvm::cl::list stdlibIncludes; + + /** Use the system C standard library + + To achieve reproducible results, MrDocs bundles the LibC headers with + its definitions. + + To use the C standard library available in the system instead, set + this option to true. + */ + llvm::cl::opt useSystemLibc; + + /** Standard Library include paths + + When `use-system-libc` is disabled, the C standard library headers are + available in these paths. + */ + llvm::cl::list libcIncludes; + + /** System include paths + + System include paths. + + These paths are used to add directories to the system include search + path. + + The system include search path is used to search for system headers. + + The system headers are headers that are provided by the system and are + not part of the project. + + The system headers are used to provide the standard library headers + and other system headers. + + The system headers are not part of the project and are not checked for + warnings and errors. + */ + llvm::cl::list systemIncludes; + + /** Include paths + + Include paths. + + These paths are used to add directories to the include search path. + + The include search path is used to search for headers. + + The headers are used to provide declarations and definitions of + symbols. + + The headers are part of the project and are checked for warnings and + errors. + */ + llvm::cl::list includes; + + //-------------------------------------------- + // Warnings + // + // Warnings and progress messages + //-------------------------------------------- + + /** Verbose output + + Verbose output. + + When set to true, MrDocs outputs additional information during the + generation of the documentation. + */ + llvm::cl::opt verbose; + + /** The minimum reporting level + + The reporting level determines the amount of information displayed + during the generation of the documentation. + + The value `-1` delegates the decision to the `log-level` option. + */ + llvm::cl::opt report; + + /** The minimum reporting level + + The reporting level determines the amount of information displayed + during the generation of the documentation. + */ + llvm::cl::opt logLevel; + + /** Enable warning messages + + 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. + */ + llvm::cl::opt warnings; + + /** Warn if symbols are not documented + + When set to `true`, MrDocs outputs a warning message if a symbol that + passes all filters is not documented. + */ + llvm::cl::opt warnIfUndocumented; + + /** Warn if documentation has errors + + 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. + */ + llvm::cl::opt warnIfDocError; + + /** Warn if parameters are not documented + + When set to `true`, MrDocs outputs a warning message if a named + function parameter is not documented. + */ + llvm::cl::opt warnNoParamdoc; + + /** Warn if documented functions have unnamed parameters + + When set to `true`, MrDocs outputs a warning message if a documented + function has a parameter that is not named. + */ + llvm::cl::opt warnUnnamedParam; + + /** Warn if enum values are not documented + + When set to `true`, MrDocs outputs a warning message if an enum value + is not documented. + */ + llvm::cl::opt warnIfUndocEnumVal; + + /** Warn if a documentation reference is broken + + When set to `true`, MrDocs outputs a warning message if a reference in + the documentation is broken. + */ + llvm::cl::opt warnBrokenRef; + + /** Treat warnings as errors + + When set to `true`, MrDocs treats warnings as errors and stops the + generation of the documentation. + */ + llvm::cl::opt warnAsError; + + //-------------------------------------------- + // Miscellaneous + // + // Miscellaneous options + //-------------------------------------------- + + /** Number of threads to use + + The desired level of concurrency: 0 for hardware-suggested. + */ + llvm::cl::opt concurrency; + + /** Continue if files are not mapped correctly + + When set to true, MrDocs continues to generate the documentation even + if some files are not mapped correctly. + + Files are not mapped correctly when the source file is not found or + the compilation database does not contain the compiler flags for the + source file. + */ + llvm::cl::opt ignoreMapErrors; + + /** Whether AST visitation failures should not stop the program + + When set to true, MrDocs continues to generate the documentation even + if there are AST visitation failures. + + AST visitation failures occur when the source code contains constructs + that are not supported by MrDocs. + */ + llvm::cl::opt ignoreFailures; + + /** Visit all options + */ + template + void + visit(F&& f) + { + std::forward(f)("cmd-line-inputs", cmdLineInputs); + std::forward(f)("config", config); + std::forward(f)("source-root", sourceRoot); + std::forward(f)("output", output); + std::forward(f)("compilation-database", compilationDatabase); + std::forward(f)("input", input); + std::forward(f)("recursive", recursive); + std::forward(f)("file-patterns", filePatterns); + std::forward(f)("exclude", exclude); + std::forward(f)("exclude-patterns", excludePatterns); + std::forward(f)("include-symbols", includeSymbols); + std::forward(f)("exclude-symbols", excludeSymbols); + std::forward(f)("see-below", seeBelow); + std::forward(f)("implementation-defined", implementationDefined); + std::forward(f)("missing-include-prefixes", missingIncludePrefixes); + std::forward(f)("missing-include-shims", missingIncludeShims); + std::forward(f)("auto-brief", autoBrief); + std::forward(f)("auto-relates", autoRelates); + std::forward(f)("auto-function-metadata", autoFunctionMetadata); + std::forward(f)("extract-all", extractAll); + std::forward(f)("extract-private", extractPrivate); + std::forward(f)("extract-private-virtual", extractPrivateVirtual); + std::forward(f)("extract-private-bases", extractPrivateBases); + std::forward(f)("extract-static", extractStatic); + std::forward(f)("extract-local-classes", extractLocalClasses); + std::forward(f)("extract-anonymous-namespaces", extractAnonymousNamespaces); + std::forward(f)("extract-empty-namespaces", extractEmptyNamespaces); + std::forward(f)("inherit-base-members", inheritBaseMembers); + std::forward(f)("extract-implicit-specializations", extractImplicitSpecializations); + std::forward(f)("extract-friends", extractFriends); + std::forward(f)("sort-members", sortMembers); + std::forward(f)("sort-members-by", sortMembersBy); + std::forward(f)("sort-namespace-members-by", sortNamespaceMembersBy); + std::forward(f)("sort-members-ctors-1st", sortMembersCtors1St); + std::forward(f)("sort-members-dtors-1st", sortMembersDtors1St); + std::forward(f)("sort-members-assignment-1st", sortMembersAssignment1St); + std::forward(f)("sort-members-conversion-last", sortMembersConversionLast); + std::forward(f)("sort-members-relational-last", sortMembersRelationalLast); + std::forward(f)("sfinae", sfinae); + std::forward(f)("overloads", overloads); + std::forward(f)("generator", generator); + std::forward(f)("multipage", multipage); + std::forward(f)("base-url", baseUrl); + std::forward(f)("addons", addons); + std::forward(f)("tagfile", tagfile); + std::forward(f)("legible-names", legibleNames); + std::forward(f)("embedded", embedded); + std::forward(f)("show-namespaces", showNamespaces); + std::forward(f)("global-namespace-index", globalNamespaceIndex); + std::forward(f)("cmake", cmake); + std::forward(f)("defines", defines); + std::forward(f)("use-system-stdlib", useSystemStdlib); + std::forward(f)("stdlib-includes", stdlibIncludes); + std::forward(f)("use-system-libc", useSystemLibc); + std::forward(f)("libc-includes", libcIncludes); + std::forward(f)("system-includes", systemIncludes); + std::forward(f)("includes", includes); + std::forward(f)("verbose", verbose); + std::forward(f)("report", report); + std::forward(f)("log-level", logLevel); + std::forward(f)("warnings", warnings); + std::forward(f)("warn-if-undocumented", warnIfUndocumented); + std::forward(f)("warn-if-doc-error", warnIfDocError); + std::forward(f)("warn-no-paramdoc", warnNoParamdoc); + std::forward(f)("warn-unnamed-param", warnUnnamedParam); + std::forward(f)("warn-if-undoc-enum-val", warnIfUndocEnumVal); + std::forward(f)("warn-broken-ref", warnBrokenRef); + std::forward(f)("warn-as-error", warnAsError); + std::forward(f)("concurrency", concurrency); + std::forward(f)("ignore-map-errors", ignoreMapErrors); + std::forward(f)("ignore-failures", ignoreFailures); + } + +}; // struct PublicToolArgs + +} // namespace mrdocs +} // namespace clang + +#endif // MRDOCS_PUBLIC_TOOLARGS_HPP diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index e9de927da..530b373b2 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -2,8 +2,8 @@ * xref:demos.adoc[] * xref:install.adoc[Installation] * xref:usage.adoc[Getting Started] -* xref:commands.adoc[Documenting the Code] * xref:config-file.adoc[] +* xref:commands.adoc[Documenting the Code] * xref:generators.adoc[] * xref:design-notes.adoc[] * xref:contribute.adoc[] diff --git a/docs/modules/ROOT/pages/usage.adoc b/docs/modules/ROOT/pages/usage.adoc index e7786b40c..473aa4297 100644 --- a/docs/modules/ROOT/pages/usage.adoc +++ b/docs/modules/ROOT/pages/usage.adoc @@ -296,3 +296,53 @@ However, if you depend on other system libraries, that means you need to handle 3. Enable `use-system-libc`. MrDocs will still use the bundled libc++ for the C++ standard library, and use the system headers for the C standard library, making all system paths available. That makes system headers such as `` or `` available by default. The trade-off is losing the reproducibility guarantees described above. The first option in this list provides the most control and the most reproducibility. The third option is the most convenient but also the most fragile. + +[#missing-includes] +=== Missing includes + +Sometimes headers aren’t present in the environment where MrDocs runs (CI, containers, new machines), even with the alternatives presented above. For instance, a library that depends on a large third-party dependency may not be able to vendor that dependency or install it in CI just to generate documentation. Instead of failing immediately, MrDocs can optionally forgive specific missing headers and — even more aggressively — synthesize shims that let Clang parse enough of your public API to extract documentation. + +This feature is opt-in. If you don’t configure any forgiveness, MrDocs behaves like plain Clang, and missing headers are hard errors. + +1. First pass (normal parse): +- MrDocs intercepts a narrow set of diagnostics (unknown type/namespace/member) that usually indicate missing symbols. +- From those diagnostics, it extracts candidate symbols that look like types or namespaces (e.g., `llvm::StringRef`, `foo::bar`). +- It records the candidates. + +2. Shim synthesis (only if safe): +- If the recorded names are unambiguous (e.g., clearly a type or clearly a namespace), MrDocs generates a temporary in-memory header with stubs: +- namespaces become empty namespaces; +- types become forward declarations or empty class stubs. +- User-provided shims (see below) override the generated stubs. + +3. Second pass (reparse with the shim): +- MrDocs reparses the TU with the synthetic shim injected so Clang can build an AST for your code. +- If the names were ambiguous or the set is complex/conflicting, MrDocs does not synthesize; it simply re-emits the original errors. + +This is heuristic by design: it works for simple, surface-level references typically found in public headers; it will not fix deep template logic, macro jungles, or APIs that require real definitions. In these cases, the custom shims (see below) you provide are the only way forward. + +- MrDocs forgives a missing include directive if a shim exists in `missing-include-shims` for that path to serve the shim. The shim should contain stubs for the external types/namespaces you need. +- MrDocs forgives a missing include if the prefix of the include string matches one of the values in `missing-include-prefixes`. These include directives are ignored and replaced with an empty include guard. + +When diagnostics still occur and the `missing-include-prefixes` option contains at least one value, Mr.Docs will extract missing symbol names from the translation unit to build the temporary in-memory shim with all symbols for the second pass. + +The configuration allows users to detemrine where forgiveness is allowed, and optional shims for specific files: + +[source,yaml] +---- +# Provide content for particular forgiven files (keys are include-style paths) +missing-include-shims: + "llvm/ADT/StringRef.h": | + namespace llvm { class StringRef; } + "boost/version.hpp": | + #define BOOST_VERSION 108400 + +# Allow certain include trees to be missing without failing +missing-include-prefixes: + - "llvm/" + - "boost/" +---- + +MrDocs wraps shim contents in an internal include guard, so repeated includes are safe. + +MrDocs **does not** attempt synthesis when a name could be either a namespace or a type and there’s no reliable disambiguation, the set of names implies conflicting declarations, or your diagnostics are unrelated to missing includes (e.g., real semantic errors). In these cases, the first-pass errors are just replayed. diff --git a/docs/mrdocs.schema.json b/docs/mrdocs.schema.json index fcf3cbcdb..8865cd48f 100644 --- a/docs/mrdocs.schema.json +++ b/docs/mrdocs.schema.json @@ -344,6 +344,24 @@ ], "title": "The minimum reporting level" }, + "missing-include-prefixes": { + "default": [], + "description": "Specifies path prefixes for include files that, if missing, will not cause documentation generation to fail. Missing files with these prefixes are served as empty files from an in-memory file system, allowing processing to continue. For example, use \"llvm/\" to forgive all includes from LLVM. If any such path is specified, MrDocs will attempt to synthesize missing included types. Only simple sets of non-conflicting inferred types can be synthesized. For more complex types or for better control, provide a shim using the \"missing-include-shims\" option.", + "items": { + "type": "string" + }, + "title": "Include path prefixes allowed to be missing", + "type": "array" + }, + "missing-include-shims": { + "additionalProperties": { + "type": "string" + }, + "default": {}, + "description": "Specifies a map of include file paths to shim contents. If a missing include file matches a forgiven prefix, MrDocs will use the shim content from this map as the file contents. If no shim is provided for a forgiven file, an empty file is used by default.", + "title": "Shims for forgiven missing include files", + "type": "object" + }, "multipage": { "default": true, "description": "Generates a multipage documentation. The output directory must be a directory. This option acts as a hint to the generator to create a multipage documentation. Whether the hint is followed or not depends on the generator.", diff --git a/docs/mrdocs.yml b/docs/mrdocs.yml index bcbe509ff..48edcb1b0 100644 --- a/docs/mrdocs.yml +++ b/docs/mrdocs.yml @@ -5,11 +5,18 @@ verbose: true source-root: .. input: - ../include +includes: + # Stubs for the generated header symbols + - config-headers/include + - config-headers/src file-patterns: - '*.hpp' include-symbols: - 'clang::mrdocs::**' implementation-defined: - 'clang::mrdocs::detail' -multipage: false + - 'clang::mrdocs::report::detail' + - 'clang::mrdocs::dom::detail' +multipage: true generator: adoc +cmake: '-D MRDOCS_DOCUMENTATION_BUILD=ON' diff --git a/include/mrdocs/ADT/Polymorphic.hpp b/include/mrdocs/ADT/Polymorphic.hpp index 8d6f64f16..f1ccca143 100644 --- a/include/mrdocs/ADT/Polymorphic.hpp +++ b/include/mrdocs/ADT/Polymorphic.hpp @@ -77,7 +77,9 @@ template class Polymorphic { /// Default constructs the value type. explicit constexpr Polymorphic() : Polymorphic(std::in_place_type) {} - /// Forwarding constructor. + /** Forwarding constructor. + * @param u The object to copy. + */ template constexpr explicit Polymorphic(U &&u) requires(not std::same_as>) && @@ -85,7 +87,9 @@ template class Polymorphic { std::derived_from, T> : WB(new Wrapper>(std::forward(u))) {} - /// In place constructor. + /** In place constructor + * @param ts Arguments to forward to the constructor of U. + */ template explicit constexpr Polymorphic(std::in_place_type_t, Ts &&...ts) requires std::same_as, U> && @@ -171,7 +175,7 @@ template inline constexpr bool IsPolymorphic> = true; } // namespace detail -/** Compares two polymorphic objects that have visit functions +/** @brief Compares two polymorphic objects that have visit functions This function compares two Polymorphic objects that have visit functions for the Base type. @@ -209,7 +213,7 @@ CompareDerived( : std::strong_ordering::greater; } -/// @copydoc CompareDerived +/// @copydoc CompareDerived(Polymorphic const&, Polymorphic const&) template requires(!detail::IsPolymorphic) && detail::CanVisitCompare auto CompareDerived(Base const &lhs, Base const &rhs) { diff --git a/include/mrdocs/Config.hpp b/include/mrdocs/Config.hpp index c383c3ad7..5fdeb915f 100644 --- a/include/mrdocs/Config.hpp +++ b/include/mrdocs/Config.hpp @@ -16,7 +16,6 @@ #include #include #include -#include "lib/Support/YamlFwd.hpp" #include #include #include @@ -25,6 +24,13 @@ #include #include +namespace llvm::yaml { + +template +struct MappingTraits; + +} // llvm::yaml + namespace clang { namespace mrdocs { @@ -62,13 +68,13 @@ class MRDOCS_DECL */ struct Settings : public PublicSettings { - /** - * @brief Loads the public configuration settings from the specified YAML file. + /** @brief Loads the public configuration settings from the specified YAML file. * * This function takes a YAML file and a set of reference directories as input. * It parses the YAML file and loads the configuration settings into a Config::Settings object. * The reference directories are used to resolve any relative paths in the configuration settings. * + * @param s A reference to a Config::Settings object where the configuration settings will be loaded. * @param configYaml A string view representing the YAML file containing the configuration settings. * @param dirs A constant reference to a PublicSettings::ReferenceDirectories object containing the reference directories. * @return An Expected object containing a Config::Settings object if the YAML file was successfully parsed and the configuration settings were loaded, or an error otherwise. @@ -80,17 +86,17 @@ class MRDOCS_DECL std::string_view configYaml, ReferenceDirectories const& dirs); - /** - * @brief Loads the public configuration settings from the specified file. - * - * This function takes a file path and a set of reference directories as input. - * It reads the file and loads the configuration settings into a Config::Settings object. - * The reference directories are used to resolve any relative paths in the configuration settings. - * - * @param s A reference to a Config::Settings object where the configuration settings will be loaded. - * @param filePath A string view representing the file path of the configuration settings. - * @param dirs A constant reference to a PublicSettings::ReferenceDirectories object containing the reference directories. - * @return An Expected object containing void if the file was successfully read and the configuration settings were loaded, or an error otherwise. + /** Loads the public configuration settings from the specified file. + + This function takes a file path and a set of reference directories as input. + It reads the file and loads the configuration settings into a Config::Settings object. + The reference directories are used to resolve any relative paths in the configuration settings. + + @param s A reference to a Config::Settings object where the configuration settings will be loaded. + @param configPath A string view representing the file path of the configuration settings. + @param dirs A constant reference to a PublicSettings::ReferenceDirectories object containing the reference directories. + + @return An Expected object containing void if the file was successfully read and the configuration settings were loaded, or an error otherwise. */ static Expected load_file( @@ -120,6 +126,8 @@ class MRDOCS_DECL This string will always be native style and have a trailing directory separator. + + @return The full path to the config file directory. */ std::string configDir() const; @@ -143,6 +151,8 @@ class MRDOCS_DECL This string will always be native style and have a trailing directory separator. + + @return The full path to the output directory. */ std::string outputDir() const; diff --git a/include/mrdocs/Corpus.hpp b/include/mrdocs/Corpus.hpp index 2a8b2a784..d50ee6e35 100644 --- a/include/mrdocs/Corpus.hpp +++ b/include/mrdocs/Corpus.hpp @@ -77,6 +77,8 @@ class MRDOCS_VISIBLE end() const noexcept = 0; /** Whether the corpus contains any symbols. + + @return true if the corpus is empty, otherwise false. */ bool empty() const noexcept; @@ -88,12 +90,23 @@ class MRDOCS_VISIBLE find(SymbolID const& id) const noexcept = 0; /** Return the Info for the matching string in a given context. + + @param context The context to look up the symbol in. + @param name The name of the symbol to look up. + @return The Info for the symbol with the specified name + in the specified context, or an error if not found. + If multiple symbols match, one is returned arbitrarily. + Use @ref traverse to find all matching symbols. */ virtual Expected> lookup(SymbolID const& context, std::string_view name) const = 0; /** Return the Info for the matching string in the global context. + + @param name The name of the symbol to look up. + @return The Info for the symbol with the specified name + in the global context, or an error if not found. */ Expected> lookup(std::string_view name) const @@ -336,6 +349,8 @@ class MRDOCS_VISIBLE the fully qualified name which is stored in the string `temp`. + @param I The Info to get the qualified name for. + @param temp The string to store the result in. @return A reference to the string `temp`. */ virtual @@ -366,6 +381,10 @@ class MRDOCS_VISIBLE If the context is not a parent of `I`, the qualified name is constructed relative to the global namespace with the prefix `::`. + + @param I The Info to get the qualified name for. + @param context The context to get the qualified name from. + @param result The string to store the result in. */ virtual void diff --git a/include/mrdocs/Dom/Array.hpp b/include/mrdocs/Dom/Array.hpp index 4b3ab1934..76f63403e 100644 --- a/include/mrdocs/Dom/Array.hpp +++ b/include/mrdocs/Dom/Array.hpp @@ -227,6 +227,8 @@ class MRDOCS_DECL If the array is read-only, an exception is thrown. + + @param args Arguments forwarded to the constructor of Value. */ template< class... Args > void emplace_back(Args&&... args); @@ -284,6 +286,14 @@ class MRDOCS_DECL std::string toString(Array const&); + /** Return a new array using a custom implementation. + + @param args Arguments forwarded to the constructor of T. + @tparam T The type of the custom implementation. + This must be derived from ArrayImpl. + @tparam Args The types of the arguments. + @return A new array using the specified implementation. + */ template requires std::derived_from friend Array newArray(Args&&... args); diff --git a/include/mrdocs/Dom/Function.hpp b/include/mrdocs/Dom/Function.hpp index a31695deb..390e38cdb 100644 --- a/include/mrdocs/Dom/Function.hpp +++ b/include/mrdocs/Dom/Function.hpp @@ -231,17 +231,25 @@ class MRDOCS_DECL call(Array const& args) const; /** Invoke the function. + + @param args The arguments to the function. + @return The return value of the function. + */ template Value operator()(Args&&... args) const; /** Invoke the function. + + @param args The arguments to the function. */ template Expected try_invoke(Args&&... args) const; /** Swap two objects. + + @param other The other object. */ void swap(Function& other) noexcept { @@ -249,12 +257,19 @@ class MRDOCS_DECL } /** Swap two objects. + + @param lhs The first object. + @param rhs The second object. */ friend void swap(Function &lhs, Function& rhs) noexcept { lhs.swap(rhs); } + /** Return a diagnostic string. + + @param args The arguments to the function. + */ template requires std::derived_from friend Function newFunction(Args&&... args); diff --git a/include/mrdocs/Dom/Kind.hpp b/include/mrdocs/Dom/Kind.hpp index 2087305cc..bd94c192c 100644 --- a/include/mrdocs/Dom/Kind.hpp +++ b/include/mrdocs/Dom/Kind.hpp @@ -70,14 +70,23 @@ namespace dom { */ enum class Kind { + /// The value is undefined. Undefined, + /// The value is null. Null, + /// The value is a boolean. Boolean, + /// The value is an integer. Integer, + /// The value is a string. String, + /// The value is a safe string. SafeString, + /// The value is an array. Array, + /// The value is an object. Object, + /// The value is a function. Function }; diff --git a/include/mrdocs/Dom/LazyArray.hpp b/include/mrdocs/Dom/LazyArray.hpp index a7119a13b..06a445bbe 100644 --- a/include/mrdocs/Dom/LazyArray.hpp +++ b/include/mrdocs/Dom/LazyArray.hpp @@ -149,6 +149,9 @@ class LazyArrayImpl : public ArrayImpl }; /** Return a new @ref dom::Array based on a lazy array implementation. + + @param arr The underlying range of elements. + @return A new dom::Array whose elements are the result of converting each element in the underlying range to a dom::Value. */ template requires HasStandaloneValueFrom> @@ -159,6 +162,10 @@ LazyArray(T const& arr) } /** Return a new dom::Array based on a FromValue context + + @param arr The underlying range of elements. + @param ctx The context used to convert each element to a dom::Value. + @return A new dom::Array whose elements are the result of converting each element in the underlying range using the specified context. */ template requires HasValueFrom, Context> @@ -169,6 +176,10 @@ LazyArray(T const& arr, Context const& ctx) } /** Return a new dom::Array based on a transformed lazy array implementation. + + @param arr The underlying range of elements. + @param f The transform function to apply to each element before converting it to a dom::Value. + @return A new dom::Array whose elements are the result of applying the transform function to each element in the underlying range. */ template requires diff --git a/include/mrdocs/Dom/LazyObject.hpp b/include/mrdocs/Dom/LazyObject.hpp index 1de9c7d11..de913e8de 100644 --- a/include/mrdocs/Dom/LazyObject.hpp +++ b/include/mrdocs/Dom/LazyObject.hpp @@ -431,6 +431,9 @@ visit(std::function fn) const /** Return a new dom::Object based on a lazy object implementation. + + @param obj The underlying object. + @return A new dom::Object whose properties are the result of converting each property in the underlying object to a dom::Value. */ template Object @@ -440,6 +443,10 @@ LazyObject(T const& obj) } /** Return a new dom::Object based on a transformed lazy array implementation. + + @param arr The underlying range of elements. + @param context The context used to convert each element to a dom::Value. + @return A new dom::Array whose elements are the result of converting each element in the underlying range using the specified context. */ template requires HasLazyObjectMap diff --git a/include/mrdocs/Dom/Object.hpp b/include/mrdocs/Dom/Object.hpp index 4b30bac0c..1a1553405 100644 --- a/include/mrdocs/Dom/Object.hpp +++ b/include/mrdocs/Dom/Object.hpp @@ -214,13 +214,25 @@ class MRDOCS_DECL std::size_t size() const; /** Return the element with the specified key + + @param key The key. + @return The value for the specified key, or null + if the key does not exist. */ Value get(std::string_view key) const; - /// @copydoc get + /** Return the element at a given index. + + @param i The index. + @return The value at the specified index, or null + if the index is out of range. + */ Value at(std::string_view i) const; /** Return true if a key exists. + + @param key The key to check for existence. + @return `true` if the key exists, otherwise `false`. */ bool exists(std::string_view key) const; @@ -242,6 +254,7 @@ class MRDOCS_DECL continue iteration, or `false` to stop iteration early. + @param fn The visitor function. @return `true` if the visitor returned `true` for all elements, otherwise `false`. @@ -265,6 +278,11 @@ class MRDOCS_DECL return an error for any element, otherwise `E`. + @param fn The visitor function. + @return `void` if the visitor returned did not + return an error for any element, otherwise + `E`. + */ template requires @@ -274,6 +292,8 @@ class MRDOCS_DECL visit(F&& fn) const; /** Invoke the visitor for each key/value pair + + @param fn The visitor function. */ template requires @@ -350,10 +370,18 @@ class MRDOCS_DECL virtual char const* type_key() const noexcept; /** Return the value for the specified key, or null. + + @param key The key. + @return The value for the specified key, or null + if the key does not exist. */ virtual Value get(std::string_view key) const = 0; /** Insert or set the given key/value pair. + + @param key The key. + @param value The value to set. + */ virtual void set(String key, Value value) = 0; @@ -366,19 +394,38 @@ class MRDOCS_DECL visitor returned `true` for all elements, otherwise `false`. + @param fn The visitor function. + @return `true` if the visitor returned `true` + for all elements, otherwise `false`. + */ - virtual bool visit(std::function) const = 0; + virtual + bool + visit(std::function fn) const = 0; /** Return the number of properties in the object. */ - virtual std::size_t size() const = 0; + virtual + std::size_t + size() const = 0; /** Determine if a key exists. + + @param key The key to check for existence. + @return `true` if the key exists, otherwise `false`. */ - virtual bool exists(std::string_view key) const; + virtual + bool + exists(std::string_view key) const; }; /** Return a new object using a custom implementation. + + @param args Arguments forwarded to the constructor of T. + @tparam T The type of the custom implementation. + This must be derived from ObjectImpl. + @tparam Args The types of the arguments. + @return A new object using the specified implementation. */ template requires std::derived_from diff --git a/include/mrdocs/Dom/String.hpp b/include/mrdocs/Dom/String.hpp index fb6c5de71..da09155d0 100644 --- a/include/mrdocs/Dom/String.hpp +++ b/include/mrdocs/Dom/String.hpp @@ -111,9 +111,9 @@ class MRDOCS_DECL This function constructs a new string from the string pointed to by `str` of length `len`. - @param `str` The string to construct with. + @param str The string to construct with. A copy of this string is made. - @param `len` The length of the string. + @param len The length of the string. */ String( char const* str, @@ -236,6 +236,8 @@ class MRDOCS_DECL } /** Swap two strings. + + @param other The other string. */ constexpr void @@ -245,6 +247,9 @@ class MRDOCS_DECL } /** Swap two strings. + + @param lhs The first string. + @param rhs The second string. */ friend constexpr @@ -305,6 +310,10 @@ class MRDOCS_DECL } /** Concatenate two strings. + + @param lhs The left-hand side string. + @param rhs The right-hand side string. + @return The concatenated string. */ friend auto operator+( String const& lhs, String const& rhs) noexcept diff --git a/include/mrdocs/Dom/Value.hpp b/include/mrdocs/Dom/Value.hpp index 74f1a75fd..0511f1bf7 100644 --- a/include/mrdocs/Dom/Value.hpp +++ b/include/mrdocs/Dom/Value.hpp @@ -53,6 +53,51 @@ safeString(std::string_view str); dom::Value safeString(dom::Value const& str); +/** Objects representing JSON-like values. + + This class is a variant-like container for holding + any kind of value that can be represented in JSON, + with extensions for functions and "safe strings". + + The class supports the following types: + + - Undefined + - Null + - Boolean + - Integer + - String + - SafeString + - Array + - Object + - Function + + The class provides type-safe accessors for each type, + as well as methods to check the type of the contained value. + + Example: + + @code{.cpp} + dom::Value v1 = 42; // Integer + dom::Value v2 = "Hello, World!"; // String + dom::Value v3 = dom::Array{v1, v2}; // Array + + if (v1.isInteger()) + { + std::cout << "v1 is an integer: " << v1.getInteger() << "\n"; + } + + if (v2.isString()) + { + std::cout << "v2 is a string: " << v2.getString() << "\n"; + } + + if (v3.isArray()) + { + std::cout << "v3 is an array with " << v3.getArray().size() << " elements.\n"; + } + @endcode + +*/ namespace dom { /** A variant container for any kind of Dom value. @@ -298,6 +343,10 @@ class MRDOCS_DECL If the Value is not an object, or the key is not found, a Value of type @ref Kind::Undefined is returned. + + @param key The key. + @return The value for the specified key, or a Value of type + @ref Kind::Undefined if the key does not exist. */ dom::Value get(std::string_view key) const; @@ -310,6 +359,9 @@ class MRDOCS_DECL } /** Return the element at a given index. + + @param i The index. + @return The value at the specified index, or a Value of type */ dom::Value get(std::size_t i) const; @@ -325,6 +377,7 @@ class MRDOCS_DECL multiple times, once for each key in the sequence of dot-separated keys. + @param keys The dot-separated sequence of keys. @return The value at the end of the sequence, or a Value of type @ref Kind::Undefined if any key is not found. @@ -333,11 +386,17 @@ class MRDOCS_DECL lookup(std::string_view keys) const; /** Set or replace the value for a given key. + + @param key The key. + @param value The value to set. */ void set(String const& key, Value const& value); /** Return true if a key exists. + + @param key The key to check for existence. + @return `true` if the key exists, otherwise `false`. */ bool exists(std::string_view key) const; @@ -357,6 +416,8 @@ class MRDOCS_DECL If the Value is not an object, or the key is not found, a Value of type @ref Kind::Undefined is returned. + + @param args The arguments to the function. */ template Value operator()(Args&&... args) const @@ -550,6 +611,8 @@ stringify(dom::Value const& value); } /** Return a non-empty string, or a null. + + @param s The string to check. */ inline Value @@ -684,7 +747,7 @@ concept HasStandaloneValueFrom = @param ctx Context passed to the conversion function. - @param jv @ref dom::Value out parameter. + @param v @ref dom::Value out parameter. @see @ref dom::ValueFromTag @@ -730,7 +793,7 @@ ValueFrom( @param t The object to convert. - @param jv @ref dom::Value out parameter. + @param v @ref dom::Value out parameter. @see @ref dom::ValueFromTag @@ -816,6 +879,8 @@ ValueFrom(T&& t) @param t The object to convert. + @param ctx Context passed to the conversion function. + @return @ref dom::Value out parameter. @see @ref dom::ValueFromTag, diff --git a/include/mrdocs/Generator.hpp b/include/mrdocs/Generator.hpp index 7127c0b26..a4c4b7b33 100644 --- a/include/mrdocs/Generator.hpp +++ b/include/mrdocs/Generator.hpp @@ -76,7 +76,7 @@ class MRDOCS_VISIBLE /** Build reference documentation for the corpus. This function invokes the generator to emit - the documentation using @ref outputPath as + the documentation using `outputPath` as a parameter indicating where the output should go. This can be a directory or a filename depending on the generator and how it is @@ -92,13 +92,14 @@ class MRDOCS_VISIBLE the symbols in the corpus to generate multiple files if the output is multi-page. - @return The error, if any occurred. - @param outputPath A directory or filename. @param corpus The symbols to emit. The generator may modify the contents of the object before returning. + + @return The error, if any occurred. + */ MRDOCS_DECL virtual diff --git a/include/mrdocs/Metadata/DomCorpus.hpp b/include/mrdocs/Metadata/DomCorpus.hpp index d5ad1e55f..0ab8ed7dd 100644 --- a/include/mrdocs/Metadata/DomCorpus.hpp +++ b/include/mrdocs/Metadata/DomCorpus.hpp @@ -30,8 +30,7 @@ class SymbolID; of extracted metadata, and provides a mechanism for constructing DOM nodes representing the metadata. - A @ref Generator subclasses this object - (e.g. @ref AdocCorpus and @ref HTMLCorpus), + A @ref Generator can subclass this object then uses it to create the Dom nodes used as input for rendering template engines. */ diff --git a/include/mrdocs/Metadata/ExtractionMode.hpp b/include/mrdocs/Metadata/ExtractionMode.hpp index b484e4e9c..9d2b7b832 100644 --- a/include/mrdocs/Metadata/ExtractionMode.hpp +++ b/include/mrdocs/Metadata/ExtractionMode.hpp @@ -89,10 +89,10 @@ tag_invoke( /** Compare ExtractionModes and returns the least specific This function returns the least specific of the two - ExtractionModes in terms of number of filters passed. + ExtractionModes in terms of the number of filters passed. If the symbol passes the filter that categorizes it - as `a` then it also passes the filter that categorizes + as `a`, then it also passes the filter that categorizes it as `b` (or vice-versa), then this function will return the final category for the symbol. */ diff --git a/include/mrdocs/Metadata/Info.hpp b/include/mrdocs/Metadata/Info.hpp index c69315794..cbb129aea 100644 --- a/include/mrdocs/Metadata/Info.hpp +++ b/include/mrdocs/Metadata/Info.hpp @@ -36,6 +36,7 @@ namespace clang::mrdocs { */ enum class InfoKind { + /// Kind is not specified. None = 0, #define INFO(Type) Type, #include @@ -275,6 +276,9 @@ void merge(Info& I, Info&& Other); /** Merges two Info objects according to the behavior of the derived class. + + @param I The Info object to merge into. + @param Other The Info object to merge from. */ template InfoTy> void @@ -304,9 +308,6 @@ canMerge(Info const& I, Info const& Other) In most cases `T` is another `Info` type that has a `Members` member which is a range of `SymbolID` values. - - However, an @ref OverloadSet is also a type that - contains `Info` members without being `Info` itself. */ template concept InfoParent = requires(InfoTy const& I) @@ -315,6 +316,10 @@ concept InfoParent = requires(InfoTy const& I) }; /** Map the Info to a @ref dom::Object. + + @param io The output parameter to receive the dom::Object. + @param I The Info to convert. + @param domCorpus The DomCorpus used to resolve references. */ template void @@ -350,6 +355,10 @@ tag_invoke( } /** Map the Polymorphic Info to a @ref dom::Object. + + @param io The output parameter to receive the dom::Object. + @param I The polymorphic Info to convert. + @param domCorpus The DomCorpus used to resolve references. */ template PolymorphicInfo> requires std::derived_from @@ -384,6 +393,10 @@ tag_invoke( } /** Map the Polymorphic Info as a @ref dom::Value object. + + @param io The output parameter to receive the dom::Value. + @param I The polymorphic Info to convert. + @param domCorpus The DomCorpus used to resolve references. */ template InfoTy> requires std::derived_from diff --git a/include/mrdocs/Metadata/Info/Concept.hpp b/include/mrdocs/Metadata/Info/Concept.hpp index 1073b7316..6a70fa066 100644 --- a/include/mrdocs/Metadata/Info/Concept.hpp +++ b/include/mrdocs/Metadata/Info/Concept.hpp @@ -48,6 +48,11 @@ void merge(ConceptInfo& I, ConceptInfo&& Other); /** Map a ConceptInfo to a dom::Object. + + @param t The tag type. + @param io The IO object to use for mapping. + @param I The ConceptInfo to map. + @param domCorpus The DomCorpus used to create */ template void diff --git a/include/mrdocs/Metadata/Info/Enum.hpp b/include/mrdocs/Metadata/Info/Enum.hpp index 0e5cb7b52..b78825428 100644 --- a/include/mrdocs/Metadata/Info/Enum.hpp +++ b/include/mrdocs/Metadata/Info/Enum.hpp @@ -57,6 +57,11 @@ void merge(EnumInfo& I, EnumInfo&& Other); /** Map a EnumInfo to a dom::Object. + + @param t The tag type. + @param io The IO object to use for mapping. + @param I The EnumInfo to map. + @param domCorpus The DomCorpus used to create */ template void @@ -73,6 +78,10 @@ tag_invoke( } /** Map the EnumInfo to a @ref dom::Value object. + + @param v The output parameter to receive the dom::Value. + @param I The EnumInfo to convert. + @param domCorpus The DomCorpus used to resolve references. */ inline void diff --git a/include/mrdocs/Metadata/Info/EnumConstant.hpp b/include/mrdocs/Metadata/Info/EnumConstant.hpp index d65c50812..c84681b87 100644 --- a/include/mrdocs/Metadata/Info/EnumConstant.hpp +++ b/include/mrdocs/Metadata/Info/EnumConstant.hpp @@ -39,6 +39,11 @@ void merge(EnumConstantInfo& I, EnumConstantInfo&& Other); /** Map a EnumConstantInfo to a dom::Object. + + @param t The tag type. + @param io The IO object to use for mapping. + @param I The EnumConstantInfo to map. + @param domCorpus The DomCorpus used to create */ template void diff --git a/include/mrdocs/Metadata/Info/Friend.hpp b/include/mrdocs/Metadata/Info/Friend.hpp index 73c869541..36b2948ac 100644 --- a/include/mrdocs/Metadata/Info/Friend.hpp +++ b/include/mrdocs/Metadata/Info/Friend.hpp @@ -39,6 +39,11 @@ void merge(FriendInfo& I, FriendInfo&& Other); /** Map a FriendInfo to a dom::Object. + + @param t The tag type. + @param io The IO object to use for mapping. + @param I The FriendInfo to map. + @param domCorpus The DomCorpus used to create */ template void diff --git a/include/mrdocs/Metadata/Info/Function.hpp b/include/mrdocs/Metadata/Info/Function.hpp index 13accfe04..7e6df927e 100644 --- a/include/mrdocs/Metadata/Info/Function.hpp +++ b/include/mrdocs/Metadata/Info/Function.hpp @@ -47,12 +47,18 @@ getShortOperatorName( OperatorKind kind) noexcept; /** Return the short name of an operator as a string. + + @param name The operator name, e.g. `operator+`, `operator++`, `operator[]`, etc. + @return The OperatorKind, or OperatorKind::None if not recognized. */ MRDOCS_DECL OperatorKind getOperatorKind(std::string_view name) noexcept; /** Return the short name of an operator as a string. + + @param suffix The operator suffix, e.g. `+`, `++`, `[]`, etc. + @return The OperatorKind, or OperatorKind::None if not recognized. */ MRDOCS_DECL OperatorKind @@ -71,6 +77,10 @@ getSafeOperatorName( bool include_keyword = false) noexcept; /** Return the human-readable name of the operator + + @param kind The kind of operator. + @param nParams The number of parameters the operator takes. + @return The readable name, or nullopt if the operator is not recognized. */ std::optional getOperatorReadableName( @@ -80,10 +90,13 @@ getOperatorReadableName( /** Function classifications */ enum class FunctionClass { + /// The function is a normal function. Normal = 0, + /// The function is a constructor. Constructor, - // conversion function + /// The function is a conversion operator. Conversion, + /// The function is a destructor. Destructor }; @@ -209,6 +222,11 @@ void merge(FunctionInfo& I, FunctionInfo&& Other); /** Map a FunctionInfo to a dom::Object. + + @param t The tag type. + @param io The IO object to use for mapping. + @param I The FunctionInfo to map. + @param domCorpus The DomCorpus used to create */ template void @@ -262,6 +280,10 @@ tag_invoke( } /** Map the FunctionInfo to a @ref dom::Value object. + + @param v The output parameter to receive the dom::Value. + @param I The FunctionInfo to convert. + @param domCorpus The DomCorpus used to resolve references. */ inline void @@ -275,6 +297,9 @@ tag_invoke( } /** Determine if one function would override the other + + @param base The base function + @param derived The derived function */ MRDOCS_DECL bool diff --git a/include/mrdocs/Metadata/Info/Guide.hpp b/include/mrdocs/Metadata/Info/Guide.hpp index 2f83f9237..934c8e590 100644 --- a/include/mrdocs/Metadata/Info/Guide.hpp +++ b/include/mrdocs/Metadata/Info/Guide.hpp @@ -60,6 +60,11 @@ void merge(GuideInfo& I, GuideInfo&& Other); /** Map a GuideInfo to a dom::Object. + + @param t The tag type. + @param io The IO object to use for mapping. + @param I The GuideInfo to map. + @param domCorpus The DomCorpus used to create */ template void diff --git a/include/mrdocs/Metadata/Info/Namespace.hpp b/include/mrdocs/Metadata/Info/Namespace.hpp index acb52ecef..01f44d3ba 100644 --- a/include/mrdocs/Metadata/Info/Namespace.hpp +++ b/include/mrdocs/Metadata/Info/Namespace.hpp @@ -67,6 +67,10 @@ allMembers(NamespaceTranche const& T) } /** Map a NamespaceTranche to a dom::Object. + + @param io The IO object to use for mapping. + @param I The NamespaceTranche to map. + @param domCorpus The DomCorpus used to create */ template void @@ -137,6 +141,11 @@ allMembers(NamespaceInfo const& T) } /** Map a NamespaceInfo to a dom::Object. + + @param t The tag type. + @param io The IO object to use for mapping. + @param I The NamespaceInfo to map. + @param domCorpus The DomCorpus used to create */ template void diff --git a/include/mrdocs/Metadata/Info/NamespaceAlias.hpp b/include/mrdocs/Metadata/Info/NamespaceAlias.hpp index 745f16a5b..df93ea841 100644 --- a/include/mrdocs/Metadata/Info/NamespaceAlias.hpp +++ b/include/mrdocs/Metadata/Info/NamespaceAlias.hpp @@ -43,6 +43,11 @@ void merge(NamespaceAliasInfo& I, NamespaceAliasInfo&& Other); /** Map a NamespaceAliasInfo to a dom::Object. + + @param t The tag type. + @param io The IO object to use for mapping. + @param I The NamespaceAliasInfo to map. + @param domCorpus The DomCorpus used to create */ template void diff --git a/include/mrdocs/Metadata/Info/Overloads.hpp b/include/mrdocs/Metadata/Info/Overloads.hpp index a716cfedb..d88e3f79b 100644 --- a/include/mrdocs/Metadata/Info/Overloads.hpp +++ b/include/mrdocs/Metadata/Info/Overloads.hpp @@ -60,6 +60,11 @@ void addMember(OverloadsInfo& I, FunctionInfo const& Member); /** Map a OverloadsInfo to a dom::Object. + + @param t The tag type. + @param io The IO object to use for mapping. + @param I The OverloadsInfo to map. + @param domCorpus The DomCorpus used to create */ template void diff --git a/include/mrdocs/Metadata/Info/Record.hpp b/include/mrdocs/Metadata/Info/Record.hpp index 1807183ae..db83088d9 100644 --- a/include/mrdocs/Metadata/Info/Record.hpp +++ b/include/mrdocs/Metadata/Info/Record.hpp @@ -82,6 +82,10 @@ allMembers(RecordTranche const& T) } /** Map a RecordTranche to a dom::Object. + + @param io The output parameter to receive the dom::Object. + @param I The RecordTranche to convert. + @param domCorpus The DomCorpus used to resolve references. */ template void @@ -166,6 +170,9 @@ void merge(RecordInterface& I, RecordInterface&& Other); /** Map a RecordInterface to a dom::Object. + + @param io The output parameter to receive the dom::Object. + @param I The RecordInterface to convert. */ template void @@ -242,10 +249,15 @@ tag_invoke( BaseInfo const& I, DomCorpus const* domCorpus); +/** The kind of record: struct, class, or union. +*/ enum class RecordKeyKind { + /// A struct. Struct, + /// A C++ class. Class, + /// A C-style Union Union }; @@ -341,6 +353,11 @@ void merge(RecordInfo& I, RecordInfo&& Other); /** Map a RecordInfo to a dom::Object. + + @param t The tag type. + @param io The IO object to use for mapping. + @param I The RecordInfo to map. + @param domCorpus The DomCorpus used to create */ template void diff --git a/include/mrdocs/Metadata/Info/Typedef.hpp b/include/mrdocs/Metadata/Info/Typedef.hpp index cb9f4a95c..96431d1cc 100644 --- a/include/mrdocs/Metadata/Info/Typedef.hpp +++ b/include/mrdocs/Metadata/Info/Typedef.hpp @@ -60,6 +60,11 @@ void merge(TypedefInfo& I, TypedefInfo&& Other); /** Map a TypedefInfo to a dom::Object. + + @param t The tag type. + @param io The IO object to use for mapping. + @param I The TypedefInfo to map. + @param domCorpus The DomCorpus used to create */ template void @@ -76,6 +81,10 @@ tag_invoke( } /** Map the TypedefInfo to a @ref dom::Value object. + + @param v The output parameter to receive the dom::Value. + @param I The TypedefInfo to convert. + @param domCorpus The DomCorpus used to resolve references. */ inline void diff --git a/include/mrdocs/Metadata/Info/Using.hpp b/include/mrdocs/Metadata/Info/Using.hpp index 93fb504b3..5308b000d 100644 --- a/include/mrdocs/Metadata/Info/Using.hpp +++ b/include/mrdocs/Metadata/Info/Using.hpp @@ -21,10 +21,19 @@ namespace clang::mrdocs { +/** The class of using declaration. + + This indicates whether the using declaration + is a normal `using`, a `using typename`, or + a `using enum`. +*/ enum class UsingClass { + /// Using declaration class. Normal = 0, // using + /// Using typename declaration class. Typename, // using typename + /// Using enum declaration class. Enum // using enum }; @@ -42,6 +51,9 @@ toString(UsingClass const& value) } /** Return the UsingClass as a @ref dom::Value string. + + @param v The output value. + @param kind The UsingClass to convert. */ inline void @@ -129,6 +141,11 @@ MRDOCS_DECL void merge(UsingInfo& I, UsingInfo&& Other); /** Map a UsingInfo to a dom::Object. + + @param t The tag type. + @param io The IO object to use for mapping. + @param I The UsingInfo to map. + @param domCorpus The DomCorpus used to create */ template void diff --git a/include/mrdocs/Metadata/Info/Variable.hpp b/include/mrdocs/Metadata/Info/Variable.hpp index 80dd60a67..973e0a5a5 100644 --- a/include/mrdocs/Metadata/Info/Variable.hpp +++ b/include/mrdocs/Metadata/Info/Variable.hpp @@ -89,6 +89,11 @@ void merge(VariableInfo& I, VariableInfo&& Other); /** Map a VariableInfo to a dom::Object. + + @param t The tag type. + @param io The IO object to use for mapping. + @param I The VariableInfo to map. + @param domCorpus The DomCorpus used to create */ template void diff --git a/include/mrdocs/Metadata/Javadoc.hpp b/include/mrdocs/Metadata/Javadoc.hpp index 9602847e4..c927c06d7 100644 --- a/include/mrdocs/Metadata/Javadoc.hpp +++ b/include/mrdocs/Metadata/Javadoc.hpp @@ -106,25 +106,45 @@ enum class NodeKind // Node::isText() and Node::isBlock() // when changing this enum! + /// A text tag text = 1, // needed by bitstream + /// An admonition tag admonition, + /// A brief tag brief, + /// A code tag code, + /// A heading tag heading, + /// A link tag link, + /// A list_item tag list_item, + /// An unordered_list tag unordered_list, + /// A paragraph tag paragraph, + /// A param tag param, + /// A returns tag returns, + /// A styled tag styled, + /// A tparam tag tparam, + /// A reference tag reference, + /// A copy_details tag copy_details, + /// A throws tag throws, + /// A details tag details, + /// A see tag see, + /// A general tag. precondition, + /// A postcondition tag. postcondition }; @@ -146,17 +166,24 @@ tag_invoke( v = toString(kind); } -/** A text style. +/** The text style. */ enum class Style { + /// No style none = 1, // needed by bitstream + /// Monospaced text mono, + /// Bold text bold, + /// Italic text italic }; /** Return the name of the @ref Style as a string. + + @param kind The style kind. + @return The string representation of the style. */ MRDOCS_DECL dom::String @@ -178,11 +205,17 @@ tag_invoke( */ enum class Admonish { + /// No admonishment none = 1, // needed by bitstream + /// A general note note, + /// A tip to the reader tip, + /// Something important important, + /// A caution admonishment caution, + /// A warning admonishment warning }; @@ -209,9 +242,13 @@ tag_invoke( */ enum class ParamDirection { + /// No direction specified none, + /// Parameter is passed in, + /// Parameter is passed back to the caller out, + /// Parameter is passed and passed back to the caller inout }; @@ -241,8 +278,11 @@ tag_invoke( */ enum class Parts { + /// Copy the brief and the description all = 1, // needed by bitstream + /// Copy the brief brief, + /// Copy the description description }; @@ -302,6 +342,10 @@ struct MRDOCS_DECL }; /** Map the @ref Node to a @ref dom::Object. + + @param io The output parameter to receive the dom::Object. + @param I The input object. + @param domCorpus The DOM corpus, or nullptr if not part of a corpus. */ template void @@ -329,6 +373,10 @@ tag_invoke( } /** Map the Polymorphic Node as a @ref dom::Value object. + + @param io The output parameter to receive the dom::Object. + @param I The input object. + @param domCorpus The DOM corpus, or nullptr if not part of a corpus. */ template NodeTy> void @@ -399,6 +447,11 @@ struct Text : Node }; /** Map the @ref Text to a @ref dom::Object. + + @param t The tag. + @param io The output object. + @param I The input object. + @param domCorpus The DOM corpus, or nullptr if not part of a corpus. */ template void @@ -461,6 +514,11 @@ struct Styled final : Text }; /** Map the @ref Styled to a @ref dom::Object. + + @param t The tag. + @param io The output object. + @param I The input object. + @param domCorpus The DOM corpus, or nullptr if not part of a corpus. */ template void @@ -515,6 +573,11 @@ struct Link final : Text }; /** Map the @ref Link to a @ref dom::Object. + + @param t The tag. + @param io The output object. + @param I The input object. + @param domCorpus The DOM corpus, or nullptr if not part of a corpus. */ template void @@ -574,6 +637,11 @@ struct Reference : Text }; /** Map the @ref Reference to a @ref dom::Object. + + @param t The tag. + @param io The output object. + @param I The input object. + @param domCorpus The DOM corpus, or nullptr if not part of a corpus. */ template void @@ -620,7 +688,12 @@ struct CopyDetails final : Reference } }; -/** Map the @ref Copied to a @ref dom::Object. +/** Map the @ref CopyDetails to a @ref dom::Object. + + @param t The tag. + @param io The output object. + @param I The input object. + @param domCorpus The DOM corpus, or nullptr if not part of a corpus. */ template void @@ -633,7 +706,11 @@ tag_invoke( tag_invoke(t, io, dynamic_cast(I), domCorpus); } -/** Return the @ref Copied as a @ref dom::Value object. +/** Return the @ref CopyDetails as a @ref dom::Value object. + + @param v The output value. + @param I The input object. + @param domCorpus The DOM corpus, or nullptr if not part of a corpus. */ inline void @@ -736,6 +813,11 @@ struct MRDOCS_DECL }; /** Map the @ref Block to a @ref dom::Object. + + @param t The tag. + @param io The output object. + @param I The input object. + @param domCorpus The DOM corpus, or nullptr if not part of a corpus. */ template void @@ -789,6 +871,11 @@ struct Heading final : Block }; /** Map the @ref Heading to a @ref dom::Object. + + @param t The tag. + @param io The output object. + @param I The input object. + @param domCorpus The DOM corpus, or nullptr if not part of a corpus. */ template void @@ -846,6 +933,11 @@ struct Paragraph : Block }; /** Map the @ref Paragraph to a @ref dom::Object. + + @param t The tag. + @param io The output object. + @param I The input object. + @param domCorpus The DOM corpus, or nullptr if not part of a corpus. */ template void @@ -914,6 +1006,11 @@ struct Brief final : Paragraph }; /** Map the @ref Brief to a @ref dom::Object. + + @param t The tag. + @param io The output object. + @param I The input object. + @param domCorpus The DOM corpus, or nullptr if not part of a corpus. */ template void @@ -971,6 +1068,11 @@ struct Admonition final : Paragraph }; /** Map the @ref Admonition to a @ref dom::Object. + + @param t The tag. + @param io The output object. + @param I The input object. + @param domCorpus The DOM corpus, or nullptr if not part of a corpus. */ template void @@ -1022,6 +1124,11 @@ struct Code final : Paragraph }; /** Map the @ref Code to a @ref dom::Object. + + @param t The tag. + @param io The output object. + @param I The input object. + @param domCorpus The DOM corpus, or nullptr if not part of a corpus. */ template void @@ -1080,6 +1187,11 @@ struct ListItem final : Paragraph }; /** Map the @ref ListItem to a @ref dom::Object. + + @param t The tag. + @param io The output object. + @param I The input object. + @param domCorpus The DOM corpus, or nullptr if not part of a corpus. */ template void @@ -1157,6 +1269,11 @@ struct UnorderedList final : Paragraph }; /** Map the @ref UnorderedList to a @ref dom::Object. + + @param t The tag. + @param io The output object. + @param I The input object. + @param domCorpus The DOM corpus, or nullptr if not part of a corpus. */ template void @@ -1211,6 +1328,11 @@ struct See final : Paragraph }; /** Map the @ref See to a @ref dom::Object. + + @param t The tag. + @param io The output object. + @param I The input object. + @param domCorpus The DOM corpus, or nullptr if not part of a corpus. */ template void @@ -1294,6 +1416,11 @@ struct Param final : Paragraph }; /** Map the @ref Param to a @ref dom::Object. + + @param t The tag. + @param io The output object. + @param I The input object. + @param domCorpus The DOM corpus, or nullptr if not part of a corpus. */ template void @@ -1366,6 +1493,11 @@ struct Returns final : Paragraph }; /** Map the @ref Returns to a @ref dom::Object. + + @param t The tag. + @param io The output object. + @param I The input object. + @param domCorpus The DOM corpus, or nullptr if not part of a corpus. */ template void @@ -1416,6 +1548,11 @@ struct TParam final : Paragraph }; /** Map the @ref TParam to a @ref dom::Object. + + @param t The tag. + @param io The output object. + @param I The input object. + @param domCorpus The DOM corpus, or nullptr if not part of a corpus. */ template void @@ -1475,6 +1612,11 @@ struct Throws final : Paragraph }; /** Map the @ref Throws to a @ref dom::Object. + + @param t The tag. + @param io The output object. + @param I The input object. + @param domCorpus The DOM corpus, or nullptr if not part of a corpus. */ template void @@ -1528,6 +1670,11 @@ struct Precondition final : Paragraph }; /** Map the @ref Precondition to a @ref dom::Object. + + @param t The tag. + @param io The output object. + @param I The input object. + @param domCorpus The DOM corpus, or nullptr if not part of a corpus. */ template void @@ -1541,6 +1688,10 @@ tag_invoke( } /** Return the @ref Precondition as a @ref dom::Value object. + + @param v The value to assign to. + @param I The input object. + @param domCorpus The DOM corpus, or nullptr if not part of a corpus. */ inline void @@ -1580,6 +1731,11 @@ struct Postcondition : Paragraph }; /** Map the @ref Postcondition to a @ref dom::Object. + + @param t The tag. + @param io The output object. + @param I The input object. + @param domCorpus The DOM corpus, or nullptr if not part of a corpus. */ template void @@ -1593,6 +1749,10 @@ tag_invoke( } /** Return the @ref Postcondition as a @ref dom::Value object. + + @param v The value to assign to. + @param I The input object. + @param domCorpus The DOM corpus, or nullptr if not part of a corpus. */ inline void @@ -1612,6 +1772,7 @@ tag_invoke( @param node The node to visit. @param fn The function to call for each node. @param args Additional arguments to pass to the function. + @return The result of calling the function. */ template< class NodeTy, @@ -1847,6 +2008,8 @@ struct MRDOCS_DECL void append(Javadoc&& other); /** Append blocks from a list to this. + + @param blocks The blocks to append. */ void append(std::vector>&& blocks); @@ -1871,6 +2034,11 @@ void merge(Javadoc& I, Javadoc&& other) } /** Map the @ref Javadoc to a @ref dom::Object. + + @param t The tag. + @param io The output object. + @param I The javadoc to map. + @param domCorpus The DOM corpus, or nullptr. */ template void diff --git a/include/mrdocs/Metadata/Name.hpp b/include/mrdocs/Metadata/Name.hpp index 5b1928dfb..9ea25520d 100644 --- a/include/mrdocs/Metadata/Name.hpp +++ b/include/mrdocs/Metadata/Name.hpp @@ -21,7 +21,9 @@ namespace clang::mrdocs { enum class NameKind { + /// A simple identifier Identifier = 1, // for bitstream + /// A template instantiation Specialization }; diff --git a/include/mrdocs/Metadata/Source.hpp b/include/mrdocs/Metadata/Source.hpp index 06d990d99..4c887b62c 100644 --- a/include/mrdocs/Metadata/Source.hpp +++ b/include/mrdocs/Metadata/Source.hpp @@ -22,11 +22,11 @@ namespace clang::mrdocs { enum class FileKind { - // File in the source directory + /// File in the source directory Source, - // File in a system include directory + /// File in a system include directory System, - // File outside the source directory + /// File outside the source directory Other }; diff --git a/include/mrdocs/Metadata/Specifiers.hpp b/include/mrdocs/Metadata/Specifiers.hpp index 11c00e210..9d8429228 100644 --- a/include/mrdocs/Metadata/Specifiers.hpp +++ b/include/mrdocs/Metadata/Specifiers.hpp @@ -30,9 +30,13 @@ namespace clang::mrdocs { */ enum class AccessKind { + /// Unspecified access None = 0, + /// Public access Public, + /// Protected access Protected, + /// Private access Private, }; @@ -43,9 +47,12 @@ enum class AccessKind */ enum class ConstexprKind { + /// No `constexpr` or `consteval` specifier None = 0, + /// The `constexpr` specifier Constexpr, - // only valid for functions + /// The `consteval` specifier + /// only valid for functions Consteval, }; @@ -119,54 +126,100 @@ struct NoexceptInfo */ enum class OperatorKind { + /// No operator None = 0, + /// The `new` Operator New, + /// The `delete` Operator Delete, + /// The `new[]` Operator ArrayNew, + /// The `delete[]` Operator ArrayDelete, + /// The + Operator Plus, + /// The - Operator Minus, + /// The * Operator Star, + /// The / Operator Slash, + /// The % Operator Percent, + /// The ^ Operator Caret, + /// The & Operator Amp, + /// The | Operator Pipe, + /// The ~ Operator Tilde, + /// The ! Operator Equal, + /// The += Operator PlusEqual, + /// The -= Operator MinusEqual, + /// The *= Operator StarEqual, + /// The /= Operator SlashEqual, + /// The %= Operator PercentEqual, + /// The ^= Operator CaretEqual, + /// The &= Operator AmpEqual, + /// The |= Operator PipeEqual, + /// The << Operator LessLess, + /// The >> Operator GreaterGreater, + /// The <<= Operator LessLessEqual, + /// The >>= Operator GreaterGreaterEqual, // Relational operators + /// The ! Operator Exclaim, + /// The == Operator EqualEqual, + /// The != Operator ExclaimEqual, + /// The < Operator Less, + /// The <= Operator LessEqual, + /// The > Operator Greater, + /// The >= Operator GreaterEqual, + /// The <=> Operator Spaceship, + /// The && Operator AmpAmp, + /// The || Operator PipePipe, + /// The ++ Operator PlusPlus, + /// The -- Operator MinusMinus, + /// The , Operator Comma, + /// The ->* Operator ArrowStar, + /// The -> Operator Arrow, + /// The () Operator Call, + /// The [] Operator Subscript, + /// The `? :` Operator Conditional, + /// The `coawait` Operator Coawait, }; @@ -196,8 +249,11 @@ isBinaryOperator(OperatorKind kind) noexcept; */ enum class ReferenceKind { + /// Not a reference None = 0, + /// An L-Value reference LValue, + /// An R-Value reference RValue }; @@ -209,14 +265,17 @@ enum class ReferenceKind */ enum class StorageClassKind { + /// No storage class specifier None = 0, + /// thread_local storage-class-specifier Extern, + /// mutable storage-class-specifier Static, - // auto storage-class-specifier (removed in C++11) - // only valid for variables + /// auto storage-class-specifier (removed in C++11) + /// only valid for variables Auto, - // register storage-class-specifier (removed in C++17) - // only valid for variables + /// register storage-class-specifier (removed in C++17) + /// only valid for variables Register }; @@ -229,11 +288,15 @@ MRDOCS_DECL dom::String toString(StorageClassKind kind) noexcept; /** Convert NoexceptInfo to a string. + @param info The noexcept-specifier information. + @param resolved If true, the operand is not shown when the exception specification is non-dependent. @param implicit If true, implicit exception specifications are shown. + + @return The string representation of the noexcept-specifier. */ MRDOCS_DECL dom::String @@ -258,6 +321,7 @@ tag_invoke( @param resolved If true, the operand is not shown when the explicit-specifier is non-dependent. @param implicit If true, implicit explicit-specifiers are shown. + @return The string representation of the explicit-specifier. */ MRDOCS_DECL dom::String @@ -267,6 +331,9 @@ toString( bool implicit = false); /** Return the ExplicitInfo as a @ref dom::Value string. + + @param v The output parameter to receive the dom::Value. + @param I The ExplicitInfo to convert. */ inline void diff --git a/include/mrdocs/Metadata/SymbolID.hpp b/include/mrdocs/Metadata/SymbolID.hpp index 5d71f84b9..9f28a188c 100644 --- a/include/mrdocs/Metadata/SymbolID.hpp +++ b/include/mrdocs/Metadata/SymbolID.hpp @@ -68,6 +68,9 @@ class SymbolID } /** Construct a SymbolID by hashing a string + + @param input The string to hash. + @return The SymbolID created by hashing the string. */ static SymbolID @@ -149,6 +152,9 @@ constexpr inline SymbolID SymbolID::global = "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"; /** Convert a SymbolID to a string + + @param id The SymbolID to convert. + @return The base16 string representation of the SymbolID. */ MRDOCS_DECL std::string @@ -162,6 +168,10 @@ toBase16Str(SymbolID const& id); the strings compare equal and then they are compared with lowercase letters coming before uppercase letters. + + @param symbolName0 The first symbol name to compare. + @param symbolName1 The second symbol name to compare. + @return The result of the comparison. */ MRDOCS_DECL std::strong_ordering @@ -189,6 +199,12 @@ tag_invoke( DomCorpus const* domCorpus); /** Convert SymbolID pointers to dom::Value or null. + + @param v The output parameter to receive the dom::Value. + @param t The SymbolID pointer to convert. If null, the + dom::Value is set to null. + @param domCorpus The DomCorpus to use, or nullptr. If null, + the SymbolID is converted to a base16 string. */ MRDOCS_DECL void diff --git a/include/mrdocs/Metadata/Template.hpp b/include/mrdocs/Metadata/Template.hpp index 377170cab..bc2836bec 100644 --- a/include/mrdocs/Metadata/Template.hpp +++ b/include/mrdocs/Metadata/Template.hpp @@ -24,13 +24,15 @@ namespace clang::mrdocs { +/** The kind of template argument. +*/ enum class TArgKind : int { - // type arguments + /// type arguments Type = 1, // for bitstream - // non-type arguments, i.e. expressions + /// non-type arguments, i.e. expressions NonType, - // template template arguments, i.e. template names + /// template template arguments, i.e. template names Template }; @@ -187,11 +189,11 @@ tag_invoke( enum class TParamKind : int { - // template type parameter, e.g. "typename T" or "class T" + /// Template type parameter, e.g. "typename T" or "class T" Type = 1, // for bitstream - // template non-type parameter, e.g. "int N" or "auto N" + /// Template non-type parameter, e.g. "int N" or "auto N" NonType, - // Template-template parameter, e.g. "template typename T" + /// Template-template parameter, e.g. "template typename T" Template }; @@ -274,7 +276,9 @@ struct TParamCommonBase : TParam /** The keyword a template parameter was declared with */ enum class TParamKeyKind : int { + /// Class keyword Class = 0, + /// Typename keyword Typename }; @@ -365,10 +369,15 @@ operator==(Polymorphic const& lhs, Polymorphic const& rhs) { // ---------------------------------------------------------------- +/** The kind of template or specialization. +*/ enum class TemplateSpecKind { + /// Primary template Primary, + /// Full template specialization Explicit, + /// Partial template specialization Partial }; diff --git a/include/mrdocs/Metadata/Type.hpp b/include/mrdocs/Metadata/Type.hpp index 702945678..4a855513e 100644 --- a/include/mrdocs/Metadata/Type.hpp +++ b/include/mrdocs/Metadata/Type.hpp @@ -32,10 +32,15 @@ operator<=>(Polymorphic const& lhs, Polymorphic const& rhs); std::strong_ordering operator<=>(Polymorphic const& lhs, Polymorphic const& rhs); +/** Type qualifiers +*/ enum QualifierKind { + /// No qualifiers None, + /// The const qualifier Const, + /// The volatile qualifier Volatile }; @@ -53,15 +58,24 @@ tag_invoke( enum class TypeKind { + /// A Named type Named = 1, // for bitstream + /// A decltype type Decltype, + /// An auto type Auto, + /// An LValueReference type LValueReference, + /// An RValueReference type RValueReference, + /// A Pointer type Pointer, + /// A MemberPointer type MemberPointer, + /// An Array type Array, - Function + /// A Function type + Function, }; MRDOCS_DECL dom::String toString(TypeKind kind) noexcept; @@ -76,9 +90,15 @@ tag_invoke( v = toString(kind); } +/** The kind of `auto` keyword used in a declaration. + + This is either `auto` or `decltype(auto)`. +*/ enum class AutoKind { + /// The `auto` keyword Auto, + /// The `decltype(auto)` keyword DecltypeAuto }; @@ -216,47 +236,47 @@ struct TypeInfoCommonBase : TypeInfo */ enum class FundamentalTypeKind { - // void + /// void Void, - // std::nullptr_t + /// std::nullptr_t Nullptr, - // bool + /// bool Bool, - // char + /// char Char, - // signed char + /// signed char SignedChar, - // unsigned char + /// unsigned char UnsignedChar, - // char8_t + /// char8_t Char8, - // char16_t + /// char16_t Char16, - // char32_t + /// char32_t Char32, - // wchar_t + /// wchar_t WChar, - // short / short int / signed short / signed short int + /// short / short int / signed short / signed short int Short, - // unsigned short / unsigned short int + /// unsigned short / unsigned short int UnsignedShort, - // int / signed / signed int + /// int / signed / signed int Int, - // unsigned / unsigned int + /// unsigned / unsigned int UnsignedInt, - // long / long int / signed long / signed long int + /// long / long int / signed long / signed long int Long, - // unsigned long / unsigned long int + /// unsigned long / unsigned long int UnsignedLong, - // long long / long long int / signed long long / signed long long int + /// long long / long long int / signed long long / signed long long int LongLong, - // unsigned long long / unsigned long long int + /// unsigned long long / unsigned long long int UnsignedLongLong, - // float + /// float Float, - // double + /// double Double, - // long double + /// long double LongDouble }; @@ -280,6 +300,9 @@ toString(FundamentalTypeKind kind) noexcept; However, the "long long" specifier cannot be split into two separate specifiers. + @param str The string to convert + @param[out] kind The resulting FundamentalTypeKind + @return true if the string was successfully converted */ MRDOCS_DECL diff --git a/include/mrdocs/Support/Concepts.hpp b/include/mrdocs/Support/Concepts.hpp index 61f976d9a..71c377bc0 100644 --- a/include/mrdocs/Support/Concepts.hpp +++ b/include/mrdocs/Support/Concepts.hpp @@ -54,6 +54,87 @@ concept dereferenceable = requires(T const& t) { *t }; }; +namespace detail { +// ---------- step 1: detect if std::tuple_size is specialized (SFINAE-safe) + +template +struct has_tuple_size : std::false_type {}; + +// This partial specialization is considered only if the operand inside void_t +// is well-formed; otherwise, substitution fails quietly (no hard error). +template +struct has_tuple_size< + U, + std::void_t>::value)>> + : std::true_type {}; + +// ---------- step 2: if tuple_size exists, verify all tuple_element exist +template +struct all_tuple_elements : std::false_type {}; + +// Enabled only when tuple_size is known to exist (avoids hard errors) +template +struct all_tuple_elements::value>> { + using T = std::remove_cvref_t; + static constexpr std::size_t N = std::tuple_size::value; + + template + static consteval bool + check(std::index_sequence) + { + return (requires { typename std::tuple_element::type; } && ...); + } + + static constexpr bool value = check(std::make_index_sequence{}); +}; +} + +/** Concept to check if a type is tuple-like + + This concept checks if a type has a specialization + of std::tuple_size and std::tuple_element for all + elements in the range [0, N), where N is the value + of std::tuple_size. + + Examples of such types are std::tuple, std::pair, + std::array, and user-defined types that provide + specializations for std::tuple_size and std::tuple_element. + */ +template +concept tuple_like = + detail::has_tuple_size::value && + detail::all_tuple_elements::value; + +/** Concept to check if a type is pair-like + + This concept checks if a type is tuple-like + and has exactly two elements. + + Examples of such types are std::pair, + std::array with two elements, and user-defined + types that provide specializations for + std::tuple_size and std::tuple_element with + exactly two elements. + */ +template +concept pair_like = + tuple_like && + (std::tuple_size>::value == 2); + +/** Concept to check if a range is a range of tuple-like elements + + This concept checks if a type is a range + and all its elements are tuple-like. + + Examples of such types are std::vector>, + std::list>, and user-defined types + that provide specializations for std::tuple_size + and std::tuple_element for their element type. + */ +template +concept range_of_tuple_like = + std::ranges::range && tuple_like>; + } // namespace clang::mrdocs diff --git a/include/mrdocs/Support/Error.hpp b/include/mrdocs/Support/Error.hpp index dc24e5110..1cffb2768 100644 --- a/include/mrdocs/Support/Error.hpp +++ b/include/mrdocs/Support/Error.hpp @@ -90,6 +90,8 @@ class MRDOCS_DECL /** Constructor. @param ec The error code. + @param loc The source location where + the error occurred. */ explicit Error( @@ -112,6 +114,10 @@ class MRDOCS_DECL or if all the errors in the list indicate success, then newly constructed object will indicate success. + + @param errors The list of errors to combine. + @param loc The source location where + the error occurred. */ Error( std::vector const& errors, diff --git a/include/mrdocs/Support/ExecutorGroup.hpp b/include/mrdocs/Support/ExecutorGroup.hpp index a4f3f435d..0e3032693 100644 --- a/include/mrdocs/Support/ExecutorGroup.hpp +++ b/include/mrdocs/Support/ExecutorGroup.hpp @@ -98,6 +98,9 @@ class ExecutorGroup : public ExecutorGroupBase The behavior is undefined if there is any outstanding work or busy threads. + + @param args Zero or more arguments + to forward to the agent constructor. */ template void @@ -115,6 +118,10 @@ class ExecutorGroup : public ExecutorGroupBase @code void( Agent&, Args... ); @endcode + + @param f The function to invoke. + @param args Zero or more arguments to + forward to the function. */ template void diff --git a/include/mrdocs/Support/Glob.hpp b/include/mrdocs/Support/Glob.hpp index dc222f33e..2b02b71a1 100644 --- a/include/mrdocs/Support/Glob.hpp +++ b/include/mrdocs/Support/Glob.hpp @@ -38,6 +38,10 @@ class GlobPattern { /** Constructs a GlobPattern with the given pattern. @param pattern The glob pattern to use for matching. + @param maxSubGlobs The maximum number of subpatterns allowed. + If the pattern contains more subpatterns than this value, + an error is returned. If not specified, there is no limit. + @return The constructed GlobPattern, or an error if the pattern is invalid. */ static Expected @@ -76,6 +80,7 @@ class GlobPattern { /** Matches the given string against the glob pattern. @param str The string to match against the pattern. + @param delimiter The character that `*` does not match. @return true if the string matches the pattern, false otherwise. */ bool @@ -92,6 +97,7 @@ class GlobPattern { function returns true. @param prefix The string to match against the pattern. + @param delimiter The character that `*` does not match. @return true if the string prefix matches the pattern, false otherwise. */ bool diff --git a/include/mrdocs/Support/Handlebars.hpp b/include/mrdocs/Support/Handlebars.hpp index 784c9bd6a..250bfce04 100644 --- a/include/mrdocs/Support/Handlebars.hpp +++ b/include/mrdocs/Support/Handlebars.hpp @@ -174,6 +174,7 @@ class MRDOCS_DECL OutputRef /** Write to output + @param os The output stream reference @param sv The string to write @return A reference to this object */ @@ -186,6 +187,7 @@ class MRDOCS_DECL OutputRef /** Write to output + @param os The output stream reference @param c The character to write @return A reference to this object */ @@ -198,7 +200,8 @@ class MRDOCS_DECL OutputRef /** Write to output - @param c The character to write + @param os The output stream reference + @param c The string to write @return A reference to this object */ friend @@ -210,7 +213,8 @@ class MRDOCS_DECL OutputRef /** Write to output - @param c The character to write + @param os The output stream reference + @param v The character to write @return A reference to this object */ template @@ -1226,8 +1230,11 @@ void registerTypeHelpers(Handlebars& hbs); /** "and" helper function - * - * The "and" helper returns true if all of the values are truthy. + + The "and" helper returns true if all of the values are truthy. + + @param args The values to test + @return True if all of the values are truthy, false otherwise. */ MRDOCS_DECL bool @@ -1242,24 +1249,32 @@ dom::Value or_fn(dom::Array const& args); /** "eq" helper function - * - * The "eq" helper returns true if all of the values are equal. + + The "eq" helper returns true if all of the values are equal. + + @param args The values to compare + @return True if all of the values are equal, false otherwise. */ MRDOCS_DECL bool eq_fn(dom::Array const& args); /** "ne" helper function - * - * The "ne" helper returns true if any of the values are not equal. + + The "ne" helper returns true if any of the values are not equal. + + @param args The values to compare + @return True if any of the values are not equal, false otherwise. */ MRDOCS_DECL bool ne_fn(dom::Array const& args); /** "not" helper function - * - * The "not" helper returns true if not all of the values are truthy. + + The "not" helper returns true if not all of the values are truthy. + + @return True if not all of the values are truthy, false otherwise. */ MRDOCS_DECL bool @@ -1304,8 +1319,10 @@ dom::Value relativize_fn(dom::Value to, dom::Value from, dom::Value context); /** "year" helper function - * - * The "year" helper returns the current year as an integer. + + The "year" helper returns the current year as an integer. + + @return The current year as an integer. */ MRDOCS_DECL int diff --git a/include/mrdocs/Support/JavaScript.hpp b/include/mrdocs/Support/JavaScript.hpp index 27d6ae6e6..b2bc036e4 100644 --- a/include/mrdocs/Support/JavaScript.hpp +++ b/include/mrdocs/Support/JavaScript.hpp @@ -41,13 +41,21 @@ class Value; */ enum class Type { + /// The value is undefined undefined = 1, + /// The value is null null, + /// The value is a boolean boolean, + /// The value is a number number, + /// The value is a string string, + /// The value is a function object, + /// The value is an array function, + /// The value is an array array }; @@ -218,24 +226,38 @@ class Scope ~Scope(); /** Push an integer to the stack + + @param value The integer value to push. + @return A Value representing the integer. */ MRDOCS_DECL Value pushInteger(std::int64_t value); /** Push a double to the stack + + @param value The double value to push. + @return A Value representing the double. */ MRDOCS_DECL Value pushDouble(double value); /** Push a boolean to the stack + + @param value The boolean value to push. + @return A Value representing the boolean. */ MRDOCS_DECL Value pushBoolean(bool value); /** Push a string to the stack + + @param value The string value to push. + The string is copied to the internal + heap. + @return A Value representing the string. */ MRDOCS_DECL Value @@ -302,6 +324,10 @@ class Scope The script returns an implicit return value equivalent to the last non-empty statement value in the code. + + @param jsCode The JavaScript code to compile. + @return A function object that can be called. + The function object has zero arguments. */ MRDOCS_DECL Expected @@ -319,6 +345,12 @@ class Scope If the function code contains more than one function, the return value is the first function compiled. + @param jsCode The JavaScript code to compile. + @return A function object that can be called. + The function object has the number of arguments + defined in the code. If the code does not define + a function, an error is returned. + */ MRDOCS_DECL Expected @@ -341,6 +373,9 @@ class Scope getGlobal(std::string_view name); /** Set a global object. + + @param name The name of the global variable. + @param value The value to set. */ MRDOCS_DECL void @@ -411,7 +446,7 @@ class MRDOCS_DECL Value there are no other Value references to the @ref Scope, all variables defined in that scope are popped - via @ref Scope::reset. + via `Scope::reset`. */ MRDOCS_DECL ~Value(); @@ -478,15 +513,24 @@ class MRDOCS_DECL Value */ MRDOCS_DECL Type type() const noexcept; - /// Check if the value is undefined. + /** Check if the value is undefined. + + @return `true` if the value is undefined, `false` otherwise + */ bool isUndefined() const noexcept; - /// Check if the value is null. + /** Check if the value is null. + + @return `true` if the value is null, `false` otherwise + */ bool isNull() const noexcept; - /// Check if the value is a boolean. + /** Check if the value is a boolean. + + @return `true` if the value is a boolean, `false` otherwise + */ bool isBoolean() const noexcept; @@ -503,6 +547,7 @@ class MRDOCS_DECL Value The user should not rely on NaNs preserving their exact non-normalized form. + @return `true` if the value is a number, `false` otherwise */ bool isNumber() const noexcept; @@ -524,19 +569,29 @@ class MRDOCS_DECL Value `d == static_cast(static_cast(d))` where `d` is the result of `toDouble()`. + @return `true` if the value is a number with no fractional part, `false` otherwise */ bool isInteger() const noexcept; - /// Check if the value is a floating point number. + /** Check if the value is a floating point number. + + @return `true` if the value is a number but not an integer, `false` otherwise + */ bool isDouble() const noexcept; - /// Check if the value is a string. + /** Check if the value is a string. + + @return `true` if the value is a string, `false` otherwise + */ bool isString() const noexcept; - /// Check if the value is an array. + /** Check if the value is an array. + + @return `true` if the value is an array, `false` otherwise + */ bool isArray() const noexcept; @@ -553,11 +608,16 @@ class MRDOCS_DECL Value a string key and an arbitrary value, including undefined. + @return `true` if the value is an object, `false` otherwise + */ bool isObject() const noexcept; - /// Check if the value is a function. + /** Check if the value is a function. + + @return `true` if the value is a function, `false` otherwise + */ bool isFunction() const noexcept; @@ -668,8 +728,13 @@ class MRDOCS_DECL Value /** Return the element for a given key. If the Value is not an object, or the key - is not found, a Value of type @ref Kind::Undefined + is not found, a Value of type Kind::Undefined is returned. + + @param key The key to look up. + @return The element for the given key, or + a Value of type Kind::Undefined if + the key is not found. */ Value get(std::string_view key) const; @@ -682,6 +747,11 @@ class MRDOCS_DECL Value } /** Return the element at a given index. + + @param i The index of the element to return. + @return The element at the given index, or + a Value of type `Kind::Undefined` if + the index is out of range. */ Value get(std::size_t i) const; @@ -697,14 +767,19 @@ class MRDOCS_DECL Value multiple times, once for each key in the sequence of dot-separated keys. + @param keys A sequence of keys separated by dots. + @return The value at the end of the sequence, or - a Value of type @ref Kind::Undefined if any key + a Value of type Kind::Undefined if any key is not found. */ Value lookup(std::string_view keys) const; /** Set or replace the value for a given key. + + @param key The key to set. + @param value The value to set. */ MRDOCS_DECL void @@ -713,6 +788,9 @@ class MRDOCS_DECL Value Value const& value) const; /** Set or replace the value for a given key. + + @param key The key to set. + @param value The value to set. */ MRDOCS_DECL void @@ -721,6 +799,9 @@ class MRDOCS_DECL Value dom::Value const& value) const; /** Return true if a key exists. + + @param key The key to check for. + @return `true` if the key exists, `false` otherwise. */ bool exists(std::string_view key) const; @@ -736,6 +817,9 @@ class MRDOCS_DECL Value size() const; /** Invoke a function. + + @param args Zero or more arguments to pass to the method. + @return The return value of the method. */ template... Args> Expected @@ -745,6 +829,9 @@ class MRDOCS_DECL Value } /** Invoke a function with variadic arguments. + + @param args Zero or more arguments to pass to the method. + @return The return value of the method. */ Expected apply(std::span args) const @@ -753,6 +840,9 @@ class MRDOCS_DECL Value } /** Invoke a function. + + @param args Zero or more arguments to pass to the method. + @return The return value of the method. */ template Value @@ -762,6 +852,10 @@ class MRDOCS_DECL Value } /** Invoke a method. + + @param prop The property name of the method to call. + @param args Zero or more arguments to pass to the method. + @return The return value of the method. */ template Expected @@ -1017,6 +1111,11 @@ isFunction() const noexcept This function registers a JavaScript function as a helper function that can be called from Handlebars templates. + + @param hbs The Handlebars instance to register the helper into + @param name The name of the helper function + @param ctx The JavaScript context to use + @param script The JavaScript code that defines the helper function */ MRDOCS_DECL Expected diff --git a/include/mrdocs/Support/Lua.hpp b/include/mrdocs/Support/Lua.hpp index 89bca4049..d8587335a 100644 --- a/include/mrdocs/Support/Lua.hpp +++ b/include/mrdocs/Support/Lua.hpp @@ -119,6 +119,11 @@ class Scope ~Scope(); /** Load a Lua chunk + + @param luaChunk The Lua chunk to load. + @param chunkName The name of the chunk (used in error messages). + @param loc The source location of the call site. + @return The function if successful, or an error. */ MRDOCS_DECL Expected @@ -129,6 +134,10 @@ class Scope source_location::current()); /** Load a Lua chunk + + @param luaChunk The Lua chunk to load. + @param loc The source location of the call site. + @return The function if successful, or an error. */ MRDOCS_DECL Expected @@ -138,6 +147,10 @@ class Scope source_location::current()); /** Run a Lua chunk. + + @param fileName The name of the file to load. + @param loc The source location of the call site. + @return The function if successful, or an error. */ MRDOCS_DECL Expected @@ -153,6 +166,10 @@ class Scope getGlobalTable(); /** Return a value from the global table if it exists. + + @param key The key to get. + @param loc The source location of the call site. + @return The value if it exists, or an error. */ MRDOCS_DECL Expected @@ -246,11 +263,17 @@ class MRDOCS_DECL */ enum class Type { + /// The value is nil nil = 0, + /// The value is a boolean boolean = 1, + /// The value is a number number = 3, + /// The value is a string string = 4, + /// The value is a table table = 5, + /// The value is a function function = 6 }; @@ -322,8 +345,9 @@ class MRDOCS_DECL If the invocation fails the return value will contain the corresponding error. - @param args... Zero or more values to pass + @param args Zero or more values to pass to the function. + @return The return value of the function. */ template Expected @@ -339,6 +363,9 @@ class MRDOCS_DECL } /** Invoke the value as a function. + + @param args Zero or more values to pass + to the function. */ template Value operator()(Args&&... args) @@ -455,6 +482,9 @@ class Table : public Value std::string_view key) const; /** Create or replace the value with a key. + + @param key The key to set. + @param value The value to set. */ MRDOCS_DECL void diff --git a/include/mrdocs/Support/Path.hpp b/include/mrdocs/Support/Path.hpp index 529a4938c..89773f094 100644 --- a/include/mrdocs/Support/Path.hpp +++ b/include/mrdocs/Support/Path.hpp @@ -38,8 +38,11 @@ struct MRDOCS_VISIBLE a directory and invoke the visitor with the path. + @param dirPath The path to the directory. @param recursive If true, files in subdirectories are also visited, recursively. + @param visitor The visitor to invoke for each file. + @return An error if any occurred. */ MRDOCS_DECL Expected @@ -79,6 +82,14 @@ class FileVisitor : public AnyFileVisitor } /** Visit each file in a directory. + + @param dirPath The path to the directory. + @param recursive If true, files in subdirectories are + also visited, recursively. + @param visitor A callable object which is invoked + for each file. + @return An error if any occurred. + */ template Expected @@ -96,11 +107,17 @@ forEachFile( namespace files { +/** The type of a file. +*/ enum class FileType { + /// The file does not exist not_found, + /// The path represents a regular file regular, + /// The file is a directory directory, + /// The file is something else other }; @@ -115,6 +132,11 @@ getFileType( std::string_view pathName); /** Return true if pathName is absolute. + + @param pathName The absolute or relative path + to the directory or file. + @return true if the path is absolute, + false otherwise. */ MRDOCS_DECL bool @@ -122,6 +144,10 @@ isAbsolute( std::string_view pathName) noexcept; /** Return an error if pathName is not absolute. + + @param pathName The absolute or relative path + to the directory or file. + @return An error if the path is not absolute. */ MRDOCS_DECL Expected @@ -129,6 +155,11 @@ requireAbsolute( std::string_view pathName); /** Return true if pathName ends in a separator. + + @param pathName The absolute or relative path + to the directory or file. + @return true if the path ends in a separator, + false otherwise. */ MRDOCS_DECL bool @@ -175,6 +206,11 @@ normalizeDir( If the parent directory is defined, the returned path will always have a trailing separator. + + @param pathName The absolute or relative path + to the directory or file. + @return The parent directory, or the empty + string if there is none. */ MRDOCS_DECL std::string @@ -185,6 +221,15 @@ getParentDir( If the parent directory is defined, the returned path will always have a trailing separator. + + @param pathName The absolute or relative path + to the directory or file. + @param levels The number of levels to go up. + If this is zero, the original path is returned. + If this is greater than the number of levels + in the path, the empty string is returned. + @return The parent directory, or the empty + string if there is none. */ MRDOCS_DECL std::string @@ -193,6 +238,11 @@ getParentDir( unsigned levels); /** Return the filename part of the path. + + @param pathName The absolute or relative path + to the directory or file. + @return The filename part of the path, + or the empty string if there is none. */ MRDOCS_DECL std::string_view @@ -200,6 +250,11 @@ getFileName( std::string_view pathName); /** Return the contents of a file as a string. + + @param pathName The absolute or relative path + to the file. + @return The contents of the file, or an error + if any occurred. */ MRDOCS_DECL Expected @@ -207,6 +262,11 @@ getFileText( std::string_view pathName); /** Append a trailing native separator if not already present. + + @param pathName The absolute or relative path + to the directory or file. + @return A copy of the path with a trailing + separator if not already present. */ MRDOCS_DECL std::string @@ -218,6 +278,8 @@ makeDirsy( Relative paths are resolved against the current working directory of the process. + @param pathName The absolute or relative path + to the directory or file. @return The absolute path, or an error if any occurred. */ @@ -227,6 +289,13 @@ makeAbsolute( std::string_view pathName); /** Return an absolute path from a possibly relative path. + + @param pathName The absolute or relative path + to the directory or file. + @param workingDir The working directory to + resolve relative paths against. + @return The absolute path, or an error if + any occurred. */ MRDOCS_DECL std::string @@ -235,6 +304,11 @@ makeAbsolute( std::string_view workingDir); /** Convert all backward slashes to forward slashes. + + @param pathName The absolute or relative path + to the directory or file. + @return A copy of the path with all + backslashes replaced with forward slashes. */ MRDOCS_DECL std::string @@ -242,6 +316,11 @@ makePosixStyle( std::string_view pathName); /** Check if the path is posix style. + + @param pathName The absolute or relative path + to the directory or file. + @return true if the path uses only forward slashes + as path separators, false otherwise. */ MRDOCS_DECL bool @@ -294,6 +373,10 @@ appendPath( std::string_view name4); /** Return an error if the path is not a directory. + + @param pathName The absolute or relative path + @return An error if the path does not exist + or is not a directory. */ MRDOCS_DECL Expected @@ -301,6 +384,10 @@ requireDirectory( std::string_view pathName); /** Determine if a path is a directory. + + @param pathName The absolute or relative path + @return true if the path exists and is a directory, + false otherwise. */ MRDOCS_DECL bool @@ -316,21 +403,34 @@ isDirectory( a directory. In this case, the function returns true if the last path segment contains a period, otherwise false. + + @param pathName The absolute or relative path + @return true if the path exists and is a directory, + or if the path does not exist and the last path + segment does not contain a period. + false otherwise. */ MRDOCS_DECL bool isLexicalDirectory( std::string_view pathName); -/** Determine if a path is a directory. +/** Determine if a path exists + + @param pathName The absolute or relative path + @return true if the path exists, false otherwise. */ MRDOCS_DECL - bool exists( std::string_view pathName); /** Return the relevant suffix of a source file path. + + @param pathName The absolute or relative path + to the file. + @return The suffix, including the leading dot, + or the empty string if there is no suffix. */ MRDOCS_DECL std::string_view diff --git a/include/mrdocs/Support/Report.hpp b/include/mrdocs/Support/Report.hpp index 050b1ae1f..022686480 100644 --- a/include/mrdocs/Support/Report.hpp +++ b/include/mrdocs/Support/Report.hpp @@ -32,11 +32,17 @@ namespace clang::mrdocs::report { */ enum class Level { + /// Programming trace messages trace = 0, + /// Debug messages debug, + /// Informational messages info, + /// Warning messages warn, + /// Error messages error, + /// Fatal error messages fatal }; @@ -76,6 +82,10 @@ getMinimumLevel() noexcept; /** If true, source location information will be printed with warnings, errors, and fatal messages. + + @param b true to enable source location + information, false to disable it. The default + value is true. */ MRDOCS_DECL void @@ -186,7 +196,7 @@ log_impl( @param fs The format string. - @param args... Optional additional arguments + @param args Optional additional arguments used to format a message to print. A trailing newline will be added to the message automatically. @@ -205,7 +215,11 @@ log( } /** Report a message to the console. -*/ + + @param format The format string. + @param args Optional additional arguments + + */ template void trace( @@ -216,7 +230,11 @@ trace( } /** Report a message to the console. -*/ + + @param format The format string. + @param args Optional additional arguments + + */ template void debug( @@ -227,7 +245,11 @@ debug( } /** Report a message to the console. -*/ + + @param format The format string. + @param args Optional additional arguments + + */ template void info( @@ -238,7 +260,11 @@ info( } /** Report a message to the console. -*/ + + @param format The format string. + @param args Optional additional arguments + + */ template void warn( @@ -249,7 +275,11 @@ warn( } /** Report a message to the console. -*/ + + @param format The format string. + @param args Optional additional arguments + + */ template void error( @@ -260,6 +290,9 @@ error( } /** Report a message to the console. + + @param format The format string. + @param args Optional additional arguments */ template void diff --git a/include/mrdocs/Support/String.hpp b/include/mrdocs/Support/String.hpp index 2fc859f26..c43e9b5dd 100644 --- a/include/mrdocs/Support/String.hpp +++ b/include/mrdocs/Support/String.hpp @@ -19,6 +19,10 @@ namespace clang::mrdocs { /** Return the substring without leading specified characters. + + @param s The string to trim. + @param chars The characters to remove. + @return The modified string. */ constexpr std::string_view @@ -30,6 +34,9 @@ ltrim( } /** Return the substring without leading horizontal whitespace. + + @param s The string to trim. + @return The modified string. */ constexpr std::string_view @@ -40,6 +47,10 @@ ltrim( } /** Return the substring without trailing specified characters. + + @param s The string to trim. + @param chars The characters to remove. + @return The modified string. */ constexpr std::string_view @@ -56,6 +67,9 @@ rtrim( } /** Return the substring without trailing horizontal whitespace. + + @param s The string to trim. + @return The modified string. */ constexpr std::string_view @@ -65,6 +79,9 @@ rtrim(std::string_view const s) noexcept } /** Return the substring without leading and trailing horizontal whitespace. + + @param s The string to trim. + @return The modified string. */ constexpr std::string_view @@ -74,6 +91,10 @@ trim(std::string_view const s) noexcept } /** Return the substring without leading and trailing specified characters. + + @param s The string to trim. + @param chars The characters to remove. + @return The modified string. */ constexpr std::string_view @@ -85,12 +106,23 @@ trim( } /** Return the substring without leading and trailing horizontal whitespace. + + @param s The string to trim. + @param from The substring to remove. + @param to The substring to replace with. + If this is empty, the substring is removed. + @return The modified string. */ MRDOCS_DECL void replace(std::string& s, std::string_view from, std::string_view to); /** Determine if a string is only whitespace. + + @param s The string to check. + @return true if the string is empty or contains only + whitespace characters (space, tab, newline, vertical tab, + form feed, carriage return). false otherwise. */ constexpr bool @@ -100,6 +132,9 @@ isWhitespace(std::string_view s) noexcept } /** Determine if a string starts with one of the specified characters + + @param s The string to check. + @param chars The characters to check for. */ constexpr bool @@ -109,6 +144,9 @@ startsWithOneOf(std::string_view s, std::string_view chars) noexcept } /** Determine if a string ends with one of the specified characters + + @param s The string to check. + @param chars The characters to check for. */ constexpr bool diff --git a/include/mrdocs/Support/ThreadPool.hpp b/include/mrdocs/Support/ThreadPool.hpp index 31b5c3f44..f679be9e7 100644 --- a/include/mrdocs/Support/ThreadPool.hpp +++ b/include/mrdocs/Support/ThreadPool.hpp @@ -80,6 +80,8 @@ class MRDOCS_VISIBLE The signature of the submitted function object should be `void(void)`. + + @param f The function object to execute. */ template void @@ -90,8 +92,12 @@ class MRDOCS_VISIBLE /** Invoke a function object for each element of a range. + @param range The range of elements to process. + @param f The function object to invoke. + @return Zero or more errors which were thrown from submitted work. + */ template [[nodiscard]] @@ -136,6 +142,8 @@ class MRDOCS_VISIBLE The signature of the submitted function object should be `void(void)`. + + @param f The function object to execute. */ template void diff --git a/include/mrdocs/Support/TypeTraits.hpp b/include/mrdocs/Support/TypeTraits.hpp index 493282be6..937508189 100644 --- a/include/mrdocs/Support/TypeTraits.hpp +++ b/include/mrdocs/Support/TypeTraits.hpp @@ -18,6 +18,8 @@ namespace clang { namespace mrdocs { /** Return the value as its underlying type. + + @param value The enum value to convert */ template requires std::is_enum_v diff --git a/include/mrdocs/Support/Visitor.hpp b/include/mrdocs/Support/Visitor.hpp index 6e84599ad..959300298 100644 --- a/include/mrdocs/Support/Visitor.hpp +++ b/include/mrdocs/Support/Visitor.hpp @@ -98,6 +98,11 @@ class Visitor function `fn` with the derived type as the first argument, followed by `args`. + @param obj The object to visit + @param fn The function object to call + @param args The arguments to pass to the function object + @return The common return type of `fn` when called + with a derived type of `obj` and `args` */ template< typename BaseTy, diff --git a/src/lib/AST/ASTAction.cpp b/src/lib/AST/ASTAction.cpp index 76e884ffe..c8468014f 100644 --- a/src/lib/AST/ASTAction.cpp +++ b/src/lib/AST/ASTAction.cpp @@ -12,9 +12,11 @@ // #include "lib/AST/ASTAction.hpp" +#include "lib/AST/MrDocsFileSystem.hpp" #include "lib/AST/ASTVisitorConsumer.hpp" #include #include +#include namespace clang { namespace mrdocs { @@ -32,7 +34,49 @@ ExecuteAction() // Ensure comments in system headers are retained. // We may want them if, e.g., a declaration was extracted // as a dependency - CI.getLangOpts().RetainCommentsFromSystemHeaders = true; + auto &Lang = CI.getLangOpts(); + Lang.RetainCommentsFromSystemHeaders = true; + bool const buildShims = + !config_.settings()->missingIncludePrefixes.empty() || + !config_.settings()->missingIncludeShims.empty(); + if (buildShims && missingSink_) + { + // Install missing symbol sink + missingSink_->setStartParsing(); + auto &DE = CI.getDiagnostics(); + std::unique_ptr prev = DE.takeClient(); + DE.setClient( + new CollectingDiagConsumer(*missingSink_, std::move(prev), DE), + /*ShouldOwnClient*/ true); + + // Turn on AST recovery: + // Enable Clang’s recovery flags so it still builds + // decls/exprs with placeholder types when something is broken. + Lang.RecoveryAST = 1; // keep building AST nodes on errors + Lang.RecoveryASTType = 1; // synthesize placeholder types + + // Mark stubbed prefixes as “system” for quieter diagnostics + for (auto &prefix : config_.settings()->missingIncludePrefixes) + { + MRDOCS_CHECK_OR_CONTINUE(!prefix.empty()); + if (prefix.back() == '/') + { + CI.getHeaderSearchOpts().AddSystemHeaderPrefix(prefix, /*IsSystemHeader=*/true); + } + else + { + std::string p = prefix; + p += '/'; + CI.getHeaderSearchOpts().AddSystemHeaderPrefix(p, /*IsSystemHeader=*/true); + } + } + } + + // Skip function bodies: + // We don’t need bodies to enumerate symbols. This eliminates + // a ton of dependent code and template instantiations. + auto &FrontendOpts = CI.getFrontendOpts(); + FrontendOpts.SkipFunctionBodies = true; if (!CI.hasSema()) { diff --git a/src/lib/AST/ASTAction.hpp b/src/lib/AST/ASTAction.hpp index cf7056172..b06154924 100644 --- a/src/lib/AST/ASTAction.hpp +++ b/src/lib/AST/ASTAction.hpp @@ -18,6 +18,8 @@ #include "lib/Support/ExecutionContext.hpp" #include #include +#include "MissingSymbolSink.hpp" + namespace clang { namespace mrdocs { @@ -41,6 +43,7 @@ class ASTAction { ExecutionContext& ex_; ConfigImpl const& config_; + MissingSymbolSink* missingSink_ = nullptr; public: ASTAction( @@ -84,6 +87,12 @@ class ASTAction CreateASTConsumer( clang::CompilerInstance& Compiler, llvm::StringRef InFile) override; + + void + setMissingSymbolSink(MissingSymbolSink& sink) noexcept + { + missingSink_ = &sink; + } }; } // mrdocs diff --git a/src/lib/AST/ASTVisitor.cpp b/src/lib/AST/ASTVisitor.cpp index 6c9c53ec9..189140aad 100644 --- a/src/lib/AST/ASTVisitor.cpp +++ b/src/lib/AST/ASTVisitor.cpp @@ -583,7 +583,10 @@ populate( unsigned line = source_.getPresumedLoc( loc, false).getLine(); FileInfo* file = findFileInfo(loc); - MRDOCS_ASSERT(file); + + // No file is not an error, it just typically means it has been generated + // in the virtual filesystem. + MRDOCS_CHECK_OR(file); if (definition) { @@ -3220,9 +3223,6 @@ find(Decl const* D) const return find(ID); } - -// KRYSTIAN NOTE: we *really* should not have a -// type named "SourceLocation"... ASTVisitor::FileInfo* ASTVisitor:: findFileInfo(clang::SourceLocation const loc) diff --git a/src/lib/AST/FrontendActionFactory.cpp b/src/lib/AST/FrontendActionFactory.cpp index 4c1a3981f..1956321fe 100644 --- a/src/lib/AST/FrontendActionFactory.cpp +++ b/src/lib/AST/FrontendActionFactory.cpp @@ -17,33 +17,14 @@ namespace clang { namespace mrdocs { -std::unique_ptr -makeFrontendActionFactory( - ExecutionContext& ex, - ConfigImpl const& config) -{ - class ASTActionFactory : - public tooling::FrontendActionFactory - { - ExecutionContext& ex_; - ConfigImpl const& config_; - public: - ASTActionFactory( - ExecutionContext& ex, - ConfigImpl const& config) noexcept - : ex_(ex) - , config_(config) - { - } - - std::unique_ptr - create() override - { - return std::make_unique(ex_, config_); - } - }; - return std::make_unique(ex, config); +std::unique_ptr +ASTActionFactory:: +create() +{ + auto A = std::make_unique(ex_, config_); + A->setMissingSymbolSink(missingSink_); + return A; } } // mrdocs diff --git a/src/lib/AST/FrontendActionFactory.hpp b/src/lib/AST/FrontendActionFactory.hpp index 603227364..1111b5db9 100644 --- a/src/lib/AST/FrontendActionFactory.hpp +++ b/src/lib/AST/FrontendActionFactory.hpp @@ -16,28 +16,32 @@ #include "lib/ConfigImpl.hpp" #include "lib/Support/ExecutionContext.hpp" +#include "lib/AST/MissingSymbolSink.hpp" #include namespace clang { namespace mrdocs { -/** Return a factory of MrDocs actions for the Clang Frontend - - This function returns an implementation of - `clang::tooling::FrontendActionFactory` that allows - one action to be created for each translation unit. - - The `create` method of this factory returns a new instance - of @ref clang::mrdocs::ASTAction for each translation unit. - - A `tooling::ClangTool`, with access to the compilation database, - can receive this factory action via `tooling::ClangTool::run()`. - This is the entry point for the AST traversal in `CorpusImpl::build`. - */ -std::unique_ptr -makeFrontendActionFactory( - ExecutionContext& ex, - ConfigImpl const& config); +class ASTActionFactory : + public tooling::FrontendActionFactory +{ + ExecutionContext& ex_; + ConfigImpl const& config_; + MissingSymbolSink& missingSink_; +public: + ASTActionFactory( + ExecutionContext& ex, + ConfigImpl const& config, + MissingSymbolSink& missingSink) noexcept + : ex_(ex) + , config_(config) + , missingSink_(missingSink) + { + } + + std::unique_ptr + create() override; +}; } // mrdocs } // clang diff --git a/src/lib/AST/MissingSymbolSink.hpp b/src/lib/AST/MissingSymbolSink.hpp new file mode 100644 index 000000000..1fb8319bc --- /dev/null +++ b/src/lib/AST/MissingSymbolSink.hpp @@ -0,0 +1,441 @@ +// +// Licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_LIB_AST_MISSING_SYMBOL_SINK_HPP +#define MRDOCS_LIB_AST_MISSING_SYMBOL_SINK_HPP + +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticSema.h" +#include "clang/Basic/SourceManager.h" +#include "lib/ConfigImpl.hpp" +#include "lib/Support/ExecutionContext.hpp" +#include "lib/Support/Report.hpp" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringSet.h" +#include +#include +#include +#include +#include + +namespace clang { +namespace mrdocs { + +struct FrozenDiag { + clang::DiagnosticsEngine::Level level{}; + unsigned id{}; + std::string msg; + std::string file; + unsigned line = 0, col = 0; + std::string optFlag; +}; + +/* This class stores missing symbols in a TU that missing includes + + When clang encounters an unknown type or namespace, it emits an error. + If the corresponding MrDocs option is enabled, we intercept these + errors, extract the missing symbol names, and store them. + + After parsing the TU, if the missing symbol names are not ambiguous + (i.e., a symbol that could be a namespace or a type), we generate + a shim header that declares these symbols as stubs and reparse the TU + with the shim included. + + This allows us to extract documentation from TUs with dependencies + That wouldn't be reasonable to always include, such as compiling + LLVM in a new CI environment, only to have the header files available + and then extract documentation. + + The process of creating stubs is not perfect. It's based on + heuristics and likely to fail for complex cases. However, + it works well enough for simple cases and is opt-in. For + other cases, the user can provide the shims via the + missing-include-shims option. + */ +class MissingSymbolSink { + // AFREITAS: This should be a tree structure to avoid ambiguities + // such as a symbol being both a type and a namespace. + // Whenever a type is added, we should check what kind of context + // it could be in considering all instances, and if it's ambiguous, + // we should fail. + std::set Types; + std::set Namespaces; + std::vector deferred; + std::size_t prevSize = 0; + mutable std::mutex Mu; + +public: + void + addType(llvm::StringRef s) + { + std::lock_guard L(Mu); + Types.insert(s.str()); + } + void + addNamespace(llvm::StringRef s) + { + std::lock_guard L(Mu); + Namespaces.insert(s.str()); + } + + std::size_t + numTypes() const + { + std::lock_guard L(Mu); + return Types.size(); + } + + std::size_t + numNamespaces() const + { + std::lock_guard L(Mu); + return Namespaces.size(); + } + + std::size_t + numSymbols() const + { + return Types.size() + Namespaces.size(); + } + + void + deferDiagnostic( + clang::DiagnosticsEngine::Level L, + clang::Diagnostic const &Info) + { + llvm::SmallString<256> buf; + Info.FormatDiagnostic(buf); + + std::string file; + unsigned line = 0, col = 0; + if (Info.getLocation().isValid()) + { + auto P = Info.getSourceManager().getPresumedLoc(Info.getLocation()); + if (P.isValid()) + { + file = P.getFilename(); + line = P.getLine(); + col = P.getColumn(); + } + } + StringRef flag = Info.getFlagValue(); + + FrozenDiag d; + d.level = L; + d.id = Info.getID(); + d.msg = std::string(buf); + d.file = std::move(file); + d.line = line; + d.col = col; + if (!flag.empty()) + { + d.optFlag = flag; + } + + std::lock_guard lock(Mu); + deferred.emplace_back(std::move(d)); + } + + std::vector + consumeDeferred() + { + std::lock_guard lock(Mu); + auto out = std::move(deferred); + deferred.clear(); + return out; + } + + void + setStartParsing() + { + std::lock_guard L(Mu); + prevSize = numSymbols(); + } + + bool + symbolsAdded() const + { + std::lock_guard L(Mu); + return numSymbols() > prevSize; + } + + std::string + buildShim() + { + MissingSymbolSink const &S = *this; + std::string H; + // __mrdocs_shims/virtual_diagnostics_shim + H += "#pragma clang system_header\n" + "#ifndef MRDOCS_SHIMS_VIRTUAL_DIAGNOSTICS_DRIVEN_SHIM\n" + "#define MRDOCS_SHIMS_VIRTUAL_DIAGNOSTICS_DRIVEN_SHIM\n" + "#ifdef __cplusplus\n"; + for (auto const &E: S.Namespaces) + { + std::format_to(std::back_inserter(H), "namespace {} {{}}\n", E); + } + for (auto const &E: S.Types) + { + auto qualifiersRange = llvm::split(E, "::"); + auto qualifiers = llvm::to_vector(qualifiersRange); + auto n = qualifiers.size(); + if (n > 0) + { + // All but last are namespaces + for (std::size_t i = 0; i + 1 < n; ++i) + { + std::format_to( + std::back_inserter(H), + "namespace {} {{ ", + qualifiers[i]); + } + // Last is class + std::format_to( + std::back_inserter(H), + "class {}; ", + qualifiers[n - 1]); + // Close namespaces + for (std::size_t i = 0; i + 1 < n; ++i) + { + H += "}"; + } + H += '\n'; + } + } + H += "#endif\n" + "#endif\n"; + return H; + } +}; + +class CollectingDiagConsumer : public clang::DiagnosticConsumer { + MissingSymbolSink &Sink_; + std::unique_ptr Downstream_; + clang::DiagnosticsEngine &DE_; + bool Replaying_ = false; + + static std::string + firstQuoted(llvm::StringRef s) + { + std::size_t l = s.find('\''), + r = (l == llvm::StringRef::npos) ? l : s.find('\'', l + 1); + return (l != llvm::StringRef::npos && r != llvm::StringRef::npos + && r > l + 1) ? + s.substr(l + 1, r - l - 1).str() : + std::string{}; + } + + static std::pair + firstAndSecondQuoted(llvm::StringRef s) + { + std::size_t l1 = s.find('\''); + if (l1 == llvm::StringRef::npos) + { + return { std::string_view{}, std::string_view{} }; + } + std::size_t r1 = s.find('\'', l1 + 1); + if (r1 == llvm::StringRef::npos) + { + return { std::string_view{}, std::string_view{} }; + } + std::size_t l2 = s.find('\'', r1 + 1); + if (l2 == llvm::StringRef::npos) + { + return { s.substr(l1 + 1, r1 - l1 - 1), std::string_view{} }; + } + std::size_t r2 = s.find('\'', l2 + 1); + if (r2 == llvm::StringRef::npos) + { + return { s.substr(l1 + 1, r1 - l1 - 1), std::string_view{} }; + } + return { s.substr(l1 + 1, r1 - l1 - 1), s.substr(l2 + 1, r2 - l2 - 1) }; + } +public: + CollectingDiagConsumer( + MissingSymbolSink &S, + std::unique_ptr Prev, + clang::DiagnosticsEngine &DE) + : Sink_(S) + , Downstream_(std::move(Prev)) + , DE_(DE) + {} + + void + HandleDiagnostic( + clang::DiagnosticsEngine::Level L, + clang::Diagnostic const &Info) override + { + if (L <= clang::DiagnosticsEngine::Warning) + { + return; + } + + if (Replaying_) + { + if (Downstream_) + { + Downstream_->HandleDiagnostic(L, Info); + } + DiagnosticConsumer::HandleDiagnostic(L, Info); + return; // let the engine count this; don't capture again + } + + + switch (Info.getID()) + { + case clang::diag::err_unknown_typename: + case clang::diag::err_incomplete_nested_name_spec: + { + llvm::SmallString<256> Msg; + Info.FormatDiagnostic(Msg); + auto name = firstQuoted(Msg); + if (!name.empty() && name != "std") + { + Sink_.addType(name); + Sink_.deferDiagnostic(L, Info); + return; + } + break; + } + case clang::diag::err_undeclared_use: + case clang::diag::err_undeclared_use_suggest: + case clang::diag::err_undeclared_var_use: + case clang::diag::err_undeclared_var_use_suggest: + { + llvm::SmallString<256> Msg; + Info.FormatDiagnostic(Msg); + auto name = firstQuoted(Msg); + if (!name.empty() && name != "std") + { + Sink_.addNamespace(name); + Sink_.deferDiagnostic(L, Info); + return; + } + break; + } + case clang::diag::err_typename_nested_not_found: + case clang::diag::err_unknown_nested_typename_suggest: + case clang::diag::err_no_member: + case clang::diag::err_no_member_overloaded_arrow: + case clang::diag::err_no_member_suggest: + { + // no type named %0 in %1 + llvm::SmallString<256> Msg; + Info.FormatDiagnostic(Msg); + auto [name, context] = firstAndSecondQuoted(Msg); + if (!context.empty()) + { + Sink_.addNamespace(context); + if (!name.empty() && name != "std") + { + std::string qualified = std::format("{}::{}", context, name); + Sink_.addType(qualified); + Sink_.deferDiagnostic(L, Info); + return; + } + } else + { + if (!name.empty() && name != "std") + { + Sink_.addType(name); + Sink_.deferDiagnostic(L, Info); + return; + } + } + break; + } + default: + break; + } + + llvm::SmallString<256> Msg; + Info.FormatDiagnostic(Msg); + if (Downstream_) + { + Downstream_->HandleDiagnostic(L, Info); + } + DiagnosticConsumer::HandleDiagnostic(L, Info); + } + + void + BeginSourceFile(clang::LangOptions const &LO, clang::Preprocessor const *PP) + override + { + if (Downstream_) + { + Downstream_->BeginSourceFile(LO, PP); + } + } + + void + EndSourceFile() override + { + if (!Downstream_) + { + return; + } + + auto frozen = Sink_.consumeDeferred(); + if (!Sink_.symbolsAdded() && + !frozen.empty()) + { + // we deferred some errors but no new symbols + Replaying_ = true; + unsigned emittedErrors = 0; + + for (auto const &d: frozen) + { + std::string prefix; + if (!d.file.empty()) + { + llvm::raw_string_ostream os(prefix); + os << d.file << ":" << d.line << ":" << d.col << ": "; + } + + // d.level is already Error/Fatal (you said you filtered) + unsigned id = DE_.getCustomDiagID(d.level, "%0%1"); + (DE_.Report(id) << prefix << d.msg); + + if (d.level >= clang::DiagnosticsEngine::Error) + { + ++emittedErrors; + } + } + + // Safety tripwire (optional): ensure a nonzero exit even if frozen + // was empty. + if (frozen.empty() || emittedErrors == 0) + { + unsigned id = DE_.getCustomDiagID( + clang::DiagnosticsEngine::Error, + "errors occurred (deferred)"); + DE_.Report(id); + } + + Replaying_ = false; + } + + Downstream_->EndSourceFile(); + } + + + void + finish() override + { + if (Downstream_) + { + Downstream_->finish(); + } + } +}; + +} // namespace mrdocs +} // namespace clang + +#endif diff --git a/src/lib/AST/MrDocsFileSystem.hpp b/src/lib/AST/MrDocsFileSystem.hpp new file mode 100644 index 000000000..268114507 --- /dev/null +++ b/src/lib/AST/MrDocsFileSystem.hpp @@ -0,0 +1,342 @@ +// +// Licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_LIB_AST_MRDOCS_FILESYSTEM_HPP +#define MRDOCS_LIB_AST_MRDOCS_FILESYSTEM_HPP + +#include "lib/ConfigImpl.hpp" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/VirtualFileSystem.h" +#include "clang/Lex/HeaderSearch.h" +#include +#include +#include +#include + +namespace clang::mrdocs { + +// A proxy and overlay FS that, when a file is missing, might serve an +// adjusted or empty file from an in-memory FS and remembers it, so repeated +// opens are cheap. +// This is used to work around missing headers in some environments and +// avoid hard failures that wouldn't allow documentation generation +// unless all dependencies were present. +// We have a config option where the user can specify the glob patterns +// for include files that should be treated this way. The name +// of the option is `forgive-missing-includes`. The user can specify +// the "**" pattern to forgive all missing includes or specific +// patterns like "llvm/**" to forgive all includes from LLVM. +class MrDocsFileSystem : public llvm::vfs::FileSystem { + IntrusiveRefCntPtr Real; + std::shared_ptr Mem; + ConfigImpl const &config_; + clang::HeaderSearch* HeaderSearch = nullptr; + + mutable std::mutex MemMu; + std::atomic CWDSet{ false }; + std::string CWD; + + // --- policy helpers --- + bool + matchesPrefixSet(llvm::StringRef Path) const + { + auto matchesPrefixSetImpl = + [](llvm::StringRef Path, auto const &Prefixes) { + for (auto const &P: Prefixes) + { + auto pos = Path.find_insensitive(P); + MRDOCS_CHECK_OR_CONTINUE(pos != llvm::StringRef::npos); + if (pos == 0 || Path[pos - 1] == '/' || Path[pos - 1] == '\\') + { + return true; + } + } + return false; + }; + if (matchesPrefixSetImpl(Path, config_->missingIncludePrefixes)) + { + return true; + } + auto shimParents = std::views::keys(config_->missingIncludeShims) + | std::views::transform([](std::string const &s) { + return llvm::sys::path::parent_path(s); + }); + return matchesPrefixSetImpl(Path, shimParents); + } + + std::optional> + matchShim(llvm::StringRef Path) const + { + for (auto const &[K, P]: config_->missingIncludeShims) + { + if (Path.ends_with_insensitive(K)) + { + return std::make_pair(std::string_view(K), std::string_view(P)); + } + } + return std::nullopt; + } + + std::string + wrapShim(std::string_view path, std::string_view contents) const + { + std::string shim_macro = "MRDOCS_DYNAMIC_CONFIG_FILE_SHIM_"; + shim_macro.reserve(shim_macro.size() + path.size()); + for (char c: path) + { + if (!std::isalnum(c)) + { + shim_macro += '_'; + } else + { + shim_macro += std::toupper(c); + } + } + std::string result; + result += "#ifndef "; + result += shim_macro; + result += "\n#define "; + result += shim_macro; + result += "\n"; + result += contents; + result += "\n#endif // "; + result += shim_macro; + result += "\n"; + return result; + } + + bool + looksLikeDirectory(llvm::StringRef P) + { + // If it's dirsy, treat as directory. + if (P.ends_with("/") || P.ends_with("\\")) + { + return true; + } + // If it has an extension, treat as file + llvm::StringRef Base = llvm::sys::path::filename(P); + if (llvm::sys::path::has_extension(Base)) + { + return false; + } + // If there's no extension, it's ambiguous, but there are still + // a few cases where we can be sure it's a file. + for (auto &[key, value]: config_->missingIncludeShims) + { + if (P.ends_with("/" + key) || P.ends_with("\\" + key)) + { + return false; + } + } + return true; + } + + + // Ensure Mem has a file at Path with given contents (thread-safe). + void + ensureMemFile(llvm::StringRef Path, llvm::StringRef Contents) + { + std::lock_guard lock(MemMu); + if (!Mem->exists(Path)) + { + auto Buf = llvm::MemoryBuffer::getMemBufferCopy(Contents, Path); + Mem->addFile(Path, /*ModTime*/ 0, std::move(Buf)); + } + } + + // Make a synthetic directory status (no need to store it in MemFS). + static llvm::vfs::Status + makeDirStatus(llvm::StringRef Path) + { + using namespace llvm::sys::fs; + // Any UniqueID is fine for synthetic entries. + static std::atomic NextInode{ 1 }; + auto Now = std::chrono::time_point_cast( + std::chrono::system_clock::now()); + llvm::sys::TimePoint<> MTime(Now.time_since_epoch()); + return { + Path, // Name (Twine-constructible) + UniqueID(/*Dev*/ 0, /*Ino*/ NextInode++), + MTime, // <-- third arg is MTime + /*User*/ 0, + /*Group*/ 0, + /*Size*/ 0, + file_type::directory_file, + perms::all_all + }; + } + + // Check if the filesystem supports virtual files because of + // configuration options + bool + containsVirtualFiles() const + { + return !config_->missingIncludePrefixes.empty() + || !config_->missingIncludeShims.empty(); + } + +public: + MrDocsFileSystem( + IntrusiveRefCntPtr RealFS, + ConfigImpl const &Cfg) + : Real(std::move(RealFS)) + , Mem(std::make_shared()) + , config_(Cfg) + {} + + // --- FileSystem interface --- + + llvm::ErrorOr + status(llvm::Twine const &Path) override + { + llvm::ErrorOr RS = Real->status(Path); + if (RS || !containsVirtualFiles()) + { + return RS; + } + + auto P = Path.str(); + if (auto MS = Mem->status(P)) + { + return MS; + } + + std::string relPath = P; + if (matchesPrefixSet(P)) + { + if (looksLikeDirectory(P)) + { + return makeDirStatus(P); + } + + ensureMemFile(P, ""); + if (auto MS = Mem->status(P)) + { + return MS; + } + // else fall through to propagate original status + } + + // propagate original real error (typically ENOENT) + return RS; + } + + llvm::ErrorOr> + openFileForRead(llvm::Twine const &Path) override + { + auto RF = Real->openFileForRead(Path); + if (RF || !containsVirtualFiles()) + { + return RF; + } + + auto P = Path.str(); + if (auto MF = Mem->openFileForRead(P)) + { + return MF; + } + + if (matchesPrefixSet(P)) + { + auto shim = matchShim(P); + if (shim) + { + ensureMemFile(P, wrapShim(shim->first, shim->second)); + } else + { + ensureMemFile(P, ""); + } + + if (auto MF = Mem->openFileForRead(P)) + { + return MF; + } + // else fall through to propagate original error + } + + return RF; // return same error as first attempt + } + + // In MrDocsFileSystem class (override of vfs::FileSystem) + llvm::vfs::directory_iterator + dir_begin(llvm::Twine const &Dir, std::error_code &EC) override + { + // Try the real filesystem first. + auto It = Real->dir_begin(Dir, EC); + if (!EC) + { + return It; + } + + // If real FS failed, try the in-memory (shim/stub) FS. + EC = std::error_code(); + auto ItMem = Mem->dir_begin(Dir, EC); + if (!EC) + { + return ItMem; + } + + // Both failed: return an empty (end) iterator and no error. + EC = std::error_code(); + return llvm::vfs::directory_iterator(); + } + + std::error_code + setCurrentWorkingDirectory(llvm::Twine const &Path) override + { + auto S = Path.str(); + if (auto EC = Real->setCurrentWorkingDirectory(S)) + { + return EC; + } + CWD = std::move(S); + CWDSet = true; + return {}; + } + + llvm::ErrorOr + getCurrentWorkingDirectory() const override + { + if (CWDSet) + { + return CWD; + } + return Real->getCurrentWorkingDirectory(); + } + + std::error_code + getRealPath(llvm::Twine const &Path, llvm::SmallVectorImpl &Output) + override + { + // Mem paths are synthetic; passthrough is fine. + return Real->getRealPath(Path, Output); + } + + bool + addVirtualFile(llvm::StringRef path, llvm::StringRef contents) + { + std::lock_guard lock(MemMu); + auto Buf = llvm::MemoryBuffer::getMemBufferCopy(contents, path); + return Mem->addFile(path, /*MTime*/ 0, std::move(Buf)); + } +}; + +inline llvm::IntrusiveRefCntPtr +createMrDocsFileSystem(ConfigImpl const &Cfg) +{ + auto Real = llvm::vfs::getRealFileSystem(); + return { new MrDocsFileSystem(Real, Cfg) }; +} + +} // namespace clang::mrdocs + +#endif diff --git a/src/lib/ConfigImpl.cpp b/src/lib/ConfigImpl.cpp index fab35bb8b..60bf20bc7 100644 --- a/src/lib/ConfigImpl.cpp +++ b/src/lib/ConfigImpl.cpp @@ -14,6 +14,7 @@ #include "lib/Support/Glob.hpp" #include "lib/Support/Path.hpp" #include +#include #include #include #include @@ -126,6 +127,18 @@ load( { c->configObj_.set(name, std::string(value)); } + else if constexpr (range_of_tuple_like) + { + dom::Object obj; + auto keys = value | std::views::keys; + auto vals = value | std::views::values; + auto zip = std::views::zip(keys, vals); + for (auto const& [k, v] : zip) + { + obj.set(k, v); + } + c->configObj_.set(name, std::move(obj)); + } else if constexpr (std::ranges::range) { using ValueType = std::ranges::range_value_t; diff --git a/src/lib/ConfigOptions.json b/src/lib/ConfigOptions.json index c3ce2d1a5..a75281f17 100644 --- a/src/lib/ConfigOptions.json +++ b/src/lib/ConfigOptions.json @@ -159,6 +159,27 @@ } ] }, + { + "category": "Semantic Parsing", + "brief": "Options to control how the source code is parsed", + "details": "Mr.Docs uses libclang to parse the source code and extract symbols. The following options control how the source code is parsed.", + "options": [ + { + "name": "missing-include-prefixes", + "brief": "Include path prefixes allowed to be missing", + "details": "Specifies path prefixes for include files that, if missing, will not cause documentation generation to fail. Missing files with these prefixes are served as empty files from an in-memory file system, allowing processing to continue. For example, use \"llvm/\" to forgive all includes from LLVM. If any such path is specified, MrDocs will attempt to synthesize missing included types. Only simple sets of non-conflicting inferred types can be synthesized. For more complex types or for better control, provide a shim using the \"missing-include-shims\" option.", + "type": "list", + "default": [] + }, + { + "name": "missing-include-shims", + "brief": "Shims for forgiven missing include files", + "details": "Specifies a map of include file paths to shim contents. If a missing include file matches a forgiven prefix, MrDocs will use the shim content from this map as the file contents. If no shim is provided for a forgiven file, an empty file is used by default.", + "type": "map", + "default": {} + } + ] + }, { "category": "Comment Parsing", "brief": "Options to control how comments are parsed", @@ -288,8 +309,8 @@ "details": "If `sort-members` is set to `true`, determine how members of a record are sorted. When set to `name`, members are sorted by name. When set to `location`, members are sorted by their primary location in the source code, considering the short name of the path and the location in the file.", "type": "enum", "values": [ - "name", - "location" + "name", + "location" ], "default": "name" }, @@ -299,8 +320,8 @@ "details": "Although members of namespaces are always sorted, determine how members of a namespace are sorted. When set to `name`, members are sorted by name. When set to `location`, members are sorted by their primary location in the source code, considering the short name of the path and the location in the file.", "type": "enum", "values": [ - "name", - "location" + "name", + "location" ], "default": "name" }, diff --git a/src/lib/CorpusImpl.cpp b/src/lib/CorpusImpl.cpp index 2f65e21da..258c4c8e6 100644 --- a/src/lib/CorpusImpl.cpp +++ b/src/lib/CorpusImpl.cpp @@ -13,18 +13,20 @@ #include "CorpusImpl.hpp" #include "lib/AST/FrontendActionFactory.hpp" +#include "lib/AST/MissingSymbolSink.hpp" +#include "lib/AST/MrDocsFileSystem.hpp" #include "lib/Metadata/Finalizers/BaseMembersFinalizer.hpp" -#include "lib/Metadata/Finalizers/OverloadsFinalizer.hpp" -#include "lib/Metadata/Finalizers/SortMembersFinalizer.hpp" +#include "lib/Metadata/Finalizers/DerivedFinalizer.hpp" #include "lib/Metadata/Finalizers/JavadocFinalizer.hpp" #include "lib/Metadata/Finalizers/NamespacesFinalizer.hpp" -#include "lib/Metadata/Finalizers/DerivedFinalizer.hpp" -#include "lib/Support/Report.hpp" +#include "lib/Metadata/Finalizers/OverloadsFinalizer.hpp" +#include "lib/Metadata/Finalizers/SortMembersFinalizer.hpp" #include "lib/Support/Chrono.hpp" +#include "lib/Support/Report.hpp" #include +#include #include #include -#include #include namespace clang::mrdocs { @@ -733,36 +735,95 @@ build( // InfoSet in the execution context. InfoExecutionContext context(*config); - // Create an `ASTActionFactory` to create multiple - // `ASTAction`s that extract the AST for each translation unit. - std::unique_ptr action = - makeFrontendActionFactory(context, *config); - MRDOCS_ASSERT(action); - // ------------------------------------------ // "Process file" task // ------------------------------------------ - auto const processFile = - [&](std::string path) + auto const processFile = [&](std::string path) { + // Create an `ASTActionFactory` to create multiple + // `ASTAction`s that extract the AST for each translation unit. + // Each CompilerInstance is used only once. + // Per-file sink: no sharing, no races + MissingSymbolSink sink; + ASTActionFactory actionFactory(context, *config, sink); + + // Retry loop: grow a per-file shim and re-run + std::size_t prevCount = 0; + constexpr unsigned kMaxCollectSteps = 1000; + int rc = 1; + + for (unsigned attempt = 0; attempt <= kMaxCollectSteps; ++attempt) { - // Each thread gets an independent copy of a VFS to allow different - // concurrent working directories. - IntrusiveRefCntPtr FS = - llvm::vfs::createPhysicalFileSystem(); + // Per-file (per-thread) VFS instance: safe to reuse across retries + auto FS = createMrDocsFileSystem(*config); + auto* FSConcrete = dynamic_cast(FS.get()); + MRDOCS_ASSERT( + FSConcrete + && "createMrDocsFileSystem must return MrDocsFileSystem"); + + // Create a Clang Tool to run the action + auto PHCCOntainerOps = std::make_shared(); + tooling::ClangTool + Tool(compilations, { path }, std::move(PHCCOntainerOps), FS); + Tool.setPrintErrorMessage(false); - // KRYSTIAN NOTE: ClangTool applies the SyntaxOnly, StripOutput, - // and StripDependencyFile argument adjusters - tooling::ClangTool Tool(compilations, { path }, - std::make_shared(), FS); + // Set the shim for missing symbols + Tool.clearArgumentsAdjusters(); + if (sink.numSymbols()) + { + // Build/refresh this TU’s shim and publish it in this VFS + std::string shimContent = sink.buildShim(); + auto makeShimPathPlatformAbsolute = []()-> std::string + { + llvm::SmallString<260> wd; + auto ec = llvm::sys::fs::current_path(wd); + (void) ec; + // "C:" + llvm::SmallString<16> root(llvm::sys::path::root_name(wd)); + llvm::sys::path::append( + root, + llvm::sys::path::root_directory(wd)); // "C:/" + llvm::SmallString<260> p(root); + llvm::sys::path::append( + p, + "__mrdocs_shims", + "virtual_diagnostics_shim.hpp"); + llvm::sys::path::native(p, llvm::sys::path::Style::posix); + return std::string(p.str()); + }; + + std::string shimPath = makeShimPathPlatformAbsolute(); + FSConcrete->addVirtualFile(shimPath, shimContent); + Tool.appendArgumentsAdjuster( + tooling::combineAdjusters( + tooling::getInsertArgumentAdjuster("-include"), + tooling::getInsertArgumentAdjuster(shimPath.data()))); + } - // Suppress error messages from the tool - Tool.setPrintErrorMessage(false); + // Run the action + rc = Tool.run(&actionFactory); - if (Tool.run(action.get())) + // Check for errors + std::size_t const curCount = sink.numSymbols(); + MRDOCS_ASSERT(curCount >= prevCount); + if (rc != 0) + { + // Regular failure: break + break; + } + if (curCount == prevCount) { - formatError("Failed to run action on {}", path).Throw(); + // Success and no new missing symbols: done + break; } - }; + // Failure and new symbols: reevaluate TU with new shim + prevCount = curCount; + } + + if (rc != 0) + { + formatError("Failed to run action on {}", path).Throw(); + } + }; // ------------------------------------------ // Run the process file task on all files diff --git a/test-files/golden-tests/config/missing-include-prefixes/main.adoc b/test-files/golden-tests/config/missing-include-prefixes/main.adoc new file mode 100644 index 000000000..c29cf627a --- /dev/null +++ b/test-files/golden-tests/config/missing-include-prefixes/main.adoc @@ -0,0 +1,43 @@ += Reference +:mrdocs: + +[#index] +== Global namespace + +=== Functions + +[cols=1] +|=== +| Name +| link:#f[`f`] +| link:#g[`g`] +|=== + +[#f] +== f + +=== Synopsis + +Declared in `<main.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +unknown_type +f(); +---- + +[#g] +== g + +=== Synopsis + +Declared in `<main.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +unknown_ns::unknown_type +g(); +---- + + +[.small]#Created with https://www.mrdocs.com[MrDocs]# diff --git a/test-files/golden-tests/config/missing-include-prefixes/main.cpp b/test-files/golden-tests/config/missing-include-prefixes/main.cpp new file mode 100644 index 000000000..44703e71a --- /dev/null +++ b/test-files/golden-tests/config/missing-include-prefixes/main.cpp @@ -0,0 +1,8 @@ +// forgiven by "llvm/" prefix - includes an empty file instead +#include + +unknown_type +f(); + +unknown_ns::unknown_type +g(); diff --git a/test-files/golden-tests/config/missing-include-prefixes/main.html b/test-files/golden-tests/config/missing-include-prefixes/main.html new file mode 100644 index 000000000..dc2bb0d2e --- /dev/null +++ b/test-files/golden-tests/config/missing-include-prefixes/main.html @@ -0,0 +1,65 @@ + + +Reference + + + +
+

Created with MrDocs

+
+ + \ No newline at end of file diff --git a/test-files/golden-tests/config/missing-include-prefixes/main.xml b/test-files/golden-tests/config/missing-include-prefixes/main.xml new file mode 100644 index 000000000..2277dc506 --- /dev/null +++ b/test-files/golden-tests/config/missing-include-prefixes/main.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + diff --git a/test-files/golden-tests/config/missing-include-prefixes/main.yml b/test-files/golden-tests/config/missing-include-prefixes/main.yml new file mode 100644 index 000000000..b48410686 --- /dev/null +++ b/test-files/golden-tests/config/missing-include-prefixes/main.yml @@ -0,0 +1,2 @@ +missing-include-prefixes: + - "llvm/" diff --git a/test-files/golden-tests/config/missing-include-shims/main.adoc b/test-files/golden-tests/config/missing-include-shims/main.adoc new file mode 100644 index 000000000..185fd9112 --- /dev/null +++ b/test-files/golden-tests/config/missing-include-shims/main.adoc @@ -0,0 +1,37 @@ += Reference +:mrdocs: + +[#index] +== Global namespace + +=== Functions + +[cols=2] +|=== +| Name +| Description +| link:#f[`f`] +| Function satisfied by missing‐include‐shims +|=== + +[#f] +== f + +Function satisfied by missing‐include‐shims + +=== Synopsis + +Declared in `<main.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +llvm::StringRef +f(); +---- + +=== Return Value + +An empty string of a type defined in the shims. + + +[.small]#Created with https://www.mrdocs.com[MrDocs]# diff --git a/test-files/golden-tests/config/missing-include-shims/main.cpp b/test-files/golden-tests/config/missing-include-shims/main.cpp new file mode 100644 index 000000000..d0521e75e --- /dev/null +++ b/test-files/golden-tests/config/missing-include-shims/main.cpp @@ -0,0 +1,8 @@ +#include + +/** Function satisfied by missing-include-shims + + @return An empty string of a type defined in the shims. + */ +llvm::StringRef +f(); diff --git a/test-files/golden-tests/config/missing-include-shims/main.html b/test-files/golden-tests/config/missing-include-shims/main.html new file mode 100644 index 000000000..cb7d1d50b --- /dev/null +++ b/test-files/golden-tests/config/missing-include-shims/main.html @@ -0,0 +1,57 @@ + + +Reference + + +
+

Reference

+
+
+

+
+

Functions

+ + + + + + + + + + + +
NameDescription
f Function satisfied by missing-include-shims
+ +
+
+
+

f

+
+Function satisfied by missing-include-shims + +
+
+
+

Synopsis

+
+Declared in <main.cpp>
+
+llvm::StringRef
+f();
+
+
+
+
+
+

Return Value

+An empty string of a type defined in the shims. +
+
+ +
+
+

Created with MrDocs

+
+ + \ No newline at end of file diff --git a/test-files/golden-tests/config/missing-include-shims/main.xml b/test-files/golden-tests/config/missing-include-shims/main.xml new file mode 100644 index 000000000..d1ded87ad --- /dev/null +++ b/test-files/golden-tests/config/missing-include-shims/main.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + Function satisfied by missing-include-shims + + + An empty string of a type defined in the shims. + + + + + diff --git a/test-files/golden-tests/config/missing-include-shims/main.yml b/test-files/golden-tests/config/missing-include-shims/main.yml new file mode 100644 index 000000000..c30fd6f0f --- /dev/null +++ b/test-files/golden-tests/config/missing-include-shims/main.yml @@ -0,0 +1,8 @@ +missing-include-shims: + # Minimal shim for a commonly included LLVM header. + # Only declare enough for libclang to parse downstream code. + "llvm/ADT/StringRef.h": | + namespace llvm { + class StringRef; + } // namespace llvm + diff --git a/util/generate-config-info.py b/util/generate-config-info.py index a0a0b9e88..ba68423e9 100644 --- a/util/generate-config-info.py +++ b/util/generate-config-info.py @@ -28,7 +28,7 @@ def to_camel_case(kebab_str): def to_pascal_case(kebab_str): - separator_chars = [' ', '<', '>'] + separator_chars = [' ', '<', '>', ','] for separator_char in separator_chars: if separator_char in kebab_str: parts = kebab_str.split(separator_char) @@ -103,6 +103,7 @@ def get_valid_option_values(): 'list', 'list', 'list', + 'map' ] return valid_option_values @@ -480,6 +481,8 @@ def generate_public_settings_hpp(config): # Function to visit all the options contents += ' /** Visit all options\n' + contents += ' \n' + contents += ' @param f The visitor\n' contents += ' */\n' contents += ' template \n' contents += ' void\n' @@ -490,6 +493,8 @@ def generate_public_settings_hpp(config): contents += ' }\n\n' contents += ' /** Visit all options\n' + contents += ' \n' + contents += ' @param f The visitor\n' contents += ' */\n' contents += ' template \n' contents += ' void\n' @@ -694,6 +699,9 @@ def generate_public_settings_cpp(config): contents += f'#include {header}\n' contents += '\n' + contents += '// std::map should be considered a YAML map\n' + contents += 'LLVM_YAML_IS_STRING_MAP(std::string)\n\n' + # Generate the LLVM YAML traits for each enum, such as: for [enum_name, enum_values] in get_valid_enum_categories().items(): cpp_enum_type = f'clang::mrdocs::PublicSettings::{to_pascal_case(enum_name)}' @@ -1051,6 +1059,33 @@ def generate_public_toolargs_cpp(config): option_contents += f' MRDOCS_TRY(auto temp, {cpp_type}::create(pattern));\n' option_contents += f' s.{camel_name}.push_back(temp);\n' option_contents += f' }}\n' + elif option["type"] in ['map']: + # s.missingIncludeShims.clear(); + # for (std::string const& stringPair : this->missingIncludeShims) + # { + # // Split the string as "key=value" + # auto pos = stringPair.find('='); + # if (pos == std::string::npos) + # { + # return Unexpected(formatError("`missing-include-shims` option: invalid format (expected key=value): {}", stringPair)); + # } + # std::string key = stringPair.substr(0, pos); + # std::string value = stringPair.substr(pos + 1); + # s.missingIncludeShims[std::move(key)] = std::move(value); + # } + option_contents += f' s.{camel_name}.clear();\n' + option_contents += f' for (std::string const& stringPair : this->{camel_name})\n' + option_contents += f' {{\n' + option_contents += f' // Split the string as "key=value"\n' + option_contents += f' auto pos = stringPair.find(\'=\');\n' + option_contents += f' if (pos == std::string::npos)\n' + option_contents += f' {{\n' + option_contents += f' return Unexpected(formatError("`{option["name"]}` option: invalid format (expected key=value): {{}}", stringPair));\n' + option_contents += f' }}\n' + option_contents += f' std::string key = stringPair.substr(0, pos);\n' + option_contents += f' std::string value = stringPair.substr(pos + 1);\n' + option_contents += f' s.{camel_name}[std::move(key)] = std::move(value);\n' + option_contents += f' }}\n' else: option_contents += f' s.{camel_name} = this->{camel_name};\n' option_contents += '}\n' @@ -1101,6 +1136,8 @@ def to_cpp_type(option): return 'std::vector' if option_type in ['list']: return 'std::vector' + if option_type in ['map']: + return 'std::map' raise ValueError(f'to_cpp_type: Cannot convert option type {option_type} to C++ type') @@ -1120,6 +1157,8 @@ def to_toolargs_type(option): return 'llvm::cl::opt' if option_type in ['list', 'list', 'list', 'list', 'list', 'list']: return 'llvm::cl::list' + if option_type in ['map']: + return 'llvm::cl::list' raise ValueError(f'to_cpp_type: Cannot convert option type {option_type} to C++ type') @@ -1171,6 +1210,10 @@ def to_cpp_default_value(option, replace_reference_dir=None): if not option_default: return None return '{' + ', '.join([f'PathGlobPattern::create("{str(s)}").value()' for s in option_default]) + '}' + if option_type in ['map']: + if not option_default: + return None + return '{' + ', '.join([f'{{"{str(k)}", "{str(v)}"}}' for [k, v] in option_default.items()]) + '}' raise ValueError(f'to_cpp_type: Cannot convert option type {option_type} to C++ type') diff --git a/util/generate-yaml-schema.py b/util/generate-yaml-schema.py index a42855c17..800fe7ef4 100644 --- a/util/generate-yaml-schema.py +++ b/util/generate-yaml-schema.py @@ -68,6 +68,11 @@ def to_yaml_schema_type(option: Option) -> SchemaType: "list", ]: return {"type": "array", "items": {"type": "string"}} + if option_type in ['map']: + return { + "type": "object", + "additionalProperties": {"type": "string"}, + } raise ValueError( f"to_yaml_schema_type: Cannot convert option type {option_type} to JSON/YAML schema type" )
+

Reference

+
+
+

+
+

Functions

+ + + + + + + + + + + +
Name
f
g
+ +
+
+
+

f

+
+
+

Synopsis

+
+Declared in <main.cpp>
+
+unknown_type
+f();
+
+
+
+
+
+
+
+

g

+
+
+

Synopsis

+
+Declared in <main.cpp>
+
+unknown_ns::unknown_type
+g();
+
+
+
+
+
+ +