diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..1033103e5 --- /dev/null +++ b/.clang-format @@ -0,0 +1,229 @@ +# +# Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com) +# +# Distributed under the Boost Software License, Version 1.0. +# See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt +# + +# --------------------------------------------------------------------------- +# MrDocs Formatting Guidelines (Clang-Format Reference) +# +# This file encodes the formatting style we generally follow in MrDocs, +# based on conventions we have been using in Boost projects. +# +# Important: +# - Do not run clang-format across entire files or the whole project. +# - This configuration is provided as a *reference* to help new contributors +# understand the style rules and configure their editors/IDEs. +# - You may use clang-format to format only the *new code you add or modify* +# in a commit. Never reformat unrelated code. +# +# The goal is to keep the codebase consistent and allow new contributors +# to follow the same style, while minimizing churn and noise, and +# without introducing large, style-only changes in version history. +# +# If in doubt, look at the surrounding code in the file and follow its style. +# This file is here to make that easier, not to replace careful review. +# --------------------------------------------------------------------------- + +Language: Cpp +Standard: Latest +BasedOnStyle: Microsoft + +# Columns and Lines +ColumnLimit: 80 +ReflowComments: Always +TabWidth: 8 +UseCRLF: false +UseTab: Never +DeriveLineEnding: true + +# Breaking around braces +InsertBraces: true +BreakBeforeBraces: Custom +BraceWrapping: + # Control + AfterControlStatement: Always + AfterCaseLabel: true + BeforeCatch: true + BeforeElse: true + BeforeWhile: true + # Definition + AfterNamespace: false + AfterStruct: false + AfterClass: false + SplitEmptyRecord: false + AfterFunction: true + SplitEmptyFunction: false + BeforeLambdaBody: false + AfterEnum: false + SplitEmptyNamespace: true + AfterUnion: false + AfterExternBlock: true + # Extra + IndentBraces: false + +# Breaking around specifiers +# Namespaces +CompactNamespaces: false +# Templates +AlwaysBreakTemplateDeclarations: Yes +BreakBeforeConceptDeclarations: true +# Classes +BreakInheritanceList: BeforeComma +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: Leave +# Functions +AlwaysBreakAfterDefinitionReturnType: All +AlwaysBreakAfterReturnType: All +MaxEmptyLinesToKeep: 2 +# Strings +AlwaysBreakBeforeMultilineStrings: false +BreakStringLiterals: true +# Expressions +BreakConstructorInitializers: BeforeComma +BreakBeforeBinaryOperators: All +BreakBeforeTernaryOperators: false + +# Breaking single line blocks +# Control +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +# Declarations +AllowShortEnumsOnASingleLine: false +# Function +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortFunctionsOnASingleLine: Empty +AllowShortLambdasOnASingleLine: Inline +# Expressions +AllowAllArgumentsOnNextLine: false + +# Indentation +# Parameters +IndentWidth: 4 +# Definitions +NamespaceIndentation: None +IndentExternBlock: NoIndent +IndentPPDirectives: AfterHash +# Classes +AccessModifierOffset: -4 +IndentAccessModifiers: false +# Templates +IndentRequires: false +# Functions +IndentWrappedFunctionNames: false +LambdaBodyIndentation: OuterScope +# Control +ConstructorInitializerIndentWidth: 4 +IndentCaseBlocks: false +IndentCaseLabels: false +IndentGotoLabels: true +# Expressions +ContinuationIndentWidth: 4 +InsertTrailingCommas: None +KeepEmptyLinesAtTheStartOfBlocks: false + +# Alignment +# Macros +AlignConsecutiveMacros: Consecutive +AttributeMacros: [ 'FUTURES_CONSTEXPR', 'FUTURES_NODISCARD' ] +IfMacros: [ 'FUTURES_IF_CONSTEXPR', 'SECTION', 'TEST_CASE' ] +TypenameMacros: [ 'FUTURES_DETAIL' ] +# Declaration +PointerAlignment: Left +ReferenceAlignment: Pointer +DerivePointerAlignment: true +AlignConsecutiveDeclarations: None +QualifierAlignment: Custom +QualifierOrder: [ 'inline', 'static', 'constexpr', 'type', 'const' ] +# Namespace +ShortNamespaceLines: 0 +# Brackets +AlignAfterOpenBracket: AlwaysBreak +# Expressions +AlignArrayOfStructures: Right +AlignConsecutiveAssignments: None +AlignConsecutiveBitFields: None +AlignEscapedNewlines: Left +AlignOperands: Align +AlignTrailingComments: true + +# Spaces +SpaceAfterCStyleCast: true +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceAroundPointerQualifiers: Default +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false +SpacesBeforeTrailingComments: 1 +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesInAngles: Never +SpacesInCStyleCastParentheses: false +SpacesInConditionalStatement: false +SpacesInParentheses: false +Cpp11BracedListStyle: false + +# BinPack +BinPackArguments: false +BinPackParameters: false +BitFieldColonSpacing: After +ExperimentalAutoDetectBinPacking: true +PackConstructorInitializers: CurrentLine + +# Penalties +PenaltyBreakAssignment: 512 +PenaltyBreakBeforeFirstCallParameter: 512 +PenaltyBreakComment: 512 +PenaltyBreakFirstLessLess: 512 +PenaltyBreakString: 512 +PenaltyBreakTemplateDeclaration: 512 +PenaltyExcessCharacter: 256 +PenaltyIndentedWhitespace: 8 +PenaltyReturnTypeOnItsOwnLine: 2 + +# Sorting +SortIncludes: CaseInsensitive +SortUsingDeclarations: true +IncludeBlocks: Merge +IncludeCategories: + - Regex: '^$' # always first + Priority: 0 + - Regex: '^".*"' # quoted includes come next + Priority: 1 + - Regex: '^' # internal project headers + Priority: 2 + - Regex: '^' # internal project headers + Priority: 3 + - Regex: '^' # test suite headers + Priority: 4 + - Regex: '^' # clang headers + Priority: 5 + - Regex: '^' # llvm headers + Priority: 6 + - Regex: '^' # fmt headers + Priority: 7 + - Regex: '^' # duktape headers + Priority: 8 + # Lua headers + - Regex: '^' # Lua headers + Priority: 9 + - Regex: '^<(?!mrdocs/|clang/|llvm/|test_suite/|fmt/|duktape/)[^/]+/.*>' # other angle-bracket includes + Priority: 10 + - Regex: '^<[^/]+>' # C++ standard headers like + Priority: 11 + - Regex: '.*' # fallback + Priority: 12 + +# Comments +FixNamespaceComments: true +CommentPragmas: '^ clang-format' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3c24266be..5c00b7dd1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -402,6 +402,26 @@ jobs: run-tests: false trace-commands: true + - name: Install Lua + uses: alandefreitas/cpp-actions/cmake-workflow@v1.8.12 + with: + source-dir: ../third-party/lua + url: https://github.com/lua/lua/archive/refs/tags/v5.4.8.tar.gz + patches: | + ./third-party/lua/CMakeLists.txt + ./third-party/lua/LuaConfig.cmake.in + build-dir: ${sourceDir}/build + cc: ${{ steps.setup-cpp.outputs.cc }} + cxx: ${{ steps.setup-cpp.outputs.cxx }} + ccflags: ${{ matrix.common-ccflags }} + cxxflags: ${{ steps.rmatrix.outputs.common-cxxflags }} + build-type: ${{ matrix.build-type }} + shared: false + install: true + install-prefix: ${sourceDir}/install + run-tests: false + trace-commands: true + - name: Install Libxml2 uses: alandefreitas/cpp-actions/cmake-workflow@v1.8.12 if: matrix.compiler == 'msvc' @@ -479,6 +499,9 @@ jobs: -D CMAKE_EXE_LINKER_FLAGS="${{ steps.rmatrix.outputs.common-ldflags }}" -D LLVM_ROOT="${{ steps.rmatrix.outputs.llvm-path }}" -D duktape_ROOT="${{ steps.rmatrix.outputs.third-party-dir }}/duktape/install" + -D LUA_ROOT="${{ steps.rmatrix.outputs.third-party-dir }}/lua/install" + -D Lua_ROOT="${{ steps.rmatrix.outputs.third-party-dir }}/lua/install" + -D lua_ROOT="${{ steps.rmatrix.outputs.third-party-dir }}/lua/install" ${{ runner.os == 'Windows' && '-D LibXml2_ROOT=../third-party/libxml2/install' || '' }} export-compile-commands: true run-tests: true @@ -497,6 +520,71 @@ jobs: # The schema in the docs folder is valid npx -y -p ajv-cli -- ajv compile -s docs/mrdocs.schema.json + - name: Verify snippet .cpp files match golden tests (bash) + shell: bash + run: | + set -euo pipefail + shopt -s nullglob + + SRC="docs/website/snippets" + DST="test-files/golden-tests/snippets" + + [[ -d "$SRC" ]] || { echo "Source directory not found: $SRC"; exit 2; } + [[ -d "$DST" ]] || { echo "Destination directory not found: $DST"; exit 2; } + + missing=() + mismatched=() + + # Walk all .cpp files under SRC + while IFS= read -r -d '' src; do + rel="${src#$SRC/}" + dst="$DST/$rel" + + if [[ ! -f "$dst" ]]; then + missing+=("$rel") + continue + fi + + if [[ "${STRICT_EOL:-0}" == "1" ]]; then + # strict byte-for-byte comparison + if ! cmp -s -- "$src" "$dst"; then + mismatched+=("$rel") + fi + else + # ignore CRLF/LF differences using git diff (available on all runners) + if ! git diff --no-index --ignore-cr-at-eol --quiet -- "$src" "$dst"; then + mismatched+=("$rel") + fi + fi + done < <(find "$SRC" -type f -name '*.cpp' -print0) + + if (( ${#missing[@]} || ${#mismatched[@]} )); then + if (( ${#missing[@]} )); then + echo "Missing corresponding golden files:" + printf ' %s\n' "${missing[@]}" + fi + if (( ${#mismatched[@]} )); then + echo "Content mismatches:" + printf ' %s\n' "${mismatched[@]}" + fi + exit 1 + fi + + echo "All snippet .cpp files are present and match." + env: + # Set to "1" to enforce byte-for-byte equality (do not ignore CRLF/LF) + STRICT_EOL: "0" + + + - name: Check landing page snippets + run: | + # Find python + python=$(command -v python3 || command -v python) + # The schema in this branch is up to date + "$python" ./util/generate-yaml-schema.py --check + # The schema in the docs folder is valid + npx -y -p ajv-cli -- ajv compile -s docs/mrdocs.schema.json + - name: Upload GitHub Release Artifacts if: ${{ matrix.is-main && matrix.compiler != 'clang' }} uses: actions/upload-artifact@v4 diff --git a/.gitignore b/.gitignore index 098519ed9..9b6623007 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,5 @@ /share/mrdocs/libcxx/ /share/mrdocs/clang/ /docs/modules/reference -/.clang-format /.gdbinit /.lldbinit \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 9631325e8..f96cea1ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -284,6 +284,9 @@ if (NOT DUKTAPE_FOUND) find_package(Duktape REQUIRED CONFIG) endif() +# Lua +find_package(Lua CONFIG REQUIRED) + unset(CMAKE_FOLDER) #------------------------------------------------- @@ -321,7 +324,8 @@ target_include_directories(mrdocs-core "$" PRIVATE "${PROJECT_SOURCE_DIR}/src" - "${PROJECT_BINARY_DIR}/src") + "${PROJECT_BINARY_DIR}/src" +) target_compile_definitions( mrdocs-core PUBLIC @@ -343,6 +347,7 @@ target_include_directories(mrdocs-core "$" ) target_link_libraries(mrdocs-core PRIVATE ${DUKTAPE_LIBRARY}) +target_link_libraries(mrdocs-core PRIVATE Lua::lua) # Clang if (CLANG_SIMPLE_LIBS) diff --git a/CMakeUserPresets.json.example b/CMakeUserPresets.json.example index a555db542..e04454a03 100644 --- a/CMakeUserPresets.json.example +++ b/CMakeUserPresets.json.example @@ -133,7 +133,8 @@ "MRDOCS_BUILD_DOCS": false, "MRDOCS_GENERATE_REFERENCE": false, "MRDOCS_GENERATE_ANTORA_REFERENCE": false, - "CMAKE_MAKE_PROGRAM": "${sourceDir}/build/third-party/ninja/ninja" + "CMAKE_MAKE_PROGRAM": "${sourceDir}/build/third-party/ninja/ninja", + "CMAKE_CXX_FLAGS": "-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE" }, "condition": { "type": "equals", @@ -163,7 +164,8 @@ "MRDOCS_BUILD_DOCS": false, "MRDOCS_GENERATE_REFERENCE": false, "MRDOCS_GENERATE_ANTORA_REFERENCE": false, - "CMAKE_MAKE_PROGRAM": "${sourceDir}/build/third-party/ninja/ninja" + "CMAKE_MAKE_PROGRAM": "${sourceDir}/build/third-party/ninja/ninja", + "CMAKE_CXX_FLAGS": "-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE" }, "warnings": { "unusedCli": false @@ -360,9 +362,9 @@ "Clang_ROOT": "${sourceDir}/build/third-party/llvm-project/install/debug-clang-asan", "duktape_ROOT": "${sourceDir}/build/third-party/duktape/install/debug-clang-asan", "Duktape_ROOT": "${sourceDir}/build/third-party/duktape/install/debug-clang-asan", - "libxml2_ROOT": "", - "LibXml2_ROOT": "", - "MRDOCS_BUILD_TESTS": false, + "libxml2_ROOT": "${sourceDir}/build/third-party/libxml2/install/release-clang", + "LibXml2_ROOT": "${sourceDir}/build/third-party/libxml2/install/release-clang", + "MRDOCS_BUILD_TESTS": true, "MRDOCS_BUILD_DOCS": false, "MRDOCS_GENERATE_REFERENCE": false, "MRDOCS_GENERATE_ANTORA_REFERENCE": false, diff --git a/bootstrap.py b/bootstrap.py index 9a2f79d5d..caaab3605 100644 --- a/bootstrap.py +++ b/bootstrap.py @@ -91,6 +91,13 @@ class InstallOptions: duktape_build_dir: str = "/build/<\"-\":if(cc)><\"-\":if(sanitizer)>" duktape_install_dir: str = "/install/<\"-\":if(cc)><\"-\":if(sanitizer)>" + # Lua + lua_src_dir: str = "/lua" + lua_url: str = "https://github.com/lua/lua/archive/refs/tags/v5.4.8.tar.gz" + lua_build_type: str = "" + lua_build_dir: str = "/build/<\"-\":if(cc)><\"-\":if(sanitizer)>" + lua_install_dir: str = "/install/<\"-\":if(cc)><\"-\":if(sanitizer)>" + # LLVM llvm_src_dir: str = "/llvm-project" llvm_build_type: str = "" @@ -152,6 +159,11 @@ class InstallOptions: "duktape_build_type": "CMake build type for Duktape. (Release, Debug, RelWithDebInfo, MilRelSize)", "duktape_build_dir": "Directory where Duktape will be built.", "duktape_install_dir": "Directory where Duktape will be installed.", + "lua_src_dir": "Directory for the Lua source code.", + "lua_url": "Download URL for the Lua source archive.", + "lua_build_type": "Build type for Lua. (Release, Debug)", + "lua_build_dir": "Directory where Lua will be built.", + "lua_install_dir": "Directory where Lua will be installed.", "libxml2_src_dir": "Directory for the libxml2 source code.", "libxml2_build_type": "CMake build type for libxml2: Release always recommended. (Release, Debug, RelWithDebInfo, MilRelSize)", "libxml2_build_dir": "Directory where libxml2 will be built.", @@ -1162,6 +1174,63 @@ def install_duktape(self): self.options.duktape_install_dir, extra_args) + def install_lua(self): + # Resolve paths/values + self.prompt_dependency_path_option("lua_src_dir") + if not os.path.exists(self.options.lua_src_dir): + self.prompt_option("lua_url") + os.makedirs(self.options.lua_src_dir, exist_ok=True) + archive_filename = os.path.basename(self.options.lua_url) + archive_path = os.path.join(self.options.third_party_src_dir, archive_filename) + self.download_file(self.options.lua_url, archive_path) + + # Extract lua-5.4.8.tar.gz, flatten top-level dir into lua_src_dir + mode = "r:gz" if archive_filename.endswith(".gz") else "r:*" + with tarfile.open(archive_path, mode) as tar: + top_level = tar.getmembers()[0].name.split('/')[0] + for member in tar.getmembers(): + rel = os.path.relpath(member.name, top_level) + if rel == '.' or rel.startswith('..'): + continue + member.name = rel + tar.extract(member, path=self.options.lua_src_dir) + os.remove(archive_path) + + # Copy our tiny CMake patch files (like we do for Duktape) + lua_patches = os.path.join(self.options.mrdocs_src_dir, 'third-party', 'lua') + if os.path.exists(lua_patches): + for fname in os.listdir(lua_patches): + src = os.path.join(lua_patches, fname) + dst = os.path.join(self.options.lua_src_dir, fname) + shutil.copy(src, dst) + + # Lua’s own tree puts sources under src/; our CMakeLists handles that. + self.prompt_build_type_option("lua_build_type") + # align ABI expectations like we do for Duktape: + if not self.is_abi_compatible(self.options.mrdocs_build_type, self.options.lua_build_type): + if self.options.mrdocs_build_type.lower() == "debug": + self.options.lua_build_type = "OptimizedDebug" + else: + self.options.lua_build_type = self.options.mrdocs_build_type + + self.prompt_dependency_path_option("lua_build_dir") + self.prompt_dependency_path_option("lua_install_dir") + + extra_args = [] + if self.options.sanitizer: + flag = self.sanitizer_flag_name(self.options.sanitizer) + for arg in ("CMAKE_C_FLAGS", "CMAKE_CXX_FLAGS"): + extra_args.append(f"-D{arg}=-fsanitize={flag} -fno-sanitize-recover={flag} -fno-omit-frame-pointer") + + # Standard cmake_workflow like Duktape + self.cmake_workflow( + self.options.lua_src_dir, + self.options.lua_build_type, + self.options.lua_build_dir, + self.options.lua_install_dir, + extra_args + ) + def install_libxml2(self): self.prompt_dependency_path_option("libxml2_src_dir") if not os.path.exists(self.options.libxml2_src_dir): @@ -1338,7 +1407,9 @@ def create_cmake_presets(self): "Duktape_ROOT": self.options.duktape_install_dir, "libxml2_ROOT": self.options.libxml2_install_dir, "LibXml2_ROOT": self.options.libxml2_install_dir, - "MRDOCS_BUILD_TESTS": self.options.mrdocs_build_tests, + "LUA_ROOT": self.options.lua_install_dir, + "Lua_ROOT": self.options.lua_install_dir, + "lua_ROOT": self.options.lua_install_dir, "MRDOCS_BUILD_DOCS": False, "MRDOCS_GENERATE_REFERENCE": False, "MRDOCS_GENERATE_ANTORA_REFERENCE": False @@ -1412,6 +1483,22 @@ def create_cmake_presets(self): if cxx_flags: new_preset["cacheVariables"]['CMAKE_CXX_FLAGS'] = cxx_flags.strip() + # if build type is debug and compiler is clang (default macos or explicitly clang), + # add "CMAKE_CXX_FLAGS": "-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE" + # or append it to existing CMAKE_CXX_FLAGS + if self.options.mrdocs_build_type.lower() == "debug": + is_clang = False + if self.options.cxx and "clang" in os.path.basename(self.options.cxx).lower(): + is_clang = True + elif "CMAKE_CXX_COMPILER_ID" in self.compiler_info and self.compiler_info["CMAKE_CXX_COMPILER_ID"].lower() == "clang": + is_clang = True + if is_clang: + hardening_flag = "-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE" + if "CMAKE_CXX_FLAGS" in new_preset["cacheVariables"]: + new_preset["cacheVariables"]["CMAKE_CXX_FLAGS"] += " " + hardening_flag + else: + new_preset["cacheVariables"]["CMAKE_CXX_FLAGS"] = hardening_flag + if self.is_windows(): if self.options.python_path: new_preset["cacheVariables"]["PYTHON_EXECUTABLE"] = self.options.python_path @@ -2369,6 +2456,12 @@ def generate_run_configs(self): "args": [], "cwd": self.options.mrdocs_src_dir }) + configs.append({ + "name": f"MrDocs Reformat Source Files", + "script": os.path.join(self.options.mrdocs_src_dir, 'util', 'reformat.py'), + "args": [], + "cwd": self.options.mrdocs_src_dir + }) # Documentation generation targets mrdocs_docs_dir = os.path.join(self.options.mrdocs_src_dir, "docs") @@ -2558,6 +2651,7 @@ def install_all(self): self.probe_compilers() self.install_ninja() self.install_duktape() + self.install_lua() self.install_llvm() if self.prompt_option("mrdocs_build_tests"): self.install_libxml2() diff --git a/docs/modules/ROOT/pages/commands.adoc b/docs/modules/ROOT/pages/commands.adoc index 76f8d0cd9..15d99ef8c 100644 --- a/docs/modules/ROOT/pages/commands.adoc +++ b/docs/modules/ROOT/pages/commands.adoc @@ -1,6 +1,6 @@ = Commands -The code should be documented with the https://docs.oracle.com/en/java/javase/13/docs/specs/javadoc/doc-comment-spec.html[Doc Comment,window=_blank] format, also informally known as "javadoc" format or Doxygen-style comments. +The code should be documented with the https://docs.oracle.com/en/java/javase/13/docs/specs/javadoc/doc-comment-spec.html[Doc Comment,window=_blank] format, also informally known as Javadoc-style or Doxygen-style comments. In its most basic form, these are usually comment blocks between `pass:[/**]` and `pass:[*/]` placed above a class, function, or field declaration: @@ -43,6 +43,64 @@ Most doc comments will contain these two sections, which could also be explicitl Doc comments can also contain many special commands, such as `@param`, that are used to document the parameters of a function. Mr. Docs supports most commands commonly used in Javadoc and Doxygen comments. +== Documentation Comment Formats + +The documentation format used by MrDocs has its roots in earlier tools and languages. It is based on writing structured comments directly in the source code, which are then parsed to generate API reference documentation. + +* **Origins: Javadoc**: The format originated with Java and the `doc` tool. +The https://docs.oracle.com/en/java/javase/17/docs/specs/doc/doc-comment-spec.html[official specification] consistently calls these comments *Documentation Comments*, often abbreviated as "doc comments". In practice, many developers also refer to them as *Javadoc-style comments* due to their close association with the `doc` tool. +* **Doxygen**: When Doxygen was created to support C, C++, and other languages, it adopted a similar comment style. In its https://www.doxygen.nl/manual/docblocks.html[documentation section about this comment style], Doxygen calls these comments *comment blocks*, but also explains them as *Javadoc style comments* for users familiar with the Java ecosystem. This established the convention of using the same recognizable syntax (`/** ... */`) across different ecosystems. +* **JSDoc**: For JavaScript, the https://jsdoc.app/[JSDoc] tool introduced the same concept. Developers usually refer to them as *JSDoc comments*, again following the tradition of naming the comments after the tool. + +Other ecosystems have adopted similar approaches and comment styles, sometimes with different syntax: + +- C#: XML documentation comments (`/// ...`) +- Rust: doc comments (`/// ...` and `//! ...`) +- Swift: markup-based documentation comments (`/// ...`) + +[cols="1,1,2,2",options="header"] +|=== +| Language | Tool | Official Term | Common Term + +| Java +| Javadoc +| https://docs.oracle.com/en/java/javase/17/docs/specs/doc/doc-comment-spec.html[Documentation comments] +| Javadoc-style comments + +| C, C++, etc. +| Doxygen +| https://www.doxygen.nl/manual/docblocks.html[Comment blocks] +| Doxygen-style comments + +| JavaScript +| JSDoc +| https://jsdoc.app/[JSDoc comments] +| - + +| C# +| XML Documentation Comments +| https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/xmldoc/[XML documentation comments] +| - + +| Rust +| Rustdoc +| https://doc.rust-lang.org/rustdoc/the-doc-attribute.html[Doc comments] +| - + +| Swift +| Swift Markup +| https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/[Markup documentation comments] +| - +|=== + +Briefly summarizing the table above: + +- The original term, from Java, is *doc comments*. Other tools follow a similar convention. +- Many tools and communities prefer to call them after the tool name, e.g. *Javadoc-style comments*, *Doxygen-style comments*, or *JSDoc-style comments*. +- MrDocs follows this tradition by supporting a familiar syntax that developers across ecosystems already recognize. + + + // == Style // // The following commands can be used to format the text in the doc comments: diff --git a/docs/modules/ROOT/pages/contribute.adoc b/docs/modules/ROOT/pages/contribute.adoc index 5530b8546..1d9e2b0d6 100644 --- a/docs/modules/ROOT/pages/contribute.adoc +++ b/docs/modules/ROOT/pages/contribute.adoc @@ -32,9 +32,9 @@ Common options are defined in `mrdocs/Config.hpp`. The `Config` class represents all public options that could be defined in a configuration file. It also provides a representation plugins can use to access public options from the command line or configuration file. -The function `clang::mrdocs::loadConfig` is also provided to parse all public options from a YAML configuration file. +The function `mrdocs::loadConfig` is also provided to parse all public options from a YAML configuration file. -Internally, Mr.Docs uses the derived `clang::mrdocs::ConfigImpl` class (`src/lib/Lib/ConfigImpl.hpp`) to also store the private representation of parsed options, such as filters. +Internally, Mr.Docs uses the derived `mrdocs::ConfigImpl` class (`src/lib/Lib/ConfigImpl.hpp`) to also store the private representation of parsed options, such as filters. ==== Finalizing Options @@ -46,6 +46,23 @@ Thus, after the command line and configuration file options are parsed, they are As a last step, `DoGenerateAction` converts the public `Config` settings into a `ConfigImpl` object, which is used by the rest of the program with the parsed options. +[#representing_symbols] +== Representing Symbols + +MrDocs has many categories of objects, where we utilize polymorphism with a fixed set of valid derived types, including Symbols (functions, classes, and enums), DocComment blocks, template parameters, template arguments, and data types. For each such family, we follow a consistent file layout. Most of these families are defined in the `mrdocs/Metadata` directory. + +Each base class is defined in its own header and, when necessary, implementation file. Each derived class also has its own header and implementation file. Finally, there is a single aggregator header file that includes all the derived headers. This file centralizes logic that requires knowledge of the full set of variants, such as visitors, comparison operators, and other operations that depend on the discriminator. + +Suppose we have a polymorphic family of `Symbol` objects, with derived types `Function`, `Class`, and `Enum`. The files would be organized as follows: + +* The `Symbol/SymbolNodes.inc` file defines the possible derived types and is used to generate code via macros. +* The `Symbol/SymbolKind.hpp` file defines the `SymbolKind` enum, which is used as a discriminator for the derived types. +* The base class `Symbol` is defined in `Symbol/BaseSymbol.hpp` and `Symbol/BaseSymbol.cpp` (if needed). +* The available kinds of derived symbols are defined in `Symbol/.hpp` and `Symbol/.cpp` files, e.g., `Symbol/Function.hpp` and `Symbol/Function.cpp`. +* The `Symbol.hpp` file includes all derived headers and defines operations that require knowledge of all variants, such as visitors and comparison operators. + +This pattern keeps the individual derived types self-contained while making cross-variant operations explicit and localized. When adding a new derived type, contributors should create its header and source file alongside the existing ones and update the corresponding aggregator file to register the new variant. This keeps the codebase predictable, avoids scattering logic, and ensures that operations over polymorphic families remain easy to find and maintain. + [#extract_symbols] === Extracting Symbols @@ -71,19 +88,18 @@ For each compilation command: * Paths are normalized * Non C++ files are filtered -[#info_nodes] -==== Info Nodes +[#symbol_nodes] +==== Symbol Nodes -MrDocs represents each C++ symbol or construct as an `Info` node (`mrdocs/Metadata/Info.hpp`). -`Info` can not only represent direct AST symbols but also {cpp} constructs that need to be inferred from these symbols. +MrDocs represents each C++ symbol or construct as an `Symbol` node (`mrdocs/Metadata/Symbol.hpp`). +`Symbol` can not only represent direct AST symbols but also {cpp} constructs that need to be inferred from these symbols. Nodes in the first category will typically be created in the initial extraction step, and nodes in the second category will be created in the finalization step. -When defining a new `Info` type, it is important to consider how this type will be supported in all other modules of the codebase, including the AST visitor, generators, tests, and the documentation. -The script `.github/check_info_nodes_support.sh` will attempt to infer whether most of these features have been implemented for each node type. +When defining a new `Symbol` type, it is important to consider how this type will be supported in all other modules of the codebase, including the AST visitor, generators, tests, and the documentation. ==== Clang LibTooling -MrDocs uses Clang to extract `Info` objects from the {cpp} AST. +MrDocs uses Clang to extract `Symbol` objects from the {cpp} AST. Clang offers two https://clang.llvm.org/docs/Tooling.html[interfaces] to access the C++ AST: the https://clang.llvm.org/doxygen/group__CINDEX.html[`LibClang`] and https://clang.llvm.org/docs/LibTooling.html[`LibTooling`] libraries. MrDocs uses the latter, as it provides full control over the AST traversal process at the cost of an unstable API. @@ -100,7 +116,7 @@ The `clang::tooling::ClangTool::run` method takes a `clang::tooling::ToolAction` The action object usually comes from a `clang::tooling::FrontendActionFactory`. In the example above, the `SyntaxOnlyAction` is used to parse the source code and generate the AST without any further processing. -In MrDocs, this process happens in `clang::mrdocs::CorpusImpl::build` (`src/lib/Lib/CorpusImpl.cpp`), where we call `Tool.run` for each object in the database with our custom `ASTAction` action and `ASTActionFactory` factory (`src/lib/AST/ASTVisitor.cpp`). +In MrDocs, this process happens in `mrdocs::CorpusImpl::build` (`src/lib/Lib/CorpusImpl.cpp`), where we call `Tool.run` for each object in the database with our custom `ASTAction` action and `ASTActionFactory` factory (`src/lib/AST/ASTVisitor.cpp`). ==== AST Traversal @@ -110,7 +126,7 @@ As the AST is generated, it is traversed by the `ASTVisitor` class. The entry point of this class is `ASTVisitor::build`, which recursively calls `ASTVisitor::traverseDecl` for the root `clang::TranslationUnitDecl` node of the translation unit. During the AST traversal stage, the complete AST generated by the clang frontend is walked beginning with this root `TranslationUnitDecl` node. -Each `clang` node is converted into a `<>` node, which is then stored with any relevant information in a `mrdocs::Corpus` object. +Each `clang` node is converted into a `<>` node, which is then stored with any relevant information in a `mrdocs::Corpus` object. ==== USR Generation @@ -123,15 +139,15 @@ This is necessary to generate the full interface for user-defined types. After running the AST traversal on all translation units, `CorpusImpl::build` contains finalization steps for the `Corpus` object. At this point, we process C++ constructs that are not directly represented in the AST. -The first finalization step happens in `CorpusImpl::build` (`src/lib/Lib/CorpusImpl.cpp`), where the `Info` objects from a single translation unit +The first finalization step happens in `CorpusImpl::build` (`src/lib/Lib/CorpusImpl.cpp`), where the `Symbol` objects from a single translation unit are merged into a map containing the merged results from all other TUs. The merging step is necessary as there may be multiple identical definitions of the same entity. For instance, this represents the case where a function is declared at different points in the code base and might have different attributes or comments. At this step, the doc comments are also finalized. -Each `Info` object has a pointer to its `Javadoc` object (`mrdocs/Metadata/Javadoc.hpp`), which is a representation of the documentation comments. +Each `Symbol` object has a pointer to its `DocComment` object (`mrdocs/Metadata/DocComment.hpp`), which is a representation of the documentation comments. -After AST traversal and `Info` merging, the result is stored as a map of `Info` objects indexed by their respective `SymbolID`. -A second finalization step is then performed in `clang::mrdocs::finalize`, where any references to `SymbolID` objects that don't exist are removed. +After AST traversal and `Symbol` merging, the result is stored as a map of `Symbol` objects indexed by their respective `SymbolID`. +A second finalization step is then performed in `mrdocs::finalize`, where any references to `SymbolID` objects that don't exist are removed. This is necessary because the AST traversal will generate references to entities that should be filtered and are not present in the corpus. At this point, the `Corpus` object contains representations of all entities in the code base and further semantic {cpp} constructs that are not directly represented in the AST can be inferred. @@ -155,7 +171,7 @@ This directory contains the public headers for the MrDocs library. * `include/mrdocs/`—The core library headers ** `include/mrdocs/ADT`—Data Structures ** `include/mrdocs/Dom`—The Document Object Model for Abstract Trees -** `include/mrdocs/Metadata`—`Info` nodes and metadata classes +** `include/mrdocs/Metadata`—`Symbol` nodes and metadata classes ** `include/mrdocs/Support`—Various utility classes ==== `src/`—The main source directory @@ -167,7 +183,7 @@ This directory contains the source code for the MrDocs library and private heade ** `src/lib/Dom/`—The Document Object Model for Abstract Trees ** `src/lib/Gen/`—Generators ** `src/lib/Lib/`—The core library classes -** `src/lib/Metadata/`—`Info` nodes and metadata classes +** `src/lib/Metadata/`—`Symbol` nodes and metadata classes ** `src/lib/Support/`—Various utility classes * `src/test/`—The test directory * `src/test_suite/`—The library used for testing @@ -201,6 +217,8 @@ This directory contains build scripts and configuration files for third-party li ** `third-party/duktape/`—CMake scripts for Duktape ** `third-party/lua/`—A bundled Lua interpreter +== Polymorphism + == Coding Standards === Paths @@ -233,6 +251,26 @@ The fixtures for golden testing are defined in `test-files/golden-tests`, where * `.bad.xml`: The test output file generated when the test fails. * `.yml`: Extra configuration options for this specific file. +== Style Guide + +This project follows informal formatting conventions established by previous Boost projects. +To help contributors, we provide a `.clang-format` file that encodes these rules. + +* Do *not* apply clang-format across entire files or the whole project. +* Use the config only as a *reference* for your IDE/editor, or to format the code you **personally add or modify** in a commit. +* Always check the surrounding code and keep consistent with it. +* Do not create style-only commits that introduce large diffs without functional changes. + +Why This Approach: + +* Keeps history clean: avoids large, style-only commits. +* Ensures new contributors are not lost when adapting to project style. +* Encourages consistency without rigid automation. + +General Advice: When in doubt, copy the style of the surrounding code. The `.clang-format` file is a tool to help you, not a rule to enforce blindly. + +The utility script `./util/reformat.sh` can also be used to check a few project invariants, such as header guards and include order. + == Contributing If you find a bug or have a feature request, please open an issue on the MrDocs GitHub repository: https://github.com/cppalliance/mrdocs/issues diff --git a/docs/modules/ROOT/pages/generators.adoc b/docs/modules/ROOT/pages/generators.adoc index 7b6bccf2b..4e6e5353c 100644 --- a/docs/modules/ROOT/pages/generators.adoc +++ b/docs/modules/ROOT/pages/generators.adoc @@ -227,7 +227,7 @@ Symbol objects that contain information about the location include the following |Property |Type| Description | `loc` -| `<>` +| `<>` | The location of the symbol in the source code. |=== @@ -267,7 +267,7 @@ When the symbol kind is `record` (e.g., `class`, `struct`, `union`), the symbol | Whether the record is a typedef. | `bases` -| `<>` +| `<>` | The base classes of the record. | `interface` @@ -275,7 +275,7 @@ When the symbol kind is `record` (e.g., `class`, `struct`, `union`), the symbol | The interface of the record. | `template` -| `<>` +| `<>` | The template information of the record. |=== @@ -285,7 +285,7 @@ When the symbol kind is `enum`, the symbol object has the following additional p |Property |Type| Description | `type` -| `<>` +| `<>` | The type information of the enum. | `isScoped` @@ -383,11 +383,11 @@ When the symbol kind is `function`, the symbol object has the following addition | The parameters of the function. | `return` -| `<>` +| `<>` | The return type of the function. | `template` -| `<>` +| `<>` | The template information of the function. | `overloadedOperator` @@ -417,11 +417,11 @@ When the symbol kind is `typedef`, the symbol object has the following additiona | Property | Type | Description | `type` -| `<>` +| `<>` | The type information of the typedef. | `template` -| `<>` +| `<>` | The template information of the typedef. | `isUsing` @@ -435,11 +435,11 @@ When the symbol kind is `variable`, the symbol object has the following addition | Property | Type | Description | `type` -| `<>` +| `<>` | The type information of the variable. | `template` -| `<>` +| `<>` | The template information of the variable. | `storageClass` @@ -477,7 +477,7 @@ When the symbol kind is `field` (i.e. non-static data members), the symbol objec | Property | Type | Description | `type` -| `<>` +| `<>` | The type information of the field. | `default` @@ -531,7 +531,7 @@ When the symbol kind is `friend`, the symbol object has the following additional | The friend symbol. | `type` -| <> +| <> | The friend type. |=== @@ -541,7 +541,7 @@ When the symbol kind is `namespace-alias`, the symbol object has the following a | Property | Type | Description | `aliasedSymbol` -| <> +| <> | The aliased symbol. |=== @@ -559,7 +559,7 @@ When the symbol kind is `using`, the symbol object has the following additional | The symbols being used. | `qualifier` -| `<>` +| `<>` | The qualifier of the using declaration. |=== @@ -583,11 +583,11 @@ When the symbol kind is `guide`, the symbol object has the following additional | The parameters of the guide. | `deduced` -| `<>` +| `<>` | The deduced type of the guide. | `template` -| `<>` +| `<>` | The template information of the guide. | `explicitSpec` @@ -601,7 +601,7 @@ When the symbol kind is `concept`, the symbol object has the following additiona | Property | Type | Description | `template` -| `<>` +| `<>` | The template information of the concept. | `constraint` @@ -610,9 +610,9 @@ When the symbol kind is `concept`, the symbol object has the following additiona |=== [#source-info-fields] -=== Source Info Fields +=== Source Fields -The `Source Info` object represents the location of the symbol in the source code. +The `Source` object represents the location of the symbol in the source code. The source info object has the following properties: |=== @@ -697,14 +697,14 @@ The base info object has the following properties: | Whether the base class is virtual. | `type` -| `<>` +| `<>` | The type information of the base class. |=== [#template-info-fields] -=== Template Info Fields +=== Template Fields -The `Template Info` object represents the template information of a record, function, or typedef. +The `Template` object represents the template information of a record, function, or typedef. The template info object has the following properties: |=== @@ -723,7 +723,7 @@ The template info object has the following properties: | The template parameters. | `args` -| `<>` +| `<>` | The template arguments. | `requires` @@ -732,9 +732,9 @@ The template info object has the following properties: |=== [#type-info-fields] -=== Type Info Fields +=== Type Fields -The `Type Info` object represents the type information of a symbol. +The `Type` object represents the type information of a symbol. The type info object has the following properties: |=== @@ -769,15 +769,15 @@ The type info object has the following properties: | The cv qualifier of the type (e.g., `const`, `volatile`). | `parent-type` -| `<>` +| `<>` | The parent type of the type. | `pointee-type` -| `<>` +| `<>` | The pointee type of the type. | `element-type` -| `<>` +| `<>` | The element type of the type. | `bounds-value` @@ -789,11 +789,11 @@ The type info object has the following properties: | The bounds expression of the type. | `return-type` -| `<>` +| `<>` | The return type of the type. | `param-types` -| `<>` +| `<>` | The parameter types of the type. | `exception-spec` @@ -823,7 +823,7 @@ The param object has the following properties: | The name of the parameter. | `type` -| `<>` +| `<>` | The type information of the parameter. | `default` @@ -832,9 +832,9 @@ The param object has the following properties: |=== [#name-info-fields] -=== Name Info Fields +=== Name Fields -The `Name Info` object represents the name of a symbol. +The `Name` object represents the name of a symbol. The name info object has the following properties: |=== @@ -849,7 +849,7 @@ The name info object has the following properties: | The unique identifier of the symbol. | `args` -| `<>` +| `<>` | The template arguments of the symbol. | `prefix` @@ -921,7 +921,7 @@ The tparam object has the following properties: | The constraint of the template parameter. | `type` -| `<>` +| `<>` | The type information of the template parameter. | `params` @@ -947,7 +947,7 @@ The targ object has the following properties: | Whether the template argument is a pack expansion. | `type` -| `<>` +| `<>` | The type information of the template argument. | `value` @@ -959,7 +959,7 @@ The targ object has the following properties: | The name of the template argument. | `template` -| `<>` +| `<>` | The template information of the template argument. |=== diff --git a/docs/modules/ROOT/pages/index.adoc b/docs/modules/ROOT/pages/index.adoc index 174b1b38a..8a4033b47 100644 --- a/docs/modules/ROOT/pages/index.adoc +++ b/docs/modules/ROOT/pages/index.adoc @@ -1,6 +1,6 @@ = Mr.Docs Alan Freitas -:description: Mr.Docs: A Clang/LLVM tool for building reference documentation from C++ code and javadoc comments. +:description: Mr.Docs: A Clang/LLVM tool for building reference documentation from C++ code and documentation comments. :sectanchors: :url-repo: https://github.com/cppalliance/mrdocs :page-tags: mrdocs diff --git a/docs/mrdocs.schema.json b/docs/mrdocs.schema.json index 8865cd48f..f904ece17 100644 --- a/docs/mrdocs.schema.json +++ b/docs/mrdocs.schema.json @@ -425,6 +425,16 @@ "title": "Detect and reduce SFINAE expressions", "type": "boolean" }, + "show-enum-constants": { + "default": false, + "description": "When set to true, MrDocs creates a page for each enum constant in the documentation.", + "enum": [ + true, + false + ], + "title": "Show enum constant pages in the documentation", + "type": "boolean" + }, "show-namespaces": { "default": true, "description": "When set to true, MrDocs creates a page for each namespace in the documentation.", diff --git a/docs/mrdocs.yml b/docs/mrdocs.yml index f9cdb762b..7692b1090 100644 --- a/docs/mrdocs.yml +++ b/docs/mrdocs.yml @@ -8,11 +8,11 @@ input: file-patterns: - '*.hpp' include-symbols: - - 'clang::mrdocs::**' + - 'mrdocs::**' implementation-defined: - - 'clang::mrdocs::detail' - - 'clang::mrdocs::report::detail' - - 'clang::mrdocs::dom::detail' + - 'mrdocs::detail' + - 'mrdocs::report::detail' + - 'mrdocs::dom::detail' multipage: true generator: adoc cmake: '-D MRDOCS_DOCUMENTATION_BUILD=ON' diff --git a/docs/website/snippets/is_prime.cpp b/docs/website/snippets/is_prime.cpp index b13810c01..9330eaf30 100644 --- a/docs/website/snippets/is_prime.cpp +++ b/docs/website/snippets/is_prime.cpp @@ -4,9 +4,8 @@ Linear in n. - @return Whether or not n is prime. + @return Whether `n` is prime. @param n The number to test - */ bool is_prime(unsigned long long n) noexcept; \ No newline at end of file diff --git a/include/mrdocs/ADT/ArrayView.hpp b/include/mrdocs/ADT/ArrayView.hpp new file mode 100644 index 000000000..1341c032a --- /dev/null +++ b/include/mrdocs/ADT/ArrayView.hpp @@ -0,0 +1,167 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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_API_ADT_ARRAYVIEW_HPP +#define MRDOCS_API_ADT_ARRAYVIEW_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mrdocs { + +/** A non-owning, read-only view over a contiguous array of T. + + Similar to std::string_view but for arbitrary element type T. +*/ +template +class ArrayView { + static_assert(!std::is_void_v, "ArrayView is ill-formed"); + +public: + // types + using value_type = T; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using pointer = const T*; + using const_pointer = const T*; + using reference = const T&; + using const_reference = const T&; + using iterator = const T*; + using const_iterator = const T*; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + static constexpr size_type npos = static_cast(-1); + + // ctors + constexpr ArrayView() noexcept = default; + + constexpr ArrayView(const T* data, size_type count) noexcept + : data_(data), size_(count) {} + + template + constexpr ArrayView(const T (&arr)[N]) noexcept + : data_(arr), size_(N) {} + + template + requires (std::contiguous_iterator && + std::same_as>>, T>) + constexpr ArrayView(It first, size_type count) noexcept + : data_(std::to_address(first)), size_(count) {} + + // iterators + constexpr const_iterator begin() const noexcept { return data_; } + constexpr const_iterator end() const noexcept { return data_ + size_; } + constexpr const_iterator cbegin() const noexcept { return begin(); } + constexpr const_iterator cend() const noexcept { return end(); } + constexpr const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); } + constexpr const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); } + + // capacity + constexpr size_type size() const noexcept { return size_; } + constexpr size_type length() const noexcept { return size_; } + [[nodiscard]] constexpr bool empty() const noexcept { return size_ == 0; } + + // element access + constexpr const_reference operator[](size_type i) const noexcept { + return data_[i]; + } + constexpr const_reference at(size_type i) const { + assert(i < size_); + return data_[i]; + } + constexpr const_reference front() const { + assert(!empty()); + return data_[0]; + } + constexpr const_reference back() const { + assert(!empty()); + return data_[size_ - 1]; + } + constexpr const_pointer data() const noexcept { return data_; } + + // modifiers (adjust the view; do not touch underlying data) + constexpr void remove_prefix(size_type n) noexcept { + assert(n <= size_); + data_ += n; + size_ -= n; + } + constexpr void remove_suffix(size_type n) noexcept { + assert(n <= size_); + size_ -= n; + } + + // slicing + constexpr ArrayView slice(size_type pos, size_type count = npos) const noexcept { + assert(pos <= size_); + const size_type rcount = (count == npos || pos + count > size_) ? (size_ - pos) : count; + return ArrayView(data_ + pos, rcount); + } + constexpr ArrayView take_front(size_type n) const noexcept { + return slice(0, n <= size_ ? n : size_); + } + constexpr ArrayView take_back(size_type n) const noexcept { + n = (n <= size_) ? n : size_; + return slice(size_ - n, n); + } + constexpr ArrayView drop_front(size_type n) const noexcept { + return (n >= size_) ? ArrayView() : ArrayView(data_ + n, size_ - n); + } + constexpr ArrayView drop_back(size_type n) const noexcept { + return (n >= size_) ? ArrayView() : ArrayView(data_, size_ - n); + } + + // comparisons + friend constexpr bool operator==(ArrayView a, ArrayView b) noexcept + requires requires (const T& x, const T& y) { { x == y } -> std::convertible_to; } + { + return a.size_ == b.size_ && std::equal(a.begin(), a.end(), b.begin()); + } + + friend constexpr auto operator<=>(ArrayView a, ArrayView b) noexcept + requires requires (const T& x, const T& y) { x <=> y; } + { + return std::lexicographical_compare_three_way( + a.begin(), a.end(), b.begin(), b.end(), std::compare_three_way{}); + } + +private: + const T* data_ = nullptr; + size_type size_ = 0; +}; + +// deduction guides +template +ArrayView(const T*, std::size_t) -> ArrayView; + +template +ArrayView(const T (&)[N]) -> ArrayView; + +// helpers +template +constexpr ArrayView make_array_view(const T* data, std::size_t count) noexcept { + return ArrayView(data, count); +} + +template +constexpr ArrayView make_array_view(const T (&arr)[N]) noexcept { + return ArrayView(arr); +} + +} // mrdocs + +#endif // MRDOCS_API_ADT_ARRAYVIEW_HPP diff --git a/include/mrdocs/ADT/Nullable.hpp b/include/mrdocs/ADT/Nullable.hpp new file mode 100644 index 000000000..ba659db60 --- /dev/null +++ b/include/mrdocs/ADT/Nullable.hpp @@ -0,0 +1,352 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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_API_ADT_NULLABLE_HPP +#define MRDOCS_API_ADT_NULLABLE_HPP + +#include +#include +#include +#include +#include +#include + +namespace mrdocs { + +/** Defines a customization point for types that have an intrinsic sentinel + value denoting “null”. + + Users may specialize this trait for their own types to declare a + sentinel-based null representation. + + When enabled, nullable semantics can be implemented in terms of the + sentinel without storing a separate engaged/disengaged flag. + + Contract for specializations: + - Provide static constexpr T sentinel() noexcept; which returns the distinguished null value. + - Provide static constexpr bool is_sentinel(const T&) noexcept; which recognizes the null value. + + If a type does not have a well-defined sentinel, leave the primary template in effect. + + Notes + - Built-in pointer types and std::nullptr_t are pre-specialized to use nullptr as the sentinel. +**/ +template +struct sentinel_traits +{ + // No sentinel() or is_sentinel() in the primary template. +}; + +/** sentinel_traits specialization for raw pointers. + + Uses nullptr as the sentinel value. +**/ +template +struct sentinel_traits { + static constexpr T* + sentinel() noexcept + { + return nullptr; + } + + static constexpr bool + is_sentinel(T const* p) noexcept + { + return p == nullptr; + } +}; + +/** sentinel_traits specialization for std::nullptr_t. +**/ +template<> +struct sentinel_traits +{ + static constexpr std::nullptr_t + sentinel() noexcept + { + return nullptr; + } + + static constexpr bool + is_sentinel(std::nullptr_t) noexcept + { + return true; + } +}; + +// ----------------------------------------------------------------------------- +// Sentinel traits for numeric and enum types +// ----------------------------------------------------------------------------- + +/** sentinel_traits specialization for unsigned integral types. + + Uses the maximum representable value (~0u) as the sentinel, + which corresponds to -1 when converted. +**/ +template +requires (!std::same_as) +struct sentinel_traits +{ + static constexpr T + sentinel() noexcept + { + return static_cast(-1); + } + + static constexpr bool + is_sentinel(T v) noexcept + { + return v == static_cast(-1); + } +}; + +/** sentinel_traits specialization for floating-point types. + + Uses a quiet NaN as the sentinel value. This assumes that T + supports NaN and that it is distinguishable from all ordinary values. +**/ +template +struct sentinel_traits +{ + static constexpr T + sentinel() noexcept + { + return std::numeric_limits::quiet_NaN(); + } + + static constexpr bool + is_sentinel(T v) noexcept + { + return std::isnan(v); + } +}; + +/** sentinel_traits specialization for enums with a well-known "null" enumerator. + + If the enum defines Unknown, UNKNOWN, None, or NONE, this trait uses + that enumerator as the sentinel. This requires that such an enumerator + exists and is accessible from the scope of T. +**/ +template +requires std::is_enum_v && + (requires { T::unknown; } || + requires { T::Unknown; } || + requires { T::UNKNOWN; } || + requires { T::none; } || + requires { T::None; } || + requires { T::NONE; }) +struct sentinel_traits +{ + static constexpr T + sentinel() noexcept + { + if constexpr (requires { T::Unknown; }) + return T::Unknown; + else if constexpr (requires { T::UNKNOWN; }) + return T::UNKNOWN; + else if constexpr (requires { T::None; }) + return T::None; + else + return T::NONE; + } + + static constexpr bool + is_sentinel(T v) noexcept + { + return v == sentinel(); + } +}; + +/** Concept that is satisfied when sentinel_traits declares a usable sentinel. +**/ +template +concept HasSentinel = + requires + { + { sentinel_traits::sentinel() } -> std::same_as; + { sentinel_traits::is_sentinel(std::declval()) } -> std::convertible_to; + }; + +/** Internal concept that matches “empty-clear default-constructible” types. + + This captures the common case of containers and data structures that + can be default-constructed to empty, tested with .empty(), and + reset with .clear(). + + Common cases of such containers include std::string, std::vector, + std::optional, std::unique_ptr, std::shared_ptr, and many more. +**/ +template +concept ClearableContainerLike = + // ---- nested container typedefs present ---- + requires { + typename T::value_type; + typename T::size_type; + typename T::difference_type; + typename T::reference; + typename T::const_reference; + typename T::iterator; + typename T::const_iterator; + } && + // size_type should be integral for normal containers + std::is_integral_v && + + // ---- member begin/end with expected iterator types ---- + requires(T& t, const T& ct) { + { t.begin() } -> std::convertible_to; + { t.end() } -> std::convertible_to; + { ct.begin() } -> std::convertible_to; + { ct.end() } -> std::convertible_to; + } && + + // ---- iterator “shape”: deref and increment ---- + requires(T& t, const T& ct) { + { *t.begin() } -> std::convertible_to; + { *ct.begin() } -> std::convertible_to; + { ++std::declval() } -> std::same_as; + } && + + // ---- size/empty/clear trio ---- + requires(T& t, const T& ct) { + { ct.size() } -> std::same_as; + { ct.empty() } -> std::convertible_to; + { t.clear() } -> std::same_as; + } && + + // ---- default constructible (most std containers satisfy this) ---- + std::default_initializable; + + +/** nullable_traits defines how to treat a T as “nullable” without an + external engaged bit. + + This trait is the canonical place to encode nullability semantics used + by any optional-like type. + + It exposes the minimal operations needed by an optional: + - is_null(const T&): test if a value is null. + - null(): create a null value. + - make_null(T&): turn an existing value into null. + + Users may explicitly specialize nullable_traits for their types to define + the desired semantics. +**/ +template +struct nullable_traits +{ + // No interface in the primary template. + // Provide a specialization to enable. +}; + +/** nullable_traits for types with a sentinel. + + Delegates null handling to sentinel_traits. +**/ +template +requires HasSentinel +struct nullable_traits +{ + static constexpr bool + is_null(T const& v) noexcept + { + return sentinel_traits::is_sentinel(v); + } + + static constexpr T + null() noexcept + { + return sentinel_traits::sentinel(); + } + + static constexpr void + make_null(T& v) noexcept + { + v = sentinel_traits::sentinel(); + } +}; + +/** nullable_traits for clearable empty types. + + Treats the empty state as null, creates null via default construction, + and erases via clear(). +**/ +template +requires (!HasSentinel && ClearableContainerLike) +struct nullable_traits +{ + static constexpr bool + is_null(T const& v) noexcept(noexcept(v.empty())) + { + return v.empty(); + } + + static constexpr T + null() noexcept(std::is_nothrow_default_constructible_v) + { + return T(); + } + + static constexpr void + make_null(T& v) noexcept(noexcept(v.clear())) + { + v.clear(); + } +}; + +/** Utility function that returns true if T has a nullable_traits + specialization enabled. +**/ +template +concept has_nullable_traits_v = + requires + { + { nullable_traits::is_null(std::declval()) } -> std::convertible_to; + { nullable_traits::null() } -> std::same_as; + { nullable_traits::make_null(std::declval()) } -> std::same_as; + }; + +/** make_null helper that uses nullable_traits if available. + + @param v The value to make null. +**/ +template +inline void +make_null(T& v) noexcept(noexcept(nullable_traits::make_null(v))) +{ + nullable_traits::make_null(v); +} + +/** is_null helper that uses nullable_traits if available. + + @param v The value to test for null. + @return true if v is null, false otherwise. +**/ +template +inline bool +is_null(T const& v) noexcept(noexcept(nullable_traits::is_null(v))) +{ + return nullable_traits::is_null(v); +} + +/** null_of helper that constructs a null T using nullable_traits. + + @return A null T value. +**/ +template +inline T +null_of() noexcept(noexcept(nullable_traits::null())) +{ + return nullable_traits::null(); +} + +} // mrdocs + +#endif // MRDOCS_API_ADT_NULLABLE_HPP diff --git a/include/mrdocs/ADT/Optional.hpp b/include/mrdocs/ADT/Optional.hpp index ddbc80602..5014344d5 100644 --- a/include/mrdocs/ADT/Optional.hpp +++ b/include/mrdocs/ADT/Optional.hpp @@ -5,6 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com) // // Official repository: https://github.com/cppalliance/mrdocs // @@ -13,151 +14,1156 @@ #define MRDOCS_API_ADT_OPTIONAL_HPP #include -#include +#include +#include +#include +#include #include #include -#include -namespace clang::mrdocs { +namespace mrdocs { -/** The default empty predicate. +template +class Optional; - This predicate is true when t.empty() returns - `true` where `t` is a `T`. -*/ -struct DefaultEmptyPredicate -{ - template - constexpr - bool - operator()(T const& t) const noexcept +namespace detail { +template +using ConvertsFromAnyCvRef = std::disjunction< + std::is_constructible, + std::is_convertible, + std::is_constructible, + std::is_convertible, + std::is_constructible, + std::is_convertible, + std::is_constructible, + std::is_convertible>; + +template +using ConvertsFromOptional = ConvertsFromAnyCvRef>; + +template +using AssignsFromOptional = std::disjunction< + std::is_assignable const&>, + std::is_assignable&>, + std::is_assignable const&&>, + std::is_assignable&&>>; + +template +inline constexpr bool isOptionalV = false; + +template +inline constexpr bool isOptionalV> = true; + +template +inline constexpr bool isOptionalV> = true; +} + +/** A compact optional that automatically uses nullable_traits when + available. + + Design + - If nullable_traits exists, the null state is encoded inside T + (via sentinel or clearable-empty semantics). Storage is exactly one T. + - Otherwise, this falls back to std::optional and uses its discriminator. + + This single implementation uses a conditional storage type plus if constexpr + on has_nullable_traits_v to select the appropriate behavior at compile + time. +**/ +template +class Optional { + using storage_t = std::conditional_t< + has_nullable_traits_v, T, std::optional>; + + static constexpr bool uses_nullable_traits = has_nullable_traits_v; + + // --- noexcept helpers (avoid instantiating both branches) --- + static consteval bool default_ctor_noex_() { - return std::ranges::empty(t); + if constexpr (uses_nullable_traits) + return noexcept(nullable_traits::null()); + else + return noexcept(storage_t{}); // std::optional{} is noexcept } - template - constexpr - bool - operator()(T const& t) const noexcept + static consteval bool reset_noex_() { - return !t; + if constexpr (uses_nullable_traits) + return noexcept(nullable_traits::make_null(std::declval())); + else + return noexcept(std::declval&>().reset()); } -}; -/** A compact optional. + static consteval bool has_value_noex_() + { + if constexpr (uses_nullable_traits) + return noexcept(nullable_traits::is_null(std::declval())); + else + return noexcept(std::declval const&>().has_value()); + } + + template> + static constexpr bool NotConstructingBoolFromOptional + = true; + + template + static constexpr bool NotConstructingBoolFromOptional + = !detail::isOptionalV>; + + template> + static constexpr bool ConstructFromContainedValue + = !detail::ConvertsFromOptional::value; + + template + static constexpr bool ConstructFromContainedValue + = true; - This works like std::optional except the - predicate is invoked to determine whether - the optional is engaged. This is a space - optimization. -*/ -template< - class T, - class EmptyPredicate = DefaultEmptyPredicate> -class Optional -{ - T t_; + + storage_t s_; public: using value_type = T; - constexpr Optional() = default; - constexpr Optional(Optional const& other) = default; + /// Default-constructs to the “null” state. + constexpr Optional() noexcept(default_ctor_noex_()) + : s_([&] { + if constexpr (uses_nullable_traits) + { + return storage_t(nullable_traits::null()); + } else + { + return storage_t(std::nullopt); + } + }()) + {} + + /** Construct from std::nullopt + */ + constexpr Optional(std::nullopt_t) noexcept(default_ctor_noex_()) + : Optional() {} + + /// Copy constructor + constexpr Optional(Optional const&) = default; + + /// Move constructor + constexpr Optional(Optional&&) = default; + + /** Construct from a value. + + @param u The value to store. It must be convertible to T. + **/ + template > + requires(!std::is_same_v>) + && (!std::is_same_v>) + && std::is_constructible_v + && NotConstructingBoolFromOptional + constexpr explicit(!std::is_convertible_v) + Optional(U&& u) noexcept(std::is_nothrow_constructible_v) + : s_([&] { + if constexpr (uses_nullable_traits) + { + return storage_t(static_cast(std::forward(u))); + } else + { + return storage_t(std::forward(u)); + } + }()) + {} + + template + requires(!std::is_same_v) && std::is_constructible_v + && ConstructFromContainedValue + constexpr explicit(!std::is_convertible_v) + Optional(Optional const& t) noexcept(std::is_nothrow_constructible_v) + : s_([&] { + if constexpr (uses_nullable_traits) + { + if (t) + return storage_t(static_cast(*t)); + else + return storage_t(nullable_traits::null()); + } else + { + if (t) + return storage_t(*t); + else + return storage_t(std::nullopt); + } + }()) + {} + + template + requires(!std::is_same_v) + && std::is_constructible_v && ConstructFromContainedValue + constexpr explicit(!std::is_convertible_v) + Optional(Optional&& t) noexcept(std::is_nothrow_constructible_v) + : s_([&] { + if constexpr (uses_nullable_traits) + { + if (t) + return storage_t(static_cast(std::move(*t))); + else + return storage_t(nullable_traits::null()); + } else + { + if (t) + return storage_t(std::move(*t)); + else + return storage_t(std::nullopt); + } + }()) + {} + + template + requires + std::is_constructible_v && + ConstructFromContainedValue + constexpr explicit(!std::is_convertible_v) + Optional(std::optional const& t) noexcept(std::is_nothrow_constructible_v) + : s_([&] { + if constexpr (uses_nullable_traits) + { + if (t) + return storage_t(static_cast(*t)); + else + return storage_t(nullable_traits::null()); + } else + { + if (t) + return storage_t(*t); + else + return storage_t(std::nullopt); + } + }()) + {} + + template + requires + std::is_constructible_v && + ConstructFromContainedValue + constexpr explicit(!std::is_convertible_v) + Optional(std::optional&& t) noexcept(std::is_nothrow_constructible_v) + : s_([&] { + if constexpr (uses_nullable_traits) + { + if (t) + return storage_t(static_cast(std::move(*t))); + else + return storage_t(nullable_traits::null()); + } else + { + if (t) + return storage_t(std::move(*t)); + else + return storage_t(std::nullopt); + } + }()) + {} + + template + requires std::is_constructible_v + explicit constexpr + Optional(std::in_place_t, Args&&... args) + noexcept(std::is_nothrow_constructible_v) + : s_([&] { + if constexpr (uses_nullable_traits) + { + return storage_t(T(std::forward(args)...)); + } else + { + return storage_t(std::in_place, std::forward(args)...); + } + }()) + {} + + template + requires std::is_constructible_v&, Args...> + explicit constexpr Optional( + std::in_place_t, std::initializer_list il, Args&&... args) + noexcept(std::is_nothrow_constructible_v&, Args...>) + : s_([&] { + if constexpr (uses_nullable_traits) + { + return storage_t(T(il, std::forward(args)...)); + } else + { + return storage_t(std::in_place, il, std::forward(args)...); + } + }()) + {} + + /// Copy assignment + constexpr Optional& + operator=(Optional const&) = default; + + /// Move assignment + constexpr Optional& + operator=(Optional&&) = default; + + constexpr Optional& + operator=(std::nullptr_t) noexcept(reset_noex_()) + { + reset(); + MRDOCS_ASSERT(!has_value()); + return *this; + } + + /** Assign from a value. + + @param u The value to store. It must be convertible to T. + **/ + template > + requires(!std::is_same_v>) + && std::is_constructible_v && std::is_assignable_v + constexpr Optional& + operator=(U&& u) noexcept( + std::is_nothrow_constructible_v && + std::is_nothrow_assignable_v ) + { + if constexpr (uses_nullable_traits) + { + s_ = std::forward(u); + } + else + { + s_ = std::forward(u); + } + return *this; + } + + template + requires (!std::is_same_v) + && std::is_constructible_v + && std::is_assignable_v + && (!detail::ConvertsFromOptional::value) + && (!detail::AssignsFromOptional::value) + constexpr Optional& + operator=(const Optional& u) noexcept( + std::is_nothrow_constructible_v + && std::is_nothrow_assignable_v) + { + if (u) + { + if constexpr (uses_nullable_traits) + { + s_ = *u; + } + else + { + s_ = *u; + } + } + else + { + reset(); + } + return *this; + } + + + template + requires (!std::is_same_v) + && std::is_constructible_v + && std::is_assignable_v + && (!detail::ConvertsFromOptional::value) + && (!detail::AssignsFromOptional::value) + constexpr Optional& + operator=(Optional&& u) noexcept( + std::is_nothrow_constructible_v + && std::is_nothrow_assignable_v) + { + if (u) + { + if constexpr (uses_nullable_traits) + { + s_ = std::move(*u); + } + else + { + s_ = std::move(*u); + } + } + else + { + reset(); + } + return *this; + } + + /** Reset to the null state. **/ + constexpr void + reset() noexcept(reset_noex_()) + { + if constexpr (uses_nullable_traits) + { + nullable_traits::make_null(s_); + } + else + { + s_.reset(); + } + MRDOCS_ASSERT(!has_value()); + } + + /** In-place construct a new value, replacing any existing one. + + @param args The arguments to forward to T's constructor. + + @return A reference to the newly constructed value. + **/ + template + requires std::is_constructible_v + constexpr value_type& + emplace(Args&&... args) noexcept( + std::is_nothrow_constructible_v) + { + if constexpr (uses_nullable_traits) + { + s_ = T(std::forward(args)...); + return s_; + } + else + { + return s_.emplace(std::forward(args)...); + } + } + + /** Determine if the value is inlined via nullable traits. + + This is a compile-time property of T. If nullable_traits is not + specialized, this function returns false to indicate that the + optional uses std::optional as storage with an extra discriminator. + If nullable_traits is specialized, this function returns true + to suggest that the null state is encoded inside T and no extra + storage is used. - template - requires std::is_constructible_v + @return `true` if the optional uses nullable_traits for storage. + */ + static constexpr bool + is_inlined() noexcept + { + return uses_nullable_traits; + } + + /** True if engaged (contains a value). + + @return `true` if the optional contains a value. + **/ + constexpr bool + has_value() const noexcept(has_value_noex_()) + { + if constexpr (uses_nullable_traits) + { + return !nullable_traits::is_null(s_); + } + else + { + return s_.has_value(); + } + } + + /** Contextual bool. + **/ constexpr explicit + operator bool() const noexcept(noexcept(this->has_value())) + { + return has_value(); + } + + /** Value access. Preconditions: has_value() is true. + + @return A reference to the contained value. + **/ + constexpr value_type& + value() & noexcept + { + MRDOCS_ASSERT(has_value()); + if constexpr (uses_nullable_traits) + { + return s_; + } + else + { + return *s_; + } + } + + /// @copydoc value() & + constexpr value_type const& + value() const& noexcept + { + MRDOCS_ASSERT(has_value()); + if constexpr (uses_nullable_traits) + { + return s_; + } + else + { + return *s_; + } + } + + /// @copydoc value() & + constexpr value_type&& + value() && noexcept + { + MRDOCS_ASSERT(has_value()); + if constexpr (uses_nullable_traits) + { + return std::move(s_); + } + else + { + return std::move(*s_); + } + } + + /// @copydoc value() & + constexpr value_type const&& + value() const&& noexcept + { + MRDOCS_ASSERT(has_value()); + if constexpr (uses_nullable_traits) + { + return std::move(s_); + } + else + { + return std::move(*s_); + } + } + + /** Pointer-like access. + + @return A pointer to the contained value. + **/ + constexpr value_type* + operator->() noexcept + { + if constexpr (uses_nullable_traits) + { + return std::addressof(s_); + } + else + { + MRDOCS_ASSERT(has_value()); + return std::addressof(*s_); + } + } + + /// @copydoc operator->() noexcept + constexpr value_type const* + operator->() const noexcept + { + if constexpr (uses_nullable_traits) + { + return std::addressof(s_); + } + else + { + MRDOCS_ASSERT(has_value()); + return std::addressof(*s_); + } + } + + /** Dereference-like access. + + @return A reference to the contained value. + **/ + constexpr value_type& + operator*() noexcept + { + if constexpr (uses_nullable_traits) + { + return s_; + } + else + { + MRDOCS_ASSERT(has_value()); + return *s_; + } + } + + /// @copydoc operator*() noexcept + constexpr value_type const& + operator*() const noexcept + { + if constexpr (uses_nullable_traits) + { + return s_; + } + else + { + MRDOCS_ASSERT(has_value()); + return *s_; + } + } +}; + +namespace detail { +template +using OptionalRelOpT = std::enable_if_t, bool>; + +template +using OptionalEqT = OptionalRelOpT< + decltype(std::declval() == std::declval())>; + +template +using OptionalNeT = OptionalRelOpT< + decltype(std::declval() != std::declval())>; + +template +using OptionalLtT = OptionalRelOpT< + decltype(std::declval() < std::declval())>; + +template +using OptionalGtT = OptionalRelOpT< + decltype(std::declval() > std::declval())>; + +template +using OptionalLeT = OptionalRelOpT< + decltype(std::declval() <= std::declval())>; + +template +using OptionalGeT = detail::OptionalRelOpT< + decltype(std::declval() >= std::declval())>; + +template +concept isDerivedFromOptional = requires(T const& t) { + [](Optional const&) { + }(t); +}; + +} // namespace detail + +template +class Optional { + T* p_ = nullptr; + + template + static constexpr bool ok_bind_v + = std::is_constructible_v + && !reference_constructs_from_temporary_v; + +public: + using value_type = T; + + constexpr + Optional() noexcept = default; + + constexpr + Optional(Optional const&) noexcept = default; + + constexpr + Optional(Optional&&) noexcept = default; + + constexpr + Optional(std::nullopt_t) noexcept + : Optional() {} + + template + requires( + !std::is_same_v, Optional> && + !std::is_same_v, std::in_place_t> && + ok_bind_v) + constexpr + explicit(!std::is_convertible_v) Optional(U&& u) - : t_(std::forward(u)) + noexcept(std::is_nothrow_constructible_v) { + T& r(static_cast(u)); + p_ = std::addressof(r); } - constexpr Optional& operator=(Optional const& other) = default; - constexpr Optional& operator=(Optional&& other) = default; + template + requires ok_bind_v + constexpr + explicit(!std::is_convertible_v) + Optional(Optional& rhs) + noexcept(std::is_nothrow_constructible_v) + { + if (rhs) + { + p_ = std::addressof(*rhs); + } + } template - requires std::is_constructible_v + requires ok_bind_v constexpr - Optional& operator=(U&& u) + explicit(!std::is_convertible_v) + Optional(Optional const& rhs) + noexcept(std::is_nothrow_constructible_v) { - t_ = std::forward(u); + if (rhs) + { + p_ = std::addressof(*rhs); + } + } + + template + requires ok_bind_v + constexpr + Optional(std::optional& o) + noexcept(std::is_nothrow_constructible_v) + { + if (o) + { + p_ = std::addressof(*o); + } + } + + template + requires ok_bind_v + constexpr + Optional(std::optional const& o) + noexcept(std::is_nothrow_constructible_v) + { + if (o) + { + p_ = std::addressof(*o); + } + } + + constexpr Optional& + operator=(Optional const&) noexcept = default; + + constexpr Optional& + operator=(Optional&&) noexcept = default; + + constexpr Optional& + operator=(std::nullopt_t) noexcept + { + p_ = nullptr; + return *this; + } + + template + requires ok_bind_v + constexpr Optional& + operator=(U&& u) + noexcept(std::is_nothrow_constructible_v) + { + T& r(static_cast(u)); + p_ = std::addressof(r); return *this; } + template + requires ok_bind_v constexpr - Optional& operator=(std::nullptr_t) + Optional& + operator=(Optional& rhs) + noexcept(std::is_nothrow_constructible_v) { - t_ = T(); - MRDOCS_ASSERT(!this->operator bool()); + p_ = rhs ? std::addressof(*rhs) : nullptr; return *this; } - constexpr void reset() + template + requires ok_bind_v + constexpr + Optional& + operator=(Optional const& rhs) + noexcept(std::is_nothrow_constructible_v) { - *this = Optional(); - MRDOCS_ASSERT(!this->operator bool()); + p_ = rhs ? std::addressof(*rhs) : nullptr; + return *this; } - template - requires std::is_constructible_v - constexpr value_type& emplace(Args&&... args) + template + requires ok_bind_v + constexpr + Optional& + operator=(Optional&& rhs) + noexcept(std::is_nothrow_constructible_v) { - return t_ = T(std::forward(args)...); + p_ = rhs ? std::addressof(*rhs) : nullptr; + return *this; } - constexpr value_type& value() & noexcept + template + requires ok_bind_v + constexpr + value_type& + emplace(U&& u) + noexcept(std::is_nothrow_constructible_v) { - return t_; + T& r(static_cast(u)); + p_ = std::addressof(r); + return *p_; } - constexpr value_type const& value() const & noexcept + static + constexpr + bool + is_inlined() noexcept { - return t_; + return true; } - constexpr value_type&& value() && noexcept + constexpr + bool + has_value() const noexcept { - return std::move(t_); + return p_ != nullptr; } - constexpr value_type const&& value() const && noexcept + constexpr + explicit + operator bool() const noexcept { - return std::move(t_); + return has_value(); } - constexpr value_type& operator*() noexcept + constexpr + void + reset() noexcept { - return t_; + p_ = nullptr; } - constexpr value_type const& operator*() const noexcept + constexpr + value_type* + operator->() noexcept { - return t_; + MRDOCS_ASSERT(has_value()); + return p_; } - constexpr value_type* operator->() noexcept + constexpr + value_type const* + operator->() const noexcept { - return &t_; + MRDOCS_ASSERT(has_value()); + return p_; } - constexpr value_type const* operator->() const noexcept + constexpr + value_type& + operator*() noexcept { - return &t_; + MRDOCS_ASSERT(has_value()); + return *p_; } - constexpr explicit operator bool() const noexcept + constexpr + value_type const& + operator*() const noexcept { - return has_value(); + MRDOCS_ASSERT(has_value()); + return *p_; } - constexpr bool has_value() const noexcept + constexpr + value_type& + value() & noexcept { - return ! EmptyPredicate()(t_); + MRDOCS_ASSERT(has_value()); + return *p_; } - auto operator<=>(Optional const&) const = default; + constexpr + value_type const& + value() const& noexcept + { + MRDOCS_ASSERT(has_value()); + return *p_; + } + + constexpr + value_type& + value() && noexcept + { + MRDOCS_ASSERT(has_value()); + return *p_; + } + + constexpr + value_type const& + value() const&& noexcept + { + MRDOCS_ASSERT(has_value()); + return *p_; + } + + constexpr void + swap(Optional& other) noexcept + { + using std::swap; + swap(p_, other.p_); + } }; -} // clang::mrdocs +template +constexpr void +swap(Optional& a, Optional& b) noexcept +{ + a.swap(b); +} + +/** Compares two Optional values for equality. + Returns true if both are engaged and their contained values are equal, or both are disengaged. + @return `true` if both optionals are engaged and equal, or both are disengaged; otherwise, `false`. +*/ +template +constexpr detail::OptionalEqT +operator==(Optional const& lhs, Optional const& rhs) +{ + return static_cast(lhs) == static_cast(rhs) + && (!lhs || *lhs == *rhs); +} + +/** Compares two Optional values for inequality. + Returns true if their engagement states differ or their contained values are not equal. + @return `true` if the optionals differ in engagement or value; otherwise, `false`. +*/ +template +constexpr detail::OptionalNeT +operator!=(Optional const& lhs, Optional const& rhs) +{ + return static_cast(lhs) != static_cast(rhs) + || (static_cast(lhs) && *lhs != *rhs); +} + +/** Checks if the left Optional is less than the right Optional. + Returns true if the right is engaged and either the left is disengaged or its value is less. + @return `true` if `lhs` is less than `rhs` according to the described rules; otherwise, `false`. +*/ +template +constexpr detail::OptionalLtT +operator<(Optional const& lhs, Optional const& rhs) +{ + return static_cast(rhs) && (!lhs || *lhs < *rhs); +} + +/** Checks if the left Optional is greater than the right Optional. + Returns true if the left is engaged and either the right is disengaged or its value is greater. + @return `true` if `lhs` is greater than `rhs` according to the described rules; otherwise, `false`. +*/ +template +constexpr detail::OptionalGtT +operator>(Optional const& lhs, Optional const& rhs) +{ + return static_cast(lhs) && (!rhs || *lhs > *rhs); +} + +/** Checks if the left Optional is less than or equal to the right Optional. + Returns true if the left is disengaged or the right is engaged and the left's value is less or equal. + @return `true` if `lhs` is less than or equal to `rhs` according to the described rules; otherwise, `false`. +*/ +template +constexpr detail::OptionalLeT +operator<=(Optional const& lhs, Optional const& rhs) +{ + return !lhs || (static_cast(rhs) && *lhs <= *rhs); +} + +/** Checks if the left Optional is greater than or equal to the right Optional. + Returns true if the right is disengaged or the left is engaged and its value is greater or equal. + @return `true` if `lhs` is greater than or equal to `rhs` according to the described rules; otherwise, `false`. +*/ +template +constexpr detail::OptionalGeT +operator>=(Optional const& lhs, Optional const& rhs) +{ + return !rhs || (static_cast(lhs) && *lhs >= *rhs); +} + +/** Performs a three-way comparison between two Optional values. + If both are engaged, compares their contained values; otherwise, compares engagement state. + @return The result of the three-way comparison between the optionals or their values. +*/ +template U> +[[nodiscard]] +constexpr std::compare_three_way_result_t +operator<=>(Optional const& x, Optional const& y) +{ + return x && y ? *x <=> *y : bool(x) <=> bool(y); +} + +/** Checks if the Optional is disengaged (equal to std::nullopt). + Returns true if the Optional does not contain a value. + @return `true` if the optional is disengaged; otherwise, `false`. +*/ +template +[[nodiscard]] +constexpr bool +operator==(Optional const& lhs, std::nullopt_t) noexcept +{ + return !lhs; +} + +/** Performs a three-way comparison between an Optional and std::nullopt. + Returns std::strong_ordering::greater if engaged, std::strong_ordering::equal if disengaged. + @return The result of the three-way comparison with `std::nullopt`. +*/ +template +[[nodiscard]] +constexpr std::strong_ordering +operator<=>(Optional const& x, std::nullopt_t) noexcept +{ + return bool(x) <=> false; +} + +/** Compares an engaged Optional to a value for equality. + Returns true if the Optional is engaged and its value equals rhs. + @return `true` if the optional is engaged and equal to `rhs`; otherwise, `false`. +*/ +template +requires(!detail::isOptionalV) +constexpr detail::OptionalEqT +operator==(Optional const& lhs, U const& rhs) +{ + return lhs && *lhs == rhs; +} + +/** Compares a value to an engaged Optional for equality. + Returns true if the Optional is engaged and its value equals lhs. + @return `true` if the optional is engaged and equal to `lhs`; otherwise, `false`. +*/ +template +requires(!detail::isOptionalV) +constexpr detail::OptionalEqT +operator==(T const& lhs, Optional const& rhs) +{ + return rhs && lhs == *rhs; +} + +/** Compares an Optional to a value for inequality. + Returns true if the Optional is disengaged or its value does not equal rhs. + @return `true` if the optional is disengaged or not equal to `rhs`; otherwise, `false`. +*/ +template +requires(!detail::isOptionalV) +constexpr detail::OptionalNeT +operator!=(Optional const& lhs, U const& rhs) +{ + return !lhs || *lhs != rhs; +} + +/** Compares a value to an Optional for inequality. + Returns true if the Optional is disengaged or its value does not equal lhs. + @return `true` if the optional is disengaged or not equal to `lhs`; otherwise, `false`. +*/ +template +requires(!detail::isOptionalV) +constexpr detail::OptionalNeT +operator!=(T const& lhs, Optional const& rhs) +{ + return !rhs || lhs != *rhs; +} + +/** Checks if the Optional is less than a value. + Returns true if the Optional is disengaged or its value is less than rhs. + @return `true` if the optional is disengaged or less than `rhs`; otherwise, `false`. +*/ +template +requires(!detail::isOptionalV) +constexpr detail::OptionalLtT +operator<[[nodiscard]] (Optional const& lhs, U const& rhs) +{ + return !lhs || *lhs < rhs; +} + +/** Checks if a value is less than an engaged Optional. + Returns true if the Optional is engaged and lhs is less than its value. + @return `true` if the optional is engaged and `lhs` is less than its value; otherwise, `false`. +*/ +template +requires(!detail::isOptionalV) +constexpr detail::OptionalLtT +operator<[[nodiscard]] (T const& lhs, Optional const& rhs) +{ + return rhs && lhs < *rhs; +} + +/** Checks if the Optional is greater than a value. + Returns true if the Optional is engaged and its value is greater than rhs. + @return `true` if the optional is engaged and greater than `rhs`; otherwise, `false`. +*/ +template +requires(!detail::isOptionalV) +constexpr detail::OptionalGtT +operator>(Optional const& lhs, U const& rhs) +{ + return lhs && *lhs > rhs; +} + +/** Checks if a value is greater than an Optional. + Returns true if the Optional is disengaged or lhs is greater than its value. + @return `true` if the optional is disengaged or `lhs` is greater than its value; otherwise, `false`. +*/ +template +requires(!detail::isOptionalV) +constexpr detail::OptionalGtT +operator>(T const& lhs, Optional const& rhs) +{ + return !rhs || lhs > *rhs; +} + +/** Checks if the Optional is less than or equal to a value. + Returns true if the Optional is disengaged or its value is less than or equal to rhs. + @return `true` if the optional is disengaged or less than or equal to `rhs`; otherwise, `false`. +*/ +template +requires(!detail::isOptionalV) +constexpr detail::OptionalLeT +operator<=(Optional const& lhs, U const& rhs) +{ + return !lhs || *lhs <= rhs; +} + +/** Checks if a value is less than or equal to an engaged Optional. + Returns true if the Optional is engaged and lhs is less than or equal to its value. + @return `true` if the optional is engaged and `lhs` is less than or equal to its value; otherwise, `false`. +*/ +template +requires(!detail::isOptionalV) +constexpr detail::OptionalLeT +operator<=(T const& lhs, Optional const& rhs) +{ + return rhs && lhs <= *rhs; +} + +/** Checks if the Optional is greater than or equal to a value. + Returns true if the Optional is engaged and its value is greater than or equal to rhs. + @return `true` if the optional is engaged and greater than or equal to `rhs`; otherwise, `false`. +*/ +template +requires(!detail::isOptionalV) +constexpr detail::OptionalGeT +operator>=(Optional const& lhs, U const& rhs) +{ + return lhs && *lhs >= rhs; +} + +/** Checks if a value is greater than or equal to an Optional. + Returns true if the Optional is disengaged or lhs is greater than or equal to its value. + @return `true` if the optional is disengaged or `lhs` is greater than or equal to its value; otherwise, `false`. +*/ +template +requires(!detail::isOptionalV) +constexpr detail::OptionalGeT +operator>=(T const& lhs, Optional const& rhs) +{ + return !rhs || lhs >= *rhs; +} + +/** Performs a three-way comparison between an Optional and a value. + If the Optional is engaged, compares its value to v; otherwise, returns less. + @return The result of the three-way comparison with the value. +*/ +template +requires(!detail::isDerivedFromOptional) + && requires { typename std::compare_three_way_result_t; } + && std::three_way_comparable_with +constexpr std::compare_three_way_result_t +operator<=>(Optional const& x, U const& v) +{ + return bool(x) ? *x <=> v : std::strong_ordering::less; +} + +} // namespace mrdocs #endif diff --git a/include/mrdocs/ADT/Overload.hpp b/include/mrdocs/ADT/Overload.hpp new file mode 100644 index 000000000..a94c6a1ea --- /dev/null +++ b/include/mrdocs/ADT/Overload.hpp @@ -0,0 +1,246 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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_API_ADT_OVERLOAD_HPP +#define MRDOCS_API_ADT_OVERLOAD_HPP + +#include +#include +#include +#include +#include + +namespace mrdocs { + +/** Combines multiple callable types into a single overloaded function object. + + This is the canonical "overloaded pattern" implemented as a class template. + It inherits from all provided callables and brings in their operator()s, + so the resulting object can be called with whichever overload matches. + + Typical use-cases include visiting std::variant and building small ad-hoc + pattern-matching style dispatchers. + + @tparam Ts The callable types to combine (lambdas, function objects, etc.) + + @example + @code + auto f = fn::makeOverload( + [](int i) { return i * 2; }, + [](const std::string& s) { return s.size(); } + ); + auto a = f(21); // calls int overload + auto b = f(std::string("hello")); // calls string overload + @endcode + */ +template +struct Overload : Ts... { + using Ts::operator()...; + + /** Constructs an Overload from the given callables. + + @param xs The callables to store. + */ + constexpr explicit Overload(Ts... xs) + noexcept((std::is_nothrow_move_constructible_v && ...)) + : Ts(std::move(xs))... {} +}; + +/** Class template argument deduction guide for Overload. + + Allows writing Overload{lambda1, lambda2, ...} without + specifying template parameters. + */ +template +Overload(Ts...) -> Overload; + +/** Factory function that creates an Overload from the given callables. + + Prefer this over constructing Overload directly when you need perfect + forwarding and decayed storage. + + @param xs The callables to combine. + @return An Overload whose base classes are the decayed types of the provided callables. + + @example + @code + auto visitor = fn::makeOverload( + [](int) { return 1; }, + [](double) { return 2; } + ); + @endcode + + */ +template +[[nodiscard]] constexpr auto makeOverload(Ts&&... xs) + noexcept((std::is_nothrow_constructible_v, Ts&&> && ...)) + -> Overload...> +{ + return Overload...>(std::forward(xs)...); +} + +/** Applies a set of callables to a std::variant using std::visit and Overload. + + This is a convenience wrapper around std::visit(makeOverload(...), variant). + It forwards the variant and the callables and returns whatever std::visit + returns. + + @param v The variant to visit (can be lvalue or rvalue; const-qualification is preserved). + @param xs The callables to be combined with makeOverload and passed to std::visit. + @return The result of std::visit. + + @example + @code + std::variant v = 42; + auto r = fn::match(v, + [](int i) { return i + 1; }, + [](const std::string& s) { return s.size(); } + ); + @endcode + */ +template +constexpr decltype(auto) match(Variant&& v, Ts&&... xs) +{ + return std::visit( + makeOverload(std::forward(xs)...), + std::forward(v)); +} + +/** Visits a std::variant and calls the combined callable with the active + index and the value. + + Unlike match, visitIndexed passes an additional first parameter to your + callable set: the runtime index of the active alternative (as a std::size_t). + + This is useful when you need both the value and which alternative was + selected, without relying on type-unique alternatives. + + The supplied callables are combined via makeOverload and are expected to + accept a signature like (std::size_t index, T value) for the relevant T. + + @param v The variant to visit. + @param xs The callables to be combined with makeOverload and invoked with (index, value). + @return The result of invoking the selected callable. + + @example + @code + std::variant v = 3.14; + fn::visitIndexed(v, + [](std::size_t i, int x) { return i + x; }, + [](std::size_t i, double d) { return d + i; }, + [](std::size_t i, const std::string& s) { return s.size() + i; }); + @endcode + */ +template +constexpr decltype(auto) visitIndexed(Variant&& v, Ts&&... xs) +{ + auto f = makeOverload(std::forward(xs)...); + const std::size_t idx = std::as_const(v).index(); + return std::visit( + [&](auto&& val) -> decltype(auto) { + return f(idx, std::forward(val)); + }, + std::forward(v) + ); +} + +/** Enables recursive lambdas by passing a self-reference as the first argument. + + YCombinator stores a callable F and exposes operator() that forwards + arguments to F, prepending a reference to *this so that F can recurse. + + Overloads are provided for &, const&, &&, const&& to preserve value + category. + + @tparam F The callable to wrap. + + @example + auto fact = fn::yCombinator( + [](auto self, int n) -> long long { + return n <= 1 ? 1 : n * self(n - 1); + }); + auto r = fact(10); + @endcode + */ +template +class YCombinator { +public: + /** Constructs a YCombinator from the given callable. + + @param f The callable to store. + */ + constexpr explicit YCombinator(F f) + noexcept(std::is_nothrow_move_constructible_v) + : f_(std::move(f)) {} + + /** Invokes the stored callable, passing *this as the first parameter. + + @param args The arguments to forward to the callable after the self reference. + @return Whatever the callable returns. + */ + template + constexpr decltype(auto) operator()(Args&&... args) & + { + return f_(*this, std::forward(args)...); + } + + /** Const lvalue overload of operator(). + */ + template + constexpr decltype(auto) operator()(Args&&... args) const & + { + return f_(*this, std::forward(args)...); + } + + /** Rvalue overload of operator(). + */ + template + constexpr decltype(auto) operator()(Args&&... args) && + { + return std::move(f_)(*this, std::forward(args)...); + } + + /** Const rvalue overload of operator(). + */ + template + constexpr decltype(auto) operator()(Args&&... args) const && + { + return std::move(f_)(*this, std::forward(args)...); + } + +private: + F f_; +}; + +/** Factory that creates a YCombinator from a callable. + + Prefer this helper to avoid spelling template arguments explicitly. + + @param f The callable to wrap. + @return A YCombinator storing a decayed copy of the callable. + + @example + auto fib = fn::yCombinator( + [](auto self, int n) -> int { + return n <= 1 ? n : self(n - 1) + self(n - 2); + }); + */ +template +[[nodiscard]] constexpr auto yCombinator(F&& f) + noexcept(std::is_nothrow_constructible_v, F&&>) + -> YCombinator> +{ + return YCombinator>(std::forward(f)); +} + +} // mrdocs + +#endif // MRDOCS_API_ADT_OVERLOAD_HPP diff --git a/include/mrdocs/ADT/Polymorphic.hpp b/include/mrdocs/ADT/Polymorphic.hpp index f1ccca143..e9be2396b 100644 --- a/include/mrdocs/ADT/Polymorphic.hpp +++ b/include/mrdocs/ADT/Polymorphic.hpp @@ -12,12 +12,12 @@ #ifndef MRDOCS_API_ADT_POLYMORPHIC_HPP #define MRDOCS_API_ADT_POLYMORPHIC_HPP -#include +#include #include -#include +#include #include -namespace clang::mrdocs { +namespace mrdocs { /** A polymorphic value-type. @@ -26,9 +26,7 @@ namespace clang::mrdocs { It implements a tweaked version of std::polymorphic, based on the reference implementation for P3019R14. Differences are: - * It supports nullability, which was originally supported in P3019 through - std::optional specializations, but was later removed from the paper. - * It implements comparison operators with a very project specific design. + * It implements comparison operators with a very project-specific design. * Fixed allocator, not parametrizable. * No initializer_list constructor. @@ -37,144 +35,177 @@ namespace clang::mrdocs { To copy polymorphic objects, the class uses the copy constructor of the owned derived-type object when copying to another value. - Similarly, to allow correct destruction of + Similarly, to allow the correct destruction of derived objects, it uses the destructor of the owned derived-type object in the destructor. */ -template class Polymorphic { - struct WrapperBase { - virtual constexpr ~WrapperBase() = default; - virtual T &getValue() = 0; - virtual constexpr WrapperBase *clone() = 0; - }; +template +class Polymorphic { + // Base class for type-erasure. + struct WrapperBase { + virtual constexpr ~WrapperBase() = default; + virtual T& getValue() = 0; + virtual constexpr WrapperBase* clone() = 0; + }; + + template + class Wrapper final : public WrapperBase { + U Value; + U& getValue() override { return Value; } + public: + template + constexpr Wrapper(Ts&&... ts) : Value(std::forward(ts)...) {} + constexpr ~Wrapper() override = default; + constexpr WrapperBase* clone() override { return new Wrapper(Value); } + }; + + // nullptr only when constructed/reset by nullable_traits + WrapperBase* WB = nullptr; + + // Private null token and constructor: only the friend traits can call this. + struct _null_t { explicit constexpr _null_t(int) {} }; + explicit constexpr Polymorphic(_null_t) noexcept : WB(nullptr) {} + + // Allow the traits specialization to access private members/ctors. + friend struct nullable_traits>; + + // std::polymorphic has a default constructor that + // default-constructs the base type T if it's default-constructible. + // We disable this constructor because this is always an error + // in MrDocs. + // constexpr explicit Polymorphic() + // requires std::is_default_constructible_v + // && std::is_copy_constructible_v + // : WB(new Wrapper()) + // {} - template class Wrapper final : public WrapperBase { - U Value; +public: + using value_type = T; + using pointer = T*; + using const_pointer = T const*; + + /// Default constructs the value type (never null via public API). + /// explicit constexpr Polymorphic() : Polymorphic(std::in_place_type) {} + + /** Forwarding constructor from a derived U. */ + template + constexpr explicit Polymorphic(U&& u) + requires (!std::same_as>) && + std::copy_constructible> && + std::derived_from, T> + : WB(new Wrapper>(std::forward(u))) {} + + /** In-place constructor for a specific derived U. + + @param ts Arguments to forward to U's constructor. + */ + template + explicit constexpr Polymorphic(std::in_place_type_t, Ts&&... ts) + requires std::same_as, U> && + std::constructible_from && + std::copy_constructible && std::derived_from + : WB(new Wrapper(std::forward(ts)...)) {} + + constexpr Polymorphic(Polymorphic const& V) + : WB(V.WB ? V.WB->clone() : nullptr) {} + + constexpr Polymorphic(Polymorphic&& V) noexcept + : WB(std::exchange(V.WB, nullptr)) {} + + constexpr ~Polymorphic() { delete WB; } + + constexpr Polymorphic& operator=(Polymorphic const& V) { + if (this != &V) { + Polymorphic Temp(V); + swap(*this, Temp); + } + return *this; + } - U &getValue() override { return Value; } + constexpr Polymorphic& operator=(Polymorphic&& V) noexcept { + if (this != &V) { + delete WB; + WB = std::exchange(V.WB, nullptr); + } + return *this; + } - public: - template - constexpr Wrapper(Ts &&...ts) : Value(std::forward(ts)...) {} - constexpr ~Wrapper() override = default; - constexpr WrapperBase *clone() override { return new Wrapper(getValue()); } - }; + [[nodiscard]] constexpr pointer operator->() noexcept { + MRDOCS_ASSERT(WB != nullptr); + return &WB->getValue(); + } - WrapperBase *WB; + [[nodiscard]] constexpr const_pointer operator->() const noexcept { + MRDOCS_ASSERT(WB != nullptr); + return &WB->getValue(); + } -public: - using value_type = T; - using pointer = T *; - using const_pointer = T const *; - - /** Empty constructor. - - Ensures: *this is empty. - */ - constexpr Polymorphic(std::nullopt_t) : WB(nullptr) {} - - /// Default constructs the value type. - explicit constexpr Polymorphic() : Polymorphic(std::in_place_type) {} - - /** Forwarding constructor. - * @param u The object to copy. - */ - template - constexpr explicit Polymorphic(U &&u) - requires(not std::same_as>) && - std::copy_constructible> && - std::derived_from, T> - : WB(new Wrapper>(std::forward(u))) {} - - /** 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> && - std::constructible_from && - std::copy_constructible && std::derived_from - : WB(new Wrapper(std::forward(ts)...)) {} - - constexpr Polymorphic(const Polymorphic &V) - : WB(V ? V.WB->clone() : nullptr) {} - - constexpr Polymorphic(Polymorphic &&V) noexcept - : WB(std::exchange(V.WB, nullptr)) {} - - constexpr ~Polymorphic() { delete WB; } - - constexpr Polymorphic &operator=(const Polymorphic &V) { - if (this != &V) { - Polymorphic Temp(V); - swap(*this, Temp); + [[nodiscard]] constexpr T& operator*() noexcept { + MRDOCS_ASSERT(WB != nullptr); + return WB->getValue(); + } + + [[nodiscard]] constexpr T const& operator*() const noexcept { + MRDOCS_ASSERT(WB != nullptr); + return WB->getValue(); + } + + constexpr bool + valueless_after_move() const noexcept + { + return WB == nullptr; } - return *this; - } - - constexpr Polymorphic &operator=(Polymorphic &&V) noexcept { - if (this != &V) { - swap(*this, V); - Polymorphic Temp = std::nullopt; - swap(V, Temp); + + friend constexpr void + swap(Polymorphic& lhs, Polymorphic& rhs) noexcept + { + std::swap(lhs.WB, rhs.WB); } - return *this; - } - - [[nodiscard]] constexpr pointer operator->() noexcept { - MRDOCS_ASSERT(WB != nullptr); - return &WB->getValue(); - } - - [[nodiscard]] constexpr const_pointer operator->() const noexcept { - MRDOCS_ASSERT(WB != nullptr); - return &WB->getValue(); - } - - [[nodiscard]] constexpr T &operator*() noexcept { - MRDOCS_ASSERT(WB != nullptr); - return WB->getValue(); - } - - [[nodiscard]] constexpr const T &operator*() const noexcept { - MRDOCS_ASSERT(WB != nullptr); - return WB->getValue(); - } - - [[nodiscard]] explicit constexpr operator bool() const noexcept { - return WB != nullptr; - } - - friend constexpr void swap(Polymorphic &lhs, Polymorphic &rhs) noexcept { - std::swap(lhs.WB, rhs.WB); - } }; namespace detail { // A function to compare two polymorphic objects that // store the same derived type -template struct VisitCompareFn { - Base const &rhs; +template +struct VisitCompareFn +{ + Base const& rhs; - template auto operator()(Derived const &lhsDerived) { - auto const &rhsDerived = dynamic_cast(rhs); - return lhsDerived <=> rhsDerived; - } + template + auto + operator()(Derived const& lhsDerived) + { + auto const& rhsDerived = dynamic_cast(rhs); + return lhsDerived <=> rhsDerived; + } }; -/** Determines if a type can be visited with VisitCompareFn - */ template -concept CanVisitCompare = - requires(Base const &b) { visit(b, VisitCompareFn{b}); }; +concept CanVisitCompare = requires(Base const& b) +{ + visit(b, VisitCompareFn{ b }); +}; template inline constexpr bool IsPolymorphic = false; template inline constexpr bool IsPolymorphic> = true; } // namespace detail +/// @copydoc CompareDerived(Polymorphic const&, Polymorphic const&) +template +requires(!detail::IsPolymorphic) && detail::CanVisitCompare +auto +CompareDerived(Base const& lhs, Base const& rhs) +{ + if (lhs.Kind == rhs.Kind) + { + return visit(lhs, detail::VisitCompareFn(rhs)); + } + return lhs.Kind <=> rhs.Kind; +} + /** @brief Compares two polymorphic objects that have visit functions This function compares two Polymorphic objects that @@ -197,38 +228,17 @@ template inline constexpr bool IsPolymorphic> = true; template requires detail::CanVisitCompare auto -CompareDerived( - Polymorphic const& lhs, - Polymorphic const& rhs) +CompareDerived(Polymorphic const& lhs, Polymorphic const& rhs) { - if (lhs && rhs) - { - if (lhs->Kind == rhs->Kind) - { - return visit(*lhs, detail::VisitCompareFn(*rhs)); - } - return lhs->Kind <=> rhs->Kind; - } - return !lhs ? std::strong_ordering::less - : std::strong_ordering::greater; -} - -/// @copydoc CompareDerived(Polymorphic const&, Polymorphic const&) -template - requires(!detail::IsPolymorphic) && detail::CanVisitCompare -auto CompareDerived(Base const &lhs, Base const &rhs) { - if (lhs.Kind == rhs.Kind) { - return visit(lhs, detail::VisitCompareFn(rhs)); - } - return lhs.Kind <=> rhs.Kind; + MRDOCS_ASSERT(!lhs.valueless_after_move()); + MRDOCS_ASSERT(!rhs.valueless_after_move()); + return CompareDerived(*lhs, *rhs); } template requires detail::CanVisitCompare auto -operator<=>( - Polymorphic const& lhs, - Polymorphic const& rhs) +operator<=>(Polymorphic const& lhs, Polymorphic const& rhs) { return CompareDerived(lhs, rhs); } @@ -236,13 +246,163 @@ operator<=>( template requires detail::CanVisitCompare bool -operator==( - Polymorphic const& lhs, - Polymorphic const& rhs) +operator==(Polymorphic const& lhs, Polymorphic const& rhs) { return lhs <=> rhs == std::strong_ordering::equal; } -} // clang::mrdocs +// -------------------- nullable_traits specialization -------------------- + +/** nullable_traits for Polymorphic. + + Only this friend specialization can create/reset the null state. +*/ +template +struct nullable_traits> +{ + static constexpr bool + is_null(Polymorphic const& v) noexcept + { + return v.valueless_after_move(); + } + + static constexpr Polymorphic + null() noexcept + { + // Use the private null constructor + return Polymorphic(typename Polymorphic::_null_t{0}); + } + + static constexpr void + make_null(Polymorphic& v) noexcept + { + delete v.WB; + v.WB = nullptr; + } +}; + +//------------------------------------------------------------------------------ +// isa(p) +//------------------------------------------------------------------------------ + +template +requires ( std::derived_from, std::remove_cvref_t> ) +[[nodiscard]] inline bool +isa(const Polymorphic& p) noexcept +{ + if (p.valueless_after_move()) return false; + using ToU = std::remove_reference_t; + return dynamic_cast(std::addressof(*p)) != nullptr; +} + +template +requires ( std::derived_from, std::remove_cvref_t> ) +[[nodiscard]] inline bool +isa_or_null(const Polymorphic* pp) noexcept +{ + return pp && isa(*pp); +} + +//------------------------------------------------------------------------------ +// dyn_cast(p) → pointer (nullptr if not) +//------------------------------------------------------------------------------ + +template +requires ( std::derived_from, std::remove_cvref_t> ) +[[nodiscard]] inline auto +dyn_cast(Polymorphic& p) noexcept + -> std::add_pointer_t> +{ + if (p.valueless_after_move()) return nullptr; + using ToU = std::remove_reference_t; + return dynamic_cast(std::addressof(*p)); +} + +template +requires ( std::derived_from, std::remove_cvref_t> ) +[[nodiscard]] inline auto +dyn_cast(const Polymorphic& p) noexcept + -> std::add_pointer_t> +{ + if (p.valueless_after_move()) return nullptr; + using ToU = std::remove_reference_t; + return dynamic_cast(std::addressof(*p)); +} + +template +requires ( std::derived_from, std::remove_cvref_t> ) +[[nodiscard]] inline auto +dyn_cast_or_null(Polymorphic* pp) noexcept + -> std::add_pointer_t> +{ + return (pp && !pp->valueless_after_move()) ? dyn_cast(*pp) : nullptr; +} + +template +requires ( std::derived_from, std::remove_cvref_t> ) +[[nodiscard]] inline auto +dyn_cast_or_null(const Polymorphic* pp) noexcept + -> std::add_pointer_t> +{ + return (pp && !pp->valueless_after_move()) ? dyn_cast(*pp) : nullptr; +} + +//------------------------------------------------------------------------------ +// cast(p) → reference (asserts on failure) +//------------------------------------------------------------------------------ + +template +requires ( std::derived_from, std::remove_cvref_t> ) +[[nodiscard]] inline auto +cast(Polymorphic& p) + -> std::remove_reference_t& +{ + assert(!p.valueless_after_move() && "cast: moved-from Polymorphic"); + auto* r = dyn_cast(p); + assert(r && "cast: invalid cast"); + return *r; +} + +template +requires ( std::derived_from, std::remove_cvref_t> ) +[[nodiscard]] inline auto +cast(const Polymorphic& p) + -> const std::remove_reference_t& +{ + assert(!p.valueless_after_move() && "cast: moved-from Polymorphic"); + auto* r = dyn_cast(p); + assert(r && "cast: invalid cast"); + return *r; +} + +//------------------------------------------------------------------------------ +// cast_or_null(pp) → pointer (asserts on bad non-null) +//------------------------------------------------------------------------------ + +template +requires ( std::derived_from, std::remove_cvref_t> ) +[[nodiscard]] inline auto +cast_or_null(Polymorphic* pp) + -> std::add_pointer_t> +{ + if (!pp) return nullptr; + auto* r = dyn_cast(*pp); + assert(r && "cast_or_null: invalid cast on non-null argument"); + return r; +} + +template +requires ( std::derived_from, std::remove_cvref_t> ) +[[nodiscard]] inline auto +cast_or_null(const Polymorphic* pp) + -> std::add_pointer_t> +{ + if (!pp) return nullptr; + auto* r = dyn_cast(*pp); + assert(r && "cast_or_null: invalid cast on non-null argument"); + return r; +} + +} // namespace mrdocs -#endif +#endif // MRDOCS_API_ADT_POLYMORPHIC_HPP diff --git a/include/mrdocs/ADT/UnorderedStringMap.hpp b/include/mrdocs/ADT/UnorderedStringMap.hpp index 71c086c59..e9abc4bfe 100644 --- a/include/mrdocs/ADT/UnorderedStringMap.hpp +++ b/include/mrdocs/ADT/UnorderedStringMap.hpp @@ -12,10 +12,10 @@ #ifndef MRDOCS_API_ADT_UNORDEREDSTRINGMAP_HPP #define MRDOCS_API_ADT_UNORDEREDSTRINGMAP_HPP -#include #include +#include -namespace clang::mrdocs { +namespace mrdocs { struct StringHash { @@ -32,6 +32,6 @@ using UnorderedStringMap = std::unordered_map using UnorderedStringMultiMap = std::unordered_multimap>; -} // clang::mrdocs +} // mrdocs -#endif +#endif // MRDOCS_API_ADT_UNORDEREDSTRINGMAP_HPP diff --git a/include/mrdocs/Config.hpp b/include/mrdocs/Config.hpp index 5fdeb915f..26e00d571 100644 --- a/include/mrdocs/Config.hpp +++ b/include/mrdocs/Config.hpp @@ -13,9 +13,9 @@ #define MRDOCS_API_CONFIG_HPP #include -#include #include #include +#include #include #include #include @@ -31,7 +31,7 @@ struct MappingTraits; } // llvm::yaml -namespace clang { + namespace mrdocs { class ThreadPool; @@ -223,6 +223,6 @@ class MRDOCS_DECL }; } // mrdocs -} // clang -#endif + +#endif // MRDOCS_API_CONFIG_HPP diff --git a/include/mrdocs/Config/ReferenceDirectories.hpp b/include/mrdocs/Config/ReferenceDirectories.hpp index 2e8e61665..b2616cb79 100644 --- a/include/mrdocs/Config/ReferenceDirectories.hpp +++ b/include/mrdocs/Config/ReferenceDirectories.hpp @@ -9,12 +9,12 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_API_CONFIG_REFERENCE_DIRECTORIES_HPP -#define MRDOCS_API_CONFIG_REFERENCE_DIRECTORIES_HPP +#ifndef MRDOCS_API_CONFIG_REFERENCEDIRECTORIES_HPP +#define MRDOCS_API_CONFIG_REFERENCEDIRECTORIES_HPP #include -namespace clang { + namespace mrdocs { /** Reference directories used to resolve paths @@ -32,6 +32,6 @@ struct ReferenceDirectories }; } // mrdocs -} // clang -#endif + +#endif // MRDOCS_API_CONFIG_REFERENCEDIRECTORIES_HPP diff --git a/include/mrdocs/Corpus.hpp b/include/mrdocs/Corpus.hpp index d50ee6e35..d323751df 100644 --- a/include/mrdocs/Corpus.hpp +++ b/include/mrdocs/Corpus.hpp @@ -17,13 +17,13 @@ #include #include #include +#include #include #include #include #include -#include -namespace clang::mrdocs { +namespace mrdocs { /** The collection of declarations in extracted form. */ @@ -43,11 +43,11 @@ class MRDOCS_VISIBLE The iterator is a forward iterator that iterates over all symbols in the index. It dereferences to a reference to a - const @ref Info. + const @ref Symbol. The logic for incrementing the iterator is provided by the Corpus implementation via - a function that retuns the next Info in the + a function that retuns the next Symbol in the index, or nullptr if there are no more. */ class iterator; @@ -83,41 +83,41 @@ class MRDOCS_VISIBLE bool empty() const noexcept; - /** Return the Info with the matching ID, or nullptr. - */ - virtual - Info const* - find(SymbolID const& id) const noexcept = 0; - - /** Return the Info for the matching string in a given context. + /** Return the Symbol 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 + @return The Symbol 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> + Expected lookup(SymbolID const& context, std::string_view name) const = 0; - /** Return the Info for the matching string in the global context. + /** Return the Symbol with the matching ID, or nullptr. + */ + virtual + Symbol const* + find(SymbolID const& id) const noexcept = 0; + + /** Return the Symbol 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 + @return The Symbol for the symbol with the specified name in the global context, or an error if not found. */ - Expected> + Expected lookup(std::string_view name) const { return lookup(SymbolID::global, name); } - /** Return true if an Info with the specified symbol ID exists. + /** Return true if an Symbol with the specified symbol ID exists. This function uses the @ref find function to locate - the Info with the specified symbol ID and returns + the Symbol with the specified symbol ID and returns true if it exists, otherwise false. */ bool @@ -127,20 +127,20 @@ class MRDOCS_VISIBLE return find(id) != nullptr; } - /** Return the Info with the specified symbol ID. + /** Return the Symbol with the specified symbol ID. This function uses the @ref find function to locate - the Info with the specified symbol ID. The result + the Symbol with the specified symbol ID. The result is converted to the specified type T and returned. The function @ref exists can be used to determine - if an Info with the specified symbol ID exists. + if an Symbol with the specified symbol ID exists. If the id does not exist, the behavior is undefined. - If the Info is not of type T, the behavior is undefined. + If the Symbol is not of type T, the behavior is undefined. */ - template - requires std::derived_from + template + requires std::derived_from T const& get(SymbolID const& id) const noexcept; @@ -150,7 +150,7 @@ class MRDOCS_VISIBLE @ref get with the symbol ID for the global namespace. */ - NamespaceInfo const& + NamespaceSymbol const& globalNamespace() const noexcept; /** Visit the specified Symbol IDs @@ -160,11 +160,11 @@ class MRDOCS_VISIBLE For each member of `I` associated with the ID in `range`, the function will invoke the function object `fn` with a - type derived from `Info` as the first argument, followed by + type derived from `Symbol` as the first argument, followed by `args...`. The type of the first argument is determined - by the `InfoKind` of the `Info` object. + by the `SymbolKind` of the `Symbol` object. @param range A range of SymbolID objects. @param f The function to invoke. @@ -182,40 +182,40 @@ class MRDOCS_VISIBLE } } - /** Options to traverse the members of an Info. + /** Options to traverse the members of an Symbol. */ struct TraverseOptions { /// Whether to traverse in a stable order bool ordered = false; - /// Whether to skip inherited members whose parent is not the Info + /// Whether to skip inherited members whose parent is not the Symbol bool skipInherited = false; - /// Whether to skip inherited members whose parent is not the Info + /// Whether to skip inherited members whose parent is not the Symbol bool recursive = false; }; - /** Visit the members of specified Info. + /** Visit the members of specified Symbol. This function invokes the specified function `f` - for each member of the specified Info `I`. + for each member of the specified Symbol `I`. For each member of `I`, the function will invoke the function object `fn` with a type derived from - `Info` as the first argument, followed by `args...`. + `Symbol` as the first argument, followed by `args...`. The type of the first argument is determined - by the `InfoKind` of the `Info` object. + by the `SymbolKind` of the `Symbol` object. @param opts The options to traverse. - @param I The Info to visit. + @param I The Symbol to visit. @param f The function to invoke. @param args The arguments to pass to the function. */ - template T, class F, class... Args> + template T, class F, class... Args> void traverse(TraverseOptions const& opts, T const& I, F&& f, Args&&... args) const { - if constexpr (InfoParent) + if constexpr (SymbolParent) { if (!opts.ordered) { @@ -237,7 +237,7 @@ class MRDOCS_VISIBLE auto nonInherited = allMembers(I) | std::views::filter([this, &I](SymbolID const& id) { - Info const* MI = find(id); + Symbol const* MI = find(id); MRDOCS_CHECK_OR(MI, false); return MI->Parent == I.id; }); @@ -265,16 +265,14 @@ class MRDOCS_VISIBLE std::ranges::sort(members, [this](SymbolID const& lhs, SymbolID const& rhs) { - auto const& lhsInfo = get(lhs); - auto const& rhsInfo = get(rhs); - if (auto const cmp = lhsInfo.Name <=> rhsInfo.Name; + auto const& lhsSymbol = get(lhs); + auto const& rhsSymbol = get(rhs); + if (auto const cmp = lhsSymbol.Name <=> rhsSymbol.Name; !std::is_eq(cmp)) { return std::is_lt(cmp); } - return std::is_lt( - CompareDerived(Polymorphic(lhsInfo), - Polymorphic(rhsInfo))); + return std::is_lt(CompareDerived(lhsSymbol, rhsSymbol)); }); if (!opts.skipInherited) { @@ -296,7 +294,7 @@ class MRDOCS_VISIBLE auto nonInherited = members | std::views::filter([this, &I](SymbolID const& id) { - Info const* MI = find(id); + Symbol const* MI = find(id); MRDOCS_CHECK_OR(MI, false); return MI->Parent == I.id; }); @@ -317,50 +315,50 @@ class MRDOCS_VISIBLE } } - /** Visit the members of specified Info. + /** Visit the members of specified Symbol. This function invokes the specified function `f` - for each member of the specified Info `I`. + for each member of the specified Symbol `I`. For each member of `I`, the function will invoke the function object `fn` with a type derived from - `Info` as the first argument, followed by `args...`. + `Symbol` as the first argument, followed by `args...`. The type of the first argument is determined - by the `InfoKind` of the `Info` object. + by the `SymbolKind` of the `Symbol` object. - @param I The Info to visit. + @param I The Symbol to visit. @param f The function to invoke. @param args The arguments to pass to the function. */ - template T, class F, class... Args> + template T, class F, class... Args> void traverse(T const& I, F&& f, Args&&... args) const { traverse({}, I, std::forward(f), std::forward(args)...); } - /** Return the fully qualified name of the specified Info. + /** Return the fully qualified name of the specified Symbol. This function returns the fully qualified name - of the specified Info `I` as a string. + of the specified Symbol `I` as a string. - The `Info` parents are traversed to construct + The `Symbol` parents are traversed to construct the fully qualified name which is stored in the string `temp`. - @param I The Info to get the qualified name for. + @param I The Symbol to get the qualified name for. @param temp The string to store the result in. @return A reference to the string `temp`. */ virtual void qualifiedName( - Info const& I, + Symbol const& I, std::string& temp) const = 0; std::string - qualifiedName(Info const& I) const + qualifiedName(Symbol const& I) const { std::string temp; qualifiedName(I, temp); @@ -370,7 +368,7 @@ class MRDOCS_VISIBLE /** Return a qualified name from the specified context. This function returns the qualified name - of the specified Info `I` from the context + of the specified Symbol `I` from the context specified by the SymbolID `context`. If the context is a parent of `I`, the @@ -382,19 +380,19 @@ class MRDOCS_VISIBLE qualified name is constructed relative to the global namespace with the prefix `::`. - @param I The Info to get the qualified name for. + @param I The Symbol 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 qualifiedName( - Info const& I, + Symbol const& I, SymbolID const& context, std::string& result) const = 0; std::string - qualifiedName(Info const& I, SymbolID const& context) const + qualifiedName(Symbol const& I, SymbolID const& context) const { std::string temp; qualifiedName(I, context, temp); @@ -405,7 +403,7 @@ class MRDOCS_VISIBLE //------------------------------------------------ template -requires std::derived_from +requires std::derived_from T const& Corpus:: get( @@ -413,7 +411,7 @@ get( { auto I = find(id); MRDOCS_ASSERT(I != nullptr); - if constexpr(std::is_same_v) + if constexpr(std::is_same_v) { return *I; } @@ -428,11 +426,11 @@ get( class Corpus::iterator { Corpus const* corpus_; - Info const* val_; - Info const*(*next_)(Corpus const*, Info const*); + Symbol const* val_; + Symbol const*(*next_)(Corpus const*, Symbol const*); public: - using value_type = const Info; + using value_type = const Symbol; using size_type = std::size_t; using difference_type = std::ptrdiff_t; using pointer = value_type*; @@ -446,8 +444,8 @@ class Corpus::iterator iterator( Corpus const* corpus, - Info const* val, - Info const*(*next)(Corpus const*, Info const*)) + Symbol const* val, + Symbol const*(*next)(Corpus const*, Symbol const*)) : corpus_(corpus) , val_(val) , next_(next) @@ -492,12 +490,12 @@ class Corpus::iterator } }; -/** Return a list of the parent symbols of the specified Info. +/** Return a list of the parent symbols of the specified Symbol. */ MRDOCS_DECL std::vector -getParents(Corpus const& C, Info const& I); +getParents(Corpus const& C, Symbol const& I); -} // clang::mrdocs +} // mrdocs -#endif +#endif // MRDOCS_API_CORPUS_HPP diff --git a/include/mrdocs/Dom.hpp b/include/mrdocs/Dom.hpp index 87b0309ad..edf7ac264 100644 --- a/include/mrdocs/Dom.hpp +++ b/include/mrdocs/Dom.hpp @@ -13,4 +13,4 @@ #include -#endif +#endif // MRDOCS_API_DOM_HPP diff --git a/include/mrdocs/Dom/Array.hpp b/include/mrdocs/Dom/Array.hpp index 76f63403e..ef394c488 100644 --- a/include/mrdocs/Dom/Array.hpp +++ b/include/mrdocs/Dom/Array.hpp @@ -17,7 +17,6 @@ #include #include -namespace clang { namespace mrdocs { namespace dom { @@ -397,7 +396,6 @@ newArray(Args&&... args) } // dom } // mrdocs -} // clang // This is here because of circular references #include diff --git a/include/mrdocs/Dom/Array.ipp b/include/mrdocs/Dom/Array.ipp index d36da06dd..7fb4b219b 100644 --- a/include/mrdocs/Dom/Array.ipp +++ b/include/mrdocs/Dom/Array.ipp @@ -11,7 +11,7 @@ #ifndef MRDOCS_API_DOM_ARRAY_IPP #define MRDOCS_API_DOM_ARRAY_IPP -namespace clang { + namespace mrdocs { namespace dom { @@ -226,6 +226,6 @@ Array operator+(Array const& lhs, Array const& rhs) } // dom } // mrdocs -} // clang + #endif diff --git a/include/mrdocs/Dom/Function.hpp b/include/mrdocs/Dom/Function.hpp index 390e38cdb..49db24d6c 100644 --- a/include/mrdocs/Dom/Function.hpp +++ b/include/mrdocs/Dom/Function.hpp @@ -17,7 +17,7 @@ #include #include -namespace clang { + namespace mrdocs { namespace dom { @@ -386,7 +386,7 @@ Function makeVariadicInvocable(F&& f) } // dom } // mrdocs -} // clang + // This is here because of circular references #include diff --git a/include/mrdocs/Dom/Function.ipp b/include/mrdocs/Dom/Function.ipp index 8947811ee..00663c3ac 100644 --- a/include/mrdocs/Dom/Function.ipp +++ b/include/mrdocs/Dom/Function.ipp @@ -11,7 +11,7 @@ #ifndef MRDOCS_API_DOM_FUNCTION_IPP #define MRDOCS_API_DOM_FUNCTION_IPP -namespace clang { + namespace mrdocs { namespace dom { @@ -257,6 +257,6 @@ call(Array const& args) const -> } // dom } // mrdocs -} // clang + #endif diff --git a/include/mrdocs/Dom/Kind.hpp b/include/mrdocs/Dom/Kind.hpp index bd94c192c..286bc9ac7 100644 --- a/include/mrdocs/Dom/Kind.hpp +++ b/include/mrdocs/Dom/Kind.hpp @@ -14,7 +14,7 @@ #include #include -namespace clang { + namespace mrdocs { namespace dom { @@ -111,6 +111,6 @@ toString(Kind const& value) } // dom } // mrdocs -} // clang + #endif diff --git a/include/mrdocs/Dom/LazyArray.hpp b/include/mrdocs/Dom/LazyArray.hpp index 06a445bbe..dd7ac690d 100644 --- a/include/mrdocs/Dom/LazyArray.hpp +++ b/include/mrdocs/Dom/LazyArray.hpp @@ -8,16 +8,16 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_LIB_DOM_LAZY_ARRAY_HPP -#define MRDOCS_LIB_DOM_LAZY_ARRAY_HPP +#ifndef MRDOCS_API_DOM_LAZYARRAY_HPP +#define MRDOCS_API_DOM_LAZYARRAY_HPP -#include #include +#include #include #include #include -namespace clang { + namespace mrdocs { namespace dom { @@ -193,6 +193,6 @@ TransformArray(T const& arr, F const& f) } // dom } // mrdocs -} // clang -#endif + +#endif // MRDOCS_API_DOM_LAZYARRAY_HPP diff --git a/include/mrdocs/Dom/LazyObject.hpp b/include/mrdocs/Dom/LazyObject.hpp index de913e8de..65767bb57 100644 --- a/include/mrdocs/Dom/LazyObject.hpp +++ b/include/mrdocs/Dom/LazyObject.hpp @@ -8,15 +8,15 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_LIB_DOM_LAZY_OBJECT_HPP -#define MRDOCS_LIB_DOM_LAZY_OBJECT_HPP +#ifndef MRDOCS_API_DOM_LAZYOBJECT_HPP +#define MRDOCS_API_DOM_LAZYOBJECT_HPP #include #include #include #include -namespace clang::mrdocs::dom { +namespace mrdocs::dom { namespace detail { @@ -456,6 +456,6 @@ LazyObject(T const& arr, Context const& context) return newObject>(arr, context); } -} // clang::mrdocs::dom +} // mrdocs::dom -#endif +#endif // MRDOCS_API_DOM_LAZYOBJECT_HPP diff --git a/include/mrdocs/Dom/Object.hpp b/include/mrdocs/Dom/Object.hpp index 1a1553405..a648a601c 100644 --- a/include/mrdocs/Dom/Object.hpp +++ b/include/mrdocs/Dom/Object.hpp @@ -18,7 +18,7 @@ #include #include -namespace clang { + namespace mrdocs { namespace dom { @@ -464,7 +464,7 @@ class MRDOCS_DECL } // dom } // mrdocs -} // clang + // This is here because of circular references #include diff --git a/include/mrdocs/Dom/Object.ipp b/include/mrdocs/Dom/Object.ipp index 4eef5b1b3..65c473891 100644 --- a/include/mrdocs/Dom/Object.ipp +++ b/include/mrdocs/Dom/Object.ipp @@ -11,7 +11,7 @@ #ifndef MRDOCS_API_DOM_OBJECT_IPP #define MRDOCS_API_DOM_OBJECT_IPP -namespace clang { + namespace mrdocs { namespace dom { @@ -144,17 +144,17 @@ Object::visit(F&& fn) const } // dom } // mrdocs -} // clang + //------------------------------------------------ template<> -struct std::formatter +struct std::formatter : std::formatter { template auto format( - clang::mrdocs::dom::Object const& value, + mrdocs::dom::Object const& value, FmtContext& ctx) const { return std::formatter::format( diff --git a/include/mrdocs/Dom/String.hpp b/include/mrdocs/Dom/String.hpp index da09155d0..60d8601da 100644 --- a/include/mrdocs/Dom/String.hpp +++ b/include/mrdocs/Dom/String.hpp @@ -11,11 +11,10 @@ #ifndef MRDOCS_API_DOM_STRING_HPP #define MRDOCS_API_DOM_STRING_HPP -#include #include #include +#include -namespace clang { namespace mrdocs { namespace dom { @@ -346,15 +345,14 @@ class MRDOCS_DECL } // dom } // mrdocs -} // clang //------------------------------------------------ template <> -struct std::formatter +struct std::formatter : std::formatter { template - auto format(clang::mrdocs::dom::String const &value, FmtContext &ctx) const { + auto format(mrdocs::dom::String const &value, FmtContext &ctx) const { return std::formatter::format(value.get(), ctx); } }; diff --git a/include/mrdocs/Dom/Value.hpp b/include/mrdocs/Dom/Value.hpp index 0511f1bf7..06d22cc67 100644 --- a/include/mrdocs/Dom/Value.hpp +++ b/include/mrdocs/Dom/Value.hpp @@ -11,21 +11,21 @@ #ifndef MRDOCS_API_DOM_VALUE_HPP #define MRDOCS_API_DOM_VALUE_HPP -#include -#include -#include +#include #include #include #include #include #include #include -#include #include +#include +#include +#include #include // BAD #include -namespace clang { + namespace mrdocs { /** Create a wrapper for a safe string. @@ -119,7 +119,7 @@ class MRDOCS_DECL friend class Array; friend class Object; - friend Value clang::mrdocs::safeString(std::string_view str); + friend Value mrdocs::safeString(std::string_view str); public: ~Value(); @@ -626,6 +626,38 @@ stringOrNull( return nullptr; } +/** Return a non-empty string, or a null. + + @param s The string to check. +*/ +inline +Value +stringOrNull( + Optional s) +{ + if(s.has_value()) + { + return {*s}; + } + return nullptr; +} + +/** Return a non-empty string, or a null. + + @param s The string to check. +*/ +inline +Value +stringOrNull( + std::string const& s) +{ + if(!s.empty()) + { + return {s}; + } + return nullptr; +} + //------------------------------------------------ /** Customization point tag. @@ -765,7 +797,8 @@ ValueFrom( { tag_invoke(ValueFromTag{}, v, static_cast(t), ctx); } - else { + else + { ValueFrom(static_cast(t), v); } } @@ -893,7 +926,7 @@ Value ValueFrom(T&& t, Context const& ctx) { dom::Value v; - ValueFrom(static_cast(t), ctx, v); + ValueFrom(std::forward(t), ctx, v); return v; } @@ -906,7 +939,7 @@ safeString(SV const& str) { } } // mrdocs -} // clang + // These are here because of circular references #include @@ -916,10 +949,10 @@ safeString(SV const& str) { //------------------------------------------------ template <> -struct std::formatter +struct std::formatter : public std::formatter { template - auto format(clang::mrdocs::dom::Value const &value, FmtContext &ctx) const { + auto format(mrdocs::dom::Value const &value, FmtContext &ctx) const { return std::formatter::format(toString(value), ctx); } }; diff --git a/include/mrdocs/Generator.hpp b/include/mrdocs/Generator.hpp index a4c4b7b33..7c7c27f4e 100644 --- a/include/mrdocs/Generator.hpp +++ b/include/mrdocs/Generator.hpp @@ -23,7 +23,7 @@ #include #include -namespace clang { + namespace mrdocs { /** Base class for documentation generators. @@ -207,6 +207,6 @@ getSinglePageFullPath( std::string_view extension); } // mrdocs -} // clang -#endif + +#endif // MRDOCS_API_GENERATOR_HPP diff --git a/include/mrdocs/Generators.hpp b/include/mrdocs/Generators.hpp index 55441e80f..79657a5e0 100644 --- a/include/mrdocs/Generators.hpp +++ b/include/mrdocs/Generators.hpp @@ -16,10 +16,10 @@ #include #include -namespace clang { + namespace mrdocs { -/** A dynamic list of Generator +/** A dynamic list of @ref Generator elements. */ class MRDOCS_VISIBLE Generators @@ -77,6 +77,6 @@ Generators const& getGenerators() noexcept; } // mrdocs -} // clang -#endif + +#endif // MRDOCS_API_GENERATORS_HPP diff --git a/include/mrdocs/Metadata.hpp b/include/mrdocs/Metadata.hpp index c13449d4f..96f133c7b 100644 --- a/include/mrdocs/Metadata.hpp +++ b/include/mrdocs/Metadata.hpp @@ -13,28 +13,26 @@ #define MRDOCS_API_METADATA_HPP #include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - +#include #include -#include #include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include -#endif +#endif // MRDOCS_API_METADATA_HPP diff --git a/include/mrdocs/Metadata/DocComment.hpp b/include/mrdocs/Metadata/DocComment.hpp new file mode 100644 index 000000000..636d7c548 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment.hpp @@ -0,0 +1,382 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_DOCCOMMENT_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_HPP + +#include +#include +#include + +namespace mrdocs { + +/** A processed documentation comment attached to a declaration. + + A complete documentation comment document consists of a + sequence of text blocks. Blocks are the top-level + structural units that might contain other blocks + or inline elements. These are analogous to markdown + blocks. + + Inline elements (text, emphasis, links, code spans, etc.) + live inside certain block types, like paragraphs or + headings. Inlines can contain other inlines + (e.g., emphasis contains text, link contains text), but + they cannot directly contain blocks. + + Some blocks contain metadata about the symbol being + documented, such as parameters and return values. These + blocks are parsed as usual, but are stored separately in the + DocComment structure. + + Each block in the document might contain: + + - No other blocks (leaf blocks) + - Other blocks (container blocks: e.g. lists) + + When they contain no other blocks, they might be: + + - Inlines only (e.g. paragraphs) + - No inlines (e.g. horizontal rule) + + Inline content elements contain other inlines + but cannot contain blocks. + */ +struct MRDOCS_DECL DocComment { + /// The list of text blocks. + std::vector> Document; + + // ---------------------- + // Symbol Metadata + + /// A brief description of the symbol. + Optional brief; + + /** The list of return type descriptions. + + Multiple return descriptions are allowed. + + The results are concatenated in the order + they appear in the source code. + */ + std::vector returns; + + /// The list of parameters. + std::vector params; + + /// The list of template parameters. + std::vector tparams; + + /// The list of exceptions. + std::vector exceptions; + + /// The list of "see also" references. + std::vector sees; + + /// The list of preconditions. + std::vector preconditions; + + /// The list of postconditions. + std::vector postconditions; + + /** The list of "relates" references. + + These references are created with the + \\relates command. + */ + std::vector relates; + + /** The list of "related" references. + + These references are the inverse of + the \\relates command. + + They are calculated automatically by + MrDocs and are rendered as + Non-Member Functions. + */ + std::vector related; + + /** Constructor. + */ + MRDOCS_DECL + DocComment() noexcept; + + /** Constructor + */ + explicit DocComment( + std::vector> blocks); + + /** Return true if this is empty + */ + bool + empty() const noexcept + { + return Document.empty() && + !brief && + returns.empty() && + params.empty() && + tparams.empty() && + exceptions.empty() && + sees.empty() && + related.empty() && + preconditions.empty() && + postconditions.empty(); + } + + auto operator<=>(DocComment const& other) const noexcept { + if (auto const cmp = Document.size() <=> other.Document.size(); + !std::is_eq(cmp)) + { + return cmp; + } + for (std::size_t i = 0; i < Document.size(); ++i) + { + if (auto cmp = CompareDerived(Document[i], other.Document[i]); + !std::is_eq(cmp)) + { + return cmp; + } + } + return std::strong_ordering::equal; + } + bool operator==(DocComment const&) const noexcept = default; + bool operator!=(DocComment const&) const noexcept; + + /** Append blocks from another DocComment to this. + */ + void + append(DocComment&& other); +}; + +inline +void merge(DocComment& I, DocComment&& other) +{ + // FIXME: this doesn't merge parameter information; + // parameters with the same name but different directions + // or descriptions end up being duplicated + if(other != I) + { + // Unconditionally extend the blocks + // since each decl may have a comment. + I.append(std::move(other)); + } +} + +/** Map the @ref DocComment to a @ref dom::Object. + + @param t The tag. + @param io The output object. + @param I The DocComment to map. + @param domCorpus The DOM corpus, or nullptr. + */ +template +void +tag_invoke( + dom::LazyObjectMapTag, + IO& io, + DocComment const& I, + DomCorpus const* domCorpus) +{ + io.defer("description", [&I, domCorpus] { + return dom::LazyArray(I.Document, domCorpus); + }); + if (I.brief && !I.brief->children.empty()) + { + io.map("brief", I.brief); + } + io.defer("returns", [&I, domCorpus] { + return dom::LazyArray(I.returns, domCorpus); + }); + io.defer("params", [&I, domCorpus] { + return dom::LazyArray(I.params, domCorpus); + }); + io.defer("tparams", [&I, domCorpus] { + return dom::LazyArray(I.tparams, domCorpus); + }); + io.defer("exceptions", [&I, domCorpus] { + return dom::LazyArray(I.exceptions, domCorpus); + }); + io.defer("sees", [&I, domCorpus] { + return dom::LazyArray(I.sees, domCorpus); + }); + io.defer("relates", [&I, domCorpus] { + return dom::LazyArray(I.relates, domCorpus); + }); + io.defer("related", [&I, domCorpus] { + return dom::LazyArray(I.related, domCorpus); + }); + io.defer("preconditions", [&I, domCorpus] { + return dom::LazyArray(I.preconditions, domCorpus); + }); + io.defer("postconditions", [&I, domCorpus] { + return dom::LazyArray(I.postconditions, domCorpus); + }); +} + +/** Return the @ref DocComment as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + DocComment const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +/** Concept to check if a type represents a DocComment node. + */ +template +concept DocCommentNode = + std::derived_from || std::derived_from; + +namespace detail { +// forward declaration to handle derived nodes +template +void +traverseImpl(Polymorphic& node, F&& func); + +// forward declaration to handle base class nodes +template +requires std::same_as || std::same_as +void +traverseImpl(T& baseNode, F&& func); + +// Traverse a derived node +template +requires(!std::same_as && !std::same_as) +void +traverseImpl(NodeTy& N, F&& func) +{ + if constexpr (!bottomUp && std::invocable) + { + func(N); + } + if constexpr (requires { N.children; }) + { + for (auto& child: N.children) + { + traverseImpl(child, func); + } + } + if constexpr (requires { N.blocks; }) + { + for (auto& block: N.blocks) + { + traverseImpl(block, func); + } + } + if constexpr (std::same_as) + { + traverseImpl(N.exception, func); + } + if constexpr (bottomUp && std::invocable) + { + func(N); + } +} + +template +void +traverseImpl(DocComment& doc, F&& func) +{ + if constexpr (!bottomUp && std::invocable) + { + func(doc); + } + for (auto& block: doc.Document) + { + traverseImpl(block, std::forward(func)); + } + if (doc.brief) + { + traverseImpl(*doc.brief, std::forward(func)); + } + for (auto& returnsEl: doc.returns) + { + traverseImpl(returnsEl, std::forward(func)); + } + for (auto& paramsEl: doc.params) + { + traverseImpl(paramsEl, std::forward(func)); + } + for (auto& tparamsEl: doc.tparams) + { + traverseImpl(tparamsEl, std::forward(func)); + } + for (auto& exceptionsEl: doc.exceptions) + { + traverseImpl(exceptionsEl, std::forward(func)); + } + for (auto& seesEl: doc.sees) + { + traverseImpl(seesEl, std::forward(func)); + } + for (auto& preconditionsEl: doc.preconditions) + { + traverseImpl(preconditionsEl, std::forward(func)); + } + for (auto& postconditionsEl: doc.postconditions) + { + traverseImpl(postconditionsEl, std::forward(func)); + } + if constexpr (bottomUp && std::invocable) + { + func(doc); + } +} + +// Traverse a reference to a base class node +template +requires std::same_as || std::same_as +void +traverseImpl(T& baseNode, F&& func) +{ + visit(baseNode, [&](NodeTy& N) { + traverseImpl(N, std::forward(func)); + }); +} + +// Traverse a polymorphic node +template +void +traverseImpl(Polymorphic& node, F&& func) +{ + traverseImpl(*node, std::forward(func)); +} +} + +template +concept DocCommentNodeTraversable = + DocCommentNode || + std::same_as || + (detail::IsPolymorphic && DocCommentNode); + +template +void +bottomUpTraverse(T& node, F&& func) +{ + detail::traverseImpl(node, std::forward(func)); +} + +template +void +topDownTraverse(T& node, F&& func) +{ + detail::traverseImpl(node, std::forward(func)); +} +} // mrdocs + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_HPP diff --git a/include/mrdocs/Metadata/DocComment/Block.hpp b/include/mrdocs/Metadata/DocComment/Block.hpp new file mode 100644 index 000000000..464da939f --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Block.hpp @@ -0,0 +1,193 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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_API_METADATA_DOCCOMMENT_BLOCK_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mrdocs::doc { +/** Visit a block. + + @param block The block to visit. + @param fn The function to call for each block. + @param args Additional arguments to pass to the function. + @return The result of calling the function. + */ +template< + std::derived_from BlockTy, + class Fn, + class... Args> +decltype(auto) +visit( + BlockTy& info, + Fn&& fn, + Args&&... args) +{ + auto visitor = makeVisitor( + info, std::forward(fn), std::forward(args)...); + switch(info.Kind) + { + #define INFO(Type) case BlockKind::Type: \ + return visitor.template visit(); +#include + default: + MRDOCS_UNREACHABLE(); + } +} + +MRDOCS_DECL +std::strong_ordering +operator<=>(Polymorphic const& lhs, Polymorphic const& rhs); + +inline bool +operator==(Polymorphic const& lhs, Polymorphic const& rhs) +{ + return lhs <=> rhs == std::strong_ordering::equal; +} + +/** Map the Polymorphic Block to a @ref dom::Object. + + @param io The output parameter to receive the dom::Object. + @param I The polymorphic Block to convert. + @param domCorpus The DomCorpus used to resolve references. + */ +template +void +tag_invoke( + dom::LazyObjectMapTag, + IO& io, + Polymorphic const& I, + DomCorpus const* domCorpus) +{ + visit(*I, [&](auto const& U) + { + tag_invoke( + dom::LazyObjectMapTag{}, + io, + U, + domCorpus); + }); +} + +/** Map the Polymorphic Block as a @ref dom::Value object. + + @param io The output parameter to receive the dom::Value. + @param I The polymorphic Block to convert. + @param domCorpus The DomCorpus used to resolve references. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + Polymorphic const& I, + DomCorpus const* domCorpus) +{ + visit(*I, [&](auto const& U) + { + tag_invoke( + dom::ValueFromTag{}, + v, + U, + domCorpus); + }); +} + + +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + Optional> const& I, + DomCorpus const* domCorpus) +{ + if (!I) + { + v = nullptr; + return; + } + MRDOCS_ASSERT(!I->valueless_after_move()); + tag_invoke(dom::ValueFromTag{}, v, *I, domCorpus); +} + +/** Removes leading whitespace from the block. + + @param el The Polymorphic to trim. + @return void + */ +inline +void +ltrim(Polymorphic& el) +{ + ltrim(*el); +} + +/** Removes trailing whitespace from the block. + + @param el The Polymorphic to trim. + @return void + */ +inline +void +rtrim(Polymorphic& el) +{ + rtrim(*el); +} + +/** Removes leading and trailing whitespace from the block. + + @param el The Polymorphic to trim. + @return void + */ +inline +void +trim(Polymorphic& el) +{ + ltrim(el); + rtrim(el); +} + +/** Determine if the inline is empty + */ +inline +bool +isEmpty(Polymorphic const& el) +{ + MRDOCS_ASSERT(!el.valueless_after_move()); + return isEmpty(*el); +} + + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_HPP diff --git a/include/mrdocs/Metadata/DocComment/Block/AdmonitionBlock.hpp b/include/mrdocs/Metadata/DocComment/Block/AdmonitionBlock.hpp new file mode 100644 index 000000000..a8584f6b2 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Block/AdmonitionBlock.hpp @@ -0,0 +1,89 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_ADMONITIONBLOCK_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_ADMONITIONBLOCK_HPP + +#include +#include +#include +#include +#include + +namespace mrdocs::doc { + +/** A block for side-notes like tips, warnings, notes + + This paragraph represents an admonition, such as + a note, tip, important, caution, or warning. + +*/ +struct AdmonitionBlock final + : BlockCommonBase + , BlockContainer +{ + /** The kind of admonition + + This is typically a string in other implementations. + */ + AdmonitionKind admonish; + + /// Optional title for the admonition + Optional> Title; + + explicit + AdmonitionBlock( + AdmonitionKind const admonish_ = AdmonitionKind::none) noexcept + : admonish(admonish_) + {} + + auto operator<=>(AdmonitionBlock const&) const = default; + bool operator==(AdmonitionBlock const&) const noexcept = default; +}; + +/** 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + AdmonitionBlock const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + tag_invoke(t, io, dynamic_cast(I), domCorpus); + io.map("admonish", I.admonish); +} + +/** Return the @ref Admonition as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + AdmonitionBlock const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_ADMONITIONBLOCK_HPP diff --git a/include/mrdocs/Metadata/DocComment/Block/AdmonitionKind.hpp b/include/mrdocs/Metadata/DocComment/Block/AdmonitionKind.hpp new file mode 100644 index 000000000..8957f04a5 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Block/AdmonitionKind.hpp @@ -0,0 +1,60 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_ADMONITIONKIND_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_ADMONITIONKIND_HPP + +#include +#include + +namespace mrdocs::doc { + +/** An admonishment style. + */ +enum class AdmonitionKind +{ + /// 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 +}; + +/** Return the name of the Admonish as a string. + */ +MRDOCS_DECL +dom::String +toString(AdmonitionKind kind) noexcept; + +/** Return the Admonish from a @ref dom::Value string. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + AdmonitionKind const kind) +{ + v = toString(kind); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_ADMONITIONKIND_HPP diff --git a/include/mrdocs/Metadata/DocComment/Block/BlockBase.hpp b/include/mrdocs/Metadata/DocComment/Block/BlockBase.hpp new file mode 100644 index 000000000..9b6c19947 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Block/BlockBase.hpp @@ -0,0 +1,295 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_BLOCKBASE_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_BLOCKBASE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mrdocs::doc { + +/* Forward declarations + */ +#define INFO(Type) struct Type##Block; +#include + +/** A piece of block content + + The top level is a list of blocks. + + There are two types of blocks: headings and paragraphs. +*/ +struct MRDOCS_DECL Block +{ + BlockKind Kind = BlockKind::Paragraph; + + virtual ~Block() = default; + + auto + operator<=>(Block const& other) const = default; + + bool + operator==(Block const& other) const noexcept = default; + + constexpr Block const& asBlock() const noexcept + { + return *this; + } + + constexpr Block& asBlock() noexcept + { + return *this; + } + + #define INFO(Type) constexpr bool is##Type() const noexcept { \ + return Kind == BlockKind::Type; \ + } +#include + +#define INFO(Type) \ + constexpr Type##Block const& as##Type() const noexcept { \ + if (Kind == BlockKind::Type) \ + return reinterpret_cast(*this); \ + MRDOCS_UNREACHABLE(); \ + } +#include + +#define INFO(Type) \ + constexpr Type##Block & as##Type() noexcept { \ + if (Kind == BlockKind::Type) \ + return reinterpret_cast(*this); \ + MRDOCS_UNREACHABLE(); \ + } +#include + +#define INFO(Type) \ + constexpr Type##Block const* as##Type##Ptr() const noexcept { \ + if (Kind == BlockKind::Type) { return reinterpret_cast(this); } \ + return nullptr; \ + } +#include + +#define INFO(Type) \ + constexpr Type##Block * as##Type##Ptr() noexcept { \ + if (Kind == BlockKind::Type) { return reinterpret_cast(this); } \ + return nullptr; \ + } +#include + +protected: + constexpr + Block() = default; + + explicit + Block(BlockKind const kind_) noexcept + : Kind(kind_) + {} +}; + +/** Base class for providing variant discriminator functions. + + This offers functions that return a boolean at + compile-time, indicating if the most-derived + class is a certain type. +*/ +template +struct BlockCommonBase : Block +{ + /** The variant discriminator constant of the most-derived class. + + It only distinguishes from `Block::kind` in that it is a constant. + + */ + static constexpr BlockKind kind_id = K; + + ~BlockCommonBase() override = default; + + #define INFO(Kind) \ + static constexpr bool is##Kind() noexcept { return K == BlockKind::Kind; } +#include + + auto operator<=>(BlockCommonBase const&) const = default; + +protected: + constexpr explicit BlockCommonBase() + : Block(K) + {} +}; + +/** 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + Block const& I, + DomCorpus const*) +{ + io.map("kind", doc::toString(I.Kind)); +} + +/** Return the @ref Block as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + Block const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +/** Removes leading whitespace from the block. + + @param el The Block to trim. + @return void + */ +MRDOCS_DECL +void +ltrim(Block& el); + +/** Removes trailing whitespace from the block. + + @param el The Block to trim. + @return void + */ +MRDOCS_DECL +void +rtrim(Block& el); + +/** Removes leading and trailing whitespace from the block. + + @param el The Block to trim. + @return void + */ +inline +void +trim(Block& el) +{ + ltrim(el); + rtrim(el); +} + +/** Determine if the inline is empty + */ +MRDOCS_DECL +bool +isEmpty(Block const& el); + + +struct MRDOCS_DECL BlockContainer +{ + std::vector> blocks; + + BlockContainer& + asBlockContainer() + { + return *this; + } + + BlockContainer const& + asBlockContainer() const + { + return *this; + } + + std::strong_ordering + operator<=>(BlockContainer const&) const; + + bool + operator==(BlockContainer const&) const = default; +}; + +template BlockTy> +void +tag_invoke( + dom::ValueFromTag, + IO& io, + BlockTy const& I, + DomCorpus const* domCorpus); + +template +void +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + BlockContainer const& I, + DomCorpus const* domCorpus) +{ + io.defer("blocks", [&I, domCorpus] { + return dom::LazyArray(I.blocks, domCorpus); + }); +} + +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + BlockContainer const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +/** Removes leading whitespace from the first text elements + + @param blocks The BlockContainer to trim. + @return void + */ +MRDOCS_DECL +void +ltrim(BlockContainer& blocks); + +/** Removes trailing whitespace from the last text elements + + @param blocks The BlockContainer to trim. + @return void + */ +MRDOCS_DECL +void +rtrim(BlockContainer& blocks); + +/** Removes leading and trailing whitespace from the text elements + + @param blocks The BlockContainer to trim. + @return void + */ +inline +void +trim(BlockContainer& blocks) +{ + ltrim(blocks); + rtrim(blocks); +} + + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_BLOCKBASE_HPP diff --git a/include/mrdocs/Metadata/DocComment/Block/BlockCommandNodes.inc b/include/mrdocs/Metadata/DocComment/Block/BlockCommandNodes.inc new file mode 100644 index 000000000..bd085dbcc --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Block/BlockCommandNodes.inc @@ -0,0 +1,24 @@ +// +// 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 INFO +#define INFO(PascalName) +#endif + +// Metadata Blocks +INFO(Param) +INFO(Postcondition) +INFO(Precondition) +INFO(Returns) +INFO(See) +INFO(Throws) +INFO(TParam) + +#undef INFO diff --git a/include/mrdocs/Metadata/DocComment/Block/BlockKind.hpp b/include/mrdocs/Metadata/DocComment/Block/BlockKind.hpp new file mode 100644 index 000000000..b5df094f9 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Block/BlockKind.hpp @@ -0,0 +1,63 @@ +// +// 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_API_METADATA_DOCCOMMENT_BLOCK_BLOCKKIND_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_BLOCKKIND_HPP + +#include +#include + +namespace mrdocs::doc { + +enum class BlockKind { + #define INFO(Type) Type, +#include +}; + +inline +dom::String +toString(BlockKind kind) noexcept +{ + switch (kind) + { + #define INFO(Type) case BlockKind::Type: return toKebabCase(#Type); +#include + } + return "Unknown"; +} + +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, BlockKind const kind) +{ + v = toString(kind); +} + +constexpr +bool +isBlockCommand(BlockKind k) noexcept +{ + switch (k) + { + #define INFO(Type) \ + case BlockKind::Type: \ + return true; +#include +#undef INFO + default: + return false; + } +} + +} // namespace mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_BLOCKKIND_HPP diff --git a/include/mrdocs/Metadata/DocComment/Block/BlockNodes.inc b/include/mrdocs/Metadata/DocComment/Block/BlockNodes.inc new file mode 100644 index 000000000..458eb8839 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Block/BlockNodes.inc @@ -0,0 +1,32 @@ +// +// 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 INFO +#define INFO(PascalName) +#endif + +// Markup Blocks +INFO(Admonition) +INFO(Brief) +INFO(Code) +INFO(Heading) +INFO(Paragraph) +INFO(List) +INFO(DefinitionList) +INFO(Quote) +INFO(ThematicBreak) +INFO(FootnoteDefinition) +INFO(Table) +INFO(Math) + +// Metadata Blocks +#include + +#undef INFO diff --git a/include/mrdocs/Metadata/DocComment/Block/BriefBlock.hpp b/include/mrdocs/Metadata/DocComment/Block/BriefBlock.hpp new file mode 100644 index 000000000..1a9e719a2 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Block/BriefBlock.hpp @@ -0,0 +1,106 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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_API_METADATA_DOCCOMMENT_BLOCK_BRIEFBLOCK_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_BRIEFBLOCK_HPP + +#include +#include +#include +#include +#include + +namespace mrdocs::doc { + +/** The brief description +*/ +struct BriefBlock final + : BlockCommonBase + , InlineContainer +{ + std::vector copiedFrom; + + BriefBlock() = default; + + BriefBlock(BriefBlock const& other) = default; + + using InlineContainer::InlineContainer; + + BriefBlock& + operator=(BriefBlock const& other) = default; + + using InlineContainer::operator=; + + auto operator<=>(BriefBlock const&) const = default; +}; + +/** 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + BriefBlock const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + tag_invoke(t, io, dynamic_cast(I), domCorpus); + io.defer("copiedFrom", [&I] { + return dom::LazyArray(I.copiedFrom); + }); +} + +/** Return the @ref Brief as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + BriefBlock const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); + + Optional o; +} + +inline +void +tag_invoke( + mrdocs::dom::ValueFromTag, + mrdocs::dom::Value& v, + Optional const& I, + DomCorpus const* domCorpus) +{ + if (!I) + { + v = nullptr; + return; + } + tag_invoke(mrdocs::dom::ValueFromTag{}, v, *I, domCorpus); +} + +static_assert(dom::HasValueFrom); +static_assert(dom::HasValueFromWithContext); + +static_assert(dom::HasValueFrom, DomCorpus const*>); +static_assert(dom::HasValueFromWithContext, DomCorpus const*>); + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_BRIEFBLOCK_HPP diff --git a/include/mrdocs/Metadata/DocComment/Block/CodeBlock.hpp b/include/mrdocs/Metadata/DocComment/Block/CodeBlock.hpp new file mode 100644 index 000000000..c32576514 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Block/CodeBlock.hpp @@ -0,0 +1,75 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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_API_METADATA_DOCCOMMENT_BLOCK_CODEBLOCK_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_CODEBLOCK_HPP + +#include +#include +#include +#include + +namespace mrdocs::doc { + +/** Preformatted source code. + */ +struct CodeBlock final + : BlockCommonBase +{ + std::string literal; + + /// Fence info string, e.g. "cpp" + std::string info; + + CodeBlock() noexcept = default; + auto operator<=>(CodeBlock const&) const = default; + bool operator==(CodeBlock const&) const noexcept = default; +}; + +/** 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + CodeBlock const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + io.map("literal", I.literal); + if (!I.info.empty()) + { + io.map("info", I.info); + } +} + +/** Return the @ref Code as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + CodeBlock const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_CODEBLOCK_HPP diff --git a/include/mrdocs/Metadata/DocComment/Block/DefinitionListBlock.hpp b/include/mrdocs/Metadata/DocComment/Block/DefinitionListBlock.hpp new file mode 100644 index 000000000..6a9ae28f1 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Block/DefinitionListBlock.hpp @@ -0,0 +1,78 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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_API_METADATA_DOCCOMMENT_BLOCK_DEFINITIONLISTBLOCK_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_DEFINITIONLISTBLOCK_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace mrdocs::doc { + +struct DefinitionListBlock final + : BlockCommonBase +{ + std::vector items; + + auto operator<=>(DefinitionListBlock const& other) const { + if (auto const cmp = items.size() <=> other.items.size(); + !std::is_eq(cmp)) + { + return cmp; + } + for (std::size_t i = 0; i < items.size(); ++i) + { + if (auto const cmp = items[i] <=> other.items[i]; + !std::is_eq(cmp)) + { + return cmp; + } + } + return std::strong_ordering::equal; + } + + bool + operator==(DefinitionListBlock const&) const noexcept = default; +}; + +template +void +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + DefinitionListBlock const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + io.defer("items", [&I, domCorpus] { + return dom::LazyArray(I.items, domCorpus); + }); +} + +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + DefinitionListBlock const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_DEFINITIONLISTBLOCK_HPP diff --git a/include/mrdocs/Metadata/DocComment/Block/DefinitionListItem.hpp b/include/mrdocs/Metadata/DocComment/Block/DefinitionListItem.hpp new file mode 100644 index 000000000..ce0dc897d --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Block/DefinitionListItem.hpp @@ -0,0 +1,67 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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_API_METADATA_DOCCOMMENT_BLOCK_DEFINITIONLISTITEM_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_DEFINITIONLISTITEM_HPP + +#include +#include +#include +#include + +namespace mrdocs::doc { + +/** An item in a definition list +*/ +struct DefinitionListItem final + : BlockContainer +{ + InlineContainer term; + + auto operator<=>(DefinitionListItem const&) const = default; + bool operator==(DefinitionListItem const&) const noexcept = default; +}; + +/** Map the @ref DefinitionListItem 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + DefinitionListItem const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, static_cast(I), domCorpus); + io.map("term", I.term); +} + +/** Return the @ref DefinitionListItem as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + DefinitionListItem const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_DEFINITIONLISTITEM_HPP diff --git a/include/mrdocs/Metadata/DocComment/Block/FootnoteDefinitionBlock.hpp b/include/mrdocs/Metadata/DocComment/Block/FootnoteDefinitionBlock.hpp new file mode 100644 index 000000000..feaa46dd5 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Block/FootnoteDefinitionBlock.hpp @@ -0,0 +1,70 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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_API_METADATA_DOCCOMMENT_BLOCK_FOOTNOTEDEFINITIONBLOCK_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_FOOTNOTEDEFINITIONBLOCK_HPP + +#include +#include +#include +#include + +namespace mrdocs::doc { + +/** Preformatted source code. + */ +struct FootnoteDefinitionBlock final + : BlockCommonBase + , BlockContainer +{ + std::string label; + + FootnoteDefinitionBlock() noexcept = default; + auto operator<=>(FootnoteDefinitionBlock const&) const = default; + bool operator==(FootnoteDefinitionBlock const&) const noexcept = default; +}; + +/** Map the @ref FootnoteDefinition 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + FootnoteDefinitionBlock const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + tag_invoke(t, io, dynamic_cast(I), domCorpus); + io.map("label", I.label); +} + +/** Return the @ref FootnoteDefinition as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + FootnoteDefinitionBlock const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_FOOTNOTEDEFINITIONBLOCK_HPP diff --git a/include/mrdocs/Metadata/DocComment/Block/HeadingBlock.hpp b/include/mrdocs/Metadata/DocComment/Block/HeadingBlock.hpp new file mode 100644 index 000000000..15ab196d5 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Block/HeadingBlock.hpp @@ -0,0 +1,69 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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_API_METADATA_DOCCOMMENT_BLOCK_HEADINGBLOCK_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_HEADINGBLOCK_HPP + +#include +#include +#include + +namespace mrdocs::doc { + +/** A manually specified section heading. +*/ +struct HeadingBlock final + : BlockCommonBase + , InlineContainer +{ + unsigned level = 1; // 1 to 6 + + using InlineContainer::InlineContainer; + auto operator<=>(HeadingBlock const&) const = default; + bool operator==(HeadingBlock const&) const noexcept = default; +}; + +/** 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + HeadingBlock const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + tag_invoke(t, io, dynamic_cast(I), domCorpus); + io.map("level", I.level); +} + +/** Return the @ref Heading as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + HeadingBlock const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_HEADINGBLOCK_HPP diff --git a/include/mrdocs/Metadata/DocComment/Block/ListBlock.hpp b/include/mrdocs/Metadata/DocComment/Block/ListBlock.hpp new file mode 100644 index 000000000..193bdbc41 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Block/ListBlock.hpp @@ -0,0 +1,91 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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_API_METADATA_DOCCOMMENT_BLOCK_LISTBLOCK_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_LISTBLOCK_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace mrdocs::doc { + +/** A list of list items +*/ +struct ListBlock final + : BlockCommonBase +{ + std::vector items; + ListKind listKind = ListKind::Unordered; + + auto operator<=>(ListBlock const& other) const { + if (auto const cmp = items.size() <=> other.items.size(); + !std::is_eq(cmp)) + { + return cmp; + } + for (std::size_t i = 0; i < items.size(); ++i) + { + if (auto const cmp = items[i] <=> other.items[i]; + !std::is_eq(cmp)) + { + return cmp; + } + } + return std::strong_ordering::equal; + } + + bool + operator==(ListBlock const&) const noexcept = default; +}; + +/** 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + ListBlock const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + io.defer("items", [&I, domCorpus] { + return dom::LazyArray(I.items, domCorpus); + }); + io.map("listKind", toString(I.listKind)); +} + +/** Return the @ref UnorderedList as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + ListBlock const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_LISTBLOCK_HPP diff --git a/include/mrdocs/Metadata/DocComment/Block/ListItem.hpp b/include/mrdocs/Metadata/DocComment/Block/ListItem.hpp new file mode 100644 index 000000000..14693d3a3 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Block/ListItem.hpp @@ -0,0 +1,64 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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_API_METADATA_DOCCOMMENT_BLOCK_LISTITEM_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_LISTITEM_HPP + +#include +#include +#include +#include + +namespace mrdocs::doc { + +/** An item in a list +*/ +struct ListItem final + : BlockContainer +{ + auto operator<=>(ListItem const&) const = default; + bool operator==(ListItem const&) const noexcept = default; +}; + +/** 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + ListItem const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, static_cast(I), domCorpus); +} + +/** Return the @ref ListItem as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + ListItem const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_LISTITEM_HPP diff --git a/include/mrdocs/Metadata/DocComment/Block/ListKind.hpp b/include/mrdocs/Metadata/DocComment/Block/ListKind.hpp new file mode 100644 index 000000000..6b56c766e --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Block/ListKind.hpp @@ -0,0 +1,49 @@ +// +// 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_API_METADATA_DOCCOMMENT_BLOCK_LISTKIND_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_LISTKIND_HPP + +#include +#include + +namespace mrdocs { + +enum class ListKind { + Unordered, + Ordered +}; + +inline +dom::String +toString(ListKind kind) noexcept +{ + switch (kind) + { + case ListKind::Unordered: + return "unordered"; + case ListKind::Ordered: + return "ordered"; + } + return "Unknown"; +} + +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, ListKind const kind) +{ + v = toString(kind); +} + +} // mrdocs + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_LISTKIND_HPP diff --git a/include/mrdocs/Metadata/DocComment/Block/MathBlock.hpp b/include/mrdocs/Metadata/DocComment/Block/MathBlock.hpp new file mode 100644 index 000000000..63cc52c51 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Block/MathBlock.hpp @@ -0,0 +1,71 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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_API_METADATA_DOCCOMMENT_BLOCK_MATHBLOCK_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_MATHBLOCK_HPP + +#include +#include +#include + +namespace mrdocs::doc { + +/** A block of LaTeX math + + A block of LaTeX math, typically between + $$ … $$ or fenced with "math". + */ +struct MathBlock final + : BlockCommonBase +{ + /// Raw TeX math source + std::string literal; + + MathBlock(MathBlock const& other) = default; + MathBlock& operator=(MathBlock const& other) = default; + auto operator<=>(MathBlock const&) const = default; +}; + +/** 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + MathBlock const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + io.map("literal", I.literal); +} + +/** Return the @ref Brief as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + MathBlock const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_MATHBLOCK_HPP diff --git a/include/mrdocs/Metadata/DocComment/Block/ParagraphBlock.hpp b/include/mrdocs/Metadata/DocComment/Block/ParagraphBlock.hpp new file mode 100644 index 000000000..48bc0daa4 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Block/ParagraphBlock.hpp @@ -0,0 +1,70 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_PARAGRAPHBLOCK_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_PARAGRAPHBLOCK_HPP + +#include +#include +#include +#include + +namespace mrdocs::doc { + +/** A sequence of text nodes. +*/ +struct ParagraphBlock + : BlockCommonBase + , InlineContainer +{ + ~ParagraphBlock() override = default; + ParagraphBlock() noexcept = default; + + auto operator<=>(ParagraphBlock const&) const = default; +}; + +/** 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + ParagraphBlock const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + tag_invoke(t, io, static_cast(I), domCorpus); +} + +/** Return the @ref Paragraph as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + ParagraphBlock const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_PARAGRAPHBLOCK_HPP diff --git a/include/mrdocs/Metadata/DocComment/Block/ParamBlock.hpp b/include/mrdocs/Metadata/DocComment/Block/ParamBlock.hpp new file mode 100644 index 000000000..26e7d2261 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Block/ParamBlock.hpp @@ -0,0 +1,94 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_PARAMBLOCK_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_PARAMBLOCK_HPP + +#include +#include +#include +#include +#include + +namespace mrdocs::doc { + +/** Documentation for a function parameter +*/ +struct ParamBlock final + : BlockCommonBase + , InlineContainer +{ + std::string name; + ParamDirection direction = ParamDirection::none; + + using InlineContainer::InlineContainer; + + ParamBlock(InlineContainer const& other) + : InlineContainer(other) + {} + + ParamBlock(InlineContainer&& other) noexcept + : InlineContainer(std::move(other)) + {} + + ParamBlock( + std::string_view name, + std::string_view text, + ParamDirection direction = ParamDirection::none) + : InlineContainer(text) + , name(name) + , direction(direction) + {} + + auto operator<=>(ParamBlock const&) const = default; + bool operator==(ParamBlock const&) + const noexcept = default; +}; + +/** 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + ParamBlock const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + tag_invoke(t, io, dynamic_cast(I), domCorpus); + io.map("name", I.name); + io.map("direction", I.direction); +} + +/** Return the @ref Param as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + ParamBlock const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_PARAMBLOCK_HPP diff --git a/include/mrdocs/Metadata/DocComment/Block/ParamDirection.hpp b/include/mrdocs/Metadata/DocComment/Block/ParamDirection.hpp new file mode 100644 index 000000000..89f66f995 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Block/ParamDirection.hpp @@ -0,0 +1,56 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_PARAMDIRECTION_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_PARAMDIRECTION_HPP + +#include +#include + +namespace mrdocs::doc { + +/** Parameter pass direction. +*/ +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 +}; + +/** Return the name of the ParamDirection as a string. + */ +MRDOCS_DECL +dom::String +toString(ParamDirection kind) noexcept; + +/** Return the ParamDirection from a @ref dom::Value string. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + ParamDirection const kind) +{ + v = toString(kind); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_PARAMDIRECTION_HPP diff --git a/include/mrdocs/Metadata/DocComment/Block/PostconditionBlock.hpp b/include/mrdocs/Metadata/DocComment/Block/PostconditionBlock.hpp new file mode 100644 index 000000000..f882c4686 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Block/PostconditionBlock.hpp @@ -0,0 +1,70 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_POSTCONDITIONBLOCK_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_POSTCONDITIONBLOCK_HPP + +#include +#include +#include + +namespace mrdocs::doc { + +struct PostconditionBlock + : BlockCommonBase + , InlineContainer +{ + using InlineContainer::InlineContainer; + auto operator<=>(PostconditionBlock const&) const = default; + bool operator==(PostconditionBlock const&) const noexcept = default; +}; + +/** 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + PostconditionBlock const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + tag_invoke(t, io, dynamic_cast(I), domCorpus); +} + +/** 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 +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + PostconditionBlock const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_POSTCONDITIONBLOCK_HPP diff --git a/include/mrdocs/Metadata/DocComment/Block/PreconditionBlock.hpp b/include/mrdocs/Metadata/DocComment/Block/PreconditionBlock.hpp new file mode 100644 index 000000000..fbd9a914e --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Block/PreconditionBlock.hpp @@ -0,0 +1,70 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_PRECONDITIONBLOCK_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_PRECONDITIONBLOCK_HPP + +#include +#include +#include + +namespace mrdocs::doc { + +struct PreconditionBlock final + : BlockCommonBase + , InlineContainer +{ + using InlineContainer::InlineContainer; + auto operator<=>(PreconditionBlock const&) const = default; + bool operator==(PreconditionBlock const&) const noexcept = default; +}; + +/** 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + PreconditionBlock const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + tag_invoke(t, io, dynamic_cast(I), domCorpus); +} + +/** 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 +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + PreconditionBlock const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_PRECONDITIONBLOCK_HPP diff --git a/include/mrdocs/Metadata/DocComment/Block/QuoteBlock.hpp b/include/mrdocs/Metadata/DocComment/Block/QuoteBlock.hpp new file mode 100644 index 000000000..53a7c6bd3 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Block/QuoteBlock.hpp @@ -0,0 +1,68 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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_API_METADATA_DOCCOMMENT_BLOCK_QUOTEBLOCK_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_QUOTEBLOCK_HPP + +#include +#include +#include +#include +#include +#include + +namespace mrdocs::doc { + +/** A list of list items +*/ +struct QuoteBlock final + : BlockCommonBase + , BlockContainer +{ + auto operator<=>(QuoteBlock const&) const = default; + bool operator==(QuoteBlock const&) const noexcept = default; +}; + +/** Map the @ref QuoteBlock 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + QuoteBlock const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + tag_invoke(t, io, dynamic_cast(I), domCorpus); +} + +/** Return the @ref QuoteBlock as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + QuoteBlock const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_QUOTEBLOCK_HPP diff --git a/include/mrdocs/Metadata/DocComment/Block/ReturnsBlock.hpp b/include/mrdocs/Metadata/DocComment/Block/ReturnsBlock.hpp new file mode 100644 index 000000000..5704e1688 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Block/ReturnsBlock.hpp @@ -0,0 +1,76 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_RETURNSBLOCK_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_RETURNSBLOCK_HPP + +#include +#include +#include +#include + +namespace mrdocs::doc { + +/** Documentation for a function return type +*/ +struct ReturnsBlock final + : BlockCommonBase + , InlineContainer +{ + using InlineContainer::InlineContainer; + ReturnsBlock(ReturnsBlock const&) = default; + ReturnsBlock(ReturnsBlock&&) noexcept = default; + ReturnsBlock(InlineContainer const& other) : InlineContainer(other) {} + ReturnsBlock(InlineContainer&& other) noexcept : InlineContainer(other) {} + ~ReturnsBlock() override = default; + ReturnsBlock& operator=(ReturnsBlock const&) = default; + ReturnsBlock& operator=(ReturnsBlock&&) noexcept = default; + auto operator<=>(ReturnsBlock const&) const = default; + bool operator==(ReturnsBlock const&) const noexcept = default; +}; + +/** 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + ReturnsBlock const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + tag_invoke(t, io, dynamic_cast(I), domCorpus); +} + +/** Return the @ref Returns as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + ReturnsBlock const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_RETURNSBLOCK_HPP diff --git a/include/mrdocs/Metadata/DocComment/Block/SeeBlock.hpp b/include/mrdocs/Metadata/DocComment/Block/SeeBlock.hpp new file mode 100644 index 000000000..09f73bb29 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Block/SeeBlock.hpp @@ -0,0 +1,69 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_SEEBLOCK_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_SEEBLOCK_HPP + +#include +#include +#include +#include + +namespace mrdocs::doc { + +/** A @see paragraph +*/ +struct SeeBlock final + : BlockCommonBase + , InlineContainer +{ + using InlineContainer::InlineContainer; + auto operator<=>(SeeBlock const&) const = default; + bool operator==(SeeBlock const&) const noexcept = default; +}; + +/** 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + SeeBlock const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + tag_invoke(t, io, dynamic_cast(I), domCorpus); +} + +/** Return the @ref See as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + SeeBlock const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_SEEBLOCK_HPP diff --git a/include/mrdocs/Metadata/DocComment/Block/TParamBlock.hpp b/include/mrdocs/Metadata/DocComment/Block/TParamBlock.hpp new file mode 100644 index 000000000..124d1007b --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Block/TParamBlock.hpp @@ -0,0 +1,72 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_TPARAMBLOCK_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_TPARAMBLOCK_HPP + +#include +#include +#include +#include + +namespace mrdocs::doc { + +/** Documentation for a template parameter +*/ +struct TParamBlock final + : BlockCommonBase + , InlineContainer +{ + std::string name; + + using InlineContainer::InlineContainer; + auto operator<=>(TParamBlock const&) const = default; + bool operator==(TParamBlock const&) const noexcept = default; +}; + +/** 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + TParamBlock const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + tag_invoke(t, io, dynamic_cast(I), domCorpus); + io.map("name", I.name); +} + +/** Return the @ref TParam as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + TParamBlock const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_TPARAMBLOCK_HPP diff --git a/include/mrdocs/Metadata/DocComment/Block/TableAlignmentKind.hpp b/include/mrdocs/Metadata/DocComment/Block/TableAlignmentKind.hpp new file mode 100644 index 000000000..8dd01f686 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Block/TableAlignmentKind.hpp @@ -0,0 +1,55 @@ +// +// 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_API_METADATA_DOCCOMMENT_BLOCK_TABLEALIGNMENTKIND_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_TABLEALIGNMENTKIND_HPP + +#include +#include + +namespace mrdocs { + +enum class TableAlignmentKind { + None, + Left, + Center, + Right, +}; + +inline +dom::String +toString(TableAlignmentKind kind) noexcept +{ + switch (kind) + { + case TableAlignmentKind::None: + return "none"; + case TableAlignmentKind::Left: + return "left"; + case TableAlignmentKind::Center: + return "center"; + case TableAlignmentKind::Right: + return "right"; + } + return "unknown"; +} + +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, TableAlignmentKind const kind) +{ + v = toString(kind); +} + +} // mrdocs + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_TABLEALIGNMENTKIND_HPP diff --git a/include/mrdocs/Metadata/DocComment/Block/TableBlock.hpp b/include/mrdocs/Metadata/DocComment/Block/TableBlock.hpp new file mode 100644 index 000000000..3ccb261fd --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Block/TableBlock.hpp @@ -0,0 +1,92 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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_API_METADATA_DOCCOMMENT_BLOCK_TABLEBLOCK_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_TABLEBLOCK_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace mrdocs::doc { + +/** A table block +*/ +struct TableBlock final: BlockCommonBase +{ + std::vector Alignments; + std::vector items; + + auto operator<=>(TableBlock const& other) const { + if (auto const cmp = items.size() <=> other.items.size(); + !std::is_eq(cmp)) + { + return cmp; + } + for (std::size_t i = 0; i < items.size(); ++i) + { + if (auto const cmp = items[i] <=> other.items[i]; + !std::is_eq(cmp)) + { + return cmp; + } + } + return std::strong_ordering::equal; + } + + bool + operator==(TableBlock const&) const noexcept = default; +}; + +/** Map the @ref UnorderedTable 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + TableBlock const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + io.defer("alignments", [&I, domCorpus] { + return dom::LazyArray(I.Alignments, domCorpus); + }); + io.defer("items", [&I, domCorpus] { + return dom::LazyArray(I.items, domCorpus); + }); +} + +/** Return the @ref UnorderedTable as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + TableBlock const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_TABLEBLOCK_HPP diff --git a/include/mrdocs/Metadata/DocComment/Block/TableCell.hpp b/include/mrdocs/Metadata/DocComment/Block/TableCell.hpp new file mode 100644 index 000000000..529329f72 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Block/TableCell.hpp @@ -0,0 +1,64 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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_API_METADATA_DOCCOMMENT_BLOCK_TABLECELL_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_TABLECELL_HPP + +#include +#include +#include +#include + +namespace mrdocs::doc { + +/** A cell in a table +*/ +struct TableCell final + : InlineContainer +{ + auto operator<=>(TableCell const&) const = default; + bool + operator==(TableCell const&) const noexcept = default; +}; + +/** Map the @ref TableCell 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + TableCell const& I, + DomCorpus const* domCorpus) +{ +} + +/** Return the @ref TableCell as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + TableCell const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_TABLECELL_HPP diff --git a/include/mrdocs/Metadata/DocComment/Block/TableRow.hpp b/include/mrdocs/Metadata/DocComment/Block/TableRow.hpp new file mode 100644 index 000000000..76f11d3f8 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Block/TableRow.hpp @@ -0,0 +1,72 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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_API_METADATA_DOCCOMMENT_BLOCK_TABLEROW_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_TABLEROW_HPP + +#include +#include +#include +#include +#include +#include + +namespace mrdocs::doc { + +/** An item in a list +*/ +struct TableRow final +{ + bool is_header = false; + std::vector Cells; + + auto operator<=>(TableRow const&) const = default; + bool + operator==(TableRow const&) const noexcept = default; +}; + +/** Map the @ref TableRow 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + TableRow const& I, + DomCorpus const* domCorpus) +{ + io.map("is_header", I.is_header); + io.defer("cells", [&] { + return dom::LazyArray(I.Cells, domCorpus); + }); +} + +/** Return the @ref TableRow as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + TableRow const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_TABLEROW_HPP diff --git a/include/mrdocs/Metadata/DocComment/Block/ThematicBreakBlock.hpp b/include/mrdocs/Metadata/DocComment/Block/ThematicBreakBlock.hpp new file mode 100644 index 000000000..a5a9baf36 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Block/ThematicBreakBlock.hpp @@ -0,0 +1,65 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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_API_METADATA_DOCCOMMENT_BLOCK_THEMATICBREAKBLOCK_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_THEMATICBREAKBLOCK_HPP + +#include +#include +#include +#include + +namespace mrdocs::doc { + +/** The brief description +*/ +struct ThematicBreakBlock final + : BlockCommonBase +{ + ThematicBreakBlock(ThematicBreakBlock const& other) = default; + ThematicBreakBlock& operator=(ThematicBreakBlock const& other) = default; + auto operator<=>(ThematicBreakBlock const&) const = default; +}; + +/** 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + ThematicBreakBlock const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); +} + +/** Return the @ref Brief as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + ThematicBreakBlock const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_THEMATICBREAKBLOCK_HPP diff --git a/include/mrdocs/Metadata/DocComment/Block/ThrowsBlock.hpp b/include/mrdocs/Metadata/DocComment/Block/ThrowsBlock.hpp new file mode 100644 index 000000000..7b7482306 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Block/ThrowsBlock.hpp @@ -0,0 +1,71 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_THROWSBLOCK_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_THROWSBLOCK_HPP + +#include +#include +#include +#include + +namespace mrdocs::doc { + +/** Documentation for a function parameter +*/ +struct ThrowsBlock final + : BlockCommonBase + , InlineContainer +{ + ReferenceInline exception; + using InlineContainer::InlineContainer; + auto operator<=>(ThrowsBlock const&) const = default; + bool operator==(ThrowsBlock const&) const noexcept = default; +}; + +/** 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + ThrowsBlock const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + tag_invoke(t, io, dynamic_cast(I), domCorpus); + io.map("exception", I.exception); +} + +/** Return the @ref Throws as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + ThrowsBlock const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_BLOCK_THROWSBLOCK_HPP diff --git a/include/mrdocs/Metadata/DocComment/Inline.hpp b/include/mrdocs/Metadata/DocComment/Inline.hpp new file mode 100644 index 000000000..fe0b503c0 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Inline.hpp @@ -0,0 +1,167 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_DOCCOMMENT_INLINE_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_INLINE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace mrdocs::doc { + +/** Visit an inline. + + @param el The inline element to visit. + @param fn The function to call for each inline. + @param args Additional arguments to pass to the function. + @return The result of calling the function. + */ +template< + class InlineTy, + class Fn, + class... Args> + requires std::derived_from +decltype(auto) +visit( + InlineTy& el, + Fn&& fn, + Args&&... args) +{ + auto visitor = makeVisitor( + el, std::forward(fn), + std::forward(args)...); + switch(el.Kind) + { + #define INFO(Type) case InlineKind::Type: \ + return visitor.template visit(); +#include + default: + MRDOCS_UNREACHABLE(); + } +} + +/** Traverse a list of inlines. + + @param list The list of texts to traverse. + @param f The function to call for each text. + @param args Additional arguments to pass to the function. + */ +template +requires std::derived_from +void traverse( + std::vector> const& list, + F&& f, Args&&... args) +{ + for(auto const& el : list) + visit(*el, + std::forward(f), + std::forward(args)...); +} + +/** Map the Polymorphic Inline 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 InlineTy> +void +tag_invoke( + dom::ValueFromTag, + IO& io, + InlineTy const& I, + DomCorpus const* domCorpus) +{ + visit(*I, [&](auto const& U) + { + tag_invoke( + dom::ValueFromTag{}, + io, + U, + domCorpus); + }); +} + +MRDOCS_DECL +std::strong_ordering +operator<=>(Polymorphic const& lhs, Polymorphic const& rhs); + +inline +bool +operator==(Polymorphic const& lhs, Polymorphic const& rhs) { + return std::is_eq(lhs <=> rhs); +} + +/** Removes leading whitespace from the inline element. + + @param el The Polymorphic to trim. + @return void + */ +MRDOCS_DECL +void +ltrim(Polymorphic& el); + +/** Removes trailing whitespace from the inline element. + + @param el The Polymorphic to trim. + @return void + */ +MRDOCS_DECL +void +rtrim(Polymorphic& el); + +/** Removes leading and trailing whitespace from the inline element. + + @param el The Polymorphic to trim. + @return void + */ +inline +void +trim(Polymorphic& el) +{ + ltrim(el); + rtrim(el); +} + +/** Determine if the inline is empty + + This determines if the inline is considered to + have no content for the purposes of trimming. + + */ +MRDOCS_DECL +bool +isEmpty(Polymorphic const& el); + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_INLINE_HPP diff --git a/include/mrdocs/Metadata/DocComment/Inline/CodeInline.hpp b/include/mrdocs/Metadata/DocComment/Inline/CodeInline.hpp new file mode 100644 index 000000000..25ae621d6 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Inline/CodeInline.hpp @@ -0,0 +1,67 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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_API_METADATA_DOCCOMMENT_INLINE_CODEINLINE_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_INLINE_CODEINLINE_HPP + +#include +#include +#include +#include + +namespace mrdocs::doc { + +/** A piece of styled text. +*/ +struct CodeInline final + : InlineCommonBase + , InlineContainer +{ + using InlineContainer::InlineContainer; + auto operator<=>(CodeInline const&) const = default; + bool operator==(CodeInline const&) const noexcept = default; +}; + +/** 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + CodeInline const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + tag_invoke(t, io, dynamic_cast(I), domCorpus); +} + +/** Return the @ref Code as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + CodeInline const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_INLINE_CODEINLINE_HPP diff --git a/include/mrdocs/Metadata/DocComment/Inline/CopyDetailsInline.hpp b/include/mrdocs/Metadata/DocComment/Inline/CopyDetailsInline.hpp new file mode 100644 index 000000000..36c4ffa96 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Inline/CopyDetailsInline.hpp @@ -0,0 +1,78 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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_API_METADATA_DOCCOMMENT_INLINE_COPYDETAILSINLINE_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_INLINE_COPYDETAILSINLINE_HPP + +#include +#include +#include +#include + +namespace mrdocs::doc { + +/** Documentation copied from another symbol. +*/ +struct CopyDetailsInline final + : InlineCommonBase +{ + std::string string; + SymbolID id = SymbolID::invalid; + + CopyDetailsInline(std::string string_ = std::string()) noexcept + : string(std::move(string_)) + { + } + + auto operator<=>(CopyDetailsInline const&) const = default; + bool operator==(CopyDetailsInline const&) const noexcept = default; +}; + +/** 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + CopyDetailsInline const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + io.map("string", I.string); + io.map("symbol", I.id); +} + +/** 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 +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + CopyDetailsInline const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_INLINE_COPYDETAILSINLINE_HPP diff --git a/include/mrdocs/Metadata/DocComment/Inline/EmphInline.hpp b/include/mrdocs/Metadata/DocComment/Inline/EmphInline.hpp new file mode 100644 index 000000000..1d78383cc --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Inline/EmphInline.hpp @@ -0,0 +1,67 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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_API_METADATA_DOCCOMMENT_INLINE_EMPHINLINE_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_INLINE_EMPHINLINE_HPP + +#include +#include +#include +#include + +namespace mrdocs::doc { + +/** A piece of styled text. +*/ +struct EmphInline final + : InlineCommonBase + , InlineContainer +{ + using InlineContainer::InlineContainer; + auto operator<=>(EmphInline const&) const = default; + bool operator==(EmphInline const&) const noexcept = default; +}; + +/** Map the @ref Emph 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + EmphInline const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + tag_invoke(t, io, dynamic_cast(I), domCorpus); +} + +/** Return the @ref Emph as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + EmphInline const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_INLINE_EMPHINLINE_HPP diff --git a/include/mrdocs/Metadata/DocComment/Inline/FootnoteReferenceInline.hpp b/include/mrdocs/Metadata/DocComment/Inline/FootnoteReferenceInline.hpp new file mode 100644 index 000000000..c8fe61f5b --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Inline/FootnoteReferenceInline.hpp @@ -0,0 +1,68 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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_API_METADATA_DOCCOMMENT_INLINE_FOOTNOTEREFERENCEINLINE_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_INLINE_FOOTNOTEREFERENCEINLINE_HPP + +#include +#include +#include +#include + +namespace mrdocs::doc { + +/** A reference to a symbol. + + In markdown, this is represented as "[^label]". +*/ +struct FootnoteReferenceInline + : InlineCommonBase +{ + std::string label; + auto operator<=>(FootnoteReferenceInline const&) const = default; + bool operator==(FootnoteReferenceInline const&) const noexcept = default; +}; + +/** Map the @ref FootnoteReference 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + FootnoteReferenceInline const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + io.map("label", I.label); +} + +/** Return the @ref FootnoteReference as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + FootnoteReferenceInline const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_INLINE_FOOTNOTEREFERENCEINLINE_HPP diff --git a/include/mrdocs/Metadata/DocComment/Inline/HighlightInline.hpp b/include/mrdocs/Metadata/DocComment/Inline/HighlightInline.hpp new file mode 100644 index 000000000..ca1ab3ab2 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Inline/HighlightInline.hpp @@ -0,0 +1,66 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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_API_METADATA_DOCCOMMENT_INLINE_HIGHLIGHTINLINE_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_INLINE_HIGHLIGHTINLINE_HPP + +#include +#include +#include +#include + +namespace mrdocs::doc { + +/** A piece of highlighted text. +*/ +struct HighlightInline final + : InlineCommonBase + , InlineContainer +{ + auto operator<=>(HighlightInline const&) const = default; + bool operator==(HighlightInline const&) const noexcept = default; +}; + +/** Map the @ref Highlight 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + HighlightInline const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + tag_invoke(t, io, dynamic_cast(I), domCorpus); +} + +/** Return the @ref Highlight as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + HighlightInline const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_INLINE_HIGHLIGHTINLINE_HPP diff --git a/include/mrdocs/Metadata/DocComment/Inline/ImageInline.hpp b/include/mrdocs/Metadata/DocComment/Inline/ImageInline.hpp new file mode 100644 index 000000000..5d4695e65 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Inline/ImageInline.hpp @@ -0,0 +1,71 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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_API_METADATA_DOCCOMMENT_INLINE_IMAGEINLINE_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_INLINE_IMAGEINLINE_HPP + +#include +#include +#include +#include + +namespace mrdocs::doc { + +/** An image. +*/ +struct ImageInline final + : InlineCommonBase + , InlineContainer +{ + std::string src; + std::string alt; + + auto operator<=>(ImageInline const&) const = default; + bool operator==(ImageInline const&) const noexcept = default; +}; + +/** Map the @ref Image 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + ImageInline const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + tag_invoke(t, io, dynamic_cast(I), domCorpus); + io.map("src", I.src); + io.map("alt", I.alt); +} + +/** Return the @ref Image as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + ImageInline const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_INLINE_IMAGEINLINE_HPP diff --git a/include/mrdocs/Metadata/DocComment/Inline/InlineBase.hpp b/include/mrdocs/Metadata/DocComment/Inline/InlineBase.hpp new file mode 100644 index 000000000..09b36ea1f --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Inline/InlineBase.hpp @@ -0,0 +1,491 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_DOCCOMMENT_INLINE_INLINEBASE_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_INLINE_INLINEBASE_HPP + +#include +#include +#include +#include +#include +#include + +namespace mrdocs::doc { + +/* Forward declarations + */ +#define INFO(Type) struct Type##Inline; +#include + +/** A Node containing a string of text. + + There will be no newlines in the text. Otherwise, + this would be represented as multiple text nodes + within a Paragraph node. +*/ +struct MRDOCS_DECL Inline +{ + InlineKind Kind = InlineKind::Text; + + virtual ~Inline() = default; + + auto operator<=>(Inline const&) const = default; + bool operator==(Inline const&) const noexcept = default; + + constexpr Inline const& asInline() const noexcept + { + return *this; + } + + constexpr Inline& asInline() noexcept + { + return *this; + } + + #define INFO(Type) constexpr bool is##Type() const noexcept { \ + return Kind == InlineKind::Type; \ + } +#include + +#define INFO(Type) \ + constexpr Type##Inline const& as##Type() const noexcept { \ + if (Kind == InlineKind::Type) \ + return reinterpret_cast(*this); \ + MRDOCS_UNREACHABLE(); \ + } +#include + +#define INFO(Type) \ + constexpr Type##Inline & as##Type() noexcept { \ + if (Kind == InlineKind::Type) \ + return reinterpret_cast(*this); \ + MRDOCS_UNREACHABLE(); \ + } +#include + +#define INFO(Type) \ + constexpr Type##Inline const* as##Type##Ptr() const noexcept { \ + if (Kind == InlineKind::Type) { return reinterpret_cast(this); } \ + return nullptr; \ + } +#include + +#define INFO(Type) \ + constexpr Type##Inline * as##Type##Ptr() noexcept { \ + if (Kind == InlineKind::Type) { return reinterpret_cast(this); } \ + return nullptr; \ + } +#include + +protected: + constexpr Inline() noexcept = default; + + Inline( + InlineKind kind_) + : Kind(kind_) + { + } +}; + +/** Base class for providing variant discriminator functions. + + This offers functions that return a boolean at + compile-time, indicating if the most-derived + class is a certain type. +*/ +template +struct InlineCommonBase : Inline +{ + /** The variant discriminator constant of the most-derived class. + + It only distinguishes from `Inline::kind` in that it is a constant. + + */ + static constexpr InlineKind kind_id = K; + + virtual ~InlineCommonBase() override = default; + + #define INFO(Kind) \ + static constexpr bool is##Kind() noexcept { return K == InlineKind::Kind; } +#include + + auto operator<=>(InlineCommonBase const&) const = default; + +protected: + constexpr explicit InlineCommonBase() + : Inline(K) + {} +}; + + +/** Map the @ref Inline 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + Inline const& I, + DomCorpus const* domCorpus) +{ + io.map("kind", toString(I.Kind)); +} + +/** Return the @ref Inline as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + Inline const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +/** Get the plain text representation of an inline element tree. + + This strips all formatting and returns just the text content. + + @param in The input inline element. + @param dst The output string to append to. + */ +MRDOCS_DECL +void +getAsPlainText(doc::Inline const& in, std::string& dst); + +/** Get the plain text representation of an inline element tree. + + This strips all formatting and returns just the text content. + + @param in The input inline element. + @return The flattened plain text. + */ +inline +std::string +getAsPlainText(doc::Inline const& in) +{ + std::string dst; + getAsPlainText(in, dst); + return dst; +} + +/// An internal node in the inline element tree +struct MRDOCS_DECL InlineContainer +{ + std::vector> children; + + virtual ~InlineContainer() = default; + + InlineContainer() = default; + + InlineContainer(InlineContainer const&) = default; + + InlineContainer(InlineContainer&&) noexcept = default; + + /// Construct an InlineContainer with a single TextInline child. + explicit + InlineContainer(std::string_view text); + + /// Construct an InlineContainer with a single TextInline child. + explicit + InlineContainer(char const* text) + : InlineContainer(std::string_view(text)) + {} + + /// Construct an InlineContainer with a single TextInline child. + explicit + InlineContainer(std::string const& text); + + /// Construct an InlineContainer with a single TextInline child. + explicit + InlineContainer(std::string&& text); + + InlineContainer& + operator=(InlineContainer const&) = default; + + InlineContainer& + operator=(InlineContainer&&) noexcept = default; + + /// Assign an InlineContainer with a single TextInline child. + InlineContainer& + operator=(std::string_view text); + + /// Helper function so that derived classes can get a reference to + /// the base class. + InlineContainer& + asInlineContainer() + { + return *this; + } + + /// @copydoc asInlineContainer + InlineContainer const& + asInlineContainer() const + { + return *this; + } + + /// Get the first inline child. + Polymorphic const& + front() const + { + MRDOCS_ASSERT(!children.empty()); + return children.front(); + } + + /// Get the first inline child. + Polymorphic& + front() + { + MRDOCS_ASSERT(!children.empty()); + return children.front(); + } + + /// Get the last inline child. + Polymorphic const& + back() const + { + MRDOCS_ASSERT(!children.empty()); + return children.back(); + } + + /// Get the last inline child. + Polymorphic& + back() + { + MRDOCS_ASSERT(!children.empty()); + return children.back(); + } + + /// Determine if there are no inline children. + bool + empty() const noexcept + { + return children.empty(); + } + + /// Get the number of inline children. + std::size_t + size() const noexcept + { + return children.size(); + } + + /// Begin iterator forwarding to children + decltype(auto) + begin(this auto&& self) noexcept + { + return self.children.begin(); + } + + /// End iterator forwarding to children + decltype(auto) + end(this auto&& self) noexcept + { + return self.children.end(); + } + + /// Erase from children + decltype(auto) + erase(this auto&& self, auto&&... args) + { + return self.children.erase(std::forward(args)...); + } + + /// Erase from children + decltype(auto) + insert(this auto&& self, auto&&... args) + { + return self.children.insert(std::forward(args)...); + } + + /// Clear all inline children. + void + clear() + { + children.clear(); + } + + /// Append a TextInline child. + InlineContainer& + append(std::string_view text); + + /// Append a child of the specified type. + template InlineTy, class... Args> + InlineContainer& + append(Args&&... args) + { + children.push_back(Polymorphic(std::in_place_type, std::forward(args)...)); + return *this; + } + + /// Append a TextInline child. + InlineContainer& + operator+=(std::string_view text) + { + return append(text); + } + + /// Append an inline child. + template InlineTy> + InlineContainer& + operator+=(InlineTy&& inlineChild) + { + children.push_back(Polymorphic(std::in_place_type, std::forward(inlineChild))); + return *this; + } + + /// Append a child of the specified type. + template InlineTy, class... Args> + InlineContainer& + emplace_back(Args&&... args) + { + children.push_back(Polymorphic(std::in_place_type, std::forward(args)...)); + return *this; + } + + /// Compare two InlineContainers. + std::strong_ordering + operator<=>(InlineContainer const&) const; + + /// Compare two InlineContainers. + bool + operator==(InlineContainer const&) const = default; +}; + +template InlineTy> +void +tag_invoke( + dom::ValueFromTag, + IO& io, + InlineTy const& I, + DomCorpus const* domCorpus); + +template +void +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + InlineContainer const& I, + DomCorpus const* domCorpus) +{ + io.defer("children", [&I, domCorpus] { + return dom::LazyArray(I.children, domCorpus); + }); +} + +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + InlineContainer const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +/** Removes leading whitespace from the first text element in the given InlineContainer. + + @param inlines The InlineContainer to trim. + @return void + */ +MRDOCS_DECL +void +ltrim(InlineContainer& inlines); + +/** Removes trailing whitespace from the last text element in the given InlineContainer. + + @param inlines The InlineContainer to trim. + @return void + */ +MRDOCS_DECL +void +rtrim(InlineContainer& inlines); + +/** Removes leading and trailing whitespace from the text elements in the given InlineContainer. + + @param inlines The InlineContainer to trim. + @return void + */ +inline +void +trim(InlineContainer& inlines) +{ + ltrim(inlines); + rtrim(inlines); +} + +/** Flatten an InlineContainer to plain text. + + This concatenates all text nodes, ignoring formatting. + + @param in The InlineContainer to flatten. + @param dst The output string to append to. + */ +MRDOCS_DECL +void +getAsPlainText(doc::InlineContainer const& in, std::string& dst); + +/** Flatten an InlineContainer to plain text. + + This concatenates all text nodes, ignoring formatting. + + @param in The InlineContainer to flatten. + @return The flattened plain text. + */ +inline +std::string +getAsPlainText(doc::InlineContainer const& in) +{ + std::string dst; + getAsPlainText(in, dst); + return dst; +} + +/// A leaf node that stores a string of text. +struct MRDOCS_DECL InlineTextLeaf +{ + std::string literal; + + explicit + InlineTextLeaf(std::string_view literal_) + : literal(literal_) + {} + + explicit + InlineTextLeaf(std::string const& literal_) + : literal(literal_) + {} + + explicit + InlineTextLeaf(std::string&& literal_) noexcept + : literal(std::move(literal_)) + {} + + auto operator<=>(InlineTextLeaf const&) const = default; + bool operator==(InlineTextLeaf const&) const noexcept = default; +}; + + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_INLINE_INLINEBASE_HPP diff --git a/include/mrdocs/Metadata/DocComment/Inline/InlineKind.hpp b/include/mrdocs/Metadata/DocComment/Inline/InlineKind.hpp new file mode 100644 index 000000000..38570e822 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Inline/InlineKind.hpp @@ -0,0 +1,47 @@ +// +// 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_API_METADATA_DOCCOMMENT_INLINE_INLINEKIND_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_INLINE_INLINEKIND_HPP + +#include +#include + +namespace mrdocs::doc { + +enum class InlineKind { + #define INFO(Type) Type, +#include +}; + +inline +dom::String +toString(InlineKind kind) noexcept +{ + switch (kind) + { + #define INFO(Type) case InlineKind::Type: return toKebabCase(#Type); +#include + } + return "Unknown"; +} + +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, InlineKind const kind) +{ + v = toString(kind); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_INLINE_INLINEKIND_HPP diff --git a/include/mrdocs/Metadata/DocComment/Inline/InlineNodes.inc b/include/mrdocs/Metadata/DocComment/Inline/InlineNodes.inc new file mode 100644 index 000000000..9afbafecc --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Inline/InlineNodes.inc @@ -0,0 +1,32 @@ +// +// 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 INFO +#define INFO(PascalName) +#endif + +INFO(Reference) +INFO(CopyDetails) +INFO(Link) +INFO(Text) +INFO(SoftBreak) +INFO(LineBreak) +INFO(Code) +INFO(Emph) +INFO(Strong) +INFO(Image) +INFO(FootnoteReference) +INFO(Strikethrough) +INFO(Math) +INFO(Superscript) +INFO(Subscript) +INFO(Highlight) + +#undef INFO diff --git a/include/mrdocs/Metadata/DocComment/Inline/LineBreakInline.hpp b/include/mrdocs/Metadata/DocComment/Inline/LineBreakInline.hpp new file mode 100644 index 000000000..45e535690 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Inline/LineBreakInline.hpp @@ -0,0 +1,65 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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_API_METADATA_DOCCOMMENT_INLINE_LINEBREAKINLINE_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_INLINE_LINEBREAKINLINE_HPP + +#include +#include +#include + +namespace mrdocs::doc { + +/** A hard line break that renders as "
" +*/ +struct LineBreakInline + : InlineCommonBase +{ + constexpr ~LineBreakInline() override = default; + constexpr LineBreakInline() noexcept = default; + auto operator<=>(LineBreakInline const&) const = default; + bool operator==(LineBreakInline const&) const noexcept = default; +}; + +/** Map the @ref LineBreak 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + LineBreakInline const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); +} + +/** Return the @ref LineBreak as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + LineBreakInline const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_INLINE_LINEBREAKINLINE_HPP diff --git a/include/mrdocs/Metadata/DocComment/Inline/LinkInline.hpp b/include/mrdocs/Metadata/DocComment/Inline/LinkInline.hpp new file mode 100644 index 000000000..947a51047 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Inline/LinkInline.hpp @@ -0,0 +1,75 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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_API_METADATA_DOCCOMMENT_INLINE_LINKINLINE_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_INLINE_LINKINLINE_HPP + +#include +#include +#include + +namespace mrdocs::doc { + +/** A hyperlink. +*/ +struct LinkInline final + : InlineCommonBase + , InlineContainer +{ + std::string href; + + LinkInline() = default; + + LinkInline(std::string_view text, std::string_view href) + : InlineContainer(text) + , href(href) + {} + + auto operator<=>(LinkInline const&) const = default; + bool operator==(LinkInline const&) const noexcept = default; +}; + +/** 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + LinkInline const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + tag_invoke(t, io, dynamic_cast(I), domCorpus); + io.map("href", I.href); +} + +/** Return the @ref Link as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + LinkInline const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_INLINE_LINKINLINE_HPP diff --git a/include/mrdocs/Metadata/DocComment/Inline/MathInline.hpp b/include/mrdocs/Metadata/DocComment/Inline/MathInline.hpp new file mode 100644 index 000000000..9ba7aceb1 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Inline/MathInline.hpp @@ -0,0 +1,75 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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_API_METADATA_DOCCOMMENT_INLINE_MATHINLINE_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_INLINE_MATHINLINE_HPP + +#include +#include +#include + +namespace mrdocs::doc { + +/** An inline LaTeX math expression + + Inline LaTeX math, typically between $…$. + */ +struct MathInline + : InlineCommonBase +{ + std::string literal; + + constexpr ~MathInline() override = default; + constexpr MathInline() noexcept = default; + + explicit MathInline(std::string string_) noexcept + : literal(std::move(string_)) + {} + + auto operator<=>(MathInline const&) const = default; + bool operator==(MathInline const&) const noexcept = default; +}; + +/** Map the @ref Math 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + MathInline const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + io.map("literal", I.literal); +} + +/** Return the @ref Math as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + MathInline const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_INLINE_MATHINLINE_HPP diff --git a/include/mrdocs/Metadata/DocComment/Inline/Parts.hpp b/include/mrdocs/Metadata/DocComment/Inline/Parts.hpp new file mode 100644 index 000000000..9a417e124 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Inline/Parts.hpp @@ -0,0 +1,58 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_DOCCOMMENT_INLINE_PARTS_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_INLINE_PARTS_HPP + +#include +#include + +namespace mrdocs::doc { + +/** Which parts of the documentation to copy. + + @li `all`: copy the brief and the description. + @li `brief`: only copy the brief. + @li `description`: only copy the description. +*/ +enum class Parts +{ + /// Copy the brief and the description + all = 1, // needed by bitstream + /// Copy the brief + brief, + /// Copy the description + description +}; + +/** Return the name of the Parts as a string. + */ +MRDOCS_DECL +dom::String +toString(Parts kind) noexcept; + +/** Return the Parts from a @ref dom::Value string. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + Parts const kind) +{ + v = toString(kind); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_INLINE_PARTS_HPP diff --git a/include/mrdocs/Metadata/DocComment/Inline/ReferenceInline.hpp b/include/mrdocs/Metadata/DocComment/Inline/ReferenceInline.hpp new file mode 100644 index 000000000..8cd7db65d --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Inline/ReferenceInline.hpp @@ -0,0 +1,75 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_DOCCOMMENT_INLINE_REFERENCEINLINE_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_INLINE_REFERENCEINLINE_HPP + +#include +#include +#include +#include + +namespace mrdocs::doc { + +/** A reference to a symbol. +*/ +struct ReferenceInline + : InlineCommonBase +{ + std::string literal; + SymbolID id = SymbolID::invalid; + + explicit ReferenceInline(std::string str = {}) noexcept + : literal(std::move(str)) + {} + + auto operator<=>(ReferenceInline const&) const = default; + bool operator==(ReferenceInline const&) const noexcept = default; +}; + +/** 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + ReferenceInline const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + io.map("literal", I.literal); + io.map("symbol", I.id); +} + +/** Return the @ref Reference as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + ReferenceInline const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_INLINE_REFERENCEINLINE_HPP diff --git a/include/mrdocs/Metadata/DocComment/Inline/SoftBreakInline.hpp b/include/mrdocs/Metadata/DocComment/Inline/SoftBreakInline.hpp new file mode 100644 index 000000000..35a77e166 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Inline/SoftBreakInline.hpp @@ -0,0 +1,65 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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_API_METADATA_DOCCOMMENT_INLINE_SOFTBREAKINLINE_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_INLINE_SOFTBREAKINLINE_HPP + +#include +#include +#include + +namespace mrdocs::doc { + +/** A line break that may render as space +*/ +struct SoftBreakInline + : InlineCommonBase +{ + constexpr ~SoftBreakInline() override = default; + constexpr SoftBreakInline() noexcept = default; + auto operator<=>(SoftBreakInline const&) const = default; + bool operator==(SoftBreakInline const&) const noexcept = default; +}; + +/** Map the @ref SoftBreak 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + SoftBreakInline const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); +} + +/** Return the @ref SoftBreak as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + SoftBreakInline const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_INLINE_SOFTBREAKINLINE_HPP diff --git a/include/mrdocs/Metadata/DocComment/Inline/StrikethroughInline.hpp b/include/mrdocs/Metadata/DocComment/Inline/StrikethroughInline.hpp new file mode 100644 index 000000000..f0f38470d --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Inline/StrikethroughInline.hpp @@ -0,0 +1,66 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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_API_METADATA_DOCCOMMENT_INLINE_STRIKETHROUGHINLINE_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_INLINE_STRIKETHROUGHINLINE_HPP + +#include +#include +#include +#include + +namespace mrdocs::doc { + +/** A piece of styled text. +*/ +struct StrikethroughInline final + : InlineCommonBase + , InlineContainer +{ + auto operator<=>(StrikethroughInline const&) const = default; + bool operator==(StrikethroughInline const&) const noexcept = default; +}; + +/** Map the @ref Strikethrough 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + StrikethroughInline const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + tag_invoke(t, io, dynamic_cast(I), domCorpus); +} + +/** Return the @ref Strikethrough as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + StrikethroughInline const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_INLINE_STRIKETHROUGHINLINE_HPP diff --git a/include/mrdocs/Metadata/DocComment/Inline/StrongInline.hpp b/include/mrdocs/Metadata/DocComment/Inline/StrongInline.hpp new file mode 100644 index 000000000..1eb209280 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Inline/StrongInline.hpp @@ -0,0 +1,67 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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_API_METADATA_DOCCOMMENT_INLINE_STRONGINLINE_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_INLINE_STRONGINLINE_HPP + +#include +#include +#include +#include + +namespace mrdocs::doc { + +/** A piece of styled text. +*/ +struct StrongInline final + : InlineCommonBase + , InlineContainer +{ + using InlineContainer::InlineContainer; + auto operator<=>(StrongInline const&) const = default; + bool operator==(StrongInline const&) const noexcept = default; +}; + +/** Map the @ref Strong 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + StrongInline const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + tag_invoke(t, io, dynamic_cast(I), domCorpus); +} + +/** Return the @ref Strong as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + StrongInline const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_INLINE_STRONGINLINE_HPP diff --git a/include/mrdocs/Metadata/DocComment/Inline/SubscriptInline.hpp b/include/mrdocs/Metadata/DocComment/Inline/SubscriptInline.hpp new file mode 100644 index 000000000..84461d57b --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Inline/SubscriptInline.hpp @@ -0,0 +1,66 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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_API_METADATA_DOCCOMMENT_INLINE_SUBSCRIPTINLINE_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_INLINE_SUBSCRIPTINLINE_HPP + +#include +#include +#include +#include + +namespace mrdocs::doc { + +/** A piece of styled text. +*/ +struct SubscriptInline final + : InlineCommonBase + , InlineContainer +{ + auto operator<=>(SubscriptInline const&) const = default; + bool operator==(SubscriptInline const&) const noexcept = default; +}; + +/** Map the @ref Subscript 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + SubscriptInline const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + tag_invoke(t, io, dynamic_cast(I), domCorpus); +} + +/** Return the @ref Subscript as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + SubscriptInline const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_INLINE_SUBSCRIPTINLINE_HPP diff --git a/include/mrdocs/Metadata/DocComment/Inline/SuperscriptInline.hpp b/include/mrdocs/Metadata/DocComment/Inline/SuperscriptInline.hpp new file mode 100644 index 000000000..772c6b4ad --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Inline/SuperscriptInline.hpp @@ -0,0 +1,66 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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_API_METADATA_DOCCOMMENT_INLINE_SUPERSCRIPTINLINE_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_INLINE_SUPERSCRIPTINLINE_HPP + +#include +#include +#include +#include + +namespace mrdocs::doc { + +/** A piece of styled text. +*/ +struct SuperscriptInline final + : InlineCommonBase + , InlineContainer +{ + auto operator<=>(SuperscriptInline const&) const = default; + bool operator==(SuperscriptInline const&) const noexcept = default; +}; + +/** Map the @ref Superscript 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + SuperscriptInline const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + tag_invoke(t, io, dynamic_cast(I), domCorpus); +} + +/** Return the @ref Superscript as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + SuperscriptInline const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_INLINE_SUPERSCRIPTINLINE_HPP diff --git a/include/mrdocs/Metadata/DocComment/Inline/TextInline.hpp b/include/mrdocs/Metadata/DocComment/Inline/TextInline.hpp new file mode 100644 index 000000000..0df55cae7 --- /dev/null +++ b/include/mrdocs/Metadata/DocComment/Inline/TextInline.hpp @@ -0,0 +1,90 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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_API_METADATA_DOCCOMMENT_INLINE_TEXTINLINE_HPP +#define MRDOCS_API_METADATA_DOCCOMMENT_INLINE_TEXTINLINE_HPP + +#include +#include +#include + +namespace mrdocs::doc { + +/** A Node containing a string of text. + + There will be no newlines in the text. Otherwise, + this would be represented as multiple text nodes + within a Paragraph node. +*/ +struct TextInline + : InlineCommonBase +{ + std::string literal; + + constexpr ~TextInline() override = default; + + constexpr TextInline() noexcept = default; + + explicit TextInline(std::string_view str) noexcept + : literal(str) + {} + + explicit TextInline(char const* str) noexcept + : literal(str) + {} + + explicit TextInline(std::string const& str) noexcept + : literal(str) + {} + + explicit TextInline(std::string&& str) noexcept + : literal(std::move(str)) + {} + + auto operator<=>(TextInline const&) const = default; + bool operator==(TextInline const&) const noexcept = default; +}; + +/** 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 +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + TextInline const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + io.map("literal", I.literal); +} + +/** Return the @ref Text as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + TextInline const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs::doc + +#endif // MRDOCS_API_METADATA_DOCCOMMENT_INLINE_TEXTINLINE_HPP diff --git a/include/mrdocs/Metadata/DomCorpus.hpp b/include/mrdocs/Metadata/DomCorpus.hpp index 0ab8ed7dd..3eab8ade3 100644 --- a/include/mrdocs/Metadata/DomCorpus.hpp +++ b/include/mrdocs/Metadata/DomCorpus.hpp @@ -10,18 +10,18 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_API_DOM_DOMCORPUS_HPP -#define MRDOCS_API_DOM_DOMCORPUS_HPP +#ifndef MRDOCS_API_METADATA_DOMCORPUS_HPP +#define MRDOCS_API_METADATA_DOMCORPUS_HPP #include #include #include -namespace clang::mrdocs { +namespace mrdocs { class Corpus; -struct Info; -struct Javadoc; +struct Symbol; +struct DocComment; class SymbolID; /** Front-end factory for producing Dom nodes. @@ -82,7 +82,7 @@ class MRDOCS_DECL */ virtual dom::Object - construct(Info const& I) const; + construct(Symbol const& I) const; /** Return a Dom object representing the given symbol. @@ -94,7 +94,7 @@ class MRDOCS_DECL dom::Value get(SymbolID const& id) const; - /** Return a Dom value representing the Javadoc. + /** Return a Dom value representing the DocComment. The default implementation returns null. A @ref Generator should override this member @@ -103,15 +103,15 @@ class MRDOCS_DECL */ virtual dom::Value - getJavadoc(Javadoc const& jd) const; + getDocComment(DocComment const& jd) const; }; /** Return a list of the parent symbols of the specified Info. */ MRDOCS_DECL dom::Array -getParents(DomCorpus const& C, Info const& I); +getParents(DomCorpus const& C, Symbol const& I); -} // clang::mrdocs +} // mrdocs -#endif +#endif // MRDOCS_API_METADATA_DOMCORPUS_HPP diff --git a/include/mrdocs/Metadata/Expression.hpp b/include/mrdocs/Metadata/Expression.hpp index fb4996eec..e2bc032b8 100644 --- a/include/mrdocs/Metadata/Expression.hpp +++ b/include/mrdocs/Metadata/Expression.hpp @@ -12,11 +12,12 @@ #define MRDOCS_API_METADATA_EXPRESSION_HPP #include +#include #include #include #include -namespace clang::mrdocs { +namespace mrdocs { /** Represents an expression */ struct ExprInfo @@ -24,6 +25,16 @@ struct ExprInfo /** The expression, as written */ std::string Written; + ExprInfo& asExpr() noexcept + { + return *this; + } + + ExprInfo const& asExpr() const noexcept + { + return *this; + } + auto operator<=>(ExprInfo const&) const = default; }; @@ -44,12 +55,11 @@ struct ConstantExprInfo The value of an expression will be unknown if it is e.g. dependent on a template parameter */ - std::optional Value; + Optional Value; auto operator<=>(ConstantExprInfo const&) const = default; - static_assert(std::integral, - "expression type must be integral"); + static_assert(std::integral, "expression type must be integral"); }; template @@ -57,15 +67,13 @@ static void merge( ConstantExprInfo& I, ConstantExprInfo&& Other) { - merge( - dynamic_cast(I), - dynamic_cast(Other)); + merge(I.asExpr(), std::move(Other.asExpr())); if (!I.Value) { I.Value = std::move(Other.Value); } } -} // clang::mrdocs +} // mrdocs #endif diff --git a/include/mrdocs/Metadata/Info.hpp b/include/mrdocs/Metadata/Info.hpp deleted file mode 100644 index cbb129aea..000000000 --- a/include/mrdocs/Metadata/Info.hpp +++ /dev/null @@ -1,431 +0,0 @@ -// -// This is a derivative work. originally part of the LLVM Project. -// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) -// -// Official repository: https://github.com/cppalliance/mrdocs -// - -#ifndef MRDOCS_API_METADATA_INFO_HPP -#define MRDOCS_API_METADATA_INFO_HPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace clang::mrdocs { - -/* Forward declarations - */ -#define INFO(Type) struct Type##Info; -#include - -/** Info variant discriminator -*/ -enum class InfoKind -{ - /// Kind is not specified. - None = 0, - #define INFO(Type) Type, - #include -}; - -/** Return the name of the InfoKind as a string. - */ -MRDOCS_DECL -dom::String -toString(InfoKind kind) noexcept; - -/** Return the InfoKind from a @ref dom::Value string. - */ -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - InfoKind const kind) -{ - v = toString(kind); -} - -consteval -std::underlying_type_t -countInfoKind() -{ - std::underlying_type_t count = 0; -#define INFO(Type) count++; -#include - return count; -} - -/** Base class with common properties of all symbols -*/ -struct MRDOCS_VISIBLE Info - : SourceInfo -{ - /** The unique identifier for this symbol. - */ - SymbolID id; - - /** The unqualified name. - */ - std::string Name; - - /** Kind of declaration. - */ - InfoKind Kind = InfoKind::None; - - /** Declaration access. - - Class members use: - @li `AccessKind::Public`, - @li `AccessKind::Protected`, and - @li `AccessKind::Private`. - - Namespace members use `AccessKind::None`. - */ - AccessKind Access = AccessKind::None; - - /** Determine why a symbol is extracted. - - This flag distinguishes `Info` from its dependencies and - indicates why it was extracted. - - Non-dependencies can be extracted in normal mode, - see-below mode, or implementation-defined mode. - - A dependency is a symbol which does not meet the configured - conditions for extraction, but had to be extracted due to it - being used transitively by a primary `Info`. - */ - ExtractionMode Extraction = ExtractionMode::Dependency; - - /** The parent symbol, if any. - - This is the parent namespace or record - where the symbol is defined. - */ - SymbolID Parent; - - /** The extracted javadoc for this declaration. - */ - std::optional javadoc; - - //-------------------------------------------- - - ~Info() override = default; - - Info(Info const& Other) = default; - - /** Move constructor. - */ - Info(Info&& Other) = default; - - /** Construct an Info. - - @param kind The kind of symbol - @param ID The unique identifier for this symbol - */ - explicit - Info( - InfoKind const kind, - SymbolID const& ID) noexcept - : id(ID) - , Kind(kind) - { - } - - #define INFO(Type) constexpr bool is##Type() const noexcept { \ - return Kind == InfoKind::Type; \ - } - #include - - constexpr Info const& asInfo() const noexcept - { - return *this; - } - - constexpr Info& asInfo() noexcept - { - return *this; - } - - #define INFO(Type) \ - constexpr Type##Info const& as##Type() const noexcept { \ - if (Kind == InfoKind::Type) \ - return reinterpret_cast(*this); \ - MRDOCS_UNREACHABLE(); \ - } - #include - - #define INFO(Type) \ - constexpr Type##Info & as##Type() noexcept { \ - if (Kind == InfoKind::Type) \ - return reinterpret_cast(*this); \ - MRDOCS_UNREACHABLE(); \ - } - #include - - #define INFO(Type) \ - constexpr Type##Info const* as##Type##Ptr() const noexcept { \ - if (Kind == InfoKind::Type) { return reinterpret_cast(this); } \ - return nullptr; \ - } - #include - - #define INFO(Type) \ - constexpr Type##Info * as##Type##Ptr() noexcept { \ - if (Kind == InfoKind::Type) { return reinterpret_cast(this); } \ - return nullptr; \ - } - #include - - auto operator<=>(Info const&) const = default; -}; - -//------------------------------------------------ - -/** Base class for providing variant discriminator functions. - - This offers functions that return a boolean at - compile-time, indicating if the most-derived - class is a certain type. -*/ -template -struct InfoCommonBase : Info -{ - /** The variant discriminator constant of the most-derived class. - - It only distinguishes from `Info::kind` in that it is a constant. - - */ - static constexpr InfoKind kind_id = K; - - #define INFO(Kind) \ - static constexpr bool is##Kind() noexcept { return K == InfoKind::Kind; } - #include - - auto operator<=>(InfoCommonBase const&) const = default; - -protected: - constexpr explicit InfoCommonBase(SymbolID const& ID) - : Info(K, ID) - { - } -}; - -/** Invoke a function object with a type derived from Info - - This function will invoke the function object `fn` with - a type derived from `Info` as the first argument, followed - by `args...`. The type of the first argument is determined - by the `InfoKind` of the `Info` object. - - @param info The Info object to visit - @param fn The function object to call - @param args Additional arguments to pass to the function object - @return The result of calling the function object with the derived type - */ -template< - std::derived_from InfoTy, - class Fn, - class... Args> -decltype(auto) -visit( - InfoTy& info, - Fn&& fn, - Args&&... args) -{ - auto visitor = makeVisitor( - info, std::forward(fn), - std::forward(args)...); - switch(info.Kind) - { - #define INFO(Type) \ - case InfoKind::Type: \ - return visitor.template visit(); - #include - default: - MRDOCS_UNREACHABLE(); - } -} - -/** Merges two Info objects. - - This function is used to merge two Info objects with the same SymbolID. - The function assumes that the two Info objects are of the same type. - If they are not, the function will fail. - - @param I The Info object to merge into. - @param Other The Info object to merge from. -*/ -MRDOCS_DECL -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 -merge(InfoTy& I, InfoTy&& Other) -{ - MRDOCS_ASSERT(I.Kind == Other.Kind); - MRDOCS_ASSERT(I.id == Other.id); - Info& base = I; - visit(base, [&](DerivedInfoTy& derived) mutable - { - DerivedInfoTy& otherDerived = static_cast(Other); - merge(derived, std::move(otherDerived)); - }); -} - -inline -bool -canMerge(Info const& I, Info const& Other) -{ - return - I.Kind == Other.Kind && - I.id == Other.id; -} - -/** A concept for types that have `Info` members. - - In most cases `T` is another `Info` type that - has a `Members` member which is a range of - `SymbolID` values. -*/ -template -concept InfoParent = requires(InfoTy const& I) -{ - { allMembers(I) } -> range_of; -}; - -/** 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 -tag_invoke( - dom::LazyObjectMapTag, - IO& io, - Info const& I, - DomCorpus const* domCorpus) -{ - MRDOCS_ASSERT(domCorpus); - io.map("class", std::string("symbol")); - io.map("kind", I.Kind); - io.map("id", I.id); - if (!I.Name.empty()) - { - io.map("name", I.Name); - } - io.map("access", I.Access); - io.map("extraction", I.Extraction); - io.map("isRegular", I.Extraction == ExtractionMode::Regular); - io.map("isSeeBelow", I.Extraction == ExtractionMode::SeeBelow); - io.map("isImplementationDefined", I.Extraction == ExtractionMode::ImplementationDefined); - io.map("isDependency", I.Extraction == ExtractionMode::Dependency); - if (I.Parent) - { - io.map("parent", I.Parent); - } - if (I.javadoc) - { - io.map("doc", *I.javadoc); - } - io.map("loc", dynamic_cast(I)); -} - -/** 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 -void -tag_invoke( - dom::LazyObjectMapTag, - IO& io, - PolymorphicInfo const& I, - DomCorpus const* domCorpus) -{ - visit(*I, [&](auto const& U) - { - tag_invoke( - dom::LazyObjectMapTag{}, - io, - U, - domCorpus); - }); -} - -/** Return the Info as a @ref dom::Value object. - */ -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - Info const& I, - DomCorpus const* domCorpus) -{ - v = dom::LazyObject(I, domCorpus); -} - -/** 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 -void -tag_invoke( - dom::ValueFromTag, - IO& io, - InfoTy const& I, - DomCorpus const* domCorpus) -{ - visit(*I, [&](auto const& U) - { - tag_invoke( - dom::ValueFromTag{}, - io, - U, - domCorpus); - }); -} - -inline -OptionalLocation -getPrimaryLocation(Info const& I) -{ - return getPrimaryLocation( - dynamic_cast(I), - I.isRecord() || I.isEnum()); -} - -} // clang::mrdocs - -#endif diff --git a/include/mrdocs/Metadata/Info/Enum.hpp b/include/mrdocs/Metadata/Info/Enum.hpp deleted file mode 100644 index b78825428..000000000 --- a/include/mrdocs/Metadata/Info/Enum.hpp +++ /dev/null @@ -1,99 +0,0 @@ -// -// This is a derivative work. originally part of the LLVM Project. -// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) -// -// Official repository: https://github.com/cppalliance/mrdocs -// - -#ifndef MRDOCS_API_METADATA_ENUM_HPP -#define MRDOCS_API_METADATA_ENUM_HPP - -#include -#include -#include -#include -#include - -namespace clang::mrdocs { - -struct EnumInfo final - : InfoCommonBase -{ - // Indicates whether this enum is scoped (e.g. enum class). - bool Scoped = false; - - // Set too nonempty to the type when this is an explicitly typed enum. For - // enum Foo : short { ... }; - // this will be "short". - Polymorphic UnderlyingType = std::nullopt; - - /** The members of this scope. - - All members are enum constants; - */ - std::vector Constants; - - //-------------------------------------------- - - explicit EnumInfo(SymbolID ID) noexcept - : InfoCommonBase(ID) - { - } -}; - -inline -auto& -allMembers(EnumInfo const& T) -{ - return T.Constants; -} - -MRDOCS_DECL -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 -tag_invoke( - dom::LazyObjectMapTag t, - IO& io, - EnumInfo const& I, - DomCorpus const* domCorpus) -{ - tag_invoke(t, io, dynamic_cast(I), domCorpus); - io.map("type", I.UnderlyingType); - io.map("isScoped", I.Scoped); - io.map("constants", dom::LazyArray(I.Constants, domCorpus)); -} - -/** 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 -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - EnumInfo const& I, - DomCorpus const* domCorpus) -{ - v = dom::LazyObject(I, domCorpus); -} - -} // clang::mrdocs - -#endif diff --git a/include/mrdocs/Metadata/Info/Function.hpp b/include/mrdocs/Metadata/Info/Function.hpp deleted file mode 100644 index 7e6df927e..000000000 --- a/include/mrdocs/Metadata/Info/Function.hpp +++ /dev/null @@ -1,310 +0,0 @@ -// -// This is a derivative work. originally part of the LLVM Project. -// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) -// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) -// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) -// -// Official repository: https://github.com/cppalliance/mrdocs -// - -#ifndef MRDOCS_API_METADATA_FUNCTION_HPP -#define MRDOCS_API_METADATA_FUNCTION_HPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace clang::mrdocs { - -/** Return the name of an operator as a string. - - @param kind The kind of operator. - @param include_keyword Whether the name - should be prefixed with the `operator` keyword. -*/ -MRDOCS_DECL -std::string_view -getOperatorName( - OperatorKind kind, - bool include_keyword = false) noexcept; - -/** Return the short name of an operator as a string. -*/ -MRDOCS_DECL -std::string_view -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 -getOperatorKindFromSuffix(std::string_view suffix) noexcept; - -/** Return the safe name of an operator as a string. - - @param kind The kind of operator. - @param include_keyword Whether the name - should be prefixed with `operator_`. -*/ -MRDOCS_DECL -std::string_view -getSafeOperatorName( - OperatorKind kind, - 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( - OperatorKind kind, - int nParams); - -/** Function classifications */ -enum class FunctionClass -{ - /// The function is a normal function. - Normal = 0, - /// The function is a constructor. - Constructor, - /// The function is a conversion operator. - Conversion, - /// The function is a destructor. - Destructor -}; - -MRDOCS_DECL dom::String toString(FunctionClass kind) noexcept; - -/** Return the FunctionClass from a @ref dom::Value string. - */ -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - FunctionClass const kind) -{ - v = toString(kind); -} - - -// KRYSTIAN TODO: attributes (nodiscard, deprecated, and carries_dependency) -// KRYSTIAN TODO: flag to indicate whether this is a function parameter pack -/** Represents a single function parameter */ -struct Param final -{ - /** The type of this parameter - */ - Polymorphic Type = std::nullopt; - - /** The parameter name. - */ - Optional Name; - - /** The default argument for this parameter, if any - */ - Optional Default; - - Param() = default; - - Param( - Polymorphic&& type, - std::string&& name, - std::string&& def_arg) - : Type(std::move(type)) - , Name(std::move(name)) - , Default(std::move(def_arg)) - {} - - auto - operator<=>(Param const&) const = default; -}; - -MRDOCS_DECL -void -merge(Param& I, Param&& Other); - -/** Return the Param as a @ref dom::Value object. - */ -MRDOCS_DECL -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - Param const& p, - DomCorpus const* domCorpus); - -// TODO: Expand to allow for documenting templating and default args. -// Info for functions. -struct FunctionInfo final - : InfoCommonBase -{ - /// Info about the return type of this function. - Polymorphic ReturnType = std::nullopt; - - /// List of parameters. - std::vector Params; - - /// When present, this function is a template or specialization. - std::optional Template; - - /// The class of function this is - FunctionClass Class = FunctionClass::Normal; - - NoexceptInfo Noexcept; - ExprInfo Requires; - bool IsVariadic = false; - bool IsDefaulted = false; - bool IsExplicitlyDefaulted = false; - bool IsDeleted = false; - bool IsDeletedAsWritten = false; - bool IsNoReturn = false; - bool HasOverrideAttr = false; - bool HasTrailingReturn = false; - bool IsNodiscard = false; - bool IsExplicitObjectMemberFunction = false; - ConstexprKind Constexpr = ConstexprKind::None; - OperatorKind OverloadedOperator = OperatorKind::None; - StorageClassKind StorageClass = StorageClassKind::None; - std::vector Attributes; - - // CXXMethodDecl - bool IsRecordMethod = false; - bool IsVirtual = false; - bool IsVirtualAsWritten = false; - bool IsPure = false; - bool IsConst = false; - bool IsVolatile = false; - bool IsFinal = false; - ReferenceKind RefQualifier = ReferenceKind::None; - ExplicitInfo Explicit; - - //-------------------------------------------- - - explicit FunctionInfo(SymbolID const& ID) noexcept - : InfoCommonBase(ID) - { - } - - std::strong_ordering - operator<=>(FunctionInfo const& other) const; -}; - -MRDOCS_DECL -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 -tag_invoke( - dom::LazyObjectMapTag t, - IO& io, - FunctionInfo const& I, - DomCorpus const* domCorpus) -{ - tag_invoke(t, io, dynamic_cast(I), domCorpus); - io.map("isVariadic", I.IsVariadic); - io.map("isVirtual", I.IsVirtual); - io.map("isVirtualAsWritten", I.IsVirtualAsWritten); - io.map("isPure", I.IsPure); - io.map("isDefaulted", I.IsDefaulted); - io.map("isExplicitlyDefaulted", I.IsExplicitlyDefaulted); - io.map("isDeleted", I.IsDeleted); - io.map("isDeletedAsWritten", I.IsDeletedAsWritten); - io.map("isNoReturn", I.IsNoReturn); - io.map("hasOverrideAttr", I.HasOverrideAttr); - io.map("hasTrailingReturn", I.HasTrailingReturn); - io.map("isConst", I.IsConst); - io.map("isVolatile", I.IsVolatile); - io.map("isFinal", I.IsFinal); - io.map("isNodiscard", I.IsNodiscard); - io.map("isExplicitObjectMemberFunction", I.IsExplicitObjectMemberFunction); - if (I.Constexpr != ConstexprKind::None) - { - io.map("constexprKind", I.Constexpr); - } - if (I.StorageClass != StorageClassKind::None) - { - io.map("storageClass", I.StorageClass); - } - if (I.RefQualifier != ReferenceKind::None) - { - io.map("refQualifier", I.RefQualifier); - } - io.map("functionClass", I.Class); - io.map("params", dom::LazyArray(I.Params, domCorpus)); - io.map("return", I.ReturnType); - io.map("template", I.Template); - io.map("overloadedOperator", I.OverloadedOperator); - io.map("exceptionSpec", I.Noexcept); - io.map("explicitSpec", I.Explicit); - if (!I.Requires.Written.empty()) - { - io.map("requires", I.Requires.Written); - } - io.map("attributes", dom::LazyArray(I.Attributes)); -} - -/** 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 -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - FunctionInfo const& I, - DomCorpus const* domCorpus) -{ - v = dom::LazyObject(I, domCorpus); -} - -/** Determine if one function would override the other - - @param base The base function - @param derived The derived function - */ -MRDOCS_DECL -bool -overrides(FunctionInfo const& base, FunctionInfo const& derived); - -} // clang::mrdocs - -#endif diff --git a/include/mrdocs/Metadata/Info/Record.hpp b/include/mrdocs/Metadata/Info/Record.hpp deleted file mode 100644 index db83088d9..000000000 --- a/include/mrdocs/Metadata/Info/Record.hpp +++ /dev/null @@ -1,397 +0,0 @@ -// -// This is a derivative work. originally part of the LLVM Project. -// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) -// -// Official repository: https://github.com/cppalliance/mrdocs -// - -#ifndef MRDOCS_API_METADATA_RECORD_HPP -#define MRDOCS_API_METADATA_RECORD_HPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace clang::mrdocs { - -/** A group of members that have the same access specifier. - - This struct represents a collection of symbols that share - the same access specifier within a record. - - It includes one vector for each info type allowed in a - record, and individual vectors for static functions, types, - and function overloads. -*/ -struct RecordTranche -{ - std::vector NamespaceAliases; - std::vector Typedefs; - std::vector Records; - std::vector Enums; - std::vector Functions; - std::vector StaticFunctions; - std::vector Variables; - std::vector StaticVariables; - std::vector Concepts; - std::vector Guides; - std::vector Usings; -}; - -MRDOCS_DECL -void -merge(RecordTranche& I, RecordTranche&& Other); - -inline -auto -allMembers(RecordTranche const& T) -{ - // This is a trick to emulate views::concat in C++20 - return std::views::transform( - std::views::iota(0, 11), - [&T](int const i) -> auto const& - { - switch (i) { - case 0: return T.NamespaceAliases; - case 1: return T.Typedefs; - case 2: return T.Records; - case 3: return T.Enums; - case 4: return T.Functions; - case 5: return T.StaticFunctions; - case 6: return T.Variables; - case 7: return T.StaticVariables; - case 8: return T.Concepts; - case 9: return T.Guides; - case 10: return T.Usings; - default: throw std::out_of_range("Invalid index"); - } - } - ) | std::ranges::views::join; -} - -/** 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 -tag_invoke( - dom::LazyObjectMapTag, - IO& io, - RecordTranche const& I, - DomCorpus const* domCorpus) -{ - io.map("namespaceAliases", dom::LazyArray(I.NamespaceAliases, domCorpus)); - io.map("typedefs", dom::LazyArray(I.Typedefs, domCorpus)); - io.map("records", dom::LazyArray(I.Records, domCorpus)); - io.map("enums", dom::LazyArray(I.Enums, domCorpus)); - io.map("functions", dom::LazyArray(I.Functions, domCorpus)); - io.map("staticFunctions", dom::LazyArray(I.StaticFunctions, domCorpus)); - io.map("variables", dom::LazyArray(I.Variables, domCorpus)); - io.map("staticVariables", dom::LazyArray(I.StaticVariables, domCorpus)); - io.map("concepts", dom::LazyArray(I.Concepts, domCorpus)); - io.map("guides", dom::LazyArray(I.Guides, domCorpus)); - io.map("usings", dom::LazyArray(I.Usings, domCorpus)); -} - -/** Map the RecordTranche to a @ref dom::Value object. - */ -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - RecordTranche const& I, - DomCorpus const* domCorpus) -{ - v = dom::LazyObject(I, domCorpus); -} - -/** The aggregated interface for a given struct, class, or union. - - This class represents the public, protected, and private - interfaces of a record. It is used to generate the - "interface" value of the DOM for symbols that represent - records or namespaces. - - The interface is not part of the Corpus. It is a temporary - structure generated to aggregate the symbols of a record. - This structure is provided to the user via the DOM. - - While the members of a Namespace are directly represented - with a Tranche, the members of a Record are represented - with an Interface. - -*/ -class RecordInterface -{ -public: - /** The aggregated public interfaces. - - This tranche contains all public members of a record - or namespace. - - */ - RecordTranche Public; - - /** The aggregated protected interfaces. - - This tranche contains all protected members of a record - or namespace. - - */ - RecordTranche Protected; - - /** The aggregated private interfaces. - - This tranche contains all private members of a record - or namespace. - - */ - RecordTranche Private; -}; - -MRDOCS_DECL -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 -tag_invoke( - dom::LazyObjectMapTag, - IO& io, - RecordInterface const& I, - DomCorpus const*) -{ - io.map("public", I.Public); - io.map("protected", I.Protected); - io.map("private", I.Private); -} - -/** Map the RecordInterface to a @ref dom::Value object. - */ -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - RecordInterface const& I, - DomCorpus const* domCorpus) -{ - v = dom::LazyObject(I, domCorpus); -} - - -inline -auto -allMembers(RecordInterface const& T) -{ - // This is a trick to emulate views::concat in C++20 - return - std::views::iota(0, 3) | - std::views::transform( - [&T](int i) -> auto { - switch (i) { - case 0: return allMembers(T.Public); - case 1: return allMembers(T.Protected); - case 2: return allMembers(T.Private); - default: throw std::out_of_range("Invalid index"); - } - }) | - std::ranges::views::join; -} - -/** Metadata for a direct base. -*/ -struct BaseInfo -{ - Polymorphic Type; - AccessKind Access = AccessKind::Public; - bool IsVirtual = false; - - BaseInfo() = default; - - BaseInfo( - Polymorphic&& type, - AccessKind const access, - bool const is_virtual) - : Type(std::move(type)) - , Access(access) - , IsVirtual(is_virtual) - { - } -}; - -MRDOCS_DECL -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - 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 -}; - -MRDOCS_DECL -dom::String -toString(RecordKeyKind kind) noexcept; - -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - RecordKeyKind kind) -{ - v = toString(kind); -} - -/** Metadata for struct, class, or union. -*/ -struct RecordInfo final - : InfoCommonBase -{ - /** Kind of record this is (class, struct, or union). - */ - RecordKeyKind KeyKind = RecordKeyKind::Struct; - - /// When present, this record is a template or specialization. - std::optional Template; - - // Indicates if the record was declared using a typedef. - // Things like anonymous structs in a typedef: - // typedef struct { ... } foo_t; - // are converted into records with the typedef as the Name + this flag set. - // KRYSTIAN FIXME: this does not account for alias-declarations - bool IsTypeDef = false; - - bool IsFinal = false; - bool IsFinalDestructor = false; - - /** List of immediate bases. - */ - std::vector Bases; - - /** List of derived classes - */ - std::vector Derived; - - /** Lists of members. - */ - RecordInterface Interface; - - /** List of friends. - */ - std::vector Friends; - - //-------------------------------------------- - - explicit RecordInfo(SymbolID const& ID) noexcept - : InfoCommonBase(ID) - { - } - - std::strong_ordering - operator<=>(RecordInfo const& other) const; -}; - -constexpr -std::string_view -getDefaultAccessString( - RecordKeyKind const& kind) noexcept -{ - switch(kind) - { - case RecordKeyKind::Class: - return "private"; - case RecordKeyKind::Struct: - case RecordKeyKind::Union: - return "public"; - default: - MRDOCS_UNREACHABLE(); - } -} - -inline -auto -allMembers(RecordInfo const& T) -{ - return allMembers(T.Interface); -} - -MRDOCS_DECL -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 -tag_invoke( - dom::LazyObjectMapTag t, - IO& io, - RecordInfo const& I, - DomCorpus const* domCorpus) -{ - tag_invoke(t, io, dynamic_cast(I), domCorpus); - io.map("tag", I.KeyKind); - io.map("defaultAccess", getDefaultAccessString(I.KeyKind)); - io.map("isFinal", I.IsFinal); - io.map("isTypedef", I.IsTypeDef); - io.map("bases", dom::LazyArray(I.Bases, domCorpus)); - io.map("derived", dom::LazyArray(I.Derived, domCorpus)); - io.map("interface", I.Interface); - io.map("template", I.Template); - io.map("friends", dom::LazyArray(I.Friends, domCorpus)); -} - -/** Map the RecordInfo to a @ref dom::Value object. - */ -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - RecordInfo const& I, - DomCorpus const* domCorpus) -{ - v = dom::LazyObject(I, domCorpus); -} - -} // clang::mrdocs - -#endif diff --git a/include/mrdocs/Metadata/Javadoc.hpp b/include/mrdocs/Metadata/Javadoc.hpp deleted file mode 100644 index c927c06d7..000000000 --- a/include/mrdocs/Metadata/Javadoc.hpp +++ /dev/null @@ -1,2102 +0,0 @@ -// -// This is a derivative work. originally part of the LLVM Project. -// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) -// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) -// -// Official repository: https://github.com/cppalliance/mrdocs -// - -#ifndef MRDOCS_API_METADATA_JAVADOC_HPP -#define MRDOCS_API_METADATA_JAVADOC_HPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace clang::mrdocs { - -/** Javadoc related types and functions. - - Javadoc is a documentation generator originally - created for the Java language from source code. - - The Javadoc documentation generator tool can interpret - text in the "doc comments" format included - directly in the source code. - - The same "doc comments" format has been replicated - and extended by documentation systems for other - languages, including the cross-language Doxygen - and the JSDoc system for JavaScript. - - Because Clang can already parse and extract - blocks of Javadoc-style comments from source - code, these classes are used to represent the - parsed documentation in a structured form. - - @see https://en.wikipedia.org/wiki/Javadoc - @see https://www.doxygen.nl - - */ -namespace doc { - -struct Node; - -/** The kind of node. - - This includes tags and block types. - - Some of the available tags are: - - @li `@author Author Name` - @li `{@docRoot}` - @li `@version version` - @li `@since since-text ` - @li `@see reference` - @li `@param name description` - @li `@return description` - @li `@exception classname description` - @li `@throws classname description` - @li `@deprecated description` - @li `{@inheritDoc}` - @li `{@link reference}` - @li `{@linkplain reference}` - @li `{@value #STATIC_FIELD}` - @li `{@code literal}` - @li `{@literal literal}` - @li `{@serial literal}` - @li `{@serialData literal}` - @li `{@serialField literal}` - - Doxygen also introduces a number of additional tags on top - of the the doc comment specification. - - @note When a new tag is added, the `visit` function overloads - must be updated to handle the new tag. - - @see https://en.wikipedia.org/wiki/Javadoc[Javadoc - Wikipedia] - @see https://docs.oracle.com/javase/1.5.0/docs/tooldocs/solaris/javadoc.html[Javadoc Documentation] - @see https://docs.oracle.com/en/java/javase/13/docs/specs/javadoc/doc-comment-spec.html[Doc Comment Specification] - @see https://www.oracle.com/java/technologies/javase/javadoc-tool.html[Javadoc Tool] - @see https://www.oracle.com/technical-resources/articles/java/javadoc-tool.html[How to Write Doc Comments] - @see https://docs.oracle.com/javase/8/docs/technotes/tools/unix/javadoc.html[Javadoc Package] - @see https://web.archive.org/web/20170714215721/http://agile.csc.ncsu.edu:80/SEMaterials/tutorials/javadoc[Javadoc Tutorial] - @see https://en.wikipedia.org/wiki/Doxygen[Doxygen - Wikipedia] - @see https://www.doxygen.nl/manual/commands.html[Doxygen Special Tags] - - */ -enum class NodeKind -{ - // VFALCO Don't forget to update - // 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 -}; - -/** Return the name of the NodeKind as a string. - */ -MRDOCS_DECL -dom::String -toString(NodeKind kind) noexcept; - -/** Return the NodeKind from a @ref dom::Value string. - */ -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - NodeKind const kind) -{ - v = toString(kind); -} - -/** 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 -toString(Style kind) noexcept; - -/** Return the @ref Style from a @ref dom::Value string. - */ -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - Style const kind) -{ - v = toString(kind); -} - -/** An admonishment style. -*/ -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 -}; - -/** Return the name of the Admonish as a string. - */ -MRDOCS_DECL -dom::String -toString(Admonish kind) noexcept; - -/** Return the Admonish from a @ref dom::Value string. - */ -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - Admonish const kind) -{ - v = toString(kind); -} - - -/** Parameter pass direction. -*/ -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 -}; - -/** Return the name of the ParamDirection as a string. - */ -MRDOCS_DECL -dom::String -toString(ParamDirection kind) noexcept; - -/** Return the ParamDirection from a @ref dom::Value string. - */ -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - ParamDirection const kind) -{ - v = toString(kind); -} - -/** Which parts of the documentation to copy. - - @li `all`: copy the brief and the description. - @li `brief`: only copy the brief. - @li `description`: only copy the description. -*/ -enum class Parts -{ - /// Copy the brief and the description - all = 1, // needed by bitstream - /// Copy the brief - brief, - /// Copy the description - description -}; - -/** Return the name of the Parts as a string. - */ -MRDOCS_DECL -dom::String -toString(Parts kind) noexcept; - -/** Return the Parts from a @ref dom::Value string. - */ -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - Parts const kind) -{ - v = toString(kind); -} - -//-------------------------------------------- - -/** This is a variant-like list element. - - There are two types of nodes: text and block. - - - The javadoc is a list of blocks. - - A block contains a list of text elements. - - A text element contains a string. -*/ -struct MRDOCS_DECL - Node -{ - NodeKind Kind; - - virtual ~Node() = default; - - explicit Node(NodeKind const kind_) noexcept - : Kind(kind_) - { - } - - virtual bool isBlock() const noexcept = 0; - - bool isText() const noexcept - { - return ! isBlock(); - } - - auto operator<=>(Node const&) const = default; - bool operator==(Node const&)const noexcept = default; - virtual bool equals(Node const& other) const noexcept - { - return Kind == other.Kind; - } -}; - -/** 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 -tag_invoke( - dom::LazyObjectMapTag, - IO& io, - Node const& I, - DomCorpus const* domCorpus) -{ - MRDOCS_ASSERT(domCorpus); - io.map("kind", I.Kind); -} - -/** Return the @ref Node as a @ref dom::Value object. - */ -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - Node const& I, - DomCorpus const* domCorpus) -{ - v = dom::LazyObject(I, domCorpus); -} - -/** 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 -tag_invoke( - dom::ValueFromTag, - IO& io, - NodeTy const& I, - DomCorpus const* domCorpus) -{ - visit(*I, [&](auto const& U) - { - tag_invoke( - dom::ValueFromTag{}, - io, - U, - domCorpus); - }); -} - -//------------------------------------------------ -// -// Text nodes -// -//------------------------------------------------ - -/** A Node containing a string of text. - - There will be no newlines in the text. Otherwise, - this would be represented as multiple text nodes - within a Paragraph node. -*/ -struct Text : Node -{ - std::string string; - - static constexpr auto static_kind = NodeKind::text; - - explicit - Text( - std::string string_ = std::string()) noexcept - : Node(NodeKind::text) - , string(std::move(string_)) - { - } - - bool - isBlock() const noexcept final - { - return false; - } - - auto operator<=>(Text const&) const = default; - bool operator==(Text const&) const noexcept = default; - bool equals(Node const& other) const noexcept override - { - return Kind == other.Kind && - *this == dynamic_cast(other); - } - -protected: - Text( - std::string string_, - NodeKind kind_) - : Node(kind_) - , string(std::move(string_)) - { - } -}; - -/** 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 -tag_invoke( - dom::LazyObjectMapTag t, - IO& io, - Text const& I, - DomCorpus const* domCorpus) -{ - tag_invoke(t, io, dynamic_cast(I), domCorpus); - io.map("string", I.string); -} - -/** Return the @ref Text as a @ref dom::Value object. - */ -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - Text const& I, - DomCorpus const* domCorpus) -{ - v = dom::LazyObject(I, domCorpus); -} - -MRDOCS_DECL -std::strong_ordering -operator<=>(Polymorphic const& lhs, Polymorphic const& rhs); - -inline -bool -operator==(Polymorphic const& lhs, Polymorphic const& rhs) { - return std::is_eq(lhs <=> rhs); -} - -/** A piece of styled text. -*/ -struct Styled final : Text -{ - Style style; - - static constexpr auto static_kind = NodeKind::styled; - - Styled( - std::string string_ = std::string(), - Style style_ = Style::none) noexcept - : Text(std::move(string_), NodeKind::styled) - , style(style_) - { - } - - auto operator<=>(Styled const&) const = default; - bool operator==(Styled const&) const noexcept = default; - bool equals(Node const& other) const noexcept override - { - return Kind == other.Kind && - *this == dynamic_cast(other); - } -}; - -/** 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 -tag_invoke( - dom::LazyObjectMapTag t, - IO& io, - Styled const& I, - DomCorpus const* domCorpus) -{ - tag_invoke(t, io, dynamic_cast(I), domCorpus); - io.map("style", I.style); -} - -/** Return the @ref Styled as a @ref dom::Value object. - */ -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - Styled const& I, - DomCorpus const* domCorpus) -{ - v = dom::LazyObject(I, domCorpus); -} - - -/** A hyperlink. -*/ -struct Link final : Text -{ - std::string href; - - static constexpr auto static_kind = NodeKind::link; - - explicit - Link( - std::string string_ = std::string(), - std::string href_ = std::string()) noexcept - : Text(std::move(string_), NodeKind::link) - , href(std::move(href_)) - { - } - - auto operator<=>(Link const&) const = default; - bool operator==(Link const&) const noexcept = default; - bool equals(Node const& other) const noexcept override - { - return Kind == other.Kind && - *this == dynamic_cast(other); - } -}; - -/** 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 -tag_invoke( - dom::LazyObjectMapTag t, - IO& io, - Link const& I, - DomCorpus const* domCorpus) -{ - tag_invoke(t, io, dynamic_cast(I), domCorpus); - io.map("href", I.href); -} - -/** Return the @ref Link as a @ref dom::Value object. - */ -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - Link const& I, - DomCorpus const* domCorpus) -{ - v = dom::LazyObject(I, domCorpus); -} - -/** A reference to a symbol. -*/ -struct Reference : Text -{ - SymbolID id = SymbolID::invalid; - - static constexpr auto static_kind = NodeKind::reference; - - explicit - Reference( - std::string string_ = std::string()) noexcept - : Text(std::move(string_), NodeKind::reference) - { - } - - auto operator<=>(Reference const&) const = default; - bool operator==(Reference const&) const noexcept = default; - bool equals(Node const& other) const noexcept override - { - return Kind == other.Kind && - *this == dynamic_cast(other); - } - -protected: - Reference( - std::string string_, - NodeKind const kind_) noexcept - : Text(std::move(string_), kind_) - { - } -}; - -/** 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 -tag_invoke( - dom::LazyObjectMapTag t, - IO& io, - Reference const& I, - DomCorpus const* domCorpus) -{ - tag_invoke(t, io, dynamic_cast(I), domCorpus); - io.map("symbol", I.id); -} - -/** Return the @ref Reference as a @ref dom::Value object. - */ -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - Reference const& I, - DomCorpus const* domCorpus) -{ - v = dom::LazyObject(I, domCorpus); -} - -/** Documentation copied from another symbol. -*/ -struct CopyDetails final : Reference -{ - static constexpr auto static_kind = NodeKind::copy_details; - - CopyDetails(std::string string_ = std::string()) noexcept - : Reference(std::move(string_), NodeKind::copy_details) - { - } - - auto operator<=>(CopyDetails const&) const = default; - bool operator==(CopyDetails const&) const noexcept = default; - bool equals(Node const& other) const noexcept override - { - return Kind == other.Kind && - *this == dynamic_cast(other); - } -}; - -/** 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 -tag_invoke( - dom::LazyObjectMapTag t, - IO& io, - CopyDetails const& I, - DomCorpus const* domCorpus) -{ - tag_invoke(t, io, dynamic_cast(I), domCorpus); -} - -/** 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 -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - CopyDetails const& I, - DomCorpus const* domCorpus) -{ - v = dom::LazyObject(I, domCorpus); -} - -//------------------------------------------------ -// -// Block nodes -// -//------------------------------------------------ - -/** A piece of block content - - The top level is a list of blocks. - - There are two types of blocks: headings and paragraphs -*/ -struct MRDOCS_DECL - Block : Node -{ - std::vector> children; - - bool isBlock() const noexcept final - { - return true; - } - - bool empty() const noexcept - { - return children.empty(); - } - - auto operator<=>(Block const& other) const { - if (auto const cmp = children.size() <=> other.children.size(); - !std::is_eq(cmp)) - { - return cmp; - } - for (std::size_t i = 0; i < children.size(); ++i) - { - if (auto const cmp = *children[i] <=> *other.children[i]; - !std::is_eq(cmp)) - { - return cmp; - } - } - return std::strong_ordering::equal; - } - - bool operator==(Block const& other) const noexcept - { - if (Kind != other.Kind) - { - return false; - } - return std::ranges:: - equal(children, other.children, - [](auto const& a, auto const& b) - { - return a->equals(*b); - }); - } - - bool equals(Node const& other) const noexcept override - { - return Kind == other.Kind && - *this == dynamic_cast(other); - } - - template T> - T& emplace_back(T&& text) - { - return static_cast(emplace_back( - std::make_unique(std::forward(text)))); - } - - void append(std::vector>&& blocks); - - void append(std::vector> const& otherChildren); - -protected: - explicit - Block( - NodeKind const kind_, - std::vector> children_ = {}) noexcept - : Node(kind_) - , children(std::move(children_)) - { - } - -private: - Text& emplace_back(Polymorphic text); -}; - -/** 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 -tag_invoke( - dom::LazyObjectMapTag t, - IO& io, - Block const& I, - DomCorpus const* domCorpus) -{ - tag_invoke(t, io, dynamic_cast(I), domCorpus); - io.defer("children", [&I, domCorpus] { - return dom::LazyArray(I.children, domCorpus); - }); -} - -/** Return the @ref Block as a @ref dom::Value object. - */ -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - Block const& I, - DomCorpus const* domCorpus) -{ - v = dom::LazyObject(I, domCorpus); -} - -/** A manually specified section heading. -*/ -struct Heading final : Block -{ - static constexpr auto static_kind = NodeKind::heading; - - std::string string; - - Heading( - std::string string_ = std::string()) noexcept - : Block(NodeKind::heading) - , string(std::move(string_)) - { - } - - auto operator<=>(Heading const&) const = default; - bool operator==(Heading const&) const noexcept = default; - bool equals(Node const& other) const noexcept override - { - return Kind == other.Kind && - *this == dynamic_cast(other); - } -}; - -/** 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 -tag_invoke( - dom::LazyObjectMapTag t, - IO& io, - Heading const& I, - DomCorpus const* domCorpus) -{ - tag_invoke(t, io, dynamic_cast(I), domCorpus); - io.map("string", I.string); -} - -/** Return the @ref Heading as a @ref dom::Value object. - */ -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - Heading const& I, - DomCorpus const* domCorpus) -{ - v = dom::LazyObject(I, domCorpus); -} - -/** A sequence of text nodes. -*/ -struct Paragraph : Block -{ - static constexpr auto static_kind = NodeKind::paragraph; - - Paragraph() noexcept : Block(NodeKind::paragraph) {} - - virtual - Paragraph& - operator=(std::string_view str); - - auto operator<=>(Paragraph const&) const = default; - - bool equals(Node const& other) const noexcept override - { - return Kind == other.Kind && - *this == dynamic_cast(other); - } - -protected: - explicit - Paragraph( - NodeKind const kind, - std::vector> children_ = {}) noexcept - : Block(kind, std::move(children_)) - { - } -}; - -/** 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 -tag_invoke( - dom::LazyObjectMapTag t, - IO& io, - Paragraph const& I, - DomCorpus const* domCorpus) -{ - tag_invoke(t, io, dynamic_cast(I), domCorpus); -} - -/** Return the @ref Paragraph as a @ref dom::Value object. - */ -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - Paragraph const& I, - DomCorpus const* domCorpus) -{ - v = dom::LazyObject(I, domCorpus); -} - - -/** The brief description -*/ -struct Brief final : Paragraph -{ - static constexpr NodeKind static_kind = NodeKind::brief; - - std::vector copiedFrom; - - Brief() noexcept - : Paragraph(NodeKind::brief) - { - } - - explicit - Brief(std::string_view const text) - : Brief() - { - operator=(text); - } - - Brief(Brief const& other) = default; - - Brief& - operator=(Brief const& other) = default; - - Brief& - operator=(std::string_view const text) override - { - Paragraph::operator=(text); - return *this; - } - - auto operator<=>(Brief const&) const = default; - - bool equals(Node const& other) const noexcept override - { - return Kind == other.Kind && - *this == dynamic_cast(other); - } -}; - -/** 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 -tag_invoke( - dom::LazyObjectMapTag t, - IO& io, - Brief const& I, - DomCorpus const* domCorpus) -{ - tag_invoke(t, io, dynamic_cast(I), domCorpus); -} - -/** Return the @ref Brief as a @ref dom::Value object. - */ -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - Brief const& I, - DomCorpus const* domCorpus) -{ - v = dom::LazyObject(I, domCorpus); -} - -/** An admonition. - - This paragraph represents an admonition, such as - a note, tip, important, caution, or warning. - -*/ -struct Admonition final : Paragraph -{ - Admonish admonish; - - explicit - Admonition( - Admonish const admonish_ = Admonish::none) noexcept - : Paragraph(NodeKind::admonition) - , admonish(admonish_) - { - } - - using Paragraph::operator=; - - auto operator<=>(Admonition const&) const = default; - - bool operator==(Admonition const&) const noexcept = default; - - bool equals(Node const& other) const noexcept override - { - return Kind == other.Kind && - *this == dynamic_cast(other); - } -}; - -/** 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 -tag_invoke( - dom::LazyObjectMapTag t, - IO& io, - Admonition const& I, - DomCorpus const* domCorpus) -{ - tag_invoke(t, io, dynamic_cast(I), domCorpus); - io.map("admonish", I.admonish); -} - -/** Return the @ref Admonition as a @ref dom::Value object. - */ -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - Admonition const& I, - DomCorpus const* domCorpus) -{ - v = dom::LazyObject(I, domCorpus); -} - -/** Preformatted source code. -*/ -struct Code final : Paragraph -{ - // VFALCO we can add a language (e.g., C++), - // then emit attributes in the generator. - - static constexpr auto static_kind = NodeKind::code; - - Code() noexcept - : Paragraph(NodeKind::code) - { - } - - using Paragraph::operator=; - auto operator<=>(Code const&) const = default; - bool operator==(Code const&) const noexcept = default; - bool equals(Node const& other) const noexcept override - { - return Kind == other.Kind && - *this == dynamic_cast(other); - } -}; - -/** 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 -tag_invoke( - dom::LazyObjectMapTag t, - IO& io, - Code const& I, - DomCorpus const* domCorpus) -{ - tag_invoke(t, io, dynamic_cast(I), domCorpus); -} - -/** Return the @ref Code as a @ref dom::Value object. - */ -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - Code const& I, - DomCorpus const* domCorpus) -{ - v = dom::LazyObject(I, domCorpus); -} - -/** An item in a list -*/ -struct ListItem final : Paragraph -{ - static constexpr auto static_kind = NodeKind::list_item; - - ListItem() - : Paragraph(static_kind) - {} - - using Paragraph::operator=; - - auto operator<=>(ListItem const&) const = default; - bool - operator==(ListItem const&) const noexcept = default; - - bool - equals(Node const& other) const noexcept override - { - auto* p = dynamic_cast(&other); - if (!p) - { - return false; - } - if (this == &other) - { - return true; - } - return *this == *p; - } -}; - -/** 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 -tag_invoke( - dom::LazyObjectMapTag t, - IO& io, - ListItem const& I, - DomCorpus const* domCorpus) -{ - tag_invoke(t, io, dynamic_cast(I), domCorpus); -} - -/** Return the @ref ListItem as a @ref dom::Value object. - */ -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - ListItem const& I, - DomCorpus const* domCorpus) -{ - v = dom::LazyObject(I, domCorpus); -} - -/** A list of list items -*/ -struct UnorderedList final : Paragraph -{ - static constexpr auto static_kind = NodeKind::unordered_list; - - std::vector items; - - UnorderedList() - : Paragraph(static_kind) - { - } - - using Paragraph::operator=; - - auto operator<=>(UnorderedList const& other) const { - if (auto const cmp = items.size() <=> other.items.size(); - !std::is_eq(cmp)) - { - return cmp; - } - for (std::size_t i = 0; i < items.size(); ++i) - { - if (auto const cmp = items[i] <=> other.items[i]; - !std::is_eq(cmp)) - { - return cmp; - } - } - return std::strong_ordering::equal; - } - - bool - operator==(UnorderedList const&) const noexcept = default; - - bool - equals(Node const& other) const noexcept override - { - auto* p = dynamic_cast(&other); - if (!p) - { - return false; - } - if (this == &other) - { - return true; - } - return *this == *p; - } -}; - -/** 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 -tag_invoke( - dom::LazyObjectMapTag t, - IO& io, - UnorderedList const& I, - DomCorpus const* domCorpus) -{ - tag_invoke(t, io, dynamic_cast(I), domCorpus); - io.defer("items", [&I, domCorpus] { - return dom::LazyArray(I.items, domCorpus); - }); -} - -/** Return the @ref UnorderedList as a @ref dom::Value object. - */ -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - UnorderedList const& I, - DomCorpus const* domCorpus) -{ - v = dom::LazyObject(I, domCorpus); -} - -/** A @see paragraph -*/ -struct See final : Paragraph -{ - static constexpr auto static_kind = NodeKind::see; - - See() - : Paragraph(static_kind) - { - } - - using Paragraph::operator=; - - auto operator<=>(See const&) const = default; - - bool operator==(See const&) - const noexcept = default; - - bool equals(Node const& other) const noexcept override - { - return Kind == other.Kind && - *this == dynamic_cast(other); - } -}; - -/** 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 -tag_invoke( - dom::LazyObjectMapTag t, - IO& io, - See const& I, - DomCorpus const* domCorpus) -{ - tag_invoke(t, io, dynamic_cast(I), domCorpus); -} - -/** Return the @ref See as a @ref dom::Value object. - */ -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - See const& I, - DomCorpus const* domCorpus) -{ - v = dom::LazyObject(I, domCorpus); -} - -/** Documentation for a function parameter -*/ -struct Param final : Paragraph -{ - std::string name; - ParamDirection direction; - - static constexpr auto static_kind = NodeKind::param; - - Param( - std::string name_ = std::string(), - Paragraph details_ = Paragraph(), - ParamDirection const direction_ = ParamDirection::none) - : Paragraph( - NodeKind::param, - std::move(details_.children)) - , name(std::move(name_)) - , direction(direction_) - { - } - - explicit - Param( - std::string_view const name, - std::string_view const text) - : Param() - { - this->name = name; - this->operator=(text); - } - - explicit - Param(Paragraph const& other) - : Param() - { - children = other.children; - } - - Param& - operator=(std::string_view const text) override - { - Paragraph::operator=(text); - return *this; - } - - auto operator<=>(Param const&) const = default; - - bool operator==(Param const&) - const noexcept = default; - - bool equals(Node const& other) const noexcept override - { - return Kind == other.Kind && - *this == dynamic_cast(other); - } -}; - -/** 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 -tag_invoke( - dom::LazyObjectMapTag t, - IO& io, - Param const& I, - DomCorpus const* domCorpus) -{ - tag_invoke(t, io, dynamic_cast(I), domCorpus); - io.map("name", I.name); - io.map("direction", I.direction); -} - -/** Return the @ref Param as a @ref dom::Value object. - */ -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - Param const& I, - DomCorpus const* domCorpus) -{ - v = dom::LazyObject(I, domCorpus); -} - -/** Documentation for a function return type -*/ -struct Returns final : Paragraph -{ - static constexpr NodeKind static_kind = NodeKind::returns; - - Returns() - : Paragraph(NodeKind::returns) - { - } - - explicit - Returns(std::string_view const text) - : Returns() - { - operator=(text); - } - - explicit - Returns(Paragraph const& other) - : Returns() - { - children = other.children; - } - - Returns& - operator=(std::string_view const text) override - { - Paragraph::operator=(text); - return *this; - } - - auto operator<=>(Returns const&) const = default; - - bool operator==(Returns const&) - const noexcept = default; - - bool equals(Node const& other) const noexcept override - { - return Kind == other.Kind && - *this == dynamic_cast(other); - } -}; - -/** 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 -tag_invoke( - dom::LazyObjectMapTag t, - IO& io, - Returns const& I, - DomCorpus const* domCorpus) -{ - tag_invoke(t, io, dynamic_cast(I), domCorpus); -} - -/** Return the @ref Returns as a @ref dom::Value object. - */ -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - Returns const& I, - DomCorpus const* domCorpus) -{ - v = dom::LazyObject(I, domCorpus); -} - -/** Documentation for a template parameter -*/ -struct TParam final : Paragraph -{ - std::string name; - - static constexpr NodeKind static_kind = NodeKind::tparam; - - TParam() - : Paragraph(NodeKind::tparam) - { - } - - using Paragraph::operator=; - - auto operator<=>(TParam const&) const = default; - bool operator==(TParam const&) const noexcept = default; - bool equals(Node const& other) const noexcept override - { - return Kind == other.Kind && - *this == dynamic_cast(other); - } -}; - -/** 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 -tag_invoke( - dom::LazyObjectMapTag t, - IO& io, - TParam const& I, - DomCorpus const* domCorpus) -{ - tag_invoke(t, io, dynamic_cast(I), domCorpus); - io.map("name", I.name); -} - -/** Return the @ref TParam as a @ref dom::Value object. - */ -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - TParam const& I, - DomCorpus const* domCorpus) -{ - v = dom::LazyObject(I, domCorpus); -} - -/** Documentation for a function parameter -*/ -struct Throws final : Paragraph -{ - Reference exception; - - static constexpr NodeKind static_kind = NodeKind::throws; - - Throws( - std::string exception_ = std::string(), - Paragraph details_ = Paragraph()) - : Paragraph( - NodeKind::throws, - std::move(details_.children)) - , exception(std::move(exception_)) - { - } - - using Paragraph::operator=; - - auto operator<=>(Throws const&) const = default; - - bool operator==(Throws const&) - const noexcept = default; - - bool equals(Node const& other) const noexcept override - { - return Kind == other.Kind && - *this == dynamic_cast(other); - } -}; - -/** 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 -tag_invoke( - dom::LazyObjectMapTag t, - IO& io, - Throws const& I, - DomCorpus const* domCorpus) -{ - tag_invoke(t, io, dynamic_cast(I), domCorpus); - io.map("exception", I.exception); -} - -/** Return the @ref Throws as a @ref dom::Value object. - */ -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - Throws const& I, - DomCorpus const* domCorpus) -{ - v = dom::LazyObject(I, domCorpus); -} - -struct Precondition final : Paragraph -{ - static constexpr NodeKind static_kind = NodeKind::precondition; - - Precondition( - Paragraph details_ = Paragraph()) - : Paragraph( - NodeKind::precondition, - std::move(details_.children)) - { - } - - using Paragraph::operator=; - - auto operator<=>(Precondition const&) const = default; - - bool operator==(Precondition const&) - const noexcept = default; - - bool equals(Node const& other) const noexcept override - { - return Kind == other.Kind && - *this == dynamic_cast(other); - } -}; - -/** 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 -tag_invoke( - dom::LazyObjectMapTag t, - IO& io, - Precondition const& I, - DomCorpus const* domCorpus) -{ - tag_invoke(t, io, dynamic_cast(I), domCorpus); -} - -/** 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 -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - Precondition const& I, - DomCorpus const* domCorpus) -{ - v = dom::LazyObject(I, domCorpus); -} - -struct Postcondition : Paragraph -{ - static constexpr auto static_kind = NodeKind::postcondition; - - Postcondition( - Paragraph details_ = Paragraph()) - : Paragraph( - NodeKind::postcondition, - std::move(details_.children)) - { - } - - using Paragraph::operator=; - - auto operator<=>(Postcondition const&) const = default; - - bool operator==(Postcondition const&) - const noexcept = default; - - bool equals(Node const& other) const noexcept override - { - return Kind == other.Kind && - *this == dynamic_cast(other); - } -}; - -/** 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 -tag_invoke( - dom::LazyObjectMapTag t, - IO& io, - Postcondition const& I, - DomCorpus const* domCorpus) -{ - tag_invoke(t, io, dynamic_cast(I), domCorpus); -} - -/** 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 -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - Postcondition const& I, - DomCorpus const* domCorpus) -{ - v = dom::LazyObject(I, domCorpus); -} - -//------------------------------------------------ - -/** Visit a node. - - @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, - class Fn, - class... Args> - requires std::derived_from -decltype(auto) -visit( - NodeTy& node, - Fn&& fn, - Args&&... args) -{ - auto visitor = makeVisitor( - node, std::forward(fn), - std::forward(args)...); - switch(node.Kind) - { - case NodeKind::admonition: - return visitor.template visit(); - case NodeKind::brief: - return visitor.template visit(); - case NodeKind::code: - return visitor.template visit(); - case NodeKind::heading: - return visitor.template visit(); - case NodeKind::paragraph: - return visitor.template visit(); - case NodeKind::link: - return visitor.template visit(); - case NodeKind::reference: - return visitor.template visit(); - case NodeKind::copy_details: - return visitor.template visit(); - case NodeKind::list_item: - return visitor.template visit(); - case NodeKind::unordered_list: - return visitor.template visit(); - case NodeKind::param: - return visitor.template visit(); - case NodeKind::returns: - return visitor.template visit(); - case NodeKind::styled: - return visitor.template visit(); - case NodeKind::text: - return visitor.template visit(); - case NodeKind::tparam: - return visitor.template visit(); - case NodeKind::throws: - return visitor.template visit(); - case NodeKind::see: - return visitor.template visit(); - case NodeKind::precondition: - return visitor.template visit(); - case NodeKind::postcondition: - return visitor.template visit(); - default: - MRDOCS_UNREACHABLE(); - } -} - -/** Traverse a list of nodes. - - @param list The list of nodes to traverse. - @param f The function to call for each node. - @param args Additional arguments to pass to the function. - */ -template -requires std::derived_from -void traverse( - std::vector> const& list, - F&& f, Args&&... args) -{ - for(auto const& node : list) - visit(*node, - std::forward(f), - std::forward(args)...); -} - -} // doc - -//------------------------------------------------ - -class Corpus; - -/** A processed Doxygen-style comment attached to a declaration. -*/ -struct MRDOCS_DECL - Javadoc -{ - /// The list of text blocks. - std::vector> blocks; - - // ---------------------- - // Symbol Metadata - - /// A brief description of the symbol. - std::optional brief; - - /** The list of return type descriptions. - - Multiple return descriptions are allowed. - - The results are concatenated in the order - they appear in the source code. - */ - std::vector returns; - - /// The list of parameters. - std::vector params; - - /// The list of template parameters. - std::vector tparams; - - /// The list of exceptions. - std::vector exceptions; - - /// The list of "see also" references. - std::vector sees; - - /// The list of preconditions. - std::vector preconditions; - - /// The list of postconditions. - std::vector postconditions; - - /** The list of "relates" references. - - These references are creates with the - \\relates command. - */ - std::vector relates; - - /** The list of "related" references. - - These references are the inverse of - the \\relates command. - */ - std::vector related; - - /** Constructor. - */ - MRDOCS_DECL - Javadoc() noexcept; - - /** Constructor - */ - explicit - Javadoc( - std::vector> blocks); - - /** Return true if this is empty - */ - bool - empty() const noexcept - { - return - blocks.empty() && - !brief && - returns.empty() && - params.empty() && - tparams.empty() && - exceptions.empty() && - sees.empty() && - related.empty() && - preconditions.empty() && - postconditions.empty(); - } - - /** Return the list of top level blocks. - */ - std::vector> const& - getBlocks() const noexcept - { - return blocks; - } - - // VFALCO This is unfortunately necessary for - // the deserialization from bitcode... - std::vector>& - getBlocks() noexcept - { - return blocks; - } - - //-------------------------------------------- - - /** Comparison - - These are used internally to impose a - total ordering, and not visible in the - output format. - */ - /** @{ */ - auto operator<=>(Javadoc const& other) const noexcept { - if (auto const cmp = blocks.size() <=> other.blocks.size(); - !std::is_eq(cmp)) - { - return cmp; - } - for (std::size_t i = 0; i < blocks.size(); ++i) - { - if (auto cmp = CompareDerived(blocks[i], other.blocks[i]); - !std::is_eq(cmp)) - { - return cmp; - } - } - return std::strong_ordering::equal; - } - bool operator==(Javadoc const&) const noexcept; - bool operator!=(Javadoc const&) const noexcept; - /* @} */ - - //-------------------------------------------- - - /** Attempt to append a block. - - @return An empty string on success, otherwise - a string indicating the reason for the failure. - - @param block The block to append. - */ - template T> - std::string - emplace_back(T&& block) - { - return emplace_back(Polymorphic(std::in_place_type, - std::forward(block))); - } - - /** Append blocks from another javadoc to this. - */ - void append(Javadoc&& other); - - /** Append blocks from a list to this. - - @param blocks The blocks to append. - */ - void - append(std::vector>&& blocks); - -private: - std::string - emplace_back(Polymorphic); -}; - -inline -void merge(Javadoc& I, Javadoc&& other) -{ - // FIXME: this doesn't merge parameter information; - // parameters with the same name but different direction - // or descriptions end up being duplicated - if(other != I) - { - // Unconditionally extend the blocks - // since each decl may have a comment. - I.append(std::move(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 -tag_invoke( - dom::LazyObjectMapTag t, - IO& io, - Javadoc const& I, - DomCorpus const* domCorpus) -{ - io.defer("description", [&I, domCorpus] { - return dom::LazyArray(I.blocks, domCorpus); - }); - if (I.brief && !I.brief->children.empty()) - { - io.map("brief", I.brief); - } - io.defer("returns", [&I, domCorpus] { - return dom::LazyArray(I.returns, domCorpus); - }); - io.defer("params", [&I, domCorpus] { - return dom::LazyArray(I.params, domCorpus); - }); - io.defer("tparams", [&I, domCorpus] { - return dom::LazyArray(I.tparams, domCorpus); - }); - io.defer("exceptions", [&I, domCorpus] { - return dom::LazyArray(I.exceptions, domCorpus); - }); - io.defer("sees", [&I, domCorpus] { - return dom::LazyArray(I.sees, domCorpus); - }); - io.defer("relates", [&I, domCorpus] { - return dom::LazyArray(I.relates, domCorpus); - }); - io.defer("related", [&I, domCorpus] { - return dom::LazyArray(I.related, domCorpus); - }); - io.defer("preconditions", [&I, domCorpus] { - return dom::LazyArray(I.preconditions, domCorpus); - }); - io.defer("postconditions", [&I, domCorpus] { - return dom::LazyArray(I.postconditions, domCorpus); - }); -} - -/** Return the @ref Javadoc as a @ref dom::Value object. - */ -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - Javadoc const& I, - DomCorpus const* domCorpus) -{ - v = dom::LazyObject(I, domCorpus); -} - -} // clang::mrdocs - -#endif diff --git a/include/mrdocs/Metadata/Name.hpp b/include/mrdocs/Metadata/Name.hpp index 9ea25520d..c97340fe8 100644 --- a/include/mrdocs/Metadata/Name.hpp +++ b/include/mrdocs/Metadata/Name.hpp @@ -13,151 +13,30 @@ #define MRDOCS_API_METADATA_NAME_HPP #include -#include -#include +#include +#include +#include #include -namespace clang::mrdocs { - -enum class NameKind -{ - /// A simple identifier - Identifier = 1, // for bitstream - /// A template instantiation - Specialization -}; - -MRDOCS_DECL -dom::String -toString(NameKind kind) noexcept; - -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - NameKind const kind) -{ - v = toString(kind); -} - -/** Represents a name for a named `TypeInfo` - - When the `TypeInfo` is a named type, this class - represents the name of the type. - - It also includes the symbol ID of the named type, - so that it can be referenced in the documentation. - - This allows the `TypeInfo` to store either a - `NameInfo` or a `SpecializationNameInfo`, - which contains the arguments for a template specialization - without requiring the application to extract an - unnecessary symbol. - */ -struct NameInfo -{ - /** The kind of name this is. - */ - NameKind Kind; - - /** The SymbolID of the named symbol, if it exists. - */ - SymbolID id = SymbolID::invalid; - - /** The unqualified name. - */ - std::string Name; - - /** The parent name info, if any. - - This recursively includes information about - the parent, such as the symbol ID and - potentially template arguments, when - the parent is a SpecializationNameInfo. - - This is particularly useful because the - parent of `id` could be a primary template. - In this case, the Prefix will contain - this primary template information - and the template arguments. - - */ - Polymorphic Prefix = std::nullopt; - - constexpr bool isIdentifier() const noexcept { return Kind == NameKind::Identifier; } - constexpr bool isSpecialization() const noexcept { return Kind == NameKind::Specialization; } - - constexpr - NameInfo() noexcept - : NameInfo(NameKind::Identifier) {} - - explicit - constexpr - NameInfo(NameKind const kind) noexcept - : Kind(kind) {} - - constexpr virtual ~NameInfo() = default; - - std::strong_ordering - operator<=>(NameInfo const& other) const; - - bool - operator==(NameInfo const& other) const - { - return std::is_eq(*this <=> other); - } -}; - -/** Represents a (possibly qualified) symbol name with template arguments. -*/ -struct SpecializationNameInfo final - : NameInfo -{ - /** The template arguments. - */ - std::vector> TemplateArgs; - - /** The SymbolID of the named symbol, if it exists. - */ - SymbolID specializationID = SymbolID::invalid; - - constexpr - SpecializationNameInfo() noexcept - : NameInfo(NameKind::Specialization) - {} - - auto - operator<=>(SpecializationNameInfo const& other) const - { - if (auto const r = - dynamic_cast(*this) <=> dynamic_cast(other); - !std::is_eq(r)) - { - return r; - } - return TemplateArgs <=> other.TemplateArgs; - } -}; +namespace mrdocs { template< - std::derived_from NameInfoTy, + std::derived_from NameTy, class Fn, class... Args> decltype(auto) visit( - NameInfoTy& info, + NameTy& info, Fn&& fn, Args&&... args) { - auto visitor = makeVisitor( + auto visitor = makeVisitor( info, std::forward(fn), std::forward(args)...); switch(info.Kind) { - case NameKind::Identifier: - return visitor.template visit(); - case NameKind::Specialization: - return visitor.template visit(); + #define INFO(Type) case NameKind::Type: \ + return visitor.template visit(); +#include default: MRDOCS_UNREACHABLE(); } @@ -165,43 +44,43 @@ visit( MRDOCS_DECL std::strong_ordering -operator<=>(Polymorphic const& lhs, Polymorphic const& rhs); +operator<=>(Polymorphic const& lhs, Polymorphic const& rhs); -inline -bool -operator==(Polymorphic const& lhs, Polymorphic const& rhs) { +inline bool +operator==(Polymorphic const& lhs, Polymorphic const& rhs) +{ return lhs <=> rhs == std::strong_ordering::equal; } -MRDOCS_DECL -std::string -toString(NameInfo const& N); - -MRDOCS_DECL +inline void tag_invoke( dom::ValueFromTag, dom::Value& v, - NameInfo const& I, - DomCorpus const* domCorpus); + Polymorphic const& I, + DomCorpus const* domCorpus) +{ + MRDOCS_ASSERT(!I.valueless_after_move()); + tag_invoke(dom::ValueFromTag{}, v, *I, domCorpus); +} inline void tag_invoke( dom::ValueFromTag, dom::Value& v, - Polymorphic const& I, + Optional> const& I, DomCorpus const* domCorpus) { - if(! I) + if (!I) { v = nullptr; return; } + MRDOCS_ASSERT(!I->valueless_after_move()); tag_invoke(dom::ValueFromTag{}, v, *I, domCorpus); } - -} // clang::mrdocs +} // mrdocs #endif diff --git a/include/mrdocs/Metadata/Name/IdentifierName.hpp b/include/mrdocs/Metadata/Name/IdentifierName.hpp new file mode 100644 index 000000000..ce4ec36a4 --- /dev/null +++ b/include/mrdocs/Metadata/Name/IdentifierName.hpp @@ -0,0 +1,48 @@ +// +// 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_API_METADATA_NAME_IDENTIFIERNAME_HPP +#define MRDOCS_API_METADATA_NAME_IDENTIFIERNAME_HPP + +#include +#include + +namespace mrdocs { + +/** Represents an identifier + + This class is used to represent an identifier + that could be in the corpus or not. + + When the symbol is in the corpus, the `id` + field will be set to the symbol ID of the + symbol. + + When the symbol is not in the corpus, + the `id` field will be set to `SymbolID::invalid`. +*/ +struct IdentifierName final + : Name +{ + constexpr + IdentifierName() noexcept + : Name(NameKind::Identifier) + {} + + auto + operator<=>(IdentifierName const& other) const + { + return asName() <=> other.asName(); + } +}; + +} // mrdocs + +#endif // MRDOCS_API_METADATA_NAME_IDENTIFIERNAME_HPP diff --git a/include/mrdocs/Metadata/Name/NameBase.hpp b/include/mrdocs/Metadata/Name/NameBase.hpp new file mode 100644 index 000000000..ea3f0285f --- /dev/null +++ b/include/mrdocs/Metadata/Name/NameBase.hpp @@ -0,0 +1,151 @@ +// +// 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_API_METADATA_NAME_NAMEBASE_HPP +#define MRDOCS_API_METADATA_NAME_NAMEBASE_HPP + +#include +#include +#include +#include + +namespace mrdocs { + +/* Forward declarations + */ +#define INFO(Type) struct Type##Name; +#include + +/** Represents a name for a named `Type` + + When the `Type` is a named type, this class + represents the name of the type. + + It also includes the symbol ID of the named type, + so that it can be referenced in the documentation. + + This allows the `Type` to store either a + `Name` or a `SpecializationName`, + which contains the arguments for a template specialization + without requiring the application to extract an + unnecessary symbol. + */ +struct Name +{ + /** The kind of name this is. + */ + NameKind Kind; + + /** The SymbolID of the named symbol, if it exists. + */ + SymbolID id = SymbolID::invalid; + + /** The unqualified name. + */ + std::string Identifier; + + /** The parent name info, if any. + + This recursively includes information about + the parent, such as the symbol ID and + potentially template arguments, when + the parent is a SpecializationName. + + This is particularly useful because the + parent of `id` could be a primary template. + In this case, the Prefix will contain + this primary template information + and the template arguments. + + */ + Optional> Prefix = std::nullopt; + + constexpr virtual ~Name() = default; + + std::strong_ordering + operator<=>(Name const& other) const; + + bool + operator==(Name const& other) const + { + return std::is_eq(*this <=> other); + } + + constexpr Name const& asName() const noexcept + { + return *this; + } + + constexpr Name& asName() noexcept + { + return *this; + } + + #define INFO(Type) constexpr bool is##Type() const noexcept { \ + return Kind == NameKind::Type; \ + } +#include + +#define INFO(Type) \ + constexpr Type##Name const& as##Type() const noexcept { \ + if (Kind == NameKind::Type) \ + return reinterpret_cast(*this); \ + MRDOCS_UNREACHABLE(); \ + } +#include + +#define INFO(Type) \ + constexpr Type##Name & as##Type() noexcept { \ + if (Kind == NameKind::Type) \ + return reinterpret_cast(*this); \ + MRDOCS_UNREACHABLE(); \ + } +#include + +#define INFO(Type) \ + constexpr Type##Name const* as##Type##Ptr() const noexcept { \ + if (Kind == NameKind::Type) { return reinterpret_cast(this); } \ + return nullptr; \ + } +#include + +#define INFO(Type) \ + constexpr Type##Name * as##Type##Ptr() noexcept { \ + if (Kind == NameKind::Type) { return reinterpret_cast(this); } \ + return nullptr; \ + } +#include + +protected: + constexpr + Name() noexcept + : Kind(NameKind::Identifier) {}; + + explicit + constexpr + Name(NameKind const kind) noexcept + : Kind(kind) {} +}; + +MRDOCS_DECL +std::string +toString(Name const& N); + +MRDOCS_DECL +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + Name const& I, + DomCorpus const* domCorpus); + +} // mrdocs + +#endif diff --git a/include/mrdocs/Metadata/Name/NameKind.hpp b/include/mrdocs/Metadata/Name/NameKind.hpp new file mode 100644 index 000000000..819c230a3 --- /dev/null +++ b/include/mrdocs/Metadata/Name/NameKind.hpp @@ -0,0 +1,39 @@ +// +// 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_API_METADATA_NAME_NAMEKIND_HPP +#define MRDOCS_API_METADATA_NAME_NAMEKIND_HPP + +#include +#include + +namespace mrdocs { + +enum class NameKind { +#define INFO(Type) Type, +#include +}; + +MRDOCS_DECL +dom::String +toString(NameKind kind) noexcept; + +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, NameKind const kind) +{ + v = toString(kind); +} + +} // mrdocs + +#endif diff --git a/include/mrdocs/Metadata/Name/NameNodes.inc b/include/mrdocs/Metadata/Name/NameNodes.inc new file mode 100644 index 000000000..c8e6aa8a7 --- /dev/null +++ b/include/mrdocs/Metadata/Name/NameNodes.inc @@ -0,0 +1,19 @@ +// +// 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 INFO +#define INFO(PascalName) +#endif + +INFO(Identifier) +INFO(Specialization) + +#undef INFO + diff --git a/include/mrdocs/Metadata/Name/SpecializationName.hpp b/include/mrdocs/Metadata/Name/SpecializationName.hpp new file mode 100644 index 000000000..a7ad1090e --- /dev/null +++ b/include/mrdocs/Metadata/Name/SpecializationName.hpp @@ -0,0 +1,52 @@ +// +// 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_API_METADATA_NAME_SPECIALIZATIONNAME_HPP +#define MRDOCS_API_METADATA_NAME_SPECIALIZATIONNAME_HPP + +#include +#include +#include + +namespace mrdocs { + +/** Represents a (possibly qualified) symbol name with template arguments. +*/ +struct SpecializationName final + : Name +{ + /** The template arguments. + */ + std::vector> TemplateArgs; + + /** The SymbolID of the named symbol, if it exists. + */ + SymbolID specializationID = SymbolID::invalid; + + constexpr + SpecializationName() noexcept + : Name(NameKind::Specialization) + {} + + auto + operator<=>(SpecializationName const& other) const + { + if (auto const r = asName() <=> other.asName(); + !std::is_eq(r)) + { + return r; + } + return TemplateArgs <=> other.TemplateArgs; + } +}; + +} // mrdocs + +#endif // MRDOCS_API_METADATA_NAME_SPECIALIZATIONNAME_HPP diff --git a/include/mrdocs/Metadata/Source.hpp b/include/mrdocs/Metadata/Source.hpp deleted file mode 100644 index 4c887b62c..000000000 --- a/include/mrdocs/Metadata/Source.hpp +++ /dev/null @@ -1,168 +0,0 @@ -// -// This is a derivative work. originally part of the LLVM Project. -// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) -// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) -// -// Official repository: https://github.com/cppalliance/mrdocs -// - -#ifndef MRDOCS_API_METADATA_SOURCE_HPP -#define MRDOCS_API_METADATA_SOURCE_HPP - -#include -#include -#include -#include - -namespace clang::mrdocs { - -enum class FileKind -{ - /// File in the source directory - Source, - /// File in a system include directory - System, - /// File outside the source directory - Other -}; - -MRDOCS_DECL -std::string_view -toString(FileKind kind); - -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - FileKind kind) -{ - v = toString(kind); -} - -struct MRDOCS_DECL - Location -{ - /** The full file path - */ - std::string FullPath; - - /** The file path relative to one of the search directories - */ - std::string ShortPath; - - /** The file path relative to the source-root directory - */ - std::string SourcePath; - - /** Line number within the file - */ - unsigned LineNumber = 0; - - /** Whether this location has documentation. - */ - bool Documented = false; - - //-------------------------------------------- - - constexpr - Location( - std::string_view const full_path = {}, - std::string_view const short_path = {}, - std::string_view const source_path = {}, - unsigned const line = 0, - bool const documented = false) - : FullPath(full_path) - , ShortPath(short_path) - , SourcePath(source_path) - , LineNumber(line) - , Documented(documented) - { - } - - auto operator<=>(Location const&) const = default; -}; - -MRDOCS_DECL -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - Location const& loc); - -struct LocationEmptyPredicate -{ - constexpr bool operator()( - Location const& loc) const noexcept - { - return loc.ShortPath.empty(); - } -}; - -/** Like std::optional -*/ -using OptionalLocation = Optional; - -/** Stores source information for a declaration. -*/ -struct MRDOCS_DECL - SourceInfo -{ - /** Location where the entity was defined - - KRYSTIAN NOTE: this is used for entities which cannot be - redeclared -- regardless of whether such a declaration is - actually a definition (e.g. alias-declarations and - typedef declarations are never definition). - */ - OptionalLocation DefLoc; - - /** Locations where the entity was declared. - - This does not include the definition. - */ - std::vector Loc; - - constexpr SourceInfo const& asSourceInfo() const noexcept - { - return *this; - } - - constexpr SourceInfo& asSourceInfo() noexcept - { - return *this; - } - - constexpr virtual ~SourceInfo() = default; - - auto operator<=>(SourceInfo const&) const = default; - -protected: - constexpr SourceInfo() = default; -}; - -MRDOCS_DECL -void -merge(SourceInfo& I, SourceInfo const& Other); - -MRDOCS_DECL -void -merge(SourceInfo& I, SourceInfo&& Other); - -MRDOCS_DECL -OptionalLocation -getPrimaryLocation(SourceInfo const& I, bool preferDefinition); - -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - SourceInfo const& I); - -} // clang::mrdocs - -#endif diff --git a/include/mrdocs/Metadata/Specifiers.hpp b/include/mrdocs/Metadata/Specifiers.hpp index 9d8429228..1d0fd86b0 100644 --- a/include/mrdocs/Metadata/Specifiers.hpp +++ b/include/mrdocs/Metadata/Specifiers.hpp @@ -4,6 +4,7 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com) // // Official repository: https://github.com/cppalliance/mrdocs // @@ -11,388 +12,14 @@ #ifndef MRDOCS_API_METADATA_SPECIFIERS_HPP #define MRDOCS_API_METADATA_SPECIFIERS_HPP -#include -#include -#include - -namespace clang::mrdocs { - -/** Access specifier. - - None is set to zero since it is the most - frequently occurring access, and it is - elided by the bitstream encoder because it - has an all-zero bit pattern. This improves - compression in the bitstream. - - None is used for namespace members and friend; - such declarations have no access. -*/ -enum class AccessKind -{ - /// Unspecified access - None = 0, - /// Public access - Public, - /// Protected access - Protected, - /// Private access - Private, -}; - -/** `constexpr`/`consteval` specifier kinds - - [dcl.spec.general] p2: At most one of the `constexpr`, `consteval`, - and `constinit` keywords shall appear in a decl-specifier-seq -*/ -enum class ConstexprKind -{ - /// No `constexpr` or `consteval` specifier - None = 0, - /// The `constexpr` specifier - Constexpr, - /// The `consteval` specifier - /// only valid for functions - Consteval, -}; - -/** Explicit specifier kinds -*/ -enum class ExplicitKind -{ - /** No explicit-specifier or explicit-specifier evaluated to false - */ - False = 0, - /** explicit-specifier evaluates to true - */ - True, - /** Dependent explicit-specifier - */ - Dependent -}; - -// KRYSTIAN FIXME: this needs to be improved (a lot) -struct ExplicitInfo -{ - /** Whether an explicit-specifier was user-written. - */ - bool Implicit = true; - - /** The evaluated exception specification. - */ - ExplicitKind Kind = ExplicitKind::False; - - /** The operand of the explicit-specifier, if any. - */ - std::string Operand; - - auto operator<=>(ExplicitInfo const&) const = default; -}; - -/** Exception specification kinds -*/ -enum class NoexceptKind -{ - /** Potentially-throwing exception specification - */ - False = 0, - /** Non-throwing exception specification - */ - True, - /** Dependent exception specification - */ - Dependent -}; - -// KRYSTIAN FIXME: this needs to be improved (a lot) -struct NoexceptInfo -{ - /** Whether a noexcept-specifier was user-written. - */ - bool Implicit = true; - - /** The evaluated exception specification. - */ - NoexceptKind Kind = NoexceptKind::False; - - /** The operand of the noexcept-specifier, if any. - */ - std::string Operand; - - auto operator<=>(NoexceptInfo const&) const = default; -}; - -/** Operator kinds -*/ -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, -}; - -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - OperatorKind kind) -{ - v = static_cast>(kind); -} - -/** Determines whether the operator is potentially unary. - */ -MRDOCS_DECL -bool -isUnaryOperator(OperatorKind kind) noexcept; - -/** Determines whether the operator is potentially binary. - */ -MRDOCS_DECL -bool -isBinaryOperator(OperatorKind kind) noexcept; - -/** Reference type kinds -*/ -enum class ReferenceKind -{ - /// Not a reference - None = 0, - /// An L-Value reference - LValue, - /// An R-Value reference - RValue -}; - -/** Storage class kinds - - [dcl.stc] p1: At most one storage-class-specifier shall appear - in a given decl-specifier-seq, except that `thread_local` - may appear with `static` or `extern`. -*/ -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, - /// register storage-class-specifier (removed in C++17) - /// only valid for variables - Register -}; - -MRDOCS_DECL dom::String toString(AccessKind kind) noexcept; -MRDOCS_DECL dom::String toString(ConstexprKind kind) noexcept; -MRDOCS_DECL dom::String toString(ExplicitKind kind) noexcept; -MRDOCS_DECL dom::String toString(NoexceptKind kind) noexcept; -MRDOCS_DECL dom::String toString(ReferenceKind kind) noexcept; -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 -toString( - NoexceptInfo const& info, - bool resolved = false, - bool implicit = false); - -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - NoexceptInfo const& info) -{ - v = toString(info, false, false); -} - -/** Convert ExplicitInfo to a string. - - @param info The explicit-specifier information. - @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 -toString( - ExplicitInfo const& info, - bool resolved = false, - 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 -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - ExplicitInfo const& I) -{ - v = toString(I); -} - -/** Return the AccessKind as a @ref dom::Value string. - */ -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - AccessKind const kind) -{ - v = toString(kind); -} - -/** Return the ConstexprKind as a @ref dom::Value string. - */ -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - ConstexprKind const kind) -{ - v = toString(kind); -} - -/** Return the StorageClassKind as a @ref dom::Value string. - */ -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - StorageClassKind const kind) -{ - v = toString(kind); -} - -/** Return the ReferenceKind as a @ref dom::Value string. - */ -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - ReferenceKind kind) -{ - v = toString(kind); -} - -} // clang::mrdocs +#include +#include +#include +#include +#include +#include +#include +#include +#include #endif diff --git a/include/mrdocs/Metadata/Specifiers/AccessKind.hpp b/include/mrdocs/Metadata/Specifiers/AccessKind.hpp new file mode 100644 index 000000000..430385ec6 --- /dev/null +++ b/include/mrdocs/Metadata/Specifiers/AccessKind.hpp @@ -0,0 +1,62 @@ +// +// 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) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_SPECIFIERS_ACCESSKIND_HPP +#define MRDOCS_API_METADATA_SPECIFIERS_ACCESSKIND_HPP + +#include +#include +#include + +namespace mrdocs { + +/** Access specifier. + + None is set to zero since it is the most + frequently occurring access, and it is + elided by the bitstream encoder because it + has an all-zero bit pattern. This improves + compression in the bitstream. + + None is used for namespace members and friend; + such declarations have no access. +*/ +enum class AccessKind +{ + /// Unspecified access + None = 0, + /// Public access + Public, + /// Protected access + Protected, + /// Private access + Private, +}; + +MRDOCS_DECL +dom::String +toString(AccessKind kind) noexcept; + +/** Return the AccessKind as a @ref dom::Value string. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + AccessKind const kind) +{ + v = toString(kind); +} + +} // mrdocs + +#endif diff --git a/include/mrdocs/Metadata/Specifiers/ConstexprKind.hpp b/include/mrdocs/Metadata/Specifiers/ConstexprKind.hpp new file mode 100644 index 000000000..9ec35ee00 --- /dev/null +++ b/include/mrdocs/Metadata/Specifiers/ConstexprKind.hpp @@ -0,0 +1,54 @@ +// +// 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) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_SPECIFIERS_CONSTEXPRKIND_HPP +#define MRDOCS_API_METADATA_SPECIFIERS_CONSTEXPRKIND_HPP + +#include +#include + +namespace mrdocs { + +/** `constexpr`/`consteval` specifier kinds + + [dcl.spec.general] p2: At most one of the `constexpr`, `consteval`, + and `constinit` keywords shall appear in a decl-specifier-seq +*/ +enum class ConstexprKind +{ + /// No `constexpr` or `consteval` specifier + None = 0, + /// The `constexpr` specifier + Constexpr, + /// The `consteval` specifier + /// only valid for functions + Consteval, +}; + +MRDOCS_DECL +dom::String +toString(ConstexprKind kind) noexcept; + +/** Return the ConstexprKind as a @ref dom::Value string. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + ConstexprKind const kind) +{ + v = toString(kind); +} + +} // mrdocs + +#endif diff --git a/include/mrdocs/Metadata/Specifiers/ExplicitInfo.hpp b/include/mrdocs/Metadata/Specifiers/ExplicitInfo.hpp new file mode 100644 index 000000000..ff95626e4 --- /dev/null +++ b/include/mrdocs/Metadata/Specifiers/ExplicitInfo.hpp @@ -0,0 +1,75 @@ +// +// 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) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_SPECIFIERS_EXPLICITINFO_HPP +#define MRDOCS_API_METADATA_SPECIFIERS_EXPLICITINFO_HPP + +#include +#include +#include + +namespace mrdocs { + +/** + Stores only the operand of the explicit-specifier or noexcept-specifier as a string. + The complete expression is not preserved at this time. + This is a temporary design and may be improved in the future. +*/ +struct ExplicitInfo +{ + /** Whether an explicit-specifier was user-written. + */ + bool Implicit = true; + + /** The evaluated exception specification. + */ + ExplicitKind Kind = ExplicitKind::False; + + /** The operand of the explicit-specifier, if any. + */ + std::string Operand; + + auto operator<=>(ExplicitInfo const&) const = default; +}; + +/** Convert ExplicitInfo to a string. + + @param info The explicit-specifier information. + @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 +toString( + ExplicitInfo const& info, + bool resolved = false, + 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 +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + ExplicitInfo const& I) +{ + v = toString(I); +} + +} // mrdocs + +#endif diff --git a/include/mrdocs/Metadata/Specifiers/ExplicitKind.hpp b/include/mrdocs/Metadata/Specifiers/ExplicitKind.hpp new file mode 100644 index 000000000..19a93923f --- /dev/null +++ b/include/mrdocs/Metadata/Specifiers/ExplicitKind.hpp @@ -0,0 +1,42 @@ +// +// 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) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_SPECIFIERS_EXPLICITKIND_HPP +#define MRDOCS_API_METADATA_SPECIFIERS_EXPLICITKIND_HPP + +#include +#include +#include + +namespace mrdocs { + +/** Explicit specifier kinds +*/ +enum class ExplicitKind +{ + /** No explicit-specifier or explicit-specifier evaluated to false + */ + False = 0, + /** explicit-specifier evaluates to true + */ + True, + /** Dependent explicit-specifier + */ + Dependent +}; + +MRDOCS_DECL +dom::String +toString(ExplicitKind kind) noexcept; + +} // mrdocs + +#endif diff --git a/include/mrdocs/Metadata/Specifiers/NoexceptInfo.hpp b/include/mrdocs/Metadata/Specifiers/NoexceptInfo.hpp new file mode 100644 index 000000000..48a644f90 --- /dev/null +++ b/include/mrdocs/Metadata/Specifiers/NoexceptInfo.hpp @@ -0,0 +1,74 @@ +// +// 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) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_SPECIFIERS_NOEXCEPTINFO_HPP +#define MRDOCS_API_METADATA_SPECIFIERS_NOEXCEPTINFO_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace mrdocs { + +// KRYSTIAN FIXME: this needs to be improved (a lot) +struct NoexceptInfo +{ + /** Whether a noexcept-specifier was user-written. + */ + bool Implicit = true; + + /** The evaluated exception specification. + */ + NoexceptKind Kind = NoexceptKind::False; + + /** The operand of the noexcept-specifier, if any. + */ + std::string Operand; + + auto operator<=>(NoexceptInfo const&) const = default; +}; + +/** 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 +toString( + NoexceptInfo const& info, + bool resolved = false, + bool implicit = false); + +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + NoexceptInfo const& info) +{ + v = toString(info, false, false); +} + +} // mrdocs + +#endif diff --git a/include/mrdocs/Metadata/Specifiers/NoexceptKind.hpp b/include/mrdocs/Metadata/Specifiers/NoexceptKind.hpp new file mode 100644 index 000000000..4ef3f8f13 --- /dev/null +++ b/include/mrdocs/Metadata/Specifiers/NoexceptKind.hpp @@ -0,0 +1,42 @@ +// +// 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) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_SPECIFIERS_NOEXCEPTKIND_HPP +#define MRDOCS_API_METADATA_SPECIFIERS_NOEXCEPTKIND_HPP + +#include +#include +#include + +namespace mrdocs { + +/** Exception specification kinds +*/ +enum class NoexceptKind +{ + /** Potentially-throwing exception specification + */ + False = 0, + /** Non-throwing exception specification + */ + True, + /** Dependent exception specification + */ + Dependent +}; + +MRDOCS_DECL +dom::String +toString(NoexceptKind kind) noexcept; + +} // mrdocs + +#endif diff --git a/include/mrdocs/Metadata/Specifiers/OperatorKind.hpp b/include/mrdocs/Metadata/Specifiers/OperatorKind.hpp new file mode 100644 index 000000000..fef84be79 --- /dev/null +++ b/include/mrdocs/Metadata/Specifiers/OperatorKind.hpp @@ -0,0 +1,207 @@ +// +// 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) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_SPECIFIERS_OPERATORKIND_HPP +#define MRDOCS_API_METADATA_SPECIFIERS_OPERATORKIND_HPP + +#include +#include +#include +#include + +namespace mrdocs { + +/** Operator kinds +*/ +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, +}; + +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + OperatorKind kind) +{ + v = static_cast>(kind); +} + +/** Determines whether the operator is potentially unary. + */ +MRDOCS_DECL +bool +isUnaryOperator(OperatorKind kind) noexcept; + +/** Determines whether the operator is potentially binary. + */ +MRDOCS_DECL +bool +isBinaryOperator(OperatorKind kind) noexcept; + +/** Return the name of an operator as a string. + + @param kind The kind of operator. + @param include_keyword Whether the name + should be prefixed with the `operator` keyword. +*/ +MRDOCS_DECL +std::string_view +getOperatorName( + OperatorKind kind, + bool include_keyword = false) noexcept; + +/** Return the short name of an operator as a string. +*/ +MRDOCS_DECL +std::string_view +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 +getOperatorKindFromSuffix(std::string_view suffix) noexcept; + +/** Return the safe name of an operator as a string. + + @param kind The kind of operator. + @param include_keyword Whether the name + should be prefixed with `operator_`. +*/ +MRDOCS_DECL +std::string_view +getSafeOperatorName( + OperatorKind kind, + 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. + */ +Optional +getOperatorReadableName( + OperatorKind kind, + int nParams); + +} // mrdocs + +#endif diff --git a/include/mrdocs/Metadata/Specifiers/ReferenceKind.hpp b/include/mrdocs/Metadata/Specifiers/ReferenceKind.hpp new file mode 100644 index 000000000..608e73665 --- /dev/null +++ b/include/mrdocs/Metadata/Specifiers/ReferenceKind.hpp @@ -0,0 +1,49 @@ +// +// 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) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_SPECIFIERS_REFERENCEKIND_HPP +#define MRDOCS_API_METADATA_SPECIFIERS_REFERENCEKIND_HPP + +#include +#include +#include + +namespace mrdocs { + +/** Reference type kinds +*/ +enum class ReferenceKind +{ + /// Not a reference + None = 0, + /// An L-Value reference + LValue, + /// An R-Value reference + RValue +}; + +MRDOCS_DECL dom::String toString(ReferenceKind kind) noexcept; + +/** Return the ReferenceKind as a @ref dom::Value string. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + ReferenceKind kind) +{ + v = toString(kind); +} + +} // mrdocs + +#endif diff --git a/include/mrdocs/Metadata/Specifiers/StorageClassKind.hpp b/include/mrdocs/Metadata/Specifiers/StorageClassKind.hpp new file mode 100644 index 000000000..6de36a278 --- /dev/null +++ b/include/mrdocs/Metadata/Specifiers/StorageClassKind.hpp @@ -0,0 +1,61 @@ +// +// 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) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_SPECIFIERS_STORAGECLASSKIND_HPP +#define MRDOCS_API_METADATA_SPECIFIERS_STORAGECLASSKIND_HPP + +#include +#include +#include + +namespace mrdocs { + +/** Storage class kinds + + [dcl.stc] p1: At most one storage-class-specifier shall appear + in a given decl-specifier-seq, except that `thread_local` + may appear with `static` or `extern`. +*/ +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, + /// register storage-class-specifier (removed in C++17) + /// only valid for variables + Register +}; + +MRDOCS_DECL +dom::String +toString(StorageClassKind kind) noexcept; + +/** Return the StorageClassKind as a @ref dom::Value string. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + StorageClassKind const kind) +{ + v = toString(kind); +} + +} // mrdocs + +#endif diff --git a/include/mrdocs/Metadata/Symbol.hpp b/include/mrdocs/Metadata/Symbol.hpp new file mode 100644 index 000000000..d9e9608ed --- /dev/null +++ b/include/mrdocs/Metadata/Symbol.hpp @@ -0,0 +1,140 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_SYMBOL_HPP +#define MRDOCS_API_METADATA_SYMBOL_HPP + +#include +#include + +namespace mrdocs { + +/** Invoke a function object with a type derived from Symbol + + This function will invoke the function object `fn` with + a type derived from `Symbol` as the first argument, followed + by `args...`. The type of the first argument is determined + by the `SymbolKind` of the `Symbol` object. + + @param info The Symbol object to visit + @param fn The function object to call + @param args Additional arguments to pass to the function object + @return The result of calling the function object with the derived type + */ +template< + std::derived_from SymbolTy, + class Fn, + class... Args> +decltype(auto) +visit( + SymbolTy& info, + Fn&& fn, + Args&&... args) +{ + auto visitor = makeVisitor< + Symbol>( + info, std::forward(fn), + std::forward(args)...); + switch(info.Kind) + { + #define INFO(Type) \ + case SymbolKind::Type: \ + return visitor.template visit(); +#include + default: + MRDOCS_UNREACHABLE(); + } +} + +/** Merges two Symbol objects according to the behavior of the derived class. + + @param I The Symbol object to merge into. + @param Other The Symbol object to merge from. + */ +template SymbolTy> +void +merge(SymbolTy& I, SymbolTy&& Other) +{ + MRDOCS_ASSERT(I.Kind == Other.Kind); + MRDOCS_ASSERT(I.id == Other.id); + Symbol& base = I; + visit(base, [&](DerivedSymbolTy& derived) mutable + { + DerivedSymbolTy& otherDerived = static_cast(Other); + merge(derived, std::move(otherDerived)); + }); +} + +/** A concept for types that have `Symbol` members. + + In most cases `T` is another `Symbol` type that + has a `Members` member which is a range of + `SymbolID` values. +*/ +template +concept SymbolParent = requires(SymbolTy const& I) +{ + { allMembers(I) } -> range_of; +}; + +/** Map the Polymorphic Symbol to a @ref dom::Object. + + @param io The output parameter to receive the dom::Object. + @param I The polymorphic Symbol to convert. + @param domCorpus The DomCorpus used to resolve references. + */ +template PolymorphicSymbol> +requires std::derived_from +void +tag_invoke( + dom::LazyObjectMapTag, + IO& io, + PolymorphicSymbol const& I, + DomCorpus const* domCorpus) +{ + visit(*I, [&](auto const& U) + { + tag_invoke( + dom::LazyObjectMapTag{}, + io, + U, + domCorpus); + }); +} + +/** Map the Polymorphic Symbol as a @ref dom::Value object. + + @param io The output parameter to receive the dom::Value. + @param I The polymorphic Symbol to convert. + @param domCorpus The DomCorpus used to resolve references. + */ +//template SymbolTy> +//requires std::derived_from +//void +//tag_invoke( +// dom::ValueFromTag, +// IO& io, +// SymbolTy const& I, +// DomCorpus const* domCorpus) +//{ +// visit(*I, [&](auto const& U) +// { +// tag_invoke( +// dom::ValueFromTag{}, +// io, +// U, +// domCorpus); +// }); +//} + +} // mrdocs + +#endif // MRDOCS_API_METADATA_SYMBOL_HPP diff --git a/include/mrdocs/Metadata/Info/Concept.hpp b/include/mrdocs/Metadata/Symbol/Concept.hpp similarity index 59% rename from include/mrdocs/Metadata/Info/Concept.hpp rename to include/mrdocs/Metadata/Symbol/Concept.hpp index 6a70fa066..7f3a31f3e 100644 --- a/include/mrdocs/Metadata/Info/Concept.hpp +++ b/include/mrdocs/Metadata/Symbol/Concept.hpp @@ -8,25 +8,27 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_API_METADATA_CONCEPT_HPP -#define MRDOCS_API_METADATA_CONCEPT_HPP +#ifndef MRDOCS_API_METADATA_SYMBOL_CONCEPT_HPP +#define MRDOCS_API_METADATA_SYMBOL_CONCEPT_HPP -#include +#include +#include +#include #include -#include +#include +#include #include -#include -namespace clang::mrdocs { +namespace mrdocs { /** Info for concepts. */ -struct ConceptInfo final - : InfoCommonBase +struct ConceptSymbol final + : SymbolCommonBase { /** The concepts template parameters */ - std::optional Template; + Optional Template; /** The concepts constraint-expression */ @@ -34,24 +36,24 @@ struct ConceptInfo final //-------------------------------------------- - explicit ConceptInfo(SymbolID const &ID) noexcept - : InfoCommonBase(ID) + explicit ConceptSymbol(SymbolID const &ID) noexcept + : SymbolCommonBase(ID) { } std::strong_ordering - operator<=>(ConceptInfo const& other) const; + operator<=>(ConceptSymbol const& other) const; }; MRDOCS_DECL void -merge(ConceptInfo& I, ConceptInfo&& Other); +merge(ConceptSymbol& I, ConceptSymbol&& Other); -/** Map a ConceptInfo to a dom::Object. +/** Map a ConceptSymbol 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 I The ConceptSymbol to map. @param domCorpus The DomCorpus used to create */ template @@ -59,10 +61,10 @@ void tag_invoke( dom::LazyObjectMapTag t, IO& io, - ConceptInfo const& I, + ConceptSymbol const& I, DomCorpus const* domCorpus) { - tag_invoke(t, io, dynamic_cast(I), domCorpus); + tag_invoke(t, io, I.asInfo(), domCorpus); io.map("template", I.Template); if (!I.Constraint.Written.empty()) { @@ -70,19 +72,19 @@ tag_invoke( } } -/** Map the ConceptInfo to a @ref dom::Value object. +/** Map the ConceptSymbol to a @ref dom::Value object. */ inline void tag_invoke( dom::ValueFromTag, dom::Value& v, - ConceptInfo const& I, + ConceptSymbol const& I, DomCorpus const* domCorpus) { v = dom::LazyObject(I, domCorpus); } -} // clang::mrdocs +} // mrdocs -#endif +#endif // MRDOCS_API_METADATA_SYMBOL_CONCEPT_HPP diff --git a/include/mrdocs/Metadata/Symbol/Enum.hpp b/include/mrdocs/Metadata/Symbol/Enum.hpp new file mode 100644 index 000000000..935da1553 --- /dev/null +++ b/include/mrdocs/Metadata/Symbol/Enum.hpp @@ -0,0 +1,115 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_SYMBOL_ENUM_HPP +#define MRDOCS_API_METADATA_SYMBOL_ENUM_HPP + +#include +#include +#include +#include +#include +#include + +namespace mrdocs { + +struct EnumSymbol final + : SymbolCommonBase +{ + /** Indicates whether this enum is scoped (e.g. enum class). + + If true, the enumerators are accessed with the scope resolution + operator (e.g. EnumName::Enumerator). + + If false, the enumerators are accessed directly (e.g. Enumerator) + in the parent context. + */ + bool Scoped = false; + + /** The underlying type of this enum, if explicitly specified. + + If not specified, the underlying type is an implementation-defined + integral type that can represent all the enumerator values defined in + the enumeration. + + For `enum Foo : short { ... };` this will be represent `short`. + */ + Optional> UnderlyingType = std::nullopt; + + /** The members of this scope. + + All members are enum constants. + + Enum constants are independent symbol types that + can be documented separately. + */ + std::vector Constants; + + //-------------------------------------------- + + explicit EnumSymbol(SymbolID ID) noexcept + : SymbolCommonBase(ID) + { + } +}; + +inline +auto& +allMembers(EnumSymbol const& T) +{ + return T.Constants; +} + +MRDOCS_DECL +void +merge(EnumSymbol& I, EnumSymbol&& Other); + +/** Map a EnumSymbol to a dom::Object. + + @param t The tag type. + @param io The IO object to use for mapping. + @param I The EnumSymbol to map. + @param domCorpus The DomCorpus used to create + */ +template +void +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + EnumSymbol const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, I.asInfo(), domCorpus); + io.map("type", I.UnderlyingType); + io.map("isScoped", I.Scoped); + io.map("constants", dom::LazyArray(I.Constants, domCorpus)); +} + +/** Map the EnumSymbol to a @ref dom::Value object. + + @param v The output parameter to receive the dom::Value. + @param I The EnumSymbol to convert. + @param domCorpus The DomCorpus used to resolve references. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + EnumSymbol const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs + +#endif // MRDOCS_API_METADATA_SYMBOL_ENUM_HPP diff --git a/include/mrdocs/Metadata/Info/EnumConstant.hpp b/include/mrdocs/Metadata/Symbol/EnumConstant.hpp similarity index 58% rename from include/mrdocs/Metadata/Info/EnumConstant.hpp rename to include/mrdocs/Metadata/Symbol/EnumConstant.hpp index c84681b87..5ff744f37 100644 --- a/include/mrdocs/Metadata/Info/EnumConstant.hpp +++ b/include/mrdocs/Metadata/Symbol/EnumConstant.hpp @@ -8,19 +8,19 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_API_METADATA_ENUMCONSTANT_HPP -#define MRDOCS_API_METADATA_ENUMCONSTANT_HPP +#ifndef MRDOCS_API_METADATA_SYMBOL_ENUMCONSTANT_HPP +#define MRDOCS_API_METADATA_SYMBOL_ENUMCONSTANT_HPP #include -#include -#include +#include +#include -namespace clang::mrdocs { +namespace mrdocs { /** Info for enum constants. */ -struct EnumConstantInfo final - : InfoCommonBase +struct EnumConstantSymbol final + : SymbolCommonBase { /** The initializer expression, if any */ @@ -28,21 +28,21 @@ struct EnumConstantInfo final //-------------------------------------------- - explicit EnumConstantInfo(SymbolID ID) noexcept - : InfoCommonBase(ID) + explicit EnumConstantSymbol(SymbolID ID) noexcept + : SymbolCommonBase(ID) { } }; MRDOCS_DECL void -merge(EnumConstantInfo& I, EnumConstantInfo&& Other); +merge(EnumConstantSymbol& I, EnumConstantSymbol&& Other); -/** Map a EnumConstantInfo to a dom::Object. +/** Map a EnumConstantSymbol 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 I The EnumConstantSymbol to map. @param domCorpus The DomCorpus used to create */ template @@ -50,29 +50,29 @@ void tag_invoke( dom::LazyObjectMapTag t, IO& io, - EnumConstantInfo const& I, + EnumConstantSymbol const& I, DomCorpus const* domCorpus) { - tag_invoke(t, io, dynamic_cast(I), domCorpus); + tag_invoke(t, io, I.asInfo(), domCorpus); if (!I.Initializer.Written.empty()) { io.map("initializer", I.Initializer.Written); } } -/** Map the EnumConstantInfo to a @ref dom::Value object. +/** Map the EnumConstantSymbol to a @ref dom::Value object. */ inline void tag_invoke( dom::ValueFromTag, dom::Value& v, - EnumConstantInfo const& I, + EnumConstantSymbol const& I, DomCorpus const* domCorpus) { v = dom::LazyObject(I, domCorpus); } -} // clang::mrdocs +} // mrdocs -#endif +#endif // MRDOCS_API_METADATA_SYMBOL_ENUMCONSTANT_HPP diff --git a/include/mrdocs/Metadata/ExtractionMode.hpp b/include/mrdocs/Metadata/Symbol/ExtractionMode.hpp similarity index 91% rename from include/mrdocs/Metadata/ExtractionMode.hpp rename to include/mrdocs/Metadata/Symbol/ExtractionMode.hpp index 9d2b7b832..b0db5f48e 100644 --- a/include/mrdocs/Metadata/ExtractionMode.hpp +++ b/include/mrdocs/Metadata/Symbol/ExtractionMode.hpp @@ -9,12 +9,12 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_API_METADATA_EXTRACTIONMODE_HPP -#define MRDOCS_API_METADATA_EXTRACTIONMODE_HPP +#ifndef MRDOCS_API_METADATA_SYMBOL_EXTRACTIONMODE_HPP +#define MRDOCS_API_METADATA_SYMBOL_EXTRACTIONMODE_HPP #include -namespace clang::mrdocs { +namespace mrdocs { /** Determine why a symbol is extracted @@ -54,7 +54,7 @@ enum class ExtractionMode Dependency, }; -/** Return the name of the InfoKind as a string. +/** Return the name of the SymbolKind as a string. */ constexpr std::string_view @@ -74,7 +74,7 @@ toString(ExtractionMode kind) noexcept MRDOCS_UNREACHABLE(); } -/** Return the InfoKind from a @ref dom::Value string. +/** Return the SymbolKind from a @ref dom::Value string. */ inline void @@ -119,6 +119,6 @@ mostSpecific(ExtractionMode const a, ExtractionMode const b) noexcept std::max(static_cast(a), static_cast(b))); } -} // clang::mrdocs +} // mrdocs -#endif +#endif // MRDOCS_API_METADATA_SYMBOL_EXTRACTIONMODE_HPP diff --git a/include/mrdocs/Metadata/Symbol/FileKind.hpp b/include/mrdocs/Metadata/Symbol/FileKind.hpp new file mode 100644 index 000000000..87f966956 --- /dev/null +++ b/include/mrdocs/Metadata/Symbol/FileKind.hpp @@ -0,0 +1,49 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_SYMBOL_FILEKIND_HPP +#define MRDOCS_API_METADATA_SYMBOL_FILEKIND_HPP + +#include +#include +#include +#include + +namespace mrdocs { + +enum class FileKind +{ + /// File in the source directory + Source, + /// File in a system include directory + System, + /// File outside the source directory + Other +}; + +MRDOCS_DECL +std::string_view +toString(FileKind kind); + +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + FileKind kind) +{ + v = toString(kind); +} + +} // mrdocs + +#endif // MRDOCS_API_METADATA_SYMBOL_FILEKIND_HPP diff --git a/include/mrdocs/Metadata/Info/Friend.hpp b/include/mrdocs/Metadata/Symbol/Friend.hpp similarity index 69% rename from include/mrdocs/Metadata/Info/Friend.hpp rename to include/mrdocs/Metadata/Symbol/Friend.hpp index 36b2948ac..6dabb0c38 100644 --- a/include/mrdocs/Metadata/Info/Friend.hpp +++ b/include/mrdocs/Metadata/Symbol/Friend.hpp @@ -8,20 +8,26 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_API_METADATA_FRIEND_HPP -#define MRDOCS_API_METADATA_FRIEND_HPP +#ifndef MRDOCS_API_METADATA_SYMBOL_FRIEND_HPP +#define MRDOCS_API_METADATA_SYMBOL_FRIEND_HPP -#include -#include +#include +#include #include -namespace clang::mrdocs { +namespace mrdocs { /** Info for friend declarations. - Friendship is not transitive - Friendship is not inherited - - Access specifiers have no effect on the meaning of friend declarations + - Access specifiers do not affect the meaning of friend declarations + + The friends of a record are stored directly in the record's metadata. + + If the friend declaration is documented, the documentation is + stored in the befriended symbol's metadata rather than in the + relationship. */ struct FriendInfo final { @@ -30,8 +36,10 @@ struct FriendInfo final SymbolID id = SymbolID::invalid; /** Befriended type. + + This member is nullable and only used when befriending a type. */ - Polymorphic Type = std::nullopt; + Optional> Type = std::nullopt; }; MRDOCS_DECL @@ -82,6 +90,6 @@ tag_invoke( v = dom::LazyObject(I, domCorpus); } -} // clang::mrdocs +} // mrdocs -#endif +#endif // MRDOCS_API_METADATA_SYMBOL_FRIEND_HPP diff --git a/include/mrdocs/Metadata/Symbol/Function.hpp b/include/mrdocs/Metadata/Symbol/Function.hpp new file mode 100644 index 000000000..17ca2953b --- /dev/null +++ b/include/mrdocs/Metadata/Symbol/Function.hpp @@ -0,0 +1,181 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_SYMBOL_FUNCTION_HPP +#define MRDOCS_API_METADATA_SYMBOL_FUNCTION_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mrdocs { + +// TODO: Expand to allow for documenting templating and default args. +// Info for functions. +struct FunctionSymbol final + : SymbolCommonBase +{ + /** Info about the return type of this function. + + If the function has a deduced return type, this contains + `auto` to indicate that. + + By default, we also use `auto` in the member to indicate + an unknown return type. + */ + Polymorphic ReturnType = Polymorphic(AutoType{}); + + /// List of parameters. + std::vector Params; + + /// When present, this function is a template or specialization. + Optional Template; + + /// The class of function this is + FunctionClass Class = FunctionClass::Normal; + + NoexceptInfo Noexcept; + ExprInfo Requires; + bool IsVariadic = false; + bool IsDefaulted = false; + bool IsExplicitlyDefaulted = false; + bool IsDeleted = false; + bool IsDeletedAsWritten = false; + bool IsNoReturn = false; + bool HasOverrideAttr = false; + bool HasTrailingReturn = false; + bool IsNodiscard = false; + bool IsExplicitObjectMemberFunction = false; + ConstexprKind Constexpr = ConstexprKind::None; + OperatorKind OverloadedOperator = OperatorKind::None; + StorageClassKind StorageClass = StorageClassKind::None; + std::vector Attributes; + + // CXXMethodDecl + bool IsRecordMethod = false; + bool IsVirtual = false; + bool IsVirtualAsWritten = false; + bool IsPure = false; + bool IsConst = false; + bool IsVolatile = false; + bool IsFinal = false; + ReferenceKind RefQualifier = ReferenceKind::None; + ExplicitInfo Explicit; + + //-------------------------------------------- + + explicit FunctionSymbol(SymbolID const& ID) noexcept + : SymbolCommonBase(ID) + { + } + + std::strong_ordering + operator<=>(FunctionSymbol const& other) const; +}; + +MRDOCS_DECL +void +merge(FunctionSymbol& I, FunctionSymbol&& Other); + +/** Map a FunctionSymbol to a dom::Object. + + @param t The tag type. + @param io The IO object to use for mapping. + @param I The FunctionSymbol to map. + @param domCorpus The DomCorpus used to create + */ +template +void +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + FunctionSymbol const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, I.asInfo(), domCorpus); + io.map("isVariadic", I.IsVariadic); + io.map("isVirtual", I.IsVirtual); + io.map("isVirtualAsWritten", I.IsVirtualAsWritten); + io.map("isPure", I.IsPure); + io.map("isDefaulted", I.IsDefaulted); + io.map("isExplicitlyDefaulted", I.IsExplicitlyDefaulted); + io.map("isDeleted", I.IsDeleted); + io.map("isDeletedAsWritten", I.IsDeletedAsWritten); + io.map("isNoReturn", I.IsNoReturn); + io.map("hasOverrideAttr", I.HasOverrideAttr); + io.map("hasTrailingReturn", I.HasTrailingReturn); + io.map("isConst", I.IsConst); + io.map("isVolatile", I.IsVolatile); + io.map("isFinal", I.IsFinal); + io.map("isNodiscard", I.IsNodiscard); + io.map("isExplicitObjectMemberFunction", I.IsExplicitObjectMemberFunction); + if (I.Constexpr != ConstexprKind::None) + { + io.map("constexprKind", I.Constexpr); + } + if (I.StorageClass != StorageClassKind::None) + { + io.map("storageClass", I.StorageClass); + } + if (I.RefQualifier != ReferenceKind::None) + { + io.map("refQualifier", I.RefQualifier); + } + io.map("functionClass", I.Class); + io.map("params", dom::LazyArray(I.Params, domCorpus)); + io.map("return", I.ReturnType); + io.map("template", I.Template); + io.map("overloadedOperator", I.OverloadedOperator); + io.map("exceptionSpec", I.Noexcept); + io.map("explicitSpec", I.Explicit); + if (!I.Requires.Written.empty()) + { + io.map("requires", I.Requires.Written); + } + io.map("attributes", dom::LazyArray(I.Attributes)); +} + +/** Map the FunctionSymbol to a @ref dom::Value object. + + @param v The output parameter to receive the dom::Value. + @param I The FunctionSymbol to convert. + @param domCorpus The DomCorpus used to resolve references. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + FunctionSymbol const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +/** Determine if one function would override the other + + @param base The base function + @param derived The derived function + */ +MRDOCS_DECL +bool +overrides(FunctionSymbol const& base, FunctionSymbol const& derived); + +} // mrdocs + +#endif // MRDOCS_API_METADATA_SYMBOL_FUNCTION_HPP diff --git a/include/mrdocs/Metadata/Symbol/FunctionClass.hpp b/include/mrdocs/Metadata/Symbol/FunctionClass.hpp new file mode 100644 index 000000000..b3f0f1743 --- /dev/null +++ b/include/mrdocs/Metadata/Symbol/FunctionClass.hpp @@ -0,0 +1,53 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_SYMBOL_FUNCTIONCLASS_HPP +#define MRDOCS_API_METADATA_SYMBOL_FUNCTIONCLASS_HPP + +#include +#include + +namespace mrdocs { + +/** Function classifications */ +enum class FunctionClass +{ + /// The function is a normal function. + Normal = 0, + /// The function is a constructor. + Constructor, + /// The function is a conversion operator. + Conversion, + /// The function is a destructor. + Destructor +}; + +MRDOCS_DECL +dom::String +toString(FunctionClass kind) noexcept; + +/** Return the FunctionClass from a @ref dom::Value string. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + FunctionClass const kind) +{ + v = toString(kind); +} + +} // mrdocs + +#endif // MRDOCS_API_METADATA_SYMBOL_FUNCTIONCLASS_HPP diff --git a/include/mrdocs/Metadata/Info/Guide.hpp b/include/mrdocs/Metadata/Symbol/Guide.hpp similarity index 60% rename from include/mrdocs/Metadata/Info/Guide.hpp rename to include/mrdocs/Metadata/Symbol/Guide.hpp index 934c8e590..402ccb469 100644 --- a/include/mrdocs/Metadata/Info/Guide.hpp +++ b/include/mrdocs/Metadata/Symbol/Guide.hpp @@ -8,34 +8,35 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_API_METADATA_GUIDE_HPP -#define MRDOCS_API_METADATA_GUIDE_HPP +#ifndef MRDOCS_API_METADATA_SYMBOL_GUIDE_HPP +#define MRDOCS_API_METADATA_SYMBOL_GUIDE_HPP #include -#include -#include +#include +#include +#include +#include +#include #include -#include #include -#include #include -namespace clang::mrdocs { +namespace mrdocs { /** Info for deduction guides. */ -struct GuideInfo final - : InfoCommonBase +struct GuideSymbol final + : SymbolCommonBase { /** The pattern for the deduced specialization. - This is always a SpecializationTypeInfo. + This is always a SpecializationType. */ - Polymorphic Deduced = std::nullopt; + Polymorphic Deduced = Polymorphic(AutoType{}); /** Template head, if any. */ - std::optional Template; + Optional Template; /** The parameters of the deduction guide. */ @@ -47,23 +48,23 @@ struct GuideInfo final //-------------------------------------------- - explicit GuideInfo(SymbolID ID) noexcept - : InfoCommonBase(ID) + explicit GuideSymbol(SymbolID ID) noexcept + : SymbolCommonBase(ID) {} std::strong_ordering - operator<=>(GuideInfo const& other) const; + operator<=>(GuideSymbol const& other) const; }; MRDOCS_DECL void -merge(GuideInfo& I, GuideInfo&& Other); +merge(GuideSymbol& I, GuideSymbol&& Other); -/** Map a GuideInfo to a dom::Object. +/** Map a GuideSymbol 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 I The GuideSymbol to map. @param domCorpus The DomCorpus used to create */ template @@ -71,29 +72,29 @@ void tag_invoke( dom::LazyObjectMapTag t, IO& io, - GuideInfo const& I, + GuideSymbol const& I, DomCorpus const* domCorpus) { - tag_invoke(t, io, dynamic_cast(I), domCorpus); + tag_invoke(t, io, I.asInfo(), domCorpus); io.map("params", dom::LazyArray(I.Params, domCorpus)); io.map("deduced", I.Deduced); io.map("template", I.Template); io.map("explicitSpec", I.Explicit); } -/** Map the GuideInfo to a @ref dom::Value object. +/** Map the GuideSymbol to a @ref dom::Value object. */ inline void tag_invoke( dom::ValueFromTag, dom::Value& v, - GuideInfo const& I, + GuideSymbol const& I, DomCorpus const* domCorpus) { v = dom::LazyObject(I, domCorpus); } -} // clang::mrdocs +} // mrdocs -#endif +#endif // MRDOCS_API_METADATA_SYMBOL_GUIDE_HPP diff --git a/include/mrdocs/Metadata/Symbol/Location.hpp b/include/mrdocs/Metadata/Symbol/Location.hpp new file mode 100644 index 000000000..d9e15c53b --- /dev/null +++ b/include/mrdocs/Metadata/Symbol/Location.hpp @@ -0,0 +1,126 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_SYMBOL_LOCATION_HPP +#define MRDOCS_API_METADATA_SYMBOL_LOCATION_HPP + +#include +#include +#include +#include + +namespace mrdocs { + +struct MRDOCS_DECL + Location +{ + /** The full file path + */ + std::string FullPath; + + /** The file path relative to one of the search directories + */ + std::string ShortPath; + + /** The file path relative to the source-root directory + */ + std::string SourcePath; + + /** Line number within the file + */ + unsigned LineNumber = 0; + + /** Column number within the line + */ + unsigned ColumnNumber = 0; + + /** Whether this location has documentation. + */ + bool Documented = false; + + //-------------------------------------------- + + constexpr + Location( + std::string_view const full_path = {}, + std::string_view const short_path = {}, + std::string_view const source_path = {}, + unsigned const line = 0, + unsigned const col = 0, + bool const documented = false) + : FullPath(full_path) + , ShortPath(short_path) + , SourcePath(source_path) + , LineNumber(line) + , ColumnNumber(col) + , Documented(documented) + { + } + + auto operator<=>(Location const&) const = default; +}; + +MRDOCS_DECL +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + Location const& loc); + +/** nullable_traits specialization for Location. + + Semantics + - The “null” (sentinel) state is any Location whose ShortPath is empty. + - Creating a null value produces a Location with all fields defaulted + and ShortPath empty. + - Making an existing value null clears ShortPath and resets the other + fields to their defaults. + + Rationale + - This mirrors the old LocationEmptyPredicate, which treated an empty + ShortPath as “empty/null.” +**/ +template<> +struct nullable_traits +{ + static constexpr bool + is_null(Location const& v) noexcept + { + return v.ShortPath.empty(); + } + + static constexpr Location + null() noexcept + { + return Location{ + /*full_path*/ {}, + /*short_path*/ {}, + /*source_path*/ {}, + /*line*/ 0u, + /*documented*/ false + }; + } + + static constexpr void + make_null(Location& v) noexcept + { + v.FullPath.clear(); + v.ShortPath.clear(); // sentinel condition + v.SourcePath.clear(); + v.LineNumber = 0; + v.Documented = false; + } +}; + +} // mrdocs + +#endif // MRDOCS_API_METADATA_SYMBOL_LOCATION_HPP diff --git a/include/mrdocs/Metadata/Info/Namespace.hpp b/include/mrdocs/Metadata/Symbol/Namespace.hpp similarity index 82% rename from include/mrdocs/Metadata/Info/Namespace.hpp rename to include/mrdocs/Metadata/Symbol/Namespace.hpp index 01f44d3ba..06a5e5683 100644 --- a/include/mrdocs/Metadata/Info/Namespace.hpp +++ b/include/mrdocs/Metadata/Symbol/Namespace.hpp @@ -9,16 +9,16 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_API_METADATA_NAMESPACE_HPP -#define MRDOCS_API_METADATA_NAMESPACE_HPP +#ifndef MRDOCS_API_METADATA_SYMBOL_NAMESPACE_HPP +#define MRDOCS_API_METADATA_SYMBOL_NAMESPACE_HPP -#include -#include #include -#include +#include +#include #include +#include -namespace clang::mrdocs { +namespace mrdocs { /** The members of a Namespace */ @@ -107,44 +107,45 @@ tag_invoke( /** Describes a namespace. */ -struct NamespaceInfo final - : InfoCommonBase +struct NamespaceSymbol final + : SymbolCommonBase { bool IsInline = false; bool IsAnonymous = false; /** Namespaces nominated by using-directives. */ - std::vector UsingDirectives; + std::vector UsingDirectives; /** The members of this namespace. */ NamespaceTranche Members; - explicit NamespaceInfo(SymbolID const &ID) noexcept - : InfoCommonBase(ID) + explicit + NamespaceSymbol(SymbolID const &ID) noexcept + : SymbolCommonBase(ID) { } std::strong_ordering - operator<=>(NamespaceInfo const&) const; + operator<=>(NamespaceSymbol const&) const; }; MRDOCS_DECL -void merge(NamespaceInfo& I, NamespaceInfo&& Other); +void merge(NamespaceSymbol& I, NamespaceSymbol&& Other); inline auto -allMembers(NamespaceInfo const& T) +allMembers(NamespaceSymbol const& T) { return allMembers(T.Members); } -/** Map a NamespaceInfo to a dom::Object. +/** Map a NamespaceSymbol 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 I The NamespaceSymbol to map. @param domCorpus The DomCorpus used to create */ template @@ -152,29 +153,29 @@ void tag_invoke( dom::LazyObjectMapTag t, IO& io, - NamespaceInfo const& I, + NamespaceSymbol const& I, DomCorpus const* domCorpus) { - tag_invoke(t, io, dynamic_cast(I), domCorpus); + tag_invoke(t, io, I.asInfo(), domCorpus); io.map("isInline", I.IsInline); io.map("isAnonymous", I.IsAnonymous); io.map("members", I.Members); io.map("usingDirectives", dom::LazyArray(I.UsingDirectives, domCorpus)); } -/** Map the NamespaceInfo to a @ref dom::Value object. +/** Map the NamespaceSymbol to a @ref dom::Value object. */ inline void tag_invoke( dom::ValueFromTag, dom::Value& v, - NamespaceInfo const& I, + NamespaceSymbol const& I, DomCorpus const* domCorpus) { v = dom::LazyObject(I, domCorpus); } -} // clang::mrdocs +} // mrdocs -#endif +#endif // MRDOCS_API_METADATA_SYMBOL_NAMESPACE_HPP diff --git a/include/mrdocs/Metadata/Info/NamespaceAlias.hpp b/include/mrdocs/Metadata/Symbol/NamespaceAlias.hpp similarity index 59% rename from include/mrdocs/Metadata/Info/NamespaceAlias.hpp rename to include/mrdocs/Metadata/Symbol/NamespaceAlias.hpp index df93ea841..8ede49e96 100644 --- a/include/mrdocs/Metadata/Info/NamespaceAlias.hpp +++ b/include/mrdocs/Metadata/Symbol/NamespaceAlias.hpp @@ -8,45 +8,45 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_API_METADATA_NAMESPACEALIAS_HPP -#define MRDOCS_API_METADATA_NAMESPACEALIAS_HPP +#ifndef MRDOCS_API_METADATA_SYMBOL_NAMESPACEALIAS_HPP +#define MRDOCS_API_METADATA_SYMBOL_NAMESPACEALIAS_HPP #include -#include -#include #include +#include +#include -namespace clang::mrdocs { +namespace mrdocs { /** Info for namespace aliases. */ -struct NamespaceAliasInfo final - : InfoCommonBase +struct NamespaceAliasSymbol final + : SymbolCommonBase { /** The aliased symbol. This is another namespace that might or might not be in the same project. */ - Polymorphic AliasedSymbol; + IdentifierName AliasedSymbol; //-------------------------------------------- - explicit NamespaceAliasInfo(SymbolID const &ID) noexcept - : InfoCommonBase(ID) + explicit NamespaceAliasSymbol(SymbolID const &ID) noexcept + : SymbolCommonBase(ID) { } }; MRDOCS_DECL void -merge(NamespaceAliasInfo& I, NamespaceAliasInfo&& Other); +merge(NamespaceAliasSymbol& I, NamespaceAliasSymbol&& Other); -/** Map a NamespaceAliasInfo to a dom::Object. +/** Map a NamespaceAliasSymbol 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 I The NamespaceAliasSymbol to map. @param domCorpus The DomCorpus used to create */ template @@ -54,26 +54,26 @@ void tag_invoke( dom::LazyObjectMapTag t, IO& io, - NamespaceAliasInfo const& I, + NamespaceAliasSymbol const& I, DomCorpus const* domCorpus) { - tag_invoke(t, io, dynamic_cast(I), domCorpus); + tag_invoke(t, io, I.asInfo(), domCorpus); io.map("aliasedSymbol", I.AliasedSymbol); } -/** Map the NamespaceAliasInfo to a @ref dom::Value object. +/** Map the NamespaceAliasSymbol to a @ref dom::Value object. */ inline void tag_invoke( dom::ValueFromTag, dom::Value& v, - NamespaceAliasInfo const& I, + NamespaceAliasSymbol const& I, DomCorpus const* domCorpus) { v = dom::LazyObject(I, domCorpus); } -} // clang::mrdocs +} // mrdocs -#endif +#endif // MRDOCS_API_METADATA_SYMBOL_NAMESPACEALIAS_HPP diff --git a/include/mrdocs/Metadata/Info/Overloads.hpp b/include/mrdocs/Metadata/Symbol/Overloads.hpp similarity index 51% rename from include/mrdocs/Metadata/Info/Overloads.hpp rename to include/mrdocs/Metadata/Symbol/Overloads.hpp index d88e3f79b..4912a6e34 100644 --- a/include/mrdocs/Metadata/Info/Overloads.hpp +++ b/include/mrdocs/Metadata/Symbol/Overloads.hpp @@ -8,19 +8,19 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_API_METADATA_OVERLOADS_HPP -#define MRDOCS_API_METADATA_OVERLOADS_HPP +#ifndef MRDOCS_API_METADATA_SYMBOL_OVERLOADS_HPP +#define MRDOCS_API_METADATA_SYMBOL_OVERLOADS_HPP #include -#include -#include +#include +#include -namespace clang::mrdocs { +namespace mrdocs { /** Represents a set of function overloads. */ -struct OverloadsInfo final - : InfoCommonBase +struct OverloadsSymbol final + : SymbolCommonBase { /// The class of the functions. FunctionClass Class = FunctionClass::Normal; @@ -31,39 +31,44 @@ struct OverloadsInfo final /// The members of the overload set. std::vector Members; - /// Info about the return type of this function. - Polymorphic ReturnType = std::nullopt; + /** Info about the return type of these function overloads. + + If all overloads have the same return type, this contains + that type. Otherwise, it contains `auto` to indicate that + the return type varies according to the parameters. + */ + Polymorphic ReturnType = Polymorphic(AutoType{}); //-------------------------------------------- - explicit OverloadsInfo(SymbolID const& ID) noexcept - : InfoCommonBase(ID) + explicit OverloadsSymbol(SymbolID const& ID) noexcept + : SymbolCommonBase(ID) { } explicit - OverloadsInfo(SymbolID const& Parent, std::string_view Name, AccessKind Access, bool isStatic) noexcept; + OverloadsSymbol(SymbolID const& Parent, std::string_view Name, AccessKind Access, bool isStatic) noexcept; }; MRDOCS_DECL -void merge(OverloadsInfo& I, OverloadsInfo&& Other); +void merge(OverloadsSymbol& I, OverloadsSymbol&& Other); inline auto& -allMembers(OverloadsInfo const& T) +allMembers(OverloadsSymbol const& T) { return T.Members; } MRDOCS_DECL void -addMember(OverloadsInfo& I, FunctionInfo const& Member); +addMember(OverloadsSymbol& I, FunctionSymbol const& Member); -/** Map a OverloadsInfo to a dom::Object. +/** Map a OverloadsSymbol 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 I The OverloadsSymbol to map. @param domCorpus The DomCorpus used to create */ template @@ -71,28 +76,28 @@ void tag_invoke( dom::LazyObjectMapTag t, IO& io, - OverloadsInfo const& I, + OverloadsSymbol const& I, DomCorpus const* domCorpus) { - tag_invoke(t, io, dynamic_cast(I), domCorpus); + tag_invoke(t, io, I.asInfo(), domCorpus); io.map("class", I.Class); io.map("overloadedOperator", I.OverloadedOperator); io.map("members", dom::LazyArray(I.Members, domCorpus)); } -/** Map the OverloadsInfo to a @ref dom::Value object. +/** Map the OverloadsSymbol to a @ref dom::Value object. */ inline void tag_invoke( dom::ValueFromTag, dom::Value& v, - OverloadsInfo const& I, + OverloadsSymbol const& I, DomCorpus const* domCorpus) { v = dom::LazyObject(I, domCorpus); } -} // clang::mrdocs +} // mrdocs -#endif +#endif // MRDOCS_API_METADATA_SYMBOL_OVERLOADS_HPP diff --git a/include/mrdocs/Metadata/Symbol/Param.hpp b/include/mrdocs/Metadata/Symbol/Param.hpp new file mode 100644 index 000000000..a37e50f78 --- /dev/null +++ b/include/mrdocs/Metadata/Symbol/Param.hpp @@ -0,0 +1,74 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_SYMBOL_PARAM_HPP +#define MRDOCS_API_METADATA_SYMBOL_PARAM_HPP + +#include +#include +#include +#include +#include + +namespace mrdocs { + +/** Represents a single function parameter */ +struct Param final +{ + /** The type of this parameter + */ + Polymorphic Type = Polymorphic(AutoType{}); + + /** The parameter name. + */ + Optional Name; + + /** The default argument for this parameter, if any + */ + Optional Default; + + // KRYSTIAN TODO: attributes (nodiscard, deprecated, and carries_dependency) + // KRYSTIAN TODO: flag to indicate whether this is a function parameter pack + + Param() = default; + + Param( + Polymorphic&& type, + std::string&& name, + std::string&& def_arg) + : Type(std::move(type)) + , Name(std::move(name)) + , Default(std::move(def_arg)) + {} + + auto + operator<=>(Param const&) const = default; +}; + +MRDOCS_DECL +void +merge(Param& I, Param&& Other); + +/** Return the Param as a @ref dom::Value object. + */ +MRDOCS_DECL +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + Param const& p, + DomCorpus const* domCorpus); + +} // mrdocs + +#endif // MRDOCS_API_METADATA_SYMBOL_PARAM_HPP diff --git a/include/mrdocs/Metadata/Symbol/Record.hpp b/include/mrdocs/Metadata/Symbol/Record.hpp new file mode 100644 index 000000000..56cae709c --- /dev/null +++ b/include/mrdocs/Metadata/Symbol/Record.hpp @@ -0,0 +1,144 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_SYMBOL_RECORD_HPP +#define MRDOCS_API_METADATA_SYMBOL_RECORD_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace mrdocs { + +/** Metadata for struct, class, or union. +*/ +struct RecordSymbol final + : SymbolCommonBase +{ + /** Kind of record this is (class, struct, or union). + */ + RecordKeyKind KeyKind = RecordKeyKind::Struct; + + /// When present, this record is a template or specialization. + Optional Template; + + // Indicates if the record was declared using a typedef. + // Things like anonymous structs in a typedef: + // typedef struct { ... } foo_t; + // are converted into records with the typedef as the Name + this flag set. + // KRYSTIAN FIXME: this does not account for alias-declarations + bool IsTypeDef = false; + + bool IsFinal = false; + bool IsFinalDestructor = false; + + /** List of immediate bases. + */ + std::vector Bases; + + /** List of derived classes + */ + std::vector Derived; + + /** Lists of members. + */ + RecordInterface Interface; + + /** List of friends. + */ + std::vector Friends; + + //-------------------------------------------- + + explicit RecordSymbol(SymbolID const& ID) noexcept + : SymbolCommonBase(ID) + { + } + + std::strong_ordering + operator<=>(RecordSymbol const& other) const; +}; + +constexpr +std::string_view +getDefaultAccessString( + RecordKeyKind const& kind) noexcept +{ + switch(kind) + { + case RecordKeyKind::Class: + return "private"; + case RecordKeyKind::Struct: + case RecordKeyKind::Union: + return "public"; + default: + MRDOCS_UNREACHABLE(); + } +} + +inline +auto +allMembers(RecordSymbol const& T) +{ + return allMembers(T.Interface); +} + +MRDOCS_DECL +void +merge(RecordSymbol& I, RecordSymbol&& Other); + +/** Map a RecordSymbol to a dom::Object. + + @param t The tag type. + @param io The IO object to use for mapping. + @param I The RecordSymbol to map. + @param domCorpus The DomCorpus used to create + */ +template +void +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + RecordSymbol const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, I.asInfo(), domCorpus); + io.map("tag", I.KeyKind); + io.map("defaultAccess", getDefaultAccessString(I.KeyKind)); + io.map("isFinal", I.IsFinal); + io.map("isTypedef", I.IsTypeDef); + io.map("bases", dom::LazyArray(I.Bases, domCorpus)); + io.map("derived", dom::LazyArray(I.Derived, domCorpus)); + io.map("interface", I.Interface); + io.map("template", I.Template); + io.map("friends", dom::LazyArray(I.Friends, domCorpus)); +} + +/** Map the RecordSymbol to a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + RecordSymbol const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +} // mrdocs + +#endif // MRDOCS_API_METADATA_SYMBOL_RECORD_HPP diff --git a/include/mrdocs/Metadata/Symbol/RecordBase.hpp b/include/mrdocs/Metadata/Symbol/RecordBase.hpp new file mode 100644 index 000000000..8045a2753 --- /dev/null +++ b/include/mrdocs/Metadata/Symbol/RecordBase.hpp @@ -0,0 +1,64 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_SYMBOL_RECORDBASE_HPP +#define MRDOCS_API_METADATA_SYMBOL_RECORDBASE_HPP + +#include +#include +#include + +namespace mrdocs { + +/** Metadata for a direct base. +*/ +struct BaseInfo +{ + /** The base type. + + This is typically a `NamedType` that refers to a + `RecordSymbol`, but it could also be a more complex type + such as a `decltype`. + */ + Polymorphic Type; + + /** The access specifier for the base. + */ + AccessKind Access = AccessKind::Public; + + /** Whether the base is virtual. + */ + bool IsVirtual = false; + + BaseInfo() = delete; + + BaseInfo( + Polymorphic&& type, + AccessKind const access, + bool const is_virtual) + : Type(std::move(type)) + , Access(access) + , IsVirtual(is_virtual) + { + } +}; + +MRDOCS_DECL +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + BaseInfo const& I, + DomCorpus const* domCorpus); + +} // mrdocs + +#endif // MRDOCS_API_METADATA_SYMBOL_RECORDBASE_HPP diff --git a/include/mrdocs/Metadata/Symbol/RecordInterface.hpp b/include/mrdocs/Metadata/Symbol/RecordInterface.hpp new file mode 100644 index 000000000..022b92ca4 --- /dev/null +++ b/include/mrdocs/Metadata/Symbol/RecordInterface.hpp @@ -0,0 +1,121 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_SYMBOL_RECORDINTERFACE_HPP +#define MRDOCS_API_METADATA_SYMBOL_RECORDINTERFACE_HPP + +#include +#include + +namespace mrdocs { + +/** The aggregated interface for a given struct, class, or union. + + This class represents the public, protected, and private + interfaces of a record. It is used to generate the + "interface" value of the DOM for symbols that represent + records or namespaces. + + The interface is not part of the Corpus. It is a temporary + structure generated to aggregate the symbols of a record. + This structure is provided to the user via the DOM. + + While the members of a Namespace are directly represented + with a Tranche, the members of a Record are represented + with an Interface. + +*/ +class RecordInterface +{ +public: + /** The aggregated public interfaces. + + This tranche contains all public members of a record + or namespace. + + */ + RecordTranche Public; + + /** The aggregated protected interfaces. + + This tranche contains all protected members of a record + or namespace. + + */ + RecordTranche Protected; + + /** The aggregated private interfaces. + + This tranche contains all private members of a record + or namespace. + + */ + RecordTranche Private; +}; + +MRDOCS_DECL +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 +tag_invoke( + dom::LazyObjectMapTag, + IO& io, + RecordInterface const& I, + DomCorpus const*) +{ + io.map("public", I.Public); + io.map("protected", I.Protected); + io.map("private", I.Private); +} + +/** Map the RecordInterface to a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + RecordInterface const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + + +inline +auto +allMembers(RecordInterface const& T) +{ + // This is a trick to emulate views::concat in C++20 + return + std::views::iota(0, 3) | + std::views::transform( + [&T](int i) -> auto { + switch (i) { + case 0: return allMembers(T.Public); + case 1: return allMembers(T.Protected); + case 2: return allMembers(T.Private); + default: throw std::out_of_range("Invalid index"); + } + }) | + std::ranges::views::join; +} + +} // mrdocs + +#endif // MRDOCS_API_METADATA_SYMBOL_RECORDINTERFACE_HPP diff --git a/include/mrdocs/Metadata/Symbol/RecordKeyKind.hpp b/include/mrdocs/Metadata/Symbol/RecordKeyKind.hpp new file mode 100644 index 000000000..14a673ec1 --- /dev/null +++ b/include/mrdocs/Metadata/Symbol/RecordKeyKind.hpp @@ -0,0 +1,49 @@ +// +// 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) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_SYMBOL_RECORDKEYKIND_HPP +#define MRDOCS_API_METADATA_SYMBOL_RECORDKEYKIND_HPP + +#include +#include +#include + +namespace mrdocs { + +/** The kind of record: struct, class, or union. +*/ +enum class RecordKeyKind +{ + /// A struct. + Struct, + /// A C++ class. + Class, + /// A C-style Union + Union +}; + +MRDOCS_DECL +dom::String +toString(RecordKeyKind kind) noexcept; + +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + RecordKeyKind kind) +{ + v = toString(kind); +} + +} // mrdocs + +#endif // MRDOCS_API_METADATA_SYMBOL_RECORDKEYKIND_HPP diff --git a/include/mrdocs/Metadata/Symbol/RecordTranche.hpp b/include/mrdocs/Metadata/Symbol/RecordTranche.hpp new file mode 100644 index 000000000..8a3b88bfc --- /dev/null +++ b/include/mrdocs/Metadata/Symbol/RecordTranche.hpp @@ -0,0 +1,121 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_SYMBOL_RECORDTRANCHE_HPP +#define MRDOCS_API_METADATA_SYMBOL_RECORDTRANCHE_HPP + +#include +#include +#include +#include +#include + +namespace mrdocs { + +/** A group of members that have the same access specifier. + + This struct represents a collection of symbols that share + the same access specifier within a record. + + It includes one vector for each info type allowed in a + record, and individual vectors for static functions, types, + and function overloads. +*/ +struct RecordTranche +{ + std::vector NamespaceAliases; + std::vector Typedefs; + std::vector Records; + std::vector Enums; + std::vector Functions; + std::vector StaticFunctions; + std::vector Variables; + std::vector StaticVariables; + std::vector Concepts; + std::vector Guides; + std::vector Usings; +}; + +MRDOCS_DECL +void +merge(RecordTranche& I, RecordTranche&& Other); + +inline +auto +allMembers(RecordTranche const& T) +{ + // This is a trick to emulate views::concat in C++20 + return std::views::transform( + std::views::iota(0, 11), + [&T](int const i) -> auto const& + { + switch (i) { + case 0: return T.NamespaceAliases; + case 1: return T.Typedefs; + case 2: return T.Records; + case 3: return T.Enums; + case 4: return T.Functions; + case 5: return T.StaticFunctions; + case 6: return T.Variables; + case 7: return T.StaticVariables; + case 8: return T.Concepts; + case 9: return T.Guides; + case 10: return T.Usings; + default: throw std::out_of_range("Invalid index"); + } + } + ) | std::ranges::views::join; +} + +/** 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 +tag_invoke( + dom::LazyObjectMapTag, + IO& io, + RecordTranche const& I, + DomCorpus const* domCorpus) +{ + io.map("namespaceAliases", dom::LazyArray(I.NamespaceAliases, domCorpus)); + io.map("typedefs", dom::LazyArray(I.Typedefs, domCorpus)); + io.map("records", dom::LazyArray(I.Records, domCorpus)); + io.map("enums", dom::LazyArray(I.Enums, domCorpus)); + io.map("functions", dom::LazyArray(I.Functions, domCorpus)); + io.map("staticFunctions", dom::LazyArray(I.StaticFunctions, domCorpus)); + io.map("variables", dom::LazyArray(I.Variables, domCorpus)); + io.map("staticVariables", dom::LazyArray(I.StaticVariables, domCorpus)); + io.map("concepts", dom::LazyArray(I.Concepts, domCorpus)); + io.map("guides", dom::LazyArray(I.Guides, domCorpus)); + io.map("usings", dom::LazyArray(I.Usings, domCorpus)); +} + +/** Map the RecordTranche to a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + RecordTranche const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + + +} // mrdocs + +#endif // MRDOCS_API_METADATA_SYMBOL_RECORDTRANCHE_HPP diff --git a/include/mrdocs/Metadata/Symbol/Source.hpp b/include/mrdocs/Metadata/Symbol/Source.hpp new file mode 100644 index 000000000..910a991f2 --- /dev/null +++ b/include/mrdocs/Metadata/Symbol/Source.hpp @@ -0,0 +1,71 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_SYMBOL_SOURCE_HPP +#define MRDOCS_API_METADATA_SYMBOL_SOURCE_HPP + +#include +#include +#include +#include +#include + +namespace mrdocs { + +/** Stores source information for a declaration. +*/ +struct MRDOCS_DECL + SourceInfo +{ + constexpr SourceInfo() = default; + + /** Location where the entity was defined + + KRYSTIAN NOTE: this is used for entities which cannot be + redeclared -- regardless of whether such a declaration is + actually a definition (e.g. alias-declarations and + typedef declarations are never definition). + */ + Optional DefLoc; + + /** Locations where the entity was declared. + + This does not include the definition. + */ + std::vector Loc; + + constexpr virtual ~SourceInfo() = default; + + auto operator<=>(SourceInfo const&) const = default; +}; + +MRDOCS_DECL +void +merge(SourceInfo& I, SourceInfo const& Other); + +MRDOCS_DECL +void +merge(SourceInfo& I, SourceInfo&& Other); + +MRDOCS_DECL +Optional +getPrimaryLocation(SourceInfo const& I, bool preferDefinition); + +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + SourceInfo const& I); + +} // mrdocs + +#endif // MRDOCS_API_METADATA_SYMBOL_SOURCE_HPP diff --git a/include/mrdocs/Metadata/Symbol/SymbolBase.hpp b/include/mrdocs/Metadata/Symbol/SymbolBase.hpp new file mode 100644 index 000000000..9769f645f --- /dev/null +++ b/include/mrdocs/Metadata/Symbol/SymbolBase.hpp @@ -0,0 +1,278 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_SYMBOL_SYMBOLBASE_HPP +#define MRDOCS_API_METADATA_SYMBOL_SYMBOLBASE_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace mrdocs { + +/* Forward declarations + */ +#define INFO(Type) struct Type##Symbol; +#include + +/** Base class with common properties of all symbols +*/ +struct MRDOCS_VISIBLE Symbol { + /** The unqualified name. + */ + std::string Name; + + /** The source location information. + */ + SourceInfo Loc; + + /** Kind of declaration. + */ + SymbolKind Kind = SymbolKind::None; + + /** The unique identifier for this symbol. + */ + SymbolID id; + + /** Declaration access. + + Class members use: + @li `AccessKind::Public`, + @li `AccessKind::Protected`, and + @li `AccessKind::Private`. + + Namespace members use `AccessKind::None`. + */ + AccessKind Access = AccessKind::None; + + /** Determine why a symbol is extracted. + + This flag distinguishes `Info` from its dependencies and + indicates why it was extracted. + + Non-dependencies can be extracted in normal mode, + see-below mode, or implementation-defined mode. + + A dependency is a symbol that does not meet the configured + conditions for extraction, but had to be extracted due to it + being used transitively by a primary `Info`. + */ + ExtractionMode Extraction = ExtractionMode::Dependency; + + /** The parent symbol, if any. + + This is the parent namespace or record + where the symbol is defined. + */ + SymbolID Parent; + + /** The extracted documentation for this declaration. + */ + Optional doc; + + //-------------------------------------------- + + virtual ~Symbol() = default; + + #define INFO(Type) constexpr bool is##Type() const noexcept { \ + return Kind == SymbolKind::Type; \ + } +#include + + constexpr Symbol const& asInfo() const noexcept + { + return *this; + } + + constexpr Symbol& asInfo() noexcept + { + return *this; + } + + #define INFO(Type) \ + constexpr Type##Symbol const& as##Type() const noexcept { \ + if (Kind == SymbolKind::Type) \ + return reinterpret_cast(*this); \ + MRDOCS_UNREACHABLE(); \ + } +#include + +#define INFO(Type) \ + constexpr Type##Symbol & as##Type() noexcept { \ + if (Kind == SymbolKind::Type) \ + return reinterpret_cast(*this); \ + MRDOCS_UNREACHABLE(); \ + } +#include + +#define INFO(Type) \ + constexpr Type##Symbol const* as##Type##Ptr() const noexcept { \ + if (Kind == SymbolKind::Type) { return reinterpret_cast(this); } \ + return nullptr; \ + } +#include + +#define INFO(Type) \ + constexpr Type##Symbol * as##Type##Ptr() noexcept { \ + if (Kind == SymbolKind::Type) { return reinterpret_cast(this); } \ + return nullptr; \ + } +#include + + auto operator<=>(Symbol const&) const = default; + +protected: + constexpr Symbol() = default; + + Symbol(Symbol const& Other) = default; + + /** Move constructor. + */ + Symbol(Symbol&& Other) = default; + + /** Construct an Symbol. + + @param kind The kind of symbol + @param ID The unique identifier for this symbol + */ + explicit + Symbol( + SymbolKind const kind, + SymbolID const& ID) noexcept + : Kind(kind) + , id(ID) + { + } +}; + +//------------------------------------------------ + +/** Base class for providing variant discriminator functions. + + This offers functions that return a boolean at + compile-time, indicating if the most-derived + class is a certain type. +*/ +template +struct SymbolCommonBase : Symbol +{ + /** The variant discriminator constant of the most-derived class. + + It only distinguishes from `Symbol::kind` in that it is a constant. + + */ + static constexpr SymbolKind kind_id = K; + + #define INFO(Kind) \ + static constexpr bool is##Kind() noexcept { return K == SymbolKind::Kind; } +#include + + auto operator<=>(SymbolCommonBase const&) const = default; + +protected: + SymbolCommonBase() = default; + + constexpr explicit SymbolCommonBase(SymbolID const& ID) + : Symbol(K, ID) + { + } +}; + +/** Merges two Symbol objects. + + This function is used to merge two Symbol objects with the same SymbolID. + The function assumes that the two Symbol objects are of the same type. + If they are not, the function will fail. + + @param I The Symbol object to merge into. + @param Other The Symbol object to merge from. +*/ +MRDOCS_DECL +void +merge(Symbol& I, Symbol&& Other); + +inline +bool +canMerge(Symbol const& I, Symbol const& Other) +{ + return + I.Kind == Other.Kind && + I.id == Other.id; +} + +/** Map the Symbol to a @ref dom::Object. + + @param io The output parameter to receive the dom::Object. + @param I The Symbol to convert. + @param domCorpus The DomCorpus used to resolve references. + */ +template +void +tag_invoke( + dom::LazyObjectMapTag, + IO& io, + Symbol const& I, + DomCorpus const* domCorpus) +{ + MRDOCS_ASSERT(domCorpus); + io.map("class", std::string("symbol")); + io.map("kind", I.Kind); + io.map("id", I.id); + if (!I.Name.empty()) + { + io.map("name", I.Name); + } + io.map("access", I.Access); + io.map("extraction", I.Extraction); + io.map("isRegular", I.Extraction == ExtractionMode::Regular); + io.map("isSeeBelow", I.Extraction == ExtractionMode::SeeBelow); + io.map("isImplementationDefined", I.Extraction == ExtractionMode::ImplementationDefined); + io.map("isDependency", I.Extraction == ExtractionMode::Dependency); + if (I.Parent) + { + io.map("parent", I.Parent); + } + if (I.doc) + { + io.map("doc", *I.doc); + } + io.map("loc", I.Loc); +} + +/** Return the Symbol as a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + Symbol const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +inline +Optional +getPrimaryLocation(Symbol const& I) +{ + return getPrimaryLocation( + I.Loc, + I.isRecord() || I.isEnum()); +} + +} // mrdocs + +#endif // MRDOCS_API_METADATA_SYMBOL_SYMBOLBASE_HPP diff --git a/include/mrdocs/Metadata/SymbolID.hpp b/include/mrdocs/Metadata/Symbol/SymbolID.hpp similarity index 95% rename from include/mrdocs/Metadata/SymbolID.hpp rename to include/mrdocs/Metadata/Symbol/SymbolID.hpp index 9f28a188c..dde44b50d 100644 --- a/include/mrdocs/Metadata/SymbolID.hpp +++ b/include/mrdocs/Metadata/Symbol/SymbolID.hpp @@ -10,19 +10,19 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_API_METADATA_SYMBOLID_HPP -#define MRDOCS_API_METADATA_SYMBOLID_HPP +#ifndef MRDOCS_API_METADATA_SYMBOL_SYMBOLID_HPP +#define MRDOCS_API_METADATA_SYMBOL_SYMBOLID_HPP #include #include +#include #include #include -#include -#include -#include #include +#include +#include -namespace clang::mrdocs { +namespace mrdocs { class DomCorpus; namespace dom { struct ValueFromTag; @@ -214,17 +214,17 @@ tag_invoke( std::unique_ptr const& t, DomCorpus const* domCorpus); -} // clang::mrdocs +} // mrdocs template<> -struct std::hash +struct std::hash { std::size_t - operator()(clang::mrdocs::SymbolID const& id) const noexcept + operator()(mrdocs::SymbolID const& id) const noexcept { return std::hash()( std::string_view(id)); } }; -#endif +#endif // MRDOCS_API_METADATA_SYMBOL_SYMBOLID_HPP diff --git a/include/mrdocs/Metadata/Symbol/SymbolKind.hpp b/include/mrdocs/Metadata/Symbol/SymbolKind.hpp new file mode 100644 index 000000000..5e3f254bd --- /dev/null +++ b/include/mrdocs/Metadata/Symbol/SymbolKind.hpp @@ -0,0 +1,58 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_SYMBOL_SYMBOLKIND_HPP +#define MRDOCS_API_METADATA_SYMBOL_SYMBOLKIND_HPP + +#include +#include + +namespace mrdocs { + +/** Info variant discriminator +*/ +enum class SymbolKind { + /// Kind is not specified. + None = 0, + #define INFO(Type) Type, +#include +}; + +/** Return the name of the SymbolKind as a string. + */ +MRDOCS_DECL +dom::String +toString(SymbolKind kind) noexcept; + +/** Return the SymbolKind from a @ref dom::Value string. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, SymbolKind const kind) +{ + v = toString(kind); +} + +consteval +std::underlying_type_t +countSymbolKind() +{ + std::underlying_type_t count = 0; +#define INFO(Type) count++; +#include + return count; +} + +} // mrdocs + +#endif // MRDOCS_API_METADATA_SYMBOL_SYMBOLKIND_HPP diff --git a/include/mrdocs/Metadata/Info/InfoNodes.inc b/include/mrdocs/Metadata/Symbol/SymbolNodes.inc similarity index 100% rename from include/mrdocs/Metadata/Info/InfoNodes.inc rename to include/mrdocs/Metadata/Symbol/SymbolNodes.inc diff --git a/include/mrdocs/Metadata/Info/Typedef.hpp b/include/mrdocs/Metadata/Symbol/Typedef.hpp similarity index 65% rename from include/mrdocs/Metadata/Info/Typedef.hpp rename to include/mrdocs/Metadata/Symbol/Typedef.hpp index 96431d1cc..94af07d2c 100644 --- a/include/mrdocs/Metadata/Info/Typedef.hpp +++ b/include/mrdocs/Metadata/Symbol/Typedef.hpp @@ -10,22 +10,23 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_API_METADATA_TYPEDEF_HPP -#define MRDOCS_API_METADATA_TYPEDEF_HPP +#ifndef MRDOCS_API_METADATA_SYMBOL_TYPEDEF_HPP +#define MRDOCS_API_METADATA_SYMBOL_TYPEDEF_HPP -#include +#include +#include +#include +#include #include #include -#include -#include -namespace clang::mrdocs { +namespace mrdocs { // Info for typedef and using statements. -struct TypedefInfo final - : InfoCommonBase +struct TypedefSymbol final + : SymbolCommonBase { - Polymorphic Type = std::nullopt; + Polymorphic Type = Polymorphic(NamedType{}); /** Indicates if this is a new C++ "using"-style typedef @@ -41,29 +42,29 @@ struct TypedefInfo final */ bool IsUsing = false; - std::optional Template; + Optional Template; //-------------------------------------------- - explicit TypedefInfo(SymbolID ID) noexcept - : InfoCommonBase(ID) + explicit TypedefSymbol(SymbolID ID) noexcept + : SymbolCommonBase(ID) { } std::strong_ordering - operator<=>(TypedefInfo const& other) const; + operator<=>(TypedefSymbol const& other) const; }; MRDOCS_DECL void -merge(TypedefInfo& I, TypedefInfo&& Other); +merge(TypedefSymbol& I, TypedefSymbol&& Other); -/** Map a TypedefInfo to a dom::Object. +/** Map a TypedefSymbol 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 I The TypedefSymbol to map. @param domCorpus The DomCorpus used to create */ template @@ -71,19 +72,19 @@ void tag_invoke( dom::LazyObjectMapTag t, IO& io, - TypedefInfo const& I, + TypedefSymbol const& I, DomCorpus const* domCorpus) { - tag_invoke(t, io, dynamic_cast(I), domCorpus); + tag_invoke(t, io, I.asInfo(), domCorpus); io.map("type", I.Type); io.map("template", I.Template); io.map("isUsing", I.IsUsing); } -/** Map the TypedefInfo to a @ref dom::Value object. +/** Map the TypedefSymbol to a @ref dom::Value object. @param v The output parameter to receive the dom::Value. - @param I The TypedefInfo to convert. + @param I The TypedefSymbol to convert. @param domCorpus The DomCorpus used to resolve references. */ inline @@ -91,12 +92,12 @@ void tag_invoke( dom::ValueFromTag, dom::Value& v, - TypedefInfo const& I, + TypedefSymbol const& I, DomCorpus const* domCorpus) { v = dom::LazyObject(I, domCorpus); } -} // clang::mrdocs +} // mrdocs -#endif +#endif // MRDOCS_API_METADATA_SYMBOL_TYPEDEF_HPP diff --git a/include/mrdocs/Metadata/Info/Using.hpp b/include/mrdocs/Metadata/Symbol/Using.hpp similarity index 81% rename from include/mrdocs/Metadata/Info/Using.hpp rename to include/mrdocs/Metadata/Symbol/Using.hpp index 5308b000d..59c581bf5 100644 --- a/include/mrdocs/Metadata/Info/Using.hpp +++ b/include/mrdocs/Metadata/Symbol/Using.hpp @@ -8,18 +8,18 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_API_METADATA_USING_HPP -#define MRDOCS_API_METADATA_USING_HPP +#ifndef MRDOCS_API_METADATA_SYMBOL_USING_HPP +#define MRDOCS_API_METADATA_SYMBOL_USING_HPP #include -#include -#include -#include -#include #include +#include +#include +#include +#include #include -namespace clang::mrdocs { +namespace mrdocs { /** The class of using declaration. @@ -74,7 +74,7 @@ tag_invoke( using A::f; // where f is a function in namespace A @endcode - would be represented by a `UsingInfo` object. + would be represented by a `UsingSymbol` object. Using-declarations can be used to introduce namespace members into other namespaces and block scopes, or to @@ -83,8 +83,8 @@ tag_invoke( and class scopes. */ -struct UsingInfo final - : InfoCommonBase +struct UsingSymbol final + : SymbolCommonBase { /** The using declaration. */ @@ -98,7 +98,7 @@ struct UsingInfo final Note that this can be a qualified name, such as `A::f` in the example above. */ - Polymorphic IntroducedName; + Polymorphic IntroducedName = Polymorphic(std::in_place_type); /** The shadow declarations. @@ -131,20 +131,20 @@ struct UsingInfo final //-------------------------------------------- - explicit UsingInfo(SymbolID ID) noexcept - : InfoCommonBase(ID) + explicit UsingSymbol(SymbolID ID) noexcept + : SymbolCommonBase(ID) { } }; MRDOCS_DECL -void merge(UsingInfo& I, UsingInfo&& Other); +void merge(UsingSymbol& I, UsingSymbol&& Other); -/** Map a UsingInfo to a dom::Object. +/** Map a UsingSymbol 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 I The UsingSymbol to map. @param domCorpus The DomCorpus used to create */ template @@ -152,28 +152,28 @@ void tag_invoke( dom::LazyObjectMapTag t, IO& io, - UsingInfo const& I, + UsingSymbol const& I, DomCorpus const* domCorpus) { - tag_invoke(t, io, dynamic_cast(I), domCorpus); + tag_invoke(t, io, I.asInfo(), domCorpus); io.map("usingClass", I.Class); io.map("shadows", dom::LazyArray(I.ShadowDeclarations, domCorpus)); io.map("qualifier", I.IntroducedName); } -/** Map the UsingInfo to a @ref dom::Value object. +/** Map the UsingSymbol to a @ref dom::Value object. */ inline void tag_invoke( dom::ValueFromTag, dom::Value& v, - UsingInfo const& I, + UsingSymbol const& I, DomCorpus const* domCorpus) { v = dom::LazyObject(I, domCorpus); } -} // clang::mrdocs +} // mrdocs -#endif +#endif // MRDOCS_API_METADATA_SYMBOL_USING_HPP diff --git a/include/mrdocs/Metadata/Info/Variable.hpp b/include/mrdocs/Metadata/Symbol/Variable.hpp similarity index 75% rename from include/mrdocs/Metadata/Info/Variable.hpp rename to include/mrdocs/Metadata/Symbol/Variable.hpp index 973e0a5a5..0772b80b6 100644 --- a/include/mrdocs/Metadata/Info/Variable.hpp +++ b/include/mrdocs/Metadata/Symbol/Variable.hpp @@ -9,31 +9,34 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_API_METADATA_VARIABLE_HPP -#define MRDOCS_API_METADATA_VARIABLE_HPP +#ifndef MRDOCS_API_METADATA_SYMBOL_VARIABLE_HPP +#define MRDOCS_API_METADATA_SYMBOL_VARIABLE_HPP +#include +#include +#include #include -#include +#include +#include #include -#include #include -#include -#include -namespace clang::mrdocs { +namespace mrdocs { /** A variable. This includes variables at namespace or record scope. */ -struct VariableInfo final - : InfoCommonBase +struct VariableSymbol final + : SymbolCommonBase { /** The type of the variable */ - Polymorphic Type = std::nullopt; + Polymorphic Type = Polymorphic(NamedType{}); - std::optional Template; + /** The template information, if any. + */ + Optional Template; /** The default member initializer, if any. */ @@ -75,24 +78,24 @@ struct VariableInfo final //-------------------------------------------- - explicit VariableInfo(SymbolID const &ID) noexcept - : InfoCommonBase(ID) + explicit VariableSymbol(SymbolID const &ID) noexcept + : SymbolCommonBase(ID) { } std::strong_ordering - operator<=>(VariableInfo const& other) const; + operator<=>(VariableSymbol const& other) const; }; MRDOCS_DECL void -merge(VariableInfo& I, VariableInfo&& Other); +merge(VariableSymbol& I, VariableSymbol&& Other); -/** Map a VariableInfo to a dom::Object. +/** Map a VariableSymbol 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 I The VariableSymbol to map. @param domCorpus The DomCorpus used to create */ template @@ -100,10 +103,10 @@ void tag_invoke( dom::LazyObjectMapTag t, IO& io, - VariableInfo const& I, + VariableSymbol const& I, DomCorpus const* domCorpus) { - tag_invoke(t, io, dynamic_cast(I), domCorpus); + tag_invoke(t, io, I.asInfo(), domCorpus); io.map("type", I.Type); io.map("template", I.Template); if (I.StorageClass != StorageClassKind::None) @@ -133,19 +136,19 @@ tag_invoke( io.map("attributes", dom::LazyArray(I.Attributes)); } -/** Map the VariableInfo to a @ref dom::Value object. +/** Map the VariableSymbol to a @ref dom::Value object. */ inline void tag_invoke( dom::ValueFromTag, dom::Value& v, - VariableInfo const& I, + VariableSymbol const& I, DomCorpus const* domCorpus) { v = dom::LazyObject(I, domCorpus); } -} // clang::mrdocs +} // mrdocs -#endif +#endif // MRDOCS_API_METADATA_SYMBOL_VARIABLE_HPP diff --git a/include/mrdocs/Metadata/TArg.hpp b/include/mrdocs/Metadata/TArg.hpp new file mode 100644 index 000000000..97813035c --- /dev/null +++ b/include/mrdocs/Metadata/TArg.hpp @@ -0,0 +1,73 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_TARG_HPP +#define MRDOCS_API_METADATA_TARG_HPP + +#include +#include +#include +#include +#include + +namespace mrdocs { + +template< + std::derived_from TArgTy, + class F, + class... Args> +constexpr +decltype(auto) +visit( + TArgTy& A, + F&& f, + Args&&... args) +{ + switch(A.Kind) + { + case TArgKind::Type: + return f(static_cast&>(A), + std::forward(args)...); + case TArgKind::Constant: + return f(static_cast&>(A), + std::forward(args)...); + case TArgKind::Template: + return f(static_cast&>(A), + std::forward(args)...); + default: + MRDOCS_UNREACHABLE(); + } +} + +MRDOCS_DECL +std::strong_ordering +operator<=>(Polymorphic const& lhs, Polymorphic const& rhs); + +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + Polymorphic const& I, + DomCorpus const* domCorpus) +{ + MRDOCS_ASSERT(!I.valueless_after_move()); + tag_invoke(dom::ValueFromTag{}, v, *I, domCorpus); +} + +} // mrdocs + +#endif diff --git a/include/mrdocs/Metadata/TArg/ConstantTArg.hpp b/include/mrdocs/Metadata/TArg/ConstantTArg.hpp new file mode 100644 index 000000000..8bcf0d8a1 --- /dev/null +++ b/include/mrdocs/Metadata/TArg/ConstantTArg.hpp @@ -0,0 +1,34 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_TARG_CONSTANTTARG_HPP +#define MRDOCS_API_METADATA_TARG_CONSTANTTARG_HPP + +#include +#include +#include + +namespace mrdocs { + +struct ConstantTArg final + : TArgCommonBase +{ + /** Template argument expression. */ + ExprInfo Value; + + auto operator<=>(ConstantTArg const&) const = default; +}; + +} // mrdocs + +#endif // MRDOCS_API_METADATA_TARG_CONSTANTTARG_HPP diff --git a/include/mrdocs/Metadata/TArg/TArgBase.hpp b/include/mrdocs/Metadata/TArg/TArgBase.hpp new file mode 100644 index 000000000..8f957351f --- /dev/null +++ b/include/mrdocs/Metadata/TArg/TArgBase.hpp @@ -0,0 +1,126 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_TARG_TARGBASE_HPP +#define MRDOCS_API_METADATA_TARG_TARGBASE_HPP + +#include +#include + +namespace mrdocs { + +/* Forward declarations + */ +#define INFO(Type) struct Type##TArg; +#include + +struct TArg +{ + /** The kind of template argument this is. */ + TArgKind Kind = TArgKind::Type; + + /** Whether this template argument is a parameter expansion. */ + bool IsPackExpansion = false; + + constexpr virtual ~TArg() = default; + + auto operator<=>(TArg const&) const = default; + + constexpr TArg const& asTArg() const noexcept + { + return *this; + } + + constexpr TArg& asTArg() noexcept + { + return *this; + } + + #define INFO(Type) constexpr bool is##Type() const noexcept { \ + return Kind == TArgKind::Type; \ + } +#include + +#define INFO(Type) \ + constexpr Type##TArg const& as##Type() const noexcept { \ + if (Kind == TArgKind::Type) \ + return reinterpret_cast(*this); \ + MRDOCS_UNREACHABLE(); \ + } +#include + +#define INFO(Type) \ + constexpr Type##TArg & as##Type() noexcept { \ + if (Kind == TArgKind::Type) \ + return reinterpret_cast(*this); \ + MRDOCS_UNREACHABLE(); \ + } +#include + +#define INFO(Type) \ + constexpr Type##TArg const* as##Type##Ptr() const noexcept { \ + if (Kind == TArgKind::Type) { return reinterpret_cast(this); } \ + return nullptr; \ + } +#include + +#define INFO(Type) \ + constexpr Type##TArg * as##Type##Ptr() noexcept { \ + if (Kind == TArgKind::Type) { return reinterpret_cast(this); } \ + return nullptr; \ + } +#include + +protected: + constexpr TArg() noexcept = default; + + constexpr + TArg( + TArgKind kind) noexcept + : Kind(kind) + { + } +}; + +template +struct TArgCommonBase : TArg +{ + static constexpr TArgKind kind_id = K; + + static constexpr bool isType() noexcept { return K == TArgKind::Type; } + static constexpr bool isConstant() noexcept { return K == TArgKind::Constant; } + static constexpr bool isTemplate() noexcept { return K == TArgKind::Template; } + +protected: + constexpr + TArgCommonBase() noexcept + : TArg(K) + { + } +}; + +MRDOCS_DECL +std::string +toString(TArg const& arg) noexcept; + +MRDOCS_DECL +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + TArg const& I, + DomCorpus const* domCorpus); + +} // mrdocs + +#endif // MRDOCS_API_METADATA_TARG_TARGBASE_HPP diff --git a/include/mrdocs/Metadata/TArg/TArgInfoNodes.inc b/include/mrdocs/Metadata/TArg/TArgInfoNodes.inc new file mode 100644 index 000000000..0cd860d31 --- /dev/null +++ b/include/mrdocs/Metadata/TArg/TArgInfoNodes.inc @@ -0,0 +1,20 @@ +// +// 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 INFO +#define INFO(PascalName) +#endif + +INFO(Type) +INFO(Constant) +INFO(Template) + +#undef INFO + diff --git a/include/mrdocs/Metadata/TArg/TArgKind.hpp b/include/mrdocs/Metadata/TArg/TArgKind.hpp new file mode 100644 index 000000000..a7f45272e --- /dev/null +++ b/include/mrdocs/Metadata/TArg/TArgKind.hpp @@ -0,0 +1,47 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_TARG_TARGKIND_HPP +#define MRDOCS_API_METADATA_TARG_TARGKIND_HPP + +#include +#include +#include + +namespace mrdocs { + +/** The kind of template argument. +*/ +enum class TArgKind : int +{ + #define INFO(Type) Type, +#include +}; + +MRDOCS_DECL +std::string_view +toString(TArgKind kind) noexcept; + +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + TArgKind kind) +{ + v = toString(kind); +} + +} // mrdocs + +#endif // MRDOCS_API_METADATA_TARG_TARGKIND_HPP diff --git a/include/mrdocs/Metadata/TArg/TemplateTArg.hpp b/include/mrdocs/Metadata/TArg/TemplateTArg.hpp new file mode 100644 index 000000000..64bd3db20 --- /dev/null +++ b/include/mrdocs/Metadata/TArg/TemplateTArg.hpp @@ -0,0 +1,38 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_TARG_TEMPLATETARG_HPP +#define MRDOCS_API_METADATA_TARG_TEMPLATETARG_HPP + +#include +#include +#include +#include + +namespace mrdocs { + +struct TemplateTArg final + : TArgCommonBase +{ + /** SymbolID of the referenced template. */ + SymbolID Template; + + /** Name of the referenced template. */ + std::string Name; + + auto operator<=>(TemplateTArg const&) const = default; +}; + +} // mrdocs + +#endif // MRDOCS_API_METADATA_TARG_TEMPLATETARG_HPP diff --git a/include/mrdocs/Metadata/TArg/TypeTArg.hpp b/include/mrdocs/Metadata/TArg/TypeTArg.hpp new file mode 100644 index 000000000..432641439 --- /dev/null +++ b/include/mrdocs/Metadata/TArg/TypeTArg.hpp @@ -0,0 +1,35 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_TARG_TYPETARG_HPP +#define MRDOCS_API_METADATA_TARG_TYPETARG_HPP + +#include +#include +#include + +namespace mrdocs { + +struct TypeTArg final + : TArgCommonBase +{ + /** Template argument type. + */ + Polymorphic Type = Polymorphic(AutoType{}); + + auto operator<=>(TypeTArg const&) const = default; +}; + +} // mrdocs + +#endif // MRDOCS_API_METADATA_TARG_TYPETARG_HPP diff --git a/include/mrdocs/Metadata/TParam.hpp b/include/mrdocs/Metadata/TParam.hpp new file mode 100644 index 000000000..ee1fe6613 --- /dev/null +++ b/include/mrdocs/Metadata/TParam.hpp @@ -0,0 +1,81 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_TPARAM_HPP +#define MRDOCS_API_METADATA_TPARAM_HPP + +#include +#include +#include +#include +#include + +namespace mrdocs { + +template< + typename TParamTy, + typename F, + typename... Args> + requires std::derived_from +constexpr +decltype(auto) +visit( + TParamTy& P, + F&& f, + Args&&... args) +{ + switch(P.Kind) + { + case TParamKind::Type: + return f(static_cast&>(P), + std::forward(args)...); + case TParamKind::Constant: + return f(static_cast&>(P), + std::forward(args)...); + case TParamKind::Template: + return f(static_cast&>(P), + std::forward(args)...); + default: + MRDOCS_UNREACHABLE(); + } +} + +MRDOCS_DECL +std::strong_ordering +operator<=>(Polymorphic const& lhs, Polymorphic const& rhs); + +inline +bool +operator==(Polymorphic const& lhs, Polymorphic const& rhs) { + return lhs <=> rhs == std::strong_ordering::equal; +} + +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + Polymorphic const& I, + DomCorpus const* domCorpus) +{ + MRDOCS_ASSERT(!I.valueless_after_move()); + tag_invoke(dom::ValueFromTag{}, v, *I, domCorpus); +} + + +} // mrdocs + +#endif diff --git a/include/mrdocs/Metadata/TParam/ConstantTParam.hpp b/include/mrdocs/Metadata/TParam/ConstantTParam.hpp new file mode 100644 index 000000000..51b354736 --- /dev/null +++ b/include/mrdocs/Metadata/TParam/ConstantTParam.hpp @@ -0,0 +1,42 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_TPARAM_CONSTANTTPARAM_HPP +#define MRDOCS_API_METADATA_TPARAM_CONSTANTTPARAM_HPP + +#include +#include +#include +#include +#include + +namespace mrdocs { + +/** A constant template parameter + + Before C++26, constant template parameters were called + non-type template parameter in the standard wording. + The terminology was changed by P2841R6 / PR#7587. + */ +struct ConstantTParam final + : TParamCommonBase +{ + /** Type of the non-type template parameter */ + Polymorphic Type = Polymorphic(AutoType{}); + + std::strong_ordering operator<=>(ConstantTParam const&) const; +}; + +} // mrdocs + +#endif // MRDOCS_API_METADATA_TPARAM_CONSTANTTPARAM_HPP diff --git a/include/mrdocs/Metadata/TParam/TParamBase.hpp b/include/mrdocs/Metadata/TParam/TParamBase.hpp new file mode 100644 index 000000000..c6a269289 --- /dev/null +++ b/include/mrdocs/Metadata/TParam/TParamBase.hpp @@ -0,0 +1,139 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_TPARAM_TPARAMBASE_HPP +#define MRDOCS_API_METADATA_TPARAM_TPARAMBASE_HPP + +#include +#include +#include +#include +#include + +namespace mrdocs { + +class DomCorpus; + +/* Forward declarations + */ +#define INFO(Type) struct Type##TParam; +#include + +struct TParam +{ + /** The kind of template parameter this is + */ + TParamKind Kind = TParamKind::Type; + + /** The template parameters name, if any + */ + std::string Name; + + /** Whether this template parameter is a parameter pack */ + bool IsParameterPack = false; + + /** The default template argument, if any + */ + Optional> Default = std::nullopt; + + constexpr virtual ~TParam() = default; + + std::strong_ordering operator<=>(TParam const&) const; + + constexpr TParam const& asTParam() const noexcept + { + return *this; + } + + constexpr TParam& asTParam() noexcept + { + return *this; + } + + #define INFO(Type) constexpr bool is##Type() const noexcept { \ + return Kind == TParamKind::Type; \ + } +#include + +#define INFO(Type) \ + constexpr Type##TParam const& as##Type() const noexcept { \ + if (Kind == TParamKind::Type) \ + return reinterpret_cast(*this); \ + MRDOCS_UNREACHABLE(); \ + } +#include + +#define INFO(Type) \ + constexpr Type##TParam & as##Type() noexcept { \ + if (Kind == TParamKind::Type) \ + return reinterpret_cast(*this); \ + MRDOCS_UNREACHABLE(); \ + } +#include + +#define INFO(Type) \ + constexpr Type##TParam const* as##Type##Ptr() const noexcept { \ + if (Kind == TParamKind::Type) { return reinterpret_cast(this); } \ + return nullptr; \ + } +#include + +#define INFO(Type) \ + constexpr Type##TParam * as##Type##Ptr() noexcept { \ + if (Kind == TParamKind::Type) { return reinterpret_cast(this); } \ + return nullptr; \ + } +#include + + +protected: + constexpr + TParam() noexcept = default; + + constexpr + TParam( + TParamKind kind) noexcept + : Kind(kind) + { + } +}; + +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + TParam const& I, + DomCorpus const* domCorpus); + +template +struct TParamCommonBase : TParam +{ + static constexpr TParamKind kind_id = K; + + static constexpr bool isType() noexcept { return K == TParamKind::Type; } + static constexpr bool isConstant() noexcept { return K == TParamKind::Constant; } + static constexpr bool isTemplate() noexcept { return K == TParamKind::Template; } + + auto operator<=>(TParamCommonBase const&) const = default; + +protected: + constexpr + TParamCommonBase() noexcept + : TParam(K) + { + } +}; + +} // mrdocs + +#endif diff --git a/include/mrdocs/Metadata/TParam/TParamInfoNodes.inc b/include/mrdocs/Metadata/TParam/TParamInfoNodes.inc new file mode 100644 index 000000000..0cd860d31 --- /dev/null +++ b/include/mrdocs/Metadata/TParam/TParamInfoNodes.inc @@ -0,0 +1,20 @@ +// +// 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 INFO +#define INFO(PascalName) +#endif + +INFO(Type) +INFO(Constant) +INFO(Template) + +#undef INFO + diff --git a/include/mrdocs/Metadata/TParam/TParamKeyKind.hpp b/include/mrdocs/Metadata/TParam/TParamKeyKind.hpp new file mode 100644 index 000000000..a897eb7c4 --- /dev/null +++ b/include/mrdocs/Metadata/TParam/TParamKeyKind.hpp @@ -0,0 +1,48 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_TPARAM_TPARAMKEYKIND_HPP +#define MRDOCS_API_METADATA_TPARAM_TPARAMKEYKIND_HPP + +#include +#include +#include + +namespace mrdocs { + +/** The keyword a template parameter was declared with */ +enum class TParamKeyKind : int +{ + /// Class keyword + Class = 0, + /// Typename keyword + Typename +}; + +MRDOCS_DECL +std::string_view +toString(TParamKeyKind kind) noexcept; + +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + TParamKeyKind kind) +{ + v = toString(kind); +} + +} // mrdocs + +#endif diff --git a/include/mrdocs/Metadata/TParam/TParamKind.hpp b/include/mrdocs/Metadata/TParam/TParamKind.hpp new file mode 100644 index 000000000..7a6088b2e --- /dev/null +++ b/include/mrdocs/Metadata/TParam/TParamKind.hpp @@ -0,0 +1,34 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_TPARAM_TPARAMKIND_HPP +#define MRDOCS_API_METADATA_TPARAM_TPARAMKIND_HPP + +#include +#include + +namespace mrdocs { + +enum class TParamKind : int +{ + #define INFO(Type) Type, +#include +}; + +MRDOCS_DECL +std::string_view +toString(TParamKind kind) noexcept; + +} // mrdocs + +#endif diff --git a/include/mrdocs/Metadata/TParam/TemplateTParam.hpp b/include/mrdocs/Metadata/TParam/TemplateTParam.hpp new file mode 100644 index 000000000..9114ad7db --- /dev/null +++ b/include/mrdocs/Metadata/TParam/TemplateTParam.hpp @@ -0,0 +1,36 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_TPARAM_TEMPLATETPARAM_HPP +#define MRDOCS_API_METADATA_TPARAM_TEMPLATETPARAM_HPP + +#include +#include +#include +#include + +namespace mrdocs { + +struct TemplateTParam final + : TParamCommonBase +{ + /** Template parameters for the template-template parameter */ + std::vector> Params; + + std::strong_ordering + operator<=>(TemplateTParam const& other) const; +}; + +} // mrdocs + +#endif diff --git a/include/mrdocs/Metadata/TParam/TypeTParam.hpp b/include/mrdocs/Metadata/TParam/TypeTParam.hpp new file mode 100644 index 000000000..73750ec1b --- /dev/null +++ b/include/mrdocs/Metadata/TParam/TypeTParam.hpp @@ -0,0 +1,40 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_TPARAM_TYPETPARAM_HPP +#define MRDOCS_API_METADATA_TPARAM_TYPETPARAM_HPP + +#include +#include +#include +#include +#include +#include + +namespace mrdocs { + +struct TypeTParam final + : TParamCommonBase +{ + /** Keyword (class/typename) the parameter uses */ + TParamKeyKind KeyKind = TParamKeyKind::Class; + + /** The type-constraint for the parameter, if any. */ + Optional> Constraint = std::nullopt; + + std::strong_ordering operator<=>(TypeTParam const&) const; +}; + +} // mrdocs + +#endif diff --git a/include/mrdocs/Metadata/Template.hpp b/include/mrdocs/Metadata/Template.hpp index bc2836bec..5675d96ad 100644 --- a/include/mrdocs/Metadata/Template.hpp +++ b/include/mrdocs/Metadata/Template.hpp @@ -16,358 +16,13 @@ #include #include -#include -#include -#include -#include +#include +#include +#include +#include #include -namespace clang::mrdocs { - -/** The kind of template argument. -*/ -enum class TArgKind : int -{ - /// type arguments - Type = 1, // for bitstream - /// non-type arguments, i.e. expressions - NonType, - /// template template arguments, i.e. template names - Template -}; - -MRDOCS_DECL -std::string_view -toString(TArgKind kind) noexcept; - -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - TArgKind kind) -{ - v = toString(kind); -} - -struct TArg -{ - /** The kind of template argument this is. */ - TArgKind Kind; - - /** Whether this template argument is a parameter expansion. */ - bool IsPackExpansion = false; - - constexpr virtual ~TArg() = default; - - constexpr bool isType() const noexcept { return Kind == TArgKind::Type; } - constexpr bool isNonType() const noexcept { return Kind == TArgKind::NonType; } - constexpr bool isTemplate() const noexcept { return Kind == TArgKind::Template; } - - auto operator<=>(TArg const&) const = default; - -protected: - constexpr - TArg( - TArgKind kind) noexcept - : Kind(kind) - { - } -}; - -template -struct IsTArg : TArg -{ - static constexpr TArgKind kind_id = K; - - static constexpr bool isType() noexcept { return K == TArgKind::Type; } - static constexpr bool isNonType() noexcept { return K == TArgKind::NonType; } - static constexpr bool isTemplate() noexcept { return K == TArgKind::Template; } - -protected: - constexpr - IsTArg() noexcept - : TArg(K) - { - } -}; - -struct TypeTArg final - : IsTArg -{ - /** Template argument type. */ - Polymorphic Type = std::nullopt; - - auto operator<=>(TypeTArg const&) const = default; -}; - - -struct NonTypeTArg final - : IsTArg -{ - /** Template argument expression. */ - ExprInfo Value; - - auto operator<=>(NonTypeTArg const&) const = default; -}; - -struct TemplateTArg final - : IsTArg -{ - /** SymbolID of the referenced template. */ - SymbolID Template; - - /** Name of the referenced template. */ - std::string Name; - - auto operator<=>(TemplateTArg const&) const = default; -}; - -template< - std::derived_from TArgTy, - class F, - class... Args> -constexpr -decltype(auto) -visit( - TArgTy& A, - F&& f, - Args&&... args) -{ - switch(A.Kind) - { - case TArgKind::Type: - return f(static_cast&>(A), - std::forward(args)...); - case TArgKind::NonType: - return f(static_cast&>(A), - std::forward(args)...); - case TArgKind::Template: - return f(static_cast&>(A), - std::forward(args)...); - default: - MRDOCS_UNREACHABLE(); - } -} - -MRDOCS_DECL -std::strong_ordering -operator<=>(Polymorphic const& lhs, Polymorphic const& rhs); - -MRDOCS_DECL -std::string -toString(TArg const& arg) noexcept; - -MRDOCS_DECL -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - TArg const& I, - DomCorpus const* domCorpus); - -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - Polymorphic const& I, - DomCorpus const* domCorpus) -{ - if (!I) - { - v = nullptr; - return; - } - tag_invoke(dom::ValueFromTag{}, v, *I, domCorpus); -} - -// ---------------------------------------------------------------- - -enum class TParamKind : int -{ - /// 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" - NonType, - /// Template-template parameter, e.g. "template typename T" - Template -}; - -MRDOCS_DECL std::string_view toString(TParamKind kind) noexcept; - -struct TParam -{ - /** The kind of template parameter this is */ - TParamKind Kind; - - /** The template parameters name, if any */ - std::string Name; - - /** Whether this template parameter is a parameter pack */ - bool IsParameterPack = false; - - /** The default template argument, if any */ - Polymorphic Default = std::nullopt; - - constexpr virtual ~TParam() = default; - - constexpr bool isType() const noexcept { return Kind == TParamKind::Type; } - constexpr bool isNonType() const noexcept { return Kind == TParamKind::NonType; } - constexpr bool isTemplate() const noexcept { return Kind == TParamKind::Template; } - - std::strong_ordering operator<=>(TParam const&) const; - -protected: - constexpr - TParam( - TParamKind kind) noexcept - : Kind(kind) - { - } -}; - -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - TParam const& I, - DomCorpus const* domCorpus); - -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - Polymorphic const& I, - DomCorpus const* domCorpus) -{ - if (!I) - { - v = nullptr; - return; - } - tag_invoke(dom::ValueFromTag{}, v, *I, domCorpus); -} - - -template -struct TParamCommonBase : TParam -{ - static constexpr TParamKind kind_id = K; - - static constexpr bool isType() noexcept { return K == TParamKind::Type; } - static constexpr bool isNonType() noexcept { return K == TParamKind::NonType; } - static constexpr bool isTemplate() noexcept { return K == TParamKind::Template; } - - auto operator<=>(TParamCommonBase const&) const = default; - -protected: - constexpr - TParamCommonBase() noexcept - : TParam(K) - { - } -}; - -/** The keyword a template parameter was declared with */ -enum class TParamKeyKind : int -{ - /// Class keyword - Class = 0, - /// Typename keyword - Typename -}; - -MRDOCS_DECL std::string_view toString(TParamKeyKind kind) noexcept; - -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - TParamKeyKind kind) -{ - v = toString(kind); -} - -struct TypeTParam final - : TParamCommonBase -{ - /** Keyword (class/typename) the parameter uses */ - TParamKeyKind KeyKind = TParamKeyKind::Class; - - /** The type-constraint for the parameter, if any. */ - Polymorphic Constraint = std::nullopt; - - std::strong_ordering operator<=>(TypeTParam const&) const; -}; - -struct NonTypeTParam final - : TParamCommonBase -{ - /** Type of the non-type template parameter */ - Polymorphic Type = std::nullopt; - - std::strong_ordering operator<=>(NonTypeTParam const&) const; -}; - -struct TemplateTParam final - : TParamCommonBase -{ - /** Template parameters for the template-template parameter */ - std::vector> Params; - - std::strong_ordering - operator<=>(TemplateTParam const& other) const; -}; - -template< - typename TParamTy, - typename F, - typename... Args> - requires std::derived_from -constexpr -decltype(auto) -visit( - TParamTy& P, - F&& f, - Args&&... args) -{ - switch(P.Kind) - { - case TParamKind::Type: - return f(static_cast&>(P), - std::forward(args)...); - case TParamKind::NonType: - return f(static_cast&>(P), - std::forward(args)...); - case TParamKind::Template: - return f(static_cast&>(P), - std::forward(args)...); - default: - MRDOCS_UNREACHABLE(); - } -} - -MRDOCS_DECL -std::strong_ordering -operator<=>(Polymorphic const& lhs, Polymorphic const& rhs); - -inline -bool -operator==(Polymorphic const& lhs, Polymorphic const& rhs) { - return lhs <=> rhs == std::strong_ordering::equal; -} - - -// ---------------------------------------------------------------- +namespace mrdocs { /** The kind of template or specialization. */ @@ -385,9 +40,10 @@ MRDOCS_DECL std::string_view toString(TemplateSpecKind kind); -/** Information pertaining to templates and specializations thereof. + +/** Information about templates and specializations thereof. */ -struct TemplateInfo +struct TemplateInfo final { std::vector> Params; std::vector> Args; @@ -427,7 +83,7 @@ merge(TemplateInfo& I, TemplateInfo&& Other); inline auto -operator<=>(std::optional const& lhs, std::optional const& rhs) +operator<=>(Optional const& lhs, Optional const& rhs) { if (!lhs) { @@ -446,7 +102,7 @@ operator<=>(std::optional const& lhs, std::optional inline bool -operator==(std::optional const& lhs, std::optional const& rhs) +operator==(Optional const& lhs, Optional const& rhs) { return lhs <=> rhs == std::strong_ordering::equal; } @@ -464,7 +120,7 @@ void tag_invoke( dom::ValueFromTag, dom::Value& v, - std::optional const& I, + Optional const& I, DomCorpus const* domCorpus) { if (!I) @@ -475,6 +131,7 @@ tag_invoke( tag_invoke(dom::ValueFromTag{}, v, *I, domCorpus); } -} // clang::mrdocs + +} // mrdocs #endif diff --git a/include/mrdocs/Metadata/Type.hpp b/include/mrdocs/Metadata/Type.hpp index 4a855513e..9cc7e084b 100644 --- a/include/mrdocs/Metadata/Type.hpp +++ b/include/mrdocs/Metadata/Type.hpp @@ -11,491 +11,25 @@ #ifndef MRDOCS_API_METADATA_TYPE_HPP #define MRDOCS_API_METADATA_TYPE_HPP -#include -#include -#include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include -namespace clang::mrdocs { - -struct NameInfo; -struct TypeInfo; - -std::strong_ordering -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 -}; - -MRDOCS_DECL dom::String toString(QualifierKind kind) noexcept; - -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - QualifierKind kind) -{ - v = toString(kind); -} - -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, - /// A Function type - Function, -}; - -MRDOCS_DECL dom::String toString(TypeKind kind) noexcept; - -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - TypeKind kind) -{ - 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 -}; - -MRDOCS_DECL dom::String toString(AutoKind kind) noexcept; - -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - AutoKind kind) -{ - v = toString(kind); -} - -/** A possibly qualified type. - - This class represents a type that may have - qualifiers (e.g. const, volatile). - - This base class is used to store the kind - of type. Derived classes are used to store - the type information according to the kind. - */ -struct TypeInfo -{ - /** The kind of TypeInfo this is - */ - TypeKind Kind; - - /** Whether this is the pattern of a pack expansion. - */ - bool IsPackExpansion = false; - - /** The const qualifier - */ - bool IsConst = false; - - /** The volatile qualifier - */ - bool IsVolatile = false; - - /** The constraints associated with the type - - This represents the constraints associated with the type, - such as SFINAE constraints. - - For instance, if SFINAE detection is enabled, the - expression `std::enable_if_t, T>` - will have type `T` (NamedType) and constraints - `{std::is_integral_v}`. - */ - std::vector Constraints; - - constexpr virtual ~TypeInfo() = default; - - constexpr bool isNamed() const noexcept { return Kind == TypeKind::Named; } - constexpr bool isDecltype() const noexcept { return Kind == TypeKind::Decltype; } - constexpr bool isAuto() const noexcept { return Kind == TypeKind::Auto; } - constexpr bool isLValueReference() const noexcept { return Kind == TypeKind::LValueReference; } - constexpr bool isRValueReference() const noexcept { return Kind == TypeKind::RValueReference; } - constexpr bool isPointer() const noexcept { return Kind == TypeKind::Pointer; } - constexpr bool isMemberPointer() const noexcept { return Kind == TypeKind::MemberPointer; } - constexpr bool isArray() const noexcept { return Kind == TypeKind::Array; } - constexpr bool isFunction() const noexcept { return Kind == TypeKind::Function; } - - /** Return the symbol named by this type. - */ - SymbolID - namedSymbol() const noexcept; - - auto operator<=>(TypeInfo const&) const = default; - -protected: - constexpr - TypeInfo( - TypeKind kind) noexcept - : Kind(kind) - { - } -}; - -MRDOCS_DECL -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - TypeInfo const& I, - DomCorpus const* domCorpus); - -inline -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - Polymorphic const& I, - DomCorpus const* domCorpus) -{ - if (!I) - { - v = nullptr; - return; - } - tag_invoke(dom::ValueFromTag{}, v, *I, domCorpus); -} - -template -struct TypeInfoCommonBase : TypeInfo -{ - static constexpr TypeKind kind_id = K; - - static constexpr bool isNamed() noexcept { return K == TypeKind::Named; } - static constexpr bool isDecltype() noexcept { return K == TypeKind::Decltype; } - static constexpr bool isAuto() noexcept { return K == TypeKind::Auto; } - static constexpr bool isLValueReference() noexcept { return K == TypeKind::LValueReference; } - static constexpr bool isRValueReference() noexcept { return K == TypeKind::RValueReference; } - static constexpr bool isPointer() noexcept { return K == TypeKind::Pointer; } - static constexpr bool isMemberPointer() noexcept { return K == TypeKind::MemberPointer; } - static constexpr bool isArray() noexcept { return K == TypeKind::Array; } - static constexpr bool isFunction() noexcept { return K == TypeKind::Function; } - - auto operator<=>(TypeInfoCommonBase const&) const = default; - -protected: - constexpr - TypeInfoCommonBase() noexcept - : TypeInfo(K) - { - } -}; - -/** Categorically describes a fundamental type. - - @see https://en.cppreference.com/w/cpp/language/types -*/ -enum class FundamentalTypeKind -{ - /// void - Void, - /// std::nullptr_t - Nullptr, - /// bool - Bool, - /// char - Char, - /// signed char - SignedChar, - /// unsigned char - UnsignedChar, - /// char8_t - Char8, - /// char16_t - Char16, - /// char32_t - Char32, - /// wchar_t - WChar, - /// short / short int / signed short / signed short int - Short, - /// unsigned short / unsigned short int - UnsignedShort, - /// int / signed / signed int - Int, - /// unsigned / unsigned int - UnsignedInt, - /// long / long int / signed long / signed long int - Long, - /// unsigned long / unsigned long int - UnsignedLong, - /// long long / long long int / signed long long / signed long long int - LongLong, - /// unsigned long long / unsigned long long int - UnsignedLongLong, - /// float - Float, - /// double - Double, - /// long double - LongDouble -}; - -/** Convert a FundamentalTypeKind to a string. - - This function converts a FundamentalTypeKind to - the shortest canonical string representing the type. - - @return The string representation of the kind - */ -MRDOCS_DECL -std::string_view -toString(FundamentalTypeKind kind) noexcept; - -/** Convert a string to a FundamentalTypeKind. - - This function converts a string to a FundamentalTypeKind. - - All variations of the type specifiers are supported. - - 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 -bool -fromString(std::string_view str, FundamentalTypeKind& kind) noexcept; - -/** Apply the "long" specifier to the type - - If applying "long" the specifier is a valid operation - the function changes the type and returns true. - - For instance, applying "long" to - `FundamentalTypeKind::Int` ("int") results in - `FundamentalTypeKind::Long` ("long int"). - - @param[in/out] kind The type to modify - @return Whether the operation was successful - */ -MRDOCS_DECL -bool -makeLong(FundamentalTypeKind& kind) noexcept; - -/** Apply the "short" specifier to the type - - If applying "short" the specifier is a valid operation - the function changes the type and returns true. - - For instance, applying "short" to - `FundamentalTypeKind::Int` ("int") results in - `FundamentalTypeKind::Short` ("short int"). - - @param[in/out] kind The type to modify - @return Whether the operation was successful - */ -MRDOCS_DECL -bool -makeShort(FundamentalTypeKind& kind) noexcept; - -/** Apply the "signed" specifier to the type - - If applying the "signed" specifier is a valid operation - the function changes the type and returns true. - - For instance, applying "signed" to - `FundamentalTypeKind::Char` ("char") results in - `FundamentalTypeKind::SignedChar` ("signed char"). - - It also returns true if applying the "signed" specifier - is a valid operation but doesn't affect the - type. - - For instance, applying "signed" to - `FundamentalTypeKind::Int` ("int") doesn't change the type - but returns `true`, even though `FundamentalTypeKind::Int` - could be declared as "int" or "signed" and multiple - "signed" specifiers are not allowed. - - @param[in/out] kind The type to modify - @return Whether the operation was successful - */ -MRDOCS_DECL -bool -makeSigned(FundamentalTypeKind& kind) noexcept; - -/** Apply the "unsigned" specifier to the type - - If applying the "unsigned" specifier is a valid operation - the function changes the type and returns true. - - For instance, applying "unsigned" to - `FundamentalTypeKind::Char` ("char") results in - `FundamentalTypeKind::UnsignedChar` ("unsigned char") - and applying "unsigned" to - `FundamentalTypeKind::Int` ("int") results in - `FundamentalTypeKind::UnsignedInt` ("unsigned int"). - - @param[in/out] kind The type to modify - @return Whether the operation was successful - */ -MRDOCS_DECL -bool -makeUnsigned(FundamentalTypeKind& kind) noexcept; - -/** Apply the "char" specifier to the type - - If applying the "char" specifier to a type - that might have been declared only with "signed/unsigned" - or "short/long" specifiers, the function changes the type - and returns true. - - For instance, applying "char" to - `FundamentalTypeKind::Int` ("int", which could be declared - as "signed") results in - `FundamentalTypeKind::SignedChar` ("signed char"). - - @param[in/out] kind The type to modify - @return Whether the operation was successful - */ -MRDOCS_DECL -bool -makeChar(FundamentalTypeKind& kind) noexcept; - -struct NamedTypeInfo final - : TypeInfoCommonBase -{ - Polymorphic Name = std::nullopt; - - std::optional FundamentalType; - - std::strong_ordering - operator<=>(NamedTypeInfo const& other) const; -}; - -struct DecltypeTypeInfo final - : TypeInfoCommonBase -{ - ExprInfo Operand; - - auto operator<=>(DecltypeTypeInfo const&) const = default; -}; - -struct AutoTypeInfo final - : TypeInfoCommonBase -{ - AutoKind Keyword = AutoKind::Auto; - Polymorphic Constraint = std::nullopt; - - std::strong_ordering - operator<=>(AutoTypeInfo const&) const; -}; - -struct LValueReferenceTypeInfo final - : TypeInfoCommonBase -{ - Polymorphic PointeeType = std::nullopt; - - std::strong_ordering - operator<=>(LValueReferenceTypeInfo const&) const; -}; - -struct RValueReferenceTypeInfo final - : TypeInfoCommonBase -{ - Polymorphic PointeeType = std::nullopt; - - std::strong_ordering - operator<=>(RValueReferenceTypeInfo const&) const; -}; - -struct PointerTypeInfo final - : TypeInfoCommonBase -{ - Polymorphic PointeeType = std::nullopt; - - std::strong_ordering - operator<=>(PointerTypeInfo const&) const; -}; - -struct MemberPointerTypeInfo final - : TypeInfoCommonBase -{ - Polymorphic ParentType = std::nullopt; - Polymorphic PointeeType = std::nullopt; - - std::strong_ordering - operator<=>(MemberPointerTypeInfo const&) const; -}; - -struct ArrayTypeInfo final - : TypeInfoCommonBase -{ - Polymorphic ElementType = std::nullopt; - ConstantExprInfo Bounds; - - std::strong_ordering - operator<=>(ArrayTypeInfo const&) const; -}; - -struct FunctionTypeInfo final - : TypeInfoCommonBase -{ - Polymorphic ReturnType = std::nullopt; - std::vector> ParamTypes; - ReferenceKind RefQualifier = ReferenceKind::None; - NoexceptInfo ExceptionSpec; - bool IsVariadic = false; - - std::strong_ordering - operator<=>(FunctionTypeInfo const&) const; -}; +namespace mrdocs { template< - std::derived_from TypeTy, + std::derived_from TypeTy, class F, class... Args> decltype(auto) @@ -504,44 +38,44 @@ visit( F&& f, Args&&... args) { - add_cv_from_t& II = I; + add_cv_from_t& II = I; switch(I.Kind) { case TypeKind::Named: return f(static_cast&>(II), + TypeTy, NamedType>&>(II), std::forward(args)...); case TypeKind::Decltype: return f(static_cast&>(II), + TypeTy, DecltypeType>&>(II), std::forward(args)...); case TypeKind::Auto: return f(static_cast&>(II), + TypeTy, AutoType>&>(II), std::forward(args)...); case TypeKind::LValueReference: return f(static_cast&>(II), + TypeTy, LValueReferenceType>&>(II), std::forward(args)...); case TypeKind::RValueReference: return f(static_cast&>(II), + TypeTy, RValueReferenceType>&>(II), std::forward(args)...); case TypeKind::Pointer: return f(static_cast&>(II), + TypeTy, PointerType>&>(II), std::forward(args)...); case TypeKind::MemberPointer: return f(static_cast&>(II), + TypeTy, MemberPointerType>&>(II), std::forward(args)...); case TypeKind::Array: return f(static_cast&>(II), + TypeTy, ArrayType>&>(II), std::forward(args)...); case TypeKind::Function: return f(static_cast&>(II), + TypeTy, FunctionType>&>(II), std::forward(args)...); default: MRDOCS_UNREACHABLE(); @@ -550,37 +84,61 @@ visit( MRDOCS_DECL std::strong_ordering -operator<=>(Polymorphic const& lhs, Polymorphic const& rhs); +operator<=>(Polymorphic const& lhs, Polymorphic const& rhs); inline bool -operator==(Polymorphic const& lhs, Polymorphic const& rhs) { +operator==(Polymorphic const& lhs, Polymorphic const& rhs) { + return lhs <=> rhs == std::strong_ordering::equal; +} + +inline std::strong_ordering +operator<=>( + Optional> const& lhs, + Optional> const& rhs) +{ + if (lhs && rhs) + { + return *lhs <=> *rhs; + } + if (!lhs && !rhs) + { + return std::strong_ordering::equal; + } + return bool(lhs) <=> bool(rhs); +} + +inline bool +operator==( + Optional> const& lhs, + Optional> const& rhs) +{ return lhs <=> rhs == std::strong_ordering::equal; } /** Return the inner type. - The inner type is the type which is modified - by a specifier (e.g. "int" in "pointer to int". + The inner type is the type that is modified + by a specifier (e.g. "int" in "pointer to int"). */ MRDOCS_DECL -std::optional const>> -innerType(TypeInfo const& TI) noexcept; +Optional const&> +innerType(Type const& TI) noexcept; -/// @copydoc innerType(TypeInfo const&) +/// @copydoc innerType(Type const&) MRDOCS_DECL -std::optional>> -innerType(TypeInfo& TI) noexcept; +Optional&> +innerType(Type& TI) noexcept; -/// @copydoc innerType(TypeInfo const&) +/// @copydoc innerType(Type const&) MRDOCS_DECL -TypeInfo const* -innerTypePtr(TypeInfo const& TI) noexcept; +Type const* +innerTypePtr(Type const& TI) noexcept; -/// @copydoc innerTypePtr(TypeInfo const&) +/// @copydoc innerTypePtr(Type const&) MRDOCS_DECL -TypeInfo* -innerTypePtr(TypeInfo& TI) noexcept; +Type* +innerTypePtr(Type& TI) noexcept; /** Return the innermost type. @@ -594,21 +152,50 @@ innerTypePtr(TypeInfo& TI) noexcept; the current type. */ MRDOCS_DECL -Polymorphic const& -innermostType(Polymorphic const& TI) noexcept; +Polymorphic const& +innermostType(Polymorphic const& TI) noexcept; -/// @copydoc innermostType(Polymorphic const&) +/// @copydoc innermostType(Polymorphic const&) MRDOCS_DECL -Polymorphic& -innermostType(Polymorphic& TI) noexcept; +Polymorphic& +innermostType(Polymorphic& TI) noexcept; // VFALCO maybe we should rename this to `renderType` or something? MRDOCS_DECL std::string -toString( - TypeInfo const& T, +toString(Type const& T, std::string_view Name = ""); -} // clang::mrdocs +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + Polymorphic const& I, + DomCorpus const* domCorpus) +{ + MRDOCS_ASSERT(!I.valueless_after_move()); + tag_invoke(dom::ValueFromTag{}, v, *I, domCorpus); +} + +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + Optional> const& I, + DomCorpus const* domCorpus) +{ + if (!I) + { + v = nullptr; + return; + } + MRDOCS_ASSERT(!I->valueless_after_move()); + tag_invoke(dom::ValueFromTag{}, v, **I, domCorpus); +} + + +} // mrdocs #endif diff --git a/include/mrdocs/Metadata/Type/ArrayType.hpp b/include/mrdocs/Metadata/Type/ArrayType.hpp new file mode 100644 index 000000000..2fcadb608 --- /dev/null +++ b/include/mrdocs/Metadata/Type/ArrayType.hpp @@ -0,0 +1,34 @@ +// +// 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) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_TYPE_ARRAYTYPE_HPP +#define MRDOCS_API_METADATA_TYPE_ARRAYTYPE_HPP + +#include +#include +#include +#include +#include + +namespace mrdocs { + +struct ArrayType final + : TypeCommonBase +{ + Polymorphic ElementType = Polymorphic(AutoType{}); + ConstantExprInfo Bounds; + + std::strong_ordering + operator<=>(ArrayType const&) const; +}; + +} // mrdocs + +#endif // MRDOCS_API_METADATA_TYPE_ARRAYTYPE_HPP diff --git a/include/mrdocs/Metadata/Type/AutoKind.hpp b/include/mrdocs/Metadata/Type/AutoKind.hpp new file mode 100644 index 000000000..2193c40df --- /dev/null +++ b/include/mrdocs/Metadata/Type/AutoKind.hpp @@ -0,0 +1,47 @@ +// +// 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) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_TYPE_AUTOKIND_HPP +#define MRDOCS_API_METADATA_TYPE_AUTOKIND_HPP + +#include +#include + +namespace mrdocs { + +/** 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 +}; + +MRDOCS_DECL +dom::String +toString(AutoKind kind) noexcept; + +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + AutoKind kind) +{ + v = toString(kind); +} + +} // mrdocs + +#endif diff --git a/include/mrdocs/Metadata/Type/AutoType.hpp b/include/mrdocs/Metadata/Type/AutoType.hpp new file mode 100644 index 000000000..11ce16cf0 --- /dev/null +++ b/include/mrdocs/Metadata/Type/AutoType.hpp @@ -0,0 +1,37 @@ +// +// 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) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_TYPE_AUTOTYPE_HPP +#define MRDOCS_API_METADATA_TYPE_AUTOTYPE_HPP + +#include +#include +#include +#include +#include + +namespace mrdocs { + +struct AutoType final + : TypeCommonBase +{ + AutoKind Keyword = AutoKind::Auto; + + /** Constraint on the auto type, if any. + */ + Optional> Constraint = std::nullopt; + + std::strong_ordering + operator<=>(AutoType const&) const; +}; + +} // mrdocs + +#endif // MRDOCS_API_METADATA_TYPE_AUTOTYPE_HPP diff --git a/include/mrdocs/Metadata/Type/DecltypeType.hpp b/include/mrdocs/Metadata/Type/DecltypeType.hpp new file mode 100644 index 000000000..79202c408 --- /dev/null +++ b/include/mrdocs/Metadata/Type/DecltypeType.hpp @@ -0,0 +1,29 @@ +// +// 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) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_TYPE_DECLTYPETYPE_HPP +#define MRDOCS_API_METADATA_TYPE_DECLTYPETYPE_HPP + +#include +#include + +namespace mrdocs { + +struct DecltypeType final + : TypeCommonBase +{ + ExprInfo Operand; + + auto operator<=>(DecltypeType const&) const = default; +}; + +} // mrdocs + +#endif // MRDOCS_API_METADATA_TYPE_DECLTYPETYPE_HPP diff --git a/include/mrdocs/Metadata/Type/FunctionType.hpp b/include/mrdocs/Metadata/Type/FunctionType.hpp new file mode 100644 index 000000000..e869665a0 --- /dev/null +++ b/include/mrdocs/Metadata/Type/FunctionType.hpp @@ -0,0 +1,38 @@ +// +// 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) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_TYPE_FUNCTIONTYPE_HPP +#define MRDOCS_API_METADATA_TYPE_FUNCTIONTYPE_HPP + +#include +#include +#include +#include +#include +#include + +namespace mrdocs { + +struct FunctionType final + : TypeCommonBase +{ + Polymorphic ReturnType = Polymorphic(AutoType{}); + std::vector> ParamTypes; + ReferenceKind RefQualifier = ReferenceKind::None; + NoexceptInfo ExceptionSpec; + bool IsVariadic = false; + + std::strong_ordering + operator<=>(FunctionType const&) const; +}; + +} // mrdocs + +#endif // MRDOCS_API_METADATA_TYPE_FUNCTIONTYPE_HPP diff --git a/include/mrdocs/Metadata/Type/FundamentalTypeKind.hpp b/include/mrdocs/Metadata/Type/FundamentalTypeKind.hpp new file mode 100644 index 000000000..7687a4b06 --- /dev/null +++ b/include/mrdocs/Metadata/Type/FundamentalTypeKind.hpp @@ -0,0 +1,196 @@ +// +// 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) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_TYPE_FUNDAMENTALTYPEKIND_HPP +#define MRDOCS_API_METADATA_TYPE_FUNDAMENTALTYPEKIND_HPP + +#include +#include + +namespace mrdocs { + +/** Categorically describes a fundamental type. + + @see https://en.cppreference.com/w/cpp/language/types +*/ +enum class FundamentalTypeKind +{ + /// void + Void, + /// std::nullptr_t + Nullptr, + /// bool + Bool, + /// char + Char, + /// signed char + SignedChar, + /// unsigned char + UnsignedChar, + /// char8_t + Char8, + /// char16_t + Char16, + /// char32_t + Char32, + /// wchar_t + WChar, + /// short / short int / signed short / signed short int + Short, + /// unsigned short / unsigned short int + UnsignedShort, + /// int / signed / signed int + Int, + /// unsigned / unsigned int + UnsignedInt, + /// long / long int / signed long / signed long int + Long, + /// unsigned long / unsigned long int + UnsignedLong, + /// long long / long long int / signed long long / signed long long int + LongLong, + /// unsigned long long / unsigned long long int + UnsignedLongLong, + /// float + Float, + /// double + Double, + /// long double + LongDouble +}; + +/** Convert a FundamentalTypeKind to a string. + + This function converts a FundamentalTypeKind to + the shortest canonical string representing the type. + + @return The string representation of the kind + */ +MRDOCS_DECL +std::string_view +toString(FundamentalTypeKind kind) noexcept; + +/** Convert a string to a FundamentalTypeKind. + + This function converts a string to a FundamentalTypeKind. + + All variations of the type specifiers are supported. + + 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 +bool +fromString(std::string_view str, FundamentalTypeKind& kind) noexcept; + +/** Apply the "long" specifier to the type + + If applying "long" the specifier is a valid operation + the function changes the type and returns true. + + For instance, applying "long" to + `FundamentalTypeKind::Int` ("int") results in + `FundamentalTypeKind::Long` ("long int"). + + @param[in/out] kind The type to modify + @return Whether the operation was successful + */ +MRDOCS_DECL +bool +makeLong(FundamentalTypeKind& kind) noexcept; + +/** Apply the "short" specifier to the type + + If applying "short" the specifier is a valid operation + the function changes the type and returns true. + + For instance, applying "short" to + `FundamentalTypeKind::Int` ("int") results in + `FundamentalTypeKind::Short` ("short int"). + + @param[in/out] kind The type to modify + @return Whether the operation was successful + */ +MRDOCS_DECL +bool +makeShort(FundamentalTypeKind& kind) noexcept; + +/** Apply the "signed" specifier to the type + + If applying the "signed" specifier is a valid operation + the function changes the type and returns true. + + For instance, applying "signed" to + `FundamentalTypeKind::Char` ("char") results in + `FundamentalTypeKind::SignedChar` ("signed char"). + + It also returns true if applying the "signed" specifier + is a valid operation but doesn't affect the + type. + + For instance, applying "signed" to + `FundamentalTypeKind::Int` ("int") doesn't change the type + but returns `true`, even though `FundamentalTypeKind::Int` + could be declared as "int" or "signed" and multiple + "signed" specifiers are not allowed. + + @param[in/out] kind The type to modify + @return Whether the operation was successful + */ +MRDOCS_DECL +bool +makeSigned(FundamentalTypeKind& kind) noexcept; + +/** Apply the "unsigned" specifier to the type + + If applying the "unsigned" specifier is a valid operation + the function changes the type and returns true. + + For instance, applying "unsigned" to + `FundamentalTypeKind::Char` ("char") results in + `FundamentalTypeKind::UnsignedChar` ("unsigned char") + and applying "unsigned" to + `FundamentalTypeKind::Int` ("int") results in + `FundamentalTypeKind::UnsignedInt` ("unsigned int"). + + @param[in/out] kind The type to modify + @return Whether the operation was successful + */ +MRDOCS_DECL +bool +makeUnsigned(FundamentalTypeKind& kind) noexcept; + +/** Apply the "char" specifier to the type + + If applying the "char" specifier to a type + that might have been declared only with "signed/unsigned" + or "short/long" specifiers, the function changes the type + and returns true. + + For instance, applying "char" to + `FundamentalTypeKind::Int` ("int", which could be declared + as "signed") results in + `FundamentalTypeKind::SignedChar` ("signed char"). + + @param[in/out] kind The type to modify + @return Whether the operation was successful + */ +MRDOCS_DECL +bool +makeChar(FundamentalTypeKind& kind) noexcept; + +} // mrdocs + +#endif diff --git a/include/mrdocs/Metadata/Type/LValueReferenceType.hpp b/include/mrdocs/Metadata/Type/LValueReferenceType.hpp new file mode 100644 index 000000000..0899d107d --- /dev/null +++ b/include/mrdocs/Metadata/Type/LValueReferenceType.hpp @@ -0,0 +1,32 @@ +// +// 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) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_TYPE_LVALUEREFERENCETYPE_HPP +#define MRDOCS_API_METADATA_TYPE_LVALUEREFERENCETYPE_HPP + +#include +#include +#include +#include + +namespace mrdocs { + +struct LValueReferenceType final + : TypeCommonBase +{ + Polymorphic PointeeType = Polymorphic(AutoType{}); + + std::strong_ordering + operator<=>(LValueReferenceType const&) const; +}; + +} // mrdocs + +#endif // MRDOCS_API_METADATA_TYPE_LVALUEREFERENCETYPE_HPP diff --git a/include/mrdocs/Metadata/Type/MemberPointerType.hpp b/include/mrdocs/Metadata/Type/MemberPointerType.hpp new file mode 100644 index 000000000..ed620cb36 --- /dev/null +++ b/include/mrdocs/Metadata/Type/MemberPointerType.hpp @@ -0,0 +1,33 @@ +// +// 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) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_TYPE_MEMBERPOINTERTYPE_HPP +#define MRDOCS_API_METADATA_TYPE_MEMBERPOINTERTYPE_HPP + +#include +#include +#include +#include + +namespace mrdocs { + +struct MemberPointerType final + : TypeCommonBase +{ + Polymorphic ParentType = Polymorphic(AutoType{}); + Polymorphic PointeeType = Polymorphic(AutoType{}); + + std::strong_ordering + operator<=>(MemberPointerType const&) const; +}; + +} // mrdocs + +#endif // MRDOCS_API_METADATA_TYPE_MEMBERPOINTERTYPE_HPP diff --git a/include/mrdocs/Metadata/Type/NamedType.hpp b/include/mrdocs/Metadata/Type/NamedType.hpp new file mode 100644 index 000000000..932856593 --- /dev/null +++ b/include/mrdocs/Metadata/Type/NamedType.hpp @@ -0,0 +1,38 @@ +// +// 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) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_TYPE_NAMEDTYPE_HPP +#define MRDOCS_API_METADATA_TYPE_NAMEDTYPE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mrdocs { + +struct NamedType final + : TypeCommonBase +{ + Polymorphic Name = Polymorphic(std::in_place_type); + + Optional FundamentalType; + + std::strong_ordering + operator<=>(NamedType const& other) const; +}; + +} // mrdocs + +#endif // MRDOCS_API_METADATA_TYPE_NAMEDTYPE_HPP diff --git a/include/mrdocs/Metadata/Type/PointerType.hpp b/include/mrdocs/Metadata/Type/PointerType.hpp new file mode 100644 index 000000000..b7a6b3e0d --- /dev/null +++ b/include/mrdocs/Metadata/Type/PointerType.hpp @@ -0,0 +1,32 @@ +// +// 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) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_TYPE_POINTERTYPE_HPP +#define MRDOCS_API_METADATA_TYPE_POINTERTYPE_HPP + +#include +#include +#include +#include + +namespace mrdocs { + +struct PointerType final + : TypeCommonBase +{ + Polymorphic PointeeType = Polymorphic(AutoType{}); + + std::strong_ordering + operator<=>(PointerType const&) const; +}; + +} // mrdocs + +#endif // MRDOCS_API_METADATA_TYPE_POINTERTYPE_HPP diff --git a/include/mrdocs/Metadata/Type/QualifierKind.hpp b/include/mrdocs/Metadata/Type/QualifierKind.hpp new file mode 100644 index 000000000..05ed091a8 --- /dev/null +++ b/include/mrdocs/Metadata/Type/QualifierKind.hpp @@ -0,0 +1,55 @@ +// +// 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) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_TYPE_QUALIFIERKIND_HPP +#define MRDOCS_API_METADATA_TYPE_QUALIFIERKIND_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mrdocs { + +/** Type qualifiers +*/ +enum QualifierKind +{ + /// No qualifiers + None, + /// The const qualifier + Const, + /// The volatile qualifier + Volatile +}; + +MRDOCS_DECL +dom::String +toString(QualifierKind kind) noexcept; + +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + QualifierKind kind) +{ + v = toString(kind); +} + +} // mrdocs + +#endif diff --git a/include/mrdocs/Metadata/Type/RValueReferenceType.hpp b/include/mrdocs/Metadata/Type/RValueReferenceType.hpp new file mode 100644 index 000000000..57703d223 --- /dev/null +++ b/include/mrdocs/Metadata/Type/RValueReferenceType.hpp @@ -0,0 +1,32 @@ +// +// 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) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_TYPE_RVALUEREFERENCETYPE_HPP +#define MRDOCS_API_METADATA_TYPE_RVALUEREFERENCETYPE_HPP + +#include +#include +#include +#include + +namespace mrdocs { + +struct RValueReferenceType final + : TypeCommonBase +{ + Polymorphic PointeeType = Polymorphic(AutoType{}); + + std::strong_ordering + operator<=>(RValueReferenceType const&) const; +}; + +} // mrdocs + +#endif // MRDOCS_API_METADATA_TYPE_RVALUEREFERENCETYPE_HPP diff --git a/include/mrdocs/Metadata/Type/TypeBase.hpp b/include/mrdocs/Metadata/Type/TypeBase.hpp new file mode 100644 index 000000000..5f6ae86b2 --- /dev/null +++ b/include/mrdocs/Metadata/Type/TypeBase.hpp @@ -0,0 +1,161 @@ +// +// 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) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_TYPE_TYPEBASE_HPP +#define MRDOCS_API_METADATA_TYPE_TYPEBASE_HPP + +#include +#include +#include +#include +#include +#include + +namespace mrdocs { + +/* Forward declarations + */ +#define INFO(TypeKind) struct TypeKind##Type; +#include + +/** A possibly qualified type. + + This class represents a type that may have + qualifiers (e.g. const, volatile). + + This base class is used to store the kind + of type. Derived classes are used to store + the type information according to the kind. + */ +struct Type { + /** The kind of Type this is + */ + TypeKind Kind; + + /** Whether this is the pattern of a pack expansion. + */ + bool IsPackExpansion = false; + + /** The const qualifier + */ + bool IsConst = false; + + /** The volatile qualifier + */ + bool IsVolatile = false; + + /** The constraints associated with the type + + This represents the constraints associated with the type, + such as SFINAE constraints. + + For instance, if SFINAE detection is enabled, the + expression `std::enable_if_t, T>` + will have type `T` (NamedType) and constraints + `{std::is_integral_v}`. + */ + std::vector Constraints; + + /** Return the symbol named by this type. + */ + SymbolID + namedSymbol() const noexcept; + + auto operator<=>(Type const&) const = default; + + constexpr Type const& asType() const noexcept + { + return *this; + } + + constexpr Type& asType() noexcept + { + return *this; + } + + #define INFO(Type) constexpr bool is##Type() const noexcept { \ + return Kind == TypeKind::Type; \ + } +#include + +#define INFO(TypeKindName) \ + constexpr TypeKindName##Type const& as##TypeKindName() const noexcept { \ + if (Kind == TypeKind::TypeKindName) \ + return reinterpret_cast(*this); \ + MRDOCS_UNREACHABLE(); \ + } +#include + +#define INFO(TypeKindName) \ + constexpr TypeKindName##Type & as##TypeKindName() noexcept { \ + if (Kind == TypeKind::TypeKindName) \ + return reinterpret_cast(*this); \ + MRDOCS_UNREACHABLE(); \ + } +#include + +#define INFO(TypeKindName) \ + constexpr TypeKindName##Type const* as##TypeKindName##Ptr() const noexcept { \ + if (Kind == TypeKind::TypeKindName) { return reinterpret_cast(this); } \ + return nullptr; \ + } +#include + +#define INFO(TypeKindName) \ + constexpr TypeKindName##Type * as##TypeKindName##Ptr() noexcept { \ + if (Kind == TypeKind::TypeKindName) { return reinterpret_cast(this); } \ + return nullptr; \ + } +#include + +protected: + constexpr virtual ~Type() = default; + + constexpr Type( + TypeKind kind) noexcept + : Kind(kind) + { + } +}; + +MRDOCS_DECL +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + Type const& I, + DomCorpus const* domCorpus); + +template +struct TypeCommonBase : Type { + static constexpr TypeKind kind_id = K; + + static constexpr bool isNamed() noexcept { return K == TypeKind::Named; } + static constexpr bool isDecltype() noexcept { return K == TypeKind::Decltype; } + static constexpr bool isAuto() noexcept { return K == TypeKind::Auto; } + static constexpr bool isLValueReference() noexcept { return K == TypeKind::LValueReference; } + static constexpr bool isRValueReference() noexcept { return K == TypeKind::RValueReference; } + static constexpr bool isPointer() noexcept { return K == TypeKind::Pointer; } + static constexpr bool isMemberPointer() noexcept { return K == TypeKind::MemberPointer; } + static constexpr bool isArray() noexcept { return K == TypeKind::Array; } + static constexpr bool isFunction() noexcept { return K == TypeKind::Function; } + + auto operator<=>(TypeCommonBase const&) const = default; + +protected: + constexpr TypeCommonBase() noexcept + : Type(K) + { + } +}; + +} // mrdocs + +#endif diff --git a/include/mrdocs/Metadata/Type/TypeKind.hpp b/include/mrdocs/Metadata/Type/TypeKind.hpp new file mode 100644 index 000000000..af01dcb1b --- /dev/null +++ b/include/mrdocs/Metadata/Type/TypeKind.hpp @@ -0,0 +1,41 @@ +// +// 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) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_TYPE_TYPEKIND_HPP +#define MRDOCS_API_METADATA_TYPE_TYPEKIND_HPP + +#include +#include + +namespace mrdocs { + +enum class TypeKind +{ + #define INFO(Type) Type, +#include +}; + +MRDOCS_DECL +dom::String +toString(TypeKind kind) noexcept; + +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + TypeKind kind) +{ + v = toString(kind); +} + +} // mrdocs + +#endif diff --git a/include/mrdocs/Metadata/Type/TypeNodes.inc b/include/mrdocs/Metadata/Type/TypeNodes.inc new file mode 100644 index 000000000..9b6cfb368 --- /dev/null +++ b/include/mrdocs/Metadata/Type/TypeNodes.inc @@ -0,0 +1,35 @@ +// +// 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 INFO +#define INFO(PascalName) +#endif + +/// A Named type +INFO(Named) +/// A decltype type +INFO(Decltype) +/// An auto type +INFO(Auto) +/// An LValueReference type +INFO(LValueReference) +/// An RValueReference type +INFO(RValueReference) +/// A Pointer type +INFO(Pointer) +/// A MemberPointer type +INFO(MemberPointer) +/// An Array type +INFO(Array) +/// A Function type +INFO(Function) + +#undef INFO + diff --git a/include/mrdocs/Platform.hpp b/include/mrdocs/Platform.hpp index 4c7f77e29..9a99dd94a 100644 --- a/include/mrdocs/Platform.hpp +++ b/include/mrdocs/Platform.hpp @@ -23,7 +23,7 @@ that is dependent on the toolchain. */ -namespace clang { + namespace mrdocs { /** The minimum version of LLVM required @@ -85,6 +85,6 @@ namespace mrdocs { #endif } // mrdocs -} // clang -#endif + +#endif // MRDOCS_API_PLATFORM_HPP diff --git a/include/mrdocs/Support/Algorithm.hpp b/include/mrdocs/Support/Algorithm.hpp index 9bc8a1218..d1eeacd78 100644 --- a/include/mrdocs/Support/Algorithm.hpp +++ b/include/mrdocs/Support/Algorithm.hpp @@ -12,10 +12,10 @@ #define MRDOCS_API_SUPPORT_ALGORITHM_HPP #include -#include #include +#include -namespace clang::mrdocs { +namespace mrdocs { /** Determine if a range contains a specific element. @param range The range to search. @@ -188,6 +188,6 @@ find_last_of(Range&& range, Els&& els) return std::ranges::end(range); } -} // clang::mrdocs +} // mrdocs #endif \ No newline at end of file diff --git a/include/mrdocs/Support/Assert.hpp b/include/mrdocs/Support/Assert.hpp index 5cd895fe7..77a97604d 100644 --- a/include/mrdocs/Support/Assert.hpp +++ b/include/mrdocs/Support/Assert.hpp @@ -13,7 +13,6 @@ #include -namespace clang { namespace mrdocs { #ifdef NDEBUG @@ -42,6 +41,5 @@ namespace mrdocs { #endif } // mrdocs -} // clang #endif \ No newline at end of file diff --git a/include/mrdocs/Support/Concepts.hpp b/include/mrdocs/Support/Concepts.hpp index 71c377bc0..bc31d07db 100644 --- a/include/mrdocs/Support/Concepts.hpp +++ b/include/mrdocs/Support/Concepts.hpp @@ -13,7 +13,7 @@ #include -namespace clang::mrdocs { +namespace mrdocs { /** Concept to check if a type is a range of T */ template @@ -135,7 +135,28 @@ template concept range_of_tuple_like = std::ranges::range && tuple_like>; -} // namespace clang::mrdocs +#ifdef __cpp_lib_reference_from_temporary + using std::reference_constructs_from_temporary_v; + using std::reference_converts_from_temporary_v; +#else + template + concept reference_converts_from_temporary_v + = std::is_reference_v + && ((!std::is_reference_v + && std::is_convertible_v< + std::remove_cvref_t*, + std::remove_cvref_t*>) + || (std::is_lvalue_reference_v + && std::is_const_v> + && std::is_convertible_v&&> + && !std::is_convertible_v&>) ); + + template + concept reference_constructs_from_temporary_v + = reference_converts_from_temporary_v; +#endif + +} // namespace mrdocs #endif // MRDOCS_API_SUPPORT_CONCEPTS_HPP diff --git a/include/mrdocs/Support/Error.hpp b/include/mrdocs/Support/Error.hpp index 1cffb2768..8d8cf92ce 100644 --- a/include/mrdocs/Support/Error.hpp +++ b/include/mrdocs/Support/Error.hpp @@ -12,12 +12,12 @@ #ifndef MRDOCS_API_SUPPORT_ERROR_HPP #define MRDOCS_API_SUPPORT_ERROR_HPP +#include +#include #include #include #include #include -#include -#include #include #include #include @@ -25,7 +25,7 @@ #include #include -namespace clang::mrdocs { +namespace mrdocs { //------------------------------------------------ // @@ -51,7 +51,7 @@ class MRDOCS_DECL public: /** Constructor. - A default constructed error is + A default-constructed error is equivalent to success. */ Error() noexcept = default; @@ -251,22 +251,22 @@ class MRDOCS_DECL } }; -} // clang::mrdocs +} // mrdocs template<> -struct std::hash<::clang::mrdocs::Error> +struct std::hash<::mrdocs::Error> { std::size_t operator()( - ::clang::mrdocs::Error const& err) const noexcept + ::mrdocs::Error const& err) const noexcept { return std::hash()(err.message()); } }; template <> -struct std::formatter : std::formatter { +struct std::formatter : std::formatter { template - auto format(clang::mrdocs::Error const &err, FmtContext &ctx) const { + auto format(mrdocs::Error const &err, FmtContext &ctx) const { return std::formatter::format(err.message(), ctx); } }; @@ -279,7 +279,7 @@ struct std::formatter : std::formatter { } }; -namespace clang::mrdocs { +namespace mrdocs { /** A source location with filename prettification. */ diff --git a/include/mrdocs/Support/ExecutorGroup.hpp b/include/mrdocs/Support/ExecutorGroup.hpp index 0e3032693..0ef56bcf7 100644 --- a/include/mrdocs/Support/ExecutorGroup.hpp +++ b/include/mrdocs/Support/ExecutorGroup.hpp @@ -12,15 +12,15 @@ #define MRDOCS_API_SUPPORT_EXECUTORGROUP_HPP #include -#include #include #include +#include #include #include #include #include -namespace clang { + namespace mrdocs { class MRDOCS_DECL @@ -143,6 +143,6 @@ class ExecutorGroup : public ExecutorGroupBase }; } // mrdocs -} // clang + #endif diff --git a/include/mrdocs/Support/Expected.hpp b/include/mrdocs/Support/Expected.hpp index f40f74919..589e1ca78 100644 --- a/include/mrdocs/Support/Expected.hpp +++ b/include/mrdocs/Support/Expected.hpp @@ -12,20 +12,21 @@ #ifndef MRDOCS_API_SUPPORT_EXPECTED_HPP #define MRDOCS_API_SUPPORT_EXPECTED_HPP +#include +#include +#include +#include #include #include #include #include -#include -#include -#include #include #include #include #include #include -namespace clang::mrdocs { +namespace mrdocs { //------------------------------------------------ // @@ -125,13 +126,13 @@ namespace detail constexpr bool isUnexpected> = true; template - using then_result = std::remove_cvref_t>; + using ThenResult = std::remove_cvref_t>; template - using result_transform = std::remove_cv_t>; + using ResultTransform = std::remove_cv_t>; template using result0 = std::remove_cvref_t>; template - using result0_xform = std::remove_cv_t>; + using result0Transform = std::remove_cv_t>; template concept can_beUnexpected = @@ -144,6 +145,11 @@ namespace detail // Tag types for in-place construction from an invocation result. struct in_place_inv { }; struct unexpect_inv { }; + + template + inline constexpr bool ok_bind_ref_v + = std::is_constructible_v + && !reference_constructs_from_temporary_v; } template @@ -477,7 +483,7 @@ class Expected template static constexpr bool same_err - = std::is_same_v; + = std::is_same_v; template friend class Expected; @@ -1091,9 +1097,9 @@ class Expected auto and_then(Fn&& f) & { - using U = detail::then_result; + using U = detail::ThenResult; static_assert(detail::isExpected); - static_assert(std::is_same_v); + static_assert(std::is_same_v); if (has_value()) { @@ -1111,9 +1117,9 @@ class Expected auto and_then(Fn&& f) const & { - using U = detail::then_result; + using U = detail::ThenResult; static_assert(detail::isExpected); - static_assert(std::is_same_v); + static_assert(std::is_same_v); if (has_value()) { @@ -1131,9 +1137,9 @@ class Expected auto and_then(Fn&& f) && { - using U = detail::then_result; + using U = detail::ThenResult; static_assert(detail::isExpected); - static_assert(std::is_same_v); + static_assert(std::is_same_v); if (has_value()) { @@ -1152,9 +1158,9 @@ class Expected auto and_then(Fn&& f) const && { - using U = detail::then_result; + using U = detail::ThenResult; static_assert(detail::isExpected); - static_assert(std::is_same_v); + static_assert(std::is_same_v); if (has_value()) { @@ -1172,9 +1178,9 @@ class Expected auto or_else(Fn&& f) & { - using G = detail::then_result; + using G = detail::ThenResult; static_assert(detail::isExpected); - static_assert(std::is_same_v); + static_assert(std::is_same_v); if (has_value()) { @@ -1192,9 +1198,9 @@ class Expected auto or_else(Fn&& f) const & { - using G = detail::then_result; + using G = detail::ThenResult; static_assert(detail::isExpected); - static_assert(std::is_same_v); + static_assert(std::is_same_v); if (has_value()) { @@ -1212,9 +1218,9 @@ class Expected auto or_else(Fn&& f) && { - using G = detail::then_result; + using G = detail::ThenResult; static_assert(detail::isExpected); - static_assert(std::is_same_v); + static_assert(std::is_same_v); if (has_value()) { @@ -1232,9 +1238,9 @@ class Expected auto or_else(Fn&& f) const && { - using G = detail::then_result; + using G = detail::ThenResult; static_assert(detail::isExpected); - static_assert(std::is_same_v); + static_assert(std::is_same_v); if (has_value()) { @@ -1252,7 +1258,7 @@ class Expected auto transform(Fn&& f) & { - using U = detail::result_transform; + using U = detail::ResultTransform; using Res = Expected; if (has_value()) @@ -1274,7 +1280,7 @@ class Expected auto transform(Fn&& f) const & { - using U = detail::result_transform; + using U = detail::ResultTransform; using Res = Expected; if (has_value()) @@ -1296,7 +1302,7 @@ class Expected auto transform(Fn&& f) && { - using U = detail::result_transform; + using U = detail::ResultTransform; using Res = Expected; if (has_value()) @@ -1318,7 +1324,7 @@ class Expected auto transform(Fn&& f) const && { - using U = detail::result_transform; + using U = detail::ResultTransform; using Res = Expected; if (has_value()) @@ -1340,7 +1346,7 @@ class Expected auto transform_error(Fn&& f) & { - using G = detail::result_transform; + using G = detail::ResultTransform; using Res = Expected; if (has_value()) @@ -1362,7 +1368,7 @@ class Expected auto transform_error(Fn&& f) const & { - using G = detail::result_transform; + using G = detail::ResultTransform; using Res = Expected; if (has_value()) @@ -1384,7 +1390,7 @@ class Expected auto transform_error(Fn&& f) && { - using G = detail::result_transform; + using G = detail::ResultTransform; using Res = Expected; if (has_value()) @@ -1406,7 +1412,7 @@ class Expected auto transform_error(Fn&& f) const && { - using G = detail::result_transform; + using G = detail::ResultTransform; using Res = Expected; if (has_value()) @@ -1576,7 +1582,7 @@ class Expected template static constexpr bool same_err - = std::is_same_v; + = std::is_same_v; template friend class Expected; @@ -1951,7 +1957,7 @@ class Expected { using U = detail::result0; static_assert(detail::isExpected); - static_assert(std::is_same_v); + static_assert(std::is_same_v); if (has_value()) { @@ -1971,7 +1977,7 @@ class Expected { using U = detail::result0; static_assert(detail::isExpected); - static_assert(std::is_same_v); + static_assert(std::is_same_v); if (has_value()) { @@ -1991,7 +1997,7 @@ class Expected { using U = detail::result0; static_assert(detail::isExpected); - static_assert(std::is_same_v); + static_assert(std::is_same_v); if (has_value()) { @@ -2011,7 +2017,7 @@ class Expected { using U = detail::result0; static_assert(detail::isExpected); - static_assert(std::is_same_v); + static_assert(std::is_same_v); if (has_value()) { @@ -2028,9 +2034,9 @@ class Expected auto or_else(Fn&& f) & { - using G = detail::then_result; + using G = detail::ThenResult; static_assert(detail::isExpected); - static_assert(std::is_same_v); + static_assert(std::is_same_v); if (has_value()) { @@ -2047,9 +2053,9 @@ class Expected auto or_else(Fn&& f) const & { - using G = detail::then_result; + using G = detail::ThenResult; static_assert(detail::isExpected); - static_assert(std::is_same_v); + static_assert(std::is_same_v); if (has_value()) { @@ -2066,9 +2072,9 @@ class Expected auto or_else(Fn&& f) && { - using G = detail::then_result; + using G = detail::ThenResult; static_assert(detail::isExpected); - static_assert(std::is_same_v); + static_assert(std::is_same_v); if (has_value()) { @@ -2085,9 +2091,9 @@ class Expected auto or_else(Fn&& f) const&& { - using G = detail::then_result; + using G = detail::ThenResult; static_assert(detail::isExpected); - static_assert(std::is_same_v); + static_assert(std::is_same_v); if (has_value()) { @@ -2105,7 +2111,7 @@ class Expected auto transform(Fn&& f) & { - using U = detail::result0_xform; + using U = detail::result0Transform; using Res = Expected; if (has_value()) @@ -2124,7 +2130,7 @@ class Expected auto transform(Fn&& f) const & { - using U = detail::result0_xform; + using U = detail::result0Transform; using Res = Expected; if (has_value()) @@ -2143,7 +2149,7 @@ class Expected auto transform(Fn&& f) && { - using U = detail::result0_xform; + using U = detail::result0Transform; using Res = Expected; if (has_value()) @@ -2162,7 +2168,7 @@ class Expected auto transform(Fn&& f) const && { - using U = detail::result0_xform; + using U = detail::result0Transform; using Res = Expected; if (has_value()) @@ -2180,7 +2186,7 @@ class Expected auto transform_error(Fn&& f) & { - using G = detail::result_transform; + using G = detail::ResultTransform; using Res = Expected; if (has_value()) @@ -2201,7 +2207,7 @@ class Expected auto transform_error(Fn&& f) const & { - using G = detail::result_transform; + using G = detail::ResultTransform; using Res = Expected; if (has_value()) @@ -2222,7 +2228,7 @@ class Expected auto transform_error(Fn&& f) && { - using G = detail::result_transform; + using G = detail::ResultTransform; using Res = Expected; if (has_value()) @@ -2243,7 +2249,7 @@ class Expected auto transform_error(Fn&& f) const && { - using G = detail::result_transform; + using G = detail::ResultTransform; using Res = Expected; if (has_value()) @@ -2336,6 +2342,939 @@ class Expected { } }; -} // clang::mrdocs +template +class Expected { + static_assert(detail::can_beUnexpected); + + // Storage: either a bound pointer to T, or an error E. + union { + T* p_; + E unex_; + }; + bool has_value_ = false; + + // Short aliases + using R = T&; + template + static constexpr bool ok_bind_v = detail::ok_bind_ref_v; + +public: + using value_type = T&; + using error_type = E; + using unexpected_type = Unexpected; + + template + using rebind = Expected; + + // ---------------------------------- + // ctors + // ---------------------------------- + + // Disengaged by default + constexpr + Expected() noexcept + : p_(nullptr), has_value_(false) {} + + // Success from lvalue: bind + template + requires(!std::is_same_v, Expected> + && !std::is_same_v, std::in_place_t> + && !detail::isUnexpected> && ok_bind_v) + constexpr + explicit(!std::is_convertible_v) + Expected(U& u) noexcept(std::is_nothrow_constructible_v) + : p_(std::addressof(static_cast(u))) + , has_value_(true) + {} + + // Deleted when binding would be from a temporary / disallowed + template + requires(!std::is_same_v, Expected> + && !std::is_same_v, std::in_place_t> + && !detail::isUnexpected> + && !ok_bind_v) + constexpr + Expected(U&&) = delete; + + // In-place: bind to an lvalue argument + template + requires ok_bind_v + constexpr + explicit + Expected(std::in_place_t, U& u) noexcept + : p_(std::addressof(static_cast(u))) + , has_value_(true) + {} + + // In-place via invocation result (mirrors your in_place_inv) + template + explicit + constexpr + Expected(detail::in_place_inv, Fn&& fn) + { + // Expect fn() to yield something bindable to R (i.e. an lvalue of T) + auto&& r = std::forward(fn)(); + static_assert(ok_bind_v); + p_ = std::addressof(static_cast(r)); + has_value_ = true; + } + + // Error ctors (same rules as primary) + template + requires std::is_constructible_v + constexpr + explicit(!std::is_convertible_v) + Expected(Unexpected const& u) + noexcept(std::is_nothrow_constructible_v) + : unex_(u.error()) + , has_value_(false) + {} + + template + requires std::is_constructible_v + constexpr + explicit(!std::is_convertible_v) + Expected(Unexpected&& u) + noexcept(std::is_nothrow_constructible_v) + : unex_(std::move(u).error()) + , has_value_(false) + {} + + // Error via invocation-result (mirrors your unexpect_inv) + template + explicit + constexpr + Expected(detail::unexpect_inv, Fn&& fn) + : unex_(std::forward(fn)()) + , has_value_(false) + {} + + // Converting ctors from other Expected ----------------------- + + // From Expected: safe to bind for any value category + template + requires detail::ok_bind_ref_v + constexpr + explicit(!std::is_convertible_v) + Expected(Expected const& other) + noexcept(std::is_nothrow_constructible_v + && std::is_nothrow_copy_constructible_v) + : has_value_(other.has_value_) + { + if (has_value_) + { + p_ = std::addressof(static_cast(other.value())); + } else + { + std::construct_at(std::addressof(unex_), other.error()); + } + } + + template + requires detail::ok_bind_ref_v + constexpr + explicit(!std::is_convertible_v) + Expected(Expected&& other) + noexcept(std::is_nothrow_constructible_v + && std::is_nothrow_move_constructible_v) + : has_value_(other.has_value_) + { + if (has_value_) + { + p_ = std::addressof(static_cast(other.value())); + } else + { + std::construct_at(std::addressof(unex_), std::move(other).error()); + } + } + + // From Expected (non-ref): only from lvalue object; forbid rvalue + template + requires detail::ok_bind_ref_v + constexpr + explicit(!std::is_convertible_v) + Expected(Expected& other) + noexcept(std::is_nothrow_constructible_v + && std::is_nothrow_copy_constructible_v) + : has_value_(other.has_value()) + { + if (has_value_) + { + p_ = std::addressof(other.value()); + } else + { + std::construct_at(std::addressof(unex_), other.error()); + } + } + + template + constexpr + Expected(Expected&&) = delete; // would dangle + + // Copy/move/dtor + constexpr + Expected(Expected const&) = default; + + constexpr + Expected(Expected&&) = default; + + template + requires std::is_constructible_v + constexpr + explicit + Expected(unexpect_t, Args&&... args) + noexcept(std::is_nothrow_constructible_v) + : unex_(std::forward(args)...) + , has_value_(false) + {} + + template + requires std::is_constructible_v&, Args...> + constexpr + explicit + Expected(unexpect_t, std::initializer_list il, Args&&... args) + noexcept(std::is_nothrow_constructible_v&, Args...>) + : unex_(il, std::forward(args)...) + , has_value_(false) + {} + + constexpr + ~Expected() + { + if (!has_value_) + { + std::destroy_at(std::addressof(unex_)); + } + } + + // ---------------------------------- + // assignment (always rebind) + // ---------------------------------- + + constexpr + Expected& + operator=(Expected const&) = default; + + constexpr + Expected& + operator=(Expected&&) = default; + + // Assign from lvalue -> rebind + template + requires ok_bind_v + constexpr + Expected& + operator=(U& u) + noexcept(std::is_nothrow_constructible_v) + { + if (!has_value_) + { + std::destroy_at(std::addressof(unex_)); + has_value_ = true; + } + p_ = std::addressof(static_cast(u)); + return *this; + } + + // Deleted for temporaries + template + requires(!ok_bind_v) + constexpr + Expected& + operator=(U&&) = delete; + + // Assign from Expected -> rebind or store error + template + requires detail::ok_bind_ref_v + constexpr + Expected& + operator=(Expected const& other) + { + if (other.has_value()) + { + if (!has_value_) + { + std::destroy_at(std::addressof(unex_)); + has_value_ = true; + } + p_ = std::addressof(static_cast(other.value())); + } else + { + if (has_value_) + { + std::construct_at(std::addressof(unex_), other.error()); + has_value_ = false; + } else + { + unex_ = other.error(); + } + } + return *this; + } + + template + requires detail::ok_bind_ref_v + constexpr + Expected& + operator=(Expected&& other) + { + if (other.has_value()) + { + if (!has_value_) + { + std::destroy_at(std::addressof(unex_)); + has_value_ = true; + } + p_ = std::addressof(static_cast(other.value())); + } else + { + if (has_value_) + { + std::construct_at( + std::addressof(unex_), + std::move(other).error()); + has_value_ = false; + } else + { + unex_ = std::move(other).error(); + } + } + return *this; + } + + // Assign from Expected lvalue only (non-ref). Rvalue deleted to avoid + // dangling. + template + requires detail::ok_bind_ref_v + constexpr + Expected& + operator=(Expected& other) + { + if (other.has_value()) + { + if (!has_value_) + { + std::destroy_at(std::addressof(unex_)); + has_value_ = true; + } + p_ = std::addressof(other.value()); + } else + { + if (has_value_) + { + std::construct_at(std::addressof(unex_), other.error()); + has_value_ = false; + } else + { + unex_ = other.error(); + } + } + return *this; + } + + template + constexpr + Expected& + operator=(Expected&&) = delete; + + // Assign error + template + requires std::is_constructible_v + && std::is_assignable_v + constexpr + Expected& + operator=(Unexpected const& e) + { + if (has_value_) + { + std::construct_at(std::addressof(unex_), e.error()); + has_value_ = false; + } else + { + unex_ = e.error(); + } + return *this; + } + + template + requires std::is_constructible_v && std::is_assignable_v + constexpr + Expected& + operator=(Unexpected&& e) + { + if (has_value_) + { + std::construct_at(std::addressof(unex_), std::move(e).error()); + has_value_ = false; + } else + { + unex_ = std::move(e).error(); + } + return *this; + } + + // Emplace: bind to an lvalue + template + requires ok_bind_v + constexpr + T& + emplace(U& u) noexcept + { + if (!has_value_) + { + std::destroy_at(std::addressof(unex_)); + has_value_ = true; + } + p_ = std::addressof(static_cast(u)); + return *p_; + } + + template + requires(!ok_bind_v) + constexpr + T& + emplace(U&&) = delete; + + // swap + constexpr + void + swap(Expected& x) + noexcept( + std::is_nothrow_move_constructible_v + && std::is_nothrow_swappable_v) + requires std::is_swappable_v + { + if (has_value_) + { + if (x.has_value_) + { + using std::swap; + swap(p_, x.p_); + } else + { + // this has value, x has error + E tmp(std::move(x.unex_)); + std::destroy_at(std::addressof(x.unex_)); + x.p_ = p_; + x.has_value_ = true; + + std::construct_at(std::addressof(unex_), std::move(tmp)); + has_value_ = false; + } + } else + { + if (x.has_value_) + { + x.swap(*this); + } else + { + using std::swap; + swap(unex_, x.unex_); + } + } + } + + friend constexpr + void + swap(Expected& a, Expected& b) + noexcept(noexcept(a.swap(b))) + requires requires { a.swap(b); } + { + a.swap(b); + } + + // ---------------------------------- + // observers + // ---------------------------------- + [[nodiscard]] + constexpr + explicit + operator bool() const noexcept + { + return has_value_; + } + + [[nodiscard]] + constexpr + bool + has_value() const noexcept + { + return has_value_; + } + + [[nodiscard]] + constexpr + T* + operator->() noexcept + { + MRDOCS_ASSERT(has_value_); + return p_; + } + + [[nodiscard]] + constexpr + T const* + operator->() const noexcept + { + MRDOCS_ASSERT(has_value_); + return p_; + } + + [[nodiscard]] + constexpr + T& + operator*() & noexcept + { + MRDOCS_ASSERT(has_value_); + return *p_; + } + + [[nodiscard]] + constexpr + T const& + operator*() const& noexcept + { + MRDOCS_ASSERT(has_value_); + return *p_; + } + + [[nodiscard]] + constexpr + T&& + operator*() && noexcept + { + MRDOCS_ASSERT(has_value_); + return std::move(*p_); + } + + [[nodiscard]] + constexpr + T const&& + operator*() const&& noexcept + { + MRDOCS_ASSERT(has_value_); + return std::move(*p_); + } + + constexpr + T& + value() & + { + if (has_value_) + { + return *p_; + } + throw BadExpectedAccess(error()); + } + + constexpr + T const& + value() const& + { + if (has_value_) + { + return *p_; + } + throw BadExpectedAccess(error()); + } + + constexpr + T&& + value() && + { + if (has_value_) + { + return std::move(*p_); + } + throw BadExpectedAccess(std::move(error())); + } + + constexpr + T const&& + value() const&& + { + if (has_value_) + { + return std::move(*p_); + } + throw BadExpectedAccess(std::move(error())); + } + + [[nodiscard]] + constexpr + E& + error() & noexcept + { + MRDOCS_ASSERT(!has_value_); + return unex_; + } + + [[nodiscard]] + constexpr + E const& + error() const& noexcept + { + MRDOCS_ASSERT(!has_value_); + return unex_; + } + + [[nodiscard]] + constexpr + E&& + error() && noexcept + { + MRDOCS_ASSERT(!has_value_); + return std::move(unex_); + } + + [[nodiscard]] + constexpr + E const&& + error() const&& noexcept + { + MRDOCS_ASSERT(!has_value_); + return std::move(unex_); + } + + // value_or: return by value (copy/move), like your non-ref primary + template + constexpr + std::remove_reference_t + value_or(U&& u) const& + { + using Rval = std::remove_reference_t; + static_assert(std::is_copy_constructible_v); + static_assert(std::is_convertible_v); + return has_value_ ? *p_ : static_cast(std::forward(u)); + } + + template + constexpr + std::remove_reference_t + value_or(U&& u) && + { + using Rval = std::remove_reference_t; + static_assert(std::is_move_constructible_v); + static_assert(std::is_convertible_v); + return has_value_ ? std::move(*p_) : + static_cast(std::forward(u)); + } + + // error_or: identical to primary + template + constexpr + E + error_or(G&& g) const& + { + return has_value_ ? static_cast(std::forward(g)) : unex_; + } + + template + constexpr + E + error_or(G&& g) && + { + return has_value_ ? static_cast(std::forward(g)) : + std::move(unex_); + } + + // ---------------------------------- + // monadic ops + // ---------------------------------- + + // and_then: F(T&) -> Expected<*, E> + template + constexpr + auto + and_then(Fn&& f) & + { + using U = std::remove_cvref_t>; + static_assert(detail::isExpected); + static_assert(std::is_same_v); + if (has_value_) + { + return std::invoke(std::forward(f), *p_); + } + return U(unexpect, unex_); + } + + template + constexpr + auto + and_then(Fn&& f) const& + { + using U = std::remove_cvref_t>; + static_assert(detail::isExpected); + static_assert(std::is_same_v); + if (has_value_) + { + return std::invoke(std::forward(f), *p_); + } + return U(unexpect, unex_); + } + + template + constexpr + auto + and_then(Fn&& f) && + { + using U = std::remove_cvref_t>; + static_assert(detail::isExpected); + static_assert(std::is_same_v); + if (has_value_) + { + return std::invoke(std::forward(f), std::move(*p_)); + } + return U(unexpect, std::move(unex_)); + } + + template + constexpr + auto + and_then(Fn&& f) const&& + { + using U = std::remove_cvref_t>; + static_assert(detail::isExpected); + static_assert(std::is_same_v); + if (has_value_) + { + return std::invoke(std::forward(f), std::move(*p_)); + } + return U(unexpect, std::move(unex_)); + } + + // or_else: same signature/behavior as primary; when engaged, return + // self-type with same binding + template + constexpr + Expected + or_else(Fn&& f) & + { + if (has_value_) + { + return *this; + } + auto g = std::forward(f)(unex_); + static_assert( + std::is_same_v, Expected>); + return g; + } + + template + constexpr + Expected + or_else(Fn&& f) const& + { + if (has_value_) + { + return *this; + } + auto g = std::forward(f)(unex_); + static_assert( + std::is_same_v, Expected>); + return g; + } + + template + constexpr + Expected + or_else(Fn&& f) && + { + if (has_value_) + { + return *this; + } + auto g = std::forward(f)(std::move(unex_)); + static_assert( + std::is_same_v, Expected>); + return g; + } + + template + constexpr + Expected + or_else(Fn&& f) const&& + { + if (has_value_) + { + return *this; + } + auto g = std::forward(f)(std::move(unex_)); + static_assert( + std::is_same_v, Expected>); + return g; + } + + // transform: F(T&) -> U ; returns Expected + template + constexpr + auto + transform(Fn&& f) & + { + using U = std::remove_cv_t>; + using Res = Expected; + if (has_value_) + { + return Res(detail::in_place_inv{}, [&] { + return std::invoke(std::forward(f), *p_); + }); + } + return Res(unexpect, unex_); + } + + template + constexpr + auto + transform(Fn&& f) const& + { + using U = std::remove_cv_t>; + using Res = Expected; + if (has_value_) + { + return Res(detail::in_place_inv{}, [&] { + return std::invoke(std::forward(f), *p_); + }); + } + return Res(unexpect, unex_); + } + + template + constexpr + auto + transform(Fn&& f) && + { + using U = std::remove_cv_t>; + using Res = Expected; + if (has_value_) + { + return Res(detail::in_place_inv{}, [&] { + return std::invoke(std::forward(f), std::move(*p_)); + }); + } + return Res(unexpect, std::move(unex_)); + } + + template + constexpr + auto + transform(Fn&& f) const&& + { + using U = std::remove_cv_t>; + using Res = Expected; + if (has_value_) + { + return Res(detail::in_place_inv{}, [&] { + return std::invoke(std::forward(f), std::move(*p_)); + }); + } + return Res(unexpect, std::move(unex_)); + } + + // transform_error: identical to primary + template + constexpr + auto + transform_error(Fn&& f) & + { + using G = std::remove_cv_t>; + using Res = Expected; + if (has_value_) + { + return Res(std::in_place, *p_); + } + return Res(detail::unexpect_inv{}, [&] { + return std::invoke(std::forward(f), unex_); + }); + } + + template + constexpr + auto + transform_error(Fn&& f) const& + { + using G = std::remove_cv_t>; + using Res = Expected; + if (has_value_) + { + return Res(std::in_place, *p_); + } + return Res(detail::unexpect_inv{}, [&] { + return std::invoke(std::forward(f), unex_); + }); + } + + template + constexpr + auto + transform_error(Fn&& f) && + { + using G = std::remove_cv_t>; + using Res = Expected; + if (has_value_) + { + return Res(std::in_place, *p_); + } + return Res(detail::unexpect_inv{}, [&] { + return std::invoke(std::forward(f), std::move(unex_)); + }); + } + + template + constexpr + auto + transform_error(Fn&& f) const&& + { + using G = std::remove_cv_t>; + using Res = Expected; + if (has_value_) + { + return Res(std::in_place, *p_); + } + return Res(detail::unexpect_inv{}, [&] { + return std::invoke(std::forward(f), std::move(unex_)); + }); + } + + template + friend + constexpr + bool + operator==(Expected const& x, Expected const& y) + noexcept(noexcept(bool(*x == *y)) && + noexcept(bool(x.error() == y.error()))) + requires (!std::is_void_v) + { + if (x.has_value()) + { + return y.has_value() && bool(*x == *y); + } else + { + return !y.has_value() && bool(x.error() == y.error()); + } + } + + + template + friend + constexpr + bool + operator==(Expected const& x, U const& v) + noexcept(noexcept(bool(*x == v))) + { + return x.has_value() && bool(*x == v); + } + + template + friend + constexpr + bool + operator==(Expected const& x, Unexpected const& e) + noexcept(noexcept(bool(x.error() == e.error()))) + { + return !x.has_value() && bool(x.error() == e.error()); + } +}; // class Expected + +} // mrdocs #endif diff --git a/include/mrdocs/Support/Glob.hpp b/include/mrdocs/Support/Glob.hpp index 2b02b71a1..333500c04 100644 --- a/include/mrdocs/Support/Glob.hpp +++ b/include/mrdocs/Support/Glob.hpp @@ -8,15 +8,16 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_API_SUPPORT_GLOBPATTERN_HPP -#define MRDOCS_API_SUPPORT_GLOBPATTERN_HPP +#ifndef MRDOCS_API_SUPPORT_GLOB_HPP +#define MRDOCS_API_SUPPORT_GLOB_HPP +#include #include #include #include #include -namespace clang::mrdocs { +namespace mrdocs { /** A glob pattern matcher @@ -45,7 +46,7 @@ class GlobPattern { */ static Expected - create(std::string_view pattern, std::optional maxSubGlobs); + create(std::string_view pattern, Optional maxSubGlobs); static Expected @@ -138,7 +139,7 @@ class PathGlobPattern { Expected create( std::string_view const pattern, - std::optional maxSubGlobs) + Optional maxSubGlobs) { MRDOCS_TRY(auto glob, GlobPattern::create(pattern, maxSubGlobs)); return PathGlobPattern{std::move(glob)}; @@ -242,7 +243,7 @@ class SymbolGlobPattern { Expected create( std::string_view const pattern, - std::optional maxSubGlobs) + Optional maxSubGlobs) { MRDOCS_TRY(auto glob, GlobPattern::create(pattern, maxSubGlobs)); return SymbolGlobPattern{std::move(glob)}; @@ -329,6 +330,6 @@ class SymbolGlobPattern { } }; -} // clang::mrdocs +} // mrdocs -#endif // MRDOCS_API_SUPPORT_GLOBPATTERN_HPP \ No newline at end of file +#endif // MRDOCS_API_SUPPORT_GLOB_HPP \ No newline at end of file diff --git a/include/mrdocs/Support/Handlebars.hpp b/include/mrdocs/Support/Handlebars.hpp index 250bfce04..b53ce81ac 100644 --- a/include/mrdocs/Support/Handlebars.hpp +++ b/include/mrdocs/Support/Handlebars.hpp @@ -8,20 +8,20 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_TOOL_SUPPORT_PATH_HPP -#define MRDOCS_TOOL_SUPPORT_PATH_HPP +#ifndef MRDOCS_API_SUPPORT_HANDLEBARS_HPP +#define MRDOCS_API_SUPPORT_HANDLEBARS_HPP -#include -#include #include #include +#include +#include #include #include #include #include #include -namespace clang { + namespace mrdocs { /** An error thrown or returned by Handlebars @@ -1330,6 +1330,6 @@ year_fn(); } // helpers } // mrdocs -} // clang -#endif + +#endif // MRDOCS_API_SUPPORT_HANDLEBARS_HPP diff --git a/include/mrdocs/Support/JavaScript.hpp b/include/mrdocs/Support/JavaScript.hpp index b2bc036e4..b3391a650 100644 --- a/include/mrdocs/Support/JavaScript.hpp +++ b/include/mrdocs/Support/JavaScript.hpp @@ -15,10 +15,10 @@ #include #include #include -#include #include +#include + -namespace clang { namespace mrdocs { class Handlebars; @@ -716,7 +716,7 @@ class MRDOCS_DECL Value `message` is a string. The mrdocs library function - `clang::mrdocs::report::print` + `mrdocs::report::print` is then called with these two arguments to report a message to the console. @@ -1120,13 +1120,13 @@ isFunction() const noexcept MRDOCS_DECL Expected registerHelper( - clang::mrdocs::Handlebars& hbs, + mrdocs::Handlebars& hbs, std::string_view name, Context& ctx, std::string_view script); } // js } // mrdocs -} // clang + #endif diff --git a/include/mrdocs/Support/Lua.hpp b/include/mrdocs/Support/Lua.hpp index d8587335a..c737dde21 100644 --- a/include/mrdocs/Support/Lua.hpp +++ b/include/mrdocs/Support/Lua.hpp @@ -8,19 +8,19 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_SUPPORT_LUA_HPP -#define MRDOCS_SUPPORT_LUA_HPP +#ifndef MRDOCS_API_SUPPORT_LUA_HPP +#define MRDOCS_API_SUPPORT_LUA_HPP -#include -#include -#include #include +#include #include #include +#include +#include #include #include -namespace clang { + namespace mrdocs { namespace lua { @@ -495,16 +495,16 @@ class Table : public Value } // lua } // mrdocs -} // clang + //------------------------------------------------ template <> -struct std::formatter : std::formatter { +struct std::formatter : std::formatter { template - auto format(clang::mrdocs::lua::Value const &value, FmtContext &ctx) const { + auto format(mrdocs::lua::Value const &value, FmtContext &ctx) const { return std::formatter::format(value.displayString(), ctx); } }; -#endif +#endif // MRDOCS_API_SUPPORT_LUA_HPP diff --git a/include/mrdocs/Support/Parse.hpp b/include/mrdocs/Support/Parse.hpp index c570bc491..a9ad549bd 100644 --- a/include/mrdocs/Support/Parse.hpp +++ b/include/mrdocs/Support/Parse.hpp @@ -14,7 +14,7 @@ #include #include -namespace clang::mrdocs { +namespace mrdocs { /** The result of a parse operation. @@ -145,6 +145,6 @@ parse(std::string_view sv) sv, pos, result.ec.reason())); } -} // clang::mrdocs +} // mrdocs #endif \ No newline at end of file diff --git a/include/mrdocs/Support/Path.hpp b/include/mrdocs/Support/Path.hpp index 1ee42d50c..f60b74f5a 100644 --- a/include/mrdocs/Support/Path.hpp +++ b/include/mrdocs/Support/Path.hpp @@ -16,7 +16,7 @@ #include #include -namespace clang { + namespace mrdocs { //------------------------------------------------ @@ -471,6 +471,6 @@ startsWith( } // files } // mrdocs -} // clang + #endif \ No newline at end of file diff --git a/include/mrdocs/Support/RangeFor.hpp b/include/mrdocs/Support/RangeFor.hpp index 016d697fd..0f7af0762 100644 --- a/include/mrdocs/Support/RangeFor.hpp +++ b/include/mrdocs/Support/RangeFor.hpp @@ -14,7 +14,7 @@ #include -namespace clang { + namespace mrdocs { /** Range to help range-for loops identify first and last. @@ -160,6 +160,6 @@ auto RangeFor::end() const noexcept -> } } // mrdocs -} // clang + #endif \ No newline at end of file diff --git a/include/mrdocs/Support/Report.hpp b/include/mrdocs/Support/Report.hpp index 022686480..8cddc7035 100644 --- a/include/mrdocs/Support/Report.hpp +++ b/include/mrdocs/Support/Report.hpp @@ -12,21 +12,21 @@ #ifndef MRDOCS_API_SUPPORT_REPORT_HPP #define MRDOCS_API_SUPPORT_REPORT_HPP +#include +#include +#include #include #include #include #include #include -#include -#include -#include #include #include #include #include #include -namespace clang::mrdocs::report { +namespace mrdocs::report { /** Severity levels attached to reported messags. */ @@ -184,7 +184,7 @@ log_impl( Level level, Located fs) { - std::string str = std::vformat(fs.value, std::make_format_args()); + std::string str(fs.value); return print(level, str, &fs.where); } } @@ -303,6 +303,6 @@ fatal( return log(Level::fatal, format, std::forward(args)...); } -} // clang::mrdocs +} // mrdocs #endif diff --git a/include/mrdocs/Support/ScopeExit.hpp b/include/mrdocs/Support/ScopeExit.hpp index 04501efc0..3574414e4 100644 --- a/include/mrdocs/Support/ScopeExit.hpp +++ b/include/mrdocs/Support/ScopeExit.hpp @@ -13,7 +13,7 @@ #include -namespace clang::mrdocs { +namespace mrdocs { template class ScopeExit { @@ -44,11 +44,23 @@ class ScopeExitRestore { T& ref_; bool dismissed_{false}; public: + /** Restore `ref` to its previous value when the scope ends + + Store the current value of `ref` and restore it + when this object goes out of scope, unless `dismiss()` + is called. + */ explicit ScopeExitRestore(T& ref) : prev_(ref), ref_(ref) {} + /** Temporarily set `ref` to `next` and restore it when the scope ends + + Store the current value of `ref`, set it to `next`, + and restore the previous value when this object goes + out of scope, unless `dismiss()` is called. + */ template T2> explicit ScopeExitRestore(T& ref, T2 next) diff --git a/include/mrdocs/Support/SplitLines.hpp b/include/mrdocs/Support/SplitLines.hpp new file mode 100644 index 000000000..2c6339923 --- /dev/null +++ b/include/mrdocs/Support/SplitLines.hpp @@ -0,0 +1,208 @@ +// +// 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_API_SUPPORT_SPLITLINES_HPP +#define MRDOCS_API_SUPPORT_SPLITLINES_HPP + +#include +#include +#include +#include +#include +#include + +namespace mrdocs { + +namespace detail { +// Return length (in bytes) of the line-break sequence starting at i, or 0 if +// none +constexpr std::size_t +lbLen(std::string_view s, std::size_t i) noexcept +{ + auto b = [&](std::size_t k) -> unsigned char { + return static_cast(s[k]); + }; + if (i >= s.size()) + { + return 0; + } + + // CRLF + if (s[i] == '\r') + { + if (i + 1 < s.size() && s[i + 1] == '\n') + { + return 2; + } + return 1; // lone CR + } + // LF, VT, FF + if (s[i] == '\n' || s[i] == '\v' || s[i] == '\f') + { + return 1; + } + + // NEL: U+0085 — single byte (0x85) or UTF-8 C2 85 + if (b(i) == 0x85) + { + return 1; + } + if (i + 1 < s.size() && b(i) == 0xC2 && b(i + 1) == 0x85) + { + return 2; + } + + // LS (U+2028) and PS (U+2029): UTF-8 E2 80 A8 / E2 80 A9 + if (i + 2 < s.size() && b(i) == 0xE2 && b(i + 1) == 0x80 + && (b(i + 2) == 0xA8 || b(i + 2) == 0xA9)) + { + return 3; + } + + return 0; +} +} + +// A lazy input range of std::string_view lines split on all known line breaks. +struct SplitLinesView : std::ranges::view_interface { + std::string_view sv_; + + constexpr SplitLinesView() = default; + explicit constexpr SplitLinesView(std::string_view sv) : sv_(sv) {} + + struct Iterator { + std::string_view sv{}; + std::size_t cur = 0; // start of current line + std::size_t nextBreak = 0; // index of current break (or npos) + bool atEnd = false; + + constexpr Iterator() = default; + explicit constexpr Iterator(std::string_view s, bool begin) : sv(s) + { + if (!begin) + { + atEnd = true; + return; + } + nextBreak = findBreak(cur); + } + + constexpr std::size_t + findBreak(std::size_t from) const noexcept + { + for (std::size_t i = from; i < sv.size(); ++i) + { + if (detail::lbLen(sv, i)) + { + return i; + } + } + return std::string_view::npos; + } + + using value_type = std::string_view; + using difference_type = std::ptrdiff_t; + + constexpr value_type + operator*() const noexcept + { + auto end = (nextBreak == std::string_view::npos) ? + sv.size() : + nextBreak; + return sv.substr(cur, end - cur); + } + + constexpr Iterator& + operator++() noexcept + { + if (nextBreak == std::string_view::npos) + { + atEnd = true; + return *this; + } + std::size_t const jump = detail::lbLen(sv, nextBreak); + cur = nextBreak + jump; + nextBreak = findBreak(cur); + if (cur > sv.size()) + { + atEnd = true; + } + return *this; + } + + constexpr void + operator++(int) + { + ++*this; + } + + friend constexpr bool + operator==(Iterator const& it, std::default_sentinel_t) noexcept + { + return it.atEnd; + } + }; + + constexpr Iterator + begin() const noexcept + { + return Iterator{ sv_, true }; + } + constexpr std::default_sentinel_t + end() const noexcept + { + return {}; + } +}; + +// Pipeable range adaptor object: +// - s | text::views::splitLines +// - text::views::splitLines(s) +struct SplitLinesAdaptor { + // Call-style + constexpr auto + operator()(std::string_view sv) const + { + return SplitLinesView{ sv }; + } + + // Accept any contiguous range of (const) char and wrap it as string_view. + template + requires + std::same_as>, char> + constexpr auto + operator()(R&& r) const + { + return SplitLinesView{ + std::string_view(std::ranges::data(r), std::ranges::size(r)) + }; + } + + // Pipe-style + template + requires + std::ranges::contiguous_range && + std::same_as>, char> + friend constexpr auto + operator|(R&& r, SplitLinesAdaptor const& a) + { + return a(std::forward(r)); + } +}; + +/** Split a string view into lines, recognizing all common line breaks + + This is a convenience function for creating a SplitLinesView. + */ +inline constexpr SplitLinesAdaptor splitLines{}; + +} // mrdocs + +#endif diff --git a/include/mrdocs/Support/String.hpp b/include/mrdocs/Support/String.hpp index c43e9b5dd..826257798 100644 --- a/include/mrdocs/Support/String.hpp +++ b/include/mrdocs/Support/String.hpp @@ -16,7 +16,7 @@ #include #include -namespace clang::mrdocs { +namespace mrdocs { /** Return the substring without leading specified characters. @@ -131,6 +131,14 @@ isWhitespace(std::string_view s) noexcept return s.find_first_not_of(" \t\n\v\f\r") == std::string::npos; } +constexpr +bool +isWhitespace(char c) noexcept +{ + return c == ' ' || c == '\t' || c == '\n' || + c == '\v' || c == '\f' || c == '\r'; +} + /** Determine if a string starts with one of the specified characters @param s The string to check. @@ -201,7 +209,7 @@ constexpr char toLowerCase(char const c) noexcept { - return isUpperCase(c) ? c + ('a' - 'A') : c; + return isUpperCase(c) ? static_cast(c - 'A' + 'a') : c; } constexpr @@ -221,7 +229,7 @@ constexpr char toUpperCase(char const c) noexcept { - return isLowerCase(c) ? c - ('a' - 'A') : c; + return isLowerCase(c) ? static_cast(c - 'a' + 'A') : c; } constexpr @@ -431,7 +439,15 @@ toPascalCase(std::string_view const input) return result; } +/** Reindent code by removing the common leading spaces and adding the specified indent. + + @param code The code block to unindent. + @return The modified code block. +*/ +MRDOCS_DECL +std::string +reindentCode(std::string_view code, std::size_t indent = 0); -} // clang::mrdocs +} // mrdocs #endif diff --git a/include/mrdocs/Support/ThreadPool.hpp b/include/mrdocs/Support/ThreadPool.hpp index f679be9e7..4cd651e33 100644 --- a/include/mrdocs/Support/ThreadPool.hpp +++ b/include/mrdocs/Support/ThreadPool.hpp @@ -9,12 +9,12 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_API_SUPPORT_THREAD_HPP -#define MRDOCS_API_SUPPORT_THREAD_HPP +#ifndef MRDOCS_API_SUPPORT_THREADPOOL_HPP +#define MRDOCS_API_SUPPORT_THREADPOOL_HPP #include -#include #include +#include #include #include #include @@ -25,7 +25,7 @@ class StdThreadPool; class ThreadPoolTaskGroup; } // llvm -namespace clang { + namespace mrdocs { class TaskGroup; @@ -186,6 +186,6 @@ forEach( } } // mrdocs -} // clang -#endif + +#endif // MRDOCS_API_SUPPORT_THREADPOOL_HPP diff --git a/include/mrdocs/Support/TypeTraits.hpp b/include/mrdocs/Support/TypeTraits.hpp index 937508189..ba076db25 100644 --- a/include/mrdocs/Support/TypeTraits.hpp +++ b/include/mrdocs/Support/TypeTraits.hpp @@ -9,12 +9,12 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_TOOL_SUPPORT_TYPE_TRAITS_HPP -#define MRDOCS_TOOL_SUPPORT_TYPE_TRAITS_HPP +#ifndef MRDOCS_API_SUPPORT_TYPETRAITS_HPP +#define MRDOCS_API_SUPPORT_TYPETRAITS_HPP #include -namespace clang { + namespace mrdocs { /** Return the value as its underlying type. @@ -122,6 +122,6 @@ using add_cvref_from_t = add_cvref_from::type; } // mrdocs -} // clang -#endif + +#endif // MRDOCS_API_SUPPORT_TYPETRAITS_HPP diff --git a/include/mrdocs/Support/Visitor.hpp b/include/mrdocs/Support/Visitor.hpp index 959300298..7cdc9e5ad 100644 --- a/include/mrdocs/Support/Visitor.hpp +++ b/include/mrdocs/Support/Visitor.hpp @@ -8,14 +8,14 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_TOOL_SUPPORT_VISITOR_HPP -#define MRDOCS_TOOL_SUPPORT_VISITOR_HPP +#ifndef MRDOCS_API_SUPPORT_VISITOR_HPP +#define MRDOCS_API_SUPPORT_VISITOR_HPP #include -#include #include +#include -namespace clang::mrdocs { +namespace mrdocs { /** A visitor for a type @@ -124,6 +124,6 @@ makeVisitor( std::forward(args)...); } -} // clang::mrdocs +} // mrdocs -#endif +#endif // MRDOCS_API_SUPPORT_VISITOR_HPP diff --git a/include/mrdocs/Support/any_callable.hpp b/include/mrdocs/Support/any_callable.hpp index 830656d0f..98698a554 100644 --- a/include/mrdocs/Support/any_callable.hpp +++ b/include/mrdocs/Support/any_callable.hpp @@ -16,7 +16,7 @@ #include #include -namespace clang { + namespace mrdocs { /** A movable, type-erased function object. @@ -73,6 +73,6 @@ class any_callable }; } // mrdocs -} // clang + #endif diff --git a/include/mrdocs/Support/source_location.hpp b/include/mrdocs/Support/source_location.hpp index 7b98535a8..60ee3dc49 100644 --- a/include/mrdocs/Support/source_location.hpp +++ b/include/mrdocs/Support/source_location.hpp @@ -15,21 +15,14 @@ #if __cpp_lib_source_location >= 201907L && \ __has_include() - #include - - namespace clang { - namespace mrdocs { - +# include +namespace mrdocs { using std::source_location; - - } // mrdocs - } // clang +} // mrdocs #else - #include - - namespace clang { - namespace mrdocs { +# include +namespace mrdocs { struct source_location { static @@ -82,8 +75,6 @@ std::uint_least32_t line_ = 0; std::uint_least32_t column_ = 0; }; - } // mrdocs -} // clang #endif #endif diff --git a/include/mrdocs/Support/unlock_guard.hpp b/include/mrdocs/Support/unlock_guard.hpp index 1b5164e3f..15f4af23d 100644 --- a/include/mrdocs/Support/unlock_guard.hpp +++ b/include/mrdocs/Support/unlock_guard.hpp @@ -14,7 +14,7 @@ #include #include -namespace clang { + namespace mrdocs { /** A scoped guard which unlocks a mutex. @@ -42,6 +42,6 @@ class unlock_guard }; } // mrdocs -} // clang + #endif diff --git a/include/mrdocs/Version.hpp.in b/include/mrdocs/Version.hpp.in index ca5d21acc..fa7c95810 100644 --- a/include/mrdocs/Version.hpp.in +++ b/include/mrdocs/Version.hpp.in @@ -14,7 +14,7 @@ #include #include -namespace clang { + namespace mrdocs { /** Project version string in MAJOR.MINOR.PATCH format. @@ -55,6 +55,6 @@ inline constexpr std::string_view project_name = "@PROJECT_NAME@" inline constexpr std::string_view project_description = "@PROJECT_DESCRIPTION@"; } // mrdocs -} // clang + #endif diff --git a/include/mrdocs/mrdocs.natvis b/include/mrdocs/mrdocs.natvis index c9882c627..9a17e3f8c 100644 --- a/include/mrdocs/mrdocs.natvis +++ b/include/mrdocs/mrdocs.natvis @@ -12,53 +12,53 @@ - + - + - (Info*)this,view(noinherit)nd + (Symbol*)this,view(noinherit)nd - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + {Kind,en} {Name} (ID = {id}) - (Info*)this,view(noinherit)nd - (NamespaceInfo*)this,view(noinherit) - (RecordInfo*)this,view(noinherit) - *(FunctionInfo*)this,view(noinherit) - *(OverloadsInfo*)this,view(noinherit) - (EnumInfo*)this,view(noinherit) - (TypedefInfo*)this,view(noinherit) - (VariableInfo*)this,view(noinherit) - (FieldInfo*)this,view(noinherit) - (SpecializationInfo*)this,view(noinherit) - (FriendInfo*)this,view(noinherit) - (EnumConstantInfo*)this,view(noinherit) - (GuideInfo*)this,view(noinherit) - (NamespaceAliasInfo*)this,view(noinherit) - (UsingInfo*)this,view(noinherit) + (Symbol*)this,view(noinherit)nd + (NamespaceSymbol*)this,view(noinherit) + (RecordSymbol*)this,view(noinherit) + *(FunctionSymbol*)this,view(noinherit) + *(OverloadsSymbol*)this,view(noinherit) + (EnumSymbol*)this,view(noinherit) + (TypedefSymbol*)this,view(noinherit) + (VariableSymbol*)this,view(noinherit) + (FieldInfo*)this,view(noinherit) + (SpecializationInfo*)this,view(noinherit) + (FriendInfo*)this,view(noinherit) + (EnumConstantSymbol*)this,view(noinherit) + (GuideSymbol*)this,view(noinherit) + (NamespaceAliasSymbol*)this,view(noinherit) + (UsingSymbol*)this,view(noinherit) - + empty SymbolID global SymbolID {data_[0],nvoXb}{data_[1],nvoXb}{data_[2],nvoXb}{data_[3],nvoXb}{data_[4],nvoXb}{data_[5],nvoXb}{data_[6],nvoXb}{data_[7],nvoXb}{data_[8],nvoXb}{data_[9],nvoXb}{data_[10],nvoXb}{data_[11],nvoXb}{data_[12],nvoXb}{data_[13],nvoXb}{data_[14],nvoXb}{data_[15],nvoXb}{data_[16],nvoXb}{data_[17],nvoXb}{data_[18],nvoXb}{data_[19],nvoXb} @@ -66,7 +66,7 @@ - + {t_} empty @@ -75,11 +75,11 @@ - + {reason_} - + {val_} {unex_} @@ -91,7 +91,7 @@ - + {psz_,s} {((char*)(impl_+1))+1,s} {((char*)(impl_+1))+2,s} @@ -106,11 +106,11 @@ [ doc::String ] - + [ dom::Array ] - + @@ -128,31 +128,31 @@ - - undefined - null - {b_} - {i_} - {str_} - {str_} - {arr_} - {obj_} - {fn_} + + undefined + null + {b_} + {i_} + {str_} + {str_} + {arr_} + {obj_} + {fn_} invalid - b_ - i_ - str_ - str_ - arr_ - obj_ - fn_ + b_ + i_ + str_ + str_ + arr_ + obj_ + fn_ - - + + doc::Block[{_Mypair._Myval2._Myend-_Mypair._Myval2._Myfirst}] @@ -161,7 +161,7 @@ - + doc::Block[{_Mypair._Myval2._Myend-_Mypair._Myval2._Myfirst}] @@ -171,9 +171,9 @@ - + - [ Admonition {(*(clang::mrdocs::doc::Admonition*)this).admonish} ] + [ Admonition {(*(mrdocs::doc::Admonition*)this).admonish} ] [ Brief ] [ Code] [ doc::Heading ] @@ -188,7 +188,7 @@ - + [ doc::Styled ] [ doc::Link ] {string} @@ -198,15 +198,15 @@ - + [ js::Access ] - + [ js::Scope ] - + undefined null {b_} @@ -219,16 +219,16 @@ {obj_} - + duk_value@{idx_} - + {pattern_} - + - + *{ptr_} (empty) diff --git a/mrdocs.rnc b/mrdocs.rnc index f1ba87798..03f131ff9 100644 --- a/mrdocs.rnc +++ b/mrdocs.rnc @@ -35,7 +35,7 @@ grammar ID?, attribute is-inline { "1" }?, attribute is-anonymous { "1" }?, - Javadoc?, + DocComment?, element using-directive { ID } *, Scope* } @@ -56,7 +56,7 @@ grammar ( BaseInfo | Attr | - Javadoc? | + DocComment? | RecordScope ) * ) @@ -92,7 +92,7 @@ grammar ID ?, TypeInfo } * | - Javadoc ? + DocComment ? ) * } @@ -115,7 +115,7 @@ grammar ID ?, TypeInfo } * | - Javadoc ? + DocComment ? ) * } @@ -129,7 +129,7 @@ grammar ID, BaseInfo ?, Location *, - Javadoc ?, + DocComment ?, EnumScope } @@ -142,7 +142,7 @@ grammar ID, attribute initializer { text } ?, Location *, - Javadoc ? + DocComment ? } #--------------------------------------------- @@ -154,7 +154,7 @@ grammar ID, Location *, TypeInfo, - Javadoc ? + DocComment ? } | element typedef { @@ -178,7 +178,7 @@ grammar ( Attr * & TypeInfo & - Javadoc ? + DocComment ? ) } @@ -206,7 +206,7 @@ grammar Access ?, ID, Location *, - Javadoc ?, + DocComment ?, element aliased { attribute name { text }, @@ -222,7 +222,7 @@ grammar Access ?, ID, Location *, - Javadoc ?, + DocComment ?, attribute class { "using" | "using typename" | "using enum" }, attribute qualifier { text } ?, element named { ID } * @@ -237,7 +237,7 @@ grammar Access ?, ID, Location *, - Javadoc ?, + DocComment ?, attribute constraint { text } } @@ -288,7 +288,7 @@ grammar TemplateArg = element targ { - attribute class { "type"|"non-type"|"template" }, + attribute class { "type"|"constant"|"template" }, attribute name { text } ?, ID ?, attribute type { text } ?, @@ -299,7 +299,7 @@ grammar element tparam { attribute name { text } ?, - attribute class { "type"|"non-type"|"template" }, + attribute class { "type"|"constant"|"template" }, attribute type { text } ?, attribute default { text } ?, TemplateParam * @@ -365,7 +365,7 @@ grammar # #--------------------------------------------- - Javadoc = element doc { BlockNode * } + DocComment = element doc { BlockNode * } BlockNode = ( Admonition | Brief | Code | Heading | ListItem | diff --git a/share/gdb/mrdocs_printers.py b/share/gdb/mrdocs_printers.py index 3b25fe066..5e616bc18 100644 --- a/share/gdb/mrdocs_printers.py +++ b/share/gdb/mrdocs_printers.py @@ -114,11 +114,11 @@ def lookup_function(val: gdb.Value): return EnumPrinter(val) typename: str = utils.resolved_typename(val) - if typename == 'clang::mrdocs::dom::Kind': + if typename == 'mrdocs::dom::Kind': return EnumPrinter(val) - if typename == 'clang::mrdocs::dom::Value': + if typename == 'mrdocs::dom::Value': return DomValuePrinter(val) - if typename == 'clang::mrdocs::dom::String': + if typename == 'mrdocs::dom::String': return DomStringPrinter(val) return None diff --git a/share/lldb/clang_ast_formatters.py b/share/lldb/clang_ast_formatters.py index 385bd0c33..ce9a455ab 100644 --- a/share/lldb/clang_ast_formatters.py +++ b/share/lldb/clang_ast_formatters.py @@ -9,7 +9,9 @@ import re -# ---------- tiny helpers ---------- +# ================================================================ +# Small utilities +# ================================================================ def _ctx(v): try: return v.Dereference() if v.TypeIsPointerType() else v @@ -26,25 +28,20 @@ def _eval_str(scope, code): return s.strip('"') if s else None return None -def _strip_ptr_ref(t): # SBType -> SBType (no pointers/refs) +def _strip_ptr_ref(t): + # SBType -> SBType (no pointers/refs) try: - # Drop references if possible + # Drop references first dt = t.GetDereferencedType() - if dt and dt.IsValid(): # only valid for refs + if dt and dt.IsValid(): t = dt - # Drop pointers + # Drop pointers next while t.IsPointerType(): t = t.GetPointeeType() except Exception: pass return t -def _kind_from_type(valobj): - t = _strip_ptr_ref(valobj.GetType()) - name = t.GetName() or "" - m = re.search(r'clang::([A-Za-z_0-9]+)Decl', name) - return m.group(1) if m else "Decl" - def _humanize_kind(kind: str) -> str: # Insert spaces at transitions: a|A, A|A a, digit|A; then lowercase. if not kind: @@ -82,7 +79,9 @@ def _is_derived_from(sbtype: lldb.SBType, qualified_basename: str) -> bool: return False -# ---------- recognizers ---------- +# ================================================================ +# Decl recognizers & summaries +# ================================================================ def is_clang_nameddecl(sbtype, _dict): t = _strip_ptr_ref(sbtype) n = t.GetName() or "" @@ -98,14 +97,22 @@ def is_clang_decl_not_named(sbtype, _dict): # Must be a Decl, but NOT a NamedDecl return _is_derived_from(t, "clang::Decl") and not _is_derived_from(t, "clang::NamedDecl") +def _kind_from_type(valobj): + t = _strip_ptr_ref(valobj.GetType()) + name = t.GetName() or "" + m = re.search(r'clang::([A-Za-z_0-9]+)Decl', name) + return m.group(1) if m else "Decl" + +# ================================================================ +# Decl Summaries +# ================================================================ -# ---------- summaries ---------- # NamedDecl (qualified name + humanized kind) def NamedDecl_summary(valobj, _dict): o = _ctx(valobj) # Qualified name first - q = _eval_str(o, "static_cast(this)->getQualifiedNameAsString()") + q = _eval_str(o, "static_cast(this)->getNameAsString()") if not q: q = _eval_str(o, "static_cast(this)->getNameAsString()") @@ -125,7 +132,6 @@ def NamedDecl_summary(valobj, _dict): return f"{q} ({hkind})" if q else f" ({hkind})" - # Non-NamedDecl (unnamed) → " (humanized kind)" def UnnamedDecl_summary(valobj, _dict): o = _ctx(valobj) @@ -133,18 +139,120 @@ def UnnamedDecl_summary(valobj, _dict): hkind = _humanize_kind(kind) return f" ({hkind})" +# ================================================================ +# Comment recognizer & frame-aware summary (actual source text) +# ================================================================ +def is_clang_comment(sbtype, _dict): + try: + n = _strip_ptr_ref(sbtype).GetName() or "" + return n.startswith("clang::comments::") + except Exception: + return False +def _frame(): + try: + tgt = lldb.debugger.GetSelectedTarget() + if not tgt: return None + proc = tgt.GetProcess() + if not proc: return None + thr = proc.GetSelectedThread() + if not thr: return None + return thr.GetSelectedFrame() + except Exception: + return None + +def _frame_eval_str(code: str): + f = _frame() + if not f: + return None + opts = lldb.SBExpressionOptions() + opts.SetLanguage(lldb.eLanguageTypeC_plus_plus) + opts.SetCoerceResultToId(False) + r = f.EvaluateExpression(code, opts) + if r and r.GetError() and r.GetError().Success(): + s = r.GetSummary() or r.GetValue() + return s.strip('"') if s else None + return None + +def _comments_ptr_expr(valobj): + try: + addr = valobj.GetValueAsUnsigned(0) + if addr: + return f"(const ::clang::comments::Comment*)0x{addr:x}" + except Exception: + pass + return None + +def _astctx_candidates_from_frame(): + # Order these to match your codebase first + return [ + "(&this->ctx_)", # common in visitors + "(&ctx_)", + "(&Ctx)", + "(&context)", + "(&Context)", + "(&ASTCtx)", + ] + +def _shorten_one_line(s: str, maxlen=160) -> str: + if not s: + return None + s = " ".join(s.replace("\r", "\n").split()) + return (s[: maxlen - 3] + "...") if len(s) > maxlen else s + +def Comment_summary(valobj, _dict): + """ + Show the exact source slice of the comment (single line), if we can obtain + an ASTContext from the current frame. Otherwise, fall back to a type label. + """ + cptr = _comments_ptr_expr(valobj) + if cptr: + text_tmpl = ( + "{ " + f" auto C = {cptr};" + " ::clang::SourceRange R = C->getSourceRange();" + " const ::clang::ASTContext* ACTX = ASTCTX_EXPR;" + " if (!ACTX) return std::string();" + " const auto& SM = ACTX->getSourceManager();" + " return std::string(::clang::Lexer::getSourceText(" + " ::clang::CharSourceRange::getTokenRange(R), SM, ACTX->getLangOpts()));" + "}" + ) + for cand in _astctx_candidates_from_frame(): + code = text_tmpl.replace("ASTCTX_EXPR", cand) + s = _frame_eval_str(code) + if s: + one = _shorten_one_line(s) + if one: + return one # just the text; no extra suffix + + # Fallback: readable type label (keeps something useful when ACTX is unreachable) + tname = (_strip_ptr_ref(valobj.GetType()).GetName() or "").rsplit("::", 1)[-1] + base = _humanize_kind(tname.replace("Comment", "")) or "comment" + return f"{base} (comment)" + + +# ================================================================ +# Registration +# ================================================================ def __lldb_init_module(debugger, _dict): - # One rule for ALL NamedDecl-derived types + # Decl summaries debugger.HandleCommand( 'type summary add ' '--python-function clang_ast_formatters.NamedDecl_summary ' '--recognizer-function clang_ast_formatters.is_clang_nameddecl' ) - # And a second rule for other Decl types (non-NamedDecl) debugger.HandleCommand( 'type summary add ' '--python-function clang_ast_formatters.UnnamedDecl_summary ' '--recognizer-function clang_ast_formatters.is_clang_decl_not_named' ) - print("[clang-ast] NamedDecl and Unnamed Decl summaries registered via recognizers.") + + # Comments summary (no synthetic; members remain visible) + debugger.HandleCommand( + 'type summary add ' + '--python-function clang_ast_formatters.Comment_summary ' + '--recognizer-function clang_ast_formatters.is_clang_comment' + ) + + print("[clang-ast] Decl summaries + comments source-text summaries registered.") diff --git a/share/lldb/mrdocs_formatters.py b/share/lldb/mrdocs_formatters.py index c9144ff27..ae68441af 100644 --- a/share/lldb/mrdocs_formatters.py +++ b/share/lldb/mrdocs_formatters.py @@ -5,18 +5,16 @@ # https://www.boost.org/LICENSE_1_0.txt # +from __future__ import annotations import lldb +import re -# ---------- SIMPLE, WORKING REGEXES ---------- -# Tip: register BOTH const and non-const; avoid fancy groups. -_INFO_REGEXES = [ - r"^clang::mrdocs::[^:]*Info$", - r"^const clang::mrdocs::[^:]*Info$", -] +# ============================================================================= +# Core utilities (shared by all providers) +# ============================================================================= - -# ---------- POINTER/REF SAFE ---------- def _deref_if_ptr_or_ref(v: lldb.SBValue) -> lldb.SBValue: + """Return v dereferenced if it is a pointer/reference and deref succeeds; else v unchanged.""" t = v.GetType() if not t or not t.IsValid(): return v @@ -26,37 +24,36 @@ def _deref_if_ptr_or_ref(v: lldb.SBValue) -> lldb.SBValue: return v -# ---------- TYPE INTROSPECTION HELPERS ---------- def _type_name(t: lldb.SBType) -> str: + """Best-effort type name ('' if unavailable).""" return (t.GetName() or "") if (t and t.IsValid()) else "" def _get_type_fields(t: lldb.SBType): + """Iterate SBTypeMember for non-virtual data members/bases (robust to exceptions).""" try: n = t.GetNumberOfFields() except Exception: n = 0 for i in range(n): - yield t.GetFieldAtIndex(i) # SBTypeMember + yield t.GetFieldAtIndex(i) def _get_direct_bases(t: lldb.SBType): + """Iterate SBTypeMember for direct base classes (robust to exceptions).""" try: n = t.GetNumberOfDirectBaseClasses() except Exception: n = 0 for i in range(n): - yield t.GetDirectBaseClassAtIndex(i) # SBTypeMember + yield t.GetDirectBaseClassAtIndex(i) def _base_value_from_typemember(parent_v: lldb.SBValue, base_tm: lldb.SBTypeMember) -> lldb.SBValue | None: - """ - Robustly get the base subobject SBValue from the parent, using the base type and byte offset. - """ + """Materialize a direct base subobject by offset. Returns None on failure.""" bt = base_tm.GetType() if not bt or not bt.IsValid(): return None - # Offset is in bits in some LLDBs; prefer GetOffsetInBytes() if available. try: off = base_tm.GetOffsetInBytes() except Exception: @@ -66,11 +63,149 @@ def _base_value_from_typemember(parent_v: lldb.SBValue, base_tm: lldb.SBTypeMemb return child if child and child.IsValid() else None -# ---------- STRING EXTRACTION (std::string only, per your note) ---------- -def _string_from_std_string(sbv: lldb.SBValue) -> str | None: +def _get_field_value_by_offset(parent_v: lldb.SBValue, field_name: str) -> lldb.SBValue | None: + """Materialize a named field via type layout offsets (preferred), else try direct lookup.""" + t = parent_v.GetType() + if t and t.IsValid(): + try: + n = t.GetNumberOfFields() + except Exception: + n = 0 + for i in range(n): + tm = t.GetFieldAtIndex(i) + try: + if tm.IsBaseClass(): + continue + except Exception: + pass + if (tm.GetName() or "") == field_name: + ftype = tm.GetType() + if not (ftype and ftype.IsValid()): + break + try: + off = tm.GetOffsetInBytes() + except Exception: + off_bits = tm.GetOffsetInBits() if hasattr(tm, "GetOffsetInBits") else 0 + off = off_bits // 8 + v = parent_v.CreateChildAtOffset(field_name, off, ftype) + return v if v and v.IsValid() else None + v = parent_v.GetChildMemberWithName(field_name) + return v if v and v.IsValid() else None + + +def _clean(s: str | None) -> str | None: + """Strip/normalize a string value; filter out LLDB 'error:' summaries.""" + if not s: + return None + s = s.strip() + if s.startswith("error:"): + return None + return s + + +def _fmt_ptr_addr(p: lldb.SBValue) -> str: + """Return pointer as 0x with no leading zeros (LLDB style).""" + try: + v = p.GetValueAsUnsigned(0) + return f"0x{v:x}" + except Exception: + s = (p.GetValue() or "").strip() + if s.startswith(("0x", "0X")): + h = s[2:].lstrip("0") + return "0x" + (h if h else "0") + return s or "0x0" + + +# ============================================================================= +# Child lookup helpers (work across synthetic/non-synthetic) +# ============================================================================= + +def _strip_prefixes_for_match(nm: str) -> str: + """Strip ordering prefixes ('NN '), optional decl prefixes ('T::'), and de-dupe suffixes (' #k').""" + if not nm: + return nm + nm = re.sub(r"^\d{2}\s+", "", nm) + if "::" in nm: + nm = nm.split("::", 1)[1] + nm = re.sub(r"\s+#\d+$", "", nm) + return nm + + +def _child_by_name_any(v: lldb.SBValue, names: tuple[str, ...]) -> lldb.SBValue | None: + """Return first child whose name matches any in `names`, searching both synthetic and raw views.""" + if not v or not v.IsValid(): + return None + views = [v] + try: + ns = v.GetNonSyntheticValue() + if ns and ns.IsValid() and ns.GetID() != v.GetID(): + views.append(ns) + except Exception: + pass + # direct lookup + for view in views: + for nm in names: + ch = view.GetChildMemberWithName(nm) + if ch and ch.IsValid(): + return ch + # enumerated children (names may be prefixed by a synthetic) + for view in views: + n = view.GetNumChildren() + for i in range(n): + ch = view.GetChildAtIndex(i) + if not (ch and ch.IsValid()): + continue + nm = _strip_prefixes_for_match(ch.GetName() or "") + if nm in names: + return ch + return None + + +def _find_field_recursive_any(v: lldb.SBValue, names: tuple[str, ...]) -> lldb.SBValue | None: + """Like _child_by_name_any, but recurses into direct bases using type offsets.""" + if not v or not v.IsValid(): + return None + got = _child_by_name_any(v, names) + if got and got.IsValid(): + return got + t = v.GetType() + if t and t.IsValid(): + # search own fields by offset + for tm in _get_type_fields(t): + try: + if tm.IsBaseClass(): + continue + except Exception: + pass + if (tm.GetName() or "") in names: + ft = tm.GetType() + if ft and ft.IsValid(): + try: + off = tm.GetOffsetInBytes() + except Exception: + off_bits = tm.GetOffsetInBits() if hasattr(tm, "GetOffsetInBits") else 0 + off = off_bits // 8 + ch = v.CreateChildAtOffset(tm.GetName() or "", off, ft) + if ch and ch.IsValid(): + return ch + # recurse bases + for btm in _get_direct_bases(t): + base_v = _base_value_from_typemember(v, btm) + if base_v and base_v.IsValid(): + got = _find_field_recursive_any(base_v, names) + if got and got.IsValid(): + return got + return None + + +# ============================================================================= +# std::string helpers (simple/robust) +# ============================================================================= + +def _str_from_std_string(sbv: lldb.SBValue) -> str | None: + """Extract std::string contents leveraging LLDB’s own summary when available.""" if not sbv or not sbv.IsValid(): return None - # LLDB usually gives a quoted summary for std::string s = sbv.GetSummary() or sbv.GetObjectDescription() or sbv.GetValue() if not s: return None @@ -80,11 +215,13 @@ def _string_from_std_string(sbv: lldb.SBValue) -> str | None: return s -# ---------- FIND Name IN SELF OR BASES (std::string only) ---------- -_CANDIDATES = ("Name", "name", "Name_", "name_") # add others if your field is different - +# ============================================================================= +# Symbol-like: summary + synthetic +# ============================================================================= def _find_std_string_name(v: lldb.SBValue) -> lldb.SBValue | None: + """Find a std::string-ish member named 'Name' (or common variants) on this object or its bases.""" + candidates = ("Name", "name", "Name_", "name_") v = _deref_if_ptr_or_ref(v) if not v or not v.IsValid(): return None @@ -92,7 +229,7 @@ def _find_std_string_name(v: lldb.SBValue) -> lldb.SBValue | None: if not t or not t.IsValid(): return None - # 1) own data members + # direct members first for tm in _get_type_fields(t): try: if tm.IsBaseClass(): @@ -100,76 +237,40 @@ def _find_std_string_name(v: lldb.SBValue) -> lldb.SBValue | None: except Exception: pass fname = tm.GetName() or "" - if fname not in _CANDIDATES: + if fname not in candidates: continue child = v.GetChildMemberWithName(fname) - if child and child.IsValid(): - # rely on LLDB’s std::string summary (no need to check its exact SBType) - if _string_from_std_string(child) is not None: - return child + if child and child.IsValid() and _str_from_std_string(child) is not None: + return child - # 2) direct bases (RELIABLE path using offset) + # then bases for btm in _get_direct_bases(t): base_v = _base_value_from_typemember(v, btm) if base_v: - got = _find_std_string_name(base_v) # recurse + got = _find_std_string_name(base_v) if got: return got return None -# ---------- SUMMARY ---------- -def InfoLikeSummaryProvider(valobj, _dict): +def SymbolLikeSummaryProvider(valobj, _dict): + """Summary for Symbol-like types: show name or ''.""" try: name_val = _find_std_string_name(valobj) if not name_val: return "" - s = _string_from_std_string(name_val) + s = _str_from_std_string(name_val) return s if s else "" except Exception as e: return f"" -# -------- SymbolID summary -------- - -# Optional: set to True if you prefer uppercase hex -_SYMBOLID_HEX_UPPER = False - - -def _get_field_value_by_offset(parent_v: lldb.SBValue, field_name: str) -> lldb.SBValue | None: - """Create the SBValue for a named field using its exact offset+type (robust across toolchains).""" - t = parent_v.GetType() - if not t or not t.IsValid(): - return None - # First try: exact match via SBTypeMember - try: - n = t.GetNumberOfFields() - except Exception: - n = 0 - for i in range(n): - tm = t.GetFieldAtIndex(i) - try: - if tm.IsBaseClass(): - continue - except Exception: - pass - if (tm.GetName() or "") == field_name: - ftype = tm.GetType() - if not ftype or not ftype.IsValid(): - break - try: - off = tm.GetOffsetInBytes() - except Exception: - off_bits = tm.GetOffsetInBits() if hasattr(tm, "GetOffsetInBits") else 0 - off = off_bits // 8 - v = parent_v.CreateChildAtOffset(field_name, off, ftype) - return v if v and v.IsValid() else None - # Fallback: if toolchain exposes it directly - v = parent_v.GetChildMemberWithName(field_name) - return v if v and v.IsValid() else None +# ---- SymbolID (fixed size blob → hex) --------------------------------------- +_SYMBOLID_HEX_UPPER = False # set True for uppercase hex def _read_process_bytes(valobj: lldb.SBValue, addr: int, n: int) -> bytes | None: + """Read n bytes from process memory at addr; None on failure.""" if addr == 0 or n <= 0: return None proc = valobj.GetProcess() @@ -181,6 +282,7 @@ def _read_process_bytes(valobj: lldb.SBValue, addr: int, n: int) -> bytes | None def _extract_symbolid_bytes(sym: lldb.SBValue, expected_len: int = 20) -> list[int] | None: + """Extract byte array from SymbolID::data_ either via children or raw memory.""" sym = _deref_if_ptr_or_ref(sym) if not sym or not sym.IsValid(): return None @@ -188,7 +290,7 @@ def _extract_symbolid_bytes(sym: lldb.SBValue, expected_len: int = 20) -> list[i if not data_field or not data_field.IsValid(): return None - # Route A: child iteration (if LLDB exposes array elements) + # A: array children n = data_field.GetNumChildren() if n >= expected_len: out = [] @@ -201,18 +303,18 @@ def _extract_symbolid_bytes(sym: lldb.SBValue, expected_len: int = 20) -> list[i if len(out) == expected_len: return out - # Route B: direct memory read from the array address + # B: direct memory addr_v = data_field.AddressOf() if addr_v and addr_v.IsValid(): addr = addr_v.GetValueAsUnsigned() raw = _read_process_bytes(sym, addr, expected_len) if raw is not None: return [b for b in raw] - return None def SymbolIDSummaryProvider(valobj, _dict): + """Render SymbolID as hex; special cases: all-FF → , all-00 → std::nullopt.""" try: bs = _extract_symbolid_bytes(valobj, 20) if not bs: @@ -221,23 +323,20 @@ def SymbolIDSummaryProvider(valobj, _dict): return "" if all(b == 0x00 for b in bs): return "std::nullopt" - if _SYMBOLID_HEX_UPPER: - return "".join(f"{b:02X}" for b in bs) - else: - return "".join(f"{b:02x}" for b in bs) + fmt = (lambda b: f"{b:02X}") if _SYMBOLID_HEX_UPPER else (lambda b: f"{b:02x}") + return "".join(fmt(b) for b in bs) except Exception as e: return f"" -# ---------- SYNTHETIC: FLATTEN BASES ONLY ---------- -# --- knobs (same as before) --- -_PREFIX_ORDER = True # add "NN " so alphabetical sort preserves order -_INCLUDE_DECL_TYPE = True # include "DeclType::" before the field name +# ---- Symbol-like synthetic: flatten bases (stable order) --------------------- +_PREFIX_ORDER = True # prefix children with "NN" to stabilize sort order +_INCLUDE_DECL_TYPE = True # include "DeclType::" before member name def _norm_typename(t: lldb.SBType) -> str: + """Strip common C++ qualifiers and keywords from type name.""" n = _type_name(t) - # strip common prefixes/qualifiers for p in ("class ", "struct ", "const ", "volatile "): if n.startswith(p): n = n[len(p):] @@ -245,14 +344,14 @@ def _norm_typename(t: lldb.SBType) -> str: def _is_info_type(t: lldb.SBType) -> bool: - return _norm_typename(t) == "clang::mrdocs::Info" + """Identify the 'mrdocs::Symbol' base to show it first.""" + return _norm_typename(t) == "mrdocs::Symbol" -class InfoLikeSyntheticProvider: +class SymbolLikeSyntheticProvider: """ - Show fields from clang::mrdocs::Info FIRST, - then all other bases deepest-first, then most-derived. - Never recurses into fields. Uses order prefixes to beat UI resorting. + Show fields from mrdocs::Symbol FIRST, then other bases (deepest→most-derived), then own fields. + Does not recurse into sub-fields. """ def __init__(self, valobj, _dict): @@ -261,6 +360,7 @@ def __init__(self, valobj, _dict): self.update() def _append_own_fields(self, v: lldb.SBValue, seen: set[str], rank: int, decl: str): + """Append this node's data members with stable, prefixed display names.""" t = v.GetType() if not t or not t.IsValid(): return @@ -277,17 +377,12 @@ def _append_own_fields(self, v: lldb.SBValue, seen: set[str], rank: int, decl: s if not child or not child.IsValid(): continue - # Build display name with stable, sortable prefix parts = [] if _PREFIX_ORDER: parts.append(f"{rank:02d}") - if _INCLUDE_DECL_TYPE and decl: - parts.append(f"{decl}::{fname}") - else: - parts.append(fname) + parts.append(f"{decl}::{fname}" if (_INCLUDE_DECL_TYPE and decl) else fname) name = " ".join(parts) - # Dedup if collisions happen base = name k = 2 while name in seen: @@ -302,9 +397,7 @@ def _append_own_fields(self, v: lldb.SBValue, seen: set[str], rank: int, decl: s self.children.append(child) def _linearize_bases_postorder(self, v: lldb.SBValue): - """ - Returns nodes in post-order: [deepest bases ..., most-derived (v)]. - """ + """Return nodes in post-order: [deepest bases …, most-derived (v)].""" order = [] def dfs(node: lldb.SBValue): @@ -321,27 +414,22 @@ def dfs(node: lldb.SBValue): return order def update(self): + """Rebuild flat child list: Symbol base(s) first, then others, then self.""" self.children = [] v = self.root if not v or not v.IsValid(): return seen = set() - order = self._linearize_bases_postorder(v) # deepest … -> most-derived + order = self._linearize_bases_postorder(v) if order: - # Partition: Info nodes first, others after (preserving their relative order) info_nodes = [n for n in order if _is_info_type(n.GetType())] other_nodes = [n for n in order if not _is_info_type(n.GetType())] - rank = 0 - for node in info_nodes: - self._append_own_fields(node, seen, rank, _norm_typename(node.GetType())) - rank += 1 - for node in other_nodes: + for node in info_nodes + other_nodes: self._append_own_fields(node, seen, rank, _norm_typename(node.GetType())) rank += 1 - # Fallback: nothing discovered → show immediate children (no field recursion) if not self.children: n = v.GetNumChildren() for i in range(n): @@ -350,15 +438,9 @@ def update(self): self.children.append(c) # required API - def has_children(self): - return len(self.children) > 0 - - def num_children(self): - return len(self.children) - - def get_child_at_index(self, idx): - return self.children[idx] if 0 <= idx < len(self.children) else None - + def has_children(self): return len(self.children) > 0 + def num_children(self): return len(self.children) + def get_child_at_index(self, idx): return self.children[idx] if 0 <= idx < len(self.children) else None def get_child_index(self, name): for i, c in enumerate(self.children): if c.GetName() == name: @@ -366,98 +448,247 @@ def get_child_index(self, name): return -1 -# -------- Optional: transparent summary + children (fixed) -------- +# ============================================================================= +# Optional: summary + synthetic (handles libc++/libstdc++) +# ============================================================================= -def _optional_inner(valobj): - v = _deref_if_ptr_or_ref(valobj) - if not v or not v.IsValid(): +def _is_std_optional_typename(tyname: str | None) -> bool: + """Heuristic: does the name look like a std::optional specialization (any vendor).""" + if not tyname: + return False + tn = tyname.replace("class ", "").replace("struct ", "").replace("const ", "").replace("volatile ", "") + return ("optional<" in tn and "std::" in tn) or "__optional" in tn or "::optional<" in tn + + +# Known payload/flag spellings across libstdc++/libc++ +_OPT_PAYLOAD_NAMES = ("__val__", "__val_", "_M_value", "_M_payload", "__value_") +_OPT_ENGAGED_NAMES = ("__engaged_", "_M_engaged", "_M_has_value", "__has_value_") + +def _nonsynth(vo): + """Prefer non-synthetic view (raw layout) when available.""" + try: + ns = vo.GetNonSyntheticValue() + return ns if (ns and ns.IsValid()) else vo + except Exception: + return vo + +def _value_from_typemember(parent_v, tm): + """Materialize a field from its SBTypeMember (offset-aware).""" + ft = tm.GetType() + if not (ft and ft.IsValid()): return None - inner = _get_field_value_by_offset(v, "t_") or v.GetChildMemberWithName("t_") - return inner if inner and inner.IsValid() else None + try: + off = tm.GetOffsetInBytes() + except Exception: + off_bits = tm.GetOffsetInBits() if hasattr(tm, "GetOffsetInBits") else 0 + off = off_bits // 8 + ch = parent_v.CreateChildAtOffset(tm.GetName() or "", off, ft) + return ch if (ch and ch.IsValid()) else None +def _find_optional_payload(s): + """ + From std::optional storage 's' (non-synthetic), return (payload_vo, engaged_bool_or_None). + Works across libc++/libstdc++ raw layouts by scanning fields/bases. + """ + s = _nonsynth(s) + if not (s and s.IsValid()): + return (None, None) -def _clean(s: str | None) -> str | None: - if not s: + t = s.GetType() + if t and t.IsValid(): + engaged = None + # own fields + for tm in _get_type_fields(t): + nm = tm.GetName() or "" + if nm in _OPT_ENGAGED_NAMES: + flag = _value_from_typemember(s, tm) + if flag and flag.IsValid(): + try: + engaged = flag.GetValueAsUnsigned(0) != 0 + except Exception: + engaged = None + if nm in _OPT_PAYLOAD_NAMES: + val = _value_from_typemember(s, tm) + if val and val.IsValid(): + return (val, engaged) + # bases + for btm in _get_direct_bases(t): + base_v = _base_value_from_typemember(s, btm) + if base_v and base_v.IsValid(): + val, eng = _find_optional_payload(base_v) + if val: + return (val, engaged if engaged is not None else eng) + + # last resort: direct child lookup by name + for nm in _OPT_PAYLOAD_NAMES: + ch = s.GetChildMemberWithName(nm) + if ch and ch.IsValid(): + return (ch, None) + + return (None, None) + +def _resolve_inner(valobj): + """ + Resolve the contained value of mrdocs::Optional. + Returns: + - SBValue of T (or T* for Optional), or + - None if disengaged/unavailable. + """ + vo = _deref_if_ptr_or_ref(valobj) + if not (vo and vo.IsValid()): return None - s = s.strip() - if s.startswith("error:"): + + # Optional path: p_ is a T* + p = _get_field_value_by_offset(vo, "p_") or vo.GetChildMemberWithName("p_") + if p is not None and p.IsValid(): + try: + if p.GetValueAsUnsigned(0) == 0: + return None + except Exception: + pv = p.GetValue() + if not pv or pv in ("0", "(nullptr)", "NULL"): + return None + deref = p.Dereference() + return deref if (deref and deref.IsValid()) else None + + # Optional path: s_ is inline T or std::optional + s = _get_field_value_by_offset(vo, "s_") or vo.GetChildMemberWithName("s_") + if not (s and s.IsValid()): + return None + + # A) Friendly synthetic with a 'Value' child + try: + n = s.GetNumChildren() + except Exception: + n = 0 + if n > 0: + for i in range(n): + ch = s.GetChildAtIndex(i) + if ch and ch.IsValid() and (ch.GetName() or "") == "Value": + return ch + # don't return s here: synthetic may be libc++ internals; fall through + + # B) Raw layout: find payload (__val_ / _M_value) and engaged flag + payload, engaged = _find_optional_payload(s) + if engaged is False: return None + if payload and payload.IsValid(): + return payload + + # C) Nullable-traits inline T: s_ is the value return s def OptionalSummaryProvider(valobj, _dict): + """ + Summary for mrdocs::Optional. + - Disengaged: 'nullopt' + - T* : '0xaddr[ → ]' + - Scalars : value + - Strings : string (no quotes) + - Structs : '' (empty summary; children show content) + """ try: - inner = _optional_inner(valobj) - if not inner: - return "" + inner = _resolve_inner(valobj) + if inner is None: + return "nullopt" + + # T* : compact address; optional hint with short pointee summary/value + t = inner.GetType() + if t and t.IsValid() and t.IsPointerType(): + addr = _fmt_ptr_addr(inner) + hint = "" + try: + dv = inner.Dereference() + if dv and dv.IsValid(): + hs = _clean(dv.GetSummary()) or _clean(dv.GetValue()) + if hs: + hint = f" → {hs}" + except Exception: + pass + return f"{addr}{hint}" - # 1) Prefer T's own summary (works with your other formatters) + # Scalars/strings s = _clean(inner.GetSummary()) - if s is not None: + if s not in (None, ""): return s - - # 2) Safe fallbacks without type introspection v = _clean(inner.GetValue()) - if v is not None: + if v not in (None, ""): return v + # Structured types: quiet summary; show children + try: + if inner.GetNumChildren() > 0 or inner.MightHaveChildren(): + return "" + except Exception: + pass + d = _clean(inner.GetObjectDescription()) - if d is not None: + if d not in (None, ""): return d - - return "" + return "" except Exception as e: return f"" class OptionalTransparentSyntheticProvider: """ - Present Optional as if it were T: forward children to t_. + Synthetic for mrdocs::Optional that forwards to the contained value. + - For T* the children are those of *T (summary still shows the pointer). """ def __init__(self, valobj, _dict): self.valobj = valobj - self.inner = None + self.inner = None # SBValue of T or T* + self.children_root = None # SBValue used to enumerate children (T or *T) self.update() def update(self): - self.inner = _optional_inner(self.valobj) + """Recompute forward target and children root (dereference T* if needed).""" + self.inner = _resolve_inner(self.valobj) + self.children_root = self.inner + if self.inner and self.inner.IsValid(): + t = self.inner.GetType() + if t and t.IsValid() and t.IsPointerType(): + try: + deref = self.inner.Dereference() + if deref and deref.IsValid(): + self.children_root = deref + except Exception: + pass def has_children(self): - return bool(self.inner) and (self.inner.GetNumChildren() > 0 or self.inner.MightHaveChildren()) + r = self.children_root + return bool(r) and (r.GetNumChildren() > 0 or r.MightHaveChildren()) def num_children(self): - return self.inner.GetNumChildren() if self.inner else 0 + r = self.children_root + return r.GetNumChildren() if r else 0 def get_child_at_index(self, i): - if not self.inner: return None - return self.inner.GetChildAtIndex(i) + r = self.children_root + if not r: + return None + return r.GetChildAtIndex(i) def get_child_index(self, name): - if not self.inner: return -1 - n = self.inner.GetNumChildren() + r = self.children_root + if not r: + return -1 + n = r.GetNumChildren() for idx in range(n): - c = self.inner.GetChildAtIndex(idx) + c = r.GetChildAtIndex(idx) if c and c.IsValid() and c.GetName() == name: return idx return -1 -# -------- Location summary: "ShortPath:LineNumber" -------- - -def _str_from_std_string(v: lldb.SBValue) -> str | None: - if not v or not v.IsValid(): - return None - s = v.GetSummary() or v.GetObjectDescription() or v.GetValue() - if not s: - return None - s = s.strip() - if len(s) >= 2 and s[0] == '"' and s[-1] == '"': - s = s[1:-1] - return s - +# ============================================================================= +# Location: summary only ("ShortPath:LineNumber [doc]") +# ============================================================================= def LocationSummaryProvider(valobj, _dict): + """Summary for mrdocs::Location → 'ShortPath:LineNumber[ [doc]]'.""" try: v = _deref_if_ptr_or_ref(valobj) if not v or not v.IsValid(): @@ -471,133 +702,34 @@ def LocationSummaryProvider(valobj, _dict): if not path: return "" - line = 0 - if ln and ln.IsValid(): - line = ln.GetValueAsUnsigned(0) - - documented = False - if dn and dn.IsValid(): - documented = dn.GetValueAsUnsigned(0) != 0 - + line = ln.GetValueAsUnsigned(0) if (ln and ln.IsValid()) else 0 + documented = (dn.GetValueAsUnsigned(0) != 0) if (dn and dn.IsValid()) else False return f"{path}:{line}{' [doc]' if documented else ''}" except Exception as e: return f"" -# ---------- helpers for name-based child lookup ---------- -def _strip_prefixes_for_match(nm: str) -> str: - """Strip our NN prefixes and optional 'Decl::' we add in other providers.""" - if not nm: - return nm - # Remove leading "NN " prefix (e.g., "03 Info::Name") - nm = re.sub(r"^\d{2}\s+", "", nm) - # If a DeclType prefix exists, keep only the field after '::' - if "::" in nm: - nm = nm.split("::", 1)[1] - # Also strip any trailing " #k" suffix used for de-duping - nm = re.sub(r"\s+#\d+$", "", nm) - return nm - - -def _child_by_name_any(v: lldb.SBValue, names: tuple[str, ...]) -> lldb.SBValue | None: - """Try to get a child by name from both synthetic and non-synthetic views.""" - if not v or not v.IsValid(): - return None - views = [v] - try: - ns = v.GetNonSyntheticValue() - if ns and ns.IsValid() and ns.GetID() != v.GetID(): - views.append(ns) - except Exception: - pass - - # 1) Direct member lookup - for view in views: - for nm in names: - ch = view.GetChildMemberWithName(nm) - if ch and ch.IsValid(): - return ch - - # 2) Enumerate children and match by (possibly prefixed) name - for view in views: - n = view.GetNumChildren() - for i in range(n): - ch = view.GetChildAtIndex(i) - if not ch or not ch.IsValid(): - continue - nm = _strip_prefixes_for_match(ch.GetName() or "") - if nm in names: - return ch - return None - - -def _find_field_recursive_any(v: lldb.SBValue, names: tuple[str, ...]) -> lldb.SBValue | None: - """Search own fields for any name in `names`; recurse into direct bases (type+offset).""" - if not v or not v.IsValid(): - return None - # Try fast path on this node - got = _child_by_name_any(v, names) - if got and got.IsValid(): - return got +# ============================================================================= +# Polymorphic: summary + synthetic +# ============================================================================= - # Try via SBType fields (offset-based) - t = v.GetType() - if t and t.IsValid(): - try: - n = t.GetNumberOfFields() - except Exception: - n = 0 - for i in range(n): - tm = t.GetFieldAtIndex(i) - # skip bases - try: - if tm.IsBaseClass(): - continue - except Exception: - pass - fname = tm.GetName() or "" - if fname in names: - try: - off = tm.GetOffsetInBytes() - except Exception: - off_bits = tm.GetOffsetInBits() if hasattr(tm, "GetOffsetInBits") else 0 - off = off_bits // 8 - ft = tm.GetType() - ch = v.CreateChildAtOffset(fname, off, ft) if ft and ft.IsValid() else None - if ch and ch.IsValid(): - return ch - - # Recurse into direct bases - for btm in _get_direct_bases(t): - base_v = _base_value_from_typemember(v, btm) - if base_v and base_v.IsValid(): - got = _find_field_recursive_any(base_v, names) - if got and got.IsValid(): - return got - return None - - -# ---------- Polymorphic: forward to WB->Value ---------- def _poly_inner_value(valobj): """ - Resolve Polymorphic::WB (WrapperBase*). If null -> "null". - Else, deref to dynamic Wrapper and return its 'Value' field. + Resolve Polymorphic::WB (WrapperBase*). + - nullptr → "null" + - else deref, take dynamic type, return its 'Value' field (own or in bases). """ v = _deref_if_ptr_or_ref(valobj) if not v or not v.IsValid(): return None - # Find WB robustly wb = (_child_by_name_any(v, ("WB", "wb", "WB_", "wb_")) or _get_field_value_by_offset(v, "WB")) if not wb or not wb.IsValid(): return None - - # nullptr? if wb.GetValueAsUnsigned(0) == 0: return "null" - # Deref WB and prefer dynamic type; also try non-synthetic view pointee = wb.Dereference() if not pointee or not pointee.IsValid(): return None @@ -608,13 +740,13 @@ def _poly_inner_value(valobj): except Exception: pass - # Find Value on the wrapper (own or in bases) value = (_child_by_name_any(wrapper, ("Value", "value", "Value_", "value_")) or _find_field_recursive_any(wrapper, ("Value", "value", "Value_", "value_"))) return value if value and value.IsValid() else None def PolymorphicSummaryProvider(valobj, _dict): + """Summary for mrdocs::Polymorphic: forward T’s summary/value; null → std::nullopt.""" try: inner = _poly_inner_value(valobj) if inner == "null": @@ -622,7 +754,6 @@ def PolymorphicSummaryProvider(valobj, _dict): if not inner: return "" - # Forward T's own summary if present; else clean fallbacks s = _clean(inner.GetSummary()) if s is not None: return s @@ -638,8 +769,7 @@ def PolymorphicSummaryProvider(valobj, _dict): class PolymorphicTransparentSyntheticProvider: - """Present Polymorphic as if it were the inner Value.""" - + """Synthetic for mrdocs::Polymorphic: expose inner 'Value' as children.""" def __init__(self, valobj, _dict): self.valobj = valobj self.inner = None @@ -648,6 +778,26 @@ def __init__(self, valobj, _dict): def update(self): iv = _poly_inner_value(self.valobj) self.inner = None if (iv is None or iv == "null") else iv + self.children_root = self.inner + + if self.inner and self.inner.IsValid(): + try: + self.inner.SetPreferDynamicValue(lldb.eDynamicCanRunTarget) + self.inner.SetPreferSyntheticValue(True) + except Exception: + pass + + t = self.inner.GetType() + if t and t.IsValid() and t.IsPointerType(): + try: + deref = self.inner.Dereference() + if deref and deref.IsValid(): + self.children_root = deref + # also ensure prefs on the deref we’ll enumerate + self.children_root.SetPreferDynamicValue(lldb.eDynamicCanRunTarget) + self.children_root.SetPreferSyntheticValue(True) + except Exception: + pass def has_children(self): return bool(self.inner) and (self.inner.GetNumChildren() > 0 or self.inner.MightHaveChildren()) @@ -671,12 +821,45 @@ def get_child_index(self, name): return -1 -# -------- NameInfo summary: "Prefix::Name" or "Name" -------- +# ============================================================================= +# Registration helpers & setup (uniform, zero hard-coded variant lists) +# ============================================================================= + +def _rx_variants(core: str) -> list[str]: + """ + Build regex variants for a given C++ type name/pattern. + + Args: + core: Base name or pattern (without ^$). Examples: + - 'mrdocs::Name' + - 'mrdocs::Optional<.*>' + - 'mrdocs::[^:]*Symbol' (already a regex) + allow_kind_prefixes: + If True, generate 'class ' and 'struct ' prefixed variants. + If False, don't (useful for templated regexes). + allow_const: + If True, generate 'const ' prefixed variants. + + Returns: + List of '^...$' anchored regex strings. + """ + kinds = ["", "class ", "struct "] + consts = ["", "const "] + out = [] + for c in consts: + for k in kinds: + # Note: const must go before class/struct, e.g. 'const class T' + prefix = f"{c}{k}".strip() + space = (prefix + " ") if prefix else "" + out.append(f"^{space}{core}$") + return out + +# -------- Name summary: "Prefix::Name" or "Name" -------- -# --- helpers for NameInfo prefix chain (names only, no ids) --- +# --- helpers for Name prefix chain (names only, no ids) --- def _nameinfo_path_from(ni: lldb.SBValue) -> str | None: - """Return 'A::B::C' built from NameInfo::Name along the Prefix chain. No ids.""" + """Return 'A::B::C' built from Name::Name along the Prefix chain. No ids.""" if not ni or not ni.IsValid(): return None # current name @@ -685,10 +868,10 @@ def _nameinfo_path_from(ni: lldb.SBValue) -> str | None: if not nm: nm = "" - # recurse into Prefix (Polymorphic) + # recurse into Prefix (Polymorphic) pref_poly = _get_field_value_by_offset(ni, "Prefix") or ni.GetChildMemberWithName("Prefix") if pref_poly and pref_poly.IsValid(): - inner = _poly_inner_value(pref_poly) # "null" | SBValue(NameInfo) | None + inner = _poly_inner_value(pref_poly) # "null" | SBValue(Name) | None if inner and inner != "null": parent = _nameinfo_path_from(inner) return f"{parent}::{nm}" if parent else nm @@ -705,9 +888,9 @@ def _hex_from_symbolid(sbv) -> str | None: return ''.join(f'{b:02x}' for b in bs) -# --- NameInfo summary: "Prefix::Name (idhex)" or "Name" --- +# --- Name summary: "Prefix::Name (idhex)" or "Name" --- -def NameInfoSummaryProvider(valobj, _dict): +def NameSummaryProvider(valobj, _dict): try: v = _deref_if_ptr_or_ref(valobj) if not v or not v.IsValid(): @@ -741,45 +924,289 @@ def NameInfoSummaryProvider(valobj, _dict): except Exception as e: return f"" +class FlattenAllBasesSyntheticProvider: + """ + Flatten data members from all base classes (deepest → most-derived), then own members. + Does not recurse into sub-fields. Skips base-class nodes as direct children. + Drop-in generic provider for “show me everything flat”. + """ + + # knobs (override per-class by assigning on the instance in __init__ if you want) + EMIT_SELF_LAST = True # after all bases, append the most-derived's own fields + INCLUDE_DECL_TYPE = _INCLUDE_DECL_TYPE + PREFIX_ORDER = _PREFIX_ORDER + + def __init__(self, valobj, _dict): + self.root = _deref_if_ptr_or_ref(valobj) + self.children = [] + self._err = None + self.update() + + # ----- helpers ----- + + def _norm_typename(self, t: lldb.SBType) -> str: + n = _type_name(t) + for p in ("class ", "struct ", "const ", "volatile "): + if n.startswith(p): + n = n[len(p):] + return n + + def _append_own_fields(self, v: lldb.SBValue, seen: set[str], rank: int, decl: str): + t = v.GetType() + if not t or not t.IsValid(): + return + for tm in _get_type_fields(t): + # only data members, never a base node + try: + if tm.IsBaseClass(): + continue + except Exception: + # older LLDBs may not have IsBaseClass; rely on data-path fallback + pass + + fname = tm.GetName() + if not fname: + continue + + child = v.GetChildMemberWithName(fname) + if not child or not child.IsValid(): + continue + + # build display name + parts = [] + if self.PREFIX_ORDER: + parts.append(f"{rank:02d}") + parts.append(f"{decl}::{fname}" if (self.INCLUDE_DECL_TYPE and decl) else fname) + name = " ".join(parts) + + # de-dup same display labels + base = name + k = 2 + while name in seen: + name = f"{base} #{k}" + k += 1 + seen.add(name) + + try: + child.SetName(name) + except Exception: + pass + + self.children.append(child) + + def _linearize_bases_postorder(self, v: lldb.SBValue): + """Nodes in post-order: [deepest bases …, most-derived (v)].""" + order = [] + visited_types = set() # avoid re-visiting virtual bases / diamonds + + def dfs(node: lldb.SBValue): + t = node.GetType() + if not t or not t.IsValid(): + return + # Use type name as a proxy key; SBType equality can be flaky across modules + key = self._norm_typename(t) + if key in visited_types: + return + visited_types.add(key) + + for btm in _get_direct_bases(t): + base_v = _base_value_from_typemember(node, btm) + if base_v and base_v.IsValid(): + dfs(base_v) + + order.append(node) + + dfs(v) + return order + + # ----- required API ----- + + def update(self): + self.children = [] + v = self.root + if not v or not v.IsValid(): + return + + try: + order = self._linearize_bases_postorder(v) + if not order: + # nothing to flatten → fall back + n = v.GetNumChildren() + for i in range(n): + c = v.GetChildAtIndex(i) + if c and c.IsValid(): + self.children.append(c) + return + + # Optionally place self (most-derived) last + if self.EMIT_SELF_LAST and order[-1] is v: + bases = order[:-1] + tail = [order[-1]] + else: + bases = order + tail = [] + + seen = set() + rank = 0 + for node in bases: + self._append_own_fields(node, seen, rank, self._norm_typename(node.GetType())) + rank += 1 + for node in tail: + self._append_own_fields(node, seen, rank, self._norm_typename(node.GetType())) + rank += 1 + + # final fallback if nothing was emitted + if not self.children: + n = v.GetNumChildren() + for i in range(n): + c = v.GetChildAtIndex(i) + if c and c.IsValid(): + self.children.append(c) + + except Exception as e: + # defensive: on any unexpected LLDB quirk, degrade gracefully + self._err = str(e) + n = v.GetNumChildren() + for i in range(n): + c = v.GetChildAtIndex(i) + if c and c.IsValid(): + self.children.append(c) + + def has_children(self): return len(self.children) > 0 + def num_children(self): return len(self.children) + def get_child_at_index(self, idx): return self.children[idx] if 0 <= idx < len(self.children) else None + def get_child_index(self, name): + for i, c in enumerate(self.children): + try: + if c.GetName() == name: + return i + except Exception: + pass + return -1 + +def _escape_for_summary(s: str) -> str: + # C-ish minimal escaping for debugger summaries + s = s.replace("\\", "\\\\").replace("\"", "\\\"") + s = s.replace("\n", "\\n").replace("\r", "\\r").replace("\t", "\\t") + return s + +def _kind_string(v: lldb.SBValue) -> str: + k = _get_field_value_by_offset(v, "Kind") or v.GetChildMemberWithName("Kind") + if not (k and k.IsValid()): + return "Inline" + return (_clean(k.GetSummary()) or _clean(k.GetValue()) or "Inline") + +def _inline_text(vo: lldb.SBValue, depth: int = 0, max_depth: int = 16, max_children: int = 512) -> str: + """ + Extract *textual* content of an Inline: + - literal → its contents + - children → concatenation of children's textual contents (recursively) + - otherwise → "" + (Does NOT prepend Kind; meant for building the quoted part.) + """ + if not (vo and vo.IsValid()): + return "" + + v = _deref_if_ptr_or_ref(vo) + + # literal + lit = _get_field_value_by_offset(v, "literal") or v.GetChildMemberWithName("literal") + if lit and lit.IsValid(): + s = _str_from_std_string(lit) + return s or "" + + # children + ch = _get_field_value_by_offset(v, "children") or v.GetChildMemberWithName("children") + if ch and ch.IsValid(): + try: + ch.SetPreferSyntheticValue(True) + except Exception: + pass + n = ch.GetNumChildren() + if n <= 0 or depth >= max_depth: + return "" + pieces = [] + limit = min(n, max_children) + for i in range(limit): + el = ch.GetChildAtIndex(i) + if not (el and el.IsValid()): + continue + inner = _poly_inner_value(el) + if inner == "null": + continue + inner = inner or el + try: + inner.SetPreferSyntheticValue(True) + inner.SetPreferDynamicValue(lldb.eDynamicCanRunTarget) + except Exception: + pass + pieces.append(_inline_text(inner, depth + 1, max_depth, max_children)) + return "".join(pieces) + + # nothing textual here + return "" + +def InlineSummaryProvider(valobj, _dict): + """ + Summary for mrdocs::doc::<...>Inline: + 1) Kind: "escaped text" (literal or children concatenation) + 2) Kind (no literal/children) + """ + try: + v = _deref_if_ptr_or_ref(valobj) + if not (v and v.IsValid()): + return "" + + kind = _kind_string(v) + + # textual content (literal or concatenated children) + text = _inline_text(v) + if text: + return f'{kind}: "{_escape_for_summary(text)}"' + + # no textual content → just Kind + return kind + except Exception as e: + return f"" def __lldb_init_module(debugger, _dict): + """ + Register all summaries/synthetics in category 'MrDocs' using uniform variant generation. + """ cat = "MrDocs" - # Register for both regexes; keep it boring and compatible. - for rx in _INFO_REGEXES: - debugger.HandleCommand(f'type summary add -w {cat} -x -P -F mrdocs_formatters.InfoLikeSummaryProvider "{rx}"') - debugger.HandleCommand(f'type synthetic add -w {cat} -x -l mrdocs_formatters.InfoLikeSyntheticProvider "{rx}"') - for rx in ("^clang::mrdocs::SymbolID$", "^const clang::mrdocs::SymbolID$"): - debugger.HandleCommand( - f'type summary add -w {cat} -x -P -F mrdocs_formatters.SymbolIDSummaryProvider "{rx}"' - ) - for rx in ("^clang::mrdocs::Optional<.*>$", "^const clang::mrdocs::Optional<.*>$"): - debugger.HandleCommand( - f'type summary add -w {cat} -x -P -F mrdocs_formatters.OptionalSummaryProvider "{rx}"' - ) - debugger.HandleCommand( - f'type synthetic add -w {cat} -x -l mrdocs_formatters.OptionalTransparentSyntheticProvider "{rx}"' - ) - for rx in ("^clang::mrdocs::Location$", "^const clang::mrdocs::Location$"): - debugger.HandleCommand( - f'type summary add -w {cat} -x -P -F mrdocs_formatters.LocationSummaryProvider "{rx}"' - ) - for rx in ("^clang::mrdocs::Polymorphic<.*>$", "^const clang::mrdocs::Polymorphic<.*>$"): - debugger.HandleCommand( - f'type summary add -w {cat} -x -P -F mrdocs_formatters.PolymorphicSummaryProvider "{rx}"' - ) - debugger.HandleCommand( - f'type synthetic add -w {cat} -x -l mrdocs_formatters.PolymorphicTransparentSyntheticProvider "{rx}"' - ) - for rx in ( - "^clang::mrdocs::NameInfo$", - "^const clang::mrdocs::NameInfo$", - "^class clang::mrdocs::NameInfo$", - "^const class clang::mrdocs::NameInfo$", - "^struct clang::mrdocs::NameInfo$", - "^const struct clang::mrdocs::NameInfo$", - ): - debugger.HandleCommand( - f'type summary add -w {cat} -x -P -F mrdocs_formatters.NameInfoSummaryProvider "{rx}"' - ) + + # Symbol-like (regex pattern + const variant; no class/struct added) + for rx in _rx_variants("mrdocs::[^:]*Symbol"): + debugger.HandleCommand(f'type summary add -w {cat} -x -P -F mrdocs_formatters.SymbolLikeSummaryProvider "{rx}"') + debugger.HandleCommand(f'type synthetic add -w {cat} -x -l mrdocs_formatters.SymbolLikeSyntheticProvider "{rx}"') + + # SymbolID (concrete type; include class/struct & const variants) + for rx in _rx_variants("mrdocs::SymbolID"): + debugger.HandleCommand(f'type summary add -w {cat} -x -P -F mrdocs_formatters.SymbolIDSummaryProvider "{rx}"') + + # Location (concrete type with variants) + for rx in _rx_variants("mrdocs::Location"): + debugger.HandleCommand(f'type summary add -w {cat} -x -P -F mrdocs_formatters.LocationSummaryProvider "{rx}"') + + # Name (concrete type; include class/struct & const variants) + for rx in _rx_variants("mrdocs::Name"): + debugger.HandleCommand(f'type summary add -w {cat} -x -P -F mrdocs_formatters.NameSummaryProvider "{rx}"') + + # Documentation: use the flattener + for rx in _rx_variants("mrdocs::doc::[^:]*Block") + _rx_variants("mrdocs::doc::[^:]*Inline"): + debugger.HandleCommand(f'type synthetic add -w {cat} -x -l mrdocs_formatters.FlattenAllBasesSyntheticProvider "{rx}"') + + # Optional (templated regex; don't add class/struct; do add const) + for rx in _rx_variants("mrdocs::Optional<.*>"): + debugger.HandleCommand(f'type summary add -w {cat} -x -P -F mrdocs_formatters.OptionalSummaryProvider "{rx}"') + debugger.HandleCommand(f'type synthetic add -w {cat} -x -l mrdocs_formatters.OptionalTransparentSyntheticProvider "{rx}"') + + # Polymorphic (templated regex; no class/struct; add const) + for rx in _rx_variants("mrdocs::Polymorphic<.*>"): + debugger.HandleCommand(f'type summary add -w {cat} -x -P -F mrdocs_formatters.PolymorphicSummaryProvider "{rx}"') + debugger.HandleCommand(f'type synthetic add -w {cat} -x -l mrdocs_formatters.PolymorphicTransparentSyntheticProvider "{rx}"') + + for rx in _rx_variants("mrdocs::doc::[^:]*Inline"): + debugger.HandleCommand(f'type summary add -w {cat} -x -P -F mrdocs_formatters.InlineSummaryProvider "{rx}"') debugger.HandleCommand(f"type category enable {cat}") diff --git a/share/mrdocs/addons/generator/adoc/partials/doc/block/admonition.adoc.hbs b/share/mrdocs/addons/generator/adoc/partials/doc/block/admonition.adoc.hbs new file mode 100644 index 000000000..1e38a0e9d --- /dev/null +++ b/share/mrdocs/addons/generator/adoc/partials/doc/block/admonition.adoc.hbs @@ -0,0 +1,5 @@ +[{{upper admonish}}] +==== +{{> doc/block-container ~}} +==== + diff --git a/share/mrdocs/addons/generator/adoc/partials/doc/block/brief.adoc.hbs b/share/mrdocs/addons/generator/adoc/partials/doc/block/brief.adoc.hbs new file mode 100644 index 000000000..5f8b2b0e6 --- /dev/null +++ b/share/mrdocs/addons/generator/adoc/partials/doc/block/brief.adoc.hbs @@ -0,0 +1,3 @@ +{{>doc/block/inline-brief}} + + diff --git a/share/mrdocs/addons/generator/adoc/partials/doc/block/code.adoc.hbs b/share/mrdocs/addons/generator/adoc/partials/doc/block/code.adoc.hbs new file mode 100644 index 000000000..2deaa1510 --- /dev/null +++ b/share/mrdocs/addons/generator/adoc/partials/doc/block/code.adoc.hbs @@ -0,0 +1,4 @@ +[,{{ or info "cpp" }}] +---- +{{{ literal }}} +---- diff --git a/share/mrdocs/addons/generator/adoc/partials/doc/block/list.adoc.hbs b/share/mrdocs/addons/generator/adoc/partials/doc/block/list.adoc.hbs new file mode 100644 index 000000000..9f5d2c86e --- /dev/null +++ b/share/mrdocs/addons/generator/adoc/partials/doc/block/list.adoc.hbs @@ -0,0 +1,9 @@ +{{#each items}} +* {{#each blocks}}{{#unless @first}} + ++ +{{/unless}} +{{#if (eq kind "paragraph")}}{{> doc/inline-container }}{{else}}{{> doc/block }}{{/if}} +{{/each}} +{{/each}} + diff --git a/share/mrdocs/addons/generator/adoc/partials/doc/block/paragraph.adoc.hbs b/share/mrdocs/addons/generator/adoc/partials/doc/block/paragraph.adoc.hbs new file mode 100644 index 000000000..01389db3b --- /dev/null +++ b/share/mrdocs/addons/generator/adoc/partials/doc/block/paragraph.adoc.hbs @@ -0,0 +1,3 @@ +{{> doc/inline-container }} + + diff --git a/share/mrdocs/addons/generator/adoc/partials/javadoc/admonition.adoc.hbs b/share/mrdocs/addons/generator/adoc/partials/javadoc/admonition.adoc.hbs deleted file mode 100644 index 9e105d399..000000000 --- a/share/mrdocs/addons/generator/adoc/partials/javadoc/admonition.adoc.hbs +++ /dev/null @@ -1,3 +0,0 @@ -[{{upper admonish}}] -{{#each children}}{{> javadoc/any-text }}{{/each}} - diff --git a/share/mrdocs/addons/generator/adoc/partials/javadoc/brief.adoc.hbs b/share/mrdocs/addons/generator/adoc/partials/javadoc/brief.adoc.hbs deleted file mode 100644 index b43fc7676..000000000 --- a/share/mrdocs/addons/generator/adoc/partials/javadoc/brief.adoc.hbs +++ /dev/null @@ -1,3 +0,0 @@ -{{>javadoc/inline-brief}} - - diff --git a/share/mrdocs/addons/generator/adoc/partials/javadoc/code.adoc.hbs b/share/mrdocs/addons/generator/adoc/partials/javadoc/code.adoc.hbs deleted file mode 100644 index 6cfe4e5e8..000000000 --- a/share/mrdocs/addons/generator/adoc/partials/javadoc/code.adoc.hbs +++ /dev/null @@ -1,5 +0,0 @@ -[,cpp] ----- -{{#each children}}{{> javadoc/any-text verbatim=true }} -{{/each}} ----- diff --git a/share/mrdocs/addons/generator/adoc/partials/javadoc/description.adoc.hbs b/share/mrdocs/addons/generator/adoc/partials/javadoc/description.adoc.hbs deleted file mode 100644 index 63e7695e6..000000000 --- a/share/mrdocs/addons/generator/adoc/partials/javadoc/description.adoc.hbs +++ /dev/null @@ -1 +0,0 @@ -{{#each .}}{{>javadoc/any-block .}}{{/each}} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/adoc/partials/javadoc/heading.adoc.hbs b/share/mrdocs/addons/generator/adoc/partials/javadoc/heading.adoc.hbs deleted file mode 100644 index 4c699e751..000000000 --- a/share/mrdocs/addons/generator/adoc/partials/javadoc/heading.adoc.hbs +++ /dev/null @@ -1 +0,0 @@ -{{#> markup/dynamic-level-h level=1 }}{{string}}{{/markup/dynamic-level-h}} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/adoc/partials/javadoc/paragraph.adoc.hbs b/share/mrdocs/addons/generator/adoc/partials/javadoc/paragraph.adoc.hbs deleted file mode 100644 index 9b14154cc..000000000 --- a/share/mrdocs/addons/generator/adoc/partials/javadoc/paragraph.adoc.hbs +++ /dev/null @@ -1,2 +0,0 @@ -{{#each children}}{{> javadoc/any-text }}{{/each}} - diff --git a/share/mrdocs/addons/generator/adoc/partials/javadoc/unordered_list.adoc.hbs b/share/mrdocs/addons/generator/adoc/partials/javadoc/unordered_list.adoc.hbs deleted file mode 100644 index f83265378..000000000 --- a/share/mrdocs/addons/generator/adoc/partials/javadoc/unordered_list.adoc.hbs +++ /dev/null @@ -1,3 +0,0 @@ -{{#each items}}* {{> javadoc/block }} -{{/each}} - diff --git a/share/mrdocs/addons/generator/adoc/partials/markup/a.adoc.hbs b/share/mrdocs/addons/generator/adoc/partials/markup/a.adoc.hbs index 172ba3efc..429b416be 100644 --- a/share/mrdocs/addons/generator/adoc/partials/markup/a.adoc.hbs +++ b/share/mrdocs/addons/generator/adoc/partials/markup/a.adoc.hbs @@ -3,7 +3,9 @@ https://gitlab.com/antora/antora/-/issues/428 }} -{{#if (starts_with href "#")~}} +{{#if (eq href @root.symbol.url)~}} + {{{> @partial-block }}} +{{~else if (starts_with href "#")~}} link:{{{ href }}}[{{> @partial-block }}] {{~else if (starts_with href "/")~}} xref:{{{remove_prefix href "/"}}}[{{> @partial-block }}] diff --git a/share/mrdocs/addons/generator/adoc/partials/markup/h.adoc.hbs b/share/mrdocs/addons/generator/adoc/partials/markup/h.adoc.hbs index d8959caaf..d8deae259 100644 --- a/share/mrdocs/addons/generator/adoc/partials/markup/h.adoc.hbs +++ b/share/mrdocs/addons/generator/adoc/partials/markup/h.adoc.hbs @@ -17,3 +17,4 @@ {{#if id}}[#{{{id}}}] {{/if}} ={{{select level (repeat "=" level) (select @root.config.multipage "==" "=")}}} {{> @partial-block }} + diff --git a/share/mrdocs/addons/generator/adoc/partials/symbol.adoc.hbs b/share/mrdocs/addons/generator/adoc/partials/symbol.adoc.hbs index abb772c7a..bc35cf07a 100644 --- a/share/mrdocs/addons/generator/adoc/partials/symbol.adoc.hbs +++ b/share/mrdocs/addons/generator/adoc/partials/symbol.adoc.hbs @@ -15,14 +15,14 @@ {{! Single page documentation: symbol is not available to the wrapper but it's available here }} {{! Include the symbol title at a higher level }} {{#> markup/h level=1 id=symbol.anchor }}{{> symbol/qualified-name-title symbol }}{{/markup/h}} -{{/unless}} +{{~/unless}} {{! Brief }} {{#if symbol.doc.brief}} -{{> javadoc/brief symbol.doc.brief }} +{{> doc/block/brief symbol.doc.brief }} {{/if}} {{! Synopsis }} {{#unless (eq symbol.kind "namespace")}} -{{#> markup/dynamic-level-h }}{{#if (ne symbol.kind "overloads")}}Synopsis{{else}}Synopses{{/if}}{{/markup/dynamic-level-h}} +{{#> markup/dynamic-level-h }}{{#if (ne symbol.kind "overloads")}}Synopsis{{else}}Synopses{{/if}}{{/markup/dynamic-level-h~}} {{>location/source dcl=(primary_location symbol)}} @@ -30,28 +30,28 @@ {{/unless}} {{! Base classes }} {{#if (any_of_by symbol.bases "isPublic")}} -{{#> markup/dynamic-level-h }}Base Classes{{/markup/dynamic-level-h}} +{{#> markup/dynamic-level-h }}Base Classes{{/markup/dynamic-level-h~}} [cols=2] |=== | Name | Description {{#each (filter_by symbol.bases "isPublic")}} | {{#>markup/code}}{{> type/declarator type }}{{/markup/code}} -| {{> javadoc/inline-brief symbol.doc.brief }} +| {{> doc/block/inline-brief type.name.symbol.doc.brief }} {{/each}} |=== {{/if}} {{! Protected Base classes }} {{#if (any_of_by symbol.bases "isProtected")}} -{{#> markup/dynamic-level-h }}Protected Base Classes{{/markup/dynamic-level-h}} +{{#> markup/dynamic-level-h }}Protected Base Classes{{/markup/dynamic-level-h~}} [cols=2] |=== | Name | Description {{#each (filter_by symbol.bases "isProtected")}} | {{#>markup/code}}{{> type/declarator type }}{{/markup/code}} -| {{> javadoc/inline-brief symbol.doc.brief }} +| {{> doc/block/inline-brief type.name.symbol.doc.brief }} {{/each}} |=== @@ -69,24 +69,11 @@ {{>symbol/tranche tranche=symbol.members label="" is-namespace=true}} {{! Enum members }} {{else if (eq symbol.kind "enum")}} -{{#if symbol.constants}} -{{#> markup/dynamic-level-h }}Members{{/markup/dynamic-level-h}} - -[cols=2] -|=== -| Name -| Description -{{#each (filter_by symbol.constants "isRegular" "isSeeBelow")}} -|`{{>symbol/name-text .}}` -|{{> javadoc/inline-brief doc.brief }} -{{/each}} -|=== - -{{/if}} +{{>symbol/members-table members=symbol.constants title="Members"}} {{/if}} {{! Friends }} {{#if symbol.friends}} -{{#> markup/dynamic-level-h }}Friends{{/markup/dynamic-level-h}} +{{#> markup/dynamic-level-h }}Friends{{/markup/dynamic-level-h~}} [cols=2] |=== | Name @@ -94,7 +81,7 @@ {{#each symbol.friends }} {{#if symbol}} | {{#>markup/code}}{{> symbol/qualified-name symbol }}{{/markup/code}} -| {{> javadoc/inline-brief symbol.doc.brief }} +| {{> doc/block/inline-brief symbol.doc.brief }} {{else}} | {{#>markup/code}}{{> type/declarator type }}{{/markup/code}} | @@ -110,21 +97,21 @@ {{/if}} {{! Related symbols }} {{#if symbol.doc.related}} -{{#> markup/dynamic-level-h }}Non-Member Functions{{/markup/dynamic-level-h}} +{{#> markup/dynamic-level-h }}Non-Member Functions{{/markup/dynamic-level-h~}} [cols=2] |=== | Name | Description {{#each symbol.doc.related}} -| {{> javadoc/reference }} -| {{> javadoc/inline-brief symbol.doc.brief }} +| {{> doc/inline/reference }} +| {{> doc/block/inline-brief symbol.doc.brief }} {{/each}} |=== {{/if}} {{! Derived classes }} {{#if symbol.derived}} -{{#> markup/dynamic-level-h }}Derived Classes{{/markup/dynamic-level-h}} +{{#> markup/dynamic-level-h }}Derived Classes{{/markup/dynamic-level-h~}} [cols=2] |=== | Name @@ -135,7 +122,7 @@ {{else~}} {{> type/name-info-text . }} {{/if}} -| {{> javadoc/inline-brief doc.brief }} +| {{> doc/block/inline-brief doc.brief }} {{/each}} |=== @@ -143,13 +130,15 @@ {{! Description }} {{#if symbol.doc.description}} {{#unless traversing-global-namespace}} -{{#> markup/dynamic-level-h }}Description{{/markup/dynamic-level-h}} -{{> javadoc/description symbol.doc.description }} +{{#unless (eq (lookup (first symbol.doc.description) "kind") "heading") }} +{{#> markup/dynamic-level-h }}Description{{/markup/dynamic-level-h~}} +{{~/unless}} +{{> doc/block/document symbol.doc.description }} {{/unless}} {{/if}} {{! Using symbols }} {{#if symbol.shadows}} -{{#> markup/dynamic-level-h }}Introduced Symbols{{/markup/dynamic-level-h}} +{{#> markup/dynamic-level-h }}Introduced Symbols{{/markup/dynamic-level-h~}} {{#if (any_of_by symbol.shadows "doc")}} [cols=2] |=== @@ -157,7 +146,7 @@ | Description {{#each symbol.shadows}} | {{> symbol/qualified-name . }} -| {{> javadoc/inline-brief doc.brief }} +| {{> doc/block/inline-brief doc.brief }} {{/each}} |=== {{else}} @@ -172,86 +161,86 @@ {{/if}} {{! Exceptions }} {{#if symbol.doc.exceptions}} -{{#> markup/dynamic-level-h }}Exceptions{{/markup/dynamic-level-h}} +{{#> markup/dynamic-level-h }}Exceptions{{/markup/dynamic-level-h~}} [cols=2] |=== | Name | Thrown on {{#each symbol.doc.exceptions}} -| {{> javadoc/reference exception}} -| {{>javadoc/block .}} +| {{> doc/inline/reference exception}} +| {{>doc/block .}} {{/each}} |=== {{/if}} {{! Return value }} {{#if symbol.doc.returns}} -{{#> markup/dynamic-level-h }}Return Value{{/markup/dynamic-level-h}} -{{#if (eq (size symbol.doc.returns) 1)}} -{{> javadoc/returns symbol.doc.returns.[0]}} +{{#> markup/dynamic-level-h }}Return Value{{/markup/dynamic-level-h~}} +{{~#if (eq (size symbol.doc.returns) 1)}} +{{> doc/block/returns symbol.doc.returns.[0]}} {{else}} {{#each symbol.doc.returns}} -* {{> javadoc/returns .}} +* {{> doc/block/returns .}} {{/each}} {{/if}} {{/if}} {{! Template Parameters }} {{#if symbol.doc.tparams}} -{{#> markup/dynamic-level-h }}Template Parameters{{/markup/dynamic-level-h}} +{{#> markup/dynamic-level-h }}Template Parameters{{/markup/dynamic-level-h~}} [cols=2] |=== | Name | Description {{#each symbol.doc.tparams}} | *{{name}}* -| {{>javadoc/block .}} +| {{>doc/block .}} {{/each}} |=== {{/if}} {{! Parameters }} {{#if symbol.doc.params}} -{{#> markup/dynamic-level-h }}Parameters{{/markup/dynamic-level-h}} +{{#> markup/dynamic-level-h }}Parameters{{/markup/dynamic-level-h~}} [cols=2] |=== | Name | Description {{#each symbol.doc.params}} | *{{name}}*{{#if direction}} [{{direction}}]{{/if}} -| {{>javadoc/block .}} +| {{>doc/block .}} {{/each}} |=== {{/if}} {{! Preconditions }} {{#if symbol.doc.preconditions}} -{{#> markup/dynamic-level-h }}Preconditions{{/markup/dynamic-level-h}} -{{#each symbol.doc.preconditions}}* {{>javadoc/block .}} +{{#> markup/dynamic-level-h }}Preconditions{{/markup/dynamic-level-h~}} +{{#each symbol.doc.preconditions}}* {{>doc/block .}} {{/each}} {{/if}} {{! Postconditions }} {{#if symbol.doc.postconditions}} -{{#> markup/dynamic-level-h }}Postconditions{{/markup/dynamic-level-h}} -{{#each symbol.doc.postconditions}}* {{>javadoc/block .}} +{{#> markup/dynamic-level-h }}Postconditions{{/markup/dynamic-level-h~}} +{{#each symbol.doc.postconditions}}* {{>doc/block .}} {{/each}} {{/if}} {{! See Also }} {{#if symbol.doc.sees}} -{{#> markup/dynamic-level-h }}See Also{{/markup/dynamic-level-h}} +{{#> markup/dynamic-level-h }}See Also{{/markup/dynamic-level-h~}} {{#each symbol.doc.sees}} -{{>javadoc/see .}} +{{>doc/block/see .}} {{/each}} {{/if}} {{! Recursive index for multipage global namespace }} {{#if (and (eq symbol.kind "namespace") @root.config.multipage @root.config.global-namespace-index (eq @root.symbol.kind "namespace") (not @root.symbol.name) (not @root.symbol.parent)) }} {{#each symbol.members.namespaces}} {{#if isRegular}} -{{#> markup/dynamic-level-h id=null }}{{> symbol/qualified-name . }} namespace{{/markup/dynamic-level-h}} +{{#> markup/dynamic-level-h id=null }}{{> symbol/qualified-name . }} namespace{{/markup/dynamic-level-h~}} {{> symbol symbol=. id=null traversing-global-namespace=true }} {{/if}} {{/each}} diff --git a/share/mrdocs/addons/generator/common/partials/doc/block-container.hbs b/share/mrdocs/addons/generator/common/partials/doc/block-container.hbs new file mode 100644 index 000000000..701bb129f --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/doc/block-container.hbs @@ -0,0 +1 @@ +{{#each blocks}}{{>doc/block }}{{/each}} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/doc/block.hbs b/share/mrdocs/addons/generator/common/partials/doc/block.hbs new file mode 100644 index 000000000..3212a9bfc --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/doc/block.hbs @@ -0,0 +1 @@ +{{> (concat 'doc/block/' kind) }} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/doc/block/admonition.hbs b/share/mrdocs/addons/generator/common/partials/doc/block/admonition.hbs new file mode 100644 index 000000000..766efa7e4 --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/doc/block/admonition.hbs @@ -0,0 +1 @@ +{{> doc/block-container }} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/doc/block/brief.hbs b/share/mrdocs/addons/generator/common/partials/doc/block/brief.hbs new file mode 100644 index 000000000..6391a4959 --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/doc/block/brief.hbs @@ -0,0 +1 @@ +{{#> markup/p }}{{> doc/inline-container }}{{/ markup/p }} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/doc/block/code.hbs b/share/mrdocs/addons/generator/common/partials/doc/block/code.hbs new file mode 100644 index 000000000..138367f69 --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/doc/block/code.hbs @@ -0,0 +1 @@ +{{ literal }} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/doc/block/definition-list.hbs b/share/mrdocs/addons/generator/common/partials/doc/block/definition-list.hbs new file mode 100644 index 000000000..b1d95fd42 --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/doc/block/definition-list.hbs @@ -0,0 +1 @@ +{{> doc/inline-container }} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/doc/block/document.hbs b/share/mrdocs/addons/generator/common/partials/doc/block/document.hbs new file mode 100644 index 000000000..cfb8830df --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/doc/block/document.hbs @@ -0,0 +1 @@ +{{#each .}}{{>doc/block .}}{{/each}} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/doc/block/footnote-definition.hbs b/share/mrdocs/addons/generator/common/partials/doc/block/footnote-definition.hbs new file mode 100644 index 000000000..b1d95fd42 --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/doc/block/footnote-definition.hbs @@ -0,0 +1 @@ +{{> doc/inline-container }} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/doc/block/heading.hbs b/share/mrdocs/addons/generator/common/partials/doc/block/heading.hbs new file mode 100644 index 000000000..122093cb7 --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/doc/block/heading.hbs @@ -0,0 +1 @@ +{{#> markup/dynamic-level-h level=(or level 1) }}{{> doc/inline-container }}{{/markup/dynamic-level-h}} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/doc/block/inline-brief.hbs b/share/mrdocs/addons/generator/common/partials/doc/block/inline-brief.hbs new file mode 100644 index 000000000..844b1debf --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/doc/block/inline-brief.hbs @@ -0,0 +1,4 @@ +{{! This partial is used to render the brief description of a Java element in an inline context, + such as within a sentence, a short paragraph or a table of member symbols. + It includes the inline-container partial to handle the actual rendering of the brief description. }} +{{> doc/inline-container }} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/doc/block/list.hbs b/share/mrdocs/addons/generator/common/partials/doc/block/list.hbs new file mode 100644 index 000000000..8829fa1f4 --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/doc/block/list.hbs @@ -0,0 +1,2 @@ +{{#each items}}* {{> doc/block }} +{{/each}} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/doc/block/math.hbs b/share/mrdocs/addons/generator/common/partials/doc/block/math.hbs new file mode 100644 index 000000000..b1d95fd42 --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/doc/block/math.hbs @@ -0,0 +1 @@ +{{> doc/inline-container }} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/doc/block/paragraph.hbs b/share/mrdocs/addons/generator/common/partials/doc/block/paragraph.hbs new file mode 100644 index 000000000..b1d95fd42 --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/doc/block/paragraph.hbs @@ -0,0 +1 @@ +{{> doc/inline-container }} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/doc/block/param.hbs b/share/mrdocs/addons/generator/common/partials/doc/block/param.hbs new file mode 100644 index 000000000..b1d95fd42 --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/doc/block/param.hbs @@ -0,0 +1 @@ +{{> doc/inline-container }} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/doc/block/postcondition.hbs b/share/mrdocs/addons/generator/common/partials/doc/block/postcondition.hbs new file mode 100644 index 000000000..b1d95fd42 --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/doc/block/postcondition.hbs @@ -0,0 +1 @@ +{{> doc/inline-container }} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/doc/block/precondition.hbs b/share/mrdocs/addons/generator/common/partials/doc/block/precondition.hbs new file mode 100644 index 000000000..b1d95fd42 --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/doc/block/precondition.hbs @@ -0,0 +1 @@ +{{> doc/inline-container }} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/doc/block/quote.hbs b/share/mrdocs/addons/generator/common/partials/doc/block/quote.hbs new file mode 100644 index 000000000..b1d95fd42 --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/doc/block/quote.hbs @@ -0,0 +1 @@ +{{> doc/inline-container }} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/doc/block/returns.hbs b/share/mrdocs/addons/generator/common/partials/doc/block/returns.hbs new file mode 100644 index 000000000..b1d95fd42 --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/doc/block/returns.hbs @@ -0,0 +1 @@ +{{> doc/inline-container }} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/doc/block/see.hbs b/share/mrdocs/addons/generator/common/partials/doc/block/see.hbs new file mode 100644 index 000000000..4f1052434 --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/doc/block/see.hbs @@ -0,0 +1 @@ +{{> doc/block/paragraph }} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/doc/block/t-param.hbs b/share/mrdocs/addons/generator/common/partials/doc/block/t-param.hbs new file mode 100644 index 000000000..b1d95fd42 --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/doc/block/t-param.hbs @@ -0,0 +1 @@ +{{> doc/inline-container }} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/doc/block/table.hbs b/share/mrdocs/addons/generator/common/partials/doc/block/table.hbs new file mode 100644 index 000000000..b1d95fd42 --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/doc/block/table.hbs @@ -0,0 +1 @@ +{{> doc/inline-container }} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/doc/block/thematic-break.hbs b/share/mrdocs/addons/generator/common/partials/doc/block/thematic-break.hbs new file mode 100644 index 000000000..b1d95fd42 --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/doc/block/thematic-break.hbs @@ -0,0 +1 @@ +{{> doc/inline-container }} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/doc/block/throws.hbs b/share/mrdocs/addons/generator/common/partials/doc/block/throws.hbs new file mode 100644 index 000000000..b1d95fd42 --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/doc/block/throws.hbs @@ -0,0 +1 @@ +{{> doc/inline-container }} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/doc/inline-container.hbs b/share/mrdocs/addons/generator/common/partials/doc/inline-container.hbs new file mode 100644 index 000000000..63ebbed3f --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/doc/inline-container.hbs @@ -0,0 +1 @@ +{{#each children}}{{> doc/inline }}{{/each}} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/doc/inline.hbs b/share/mrdocs/addons/generator/common/partials/doc/inline.hbs new file mode 100644 index 000000000..2850b0bf9 --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/doc/inline.hbs @@ -0,0 +1 @@ +{{> (concat 'doc/inline/' kind) }} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/doc/inline/code.hbs b/share/mrdocs/addons/generator/common/partials/doc/inline/code.hbs new file mode 100644 index 000000000..fd8549c7c --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/doc/inline/code.hbs @@ -0,0 +1 @@ +{{#> markup/code }}{{> doc/inline-container }}{{/ markup/code }} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/doc/inline/emph.hbs b/share/mrdocs/addons/generator/common/partials/doc/inline/emph.hbs new file mode 100644 index 000000000..2bd7f82f1 --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/doc/inline/emph.hbs @@ -0,0 +1 @@ +{{#> markup/i }}{{> doc/inline-container }}{{/ markup/i }} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/doc/inline/footnote-reference.hbs b/share/mrdocs/addons/generator/common/partials/doc/inline/footnote-reference.hbs new file mode 100644 index 000000000..31a6db315 --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/doc/inline/footnote-reference.hbs @@ -0,0 +1 @@ +{{#> markup/i }}{{literal}}{{/ markup/i }} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/doc/inline/highlight.hbs b/share/mrdocs/addons/generator/common/partials/doc/inline/highlight.hbs new file mode 100644 index 000000000..2bd7f82f1 --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/doc/inline/highlight.hbs @@ -0,0 +1 @@ +{{#> markup/i }}{{> doc/inline-container }}{{/ markup/i }} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/doc/inline/image.hbs b/share/mrdocs/addons/generator/common/partials/doc/inline/image.hbs new file mode 100644 index 000000000..2bd7f82f1 --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/doc/inline/image.hbs @@ -0,0 +1 @@ +{{#> markup/i }}{{> doc/inline-container }}{{/ markup/i }} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/doc/inline/line-break.hbs b/share/mrdocs/addons/generator/common/partials/doc/inline/line-break.hbs new file mode 100644 index 000000000..e69de29bb diff --git a/share/mrdocs/addons/generator/common/partials/doc/inline/link.hbs b/share/mrdocs/addons/generator/common/partials/doc/inline/link.hbs new file mode 100644 index 000000000..ab8706785 --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/doc/inline/link.hbs @@ -0,0 +1 @@ +{{#>markup/a href=href}}{{> doc/inline-container }}{{/markup/a}} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/doc/inline/math.hbs b/share/mrdocs/addons/generator/common/partials/doc/inline/math.hbs new file mode 100644 index 000000000..31a6db315 --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/doc/inline/math.hbs @@ -0,0 +1 @@ +{{#> markup/i }}{{literal}}{{/ markup/i }} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/doc/inline/reference.hbs b/share/mrdocs/addons/generator/common/partials/doc/inline/reference.hbs new file mode 100644 index 000000000..942630119 --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/doc/inline/reference.hbs @@ -0,0 +1,5 @@ +{{#if symbol.url~}} +{{#>markup/a href=symbol.url}}{{#>markup/code}}{{ literal }}{{/markup/code}}{{/markup/a}} +{{~else~}} +{{#>markup/code}}{{ literal }}{{/markup/code}} +{{~/if}} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/doc/inline/soft-break.hbs b/share/mrdocs/addons/generator/common/partials/doc/inline/soft-break.hbs new file mode 100644 index 000000000..e69de29bb diff --git a/share/mrdocs/addons/generator/common/partials/doc/inline/strikethrough.hbs b/share/mrdocs/addons/generator/common/partials/doc/inline/strikethrough.hbs new file mode 100644 index 000000000..2bd7f82f1 --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/doc/inline/strikethrough.hbs @@ -0,0 +1 @@ +{{#> markup/i }}{{> doc/inline-container }}{{/ markup/i }} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/doc/inline/strong.hbs b/share/mrdocs/addons/generator/common/partials/doc/inline/strong.hbs new file mode 100644 index 000000000..ef363be65 --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/doc/inline/strong.hbs @@ -0,0 +1 @@ +{{#> markup/b }}{{> doc/inline-container }}{{/ markup/b }} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/doc/inline/subscript.hbs b/share/mrdocs/addons/generator/common/partials/doc/inline/subscript.hbs new file mode 100644 index 000000000..2bd7f82f1 --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/doc/inline/subscript.hbs @@ -0,0 +1 @@ +{{#> markup/i }}{{> doc/inline-container }}{{/ markup/i }} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/doc/inline/superscript.hbs b/share/mrdocs/addons/generator/common/partials/doc/inline/superscript.hbs new file mode 100644 index 000000000..2bd7f82f1 --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/doc/inline/superscript.hbs @@ -0,0 +1 @@ +{{#> markup/i }}{{> doc/inline-container }}{{/ markup/i }} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/doc/inline/text.hbs b/share/mrdocs/addons/generator/common/partials/doc/inline/text.hbs new file mode 100644 index 000000000..4ee9c5180 --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/doc/inline/text.hbs @@ -0,0 +1 @@ +{{literal}} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/javadoc/any-block.hbs b/share/mrdocs/addons/generator/common/partials/javadoc/any-block.hbs deleted file mode 100644 index e232ea71d..000000000 --- a/share/mrdocs/addons/generator/common/partials/javadoc/any-block.hbs +++ /dev/null @@ -1 +0,0 @@ -{{> (concat 'javadoc/' kind) }} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/javadoc/any-text.hbs b/share/mrdocs/addons/generator/common/partials/javadoc/any-text.hbs deleted file mode 100644 index e232ea71d..000000000 --- a/share/mrdocs/addons/generator/common/partials/javadoc/any-text.hbs +++ /dev/null @@ -1 +0,0 @@ -{{> (concat 'javadoc/' kind) }} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/javadoc/block.hbs b/share/mrdocs/addons/generator/common/partials/javadoc/block.hbs deleted file mode 100644 index 0475cb215..000000000 --- a/share/mrdocs/addons/generator/common/partials/javadoc/block.hbs +++ /dev/null @@ -1 +0,0 @@ -{{#>markup/span class=undefined}}{{#each children}}{{>javadoc/any-text }}{{/each}}{{/markup/span}} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/javadoc/brief.hbs b/share/mrdocs/addons/generator/common/partials/javadoc/brief.hbs deleted file mode 100644 index d19c8494c..000000000 --- a/share/mrdocs/addons/generator/common/partials/javadoc/brief.hbs +++ /dev/null @@ -1 +0,0 @@ -{{>javadoc/block}} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/javadoc/inline-brief.hbs b/share/mrdocs/addons/generator/common/partials/javadoc/inline-brief.hbs deleted file mode 100644 index d19c8494c..000000000 --- a/share/mrdocs/addons/generator/common/partials/javadoc/inline-brief.hbs +++ /dev/null @@ -1 +0,0 @@ -{{>javadoc/block}} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/javadoc/link.hbs b/share/mrdocs/addons/generator/common/partials/javadoc/link.hbs deleted file mode 100644 index f4fb3636c..000000000 --- a/share/mrdocs/addons/generator/common/partials/javadoc/link.hbs +++ /dev/null @@ -1 +0,0 @@ -{{#>markup/a href=href}}{{> javadoc/block }}{{/markup/a}} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/javadoc/reference.hbs b/share/mrdocs/addons/generator/common/partials/javadoc/reference.hbs deleted file mode 100644 index e1d877236..000000000 --- a/share/mrdocs/addons/generator/common/partials/javadoc/reference.hbs +++ /dev/null @@ -1,5 +0,0 @@ -{{#if symbol.url~}} -{{#>markup/a href=symbol.url}}{{#>markup/code}}{{string}}{{/markup/code}}{{/markup/a}} -{{~else~}} -{{#>markup/code}}{{string}}{{/markup/code}} -{{~/if}} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/javadoc/returns.hbs b/share/mrdocs/addons/generator/common/partials/javadoc/returns.hbs deleted file mode 100644 index d19c8494c..000000000 --- a/share/mrdocs/addons/generator/common/partials/javadoc/returns.hbs +++ /dev/null @@ -1 +0,0 @@ -{{>javadoc/block}} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/javadoc/see.hbs b/share/mrdocs/addons/generator/common/partials/javadoc/see.hbs deleted file mode 100644 index c15e96828..000000000 --- a/share/mrdocs/addons/generator/common/partials/javadoc/see.hbs +++ /dev/null @@ -1 +0,0 @@ -{{>javadoc/paragraph}} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/javadoc/styled.hbs b/share/mrdocs/addons/generator/common/partials/javadoc/styled.hbs deleted file mode 100644 index 1a11bfe15..000000000 --- a/share/mrdocs/addons/generator/common/partials/javadoc/styled.hbs +++ /dev/null @@ -1 +0,0 @@ -{{> (concat 'javadoc/styled/' style) }} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/javadoc/styled/bold.hbs b/share/mrdocs/addons/generator/common/partials/javadoc/styled/bold.hbs deleted file mode 100644 index 2e32e2505..000000000 --- a/share/mrdocs/addons/generator/common/partials/javadoc/styled/bold.hbs +++ /dev/null @@ -1 +0,0 @@ -{{#> markup/b }}{{string}}{{/ markup/b }} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/javadoc/styled/italic.hbs b/share/mrdocs/addons/generator/common/partials/javadoc/styled/italic.hbs deleted file mode 100644 index 63f67e78e..000000000 --- a/share/mrdocs/addons/generator/common/partials/javadoc/styled/italic.hbs +++ /dev/null @@ -1 +0,0 @@ -{{#> markup/i }}{{string}}{{/ markup/i }} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/javadoc/styled/mono.hbs b/share/mrdocs/addons/generator/common/partials/javadoc/styled/mono.hbs deleted file mode 100644 index 291958e65..000000000 --- a/share/mrdocs/addons/generator/common/partials/javadoc/styled/mono.hbs +++ /dev/null @@ -1 +0,0 @@ -{{#> markup/code }}{{string}}{{/ markup/code }} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/javadoc/text.hbs b/share/mrdocs/addons/generator/common/partials/javadoc/text.hbs deleted file mode 100644 index c6019f9fa..000000000 --- a/share/mrdocs/addons/generator/common/partials/javadoc/text.hbs +++ /dev/null @@ -1 +0,0 @@ -{{#unless verbatim}}{{string}}{{else}}{{{string}}}{{/unless}} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/markup/dynamic-level-h.hbs b/share/mrdocs/addons/generator/common/partials/markup/dynamic-level-h.hbs index fce6d34e3..ce78690c4 100644 --- a/share/mrdocs/addons/generator/common/partials/markup/dynamic-level-h.hbs +++ b/share/mrdocs/addons/generator/common/partials/markup/dynamic-level-h.hbs @@ -19,7 +19,7 @@ See: https://mrdocs.com/docs/mrdocs/develop/generators.html#dom_reference --}} {{#> markup/h - level=(select @root.config.multipage (or level 1) (add (or level 1) 1)) + level=(add (or level 1) (select @root.config.multipage 0 1)) id=id }} {{> @partial-block }} diff --git a/share/mrdocs/addons/generator/common/partials/symbol/detail/members-table-row.hbs b/share/mrdocs/addons/generator/common/partials/symbol/detail/members-table-row.hbs index 52fb4bd6e..568f395a3 100644 --- a/share/mrdocs/addons/generator/common/partials/symbol/detail/members-table-row.hbs +++ b/share/mrdocs/addons/generator/common/partials/symbol/detail/members-table-row.hbs @@ -15,7 +15,7 @@ {{~/markup/td}} {{#if includeBrief~}} {{~#>markup/td~}} -{{~> javadoc/inline-brief doc.brief~}} +{{~> doc/block/inline-brief doc.brief~}} {{~/markup/td}} {{/if}} {{/markup/tr}} diff --git a/share/mrdocs/addons/generator/common/partials/symbol/members-table.hbs b/share/mrdocs/addons/generator/common/partials/symbol/members-table.hbs index fb8f53584..daa5418c0 100644 --- a/share/mrdocs/addons/generator/common/partials/symbol/members-table.hbs +++ b/share/mrdocs/addons/generator/common/partials/symbol/members-table.hbs @@ -4,7 +4,7 @@ This partials renders each row with the symbol name and brief for each member. - Expected Context: {Symbol|NameInfo}[] + Expected Context: {Symbol|Name}[] Parameters: members: An array of symbols or nameinfo to render. @@ -17,7 +17,7 @@ {{#if members}} {{#if (or (eq members[0].class "name") (any_of_by members "isRegular" "isSeeBelow"))}} {{#>markup/h level=(select @root.config.multipage (select traversing-global-namespace 2 1) 2)}}{{title}}{{/markup/h}} -{{>symbol/detail/members-table-impl members=members isName=(eq members[0].class "name") includeBrief=(select (any_of_by members "doc") true false) title=title}} +{{~>symbol/detail/members-table-impl members=members isName=(eq members[0].class "name") includeBrief=(select (any_of_by members "doc.brief") true false) title=title}} {{/if}} {{/if}} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/symbol/signatures.hbs b/share/mrdocs/addons/generator/common/partials/symbol/signatures.hbs index a75a16990..2f5a2cec5 100644 --- a/share/mrdocs/addons/generator/common/partials/symbol/signatures.hbs +++ b/share/mrdocs/addons/generator/common/partials/symbol/signatures.hbs @@ -18,7 +18,7 @@ {{/markup/code-block}} {{else}} {{#each members as | member |}} -{{#if member.doc.brief}}{{> javadoc/brief member.doc.brief }}{{/if}} +{{#if member.doc.brief}}{{> doc/block/brief member.doc.brief }}{{/if}} {{#> markup/code-block }} {{> symbol/signature/function member force-link=true }} {{/markup/code-block}} diff --git a/share/mrdocs/addons/generator/common/partials/template/arg.hbs b/share/mrdocs/addons/generator/common/partials/template/arg.hbs index 601600679..4b6c55f07 100644 --- a/share/mrdocs/addons/generator/common/partials/template/arg.hbs +++ b/share/mrdocs/addons/generator/common/partials/template/arg.hbs @@ -13,8 +13,8 @@ {{#if (eq kind "type")~}} {{! TArg is a type: render the declarator ~}} {{~>type/declarator type link-components-impl=link-components-impl~}} -{{else if (eq kind "non-type")~}} - {{! TArg is a non-type: render the value string as is ~}} +{{else if (eq kind "constant")~}} + {{! TArg is a constant: render the value string as is ~}} {{~value~}} {{else if (eq kind "template")~}} {{! TArg is another template: render the template head ~}} diff --git a/share/mrdocs/addons/generator/common/partials/template/param-text.hbs b/share/mrdocs/addons/generator/common/partials/template/param-text.hbs index 1f11c5272..458628f27 100644 --- a/share/mrdocs/addons/generator/common/partials/template/param-text.hbs +++ b/share/mrdocs/addons/generator/common/partials/template/param-text.hbs @@ -2,7 +2,7 @@ Renders a template parameter as text. If the template parameter is a type, the templated type name is rendered. - If the template parameter is a non-type, the declarator of the type is rendered. + If the template parameter is a constant, the declarator of the type is rendered. If the template parameter is a template, the template head is rendered. Expected Context: {TParam} diff --git a/share/mrdocs/addons/generator/common/partials/template/param.hbs b/share/mrdocs/addons/generator/common/partials/template/param.hbs index 808e7c533..83178c7a4 100644 --- a/share/mrdocs/addons/generator/common/partials/template/param.hbs +++ b/share/mrdocs/addons/generator/common/partials/template/param.hbs @@ -2,7 +2,7 @@ Renders a template parameter with links to any named components. If the template parameter is a type, the templated type name is rendered. - If the template parameter is a non-type, the declarator of the type is rendered. + If the template parameter is a constant, the declarator of the type is rendered. If the template parameter is a template, the template head is rendered. Expected Context: {TParam} @@ -17,7 +17,7 @@ {{#if is-pack}}{{ str "..." }}{{/if~}} {{#if name}} {{name}}{{/if~}} {{#if default}} = {{>template/arg default link-components-impl=link-components-impl ~}}{{/if~}} -{{else if (eq kind "non-type")~}} +{{else if (eq kind "constant")~}} {{>type/declarator type decl-name=name link-components-impl=link-components-impl }}{{#if is-pack}}{{ str "..." }}{{/if~}} {{#if default}} = {{>template/arg default link-components-impl=link-components-impl~}}{{/if~}} {{else if (eq kind "template")~}} diff --git a/share/mrdocs/addons/generator/html/partials/javadoc/admonition.html.hbs b/share/mrdocs/addons/generator/html/partials/doc/block/admonition.html.hbs similarity index 50% rename from share/mrdocs/addons/generator/html/partials/javadoc/admonition.html.hbs rename to share/mrdocs/addons/generator/html/partials/doc/block/admonition.html.hbs index 92e022b0c..4f6bef73d 100644 --- a/share/mrdocs/addons/generator/html/partials/javadoc/admonition.html.hbs +++ b/share/mrdocs/addons/generator/html/partials/doc/block/admonition.html.hbs @@ -1,4 +1,5 @@

{{upper admonish}}

-

{{#each children}}{{> javadoc/any-text }}{{/each}}

+
+{{> doc/block-container ~}}
\ No newline at end of file diff --git a/share/mrdocs/addons/generator/html/partials/doc/block/code.html.hbs b/share/mrdocs/addons/generator/html/partials/doc/block/code.html.hbs new file mode 100644 index 000000000..daabda628 --- /dev/null +++ b/share/mrdocs/addons/generator/html/partials/doc/block/code.html.hbs @@ -0,0 +1,3 @@ + +{{{ replace (replace (replace literal "&" "&") ">" ">") "<" "<" }}} + \ No newline at end of file diff --git a/share/mrdocs/addons/generator/html/partials/doc/block/list.html.hbs b/share/mrdocs/addons/generator/html/partials/doc/block/list.html.hbs new file mode 100644 index 000000000..6711340cb --- /dev/null +++ b/share/mrdocs/addons/generator/html/partials/doc/block/list.html.hbs @@ -0,0 +1,4 @@ +
    +{{#each items}}
  • {{#each blocks}}{{> doc/block }}{{/each}}
  • +{{/each}} +
\ No newline at end of file diff --git a/share/mrdocs/addons/generator/html/partials/doc/block/paragraph.html.hbs b/share/mrdocs/addons/generator/html/partials/doc/block/paragraph.html.hbs new file mode 100644 index 000000000..63e7cbbf7 --- /dev/null +++ b/share/mrdocs/addons/generator/html/partials/doc/block/paragraph.html.hbs @@ -0,0 +1 @@ +{{#>markup/p}}{{#each children}}{{> doc/inline }}{{/each}}{{/markup/p}} diff --git a/share/mrdocs/addons/generator/html/partials/javadoc/code.html.hbs b/share/mrdocs/addons/generator/html/partials/javadoc/code.html.hbs deleted file mode 100644 index 586376c5f..000000000 --- a/share/mrdocs/addons/generator/html/partials/javadoc/code.html.hbs +++ /dev/null @@ -1,4 +0,0 @@ - -{{#each children}}{{> javadoc/any-text verbatim=true }} -{{/each}} - \ No newline at end of file diff --git a/share/mrdocs/addons/generator/html/partials/javadoc/description.html.hbs b/share/mrdocs/addons/generator/html/partials/javadoc/description.html.hbs deleted file mode 100644 index 2ecb541c2..000000000 --- a/share/mrdocs/addons/generator/html/partials/javadoc/description.html.hbs +++ /dev/null @@ -1,2 +0,0 @@ -{{#each .}}{{>javadoc/any-block .}} -{{/each}} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/html/partials/javadoc/heading.html.hbs b/share/mrdocs/addons/generator/html/partials/javadoc/heading.html.hbs deleted file mode 100644 index 075f69652..000000000 --- a/share/mrdocs/addons/generator/html/partials/javadoc/heading.html.hbs +++ /dev/null @@ -1 +0,0 @@ -

{{string}}

\ No newline at end of file diff --git a/share/mrdocs/addons/generator/html/partials/javadoc/paragraph.html.hbs b/share/mrdocs/addons/generator/html/partials/javadoc/paragraph.html.hbs deleted file mode 100644 index 52e81cd45..000000000 --- a/share/mrdocs/addons/generator/html/partials/javadoc/paragraph.html.hbs +++ /dev/null @@ -1 +0,0 @@ -{{#>markup/p}}{{#each children}}{{> javadoc/any-text }}{{/each}}{{/markup/p}} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/html/partials/javadoc/unordered_list.html.hbs b/share/mrdocs/addons/generator/html/partials/javadoc/unordered_list.html.hbs deleted file mode 100644 index 20244bbe0..000000000 --- a/share/mrdocs/addons/generator/html/partials/javadoc/unordered_list.html.hbs +++ /dev/null @@ -1,4 +0,0 @@ -
    -{{#each items}}
  • {{> javadoc/block }}
  • -{{/each}} -
\ No newline at end of file diff --git a/share/mrdocs/addons/generator/html/partials/markup/a.html.hbs b/share/mrdocs/addons/generator/html/partials/markup/a.html.hbs index 03626d5ca..fcfdd3283 100644 --- a/share/mrdocs/addons/generator/html/partials/markup/a.html.hbs +++ b/share/mrdocs/addons/generator/html/partials/markup/a.html.hbs @@ -1 +1,5 @@ -{{> @partial-block }} \ No newline at end of file +{{#if (eq href @root.symbol.url)~}} + {{{> @partial-block }}} +{{~else~}} + {{> @partial-block }} +{{~/if~}} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/html/partials/markup/code-block.html.hbs b/share/mrdocs/addons/generator/html/partials/markup/code-block.html.hbs index d1dbe4e22..e1992ae29 100644 --- a/share/mrdocs/addons/generator/html/partials/markup/code-block.html.hbs +++ b/share/mrdocs/addons/generator/html/partials/markup/code-block.html.hbs @@ -1,5 +1,3 @@
-{{> @partial-block }}
-
-
+{{> @partial-block }}
 
\ No newline at end of file diff --git a/share/mrdocs/addons/generator/html/partials/markup/h.html.hbs b/share/mrdocs/addons/generator/html/partials/markup/h.html.hbs index 41f84c971..996a129cc 100644 --- a/share/mrdocs/addons/generator/html/partials/markup/h.html.hbs +++ b/share/mrdocs/addons/generator/html/partials/markup/h.html.hbs @@ -1 +1 @@ -{{> @partial-block }} \ No newline at end of file +{{> @partial-block }} diff --git a/share/mrdocs/addons/generator/html/partials/symbol.html.hbs b/share/mrdocs/addons/generator/html/partials/symbol.html.hbs index da0093e42..3f51a4f99 100644 --- a/share/mrdocs/addons/generator/html/partials/symbol.html.hbs +++ b/share/mrdocs/addons/generator/html/partials/symbol.html.hbs @@ -17,12 +17,11 @@
{{#> markup/h level=2 id=symbol.anchor }}{{> symbol/qualified-name symbol }}{{/markup/h}} -{{/unless}} +{{~/unless}} {{! Brief }} {{#if symbol.doc.brief}}
-{{> javadoc/brief symbol.doc.brief }} - +{{> doc/block/brief symbol.doc.brief }}
{{/if}} @@ -32,7 +31,7 @@ {{! Synopsis }} {{#unless (eq symbol.kind "namespace")}}
-{{#> markup/dynamic-level-h level=2 }}{{#if (ne symbol.kind "overloads")}}Synopsis{{else}}Synopses{{/if}}{{/markup/dynamic-level-h}} +{{#> markup/dynamic-level-h level=2 }}{{#if (ne symbol.kind "overloads")}}Synopsis{{else}}Synopses{{/if}}{{/markup/dynamic-level-h~}}
{{>location/source dcl=(primary_location symbol)}}
@@ -43,7 +42,7 @@ {{! Base classes }} {{#if (any_of_by symbol.bases "isPublic")}}
-{{#> markup/dynamic-level-h }}Base Classes{{/markup/dynamic-level-h}} +{{#> markup/dynamic-level-h }}Base Classes{{/markup/dynamic-level-h~}} @@ -53,7 +52,7 @@ {{#each (filter_by symbol.bases "isPublic")}} - + {{/each}}
{{#>markup/code}}{{> type/declarator type }}{{/markup/code}}{{> javadoc/inline-brief symbol.doc.brief }}
{{#>markup/code}}{{> type/declarator type }}{{/markup/code}}{{> doc/block/inline-brief type.name.symbol.doc.brief }}
@@ -62,7 +61,7 @@ {{! Protected Base classes }} {{#if (any_of_by symbol.bases "isProtected")}}
-{{#> markup/dynamic-level-h }}Protected Base Classes{{/markup/dynamic-level-h}} +{{#> markup/dynamic-level-h }}Protected Base Classes{{/markup/dynamic-level-h~}} @@ -72,7 +71,7 @@ {{#each (filter_by symbol.bases "isProtected")}} - + {{/each}}
{{#>markup/code}}{{> type/declarator type }}{{/markup/code}}{{> javadoc/inline-brief symbol.doc.brief }}
{{#>markup/code}}{{> type/declarator type }}{{/markup/code}}{{> doc/block/inline-brief type.name.symbol.doc.brief }}
@@ -93,30 +92,12 @@ {{>symbol/tranche tranche=symbol.members label="" is-namespace=true}} {{! Enum members }} {{else if (eq symbol.kind "enum")}} -
-{{#> markup/dynamic-level-h level=2 }}Members{{/markup/dynamic-level-h}} - - - - - - - - -{{#each (filter_by symbol.constants "isRegular" "isSeeBelow")}} - - - - -{{/each}} - -
NameDescription
{{>symbol/name-text .}}{{> javadoc/inline-brief doc.brief }}
-
+{{>symbol/members-table members=symbol.constants title="Members"}} {{/if}} {{! Friends }} {{#if symbol.friends}}
-{{#> markup/dynamic-level-h }}Friends{{/markup/dynamic-level-h}} +{{#> markup/dynamic-level-h }}Friends{{/markup/dynamic-level-h~}} @@ -129,7 +110,7 @@ {{#if symbol}} - + {{else}} @@ -152,7 +133,7 @@ {{! Related symbols }} {{#if symbol.doc.related}}
-{{#> markup/dynamic-level-h }}Non-Member Functions{{/markup/dynamic-level-h}} +{{#> markup/dynamic-level-h }}Non-Member Functions{{/markup/dynamic-level-h~}}
{{#>markup/code}}{{> symbol/name symbol }}{{/markup/code}}{{> javadoc/inline-brief symbol.doc.brief }}{{> doc/block/inline-brief symbol.doc.brief }}{{#>markup/code}}{{> type/declarator type }}{{/markup/code}}
@@ -162,7 +143,7 @@ {{#each symbol.doc.related}} - + {{/each}}
{{> javadoc/reference }}{{> javadoc/inline-brief symbol.doc.brief }}
{{> doc/inline/reference }}{{> doc/block/inline-brief symbol.doc.brief }}
@@ -171,7 +152,7 @@ {{! Derived classes }} {{#if symbol.derived}}
-{{#> markup/dynamic-level-h }}Derived Classes{{/markup/dynamic-level-h}} +{{#> markup/dynamic-level-h }}Derived Classes{{/markup/dynamic-level-h~}} @@ -185,7 +166,7 @@ {{#>markup/a href=url}}{{#>markup/code}}{{> type/name-info-text . }}{{/markup/code}}{{/markup/a}} {{else~}} {{> type/name-info-text . }} -{{/if}} +{{/if}} {{/each}}
{{> javadoc/inline-brief doc.brief }}
{{> doc/block/inline-brief doc.brief }}
@@ -195,15 +176,17 @@ {{#if symbol.doc.description}} {{#unless traversing-global-namespace}}
-{{#> markup/dynamic-level-h level=2 }}Description{{/markup/dynamic-level-h}} -{{> javadoc/description symbol.doc.description }} +{{#unless (eq (lookup (first symbol.doc.description) "kind") "heading") }} +{{#> markup/dynamic-level-h level=2 }}Description{{/markup/dynamic-level-h~}} +{{~/unless}} +{{> doc/block/document symbol.doc.description }}
{{/unless}} {{/if}} {{! Using symbols }} {{#if symbol.shadows}}
-{{#> markup/dynamic-level-h level=2 }}Introduced Symbols{{/markup/dynamic-level-h}} +{{#> markup/dynamic-level-h level=2 }}Introduced Symbols{{/markup/dynamic-level-h~}} {{#if (any_of_by symbol.shadows "doc")}} @@ -216,7 +199,7 @@ {{#each symbol.shadows}} - + {{/each}} @@ -243,7 +226,7 @@ {{! Exceptions }} {{#if symbol.doc.exceptions}}
-{{#> markup/dynamic-level-h level=2 }}Exceptions{{/markup/dynamic-level-h}} +{{#> markup/dynamic-level-h level=2 }}Exceptions{{/markup/dynamic-level-h~}}
{{> symbol/qualified-name . }}{{> javadoc/inline-brief doc.brief }}{{> doc/block/inline-brief doc.brief }}
@@ -254,8 +237,8 @@ {{#each symbol.doc.exceptions}} - - + + {{/each}} @@ -265,14 +248,14 @@ {{! Return value }} {{#if symbol.doc.returns}}
-{{#> markup/dynamic-level-h level=2 }}Return Value{{/markup/dynamic-level-h}} +{{#> markup/dynamic-level-h level=2 }}Return Value{{/markup/dynamic-level-h~}} {{#if (eq (size symbol.doc.returns) 1)}} -{{> javadoc/returns symbol.doc.returns.[0]}} +{{> doc/block/returns symbol.doc.returns.[0]}} {{else}}
    {{#each symbol.doc.returns}} -
  • {{> javadoc/returns .}}
  • +
  • {{> doc/block/returns .}}
  • {{/each}}
{{/if}} @@ -281,7 +264,7 @@ {{! Template Parameters }} {{#if symbol.doc.tparams}}
-{{#> markup/dynamic-level-h level=2 }}Template Parameters{{/markup/dynamic-level-h}} +{{#> markup/dynamic-level-h level=2 }}Template Parameters{{/markup/dynamic-level-h~}}
{{> javadoc/reference exception}}{{> javadoc/block .}}{{> doc/inline/reference exception}}{{> doc/block .}}
@@ -293,7 +276,7 @@ {{#each symbol.doc.tparams}} - + {{/each}} @@ -303,7 +286,7 @@ {{! Parameters }} {{#if symbol.doc.params}}
-{{#> markup/dynamic-level-h level=2 }}Parameters{{/markup/dynamic-level-h}} +{{#> markup/dynamic-level-h level=2 }}Parameters{{/markup/dynamic-level-h~}}
{{name}}{{>javadoc/block .}}{{>doc/block .}}
@@ -315,7 +298,7 @@ {{#each symbol.doc.params}} - + {{/each}} @@ -325,10 +308,10 @@ {{! Preconditions }} {{#if symbol.doc.preconditions}}
-{{#> markup/dynamic-level-h level=2 }}Preconditions{{/markup/dynamic-level-h}} +{{#> markup/dynamic-level-h level=2 }}Preconditions{{/markup/dynamic-level-h~}}
    {{#each symbol.doc.preconditions}} -
  • {{>javadoc/block .}}
  • +
  • {{>doc/block .}}
  • {{/each}}
@@ -336,10 +319,10 @@ {{! Postconditions }} {{#if symbol.doc.postconditions}}
-{{#> markup/dynamic-level-h level=2 }}Postconditions{{/markup/dynamic-level-h}} +{{#> markup/dynamic-level-h level=2 }}Postconditions{{/markup/dynamic-level-h~}}
    {{#each symbol.doc.postconditions}} -
  • {{>javadoc/block .}}
  • +
  • {{>doc/block .}}
  • {{/each}}
@@ -347,9 +330,9 @@ {{! See Also }} {{#if symbol.doc.sees}}
-{{#> markup/dynamic-level-h level=2 }}See Also{{/markup/dynamic-level-h}} +{{#> markup/dynamic-level-h level=2 }}See Also{{/markup/dynamic-level-h~}} {{#each symbol.doc.sees}} -

{{>javadoc/block .}}

+

{{>doc/block .}}

{{/each}}
{{/if}} @@ -357,7 +340,7 @@ {{#if (and (eq symbol.kind "namespace") @root.config.multipage @root.config.global-namespace-index (eq @root.symbol.kind "namespace") (not @root.symbol.name) (not @root.symbol.parent)) }} {{#each symbol.members.namespaces}} {{#if isRegular}} -{{#> markup/dynamic-level-h id=null }}{{> symbol/qualified-name . }} namespace{{/markup/dynamic-level-h}} +{{#> markup/dynamic-level-h id=null }}{{> symbol/qualified-name . }} namespace{{/markup/dynamic-level-h~}} {{> symbol symbol=. id=null traversing-global-namespace=true }} {{/if}} {{/each}} diff --git a/src/lib/AST/ASTAction.cpp b/src/lib/AST/ASTAction.cpp index c8468014f..0dfa6a68a 100644 --- a/src/lib/AST/ASTAction.cpp +++ b/src/lib/AST/ASTAction.cpp @@ -11,21 +11,21 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include "lib/AST/ASTAction.hpp" -#include "lib/AST/MrDocsFileSystem.hpp" -#include "lib/AST/ASTVisitorConsumer.hpp" +#include +#include +#include #include -#include #include +#include + -namespace clang { namespace mrdocs { void ASTAction:: ExecuteAction() { - CompilerInstance& CI = getCompilerInstance(); + clang::CompilerInstance& CI = getCompilerInstance(); if (!CI.hasPreprocessor()) { return; @@ -100,4 +100,4 @@ CreateASTConsumer( } } // mrdocs -} // clang + diff --git a/src/lib/AST/ASTAction.hpp b/src/lib/AST/ASTAction.hpp index b06154924..fd87e63aa 100644 --- a/src/lib/AST/ASTAction.hpp +++ b/src/lib/AST/ASTAction.hpp @@ -14,14 +14,13 @@ #ifndef MRDOCS_LIB_AST_ASTACTION_HPP #define MRDOCS_LIB_AST_ASTACTION_HPP -#include "lib/ConfigImpl.hpp" -#include "lib/Support/ExecutionContext.hpp" -#include #include -#include "MissingSymbolSink.hpp" +#include +#include +#include +#include -namespace clang { namespace mrdocs { /** The clang frontend action for visiting the AST @@ -96,6 +95,6 @@ class ASTAction }; } // mrdocs -} // clang -#endif + +#endif // MRDOCS_LIB_AST_ASTACTION_HPP diff --git a/src/lib/AST/ASTVisitor.cpp b/src/lib/AST/ASTVisitor.cpp index a33826061..508f77b81 100644 --- a/src/lib/AST/ASTVisitor.cpp +++ b/src/lib/AST/ASTVisitor.cpp @@ -11,18 +11,18 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include "lib/AST/ASTVisitor.hpp" -#include "lib/AST/NameInfoBuilder.hpp" -#include "lib/AST/ClangHelpers.hpp" -#include "lib/AST/ParseJavadoc.hpp" -#include "lib/AST/TypeInfoBuilder.hpp" -#include "lib/Support/Path.hpp" -#include "lib/Support/Debug.hpp" -#include "lib/Support/Radix.hpp" -#include "lib/Diagnostics.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include #include -#include #include +#include #include #include #include @@ -35,22 +35,22 @@ #include #include #include -#include #include +#include #include #include #include #include -namespace clang::mrdocs { +namespace mrdocs { ASTVisitor:: ASTVisitor( ConfigImpl const& config, Diagnostics const& diags, - CompilerInstance& compiler, - ASTContext& context, - Sema& sema) noexcept + clang::CompilerInstance& compiler, + clang::ASTContext& context, + clang::Sema& sema) noexcept : config_(config) , diags_(diags) , compiler_(compiler) @@ -62,12 +62,12 @@ ASTVisitor( initCustomCommentCommands(context_); // The traversal scope should *only* consist of the - // top-level TranslationUnitDecl. + // top-level clang::TranslationUnitDecl. // If this `assert` fires, then it means // ASTContext::setTraversalScope is being (erroneously) // used somewhere MRDOCS_ASSERT(context_.getTraversalScope() == - ArrayRef{context_.getTranslationUnitDecl()}); + llvm::ArrayRef{context_.getTranslationUnitDecl()}); } void @@ -77,15 +77,15 @@ build() // Traverse the translation unit, only extracting // declarations which satisfy all filter conditions. // dependencies will be tracked, but not extracted - TranslationUnitDecl const* TU = context_.getTranslationUnitDecl(); + clang::TranslationUnitDecl const* TU = context_.getTranslationUnitDecl(); traverse(TU); MRDOCS_ASSERT(find(SymbolID::global)); } template < class InfoTy, - std::derived_from DeclTy> -Info* + std::derived_from DeclTy> +Symbol* ASTVisitor:: traverse(DeclTy const* D) { @@ -93,20 +93,20 @@ traverse(DeclTy const* D) MRDOCS_CHECK_OR(!D->isInvalidDecl(), nullptr); MRDOCS_SYMBOL_TRACE(D, context_); - if constexpr (std::same_as) + if constexpr (std::same_as) { // Convert to the most derived type of the Decl // and call the appropriate traverse function - return visit(D, [&](DeclTyU* U) -> Info* + return visit(D, [&](DeclTyU* U) -> Symbol* { - if constexpr (!std::same_as) + if constexpr (!std::same_as) { return traverse(U); } return nullptr; }); } - else if constexpr (HasInfoTypeFor || std::derived_from) + else if constexpr (HasInfoTypeFor || std::derived_from) { // If the declaration has a corresponding Info type, // we build the Info object and populate it with the @@ -143,45 +143,46 @@ traverse(DeclTy const* D) return nullptr; } -Info* +Symbol* ASTVisitor:: -traverse(FunctionTemplateDecl const* D) +traverse(clang::FunctionTemplateDecl const* D) { - // Route the traversal to GuideInfo or FunctionInfo - if (FunctionDecl* FD = D->getTemplatedDecl(); - FD && isa(FD)) + // Route the traversal to GuideSymbol or FunctionSymbol + if (clang::FunctionDecl* FD = D->getTemplatedDecl(); + FD && isa(FD)) { - return traverse(D); + return traverse(D); } - return traverse(D); + return traverse(D); } -Info* +Symbol* ASTVisitor:: -traverse(UsingDirectiveDecl const* D) +traverse(clang::UsingDirectiveDecl const* D) { MRDOCS_SYMBOL_TRACE(D, context_); // Find the parent namespace ScopeExitRestore s1(mode_, TraversalMode::Dependency); - Decl const* P = getParent(D); + clang::Decl const* P = getParent(D); MRDOCS_SYMBOL_TRACE(P, context_); - Info* PI = findOrTraverse(P); + Symbol* PI = findOrTraverse(P); MRDOCS_CHECK_OR(PI, nullptr); MRDOCS_CHECK_OR(PI->isNamespace(), nullptr); auto& PNI = PI->asNamespace(); // Find the nominated namespace - Decl const* ND = D->getNominatedNamespace(); + clang::Decl const* ND = D->getNominatedNamespace(); MRDOCS_SYMBOL_TRACE(ND, context_); ScopeExitRestore s2(mode_, TraversalMode::Dependency); - Info* NDI = findOrTraverse(ND); + Symbol* NDI = findOrTraverse(ND); MRDOCS_CHECK_OR(NDI, nullptr); - auto res = toNameInfo(ND); - MRDOCS_ASSERT(res); - MRDOCS_ASSERT(res->isIdentifier()); - if (NameInfo NI = *res; + Optional> res = toName(ND); + MRDOCS_CHECK_OR(res, nullptr); + MRDOCS_ASSERT(!res->valueless_after_move()); + MRDOCS_ASSERT((*res)->isIdentifier()); + if (Name NI = **res; !contains(PNI.UsingDirectives, NI)) { PNI.UsingDirectives.push_back(std::move(NI)); @@ -189,17 +190,17 @@ traverse(UsingDirectiveDecl const* D) return nullptr; } -Info* +Symbol* ASTVisitor:: -traverse(IndirectFieldDecl const* D) +traverse(clang::IndirectFieldDecl const* D) { return traverse(D->getAnonField()); } template < - std::derived_from InfoTy, - std::derived_from DeclTy> -requires (!std::derived_from) + std::derived_from InfoTy, + std::derived_from DeclTy> +requires (!std::derived_from) void ASTVisitor:: traverseMembers(InfoTy& I, DeclTy const* DC) @@ -208,14 +209,14 @@ traverseMembers(InfoTy& I, DeclTy const* DC) // not traverse its members as function arguments are // not main Info members. if constexpr ( - !std::derived_from && - std::derived_from) + !std::derived_from && + std::derived_from) { // We only need members of regular symbols and see-below namespaces // - If symbol is SeeBelow we want the members if it's a namespace MRDOCS_CHECK_OR( I.Extraction != ExtractionMode::SeeBelow || - I.Kind == InfoKind::Namespace); + I.Kind == SymbolKind::Namespace); // - If symbol is a Dependency, we only want the members if // the traversal mode is BaseClass @@ -231,9 +232,9 @@ traverseMembers(InfoTy& I, DeclTy const* DC) // There are many implicit declarations, especially in the // translation unit declaration, so we preemtively skip them here. - auto explicitMembers = std::ranges::views::filter(DC->decls(), [](Decl* D) + auto explicitMembers = std::ranges::views::filter(DC->decls(), [](clang::Decl* D) { - return !D->isImplicit() || isa(D); + return !D->isImplicit() || isa(D); }); for (auto* D : explicitMembers) { @@ -247,8 +248,8 @@ traverseMembers(InfoTy& I, DeclTy const* DC) } template < - std::derived_from InfoTy, - std::derived_from DeclTy> + std::derived_from InfoTy, + std::derived_from DeclTy> void ASTVisitor:: traverseMembers(InfoTy& I, DeclTy const* D) @@ -257,15 +258,15 @@ traverseMembers(InfoTy& I, DeclTy const* D) } template < - std::derived_from InfoTy, - std::derived_from DeclTy> -requires (!std::derived_from) + std::derived_from InfoTy, + std::derived_from DeclTy> +requires (!std::derived_from) void ASTVisitor:: traverseParent(InfoTy& I, DeclTy const* DC) { MRDOCS_SYMBOL_TRACE(DC, context_); - if (Decl const* PD = getParent(DC)) + if (clang::Decl const* PD = getParent(DC)) { MRDOCS_SYMBOL_TRACE(PD, context_); @@ -273,7 +274,7 @@ traverseParent(InfoTy& I, DeclTy const* DC) // to extract the parent scope: // Traverse the parent scope as a dependency if it // hasn't been extracted yet - Info* PI = nullptr; + Symbol* PI = nullptr; { ScopeExitRestore s(mode_, Dependency); if (PI = findOrTraverse(PD); !PI) @@ -287,7 +288,7 @@ traverseParent(InfoTy& I, DeclTy const* DC) visit(*PI, [&](ParentInfoTy& PU) -> void { - if constexpr (InfoParent) + if constexpr (SymbolParent) { addMember(PU, I); } @@ -296,8 +297,8 @@ traverseParent(InfoTy& I, DeclTy const* DC) } template < - std::derived_from InfoTy, - std::derived_from DeclTy> + std::derived_from InfoTy, + std::derived_from DeclTy> void ASTVisitor:: traverseParent(InfoTy& I, DeclTy const* D) @@ -308,14 +309,14 @@ traverseParent(InfoTy& I, DeclTy const* D) Expected> ASTVisitor:: -generateUSR(Decl const* D) const +generateUSR(clang::Decl const* D) const { MRDOCS_ASSERT(D); llvm::SmallString<128> res; - if (auto const* NAD = dyn_cast(D)) + if (auto const* NAD = dyn_cast(D)) { - if (index::generateUSRForDecl(cast(NAD->getNamespace()), res)) + if (clang::index::generateUSRForDecl(cast(NAD->getNamespace()), res)) { return Unexpected(Error("Failed to generate USR")); } @@ -325,11 +326,11 @@ generateUSR(Decl const* D) const } // Handling UsingDecl - if (auto const* UD = dyn_cast(D)) + if (auto const* UD = dyn_cast(D)) { for (auto const* shadow : UD->shadows()) { - if (index::generateUSRForDecl(shadow->getTargetDecl(), res)) + if (clang::index::generateUSRForDecl(shadow->getTargetDecl(), res)) { return Unexpected(Error("Failed to generate USR")); } @@ -339,9 +340,9 @@ generateUSR(Decl const* D) const return res; } - if (auto const* UD = dyn_cast(D)) + if (auto const* UD = dyn_cast(D)) { - if (index::generateUSRForDecl(UD->getNominatedNamespace(), res)) + if (clang::index::generateUSRForDecl(UD->getNominatedNamespace(), res)) { return Unexpected(Error("Failed to generate USR")); } @@ -350,10 +351,10 @@ generateUSR(Decl const* D) const return res; } - // Handling UnresolvedUsingTypenameDecl - if (auto const* UD = dyn_cast(D)) + // Handling clang::UnresolvedUsingTypenameDecl + if (auto const* UD = dyn_cast(D)) { - if (index::generateUSRForDecl(UD, res)) + if (clang::index::generateUSRForDecl(UD, res)) { return Unexpected(Error("Failed to generate USR")); } @@ -362,10 +363,10 @@ generateUSR(Decl const* D) const return res; } - // Handling UnresolvedUsingValueDecl - if (auto const* UD = dyn_cast(D)) + // Handling clang::UnresolvedUsingValueDecl + if (auto const* UD = dyn_cast(D)) { - if (index::generateUSRForDecl(UD, res)) + if (clang::index::generateUSRForDecl(UD, res)) { return Unexpected(Error("Failed to generate USR")); } @@ -374,10 +375,10 @@ generateUSR(Decl const* D) const return res; } - // Handling UsingPackDecl - if (auto const* UD = dyn_cast(D)) + // Handling clang::UsingPackDecl + if (auto const* UD = dyn_cast(D)) { - if (index::generateUSRForDecl(UD, res)) + if (clang::index::generateUSRForDecl(UD, res)) { return Unexpected(Error("Failed to generate USR")); } @@ -386,15 +387,15 @@ generateUSR(Decl const* D) const return res; } - // Handling UsingEnumDecl - if (auto const* UD = dyn_cast(D)) + // Handling clang::UsingEnumDecl + if (auto const* UD = dyn_cast(D)) { - if (index::generateUSRForDecl(UD, res)) + if (clang::index::generateUSRForDecl(UD, res)) { return Unexpected(Error("Failed to generate USR")); } res.append("@UED"); - EnumDecl const* ED = UD->getEnumDecl(); + clang::EnumDecl const* ED = UD->getEnumDecl(); res.append(ED->getQualifiedNameAsString()); return res; } @@ -403,10 +404,10 @@ generateUSR(Decl const* D) const // generating USRs for friend declarations, so we // will improvise until I can merge a patch which // adds support for them - if(auto const* FD = dyn_cast(D)) + if(auto const* FD = dyn_cast(D)) { // first, generate the USR for the containing class - if (index::generateUSRForDecl(cast(FD->getDeclContext()), res)) + if (clang::index::generateUSRForDecl(cast(FD->getDeclContext()), res)) { return Unexpected(Error("Failed to generate USR")); } @@ -414,9 +415,9 @@ generateUSR(Decl const* D) const res.append("@FD"); // if the friend declaration names a type, // use the USR generator for types - if (TypeSourceInfo* TSI = FD->getFriendType()) + if (clang::TypeSourceInfo* TSI = FD->getFriendType()) { - if (index::generateUSRForType(TSI->getType(), context_, res)) + if (clang::index::generateUSRForType(TSI->getType(), context_, res)) { return Unexpected(Error("Failed to generate USR")); } @@ -430,12 +431,12 @@ generateUSR(Decl const* D) const } } - if (index::generateUSRForDecl(D, res)) + if (clang::index::generateUSRForDecl(D, res)) { return Unexpected(Error("Failed to generate USR")); } - auto const* Described = dyn_cast_if_present(D); + auto const* Described = dyn_cast_if_present(D); auto const* Templated = D; if (auto const* DT = D->getDescribedTemplate()) { @@ -448,33 +449,33 @@ generateUSR(Decl const* D) const if(Described) { - TemplateParameterList const* TPL = Described->getTemplateParameters(); + clang::TemplateParameterList const* TPL = Described->getTemplateParameters(); if(auto const* RC = TPL->getRequiresClause()) { RC = SubstituteConstraintExpressionWithoutSatisfaction( - sema_, cast(isa(Described) ? Described : Templated), RC); + sema_, cast(isa(Described) ? Described : Templated), RC); if (!RC) { return Unexpected(Error("Failed to generate USR")); } - ODRHash odr_hash; + clang::ODRHash odr_hash; odr_hash.AddStmt(RC); res.append("@TPL#"); res.append(llvm::itostr(odr_hash.CalculateHash())); } } - if(auto* FD = dyn_cast(Templated); + if(auto* FD = dyn_cast(Templated); FD && FD->getTrailingRequiresClause()) { - Expr const* RC = FD->getTrailingRequiresClause().ConstraintExpr; + clang::Expr const* RC = FD->getTrailingRequiresClause().ConstraintExpr; RC = SubstituteConstraintExpressionWithoutSatisfaction( - sema_, cast(Described ? Described : Templated), RC); + sema_, cast(Described ? Described : Templated), RC); if (!RC) { return Unexpected(Error("Failed to generate USR")); } - ODRHash odr_hash; + clang::ODRHash odr_hash; odr_hash.AddStmt(RC); res.append("@TRC#"); res.append(llvm::itostr(odr_hash.CalculateHash())); @@ -486,7 +487,7 @@ generateUSR(Decl const* D) const bool ASTVisitor:: generateID( - Decl const* D, + clang::Decl const* D, SymbolID& id) const { if (!D) @@ -494,7 +495,7 @@ generateID( return false; } - if (isa(D)) + if (isa(D)) { id = SymbolID::global; return true; @@ -512,32 +513,32 @@ generateID( SymbolID ASTVisitor:: -generateID(Decl const* D) const +generateID(clang::Decl const* D) const { SymbolID id = SymbolID::invalid; generateID(D, id); return id; } -template DeclTy> +template DeclTy> void ASTVisitor:: -populate(Info& I, bool const isNew, DeclTy const* D) +populate(Symbol& I, bool const isNew, DeclTy const* D) { - populate(I.javadoc, D); - populate(I.asSourceInfo(), D); + populate(I.doc, D); + populate(I.Loc, D); // All other information is redundant if the symbol is not new MRDOCS_CHECK_OR(isNew); // These should already have been populated by traverseImpl MRDOCS_ASSERT(I.id); - MRDOCS_ASSERT(I.Kind != InfoKind::None); + MRDOCS_ASSERT(I.Kind != SymbolKind::None); I.Name = extractName(D); } -template DeclTy> +template DeclTy> void ASTVisitor:: populate(SourceInfo& I, DeclTy const* D) @@ -550,7 +551,7 @@ populate(SourceInfo& I, DeclTy const* D) if (Loc.isValid()) { populate( - I.asSourceInfo(), + I, Loc, isDefinition(D), isDocumented(D)); @@ -560,15 +561,15 @@ populate(SourceInfo& I, DeclTy const* D) bool ASTVisitor:: populate( - std::optional& javadoc, - Decl const* D) + Optional& doc, + clang::Decl const* D) { - RawComment const* RC = getDocumentation(D); + clang::RawComment const* RC = getDocumentation(D); MRDOCS_CHECK_OR(RC, false); - comments::FullComment* FC = + clang::comments::FullComment* FC = RC->parse(D->getASTContext(), &sema_.getPreprocessor(), D); MRDOCS_CHECK_OR(FC, false); - parseJavadoc(javadoc, FC, D, config_, diags_); + populateDocComment(doc, FC, D, config_, diags_); return true; } @@ -580,21 +581,28 @@ populate( bool const definition, bool const documented) { - unsigned line = source_.getPresumedLoc( - loc, false).getLine(); + auto presLoc = source_.getPresumedLoc(loc, false); + unsigned line = presLoc.getLine(); + unsigned col = presLoc.getColumn(); FileInfo* file = findFileInfo(loc); // No file is not an error, it just typically means it has been generated // in the virtual filesystem. MRDOCS_CHECK_OR(file); + Location Loc(file->full_path, + file->short_path, + file->source_path, + line, + col, + documented); if (definition) { if (I.DefLoc) { return; } - I.DefLoc.emplace(file->full_path, file->short_path, file->source_path, line, documented); + I.DefLoc = std::move(Loc); } else { @@ -609,15 +617,15 @@ populate( { return; } - I.Loc.emplace_back(file->full_path, file->short_path, file->source_path, line, documented); + I.Loc.push_back(std::move(Loc)); } } void ASTVisitor:: populate( - NamespaceInfo& I, - NamespaceDecl const* D) + NamespaceSymbol& I, + clang::NamespaceDecl const* D) { I.IsAnonymous = D->isAnonymousNamespace(); if (!I.IsAnonymous) @@ -630,8 +638,8 @@ populate( void ASTVisitor:: populate( - NamespaceInfo& I, - TranslationUnitDecl const*) + NamespaceSymbol& I, + clang::TranslationUnitDecl const*) { I.id = SymbolID::global; I.IsAnonymous = false; @@ -641,37 +649,37 @@ populate( void ASTVisitor:: populate( - RecordInfo& I, - CXXRecordDecl const* D) + RecordSymbol& I, + clang::CXXRecordDecl const* D) { if (D->getTypedefNameForAnonDecl()) { I.IsTypeDef = true; } I.KeyKind = toRecordKeyKind(D->getTagKind()); - // These are from CXXRecordDecl::isEffectivelyFinal() - I.IsFinal = D->hasAttr(); + // These are from clang::CXXRecordDecl::isEffectivelyFinal() + I.IsFinal = D->hasAttr(); if (auto const* DT = D->getDestructor()) { - I.IsFinalDestructor = DT->hasAttr(); + I.IsFinalDestructor = DT->hasAttr(); } // Extract direct bases. D->bases() will get the bases // from whichever declaration is the definition (if any) if(D->hasDefinition() && I.Bases.empty()) { - for (CXXBaseSpecifier const& B : D->bases()) + for (clang::CXXBaseSpecifier const& B : D->bases()) { - AccessSpecifier const access = B.getAccessSpecifier(); + clang::AccessSpecifier const access = B.getAccessSpecifier(); if (!config_->extractPrivateBases && - access == AS_private) + access == clang::AS_private) { continue; } - QualType const BT = B.getType(); - auto BaseType = toTypeInfo(BT, BaseClass); + clang::QualType const BT = B.getType(); + auto BaseType = toType(BT, BaseClass); // If we're going to copy the members from the specialization, // we need to instantiate and traverse the specialization @@ -679,35 +687,36 @@ populate( if (config_->extractImplicitSpecializations) { [&] { - auto* TST = BT->getAs(); + auto* TST = BT->getAs(); MRDOCS_CHECK_OR(TST); MRDOCS_SYMBOL_TRACE(TST, context_); auto const* CTSD = dyn_cast_or_null< - ClassTemplateSpecializationDecl>( + clang::ClassTemplateSpecializationDecl>( TST->getAsCXXRecordDecl()); MRDOCS_CHECK_OR(CTSD); MRDOCS_SYMBOL_TRACE(CTSD, context_); // Traverse the Decl as a dependency ScopeExitRestore s(mode_, TraversalMode::BaseClass); - Info const* SI = findOrTraverse(CTSD); + Symbol const* SI = findOrTraverse(CTSD); MRDOCS_CHECK_OR(SI); auto& inner = innermostType(BaseType); MRDOCS_CHECK_OR(inner); MRDOCS_CHECK_OR(inner->isNamed()); - auto& NTI = dynamic_cast(*inner); + auto& NTI = inner->asNamed(); MRDOCS_CHECK_OR(NTI.Name); MRDOCS_CHECK_OR(NTI.Name->isSpecialization()); - auto& SNI = dynamic_cast(*NTI.Name); + auto& SNI = NTI.Name->asSpecialization(); SNI.specializationID = SI->id; }(); } - // CXXBaseSpecifier::getEllipsisLoc indicates whether the + // clang::CXXBaseSpecifier::getEllipsisLoc indicates whether the // base was a pack expansion; a PackExpansionType is not built // for base-specifiers - if (BaseType && B.getEllipsisLoc().isValid()) + MRDOCS_ASSERT(!BaseType.valueless_after_move()); + if (B.getEllipsisLoc().isValid()) { BaseType->IsPackExpansion = true; } @@ -723,22 +732,22 @@ populate( D->hasDefinition() && D->hasFriends()) { - for (FriendDecl const* FD : D->friends()) + for (clang::FriendDecl const* FD : D->friends()) { // Check if the friend is a fundamental type // Declaring a fundamental type like `int` as a friend of a // class or struct does not have any practical effect. Thus, // it's not considered part of the public API. - if (TypeSourceInfo const* TSI = FD->getFriendType()) + if (clang::TypeSourceInfo const* TSI = FD->getFriendType()) { - Type const* T = TSI->getType().getTypePtrOrNull(); + clang::Type const* T = TSI->getType().getTypePtrOrNull(); MRDOCS_CHECK_OR_CONTINUE(!T || !T->isBuiltinType()); } FriendInfo F; populate(F, FD); if (F.id != SymbolID::invalid) { - Info* FI = this->find(F.id); + Symbol* FI = this->find(F.id); MRDOCS_CHECK_OR_CONTINUE(FI); MRDOCS_CHECK_OR_CONTINUE(FI->Extraction != ExtractionMode::ImplementationDefined); } @@ -760,7 +769,7 @@ populate( void ASTVisitor:: -populate(RecordInfo& I, ClassTemplateDecl const* D) +populate(RecordSymbol& I, clang::ClassTemplateDecl const* D) { populate(I.Template, D->getTemplatedDecl(), D); populate(I, D->getTemplatedDecl()); @@ -768,24 +777,24 @@ populate(RecordInfo& I, ClassTemplateDecl const* D) void ASTVisitor:: -populate(RecordInfo& I, ClassTemplateSpecializationDecl const* D) +populate(RecordSymbol& I, clang::ClassTemplateSpecializationDecl const* D) { populate(I.Template, D, D->getSpecializedTemplate()); - populate(I, cast(D)); + populate(I, cast(D)); } void ASTVisitor:: -populate(RecordInfo& I, ClassTemplatePartialSpecializationDecl const* D) +populate(RecordSymbol& I, clang::ClassTemplatePartialSpecializationDecl const* D) { - populate(I, dynamic_cast(D)); + populate(I, dynamic_cast(D)); } void ASTVisitor:: populate( - FunctionInfo& I, - FunctionDecl const* D) + FunctionSymbol& I, + clang::FunctionDecl const* D) { MRDOCS_SYMBOL_TRACE(D, context_); @@ -831,7 +840,7 @@ populate( if (auto FT = getDeclaratorType(D); !FT.isNull()) { MRDOCS_SYMBOL_TRACE(FT, context_); - auto const* FPT = FT->template getAs(); + auto const* FPT = FT->template getAs(); MRDOCS_SYMBOL_TRACE(FPT, context_); populate(I.Noexcept, FPT); I.HasTrailingReturn |= FPT->hasTrailingReturn(); @@ -844,27 +853,27 @@ populate( I.IsDeleted |= D->isDeleted(); I.IsDeletedAsWritten |= D->isDeletedAsWritten(); I.IsNoReturn |= D->isNoReturn(); - I.HasOverrideAttr |= D->hasAttr(); + I.HasOverrideAttr |= D->hasAttr(); - if (ConstexprSpecKind const CSK = D->getConstexprKind(); - CSK != ConstexprSpecKind::Unspecified) + if (clang::ConstexprSpecKind const CSK = D->getConstexprKind(); + CSK != clang::ConstexprSpecKind::Unspecified) { I.Constexpr = toConstexprKind(CSK); } - if (StorageClass const SC = D->getStorageClass()) + if (clang::StorageClass const SC = D->getStorageClass()) { I.StorageClass = toStorageClassKind(SC); } - I.IsNodiscard |= D->hasAttr(); + I.IsNodiscard |= D->hasAttr(); I.IsExplicitObjectMemberFunction |= D->hasCXXExplicitFunctionObjectParameter(); - ArrayRef const params = D->parameters(); + llvm::ArrayRef const params = D->parameters(); I.Params.resize(params.size()); for (std::size_t i = 0; i < params.size(); ++i) { - ParmVarDecl const* P = params[i]; + clang::ParmVarDecl const* P = params[i]; MRDOCS_SYMBOL_TRACE(P, context_); Param& param = I.Params[i]; @@ -873,21 +882,24 @@ populate( param.Name = P->getName(); } - if (!param.Type) + if (param.Type->isAuto()) { - param.Type = toTypeInfo(P->getOriginalType()); + param.Type = toType(P->getOriginalType()); } - Expr const* default_arg = P->hasUninstantiatedDefaultArg() ? + clang::Expr const* default_arg = P->hasUninstantiatedDefaultArg() ? P->getUninstantiatedDefaultArg() : P->getInit(); if (!param.Default && default_arg) { param.Default = getSourceCode(default_arg->getSourceRange()); - param.Default = trim(*param.Default); - if (param.Default->starts_with("= ")) + if (param.Default) { - param.Default->erase(0, 2); - param.Default = ltrim(*param.Default); + param.Default = trim(*param.Default); + if (param.Default->starts_with("= ")) + { + param.Default->erase(0, 2); + param.Default = ltrim(*param.Default); + } } } } @@ -896,10 +908,10 @@ populate( // extract the return type in direct dependency mode // if it contains a placeholder type which is - // deduceded as a local class type - QualType const RT = D->getReturnType(); + // deduced as a local class type + clang::QualType const RT = D->getReturnType(); MRDOCS_SYMBOL_TRACE(RT, context_); - I.ReturnType = toTypeInfo(RT); + I.ReturnType = toType(RT); if (auto* TRC = D->getTrailingRequiresClause().ConstraintExpr) { @@ -907,9 +919,9 @@ populate( } else if (I.Requires.Written.empty()) { + MRDOCS_ASSERT(!I.ReturnType.valueless_after_move()); // Return type SFINAE constraints - if (I.ReturnType && - !I.ReturnType->Constraints.empty()) + if (!I.ReturnType->Constraints.empty()) { for (ExprInfo const& constraint: I.ReturnType->Constraints) { @@ -924,8 +936,8 @@ populate( // Iterate I.Params to find trailing requires clauses for (auto it = I.Params.begin(); it != I.Params.end(); ) { - if (it->Type && - !it->Type->Constraints.empty()) + MRDOCS_ASSERT(!it->Type.valueless_after_move()); + if (!it->Type->Constraints.empty()) { for (ExprInfo const& constraint: it->Type->Constraints) { @@ -949,23 +961,23 @@ populate( void ASTVisitor:: -populate(FunctionInfo& I, FunctionTemplateDecl const* D) +populate(FunctionSymbol& I, clang::FunctionTemplateDecl const* D) { - FunctionDecl const* TD = D->getTemplatedDecl(); + clang::FunctionDecl const* TD = D->getTemplatedDecl(); populate(I.Template, TD, D); - if (auto* C = dyn_cast(TD)) + if (auto* C = dyn_cast(TD)) { populate(I, C); } - else if (auto* Dtor = dyn_cast(TD)) + else if (auto* Dtor = dyn_cast(TD)) { populate(I, Dtor); } - else if (auto* Conv = dyn_cast(TD)) + else if (auto* Conv = dyn_cast(TD)) { populate(I, Conv); } - else if (auto* M = dyn_cast(TD)) + else if (auto* M = dyn_cast(TD)) { populate(I, M); } @@ -977,9 +989,9 @@ populate(FunctionInfo& I, FunctionTemplateDecl const* D) void ASTVisitor:: -populate(FunctionInfo& I, CXXMethodDecl const* D) +populate(FunctionSymbol& I, clang::CXXMethodDecl const* D) { - FunctionDecl const* FD = D; + clang::FunctionDecl const* FD = D; populate(I, FD); I.IsRecordMethod = true; I.IsVirtual |= D->isVirtual(); @@ -988,31 +1000,31 @@ populate(FunctionInfo& I, CXXMethodDecl const* D) I.IsConst |= D->isConst(); I.IsVolatile |= D->isVolatile(); I.RefQualifier = toReferenceKind(D->getRefQualifier()); - I.IsFinal |= D->hasAttr(); + I.IsFinal |= D->hasAttr(); } void ASTVisitor:: -populate(FunctionInfo& I, CXXConstructorDecl const* D) +populate(FunctionSymbol& I, clang::CXXConstructorDecl const* D) { - CXXMethodDecl const* FD = D; + clang::CXXMethodDecl const* FD = D; populate(I, FD); populate(I.Explicit, D->getExplicitSpecifier()); } void ASTVisitor:: -populate(FunctionInfo& I, CXXDestructorDecl const* D) +populate(FunctionSymbol& I, clang::CXXDestructorDecl const* D) { - CXXMethodDecl const* FD = D; + clang::CXXMethodDecl const* FD = D; populate(I, FD); } void ASTVisitor:: -populate(FunctionInfo& I, CXXConversionDecl const* D) +populate(FunctionSymbol& I, clang::CXXConversionDecl const* D) { - CXXMethodDecl const* FD = D; + clang::CXXMethodDecl const* FD = D; populate(I, FD); populate(I.Explicit, D->getExplicitSpecifier()); } @@ -1021,21 +1033,21 @@ populate(FunctionInfo& I, CXXConversionDecl const* D) void ASTVisitor:: populate( - EnumInfo& I, - EnumDecl const* D) + EnumSymbol& I, + clang::EnumDecl const* D) { I.Scoped = D->isScoped(); if (D->isFixed()) { - I.UnderlyingType = toTypeInfo(D->getIntegerType()); + I.UnderlyingType = toType(D->getIntegerType()); } } void ASTVisitor:: populate( - EnumConstantInfo& I, - EnumConstantDecl const* D) + EnumConstantSymbol& I, + clang::EnumConstantDecl const* D) { I.Name = extractName(D); populate( @@ -1046,36 +1058,36 @@ populate( void ASTVisitor:: -populate(TypedefInfo& I, TypedefNameDecl const* D) +populate(TypedefSymbol& I, clang::TypedefNameDecl const* D) { - QualType const QT = D->getUnderlyingType(); - I.Type = toTypeInfo(QT); + clang::QualType const QT = D->getUnderlyingType(); + I.Type = toType(QT); } void ASTVisitor:: -populate(TypedefInfo& I, TypedefDecl const* D) +populate(TypedefSymbol& I, clang::TypedefDecl const* D) { - populate(I, cast(D)); + populate(I, cast(D)); } void ASTVisitor:: -populate(TypedefInfo& I, TypeAliasDecl const* D) +populate(TypedefSymbol& I, clang::TypeAliasDecl const* D) { - I.IsUsing = isa(D); - populate(I, cast(D)); + I.IsUsing = isa(D); + populate(I, cast(D)); } void ASTVisitor:: -populate(TypedefInfo& I, TypeAliasTemplateDecl const* D) +populate(TypedefSymbol& I, clang::TypeAliasTemplateDecl const* D) { populate(I.Template, D->getTemplatedDecl(), D); if (auto* TD = D->getTemplatedDecl(); - isa(TD)) + isa(TD)) { - populate(I, cast(TD)); + populate(I, cast(TD)); } else { @@ -1086,28 +1098,28 @@ populate(TypedefInfo& I, TypeAliasTemplateDecl const* D) void ASTVisitor:: populate( - VariableInfo& I, - VarDecl const* D) + VariableSymbol& I, + clang::VarDecl const* D) { // KRYSTIAN FIXME: we need to properly merge storage class - if (StorageClass const SC = D->getStorageClass()) + if (clang::StorageClass const SC = D->getStorageClass()) { I.StorageClass = toStorageClassKind(SC); } // this handles thread_local, as well as the C // __thread and __Thread_local specifiers I.IsThreadLocal |= D->getTSCSpec() != - ThreadStorageClassSpecifier::TSCS_unspecified; + clang::ThreadStorageClassSpecifier::TSCS_unspecified; // KRYSTIAN NOTE: VarDecl does not provide getConstexprKind, // nor does it use getConstexprKind to store whether // a variable is constexpr/constinit. Although // only one is permitted in a variable declaration, // it is possible to declare a static data member // as both constexpr and constinit in separate declarations.. - I.IsConstinit |= D->hasAttr(); + I.IsConstinit |= D->hasAttr(); I.IsConstexpr |= D->isConstexpr(); I.IsInline |= D->isInline(); - if (Expr const* E = D->getInit()) + if (clang::Expr const* E = D->getInit()) { populate(I.Initializer, E); } @@ -1117,13 +1129,13 @@ populate( // `const` qualifier which we need to remove QT.removeLocalConst(); } - I.Type = toTypeInfo(QT); + I.Type = toType(QT); populateAttributes(I, D); } void ASTVisitor:: -populate(VariableInfo& I, VarTemplateDecl const* D) +populate(VariableSymbol& I, clang::VarTemplateDecl const* D) { populate(I.Template, D->getTemplatedDecl(), D); populate(I, D->getTemplatedDecl()); @@ -1131,28 +1143,28 @@ populate(VariableInfo& I, VarTemplateDecl const* D) void ASTVisitor:: -populate(VariableInfo& I, VarTemplateSpecializationDecl const* D) +populate(VariableSymbol& I, clang::VarTemplateSpecializationDecl const* D) { populate(I.Template, D, D->getSpecializedTemplate()); - populate(I, cast(D)); + populate(I, cast(D)); } void ASTVisitor:: -populate(VariableInfo& I, VarTemplatePartialSpecializationDecl const* D) +populate(VariableSymbol& I, clang::VarTemplatePartialSpecializationDecl const* D) { - populate(I, dynamic_cast(D)); + populate(I, dynamic_cast(D)); } void ASTVisitor:: populate( - VariableInfo& I, - FieldDecl const* D) + VariableSymbol& I, + clang::FieldDecl const* D) { I.IsRecordField = true; - I.Type = toTypeInfo(D->getType()); - if (Expr const* E = D->getInClassInitializer()) + I.Type = toType(D->getType()); + if (clang::Expr const* E = D->getInClassInitializer()) { populate(I.Initializer, E); } @@ -1163,9 +1175,9 @@ populate( I.IsBitfield = true; populate(I.BitfieldWidth, D->getBitWidth()); } - I.HasNoUniqueAddress = D->hasAttr(); - I.IsDeprecated = D->hasAttr(); - I.IsMaybeUnused = D->hasAttr(); + I.HasNoUniqueAddress = D->hasAttr(); + I.IsDeprecated = D->hasAttr(); + I.IsMaybeUnused = D->hasAttr(); populateAttributes(I, D); } @@ -1173,21 +1185,21 @@ void ASTVisitor:: populate( FriendInfo& I, - FriendDecl const* D) + clang::FriendDecl const* D) { - if (TypeSourceInfo const* TSI = D->getFriendType()) + if (clang::TypeSourceInfo const* TSI = D->getFriendType()) { - I.Type = toTypeInfo(TSI->getType()); - MRDOCS_CHECK_OR(I.Type->isNamed()); - auto const& NTI = dynamic_cast(*I.Type); + I.Type = toType(TSI->getType()); + MRDOCS_CHECK_OR((*I.Type)->isNamed()); + auto const& NTI = (*I.Type)->asNamed(); MRDOCS_CHECK_OR(NTI.Name); I.id = NTI.Name->id; } - else if (NamedDecl const* ND = D->getFriendDecl()) + else if (clang::NamedDecl const* ND = D->getFriendDecl()) { // ND can be a function or a class ScopeExitRestore s(mode_, Dependency); - if (Info const* SI = traverse(dyn_cast(ND))) + if (Symbol const* SI = traverse(dyn_cast(ND))) { I.id = SI->id; } @@ -1196,23 +1208,23 @@ populate( // documentation from the FriendDecl when the friend // is the only declaration. MRDOCS_CHECK_OR(isDocumented(D)); - Info* TI = find(I.id); + Symbol* TI = find(I.id); MRDOCS_CHECK_OR(TI); - MRDOCS_CHECK_OR(!TI->javadoc); - populate(TI->javadoc, D); + MRDOCS_CHECK_OR(!TI->doc); + populate(TI->doc, D); } void ASTVisitor:: populate( - GuideInfo& I, - CXXDeductionGuideDecl const* D) + GuideSymbol& I, + clang::CXXDeductionGuideDecl const* D) { - I.Deduced = toTypeInfo(D->getReturnType()); - for (ParmVarDecl const* P : D->parameters()) + I.Deduced = toType(D->getReturnType()); + for (clang::ParmVarDecl const* P : D->parameters()) { I.Params.emplace_back( - toTypeInfo(P->getOriginalType()), + toType(P->getOriginalType()), P->getNameAsString(), // deduction guides cannot have default arguments std::string()); @@ -1223,40 +1235,45 @@ populate( void ASTVisitor:: populate( - GuideInfo& I, - FunctionTemplateDecl const* D) + GuideSymbol& I, + clang::FunctionTemplateDecl const* D) { populate(I.Template, D->getTemplatedDecl(), D); - populate(I, cast(D->getTemplatedDecl())); + populate(I, cast(D->getTemplatedDecl())); } void ASTVisitor:: populate( - NamespaceAliasInfo& I, - NamespaceAliasDecl const* D) + NamespaceAliasSymbol& I, + clang::NamespaceAliasDecl const* D) { - NamedDecl const* Aliased = D->getAliasedNamespace(); - NestedNameSpecifier NNS = D->getQualifier(); - I.AliasedSymbol = toNameInfo(Aliased, {}, NNS); + clang::NamedDecl const* Aliased = D->getAliasedNamespace(); + clang::NestedNameSpecifier NNS = D->getQualifier(); + Optional> NI = toName(Aliased, {}, NNS); + MRDOCS_CHECK_OR(NI); + MRDOCS_ASSERT((*NI)->isIdentifier()); + I.AliasedSymbol = std::move((**NI).asIdentifier()); } void ASTVisitor:: populate( - UsingInfo& I, - UsingDecl const* D) + UsingSymbol& I, + clang::UsingDecl const* D) { I.Class = UsingClass::Normal; - DeclarationName const& Name = D->getNameInfo().getName(); - NestedNameSpecifier const& NNS = D->getQualifier(); - I.IntroducedName = toNameInfo(Name, {}, NNS); - for (UsingShadowDecl const* UDS: D->shadows()) + clang::DeclarationName const& Name = D->getNameInfo().getName(); + clang::NestedNameSpecifier const& NNS = D->getQualifier(); + auto INI = toName(Name, {}, NNS); + MRDOCS_CHECK_OR(INI); + I.IntroducedName = *INI; + for (clang::UsingShadowDecl const* UDS: D->shadows()) { ScopeExitRestore s(mode_, Dependency); - Decl* S = UDS->getTargetDecl(); + clang::Decl* S = UDS->getTargetDecl(); using enum ExtractionMode; - if (Info* SI = findOrTraverse(S); + if (Symbol* SI = findOrTraverse(S); SI && !is_one_of(SI->Extraction,{Dependency, ImplementationDefined})) { @@ -1268,8 +1285,8 @@ populate( void ASTVisitor:: populate( - ConceptInfo& I, - ConceptDecl const* D) + ConceptSymbol& I, + clang::ConceptDecl const* D) { populate(I.Template, D, D); populate(I.Constraint, D->getConstraintExpr()); @@ -1279,23 +1296,23 @@ populate( /* Default function to populate template parameters */ template < - std::derived_from DeclTy, - std::derived_from TemplateDeclTy> + std::derived_from DeclTy, + std::derived_from TemplateDeclTy> void ASTVisitor:: populate(TemplateInfo& Template, DeclTy const*, TemplateDeclTy const* TD) { MRDOCS_ASSERT(TD); MRDOCS_CHECK_OR(!TD->isImplicit()); - if (TemplateParameterList const* TPL = TD->getTemplateParameters(); + if (clang::TemplateParameterList const* TPL = TD->getTemplateParameters(); !TPL->empty() && - std::ranges::none_of(TPL->asArray(), [](NamedDecl const* ND) { + std::ranges::none_of(TPL->asArray(), [](clang::NamedDecl const* ND) { return !ND->isImplicit(); })) { return; } - TemplateParameterList const* TPL = TD->getTemplateParameters(); + clang::TemplateParameterList const* TPL = TD->getTemplateParameters(); populate(Template, TPL); } @@ -1303,8 +1320,8 @@ void ASTVisitor:: populate( TemplateInfo& Template, - ClassTemplateSpecializationDecl const* CTSD, - ClassTemplateDecl const* CTD) + clang::ClassTemplateSpecializationDecl const* CTSD, + clang::ClassTemplateDecl const* CTD) { MRDOCS_ASSERT(CTD); @@ -1328,26 +1345,23 @@ populate( for (auto it = Template.Args.begin(); it != Template.Args.end();) { auto& arg = *it; - if (!arg) - { - ++it; - continue; - } - if (auto* T = dynamic_cast(arg.operator->()); - T && - T->Type && - !T->Type->Constraints.empty()) + MRDOCS_ASSERT(!arg.valueless_after_move()); + if (auto* T = (arg.operator->())->asTypePtr()) { - for (ExprInfo const& constraint: T->Type->Constraints) + MRDOCS_ASSERT(!T->Type.valueless_after_move()); + if (!T->Type->Constraints.empty()) { - if (!Template.Requires.Written.empty()) + for (ExprInfo const& constraint: T->Type->Constraints) { - Template.Requires.Written += " && "; + if (!Template.Requires.Written.empty()) + { + Template.Requires.Written += " && "; + } + Template.Requires.Written += constraint.Written; } - Template.Requires.Written += constraint.Written; + it = Template.Args.erase(it); + continue; } - it = Template.Args.erase(it); - continue; } ++it; } @@ -1355,31 +1369,31 @@ populate( // Extract the template parameters if this is a partial specialization - if (auto* CTPSD = dyn_cast(CTSD)) + if (auto* CTPSD = dyn_cast(CTSD)) { - TemplateParameterList* params = CTPSD->getTemplateParameters(); + clang::TemplateParameterList* params = CTPSD->getTemplateParameters(); populate(Template, params); } } -template VarDeclTy> +template VarDeclTy> void ASTVisitor:: populate( TemplateInfo& Template, VarDeclTy const* D, - VarTemplateDecl const* VTD) + clang::VarTemplateDecl const* VTD) { MRDOCS_ASSERT(VTD); // If D is a partial/explicit specialization, extract the template arguments - if(auto* VTSD = dyn_cast(D)) + if(auto* VTSD = dyn_cast(D)) { generateID(getInstantiatedFrom(VTD), Template.Primary); // extract the template arguments of the specialization populate(Template.Args, VTSD->getTemplateArgsAsWritten()); // extract the template parameters if this is a partial specialization - if (auto* VTPSD = dyn_cast(D)) + if (auto* VTPSD = dyn_cast(D)) { populate(Template, VTPSD->getTemplateParameters()); } @@ -1395,14 +1409,14 @@ void ASTVisitor:: populate( NoexceptInfo& I, - FunctionProtoType const* FPT) + clang::FunctionProtoType const* FPT) { MRDOCS_ASSERT(FPT); I.Implicit = ! FPT->hasNoexceptExceptionSpec(); I.Kind = toNoexceptKind( FPT->getExceptionSpecType()); // store the operand, if any - if (Expr const* NoexceptExpr = FPT->getNoexceptExpr()) + if (clang::Expr const* NoexceptExpr = FPT->getNoexceptExpr()) { I.Operand = toString(NoexceptExpr); } @@ -1412,13 +1426,13 @@ void ASTVisitor:: populate( ExplicitInfo& I, - ExplicitSpecifier const& ES) + clang::ExplicitSpecifier const& ES) { I.Implicit = ! ES.isSpecified(); I.Kind = toExplicitKind(ES); // store the operand, if any - if (Expr const* ExplicitExpr = ES.getExpr()) + if (clang::Expr const* ExplicitExpr = ES.getExpr()) { I.Operand = toString(ExplicitExpr); } @@ -1428,7 +1442,7 @@ void ASTVisitor:: populate( ExprInfo& I, - Expr const* E) + clang::Expr const* E) { if (!E) { @@ -1443,7 +1457,7 @@ void ASTVisitor:: populate( ConstantExprInfo& I, - Expr const* E) + clang::Expr const* E) { populate(static_cast(I), E); // if the expression is dependent, @@ -1460,7 +1474,7 @@ void ASTVisitor:: populate( ConstantExprInfo& I, - Expr const* E, + clang::Expr const* E, llvm::APInt const& V) { populate(I, E); @@ -1472,7 +1486,7 @@ void ASTVisitor:: populate( ConstantExprInfo& I, - Expr const* E, + clang::Expr const* E, llvm::APInt const& V); @@ -1480,20 +1494,21 @@ void ASTVisitor:: populate( Polymorphic& I, - NamedDecl const* N) + clang::NamedDecl const* N) { + MRDOCS_ASSERT(!I.valueless_after_move()); visit(N, [&](DeclTy const* P) { - constexpr Decl::Kind kind = + constexpr clang::Decl::Kind kind = DeclToKind(); - if constexpr(kind == Decl::TemplateTypeParm) + if constexpr(kind == clang::Decl::TemplateTypeParm) { - if (!I) + if (I->Kind != TParamKind::Type) { I = Polymorphic(std::in_place_type); } - auto* R = dynamic_cast(I.operator->()); + auto* R = (I.operator->())->asTypePtr(); if (P->wasDeclaredWithTypename()) { R->KeyKind = TParamKeyKind::Typename; @@ -1503,27 +1518,27 @@ populate( R->Default = toTArg( P->getDefaultArgument().getArgument()); } - if(TypeConstraint const* TC = P->getTypeConstraint()) + if (clang::TypeConstraint const* TC = P->getTypeConstraint()) { - NestedNameSpecifier NNS = + clang::NestedNameSpecifier NNS = TC->getNestedNameSpecifierLoc().getNestedNameSpecifier(); - std::optional TArgs; + Optional TArgs; if (TC->hasExplicitTemplateArgs()) { TArgs.emplace(TC->getTemplateArgsAsWritten()); } - R->Constraint = toNameInfo(TC->getNamedConcept(), TArgs, NNS); + R->Constraint = toName(TC->getNamedConcept(), TArgs, NNS); } return; } - else if constexpr(kind == Decl::NonTypeTemplateParm) + else if constexpr(kind == clang::Decl::NonTypeTemplateParm) { - if (!I) + if (I->Kind != TParamKind::Constant) { - I = Polymorphic(std::in_place_type); + I = Polymorphic(std::in_place_type); } - auto* R = dynamic_cast(I.operator->()); - R->Type = toTypeInfo(P->getType()); + auto* R = (I.operator->())->asConstantPtr(); + R->Type = toType(P->getType()); if (P->hasDefaultArgument() && !R->Default) { R->Default = toTArg( @@ -1531,30 +1546,31 @@ populate( } return; } - else if constexpr(kind == Decl::TemplateTemplateParm) + else if constexpr(kind == clang::Decl::TemplateTemplateParm) { - if (!I) + if (I->Kind != TParamKind::Template) { I = Polymorphic(std::in_place_type); } - auto const* TTPD = cast(P); + auto const* TTPD = cast(P); MRDOCS_CHECK_OR(TTPD); - TemplateParameterList const* TPL = TTPD->getTemplateParameters(); + clang::TemplateParameterList const* TPL = TTPD->getTemplateParameters(); MRDOCS_CHECK_OR(TPL); - auto* Result = dynamic_cast(I.operator->()); + auto* Result = (I.operator->())->asTemplatePtr(); Result->Params.reserve(TPL->size()); for (std::size_t i = 0; i < TPL->size(); ++i) { - NamedDecl const* TP = TPL->getParam(i); - auto &Param = i < Result->Params.size() - ? Result->Params[i] - : Result->Params.emplace_back(std::nullopt); + clang::NamedDecl const* TP = TPL->getParam(i); + auto& Param + = i < Result->Params.size() ? + Result->Params[i] : + Result->Params.emplace_back(std::in_place_type); populate(Param, TP); } if (TTPD->hasDefaultArgument() && !Result->Default) { - TemplateArgumentLoc const& TAL = TTPD->getDefaultArgument(); - TemplateArgument const& TA = TAL.getArgument(); + clang::TemplateArgumentLoc const& TAL = TTPD->getDefaultArgument(); + clang::TemplateArgument const& TA = TAL.getArgument(); Result->Default = toTArg(TA); } return; @@ -1562,6 +1578,7 @@ populate( MRDOCS_UNREACHABLE(); }); + MRDOCS_ASSERT(!I.valueless_after_move()); if (I->Name.empty()) { I->Name = extractName(N); @@ -1576,7 +1593,7 @@ void ASTVisitor:: populate( TemplateInfo& TI, - TemplateParameterList const* TPL) + clang::TemplateParameterList const* TPL) { if (!TPL) { @@ -1586,7 +1603,7 @@ populate( auto ExplicitTemplateParameters = std::views::filter( TemplateParameters, - [](NamedDecl const* P) + [](clang::NamedDecl const* P) { return !P->isImplicit(); }); @@ -1598,10 +1615,11 @@ populate( std::size_t i = 0; while (explicitIt != ExplicitTemplateParameters.end()) { - NamedDecl const* P = TPL->getParam(i); - auto &Param = i < TI.Params.size() - ? TI.Params[i] - : TI.Params.emplace_back(std::nullopt); + clang::NamedDecl const* P = TPL->getParam(i); + Polymorphic& Param = + i < TI.Params.size() ? + TI.Params[i] : + TI.Params.emplace_back(std::in_place_type); populate(Param, P); ++explicitIt; ++i; @@ -1617,42 +1635,46 @@ populate( for (auto it = TI.Params.begin(); it != TI.Params.end(); ) { Polymorphic& param = *it; + MRDOCS_ASSERT(!param.valueless_after_move()); - if (auto const* T = dynamic_cast(param.operator->()); - T && - T->Type && - !T->Type->Constraints.empty()) + if (param->isConstant()) { - for (ExprInfo const& constraint: T->Type->Constraints) + auto& T = (*param).asConstant(); + MRDOCS_ASSERT(!T.Type.valueless_after_move()); + if (!T.Type->Constraints.empty()) { - if (!TI.Requires.Written.empty()) + for (ExprInfo const& constraint: T.Type->Constraints) { - TI.Requires.Written += " && "; + if (!TI.Requires.Written.empty()) + { + TI.Requires.Written += " && "; + } + TI.Requires.Written += constraint.Written; } - TI.Requires.Written += constraint.Written; + it = TI.Params.erase(it); + continue; } - it = TI.Params.erase(it); - continue; } if (param->Default && - param->Default->isType()) + (*param->Default)->isType()) { - if (auto const* T = dynamic_cast(param->Default.operator->()); - T && - T->Type && - !T->Type->Constraints.empty()) + if (auto const* T = ((*param->Default).operator->())->asTypePtr()) { - for (ExprInfo const& constraint: T->Type->Constraints) + MRDOCS_ASSERT(!T->Type.valueless_after_move()); + if (!T->Type->Constraints.empty()) { - if (!TI.Requires.Written.empty()) + for (ExprInfo const& constraint: T->Type->Constraints) { - TI.Requires.Written += " && "; + if (!TI.Requires.Written.empty()) + { + TI.Requires.Written += " && "; + } + TI.Requires.Written += constraint.Written; } - TI.Requires.Written += constraint.Written; + it = TI.Params.erase(it); + continue; } - it = TI.Params.erase(it); - continue; } } @@ -1665,7 +1687,7 @@ void ASTVisitor:: populate( std::vector>& result, - ASTTemplateArgumentListInfo const* args) + clang::ASTTemplateArgumentListInfo const* args) { if (!args) { @@ -1679,17 +1701,17 @@ populate( })); } -template InfoTy> +template InfoTy> void ASTVisitor:: -populateAttributes(InfoTy& I, Decl const* D) +populateAttributes(InfoTy& I, clang::Decl const* D) { if constexpr (requires { I.Attributes; }) { MRDOCS_CHECK_OR(D->hasAttrs()); - for (Attr const* attr: D->getAttrs()) + for (clang::Attr const* attr: D->getAttrs()) { - IdentifierInfo const* II = attr->getAttrName(); + clang::IdentifierInfo const* II = attr->getAttrName(); if (!II) { continue; @@ -1705,8 +1727,7 @@ populateAttributes(InfoTy& I, Decl const* D) void ASTVisitor:: addMember( - NamespaceInfo& I, - Info const& Member) + NamespaceSymbol& I, Symbol const& Member) { if (auto const* U = Member.asNamespacePtr()) { @@ -1767,8 +1788,7 @@ addMember( void ASTVisitor:: addMember( - RecordInfo& I, - Info const& Member) + RecordSymbol& I, Symbol const& Member) { switch (Member.Access) { @@ -1788,7 +1808,7 @@ addMember( void ASTVisitor:: -addMember(RecordTranche& T, Info const& Member) +addMember(RecordTranche& T, Symbol const& Member) { if (auto const* U = Member.asNamespaceAliasPtr()) { @@ -1856,7 +1876,7 @@ addMember(RecordTranche& T, Info const& Member) void ASTVisitor:: -addMember(EnumInfo& I, Info const& Member) const +addMember(EnumSymbol& I, Symbol const& Member) const { if (auto const* U = Member.asEnumConstantPtr()) { @@ -1871,7 +1891,7 @@ addMember(EnumInfo& I, Info const& Member) const void ASTVisitor:: -addMember(OverloadsInfo& I, Info const& Member) const +addMember(OverloadsSymbol& I, Symbol const& Member) const { if (Member.isFunction()) { @@ -1886,7 +1906,7 @@ addMember(OverloadsInfo& I, Info const& Member) const void ASTVisitor:: -addMember(std::vector& container, Info const& Member) const +addMember(std::vector& container, Symbol const& Member) const { if (!contains(container, Member.id)) { @@ -1894,22 +1914,22 @@ addMember(std::vector& container, Info const& Member) const } } -template DeclTy> +template DeclTy> std::string ASTVisitor:: extractName(DeclTy const* D) { - if constexpr (std::same_as) + if constexpr (std::same_as) { return extractName(D->getDeducedTemplate()); } - else if constexpr (std::derived_from) + else if constexpr (std::derived_from) { if (auto* FD = D->getFriendDecl()) { return extractName(D->getFriendDecl()); } - if (TypeSourceInfo const* FT = D->getFriendType()) + if (clang::TypeSourceInfo const* FT = D->getFriendType()) { std::string Name; llvm::raw_string_ostream os(Name); @@ -1917,27 +1937,27 @@ extractName(DeclTy const* D) return Name; } } - else if constexpr (std::derived_from) + else if constexpr (std::derived_from) { return extractName(D->getNominatedNamespace()); } - else if constexpr (std::derived_from) + else if constexpr (std::derived_from) { - return extractName(cast(D)); + return extractName(cast(D)); } return {}; } std::string ASTVisitor:: -extractName(NamedDecl const* D) +extractName(clang::NamedDecl const* D) { return extractName(D->getDeclName()); } std::string ASTVisitor:: -extractName(DeclarationName const N) +extractName(clang::DeclarationName const N) { std::string result; if (N.isEmpty()) @@ -1946,36 +1966,36 @@ extractName(DeclarationName const N) } switch(N.getNameKind()) { - case DeclarationName::Identifier: + case clang::DeclarationName::Identifier: if (auto const* I = N.getAsIdentifierInfo()) { result.append(I->getName()); } break; - case DeclarationName::CXXDestructorName: + case clang::DeclarationName::CXXDestructorName: result.push_back('~'); [[fallthrough]]; - case DeclarationName::CXXConstructorName: + case clang::DeclarationName::CXXConstructorName: if (auto const* R = N.getCXXNameType()->getAsCXXRecordDecl()) { result.append(R->getIdentifier()->getName()); } break; - case DeclarationName::CXXDeductionGuideName: + case clang::DeclarationName::CXXDeductionGuideName: if (auto const* T = N.getCXXDeductionGuideTemplate()) { result.append(T->getIdentifier()->getName()); } break; - case DeclarationName::CXXConversionFunctionName: + case clang::DeclarationName::CXXConversionFunctionName: { result.append("operator "); // KRYSTIAN FIXME: we *really* should not be // converting types to strings like this - result.append(mrdocs::toString(*toTypeInfo(N.getCXXNameType()))); + result.append(mrdocs::toString(*toType(N.getCXXNameType()))); break; } - case DeclarationName::CXXOperatorName: + case clang::DeclarationName::CXXOperatorName: { OperatorKind const K = toOperatorKind( N.getCXXOverloadedOperator()); @@ -1988,8 +2008,8 @@ extractName(DeclarationName const N) result.append(name); break; } - case DeclarationName::CXXLiteralOperatorName: - case DeclarationName::CXXUsingDirective: + case clang::DeclarationName::CXXLiteralOperatorName: + case clang::DeclarationName::CXXUsingDirective: break; default: MRDOCS_UNREACHABLE(); @@ -1997,30 +2017,30 @@ extractName(DeclarationName const N) return result; } -SmallString<256> +llvm::SmallString<256> ASTVisitor:: -qualifiedName(Decl const* D) const +qualifiedName(clang::Decl const* D) const { - if (auto* ND = dyn_cast(D)) + if (auto* ND = dyn_cast(D)) { return qualifiedName(ND); } return {}; } -SmallString<256> +llvm::SmallString<256> ASTVisitor:: -qualifiedName(NamedDecl const* ND) const +qualifiedName(clang::NamedDecl const* ND) const { - SmallString<256> name; + llvm::SmallString<256> name; llvm::raw_svector_ostream stream(name); getQualifiedName(ND, stream, context_.getPrintingPolicy()); return name; } -Polymorphic +Polymorphic ASTVisitor:: -toTypeInfo(QualType const qt, TraversalMode const mode) +toType(clang::QualType const qt, TraversalMode const mode) { MRDOCS_SYMBOL_TRACE(qt, context_); @@ -2030,110 +2050,114 @@ toTypeInfo(QualType const qt, TraversalMode const mode) // extraction criteria ScopeExitRestore s(mode_, mode); - // Build the TypeInfo representation for the type - TypeInfoBuilder Builder(*this); + // Build the Type representation for the type + TypeBuilder Builder(*this); Builder.Visit(qt); return Builder.result(); } -Polymorphic ASTVisitor::toNameInfo(NestedNameSpecifier NNS) +Optional> +ASTVisitor:: +toName(clang::NestedNameSpecifier NNS) { MRDOCS_SYMBOL_TRACE(NNS, context_); ScopeExitRestore scope(mode_, Dependency); switch(NNS.getKind()) { - case NestedNameSpecifier::Kind::Null: + case clang::NestedNameSpecifier::Kind::Null: return std::nullopt; - case NestedNameSpecifier::Kind::Type: { - const Type *T = NNS.getAsType(); - NameInfoBuilder Builder(*this); + case clang::NestedNameSpecifier::Kind::Type: { + const clang::Type *T = NNS.getAsType(); + NameBuilder Builder(*this); Builder.Visit(T); return Builder.result(); } - case NestedNameSpecifier::Kind::Namespace: { - auto I = Polymorphic(); + case clang::NestedNameSpecifier::Kind::Namespace: { + auto I = Polymorphic(std::in_place_type); auto [ND, Prefix] = NNS.getAsNamespaceAndPrefix(); - I->Name = ND->getIdentifier()->getName(); - I->Prefix = toNameInfo(Prefix); - Decl const* ID = getInstantiatedFrom(ND); - if (Info* info = findOrTraverse(const_cast(ID))) + I->Identifier = ND->getIdentifier()->getName(); + I->Prefix = toName(Prefix); + clang::Decl const* ID = getInstantiatedFrom(ND); + if (Symbol* info = findOrTraverse(const_cast(ID))) { I->id = info->id; } return I; } - case NestedNameSpecifier::Kind::Global: - case NestedNameSpecifier::Kind::MicrosoftSuper: - // FIXME: Unimplemented. + case clang::NestedNameSpecifier::Kind::Global: + case clang::NestedNameSpecifier::Kind::MicrosoftSuper: + default: + // Unimplemented return std::nullopt; } MRDOCS_UNREACHABLE(); } template -Polymorphic +Optional> ASTVisitor:: -toNameInfo(DeclarationName const Name, - std::optional TArgs, - NestedNameSpecifier NNS) +toName( + clang::DeclarationName const Name, + Optional TArgs, + clang::NestedNameSpecifier NNS) { if (Name.isEmpty()) { return std::nullopt; } - Polymorphic I = std::nullopt; - if(TArgs) + Optional> I = std::nullopt; + if (TArgs) { - I = Polymorphic(std::in_place_type); - populate(static_cast(*I).TemplateArgs, *TArgs); + I = Polymorphic(std::in_place_type); + populate((**I).asSpecialization().TemplateArgs, *TArgs); } else { - I = Polymorphic(); + I = Polymorphic(std::in_place_type); } - I->Name = extractName(Name); - I->Prefix = toNameInfo(NNS); + (*I)->Identifier = extractName(Name); + (*I)->Prefix = toName(NNS); return I; } template -Polymorphic +Optional> ASTVisitor:: -toNameInfo( - Decl const* D, - std::optional TArgs, - NestedNameSpecifier NNS) +toName( + clang::Decl const* D, + Optional TArgs, + clang::NestedNameSpecifier NNS) { - auto const* ND = dyn_cast_if_present(D); + auto const* ND = dyn_cast_if_present(D); if (!ND) { return std::nullopt; } - auto I = toNameInfo( - ND->getDeclName(), std::move(TArgs), NNS); + Optional> I = toName(ND->getDeclName(), std::move(TArgs), NNS); if (!I) { return std::nullopt; } + MRDOCS_ASSERT(!I->valueless_after_move()); ScopeExitRestore scope(mode_, Dependency); auto* ID = getInstantiatedFrom(D); - if (Info const* info = findOrTraverse(const_cast(ID))) + if (Symbol const* info = findOrTraverse(const_cast(ID))) { - I->id = info->id; + (*I)->id = info->id; } return I; } template -Polymorphic +Optional> ASTVisitor:: -toNameInfo>( - Decl const* D, - std::optional> TArgs, - NestedNameSpecifier NNS); +toName>( + clang::Decl const* D, + Optional> TArgs, + clang::NestedNameSpecifier NNS); Polymorphic ASTVisitor:: -toTArg(TemplateArgument const& A) +toTArg(clang::TemplateArgument const& A) { // TypePrinter generates an internal placeholder name (e.g. type-parameter-0-0) // for template type parameters used as arguments. it also cannonicalizes @@ -2147,38 +2171,38 @@ toTArg(TemplateArgument const& A) switch(A.getKind()) { // empty template argument (e.g. not yet deduced) - case TemplateArgument::Null: + case clang::TemplateArgument::Null: break; // a template argument pack (any kind) - case TemplateArgument::Pack: + case clang::TemplateArgument::Pack: { - // we should never a TemplateArgument::Pack here + // we should never a clang::TemplateArgument::Pack here MRDOCS_UNREACHABLE(); break; } // type - case TemplateArgument::Type: + case clang::TemplateArgument::Type: { auto R = Polymorphic(std::in_place_type); - QualType QT = A.getAsType(); + clang::QualType QT = A.getAsType(); MRDOCS_ASSERT(! QT.isNull()); // if the template argument is a pack expansion, // use the expansion pattern as the type & mark // the template argument as a pack expansion - if(Type const* T = QT.getTypePtr(); - auto* PT = dyn_cast(T)) + if(clang::Type const* T = QT.getTypePtr(); + auto* PT = dyn_cast(T)) { R->IsPackExpansion = true; QT = PT->getPattern(); } - static_cast(*R).Type = toTypeInfo(QT); + static_cast(*R).Type = toType(QT); return R; } // pack expansion of a template name - case TemplateArgument::TemplateExpansion: + case clang::TemplateArgument::TemplateExpansion: // template name - case TemplateArgument::Template: + case clang::TemplateArgument::Template: { auto R = Polymorphic(std::in_place_type); R->IsPackExpansion = A.isPackExpansion(); @@ -2189,7 +2213,7 @@ toTArg(TemplateArgument const& A) // for the time being, we will use the name & SymbolID of // the referenced declaration (if it isn't dependent), // and fallback to printing the template name otherwise - TemplateName const TN = A.getAsTemplateOrTemplatePattern(); + clang::TemplateName const TN = A.getAsTemplateOrTemplatePattern(); if(auto* TD = TN.getAsTemplateDecl()) { if (auto* II = TD->getIdentifier()) @@ -2201,29 +2225,29 @@ toTArg(TemplateArgument const& A) { llvm::raw_string_ostream stream(Name); TN.print(stream, context_.getPrintingPolicy(), - TemplateName::Qualified::AsWritten); + clang::TemplateName::Qualified::AsWritten); } return R; } // nullptr value - case TemplateArgument::NullPtr: + case clang::TemplateArgument::NullPtr: // expression referencing a declaration - case TemplateArgument::Declaration: + case clang::TemplateArgument::Declaration: // integral expression - case TemplateArgument::Integral: + case clang::TemplateArgument::Integral: // expression - case TemplateArgument::Expression: + case clang::TemplateArgument::Expression: { - auto R = Polymorphic(std::in_place_type); + auto R = Polymorphic(std::in_place_type); R->IsPackExpansion = A.isPackExpansion(); // if this is a pack expansion, use the template argument // expansion pattern in place of the template argument pack - TemplateArgument const& adjusted = + clang::TemplateArgument const& adjusted = R->IsPackExpansion ? A.getPackExpansionPattern() : A; llvm::raw_string_ostream stream( - static_cast(*R).Value.Written); + static_cast(*R).Value.Written); adjusted.print(context_.getPrintingPolicy(), stream, false); return Polymorphic(R); @@ -2231,13 +2255,13 @@ toTArg(TemplateArgument const& A) default: MRDOCS_UNREACHABLE(); } - return std::nullopt; + return Polymorphic(std::in_place_type); } std::string ASTVisitor:: -toString(Expr const* E) +toString(clang::Expr const* E) { std::string result; llvm::raw_string_ostream stream(result); @@ -2247,30 +2271,30 @@ toString(Expr const* E) std::string ASTVisitor:: -toString(Type const* T) +toString(clang::Type const* T) { - if(auto* AT = dyn_cast_if_present(T)) + if(auto* AT = dyn_cast_if_present(T)) { switch(AT->getKeyword()) { - case AutoTypeKeyword::Auto: - case AutoTypeKeyword::GNUAutoType: + case clang::AutoTypeKeyword::Auto: + case clang::AutoTypeKeyword::GNUAutoType: return "auto"; - case AutoTypeKeyword::DecltypeAuto: + case clang::AutoTypeKeyword::DecltypeAuto: return "decltype(auto)"; default: MRDOCS_UNREACHABLE(); } } - if(auto* TTPT = dyn_cast_if_present(T)) + if(auto* TTPT = dyn_cast_if_present(T)) { - if (TemplateTypeParmDecl* TTPD = TTPT->getDecl(); + if (clang::TemplateTypeParmDecl* TTPD = TTPT->getDecl(); TTPD && TTPD->isImplicit()) { return "auto"; } } - return QualType(T, 0).getAsString( + return clang::QualType(T, 0).getAsString( context_.getPrintingPolicy()); } @@ -2291,17 +2315,17 @@ toInteger(llvm::APInt const& V) std::string ASTVisitor:: -getSourceCode(SourceRange const& R) const +getSourceCode(clang::SourceRange const& R) const { - return Lexer::getSourceText( - CharSourceRange::getTokenRange(R), + return clang::Lexer::getSourceText( + clang::CharSourceRange::getTokenRange(R), source_, context_.getLangOpts()).str(); } -std::optional +Optional ASTVisitor:: -extractSFINAEInfo(QualType const T) +extractSFINAEInfo(clang::QualType const T) { MRDOCS_SYMBOL_TRACE(T, context_); MRDOCS_CHECK_OR(config_->sfinae, std::nullopt); @@ -2331,9 +2355,9 @@ extractSFINAEInfo(QualType const T) if (SFINAEControl->ControllingParams[I]) { MRDOCS_SYMBOL_TRACE(Args[I], context_); - TemplateArgument ArgsI = Args[I]; - MRDOCS_CHECK_OR_CONTINUE(ArgsI.getKind() == TemplateArgument::ArgKind::Expression); - Expr* E = Args[I].getAsExpr(); + clang::TemplateArgument ArgsI = Args[I]; + MRDOCS_CHECK_OR_CONTINUE(ArgsI.getKind() == clang::TemplateArgument::ArgKind::Expression); + clang::Expr* E = Args[I].getAsExpr(); MRDOCS_CHECK_OR_CONTINUE(E); Result.Constraints.emplace_back(); populate(Result.Constraints.back(), E); @@ -2344,11 +2368,11 @@ extractSFINAEInfo(QualType const T) return Result; } -std::optional +Optional ASTVisitor:: getSFINAEControlParams( - TemplateDecl* TD, - IdentifierInfo const* Member) + clang::TemplateDecl* TD, + clang::IdentifierInfo const* Member) { MRDOCS_SYMBOL_TRACE(TD, context_); MRDOCS_SYMBOL_TRACE(Member, context_); @@ -2359,18 +2383,18 @@ getSFINAEControlParams( // to find the index of the controlling parameter in the list of // template arguments of the template declaration. auto FindParam = [this]( - ArrayRef Arguments, - TemplateArgument const& Arg) -> std::size_t + llvm::ArrayRef Arguments, + clang::TemplateArgument const& Arg) -> std::size_t { - if (Arg.getKind() != TemplateArgument::Type) + if (Arg.getKind() != clang::TemplateArgument::Type) { return -1; } auto const It = std::ranges::find_if( Arguments, - [&](TemplateArgument const& Other) + [&](clang::TemplateArgument const& Other) { - if (Other.getKind() != TemplateArgument::Type) + if (Other.getKind() != clang::TemplateArgument::Type) { return false; } @@ -2380,7 +2404,7 @@ getSFINAEControlParams( return found ? It - Arguments.data() : static_cast(-1); }; - if(auto* ATD = dyn_cast(TD)) + if(auto* ATD = dyn_cast(TD)) { // If the alias template is an alias template specialization, // we need to do the process for the underlying type @@ -2415,7 +2439,7 @@ getSFINAEControlParams( // Return the controlling parameters with values corresponding to // the primary template arguments - TemplateParameterList* primaryTemplParams = ATD->getTemplateParameters(); + clang::TemplateParameterList* primaryTemplParams = ATD->getTemplateParameters(); MRDOCS_SYMBOL_TRACE(primaryTemplParams, context_); return SFINAEControlParams( primaryTemplParams, @@ -2423,8 +2447,8 @@ getSFINAEControlParams( ParamIdx); } - // Ensure this is a ClassTemplateDecl - auto* CTD = dyn_cast(TD); + // Ensure this is a clang::ClassTemplateDecl + auto* CTD = dyn_cast(TD); MRDOCS_SYMBOL_TRACE(CTD, context_); MRDOCS_CHECK_OR(CTD, std::nullopt); @@ -2433,7 +2457,7 @@ getSFINAEControlParams( MRDOCS_SYMBOL_TRACE(PrimaryArgs, context_); // Type of the member that represents the SFINAE result. - QualType MemberType; + clang::QualType MemberType; // Index of the parameter that represents the the SFINAE result. // For instance, in the specialization `std::enable_if::type`, @@ -2442,14 +2466,14 @@ getSFINAEControlParams( unsigned ParamIdx = -1; // The `IsMismatch` function checks if there's a mismatch between the - // CXXRecordDecl of the ClassTemplateDecl and the specified template + // clang::CXXRecordDecl of the clang::ClassTemplateDecl and the specified template // arguments. If there's a mismatch and `IsMismatch` returns `true`, // the caller returns `std::nullopt` to indicate that the template // is not a SFINAE template. If there are no mismatches, the caller // continues to check the controlling parameters of the template. // This function also updates the `MemberType` and `ParamIdx` variables // so that they can be used to check the controlling parameters. - auto IsMismatch = [&](CXXRecordDecl* RD, ArrayRef Args) + auto IsMismatch = [&](clang::CXXRecordDecl* RD, llvm::ArrayRef Args) { MRDOCS_SYMBOL_TRACE(RD, context_); MRDOCS_SYMBOL_TRACE(Args, context_); @@ -2461,7 +2485,7 @@ getSFINAEControlParams( // as the member `::type` in `std::enable_if` auto MemberLookup = RD->lookup(Member); MRDOCS_SYMBOL_TRACE(MemberLookup, context_); - QualType CurrentType; + clang::QualType CurrentType; if(MemberLookup.empty()) { if (!RD->getNumBases()) @@ -2531,7 +2555,7 @@ getSFINAEControlParams( // SFINAE template. return true; } - if (auto* TND = dyn_cast(MemberLookup.front())) + if (auto* TND = dyn_cast(MemberLookup.front())) { // Update the current type to the underlying type of the // typedef declaration. @@ -2556,7 +2580,7 @@ getSFINAEControlParams( // `true` to indicate a mismatch. if(CurrentType->isDependentType()) { - TemplateArgument asTemplateArg(CurrentType); + clang::TemplateArgument asTemplateArg(CurrentType); auto FoundIdx = FindParam(Args, asTemplateArg); if (FoundIdx == static_cast(-1) || FoundIdx >= PrimaryArgs.size()) @@ -2572,10 +2596,10 @@ getSFINAEControlParams( ParamIdx = FoundIdx; // Get this primary template argument as a template // argument of the current type. - TemplateArgument MappedPrimary = PrimaryArgs[FoundIdx]; + clang::TemplateArgument MappedPrimary = PrimaryArgs[FoundIdx]; MRDOCS_SYMBOL_TRACE(MappedPrimary, context_); // The primary argument in SFINAE should be a type - MRDOCS_ASSERT(MappedPrimary.getKind() == TemplateArgument::Type); + MRDOCS_ASSERT(MappedPrimary.getKind() == clang::TemplateArgument::Type); // Update the current type to the type of the primary argument CurrentType = MappedPrimary.getAsType(); MRDOCS_SYMBOL_TRACE(CurrentType, context_); @@ -2595,7 +2619,7 @@ getSFINAEControlParams( }; // Check if there's a mismatch between the primary record and the arguments - CXXRecordDecl* PrimaryRD = CTD->getTemplatedDecl(); + clang::CXXRecordDecl* PrimaryRD = CTD->getTemplatedDecl(); MRDOCS_SYMBOL_TRACE(PrimaryRD, context_); MRDOCS_CHECK_OR(!IsMismatch(PrimaryRD, PrimaryArgs), std::nullopt); @@ -2607,12 +2631,12 @@ getSFINAEControlParams( { continue; } - ArrayRef SpecArgs = CTSD->getTemplateArgs().asArray(); + llvm::ArrayRef SpecArgs = CTSD->getTemplateArgs().asArray(); MRDOCS_CHECK_OR(!IsMismatch(CTSD, SpecArgs), std::nullopt); } // Check if there's a mismatch between any partial specialization and the arguments - SmallVector PartialSpecs; + llvm::SmallVector PartialSpecs; CTD->getPartialSpecializations(PartialSpecs); for(auto* CTPSD : PartialSpecs) { @@ -2633,16 +2657,16 @@ getSFINAEControlParams( MRDOCS_SYMBOL_TRACE(PartialArgs, context_); for(std::size_t i = 0; i < PartialArgs.size(); ++i) { - TemplateArgument Arg = PartialArgs[i]; + clang::TemplateArgument Arg = PartialArgs[i]; MRDOCS_SYMBOL_TRACE(Arg, context_); switch (Arg.getKind()) { - case TemplateArgument::Integral: - case TemplateArgument::Declaration: - case TemplateArgument::StructuralValue: - case TemplateArgument::NullPtr: + case clang::TemplateArgument::Integral: + case clang::TemplateArgument::Declaration: + case clang::TemplateArgument::StructuralValue: + case clang::TemplateArgument::NullPtr: break; - case TemplateArgument::Expression: + case clang::TemplateArgument::Expression: if(getNTTPFromExpr( Arg.getAsExpr(), CTPSD->getTemplateDepth() - 1)) @@ -2658,8 +2682,8 @@ getSFINAEControlParams( return SFINAEControlParams(CTD->getTemplateParameters(), std::move(ControllingParams), ParamIdx); } -std::optional -ASTVisitor::getSFINAETemplateInfo(QualType T, bool const AllowDependentNames) const +Optional +ASTVisitor::getSFINAETemplateInfo(clang::QualType T, bool const AllowDependentNames) const { MRDOCS_SYMBOL_TRACE(T, context_); MRDOCS_ASSERT(!T.isNull()); @@ -2667,12 +2691,12 @@ ASTVisitor::getSFINAETemplateInfo(QualType T, bool const AllowDependentNames) co // If the type is a dependent name type and dependent names are allowed, // extract the identifier and the qualifier's type SFINAETemplateInfo SFINAE; - if (auto* DNT = T->getAsAdjusted(); + if (auto* DNT = T->getAsAdjusted(); DNT && AllowDependentNames) { SFINAE.Member = DNT->getIdentifier(); MRDOCS_SYMBOL_TRACE(SFINAE.Member, context_); - T = QualType(DNT->getQualifier().getAsType(), 0); + T = clang::QualType(DNT->getQualifier().getAsType(), 0); MRDOCS_SYMBOL_TRACE(T, context_); } if (!T.getTypePtrOrNull()) @@ -2682,7 +2706,7 @@ ASTVisitor::getSFINAETemplateInfo(QualType T, bool const AllowDependentNames) co // If the type is a template specialization type, extract the template name // and the template arguments - if (auto* TST = T->getAsAdjusted()) + if (auto* TST = T->getAsAdjusted()) { MRDOCS_SYMBOL_TRACE(TST, context_); SFINAE.Template = TST->getTemplateName().getAsTemplateDecl(); @@ -2696,11 +2720,11 @@ ASTVisitor::getSFINAETemplateInfo(QualType T, bool const AllowDependentNames) co return std::nullopt; } -std::optional +Optional ASTVisitor:: tryGetTemplateArgument( - TemplateParameterList* Parameters, - ArrayRef const Arguments, + clang::TemplateParameterList* Parameters, + llvm::ArrayRef const Arguments, std::size_t const Index) { MRDOCS_SYMBOL_TRACE(Parameters, context_); @@ -2717,15 +2741,15 @@ tryGetTemplateArgument( MRDOCS_CHECK_OR(Index < Parameters->size(), std::nullopt); // Attempt to get the default argument of the template parameter - NamedDecl* ND = Parameters->getParam(Index); + clang::NamedDecl* ND = Parameters->getParam(Index); MRDOCS_SYMBOL_TRACE(ND, context_); - if(auto* TTPD = dyn_cast(ND); + if(auto* TTPD = dyn_cast(ND); TTPD && TTPD->hasDefaultArgument()) { MRDOCS_SYMBOL_TRACE(TTPD, context_); return TTPD->getDefaultArgument().getArgument(); } - if(auto* NTTPD = dyn_cast(ND); + if(auto* NTTPD = dyn_cast(ND); NTTPD && NTTPD->hasDefaultArgument()) { MRDOCS_SYMBOL_TRACE(NTTPD, context_); @@ -2737,8 +2761,8 @@ tryGetTemplateArgument( ExtractionMode ASTVisitor:: checkFilters( - Decl const* D, - AccessSpecifier const access) + clang::Decl const* D, + clang::AccessSpecifier const access) { if (mode_ == BaseClass && isAnyImplicitSpecialization(D)) @@ -2750,7 +2774,7 @@ checkFilters( // global namespace. It can't fail any of the filters // because its qualified name is represented by the // empty string, and it has no file associated with it. - MRDOCS_CHECK_OR(!isa(D), ExtractionMode::Regular); + MRDOCS_CHECK_OR(!isa(D), ExtractionMode::Regular); // Check if this kind of symbol should be extracted. // This filters symbols supported by MrDocs and @@ -2778,9 +2802,9 @@ checkFilters( bool ASTVisitor:: -checkTypeFilters(Decl const* D, AccessSpecifier const access) +checkTypeFilters(clang::Decl const* D, clang::AccessSpecifier const access) { - if (access == AS_private) + if (access == clang::AS_private) { // Don't extract private members if (isVirtualMember(D)) @@ -2799,7 +2823,7 @@ checkTypeFilters(Decl const* D, AccessSpecifier const access) { MRDOCS_CHECK_OR(!isStaticFileLevelMember(D), false); } - if (!config_->extractLocalClasses && isa(D)) + if (!config_->extractLocalClasses && isa(D)) { if (auto const* FI = findFileInfo(D); FI->full_path.ends_with(".cpp") || @@ -2812,18 +2836,18 @@ checkTypeFilters(Decl const* D, AccessSpecifier const access) } // Don't extract anonymous unions - auto const* RD = dyn_cast(D); + auto const* RD = dyn_cast(D); MRDOCS_CHECK_OR(!RD || !RD->isAnonymousStructOrUnion(), false); // Don't extract declarations implicitly generated by the compiler - MRDOCS_CHECK_OR(!D->isImplicit() || isa(D), false); + MRDOCS_CHECK_OR(!D->isImplicit() || isa(D), false); return true; } bool ASTVisitor:: -checkFileFilters(Decl const* D) +checkFileFilters(clang::Decl const* D) { MRDOCS_SYMBOL_TRACE(D, context_); @@ -2902,7 +2926,7 @@ checkFileFilters(std::string_view const symbolPath) const ASTVisitor::ExtractionInfo ASTVisitor:: -checkSymbolFilters(Decl const* D, bool const AllowParent) +checkSymbolFilters(clang::Decl const* D, bool const AllowParent) { // Use the cache if (auto const it = extraction_.find(D); it != extraction_.end()) @@ -2916,8 +2940,8 @@ checkSymbolFilters(Decl const* D, bool const AllowParent) return result; }; - // If not a NamedDecl, then symbol filters don't apply - auto const* ND = dyn_cast(D); + // If not a clang::NamedDecl, then symbol filters don't apply + auto const* ND = dyn_cast(D); if (!ND) { constexpr ExtractionInfo res{ExtractionMode::Regular, ExtractionMatchType::Strict}; @@ -2925,14 +2949,14 @@ checkSymbolFilters(Decl const* D, bool const AllowParent) } // Get the symbol name - SmallString<256> const name = qualifiedName(ND); + llvm::SmallString<256> const name = qualifiedName(ND); auto const symbolName = name.str(); // Function to check if parent is of a certain extraction mode - auto ParentIs = [&](Decl const* D, ExtractionMode expected) { - if (Decl const* P = getParent(D); + auto ParentIs = [&](clang::Decl const* D, ExtractionMode expected) { + if (clang::Decl const* P = getParent(D); P && - !isa(P)) + !isa(P)) { auto const [parentMode, kind] = checkSymbolFilters(P); return parentMode == expected; @@ -2996,9 +3020,9 @@ checkSymbolFilters(Decl const* D, bool const AllowParent) { // A child of see-below is also see-below (if namespace) // or dependency (if record) - if (Decl const* P = getParent(D); + if (clang::Decl const* P = getParent(D); P && - isa(P)) + isa(P)) { return updateCache( { ExtractionMode::SeeBelow, @@ -3033,13 +3057,13 @@ checkSymbolFilters(Decl const* D, bool const AllowParent) if (containsLiteralPatterns) { // 2b) For each parent namespace - Decl const* Cur = getParent(D); + clang::Decl const* Cur = getParent(D); while (Cur) { - if (isa(Cur)) + if (isa(Cur)) { // 2c) Check if it matches any literal pattern - SmallString<256> const namespaceName = qualifiedName(Cur); + llvm::SmallString<256> const namespaceName = qualifiedName(Cur); for (auto const& [patterns, mode] : patternsAndModes) { if (!patterns->empty() && @@ -3067,9 +3091,9 @@ checkSymbolFilters(Decl const* D, bool const AllowParent) // and `implementation-defined`, we should extract it as `include-symbol`, // since symbols that only pass `include-symbol` will also be included in this namespace // later on. - if (isa(D) || isa(D)) + if (isa(D) || isa(D)) { - SmallString<256> symbolAsPrefix{ symbolName }; + llvm::SmallString<256> symbolAsPrefix{ symbolName }; symbolAsPrefix += "::"; for (auto const& [patterns, mode] : std::ranges::views::reverse(patternsAndModes)) { @@ -3081,12 +3105,12 @@ checkSymbolFilters(Decl const* D, bool const AllowParent) // prefixes that can potentially include children, but // we have to check if any children actually matches // the pattern strictly. - auto const* DC = cast(D); + auto const* DC = cast(D); auto childrenMode = ExtractionMode::Dependency; for (auto* M : DC->decls()) { MRDOCS_SYMBOL_TRACE(M, context_); - if (M->isImplicit() && !isa(M)) + if (M->isImplicit() && !isa(M)) { // Ignore implicit members continue; @@ -3128,7 +3152,7 @@ checkSymbolFilters(Decl const* D, bool const AllowParent) } else if (AllowParent) { - Decl const* P = getParent(D); + clang::Decl const* P = getParent(D); if (P) { // 4) Parent symbols imply this symbol should be included @@ -3212,7 +3236,7 @@ checkSymbolFiltersImpl( } -Info* +Symbol* ASTVisitor:: find(SymbolID const& id) const { @@ -3223,9 +3247,9 @@ find(SymbolID const& id) const return nullptr; } -Info* +Symbol* ASTVisitor:: -find(Decl const* D) const +find(clang::Decl const* D) const { auto ID = generateID(D); MRDOCS_CHECK_OR(ID, nullptr); @@ -3238,8 +3262,8 @@ findFileInfo(clang::SourceLocation const loc) { MRDOCS_CHECK_OR(!loc.isInvalid(), nullptr); // Find the presumed location, ignoring #line directives - PresumedLoc presumed = source_.getPresumedLoc(loc, false); - FileID id = presumed.getFileID(); + clang::PresumedLoc presumed = source_.getPresumedLoc(loc, false); + clang::FileID id = presumed.getFileID(); if(id.isInvalid()) return nullptr; @@ -3254,7 +3278,7 @@ findFileInfo(clang::SourceLocation const loc) ASTVisitor::FileInfo* ASTVisitor:: -findFileInfo(Decl const* D) +findFileInfo(clang::Decl const* D) { clang::SourceLocation Loc = D->getBeginLoc(); if (Loc.isInvalid()) @@ -3301,7 +3325,7 @@ buildFileInfo(std::string_view path) // Attempts to get a relative path for the prefix auto tryGetRelativePosixPath = [&file_info](std::string_view const prefix) - -> std::optional + -> Optional { if (files::startsWith(file_info.full_path, prefix)) { @@ -3317,7 +3341,7 @@ buildFileInfo(std::string_view path) }; auto tryGetRelativePath = [&tryGetRelativePosixPath](std::string_view const prefix) - -> std::optional + -> Optional { if (!files::isAbsolute(prefix)) { @@ -3343,16 +3367,16 @@ buildFileInfo(std::string_view path) } // Find the best match for the file path in the search directories - for (HeaderSearch& HS = sema_.getPreprocessor().getHeaderSearchInfo(); - DirectoryLookup const& DL : HS.search_dir_range()) + for (clang::HeaderSearch& HS = sema_.getPreprocessor().getHeaderSearchInfo(); + clang::DirectoryLookup const& DL : HS.search_dir_range()) { - OptionalDirectoryEntryRef DR = DL.getDirRef(); + clang::OptionalDirectoryEntryRef DR = DL.getDirRef(); if (!DL.isNormalDir() || !DR) { // Only consider normal directories continue; } - StringRef searchDir = DR->getName(); + clang::StringRef searchDir = DR->getName(); if (auto shortPath = tryGetRelativePath(searchDir)) { file_info.short_path = std::string(*shortPath); @@ -3368,7 +3392,7 @@ buildFileInfo(std::string_view path) } // Fallback to system search paths in PATH - std::optional const optEnvPathsStr = llvm::sys::Process::GetEnv("PATH"); + Optional const optEnvPathsStr = llvm::sys::Process::GetEnv("PATH"); MRDOCS_CHECK_OR(optEnvPathsStr, file_info); std::string const& envPathsStr = *optEnvPathsStr; for (auto const envPaths = llvm::split(envPathsStr, llvm::sys::EnvPathSeparator); @@ -3390,14 +3414,14 @@ buildFileInfo(std::string_view path) return file_info; } -template InfoTy> +template InfoTy> ASTVisitor::upsertResult ASTVisitor:: upsert(SymbolID const& id) { // Creating symbol with invalid SymbolID MRDOCS_ASSERT(id != SymbolID::invalid); - Info* info = find(id); + Symbol* info = find(id); bool const isNew = !info; if (!info) { @@ -3465,15 +3489,15 @@ upsert(DeclType const* D) } // Already populate the access specifier - AccessSpecifier const access = getAccess(D); + clang::AccessSpecifier const access = getAccess(D); I.Access = toAccessKind(access); return upsertResult{std::ref(I), isNew}; } template < - std::derived_from InfoTy, - std::derived_from DeclTy> + std::derived_from InfoTy, + std::derived_from DeclTy> Expected ASTVisitor:: checkUndocumented( @@ -3485,7 +3509,7 @@ checkUndocumented( MRDOCS_CHECK_OR(!config_->extractAll, {}); // If the symbol is a namespace, the `extract-all` // doesn't apply to it - MRDOCS_CHECK_OR((!std::same_as), {}); + MRDOCS_CHECK_OR((!std::same_as), {}); // If the symbol is not being extracted as a Regular // symbol, we don't need to check for undocumented symbols // These are expected to be potentially undocumented @@ -3506,7 +3530,7 @@ checkUndocumented( // documented version before. if (auto const infoIt = info_.find(id); infoIt != info_.end() && - infoIt->get()->javadoc) + infoIt->get()->doc) { return {}; } @@ -3518,16 +3542,16 @@ checkUndocumented( auto const undocIt = undocumented_.find(id); if (undocIt == undocumented_.end()) { - InfoKind const kind = InfoTy::kind_id; - undocumented_.insert(UndocumentedInfo{id, extractName(D), kind}); + SymbolKind const kind = InfoTy::kind_id; + undocumented_.insert(UndocumentedSymbol{id, extractName(D), kind}); } // Populate the location auto handle = undocumented_.extract(undocIt); - UndocumentedInfo& UI = handle.value(); - populate(UI.asSourceInfo(), D); + UndocumentedSymbol& UI = handle.value(); + populate(UI.Loc, D); undocumented_.insert(std::move(handle)); } return Unexpected(Error("Undocumented")); } -} // clang::mrdocs +} // mrdocs diff --git a/src/lib/AST/ASTVisitor.hpp b/src/lib/AST/ASTVisitor.hpp index a06e557fb..7d2130f3d 100644 --- a/src/lib/AST/ASTVisitor.hpp +++ b/src/lib/AST/ASTVisitor.hpp @@ -14,21 +14,21 @@ #ifndef MRDOCS_LIB_AST_ASTVISITOR_HPP #define MRDOCS_LIB_AST_ASTVISITOR_HPP -#include "lib/ConfigImpl.hpp" -#include "lib/Support/ExecutionContext.hpp" -#include "lib/AST/ClangHelpers.hpp" -#include -#include +#include +#include +#include #include +#include +#include #include -#include #include +#include #include -namespace clang::mrdocs { +namespace mrdocs { -class TypeInfoBuilder; -class NameInfoBuilder; +class TypeBuilder; +class NameBuilder; template class TerminalTypeVisitor; @@ -43,17 +43,17 @@ class TerminalTypeVisitor; only handles translation units represented by a `clang::ASTContext` by creating an instance of this class and calling the `build` method, which recursively - traverses the `clang::Decl` representing the + traverses the `clang::clang::Decl` representing the translation unit. - As it traverse nodes, the `ASTVisitor` class will + As it traverses nodes, the `ASTVisitor` class will create MrDocs `Info` objects for each declaration that passes the filter configurations. */ class ASTVisitor { - friend TypeInfoBuilder; - friend NameInfoBuilder; + friend TypeBuilder; + friend NameBuilder; template friend class TerminalTypeVisitor; @@ -64,19 +64,19 @@ class ASTVisitor Diagnostics diags_; // The compiler instance - CompilerInstance& compiler_; + clang::CompilerInstance& compiler_; // The AST context - ASTContext& context_; + clang::ASTContext& context_; // The source files in memory - SourceManager& source_; + clang::SourceManager& source_; // Semantic analysis - Sema& sema_; + clang::Sema& sema_; // An unordered set of all extracted Info declarations - InfoSet info_; + SymbolSet info_; /* The symbols we would extract if they were documented @@ -99,7 +99,7 @@ class ASTVisitor units are merged, we will iterate these symbols and warn if they are not documented. */ - UndocumentedInfoSet undocumented_; + UndocumentedSymbolSet undocumented_; /* Struct to hold pre-processed file information. @@ -121,7 +121,7 @@ class ASTVisitor std::string source_path; // Whether this file passes the file filters - std::optional passesFilters; + Optional passesFilters; }; /* A map of Clang FileEntry objects to Visitor FileInfo objects @@ -138,9 +138,9 @@ class ASTVisitor if a file should be extracted or to add the SourceInfo to an Info object. */ - llvm::DenseMap files_; + llvm::DenseMap files_; - /* Determine how a Decl matched the filters + /* Determine how a clang::Decl matched the filters */ enum class ExtractionMatchType { // It matches one of the patterns as is @@ -159,7 +159,7 @@ class ASTVisitor /* Extraction Info This struct is used to store information about - the filters a Decl pass. + the filters a clang::Decl pass. */ struct ExtractionInfo { @@ -171,7 +171,7 @@ class ASTVisitor ExtractionMatchType kind; }; - /* A map of Clang Decl objects to ExtractionMode values + /* A map of Clang clang::Decl objects to ExtractionMode values This map is used to store the extraction mode for declarations that have been identified through the @@ -181,7 +181,7 @@ class ASTVisitor function to determine if a declaration should be extracted based on the extraction mode. */ - std::unordered_map extraction_; + std::unordered_map extraction_; /* How we should traverse the current node */ @@ -254,13 +254,13 @@ class ASTVisitor /* A map which stores the Info types created by each decl. */ - std::unordered_map friendDecls_; + std::unordered_map friendDecls_; public: /** Constructor for ASTVisitor. This constructor initializes the ASTVisitor with the given configuration, - diagnostics, compiler instance, AST context, and Sema object. + diagnostics, compiler instance, AST context, and clang::Sema object. It also initializes clang custom documentation commands and populates `files_` with the FileInfo for files in the @@ -270,14 +270,14 @@ class ASTVisitor @param diags The diagnostics object. @param compiler The compiler instance. @param context The AST context. - @param sema The Sema object. + @param sema The clang::Sema object. */ ASTVisitor( ConfigImpl const& config, Diagnostics const& diags, - CompilerInstance& compiler, - ASTContext& context, - Sema& sema) noexcept; + clang::CompilerInstance& compiler, + clang::ASTContext& context, + clang::Sema& sema) noexcept; /** Build the metadata representation from the AST. @@ -307,9 +307,9 @@ class ASTVisitor This function returns a reference to the set of Info declarations that have been extracted by the ASTVisitor. - @return A reference to the InfoSet containing the extracted Info declarations. + @return A reference to the SymbolSet containing the extracted Info declarations. */ - InfoSet& + SymbolSet& results() { return info_; @@ -320,9 +320,9 @@ class ASTVisitor This function returns a reference to the set of Info declarations that have been extracted by the ASTVisitor. - @return A reference to the InfoSet containing the extracted Info declarations. + @return A reference to the SymbolSet containing the extracted Info declarations. */ - UndocumentedInfoSet& + UndocumentedSymbolSet& undocumented() { return undocumented_; @@ -351,8 +351,8 @@ class ASTVisitor */ template < class InfoTy = void, - std::derived_from DeclTy> - Info* + std::derived_from DeclTy> + Symbol* traverse(DeclTy const* D); /* Traverse a function template @@ -362,8 +362,8 @@ class ASTVisitor concept or function template. */ - Info* - traverse(FunctionTemplateDecl const* D); + Symbol* + traverse(clang::FunctionTemplateDecl const* D); /* Traverse a using directive @@ -373,16 +373,16 @@ class ASTVisitor If the parent declaration is a Namespace, we update its `UsingDirectives` const field. */ - Info* - traverse(UsingDirectiveDecl const* D); + Symbol* + traverse(clang::UsingDirectiveDecl const* D); /* Traverse a member of an anonymous union. We get the anonymous union field and traverse it as a regular `FieldDecl`. */ - Info* - traverse(IndirectFieldDecl const* D); + Symbol* + traverse(clang::IndirectFieldDecl const* D); // ================================================= // AST Traversal Helpers @@ -391,28 +391,28 @@ class ASTVisitor /* Traverse the members of a declaration This function is called to traverse the members of - a Decl that is a DeclContext with other members. + a clang::Decl that is a DeclContext with other members. The function will call traverseAny for all members of the declaration context. */ template < - std::derived_from InfoTy, - std::derived_from DeclTy> - requires (!std::derived_from) + std::derived_from InfoTy, + std::derived_from DeclTy> + requires (!std::derived_from) void traverseMembers(InfoTy& I, DeclTy const* DC); template < - std::derived_from InfoTy, - std::derived_from DeclTy> + std::derived_from InfoTy, + std::derived_from DeclTy> void traverseMembers(InfoTy& I, DeclTy const* DC); /* Traverse the parents of a declaration This function is called to traverse the parents of - a Decl until we find the translation unit declaration + a clang::Decl until we find the translation unit declaration or a parent that has already been extracted. This function is called when the declaration is @@ -424,15 +424,15 @@ class ASTVisitor context. */ template < - std::derived_from InfoTy, - std::derived_from DeclTy> - requires (!std::derived_from) + std::derived_from InfoTy, + std::derived_from DeclTy> + requires (!std::derived_from) void traverseParent(InfoTy& I, DeclTy const* DC); template < - std::derived_from InfoTy, - std::derived_from DeclTy> + std::derived_from InfoTy, + std::derived_from DeclTy> void traverseParent(InfoTy& I, DeclTy const* DC); @@ -440,13 +440,13 @@ class ASTVisitor USRs are strings that provide an unambiguous reference to a symbol. - This function determines the underlying Decl type and + This function determines the underlying clang::Decl type and generates a USR for it. @returns true if USR generation succeeded. */ - Expected> - generateUSR(Decl const* D) const; + Expected> + generateUSR(clang::Decl const* D) const; /* Generate the symbol ID for a declaration. @@ -460,13 +460,13 @@ class ASTVisitor To guarantee the uniqueness of symbols while using a relatively small amount of memory (vs storing - USRs directly), this function hashes the Decl + USRs directly), this function hashes the clang::Decl USR value with SHA1. @return true if the symbol ID could be extracted. */ bool - generateID(Decl const* D, SymbolID& id) const; + generateID(clang::Decl const* D, SymbolID& id) const; /* Extracts the symbol ID for a declaration. @@ -479,123 +479,123 @@ class ASTVisitor @return the symbol ID for the declaration. */ SymbolID - generateID(Decl const* D) const; + generateID(clang::Decl const* D) const; // ================================================= // Populate functions // ================================================= - template DeclTy> + template DeclTy> void - populate(Info& I, bool isNew, DeclTy const* D); + populate(Symbol& I, bool isNew, DeclTy const* D); - template DeclTy> + template DeclTy> void populate(SourceInfo& I, DeclTy const* D); - /* Parse the comments above a declaration as Javadoc + /* Parse the comments above a declaration as DocComment This function will parse the comments above a declaration - as Javadoc, and store the results in the `javadoc` input + as DocComment, and store the results in the `doc` input parameter. @return true if the comments were successfully parsed as - Javadoc, and false otherwise. + DocComment, and false otherwise. */ bool populate( - std::optional& javadoc, - Decl const* D); + Optional& doc, + clang::Decl const* D); void populate(SourceInfo& I, clang::SourceLocation loc, bool definition, bool documented); void - populate(NamespaceInfo& I, NamespaceDecl const* D); + populate(NamespaceSymbol& I, clang::NamespaceDecl const* D); static void - populate(NamespaceInfo& I, TranslationUnitDecl const* D); + populate(NamespaceSymbol& I, clang::TranslationUnitDecl const* D); void - populate(RecordInfo& I, CXXRecordDecl const* D); + populate(RecordSymbol& I, clang::CXXRecordDecl const* D); void - populate(RecordInfo& I, ClassTemplateDecl const* D); + populate(RecordSymbol& I, clang::ClassTemplateDecl const* D); void - populate(RecordInfo& I, ClassTemplateSpecializationDecl const* D); + populate(RecordSymbol& I, clang::ClassTemplateSpecializationDecl const* D); void - populate(RecordInfo& I, ClassTemplatePartialSpecializationDecl const* D); + populate(RecordSymbol& I, clang::ClassTemplatePartialSpecializationDecl const* D); void - populate(FunctionInfo& I, FunctionDecl const* D); + populate(FunctionSymbol& I, clang::FunctionDecl const* D); void - populate(FunctionInfo& I, FunctionTemplateDecl const* D); + populate(FunctionSymbol& I, clang::FunctionTemplateDecl const* D); void - populate(FunctionInfo& I, CXXMethodDecl const* D); + populate(FunctionSymbol& I, clang::CXXMethodDecl const* D); void - populate(FunctionInfo& I, CXXConstructorDecl const* D); + populate(FunctionSymbol& I, clang::CXXConstructorDecl const* D); void - populate(FunctionInfo& I, CXXDestructorDecl const* D); + populate(FunctionSymbol& I, clang::CXXDestructorDecl const* D); void - populate(FunctionInfo& I, CXXConversionDecl const* D); + populate(FunctionSymbol& I, clang::CXXConversionDecl const* D); void - populate(EnumInfo& I, EnumDecl const* D); + populate(EnumSymbol& I, clang::EnumDecl const* D); void - populate(EnumConstantInfo& I, EnumConstantDecl const* D); + populate(EnumConstantSymbol& I, clang::EnumConstantDecl const* D); void - populate(TypedefInfo& I, TypedefNameDecl const* D); + populate(TypedefSymbol& I, clang::TypedefNameDecl const* D); void - populate(TypedefInfo& I, TypedefDecl const* D); + populate(TypedefSymbol& I, clang::TypedefDecl const* D); void - populate(TypedefInfo& I, TypeAliasDecl const* D); + populate(TypedefSymbol& I, clang::TypeAliasDecl const* D); void - populate(TypedefInfo& I, TypeAliasTemplateDecl const* D); + populate(TypedefSymbol& I, clang::TypeAliasTemplateDecl const* D); void - populate(VariableInfo& I, VarDecl const* D); + populate(VariableSymbol& I, clang::VarDecl const* D); void - populate(VariableInfo& I, VarTemplateDecl const* D); + populate(VariableSymbol& I, clang::VarTemplateDecl const* D); void - populate(VariableInfo& I, VarTemplateSpecializationDecl const* D); + populate(VariableSymbol& I, clang::VarTemplateSpecializationDecl const* D); void - populate(VariableInfo& I, VarTemplatePartialSpecializationDecl const* D); + populate(VariableSymbol& I, clang::VarTemplatePartialSpecializationDecl const* D); void - populate(VariableInfo& I, FieldDecl const* D); + populate(VariableSymbol& I, clang::FieldDecl const* D); void - populate(FriendInfo& I, FriendDecl const* D); + populate(FriendInfo& I, clang::FriendDecl const* D); void - populate(GuideInfo& I, CXXDeductionGuideDecl const* D); + populate(GuideSymbol& I, clang::CXXDeductionGuideDecl const* D); void - populate(GuideInfo& I, FunctionTemplateDecl const* D); + populate(GuideSymbol& I, clang::FunctionTemplateDecl const* D); void - populate(NamespaceAliasInfo& I, NamespaceAliasDecl const* D); + populate(NamespaceAliasSymbol& I, clang::NamespaceAliasDecl const* D); void - populate(UsingInfo& I, UsingDecl const* D); + populate(UsingSymbol& I, clang::UsingDecl const* D); void - populate(ConceptInfo& I, ConceptDecl const* D); + populate(ConceptSymbol& I, clang::ConceptDecl const* D); /* Default function to populate the template information @@ -604,34 +604,34 @@ class ASTVisitor of the template declaration. */ template < - std::derived_from DeclTy, - std::derived_from TemplateDeclTy> + std::derived_from DeclTy, + std::derived_from TemplateDeclTy> void populate(TemplateInfo& Template, DeclTy const* D, TemplateDeclTy const* TD); void - populate(TemplateInfo& Template, ClassTemplateSpecializationDecl const* D, ClassTemplateDecl const* CTD); + populate(TemplateInfo& Template, clang::ClassTemplateSpecializationDecl const* D, clang::ClassTemplateDecl const* CTD); /* Populate the template information for a variable template The function will populate the template parameters depending on whether the variable is a specialization. */ - template VarDeclTy> + template VarDeclTy> void - populate(TemplateInfo& Template, VarDeclTy const* D, VarTemplateDecl const* VTD); + populate(TemplateInfo& Template, VarDeclTy const* D, clang::VarTemplateDecl const* VTD); template< - std::derived_from DeclTy, - std::derived_from TemplateDeclTy> + std::derived_from DeclTy, + std::derived_from TemplateDeclTy> void - populate(std::optional& Template, DeclTy const* D, TemplateDeclTy const* VTD) + populate(Optional& Template, DeclTy const* D, TemplateDeclTy const* VTD) { MRDOCS_CHECK_OR(VTD); MRDOCS_CHECK_OR(!VTD->isImplicit()); - if (TemplateParameterList const* TPL = VTD->getTemplateParameters(); + if (clang::TemplateParameterList const* TPL = VTD->getTemplateParameters(); !TPL->empty() && - std::ranges::none_of(TPL->asArray(), [](NamedDecl const* ND) { + std::ranges::none_of(TPL->asArray(), [](clang::NamedDecl const* ND) { return !ND->isImplicit(); })) { @@ -646,27 +646,27 @@ class ASTVisitor } void - populate(NoexceptInfo& I, FunctionProtoType const* FPT); + populate(NoexceptInfo& I, clang::FunctionProtoType const* FPT); void - populate(ExplicitInfo& I, ExplicitSpecifier const& ES); + populate(ExplicitInfo& I, clang::ExplicitSpecifier const& ES); void - populate(ExprInfo& I, Expr const* E); + populate(ExprInfo& I, clang::Expr const* E); template void - populate(ConstantExprInfo& I, Expr const* E); + populate(ConstantExprInfo& I, clang::Expr const* E); template void - populate(ConstantExprInfo& I, Expr const* E, llvm::APInt const& V); + populate(ConstantExprInfo& I, clang::Expr const* E, llvm::APInt const& V); void - populate(Polymorphic& I, NamedDecl const* N); + populate(Polymorphic& I, clang::NamedDecl const* N); void - populate(std::optional& TI, TemplateParameterList const* TPL) + populate(Optional& TI, clang::TemplateParameterList const* TPL) { if (!TI) { @@ -676,16 +676,16 @@ class ASTVisitor } void - populate(TemplateInfo& TI, TemplateParameterList const* TPL); + populate(TemplateInfo& TI, clang::TemplateParameterList const* TPL); - template Range> + template Range> void populate( std::vector>& result, Range&& args) { std::size_t i = 0; - for (TemplateArgument const& arg : args) + for (clang::TemplateArgument const& arg : args) { if (arg.getIsDefaulted()) { @@ -694,7 +694,7 @@ class ASTVisitor // KRYSTIAN NOTE: is this correct? should we have a // separate TArgKind for packs instead of "unlaminating" // them as we are doing here? - if (arg.getKind() == TemplateArgument::Pack) + if (arg.getKind() == clang::TemplateArgument::Pack) { populate(result, arg.pack_elements()); } @@ -716,87 +716,88 @@ class ASTVisitor void populate( std::vector>& result, - ASTTemplateArgumentListInfo const* args); + clang::ASTTemplateArgumentListInfo const* args); - template InfoTy> + template InfoTy> static void - populateAttributes(InfoTy& I, Decl const* D); + populateAttributes(InfoTy& I, clang::Decl const* D); // ================================================= // Populate function helpers // ================================================= - template DeclTy> + template DeclTy> std::string extractName(DeclTy const* D); // Extract the name of a declaration std::string - extractName(NamedDecl const* D); + extractName(clang::NamedDecl const* D); // Extract the name of a declaration std::string - extractName(DeclarationName N); + extractName(clang::DeclarationName N); - SmallString<256> - qualifiedName(Decl const* D) const; + llvm::SmallString<256> + qualifiedName(clang::Decl const* D) const; - SmallString<256> - qualifiedName(NamedDecl const* ND) const; + llvm::SmallString<256> + qualifiedName(clang::NamedDecl const* ND) const; void - addMember(NamespaceInfo& I, Info const& Member); + addMember(NamespaceSymbol& I, Symbol const& Member); void - addMember(RecordInfo& I, Info const& Member); + addMember(RecordSymbol& I, Symbol const& Member); void - addMember(RecordTranche& I, Info const& Member); + addMember(RecordTranche& I, Symbol const& Member); void - addMember(EnumInfo& I, Info const& Member) const; + addMember(EnumSymbol& I, Symbol const& Member) const; void - addMember(OverloadsInfo& I, Info const& Member) const; + addMember(OverloadsSymbol& I, Symbol const& Member) const; void - addMember(std::vector& container, Info const& Member) const; + addMember(std::vector& container, Symbol const& Member) const; - Polymorphic - toTypeInfo(QualType qt, TraversalMode mode); + Polymorphic + toType(clang::QualType qt, TraversalMode mode); - Polymorphic - toTypeInfo(QualType const qt) + Polymorphic + toType(clang::QualType const qt) { - return toTypeInfo(qt, TraversalMode::Dependency); + return toType(qt, TraversalMode::Dependency); } - Polymorphic toNameInfo(NestedNameSpecifier NNS); + Optional> + toName(clang::NestedNameSpecifier NNS); - template > - Polymorphic - toNameInfo( - DeclarationName Name, - std::optional TArgs = std::nullopt, - NestedNameSpecifier NNS = std::nullopt); + template > + Optional> + toName( + clang::DeclarationName Name, + Optional TArgs = std::nullopt, + clang::NestedNameSpecifier NNS = std::nullopt); - template > - Polymorphic - toNameInfo( - Decl const* D, - std::optional TArgs = std::nullopt, - NestedNameSpecifier NNS = std::nullopt); + template > + Optional> + toName( + clang::Decl const* D, + Optional TArgs = std::nullopt, + clang::NestedNameSpecifier NNS = std::nullopt); Polymorphic - toTArg(TemplateArgument const& A); + toTArg(clang::TemplateArgument const& A); // Pretty-print an expression std::string - toString(Expr const* E); + toString(clang::Expr const* E); // Pretty-print a type std::string - toString(Type const* T); + toString(clang::Type const* T); template Integer @@ -810,7 +811,7 @@ class ASTVisitor arguments. */ std::string - getSourceCode(SourceRange const& R) const; + getSourceCode(clang::SourceRange const& R) const; /* Struct to hold the underlying type result of a SFINAE type. @@ -824,7 +825,7 @@ class ASTVisitor struct SFINAEInfo { // The underlying type of the SFINAE type. - QualType Type; + clang::QualType Type; // The template arguments used in the SFINAE context. std::vector Constraints; @@ -844,14 +845,14 @@ class ASTVisitor and the underlying type with the template arguments otherwise. */ - std::optional - extractSFINAEInfo(QualType T); + Optional + extractSFINAEInfo(clang::QualType T); - // @copydoc extractSFINAEInfo(QualType) - std::optional - extractSFINAEInfo(Type const* T) + // @copydoc extractSFINAEInfo(clang::QualType) + Optional + extractSFINAEInfo(clang::Type const* T) { - return extractSFINAEInfo(QualType(T, 0)); + return extractSFINAEInfo(clang::QualType(T, 0)); } /* Struct to hold SFINAE information. @@ -867,13 +868,13 @@ class ASTVisitor struct SFINAETemplateInfo { /// The template declaration involved in SFINAE - TemplateDecl* Template = nullptr; + clang::TemplateDecl* Template = nullptr; /// The identifier of the member being checked. - IdentifierInfo const* Member = nullptr; + clang::IdentifierInfo const* Member = nullptr; /// The template arguments used in the SFINAE context. - ArrayRef Arguments; + llvm::ArrayRef Arguments; }; /* Get the template declaration and member identifier @@ -894,8 +895,8 @@ class ASTVisitor respective to `std::enable_if`: `Template` would be `std::enable_if`, and `Arguments` would be `{B,T}`. */ - std::optional - getSFINAETemplateInfo(QualType T, bool AllowDependentNames) const; + Optional + getSFINAETemplateInfo(clang::QualType T, bool AllowDependentNames) const; /* The controlling parameters of a SFINAE template @@ -913,7 +914,7 @@ class ASTVisitor struct SFINAEControlParams { // The template parameters of the template declaration - TemplateParameterList* Parameters = nullptr; + clang::TemplateParameterList* Parameters = nullptr; // The controlling parameters of the template declaration llvm::SmallBitVector ControllingParams; @@ -935,10 +936,10 @@ class ASTVisitor template information of the underlying type (such as `typename enable_if::type`) will be extract instead. */ - std::optional - getSFINAEControlParams(TemplateDecl* TD, IdentifierInfo const* Member); + Optional + getSFINAEControlParams(clang::TemplateDecl* TD, clang::IdentifierInfo const* Member); - std::optional + Optional getSFINAEControlParams(SFINAETemplateInfo const& SFINAE) { return getSFINAEControlParams(SFINAE.Template, SFINAE.Member); } @@ -953,10 +954,10 @@ class ASTVisitor arguments, the function will attempt to get the argument from the default template arguments. */ - std::optional + Optional tryGetTemplateArgument( - TemplateParameterList* Parameters, - ArrayRef Arguments, + clang::TemplateParameterList* Parameters, + llvm::ArrayRef Arguments, std::size_t Index); // ================================================= @@ -988,28 +989,28 @@ class ASTVisitor possible for the declaration. */ ExtractionMode - checkFilters(Decl const* D, AccessSpecifier access); + checkFilters(clang::Decl const* D, clang::AccessSpecifier access); static ExtractionMode - checkFilters(TranslationUnitDecl const*, AccessSpecifier) + checkFilters(clang::TranslationUnitDecl const*, clang::AccessSpecifier) { return ExtractionMode::Regular; } - template DeclTy> + template DeclTy> ExtractionMode checkFilters(DeclTy const* D) { - AccessSpecifier A = getAccess(D); + clang::AccessSpecifier A = getAccess(D); return checkFilters(D, A); } bool - checkTypeFilters(Decl const* D, AccessSpecifier access); + checkTypeFilters(clang::Decl const* D, clang::AccessSpecifier access); bool - checkFileFilters(Decl const* D); + checkFileFilters(clang::Decl const* D); bool checkFileFilters(std::string_view symbolPath) const; @@ -1021,10 +1022,10 @@ class ASTVisitor be allowed to inherit the value from the parent. */ ExtractionInfo - checkSymbolFilters(Decl const* D, bool AllowParent); + checkSymbolFilters(clang::Decl const* D, bool AllowParent); ExtractionInfo - checkSymbolFilters(Decl const* D) + checkSymbolFilters(clang::Decl const* D) { return checkSymbolFilters(D, true); } @@ -1058,24 +1059,24 @@ class ASTVisitor // Element access // ================================================= - /* Get Info from ASTVisitor InfoSet + /* Get Info from ASTVisitor SymbolSet */ - Info* + Symbol* find(SymbolID const& id) const; - /* Get Info from ASTVisitor InfoSet + /* Get Info from ASTVisitor SymbolSet This function will generate a symbol ID for the declaration and call the other `find` function to get the Info object for the declaration. */ - Info* - find(Decl const* D) const; + Symbol* + find(clang::Decl const* D) const; /* Find or traverse a declaration This function will first attempt to find the Info - object for a declaration in the InfoSet. + object for a declaration in the SymbolSet. If the Info object does not exist, the function will traverse the declaration and create a new @@ -1100,8 +1101,8 @@ class ASTVisitor @return a pointer to the Info object. */ - Info* - findOrTraverse(Decl const* D) + Symbol* + findOrTraverse(clang::Decl const* D) { MRDOCS_CHECK_OR(D, nullptr); if (auto* I = find(D)) @@ -1128,7 +1129,7 @@ class ASTVisitor findFileInfo(clang::SourceLocation loc); FileInfo* - findFileInfo(Decl const* D); + findFileInfo(clang::Decl const* D); /* Build a FileInfo for a string path @@ -1177,7 +1178,7 @@ class ASTVisitor `InfoTy` with the given `id`, and return a reference to the new Info object. */ - template InfoTy> + template InfoTy> upsertResult upsert(SymbolID const& id); @@ -1205,14 +1206,14 @@ class ASTVisitor upsert(DeclType const* D); template < - std::derived_from InfoTy, - std::derived_from DeclTy> + std::derived_from InfoTy, + std::derived_from DeclTy> Expected checkUndocumented( SymbolID const& id, DeclTy const* D); }; -} // clang::mrdocs +} // mrdocs -#endif +#endif // MRDOCS_LIB_AST_ASTVISITOR_HPP diff --git a/src/lib/AST/ASTVisitorConsumer.cpp b/src/lib/AST/ASTVisitorConsumer.cpp index 91b049e7e..dd96ce1ee 100644 --- a/src/lib/AST/ASTVisitorConsumer.cpp +++ b/src/lib/AST/ASTVisitorConsumer.cpp @@ -11,15 +11,15 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include "lib/AST/ASTVisitorConsumer.hpp" -#include "lib/AST/ASTVisitor.hpp" -#include "lib/Support/Path.hpp" +#include +#include +#include -namespace clang::mrdocs { +namespace mrdocs { void ASTVisitorConsumer:: -HandleTranslationUnit(ASTContext& Context) +HandleTranslationUnit(clang::ASTContext& Context) { MRDOCS_ASSERT(sema_); Diagnostics diags; @@ -33,4 +33,4 @@ HandleTranslationUnit(ASTContext& Context) ex_.report(std::move(visitor.results()), std::move(diags), std::move(visitor.undocumented())); } -} // clang::mrdocs +} // mrdocs diff --git a/src/lib/AST/ASTVisitorConsumer.hpp b/src/lib/AST/ASTVisitorConsumer.hpp index 559707ac5..8a15796e1 100644 --- a/src/lib/AST/ASTVisitorConsumer.hpp +++ b/src/lib/AST/ASTVisitorConsumer.hpp @@ -14,13 +14,13 @@ #ifndef MRDOCS_LIB_AST_ASTVISITORCONSUMER_HPP #define MRDOCS_LIB_AST_ASTVISITORCONSUMER_HPP -#include "lib/ConfigImpl.hpp" -#include "lib/Support/ExecutionContext.hpp" #include -#include +#include +#include #include +#include + -namespace clang { namespace mrdocs { /** A consumer for visiting AST nodes and performing semantic analysis. @@ -37,18 +37,18 @@ namespace mrdocs { when the translation unit is complete. */ class ASTVisitorConsumer - : public SemaConsumer + : public clang::SemaConsumer { ConfigImpl const& config_; ExecutionContext& ex_; - CompilerInstance& compiler_; - Sema* sema_ = nullptr; + clang::CompilerInstance& compiler_; + clang::Sema* sema_ = nullptr; public: ASTVisitorConsumer( ConfigImpl const& config, ExecutionContext& ex, - CompilerInstance& compiler) noexcept + clang::CompilerInstance& compiler) noexcept : config_(config) , ex_(ex) , compiler_(compiler) @@ -62,7 +62,7 @@ class ASTVisitorConsumer tree. */ void - InitializeSema(Sema& S) override + InitializeSema(clang::Sema& S) override { // Sema should not have been initialized yet MRDOCS_ASSERT(!sema_); @@ -93,7 +93,7 @@ class ASTVisitorConsumer of declaration or definition is found is left as an empty stub. */ void - HandleTranslationUnit(ASTContext& Context) override; + HandleTranslationUnit(clang::ASTContext& Context) override; /** Handle the specified top-level declaration. @@ -103,7 +103,7 @@ class ASTVisitorConsumer @returns `true` to always continue parsing */ bool - HandleTopLevelDecl(DeclGroupRef) override + HandleTopLevelDecl(clang::DeclGroupRef) override { return true; } @@ -123,7 +123,7 @@ class ASTVisitorConsumer @param D The declaration of the static member variable */ void - HandleCXXStaticMemberVarInstantiation(VarDecl* D) override + HandleCXXStaticMemberVarInstantiation(clang::VarDecl* D) override { D->setImplicit(); } @@ -142,12 +142,12 @@ class ASTVisitorConsumer because implicitly instantiated definitions of member functions of class templates are added to the end of the TU DeclContext. As a result, Decl::isImplicit returns - false for these FunctionDecls, so we manually set it here. + false for these clang::FunctionDecls, so we manually set it here. @param D The declaration of the function */ void - HandleCXXImplicitFunctionInstantiation(FunctionDecl* D) override + HandleCXXImplicitFunctionInstantiation(clang::FunctionDecl* D) override { D->setImplicit(); } @@ -161,7 +161,7 @@ class ASTVisitorConsumer @param D The declaration of the function */ - void HandleInlineFunctionDefinition(FunctionDecl*) override { } + void HandleInlineFunctionDefinition(clang::FunctionDecl*) override { } /** Handle a tag declaration definition. @@ -172,7 +172,7 @@ class ASTVisitorConsumer @param D The declaration of the tag */ - void HandleTagDeclDefinition(TagDecl*) override { } + void HandleTagDeclDefinition(clang::TagDecl*) override { } /** Handle a tag declaration required definition. @@ -183,7 +183,7 @@ class ASTVisitorConsumer @param D The declaration of the tag */ - void HandleTagDeclRequiredDefinition(TagDecl const*) override { } + void HandleTagDeclRequiredDefinition(clang::TagDecl const*) override { } /** Handle an interesting declaration. @@ -196,7 +196,7 @@ class ASTVisitorConsumer @param D The declaration */ - void HandleInterestingDecl(DeclGroupRef) override { } + void HandleInterestingDecl(clang::DeclGroupRef) override { } /** Handle a tentative definition. @@ -206,7 +206,7 @@ class ASTVisitorConsumer @param D The declaration */ - void CompleteTentativeDefinition(VarDecl*) override { } + void CompleteTentativeDefinition(clang::VarDecl*) override { } /** Handle a tentative definition. @@ -216,7 +216,7 @@ class ASTVisitorConsumer @param D The declaration */ - void CompleteExternalDeclaration(DeclaratorDecl*) override { } + void CompleteExternalDeclaration(clang::DeclaratorDecl*) override { } /** Handle a vtable. @@ -226,7 +226,7 @@ class ASTVisitorConsumer @param D The declaration */ - void AssignInheritanceModel(CXXRecordDecl*) override { } + void AssignInheritanceModel(clang::CXXRecordDecl*) override { } /** Handle an implicit import declaration. @@ -236,7 +236,7 @@ class ASTVisitorConsumer @param D The declaration */ - void HandleVTable(CXXRecordDecl*) override { } + void HandleVTable(clang::CXXRecordDecl*) override { } /** Handle an implicit import declaration. @@ -246,7 +246,7 @@ class ASTVisitorConsumer @param D The declaration */ - void HandleImplicitImportDecl(ImportDecl*) override { } + void HandleImplicitImportDecl(clang::ImportDecl*) override { } /** Handle a top-level declaration in an Objective-C container. @@ -257,10 +257,10 @@ class ASTVisitorConsumer @param D The declaration */ - void HandleTopLevelDeclInObjCContainer(DeclGroupRef) override { } + void HandleTopLevelDeclInObjCContainer(clang::DeclGroupRef) override { } }; } // mrdocs -} // clang -#endif + +#endif // MRDOCS_LIB_AST_ASTVISITORCONSUMER_HPP diff --git a/src/lib/AST/ClangHelpers.cpp b/src/lib/AST/ClangHelpers.cpp index 8de956239..430d1c1f8 100644 --- a/src/lib/AST/ClangHelpers.cpp +++ b/src/lib/AST/ClangHelpers.cpp @@ -10,21 +10,21 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include "lib/AST/ClangHelpers.hpp" +#include #include #include -#include #include +#include #include -namespace clang::mrdocs { +namespace mrdocs { -Expr const* +clang::Expr const* SubstituteConstraintExpressionWithoutSatisfaction( - Sema &S, const Sema::TemplateCompareNewDeclInfo &DeclInfo, - const Expr *ConstrExpr) + clang::Sema &S, const clang::Sema::TemplateCompareNewDeclInfo &DeclInfo, + clang::Expr const* ConstrExpr) { - MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs( + clang::MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs( DeclInfo.getDecl(), DeclInfo.getLexicalDeclContext(), /*Final=*/false, /*Innermost=*/std::nullopt, /*RelativeToPrimary=*/true, @@ -36,12 +36,12 @@ SubstituteConstraintExpressionWithoutSatisfaction( return ConstrExpr; } - Sema::SFINAETrap const SFINAE(S, /*AccessCheckingSFINAE=*/false); + clang::Sema::SFINAETrap const SFINAE(S, /*AccessCheckingSFINAE=*/false); - Sema::InstantiatingTemplate Inst( + clang::Sema::InstantiatingTemplate Inst( S, DeclInfo.getLocation(), - Sema::InstantiatingTemplate::ConstraintNormalization{}, - const_cast(DeclInfo.getDecl()), SourceRange{}); + clang::Sema::InstantiatingTemplate::ConstraintNormalization{}, + const_cast(DeclInfo.getDecl()), clang::SourceRange{}); if (Inst.isInvalid()) { return nullptr; @@ -51,7 +51,7 @@ SubstituteConstraintExpressionWithoutSatisfaction( // parameters that the surrounding function hasn't been instantiated yet. Note // this may happen while we're comparing two templates' constraint // equivalence. - LocalInstantiationScope ScopeForParameters(S); + clang::LocalInstantiationScope ScopeForParameters(S); if (auto *FD = DeclInfo.getDecl()->getAsFunction()) { for (auto *PVD : FD->parameters()) @@ -60,7 +60,7 @@ SubstituteConstraintExpressionWithoutSatisfaction( } } - std::optional ThisScope; + Optional ThisScope; // See TreeTransform::RebuildTemplateSpecializationType. A context scope is // essential for having an injected class as the canonical type for a template @@ -69,14 +69,14 @@ SubstituteConstraintExpressionWithoutSatisfaction( // template specializations can be profiled to the same value, which makes it // possible that e.g. constraints involving C> and C are // perceived identical. - std::optional ContextScope; - if (auto *RD = dyn_cast(DeclInfo.getDeclContext())) + Optional ContextScope; + if (auto *RD = dyn_cast(DeclInfo.getDeclContext())) { - ThisScope.emplace(S, const_cast(RD), Qualifiers()); - ContextScope.emplace(S, const_cast(cast(RD)), + ThisScope.emplace(S, const_cast(RD), clang::Qualifiers()); + ContextScope.emplace(S, const_cast(cast(RD)), /*NewThisContext=*/false); } - ExprResult SubstConstr = S.SubstConstraintExprWithoutSatisfaction( + clang::ExprResult SubstConstr = S.SubstConstraintExprWithoutSatisfaction( const_cast(ConstrExpr), MLTAL); if (SFINAE.hasErrorOccurred() || !SubstConstr.isUsable()) { @@ -85,34 +85,34 @@ SubstituteConstraintExpressionWithoutSatisfaction( return SubstConstr.get(); } -AccessSpecifier -getAccess(Decl const* D) +clang::AccessSpecifier +getAccess(clang::Decl const* D) { // First, get the declaration this was instantiated from D = getInstantiatedFrom(D); // If this is the template declaration of a template, // use the access of the template - if (TemplateDecl const* TD = D->getDescribedTemplate()) + if (clang::TemplateDecl const* TD = D->getDescribedTemplate()) { return TD->getAccessUnsafe(); } // For class/variable template partial/explicit specializations, // we want to use the access of the primary template - if (auto const* CTSD = dyn_cast(D)) + if (auto const* CTSD = dyn_cast(D)) { return CTSD->getSpecializedTemplate()->getAccessUnsafe(); } - if (auto const* VTSD = dyn_cast(D)) + if (auto const* VTSD = dyn_cast(D)) { return VTSD->getSpecializedTemplate()->getAccessUnsafe(); } // For function template specializations, use the access of the // primary template if it has been resolved - if(auto const* FD = dyn_cast(D)) + if(auto const* FD = dyn_cast(D)) { if (auto const* FTD = FD->getPrimaryTemplate()) { @@ -124,23 +124,23 @@ getAccess(Decl const* D) // their access based on the default access for the tag they // appear in, and any AccessSpecDecls which appears lexically // before them - if(auto const* FD = dyn_cast(D)) + if(auto const* FD = dyn_cast(D)) { - auto const* RD = dyn_cast( + auto const* RD = dyn_cast( FD->getLexicalDeclContext()); // RD should never be null in well-formed code, // but clang error recovery may build an AST // where the assumption will not hold if (!RD) { - return AccessSpecifier::AS_public; + return clang::AccessSpecifier::AS_public; } auto access = RD->isClass() ? - AccessSpecifier::AS_private : - AccessSpecifier::AS_public; + clang::AccessSpecifier::AS_private : + clang::AccessSpecifier::AS_public; for(auto* M : RD->decls()) { - if (auto* AD = dyn_cast(M)) + if (auto* AD = dyn_cast(M)) { access = AD->getAccessUnsafe(); } else if (M == FD) @@ -158,8 +158,8 @@ getAccess(Decl const* D) return D->getAccessUnsafe(); } -QualType -getDeclaratorType(DeclaratorDecl const* DD) +clang::QualType +getDeclaratorType(clang::DeclaratorDecl const* DD) { if (auto* TSI = DD->getTypeSourceInfo(); TSI && !TSI->getType().isNull()) @@ -169,27 +169,27 @@ getDeclaratorType(DeclaratorDecl const* DD) return DD->getType(); } -NonTypeTemplateParmDecl const* -getNTTPFromExpr(Expr const* E, unsigned const Depth) +clang::NonTypeTemplateParmDecl const* +getNTTPFromExpr(clang::Expr const* E, unsigned const Depth) { while(true) { - if(auto const* ICE = dyn_cast(E)) + if(auto const* ICE = dyn_cast(E)) { E = ICE->getSubExpr(); continue; } - if(auto const* CE = dyn_cast(E)) + if(auto const* CE = dyn_cast(E)) { E = CE->getSubExpr(); continue; } - if(auto const* SNTTPE = dyn_cast(E)) + if(auto const* SNTTPE = dyn_cast(E)) { E = SNTTPE->getReplacement(); continue; } - if(auto const* CCE = dyn_cast(E); + if(auto const* CCE = dyn_cast(E); CCE && CCE->getParenOrBraceRange().isInvalid()) { // look through implicit copy construction from an lvalue of the same type @@ -199,13 +199,13 @@ getNTTPFromExpr(Expr const* E, unsigned const Depth) break; } - auto const* DRE = dyn_cast(E); + auto const* DRE = dyn_cast(E); if (!DRE) { return nullptr; } - auto const* NTTPD = dyn_cast(DRE->getDecl()); + auto const* NTTPD = dyn_cast(DRE->getDecl()); if (!NTTPD || NTTPD->getDepth() != Depth) { return nullptr; @@ -214,30 +214,29 @@ getNTTPFromExpr(Expr const* E, unsigned const Depth) return NTTPD; } -Decl const* -getParent(Decl const* D) +clang::Decl const* +getParent(clang::Decl const* D) { - while((D = cast_if_present< - Decl>(D->getDeclContext()))) + while((D = cast_if_present(D->getDeclContext()))) { switch(D->getKind()) { - case Decl::CXXRecord: + case clang::Decl::CXXRecord: // we treat anonymous unions as "transparent" - if (auto const* RD = cast(D); + if (auto const* RD = cast(D); RD && RD->isAnonymousStructOrUnion()) { break; } [[fallthrough]]; - case Decl::TranslationUnit: - case Decl::Namespace: - case Decl::Enum: - case Decl::ClassTemplateSpecialization: - case Decl::ClassTemplatePartialSpecialization: + case clang::Decl::TranslationUnit: + case clang::Decl::Namespace: + case clang::Decl::Enum: + case clang::Decl::ClassTemplateSpecialization: + case clang::Decl::ClassTemplatePartialSpecialization: // we treat anonymous namespaces as "transparent" - if (auto const* ND = dyn_cast(D); + if (auto const* ND = dyn_cast(D); ND && (ND->isInlineNamespace() || ND->isAnonymousNamespace())) @@ -255,14 +254,14 @@ getParent(Decl const* D) void getQualifiedName( - NamedDecl const* ND, - raw_ostream& stream, - const PrintingPolicy &policy) + clang::NamedDecl const* ND, + clang::raw_ostream& stream, + clang::PrintingPolicy const& policy) { - if (auto const* CTS = dyn_cast(ND)) + if (auto const* CTS = dyn_cast(ND)) { CTS->getSpecializedTemplate()->printQualifiedName(stream, policy); - TemplateArgumentList const& args = CTS->getTemplateArgs(); + clang::TemplateArgumentList const& args = CTS->getTemplateArgs(); stream << '<'; for (unsigned i = 0, e = args.size(); i != e; ++i) { if (args[i].getIsDefaulted()) @@ -283,18 +282,18 @@ getQualifiedName( } } -Decl const* -decayToPrimaryTemplate(Decl const* D) +clang::Decl const* +decayToPrimaryTemplate(clang::Decl const* D) { #ifndef NDEBUG // Print only the class header (name and template args if specialization) - SmallString<128> symbolName; - if (const auto* ND = dyn_cast(D)) + llvm::SmallString<128> symbolName; + if (const auto* ND = dyn_cast(D)) { - if (const auto* CTS = dyn_cast(ND)) { + if (const auto* CTS = dyn_cast(ND)) { llvm::raw_svector_ostream os(symbolName); CTS->getSpecializedTemplate()->printQualifiedName(os, CTS->getASTContext().getPrintingPolicy()); - const TemplateArgumentList& args = CTS->getTemplateArgs(); + const clang::TemplateArgumentList& args = CTS->getTemplateArgs(); os << '<'; for (unsigned i = 0, e = args.size(); i != e; ++i) { @@ -310,28 +309,28 @@ decayToPrimaryTemplate(Decl const* D) else { llvm::raw_svector_ostream os(symbolName); - os << "Decl::getDeclKindName() << ">"; + os << "clang::Decl::getDeclKindName() << ">"; } llvm::raw_svector_ostream os(symbolName); report::trace("symbolName: ", std::string_view(os.str())); #endif - Decl const* ID = D; + clang::Decl const* ID = D; // Check parent - if (CXXRecordDecl const* ClassParent = dyn_cast(getParent(ID))) + if (clang::CXXRecordDecl const* ClassParent = dyn_cast(getParent(ID))) { - if (Decl const* DecayedClassParent = decayToPrimaryTemplate(ClassParent); + if (clang::Decl const* DecayedClassParent = decayToPrimaryTemplate(ClassParent); DecayedClassParent != ClassParent && - isa(DecayedClassParent)) + isa(DecayedClassParent)) { - auto const* RD = dyn_cast(DecayedClassParent); - CXXRecordDecl* RDParent = RD->getTemplatedDecl(); - auto* NamedID = dyn_cast(ID); + auto const* RD = dyn_cast(DecayedClassParent); + clang::CXXRecordDecl* RDParent = RD->getTemplatedDecl(); + auto* NamedID = dyn_cast(ID); auto NamedDecls = RDParent->decls() - | std::ranges::views::transform([](Decl* C) { return dyn_cast(C); }) - | std::ranges::views::filter([](NamedDecl* C) { return C; }); - for (NamedDecl const* Child : NamedDecls) + | std::ranges::views::transform([](clang::Decl* C) { return dyn_cast(C); }) + | std::ranges::views::filter([](clang::NamedDecl* C) { return C; }); + for (clang::NamedDecl const* Child : NamedDecls) { if (Child->getDeclName() == NamedID->getDeclName() && Child->getKind() == ID->getKind()) @@ -344,7 +343,7 @@ decayToPrimaryTemplate(Decl const* D) } // Check template specialization - if (auto const* TSD = dynamic_cast(ID); + if (auto const* TSD = dynamic_cast(ID); TSD && !TSD->isExplicitSpecialization()) { @@ -355,19 +354,19 @@ decayToPrimaryTemplate(Decl const* D) } bool -isAllImplicitSpecialization(Decl const* D) +isAllImplicitSpecialization(clang::Decl const* D) { if (!D) { return true; } - if (auto const* TSD = dynamic_cast(D); + if (auto const* TSD = dynamic_cast(D); TSD && TSD->isExplicitSpecialization()) { return false; } - if (auto const* TSD = dynamic_cast(D); + if (auto const* TSD = dynamic_cast(D); TSD && TSD->isExplicitSpecialization()) { @@ -378,19 +377,19 @@ isAllImplicitSpecialization(Decl const* D) } bool -isAnyImplicitSpecialization(Decl const* D) +isAnyImplicitSpecialization(clang::Decl const* D) { if (!D) { return false; } - if (auto const* TSD = dynamic_cast(D); + if (auto const* TSD = dynamic_cast(D); TSD && !TSD->isExplicitSpecialization()) { return true; } - if (auto const* TSD = dynamic_cast(D); + if (auto const* TSD = dynamic_cast(D); TSD && !TSD->isExplicitSpecialization()) { @@ -401,9 +400,9 @@ isAnyImplicitSpecialization(Decl const* D) } bool -isVirtualMember(Decl const* D) +isVirtualMember(clang::Decl const* D) { - if (auto const* MD = dyn_cast(D)) + if (auto const* MD = dyn_cast(D)) { return MD->isVirtual(); } @@ -411,9 +410,9 @@ isVirtualMember(Decl const* D) } bool -isAnonymousNamespace(Decl const* D) +isAnonymousNamespace(clang::Decl const* D) { - if (auto const* ND = dyn_cast(D)) + if (auto const* ND = dyn_cast(D)) { return ND->isAnonymousNamespace(); } @@ -421,27 +420,27 @@ isAnonymousNamespace(Decl const* D) } bool -isStaticFileLevelMember(Decl const* D) +isStaticFileLevelMember(clang::Decl const* D) { - if (const auto *VD = dyn_cast(D)) { - return VD->getStorageClass() == SC_Static && VD->getDeclContext()->isFileContext(); + if (const auto *VD = dyn_cast(D)) { + return VD->getStorageClass() == clang::SC_Static && VD->getDeclContext()->isFileContext(); } - if (const auto *FD = dyn_cast(D)) { - return FD->getStorageClass() == SC_Static && FD->getDeclContext()->isFileContext(); + if (const auto *FD = dyn_cast(D)) { + return FD->getStorageClass() == clang::SC_Static && FD->getDeclContext()->isFileContext(); } return false; } -RawComment const* -getDocumentation(Decl const* D) +clang::RawComment const* +getDocumentation(clang::Decl const* D) { - RawComment const* RC = + clang::RawComment const* RC = D->getASTContext().getRawCommentForDeclNoCache(D); if (!RC) { - auto const* TD = dyn_cast(D); + auto const* TD = dyn_cast(D); MRDOCS_CHECK_OR(TD, nullptr); - NamedDecl const* ND = TD->getTemplatedDecl(); + clang::NamedDecl const* ND = TD->getTemplatedDecl(); MRDOCS_CHECK_OR(ND, nullptr); RC = ND->getASTContext().getRawCommentForDeclNoCache(ND); } @@ -449,9 +448,9 @@ getDocumentation(Decl const* D) } bool -isDocumented(Decl const* D) +isDocumented(clang::Decl const* D) { return getDocumentation(D) != nullptr; } -} // clang::mrdocs +} // mrdocs diff --git a/src/lib/AST/ClangHelpers.hpp b/src/lib/AST/ClangHelpers.hpp index 2cd2cbf0e..f2305f6fa 100644 --- a/src/lib/AST/ClangHelpers.hpp +++ b/src/lib/AST/ClangHelpers.hpp @@ -14,20 +14,21 @@ #ifndef MRDOCS_LIB_AST_CLANGHELPERS_HPP #define MRDOCS_LIB_AST_CLANGHELPERS_HPP -#include -#include -#include -#include -#include #include +#include #include +#include +#include +#include #include #include #include #include +#include +#include #include -namespace clang::mrdocs { +namespace mrdocs { /** Substitute the constraint expression without satisfaction. @@ -42,11 +43,11 @@ namespace clang::mrdocs { @param ConstrExpr The constraint expression to be substituted. @return The substituted constraint expression, or nullptr if an error occurs. */ -Expr const* +clang::Expr const* SubstituteConstraintExpressionWithoutSatisfaction( - Sema &S, - const Sema::TemplateCompareNewDeclInfo &DeclInfo, - const Expr *ConstrExpr); + clang::Sema &S, + const clang::Sema::TemplateCompareNewDeclInfo &DeclInfo, + const clang::Expr *ConstrExpr); /** Determine the MrDocs Info type for a Clang DeclType @@ -67,131 +68,131 @@ SubstituteConstraintExpressionWithoutSatisfaction( template struct InfoTypeFor {}; -// Extract NamespaceInfo from NamespaceDecl or TranslationUnitDecl +// Extract NamespaceSymbol from NamespaceDecl or TranslationUnitDecl template <> -struct InfoTypeFor - : std::type_identity {}; +struct InfoTypeFor + : std::type_identity {}; template <> -struct InfoTypeFor - : std::type_identity {}; +struct InfoTypeFor + : std::type_identity {}; -// Extract RecordInfo from anything derived from CXXRecordDecl -// and ClassTemplateDecl. Decls derived from CXXRecordDecl +// Extract RecordSymbol from anything derived from clang::CXXRecordDecl +// and clang::ClassTemplateDecl. Decls derived from clang::CXXRecordDecl // include class specializations. template <> -struct InfoTypeFor - : std::type_identity {}; +struct InfoTypeFor + : std::type_identity {}; template <> -struct InfoTypeFor - : std::type_identity {}; +struct InfoTypeFor + : std::type_identity {}; template <> -struct InfoTypeFor - : std::type_identity {}; +struct InfoTypeFor + : std::type_identity {}; template <> -struct InfoTypeFor - : std::type_identity {}; +struct InfoTypeFor + : std::type_identity {}; -// Extract FunctionInfo from anything derived from FunctionDecl +// Extract FunctionSymbol from anything derived from clang::FunctionDecl template <> -struct InfoTypeFor - : std::type_identity {}; +struct InfoTypeFor + : std::type_identity {}; template <> -struct InfoTypeFor - : std::type_identity {}; +struct InfoTypeFor + : std::type_identity {}; template <> -struct InfoTypeFor - : std::type_identity {}; +struct InfoTypeFor + : std::type_identity {}; template <> -struct InfoTypeFor - : std::type_identity {}; +struct InfoTypeFor + : std::type_identity {}; template <> -struct InfoTypeFor - : std::type_identity {}; +struct InfoTypeFor + : std::type_identity {}; template <> -struct InfoTypeFor - : std::type_identity {}; +struct InfoTypeFor + : std::type_identity {}; -// Extract EnumInfo from EnumDecl +// Extract EnumSymbol from EnumDecl template <> -struct InfoTypeFor - : std::type_identity {}; +struct InfoTypeFor + : std::type_identity {}; -// Extract EnumConstantInfo from EnumConstantDecl +// Extract EnumConstantSymbol from EnumConstantDecl template <> -struct InfoTypeFor - : std::type_identity {}; +struct InfoTypeFor + : std::type_identity {}; -// Extract TypedefInfo from anything derived from TypedefNameDecl +// Extract TypedefSymbol from anything derived from TypedefNameDecl template <> -struct InfoTypeFor - : std::type_identity {}; +struct InfoTypeFor + : std::type_identity {}; template <> -struct InfoTypeFor - : std::type_identity {}; +struct InfoTypeFor + : std::type_identity {}; template <> -struct InfoTypeFor - : std::type_identity {}; +struct InfoTypeFor + : std::type_identity {}; template <> -struct InfoTypeFor - : std::type_identity {}; +struct InfoTypeFor + : std::type_identity {}; -// Extract VariableInfo from anything derived from VarDecl +// Extract VariableSymbol from anything derived from VarDecl // and VarTemplateDecl. template <> -struct InfoTypeFor - : std::type_identity {}; +struct InfoTypeFor + : std::type_identity {}; template <> -struct InfoTypeFor - : std::type_identity {}; +struct InfoTypeFor + : std::type_identity {}; template <> -struct InfoTypeFor - : std::type_identity {}; +struct InfoTypeFor + : std::type_identity {}; template <> -struct InfoTypeFor - : std::type_identity {}; +struct InfoTypeFor + : std::type_identity {}; template <> -struct InfoTypeFor - : std::type_identity {}; +struct InfoTypeFor + : std::type_identity {}; -// Extract GuideInfo from CXXDeductionGuideDecl +// Extract GuideSymbol from CXXDeductionGuideDecl template <> -struct InfoTypeFor - : std::type_identity {}; +struct InfoTypeFor + : std::type_identity {}; -// Extract NamespaceAliasInfo from NamespaceAliasDecl +// Extract NamespaceAliasSymbol from NamespaceAliasDecl template <> -struct InfoTypeFor - : std::type_identity {}; +struct InfoTypeFor + : std::type_identity {}; -// Extract UsingInfo from UsingDecl +// Extract UsingSymbol from UsingDecl template <> -struct InfoTypeFor - : std::type_identity {}; +struct InfoTypeFor + : std::type_identity {}; -// Extract ConceptInfo from ConceptDecl +// Extract ConceptSymbol from ConceptDecl template <> -struct InfoTypeFor - : std::type_identity {}; +struct InfoTypeFor + : std::type_identity {}; /// Determine if there's a MrDocs Info type for a Clang DeclType template -concept HasInfoTypeFor = std::derived_from && requires +concept HasInfoTypeFor = std::derived_from && requires { typename InfoTypeFor::type; }; @@ -204,14 +205,14 @@ using InfoTypeFor_t = typename InfoTypeFor::type; */ inline AccessKind -toAccessKind(AccessSpecifier const spec) +toAccessKind(clang::AccessSpecifier const spec) { switch(spec) { - case AccessSpecifier::AS_public: return AccessKind::Public; - case AccessSpecifier::AS_protected: return AccessKind::Protected; - case AccessSpecifier::AS_private: return AccessKind::Private; - case AccessSpecifier::AS_none: return AccessKind::None; + case clang::AccessSpecifier::AS_public: return AccessKind::Public; + case clang::AccessSpecifier::AS_protected: return AccessKind::Protected; + case clang::AccessSpecifier::AS_private: return AccessKind::Private; + case clang::AccessSpecifier::AS_none: return AccessKind::None; default: MRDOCS_UNREACHABLE(); } @@ -221,15 +222,15 @@ toAccessKind(AccessSpecifier const spec) */ inline StorageClassKind -toStorageClassKind(StorageClass const spec) +toStorageClassKind(clang::StorageClass const spec) { switch(spec) { - case StorageClass::SC_None: return StorageClassKind::None; - case StorageClass::SC_Extern: return StorageClassKind::Extern; - case StorageClass::SC_Static: return StorageClassKind::Static; - case StorageClass::SC_Auto: return StorageClassKind::Auto; - case StorageClass::SC_Register: return StorageClassKind::Register; + case clang::StorageClass::SC_None: return StorageClassKind::None; + case clang::StorageClass::SC_Extern: return StorageClassKind::Extern; + case clang::StorageClass::SC_Static: return StorageClassKind::Static; + case clang::StorageClass::SC_Auto: return StorageClassKind::Auto; + case clang::StorageClass::SC_Register: return StorageClassKind::Register; default: // SC_PrivateExtern (__private_extern__) // is a C only Apple extension @@ -241,18 +242,18 @@ toStorageClassKind(StorageClass const spec) */ inline ConstexprKind -toConstexprKind(ConstexprSpecKind const spec) +toConstexprKind(clang::ConstexprSpecKind const spec) { switch(spec) { - case ConstexprSpecKind::Unspecified: return ConstexprKind::None; - case ConstexprSpecKind::Constexpr: return ConstexprKind::Constexpr; - case ConstexprSpecKind::Consteval: return ConstexprKind::Consteval; - // KRYSTIAN NOTE: ConstexprSpecKind::Constinit exists, + case clang::ConstexprSpecKind::Unspecified: return ConstexprKind::None; + case clang::ConstexprSpecKind::Constexpr: return ConstexprKind::Constexpr; + case clang::ConstexprSpecKind::Consteval: return ConstexprKind::Consteval; + // KRYSTIAN NOTE: clang::ConstexprSpecKind::Constinit exists, // but I don't think it's ever used because a variable // can be declared both constexpr and constinit // (but not both in the same declaration) - case ConstexprSpecKind::Constinit: + case clang::ConstexprSpecKind::Constinit: default: MRDOCS_UNREACHABLE(); } @@ -262,7 +263,7 @@ toConstexprKind(ConstexprSpecKind const spec) */ inline ExplicitKind -toExplicitKind(ExplicitSpecifier const& spec) +toExplicitKind(clang::ExplicitSpecifier const& spec) { // no explicit-specifier if (!spec.isSpecified()) @@ -272,9 +273,9 @@ toExplicitKind(ExplicitSpecifier const& spec) switch(spec.getKind()) { - case ExplicitSpecKind::ResolvedFalse: return ExplicitKind::False; - case ExplicitSpecKind::ResolvedTrue: return ExplicitKind::True; - case ExplicitSpecKind::Unresolved: return ExplicitKind::Dependent; + case clang::ExplicitSpecKind::ResolvedFalse: return ExplicitKind::False; + case clang::ExplicitSpecKind::ResolvedTrue: return ExplicitKind::True; + case clang::ExplicitSpecKind::Unresolved: return ExplicitKind::Dependent; default: MRDOCS_UNREACHABLE(); } @@ -284,133 +285,133 @@ toExplicitKind(ExplicitSpecifier const& spec) */ inline NoexceptKind -toNoexceptKind(ExceptionSpecificationType const spec) +toNoexceptKind(clang::ExceptionSpecificationType const spec) { // KRYSTIAN TODO: right now we convert pre-C++17 dynamic exception // specifications to an (roughly) equivalent noexcept-specifier switch(spec) { - case ExceptionSpecificationType::EST_None: - case ExceptionSpecificationType::EST_MSAny: - case ExceptionSpecificationType::EST_Unevaluated: - case ExceptionSpecificationType::EST_Uninstantiated: + case clang::ExceptionSpecificationType::EST_None: + case clang::ExceptionSpecificationType::EST_MSAny: + case clang::ExceptionSpecificationType::EST_Unevaluated: + case clang::ExceptionSpecificationType::EST_Uninstantiated: // we *shouldn't* ever encounter an unparsed exception specification, // assuming that clang is working correctly... - case ExceptionSpecificationType::EST_Unparsed: - case ExceptionSpecificationType::EST_Dynamic: - case ExceptionSpecificationType::EST_NoexceptFalse: + case clang::ExceptionSpecificationType::EST_Unparsed: + case clang::ExceptionSpecificationType::EST_Dynamic: + case clang::ExceptionSpecificationType::EST_NoexceptFalse: return NoexceptKind::False; - case ExceptionSpecificationType::EST_NoThrow: - case ExceptionSpecificationType::EST_BasicNoexcept: - case ExceptionSpecificationType::EST_NoexceptTrue: - case ExceptionSpecificationType::EST_DynamicNone: + case clang::ExceptionSpecificationType::EST_NoThrow: + case clang::ExceptionSpecificationType::EST_BasicNoexcept: + case clang::ExceptionSpecificationType::EST_NoexceptTrue: + case clang::ExceptionSpecificationType::EST_DynamicNone: return NoexceptKind::True; - case ExceptionSpecificationType::EST_DependentNoexcept: + case clang::ExceptionSpecificationType::EST_DependentNoexcept: return NoexceptKind::Dependent; default: MRDOCS_UNREACHABLE(); } } -/** Convert a Clang OverloadedOperatorKind into a MrDocs OperatorKind +/** Convert a Clang clang::OverloadedOperatorKind into a MrDocs OperatorKind */ inline OperatorKind -toOperatorKind(OverloadedOperatorKind const kind) +toOperatorKind(clang::OverloadedOperatorKind const kind) { switch(kind) { - case OverloadedOperatorKind::OO_None: + case clang::OverloadedOperatorKind::OO_None: return OperatorKind::None; - case OverloadedOperatorKind::OO_New: + case clang::OverloadedOperatorKind::OO_New: return OperatorKind::New; - case OverloadedOperatorKind::OO_Delete: + case clang::OverloadedOperatorKind::OO_Delete: return OperatorKind::Delete; - case OverloadedOperatorKind::OO_Array_New: + case clang::OverloadedOperatorKind::OO_Array_New: return OperatorKind::ArrayNew; - case OverloadedOperatorKind::OO_Array_Delete: + case clang::OverloadedOperatorKind::OO_Array_Delete: return OperatorKind::ArrayDelete; - case OverloadedOperatorKind::OO_Plus: + case clang::OverloadedOperatorKind::OO_Plus: return OperatorKind::Plus; - case OverloadedOperatorKind::OO_Minus: + case clang::OverloadedOperatorKind::OO_Minus: return OperatorKind::Minus; - case OverloadedOperatorKind::OO_Star: + case clang::OverloadedOperatorKind::OO_Star: return OperatorKind::Star; - case OverloadedOperatorKind::OO_Slash: + case clang::OverloadedOperatorKind::OO_Slash: return OperatorKind::Slash; - case OverloadedOperatorKind::OO_Percent: + case clang::OverloadedOperatorKind::OO_Percent: return OperatorKind::Percent; - case OverloadedOperatorKind::OO_Caret: + case clang::OverloadedOperatorKind::OO_Caret: return OperatorKind::Caret; - case OverloadedOperatorKind::OO_Amp: + case clang::OverloadedOperatorKind::OO_Amp: return OperatorKind::Amp; - case OverloadedOperatorKind::OO_Pipe: + case clang::OverloadedOperatorKind::OO_Pipe: return OperatorKind::Pipe; - case OverloadedOperatorKind::OO_Tilde: + case clang::OverloadedOperatorKind::OO_Tilde: return OperatorKind::Tilde; - case OverloadedOperatorKind::OO_Exclaim: + case clang::OverloadedOperatorKind::OO_Exclaim: return OperatorKind::Exclaim; - case OverloadedOperatorKind::OO_Equal: + case clang::OverloadedOperatorKind::OO_Equal: return OperatorKind::Equal; - case OverloadedOperatorKind::OO_Less: + case clang::OverloadedOperatorKind::OO_Less: return OperatorKind::Less; - case OverloadedOperatorKind::OO_Greater: + case clang::OverloadedOperatorKind::OO_Greater: return OperatorKind::Greater; - case OverloadedOperatorKind::OO_PlusEqual: + case clang::OverloadedOperatorKind::OO_PlusEqual: return OperatorKind::PlusEqual; - case OverloadedOperatorKind::OO_MinusEqual: + case clang::OverloadedOperatorKind::OO_MinusEqual: return OperatorKind::MinusEqual; - case OverloadedOperatorKind::OO_StarEqual: + case clang::OverloadedOperatorKind::OO_StarEqual: return OperatorKind::StarEqual; - case OverloadedOperatorKind::OO_SlashEqual: + case clang::OverloadedOperatorKind::OO_SlashEqual: return OperatorKind::SlashEqual; - case OverloadedOperatorKind::OO_PercentEqual: + case clang::OverloadedOperatorKind::OO_PercentEqual: return OperatorKind::PercentEqual; - case OverloadedOperatorKind::OO_CaretEqual: + case clang::OverloadedOperatorKind::OO_CaretEqual: return OperatorKind::CaretEqual; - case OverloadedOperatorKind::OO_AmpEqual: + case clang::OverloadedOperatorKind::OO_AmpEqual: return OperatorKind::AmpEqual; - case OverloadedOperatorKind::OO_PipeEqual: + case clang::OverloadedOperatorKind::OO_PipeEqual: return OperatorKind::PipeEqual; - case OverloadedOperatorKind::OO_LessLess: + case clang::OverloadedOperatorKind::OO_LessLess: return OperatorKind::LessLess; - case OverloadedOperatorKind::OO_GreaterGreater: + case clang::OverloadedOperatorKind::OO_GreaterGreater: return OperatorKind::GreaterGreater; - case OverloadedOperatorKind::OO_LessLessEqual: + case clang::OverloadedOperatorKind::OO_LessLessEqual: return OperatorKind::LessLessEqual; - case OverloadedOperatorKind::OO_GreaterGreaterEqual: + case clang::OverloadedOperatorKind::OO_GreaterGreaterEqual: return OperatorKind::GreaterGreaterEqual; - case OverloadedOperatorKind::OO_EqualEqual: + case clang::OverloadedOperatorKind::OO_EqualEqual: return OperatorKind::EqualEqual; - case OverloadedOperatorKind::OO_ExclaimEqual: + case clang::OverloadedOperatorKind::OO_ExclaimEqual: return OperatorKind::ExclaimEqual; - case OverloadedOperatorKind::OO_LessEqual: + case clang::OverloadedOperatorKind::OO_LessEqual: return OperatorKind::LessEqual; - case OverloadedOperatorKind::OO_GreaterEqual: + case clang::OverloadedOperatorKind::OO_GreaterEqual: return OperatorKind::GreaterEqual; - case OverloadedOperatorKind::OO_Spaceship: + case clang::OverloadedOperatorKind::OO_Spaceship: return OperatorKind::Spaceship; - case OverloadedOperatorKind::OO_AmpAmp: + case clang::OverloadedOperatorKind::OO_AmpAmp: return OperatorKind::AmpAmp; - case OverloadedOperatorKind::OO_PipePipe: + case clang::OverloadedOperatorKind::OO_PipePipe: return OperatorKind::PipePipe; - case OverloadedOperatorKind::OO_PlusPlus: + case clang::OverloadedOperatorKind::OO_PlusPlus: return OperatorKind::PlusPlus; - case OverloadedOperatorKind::OO_MinusMinus: + case clang::OverloadedOperatorKind::OO_MinusMinus: return OperatorKind::MinusMinus; - case OverloadedOperatorKind::OO_Comma: + case clang::OverloadedOperatorKind::OO_Comma: return OperatorKind::Comma; - case OverloadedOperatorKind::OO_ArrowStar: + case clang::OverloadedOperatorKind::OO_ArrowStar: return OperatorKind::ArrowStar; - case OverloadedOperatorKind::OO_Arrow: + case clang::OverloadedOperatorKind::OO_Arrow: return OperatorKind::Arrow; - case OverloadedOperatorKind::OO_Call: + case clang::OverloadedOperatorKind::OO_Call: return OperatorKind::Call; - case OverloadedOperatorKind::OO_Subscript: + case clang::OverloadedOperatorKind::OO_Subscript: return OperatorKind::Subscript; - case OverloadedOperatorKind::OO_Conditional: + case clang::OverloadedOperatorKind::OO_Conditional: return OperatorKind::Conditional; - case OverloadedOperatorKind::OO_Coawait: + case clang::OverloadedOperatorKind::OO_Coawait: return OperatorKind::Coawait; default: MRDOCS_UNREACHABLE(); @@ -421,34 +422,34 @@ toOperatorKind(OverloadedOperatorKind const kind) */ inline ReferenceKind -toReferenceKind(RefQualifierKind const kind) +toReferenceKind(clang::RefQualifierKind const kind) { switch(kind) { - case RefQualifierKind::RQ_None: + case clang::RefQualifierKind::RQ_None: return ReferenceKind::None; - case RefQualifierKind::RQ_LValue: + case clang::RefQualifierKind::RQ_LValue: return ReferenceKind::LValue; - case RefQualifierKind::RQ_RValue: + case clang::RefQualifierKind::RQ_RValue: return ReferenceKind::RValue; default: MRDOCS_UNREACHABLE(); } } -/** Convert a Clang TagTypeKind into a MrDocs RecordKeyKind +/** Convert a Clang clang::TagTypeKind into a MrDocs RecordKeyKind */ inline RecordKeyKind -toRecordKeyKind(TagTypeKind const kind) +toRecordKeyKind(clang::TagTypeKind const kind) { switch(kind) { - case TagTypeKind::Struct: return RecordKeyKind::Struct; - case TagTypeKind::Class: return RecordKeyKind::Class; - case TagTypeKind::Union: return RecordKeyKind::Union; + case clang::TagTypeKind::Struct: return RecordKeyKind::Struct; + case clang::TagTypeKind::Class: return RecordKeyKind::Class; + case clang::TagTypeKind::Union: return RecordKeyKind::Union; default: - // unsupported TagTypeKind (Interface, or Enum) + // unsupported clang::TagTypeKind (Interface, or Enum) MRDOCS_UNREACHABLE(); } } @@ -460,11 +461,11 @@ QualifierKind toQualifierKind(unsigned const quals) { std::underlying_type_t result = QualifierKind::None; - if (quals & Qualifiers::Const) + if (quals & clang::Qualifiers::Const) { result |= QualifierKind::Const; } - if (quals & Qualifiers::Volatile) + if (quals & clang::Qualifiers::Volatile) { result |= QualifierKind::Volatile; } @@ -476,89 +477,89 @@ toQualifierKind(unsigned const quals) */ inline FunctionClass -toFunctionClass(Decl::Kind const kind) +toFunctionClass(clang::Decl::Kind const kind) { switch(kind) { - case Decl::Kind::Function: - case Decl::Kind::CXXMethod: return FunctionClass::Normal; - case Decl::Kind::CXXConstructor: return FunctionClass::Constructor; - case Decl::Kind::CXXConversion: return FunctionClass::Conversion; - case Decl::Kind::CXXDestructor: return FunctionClass::Destructor; + case clang::Decl::Kind::Function: + case clang::Decl::Kind::CXXMethod: return FunctionClass::Normal; + case clang::Decl::Kind::CXXConstructor: return FunctionClass::Constructor; + case clang::Decl::Kind::CXXConversion: return FunctionClass::Conversion; + case clang::Decl::Kind::CXXDestructor: return FunctionClass::Destructor; default: MRDOCS_UNREACHABLE(); } } -/** Convert a Clang AutoTypeKeyword into a MrDocs AutoKind +/** Convert a Clang clang::AutoTypeKeyword into a MrDocs AutoKind */ inline AutoKind -toAutoKind(AutoTypeKeyword const kind) +toAutoKind(clang::AutoTypeKeyword const kind) { switch(kind) { - case AutoTypeKeyword::Auto: - case AutoTypeKeyword::GNUAutoType: + case clang::AutoTypeKeyword::Auto: + case clang::AutoTypeKeyword::GNUAutoType: return AutoKind::Auto; - case AutoTypeKeyword::DecltypeAuto: + case clang::AutoTypeKeyword::DecltypeAuto: return AutoKind::DecltypeAuto; default: MRDOCS_UNREACHABLE(); } } -/** Convert a Clang AutoTypeKeyword into a MrDocs AutoKind +/** Convert a Clang clang::clang::BuiltinType into a MrDocs AutoKind */ inline -std::optional -toFundamentalTypeKind(BuiltinType::Kind const kind) +Optional +toFundamentalTypeKind(clang::BuiltinType::Kind const kind) { switch(kind) { - case BuiltinType::Kind::Void: + case clang::BuiltinType::Kind::Void: return FundamentalTypeKind::Void; - case BuiltinType::Kind::NullPtr: + case clang::BuiltinType::Kind::NullPtr: return FundamentalTypeKind::Nullptr; - case BuiltinType::Kind::Bool: + case clang::BuiltinType::Kind::Bool: return FundamentalTypeKind::Bool; - case BuiltinType::Kind::Char_U: - case BuiltinType::Kind::Char_S: + case clang::BuiltinType::Kind::Char_U: + case clang::BuiltinType::Kind::Char_S: return FundamentalTypeKind::Char; - case BuiltinType::Kind::SChar: + case clang::BuiltinType::Kind::SChar: return FundamentalTypeKind::SignedChar; - case BuiltinType::Kind::UChar: + case clang::BuiltinType::Kind::UChar: return FundamentalTypeKind::UnsignedChar; - case BuiltinType::Kind::Char8: + case clang::BuiltinType::Kind::Char8: return FundamentalTypeKind::Char8; - case BuiltinType::Kind::Char16: + case clang::BuiltinType::Kind::Char16: return FundamentalTypeKind::Char16; - case BuiltinType::Kind::Char32: + case clang::BuiltinType::Kind::Char32: return FundamentalTypeKind::Char32; - case BuiltinType::Kind::WChar_S: - case BuiltinType::Kind::WChar_U: + case clang::BuiltinType::Kind::WChar_S: + case clang::BuiltinType::Kind::WChar_U: return FundamentalTypeKind::WChar; - case BuiltinType::Kind::Short: + case clang::BuiltinType::Kind::Short: return FundamentalTypeKind::Short; - case BuiltinType::Kind::UShort: + case clang::BuiltinType::Kind::UShort: return FundamentalTypeKind::UnsignedShort; - case BuiltinType::Kind::Int: + case clang::BuiltinType::Kind::Int: return FundamentalTypeKind::Int; - case BuiltinType::Kind::UInt: + case clang::BuiltinType::Kind::UInt: return FundamentalTypeKind::UnsignedInt; - case BuiltinType::Kind::Long: + case clang::BuiltinType::Kind::Long: return FundamentalTypeKind::Long; - case BuiltinType::Kind::ULong: + case clang::BuiltinType::Kind::ULong: return FundamentalTypeKind::UnsignedLong; - case BuiltinType::Kind::LongLong: + case clang::BuiltinType::Kind::LongLong: return FundamentalTypeKind::LongLong; - case BuiltinType::Kind::ULongLong: + case clang::BuiltinType::Kind::ULongLong: return FundamentalTypeKind::UnsignedLongLong; - case BuiltinType::Kind::Float: + case clang::BuiltinType::Kind::Float: return FundamentalTypeKind::Float; - case BuiltinType::Kind::Double: + case clang::BuiltinType::Kind::Double: return FundamentalTypeKind::Double; - case BuiltinType::Kind::LongDouble: + case clang::BuiltinType::Kind::LongDouble: return FundamentalTypeKind::LongDouble; default: return std::nullopt; @@ -573,7 +574,7 @@ template< typename DeclTy, typename Visitor, typename... Args> - requires std::derived_from + requires std::derived_from decltype(auto) visit( DeclTy* D, @@ -585,16 +586,16 @@ visit( { #define ABSTRACT_DECL(TYPE) #define DECL(DERIVED, BASE) \ - case Decl::DERIVED: \ - if constexpr(std::derived_from) \ + case clang::Decl::DERIVED: \ + if constexpr(std::derived_from) \ return std::forward(visitor)( \ static_cast*>(D), \ + clang::DERIVED##Decl>*>(D), \ std::forward(args)...); \ else \ MRDOCS_UNREACHABLE(); - #include +#include default: MRDOCS_UNREACHABLE(); @@ -603,23 +604,23 @@ visit( template consteval -Decl::Kind +clang::Decl::Kind DeclToKindImpl() = delete; #define ABSTRACT_DECL(TYPE) #define DECL(DERIVED, BASE) \ template<> \ consteval \ - Decl::Kind \ - DeclToKindImpl() { return Decl::DERIVED; } + clang::Decl::Kind \ + DeclToKindImpl() { return clang::Decl::DERIVED; } #include -/** Get the Decl::Kind for a type DeclTy derived from Decl. +/** Get the clang::Decl::Kind for a type DeclTy derived from Decl. */ template consteval -Decl::Kind +clang::Decl::Kind DeclToKind() { return DeclToKindImpl< @@ -634,7 +635,7 @@ template< typename TypeTy, typename Visitor, typename... Args> - requires std::derived_from + requires std::derived_from decltype(auto) visit( TypeTy* T, @@ -645,16 +646,16 @@ visit( switch(T->getTypeClass()) { #define TYPE(DERIVED, BASE) \ - case Type::DERIVED: \ - if constexpr(std::derived_from) \ + case clang::Type::DERIVED: \ + if constexpr(std::derived_from) \ return std::forward(visitor)( \ static_cast*>(T), \ + clang::DERIVED##Type>*>(T), \ std::forward(args)...); \ else \ MRDOCS_UNREACHABLE(); - #include +#include default: MRDOCS_UNREACHABLE(); @@ -663,21 +664,21 @@ visit( template consteval -Type::TypeClass +clang::Type::TypeClass TypeToKindImpl() = delete; #define ABSTRACT_TYPE(DERIVED, BASE) #define TYPE(DERIVED, BASE) \ template<> \ consteval \ - Type::TypeClass \ - TypeToKindImpl() { return Type::DERIVED; } + clang::Type::TypeClass \ + TypeToKindImpl() { return clang::Type::DERIVED; } #include template consteval -Type::TypeClass +clang::Type::TypeClass TypeToKind() { return TypeToKindImpl< @@ -692,7 +693,7 @@ template< typename TypeLocTy, typename Visitor, typename... Args> - requires std::derived_from + requires std::derived_from decltype(auto) visit( TypeLocTy* T, @@ -703,16 +704,16 @@ visit( { #define ABSTRACT_TYPELOC(DERIVED, BASE) #define TYPELOC(DERIVED, BASE) \ - case TypeLoc::DERIVED: \ - if constexpr(std::derived_from) \ + case clang::TypeLoc::DERIVED: \ + if constexpr(std::derived_from) \ return std::forward(visitor)( \ static_cast*>(T), \ + clang::DERIVED##TypeLoc>*>(T), \ std::forward(args)...); \ else \ MRDOCS_UNREACHABLE(); - #include +#include default: MRDOCS_UNREACHABLE(); @@ -721,23 +722,23 @@ visit( template consteval -TypeLoc::TypeLocClass +clang::TypeLoc::TypeLocClass TypeLocToKindImpl() = delete; #define ABSTRACT_TYPELOC(DERIVED, BASE) #define TYPELOC(DERIVED, BASE) \ template<> \ consteval \ - TypeLoc::TypeLocClass \ - TypeLocToKindImpl() { return TypeLoc::DERIVED; } + clang::TypeLoc::TypeLocClass \ + TypeLocToKindImpl() { return clang::TypeLoc::DERIVED; } #include -/** Get the TypeLoc::TypeLocClass for a type TypeLocTy derived from TypeLoc. +/** Get the clang::TypeLoc::TypeLocClass for a type TypeLocTy derived from TypeLoc. */ template consteval -TypeLoc::TypeLocClass +clang::TypeLoc::TypeLocClass TypeLocToKind() { return TypeLocToKindImpl< @@ -764,52 +765,52 @@ getInstantiatedFrom(DeclTy const* D) { return nullptr; } - Decl const* resultDecl = InstantiatedFromVisitor().Visit(D); + clang::Decl const* resultDecl = InstantiatedFromVisitor().Visit(D); return cast(resultDecl); } template requires - std::derived_from || - std::same_as> -FunctionDecl const* + std::derived_from || + std::same_as> +clang::FunctionDecl const* getInstantiatedFrom(DeclTy const* D) { - return dyn_cast_if_present( - getInstantiatedFrom(D)); + return dyn_cast_if_present( + getInstantiatedFrom(D)); } template requires - std::derived_from || - std::same_as> -CXXRecordDecl const* + std::derived_from || + std::same_as> +clang::CXXRecordDecl const* getInstantiatedFrom(DeclTy const* D) { - return dyn_cast_if_present( - getInstantiatedFrom(D)); + return dyn_cast_if_present( + getInstantiatedFrom(D)); } template requires - std::derived_from || - std::same_as> -VarDecl const* + std::derived_from || + std::same_as> +clang::VarDecl const* getInstantiatedFrom(DeclTy const* D) { - return dyn_cast_if_present( - getInstantiatedFrom(D)); + return dyn_cast_if_present( + getInstantiatedFrom(D)); } template requires - std::derived_from || - std::same_as> -TypedefNameDecl const* + std::derived_from || + std::same_as> +clang::TypedefNameDecl const* getInstantiatedFrom(DeclTy const* D) { - return dyn_cast_if_present( - getInstantiatedFrom(D)); + return dyn_cast_if_present( + getInstantiatedFrom(D)); } /** Get the access specifier for a `Decl` @@ -818,19 +819,19 @@ getInstantiatedFrom(DeclTy const* D) context and return the access specifier for the declaration. */ MRDOCS_DECL -AccessSpecifier -getAccess(Decl const* D); +clang::AccessSpecifier +getAccess(clang::Decl const* D); MRDOCS_DECL -QualType -getDeclaratorType(DeclaratorDecl const* DD); +clang::QualType +getDeclaratorType(clang::DeclaratorDecl const* DD); /** Get the NonTypeTemplateParm of an expression - This function will return the NonTypeTemplateParmDecl + This function will return the clang::NonTypeTemplateParmDecl corresponding to the expression `E` if it is a - NonTypeTemplateParmDecl. If the expression is not - a NonTypeTemplateParmDecl, the function will return + clang::NonTypeTemplateParmDecl. If the expression is not + a clang::NonTypeTemplateParmDecl, the function will return nullptr. For instance, given the expression `x` in the following @@ -841,38 +842,38 @@ getDeclaratorType(DeclaratorDecl const* DD); void f() {} @endcode - the function will return the NonTypeTemplateParmDecl + the function will return the clang::NonTypeTemplateParmDecl corresponding to `x`, which is the template parameter of the function `f`. */ MRDOCS_DECL -NonTypeTemplateParmDecl const* -getNTTPFromExpr(Expr const* E, unsigned Depth); +clang::NonTypeTemplateParmDecl const* +getNTTPFromExpr(clang::Expr const* E, unsigned Depth); // Get the parent declaration of a declaration MRDOCS_DECL -Decl const* -getParent(Decl const* D); +clang::Decl const* +getParent(clang::Decl const* D); MRDOCS_DECL void getQualifiedName( - NamedDecl const* ND, - raw_ostream &Out, - const PrintingPolicy &Policy); + clang::NamedDecl const* ND, + clang::raw_ostream &Out, + clang::PrintingPolicy const& Policy); // If D refers to an implicit instantiation of a template specialization, -// decay it to the Decl of the primary template. The template arguments -// will be extracted separately as part of the TypeInfo. -// For instance, a Decl to `S<0>` becomes a Decl to `S`, unless `S<0>` is +// decay it to the clang::Decl of the primary template. The template arguments +// will be extracted separately as part of the Type. +// For instance, a clang::Decl to `S<0>` becomes a clang::Decl to `S`, unless `S<0>` is // an explicit specialization of the primary template. // This function also applies recursively to the parent of D so that // the primary template is resolved for nested classes. -// For instance, a Decl to `A<0>::S` becomes a Decl to `A::S`, unless +// For instance, a clang::Decl to `A<0>::S` becomes a clang::Decl to `A::S`, unless // `A<0>` is an explicit specialization of the primary template. MRDOCS_DECL -Decl const* -decayToPrimaryTemplate(Decl const* D); +clang::Decl const* +decayToPrimaryTemplate(clang::Decl const* D); // Iterate the Decl and check if this is a template specialization // also considering the parent declarations. For instance, @@ -881,17 +882,17 @@ decayToPrimaryTemplate(Decl const* D); // template specializations. MRDOCS_DECL bool -isAllImplicitSpecialization(Decl const* D); +isAllImplicitSpecialization(clang::Decl const* D); // Check if any component of D is an implicit specialization MRDOCS_DECL bool -isAnyImplicitSpecialization(Decl const* D); +isAnyImplicitSpecialization(clang::Decl const* D); // Check if at least one component of D is explicit inline bool -isAnyExplicitSpecialization(Decl const* D) +isAnyExplicitSpecialization(clang::Decl const* D) { return !isAllImplicitSpecialization(D); } @@ -899,30 +900,30 @@ isAnyExplicitSpecialization(Decl const* D) // Check if all components are explicit inline bool -isAllExplicitSpecialization(Decl const* D) +isAllExplicitSpecialization(clang::Decl const* D) { return !isAnyImplicitSpecialization(D); } MRDOCS_DECL bool -isVirtualMember(Decl const* D); +isVirtualMember(clang::Decl const* D); MRDOCS_DECL bool -isAnonymousNamespace(Decl const* D); +isAnonymousNamespace(clang::Decl const* D); MRDOCS_DECL bool -isStaticFileLevelMember(Decl const *D); +isStaticFileLevelMember(clang::Decl const *D); MRDOCS_DECL bool -isDocumented(Decl const *D); +isDocumented(clang::Decl const *D); MRDOCS_DECL -RawComment const* -getDocumentation(Decl const *D); +clang::RawComment const* +getDocumentation(clang::Decl const *D); template bool @@ -947,25 +948,25 @@ namespace detail { // getASTVisitor().context_.getPrintingPolicy()); // is a valid expression template - concept HasPrintQualifiedName = requires(T const& D, llvm::raw_svector_ostream& OS, PrintingPolicy PP) + concept HasPrintQualifiedName = requires(T const& D, llvm::raw_svector_ostream& OS, clang::PrintingPolicy PP) { D.printQualifiedName(OS, PP); }; template - concept HasPrint = requires(T const& D, llvm::raw_svector_ostream& OS, PrintingPolicy PP) + concept HasPrint = requires(T const& D, llvm::raw_svector_ostream& OS, clang::PrintingPolicy PP) { D.print(OS, PP); }; template - concept HasPrintWithPolicyFirst = requires(T const& D, llvm::raw_svector_ostream& OS, PrintingPolicy PP) + concept HasPrintWithPolicyFirst = requires(T const& D, llvm::raw_svector_ostream& OS, clang::PrintingPolicy PP) { D.print(PP, OS, true); }; template - concept HasDump = requires(T const& D, llvm::raw_svector_ostream& OS, ASTContext const& C) + concept HasDump = requires(T const& D, llvm::raw_svector_ostream& OS, clang::ASTContext const& C) { D.dump(OS, C); }; @@ -985,20 +986,20 @@ namespace detail { template requires (!std::is_pointer_v) void - printTraceName(T const& D, ASTContext const& C, std::string& symbol_name); + printTraceName(T const& D, clang::ASTContext const& C, std::string& symbol_name); template void - printTraceName(T const* D, ASTContext const& C, std::string& symbol_name) + printTraceName(T const* D, clang::ASTContext const& C, std::string& symbol_name) { if (!D) { return; } llvm::raw_string_ostream os(symbol_name); - if constexpr (std::derived_from) + if constexpr (std::derived_from) { - if (NamedDecl const* ND = dyn_cast(D)) + if (clang::NamedDecl const* ND = dyn_cast(D)) { getQualifiedName(ND, os, C.getPrintingPolicy()); } @@ -1021,7 +1022,7 @@ namespace detail { } else if constexpr (ConvertibleToUnqualifiedQualType) { - QualType const QT(D, 0); + clang::QualType const QT(D, 0); QT.print(os, C.getPrintingPolicy()); } else if constexpr (HasDump) @@ -1052,14 +1053,14 @@ namespace detail { template requires (!std::is_pointer_v) void - printTraceName(T const& D, ASTContext const& C, std::string& symbol_name) + printTraceName(T const& D, clang::ASTContext const& C, std::string& symbol_name) { printTraceName(&D, C, symbol_name); } template void - printTraceName(std::optional const& D, ASTContext const& C, std::string& symbol_name) + printTraceName(Optional const& D, clang::ASTContext const& C, std::string& symbol_name) { if (D) { @@ -1081,6 +1082,6 @@ namespace detail { report::trace("{}", MRDOCS_SYMBOL_TRACE_UNIQUE_NAME) #endif -} // clang::mrdocs +} // mrdocs #endif diff --git a/src/lib/AST/ExtractDocComment.cpp b/src/lib/AST/ExtractDocComment.cpp new file mode 100644 index 000000000..93b498096 --- /dev/null +++ b/src/lib/AST/ExtractDocComment.cpp @@ -0,0 +1,1188 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) +// Copyright (c) 2025 Gennaro Prota (gennaro.prota@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#include "ExtractDocComment.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 5054) +# pragma warning(pop) +#endif + +#ifdef NDEBUG +# define MRDOCS_COMMENT_TRACE(D, Ctx) +#else +# define MRDOCS_COMMENT_TRACE_MERGE_(a, b) a##b +# define MRDOCS_COMMENT_TRACE_LABEL_(a) \ + MRDOCS_COMMENT_TRACE_MERGE_(comment_content_, a) +# define MRDOCS_COMMENT_TRACE_UNIQUE_NAME \ + MRDOCS_COMMENT_TRACE_LABEL_(__LINE__) +namespace detail { +template +static void +dumpCommentContent( + T const* C, + clang::ASTContext const& Ctx, + llvm::SmallString<1024>& contents) +{ + if (!C) + { + return; + } + + if constexpr (std::derived_from) + { + auto const* CC = static_cast(C); + clang::SourceRange const R = CC->getSourceRange(); + clang::SourceManager const& SM = Ctx.getSourceManager(); + contents = clang::Lexer::getSourceText( + clang::CharSourceRange::getTokenRange(R), + SM, + Ctx.getLangOpts()); + } +} +template +requires(!std::is_pointer_v) +static void +dumpCommentContent( + T const& C, + clang::ASTContext const& Ctx, + llvm::SmallString<1024>& contents) +{ + dumpCommentContent(&C, Ctx, contents); +} +} // namespace detail + +# define MRDOCS_COMMENT_TRACE(D, Ctx) \ + llvm::SmallString<1024> MRDOCS_COMMENT_TRACE_UNIQUE_NAME; \ + ::detail::dumpCommentContent(D, Ctx, MRDOCS_COMMENT_TRACE_UNIQUE_NAME); \ + report::trace( \ + "{}", \ + std::string_view(MRDOCS_COMMENT_TRACE_UNIQUE_NAME.str())) +#endif + +namespace mrdocs { +namespace { + +// -------- Small helpers + +static std::string +ensureUTF8(std::string s) +{ + if (!llvm::json::isUTF8(s)) + { + s = llvm::json::fixUTF8(s); + } + return s; +} + +static doc::InlineKind +convertStyle(clang::comments::InlineCommandRenderKind k) +{ + using K = clang::comments::InlineCommandRenderKind; + switch (k) + { + case K::Monospaced: + return doc::InlineKind::Code; + case K::Bold: + return doc::InlineKind::Strong; + case K::Emphasized: + return doc::InlineKind::Emph; + case K::Normal: + case K::Anchor: + default: + return doc::InlineKind::Text; + } +} + +static doc::ParamDirection +convertDirection(clang::comments::ParamCommandPassDirection d) +{ + using D = clang::comments::ParamCommandPassDirection; + switch (d) + { + case D::In: + return doc::ParamDirection::in; + case D::Out: + return doc::ParamDirection::out; + case D::InOut: + return doc::ParamDirection::inout; + } + report::error( + "error: unsupported ParamCommandPassDirection <{}>", + static_cast(d)); + MRDOCS_UNREACHABLE(); +} + +static doc::Parts +convertCopydoc(unsigned id) +{ + using T = clang::comments::CommandTraits; + switch (id) + { + case T::KCI_copydoc: + return doc::Parts::all; + case T::KCI_copybrief: + return doc::Parts::brief; + case T::KCI_copydetails: + return doc::Parts::description; + default: + report::error("error: unsupported CommandTrait id <{}>", id); + MRDOCS_UNREACHABLE(); + } +} + +// Cursor: immutable snapshot of children with index-based look-ahead/consume. +struct Cursor { + llvm::SmallVector children; + std::size_t i{ 0 }; + + Cursor() = default; + explicit Cursor(clang::comments::Comment const* parent) + { + children.assign(parent->child_begin(), parent->child_end()); + } + + bool + done() const + { + return i >= children.size(); + } + clang::comments::Comment const* + cur() const + { + return done() ? nullptr : children[i]; + } + clang::comments::Comment const* + peek(std::size_t k = 1) const + { + std::size_t j = i + k; + return (j < children.size()) ? children[j] : nullptr; + } + void + advance() + { + if (!done()) + { + ++i; + } + } + // consume n *intermediate* siblings after current (not including current) + void + consume_intermediate(std::size_t n) + { + // We will call this only after we've already processed current; + // It should skip *n* immediately following items. + i += n; + if (i > children.size()) + { + i = children.size(); + } + } +}; + +//------------------------------------------------ + +class DocCommentVisitor + : public clang::comments::ConstCommentVisitor { + Config const& config_; + clang::ASTContext const& ctx_; + clang::SourceManager const& sm_; + clang::comments::FullComment const* FC_; + Diagnostics& diags_; + + DocComment jd_; + doc::InlineContainer* curInlines_{ nullptr }; + bool newline_blocks_merge_{ false }; + + // --- inline assembly + + template InlineTy, class... Args> + void + emplaceInline(bool end_with_nl, Args&&... args) + { + MRDOCS_ASSERT(curInlines_ != nullptr); + auto& vec = curInlines_->children; + + auto mergeable = [](doc::InlineKind k) { + return is_one_of( + k, + { doc::InlineKind::Text, + doc::InlineKind::Emph, + doc::InlineKind::Strong, + doc::InlineKind::Code }); + }; + + InlineTy elem(std::forward(args)...); + + if (!newline_blocks_merge_ && !vec.empty()) + { + doc::Inline& last = *vec.back(); + if (last.Kind == elem.Kind && mergeable(elem.Kind)) + { + if constexpr (std::is_same_v) + { + last.asText().literal.append(elem.asText().literal); + newline_blocks_merge_ = end_with_nl; + return; + } + // For Emph/Strong/Code we preserve node boundaries (safer). + } + } + + vec.emplace_back(std::in_place_type, std::move(elem)); + newline_blocks_merge_ = end_with_nl; + } + + struct BlockScope { + DocCommentVisitor& v_; + doc::InlineContainer* prev_; + bool prev_merge_; + BlockScope(DocCommentVisitor& v, doc::InlineContainer* dst) + : v_(v) + , prev_(v.curInlines_) + , prev_merge_(v.newline_blocks_merge_) + { + v.curInlines_ = dst; + v.newline_blocks_merge_ = false; + } + ~BlockScope() + { + v_.curInlines_ = prev_; + v_.newline_blocks_merge_ = prev_merge_; + } + }; + + BlockScope + enterScope(doc::InlineContainer& dst) + { + return BlockScope(*this, &dst); + } + + // --- diagnostics helpers + + bool + goodArgCount(std::size_t n, clang::comments::InlineCommandComment const& C) + { + if (C.getNumArgs() != n) + { + auto loc = sm_.getPresumedLoc(C.getBeginLoc()); + diags_.error( + std::format( + "Expected {} but got {} args\nFile: {}, line {}, col {}\n", + n, + C.getNumArgs(), + files::makePosixStyle(loc.getFilename()), + loc.getLine(), + loc.getColumn())); + return false; + } + return true; + } + + // --- “peek next text” & reference fixer using Cursor + + Optional + peekNextText(Cursor const& cur) const + { + using namespace clang::comments; + auto* n = cur.peek(); + if (!n || n->getCommentKind() != CommentKind::TextComment) + { + return std::nullopt; + } + return static_cast(n)->getText(); + } + + std::string + fixReference(std::string& ref, Cursor& cur) + { + ParsedRef v; + for (;;) + { + char const* first = ref.data(); + char const* last = first + ref.size(); + auto pres = parse(first, last, v); + if (!pres) + { + if (auto nextText = peekNextText(cur)) + { + ref += *nextText; + cur.advance(); // consume that sibling + continue; + } + return {}; + } + + if (pres.ptr != last) + { + std::string leftover(pres.ptr, last - pres.ptr); + if (!isWhitespace(leftover)) + { + ref.erase(pres.ptr - first); + return leftover; + } + } + + bool const mightHaveMoreQualifiers + = v.HasFunctionParameters && v.ExceptionSpec.Implicit + && v.ExceptionSpec.Operand.empty(); + + if (mightHaveMoreQualifiers) + { + llvm::SmallVector quals; + if (v.Kind == ReferenceKind::None) + { + if (!v.IsConst) + { + quals.push_back("const"); + } + if (!v.IsVolatile) + { + quals.push_back("volatile"); + } + quals.push_back("&"); + } + else if (v.Kind == ReferenceKind::LValue && ref.ends_with('&')) + { + quals.push_back("&"); + } + quals.push_back("noexcept"); + + if (auto nextText = peekNextText(cur)) + { + auto trimmed = ltrim(*nextText); + if (trimmed.empty() + || std::ranges::any_of(quals, [&](std::string_view s) { + return trimmed.starts_with(s); + })) + { + ref += *nextText; + cur.advance(); + continue; + } + } + else + { + std::string leftover(pres.ptr, last - pres.ptr); + ref.erase(pres.ptr - first); + return leftover; + } + } + + if (!v.HasFunctionParameters) + { + if (auto nextText = peekNextText(cur)) + { + std::string_view trimmed = ltrim(*nextText); + static constexpr std::string_view idChars + = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789" + "_:"; + if (trimmed.empty() || contains(idChars, trimmed.front())) + { + ref += *nextText; + cur.advance(); + continue; + } + } + else + { + std::string leftover(pres.ptr, last - pres.ptr); + ref.erase(pres.ptr - first); + return leftover; + } + } + + return {}; + } + } + + // --- Visiting using Cursor (no member iterators) + + void + visitChildrenWithCursor(clang::comments::Comment const* C) + { + MRDOCS_COMMENT_TRACE(C, ctx_); + Cursor cur(C); + while (!cur.done()) + { + auto* n = cur.cur(); + MRDOCS_COMMENT_TRACE(n, ctx_); + visitNode(n, cur); // may advance/consume + cur.advance(); + } + } + + // “HTML text gatherer” equivalent with cursor + struct TagComponents { + std::string tag; + std::string text; + std::size_t n_intermediate{ 0 }; + }; + + Expected + parseHTMLStartSpan( + clang::comments::HTMLStartTagComment const* C, + Cursor const& cur) const + { + TagComponents res; + res.tag = C->getTagName().str(); + + static constexpr std::array noEndTag = { + "br", "img", "input", "hr", "meta", "link", + "base", "area", "col", "command", "embed", "keygen", + "param", "source", "track", "wbr" + }; + bool requiresEnd = std::ranges::find(noEndTag, res.tag) + == noEndTag.end(); + if (!requiresEnd) + { + return res; + } + + using namespace clang::comments; + // find matching end tag ahead + std::size_t j = 1; + for (; cur.i + j < cur.children.size(); ++j) + { + auto* c = cur.children[cur.i + j]; + if (c->getCommentKind() != CommentKind::HTMLEndTagComment) + { + continue; + } + auto* e = static_cast(c); + if (e->getTagName() == res.tag) + { + break; + } + } + if (cur.i + j >= cur.children.size()) + { + return Unexpected(formatError( + "warning: HTML <{}> tag not followed by end tag", + res.tag)); + } + + // ensure all in-between are TextComment + for (std::size_t k = 1; k < j; ++k) + { + if (cur.children[cur.i + k]->getCommentKind() + != clang::comments::CommentKind::TextComment) + { + return Unexpected(Error( + std::format( + "warning: HTML <{}> tag not followed by pure text", + res.tag))); + } + } + + res.n_intermediate = j - 1; + for (std::size_t k = 1; k < j; ++k) + { + auto* t = static_cast( + cur.children[cur.i + k]); + res.text += t->getText(); + } + return res; + } + + // Single-dispatch “node” entry that can use/modify the cursor + void + visitNode(clang::comments::Comment const* C, Cursor& cur) + { + using namespace clang::comments; + switch (C->getCommentKind()) + { + case CommentKind::TextComment: + visitText(static_cast(C)); + return; + + case CommentKind::HTMLStartTagComment: + visitHTMLStart(static_cast(C), cur); + return; + + case CommentKind::HTMLEndTagComment: + // noop; already handled when start is processed + return; + + case CommentKind::InlineCommandComment: + visitInlineCommand(static_cast(C), cur); + return; + + case CommentKind::ParagraphComment: + visitParagraph(static_cast(C)); + return; + + case CommentKind::BlockCommandComment: + visitBlockCommand(static_cast(C)); + return; + + case CommentKind::ParamCommandComment: + visitParam(static_cast(C)); + return; + + case CommentKind::TParamCommandComment: + visitTParam(static_cast(C)); + return; + + case CommentKind::VerbatimBlockComment: + visitVerbatimBlock(static_cast(C)); + return; + + case CommentKind::VerbatimBlockLineComment: + visitVerbatimBlockLine( + static_cast(C)); + return; + + case CommentKind::VerbatimLineComment: + // not used + return; + + default: + // generic: recurse + visitChildrenWithCursor(C); + return; + } + } + + // ---- Implementations + + void + visitText(clang::comments::TextComment const* C) + { + MRDOCS_COMMENT_TRACE(C, ctx_); + llvm::StringRef s = C->getText(); + if (curInlines_ && curInlines_->children.empty()) + { + s = s.ltrim(); + } + if (!s.empty()) + { + emplaceInline< + doc::TextInline>(C->hasTrailingNewline(), ensureUTF8(s.str())); + } + } + + void + visitHTMLStart(clang::comments::HTMLStartTagComment const* C, Cursor& cur) + { + MRDOCS_COMMENT_TRACE(C, ctx_); + MRDOCS_ASSERT(C->child_begin() == C->child_end()); + + auto loc = sm_.getPresumedLoc(C->getBeginLoc()); + auto filename = files::makePosixStyle(loc.getFilename()); + + auto getAttr = [&C](llvm::StringRef name) -> Expected { + for (unsigned i = 0, n = C->getNumAttrs(); i < n; ++i) + { + auto const& a = C->getAttr(i); + if (a.Name == name) + { + return std::string(a.Value); + } + } + return Unexpected(Error( + std::format( + "warning: HTML <{}> tag has no {} attribute", + C->getTagName().str(), + name.str()))); + }; + + auto compsExp = parseHTMLStartSpan(C, cur); + if (!compsExp) + { + report::error( + "{} at {} ({})", + compsExp.error().message(), + filename, + loc.getLine()); + return; + } + auto comps = *compsExp; + + if (comps.tag == "a") + { + auto r = getAttr("href"); + if (!r) + { + report::error(r.error().message()); + return; + } + emplaceInline( + C->hasTrailingNewline(), + ensureUTF8(std::move(comps.text)), + ensureUTF8(std::move(*r))); + } + else if (comps.tag == "br") + { + emplaceInline(true, ""); + } + else if (comps.tag == "em") + { + emplaceInline( + C->hasTrailingNewline(), + ensureUTF8(std::move(comps.text))); + } + else + { + report::warn( + std::format("warning: unsupported HTML tag <{}>", comps.tag), + filename, + loc.getLine()); + } + + // Skip the intermediate siblings consumed for text gathering + cur.consume_intermediate(comps.n_intermediate); + } + + void + visitInlineCommand( + clang::comments::InlineCommandComment const* C, + Cursor& cur) + { + MRDOCS_COMMENT_TRACE(C, ctx_); + auto const* cmd = ctx_.getCommentCommandTraits().getCommandInfo( + C->getCommandID()); + MRDOCS_ASSERT(cmd != nullptr); + + using T = clang::comments::CommandTraits; + switch (unsigned ID = cmd->getID()) + { + case T::KCI_n: + if (!goodArgCount(0, *C)) + { + return; + } + emplaceInline(true, "\n"); + return; + + case T::KCI_a: + case T::KCI_e: + case T::KCI_em: + { + MRDOCS_CHECK_OR(goodArgCount(1, *C)); + emplaceInline( + C->hasTrailingNewline(), + C->getArgText(0).str()); + return; + } + + case T::KCI_copybrief: + case T::KCI_copydetails: + case T::KCI_copydoc: + { + MRDOCS_CHECK_OR(goodArgCount(1, *C)); + std::string ref = C->getArgText(0).str(); + std::string leftover = fixReference(ref, cur); + bool hasExtra = !leftover.empty(); + + doc::Parts parts = convertCopydoc(ID); + bool copyBrief = parts == doc::Parts::brief + || parts == doc::Parts::all; + bool copyDetails = parts == doc::Parts::description + || parts == doc::Parts::all; + + if (copyBrief) + { + if (!jd_.brief) + { + jd_.brief.emplace(); + } + if (!contains(jd_.brief->copiedFrom, ref)) + { + jd_.brief->copiedFrom.emplace_back(ref); + } + } + if (copyDetails) + { + emplaceInline( + C->hasTrailingNewline() && !hasExtra, + ref); + } + if (hasExtra) + { + emplaceInline( + C->hasTrailingNewline(), + std::move(leftover)); + } + return; + } + + case T::KCI_ref: + { + MRDOCS_CHECK_OR(goodArgCount(1, *C)); + std::string ref = C->getArgText(0).str(); + std::string leftover = fixReference(ref, cur); + bool hasExtra = !leftover.empty(); + emplaceInline< + doc::ReferenceInline>(C->hasTrailingNewline() && !hasExtra, ref); + if (hasExtra) + { + emplaceInline( + C->hasTrailingNewline(), + std::move(leftover)); + } + return; + } + + case T::KCI_related: + case T::KCI_relates: + case T::KCI_relatedalso: + case T::KCI_relatesalso: + case T::KCI_memberof: + { + MRDOCS_CHECK_OR(goodArgCount(1, *C)); + std::string ref = C->getArgText(0).str(); + std::string leftover = fixReference(ref, cur); + bool hasExtra = !leftover.empty(); + jd_.relates.emplace_back(std::move(ref)); + if (hasExtra) + { + emplaceInline( + C->hasTrailingNewline(), + std::move(leftover)); + } + return; + } + + default: + break; + } + + // default rendering: concatenate all args and style accordingly + std::string s; + s.reserve([&] { + size_t n = 0; + for (unsigned i = 0; i < C->getNumArgs(); ++i) + { + n += C->getArgText(i).size(); + } + return n; + }()); + for (unsigned i = 0; i < C->getNumArgs(); ++i) + { + s.append(C->getArgText(i)); + } + + switch (convertStyle(C->getRenderKind())) + { + case doc::InlineKind::Emph: + emplaceInline< + doc::EmphInline>(C->hasTrailingNewline(), std::move(s)); + break; + case doc::InlineKind::Strong: + emplaceInline< + doc::StrongInline>(C->hasTrailingNewline(), std::move(s)); + break; + case doc::InlineKind::Code: + emplaceInline< + doc::CodeInline>(C->hasTrailingNewline(), std::move(s)); + break; + default: + emplaceInline< + doc::TextInline>(C->hasTrailingNewline(), std::move(s)); + break; + } + } + + void + visitParagraph(clang::comments::ParagraphComment const* C) + { + MRDOCS_COMMENT_TRACE(C, ctx_); + if (curInlines_) + { + visitChildrenWithCursor(C); + return; + } + doc::ParagraphBlock paragraph; + auto scope = enterScope(paragraph); + visitChildrenWithCursor(C); + if (!paragraph.empty()) + { + jd_.Document.emplace_back(std::move(paragraph)); + } + } + + void + visitBlockCommand(clang::comments::BlockCommandComment const* C) + { + MRDOCS_COMMENT_TRACE(C, ctx_); + auto const* cmd = ctx_.getCommentCommandTraits().getCommandInfo( + C->getCommandID()); + if (!cmd) + { + return; + } + + auto parseBlock = + [this, C](std::in_place_type_t) -> BlockTy + requires std::derived_from + && std::derived_from + { + BlockTy b; + auto& inlines = static_cast(b); + auto scope = enterScope(inlines); + + // Paragraph may be null for some block commands; guard it. + if (auto* P = C->getParagraph()) + { + visitChildrenWithCursor(P); + } + + if constexpr (requires { BlockTy::name; }) + { + if (C->getNumArgs() > 0) + { + b.name = C->getArgText(0).str(); + } + } + if (!inlines.children.empty()) + { + trim(inlines); + } + return b; + }; + + using T = clang::comments::CommandTraits; + switch (cmd->getID()) + { + case T::KCI_brief: + case T::KCI_short: + { + auto b = parseBlock(std::in_place_type); + jd_.brief.emplace(std::move(b)); + return; + } + case T::KCI_return: + case T::KCI_returns: + case T::KCI_result: + { + auto b = parseBlock(std::in_place_type); + jd_.returns.push_back(std::move(b)); + return; + } + case T::KCI_throw: + case T::KCI_throws: + case T::KCI_exception: + { + auto b = parseBlock(std::in_place_type); + if (C->getNumArgs() > 0) + { + b.exception.literal = C->getArgText(0).str(); + } + jd_.exceptions.push_back(std::move(b)); + return; + } + case T::KCI_note: + case T::KCI_warning: + { + auto p = parseBlock(std::in_place_type); + doc::AdmonitionKind k = (cmd->getID() == T::KCI_note) ? + doc::AdmonitionKind::note : + doc::AdmonitionKind::warning; + doc::AdmonitionBlock adm(k); + adm.blocks.emplace_back(std::move(p)); + jd_.Document.emplace_back(std::move(adm)); + return; + } + case T::KCI_par: + { + auto paragraph = parseBlock( + std::in_place_type); + if (C->getNumArgs() > 0) + { + jd_.Document.emplace_back( + doc::HeadingBlock(C->getArgText(0).str())); + } + + if (!paragraph.children.empty() + && paragraph.children.front()->isText()) + { + if (C->getNumArgs() == 0) + { + std::string text = std::move( + paragraph.children.front()->asText().literal); + if (auto s = trim(text); s.size() != text.size()) + { + text = s; + } + jd_.Document.emplace_back( + doc::HeadingBlock(std::move(text))); + paragraph.children.erase(paragraph.children.begin()); + } + if (!paragraph.children.empty()) + { + jd_.Document.emplace_back(std::move(paragraph)); + } + } + return; + } + case T::KCI_li: + { + if (jd_.Document.empty() || !jd_.Document.back()->isList()) + { + jd_.Document.emplace_back(doc::ListBlock{}); + } + auto& list = jd_.Document.back()->asList(); + auto& item = list.items.emplace_back(); + auto p = parseBlock(std::in_place_type); + item.blocks.emplace_back(std::move(p)); + return; + } + case T::KCI_details: + { + auto details = parseBlock(std::in_place_type); + jd_.Document.emplace_back(std::move(details)); + return; + } + case T::KCI_see: + { + auto see = parseBlock(std::in_place_type); + jd_.sees.push_back(std::move(see)); + return; + } + case T::KCI_pre: + { + auto pre = parseBlock(std::in_place_type); + jd_.preconditions.push_back(std::move(pre)); + return; + } + case T::KCI_post: + { + auto post = parseBlock(std::in_place_type); + jd_.postconditions.push_back(std::move(post)); + return; + } + + // Inline-only kinds here would be a logic error: + case T::KCI_a: + case T::KCI_e: + case T::KCI_em: + case T::KCI_n: + case T::KCI_copybrief: + case T::KCI_copydetails: + case T::KCI_copydoc: + report::error( + "error: inline command {} should be handled elsewhere", + cmd->Name); + MRDOCS_UNREACHABLE(); + + default: + // unsupported → ignore + return; + } + } + + void + visitParam(clang::comments::ParamCommandComment const* C) + { + MRDOCS_COMMENT_TRACE(C, ctx_); + doc::ParamBlock param; + if (C->hasParamName()) + { + param.name = ensureUTF8(C->getParamNameAsWritten().str()); + } + else + { + diags_.error("Missing parameter name in @param"); + param.name = "@anon"; + } + + if (C->isDirectionExplicit()) + { + param.direction = convertDirection(C->getDirection()); + } + + if (auto* P = C->getParagraph()) + { + auto scope = enterScope(param); + visitChildrenWithCursor(P); + } + + // warn on duplicates + auto it = std::ranges:: + find_if(jd_.Document, [&](Polymorphic const& b) { + if (!b->isParam()) + { + return false; + } + auto const* p = dynamic_cast( + b.operator->()); + MRDOCS_ASSERT(p != nullptr); + return p->name == param.name; + }); + if (it != jd_.Document.end()) + { + report::warn( + "{}: Duplicate @param for argument {}", + C->getBeginLoc().printToString(sm_), + param.name); + } + + jd_.params.push_back(std::move(param)); + } + + void + visitTParam(clang::comments::TParamCommandComment const* C) + { + MRDOCS_COMMENT_TRACE(C, ctx_); + doc::TParamBlock tparam; + if (C->hasParamName()) + { + tparam.name = ensureUTF8(C->getParamNameAsWritten().str()); + } + else + { + diags_.error("Missing parameter name in @tparam"); + tparam.name = "@anon"; + } + + if (auto* P = C->getParagraph()) + { + auto scope = enterScope(tparam); + visitChildrenWithCursor(P); + } + + auto it = std::ranges:: + find_if(jd_.Document, [&](Polymorphic const& b) { + if (!b->isTParam()) + { + return false; + } + auto const* tp = dynamic_cast( + b.operator->()); + MRDOCS_ASSERT(tp != nullptr); + return tp->name == tparam.name; + }); + if (it != jd_.Document.end()) + { + report::warn( + "{}: Duplicate @tparam for argument {}", + C->getBeginLoc().printToString(sm_), + tparam.name); + } + + jd_.tparams.push_back(std::move(tparam)); + } + + void + visitVerbatimBlock(clang::comments::VerbatimBlockComment const* C) + { + MRDOCS_COMMENT_TRACE(C, ctx_); + doc::CodeBlock code; + std::string payload; + unsigned n = C->getNumLines(); + for (unsigned i = 0; i < n; ++i) + { + llvm::StringRef line = C->getText(i); + payload.append(line.data(), line.size()); + if (i + 1 != n) + { + payload.push_back('\n'); + } + } + code.literal = std::move(payload); + jd_.Document.emplace_back(std::move(code)); + } + + void + visitVerbatimBlockLine(clang::comments::VerbatimBlockLineComment const* C) + { + MRDOCS_COMMENT_TRACE(C, ctx_); + emplaceInline(true, C->getText().str()); + } + +public: + DocCommentVisitor( + clang::comments::FullComment const* FC, + clang::Decl const* D, + Config const& config, + Diagnostics& diags) + : config_(config) + , ctx_(D->getASTContext()) + , sm_(ctx_.getSourceManager()) + , FC_(FC) + , diags_(diags) + {} + + DocComment + build() + { + MRDOCS_COMMENT_TRACE(FC_, ctx_); + visitChildrenWithCursor(FC_); + return std::move(jd_); + } +}; + +//------------------------------------------------ + +} // namespace + +void +initCustomCommentCommands(clang::ASTContext& context) +{ + (void) context; + // Reserved for future custom commands registration. +} + +void +populateDocComment( + Optional& jd, + clang::comments::FullComment const* FC, + clang::Decl const* D, + Config const& config, + Diagnostics& diags) +{ + MRDOCS_COMMENT_TRACE(FC, D->getASTContext()); + DocCommentVisitor visitor(FC, D, config, diags); + auto result = visitor.build(); + if (!result.empty()) + { + if (!jd) + { + jd = std::move(result); + } + else if (*jd != result) + { + jd->append(std::move(result)); + } + } +} + +} // namespace mrdocs diff --git a/src/lib/AST/ParseJavadoc.hpp b/src/lib/AST/ExtractDocComment.hpp similarity index 62% rename from src/lib/AST/ParseJavadoc.hpp rename to src/lib/AST/ExtractDocComment.hpp index 2af42be28..627efeb40 100644 --- a/src/lib/AST/ParseJavadoc.hpp +++ b/src/lib/AST/ExtractDocComment.hpp @@ -9,23 +9,22 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_LIB_AST_PARSEJAVADOC_HPP -#define MRDOCS_LIB_AST_PARSEJAVADOC_HPP +#ifndef MRDOCS_LIB_AST_EXTRACTDOCCOMMENT_HPP +#define MRDOCS_LIB_AST_EXTRACTDOCCOMMENT_HPP -#include "lib/Diagnostics.hpp" #include +#include #include -#include +#include namespace clang { - class Decl; class ASTContext; class RawComment; - namespace comments { - class FullComment; +class FullComment; } // comments +} // clang namespace mrdocs { @@ -36,28 +35,28 @@ namespace mrdocs { */ void initCustomCommentCommands( - ASTContext& ctx); + clang::ASTContext& ctx); -/** Parse doc comments from a declaration +/** Extract doc comments from a declaration - Parse the Javadoc from a declaration, populating the - Javadoc object with the parsed information. + Extract the DocComment from a declaration, populating the + DocComment object with the information parsed by clang. - @param jd The Javadoc object to populate + @param jd The DocComment object to populate @param FC The full comment to parse @param D The declaration to which the comment applies @param config The MrDocs configuration object @param diags The diagnostics object */ void -parseJavadoc( - std::optional& jd, - comments::FullComment const* FC, - Decl const* D, +populateDocComment( + Optional& jd, + clang::comments::FullComment const* FC, + clang::Decl const* D, Config const& config, Diagnostics& diags); } // mrdocs -} // clang -#endif + +#endif // MRDOCS_LIB_AST_EXTRACTDOCCOMMENT_HPP diff --git a/src/lib/AST/FrontendActionFactory.cpp b/src/lib/AST/FrontendActionFactory.cpp index 1956321fe..80943fd0d 100644 --- a/src/lib/AST/FrontendActionFactory.cpp +++ b/src/lib/AST/FrontendActionFactory.cpp @@ -12,13 +12,13 @@ // #include "FrontendActionFactory.hpp" -#include "lib/AST/ASTAction.hpp" +#include + -namespace clang { namespace mrdocs { -std::unique_ptr +std::unique_ptr ASTActionFactory:: create() { @@ -28,4 +28,4 @@ create() } } // mrdocs -} // clang + diff --git a/src/lib/AST/FrontendActionFactory.hpp b/src/lib/AST/FrontendActionFactory.hpp index 1111b5db9..05e983e96 100644 --- a/src/lib/AST/FrontendActionFactory.hpp +++ b/src/lib/AST/FrontendActionFactory.hpp @@ -14,16 +14,16 @@ #ifndef MRDOCS_LIB_AST_FRONTENDACTIONFACTORY_HPP #define MRDOCS_LIB_AST_FRONTENDACTIONFACTORY_HPP -#include "lib/ConfigImpl.hpp" -#include "lib/Support/ExecutionContext.hpp" -#include "lib/AST/MissingSymbolSink.hpp" +#include +#include +#include #include -namespace clang { + namespace mrdocs { class ASTActionFactory : - public tooling::FrontendActionFactory + public clang::tooling::FrontendActionFactory { ExecutionContext& ex_; ConfigImpl const& config_; @@ -39,11 +39,11 @@ class ASTActionFactory : { } - std::unique_ptr + std::unique_ptr create() override; }; } // mrdocs -} // clang -#endif + +#endif // MRDOCS_LIB_AST_FRONTENDACTIONFACTORY_HPP diff --git a/src/lib/AST/InstantiatedFromVisitor.hpp b/src/lib/AST/InstantiatedFromVisitor.hpp index f284ba637..2a7d9651a 100644 --- a/src/lib/AST/InstantiatedFromVisitor.hpp +++ b/src/lib/AST/InstantiatedFromVisitor.hpp @@ -16,7 +16,7 @@ #include -namespace clang::mrdocs { +namespace mrdocs { /** A visitor class for handling instantiations from templates. @@ -24,17 +24,17 @@ namespace clang::mrdocs { and retrieve the original declarations from which they were instantiated. */ class InstantiatedFromVisitor - : public ConstDeclVisitor + : public clang::ConstDeclVisitor { public: - Decl const* - VisitDecl(Decl const* D) + clang::Decl const* + VisitDecl(clang::Decl const* D) { return D; } - FunctionDecl const* - VisitFunctionTemplateDecl(FunctionTemplateDecl const* D) + clang::FunctionDecl const* + VisitFunctionTemplateDecl(clang::FunctionTemplateDecl const* D) { while(auto* MT = D->getInstantiatedFromMemberTemplate()) { @@ -47,8 +47,8 @@ class InstantiatedFromVisitor return D->getTemplatedDecl(); } - CXXRecordDecl const* - VisitClassTemplateDecl(ClassTemplateDecl const* D) + clang::CXXRecordDecl const* + VisitClassTemplateDecl(clang::ClassTemplateDecl const* D) { while (auto* MT = D->getInstantiatedFromMemberTemplate()) { @@ -61,8 +61,8 @@ class InstantiatedFromVisitor return D->getTemplatedDecl(); } - VarDecl const* - VisitVarTemplateDecl(VarTemplateDecl const* D) + clang::VarDecl const* + VisitVarTemplateDecl(clang::VarTemplateDecl const* D) { while(auto* MT = D->getInstantiatedFromMemberTemplate()) { @@ -75,8 +75,8 @@ class InstantiatedFromVisitor return D->getTemplatedDecl(); } - TypedefNameDecl const* - VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl const* D) + clang::TypedefNameDecl const* + VisitTypeAliasTemplateDecl(clang::TypeAliasTemplateDecl const* D) { if(auto* MT = D->getInstantiatedFromMemberTemplate()) { @@ -89,24 +89,24 @@ class InstantiatedFromVisitor return VisitTypedefNameDecl(D->getTemplatedDecl()); } - FunctionDecl const* - VisitFunctionDecl(FunctionDecl const* D) + clang::FunctionDecl const* + VisitFunctionDecl(clang::FunctionDecl const* D) { - if (FunctionDecl const* DD = nullptr; + if (clang::FunctionDecl const* DD = nullptr; D->isDefined(DD, false)) { - D = const_cast(DD); + D = const_cast(DD); } - if (MemberSpecializationInfo const* MSI = D->getMemberSpecializationInfo()) + if (clang::MemberSpecializationInfo const* MSI = D->getMemberSpecializationInfo()) { if (!MSI->isExplicitSpecialization()) { - D = cast(MSI->getInstantiatedFrom()); + D = cast(MSI->getInstantiatedFrom()); } } else if(D->getTemplateSpecializationKind() != - TSK_ExplicitSpecialization) + clang::TSK_ExplicitSpecialization) { D = D->getFirstDecl(); if (auto* FTD = D->getPrimaryTemplate()) @@ -117,8 +117,8 @@ class InstantiatedFromVisitor return D; } - CXXRecordDecl const* - VisitClassTemplatePartialSpecializationDecl(ClassTemplatePartialSpecializationDecl const* D) + clang::CXXRecordDecl const* + VisitClassTemplatePartialSpecializationDecl(clang::ClassTemplatePartialSpecializationDecl const* D) { while (auto* MT = D->getInstantiatedFromMember()) { @@ -131,20 +131,20 @@ class InstantiatedFromVisitor return VisitClassTemplateSpecializationDecl(D); } - CXXRecordDecl const* - VisitClassTemplateSpecializationDecl(ClassTemplateSpecializationDecl const* D) + clang::CXXRecordDecl const* + VisitClassTemplateSpecializationDecl(clang::ClassTemplateSpecializationDecl const* D) { if (!D->isExplicitSpecialization()) { auto const inst_from = D->getSpecializedTemplateOrPartial(); if(auto* CTPSD = inst_from.dyn_cast< - ClassTemplatePartialSpecializationDecl*>()) + clang::ClassTemplatePartialSpecializationDecl*>()) { MRDOCS_ASSERT(D != CTPSD); return VisitClassTemplatePartialSpecializationDecl(CTPSD); } // Explicit instantiation declaration/definition - else if(auto* CTD = inst_from.dyn_cast()) + else if(auto* CTD = inst_from.dyn_cast()) { return VisitClassTemplateDecl(CTD); } @@ -152,10 +152,10 @@ class InstantiatedFromVisitor return VisitCXXRecordDecl(D); } - CXXRecordDecl const* - VisitCXXRecordDecl(CXXRecordDecl const* D) + clang::CXXRecordDecl const* + VisitCXXRecordDecl(clang::CXXRecordDecl const* D) { - while (MemberSpecializationInfo const* MSI = + while (clang::MemberSpecializationInfo const* MSI = D->getMemberSpecializationInfo()) { // if this is a member of an explicit specialization, @@ -164,13 +164,13 @@ class InstantiatedFromVisitor { break; } - D = cast(MSI->getInstantiatedFrom()); + D = cast(MSI->getInstantiatedFrom()); } return D; } - VarDecl const* - VisitVarTemplatePartialSpecializationDecl(VarTemplatePartialSpecializationDecl const* D) + clang::VarDecl const* + VisitVarTemplatePartialSpecializationDecl(clang::VarTemplatePartialSpecializationDecl const* D) { while(auto* MT = D->getInstantiatedFromMember()) { @@ -183,21 +183,21 @@ class InstantiatedFromVisitor return VisitVarTemplateSpecializationDecl(D); } - VarDecl const* - VisitVarTemplateSpecializationDecl(VarTemplateSpecializationDecl const* D) + clang::VarDecl const* + VisitVarTemplateSpecializationDecl(clang::VarTemplateSpecializationDecl const* D) { if(! D->isExplicitSpecialization()) { auto const inst_from = D->getSpecializedTemplateOrPartial(); if(auto* VTPSD = inst_from.dyn_cast< - VarTemplatePartialSpecializationDecl*>()) + clang::VarTemplatePartialSpecializationDecl*>()) { MRDOCS_ASSERT(D != VTPSD); return VisitVarTemplatePartialSpecializationDecl(VTPSD); } // explicit instantiation declaration/definition else if(auto* VTD = inst_from.dyn_cast< - VarTemplateDecl*>()) + clang::VarTemplateDecl*>()) { return VisitVarTemplateDecl(VTD); } @@ -205,60 +205,60 @@ class InstantiatedFromVisitor return VisitVarDecl(D); } - VarDecl const* - VisitVarDecl(VarDecl const* D) + clang::VarDecl const* + VisitVarDecl(clang::VarDecl const* D) { - while(MemberSpecializationInfo* MSI = + while(clang::MemberSpecializationInfo* MSI = D->getMemberSpecializationInfo()) { if (MSI->isExplicitSpecialization()) { break; } - D = cast(MSI->getInstantiatedFrom()); + D = cast(MSI->getInstantiatedFrom()); } return D; } - EnumDecl const* - VisitEnumDecl(EnumDecl const* D) + clang::EnumDecl const* + VisitEnumDecl(clang::EnumDecl const* D) { - while(MemberSpecializationInfo* MSI = + while(clang::MemberSpecializationInfo* MSI = D->getMemberSpecializationInfo()) { if (MSI->isExplicitSpecialization()) { break; } - D = cast(MSI->getInstantiatedFrom()); + D = cast(MSI->getInstantiatedFrom()); } return D; } - TypedefNameDecl const* - VisitTypedefNameDecl(TypedefNameDecl const* D) + clang::TypedefNameDecl const* + VisitTypedefNameDecl(clang::TypedefNameDecl const* D) { - DeclContext const* Context = D->getNonTransparentDeclContext(); + clang::DeclContext const* Context = D->getNonTransparentDeclContext(); if (Context->isFileContext()) { return D; } - Decl const* ContextDecl = Decl::castFromDeclContext(Context); - Decl const* ContextInstatiationContextDecl = Visit(ContextDecl); - DeclContext const* ContextPattern = - Decl::castToDeclContext(ContextInstatiationContextDecl); + clang::Decl const* ContextDecl = clang::Decl::castFromDeclContext(Context); + clang::Decl const* ContextInstatiationContextDecl = Visit(ContextDecl); + clang::DeclContext const* ContextPattern = + clang::Decl::castToDeclContext(ContextInstatiationContextDecl); if (Context == ContextPattern) { return D; } for (auto lookup = ContextPattern->lookup(D->getDeclName()); - NamedDecl * ND : lookup) + clang::NamedDecl * ND : lookup) { - if (auto const* TND = dyn_cast(ND)) + if (auto const* TND = dyn_cast(ND)) { return TND; } - if (auto const* TATD = dyn_cast(ND)) + if (auto const* TATD = dyn_cast(ND)) { return TATD->getTemplatedDecl(); } @@ -267,6 +267,6 @@ class InstantiatedFromVisitor } }; -} // clang::mrdocs +} // mrdocs #endif diff --git a/src/lib/AST/MissingSymbolSink.hpp b/src/lib/AST/MissingSymbolSink.hpp index 1fb8319bc..87bbf04e5 100644 --- a/src/lib/AST/MissingSymbolSink.hpp +++ b/src/lib/AST/MissingSymbolSink.hpp @@ -8,24 +8,24 @@ // 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" +#ifndef MRDOCS_LIB_AST_MISSINGSYMBOLSINK_HPP +#define MRDOCS_LIB_AST_MISSINGSYMBOLSINK_HPP + #include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include #include -namespace clang { + namespace mrdocs { struct FrozenDiag { @@ -125,7 +125,7 @@ class MissingSymbolSink { col = P.getColumn(); } } - StringRef flag = Info.getFlagValue(); + llvm::StringRef flag = Info.getFlagValue(); FrozenDiag d; d.level = L; @@ -436,6 +436,6 @@ class CollectingDiagConsumer : public clang::DiagnosticConsumer { }; } // namespace mrdocs -} // namespace clang -#endif + +#endif // MRDOCS_LIB_AST_MISSINGSYMBOLSINK_HPP diff --git a/src/lib/AST/MrDocsFileSystem.hpp b/src/lib/AST/MrDocsFileSystem.hpp index 268114507..5aa2a5535 100644 --- a/src/lib/AST/MrDocsFileSystem.hpp +++ b/src/lib/AST/MrDocsFileSystem.hpp @@ -8,21 +8,21 @@ // 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" +#ifndef MRDOCS_LIB_AST_MRDOCSFILESYSTEM_HPP +#define MRDOCS_LIB_AST_MRDOCSFILESYSTEM_HPP + +#include +#include +#include +#include +#include +#include #include #include #include #include -namespace clang::mrdocs { +namespace 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 @@ -36,7 +36,7 @@ namespace clang::mrdocs { // 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; + llvm::IntrusiveRefCntPtr Real; std::shared_ptr Mem; ConfigImpl const &config_; clang::HeaderSearch* HeaderSearch = nullptr; @@ -73,7 +73,7 @@ class MrDocsFileSystem : public llvm::vfs::FileSystem { return matchesPrefixSetImpl(Path, shimParents); } - std::optional> + Optional> matchShim(llvm::StringRef Path) const { for (auto const &[K, P]: config_->missingIncludeShims) @@ -186,7 +186,7 @@ class MrDocsFileSystem : public llvm::vfs::FileSystem { public: MrDocsFileSystem( - IntrusiveRefCntPtr RealFS, + llvm::IntrusiveRefCntPtr RealFS, ConfigImpl const &Cfg) : Real(std::move(RealFS)) , Mem(std::make_shared()) @@ -337,6 +337,6 @@ createMrDocsFileSystem(ConfigImpl const &Cfg) return { new MrDocsFileSystem(Real, Cfg) }; } -} // namespace clang::mrdocs +} // namespace mrdocs -#endif +#endif // MRDOCS_LIB_AST_MRDOCSFILESYSTEM_HPP diff --git a/src/lib/AST/NameBuilder.cpp b/src/lib/AST/NameBuilder.cpp new file mode 100644 index 000000000..d1846cc08 --- /dev/null +++ b/src/lib/AST/NameBuilder.cpp @@ -0,0 +1,122 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#include + +namespace mrdocs { + +void +NameBuilder:: +buildDecltype( + clang::DecltypeType const*, + unsigned, + bool) +{ + // KRYSTIAN TODO: support decltype in names + // (e.g. within nested-name-specifiers). +} + +void +NameBuilder:: +buildTerminal( + clang::Type const* T, + unsigned, + bool) +{ + Result = Polymorphic(std::in_place_type); + (*Result)->Identifier = getASTVisitor().toString(T); +} + +void +NameBuilder:: +buildTerminal( + clang::NestedNameSpecifier NNS, + clang::IdentifierInfo const* II, + Optional> TArgs, + unsigned, + bool) +{ + if (TArgs) + { + Result = + Polymorphic(std::in_place_type); + if (II) + { + (*Result)->Identifier = II->getName(); + } + getASTVisitor().populate( + static_cast(**Result).TemplateArgs, + *TArgs); + } + else + { + Result = Polymorphic(std::in_place_type); + if (II) + { + (*Result)->Identifier = II->getName(); + } + } + if (NNS) + { + (*Result)->Prefix = getASTVisitor().toName(NNS); + } +} + +void +NameBuilder:: +buildTerminal( + clang::NestedNameSpecifier NNS, + clang::NamedDecl const* D, + Optional> const& TArgs, + unsigned, + bool) +{ + // Look for the Info type. If this is a template specialization, + // we look for the Info of the specialized record. + clang::Decl const* ID = decayToPrimaryTemplate(D); + + + auto populateName = [&](Name& Name, clang::NamedDecl const* DU) + { + if(clang::IdentifierInfo const* II = DU->getIdentifier()) + { + Name.Identifier = II->getName(); + } + if (Symbol const* I = getASTVisitor().findOrTraverse(const_cast(ID))) + { + Name.id = I->id; + } + if (NNS) + { + Name.Prefix = getASTVisitor().toName(NNS); + } + }; + + if (!TArgs) + { + Result = Polymorphic(std::in_place_type); + populateName(**Result, D); + } + else + { + Result = Polymorphic( + std::in_place_type); + populateName(**Result, D); + MRDOCS_ASSERT(Result); + getASTVisitor().populate( + (*Result)->asSpecialization().TemplateArgs, + *TArgs); + } +} + +} // mrdocs diff --git a/src/lib/AST/NameInfoBuilder.hpp b/src/lib/AST/NameBuilder.hpp similarity index 53% rename from src/lib/AST/NameInfoBuilder.hpp rename to src/lib/AST/NameBuilder.hpp index 515a093bd..17d094e99 100644 --- a/src/lib/AST/NameInfoBuilder.hpp +++ b/src/lib/AST/NameBuilder.hpp @@ -11,58 +11,68 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_LIB_AST_NAMEINFOBUILDER_HPP -#define MRDOCS_LIB_AST_NAMEINFOBUILDER_HPP +#ifndef MRDOCS_LIB_AST_NAMEBUILDER_HPP +#define MRDOCS_LIB_AST_NAMEBUILDER_HPP -#include #include +#include #include -namespace clang::mrdocs { +namespace mrdocs { -class NameInfoBuilder - : public TerminalTypeVisitor +class NameBuilder + : public TerminalTypeVisitor { - Polymorphic Result; + Optional> Result; public: using TerminalTypeVisitor::TerminalTypeVisitor; - Polymorphic + Polymorphic result() { - return std::move(Result); + if (Result.has_value()) + { + return std::move(*Result); + } + return Polymorphic(std::in_place_type); + } + + constexpr bool + hasResult() const noexcept + { + return Result.has_value(); } void buildDecltype( - DecltypeType const* T, + clang::DecltypeType const* T, unsigned quals, bool pack); void buildTerminal( - Type const* T, + clang::Type const* T, unsigned quals, bool pack); void buildTerminal( - NestedNameSpecifier NNS, - IdentifierInfo const* II, - std::optional> TArgs, + clang::NestedNameSpecifier NNS, + clang::IdentifierInfo const* II, + Optional> TArgs, unsigned quals, bool pack); void buildTerminal( - NestedNameSpecifier NNS, - NamedDecl const* D, - std::optional> const& TArgs, + clang::NestedNameSpecifier NNS, + clang::NamedDecl const* D, + Optional> const& TArgs, unsigned quals, bool pack); }; -} // clang::mrdocs +} // mrdocs -#endif +#endif // MRDOCS_LIB_AST_NAMEBUILDER_HPP diff --git a/src/lib/AST/NameInfoBuilder.cpp b/src/lib/AST/NameInfoBuilder.cpp deleted file mode 100644 index 70bb3f60a..000000000 --- a/src/lib/AST/NameInfoBuilder.cpp +++ /dev/null @@ -1,119 +0,0 @@ -// -// This is a derivative work. originally part of the LLVM Project. -// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) -// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) -// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) -// -// Official repository: https://github.com/cppalliance/mrdocs -// - -#include "lib/AST/NameInfoBuilder.hpp" - -namespace clang::mrdocs { - -void -NameInfoBuilder:: -buildDecltype( - DecltypeType const*, - unsigned, - bool) -{ - // KRYSTIAN TODO: support decltype in names - // (e.g. within nested-name-specifiers). -} - -void -NameInfoBuilder:: -buildTerminal( - Type const* T, - unsigned, - bool) -{ - Result = Polymorphic(); - Result->Name = getASTVisitor().toString(T); -} - -void -NameInfoBuilder:: -buildTerminal( - NestedNameSpecifier NNS, - IdentifierInfo const* II, - std::optional> TArgs, - unsigned, - bool) -{ - if (TArgs) - { - Result = - Polymorphic(std::in_place_type); - if (II) - { - Result->Name = II->getName(); - } - getASTVisitor().populate( - static_cast(*Result).TemplateArgs, *TArgs); - } - else - { - Result = Polymorphic(); - if (II) - { - Result->Name = II->getName(); - } - } - if (NNS) - { - Result->Prefix = getASTVisitor().toNameInfo(NNS); - } -} - -void -NameInfoBuilder:: -buildTerminal( - NestedNameSpecifier NNS, - NamedDecl const* D, - std::optional> const& TArgs, - unsigned, - bool) -{ - // Look for the Info type. If this is a template specialization, - // we look for the Info of the specialized record. - Decl const* ID = decayToPrimaryTemplate(D); - - - auto populateNameInfo = [&](NameInfo& Name, NamedDecl const* DU) - { - if(IdentifierInfo const* II = DU->getIdentifier()) - { - Name.Name = II->getName(); - } - if (Info const* I = getASTVisitor().findOrTraverse(const_cast(ID))) - { - Name.id = I->id; - } - if (NNS) - { - Name.Prefix = getASTVisitor().toNameInfo(NNS); - } - }; - - if (!TArgs) - { - Result = Polymorphic(); - populateNameInfo(*Result, D); - } - else - { - Result = - Polymorphic(std::in_place_type); - populateNameInfo(*Result, D); - getASTVisitor().populate( - static_cast(*Result).TemplateArgs, *TArgs); - } -} - -} // clang::mrdocs diff --git a/src/lib/AST/ParseJavadoc.cpp b/src/lib/AST/ParseJavadoc.cpp deleted file mode 100644 index 70bde23cb..000000000 --- a/src/lib/AST/ParseJavadoc.cpp +++ /dev/null @@ -1,1820 +0,0 @@ -// -// This is a derivative work. originally part of the LLVM Project. -// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) -// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) -// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) -// Copyright (c) 2025 Gennaro Prota (gennaro.prota@gmail.com) -// -// Official repository: https://github.com/cppalliance/mrdocs -// - -#include "ParseJavadoc.hpp" -#include "lib/AST/ParseRef.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable: 5054) // C5054: operator '+': deprecated between enumerations of different types -#endif -#include -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif -#include -#include -#include -#include - -#ifdef NDEBUG -#define MRDOCS_COMMENT_TRACE(D, C) -#else - -# define MRDOCS_COMMENT_TRACE_MERGE_(a, b) a## b -# define MRDOCS_COMMENT_TRACE_LABEL_(a) MRDOCS_COMMENT_TRACE_MERGE_(comment_content_, a) -# define MRDOCS_COMMENT_TRACE_UNIQUE_NAME MRDOCS_COMMENT_TRACE_LABEL_(__LINE__) - -namespace detail { - template - void - dumpCommentContent(T const* C, clang::ASTContext const& Ctx, llvm::SmallString<1024>& contents) - { - if (!C) - { - return; - } - llvm::raw_svector_ostream os(contents); - if constexpr (std::derived_from) - { - auto const* CC = static_cast(C); - clang::SourceRange const R = CC->getSourceRange(); - clang::SourceManager const& SM = Ctx.getSourceManager(); - contents = clang::Lexer::getSourceText( - clang::CharSourceRange::getTokenRange(R), - SM, - Ctx.getLangOpts()); - } - } - - template - requires (!std::is_pointer_v) - void - dumpCommentContent(T const& C, clang::ASTContext const& Ctx, llvm::SmallString<1024>& contents) - { - dumpCommentContent(&C, Ctx, contents); - } -} // namespace detail - -#define MRDOCS_COMMENT_TRACE(D, C) \ - SmallString<1024> MRDOCS_COMMENT_TRACE_UNIQUE_NAME; \ - ::detail::dumpCommentContent(D, C, MRDOCS_COMMENT_TRACE_UNIQUE_NAME); \ - report::trace("{}", std::string_view(MRDOCS_COMMENT_TRACE_UNIQUE_NAME.str())) -#endif - - -/* AST Types - - Comment - abstract base for all comments - - FullComment : Comment - The entire extracted comment(s) attached to a declaration. - - InlineContentComment : Comment - contained within a block, abstract - - TextComment : InlineContentComment - plain text - - InlineCommandComment : InlineContentComment - command with args as inline content - - HTMLTagComment : InlineContentComment - Abstract class for opening and closing HTML tags, inline content - - HTMLStartTagComment : HTMLTagComment - opening HTML tag with attributes. - - HTMLEndTagComment : HTMLTagComment - closing HTML tag. - - BlockContentComment : Comment - Block content (contains inline content). abstract - - ParagraphComment : BlockContentComment - A single paragraph that contains inline content. - - BlockCommandComment : BlockContentComment - zero or more word-like arguments, then a paragraph - - ParamCommandComment : BlockCommandComment - describes a parameter - - TParamCommandComment : BlockCommandComment - describes a template parameter. - - VerbatimBlockComment : BlockCommandComment - A verbatim block command (e. g., preformatted code). Verbatim - block has an opening and a closing command and contains multiple - lines of text (VerbatimBlockLineComment nodes). - - VerbatimLineComment : BlockCommandComment - A verbatim line command. Verbatim line has an opening command, - a single line of text (up to the newline after the opening command) - and has no closing command. - - VerbatimBlockLineComment : Comment - A line of text contained in a verbatim block. - - BlockCommandComment - Always has one child of type ParagraphComment child? - -*/ - -namespace clang::mrdocs { - -namespace { - -using namespace comments; - -//------------------------------------------------ - -/** A visitor for extracting javadoc from comments. - - This class is a visitor for extracting javadoc from - comments in `clang::comments::FullComment` objects. - - */ -class JavadocVisitor - : public ConstCommentVisitor -{ - Config const& config_; - ASTContext const& ctx_; - SourceManager const& sm_; - FullComment const* FC_; - Javadoc jd_; - Diagnostics& diags_; - doc::Block* block_ = nullptr; - doc::Text* last_child_ = nullptr; - std::size_t htmlTagNesting_ = 0; - - // Used to visit children of a comment - Comment const* parent_ = nullptr; - Comment::child_iterator it_{}; - Comment::child_iterator end_{}; - - /** Ensure that a string is valid UTF-8. - - This function checks if the specified string is valid - UTF-8, and if not it fixes it and returns the result. - - */ - static std::string& ensureUTF8(std::string&&); - - /** Visit the children of a comment. - - This is a helper function that sets the current - iterator to the beginning of the children of the - specified comment, and then restores the original - iterator when the function returns. - - The children of the comment are visited in order - and appended to the current block. - - */ - void visitChildren(Comment const* C); - - - /** Temporarily set a block as the current block. - - This sets the current block of the visitor to the - specified block, and restores the previous block - when the scope is exited. - - The current block is where the visitor will - append new children when visiting comments. - - */ - class [[nodiscard]] BlockScope - { - JavadocVisitor& visitor_; - doc::Block* prev_; - - public: - BlockScope( - JavadocVisitor& visitor, - doc::Block* blk) - : visitor_(visitor) - , prev_(visitor.block_) - { - visitor_.block_ = blk; - visitor_.last_child_ = nullptr; - } - - ~BlockScope() - { - visitor_.block_ = prev_; - visitor_.last_child_ = nullptr; - } - }; - - /** Enter a block scope. - - This function creates a `BlockScope` object associated - to the visitor to temporarily set the current block to - the specified block. - - New children will be appended to the specified block - until the `BlockScope` object goes out of scope. - - @param blk The block to set as the current block - @returns A `BlockScope` object that will restore - the previous block when it goes out of scope - - */ - BlockScope enterScope(doc::Block& blk) - { - return {*this, &blk}; - } - - struct TagComponents - { - std::string tag; - std::string text; - std::size_t n_siblings{0}; - }; - -public: - /** Constructor - - Construct a JavadocVisitor to visit the specified - comment and extract javadoc from it. - - @param FC The full comment to visit - @param D The declaration to which the comment is attached - @param config The MrDocs configuration - @param diags The diagnostics sink - - */ - JavadocVisitor( - FullComment const*, Decl const*, - Config const&, Diagnostics&); - - /** Extract the javadoc from the comment. - - The comment specified in the constructor is visited - and the javadoc is extracted. The result is returned - by the function. - - */ - Javadoc build(); - - /** Visit any abstract comment. - - This is the base case for all comments. - It simply attempts to visit the children - of the comment. - - */ - void - visitComment(Comment const* C); - - /** Visit a plain text comment. - */ - void - visitTextComment(TextComment const* C); - - /** Visit an opening HTML tag with attributes. - */ - void - visitHTMLStartTagComment(HTMLStartTagComment const* C); - - /** Visit a closing HTML tag. - */ - void - visitHTMLEndTagComment(HTMLEndTagComment const* C); - - /** Visit a command with word-like arguments that is considered inline content. - - This is a command with word-like arguments that is considered - inline content. The number of arguments depends on the command - name. - - For instance, this could be something like "@ref foo", - where "foo" is the argument. - - */ - void - visitInlineCommandComment(InlineCommandComment const* C); - - /** Fix a reference string - - The clang parser considers all chars up to the first - whitespace as part of the reference. - - In some cases, this causes the reference to be incomplete, - especially when handling a function signature. In this - case, we have to extract the text from the next comments - to complete the reference. - - In other cases, the reference may contain characters that - are not valid in identifiers. For instance, when the - identifier is followed by punctuation. In this case, we - have to truncate the reference at the last valid identifier - character. - - @param ref The reference string to fix - @return Any leftover text that removed from the reference - */ - std::string - fixReference(std::string& ref); - - /** Visit a single paragraph that contains inline content. - */ - void - visitParagraphComment(ParagraphComment const* C); - - /** Visit a command that has zero or more word-like arguments - - The number of word-like arguments depends on command name and - a paragraph as an argument (e. g., @brief). - - */ - void - visitBlockCommandComment(BlockCommandComment const* C); - - /** Visit the doxygen @param command. - */ - void - visitParamCommandComment(ParamCommandComment const* C); - - - /** Visit the doxygen @tparam command - - The @tparam describes a template parameter. - */ - void - visitTParamCommandComment(TParamCommandComment const* C); - - /** Visit a verbatim block command (e. g., preformatted code). - - Verbatim block has an opening and a closing command and contains - multiple lines of text (VerbatimBlockLineComment nodes). - */ - void - visitVerbatimBlockComment(VerbatimBlockComment const* C); - - /** Visit a verbatim line command. - - Verbatim line has an opening command, a single line of text - (up to the newline after the opening command) and has no - closing command. - */ - void - visitVerbatimLineComment(VerbatimLineComment const* C); - - /** Visit a line of text contained in a verbatim block. - */ - void - visitVerbatimBlockLineComment(VerbatimBlockLineComment const* C); - - /** @name Helpers - * @{ - */ - - /** Check if the number of arguments is correct for a command. - - This function checks if the number of arguments - in the specified command is correct, and if not - it emits a diagnostic and returns false. - - @returns `true` if the number of arguments is correct - */ - bool - goodArgCount(std::size_t n, InlineCommandComment const& C); - - - Expected - parseHTMLTag(HTMLStartTagComment const* C); - - /** Append a text node to the current block. - - This function appends a text node to the current - block. - - If the previous child appended was of the same type, - the content is merged instead of creating a new node. - - If the text ends with a newline, it sets the last - child to null to indicate the next element cannot be - merged. - - @param end_with_nl `true` if the text ends with a newline - @param args The arguments to construct the text node - */ - template TextTy, typename... Args> - void - emplaceText(bool const end_with_nl, Args&&... args) - { - TextTy elem(std::forward(args)...); - bool can_merge = false; - - if (last_child_ && last_child_->Kind == elem.Kind) - { - if constexpr(TextTy::static_kind == doc::NodeKind::text) - can_merge = true; - - if constexpr(TextTy::static_kind == doc::NodeKind::styled) - can_merge = dynamic_cast( - last_child_)->style == elem.style; - } - - if (!can_merge) - { - last_child_ = &*block_->children.emplace_back( - std::in_place_type, std::move(elem)); - } - else - { - last_child_->string.append(elem.string); - } - - if (end_with_nl) - { - last_child_ = nullptr; - } - } - - /** @} */ -}; - -//------------------------------------------------ - -std::string &JavadocVisitor::ensureUTF8(std::string &&s) { - if (!llvm::json::isUTF8(s)) - s = llvm::json::fixUTF8(s); - return static_cast(s); -} - -/* Parse the inline content of a text - - This function takes a string from a comment - and parses it into a sequence of styled text - nodes. - - The string may contain inline commands that - change the style of the text: - - Regular text is stored as a doc::Text. - Styled text is stored as a doc::Styled. - - The styles can be one of: mono, bold, or italic. - - The tags "`", "*", and "_" are used to indicate - the start and end of styled text. They can be - escaped by prefixing them with a backslash. - - */ -std::vector> -parseStyled(StringRef s) -{ - std::vector> result; - std::string currentText; - auto currentStyle = doc::Style::none; - bool escapeNext = false; - - auto isStyleMarker = [](char c) { - return c == '`' || c == '*' || c == '_'; - }; - - auto flushCurrentText = [&]() { - if (!currentText.empty()) { - if (currentStyle == doc::Style::none) { - bool const lastIsSame = - !result.empty() && - result.back()->Kind == doc::NodeKind::text; - if (lastIsSame) - { - auto& lastText = static_cast(*result.back()); - lastText.string.append(currentText); - } - else - { - result.emplace_back(std::in_place_type, - std::move(currentText)); - } - } else { - bool const lastIsSame = - !result.empty() && - result.back()->Kind == doc::NodeKind::styled && - dynamic_cast(*result.back()).style == currentStyle; - if (lastIsSame) - { - auto& lastStyled = dynamic_cast(*result.back()); - lastStyled.string.append(currentText); - } - else - { - result.emplace_back(std::in_place_type, - std::move(currentText), currentStyle); - } - } - currentText.clear(); - } - }; - - auto isPunctuationOrSpace = [](unsigned char c) { - return std::isspace(c) || std::ispunct(c); - }; - - for (std::size_t i = 0; i < s.size(); ++i) { - char c = s[i]; - if (escapeNext) { - currentText.push_back(c); - escapeNext = false; - } else if (c == '\\') { - escapeNext = true; - } else if (isStyleMarker(c)) { - bool const atWordBoundary = - (currentStyle == doc::Style::none && ((i == 0) || isPunctuationOrSpace(s[i - 1]))) || - (currentStyle != doc::Style::none && ((i == s.size() - 1) || isPunctuationOrSpace(s[i + 1]))); - if (atWordBoundary) { - flushCurrentText(); - if (c == '`') { - currentStyle = (currentStyle == doc::Style::mono) ? doc::Style::none : doc::Style::mono; - } else if (c == '*') { - currentStyle = (currentStyle == doc::Style::bold) ? doc::Style::none : doc::Style::bold; - } else if (c == '_') { - currentStyle = (currentStyle == doc::Style::italic) ? doc::Style::none : doc::Style::italic; - } - } else { - currentText.push_back(c); - } - } else { - currentText.push_back(c); - } - } - - // Whatever style we started, we should end it because - // we reached the end of the string without a closing - // marker. - currentStyle = doc::Style::none; - flushCurrentText(); - - return result; -} - -void -JavadocVisitor:: -visitChildren( - Comment const* C) -{ - MRDOCS_COMMENT_TRACE(C, ctx_); - ScopeExitRestore s1(it_, C->child_begin()); - ScopeExitRestore s2(end_, C->child_end()); - ScopeExitRestore s3(parent_, C); - while(it_ != end_) - { - MRDOCS_COMMENT_TRACE(*it_, ctx_); - visit(*it_); - ++it_; - } - - if (!block_) - { - return; - } - - bool const isVerbatim = block_->Kind == doc::NodeKind::code; - if (isVerbatim) - { - return; - } - - // Merge consecutive plain text nodes in the current block - auto it = block_->children.begin(); - while(it != block_->children.end()) - { - if (auto& child = *it; - child->Kind == doc::NodeKind::text) - { - auto* text = dynamic_cast(child.operator->()); - MRDOCS_ASSERT(text); - auto next = std::next(it); - if(next != block_->children.end()) - { - if((*next)->Kind == doc::NodeKind::text) - { - auto* next_text = dynamic_cast(next->operator->()); - MRDOCS_ASSERT(next_text); - text->string.append(next_text->string); - it = block_->children.erase(next); - continue; - } - } - } - ++it; - } - - // Parse any Text nodes for styled text - for (auto cIt = block_->children.begin(); cIt != block_->children.end();) - { - MRDOCS_ASSERT(cIt->operator->()); - if ((*cIt)->Kind == doc::NodeKind::text) - { - auto* text = dynamic_cast(cIt->operator->()); - auto styledText = parseStyled(text->string); - std::size_t const offset = std::distance(block_->children.begin(), cIt); - std::size_t const n = styledText.size(); - block_->children.erase(cIt); - block_->children.insert( - block_->children.begin() + offset, - std::make_move_iterator(styledText.begin()), - std::make_move_iterator(styledText.end())); - cIt = block_->children.begin() + offset + n; - } - else - { - ++cIt; - } - } -} - -//------------------------------------------------ - -JavadocVisitor:: -JavadocVisitor( - FullComment const* FC, - Decl const* D, - Config const& config, - Diagnostics& diags) - : config_(config) - , ctx_(D->getASTContext()) - , sm_(ctx_.getSourceManager()) - , FC_(FC) - , diags_(diags) -{ -} - -Javadoc -JavadocVisitor:: -build() -{ - MRDOCS_COMMENT_TRACE(FC_, ctx_); - visit(FC_); - - // Merge ListItems into UnorderedList - auto& blocks = jd_.getBlocks(); - for (auto it = blocks.begin(); it != blocks.end(); ) { - if ((*it)->Kind == doc::NodeKind::list_item) { - doc::UnorderedList ul; - // Find last list item - auto const begin = it; - auto last = it; - while (last != blocks.end() && (*last)->Kind == doc::NodeKind::list_item) { - ++last; - } - // Move list items to ul.items - ul.items.reserve(std::distance(it, last)); - for (auto li_it = begin; li_it != last; ++li_it) { - auto *li = dynamic_cast(&**li_it); - MRDOCS_ASSERT(li != nullptr); - ul.items.emplace_back(std::move(*li)); - } - // Remove the list items and insert the ul - it = blocks.erase(begin, last); - it = blocks.emplace( - it, - std::in_place_type, - std::move(ul)); - } - ++it; - } - - return std::move(jd_); -} - -void -JavadocVisitor:: -visitComment( - Comment const* C) -{ - MRDOCS_COMMENT_TRACE(C, ctx_); - visitChildren(C); -} - -//------------------------------------------------ -// -// inline content -// -//------------------------------------------------ - -void -JavadocVisitor:: -visitTextComment( - TextComment const* C) -{ - MRDOCS_COMMENT_TRACE(C, ctx_); - llvm::StringRef s = C->getText(); - // If this is the first text comment in the - // paragraph then remove all the leading space. - // Otherwise, just remove the trailing space. - if (block_->children.empty()) - { - s = s.ltrim(); - } - - // Only insert non-empty text nodes - if(! s.empty()) - { - emplaceText( - C->hasTrailingNewline(), - ensureUTF8(s.str())); - } -} - -Expected -JavadocVisitor:: -parseHTMLTag(HTMLStartTagComment const* C) -{ - MRDOCS_COMMENT_TRACE(C, ctx_); - TagComponents res; - res.tag = C->getTagName().str(); - - static constexpr std::array noEndTagTags = - {"br", "img", "input", "hr", "meta", "link", "base", "area", "col", - "command", "embed", "keygen", "param", "source", "track", "wbr"}; - bool const requiresEndTag = std::ranges::find(noEndTagTags, res.tag) == noEndTagTags.end(); - if (!requiresEndTag) - { - return res; - } - - // Find Comment::HTMLEndTagCommentKind - auto const tagEndIt = - requiresEndTag ? std::ranges::find_if(it_ + 1, end_, [](Comment const* c) - { - return c->getCommentKind() == CommentKind::HTMLEndTagComment; - }) : it_; - if (tagEndIt == end_) - { - return Unexpected(formatError("warning: HTML <{}> tag not followed by end tag", res.tag)); - } - - // Check if end tag matches start tag - auto const& cEndTag = - *static_cast(*tagEndIt); - if(cEndTag.getTagName() != res.tag) - { - return Unexpected(Error(std::format( - "warning: HTML <{}> tag not followed by same end tag ( found)", - res.tag, cEndTag.getTagName().str()))); - } - - // Check if all the siblings are text nodes - bool const areAllText = std::all_of(it_ + 1, tagEndIt, [](Comment const* c) - { - return c->getCommentKind() == CommentKind::TextComment; - }); - if (!areAllText) - { - return Unexpected(Error( - std::format("warning: HTML <{}> tag not followed by text", res.tag))); - } - - // Extract text from all the siblings - res.n_siblings = std::distance(it_, tagEndIt); - for (auto it = std::next(it_); it != tagEndIt; ++it) - { - auto const& cText = - *static_cast(*it); - res.text += cText.getText(); - } - - return res; -} - -void -JavadocVisitor:: -visitHTMLStartTagComment( - HTMLStartTagComment const* C) -{ - MRDOCS_COMMENT_TRACE(C, ctx_); - MRDOCS_ASSERT(C->child_begin() == C->child_end()); - PresumedLoc const loc = sm_.getPresumedLoc(C->getBeginLoc()); - auto filename = files::makePosixStyle(loc.getFilename()); - - auto getAttribute = [&C](StringRef name) -> Expected - { - auto idxs = std::views::iota(unsigned(0), C->getNumAttrs()); - auto attr_it = std::ranges::find_if(idxs, [&C, name](std::size_t i) - { - return C->getAttr(i).Name == name; - }); - if (attr_it == idxs.end()) - { - return Unexpected( - Error(std::format("warning: HTML <{}> tag has no {} attribute", - C->getTagName().str(), name.str()))); - } - return C->getAttr(*attr_it).Value.str(); - }; - - last_child_ = nullptr; - auto tagComponentsExp = parseHTMLTag(C); - if (!tagComponentsExp) - { - Error e = tagComponentsExp.error(); - report::error("{} at {} ({})", e.message(), filename, loc.getLine()); - return; - } - auto tagComponents = *tagComponentsExp; - if(tagComponents.tag == "a") - { - auto r = getAttribute("href"); - if (!r) - { - report::error(r.error().message()); - return; - } - std::string href = *r; - emplaceText( - C->hasTrailingNewline(), - ensureUTF8(std::move(tagComponents.text)), - ensureUTF8(std::move(href))); - } - else if(tagComponents.tag == "br") - { - emplaceText(true,""); - } - else if(tagComponents.tag == "em") - { - emplaceText( - C->hasTrailingNewline(), - ensureUTF8(std::move(tagComponents.text)), - doc::Style::italic); - } - else - { - report::warn( - std::format("warning: unsupported HTML tag <{}>", tagComponents.tag), - filename, loc.getLine()); - } - // Skip the children we consumed in parseHTMLTag - it_ += tagComponents.n_siblings; - --htmlTagNesting_; -} - -void -JavadocVisitor:: -visitHTMLEndTagComment( - HTMLEndTagComment const* C) -{ - MRDOCS_COMMENT_TRACE(C, ctx_); - MRDOCS_ASSERT(C->child_begin() == C->child_end()); - --htmlTagNesting_; -} - -static -doc::Parts -convertCopydoc(unsigned id) -{ - switch(id) - { - case CommandTraits::KCI_copydoc: - return doc::Parts::all; - case CommandTraits::KCI_copybrief: - return doc::Parts::brief; - case CommandTraits::KCI_copydetails: - return doc::Parts::description; - default: - { - report::error("error: unsupported CommandTrait id <{}>", id); - MRDOCS_UNREACHABLE(); - } - } -} - -static -doc::Style -convertStyle(InlineCommandRenderKind kind) -{ - switch(kind) - { - case InlineCommandRenderKind::Monospaced: - return doc::Style::mono; - case InlineCommandRenderKind::Bold: - return doc::Style::bold; - case InlineCommandRenderKind::Emphasized: - return doc::Style::italic; - case InlineCommandRenderKind::Normal: - case InlineCommandRenderKind::Anchor: - return doc::Style::none; - default: - // unknown RenderKind - report::error("error: unsupported InlineCommandRenderKind <{}>", static_cast(kind)); - MRDOCS_UNREACHABLE(); - } -} - -static -doc::ParamDirection -convertDirection(ParamCommandPassDirection kind) -{ - switch(kind) - { - case ParamCommandPassDirection::In: - return doc::ParamDirection::in; - case ParamCommandPassDirection::Out: - return doc::ParamDirection::out; - case ParamCommandPassDirection::InOut: - return doc::ParamDirection::inout; - default: - report::error("error: unsupported ParamCommandPassDirection <{}>", static_cast(kind)); - MRDOCS_UNREACHABLE(); - } -} - -void -JavadocVisitor:: -visitInlineCommandComment( - InlineCommandComment const* C) -{ - MRDOCS_COMMENT_TRACE(C, ctx_); - auto const* cmd = ctx_ - .getCommentCommandTraits() - .getCommandInfo(C->getCommandID()); - - // VFALCO I'd like to know when this happens - MRDOCS_ASSERT(cmd != nullptr); - - switch(unsigned ID = cmd->getID()) - { - // Newline - case CommandTraits::KCI_n: - { - if(! goodArgCount(0, *C)) - return; - last_child_ = nullptr; - emplaceText(true, "\n"); - return; - } - // Emphasis - case CommandTraits::KCI_a: - case CommandTraits::KCI_e: - case CommandTraits::KCI_em: - { - MRDOCS_CHECK_OR(goodArgCount(1, *C)); - auto style = doc::Style::italic; - emplaceText( - C->hasTrailingNewline(), - C->getArgText(0).str(), - style); - return; - } - - // copy - case CommandTraits::KCI_copybrief: - case CommandTraits::KCI_copydetails: - case CommandTraits::KCI_copydoc: - { - MRDOCS_CHECK_OR(goodArgCount(1, *C)); - std::string ref = C->getArgText(0).str(); - std::string leftOver = fixReference(ref); - bool const hasExtra = !leftOver.empty(); - doc::Parts const parts = convertCopydoc(ID); - bool const copyBrief = parts == doc::Parts::brief || parts == doc::Parts::all; - bool const copyDetails = parts == doc::Parts::description || parts == doc::Parts::all; - MRDOCS_ASSERT(copyBrief || copyDetails); - if (copyBrief) - { - // The brief is metadata associated with the javadoc - if (!jd_.brief) - { - jd_.brief.emplace(); - } - if (!contains(jd_.brief->copiedFrom, ref)) - { - jd_.brief->copiedFrom.emplace_back(ref); - } - } - if (copyDetails) - { - // The details are in the main body of the javadoc - // and are replaced at the same position as the command - emplaceText( - C->hasTrailingNewline() && !hasExtra, - ref); - } - if (hasExtra) - { - emplaceText( - C->hasTrailingNewline(), - leftOver); - } - return; - } - case CommandTraits::KCI_ref: - { - MRDOCS_CHECK_OR(goodArgCount(1, *C)); - std::string ref = C->getArgText(0).str(); - std::string leftOver = fixReference(ref); - bool const hasExtra = !leftOver.empty(); - emplaceText( - C->hasTrailingNewline() && !hasExtra, - ref); - if (hasExtra) - { - emplaceText( - C->hasTrailingNewline(), - leftOver); - } - return; - } - // KRYSTIAN FIXME: these need to be made inline commands in clang - case CommandTraits::KCI_related: - case CommandTraits::KCI_relates: - // MrDocs doesn't document members inline, so there's no - // distinction between "related" and "relatedalso" - case CommandTraits::KCI_relatedalso: - case CommandTraits::KCI_relatesalso: - // Member of is a concept used only in C. MrDocs handles - // it as a non-member function is all cases. - case CommandTraits::KCI_memberof: - { - MRDOCS_CHECK_OR(goodArgCount(1, *C)); - std::string ref = C->getArgText(0).str(); - std::string leftOver = fixReference(ref); - bool const hasExtra = !leftOver.empty(); - jd_.relates.emplace_back(std::move(ref)); - if (hasExtra) - { - emplaceText( - C->hasTrailingNewline(), - leftOver); - } - return; - } - - default: - break; - } - - // It looks like the clang parser does not - // emit nested styles, so only one inline - // style command can be applied per args. - std::string s; - std::size_t n = 0; - for (unsigned i = 0; i < C->getNumArgs(); ++i) - { - n += C->getArgText(i).size(); - } - s.reserve(n); - for (unsigned i = 0; i < C->getNumArgs(); ++i) - { - s.append(C->getArgText(i)); - } - - if (doc::Style style = convertStyle(C->getRenderKind()); - style != doc::Style::none) - { - emplaceText(C->hasTrailingNewline(), std::move(s), style); - } else - { - emplaceText(C->hasTrailingNewline(), std::move(s)); - } -} - -std::string -JavadocVisitor:: -fixReference(std::string& ref) -{ - auto peekNextIt = [&]() -> std::optional - { - ++it_; - if (it_ == end_ || - (*it_)->getCommentKind() != CommentKind::TextComment) - { - --it_; - return std::nullopt; - } - Comment const* c = *it_; - std::string_view text = static_cast(c)->getText(); - --it_; - return text; - }; - - ParsedRef v; - while (true) - { - // Attempt to parse ref - char const* first = ref.data(); - char const* last = first + ref.size(); - auto const pres = parse(first, last, v); - if (!pres) - { - // The ref could not be parsed, add content from next - // text comment to the ref - auto const nextTextOpt = peekNextIt(); - if (!nextTextOpt) - { - return {}; - } - ref += *nextTextOpt; - ++it_; - continue; - } - - // The ref is not fully parsed - if (pres.ptr != last) - { - // The ref didn't consume all the text, so we need to - // remove the leftover text from the ref and return it - auto leftover = std::string(pres.ptr, last - pres.ptr); - // If leftover is only whitespace, the ref might need - // the next text comment to complete it. - if (!isWhitespace(leftover)) - { - ref.erase(pres.ptr - first); - return leftover; - } - } - - // The ref is fully parsed, but we might want to - // include the next text comment if it contains - // a valid continuation to the ref. - bool const mightHaveMoreQualifiers = - v.HasFunctionParameters && - v.ExceptionSpec.Implicit && - v.ExceptionSpec.Operand.empty(); - if (mightHaveMoreQualifiers) - { - llvm::SmallVector potentialQualifiers; - if (v.Kind == ReferenceKind::None) - { - // "&&" or "&" not defined yet - if (!v.IsConst) - { - potentialQualifiers.push_back("const"); - } - if (!v.IsVolatile) - { - potentialQualifiers.push_back("volatile"); - } - potentialQualifiers.push_back("&"); - } - else if ( - v.Kind == ReferenceKind::LValue && - ref.ends_with('&')) - { - // The second "&" might be in the next Text block - potentialQualifiers.push_back("&"); - } - potentialQualifiers.push_back("noexcept"); - auto const nextTextOpt = peekNextIt(); - if (!nextTextOpt) - { - auto leftover = std::string(pres.ptr, last - pres.ptr); - ref.erase(pres.ptr - first); - return leftover; - } - std::string_view const nextText = *nextTextOpt; - std::string_view const trimmed = ltrim(nextText); - if (trimmed.empty() || - std::ranges::any_of( - potentialQualifiers, - [&](std::string_view s) - { - return trimmed.starts_with(s); - })) - { - ref += nextText; - ++it_; - continue; - } - } - - // The ref might have more components - bool const mightHaveMoreComponents = - !v.HasFunctionParameters; - if (mightHaveMoreComponents) - { - auto const nextTextOpt = peekNextIt(); - if (!nextTextOpt) - { - auto leftover = std::string(pres.ptr, last - pres.ptr); - ref.erase(pres.ptr - first); - return leftover; - } - std::string_view const nextText = *nextTextOpt; - std::string_view const trimmed = ltrim(nextText); - static constexpr std::string_view idChars - = "abcdefghijklmnopqrstuvwxyz" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "0123456789" - "_:"; - if (trimmed.empty() || - contains(idChars, trimmed.front())) - { - ref += nextText; - ++it_; - continue; - } - } - - return {}; - } -} - -//------------------------------------------------ -// -// block Content -// -//------------------------------------------------ - -void -JavadocVisitor:: -visitParagraphComment( - ParagraphComment const* C) -{ - MRDOCS_COMMENT_TRACE(C, ctx_); - if(block_) - return visitChildren(C); - doc::Paragraph paragraph; - auto scope = enterScope(paragraph); - visitChildren(C); - // VFALCO Figure out why we get empty ParagraphComment - if(! paragraph.empty()) - jd_.emplace_back(std::move(paragraph)); -} - -void -JavadocVisitor:: -visitBlockCommandComment( - BlockCommandComment const* C) -{ - MRDOCS_COMMENT_TRACE(C, ctx_); - auto const* cmd = ctx_ - .getCommentCommandTraits() - .getCommandInfo(C->getCommandID()); - if(cmd == nullptr) - { - // ignore this command and the - // text that follows for now. - return; - } - - switch(cmd->getID()) - { - case CommandTraits::KCI_brief: - case CommandTraits::KCI_short: - { - doc::Brief brief; - auto scope = enterScope(brief); - // Scope scope(brief, block_); - visitChildren(C->getParagraph()); - // Here, we want empty briefs, because - // the @brief command was explicitly given. - if (!brief.children.empty()) - { - jd_.brief.emplace(std::move(brief)); - } - return; - } - - case CommandTraits::KCI_return: - case CommandTraits::KCI_returns: - case CommandTraits::KCI_result: - { - doc::Returns returns; - auto scope = enterScope(returns); - visitChildren(C->getParagraph()); - if (!returns.children.empty()) - { - returns.children.front()->string = ltrim(returns.children.front()->string); - returns.children.back()->string = rtrim(returns.children.back()->string); - jd_.returns.push_back(std::move(returns)); - } - return; - } - case CommandTraits::KCI_throw: - case CommandTraits::KCI_throws: - case CommandTraits::KCI_exception: - { - doc::Throws throws; - auto scope = enterScope(throws); - visitChildren(C->getParagraph()); - if (C->getNumArgs()) - { - throws.exception.string = C->getArgText(0); - } - else - { - throws.exception.string = "undefined"; - } - jd_.exceptions.push_back(std::move(throws)); - return; - } - case CommandTraits::KCI_note: - case CommandTraits::KCI_warning: - { - doc::Admonish admonish = cmd->getID() == CommandTraits::KCI_note - ? doc::Admonish::note - : doc::Admonish::warning; - doc::Admonition paragraph(admonish); - auto scope = enterScope(paragraph); - visitChildren(C->getParagraph()); - jd_.emplace_back(std::move(paragraph)); - return; - } - case CommandTraits::KCI_par: - { - // VFALCO This is legacy compatibility - // for Boost libraries using @par as a - // section heading. - doc::Paragraph paragraph; - auto scope = enterScope(paragraph); - visitChildren(C->getParagraph()); - if (C->getNumArgs() > 0) - { - jd_.emplace_back(doc::Heading(C->getArgText(0).str())); - } - if (!paragraph.children.empty()) - { - // the first TextComment is the heading text - if (C->getNumArgs() == 0) - { - std::string text(std::move( - paragraph.children.front()->string)); - - // VFALCO Unfortunately clang puts at least - // one space in front of the text, which seems - // incorrect. - if (auto const s = trim(text); - s.size() != text.size()) - { - text = s; - } - - doc::Heading heading(std::move(text)); - jd_.emplace_back(std::move(heading)); - - // remaining TextComment, if any - paragraph.children.erase(paragraph.children.begin()); - } - - if (!paragraph.children.empty()) - { - jd_.emplace_back(std::move(paragraph)); - } - } - return; - } - case CommandTraits::KCI_li: - { - doc::ListItem paragraph; - auto scope = enterScope(paragraph); - visitChildren(C->getParagraph()); - jd_.emplace_back(std::move(paragraph)); - return; - } - case CommandTraits::KCI_details: - { - doc::Paragraph paragraph; - auto scope = enterScope(paragraph); - visitChildren(C->getParagraph()); - jd_.emplace_back(std::move(paragraph)); - return; - } - case CommandTraits::KCI_see: - { - doc::See see; - auto scope = enterScope(see); - visitChildren(C->getParagraph()); - jd_.sees.push_back(std::move(see)); - return; - } - case CommandTraits::KCI_pre: - { - doc::Precondition pre; - auto scope = enterScope(pre); - visitChildren(C->getParagraph()); - jd_.preconditions.push_back(std::move(pre)); - return; - } - case CommandTraits::KCI_post: - { - doc::Postcondition post; - auto scope = enterScope(post); - visitChildren(C->getParagraph()); - jd_.postconditions.push_back(std::move(post)); - return; - } - - case CommandTraits::KCI_addindex: - case CommandTraits::KCI_addtogroup: - case CommandTraits::KCI_anchor: - case CommandTraits::KCI_arg: - case CommandTraits::KCI_attention: - case CommandTraits::KCI_author: - case CommandTraits::KCI_authors: - case CommandTraits::KCI_b: - case CommandTraits::KCI_bug: - case CommandTraits::KCI_c: - case CommandTraits::KCI_callergraph: - case CommandTraits::KCI_callgraph: - case CommandTraits::KCI_category: - case CommandTraits::KCI_cite: - case CommandTraits::KCI_class: - case CommandTraits::KCI_code: - case CommandTraits::KCI_concept: - case CommandTraits::KCI_cond: - case CommandTraits::KCI_copyright: - case CommandTraits::KCI_date: - case CommandTraits::KCI_def: - case CommandTraits::KCI_defgroup: - case CommandTraits::KCI_deprecated: - case CommandTraits::KCI_diafile: - case CommandTraits::KCI_dir: - case CommandTraits::KCI_docbookinclude: - case CommandTraits::KCI_docbookonly: - case CommandTraits::KCI_dontinclude: - case CommandTraits::KCI_dot: - case CommandTraits::KCI_dotfile: - //case CommandTraits::KCI_doxyconfig: - case CommandTraits::KCI_else: - case CommandTraits::KCI_elseif: - case CommandTraits::KCI_emoji: - case CommandTraits::KCI_endcode: - case CommandTraits::KCI_endcond: - case CommandTraits::KCI_enddocbookonly: - case CommandTraits::KCI_enddot: - case CommandTraits::KCI_endhtmlonly: - case CommandTraits::KCI_endif: - case CommandTraits::KCI_endinternal: - case CommandTraits::KCI_endlatexonly: - //case CommandTraits::KCI_endlink: - case CommandTraits::KCI_endmanonly: - case CommandTraits::KCI_endmsc: - case CommandTraits::KCI_endparblock: - case CommandTraits::KCI_endrtfonly: - case CommandTraits::KCI_endsecreflist: - case CommandTraits::KCI_endverbatim: - case CommandTraits::KCI_enduml: - case CommandTraits::KCI_endxmlonly: - case CommandTraits::KCI_enum: - case CommandTraits::KCI_example: - case CommandTraits::KCI_extends: - case CommandTraits::KCI_flparen: // @f( - case CommandTraits::KCI_frparen: // @f) - case CommandTraits::KCI_fdollar: // @f$ - case CommandTraits::KCI_flsquare: // @f[ - case CommandTraits::KCI_frsquare: // @f] - case CommandTraits::KCI_flbrace: // @f{ - case CommandTraits::KCI_frbrace: // @f} - case CommandTraits::KCI_file: - //case CommandTraits::KCI_fileinfo: - case CommandTraits::KCI_fn: - case CommandTraits::KCI_headerfile: - case CommandTraits::KCI_hidecallergraph: - case CommandTraits::KCI_hidecallgraph: - case CommandTraits::KCI_hiderefby: - case CommandTraits::KCI_hiderefs: - case CommandTraits::KCI_hideinitializer: - case CommandTraits::KCI_htmlinclude: - case CommandTraits::KCI_htmlonly: - case CommandTraits::KCI_idlexcept: - case CommandTraits::KCI_if: - case CommandTraits::KCI_ifnot: - case CommandTraits::KCI_image: - case CommandTraits::KCI_implements: - case CommandTraits::KCI_include: - //case CommandTraits::KCI_includedoc: - //case CommandTraits::KCI_includelineno: - case CommandTraits::KCI_ingroup: - case CommandTraits::KCI_internal: - case CommandTraits::KCI_invariant: - case CommandTraits::KCI_interface: - case CommandTraits::KCI_latexinclude: - case CommandTraits::KCI_latexonly: - case CommandTraits::KCI_line: - //case CommandTraits::KCI_lineinfo: - case CommandTraits::KCI_link: - case CommandTraits::KCI_mainpage: - case CommandTraits::KCI_maninclude: - case CommandTraits::KCI_manonly: - case CommandTraits::KCI_memberof: - case CommandTraits::KCI_msc: - case CommandTraits::KCI_mscfile: - case CommandTraits::KCI_name: - case CommandTraits::KCI_namespace: - case CommandTraits::KCI_noop: - case CommandTraits::KCI_nosubgrouping: - case CommandTraits::KCI_overload: - case CommandTraits::KCI_p: - //case CommandTraits::KCI_package: - case CommandTraits::KCI_page: - case CommandTraits::KCI_paragraph: - case CommandTraits::KCI_param: - case CommandTraits::KCI_parblock: - case CommandTraits::KCI_private: - case CommandTraits::KCI_privatesection: - case CommandTraits::KCI_property: - case CommandTraits::KCI_protected: - case CommandTraits::KCI_protectedsection: - case CommandTraits::KCI_protocol: - case CommandTraits::KCI_public: - case CommandTraits::KCI_publicsection: - case CommandTraits::KCI_pure: - //case CommandTraits::KCI_qualifier: - //case CommandTraits::KCI_raisewarning: - case CommandTraits::KCI_ref: - case CommandTraits::KCI_refitem: - case CommandTraits::KCI_related: - case CommandTraits::KCI_relates: - case CommandTraits::KCI_relatedalso: - case CommandTraits::KCI_relatesalso: - case CommandTraits::KCI_remark: - case CommandTraits::KCI_remarks: - - case CommandTraits::KCI_retval: - case CommandTraits::KCI_rtfinclude: - case CommandTraits::KCI_rtfonly: - case CommandTraits::KCI_sa: - case CommandTraits::KCI_secreflist: - case CommandTraits::KCI_section: - //case CommandTraits::KCI_showdate: - case CommandTraits::KCI_showinitializer: - case CommandTraits::KCI_showrefby: - case CommandTraits::KCI_showrefs: - case CommandTraits::KCI_since: - case CommandTraits::KCI_skip: - case CommandTraits::KCI_skipline: - case CommandTraits::KCI_snippet: - //case CommandTraits::KCI_snippetdoc: - //case CommandTraits::KCI_snippetlineno: - case CommandTraits::KCI_static: - case CommandTraits::KCI_startuml: - case CommandTraits::KCI_struct: - case CommandTraits::KCI_subpage: - case CommandTraits::KCI_subsection: - case CommandTraits::KCI_subsubsection: - case CommandTraits::KCI_tableofcontents: - case CommandTraits::KCI_test: - case CommandTraits::KCI_todo: - case CommandTraits::KCI_tparam: - case CommandTraits::KCI_typedef: - case CommandTraits::KCI_union: - case CommandTraits::KCI_until: - case CommandTraits::KCI_var: - case CommandTraits::KCI_verbatim: - case CommandTraits::KCI_verbinclude: - case CommandTraits::KCI_version: - //case CommandTraits::KCI_vhdlflow: - case CommandTraits::KCI_weakgroup: - case CommandTraits::KCI_xmlinclude: - case CommandTraits::KCI_xmlonly: - case CommandTraits::KCI_xrefitem: - /* - @$ - @@ - @\ - @& - @~ - @< - @= - @> - @# - @% - @" - @. - @:: - @| - @-- - @--- - */ - break; - - // inline commands handled elsewhere - case CommandTraits::KCI_a: - case CommandTraits::KCI_e: - case CommandTraits::KCI_em: - case CommandTraits::KCI_n: - case CommandTraits::KCI_copybrief: - case CommandTraits::KCI_copydetails: - case CommandTraits::KCI_copydoc: - report::error("error: inline command {} should be handled elsewhere", cmd->Name); - MRDOCS_UNREACHABLE(); - default: - report::error("error: unsupported block command {}", cmd->Name); - MRDOCS_UNREACHABLE(); - } -} - -void -JavadocVisitor:: -visitParamCommandComment( - ParamCommandComment const* C) -{ - MRDOCS_COMMENT_TRACE(C, ctx_); - doc::Param param; - if(C->hasParamName()) - { - param.name = ensureUTF8(C->getParamNameAsWritten().str()); - } - else - { - // VFALCO report SourceLocation here - diags_.error("Missing parameter name in @param"); - param.name = "@anon"; - } - if(C->isDirectionExplicit()) - param.direction = convertDirection(C->getDirection()); - - auto scope = enterScope(param); - visitChildren(C->getParagraph()); - - auto const itr = std::ranges::find_if( - jd_.getBlocks(), - [&](Polymorphic const& b) - { - if (b->Kind != doc::NodeKind::param) - { - return false; - } - auto p = dynamic_cast(b.operator->()); - MRDOCS_ASSERT(p != nullptr); - return p->name == param.name; - }); - if (itr != jd_.getBlocks().end()) - { - report::warn( - "{}: Duplicate @param for argument {}", - C->getBeginLoc().printToString(sm_), param.name); - } - - // We want the node even if it is empty - jd_.params.push_back(std::move(param)); -} - -void -JavadocVisitor:: -visitTParamCommandComment( - TParamCommandComment const* C) -{ - MRDOCS_COMMENT_TRACE(C, ctx_); - doc::TParam tparam; - if(C->hasParamName()) - { - tparam.name = ensureUTF8(C->getParamNameAsWritten().str()); - } - else - { - // VFALCO report SourceLocation here - diags_.error("Missing parameter name in @tparam"); - tparam.name = "@anon"; - } - auto scope = enterScope(tparam); - visitChildren(C->getParagraph()); - - auto const itr = std::ranges::find_if( - jd_.getBlocks(), - [&](Polymorphic const& b) - { - if (b->Kind != doc::NodeKind::tparam) - { - return false; - } - auto const tp = dynamic_cast(b.operator->()); - MRDOCS_ASSERT(tp != nullptr); - return tp->name == tparam.name; - }); - if (itr != jd_.getBlocks().end()) - { - report::warn( - "{}: Duplicate @tparam for argument {}", - C->getBeginLoc().printToString(sm_), tparam.name); - } - - // We want the node even if it is empty - jd_.tparams.push_back(std::move(tparam)); -} - -void -JavadocVisitor:: -visitVerbatimBlockComment( - VerbatimBlockComment const* C) -{ - MRDOCS_COMMENT_TRACE(C, ctx_); - doc::Code code; - auto scope = enterScope(code); - //if(C->hasNonWhitespaceParagraph()) - visitChildren(C); - jd_.emplace_back(std::move(code)); -} - -void -JavadocVisitor:: -visitVerbatimLineComment( - VerbatimLineComment const* C) -{ - MRDOCS_COMMENT_TRACE(C, ctx_); - // VFALCO This doesn't seem to be used - // in any of my codebases, follow up -} - -void -JavadocVisitor:: -visitVerbatimBlockLineComment( - VerbatimBlockLineComment const* C) -{ - MRDOCS_COMMENT_TRACE(C, ctx_); - emplaceText(true, C->getText().str()); -} - -//------------------------------------------------ - -bool -JavadocVisitor:: -goodArgCount(std::size_t n, - InlineCommandComment const& C) -{ - MRDOCS_COMMENT_TRACE(C, ctx_); - if(C.getNumArgs() != n) - { - auto loc = sm_.getPresumedLoc(C.getBeginLoc()); - - diags_.error(std::format("Expected {} but got {} args\n" - "File: {}, line {}, col {}\n", - n, C.getNumArgs(), loc.getFilename(), - loc.getLine(), loc.getColumn())); - - return false; - } - return true; -} - -//------------------------------------------------ - -} // (anon) - -void -initCustomCommentCommands(ASTContext& context) -{ - auto& traits = context.getCommentCommandTraits(); - - { - //auto cmd = traits.registerBlockCommand("mrdocs"); - //auto cmd = traits.registerBlockCommand("par"); - - //CommentOptions opt; - //opt.BlockCommandNames.push_back("par"); - //traits.registerCommentOptions(opt); - } - - (void)traits; -} - -void -parseJavadoc( - std::optional& jd, - FullComment const* FC, - Decl const* D, - Config const& config, - Diagnostics& diags) -{ - MRDOCS_COMMENT_TRACE(FC, D->getASTContext()); - JavadocVisitor visitor(FC, D, config, diags); - auto result = visitor.build(); - if (!result.empty()) - { - if (!jd) - { - jd = std::move(result); - } - else if(*jd != result) - { - // merge - jd->append(std::move(result)); - } - } - -} - -} // clang::mrdocs diff --git a/src/lib/AST/ParseRef.cpp b/src/lib/AST/ParseRef.cpp index 1a974ae20..541bff34e 100644 --- a/src/lib/AST/ParseRef.cpp +++ b/src/lib/AST/ParseRef.cpp @@ -8,17 +8,16 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include "lib/AST/ParseRef.hpp" -#include +#include #include +#include #include #include #include #include - #include -namespace clang::mrdocs { +namespace mrdocs { namespace { constexpr @@ -42,13 +41,38 @@ isIdentifierContinuation(char const c) return isIdentifierStart(c) || isDigit(c); } +/* Holds information about a parsed function suffix during reference parsing. + + Used internally by RefParser to accumulate details about function parameters, + variadic status, and exception specifications while parsing C++ symbol + references. + + Example: In 'void foo(int, double, ...)', Params holds 'int' and 'double', + IsVariadic is true, HasVoid is false. +*/ struct ParsedFunctionSuffix { - llvm::SmallVector, 8> Params; + /* List of parsed function parameter types. + Example: For 'void foo(int, double)', Params contains 'int' and 'double'. + */ + llvm::SmallVector, 8> Params; + + /* True if the parameter list contains only 'void'. + Example: For 'void foo(void)', HasVoid is true. + */ bool HasVoid{false}; + + /* True if the function is variadic (contains ...). + Example: For 'void foo(int, ...)', IsVariadic is true. + */ bool IsVariadic{false}; + + /* Exception specification for the function. + Example: For 'void foo() noexcept', ExceptionSpec holds 'noexcept'. + */ NoexceptInfo ExceptionSpec; + /* Virtual destructor for safe polymorphic deletion. */ virtual ~ParsedFunctionSuffix() = default; }; @@ -448,7 +472,7 @@ class RefParser return false; } skipWhitespace(); - Polymorphic conversionType = std::nullopt; + Optional> conversionType = std::nullopt; if (!parseDeclarationSpecifier(conversionType) || !conversionType) { @@ -585,14 +609,17 @@ class RefParser return false; } skipWhitespace(); - TemplateArguments.emplace_back(std::nullopt); + // Add an empty slot for the first template argument + TemplateArguments.emplace_back(nullable_traits>::null()); while (parseTemplateArgument(TemplateArguments.back())) { skipWhitespace(); if (parseLiteral(',')) { skipWhitespace(); - TemplateArguments.emplace_back(std::nullopt); + // Add another empty slot for the next argument after each comma + // This allows parseTemplateArgument to fill the new slot + TemplateArguments.emplace_back(nullable_traits>::null()); } else { @@ -622,16 +649,17 @@ class RefParser } skipWhitespace(); char const* start = ptr_; - Polymorphic type = std::nullopt; + Optional> type = std::nullopt; if (parseTypeId(type)) { + MRDOCS_ASSERT(type); dest = Polymorphic(std::in_place_type); - static_cast(*dest).Type = std::move(type); + dest->asType().Type = std::move(*type); return true; } // If the argument is not a type-id, it is an expression - // The expression is internally balanced in regards to '<' + // The expression is internally balanced regarding '<' // and '>' and ends with a comma char const* exprStart = ptr_; while (parseBalanced("<", ">", {",", ">"})) @@ -648,8 +676,8 @@ class RefParser ptr_ = start; return false; } - dest = Polymorphic(std::in_place_type); - static_cast(*dest).Value.Written = + dest = Polymorphic(std::in_place_type); + static_cast(*dest).Value.Written = trim(std::string_view(exprStart, ptr_ - exprStart)); return true; } @@ -790,13 +818,15 @@ class RefParser // https://en.cppreference.com/w/cpp/language/function#Parameter_list // decl-specifier-seq - auto &curParam = dest.Params.emplace_back(std::nullopt); - if (!parseTypeId(curParam)) + Optional> curParamOpt = std::nullopt; + if (!parseTypeId(curParamOpt)) { ptr_ = start; setError("expected type-id"); return false; } + MRDOCS_ASSERT(curParamOpt); + auto &curParam = dest.Params.emplace_back(std::move(*curParamOpt)); // 2. After determining the type of each parameter, any parameter // of type “array of T” or of function type T is adjusted to be @@ -804,11 +834,12 @@ class RefParser // https://en.cppreference.com/w/cpp/language/function#Function_type if (curParam->isArray()) { - auto ATI = dynamic_cast(*curParam); - curParam = Polymorphic(std::in_place_type); - *curParam = ATI; - static_cast(*curParam).PointeeType = - std::move(ATI.ElementType); + ArrayType PrevParamType = curParam->asArray(); + curParam = Polymorphic(std::in_place_type); + auto& curAsPointerType = curParam->asPointer(); + curAsPointerType.PointeeType = std::move(PrevParamType.ElementType); + auto& PrevAsBase = PrevParamType.asType(); + *curParam = std::move(PrevAsBase); } // 3. After producing the list of parameter types, any top-level @@ -823,7 +854,7 @@ class RefParser } bool - parseTypeId(Polymorphic& dest) + parseTypeId(Optional>& dest) { char const* start = ptr_; @@ -852,7 +883,7 @@ class RefParser } bool - parseDeclarationSpecifiers(Polymorphic& dest) + parseDeclarationSpecifiers(Optional>& dest) { //static constexpr std::string_view typeQualifiers[] = { // "const", "volatile" @@ -981,14 +1012,13 @@ class RefParser // which is "int" if (!dest) { - dest = Polymorphic(std::in_place_type); - auto &NTI = static_cast(*dest); - NTI.Name = Polymorphic(); - NTI.Name->Name = "int"; + dest = Polymorphic(std::in_place_type); + auto &NTI = (*dest)->asNamed(); + NTI.Name->Identifier = "int"; NTI.FundamentalType = FundamentalTypeKind::Int; } // Check if the type is named - if (!dest->isNamed()) + if (!(*dest)->isNamed()) { setError(std::format("expected named type for '{}' specifier", signStr)); @@ -996,7 +1026,7 @@ class RefParser return false; } // Check if the type is "int" or "char" - auto& namedParam = dynamic_cast(*dest); + auto& namedParam = (*dest)->asNamed(); if (!namedParam.FundamentalType) { setError(std::format( @@ -1016,7 +1046,7 @@ class RefParser return false; } // Add the specifier to the type name - namedParam.Name->Name = toString(*namedParam.FundamentalType); + namedParam.Name->Identifier = toString(*namedParam.FundamentalType); } // - "short" can be combined with int. @@ -1025,14 +1055,14 @@ class RefParser // Infer basic fundamental type for "short", which is "int" if (!dest) { - dest = Polymorphic(std::in_place_type); - auto &NTI = static_cast(*dest); - NTI.Name = Polymorphic(); - NTI.Name->Name = "int"; + dest = Polymorphic(std::in_place_type); + auto &NTI = (*dest)->asNamed(); + NTI.Name = Polymorphic(std::in_place_type); + NTI.Name->Identifier = "int"; NTI.FundamentalType = FundamentalTypeKind::Int; } // Check if the type is named - if (!dest->isNamed()) + if (!(*dest)->isNamed()) { setError(start, "expected named type for 'short' specifier"); ptr_ = start; @@ -1040,7 +1070,7 @@ class RefParser } // Check if the type is "int" - auto& namedParam = dynamic_cast(*dest); + auto& namedParam = (*dest)->asNamed(); if (!namedParam.FundamentalType) { setError(start, "expected fundamental type for 'short' specifier"); @@ -1055,7 +1085,7 @@ class RefParser return false; } // Add the specifier to the type name - namedParam.Name->Name = toString(*namedParam.FundamentalType); + namedParam.Name->Identifier = toString(*namedParam.FundamentalType); } // - "long" can be combined with "int", "double" and "long" @@ -1064,20 +1094,20 @@ class RefParser // Infer basic fundamental type for "long", which is "int" if (!dest) { - dest = Polymorphic(std::in_place_type); - auto &NTI = static_cast(*dest); - NTI.Name = Polymorphic(); - NTI.Name->Name = "int"; + dest = Polymorphic(std::in_place_type); + auto &NTI = (*dest)->asNamed(); + NTI.Name = Polymorphic(std::in_place_type); + NTI.Name->Identifier = "int"; NTI.FundamentalType = FundamentalTypeKind::Int; } // Check if the type is named - if (!dest->isNamed()) + if (!(*dest)->isNamed()) { setError(start, "expected named type for 'long' specifier"); ptr_ = start; return false; } - auto& namedParam = dynamic_cast(*dest); + auto& namedParam = (*dest)->asNamed(); if (!namedParam.FundamentalType) { setError(start, "expected fundamental type for 'long' specifier"); @@ -1102,7 +1132,7 @@ class RefParser } } // Add the specifier to the type name - namedParam.Name->Name = toString(*namedParam.FundamentalType); + namedParam.Name->Identifier = toString(*namedParam.FundamentalType); } // Final check: if dest is still empty, we have an error @@ -1114,19 +1144,19 @@ class RefParser } // Set cv qualifiers - dest->IsConst = contains(specifiers, "const"); - dest->IsVolatile = contains(specifiers, "volatile"); + (*dest)->IsConst = contains(specifiers, "const"); + (*dest)->IsVolatile = contains(specifiers, "volatile"); return true; } bool - parseDeclarationSpecifier(Polymorphic& dest) + parseDeclarationSpecifier(Optional>& dest) { char const* start = ptr_; // Some rules are only valid if dest was initially empty - auto checkDestWasEmpty = [destWasEmpty=!dest, start, this]() { + auto checkDestWasEmpty = [destWasEmpty=!dest.has_value(), start, this]() { if (!destWasEmpty) { setError(start, "multiple type declaration specifiers"); @@ -1177,12 +1207,14 @@ class RefParser { MRDOCS_CHECK_OR(checkDestWasEmpty(), false); - dest = Polymorphic(std::in_place_type); - auto &NTI = static_cast(*dest); - NTI.Name = Polymorphic(); - NTI.Name->Name = std::string_view(start, ptr_ - start); + dest = Polymorphic(std::in_place_type); + MRDOCS_ASSERT(dest); + MRDOCS_ASSERT(!dest->valueless_after_move()); + auto &NTI = (*dest)->asNamed(); + MRDOCS_ASSERT(!NTI.Name.valueless_after_move()); + NTI.Name->Identifier = std::string_view(start, ptr_ - start); if (FundamentalTypeKind k; - fromString(NTI.Name->Name, k)) + fromString(NTI.Name->Identifier, k)) { NTI.FundamentalType = k; } @@ -1193,7 +1225,7 @@ class RefParser if (parseKeyword("auto")) { MRDOCS_CHECK_OR(checkDestWasEmpty(), false); - dest = Polymorphic(std::in_place_type); + dest = Polymorphic(std::in_place_type); return true; } @@ -1209,12 +1241,14 @@ class RefParser skipWhitespace(); if (parseLiteral(")")) { - MRDOCS_CHECK_OR(checkDestWasEmpty(), false); - dest = Polymorphic( - std::in_place_type); - static_cast(*dest).Keyword = - AutoKind::DecltypeAuto; - return true; + MRDOCS_CHECK_OR(checkDestWasEmpty(), false); + dest = Polymorphic( + std::in_place_type); + MRDOCS_ASSERT(dest); + MRDOCS_ASSERT(!dest->valueless_after_move()); + static_cast(**dest).Keyword = AutoKind:: + DecltypeAuto; + return true; } } // - "decltype(expression)" @@ -1242,10 +1276,11 @@ class RefParser return false; } MRDOCS_CHECK_OR(checkDestWasEmpty(), false); - dest = Polymorphic( - std::in_place_type); - static_cast(*dest).Operand.Written = - expr; + dest = Polymorphic( + std::in_place_type); + MRDOCS_ASSERT(dest); + MRDOCS_ASSERT(!dest->valueless_after_move()); + (*dest)->asDecltype().Operand.Written = expr; return true; } setError("expected expression in decltype"); @@ -1349,12 +1384,13 @@ class RefParser bool parseQualifiedIdentifier( - Polymorphic& dest, + Optional>& dest, bool const allowTemplateDisambiguation, bool const allowTemplateArguments) { if (dest) { + MRDOCS_ASSERT(!dest->valueless_after_move()); setError("type specifier is already set"); return false; } @@ -1372,13 +1408,14 @@ class RefParser // Populate dest auto const idStr = std::string_view(idStart, ptr_ - idStart); - Polymorphic ParentName = - dest ? dynamic_cast(&*dest)->Name - : std::nullopt; - dest = Polymorphic(std::in_place_type); - auto &NTI = static_cast(*dest); - NTI.Name = Polymorphic(); - NTI.Name->Name = idStr; + Optional> ParentName + = dest ? + Optional>((*dest)->asNamed().Name) : + Optional>(std::nullopt); + dest = Polymorphic(std::in_place_type); + auto &NTI = (*dest)->asNamed(); + NTI.Name = Polymorphic(std::in_place_type); + NTI.Name->Identifier = idStr; NTI.Name->Prefix = std::move(ParentName); // Look for the next "::" @@ -1402,16 +1439,17 @@ class RefParser skipWhitespace(); if (peek('<')) { - if (!dest->isNamed()) + MRDOCS_ASSERT(dest); + if (!(*dest)->isNamed()) { setError("expected named type for template arguments"); ptr_ = start; return false; } // Replace the nameinfo with a nameinfo with args - auto& namedParam = dynamic_cast(*dest); - SpecializationNameInfo SNI; - SNI.Name = std::move(namedParam.Name->Name); + auto& namedParam = (*dest)->asNamed(); + SpecializationName SNI; + SNI.Identifier = std::move(namedParam.Name->Identifier); SNI.Prefix = std::move(namedParam.Name->Prefix); SNI.id = namedParam.Name->id; if (!parseTemplateArguments(SNI.TemplateArgs)) @@ -1419,8 +1457,8 @@ class RefParser ptr_ = start; return false; } - namedParam.Name = Polymorphic( - std::in_place_type, std::move(SNI)); + namedParam.Name = Polymorphic( + std::in_place_type, std::move(SNI)); } else { @@ -1443,14 +1481,14 @@ class RefParser }; bool - parseAbstractDeclarator(Polymorphic& dest) + parseAbstractDeclarator(Optional>& dest) { return parseDeclarator(dest); } template bool - parseDeclarator(Polymorphic& dest) + parseDeclarator(Optional>& dest) { char const *start = ptr_; if (!parseDeclaratorOrNoPtrDeclarator(dest)) @@ -1491,7 +1529,9 @@ class RefParser auto isNoPtrDeclarator = [&dest, &suffixLevel, this]() { if (suffixLevel == 0) { - if (dest->isLValueReference() || dest->isRValueReference() || dest->isPointer()) + if ((*dest)->isLValueReference() || + (*dest)->isRValueReference() || + (*dest)->isPointer()) { return peekBack(')', ' '); } @@ -1516,21 +1556,24 @@ class RefParser // The same logic other elements that have inner types (pointers, // arrays, and references). // The current inner type of dest is the function return type. - auto inner = innerType(*dest); + MRDOCS_ASSERT(dest); + MRDOCS_ASSERT(!dest->valueless_after_move()); + Optional &> inner = innerType(**dest); // The more suffixes we have, the more levels of inner types // the suffix affects. // For instance, in "int (*)[3][6]", we have a pointer to an // array of 3 arrays of 6 ints. std::size_t curSuffixLevel = suffixLevel; - while (curSuffixLevel > 0 && inner && inner->get()) + while (curSuffixLevel > 0 && inner) { - auto& ref = inner->get(); + MRDOCS_ASSERT(!inner->valueless_after_move()); + auto& ref = *inner; inner = innerType(*ref); --curSuffixLevel; } char const* parenStart = ptr_; if (!parseArrayOrFunctionDeclaratorSuffix( - inner ? inner->get() : dest)) + inner ? *inner : *dest)) { setError(parenStart, "expected declarator"); ptr_ = start; @@ -1543,7 +1586,7 @@ class RefParser template bool - parseDeclaratorOrNoPtrDeclarator(Polymorphic& dest) + parseDeclaratorOrNoPtrDeclarator(Optional>& dest) { static constexpr bool isAbstractDeclarator = static_cast(declarator_type & abstract); static constexpr bool isInternalDeclarator = static_cast(declarator_type & internal_declarator); @@ -1653,7 +1696,7 @@ class RefParser { skipWhitespace(); parseIdentifier(false); - dest->IsPackExpansion = true; + (*dest)->IsPackExpansion = true; return parseClosingParens(); } @@ -1663,8 +1706,8 @@ class RefParser // https://en.cppreference.com/w/cpp/language/pointer if (parseLiteral("*")) { - if (dest->isLValueReference() || - dest->isRValueReference()) + if ((*dest)->isLValueReference() || + (*dest)->isRValueReference()) { setError("pointer to reference not allowed"); ptr_ = start; @@ -1672,15 +1715,15 @@ class RefParser } // Change current type to pointer type - PointerTypeInfo PTI; - PTI.PointeeType = std::move(dest); - dest = Polymorphic(std::move(PTI)); + PointerType PTI; + PTI.PointeeType = std::move(*dest); + dest = Polymorphic(std::move(PTI)); skipWhitespace(); // cv is a sequence of const and volatile qualifiers, // where either qualifier may appear at most once // in the sequence. - parseCV(dest->IsConst, dest->IsVolatile); + parseCV((*dest)->IsConst, (*dest)->IsVolatile); // Parse the next declarator, potentially wrapping // the destination type in another type // If this declarator is abstract, the pointer @@ -1720,14 +1763,14 @@ class RefParser } // Assemble the parent type for the NNS - NamedTypeInfo ParentType; + NamedType ParentType; auto NNSString = std::string_view(start, NNSEnd - start); - NameInfo NNS; + IdentifierName NNS; auto const NNSRange = llvm::split(NNSString, "::"); MRDOCS_ASSERT(!NNSRange.empty()); auto NNSIt = NNSRange.begin(); std::string_view unqualID = *NNSIt; - NNS.Name = std::string(unqualID); + NNS.Identifier = std::string(unqualID); ++NNSIt; while (NNSIt != NNSRange.end()) { @@ -1736,25 +1779,25 @@ class RefParser { break; } - NameInfo NewNNS; - NewNNS.Name = std::string(unqualID); - NewNNS.Prefix = Polymorphic(std::move(NNS)); + IdentifierName NewNNS; + NewNNS.Identifier = std::string(unqualID); + NewNNS.Prefix = Polymorphic(std::move(NNS)); NNS = NewNNS; ++NNSIt; } - ParentType.Name = Polymorphic(std::move(NNS)); + ParentType.Name = Polymorphic(std::move(NNS)); // Change current type to member pointer type - MemberPointerTypeInfo MPTI; - MPTI.PointeeType = Polymorphic(std::move(dest)); - MPTI.ParentType = Polymorphic(std::move(ParentType)); - dest = Polymorphic(std::move(MPTI)); + MemberPointerType MPTI; + MPTI.PointeeType = Polymorphic(std::move(*dest)); + MPTI.ParentType = Polymorphic(std::move(ParentType)); + dest = Polymorphic(std::move(MPTI)); skipWhitespace(); // cv is a sequence of const and volatile qualifiers, // where either qualifier may appear at most once // in the sequence. - parseCV(dest->IsConst, dest->IsVolatile); + parseCV((*dest)->IsConst, (*dest)->IsVolatile); parseIdentifier(false); // We ignore the name and just return true return parseClosingParens(); @@ -1785,15 +1828,15 @@ class RefParser if (bool const isRValue = parseLiteral("&"); !isRValue) { - LValueReferenceTypeInfo RTI; - RTI.PointeeType = std::move(dest); - dest = Polymorphic(std::move(RTI)); + LValueReferenceType RTI; + RTI.PointeeType = std::move(*dest); + dest = Polymorphic(std::move(RTI)); } else { - RValueReferenceTypeInfo RTI; - RTI.PointeeType = std::move(dest); - dest = Polymorphic(std::move(RTI)); + RValueReferenceType RTI; + RTI.PointeeType = std::move(*dest); + dest = Polymorphic(std::move(RTI)); } skipWhitespace(); @@ -1816,7 +1859,8 @@ class RefParser // function parenDepth = 0; ptr_ = start; - if (parseArrayOrFunctionDeclaratorSuffix(dest)) + MRDOCS_ASSERT(dest); + if (parseArrayOrFunctionDeclaratorSuffix(*dest)) { return true; } @@ -1840,8 +1884,10 @@ class RefParser // is empty. template bool - parseArrayOrFunctionDeclaratorSuffix(Polymorphic& dest) + parseArrayOrFunctionDeclaratorSuffix(Polymorphic& dest) { + MRDOCS_ASSERT(!dest.valueless_after_move()); + char const* start = ptr_; // (8) Array declarator. noptr-declarator any valid declarator, but @@ -1849,8 +1895,10 @@ class RefParser // parentheses. // noptr-declarator [expr (optional)] attr (optional) // https://en.cppreference.com/w/cpp/language/array - if (parseArrayDeclaratorSuffix(dest)) + Optional> r; + if (parseArrayDeclaratorSuffix(r)) { + dest = *r; return true; } ptr_ = start; @@ -1861,8 +1909,9 @@ class RefParser // noptr-declarator ( parameter-list ) cv (optional) ref (optional) except (optional) attr (optional) // https://en.cppreference.com/w/cpp/language/function // https://en.cppreference.com/w/cpp/language/function#Function_type - if (parseFunctionDeclaratorSuffix(dest)) + if (parseFunctionDeclaratorSuffix(r)) { + dest = *r; return true; } @@ -1871,7 +1920,7 @@ class RefParser template bool - parseArrayDeclaratorSuffix(Polymorphic& dest) + parseArrayDeclaratorSuffix(Optional>& dest) { char const* start = ptr_; @@ -1890,8 +1939,11 @@ class RefParser } // Change current type to array type - ArrayTypeInfo ATI; - ATI.ElementType = std::move(dest); + ArrayType ATI; + if (dest) + { + ATI.ElementType = std::move(*dest); + } // expr (optional) char const* exprStart = ptr_; @@ -1903,12 +1955,12 @@ class RefParser // Bounds.Value is an optional integer with the value // Bounds.Written is the original string representation // of the bounds - std::optional boundsValue = 0; + Optional boundsValue = 0; if (ConstantExprInfo Bounds; parseInteger(boundsValue) && peek(']', ' ')) { - Bounds.Value = boundsValue; + Bounds.Value = *boundsValue; Bounds.Written = std::string_view(exprStart, ptr_ - exprStart); ATI.Bounds = Bounds; if (!parseLiteral("]")) @@ -1933,7 +1985,7 @@ class RefParser ATI.Bounds = Bounds; } } - dest = Polymorphic(std::move(ATI)); + dest = Polymorphic(std::move(ATI)); skipWhitespace(); // We ignore the name and just return true @@ -1944,7 +1996,7 @@ class RefParser template bool - parseFunctionDeclaratorSuffix(Polymorphic& dest) + parseFunctionDeclaratorSuffix(Optional>& dest) { char const* start = ptr_; @@ -1977,15 +2029,18 @@ class RefParser ptr_ = start; return false; } - FunctionTypeInfo FTI; - FTI.ReturnType = std::move(dest); + FunctionType FTI; + if (dest) + { + FTI.ReturnType = std::move(*dest); + } FTI.ParamTypes.insert( FTI.ParamTypes.end(), std::make_move_iterator(function.Params.begin()), std::make_move_iterator(function.Params.end())); FTI.ExceptionSpec = std::move(function.ExceptionSpec); FTI.IsVariadic = function.IsVariadic; - dest = Polymorphic(std::move(FTI)); + dest = Polymorphic(std::move(FTI)); return true; } @@ -1993,7 +2048,7 @@ class RefParser } bool - parseInteger(std::optional& dest) + parseInteger(Optional& dest) { if (!hasMore()) { @@ -2215,4 +2270,4 @@ parse( return res; } -} // clang::mrdocs +} // mrdocs diff --git a/src/lib/AST/ParseRef.hpp b/src/lib/AST/ParseRef.hpp index 03091ba90..064ef3723 100644 --- a/src/lib/AST/ParseRef.hpp +++ b/src/lib/AST/ParseRef.hpp @@ -8,17 +8,17 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_LIB_PARSEREF_HPP -#define MRDOCS_LIB_PARSEREF_HPP +#ifndef MRDOCS_LIB_AST_PARSEREF_HPP +#define MRDOCS_LIB_AST_PARSEREF_HPP +#include #include #include #include -#include #include #include -namespace clang::mrdocs { +namespace mrdocs { struct TArg; @@ -36,7 +36,7 @@ struct ParsedRefComponent { // If not empty, this is a conversion operator // Only the last component can be a conversion operator - Polymorphic ConversionType = std::nullopt; + Optional> ConversionType = std::nullopt; constexpr bool @@ -65,7 +65,7 @@ struct ParsedRef { // The following are populated when the last element is a function bool HasFunctionParameters = false; - llvm::SmallVector, 8> FunctionParameters; + llvm::SmallVector, 8> FunctionParameters; bool IsVariadic = false; bool IsExplicitObjectMemberFunction = false; ReferenceKind Kind = ReferenceKind::None; @@ -80,6 +80,6 @@ parse( char const* last, ParsedRef& value); -} // clang::mrdocs +} // mrdocs -#endif +#endif // MRDOCS_LIB_AST_PARSEREF_HPP diff --git a/src/lib/AST/TerminalTypeVisitor.hpp b/src/lib/AST/TerminalTypeVisitor.hpp index ee2821f90..88bfd9947 100644 --- a/src/lib/AST/TerminalTypeVisitor.hpp +++ b/src/lib/AST/TerminalTypeVisitor.hpp @@ -17,12 +17,12 @@ #include #include -namespace clang::mrdocs { +namespace mrdocs { /** A visitor to build objects from `Type`s. MrDocs might need to convert class instances derived from `Type` - into other struct instances like `TypeInfo` or `NameInfo`. + into other struct instances like `Type` or `Name`. This class can be used to define a visitor to build objects from `Type`s. The visitor can be defined as a class that @@ -55,7 +55,7 @@ namespace clang::mrdocs { from `TerminalTypeVisitor`, which converts the `Type` into the concrete type and calls the corresponding `VisitXXXType` function. It also provides - `bool Visit(QualType QT)` as an extension to visit the + `bool Visit(clang::QualType QT)` as an extension to visit the `Type` associated with the qualified type. Each `VisitXXXType` function will store any relative information @@ -71,7 +71,7 @@ namespace clang::mrdocs { */ template class TerminalTypeVisitor - : public TypeVisitor, bool> + : public clang::TypeVisitor, bool> { friend class TerminalTypeVisitor::TypeVisitor; @@ -122,11 +122,11 @@ class TerminalTypeVisitor - Unwrapped type: `int` */ bool - Visit(QualType const QT) + Visit(clang::QualType const QT) { MRDOCS_SYMBOL_TRACE(QT, Visitor_.context_); Quals_ |= QT.getLocalFastQualifiers(); - Type const* T = QT.getTypePtrOrNull(); + clang::Type const* T = QT.getTypePtrOrNull(); MRDOCS_SYMBOL_TRACE(T, Visitor_.context_); return Visit(T); } @@ -151,7 +151,7 @@ class TerminalTypeVisitor static void buildPointer - (PointerType const*, + (clang::PointerType const*, unsigned) { } @@ -163,7 +163,7 @@ class TerminalTypeVisitor */ void buildLValueReference( - LValueReferenceType const*) + clang::LValueReferenceType const*) { } @@ -174,7 +174,7 @@ class TerminalTypeVisitor */ void buildRValueReference( - RValueReferenceType const*) + clang::RValueReferenceType const*) { } @@ -185,7 +185,7 @@ class TerminalTypeVisitor */ void buildMemberPointer( - MemberPointerType const*, unsigned) + clang::MemberPointerType const*, unsigned) { } @@ -196,13 +196,13 @@ class TerminalTypeVisitor */ void buildArray( - ArrayType const*) + clang::ArrayType const*) { } void populate( - FunctionType const*) + clang::FunctionType const*) { } @@ -213,7 +213,7 @@ class TerminalTypeVisitor */ void buildDecltype( - DecltypeType const*, + clang::DecltypeType const*, unsigned, bool) { @@ -226,7 +226,7 @@ class TerminalTypeVisitor */ void buildAuto( - AutoType const*, + clang::AutoType const*, unsigned, bool) { @@ -239,8 +239,8 @@ class TerminalTypeVisitor */ void buildTerminal( - NestedNameSpecifier, - Type const*, + clang::NestedNameSpecifier, + clang::Type const*, unsigned, bool) { @@ -253,9 +253,9 @@ class TerminalTypeVisitor */ void buildTerminal( - NestedNameSpecifier, - IdentifierInfo const*, - std::optional>, + clang::NestedNameSpecifier, + clang::IdentifierInfo const*, + Optional>, unsigned, bool) { @@ -268,9 +268,9 @@ class TerminalTypeVisitor */ void buildTerminal( - NestedNameSpecifier, - NamedDecl*, - std::optional>, + clang::NestedNameSpecifier, + clang::NamedDecl*, + Optional>, unsigned, bool) { @@ -299,9 +299,9 @@ class TerminalTypeVisitor - Unwrapped type: `int` */ bool - VisitParenType(ParenType const* T) + VisitParenType(clang::ParenType const* T) { - QualType I = T->getInnerType(); + clang::QualType I = T->getInnerType(); return Visit(I); } @@ -315,9 +315,9 @@ class TerminalTypeVisitor */ bool VisitMacroQualified( - MacroQualifiedType const* T) + clang::MacroQualifiedType const* T) { - QualType UT = T->getUnderlyingType(); + clang::QualType UT = T->getUnderlyingType(); return Visit(UT); } @@ -331,9 +331,9 @@ class TerminalTypeVisitor */ bool VisitAttributedType( - AttributedType const* T) + clang::AttributedType const* T) { - QualType MT = T->getModifiedType(); + clang::QualType MT = T->getModifiedType(); return Visit(MT); } @@ -346,9 +346,9 @@ class TerminalTypeVisitor - Unwrapped type: original `int[4]` */ bool - VisitAdjustedType(AdjustedType const* T) + VisitAdjustedType(clang::AdjustedType const* T) { - QualType OT = T->getOriginalType(); + clang::QualType OT = T->getOriginalType(); return Visit(OT); } @@ -361,9 +361,9 @@ class TerminalTypeVisitor - Unwrapped type: `int` */ bool - VisitUsingType(UsingType const* T) + VisitUsingType(clang::UsingType const* T) { - QualType UT = T->desugar(); + clang::QualType UT = T->desugar(); return Visit(UT); } @@ -377,9 +377,9 @@ class TerminalTypeVisitor */ bool VisitSubstTemplateTypeParmType( - SubstTemplateTypeParmType const* T) + clang::SubstTemplateTypeParmType const* T) { - QualType RT = T->getReplacementType(); + clang::QualType RT = T->getReplacementType(); return Visit(RT); } @@ -395,10 +395,10 @@ class TerminalTypeVisitor */ bool VisitPackExpansionType( - PackExpansionType const* T) + clang::PackExpansionType const* T) { IsPack_ = true; - QualType PT = T->getPattern(); + clang::QualType PT = T->getPattern(); return Visit(PT); } @@ -414,10 +414,10 @@ class TerminalTypeVisitor */ bool VisitPointerType( - PointerType const* T) + clang::PointerType const* T) { getDerived().buildPointer(T, std::exchange(Quals_, 0)); - QualType PT = T->getPointeeType(); + clang::QualType PT = T->getPointeeType(); return Visit(PT); } @@ -431,11 +431,11 @@ class TerminalTypeVisitor */ bool VisitLValueReferenceType( - LValueReferenceType const* T) + clang::LValueReferenceType const* T) { getDerived().buildLValueReference(T); Quals_ = 0; - QualType PT = T->getPointeeType(); + clang::QualType PT = T->getPointeeType(); return Visit(PT); } @@ -449,11 +449,11 @@ class TerminalTypeVisitor */ bool VisitRValueReferenceType( - RValueReferenceType const* T) + clang::RValueReferenceType const* T) { getDerived().buildRValueReference(T); Quals_ = 0; - QualType PT = T->getPointeeType(); + clang::QualType PT = T->getPointeeType(); return Visit(PT); } @@ -467,19 +467,19 @@ class TerminalTypeVisitor */ bool VisitMemberPointerType( - MemberPointerType const* T) + clang::MemberPointerType const* T) { getDerived().buildMemberPointer(T, std::exchange(Quals_, 0)); - QualType PT = T->getPointeeType(); + clang::QualType PT = T->getPointeeType(); return Visit(PT); } bool VisitFunctionType( - FunctionType const* T) + clang::FunctionType const* T) { getDerived().populate(T); - QualType RT = T->getReturnType(); + clang::QualType RT = T->getReturnType(); return Visit(RT); } @@ -493,10 +493,10 @@ class TerminalTypeVisitor */ bool VisitArrayType( - ArrayType const* T) + clang::ArrayType const* T) { getDerived().buildArray(T); - QualType ET = T->getElementType(); + clang::QualType ET = T->getElementType(); return Visit(ET); } @@ -504,7 +504,7 @@ class TerminalTypeVisitor bool VisitDecltypeType( - DecltypeType const* T) + clang::DecltypeType const* T) { getDerived().buildDecltype(T, Quals_, IsPack_); return true; @@ -512,7 +512,7 @@ class TerminalTypeVisitor bool VisitAutoType( - AutoType const* T) + clang::AutoType const* T) { // KRYSTIAN NOTE: we don't use isDeduced because it will // return true if the type is dependent @@ -523,18 +523,18 @@ class TerminalTypeVisitor bool VisitDeducedTemplateSpecializationType( - DeducedTemplateSpecializationType const* T) + clang::DeducedTemplateSpecializationType const* T) { - // KRYSTIAN TODO: we should probably add a TypeInfo + // KRYSTIAN TODO: we should probably add a Type // to represent deduced types also stores what it // was deduced as. - if (QualType DT = T->getDeducedType(); !DT.isNull()) + if (clang::QualType DT = T->getDeducedType(); !DT.isNull()) { return Visit(DT); } - TemplateName const TN = T->getTemplateName(); + clang::TemplateName const TN = T->getTemplateName(); MRDOCS_ASSERT(! TN.isNull()); - NamedDecl* ND = TN.getAsTemplateDecl(); + clang::NamedDecl* ND = TN.getAsTemplateDecl(); getDerived().buildTerminal(TN.getQualifier(), ND, std::nullopt, Quals_, IsPack_); return true; @@ -542,7 +542,7 @@ class TerminalTypeVisitor bool VisitDependentNameType( - DependentNameType const* T) + clang::DependentNameType const* T) { if (auto SFINAE = getASTVisitor().extractSFINAEInfo(T)) { @@ -557,10 +557,10 @@ class TerminalTypeVisitor bool VisitDependentTemplateSpecializationType( - DependentTemplateSpecializationType const* T) + clang::DependentTemplateSpecializationType const* T) { MRDOCS_SYMBOL_TRACE(T, Visitor_.context_); - const DependentTemplateStorage &S = T->getDependentTemplateName(); + const clang::DependentTemplateStorage &S = T->getDependentTemplateName(); getDerived().buildTerminal(S.getQualifier(), S.getName().getIdentifier(), T->template_arguments(), Quals_, IsPack_); return true; @@ -569,7 +569,7 @@ class TerminalTypeVisitor // Visit a template specialization such as `A` bool VisitTemplateSpecializationType( - TemplateSpecializationType const* T) + clang::TemplateSpecializationType const* T) { MRDOCS_SYMBOL_TRACE(T, Visitor_.context_); if (auto SFINAE = getASTVisitor().extractSFINAEInfo(T)) @@ -583,18 +583,18 @@ class TerminalTypeVisitor // is `template class X { };`. // Template names can also refer to function templates, // C++0x template aliases, etc... - TemplateName const TN = T->getTemplateName(); + clang::TemplateName const TN = T->getTemplateName(); MRDOCS_SYMBOL_TRACE(TN, Visitor_.context_); MRDOCS_ASSERT(! TN.isNull()); // The list of template parameters and a reference to // the templated scoped declaration - NamedDecl* D = TN.getAsTemplateDecl(); + clang::NamedDecl* D = TN.getAsTemplateDecl(); MRDOCS_SYMBOL_TRACE(TN, Visitor_.context_); if (!T->isTypeAlias()) { - if (auto* CT = dyn_cast(T->getCanonicalTypeInternal())) + if (auto* CT = dyn_cast(T->getCanonicalTypeInternal())) { MRDOCS_SYMBOL_TRACE(CT, Visitor_.context_); D = CT->getOriginalDecl()->getDefinitionOrSelf(); @@ -611,13 +611,13 @@ class TerminalTypeVisitor bool VisitRecordType( - RecordType const* T) + clang::RecordType const* T) { - RecordDecl* RD = T->getOriginalDecl()->getDefinitionOrSelf(); + clang::RecordDecl* RD = T->getOriginalDecl()->getDefinitionOrSelf(); // if this is an instantiation of a class template, - // create a SpecializationTypeInfo & extract the template arguments - std::optional> TArgs = std::nullopt; - if (auto const* CTSD = dyn_cast(RD)) + // create a SpecializationType & extract the template arguments + Optional> TArgs = std::nullopt; + if (auto const* CTSD = dyn_cast(RD)) { TArgs = CTSD->getTemplateArgs().asArray(); } @@ -628,7 +628,7 @@ class TerminalTypeVisitor bool VisitInjectedClassNameType( - InjectedClassNameType const* T) + clang::InjectedClassNameType const* T) { getDerived().buildTerminal(T->getQualifier(), T->getOriginalDecl()->getDefinitionOrSelf(), std::nullopt, Quals_, IsPack_); @@ -637,7 +637,7 @@ class TerminalTypeVisitor bool VisitEnumType( - EnumType const* T) + clang::EnumType const* T) { getDerived().buildTerminal(T->getQualifier(), T->getOriginalDecl()->getDefinitionOrSelf(), std::nullopt, Quals_, IsPack_); @@ -646,7 +646,7 @@ class TerminalTypeVisitor bool VisitTypedefType( - TypedefType const* T) + clang::TypedefType const* T) { getDerived().buildTerminal(T->getQualifier(), T->getDecl(), std::nullopt, Quals_, IsPack_); @@ -655,11 +655,11 @@ class TerminalTypeVisitor bool VisitTemplateTypeParmType( - TemplateTypeParmType const* T) + clang::TemplateTypeParmType const* T) { MRDOCS_SYMBOL_TRACE(T, Visitor_.context_); - IdentifierInfo const* II = nullptr; - if (TemplateTypeParmDecl const* D = T->getDecl()) + clang::IdentifierInfo const* II = nullptr; + if (clang::TemplateTypeParmDecl const* D = T->getDecl()) { MRDOCS_SYMBOL_TRACE(D, Visitor_.context_); if(D->isImplicit()) @@ -679,7 +679,7 @@ class TerminalTypeVisitor bool VisitSubstTemplateTypeParmPackType( - SubstTemplateTypeParmPackType const* T) + clang::SubstTemplateTypeParmPackType const* T) { getDerived().buildTerminal(std::nullopt, T->getIdentifier(), std::nullopt, Quals_, IsPack_); @@ -687,7 +687,7 @@ class TerminalTypeVisitor } bool - VisitType(Type const* T) + VisitType(clang::Type const* T) { getDerived().buildTerminal( T, Quals_, IsPack_); @@ -695,6 +695,6 @@ class TerminalTypeVisitor } }; -} // clang::mrdocs +} // mrdocs #endif diff --git a/src/lib/AST/TypeBuilder.cpp b/src/lib/AST/TypeBuilder.cpp new file mode 100644 index 000000000..f2bea70d5 --- /dev/null +++ b/src/lib/AST/TypeBuilder.cpp @@ -0,0 +1,290 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#include + +namespace mrdocs { + +void +TypeBuilder:: +buildPointer(clang::PointerType const*, unsigned quals) +{ + MRDOCS_ASSERT(Inner); + *Inner = Polymorphic(std::in_place_type); + auto &I = (*Inner)->asPointer(); + I.IsConst = quals & clang::Qualifiers::Const; + I.IsVolatile = quals & clang::Qualifiers::Volatile; + Inner = &I.PointeeType; +} + +void +TypeBuilder:: +buildLValueReference(clang::LValueReferenceType const*) +{ + MRDOCS_ASSERT(Inner); + *Inner = Polymorphic(std::in_place_type); + Inner = &(*Inner)->asLValueReference().PointeeType; +} + +void +TypeBuilder:: +buildRValueReference(clang::RValueReferenceType const*) +{ + MRDOCS_ASSERT(Inner); + *Inner = Polymorphic(std::in_place_type); + Inner = &(*Inner)->asRValueReference().PointeeType; +} + +void +TypeBuilder:: +buildMemberPointer( + clang::MemberPointerType const* T, + unsigned const quals) +{ + MRDOCS_ASSERT(Inner); + *Inner = Polymorphic(std::in_place_type); + auto &I = (*Inner)->asMemberPointer(); + I.IsConst = quals & clang::Qualifiers::Const; + I.IsVolatile = quals & clang::Qualifiers::Volatile; + // do not set NNS because the parent type is *not* + // a nested-name-specifier which qualifies the pointee type + I.ParentType = + getASTVisitor().toType(clang::QualType(T->getQualifier().getAsType(), 0)); + Inner = &I.PointeeType; +} + +void +TypeBuilder:: +buildArray(clang::ArrayType const* T) +{ + MRDOCS_ASSERT(Inner); + *Inner = Polymorphic(std::in_place_type); + auto &I = (*Inner)->asArray(); + if (auto* CAT = dyn_cast(T)) + { + getASTVisitor().populate(I.Bounds, CAT->getSizeExpr(), CAT->getSize()); + } + else if (auto* DAT = dyn_cast(T)) + { + getASTVisitor().populate(I.Bounds, DAT->getSizeExpr()); + } + Inner = &I.ElementType; +} + +void +TypeBuilder:: +populate(clang::FunctionType const* T) +{ + auto* FPT = cast(T); + MRDOCS_ASSERT(Inner); + *Inner = Polymorphic(std::in_place_type); + auto &I = (*Inner)->asFunction(); + for (clang::QualType const PT : FPT->getParamTypes()) + { + I.ParamTypes.emplace_back(getASTVisitor().toType(PT)); + } + I.RefQualifier = toReferenceKind( + FPT->getRefQualifier()); + unsigned const quals = FPT->getMethodQuals().getFastQualifiers(); + I.IsConst = quals & clang::Qualifiers::Const; + I.IsVolatile = quals & clang::Qualifiers::Volatile; + I.IsVariadic = FPT->isVariadic(); + getASTVisitor().populate(I.ExceptionSpec, FPT); + Inner = &I.ReturnType; +} + +void +TypeBuilder:: +buildDecltype( + clang::DecltypeType const* T, + unsigned quals, + bool pack) +{ + MRDOCS_ASSERT(Inner); + *Inner = Polymorphic(std::in_place_type); + (*Inner)->Constraints = this->Constraints; + (*Inner)->IsPackExpansion = pack; + + auto &I = (*Inner)->asDecltype(); + getASTVisitor().populate(I.Operand, T->getUnderlyingExpr()); + I.IsConst = quals & clang::Qualifiers::Const; + I.IsVolatile = quals & clang::Qualifiers::Volatile; + + Result->Constraints = this->Constraints; + Result->IsPackExpansion = pack; +} + +void +TypeBuilder:: +buildAuto( + clang::AutoType const* T, + unsigned const quals, + bool const pack) +{ + MRDOCS_ASSERT(Inner); + *Inner = Polymorphic(std::in_place_type); + (*Inner)->Constraints = this->Constraints; + (*Inner)->IsPackExpansion = pack; + + auto &I = (*Inner)->asAuto(); + I.IsConst = quals & clang::Qualifiers::Const; + I.IsVolatile = quals & clang::Qualifiers::Volatile; + I.Keyword = toAutoKind(T->getKeyword()); + if(T->isConstrained()) + { + Optional> TArgs; + if(auto Args = T->getTypeConstraintArguments(); + ! Args.empty()) + { + TArgs.emplace(Args); + } + I.Constraint = + getASTVisitor().toName(T->getTypeConstraintConcept(), TArgs); + // Constraint->Prefix = getASTVisitor().buildName( + // cast(CD->getDeclContext())); + } + Result->Constraints = this->Constraints; + Result->IsPackExpansion = pack; +} + +void +TypeBuilder:: +buildTerminal( + clang::Type const* T, + unsigned quals, + bool pack) +{ + MRDOCS_SYMBOL_TRACE(T, getASTVisitor().context_); + MRDOCS_ASSERT(Inner); + *Inner = Polymorphic(std::in_place_type); + (*Inner)->IsPackExpansion = pack; + (*Inner)->Constraints = this->Constraints; + + auto &TI = (*Inner)->asNamed(); + TI.IsConst = quals & clang::Qualifiers::Const; + TI.IsVolatile = quals & clang::Qualifiers::Volatile; + TI.Name = Polymorphic(std::in_place_type); + TI.Name->Identifier = getASTVisitor().toString(T); + if (isa(T)) + { + auto const* FT = cast(T); + TI.FundamentalType = toFundamentalTypeKind(FT->getKind()); + } + Result->Constraints = this->Constraints; + Result->IsPackExpansion = pack; +} + +void +TypeBuilder:: +buildTerminal( + clang::NestedNameSpecifier NNS, + clang::IdentifierInfo const* II, + Optional> TArgs, + unsigned quals, + bool pack) +{ + MRDOCS_ASSERT(Inner); + *Inner = Polymorphic(std::in_place_type); + (*Inner)->IsPackExpansion = pack; + (*Inner)->Constraints = this->Constraints; + + auto &I = (*Inner)->asNamed(); + I.IsConst = quals & clang::Qualifiers::Const; + I.IsVolatile = quals & clang::Qualifiers::Volatile; + + if (TArgs) + { + I.Name = Polymorphic(std::in_place_type); + auto &Name = I.Name->asSpecialization(); + if (II) + { + Name.Identifier = II->getName(); + } + Name.Prefix = getASTVisitor().toName(NNS); + getASTVisitor().populate(Name.TemplateArgs, *TArgs); + } + else + { + I.Name = Polymorphic(std::in_place_type); + auto &Name = *I.Name; + if (II) + { + Name.Identifier = II->getName(); + } + Name.Prefix = getASTVisitor().toName(NNS); + } + Result->Constraints = this->Constraints; + Result->IsPackExpansion = pack; +} + +void +TypeBuilder:: +buildTerminal( + clang::NestedNameSpecifier NNS, + clang::NamedDecl* D, + Optional> TArgs, + unsigned quals, + bool pack) +{ + MRDOCS_SYMBOL_TRACE(NNS, getASTVisitor().context_); + MRDOCS_SYMBOL_TRACE(D, getASTVisitor().context_); + MRDOCS_SYMBOL_TRACE(TArgs, getASTVisitor().context_); + + // Look for the Info type. If this is a template specialization, + // we look for the Info of the specialized record. + clang::Decl const* ID = decayToPrimaryTemplate(D); + MRDOCS_SYMBOL_TRACE(ID, getASTVisitor().context_); + + MRDOCS_ASSERT(Inner); + *Inner = Polymorphic(std::in_place_type); + (*Inner)->IsPackExpansion = pack; + (*Inner)->Constraints = this->Constraints; + + auto &TI = (*Inner)->asNamed(); + TI.IsConst = quals & clang::Qualifiers::Const; + TI.IsVolatile = quals & clang::Qualifiers::Volatile; + + auto populateName = [&](Name& Name, clang::NamedDecl* D) + { + if(clang::IdentifierInfo const* II = D->getIdentifier()) + { + Name.Identifier = II->getName(); + } + if (Symbol const* I = getASTVisitor().findOrTraverse(const_cast(ID))) + { + Name.id = I->id; + } + if(NNS) + { + Name.Prefix = getASTVisitor().toName(NNS); + } + }; + + if (!TArgs) + { + TI.Name = Polymorphic(std::in_place_type); + populateName(*TI.Name, D); + } + else + { + TI.Name = + Polymorphic(std::in_place_type); + auto &Name = TI.Name->asSpecialization(); + populateName(Name, D); + getASTVisitor().populate(Name.TemplateArgs, *TArgs); + } + Result->Constraints = this->Constraints; + Result->IsPackExpansion = pack; +} + +} // mrdocs diff --git a/src/lib/AST/TypeInfoBuilder.hpp b/src/lib/AST/TypeBuilder.hpp similarity index 60% rename from src/lib/AST/TypeInfoBuilder.hpp rename to src/lib/AST/TypeBuilder.hpp index 7d103f253..e868e0987 100644 --- a/src/lib/AST/TypeInfoBuilder.hpp +++ b/src/lib/AST/TypeBuilder.hpp @@ -11,57 +11,57 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_LIB_AST_TYPEINFOBUILDER_HPP -#define MRDOCS_LIB_AST_TYPEINFOBUILDER_HPP +#ifndef MRDOCS_LIB_AST_TYPEBUILDER_HPP +#define MRDOCS_LIB_AST_TYPEBUILDER_HPP #include -namespace clang::mrdocs { +namespace mrdocs { -/** A visitor to build a `mrdocs::TypeInfo` from a `clang::Type`. +/** A visitor to build a `mrdocs::Type` from a `clang::Type`. This class is used to build type information by visiting various terminal types. The method `Visit` from the base class can be used to iterate over the type information - and build the corresponding `TypeInfo` object: + and build the corresponding `Type` object: @code - TypeInfoBuilder Builder(astVisitor); + TypeBuilder Builder(astVisitor); Builder.Visit(qt); - std::unique_ptr typeInfo = Builder.result(); + std::unique_ptr typeInfo = Builder.result(); @endcode */ -class TypeInfoBuilder - : public TerminalTypeVisitor +class TypeBuilder + : public TerminalTypeVisitor { - /* The resulting of converting a QualType to a TypeInfo + /* The resulting of converting a QualType to a Type This variable holds the result of the type information - as a polymorphic `TypeInfo` object. + as a polymorphic `Type` object. */ - Polymorphic Result = std::nullopt; + Polymorphic Result = Polymorphic(AutoType{}); /* A pointer to the inner type of result currently being populated. - The Result variable is a polymorphic `TypeInfo` object that + The Result variable is a polymorphic `Type` object that might contain nested type information, also represented - as a `TypeInfo` object. + as a `Type` object. - For instance `int&` is represented as a `ReferenceTypeInfo` - object that contains a `NamedTypeInfo` object representing + For instance `int&` is represented as a `ReferenceType` + object that contains a `NamedType` object representing the `int` type. The builder will always populate the inner type of the result being constructed. For instance, when building - a `ReferenceTypeInfo` object for `int&`, the inner type + a `ReferenceType` object for `int&`, the inner type (initially the same as the result) will be set to a - `LValueReferenceTypeInfo`, that contains the `NamedTypeInfo` + `LValueReferenceType`, that contains the `NamedType` as a member. So `Inner` becomes a pointer to this - `NamedTypeInfo` object, and the visiting process continues + `NamedType` object, and the visiting process continues populating the `Inner` object. */ - Polymorphic* Inner = &Result; + Polymorphic* Inner = &Result; public: using TerminalTypeVisitor::TerminalTypeVisitor; @@ -69,11 +69,11 @@ class TypeInfoBuilder /** Get the result of the type information. This function returns the result of the type information - as a unique pointer to the `TypeInfo` object. + as a unique pointer to the `Type` object. - @return A unique pointer to the `TypeInfo` object. + @return A unique pointer to the `Type` object. */ - Polymorphic + Polymorphic result() { return std::move(Result); @@ -81,90 +81,90 @@ class TypeInfoBuilder /** Build type information for a pointer type. - Create a `PointerTypeInfo` object and populate it with + Create a `PointerType` object and populate it with the qualifiers and the pointee type. @param T The pointer type to build. @param quals The qualifiers for the pointer type. */ - void buildPointer(PointerType const* T, unsigned quals); + void buildPointer(clang::PointerType const* T, unsigned quals); /** Build type information for a lvalue reference type. - Create a `LValueReferenceTypeInfo` object and populate it with + Create a `LValueReferenceType` object and populate it with the pointee type. @param T The lvalue reference type to build. */ - void buildLValueReference(LValueReferenceType const* T); + void buildLValueReference(clang::LValueReferenceType const* T); /** Build type information for an rvalue reference type. - Create a `RValueReferenceTypeInfo` object and populate it with + Create a `RValueReferenceType` object and populate it with the pointee type. @param T The rvalue reference type to build. */ - void buildRValueReference(RValueReferenceType const* T); + void buildRValueReference(clang::RValueReferenceType const* T); /** Build type information for a member pointer type. - Create a `MemberPointerTypeInfo` object and populate it with + Create a `MemberPointerType` object and populate it with the qualifiers and the parent type. - A `MemberPointerTypeInfo` object is used to represent a pointer + A `MemberPointerType` object is used to represent a pointer to a member of a class. @param T The member pointer type to build. @param quals The qualifiers for the member pointer type. */ - void buildMemberPointer(MemberPointerType const* T, unsigned quals); + void buildMemberPointer(clang::MemberPointerType const* T, unsigned quals); /** Build type information for an array type. - Create an `ArrayTypeInfo` object and populate it with the + Create an `ArrayType` object and populate it with the element type and the array bounds. - An `ArrayTypeInfo` object is used to represent an array type. - It includes the internal `TypeInfo` object for the element type + An `ArrayType` object is used to represent an array type. + It includes the internal `Type` object for the element type and the expression defining the array bounds. @param T The array type to build. */ - void buildArray(ArrayType const* T); + void buildArray(clang::ArrayType const* T); /** Populate type information for a function type. - Create a `FunctionTypeInfo` object and populate it with + Create a `FunctionType` object and populate it with the function type information. - A `FunctionTypeInfo` object is used to represent a function type. + A `FunctionType` object is used to represent a function type. It includes the return type and the parameter types. @param T The function type to populate. */ - void populate(FunctionType const* T); + void populate(clang::FunctionType const* T); /** Build type information for a decltype type. - Create a `DecltypeTypeInfo` object and populate it with + Create a `DecltypeType` object and populate it with the underlying expression. - A `DecltypeTypeInfo` object is used to represent a decltype type. + A `DecltypeType` object is used to represent a decltype type. It includes the underlying expression. @param T The decltype type to build. @param quals The qualifiers for the decltype type. @param pack Whether the decltype type is a pack. */ - void buildDecltype(DecltypeType const* T, unsigned quals, bool pack); + void buildDecltype(clang::DecltypeType const* T, unsigned quals, bool pack); /** Build type information for an auto type. - Create an `AutoTypeInfo` object and populate it with + Create an `AutoType` object and populate it with the qualifiers. - An `AutoTypeInfo` object is used to represent an auto type. + An `AutoType` object is used to represent an auto type. It includes the qualifiers for the auto type, the keyword used to declare the auto type, and constraints. @@ -172,14 +172,14 @@ class TypeInfoBuilder @param quals The qualifiers for the auto type. @param pack Whether the auto type is a pack. */ - void buildAuto(AutoType const* T, unsigned quals, bool pack); + void buildAuto(clang::AutoType const* T, unsigned quals, bool pack); /** Build type information for a terminal type. - Create a `NamedTypeInfo` object and populate it with + Create a `NamedType` object and populate it with the name information. - A `NamedTypeInfo` object is used to represent a terminal type. + A `NamedType` object is used to represent a terminal type. It includes the name information, the nested name specifier, and the qualifiers for the terminal type. @@ -189,16 +189,16 @@ class TypeInfoBuilder @param pack Whether the terminal type is a pack. */ void buildTerminal( - Type const* T, + clang::Type const* T, unsigned quals, bool pack); /** Build type information for a terminal type with an identifier. - Create a `NamedTypeInfo` object and populate it with + Create a `NamedType` object and populate it with the name information. - A `NamedTypeInfo` object is used to represent a terminal type. + A `NamedType` object is used to represent a terminal type. It includes the name information, the nested name specifier, and the qualifiers for the terminal type. @@ -209,18 +209,18 @@ class TypeInfoBuilder @param pack Whether the terminal type is a pack. */ void buildTerminal( - NestedNameSpecifier NNS, - IdentifierInfo const* II, - std::optional> TArgs, + clang::NestedNameSpecifier NNS, + clang::IdentifierInfo const* II, + Optional> TArgs, unsigned quals, bool pack); /** Build type information for a terminal type with a named declaration. - Create a `NamedTypeInfo` object and populate it with + Create a `NamedType` object and populate it with the name information. - A `NamedTypeInfo` object is used to represent a terminal type. + A `NamedType` object is used to represent a terminal type. It includes the name information, the nested name specifier, and the qualifiers for the terminal type. @@ -231,13 +231,13 @@ class TypeInfoBuilder @param pack Whether the terminal type is a pack. */ void buildTerminal( - NestedNameSpecifier NNS, - NamedDecl* D, - std::optional> TArgs, + clang::NestedNameSpecifier NNS, + clang::NamedDecl* D, + Optional> TArgs, unsigned quals, bool pack); }; -} // clang::mrdocs +} // mrdocs -#endif +#endif // MRDOCS_LIB_AST_TYPEBUILDER_HPP diff --git a/src/lib/AST/TypeInfoBuilder.cpp b/src/lib/AST/TypeInfoBuilder.cpp deleted file mode 100644 index 30527d5de..000000000 --- a/src/lib/AST/TypeInfoBuilder.cpp +++ /dev/null @@ -1,279 +0,0 @@ -// -// This is a derivative work. originally part of the LLVM Project. -// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) -// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) -// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) -// -// Official repository: https://github.com/cppalliance/mrdocs -// - -#include "lib/AST/TypeInfoBuilder.hpp" - -namespace clang::mrdocs { - -void -TypeInfoBuilder:: -buildPointer(PointerType const*, unsigned quals) -{ - *Inner = Polymorphic(std::in_place_type); - auto &I = static_cast(**Inner); - I.IsConst = quals & Qualifiers::Const; - I.IsVolatile = quals & Qualifiers::Volatile; - Inner = &I.PointeeType; -} - -void -TypeInfoBuilder:: -buildLValueReference(LValueReferenceType const*) -{ - *Inner = Polymorphic(std::in_place_type); - Inner = &static_cast(**Inner).PointeeType; -} - -void -TypeInfoBuilder:: -buildRValueReference(RValueReferenceType const*) -{ - *Inner = Polymorphic(std::in_place_type); - Inner = &static_cast(**Inner).PointeeType; -} - -void -TypeInfoBuilder:: -buildMemberPointer( - MemberPointerType const* T, - unsigned const quals) -{ - *Inner = Polymorphic(std::in_place_type); - auto &I = static_cast(**Inner); - I.IsConst = quals & Qualifiers::Const; - I.IsVolatile = quals & Qualifiers::Volatile; - // do not set NNS because the parent type is *not* - // a nested-name-specifier which qualifies the pointee type - I.ParentType = - getASTVisitor().toTypeInfo(QualType(T->getQualifier().getAsType(), 0)); - Inner = &I.PointeeType; -} - -void -TypeInfoBuilder:: -buildArray(ArrayType const* T) -{ - *Inner = Polymorphic(std::in_place_type); - auto &I = static_cast(**Inner); - if (auto* CAT = dyn_cast(T)) - { - getASTVisitor().populate(I.Bounds, CAT->getSizeExpr(), CAT->getSize()); - } - else if (auto* DAT = dyn_cast(T)) - { - getASTVisitor().populate(I.Bounds, DAT->getSizeExpr()); - } - Inner = &I.ElementType; -} - -void -TypeInfoBuilder:: -populate(FunctionType const* T) -{ - auto* FPT = cast(T); - *Inner = Polymorphic(std::in_place_type); - auto &I = static_cast(**Inner); - for (QualType const PT : FPT->getParamTypes()) - { - I.ParamTypes.emplace_back(getASTVisitor().toTypeInfo(PT)); - } - I.RefQualifier = toReferenceKind( - FPT->getRefQualifier()); - unsigned const quals = FPT->getMethodQuals().getFastQualifiers(); - I.IsConst = quals & Qualifiers::Const; - I.IsVolatile = quals & Qualifiers::Volatile; - I.IsVariadic = FPT->isVariadic(); - getASTVisitor().populate(I.ExceptionSpec, FPT); - Inner = &I.ReturnType; -} - -void -TypeInfoBuilder:: -buildDecltype( - DecltypeType const* T, - unsigned quals, - bool pack) -{ - *Inner = Polymorphic(std::in_place_type); - (*Inner)->Constraints = this->Constraints; - (*Inner)->IsPackExpansion = pack; - - auto &I = static_cast(**Inner); - getASTVisitor().populate(I.Operand, T->getUnderlyingExpr()); - I.IsConst = quals & Qualifiers::Const; - I.IsVolatile = quals & Qualifiers::Volatile; - - Result->Constraints = this->Constraints; - Result->IsPackExpansion = pack; -} - -void -TypeInfoBuilder:: -buildAuto( - AutoType const* T, - unsigned const quals, - bool const pack) -{ - *Inner = Polymorphic(std::in_place_type); - (*Inner)->Constraints = this->Constraints; - (*Inner)->IsPackExpansion = pack; - - auto &I = static_cast(**Inner); - I.IsConst = quals & Qualifiers::Const; - I.IsVolatile = quals & Qualifiers::Volatile; - I.Keyword = toAutoKind(T->getKeyword()); - if(T->isConstrained()) - { - std::optional> TArgs; - if(auto Args = T->getTypeConstraintArguments(); - ! Args.empty()) - { - TArgs.emplace(Args); - } - I.Constraint = - getASTVisitor().toNameInfo(T->getTypeConstraintConcept(), TArgs); - // Constraint->Prefix = getASTVisitor().buildNameInfo( - // cast(CD->getDeclContext())); - } - Result->Constraints = this->Constraints; - Result->IsPackExpansion = pack; -} - -void -TypeInfoBuilder:: -buildTerminal( - Type const* T, - unsigned quals, - bool pack) -{ - MRDOCS_SYMBOL_TRACE(T, getASTVisitor().context_); - *Inner = Polymorphic(std::in_place_type); - (*Inner)->IsPackExpansion = pack; - (*Inner)->Constraints = this->Constraints; - - auto &TI = static_cast(**Inner); - TI.IsConst = quals & Qualifiers::Const; - TI.IsVolatile = quals & Qualifiers::Volatile; - TI.Name = Polymorphic(); - TI.Name->Name = getASTVisitor().toString(T); - if (isa(T)) - { - auto const* FT = cast(T); - TI.FundamentalType = toFundamentalTypeKind(FT->getKind()); - } - Result->Constraints = this->Constraints; - Result->IsPackExpansion = pack; -} - -void -TypeInfoBuilder:: -buildTerminal( - NestedNameSpecifier NNS, - IdentifierInfo const* II, - std::optional> TArgs, - unsigned quals, - bool pack) -{ - *Inner = Polymorphic(std::in_place_type); - (*Inner)->IsPackExpansion = pack; - (*Inner)->Constraints = this->Constraints; - - auto &I = static_cast(**Inner); - I.IsConst = quals & Qualifiers::Const; - I.IsVolatile = quals & Qualifiers::Volatile; - - if (TArgs) - { - I.Name = Polymorphic(std::in_place_type); - auto &Name = static_cast(*I.Name); - if (II) - { - Name.Name = II->getName(); - } - Name.Prefix = getASTVisitor().toNameInfo(NNS); - getASTVisitor().populate(Name.TemplateArgs, *TArgs); - } - else - { - I.Name = Polymorphic(); - auto &Name = *I.Name; - if (II) - { - Name.Name = II->getName(); - } - Name.Prefix = getASTVisitor().toNameInfo(NNS); - } - Result->Constraints = this->Constraints; - Result->IsPackExpansion = pack; -} - -void -TypeInfoBuilder:: -buildTerminal( - NestedNameSpecifier NNS, - NamedDecl* D, - std::optional> TArgs, - unsigned quals, - bool pack) -{ - MRDOCS_SYMBOL_TRACE(NNS, getASTVisitor().context_); - MRDOCS_SYMBOL_TRACE(D, getASTVisitor().context_); - MRDOCS_SYMBOL_TRACE(TArgs, getASTVisitor().context_); - - // Look for the Info type. If this is a template specialization, - // we look for the Info of the specialized record. - Decl const* ID = decayToPrimaryTemplate(D); - MRDOCS_SYMBOL_TRACE(ID, getASTVisitor().context_); - - *Inner = Polymorphic(std::in_place_type); - (*Inner)->IsPackExpansion = pack; - (*Inner)->Constraints = this->Constraints; - - auto &TI = static_cast(**Inner); - TI.IsConst = quals & Qualifiers::Const; - TI.IsVolatile = quals & Qualifiers::Volatile; - - auto populateNameInfo = [&](NameInfo& Name, NamedDecl* D) - { - if(IdentifierInfo const* II = D->getIdentifier()) - { - Name.Name = II->getName(); - } - if (Info const* I = getASTVisitor().findOrTraverse(const_cast(ID))) - { - Name.id = I->id; - } - if(NNS) - { - Name.Prefix = getASTVisitor().toNameInfo(NNS); - } - }; - - if (!TArgs) - { - TI.Name = Polymorphic(); - populateNameInfo(*TI.Name, D); - } - else - { - TI.Name = - Polymorphic(std::in_place_type); - auto &Name = static_cast(*TI.Name); - populateNameInfo(Name, D); - getASTVisitor().populate(Name.TemplateArgs, *TArgs); - } - Result->Constraints = this->Constraints; - Result->IsPackExpansion = pack; -} - -} // clang::mrdocs diff --git a/src/lib/Config.cpp b/src/lib/Config.cpp index 943c9302d..c6ad84735 100644 --- a/src/lib/Config.cpp +++ b/src/lib/Config.cpp @@ -14,7 +14,7 @@ #include #include -namespace clang::mrdocs { +namespace mrdocs { Config:: Config() noexcept @@ -562,4 +562,4 @@ outputDir() const return files::getParentDir(output); } -} // clang::mrdocs +} // mrdocs diff --git a/src/lib/ConfigImpl.cpp b/src/lib/ConfigImpl.cpp index 795be704c..977a70b0e 100644 --- a/src/lib/ConfigImpl.cpp +++ b/src/lib/ConfigImpl.cpp @@ -11,10 +11,10 @@ // #include "ConfigImpl.hpp" -#include "lib/Support/Glob.hpp" -#include "lib/Support/Path.hpp" -#include +#include +#include #include +#include #include #include #include @@ -23,7 +23,7 @@ #include #include -namespace clang { + namespace mrdocs { namespace { @@ -42,7 +42,7 @@ ConfigImpl(access_token, ThreadPool& threadPool) bool ConfigImpl:: -shouldVisitSymbol(StringRef filePath) const noexcept +shouldVisitSymbol(llvm::StringRef filePath) const noexcept { if (settings_.input.empty()) { @@ -75,7 +75,7 @@ shouldVisitSymbol(StringRef filePath) const noexcept bool ConfigImpl:: shouldExtractFromFile( - StringRef filePath, + llvm::StringRef filePath, std::string& prefixPath) const noexcept { namespace path = llvm::sys::path; @@ -133,10 +133,10 @@ toDomObject(llvm::yaml::MappingNode* Object) dom::Object obj; for (auto &Pair : *Object) { - auto *KeyString = dyn_cast(Pair.getKey()); + auto *KeyString = clang::dyn_cast(Pair.getKey()); if (!KeyString) { continue; } - SmallString<10> KeyStorage; - StringRef KeyValue = KeyString->getValue(KeyStorage); + llvm::SmallString<10> KeyStorage; + llvm::StringRef KeyValue = KeyString->getValue(KeyStorage); llvm::yaml::Node *Value = Pair.getValue(); if (!Value) { obj.set(KeyValue, dom::Kind::Undefined); @@ -163,9 +163,9 @@ toDomArray(llvm::yaml::SequenceNode* Array) dom::Value toDomScalar(llvm::yaml::ScalarNode* Scalar) { - SmallString<10> ScalarStorage; - StringRef ScalarValue = Scalar->getValue(ScalarStorage); - StringRef RawValue = Scalar->getRawValue(); + llvm::SmallString<10> ScalarStorage; + llvm::StringRef ScalarValue = Scalar->getValue(ScalarStorage); + llvm::StringRef RawValue = Scalar->getRawValue(); bool const isEscaped = RawValue.size() != ScalarValue.size(); if (isEscaped) { @@ -196,17 +196,17 @@ toDomScalar(llvm::yaml::ScalarNode* Scalar) dom::Value toDom(llvm::yaml::Node* Value) { - auto *ValueObject = dyn_cast(Value); + auto *ValueObject = clang::dyn_cast(Value); if (ValueObject) { return toDomObject(ValueObject); } - auto *ValueArray = dyn_cast(Value); + auto *ValueArray = clang::dyn_cast(Value); if (ValueArray) { return toDomArray(ValueArray); } - auto *ValueString = dyn_cast(Value); + auto *ValueString = clang::dyn_cast(Value); if (ValueString) { return toDomScalar(ValueString); @@ -246,7 +246,7 @@ toDomObject(std::string_view yaml) return {}; } llvm::yaml::Node *Root = I->getRoot(); - auto *Object = dyn_cast(Root); + auto *Object = clang::dyn_cast(Root); if (!Object) { return {}; @@ -320,4 +320,4 @@ updateConfigDom() } // mrdocs -} // clang + diff --git a/src/lib/ConfigImpl.hpp b/src/lib/ConfigImpl.hpp index b3a839286..05d8d0b6e 100644 --- a/src/lib/ConfigImpl.hpp +++ b/src/lib/ConfigImpl.hpp @@ -13,13 +13,13 @@ #ifndef MRDOCS_LIB_CONFIGIMPL_HPP #define MRDOCS_LIB_CONFIGIMPL_HPP -#include "lib/Support/YamlFwd.hpp" +#include #include #include #include #include -namespace clang { + namespace mrdocs { /* Private configuration implementation. @@ -159,6 +159,6 @@ class ConfigImpl } // mrdocs -} // clang -#endif + +#endif // MRDOCS_LIB_CONFIGIMPL_HPP diff --git a/src/lib/ConfigOptions.json b/src/lib/ConfigOptions.json index a75281f17..dc8212890 100644 --- a/src/lib/ConfigOptions.json +++ b/src/lib/ConfigOptions.json @@ -453,6 +453,13 @@ "type": "bool", "default": true }, + { + "name": "show-enum-constants", + "brief": "Show enum constant pages in the documentation", + "details": "When set to true, MrDocs creates a page for each enum constant in the documentation.", + "type": "bool", + "default": false + }, { "name": "global-namespace-index", "brief": "Use the global namespace page as an index for all symbols", diff --git a/src/lib/Corpus.cpp b/src/lib/Corpus.cpp index 159a02938..cf8ed6ab3 100644 --- a/src/lib/Corpus.cpp +++ b/src/lib/Corpus.cpp @@ -9,13 +9,13 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include "ConfigImpl.hpp" +#include #include #include #include #include -namespace clang::mrdocs { +namespace mrdocs { //------------------------------------------------ @@ -36,11 +36,11 @@ empty() const noexcept /** Return the metadata for the global namespace. */ -NamespaceInfo const& +NamespaceSymbol const& Corpus:: globalNamespace() const noexcept { - return get(SymbolID::global); + return get(SymbolID::global); } //------------------------------------------------ @@ -50,7 +50,7 @@ globalNamespace() const noexcept //------------------------------------------------ std::vector -getParents(Corpus const& C, Info const& I) +getParents(Corpus const& C, Symbol const& I) { std::vector parents; std::size_t n = 0; @@ -73,4 +73,4 @@ getParents(Corpus const& C, Info const& I) return parents; } -} // clang::mrdocs +} // mrdocs diff --git a/src/lib/CorpusImpl.cpp b/src/lib/CorpusImpl.cpp index 258c4c8e6..e7cd73038 100644 --- a/src/lib/CorpusImpl.cpp +++ b/src/lib/CorpusImpl.cpp @@ -12,24 +12,24 @@ // #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/DerivedFinalizer.hpp" -#include "lib/Metadata/Finalizers/JavadocFinalizer.hpp" -#include "lib/Metadata/Finalizers/NamespacesFinalizer.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 +#include +#include +#include +#include +#include #include #include #include #include #include -namespace clang::mrdocs { +namespace mrdocs { auto CorpusImpl:: @@ -41,8 +41,7 @@ begin() const noexcept -> // KRYSTIAN NOTE: this is far from ideal, but i'm not sure // to what extent implementation detail should be hidden. return iterator(this, info_.begin()->get(), - [](Corpus const* corpus, Info const* val) -> - Info const* + [](Corpus const* corpus, Symbol const* val) -> Symbol const* { MRDOCS_ASSERT(val); CorpusImpl const* impl = @@ -62,7 +61,7 @@ end() const noexcept -> return iterator(); } -Info* +Symbol* CorpusImpl:: find( SymbolID const& id) noexcept @@ -75,7 +74,7 @@ find( return nullptr; } -Info const* +Symbol const* CorpusImpl:: find(SymbolID const& id) const noexcept { @@ -89,7 +88,7 @@ find(SymbolID const& id) const noexcept namespace { bool -isTransparent(Info const& info) +isTransparent(Symbol const& info) { return visit(info, []( InfoTy const& I) -> bool @@ -108,7 +107,7 @@ isTransparent(Info const& info) SymbolID findFirstParentInfo( - InfoSet const& info, + SymbolSet const& info, SymbolID const& contextID) { SymbolID currentID = contextID; @@ -123,7 +122,7 @@ findFirstParentInfo( bool const isParent = visit(context, []( InfoTy const& I) -> bool { - return InfoParent; + return SymbolParent; }); if (isParent) { @@ -135,23 +134,24 @@ findFirstParentInfo( bool qualifiedNameCompare( - Polymorphic const& lhs0, - Polymorphic const& rhs0, - Info const& context, + Polymorphic const& lhs0, + Polymorphic const& rhs0, + Symbol const& context, CorpusImpl const& corpus) { - MRDOCS_CHECK_OR(lhs0 && rhs0, false); + MRDOCS_ASSERT(!lhs0.valueless_after_move()); + MRDOCS_ASSERT(!rhs0.valueless_after_move()); // Compare each component of the qualified name - NameInfo const* lhs = &*lhs0; - NameInfo const* rhs = &*rhs0; + Name const* lhs = &*lhs0; + Name const* rhs = &*rhs0; while (lhs && rhs) { - if (lhs->Name != rhs->Name) + if (lhs->Identifier != rhs->Identifier) { return false; } - lhs = lhs->Prefix ? &*lhs->Prefix : nullptr; - rhs = rhs->Prefix ? &*rhs->Prefix : nullptr; + lhs = lhs->Prefix ? &**lhs->Prefix : nullptr; + rhs = rhs->Prefix ? &**rhs->Prefix : nullptr; } // We consumed all components of both names if (!lhs && !rhs) @@ -162,13 +162,13 @@ qualifiedNameCompare( // these component should match the names from // the context. When we doesn't match the context // we try again with the parent context. - NameInfo const* curName0 = lhs ? lhs : rhs; - Info const* curContext0 = &context; - NameInfo const* curName = curName0; - Info const* curContext = curContext0; + Name const* curName0 = lhs ? lhs : rhs; + Symbol const* curContext0 = &context; + Name const* curName = curName0; + Symbol const* curContext = curContext0; while (curName && curContext) { - if (curName->Name != curContext->Name) + if (curName->Identifier != curContext->Name) { // The name doesn't match the context name // Try again, starting from the parent @@ -184,7 +184,7 @@ qualifiedNameCompare( continue; } // Names matches, match next component - curName = curName->Prefix ? &*curName->Prefix : nullptr; + curName = curName->Prefix ? &**curName->Prefix : nullptr; curContext = curContext->Parent ? corpus.find(curContext->Parent) : nullptr; } // We should have consumed all components of the name with the context @@ -194,15 +194,31 @@ qualifiedNameCompare( template bool isDecayedEqualImpl( - Polymorphic const& lhs, - Polymorphic const& rhs, - Info const& context, + Optional> const& lhs, + Optional> const& rhs, + Symbol const& context, + CorpusImpl const& corpus); + +// Check if two types are equal after decay +// +// The isInner template parameter indicates if +// we are comparing inner types (e.g., pointee types) +// or root types (e.g., function parameter types) because +// the rules are slightly different depending +// on the level of the type specifiers. +// +template +bool +isDecayedEqualImpl( + Polymorphic const& lhs, + Polymorphic const& rhs, + Symbol const& context, CorpusImpl const& corpus) { // Polymorphic - MRDOCS_CHECK_OR(static_cast(lhs) == static_cast(rhs), false); - MRDOCS_CHECK_OR(static_cast(lhs) && static_cast(rhs), true); - // TypeInfo + MRDOCS_ASSERT(!lhs.valueless_after_move()); + MRDOCS_ASSERT(!rhs.valueless_after_move()); + // Type bool const decayToPointer = !isInner && (lhs->isArray() || rhs->isArray()); if (!decayToPointer) { @@ -217,7 +233,7 @@ isDecayedEqualImpl( MRDOCS_CHECK_OR(lhs->IsPackExpansion == rhs->IsPackExpansion, false); if constexpr (isInner) { - // const and volative are ignored from root types + // const and volatile are ignored from root types // in function parameters MRDOCS_CHECK_OR(lhs->IsConst == rhs->IsConst, false); MRDOCS_CHECK_OR(lhs->IsVolatile == rhs->IsVolatile, false); @@ -227,24 +243,24 @@ isDecayedEqualImpl( { // Types that never decay are compared directly, but we // only compare the fields of the type, without reevaluating - // the fields of TypeInfo. + // the fields of Type. case TypeKind::Named: { return qualifiedNameCompare( - dynamic_cast(*lhs).Name, - dynamic_cast(*rhs).Name, + lhs->asNamed().Name, + rhs->asNamed().Name, context, corpus); } case TypeKind::Decltype: { - return dynamic_cast(*lhs).Operand == - dynamic_cast(*rhs).Operand; + return lhs->asDecltype().Operand == + rhs->asDecltype().Operand; } case TypeKind::Auto: { - auto const& lhsAuto = dynamic_cast(*lhs); - auto const& rhsAuto = dynamic_cast(*rhs); + auto const& lhsAuto = lhs->asAuto(); + auto const& rhsAuto = rhs->asAuto(); return lhsAuto.Keyword == rhsAuto.Keyword && lhsAuto.Constraint == rhsAuto.Constraint; } @@ -252,30 +268,30 @@ isDecayedEqualImpl( { return isDecayedEqualImpl( - dynamic_cast(*lhs).PointeeType, - dynamic_cast(*rhs).PointeeType, + lhs->asLValueReference().PointeeType, + rhs->asLValueReference().PointeeType, context, corpus); } case TypeKind::RValueReference: { return isDecayedEqualImpl( - dynamic_cast(*lhs).PointeeType, - dynamic_cast(*rhs).PointeeType, + dynamic_cast(*lhs).PointeeType, + dynamic_cast(*rhs).PointeeType, context, corpus); } case TypeKind::MemberPointer: { - auto const& lhsMP = dynamic_cast(*lhs); - auto const& rhsMP = dynamic_cast(*rhs); + auto const& lhsMP = dynamic_cast(*lhs); + auto const& rhsMP = dynamic_cast(*rhs); return isDecayedEqualImpl(lhsMP.PointeeType, rhsMP.PointeeType, context, corpus) && isDecayedEqualImpl(lhsMP.ParentType, rhsMP.ParentType, context, corpus); } case TypeKind::Function: { - auto const& lhsF = dynamic_cast(*lhs); - auto const& rhsF = dynamic_cast(*rhs); + auto const& lhsF = dynamic_cast(*lhs); + auto const& rhsF = dynamic_cast(*rhs); MRDOCS_CHECK_OR(lhsF.RefQualifier == rhsF.RefQualifier, false); MRDOCS_CHECK_OR(lhsF.ExceptionSpec == rhsF.ExceptionSpec, false); MRDOCS_CHECK_OR(lhsF.IsVariadic == rhsF.IsVariadic, false); @@ -293,9 +309,12 @@ isDecayedEqualImpl( { auto const I1 = innerType(*lhs); auto const I2 = innerType(*rhs); + // Both inner types must be present or absent, otherwise not equal MRDOCS_CHECK_OR(static_cast(I1) == static_cast(I2), false); + // Both inner types are absent: they are equal MRDOCS_CHECK_OR(static_cast(I1) && static_cast(I2), true); - return isDecayedEqualImpl(I1->get(), I2->get(), context, corpus); + // Both inner types are present: compare them internally + return isDecayedEqualImpl(*I1, *I2, context, corpus); } default: MRDOCS_UNREACHABLE(); @@ -303,14 +322,27 @@ isDecayedEqualImpl( return true; } +template +bool +isDecayedEqualImpl( + Optional> const& lhs, + Optional> const& rhs, + Symbol const& context, + CorpusImpl const& corpus) +{ + MRDOCS_CHECK_OR(static_cast(lhs) == static_cast(rhs), false); + MRDOCS_CHECK_OR(static_cast(lhs) && static_cast(rhs), true); + return isDecayedEqualImpl(*lhs, *rhs, context, corpus); +} + /* Compare two types for equality for the purposes of overload resolution. */ bool isDecayedEqual( - Polymorphic const& lhs, - Polymorphic const& rhs, - Info const& context, + Polymorphic const& lhs, + Polymorphic const& rhs, + Symbol const& context, CorpusImpl const& corpus) { return isDecayedEqualImpl(lhs, rhs, context, corpus); @@ -320,7 +352,7 @@ bool isDecayedEqual( Polymorphic const& lhs, Polymorphic const& rhs, - Info const& context, + Symbol const& context, CorpusImpl const& corpus) { if (lhs->Kind != rhs->Kind) @@ -333,23 +365,23 @@ isDecayedEqual( static_cast(*rhs).Type, context, corpus); } - if (lhs->isNonType()) + if (lhs->isConstant()) { - return trim(static_cast(*lhs).Value.Written) == - trim(static_cast(*rhs).Value.Written); + return trim(static_cast(*lhs).Value.Written) == + trim(static_cast(*rhs).Value.Written); } return false; } } -Expected> +Expected CorpusImpl:: lookup(SymbolID const& context, std::string_view const name) const { return lookupImpl(*this, context, name); } -Expected> +Expected CorpusImpl:: lookup(SymbolID const& context, std::string_view name) { @@ -357,7 +389,7 @@ lookup(SymbolID const& context, std::string_view name) } template -Expected> +Expected CorpusImpl:: lookupImpl(Self&& self, SymbolID const& contextId0, std::string_view name) { @@ -380,7 +412,7 @@ lookupImpl(Self&& self, SymbolID const& contextId0, std::string_view name) name, self.Corpus::qualifiedName(*self.find(contextId)))); } - return std::cref(*info); + return *info; } auto const expRef = parse(name); if (!expRef) @@ -388,7 +420,7 @@ lookupImpl(Self&& self, SymbolID const& contextId0, std::string_view name) return Unexpected(formatError("Failed to parse '{}'\n {}", name, expRef.error().reason())); } ParsedRef const& ref = *std::move(expRef); - Info const* res = lookupImpl(self, contextId, ref, name, false); + Symbol const* res = lookupImpl(self, contextId, ref, name, false); if (!res) { auto const contextPtr = self.find(contextId); @@ -401,11 +433,11 @@ lookupImpl(Self&& self, SymbolID const& contextId0, std::string_view name) name, self.Corpus::qualifiedName(*contextPtr))); } - return std::cref(*res); + return *res; } template -Info const* +Symbol const* CorpusImpl:: lookupImpl( Self&& self, @@ -423,12 +455,12 @@ lookupImpl( } } - Info const* contextPtr = self.find(contextId); + Symbol const* contextPtr = self.find(contextId); MRDOCS_CHECK_OR(contextPtr, nullptr); report::trace(" Context: '{}'", contextPtr->Name); // Check the current contextId - Info const* curContext = contextPtr; + Symbol const* curContext = contextPtr; for (std::size_t i = 0; i < ref.Components.size(); ++i) { ParsedRefComponent const& component = ref.Components[i]; @@ -446,13 +478,13 @@ lookupImpl( } // Fallback to parent contextId - Info const& contextInfo = *contextPtr; - Info const* res = lookupImpl(self, contextInfo.Parent, ref, name, true); + Symbol const& contextInfo = *contextPtr; + Symbol const* res = lookupImpl(self, contextInfo.Parent, ref, name, true); self.lookupCacheSet(contextId, name, res); return res; } -Info const* +Symbol const* CorpusImpl:: lookupImpl( SymbolID const& contextId, @@ -462,7 +494,7 @@ lookupImpl( { report::trace("Looking up component '{}'", component.Name); // 1. Find context - Info const* contextPtr = this->find(contextId); + Symbol const* contextPtr = this->find(contextId); MRDOCS_CHECK_OR(contextPtr, nullptr); report::trace(" Context: '{}'", contextPtr->Name); @@ -470,20 +502,21 @@ lookupImpl( if (auto* TI = contextPtr->asTypedefPtr()) { MRDOCS_CHECK_OR(TI->Type, nullptr); - SymbolID resolvedSymbolID = TI->Type->namedSymbol(); + MRDOCS_ASSERT(!TI->Type.valueless_after_move()); + SymbolID resolvedSymbolID = (*TI->Type).namedSymbol(); contextPtr = this->find(resolvedSymbolID); MRDOCS_CHECK_OR(contextPtr, nullptr); } - Info const& context = *contextPtr; + Symbol const& context = *contextPtr; // 2. Get a list of members report::trace(" Finding members of context '{}'", contextPtr->Name); - SmallVector memberIDs; - auto pushAllMembersOf = [&](Info const& I) + llvm::SmallVector memberIDs; + auto pushAllMembersOf = [&](Symbol const& I) { - visit(I, [&](CInfoTy const& C) -> Info const* + visit(I, [&](CInfoTy const& C) -> Symbol const* { - if constexpr (InfoParent) + if constexpr (SymbolParent) { for (SymbolID const& MId : allMembers(C)) { @@ -501,7 +534,7 @@ lookupImpl( for (std::size_t i = 0; i < rootMembersSize; ++i) { SymbolID const& id = memberIDs[i]; - Info const* I = find(id); + Symbol const* I = find(id); MRDOCS_CHECK_OR_CONTINUE(I); MRDOCS_CHECK_OR_CONTINUE(I->isOverloads()); pushAllMembersOf(*I); @@ -526,12 +559,12 @@ lookupImpl( MatchLevel::TemplateArgs : MatchLevel::Qualifiers; auto matchLevel = MatchLevel::None; - Info const* res = nullptr; + Symbol const* res = nullptr; for (SymbolID const& memberID : memberIDs) { - Info const* memberPtr = find(memberID); + Symbol const* memberPtr = find(memberID); MRDOCS_CHECK_OR_CONTINUE(memberPtr); - Info const& member = *memberPtr; + Symbol const& member = *memberPtr; report::trace(" Attempting to match {} '{}'", toString(member.Kind), member.Name); MatchLevel const memberMatchLevel = visit(member, [&](MInfoTy const& M) @@ -541,8 +574,8 @@ lookupImpl( // Name match if constexpr ( - std::same_as || - std::same_as) + std::same_as || + std::same_as) { if (component.isOperator()) { @@ -568,7 +601,7 @@ lookupImpl( TemplateInfo const* templateInfo = [&]() -> TemplateInfo const* { if constexpr (requires { M.Template; }) { - std::optional const& OTI = M.Template; + Optional const& OTI = M.Template; MRDOCS_CHECK_OR(OTI, nullptr); TemplateInfo const& TI = *OTI; return &TI; @@ -610,7 +643,7 @@ lookupImpl( // This is an intermediary level because among choices that // doesn't exactly match the function parameters, we prefer // the one that is documented as the most "natural" choice. - if (F.javadoc) + if (F.doc) { matchRes = MatchLevel::FunctionParametersSizeAndDocumented; } @@ -619,7 +652,8 @@ lookupImpl( MRDOCS_CHECK_OR(F.IsExplicitObjectMemberFunction == ref.IsExplicitObjectMemberFunction, matchRes); for (std::size_t i = 0; i < F.Params.size(); ++i) { - auto& lhsType = F.Params[i].Type; + Polymorphic const& lhsType = F.Params[i].Type; + MRDOCS_ASSERT(!lhsType.valueless_after_move()); auto& rhsType = ref.FunctionParameters[i]; MRDOCS_CHECK_OR(isDecayedEqual(lhsType, rhsType, context, *this), matchRes); } @@ -645,7 +679,7 @@ lookupImpl( { res = &member; matchLevel = memberMatchLevel; - // Early exit if the matchMatchLevel is the highest possible + // Early exit if the match level is the highest possible // for the component and the parsed reference if (matchLevel >= highestMatchLevel) { @@ -664,11 +698,11 @@ lookupImpl( report::trace(" Looking up in transparent contexts"); for (SymbolID const& memberID : memberIDs) { - Info const* memberPtr = find(memberID); + Symbol const* memberPtr = find(memberID); MRDOCS_CHECK_OR_CONTINUE(memberPtr); - Info const& member = *memberPtr; + Symbol const& member = *memberPtr; MRDOCS_CHECK_OR_CONTINUE(isTransparent(member)); - if (Info const* r = lookupImpl(member.id, component, ref, checkParameters)) + if (Symbol const* r = lookupImpl(member.id, component, ref, checkParameters)) { return r; } @@ -678,7 +712,7 @@ lookupImpl( return nullptr; } -std::pair +std::pair CorpusImpl:: lookupCacheGet( SymbolID const& context, @@ -703,7 +737,7 @@ CorpusImpl:: lookupCacheSet( SymbolID const& contextId, std::string_view name, - Info const* info) + Symbol const* info) { lookupCache_[contextId][std::string(name)] = info; } @@ -715,7 +749,7 @@ mrdocs::Expected> CorpusImpl:: build( std::shared_ptr const& config, - tooling::CompilationDatabase const& compilations) + clang::tooling::CompilationDatabase const& compilations) { using clock_type = std::chrono::steady_clock; auto start_time = clock_type::now(); @@ -732,7 +766,7 @@ build( // Create an execution context to store the // results of the AST traversal. // Any new Info objects will be added to the - // InfoSet in the execution context. + // SymbolSet in the execution context. InfoExecutionContext context(*config); // ------------------------------------------ @@ -761,8 +795,8 @@ build( && "createMrDocsFileSystem must return MrDocsFileSystem"); // Create a Clang Tool to run the action - auto PHCCOntainerOps = std::make_shared(); - tooling::ClangTool + auto PHCCOntainerOps = std::make_shared(); + clang::tooling::ClangTool Tool(compilations, { path }, std::move(PHCCOntainerOps), FS); Tool.setPrintErrorMessage(false); @@ -794,9 +828,9 @@ build( std::string shimPath = makeShimPathPlatformAbsolute(); FSConcrete->addVirtualFile(shimPath, shimContent); Tool.appendArgumentsAdjuster( - tooling::combineAdjusters( - tooling::getInsertArgumentAdjuster("-include"), - tooling::getInsertArgumentAdjuster(shimPath.data()))); + clang::tooling::combineAdjusters( + clang::tooling::getInsertArgumentAdjuster("-include"), + clang::tooling::getInsertArgumentAdjuster(shimPath.data()))); } // Run the action @@ -920,7 +954,7 @@ build( void CorpusImpl:: -qualifiedName(Info const& I, std::string& result) const +qualifiedName(Symbol const& I, std::string& result) const { result.clear(); if (!I.id || I.id == SymbolID::global) @@ -931,7 +965,7 @@ qualifiedName(Info const& I, std::string& result) const if (I.Parent && I.Parent != SymbolID::global) { - if (Info const* PI = find(I.Parent)) + if (Symbol const* PI = find(I.Parent)) { qualifiedName(*PI, result); result += "::"; @@ -952,7 +986,7 @@ qualifiedName(Info const& I, std::string& result) const void CorpusImpl:: qualifiedName( - Info const& I, + Symbol const& I, SymbolID const& context, std::string& result) const { @@ -972,7 +1006,7 @@ qualifiedName( !is_one_of(I.Parent, {SymbolID::global, context}) && I.id != context) { - if (Info const* PI = find(I.Parent)) + if (Symbol const* PI = find(I.Parent)) { qualifiedName(*PI, context, result); result += "::"; @@ -1040,12 +1074,12 @@ CorpusImpl::finalize() finalizer.build(); } - // Finalize javadoc + // Finalize documentation comments { - report::debug(" - Finalizing javadoc"); - JavadocFinalizer finalizer(*this); + report::debug(" - Finalizing documentation comments"); + DocCommentFinalizer finalizer(*this); finalizer.build(); } } -} // clang::mrdocs +} // mrdocs diff --git a/src/lib/CorpusImpl.hpp b/src/lib/CorpusImpl.hpp index c8d70d0cf..1b6dd772c 100644 --- a/src/lib/CorpusImpl.hpp +++ b/src/lib/CorpusImpl.hpp @@ -12,23 +12,23 @@ #ifndef MRDOCS_LIB_CORPUSIMPL_HPP #define MRDOCS_LIB_CORPUSIMPL_HPP -#include "lib/AST/ParseRef.hpp" -#include "ConfigImpl.hpp" -#include "lib/Metadata/InfoSet.hpp" -#include "lib/Support/Debug.hpp" -#include +#include +#include +#include +#include +#include #include #include #include -#include #include +#include +#include #include #include -#include #include -#include +#include -namespace clang::mrdocs { +namespace mrdocs { /** Implements the Corpus. @@ -45,21 +45,21 @@ class CorpusImpl final : public Corpus std::shared_ptr config_; // Info keyed on Symbol ID. - InfoSet info_; + SymbolSet info_; // Undocumented symbols - UndocumentedInfoSet undocumented_; + UndocumentedSymbolSet undocumented_; // Lookup cache // The key represents the context symbol ID. // The value is another map from the name to the Info. - std::map> lookupCache_; + std::map> lookupCache_; friend class Corpus; friend class BaseMembersFinalizer; friend class OverloadsFinalizer; friend class SortMembersFinalizer; - friend class JavadocFinalizer; + friend class DocCommentFinalizer; friend class NamespacesFinalizer; friend class DerivedFinalizer; @@ -88,10 +88,10 @@ class CorpusImpl final : public Corpus If the id does not exist, the behavior is undefined. */ - Info* + Symbol* find(SymbolID const& id) noexcept; - Info const* + Symbol const* find(SymbolID const& id) const noexcept override; /** Return a range of Info objects for the specified Symbol IDs. @@ -103,15 +103,15 @@ class CorpusImpl final : public Corpus return std::views::transform( range, - [this](SymbolID const& id) -> Info* + [this](SymbolID const& id) -> Symbol* { return this->find(id); }) | - std::views::filter([](Info const* info) + std::views::filter([](Symbol const* info) { return info != nullptr; }) | - std::views::transform([](Info* info) -> Info& + std::views::transform([](Symbol* info) -> Symbol& { return *info; }) | @@ -127,25 +127,25 @@ class CorpusImpl final : public Corpus return std::views::transform( range, - [this](SymbolID const& id) -> Info const* + [this](SymbolID const& id) -> Symbol const* { return this->find(id); }) | - std::views::filter([](Info const* info) + std::views::filter([](Symbol const* info) { return info != nullptr; }) | - std::views::transform([](Info const* info) -> Info const& + std::views::transform([](Symbol const* info) -> Symbol const& { return *info; }) | std::views::common; } - Expected> + Expected lookup(SymbolID const& context, std::string_view name) const override; - Expected> + Expected lookup(SymbolID const& context, std::string_view name); /** Build metadata for a set of translation units. @@ -169,16 +169,14 @@ class CorpusImpl final : public Corpus mrdocs::Expected> build( std::shared_ptr const& config, - tooling::CompilationDatabase const& compilations); + clang::tooling::CompilationDatabase const& compilations); void - qualifiedName( - Info const& I, + qualifiedName(Symbol const& I, std::string& result) const override; void - qualifiedName( - Info const& I, + qualifiedName(Symbol const& I, SymbolID const& context, std::string& result) const override; @@ -198,15 +196,14 @@ class CorpusImpl final : public Corpus template static - Expected> + Expected lookupImpl( Self&& self, SymbolID const& contextId, std::string_view name); template - static - Info const* + static Symbol const* lookupImpl( Self&& self, SymbolID const& context, @@ -214,14 +211,14 @@ class CorpusImpl final : public Corpus std::string_view name, bool cache); - Info const* + Symbol const* lookupImpl( SymbolID const& contextId, ParsedRefComponent const& component, ParsedRef const& ref, bool checkParameters) const; - std::pair + std::pair lookupCacheGet( SymbolID const& context, std::string_view name) const; @@ -230,13 +227,12 @@ class CorpusImpl final : public Corpus lookupCacheSet( SymbolID const& context, std::string_view name, - Info const* info); + Symbol const* info); void lookupCacheSet( SymbolID const&, - std::string_view, - Info const*) const + std::string_view, Symbol const*) const { // no-op when const } @@ -251,11 +247,11 @@ get( auto I = find(id); MRDOCS_ASSERT(I != nullptr); auto const t = static_cast(I); - if constexpr(! std::is_same_v) + if constexpr(! std::is_same_v) MRDOCS_ASSERT(t->Kind == T::kind_id); return *t; } -} // clang::mrdocs +} // mrdocs -#endif +#endif // MRDOCS_LIB_CORPUSIMPL_HPP diff --git a/src/lib/Diagnostics.hpp b/src/lib/Diagnostics.hpp index c3f6c0141..5f9499c95 100644 --- a/src/lib/Diagnostics.hpp +++ b/src/lib/Diagnostics.hpp @@ -8,16 +8,16 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_LIB_TOOL_DIAGNOSTICS_HPP -#define MRDOCS_LIB_TOOL_DIAGNOSTICS_HPP +#ifndef MRDOCS_LIB_DIAGNOSTICS_HPP +#define MRDOCS_LIB_DIAGNOSTICS_HPP -#include -#include #include +#include +#include #include #include -namespace clang { + namespace mrdocs { /** Diagnostic information accumulated during visitation. @@ -139,6 +139,6 @@ class Diagnostics }; } // mrdocs -} // clang -#endif + +#endif // MRDOCS_LIB_DIAGNOSTICS_HPP diff --git a/src/lib/Dom/Array.cpp b/src/lib/Dom/Array.cpp index c2df24665..3e395599e 100644 --- a/src/lib/Dom/Array.cpp +++ b/src/lib/Dom/Array.cpp @@ -10,7 +10,7 @@ #include -namespace clang { + namespace mrdocs { namespace dom { @@ -220,4 +220,4 @@ type_key() const noexcept } // dom } // mrdocs -} // clang + diff --git a/src/lib/Dom/Function.cpp b/src/lib/Dom/Function.cpp index 34baf5530..0ec509967 100644 --- a/src/lib/Dom/Function.cpp +++ b/src/lib/Dom/Function.cpp @@ -10,7 +10,7 @@ #include -namespace clang { + namespace mrdocs { namespace dom { @@ -89,4 +89,4 @@ type_key() const noexcept } // dom } // mrdocs -} // clang + diff --git a/src/lib/Dom/Object.cpp b/src/lib/Dom/Object.cpp index fcdab886a..bec43d3e7 100644 --- a/src/lib/Dom/Object.cpp +++ b/src/lib/Dom/Object.cpp @@ -8,13 +8,13 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include -#include #include #include +#include +#include #include -namespace clang { + namespace mrdocs { namespace dom { @@ -208,4 +208,4 @@ DefaultObjectImpl::exists(std::string_view key) const { } // dom } // mrdocs -} // clang + diff --git a/src/lib/Dom/String.cpp b/src/lib/Dom/String.cpp index 6e1b5d147..f14a7e1be 100644 --- a/src/lib/Dom/String.cpp +++ b/src/lib/Dom/String.cpp @@ -8,11 +8,11 @@ // Official repository: https://github.com/cppalliance/mrdocs // +#include #include #include -#include -namespace clang { + namespace mrdocs { namespace dom { @@ -139,4 +139,4 @@ data() const noexcept } // dom } // mrdocs -} // clang + diff --git a/src/lib/Dom/Value.cpp b/src/lib/Dom/Value.cpp index 7927f95b2..49457c72e 100644 --- a/src/lib/Dom/Value.cpp +++ b/src/lib/Dom/Value.cpp @@ -9,10 +9,10 @@ // #include -#include #include +#include + -namespace clang { namespace mrdocs { namespace dom { @@ -91,7 +91,7 @@ Value( Value&& other) noexcept : kind_(other.kind_) { - using enum clang::mrdocs::dom::Kind; + using enum mrdocs::dom::Kind; switch(kind_) { case Undefined: @@ -481,7 +481,7 @@ empty() const { switch(kind_) { - using enum clang::mrdocs::dom::Kind; + using enum mrdocs::dom::Kind; case Undefined: case Null: return true; @@ -508,7 +508,7 @@ size() const { switch(kind_) { - using enum clang::mrdocs::dom::Kind; + using enum mrdocs::dom::Kind; case Undefined: case Null: return 0; @@ -899,4 +899,4 @@ toString( } // dom } // mrdocs -} // clang + diff --git a/src/lib/Gen/adoc/AdocEscape.cpp b/src/lib/Gen/adoc/AdocEscape.cpp index ca00fe28d..29244a179 100644 --- a/src/lib/Gen/adoc/AdocEscape.cpp +++ b/src/lib/Gen/adoc/AdocEscape.cpp @@ -11,13 +11,14 @@ #include "AdocEscape.hpp" #include +#include -namespace clang::mrdocs::adoc { +namespace mrdocs::adoc { namespace { constexpr -std::optional +Optional HTMLNamedEntity(char const c) { // If c has a named entity, we use it @@ -96,4 +97,4 @@ AdocEscape(std::string_view str) { } -} // clang::mrdocs::adoc +} // mrdocs::adoc diff --git a/src/lib/Gen/adoc/AdocEscape.hpp b/src/lib/Gen/adoc/AdocEscape.hpp index 514b94109..f01cfbdd8 100644 --- a/src/lib/Gen/adoc/AdocEscape.hpp +++ b/src/lib/Gen/adoc/AdocEscape.hpp @@ -17,7 +17,7 @@ #include #include -namespace clang::mrdocs::adoc { +namespace mrdocs::adoc { /** Escape a string for use in AsciiDoc */ @@ -29,6 +29,6 @@ MRDOCS_DECL std::string AdocEscape(std::string_view str); -} // clang::mrdocs::adoc +} // mrdocs::adoc #endif diff --git a/src/lib/Gen/adoc/AdocGenerator.cpp b/src/lib/Gen/adoc/AdocGenerator.cpp index cdd7717a9..3a74512a4 100644 --- a/src/lib/Gen/adoc/AdocGenerator.cpp +++ b/src/lib/Gen/adoc/AdocGenerator.cpp @@ -13,7 +13,7 @@ #include "AdocEscape.hpp" #include -namespace clang::mrdocs { +namespace mrdocs { namespace adoc { void @@ -31,4 +31,4 @@ makeAdocGenerator() return std::make_unique(); } -} // clang::mrdocs +} // mrdocs diff --git a/src/lib/Gen/adoc/AdocGenerator.hpp b/src/lib/Gen/adoc/AdocGenerator.hpp index a604be9ca..382e231c5 100644 --- a/src/lib/Gen/adoc/AdocGenerator.hpp +++ b/src/lib/Gen/adoc/AdocGenerator.hpp @@ -14,10 +14,10 @@ #define MRDOCS_LIB_GEN_ADOC_ADOCGENERATOR_HPP #include -#include #include +#include -namespace clang::mrdocs::adoc { +namespace mrdocs::adoc { class AdocGenerator final : public hbs::HandlebarsGenerator @@ -46,6 +46,6 @@ class AdocGenerator final escape(OutputRef& os, std::string_view str) const override; }; -} // clang::mrdocs::adoc +} // mrdocs::adoc #endif diff --git a/src/lib/Gen/hbs/Builder.cpp b/src/lib/Gen/hbs/Builder.cpp index 7468013f0..8cb40bf46 100644 --- a/src/lib/Gen/hbs/Builder.cpp +++ b/src/lib/Gen/hbs/Builder.cpp @@ -10,14 +10,14 @@ // #include "Builder.hpp" -#include "lib/ConfigImpl.hpp" -#include -#include +#include #include #include #include +#include +#include + -namespace clang { namespace mrdocs { namespace lua { @@ -306,8 +306,7 @@ getRelPrefix(std::size_t depth) dom::Object Builder:: -createContext( - Info const& I) +createContext(Symbol const& I) { dom::Object ctx; ctx.set("symbol", domCorpus.get(I.id)); @@ -315,7 +314,7 @@ createContext( return ctx; } -template T> +template T> Expected Builder:: operator()(std::ostream& os, T const& I) @@ -347,9 +346,9 @@ operator()(std::ostream& os, T const& I) return callTemplate(os, wrapperFile, wrapperCtx); } -// Compile the Builder::operator() for each Info type -#define INFO(T) template Expected Builder::operator()(std::ostream&, T##Info const&); -#include +// Compile the Builder::operator() for each Symbol type +#define INFO(T) template Expected Builder::operator()(std::ostream&, T##Symbol const&); +#include Expected Builder:: @@ -436,4 +435,4 @@ commonTemplatesDir(std::string_view const subdir) const } // hbs } // mrdocs -} // clang + diff --git a/src/lib/Gen/hbs/Builder.hpp b/src/lib/Gen/hbs/Builder.hpp index e2cc3894f..3f64e6f8a 100644 --- a/src/lib/Gen/hbs/Builder.hpp +++ b/src/lib/Gen/hbs/Builder.hpp @@ -12,15 +12,15 @@ #ifndef MRDOCS_LIB_GEN_HBS_BUILDER_HPP #define MRDOCS_LIB_GEN_HBS_BUILDER_HPP -#include "HandlebarsCorpus.hpp" -#include "lib/Support/Radix.hpp" +#include +#include #include #include #include #include #include -namespace clang { + namespace mrdocs { namespace hbs { @@ -57,7 +57,7 @@ class Builder this function renders the wrapper template with the index template as the contents. */ - template T> + template T> Expected operator()(std::ostream& os, T const&); @@ -112,7 +112,7 @@ class Builder the section where the symbol is located. */ dom::Object - createContext(Info const& I); + createContext(Symbol const& I); /** Render a Handlebars template from the templates directory. */ @@ -126,6 +126,6 @@ class Builder } // hbs } // mrdocs -} // clang -#endif + +#endif // MRDOCS_LIB_GEN_HBS_BUILDER_HPP diff --git a/src/lib/Gen/hbs/HandlebarsCorpus.cpp b/src/lib/Gen/hbs/HandlebarsCorpus.cpp index 5e4314025..5b2970c5f 100644 --- a/src/lib/Gen/hbs/HandlebarsCorpus.cpp +++ b/src/lib/Gen/hbs/HandlebarsCorpus.cpp @@ -12,17 +12,15 @@ #include "HandlebarsCorpus.hpp" #include "VisitorHelpers.hpp" -#include -#include -#include #include #include +#include -namespace clang::mrdocs::hbs { +namespace mrdocs::hbs { dom::Object HandlebarsCorpus:: -construct(Info const& I) const +construct(Symbol const& I) const { dom::Object obj = this->DomCorpus::construct(I); if (shouldGenerate(I, getCorpus().config)) @@ -35,7 +33,7 @@ construct(Info const& I) const // If the URL is not available because it's a specialization // or dependency, we still want to generate the URL and anchor // for the primary template if it's part of the corpus. - if (Info const* primaryInfo = findAlternativeURLInfo(getCorpus(), I)) + if (Symbol const* primaryInfo = findAlternativeURLInfo(getCorpus(), I)) { MRDOCS_ASSERT(shouldGenerate(*primaryInfo, getCorpus().config)); obj.set("url", getURL(*primaryInfo)); @@ -46,7 +44,7 @@ construct(Info const& I) const std::string HandlebarsCorpus:: -getURL(Info const& I) const +getURL(Symbol const& I) const { bool const multipage = getCorpus().config->multipage; char const prefix = multipage ? '/' : '#'; @@ -61,4 +59,4 @@ getURL(Info const& I) const return href; } -} // clang::mrdocs::hbs +} // mrdocs::hbs diff --git a/src/lib/Gen/hbs/HandlebarsCorpus.hpp b/src/lib/Gen/hbs/HandlebarsCorpus.hpp index 3567d9590..4d11b5091 100644 --- a/src/lib/Gen/hbs/HandlebarsCorpus.hpp +++ b/src/lib/Gen/hbs/HandlebarsCorpus.hpp @@ -13,12 +13,12 @@ #define MRDOCS_LIB_GEN_HBS_HANDLEBARSCORPUS_HPP #include -#include "lib/Support/LegibleNames.hpp" -#include -#include +#include #include +#include +#include -namespace clang::mrdocs::hbs { +namespace mrdocs::hbs { /** A specialized DomCorpus for generating Handlebars values. @@ -42,7 +42,7 @@ class HandlebarsCorpus final : public DomCorpus @param corpus The base corpus. @param fileExtension The file extension for the generated files. - @param toStringFn The function to convert a Javadoc node to a string. + @param toStringFn The function to convert a DocComment node to a string. */ HandlebarsCorpus( Corpus const& corpus, @@ -59,7 +59,7 @@ class HandlebarsCorpus final : public DomCorpus @return A dom::Object representing the Info. */ dom::Object - construct(Info const& I) const override; + construct(Symbol const& I) const override; /** Get the cross-reference for the given Info. @@ -67,9 +67,9 @@ class HandlebarsCorpus final : public DomCorpus @return A string representing the cross-reference. */ std::string - getURL(Info const& I) const; + getURL(Symbol const& I) const; }; -} // clang::mrdocs::hbs +} // mrdocs::hbs -#endif +#endif // MRDOCS_LIB_GEN_HBS_HANDLEBARSCORPUS_HPP diff --git a/src/lib/Gen/hbs/HandlebarsGenerator.cpp b/src/lib/Gen/hbs/HandlebarsGenerator.cpp index fef070e24..7ba936a9a 100644 --- a/src/lib/Gen/hbs/HandlebarsGenerator.cpp +++ b/src/lib/Gen/hbs/HandlebarsGenerator.cpp @@ -11,25 +11,21 @@ // #include "HandlebarsGenerator.hpp" -#include "HandlebarsCorpus.hpp" #include "Builder.hpp" +#include "HandlebarsCorpus.hpp" #include "MultiPageVisitor.hpp" #include "SinglePageVisitor.hpp" - #include "TagfileWriter.hpp" -#include "lib/Support/RawOstream.hpp" - +#include +#include +#include #include #include #include - -#include -#include - #include #include -namespace clang::mrdocs::hbs { +namespace mrdocs::hbs { namespace { std::function @@ -217,4 +213,4 @@ escape(OutputRef& out, std::string_view str) const out << str; } -} // clang::mrdocs::hbs \ No newline at end of file +} // mrdocs::hbs \ No newline at end of file diff --git a/src/lib/Gen/hbs/HandlebarsGenerator.hpp b/src/lib/Gen/hbs/HandlebarsGenerator.hpp index 3248517c6..f077f400a 100644 --- a/src/lib/Gen/hbs/HandlebarsGenerator.hpp +++ b/src/lib/Gen/hbs/HandlebarsGenerator.hpp @@ -13,12 +13,12 @@ #define MRDOCS_LIB_GEN_HBS_HANDLEBARSGENERATOR_HPP #include +#include #include #include -#include #include -namespace clang::mrdocs { +namespace mrdocs { class OutputRef; @@ -61,6 +61,6 @@ class HandlebarsGenerator } // hbs -} // clang::mrdocs +} // mrdocs #endif diff --git a/src/lib/Gen/hbs/MultiPageVisitor.cpp b/src/lib/Gen/hbs/MultiPageVisitor.cpp index 09bb15a85..5096b3f44 100644 --- a/src/lib/Gen/hbs/MultiPageVisitor.cpp +++ b/src/lib/Gen/hbs/MultiPageVisitor.cpp @@ -11,13 +11,13 @@ #include "MultiPageVisitor.hpp" #include "VisitorHelpers.hpp" -#include #include #include +#include -namespace clang::mrdocs::hbs { +namespace mrdocs::hbs { -template T> +template T> void MultiPageVisitor:: operator()(T const& I) @@ -68,12 +68,12 @@ operator()(T const& I) // =================================== // Traverse the symbol members // =================================== - Corpus::TraverseOptions opts = {.skipInherited = std::same_as}; + Corpus::TraverseOptions opts = {.skipInherited = std::same_as}; corpus_.traverse(opts, I, *this); }); } -#define INFO(T) template void MultiPageVisitor::operator()(T##Info const&); -#include +#define INFO(T) template void MultiPageVisitor::operator()(T##Symbol const&); +#include -} // clang::mrdocs::hbs +} // mrdocs::hbs diff --git a/src/lib/Gen/hbs/MultiPageVisitor.hpp b/src/lib/Gen/hbs/MultiPageVisitor.hpp index d2dfd7df2..61eab75de 100644 --- a/src/lib/Gen/hbs/MultiPageVisitor.hpp +++ b/src/lib/Gen/hbs/MultiPageVisitor.hpp @@ -12,16 +12,16 @@ #ifndef MRDOCS_LIB_GEN_HBS_MULTIPAGEVISITOR_HPP #define MRDOCS_LIB_GEN_HBS_MULTIPAGEVISITOR_HPP -#include "Builder.hpp" +#include +#include #include -#include +#include #include #include #include #include -#include -namespace clang::mrdocs::hbs { +namespace mrdocs::hbs { /** Visitor which emites a multi-page reference. */ @@ -49,7 +49,7 @@ class MultiPageVisitor respective tasks are also pushed to the executor group. */ - template T> + template T> void operator()(T const& I); @@ -62,6 +62,6 @@ class MultiPageVisitor } }; -} // clang::mrdocs::hbs +} // mrdocs::hbs #endif diff --git a/src/lib/Gen/hbs/SinglePageVisitor.cpp b/src/lib/Gen/hbs/SinglePageVisitor.cpp index 9c9fc9e8a..28cd26a3c 100644 --- a/src/lib/Gen/hbs/SinglePageVisitor.cpp +++ b/src/lib/Gen/hbs/SinglePageVisitor.cpp @@ -14,9 +14,9 @@ #include #include -namespace clang::mrdocs::hbs { +namespace mrdocs::hbs { -template T> +template T> void SinglePageVisitor:: operator()(T const& I) @@ -38,12 +38,12 @@ operator()(T const& I) } }); } - Corpus::TraverseOptions opts = {.skipInherited = std::same_as}; + Corpus::TraverseOptions opts = {.skipInherited = std::same_as}; corpus_.traverse(opts, I, *this); } -#define INFO(T) template void SinglePageVisitor::operator()(T##Info const&); -#include +#define INFO(T) template void SinglePageVisitor::operator()(T##Symbol const&); +#include // pageNumber is zero-based void @@ -97,4 +97,4 @@ writePage( } } -} // clang::mrdocs::hbs +} // mrdocs::hbs diff --git a/src/lib/Gen/hbs/SinglePageVisitor.hpp b/src/lib/Gen/hbs/SinglePageVisitor.hpp index 797da308a..ac52b7ea3 100644 --- a/src/lib/Gen/hbs/SinglePageVisitor.hpp +++ b/src/lib/Gen/hbs/SinglePageVisitor.hpp @@ -12,14 +12,14 @@ #ifndef MRDOCS_LIB_GEN_HBS_SINGLEPAGEVISITOR_HPP #define MRDOCS_LIB_GEN_HBS_SINGLEPAGEVISITOR_HPP -#include "Builder.hpp" +#include #include #include #include #include #include -namespace clang::mrdocs::hbs { +namespace mrdocs::hbs { /** Visitor which writes everything to a single page. */ @@ -31,7 +31,7 @@ class SinglePageVisitor std::size_t numSymbols_ = 0; std::mutex mutex_; std::size_t topSymbol_ = 0; - std::vector> symbols_; + std::vector> symbols_; void writePage(std::string pageText, std::size_t symbolIdx); public: @@ -51,10 +51,10 @@ class SinglePageVisitor respective tasks are also pushed to the executor group. */ - template T> + template T> void operator()(T const& I); }; -} // clang::mrdocs::hbs +} // mrdocs::hbs #endif diff --git a/src/lib/Gen/hbs/TagfileWriter.cpp b/src/lib/Gen/hbs/TagfileWriter.cpp index 7496de64c..57e3c25c9 100644 --- a/src/lib/Gen/hbs/TagfileWriter.cpp +++ b/src/lib/Gen/hbs/TagfileWriter.cpp @@ -9,17 +9,15 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include "lib/Gen/xml/CXXTags.hpp" #include "TagfileWriter.hpp" -#include "lib/Gen/hbs/VisitorHelpers.hpp" -#include "lib/ConfigImpl.hpp" -#include "lib/Support/RawOstream.hpp" - -#include - +#include +#include +#include +#include #include +#include -namespace clang::mrdocs { +namespace mrdocs { //------------------------------------------------ // @@ -79,7 +77,7 @@ void TagfileWriter:: operator()(T const& I) { - if constexpr (std::derived_from) + if constexpr (std::derived_from) { if (!hbs::shouldGenerate(I, corpus_.getCorpus().config)) { @@ -102,24 +100,24 @@ operator()(T const& I) } } -#define INFO(Type) template void TagfileWriter::operator()(Type##Info const&); -#include +#define INFO(Type) template void TagfileWriter::operator()(Type##Symbol const&); +#include void TagfileWriter:: writeNamespace( - NamespaceInfo const& I) + NamespaceSymbol const& I) { // Check if this namespace contains only other namespaces bool onlyNamespaces = true; - corpus_->traverse(I, [&](Info const& U) + corpus_->traverse(I, [&](Symbol const& U) { if (!hbs::shouldGenerate(U, corpus_.getCorpus().config)) { return; } - if (U.Kind != InfoKind::Namespace) + if (U.Kind != SymbolKind::Namespace) { onlyNamespaces = false; } @@ -199,9 +197,10 @@ writeClassLike( void TagfileWriter:: -writeFunctionMember(FunctionInfo const& I) +writeFunctionMember(FunctionSymbol const& I) { tags_.open("member", {{"kind", "function"}}); + MRDOCS_ASSERT(!I.ReturnType.valueless_after_move()); tags_.write("type", toString(*I.ReturnType)); tags_.write("name", I.Name); auto [anchorFile, anchor] = generateFileAndAnchor(I); @@ -210,9 +209,13 @@ writeFunctionMember(FunctionInfo const& I) std::string arglist = "("; for(auto const& J : I.Params) { + MRDOCS_ASSERT(!J.Type.valueless_after_move()); arglist += toString(*J.Type); - arglist += " "; - arglist += *J.Name; + if (J.Name) + { + arglist += " "; + arglist += *J.Name; + } arglist += ", "; } if (arglist.size() > 2) { @@ -280,4 +283,4 @@ generateFileAndAnchor(T const& I) return {url.substr(0, pos), url.substr(pos + 1)}; } -} // clang::mrdocs \ No newline at end of file +} // mrdocs \ No newline at end of file diff --git a/src/lib/Gen/hbs/TagfileWriter.hpp b/src/lib/Gen/hbs/TagfileWriter.hpp index c571f5a6a..43b82ddc1 100644 --- a/src/lib/Gen/hbs/TagfileWriter.hpp +++ b/src/lib/Gen/hbs/TagfileWriter.hpp @@ -9,18 +9,17 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_LIB_TAGFILEWRITER_HPP -#define MRDOCS_LIB_TAGFILEWRITER_HPP +#ifndef MRDOCS_LIB_GEN_HBS_TAGFILEWRITER_HPP +#define MRDOCS_LIB_GEN_HBS_TAGFILEWRITER_HPP -#include "lib/Gen/xml/XMLTags.hpp" -#include "lib/Gen/hbs/HandlebarsCorpus.hpp" +#include +#include #include #include - #include #include -namespace clang { + namespace mrdocs { struct SimpleWriterTag {}; //Tag dispatch for simple writers @@ -117,14 +116,14 @@ class TagfileWriter // Write // ================================================== void - writeNamespace(NamespaceInfo const&); + writeNamespace(NamespaceSymbol const&); template void writeClassLike(T const& I); void - writeFunctionMember(FunctionInfo const& I); + writeFunctionMember(FunctionSymbol const& I); // ================================================== // URLs @@ -140,6 +139,6 @@ class TagfileWriter }; } // mrdocs -} // clang -#endif \ No newline at end of file + +#endif // MRDOCS_LIB_GEN_HBS_TAGFILEWRITER_HPP \ No newline at end of file diff --git a/src/lib/Gen/hbs/VisitorHelpers.cpp b/src/lib/Gen/hbs/VisitorHelpers.cpp index 5464e1f40..965ca8c57 100644 --- a/src/lib/Gen/hbs/VisitorHelpers.cpp +++ b/src/lib/Gen/hbs/VisitorHelpers.cpp @@ -10,16 +10,16 @@ #include "VisitorHelpers.hpp" #include -#include #include +#include #include -namespace clang::mrdocs::hbs { +namespace mrdocs::hbs { bool -shouldGenerate(Info const& I, Config const& config) +shouldGenerate(Symbol const& I, Config const& config) { - if (I.isEnumConstant()) + if (I.isEnumConstant() && !config->showEnumConstants) { return false; } @@ -44,19 +44,21 @@ shouldGenerate(Info const& I, Config const& config) namespace { // Resolve a typedef to its underlying Info type -Info const* -resolveTypedef(Corpus const& c, Info const& I) +Symbol const* +resolveTypedef(Corpus const& c, Symbol const& I) { if (I.isTypedef()) { auto const& TI = I.asTypedef(); - Polymorphic const& T = TI.Type; - MRDOCS_CHECK_OR(T && T->Kind == TypeKind::Named, &I); - auto const& NT = dynamic_cast(*T); + MRDOCS_CHECK_OR(TI.Type, &I); + MRDOCS_ASSERT(!TI.Type.valueless_after_move()); + Type const& T = *TI.Type; + MRDOCS_CHECK_OR(T.Kind == TypeKind::Named, &I); + auto const& NT = T.asNamed(); MRDOCS_CHECK_OR(NT.Name, &I); - Info const* resolved = c.find(NT.Name->id); + Symbol const* resolved = c.find(NT.Name->id); MRDOCS_CHECK_OR(resolved, &I); - if (resolved->Kind == InfoKind::Typedef) + if (resolved->Kind == SymbolKind::Typedef) { return resolveTypedef(c, *resolved); } @@ -67,15 +69,15 @@ resolveTypedef(Corpus const& c, Info const& I) /* Look for an equivalent symbol in the parent Info */ -Info const* -findPrimarySiblingWithUrl(Corpus const& c, Info const& I, Info const& parent) +Symbol const* +findPrimarySiblingWithUrl(Corpus const& c, Symbol const& I, Symbol const& parent) { // Look for the primary sibling in the parent scope MRDOCS_CHECK_OR(parent, nullptr); return visit(parent, [&](InfoTy const& U) - -> Info const* + -> Symbol const* { - if constexpr (InfoParent) + if constexpr (SymbolParent) { namespace stdv = std::ranges::views; auto sameNameSiblings = @@ -84,11 +86,11 @@ findPrimarySiblingWithUrl(Corpus const& c, Info const& I, Info const& parent) { return c.find(siblingID); }) | - stdv::filter([&](Info const* sibling) + stdv::filter([&](Symbol const* sibling) { return sibling && sibling->Name == I.Name; }); - for (Info const* sibling: sameNameSiblings) + for (Symbol const* sibling: sameNameSiblings) { if (!sibling || !shouldGenerate(*sibling, c.config)) @@ -99,7 +101,7 @@ findPrimarySiblingWithUrl(Corpus const& c, Info const& I, Info const& parent) { if constexpr (requires { V.Template; }) { - std::optional const& Template = V.Template; + Optional const& Template = V.Template; MRDOCS_CHECK_OR(Template, false); return !Template->Params.empty() && Template->Args.empty(); } @@ -115,8 +117,8 @@ findPrimarySiblingWithUrl(Corpus const& c, Info const& I, Info const& parent) }); } -Info const* -findPrimarySiblingWithUrl(Corpus const& c, Info const& I); +Symbol const* +findPrimarySiblingWithUrl(Corpus const& c, Symbol const& I); /* Find the parent and look for equivalent symbol in the parent @@ -128,12 +130,12 @@ findPrimarySiblingWithUrl(Corpus const& c, Info const& I); we look for a symbol equivalent to the parent, and then look for an equivalent symbol in the parent. */ -Info const* -findDirectPrimarySiblingWithUrl(Corpus const& c, Info const& I) +Symbol const* +findDirectPrimarySiblingWithUrl(Corpus const& c, Symbol const& I) { // If the parent is a scope, look for a primary sibling // in the parent scope for which we want to generate the URL - Info const* parent = c.find(I.Parent); + Symbol const* parent = c.find(I.Parent); MRDOCS_CHECK_OR(parent, nullptr); if (!shouldGenerate(*parent, c.config)) { @@ -148,8 +150,8 @@ findDirectPrimarySiblingWithUrl(Corpus const& c, Info const& I) This function will resolve typedefs and look for an equivalent symbol in the parent scope for which we want to generate the URL. */ -Info const* -findResolvedPrimarySiblingWithUrl(Corpus const& c, Info const& I) +Symbol const* +findResolvedPrimarySiblingWithUrl(Corpus const& c, Symbol const& I) { // Check if this info is a specialization or a typedef to // a specialization, otherwise there's nothing to resolve @@ -158,7 +160,7 @@ findResolvedPrimarySiblingWithUrl(Corpus const& c, Info const& I) // The symbol is a specialization if constexpr (requires { U.Template; }) { - std::optional const& Template = U.Template; + Optional const& Template = U.Template; if (Template && !Template->Args.empty()) { @@ -166,13 +168,15 @@ findResolvedPrimarySiblingWithUrl(Corpus const& c, Info const& I) } } // The symbol is a typedef to a specialization - if constexpr (std::same_as) + if constexpr (std::same_as) { - Polymorphic const& T = U.Type; - MRDOCS_CHECK_OR(T && T->Kind == TypeKind::Named, false); - auto const& NT = dynamic_cast(*T); + auto const& TOpt = U.Type; + MRDOCS_ASSERT(!TOpt.valueless_after_move()); + Type const& T = *TOpt; + MRDOCS_CHECK_OR(T.Kind == TypeKind::Named, false); + auto const& NT = T.asNamed(); MRDOCS_CHECK_OR(NT.Name, false); - MRDOCS_CHECK_OR(NT.Name->Kind == NameKind::Specialization, false); + MRDOCS_CHECK_OR(NT.Name->isSpecialization(), false); return true; } return false; @@ -181,14 +185,14 @@ findResolvedPrimarySiblingWithUrl(Corpus const& c, Info const& I) // Find the parent scope containing the primary sibling // for which we want to generate the URL - Info const* parent = c.find(I.Parent); + Symbol const* parent = c.find(I.Parent); MRDOCS_CHECK_OR(parent, nullptr); // If the parent is a typedef, resolve it // so we can iterate the members of this scope. // We can't find siblings in a typedef because // it's not a scope. - if (parent->Kind == InfoKind::Typedef) + if (parent->Kind == SymbolKind::Typedef) { parent = resolveTypedef(c, *parent); MRDOCS_CHECK_OR(parent, nullptr); @@ -207,10 +211,10 @@ findResolvedPrimarySiblingWithUrl(Corpus const& c, Info const& I) return findPrimarySiblingWithUrl(c, I, *parent); } -Info const* -findPrimarySiblingWithUrl(Corpus const& c, Info const& I) +Symbol const* +findPrimarySiblingWithUrl(Corpus const& c, Symbol const& I) { - if (Info const* primary = findDirectPrimarySiblingWithUrl(c, I)) + if (Symbol const* primary = findDirectPrimarySiblingWithUrl(c, I)) { return primary; } @@ -230,10 +234,10 @@ findPrimarySiblingWithUrl(Corpus const& c, Info const& I) is looking for a URL for a symbol. */ -Info const* -findParentWithUrl(Corpus const& c, Info const& I) +Symbol const* +findParentWithUrl(Corpus const& c, Symbol const& I) { - Info const* parent = c.find(I.Parent); + Symbol const* parent = c.find(I.Parent); MRDOCS_CHECK_OR(parent, nullptr); MRDOCS_CHECK_OR(!parent->isNamespace(), nullptr); if (shouldGenerate(*parent, c.config)) @@ -247,14 +251,14 @@ findParentWithUrl(Corpus const& c, Info const& I) } } -Info const* -findAlternativeURLInfo(Corpus const& c, Info const& I) +Symbol const* +findAlternativeURLInfo(Corpus const& c, Symbol const& I) { - if (Info const* primary = findPrimarySiblingWithUrl(c, I)) + if (Symbol const* primary = findPrimarySiblingWithUrl(c, I)) { return primary; } return findParentWithUrl(c, I); } -} // clang::mrdocs::hbs +} // mrdocs::hbs diff --git a/src/lib/Gen/hbs/VisitorHelpers.hpp b/src/lib/Gen/hbs/VisitorHelpers.hpp index 8a1341ae1..48ba983b0 100644 --- a/src/lib/Gen/hbs/VisitorHelpers.hpp +++ b/src/lib/Gen/hbs/VisitorHelpers.hpp @@ -11,10 +11,11 @@ #ifndef MRDOCS_LIB_GEN_HBS_VISITORHELPERS_HPP #define MRDOCS_LIB_GEN_HBS_VISITORHELPERS_HPP -#include #include +#include +#include -namespace clang::mrdocs::hbs { +namespace mrdocs::hbs { /** @brief Determine if the generator should generate a page for this Info. @@ -23,7 +24,7 @@ namespace clang::mrdocs::hbs { */ MRDOCS_DECL bool -shouldGenerate(Info const& I, Config const& config); +shouldGenerate(Symbol const& I, Config const& config); /** Find an Info type whose URL we can use for the specified Info @@ -40,9 +41,9 @@ shouldGenerate(Info const& I, Config const& config); return nullptr. */ MRDOCS_DECL -Info const* -findAlternativeURLInfo(Corpus const& c, Info const& I); +Symbol const* +findAlternativeURLInfo(Corpus const& c, Symbol const& I); -} // clang::mrdocs::hbs +} // mrdocs::hbs #endif diff --git a/src/lib/Gen/html/HTMLGenerator.cpp b/src/lib/Gen/html/HTMLGenerator.cpp index 1368cf3c5..f2cfe4ebc 100644 --- a/src/lib/Gen/html/HTMLGenerator.cpp +++ b/src/lib/Gen/html/HTMLGenerator.cpp @@ -12,7 +12,7 @@ #include "HTMLGenerator.hpp" #include -namespace clang::mrdocs { +namespace mrdocs { namespace html { void @@ -32,4 +32,4 @@ makeHTMLGenerator() return std::make_unique(); } -} // clang::mrdocs +} // mrdocs diff --git a/src/lib/Gen/html/HTMLGenerator.hpp b/src/lib/Gen/html/HTMLGenerator.hpp index 0b66f532b..5d9594419 100644 --- a/src/lib/Gen/html/HTMLGenerator.hpp +++ b/src/lib/Gen/html/HTMLGenerator.hpp @@ -14,10 +14,10 @@ #define MRDOCS_LIB_GEN_HTML_HTMLGENERATOR_HPP #include -#include #include +#include -namespace clang::mrdocs::html { +namespace mrdocs::html { class HTMLGenerator final : public hbs::HandlebarsGenerator @@ -45,6 +45,6 @@ class HTMLGenerator final escape(OutputRef& os, std::string_view str) const override; }; -} // clang::mrdocs::html +} // mrdocs::html #endif diff --git a/src/lib/Gen/xml/CXXTags.cpp b/src/lib/Gen/xml/CXXTags.cpp index a5ea3bbfd..c7c55f3ff 100644 --- a/src/lib/Gen/xml/CXXTags.cpp +++ b/src/lib/Gen/xml/CXXTags.cpp @@ -10,21 +10,21 @@ // #include "CXXTags.hpp" -#include -#include +#include +#include #include -namespace clang::mrdocs::xml { +namespace mrdocs::xml { std::string -getDefaultTagName(Info const& I) noexcept +getDefaultTagName(Symbol const& I) noexcept { switch(I.Kind) { #define INFO(Type) \ - case InfoKind::Type: \ + case SymbolKind::Type: \ return toKebabCase(#Type) + "TagName"; -#include +#include default: break; } @@ -33,12 +33,12 @@ getDefaultTagName(Info const& I) noexcept std::string -getTagName(Info const& I) noexcept +getTagName(Symbol const& I) noexcept { switch(I.Kind) { - case InfoKind::Record: - switch(static_cast(I).KeyKind) + case SymbolKind::Record: + switch(static_cast(I).KeyKind) { case RecordKeyKind::Class: return classTagName; case RecordKeyKind::Struct: return structTagName; @@ -47,8 +47,8 @@ getTagName(Info const& I) noexcept break; } break; - case InfoKind::Typedef: - if (static_cast(I).IsUsing) + case SymbolKind::Typedef: + if (static_cast(I).IsUsing) { return "namespace"; } @@ -62,4 +62,4 @@ getTagName(Info const& I) noexcept MRDOCS_UNREACHABLE(); } -} // clang::mrdocs::xml +} // mrdocs::xml diff --git a/src/lib/Gen/xml/CXXTags.hpp b/src/lib/Gen/xml/CXXTags.hpp index a9d74a7d0..66f6f74d7 100644 --- a/src/lib/Gen/xml/CXXTags.hpp +++ b/src/lib/Gen/xml/CXXTags.hpp @@ -13,9 +13,10 @@ #ifndef MRDOCS_LIB_GEN_XML_CXXTAGS_HPP #define MRDOCS_LIB_GEN_XML_CXXTAGS_HPP -#include "XMLTags.hpp" -#include +#include #include +#include +#include #include /* @@ -24,7 +25,7 @@ in the MRDOCS DTD XML schema. */ -namespace clang::mrdocs::xml { +namespace mrdocs::xml { constexpr auto accessTagName = "access"; constexpr auto attributeTagName = "attr"; @@ -32,7 +33,7 @@ constexpr auto baseTagName = "base"; constexpr auto bitfieldTagName = "bitfield"; constexpr auto classTagName = "class"; constexpr auto dataMemberTagName = "field"; -constexpr auto javadocTagName = "doc"; +constexpr auto docTagName = "doc"; constexpr auto paramTagName = "param"; constexpr auto returnTagName = "return"; constexpr auto deducedTagName = "deduced"; @@ -120,13 +121,11 @@ writeTemplateArg(TArg const& I, XMLTags& tags); inline void -writeType( - TypeInfo const& I, +writeType(Type const& I, XMLTags& tags, std::string_view type_tag = "type") { - visit(I, [&]( - T const& t) + visit(I, [&](T const& t) { Attributes attrs = { { "class", toString(T::kind_id), @@ -134,20 +133,18 @@ writeType( { "is-pack", "1", t.IsPackExpansion } }; - // KRYSTIAN FIXME: parent should is a type itself + // KRYSTIAN FIXME: parent should be a type itself if constexpr(requires { t.ParentType; }) { - if(t.ParentType) - attrs.push({"parent", toString(*t.ParentType)}); + MRDOCS_ASSERT(!t.ParentType.valueless_after_move()); + attrs.push({ "parent", toString(*t.ParentType) }); } if constexpr(T::isNamed()) { - if(t.Name) - { - attrs.push({t.Name->id}); - attrs.push({"name", toString(*t.Name)}); - } + MRDOCS_ASSERT(!t.Name.valueless_after_move()); + attrs.push({t.Name->id}); + attrs.push({"name", toString(*t.Name)}); } std::string cvQualifiers; @@ -184,9 +181,13 @@ writeType( if constexpr(T::isAuto()) { - attrs.push({"keyword", toString(t.Keyword)}); - if(t.Constraint) - attrs.push({"constraint", toString(*t.Constraint)}); + AutoType const& at = t; + attrs.push({"keyword", toString(at.Keyword)}); + if(at.Constraint) + { + MRDOCS_ASSERT(!at.Constraint->valueless_after_move()); + attrs.push({"constraint", toString(**t.Constraint)}); + } } if constexpr(T::isFunction()) @@ -197,7 +198,7 @@ writeType( if(t.RefQualifier != ReferenceKind::None) attrs.push({"ref-qualifier", toString(t.RefQualifier)}); - // KRYSTIAN TODO: TypeInfo should use ExceptionInfo! + // KRYSTIAN TODO: Type should use ExceptionInfo! if(auto spec = toString(t.ExceptionSpec); ! spec.empty()) attrs.push({"exception-spec", spec}); } @@ -215,37 +216,75 @@ writeType( if constexpr(requires { t.PointeeType; }) { + Polymorphic const& pointee = t.PointeeType; + MRDOCS_ASSERT(!pointee.valueless_after_move()); writeType(*t.PointeeType, tags, "pointee-type"); } if constexpr(T::isArray()) { + ArrayType const& at = t; + MRDOCS_ASSERT(!at.ElementType.valueless_after_move()); writeType(*t.ElementType, tags, "element-type"); } if constexpr(T::isFunction()) { + FunctionType const& ft = t; + MRDOCS_ASSERT(!ft.ReturnType.valueless_after_move()); writeType(*t.ReturnType, tags, "return-type"); - for(auto const& p : t.ParamTypes) + for (auto const& p: t.ParamTypes) + { writeType(*p, tags, "param-type"); + } } - tags.close(type_tag); }); } + inline void writeType( - Polymorphic const& type, + Name const& I, + XMLTags& tags, + std::string_view type_tag = "type") +{ + Attributes attrs = { + { "class", toString(TypeKind::Named), false }, + { "is-pack", "1", false } + }; + + attrs.push({I.id}); + attrs.push({"name", I.Identifier}); + + tags.write(type_tag, {}, std::move(attrs)); +} + +inline +void +writeType( + Polymorphic const& type, XMLTags& tags) { - if(! type) - return; + MRDOCS_ASSERT(!type.valueless_after_move()); writeType(*type, tags); } -inline void writeReturnType(TypeInfo const& I, XMLTags& tags) +inline +void +writeType( + Optional> const& type, + XMLTags& tags) +{ + if (type) + { + writeType(*type, tags); + } +} + + +inline void writeReturnType(Type const& I, XMLTags& tags) { // KRYSTIAN NOTE: we don't *have* to do this... if (toString(I) == "void") @@ -260,8 +299,8 @@ inline void writeReturnType(TypeInfo const& I, XMLTags& tags) inline void writeParam(Param const& P, XMLTags& tags) { tags.open(paramTagName, { - { "name", *P.Name, P.Name.has_value() }, - { "default", *P.Default, P.Default.has_value() }, + { "name", P.Name.has_value() ? *P.Name : "", P.Name.has_value() }, + { "default", P.Default.has_value() ? *P.Default : "", P.Default.has_value() }, }); writeType(*P.Type, tags); tags.close(paramTagName); @@ -269,67 +308,73 @@ inline void writeParam(Param const& P, XMLTags& tags) inline void writeTemplateParam(TParam const& I, XMLTags& tags) { - visit(I, [&](T const& P) + visit(I, [&](T const& P) { + TParam const & tp = P; + Attributes attrs = { + { "name", tp.Name, !tp.Name.empty() }, + { "class", toString(T::kind_id) } + }; + + if constexpr (T::isConstant()) { - Attributes attrs = { - {"name", P.Name, ! P.Name.empty()}, - {"class", toString(T::kind_id)} - }; - - if constexpr(T::isNonType()) - attrs.push({"type", toString(*P.Type)}); + ConstantTParam const& nt = P; + MRDOCS_ASSERT(!nt.Type.valueless_after_move()); + attrs.push({ "type", toString(*nt.Type) }); + } - if(P.Default) - attrs.push({"default", toString(*P.Default)}); + if (tp.Default) + { + MRDOCS_ASSERT(!tp.Default->valueless_after_move()); + attrs.push({ "default", toString(**P.Default) }); + } - if constexpr(T::isTemplate()) - { - tags.open(tparamTagName, - std::move(attrs)); - for(auto const& tparam : P.Params) - writeTemplateParam(*tparam, tags); - tags.close(tparamTagName); - } - else + if constexpr (T::isTemplate()) + { + tags.open(tparamTagName, std::move(attrs)); + for (auto const& tparam: P.Params) { - tags.write(tparamTagName, {}, - std::move(attrs)); + writeTemplateParam(*tparam, tags); } - }); + tags.close(tparamTagName); + } else + { + tags.write(tparamTagName, {}, std::move(attrs)); + } + }); } inline void writeTemplateArg(TArg const& I, XMLTags& tags) { - visit(I, [&](T const& A) - { - Attributes attrs = { - {"class", toString(T::kind_id)} - }; + visit(I, [&](T const& A) { + Attributes attrs = { + { "class", toString(T::kind_id) } + }; - if constexpr(T::isType()) - { - attrs.push({"type", toString(*A.Type)}); - } - if constexpr(T::isNonType()) - { - attrs.push({"value", A.Value.Written}); - } - if constexpr(T::isTemplate()) - { - attrs.push({"name", A.Name}); - attrs.push({A.Template}); - } + if constexpr (T::isType()) + { + TypeTArg const& at = A; + MRDOCS_ASSERT(!at.Type.valueless_after_move()); + attrs.push({ "type", toString(*A.Type) }); + } + if constexpr (T::isConstant()) + { + attrs.push({ "value", A.Value.Written }); + } + if constexpr (T::isTemplate()) + { + attrs.push({ "name", A.Name }); + attrs.push({ A.Template }); + } - tags.write(targTagName, {}, - std::move(attrs)); - }); + tags.write(targTagName, {}, std::move(attrs)); + }); } /** Return the xml tag name for the Info. */ std::string -getTagName(Info const& I) noexcept; +getTagName(Symbol const& I) noexcept; -} // clang::mrdocs::xml +} // mrdocs::xml #endif diff --git a/src/lib/Gen/xml/XMLGenerator.cpp b/src/lib/Gen/xml/XMLGenerator.cpp index d4b5b0a90..2d42cd8d3 100644 --- a/src/lib/Gen/xml/XMLGenerator.cpp +++ b/src/lib/Gen/xml/XMLGenerator.cpp @@ -11,12 +11,12 @@ #include "XMLGenerator.hpp" #include "XMLWriter.hpp" -#include "lib/Support/Radix.hpp" -#include "lib/Support/RawOstream.hpp" -#include +#include +#include #include +#include + -namespace clang { namespace mrdocs { namespace xml { @@ -42,4 +42,4 @@ makeXMLGenerator() } } // mrdocs -} // clang + diff --git a/src/lib/Gen/xml/XMLGenerator.hpp b/src/lib/Gen/xml/XMLGenerator.hpp index 57a45b58d..32e4cd21d 100644 --- a/src/lib/Gen/xml/XMLGenerator.hpp +++ b/src/lib/Gen/xml/XMLGenerator.hpp @@ -14,10 +14,10 @@ #include #include -#include +#include #include -namespace clang::mrdocs::xml { +namespace mrdocs::xml { //------------------------------------------------ @@ -47,6 +47,6 @@ struct XMLGenerator : Generator Corpus const& corpus) const override; }; -} // clang::mrdocs::xml +} // mrdocs::xml #endif diff --git a/src/lib/Gen/xml/XMLTags.cpp b/src/lib/Gen/xml/XMLTags.cpp index 2885bdf12..6dedad694 100644 --- a/src/lib/Gen/xml/XMLTags.cpp +++ b/src/lib/Gen/xml/XMLTags.cpp @@ -10,10 +10,10 @@ // #include "XMLTags.hpp" -#include "lib/Support/Radix.hpp" #include +#include + -namespace clang { namespace mrdocs { namespace xml { @@ -210,4 +210,4 @@ nest(int levels) } // xml } // mrdocs -} // clang + diff --git a/src/lib/Gen/xml/XMLTags.hpp b/src/lib/Gen/xml/XMLTags.hpp index 6dbc01247..85ae67f4e 100644 --- a/src/lib/Gen/xml/XMLTags.hpp +++ b/src/lib/Gen/xml/XMLTags.hpp @@ -12,22 +12,22 @@ #ifndef MRDOCS_LIB_GEN_XML_XMLTAGS_HPP #define MRDOCS_LIB_GEN_XML_XMLTAGS_HPP -#include -#include +#include +#include +#include +#include #include #include #include -#include -#include -#include -#include +#include +#include /* Object for assisting with generating XML tags and correctly escaped strings */ -namespace clang::mrdocs::xml { +namespace mrdocs::xml { class jit_indenter; @@ -113,7 +113,7 @@ struct Attribute //------------------------------------------------ -/** An vector of zero or more XML attributes. +/** A vector of zero or more XML attributes. */ struct Attributes { @@ -203,6 +203,6 @@ class XMLTags void nest(int levels); }; -} // clang::mrdocs::xml +} // mrdocs::xml #endif diff --git a/src/lib/Gen/xml/XMLWriter.cpp b/src/lib/Gen/xml/XMLWriter.cpp index d39f58345..3aebf8c73 100644 --- a/src/lib/Gen/xml/XMLWriter.cpp +++ b/src/lib/Gen/xml/XMLWriter.cpp @@ -6,18 +6,16 @@ // // Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) // Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) // // Official repository: https://github.com/cppalliance/mrdocs // -#include "CXXTags.hpp" #include "XMLWriter.hpp" -#include "lib/ConfigImpl.hpp" -#include "lib/Support/Yaml.hpp" -#include "lib/Support/LegibleNames.hpp" -#include "lib/Support/Radix.hpp" #include -#include +#include "CXXTags.hpp" +#include +#include #include //------------------------------------------------ @@ -26,7 +24,7 @@ // //------------------------------------------------ -namespace clang::mrdocs::xml { +namespace mrdocs::xml { //------------------------------------------------ // @@ -53,53 +51,25 @@ build() "\n"; - // writeIndex(); - - visit(corpus_.globalNamespace(), *this); + this->operator()(corpus_.globalNamespace()); os_ << "\n"; return {}; } -//------------------------------------------------ - +template SymbolTy> void XMLWriter:: -writeIndex() +operator()(SymbolTy const& I) { - std::string temp; - temp.reserve(256); - tags_.open("symbols"); - LegibleNames const names(corpus_, true); - for(auto& I : corpus_) - { - corpus_.qualifiedName(I, temp); - auto legible_name = names.getUnqualified(I.id); - tags_.write("symbol", {}, { - { "legible", legible_name }, - { "name", temp }, - { "tag", toString(I.Kind) }, - { I.id } }); - } - tags_.close("symbols"); -} - -//------------------------------------------------ - -template -void -XMLWriter:: -operator()( - T const& I) -{ - if (Info const& base = I; + if (Symbol const& base = I; base.Extraction == ExtractionMode::Dependency) { return; } - #define INFO(Type) if constexpr(T::is##Type()) write##Type(I); - #include + #define INFO(Type) if constexpr(SymbolTy::is##Type()) write##Type(I); +#include } //------------------------------------------------ @@ -107,7 +77,7 @@ operator()( void XMLWriter:: writeNamespace( - NamespaceInfo const& I) + NamespaceSymbol const& I) { constexpr std::string_view namespaceTagName = "namespace"; tags_.open(namespaceTagName, { @@ -116,8 +86,8 @@ writeNamespace( { "is-anonymous", "1", I.IsAnonymous}, { "is-inline", "1", I.IsInline} }); - writeJavadoc(I.javadoc); - for (NameInfo const& NI: I.UsingDirectives) + writeDocComment(I.doc); + for (Name const& NI: I.UsingDirectives) { if (auto const& id = NI.id; id != SymbolID::invalid) { @@ -131,7 +101,7 @@ writeNamespace( void XMLWriter:: writeEnum( - EnumInfo const& I) + EnumSymbol const& I) { constexpr std::string_view enumTagName = "enum"; tags_.open(enumTagName, { @@ -147,9 +117,9 @@ writeEnum( tags_.close(baseTagName); } - writeSourceInfo(I); + writeSourceInfo(I.Loc); - writeJavadoc(I.javadoc); + writeDocComment(I.doc); corpus_.traverse(I, *this); @@ -159,7 +129,7 @@ writeEnum( void XMLWriter:: writeEnumConstant( - EnumConstantInfo const& I) + EnumConstantSymbol const& I) { std::string val = I.Initializer.Value ? std::to_string(*I.Initializer.Value) : @@ -173,9 +143,9 @@ writeEnumConstant( { I.id } }); - writeSourceInfo(I); + writeSourceInfo(I.Loc); - writeJavadoc(I.javadoc); + writeDocComment(I.doc); tags_.close(enumConstantTagName); } @@ -197,7 +167,7 @@ writeFriend( } else if (I.Type) { - attrs.push({ "type", toString(*I.Type) }); + attrs.push({ "type", toString(**I.Type) }); } tags_.write("befriended", {}, attrs); @@ -208,7 +178,7 @@ writeFriend( void XMLWriter:: writeFunction( - FunctionInfo const& I) + FunctionSymbol const& I) { openTemplate(I.Template); @@ -230,7 +200,7 @@ writeFunction( { I.id } }); - writeSourceInfo(I); + writeSourceInfo(I.Loc); writeAttr(I.IsVariadic, "is-variadic", tags_); writeAttr(I.IsVirtualAsWritten, "is-virtual-as-written", tags_); @@ -251,12 +221,15 @@ writeFunction( writeAttr(I.IsNodiscard, "nodiscard", tags_); writeAttr(I.IsExplicitObjectMemberFunction, "is-explicit-object-member-function", tags_); + MRDOCS_ASSERT(!I.ReturnType.valueless_after_move()); writeReturnType(*I.ReturnType, tags_); - for(auto const& J : I.Params) - writeParam(J, tags_); + for (auto const& J: I.Params) + { + ::mrdocs::xml::writeParam(J, tags_); + } - writeJavadoc(I.javadoc); + writeDocComment(I.doc); tags_.close(functionTagName); @@ -266,7 +239,7 @@ writeFunction( void XMLWriter:: writeOverloads( - OverloadsInfo const& I) + OverloadsSymbol const& I) { corpus_.traverse(I, *this); } @@ -274,7 +247,7 @@ writeOverloads( void XMLWriter:: writeGuide( - GuideInfo const& I) + GuideSymbol const& I) { openTemplate(I.Template); @@ -289,16 +262,18 @@ writeGuide( { I.id } }); - writeSourceInfo(I); + writeSourceInfo(I.Loc); tags_.open(deducedTagName); writeType(I.Deduced, tags_); tags_.close(deducedTagName); - for(auto const& J : I.Params) - writeParam(J, tags_); + for (auto const& J: I.Params) + { + xml::writeParam(J, tags_); + } - writeJavadoc(I.javadoc); + writeDocComment(I.doc); tags_.close(guideTagName); @@ -308,7 +283,7 @@ writeGuide( void XMLWriter:: writeConcept( - ConceptInfo const& I) + ConceptSymbol const& I) { openTemplate(I.Template); @@ -320,9 +295,9 @@ writeConcept( { "constraint", I.Constraint.Written }, }); - writeSourceInfo(I); + writeSourceInfo(I.Loc); - writeJavadoc(I.javadoc); + writeDocComment(I.doc); tags_.close(conceptTagName); @@ -332,7 +307,7 @@ writeConcept( void XMLWriter:: writeNamespaceAlias( - NamespaceAliasInfo const& I) + NamespaceAliasSymbol const& I) { constexpr std::string_view namespaceAliasTagName = "namespace-alias"; tags_.open(namespaceAliasTagName, { @@ -341,20 +316,21 @@ writeNamespaceAlias( { I.id } }); - writeSourceInfo(I); + writeSourceInfo(I.Loc); - writeJavadoc(I.javadoc); + writeDocComment(I.doc); tags_.write("aliased", {}, { - {"name", toString(*I.AliasedSymbol)}, - { I.AliasedSymbol->id } + {"name", toString(I.AliasedSymbol)}, + { I.AliasedSymbol.id } }); + tags_.close(namespaceAliasTagName); } void XMLWriter:: - writeUsing(UsingInfo const& I) + writeUsing(UsingSymbol const& I) { dom::String classStr; switch (I.Class) @@ -373,10 +349,8 @@ XMLWriter:: } std::string qualifierStr; - if (I.IntroducedName) - { - qualifierStr = toString(*I.IntroducedName); - } + MRDOCS_ASSERT(!I.IntroducedName.valueless_after_move()); + qualifierStr = toString(*I.IntroducedName); constexpr std::string_view usingTagName = "using"; tags_.open(usingTagName, { @@ -386,9 +360,9 @@ XMLWriter:: { "qualifier", qualifierStr, !qualifierStr.empty() } }); - writeSourceInfo(I); + writeSourceInfo(I.Loc); - writeJavadoc(I.javadoc); + writeDocComment(I.doc); for (auto const& id : I.ShadowDeclarations) tags_.write("named", {}, { id }); @@ -399,7 +373,7 @@ XMLWriter:: void XMLWriter:: writeRecord( - RecordInfo const& I) + RecordSymbol const& I) { openTemplate(I.Template); @@ -411,7 +385,7 @@ writeRecord( { I.id } }); - writeSourceInfo(I); + writeSourceInfo(I.Loc); writeAttr(I.IsFinal, "is-final", tags_); writeAttr(I.IsFinalDestructor, "is-final-dtor", tags_); @@ -422,7 +396,7 @@ writeRecord( { B.Access }, { "class", "virtual", B.IsVirtual }, }); - writeType(B.Type, tags_); + writeType(*B.Type, tags_); tags_.close(baseTagName); } @@ -431,7 +405,7 @@ writeRecord( writeFriend(F); } - writeJavadoc(I.javadoc); + writeDocComment(I.doc); corpus_.traverse(I, *this); @@ -443,7 +417,7 @@ writeRecord( void XMLWriter:: writeTypedef( - TypedefInfo const& I) + TypedefSymbol const& I) { openTemplate(I.Template); @@ -463,11 +437,11 @@ writeTypedef( { I.id } }); - writeSourceInfo(I); + writeSourceInfo(I.Loc); writeType(I.Type, tags_); - writeJavadoc(I.javadoc); + writeDocComment(I.doc); tags_.close(tag); @@ -477,7 +451,7 @@ writeTypedef( void XMLWriter:: writeVariable( - VariableInfo const& I) + VariableSymbol const& I) { openTemplate(I.Template); @@ -497,7 +471,7 @@ writeVariable( { "default", I.Initializer.Written, ! I.Initializer.Written.empty() } }); - writeSourceInfo(I); + writeSourceInfo(I.Loc); if(I.IsMutable) tags_.write(attributeTagName, {}, { @@ -517,7 +491,7 @@ writeVariable( writeType(I.Type, tags_); - writeJavadoc(I.javadoc); + writeDocComment(I.doc); tags_.close(varTagName); @@ -560,10 +534,12 @@ writeLocation( void XMLWriter:: openTemplate( - const std::optional& I) + Optional const& I) { - if(! I) + if (!I) + { return; + } tags_.open(templateTagName, { {"class", toString(I->specializationKind()), @@ -573,16 +549,20 @@ openTemplate( {I->Primary} }); - for(auto const& tparam : I->Params) + for (auto const& tparam: I->Params) + { writeTemplateParam(*tparam, tags_); - for(auto const& targ : I->Args) + } + for (auto const& targ: I->Args) + { writeTemplateArg(*targ, tags_); + } } void XMLWriter:: closeTemplate( - const std::optional& I) + const Optional& I) { if(! I) return; @@ -593,125 +573,90 @@ closeTemplate( void XMLWriter:: -writeJavadoc( - std::optional const& javadoc) +writeDocComment( + Optional const& doc) { - if (!javadoc) + if (!doc) { return; } - tags_.open(javadocTagName); - if (javadoc->brief) + tags_.open(docTagName); + if (doc->brief) { - writeNode(*javadoc->brief); + writeBrief(*doc->brief); } - writeNodes(javadoc->getBlocks()); - writeNodes(javadoc->returns); - writeNodes(javadoc->params); - writeNodes(javadoc->tparams); - writeNodes(javadoc->exceptions); - writeNodes(javadoc->sees); - writeNodes(javadoc->preconditions); - writeNodes(javadoc->postconditions); - if (!javadoc->relates.empty()) + writeBlocks(doc->Document); + writeBlocks(doc->returns); + writeBlocks(doc->params); + writeBlocks(doc->tparams); + writeBlocks(doc->exceptions); + writeBlocks(doc->sees); + writeBlocks(doc->preconditions); + writeBlocks(doc->postconditions); + if (!doc->relates.empty()) { tags_.open(relatesTagName); - writeNodes(javadoc->relates); + writeInlines(doc->relates); tags_.close(relatesTagName); } - if (!javadoc->related.empty()) + if (!doc->related.empty()) { tags_.open(relatedTagName); - writeNodes(javadoc->related); + writeInlines(doc->related); tags_.close(relatedTagName); } - tags_.close(javadocTagName); + tags_.close(docTagName); } -void -XMLWriter:: -writeNode(doc::Node const& node) -{ - switch(node.Kind) - { - case doc::NodeKind::text: - writeText(dynamic_cast(node)); - break; - case doc::NodeKind::styled: - writeStyledText(dynamic_cast(node)); - break; - case doc::NodeKind::heading: - writeHeading(dynamic_cast(node)); - break; - case doc::NodeKind::paragraph: - writeParagraph(dynamic_cast(node)); - break; - case doc::NodeKind::link: - writeLink(dynamic_cast(node)); - break; - case doc::NodeKind::list_item: - writeListItem(dynamic_cast(node)); - break; - case doc::NodeKind::unordered_list: - writeUnorderedList(dynamic_cast(node)); - break; - case doc::NodeKind::brief: - writeBrief(dynamic_cast(node)); - break; - case doc::NodeKind::admonition: - writeAdmonition(dynamic_cast(node)); - break; - case doc::NodeKind::code: - writeCode(dynamic_cast(node)); - break; - case doc::NodeKind::param: - writeJParam(dynamic_cast(node)); - break; - case doc::NodeKind::tparam: - writeTParam(dynamic_cast(node)); - break; - case doc::NodeKind::returns: - writeReturns(dynamic_cast(node)); - break; - case doc::NodeKind::reference: - writeReference(dynamic_cast(node)); - break; - case doc::NodeKind::copy_details: - writeCopied(dynamic_cast(node)); - break; - case doc::NodeKind::throws: - writeThrows(dynamic_cast(node)); - break; - case doc::NodeKind::see: - writeSee(dynamic_cast(node)); - break; - case doc::NodeKind::precondition: - writePrecondition(dynamic_cast(node)); - break; - case doc::NodeKind::postcondition: - writePostcondition(dynamic_cast(node)); - break; - default: - // unknown kind - MRDOCS_UNREACHABLE(); - } -} +//void +//XMLWriter:: +//writeNode(doc::Node const& node) +//{ +// auto* nodePtr = &node; +// auto* blockPtr = dynamic_cast(nodePtr); +// if (blockPtr) +// { +// switch (blockPtr->Kind) +// { +// #define INFO(Type) case doc::BlockKind::Type: \ +// write##Type(dynamic_cast(node)); \ +// return; +//#include +// default: +// MRDOCS_UNREACHABLE(); +// } +// } +// auto* inlinePtr = dynamic_cast(nodePtr); +// if (inlinePtr) +// { +// switch (inlinePtr->Kind) +// { +// #define INFO(Type) case doc::InlineKind::Type: \ +// write##Type(dynamic_cast(node)); \ +// return; +//#include +// default: +// MRDOCS_UNREACHABLE(); +// } +// } +// MRDOCS_UNREACHABLE(); +//} void XMLWriter:: writeReference( - doc::Reference const& node) + doc::ReferenceInline const& node) { - tags_.write("reference", node.string, { + tags_.write("reference", node.literal, { { node.id } }); } void XMLWriter:: -writeCopied( - doc::CopyDetails const& node) +writeCopyDetails( + doc::CopyDetailsInline const& node) { std::string_view constexpr tag_name = "copydetails"; tags_.write(tag_name, node.string, { @@ -722,11 +667,12 @@ writeCopied( void XMLWriter:: writeLink( - doc::Link const& node) + doc::LinkInline const& node) { - tags_.write("link", node.string, { - { "href", node.href } - }); + + tags_.write( + "link", doc::getAsPlainText(node.asInline()), + {{ "href", node.href }}); } void @@ -735,174 +681,201 @@ writeListItem( doc::ListItem const& node) { tags_.open("listitem"); - writeNodes(node.children); + // flatten blocks to write their inlines directly when possible + // this maintains the schema, but it would be best to + // just render the blocks directly + for (auto const& childb : node.blocks) + { + auto* ilnPtr = dynamic_cast(&*childb); + MRDOCS_CHECK_OR_CONTINUE(ilnPtr); + writeInlines(ilnPtr->children); + } tags_.close("listitem"); } void XMLWriter:: -writeUnorderedList( - doc::UnorderedList const& node) +writeList( + doc::ListBlock const& node) { tags_.open("unorderedlist"); - writeNodes(node.items); + for (auto const& li: node.items) + { + writeListItem(li); + } tags_.close("unorderedlist"); } void XMLWriter:: writeBrief( - doc::Paragraph const& node) + doc::BriefBlock const& node) { tags_.open("brief"); - writeNodes(node.children); + writeInlines(node.children); tags_.close("brief"); } void XMLWriter:: writeText( - doc::Text const& node) + doc::TextInline const& node) { tags_.indent() << "" << - xmlEscape(node.string) << + xmlEscape(node.literal) << "\n"; } void XMLWriter:: -writeStyledText( - doc::Styled const& node) +writeCode(doc::CodeInline const& node) { - tags_.write(toString(node.style), node.string); + tags_.write("mono", doc::getAsPlainText(node.asInline())); +} + +void +XMLWriter:: +writeStrong(doc::StrongInline const& node) +{ + tags_.write("bold", doc::getAsPlainText(node.asInline())); +} + +void +XMLWriter:: +writeEmph(doc::EmphInline const& node) +{ + tags_.write("italic", doc::getAsPlainText(node.asInline())); } void XMLWriter:: writeHeading( - doc::Heading const& heading) + doc::HeadingBlock const& heading) { - tags_.write("head", heading.string); + tags_.write("head", doc::getAsPlainText(heading.asInlineContainer())); } void XMLWriter:: -writeParagraph( - doc::Paragraph const& para, - llvm::StringRef tag) +writeInlineContainer( + doc::InlineContainer const& node, + std::string_view tag) { - tags_.open("para", { - { "class", tag, ! tag.empty() }}); - writeNodes(para.children); + tags_.open("para", {{ "class", tag, !tag.empty() }}); + writeInlines(node.children); tags_.close("para"); } void XMLWriter:: -writeSee( - doc::See const& para, - llvm::StringRef tag) +writeSee(doc::SeeBlock const& para) { - tags_.open("see", { - { "class", tag, ! tag.empty() }}); - writeNodes(para.children); + tags_.open("see"); + writeInlines(para.children); tags_.close("see"); } void XMLWriter:: writePrecondition( - doc::Precondition const& para) + doc::PreconditionBlock const& para) { tags_.open("pre", {}); - writeNodes(para.children); + writeInlines(para.children); tags_.close("pre"); } void XMLWriter:: writePostcondition( - doc::Postcondition const& para) + doc::PostconditionBlock const& para) { tags_.open("post", {}); - writeNodes(para.children); + writeInlines(para.children); tags_.close("post"); } void XMLWriter:: writeAdmonition( - doc::Admonition const& admonition) + doc::AdmonitionBlock const& admonition) { llvm::StringRef tag; switch(admonition.admonish) { - case doc::Admonish::note: + case doc::AdmonitionKind::note: tag = "note"; break; - case doc::Admonish::tip: + case doc::AdmonitionKind::tip: tag = "tip"; break; - case doc::Admonish::important: + case doc::AdmonitionKind::important: tag = "important"; break; - case doc::Admonish::caution: + case doc::AdmonitionKind::caution: tag = "caution"; break; - case doc::Admonish::warning: + case doc::AdmonitionKind::warning: tag = "warning"; break; default: // unknown style MRDOCS_UNREACHABLE(); } - writeParagraph(admonition, tag); + MRDOCS_CHECK_OR(!admonition.blocks.empty()); + auto& firstBlock = admonition.blocks.front(); + auto const* asInlineContainer = dynamic_cast(&*firstBlock); + if (asInlineContainer) + { + writeInlineContainer(*asInlineContainer, tag); + } } void XMLWriter:: -writeCode(doc::Code const& code) +writeCode(doc::CodeBlock const& code) { - if(code.children.empty()) + if(code.literal.empty()) { tags_.indent() << "\n"; return; } tags_.open("code"); - writeNodes(code.children); + tags_.indent() << "" << xmlEscape(code.literal) << "\n"; tags_.close("code"); } void XMLWriter:: writeReturns( - doc::Returns const& returns) + doc::ReturnsBlock const& returns) { - if(returns.empty()) + if (returns.empty()) + { return; + } tags_.open("returns"); - writeNodes(returns.children); + writeInlines(returns.children); tags_.close("returns"); } void XMLWriter:: writeThrows( - doc::Throws const& throws) + doc::ThrowsBlock const& throws) { if(throws.empty()) return; tags_.open("throws"); - writeNodes(throws.children); + writeInlines(throws.children); tags_.close("throws"); } void XMLWriter:: -writeJParam( - doc::Param const& param) +writeParam(doc::ParamBlock const& param) { dom::String direction; switch(param.direction) @@ -926,19 +899,240 @@ writeJParam( { "name", param.name, ! param.name.empty() }, { "class", direction, ! direction.empty() } }); - writeNodes(param.children); + writeInlines(param.children); tags_.close("param"); } void XMLWriter:: writeTParam( - doc::TParam const& tparam) + doc::TParamBlock const& tparam) { tags_.open("tparam", { { "name", tparam.name, ! tparam.name.empty() }}); - writeNodes(tparam.children); + writeInlines(tparam.children); tags_.close("tparam"); } -} // clang::mrdocs::xml +void +XMLWriter:: +writeImage(doc::ImageInline const& el) +{ + tags_.open("image"); + auto text = doc::getAsPlainText(el.asInline()); + if (!text.empty()) + { + tags_.write("alt", text); + } + tags_.close("image"); +} + +void +XMLWriter:: +writeQuote(doc::QuoteBlock const& el) +{ + tags_.open("quote"); + writeBlocks(el.blocks); + tags_.close("quote"); +} + +void +XMLWriter:: +writeTable(doc::TableBlock const& el) +{ + tags_.open("table"); + for (auto const& row : el.items) + { + tags_.open("tablerow"); + for (auto const& cell : row.Cells) + { + tags_.open("tablecell"); + auto str = doc::getAsPlainText(cell.asInlineContainer()); + if (!str.empty()) + { + tags_.write("celltext", str); + } + tags_.close("tablecell"); + } + tags_.close("tablerow"); + } + tags_.close("table"); +} + +void +XMLWriter:: +writeHighlight(doc::HighlightInline const& el) +{ + tags_.open("highlight"); + auto text = doc::getAsPlainText(el.asInline()); + tags_.close("highlight"); +} + +void +XMLWriter:: +writeLineBreak(doc::LineBreakInline const& el) +{ + tags_.write("linebreak", {}); +} + +void +XMLWriter:: +writeParagraph(doc::ParagraphBlock const& el) +{ + tags_.open("para"); + for (Polymorphic const& child : el.children) + { + writeInline(*child); + } + tags_.close("para"); +} + +void +XMLWriter:: +writeSoftBreak(doc::SoftBreakInline const& el) +{ + tags_.write("softbreak", {}); +} + +void +XMLWriter:: +writeSubscript(doc::SubscriptInline const& el) +{ + tags_.open("subscript"); + auto text = doc::getAsPlainText(el.asInline()); + if (!text.empty()) + { + tags_.indent() << xmlEscape(text) << "\n"; + } + tags_.close("subscript"); +} + +void +XMLWriter:: +writeSuperscript(doc::SuperscriptInline const& el) +{ + tags_.open("superscript"); + auto text = doc::getAsPlainText(el.asInline()); + if (!text.empty()) + { + tags_.indent() << xmlEscape(text) << "\n"; + } + tags_.close("superscript"); +} + +void +XMLWriter:: +writeStrikethrough(doc::StrikethroughInline const& el) +{ + tags_.open("strikethrough"); + auto text = doc::getAsPlainText(el.asInline()); + if (!text.empty()) + { + tags_.indent() << xmlEscape(text) << "\n"; + } + tags_.close("strikethrough"); +} + +void +XMLWriter:: +writeThematicBreak(doc::ThematicBreakBlock const& el) +{ + tags_.write("thematicbreak", {}); +} + +void +XMLWriter:: +writeFootnoteReference(doc::FootnoteReferenceInline const& el) +{ + tags_.open("footnotereference"); + if (!el.label.empty()) + { + tags_.write("label", el.label); + } + tags_.close("footnotereference"); +} + +void +XMLWriter:: +writeFootnoteDefinition(doc::FootnoteDefinitionBlock const& el) +{ + tags_.open("footnotedefinition"); + if (!el.label.empty()) + { + tags_.write("label", el.label); + } + writeBlocks(el.blocks); + tags_.close("footnotedefinition"); +} + +void +XMLWriter:: +writeMath(doc::MathInline const& el) +{ + tags_.open("math"); + if (!el.literal.empty()) + { + tags_.indent() << xmlEscape(el.literal) << "\n"; + } + tags_.close("math"); +} + +void +XMLWriter:: +writeMath(doc::MathBlock const& el) +{ + tags_.open("mathblock"); + if (!el.literal.empty()) + { + tags_.indent() << xmlEscape(el.literal) << "\n"; + } + tags_.close("mathblock"); +} + +void +XMLWriter:: +writeDefinitionList(doc::DefinitionListBlock const& el) +{ + tags_.open("definitionlist"); + for (auto const& item : el.items) + { + tags_.open("definitionitem"); + tags_.open("term"); + writeInlines(item.term.children); + tags_.close("term"); + tags_.open("definition"); + writeBlocks(item.blocks); + tags_.close("definition"); + tags_.close("definitionitem"); + } + tags_.close("definitionlist"); +} + +void +XMLWriter:: +writeBlock(doc::Block const& node) +{ + switch (node.Kind) + { + #define INFO(Type) case doc::BlockKind::Type: \ + write##Type(node.as##Type()); \ + return; +#include + } +} + +void +XMLWriter:: +writeInline(doc::Inline const& node) +{ + switch (node.Kind) + { + #define INFO(Type) case doc::InlineKind::Type: \ + write##Type(node.as##Type()); \ + return; +#include + } +} + + +} // mrdocs::xml diff --git a/src/lib/Gen/xml/XMLWriter.hpp b/src/lib/Gen/xml/XMLWriter.hpp index 05c5ecda2..c6ad19714 100644 --- a/src/lib/Gen/xml/XMLWriter.hpp +++ b/src/lib/Gen/xml/XMLWriter.hpp @@ -12,18 +12,18 @@ #ifndef MRDOCS_LIB_GEN_XML_XMLWRITER_HPP #define MRDOCS_LIB_GEN_XML_XMLWRITER_HPP -#include "XMLTags.hpp" -#include "lib/Support/YamlFwd.hpp" +#include +#include #include #include #include #include -namespace clang::mrdocs::xml { +namespace mrdocs::xml { class jit_indenter; -/** A writer which outputs XML. +/** A writer that outputs XML. */ class XMLWriter { @@ -43,70 +43,108 @@ class XMLWriter Expected build(); - void writeIndex(); - - template - void operator()(T const&); + template SymbolTy> + void + operator()(SymbolTy const& I); // --------------- // Info types -#define INFO(Type) void write##Type(Type##Info const&); -#include +#define INFO(Type) void write##Type(Type##Symbol const&); +#include void writeSourceInfo(SourceInfo const& I); void writeLocation(Location const& loc, bool def = false); - void writeJavadoc(std::optional const& javadoc); + void writeDocComment(Optional const& doc); void writeFriend(FriendInfo const& I); - void openTemplate(const std::optional& I); - void closeTemplate(const std::optional& I); + void openTemplate(Optional const& I); + void closeTemplate(Optional const& I); // --------------- - // Javadoc types - - void writeAdmonition(doc::Admonition const& node); - void writeBrief(doc::Paragraph const& node); - void writeCode(doc::Code const& node); - void writeHeading(doc::Heading const& node); - void writeLink(doc::Link const& node); - void writeListItem(doc::ListItem const& node); - void writeUnorderedList(doc::UnorderedList const& node); - void writeParagraph(doc::Paragraph const& node, llvm::StringRef tag = ""); - void writeJParam(doc::Param const& node); - void writeReturns(doc::Returns const& node); - void writeStyledText(doc::Styled const& node); - void writeText(doc::Text const& node); - void writeTParam(doc::TParam const& node); - void writeReference(doc::Reference const& node); - void writeCopied(doc::CopyDetails const& node); - void writeThrows(doc::Throws const& node); - void writeSee(doc::See const& node, llvm::StringRef tag = ""); - void writePrecondition(doc::Precondition const& node); - void writePostcondition(doc::Postcondition const& node); - - void writeNode(doc::Node const& node); + // DocComment types + +#define INFO(Type) void write##Type(doc::Type##Block const&); +#include - template void - writeNodes(std::vector> const& list) + writeBlock(doc::Block const& node); + + template T> + void + writeBlocks(std::vector const& list) { for (auto const& node: list) { - writeNode(*node); + writeBlock(node); + } + } + + void + writeBlocks(std::vector> const& list) + { + for (auto const& node: list) + { + writeBlock(*node); } } - template T> - void writeNodes(std::vector const& list) +#define INFO(Type) void write##Type(doc::Type##Inline const&); +#include + + void + writeInline(doc::Inline const& node); + + template T> + void + writeInlines(std::vector const& list) { for (auto const& node: list) { - writeNode(node); + writeInline(node); } } + + void + writeInlines(std::vector> const& list) + { + for (auto const& node: list) + { + writeInline(*node); + } + } + + void + writeInlineContainer( + doc::InlineContainer const& node, + std::string_view tag); + + void + writeListItem(doc::ListItem const& node); + +// void +// writeNode(doc::Node const& node); + + template + void + writeNodes(std::vector> const& list) + { + for (auto const& node: list) + { + writeNode(*node); + } + } + +// template T> +// void writeNodes(std::vector const& list) +// { +// for (auto const& node: list) +// { +// writeNode(node); +// } +// } }; -} // clang::mrdocs::xml +} // mrdocs::xml -#endif +#endif // MRDOCS_LIB_GEN_XML_XMLWRITER_HPP diff --git a/src/lib/Metadata/DocComment.cpp b/src/lib/Metadata/DocComment.cpp new file mode 100644 index 000000000..74318b8eb --- /dev/null +++ b/src/lib/Metadata/DocComment.cpp @@ -0,0 +1,182 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace mrdocs { +namespace doc { + +dom::String +toString(AdmonitionKind kind) noexcept +{ + switch(kind) + { + case AdmonitionKind::none: + return ""; + case AdmonitionKind::note: + return "note"; + case AdmonitionKind::tip: + return "tip"; + case AdmonitionKind::important: + return "important"; + case AdmonitionKind::caution: + return "caution"; + case AdmonitionKind::warning: + return "warning"; + default: + MRDOCS_UNREACHABLE(); + } +} + +dom::String +toString(ParamDirection kind) noexcept +{ + switch(kind) + { + case ParamDirection::none: + return ""; + case ParamDirection::in: + return "in"; + case ParamDirection::out: + return "out"; + case ParamDirection::inout: + return "inout"; + default: + MRDOCS_UNREACHABLE(); + } +} + +dom::String +toString(Parts kind) noexcept +{ + switch(kind) + { + case Parts::all: + return "all"; + case Parts::brief: + return "brief"; + case Parts::description: + return "description"; + default: + MRDOCS_UNREACHABLE(); + } +} + +std::strong_ordering +operator<=>(Polymorphic const& lhs, Polymorphic const& rhs) +{ + MRDOCS_ASSERT(!lhs.valueless_after_move()); + MRDOCS_ASSERT(!rhs.valueless_after_move()); + if (lhs->Kind == rhs->Kind) + { + return visit(*lhs, detail::VisitCompareFn(*rhs)); + } + return lhs->Kind <=> rhs->Kind; +} + +} // doc + +//------------------------------------------------ + +DocComment::DocComment() noexcept = default; + +DocComment::DocComment( + std::vector> blocks) + : Document(std::move(blocks)) +{ +} + +bool +DocComment:: +operator!=(DocComment const& other) const noexcept +{ + return !(*this == other); +} + +void +DocComment:: +append(DocComment&& other) +{ + using std::ranges::find; + using std::ranges::copy_if; + using std::views::transform; + + // blocks + for (auto&& block: other.Document) + { + Document.emplace_back(std::move(block)); + } + // returns + copy_if(other.returns, std::back_inserter(returns), + [&](auto const& r) + { + return find(returns, r) == returns.end(); + }); + // params + copy_if(other.params, std::back_inserter(params), + [&](auto const& p) + { + auto namesAndDirection = transform(params, [](auto const& q) + { + return std::make_pair(std::string_view(q.name), q.direction); + }); + auto el = std::make_pair(std::string_view(p.name), p.direction); + return find(namesAndDirection, el) == namesAndDirection.end(); + }); + // tparams + copy_if(other.tparams, std::back_inserter(tparams), + [&](auto const& p) + { + auto names = transform(tparams, &doc::TParamBlock::name); + return find(names, p.name) == names.end(); + }); + // exceptions + copy_if(other.exceptions, std::back_inserter(exceptions), + [&](auto const& e) + { + // e.exception.string + auto exceptionRefs = transform(exceptions, &doc::ThrowsBlock::exception); + static_assert(range_of); + auto exceptionStrs = transform(exceptionRefs, &doc::ReferenceInline::literal); + static_assert(range_of); + return find(exceptionStrs, e.exception.literal) == exceptionStrs.end(); + }); + // sees + copy_if(other.sees, std::back_inserter(sees), + [&](auto const& s) + { + return find(sees, s) == sees.end(); + }); + // preconditions + copy_if(other.preconditions, std::back_inserter(preconditions), + [&](auto const& p) + { + return find(preconditions, p) == preconditions.end(); + }); + // postconditions + copy_if(other.postconditions, std::back_inserter(postconditions), + [&](auto const& p) + { + return find(postconditions, p) == postconditions.end(); + }); +} + +} // mrdocs + diff --git a/src/lib/Metadata/DocComment/Block.cpp b/src/lib/Metadata/DocComment/Block.cpp new file mode 100644 index 000000000..b95d2f259 --- /dev/null +++ b/src/lib/Metadata/DocComment/Block.cpp @@ -0,0 +1,26 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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 +// + +#include +#include +#include + +namespace mrdocs { +namespace doc { + +std::strong_ordering +operator<=>(Polymorphic const& lhs, Polymorphic const& rhs) +{ + return CompareDerived(lhs, rhs); +} + +} +} // mrdocs \ No newline at end of file diff --git a/src/lib/Metadata/DocComment/Block/BlockBase.cpp b/src/lib/Metadata/DocComment/Block/BlockBase.cpp new file mode 100644 index 000000000..4ccf1e77a --- /dev/null +++ b/src/lib/Metadata/DocComment/Block/BlockBase.cpp @@ -0,0 +1,199 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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 +// + +#include +#include +#include +#include +#include +#include + +namespace mrdocs { +namespace doc { + +void +ltrim(Block& el) +{ + visit(el, [](BlockTy& N) + { + if constexpr (std::derived_from) + { + ltrim(N.asBlockContainer()); + } + if constexpr (std::derived_from) + { + ltrim(N.asInlineContainer()); + } + if constexpr (requires { { N.literal } -> std::same_as; }) + { + std::string& text = N.literal; + text = mrdocs::ltrim(text); + } + }); +} + +void +rtrim(Block& el) +{ + visit(el, [](BlockTy& N) { + if constexpr (std::derived_from) + { + rtrim(N.asBlockContainer()); + } + else if constexpr (std::derived_from) + { + rtrim(N.asInlineContainer()); + } + else if constexpr (requires { { N.literal } -> std::same_as; }) + { + std::string& text = N.literal; + text = mrdocs::rtrim(text); + } + }); +} + +bool +isEmpty(Block const& el) +{ + return visit(el, [](BlockTy const& N) -> bool + { + if constexpr (std::derived_from) + { + return N.blocks.empty(); + } + else if constexpr (std::derived_from) + { + return N.children.empty(); + } + else if constexpr (requires { { N.literal } -> std::same_as; }) + { + return N.literal.empty(); + } + else + { + return is_one_of( + N.Kind, + {BlockKind::ThematicBreak}); + } + }); +} + + +std::strong_ordering +BlockContainer:: +operator<=>(BlockContainer const& rhs) const +{ + return this->blocks <=> rhs.blocks; +} + +void +ltrim(BlockContainer& container) +{ + for (auto it = container.blocks.begin(); it != container.blocks.end();) + { + auto& child = *it; + visit(*child, [](BlockTy& N) { + if constexpr (std::derived_from) + { + ltrim(N.asBlockContainer()); + } + else if constexpr (std::same_as) + { + for (TableRow& row : N.items) + { + for (TableCell& cell : row.Cells) + { + ltrim(cell.asInlineContainer()); + if (!cell.children.empty()) + { + break; + } + } + } + } + else if constexpr (std::same_as) + { + for (ListItem& item : N.items) + { + ltrim(item.asBlockContainer()); + if (!item.blocks.empty()) + { + break; + } + } + } + else if constexpr (requires { { N.literal } -> std::same_as; }) + { + std::string& text = N.literal; + text = mrdocs::ltrim(text); + } + }); + if (!isEmpty(child)) + { + break; + } + it = container.blocks.erase(it); + } +} + +void +rtrim(BlockContainer& container) +{ + for (auto it = container.blocks.rbegin(); it != container.blocks.rend();) + { + auto& child = *it; + visit(*child, [](BlockTy& N) { + if constexpr (std::derived_from) + { + rtrim(N.asBlockContainer()); + } + else if constexpr (std::same_as) + { + for (TableRow& row : N.items) + { + for (TableCell& cell : row.Cells) + { + ltrim(cell.asInlineContainer()); + if (!cell.children.empty()) + { + break; + } + } + } + } + else if constexpr (std::same_as) + { + for (ListItem& item: N.items) + { + rtrim(item.asBlockContainer()); + if (!item.blocks.empty()) + { + break; + } + } + } + else if constexpr (requires { { N.literal } -> std::same_as; }) + { + std::string& text = N.literal; + text = mrdocs::rtrim(text); + } + }); + if (!isEmpty(child)) + { + break; + } + it = decltype(it){ container.blocks.erase(std::next(it).base()) }; + } +} + + +} +} // mrdocs \ No newline at end of file diff --git a/src/lib/Metadata/DocComment/Inline.cpp b/src/lib/Metadata/DocComment/Inline.cpp new file mode 100644 index 000000000..f0abb0228 --- /dev/null +++ b/src/lib/Metadata/DocComment/Inline.cpp @@ -0,0 +1,87 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#include +#include + +namespace mrdocs::doc { + +std::strong_ordering +operator<=>(Polymorphic const& lhs, Polymorphic const& rhs) +{ + return CompareDerived(lhs, rhs); +} + +void +ltrim(Polymorphic& el) +{ + // An inline element can contain: + // 1. nothing: the ones that represent whitespace should be removed + // 2. literal text: the initial whitespace should be removed + // 3. other inlines: we should remove all whitespace-only inlines + visit(*el, [](InlineTy& N) + { + if constexpr (std::derived_from) + { + ltrim(N.asInlineContainer()); + } + else if constexpr (requires { { N.literal } -> std::same_as; }) + { + std::string& text = N.literal; + text = mrdocs::ltrim(text); + } + }); +} + +void +rtrim(Polymorphic& el) +{ + // Same as ltrim but for the end of the element + visit(*el, [](InlineTy& N) + { + if constexpr (std::derived_from) + { + rtrim(N.asInlineContainer()); + } + else if constexpr (requires { { N.literal } -> std::same_as; }) + { + std::string& text = N.literal; + text = mrdocs::rtrim(text); + } + }); +} + +MRDOCS_DECL +bool +isEmpty(Polymorphic const& el) +{ + return visit(*el, [](InlineTy const& N) -> bool + { + if constexpr (std::derived_from) + { + return N.children.empty(); + } + else if constexpr (requires { { N.literal } -> std::same_as; }) + { + return N.literal.empty(); + } + else + { + return is_one_of( + N.Kind, + { InlineKind::LineBreak, InlineKind::SoftBreak }); + } + }); +} + + +} // mrdocs::doc \ No newline at end of file diff --git a/src/lib/Metadata/DocComment/Inline/InlineBase.cpp b/src/lib/Metadata/DocComment/Inline/InlineBase.cpp new file mode 100644 index 000000000..256c49624 --- /dev/null +++ b/src/lib/Metadata/DocComment/Inline/InlineBase.cpp @@ -0,0 +1,148 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#include +#include +#include + +namespace mrdocs::doc { + +void +getAsPlainText(doc::Inline const& in, std::string& dst) +{ + if (auto const* text = dynamic_cast(&in)) + { + dst += text->literal; + return; + } + else if (in.isSoftBreak()) + { + dst += ' '; + return; + } + else if (in.isLineBreak()) + { + dst += '\n'; + return; + } + else if (auto const* container = dynamic_cast(&in)) + { + getAsPlainText(*container, dst); + return; + } +} + +std::strong_ordering +InlineContainer:: +operator<=>(InlineContainer const& rhs) const +{ + return this->children <=> rhs.children; +} + +InlineContainer:: +InlineContainer(std::string_view text) +{ + operator=(text); +} + +InlineContainer:: +InlineContainer(std::string const& text) + : InlineContainer(std::string_view(text)) +{} + + +InlineContainer:: +InlineContainer(std::string&& text) + : InlineContainer(std::string_view(text)) +{} + + +InlineContainer& +InlineContainer:: +operator=(std::string_view text) +{ + this->children.clear(); + return append(text); +} + + +InlineContainer& +InlineContainer:: +append(std::string_view text) +{ + if (!text.empty()) + { + this->children.push_back(Polymorphic(std::in_place_type, text)); + } + return *this; +} + +void +ltrim(InlineContainer& inlines) +{ + for (auto it = inlines.children.begin(); it != inlines.children.end();) + { + auto& child = *it; + visit(*child, [](InlineTy& N) { + if constexpr (std::derived_from) + { + ltrim(N.asInlineContainer()); + } + if constexpr (requires { { N.literal } -> std::same_as; }) + { + std::string& text = N.literal; + text = mrdocs::ltrim(text); + } + }); + if (!isEmpty(child)) + { + break; + } + it = inlines.children.erase(it); + } +} + +void +rtrim(InlineContainer& inlines) +{ + for (auto it = inlines.children.rbegin(); it != inlines.children.rend();) + { + auto& child = *it; + visit(*child, [](InlineTy& N) { + if constexpr (std::derived_from) + { + rtrim(N.asInlineContainer()); + } + if constexpr (requires { { N.literal } -> std::same_as; }) + { + std::string& text = N.literal; + text = mrdocs::rtrim(text); + } + }); + if (!isEmpty(child)) + { + break; + } + it = decltype(it){ inlines.children.erase(std::next(it).base()) }; + } +} + +void +getAsPlainText(doc::InlineContainer const& in, std::string& dst) +{ + for (auto const& child : in.children) + { + getAsPlainText(*child, dst); + } +} + +} // mrdocs::doc \ No newline at end of file diff --git a/src/lib/Metadata/DomCorpus.cpp b/src/lib/Metadata/DomCorpus.cpp index db12ba844..62af85736 100644 --- a/src/lib/Metadata/DomCorpus.cpp +++ b/src/lib/Metadata/DomCorpus.cpp @@ -10,18 +10,14 @@ // Official repository: https://github.com/cppalliance/mrdocs // +#include #include -#include -#include "lib/Support/LegibleNames.hpp" -#include "lib/Support/Radix.hpp" -#include -#include -#include #include -#include +#include #include +#include -namespace clang::mrdocs { +namespace mrdocs { class DomCorpus::Impl { @@ -46,7 +42,7 @@ class DomCorpus::Impl } dom::Object - create(Info const& I) const + create(Symbol const& I) const { return domCorpus_.construct(I); } @@ -56,7 +52,7 @@ class DomCorpus::Impl { // VFALCO Hack to deal with symbol IDs // being emitted without the corresponding data. - Info const* I = corpus_.find(id); + Symbol const* I = corpus_.find(id); MRDOCS_CHECK_OR(I, {}); return create(*I); } @@ -82,7 +78,7 @@ getCorpus() const dom::Object DomCorpus:: -construct(Info const& I) const +construct(Symbol const& I) const { return visit(I, [this](T const& U) -> dom::Object { @@ -103,14 +99,14 @@ get(SymbolID const& id) const dom::Value DomCorpus:: -getJavadoc(Javadoc const&) const +getDocComment(DocComment const&) const { // Default implementation returns null. return nullptr; } dom::Array -getParents(DomCorpus const& C, Info const& I) +getParents(DomCorpus const& C, Symbol const& I) { // A convenient list to iterate over the parents // with resorting to partial template recursion @@ -119,10 +115,10 @@ getParents(DomCorpus const& C, Info const& I) dom::Array res; for (SymbolID const& id : pIds) { - Info const& PI = corpus.get(id); + Symbol const& PI = corpus.get(id); res.push_back(C.construct(PI)); } return res; } -} // clang::mrdocs +} // mrdocs diff --git a/src/lib/Metadata/Expression.cpp b/src/lib/Metadata/Expression.cpp index 223c3c78c..9b5c8261b 100644 --- a/src/lib/Metadata/Expression.cpp +++ b/src/lib/Metadata/Expression.cpp @@ -11,7 +11,7 @@ #include -namespace clang::mrdocs { +namespace mrdocs { void merge(ExprInfo& I, ExprInfo&& Other) @@ -22,4 +22,4 @@ merge(ExprInfo& I, ExprInfo&& Other) } } -} // clang::mrdocs +} // mrdocs diff --git a/src/lib/Metadata/Finalizers/BaseMembersFinalizer.cpp b/src/lib/Metadata/Finalizers/BaseMembersFinalizer.cpp index 40b944618..dc56b86e3 100644 --- a/src/lib/Metadata/Finalizers/BaseMembersFinalizer.cpp +++ b/src/lib/Metadata/Finalizers/BaseMembersFinalizer.cpp @@ -9,15 +9,15 @@ // #include "BaseMembersFinalizer.hpp" -#include #include #include +#include -namespace clang::mrdocs { +namespace mrdocs { void BaseMembersFinalizer:: -inheritBaseMembers(RecordInfo& I, RecordInfo const& B, AccessKind const A) +inheritBaseMembers(RecordSymbol& I, RecordSymbol const& B, AccessKind const A) { inheritBaseMembers(I.id, I.Interface, B.Interface, A); } @@ -81,7 +81,7 @@ inheritBaseMembers( namespace { bool -shouldCopy(Config const& config, Info const& M) +shouldCopy(Config const& config, Symbol const& M) { if (config->inheritBaseMembers == PublicSettings::BaseMemberInheritance::CopyDependencies) { @@ -102,13 +102,12 @@ inheritBaseMembers( { // Find the info from the base class MRDOCS_CHECK_OR_CONTINUE(!contains(derived, otherID)); - Info* otherInfoPtr = corpus_.find(otherID); + Symbol* otherInfoPtr = corpus_.find(otherID); MRDOCS_CHECK_OR_CONTINUE(otherInfoPtr); - Info& otherInfo = *otherInfoPtr; + Symbol& otherInfo = *otherInfoPtr; // Check if we're not attempt to copy a special member function - if (auto const *funcPtr = - dynamic_cast(otherInfoPtr)) { + if (auto const *funcPtr = otherInfoPtr->asFunctionPtr()) { MRDOCS_CHECK_OR_CONTINUE( !is_one_of(funcPtr->Class, {FunctionClass::Constructor, FunctionClass::Destructor})); @@ -119,7 +118,7 @@ inheritBaseMembers( derived, [&](SymbolID const& id) { - Info* infoPtr = corpus_.find(id); + Symbol* infoPtr = corpus_.find(id); MRDOCS_CHECK_OR(infoPtr, false); auto& info = *infoPtr; MRDOCS_CHECK_OR(info.Kind == otherInfo.Kind, false); @@ -127,8 +126,8 @@ inheritBaseMembers( { // If it's a function, it's only a shadow if the signatures // are the same - auto const& otherFunc = static_cast(otherInfo); - auto const& func = static_cast(info); + auto const& otherFunc = static_cast(otherInfo); + auto const& func = static_cast(info); return overrides(func, otherFunc); } // For other kinds of members, it's a shadow if the names @@ -153,9 +152,9 @@ inheritBaseMembers( } else { - std::unique_ptr otherCopy = + std::unique_ptr otherCopy = visit(otherInfo, [&](T const& other) - -> std::unique_ptr + -> std::unique_ptr { return std::make_unique(other); }); @@ -167,9 +166,9 @@ inheritBaseMembers( // Get the extraction mode from the derived class if (otherCopy->Extraction == ExtractionMode::Dependency) { - Info* derivedInfoPtr = corpus_.find(derivedId); + Symbol* derivedInfoPtr = corpus_.find(derivedId); MRDOCS_CHECK_OR_CONTINUE(derivedInfoPtr); - Info const& derivedInfo = *derivedInfoPtr; + Symbol const& derivedInfo = *derivedInfoPtr; otherCopy->Extraction = derivedInfo.Extraction; } corpus_.info_.insert(std::move(otherCopy)); @@ -183,7 +182,7 @@ finalizeRecords(std::vector const& ids) { for (SymbolID const& id: ids) { - Info* infoPtr = corpus_.find(id); + Symbol* infoPtr = corpus_.find(id); MRDOCS_CHECK_OR_CONTINUE(infoPtr); auto* record = infoPtr->asRecordPtr(); MRDOCS_CHECK_OR_CONTINUE(record); @@ -197,7 +196,7 @@ finalizeNamespaces(std::vector const& ids) { for (SymbolID const& id: ids) { - Info* infoPtr = corpus_.find(id); + Symbol* infoPtr = corpus_.find(id); MRDOCS_CHECK_OR_CONTINUE(infoPtr); auto* ns = infoPtr->asNamespacePtr(); MRDOCS_CHECK_OR_CONTINUE(ns); @@ -207,7 +206,7 @@ finalizeNamespaces(std::vector const& ids) void BaseMembersFinalizer:: -operator()(NamespaceInfo& I) +operator()(NamespaceSymbol& I) { report::trace( "Extracting base members for namespace '{}'", @@ -218,7 +217,7 @@ operator()(NamespaceInfo& I) void BaseMembersFinalizer:: -operator()(RecordInfo& I) +operator()(RecordSymbol& I) { MRDOCS_CHECK_OR(I.Extraction == ExtractionMode::Regular); report::trace( @@ -227,20 +226,17 @@ operator()(RecordInfo& I) MRDOCS_CHECK_OR(!finalized_.contains(I.id)); for (BaseInfo const& baseI: I.Bases) { - auto const *baseNameType = - dynamic_cast(&*baseI.Type); - MRDOCS_CHECK_OR_CONTINUE(baseNameType); - auto const *baseName = - dynamic_cast(&*baseNameType->Name); - MRDOCS_CHECK_OR_CONTINUE(baseName); - SymbolID baseID = baseName->id; + MRDOCS_ASSERT(!baseI.Type.valueless_after_move()); + MRDOCS_CHECK_OR_CONTINUE(baseI.Type->isNamed()); + auto& baseNameType = baseI.Type->asNamed(); + MRDOCS_ASSERT(!baseNameType.Name.valueless_after_move()); + auto& baseName = baseNameType.Name->asName(); + SymbolID baseID = baseName.id; if (corpus_.config->extractImplicitSpecializations && - baseName->isSpecialization()) + baseName.isSpecialization()) { - auto const *baseSpec = - dynamic_cast(baseName); - MRDOCS_CHECK_OR_CONTINUE(baseSpec); - baseID = baseSpec->specializationID; + auto& baseSpec = baseName.asSpecialization(); + baseID = baseSpec.specializationID; } MRDOCS_CHECK_OR_CONTINUE(baseID); auto basePtr = corpus_.find(baseID); @@ -256,4 +252,4 @@ operator()(RecordInfo& I) finalized_.emplace(I.id); } -} // clang::mrdocs +} // mrdocs diff --git a/src/lib/Metadata/Finalizers/BaseMembersFinalizer.hpp b/src/lib/Metadata/Finalizers/BaseMembersFinalizer.hpp index 1b6659352..e74bf1e79 100644 --- a/src/lib/Metadata/Finalizers/BaseMembersFinalizer.hpp +++ b/src/lib/Metadata/Finalizers/BaseMembersFinalizer.hpp @@ -8,13 +8,13 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_LIB_METADATA_FINALIZER_BASEMEMBERSFINALIZER_HPP -#define MRDOCS_LIB_METADATA_FINALIZER_BASEMEMBERSFINALIZER_HPP +#ifndef MRDOCS_LIB_METADATA_FINALIZERS_BASEMEMBERSFINALIZER_HPP +#define MRDOCS_LIB_METADATA_FINALIZERS_BASEMEMBERSFINALIZER_HPP -#include "lib/Metadata/InfoSet.hpp" -#include "lib/CorpusImpl.hpp" +#include +#include -namespace clang::mrdocs { +namespace mrdocs { /** Finalizes a set of Info. @@ -30,7 +30,7 @@ class BaseMembersFinalizer std::unordered_set finalized_; void - inheritBaseMembers(RecordInfo& I, RecordInfo const& B, AccessKind A); + inheritBaseMembers(RecordSymbol& I, RecordSymbol const& B, AccessKind A); void inheritBaseMembers( @@ -66,21 +66,21 @@ class BaseMembersFinalizer void build() { - Info* info = corpus_.find(SymbolID::global); + Symbol* info = corpus_.find(SymbolID::global); MRDOCS_CHECK_OR(info); operator()(info->asNamespace()); } void - operator()(NamespaceInfo& I); + operator()(NamespaceSymbol& I); void - operator()(RecordInfo& I); + operator()(RecordSymbol& I); void - operator()(Info&) {} + operator()(Symbol&) {} }; -} // clang::mrdocs +} // mrdocs -#endif +#endif // MRDOCS_LIB_METADATA_FINALIZERS_BASEMEMBERSFINALIZER_HPP diff --git a/src/lib/Metadata/Finalizers/DerivedFinalizer.cpp b/src/lib/Metadata/Finalizers/DerivedFinalizer.cpp index 2c106c563..d492682ee 100644 --- a/src/lib/Metadata/Finalizers/DerivedFinalizer.cpp +++ b/src/lib/Metadata/Finalizers/DerivedFinalizer.cpp @@ -8,12 +8,12 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include "DerivedFinalizer.hpp" -#include +#include #include +#include #include -namespace clang::mrdocs { +namespace mrdocs { void DerivedFinalizer:: @@ -30,12 +30,13 @@ build() { MRDOCS_CHECK_OR_CONTINUE(base.Access == AccessKind::Public); MRDOCS_CHECK_OR_CONTINUE(base.Type); + MRDOCS_ASSERT(!base.Type.valueless_after_move()); MRDOCS_CHECK_OR_CONTINUE(base.Type->isNamed()); - auto& namedType = dynamic_cast(*base.Type); + auto& namedType = base.Type->asNamed(); MRDOCS_CHECK_OR_CONTINUE(namedType.Name); SymbolID const namedSymbolID = namedType.Name->id; MRDOCS_CHECK_OR_CONTINUE(namedSymbolID != SymbolID::invalid); - Info* baseInfoPtr = corpus_.find(namedSymbolID); + Symbol* baseInfoPtr = corpus_.find(namedSymbolID); MRDOCS_CHECK_OR_CONTINUE(baseInfoPtr); MRDOCS_CHECK_OR_CONTINUE(baseInfoPtr->isRecord()); MRDOCS_CHECK_OR_CONTINUE(baseInfoPtr->Extraction == ExtractionMode::Regular); @@ -63,4 +64,4 @@ build() } } -} // clang::mrdocs +} // mrdocs diff --git a/src/lib/Metadata/Finalizers/DerivedFinalizer.hpp b/src/lib/Metadata/Finalizers/DerivedFinalizer.hpp index 01b07c973..edb99a218 100644 --- a/src/lib/Metadata/Finalizers/DerivedFinalizer.hpp +++ b/src/lib/Metadata/Finalizers/DerivedFinalizer.hpp @@ -9,20 +9,20 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_LIB_METADATA_FINALIZER_DERIVEDFINALIZER_HPP -#define MRDOCS_LIB_METADATA_FINALIZER_DERIVEDFINALIZER_HPP +#ifndef MRDOCS_LIB_METADATA_FINALIZERS_DERIVEDFINALIZER_HPP +#define MRDOCS_LIB_METADATA_FINALIZERS_DERIVEDFINALIZER_HPP -#include "lib/CorpusImpl.hpp" -#include "lib/Metadata/InfoSet.hpp" +#include +#include #include -namespace clang::mrdocs { +namespace mrdocs { /** Finalizes a set of Info. This finalizer finds non-member functions for a record and populate the related - field of the javadoc. + field of the doc. */ class DerivedFinalizer { @@ -37,6 +37,6 @@ class DerivedFinalizer build(); }; -} // clang::mrdocs +} // mrdocs -#endif +#endif // MRDOCS_LIB_METADATA_FINALIZERS_DERIVEDFINALIZER_HPP diff --git a/src/lib/Metadata/Finalizers/Javadoc/Function.hpp b/src/lib/Metadata/Finalizers/DocComment/Function.hpp similarity index 63% rename from src/lib/Metadata/Finalizers/Javadoc/Function.hpp rename to src/lib/Metadata/Finalizers/DocComment/Function.hpp index a8b0c3f24..dd0168472 100644 --- a/src/lib/Metadata/Finalizers/Javadoc/Function.hpp +++ b/src/lib/Metadata/Finalizers/DocComment/Function.hpp @@ -8,19 +8,19 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_LIB_METADATA_FINALIZER_JAVADOCFINALIZER_FUNCTION_HPP -#define MRDOCS_LIB_METADATA_FINALIZER_JAVADOCFINALIZER_FUNCTION_HPP +#ifndef MRDOCS_LIB_METADATA_FINALIZERS_DOCCOMMENT_FUNCTION_HPP +#define MRDOCS_LIB_METADATA_FINALIZERS_DOCCOMMENT_FUNCTION_HPP -#include "lib/CorpusImpl.hpp" -#include "lib/Metadata/InfoSet.hpp" -#include +#include +#include #include +#include #include -namespace clang::mrdocs { +namespace mrdocs { namespace { bool -isSpecialFunction(FunctionInfo const& I) +isSpecialFunction(FunctionSymbol const& I) { return I.Class != FunctionClass::Normal || @@ -28,14 +28,14 @@ isSpecialFunction(FunctionInfo const& I) } bool -isDefaultConstructor(FunctionInfo const& I) +isDefaultConstructor(FunctionSymbol const& I) { return I.Class == FunctionClass::Constructor && I.Params.empty(); } template bool -isCopyOrMoveConstructorOrAssignment(FunctionInfo const& I) +isCopyOrMoveConstructorOrAssignment(FunctionSymbol const& I) { if constexpr (!assignment) { @@ -47,8 +47,8 @@ isCopyOrMoveConstructorOrAssignment(FunctionInfo const& I) } MRDOCS_CHECK_OR(I.Params.size() == 1, false); auto const& param = I.Params[0]; - auto const& paramType = param.Type; - MRDOCS_CHECK_OR(paramType, false); + Polymorphic const& paramType = param.Type; + MRDOCS_ASSERT(!paramType.valueless_after_move()); if constexpr (!move) { MRDOCS_CHECK_OR(paramType->isLValueReference(), false); @@ -57,18 +57,17 @@ isCopyOrMoveConstructorOrAssignment(FunctionInfo const& I) { MRDOCS_CHECK_OR(paramType->isRValueReference(), false); } - using RefType = std::conditional_t; + using RefType = std::conditional_t; auto const& paramRefType = static_cast(*paramType); - auto const& paramRefPointee = paramRefType.PointeeType; - MRDOCS_CHECK_OR(paramRefPointee, false); - auto const *paramRefPointeeNamed = - static_cast(&*paramRefPointee); - if (!paramRefPointeeNamed) - return false; + auto const& paramRefPointeeOpt = paramRefType.PointeeType; + MRDOCS_CHECK_OR(paramRefPointeeOpt, false); + Type const& paramRefPointee = *paramRefPointeeOpt; + auto const *paramRefPointeeNamed = paramRefPointee.asNamedPtr(); + MRDOCS_CHECK_OR(paramRefPointeeNamed, false); MRDOCS_CHECK_OR(paramRefPointeeNamed->Name, false); auto const& paramName = paramRefPointeeNamed->Name; MRDOCS_CHECK_OR(paramName, false); - auto const& paramRefPointeeNamedName = paramName->Name; + auto const& paramRefPointeeNamedName = paramName->Identifier; MRDOCS_CHECK_OR(!paramRefPointeeNamedName.empty(), false); SymbolID const& id = paramName->id; MRDOCS_CHECK_OR(id, false); @@ -76,43 +75,50 @@ isCopyOrMoveConstructorOrAssignment(FunctionInfo const& I) } bool -isCopyConstructor(FunctionInfo const& I) +isCopyConstructor(FunctionSymbol const& I) { return isCopyOrMoveConstructorOrAssignment(I); } bool -isMoveConstructor(FunctionInfo const& I) +isMoveConstructor(FunctionSymbol const& I) { return isCopyOrMoveConstructorOrAssignment(I); } bool -isCopyAssignment(FunctionInfo const& I) +isCopyAssignment(FunctionSymbol const& I) { return isCopyOrMoveConstructorOrAssignment(I); } bool -isMoveAssignment(FunctionInfo const& I) +isMoveAssignment(FunctionSymbol const& I) { return isCopyOrMoveConstructorOrAssignment(I); } -std::optional -innermostTypenameString(Polymorphic const& T) +Optional +innermostTypenameString(Polymorphic const& T) { auto& R = innermostType(T); MRDOCS_CHECK_OR(R->isNamed(), {}); - MRDOCS_CHECK_OR(static_cast(*R).Name, {}); - MRDOCS_CHECK_OR(!static_cast(*R).Name->Name.empty(), + MRDOCS_CHECK_OR(dynamic_cast(*R).Name, {}); + MRDOCS_CHECK_OR(!dynamic_cast(*R).Name->Identifier.empty(), {}); - auto& RStr = static_cast(*R).Name->Name; + auto& RStr = dynamic_cast(*R).Name->Identifier; return RStr; } +//Optional +//innermostTypenameString(Optional> const& T) +//{ +// MRDOCS_CHECK_OR(T, {}); +// return innermostTypenameString(*T); +//} + bool -populateFunctionBriefFromClass(FunctionInfo& I, CorpusImpl const& corpus) +populateFunctionBriefFromClass(FunctionSymbol& I, CorpusImpl const& corpus) { switch (I.Class) { @@ -122,17 +128,17 @@ populateFunctionBriefFromClass(FunctionInfo& I, CorpusImpl const& corpus) { if (isDefaultConstructor(I)) { - I.javadoc->brief = "Default constructor"; + I.doc->brief = "Default constructor"; return true; } if (isCopyConstructor(I)) { - I.javadoc->brief = "Copy constructor"; + I.doc->brief = "Copy constructor"; return true; } if (isMoveConstructor(I)) { - I.javadoc->brief = "Move constructor"; + I.doc->brief = "Move constructor"; return true; } if (I.Params.size() == 1) @@ -140,27 +146,22 @@ populateFunctionBriefFromClass(FunctionInfo& I, CorpusImpl const& corpus) auto const& param = I.Params[0]; if (auto const res = innermostTypenameString(param.Type)) { - if (!I.javadoc->brief) + if (!I.doc->brief) { - I.javadoc->brief.emplace(); + I.doc->brief.emplace(); } - I.javadoc->brief->children.clear(); - I.javadoc->brief->children.emplace_back( - Polymorphic(std::in_place_type, - std::string("Construct from "))); - I.javadoc->brief->children.emplace_back( - Polymorphic(std::in_place_type, - std::string(*res), - doc::Style::mono)); + I.doc->brief->clear(); + I.doc->brief->append("Construct from "); + I.doc->brief->append(*res); return true; } } - I.javadoc->brief = "Constructor"; + I.doc->brief = "Constructor"; return true; } case FunctionClass::Destructor: { - I.javadoc->brief = "Destructor"; + I.doc->brief = "Destructor"; return true; } case FunctionClass::Conversion: @@ -168,17 +169,13 @@ populateFunctionBriefFromClass(FunctionInfo& I, CorpusImpl const& corpus) if (auto const R = innermostTypenameString(I.ReturnType)) { auto const RStr = *R; - I.javadoc->brief.emplace(); - I.javadoc->brief->children.emplace_back( - Polymorphic(std::in_place_type, - std::string("Conversion to "))); - I.javadoc->brief->children.emplace_back(Polymorphic( - std::in_place_type, std::string(RStr), - doc::Style::mono)); + I.doc->brief.emplace(); + I.doc->brief->emplace_back("Conversion to "); + I.doc->brief->emplace_back(RStr); } else { - I.javadoc->brief = "Conversion operator"; + I.doc->brief = "Conversion operator"; } return true; } @@ -187,48 +184,53 @@ populateFunctionBriefFromClass(FunctionInfo& I, CorpusImpl const& corpus) } } +// Check if the function is a stream insertion operator by +// checking if it is a non-member function with two parameters +// where the first parameter is a mutable reference to a named type +// and the return type is the same as the first parameter type. bool -isStreamInsertion(FunctionInfo const& function) +isStreamInsertion(FunctionSymbol const& function) { MRDOCS_CHECK_OR(!function.IsRecordMethod, false); MRDOCS_CHECK_OR(function.Params.size() == 2, false); MRDOCS_CHECK_OR(function.OverloadedOperator == OperatorKind::LessLess, false); - // Check first param is mutable left reference + // Check first param is a mutable left reference auto& firstParam = function.Params[0]; MRDOCS_CHECK_OR(firstParam, false); - auto& firstQualType = firstParam.Type; - MRDOCS_CHECK_OR(firstQualType, false); + Polymorphic const& firstQualType = firstParam.Type; + MRDOCS_ASSERT(!firstQualType.valueless_after_move()); MRDOCS_CHECK_OR(firstQualType->isLValueReference(), false); - auto& firstNamedType = - static_cast(*firstQualType) + auto& firstNamedTypeOpt = + dynamic_cast(*firstQualType) .PointeeType; - MRDOCS_CHECK_OR(firstNamedType, false); - MRDOCS_CHECK_OR(firstNamedType->isNamed(), false); + MRDOCS_CHECK_OR(firstNamedTypeOpt, false); + auto& firstNamedType = *firstNamedTypeOpt; + MRDOCS_CHECK_OR(firstNamedType.isNamed(), false); // Check return type return firstQualType == function.ReturnType; } bool -populateFunctionBriefFromOperator(FunctionInfo& I) +populateFunctionBriefFromOperator(FunctionSymbol& I) { MRDOCS_CHECK_OR(I.OverloadedOperator != OperatorKind::None, false); // Stream insertion operators are implemented as an exception to the operator name if (isStreamInsertion(I)) { - I.javadoc->brief = "Stream insertion operator"; + I.doc->brief = "Stream insertion operator"; return true; } if (isCopyAssignment(I)) { - I.javadoc->brief = "Copy assignment operator"; + I.doc->brief = "Copy assignment operator"; return true; } if (isMoveAssignment(I)) { - I.javadoc->brief = "Move assignment operator"; + I.doc->brief = "Move assignment operator"; return true; } @@ -240,53 +242,51 @@ populateFunctionBriefFromOperator(FunctionInfo& I) MRDOCS_CHECK_OR(res, false); std::string briefStr(*res); briefStr += " operator"; - I.javadoc->brief = std::move(briefStr); + I.doc->brief = std::move(briefStr); return true; } void -populateFunctionBrief(FunctionInfo& I, CorpusImpl const& corpus) +populateFunctionBrief(FunctionSymbol& I, CorpusImpl const& corpus) { - MRDOCS_CHECK_OR(!I.javadoc->brief); + MRDOCS_CHECK_OR(!I.doc->brief); MRDOCS_CHECK_OR(!populateFunctionBriefFromClass(I, corpus)); MRDOCS_CHECK_OR(!populateFunctionBriefFromOperator(I)); } - - -Info const* +Symbol const* getInfo( - Polymorphic const& R, + Polymorphic const& R, CorpusImpl const& corpus) { SymbolID const id = R->namedSymbol(); MRDOCS_CHECK_OR(id, nullptr); - Info const* Rinfo = corpus.find(id); + Symbol const* Rinfo = corpus.find(id); return Rinfo; } -doc::Brief const* +doc::BriefBlock const* getInfoBrief( - Polymorphic const& R, + Polymorphic const& R, CorpusImpl const& corpus) { - Info const* Rinfo = getInfo(R, corpus); + Symbol const* Rinfo = getInfo(R, corpus); MRDOCS_CHECK_OR(Rinfo, nullptr); - MRDOCS_CHECK_OR(Rinfo->javadoc, nullptr); - MRDOCS_CHECK_OR(Rinfo->javadoc->brief, nullptr); - return &*Rinfo->javadoc->brief; + MRDOCS_CHECK_OR(Rinfo->doc, nullptr); + MRDOCS_CHECK_OR(Rinfo->doc->brief, nullptr); + return &*Rinfo->doc->brief; } bool populateFunctionReturnsFromFunctionBrief( - FunctionInfo& I) + FunctionSymbol& I) { - MRDOCS_CHECK_OR(I.javadoc->brief, false); - MRDOCS_CHECK_OR(I.javadoc->brief->children.size() == 1, false); - auto& brief = I.javadoc->brief->children[0]; - MRDOCS_CHECK_OR(brief->Kind == doc::NodeKind::text, false); - std::string_view briefText = brief->string; + MRDOCS_CHECK_OR(I.doc->brief, false); + MRDOCS_CHECK_OR(I.doc->brief->children.size() == 1, false); + auto& briefInline = I.doc->brief->children[0]; + MRDOCS_CHECK_OR(briefInline->Kind == doc::InlineKind::Text, false); + std::string_view briefText = briefInline->asText().literal; static constexpr std::string_view briefPrefixes[] = { "Returns ", "Return ", @@ -300,37 +300,33 @@ populateFunctionReturnsFromFunctionBrief( if (briefText.starts_with(prefix)) { briefText.remove_prefix(prefix.size()); - I.javadoc->returns.emplace_back(std::string(briefText)); + I.doc->returns.emplace_back(briefText); return true; } } return false; } - bool populateFunctionReturnsForSpecial( - FunctionInfo& I, - Polymorphic const& innerR, + FunctionSymbol& I, + Polymorphic const& innerR, CorpusImpl const& corpus) { if (I.Class == FunctionClass::Conversion) { if (auto* brief = getInfoBrief(innerR, corpus)) { - I.javadoc->returns.emplace_back(*brief); + doc::ReturnsBlock r; + r.children = brief->children; + I.doc->returns.emplace_back(std::move(r)); return true; } auto const exp = innermostTypenameString(innerR); MRDOCS_CHECK_OR(exp, false); - doc::Returns r; - r.children.emplace_back( - Polymorphic(std::in_place_type, - std::string("The object converted to "))); - r.children.emplace_back( - Polymorphic(std::in_place_type, - std::string(*exp), doc::Style::mono)); - I.javadoc->returns.emplace_back(std::move(r)); + doc::ReturnsBlock r("The object converted to "); + r.emplace_back(std::string(*exp)); + I.doc->returns.emplace_back(std::move(r)); return true; } MRDOCS_CHECK_OR(I.OverloadedOperator != OperatorKind::None, false); @@ -338,27 +334,26 @@ populateFunctionReturnsForSpecial( // Stream operator if (isStreamInsertion(I)) { - I.javadoc->returns.emplace_back("Reference to the current output stream"); + I.doc->returns.emplace_back("Reference to the current output stream"); return true; } // Special functions that should return a reference to self + MRDOCS_ASSERT(!I.ReturnType.valueless_after_move()); if (I.ReturnType->isLValueReference()) { - Info const* RInfo = getInfo(innerR, corpus); + Symbol const* RInfo = getInfo(innerR, corpus); MRDOCS_CHECK_OR(RInfo, false); MRDOCS_CHECK_OR(RInfo->id == I.Parent, false); - I.javadoc->returns.emplace_back("Reference to the current object"); + I.doc->returns.emplace_back("Reference to the current object"); return true; } - - // Special functions that should return a pointer to self - if (I.ReturnType->isPointer()) + else if (I.ReturnType->isPointer()) { - Info const* RInfo = getInfo(innerR, corpus); + Symbol const* RInfo = getInfo(innerR, corpus); MRDOCS_CHECK_OR(RInfo, false); MRDOCS_CHECK_OR(RInfo->id == I.Parent, false); - I.javadoc->returns.emplace_back("Pointer to the current object"); + I.doc->returns.emplace_back("Pointer to the current object"); return true; } @@ -373,15 +368,14 @@ populateFunctionReturnsForSpecial( OperatorKind::GreaterEqual, OperatorKind::Exclaim })) { + MRDOCS_CHECK_OR(I.ReturnType, false); MRDOCS_CHECK_OR(I.ReturnType->isNamed(), false); MRDOCS_CHECK_OR( - static_cast(*I.ReturnType).FundamentalType == + dynamic_cast(*I.ReturnType).FundamentalType == FundamentalTypeKind::Bool, false); - doc::Returns r; - r.children.emplace_back( - Polymorphic(std::in_place_type, - std::string("true"), doc::Style::mono)); + doc::ReturnsBlock r; + r.emplace_back("true"); std::string_view midText; switch (I.OverloadedOperator) { @@ -409,46 +403,45 @@ populateFunctionReturnsForSpecial( default: MRDOCS_UNREACHABLE(); } - r.children.emplace_back(Polymorphic( - std::in_place_type, std::string(midText))); - r.children.emplace_back( - Polymorphic(std::in_place_type, - std::string("false"), doc::Style::mono)); - r.children.emplace_back(Polymorphic( - std::in_place_type, std::string(" otherwise"))); - I.javadoc->returns.emplace_back(std::move(r)); + r += midText; + r += doc::CodeInline("false"); + r += " otherwise"; + I.doc->returns.emplace_back(std::move(r)); return false; } // Spaceship operator if (I.OverloadedOperator == OperatorKind::Spaceship) { - I.javadoc->returns.emplace_back("The relative order of the objects"); + I.doc->returns.emplace_back("The relative order of the objects"); return true; } // Special function that return the same type as the parent - if (I.IsRecordMethod && innerR->isNamed() && - static_cast(*innerR).Name && - static_cast(*innerR).Name->id && - static_cast(*innerR).Name->id == I.Parent) + MRDOCS_ASSERT(!innerR.valueless_after_move()); + if (I.IsRecordMethod && + innerR->isNamed() && + dynamic_cast(*innerR).Name->id && + dynamic_cast(*innerR).Name->id == I.Parent) { + MRDOCS_CHECK_OR(I.ReturnType, false); + MRDOCS_ASSERT(!I.ReturnType.valueless_after_move()); if (I.ReturnType->isLValueReference()) { - I.javadoc->returns.emplace_back("Reference to the current object"); + I.doc->returns.emplace_back("Reference to the current object"); } else if (I.ReturnType->isRValueReference()) { - I.javadoc->returns.emplace_back( + I.doc->returns.emplace_back( "Rvalue reference to the current object"); } else if (I.ReturnType->isPointer()) { - I.javadoc->returns.emplace_back("Pointer to the current object"); + I.doc->returns.emplace_back("Pointer to the current object"); } else { - I.javadoc->returns.emplace_back("Another instance of the object"); + I.doc->returns.emplace_back("Another instance of the object"); } return true; } @@ -458,22 +451,22 @@ populateFunctionReturnsForSpecial( bool populateFunctionReturnsFromReturnTypeBrief( - FunctionInfo& I, - Polymorphic const& innerR, + FunctionSymbol& I, + Polymorphic const& innerR, CorpusImpl const& corpus) { if (auto* brief = getInfoBrief(innerR, corpus)) { - I.javadoc->returns.emplace_back(*brief); + I.doc->returns.emplace_back(*brief); return true; } return false; } void -populateFunctionReturns(FunctionInfo& I, CorpusImpl const& corpus) +populateFunctionReturns(FunctionSymbol& I, CorpusImpl const& corpus) { - MRDOCS_CHECK_OR(I.javadoc->returns.empty()); + MRDOCS_CHECK_OR(I.doc->returns.empty()); // Populate the return doc from brief of the function // when the brief is "Returns ..." @@ -481,13 +474,14 @@ populateFunctionReturns(FunctionInfo& I, CorpusImpl const& corpus) // Check if we have a named return type MRDOCS_CHECK_OR(I.ReturnType); + MRDOCS_ASSERT(!I.ReturnType.valueless_after_move()); auto& inner = innermostType(I.ReturnType); MRDOCS_CHECK_OR(inner); if (inner->isNamed()) { - auto& Ninner = static_cast(*inner); + auto& Ninner = dynamic_cast(*inner); MRDOCS_CHECK_OR(Ninner.Name); - MRDOCS_CHECK_OR(!Ninner.Name->Name.empty()); + MRDOCS_CHECK_OR(!Ninner.Name->Identifier.empty()); MRDOCS_CHECK_OR(Ninner.FundamentalType != FundamentalTypeKind::Void); } @@ -499,19 +493,19 @@ populateFunctionReturns(FunctionInfo& I, CorpusImpl const& corpus) MRDOCS_CHECK_OR(!populateFunctionReturnsFromReturnTypeBrief(I, inner, corpus)); } -/* Get a list of all parameter names in javadoc +/* Get a list of all parameter names in doc - The javadoc parameter names can contain a single parameter or + The doc parameter names can contain a single parameter or a list of parameters separated by commas. This function - returns a list of all parameter names in the javadoc. + returns a list of all parameter names in the doc. */ -SmallVector -getJavadocParamNames(Javadoc const& javadoc) +llvm::SmallVector +getDocCommentParamNames(DocComment const& doc) { - SmallVector result; - for (auto const& javadocParam: javadoc.params) + llvm::SmallVector result; + for (auto const& docParam: doc.params) { - auto const& paramNamesStr = javadocParam.name; + auto const& paramNamesStr = docParam.name; for (auto paramNames = std::views::split(paramNamesStr, ','); auto const& paramName: paramNames) { @@ -523,7 +517,7 @@ getJavadocParamNames(Javadoc const& javadoc) bool setCntrOrAssignParamName( - FunctionInfo& I, + FunctionSymbol& I, Param& param, std::size_t const index) { @@ -534,11 +528,16 @@ setCntrOrAssignParamName( I.Class == FunctionClass::Constructor || I.OverloadedOperator == OperatorKind::Equal, false); - auto paramNames = std::views:: - transform(I.Params, [](Param const& p) -> std::string_view { - return *p.Name; - }); - auto& innerP = innermostType(param.Type); + auto paramNames = + std::views::filter(I.Params, [](Param const& p) -> bool { + return p.Name.has_value(); + }) | + std::views::transform([](Param const& p) -> std::string_view { + return *p.Name; + }); + MRDOCS_CHECK_OR(param.Type, false); + MRDOCS_ASSERT(!param.Type.valueless_after_move()); + Polymorphic& innerP = innermostType(param.Type); std::string_view paramName = "value"; if (innerP->namedSymbol() == I.Parent) { @@ -551,14 +550,17 @@ setCntrOrAssignParamName( bool setStreamOperatorParamName( - FunctionInfo& I, + FunctionSymbol& I, Param& param, std::size_t const index) { MRDOCS_CHECK_OR(index < 2, false); MRDOCS_CHECK_OR(isStreamInsertion(I), false); - auto paramNames = std::views:: - transform(I.Params, [](Param const& p) -> std::string_view { + auto paramNames = + std::views::filter(I.Params, [](Param const& p) -> bool { + return p.Name.has_value(); + }) | + std::views::transform([](Param const& p) -> std::string_view { return *p.Name; }); std::string_view paramName = @@ -570,7 +572,7 @@ setStreamOperatorParamName( bool setBinaryOpParamName( - FunctionInfo& I, + FunctionSymbol& I, Param& param, std::size_t const index) { @@ -579,8 +581,11 @@ setBinaryOpParamName( std::size_t const sizeFree = I.IsRecordMethod ? I.Params.size() + 1 : I.Params.size(); MRDOCS_CHECK_OR(sizeFree == 2, false); - auto const paramNames = std::views:: - transform(I.Params, [](Param const& p) -> std::string_view { + auto paramNames = + std::views::filter(I.Params, [](Param const& p) -> bool { + return p.Name.has_value(); + }) | + std::views::transform([](Param const& p) -> std::string_view { return *p.Name; }); std::size_t const indexFree = I.IsRecordMethod ? index + 1 : index; @@ -592,7 +597,7 @@ setBinaryOpParamName( bool setUnaryOpParamName( - FunctionInfo& I, + FunctionSymbol& I, Param& param, std::size_t const index) { @@ -601,8 +606,11 @@ setUnaryOpParamName( MRDOCS_CHECK_OR(isUnaryOperator(I.OverloadedOperator), false); MRDOCS_CHECK_OR(I.Params.size() == 1, false); - auto const paramNames = std::views:: - transform(I.Params, [](Param const& p) -> std::string_view { + auto paramNames = + std::views::filter(I.Params, [](Param const& p) -> bool { + return p.Name.has_value(); + }) | + std::views::transform([](Param const& p) -> std::string_view { return *p.Name; }); std::string_view paramName = "value"; @@ -613,7 +621,7 @@ setUnaryOpParamName( bool setSpecialFunctionParamName( - FunctionInfo& I, + FunctionSymbol& I, Param& param, std::size_t index) { @@ -626,7 +634,7 @@ setSpecialFunctionParamName( bool setCntrOrAssignParamDoc( - FunctionInfo& I, + FunctionSymbol& I, Param& param, std::size_t const index) { @@ -639,18 +647,21 @@ setCntrOrAssignParamDoc( // Set the parameter description if we can MRDOCS_CHECK_OR(param, false); + MRDOCS_CHECK_OR(param.Type, false); + MRDOCS_ASSERT(!param.Type.valueless_after_move()); auto& innerParam = innermostType(param.Type); MRDOCS_CHECK_OR(innerParam, false); MRDOCS_CHECK_OR(innerParam->isNamed(), false); std::string_view const paramNoun - = static_cast(*innerParam).FundamentalType ? "value" + = dynamic_cast(*innerParam).FundamentalType ? "value" : "object"; std::string_view const functionVerb = [&]() { bool const isAssign = I.OverloadedOperator == OperatorKind::Equal; - if (static_cast(*innerParam).FundamentalType) + if (dynamic_cast(*innerParam).FundamentalType) { return !isAssign ? "construct" : "assign"; } + MRDOCS_ASSERT(!param.Type.valueless_after_move()); if (param.Type->isLValueReference()) { return !isAssign ? "copy construct" : "copy assign"; @@ -661,14 +672,15 @@ setCntrOrAssignParamDoc( } return !isAssign ? "construct" : "assign"; }(); - I.javadoc->params.emplace_back( - *param.Name, std::format("The {} to {} from", paramNoun, functionVerb)); + I.doc->params.emplace_back( + *param.Name, + std::format("The {} to {} from", paramNoun, functionVerb)); return true; } bool setBinaryOpParamDoc( - FunctionInfo& I, + FunctionSymbol& I, Param& param, std::size_t const index) { @@ -680,14 +692,14 @@ setBinaryOpParamDoc( // Set the parameter description if we can std::string_view const paramAdj = indexFree == 0 ? "left" : "right"; - I.javadoc->params.emplace_back(*param.Name, + I.doc->params.emplace_back(*param.Name, std::format("The {} operand", paramAdj)); return true; } bool setUnaryOpParamDoc( - FunctionInfo& I, + FunctionSymbol& I, Param& param, std::size_t const index) { @@ -697,13 +709,13 @@ setUnaryOpParamDoc( MRDOCS_CHECK_OR(I.Params.size() == 1, false); // Set the parameter description if we can - I.javadoc->params.emplace_back(*param.Name, "The operand"); + I.doc->params.emplace_back(*param.Name, "The operand"); return true; } bool setStreamOperatorParamDoc( - FunctionInfo& I, + FunctionSymbol& I, Param& param, std::size_t const index) { @@ -711,18 +723,18 @@ setStreamOperatorParamDoc( MRDOCS_CHECK_OR(isStreamInsertion(I), false); if (index == 0) { - I.javadoc->params.emplace_back(*param.Name, "An output stream"); + I.doc->params.emplace_back(*param.Name, "An output stream"); } else { - I.javadoc->params.emplace_back(*param.Name, "The object to output"); + I.doc->params.emplace_back(*param.Name, "The object to output"); } return true; } void setFunctionParamDoc( - FunctionInfo& I, + FunctionSymbol& I, Param& param, std::size_t const index, CorpusImpl const& corpus) @@ -739,7 +751,6 @@ setFunctionParamDoc( { return; } - if (setUnaryOpParamDoc(I, param, index)) { return; @@ -747,18 +758,21 @@ setFunctionParamDoc( // param is a named parameter: use the brief of the type // as a description for the parameter + MRDOCS_ASSERT(!param.Type.valueless_after_move()); auto const& innerParam = innermostType(param.Type); - doc::Brief const* paramBrief = getInfoBrief(innerParam, corpus); + doc::BriefBlock const* paramBrief = getInfoBrief(innerParam, corpus); MRDOCS_CHECK_OR(paramBrief); - I.javadoc->params.emplace_back(*param.Name, *paramBrief); + doc::ParamBlock p(paramBrief->asInlineContainer()); + p.name = *param.Name; + I.doc->params.emplace_back(std::move(p)); } void populateFunctionParam( - FunctionInfo& I, + FunctionSymbol& I, Param& param, std::size_t const index, - SmallVector& documentedParams, + llvm::SmallVector& documentedParams, CorpusImpl const& corpus) { if (!param.Name) @@ -774,9 +788,9 @@ populateFunctionParam( } void -populateFunctionParams(FunctionInfo& I, CorpusImpl const& corpus) +populateFunctionParams(FunctionSymbol& I, CorpusImpl const& corpus) { - auto documentedParams = getJavadocParamNames(*I.javadoc); + auto documentedParams = getDocCommentParamNames(*I.doc); for (std::size_t i = 0; i < I.Params.size(); ++i) { populateFunctionParam(I, I.Params[i], i, documentedParams, corpus); @@ -784,6 +798,6 @@ populateFunctionParams(FunctionInfo& I, CorpusImpl const& corpus) } } -} // clang::mrdocs +} // mrdocs -#endif +#endif // MRDOCS_LIB_METADATA_FINALIZERS_DOCCOMMENT_FUNCTION_HPP diff --git a/src/lib/Metadata/Finalizers/DocComment/Overloads.hpp b/src/lib/Metadata/Finalizers/DocComment/Overloads.hpp new file mode 100644 index 000000000..c48ba576f --- /dev/null +++ b/src/lib/Metadata/Finalizers/DocComment/Overloads.hpp @@ -0,0 +1,380 @@ +// +// 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_METADATA_FINALIZERS_DOCCOMMENT_OVERLOADS_HPP +#define MRDOCS_LIB_METADATA_FINALIZERS_DOCCOMMENT_OVERLOADS_HPP + +#include +#include +#include +#include + +namespace mrdocs { +namespace { + +// Create a view of all functions in an overload set +auto +overloadFunctionsRange(OverloadsSymbol const& O, CorpusImpl& corpus_) +{ + // Create a view all Info members of O + auto functions = + O.Members | + std::views::transform([&](SymbolID const& id) + { + return corpus_.find(id); + }) | + std::views::filter([](Symbol const* infoPtr) + { + return infoPtr != nullptr && infoPtr->isFunction(); + }) | + std::views::transform([](Symbol const* infoPtr) -> FunctionSymbol const& + { + return infoPtr->asFunction(); + }); + return functions; +} + +template +bool +populateOverloadsBriefIfAllSameBrief(OverloadsSymbol& I, Range&& functionsWithBrief) +{ + auto first = *functionsWithBrief.begin(); + doc::BriefBlock const& firstBrief = *first.doc->brief; + if (auto otherFunctions = std::views::drop(functionsWithBrief, 1); + std::ranges::all_of(otherFunctions, [&](FunctionSymbol const& otherFunction) + { + doc::BriefBlock const& otherBrief = *otherFunction.doc->brief; + return otherBrief.children == firstBrief.children; + })) + { + I.doc->brief = firstBrief; + return true; + } + return false; +} + +bool +populateOverloadsFromClass(OverloadsSymbol& I) +{ + switch (I.Class) + { + case FunctionClass::Normal: + return false; + case FunctionClass::Constructor: + { + I.doc->brief = "Constructors"; + return true; + } + case FunctionClass::Destructor: + { + I.doc->brief = "Destructors"; + return true; + } + case FunctionClass::Conversion: + { + I.doc->brief = "Conversion operators"; + return true; + } + default: + MRDOCS_UNREACHABLE(); + } +} + +template +bool +populateOverloadsFromOperator(OverloadsSymbol& I, Range&& functions) +{ + MRDOCS_CHECK_OR(I.OverloadedOperator != OperatorKind::None, false); + + // Stream insertion operators are implemented as an exception to the operator name + if (I.OverloadedOperator == OperatorKind::LessLess && + std::ranges::all_of(functions, isStreamInsertion)) + { + I.doc->brief = "Stream insertion operators"; + return true; + } + + // Find the operator name + auto isBinary = [&](FunctionSymbol const& function) { + return (function.Params.size() + function.IsRecordMethod) == 2; + }; + auto isAllBinary = std::ranges::all_of(functions, isBinary); + int const nParams = isAllBinary ? 2 : 1; + auto const res = getOperatorReadableName(I.OverloadedOperator, nParams); + MRDOCS_CHECK_OR(res, false); + std::string briefStr(*res); + briefStr += " operators"; + I.doc->brief = std::move(briefStr); + return true; +} + +bool +populateOverloadsFromFunctionName(OverloadsSymbol& I) +{ + std::string name = I.Name; + if (name.empty() && + I.OverloadedOperator != OperatorKind::None) + { + name = getOperatorName(I.OverloadedOperator, true); + } + if (name.empty()) + { + return false; + } + I.doc->brief.emplace(); + I.doc->brief->emplace_back(name); + I.doc->brief->children.emplace_back( + std::in_place_type, std::string(" overloads")); + return true; +} + +// Populate the brief of an overload set according to the following rules: +// 1. If all functions have the same brief, use that brief +// 2. Otherwise, if the overload set is for a special function (constructor, destructor +// or conversion operator), use a generic brief according to the function class +// 3. Otherwise, if the overload set is for an operator, use a generic brief +// according to the operator name +// 4. Otherwise, if any function has a brief, use the function name as the brief +// 5. Otherwise, do not populate the brief +// +template +void +populateOverloadsBrief(OverloadsSymbol& I, Range&& functions, CorpusImpl& corpus) +{ + auto functionsWithBrief = std::views:: + filter(functions, [](FunctionSymbol const& function) { + return function.doc && function.doc->brief + && !function.doc->brief->empty(); + }); + auto anyMemberBrief = !std::ranges::empty(functionsWithBrief); + if (!corpus.config->autoFunctionMetadata && + !anyMemberBrief) + { + // If there are no briefs, and we'll not populate the briefs + // from function names, we'll also not populate the briefs + // of the overload set. + return; + } + if (anyMemberBrief) + { + MRDOCS_CHECK_OR(!populateOverloadsBriefIfAllSameBrief(I, functionsWithBrief)); + } + MRDOCS_CHECK_OR(!populateOverloadsFromClass(I)); + MRDOCS_CHECK_OR(!populateOverloadsFromOperator(I, functions)); + if (anyMemberBrief) + { + // We recur to the function name when the briefs are in conflict + // If there are no briefs, we don't consider it a conflict + // We just leave the overload set also without a brief + MRDOCS_CHECK_OR(!populateOverloadsFromFunctionName(I)); + } +} + +// Populate with all the unique "returns" from the functions +template +void +populateOverloadsReturns(OverloadsSymbol& I, Range&& functions) { + auto functionReturns = functions | + std::views::filter([](FunctionSymbol const& function) + { + return function.doc && !function.doc->returns.empty(); + }) | + std::views::transform([](FunctionSymbol const& function) + { + return function.doc->returns; + }) | + std::views::join; + for (doc::ReturnsBlock const& functionReturn: functionReturns) + { + auto sameIt = std::ranges::find_if( + I.doc->returns, + [&functionReturn](doc::ReturnsBlock const& overloadReturns) + { + return overloadReturns == functionReturn; + }); + if (sameIt == I.doc->returns.end()) + { + I.doc->returns.push_back(functionReturn); + } + } +} + +template +void +populateOverloadsParams(OverloadsSymbol& I, Range& functions) { + auto functionParams = functions | + std::views::filter([](FunctionSymbol const& function) + { + return function.doc && !function.doc->params.empty(); + }) | + std::views::transform([](FunctionSymbol const& function) + { + return function.doc->params; + }) | + std::views::join; + for (doc::ParamBlock const& functionParam: functionParams) + { + auto sameIt = std::ranges::find_if( + I.doc->params, + [&functionParam](doc::ParamBlock const& overloadParam) + { + return overloadParam.name == functionParam.name; + }); + if (sameIt == I.doc->params.end()) + { + I.doc->params.push_back(functionParam); + } + } +} + +template +void +populateOverloadsTParams(OverloadsSymbol& I, Range& functions) { + auto functionTParams = functions | + std::views::filter([](FunctionSymbol const& function) + { + return function.doc && !function.doc->tparams.empty(); + }) | + std::views::transform([](FunctionSymbol const& function) + { + return function.doc->tparams; + }) | + std::views::join; + for (doc::TParamBlock const& functionTParam: functionTParams) + { + auto sameIt = std::ranges::find_if( + I.doc->tparams, + [&functionTParam](doc::TParamBlock const& overloadTParam) + { + return overloadTParam.name == functionTParam.name; + }); + if (sameIt == I.doc->tparams.end()) + { + I.doc->tparams.push_back(functionTParam); + } + } +} + +template +void +populateOverloadsExceptions(OverloadsSymbol& I, Range& functions) { + auto functionExceptions = functions | + std::views::filter([](FunctionSymbol const& function) + { + return function.doc && !function.doc->exceptions.empty(); + }) | + std::views::transform([](FunctionSymbol const& function) + { + return function.doc->exceptions; + }) | + std::views::join; + for (doc::ThrowsBlock const& functionException: functionExceptions) + { + auto sameIt = std::ranges::find_if( + I.doc->exceptions, + [&functionException](doc::ThrowsBlock const& overloadException) + { + return overloadException.exception.literal == functionException.exception.literal; + }); + if (sameIt == I.doc->exceptions.end()) + { + I.doc->exceptions.push_back(functionException); + } + } +} + +template +void +populateOverloadsSees(OverloadsSymbol& I, Range& functions) { + auto functionSees = functions | + std::views::filter([](FunctionSymbol const& function) + { + return function.doc && !function.doc->sees.empty(); + }) | + std::views::transform([](FunctionSymbol const& function) + { + return function.doc->sees; + }) | + std::views::join; + for (doc::SeeBlock const& functionSee: functionSees) + { + auto sameIt = std::ranges::find_if( + I.doc->sees, + [&functionSee](doc::SeeBlock const& overloadSee) + { + return overloadSee.children == functionSee.children; + }); + if (sameIt == I.doc->sees.end()) + { + I.doc->sees.push_back(functionSee); + } + } +} + +template +void +populateOverloadsPreconditions(OverloadsSymbol& I, Range& functions) { + auto functionsPres = functions | + std::views::filter([](FunctionSymbol const& function) + { + return function.doc && !function.doc->preconditions.empty(); + }) | + std::views::transform([](FunctionSymbol const& function) + { + return function.doc->preconditions; + }) | + std::views::join; + for (doc::PreconditionBlock const& functionPre: functionsPres) + { + auto sameIt = std::ranges::find_if( + I.doc->preconditions, + [&functionPre](doc::PreconditionBlock const& overloadPre) + { + return overloadPre.children == functionPre.children; + }); + if (sameIt == I.doc->preconditions.end()) + { + I.doc->preconditions.push_back(functionPre); + } + } +} + +template +void +populateOverloadsPostconditions(OverloadsSymbol& I, Range& functions) { + auto functionsPosts = functions | + std::views::filter([](FunctionSymbol const& function) + { + return function.doc && !function.doc->postconditions.empty(); + }) | + std::views::transform([](FunctionSymbol const& function) + { + return function.doc->postconditions; + }) | + std::views::join; + for (doc::PostconditionBlock const& functionPost: functionsPosts) + { + auto sameIt = std::ranges::find_if( + I.doc->postconditions, + [&functionPost](doc::PostconditionBlock const& overloadPost) + { + return overloadPost.children == functionPost.children; + }); + if (sameIt == I.doc->postconditions.end()) + { + I.doc->postconditions.push_back(functionPost); + } + } +} + +} +} // mrdocs + +#endif // MRDOCS_LIB_METADATA_FINALIZERS_DOCCOMMENT_OVERLOADS_HPP diff --git a/src/lib/Metadata/Finalizers/DocComment/parseInlines.hpp b/src/lib/Metadata/Finalizers/DocComment/parseInlines.hpp new file mode 100644 index 000000000..36b76f165 --- /dev/null +++ b/src/lib/Metadata/Finalizers/DocComment/parseInlines.hpp @@ -0,0 +1,1062 @@ +// +// 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_METADATA_FINALIZERS_DOCCOMMENT_PARSEINLINES_HPP +#define MRDOCS_LIB_METADATA_FINALIZERS_DOCCOMMENT_PARSEINLINES_HPP + +#include +#include +#include +#include +#include + +namespace mrdocs::doc { +namespace { +inline +bool +isPunctuation(char c) +{ + // ASCII punctuation per CommonMark (§2.2) (includes '_') + return is_one_of(c, R"(!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~)"); +} + +// ========================== RULE TABLE & FLAGS =============================== + +/// Flags that configure how a token behaves in the inline parser. +/// Multiple flags may be OR'ed together for a rule. +enum class RuleFlags : unsigned { + /// No special behavior. + None = 0, + + /// The token follows Markdown semantics (e.g., emphasis/strong rules). + /// Used to apply CommonMark-style delimiter and boundary logic. + Markdown = 1u << 0, + + /// The token represents an HTML tag (case-insensitive), allowing optional + /// whitespace and attributes inside angle brackets (e.g., , ). + Html = 1u << 1, + + /// Apply CommonMark left/right “flanking” checks to decide whether a + /// delimiter may open and/or close. Crucial for underscores near identifiers. + RequiresFlank = 1u << 2, + + /// Allows a closing delimiter to pop through intervening frames until a + /// matching kind is found (typical Markdown/HTML implicit closing behavior). + ImplicitClose = 1u << 3, + + /// Do not scan nested delimiters inside + Barrier = 1u << 4, + + /// The token cannot be used intraword (e.g., '^' and '~' for super/subscript). + /// This is stricter than RequiresFlank. + NoIntraWord = 1u << 5, +}; + +/// Bitwise OR operator for RuleFlags. +constexpr +RuleFlags +operator|(RuleFlags a, RuleFlags b) +{ + return static_cast(static_cast(a) | static_cast(b)); +} + +/// Bitwise AND operator for RuleFlags. +constexpr +bool +has(RuleFlags f, RuleFlags bit) +{ + return (static_cast(f) & static_cast(bit)) != 0; +} + +/// Single inline markup rule +struct TagRule { + /// The kind of inline node this rule produces (e.g., Emph, Strong, Code, etc.) + InlineKind kind; + /// The exact string that triggers this rule when opening (e.g., "*", "", "^") + std::string_view open; + /// The exact string that closes this rule (may equal `open` for symmetric tokens) + std::string_view close; + /// Flags describing behavior (Markdown, HTML, AsciiDoc, boundary rules, etc.) + RuleFlags flags; +}; + +// Table of tag rules +// Order matters: try longer tokens first to disambiguate (** before *). +inline +constexpr +TagRule +kRules[] = { + // Markdown strong/emph/strike/code + { doc::InlineKind::Strong, "**", "**", RuleFlags::Markdown | RuleFlags::RequiresFlank | RuleFlags::ImplicitClose }, + { doc::InlineKind::Strong, "__", "__", RuleFlags::Markdown | RuleFlags::RequiresFlank | RuleFlags::ImplicitClose | RuleFlags::NoIntraWord }, + { doc::InlineKind::Strikethrough,"~~", "~~", RuleFlags::Markdown | RuleFlags::RequiresFlank | RuleFlags::ImplicitClose }, + { doc::InlineKind::Emph, "*", "*", RuleFlags::Markdown | RuleFlags::RequiresFlank | RuleFlags::ImplicitClose }, + { doc::InlineKind::Emph, "_", "_", RuleFlags::Markdown | RuleFlags::RequiresFlank | RuleFlags::ImplicitClose | RuleFlags::NoIntraWord }, + { doc::InlineKind::Code, "`", "`", RuleFlags::Markdown | RuleFlags::ImplicitClose | RuleFlags::Barrier }, + { doc::InlineKind::Math, "$$", "$$", RuleFlags::Markdown | RuleFlags::ImplicitClose | RuleFlags::Barrier }, + { doc::InlineKind::Math, "$", "$", RuleFlags::Markdown | RuleFlags::ImplicitClose | RuleFlags::Barrier }, + { doc::InlineKind::Superscript, "^", "^", RuleFlags::Markdown | RuleFlags::RequiresFlank | RuleFlags::ImplicitClose | RuleFlags::NoIntraWord }, + { doc::InlineKind::Subscript, "~", "~", RuleFlags::Markdown | RuleFlags::RequiresFlank | RuleFlags::ImplicitClose | RuleFlags::NoIntraWord }, + { doc::InlineKind::Highlight, "==", "==", RuleFlags::Markdown | RuleFlags::RequiresFlank | RuleFlags::ImplicitClose }, + + // HTML tags (case-insensitive). Allow spaces around names/attrs. + { doc::InlineKind::Emph, "", "", RuleFlags::Html | RuleFlags::ImplicitClose }, + { doc::InlineKind::Strong, "", "", RuleFlags::Html | RuleFlags::ImplicitClose }, + { doc::InlineKind::Code, "", "", RuleFlags::Html | RuleFlags::ImplicitClose }, + { doc::InlineKind::Superscript, "", "", RuleFlags::Html | RuleFlags::ImplicitClose }, + { doc::InlineKind::Subscript, "", "", RuleFlags::Html | RuleFlags::ImplicitClose }, + { doc::InlineKind::Strikethrough,"", "", RuleFlags::Html | RuleFlags::ImplicitClose }, + { doc::InlineKind::Highlight, "", "", RuleFlags::Html | RuleFlags::ImplicitClose }, + { doc::InlineKind::LineBreak, "
", "", RuleFlags::Html }, // also accept
at runtime +}; + +// Synthetic rule to tag frames as "HTML-ish" so the stack logic can +// consult its flags (ImplicitClose etc.). Needs to be visible to helpers. +inline constexpr TagRule kHtmlRule{ + doc::InlineKind::Text, "", "", RuleFlags::Html | RuleFlags::ImplicitClose +}; + +// Map supported HTML tag names to InlineKind +inline +std::optional +html_inline_kind(std::string const& n) +{ + if (n == "em") + { + return doc::InlineKind::Emph; + } + if (n == "strong") + { + return doc::InlineKind::Strong; + } + if (n == "code") + { + return doc::InlineKind::Code; + } + if (n == "sub") + { + return doc::InlineKind::Subscript; + } + if (n == "sup") + { + return doc::InlineKind::Superscript; + } + if (n == "del") + { + return doc::InlineKind::Strikethrough; + } + if (n == "mark") + { + return doc::InlineKind::Highlight; + } + return std::nullopt; +} + + +// Find the first rule whose token matches s at i (opening or closing). +template +TagRule const* +matchRuleImpl(std::string_view s, std::size_t i) +{ + for (auto const& r: kRules) + { + auto tok = opening ? r.open : r.close; + if (tok.empty()) + { + continue; + } + if (i + tok.size() > s.size()) + { + continue; + } + if (s.compare(i, tok.size(), tok) == 0) + { + return &r; + } + } + return nullptr; +} + +inline +TagRule const* +matchOpeningRule(std::string_view s, std::size_t i) +{ + return matchRuleImpl(s, i); +} + +inline +TagRule const* +matchClosingRule(std::string_view s, std::size_t i) +{ + return matchRuleImpl(s, i); +} + +// ========================== COMMONMARK FLANKING ============================== +// +// left/right-flanking per CM 0.30: this basically disallows opening/closing +// inside words: prev and next both alnum. +// + +/// Evaluate left/right flanking at position i for a token of length len. +struct Flank { + /// The position to the left is left-flanking, meaning the token can open + bool left = false; + /// The position to the right is right-flanking, meaning the token can close + bool right = false; +}; + +inline +Flank +flank_at(std::string_view s, std::size_t i, std::size_t len) +{ + const char prev = (i == 0) ? '\0' : s[i - 1]; + const char next = (i + len >= s.size()) ? '\0' : s[i + len]; + + const bool prev_space = (i == 0) || isWhitespace(prev); + const bool next_space = (i + len >= s.size()) || isWhitespace(next); + const bool prev_punct = (i > 0) && isPunctuation(prev); + const bool next_punct = (i + len < s.size()) && isPunctuation(next); + + Flank f; + f.left = !next_space && (!next_punct || prev_space || prev_punct); + f.right = !prev_space && (!prev_punct || next_space || next_punct); + return f; +} + +inline +bool +can_open(TagRule const& r, std::string_view s, std::size_t i) +{ + if (!has(r.flags, RuleFlags::RequiresFlank)) + { + return true; + } + + std::size_t const len = r.open.size(); + Flank const f = flank_at(s, i, len); + + // Stricter rule: never open intraword + if (has(r.flags, RuleFlags::NoIntraWord)) + { + char const prev = (i == 0) ? '\0' : s[i - 1]; + char const next = (i + len >= s.size()) ? '\0' : s[i + len]; + bool const is_intraword = isAlphaNumeric(prev) && isAlphaNumeric(next); + if (is_intraword) + { + return false; + } + return f.left; + } + + // '*', '==', '~~' use standard flanking + return f.left; +} + +inline +bool +can_close(TagRule const& r, std::string_view s, std::size_t i) +{ + if (!has(r.flags, RuleFlags::RequiresFlank)) + { + return true; + } + + std::size_t const len = r.close.size(); + Flank const f = flank_at(s, i, len); + + // Stricter rule: never close intraword + if (has(r.flags, RuleFlags::NoIntraWord)) + { + char const prev = (i == 0) ? '\0' : s[i - 1]; + char const next = (i + len >= s.size()) ? '\0' : s[i + len]; + bool is_intraword = isAlphaNumeric(prev) && isAlphaNumeric(next); + if (is_intraword) + { + return false; + } + return f.right; + } + + return f.right; +} + +// ============================ EMIT/START HELPERS ============================= + +// Emit text to the inline container, merging with previous text if possible. +inline +void +emit_text(doc::InlineContainer& out, std::string&& text) +{ + if (text.empty()) + { + return; + } + if (!out.empty() && out.back()->isText()) + { + out.back()->asText().literal += text; + return; + } + out.emplace_back(std::move(text)); +} + +// Start a new inline container of kind k, appending it to out. +// Returns a reference to the new container for appending children. +inline +doc::InlineContainer& +start_container(doc::InlineContainer& out, doc::InlineKind k) +{ + // Create k and return its child container. + switch (k) + { + case doc::InlineKind::Emph: + out.emplace_back(); + break; + case doc::InlineKind::Strong: + out.emplace_back(); + break; + case doc::InlineKind::Strikethrough: + out.emplace_back(); + break; + case doc::InlineKind::Highlight: + out.emplace_back(); + break; + case doc::InlineKind::Superscript: + out.emplace_back(); + break; + case doc::InlineKind::Subscript: + out.emplace_back(); + break; + case doc::InlineKind::Code: + out.emplace_back(); + break; + default: + MRDOCS_UNREACHABLE(); + } + auto* ic = dynamic_cast(&*out.back()); + MRDOCS_ASSERT(ic != nullptr); // container types must derive InlineContainer + return *ic; +} + +// Emit a line break (hard or soft) to the output container. +inline +void +emit_break(doc::InlineContainer& out, bool hard) +{ + if (hard) + { + out.emplace_back(); + } + else + { + out.emplace_back(); + } +} + +// Link/Image helpers +inline +doc::InlineContainer& +start_link(doc::InlineContainer& out, std::string&& href) { + out.emplace_back(); + auto& link = out.back()->asLink(); + link.href = std::move(href); + auto* ic = dynamic_cast(&*out.back()); + MRDOCS_ASSERT(ic != nullptr); + return *ic; +} + +inline +doc::InlineContainer& +emit_image(doc::InlineContainer& out, std::string&& src, std::string&& alt) +{ + out.emplace_back(); + auto& img = out.back()->asImage(); + img.src = std::move(src); + img.alt = std::move(alt); + auto* ic = dynamic_cast(&*out.back()); + MRDOCS_ASSERT(ic != nullptr); + return *ic; +} + +// Flatten a temporary InlineContainer to plain text (for alt text fallback). +inline +std::string +flatten_text(doc::InlineContainer const& c) +{ + std::string r; + for (auto const& el: c.children) + { + switch (el->Kind) + { + case doc::InlineKind::Text: + r += el->asText().literal; + break; + default: /* ignore formatting for alt */ + break; + } + } + return r; +} + +// Move all children from src → dst and clear src +inline +void move_children(doc::InlineContainer& dst, doc::InlineContainer& src) +{ + dst.insert( + dst.end(), + std::make_move_iterator(src.begin()), + std::make_move_iterator(src.end())); + src.clear(); +} + +// ================================ FRAMES ===================================== + +// A Frame represents an opened container collecting children until its +// corresponding close token is encountered. +struct Frame { + // what we're building + doc::InlineKind kind; + // the rule that opened it + TagRule const* rule; + // where the finished node will be inserted + doc::InlineContainer* parent; + // children collected while open + doc::InlineContainer scratch; + // literal open token (for fallback) + std::string open_tok; +}; + +// For pending [label] or ![alt] +struct Bracket { + bool is_image = false; + // where link/image will be emitted + doc::InlineContainer* parent = nullptr; + // label/alt children + doc::InlineContainer label; +}; + +// HTML parsing helpers --------------------------------------------------------- + +inline +std::size_t +skip_spaces(std::string_view s, std::size_t i) +{ + while (i < s.size() && isWhitespace(s[i])) + { + ++i; + } + return i; +} + +// Parse a case-insensitive HTML tag with optional spaces/attrs. +struct HtmlTag { + bool closing = false; + // lowercased name + std::string name; + // raw inside <> minus name and /, for simple attr parsing + std::string attrs; + // index of char after '>' + std::size_t end = 0; +}; + +// Parse an HTML tag at s[i], returning nullopt if not a tag. +// On success, end is the index after '>'. +inline +std::optional +parse_html_tag(std::string_view s, std::size_t i) +{ + if (i >= s.size() || s[i] != '<') + { + return std::nullopt; + } + + std::size_t j = i + 1; + j = skip_spaces(s, j); + + // is it a closing tag? + bool closing = false; + if (j < s.size() && s[j] == '/') + { + closing = true; + j = skip_spaces(s, j + 1); + } + + // tag name + std::size_t name_start = j; + while (j < s.size() && isAlphaNumeric(s[j])) + { + ++j; + } + if (j == name_start) + { + return std::nullopt; + } + std::string name = toLowerCase(s.substr(name_start, j - name_start)); + + // attrs up to '>' + std::size_t attrs_start = j; + while (j < s.size() && s[j] != '>') ++j; + if (j >= s.size()) return std::nullopt; + // may include spaces and '/' + std::string attrs(s.substr(attrs_start, j - attrs_start)); + + return HtmlTag{closing, std::move(name), std::move(attrs), j + 1}; +} + +// read attribute value for key (case-insensitive). Accepts key="..."/'...' or key=bare. +inline +std::optional +html_get_attr(std::string const& attrs, std::string_view key) +{ + auto k = toLowerCase(key); + std::size_t i = 0; + while (i < attrs.size()) { + while (i < attrs.size() && isWhitespace(attrs[i])) + { + ++i; + } + std::size_t kstart = i; + while ( + i < attrs.size() + && (isAlphaNumeric(attrs[i]) || attrs[i] == '-' || attrs[i] == '_')) + { + ++i; + } + if (i == kstart) + { + break; + } + std::string name = toLowerCase(std::string_view(attrs).substr(kstart, i - kstart)); + i = skip_spaces(attrs, i); + if (i >= attrs.size() || attrs[i] != '=') + { + if (name == k) + { + return std::string{}; + } + while (i < attrs.size() && !isWhitespace(attrs[i])) + { + ++i; + } + continue; + } + ++i; + i = skip_spaces(attrs, i); + if (i >= attrs.size()) + { + break; + } + std::string value; + if (attrs[i] == '"' || attrs[i] == '\'') { + char q = attrs[i++]; + std::size_t vstart = i; + while (i < attrs.size() && attrs[i] != q) + { + ++i; + } + value.assign(attrs.substr(vstart, i - vstart)); + if (i < attrs.size()) + { + ++i; + } + } + else + { + std::size_t vstart = i; + while ( + i < attrs.size() && + !isWhitespace(attrs[i]) && + attrs[i] != '/') + { + ++i; + } + value.assign(attrs.substr(vstart, i - vstart)); + } + if (name == k) + { + return value; + } + } + return std::nullopt; +} + +// Minimal state holder to replace lots of lambdas in parse() +struct ParserState { + // input + std::string_view s; + // stack of open frames + std::vector frames; + // stack of open [label] or ![alt] + std::vector brackets; + // current container for text accumulation + doc::InlineContainer* cur{}; + // output + std::string text; + // whether the next char is escaped + bool escape_next{ false }; + + void + flush_text() + { + if (!text.empty()) + { + emit_text(*cur, std::move(text)); + text.clear(); + } + } + + static void + push_text_node(doc::InlineContainer& out, std::string_view sv) + { + out.emplace_back(std::string(sv)); + } + + void + push_frame(TagRule const* r) + { + flush_text(); + frames.push_back(Frame{ r->kind, r, cur, {}, std::string(r->open) }); + cur = &frames.back().scratch; + } + + // Emit the opening token + flattened contents as literal text into its + // parent. + void + fallback_unclosed(Frame& f) + { + std::string literal(f.open_tok); + for (auto& el: f.scratch) + { + getAsPlainText(*el, literal); + } + emit_text(*f.parent, std::move(literal)); + } + + // Materialize a finished frame to its parent + inline + void + materialize_and_pop(Frame f) + { + // Line breaks are leaf inlines + if (f.kind == doc::InlineKind::LineBreak) + { + emit_break(*f.parent, /*hard*/ true); + cur = f.parent; + return; + } + if (f.kind == doc::InlineKind::SoftBreak) + { + emit_break(*f.parent, /*hard*/ false); + cur = f.parent; + return; + } + + // Math is a *leaf* (not an InlineContainer): emit a single literal payload. + if (f.kind == doc::InlineKind::Math) + { + // We recorded its body into f.scratch (as Text children) while inside the barrier. + // Flatten to a single string and emit a MathInline leaf. + std::string lit = flatten_text(f.scratch); + f.parent->emplace_back(); + f.parent->back()->asMath().literal = std::move(lit); + cur = f.parent; + return; + } + + // Links opened from HTML
keep href in open_tok + if (f.kind == doc::InlineKind::Link && f.rule + && has(f.rule->flags, RuleFlags::Html)) + { + doc::InlineContainer& outC = start_link(*f.parent, std::move(f.open_tok)); + move_children(outC, f.scratch); + cur = f.parent; + return; + } + + // All remaining supported formatting kinds are containers + doc::InlineContainer* outC = &start_container(*f.parent, f.kind); + move_children(*outC, f.scratch); + cur = f.parent; + } + + // Try to close the topmost frame of kind k, popping frames as needed. + bool + close_to_kind(doc::InlineKind k) + { + std::size_t match = frames.size(); + while (match > 0) + { + --match; + if (frames[match].kind == k) + { + break; + } + auto* rr = frames[match].rule; + bool const can_cross = rr + && has(rr->flags, RuleFlags::ImplicitClose) + && !has(rr->flags, RuleFlags::Barrier); + if (!can_cross) + { + return false; + } + } + if (match == frames.size() || frames[match].kind != k) + { + return false; + } + + flush_text(); + + // Literalize intervening crossed frames + while (frames.size() > match + 1) + { + Frame f = std::move(frames.back()); + frames.pop_back(); + fallback_unclosed(f); + cur = f.parent; + } + + // Materialize the match + Frame f = std::move(frames.back()); + frames.pop_back(); + materialize_and_pop(std::move(f)); + return true; + } + + // Markdown link/image finalization at ']' + std::optional + try_close_bracket(std::size_t i) + { + if (brackets.empty()) + { + return std::nullopt; + } + + flush_text(); + + Bracket b = std::move(brackets.back()); + brackets.pop_back(); + + // After ']', expect optional spaces then '(' dest [title] ')' + std::size_t j = i + 1; + j = skip_spaces(s, j); + if (j >= s.size() || s[j] != '(') + { + // Not a link/image — emit literal "[...]" back to parent + push_text_node(*b.parent, b.is_image ? "![" : "["); + if (!b.label.empty()) + { + move_children(*b.parent, b.label); + } + push_text_node(*b.parent, "]"); + cur = b.parent; + return j - i; // we consumed ']' + } + ++j; + j = skip_spaces(s, j); + + // Parse destination + std::string dest, title; + if (j < s.size() && (s[j] == '"' || s[j] == '\'')) + { + char q = s[j++]; + std::size_t start = j; + while (j < s.size() && s[j] != q) + { + ++j; + } + dest.assign(s.substr(start, j - start)); + if (j < s.size()) + { + ++j; + } + } + else + { + std::size_t start = j; + while (j < s.size() && s[j] != ')' && !isWhitespace(s[j])) + { + ++j; + } + dest.assign(s.substr(start, j - start)); + } + j = skip_spaces(s, j); + + // Optional title (ignored, but must be consumed) + if (j < s.size() && (s[j] == '"' || s[j] == '\'')) + { + char q = s[j++]; + std::size_t start = j; + while (j < s.size() && s[j] != q) + { + ++j; + } + title.assign(s.substr(start, j - start)); + if (j < s.size()) + { + ++j; + } + j = skip_spaces(s, j); + } + + if (j >= s.size() || s[j] != ')') + { + // Invalid link — degrade to literal + push_text_node(*b.parent, b.is_image ? "![" : "["); + if (!b.label.empty()) + { + move_children(*b.parent, b.label); + } + push_text_node(*b.parent, "]"); + cur = b.parent; + return j - i; + } + ++j; // consume ')' + + // Materialize + if (b.is_image) + { + emit_image(*b.parent, std::move(dest), flatten_text(b.label)); + } + else + { + doc::InlineContainer& linkC = start_link(*b.parent, std::move(dest)); + move_children(linkC, b.label); + } + cur = b.parent; + return j - i; + } +}; + + +} + + +/* Parse the inline content of a text + + The furthest clang goes is + clang::comments::TextComment, which we parse + as doc::TextInline. However, these still contain + javadoc, HTML, and markdown-like inline elements + that we want to parse and to represent in the corpus, + as they are supported by doxygen. + + This parsing happens in a post-processing finalizer step + because the javadoc parser needs to concatenate + text nodes in multiple forms and we won't + have the tidied up text until after the + post-processing. + + This is a focused implementation; it does not implement every last + corner of CommonMark, but only the features that exist in Doxygen. + It should implement the critical delimiter-run algorithm accurately. + + What this parsing function implements: + + - CommonMark-correct emphasis (* and _) including underscore + intraword rules and the “multiple-of-3” restriction. + - Backtick code spans per CommonMark (no parsing inside; we still emit a + container Code node with a single Text child). + - HTML tags with optional spacing and attributes; support , , + , , , , ,
,
, . + - Markdown links/images: [text](dest "title") and ![alt](src "title"). + - Nesting via a container stack; text nodes are terminal. + - Compact rule table for non-HTML markers. + - No ; all checks are explicit ASCII. + - Clear, rationale-heavy comments. + */ +inline +ParseResult +parse(char const* first, char const* last, doc::InlineContainer& out_root) +{ + std::string_view s(first, static_cast(last - first)); + + ParserState st{ + .s = s, + .frames = {}, + .brackets = {}, + .cur = &out_root, + .text = {}, + .escape_next = false + }; + st.frames.reserve(8); + st.brackets.reserve(4); + st.text.reserve(64); + + for (std::size_t i = 0; i < s.size();) { + // If inside a barrier (e.g. backticks), only look for its own closer + if (!st.frames.empty() && + st.frames.back().rule && + has(st.frames.back().rule->flags, RuleFlags::Barrier)) + { + TagRule const* br = st.frames.back().rule; + if (i + br->close.size() <= s.size() && + s.compare(i, br->close.size(), br->close) == 0) + { + st.flush_text(); + Frame f = std::move(st.frames.back()); + st.frames.pop_back(); + st.materialize_and_pop(std::move(f)); + i += br->close.size(); + continue; + } + st.text.push_back(s[i++]); // literal inside code span + continue; + } + + char c = s[i]; + + // Backslash escape + if (st.escape_next) { st.text.push_back(c); st.escape_next = false; ++i; continue; } + if (c == '\\') { st.escape_next = true; ++i; continue; } + + // Markdown link/image openers + if (c == '!' && i + 1 < s.size() && s[i + 1] == '[') { + st.flush_text(); + st.brackets.push_back(Bracket{ true, st.cur, {} }); + st.cur = &st.brackets.back().label; + i += 2; + continue; + } + if (c == '[') { + st.flush_text(); + st.brackets.push_back(Bracket{ false, st.cur, {} }); + st.cur = &st.brackets.back().label; + ++i; + continue; + } + if (c == ']') { + if (auto adv = st.try_close_bracket(i)) { i += *adv; continue; } + st.text.push_back(c); + ++i; + continue; + } + + // HTML tags (with spaces, attrs, case-insensitive) + if (c == '<') { + if (auto tag = parse_html_tag(s, i)) { + std::string const& name = tag->name; + + //
and
+ if (!tag->closing && name == "br") { + st.flush_text(); + emit_break(*st.cur, /*hard*/true); + i = tag->end; + continue; + } + + // + if (!tag->closing && name == "img") { + st.flush_text(); + auto src = html_get_attr(tag->attrs, "src").value_or(std::string{}); + auto alt = html_get_attr(tag->attrs, "alt").value_or(std::string{}); + emit_image(*st.cur, std::move(src), std::move(alt)); + i = tag->end; + continue; + } + + //
/ + if (name == "a") { + if (tag->closing) { + if (st.close_to_kind(doc::InlineKind::Link)) { + i = tag->end; + continue; + } + } else { + auto href = html_get_attr(tag->attrs, "href").value_or(std::string{}); + st.flush_text(); + st.frames.push_back(Frame{ doc::InlineKind::Link, &kHtmlRule, st.cur, {}, std::string{} }); + st.cur = &st.frames.back().scratch; + st.frames.back().open_tok = std::move(href); // own the href bytes + } + i = tag->end; + continue; + } + + // Other supported phrasing tags + if (auto kind = html_inline_kind(name)) { + if (tag->closing) { + if (st.close_to_kind(*kind)) { + i = tag->end; + continue; + } + } else { + st.flush_text(); + st.frames.push_back(Frame{ *kind, &kHtmlRule, st.cur, {}, "" }); + st.cur = &st.frames.back().scratch; + } + i = tag->end; + continue; + } + // Unknown … — fall through to literal + } + } + + // Try a closer first (for symmetric tokens) + if (TagRule const* rc = matchClosingRule(s, i)) { + const bool ok_close = + !has(rc->flags, RuleFlags::Markdown) || can_close(*rc, s, i); + if (ok_close && st.close_to_kind(rc->kind)) { + i += rc->close.size(); + continue; + } + } + + // Then an opener + if (TagRule const* ro = matchOpeningRule(s, i)) { + if (has(ro->flags, RuleFlags::Markdown) && !can_open(*ro, s, i)) { + st.text.append(ro->open); + i += ro->open.size(); + continue; + } + st.push_frame(ro); + i += ro->open.size(); + continue; + } + + // Plain char + st.text.push_back(c); + ++i; + } + + // EOF: flush and unwind + st.flush_text(); + + // Unclosed brackets → literal "[...]" back to parent + while (!st.brackets.empty()) { + Bracket b = std::move(st.brackets.back()); + st.brackets.pop_back(); + ParserState::push_text_node(*b.parent, b.is_image ? "![" : "["); + if (!b.label.empty()) + move_children(*b.parent, b.label); + ParserState::push_text_node(*b.parent, "]"); + st.cur = b.parent; + } + + // Unclosed frames: HTML autoclosed, Markdown literalized + st.flush_text(); + while (!st.frames.empty()) { + Frame f = std::move(st.frames.back()); + st.frames.pop_back(); + if (f.rule && has(f.rule->flags, RuleFlags::Html)) { + st.materialize_and_pop(std::move(f)); + } else { + st.fallback_unclosed(f); + st.cur = f.parent; + } + } + + ParseResult res{}; + res.ptr = last; + return res; +} + +} // mrdocs::doc + +#endif // MRDOCS_LIB_METADATA_FINALIZERS_DOCCOMMENT_PARSEINLINES_HPP diff --git a/src/lib/Metadata/Finalizers/DocCommentFinalizer.cpp b/src/lib/Metadata/Finalizers/DocCommentFinalizer.cpp new file mode 100644 index 000000000..68d741410 --- /dev/null +++ b/src/lib/Metadata/Finalizers/DocCommentFinalizer.cpp @@ -0,0 +1,1897 @@ +// +// 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) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mrdocs { + +void +DocCommentFinalizer:: +build() +{ + auto infos = + corpus_.info_ | + std::views::filter([](std::unique_ptr const& ptr) { + return ptr && ptr->Extraction != ExtractionMode::Dependency; + }) | + std::views::transform([](std::unique_ptr const& ptr) -> Symbol& { + MRDOCS_ASSERT(ptr); + return *ptr; + }); + + // Finalize briefs: + // We do it first because all other steps require accessing + // the brief of other functions, these often need to be resolved + // with @copybrief or auto-brief, and we need to ensure that + // there are no circular dependencies for other metadata. + for (auto& I : infos) + { + finalizeBrief(I); + } + + // Finalize metadata: + // A @copydetails command also implies we should copy + // other metadata from the referenced symbol. + // The metadata from other symbols includes things + // like function parameters, return types, etc... + // We copy this now because we need the complete metadata + // for all objects to generate doc for overloads. + // For instance, overloads cannot aggregate function + // parameters as if the parameters are not resolved. + for (auto& I : infos) + { + copyDetails(I); + } + + // Create doc for overloads: + // We do it before the references because the overloads + // themselves can be used in the references. For instance, + // `@ref foo` refers to the overload set because it doesn't + // specify the function signature. + if (corpus_.config->overloads) + { + for (auto& I : infos) + { + MRDOCS_CHECK_OR_CONTINUE(I.isOverloads()); + generateOverloadDocs(I.asOverloads()); + } + } + + // Resolve references in the doc: + // We do this before resolving overloads because a reference + // to a function without signature should resolve to the + // overload set, not to a specific function. + for (auto& I : infos) + { + // Rename this to "finalizeReferences" and move other + // functionality to other loops. + resolveReferences(I); + } + + // Populate trivial function metadata + // - We do it after the overloads because they should not + // rely on metadata inherited from automatic generated doc + // - We also do it after the references because some metadata + // might be resolved from references with @copydetails + if (corpus_.config->autoFunctionMetadata) + { + for (auto& I : infos) + { + MRDOCS_CHECK_OR_CONTINUE(I.isFunction()); + generateAutoFunctionMetadata(I.asFunction()); + } + } + + // Process relates + for (auto& I : infos) + { + processRelates(I); + } + + // Normalize siblings + for (auto& I : infos) + { + normalizeSiblings(I); + } + + // Tidy up doc + for (auto& I : infos) + { + tidyUp(I); + } + + // Parse inlines in terminal text nodes + for (auto& I : infos) + { + parseInlines(I); + } + + // Remove invalid references + for (auto& I : infos) + { + removeInvalidReferences(I); + } + + // - Emitting param warning require everything to be completely + // processed + emitWarnings(); +} + +void +DocCommentFinalizer:: +finalizeBrief(Symbol& I) +{ + MRDOCS_CHECK_OR(!finalized_brief_.contains(&I)); + finalized_brief_.emplace(&I); + + report::trace( + "Finalizing brief for '{}'", + corpus_.Corpus::qualifiedName(I)); + + if (I.isOverloads()) + { + // Overloads are expected not to have doc. + // We'll create a doc for them if they don't have one. + if (!I.doc) + { + I.doc.emplace(); + } + // The brief of an overload is always empty + auto& OI = I.asOverloads(); + for (auto const& MemberIDs = OI.Members; + auto& memberID : MemberIDs) + { + Symbol* member = corpus_.find(memberID); + MRDOCS_CHECK_OR_CONTINUE(member); + finalizeBrief(*member); + } + auto functions = overloadFunctionsRange(OI, corpus_); + populateOverloadsBrief(OI, functions, corpus_); + return; + } + + MRDOCS_CHECK_OR(I.doc); + auto& doc = *I.doc; + // Copy brief from other symbols if there's a @copydoc + copyBrief(I, doc); + // Set auto brief if brief is still empty + setAutoBrief(doc); +} + +void +DocCommentFinalizer:: +copyBrief(Symbol const& ctx, DocComment& doc) +{ + MRDOCS_CHECK_OR(doc.brief); + MRDOCS_CHECK_OR(!doc.brief->copiedFrom.empty()); + MRDOCS_CHECK_OR(doc.brief->children.empty()); + + for (std::string const& ref: doc.brief->copiedFrom) + { + // Look for source + auto resRef = corpus_.lookup(ctx.id, ref); + + // Check if the source exists + if (!resRef) + { + if (corpus_.config->warnings && + corpus_.config->warnBrokenRef && + !refWarned_.contains({ref, ctx.Name})) + { + this->warn( + ctx, + "{}: Failed to copy brief from '{}' (symbol not found)\n" + " {}", + corpus_.Corpus::qualifiedName(ctx), + ref, + resRef.error().reason()); + } + continue; + } + + // Ensure the brief source is finalized + Symbol const& res = *resRef; + finalizeBrief(const_cast(res)); + + // Check if the source has a brief + if (!res.doc || + !res.doc->brief.has_value()) + { + if (corpus_.config->warnings && + corpus_.config->warnBrokenRef && + !refWarned_.contains({ref, ctx.Name})) + { + auto resPrimaryLoc = getPrimaryLocation(res); + this->warn( + ctx, + "{}: Failed to copy brief from {} '{}' (no brief available).\n" + " No brief available.\n" + " {}:{}\n" + " Note: No brief available for '{}'.", + corpus_.Corpus::qualifiedName(ctx), + toString(res.Kind), + ref, + resPrimaryLoc->FullPath, + resPrimaryLoc->LineNumber, + corpus_.Corpus::qualifiedName(res)); + } + continue; + } + + DocComment const& src = *res.doc; + doc.brief->children = src.brief->children; + return; + } +} + +void +DocCommentFinalizer:: +setAutoBrief(DocComment& doc) const +{ + MRDOCS_CHECK_OR(corpus_.config->autoBrief); + MRDOCS_CHECK_OR(!doc.brief); + MRDOCS_CHECK_OR(!doc.Document.empty()); + + auto isInvalidBriefText = []( + Polymorphic const& el) + { + MRDOCS_ASSERT(!el.valueless_after_move()); + return !el->isText() || + el->asText().literal.empty() || + el->asText().Kind == doc::InlineKind::CopyDetails || + isWhitespace(el->asText().literal); + }; + + for (auto it = doc.Document.begin(); it != doc.Document.end();) + { + if (auto& block = *it; + block->Kind == doc::BlockKind::Paragraph) + { + auto& para = dynamic_cast(*block); + if (std::ranges::all_of(para.children, isInvalidBriefText)) + { + ++it; + continue; + } + doc.brief.emplace(); + doc.brief->children = para.children; + it = doc.Document.erase(it); + return; + } + ++it; + } +} + +void +DocCommentFinalizer:: +copyDetails(Symbol& I) +{ + MRDOCS_CHECK_OR(!finalized_metadata_.contains(&I)); + finalized_metadata_.emplace(&I); + + report::trace( + "Finalizing metadata for '{}'", + corpus_.Corpus::qualifiedName(I)); + + MRDOCS_CHECK_OR(I.doc); + MRDOCS_CHECK_OR(!I.doc->Document.empty()); + DocComment& destDoc = *I.doc; + + llvm::SmallVector copiedRefs; + for (auto& block: destDoc.Document) + { + MRDOCS_CHECK_OR_CONTINUE(block->isParagraph()); + auto& para = dynamic_cast(*block); + MRDOCS_CHECK_OR_CONTINUE(!para.children.empty()); + + for (auto& text: para.children) + { + MRDOCS_CHECK_OR_CONTINUE(text->isCopyDetails()); + copiedRefs.emplace_back(dynamic_cast(*text)); + } + MRDOCS_CHECK_OR_CONTINUE(!copiedRefs.empty()); + } + + for (doc::CopyDetailsInline const& copied: copiedRefs) + { + // Find element + auto resRef = corpus_.lookup(I.id, copied.string); + if (!resRef) + { + if (corpus_.config->warnings && + corpus_.config->warnBrokenRef && + !refWarned_.contains({copied.string, I.Name})) + { + this->warn( + I, + "{}: Failed to copy metadata from '{}' (symbol not found)\n" + " {}", + corpus_.Corpus::qualifiedName(I), + copied.string, + resRef.error().reason()); + } + continue; + } + + // Function to copy the metadata from a ranges + // of source functions. This range might + // contain more than one function if the + // destination is an overload set. + // We can't copy directly from the overload set + // because its metadata is not created at this + // step yet. + auto copyInfoRangeMetadata = [&](llvm::ArrayRef srcInfoPtrs) + { + auto srcInfos = srcInfoPtrs + | std::views::transform( + [](Symbol const* ptr) -> Symbol const& { + return *ptr; + }); + + // Ensure the source metadata is finalized + for (auto& srcInfo: srcInfos) + { + auto& mutSrcInfo = const_cast(srcInfo); + copyDetails(mutSrcInfo); + } + + // Copy returns only if destination is empty + if (destDoc.returns.empty()) + { + for (auto const& srcInfo: srcInfos) + { + MRDOCS_CHECK_OR_CONTINUE(srcInfo.doc); + for (doc::ReturnsBlock const& returnsEl: srcInfo.doc->returns) + { + MRDOCS_CHECK_OR_CONTINUE(!contains(destDoc.returns, returnsEl)); + destDoc.returns.push_back(returnsEl); + } + } + } + + // Copy only params that don't exist at the destination + // documentation but that do exist in the destination + // function parameters declaration. + if (I.isFunction()) + { + auto& destF = I.asFunction(); + for (Symbol const& srcInfo: srcInfos) + { + MRDOCS_CHECK_OR_CONTINUE(srcInfo.isFunction()); + auto const& srcFunction = srcInfo.asFunction(); + MRDOCS_CHECK_OR_CONTINUE(srcFunction.doc); + for (DocComment const& srcDocComment = *srcFunction.doc; + auto const& srcDocParam: srcDocComment.params) + { + // check if param doc doesn't already exist + MRDOCS_CHECK_OR_CONTINUE( + std::ranges::none_of( + destDoc.params, + [&srcDocParam](doc::ParamBlock const& destDocParam) { + return srcDocParam.name == destDocParam.name; + })); + + // check if param name exists in the destination function + MRDOCS_CHECK_OR_CONTINUE( + std::ranges::any_of( + destF.Params, + [&srcDocParam](Param const& destParam) { + return srcDocParam.name == *destParam.Name; + })); + + // Push the new param ot the + destDoc.params.push_back(srcDocParam); + } + } + } + + // Copy only tparams that don't exist at the destination + // documentation but that do exist in the destination + // template parameters. + auto getTemplateInfo = [](Symbol& I) -> TemplateInfo const* + { + return visit(I, [](auto& I) -> TemplateInfo const* { + if constexpr (requires { I.Template; }) + { + if (I.Template) + { + return &*I.Template; + } + } + return nullptr; + }); + }; + + + if (auto const destTemplateInfo = getTemplateInfo(I)) + { + for (Symbol const& srcInfo: srcInfos) + { + MRDOCS_CHECK_OR_CONTINUE(srcInfo.doc); + for (DocComment const& srcDocComment = *srcInfo.doc; + auto const& srcTParam: srcDocComment.tparams) + { + // tparam doesn't already exist at the destination + MRDOCS_CHECK_OR_CONTINUE( + std::ranges::none_of( + destDoc.tparams, + [&srcTParam](doc::TParamBlock const& destTParam) { + return srcTParam.name == destTParam.name; + })); + + // TParam name exists in the destination definition + MRDOCS_CHECK_OR_CONTINUE( + std::ranges::any_of( + destTemplateInfo->Params, + [&srcTParam]( + Polymorphic const& destTParam) { + return srcTParam.name == destTParam->Name; + })); + + // Push the new param + destDoc.tparams.push_back(srcTParam); + } + } + } + + // Copy exceptions only if destination exceptions are empty + // and the destination is not noexcept + bool const destIsNoExcept = + I.isFunction() && + I.asFunction().Noexcept.Kind == NoexceptKind::False; + if (destDoc.exceptions.empty() && + !destIsNoExcept) + { + for (Symbol const& srcInfo: srcInfos) + { + MRDOCS_CHECK_OR_CONTINUE(srcInfo.doc); + for (doc::ThrowsBlock const& exceptionsEl: srcInfo.doc->exceptions) + { + MRDOCS_CHECK_OR_CONTINUE(!contains(destDoc.exceptions, exceptionsEl)); + destDoc.exceptions.push_back(exceptionsEl); + } + } + } + + // Copy sees only if destination sees are empty + if (destDoc.sees.empty()) + { + for (Symbol const& srcInfo: srcInfos) + { + MRDOCS_CHECK_OR_CONTINUE(srcInfo.doc); + for (doc::SeeBlock const& seesEl: srcInfo.doc->sees) + { + MRDOCS_CHECK_OR_CONTINUE(!contains(destDoc.sees, seesEl)); + destDoc.sees.push_back(seesEl); + } + } + } + + // Copy preconditions only if destination preconditions is empty + if (destDoc.preconditions.empty()) + { + for (Symbol const& srcInfo: srcInfos) + { + MRDOCS_CHECK_OR_CONTINUE(srcInfo.doc); + for (doc::PreconditionBlock const& preconditionsEl: srcInfo.doc->preconditions) + { + MRDOCS_CHECK_OR_CONTINUE(!contains(destDoc.preconditions, preconditionsEl)); + destDoc.preconditions.push_back(preconditionsEl); + } + } + } + + // Copy postconditions only if destination postconditions is empty + if (destDoc.postconditions.empty()) + { + for (Symbol const& srcInfo: srcInfos) + { + MRDOCS_CHECK_OR_CONTINUE(srcInfo.doc); + for (doc::PostconditionBlock const& postconditionsEl: + srcInfo.doc->postconditions) + { + MRDOCS_CHECK_OR_CONTINUE(!contains( + destDoc.postconditions, + postconditionsEl)); + destDoc.postconditions.push_back(postconditionsEl); + } + } + } + }; + + // Ensure the source metadata is finalized + Symbol const& res = *resRef; + if (!res.isOverloads()) + { + // If it's a single element, we check the element doc + if (!res.doc) + { + if (corpus_.config->warnings && + corpus_.config->warnBrokenRef && + !refWarned_.contains({copied.string, I.Name})) + { + auto resPrimaryLoc = getPrimaryLocation(res); + this->warn( + I, + "{}: Failed to copy metadata from {} '{}' (no documentation available).\n" + " No metadata available.\n" + " {}:{}\n" + " Note: No documentation available for '{}'.", + corpus_.Corpus::qualifiedName(I), + toString(res.Kind), + copied.string, + resPrimaryLoc->FullPath, + resPrimaryLoc->LineNumber, + corpus_.Corpus::qualifiedName(res)); + } + continue; + } + llvm::SmallVector srcInfos = { &res }; + copyInfoRangeMetadata(srcInfos); + } + else + { + auto& OI = res.asOverloads(); + llvm::SmallVector srcInfos; + srcInfos.reserve(OI.Members.size()); + for (auto& memberID: OI.Members) + { + Symbol* member = corpus_.find(memberID); + MRDOCS_CHECK_OR_CONTINUE(member); + srcInfos.push_back(member); + } + copyInfoRangeMetadata(srcInfos); + } + } + + if (I.doc) + { + copyDetails(I,*I.doc); + } +} + +void +DocCommentFinalizer:: +copyDetails(Symbol const& ctx, DocComment& doc) +{ + for (auto blockIt = doc.Document.begin(); blockIt != doc.Document.end();) + { + // Get paragraph + auto& block = *blockIt; + if (!block->isParagraph()) + { + ++blockIt; + continue; + } + auto& para = block->asParagraph(); + if (para.empty()) + { + ++blockIt; + continue; + } + + // Find copydetails command + Optional copied; + for (auto inlineIt = para.children.begin(); inlineIt != para.children.end();) + { + // Find copydoc command + auto& inlineEl = *inlineIt; + if (!inlineEl->isCopyDetails()) + { + ++inlineIt; + continue; + } + // Copy reference + copied = inlineEl->asCopyDetails(); + + // Remove copied node from the inlineEl + /* it2 = */ para.children.erase(inlineIt); + break; + } + + // Trim the paragraph after removing the copydetails command + doc::trim(para.asInlineContainer()); + + // Remove empty children from the paragraph + std::erase_if(para.children, [](Polymorphic const& child) { + return doc::isEmpty(child); + }); + + // We should merge consecutive text nodes that have exactly the + // same terminal kind + + // Remove the entire paragraph block from the doc if it is empty + if (para.empty()) + { + blockIt = doc.Document.erase(blockIt); + MRDOCS_CHECK_OR_CONTINUE(copied); + } + + // Nothing to copy: continue to the next block + if (!copied) + { + ++blockIt; + continue; + } + + // Find the node to copy from + auto resRef = corpus_.lookup(ctx.id, copied->string); + if (!resRef) + { + if (corpus_.config->warnings && + corpus_.config->warnBrokenRef && + !refWarned_.contains({copied->string, ctx.Name})) + { + this->warn( + ctx, + "{}: Failed to copy documentation from '{}' (symbol not found)\n" + " {}", + corpus_.Corpus::qualifiedName(ctx), + copied->string, + resRef.error().reason()); + } + continue; + } + + // Ensure the source node is finalized + Symbol const& res = *resRef; + resolveReferences(const_cast(res)); + + // Check if there's any documentation details to copy + if (!res.doc) + { + if (corpus_.config->warnings && + corpus_.config->warnBrokenRef && + !refWarned_.contains({copied->string, ctx.Name})) + { + auto resPrimaryLoc = getPrimaryLocation(res); + this->warn( + ctx, + "{}: Failed to copy documentation from {} '{}' (no documentation available).\n" + " No documentation available.\n" + " {}:{}\n" + " Note: No documentation available for '{}'.", + corpus_.Corpus::qualifiedName(ctx), + toString(res.Kind), + copied->string, + resPrimaryLoc->FullPath, + resPrimaryLoc->LineNumber, + corpus_.Corpus::qualifiedName(res)); + } + continue; + } + + // Copy detail blocks from source to destination to + // the same position in the destination + DocComment const& src = *res.doc; + if (!src.Document.empty()) + { + blockIt = doc.Document.insert(blockIt, src.Document.begin(), src.Document.end()); + blockIt += src.Document.size(); + } + } +} + +void +DocCommentFinalizer::generateOverloadDocs(OverloadsSymbol& I) +{ + if (!I.doc) + { + I.doc.emplace(); + } + + // Create a view all Info members of I. + // The doc for these function should already be as + // complete as possible + auto functions = + I.Members | + std::views::transform([&](SymbolID const& id) + { + return corpus_.find(id); + }) | + std::views::filter([](Symbol const* infoPtr) + { + return infoPtr != nullptr && infoPtr->isFunction(); + }) | + std::views::transform([](Symbol const* infoPtr) -> FunctionSymbol const& + { + return infoPtr->asFunction(); + }); + if (!I.doc) + { + I.doc.emplace(); + } + + // briefs: populated in a previous step + // blocks: we do not copy doc detail blocks because + // it's impossible to guarantee that the details for + // any of the functions make sense for all overloads. + // We can only merge metadata. + populateOverloadsReturns(I, functions); + populateOverloadsParams(I, functions); + populateOverloadsTParams(I, functions); + populateOverloadsExceptions(I, functions); + populateOverloadsSees(I, functions); + populateOverloadsPreconditions(I, functions); + populateOverloadsPostconditions(I, functions); +} + +void +DocCommentFinalizer::resolveReferences(Symbol& I) +{ + MRDOCS_CHECK_OR(!finalized_.contains(&I)); + finalized_.emplace(&I); + + report::trace( + "Finalizing doc for '{}'", + corpus_.Corpus::qualifiedName(I)); + + if (I.doc) + { + auto& doc = *I.doc; + bottomUpTraverse(doc, makeOverload( + [this, &I](doc::ReferenceInline& node) { this->resolveReference(I, node, true); }, + [this, &I](doc::ThrowsBlock& node) { this->resolveReference(I, node.exception, false); })); + } +} + +void +DocCommentFinalizer::resolveReference( + Symbol const& ctx, + doc::ReferenceInline& ref, + bool const emitWarning) +{ + if (ref.id != SymbolID::invalid) + { + // Already resolved + return; + } + if (auto resRef = corpus_.lookup(ctx.id, ref.literal)) + { + // KRYSTIAN NOTE: We should provide an overload that + // returns a non-const reference. + auto& res = const_cast(*resRef); + ref.id = res.id; + } + else if ( + emitWarning && + corpus_.config->warnings && + corpus_.config->warnBrokenRef && + // Only warn once per reference + !refWarned_.contains({ref.literal, ctx.Name}) && + // Ignore std:: references + !ref.literal.starts_with("std::")) + { + this->warn( + ctx, + "{}: Failed to resolve reference to '{}'\n" + " {}", + corpus_.Corpus::qualifiedName(ctx), + ref.literal, + resRef.error().reason()); + refWarned_.insert({ref.literal, ctx.Name}); + } +} + +void +DocCommentFinalizer:: +generateAutoFunctionMetadata(FunctionSymbol& I) const +{ + // For special functions (constructors, destructors, ...), + // we create the doc if it does not exist because + // we can populate all the fields from the function category. + // For other types of functions, we'll only populate + // the missing fields when the doc already exists. + bool const isSpecial = isSpecialFunction(I); + MRDOCS_CHECK_OR(isSpecial || I.doc); + bool forceEmplaced = false; + if (isSpecial && + !I.doc) + { + I.doc.emplace(); + forceEmplaced = true; + } + + // Populate a missing doc brief + populateFunctionBrief(I, corpus_); + + // Populate a missing doc returns + populateFunctionReturns(I, corpus_); + + // Populate missing doc params + populateFunctionParams(I, corpus_); + + // If we forcefully created the doc, we need to + // check if the function was able to populate all the + // fields. If not, we'll remove the doc. + if (forceEmplaced) + { + // Check brief and returns + if (!I.doc->brief) + { + I.doc.reset(); + return; + } + + if (!is_one_of(I.Class, { + FunctionClass::Constructor, + FunctionClass::Destructor }) && + I.doc->returns.empty()) + { + I.doc.reset(); + return; + } + + // Check params size + std::size_t const nNamedParams = std::ranges:: + count_if(I.Params, [](Param const& p) -> bool { + return p.Name.has_value(); + }); + auto const documentedParams = getDocCommentParamNames(*I.doc); + if (nNamedParams != documentedParams.size()) + { + I.doc.reset(); + return; + } + + // Check param names + if (!std::ranges::all_of(I.Params, [&](Param const& param) { + if (param.Name) + { + return contains(documentedParams, *param.Name); + } + return true; + })) + { + I.doc.reset(); + } + } +} + +namespace { +// Comparison function for reference to keep the related +// references sorted by name. +bool +referenceCmp( + doc::ReferenceInline const& lhs, + doc::ReferenceInline const& rhs) { + bool const lhsIsGlobal = lhs.literal.starts_with("::"); + bool const rhsIsGlobal = rhs.literal.starts_with("::"); + if (lhsIsGlobal != rhsIsGlobal) + { + return lhsIsGlobal < rhsIsGlobal; + } + std::size_t const lhsCount = std::ranges::count(lhs.literal, ':'); + std::size_t const rhsCount = std::ranges::count(rhs.literal, ':'); + if (lhsCount != rhsCount) + { + return lhsCount < rhsCount; + } + if (lhs.literal != rhs.literal) + { + return lhs.literal < rhs.literal; + } + return lhs.id < rhs.id; +} +} + +void +DocCommentFinalizer:: +processRelates(Symbol& ctx, DocComment& doc) +{ + if (corpus_.config->autoRelates) + { + setAutoRelates(ctx); + } + + MRDOCS_CHECK_OR(!doc.relates.empty()); + + Symbol const* currentPtr = corpus_.find(ctx.id); + MRDOCS_ASSERT(currentPtr); + Symbol const& current = *currentPtr; + + if (!current.isFunction()) + { + this->warn( + ctx, + "{}: `@relates` only allowed for functions", + corpus_.Corpus::qualifiedName(current)); + doc.relates.clear(); + return; + } + + for (doc::ReferenceInline& ref: doc.relates) + { + resolveReference(ctx, ref, true); + Symbol* relatedPtr = corpus_.find(ref.id); + MRDOCS_CHECK_OR_CONTINUE(relatedPtr); + Symbol& related = *relatedPtr; + if (!related.doc) + { + related.doc.emplace(); + } + if (std::ranges::none_of( + related.doc->related, + [&ctx](doc::ReferenceInline const& otherRef) { + return otherRef.id == ctx.id; + })) + { + std::string currentName = corpus_.Corpus::qualifiedName(current, relatedPtr->Parent); + doc::ReferenceInline relatedRef(std::move(currentName)); + relatedRef.id = ctx.id; + // Insert in order by name + auto const it = std::ranges::lower_bound( + related.doc->related, + relatedRef, + referenceCmp); + related.doc->related.insert(it, std::move(relatedRef)); + } + } + + // Erase anything in the doc without a valid id + std::erase_if(doc.relates, [](doc::ReferenceInline const& ref) { + return !ref.id; + }); +} + +namespace { +void +pushAllDerivedClasses( + RecordSymbol const* record, + llvm::SmallVector& relatedRecordsOrEnums, + CorpusImpl& corpus) +{ + for (auto& derivedId : record->Derived) + { + Symbol* derivedPtr = corpus.find(derivedId); + MRDOCS_CHECK_OR_CONTINUE(derivedPtr); + MRDOCS_CHECK_OR_CONTINUE(derivedPtr->Extraction == ExtractionMode::Regular); + auto derived = dynamic_cast(derivedPtr); + MRDOCS_CHECK_OR_CONTINUE(derived); + relatedRecordsOrEnums.push_back(derived); + // Recursively get derived classes of the derived class + pushAllDerivedClasses(derived, relatedRecordsOrEnums, corpus); + } +} +} + +void +DocCommentFinalizer:: +setAutoRelates(Symbol& ctx) +{ + MRDOCS_CHECK_OR(ctx.Extraction == ExtractionMode::Regular); + MRDOCS_CHECK_OR(ctx.isFunction()); + MRDOCS_CHECK_OR(ctx.doc); + auto& I = ctx.asFunction(); + MRDOCS_CHECK_OR(!I.IsRecordMethod); + auto* parentPtr = corpus_.find(I.Parent); + MRDOCS_CHECK_OR(parentPtr); + MRDOCS_CHECK_OR(parentPtr->isNamespace()); + + auto toRecordOrEnum = [&](Polymorphic const& type) -> Symbol* { + MRDOCS_CHECK_OR(type, nullptr); + auto& innermost = innermostType(type); + MRDOCS_CHECK_OR(innermost, nullptr); + MRDOCS_CHECK_OR(innermost->isNamed(), nullptr); + auto const& namedType = dynamic_cast(*innermost); + MRDOCS_CHECK_OR(namedType.Name, nullptr); + SymbolID const namedSymbolID = namedType.Name->id; + MRDOCS_CHECK_OR(namedSymbolID != SymbolID::invalid, nullptr); + Symbol* infoPtr = corpus_.find(namedSymbolID); + MRDOCS_CHECK_OR(infoPtr, nullptr); + MRDOCS_CHECK_OR( + infoPtr->isRecord() || + infoPtr->isEnum(), nullptr); + return infoPtr; + }; + + llvm::SmallVector relatedRecordsOrEnums; + + // 1) Inner type of the first parameter + [&] { + MRDOCS_CHECK_OR(!I.Params.empty()); + auto* firstParamInfo = toRecordOrEnum(I.Params.front().Type); + MRDOCS_CHECK_OR(firstParamInfo); + if (firstParamInfo->Extraction == ExtractionMode::Regular) + { + relatedRecordsOrEnums.push_back(firstParamInfo); + } + // 2) If the type is a reference or a pointer, derived classes + // of this inner type are also valid related records + MRDOCS_CHECK_OR(firstParamInfo->isRecord()); + auto const* firstParamRecord = dynamic_cast(firstParamInfo); + MRDOCS_CHECK_OR( + I.Params.front().Type->isLValueReference() || + I.Params.front().Type->isRValueReference() || + I.Params.front().Type->isPointer()); + // Get all transitively derived classes of firstParamRecord + pushAllDerivedClasses(firstParamRecord, relatedRecordsOrEnums, corpus_); + }(); + + // 3) The return type of the function + if (auto* returnType = toRecordOrEnum(I.ReturnType)) + { + if (returnType->Extraction == ExtractionMode::Regular) + { + relatedRecordsOrEnums.push_back(returnType); + } + // 4) If the return type is a template specialization, + // and the template parameters are records, then + // each template parameter is also a related record + [&] { + MRDOCS_CHECK_OR(I.ReturnType); + MRDOCS_CHECK_OR(I.ReturnType->isNamed()); + auto& NTI = dynamic_cast(*I.ReturnType); + MRDOCS_CHECK_OR(NTI.Name); + MRDOCS_CHECK_OR(NTI.Name->isSpecialization()); + auto const& NTIS = dynamic_cast(*NTI.Name); + MRDOCS_CHECK_OR(!NTIS.TemplateArgs.empty()); + Polymorphic const& firstArg = NTIS.TemplateArgs.front(); + MRDOCS_CHECK_OR(firstArg->isType()); + auto const& typeArg = dynamic_cast(*firstArg); + if (auto* argInfo = toRecordOrEnum(typeArg.Type)) + { + if (argInfo->Extraction == ExtractionMode::Regular) + { + relatedRecordsOrEnums.push_back(argInfo); + } + } + }(); + } + + // Remove duplicates from relatedRecordsOrEnums + std::ranges::sort(relatedRecordsOrEnums); + relatedRecordsOrEnums.erase( + std::ranges::unique(relatedRecordsOrEnums).begin(), + relatedRecordsOrEnums.end()); + + // Insert the records with valid ids into the doc relates section + std::size_t const prevRelatesSize = I.doc->relates.size(); + for (Symbol const* relatedRecordOrEnumPtr : relatedRecordsOrEnums) + { + MRDOCS_CHECK_OR_CONTINUE(relatedRecordOrEnumPtr); + MRDOCS_ASSERT(I.doc); + Symbol const& recordOrEnum = *relatedRecordOrEnumPtr; + MRDOCS_CHECK_OR_CONTINUE(recordOrEnum.Extraction == ExtractionMode::Regular); + doc::ReferenceInline ref(recordOrEnum.Name); + ref.id = recordOrEnum.id; + + // Check if already listed as friend + if (auto* record = dynamic_cast(relatedRecordOrEnumPtr)) + { + using std::views::transform; + if (contains(transform(record->Friends, &FriendInfo::id), I.id)) + { + // Already listed as a public friend + continue; + } + } + + // Ensure no duplicates + if (std::ranges::none_of( + I.doc->relates, + [&ref](doc::ReferenceInline const& otherRef) { + return otherRef.literal == ref.literal || otherRef.id == ref.id; + })) + { + // Insert in order by name + auto const it = std::ranges::lower_bound( + I.doc->relates.begin() + prevRelatesSize, + I.doc->relates.end(), + ref, + referenceCmp); + I.doc->relates.insert(it, std::move(ref)); + } + } +} + +void +DocCommentFinalizer:: +tidyUp(DocComment& doc) +{ + // Bottom-up traversal cleaning up the doc + bottomUpTraverse(doc, [](NodeTy& node) { + // Remove any @copy* nodes that got left behind + if constexpr (requires { { node.children } -> range_of>; }) + { + std::erase_if(node.children, [](Polymorphic const& el) + { + return el->isCopyDetails(); + }); + } + + // - Trim leading and trailing empty inlines in the node + // - Merging consecutive empty blocks (like HTML whitespace normalization) + // To be implemented and improved as needed + if constexpr (std::derived_from) + { + doc::trim(node.asBlock()); + } + + // Remove consecutive whitespace characters in text nodes + if constexpr (std::same_as) + { + auto& textNode = static_cast(node); + std::string_view sv = textNode.literal; + + // Early out if there is NO consecutive whitespace. + auto it = std::ranges::adjacent_find(sv, [](char a, char b) { + return isWhitespace(a) && isWhitespace(b); + }); + if (it == sv.end()) + { + return; + } + + std::string out; + out.reserve(sv.size()); + + bool lastWasSpace = false; // whether we last EMITTED a space + for (char c: sv) + { + if (isWhitespace(c)) + { + if (!lastWasSpace) + { + out.push_back(' '); + lastWasSpace = true; + } + } + else + { + out.push_back(c); + lastWasSpace = false; + } + } + + textNode.literal = std::move(out); + } + + // - Remove any child blocks or inlines without content + // (especially after we do the trimming bottom up) + if constexpr (requires { { node.children } -> range_of>; }) + { + std::erase_if(node.children, [](Polymorphic const& el) + { + return isEmpty(el); + }); + } + if constexpr (requires { { node.blocks } -> range_of>; }) + { + std::erase_if(node.blocks, [](Polymorphic const& el) + { + return isEmpty(el); + }); + } + if constexpr (requires { { node.Document } -> range_of>; }) + { + std::erase_if(node.Document, [](Polymorphic const& el) + { + return isEmpty(el); + }); + } + + // - Unindenting code blocks (but not Code inlines) + if constexpr (std::same_as) + { + auto& codeBlock = static_cast(node); + codeBlock.literal = reindentCode(codeBlock.literal, 0); + } + }); + + // Remove elements of main DocComment that happen to be empty after trimming + // Lambda that takes a vector of T and removes elements for which isEmpty returns true + auto removeEmpty = [](auto& vec) { + std::erase_if(vec, [](auto const& el) { + return isEmpty(el); + }); + }; + removeEmpty(doc.Document); + removeEmpty(doc.returns); + removeEmpty(doc.params); + removeEmpty(doc.tparams); + removeEmpty(doc.exceptions); + removeEmpty(doc.sees); + removeEmpty(doc.preconditions); + removeEmpty(doc.postconditions); + // removeEmpty(doc.relates); + // removeEmpty(doc.related); + if (doc.brief && isEmpty(*doc.brief)) + { + doc.brief.reset(); + } +} + +void +DocCommentFinalizer:: +normalizeSiblings(DocComment& doc) +{ + // Bottom-up traversal cleaning up the doc + bottomUpTraverse(doc, [](doc::InlineContainer& node) { + // Only containers with inline children can participate in merging + // (1) Optional: flatten trivial same-type single-child wrappers + // e.g. ...... + // We do this locally for each child to prevent unnecessary barriers + // to sibling merge. + for (auto& ch: node.children) + { + visit(*ch, [](InlineTy& inl) { + auto* outer = dynamic_cast(&inl); + MRDOCS_CHECK_OR(outer); + MRDOCS_CHECK_OR(outer->children.size() == 1); + auto& only = outer->children.front(); + MRDOCS_CHECK_OR(only->Kind == inl.Kind); + auto* only_inner = dynamic_cast(&*only); + MRDOCS_CHECK_OR(only_inner); + // Move grandchildren up into outer + outer->children.insert( + outer->children.end(), + std::make_move_iterator(only_inner->children.begin()), + std::make_move_iterator(only_inner->children.end())); + only_inner->children.clear(); + // make `only` a moved-from node to be removed later + // (we can't just reset/move it out because we're in a + // reference to it in the vector) + auto tmp = std::move(only); + }); + } + // Filter out any nulls created by the flatten step + std::erase_if(node.children, [](Polymorphic const& el) { + return el.valueless_after_move(); + }); + + // (2) Single left→right pass that coalesces adjacent siblings + // - Text + Text: concatenate + // - Same-kind wrappers: move-append children + // (attributes must match if you model them; keep the check + // next to Kind) + std::vector> out; + out.reserve(node.children.size()); + auto can_merge_same_kind = + [](doc::Inline const& a, doc::Inline const& b) { + // Filter out kinds that don't make sense to merge, + // like images and links. + return a.Kind == b.Kind + && !is_one_of( + a.Kind, + { doc::InlineKind::Link, + doc::InlineKind::Image, + doc::InlineKind::LineBreak, + doc::InlineKind::SoftBreak }); + }; + for (auto& cur: node.children) + { + MRDOCS_ASSERT(!cur.valueless_after_move()); + + if (!out.empty()) + { + auto& prev = out.back(); + + // Text + Text + if (prev->isText() && + cur->isText()) + { + prev->asText().literal += cur->asText().literal; + // drop cur + continue; + } + + // Same-kind wrappers: merge containers by moving children + if (can_merge_same_kind(*prev, *cur)) + { + // Try to view both as InlineContainer (non-text + // wrappers should be) + auto* pc = dynamic_cast(&*prev); + auto* cc = dynamic_cast(&*cur); + if (pc && cc) + { + pc->children.insert( + pc->children.end(), + std::make_move_iterator(cc->children.begin()), + std::make_move_iterator(cc->children.end())); + cc->children.clear(); + // merged; drop cur + continue; + } + } + } + + out.emplace_back(std::move(cur)); + } + + node.children = std::move(out); + }); +} + +void +DocCommentFinalizer:: +parseInlines(DocComment& doc) +{ + bottomUpTraverse(doc, [] NodeTy>(NodeTy& node) { + if constexpr (requires { { node.children } -> range_of>; }) + { + auto it = node.children.begin(); + while (it != node.children.end()) + { + Polymorphic& el = *it; + + // Advance when doesn't text + if (!el->isText()) { + ++it; + continue; + } + + auto& textEl = el->asText(); + doc::InlineContainer v; + ParseResult r = parse(textEl.literal, v); + + // advance on parse failure + if (!r) + { + ++it; + continue; + } + + // Remove the original text node; 'it' becomes the + // insertion position. + it = node.children.erase(it); + + // Move-insert each parsed child; + // advance using returned iterators. + for (auto& child : v.children) + { + it = node.children.insert(it, std::move(child)); + ++it; + } + } + } + }); +} + +namespace { +// A function erases all references in a vector that don't exist +// in the corpus with invalid references. +inline void +removeInvalidIds(CorpusImpl& corpus, std::vector& refs) +{ + std::erase_if(refs, [&corpus](SymbolID const& id) { + if (id == SymbolID::invalid) + { + return true; + } + if (!corpus.find(id)) + { + return true; + } + return false; + }); +} + +inline void +removeInvalidIds(CorpusImpl& corpus, std::vector& refs) +{ + std::erase_if(refs, [&corpus](struct Name const& N) { + if (N.id == SymbolID::invalid) + { + return true; + } + if (!corpus.find(N.id)) + { + return true; + } + if (N.isSpecialization()) + { + if (!corpus.find(N.asSpecialization().specializationID)) + { + return true; + } + } + return false; + }); +} + +inline void +removeInvalidIds(CorpusImpl& corpus, std::vector& refs) +{ + std::erase_if(refs, [&corpus](doc::ReferenceInline const& ref) { + if (ref.id == SymbolID::invalid) + { + return true; + } + if (!corpus.find(ref.id)) + { + return true; + } + return false; + }); +} + +inline void +removeInvalidIds(CorpusImpl& corpus, TemplateInfo& T) +{ + if (T.Primary != SymbolID::invalid && + !corpus.find(T.Primary)) + { + T.Primary = SymbolID::invalid; + } +} +} + +void +DocCommentFinalizer:: +removeInvalidReferences(Symbol& I) +{ + if (auto* asUsing = dynamic_cast(&I)) + { + removeInvalidIds(corpus_, asUsing->ShadowDeclarations); + } + else if (auto* asNamespace = dynamic_cast(&I)) + { + removeInvalidIds(corpus_, asNamespace->UsingDirectives); + } + else if (auto* asNamespaceAlias = dynamic_cast(&I)) + { + if (!corpus_.find(asNamespaceAlias->AliasedSymbol.id)) + { + asNamespaceAlias->AliasedSymbol.id = SymbolID::invalid; + } + } + else if (auto* asFunction = dynamic_cast(&I)) + { + if (asFunction->Template) + { + removeInvalidIds(corpus_, *asFunction->Template); + } + } + else if (auto* asRecord = dynamic_cast(&I)) + { + if (asRecord->Template) + { + removeInvalidIds(corpus_, *asRecord->Template); + } + } + else if (auto* asTypedef = dynamic_cast(&I)) + { + if (asTypedef->Template) + { + removeInvalidIds(corpus_, *asTypedef->Template); + } + } + else if (auto* asVariable = dynamic_cast(&I)) + { + if (asVariable->Template) + { + removeInvalidIds(corpus_, *asVariable->Template); + } + } + else if (auto* asConcept = dynamic_cast(&I)) + { + if (asConcept->Template) + { + removeInvalidIds(corpus_, *asConcept->Template); + } + } + + MRDOCS_CHECK_OR(I.doc); + auto& J = *I.doc; + removeInvalidReferences(J); +} + +void +DocCommentFinalizer:: +removeInvalidReferences(DocComment& doc) +{ + // Use the bottom up traversal to ensure that + // we resolve references in inner nodes. + // The only nodes that can contain references + // are ReferenceInline and ThrowsBlock. + bottomUpTraverse(doc, Overload( + [this](DocComment& node) { + removeInvalidIds(corpus_, node.relates); + removeInvalidIds(corpus_, node.related); + }, + [this](doc::ReferenceInline& node) { + if (node.id != SymbolID::invalid) + { + if (!corpus_.find(node.id)) + { + node.id = SymbolID::invalid; + } + } + }, + [this](doc::ThrowsBlock& node) { + if (node.exception.id != SymbolID::invalid) + { + if (!corpus_.find(node.exception.id)) + { + node.exception.id = SymbolID::invalid; + } + } + })); +} + +namespace { +// Expand tabs to spaces using a tab stop of 8 (common in toolchains) +inline +std::string +expand_tabs(std::string_view s, std::size_t tabw) +{ + std::string out; + out.reserve(s.size()); + std::size_t col = 0; + for (char ch: s) + { + if (ch == '\t') + { + std::size_t spaces = tabw - (col % tabw); + out.append(spaces, ' '); + col += spaces; + } else + { + out.push_back(ch); + // naive column advance; + // good enough for ASCII/byte-based columns + ++col; + } + } + return out; +} + +// Split into lines; tolerates \n, \r\n, and final line w/o newline +inline +std::vector +split_lines(std::string const& text) +{ + std::vector lines; + std::size_t start = 0; + while (start <= text.size()) + { + auto nl = text.find('\n', start); + if (nl == std::string::npos) + { + // last line (may be empty) + lines.emplace_back(text.data() + start, text.size() - start); + break; + } + // trim a preceding '\r' if present + std::size_t len = nl - start; + if (len > 0 && text[nl - 1] == '\r') + { + --len; + } + lines.emplace_back(text.data() + start, len); + start = nl + 1; + } + return lines; +} +} // namespace + +void +DocCommentFinalizer::emitWarnings() +{ + MRDOCS_CHECK_OR(corpus_.config->warnings); + warnUndocumented(); + warnDocErrors(); + warnNoParamDocs(); + warnUndocEnumValues(); + warnUnnamedParams(); + + auto const level = !corpus_.config->warnAsError ? + report::Level::warn : + report::Level::error; + + // Simple cache for the last file we touched + std::string_view lastPath; + std::string fileContents; + std::vector fileLines; + + for (auto const& [loc, msgs]: warnings_) + { + // Build the location header + std::string out; + out += std::format("{}:{}:{}:\n", loc.FullPath, loc.LineNumber, loc.ColumnNumber); + + // Append grouped messages for this location + { + int i = 1; + for (auto const& msg: msgs) + { + out += std::format(" {}) {}\n", i++, msg); + } + } + + // Render the source snippet if possible + // Load file if path changed + if (loc.FullPath != lastPath) + { + lastPath = loc.FullPath; + fileContents.clear(); + fileLines.clear(); + + if (auto expFileContents = files::getFileText(loc.FullPath); + expFileContents) + { + fileContents = std::move(*expFileContents); + fileLines = split_lines(fileContents); + } + else + { + fileLines.clear(); + } + } + + if (loc.LineNumber < fileLines.size() && + loc.LineNumber > 0) + { + std::string_view rawLine = fileLines[loc.LineNumber - 1]; + std::size_t caretCol = + loc.ColumnNumber < rawLine.size() && + loc.ColumnNumber > 0 + ? loc.ColumnNumber - 1 + : std::size_t(-1); + std::string lineExpanded = expand_tabs(rawLine, 8); + + // Compute width for the line number gutter + std::string gutter = std::format(" {} | ", loc.LineNumber); + out += gutter; + + // Line text + out += lineExpanded; + out += "\n"; + + // Create gutter for the caret line + std::size_t const gutterWidth = gutter.size(); + gutter = std::string(gutterWidth - 2, ' ') + "| "; + out += gutter; + + if (caretCol != std::size_t(-1) && caretCol < rawLine.size()) + { + std::size_t expandedCaretCol = 0; + for (std::size_t i = 0; i < caretCol; ++i) + { + if (rawLine[i] == '\t') + { + expandedCaretCol += 8; + } + else + { + ++expandedCaretCol; + } + } + MRDOCS_ASSERT(expandedCaretCol <= lineExpanded.size()); + + out += std::string(expandedCaretCol, ' '); + out += "^"; + + out += std::string(lineExpanded.size() - expandedCaretCol - 1, '~'); + out += "\n"; + } + } + + report::log(level, out); + } +} + +void +DocCommentFinalizer::warnUndocumented() +{ + MRDOCS_CHECK_OR(corpus_.config->warnIfUndocumented); + for (auto& undocI: corpus_.undocumented_) + { + if (Symbol const* I = corpus_.find(undocI.id)) + { + MRDOCS_CHECK_OR( + !I->doc || I->Extraction == ExtractionMode::Regular); + } + bool const prefer_definition = is_one_of( + undocI.kind, {SymbolKind::Record, SymbolKind::Enum}); + this->warn( + *getPrimaryLocation(undocI.Loc, prefer_definition), + "{}: Symbol is undocumented", + undocI.name); + } + corpus_.undocumented_.clear(); +} + +void +DocCommentFinalizer:: +warnDocErrors() +{ + MRDOCS_CHECK_OR(corpus_.config->warnIfDocError); + for (auto const& I : corpus_.info_) + { + MRDOCS_CHECK_OR_CONTINUE(I->Extraction == ExtractionMode::Regular); + MRDOCS_CHECK_OR_CONTINUE(I->isFunction()); + warnParamErrors(dynamic_cast(*I)); + } +} + +void +DocCommentFinalizer:: +warnParamErrors(FunctionSymbol const& I) +{ + MRDOCS_CHECK_OR(I.doc); + + // Check for duplicate doc parameters + auto docParamNames = getDocCommentParamNames(*I.doc); + std::ranges::sort(docParamNames); + auto [firstDup, lastUnique] = std::ranges::unique(docParamNames); + auto duplicateParamNames = std::ranges::subrange(firstDup, lastUnique); + auto [firstDupDup, _] = std::ranges::unique(duplicateParamNames); + for (auto const uniqueDuplicateParamNames = std::ranges::subrange(firstDup, firstDupDup); + std::string_view duplicateParamName: uniqueDuplicateParamNames) + { + this->warn( + *getPrimaryLocation(I), + "{}: Duplicate parameter documentation for '{}'", + corpus_.Corpus::qualifiedName(I), + duplicateParamName); + } + docParamNames.erase(lastUnique, docParamNames.end()); + + // Check for documented parameters that don't exist in the function + auto paramNames = + std::views::transform(I.Params, &Param::Name) | + std::views::filter([](Optional const& name) { return static_cast(name); }) | + std::views::transform([](Optional const& name) -> std::string_view { return *name; }); + for (std::string_view docParamName: docParamNames) + { + if (std::ranges::find(paramNames, docParamName) == paramNames.end()) + { + this->warn( + *getPrimaryLocation(I), + "{}: Documented parameter '{}' does not exist", + corpus_.Corpus::qualifiedName(I), + docParamName); + } + } + +} + +void +DocCommentFinalizer:: +warnNoParamDocs() +{ + MRDOCS_CHECK_OR(corpus_.config->warnNoParamdoc); + for (auto const& I : corpus_.info_) + { + MRDOCS_CHECK_OR_CONTINUE(I->Extraction == ExtractionMode::Regular); + MRDOCS_CHECK_OR_CONTINUE(I->isFunction()); + MRDOCS_CHECK_OR_CONTINUE(I->doc); + warnNoParamDocs(dynamic_cast(*I)); + } +} + +void +DocCommentFinalizer:: +warnNoParamDocs(FunctionSymbol const& I) +{ + MRDOCS_CHECK_OR(!I.IsDeleted); + // Check for function parameters that are not documented in doc + auto docParamNames = getDocCommentParamNames(*I.doc); + auto paramNames = + std::views::transform(I.Params, &Param::Name) | + std::views::filter([](Optional const& name) { return name.has_value(); }) | + std::views::transform([](Optional const& name) -> std::string_view { return *name; }) | + std::views::filter([](std::string_view const& name) { return !name.empty(); }); + for (auto const& paramName: paramNames) + { + if (std::ranges::find(docParamNames, paramName) == docParamNames.end()) + { + this->warn( + *getPrimaryLocation(I), + "{}: Missing documentation for parameter '{}'", + corpus_.Corpus::qualifiedName(I), + paramName); + } + } + + // Check for undocumented return type + if (I.doc->returns.empty()) + { + MRDOCS_ASSERT(!I.ReturnType.valueless_after_move()); + auto isVoid = [](Type const& returnType) -> bool + { + if (returnType.isNamed()) + { + auto const& namedReturnType = dynamic_cast(returnType); + return namedReturnType.Name->Identifier == "void"; + } + return false; + }; + if (!isVoid(*I.ReturnType)) + { + this->warn( + *getPrimaryLocation(I), + "{}: Missing documentation for return value", + corpus_.Corpus::qualifiedName(I)); + } + } +} + +void +DocCommentFinalizer:: +warnUndocEnumValues() +{ + MRDOCS_CHECK_OR(corpus_.config->warnIfUndocEnumVal); + for (auto const& I : corpus_.info_) + { + MRDOCS_CHECK_OR_CONTINUE(I->isEnumConstant()); + MRDOCS_CHECK_OR_CONTINUE(I->Extraction == ExtractionMode::Regular); + MRDOCS_CHECK_OR_CONTINUE(!I->doc); + this->warn( + *getPrimaryLocation(*I), + "{}: Missing documentation for enum value", + corpus_.Corpus::qualifiedName(*I)); + } +} + +void +DocCommentFinalizer:: +warnUnnamedParams() +{ + MRDOCS_CHECK_OR(corpus_.config->warnUnnamedParam); + for (auto const& I : corpus_.info_) + { + MRDOCS_CHECK_OR_CONTINUE(I->isFunction()); + MRDOCS_CHECK_OR_CONTINUE(I->Extraction == ExtractionMode::Regular); + MRDOCS_CHECK_OR_CONTINUE(I->doc); + warnUnnamedParams(dynamic_cast(*I)); + } +} + +void +DocCommentFinalizer:: +warnUnnamedParams(FunctionSymbol const& I) +{ + auto orderSuffix = [](std::size_t const i) -> std::string + { + if (i == 0) + { + return "st"; + } + if (i == 1) + { + return "nd"; + } + if (i == 2) + { + return "rd"; + } + return "th"; + }; + + for (std::size_t i = 0; i < I.Params.size(); ++i) + { + if (!I.Params[i].Name) + { + this->warn( + *getPrimaryLocation(I), + "{}: {}{} parameter is unnamed", + corpus_.Corpus::qualifiedName(I), + i + 1, + orderSuffix(i)); + } + } +} + +} // mrdocs diff --git a/src/lib/Metadata/Finalizers/DocCommentFinalizer.hpp b/src/lib/Metadata/Finalizers/DocCommentFinalizer.hpp new file mode 100644 index 000000000..a80d87c54 --- /dev/null +++ b/src/lib/Metadata/Finalizers/DocCommentFinalizer.hpp @@ -0,0 +1,333 @@ +// +// 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) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_LIB_METADATA_FINALIZERS_DOCCOMMENTFINALIZER_HPP +#define MRDOCS_LIB_METADATA_FINALIZERS_DOCCOMMENTFINALIZER_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace mrdocs { + +/** Finalizes a set of Info. + + This removes any references to SymbolIDs + which do not exist. + + References which should always be valid + are not checked. +*/ +class DocCommentFinalizer { + CorpusImpl& corpus_; + + /* Broken references for which we have already emitted + a warning. + */ + std::set> refWarned_; + + /* Info objects whose briefs have been finalized + */ + std::set finalized_brief_; + + /* Info objects whose metadata has been finalized + */ + std::set finalized_metadata_; + + /* Info objects that have been finalized + + This is used to avoid recursion when finalizing + references. + */ + std::set finalized_; + + // A comparison function that sorts locations by: + // 1) ascending full path + // 2) descending line number + // This is the most convenient order for users to fix + // warnings in the source code. This is because fixing a problem + // at a particular line, without this ordering, would invalidate + // the line numbers of all subsequent warnings. + struct WarningLocationCompare + { + bool + operator()(Location const& lhs, Location const& rhs) const + { + if (lhs.FullPath != rhs.FullPath) + { + return lhs.FullPath < rhs.FullPath; + } + if (lhs.LineNumber != rhs.LineNumber) + { + return lhs.LineNumber > rhs.LineNumber; + } + return lhs.ColumnNumber > rhs.ColumnNumber; + } + }; + + /* Warnings that should be emitted after finalization + + The warnings are initially stored in this container + where the messages are sorted by location. + + This makes it easier for the user to go through + the warnings in the order they appear in the source + code and fix them. + */ + std::map, WarningLocationCompare> warnings_; + +public: + DocCommentFinalizer(CorpusImpl& corpus) + : corpus_(corpus) + { + } + + /** Finalize the doc for all symbols + */ + void + build(); + +private: + /* Finalize the brief of a symbol + + This might mean copying the brief from another + symbol (when there's a copybrief command) or + populating it automatically (first sentence). + */ + void + finalizeBrief(Symbol& I); + + void + copyBrief(Symbol const& ctx, DocComment& doc); + + void + setAutoBrief(DocComment& doc) const; + + /* Finalize the metadata copies + + Copy the details and metadata from other symbols to + the current symbol context whenever the current + context contains a reference to another symbol + created with \@copydoc or \@copydetails. + */ + void + copyDetails(Symbol& I); + + void + copyDetails(Symbol const& ctx, DocComment& doc); + + /* Populate the metadata of overloads + + This function populates the metadata of overloads + with the metadata of the functions it overloads. + */ + void + generateOverloadDocs(OverloadsSymbol&); + + /* Resolve references in the doc + + This function traverses the doc tree + of a symbol and resolves all references + to other symbols. + + The references are resolved by looking + up the symbol in the corpus and setting the ID of + the reference. + */ + void + resolveReferences(Symbol& I); + + void + resolveReference( + Symbol const& I, + doc::ReferenceInline& ref, + bool emitWarning); + + /* Populate function doc from with missing fields + + This function populates the function doc with + missing fields of special functions. + */ + void + generateAutoFunctionMetadata(FunctionSymbol&) const; + + /* Populate and resolve \@relates references + + This function populates the "relates" symbols + of a doc (if the option is enabled), then + finds the related symbols, resolves them. + In other words, it also sets the inverse + of the "relates" reference so that the related + symbol also knows about the function that + relates to it and can generate a link to it + in the non-member functions section. + */ + void + processRelates(Symbol& I) + { + MRDOCS_CHECK_OR(I.doc); + processRelates(I, *I.doc); + } + + void + processRelates(Symbol& I, DocComment& doc); + + + void + setAutoRelates(Symbol& I); + + /* Normalize doc siblings + + We first do a post-order structural merge/flatten so that, by the time we + run the tidy-up pass below, each container's children are already in a + canonical form: + + - adjacent Text nodes are coalesced, + - adjacent wrappers of the same kind/attributes are merged, + - trivial same-type nesting is flattened. + + Doing this in a dedicated pass avoids backtracking and iterator + invalidation in the tidy-up phase, and guarantees that the tidy-up can + make a single linear scan over each child list without missing newly + created adjacencies. + */ + void + normalizeSiblings(DocComment& doc); + + void + normalizeSiblings(Symbol& I) + { + MRDOCS_CHECK_OR(I.doc); + normalizeSiblings(*I.doc); + } + + /* Tidy up the doc: + + This function performs various bottom-up + tidying operations on the doc, such as: + + - Remove any @copy* nodes that got left behind + - Trimming leading and trailing empty inlines + - Merging consecutive empty blocks (like HTML whitespace normalization) + - Remove any blocks or inlines without content + (especially after we do the trimming bottom up) + - Unindenting code blocks. + */ + void + tidyUp(DocComment& doc); + + void + tidyUp(Symbol& I) + { + MRDOCS_CHECK_OR(I.doc); + tidyUp(*I.doc); + if (I.doc->empty()) + { + I.doc.reset(); + } + } + + /* Parse inlines in terminal text nodes. + */ + void + parseInlines(DocComment& doc); + + void + parseInlines(Symbol& I) + { + MRDOCS_CHECK_OR(I.doc); + parseInlines(*I.doc); + } + + /* Remove references to symbols that are not in the corpus + + This function traverses the symbol and DocComment + tree of a symbol and removes all references + to symbols that do not exist in the corpus. + + These are references clang was able to resolve + when generating the AST, but which do not exist + in the final MrDocs corpus, so they are invalid + references in the context of MrDocs output. + */ + void + removeInvalidReferences(DocComment& doc); + + void + removeInvalidReferences(Symbol& I); + + /* Check the documentation for problems and creates warnings + + We first collect all warnings and then print + them at once at the end of the finalization + process. This way, the warnings can be sorted + by location and the user can fix them in order. + */ + void + emitWarnings(); + + template + void + warn( + Location const& loc, + report::Located const format, + Args&&... args) + { + MRDOCS_CHECK_OR(corpus_.config->warnings); + std::string const str = + std::vformat(format.value, std::make_format_args(args...)); + warnings_[loc].push_back(str); + } + + template + void + warn( + Symbol const& ctx, + report::Located const format, + Args&&... args) + { + MRDOCS_CHECK_OR(corpus_.config->warnings); + auto loc = getPrimaryLocation(ctx); + warn(*loc, format, std::forward(args)...); + } + + void + warnUndocumented(); + + void + warnDocErrors(); + + void + warnParamErrors(FunctionSymbol const& I); + + void + warnNoParamDocs(); + + void + warnNoParamDocs(FunctionSymbol const& I); + + void + warnUndocEnumValues(); + + void + warnUnnamedParams(); + + void + warnUnnamedParams(FunctionSymbol const& I); +}; + +} // mrdocs + +#endif // MRDOCS_LIB_METADATA_FINALIZERS_DOCCOMMENTFINALIZER_HPP diff --git a/src/lib/Metadata/Finalizers/Javadoc/Overloads.hpp b/src/lib/Metadata/Finalizers/Javadoc/Overloads.hpp deleted file mode 100644 index a907edd76..000000000 --- a/src/lib/Metadata/Finalizers/Javadoc/Overloads.hpp +++ /dev/null @@ -1,370 +0,0 @@ -// -// 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_METADATA_FINALIZER_JAVADOCFINALIZER_OVERLOADS_HPP -#define MRDOCS_LIB_METADATA_FINALIZER_JAVADOCFINALIZER_OVERLOADS_HPP - -#include "lib/CorpusImpl.hpp" -#include "lib/Metadata/InfoSet.hpp" -#include "Function.hpp" -#include - -namespace clang::mrdocs { -namespace { -auto -overloadFunctionsRange(OverloadsInfo const& O, CorpusImpl& corpus_) -{ - // Create a view all Info members of O - auto functions = - O.Members | - std::views::transform([&](SymbolID const& id) - { - return corpus_.find(id); - }) | - std::views::filter([](Info const* infoPtr) - { - return infoPtr != nullptr && infoPtr->isFunction(); - }) | - std::views::transform([](Info const* infoPtr) -> FunctionInfo const& - { - return infoPtr->asFunction(); - }); - return functions; -} - -template -bool -populateOverloadsBriefIfAllSameBrief(OverloadsInfo& I, Range&& functionsWithBrief) -{ - auto first = *functionsWithBrief.begin(); - doc::Brief const& firstBrief = *first.javadoc->brief; - if (auto otherFunctions = std::views::drop(functionsWithBrief, 1); - std::ranges::all_of(otherFunctions, [&](FunctionInfo const& otherFunction) - { - doc::Brief const& otherBrief = *otherFunction.javadoc->brief; - return otherBrief.children == firstBrief.children; - })) - { - I.javadoc->brief = firstBrief; - return true; - } - return false; -} - -bool -populateOverloadsFromClass(OverloadsInfo& I) -{ - switch (I.Class) - { - case FunctionClass::Normal: - return false; - case FunctionClass::Constructor: - { - I.javadoc->brief = "Constructors"; - return true; - } - case FunctionClass::Destructor: - { - I.javadoc->brief = "Destructors"; - return true; - } - case FunctionClass::Conversion: - { - I.javadoc->brief = "Conversion operators"; - return true; - } - default: - MRDOCS_UNREACHABLE(); - } -} - -template -bool -populateOverloadsFromOperator(OverloadsInfo& I, Range&& functions) -{ - MRDOCS_CHECK_OR(I.OverloadedOperator != OperatorKind::None, false); - - // Stream insertion operators are implemented as an exception to the operator name - if (I.OverloadedOperator == OperatorKind::LessLess && - std::ranges::all_of(functions, isStreamInsertion)) - { - I.javadoc->brief = "Stream insertion operators"; - return true; - } - - // Find the operator name - auto isBinary = [&](FunctionInfo const& function) { - return (function.Params.size() + function.IsRecordMethod) == 2; - }; - auto isAllBinary = std::ranges::all_of(functions, isBinary); - int const nParams = isAllBinary ? 2 : 1; - auto const res = getOperatorReadableName(I.OverloadedOperator, nParams); - MRDOCS_CHECK_OR(res, false); - std::string briefStr(*res); - briefStr += " operators"; - I.javadoc->brief = std::move(briefStr); - return true; -} - -bool -populateOverloadsFromFunctionName(OverloadsInfo& I) -{ - std::string name = I.Name; - if (name.empty() && - I.OverloadedOperator != OperatorKind::None) - { - name = getOperatorName(I.OverloadedOperator, true); - } - if (name.empty()) - { - return false; - } - I.javadoc->brief.emplace(); - I.javadoc->brief->children.emplace_back( - std::in_place_type, std::string(name), doc::Style::mono); - I.javadoc->brief->children.emplace_back( - std::in_place_type, std::string(" overloads")); - return true; -} - -template -void -populateOverloadsBrief(OverloadsInfo& I, Range&& functions, CorpusImpl& corpus) -{ - auto functionsWithBrief = std::views:: - filter(functions, [](FunctionInfo const& function) { - return function.javadoc && function.javadoc->brief - && !function.javadoc->brief->empty(); - }); - auto anyMemberBrief = !std::ranges::empty(functionsWithBrief); - if (!corpus.config->autoFunctionMetadata && - !anyMemberBrief) - { - // If there are no briefs, and we'll not populate the briefs - // from function names, we'll also not populate the briefs - // of the overload set. - return; - } - if (anyMemberBrief) - { - MRDOCS_CHECK_OR(!populateOverloadsBriefIfAllSameBrief(I, functionsWithBrief)); - } - MRDOCS_CHECK_OR(!populateOverloadsFromClass(I)); - MRDOCS_CHECK_OR(!populateOverloadsFromOperator(I, functions)); - if (anyMemberBrief) - { - // We recur to the function name when the briefs are in conflict - // If there are no briefs, we don't consider it a conflict - // We just leave the overload set also without a brief - MRDOCS_CHECK_OR(!populateOverloadsFromFunctionName(I)); - } -} - -// Populate with all the unique "returns" from the functions -template -void -populateOverloadsReturns(OverloadsInfo& I, Range&& functions) { - auto functionReturns = functions | - std::views::filter([](FunctionInfo const& function) - { - return function.javadoc && !function.javadoc->returns.empty(); - }) | - std::views::transform([](FunctionInfo const& function) - { - return function.javadoc->returns; - }) | - std::views::join; - for (doc::Returns const& functionReturn: functionReturns) - { - auto sameIt = std::ranges::find_if( - I.javadoc->returns, - [&functionReturn](doc::Returns const& overloadReturns) - { - return overloadReturns == functionReturn; - }); - if (sameIt == I.javadoc->returns.end()) - { - I.javadoc->returns.push_back(functionReturn); - } - } -} - -template -void -populateOverloadsParams(OverloadsInfo& I, Range& functions) { - auto functionParams = functions | - std::views::filter([](FunctionInfo const& function) - { - return function.javadoc && !function.javadoc->params.empty(); - }) | - std::views::transform([](FunctionInfo const& function) - { - return function.javadoc->params; - }) | - std::views::join; - for (doc::Param const& functionParam: functionParams) - { - auto sameIt = std::ranges::find_if( - I.javadoc->params, - [&functionParam](doc::Param const& overloadParam) - { - return overloadParam.name == functionParam.name; - }); - if (sameIt == I.javadoc->params.end()) - { - I.javadoc->params.push_back(functionParam); - } - } -} - -template -void -populateOverloadsTParams(OverloadsInfo& I, Range& functions) { - auto functionTParams = functions | - std::views::filter([](FunctionInfo const& function) - { - return function.javadoc && !function.javadoc->tparams.empty(); - }) | - std::views::transform([](FunctionInfo const& function) - { - return function.javadoc->tparams; - }) | - std::views::join; - for (doc::TParam const& functionTParam: functionTParams) - { - auto sameIt = std::ranges::find_if( - I.javadoc->tparams, - [&functionTParam](doc::TParam const& overloadTParam) - { - return overloadTParam.name == functionTParam.name; - }); - if (sameIt == I.javadoc->tparams.end()) - { - I.javadoc->tparams.push_back(functionTParam); - } - } -} - -template -void -populateOverloadsExceptions(OverloadsInfo& I, Range& functions) { - auto functionExceptions = functions | - std::views::filter([](FunctionInfo const& function) - { - return function.javadoc && !function.javadoc->exceptions.empty(); - }) | - std::views::transform([](FunctionInfo const& function) - { - return function.javadoc->exceptions; - }) | - std::views::join; - for (doc::Throws const& functionException: functionExceptions) - { - auto sameIt = std::ranges::find_if( - I.javadoc->exceptions, - [&functionException](doc::Throws const& overloadException) - { - return overloadException.exception.string == functionException.exception.string; - }); - if (sameIt == I.javadoc->exceptions.end()) - { - I.javadoc->exceptions.push_back(functionException); - } - } -} - -template -void -populateOverloadsSees(OverloadsInfo& I, Range& functions) { - auto functionSees = functions | - std::views::filter([](FunctionInfo const& function) - { - return function.javadoc && !function.javadoc->sees.empty(); - }) | - std::views::transform([](FunctionInfo const& function) - { - return function.javadoc->sees; - }) | - std::views::join; - for (doc::See const& functionSee: functionSees) - { - auto sameIt = std::ranges::find_if( - I.javadoc->sees, - [&functionSee](doc::See const& overloadSee) - { - return overloadSee.children == functionSee.children; - }); - if (sameIt == I.javadoc->sees.end()) - { - I.javadoc->sees.push_back(functionSee); - } - } -} - -template -void -populateOverloadsPreconditions(OverloadsInfo& I, Range& functions) { - auto functionsPres = functions | - std::views::filter([](FunctionInfo const& function) - { - return function.javadoc && !function.javadoc->preconditions.empty(); - }) | - std::views::transform([](FunctionInfo const& function) - { - return function.javadoc->preconditions; - }) | - std::views::join; - for (doc::Precondition const& functionPre: functionsPres) - { - auto sameIt = std::ranges::find_if( - I.javadoc->preconditions, - [&functionPre](doc::Precondition const& overloadPre) - { - return overloadPre.children == functionPre.children; - }); - if (sameIt == I.javadoc->preconditions.end()) - { - I.javadoc->preconditions.push_back(functionPre); - } - } -} - -template -void -populateOverloadsPostconditions(OverloadsInfo& I, Range& functions) { - auto functionsPosts = functions | - std::views::filter([](FunctionInfo const& function) - { - return function.javadoc && !function.javadoc->postconditions.empty(); - }) | - std::views::transform([](FunctionInfo const& function) - { - return function.javadoc->postconditions; - }) | - std::views::join; - for (doc::Postcondition const& functionPost: functionsPosts) - { - auto sameIt = std::ranges::find_if( - I.javadoc->postconditions, - [&functionPost](doc::Postcondition const& overloadPost) - { - return overloadPost.children == functionPost.children; - }); - if (sameIt == I.javadoc->postconditions.end()) - { - I.javadoc->postconditions.push_back(functionPost); - } - } -} - -} -} // clang::mrdocs - -#endif diff --git a/src/lib/Metadata/Finalizers/JavadocFinalizer.cpp b/src/lib/Metadata/Finalizers/JavadocFinalizer.cpp deleted file mode 100644 index 7230aaaec..000000000 --- a/src/lib/Metadata/Finalizers/JavadocFinalizer.cpp +++ /dev/null @@ -1,1865 +0,0 @@ -// -// 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) 2023 Krystian Stasiowski (sdkrystian@gmail.com) -// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) -// -// Official repository: https://github.com/cppalliance/mrdocs -// - -#include "JavadocFinalizer.hpp" -#include "Javadoc/Function.hpp" -#include "Javadoc/Overloads.hpp" -#include -#include -#include -#include - -namespace clang::mrdocs { - -void -JavadocFinalizer:: -build() -{ - // This function finalizes groups of javadoc components in - // different loops. This allows us to resolve references - // that are only related to that component group without - // creating circular dependencies. - - // Finalize briefs: - // We do it first because all other steps require accessing - // the brief of other functions, these often need to be resolved - // with @copybrief or auto-brief, and we need to ensure that - // there are no circular dependencies for other metadata. - for (auto& infoPtr : corpus_.info_) - { - MRDOCS_ASSERT(infoPtr); - Info& I = *infoPtr; - MRDOCS_CHECK_OR_CONTINUE(I.Extraction != ExtractionMode::Dependency); - finalizeBrief(I); - } - - // Finalize metadata: - // A @copydetails command also implies we should copy - // other metadata from the referenced symbol. - // We do it now because we need the complete metadata - // for all objects to generate javadoc for overloads. - // For instance, overloads cannot aggregate function - // parameters as if the parameters are not resolved. - for (auto& infoPtr : corpus_.info_) - { - MRDOCS_ASSERT(infoPtr); - Info& I = *infoPtr; - MRDOCS_CHECK_OR_CONTINUE(I.Extraction != ExtractionMode::Dependency); - finalizeMetadataCopies(I); - } - - // Create javadoc for overloads - // - We do it before the references because the overloads - // themselves can be used in the references. For instance, - // `@ref foo` refers to the overload set because it doesn't - // specify the function signature. - if (corpus_.config->overloads) - { - for (auto& infoPtr : corpus_.info_) - { - MRDOCS_ASSERT(infoPtr); - Info& I = *infoPtr; - MRDOCS_CHECK_OR_CONTINUE(I.isOverloads()); - MRDOCS_CHECK_OR_CONTINUE(I.Extraction != ExtractionMode::Dependency); - if (!I.javadoc) - { - I.javadoc.emplace(); - } - populateOverloadJavadoc(infoPtr->asOverloads()); - } - } - - // Resolve references in the javadoc - for (auto& infoPtr : corpus_.info_) - { - MRDOCS_ASSERT(infoPtr); - Info& I = *infoPtr; - MRDOCS_CHECK_OR_CONTINUE(I.Extraction != ExtractionMode::Dependency); - finalizeJavadoc(I); - } - - // Populate trivial function metadata - // - We do it after the overloads because they should not - // rely on metadata inherited from automatic generated javadoc - // - We also do it after the references because some metadata - // might be resolved from references with @copydetails - if (corpus_.config->autoFunctionMetadata) - { - for (auto& infoPtr : corpus_.info_) - { - MRDOCS_ASSERT(infoPtr); - Info& I = *infoPtr; - MRDOCS_CHECK_OR_CONTINUE(I.isFunction()); - MRDOCS_CHECK_OR_CONTINUE(I.Extraction != ExtractionMode::Dependency); - populateFunctionJavadoc(I.asFunction()); - } - } - - // Remove invalid references in the Info objects - for (auto& infoPtr : corpus_.info_) - { - MRDOCS_ASSERT(infoPtr); - Info& I = *infoPtr; - visit(I, [&](auto& U) { - finalizeInfoData(U); - }); - } - - // - Emitting param warning require everything to be completely - // processed - emitWarnings(); -} - -void -JavadocFinalizer:: -finalizeBrief(Info& I) -{ - MRDOCS_CHECK_OR(!finalized_brief_.contains(&I)); - finalized_brief_.emplace(&I); - ScopeExitRestore s(current_context_, &I); - - report::trace( - "Finalizing brief for '{}'", - corpus_.Corpus::qualifiedName(I)); - - if (I.isOverloads()) - { - // Overloads are expected not to have javadoc. - // We'll create a javadoc for them if they don't have one. - if (!I.javadoc) - { - I.javadoc.emplace(); - } - // The brief of an overload is always empty - auto& OI = I.asOverloads(); - for (auto const& MemberIDs = OI.Members; - auto& memberID : MemberIDs) - { - Info* member = corpus_.find(memberID); - MRDOCS_CHECK_OR_CONTINUE(member); - finalizeBrief(*member); - } - auto functions = overloadFunctionsRange(OI, corpus_); - populateOverloadsBrief(OI, functions, corpus_); - return; - } - - MRDOCS_CHECK_OR(I.javadoc); - // Copy brief from other symbols if there's a @copydoc - copyBrief(*I.javadoc); - // Set auto brief if brief is still empty - setAutoBrief(*I.javadoc); -} - -void -JavadocFinalizer:: -copyBrief(Javadoc& javadoc) -{ - MRDOCS_CHECK_OR(javadoc.brief); - MRDOCS_CHECK_OR(!javadoc.brief->copiedFrom.empty()); - MRDOCS_CHECK_OR(javadoc.brief->children.empty()); - - for (std::string const& ref: javadoc.brief->copiedFrom) - { - // Look for source - auto resRef = - corpus_.lookup(current_context_->id, ref); - - // Check if the source exists - if (!resRef) - { - if (corpus_.config->warnings && - corpus_.config->warnBrokenRef && - !refWarned_.contains({ref, current_context_->Name})) - { - this->warn( - "{}: Failed to copy brief from '{}' (symbol not found)\n" - " {}", - corpus_.Corpus::qualifiedName(*current_context_), - ref, - resRef.error().reason()); - } - continue; - } - - // Ensure the brief source is finalized - Info const& res = *resRef; - finalizeBrief(const_cast(res)); - - // Check if the source has a brief - if (!res.javadoc || - !res.javadoc->brief.has_value()) - { - if (corpus_.config->warnings && - corpus_.config->warnBrokenRef && - !refWarned_.contains({ref, current_context_->Name})) - { - auto resPrimaryLoc = getPrimaryLocation(res); - this->warn( - "{}: Failed to copy brief from {} '{}' (no brief available).\n" - " No brief available.\n" - " {}:{}\n" - " Note: No brief available for '{}'.", - corpus_.Corpus::qualifiedName(*current_context_), - toString(res.Kind), - ref, - resPrimaryLoc->FullPath, - resPrimaryLoc->LineNumber, - corpus_.Corpus::qualifiedName(res)); - } - continue; - } - - Javadoc const& src = *res.javadoc; - javadoc.brief->children = src.brief->children; - return; - } -} - -void -JavadocFinalizer:: -setAutoBrief(Javadoc& javadoc) const -{ - MRDOCS_CHECK_OR(corpus_.config->autoBrief); - MRDOCS_CHECK_OR(!javadoc.brief); - MRDOCS_CHECK_OR(!javadoc.blocks.empty()); - - auto isInvalidBriefText = [](Polymorphic const& text) { - return !text || text->string.empty() - || text->Kind == doc::NodeKind::copy_details - || isWhitespace(text->string); - }; - - for (auto it = javadoc.blocks.begin(); it != javadoc.blocks.end();) - { - if (auto& block = *it; - block->Kind == doc::NodeKind::paragraph || - block->Kind == doc::NodeKind::details) - { - auto& para = dynamic_cast(*block); - if (std::ranges::all_of(para.children, isInvalidBriefText)) - { - ++it; - continue; - } - javadoc.brief.emplace(); - javadoc.brief->children = para.children; - it = javadoc.blocks.erase(it); - return; - } - ++it; - } -} - -namespace { -TemplateInfo const* -getTemplateInfo(Info& I) -{ - return visit(I, [](auto& I) -> TemplateInfo const* { - if constexpr (requires { I.Template; }) - { - if (I.Template) - { - return &*I.Template; - } - } - return nullptr; - }); -} -} - -void -JavadocFinalizer:: -finalizeMetadataCopies(Info& I) -{ - MRDOCS_CHECK_OR(!finalized_metadata_.contains(&I)); - finalized_metadata_.emplace(&I); - ScopeExitRestore s(current_context_, &I); - - report::trace( - "Finalizing metadata for '{}'", - corpus_.Corpus::qualifiedName(I)); - - MRDOCS_CHECK_OR(I.javadoc); - MRDOCS_CHECK_OR(!I.javadoc->blocks.empty()); - Javadoc& destJavadoc = *I.javadoc; - - SmallVector copiedRefs; - for (auto& block: destJavadoc.blocks) - { - MRDOCS_CHECK_OR_CONTINUE( - block->Kind == doc::NodeKind::paragraph - || block->Kind == doc::NodeKind::details); - auto& para = dynamic_cast(*block); - MRDOCS_CHECK_OR_CONTINUE(!para.children.empty()); - - for (auto& text: para.children) - { - MRDOCS_CHECK_OR_CONTINUE(text->Kind == doc::NodeKind::copy_details); - copiedRefs.emplace_back(dynamic_cast(*text)); - } - MRDOCS_CHECK_OR_CONTINUE(!copiedRefs.empty()); - } - - for (doc::CopyDetails const& copied: copiedRefs) - { - // Find element - auto resRef = corpus_.lookup(current_context_->id, copied.string); - if (!resRef) - { - if (corpus_.config->warnings && - corpus_.config->warnBrokenRef && - !refWarned_.contains({copied.string, current_context_->Name})) - { - this->warn( - "{}: Failed to copy metadata from '{}' (symbol not found)\n" - " {}", - corpus_.Corpus::qualifiedName(*current_context_), - copied.string, - resRef.error().reason()); - } - continue; - } - - // Function to copy the metadata from a ranges - // of source functions. This range might - // contain more than one function if the - // destination is an overload set. - // We can't copy directly from the overload set - // because its metadata is not created at this - // step yet. - auto copyInfoRangeMetadata = [&](ArrayRef srcInfoPtrs) - { - auto srcInfos = srcInfoPtrs - | std::views::transform( - [](Info const* ptr) -> Info const& { - return *ptr; - }); - - // Ensure the source metadata is finalized - for (auto& srcInfo: srcInfos) - { - auto& mutSrcInfo = const_cast(srcInfo); - finalizeMetadataCopies(mutSrcInfo); - } - - // Copy returns only if destination is empty - if (destJavadoc.returns.empty()) - { - for (auto const& srcInfo: srcInfos) - { - MRDOCS_CHECK_OR_CONTINUE(srcInfo.javadoc); - for (doc::Returns const& returnsEl: srcInfo.javadoc->returns) - { - MRDOCS_CHECK_OR_CONTINUE(!contains(destJavadoc.returns, returnsEl)); - destJavadoc.returns.push_back(returnsEl); - } - } - } - - // Copy only params that don't exist at the destination - // documentation but that do exist in the destination - // function parameters declaration. - if (I.isFunction()) - { - auto& destF = I.asFunction(); - for (Info const& srcInfo: srcInfos) - { - MRDOCS_CHECK_OR_CONTINUE(srcInfo.isFunction()); - auto const& srcFunction = srcInfo.asFunction(); - MRDOCS_CHECK_OR_CONTINUE(srcFunction.javadoc); - for (Javadoc const& srcJavadoc = *srcFunction.javadoc; - auto const& srcDocParam: srcJavadoc.params) - { - // check if param doc doesn't already exist - MRDOCS_CHECK_OR_CONTINUE( - std::ranges::none_of( - destJavadoc.params, - [&srcDocParam](doc::Param const& destDocParam) { - return srcDocParam.name == destDocParam.name; - })); - - // check if param name exists in the destination function - MRDOCS_CHECK_OR_CONTINUE( - std::ranges::any_of( - destF.Params, - [&srcDocParam](Param const& destParam) { - return srcDocParam.name == *destParam.Name; - })); - - // Push the new param ot the - destJavadoc.params.push_back(srcDocParam); - } - } - } - - // Copy only tparams that don't exist at the destination - // documentation but that do exist in the destination - // template parameters. - if (auto const destTemplateInfo = getTemplateInfo(I)) - { - for (Info const& srcInfo: srcInfos) - { - MRDOCS_CHECK_OR_CONTINUE(srcInfo.javadoc); - for (Javadoc const& srcJavadoc = *srcInfo.javadoc; - auto const& srcTParam: srcJavadoc.tparams) - { - // tparam doesn't already exist at the destination - MRDOCS_CHECK_OR_CONTINUE( - std::ranges::none_of( - destJavadoc.tparams, - [&srcTParam](doc::TParam const& destTParam) { - return srcTParam.name == destTParam.name; - })); - - // TParam name exists in the destination definition - MRDOCS_CHECK_OR_CONTINUE( - std::ranges::any_of( - destTemplateInfo->Params, - [&srcTParam]( - Polymorphic const& destTParam) { - return srcTParam.name == destTParam->Name; - })); - - // Push the new param - destJavadoc.tparams.push_back(srcTParam); - } - } - } - - // Copy exceptions only if destination exceptions are empty - // and the destination is not noexcept - bool const destIsNoExcept = - I.isFunction() ? - I.asFunction().Noexcept.Kind == NoexceptKind::False : - false; - if (destJavadoc.exceptions.empty() && - !destIsNoExcept) - { - for (Info const& srcInfo: srcInfos) - { - MRDOCS_CHECK_OR_CONTINUE(srcInfo.javadoc); - for (doc::Throws const& exceptionsEl: srcInfo.javadoc->exceptions) - { - MRDOCS_CHECK_OR_CONTINUE(!contains(destJavadoc.exceptions, exceptionsEl)); - destJavadoc.exceptions.push_back(exceptionsEl); - } - } - } - - // Copy sees only if destination sees are empty - if (destJavadoc.sees.empty()) - { - for (Info const& srcInfo: srcInfos) - { - MRDOCS_CHECK_OR_CONTINUE(srcInfo.javadoc); - for (doc::See const& seesEl: srcInfo.javadoc->sees) - { - MRDOCS_CHECK_OR_CONTINUE(!contains(destJavadoc.sees, seesEl)); - destJavadoc.sees.push_back(seesEl); - } - } - } - - // Copy preconditions only if destination preconditions is empty - if (destJavadoc.preconditions.empty()) - { - for (Info const& srcInfo: srcInfos) - { - MRDOCS_CHECK_OR_CONTINUE(srcInfo.javadoc); - for (doc::Precondition const& preconditionsEl: srcInfo.javadoc->preconditions) - { - MRDOCS_CHECK_OR_CONTINUE(!contains(destJavadoc.preconditions, preconditionsEl)); - destJavadoc.preconditions.push_back(preconditionsEl); - } - } - } - - // Copy postconditions only if destination postconditions is empty - if (destJavadoc.postconditions.empty()) - { - for (Info const& srcInfo: srcInfos) - { - MRDOCS_CHECK_OR_CONTINUE(srcInfo.javadoc); - for (doc::Postcondition const& postconditionsEl: - srcInfo.javadoc->postconditions) - { - MRDOCS_CHECK_OR_CONTINUE(!contains( - destJavadoc.postconditions, - postconditionsEl)); - destJavadoc.postconditions.push_back(postconditionsEl); - } - } - } - }; - - // Ensure the source metadata is finalized - Info const& res = *resRef; - if (!res.isOverloads()) - { - // If it's a single element, we check the element javadoc - if (!res.javadoc) - { - if (corpus_.config->warnings && - corpus_.config->warnBrokenRef && - !refWarned_.contains({copied.string, current_context_->Name})) - { - auto resPrimaryLoc = getPrimaryLocation(res); - this->warn( - "{}: Failed to copy metadata from {} '{}' (no documentation available).\n" - " No metadata available.\n" - " {}:{}\n" - " Note: No documentation available for '{}'.", - corpus_.Corpus::qualifiedName(*current_context_), - toString(res.Kind), - copied.string, - resPrimaryLoc->FullPath, - resPrimaryLoc->LineNumber, - corpus_.Corpus::qualifiedName(res)); - } - continue; - } - SmallVector srcInfos = { &res }; - copyInfoRangeMetadata(srcInfos); - } - else - { - auto& OI = res.asOverloads(); - SmallVector srcInfos; - srcInfos.reserve(OI.Members.size()); - for (auto& memberID: OI.Members) - { - Info* member = corpus_.find(memberID); - MRDOCS_CHECK_OR_CONTINUE(member); - srcInfos.push_back(member); - } - copyInfoRangeMetadata(srcInfos); - } - } -} - -void -JavadocFinalizer:: -populateFunctionJavadoc(FunctionInfo& I) const -{ - // For special functions (constructors, destructors, ...), - // we create the javadoc if it does not exist because - // we can populate all the fields from the function category. - // For other types of functions, we'll only populate - // the missing fields when the javadoc already exists. - bool const isSpecial = isSpecialFunction(I); - MRDOCS_CHECK_OR(isSpecial || I.javadoc); - bool forceEmplaced = false; - if (isSpecial && - !I.javadoc) - { - I.javadoc.emplace(); - forceEmplaced = true; - } - - // Populate a missing javadoc brief - populateFunctionBrief(I, corpus_); - - // Populate a missing javadoc returns - populateFunctionReturns(I, corpus_); - - // Populate missing javadoc params - populateFunctionParams(I, corpus_); - - // If we forcefully created the javadoc, we need to - // check if the function was able to populate all the - // fields. If not, we'll remove the javadoc. - if (forceEmplaced) - { - // Check brief and returns - if (!I.javadoc->brief) - { - I.javadoc.reset(); - return; - } - - if (!is_one_of(I.Class, { - FunctionClass::Constructor, - FunctionClass::Destructor }) && - I.javadoc->returns.empty()) - { - I.javadoc.reset(); - return; - } - - // Check params size - std::size_t const nNamedParams = std::ranges:: - count_if(I.Params, [](Param const& p) -> bool { - return p.Name.has_value(); - }); - auto const documentedParams = getJavadocParamNames(*I.javadoc); - if (nNamedParams != documentedParams.size()) - { - I.javadoc.reset(); - return; - } - - // Check param names - if (!std::ranges::all_of(I.Params, [&](Param const& param) { - if (param.Name) - { - return contains(documentedParams, *param.Name); - } - return true; - })) - { - I.javadoc.reset(); - } - } -} - -void -JavadocFinalizer:: -populateOverloadJavadoc(OverloadsInfo& I) -{ - // Create a view all Info members of I. - // The javadoc for these function should already be as - // complete as possible - auto functions = - I.Members | - std::views::transform([&](SymbolID const& id) - { - return corpus_.find(id); - }) | - std::views::filter([](Info const* infoPtr) - { - return infoPtr != nullptr && infoPtr->isFunction(); - }) | - std::views::transform([](Info const* infoPtr) -> FunctionInfo const& - { - return infoPtr->asFunction(); - }); - if (!I.javadoc) - { - I.javadoc.emplace(); - } - - // briefs: populated in a previous step - // blocks: we do not copy javadoc detail blocks because - // it's impossible to guarantee that the details for - // any of the functions make sense for all overloads. - // We can only merge metadata. - populateOverloadsReturns(I, functions); - populateOverloadsParams(I, functions); - populateOverloadsTParams(I, functions); - populateOverloadsExceptions(I, functions); - populateOverloadsSees(I, functions); - populateOverloadsPreconditions(I, functions); - populateOverloadsPostconditions(I, functions); -} - -void -JavadocFinalizer:: -finalizeJavadoc(Info& I) -{ - MRDOCS_CHECK_OR(!finalized_.contains(&I)); - finalized_.emplace(&I); - ScopeExitRestore s(current_context_, &I); - - report::trace( - "Finalizing javadoc for '{}'", - corpus_.Corpus::qualifiedName(I)); - - if (I.javadoc) - { - finalize(*I.javadoc); - } -} - -template -void -JavadocFinalizer:: -finalizeInfoData(InfoTy& I) -{ -#ifndef NDEBUG - // Check references - if (I.Parent) - { - checkExists(I.Parent); - } - if constexpr (InfoParent) - { - checkExists(allMembers(I)); - } -#endif - - if constexpr (requires { I.UsingDirectives; }) - { - finalize(I.UsingDirectives); - } - if constexpr (requires { I.Template; }) - { - finalize(I.Template); - } - if constexpr (requires { I.Bases; }) - { - finalize(I.Bases); - } - if constexpr (requires { I.Primary; }) - { - finalize(I.Primary); - } - if constexpr (requires { I.Args; }) - { - finalize(I.Args); - } - if constexpr (requires { I.ReturnType; }) - { - finalize(I.ReturnType); - } - if constexpr (requires { I.Params; }) - { - finalize(I.Params); - } - if constexpr (requires { I.Type; }) - { - finalize(I.Type); - } - if constexpr (requires { I.UnderlyingType; }) - { - finalize(I.UnderlyingType); - } - if constexpr (requires { I.FriendSymbol; }) - { - finalize(I.FriendSymbol); - } - if constexpr (requires { I.FriendType; }) - { - finalize(I.FriendType); - } - if constexpr (requires { I.AliasedSymbol; }) - { - finalize(I.AliasedSymbol); - } - if constexpr (requires { I.IntroducedName; }) - { - finalize(I.IntroducedName); - } - if constexpr (requires { I.ShadowDeclarations; }) - { - finalize(I.ShadowDeclarations); - } - if constexpr (requires { I.Deduced; }) - { - finalize(I.Deduced); - } -} - -#define INFO(T) template void JavadocFinalizer::finalizeInfoData(T## Info&); -#include - -void -JavadocFinalizer:: -finalize(doc::Reference& ref, bool const emitWarning) -{ - if (ref.id != SymbolID::invalid) - { - // Already resolved - return; - } - if (auto resRef = corpus_.lookup(current_context_->id, ref.string)) - { - // KRYSTIAN NOTE: we should provide an overload that - // returns a non-const reference. - auto& res = const_cast(resRef->get()); - ref.id = res.id; - } - else if ( - emitWarning && - corpus_.config->warnings && - corpus_.config->warnBrokenRef && - // Only warn once per reference - !refWarned_.contains({ref.string, current_context_->Name}) && - // Ignore std:: references - !ref.string.starts_with("std::") && - // Ignore other reference types that can't be replaced by `...` - ref.Kind == doc::NodeKind::reference) - { - MRDOCS_ASSERT(current_context_); - this->warn( - "{}: Failed to resolve reference to '{}'\n" - " {}", - corpus_.Corpus::qualifiedName(*current_context_), - ref.string, - resRef.error().reason()); - refWarned_.insert({ref.string, current_context_->Name}); - } -} - -void -JavadocFinalizer:: -finalize(doc::Node& node) -{ - visit(node, [&](NodeTy& N) - { - if constexpr (requires { N.children; }) - { - finalize(N.children); - } - - if constexpr(std::same_as) - { - finalize(dynamic_cast(N), true); - } - else if constexpr (std::same_as) - { - finalize(dynamic_cast(N).exception, false); - } - }); -} - -void -JavadocFinalizer:: -finalize(Javadoc& javadoc) -{ - finalize(javadoc.blocks); - finalize(javadoc.brief); - finalize(javadoc.returns); - finalize(javadoc.params); - finalize(javadoc.tparams); - finalize(javadoc.exceptions); - finalize(javadoc.sees); - finalize(javadoc.preconditions); - finalize(javadoc.postconditions); - processRelates(javadoc); - copyDetails(javadoc); - removeTempTextNodes(javadoc); - trimBlocks(javadoc); - unindentCodeBlocks(javadoc); -} - -namespace { -bool -referenceCmp( - doc::Reference const& lhs, - doc::Reference const& rhs) { - bool const lhsIsGlobal = lhs.string.starts_with("::"); - bool const rhsIsGlobal = rhs.string.starts_with("::"); - if (lhsIsGlobal != rhsIsGlobal) - { - return lhsIsGlobal < rhsIsGlobal; - } - std::size_t const lhsCount = std::ranges::count(lhs.string, ':'); - std::size_t const rhsCount = std::ranges::count(rhs.string, ':'); - if (lhsCount != rhsCount) - { - return lhsCount < rhsCount; - } - if (lhs.string != rhs.string) - { - return lhs.string < rhs.string; - } - return lhs.id < rhs.id; -} -} - -void -JavadocFinalizer:: -processRelates(Javadoc& javadoc) -{ - if (corpus_.config->autoRelates) - { - setAutoRelates(); - } - - MRDOCS_CHECK_OR(!javadoc.relates.empty()); - - Info const* currentPtr = corpus_.find(current_context_->id); - MRDOCS_ASSERT(currentPtr); - Info const current = *currentPtr; - - if (!current.isFunction()) - { - this->warn( - "{}: `@relates` only allowed for functions", - corpus_.Corpus::qualifiedName(current)); - javadoc.relates.clear(); - return; - } - - for (doc::Reference& ref: javadoc.relates) - { - finalize(ref, true); - Info* relatedPtr = corpus_.find(ref.id); - MRDOCS_CHECK_OR_CONTINUE(relatedPtr); - Info& related = *relatedPtr; - if (!related.javadoc) - { - related.javadoc.emplace(); - } - if (std::ranges::none_of( - related.javadoc->related, - [this](doc::Reference const& otherRef) { - return otherRef.id == current_context_->id; - })) - { - std::string currentName = corpus_.Corpus::qualifiedName(current, relatedPtr->Parent); - doc::Reference relatedRef(std::move(currentName)); - relatedRef.id = current_context_->id; - // Insert in order by name - auto const it = std::ranges::lower_bound( - related.javadoc->related, - relatedRef, - referenceCmp); - related.javadoc->related.insert(it, std::move(relatedRef)); - } - } - - // Erase anything in the javadoc without a valid id - std::erase_if(javadoc.relates, [](doc::Reference const& ref) { - return !ref.id; - }); -} - -void -JavadocFinalizer:: -removeTempTextNodes(Javadoc& javadoc) -{ - removeTempTextNodes(javadoc.blocks); - if (javadoc.brief) - { - removeTempTextNodes(*javadoc.brief); - } - removeTempTextNodes(javadoc.returns); - removeTempTextNodes(javadoc.params); - removeTempTextNodes(javadoc.tparams); - removeTempTextNodes(javadoc.exceptions); - removeTempTextNodes(javadoc.sees); - removeTempTextNodes(javadoc.preconditions); - removeTempTextNodes(javadoc.postconditions); -} - -void -JavadocFinalizer:: -removeTempTextNodes(std::vector>& blocks) -{ - for (auto& block: blocks) - { - removeTempTextNodes(*block); - } - // Erase all blocks of zero elements - std::erase_if(blocks, [](Polymorphic const& block) { - if (block->Kind == doc::NodeKind::unordered_list) - { - return static_cast(*block).items.empty(); - } - if (block->Kind == doc::NodeKind::heading) - { - return static_cast(*block).string.empty(); - } - return block->children.empty(); - }); -} - -void -JavadocFinalizer:: -removeTempTextNodes(doc::Block& block) -{ - std::erase_if(block.children, [](Polymorphic const& child) { - return is_one_of( - child->Kind, - { doc::NodeKind::copy_details }); - }); -} - -void -JavadocFinalizer:: -trimBlocks(Javadoc& javadoc) -{ - trimBlocks(javadoc.blocks); - if (javadoc.brief) - { - trimBlock(*javadoc.brief); - } - trimBlocks(javadoc.returns); - trimBlocks(javadoc.params); - trimBlocks(javadoc.tparams); - trimBlocks(javadoc.exceptions); - trimBlocks(javadoc.sees); - trimBlocks(javadoc.preconditions); - trimBlocks(javadoc.postconditions); -} - -void -JavadocFinalizer:: -trimBlocks(std::vector>& blocks) -{ - for (auto& block: blocks) - { - bool const isVerbatim = block->Kind == doc::NodeKind::code; - MRDOCS_CHECK_OR_CONTINUE(!isVerbatim); - trimBlock(*block); - } -} - -void -JavadocFinalizer:: -trimBlock(doc::Block& block) -{ - if (block.Kind == doc::NodeKind::unordered_list) - { - auto& ul = dynamic_cast(block); - trimBlocks(ul.items); - return; - } - - MRDOCS_CHECK_OR(!block.children.empty()); - - // Helper functions - static constexpr std::string_view whitespace_chars = " \t\n\v\f\r"; - auto endsWithSpace = [](std::string_view const str) { - return endsWithOneOf(str, whitespace_chars); - }; - auto startsWithSpace = [](std::string_view const str) { - return startsWithOneOf(str, whitespace_chars); - }; - - // The first children are ltrimmed as one - while (!block.children.empty()) - { - auto& first = block.children.front()->string; - if (startsWithSpace(first)) - { - first = ltrim(first); - } - if (first.empty()) - { - // "pop_front" - block.children.erase(block.children.begin()); - } - else - { - break; - } - } - - // The last children are rtrimmed as one - while (!block.children.empty()) - { - auto& last = block.children.back()->string; - if (endsWithSpace(last)) - { - last = rtrim(last); - } - if (last.empty()) - { - block.children.pop_back(); - } - else - { - break; - } - } - - // Like in HTML, multiple whitespaces (spaces, tabs, and newlines) - // between and within child nodes are collapsed into a single space. - if (!block.children.empty()) - { - for ( - auto it = block.children.begin() + 1; - it != block.children.end(); - ++it) - { - auto& child = *it; - auto& prev = *std::prev(it); - if (endsWithSpace(prev->string) && startsWithSpace(child->string)) - { - // The first visible space character is maintained. - // All others are removed. - prev->string = rtrim(prev->string); - prev->string.push_back(' '); - child->string = ltrim(child->string); - } - } - } - - // Like in HTML, multiple whitespaces (spaces, tabs, and newlines) - // within child nodes are collapsed into a single space. - for (auto& child: block.children) - { - auto& str = child->string; - for (std::size_t i = 0; i < str.size();) - { - if (contains(whitespace_chars, str[i])) - { - std::size_t const runStart = i; - std::size_t runEnd = i + 1; - while ( - runEnd < str.size() && - contains(whitespace_chars, str[runEnd])) - { - ++runEnd; - } - if (runEnd > runStart + 1) - { - std::size_t const runSize = runEnd - runStart; - str.erase(runStart + 1, runSize - 1); - str[runStart] = ' '; - } - i = runEnd; - } - else - { - ++i; - } - } - } -} - -void -JavadocFinalizer:: -unindentCodeBlocks(Javadoc& javadoc) -{ - unindentCodeBlocks(javadoc.blocks); - if (javadoc.brief) - { - unindentCodeBlocks(*javadoc.brief); - } - unindentCodeBlocks(javadoc.returns); - unindentCodeBlocks(javadoc.params); - unindentCodeBlocks(javadoc.tparams); - unindentCodeBlocks(javadoc.exceptions); - unindentCodeBlocks(javadoc.sees); - unindentCodeBlocks(javadoc.preconditions); - unindentCodeBlocks(javadoc.postconditions); -} - -void -JavadocFinalizer:: -unindentCodeBlocks(std::vector>& blocks) -{ - for (auto& block: blocks) - { - if (block->Kind == doc::NodeKind::code) - { - unindentCodeBlocks(*block); - } - } -} - -void -JavadocFinalizer:: -unindentCodeBlocks(doc::Block& block) -{ - MRDOCS_CHECK_OR(block.Kind == doc::NodeKind::code); - MRDOCS_CHECK_OR(!block.children.empty()); - - // Determine the left margin - std::size_t leftMargin = std::numeric_limits::max(); - for (auto& pText: block.children) - { - auto& text = dynamic_cast(*pText); - if (text.string.empty()) - { - continue; - } - std::size_t const margin = text.string.find_first_not_of(" \t"); - if (margin == std::string::npos) - { - continue; - } - leftMargin = std::min(leftMargin, margin); - } - - MRDOCS_CHECK_OR(leftMargin != std::numeric_limits::max()); - - // Remove the left margin - for (auto& pText: block.children) - { - auto& text = dynamic_cast(*pText); - if (text.string.size() < leftMargin) - { - continue; - } - text.string = text.string.substr(leftMargin); - } -} - -namespace { -void -pushAllDerivedClasses( - RecordInfo const* record, - SmallVector& relatedRecordsOrEnums, - CorpusImpl& corpus) -{ - for (auto& derivedId : record->Derived) - { - Info* derivedPtr = corpus.find(derivedId); - MRDOCS_CHECK_OR_CONTINUE(derivedPtr); - MRDOCS_CHECK_OR_CONTINUE(derivedPtr->Extraction == ExtractionMode::Regular); - auto derived = dynamic_cast(derivedPtr); - MRDOCS_CHECK_OR_CONTINUE(derived); - relatedRecordsOrEnums.push_back(derived); - // Recursively get derived classes of the derived class - pushAllDerivedClasses(derived, relatedRecordsOrEnums, corpus); - } -} -} - -void -JavadocFinalizer:: -setAutoRelates() -{ - MRDOCS_ASSERT(current_context_); - MRDOCS_CHECK_OR(current_context_->Extraction == ExtractionMode::Regular); - MRDOCS_CHECK_OR(current_context_->isFunction()); - MRDOCS_CHECK_OR(current_context_->javadoc); - auto& I = dynamic_cast(*current_context_); - MRDOCS_CHECK_OR(!I.IsRecordMethod); - auto* parentPtr = corpus_.find(I.Parent); - MRDOCS_CHECK_OR(parentPtr); - MRDOCS_CHECK_OR(parentPtr->isNamespace()); - - auto toRecordOrEnum = [&](Polymorphic const& type) -> Info* { - MRDOCS_CHECK_OR(type, nullptr); - auto& innermost = innermostType(type); - MRDOCS_CHECK_OR(innermost, nullptr); - MRDOCS_CHECK_OR(innermost->isNamed(), nullptr); - auto const& namedType = dynamic_cast(*innermost); - MRDOCS_CHECK_OR(namedType.Name, nullptr); - SymbolID const namedSymbolID = namedType.Name->id; - MRDOCS_CHECK_OR(namedSymbolID != SymbolID::invalid, nullptr); - Info* infoPtr = corpus_.find(namedSymbolID); - MRDOCS_CHECK_OR(infoPtr, nullptr); - MRDOCS_CHECK_OR( - infoPtr->isRecord() || - infoPtr->isEnum(), nullptr); - return infoPtr; - }; - - SmallVector relatedRecordsOrEnums; - - // 1) Inner type of the first parameter - [&] { - MRDOCS_CHECK_OR(!I.Params.empty()); - auto* firstParamInfo = toRecordOrEnum(I.Params.front().Type); - MRDOCS_CHECK_OR(firstParamInfo); - if (firstParamInfo->Extraction == ExtractionMode::Regular) - { - relatedRecordsOrEnums.push_back(firstParamInfo); - } - // 2) If the type is a reference or a pointer, derived classes - // of this inner type are also valid related records - MRDOCS_CHECK_OR(firstParamInfo->isRecord()); - auto const* firstParamRecord = dynamic_cast(firstParamInfo); - MRDOCS_CHECK_OR( - I.Params.front().Type->isLValueReference() || - I.Params.front().Type->isRValueReference() || - I.Params.front().Type->isPointer()); - // Get all transitively derived classes of firstParamRecord - pushAllDerivedClasses(firstParamRecord, relatedRecordsOrEnums, corpus_); - }(); - - // 3) The return type of the function - if (auto* returnTypeInfo = toRecordOrEnum(I.ReturnType)) - { - if (returnTypeInfo->Extraction == ExtractionMode::Regular) - { - relatedRecordsOrEnums.push_back(returnTypeInfo); - } - // 4) If the return type is a template specialization, - // and the template parameters are records, then - // each template parameter is also a related record - [&] { - MRDOCS_CHECK_OR(I.ReturnType); - MRDOCS_CHECK_OR(I.ReturnType->isNamed()); - auto& NTI = static_cast(*I.ReturnType); - MRDOCS_CHECK_OR(NTI.Name); - MRDOCS_CHECK_OR(NTI.Name->isSpecialization()); - auto const& NTIS = static_cast(*NTI.Name); - MRDOCS_CHECK_OR(!NTIS.TemplateArgs.empty()); - Polymorphic const& firstArg = NTIS.TemplateArgs.front(); - MRDOCS_CHECK_OR(firstArg->isType()); - auto const& typeArg = static_cast(*firstArg); - if (auto* argInfo = toRecordOrEnum(typeArg.Type)) - { - if (argInfo->Extraction == ExtractionMode::Regular) - { - relatedRecordsOrEnums.push_back(argInfo); - } - } - }(); - } - - // Remove duplicates from relatedRecordsOrEnums - std::ranges::sort(relatedRecordsOrEnums); - relatedRecordsOrEnums.erase( - std::ranges::unique(relatedRecordsOrEnums).begin(), - relatedRecordsOrEnums.end()); - - // Insert the records with valid ids into the javadoc relates section - std::size_t const prevRelatesSize = I.javadoc->relates.size(); - for (Info const* relatedRecordOrEnumPtr : relatedRecordsOrEnums) - { - MRDOCS_CHECK_OR_CONTINUE(relatedRecordOrEnumPtr); - MRDOCS_ASSERT(I.javadoc); - Info const& recordOrEnum = *relatedRecordOrEnumPtr; - MRDOCS_CHECK_OR_CONTINUE(recordOrEnum.Extraction == ExtractionMode::Regular); - doc::Reference ref(recordOrEnum.Name); - ref.id = recordOrEnum.id; - - // Check if already listed as friend - if (auto* record = dynamic_cast(relatedRecordOrEnumPtr)) - { - using std::views::transform; - if (contains(transform(record->Friends, &FriendInfo::id), I.id)) - { - // Already listed as a public friend - continue; - } - } - - // Ensure no duplicates - if (std::ranges::none_of( - I.javadoc->relates, - [&ref](doc::Reference const& otherRef) { - return otherRef.string == ref.string || otherRef.id == ref.id; - })) - { - // Insert in order by name - auto const it = std::ranges::lower_bound( - I.javadoc->relates.begin() + prevRelatesSize, - I.javadoc->relates.end(), - ref, - referenceCmp); - I.javadoc->relates.insert(it, std::move(ref)); - } - } -} - -void -JavadocFinalizer:: -copyDetails(Javadoc& javadoc) -{ - for (auto blockIt = javadoc.blocks.begin(); blockIt != javadoc.blocks.end();) - { - // Get paragraph - auto& block = *blockIt; - if (block->Kind != doc::NodeKind::paragraph && - block->Kind != doc::NodeKind::details) - { - ++blockIt; - continue; - } - auto& para = dynamic_cast(*block); - if (para.children.empty()) - { - ++blockIt; - continue; - } - - // Find copydetails command - std::optional copied; - for (auto textIt = para.children.begin(); textIt != para.children.end();) - { - // Find copydoc command - auto& text = *textIt; - if (text->Kind != doc::NodeKind::copy_details) - { - ++textIt; - continue; - } - // Copy reference - copied = dynamic_cast(*text); - - // Remove copied node from the text - /* it2 = */ para.children.erase(textIt); - break; - } - - // Remove leading children from the paragraph that are - // either empty or only white spaces. We also ltrim - // the first child with content. - while (!para.children.empty()) - { - if (para.children.front()->string.find_first_not_of(" \t\n\v\f\r") == std::string::npos) - { - para.children.erase(para.children.begin()); - } - else - { - para.children.front()->string = ltrim(para.children.front()->string); - break; - } - } - - // Remove trailing children from the paragraph that are - // either empty or only white spaces. We also rtrim - // the last child with content. - while (!para.children.empty()) - { - if (para.children.back()->string.find_first_not_of(" \t\n\v\f\r") == std::string::npos) - { - para.children.pop_back(); - } - else - { - para.children.back()->string = rtrim(para.children.back()->string); - break; - } - } - - // Remove empty completely empty children from the paragraph - std::erase_if(para.children, [](Polymorphic const& child) { - return child->string.empty(); - }); - - // Merge consecutive text nodes that have exactly the same terminal kind - for (auto textIt = para.children.begin(); textIt != para.children.end();) - { - auto& text = *textIt; - if (textIt != para.children.begin()) - { - if (auto& prev = *std::prev(textIt); - prev->Kind == text->Kind) - { - prev->string += text->string; - textIt = para.children.erase(textIt); - continue; - } - } - ++textIt; - } - - // Remove the entire paragraph block from the javadoc if it is empty - if (para.empty()) - { - blockIt = javadoc.blocks.erase(blockIt); - MRDOCS_CHECK_OR_CONTINUE(copied); - } - - // Nothing to copy: continue to the next block - if (!copied) - { - ++blockIt; - continue; - } - - // Find the node to copy from - auto resRef = corpus_.lookup(current_context_->id, copied->string); - if (!resRef) - { - if (corpus_.config->warnings && - corpus_.config->warnBrokenRef && - !refWarned_.contains({copied->string, current_context_->Name})) - { - this->warn( - "{}: Failed to copy documentation from '{}' (symbol not found)\n" - " {}", - corpus_.Corpus::qualifiedName(*current_context_), - copied->string, - resRef.error().reason()); - } - continue; - } - - // Ensure the source node is finalized - Info const& res = *resRef; - finalizeJavadoc(const_cast(res)); - - // Check if there's any documentation details to copy - if (!res.javadoc) - { - if (corpus_.config->warnings && - corpus_.config->warnBrokenRef && - !refWarned_.contains({copied->string, current_context_->Name})) - { - auto resPrimaryLoc = getPrimaryLocation(res); - this->warn( - "{}: Failed to copy documentation from {} '{}' (no documentation available).\n" - " No documentation available.\n" - " {}:{}\n" - " Note: No documentation available for '{}'.", - corpus_.Corpus::qualifiedName(*current_context_), - toString(res.Kind), - copied->string, - resPrimaryLoc->FullPath, - resPrimaryLoc->LineNumber, - corpus_.Corpus::qualifiedName(res)); - } - continue; - } - - // Copy detail blocks from source to destination to - // the same position in the destination - Javadoc const& src = *res.javadoc; - if (!src.blocks.empty()) - { - blockIt = javadoc.blocks.insert(blockIt, src.blocks.begin(), src.blocks.end()); - blockIt += src.blocks.size(); - } - } -} - -void -JavadocFinalizer:: -finalize(SymbolID& id) -{ - if (id && !corpus_.info_.contains(id)) - { - id = SymbolID::invalid; - } -} - -void -JavadocFinalizer:: -finalize(std::vector& ids) -{ - std::erase_if(ids, [this](SymbolID const& id) - { - return !id || ! corpus_.info_.contains(id); - }); -} - -void -JavadocFinalizer:: -finalize(TArg& arg) -{ - visit(arg, [this](Ty& A) - { - if constexpr (Ty::isType()) - { - finalize(A.Type); - } - if constexpr (Ty::isTemplate()) - { - finalize(A.Template); - } - }); -} - -void -JavadocFinalizer:: -finalize(TParam& param) -{ - finalize(param.Default); - - visit(param, [this](Ty& P) - { - if constexpr (Ty::isType()) - { - finalize(P.Constraint); - } - - if constexpr (Ty::isNonType()) - { - finalize(P.Type); - } - - if constexpr (Ty::isTemplate()) - { - finalize(P.Params); - } - }); -} - -void -JavadocFinalizer:: -finalize(Param& param) -{ - finalize(param.Type); -} - -void -JavadocFinalizer:: -finalize(BaseInfo& info) -{ - finalize(info.Type); -} - -void -JavadocFinalizer:: -finalize(TemplateInfo& info) -{ - finalize(info.Args); - finalize(info.Params); - finalize(info.Primary); -} - -void -JavadocFinalizer:: -finalize(TypeInfo& type) -{ - finalize(innerTypePtr(type)); - - visit(type, [this](Ty& T) - { - if constexpr (requires { T.ParentType; }) - { - finalize(T.ParentType); - } - - if constexpr (Ty::isNamed()) - { - finalize(T.Name); - } - - if constexpr (Ty::isAuto()) - { - finalize(T.Constraint); - } - }); -} - -void -JavadocFinalizer:: -finalize(NameInfo& name) -{ - visit(name, [this](Ty& T) - { - finalize(T.Prefix); - if constexpr(requires { T.TemplateArgs; }) - { - finalize(T.TemplateArgs); - } - finalize(T.id); - }); -} - -void -JavadocFinalizer:: -checkExists(SymbolID const& id) const -{ - MRDOCS_ASSERT(corpus_.info_.contains(id)); -} - -void -JavadocFinalizer:: -emitWarnings() -{ - MRDOCS_CHECK_OR(corpus_.config->warnings); - warnUndocumented(); - warnDocErrors(); - warnNoParamDocs(); - warnUndocEnumValues(); - warnUnnamedParams(); - - // Print to the console - auto const level = !corpus_.config->warnAsError ? report::Level::warn : report::Level::error; - for (auto const& [loc, msgs] : warnings_) - { - std::string locWarning = - std::format("{}:{}\n", loc.FullPath, loc.LineNumber); - int i = 1; - for (auto const &msg : msgs) { - locWarning += std::format(" {}) {}\n", i++, msg); - } - report::log(level, locWarning); - } -} - -void -JavadocFinalizer:: -warnUndocumented() -{ - MRDOCS_CHECK_OR(corpus_.config->warnIfUndocumented); - for (auto& undocI : corpus_.undocumented_) - { - if (Info const* I = corpus_.find(undocI.id)) - { - MRDOCS_CHECK_OR( - !I->javadoc || I->Extraction == ExtractionMode::Regular); - } - bool const prefer_definition = undocI.kind == InfoKind::Record - || undocI.kind == InfoKind::Enum; - this->warn( - *getPrimaryLocation(undocI, prefer_definition), - "{}: Symbol is undocumented", undocI.name); - } - corpus_.undocumented_.clear(); -} - -void -JavadocFinalizer:: -warnDocErrors() -{ - MRDOCS_CHECK_OR(corpus_.config->warnIfDocError); - for (auto const& I : corpus_.info_) - { - MRDOCS_CHECK_OR_CONTINUE(I->Extraction == ExtractionMode::Regular); - MRDOCS_CHECK_OR_CONTINUE(I->isFunction()); - warnParamErrors(dynamic_cast(*I)); - } -} - -void -JavadocFinalizer:: -warnParamErrors(FunctionInfo const& I) -{ - MRDOCS_CHECK_OR(I.javadoc); - - // Check for duplicate javadoc parameters - auto javadocParamNames = getJavadocParamNames(*I.javadoc); - std::ranges::sort(javadocParamNames); - auto [firstDup, lastUnique] = std::ranges::unique(javadocParamNames); - auto duplicateParamNames = std::ranges::subrange(firstDup, lastUnique); - auto [firstDupDup, _] = std::ranges::unique(duplicateParamNames); - for (auto const uniqueDuplicateParamNames = std::ranges::subrange(firstDup, firstDupDup); - std::string_view duplicateParamName: uniqueDuplicateParamNames) - { - this->warn( - *getPrimaryLocation(I), - "{}: Duplicate parameter documentation for '{}'", - corpus_.Corpus::qualifiedName(I), - duplicateParamName); - } - javadocParamNames.erase(lastUnique, javadocParamNames.end()); - - // Check for documented parameters that don't exist in the function - auto paramNames = - std::views::transform(I.Params, &Param::Name) | - std::views::filter([](Optional const& name) { return static_cast(name); }) | - std::views::transform([](Optional const& name) -> std::string_view { return *name; }); - for (std::string_view javadocParamName: javadocParamNames) - { - if (std::ranges::find(paramNames, javadocParamName) == paramNames.end()) - { - this->warn( - *getPrimaryLocation(I), - "{}: Documented parameter '{}' does not exist", - corpus_.Corpus::qualifiedName(I), - javadocParamName); - } - } - -} - -void -JavadocFinalizer:: -warnNoParamDocs() -{ - MRDOCS_CHECK_OR(corpus_.config->warnNoParamdoc); - for (auto const& I : corpus_.info_) - { - MRDOCS_CHECK_OR_CONTINUE(I->Extraction == ExtractionMode::Regular); - MRDOCS_CHECK_OR_CONTINUE(I->isFunction()); - MRDOCS_CHECK_OR_CONTINUE(I->javadoc); - warnNoParamDocs(dynamic_cast(*I)); - } -} - -void -JavadocFinalizer:: -warnNoParamDocs(FunctionInfo const& I) -{ - MRDOCS_CHECK_OR(!I.IsDeleted); - // Check for function parameters that are not documented in javadoc - auto javadocParamNames = getJavadocParamNames(*I.javadoc); - auto paramNames = - std::views::transform(I.Params, &Param::Name) | - std::views::transform([](Optional const& name) -> std::string_view { return *name; }) | - std::views::filter([](std::string_view const& name) { return !name.empty(); }); - for (auto const& paramName: paramNames) - { - if (std::ranges::find(javadocParamNames, paramName) == javadocParamNames.end()) - { - this->warn( - *getPrimaryLocation(I), - "{}: Missing documentation for parameter '{}'", - corpus_.Corpus::qualifiedName(I), - paramName); - } - } - - // Check for undocumented return type - if (I.javadoc->returns.empty() && I.ReturnType) - { - auto isVoid = [](TypeInfo const& returnType) -> bool - { - if (returnType.isNamed()) - { - auto const& namedReturnType = dynamic_cast(returnType); - return namedReturnType.Name->Name == "void"; - } - return false; - }; - if (!isVoid(*I.ReturnType)) - { - this->warn( - *getPrimaryLocation(I), - "{}: Missing documentation for return value", - corpus_.Corpus::qualifiedName(I)); - } - } -} - -void -JavadocFinalizer:: -warnUndocEnumValues() -{ - MRDOCS_CHECK_OR(corpus_.config->warnIfUndocEnumVal); - for (auto const& I : corpus_.info_) - { - MRDOCS_CHECK_OR_CONTINUE(I->isEnumConstant()); - MRDOCS_CHECK_OR_CONTINUE(I->Extraction == ExtractionMode::Regular); - MRDOCS_CHECK_OR_CONTINUE(!I->javadoc); - this->warn( - *getPrimaryLocation(*I), - "{}: Missing documentation for enum value", - corpus_.Corpus::qualifiedName(*I)); - } -} - -void -JavadocFinalizer:: -warnUnnamedParams() -{ - MRDOCS_CHECK_OR(corpus_.config->warnUnnamedParam); - for (auto const& I : corpus_.info_) - { - MRDOCS_CHECK_OR_CONTINUE(I->isFunction()); - MRDOCS_CHECK_OR_CONTINUE(I->Extraction == ExtractionMode::Regular); - MRDOCS_CHECK_OR_CONTINUE(I->javadoc); - warnUnnamedParams(dynamic_cast(*I)); - } -} - -void -JavadocFinalizer:: -warnUnnamedParams(FunctionInfo const& I) -{ - auto orderSuffix = [](std::size_t const i) -> std::string - { - if (i == 0) - { - return "st"; - } - if (i == 1) - { - return "nd"; - } - if (i == 2) - { - return "rd"; - } - return "th"; - }; - - for (std::size_t i = 0; i < I.Params.size(); ++i) - { - if (!I.Params[i].Name) - { - this->warn( - *getPrimaryLocation(I), - "{}: {}{} parameter is unnamed", - corpus_.Corpus::qualifiedName(I), - i + 1, - orderSuffix(i)); - } - } -} - -} // clang::mrdocs diff --git a/src/lib/Metadata/Finalizers/JavadocFinalizer.hpp b/src/lib/Metadata/Finalizers/JavadocFinalizer.hpp deleted file mode 100644 index 6b3743564..000000000 --- a/src/lib/Metadata/Finalizers/JavadocFinalizer.hpp +++ /dev/null @@ -1,434 +0,0 @@ -// -// 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) 2023 Krystian Stasiowski (sdkrystian@gmail.com) -// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) -// -// Official repository: https://github.com/cppalliance/mrdocs -// - -#ifndef MRDOCS_LIB_METADATA_FINALIZER_JAVADOCFINALIZER_HPP -#define MRDOCS_LIB_METADATA_FINALIZER_JAVADOCFINALIZER_HPP - -#include "lib/CorpusImpl.hpp" -#include "lib/Metadata/InfoSet.hpp" -#include -#include -#include -#include -#include - -namespace clang::mrdocs { - -/** Finalizes a set of Info. - - This removes any references to SymbolIDs - which do not exist. - - References which should always be valid - are not checked. -*/ -class JavadocFinalizer -{ - CorpusImpl& corpus_; - Info* current_context_ = nullptr; - - /* Broken references for which we have already emitted - a warning. - */ - std::set> refWarned_; - - /* Info objects that have been finalized - - This is used to avoid allow recursion when - finalizing references. - */ - std::set finalized_; - - /* Info objects whose brief have been finalized - */ - std::set finalized_brief_; - - /* Info objects whose metadata have been finalized - */ - std::set finalized_metadata_; - - // A comparison function that sorts locations by: - // 1) ascending full path - // 2) descending line number - // This is the most convenient order to fix - // warnings in the source code because fixing a problem - // at a certain line will invalidate the line numbers - // of all subsequent warnings. - struct WarningLocationCompare - { - bool - operator()(Location const& lhs, Location const& rhs) const - { - if (lhs.FullPath != rhs.FullPath) - { - return lhs.FullPath < rhs.FullPath; - } - return lhs.LineNumber > rhs.LineNumber; - } - }; - - /* Warnings that should be emitted after finalization - - The warnings are initially stored in this container - where the messages are sorted by location. - - This makes it easier for the user to go through - the warnings in the order they appear in the source - code and fix them. - */ - std::map, WarningLocationCompare> warnings_; - -public: - JavadocFinalizer(CorpusImpl& corpus) - : corpus_(corpus) - { - } - - /** Finalize the javadoc for all symbols - - The procedure is composed of steps that resolve - groups of javadoc components for each symbol. - - Groups of components processed later might depend - on components processed earlier. For example, a - function's javadoc might depend on the brief of - the return type and the parameters. Or, the - javadoc of an overload depends on the complete - documentation of the functions it overloads. - */ - void - build(); - -private: - /** Finalize the brief of a symbol - - The brief is the first paragraph of the documentation - and is used in the index and in the documentation - summary. - */ - void - finalizeBrief(Info& I); - - /** Copy the brief from another symbol - - This function copies the brief from another symbol - to the current context. The brief is copied only if - the brief of the current context is empty and - it contains a reference to another symbol created - with \@copybrief or \@copydoc. - */ - void - copyBrief(Javadoc& javadoc); - - /** Set brief content automatically - - If the brief is empty, this function sets the brief - to the first paragraph of the documentation. - */ - void - setAutoBrief(Javadoc& javadoc) const; - - /** Finalize the metadata copies - - Copy the metadata from other symbols to the current - context whenever the current context contains a - reference to another symbol created with \@copydoc. - */ - void - finalizeMetadataCopies(Info& I); - - /** Populate function javadoc from with missing fields - - This function populates the function javadoc with - missing fields of special functions. - */ - void - populateFunctionJavadoc(FunctionInfo&) const; - - /** Populate the metadata of overloads - - This function populates the metadata of overloads - with the metadata of the functions it overloads. - */ - void - populateOverloadJavadoc(OverloadsInfo&); - - /** Resolve references in the javadoc - - This function resolves references in the javadoc - of a symbol. The references are resolved by looking - up the symbol in the corpus and setting the id of - the reference. - */ - void - finalizeJavadoc(Info& I); - - /** Recursively finalize javadoc members - - This function also processes related symbols, - merges consecutive blocks, trims blocks, and - unindents code blocks. - */ - void - finalize(Javadoc& javadoc); - - /** Resolve \@relates references - - This function finds the ID of "relates" symbols - and populates the "related" symbol with the inverse. - */ - void - processRelates(Javadoc& javadoc); - - /** Remove all temporary text nodes from a block - - The temporary nodes (copied, related, etc...) should - have been processed by the previous steps and should - not be present in the final output. - */ - void - removeTempTextNodes(Javadoc& javadoc); - - /// A range of values derived from blocks - template - requires std::derived_from, doc::Block> - void - removeTempTextNodes(R&& blocks) - { - for (auto& block: blocks) - { - removeTempTextNodes(block); - } - } - - /// Remove temporary text nodes from a block - void - removeTempTextNodes(std::vector>& blocks); - - /// Remove temporary text nodes from a block - void - removeTempTextNodes(doc::Block& block); - - /** Trim all block childen in the javadoc - - The first child is rtrimmed and the last child is ltrimmed. - - Like in HTML, multiple whitespaces (spaces, tabs, and newlines) - between and within child nodes are collapsed into a single space. - */ - void - trimBlocks(Javadoc& javadoc); - - /// A range of values derived from blocks - template - requires std::derived_from, doc::Block> - void - trimBlocks(R&& blocks) - { - for (auto& block: blocks) - { - trimBlock(block); - } - } - - /// Trim a block - void - trimBlocks(std::vector>& blocks); - - /// Trim a block - void - trimBlock(doc::Block& block); - - /// Unindent all code blocks in the javadoc - void - unindentCodeBlocks(Javadoc& javadoc); - - /// Unindent code blocks in a range - template - requires std::derived_from, doc::Block> - void - unindentCodeBlocks(R&& blocks) - { - for (auto& block: blocks) - { - unindentCodeBlocks(block); - } - } - - /// Unindent code blocks in a vector - void - unindentCodeBlocks(std::vector>& blocks); - - /// Unindent a code block - void - unindentCodeBlocks(doc::Block& block); - - /** Check and finalize data unrelated to javadoc - - This checks if all symbol IDs are valid and - removes invalid IDs from the Info data. - */ - template - void - finalizeInfoData(InfoTy& I); - - /** Check the documentation for problems and creates warnings - */ - void - emitWarnings(); - - // Look for symbol and set the id of a reference - void - finalize(doc::Reference& ref, bool emitWarning = true); - - // Recursively finalize references in a javadoc node - void - finalize(doc::Node& node); - - // Automatically find related symbols - void - setAutoRelates(); - - // Copy brief and details to the current context - void - copyDetails(Javadoc& javadoc); - - // Set id to invalid if it does not exist - void - finalize(SymbolID& id); - - // Remove invalid ids from a vector - void - finalize(std::vector& ids); - - // Remove invalid ids from TArg members - void - finalize(TArg& arg); - - // Remove invalid ids from TParam members - void - finalize(TParam& param); - - // Remove invalid ids from Javadoc members - void - finalize(Param& param); - - /// Remove invalid ids from BaseInfo members - void - finalize(BaseInfo& info); - - /// Remove invalid ids from TemplateInfo members - void - finalize(TemplateInfo& info); - - /// Remove invalid ids from TypeInfo members - void - finalize(TypeInfo& type); - - /// Remove invalid ids from NameInfo members - void - finalize(NameInfo& name); - - /// Finalize optional and pointer-like members - template - void - finalize(T&& val) requires - requires { this->finalize(*val); } - { - if (val) - { - finalize(*val); - } - } - - /// Finalize a range of elements - template - requires std::ranges::input_range - void - finalize(Range&& range) - { - for (auto&& elem: range) - { - finalize(elem); - } - } - - // Check if SymbolID exists in info - void - checkExists(SymbolID const& id) const; - - // Check if SymbolIDs exist in info - template T> - void - checkExists(T&& ids) const - { - MRDOCS_ASSERT(std::ranges::all_of(ids, - [this](SymbolID const& id) - { - return corpus_.find(id) != nullptr; - })); - } - - - - template - void - warn( - Location const& loc, - report::Located const format, - Args&&... args) - { - MRDOCS_CHECK_OR(corpus_.config->warnings); - std::string const str = - std::vformat(format.value, std::make_format_args(args...)); - warnings_[loc].push_back(str); - } - - template - void - warn( - report::Located const format, - Args&&... args) - { - MRDOCS_CHECK_OR(corpus_.config->warnings); - MRDOCS_ASSERT(current_context_); - auto loc = getPrimaryLocation(*current_context_); - warn(*loc, format, std::forward(args)...); - } - - void - warnUndocumented(); - - void - warnDocErrors(); - - void - warnParamErrors(FunctionInfo const& I); - - void - warnNoParamDocs(); - - void - warnNoParamDocs(FunctionInfo const& I); - - void - warnUndocEnumValues(); - - void - warnUnnamedParams(); - - void - warnUnnamedParams(FunctionInfo const& I); -}; - -} // clang::mrdocs - -#endif diff --git a/src/lib/Metadata/Finalizers/NamespacesFinalizer.cpp b/src/lib/Metadata/Finalizers/NamespacesFinalizer.cpp index 424f4cebf..64c2ab47c 100644 --- a/src/lib/Metadata/Finalizers/NamespacesFinalizer.cpp +++ b/src/lib/Metadata/Finalizers/NamespacesFinalizer.cpp @@ -11,20 +11,20 @@ #include "NamespacesFinalizer.hpp" #include -namespace clang::mrdocs { +namespace mrdocs { NamespacesFinalizer::FinalizerResult NamespacesFinalizer:: -operator()(NamespaceInfo& I) +operator()(NamespaceSymbol& I) { report::trace( "Finalizing namespace '{}'", corpus_.Corpus::qualifiedName(I)); // 1) Finalize sub-namespaces - SmallVector removedNamespaces; + llvm::SmallVector removedNamespaces; for (auto subNamespaces = corpus_.find(I.Members.Namespaces); - Info& info: subNamespaces) + Symbol& info: subNamespaces) { MRDOCS_ASSERT(info.isNamespace()); SymbolID memberID = info.id; @@ -46,7 +46,7 @@ operator()(NamespaceInfo& I) MRDOCS_CHECK_OR(I.id != SymbolID::global, FinalizerResult::None); // No more steps for documented namespaces - MRDOCS_CHECK_OR(!I.javadoc, FinalizerResult::None); + MRDOCS_CHECK_OR(!I.doc, FinalizerResult::None); // 3) Remove empty undocumented namespaces auto memberIds = allMembers(I); @@ -70,7 +70,7 @@ operator()(NamespaceInfo& I) bool allDependencies = true; bool allImplementationDefined = true; bool anySeeBelow = false; - for (Info const& member : members) + for (Symbol const& member : members) { if (member.Extraction == ExtractionMode::Regular) { @@ -107,4 +107,4 @@ operator()(NamespaceInfo& I) return FinalizerResult::Changed; } -} // clang::mrdocs +} // mrdocs diff --git a/src/lib/Metadata/Finalizers/NamespacesFinalizer.hpp b/src/lib/Metadata/Finalizers/NamespacesFinalizer.hpp index 421e3aa46..c37053a56 100644 --- a/src/lib/Metadata/Finalizers/NamespacesFinalizer.hpp +++ b/src/lib/Metadata/Finalizers/NamespacesFinalizer.hpp @@ -8,13 +8,13 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_LIB_METADATA_FINALIZER_NAMESPACESFINALIZER_HPP -#define MRDOCS_LIB_METADATA_FINALIZER_NAMESPACESFINALIZER_HPP +#ifndef MRDOCS_LIB_METADATA_FINALIZERS_NAMESPACESFINALIZER_HPP +#define MRDOCS_LIB_METADATA_FINALIZERS_NAMESPACESFINALIZER_HPP -#include "lib/Metadata/InfoSet.hpp" -#include "lib/CorpusImpl.hpp" +#include +#include -namespace clang::mrdocs { +namespace mrdocs { /** Finalizes the namespaces in corpus. @@ -36,7 +36,7 @@ class NamespacesFinalizer void build() { - Info* info = corpus_.find(SymbolID::global); + Symbol* info = corpus_.find(SymbolID::global); MRDOCS_CHECK_OR(info); MRDOCS_ASSERT(info->isNamespace()); operator()(info->asNamespace()); @@ -49,9 +49,9 @@ class NamespacesFinalizer }; FinalizerResult - operator()(NamespaceInfo& I); + operator()(NamespaceSymbol& I); }; -} // clang::mrdocs +} // mrdocs -#endif +#endif // MRDOCS_LIB_METADATA_FINALIZERS_NAMESPACESFINALIZER_HPP diff --git a/src/lib/Metadata/Finalizers/OverloadsFinalizer.cpp b/src/lib/Metadata/Finalizers/OverloadsFinalizer.cpp index eca1d1096..5ee684be3 100644 --- a/src/lib/Metadata/Finalizers/OverloadsFinalizer.cpp +++ b/src/lib/Metadata/Finalizers/OverloadsFinalizer.cpp @@ -11,22 +11,23 @@ #include "OverloadsFinalizer.hpp" #include -namespace clang::mrdocs { +namespace mrdocs { namespace { SymbolID findBaseClassPermutation( SymbolID const& contextId, CorpusImpl& corpus, - ArrayRef sameNameFunctionIds) + llvm::ArrayRef sameNameFunctionIds) { - // Find the RecordInfo - Info* info = corpus.find(contextId); + // Find the RecordSymbol + Symbol* info = corpus.find(contextId); MRDOCS_CHECK_OR(info, SymbolID::invalid); MRDOCS_CHECK_OR(info->isRecord(), SymbolID::invalid); for (auto const& base: info->asRecordPtr()->Bases) { // Find the i-th base class + MRDOCS_CHECK_OR(base.Type, SymbolID::invalid); auto const baseInfo = corpus.find(base.Type->namedSymbol()); MRDOCS_CHECK_OR_CONTINUE(baseInfo); auto const baseRecord = baseInfo->asRecordPtr(); @@ -51,7 +52,7 @@ findBaseClassPermutation( // name functions for (SymbolID const& baseID: *trancheFunctionsPtr) { - Info* baseFuncMember = corpus.find(baseID); + Symbol* baseFuncMember = corpus.find(baseID); MRDOCS_CHECK_OR_CONTINUE(baseFuncMember); MRDOCS_CHECK_OR_CONTINUE(baseFuncMember->isOverloads()); auto* overloads = baseFuncMember->asOverloadsPtr(); @@ -73,23 +74,23 @@ SymbolID findIntroducedNamespacePermutation( SymbolID const& contextId, CorpusImpl& corpus, - ArrayRef sameNameFunctionIds) + llvm::ArrayRef sameNameFunctionIds) { - // Find the UsingInfo - Info* info = corpus.find(contextId); + // Find the UsingSymbol + Symbol* info = corpus.find(contextId); MRDOCS_CHECK_OR(info, SymbolID::invalid); MRDOCS_CHECK_OR(info->isUsing(), SymbolID::invalid); - // Find the FunctionInfo for the first shadow declaration + // Find the FunctionSymbol for the first shadow declaration MRDOCS_CHECK_OR(!sameNameFunctionIds.empty(), SymbolID::invalid); - Info* firstShadowInfo = corpus.find(sameNameFunctionIds.front()); + Symbol* firstShadowInfo = corpus.find(sameNameFunctionIds.front()); MRDOCS_CHECK_OR(firstShadowInfo, SymbolID::invalid); MRDOCS_CHECK_OR(firstShadowInfo->isFunction(), SymbolID::invalid); auto* firstShadowFunction = firstShadowInfo->asFunctionPtr(); // Find the introduced namespace of the first shadow declaration MRDOCS_CHECK_OR(firstShadowFunction->Parent, SymbolID::invalid); - Info* parentInfo = corpus.find(firstShadowFunction->Parent); + Symbol* parentInfo = corpus.find(firstShadowFunction->Parent); MRDOCS_CHECK_OR(parentInfo, SymbolID::invalid); MRDOCS_CHECK_OR(parentInfo->isNamespace(), SymbolID::invalid); auto* parentNamespace = parentInfo->asNamespacePtr(); @@ -97,7 +98,7 @@ findIntroducedNamespacePermutation( // Find an overload set that's a permutation of the same name functions for (SymbolID const& baseID: parentNamespace->Members.Functions) { - Info* baseFuncMember = corpus.find(baseID); + Symbol* baseFuncMember = corpus.find(baseID); MRDOCS_CHECK_OR_CONTINUE(baseFuncMember); MRDOCS_CHECK_OR_CONTINUE(baseFuncMember->isOverloads()); auto* overloads = baseFuncMember->asOverloadsPtr(); @@ -117,27 +118,27 @@ void OverloadsFinalizer:: foldOverloads(SymbolID const& contextId, std::vector& functionIds, bool isStatic) { - Info* contextInfo = corpus_.find(contextId); + Symbol* contextInfo = corpus_.find(contextId); MRDOCS_CHECK_OR(contextInfo); for (auto functionIdIt = functionIds.begin(); functionIdIt != functionIds.end(); ++functionIdIt) { - // Get the FunctionInfo for the current id + // Get the FunctionSymbol for the current id auto infoPtr = corpus_.find(*functionIdIt); MRDOCS_CHECK_OR_CONTINUE(infoPtr); auto* function = infoPtr->asFunctionPtr(); MRDOCS_CHECK_OR_CONTINUE(function); - // Check if the FunctionInfo is unique + // Check if the FunctionSymbol is unique std::ranges::subrange otherFunctionIds( std::next(functionIdIt), functionIds.end()); auto isSameNameFunction = [&](SymbolID const& otherID) { auto const otherFunctionPtr = corpus_.find(otherID); MRDOCS_CHECK_OR(otherFunctionPtr, false); - Info const& otherInfo = *otherFunctionPtr; + Symbol const& otherInfo = *otherFunctionPtr; return function->Name == otherInfo.Name; }; auto sameNameIt = std::ranges:: @@ -145,11 +146,11 @@ foldOverloads(SymbolID const& contextId, std::vector& functionIds, boo bool const isUniqueFunction = sameNameIt == otherFunctionIds.end(); MRDOCS_CHECK_OR_CONTINUE(!isUniqueFunction); - // Create a list of FunctionInfo overloads + // Create a list of FunctionSymbol overloads auto sameNameFunctionIdsView = std::ranges::subrange(functionIdIt, functionIds.end()) | std::views::filter(isSameNameFunction); - SmallVector sameNameFunctionIds( + llvm::SmallVector sameNameFunctionIds( sameNameFunctionIdsView.begin(), sameNameFunctionIdsView.end()); @@ -208,16 +209,16 @@ foldOverloads(SymbolID const& contextId, std::vector& functionIds, boo } } - // FunctionInfo is not unique and there's no equivalent + // FunctionSymbol is not unique and there's no equivalent // overload set in base classes, so we merge it with the - // other FunctionInfos into a new OverloadsInfo - OverloadsInfo O(contextId, function->Name, function->Access, isStatic); + // other FunctionSymbols into a new OverloadsSymbol + OverloadsSymbol O(contextId, function->Name, function->Access, isStatic); addMember(O, *function); *functionIdIt = O.id; auto const itOffset = functionIdIt - functionIds.begin(); for (auto otherIt = functionIdIt + 1; otherIt != functionIds.end(); ++otherIt) { - Info* otherInfoPtr = corpus_.find(*otherIt); + Symbol* otherInfoPtr = corpus_.find(*otherIt); MRDOCS_CHECK_OR_CONTINUE(otherInfoPtr); auto* otherFunction = otherInfoPtr->asFunctionPtr(); MRDOCS_CHECK_OR_CONTINUE(otherFunction); @@ -228,7 +229,7 @@ foldOverloads(SymbolID const& contextId, std::vector& functionIds, boo } } functionIdIt = functionIds.begin() + itOffset; - MRDOCS_ASSERT(corpus_.info_.emplace(std::make_unique(std::move(O))).second); + MRDOCS_ASSERT(corpus_.info_.emplace(std::make_unique(std::move(O))).second); } } @@ -242,10 +243,10 @@ toDerivedView(std::vector const& ids, CorpusImpl& c) std::views::transform([&c](SymbolID const& id) { return c.find(id); }) | - std::views::filter([](Info* infoPtr) { + std::views::filter([](Symbol* infoPtr) { return infoPtr != nullptr; }) | - std::views::transform([](Info* infoPtr) -> T* { + std::views::transform([](Symbol* infoPtr) -> T* { return dynamic_cast(infoPtr); }) | std::views::filter([](T* ptr) { @@ -259,21 +260,21 @@ toDerivedView(std::vector const& ids, CorpusImpl& c) void OverloadsFinalizer:: -operator()(NamespaceInfo& I) +operator()(NamespaceSymbol& I) { MRDOCS_CHECK_OR(!finalized_.contains(I.id)); finalized_.emplace(I.id); foldOverloads(I.id, I.Members.Functions, true); - for (RecordInfo& RI: toDerivedView(I.Members.Records, corpus_)) + for (RecordSymbol& RI: toDerivedView(I.Members.Records, corpus_)) { operator()(RI); } - for (NamespaceInfo& NI: toDerivedView(I.Members.Namespaces, corpus_)) + for (NamespaceSymbol& NI: toDerivedView(I.Members.Namespaces, corpus_)) { operator()(NI); } - for (UsingInfo& UI: toDerivedView(I.Members.Usings, corpus_)) + for (UsingSymbol& UI: toDerivedView(I.Members.Usings, corpus_)) { operator()(UI); } @@ -281,7 +282,7 @@ operator()(NamespaceInfo& I) void OverloadsFinalizer:: -operator()(RecordInfo& I) +operator()(RecordSymbol& I) { MRDOCS_CHECK_OR(!finalized_.contains(I.id)); finalized_.emplace(I.id); @@ -289,13 +290,13 @@ operator()(RecordInfo& I) for (auto& b: I.Bases) { auto& BT = b.Type; - MRDOCS_CHECK_OR(BT); + MRDOCS_ASSERT(!BT.valueless_after_move()); MRDOCS_CHECK_OR(BT->isNamed()); - auto& NT = dynamic_cast(*BT); + auto& NT = BT->asNamed(); MRDOCS_CHECK_OR(NT.Name); - auto& NI = dynamic_cast(*NT.Name); + auto& NI = NT.Name->asName(); MRDOCS_CHECK_OR(NI.id); - Info* baseInfo = corpus_.find(NI.id); + Symbol* baseInfo = corpus_.find(NI.id); MRDOCS_CHECK_OR(baseInfo); auto* baseRecord = baseInfo->asRecordPtr(); MRDOCS_CHECK_OR(baseRecord); @@ -307,28 +308,28 @@ operator()(RecordInfo& I) foldOverloads(I.id, I.Interface.Public.StaticFunctions, true); foldOverloads(I.id, I.Interface.Protected.StaticFunctions, true); foldOverloads(I.id, I.Interface.Private.StaticFunctions, true); - for (RecordInfo& RI: toDerivedView(I.Interface.Public.Records, corpus_)) { + for (RecordSymbol& RI: toDerivedView(I.Interface.Public.Records, corpus_)) { operator()(RI); } - for (RecordInfo& RI: toDerivedView(I.Interface.Protected.Records, corpus_)) { + for (RecordSymbol& RI: toDerivedView(I.Interface.Protected.Records, corpus_)) { operator()(RI); } - for (RecordInfo& RI: toDerivedView(I.Interface.Private.Records, corpus_)) { + for (RecordSymbol& RI: toDerivedView(I.Interface.Private.Records, corpus_)) { operator()(RI); } } void OverloadsFinalizer:: -operator()(UsingInfo& I) +operator()(UsingSymbol& I) { MRDOCS_CHECK_OR(!finalized_.contains(I.id)); finalized_.emplace(I.id); - auto shadowFunctions = toDerivedView(I.ShadowDeclarations, corpus_); - for (FunctionInfo& FI: shadowFunctions) + auto shadowFunctions = toDerivedView(I.ShadowDeclarations, corpus_); + for (FunctionSymbol& FI: shadowFunctions) { - Info* PI = corpus_.find(FI.Parent); + Symbol* PI = corpus_.find(FI.Parent); MRDOCS_CHECK_OR_CONTINUE(PI); if (auto* NI = PI->asNamespacePtr()) { @@ -343,4 +344,4 @@ operator()(UsingInfo& I) foldOverloads(I.id, I.ShadowDeclarations, true); } -} // clang::mrdocs +} // mrdocs diff --git a/src/lib/Metadata/Finalizers/OverloadsFinalizer.hpp b/src/lib/Metadata/Finalizers/OverloadsFinalizer.hpp index 189417538..cb065ba2d 100644 --- a/src/lib/Metadata/Finalizers/OverloadsFinalizer.hpp +++ b/src/lib/Metadata/Finalizers/OverloadsFinalizer.hpp @@ -8,13 +8,13 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_LIB_METADATA_FINALIZER_OVERLOADSFINALIZER_HPP -#define MRDOCS_LIB_METADATA_FINALIZER_OVERLOADSFINALIZER_HPP +#ifndef MRDOCS_LIB_METADATA_FINALIZERS_OVERLOADSFINALIZER_HPP +#define MRDOCS_LIB_METADATA_FINALIZERS_OVERLOADSFINALIZER_HPP -#include "lib/Metadata/InfoSet.hpp" -#include "lib/CorpusImpl.hpp" +#include +#include -namespace clang::mrdocs { +namespace mrdocs { /** Finalizes a set of Info. @@ -52,24 +52,24 @@ class OverloadsFinalizer /* Visit the namespace members identifying overload sets */ void - operator()(NamespaceInfo& I); + operator()(NamespaceSymbol& I); /* Visit the record members identifying overload sets */ void - operator()(RecordInfo& I); + operator()(RecordSymbol& I); /* Visit the using directive shadows for overloads */ void - operator()(UsingInfo& I); + operator()(UsingSymbol& I); /* No-op for other Info types */ void - operator()(Info&) {} + operator()(Symbol&) {} }; -} // clang::mrdocs +} // mrdocs -#endif +#endif // MRDOCS_LIB_METADATA_FINALIZERS_OVERLOADSFINALIZER_HPP diff --git a/src/lib/Metadata/Finalizers/SortMembersFinalizer.cpp b/src/lib/Metadata/Finalizers/SortMembersFinalizer.cpp index 3045ab1bd..c6fd50ca7 100644 --- a/src/lib/Metadata/Finalizers/SortMembersFinalizer.cpp +++ b/src/lib/Metadata/Finalizers/SortMembersFinalizer.cpp @@ -9,10 +9,10 @@ // #include "SortMembersFinalizer.hpp" -#include #include +#include -namespace clang::mrdocs { +namespace mrdocs { namespace { // Comparison function by symbol IDs @@ -22,20 +22,20 @@ struct SymbolIDCompareFn template static - std::optional + Optional findFunctionClass(InfoTy const& I) { - if constexpr (std::same_as) + if constexpr (std::same_as) { return visit(I, [](U const& u) - -> std::optional + -> Optional { return findFunctionClass(u); }); } else if constexpr ( - std::same_as || - std::same_as) + std::same_as || + std::same_as) { return I.Class; } @@ -44,20 +44,20 @@ struct SymbolIDCompareFn template static - std::optional + Optional findOperatorKind(InfoTy const& I) { - if constexpr (std::same_as) + if constexpr (std::same_as) { return visit(I, [](U const& u) - -> std::optional + -> Optional { return findOperatorKind(u); }); } else if constexpr ( - std::same_as || - std::same_as) + std::same_as || + std::same_as) { return I.OverloadedOperator; } @@ -68,16 +68,16 @@ struct SymbolIDCompareFn operator()(SymbolID const& lhsId, SymbolID const& rhsId) const { // Get Info from SymbolID - Info const* lhsPtr = corpus_.find(lhsId); + Symbol const* lhsPtr = corpus_.find(lhsId); MRDOCS_CHECK_OR(lhsPtr, false); - Info const* rhsPtr = corpus_.find(rhsId); + Symbol const* rhsPtr = corpus_.find(rhsId); MRDOCS_CHECK_OR(rhsPtr, true); - Info const& lhs = *lhsPtr; - Info const& rhs = *rhsPtr; + Symbol const& lhs = *lhsPtr; + Symbol const& rhs = *rhsPtr; // Constructors come first - std::optional const lhsClass = findFunctionClass(lhs); - std::optional const rhsClass = findFunctionClass(rhs); + Optional const lhsClass = findFunctionClass(lhs); + Optional const rhsClass = findFunctionClass(rhs); if (corpus_.config->sortMembersCtors1St) { bool const lhsIsCtor = lhsClass && *lhsClass == FunctionClass::Constructor; @@ -100,8 +100,8 @@ struct SymbolIDCompareFn } // Assignment operators come next - std::optional const lhsOp = findOperatorKind(lhs); - std::optional const rhsOp = findOperatorKind(rhs); + Optional const lhsOp = findOperatorKind(lhs); + Optional const rhsOp = findOperatorKind(rhs); if (corpus_.config->sortMembersAssignment1St) { bool const lhsIsAssign = lhsOp && *lhsOp == OperatorKind::Equal; @@ -163,31 +163,30 @@ struct SymbolIDCompareFn (lhsOp && *lhsOp == OperatorKind::Equal && rhsOp && *rhsOp == OperatorKind::Equal)) { - auto& lhsF = lhs.asFunction(); - auto& rhsF = rhs.asFunction(); + FunctionSymbol const& lhsF = lhs.asFunction(); + FunctionSymbol const& rhsF = rhs.asFunction(); if (lhsF.Params.size() == 1 && rhsF.Params.size() == 1) { - auto isCopyOrMoveConstOrAssign = [](FunctionInfo const& I) { + auto isCopyOrMoveConstOrAssign = [](FunctionSymbol const& I) { if (I.Params.size() == 1) { auto const& param = I.Params[0]; - auto const& paramType = param.Type; - if (!paramType->isLValueReference() - && !paramType->isRValueReference()) - { - return false; - } - auto const ¶mRefPointee = + Polymorphic const& paramType = param.Type; + MRDOCS_ASSERT(!paramType.valueless_after_move()); + MRDOCS_CHECK_OR( + paramType->isLValueReference() || + paramType->isRValueReference(), false); + Polymorphic const ¶mRefPointeeOpt = paramType->isLValueReference() - ? static_cast(*paramType) - .PointeeType - : static_cast(*paramType) - .PointeeType; - if (!paramRefPointee->isNamed()) + ? paramType->asLValueReference().PointeeType + : paramType->asRValueReference().PointeeType; + MRDOCS_CHECK_OR(paramRefPointeeOpt, false); + auto const& paramRefPointee = *paramRefPointeeOpt; + if (!paramRefPointee.isNamed()) { return false; } - return paramRefPointee->namedSymbol() == I.Parent; + return paramRefPointee.namedSymbol() == I.Parent; } return false; }; @@ -202,6 +201,8 @@ struct SymbolIDCompareFn // Ensure move comes after copy if (lhsIsCopyOrMove && rhsIsCopyOrMove) { + MRDOCS_ASSERT(!lhsF.Params[0].Type.valueless_after_move()); + MRDOCS_ASSERT(!rhsF.Params[0].Type.valueless_after_move()); bool const lhsIsMove = lhsF.Params[0].Type->isRValueReference(); bool const rhsIsMove = rhsF.Params[0].Type->isRValueReference(); if (lhsIsMove != rhsIsMove) @@ -213,7 +214,7 @@ struct SymbolIDCompareFn } // Special cases are handled, so use the configuration criteria - Info const* P = corpus_.find(lhs.Parent); + Symbol const* P = corpus_.find(lhs.Parent); bool const isClassMember = P && P->isRecord(); auto const generalSortCriteria = isClassMember @@ -221,13 +222,13 @@ struct SymbolIDCompareFn : corpus_.config->sortNamespaceMembersBy; switch (generalSortCriteria) { - case clang::mrdocs::PublicSettings::SortSymbolBy::Name: + case mrdocs::PublicSettings::SortSymbolBy::Name: if (auto const cmp = lhs.Name <=> rhs.Name; cmp != 0) { return std::is_lt(cmp); } break; - case clang::mrdocs::PublicSettings::SortSymbolBy::Location: + case mrdocs::PublicSettings::SortSymbolBy::Location: { // By location: short path, line, column auto const& lhsLoc = getPrimaryLocation(lhs); @@ -242,6 +243,11 @@ struct SymbolIDCompareFn { return std::is_lt(cmp); } + if (auto const cmp = lhsLoc->ColumnNumber <=> rhsLoc->ColumnNumber; + cmp != 0) + { + return std::is_lt(cmp); + } break; } default: @@ -317,10 +323,10 @@ toDerivedView(std::vector const& ids, CorpusImpl& c) std::views::transform([&c](SymbolID const& id) { return c.find(id); }) | - std::views::filter([](Info* infoPtr) { + std::views::filter([](Symbol* infoPtr) { return infoPtr != nullptr; }) | - std::views::transform([](Info* infoPtr) -> T* { + std::views::transform([](Symbol* infoPtr) -> T* { return dynamic_cast(infoPtr); }) | std::views::filter([](T* ptr) { @@ -334,21 +340,21 @@ toDerivedView(std::vector const& ids, CorpusImpl& c) void SortMembersFinalizer:: -operator()(NamespaceInfo& I) +operator()(NamespaceSymbol& I) { // Sort members of all tranches sortMembers(I.Members); // Recursively sort members of child namespaces, records, and overloads - for (RecordInfo& RI: toDerivedView(I.Members.Records, corpus_)) + for (RecordSymbol& RI: toDerivedView(I.Members.Records, corpus_)) { operator()(RI); } - for (NamespaceInfo& RI: toDerivedView(I.Members.Namespaces, corpus_)) + for (NamespaceSymbol& RI: toDerivedView(I.Members.Namespaces, corpus_)) { operator()(RI); } - for (OverloadsInfo& RI: toDerivedView(I.Members.Functions, corpus_)) + for (OverloadsSymbol& RI: toDerivedView(I.Members.Functions, corpus_)) { operator()(RI); } @@ -356,7 +362,7 @@ operator()(NamespaceInfo& I) void SortMembersFinalizer:: -operator()(RecordInfo& I) +operator()(RecordSymbol& I) { // Sort members of all tranches if sorting is enabled for records if (corpus_.config->sortMembers) @@ -365,39 +371,39 @@ operator()(RecordInfo& I) } // Recursively sort members of child records and overloads - for (RecordInfo& RI: toDerivedView(I.Interface.Public.Records, corpus_)) + for (RecordSymbol& RI: toDerivedView(I.Interface.Public.Records, corpus_)) { operator()(RI); } - for (RecordInfo& RI: toDerivedView(I.Interface.Protected.Records, corpus_)) + for (RecordSymbol& RI: toDerivedView(I.Interface.Protected.Records, corpus_)) { operator()(RI); } - for (RecordInfo& RI: toDerivedView(I.Interface.Private.Records, corpus_)) + for (RecordSymbol& RI: toDerivedView(I.Interface.Private.Records, corpus_)) { operator()(RI); } - for (OverloadsInfo& RI: toDerivedView(I.Interface.Public.Functions, corpus_)) + for (OverloadsSymbol& RI: toDerivedView(I.Interface.Public.Functions, corpus_)) { operator()(RI); } - for (OverloadsInfo& RI: toDerivedView(I.Interface.Protected.Functions, corpus_)) + for (OverloadsSymbol& RI: toDerivedView(I.Interface.Protected.Functions, corpus_)) { operator()(RI); } - for (OverloadsInfo& RI: toDerivedView(I.Interface.Private.Functions, corpus_)) + for (OverloadsSymbol& RI: toDerivedView(I.Interface.Private.Functions, corpus_)) { operator()(RI); } - for (OverloadsInfo& RI: toDerivedView(I.Interface.Public.StaticFunctions, corpus_)) + for (OverloadsSymbol& RI: toDerivedView(I.Interface.Public.StaticFunctions, corpus_)) { operator()(RI); } - for (OverloadsInfo& RI: toDerivedView(I.Interface.Protected.StaticFunctions, corpus_)) + for (OverloadsSymbol& RI: toDerivedView(I.Interface.Protected.StaticFunctions, corpus_)) { operator()(RI); } - for (OverloadsInfo& RI: toDerivedView(I.Interface.Private.StaticFunctions, corpus_)) + for (OverloadsSymbol& RI: toDerivedView(I.Interface.Private.StaticFunctions, corpus_)) { operator()(RI); } @@ -405,10 +411,10 @@ operator()(RecordInfo& I) void SortMembersFinalizer:: -operator()(OverloadsInfo& I) +operator()(OverloadsSymbol& I) { // Sort the member functions sortMembers(I.Members); } -} // clang::mrdocs +} // mrdocs diff --git a/src/lib/Metadata/Finalizers/SortMembersFinalizer.hpp b/src/lib/Metadata/Finalizers/SortMembersFinalizer.hpp index be01754d7..efeaeb980 100644 --- a/src/lib/Metadata/Finalizers/SortMembersFinalizer.hpp +++ b/src/lib/Metadata/Finalizers/SortMembersFinalizer.hpp @@ -8,13 +8,13 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_LIB_METADATA_FINALIZER_SORTMEMBERSFINALIZER_HPP -#define MRDOCS_LIB_METADATA_FINALIZER_SORTMEMBERSFINALIZER_HPP +#ifndef MRDOCS_LIB_METADATA_FINALIZERS_SORTMEMBERSFINALIZER_HPP +#define MRDOCS_LIB_METADATA_FINALIZERS_SORTMEMBERSFINALIZER_HPP -#include "lib/Metadata/InfoSet.hpp" -#include "lib/CorpusImpl.hpp" +#include +#include -namespace clang::mrdocs { +namespace mrdocs { /** Finalizes a set of Info. @@ -57,25 +57,25 @@ class SortMembersFinalizer void build() { - Info* globalPtr = corpus_.find(SymbolID::global); + Symbol* globalPtr = corpus_.find(SymbolID::global); MRDOCS_CHECK_OR(globalPtr); MRDOCS_ASSERT(globalPtr->isNamespace()); operator()(globalPtr->asNamespace()); } void - operator()(NamespaceInfo& I); + operator()(NamespaceSymbol& I); void - operator()(RecordInfo& I); + operator()(RecordSymbol& I); void - operator()(OverloadsInfo& I); + operator()(OverloadsSymbol& I); void - operator()(Info&) {} + operator()(Symbol&) {} }; -} // clang::mrdocs +} // mrdocs -#endif +#endif // MRDOCS_LIB_METADATA_FINALIZERS_SORTMEMBERSFINALIZER_HPP diff --git a/src/lib/Metadata/Info.cpp b/src/lib/Metadata/Info.cpp index aecbce349..397e770b1 100644 --- a/src/lib/Metadata/Info.cpp +++ b/src/lib/Metadata/Info.cpp @@ -10,38 +10,38 @@ // Official repository: https://github.com/cppalliance/mrdocs // +#include #include #include -#include "lib/Support/Radix.hpp" +#include +#include +#include +#include #include #include #include #include -#include -#include -#include -#include -namespace clang::mrdocs { +namespace mrdocs { dom::String -toString(InfoKind const kind) noexcept +toString(SymbolKind const kind) noexcept { switch(kind) { #define INFO(Type) \ - case InfoKind::Type: return toKebabCase(#Type); -#include + case SymbolKind::Type: return toKebabCase(#Type); +#include default: MRDOCS_UNREACHABLE(); } } void -merge(Info& I, Info&& Other) +merge(Symbol& I, Symbol&& Other) { MRDOCS_ASSERT(I.id); - merge(I.asSourceInfo(), std::move(Other.asSourceInfo())); + merge(I.Loc, std::move(Other.Loc)); if (I.Name == "") { I.Name = Other.Name; @@ -56,15 +56,15 @@ merge(Info& I, Info&& Other) } I.Extraction = leastSpecific(I.Extraction, Other.Extraction); - // Append javadocs - if (!I.javadoc) + // Append docs + if (!I.doc) { - I.javadoc = std::move(Other.javadoc); + I.doc = std::move(Other.doc); } - else if (Other.javadoc) + else if (Other.doc) { - merge(*I.javadoc, std::move(*Other.javadoc)); + merge(*I.doc, std::move(*Other.doc)); } } -} // clang::mrdocs +} // mrdocs diff --git a/src/lib/Metadata/InfoSet.hpp b/src/lib/Metadata/InfoSet.hpp deleted file mode 100644 index 5573304cb..000000000 --- a/src/lib/Metadata/InfoSet.hpp +++ /dev/null @@ -1,184 +0,0 @@ -// -// This is a derivative work. originally part of the LLVM Project. -// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) -// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) -// -// Official repository: https://github.com/cppalliance/mrdocs -// - -#ifndef MRDOCS_LIB_TOOL_INFOSET_HPP -#define MRDOCS_LIB_TOOL_INFOSET_HPP - -#include "mrdocs/Platform.hpp" -#include "mrdocs/Metadata/Info.hpp" -#include -#include - -namespace clang::mrdocs { - -/** A hash function for Info pointers. - - The generated hash is based on the SymbolID - of the Info object. - - This hasher is used to implement the InfoSet. -*/ -struct InfoPtrHasher -{ - using is_transparent = void; - - /** Returns the hash of the Info object. - - The hash is based on the SymbolID of the Info object. - - @param I The Info object to hash. - */ - std::size_t - operator()( - const std::unique_ptr& I) const; - - /** Returns the hash of the SymbolID. - - The function allows the SymbolID to be used as a key - in an unordered container. - - @param id The SymbolID to hash. - */ - std::size_t - operator()( - SymbolID const& id) const; -}; - -/** Equality comparison for Info pointers. - - The comparison is based on the SymbolID of the Info object. - - This equality comparison is used to implement the InfoSet. -*/ -struct InfoPtrEqual -{ - using is_transparent = void; - - /** Returns `true` if the Info objects are equal. - - The comparison is based on the SymbolID of the Info object. - - @param a The first Info object to compare. - @param b The second Info object to compare. - */ - bool - operator()( - const std::unique_ptr& a, - const std::unique_ptr& b) const; - - /** Returns `true` if the id of the Info object is equal to the SymbolID. - - The comparison is based on the SymbolID of the Info object. - - The function allows the SymbolID to be used as a key - in an unordered container. - - @param a The Info object to compare. - @param b The SymbolID to compare. - */ - bool - operator()( - const std::unique_ptr& a, - SymbolID const& b) const; - - /** Returns `true` if the SymbolID is equal to the id of the Info object. - - The comparison is based on the SymbolID of the Info object. - - The function allows the SymbolID to be used as a key - in an unordered container. - - @param a The SymbolID to compare. - @param b The Info object to compare. - */ - bool - operator()( - SymbolID const& a, - const std::unique_ptr& b) const; -}; - -/** A set of Info objects. - - This set is used to store the results of the execution - of a tool at the end of the processing. - - This is a set of unique pointers to Info objects. -*/ -using InfoSet = std::unordered_set< - std::unique_ptr, InfoPtrHasher, InfoPtrEqual>; - -struct UndocumentedInfo final : SourceInfo { - SymbolID id; - std::string name; - InfoKind kind; - - constexpr - UndocumentedInfo( - SymbolID id_, - std::string name_, - InfoKind kind_) noexcept - : SourceInfo() - , id(id_) - , name(std::move(name_)) - , kind(kind_) - { - } -}; - -struct UndocumentedInfoHasher { - using is_transparent = void; - - std::size_t - operator()(SymbolID const& I) const { - return std::hash()(I); - } - - std::size_t - operator()(UndocumentedInfo const& I) const { - return std::hash()(I.id); - } -}; - -struct UndocumentedInfoEqual { - using is_transparent = void; - - bool - operator()( - UndocumentedInfo const& a, - UndocumentedInfo const& b) const - { - return a.id == b.id; - } - - bool - operator()( - UndocumentedInfo const& a, - SymbolID const& b) const - { - return a.id == b; - } - - bool - operator()( - SymbolID const& a, - UndocumentedInfo const& b) const - { - return a == b.id; - } -}; - -using UndocumentedInfoSet = std::unordered_set< - UndocumentedInfo, UndocumentedInfoHasher, UndocumentedInfoEqual>; - -} // clang::mrdocs - -#endif diff --git a/src/lib/Metadata/Javadoc.cpp b/src/lib/Metadata/Javadoc.cpp deleted file mode 100644 index 700884f25..000000000 --- a/src/lib/Metadata/Javadoc.cpp +++ /dev/null @@ -1,393 +0,0 @@ -// -// This is a derivative work. originally part of the LLVM Project. -// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) -// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) -// -// Official repository: https://github.com/cppalliance/mrdocs -// - -#include "lib/Support/Debug.hpp" -#include -#include -#include -#include -#include -#include - -namespace clang { -namespace mrdocs { -namespace doc { - -dom::String -toString(NodeKind kind) noexcept -{ - switch(kind) - { - case NodeKind::text: - return "text"; - case NodeKind::admonition: - return "admonition"; - case NodeKind::brief: - return "brief"; - case NodeKind::code: - return "code"; - case NodeKind::heading: - return "heading"; - case NodeKind::link: - return "link"; - case NodeKind::list_item: - return "list_item"; - case NodeKind::unordered_list: - return "unordered_list"; - case NodeKind::paragraph: - return "paragraph"; - case NodeKind::param: - return "param"; - case NodeKind::returns: - return "returns"; - case NodeKind::styled: - return "styled"; - case NodeKind::tparam: - return "tparam"; - case NodeKind::reference: - return "reference"; - case NodeKind::copy_details: - return "copied"; - case NodeKind::throws: - return "throws"; - case NodeKind::details: - return "details"; - case NodeKind::see: - return "see"; - case NodeKind::precondition: - return "precondition"; - case NodeKind::postcondition: - return "postcondition"; - default: - MRDOCS_UNREACHABLE(); - } -} - -dom::String -toString(Style kind) noexcept -{ - switch(kind) - { - case Style::none: - return ""; - case Style::mono: - return "mono"; - case Style::bold: - return "bold"; - case Style::italic: - return "italic"; - default: - MRDOCS_UNREACHABLE(); - } -} - -dom::String -toString(Admonish kind) noexcept -{ - switch(kind) - { - case Admonish::none: - return ""; - case Admonish::note: - return "note"; - case Admonish::tip: - return "tip"; - case Admonish::important: - return "important"; - case Admonish::caution: - return "caution"; - case Admonish::warning: - return "warning"; - default: - MRDOCS_UNREACHABLE(); - } -} - -dom::String -toString(ParamDirection kind) noexcept -{ - switch(kind) - { - case ParamDirection::none: - return ""; - case ParamDirection::in: - return "in"; - case ParamDirection::out: - return "out"; - case ParamDirection::inout: - return "inout"; - default: - MRDOCS_UNREACHABLE(); - } -} - -dom::String -toString(Parts kind) noexcept -{ - switch(kind) - { - case Parts::all: - return "all"; - case Parts::brief: - return "brief"; - case Parts::description: - return "description"; - default: - MRDOCS_UNREACHABLE(); - } -} - -Text& -Block:: -emplace_back( - Polymorphic text) -{ - MRDOCS_ASSERT(text->isText()); - return *children.emplace_back(std::move(text)); -} - -void -Block:: -append(std::vector>&& blocks) -{ - children.reserve(children.size() + blocks.size()); - for (auto&& block : blocks) - { - MRDOCS_ASSERT(block->isText()); - emplace_back(Polymorphic(std::in_place_type, - dynamic_cast(*block))); - } -} - -void -Block:: -append(std::vector> const& otherChildren) -{ - children.insert( - children.end(), - otherChildren.begin(), - otherChildren.end()); -} - - -std::strong_ordering -operator<=>(Polymorphic const& lhs, Polymorphic const& rhs) -{ - if (lhs && rhs) - { - if (lhs->Kind == rhs->Kind) - { - return visit(*lhs, detail::VisitCompareFn(*rhs)); - } - return lhs->Kind <=> rhs->Kind; - } - if (!lhs && !rhs) - { - return std::strong_ordering::equal; - } - return !lhs ? std::strong_ordering::less - : std::strong_ordering::greater; -} - -Paragraph& -Paragraph:: -operator=(std::string_view str) -{ - this->children.clear(); - this->children.emplace_back(Polymorphic( - std::in_place_type, std::string(str))); - return *this; -} - -} // doc - -//------------------------------------------------ - -Javadoc:: -Javadoc() noexcept = default; - -Javadoc:: -Javadoc( - std::vector> blocks) - : blocks(std::move(blocks)) -{ -} - -bool -Javadoc:: -operator==(Javadoc const& other) const noexcept -{ - return std::ranges::equal(blocks, other.blocks, - [](auto const& a, auto const& b) - { - return a->equals(static_cast(*b)); - }) && - returns == other.returns && - brief == other.brief && - params == other.params && - tparams == other.tparams && - exceptions == other.exceptions && - sees == other.sees && - related == other.related && - preconditions == other.preconditions && - postconditions == other.postconditions; -} - -bool -Javadoc:: -operator!=( - Javadoc const& other) const noexcept -{ - return !(*this == other); -} - -std::string -Javadoc:: -emplace_back( - Polymorphic block) -{ - MRDOCS_ASSERT(block->isBlock()); - - std::string result; - switch(block->Kind) - { - case doc::NodeKind::param: - { - // check for duplicate parameter name - auto t = dynamic_cast(block.operator->()); - for(auto const& q : blocks) - { - if(q->Kind == doc::NodeKind::param) - { - auto u = dynamic_cast(q.operator->()); - if(u->name == t->name) - { - result = std::format("duplicate param {}", t->name); - break; - } - } - } - break; - } - case doc::NodeKind::tparam: - { - // check for duplicate template parameter name - auto t = dynamic_cast(block.operator->()); - for(auto const& q : blocks) - { - if(q->Kind == doc::NodeKind::tparam) - { - auto u = dynamic_cast(q.operator->()); - if(u->name == t->name) - { - result = std::format("duplicate tparam {}", t->name); - break; - } - } - } - break; - } - default: - break; - } - - blocks.emplace_back(std::move(block)); - return result; -} - -void -Javadoc:: -append( - Javadoc&& other) -{ - using std::ranges::find; - using std::ranges::copy_if; - using std::views::transform; - - // blocks - for (auto&& block: other.blocks) - { - emplace_back(std::move(block)); - } - // returns - copy_if(other.returns, std::back_inserter(returns), - [&](auto const& r) - { - return find(returns, r) == returns.end(); - }); - // params - copy_if(other.params, std::back_inserter(params), - [&](auto const& p) - { - auto namesAndDirection = transform(params, [](auto const& q) - { - return std::make_pair(std::string_view(q.name), q.direction); - }); - auto el = std::make_pair(std::string_view(p.name), p.direction); - return find(namesAndDirection, el) == namesAndDirection.end(); - }); - // tparams - copy_if(other.tparams, std::back_inserter(tparams), - [&](auto const& p) - { - auto names = transform(tparams, &doc::TParam::name); - return find(names, p.name) == names.end(); - }); - // exceptions - copy_if(other.exceptions, std::back_inserter(exceptions), - [&](auto const& e) - { - // e.exception.string - auto exceptionRefs = transform(exceptions, &doc::Throws::exception); - auto exceptionStrs = transform(exceptionRefs, &doc::Reference::string); - return find(exceptionStrs, e) == exceptionStrs.end(); - }); - // sees - copy_if(other.sees, std::back_inserter(sees), - [&](auto const& s) - { - return find(sees, s) == sees.end(); - }); - // preconditions - copy_if(other.preconditions, std::back_inserter(preconditions), - [&](auto const& p) - { - return find(preconditions, p) == preconditions.end(); - }); - // postconditions - copy_if(other.postconditions, std::back_inserter(postconditions), - [&](auto const& p) - { - return find(postconditions, p) == postconditions.end(); - }); -} - -void -Javadoc:: -append(std::vector>&& blocks) -{ - blocks.reserve(blocks.size() + blocks.size()); - for(auto&& blockAsNode : blocks) - { - if (auto *Block = dynamic_cast(&*blockAsNode)) - { - emplace_back(std::move(*Block)); - } - else - { - blockAsNode = std::nullopt; - } - } -} - -} // mrdocs -} // clang diff --git a/src/lib/Metadata/Name.cpp b/src/lib/Metadata/Name.cpp index e9e0e97ee..4f9d0656f 100644 --- a/src/lib/Metadata/Name.cpp +++ b/src/lib/Metadata/Name.cpp @@ -9,13 +9,13 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include #include #include #include #include +#include -namespace clang::mrdocs { +namespace mrdocs { dom::String toString(NameKind kind) noexcept { @@ -40,56 +40,67 @@ writeTo( } std::strong_ordering -NameInfo:: -operator<=>(NameInfo const& other) const +Name:: +operator<=>(Name const& other) const { - return - std::tie(Kind, Name, Prefix) <=> - std::tie(other.Kind, other.Name, other.Prefix); + if (this == &other) + { + return std::strong_ordering::equal; + } + if (Kind != other.Kind) + { + return Kind <=> other.Kind; + } + if (Identifier != other.Identifier) + { + return Identifier <=> other.Identifier; + } + if (bool(Prefix) != bool(other.Prefix)) + { + return bool(Prefix) <=> bool(other.Prefix); + } + if (Prefix && other.Prefix) + { + return *Prefix <=> *other.Prefix; + } + return std::strong_ordering::equal; } std::strong_ordering -operator<=>(Polymorphic const& lhs, Polymorphic const& rhs) +operator<=>(Polymorphic const& lhs, Polymorphic const& rhs) { - if (lhs && rhs) + MRDOCS_ASSERT(!lhs.valueless_after_move()); + MRDOCS_ASSERT(!rhs.valueless_after_move()); + if (lhs->Kind == rhs->Kind) { - if (lhs->Kind == rhs->Kind) + if (lhs->isIdentifier()) { - if (lhs->isIdentifier()) - { - return *lhs <=> *rhs; - } - return visit(*lhs, detail::VisitCompareFn(*rhs)); + return *lhs <=> *rhs; } - return lhs->Kind <=> rhs->Kind; - } - if (!lhs && !rhs) - { - return std::strong_ordering::equal; + return visit(*lhs, detail::VisitCompareFn(*rhs)); } - return !lhs ? std::strong_ordering::less - : std::strong_ordering::greater; + return lhs->Kind <=> rhs->Kind; } static void toStringImpl( std::string& result, - NameInfo const& N) + Name const& N) { if (N.Prefix) { - toStringImpl(result, *N.Prefix); + toStringImpl(result, **N.Prefix); writeTo(result, "::"); } - writeTo(result, N.Name); + writeTo(result, N.Identifier); if (!N.isSpecialization()) { return; } - auto const& NN = dynamic_cast(N); + auto const& NN = N.asSpecialization(); std::span const targs = NN.TemplateArgs; writeTo(result, '<'); if(! targs.empty()) @@ -99,10 +110,10 @@ toStringImpl( { if constexpr(U::isType()) { - if(u.Type) - writeTo(result, toString(*u.Type)); + MRDOCS_ASSERT(!u.Type.valueless_after_move()); + writeTo(result, toString(*u.Type)); } - if constexpr(U::isNonType()) + if constexpr(U::isConstant()) { writeTo(result, u.Value.Written); } @@ -124,7 +135,7 @@ toStringImpl( } std::string -toString(NameInfo const& N) +toString(Name const& N) { std::string result; toStringImpl(result, N); @@ -136,14 +147,14 @@ void tag_invoke( dom::LazyObjectMapTag, IO& io, - NameInfo const& I, + Name const& I, DomCorpus const* domCorpus) { io.map("class", std::string("name")); io.map("kind", I.Kind); visit(I, [domCorpus, &io](T const& t) { - io.map("name", t.Name); + io.map("name", t.Identifier); io.map("symbol", t.id); if constexpr(requires { t.TemplateArgs; }) { @@ -157,10 +168,10 @@ void tag_invoke( dom::ValueFromTag, dom::Value& v, - NameInfo const& I, + Name const& I, DomCorpus const* domCorpus) { v = dom::LazyObject(I, domCorpus); } -} // clang::mrdocs +} // mrdocs diff --git a/src/lib/Metadata/Reduce.hpp b/src/lib/Metadata/Reduce.hpp index ec011697c..d26a666f3 100644 --- a/src/lib/Metadata/Reduce.hpp +++ b/src/lib/Metadata/Reduce.hpp @@ -16,7 +16,7 @@ #include #include -namespace clang::mrdocs { +namespace mrdocs { // // This file defines the merging of different types of infos. The data in the @@ -33,11 +33,11 @@ namespace clang::mrdocs { // template -std::unique_ptr -reduce(std::vector>& Values) +std::unique_ptr +reduce(std::vector>& Values) { MRDOCS_ASSERT(! Values.empty() && Values[0]); - std::unique_ptr Merged = std::make_unique(Values[0]->id); + std::unique_ptr Merged = std::make_unique(Values[0]->id); T* Tmp = static_cast(Merged.get()); for (auto& I: Values) { @@ -79,7 +79,7 @@ reduceChildren( } } -} // clang::mrdocs +} // mrdocs #endif \ No newline at end of file diff --git a/src/lib/Metadata/Source.cpp b/src/lib/Metadata/Source.cpp index 04f19902f..817a5dd66 100644 --- a/src/lib/Metadata/Source.cpp +++ b/src/lib/Metadata/Source.cpp @@ -11,11 +11,13 @@ #include #include -#include +#include +#include +#include #include -#include +#include -namespace clang::mrdocs { +namespace mrdocs { std::string_view toString(FileKind kind) @@ -80,7 +82,7 @@ merge(SourceInfo& I, SourceInfo&& Other) mergeImpl(I, Other); } -OptionalLocation +Optional getPrimaryLocation(SourceInfo const& I, bool const preferDefinition) { if (I.Loc.empty() || @@ -93,9 +95,9 @@ getPrimaryLocation(SourceInfo const& I, bool const preferDefinition) I.Loc, &Location::Documented); if (documentedIt != I.Loc.end()) { - return OptionalLocation(*documentedIt); + return Optional(*documentedIt); } - return OptionalLocation(I.Loc.front()); + return Optional(I.Loc.front()); } template @@ -109,6 +111,7 @@ tag_invoke( io.map("shortPath", loc.ShortPath); io.map("sourcePath", loc.SourcePath); io.map("line", loc.LineNumber); + io.map("column", loc.ColumnNumber); io.map("documented", loc.Documented); } @@ -147,4 +150,4 @@ tag_invoke( v = dom::LazyObject(I); } -} // clang::mrdocs +} // mrdocs diff --git a/src/lib/Metadata/Specifiers.cpp b/src/lib/Metadata/Specifiers.cpp index 38b7b97f6..9f8b45848 100644 --- a/src/lib/Metadata/Specifiers.cpp +++ b/src/lib/Metadata/Specifiers.cpp @@ -8,11 +8,11 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include "mrdocs/Support/Algorithm.hpp" -#include #include +#include +#include + -namespace clang { namespace mrdocs { dom::String toString(AccessKind kind) noexcept @@ -201,5 +201,5 @@ isBinaryOperator(OperatorKind kind) noexcept } -} // clang + } // mrdocs diff --git a/src/lib/Metadata/Info/Concept.cpp b/src/lib/Metadata/Symbol/Concept.cpp similarity index 90% rename from src/lib/Metadata/Info/Concept.cpp rename to src/lib/Metadata/Symbol/Concept.cpp index a0cdb3a9c..28aea5bf5 100644 --- a/src/lib/Metadata/Info/Concept.cpp +++ b/src/lib/Metadata/Symbol/Concept.cpp @@ -9,15 +9,15 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include #include +#include #include -namespace clang::mrdocs { +namespace mrdocs { std::strong_ordering -ConceptInfo:: -operator<=>(ConceptInfo const& other) const +ConceptSymbol:: +operator<=>(ConceptSymbol const& other) const { if (auto const cmp = Name <=> other.Name; !std::is_eq(cmp)) @@ -59,7 +59,7 @@ operator<=>(ConceptInfo const& other) const } void -merge(ConceptInfo& I, ConceptInfo&& Other) +merge(ConceptSymbol& I, ConceptSymbol&& Other) { MRDOCS_ASSERT(canMerge(I, Other)); merge(I.asInfo(), std::move(Other.asInfo())); @@ -73,5 +73,5 @@ merge(ConceptInfo& I, ConceptInfo&& Other) } } -} // clang::mrdocs +} // mrdocs diff --git a/src/lib/Metadata/Info/Enum.cpp b/src/lib/Metadata/Symbol/Enum.cpp similarity index 89% rename from src/lib/Metadata/Info/Enum.cpp rename to src/lib/Metadata/Symbol/Enum.cpp index 47405e1f5..d076cf8c7 100644 --- a/src/lib/Metadata/Info/Enum.cpp +++ b/src/lib/Metadata/Symbol/Enum.cpp @@ -9,11 +9,11 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include #include +#include #include -namespace clang::mrdocs { +namespace mrdocs { namespace { @@ -35,7 +35,7 @@ reduceSymbolIDs( } // (anon) void -merge(EnumInfo& I, EnumInfo&& Other) +merge(EnumSymbol& I, EnumSymbol&& Other) { MRDOCS_ASSERT(canMerge(I, Other)); merge(I.asInfo(), std::move(Other.asInfo())); @@ -50,5 +50,5 @@ merge(EnumInfo& I, EnumInfo&& Other) reduceSymbolIDs(I.Constants, std::move(Other.Constants)); } -} // clang::mrdocs +} // mrdocs diff --git a/src/lib/Metadata/Info/EnumConstant.cpp b/src/lib/Metadata/Symbol/EnumConstant.cpp similarity index 81% rename from src/lib/Metadata/Info/EnumConstant.cpp rename to src/lib/Metadata/Symbol/EnumConstant.cpp index 558ed222b..a8ae6aeaa 100644 --- a/src/lib/Metadata/Info/EnumConstant.cpp +++ b/src/lib/Metadata/Symbol/EnumConstant.cpp @@ -9,14 +9,14 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include #include +#include #include -namespace clang::mrdocs { +namespace mrdocs { void -merge(EnumConstantInfo& I, EnumConstantInfo&& Other) +merge(EnumConstantSymbol& I, EnumConstantSymbol&& Other) { MRDOCS_ASSERT(canMerge(I, Other)); merge(I.asInfo(), std::move(Other.asInfo())); @@ -26,5 +26,5 @@ merge(EnumConstantInfo& I, EnumConstantInfo&& Other) } } -} // clang::mrdocs +} // mrdocs diff --git a/src/lib/Metadata/Info/Friend.cpp b/src/lib/Metadata/Symbol/Friend.cpp similarity index 87% rename from src/lib/Metadata/Info/Friend.cpp rename to src/lib/Metadata/Symbol/Friend.cpp index 167304de6..d4d7f4826 100644 --- a/src/lib/Metadata/Info/Friend.cpp +++ b/src/lib/Metadata/Symbol/Friend.cpp @@ -9,11 +9,11 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include #include +#include #include -namespace clang::mrdocs { +namespace mrdocs { void merge(FriendInfo& I, FriendInfo&& Other) @@ -28,5 +28,5 @@ merge(FriendInfo& I, FriendInfo&& Other) } } -} // clang::mrdocs +} // mrdocs diff --git a/src/lib/Metadata/Info/Function.cpp b/src/lib/Metadata/Symbol/Function.cpp similarity index 96% rename from src/lib/Metadata/Info/Function.cpp rename to src/lib/Metadata/Symbol/Function.cpp index 702cd5082..8a2c20ad2 100644 --- a/src/lib/Metadata/Info/Function.cpp +++ b/src/lib/Metadata/Symbol/Function.cpp @@ -10,12 +10,12 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include #include -#include +#include #include +#include -namespace clang::mrdocs { +namespace mrdocs { namespace { @@ -168,7 +168,7 @@ getSafeOperatorName( return full.substr(9); } -std::optional +Optional getOperatorReadableName( OperatorKind const kind, int const nParams) @@ -292,7 +292,7 @@ toString(FunctionClass const kind) noexcept void merge(Param& I, Param&& Other) { - if (!I.Type) + if (I.Type->isAuto()) { I.Type = std::move(Other.Type); } @@ -314,9 +314,9 @@ tag_invoke( Param const& p, DomCorpus const*) { - io.map("name", dom::stringOrNull(*p.Name)); + io.map("name", dom::stringOrNull(p.Name)); io.map("type", p.Type); - io.map("default", dom::stringOrNull(*p.Default)); + io.map("default", dom::stringOrNull(p.Default)); } void @@ -330,8 +330,8 @@ tag_invoke( } std::strong_ordering -FunctionInfo:: -operator<=>(FunctionInfo const& other) const +FunctionSymbol:: +operator<=>(FunctionSymbol const& other) const { if (auto const cmp = Name <=> other.Name; !std::is_eq(cmp)) @@ -383,7 +383,7 @@ operator<=>(FunctionInfo const& other) const } void -merge(FunctionInfo& I, FunctionInfo&& Other) +merge(FunctionSymbol& I, FunctionSymbol&& Other) { MRDOCS_ASSERT(canMerge(I, Other)); merge(I.asInfo(), std::move(Other.asInfo())); @@ -391,10 +391,7 @@ merge(FunctionInfo& I, FunctionInfo&& Other) { I.Class = Other.Class; } - if (!I.ReturnType) - { - I.ReturnType = std::move(Other.ReturnType); - } + I.ReturnType = std::move(Other.ReturnType); std::size_t const n = std::min(I.Params.size(), Other.Params.size()); for (std::size_t i = 0; i < n; ++i) { @@ -460,9 +457,9 @@ merge(FunctionInfo& I, FunctionInfo&& Other) MRDOCS_DECL bool -overrides(FunctionInfo const& base, FunctionInfo const& derived) +overrides(FunctionSymbol const& base, FunctionSymbol const& derived) { - auto toOverrideTuple = [](FunctionInfo const& f) { + auto toOverrideTuple = [](FunctionSymbol const& f) { return std::forward_as_tuple( f.Name, f.Params, @@ -475,5 +472,5 @@ overrides(FunctionInfo const& base, FunctionInfo const& derived) return toOverrideTuple(base) == toOverrideTuple(derived); } -} // clang::mrdocs +} // mrdocs diff --git a/src/lib/Metadata/Info/Guide.cpp b/src/lib/Metadata/Symbol/Guide.cpp similarity index 79% rename from src/lib/Metadata/Info/Guide.cpp rename to src/lib/Metadata/Symbol/Guide.cpp index 391f0bb95..a9a1e4122 100644 --- a/src/lib/Metadata/Info/Guide.cpp +++ b/src/lib/Metadata/Symbol/Guide.cpp @@ -9,15 +9,17 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include #include +#include +#include #include +#include -namespace clang::mrdocs { +namespace mrdocs { std::strong_ordering -GuideInfo:: -operator<=>(GuideInfo const& other) const +GuideSymbol:: +operator<=>(GuideSymbol const& other) const { if (auto const cmp = Name <=> other.Name; !std::is_eq(cmp)) @@ -47,11 +49,19 @@ operator<=>(GuideInfo const& other) const return cmp; } } - if (auto const cmp = Params <=> other.Params; + if (auto const cmp = Params.size() <=> other.Params.size(); !std::is_eq(cmp)) { return cmp; } + for (size_t i = 0; i < Params.size(); ++i) + { + if (auto const cmp = Params[i] <=> other.Params[i]; + !std::is_eq(cmp)) + { + return cmp; + } + } if (Template && other.Template) { if (auto const cmp = Template->Args <=> other.Template->Args; @@ -69,11 +79,11 @@ operator<=>(GuideInfo const& other) const } -void merge(GuideInfo& I, GuideInfo&& Other) +void merge(GuideSymbol& I, GuideSymbol&& Other) { MRDOCS_ASSERT(canMerge(I, Other)); merge(I.asInfo(), std::move(Other.asInfo())); - if (!I.Deduced) + if (I.Deduced->isAuto()) { I.Deduced = std::move(Other.Deduced); } @@ -91,5 +101,4 @@ void merge(GuideInfo& I, GuideInfo&& Other) } } -} // clang::mrdocs - +} // mrdocs diff --git a/src/lib/Metadata/Info/Namespace.cpp b/src/lib/Metadata/Symbol/Namespace.cpp similarity index 91% rename from src/lib/Metadata/Info/Namespace.cpp rename to src/lib/Metadata/Symbol/Namespace.cpp index 5ae737ac8..b38fc9073 100644 --- a/src/lib/Metadata/Info/Namespace.cpp +++ b/src/lib/Metadata/Symbol/Namespace.cpp @@ -9,15 +9,15 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include #include +#include #include -namespace clang::mrdocs { +namespace mrdocs { std::strong_ordering -NamespaceInfo:: -operator<=>(NamespaceInfo const& other) const +NamespaceSymbol:: +operator<=>(NamespaceSymbol const& other) const { if (auto const res = this->asInfo() <=> other.asInfo(); std::is_neq(res)) @@ -73,8 +73,8 @@ reduceSymbolIDs( void reduceNames( - std::vector& list, - std::vector&& otherList) + std::vector& list, + std::vector&& otherList) { for(auto const& id : otherList) { @@ -103,7 +103,7 @@ merge(NamespaceTranche& I, NamespaceTranche&& Other) } void -merge(NamespaceInfo& I, NamespaceInfo&& Other) +merge(NamespaceSymbol& I, NamespaceSymbol&& Other) { MRDOCS_ASSERT(canMerge(I, Other)); merge(I.asInfo(), std::move(Other.asInfo())); @@ -112,5 +112,5 @@ merge(NamespaceInfo& I, NamespaceInfo&& Other) I.IsInline |= Other.IsInline; I.IsAnonymous |= Other.IsAnonymous; } -} // clang::mrdocs +} // mrdocs diff --git a/src/lib/Metadata/Info/NamespaceAlias.cpp b/src/lib/Metadata/Symbol/NamespaceAlias.cpp similarity index 60% rename from src/lib/Metadata/Info/NamespaceAlias.cpp rename to src/lib/Metadata/Symbol/NamespaceAlias.cpp index a8afd5897..379c6cf94 100644 --- a/src/lib/Metadata/Info/NamespaceAlias.cpp +++ b/src/lib/Metadata/Symbol/NamespaceAlias.cpp @@ -9,22 +9,26 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include #include +#include #include -namespace clang::mrdocs { +namespace mrdocs { void -merge(NamespaceAliasInfo& I, NamespaceAliasInfo&& Other) +merge(NamespaceAliasSymbol& I, NamespaceAliasSymbol&& Other) { MRDOCS_ASSERT(canMerge(I, Other)); merge(I.asInfo(), std::move(Other.asInfo())); - if (!I.AliasedSymbol) + if (I.AliasedSymbol.Identifier.empty()) { - I.AliasedSymbol = std::move(Other.AliasedSymbol); + I.AliasedSymbol.Identifier = std::move(Other.AliasedSymbol.Identifier); + } + if (!I.AliasedSymbol.id) + { + I.AliasedSymbol.id = Other.AliasedSymbol.id; } } -} // clang::mrdocs +} // mrdocs diff --git a/src/lib/Metadata/Info/Overloads.cpp b/src/lib/Metadata/Symbol/Overloads.cpp similarity index 71% rename from src/lib/Metadata/Info/Overloads.cpp rename to src/lib/Metadata/Symbol/Overloads.cpp index fb27ba057..a6b1b4996 100644 --- a/src/lib/Metadata/Info/Overloads.cpp +++ b/src/lib/Metadata/Symbol/Overloads.cpp @@ -8,28 +8,28 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include "lib/Support/Radix.hpp" -#include -#include -#include +#include #include #include #include -#include -#include -#include +#include +#include +#include +#include +#include +#include -namespace clang::mrdocs { +namespace mrdocs { -OverloadsInfo::OverloadsInfo(SymbolID const &Parent, std::string_view Name, +OverloadsSymbol::OverloadsSymbol(SymbolID const &Parent, std::string_view Name, AccessKind access, bool isStatic) noexcept - : InfoCommonBase(SymbolID::createFromString(std::format( + : SymbolCommonBase(SymbolID::createFromString(std::format( "{}-{}-{}-{}", toBase16(Parent), Name, toString(access), isStatic))) { this->Parent = Parent; } void -merge(OverloadsInfo& I, OverloadsInfo&& Other) +merge(OverloadsSymbol& I, OverloadsSymbol&& Other) { merge(I.asInfo(), std::move(Other.asInfo())); namespace stdr = std::ranges; @@ -41,7 +41,7 @@ merge(OverloadsInfo& I, OverloadsInfo&& Other) } void -addMember(OverloadsInfo& I, FunctionInfo const& Member) +addMember(OverloadsSymbol& I, FunctionSymbol const& Member) { if (I.Members.empty()) { @@ -57,11 +57,12 @@ addMember(OverloadsInfo& I, FunctionInfo const& Member) I.Extraction = leastSpecific(I.Extraction, Member.Extraction); if (I.ReturnType != Member.ReturnType) { - I.ReturnType = std::nullopt; + // The return types differ, so we use 'auto' to indicate that. + I.ReturnType = Polymorphic(AutoType{}); } } - merge(I.asSourceInfo(), Member.asSourceInfo()); + merge(I.Loc, Member.Loc); I.Members.push_back(Member.id); } -} // clang::mrdocs +} // mrdocs diff --git a/src/lib/Metadata/Info/Record.cpp b/src/lib/Metadata/Symbol/Record.cpp similarity index 95% rename from src/lib/Metadata/Info/Record.cpp rename to src/lib/Metadata/Symbol/Record.cpp index 2d95ca8ff..75b464128 100644 --- a/src/lib/Metadata/Info/Record.cpp +++ b/src/lib/Metadata/Symbol/Record.cpp @@ -9,10 +9,11 @@ // #include -#include +#include +#include #include -namespace clang::mrdocs { +namespace mrdocs { dom::String toString( @@ -67,8 +68,8 @@ reduceSymbolIDs( } // (anon) std::strong_ordering -RecordInfo:: -operator<=>(RecordInfo const& other) const +RecordSymbol:: +operator<=>(RecordSymbol const& other) const { if (auto const cmp = Name <=> other.Name; !std::is_eq(cmp)) @@ -133,7 +134,7 @@ merge(RecordInterface& I, RecordInterface&& Other) } void -merge(RecordInfo& I, RecordInfo&& Other) +merge(RecordSymbol& I, RecordSymbol&& Other) { merge(I.asInfo(), std::move(Other.asInfo())); if (Other.KeyKind != RecordKeyKind::Struct && @@ -170,7 +171,6 @@ tag_invoke( io.map("isPrivate", I.Access == AccessKind::Private); io.map("isVirtual", I.IsVirtual); io.map("type", dom::ValueFrom(I.Type, domCorpus)); - io.map("symbol", I.Type->namedSymbol()); } void @@ -183,4 +183,4 @@ tag_invoke( v = dom::LazyObject(I, domCorpus); } -} // clang::mrdocs +} // mrdocs diff --git a/src/lib/Metadata/Info/Typedef.cpp b/src/lib/Metadata/Symbol/Typedef.cpp similarity index 83% rename from src/lib/Metadata/Info/Typedef.cpp rename to src/lib/Metadata/Symbol/Typedef.cpp index 47f13cd2b..e6d798e8f 100644 --- a/src/lib/Metadata/Info/Typedef.cpp +++ b/src/lib/Metadata/Symbol/Typedef.cpp @@ -9,16 +9,16 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include -#include #include +#include +#include #include -namespace clang::mrdocs { +namespace mrdocs { std::strong_ordering -TypedefInfo:: -operator<=>(TypedefInfo const& other) const +TypedefSymbol:: +operator<=>(TypedefSymbol const& other) const { if (auto const cmp = Name <=> other.Name; !std::is_eq(cmp)) @@ -59,7 +59,7 @@ operator<=>(TypedefInfo const& other) const return this->asInfo() <=> other.asInfo(); } -void merge(TypedefInfo& I, TypedefInfo&& Other) +void merge(TypedefSymbol& I, TypedefSymbol&& Other) { MRDOCS_ASSERT(canMerge(I, Other)); merge(I.asInfo(), std::move(Other.asInfo())); @@ -67,7 +67,9 @@ void merge(TypedefInfo& I, TypedefInfo&& Other) { I.IsUsing = Other.IsUsing; } - if (!I.Type) + MRDOCS_ASSERT(!I.Type.valueless_after_move()); + if (I.Type->isNamed() && + I.Type->asNamed().Name->Identifier.empty()) { I.Type = std::move(Other.Type); } @@ -77,5 +79,5 @@ void merge(TypedefInfo& I, TypedefInfo&& Other) } } -} // clang::mrdocs +} // mrdocs diff --git a/src/lib/Metadata/Info/Using.cpp b/src/lib/Metadata/Symbol/Using.cpp similarity index 82% rename from src/lib/Metadata/Info/Using.cpp rename to src/lib/Metadata/Symbol/Using.cpp index 583d7f44d..f4ad51aa4 100644 --- a/src/lib/Metadata/Info/Using.cpp +++ b/src/lib/Metadata/Symbol/Using.cpp @@ -9,11 +9,11 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include #include +#include #include -namespace clang::mrdocs { +namespace mrdocs { namespace { @@ -35,7 +35,7 @@ reduceSymbolIDs( } // (anon) void -merge(UsingInfo& I, UsingInfo&& Other) +merge(UsingSymbol& I, UsingSymbol&& Other) { MRDOCS_ASSERT(canMerge(I, Other)); merge(I.asInfo(), std::move(Other.asInfo())); @@ -44,11 +44,12 @@ merge(UsingInfo& I, UsingInfo&& Other) { I.Class = Other.Class; } - if (!I.IntroducedName) + MRDOCS_ASSERT(!I.IntroducedName.valueless_after_move()); + if (I.IntroducedName->Identifier.empty()) { I.IntroducedName = std::move(Other.IntroducedName); } } -} // clang::mrdocs +} // mrdocs diff --git a/src/lib/Metadata/Info/Variable.cpp b/src/lib/Metadata/Symbol/Variable.cpp similarity index 88% rename from src/lib/Metadata/Info/Variable.cpp rename to src/lib/Metadata/Symbol/Variable.cpp index 76474bcaf..8251ff1e9 100644 --- a/src/lib/Metadata/Info/Variable.cpp +++ b/src/lib/Metadata/Symbol/Variable.cpp @@ -9,15 +9,15 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include #include +#include #include -namespace clang::mrdocs { +namespace mrdocs { std::strong_ordering -VariableInfo:: -operator<=>(VariableInfo const& other) const +VariableSymbol:: +operator<=>(VariableSymbol const& other) const { if (auto const cmp = Name <=> other.Name; !std::is_eq(cmp)) @@ -59,11 +59,13 @@ operator<=>(VariableInfo const& other) const } void -merge(VariableInfo& I, VariableInfo&& Other) +merge(VariableSymbol& I, VariableSymbol&& Other) { MRDOCS_ASSERT(canMerge(I, Other)); merge(I.asInfo(), std::move(Other.asInfo())); - if (!I.Type) + MRDOCS_ASSERT(!I.Type.valueless_after_move()); + if (I.Type->isNamed() && + I.Type->asNamed().Name->Identifier.empty()) { I.Type = std::move(Other.Type); } @@ -99,5 +101,5 @@ merge(VariableInfo& I, VariableInfo&& Other) I.HasNoUniqueAddress |= Other.HasNoUniqueAddress; } -} // clang::mrdocs +} // mrdocs diff --git a/src/lib/Metadata/SymbolID.cpp b/src/lib/Metadata/SymbolID.cpp index d9aca9eda..5eb5a3349 100644 --- a/src/lib/Metadata/SymbolID.cpp +++ b/src/lib/Metadata/SymbolID.cpp @@ -9,15 +9,15 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include +#include #include #include -#include +#include +#include #include #include -#include -namespace clang { + namespace mrdocs { // Better have 8 bits per byte, otherwise @@ -156,4 +156,4 @@ tag_invoke( } // mrdocs -} // clang + diff --git a/src/lib/Metadata/InfoSet.cpp b/src/lib/Metadata/SymbolSet.cpp similarity index 61% rename from src/lib/Metadata/InfoSet.cpp rename to src/lib/Metadata/SymbolSet.cpp index 23b1a2f35..05acc9110 100644 --- a/src/lib/Metadata/InfoSet.cpp +++ b/src/lib/Metadata/SymbolSet.cpp @@ -1,12 +1,12 @@ -#include "InfoSet.hpp" +#include "SymbolSet.hpp" -namespace clang::mrdocs { +namespace mrdocs { std::size_t -InfoPtrHasher:: +SymbolPtrHasher:: operator()( - const std::unique_ptr& I) const + const std::unique_ptr& I) const { // The info set should never contain nullptrs MRDOCS_ASSERT(I); @@ -14,7 +14,7 @@ operator()( } std::size_t -InfoPtrHasher:: +SymbolPtrHasher:: operator()( SymbolID const& id) const { @@ -22,10 +22,10 @@ operator()( } bool -InfoPtrEqual:: +SymbolPtrEqual:: operator()( - const std::unique_ptr& a, - const std::unique_ptr& b) const + const std::unique_ptr& a, + const std::unique_ptr& b) const { MRDOCS_ASSERT(a && b); if(a == b) @@ -36,9 +36,9 @@ operator()( } bool -InfoPtrEqual:: +SymbolPtrEqual:: operator()( - const std::unique_ptr& a, + const std::unique_ptr& a, SymbolID const& b) const { MRDOCS_ASSERT(a); @@ -46,13 +46,13 @@ operator()( } bool -InfoPtrEqual:: +SymbolPtrEqual:: operator()( SymbolID const& a, - const std::unique_ptr& b) const + const std::unique_ptr& b) const { MRDOCS_ASSERT(b); return b->id == a; } -} // clang::mrdocs +} // mrdocs diff --git a/src/lib/Metadata/SymbolSet.hpp b/src/lib/Metadata/SymbolSet.hpp new file mode 100644 index 000000000..3e0b7e4fb --- /dev/null +++ b/src/lib/Metadata/SymbolSet.hpp @@ -0,0 +1,184 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_LIB_METADATA_SYMBOLSET_HPP +#define MRDOCS_LIB_METADATA_SYMBOLSET_HPP + +#include +#include +#include +#include + +namespace mrdocs { + +/** A hash function for Symbol pointers. + + The generated hash is based on the SymbolID + of the Symbol object. + + This hasher is used to implement the SymbolSet. +*/ +struct SymbolPtrHasher +{ + using is_transparent = void; + + /** Returns the hash of the Symbol object. + + The hash is based on the SymbolID of the Symbol object. + + @param I The Symbol object to hash. + */ + std::size_t + operator()( + const std::unique_ptr& I) const; + + /** Returns the hash of the SymbolID. + + The function allows the SymbolID to be used as a key + in an unordered container. + + @param id The SymbolID to hash. + */ + std::size_t + operator()( + SymbolID const& id) const; +}; + +/** Equality comparison for Symbol pointers. + + The comparison is based on the SymbolID of the Symbol object. + + This equality comparison is used to implement the SymbolSet. +*/ +struct SymbolPtrEqual +{ + using is_transparent = void; + + /** Returns `true` if the Symbol objects are equal. + + The comparison is based on the SymbolID of the Symbol object. + + @param a The first Symbol object to compare. + @param b The second Symbol object to compare. + */ + bool + operator()( + const std::unique_ptr& a, + const std::unique_ptr& b) const; + + /** Returns `true` if the id of the Symbol object is equal to the SymbolID. + + The comparison is based on the SymbolID of the Symbol object. + + The function allows the SymbolID to be used as a key + in an unordered container. + + @param a The Symbol object to compare. + @param b The SymbolID to compare. + */ + bool + operator()( + const std::unique_ptr& a, + SymbolID const& b) const; + + /** Returns `true` if the SymbolID is equal to the id of the Symbol object. + + The comparison is based on the SymbolID of the Symbol object. + + The function allows the SymbolID to be used as a key + in an unordered container. + + @param a The SymbolID to compare. + @param b The Symbol object to compare. + */ + bool + operator()( + SymbolID const& a, + const std::unique_ptr& b) const; +}; + +/** A set of Symbol objects. + + This set is used to store the results of the execution + of a tool at the end of the processing. + + This is a set of unique pointers to Symbol objects. +*/ +using SymbolSet = std::unordered_set< + std::unique_ptr, SymbolPtrHasher, SymbolPtrEqual>; + +struct UndocumentedSymbol final { + SymbolID id; + std::string name; + SymbolKind kind; + SourceInfo Loc; + + constexpr + UndocumentedSymbol( + SymbolID id_, + std::string name_, + SymbolKind kind_) noexcept + : id(id_) + , name(std::move(name_)) + , kind(kind_) + { + } +}; + +struct UndocumentedSymbolHasher { + using is_transparent = void; + + std::size_t + operator()(SymbolID const& I) const { + return std::hash()(I); + } + + std::size_t + operator()(UndocumentedSymbol const& I) const { + return std::hash()(I.id); + } +}; + +struct UndocumentedSymbolEqual { + using is_transparent = void; + + bool + operator()( + UndocumentedSymbol const& a, + UndocumentedSymbol const& b) const + { + return a.id == b.id; + } + + bool + operator()( + UndocumentedSymbol const& a, + SymbolID const& b) const + { + return a.id == b; + } + + bool + operator()( + SymbolID const& a, + UndocumentedSymbol const& b) const + { + return a == b.id; + } +}; + +using UndocumentedSymbolSet = std::unordered_set< + UndocumentedSymbol, UndocumentedSymbolHasher, UndocumentedSymbolEqual>; + +} // mrdocs + +#endif // MRDOCS_LIB_METADATA_SYMBOLSET_HPP diff --git a/src/lib/Metadata/Template.cpp b/src/lib/Metadata/Template.cpp index 51cd3d15d..b989632e7 100644 --- a/src/lib/Metadata/Template.cpp +++ b/src/lib/Metadata/Template.cpp @@ -16,7 +16,7 @@ #include #include -namespace clang::mrdocs { +namespace mrdocs { std::string_view toString(TArgKind kind) noexcept @@ -25,8 +25,8 @@ toString(TArgKind kind) noexcept { case TArgKind::Type: return "type"; - case TArgKind::NonType: - return "non-type"; + case TArgKind::Constant: + return "constant"; case TArgKind::Template: return "template"; default: @@ -42,8 +42,8 @@ toString( { case TParamKind::Type: return "type"; - case TParamKind::NonType: - return "non-type"; + case TParamKind::Constant: + return "constant"; case TParamKind::Template: return "template"; default: @@ -73,16 +73,13 @@ toString( std::strong_ordering operator<=>(Polymorphic const& lhs, Polymorphic const& rhs) { - if (lhs && rhs) + MRDOCS_ASSERT(!lhs.valueless_after_move()); + MRDOCS_ASSERT(!rhs.valueless_after_move()); + if (lhs->Kind == rhs->Kind) { - if (lhs->Kind == rhs->Kind) - { - return visit(*lhs, detail::VisitCompareFn(*rhs)); - } - return lhs->Kind <=> rhs->Kind; + return visit(*lhs, detail::VisitCompareFn(*rhs)); } - return !lhs ? std::strong_ordering::less - : std::strong_ordering::greater; + return lhs->Kind <=> rhs->Kind; } std::string_view @@ -105,16 +102,13 @@ toString( std::strong_ordering operator<=>(Polymorphic const& lhs, Polymorphic const& rhs) { - if (lhs && rhs) + MRDOCS_ASSERT(!lhs.valueless_after_move()); + MRDOCS_ASSERT(!rhs.valueless_after_move()); + if (lhs->Kind == rhs->Kind) { - if (lhs->Kind == rhs->Kind) - { - return visit(*lhs, detail::VisitCompareFn(*rhs)); - } - return lhs->Kind <=> rhs->Kind; + return visit(*lhs, detail::VisitCompareFn(*rhs)); } - return !lhs ? std::strong_ordering::less - : std::strong_ordering::greater; + return lhs->Kind <=> rhs->Kind; } std::string @@ -127,10 +121,10 @@ toString( std::string result; if constexpr(T::isType()) { - if(t.Type) - result += toString(*t.Type); + MRDOCS_ASSERT(!t.Type.valueless_after_move()); + result += toString(*t.Type); } - if constexpr(T::isNonType()) + if constexpr(T::isConstant()) { result += t.Value.Written; } @@ -160,7 +154,7 @@ tag_invoke( { io.map("type", t.Type); } - if constexpr(T::isNonType()) + if constexpr(T::isConstant()) { io.map("value", t.Value.Written); } @@ -188,8 +182,8 @@ TypeTParam:: operator<=>(TypeTParam const&) const = default; std::strong_ordering -NonTypeTParam:: -operator<=>(NonTypeTParam const&) const = default; +ConstantTParam:: +operator<=>(ConstantTParam const&) const = default; std::strong_ordering TemplateTParam:: @@ -225,7 +219,7 @@ tag_invoke( visit(I, [domCorpus, &io](T const& t) { if(t.Default) { - io.map("default", t.Default); + io.map("default", **t.Default); } if constexpr(T::isType()) { @@ -235,7 +229,7 @@ tag_invoke( io.map("constraint", t.Constraint); } } - if constexpr(T::isNonType()) + if constexpr(T::isConstant()) { io.map("type", t.Type); } @@ -279,7 +273,8 @@ merge(TemplateInfo& I, TemplateInfo&& Other) std::size_t const pn = std::min(I.Params.size(), Other.Params.size()); for (std::size_t i = 0; i < pn; ++i) { - if (!I.Params[i] || I.Params[i]->Kind != Other.Params[i]->Kind) + MRDOCS_ASSERT(!I.Params[i].valueless_after_move()); + if (I.Params[i]->Kind != Other.Params[i]->Kind) { I.Params[i] = std::move(Other.Params[i]); } @@ -306,7 +301,8 @@ merge(TemplateInfo& I, TemplateInfo&& Other) std::size_t const an = std::min(I.Args.size(), Other.Args.size()); for (std::size_t i = 0; i < an; ++i) { - if (!I.Args[i] || I.Args[i]->Kind != Other.Args[i]->Kind) + MRDOCS_ASSERT(!I.Args[i].valueless_after_move()); + if (I.Args[i]->Kind != Other.Args[i]->Kind) { I.Args[i] = std::move(Other.Args[i]); } @@ -360,4 +356,4 @@ tag_invoke( v = dom::LazyObject(I, domCorpus); } -} // clang::mrdocs +} // mrdocs diff --git a/src/lib/Metadata/Type.cpp b/src/lib/Metadata/Type.cpp index 0a9d2c262..6e8e48f0b 100644 --- a/src/lib/Metadata/Type.cpp +++ b/src/lib/Metadata/Type.cpp @@ -12,8 +12,9 @@ #include #include #include +#include -namespace clang::mrdocs { +namespace mrdocs { dom::String toString( @@ -79,18 +80,16 @@ toString( } SymbolID -TypeInfo:: +Type:: namedSymbol() const noexcept { if (!isNamed()) { return SymbolID::invalid; } - auto const* NT = dynamic_cast(this); - if (!NT->Name) - { - return SymbolID::invalid; - } + auto const* NT = this->asNamedPtr(); + MRDOCS_ASSERT(NT); + MRDOCS_ASSERT(!NT->Name.valueless_after_move()); return NT->Name->id; } @@ -147,7 +146,7 @@ operator()( auto& write, std::bool_constant) const { - if (TypeInfo const* inner = innerTypePtr(t)) + if (Type const* inner = innerTypePtr(t)) { visit(*inner, *this, write, std::bool_constant(NamedTypeInfo const& other) const +NamedType:: +operator<=>(NamedType const& other) const { - if (auto const br = dynamic_cast(*this) <=> dynamic_cast(other); + if (auto const br = this->asType() <=> other.asType(); !std::is_eq(br)) { return br; @@ -339,34 +338,33 @@ operator<=>(NamedTypeInfo const& other) const } std::strong_ordering -AutoTypeInfo:: -operator<=>(AutoTypeInfo const&) const = default; +AutoType:: +operator<=>(AutoType const&) const = default; std::strong_ordering -LValueReferenceTypeInfo:: -operator<=>(LValueReferenceTypeInfo const&) const = default; +LValueReferenceType:: +operator<=>(LValueReferenceType const&) const = default; std::strong_ordering -RValueReferenceTypeInfo:: -operator<=>(RValueReferenceTypeInfo const&) const = default; +RValueReferenceType:: +operator<=>(RValueReferenceType const&) const = default; std::strong_ordering -PointerTypeInfo:: -operator<=>(PointerTypeInfo const&) const = default; +PointerType:: +operator<=>(PointerType const&) const = default; std::strong_ordering -MemberPointerTypeInfo:: -operator<=>(MemberPointerTypeInfo const&) const = default; +MemberPointerType:: +operator<=>(MemberPointerType const&) const = default; std::strong_ordering -ArrayTypeInfo:: -operator<=>(ArrayTypeInfo const&) const = default; +ArrayType:: +operator<=>(ArrayType const&) const = default; std::strong_ordering -FunctionTypeInfo:: -operator<=>(FunctionTypeInfo const& other) const { - if (auto const r = dynamic_cast(*this) <=> - dynamic_cast(other); +FunctionType:: +operator<=>(FunctionType const& other) const { + if (auto const r = this->asType() <=> other.asType(); !std::is_eq(r)) { return r; @@ -395,8 +393,7 @@ operator<=>(FunctionTypeInfo const& other) const { std::string -toString( - TypeInfo const& T, +toString(Type const& T, std::string_view Name) { auto write = [result = std::string()]( @@ -419,7 +416,7 @@ void tag_invoke( dom::LazyObjectMapTag, IO& io, - TypeInfo const& I, + Type const& I, DomCorpus const* domCorpus) { io.map("class", std::string("type")); @@ -477,7 +474,7 @@ void tag_invoke( dom::ValueFromTag, dom::Value& v, - TypeInfo const& I, + Type const& I, DomCorpus const* domCorpus) { v = dom::LazyObject(I, domCorpus); @@ -754,46 +751,46 @@ makeChar(FundamentalTypeKind& kind) noexcept } std::strong_ordering -operator<=>(Polymorphic const& lhs, Polymorphic const& rhs) +operator<=>(Polymorphic const& lhs, Polymorphic const& rhs) { - if (lhs && rhs) + MRDOCS_ASSERT(!lhs.valueless_after_move()); + MRDOCS_ASSERT(!rhs.valueless_after_move()); + auto& lhsRef = *lhs; + auto& rhsRef = *rhs; + if (lhsRef.Kind == rhsRef.Kind) { - auto& lhsRef = *lhs; - auto& rhsRef = *rhs; - if (lhsRef.Kind == rhsRef.Kind) - { - return visit(lhsRef, detail::VisitCompareFn(rhsRef)); - } - return lhsRef.Kind <=> rhsRef.Kind; + return visit(lhsRef, detail::VisitCompareFn(rhsRef)); } - return !lhs ? std::strong_ordering::less - : std::strong_ordering::greater; + return lhsRef.Kind <=> rhsRef.Kind; } namespace { // Get an optional reference to the inner type template < - class TypeInfoTy, - bool isMutable = !std::is_const_v>, - class Ptr = std::conditional_t*, Polymorphic const*>, - class Ref = std::conditional_t>, std::reference_wrapper const>>> -requires std::same_as, TypeInfo> -std::optional -innerTypeImpl(TypeInfoTy&& TI) noexcept + class TypeTy, + bool isMutable = !std::is_const_v>, + class Ptr = std::conditional_t*, Polymorphic const*>, + class Ref = std::conditional_t&, Polymorphic const&>> +requires std::same_as, Type> +Optional +innerTypeImpl(TypeTy&& TI) noexcept { // Get a pointer to the inner type Ptr innerPtr = visit(TI, [](T& t) -> Ptr { if constexpr(requires { t.PointeeType; }) { + MRDOCS_ASSERT(!t.PointeeType.valueless_after_move()); return &t.PointeeType; } if constexpr(requires { t.ElementType; }) { + MRDOCS_ASSERT(!t.ElementType.valueless_after_move()); return &t.ElementType; } if constexpr(requires { t.ReturnType; }) { + MRDOCS_ASSERT(!t.ReturnType.valueless_after_move()); return &t.ReturnType; } return nullptr; @@ -814,85 +811,92 @@ innerTypeImpl(TypeInfoTy&& TI) noexcept } // Get a pointer to the inner type -template -auto -innerTypePtrImpl(TypeInfoTy&& TI) noexcept +template < + class TypeTy, + bool isMutable = !std::is_const_v>, + class Ptr = std::conditional_t*, Polymorphic const*>, + class Ref = std::conditional_t&, Polymorphic const&>, + class InnerPtr = std::conditional_t> +requires std::same_as, Type> +InnerPtr +innerTypePtrImpl(TypeTy&& TI) noexcept { - auto res = innerTypeImpl(TI); + Optional res = innerTypeImpl(TI); if (res) { - auto& ref = res->get(); - return &*ref; + MRDOCS_ASSERT(!res->valueless_after_move()); + return &**res; } - return decltype(&*res->get())(nullptr); + return nullptr; } // Get the innermost type // If there's an internal type, return it // If there's no internal type, return the current type -template -requires std::same_as, Polymorphic> -auto& -innermostTypeImpl(PolymorphicTypeInfoTy&& TI) noexcept +template < + class PolymorphicTypeTy, + bool isMutable = !std::is_const_v>, + class Ref = std::conditional_t&, Polymorphic const&>> +requires std::same_as, Polymorphic> +Ref +innermostTypeImpl(PolymorphicTypeTy&& TI) noexcept { - if (!TI) - { - return TI; - } - /* optional */ auto inner = innerTypeImpl(*TI); + MRDOCS_ASSERT(!TI.valueless_after_move()); + Optional inner = innerTypeImpl(*TI); if (!inner) { return TI; } while (inner) { - /* polymorphic */ auto& ref = inner->get(); - if (!ref || - ref->isNamed()) + Ref ref = *inner; + MRDOCS_ASSERT(!ref.valueless_after_move()); + if (ref->isNamed()) { return ref; } inner = innerTypeImpl(*ref); } - return inner->get(); + Ref ref = *inner; + return ref; } } -std::optional const>> -innerType(TypeInfo const& TI) noexcept +Optional const&> +innerType(Type const& TI) noexcept { - return innerTypeImpl(TI); + return innerTypeImpl(TI); } -std::optional>> -innerType(TypeInfo& TI) noexcept +Optional&> +innerType(Type& TI) noexcept { - return innerTypeImpl(TI); + return innerTypeImpl(TI); } -TypeInfo const* -innerTypePtr(TypeInfo const& TI) noexcept +Type const* +innerTypePtr(Type const& TI) noexcept { - return innerTypePtrImpl(TI); + return innerTypePtrImpl(TI); } -TypeInfo* -innerTypePtr(TypeInfo& TI) noexcept +Type* +innerTypePtr(Type& TI) noexcept { - return innerTypePtrImpl(TI); + return innerTypePtrImpl(TI); } -Polymorphic const& -innermostType(Polymorphic const& TI) noexcept +Polymorphic const& +innermostType(Polymorphic const& TI) noexcept { - return innermostTypeImpl(TI); + return innermostTypeImpl const&>(TI); } -Polymorphic& -innermostType(Polymorphic& TI) noexcept +Polymorphic& +innermostType(Polymorphic& TI) noexcept { - return innermostTypeImpl(TI); + return innermostTypeImpl&>(TI); } -} // clang::mrdocs +} // mrdocs diff --git a/src/lib/MrDocsCompilationDatabase.cpp b/src/lib/MrDocsCompilationDatabase.cpp index e565db784..7e6972746 100644 --- a/src/lib/MrDocsCompilationDatabase.cpp +++ b/src/lib/MrDocsCompilationDatabase.cpp @@ -10,24 +10,24 @@ // #include "MrDocsCompilationDatabase.hpp" -#include "lib/ConfigImpl.hpp" -#include "lib/Support/Debug.hpp" -#include "lib/Support/ExecuteAndWaitWithLogging.hpp" -#include "lib/Support/Path.hpp" +#include +#include +#include +#include +#include #include #include #include #include -#include #include #include #include #include #include -#include +#include #include -namespace clang { + namespace mrdocs { static @@ -35,9 +35,9 @@ bool isCXXSrcFile( std::string_view filename) { - StringRef ext = llvm::sys::path::extension(filename).drop_front(); - driver::types::ID extensionId = driver::types::lookupTypeForExtension(ext); - return driver::types::isCXX(extensionId); + llvm::StringRef ext = llvm::sys::path::extension(filename).drop_front(); + clang::driver::types::ID extensionId = clang::driver::types::lookupTypeForExtension(ext); + return clang::driver::types::isCXX(extensionId); } static @@ -45,7 +45,7 @@ bool isCXXHeaderFile( std::string_view filename) { - StringRef ext = llvm::sys::path::extension(filename).drop_front(); + llvm::StringRef ext = llvm::sys::path::extension(filename).drop_front(); return ext == "hpp" || ext == "hh" || ext == "hxx" || ext == "h++"; } @@ -54,7 +54,7 @@ bool isCSrcFile( std::string_view filename) { - StringRef ext = llvm::sys::path::extension(filename).drop_front(); + llvm::StringRef ext = llvm::sys::path::extension(filename).drop_front(); return ext == "c"; } @@ -63,7 +63,7 @@ bool isCHeaderFile( std::string_view filename) { - StringRef ext = llvm::sys::path::extension(filename).drop_front(); + llvm::StringRef ext = llvm::sys::path::extension(filename).drop_front(); return ext == "h"; } @@ -95,153 +95,153 @@ isValidMrDocsOption( if (optionMatchesAny(opt, // unknown options - driver::options::OPT_UNKNOWN, + clang::driver::options::OPT_UNKNOWN, // sanitizers - driver::options::OPT_fsanitize_EQ, - driver::options::OPT_fno_sanitize_EQ, - driver::options::OPT_fsanitize_recover_EQ, - driver::options::OPT_fno_sanitize_recover_EQ, - driver::options::OPT_fsanitize_trap_EQ, - driver::options::OPT_fno_sanitize_trap_EQ, - driver::options::OPT_fsanitize_address_use_after_scope, - driver::options::OPT_fexperimental_sanitize_metadata_ignorelist_EQ, - driver::options::OPT_fexperimental_sanitize_metadata_EQ_atomics, - driver::options::OPT_fexperimental_sanitize_metadata_EQ_covered, - driver::options::OPT_fexperimental_sanitize_metadata_EQ, - driver::options::OPT_fgpu_sanitize, - driver::options::OPT_fno_experimental_sanitize_metadata_EQ, - driver::options::OPT_fno_gpu_sanitize, - driver::options::OPT_fno_sanitize_address_globals_dead_stripping, - driver::options::OPT_fno_sanitize_address_outline_instrumentation, - driver::options::OPT_fno_sanitize_address_poison_custom_array_cookie, - driver::options::OPT_fno_sanitize_address_use_after_scope, - driver::options::OPT_fno_sanitize_address_use_odr_indicator, - driver::options::OPT__SLASH_fno_sanitize_address_vcasan_lib, - driver::options::OPT_fno_sanitize_cfi_canonical_jump_tables, - driver::options::OPT_fno_sanitize_cfi_cross_dso, - driver::options::OPT_fno_sanitize_coverage, - driver::options::OPT_fno_sanitize_hwaddress_experimental_aliasing, - driver::options::OPT_fno_sanitize_ignorelist, - driver::options::OPT_fno_sanitize_link_cxx_runtime, - driver::options::OPT_fno_sanitize_link_runtime, - driver::options::OPT_fno_sanitize_memory_param_retval, - driver::options::OPT_fno_sanitize_memory_track_origins, - driver::options::OPT_fno_sanitize_memory_use_after_dtor, - driver::options::OPT_fno_sanitize_minimal_runtime, - driver::options::OPT_fno_sanitize_recover_EQ, - driver::options::OPT_fno_sanitize_recover, - driver::options::OPT_fno_sanitize_stable_abi, - driver::options::OPT_fno_sanitize_stats, - driver::options::OPT_fno_sanitize_thread_atomics, - driver::options::OPT_fno_sanitize_thread_func_entry_exit, - driver::options::OPT_fno_sanitize_thread_memory_access, - driver::options::OPT_fno_sanitize_trap_EQ, - driver::options::OPT_fno_sanitize_trap, - driver::options::OPT_fno_sanitize_undefined_trap_on_error, - driver::options::OPT_fno_sanitize_EQ, - driver::options::OPT_sanitize_address_destructor_EQ, - driver::options::OPT_fsanitize_address_field_padding, - driver::options::OPT_fsanitize_address_globals_dead_stripping, - driver::options::OPT_fsanitize_address_outline_instrumentation, - driver::options::OPT_fsanitize_address_poison_custom_array_cookie, - driver::options::OPT_sanitize_address_use_after_return_EQ, - driver::options::OPT__SLASH_fsanitize_address_use_after_return, - driver::options::OPT_fsanitize_address_use_after_scope, - driver::options::OPT_fsanitize_address_use_odr_indicator, - driver::options::OPT_fsanitize_cfi_canonical_jump_tables, - driver::options::OPT_fsanitize_cfi_cross_dso, - driver::options::OPT_fsanitize_cfi_icall_normalize_integers, - driver::options::OPT_fsanitize_cfi_icall_generalize_pointers, - driver::options::OPT_fsanitize_coverage_8bit_counters, - driver::options::OPT_fsanitize_coverage_allowlist, - driver::options::OPT_fsanitize_coverage_control_flow, - driver::options::OPT_fsanitize_coverage_ignorelist, - driver::options::OPT_fsanitize_coverage_indirect_calls, - driver::options::OPT_fsanitize_coverage_inline_8bit_counters, - driver::options::OPT_fsanitize_coverage_inline_bool_flag, - driver::options::OPT_fsanitize_coverage_no_prune, - driver::options::OPT_fsanitize_coverage_pc_table, - driver::options::OPT_fsanitize_coverage_stack_depth, - driver::options::OPT_fsanitize_coverage_trace_bb, - driver::options::OPT_fsanitize_coverage_trace_cmp, - driver::options::OPT_fsanitize_coverage_trace_div, - driver::options::OPT_fsanitize_coverage_trace_gep, - driver::options::OPT_fsanitize_coverage_trace_loads, - driver::options::OPT_fsanitize_coverage_trace_pc_guard, - driver::options::OPT_fsanitize_coverage_trace_pc, - driver::options::OPT_fsanitize_coverage_trace_stores, - driver::options::OPT_fsanitize_coverage_type, - driver::options::OPT_fsanitize_coverage, - driver::options::OPT_fsanitize_hwaddress_abi_EQ, - driver::options::OPT_fsanitize_hwaddress_experimental_aliasing, - driver::options::OPT_fsanitize_ignorelist_EQ, - driver::options::OPT_fsanitize_link_cxx_runtime, - driver::options::OPT_fsanitize_link_runtime, - driver::options::OPT_fsanitize_memory_param_retval, - driver::options::OPT_fsanitize_memory_track_origins_EQ, - driver::options::OPT_fsanitize_memory_track_origins, - driver::options::OPT_fsanitize_memory_use_after_dtor, - driver::options::OPT_fsanitize_memtag_mode_EQ, - driver::options::OPT_fsanitize_minimal_runtime, - driver::options::OPT_fsanitize_recover_EQ, - driver::options::OPT_fsanitize_recover, - driver::options::OPT_fsanitize_stable_abi, - driver::options::OPT_fsanitize_stats, - driver::options::OPT_fsanitize_system_ignorelist_EQ, - driver::options::OPT_fsanitize_thread_atomics, - driver::options::OPT_fsanitize_thread_func_entry_exit, - driver::options::OPT_fsanitize_thread_memory_access, - driver::options::OPT_fsanitize_trap_EQ, - driver::options::OPT_fsanitize_trap, - driver::options::OPT_fsanitize_undefined_strip_path_components_EQ, - driver::options::OPT_fsanitize_undefined_trap_on_error, - driver::options::OPT__SLASH_fsanitize_EQ_address, - driver::options::OPT_fsanitize_EQ, - driver::options::OPT_shared_libsan, - driver::options::OPT_static_libsan, - driver::options::OPT_static_libsan, + clang::driver::options::OPT_fsanitize_EQ, + clang::driver::options::OPT_fno_sanitize_EQ, + clang::driver::options::OPT_fsanitize_recover_EQ, + clang::driver::options::OPT_fno_sanitize_recover_EQ, + clang::driver::options::OPT_fsanitize_trap_EQ, + clang::driver::options::OPT_fno_sanitize_trap_EQ, + clang::driver::options::OPT_fsanitize_address_use_after_scope, + clang::driver::options::OPT_fexperimental_sanitize_metadata_ignorelist_EQ, + clang::driver::options::OPT_fexperimental_sanitize_metadata_EQ_atomics, + clang::driver::options::OPT_fexperimental_sanitize_metadata_EQ_covered, + clang::driver::options::OPT_fexperimental_sanitize_metadata_EQ, + clang::driver::options::OPT_fgpu_sanitize, + clang::driver::options::OPT_fno_experimental_sanitize_metadata_EQ, + clang::driver::options::OPT_fno_gpu_sanitize, + clang::driver::options::OPT_fno_sanitize_address_globals_dead_stripping, + clang::driver::options::OPT_fno_sanitize_address_outline_instrumentation, + clang::driver::options::OPT_fno_sanitize_address_poison_custom_array_cookie, + clang::driver::options::OPT_fno_sanitize_address_use_after_scope, + clang::driver::options::OPT_fno_sanitize_address_use_odr_indicator, + clang::driver::options::OPT__SLASH_fno_sanitize_address_vcasan_lib, + clang::driver::options::OPT_fno_sanitize_cfi_canonical_jump_tables, + clang::driver::options::OPT_fno_sanitize_cfi_cross_dso, + clang::driver::options::OPT_fno_sanitize_coverage, + clang::driver::options::OPT_fno_sanitize_hwaddress_experimental_aliasing, + clang::driver::options::OPT_fno_sanitize_ignorelist, + clang::driver::options::OPT_fno_sanitize_link_cxx_runtime, + clang::driver::options::OPT_fno_sanitize_link_runtime, + clang::driver::options::OPT_fno_sanitize_memory_param_retval, + clang::driver::options::OPT_fno_sanitize_memory_track_origins, + clang::driver::options::OPT_fno_sanitize_memory_use_after_dtor, + clang::driver::options::OPT_fno_sanitize_minimal_runtime, + clang::driver::options::OPT_fno_sanitize_recover_EQ, + clang::driver::options::OPT_fno_sanitize_recover, + clang::driver::options::OPT_fno_sanitize_stable_abi, + clang::driver::options::OPT_fno_sanitize_stats, + clang::driver::options::OPT_fno_sanitize_thread_atomics, + clang::driver::options::OPT_fno_sanitize_thread_func_entry_exit, + clang::driver::options::OPT_fno_sanitize_thread_memory_access, + clang::driver::options::OPT_fno_sanitize_trap_EQ, + clang::driver::options::OPT_fno_sanitize_trap, + clang::driver::options::OPT_fno_sanitize_undefined_trap_on_error, + clang::driver::options::OPT_fno_sanitize_EQ, + clang::driver::options::OPT_sanitize_address_destructor_EQ, + clang::driver::options::OPT_fsanitize_address_field_padding, + clang::driver::options::OPT_fsanitize_address_globals_dead_stripping, + clang::driver::options::OPT_fsanitize_address_outline_instrumentation, + clang::driver::options::OPT_fsanitize_address_poison_custom_array_cookie, + clang::driver::options::OPT_sanitize_address_use_after_return_EQ, + clang::driver::options::OPT__SLASH_fsanitize_address_use_after_return, + clang::driver::options::OPT_fsanitize_address_use_after_scope, + clang::driver::options::OPT_fsanitize_address_use_odr_indicator, + clang::driver::options::OPT_fsanitize_cfi_canonical_jump_tables, + clang::driver::options::OPT_fsanitize_cfi_cross_dso, + clang::driver::options::OPT_fsanitize_cfi_icall_normalize_integers, + clang::driver::options::OPT_fsanitize_cfi_icall_generalize_pointers, + clang::driver::options::OPT_fsanitize_coverage_8bit_counters, + clang::driver::options::OPT_fsanitize_coverage_allowlist, + clang::driver::options::OPT_fsanitize_coverage_control_flow, + clang::driver::options::OPT_fsanitize_coverage_ignorelist, + clang::driver::options::OPT_fsanitize_coverage_indirect_calls, + clang::driver::options::OPT_fsanitize_coverage_inline_8bit_counters, + clang::driver::options::OPT_fsanitize_coverage_inline_bool_flag, + clang::driver::options::OPT_fsanitize_coverage_no_prune, + clang::driver::options::OPT_fsanitize_coverage_pc_table, + clang::driver::options::OPT_fsanitize_coverage_stack_depth, + clang::driver::options::OPT_fsanitize_coverage_trace_bb, + clang::driver::options::OPT_fsanitize_coverage_trace_cmp, + clang::driver::options::OPT_fsanitize_coverage_trace_div, + clang::driver::options::OPT_fsanitize_coverage_trace_gep, + clang::driver::options::OPT_fsanitize_coverage_trace_loads, + clang::driver::options::OPT_fsanitize_coverage_trace_pc_guard, + clang::driver::options::OPT_fsanitize_coverage_trace_pc, + clang::driver::options::OPT_fsanitize_coverage_trace_stores, + clang::driver::options::OPT_fsanitize_coverage_type, + clang::driver::options::OPT_fsanitize_coverage, + clang::driver::options::OPT_fsanitize_hwaddress_abi_EQ, + clang::driver::options::OPT_fsanitize_hwaddress_experimental_aliasing, + clang::driver::options::OPT_fsanitize_ignorelist_EQ, + clang::driver::options::OPT_fsanitize_link_cxx_runtime, + clang::driver::options::OPT_fsanitize_link_runtime, + clang::driver::options::OPT_fsanitize_memory_param_retval, + clang::driver::options::OPT_fsanitize_memory_track_origins_EQ, + clang::driver::options::OPT_fsanitize_memory_track_origins, + clang::driver::options::OPT_fsanitize_memory_use_after_dtor, + clang::driver::options::OPT_fsanitize_memtag_mode_EQ, + clang::driver::options::OPT_fsanitize_minimal_runtime, + clang::driver::options::OPT_fsanitize_recover_EQ, + clang::driver::options::OPT_fsanitize_recover, + clang::driver::options::OPT_fsanitize_stable_abi, + clang::driver::options::OPT_fsanitize_stats, + clang::driver::options::OPT_fsanitize_system_ignorelist_EQ, + clang::driver::options::OPT_fsanitize_thread_atomics, + clang::driver::options::OPT_fsanitize_thread_func_entry_exit, + clang::driver::options::OPT_fsanitize_thread_memory_access, + clang::driver::options::OPT_fsanitize_trap_EQ, + clang::driver::options::OPT_fsanitize_trap, + clang::driver::options::OPT_fsanitize_undefined_strip_path_components_EQ, + clang::driver::options::OPT_fsanitize_undefined_trap_on_error, + clang::driver::options::OPT__SLASH_fsanitize_EQ_address, + clang::driver::options::OPT_fsanitize_EQ, + clang::driver::options::OPT_shared_libsan, + clang::driver::options::OPT_static_libsan, + clang::driver::options::OPT_static_libsan, // diagnostic options - driver::options::OPT_Diag_Group, - driver::options::OPT_W_value_Group, - driver::options::OPT__SLASH_wd, + clang::driver::options::OPT_Diag_Group, + clang::driver::options::OPT_W_value_Group, + clang::driver::options::OPT__SLASH_wd, // language conformance options - driver::options::OPT_pedantic_Group, - driver::options::OPT__SLASH_permissive, - driver::options::OPT__SLASH_permissive_, + clang::driver::options::OPT_pedantic_Group, + clang::driver::options::OPT__SLASH_permissive, + clang::driver::options::OPT__SLASH_permissive_, // ignored options - driver::options::OPT_cl_ignored_Group, - driver::options::OPT_cl_ignored_Group, - driver::options::OPT_clang_ignored_f_Group, - driver::options::OPT_clang_ignored_gcc_optimization_f_Group, - driver::options::OPT_clang_ignored_legacy_options_Group, - driver::options::OPT_clang_ignored_m_Group, - driver::options::OPT_flang_ignored_w_Group + clang::driver::options::OPT_cl_ignored_Group, + clang::driver::options::OPT_cl_ignored_Group, + clang::driver::options::OPT_clang_ignored_f_Group, + clang::driver::options::OPT_clang_ignored_gcc_optimization_f_Group, + clang::driver::options::OPT_clang_ignored_legacy_options_Group, + clang::driver::options::OPT_clang_ignored_m_Group, + clang::driver::options::OPT_flang_ignored_w_Group #if 0 // input file options - driver::options::OPT_INPUT, + clang::driver::options::OPT_INPUT, // output file options - driver::options::OPT_o, - driver::options::OPT__SLASH_o, - driver::options::OPT__SLASH_Fo, - driver::options::OPT__SLASH_Fe, - driver::options::OPT__SLASH_Fd, - driver::options::OPT__SLASH_FA, - driver::options::OPT__SLASH_Fa, - driver::options::OPT__SLASH_Fi, - driver::options::OPT__SLASH_FR, - driver::options::OPT__SLASH_Fr, - driver::options::OPT__SLASH_Fm, - driver::options::OPT__SLASH_Fx, + clang::driver::options::OPT_o, + clang::driver::options::OPT__SLASH_o, + clang::driver::options::OPT__SLASH_Fo, + clang::driver::options::OPT__SLASH_Fe, + clang::driver::options::OPT__SLASH_Fd, + clang::driver::options::OPT__SLASH_FA, + clang::driver::options::OPT__SLASH_Fa, + clang::driver::options::OPT__SLASH_Fi, + clang::driver::options::OPT__SLASH_FR, + clang::driver::options::OPT__SLASH_Fr, + clang::driver::options::OPT__SLASH_Fm, + clang::driver::options::OPT__SLASH_Fx, #endif - // driver::options::OPT__SLASH_TP - // driver::options::OPT__SLASH_Tp - // driver::options::OPT__SLASH_TC - // driver::options::OPT__SLASH_Tc + // clang::driver::options::OPT__SLASH_TP + // clang::driver::options::OPT__SLASH_Tp + // clang::driver::options::OPT__SLASH_TC + // clang::driver::options::OPT__SLASH_Tc )) { return false; @@ -273,7 +273,7 @@ isValidMrDocsOption( static std::vector adjustCommandLine( - StringRef const workingDir, + llvm::StringRef const workingDir, std::vector const& cmdline, std::shared_ptr const& config, std::unordered_map> const& implicitIncludeDirectories, @@ -307,10 +307,10 @@ adjustCommandLine( // command line option formats. The value is deduced from // the `-drive-mode` option or from `progName`. // Common values are "gcc", "g++", "cpp", "cl" and "flang". - StringRef const driver_mode = driver::getDriverMode(progName, cmdLineCStrs); + llvm::StringRef const driver_mode = clang::driver::getDriverMode(progName, cmdLineCStrs); // Identify if we should use "msvc/clang-cl" or "clang/gcc" format // for options. - bool const is_clang_cl = driver::IsClangCL(driver_mode); + bool const is_clang_cl = clang::driver::IsClangCL(driver_mode); // ------------------------------------------------------ // Supress all warnings @@ -502,7 +502,7 @@ adjustCommandLine( // errors. llvm::opt::OptTable const& opts_table = clang::driver::getDriverOptTable(); llvm::opt::Visibility visibility(is_clang_cl ? - driver::options::CLOption : driver::options::ClangOption); + clang::driver::options::CLOption : clang::driver::options::ClangOption); unsigned idx = 1; while (idx < cmdline.size()) { @@ -546,14 +546,14 @@ makeAbsoluteAndNative( MrDocsCompilationDatabase:: MrDocsCompilationDatabase( - StringRef const workingDir, + llvm::StringRef const workingDir, CompilationDatabase const& inner, std::shared_ptr const& config, std::unordered_map> const& implicitIncludeDirectories) { namespace fs = llvm::sys::fs; namespace path = llvm::sys::path; - using tooling::CompileCommand; + using clang::tooling::CompileCommand; std::vector allCommands = inner.getAllCompileCommands(); AllCommands_.reserve(allCommands.size()); @@ -591,7 +591,7 @@ MrDocsCompilationDatabase( } } -std::vector +std::vector MrDocsCompilationDatabase:: getCompileCommands( llvm::StringRef FilePath) const @@ -602,7 +602,7 @@ getCompileCommands( auto const it = IndexByFile_.find(nativeFilePath); if (it == IndexByFile_.end()) return {}; - std::vector Commands; + std::vector Commands; Commands.push_back(AllCommands_[it->getValue()]); return Commands; } @@ -618,7 +618,7 @@ getAllFiles() const return allFiles; } -std::vector +std::vector MrDocsCompilationDatabase:: getAllCompileCommands() const { @@ -626,4 +626,4 @@ getAllCompileCommands() const } } // mrdocs -} // clang + diff --git a/src/lib/MrDocsCompilationDatabase.hpp b/src/lib/MrDocsCompilationDatabase.hpp index 490955ad2..cb45862ea 100644 --- a/src/lib/MrDocsCompilationDatabase.hpp +++ b/src/lib/MrDocsCompilationDatabase.hpp @@ -8,15 +8,15 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_LIB_TOOL_MR_DOCS_COMPILATION_DATABASE_HPP -#define MRDOCS_LIB_TOOL_MR_DOCS_COMPILATION_DATABASE_HPP +#ifndef MRDOCS_LIB_MRDOCSCOMPILATIONDATABASE_HPP +#define MRDOCS_LIB_MRDOCSCOMPILATIONDATABASE_HPP #include #include #include #include -namespace clang { + namespace mrdocs { /** A compilation database for MrDocs. @@ -34,9 +34,9 @@ namespace mrdocs { */ class MrDocsCompilationDatabase - : public tooling::CompilationDatabase + : public clang::tooling::CompilationDatabase { - std::vector AllCommands_; + std::vector AllCommands_; llvm::StringMap IndexByFile_; public: @@ -54,7 +54,7 @@ class MrDocsCompilationDatabase * implicit include directories, as determined by the system's compiler. */ MrDocsCompilationDatabase( - StringRef workingDir, + llvm::StringRef workingDir, CompilationDatabase const& inner, std::shared_ptr const& config, std::unordered_map> const& implicitIncludeDirectories); @@ -63,9 +63,9 @@ class MrDocsCompilationDatabase @return A vector of compile commands. */ - std::vector + std::vector getCompileCommands( - StringRef FilePath) const override; + llvm::StringRef FilePath) const override; /** Get all files in the database. @@ -78,12 +78,12 @@ class MrDocsCompilationDatabase @return A vector of compile commands. */ - std::vector + std::vector getAllCompileCommands() const override; }; } // mrdocs -} // clang -#endif // MRDOCS_LIB_TOOL_MR_DOCS_COMPILATION_DATABASE_HPP + +#endif // MRDOCS_LIB_MRDOCSCOMPILATIONDATABASE_HPP diff --git a/src/lib/MrDocsSettingsDB.cpp b/src/lib/MrDocsSettingsDB.cpp index 5740475ab..d32b0cc6b 100644 --- a/src/lib/MrDocsSettingsDB.cpp +++ b/src/lib/MrDocsSettingsDB.cpp @@ -12,7 +12,7 @@ #include "MrDocsSettingsDB.hpp" #include -namespace clang { + namespace mrdocs { MrDocsSettingsDB::MrDocsSettingsDB(ConfigImpl const& config) @@ -79,10 +79,10 @@ MrDocsSettingsDB::MrDocsSettingsDB(ConfigImpl const& config) } } -std::vector +std::vector MrDocsSettingsDB::getCompileCommands(llvm::StringRef FilePath) const { - std::vector result; + std::vector result; for (auto const& cmd: cc_) { if (cmd.Filename == FilePath) @@ -105,11 +105,10 @@ MrDocsSettingsDB::getAllFiles() const return result; } -std::vector +std::vector MrDocsSettingsDB::getAllCompileCommands() const { return cc_; } } // namespace mrdocs -} // namespace clang diff --git a/src/lib/MrDocsSettingsDB.hpp b/src/lib/MrDocsSettingsDB.hpp index ad3f14fe1..24da5c554 100644 --- a/src/lib/MrDocsSettingsDB.hpp +++ b/src/lib/MrDocsSettingsDB.hpp @@ -11,40 +11,40 @@ #ifndef MRDOCS_LIB_MRDOCSSETTINGSDB_HPP #define MRDOCS_LIB_MRDOCSSETTINGSDB_HPP +#include #include #include -#include "ConfigImpl.hpp" #include #include #include -namespace clang { + namespace mrdocs { /** A compilation database generated from the mrdocs.yml options */ class MrDocsSettingsDB - : public tooling::CompilationDatabase + : public clang::tooling::CompilationDatabase { - std::vector cc_; + std::vector cc_; public: explicit MrDocsSettingsDB( ConfigImpl const& config); - std::vector + std::vector getCompileCommands( llvm::StringRef FilePath) const override; std::vector getAllFiles() const override; - std::vector + std::vector getAllCompileCommands() const override; }; } // mrdocs -} // clang + #endif diff --git a/src/lib/SingleFileDB.hpp b/src/lib/SingleFileDB.hpp index 16af01ed5..5139aba97 100644 --- a/src/lib/SingleFileDB.hpp +++ b/src/lib/SingleFileDB.hpp @@ -17,15 +17,15 @@ #include #include -namespace clang { + namespace mrdocs { /** Compilation database for a single .cpp file. */ class SingleFileDB - : public tooling::CompilationDatabase + : public clang::tooling::CompilationDatabase { - std::vector cc_; + std::vector cc_; public: explicit @@ -50,7 +50,7 @@ class SingleFileDB cc_.back().Heuristic = "unit test"; } - std::vector + std::vector getCompileCommands( llvm::StringRef FilePath) const override { @@ -65,7 +65,7 @@ class SingleFileDB return { cc_.front().Filename }; } - std::vector + std::vector getAllCompileCommands() const override { return { cc_.front() }; @@ -73,6 +73,6 @@ class SingleFileDB }; } // mrdocs -} // clang + #endif diff --git a/src/lib/Support/Assert.cpp b/src/lib/Support/Assert.cpp index d54ba7bac..82666a9c3 100644 --- a/src/lib/Support/Assert.cpp +++ b/src/lib/Support/Assert.cpp @@ -8,16 +8,16 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include -#include #include #include +#include +#include namespace SourceFileNames { extern char const* getFileName(char const*) noexcept; } // SourceFileNames -namespace clang { + namespace mrdocs { void @@ -31,4 +31,4 @@ assert_failed( } } // mrdocs -} // clang + diff --git a/src/lib/Support/CMakeExecution.cpp b/src/lib/Support/CMakeExecution.cpp index ec78d0057..00636d909 100644 --- a/src/lib/Support/CMakeExecution.cpp +++ b/src/lib/Support/CMakeExecution.cpp @@ -10,14 +10,13 @@ #include "CMakeExecution.hpp" #include "ExecuteAndWaitWithLogging.hpp" -#include "lib/Support/Path.hpp" - +#include #include #include #include #include -namespace clang { + namespace mrdocs { namespace { @@ -536,4 +535,4 @@ executeCmakeExportCompileCommands(llvm::StringRef projectPath, llvm::StringRef c } // mrdocs -} // clang + diff --git a/src/lib/Support/CMakeExecution.hpp b/src/lib/Support/CMakeExecution.hpp index b0184f534..9d4439432 100644 --- a/src/lib/Support/CMakeExecution.hpp +++ b/src/lib/Support/CMakeExecution.hpp @@ -8,14 +8,14 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_LIB_TOOL_CMAKE_EXECUTION_HPP -#define MRDOCS_LIB_TOOL_CMAKE_EXECUTION_HPP +#ifndef MRDOCS_LIB_SUPPORT_CMAKEEXECUTION_HPP +#define MRDOCS_LIB_SUPPORT_CMAKEEXECUTION_HPP #include #include #include -namespace clang { + namespace mrdocs { /** @@ -34,7 +34,7 @@ Expected executeCmakeExportCompileCommands(llvm::StringRef projectPath, llvm::StringRef cmakeArgs, llvm::StringRef tempDir); } // mrdocs -} // clang -#endif // MRDOCS_LIB_TOOL_CMAKE_EXECUTION_HPP + +#endif // MRDOCS_LIB_SUPPORT_CMAKEEXECUTION_HPP diff --git a/src/lib/Support/Chrono.hpp b/src/lib/Support/Chrono.hpp index f98ccad67..1ec6a9a1c 100644 --- a/src/lib/Support/Chrono.hpp +++ b/src/lib/Support/Chrono.hpp @@ -17,7 +17,7 @@ #include #include -namespace clang { + namespace mrdocs { /** Formats a duration into a human-readable string. @@ -65,6 +65,6 @@ format_duration( } } // mrdocs -} // clang + #endif diff --git a/src/lib/Support/Debug.cpp b/src/lib/Support/Debug.cpp index f3218166d..d3d1738b7 100644 --- a/src/lib/Support/Debug.cpp +++ b/src/lib/Support/Debug.cpp @@ -9,15 +9,15 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include "lib/Support/Debug.hpp" -#include "lib/Support/Radix.hpp" +#include +#include +#include +#include +#include #include #include -#include -#include -#include -namespace clang { + namespace mrdocs { void @@ -32,16 +32,16 @@ debugEnableHeapChecking() } } // mrdocs -} // clang + std::string -std::formatter::toString(clang::mrdocs::Info const &i) { +std::formatter::toString(mrdocs::Symbol const &i) { std::string str = std::format("Info: kind = {}", i.Kind); if (!i.Name.empty()) { str += std::format(", name = '{}'", i.Name); } str += std::format(", ID = {}", i.id); - clang::mrdocs::SymbolID curParent = i.Parent; + mrdocs::SymbolID curParent = i.Parent; std::string namespaces; while (curParent) { namespaces += std::format("{}", curParent); diff --git a/src/lib/Support/Debug.hpp b/src/lib/Support/Debug.hpp index 084e21d47..8292a55cb 100644 --- a/src/lib/Support/Debug.hpp +++ b/src/lib/Support/Debug.hpp @@ -14,59 +14,59 @@ #include #if ! defined(NDEBUG) -#include +# include #endif -#include "lib/Support/Radix.hpp" -#include +#include +#include +#include #include -#include -#include +#include #include template <> -struct std::formatter : std::formatter { +struct std::formatter : std::formatter { template - std::format_context::iterator format(clang::mrdocs::SymbolID const &s, + std::format_context::iterator format(mrdocs::SymbolID const &s, FmtContext &ctx) const { - std::string str = s ? clang::mrdocs::toBase64(s) : ""; + std::string str = s ? mrdocs::toBase64(s) : ""; return std::formatter::format(std::move(str), ctx); } }; template <> -struct std::formatter : std::formatter { +struct std::formatter : std::formatter { template - std::format_context::iterator format(clang::mrdocs::InfoKind t, + std::format_context::iterator format(mrdocs::SymbolKind t, FmtContext &ctx) const { return std::formatter::format(toString(t).str(), ctx); } }; template <> -struct std::formatter : std::formatter { +struct std::formatter : std::formatter { template - std::format_context::iterator format(clang::mrdocs::AccessKind a, + std::format_context::iterator format(mrdocs::AccessKind a, FmtContext &ctx) const { return std::formatter::format(toString(a).str(), ctx); } }; template <> -struct std::formatter : std::formatter { +struct std::formatter : std::formatter { template - std::format_context::iterator format(clang::mrdocs::Info const &i, + std::format_context::iterator format(mrdocs::Symbol const &i, FmtContext &ctx) const { return std::formatter::format(toString(i), ctx); } private: - static std::string toString(clang::mrdocs::Info const &i); + static std::string toString(mrdocs::Symbol const &i); }; // Some nice odds and ends such as leak checking // and redirection to the Visual Studio output window. -namespace clang::mrdocs { +namespace mrdocs { /** Enable debug heap checking. */ @@ -75,6 +75,6 @@ MRDOCS_DECL void debugEnableHeapChecking(); #define static_error(msg, value) \ static_assert(!std::is_same_v,msg) -} // clang::mrdocs +} // mrdocs -#endif +#endif // MRDOCS_LIB_SUPPORT_DEBUG_HPP diff --git a/src/lib/Support/ExecuteAndWaitWithLogging.cpp b/src/lib/Support/ExecuteAndWaitWithLogging.cpp index cf1c00a77..8ad2ef7ae 100644 --- a/src/lib/Support/ExecuteAndWaitWithLogging.cpp +++ b/src/lib/Support/ExecuteAndWaitWithLogging.cpp @@ -9,10 +9,10 @@ // #include "ExecuteAndWaitWithLogging.hpp" -#include "lib/Support/Report.hpp" -#include "mrdocs/Support/Assert.hpp" +#include +#include -namespace clang::mrdocs { +namespace mrdocs { int ExecuteAndWaitWithLogging( llvm::StringRef program, @@ -36,4 +36,4 @@ int ExecuteAndWaitWithLogging( return llvm::sys::ExecuteAndWait(program, args, env, redirects, secondsToWait, memoryLimit, errMsg, executionFailed, procStat, affinityMask); } -} // clang::mrdocs +} // mrdocs diff --git a/src/lib/Support/ExecuteAndWaitWithLogging.hpp b/src/lib/Support/ExecuteAndWaitWithLogging.hpp index ab9d79b21..1e9ddd68d 100644 --- a/src/lib/Support/ExecuteAndWaitWithLogging.hpp +++ b/src/lib/Support/ExecuteAndWaitWithLogging.hpp @@ -8,12 +8,12 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_LIB_EXECUTE_AND_WAIT_WITH_LOGGING_HPP -#define MRDOCS_LIB_EXECUTE_AND_WAIT_WITH_LOGGING_HPP +#ifndef MRDOCS_LIB_SUPPORT_EXECUTEANDWAITWITHLOGGING_HPP +#define MRDOCS_LIB_SUPPORT_EXECUTEANDWAITWITHLOGGING_HPP #include -namespace clang::mrdocs { +namespace mrdocs { /** * A wrapper around llvm::sys::ExecuteAndWait() that prints the command @@ -37,6 +37,6 @@ int ExecuteAndWaitWithLogging( std::optional* procStat = nullptr, llvm::BitVector* affinityMask = nullptr); -} // clang::mrdocs +} // mrdocs -#endif // MRDOCS_LIB_EXECUTE_AND_WAIT_WITH_LOGGING_HPP +#endif // MRDOCS_LIB_SUPPORT_EXECUTEANDWAITWITHLOGGING_HPP diff --git a/src/lib/Support/ExecutionContext.cpp b/src/lib/Support/ExecutionContext.cpp index 337d5b943..ac724cc0e 100644 --- a/src/lib/Support/ExecutionContext.cpp +++ b/src/lib/Support/ExecutionContext.cpp @@ -12,13 +12,13 @@ // #include "ExecutionContext.hpp" -#include "lib/Metadata/Reduce.hpp" -#include "mrdocs/Support/Assert.hpp" +#include #include -#include +#include +#include #include -namespace clang { + namespace mrdocs { namespace { @@ -32,8 +32,8 @@ namespace { @param Values The vector of Info objects to merge. */ -mrdocs::Expected> -mergeInfos(std::vector>& Values) +mrdocs::Expected> +mergeInfos(std::vector>& Values) { if(Values.empty() || ! Values[0]) { @@ -56,11 +56,11 @@ mergeInfos(std::vector>& Values) void InfoExecutionContext:: report( - InfoSet&& results, + SymbolSet&& results, Diagnostics&& diags, - UndocumentedInfoSet&& undocumented) + UndocumentedSymbolSet&& undocumented) { - InfoSet info = std::move(results); + SymbolSet info = std::move(results); std::unique_lock write_lock(mutex_); // Add all new Info to the existing set. @@ -89,7 +89,7 @@ report( { if (auto infoIt = info_.find(it->id); infoIt != info_.end() && - infoIt->get()->javadoc) + infoIt->get()->doc) { it = undocumented_.erase(it); } @@ -107,14 +107,14 @@ reportEnd(report::Level level) diags_.reportTotals(level); } -Expected +Expected InfoExecutionContext:: results() { return std::move(info_); } -UndocumentedInfoSet +UndocumentedSymbolSet InfoExecutionContext:: undocumented() { @@ -122,4 +122,4 @@ undocumented() } } // mrdocs -} // clang + diff --git a/src/lib/Support/ExecutionContext.hpp b/src/lib/Support/ExecutionContext.hpp index bf793efaf..11ef036a6 100644 --- a/src/lib/Support/ExecutionContext.hpp +++ b/src/lib/Support/ExecutionContext.hpp @@ -10,12 +10,12 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_LIB_TOOL_EXECUTIONCONTEXT_HPP -#define MRDOCS_LIB_TOOL_EXECUTIONCONTEXT_HPP +#ifndef MRDOCS_LIB_SUPPORT_EXECUTIONCONTEXT_HPP +#define MRDOCS_LIB_SUPPORT_EXECUTIONCONTEXT_HPP -#include "lib/ConfigImpl.hpp" -#include "lib/Diagnostics.hpp" -#include "lib/Metadata/InfoSet.hpp" +#include +#include +#include #include #include #include @@ -23,7 +23,7 @@ #include #include -namespace clang::mrdocs { +namespace mrdocs { /** A custom execution context for visitation. @@ -38,7 +38,7 @@ namespace clang::mrdocs { MrDocs by referring to the `ConfigImpl`, reporting based on the `Info` and `Diagnostics` classes, and including a `results` method - which returns the `InfoSet`. + which returns the `SymbolSet`. */ class ExecutionContext { @@ -66,7 +66,7 @@ class ExecutionContext This function is called to report the results of an execution. - The `InfoSet` is merged into the existing + The `SymbolSet` is merged into the existing set of results. Duplicate IDs are merged. Any new diagnostics are appended to the @@ -79,9 +79,9 @@ class ExecutionContext virtual void report( - InfoSet&& info, + SymbolSet&& info, Diagnostics&& diags, - UndocumentedInfoSet&& undocumented) = 0; + UndocumentedSymbolSet&& undocumented) = 0; /** Called when the execution is complete. @@ -102,19 +102,19 @@ class ExecutionContext @return The results of the execution. */ virtual - mrdocs::Expected + mrdocs::Expected results() = 0; virtual - UndocumentedInfoSet + UndocumentedSymbolSet undocumented() = 0; }; // ---------------------------------------------------------------- -/** An execution context which stores the InfoSet and Diagnostics. +/** An execution context which stores the SymbolSet and Diagnostics. - It stores the `InfoSet` and `Diagnostics` + It stores the `SymbolSet` and `Diagnostics` objects, and returns them when `results` is called. */ @@ -123,8 +123,8 @@ class InfoExecutionContext { std::shared_mutex mutex_; Diagnostics diags_; - InfoSet info_; - UndocumentedInfoSet undocumented_; + SymbolSet info_; + UndocumentedSymbolSet undocumented_; public: using ExecutionContext::ExecutionContext; @@ -132,9 +132,9 @@ class InfoExecutionContext /// @copydoc ExecutionContext::report void report( - InfoSet&& info, + SymbolSet&& info, Diagnostics&& diags, - UndocumentedInfoSet&& undocumented) override; + UndocumentedSymbolSet&& undocumented) override; /// @copydoc ExecutionContext::reportEnd void @@ -145,18 +145,18 @@ class InfoExecutionContext The results are returned as a set of `Info` objects. - The `InfoSet` object is moved out of + The `SymbolSet` object is moved out of the execution context. @return The results of the execution. */ - Expected + Expected results() override; - UndocumentedInfoSet + UndocumentedSymbolSet undocumented() override; }; -} // clang::mrdocs +} // mrdocs -#endif +#endif // MRDOCS_LIB_SUPPORT_EXECUTIONCONTEXT_HPP diff --git a/src/lib/Support/ExecutorGroup.cpp b/src/lib/Support/ExecutorGroup.cpp index ac97398a2..bd21cd92a 100644 --- a/src/lib/Support/ExecutorGroup.cpp +++ b/src/lib/Support/ExecutorGroup.cpp @@ -14,7 +14,7 @@ #include #include -namespace clang { + namespace mrdocs { struct ExecutorGroupBase:: @@ -152,4 +152,4 @@ wait() noexcept } } // mrdocs -} // clang + diff --git a/src/lib/Support/Generator.cpp b/src/lib/Support/Generator.cpp index c496e1843..76f42c761 100644 --- a/src/lib/Support/Generator.cpp +++ b/src/lib/Support/Generator.cpp @@ -9,18 +9,18 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include "lib/AST/ParseJavadoc.hpp" -#include "lib/Support/Path.hpp" -#include "lib/Support/Chrono.hpp" -#include +#include +#include +#include #include +#include #include #include #include #include #include -namespace clang { + namespace mrdocs { Generator:: @@ -157,4 +157,4 @@ getSinglePageFullPath( } } // mrdocs -} // clang + diff --git a/src/lib/Support/GeneratorsImpl.cpp b/src/lib/Support/GeneratorsImpl.cpp index b0cfdbbfb..8238339ba 100644 --- a/src/lib/Support/GeneratorsImpl.cpp +++ b/src/lib/Support/GeneratorsImpl.cpp @@ -12,7 +12,7 @@ #include #include -namespace clang { + namespace mrdocs { extern @@ -90,4 +90,4 @@ getGenerators() noexcept } } // mrdocs -} // clang + diff --git a/src/lib/Support/GeneratorsImpl.hpp b/src/lib/Support/GeneratorsImpl.hpp index 8fae493a8..d9ba47eea 100644 --- a/src/lib/Support/GeneratorsImpl.hpp +++ b/src/lib/Support/GeneratorsImpl.hpp @@ -11,14 +11,14 @@ #ifndef MRDOCS_LIB_SUPPORT_GENERATORSIMPL_HPP #define MRDOCS_LIB_SUPPORT_GENERATORSIMPL_HPP -#include #include #include +#include #include #include #include -namespace clang { + namespace mrdocs { /** Implementaiton of Generators. @@ -63,6 +63,6 @@ GeneratorsImpl& getGeneratorsImpl() noexcept; } // mrdocs -} // clang + #endif diff --git a/src/lib/Support/Glob.cpp b/src/lib/Support/Glob.cpp index 6b445a2e7..117a282f4 100644 --- a/src/lib/Support/Glob.cpp +++ b/src/lib/Support/Glob.cpp @@ -8,15 +8,15 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include +#include +#include #include #include #include -#include -#include #include +#include -namespace clang::mrdocs { +namespace mrdocs { namespace { @@ -86,7 +86,7 @@ parseCharRange( Expected> parseBraceExpansions( std::string_view str, - std::optional const max) + Optional const max) { // If there are no brace expansions, return the original string // as the only subpattern. @@ -365,7 +365,7 @@ match(std::string_view str, char const delimiter) const std::string_view starStr = str; // The pattern suffix after the "*" run. - std::optional patternStarSuffix; + Optional patternStarSuffix; // The saved brackets index for backtracking. std::size_t bracketsStarMatchIdx = 0; @@ -564,7 +564,7 @@ Expected GlobPattern:: create( std::string_view const pattern, - std::optional maxSubGlobs) + Optional maxSubGlobs) { if (pattern.empty()) { @@ -719,5 +719,5 @@ pattern() const return impl_->pattern; } -} // clang::mrdocs +} // mrdocs diff --git a/src/lib/Support/Glob.hpp b/src/lib/Support/Glob.hpp index aa4306003..8e1e50345 100644 --- a/src/lib/Support/Glob.hpp +++ b/src/lib/Support/Glob.hpp @@ -13,7 +13,7 @@ #include -namespace clang { + namespace mrdocs { /// Check if the string matches the glob pattern @@ -23,7 +23,7 @@ globMatch( std::string_view str) noexcept; } // mrdocs -} // clang + #endif // MRDOCS_LIB_SUPPORT_GLOB_HPP diff --git a/src/lib/Support/Handlebars.cpp b/src/lib/Support/Handlebars.cpp index 7aa16e3f4..115db29f7 100644 --- a/src/lib/Support/Handlebars.cpp +++ b/src/lib/Support/Handlebars.cpp @@ -8,20 +8,20 @@ // Official repository: https://github.com/cppalliance/mrdocs // +#include +#include #include #include #include #include #include #include -#include -#include #include #include #include #include -namespace clang { + namespace mrdocs { // ============================================================== @@ -3354,7 +3354,7 @@ renderBlock( return {}; }; - std::optional hbs_error; + Optional hbs_error; if (!tag.rawBlock) { cb.set("write", dom::makeInvocable([&out, &write_nested_block, &hbs_error]( dom::Value const& newContext, @@ -6493,9 +6493,7 @@ registerContainerHelpers(Handlebars& hbs) // whichever has the key auto matchIt = std::ranges::find_if(keys, [&](dom::Value const& key) { - return - el.getObject().exists(key.getString()) && - el.getObject().get(key.getString()).isTruthy(); + return el.lookup(key.getString()).isTruthy(); }); if (bool const matchAny = matchIt != keys.end(); !matchAny) @@ -6987,5 +6985,5 @@ unregisterHelper(std::string_view name) { } } // mrdocs -} // clang + diff --git a/src/lib/Support/JavaScript.cpp b/src/lib/Support/JavaScript.cpp index e7fc49a4f..28bbd357c 100644 --- a/src/lib/Support/JavaScript.cpp +++ b/src/lib/Support/JavaScript.cpp @@ -9,17 +9,17 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include "lib/Support/Report.hpp" -#include -#include -#include +#include #include #include #include +#include +#include +#include #include #include -namespace clang { + namespace mrdocs { namespace js { @@ -1883,7 +1883,7 @@ operator&&(Value const& lhs, Value const& rhs) Expected registerHelper( - clang::mrdocs::Handlebars& hbs, + mrdocs::Handlebars& hbs, std::string_view name, Context& ctx, std::string_view script) @@ -1987,4 +1987,4 @@ registerHelper( } // js } // mrdocs -} // clang + diff --git a/src/lib/Support/LegibleNames.cpp b/src/lib/Support/LegibleNames.cpp index c0cde8593..c9c91daf1 100644 --- a/src/lib/Support/LegibleNames.cpp +++ b/src/lib/Support/LegibleNames.cpp @@ -10,26 +10,26 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include "lib/Support/LegibleNames.hpp" -#include "lib/Support/Debug.hpp" -#include "lib/Support/Radix.hpp" -#include "lib/Support/Validate.hpp" -#include +#include +#include +#include +#include +#include #include #include #include -#include #include #include +#include #include #include #include -namespace clang::mrdocs { +namespace mrdocs { namespace { std::string -getUnnamedInfoName(Info const& I) +getUnnamedInfoName(Symbol const& I) { // All valid c++ identifiers begin with // an underscore or alphabetic character, @@ -99,7 +99,7 @@ class LegibleNames::Impl /* The info related to the legible name for a symbol */ - struct LegibleNameInfo + struct LegibleName { /* Raw unqualified name for the symbol */ @@ -117,11 +117,11 @@ class LegibleNames::Impl /* A map from SymbolID to legible name information */ - std::unordered_map map_; + std::unordered_map map_; /* Maps unqualified names to all symbols with that name within the current scope */ - UnorderedStringMultiMap disambiguation_map_; + UnorderedStringMultiMap disambiguation_map_; public: /* Build the map of legible names for all symbols in the corpus @@ -130,7 +130,7 @@ class LegibleNames::Impl : corpus_(corpus) , global_ns_(global_ns) { - NamespaceInfo const& global = corpus_.globalNamespace(); + NamespaceSymbol const& global = corpus_.globalNamespace(); // Treat the global namespace as-if its "name" // is in the same scope as its members @@ -149,11 +149,11 @@ class LegibleNames::Impl void operator()(InfoTy const& I) { - if constexpr (InfoParent && !std::same_as) + if constexpr (SymbolParent && !std::same_as) { // Visit the members of the symbol and build legible names constexpr Corpus::TraverseOptions opts = {.skipInherited = true}; - corpus_.traverse(opts, I, [this, &I](Info const& M) + corpus_.traverse(opts, I, [this, &I](Symbol const& M) { auto const raw = getRawUnqualified(M); buildLegibleMember(M, raw); @@ -161,7 +161,7 @@ class LegibleNames::Impl // Traverse non inherited function overloads inline if (auto* MO = M.asOverloadsPtr()) { - corpus_.traverse(*MO, [this, &I](Info const& M2) + corpus_.traverse(*MO, [this, &I](Symbol const& M2) { // Not inherited in regard to I MRDOCS_CHECK_OR(M2.Parent == I.id); @@ -176,7 +176,7 @@ class LegibleNames::Impl // Visit the members of the symbol to build legible names // for their members - corpus_.traverse(opts, I, [this](Info const& M) + corpus_.traverse(opts, I, [this](Symbol const& M) { visit(M, *this); }); @@ -194,7 +194,7 @@ class LegibleNames::Impl std::string getRawUnqualified(SymbolID const& id) { - Info const* I = corpus_.find(id); + Symbol const* I = corpus_.find(id); MRDOCS_ASSERT(I); return getRawUnqualified(*I); } @@ -202,7 +202,7 @@ class LegibleNames::Impl /* @copydoc getRawUnqualified(SymbolID const&) */ std::string - getRawUnqualified(Info const& I) + getRawUnqualified(Symbol const& I) { MRDOCS_ASSERT(I.id && I.id != SymbolID::global); if (I.Name.empty()) @@ -239,19 +239,18 @@ class LegibleNames::Impl /* Take the raw unqualified name for a symbol and build a legible name */ void - buildLegibleMember( - Info const& I, + buildLegibleMember(Symbol const& I, std::string_view rawName) { // Generate the legible name information for this symbol auto const idAsString = toBase16(I.id, true); - LegibleNameInfo LI(std::string(rawName), 0, idAsString); - LegibleNameInfo& info = map_.emplace(I.id, std::move(LI)).first->second; + LegibleName LI(std::string(rawName), 0, idAsString); + LegibleName& info = map_.emplace(I.id, std::move(LI)).first->second; // Look for symbols with the same unqualified name auto [first, last] = disambiguation_map_.equal_range(rawName); - auto sameNameInfos = std::ranges::subrange(first, last) | std::views::values; - if (std::ranges::empty(sameNameInfos)) + auto sameNames = std::ranges::subrange(first, last) | std::views::values; + if (std::ranges::empty(sameNames)) { // Add this symbol to the disambiguation map disambiguation_map_.emplace(rawName, &info); @@ -263,7 +262,7 @@ class LegibleNames::Impl // Iterate over the other symbols with the same raw unqualified name // and update their legible name information std::uint8_t suffix_size_required = 0; - for (LegibleNameInfo* other : sameNameInfos) + for (LegibleName* other : sameNames) { // Find the first character that differs between the two symbol IDs auto const mismatch_it = std::ranges::mismatch( @@ -380,4 +379,4 @@ getQualified( return result; } -} // clang::mrdocs +} // mrdocs diff --git a/src/lib/Support/LegibleNames.hpp b/src/lib/Support/LegibleNames.hpp index a5ca1ee41..1bd7babac 100644 --- a/src/lib/Support/LegibleNames.hpp +++ b/src/lib/Support/LegibleNames.hpp @@ -12,12 +12,12 @@ #ifndef MRDOCS_LIB_SUPPORT_LEGIBLENAMES_HPP #define MRDOCS_LIB_SUPPORT_LEGIBLENAMES_HPP -#include #include +#include #include #include -namespace clang::mrdocs { +namespace mrdocs { /** A table mapping Info objects to legible names. @@ -61,6 +61,6 @@ class LegibleNames char delim = '-') const; }; -} // clang::mrdocs +} // mrdocs #endif diff --git a/src/lib/Support/Lua.cpp b/src/lib/Support/Lua.cpp index 9499f070a..b49e6a668 100644 --- a/src/lib/Support/Lua.cpp +++ b/src/lib/Support/Lua.cpp @@ -8,17 +8,17 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include "../../../third-party/lua/src/lua.hpp" -#include +#include #include #include #include -#include - #include -#include "lib/Support/LuaHandlebars.hpp" +#include +#include +#include +#include +#include -namespace clang { namespace mrdocs { namespace lua { @@ -1206,4 +1206,3 @@ lua_main() } // lua } // mrdocs -} // clang diff --git a/src/lib/Support/LuaHandlebars.cpp b/src/lib/Support/LuaHandlebars.cpp index 60474aa08..363b0a6cf 100644 --- a/src/lib/Support/LuaHandlebars.cpp +++ b/src/lib/Support/LuaHandlebars.cpp @@ -11,7 +11,6 @@ #include "LuaHandlebars.hpp" #include -namespace clang { namespace mrdocs { // VFALCO: @@ -67,4 +66,3 @@ tryLoadHandlebars( } } // mrdocs -} // clang diff --git a/src/lib/Support/LuaHandlebars.hpp b/src/lib/Support/LuaHandlebars.hpp index 7e32b0202..6372fe2b4 100644 --- a/src/lib/Support/LuaHandlebars.hpp +++ b/src/lib/Support/LuaHandlebars.hpp @@ -8,13 +8,12 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_API_SUPPORT_LUAHANDLEBARS_HPP -#define MRDOCS_API_SUPPORT_LUAHANDLEBARS_HPP +#ifndef MRDOCS_LIB_SUPPORT_LUAHANDLEBARS_HPP +#define MRDOCS_LIB_SUPPORT_LUAHANDLEBARS_HPP #include #include -namespace clang { namespace mrdocs { /** Add the Handlebars Lua instance as a global @@ -25,6 +24,5 @@ tryLoadHandlebars( lua::Context const& ctx); } // mrdocs -} // clang -#endif +#endif // MRDOCS_LIB_SUPPORT_LUAHANDLEBARS_HPP diff --git a/src/lib/Support/Path.cpp b/src/lib/Support/Path.cpp index f0c7d5f90..38fc0adbf 100644 --- a/src/lib/Support/Path.cpp +++ b/src/lib/Support/Path.cpp @@ -17,7 +17,7 @@ #include #include -namespace clang { + namespace mrdocs { AnyFileVisitor:: @@ -561,4 +561,3 @@ error() const } } // mrdocs -} // clang \ No newline at end of file diff --git a/src/lib/Support/Path.hpp b/src/lib/Support/Path.hpp index 9e17725eb..9781402c3 100644 --- a/src/lib/Support/Path.hpp +++ b/src/lib/Support/Path.hpp @@ -18,7 +18,7 @@ #include #include -namespace clang { + namespace mrdocs { /** A reasonably sized small string for paths. @@ -45,7 +45,7 @@ convert_to_slash( */ class ScopedTempFile { - clang::mrdocs::SmallPathString path_; + mrdocs::SmallPathString path_; bool ok_ = false; public: /** Destructor @@ -93,7 +93,7 @@ class ScopedTempDirectory CannotCreateDirectories }; - clang::mrdocs::SmallPathString path_; + mrdocs::SmallPathString path_; ErrorStatus status_ = ErrorStatus::None; public: /** Destructor @@ -164,6 +164,6 @@ class ScopedTempDirectory }; } // mrdocs -} // clang + #endif diff --git a/src/lib/Support/Radix.cpp b/src/lib/Support/Radix.cpp index d98b04283..26ddf1915 100644 --- a/src/lib/Support/Radix.cpp +++ b/src/lib/Support/Radix.cpp @@ -9,14 +9,14 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include "lib/Support/Radix.hpp" -#include "lib/Support/Debug.hpp" #include +#include +#include #include #include #include -namespace clang { + namespace mrdocs { namespace { @@ -329,4 +329,4 @@ toBase16( } } // mrdocs -} // clang + diff --git a/src/lib/Support/Radix.hpp b/src/lib/Support/Radix.hpp index a8347e3fc..36406d7b7 100644 --- a/src/lib/Support/Radix.hpp +++ b/src/lib/Support/Radix.hpp @@ -20,7 +20,7 @@ #include #include -namespace clang::mrdocs { +namespace mrdocs { std::string toBase64(std::string_view str); @@ -42,6 +42,6 @@ toBase16( std::string_view str, bool lowercase = false); -} // clang::mrdocs +} // mrdocs #endif diff --git a/src/lib/Support/RawOstream.hpp b/src/lib/Support/RawOstream.hpp index 2001193e1..230af79a3 100644 --- a/src/lib/Support/RawOstream.hpp +++ b/src/lib/Support/RawOstream.hpp @@ -14,7 +14,7 @@ #include #include -namespace clang { + namespace mrdocs { class RawOstream : public llvm::raw_ostream @@ -51,6 +51,6 @@ class RawOstream : public llvm::raw_ostream }; } // mrdocs -} // clang + #endif diff --git a/src/lib/Support/Report.cpp b/src/lib/Support/Report.cpp index ae0e0b4d8..4c31ad886 100644 --- a/src/lib/Support/Report.cpp +++ b/src/lib/Support/Report.cpp @@ -8,29 +8,32 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include "lib/Support/Report.hpp" -#include -#include +#include +#include +#include +#include #include #include #include -#include -#include +#include +#include #include #ifdef _MSC_VER #define WIN32_LEAN_AND_MEAN +// clang-format off #include #include #include #include +// clang-format on #endif namespace SourceFileNames { extern char const* getFileName(char const*) noexcept; } // SourceFileNames -namespace clang { + namespace mrdocs { //------------------------------------------------ @@ -334,4 +337,4 @@ call_impl( } // report } // mrdocs -} // clang + diff --git a/src/lib/Support/Report.hpp b/src/lib/Support/Report.hpp index d8800a8a2..39afcbddf 100644 --- a/src/lib/Support/Report.hpp +++ b/src/lib/Support/Report.hpp @@ -8,19 +8,19 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_LIB_SUPPORT_ERROR_HPP -#define MRDOCS_LIB_SUPPORT_ERROR_HPP +#ifndef MRDOCS_LIB_SUPPORT_REPORT_HPP +#define MRDOCS_LIB_SUPPORT_REPORT_HPP -#include -#include +#include #include #include #include -#include +#include +#include #include #include -namespace clang::mrdocs { +namespace mrdocs { namespace report { @@ -128,7 +128,7 @@ call( } // report -} // clang::mrdocs +} // mrdocs template <> struct std::formatter : std::formatter { @@ -149,4 +149,4 @@ struct std::formatter> } }; -#endif +#endif // MRDOCS_LIB_SUPPORT_REPORT_HPP diff --git a/src/lib/Support/String.cpp b/src/lib/Support/String.cpp index 8959faced..14a68d19f 100644 --- a/src/lib/Support/String.cpp +++ b/src/lib/Support/String.cpp @@ -8,9 +8,11 @@ // Official repository: https://github.com/cppalliance/mrdocs // +#include #include +#include -namespace clang::mrdocs { +namespace mrdocs { void replace(std::string& s, std::string_view from, std::string_view to) @@ -25,4 +27,87 @@ replace(std::string& s, std::string_view from, std::string_view to) } } -} // clang::mrdocs +namespace { +// Return true if c is space or tab +constexpr +bool +isSpaceOrTab(char c) noexcept { return c == ' ' || c == '\t'; } + +// Return true if s ends with a line break sequence +constexpr +bool +endsWithLineBreak(std::string_view s) noexcept +{ + // Recognize if s ends exactly at a line break sequence handled by lbLen + for (std::size_t k = 1; k <= 3 && k <= s.size(); ++k) + { + const std::size_t i = s.size() - k; + if (detail::lbLen(s, i) == k) return true; + } + return false; +} +} // namespace + +std::string +reindentCode(std::string_view code, std::size_t indent) +{ + auto lines = splitLines(code); + + // 1) Compute common indentation (spaces/tabs) across non-blank lines + std::size_t common = std::numeric_limits::max(); + bool sawContent = false; + for (std::string_view line : lines) + { + auto it = std::ranges::find_if_not(line, isSpaceOrTab); + if (it == line.end()) + { + // blank/whitespace-only -> skip + continue; + } + auto lead = static_cast(it - line.begin()); + common = std::min(common, lead); + sawContent = true; + } + if (!sawContent) + { + // all blank lines -> nothing to do + common = 0; + } + + // 2) Build output: remove common, add requested indent to non-blank lines + std::string out; + out.reserve(code.size()); // heuristic; result is often similar size + const std::string indentStr(indent, ' '); + const bool hadTrailingLB = endsWithLineBreak(code); + + bool first = true; + for (std::string_view line : lines) { + if (!first) + { + out.push_back('\n'); + } + first = false; + + // Keep blank lines blank (no added indentation) + auto it = std::ranges::find_if_not(line, isSpaceOrTab); + if (it == line.end()) + { + // blank -> nothing to append on this line + continue; + } + + auto lead = static_cast(it - line.begin()); + std::size_t remove = std::min(common, lead); + std::string_view tail = line.substr(remove); + + out.append(indentStr); + out.append(tail.data(), tail.size()); + } + if (hadTrailingLB) + { + out.push_back('\n'); + } + return out; +} + +} // mrdocs diff --git a/src/lib/Support/ThreadPool.cpp b/src/lib/Support/ThreadPool.cpp index 07a962e55..191397624 100644 --- a/src/lib/Support/ThreadPool.cpp +++ b/src/lib/Support/ThreadPool.cpp @@ -16,7 +16,7 @@ #include #include -namespace clang { + namespace mrdocs { //------------------------------------------------ @@ -185,4 +185,4 @@ post( } } // mrdocs -} // clang + diff --git a/src/lib/Support/Validate.cpp b/src/lib/Support/Validate.cpp index c910e97e4..c623226e6 100644 --- a/src/lib/Support/Validate.cpp +++ b/src/lib/Support/Validate.cpp @@ -12,7 +12,7 @@ #include "Validate.hpp" #include -namespace clang { + namespace mrdocs { bool @@ -34,4 +34,4 @@ validAdocSectionID( } } // mrdocs -} // clang + diff --git a/src/lib/Support/Validate.hpp b/src/lib/Support/Validate.hpp index 4b7c9cb74..0c124f873 100644 --- a/src/lib/Support/Validate.hpp +++ b/src/lib/Support/Validate.hpp @@ -20,7 +20,7 @@ as Asciidoc section identifiers or XML attributes. */ -namespace clang { + namespace mrdocs { /** Return true if s is a valid Asciidoc section ID. @@ -30,6 +30,6 @@ validAdocSectionID( llvm::StringRef s) noexcept; } // mrdocs -} // clang + #endif diff --git a/src/lib/Support/Yaml.cpp b/src/lib/Support/Yaml.cpp index 3b908fcf3..4c8a2ef95 100644 --- a/src/lib/Support/Yaml.cpp +++ b/src/lib/Support/Yaml.cpp @@ -9,10 +9,10 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include "lib/Support/Report.hpp" -#include "lib/Support/Yaml.hpp" +#include +#include + -namespace clang { namespace mrdocs { void YamlReporter::diag(llvm::SMDiagnostic const &D, void *) { @@ -45,4 +45,4 @@ void YamlReporter::diag(llvm::SMDiagnostic const &D, void *) { } } // mrdocs -} // clang + diff --git a/src/lib/Support/Yaml.hpp b/src/lib/Support/Yaml.hpp index c00b93223..3431a79b2 100644 --- a/src/lib/Support/Yaml.hpp +++ b/src/lib/Support/Yaml.hpp @@ -15,7 +15,7 @@ #include #include -namespace clang { + namespace mrdocs { class MRDOCS_DECL @@ -30,6 +30,6 @@ class MRDOCS_DECL }; } // mrdocs -} // clang + #endif diff --git a/src/lib/clang.natvis b/src/lib/clang.natvis index 99226d36f..85022670b 100644 --- a/src/lib/clang.natvis +++ b/src/lib/clang.natvis @@ -109,12 +109,12 @@ For later versions of Visual Studio, no setup is required--> {(clang::QualType *)Arg,view(cpp)na} {*(clang::QualType *)Arg} - {*(clang::Expr *)Arg} + {*(clang::Expr *)Arg} {*(clang::TemplateName *)Arg Kind,en (clang::QualType *)Arg - (clang::Expr *)Arg + (clang::Expr *)Arg (clang::TemplateName *)Arg @@ -316,8 +316,8 @@ For later versions of Visual Studio, no setup is required--> - + {*(IdentifierInfo*)get_ptr(),na} @@ -335,7 +335,7 @@ For later versions of Visual Studio, no setup is required--> get_name_kind(),en - StoredNameKind(get_stored_kind()),en + StoredName(get_stored_kind()),en *(IdentifierInfo*)get_ptr(),na *(IdentifierInfo*)get_ptr(),na @@ -402,7 +402,7 @@ For later versions of Visual Studio, no setup is required--> - + {Name} @@ -1141,16 +1141,16 @@ For later versions of Visual Studio, no setup is required--> (ObjCPropertyDecl*)this (BuiltinTemplateDecl*)this (ConceptDecl*)this - (ClassTemplateDecl*)this + (clang::ClassTemplateDecl*)this (FunctionTemplateDecl*)this (TypeAliasTemplateDecl*)this (VarTemplateDecl*)this (TemplateTemplateParmDecl*)this (EnumDecl*)this (RecordDecl*)this - (CXXRecordDecl*)this + (clang::CXXRecordDecl*)this (ClassTemplateSpecializationDecl*)this - (ClassTemplatePartialSpecializationDecl*)this + (clang::ClassTemplatePartialSpecializationDecl*)this (TemplateTypeParmDecl*)this (ObjCTypeParamDecl*)this (TypeAliasDecl*)this @@ -1165,7 +1165,7 @@ For later versions of Visual Studio, no setup is required--> (FieldDecl*)this (ObjCAtDefsFieldDecl*)this (ObjCIvarDecl*)this - (FunctionDecl*)this + (clang::FunctionDecl*)this (CXXDeductionGuideDecl*)this (CXXMethodDecl*)this (CXXConstructorDecl*)this @@ -1226,7 +1226,7 @@ For later versions of Visual Studio, no setup is required--> - (FunctionDecl*)this + (clang::FunctionDecl*)this (CXXDeductionGuideDecl*)this (CXXMethodDecl*)this (CXXConstructorDecl*)this @@ -1234,9 +1234,9 @@ For later versions of Visual Studio, no setup is required--> (CXXDestructorDecl*)this (EnumDecl*)this (RecordDecl*)this - (CXXRecordDecl*)this + (clang::CXXRecordDecl*)this (ClassTemplateSpecializationDecl*)this - (ClassTemplatePartialSpecializationDecl*)this + (clang::ClassTemplatePartialSpecializationDecl*)this (BlockDecl*)this (CapturedDecl*)this (TranslationUnitDecl*)this @@ -1418,7 +1418,7 @@ For later versions of Visual Studio, no setup is required--> - + @@ -1432,19 +1432,19 @@ For later versions of Visual Studio, no setup is required--> get_qualifier(),na - (FunctionDeclBits.IsDeleted && ! FunctionDeclBits.IsDefaulted) || - ((! FunctionDeclBits.HasDefaultedFunctionInfo && Body.Ptr) || FunctionDeclBits.IsLateTemplateParsed) || - FunctionDeclBits.HasSkippedBody || - FunctionDeclBits.WillHaveBody + (clang::FunctionDeclBits.IsDeleted && ! clang::FunctionDeclBits.IsDefaulted) || + ((! clang::FunctionDeclBits.HasDefaultedFunctionSymbol && Body.Ptr) || clang::FunctionDeclBits.IsLateTemplateParsed) || + clang::FunctionDeclBits.HasSkippedBody || + clang::FunctionDeclBits.WillHaveBody - (FunctionDecl*)TemplateOrSpecialization.get_ptr() + (clang::FunctionDecl*)TemplateOrSpecialization.get_ptr() - (MemberSpecializationInfo*)TemplateOrSpecialization.get_ptr(),na + (clang::MemberSpecializationInfo*)TemplateOrSpecialization.get_ptr(),na @@ -1491,7 +1491,7 @@ For later versions of Visual Studio, no setup is required--> - + *(RedeclarableTemplateDecl::CommonBase*)this,nd InjectedClassNameType @@ -1511,7 +1511,7 @@ For later versions of Visual Studio, no setup is required--> (size_t)PartialSpecializations.Vector.Size - (ClassTemplatePartialSpecializationDecl**)PartialSpecializations.Vector.BeginX + (clang::ClassTemplatePartialSpecializationDecl**)PartialSpecializations.Vector.BeginX @@ -1640,7 +1640,7 @@ For later versions of Visual Studio, no setup is required--> common = decl ? decl->Common : nullptr - (ClassTemplateDecl::Common*)common + (clang::ClassTemplateDecl::Common*)common (VarTemplateDecl::Common*)common (FunctionTemplateDecl::Common*)common (TypeAliasTemplateDecl::Common*)common @@ -1648,7 +1648,7 @@ For later versions of Visual Studio, no setup is required--> --> - *(ClassTemplateDecl::Common*)get_common_ptr() + *(clang::ClassTemplateDecl::Common*)get_common_ptr() *(VarTemplateDecl::Common*)get_common_ptr() @@ -1687,7 +1687,7 @@ For later versions of Visual Studio, no setup is required--> clang::TemplateSpecializationKind(Template.get_int() + 1),en Function.get_int() - *reinterpret_cast<MemberSpecializationInfo**>(this + 1) + *reinterpret_cast<clang::MemberSpecializationInfo**>(this + 1) Function.get_ptr() Template.get_ptr() @@ -1707,7 +1707,7 @@ For later versions of Visual Studio, no setup is required--> - + clang::TemplateSpecializationKind(MemberAndTSK.get_int() + 1),en @@ -1743,7 +1743,7 @@ For later versions of Visual Studio, no setup is required--> @@ -1752,7 +1752,7 @@ For later versions of Visual Studio, no setup is required--> (VarTemplateDecl*)TemplateOrInstantiation.get_ptr() - (MemberSpecializationInfo*)TemplateOrInstantiation.get_ptr() + (clang::MemberSpecializationInfo*)TemplateOrInstantiation.get_ptr() --> @@ -1785,11 +1785,11 @@ For later versions of Visual Studio, no setup is required--> - - + + - + @@ -1807,14 +1807,14 @@ For later versions of Visual Studio, no setup is required--> clang::TemplateSpecializationKind( - ((MemberSpecializationInfo*)TemplateOrInstantiation.get_ptr())->MemberAndTSK.get_int() + 1),en + ((clang::MemberSpecializationInfo*)TemplateOrInstantiation.get_ptr())->MemberAndTSK.get_int() + 1),en - (ClassTemplateDecl*)TemplateOrInstantiation.get_ptr() + (clang::ClassTemplateDecl*)TemplateOrInstantiation.get_ptr() - (MemberSpecializationInfo*)TemplateOrInstantiation.get_ptr(),na + (clang::MemberSpecializationInfo*)TemplateOrInstantiation.get_ptr(),na InstantiatedFromMember.get_int() @@ -1823,7 +1823,7 @@ For later versions of Visual Studio, no setup is required--> - (ClassTemplateDecl*)SpecializedTemplate.get_ptr() + (clang::ClassTemplateDecl*)SpecializedTemplate.get_ptr() ((ClassTemplateSpecializationDecl::SpecializedPartialSpecialization*)(SpecializedTemplate.get_ptr()))->PartialSpecialization @@ -1856,22 +1856,22 @@ For later versions of Visual Studio, no setup is required--> {ExplicitSpec,view(int)en} : {ExplicitSpec,view(ptr)na} - {ExplicitSpec,view(cpp)}{Name,view(cpp)nd}({(FunctionDecl*)this,view(parm0)nand}) -> {((clang::FunctionProtoType *)((clang::ExtQualsTypeCommonBase *)(((uintptr_t)DeclType.Value.Value) & ~15))->BaseType)->ResultType,view(cpp)} + {ExplicitSpec,view(cpp)}{Name,view(cpp)nd}({(clang::FunctionDecl*)this,view(parm0)nand}) -> {((clang::FunctionProtoType *)((clang::ExtQualsTypeCommonBase *)(((uintptr_t)DeclType.Value.Value) & ~15))->BaseType)->ResultType,view(cpp)} ExplicitSpec - (bool)FunctionDeclBits.IsCopyDeductionCandidate - (FunctionDecl*)this,nd + (bool)clang::FunctionDeclBits.IsCopyDeductionCandidate + (clang::FunctionDecl*)this,nd @@ -1990,7 +1990,7 @@ For later versions of Visual Studio, no setup is required--> - + {((clang::FunctionProtoType *)((clang::ExtQualsTypeCommonBase *)((*(uintptr_t *)DeclType.Value.Value.Data) & ~15))->BaseType)->ResultType,view(cpp)} {ParamInfo[0],na}{*this,view(parm1)nd} @@ -2010,7 +2010,7 @@ For later versions of Visual Studio, no setup is required--> {this,view(retType)nand} {Name,view(cpp)nd}({*this,view(parm0)nd}) (clang::DeclaratorDecl *)this,nd - (clang::Redeclarable<clang::FunctionDecl> *)this,nd + (clang::Redeclarable<clang::clang::FunctionDecl> *)this,nd ((clang::FunctionProtoType *)((clang::ExtQualsTypeCommonBase *)((*(uintptr_t *)DeclType.Value.Value.Data) & ~15))->BaseType)->ResultType {*this,view(parm0)nd} diff --git a/src/lib/lua.cpp b/src/lib/lua.cpp deleted file mode 100644 index 5e4982776..000000000 --- a/src/lib/lua.cpp +++ /dev/null @@ -1,59 +0,0 @@ -extern "C" { - -#if defined(__clang__) -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wcast-qual" -# pragma clang diagnostic ignored "-Wimplicit-fallthrough" -#elif defined(__GNUC__) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wcast-qual" -# pragma GCC diagnostic ignored "-Wimplicit-fallthrough" -#elif defined(_MSC_VER) -# pragma warning(push) -# pragma warning(disable: 4334) // result of 32-bit shift implicitly converted to 64 bits (was 64-bit shift intended?) -#endif - -#include "../third-party/lua/src/lapi.c" -#include "../third-party/lua/src/lauxlib.c" -#include "../third-party/lua/src/lbaselib.c" -#include "../third-party/lua/src/lcode.c" -#include "../third-party/lua/src/lcorolib.c" -#include "../third-party/lua/src/lctype.c" -#include "../third-party/lua/src/ldblib.c" -#include "../third-party/lua/src/ldebug.c" -#include "../third-party/lua/src/ldo.c" -#include "../third-party/lua/src/ldump.c" -#include "../third-party/lua/src/lfunc.c" -#include "../third-party/lua/src/lgc.c" -#include "../third-party/lua/src/linit.c" -#include "../third-party/lua/src/liolib.c" -#include "../third-party/lua/src/llex.c" -#include "../third-party/lua/src/lmathlib.c" -#include "../third-party/lua/src/lmem.c" -#include "../third-party/lua/src/loadlib.c" -#include "../third-party/lua/src/lobject.c" -#include "../third-party/lua/src/lopcodes.c" -#include "../third-party/lua/src/loslib.c" -#include "../third-party/lua/src/lparser.c" -#include "../third-party/lua/src/lstate.c" -#include "../third-party/lua/src/lstring.c" -#include "../third-party/lua/src/lstrlib.c" -#include "../third-party/lua/src/ltable.c" -#include "../third-party/lua/src/ltablib.c" -#include "../third-party/lua/src/ltm.c" -//#include "../third-party/lua/src/lua.c" -//#include "../third-party/lua/src/luac.c" -#include "../third-party/lua/src/lundump.c" -#include "../third-party/lua/src/lutf8lib.c" -#include "../third-party/lua/src/lvm.c" -#include "../third-party/lua/src/lzio.c" - -#if defined(__clang__) -# pragma clang diagnostic pop -#elif defined(__GNUC__) -# pragma GCC diagnostic pop -#elif defined(_MSC_VER) -# pragma warning(pop) -#endif - -} // "C" diff --git a/src/test/ADT/ArrayView.cpp b/src/test/ADT/ArrayView.cpp new file mode 100644 index 000000000..f287bd2b9 --- /dev/null +++ b/src/test/ADT/ArrayView.cpp @@ -0,0 +1,171 @@ +// +// 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 +// + +#include +#include + +namespace mrdocs { + +struct ArrayViewTest { + template + static bool equal(ArrayView v, std::initializer_list il) { + if (v.size() != il.size()) + return false; + return std::equal(v.begin(), v.end(), il.begin(), il.end()); + } + + void + run() + { + // default ctor + { + ArrayView v; + BOOST_TEST(v.empty()); + BOOST_TEST_EQ(v.size(), 0u); + BOOST_TEST(v.begin() == v.end()); + BOOST_TEST(v.data() == nullptr); + } + + // array ctor + deduction guide + { + int a[] = {1,2,3,4}; + ArrayView v(a); // CTAD + static_assert(std::is_same_v>); + BOOST_TEST(!v.empty()); + BOOST_TEST_EQ(v.size(), 4u); + BOOST_TEST_EQ(v.front(), 1); + BOOST_TEST_EQ(v.back(), 4); + BOOST_TEST(v.data() == a); + BOOST_TEST(equal(v, {1,2,3,4})); + } + + // pointer + size ctor + { + int a[] = {10,20,30,40,50}; + ArrayView v(a + 1, 3); + BOOST_TEST_EQ(v.size(), 3u); + BOOST_TEST(equal(v, {20,30,40})); + BOOST_TEST_EQ(v[0], 20); + BOOST_TEST_EQ(v[2], 40); + } + + // iterator + count ctor (contiguous iterators) + { + int a[] = {7,8,9}; + auto first = a; + ArrayView v(first, 2); + BOOST_TEST_EQ(v.size(), 2u); + BOOST_TEST(equal(v, {7,8})); + } + + // iterators and reverse iterators + { + int a[] = {1,2,3}; + ArrayView v(a); + BOOST_TEST(std::distance(v.begin(), v.end()) == 3); + BOOST_TEST(*v.begin() == 1); + BOOST_TEST(*(v.end() - 1) == 3); + + auto r = v.rbegin(); + BOOST_TEST(*r == 3); + ++r; + BOOST_TEST(*r == 2); + } + + // at(), bounds-checked (via assert in debug); just verify value here + { + int a[] = {11,22}; + ArrayView v(a); + BOOST_TEST_EQ(v.at(0), 11); + BOOST_TEST_EQ(v.at(1), 22); + } + + // remove_prefix / remove_suffix adjust the view only + { + int a[] = {1,2,3,4,5}; + ArrayView v(a); + v.remove_prefix(1); + BOOST_TEST(equal(v, {2,3,4,5})); + v.remove_suffix(2); + BOOST_TEST(equal(v, {2,3})); + } + + // slice / take_* / drop_* helpers + { + int a[] = {5,6,7,8,9}; + ArrayView v(a); + auto s1 = v.slice(1, 3); + BOOST_TEST(equal(s1, {6,7,8})); + + auto s2 = v.slice(3, ArrayView::npos); + BOOST_TEST(equal(s2, {8,9})); + + auto tf = v.take_front(2); + BOOST_TEST(equal(tf, {5,6})); + + auto tb = v.take_back(3); + BOOST_TEST(equal(tb, {7,8,9})); + + auto df = v.drop_front(4); + BOOST_TEST(equal(df, {9})); + + auto db = v.drop_back(5); + BOOST_TEST(db.empty()); + } + + // comparisons: == and <=> + { + int a[] = {1,2,3}; + int b[] = {1,2,3}; + int c[] = {1,2,4}; + int d[] = {1,2,3,0}; + + ArrayView va(a), vb(b), vc(c), vd(d); + + // equality + BOOST_TEST(va == vb); + BOOST_TEST(!(va == vc)); + + // three-way: va vs vc (3 < 4) + { + auto ord = (va <=> vc); + BOOST_TEST(ord == std::strong_ordering::less); + } + // three-way: vc vs va + { + auto ord = (vc <=> va); + BOOST_TEST(ord == std::strong_ordering::greater); + } + // prefix vs longer with same prefix: va (3) vs vd (3,0) + { + auto ord = (va <=> vd); + BOOST_TEST(ord == std::strong_ordering::less); + } + // identical content + { + auto ord = (va <=> vb); + BOOST_TEST(ord == std::strong_ordering::equal); + } + } + + // data() exposure and aliasing (view must not modify underlying) + { + int a[] = {42,43}; + ArrayView v(a); + // mutate underlying array; view should reflect it + a[1] = 99; + BOOST_TEST_EQ(v.back(), 99); + } + } +}; + +TEST_SUITE(ArrayViewTest, "clang.mrdocs.ADT.ArrayView"); + +} // namespace mrdocs diff --git a/src/test/ADT/Nullable.cpp b/src/test/ADT/Nullable.cpp new file mode 100644 index 000000000..e9d13c734 --- /dev/null +++ b/src/test/ADT/Nullable.cpp @@ -0,0 +1,239 @@ +// +// 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 +// + +#include +#include +#include +#include +#include +#include + +namespace mrdocs { + +struct NullableTest { + // Types for testing + struct NoTraits { + int v{}; + }; + + enum class EUnknown { + A, + B, + Unknown + }; + enum class EUNKNOWN { + A, + B, + UNKNOWN + }; + enum class ENone { + A, + B, + None + }; + enum class ENONE { + A, + B, + NONE + }; + + void + test_concepts_and_detection() + { + // HasSentinel: pointers, nullptr_t, unsigned, floating, enums with + // known nuller + static_assert(HasSentinel); + static_assert(HasSentinel); + static_assert(HasSentinel); + static_assert(HasSentinel); + static_assert(HasSentinel); + static_assert(HasSentinel); + static_assert(HasSentinel); + static_assert(HasSentinel); + + // HasSentinel should NOT hold for std::string or NoTraits + static_assert(!HasSentinel); + static_assert(!HasSentinel); + + // ClearableEmpty: string, vector qualify + static_assert(ClearableContainerLike); + static_assert(ClearableContainerLike>); + + // ClearableEmpty should NOT hold for primitive or pointer + static_assert(!ClearableContainerLike); + static_assert(!ClearableContainerLike); + + // has_nullable_traits_v (the concept) should be true when either + // sentinel or clearable-empty applies + static_assert(has_nullable_traits_v); + static_assert(has_nullable_traits_v); + static_assert(has_nullable_traits_v); + static_assert(has_nullable_traits_v); + static_assert(has_nullable_traits_v); + static_assert(has_nullable_traits_v); + static_assert(has_nullable_traits_v>); + static_assert(!has_nullable_traits_v); + } + + void + test_sentinel_traits_pointers() + { + int* p = nullptr; + BOOST_TEST(sentinel_traits::is_sentinel(p)); + BOOST_TEST(sentinel_traits::sentinel() == nullptr); + + int x = 0; + p = &x; + BOOST_TEST(!sentinel_traits::is_sentinel(p)); + + // nullable_traits uses sentinel for pointers + BOOST_TEST(is_null(nullptr)); + int* q = &x; + BOOST_TEST(!is_null(q)); + make_null(q); + BOOST_TEST(is_null(q)); + auto n = null_of(); + BOOST_TEST(n == nullptr); + } + + void + test_sentinel_traits_nullptr_t() + { + std::nullptr_t z = nullptr; + BOOST_TEST(sentinel_traits::is_sentinel(z)); + BOOST_TEST(sentinel_traits::sentinel() == nullptr); + + // nullable_traits via sentinel + BOOST_TEST(is_null(z)); + auto n = null_of(); + (void) n; // always null + std::nullptr_t t = nullptr; + make_null(t); + BOOST_TEST(is_null(t)); + } + + void + test_sentinel_traits_unsigned() + { + using U = unsigned; + U s = sentinel_traits::sentinel(); + BOOST_TEST(s == static_cast(-1)); + BOOST_TEST(sentinel_traits::is_sentinel(s)); + BOOST_TEST(!sentinel_traits::is_sentinel(static_cast(0))); + + // nullable_traits via sentinel + U v = 7u; + BOOST_TEST(!is_null(v)); + make_null(v); + BOOST_TEST(is_null(v)); + BOOST_TEST(sentinel_traits::is_sentinel(v)); + BOOST_TEST(is_null(null_of())); + } + + void + test_sentinel_traits_floating() + { + using F = double; + F s = sentinel_traits::sentinel(); + BOOST_TEST(std::isnan(s)); + BOOST_TEST(sentinel_traits::is_sentinel(s)); + BOOST_TEST(!sentinel_traits::is_sentinel(0.0)); + + // nullable_traits via sentinel (NaN) + F v = 0.5; + BOOST_TEST(!is_null(v)); + make_null(v); + BOOST_TEST(is_null(v)); + BOOST_TEST(std::isnan(null_of())); + } + + void + test_sentinel_traits_enums_all_variants() + { + // EUnknown + BOOST_TEST(sentinel_traits::is_sentinel(EUnknown::Unknown)); + BOOST_TEST(sentinel_traits::sentinel() == EUnknown::Unknown); + EUnknown eu = EUnknown::A; + BOOST_TEST(!is_null(eu)); + make_null(eu); + BOOST_TEST(is_null(eu)); + BOOST_TEST(null_of() == EUnknown::Unknown); + + // EUNKNOWN + BOOST_TEST(sentinel_traits::is_sentinel(EUNKNOWN::UNKNOWN)); + BOOST_TEST(sentinel_traits::sentinel() == EUNKNOWN::UNKNOWN); + + // ENone + BOOST_TEST(sentinel_traits::is_sentinel(ENone::None)); + BOOST_TEST(sentinel_traits::sentinel() == ENone::None); + + // ENONE + BOOST_TEST(sentinel_traits::is_sentinel(ENONE::NONE)); + BOOST_TEST(sentinel_traits::sentinel() == ENONE::NONE); + } + + void + test_nullable_traits_clearable_empty_string() + { + std::string s; + BOOST_TEST(has_nullable_traits_v); + BOOST_TEST(is_null(s)); // empty + s = "abc"; + BOOST_TEST(!is_null(s)); + make_null(s); // clear() + BOOST_TEST(is_null(s)); + + auto z = null_of(); // default empty + BOOST_TEST(is_null(z)); + } + + void + test_nullable_traits_clearable_empty_vector() + { + std::vector v; + BOOST_TEST(has_nullable_traits_v>); + BOOST_TEST(is_null(v)); + v.push_back(1); + BOOST_TEST(!is_null(v)); + make_null(v); + BOOST_TEST(is_null(v)); + auto z = null_of>(); + BOOST_TEST(is_null(z)); + } + + void + test_negative_no_traits() + { + // No traits ⇒ helpers are not available by constraints; we only assert + // detection. + static_assert(!has_nullable_traits_v); + BOOST_TEST(true); + } + + void + run() + { + test_concepts_and_detection(); + test_sentinel_traits_pointers(); + test_sentinel_traits_nullptr_t(); + test_sentinel_traits_unsigned(); + test_sentinel_traits_floating(); + test_sentinel_traits_enums_all_variants(); + test_nullable_traits_clearable_empty_string(); + test_nullable_traits_clearable_empty_vector(); + test_negative_no_traits(); + + BOOST_TEST(true); + } +}; + +TEST_SUITE(NullableTest, "clang.mrdocs.ADT.Nullable"); + +} // namespace mrdocs diff --git a/src/test/ADT/Optional.cpp b/src/test/ADT/Optional.cpp new file mode 100644 index 000000000..b76b133af --- /dev/null +++ b/src/test/ADT/Optional.cpp @@ -0,0 +1,340 @@ +// +// 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 +// + +#include +#include +#include +#include +#include + +namespace mrdocs { + +struct OptionalTest { + // A fallback-only payload type (no sentinel, no empty()/clear()). + struct NoTraits { + int x = 0; + int + forty_two() const + { + return 42; + } + auto + operator<=>(NoTraits const&) const + = default; + }; + + // An enum that should pick up sentinel via Unknown + enum class Color { + Red, + Green, + Unknown + }; + + void + test_nullable_string() + { + static_assert( + has_nullable_traits_v, + "string should use nullable_traits"); + Optional o; + BOOST_TEST(!o.has_value()); // default-null via empty() + + o.emplace("hi"); + BOOST_TEST(o.has_value()); + BOOST_TEST(*o == "hi"); + BOOST_TEST(o->size() == 2u); + + // value() overloads + { + std::string& lref = o.value(); + BOOST_TEST(&lref == &*o); + + std::string const& clref = std::as_const(o).value(); + BOOST_TEST(&clref == &*std::as_const(o)); + + std::string moved = std::move(o).value(); // value() && + BOOST_TEST(moved == "hi"); + } + + // assign null + o = nullptr; + BOOST_TEST(!o.has_value()); + + // reset() + o.reset(); + BOOST_TEST(!o.has_value()); + + // construct from value + Optional o2{ "abc" }; + BOOST_TEST(o2.has_value()); + BOOST_TEST(o2.value() == "abc"); + + // comparisons (spaceship defaults) + Optional a{ "abc" }, b{ "abc" }, c{ "abd" }; + BOOST_TEST(a == b); + BOOST_TEST(a < c); + } + + void + test_nullable_unsigned() + { + static_assert( + has_nullable_traits_v, + "unsigned should use sentinel -1"); + Optional id; // sentinel == max + BOOST_TEST(!id.has_value()); + + id = 7u; + BOOST_TEST(id.has_value()); + BOOST_TEST(*id == 7u); + + id = static_cast(-1); // assign sentinel -> null + BOOST_TEST(!id.has_value()); + + id.reset(); + BOOST_TEST(!id.has_value()); + + id.emplace(42u); + BOOST_TEST(id.has_value()); + BOOST_TEST(id.value() == 42u); + } + + void + test_nullable_double() + { + static_assert( + has_nullable_traits_v, + "double should use NaN sentinel"); + Optional dop; // NaN -> null + BOOST_TEST(!dop.has_value()); + + dop = 0.0; + BOOST_TEST(dop.has_value()); + BOOST_TEST(*dop == 0.0); + + dop = nullptr; // back to null + BOOST_TEST(!dop.has_value()); + } + + void + test_nullable_enum() + { + static_assert( + has_nullable_traits_v, + "enum with Unknown should have sentinel"); + Optional c; + BOOST_TEST(!c.has_value()); // default to Unknown sentinel + + c = Color::Red; + BOOST_TEST(c.has_value()); + BOOST_TEST(*c == Color::Red); + + c.reset(); + BOOST_TEST(!c.has_value()); + } + + void + test_nullable_location() + { + // Location is nullable when ShortPath is empty (trait specialization + // provided by user) + static_assert( + has_nullable_traits_v, + "Location should use nullable_traits"); + Optional loc; + BOOST_TEST(!loc.has_value()); // default has empty ShortPath + + Location L{ "full.cpp", "short.cpp", "src.cpp", 10u, 0, true }; + Optional a{ L }; + BOOST_TEST(a.has_value()); + BOOST_TEST(a->ShortPath == "short.cpp"); + BOOST_TEST(a->LineNumber == 10u); + BOOST_TEST((*a).Documented == true); + + a = nullptr; + BOOST_TEST(!a.has_value()); + // Invalid: BOOST_TEST(a.value().ShortPath.empty()); + } + + void + test_fallback_notraits() + { + static_assert( + !has_nullable_traits_v, + "NoTraits must fall back to Optional"); + Optional o; + BOOST_TEST(!o.has_value()); // default disengaged + + o.emplace(NoTraits{ 7 }); + BOOST_TEST(o.has_value()); + BOOST_TEST((*o).x == 7); + BOOST_TEST(o->forty_two() == 42); + + // copy / move + Optional copy = o; + BOOST_TEST(copy.has_value()); + BOOST_TEST(copy->x == 7); + + Optional moved = std::move(o); + BOOST_TEST(moved.has_value()); + BOOST_TEST(moved->x == 7); + + // assign value + o = NoTraits{ 9 }; + BOOST_TEST(o.has_value()); + BOOST_TEST(o->x == 9); + + // value() overloads + { + NoTraits& lref = o.value(); + BOOST_TEST(&lref == &*o); + + NoTraits const& clref = std::as_const(o).value(); + BOOST_TEST(&clref == &*std::as_const(o)); + + NoTraits moved_val = std::move(o).value(); + BOOST_TEST(moved_val.x == 9); + } + + // reset and null-assign + o.reset(); + BOOST_TEST(!o.has_value()); + o = nullptr; + BOOST_TEST(!o.has_value()); + + // comparisons + Optional a, b; + BOOST_TEST(a == b); // both disengaged + a.emplace(NoTraits{ 1 }); + b.emplace(NoTraits{ 2 }); + BOOST_TEST(a != b); + BOOST_TEST(a < b); + } + + void + test_reference_optional() + { + static_assert(detail::isOptionalV>); + + int a = 1; + int b = 2; + int c = 3; + + Optional r = a; + BOOST_TEST(r.has_value()); + BOOST_TEST(&*r == &a); + BOOST_TEST(r.value() == 1); + + struct S { + int v{ 0 }; + + int + inc() + { + return ++v; + } + }; + S s{ 7 }; + Optional rs = s; + BOOST_TEST(rs->inc() == 8); + BOOST_TEST((*rs).v == 8); + + r = b; + BOOST_TEST(&*r == &b); + BOOST_TEST(*r == 2); + + // assign from Optional + Optional rr = a; + rr = r; + BOOST_TEST(&*rr == &b); + + // construct from Optional + Optional rr2{ r }; + BOOST_TEST(&*rr2 == &b); + + // Rebinding from Optional + Optional vo = 42; + rr = vo; // rebind to the referent inside vo + BOOST_TEST(&*rr == &*vo); + BOOST_TEST(*rr == *vo); + + // rebind to different lvalue + rr2 = c; + BOOST_TEST(&*rr2 == &c); + BOOST_TEST(*rr2 == 3); + + rr2.reset(); + BOOST_TEST(!rr2.has_value()); + rr2 = a; + BOOST_TEST(&*rr2 == &a); + + Optional copy = rr2; + BOOST_TEST(&*copy == &a); + Optional moved = std::move(rr2); + BOOST_TEST(&*moved == &a); + + Optional rx = b; + Optional ry = c; + rx.swap(ry); + BOOST_TEST(&*rx == &c); + BOOST_TEST(&*ry == &b); + + Optional r1 = a; + Optional r2 = b; + BOOST_TEST(r1 != r2); + BOOST_TEST((r1 < r2) == (a < b)); + + Optional rn; + BOOST_TEST(rn == std::nullopt); + BOOST_TEST((r1 <=> std::nullopt) == std::strong_ordering::greater); + BOOST_TEST((rn <=> std::nullopt) == std::strong_ordering::equal); + + int z = 9; + Optional rz = z; + int& zref = std::move(rz).value(); + BOOST_TEST(&zref == &z); + + static_assert(!std::is_constructible_v, int&&>); + static_assert(!std::is_constructible_v, int const&&>); + static_assert(!std::is_constructible_v, Optional&&>); + static_assert(!std::is_constructible_v, Optional const&&>); + static_assert(!std::is_assignable_v, int&&>); + static_assert(!std::is_assignable_v, int const&&>); + + static_assert(std::is_constructible_v, int&>); + static_assert(std::is_assignable_v, int&>); + static_assert(std::is_constructible_v, Optional&>); + static_assert(std::is_constructible_v, Optional const&>); + static_assert(std::is_assignable_v, Optional&>); + static_assert(std::is_assignable_v, Optional const&>); + + Optional on = 1; + Optional orf = a; + BOOST_TEST(orf == on); + a = 5; + BOOST_TEST(orf != on); + } + + void + run() + { + test_nullable_string(); + test_nullable_unsigned(); + test_nullable_double(); + test_nullable_enum(); + test_nullable_location(); + test_fallback_notraits(); + test_reference_optional(); + } +}; + +TEST_SUITE(OptionalTest, "clang.mrdocs.ADT.Optional"); + +} // namespace mrdocs diff --git a/src/test/ADT/Polymorphic.cpp b/src/test/ADT/Polymorphic.cpp index 20ebe29d3..50d6b5451 100644 --- a/src/test/ADT/Polymorphic.cpp +++ b/src/test/ADT/Polymorphic.cpp @@ -11,7 +11,7 @@ #include #include -namespace clang::mrdocs { +namespace mrdocs { struct X { virtual ~X() = default; @@ -33,16 +33,16 @@ struct Polymorphic_test void testConstructors() { - // nullopt constructor - { - Polymorphic constexpr v(std::nullopt); - BOOST_TEST_NOT(v); - } + // nullopt constructor (removed) + // { + // constexpr Polymorphic v(std::nullopt); + // BOOST_TEST_NOT(v); + // } // from derived object { Polymorphic x(Y{}); - BOOST_TEST(x); + BOOST_TEST(!x.valueless_after_move()); BOOST_TEST(x->a == 42); BOOST_TEST(dynamic_cast(&*x)->b == 43); } @@ -50,7 +50,7 @@ struct Polymorphic_test // from derived { auto x = Polymorphic(std::in_place_type); - BOOST_TEST(x); + BOOST_TEST(!x.valueless_after_move()); BOOST_TEST(x->a == 42); BOOST_TEST(dynamic_cast(&*x)->b == 43); } @@ -58,18 +58,18 @@ struct Polymorphic_test // Copy constructor { // from empty - { - Polymorphic x = std::nullopt; - Polymorphic y(x); - BOOST_TEST_NOT(y); - } + // { + // Polymorphic x = std::nullopt; + // Polymorphic y(x); + // BOOST_TEST_NOT(y); + // } // from valid { Polymorphic x(std::in_place_type); x->a = 45; Polymorphic y(x); - BOOST_TEST(y); + BOOST_TEST(!y.valueless_after_move()); BOOST_TEST(y->a == 45); BOOST_TEST(dynamic_cast(&*y)->b == 43); } @@ -80,26 +80,26 @@ struct Polymorphic_test auto x = Polymorphic(std::in_place_type); x->a = 45; Polymorphic y(std::move(x)); - BOOST_TEST_NOT(x); - BOOST_TEST(y); + BOOST_TEST_NOT(!x.valueless_after_move()); + BOOST_TEST(!y.valueless_after_move()); BOOST_TEST(y->a == 45); BOOST_TEST(dynamic_cast(&*y)->b == 43); } // Copy from derived value constructor - { - Polymorphic x; - x->a = 45; - Polymorphic y(std::in_place_type, *x); - BOOST_TEST(y); - BOOST_TEST(y->a == 45); - BOOST_TEST(dynamic_cast(&*y)->b == 43); - } + // { + // Polymorphic x; + // x->a = 45; + // Polymorphic y(std::in_place_type, *x); + // BOOST_TEST(y); + // BOOST_TEST(y->a == 45); + // BOOST_TEST(dynamic_cast(&*y)->b == 43); + // } // In-place constructor { Polymorphic x(std::in_place_type); - BOOST_TEST(x); + BOOST_TEST(!x.valueless_after_move()); BOOST_TEST(x->a == 42); BOOST_TEST(dynamic_cast(&*x)->b == 43); } @@ -123,7 +123,7 @@ struct Polymorphic_test { Polymorphic lhs(std::in_place_type); lhs = lhs; - BOOST_TEST(lhs); + BOOST_TEST(!lhs.valueless_after_move()); BOOST_TEST(lhs->a == 42); } #if defined(__clang__) @@ -133,12 +133,12 @@ struct Polymorphic_test #pragma GCC diagnostic pop #endif // from empty - { - Polymorphic lhs; - Polymorphic rhs = std::nullopt; - lhs = rhs; - BOOST_TEST_NOT(lhs); - } + // { + // Polymorphic lhs; + // Polymorphic rhs = std::nullopt; + // lhs = rhs; + // BOOST_TEST_NOT(lhs); + // } // from valid { @@ -149,10 +149,10 @@ struct Polymorphic_test BOOST_TEST(lhs->a == 45); BOOST_TEST(rhs->a == 46); lhs = rhs; - BOOST_TEST(lhs); + BOOST_TEST(!lhs.valueless_after_move()); BOOST_TEST(lhs->a == 46); BOOST_TEST(rhs->a == 46); - BOOST_TEST(static_cast(*lhs).b == 43); + BOOST_TEST(dynamic_cast(*lhs).b == 43); } } @@ -170,7 +170,7 @@ struct Polymorphic_test { Polymorphic lhs(std::in_place_type); lhs = std::move(lhs); - BOOST_TEST(lhs); + BOOST_TEST(!lhs.valueless_after_move()); BOOST_TEST(lhs->a == 42); } #if defined(__clang__) @@ -181,13 +181,13 @@ struct Polymorphic_test #endif // from empty - { - Polymorphic lhs; - Polymorphic rhs = std::nullopt; - lhs = std::move(rhs); - BOOST_TEST_NOT(lhs); - BOOST_TEST_NOT(rhs); - } + // { + // Polymorphic lhs; + // Polymorphic rhs = std::nullopt; + // lhs = std::move(rhs); + // BOOST_TEST_NOT(lhs); + // BOOST_TEST_NOT(rhs); + // } // from valid { @@ -199,8 +199,8 @@ struct Polymorphic_test BOOST_TEST(rhs->a == 46); lhs = std::move(rhs); BOOST_TEST(lhs->a == 46); - BOOST_TEST_NOT(rhs); - BOOST_TEST(static_cast(*lhs).b == 43); + BOOST_TEST_NOT(!rhs.valueless_after_move()); + BOOST_TEST(dynamic_cast(*lhs).b == 43); } } @@ -213,9 +213,9 @@ struct Polymorphic_test BOOST_TEST(lhs->a == 45); BOOST_TEST(rhs.a == 46); lhs = Polymorphic(std::in_place_type, rhs); - BOOST_TEST(lhs); + BOOST_TEST(!lhs.valueless_after_move()); BOOST_TEST(lhs->a == 46); - BOOST_TEST(static_cast(*lhs).b == 43); + BOOST_TEST(dynamic_cast(*lhs).b == 43); } // copy from derived @@ -227,9 +227,9 @@ struct Polymorphic_test BOOST_TEST(lhs->a == 45); BOOST_TEST(rhs.a == 46); lhs = Polymorphic(std::in_place_type, std::move(rhs)); - BOOST_TEST(lhs); + BOOST_TEST(!lhs.valueless_after_move()); BOOST_TEST(lhs->a == 46); - BOOST_TEST(static_cast(*lhs).b == 43); + BOOST_TEST(dynamic_cast(*lhs).b == 43); } } @@ -257,36 +257,36 @@ struct Polymorphic_test testSwap() { // default constructor - { - Polymorphic lhs = std::nullopt; - Polymorphic rhs(std::in_place_type); - swap(lhs, rhs); - BOOST_TEST(lhs); - BOOST_TEST(lhs->a == 42); - BOOST_TEST(dynamic_cast(&*lhs)->b == 43); - BOOST_TEST_NOT(rhs); - } + // { + // Polymorphic lhs = std::nullopt; + // Polymorphic rhs(std::in_place_type); + // swap(lhs, rhs); + // BOOST_TEST(lhs); + // BOOST_TEST(lhs->a == 42); + // BOOST_TEST(dynamic_cast(&*lhs)->b == 43); + // BOOST_TEST_NOT(rhs); + // } // rhs: default constructor - { - Polymorphic lhs(std::in_place_type); - Polymorphic rhs = std::nullopt; - swap(lhs, rhs); - BOOST_TEST_NOT(lhs); - BOOST_TEST(rhs); - BOOST_TEST(rhs->a == 42); - BOOST_TEST(dynamic_cast(&*rhs)->b == 43); - } + // { + // Polymorphic lhs(std::in_place_type); + // Polymorphic rhs = std::nullopt; + // swap(lhs, rhs); + // BOOST_TEST_NOT(lhs); + // BOOST_TEST(rhs); + // BOOST_TEST(rhs->a == 42); + // BOOST_TEST(dynamic_cast(&*rhs)->b == 43); + // } // lhs: from derived object { Polymorphic lhs(Y{}); Polymorphic rhs(std::in_place_type); swap(lhs, rhs); - BOOST_TEST(rhs); + BOOST_TEST(!rhs.valueless_after_move()); BOOST_TEST(rhs->a == 42); BOOST_TEST(dynamic_cast(&*rhs)->b == 43); - BOOST_TEST(lhs); + BOOST_TEST(!lhs.valueless_after_move()); BOOST_TEST(lhs->a == 42); BOOST_TEST(dynamic_cast(&*lhs)->b == 43); } @@ -296,10 +296,10 @@ struct Polymorphic_test Polymorphic lhs(std::in_place_type); Polymorphic rhs(Y{}); swap(lhs, rhs); - BOOST_TEST(lhs); + BOOST_TEST(!lhs.valueless_after_move()); BOOST_TEST(lhs->a == 42); BOOST_TEST(dynamic_cast(&*lhs)->b == 43); - BOOST_TEST(rhs); + BOOST_TEST(!rhs.valueless_after_move()); BOOST_TEST(rhs->a == 42); BOOST_TEST(dynamic_cast(&*rhs)->b == 43); } @@ -309,10 +309,10 @@ struct Polymorphic_test Polymorphic lhs(std::in_place_type); Polymorphic rhs(std::in_place_type); swap(lhs, rhs); - BOOST_TEST(rhs); + BOOST_TEST(!rhs.valueless_after_move()); BOOST_TEST(rhs->a == 42); BOOST_TEST(dynamic_cast(&*rhs)->b == 43); - BOOST_TEST(lhs); + BOOST_TEST(!lhs.valueless_after_move()); BOOST_TEST(lhs->a == 42); BOOST_TEST(dynamic_cast(&*lhs)->b == 43); } @@ -332,5 +332,5 @@ TEST_SUITE( Polymorphic_test, "clang.mrdocs.ADT.Polymorphic"); -} // clang::mrdocs +} // mrdocs diff --git a/src/test/Support/Expected.cpp b/src/test/Support/Expected.cpp new file mode 100644 index 000000000..8e7a03a9d --- /dev/null +++ b/src/test/Support/Expected.cpp @@ -0,0 +1,477 @@ +// +// 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 (vinnie.falco@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +// +// 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 +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#include +#include +#include +#include +#include + + +namespace mrdocs { + +struct ExpectedTest +{ + struct S { + int v = 0; + int + get() const noexcept + { + return v; + } + auto + operator<=>(S const&) const + = default; + }; + + // Helpers returning Expected for monadic tests + static + Expected + plus_one(int x) + { + return Expected(x + 1); + } + + static + Expected + fail_with(std::string msg) + { + return Expected(unexpect, Error(std::move(msg))); + } + + static + Expected + ok_void() + { + return Expected(std::in_place); + } + + static + Expected + fail_void(std::string msg) + { + return Expected(unexpect, Error(std::move(msg))); + } + + void + test_value_expected_basic() + { + using E = Error; + + // default-construct value (if T is default-constructible) + { + Expected e(std::in_place, 0); + BOOST_TEST(e.has_value()); + BOOST_TEST(*e == 0); + BOOST_TEST(e.value() == 0); + } + + // construct from value + { + Expected e(42); + BOOST_TEST(e); + BOOST_TEST(*e == 42); + + // copy/move + Expected c = e; + BOOST_TEST(c); + BOOST_TEST(*c == 42); + + Expected m = std::move(e); + BOOST_TEST(m); + BOOST_TEST(*m == 42); + + // assign value + c = 7; + BOOST_TEST(c); + BOOST_TEST(*c == 7); + } + + // construct unexpected + { + Expected e(unexpect, E("bang")); + BOOST_TEST(!e); + BOOST_TEST(e.error().failed()); + BOOST_TEST(!e.has_value()); + // error_or + auto er = e.error_or(E("alt")); + BOOST_TEST(er.failed()); + } + + // assign unexpected + { + Expected e(3); + e = Unexpected(E("nope")); + BOOST_TEST(!e); + BOOST_TEST(e.error().failed()); + } + + // emplace + { + Expected e(unexpect, E("x")); + e.emplace(99); + BOOST_TEST(e); + BOOST_TEST(*e == 99); + } + + // operator-> / operator* with object type + { + Expected es(S{ 5 }); + BOOST_TEST(es->get() == 5); + BOOST_TEST((*es).v == 5); + } + + // value_or + { + Expected a(10); + Expected b(unexpect, E("err")); + BOOST_TEST(a.value_or(1) == 10); + BOOST_TEST(b.value_or(1) == 1); + } + + // and_then (success) + { + Expected e(10); + auto r = e.and_then([](int& x) { return plus_one(x); }); + BOOST_TEST(r); + BOOST_TEST(*r == 11); + } + + // and_then (error propagates) + { + Expected e(unexpect, E("err")); + auto r = e.and_then([](int& x) { return plus_one(x); }); + BOOST_TEST(!r); + BOOST_TEST(r.error().failed()); + } + + // or_else (success path returns same value) + { + Expected e(3); + auto r = e.or_else([](E&) { return fail_with("should-not-run"); }); + BOOST_TEST(r); + BOOST_TEST(*r == 3); + } + + // or_else (error path produces alternate) + { + Expected e(unexpect, E("oops")); + auto r = e.or_else([](E&) { return Expected(7); }); + BOOST_TEST(r); + BOOST_TEST(*r == 7); + } + + // transform maps the value + { + Expected e(8); + auto r = e.transform([](int& x) { return x * 2; }); + static_assert(std::is_same_v>); + BOOST_TEST(r); + BOOST_TEST(*r == 16); + } + + // transform_error maps the error + { + struct MyErr { + std::string s; + }; + Expected e(unexpect, E("bad")); + auto r = e.transform_error([](E& old) { + return MyErr{ old.message() }; + }); + static_assert(std::is_same_v>); + BOOST_TEST(!r); + BOOST_TEST(r.error().s.find("bad") != std::string::npos); + } + + // swap combinations + { + Expected a(1); + Expected b(2); + swap(a, b); + BOOST_TEST(*a == 2); + BOOST_TEST(*b == 1); + + Expected c(unexpect, E("err")); + swap(a, c); + BOOST_TEST(!a); + BOOST_TEST(c); + BOOST_TEST(*c == 2); + } + + // equality (value vs value, error vs error) + { + Expected a(5), b(5), c(6); + Expected xe(unexpect, E("x")), ye(unexpect, E("x")); + BOOST_TEST(a == b); + BOOST_TEST(!(a == c)); + BOOST_TEST(xe == ye); + BOOST_TEST(!(a == xe)); + } + } + + void + test_void_expected_basic() + { + using Ev = Error; + + // default engaged + { + Expected e(std::in_place); + BOOST_TEST(e); + // operator* is a no-op with an assert, we won't call it here. + e.value(); // should not throw + } + + // unexpected + { + Expected e(unexpect, Ev("boom")); + BOOST_TEST(!e); + BOOST_TEST(e.error().failed()); + auto er = e.error_or(Ev("alt")); + BOOST_TEST(er.failed()); + } + + // copy/move assign between states + { + Expected ok(std::in_place); + Expected err(unexpect, Ev("x")); + + ok = err; // ok -> err + BOOST_TEST(!ok); + + err = Expected(std::in_place); // err -> ok + BOOST_TEST(err); + } + + // emplace clears error + { + Expected e(unexpect, Ev("no")); + e.emplace(); + BOOST_TEST(e); + } + + // and_then + { + Expected e(std::in_place); + auto r = e.and_then([] { return ok_void(); }); + BOOST_TEST(r); + } + { + Expected e(unexpect, Ev("n")); + auto r = e.and_then([] { return ok_void(); }); + BOOST_TEST(!r); + } + + // or_else + { + Expected e(unexpect, Ev("nope")); + auto r = e.or_else([](Ev&) { return ok_void(); }); + BOOST_TEST(r); + } + + // transform to non-void + { + Expected e(std::in_place); + auto r = e.transform([] { return 17; }); + static_assert(std::is_same_v>); + BOOST_TEST(r); + BOOST_TEST(*r == 17); + } + + // transform_error + { + struct MyErr { + std::string s; + }; + Expected e(unexpect, Ev("zzz")); + auto r = e.transform_error([](Ev& old) { + return MyErr{ old.message() }; + }); + static_assert(std::is_same_v>); + BOOST_TEST(!r); + BOOST_TEST(r.error().s.find("zzz") != std::string::npos); + } + + // swap + { + Expected a(std::in_place); + Expected b(unexpect, Ev("e")); + swap(a, b); + BOOST_TEST(!a); + BOOST_TEST(b); + } + + // equality + { + Expected ok1(std::in_place), ok2(std::in_place); + Expected er1(unexpect, Ev("e1")), er2(unexpect, Ev("e1")); + BOOST_TEST(ok1 == ok2); + BOOST_TEST(er1 == er2); + BOOST_TEST(!(ok1 == er1)); + } + } + + void + test_reference_expected_basic() + { + using E = Error; + + int x = 10; + int y = 20; + + // bind to lvalue, arrow/deref/value + { + Expected er(std::in_place, x); + BOOST_TEST(er); + BOOST_TEST(&er.value() == &x); + BOOST_TEST(*er == 10); + + er = y; // rebinding + BOOST_TEST(&er.value() == &y); + BOOST_TEST(*er == 20); + } + + // construct from Expected& (binds to contained lvalue) + { + Expected ev(std::in_place, 42); + Expected er(std::in_place, x); + er = ev; // rebind to ev.value() + BOOST_TEST(&er.value() == &ev.value()); + BOOST_TEST(*er == 42); + } + + // value_or returns by value (copy) + { + Expected er(std::in_place, x); + Expected bad(unexpect, E("err")); + auto v1 = er.value_or(5); + auto v2 = bad.value_or(5); + static_assert(std::is_same_v); + BOOST_TEST(v1 == 10); + BOOST_TEST(v2 == 5); + } + + // error transitions + { + Expected er(unexpect, E("e")); + BOOST_TEST(!er); + er = x; // rebind from error -> success + BOOST_TEST(er); + BOOST_TEST(&er.value() == &x); + } + + // transform + { + Expected er(std::in_place, x); + auto r = er.transform([](int& r) { return r * 3; }); + static_assert(std::is_same_v>); + BOOST_TEST(r); + BOOST_TEST(*r == 30); + } + + // and_then (success) + { + Expected er(std::in_place, x); + auto r = er.and_then([](int& r) { return plus_one(r); }); + BOOST_TEST(r); + BOOST_TEST(*r == 11); + } + + // and_then (error propagates) + { + Expected er(unexpect, E("err")); + auto r = er.and_then([](int& r) { return plus_one(r); }); + BOOST_TEST(!r); + } + + // transform_error changes error type, preserves binding semantics on + // success + { + struct MyErr { + std::string s; + }; + + Expected ok(std::in_place, x); + auto r1 = ok.transform_error([](E& e) { + return MyErr{ e.message() }; + }); + static_assert(std::is_same_v>); + BOOST_TEST(r1); + BOOST_TEST(&r1.value() == &x); + + Expected bad(unexpect, E("xx")); + auto r2 = bad.transform_error([](E& e) { + return MyErr{ e.message() }; + }); + BOOST_TEST(!r2); + BOOST_TEST(r2.error().s.find("xx") != std::string::npos); + } + + // swap: value<->value and value<->error + { + Expected a(std::in_place, x); + Expected b(std::in_place, y); + swap(a, b); + BOOST_TEST(&a.value() == &y); + BOOST_TEST(&b.value() == &x); + + Expected c(unexpect, E("err")); + swap(a, c); + BOOST_TEST(!a); + BOOST_TEST(c); + BOOST_TEST(&c.value() == &y); + } + + // equality (value vs value, error vs error) + { + Expected a(std::in_place, x); + Expected b(std::in_place, x); + Expected c(std::in_place, y); + Expected xe(unexpect, E("e")), ye(unexpect, E("e")); + + BOOST_TEST(a == b); + BOOST_TEST(!(a == c)); + BOOST_TEST(xe == ye); + BOOST_TEST(!(a == xe)); + } + + // deleted constructs/assignments from temporaries (compile-time checks) + { + static_assert(!std::is_constructible_v, int&&>); + static_assert(!std::is_assignable_v, int&&>); + static_assert(!std::is_constructible_v, Expected&&>); + static_assert(!std::is_assignable_v, Expected&&>); + } + } + + void + run() + { + test_value_expected_basic(); + test_void_expected_basic(); + test_reference_expected_basic(); + } +}; + +TEST_SUITE( + ExpectedTest, + "clang.mrdocs.Expected"); + +} // mrdocs + diff --git a/src/test/Support/Glob.cpp b/src/test/Support/Glob.cpp index be83be4fd..51979f302 100644 --- a/src/test/Support/Glob.cpp +++ b/src/test/Support/Glob.cpp @@ -11,7 +11,7 @@ #include #include -namespace clang::mrdocs { +namespace mrdocs { struct Glob_test { @@ -609,5 +609,5 @@ TEST_SUITE( Glob_test, "clang.mrdocs.Glob"); -} // clang::mrdocs +} // mrdocs diff --git a/src/test/Support/JavaScript.cpp b/src/test/Support/JavaScript.cpp index e8e7ec9e3..c9edbe9a1 100644 --- a/src/test/Support/JavaScript.cpp +++ b/src/test/Support/JavaScript.cpp @@ -8,12 +8,12 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include #include +#include #include #include -namespace clang { + namespace mrdocs { namespace js { @@ -1306,5 +1306,5 @@ TEST_SUITE( } // js } // mrdocs -} // clang + diff --git a/src/test/TestArgs.cpp b/src/test/TestArgs.cpp index 067616748..44e33bf59 100644 --- a/src/test/TestArgs.cpp +++ b/src/test/TestArgs.cpp @@ -11,7 +11,7 @@ #include "TestArgs.hpp" #include -namespace clang::mrdocs { +namespace mrdocs { TestArgs TestArgs::instance_; @@ -83,4 +83,4 @@ hideForeignOptions() const } } -} // clang::mrdocs +} // mrdocs diff --git a/src/test/TestArgs.hpp b/src/test/TestArgs.hpp index 2ee9f2b17..d1480d4d8 100644 --- a/src/test/TestArgs.hpp +++ b/src/test/TestArgs.hpp @@ -12,10 +12,10 @@ #define MRDOCS_TEST_TESTARGS_HPP #include -#include #include +#include + -namespace clang { namespace mrdocs { enum Action : int @@ -54,6 +54,6 @@ class TestArgs : public PublicToolArgs constexpr static TestArgs& testArgs = TestArgs::instance_; } // mrdocs -} // clang + #endif diff --git a/src/test/TestMain.cpp b/src/test/TestMain.cpp index 4b7e52f66..85fb91df3 100644 --- a/src/test/TestMain.cpp +++ b/src/test/TestMain.cpp @@ -8,29 +8,29 @@ // Official repository: https://github.com/cppalliance/mrdocs // +#include #include "TestArgs.hpp" #include "TestRunner.hpp" -#include "lib/Support/Debug.hpp" -#include "lib/Support/Report.hpp" -#include -#include +#include +#include #include #include +#include #include #include -#include #include #include +#include #include int main(int argc, char const** argv); -namespace clang { + namespace mrdocs { void DoTestAction(char const** argv) { - using namespace clang::mrdocs; + using namespace mrdocs; std::vector testPaths( testArgs.cmdLineInputs.begin(), @@ -134,25 +134,25 @@ static void reportUnhandledException( #endif } // mrdocs -} // clang + int main(int argc, char const** argv) { #ifndef _NDEBUG - return clang::mrdocs::test_main(argc, argv); + return mrdocs::test_main(argc, argv); #else try { - return clang::mrdocs::test_main(argc, argv); + return mrdocs::test_main(argc, argv); } - catch(clang::mrdocs::Exception const& ex) + catch(mrdocs::Exception const& ex) { // thrown Exception should never get here. - clang::mrdocs::reportUnhandledException(ex); + mrdocs::reportUnhandledException(ex); } catch(std::exception const& ex) { - clang::mrdocs::reportUnhandledException(ex); + mrdocs::reportUnhandledException(ex); } return EXIT_FAILURE; #endif diff --git a/src/test/TestRunner.cpp b/src/test/TestRunner.cpp index 31ea0e38d..13de6b806 100644 --- a/src/test/TestRunner.cpp +++ b/src/test/TestRunner.cpp @@ -8,21 +8,21 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include "TestRunner.hpp" +#include #include "TestArgs.hpp" -#include "lib/Support/Report.hpp" -#include "lib/Support/Path.hpp" -#include "lib/ConfigImpl.hpp" -#include "lib/CorpusImpl.hpp" -#include "lib/Support/ExecuteAndWaitWithLogging.hpp" -#include "lib/MrDocsCompilationDatabase.hpp" -#include "lib/SingleFileDB.hpp" -#include "lib/Gen/hbs/HandlebarsGenerator.hpp" -#include "test_suite/diff.hpp" +#include "TestRunner.hpp" +#include +#include +#include +#include +#include +#include +#include +#include #include #include -#include #include +#include #include #include #include @@ -30,7 +30,7 @@ #include #include -namespace clang::mrdocs { +namespace mrdocs { TestRunner:: TestRunner(std::string_view generator) @@ -394,4 +394,4 @@ checkPath( } } -} // clang::mrdocs +} // mrdocs diff --git a/src/test/TestRunner.hpp b/src/test/TestRunner.hpp index 2642a2262..6e8974f17 100644 --- a/src/test/TestRunner.hpp +++ b/src/test/TestRunner.hpp @@ -11,7 +11,7 @@ #ifndef MRDOCS_TEST_TESTRUNNER_HPP #define MRDOCS_TEST_TESTRUNNER_HPP -#include "lib/ConfigImpl.hpp" +#include #include #include #include @@ -21,7 +21,7 @@ #include #include -namespace clang { + namespace mrdocs { struct TestResults @@ -84,6 +84,6 @@ class TestRunner }; } // mrdocs -} // clang -#endif + +#endif // MRDOCS_TEST_TESTRUNNER_HPP diff --git a/src/test/lib/AST/ParseRef.cpp b/src/test/lib/AST/ParseRef.cpp index a7837e711..532f975e1 100644 --- a/src/test/lib/AST/ParseRef.cpp +++ b/src/test/lib/AST/ParseRef.cpp @@ -5,12 +5,12 @@ // https://www.boost.org/LICENSE_1_0.txt // +#include #include #include #include -#include "lib/AST/ParseRef.hpp" -namespace clang::mrdocs { +namespace mrdocs { struct ParseRef_test { @@ -321,4 +321,4 @@ TEST_SUITE( ParseRef_test, "clang.mrdocs.ParseRef"); -} // clang::mrdocs +} // mrdocs diff --git a/src/test/lib/Dom/Dom.cpp b/src/test/lib/Dom/Dom.cpp index 4e5f17861..cc688ab27 100644 --- a/src/test/lib/Dom/Dom.cpp +++ b/src/test/lib/Dom/Dom.cpp @@ -8,11 +8,11 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include #include #include +#include + -namespace clang { namespace mrdocs { namespace dom { @@ -227,7 +227,7 @@ struct Dom_test BOOST_TEST(s3 == "helloworld"); } - // std::formatter + // std::formatter { String s("hello"); BOOST_TEST(std::format("{}", s) == "hello"); @@ -1132,16 +1132,16 @@ struct Dom_test BOOST_TEST(v == "hello"); } - // Value(std::optional const& opt) + // Value(Optional const& opt) { { - std::optional opt; + Optional opt; Value v(opt); BOOST_TEST(v.isUndefined()); } { - std::optional opt(123); + Optional opt(123); Value v(opt); BOOST_TEST(v.isInteger()); BOOST_TEST(v == 123); @@ -2154,5 +2154,5 @@ TEST_SUITE( } // dom } // mrdocs -} // clang + diff --git a/src/test/lib/Dom/Function.cpp b/src/test/lib/Dom/Function.cpp index 581802b3f..10bdf0b4a 100644 --- a/src/test/lib/Dom/Function.cpp +++ b/src/test/lib/Dom/Function.cpp @@ -11,7 +11,7 @@ #include #include -namespace clang { + namespace mrdocs { namespace dom { @@ -43,5 +43,5 @@ TEST_SUITE( } // dom } // mrdocs -} // clang + diff --git a/src/test/lib/Dom/LazyObject.cpp b/src/test/lib/Dom/LazyObject.cpp index f280be3fa..4139d3325 100644 --- a/src/test/lib/Dom/LazyObject.cpp +++ b/src/test/lib/Dom/LazyObject.cpp @@ -11,7 +11,7 @@ #include #include -namespace clang { + namespace mrdocs { namespace dom { @@ -251,5 +251,5 @@ TEST_SUITE( } // dom } // mrdocs -} // clang + diff --git a/src/test/lib/Metadata/Finalizers/Javadoc/parseInlines.cpp b/src/test/lib/Metadata/Finalizers/Javadoc/parseInlines.cpp new file mode 100644 index 000000000..7e1ffbb4a --- /dev/null +++ b/src/test/lib/Metadata/Finalizers/Javadoc/parseInlines.cpp @@ -0,0 +1,322 @@ +// +// 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 +// + +#include +#include +#include +#include +#include + +namespace mrdocs { +namespace { + +using namespace doc; + +// ---------- helpers to drive the public parser and assert results + +std::string +dumpInline(Inline const& n); + +// Dump all children of a container, concatenated +std::string +dumpContainer(InlineContainer const& c) +{ + std::string r; + for (auto const& p: c.children) + { + r += dumpInline(*p); + } + return r; +} + +std::string +esc(std::string const& s) +{ + std::string out; + out.reserve(s.size()); + for (char ch: s) + { + if (ch == '\\') + { + out += "\\\\"; + } + else if (ch == '"') + { + out += "\\\""; + } + else + { + out += ch; + } + } + return out; +} + +// Dump a single Inline node as a string with its type and key data +std::string +dumpInline(Inline const& n) +{ + switch (n.Kind) + { + case InlineKind::Text: + return "T(\"" + esc(n.asText().literal) + "\")"; + case InlineKind::Emph: + return "Em{" + dumpContainer(n.asEmph()) + "}"; + case InlineKind::Strong: + return "Str{" + dumpContainer(n.asStrong()) + "}"; + case InlineKind::Strikethrough: + return "Del{" + dumpContainer(n.asStrikethrough()) + "}"; + case InlineKind::Highlight: + return "Mark{" + dumpContainer(n.asHighlight()) + "}"; + case InlineKind::Superscript: + return "Sup{" + dumpContainer(n.asSuperscript()) + "}"; + case InlineKind::Subscript: + return "Sub{" + dumpContainer(n.asSubscript()) + "}"; + case InlineKind::Code: + return "Code{" + dumpContainer(n.asCode()) + "}"; + case InlineKind::LineBreak: + return "BR"; + case InlineKind::SoftBreak: + return "SBR"; + case InlineKind::Link: + return "A(href=\"" + esc(n.asLink().href) + "\"){" + + dumpContainer(n.asLink()) + "}"; + case InlineKind::Image: + return "IMG(src=\"" + esc(n.asImage().src) + "\",alt=\"" + + esc(n.asImage().alt) + "\")"; + default: + // If new kinds appear, fail loudly so tests get updated + assert(false && "Unhandled InlineKind in dumpInline"); + return {}; + } +} + +std::string +parse_to_dump(std::string_view in) +{ + InlineContainer root; + auto res = parse(in.data(), in.data() + in.size(), root); + (void) res; + return dumpContainer(root); +} + +void +expect_dump(std::string_view input, std::string_view expect) +{ + auto got = parse_to_dump(input); + BOOST_TEST_EQ(expect, got); +} + +} // namespace + +// ---------------------------------- Tests ------------------------------------ + +struct parseInlinesTest { + void + test_plain_and_merge() + { + // Plain text + merge of consecutive text nodes + // Adjacent text from literal degradation should merge too. + // This is exercised later. + expect_dump("hello world", R"(T("hello world"))"); + } + + void + test_escaping() + { + // Backslash escapes next char + expect_dump(R"(foo\*bar)", R"(T("foo*bar"))"); + // Two backslashes before special -> one escapes the other + expect_dump(R"(foo\\*bar)", R"(T("foo\\*bar"))"); + expect_dump(R"(*foo)", R"(T("*foo"))"); + expect_dump(R"(__x)", R"(T("__x"))"); + expect_dump(R"(~~x)", R"(T("~~x"))"); + } + + void + test_markdown_basic() + { + expect_dump("*em*", R"(Em{T("em")})"); + expect_dump("**strong**", R"(Str{T("strong")})"); + expect_dump("~~strike~~", R"(Del{T("strike")})"); + expect_dump("==mark==", R"(Mark{T("mark")})"); + expect_dump("^sup^", R"(Sup{T("sup")})"); + expect_dump("~sub~", R"(Sub{T("sub")})"); + expect_dump("`code`", R"(Code{T("code")})"); + + // mixed and interleaved with text + expect_dump("pre *em* mid **st** end", + R"(T("pre ")Em{T("em")}T(" mid ")Str{T("st")}T(" end"))"); + expect_dump("a ~~del~~ b ==mark== c ^sup^ d ~sub~ e", + R"(T("a ")Del{T("del")}T(" b ")Mark{T("mark")}T(" c ")Sup{T("sup")}T(" d ")Sub{T("sub")}T(" e"))"); + + // nested inline elements + expect_dump("**a *b* c**", + R"(Str{T("a ")Em{T("b")}T(" c")})"); + expect_dump("*em **strong** em*", + R"(Em{T("em ")Str{T("strong")}T(" em")})"); + expect_dump("~~x ==y== z~~", + R"(Del{T("x ")Mark{T("y")}T(" z")})"); + expect_dump("**x ~y~ z**", + R"(Str{T("x ")Sub{T("y")}T(" z")})"); + expect_dump("==m ^s^ n==", + R"(Mark{T("m ")Sup{T("s")}T(" n")})"); + + // nested with a barrier inside (markdown code should not parse inner markup) + expect_dump("**a `b * c` d**", + R"(Str{T("a ")Code{T("b * c")}T(" d")})"); + } + + void + test_flanking_intraword_rules() + { + // '_' and '__' are NoIntraWord; shouldn't trigger inside identifiers + expect_dump("foo_bar_baz", R"(T("foo_bar_baz"))"); + expect_dump("foo__bar__baz", R"(T("foo__bar__baz"))"); + + // '*' is allowed intraword; still needs flanking + expect_dump("foo*bar*", R"(T("foo")Em{T("bar")})"); + + // Leading/trailing spaces affect flank + // not left-flanking (space after token) + expect_dump("* a*", R"(T("* a*"))"); + // not right-flanking (space before close) + expect_dump("*a *", R"(T("*a *"))"); + } + + void + test_barrier_code_span() + { + // Everything inside backticks is literal text children of Code + expect_dump("`a*b_[c]`", R"(Code{T("a*b_[c]")})"); + + // Ensure barrier close path: multiple text pushes until matching '`' + expect_dump("x`y`z", R"(T("x")Code{T("y")}T("z"))"); + + // Unclosed barrier at EOF -> literal fallback of opening + contents + expect_dump("pre `code", R"(T("pre `code"))"); + } + + void + test_html_phrasing_and_breaks() + { + // Simple phrasing tags open/close + expect_dump("x", R"(Em{T("x")})"); + expect_dump("x", R"(Str{T("x")})"); + expect_dump("x", R"(Code{T("x")})"); + expect_dump("x", R"(Sub{T("x")})"); + expect_dump("x", R"(Sup{T("x")})"); + expect_dump("x", R"(Del{T("x")})"); + expect_dump("x", R"(Mark{T("x")})"); + + //
becomes hard break + expect_dump("a
b", R"(T("a")BRT("b"))"); + + // Unknown tag -> literal + expect_dump("bar", R"(T("bar"))"); + } + + void + test_html_a_img_attrs() + { + // with content + expect_dump("y", R"(A(href="/x"){T("y")})"); + + // Attribute permutations and spaces + expect_dump( + "t", + R"(A(href="/p?q=1"){T("t")})"); + + // with src/alt, self-contained + expect_dump( + R"(pic)", + R"(IMG(src="/i.png",alt="pic"))"); + + // Missing attributes tolerated → empty strings + expect_dump("", R"(IMG(src="",alt=""))"); + } + + void + test_markdown_links_and_images() + { + // Link: [label](dest) + expect_dump("[x](y)", R"(A(href="y"){T("x")})"); + + // Quoted destination and title (title ignored in current model, but + // path still consumed) + expect_dump(R"([x]("/p?q" "t"))", R"(A(href="/p?q"){T("x")})"); + + // Image: ![alt](src) + expect_dump("![pic](/i.png)", R"(IMG(src="/i.png",alt="pic"))"); + + // Invalid: missing closing paren → degrade to literal "[label]" + expect_dump("[x](y", R"(T("[")T("x")T("]"))"); + + // Unmatched '[' at EOF → literal + expect_dump("[unclosed", R"(T("[")T("unclosed")T("]"))"); + + // Unmatched '!' '[' at EOF → literal + expect_dump("![alt", R"(T("![")T("alt")T("]"))"); + } + + void + test_implicit_close_crossing() + { + // Strong opened, then emph opened, emph closes, then strong closes + // (proper nesting) + expect_dump("**a *b* c**", R"(Str{T("a ")Em{T("b")}T(" c")})"); + + // Crossing with implicit-close allowed: ~~ can close past * if needed + // '*' left unclosed → literal later + expect_dump( + "~~a *b~~ c*", + R"(Del{T("a *b")}T(" c*"))"); + } + + void + test_closing_without_open() + { + // Lone closing tokens should be literal text because close_to_kind fails + // first '**' treated as literal, then '*' parsed + expect_dump("**a*b", R"(T("**a*b"))"); + expect_dump(") stray", R"(T(") stray"))"); + } + + void + test_unclosed_frames_fallback_literal() + { + // Open emph but never close → literal '*' + contents + expect_dump("*x", R"(T("*x"))"); + + // Open HTML but never close → literal open + contents + // open_tok for HTML containers is "" + expect_dump("x", R"(Em{T("x")})"); + } + + void + run() + { + test_plain_and_merge(); + test_escaping(); + test_markdown_basic(); + test_flanking_intraword_rules(); + test_barrier_code_span(); + test_html_phrasing_and_breaks(); + test_html_a_img_attrs(); + test_markdown_links_and_images(); + test_implicit_close_crossing(); + test_closing_without_open(); + test_unclosed_frames_fallback_literal(); + } +}; + +TEST_SUITE(parseInlinesTest, "clang.mrdocs.Metadata.Finalizers.Javadoc.parseInlines"); + +} // namespace mrdocs diff --git a/src/test/lib/Support/Handlebars.cpp b/src/test/lib/Support/Handlebars.cpp index 7a0a10c74..b54c6a139 100644 --- a/src/test/lib/Support/Handlebars.cpp +++ b/src/test/lib/Support/Handlebars.cpp @@ -5,10 +5,6 @@ // https://www.boost.org/LICENSE_1_0.txt // -#include -#include -#include -#include #include #include #include @@ -16,9 +12,13 @@ #include #include #include +#include +#include +#include +#include #include -namespace clang { + namespace mrdocs { template DomValue> @@ -116,10 +116,10 @@ setup_context() const page.set("name", "from_chars"); page.set("decl", "std::from_chars"); page.set("loc", "charconv"); - dom::Object javadoc; - javadoc.set("brief", "Converts strings to numbers"); - javadoc.set("details", "This function converts strings to numbers"); - page.set("javadoc", javadoc); + dom::Object doc; + doc.set("brief", "Converts strings to numbers"); + doc.set("details", "This function converts strings to numbers"); + page.set("doc", doc); page.set("synopsis", "This is the from_chars function"); dom::Object person; person.set("firstname", "John"); @@ -5270,19 +5270,19 @@ to_dom(llvm::json::Value& val) } // val is string - std::optional str_opt = val.getAsString(); + Optional str_opt = val.getAsString(); if (str_opt) { return str_opt.value().str(); } // val is integer - std::optional int_opt = val.getAsInteger(); + Optional int_opt = val.getAsInteger(); if (int_opt) { return int_opt.value(); } // val is double (convert to string) - std::optional num_opt = val.getAsNumber(); + Optional num_opt = val.getAsNumber(); if (num_opt) { std::string double_str = std::to_string(num_opt.value()); double_str.erase(double_str.find_last_not_of('0') + 1, std::string::npos); @@ -5290,7 +5290,7 @@ to_dom(llvm::json::Value& val) } // val is bool - std::optional bool_opt = val.getAsBoolean(); + Optional bool_opt = val.getAsBoolean(); if (bool_opt) { return bool_opt.value(); } @@ -5445,4 +5445,4 @@ TEST_SUITE( "clang.mrdocs.Handlebars"); } // mrdocs -} // clang + diff --git a/src/test/lib/Support/Path.cpp b/src/test/lib/Support/Path.cpp index 80649d59a..17c3b11de 100644 --- a/src/test/lib/Support/Path.cpp +++ b/src/test/lib/Support/Path.cpp @@ -8,10 +8,10 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include "lib/Support/Path.hpp" +#include #include -namespace clang { + namespace mrdocs { struct Path_test @@ -76,5 +76,5 @@ TEST_SUITE( "clang.mrdocs.Path"); } // mrdocs -} // clang + diff --git a/src/test_suite/detail/decomposer.cpp b/src/test_suite/detail/decomposer.cpp index 97a11fc86..d3f937b1b 100644 --- a/src/test_suite/detail/decomposer.cpp +++ b/src/test_suite/detail/decomposer.cpp @@ -10,7 +10,7 @@ #include "decomposer.hpp" #if __has_include() -#include +# include #endif namespace test_suite { diff --git a/src/test_suite/detail/decomposer.hpp b/src/test_suite/detail/decomposer.hpp index 21d2a7011..d678e0d97 100644 --- a/src/test_suite/detail/decomposer.hpp +++ b/src/test_suite/detail/decomposer.hpp @@ -5,8 +5,8 @@ // https://www.boost.org/LICENSE_1_0.txt // -#ifndef MRDOCS_TEST_DECOMPOSER_HPP -#define MRDOCS_TEST_DECOMPOSER_HPP +#ifndef MRDOCS_TEST_SUITE_DETAIL_DECOMPOSER_HPP +#define MRDOCS_TEST_SUITE_DETAIL_DECOMPOSER_HPP #include #include @@ -117,7 +117,7 @@ namespace test_suite::detail binary_operands operator==(first_operand &&lhs, U &&rhs) { - if constexpr (integral_comparison) + if constexpr (integral_comparison && !std::same_as>) { return {std::cmp_equal( lhs.lhs_, rhs ), lhs.lhs_, "==", rhs}; } @@ -287,4 +287,4 @@ namespace test_suite::detail # define DETAIL_SUPPRESS_PARENTHESES_WARNINGS #endif -#endif //MRDOCS_TEST_DECOMPOSER_HPP +#endif // MRDOCS_TEST_SUITE_DETAIL_DECOMPOSER_HPP diff --git a/src/test_suite/diff.cpp b/src/test_suite/diff.cpp index 341bba588..3d0b8f794 100644 --- a/src/test_suite/diff.cpp +++ b/src/test_suite/diff.cpp @@ -31,7 +31,8 @@ diffStrings(std::string_view str1, std::string_view str2, std::size_t context_si { newPos = text.length(); } - lines.push_back(text.substr(pos, newPos - pos)); + auto line = text.substr(pos, newPos - pos); + lines.push_back(line); pos = newPos + 1; } }; @@ -81,7 +82,9 @@ diffStrings(std::string_view str1, std::string_view str2, std::size_t context_si { for (size_t j = 0; j < lines2.size(); ++j) { - if (trim_spaces(lines1[i]) == trim_spaces(lines2[j])) + auto line1 = trim_spaces(lines1[i]); + auto line2 = trim_spaces(lines2[j]); + if (line1 == line2) { // If the lines are equal, it means they contribute to the common subsequence. // In this case, the value in the current cell lcsTable[i + 1][j + 1] is set @@ -119,12 +122,14 @@ diffStrings(std::string_view str1, std::string_view str2, std::size_t context_si // the adjacent cells to determine the direction of the LCS while (i > 0 && j > 0) { - if (lines1[i - 1] == lines2[j - 1]) + auto line1 = trim_spaces(lines1[i - 1]); + auto line2 = trim_spaces(lines2[j - 1]); + if (line1 == line2) { // If the current lines lines1[i-1] and lines2[j-1] are equal, // it means the line is common to both multiline strings. It // is added to diffLines with a space prefix, indicating no change. - diffLines.push_back({std::string(lines1[i - 1]), false, false}); + diffLines.push_back({std::string(line1), false, false}); --i; --j; result.unmodified++; @@ -136,7 +141,7 @@ diffStrings(std::string_view str1, std::string_view str2, std::size_t context_si // lcsTable[i-1][j], it means the line in lines2[j-1] is // part of the LCS. Thus, it is added to diffLines with // a "+" prefix to indicate an addition. - diffLines.push_back({std::string(lines2[j - 1]), true, false}); + diffLines.push_back({std::string(line2), true, false}); --j; result.added++; } @@ -144,7 +149,7 @@ diffStrings(std::string_view str1, std::string_view str2, std::size_t context_si { // Otherwise, the line in lines1[i-1] is part of the LCS, and it // is added to diffLines with a "-" prefix to indicate a deletion. - diffLines.push_back({std::string(lines1[i - 1]), false, true}); + diffLines.push_back({std::string(line1), false, true}); --i; result.removed++; } @@ -152,14 +157,16 @@ diffStrings(std::string_view str1, std::string_view str2, std::size_t context_si while (i > 0) { - diffLines.push_back({std::string(lines1[i - 1]), false, true}); + auto line1 = lines1[i - 1]; + diffLines.push_back({std::string(line1), false, true}); --i; result.removed++; } while (j > 0) { - diffLines.push_back({std::string(lines2[j - 1]), true, false}); + auto line2 = lines2[j - 1]; + diffLines.push_back({std::string(line2), true, false}); --j; result.added++; } @@ -196,7 +203,7 @@ diffStrings(std::string_view str1, std::string_view str2, std::size_t context_si } } - // Concatenate diff lines into a single string considering number + // Concatenate diff lines into a single string considering the number // of unmodified lines in the context std::size_t out_of_context = 0; for (auto diffLine : diffLines) @@ -265,8 +272,6 @@ BOOST_TEST_DIFF( BOOST_TEST(diff.added == 0); BOOST_TEST(diff.removed == 0); } - BOOST_TEST(rendered_contents.size() == expected_contents.size()); - BOOST_TEST((rendered_contents == expected_contents)); } } diff --git a/src/test_suite/diff.hpp b/src/test_suite/diff.hpp index 50665b457..4af4e4c60 100644 --- a/src/test_suite/diff.hpp +++ b/src/test_suite/diff.hpp @@ -5,8 +5,8 @@ // https://www.boost.org/LICENSE_1_0.txt // -#ifndef MRDOCS_TEST_DIFF_HPP -#define MRDOCS_TEST_DIFF_HPP +#ifndef MRDOCS_TEST_SUITE_DIFF_HPP +#define MRDOCS_TEST_SUITE_DIFF_HPP #include #include @@ -76,4 +76,4 @@ BOOST_TEST_DIFF( } -#endif //MRDOCS_TEST_DIFF_HPP +#endif // MRDOCS_TEST_SUITE_DIFF_HPP diff --git a/src/test_suite/test_suite.cpp b/src/test_suite/test_suite.cpp index d502b7840..0621a41eb 100644 --- a/src/test_suite/test_suite.cpp +++ b/src/test_suite/test_suite.cpp @@ -8,7 +8,6 @@ // #include "test_suite.hpp" - #include #include #include @@ -20,10 +19,12 @@ #ifdef _MSC_VER #define WIN32_LEAN_AND_MEAN +// clang-format off #include #include #include #include +// clang-format on #endif namespace test_suite { diff --git a/src/test_suite/test_suite.hpp b/src/test_suite/test_suite.hpp index ec16adbd3..1a41ee773 100644 --- a/src/test_suite/test_suite.hpp +++ b/src/test_suite/test_suite.hpp @@ -7,18 +7,18 @@ // Official repository: https://github.com/boostorg/url // -#ifndef MRDOCS_TEST_HPP -#define MRDOCS_TEST_HPP +#ifndef MRDOCS_TEST_SUITE_TEST_SUITE_HPP +#define MRDOCS_TEST_SUITE_TEST_SUITE_HPP #if defined(_MSC_VER) # pragma once #endif //#include +#include #include #include #include -#include "detail/decomposer.hpp" // This is a derivative work // Copyright 2002-2018 Peter Dimov @@ -250,15 +250,15 @@ constexpr detail::log_type log{}; if (!(static_cast(__VA_ARGS__))) \ { \ DETAIL_START_WARNINGS_SUPPRESSION \ - std::string d = DETAIL_STRINGIFY(__VA_ARGS__); \ - d += "\n ("; \ + std::string detail_boost_test_error_str = DETAIL_STRINGIFY(__VA_ARGS__); \ + detail_boost_test_error_str += "\n ("; \ DETAIL_SUPPRESS_PARENTHESES_WARNINGS \ - d += (test_suite::detail::decomposer() <= __VA_ARGS__).format(); \ + detail_boost_test_error_str += (test_suite::detail::decomposer() <= __VA_ARGS__).format(); \ DETAIL_STOP_WARNINGS_SUPPRESSION \ - d += ")"; \ + detail_boost_test_error_str += ")"; \ return ::test_suite::detail::test_impl( \ false, \ - d.data(), \ + detail_boost_test_error_str.data(), \ "@anon", \ __FILE__, \ __LINE__ ); \ @@ -385,4 +385,4 @@ extern int unit_test_main(int argc, char const* const* argv); } // test_suite -#endif +#endif // MRDOCS_TEST_SUITE_TEST_SUITE_HPP diff --git a/src/tool/CompilerInfo.cpp b/src/tool/CompilerInfo.cpp index c81e381ac..595cbe865 100644 --- a/src/tool/CompilerInfo.cpp +++ b/src/tool/CompilerInfo.cpp @@ -10,15 +10,14 @@ // #include "CompilerInfo.hpp" -#include "lib/Support/ExecuteAndWaitWithLogging.hpp" +#include #include - #include -namespace clang { + namespace mrdocs { -std::optional +Optional getCompilerVerboseOutput(llvm::StringRef compilerPath) { if ( ! llvm::sys::fs::exists(compilerPath)) @@ -117,4 +116,4 @@ getCompilersDefaultIncludeDir(clang::tooling::CompilationDatabase const& compDb, } } // mrdocs -} // clang + diff --git a/src/tool/CompilerInfo.hpp b/src/tool/CompilerInfo.hpp index f0cdae0b0..de6b3cbbe 100644 --- a/src/tool/CompilerInfo.hpp +++ b/src/tool/CompilerInfo.hpp @@ -8,27 +8,27 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_TOOL_COMPILER_HPP -#define MRDOCS_TOOL_COMPILER_HPP +#ifndef MRDOCS_TOOL_COMPILERINFO_HPP +#define MRDOCS_TOOL_COMPILERINFO_HPP +#include +#include +#include #include #include -#include #include +#include -#include -#include -namespace clang { namespace mrdocs { /** * @brief Get the compiler verbose output. * * @param compilerPath The compiler path. - * @return std::optional The compiler verbose output. + * @return The compiler verbose output. */ -std::optional +Optional getCompilerVerboseOutput(llvm::StringRef compilerPath); /** @@ -51,6 +51,6 @@ std::unordered_map> getCompilersDefaultIncludeDir(clang::tooling::CompilationDatabase const& compDb, bool useSystemStdlib); } // mrdocs -} // clang -#endif + +#endif // MRDOCS_TOOL_COMPILERINFO_HPP diff --git a/src/tool/GenerateAction.cpp b/src/tool/GenerateAction.cpp index 91a4dc813..a05fd38b6 100644 --- a/src/tool/GenerateAction.cpp +++ b/src/tool/GenerateAction.cpp @@ -12,16 +12,16 @@ #include "ToolArgs.hpp" #include "ToolCompilationDatabase.hpp" -#include "lib/ConfigImpl.hpp" -#include "lib/CorpusImpl.hpp" -#include "lib/MrDocsCompilationDatabase.hpp" -#include "lib/Support/Path.hpp" +#include +#include +#include +#include #include -#include #include +#include #include -namespace clang::mrdocs { +namespace mrdocs { Expected @@ -73,7 +73,7 @@ DoGenerateAction( } MRDOCS_TRY( MrDocsCompilationDatabase compilationDatabase, - generateCompilationDatabase(tempDir.path(), config, threadPool)); + generateCompilationDatabase(tempDir.path(), config)); // -------------------------------------------------------------- // @@ -108,4 +108,4 @@ DoGenerateAction( return {}; } -} // clang::mrdocs +} // mrdocs diff --git a/src/tool/ToolArgs.cpp b/src/tool/ToolArgs.cpp index 561f141e8..229ea87f0 100644 --- a/src/tool/ToolArgs.cpp +++ b/src/tool/ToolArgs.cpp @@ -11,11 +11,11 @@ #include "ToolArgs.hpp" #include #include -#include -#include #include +#include +#include + -namespace clang { namespace mrdocs { ToolArgs ToolArgs::instance_; @@ -56,4 +56,4 @@ hideForeignOptions() } } // mrdocs -} // clang + diff --git a/src/tool/ToolArgs.hpp b/src/tool/ToolArgs.hpp index aed5016f8..89656140b 100644 --- a/src/tool/ToolArgs.hpp +++ b/src/tool/ToolArgs.hpp @@ -11,12 +11,12 @@ #ifndef MRDOCS_TOOL_TOOLARGS_HPP #define MRDOCS_TOOL_TOOLARGS_HPP -#include -#include #include +#include #include +#include + -namespace clang { namespace mrdocs { /** Command line options and tool settings. @@ -44,6 +44,6 @@ class ToolArgs : public PublicToolArgs constexpr static ToolArgs& toolArgs = ToolArgs::instance_; } // mrdocs -} // clang + #endif diff --git a/src/tool/ToolCompilationDatabase.cpp b/src/tool/ToolCompilationDatabase.cpp index 08bb1e225..6872f602b 100644 --- a/src/tool/ToolCompilationDatabase.cpp +++ b/src/tool/ToolCompilationDatabase.cpp @@ -10,12 +10,12 @@ #include "ToolCompilationDatabase.hpp" #include "CompilerInfo.hpp" -#include "lib/MrDocsSettingsDB.hpp" -#include "lib/Support/CMakeExecution.hpp" -#include "lib/Support/Path.hpp" +#include +#include +#include #include -namespace clang { + namespace mrdocs { namespace { @@ -78,8 +78,7 @@ generateCompileCommandsFile(llvm::StringRef inputPath, llvm::StringRef cmakeArgs Expected generateCompilationDatabase( std::string_view tempDir, - std::shared_ptr const& config, - ThreadPool& threadPool) + std::shared_ptr const& config) { auto& settings = config->settings(); std::string compilationDatabasePath = settings.compilationDatabase; @@ -136,10 +135,10 @@ generateCompilationDatabase( std::string errorMessage; std::unique_ptr jsonDatabasePtr = - tooling::JSONCompilationDatabase::loadFromFile( + clang::tooling::JSONCompilationDatabase::loadFromFile( compileCommandsPath, errorMessage, - tooling::JSONCommandLineSyntax::AutoDetect); + clang::tooling::JSONCommandLineSyntax::AutoDetect); if (!jsonDatabasePtr) { return Unexpected(formatError( @@ -160,4 +159,4 @@ generateCompilationDatabase( } } // mrdocs -} // clang + diff --git a/src/tool/ToolCompilationDatabase.hpp b/src/tool/ToolCompilationDatabase.hpp index 366b5ec35..f4d888173 100644 --- a/src/tool/ToolCompilationDatabase.hpp +++ b/src/tool/ToolCompilationDatabase.hpp @@ -8,22 +8,21 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_TOOL_COMPILATIONDATABASE_HPP -#define MRDOCS_TOOL_COMPILATIONDATABASE_HPP +#ifndef MRDOCS_TOOL_TOOLCOMPILATIONDATABASE_HPP +#define MRDOCS_TOOL_TOOLCOMPILATIONDATABASE_HPP + +#include +#include -#include "lib/MrDocsCompilationDatabase.hpp" -#include "lib/ConfigImpl.hpp" -namespace clang { namespace mrdocs { Expected generateCompilationDatabase( std::string_view tempDir, - std::shared_ptr const& config, - ThreadPool& threadPool); + std::shared_ptr const& config); } // mrdocs -} // clang -#endif + +#endif // MRDOCS_TOOL_TOOLCOMPILATIONDATABASE_HPP diff --git a/src/tool/ToolMain.cpp b/src/tool/ToolMain.cpp index 46bbeca11..fa8c60601 100644 --- a/src/tool/ToolMain.cpp +++ b/src/tool/ToolMain.cpp @@ -10,20 +10,21 @@ // #include "ToolArgs.hpp" -#include "lib/Support/Debug.hpp" -#include "lib/Support/Report.hpp" +#include +#include #include #include #include -#include #include #include +#include +#include #include #include extern int main(int argc, char const** argv); -namespace clang::mrdocs { +namespace mrdocs { extern int @@ -36,16 +37,6 @@ DoGenerateAction( ReferenceDirectories const& dirs, char const** argv); -void -print_version(llvm::raw_ostream& os) -{ - os << project_name - << "\n " << project_description - << "\n version: " << project_version_with_build - << "\n build: " << project_version_build - << "\n built with LLVM " << LLVM_VERSION_STRING - << "\n"; -} Expected getReferenceDirectories(std::string const& execPath) @@ -99,8 +90,28 @@ mrdocs_main(int argc, char const** argv) llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); llvm::setBugReportMsg("PLEASE submit a bug report to https://github.com/cppalliance/mrdocs/issues/ and include the crash backtrace.\n"); + // Set up addons directory +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wpedantic" + // error: ISO C++ forbids taking address of function ‘::main’ +#endif + void* addressOfMain = reinterpret_cast(&main); +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif + std::string execPath = llvm::sys::fs:: + getMainExecutable(argv[0], addressOfMain); + // Parse command line options - llvm::cl::SetVersionPrinter(&print_version); + llvm::cl::SetVersionPrinter([execPath](llvm::raw_ostream& os) { + os << project_name << " version " << project_version_with_build << "\n"; + os << "Built with LLVM " << LLVM_VERSION_STRING << "\n"; + os << "Build SHA: " << project_version_build << "\n"; + os << "Target: " << llvm::sys::getDefaultTargetTriple() << "\n"; + os << "InstalledDir: " << files::getParentDir(execPath) << "\n"; + }); + toolArgs.hideForeignOptions(); if (!llvm::cl::ParseCommandLineOptions( argc, argv, toolArgs.usageText)) @@ -108,17 +119,6 @@ mrdocs_main(int argc, char const** argv) return EXIT_FAILURE; } - // Set up addons directory -#ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wpedantic" -// error: ISO C++ forbids taking address of function ‘::main’ -#endif - void* addressOfMain = reinterpret_cast(&main); -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#endif - std::string execPath = llvm::sys::fs::getMainExecutable(argv[0], addressOfMain); // Before `DoGenerateAction`, we use an error reporting level. // DoGenerateAction will set the level to whatever is specified in @@ -166,26 +166,26 @@ reportUnhandledException( } #endif -} // clang::mrdocs +} // mrdocs int main(int argc, char const** argv) { #ifndef _NDEBUG - return clang::mrdocs::mrdocs_main(argc, argv); + return mrdocs::mrdocs_main(argc, argv); #else try { - return clang::mrdocs::mrdocs_main(argc, argv); + return mrdocs::mrdocs_main(argc, argv); } - catch(clang::mrdocs::Exception const& ex) + catch(mrdocs::Exception const& ex) { // Thrown Exception should never get here. - clang::mrdocs::reportUnhandledException(ex); + mrdocs::reportUnhandledException(ex); } catch(std::exception const& ex) { - clang::mrdocs::reportUnhandledException(ex); + mrdocs::reportUnhandledException(ex); } return EXIT_FAILURE; #endif diff --git a/test-files/golden-tests/config/auto-brief/auto-brief.html b/test-files/golden-tests/config/auto-brief/auto-brief.html index 4eeac05f7..dc5d44dc3 100644 --- a/test-files/golden-tests/config/auto-brief/auto-brief.html +++ b/test-files/golden-tests/config/auto-brief/auto-brief.html @@ -7,7 +7,7 @@

Reference

-

+

Functions

{{name}}{{#if direction}} [{{direction}}]{{/if}}{{>javadoc/block .}}{{>doc/block .}}
@@ -19,47 +19,46 @@

Functions

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
copyBriefFromCopyBrief This is the explicit brief.
copyBriefFromExplicitBrief This is the explicit brief.
copyBriefFromFirstSentenceAsBrief This is the brief.
copyBriefFromFirstValid This function has documentation but no brief.
copyDetailsFromCopyBrief Details will be copied
copyDetailsFromDocNoBrief Custom brief
copyDetailsFromExplicitBrief
copyDetailsFromFirstSentenceAsBrief
copyDetailsFromNoDoc Custom brief
copyDocFromCopyBrief This is the explicit brief.
copyDocFromExplicitBrief This is the explicit brief.
copyDocFromFirstSentenceAsBrief This is the brief.
docNoBriefFunction This function has documentation but no brief.
explicitBriefFunction This is the explicit brief.
explicitBriefFunction2 This is the explicit brief.
failCircularReferenceCopyFunction
failCircularSourceFunctionA
failCircularSourceFunctionB
failCopyBriefFromDocNoBrief This function has documentation but no brief.
failCopyBriefFromInvalidReference
failCopyBriefFromNoDoc
failCopyDetailsFromInvalidReference
failCopyDocFromDocNoBrief This function has documentation but no brief.
failCopyDocFromInvalidReference
failCopyDocFromNoDoc
failInvalidReferenceCopyFunctions
firstSentenceAsBriefFunction This is the brief.
noDocFunction
recursiveReferenceCopyFunction Final recursive brief
recursiveSourceFunctionA Final recursive brief
recursiveSourceFunctionB Final recursive brief
copyBriefFromCopyBrief This is the explicit brief.
copyBriefFromExplicitBrief This is the explicit brief.
copyBriefFromFirstSentenceAsBrief This is the brief.
copyBriefFromFirstValid This function has documentation but no brief.
copyDetailsFromCopyBrief Details will be copied
copyDetailsFromDocNoBrief Custom brief
copyDetailsFromExplicitBrief
copyDetailsFromFirstSentenceAsBrief
copyDetailsFromNoDoc Custom brief
copyDocFromCopyBrief This is the explicit brief.
copyDocFromExplicitBrief This is the explicit brief.
copyDocFromFirstSentenceAsBrief This is the brief.
docNoBriefFunction This function has documentation but no brief.
explicitBriefFunction This is the explicit brief.
explicitBriefFunction2 This is the explicit brief.
failCircularReferenceCopyFunction
failCircularSourceFunctionA
failCircularSourceFunctionB
failCopyBriefFromDocNoBrief This function has documentation but no brief.
failCopyBriefFromInvalidReference
failCopyBriefFromNoDoc
failCopyDetailsFromInvalidReference
failCopyDocFromDocNoBrief This function has documentation but no brief.
failCopyDocFromInvalidReference
failCopyDocFromNoDoc
failInvalidReferenceCopyFunctions
firstSentenceAsBriefFunction This is the brief.
noDocFunction
recursiveReferenceCopyFunction Final recursive brief
recursiveSourceFunctionA Final recursive brief
recursiveSourceFunctionB Final recursive brief
-

copyBriefFromCopyBrief

+

copyBriefFromCopyBrief

-This is the explicit brief. - +

This is the explicit brief.

@@ -68,18 +67,15 @@

Synopsis

Declared in <auto-brief.cpp>
 void
-copyBriefFromCopyBrief();
-
-
+copyBriefFromCopyBrief();
 
-

copyBriefFromExplicitBrief

+

copyBriefFromExplicitBrief

-This is the explicit brief. - +

This is the explicit brief.

@@ -88,18 +84,15 @@

Synopsis

Declared in <auto-brief.cpp>
 void
-copyBriefFromExplicitBrief();
-
-
+copyBriefFromExplicitBrief();
 
-

copyBriefFromFirstSentenceAsBrief

+

copyBriefFromFirstSentenceAsBrief

-This is the brief. - +

This is the brief.

@@ -108,18 +101,15 @@

Synopsis

Declared in <auto-brief.cpp>
 void
-copyBriefFromFirstSentenceAsBrief();
-
-
+copyBriefFromFirstSentenceAsBrief();
 
-

copyBriefFromFirstValid

+

copyBriefFromFirstValid

-This function has documentation but no brief. - +

This function has documentation but no brief.

@@ -128,18 +118,15 @@

Synopsis

Declared in <auto-brief.cpp>
 void
-copyBriefFromFirstValid();
-
-
+copyBriefFromFirstValid();
 
-

copyDetailsFromCopyBrief

+

copyDetailsFromCopyBrief

-Details will be copied - +

Details will be copied

@@ -148,18 +135,15 @@

Synopsis

Declared in <auto-brief.cpp>
 void
-copyDetailsFromCopyBrief();
-
-
+copyDetailsFromCopyBrief();
 
-

copyDetailsFromDocNoBrief

+

copyDetailsFromDocNoBrief

-Custom brief - +

Custom brief

@@ -168,15 +152,13 @@

Synopsis

Declared in <auto-brief.cpp>
 void
-copyDetailsFromDocNoBrief();
-
-
+copyDetailsFromDocNoBrief();
 
-

copyDetailsFromExplicitBrief

+

copyDetailsFromExplicitBrief

Synopsis

@@ -184,9 +166,7 @@

Synopsis

Declared in <auto-brief.cpp>
 void
-copyDetailsFromExplicitBrief();
-
-
+copyDetailsFromExplicitBrief();
 
@@ -196,7 +176,7 @@

Description

-

copyDetailsFromFirstSentenceAsBrief

+

copyDetailsFromFirstSentenceAsBrief

Synopsis

@@ -204,9 +184,7 @@

Synopsis

Declared in <auto-brief.cpp>
 void
-copyDetailsFromFirstSentenceAsBrief();
-
-
+copyDetailsFromFirstSentenceAsBrief();
 
@@ -216,10 +194,9 @@

Description

-

copyDetailsFromNoDoc

+

copyDetailsFromNoDoc

-Custom brief - +

Custom brief

@@ -228,18 +205,15 @@

Synopsis

Declared in <auto-brief.cpp>
 void
-copyDetailsFromNoDoc();
-
-
+copyDetailsFromNoDoc();
 
-

copyDocFromCopyBrief

+

copyDocFromCopyBrief

-This is the explicit brief. - +

This is the explicit brief.

@@ -248,9 +222,7 @@

Synopsis

Declared in <auto-brief.cpp>
 void
-copyDocFromCopyBrief();
-
-
+copyDocFromCopyBrief();
 
@@ -260,10 +232,9 @@

Description

-

copyDocFromExplicitBrief

+

copyDocFromExplicitBrief

-This is the explicit brief. - +

This is the explicit brief.

@@ -272,9 +243,7 @@

Synopsis

Declared in <auto-brief.cpp>
 void
-copyDocFromExplicitBrief();
-
-
+copyDocFromExplicitBrief();
 
@@ -284,10 +253,9 @@

Description

-

copyDocFromFirstSentenceAsBrief

+

copyDocFromFirstSentenceAsBrief

-This is the brief. - +

This is the brief.

@@ -296,9 +264,7 @@

Synopsis

Declared in <auto-brief.cpp>
 void
-copyDocFromFirstSentenceAsBrief();
-
-
+copyDocFromFirstSentenceAsBrief();
 
@@ -308,10 +274,9 @@

Description

-

docNoBriefFunction

+

docNoBriefFunction

-This function has documentation but no brief. - +

This function has documentation but no brief.

@@ -320,18 +285,15 @@

Synopsis

Declared in <auto-brief.cpp>
 void
-docNoBriefFunction();
-
-
+docNoBriefFunction();
 
-

explicitBriefFunction

+

explicitBriefFunction

-This is the explicit brief. - +

This is the explicit brief.

@@ -340,9 +302,7 @@

Synopsis

Declared in <auto-brief.cpp>
 void
-explicitBriefFunction();
-
-
+explicitBriefFunction();
 
@@ -352,10 +312,9 @@

Description

-

explicitBriefFunction2

+

explicitBriefFunction2

-This is the explicit brief. - +

This is the explicit brief.

@@ -364,9 +323,7 @@

Synopsis

Declared in <auto-brief.cpp>
 void
-explicitBriefFunction2();
-
-
+explicitBriefFunction2();
 
@@ -376,7 +333,7 @@

Description

-

failCircularReferenceCopyFunction

+

failCircularReferenceCopyFunction

Synopsis

@@ -384,15 +341,13 @@

Synopsis

Declared in <auto-brief.cpp>
 void
-failCircularReferenceCopyFunction();
-
-
+failCircularReferenceCopyFunction();
 
-

failCircularSourceFunctionA

+

failCircularSourceFunctionA

Synopsis

@@ -400,15 +355,13 @@

Synopsis

Declared in <auto-brief.cpp>
 void
-failCircularSourceFunctionA();
-
-
+failCircularSourceFunctionA();
 
-

failCircularSourceFunctionB

+

failCircularSourceFunctionB

Synopsis

@@ -416,18 +369,15 @@

Synopsis

Declared in <auto-brief.cpp>
 void
-failCircularSourceFunctionB();
-
-
+failCircularSourceFunctionB();
 
-

failCopyBriefFromDocNoBrief

+

failCopyBriefFromDocNoBrief

-This function has documentation but no brief. - +

This function has documentation but no brief.

@@ -436,15 +386,13 @@

Synopsis

Declared in <auto-brief.cpp>
 void
-failCopyBriefFromDocNoBrief();
-
-
+failCopyBriefFromDocNoBrief();
 
-

failCopyBriefFromInvalidReference

+

failCopyBriefFromInvalidReference

Synopsis

@@ -452,15 +400,13 @@

Synopsis

Declared in <auto-brief.cpp>
 void
-failCopyBriefFromInvalidReference();
-
-
+failCopyBriefFromInvalidReference();
 
-

failCopyBriefFromNoDoc

+

failCopyBriefFromNoDoc

Synopsis

@@ -468,15 +414,13 @@

Synopsis

Declared in <auto-brief.cpp>
 void
-failCopyBriefFromNoDoc();
-
-
+failCopyBriefFromNoDoc();
 
-

failCopyDetailsFromInvalidReference

+

failCopyDetailsFromInvalidReference

Synopsis

@@ -484,18 +428,15 @@

Synopsis

Declared in <auto-brief.cpp>
 void
-failCopyDetailsFromInvalidReference();
-
-
+failCopyDetailsFromInvalidReference();
 
-

failCopyDocFromDocNoBrief

+

failCopyDocFromDocNoBrief

-This function has documentation but no brief. - +

This function has documentation but no brief.

@@ -504,15 +445,13 @@

Synopsis

Declared in <auto-brief.cpp>
 void
-failCopyDocFromDocNoBrief();
-
-
+failCopyDocFromDocNoBrief();
 
-

failCopyDocFromInvalidReference

+

failCopyDocFromInvalidReference

Synopsis

@@ -520,15 +459,13 @@

Synopsis

Declared in <auto-brief.cpp>
 void
-failCopyDocFromInvalidReference();
-
-
+failCopyDocFromInvalidReference();
 
-

failCopyDocFromNoDoc

+

failCopyDocFromNoDoc

Synopsis

@@ -536,15 +473,13 @@

Synopsis

Declared in <auto-brief.cpp>
 void
-failCopyDocFromNoDoc();
-
-
+failCopyDocFromNoDoc();
 
-

failInvalidReferenceCopyFunctions

+

failInvalidReferenceCopyFunctions

Synopsis

@@ -552,18 +487,15 @@

Synopsis

Declared in <auto-brief.cpp>
 void
-failInvalidReferenceCopyFunctions();
-
-
+failInvalidReferenceCopyFunctions();
 
-

firstSentenceAsBriefFunction

+

firstSentenceAsBriefFunction

-This is the brief. - +

This is the brief.

@@ -572,9 +504,7 @@

Synopsis

Declared in <auto-brief.cpp>
 void
-firstSentenceAsBriefFunction();
-
-
+firstSentenceAsBriefFunction();
 
@@ -584,7 +514,7 @@

Description

-

noDocFunction

+

noDocFunction

Synopsis

@@ -592,18 +522,15 @@

Synopsis

Declared in <auto-brief.cpp>
 void
-noDocFunction();
-
-
+noDocFunction();
 
-

recursiveReferenceCopyFunction

+

recursiveReferenceCopyFunction

-Final recursive brief - +

Final recursive brief

@@ -612,18 +539,15 @@

Synopsis

Declared in <auto-brief.cpp>
 void
-recursiveReferenceCopyFunction();
-
-
+recursiveReferenceCopyFunction();
 
-

recursiveSourceFunctionA

+

recursiveSourceFunctionA

-Final recursive brief - +

Final recursive brief

@@ -632,18 +556,15 @@

Synopsis

Declared in <auto-brief.cpp>
 void
-recursiveSourceFunctionA();
-
-
+recursiveSourceFunctionA();
 
-

recursiveSourceFunctionB

+

recursiveSourceFunctionB

-Final recursive brief - +

Final recursive brief

@@ -652,9 +573,7 @@

Synopsis

Declared in <auto-brief.cpp>
 void
-recursiveSourceFunctionB();
-
-
+recursiveSourceFunctionB();
 
diff --git a/test-files/golden-tests/config/auto-brief/auto-brief.xml b/test-files/golden-tests/config/auto-brief/auto-brief.xml index a53007dee..359691cbd 100644 --- a/test-files/golden-tests/config/auto-brief/auto-brief.xml +++ b/test-files/golden-tests/config/auto-brief/auto-brief.xml @@ -139,24 +139,12 @@ - - - - - - - - - - - - @@ -168,22 +156,12 @@ - - - - - - - - - - @@ -195,24 +173,12 @@ - - - - - - - - - - - - diff --git a/test-files/golden-tests/config/auto-brief/no-auto-brief.html b/test-files/golden-tests/config/auto-brief/no-auto-brief.html index bfeb14222..ef3572b66 100644 --- a/test-files/golden-tests/config/auto-brief/no-auto-brief.html +++ b/test-files/golden-tests/config/auto-brief/no-auto-brief.html @@ -7,7 +7,7 @@

Reference

-

+

Functions

@@ -19,47 +19,46 @@

Functions

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
copyBriefFromCopyBrief This is the explicit brief.
copyBriefFromExplicitBrief This is the explicit brief.
copyBriefFromFirstSentenceAsBrief
copyBriefFromFirstValid This is the explicit brief.
copyDetailsFromCopyBrief Details will be copied
copyDetailsFromDocNoBrief Custom brief
copyDetailsFromExplicitBrief
copyDetailsFromFirstSentenceAsBrief
copyDetailsFromNoDoc Custom brief
copyDocFromCopyBrief This is the explicit brief.
copyDocFromExplicitBrief This is the explicit brief.
copyDocFromFirstSentenceAsBrief
docNoBriefFunction
explicitBriefFunction This is the explicit brief.
explicitBriefFunction2 This is the explicit brief.
failCircularReferenceCopyFunction
failCircularSourceFunctionA
failCircularSourceFunctionB
failCopyBriefFromDocNoBrief
failCopyBriefFromInvalidReference
failCopyBriefFromNoDoc
failCopyDetailsFromInvalidReference
failCopyDocFromDocNoBrief
failCopyDocFromInvalidReference
failCopyDocFromNoDoc
failInvalidReferenceCopyFunctions
firstSentenceAsBriefFunction
noDocFunction
recursiveReferenceCopyFunction Final recursive brief
recursiveSourceFunctionA Final recursive brief
recursiveSourceFunctionB Final recursive brief
copyBriefFromCopyBrief This is the explicit brief.
copyBriefFromExplicitBrief This is the explicit brief.
copyBriefFromFirstSentenceAsBrief
copyBriefFromFirstValid This is the explicit brief.
copyDetailsFromCopyBrief Details will be copied
copyDetailsFromDocNoBrief Custom brief
copyDetailsFromExplicitBrief
copyDetailsFromFirstSentenceAsBrief
copyDetailsFromNoDoc Custom brief
copyDocFromCopyBrief This is the explicit brief.
copyDocFromExplicitBrief This is the explicit brief.
copyDocFromFirstSentenceAsBrief
docNoBriefFunction
explicitBriefFunction This is the explicit brief.
explicitBriefFunction2 This is the explicit brief.
failCircularReferenceCopyFunction
failCircularSourceFunctionA
failCircularSourceFunctionB
failCopyBriefFromDocNoBrief
failCopyBriefFromInvalidReference
failCopyBriefFromNoDoc
failCopyDetailsFromInvalidReference
failCopyDocFromDocNoBrief
failCopyDocFromInvalidReference
failCopyDocFromNoDoc
failInvalidReferenceCopyFunctions
firstSentenceAsBriefFunction
noDocFunction
recursiveReferenceCopyFunction Final recursive brief
recursiveSourceFunctionA Final recursive brief
recursiveSourceFunctionB Final recursive brief
-

copyBriefFromCopyBrief

+

copyBriefFromCopyBrief

-This is the explicit brief. - +

This is the explicit brief.

@@ -68,18 +67,15 @@

Synopsis

Declared in <no-auto-brief.cpp>
 void
-copyBriefFromCopyBrief();
-
-
+copyBriefFromCopyBrief();
 
-

copyBriefFromExplicitBrief

+

copyBriefFromExplicitBrief

-This is the explicit brief. - +

This is the explicit brief.

@@ -88,15 +84,13 @@

Synopsis

Declared in <no-auto-brief.cpp>
 void
-copyBriefFromExplicitBrief();
-
-
+copyBriefFromExplicitBrief();
 
-

copyBriefFromFirstSentenceAsBrief

+

copyBriefFromFirstSentenceAsBrief

Synopsis

@@ -104,18 +98,15 @@

Synopsis

Declared in <no-auto-brief.cpp>
 void
-copyBriefFromFirstSentenceAsBrief();
-
-
+copyBriefFromFirstSentenceAsBrief();
 
-

copyBriefFromFirstValid

+

copyBriefFromFirstValid

-This is the explicit brief. - +

This is the explicit brief.

@@ -124,18 +115,15 @@

Synopsis

Declared in <no-auto-brief.cpp>
 void
-copyBriefFromFirstValid();
-
-
+copyBriefFromFirstValid();
 
-

copyDetailsFromCopyBrief

+

copyDetailsFromCopyBrief

-Details will be copied - +

Details will be copied

@@ -144,18 +132,15 @@

Synopsis

Declared in <no-auto-brief.cpp>
 void
-copyDetailsFromCopyBrief();
-
-
+copyDetailsFromCopyBrief();
 
-

copyDetailsFromDocNoBrief

+

copyDetailsFromDocNoBrief

-Custom brief - +

Custom brief

@@ -164,15 +149,13 @@

Synopsis

Declared in <no-auto-brief.cpp>
 void
-copyDetailsFromDocNoBrief();
-
-
+copyDetailsFromDocNoBrief();
 
-

copyDetailsFromExplicitBrief

+

copyDetailsFromExplicitBrief

Synopsis

@@ -180,9 +163,7 @@

Synopsis

Declared in <no-auto-brief.cpp>
 void
-copyDetailsFromExplicitBrief();
-
-
+copyDetailsFromExplicitBrief();
 
@@ -192,7 +173,7 @@

Description

-

copyDetailsFromFirstSentenceAsBrief

+

copyDetailsFromFirstSentenceAsBrief

Synopsis

@@ -200,9 +181,7 @@

Synopsis

Declared in <no-auto-brief.cpp>
 void
-copyDetailsFromFirstSentenceAsBrief();
-
-
+copyDetailsFromFirstSentenceAsBrief();
 
@@ -213,10 +192,9 @@

Description

-

copyDetailsFromNoDoc

+

copyDetailsFromNoDoc

-Custom brief - +

Custom brief

@@ -225,18 +203,15 @@

Synopsis

Declared in <no-auto-brief.cpp>
 void
-copyDetailsFromNoDoc();
-
-
+copyDetailsFromNoDoc();
 
-

copyDocFromCopyBrief

+

copyDocFromCopyBrief

-This is the explicit brief. - +

This is the explicit brief.

@@ -245,9 +220,7 @@

Synopsis

Declared in <no-auto-brief.cpp>
 void
-copyDocFromCopyBrief();
-
-
+copyDocFromCopyBrief();
 
@@ -257,10 +230,9 @@

Description

-

copyDocFromExplicitBrief

+

copyDocFromExplicitBrief

-This is the explicit brief. - +

This is the explicit brief.

@@ -269,9 +241,7 @@

Synopsis

Declared in <no-auto-brief.cpp>
 void
-copyDocFromExplicitBrief();
-
-
+copyDocFromExplicitBrief();
 
@@ -281,7 +251,7 @@

Description

-

copyDocFromFirstSentenceAsBrief

+

copyDocFromFirstSentenceAsBrief

Synopsis

@@ -289,9 +259,7 @@

Synopsis

Declared in <no-auto-brief.cpp>
 void
-copyDocFromFirstSentenceAsBrief();
-
-
+copyDocFromFirstSentenceAsBrief();
 
@@ -302,7 +270,7 @@

Description

-

docNoBriefFunction

+

docNoBriefFunction

Synopsis

@@ -310,9 +278,7 @@

Synopsis

Declared in <no-auto-brief.cpp>
 void
-docNoBriefFunction();
-
-
+docNoBriefFunction();
 
@@ -322,10 +288,9 @@

Description

-

explicitBriefFunction

+

explicitBriefFunction

-This is the explicit brief. - +

This is the explicit brief.

@@ -334,9 +299,7 @@

Synopsis

Declared in <no-auto-brief.cpp>
 void
-explicitBriefFunction();
-
-
+explicitBriefFunction();
 
@@ -346,10 +309,9 @@

Description

-

explicitBriefFunction2

+

explicitBriefFunction2

-This is the explicit brief. - +

This is the explicit brief.

@@ -358,9 +320,7 @@

Synopsis

Declared in <no-auto-brief.cpp>
 void
-explicitBriefFunction2();
-
-
+explicitBriefFunction2();
 
@@ -370,7 +330,7 @@

Description

-

failCircularReferenceCopyFunction

+

failCircularReferenceCopyFunction

Synopsis

@@ -378,15 +338,13 @@

Synopsis

Declared in <no-auto-brief.cpp>
 void
-failCircularReferenceCopyFunction();
-
-
+failCircularReferenceCopyFunction();
 
-

failCircularSourceFunctionA

+

failCircularSourceFunctionA

Synopsis

@@ -394,15 +352,13 @@

Synopsis

Declared in <no-auto-brief.cpp>
 void
-failCircularSourceFunctionA();
-
-
+failCircularSourceFunctionA();
 
-

failCircularSourceFunctionB

+

failCircularSourceFunctionB

Synopsis

@@ -410,15 +366,13 @@

Synopsis

Declared in <no-auto-brief.cpp>
 void
-failCircularSourceFunctionB();
-
-
+failCircularSourceFunctionB();
 
-

failCopyBriefFromDocNoBrief

+

failCopyBriefFromDocNoBrief

Synopsis

@@ -426,15 +380,13 @@

Synopsis

Declared in <no-auto-brief.cpp>
 void
-failCopyBriefFromDocNoBrief();
-
-
+failCopyBriefFromDocNoBrief();
 
-

failCopyBriefFromInvalidReference

+

failCopyBriefFromInvalidReference

Synopsis

@@ -442,15 +394,13 @@

Synopsis

Declared in <no-auto-brief.cpp>
 void
-failCopyBriefFromInvalidReference();
-
-
+failCopyBriefFromInvalidReference();
 
-

failCopyBriefFromNoDoc

+

failCopyBriefFromNoDoc

Synopsis

@@ -458,15 +408,13 @@

Synopsis

Declared in <no-auto-brief.cpp>
 void
-failCopyBriefFromNoDoc();
-
-
+failCopyBriefFromNoDoc();
 
-

failCopyDetailsFromInvalidReference

+

failCopyDetailsFromInvalidReference

Synopsis

@@ -474,15 +422,13 @@

Synopsis

Declared in <no-auto-brief.cpp>
 void
-failCopyDetailsFromInvalidReference();
-
-
+failCopyDetailsFromInvalidReference();
 
-

failCopyDocFromDocNoBrief

+

failCopyDocFromDocNoBrief

Synopsis

@@ -490,9 +436,7 @@

Synopsis

Declared in <no-auto-brief.cpp>
 void
-failCopyDocFromDocNoBrief();
-
-
+failCopyDocFromDocNoBrief();
 
@@ -502,7 +446,7 @@

Description

-

failCopyDocFromInvalidReference

+

failCopyDocFromInvalidReference

Synopsis

@@ -510,15 +454,13 @@

Synopsis

Declared in <no-auto-brief.cpp>
 void
-failCopyDocFromInvalidReference();
-
-
+failCopyDocFromInvalidReference();
 
-

failCopyDocFromNoDoc

+

failCopyDocFromNoDoc

Synopsis

@@ -526,15 +468,13 @@

Synopsis

Declared in <no-auto-brief.cpp>
 void
-failCopyDocFromNoDoc();
-
-
+failCopyDocFromNoDoc();
 
-

failInvalidReferenceCopyFunctions

+

failInvalidReferenceCopyFunctions

Synopsis

@@ -542,15 +482,13 @@

Synopsis

Declared in <no-auto-brief.cpp>
 void
-failInvalidReferenceCopyFunctions();
-
-
+failInvalidReferenceCopyFunctions();
 
-

firstSentenceAsBriefFunction

+

firstSentenceAsBriefFunction

Synopsis

@@ -558,9 +496,7 @@

Synopsis

Declared in <no-auto-brief.cpp>
 void
-firstSentenceAsBriefFunction();
-
-
+firstSentenceAsBriefFunction();
 
@@ -571,7 +507,7 @@

Description

-

noDocFunction

+

noDocFunction

Synopsis

@@ -579,18 +515,15 @@

Synopsis

Declared in <no-auto-brief.cpp>
 void
-noDocFunction();
-
-
+noDocFunction();
 
-

recursiveReferenceCopyFunction

+

recursiveReferenceCopyFunction

-Final recursive brief - +

Final recursive brief

@@ -599,18 +532,15 @@

Synopsis

Declared in <no-auto-brief.cpp>
 void
-recursiveReferenceCopyFunction();
-
-
+recursiveReferenceCopyFunction();
 
-

recursiveSourceFunctionA

+

recursiveSourceFunctionA

-Final recursive brief - +

Final recursive brief

@@ -619,18 +549,15 @@

Synopsis

Declared in <no-auto-brief.cpp>
 void
-recursiveSourceFunctionA();
-
-
+recursiveSourceFunctionA();
 
-

recursiveSourceFunctionB

+

recursiveSourceFunctionB

-Final recursive brief - +

Final recursive brief

@@ -639,9 +566,7 @@

Synopsis

Declared in <no-auto-brief.cpp>
 void
-recursiveSourceFunctionB();
-
-
+recursiveSourceFunctionB();
 
diff --git a/test-files/golden-tests/config/auto-brief/no-auto-brief.xml b/test-files/golden-tests/config/auto-brief/no-auto-brief.xml index 14eaf504a..21f3ea0a0 100644 --- a/test-files/golden-tests/config/auto-brief/no-auto-brief.xml +++ b/test-files/golden-tests/config/auto-brief/no-auto-brief.xml @@ -20,10 +20,6 @@
- - - - @@ -101,8 +97,6 @@ - - This is the brief. @@ -143,56 +137,28 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - This function has documentation but no brief. @@ -200,24 +166,12 @@ - - - - - - - - - - - - diff --git a/test-files/golden-tests/config/auto-function-metadata/brief-from-function-class.html b/test-files/golden-tests/config/auto-function-metadata/brief-from-function-class.html index c805d70c3..44301b34e 100644 --- a/test-files/golden-tests/config/auto-function-metadata/brief-from-function-class.html +++ b/test-files/golden-tests/config/auto-function-metadata/brief-from-function-class.html @@ -7,7 +7,7 @@

Reference

-

+

Types

@@ -19,18 +19,17 @@

Types

- - + +
A A helper tag
X Test class
A A helper tag
X Test class
-

A

+

A

-A helper tag - +

A helper tag

@@ -38,9 +37,7 @@

Synopsis

Declared in <brief-from-function-class.cpp>
-struct A;
-
-
+struct A;
 
@@ -48,10 +45,9 @@

Synopsis

-

X

+

X

-Test class - +

Test class

@@ -59,9 +55,7 @@

Synopsis

Declared in <brief-from-function-class.cpp>
-struct X;
-
-
+struct X;
 

Member Functions

@@ -74,10 +68,10 @@

Member Functions

-X [constructor]Constructors -~X [destructor]Destructor -operator A Conversion to A -operator int Conversion to int +X [constructor]Constructors +~X [destructor]Destructor +operator A Conversion to A +operator int Conversion to int @@ -86,59 +80,46 @@

Member Functions

-

X::X

+

X::X

-Constructors - +

Constructors

Synopses

Declared in <brief-from-function-class.cpp>
-Default constructor +

Default constructor

 constexpr
-X() = default;
-
-
+X() = default;
 
» more... -Copy constructor +

Copy constructor

 constexpr
-X(X const& other) = default;
-
-
+X(X const& other) = default;
 
» more... -Move constructor +

Move constructor

 constexpr
-X(X&& other) = default;
-
-
+X(X&& other) = default;
 
» more... -Construct from int +

Construct from int

-X(int value);
-
-
+X(int value);
 
» more... -Construct from A +

Construct from A

-X(A const& value);
-
-
+X(A const& value);
 
» more... -Construct from A +

Construct from A

-X(A&& value);
-
-
+X(A&& value);
 
» more... @@ -146,10 +127,9 @@

Synopses

-

X::X

+

X::X

-Default constructor - +

Default constructor

@@ -158,18 +138,15 @@

Synopsis

Declared in <brief-from-function-class.cpp>
 constexpr
-X() = default;
-
-
+X() = default;
 
-

X::X

+

X::X

-Copy constructor - +

Copy constructor

@@ -178,9 +155,7 @@

Synopsis

Declared in <brief-from-function-class.cpp>
 constexpr
-X(X const& other) = default;
-
-
+X(X const& other) = default;
 
@@ -195,7 +170,7 @@

Parameters

other -The object to copy construct from +The object to copy construct from @@ -203,10 +178,9 @@

Parameters

-

X::X

+

X::X

-Move constructor - +

Move constructor

@@ -215,9 +189,7 @@

Synopsis

Declared in <brief-from-function-class.cpp>
 constexpr
-X(X&& other) = default;
-
-
+X(X&& other) = default;
 
@@ -232,7 +204,7 @@

Parameters

other -The object to move construct from +The object to move construct from @@ -240,10 +212,9 @@

Parameters

-

X::X

+

X::X

-Construct from int - +

Construct from int

@@ -251,9 +222,7 @@

Synopsis

Declared in <brief-from-function-class.cpp>
-X(int value);
-
-
+X(int value);
 
@@ -268,7 +237,7 @@

Parameters

value -The value to construct from +The value to construct from @@ -276,10 +245,9 @@

Parameters

-

X::X

+

X::X

-Construct from A - +

Construct from A

@@ -287,9 +255,7 @@

Synopsis

Declared in <brief-from-function-class.cpp>
-X(A const& value);
-
-
+X(A const& value);
 
@@ -304,7 +270,7 @@

Parameters

value -The object to copy construct from +The object to copy construct from @@ -312,10 +278,9 @@

Parameters

-

X::X

+

X::X

-Construct from A - +

Construct from A

@@ -323,9 +288,7 @@

Synopsis

Declared in <brief-from-function-class.cpp>
-X(A&& value);
-
-
+X(A&& value);
 
@@ -340,7 +303,7 @@

Parameters

value -The object to move construct from +The object to move construct from @@ -348,10 +311,9 @@

Parameters

-

X::~X

+

X::~X

-Destructor - +

Destructor

@@ -359,18 +321,15 @@

Synopsis

Declared in <brief-from-function-class.cpp>
-~X();
-
-
+~X();
 
-

X::operator A

+

X::operator A

-Conversion to A - +

Conversion to A

@@ -378,22 +337,19 @@

Synopsis

Declared in <brief-from-function-class.cpp>
-operator A() const;
-
-
+operator A() const;
 

Return Value

-A helper tag +A helper tag
-

X::operator int

+

X::operator int

-Conversion to int - +

Conversion to int

@@ -401,14 +357,12 @@

Synopsis

Declared in <brief-from-function-class.cpp>
-operator int() const;
-
-
+operator int() const;
 

Return Value

-The object converted to int +The object converted to int
diff --git a/test-files/golden-tests/config/auto-function-metadata/brief-from-operator.adoc b/test-files/golden-tests/config/auto-function-metadata/brief-from-operator.adoc index 8fb246f40..d6b2b01e1 100644 --- a/test-files/golden-tests/config/auto-function-metadata/brief-from-operator.adoc +++ b/test-files/golden-tests/config/auto-function-metadata/brief-from-operator.adoc @@ -240,6 +240,16 @@ Declared in `<brief‐from‐operator.cpp>` struct ostream; ---- +=== Non-Member Functions + +[cols=2] +|=== +| Name +| Description +| link:#operator_lshift[`operator<<`] +| Stream insertion operator +|=== + [#operator_lshift] == operator<< diff --git a/test-files/golden-tests/config/auto-function-metadata/brief-from-operator.html b/test-files/golden-tests/config/auto-function-metadata/brief-from-operator.html index e494d0fcd..9127351ba 100644 --- a/test-files/golden-tests/config/auto-function-metadata/brief-from-operator.html +++ b/test-files/golden-tests/config/auto-function-metadata/brief-from-operator.html @@ -7,7 +7,7 @@

Reference

-

+

Types

@@ -19,9 +19,9 @@

Types

- - - + + +
A A helper tag
X Test class
ostream A dumb ostream class
A A helper tag
X Test class
ostream A dumb ostream class
@@ -35,17 +35,16 @@

Functions

-operator<< Stream insertion operator +operator<< Stream insertion operator
-

A

+

A

-A helper tag - +

A helper tag

@@ -53,9 +52,7 @@

Synopsis

Declared in <brief-from-operator.cpp>
-struct A;
-
-
+struct A;
 
@@ -63,10 +60,9 @@

Synopsis

-

X

+

X

-Test class - +

Test class

@@ -74,9 +70,7 @@

Synopsis

Declared in <brief-from-operator.cpp>
-struct X;
-
-
+struct X;
 

Member Functions

@@ -89,8 +83,8 @@

Member Functions

-operator= Assignment operators -operator+= Addition assignment operator +operator= Assignment operators +operator+= Addition assignment operator @@ -99,38 +93,31 @@

Member Functions

-

X::operator=

+

X::operator=

-Assignment operators - +

Assignment operators

Synopses

Declared in <brief-from-operator.cpp>
-Copy assignment operator +

Copy assignment operator

 X&
-operator=(X const& other);
-
-
+operator=(X const& other);
 
» more... -Move assignment operator +

Move assignment operator

 X&
-operator=(X&& other);
-
-
+operator=(X&& other);
 
» more... -Assignment operator +

Assignment operator

 X&
-operator=(A const& value);
-
-
+operator=(A const& value);
 
» more... @@ -138,10 +125,9 @@

Synopses

-

X::operator=

+

X::operator=

-Copy assignment operator - +

Copy assignment operator

@@ -150,14 +136,12 @@

Synopsis

Declared in <brief-from-operator.cpp>
 X&
-operator=(X const& other);
-
-
+operator=(X const& other);
 

Return Value

-Reference to the current object +Reference to the current object

Parameters

@@ -171,7 +155,7 @@

Parameters

other -The object to copy assign from +The object to copy assign from @@ -179,10 +163,9 @@

Parameters

-

X::operator=

+

X::operator=

-Move assignment operator - +

Move assignment operator

@@ -191,14 +174,12 @@

Synopsis

Declared in <brief-from-operator.cpp>
 X&
-operator=(X&& other);
-
-
+operator=(X&& other);
 

Return Value

-Reference to the current object +Reference to the current object

Parameters

@@ -212,7 +193,7 @@

Parameters

other -The object to move assign from +The object to move assign from @@ -220,10 +201,9 @@

Parameters

-

X::operator=

+

X::operator=

-Assignment operator - +

Assignment operator

@@ -232,14 +212,12 @@

Synopsis

Declared in <brief-from-operator.cpp>
 X&
-operator=(A const& value);
-
-
+operator=(A const& value);
 

Return Value

-Reference to the current object +Reference to the current object

Parameters

@@ -253,7 +231,7 @@

Parameters

value -The object to copy assign from +The object to copy assign from @@ -261,10 +239,9 @@

Parameters

-

X::operator+=

+

X::operator+=

-Addition assignment operator - +

Addition assignment operator

@@ -273,14 +250,12 @@

Synopsis

Declared in <brief-from-operator.cpp>
 X&
-operator+=(X const& rhs);
-
-
+operator+=(X const& rhs);
 

Return Value

-Reference to the current object +Reference to the current object

Parameters

@@ -294,7 +269,7 @@

Parameters

rhs -The right operand +The right operand @@ -302,10 +277,9 @@

Parameters

-

ostream

+

ostream

-A dumb ostream class - +

A dumb ostream class

@@ -313,20 +287,31 @@

Synopsis

Declared in <brief-from-operator.cpp>
-struct ostream;
-
-
+struct ostream;
 
+
+

Non-Member Functions

+ + + + + + + + + + +
NameDescription
operator<<Stream insertion operator
+
-

operator<<

+

operator<<

-Stream insertion operator - +

Stream insertion operator

@@ -337,14 +322,12 @@

Synopsis

ostream& operator<<( ostream& os, - X const& x); - - + X const& x);

Return Value

-Reference to the current output stream +Reference to the current output stream

Parameters

@@ -358,11 +341,11 @@

Parameters

os -An output stream +An output stream x -The object to output +The object to output diff --git a/test-files/golden-tests/config/auto-function-metadata/brief-from-operator.xml b/test-files/golden-tests/config/auto-function-metadata/brief-from-operator.xml index 7acdca1ae..1e28354a3 100644 --- a/test-files/golden-tests/config/auto-function-metadata/brief-from-operator.xml +++ b/test-files/golden-tests/config/auto-function-metadata/brief-from-operator.xml @@ -124,6 +124,9 @@ A dumb ostream class + + operator<< + @@ -157,6 +160,9 @@ The object to output + + ostream + diff --git a/test-files/golden-tests/config/auto-function-metadata/param-from-function-class.html b/test-files/golden-tests/config/auto-function-metadata/param-from-function-class.html index bdcfd1f52..318ce0098 100644 --- a/test-files/golden-tests/config/auto-function-metadata/param-from-function-class.html +++ b/test-files/golden-tests/config/auto-function-metadata/param-from-function-class.html @@ -7,7 +7,7 @@

Reference

-

+

Types

@@ -19,18 +19,17 @@

Types

- - + +
A A helper tag
X Test class
A A helper tag
X Test class
-

A

+

A

-A helper tag - +

A helper tag

@@ -38,9 +37,7 @@

Synopsis

Declared in <param-from-function-class.cpp>
-struct A;
-
-
+struct A;
 
@@ -48,10 +45,9 @@

Synopsis

-

X

+

X

-Test class - +

Test class

@@ -59,9 +55,7 @@

Synopsis

Declared in <param-from-function-class.cpp>
-struct X;
-
-
+struct X;
 

Member Functions

@@ -74,8 +68,8 @@

Member Functions

-X [constructor]Constructors -operator= Assignment operators +X [constructor]Constructors +operator= Assignment operators @@ -84,49 +78,38 @@

Member Functions

-

X::X

+

X::X

-Constructors - +

Constructors

Synopses

Declared in <param-from-function-class.cpp>
-Copy constructor +

Copy constructor

-X(X const& other);
-
-
+X(X const& other);
 
» more... -Move constructor +

Move constructor

-X(X&& other);
-
-
+X(X&& other);
 
» more... -Construct from int +

Construct from int

-X(int value);
-
-
+X(int value);
 
» more... -Construct from A +

Construct from A

-X(A const& value);
-
-
+X(A const& value);
 
» more... -Construct from A +

Construct from A

-X(A&& value);
-
-
+X(A&& value);
 
» more... @@ -134,10 +117,9 @@

Synopses

-

X::X

+

X::X

-Copy constructor - +

Copy constructor

@@ -145,9 +127,7 @@

Synopsis

Declared in <param-from-function-class.cpp>
-X(X const& other);
-
-
+X(X const& other);
 
@@ -162,7 +142,7 @@

Parameters

other -The object to copy construct from +The object to copy construct from @@ -170,10 +150,9 @@

Parameters

-

X::X

+

X::X

-Move constructor - +

Move constructor

@@ -181,9 +160,7 @@

Synopsis

Declared in <param-from-function-class.cpp>
-X(X&& other);
-
-
+X(X&& other);
 
@@ -198,7 +175,7 @@

Parameters

other -The object to move construct from +The object to move construct from @@ -206,10 +183,9 @@

Parameters

-

X::X

+

X::X

-Construct from int - +

Construct from int

@@ -217,9 +193,7 @@

Synopsis

Declared in <param-from-function-class.cpp>
-X(int value);
-
-
+X(int value);
 
@@ -234,7 +208,7 @@

Parameters

value -The value to construct from +The value to construct from @@ -242,10 +216,9 @@

Parameters

-

X::X

+

X::X

-Construct from A - +

Construct from A

@@ -253,9 +226,7 @@

Synopsis

Declared in <param-from-function-class.cpp>
-X(A const& value);
-
-
+X(A const& value);
 
@@ -270,7 +241,7 @@

Parameters

value -The object to copy construct from +The object to copy construct from @@ -278,10 +249,9 @@

Parameters

-

X::X

+

X::X

-Construct from A - +

Construct from A

@@ -289,9 +259,7 @@

Synopsis

Declared in <param-from-function-class.cpp>
-X(A&& value);
-
-
+X(A&& value);
 
@@ -306,7 +274,7 @@

Parameters

value -The object to move construct from +The object to move construct from @@ -314,54 +282,43 @@

Parameters

-

X::operator=

+

X::operator=

-Assignment operators - +

Assignment operators

Synopses

Declared in <param-from-function-class.cpp>
-Copy assignment operator +

Copy assignment operator

 X&
-operator=(X const& other);
-
-
+operator=(X const& other);
 
» more... -Move assignment operator +

Move assignment operator

 X&
-operator=(X&& other);
-
-
+operator=(X&& other);
 
» more... -Assignment operator +

Assignment operator

 X&
-operator=(int value);
-
-
+operator=(int value);
 
» more... -Assignment operator +

Assignment operator

 X&
-operator=(A const& value);
-
-
+operator=(A const& value);
 
» more... -Assignment operator +

Assignment operator

 X&
-operator=(A&& value);
-
-
+operator=(A&& value);
 
» more... @@ -369,10 +326,9 @@

Synopses

-

X::operator=

+

X::operator=

-Copy assignment operator - +

Copy assignment operator

@@ -381,14 +337,12 @@

Synopsis

Declared in <param-from-function-class.cpp>
 X&
-operator=(X const& other);
-
-
+operator=(X const& other);
 

Return Value

-Reference to the current object +Reference to the current object

Parameters

@@ -402,7 +356,7 @@

Parameters

other -The object to copy assign from +The object to copy assign from @@ -410,10 +364,9 @@

Parameters

-

X::operator=

+

X::operator=

-Move assignment operator - +

Move assignment operator

@@ -422,14 +375,12 @@

Synopsis

Declared in <param-from-function-class.cpp>
 X&
-operator=(X&& other);
-
-
+operator=(X&& other);
 

Return Value

-Reference to the current object +Reference to the current object

Parameters

@@ -443,7 +394,7 @@

Parameters

other -The object to move assign from +The object to move assign from @@ -451,10 +402,9 @@

Parameters

-

X::operator=

+

X::operator=

-Assignment operator - +

Assignment operator

@@ -463,14 +413,12 @@

Synopsis

Declared in <param-from-function-class.cpp>
 X&
-operator=(int value);
-
-
+operator=(int value);
 

Return Value

-Reference to the current object +Reference to the current object

Parameters

@@ -484,7 +432,7 @@

Parameters

value -The value to assign from +The value to assign from @@ -492,10 +440,9 @@

Parameters

-

X::operator=

+

X::operator=

-Assignment operator - +

Assignment operator

@@ -504,14 +451,12 @@

Synopsis

Declared in <param-from-function-class.cpp>
 X&
-operator=(A const& value);
-
-
+operator=(A const& value);
 

Return Value

-Reference to the current object +Reference to the current object

Parameters

@@ -525,7 +470,7 @@

Parameters

value -The object to copy assign from +The object to copy assign from @@ -533,10 +478,9 @@

Parameters

-

X::operator=

+

X::operator=

-Assignment operator - +

Assignment operator

@@ -545,14 +489,12 @@

Synopsis

Declared in <param-from-function-class.cpp>
 X&
-operator=(A&& value);
-
-
+operator=(A&& value);
 

Return Value

-Reference to the current object +Reference to the current object

Parameters

@@ -566,7 +508,7 @@

Parameters

value -The object to move assign from +The object to move assign from diff --git a/test-files/golden-tests/config/auto-function-metadata/param-from-operator.adoc b/test-files/golden-tests/config/auto-function-metadata/param-from-operator.adoc index 2814e9be7..5ffba3d13 100644 --- a/test-files/golden-tests/config/auto-function-metadata/param-from-operator.adoc +++ b/test-files/golden-tests/config/auto-function-metadata/param-from-operator.adoc @@ -70,6 +70,18 @@ struct X; | Addition operator |=== +=== Non-Member Functions + +[cols=2] +|=== +| Name +| Description +| link:#operator_not[`operator!`] +| Negation operator +| link:#operator_minus[`operator‐`] +| Subtraction operator +|=== + [#X-operator_plus] == link:#X[X]::operator+ @@ -113,6 +125,16 @@ Declared in `<param‐from‐operator.cpp>` struct ostream; ---- +=== Non-Member Functions + +[cols=2] +|=== +| Name +| Description +| link:#operator_lshift[`operator<<`] +| Stream insertion operator +|=== + [#operator_minus] == operator‐ diff --git a/test-files/golden-tests/config/auto-function-metadata/param-from-operator.html b/test-files/golden-tests/config/auto-function-metadata/param-from-operator.html index 02d6797e8..b87e0880b 100644 --- a/test-files/golden-tests/config/auto-function-metadata/param-from-operator.html +++ b/test-files/golden-tests/config/auto-function-metadata/param-from-operator.html @@ -7,7 +7,7 @@

Reference

-

+

Types

@@ -19,9 +19,9 @@

Types

- - - + + +
A A helper tag
X Test class
ostream A dumb ostream class
A A helper tag
X Test class
ostream A dumb ostream class
@@ -35,19 +35,18 @@

Functions

-operator- Subtraction operator -operator<< Stream insertion operator -operator! Negation operator +operator- Subtraction operator +operator<< Stream insertion operator +operator! Negation operator
-

A

+

A

-A helper tag - +

A helper tag

@@ -55,9 +54,7 @@

Synopsis

Declared in <param-from-operator.cpp>
-struct A;
-
-
+struct A;
 
@@ -65,10 +62,9 @@

Synopsis

-

X

+

X

-Test class - +

Test class

@@ -76,9 +72,7 @@

Synopsis

Declared in <param-from-operator.cpp>
-struct X;
-
-
+struct X;
 

Member Functions

@@ -91,19 +85,33 @@

Member Functions

-operator+ Addition operator +operator+ Addition operator +
+

Non-Member Functions

+ + + + + + + + + + + +
NameDescription
operator!Negation operator
operator-Subtraction operator
+
-

X::operator+

+

X::operator+

-Addition operator - +

Addition operator

@@ -112,14 +120,12 @@

Synopsis

Declared in <param-from-operator.cpp>
 X
-operator+(X const& x) const;
-
-
+operator+(X const& x) const;
 

Return Value

-Another instance of the object +Another instance of the object

Parameters

@@ -133,7 +139,7 @@

Parameters

x -The right operand +The right operand @@ -141,10 +147,9 @@

Parameters

-

ostream

+

ostream

-A dumb ostream class - +

A dumb ostream class

@@ -152,20 +157,31 @@

Synopsis

Declared in <param-from-operator.cpp>
-struct ostream;
-
-
+struct ostream;
 
+
+

Non-Member Functions

+ + + + + + + + + + +
NameDescription
operator<<Stream insertion operator
+
-

operator-

+

operator-

-Subtraction operator - +

Subtraction operator

@@ -176,14 +192,12 @@

Synopsis

X operator-( X const& x, - X const& y); - - + X const& y);

Return Value

-Test class +Test class

Parameters

@@ -197,11 +211,11 @@

Parameters

x -The left operand +The left operand y -The right operand +The right operand @@ -209,10 +223,9 @@

Parameters

-

operator<<

+

operator<<

-Stream insertion operator - +

Stream insertion operator

@@ -223,14 +236,12 @@

Synopsis

ostream& operator<<( ostream& os, - X const& x); - - + X const& x);

Return Value

-Reference to the current output stream +Reference to the current output stream

Parameters

@@ -244,11 +255,11 @@

Parameters

os -An output stream +An output stream x -The object to output +The object to output @@ -256,10 +267,9 @@

Parameters

-

operator!

+

operator!

-Negation operator - +

Negation operator

@@ -268,14 +278,12 @@

Synopsis

Declared in <param-from-operator.cpp>
 X
-operator!(X const& x);
-
-
+operator!(X const& x);
 

Return Value

-Test class +Test class

Parameters

@@ -289,7 +297,7 @@

Parameters

x -The operand +The operand diff --git a/test-files/golden-tests/config/auto-function-metadata/param-from-operator.xml b/test-files/golden-tests/config/auto-function-metadata/param-from-operator.xml index 534f8bc38..5a42b6340 100644 --- a/test-files/golden-tests/config/auto-function-metadata/param-from-operator.xml +++ b/test-files/golden-tests/config/auto-function-metadata/param-from-operator.xml @@ -16,6 +16,10 @@ Test class + + operator! + operator- + @@ -48,6 +52,9 @@ A dumb ostream class + + operator<< + @@ -79,6 +86,9 @@ The right operand + + X + @@ -112,6 +122,9 @@ The object to output + + ostream + @@ -135,6 +148,9 @@ The operand + + X + diff --git a/test-files/golden-tests/config/auto-function-metadata/returns-from-brief.html b/test-files/golden-tests/config/auto-function-metadata/returns-from-brief.html index a96ba4557..e397c6107 100644 --- a/test-files/golden-tests/config/auto-function-metadata/returns-from-brief.html +++ b/test-files/golden-tests/config/auto-function-metadata/returns-from-brief.html @@ -7,7 +7,7 @@

Reference

-

+

Types

@@ -19,17 +19,16 @@

Types

- +
X Test class
X Test class
-

X

+

X

-Test class - +

Test class

@@ -37,9 +36,7 @@

Synopsis

Declared in <returns-from-brief.cpp>
-struct X;
-
-
+struct X;
 

Member Functions

@@ -52,9 +49,9 @@

Member Functions

-empty Determines whether the range is empty. -front Returns the first element of the range. -size Get the range size. +empty Determines whether the range is empty. +front Returns the first element of the range. +size Get the range size. @@ -63,10 +60,9 @@

Member Functions

-

X::empty

+

X::empty

-Determines whether the range is empty. - +

Determines whether the range is empty.

@@ -75,22 +71,19 @@

Synopsis

Declared in <returns-from-brief.cpp>
 bool
-empty();
-
-
+empty();
 

Return Value

-whether the range is empty. +whether the range is empty.
-

X::front

+

X::front

-Returns the first element of the range. - +

Returns the first element of the range.

@@ -99,22 +92,19 @@

Synopsis

Declared in <returns-from-brief.cpp>
 int
-front() const;
-
-
+front() const;
 

Return Value

-the first element of the range. +the first element of the range.
-

X::size

+

X::size

-Get the range size. - +

Get the range size.

@@ -123,14 +113,12 @@

Synopsis

Declared in <returns-from-brief.cpp>
 int
-size() const;
-
-
+size() const;
 

Return Value

-the range size. +the range size.
diff --git a/test-files/golden-tests/config/auto-function-metadata/returns-from-return-brief.html b/test-files/golden-tests/config/auto-function-metadata/returns-from-return-brief.html index 9c628cf83..5cdc9840a 100644 --- a/test-files/golden-tests/config/auto-function-metadata/returns-from-return-brief.html +++ b/test-files/golden-tests/config/auto-function-metadata/returns-from-return-brief.html @@ -7,7 +7,7 @@

Reference

-

+

Types

@@ -19,7 +19,7 @@

Types

- +
R The return type of the function
R The return type of the function
@@ -33,17 +33,16 @@

Functions

-getR Test function +getR Test function
-

R

+

R

-The return type of the function - +

The return type of the function

@@ -51,9 +50,7 @@

Synopsis

Declared in <returns-from-return-brief.cpp>
-struct R;
-
-
+struct R;
 
@@ -68,17 +65,16 @@

Non-Member Functions

-getRTest function +getRTest function
-

getR

+

getR

-Test function - +

Test function

@@ -87,14 +83,12 @@

Synopsis

Declared in <returns-from-return-brief.cpp>
 R
-getR();
-
-
+getR();
 

Return Value

-The return type of the function +The return type of the function
diff --git a/test-files/golden-tests/config/auto-function-metadata/returns-from-special.adoc b/test-files/golden-tests/config/auto-function-metadata/returns-from-special.adoc index d753647a4..20faf5a1e 100644 --- a/test-files/golden-tests/config/auto-function-metadata/returns-from-special.adoc +++ b/test-files/golden-tests/config/auto-function-metadata/returns-from-special.adoc @@ -60,6 +60,30 @@ Declared in `<returns‐from‐special.cpp>` struct A; ---- +=== Non-Member Functions + +[cols=2] +|=== +| Name +| Description +| link:#operator_not[`operator!`] +| Negation operator +| link:#operator_not_eq[`operator!=`] +| Inequality operator +| link:#operator_lt[`operator<`] +| Less‐than operator +| link:#operator_le[`operator<=`] +| Less‐than‐or‐equal operator +| link:#operator_3way[`operator<=>`] +| Three‐way comparison operator +| link:#operator_eq[`operator==`] +| Equality operator +| link:#operator_gt[`operator>`] +| Greater‐than operator +| link:#operator_ge[`operator>=`] +| Greater‐than‐or‐equal operator +|=== + [#Undoc] == Undoc @@ -529,6 +553,16 @@ Declared in `<returns‐from‐special.cpp>` struct ostream; ---- +=== Non-Member Functions + +[cols=2] +|=== +| Name +| Description +| link:#operator_lshift[`operator<<`] +| Stream insertion operator +|=== + [#operator_lshift] == operator<< diff --git a/test-files/golden-tests/config/auto-function-metadata/returns-from-special.html b/test-files/golden-tests/config/auto-function-metadata/returns-from-special.html index 98024861f..e60a481f2 100644 --- a/test-files/golden-tests/config/auto-function-metadata/returns-from-special.html +++ b/test-files/golden-tests/config/auto-function-metadata/returns-from-special.html @@ -7,7 +7,7 @@

Reference

-

+

Types

@@ -19,10 +19,10 @@

Types

- - - - + + + +
A A helper class
Undoc
X Test class
ostream A fake output stream
A A helper class
Undoc
X Test class
ostream A fake output stream
@@ -36,25 +36,24 @@

Functions

-operator<< Stream insertion operator -operator! Negation operator -operator== Equality operator -operator!= Inequality operator -operator< Less-than operator -operator<= Less-than-or-equal operator -operator> Greater-than operator -operator>= Greater-than-or-equal operator -operator<=> Three-way comparison operator +operator<< Stream insertion operator +operator! Negation operator +operator== Equality operator +operator!= Inequality operator +operator< Less-than operator +operator<= Less-than-or-equal operator +operator> Greater-than operator +operator>= Greater-than-or-equal operator +operator<=> Three-way comparison operator
-

A

+

A

-A helper class - +

A helper class

@@ -62,26 +61,43 @@

Synopsis

Declared in <returns-from-special.cpp>
-struct A;
-
-
+struct A;
 
+
+

Non-Member Functions

+ + + + + + + + + + + + + + + + + +
NameDescription
operator!Negation operator
operator!=Inequality operator
operator<Less-than operator
operator<=Less-than-or-equal operator
operator<=>Three-way comparison operator
operator==Equality operator
operator>Greater-than operator
operator>=Greater-than-or-equal operator
+
-

Undoc

+

Undoc

Synopsis

Declared in <returns-from-special.cpp>
-struct Undoc;
-
-
+struct Undoc;
 
@@ -89,10 +105,9 @@

Synopsis

-

X

+

X

-Test class - +

Test class

@@ -100,9 +115,7 @@

Synopsis

Declared in <returns-from-special.cpp>
-struct X;
-
-
+struct X;
 

Member Functions

@@ -115,19 +128,19 @@

Member Functions

-operator= Assignment operators -operator+ Addition operator -operator-> Member access operator -operator A Conversion to A -operator Undoc Conversion to Undoc -operator! Negation operator -operator== Equality operator -operator!= Inequality operator -operator< Less-than operator -operator<= Less-than-or-equal operator -operator> Greater-than operator -operator>= Greater-than-or-equal operator -operator<=> Three-way comparison operator +operator= Assignment operators +operator+ Addition operator +operator-> Member access operator +operator A Conversion to A +operator Undoc Conversion to Undoc +operator! Negation operator +operator== Equality operator +operator!= Inequality operator +operator< Less-than operator +operator<= Less-than-or-equal operator +operator> Greater-than operator +operator>= Greater-than-or-equal operator +operator<=> Three-way comparison operator @@ -136,30 +149,25 @@

Member Functions

-

X::operator=

+

X::operator=

-Assignment operators - +

Assignment operators

Synopses

Declared in <returns-from-special.cpp>
-Assignment operator +

Assignment operator

 X&
-operator=(A const& value);
-
-
+operator=(A const& value);
 
» more... -Assignment operator +

Assignment operator

 X&&
-operator=(A&& value);
-
-
+operator=(A&& value);
 
» more... @@ -167,10 +175,9 @@

Synopses

-

X::operator=

+

X::operator=

-Assignment operator - +

Assignment operator

@@ -179,14 +186,12 @@

Synopsis

Declared in <returns-from-special.cpp>
 X&
-operator=(A const& value);
-
-
+operator=(A const& value);
 

Return Value

-Reference to the current object +Reference to the current object

Parameters

@@ -200,7 +205,7 @@

Parameters

value -The object to copy assign from +The object to copy assign from @@ -208,10 +213,9 @@

Parameters

-

X::operator=

+

X::operator=

-Assignment operator - +

Assignment operator

@@ -220,14 +224,12 @@

Synopsis

Declared in <returns-from-special.cpp>
 X&&
-operator=(A&& value);
-
-
+operator=(A&& value);
 

Return Value

-Rvalue reference to the current object +Rvalue reference to the current object

Parameters

@@ -241,7 +243,7 @@

Parameters

value -The object to move assign from +The object to move assign from @@ -249,10 +251,9 @@

Parameters

-

X::operator+

+

X::operator+

-Addition operator - +

Addition operator

@@ -261,14 +262,12 @@

Synopsis

Declared in <returns-from-special.cpp>
 X
-operator+(X const& rhs) const;
-
-
+operator+(X const& rhs) const;
 

Return Value

-Another instance of the object +Another instance of the object

Parameters

@@ -282,7 +281,7 @@

Parameters

rhs -The right operand +The right operand @@ -290,10 +289,9 @@

Parameters

-

X::operator->

+

X::operator->

-Member access operator - +

Member access operator

@@ -302,22 +300,19 @@

Synopsis

Declared in <returns-from-special.cpp>
 X*
-operator->();
-
-
+operator->();
 

Return Value

-Pointer to the current object +Pointer to the current object
-

X::operator A

+

X::operator A

-Conversion to A - +

Conversion to A

@@ -325,22 +320,19 @@

Synopsis

Declared in <returns-from-special.cpp>
-operator A() const;
-
-
+operator A() const;
 

Return Value

-A helper class +A helper class
-

X::operator Undoc

+

X::operator Undoc

-Conversion to Undoc - +

Conversion to Undoc

@@ -348,22 +340,19 @@

Synopsis

Declared in <returns-from-special.cpp>
-operator Undoc() const;
-
-
+operator Undoc() const;
 

Return Value

-The object converted to Undoc +The object converted to Undoc
-

X::operator!

+

X::operator!

-Negation operator - +

Negation operator

@@ -372,22 +361,19 @@

Synopsis

Declared in <returns-from-special.cpp>
 bool
-operator!() const;
-
-
+operator!() const;
 

Return Value

-true if the object is falsy, false otherwise +true if the object is falsy, false otherwise
-

X::operator==

+

X::operator==

-Equality operator - +

Equality operator

@@ -396,14 +382,12 @@

Synopsis

Declared in <returns-from-special.cpp>
 bool
-operator==(X const& rhs) const;
-
-
+operator==(X const& rhs) const;
 

Return Value

-true if the objects are equal, false otherwise +true if the objects are equal, false otherwise

Parameters

@@ -417,7 +401,7 @@

Parameters

rhs -The right operand +The right operand @@ -425,10 +409,9 @@

Parameters

-

X::operator!=

+

X::operator!=

-Inequality operator - +

Inequality operator

@@ -437,14 +420,12 @@

Synopsis

Declared in <returns-from-special.cpp>
 bool
-operator!=(X const& rhs) const;
-
-
+operator!=(X const& rhs) const;
 

Return Value

-true if the objects are not equal, false otherwise +true if the objects are not equal, false otherwise

Parameters

@@ -458,7 +439,7 @@

Parameters

rhs -The right operand +The right operand @@ -466,10 +447,9 @@

Parameters

-

X::operator<

+

X::operator<

-Less-than operator - +

Less-than operator

@@ -478,14 +458,12 @@

Synopsis

Declared in <returns-from-special.cpp>
 bool
-operator<(X const& rhs) const;
-
-
+operator<(X const& rhs) const;
 

Return Value

-true if the left object is less than the right object, false otherwise +true if the left object is less than the right object, false otherwise

Parameters

@@ -499,7 +477,7 @@

Parameters

rhs -The right operand +The right operand @@ -507,10 +485,9 @@

Parameters

-

X::operator<=

+

X::operator<=

-Less-than-or-equal operator - +

Less-than-or-equal operator

@@ -519,14 +496,12 @@

Synopsis

Declared in <returns-from-special.cpp>
 bool
-operator<=(X const& rhs) const;
-
-
+operator<=(X const& rhs) const;
 

Return Value

-true if the left object is less than or equal to the right object, false otherwise +true if the left object is less than or equal to the right object, false otherwise

Parameters

@@ -540,7 +515,7 @@

Parameters

rhs -The right operand +The right operand @@ -548,10 +523,9 @@

Parameters

-

X::operator>

+

X::operator>

-Greater-than operator - +

Greater-than operator

@@ -560,14 +534,12 @@

Synopsis

Declared in <returns-from-special.cpp>
 bool
-operator>(X const& rhs) const;
-
-
+operator>(X const& rhs) const;
 

Return Value

-true if the left object is greater than the right object, false otherwise +true if the left object is greater than the right object, false otherwise

Parameters

@@ -581,7 +553,7 @@

Parameters

rhs -The right operand +The right operand @@ -589,10 +561,9 @@

Parameters

-

X::operator>=

+

X::operator>=

-Greater-than-or-equal operator - +

Greater-than-or-equal operator

@@ -601,14 +572,12 @@

Synopsis

Declared in <returns-from-special.cpp>
 bool
-operator>=(X const& rhs) const;
-
-
+operator>=(X const& rhs) const;
 

Return Value

-true if the left object is greater than or equal to the right object, false otherwise +true if the left object is greater than or equal to the right object, false otherwise

Parameters

@@ -622,7 +591,7 @@

Parameters

rhs -The right operand +The right operand @@ -630,10 +599,9 @@

Parameters

-

X::operator<=>

+

X::operator<=>

-Three-way comparison operator - +

Three-way comparison operator

@@ -642,14 +610,12 @@

Synopsis

Declared in <returns-from-special.cpp>
 auto
-operator<=>(X const& rhs) const;
-
-
+operator<=>(X const& rhs) const;
 

Return Value

-The relative order of the objects +The relative order of the objects

Parameters

@@ -663,7 +629,7 @@

Parameters

rhs -The right operand +The right operand @@ -671,10 +637,9 @@

Parameters

-

ostream

+

ostream

-A fake output stream - +

A fake output stream

@@ -682,20 +647,31 @@

Synopsis

Declared in <returns-from-special.cpp>
-struct ostream;
-
-
+struct ostream;
 
+
+

Non-Member Functions

+ + + + + + + + + + +
NameDescription
operator<<Stream insertion operator
+
-

operator<<

+

operator<<

-Stream insertion operator - +

Stream insertion operator

@@ -706,14 +682,12 @@

Synopsis

ostream& operator<<( ostream& os, - A const& value); - - + A const& value);

Return Value

-Reference to the current output stream +Reference to the current output stream

Parameters

@@ -727,11 +701,11 @@

Parameters

os -An output stream +An output stream value -The object to output +The object to output @@ -739,10 +713,9 @@

Parameters

-

operator!

+

operator!

-Negation operator - +

Negation operator

@@ -751,14 +724,12 @@

Synopsis

Declared in <returns-from-special.cpp>
 bool
-operator!(A const& value);
-
-
+operator!(A const& value);
 

Return Value

-true if the object is falsy, false otherwise +true if the object is falsy, false otherwise

Parameters

@@ -772,7 +743,7 @@

Parameters

value -The operand +The operand @@ -780,10 +751,9 @@

Parameters

-

operator==

+

operator==

-Equality operator - +

Equality operator

@@ -794,14 +764,12 @@

Synopsis

bool operator==( A const& lhs, - A const& rhs); - - + A const& rhs);

Return Value

-true if the objects are equal, false otherwise +true if the objects are equal, false otherwise

Parameters

@@ -815,11 +783,11 @@

Parameters

lhs -The left operand +The left operand rhs -The right operand +The right operand @@ -827,10 +795,9 @@

Parameters

-

operator!=

+

operator!=

-Inequality operator - +

Inequality operator

@@ -841,14 +808,12 @@

Synopsis

bool operator!=( A const& lhs, - A const& rhs); - - + A const& rhs);

Return Value

-true if the objects are not equal, false otherwise +true if the objects are not equal, false otherwise

Parameters

@@ -862,11 +827,11 @@

Parameters

lhs -The left operand +The left operand rhs -The right operand +The right operand @@ -874,10 +839,9 @@

Parameters

-

operator<

+

operator<

-Less-than operator - +

Less-than operator

@@ -888,14 +852,12 @@

Synopsis

bool operator<( A const& lhs, - A const& rhs); - - + A const& rhs);

Return Value

-true if the left object is less than the right object, false otherwise +true if the left object is less than the right object, false otherwise

Parameters

@@ -909,11 +871,11 @@

Parameters

lhs -The left operand +The left operand rhs -The right operand +The right operand @@ -921,10 +883,9 @@

Parameters

-

operator<=

+

operator<=

-Less-than-or-equal operator - +

Less-than-or-equal operator

@@ -935,14 +896,12 @@

Synopsis

bool operator<=( A const& lhs, - A const& rhs); - - + A const& rhs);

Return Value

-true if the left object is less than or equal to the right object, false otherwise +true if the left object is less than or equal to the right object, false otherwise

Parameters

@@ -956,11 +915,11 @@

Parameters

lhs -The left operand +The left operand rhs -The right operand +The right operand @@ -968,10 +927,9 @@

Parameters

-

operator>

+

operator>

-Greater-than operator - +

Greater-than operator

@@ -982,14 +940,12 @@

Synopsis

bool operator>( A const& lhs, - A const& rhs); - - + A const& rhs);

Return Value

-true if the left object is greater than the right object, false otherwise +true if the left object is greater than the right object, false otherwise

Parameters

@@ -1003,11 +959,11 @@

Parameters

lhs -The left operand +The left operand rhs -The right operand +The right operand @@ -1015,10 +971,9 @@

Parameters

-

operator>=

+

operator>=

-Greater-than-or-equal operator - +

Greater-than-or-equal operator

@@ -1029,14 +984,12 @@

Synopsis

bool operator>=( A const& lhs, - A const& rhs); - - + A const& rhs);

Return Value

-true if the left object is greater than or equal to the right object, false otherwise +true if the left object is greater than or equal to the right object, false otherwise

Parameters

@@ -1050,11 +1003,11 @@

Parameters

lhs -The left operand +The left operand rhs -The right operand +The right operand @@ -1062,10 +1015,9 @@

Parameters

-

operator<=>

+

operator<=>

-Three-way comparison operator - +

Three-way comparison operator

@@ -1076,14 +1028,12 @@

Synopsis

auto operator<=>( A const& lhs, - A const& rhs); - - + A const& rhs);

Return Value

-The relative order of the objects +The relative order of the objects

Parameters

@@ -1097,11 +1047,11 @@

Parameters

lhs -The left operand +The left operand rhs -The right operand +The right operand diff --git a/test-files/golden-tests/config/auto-function-metadata/returns-from-special.xml b/test-files/golden-tests/config/auto-function-metadata/returns-from-special.xml index 91b8ee830..79acf1842 100644 --- a/test-files/golden-tests/config/auto-function-metadata/returns-from-special.xml +++ b/test-files/golden-tests/config/auto-function-metadata/returns-from-special.xml @@ -8,6 +8,16 @@ A helper class + + operator! + operator!= + operator< + operator<= + operator<=> + operator== + operator> + operator>= + @@ -357,6 +367,9 @@ A fake output stream + + operator<< + @@ -390,6 +403,9 @@ The object to output + + ostream + @@ -416,6 +432,9 @@ The operand + + A + @@ -450,6 +469,9 @@ The right operand + + A + @@ -484,6 +506,9 @@ The right operand + + A + @@ -518,6 +543,9 @@ The right operand + + A + @@ -552,6 +580,9 @@ The right operand + + A + @@ -586,6 +617,9 @@ The right operand + + A + @@ -620,6 +654,9 @@ The right operand + + A + @@ -652,6 +689,9 @@ The right operand + + A + diff --git a/test-files/golden-tests/config/auto-relates/auto-relates.html b/test-files/golden-tests/config/auto-relates/auto-relates.html index fc7d27d70..f50d7347a 100644 --- a/test-files/golden-tests/config/auto-relates/auto-relates.html +++ b/test-files/golden-tests/config/auto-relates/auto-relates.html @@ -7,7 +7,7 @@

Reference

-

+

Types

@@ -19,7 +19,7 @@

Types

- +
A A class with non-member functions
A A class with non-member functions
@@ -33,22 +33,21 @@

Functions

-f1 A non-member function of A -f2 A non-member function of A -f3 A non-member function of A -f4 A non-member function of A -f5 A non-member function of A -f6 A non-member function of A +f1 A non-member function of A +f2 A non-member function of A +f3 A non-member function of A +f4 A non-member function of A +f5 A non-member function of A +f6 A non-member function of A
-

A

+

A

-A class with non-member functions - +

A class with non-member functions

@@ -56,9 +55,7 @@

Synopsis

Declared in <auto-relates.cpp>
-class A;
-
-
+class A;
 
@@ -73,22 +70,21 @@

Non-Member Functions

-f1A non-member function of A -f2A non-member function of A -f3A non-member function of A -f4A non-member function of A -f5A non-member function of A -f6A non-member function of A +f1A non-member function of A +f2A non-member function of A +f3A non-member function of A +f4A non-member function of A +f5A non-member function of A +f6A non-member function of A
-

f1

+

f1

-A non-member function of A - +

A non-member function of A

@@ -97,18 +93,15 @@

Synopsis

Declared in <auto-relates.cpp>
 void
-f1(A);
-
-
+f1(A);
 
-

f2

+

f2

-A non-member function of A - +

A non-member function of A

@@ -117,18 +110,15 @@

Synopsis

Declared in <auto-relates.cpp>
 void
-f2(A&);
-
-
+f2(A&);
 
-

f3

+

f3

-A non-member function of A - +

A non-member function of A

@@ -137,18 +127,15 @@

Synopsis

Declared in <auto-relates.cpp>
 void
-f3(A const&);
-
-
+f3(A const&);
 
-

f4

+

f4

-A non-member function of A - +

A non-member function of A

@@ -157,18 +144,15 @@

Synopsis

Declared in <auto-relates.cpp>
 void
-f4(A*);
-
-
+f4(A*);
 
-

f5

+

f5

-A non-member function of A - +

A non-member function of A

@@ -177,18 +161,15 @@

Synopsis

Declared in <auto-relates.cpp>
 void
-f5(A const*);
-
-
+f5(A const*);
 
-

f6

+

f6

-A non-member function of A - +

A non-member function of A

@@ -197,9 +178,7 @@

Synopsis

Declared in <auto-relates.cpp>
 void
-f6(A const*);
-
-
+f6(A const*);
 
diff --git a/test-files/golden-tests/config/auto-relates/derived.html b/test-files/golden-tests/config/auto-relates/derived.html index d4789cb95..89399b3fe 100644 --- a/test-files/golden-tests/config/auto-relates/derived.html +++ b/test-files/golden-tests/config/auto-relates/derived.html @@ -7,7 +7,7 @@

Reference

-

+

Types

@@ -19,10 +19,10 @@

Types

- - - - + + + +
A A concrete implementation for ABase
ABase A base class for non-member functions
AView A view of A
AView2 Another view of A
A A concrete implementation for ABase
ABase A base class for non-member functions
AView A view of A
AView2 Another view of A
@@ -36,22 +36,21 @@

Functions

-f1 A non-member function of ABase -f2 A non-member function of ABase -f3 A non-member function of ABase -f4 A non-member function of ABase -f5 A non-member function of ABase -n A non-member function of ABase only +f1 A non-member function of ABase +f2 A non-member function of ABase +f3 A non-member function of ABase +f4 A non-member function of ABase +f5 A non-member function of ABase +n A non-member function of ABase only
-

A

+

A

-A concrete implementation for ABase - +

A concrete implementation for ABase

@@ -60,9 +59,7 @@

Synopsis

Declared in <derived.cpp>
 struct A
-    : ABase
-
-
+    : ABase
 
@@ -75,7 +72,7 @@

Base Classes

-ABaseA base class for non-member functions +ABaseA base class for non-member functions
@@ -91,21 +88,20 @@

Non-Member Functions

-f1A non-member function of ABase -f2A non-member function of ABase -f3A non-member function of ABase -f4A non-member function of ABase -f5A non-member function of ABase +f1A non-member function of ABase +f2A non-member function of ABase +f3A non-member function of ABase +f4A non-member function of ABase +f5A non-member function of ABase
-

ABase

+

ABase

-A base class for non-member functions - +

A base class for non-member functions

@@ -113,9 +109,7 @@

Synopsis

Declared in <derived.cpp>
-struct ABase;
-
-
+struct ABase;
 
@@ -130,12 +124,12 @@

Non-Member Functions

-f1A non-member function of ABase -f2A non-member function of ABase -f3A non-member function of ABase -f4A non-member function of ABase -f5A non-member function of ABase -nA non-member function of ABase only +f1A non-member function of ABase +f2A non-member function of ABase +f3A non-member function of ABase +f4A non-member function of ABase +f5A non-member function of ABase +nA non-member function of ABase only
@@ -150,19 +144,18 @@

Derived Classes

A - A concrete implementation for ABase + A concrete implementation for ABase AView - A view of A + A view of A
-

AView

+

AView

-A view of A - +

A view of A

@@ -171,9 +164,7 @@

Synopsis

Declared in <derived.cpp>
 struct AView
-    : ABase
-
-
+    : ABase
 
@@ -186,7 +177,7 @@

Base Classes

-ABaseA base class for non-member functions +ABaseA base class for non-member functions
@@ -202,11 +193,11 @@

Non-Member Functions

-f1A non-member function of ABase -f2A non-member function of ABase -f3A non-member function of ABase -f4A non-member function of ABase -f5A non-member function of ABase +f1A non-member function of ABase +f2A non-member function of ABase +f3A non-member function of ABase +f4A non-member function of ABase +f5A non-member function of ABase
@@ -221,17 +212,16 @@

Derived Classes

AView2 - Another view of A + Another view of A
-

AView2

+

AView2

-Another view of A - +

Another view of A

@@ -240,9 +230,7 @@

Synopsis

Declared in <derived.cpp>
 struct AView2
-    : AView
-
-
+    : AView
 
@@ -255,7 +243,7 @@

Base Classes

-AViewA view of A +AViewA view of A
@@ -271,11 +259,11 @@

Non-Member Functions

-f1A non-member function of ABase -f2A non-member function of ABase -f3A non-member function of ABase -f4A non-member function of ABase -f5A non-member function of ABase +f1A non-member function of ABase +f2A non-member function of ABase +f3A non-member function of ABase +f4A non-member function of ABase +f5A non-member function of ABase
@@ -286,10 +274,9 @@

Description

-

f1

+

f1

-A non-member function of ABase - +

A non-member function of ABase

@@ -298,18 +285,15 @@

Synopsis

Declared in <derived.cpp>
 void
-f1(ABase const&);
-
-
+f1(ABase const&);
 
-

f2

+

f2

-A non-member function of ABase - +

A non-member function of ABase

@@ -318,18 +302,15 @@

Synopsis

Declared in <derived.cpp>
 void
-f2(ABase&);
-
-
+f2(ABase&);
 
-

f3

+

f3

-A non-member function of ABase - +

A non-member function of ABase

@@ -338,18 +319,15 @@

Synopsis

Declared in <derived.cpp>
 void
-f3(ABase const*);
-
-
+f3(ABase const*);
 
-

f4

+

f4

-A non-member function of ABase - +

A non-member function of ABase

@@ -358,18 +336,15 @@

Synopsis

Declared in <derived.cpp>
 void
-f4(ABase*);
-
-
+f4(ABase*);
 
-

f5

+

f5

-A non-member function of ABase - +

A non-member function of ABase

@@ -378,18 +353,15 @@

Synopsis

Declared in <derived.cpp>
 void
-f5(ABase const*);
-
-
+f5(ABase const*);
 
-

n

+

n

-A non-member function of ABase only - +

A non-member function of ABase only

@@ -398,9 +370,7 @@

Synopsis

Declared in <derived.cpp>
 void
-n(ABase);
-
-
+n(ABase);
 
diff --git a/test-files/golden-tests/config/auto-relates/enum.html b/test-files/golden-tests/config/auto-relates/enum.html index 3cab62b65..ba6073a7a 100644 --- a/test-files/golden-tests/config/auto-relates/enum.html +++ b/test-files/golden-tests/config/auto-relates/enum.html @@ -7,7 +7,7 @@

Reference

-

+

Types

@@ -19,8 +19,8 @@

Types

- - + +
Result Helper result class
SmallVector Helper result class
Result Helper result class
SmallVector Helper result class
@@ -34,7 +34,7 @@

Enums

-E An enum with non-member functions +E An enum with non-member functions @@ -48,19 +48,18 @@

Functions

-makeE Function that returns A -makeEs Function that returns template on A -tryMakeE Function that returns template on A +makeE Function that returns A +makeEs Function that returns template on A +tryMakeE Function that returns template on A
-

Result

+

Result

-Helper result class - +

Helper result class

@@ -69,9 +68,7 @@

Synopsis

Declared in <enum.cpp>
 template<class T>
-class Result;
-
-
+class Result;
 
@@ -86,17 +83,16 @@

Non-Member Functions

-tryMakeEFunction that returns template on A +tryMakeEFunction that returns template on A
-

SmallVector

+

SmallVector

-Helper result class - +

Helper result class

@@ -107,9 +103,7 @@

Synopsis

template< class T, unsigned long N> -class SmallVector; - - +class SmallVector;
@@ -124,17 +118,16 @@

Non-Member Functions

-makeEsFunction that returns template on A +makeEsFunction that returns template on A
-

E

+

E

-An enum with non-member functions - +

An enum with non-member functions

@@ -142,25 +135,10 @@

Synopsis

Declared in <enum.cpp>
-enum class E : int;
-
-
+enum class E : int;
 
-

Members

- - - - - - - - - -
NameDescription
-
-

Non-Member Functions

@@ -170,19 +148,18 @@

Non-Member Functions

- - - + + +
makeEFunction that returns A
makeEsFunction that returns template on A
tryMakeEFunction that returns template on A
makeEFunction that returns A
makeEsFunction that returns template on A
tryMakeEFunction that returns template on A
-

makeE

+

makeE

-Function that returns A - +

Function that returns A

@@ -191,22 +168,19 @@

Synopsis

Declared in <enum.cpp>
 E
-makeE();
-
-
+makeE();
 

Return Value

-An instance of A +An instance of A
-

makeEs

+

makeEs

-Function that returns template on A - +

Function that returns template on A

@@ -215,22 +189,19 @@

Synopsis

Declared in <enum.cpp>
 SmallVector<E, 3>
-makeEs();
-
-
+makeEs();
 

Return Value

-A vector of As +A vector of As
-

tryMakeE

+

tryMakeE

-Function that returns template on A - +

Function that returns template on A

@@ -239,14 +210,12 @@

Synopsis

Declared in <enum.cpp>
 Result<E>
-tryMakeE();
-
-
+tryMakeE();
 

Return Value

-An instance of A or an error +An instance of A or an error
diff --git a/test-files/golden-tests/config/auto-relates/enum.xml b/test-files/golden-tests/config/auto-relates/enum.xml index 5667f16fc..6c4490ced 100644 --- a/test-files/golden-tests/config/auto-relates/enum.xml +++ b/test-files/golden-tests/config/auto-relates/enum.xml @@ -18,7 +18,7 @@ + + + operator! + operator!= + operator== + + @@ -318,6 +325,9 @@ The operand + + A +
@@ -352,6 +362,9 @@ The right operand + + A + @@ -386,6 +399,9 @@ The right operand + + A + diff --git a/test-files/golden-tests/config/sort-namespace-members-by/sort-namespace-members-by-name.adoc b/test-files/golden-tests/config/sort-namespace-members-by/sort-namespace-members-by-name.adoc index 1ba8bff7f..6c59c4d92 100644 --- a/test-files/golden-tests/config/sort-namespace-members-by/sort-namespace-members-by-name.adoc +++ b/test-files/golden-tests/config/sort-namespace-members-by/sort-namespace-members-by-name.adoc @@ -52,6 +52,20 @@ Declared in `<sort‐namespace‐members‐by‐name&peri struct A; ---- +=== Non-Member Functions + +[cols=2] +|=== +| Name +| Description +| link:#operator_not[`operator!`] +| Negation operator +| link:#operator_not_eq[`operator!=`] +| Inequality operator +| link:#operator_eq[`operator==`] +| Equality operator +|=== + [#B-0b] == B diff --git a/test-files/golden-tests/config/sort-namespace-members-by/sort-namespace-members-by-name.html b/test-files/golden-tests/config/sort-namespace-members-by/sort-namespace-members-by-name.html index 399b5f869..948aabf43 100644 --- a/test-files/golden-tests/config/sort-namespace-members-by/sort-namespace-members-by-name.html +++ b/test-files/golden-tests/config/sort-namespace-members-by/sort-namespace-members-by-name.html @@ -7,7 +7,7 @@

Reference

-

+

Types

@@ -40,36 +40,50 @@

Functions

- - - - - - + + + + + +
f
g
h
operator! Negation operator
operator== Equality operator
operator!= Inequality operator
f
g
h
operator! Negation operator
operator== Equality operator
operator!= Inequality operator
-

A

+

A

Synopsis

Declared in <sort-namespace-members-by-name.cpp>
-struct A;
-
-
+struct A;
 
+
+

Non-Member Functions

+ + + + + + + + + + + + +
NameDescription
operator!Negation operator
operator!=Inequality operator
operator==Equality operator
+
-

B

+

B

Synopsis

@@ -79,9 +93,7 @@

Synopsis

template< class T, class U> -struct B; - - +struct B;
@@ -89,7 +101,7 @@

Synopsis

-

B<int, char>

+

B<int, char>

Synopsis

@@ -97,9 +109,7 @@

Synopsis

Declared in <sort-namespace-members-by-name.cpp>
 template<>
-struct B<int, char>;
-
-
+struct B<int, char>;
 
@@ -107,7 +117,7 @@

Synopsis

-

B<int, U>

+

B<int, U>

Synopsis

@@ -115,9 +125,7 @@

Synopsis

Declared in <sort-namespace-members-by-name.cpp>
 template<class U>
-struct B<int, U>;
-
-
+struct B<int, U>;
 
@@ -125,7 +133,7 @@

Synopsis

-

C

+

C

Synopsis

@@ -135,9 +143,7 @@

Synopsis

template< class T, class U = void> -struct C; - - +struct C;
@@ -145,7 +151,7 @@

Synopsis

-

C<int>

+

C<int>

Synopsis

@@ -153,9 +159,7 @@

Synopsis

Declared in <sort-namespace-members-by-name.cpp>
 template<>
-struct C<int>;
-
-
+struct C<int>;
 
@@ -163,7 +167,7 @@

Synopsis

-

C<int, char>

+

C<int, char>

Synopsis

@@ -171,9 +175,7 @@

Synopsis

Declared in <sort-namespace-members-by-name.cpp>
 template<>
-struct C<int, char>;
-
-
+struct C<int, char>;
 
@@ -181,16 +183,14 @@

Synopsis

-

D

+

D

Synopsis

Declared in <sort-namespace-members-by-name.cpp>
-struct D;
-
-
+struct D;
 
@@ -198,16 +198,14 @@

Synopsis

-

Z

+

Z

Synopsis

Declared in <sort-namespace-members-by-name.cpp>
-struct Z;
-
-
+struct Z;
 

Member Functions

@@ -220,14 +218,14 @@

Member Functions

-Z [constructor]Constructors -~Z [destructor]Destructor -foo -operator bool Conversion to bool -operator! Negation operator -operator== Equality operator -operator!= Inequality operator -operator<=> Three-way comparison operator +Z [constructor]Constructors +~Z [destructor]Destructor +foo +operator bool Conversion to bool +operator! Negation operator +operator== Equality operator +operator!= Inequality operator +operator<=> Three-way comparison operator @@ -236,28 +234,23 @@

Member Functions

-

Z::Z

+

Z::Z

-Constructors - +

Constructors

Synopses

Declared in <sort-namespace-members-by-name.cpp>
-Default constructor +

Default constructor

-Z();
-
-
+Z();
 
» more... -Construct from int +

Construct from int

-Z(int value);
-
-
+Z(int value);
 
» more... @@ -265,10 +258,9 @@

Synopses

-

Z::Z

+

Z::Z

-Default constructor - +

Default constructor

@@ -276,18 +268,15 @@

Synopsis

Declared in <sort-namespace-members-by-name.cpp>
-Z();
-
-
+Z();
 
-

Z::Z

+

Z::Z

-Construct from int - +

Construct from int

@@ -295,9 +284,7 @@

Synopsis

Declared in <sort-namespace-members-by-name.cpp>
-Z(int value);
-
-
+Z(int value);
 
@@ -312,7 +299,7 @@

Parameters

value -The value to construct from +The value to construct from @@ -320,10 +307,9 @@

Parameters

-

Z::~Z

+

Z::~Z

-Destructor - +

Destructor

@@ -331,15 +317,13 @@

Synopsis

Declared in <sort-namespace-members-by-name.cpp>
-~Z();
-
-
+~Z();
 
-

Z::foo

+

Z::foo

Synopsis

@@ -347,18 +331,15 @@

Synopsis

Declared in <sort-namespace-members-by-name.cpp>
 void
-foo() const;
-
-
+foo() const;
 
-

Z::operator bool

+

Z::operator bool

-Conversion to bool - +

Conversion to bool

@@ -366,22 +347,19 @@

Synopsis

Declared in <sort-namespace-members-by-name.cpp>
-operator bool() const;
-
-
+operator bool() const;
 

Return Value

-The object converted to bool +The object converted to bool
-

Z::operator!

+

Z::operator!

-Negation operator - +

Negation operator

@@ -390,22 +368,19 @@

Synopsis

Declared in <sort-namespace-members-by-name.cpp>
 bool
-operator!() const;
-
-
+operator!() const;
 

Return Value

-true if the object is falsy, false otherwise +true if the object is falsy, false otherwise
-

Z::operator==

+

Z::operator==

-Equality operator - +

Equality operator

@@ -414,14 +389,12 @@

Synopsis

Declared in <sort-namespace-members-by-name.cpp>
 bool
-operator==(Z const& rhs) const;
-
-
+operator==(Z const& rhs) const;
 

Return Value

-true if the objects are equal, false otherwise +true if the objects are equal, false otherwise

Parameters

@@ -435,7 +408,7 @@

Parameters

rhs -The right operand +The right operand @@ -443,10 +416,9 @@

Parameters

-

Z::operator!=

+

Z::operator!=

-Inequality operator - +

Inequality operator

@@ -455,14 +427,12 @@

Synopsis

Declared in <sort-namespace-members-by-name.cpp>
 bool
-operator!=(Z const& rhs) const;
-
-
+operator!=(Z const& rhs) const;
 

Return Value

-true if the objects are not equal, false otherwise +true if the objects are not equal, false otherwise

Parameters

@@ -476,7 +446,7 @@

Parameters

rhs -The right operand +The right operand @@ -484,10 +454,9 @@

Parameters

-

Z::operator<=>

+

Z::operator<=>

-Three-way comparison operator - +

Three-way comparison operator

@@ -496,14 +465,12 @@

Synopsis

Declared in <sort-namespace-members-by-name.cpp>
 auto
-operator<=>(Z const& rhs) const;
-
-
+operator<=>(Z const& rhs) const;
 

Return Value

-The relative order of the objects +The relative order of the objects

Parameters

@@ -517,7 +484,7 @@

Parameters

rhs -The right operand +The right operand @@ -525,7 +492,7 @@

Parameters

-

f

+

f

Synopsis

@@ -533,15 +500,13 @@

Synopsis

Declared in <sort-namespace-members-by-name.cpp>
 void
-f();
-
-
+f();
 
-

g

+

g

Synopses

@@ -550,25 +515,19 @@

Synopses

 void
-g();
-
-
+g();
 
» more...
 char
-g(int);
-
-
+g(int);
 
» more...
 char
-g(double);
-
-
+g(double);
 
» more... @@ -576,9 +535,7 @@

Synopses

char g( double, - char); - - + char); » more... @@ -587,9 +544,7 @@

Synopses

g( char, char, - char); - - + char); » more... @@ -599,9 +554,7 @@

Synopses

g( T, T, - T); - - + T); » more... @@ -611,9 +564,7 @@

Synopses

g<int>( int, int, - int); - - + int); » more... @@ -621,7 +572,7 @@

Synopses

-

g

+

g

Synopsis

@@ -629,15 +580,13 @@

Synopsis

Declared in <sort-namespace-members-by-name.cpp>
 void
-g();
-
-
+g();
 
-

g

+

g

Synopsis

@@ -645,15 +594,13 @@

Synopsis

Declared in <sort-namespace-members-by-name.cpp>
 char
-g(int);
-
-
+g(int);
 
-

g

+

g

Synopsis

@@ -661,15 +608,13 @@

Synopsis

Declared in <sort-namespace-members-by-name.cpp>
 char
-g(double);
-
-
+g(double);
 
-

g

+

g

Synopsis

@@ -679,15 +624,13 @@

Synopsis

char g( double, - char); - - + char);
-

g

+

g

Synopsis

@@ -698,15 +641,13 @@

Synopsis

g( char, char, - char); - - + char);
-

g

+

g

Synopsis

@@ -718,15 +659,13 @@

Synopsis

g( T, T, - T); - - + T);
-

g<int>

+

g<int>

Synopsis

@@ -738,15 +677,13 @@

Synopsis

g<int>( int, int, - int); - - + int);
-

h

+

h

Synopsis

@@ -754,18 +691,15 @@

Synopsis

Declared in <sort-namespace-members-by-name.cpp>
 void
-h();
-
-
+h();
 
-

operator!

+

operator!

-Negation operator - +

Negation operator

@@ -774,14 +708,12 @@

Synopsis

Declared in <sort-namespace-members-by-name.cpp>
 bool
-operator!(A const& v);
-
-
+operator!(A const& v);
 

Return Value

-true if the object is falsy, false otherwise +true if the object is falsy, false otherwise

Parameters

@@ -795,7 +727,7 @@

Parameters

v -The operand +The operand @@ -803,10 +735,9 @@

Parameters

-

operator==

+

operator==

-Equality operator - +

Equality operator

@@ -817,14 +748,12 @@

Synopsis

bool operator==( A const& lhs, - A const& rhs); - - + A const& rhs);

Return Value

-true if the objects are equal, false otherwise +true if the objects are equal, false otherwise

Parameters

@@ -838,11 +767,11 @@

Parameters

lhs -The left operand +The left operand rhs -The right operand +The right operand @@ -850,10 +779,9 @@

Parameters

-

operator!=

+

operator!=

-Inequality operator - +

Inequality operator

@@ -864,14 +792,12 @@

Synopsis

bool operator!=( A const& lhs, - A const& rhs); - - + A const& rhs);

Return Value

-true if the objects are not equal, false otherwise +true if the objects are not equal, false otherwise

Parameters

@@ -885,11 +811,11 @@

Parameters

lhs -The left operand +The left operand rhs -The right operand +The right operand diff --git a/test-files/golden-tests/config/sort-namespace-members-by/sort-namespace-members-by-name.xml b/test-files/golden-tests/config/sort-namespace-members-by/sort-namespace-members-by-name.xml index db912f484..3d1b59bda 100644 --- a/test-files/golden-tests/config/sort-namespace-members-by/sort-namespace-members-by-name.xml +++ b/test-files/golden-tests/config/sort-namespace-members-by/sort-namespace-members-by-name.xml @@ -4,6 +4,13 @@ + + + operator! + operator!= + operator== + +